From: Florian Forster Date: Sun, 10 Feb 2019 20:50:51 +0000 (+0100) Subject: Merge branch 'collectd-5.8' X-Git-Url: https://git.octo.it/?p=collectd.git;a=commitdiff_plain;h=711f5b6c86f51061c21bedcaa46214a01de0125c;hp=2700666217249cd4794dfc17b4ac3b28f3dca56b Merge branch 'collectd-5.8' Conflicts: src/sensors.c --- diff --git a/.github/issue_template.md b/.github/issue_template.md index 49d24b30..05d29875 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,5 +1,6 @@ * Version of collectd: * Operating system / distribution: +* Kernel version (if applicable): ## Expected behavior diff --git a/.gitignore b/.gitignore index 2911069a..afdbe5cd 100644 --- a/.gitignore +++ b/.gitignore @@ -99,9 +99,8 @@ bindings/perl/pm_to_blib cscope.* # Unit tests -src/daemon/test-suite.log +test-suite.log src/tests/ -src/test-suite.log test_* # src/daemon/... diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..009ae439 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "gnulib"] + path = gnulib + url = git://git.savannah.gnu.org/gnulib.git diff --git a/.travis.yml b/.travis.yml index fc250cbb..97115d14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,9 @@ +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "ZdWWp0XX3C4sLIp4lqeQTWC7vt+GsWjmyRiD17T9833NBAW4dddz310I6iyeXe6oX09ZFFiVIN4ogx9ANcNBx9jriGXI2/82nBhpxOJBebet8JCNS5VeTr4rDSfQOKP+Oc+ko5KbbghTuAtO2CFYiH3jZUcn4TdsYbVanf+TwUs=" + sudo: required dist: trusty compiler: @@ -5,8 +11,11 @@ compiler: - clang language: c before_install: + # When building the coverity_scan branch, allow only the first job to continue to avoid travis-ci/travis-ci#1975. + - if [[ "${TRAVIS_BRANCH}" == "coverity_scan" && ! "${TRAVIS_JOB_NUMBER}" =~ \.1$ ]]; then exit 0; fi - sudo apt-get update -qq - sudo apt-get install -qq --no-install-recommends + autotools-dev iptables-dev libatasmart-dev libcap-dev @@ -21,6 +30,8 @@ before_install: libi2c-dev libldap2-dev libltdl-dev + liblua50-dev + liblua5.1-0-dev liblua5.2-dev liblvm2-dev libmemcached-dev @@ -39,6 +50,7 @@ before_install: libprotobuf-c0-dev librabbitmq-dev librdkafka-dev + libriemann-client-dev librrd-dev libsensors4-dev libsigrok-dev @@ -50,10 +62,28 @@ before_install: libupsclient-dev libvarnish-dev libvirt-dev + libxen-dev libxml2-dev libyajl-dev linux-libc-dev perl protobuf-c-compiler + python3-dev python-dev -script: sh build.sh && ./configure && make distcheck + xfslibs-dev +before_script: autoreconf -fi +script: + - if [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]]; then exit 0; fi + - ./configure + - make -j $(nproc) + - make check + +addons: + coverity_scan: + project: + name: "collectd/collectd" + description: "Build submitted via Travis CI" + notification_email: collectd-changes@verplant.org + build_command_prepend: "./configure; make clean" + build_command: "make -j $(nproc)" + branch_pattern: coverity_scan diff --git a/AUTHORS b/AUTHORS index 4df743c5..409655ac 100644 --- a/AUTHORS +++ b/AUTHORS @@ -59,6 +59,9 @@ Andreas Henriksson Andy Parkins - battery plugin: sysfs code. +Andy Smith + - AMQP 1.0 plugin. + Anthony Dewhurst - zfs_arc plugin. @@ -286,7 +289,7 @@ Scott Sanders - Write-Graphite plugin. Sebastien Pahl - - AMQP plugin. + - AMQP 0.9 plugin. Serhiy Pshyk - intel_pmu plugin diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..55fbd049 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,15 @@ +# Code ownership information. +# See https://help.github.com/articles/about-code-owners/ for details. + +# These owners will be the default owners for everything in the repo. Unless a +# later match takes precedence, # @trusted-contributors will be requested for +# review when someone opens a pull request. +* @trusted-contributors + +/src/intel_pmu.c @kwiatrox @sunkuranganath +/src/intel_rdt.c @kwiatrox @sunkuranganath +/src/ipmi.c @anaudx @rjablonx +/src/mcelog.c @kwiatrox @sunkuranganath +/src/virt.c @anaudx @rjablonx +# TODO(#2926): Add the following owners: +#/src/redfish.c @kkepka @mkobyli diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 11969ded..e4d683c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,6 +51,26 @@ the mailing list have a tendency to fall through the cracks. entries to `src/types.db`, you should talk to us early in the design process. +### ChangeLog + +All PRs need to have a one-line description in the initial pull request body. +This information is used to automatically generate release notes. Follow this +style: + +``` +Foo plugin: A specific issue people had has been fixed. +``` + +Start with "Foo plugin" to give the reader context for the information. Other +common prefixes are "collectd" for the core daemon and "Build system". Use past +tense and passive voice the for remainder, e.g. "a bug has been fixed", "a +feature has been added". + +Some PRs should not be added to the release notes, e.g. changes to project +internal documentation (such as this file). Those changes are not interesting +for external users of the project and would reduce the value of the release +notes. Maintainers may use the `Unlisted Change` label to mark those PRs. + ## Other resources * [Mailing list](http://mailman.verplant.org/listinfo/collectd) diff --git a/Makefile.am b/Makefile.am index e1fa3333..9bb22fc5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,15 @@ 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 \ @@ -61,6 +70,7 @@ EXTRA_DIST = \ src/types.db \ src/types.db.pod \ src/valgrind.FreeBSD.suppress \ + src/valgrind.suppress \ testwrapper.sh \ version-gen.sh @@ -98,7 +108,13 @@ pkginclude_HEADERS = \ 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 @@ -108,6 +124,7 @@ bin_PROGRAMS = \ collectd-nagios \ collectd-tg \ collectdctl +endif # BUILD_WIN32 noinst_LTLIBRARIES = \ @@ -150,7 +167,7 @@ TESTS = $(check_PROGRAMS) LOG_COMPILER = env VALGRIND="@VALGRIND@" $(abs_srcdir)/testwrapper.sh -jardir = $(pkgdatadir)/java +jardir = $(cpkgdatadir)/java pkglib_LTLIBRARIES = @@ -159,6 +176,9 @@ PLUGIN_LDFLAGS = \ -module \ -avoid-version \ -export-symbols-regex '\' +if BUILD_WIN32 +PLUGIN_LDFLAGS += -shared -no-undefined -lcollectd -L. +endif AM_CPPFLAGS = \ @@ -166,13 +186,26 @@ 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 @@ -194,6 +227,7 @@ endif collectd_SOURCES = \ + src/daemon/cmd.h \ src/daemon/collectd.c \ src/daemon/collectd.h \ src/daemon/configfile.c \ @@ -236,6 +270,13 @@ collectd_LDADD = \ $(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 @@ -247,6 +288,9 @@ collectd_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS) collectd_LDADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS) endif +if BUILD_WIN32 +collectd_LDFLAGS += -Wl,--out-implib,libcollectd.a +endif collectdmon_SOURCES = src/collectdmon.c @@ -498,6 +542,10 @@ libcollectdclient_la_CPPFLAGS = \ -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) @@ -526,6 +574,68 @@ liboconfig_la_SOURCES = \ 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 @@ -549,6 +659,20 @@ amqp_la_LIBADD = \ libformat_json.la endif +if BUILD_PLUGIN_AMQP1 +pkglib_LTLIBRARIES += amqp1.la +amqp1_la_SOURCES = \ + src/amqp1.c \ + src/utils_deq.h +amqp1_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBQPIDPROTON_CPPFLAGS) +amqp1_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBQPIDPROTON_LDFLAGS) +amqp1_la_LIBADD = \ + $(BUILD_WITH_LIBQPIDPROTON_LIBS) \ + libcmds.la \ + libformat_graphite.la \ + libformat_json.la +endif + if BUILD_PLUGIN_APACHE pkglib_LTLIBRARIES += apache.la apache_la_SOURCES = src/apache.c @@ -599,7 +723,7 @@ if BUILD_PLUGIN_BAROMETER pkglib_LTLIBRARIES += barometer.la barometer_la_SOURCES = src/barometer.c barometer_la_LDFLAGS = $(PLUGIN_LDFLAGS) -barometer_la_LIBADD = -lm +barometer_la_LIBADD = -lm $(BUILD_WITH_LIBI2C_LIBS) endif if BUILD_PLUGIN_BATTERY @@ -890,6 +1014,14 @@ gps_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBGPS_LDFLAGS) 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_CPPFLAGS = $(PLUGIN_CPPFLAGS) $(BUILD_WITH_GPU_CUDA_CPPFLAGS) +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 @@ -1025,6 +1157,7 @@ if BUILD_PLUGIN_LOGFILE 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 @@ -1364,6 +1497,24 @@ ovs_stats_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS) ovs_stats_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS) endif +if BUILD_PLUGIN_PCIE_ERRORS +pkglib_LTLIBRARIES += pcie_errors.la +pcie_errors_la_SOURCES = src/pcie_errors.c +pcie_errors_la_CPPFLAGS = $(AM_CPPFLAGS) +pcie_errors_la_LDFLAGS = $(PLUGIN_LDFLAGS) + +test_plugin_pcie_errors_SOURCES = \ + src/pcie_errors_test.c \ + src/daemon/utils_llist.c \ + src/daemon/configfile.c \ + src/daemon/types_list.c +test_plugin_pcie_errors_CPPFLAGS = $(AM_CPPFLAGS) +test_plugin_pcie_errors_LDFLAGS = $(PLUGIN_LDFLAGS) +test_plugin_pcie_errors_LDADD = liboconfig.la libplugin_mock.la +check_PROGRAMS += test_plugin_pcie_errors +TESTS += test_plugin_pcie_errors +endif + if BUILD_PLUGIN_PERL pkglib_LTLIBRARIES += perl.la perl_la_SOURCES = src/perl.c @@ -1435,14 +1586,28 @@ python_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBPYTHON_CPPFLAGS) python_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBPYTHON_LDFLAGS) endif +if HAVE_LIBMNL +noinst_LTLIBRARIES += libtaskstats.la +libtaskstats_la_SOURCES = \ + src/utils_taskstats.c \ + src/utils_taskstats.h +libtaskstats_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS) +libtaskstats_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS) +endif + if BUILD_PLUGIN_PROCESSES pkglib_LTLIBRARIES += processes.la processes_la_SOURCES = src/processes.c +processes_la_CPPFLAGS = $(AM_CPPFLAGS) processes_la_LDFLAGS = $(PLUGIN_LDFLAGS) processes_la_LIBADD = if BUILD_WITH_LIBKVM_GETPROCS processes_la_LIBADD += -lkvm endif +if HAVE_LIBMNL +processes_la_CPPFLAGS += -DHAVE_LIBTASKSTATS=1 +processes_la_LIBADD += libtaskstats.la +endif endif if BUILD_PLUGIN_PROTOCOLS @@ -1527,7 +1692,7 @@ pkglib_LTLIBRARIES += snmp.la snmp_la_SOURCES = src/snmp.c snmp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBNETSNMP_CPPFLAGS) snmp_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBNETSNMP_LDFLAGS) -snmp_la_LIBADD = $(BUILD_WITH_LIBNETSNMP_LIBS) +snmp_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBNETSNMP_LIBS) endif if BUILD_PLUGIN_SNMP_AGENT @@ -1536,6 +1701,23 @@ snmp_agent_la_SOURCES = src/snmp_agent.c snmp_agent_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBNETSNMPAGENT_CPPFLAGS) snmp_agent_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBNETSNMPAGENT_LDFLAGS) snmp_agent_la_LIBADD = $(BUILD_WITH_LIBNETSNMPAGENT_LIBS) + +test_plugin_snmp_agent_SOURCES = src/snmp_agent_test.c \ + src/daemon/utils_avltree.c \ + src/daemon/utils_llist.c \ + src/daemon/configfile.c \ + src/daemon/types_list.c +test_plugin_snmp_agent_CPPFLAGS = $(AM_CPPFLAGS) \ + $(BUILD_WITH_LIBNETSNMPAGENT_CPPFLAGS) +test_plugin_snmp_agent_LDFLAGS = $(PLUGIN_LDFLAGS) \ + $(BUILD_WITH_LIBNETSNMPAGENT_LDFLAGS) +test_plugin_snmp_agent_LDADD = liboconfig.la libplugin_mock.la \ + $(BUILD_WITH_LIBNETSNMPAGENT_LIBS) $(BUILD_WITH_LIBNETSNMP_LIBS) + +check_PROGRAMS += test_plugin_snmp_agent +TESTS += test_plugin_snmp_agent + + endif if BUILD_PLUGIN_STATSD @@ -1757,18 +1939,15 @@ virt_la_CFLAGS = $(AM_CFLAGS) \ virt_la_LDFLAGS = $(PLUGIN_LDFLAGS) virt_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS) -# TODO: enable once we support only modern libvirts which depends on libnl-3 -# the libvirt on wheezy is linked in libnl v1, and there is a small leak here, -# triggered by the library initialization. There are no means to avoid it, -# and libvirt switched to libnl3 anyway -#test_plugin_virt_SOURCES = src/virt_test.c -#test_plugin_virt_CPPFLAGS = $(AM_CPPFLAGS) \ -# $(BUILD_WITH_LIBVIRT_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS) -#test_plugin_virt_LDFLAGS = $(PLUGIN_LDFLAGS) -#test_plugin_virt_LDADD = libplugin_mock.la \ -# $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS) -#check_PROGRAMS += test_plugin_virt -#TESTS += test_plugin_virt +test_plugin_virt_SOURCES = src/virt_test.c +test_plugin_virt_CPPFLAGS = $(AM_CPPFLAGS) \ + $(BUILD_WITH_LIBVIRT_CPPFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS) +test_plugin_virt_LDFLAGS = $(PLUGIN_LDFLAGS) \ + $(BUILD_WITH_LIBVIRT_LDFLAGS) $(BUILD_WITH_LIBXML2_LDFLAGS) +test_plugin_virt_LDADD = libplugin_mock.la \ + $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS) +check_PROGRAMS += test_plugin_virt +TESTS += test_plugin_virt endif if BUILD_PLUGIN_VMEM @@ -1869,6 +2048,15 @@ write_sensu_la_SOURCES = src/write_sensu.c write_sensu_la_LDFLAGS = $(PLUGIN_LDFLAGS) endif +if BUILD_PLUGIN_WRITE_STACKDRIVER +pkglib_LTLIBRARIES += write_stackdriver.la +write_stackdriver_la_SOURCES = src/write_stackdriver.c +write_stackdriver_la_LDFLAGS = $(PLUGIN_LDFLAGS) +write_stackdriver_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS) +write_stackdriver_la_LIBADD = libformat_stackdriver.la libgce.la liboauth.la \ + $(BUILD_WITH_LIBCURL_LIBS) +endif + if BUILD_PLUGIN_WRITE_TSDB pkglib_LTLIBRARIES += write_tsdb.la write_tsdb_la_SOURCES = src/write_tsdb.c @@ -1984,19 +2172,19 @@ install-exec-hook: $(mkinstalldirs) $(DESTDIR)$(sysconfdir) if test -e $(DESTDIR)$(sysconfdir)/collectd.conf; \ then \ - $(INSTALL) -m 0640 $(srcdir)/src/collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf.pkg-orig; \ + $(INSTALL) -m 0640 $(builddir)/src/collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf.pkg-orig; \ else \ - $(INSTALL) -m 0640 $(srcdir)/src/collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \ + $(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@ @@ -2076,3 +2264,4 @@ generic-jmx.jar: $(JAVA_TIMESTAMP_FILE) jar_DATA = collectd-api.jar generic-jmx.jar endif + diff --git a/README b/README index ca86c84d..87760548 100644 --- a/README +++ b/README @@ -100,6 +100,9 @@ Features Collect DPDK interface statistics. See docs/BUILD.dpdkstat.md for detailed build instructions. + This plugin should be compiled with compiler defenses enabled, for + example -fstack-protector. + - drbd Collect individual drbd resource statistics. @@ -132,6 +135,9 @@ Features - gps Monitor gps related data through gpsd. + - gpu_nvidia + Monitor NVIDIA GPU statistics available through NVML. + - hddtemp Hard disk temperatures using hddtempd. @@ -140,6 +146,9 @@ Features hugepages can be found here: https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt. + This plugin should be compiled with compiler defenses enabled, for + example -fstack-protector. + - intel_pmu The intel_pmu plugin reads performance counters provided by the Linux kernel perf interface. The plugin uses jevents library to resolve named @@ -308,6 +317,10 @@ Features OVS documentation. + - pcie_errors + Read errors from PCI Express Device Status and AER extended capabilities. + + - perl The perl plugin implements a Perl-interpreter into collectd. You can write your own plugins in Perl and return arbitrary values using this @@ -459,7 +472,11 @@ Features - amqp Sends JSON-encoded data to an Advanced Message Queuing Protocol (AMQP) - server, such as RabbitMQ. + 0.9.1 server, such as RabbitMQ. + + - amqp1 + Sends JSON-encoded data to an Advanced Message Queuing Protocol (AMQP) + 1.0 server, such as Qpid Dispatch Router or Apache Artemis Broker. - csv Write to comma separated values (CSV) files. This needs lots of @@ -735,6 +752,10 @@ Prerequisites particular. + * CUDA (optional) + Used by the `gpu_nvidia' plugin + + * libatasmart (optional) Used by the `smart' plugin. @@ -902,8 +923,14 @@ Prerequisites are supported. + * libqpid-proton (optional) + Used by the `amqp1' plugin for AMQP 1.0 connections, for example to + Qdrouterd. + + * librabbitmq (optional; also called “rabbitmq-c”) - Used by the `amqp' plugin for AMQP connections, for example to RabbitMQ. + Used by the `amqp' plugin for AMQP 0.9.1 connections, for example to + RabbitMQ. * librdkafka (optional; also called “rdkafka”) @@ -981,9 +1008,8 @@ Configuring / Compiling / Installing ------------------------------------ To configure, build and install collectd with the default settings, run - `./configure && make && make install'. For detailed, generic instructions - see INSTALL. For a complete list of configure options and their description, - run `./configure --help'. + `./configure && make && make install'. For a complete list of configure + options and their description, run `./configure --help'. By default, the configure script will check for all build dependencies and disable all plugins whose requirements cannot be fulfilled (any other plugin @@ -1025,6 +1051,37 @@ To generate the `configure` script, you'll need the following dependencies: 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 -------------- diff --git a/build.sh b/build.sh index 40f5361d..c0ccce3a 100755 --- a/build.sh +++ b/build.sh @@ -1,54 +1,173 @@ -#! /bin/sh +#!/bin/sh GLOBAL_ERROR_INDICATOR=0 -check_for_application () +check_for_application() { - for PROG in "$@" - do - which "$PROG" >/dev/null 2>&1 - if test $? -ne 0; then - cat >&2 </dev/null 2>&1 + if test $? -ne 0; then + cat >&2 </dev/null 2>/dev/null -if test $? -eq 0 -then - libtoolize=libtoolize -else - glibtoolize --version >/dev/null 2>/dev/null - if test $? -eq 0 - then - libtoolize=glibtoolize - else - cat >&2 </dev/null 2>/dev/null + if test $? -eq 0; then + libtoolize=libtoolize + else + glibtoolize --version >/dev/null 2>/dev/null + if test $? -eq 0; then + libtoolize=glibtoolize + else + cat >&2 <" >> src/gnulib_config.h -set -x + 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 \ -&& $libtoolize --copy --force \ -&& automake --add-missing --copy \ -&& autoconf diff --git a/configure.ac b/configure.ac index d93b51b0..cadbb249 100644 --- a/configure.ac +++ b/configure.ac @@ -99,6 +99,10 @@ case $host_os in 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" ;; @@ -111,6 +115,7 @@ AM_CONDITIONAL([BUILD_FREEBSD], [test "x$ac_system" = "xFreeBSD"]) AM_CONDITIONAL([BUILD_LINUX], [test "x$ac_system" = "xLinux"]) AM_CONDITIONAL([BUILD_OPENBSD], [test "x$ac_system" = "xOpenBSD"]) AM_CONDITIONAL([BUILD_SOLARIS], [test "x$ac_system" = "xSolaris"]) +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.]) @@ -550,6 +555,12 @@ if test "x$ac_system" = "xLinux"; then AC_DEFINE([HAVE_CAPABILITY], [1], [Define to 1 if you have cap_get_proc() (-lcap).]) fi + # For pcie_errors plugin + AC_CHECK_HEADERS([linux/pci_regs.h], + [have_pci_regs_h="yes"], + [have_pci_regs_h="no (linux/pci_regs.h not found)"] + ) + else have_linux_raid_md_u_h="no" have_linux_wireless_h="no" @@ -745,6 +756,7 @@ AC_CHECK_FUNCS_ONCE([ \ getaddrinfo \ getgrnam_r \ getnameinfo \ + getpwnam \ getpwnam_r \ gettimeofday \ if_indextoname \ @@ -766,9 +778,12 @@ AC_CHECK_FUNCS_ONCE([ \ AC_FUNC_STRERROR_R SAVE_CFLAGS="$CFLAGS" -# Emulate behavior of src/Makefile.am -if test "x$GCC" = "xyes"; then - CFLAGS="$CFLAGS -Wall -Werror" +CFLAGS="-Wall -Werror" +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], @@ -842,6 +857,7 @@ if test "x$c_cv_have_strtok_r_default" = "xno"; then fi CFLAGS="$SAVE_CFLAGS" +LDFLAGS="$SAVE_LDFLAGS" if test "x$c_cv_have_strtok_r_reentrant" = "xyes"; then CFLAGS="$CFLAGS -D_REENTRANT=1" fi @@ -851,11 +867,17 @@ AC_CHECK_FUNCS([socket], [ 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], @@ -1567,7 +1589,7 @@ if test "x$have_getmntent" = "xlibc"; then struct mntent *me; fh = setmntent ("/etc/mtab", "r"); me = getmntent (fh); - return(me->mnt_passno); + return me->mnt_passno; ]] ) ], @@ -1590,7 +1612,7 @@ if test "x$have_getmntent" = "xlibc"; then int status; fh = fopen ("/etc/mnttab", "r"); status = getmntent (fh, &mt); - return(status); + return status; ]] ) ], @@ -1878,14 +1900,23 @@ fi # libi2c-dev if test "x$ac_system" = "xLinux"; then + with_libi2c_libs="" + AC_CHECK_HEADERS([i2c/smbus.h], + [with_libi2c_libs="-li2c"] + ) AC_CHECK_DECL([i2c_smbus_read_i2c_block_data], [with_libi2c="yes"], [with_libi2c="no (symbol i2c_smbus_read_i2c_block_data not found - have you installed libi2c-dev ?)"], [[ #include #include + #if HAVE_I2C_SMBUS_H + # include + #endif ]] ) + BUILD_WITH_LIBI2C_LIBS="$with_libi2c_libs" + AC_SUBST([BUILD_WITH_LIBI2C_LIBS]) else with_libi2c="no (Linux only)" fi @@ -2043,6 +2074,45 @@ if test "x$with_kvm_openfiles" = "xyes"; then with_libkvm="yes" fi +# --with-cuda {{{ +AC_ARG_WITH([cuda], + [AS_HELP_STRING([--with-cuda@<:@=PREFIX@:>@], [Path to cuda.])], + [ + if test "x$withval" != "xno" && test "x$withval" != "xyes"; then + with_cuda_cppflags="-I$withval/include" + with_cuda_ldflags="-I$withval/lib" + with_cuda="yes" + else + with_cuda="$withval" + fi + ], + [with_cuda="no"] +) + +if test "x$with_cuda" = "xyes"; then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_cuda_cppflags" + + AC_CHECK_HEADERS([nvml.h], + [with_cuda="yes"], + [with_cuda="no (nvml.h not found)"] + ) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi + +if test "x$with_cuda" = "xyes"; then + BUILD_WITH_CUDA_CPPFLAGS="$CUDA_CPPFLAGS" + BUILD_WITH_CUDA_LDFLAGS="$CUDA_LDFLAGS" + BUILD_WITH_CUDA_LIBS="-lnvidia-ml" +fi + +AC_SUBST([BUILD_WITH_CUDA_CPPFLAGS]) +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.])], @@ -2273,6 +2343,8 @@ fi AC_SUBST(BUILD_WITH_LIBCURL_CFLAGS) AC_SUBST(BUILD_WITH_LIBCURL_LIBS) + +AM_CONDITIONAL([BUILD_WITH_LIBCURL], [test "x$with_libcurl" = "xyes"]) # }}} # --with-libdbi {{{ @@ -2652,6 +2724,7 @@ AC_ARG_WITH([libgrpc++], if test "x$withval" != "xno" && test "x$withval" != "xyes"; then with_libgrpcpp_cppflags="-I$withval/include" with_libgrpcpp_ldflags="-L$withval/lib" + with_libgrpcpp_bin="$withval/bin" with_libgrpcpp="yes" fi if test "x$withval" = "xno"; then @@ -2731,7 +2804,11 @@ AC_SUBST([BUILD_WITH_LIBGRPCPP_LIBS]) # }}} AC_ARG_VAR([GRPC_CPP_PLUGIN], [path to the grpc_cpp_plugin binary]) -AC_PATH_PROG([GRPC_CPP_PLUGIN], [grpc_cpp_plugin]) +if test "x$with_libgrpcpp_bin" = "x"; then + AC_PATH_PROG([GRPC_CPP_PLUGIN], [grpc_cpp_plugin]) +else + AC_PATH_PROG([GRPC_CPP_PLUGIN], [grpc_cpp_plugin], [], "$with_libgrpcpp_bin:$PATH") +fi AM_CONDITIONAL([HAVE_GRPC_CPP], [test "x$GRPC_CPP_PLUGIN" != "x"]) # --with-libiptc {{{ @@ -3061,18 +3138,33 @@ else PKG_CHECK_MODULES([LUA], [lua5.3], [with_liblua="yes"], [ - PKG_CHECK_MODULES([LUA], [lua-5.2], + PKG_CHECK_MODULES([LUA], [lua53], [with_liblua="yes"], [ - PKG_CHECK_MODULES([LUA], [lua5.2], + PKG_CHECK_MODULES([LUA], [lua-5.2], [with_liblua="yes"], [ - PKG_CHECK_MODULES([LUA], [lua-5.1], + PKG_CHECK_MODULES([LUA], [lua5.2], [with_liblua="yes"], [ - PKG_CHECK_MODULES([LUA], [lua5.1], + PKG_CHECK_MODULES([LUA], [lua52], [with_liblua="yes"], - [with_liblua="no (pkg-config cannot find liblua)"] + [ + PKG_CHECK_MODULES([LUA], [lua-5.1], + [with_liblua="yes"], + [ + PKG_CHECK_MODULES([LUA], [lua5.1], + [with_liblua="yes"], + [ + PKG_CHECK_MODULES([LUA], [lua51], + [with_liblua="yes"], + [with_liblua="no (pkg-config cannot find liblua)"] + ) + ] + ) + ] + ) + ] ) ] ) @@ -3674,6 +3766,18 @@ if test "x$with_libmnl" = "xyes"; then [[#include ]] ) + AC_CHECK_MEMBERS([struct rtnl_link_stats.rx_nohandler], + [], + [], + [[#include ]] + ) + + AC_CHECK_MEMBERS([struct rtnl_link_stats64.rx_nohandler], + [], + [], + [[#include ]] + ) + AC_CHECK_LIB([mnl], [mnl_nlmsg_get_payload], [with_libmnl="yes"], [with_libmnl="no (symbol 'mnl_nlmsg_get_payload' not found)"], @@ -3687,6 +3791,7 @@ if test "x$with_libmnl" = "xyes"; then fi AC_SUBST([BUILD_WITH_LIBMNL_CFLAGS]) AC_SUBST([BUILD_WITH_LIBMNL_LIBS]) +AM_CONDITIONAL([HAVE_LIBMNL], [test "x$with_libmnl" = "xyes"]) # }}} # --with-libnetapp {{{ @@ -3794,7 +3899,7 @@ if test "x$with_libnetsnmp" = "xyes"; then LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags" AC_CHECK_LIB([netsnmp], [init_snmp], - [with_libnetsmp="yes"], + [with_libnetsnmp="yes"], [with_libnetsnmp="no (libnetsnmp not found)"] ) @@ -3802,6 +3907,62 @@ if test "x$with_libnetsnmp" = "xyes"; then fi if test "x$with_libnetsnmp" = "xyes"; then + SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags" + + AC_CHECK_LIB([netsnmp], [netsnmp_get_version], + [with_libnetsnmp="yes"], + [with_libnetsnmp="no (couldn't get libnetsnmp version)"] + ) + + LDFLAGS="$SAVE_LDFLAGS" +fi + +if test "x$with_libnetsnmp" = "xyes"; then + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + SAVE_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $with_libnetsnmp_cppflags -Wall -Werror" + LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags" + LIBS="$LIBS -lnetsnmp" + + AC_CACHE_CHECK([whether netsnmp library has old API], + [c_cv_have_netsnmp_old_api], + [ + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM( + [[ + #include + #include + ]], + [[ + netsnmp_variable_list *key = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);; + int val; + u_char type = ASN_INTEGER; + snmp_set_var_value(key, &val, sizeof(val)); + snmp_set_var_typed_value(key, type, &val, sizeof(val)); + return 0; + ]] + ) + ], + [c_cv_have_netsnmp_old_api="no"], + [c_cv_have_netsnmp_old_api="yes"] + ) + ] + ) + + if test "x$c_cv_have_netsnmp_old_api" = "xyes"; then + AC_DEFINE([HAVE_NETSNMP_OLD_API], [1], + ["Define 1 if you have old netsnmp API]") + fi + + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" + LIBS="$SAVE_LIBS" +fi + +if test "x$with_libnetsnmp" = "xyes"; then BUILD_WITH_LIBNETSNMP_CPPFLAGS="$with_libnetsnmp_cppflags" BUILD_WITH_LIBNETSNMP_LDFLAGS="$with_libnetsnmp_ldflags" BUILD_WITH_LIBNETSNMP_LIBS="-lnetsnmp" @@ -3812,7 +3973,7 @@ AC_SUBST([BUILD_WITH_LIBNETSNMP_LDFLAGS]) AC_SUBST([BUILD_WITH_LIBNETSNMP_LIBS]) # }}} -# --with-libnetsmpagent {{{ +# --with-libnetsnmpagent {{{ AC_ARG_WITH([libnetsnmpagent], [AS_HELP_STRING([--with-libnetsnmpagent@<:@=PREFIX@:>@], [Path to libnetsnmpagent.])], [ @@ -4094,7 +4255,7 @@ if test "x$with_libpcap" = "xyes"; then [[#include ]], [[ int val = PCAP_ERROR_IFACE_NOT_UP; - return(val); + return val; ]] ) ], @@ -4699,6 +4860,56 @@ if test "$with_libpython" != "xno"; then fi # }}} --with-libpython +# --with-libqpid_proton {{{ +AC_ARG_WITH([libqpid_proton], + [AS_HELP_STRING([--with-libqpid_proton@<:@=PREFIX@:>@], [Path to libqpid_proton.])], + [ + if test "x$withval" != "xno" && test "x$withval" != "xyes"; then + with_libqpid_proton_cppflags="-I$withval/include" + with_libqpid_proton_ldflags="-L$withval/lib" + with_libqpid_proton="yes" + else + with_libqpid_proton="$withval" + fi + ], + [with_libqpid_proton="yes"] +) + +if test "x$with_libqpid_proton" = "xyes"; then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libqpid_proton_cppflags" + + AC_CHECK_HEADERS([proton/proactor.h], + [with_libqpid_proton="yes"], + [with_libqpid_proton="no (proton/proactor.h not found)"] + ) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi + +if test "x$with_libqpid_proton" = "xyes"; then + SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $with_libqpid_proton_ldflags" + + AC_CHECK_LIB([qpid-proton], [pn_connection], + [with_libqpid_proton="yes"], + [with_libqpid_proton="no (Symbol 'pn_connection' not found)"]) + + LDFLAGS="$SAVE_LDFLAGS" +fi + +if test "x$with_libqpid_proton" = "xyes"; then + BUILD_WITH_LIBQPIDPROTON_CPPFLAGS="$with_libqpid_proton_cppflags" + BUILD_WITH_LIBQPIDPROTON_LDFLAGS="$with_libqpid_proton_ldflags" + BUILD_WITH_LIBQPIDPROTON_LIBS="-lqpid-proton" +fi + +AC_SUBST(BUILD_WITH_LIBQPIDPROTON_CPPFLAGS) +AC_SUBST(BUILD_WITH_LIBQPIDPROTON_LDFLAGS) +AC_SUBST(BUILD_WITH_LIBQPIDPROTON_LIBS) + +# }}} + # --with-librabbitmq {{{ AC_ARG_WITH([librabbitmq], [AS_HELP_STRING([--with-librabbitmq@<:@=PREFIX@:>@], [Path to librabbitmq.])], @@ -5063,6 +5274,27 @@ if test "x$with_libsensors" = "xyes"; then fi if test "x$with_libsensors" = "xyes"; then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_sensors_cppflags" + AC_PREPROC_IFELSE( + [ + AC_LANG_SOURCE( + [[ + #include + #if SENSORS_API_VERSION < 0x400 + #error "required libsensors version >= 3.0" + #endif + ]] + ) + ], + [with_libsensors="yes"], + [with_libsensors="no (sensors library version 3.0.0 or higher is required)"] + ) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi + +if test "x$with_libsensors" = "xyes"; then BUILD_WITH_LIBSENSORS_CPPFLAGS="$with_sensors_cppflags" BUILD_WITH_LIBSENSORS_LDFLAGS="$with_sensors_ldflags" BUILD_WITH_LIBSENSORS_LIBS="-lsensors" @@ -5083,6 +5315,55 @@ PKG_CHECK_MODULES([LIBSIGROK], [libsigrok < 0.4], ) # }}} +# --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.])], @@ -5638,6 +5919,7 @@ AC_SUBST([BUILD_WITH_LIBYAJL_LDFLAGS]) 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 {{{ @@ -5769,32 +6051,41 @@ AC_SUBST([BUILD_WITH_LIBVARNISH_CFLAGS]) 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" @@ -5827,6 +6118,15 @@ fi 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`" @@ -6141,6 +6441,7 @@ plugin_ethstat="no" plugin_fhcount="no" plugin_fscache="no" plugin_gps="no" +plugin_gpu_nvidia="no" plugin_grpc="no" plugin_hugepages="no" plugin_intel_pmu="no" @@ -6159,6 +6460,7 @@ plugin_nfs="no" plugin_numa="no" plugin_ovs_events="no" plugin_ovs_stats="no" +plugin_pcie_errors="no" plugin_perl="no" plugin_pinba="no" plugin_processes="no" @@ -6180,6 +6482,7 @@ plugin_vmem="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" @@ -6237,6 +6540,10 @@ if test "x$ac_system" = "xLinux"; then plugin_ovs_events="yes" plugin_ovs_stats="yes" fi + + if test "x$have_pci_regs_h" = "xyes"; then + plugin_pcie_errors="yes" + fi fi if test "x$ac_system" = "xOpenBSD"; then @@ -6334,6 +6641,10 @@ if test "x$with_libcurl" = "xyes" && test "x$with_libyajl" = "xyes"; then plugin_curl_json="yes" fi +if test "x$with_libcurl" = "xyes" && test "x$with_libssl" = "xyes" && test "x$with_libyajl" = "xyes" && test "x$with_libyajl2" = "xyes"; then + plugin_write_stackdriver="yes" +fi + if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"; then plugin_curl_xml="yes" fi @@ -6397,8 +6708,18 @@ if test "x$with_libgps" = "xyes"; then plugin_gps="yes" fi -if test "x$with_libgrpcpp" = "xyes" && test "x$with_libprotobuf" = "xyes" && test "x$have_protoc3" = "xyes" && test "x$GRPC_CPP_PLUGIN" != "x"; then - plugin_grpc="yes" +plugin_grpc="yes" +if test "x$GRPC_CPP_PLUGIN" = "x"; then + plugin_grpc="no (grpc_cpp_plugin not found)" +fi +if test "x$have_protoc3" != "xyes"; then + plugin_grpc="no (protoc3 not found)" +fi +if test "x$with_libprotobuf" != "xyes"; then + plugin_grpc="no (libprotobuf not found)" +fi +if test "x$with_libgrpcpp" != "xyes"; then + plugin_grpc="no (libgrpc++ not found)" fi if test "x$have_getifaddrs" = "xyes"; then @@ -6512,161 +6833,165 @@ AC_ARG_ENABLE([all-plugins], m4_divert_once([HELP_ENABLE], []) -AC_PLUGIN([aggregation], [yes], [Aggregation plugin]) -AC_PLUGIN([amqp], [$with_librabbitmq], [AMQP 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([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 @@ -6891,6 +7216,7 @@ AC_MSG_RESULT([ libnetapp . . . . . . $with_libnetapp]) AC_MSG_RESULT([ libnetsnmp . . . . . $with_libnetsnmp]) AC_MSG_RESULT([ libnetsnmpagent . . . $with_libnetsnmpagent]) AC_MSG_RESULT([ libnotify . . . . . . $with_libnotify]) +AC_MSG_RESULT([ libnvidia-ml . . . . $with_cuda]) AC_MSG_RESULT([ libopenipmi . . . . . $with_libopenipmipthread]) AC_MSG_RESULT([ liboping . . . . . . $with_liboping]) AC_MSG_RESULT([ libowcapi . . . . . . $with_libowcapi]) @@ -6902,6 +7228,7 @@ AC_MSG_RESULT([ libpqos . . . . . . . $with_libpqos]) AC_MSG_RESULT([ libprotobuf . . . . . $with_libprotobuf]) AC_MSG_RESULT([ libprotobuf-c . . . . $with_libprotobuf_c]) AC_MSG_RESULT([ libpython . . . . . . $with_libpython]) +AC_MSG_RESULT([ libqpid-proton . . . $with_libqpid_proton]) AC_MSG_RESULT([ librabbitmq . . . . . $with_librabbitmq]) AC_MSG_RESULT([ libriemann-client . . $with_libriemann_client]) AC_MSG_RESULT([ librdkafka . . . . . $with_librdkafka]) @@ -6909,6 +7236,7 @@ AC_MSG_RESULT([ librouteros . . . . . $with_librouteros]) AC_MSG_RESULT([ librrd . . . . . . . $with_librrd]) AC_MSG_RESULT([ libsensors . . . . . $with_libsensors]) AC_MSG_RESULT([ libsigrok . . . . . $with_libsigrok]) +AC_MSG_RESULT([ libssl . . . . . . . $with_libssl]) AC_MSG_RESULT([ libstatgrab . . . . . $with_libstatgrab]) AC_MSG_RESULT([ libtokyotyrant . . . $with_libtokyotyrant]) AC_MSG_RESULT([ libudev . . . . . . . $with_libudev]) @@ -6933,6 +7261,7 @@ AC_MSG_RESULT() AC_MSG_RESULT([ Modules:]) AC_MSG_RESULT([ aggregation . . . . . $enable_aggregation]) AC_MSG_RESULT([ amqp . . . . . . . $enable_amqp]) +AC_MSG_RESULT([ amqp1 . . . . . . . $enable_amqp1]) AC_MSG_RESULT([ apache . . . . . . . $enable_apache]) AC_MSG_RESULT([ apcups . . . . . . . $enable_apcups]) AC_MSG_RESULT([ apple_sensors . . . . $enable_apple_sensors]) @@ -6969,6 +7298,7 @@ AC_MSG_RESULT([ filecount . . . . . . $enable_filecount]) 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]) @@ -7022,6 +7352,7 @@ AC_MSG_RESULT([ openvpn . . . . . . . $enable_openvpn]) AC_MSG_RESULT([ oracle . . . . . . . $enable_oracle]) AC_MSG_RESULT([ ovs_events . . . . . $enable_ovs_events]) AC_MSG_RESULT([ ovs_stats . . . . . . $enable_ovs_stats]) +AC_MSG_RESULT([ pcie_errors . . . . . $enable_pcie_errors]) AC_MSG_RESULT([ perl . . . . . . . . $enable_perl]) AC_MSG_RESULT([ pf . . . . . . . . . $enable_pf]) AC_MSG_RESULT([ pinba . . . . . . . . $enable_pinba]) @@ -7079,6 +7410,7 @@ AC_MSG_RESULT([ write_prometheus. . . $enable_write_prometheus]) 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]) @@ -7096,3 +7428,4 @@ if test "x$dependency_warning" = "xyes"; then fi # vim: set fdm=marker sw=2 sts=2 ts=2 et : + diff --git a/contrib/collection3/bin/graph.cgi b/contrib/collection3/bin/graph.cgi index 2b3f2fe1..ba189dea 100755 --- a/contrib/collection3/bin/graph.cgi +++ b/contrib/collection3/bin/graph.cgi @@ -140,10 +140,10 @@ sub main if (param ('debug')) { print < + + DataSources value + DSName value Temp + RRDTitle "Temperature ({instance})" + RRDVerticalLabel "°Celsius" + RRDFormat "%4.1lf°C" + DataSources value RRDTitle "Signal / noise ratio ({instance})" diff --git a/contrib/collection3/share/style.css b/contrib/collection3/share/style.css index 8c12951b..9c3f0ed3 100644 --- a/contrib/collection3/share/style.css +++ b/contrib/collection3/share/style.css @@ -1,3 +1,12 @@ +form { + display: flex; + margin-bottom: 10px; +} + +fieldset { + margin-left: 0; +} + div.graph { } diff --git a/contrib/docker/50docker-apt-conf b/contrib/docker/50docker-apt-conf index 3f898b3b..1b71704f 100644 --- a/contrib/docker/50docker-apt-conf +++ b/contrib/docker/50docker-apt-conf @@ -1,4 +1,4 @@ -APT::Install-Recommends "0"; +APT::Install-Recommends "1"; APT::Install-Suggests "0"; APT::Get::Assume-Yes "1"; APT::Get::AutomaticRemove "1"; diff --git a/contrib/docker/rootfs_prefix/rootfs_prefix.c b/contrib/docker/rootfs_prefix/rootfs_prefix.c index 2b831518..fdacf743 100644 --- a/contrib/docker/rootfs_prefix/rootfs_prefix.c +++ b/contrib/docker/rootfs_prefix/rootfs_prefix.c @@ -1,3 +1,33 @@ +/** + * 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 + * Ruben Kerkhof + **/ + #define _GNU_SOURCE #include diff --git a/contrib/exec-munin.px b/contrib/exec-munin.px index 3e62ce00..5309cc66 100755 --- a/contrib/exec-munin.px +++ b/contrib/exec-munin.px @@ -56,7 +56,7 @@ exit (0); =head1 CONFIGURATION -This script reads it's configuration from F. The +This script reads its configuration from F. The configuration is read using C which understands a Apache-like config syntax, so it's very similar to the F syntax, too. diff --git a/contrib/exec-nagios.px b/contrib/exec-nagios.px index ec13b0a0..b9758ec9 100755 --- a/contrib/exec-nagios.px +++ b/contrib/exec-nagios.px @@ -36,7 +36,7 @@ exit (0); =head1 CONFIGURATION -This script reads it's configuration from F. The +This script reads its configuration from F. The configuration is read using C which understands a Apache-like config syntax, so it's very similar to the F syntax, too. diff --git a/contrib/php-collection/functions.php b/contrib/php-collection/functions.php index fa2badce..c063d57d 100644 --- a/contrib/php-collection/functions.php +++ b/contrib/php-collection/functions.php @@ -536,7 +536,7 @@ function rrd_get_color($code, $line = true) { } /** - * Draw RRD file based on it's structure + * Draw RRD file based on its structure * @host * @plugin * @pinst @@ -635,7 +635,7 @@ function collectd_draw_rrd($host, $plugin, $pinst = null, $type, $tinst = null, } /** - * Draw RRD file based on it's structure + * Draw RRD file based on its structure * @timespan * @host * @plugin diff --git a/contrib/redhat/collectd.spec b/contrib/redhat/collectd.spec index d84b457b..6f86b7e8 100644 --- a/contrib/redhat/collectd.spec +++ b/contrib/redhat/collectd.spec @@ -38,9 +38,13 @@ %global _hardened_build 1 %{?perl_default_filter} +# disable collectd debug by default +%bcond_with debug + # plugins enabled by default %define with_aggregation 0%{!?_without_aggregation:1} %define with_amqp 0%{!?_without_amqp:1} +%define with_amqp1 0%{!?_without_amqp1:1} %define with_apache 0%{!?_without_apache:1} %define with_apcups 0%{!?_without_apcups:1} %define with_ascent 0%{!?_without_ascent:1} @@ -248,7 +252,7 @@ Summary: Statistics collection and monitoring daemon Name: collectd Version: 5.7.1 -Release: 8%{?dist} +Release: 9%{?dist} URL: https://collectd.org Source: https://collectd.org/files/%{name}-%{version}.tar.bz2 License: GPLv2 @@ -277,13 +281,24 @@ every 10 seconds by default. %if %{with_amqp} %package amqp -Summary: AMQP plugin for collectd +Summary: AMQP 0.9 plugin for collectd Group: System Environment/Daemons Requires: %{name}%{?_isa} = %{version}-%{release} BuildRequires: librabbitmq-devel %description amqp -The AMQP plugin transmits or receives values collected by collectd via the -Advanced Message Queuing Protocol (AMQP). +The AMQP 0.9 plugin transmits or receives values collected by collectd via the +Advanced Message Queuing Protocol v0.9 (AMQP). +%endif + +%if %{with_amqp1} +%package amqp1 +Summary: AMQP 1.0 plugin for collectd +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +BuildRequires: qpid-proton-c-devel +%description amqp1 +The AMQP 1.0 plugin transmits or receives values collected by collectd via the +Advanced Message Queuing Protocol v1.0 (AMQP1). %endif %if %{with_apache} @@ -1015,6 +1030,12 @@ Collectd utilities %define _with_amqp --disable-amqp %endif +%if %{with_amqp1} +%define _with_amqp1 --enable-amqp1 +%else +%define _with_amqp1 --disable-amqp1 +%endif + %if %{with_apache} %define _with_apache --enable-apache %else @@ -1360,7 +1381,7 @@ Collectd utilities %if %{with_mcelog} %define _with_mcelog --enable-mcelog %else -%define _with_mbmon --disable-mcelog +%define _with_mcelog --disable-mcelog %endif %if %{with_md} @@ -1872,8 +1893,15 @@ Collectd utilities %define _with_zookeeper --disable-zookeeper %endif +%if %{with debug} +%define _feature_debug --enable-debug +%else +%define _feature_debug --disable-debug +%endif + %configure CFLAGS="%{optflags} -DLT_LAZY_OR_NOW=\"RTLD_LAZY|RTLD_GLOBAL\"" \ %{?_python_config} \ + %{?_feature_debug} \ --disable-static \ --enable-all-plugins=yes \ --enable-match_empty_counter \ @@ -1888,6 +1916,7 @@ Collectd utilities --enable-target_v5upgrade \ %{?_with_aggregation} \ %{?_with_amqp} \ + %{?_with_amqp1} \ %{?_with_apache} \ %{?_with_apcups} \ %{?_with_apple_sensors} \ @@ -2399,6 +2428,11 @@ fi %{_libdir}/%{name}/amqp.so %endif +%if %{with_amqp1} +%files amqp1 +%{_libdir}/%{name}/amqp1.so +%endif + %if %{with_apache} %files apache %{_libdir}/%{name}/apache.so @@ -2737,6 +2771,9 @@ fi %doc contrib/ %changelog +* Thu Sep 28 2017 Jakub Jankowski - 5.7.1-9 +- Fix mbmon/mcelog build options + * Thu Sep 28 2017 xakru - 5.7.1-8 - Add new libcollectdclient/network_parse - Add new libcollectdclient/server diff --git a/contrib/sles10.1/collectd.spec b/contrib/sles10.1/collectd.spec index 2d558bdb..82d709a6 100644 --- a/contrib/sles10.1/collectd.spec +++ b/contrib/sles10.1/collectd.spec @@ -14,7 +14,7 @@ Vendor: Florian octo Forster %description collectd is a small daemon written in C for performance. It reads various -system statistics and updates RRD files, creating them if neccessary. +system statistics and updates RRD files, creating them if necessary. Since the daemon doesn't need to startup every time it wants to update the files it's very fast and easy on the system. Also, the statistics are very fine grained since the files are updated every 10 seconds. diff --git a/contrib/snmp-probe-host.px b/contrib/snmp-probe-host.px index d1a7a886..9776af62 100755 --- a/contrib/snmp-probe-host.px +++ b/contrib/snmp-probe-host.px @@ -306,9 +306,9 @@ snmp-probe-host.px - Find out what information an SNMP device provides. The C script can be used to automatically generate SNMP configuration snippets for collectd's snmp plugin (see L). -This script parses the collectd configuration and detecs all "data" blocks that +This script parses the collectd configuration and detects all "data" blocks that are defined for the SNMP plugin. It then queries the device specified on the -command line for all OIDs and registeres which OIDs could be answered correctly +command line for all OIDs and registers which OIDs could be answered correctly and which resulted in an error. With that information the script figures out which "data" blocks can be used with this hosts and prints an appropriate "host" block to standard output. diff --git a/contrib/systemd.collectd.service b/contrib/systemd.collectd.service index 9c037a4e..c5b1142b 100644 --- a/contrib/systemd.collectd.service +++ b/contrib/systemd.collectd.service @@ -22,6 +22,7 @@ ProtectHome=true # intel_pmu CAP_SYS_ADMIN # iptables CAP_NET_ADMIN # ping CAP_NET_RAW +# processes CAP_NET_ADMIN (CollectDelayAccounting only) # smart CAP_SYS_RAWIO # turbostat CAP_SYS_RAWIO # diff --git a/docs/README.virt.md b/docs/README.virt.md index a80e9eac..9a63a18a 100644 --- a/docs/README.virt.md +++ b/docs/README.virt.md @@ -43,7 +43,7 @@ Collectd will just use the domain tags, but never enforces or requires them. It is up to an external entity, like a software management system, to attach and manage the tags to the domain. -Please note that unless you have such tag-aware management sofware, +Please note that unless you have such tag-aware management software, it most likely make no sense to enable more than one reader instance on your setup. @@ -179,8 +179,8 @@ API, but it is rather a byproduct of how libvirt and QEMU interact. Whenever we query more than one VM, we should take care to avoid that one blocked VM prevent other, well behaving VMs to be queried. We don't want one rogue VM to disrupt well-behaving VMs. -Unfortunately, any way we enumerate VMs, either implicitely, using the libvirt bulk stats API, -or explicitely, listing all libvirt domains and query each one in turn, we may unpredictably encounter +Unfortunately, any way we enumerate VMs, either implicitly, using the libvirt bulk stats API, +or explicitly, listing all libvirt domains and query each one in turn, we may unpredictably encounter one unresponsive VM. There are many possible approaches to deal with this issue. The virt plugin supports @@ -237,4 +237,3 @@ The QEMU core, including the handling of the QMP protocol, is single-threaded. All the above combined make it possible for a client to block forever waiting for one QMP request, if QEMU itself is blocked. The most likely cause of block is I/O, and this is especially true considering how QEMU is used in a datacenter. - diff --git a/docs/review_comments.md b/docs/review_comments.md new file mode 100644 index 00000000..9bad458e --- /dev/null +++ b/docs/review_comments.md @@ -0,0 +1,96 @@ +# 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); +``` diff --git a/docs/review_comments_example.png b/docs/review_comments_example.png new file mode 100644 index 00000000..81eb4582 Binary files /dev/null and b/docs/review_comments_example.png differ diff --git a/gnulib b/gnulib new file mode 160000 index 00000000..2f8140bc --- /dev/null +++ b/gnulib @@ -0,0 +1 @@ +Subproject commit 2f8140bc8ce5501e31dcc665b42b5df64f84c20c diff --git a/src/aggregation.c b/src/aggregation.c index 0ed97ae1..a8021996 100644 --- a/src/aggregation.c +++ b/src/aggregation.c @@ -48,12 +48,12 @@ struct aggregation_s /* {{{ */ char *set_plugin_instance; char *set_type_instance; - _Bool calc_num; - _Bool calc_sum; - _Bool calc_average; - _Bool calc_min; - _Bool calc_max; - _Bool calc_stddev; + bool calc_num; + bool calc_sum; + bool calc_average; + bool calc_min; + bool calc_max; + bool calc_stddev; }; /* }}} */ typedef struct aggregation_s aggregation_t; @@ -83,27 +83,25 @@ struct agg_instance_s /* {{{ */ agg_instance_t *next; }; /* }}} */ -static lookup_t *lookup = NULL; +static lookup_t *lookup; static pthread_mutex_t agg_instance_list_lock = PTHREAD_MUTEX_INITIALIZER; -static agg_instance_t *agg_instance_list_head = NULL; +static agg_instance_t *agg_instance_list_head; -static _Bool agg_is_regex(char const *str) /* {{{ */ +static bool agg_is_regex(char const *str) /* {{{ */ { - size_t len; - if (str == NULL) - return 0; + return false; - len = strlen(str); + size_t len = strlen(str); if (len < 3) - return 0; + return false; if ((str[0] == '/') && (str[len - 1] == '/')) - return 1; + return true; else - return 0; -} /* }}} _Bool agg_is_regex */ + return false; +} /* }}} bool agg_is_regex */ static void agg_destroy(aggregation_t *agg) /* {{{ */ { @@ -227,11 +225,9 @@ static int agg_instance_create_name(agg_instance_t *inst, /* {{{ */ static agg_instance_t *agg_instance_create(data_set_t const *ds, /* {{{ */ value_list_t const *vl, aggregation_t *agg) { - agg_instance_t *inst; - DEBUG("aggregation plugin: Creating new instance."); - inst = calloc(1, sizeof(*inst)); + agg_instance_t *inst = calloc(1, sizeof(*inst)); if (inst == NULL) { ERROR("aggregation plugin: calloc() failed."); return NULL; @@ -282,8 +278,6 @@ static agg_instance_t *agg_instance_create(data_set_t const *ds, /* {{{ */ * and non-zero otherwise. */ static int agg_instance_update(agg_instance_t *inst, /* {{{ */ data_set_t const *ds, value_list_t const *vl) { - gauge_t *rate; - if (ds->ds_num != 1) { ERROR("aggregation plugin: The \"%s\" type (data set) has more than one " "data source. This is currently not supported by this plugin. " @@ -292,7 +286,7 @@ static int agg_instance_update(agg_instance_t *inst, /* {{{ */ return EINVAL; } - rate = uc_get_rate(ds, vl); + gauge_t *rate = uc_get_rate(ds, vl); if (rate == NULL) { char ident[6 * DATA_MAX_NAME_LEN]; FORMAT_VL(ident, sizeof(ident), vl); @@ -328,16 +322,15 @@ static int agg_instance_read_func(agg_instance_t *inst, /* {{{ */ rate_to_value_state_t *state, value_list_t *vl, char const *pi_prefix, cdtime_t t) { - value_t v; - int status; - if (pi_prefix[0] != 0) subst_string(vl->plugin_instance, sizeof(vl->plugin_instance), pi_prefix, AGG_FUNC_PLACEHOLDER, func); else sstrncpy(vl->plugin_instance, func, sizeof(vl->plugin_instance)); - status = rate_to_value(&v, rate, state, inst->ds_type, t); + value_t v; + + int status = rate_to_value(&v, rate, state, inst->ds_type, t); if (status != 0) { /* If this is the first iteration and rate_to_value() was asked to return a * COUNTER or a DERIVE, it will return EAGAIN. Catch this and handle @@ -473,8 +466,6 @@ static void agg_lookup_free_obj_callback(void *user_obj) /* {{{ */ static int agg_config_handle_group_by(oconfig_item_t const *ci, /* {{{ */ aggregation_t *agg) { for (int i = 0; i < ci->values_num; i++) { - char const *value; - if (ci->values[i].type != OCONFIG_TYPE_STRING) { ERROR("aggregation plugin: Argument %i of the \"GroupBy\" option " "is not a string.", @@ -482,7 +473,7 @@ static int agg_config_handle_group_by(oconfig_item_t const *ci, /* {{{ */ continue; } - value = ci->values[i].value.string; + const char *value = ci->values[i].value.string; if (strcasecmp("Host", value) == 0) agg->group_by |= LU_GROUP_BY_HOST; @@ -580,7 +571,7 @@ static int agg_config_aggregation(oconfig_item_t *ci) /* {{{ */ agg->regex_fields |= LU_GROUP_BY_TYPE_INSTANCE; /* Sanity checking */ - _Bool is_valid = 1; + bool is_valid = true; if (strcmp("/.*/", agg->ident.type) == 0) /* {{{ */ { ERROR("aggregation plugin: It appears you did not specify the required " @@ -589,13 +580,13 @@ static int agg_config_aggregation(oconfig_item_t *ci) /* {{{ */ "Type \"%s\", TypeInstance \"%s\")", agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance, agg->ident.type, agg->ident.type_instance); - is_valid = 0; + is_valid = false; } else if (strchr(agg->ident.type, '/') != NULL) { ERROR("aggregation plugin: The \"Type\" may not contain the '/' " "character. Especially, it may not be a regex. The current " "value is \"%s\".", agg->ident.type); - is_valid = 0; + is_valid = false; } /* }}} */ /* Check that there is at least one regex field without a grouping. {{{ */ @@ -608,7 +599,7 @@ static int agg_config_aggregation(oconfig_item_t *ci) /* {{{ */ "Type \"%s\", TypeInstance \"%s\")", agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance, agg->ident.type, agg->ident.type_instance); - is_valid = 0; + is_valid = false; } /* }}} */ /* Check that all grouping fields are regular expressions. {{{ */ @@ -620,7 +611,7 @@ static int agg_config_aggregation(oconfig_item_t *ci) /* {{{ */ "Type \"%s\", TypeInstance \"%s\")", agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance, agg->ident.type, agg->ident.type_instance); - is_valid = 0; + is_valid = false; } /* }}} */ if (!agg->calc_num && !agg->calc_sum && !agg->calc_average /* {{{ */ @@ -631,7 +622,7 @@ static int agg_config_aggregation(oconfig_item_t *ci) /* {{{ */ "Type \"%s\", TypeInstance \"%s\")", agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance, agg->ident.type, agg->ident.type_instance); - is_valid = 0; + is_valid = false; } /* }}} */ if (!is_valid) { /* {{{ */ @@ -687,11 +678,8 @@ static int agg_config(oconfig_item_t *ci) /* {{{ */ static int agg_read(void) /* {{{ */ { - cdtime_t t; - int success; - - t = cdtime(); - success = 0; + cdtime_t t = cdtime(); + int success = 0; pthread_mutex_lock(&agg_instance_list_lock); @@ -708,9 +696,7 @@ static int agg_read(void) /* {{{ */ for (agg_instance_t *this = agg_instance_list_head; this != NULL; this = this->next) { - int status; - - status = agg_instance_read(this, t); + int status = agg_instance_read(this, t); if (status != 0) WARNING("aggregation plugin: Reading an aggregation instance " "failed with status %i.", @@ -726,9 +712,7 @@ static int agg_read(void) /* {{{ */ static int agg_write(data_set_t const *ds, value_list_t const *vl, /* {{{ */ __attribute__((unused)) user_data_t *user_data) { - _Bool created_by_aggregation = 0; - int status; - + bool created_by_aggregation = false; /* Ignore values that were created by the aggregation plugin to avoid weird * effects. */ (void)meta_data_get_boolean(vl->meta, "aggregation:created", @@ -736,6 +720,8 @@ static int agg_write(data_set_t const *ds, value_list_t const *vl, /* {{{ */ if (created_by_aggregation) return 0; + int status; + if (lookup == NULL) status = ENOENT; else { diff --git a/src/amqp.c b/src/amqp.c index 467b7ff4..281130b1 100644 --- a/src/amqp.c +++ b/src/amqp.c @@ -66,7 +66,7 @@ int amqp_socket_close(amqp_socket_t *); * Data types */ struct camqp_config_s { - _Bool publish; + bool publish; char *name; char *host; @@ -83,7 +83,7 @@ struct camqp_config_s { /* publish only */ uint8_t delivery_mode; - _Bool store_rates; + bool store_rates; int format; /* publish & graphite format only */ char *prefix; @@ -94,8 +94,8 @@ struct camqp_config_s { /* subscribe only */ char *exchange_type; char *queue; - _Bool queue_durable; - _Bool queue_auto_delete; + bool queue_durable; + bool queue_auto_delete; amqp_connection_state_t connection; pthread_mutex_t lock; @@ -111,9 +111,9 @@ static const char *def_user = "guest"; static const char *def_password = "guest"; static const char *def_exchange = "amq.fanout"; -static pthread_t *subscriber_threads = NULL; -static size_t subscriber_threads_num = 0; -static _Bool subscriber_threads_running = 1; +static pthread_t *subscriber_threads; +static size_t subscriber_threads_num; +static bool subscriber_threads_running = true; #define CONF(c, f) (((c)->f != NULL) ? (c)->f : def_##f) @@ -176,16 +176,16 @@ static char *camqp_bytes_cstring(amqp_bytes_t *in) /* {{{ */ return ret; } /* }}} char *camqp_bytes_cstring */ -static _Bool camqp_is_error(camqp_config_t *conf) /* {{{ */ +static bool camqp_is_error(camqp_config_t *conf) /* {{{ */ { amqp_rpc_reply_t r; r = amqp_get_rpc_reply(conf->connection); if (r.reply_type == AMQP_RESPONSE_NORMAL) - return 0; + return false; - return 1; -} /* }}} _Bool camqp_is_error */ + return true; +} /* }}} bool camqp_is_error */ static char *camqp_strerror(camqp_config_t *conf, /* {{{ */ char *buffer, size_t buffer_size) { @@ -396,7 +396,7 @@ static int camqp_setup_queue(camqp_config_t *conf) /* {{{ */ static int camqp_connect(camqp_config_t *conf) /* {{{ */ { - static time_t last_connect_time = 0; + static time_t last_connect_time; amqp_rpc_reply_t reply; int status; @@ -441,10 +441,8 @@ static int camqp_connect(camqp_config_t *conf) /* {{{ */ status = amqp_socket_open(socket, CONF(conf, host), conf->port); if (status < 0) { - char errbuf[1024]; status *= -1; - ERROR("amqp plugin: amqp_socket_open failed: %s", - sstrerror(status, errbuf, sizeof(errbuf))); + ERROR("amqp plugin: amqp_socket_open failed: %s", STRERROR(status)); amqp_destroy_connection(conf->connection); conf->connection = NULL; return status; @@ -454,10 +452,8 @@ static int camqp_connect(camqp_config_t *conf) /* {{{ */ /* this interface is deprecated as of rabbitmq-c 0.4 */ sockfd = amqp_open_socket(CONF(conf, host), conf->port); if (sockfd < 0) { - char errbuf[1024]; status = (-1) * sockfd; - ERROR("amqp plugin: amqp_open_socket failed: %s", - sstrerror(status, errbuf, sizeof(errbuf))); + ERROR("amqp plugin: amqp_open_socket failed: %s", STRERROR(status)); amqp_destroy_connection(conf->connection); conf->connection = NULL; return status; @@ -507,7 +503,7 @@ static int camqp_connect(camqp_config_t *conf) /* {{{ */ static int camqp_shutdown(void) /* {{{ */ { - DEBUG("amqp plugin: Shutting down %zu subscriber threads.", + DEBUG("amqp plugin: Shutting down %" PRIsz " subscriber threads.", subscriber_threads_num); subscriber_threads_running = 0; @@ -545,10 +541,8 @@ static int camqp_read_body(camqp_config_t *conf, /* {{{ */ while (received < body_size) { status = amqp_simple_wait_frame(conf->connection, &frame); if (status < 0) { - char errbuf[1024]; status = (-1) * status; - ERROR("amqp plugin: amqp_simple_wait_frame failed: %s", - sstrerror(status, errbuf, sizeof(errbuf))); + ERROR("amqp plugin: amqp_simple_wait_frame failed: %s", STRERROR(status)); camqp_close_connection(conf); return status; } @@ -597,10 +591,8 @@ static int camqp_read_header(camqp_config_t *conf) /* {{{ */ status = amqp_simple_wait_frame(conf->connection, &frame); if (status < 0) { - char errbuf[1024]; status = (-1) * status; - ERROR("amqp plugin: amqp_simple_wait_frame failed: %s", - sstrerror(status, errbuf, sizeof(errbuf))); + ERROR("amqp plugin: amqp_simple_wait_frame failed: %s", STRERROR(status)); camqp_close_connection(conf); return status; } @@ -693,9 +685,7 @@ static int camqp_subscribe_init(camqp_config_t *conf) /* {{{ */ status = plugin_thread_create(tmp, /* attr = */ NULL, camqp_subscribe_thread, conf, "amqp subscribe"); if (status != 0) { - char errbuf[1024]; - ERROR("amqp plugin: pthread_create failed: %s", - sstrerror(status, errbuf, sizeof(errbuf))); + ERROR("amqp plugin: pthread_create failed: %s", STRERROR(status)); return status; } @@ -835,7 +825,7 @@ static int camqp_config_set_format(oconfig_item_t *ci, /* {{{ */ } /* }}} int config_set_string */ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */ - _Bool publish) { + bool publish) { camqp_config_t *conf; int status; @@ -860,7 +850,7 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */ /* publish only */ conf->delivery_mode = CAMQP_DM_VOLATILE; - conf->store_rates = 0; + conf->store_rates = false; conf->graphite_flags = 0; /* publish & graphite only */ conf->prefix = NULL; @@ -869,8 +859,8 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */ /* subscribe only */ conf->exchange_type = NULL; conf->queue = NULL; - conf->queue_durable = 0; - conf->queue_auto_delete = 1; + conf->queue_durable = false; + conf->queue_auto_delete = true; /* general */ conf->connection = NULL; pthread_mutex_init(&conf->lock, /* attr = */ NULL); @@ -912,7 +902,7 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */ else if (strcasecmp("RoutingKey", child->key) == 0) status = cf_util_get_string(child, &conf->routing_key); else if ((strcasecmp("Persistent", child->key) == 0) && publish) { - _Bool tmp = 0; + bool tmp = 0; status = cf_util_get_boolean(child, &tmp); if (tmp) conf->delivery_mode = CAMQP_DM_PERSISTENT; @@ -982,10 +972,11 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */ char cbname[128]; snprintf(cbname, sizeof(cbname), "amqp/%s", conf->name); - status = plugin_register_write( - cbname, camqp_write, &(user_data_t){ - .data = conf, .free_func = camqp_config_free, - }); + status = + plugin_register_write(cbname, camqp_write, + &(user_data_t){ + .data = conf, .free_func = camqp_config_free, + }); if (status != 0) { camqp_config_free(conf); return status; @@ -1007,9 +998,9 @@ static int camqp_config(oconfig_item_t *ci) /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp("Publish", child->key) == 0) - camqp_config_connection(child, /* publish = */ 1); + camqp_config_connection(child, /* publish = */ true); else if (strcasecmp("Subscribe", child->key) == 0) - camqp_config_connection(child, /* publish = */ 0); + camqp_config_connection(child, /* publish = */ false); else WARNING("amqp plugin: Ignoring unknown config option \"%s\".", child->key); diff --git a/src/amqp1.c b/src/amqp1.c new file mode 100644 index 00000000..87bb50cf --- /dev/null +++ b/src/amqp1.c @@ -0,0 +1,773 @@ +/** + * collectd - src/amqp1.c + * Copyright(c) 2017 Red Hat 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: + * Andy Smith + */ + +#include "collectd.h" + +#include "common.h" +#include "plugin.h" +#include "utils_cmd_putval.h" +#include "utils_deq.h" +#include "utils_format_graphite.h" +#include "utils_format_json.h" +#include "utils_random.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BUFSIZE 8192 +#define AMQP1_FORMAT_JSON 0 +#define AMQP1_FORMAT_COMMAND 1 +#define AMQP1_FORMAT_GRAPHITE 2 + +typedef struct amqp1_config_transport_s { + DEQ_LINKS(struct amqp1_config_transport_s); + char *name; + char *host; + char *port; + char *user; + char *password; + char *address; + int retry_delay; +} amqp1_config_transport_t; + +typedef struct amqp1_config_instance_s { + DEQ_LINKS(struct amqp1_config_instance_s); + char *name; + bool notify; + uint8_t format; + unsigned int graphite_flags; + bool store_rates; + char *prefix; + char *postfix; + char escape_char; + bool pre_settle; + char send_to[1024]; +} amqp1_config_instance_t; + +DEQ_DECLARE(amqp1_config_instance_t, amqp1_config_instance_list_t); + +typedef struct cd_message_s { + DEQ_LINKS(struct cd_message_s); + pn_rwbytes_t mbuf; + amqp1_config_instance_t *instance; +} cd_message_t; + +DEQ_DECLARE(cd_message_t, cd_message_list_t); + +/* + * Globals + */ +static pn_connection_t *conn; +static pn_link_t *sender; +static pn_proactor_t *proactor; +static pthread_mutex_t send_lock; +static cd_message_list_t out_messages; +static uint64_t cd_tag = 1; +static uint64_t acknowledged; +static amqp1_config_transport_t *transport; +static bool stopping; +static bool event_thread_running; +static pthread_t event_thread_id; + +/* + * Functions + */ +static void cd_message_free(cd_message_t *cdm) { + free(cdm->mbuf.start); + free(cdm); +} /* }}} void cd_message_free */ + +static int amqp1_send_out_messages(pn_link_t *link) /* {{{ */ +{ + uint64_t dtag; + cd_message_list_t to_send; + cd_message_t *cdm; + int link_credit = pn_link_credit(link); + int event_count = 0; + pn_delivery_t *dlv; + + if (stopping) { + return 0; + } + + DEQ_INIT(to_send); + + pthread_mutex_lock(&send_lock); + + if (link_credit > 0) { + dtag = cd_tag; + cdm = DEQ_HEAD(out_messages); + while (cdm) { + DEQ_REMOVE_HEAD(out_messages); + DEQ_INSERT_TAIL(to_send, cdm); + if (DEQ_SIZE(to_send) == (size_t)link_credit) + break; + cdm = DEQ_HEAD(out_messages); + } + cd_tag += DEQ_SIZE(to_send); + } + + pthread_mutex_unlock(&send_lock); + + /* message is already formatted and encoded */ + cdm = DEQ_HEAD(to_send); + while (cdm) { + DEQ_REMOVE_HEAD(to_send); + dtag++; + dlv = pn_delivery(link, pn_dtag((const char *)&dtag, sizeof(dtag))); + pn_link_send(link, cdm->mbuf.start, cdm->mbuf.size); + pn_link_advance(link); + if (cdm->instance->pre_settle == true) { + pn_delivery_settle(dlv); + } + event_count++; + cd_message_free(cdm); + cdm = DEQ_HEAD(to_send); + } + + return event_count; +} /* }}} int amqp1_send_out_messages */ + +static void check_condition(pn_event_t *e, pn_condition_t *cond) /* {{{ */ +{ + if (pn_condition_is_set(cond)) { + ERROR("amqp1 plugin: %s: %s: %s", pn_event_type_name(pn_event_type(e)), + pn_condition_get_name(cond), pn_condition_get_description(cond)); + pn_connection_close(pn_event_connection(e)); + conn = NULL; + } +} /* }}} void check_condition */ + +static bool handle(pn_event_t *event) /* {{{ */ +{ + + switch (pn_event_type(event)) { + + case PN_CONNECTION_INIT: { + conn = pn_event_connection(event); + pn_connection_set_container(conn, transport->name); + pn_connection_open(conn); + pn_session_t *ssn = pn_session(conn); + pn_session_open(ssn); + sender = pn_sender(ssn, "cd-sender"); + pn_link_set_snd_settle_mode(sender, PN_SND_MIXED); + pn_link_open(sender); + break; + } + + case PN_LINK_FLOW: { + /* peer has given us credit, send outbound messages */ + amqp1_send_out_messages(sender); + break; + } + + case PN_DELIVERY: { + /* acknowledgement from peer that a message was delivered */ + pn_delivery_t *dlv = pn_event_delivery(event); + if (pn_delivery_remote_state(dlv) == PN_ACCEPTED) { + pn_delivery_settle(dlv); + acknowledged++; + } + break; + } + + case PN_CONNECTION_WAKE: { + if (!stopping) { + amqp1_send_out_messages(sender); + } + break; + } + + case PN_TRANSPORT_CLOSED: { + check_condition(event, pn_transport_condition(pn_event_transport(event))); + break; + } + + case PN_CONNECTION_REMOTE_CLOSE: { + check_condition(event, + pn_session_remote_condition(pn_event_session(event))); + pn_connection_close(pn_event_connection(event)); + break; + } + + case PN_SESSION_REMOTE_CLOSE: { + check_condition(event, + pn_session_remote_condition(pn_event_session(event))); + pn_connection_close(pn_event_connection(event)); + break; + } + + case PN_LINK_REMOTE_CLOSE: + case PN_LINK_REMOTE_DETACH: { + check_condition(event, pn_link_remote_condition(pn_event_link(event))); + pn_connection_close(pn_event_connection(event)); + break; + } + + case PN_PROACTOR_INACTIVE: { + return false; + } + + default: + break; + } + return true; +} /* }}} bool handle */ + +static void *event_thread(void __attribute__((unused)) * arg) /* {{{ */ +{ + char addr[PN_MAX_ADDR]; + cd_message_t *cdm; + + /* setup proactor */ + proactor = pn_proactor(); + pn_proactor_addr(addr, sizeof(addr), transport->host, transport->port); + + while (!stopping) { + /* make connection */ + conn = pn_connection(); + if (transport->user != NULL) { + pn_connection_set_user(conn, transport->user); + pn_connection_set_password(conn, transport->password); + } + pn_proactor_connect(proactor, conn, addr); + + bool engine_running = true; + while (engine_running && !stopping) { + pn_event_batch_t *events = pn_proactor_wait(proactor); + pn_event_t *e; + while ((e = pn_event_batch_next(events))) { + engine_running = handle(e); + if (!engine_running) { + break; + } + } + pn_proactor_done(proactor, events); + } + + pn_proactor_release_connection(conn); + + DEBUG("amqp1 plugin: retrying connection"); + int delay = transport->retry_delay; + while (delay-- > 0 && !stopping) { + sleep(1.0); + } + } + + pn_proactor_disconnect(proactor, NULL); + + /* Free the remaining out_messages */ + cdm = DEQ_HEAD(out_messages); + while (cdm) { + DEQ_REMOVE_HEAD(out_messages); + cd_message_free(cdm); + cdm = DEQ_HEAD(out_messages); + } + + event_thread_running = false; + + return NULL; +} /* }}} void event_thread */ + +static int encqueue(cd_message_t *cdm, + amqp1_config_instance_t *instance) /* {{{ */ +{ + /* encode message */ + pn_message_t *message = pn_message(); + pn_message_set_address(message, instance->send_to); + pn_data_t *body = pn_message_body(message); + pn_data_clear(body); + pn_data_put_binary(body, pn_bytes(cdm->mbuf.size, cdm->mbuf.start)); + pn_data_exit(body); + + /* put_binary copies and stores so ok to use mbuf */ + cdm->mbuf.size = BUFSIZE; + + int status; + char *start; + while ((status = pn_message_encode(message, cdm->mbuf.start, + &cdm->mbuf.size)) == PN_OVERFLOW) { + DEBUG("amqp1 plugin: increasing message buffer size %zu", cdm->mbuf.size); + cdm->mbuf.size *= 2; + start = realloc(cdm->mbuf.start, cdm->mbuf.size); + if (start == NULL) { + status = -1; + break; + } else { + cdm->mbuf.start = start; + } + } + + if (status != 0) { + ERROR("amqp1 plugin: error encoding message: %s", + pn_error_text(pn_message_error(message))); + pn_message_free(message); + return -1; + } + + pthread_mutex_lock(&send_lock); + DEQ_INSERT_TAIL(out_messages, cdm); + pthread_mutex_unlock(&send_lock); + + pn_message_free(message); + + /* activate the sender */ + if (conn) { + pn_connection_wake(conn); + } + + return 0; +} /* }}} int encqueue */ + +static int amqp1_notify(notification_t const *n, + user_data_t *user_data) /* {{{ */ +{ + int status = 0; + size_t bfree = BUFSIZE; + size_t bfill = 0; + size_t bufsize = BUFSIZE; + + if (n == NULL || user_data == NULL) + return EINVAL; + + amqp1_config_instance_t *instance = user_data->data; + + if (instance->notify != true) { + ERROR("amqp1 plugin: write notification failed"); + } + + cd_message_t *cdm = malloc(sizeof(*cdm)); + if (cdm == NULL) { + ERROR("amqp1 plugin: notify failed"); + return -1; + } + + DEQ_ITEM_INIT(cdm); + char *start = malloc(bufsize); + if (start == NULL) { + ERROR("amqp1 plugin: malloc failed"); + free(cdm); + return -1; + } + cdm->mbuf.size = bufsize; + cdm->mbuf.start = start; + cdm->instance = instance; + + switch (instance->format) { + case AMQP1_FORMAT_JSON: + format_json_initialize(cdm->mbuf.start, &bfill, &bfree); + status = format_json_notification(cdm->mbuf.start, bufsize, n); + if (status != 0) { + ERROR("amqp1 plugin: formatting notification failed"); + cd_message_free(cdm); + return status; + } + cdm->mbuf.size = strlen(cdm->mbuf.start); + if (cdm->mbuf.size >= BUFSIZE) { + ERROR("amqp1 plugin: notify format json failed"); + cd_message_free(cdm); + return -1; + } + break; + default: + ERROR("amqp1 plugin: Invalid notify format (%i).", instance->format); + cd_message_free(cdm); + return -1; + } + + /* encode message and place on outbound queue */ + status = encqueue(cdm, instance); + if (status != 0) { + ERROR("amqp1 plugin: notify enqueue failed"); + cd_message_free(cdm); + } + return status; + +} /* }}} int amqp1_notify */ + +static int amqp1_write(const data_set_t *ds, const value_list_t *vl, /* {{{ */ + user_data_t *user_data) { + int status = 0; + size_t bfree = BUFSIZE; + size_t bfill = 0; + size_t bufsize = BUFSIZE; + + if (ds == NULL || vl == NULL || transport == NULL || user_data == NULL) + return EINVAL; + + amqp1_config_instance_t *instance = user_data->data; + + if (instance->notify != false) { + ERROR("amqp1 plugin: write failed"); + } + + cd_message_t *cdm = malloc(sizeof(*cdm)); + if (cdm == NULL) { + ERROR("amqp1 plugin: malloc failed."); + return -1; + } + DEQ_ITEM_INIT(cdm); + char *start = malloc(bufsize); + if (start == NULL) { + ERROR("amqp1 plugin: malloc failed."); + free(cdm); + return -1; + } + cdm->mbuf.size = bufsize; + cdm->mbuf.start = start; + cdm->instance = instance; + + switch (instance->format) { + case AMQP1_FORMAT_COMMAND: + status = cmd_create_putval((char *)cdm->mbuf.start, bufsize, ds, vl); + if (status != 0) { + ERROR("amqp1 plugin: cmd_create_putval failed with status %i.", status); + cd_message_free(cdm); + return status; + } + cdm->mbuf.size = strlen(cdm->mbuf.start); + if (cdm->mbuf.size >= BUFSIZE) { + ERROR("amqp1 plugin: format cmd failed"); + cd_message_free(cdm); + return -1; + } + break; + case AMQP1_FORMAT_JSON: + 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); + if (status != 0) { + ERROR("amqp1 plugin: format_json_finalize failed with status %i.", + status); + cd_message_free(cdm); + return status; + } + cdm->mbuf.size = strlen(cdm->mbuf.start); + if (cdm->mbuf.size >= BUFSIZE) { + ERROR("amqp1 plugin: format json failed"); + cd_message_free(cdm); + return -1; + } + break; + case AMQP1_FORMAT_GRAPHITE: + status = format_graphite((char *)cdm->mbuf.start, bufsize, ds, vl, + instance->prefix, instance->postfix, + instance->escape_char, instance->graphite_flags); + if (status != 0) { + ERROR("amqp1 plugin: format_graphite failed with status %i.", status); + cd_message_free(cdm); + return status; + } + cdm->mbuf.size = strlen(cdm->mbuf.start); + if (cdm->mbuf.size >= BUFSIZE) { + ERROR("amqp1 plugin: format graphite failed"); + cd_message_free(cdm); + return -1; + } + break; + default: + ERROR("amqp1 plugin: Invalid write format (%i).", instance->format); + cd_message_free(cdm); + return -1; + } + + /* encode message and place on outbound queue */ + status = encqueue(cdm, instance); + if (status != 0) { + ERROR("amqp1 plugin: write enqueue failed"); + cd_message_free(cdm); + } + return status; + +} /* }}} int amqp1_write */ + +static void amqp1_config_transport_free(void *ptr) /* {{{ */ +{ + amqp1_config_transport_t *transport = ptr; + + if (transport == NULL) + return; + + sfree(transport->name); + sfree(transport->host); + sfree(transport->port); + sfree(transport->user); + sfree(transport->password); + sfree(transport->address); + + sfree(transport); +} /* }}} void amqp1_config_transport_free */ + +static void amqp1_config_instance_free(void *ptr) /* {{{ */ +{ + amqp1_config_instance_t *instance = ptr; + + if (instance == NULL) + return; + + sfree(instance->name); + sfree(instance->prefix); + sfree(instance->postfix); + + sfree(instance); +} /* }}} void amqp1_config_instance_free */ + +static int amqp1_config_instance(oconfig_item_t *ci) /* {{{ */ +{ + amqp1_config_instance_t *instance = calloc(1, sizeof(*instance)); + if (instance == NULL) { + ERROR("amqp1 plugin: calloc failed."); + return ENOMEM; + } + + int status = cf_util_get_string(ci, &instance->name); + if (status != 0) { + sfree(instance); + return status; + } + + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("PreSettle", child->key) == 0) + status = cf_util_get_boolean(child, &instance->pre_settle); + else if (strcasecmp("Notify", child->key) == 0) + status = cf_util_get_boolean(child, &instance->notify); + else if (strcasecmp("Format", child->key) == 0) { + char *key = NULL; + status = cf_util_get_string(child, &key); + if (status != 0) + return status; + assert(key != NULL); + if (strcasecmp(key, "Command") == 0) { + instance->format = AMQP1_FORMAT_COMMAND; + } else if (strcasecmp(key, "Graphite") == 0) { + instance->format = AMQP1_FORMAT_GRAPHITE; + } else if (strcasecmp(key, "JSON") == 0) { + instance->format = AMQP1_FORMAT_JSON; + } else { + WARNING("amqp1 plugin: Invalid format string: %s", key); + } + sfree(key); + } else if (strcasecmp("StoreRates", child->key) == 0) + status = cf_util_get_boolean(child, &instance->store_rates); + else if (strcasecmp("GraphiteSeparateInstances", child->key) == 0) + status = cf_util_get_flag(child, &instance->graphite_flags, + GRAPHITE_SEPARATE_INSTANCES); + else if (strcasecmp("GraphiteAlwaysAppendDS", child->key) == 0) + status = cf_util_get_flag(child, &instance->graphite_flags, + GRAPHITE_ALWAYS_APPEND_DS); + else if (strcasecmp("GraphitePreserveSeparator", child->key) == 0) + status = cf_util_get_flag(child, &instance->graphite_flags, + GRAPHITE_PRESERVE_SEPARATOR); + else if (strcasecmp("GraphitePrefix", child->key) == 0) + status = cf_util_get_string(child, &instance->prefix); + else if (strcasecmp("GraphitePostfix", child->key) == 0) + status = cf_util_get_string(child, &instance->postfix); + else if (strcasecmp("GraphiteEscapeChar", child->key) == 0) { + char *tmp_buff = NULL; + status = cf_util_get_string(child, &tmp_buff); + if (status == 0) { + if (strlen(tmp_buff) > 1) + WARNING("amqp1 plugin: The option \"GraphiteEscapeChar\" handles " + "only one character. Others will be ignored."); + instance->escape_char = tmp_buff[0]; + } + sfree(tmp_buff); + } else + WARNING("amqp1 plugin: Ignoring unknown " + "instance configuration option " + "\"%s\".", + child->key); + if (status != 0) + break; + } + + if (status != 0) { + amqp1_config_instance_free(instance); + return status; + } else { + char tpname[DATA_MAX_NAME_LEN]; + status = snprintf(tpname, sizeof(tpname), "amqp1/%s", instance->name); + if ((status < 0) || (size_t)status >= sizeof(tpname)) { + ERROR("amqp1 plugin: Instance name would have been truncated."); + return -1; + } + status = snprintf(instance->send_to, sizeof(instance->send_to), "/%s/%s", + transport->address, instance->name); + if ((status < 0) || (size_t)status >= sizeof(instance->send_to)) { + ERROR("amqp1 plugin: send_to address would have been truncated."); + return -1; + } + if (instance->notify) { + status = plugin_register_notification( + tpname, amqp1_notify, + &(user_data_t){ + .data = instance, .free_func = amqp1_config_instance_free, + }); + } else { + status = plugin_register_write( + tpname, amqp1_write, + &(user_data_t){ + .data = instance, .free_func = amqp1_config_instance_free, + }); + } + + if (status != 0) { + amqp1_config_instance_free(instance); + } + } + + return status; +} /* }}} int amqp1_config_instance */ + +static int amqp1_config_transport(oconfig_item_t *ci) /* {{{ */ +{ + transport = calloc(1, sizeof(*transport)); + if (transport == NULL) { + ERROR("amqp1 plugin: calloc failed."); + return ENOMEM; + } + + /* Initialize transport configuration {{{ */ + transport->retry_delay = 1; + + int status = cf_util_get_string(ci, &transport->name); + if (status != 0) { + sfree(transport); + return status; + } + + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("Host", child->key) == 0) + status = cf_util_get_string(child, &transport->host); + else if (strcasecmp("Port", child->key) == 0) + status = cf_util_get_string(child, &transport->port); + else if (strcasecmp("User", child->key) == 0) + status = cf_util_get_string(child, &transport->user); + else if (strcasecmp("Password", child->key) == 0) + status = cf_util_get_string(child, &transport->password); + else if (strcasecmp("Address", child->key) == 0) + status = cf_util_get_string(child, &transport->address); + else if (strcasecmp("RetryDelay", child->key) == 0) + status = cf_util_get_int(child, &transport->retry_delay); + else if (strcasecmp("Instance", child->key) == 0) + amqp1_config_instance(child); + else + WARNING("amqp1 plugin: Ignoring unknown " + "transport configuration option " + "\"%s\".", + child->key); + + if (status != 0) + break; + } + + if (status != 0) { + amqp1_config_transport_free(transport); + } + return status; +} /* }}} int amqp1_config_transport */ + +static int amqp1_config(oconfig_item_t *ci) /* {{{ */ +{ + + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("Transport", child->key) == 0) + amqp1_config_transport(child); + else + WARNING("amqp1 plugin: Ignoring unknown config option \"%s\".", + child->key); + } + + return 0; +} /* }}} int amqp1_config */ + +static int amqp1_init(void) /* {{{ */ +{ + if (transport == NULL) { + ERROR("amqp1: init failed, no transport configured"); + return -1; + } + + if (proactor == NULL) { + pthread_mutex_init(&send_lock, /* attr = */ NULL); + /* start_thread */ + int status = + plugin_thread_create(&event_thread_id, NULL /* no attributes */, + event_thread, NULL /* no argument */, "handle"); + if (status != 0) { + ERROR("amqp1 plugin: pthread_create failed: %s", STRERRNO); + } else { + event_thread_running = true; + } + } + return 0; +} /* }}} int amqp1_init */ + +static int amqp1_shutdown(void) /* {{{ */ +{ + stopping = true; + + /* Stop the proactor thread */ + if (event_thread_running) { + DEBUG("amqp1 plugin: Shutting down proactor thread."); + pn_connection_wake(conn); + } + pthread_join(event_thread_id, NULL /* no return value */); + memset(&event_thread_id, 0, sizeof(event_thread_id)); + + DEBUG("amqp1 plugin: proactor thread exited."); + + if (transport) { + amqp1_config_transport_free(transport); + } + + return 0; +} /* }}} int amqp1_shutdown */ + +void module_register(void) { + plugin_register_complex_config("amqp1", amqp1_config); + plugin_register_init("amqp1", amqp1_init); + plugin_register_shutdown("amqp1", amqp1_shutdown); +} /* void module_register */ diff --git a/src/apache.c b/src/apache.c index 07b2b57d..5c67a388 100644 --- a/src/apache.c +++ b/src/apache.c @@ -40,8 +40,8 @@ struct apache_s { char *url; char *user; char *pass; - _Bool verify_peer; - _Bool verify_host; + bool verify_peer; + bool verify_host; char *cacert; char *ssl_ciphers; char *server; /* user specific server type */ @@ -82,23 +82,19 @@ static void apache_free(void *arg) { static size_t apache_curl_callback(void *buf, size_t size, size_t nmemb, void *user_data) { - size_t len = size * nmemb; - apache_t *st; - - st = user_data; + apache_t *st = user_data; if (st == NULL) { ERROR("apache plugin: apache_curl_callback: " "user_data pointer is NULL."); return 0; } + size_t len = size * nmemb; if (len == 0) return len; if ((st->apache_buffer_fill + len) >= st->apache_buffer_size) { - char *temp; - - temp = realloc(st->apache_buffer, st->apache_buffer_fill + len + 1); + char *temp = realloc(st->apache_buffer, st->apache_buffer_fill + len + 1); if (temp == NULL) { ERROR("apache plugin: realloc failed."); return 0; @@ -116,16 +112,14 @@ static size_t apache_curl_callback(void *buf, size_t size, size_t nmemb, static size_t apache_header_callback(void *buf, size_t size, size_t nmemb, void *user_data) { - size_t len = size * nmemb; - apache_t *st; - - st = user_data; + apache_t *st = user_data; if (st == NULL) { ERROR("apache plugin: apache_header_callback: " "user_data pointer is NULL."); return 0; } + size_t len = size * nmemb; if (len == 0) return len; @@ -158,10 +152,7 @@ static size_t apache_header_callback(void *buf, size_t size, size_t nmemb, * */ static int config_add(oconfig_item_t *ci) { - apache_t *st; - int status; - - st = calloc(1, sizeof(*st)); + apache_t *st = calloc(1, sizeof(*st)); if (st == NULL) { ERROR("apache plugin: calloc failed."); return -1; @@ -169,7 +160,7 @@ static int config_add(oconfig_item_t *ci) { st->timeout = -1; - status = cf_util_get_string(ci, &st->name); + int status = cf_util_get_string(ci, &st->name); if (status != 0) { sfree(st); return status; @@ -238,8 +229,6 @@ static int config_add(oconfig_item_t *ci) { } /* int config_add */ static int config(oconfig_item_t *ci) { - int status = 0; - for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; @@ -253,7 +242,7 @@ static int config(oconfig_item_t *ci) { child->key); } /* for (ci->children) */ - return status; + return 0; } /* int config */ /* initialize curl for each host */ @@ -308,10 +297,8 @@ static int init_host(apache_t *st) /* {{{ */ (st->pass == NULL) ? "" : st->pass); #else static char credentials[1024]; - int status; - - status = snprintf(credentials, sizeof(credentials), "%s:%s", st->user, - (st->pass == NULL) ? "" : st->pass); + int status = snprintf(credentials, sizeof(credentials), "%s:%s", st->user, + (st->pass == NULL) ? "" : st->pass); if ((status < 0) || ((size_t)status >= sizeof(credentials))) { ERROR("apache plugin: init_host: Returning an error " "because the credentials have been " @@ -479,28 +466,13 @@ static void submit_scoreboard(char *buf, apache_t *st) { static int apache_read_host(user_data_t *user_data) /* {{{ */ { - char *ptr; - char *saveptr; - char *line; - - char *fields[4]; - int fields_num; - - apache_t *st; - - st = user_data->data; - - int status; - - char *content_type; - static const char *text_plain = "text/plain"; + apache_t *st = user_data->data; assert(st->url != NULL); /* (Assured by `config_add') */ if (st->curl == NULL) { - status = init_host(st); - if (status != 0) + if (init_host(st) != 0) return -1; } assert(st->curl != NULL); @@ -521,7 +493,10 @@ static int apache_read_host(user_data_t *user_data) /* {{{ */ st->server_type = APACHE; } - status = curl_easy_getinfo(st->curl, CURLINFO_CONTENT_TYPE, &content_type); + char *content_type; + static const char *text_plain = "text/plain"; + int status = + curl_easy_getinfo(st->curl, CURLINFO_CONTENT_TYPE, &content_type); if ((status == CURLE_OK) && (content_type != NULL) && (strncasecmp(content_type, text_plain, strlen(text_plain)) != 0)) { WARNING("apache plugin: `Content-Type' response header is not `%s' " @@ -530,11 +505,14 @@ static int apache_read_host(user_data_t *user_data) /* {{{ */ text_plain, content_type); } - ptr = st->apache_buffer; - saveptr = NULL; + char *ptr = st->apache_buffer; + char *saveptr = NULL; + char *line; while ((line = strtok_r(ptr, "\n\r", &saveptr)) != NULL) { ptr = NULL; - fields_num = strsplit(line, fields, STATIC_ARRAY_SIZE(fields)); + char *fields[4]; + + int fields_num = strsplit(line, fields, STATIC_ARRAY_SIZE(fields)); if (fields_num == 3) { if ((strcmp(fields[0], "Total") == 0) && diff --git a/src/apcups.c b/src/apcups.c index 406c164a..2931d2c0 100644 --- a/src/apcups.c +++ b/src/apcups.c @@ -70,16 +70,16 @@ typedef struct { * Private variables */ /* Default values for contacting daemon */ -static char *conf_node = NULL; -static char *conf_service = NULL; +static char *conf_node; +static char *conf_service; /* Defaults to false for backwards compatibility. */ -static _Bool conf_report_seconds = 0; -static _Bool conf_persistent_conn = 1; +static bool conf_report_seconds; +static bool conf_persistent_conn = true; static int global_sockfd = -1; -static int count_retries = 0; -static int count_iterations = 0; +static int count_retries; +static int count_iterations; static int net_shutdown(int *fd) { uint16_t packet_size = 0; @@ -119,10 +119,8 @@ static int net_open(char const *node, char const *service) { status = getaddrinfo(node, service, &ai_hints, &ai_return); if (status != 0) { - char errbuf[1024]; INFO("apcups plugin: getaddrinfo failed: %s", - (status == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf)) - : gai_strerror(status)); + (status == EAI_SYSTEM) ? STRERRNO : gai_strerror(status)); return -1; } @@ -147,9 +145,7 @@ static int net_open(char const *node, char const *service) { if (status != 0) /* `connect(2)' failed */ { - char errbuf[1024]; - INFO("apcups plugin: connect failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + INFO("apcups plugin: connect failed: %s", STRERRNO); close(sd); return -1; } @@ -240,7 +236,8 @@ static int apc_query_server(char const *node, char const *service, char recvline[1024]; char *tokptr; char *toksaveptr; - int try = 0; + int try + = 0; int status; #if APCMAIN @@ -265,7 +262,8 @@ static int apc_query_server(char const *node, char const *service, /* net_send closes the socket on error. */ assert(global_sockfd < 0); if (try == 0) { - try++; + try + ++; count_retries++; continue; } @@ -287,7 +285,7 @@ static int apc_query_server(char const *node, char const *service, "first %i iterations. Will close the socket " "in future iterations.", count_retries, count_iterations); - conf_persistent_conn = 0; + conf_persistent_conn = false; } while ((n = net_recv(&global_sockfd, recvline, sizeof(recvline) - 1)) > 0) { @@ -341,9 +339,7 @@ static int apc_query_server(char const *node, char const *service, net_shutdown(&global_sockfd); if (n < 0) { - char errbuf[1024]; - ERROR("apcups plugin: Reading from socket failed: %s", - sstrerror(status, errbuf, sizeof(errbuf))); + ERROR("apcups plugin: Reading from socket failed: %s", STRERROR(status)); return -1; } @@ -351,7 +347,7 @@ static int apc_query_server(char const *node, char const *service, } static int apcups_config(oconfig_item_t *ci) { - _Bool persistent_conn_set = 0; + bool persistent_conn_set = false; for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; @@ -364,7 +360,7 @@ static int apcups_config(oconfig_item_t *ci) { cf_util_get_boolean(child, &conf_report_seconds); else if (strcasecmp(child->key, "PersistentConnection") == 0) { cf_util_get_boolean(child, &conf_persistent_conn); - persistent_conn_set = 1; + persistent_conn_set = true; } else ERROR("apcups plugin: Unknown config option \"%s\".", child->key); } @@ -376,7 +372,7 @@ static int apcups_config(oconfig_item_t *ci) { "Apcupsd NIS socket timeout is %.3f seconds, " "PersistentConnection disabled by default.", interval, APCUPS_SERVER_TIMEOUT); - conf_persistent_conn = 0; + conf_persistent_conn = false; } } @@ -424,8 +420,8 @@ static int apcups_read(void) { int status = apc_query_server(conf_node, conf_service, &apcups_detail); if (status != 0) { - DEBUG("apcups plugin: apc_query_server (\"%s\", \"%s\") = %d", - conf_node, conf_service, status); + DEBUG("apcups plugin: apc_query_server (\"%s\", \"%s\") = %d", conf_node, + conf_service, status); return status; } diff --git a/src/aquaero.c b/src/aquaero.c index 77835619..937742b0 100644 --- a/src/aquaero.c +++ b/src/aquaero.c @@ -30,7 +30,7 @@ * Private variables */ /* Default values for contacting daemon */ -static char *conf_device = NULL; +static char *conf_device; static int aquaero_config(oconfig_item_t *ci) { for (int i = 0; i < ci->children_num; i++) { @@ -94,19 +94,15 @@ static int aquaero_read(void) { char type_instance[DATA_MAX_NAME_LEN]; if (libaquaero5_poll(conf_device, &aq_data, &err_msg) < 0) { - char errbuf[1024]; ERROR("aquaero plugin: Failed to poll device \"%s\": %s (%s)", - conf_device ? conf_device : "default", err_msg, - sstrerror(errno, errbuf, sizeof(errbuf))); + conf_device ? conf_device : "default", err_msg, STRERRNO); return -1; } if (libaquaero5_getsettings(conf_device, &aq_sett, &err_msg) < 0) { - char errbuf[1024]; ERROR("aquaero plugin: Failed to get settings " "for device \"%s\": %s (%s)", - conf_device ? conf_device : "default", err_msg, - sstrerror(errno, errbuf, sizeof(errbuf))); + conf_device ? conf_device : "default", err_msg, STRERRNO); return -1; } diff --git a/src/ascent.c b/src/ascent.c index 36694f61..22358658 100644 --- a/src/ascent.c +++ b/src/ascent.c @@ -87,19 +87,19 @@ typedef struct player_info_s player_info_t; #define PLAYER_INFO_STATIC_INIT \ { -1, -1, -1, -1, -1 } -static char *url = NULL; -static char *user = NULL; -static char *pass = NULL; -static char *verify_peer = NULL; -static char *verify_host = NULL; -static char *cacert = NULL; -static char *timeout = NULL; - -static CURL *curl = NULL; - -static char *ascent_buffer = NULL; -static size_t ascent_buffer_size = 0; -static size_t ascent_buffer_fill = 0; +static char *url; +static char *user; +static char *pass; +static char *verify_peer; +static char *verify_host; +static char *cacert; +static char *timeout; + +static CURL *curl; + +static char *ascent_buffer; +static size_t ascent_buffer_size; +static size_t ascent_buffer_fill; static char ascent_curl_error[CURL_ERROR_SIZE]; static const char *config_keys[] = { diff --git a/src/barometer.c b/src/barometer.c index f698005c..b6f2bc00 100644 --- a/src/barometer.c +++ b/src/barometer.c @@ -27,6 +27,9 @@ #include #include +#if HAVE_I2C_SMBUS_H +#include +#endif #include #include #include @@ -177,23 +180,23 @@ static const char *config_keys[] = { static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); -static char *config_device = NULL; /**< I2C bus device */ -static int config_oversample = 1; /**< averaging window */ +static char *config_device; /**< I2C bus device */ +static int config_oversample = 1; /**< averaging window */ -static double config_press_offset = 0.0; /**< pressure offset */ -static double config_temp_offset = 0.0; /**< temperature offset */ +static double config_press_offset; /**< pressure offset */ +static double config_temp_offset; /**< temperature offset */ static double config_altitude = NAN; /**< altitude */ -static int config_normalize = 0; /**< normalization method */ +static int config_normalize; /**< normalization method */ -static _Bool configured = 0; /**< the whole plugin config status */ +static bool configured; /**< the whole plugin config status */ static int i2c_bus_fd = -1; /**< I2C bus device FD */ static enum Sensor_type sensor_type = Sensor_none; /**< detected/used sensor type */ -static __s32 mpl3115_oversample = 0; /**< MPL3115 CTRL1 oversample setting */ +static __s32 mpl3115_oversample; /**< MPL3115 CTRL1 oversample setting */ // BMP085 configuration static unsigned bmp085_oversampling; /**< BMP085 oversampling (0-3) */ @@ -226,7 +229,7 @@ static short bmp085_MD; /* Used only for MPL115. MPL3115 supports real oversampling in the device so */ /* no need for any postprocessing. */ -static _Bool avg_initialized = 0; /**< already initialized by real values */ +static bool avg_initialized; /**< already initialized by real values */ typedef struct averaging_s { long int *ring_buffer; @@ -235,8 +238,8 @@ typedef struct averaging_s { int ring_buffer_head; } averaging_t; -static averaging_t pressure_averaging = {NULL, 0, 0L, 0}; -static averaging_t temperature_averaging = {NULL, 0, 0L, 0}; +static averaging_t pressure_averaging; +static averaging_t temperature_averaging; /** * Create / allocate averaging buffer @@ -249,7 +252,7 @@ static averaging_t temperature_averaging = {NULL, 0, 0L, 0}; * @return Zero when successful */ static int averaging_create(averaging_t *avg, int size) { - avg->ring_buffer = calloc((size_t)size, sizeof(*avg->ring_buffer)); + avg->ring_buffer = calloc(size, sizeof(*avg->ring_buffer)); if (avg->ring_buffer == NULL) { ERROR("barometer: averaging_create - ring buffer allocation of size %d " "failed", @@ -313,11 +316,11 @@ static double averaging_add_sample(averaging_t *avg, long int sample) { typedef struct temperature_list_s { char *sensor_name; /**< sensor name/reference */ size_t num_values; /**< number of values (usually one) */ - _Bool initialized; /**< sensor already provides data */ + bool initialized; /**< sensor already provides data */ struct temperature_list_s *next; /**< next in the list */ } temperature_list_t; -static temperature_list_t *temp_list = NULL; +static temperature_list_t *temp_list; /* * Add new sensor to the temperature reference list @@ -412,16 +415,16 @@ static int get_reference_temperature(double *result) { continue; } - DEBUG( - "barometer: get_reference_temperature - initialize \"%s\", %zu vals", - list->sensor_name, values_num); + DEBUG("barometer: get_reference_temperature - initialize \"%s\", %" PRIsz + " vals", + list->sensor_name, values_num); list->initialized = 1; list->num_values = values_num; for (size_t i = 0; i < values_num; ++i) { - DEBUG("barometer: get_reference_temperature - rate %zu: %lf **", i, - values[i]); + DEBUG("barometer: get_reference_temperature - rate %" PRIsz ": %lf **", + i, values[i]); if (!isnan(values[i])) { avg_sum += values[i]; ++avg_num; @@ -444,7 +447,7 @@ static int get_reference_temperature(double *result) { } for (size_t i = 0; i < REF_TEMP_AVG_NUM * list->num_values; ++i) { - DEBUG("barometer: get_reference_temperature - history %zu: %lf", i, + DEBUG("barometer: get_reference_temperature - history %" PRIsz ": %lf", i, values_history[i]); if (!isnan(values_history[i])) { avg_sum += values_history[i]; @@ -464,8 +467,9 @@ static int get_reference_temperature(double *result) { } for (size_t i = 0; i < values_num; ++i) { - DEBUG("barometer: get_reference_temperature - rate last %zu: %lf **", i, - values[i]); + DEBUG("barometer: get_reference_temperature - rate last %" PRIsz + ": %lf **", + i, values[i]); if (!isnan(values[i])) { avg_sum += values[i]; ++avg_num; @@ -514,12 +518,11 @@ static int get_reference_temperature(double *result) { */ static int MPL115_detect(void) { __s32 res; - char errbuf[1024]; if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL115_I2C_ADDRESS) < 0) { ERROR("barometer: MPL115_detect problem setting i2c slave address to " "0x%02X: %s", - MPL115_I2C_ADDRESS, sstrerror(errno, errbuf, sizeof(errbuf))); + MPL115_I2C_ADDRESS, STRERRNO); return 0; } @@ -548,14 +551,11 @@ static int MPL115_read_coeffs(void) { int8_t sic12MSB, sic12LSB, sic11MSB, sic11LSB, sic22MSB, sic22LSB; int16_t sia0, sib1, sib2, sic12, sic11, sic22; - char errbuf[1024]; - res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, MPL115_ADDR_COEFFS, STATIC_ARRAY_SIZE(mpl115_coeffs), mpl115_coeffs); if (res < 0) { - ERROR("barometer: MPL115_read_coeffs - problem reading data: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("barometer: MPL115_read_coeffs - problem reading data: %s", STRERRNO); return -1; } @@ -657,7 +657,6 @@ static int MPL115_read_averaged(double *pressure, double *temperature) { int conv_temperature; double adc_pressure; double adc_temperature; - char errbuf[1024]; *pressure = 0.0; *temperature = 0.0; @@ -674,11 +673,11 @@ static int MPL115_read_averaged(double *pressure, double *temperature) { if (retries > 0) { ERROR("barometer: MPL115_read_averaged - requesting conversion: %s, " "will retry at most %d more times", - sstrerror(errno, errbuf, sizeof(errbuf)), retries); + STRERRNO, retries); } else { ERROR("barometer: MPL115_read_averaged - requesting conversion: %s, " "too many failed retries", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return -1; } } @@ -697,11 +696,11 @@ static int MPL115_read_averaged(double *pressure, double *temperature) { if (retries > 0) { ERROR("barometer: MPL115_read_averaged - reading conversion: %s, " "will retry at most %d more times", - sstrerror(errno, errbuf, sizeof(errbuf)), retries); + STRERRNO, retries); } else { ERROR("barometer: MPL115_read_averaged - reading conversion: %s, " "too many failed retries", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return -1; } } @@ -738,12 +737,11 @@ static int MPL115_read_averaged(double *pressure, double *temperature) { */ static int MPL3115_detect(void) { __s32 res; - char errbuf[1024]; if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL3115_I2C_ADDRESS) < 0) { ERROR("barometer: MPL3115_detect problem setting i2c slave address to " "0x%02X: %s", - MPL3115_I2C_ADDRESS, sstrerror(errno, errbuf, sizeof(errbuf))); + MPL3115_I2C_ADDRESS, STRERRNO); return 0; } @@ -810,21 +808,18 @@ static int MPL3115_read(double *pressure, double *temperature) { __s32 ctrl; __u8 data[MPL3115_NUM_CONV_VALS]; long int tmp_value = 0; - char errbuf[1024]; /* Set Active - activate the device from standby */ res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_CTRL_REG1); if (res < 0) { - ERROR("barometer: MPL3115_read - cannot read CTRL_REG1: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("barometer: MPL3115_read - cannot read CTRL_REG1: %s", STRERRNO); return 1; } ctrl = res; res = i2c_smbus_write_byte_data(i2c_bus_fd, MPL3115_REG_CTRL_REG1, ctrl | MPL3115_CTRL_REG1_SBYB); if (res < 0) { - ERROR("barometer: MPL3115_read - problem activating: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("barometer: MPL3115_read - problem activating: %s", STRERRNO); return 1; } @@ -835,7 +830,7 @@ static int MPL3115_read(double *pressure, double *temperature) { res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_STATUS); if (res < 0) { ERROR("barometer: MPL3115_read - cannot read status register: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return 1; } @@ -848,7 +843,7 @@ static int MPL3115_read(double *pressure, double *temperature) { res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_STATUS); if (res < 0) { ERROR("barometer: MPL3115_read - cannot read status register: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return 1; } } @@ -857,8 +852,7 @@ static int MPL3115_read(double *pressure, double *temperature) { res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, MPL3115_REG_OUT_P_MSB, MPL3115_NUM_CONV_VALS, data); if (res < 0) { - ERROR("barometer: MPL3115_read - cannot read data registers: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("barometer: MPL3115_read - cannot read data registers: %s", STRERRNO); return 1; } @@ -888,7 +882,6 @@ static int MPL3115_read(double *pressure, double *temperature) { static int MPL3115_init_sensor(void) { __s32 res; __s8 offset; - char errbuf[1024]; /* Reset the sensor. It will reset immediately without ACKing */ /* the transaction, so no error handling here. */ @@ -906,7 +899,7 @@ static int MPL3115_init_sensor(void) { res = i2c_smbus_write_byte_data(i2c_bus_fd, MPL3115_REG_OFF_T, offset); if (res < 0) { ERROR("barometer: MPL3115_init_sensor - problem setting temp offset: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return -1; } @@ -917,7 +910,7 @@ static int MPL3115_init_sensor(void) { if (res < 0) { ERROR( "barometer: MPL3115_init_sensor - problem setting pressure offset: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return -1; } @@ -927,7 +920,7 @@ static int MPL3115_init_sensor(void) { MPL3115_PT_DATA_TDEF); if (res < 0) { ERROR("barometer: MPL3115_init_sensor - problem setting PT_DATA_CFG: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return -1; } @@ -936,7 +929,7 @@ static int MPL3115_init_sensor(void) { mpl3115_oversample); if (res < 0) { ERROR("barometer: MPL3115_init_sensor - problem configuring CTRL_REG1: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return -1; } @@ -954,12 +947,11 @@ static int MPL3115_init_sensor(void) { */ static int BMP085_detect(void) { __s32 res; - char errbuf[1024]; if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, BMP085_I2C_ADDRESS) < 0) { ERROR("barometer: BMP085_detect - problem setting i2c slave address to " "0x%02X: %s", - BMP085_I2C_ADDRESS, sstrerror(errno, errbuf, sizeof(errbuf))); + BMP085_I2C_ADDRESS, STRERRNO); return 0; } @@ -971,7 +963,7 @@ static int BMP085_detect(void) { res = i2c_smbus_read_byte_data(i2c_bus_fd, BMP085_ADDR_VERSION); if (res < 0) { ERROR("barometer: BMP085_detect - problem checking chip version: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return 0; } DEBUG("barometer: BMP085_detect - chip version ML:0x%02X AL:0x%02X", @@ -1033,13 +1025,11 @@ static void BMP085_adjust_oversampling(void) { static int BMP085_read_coeffs(void) { __s32 res; __u8 coeffs[BMP085_NUM_COEFFS]; - char errbuf[1024]; res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, BMP085_ADDR_COEFFS, BMP085_NUM_COEFFS, coeffs); if (res < 0) { - ERROR("barometer: BMP085_read_coeffs - problem reading data: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("barometer: BMP085_read_coeffs - problem reading data: %s", STRERRNO); return -1; } @@ -1140,15 +1130,13 @@ static int BMP085_read(double *pressure, double *temperature) { long adc_pressure; long adc_temperature; - char errbuf[1024]; - /* start conversion of temperature */ res = i2c_smbus_write_byte_data(i2c_bus_fd, BMP085_ADDR_CTRL_REG, BMP085_CMD_CONVERT_TEMP); if (res < 0) { ERROR("barometer: BMP085_read - problem requesting temperature conversion: " "%s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return 1; } @@ -1158,7 +1146,7 @@ static int BMP085_read(double *pressure, double *temperature) { i2c_smbus_read_i2c_block_data(i2c_bus_fd, BMP085_ADDR_CONV, 2, measBuff); if (res < 0) { ERROR("barometer: BMP085_read - problem reading temperature data: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return 1; } @@ -1169,7 +1157,7 @@ static int BMP085_read(double *pressure, double *temperature) { bmp085_cmdCnvPress); if (res < 0) { ERROR("barometer: BMP085_read - problem requesting pressure conversion: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return 1; } @@ -1179,7 +1167,7 @@ static int BMP085_read(double *pressure, double *temperature) { i2c_smbus_read_i2c_block_data(i2c_bus_fd, BMP085_ADDR_CONV, 3, measBuff); if (res < 0) { ERROR("barometer: BMP085_read - problem reading pressure data: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return 1; } @@ -1407,7 +1395,7 @@ static int MPL115_collectd_barometer_read(void) { config_oversample - 1); usleep(20000); } - avg_initialized = 1; + avg_initialized = true; } result = MPL115_read_averaged(&pressure, &temperature); @@ -1571,7 +1559,6 @@ static int BMP085_collectd_barometer_read(void) { * @return Zero when successful. */ static int collectd_barometer_init(void) { - char errbuf[1024]; DEBUG("barometer: collectd_barometer_init"); @@ -1596,7 +1583,7 @@ static int collectd_barometer_init(void) { if (i2c_bus_fd < 0) { ERROR("barometer: collectd_barometer_init problem opening I2C bus device " "\"%s\": %s (is loaded mod i2c-dev?)", - config_device, sstrerror(errno, errbuf, sizeof(errbuf))); + config_device, STRERRNO); return -1; } @@ -1651,7 +1638,7 @@ static int collectd_barometer_init(void) { return -1; } - configured = 1; + configured = true; return 0; } diff --git a/src/battery.c b/src/battery.c index b6dea0f3..a74e7b64 100644 --- a/src/battery.c +++ b/src/battery.c @@ -72,9 +72,9 @@ int battery_read_statefs( void); /* defined in battery_statefs; used by StateFS backend */ -static _Bool report_percent = 0; -static _Bool report_degraded = 0; -static _Bool query_statefs = 0; +static bool report_percent; +static bool report_degraded; +static bool query_statefs; static void battery_submit2(char const *plugin_instance, /* {{{ */ char const *type, char const *type_instance, @@ -410,7 +410,7 @@ static int read_sysfs_callback(char const *dir, /* {{{ */ char const *plugin_instance; char buffer[32]; gauge_t v = NAN; - _Bool discharging = 0; + bool discharging = false; int status; /* Ignore non-battery directories, such as AC power. */ @@ -424,7 +424,7 @@ static int read_sysfs_callback(char const *dir, /* {{{ */ (void)sysfs_file_to_buffer(dir, power_supply, "status", buffer, sizeof(buffer)); if (strcasecmp("Discharging", buffer) == 0) - discharging = 1; + discharging = true; /* FIXME: This is a dirty hack for backwards compatibility: The battery * plugin, for a very long time, has had the plugin_instance @@ -522,8 +522,8 @@ static int read_acpi_callback(char const *dir, /* {{{ */ gauge_t capacity_charged = NAN; gauge_t capacity_full = NAN; gauge_t capacity_design = NAN; - _Bool charging = 0; - _Bool is_current = 0; + bool charging = false; + bool is_current = false; char const *plugin_instance; char filename[PATH_MAX]; @@ -560,9 +560,9 @@ static int read_acpi_callback(char const *dir, /* {{{ */ if ((strcmp(fields[0], "charging") == 0) && (strcmp(fields[1], "state:") == 0)) { if (strcmp(fields[2], "charging") == 0) - charging = 1; + charging = true; else - charging = 0; + charging = false; continue; } @@ -575,7 +575,7 @@ static int read_acpi_callback(char const *dir, /* {{{ */ strtogauge(fields[2], &power); if ((numfields >= 4) && (strcmp("mA", fields[3]) == 0)) - is_current = 1; + is_current = true; } else if ((strcmp(fields[0], "remaining") == 0) && (strcmp(fields[1], "capacity:") == 0)) strtogauge(fields[2], &capacity_charged); diff --git a/src/bind.c b/src/bind.c index 990e2ca7..fe3480d0 100644 --- a/src/bind.c +++ b/src/bind.c @@ -73,9 +73,9 @@ typedef int (*list_callback_t)(const char *name, value_t value, struct cb_view_s { char *name; - int qtypes; - int resolver_stats; - int cacherrsets; + _Bool qtypes; + _Bool resolver_stats; + _Bool cacherrsets; char **zones; size_t zones_num; @@ -104,25 +104,25 @@ typedef struct list_info_ptr_s list_info_ptr_t; /* FIXME: Enabled by default for backwards compatibility. */ /* TODO: Remove time parsing code. */ -static _Bool config_parse_time = 1; - -static char *url = NULL; -static int global_opcodes = 1; -static int global_qtypes = 1; -static int global_server_stats = 1; -static int global_zone_maint_stats = 1; -static int global_resolver_stats = 0; -static int global_memory_stats = 1; +static bool config_parse_time = true; + +static char *url; +static _Bool global_opcodes = 1; +static _Bool global_qtypes = 1; +static _Bool global_server_stats = 1; +static _Bool global_zone_maint_stats = 1; +static _Bool global_resolver_stats; +static _Bool global_memory_stats = 1; static int timeout = -1; -static cb_view_t *views = NULL; -static size_t views_num = 0; +static cb_view_t *views; +static size_t views_num; -static CURL *curl = NULL; +static CURL *curl; -static char *bind_buffer = NULL; -static size_t bind_buffer_size = 0; -static size_t bind_buffer_fill = 0; +static char *bind_buffer; +static size_t bind_buffer_size; +static size_t bind_buffer_fill; static char bind_curl_error[CURL_ERROR_SIZE]; /* Translation table for the `nsstats' values. */ @@ -274,9 +274,7 @@ static size_t bind_curl_callback(void *buf, size_t size, /* {{{ */ return len; if ((bind_buffer_fill + len) >= bind_buffer_size) { - char *temp; - - temp = realloc(bind_buffer, bind_buffer_fill + len + 1); + char *temp = realloc(bind_buffer, bind_buffer_fill + len + 1); if (temp == NULL) { ERROR("bind plugin: realloc failed."); return 0; @@ -335,20 +333,16 @@ static int bind_xml_list_callback(const char *name, /* {{{ */ static int bind_xml_read_derive(xmlDoc *doc, xmlNode *node, /* {{{ */ derive_t *ret_value) { - char *str_ptr; - value_t value; - int status; - - str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + char *str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if (str_ptr == NULL) { ERROR("bind plugin: bind_xml_read_derive: xmlNodeListGetString failed."); return -1; } - status = parse_value(str_ptr, &value, DS_TYPE_DERIVE); + value_t value; + + int status = parse_value(str_ptr, &value, DS_TYPE_DERIVE); if (status != 0) { - ERROR("bind plugin: Parsing string \"%s\" to derive value failed.", - str_ptr); xmlFree(str_ptr); return -1; } @@ -360,17 +354,15 @@ static int bind_xml_read_derive(xmlDoc *doc, xmlNode *node, /* {{{ */ static int bind_xml_read_gauge(xmlDoc *doc, xmlNode *node, /* {{{ */ gauge_t *ret_value) { - char *str_ptr, *end_ptr; - double value; - - str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + char *str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if (str_ptr == NULL) { ERROR("bind plugin: bind_xml_read_gauge: xmlNodeListGetString failed."); return -1; } + char *end_ptr; errno = 0; - value = strtod(str_ptr, &end_ptr); + double value = strtod(str_ptr, &end_ptr); xmlFree(str_ptr); if (str_ptr == end_ptr || errno) { if (errno && (value < 0)) @@ -389,13 +381,8 @@ static int bind_xml_read_gauge(xmlDoc *doc, xmlNode *node, /* {{{ */ static int bind_xml_read_timestamp(const char *xpath_expression, /* {{{ */ xmlDoc *doc, xmlXPathContext *xpathCtx, time_t *ret_value) { - xmlXPathObject *xpathObj = NULL; - xmlNode *node; - char *str_ptr; - char *tmp; - struct tm tm = {0}; - - xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx); + xmlXPathObject *xpathObj = + xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx); if (xpathObj == NULL) { ERROR("bind plugin: Unable to evaluate XPath expression `%s'.", xpath_expression); @@ -413,7 +400,7 @@ static int bind_xml_read_timestamp(const char *xpath_expression, /* {{{ */ xpath_expression, xpathObj->nodesetval->nodeNr); } - node = xpathObj->nodesetval->nodeTab[0]; + xmlNode *node = xpathObj->nodesetval->nodeTab[0]; if (node->xmlChildrenNode == NULL) { ERROR("bind plugin: bind_xml_read_timestamp: " @@ -422,14 +409,15 @@ static int bind_xml_read_timestamp(const char *xpath_expression, /* {{{ */ return -1; } - str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); + char *str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if (str_ptr == NULL) { ERROR("bind plugin: bind_xml_read_timestamp: xmlNodeListGetString failed."); xmlXPathFreeObject(xpathObj); return -1; } - tmp = strptime(str_ptr, "%Y-%m-%dT%T", &tm); + struct tm tm = {0}; + char *tmp = strptime(str_ptr, "%Y-%m-%dT%T", &tm); xmlFree(str_ptr); if (tmp == NULL) { ERROR("bind plugin: bind_xml_read_timestamp: strptime failed."); @@ -440,18 +428,14 @@ static int bind_xml_read_timestamp(const char *xpath_expression, /* {{{ */ #if HAVE_TIMEGM time_t t = timegm(&tm); if (t == ((time_t)-1)) { - char errbuf[1024]; - ERROR("bind plugin: timegm() failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("bind plugin: timegm() failed: %s", STRERRNO); return -1; } *ret_value = t; #else time_t t = mktime(&tm); if (t == ((time_t)-1)) { - char errbuf[1024]; - ERROR("bind plugin: mktime() failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("bind plugin: mktime() failed: %s", STRERRNO); return -1; } /* mktime assumes that tm is local time. Luckily, it also sets timezone to @@ -478,25 +462,23 @@ static int bind_parse_generic_name_value(const char *xpath_expression, /* {{{ */ void *user_data, xmlDoc *doc, xmlXPathContext *xpathCtx, time_t current_time, int ds_type) { - xmlXPathObject *xpathObj = NULL; - int num_entries; - - xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx); + xmlXPathObject *xpathObj = + xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx); if (xpathObj == NULL) { ERROR("bind plugin: Unable to evaluate XPath expression `%s'.", xpath_expression); return -1; } - num_entries = 0; + int num_entries = 0; /* Iterate over all matching nodes. */ for (int i = 0; xpathObj->nodesetval && (i < xpathObj->nodesetval->nodeNr); i++) { + xmlNode *name_node = NULL; xmlNode *counter = NULL; - xmlNode *parent; - parent = xpathObj->nodesetval->nodeTab[i]; + xmlNode *parent = xpathObj->nodesetval->nodeTab[i]; DEBUG("bind plugin: bind_parse_generic_name_value: parent->name = %s;", (char *)parent->name); @@ -559,32 +541,29 @@ static int bind_parse_generic_value_list(const char *xpath_expression, /* {{{ */ void *user_data, xmlDoc *doc, xmlXPathContext *xpathCtx, time_t current_time, int ds_type) { - xmlXPathObject *xpathObj = NULL; - int num_entries; - - xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx); + xmlXPathObject *xpathObj = + xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx); if (xpathObj == NULL) { ERROR("bind plugin: Unable to evaluate XPath expression `%s'.", xpath_expression); return -1; } - num_entries = 0; + int num_entries = 0; /* Iterate over all matching nodes. */ for (int i = 0; xpathObj->nodesetval && (i < xpathObj->nodesetval->nodeNr); i++) { /* Iterate over all child nodes. */ for (xmlNode *child = xpathObj->nodesetval->nodeTab[i]->xmlChildrenNode; child != NULL; child = child->next) { - char *node_name; - value_t value; - int status; if (child->type != XML_ELEMENT_NODE) continue; - node_name = (char *)child->name; + char *node_name = (char *)child->name; + value_t value; + int status; if (ds_type == DS_TYPE_GAUGE) status = bind_xml_read_gauge(doc, child, &value.gauge); else @@ -621,17 +600,16 @@ static int bind_parse_generic_name_attr_value_list( const char *xpath_expression, /* {{{ */ list_callback_t list_callback, void *user_data, xmlDoc *doc, xmlXPathContext *xpathCtx, time_t current_time, int ds_type) { - xmlXPathObject *xpathObj = NULL; - int num_entries; - xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx); + xmlXPathObject *xpathObj = + xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx); if (xpathObj == NULL) { ERROR("bind plugin: Unable to evaluate XPath expression `%s'.", xpath_expression); return -1; } - num_entries = 0; + int num_entries = 0; /* Iterate over all matching nodes. */ for (int i = 0; xpathObj->nodesetval && (i < xpathObj->nodesetval->nodeNr); i++) { @@ -644,15 +622,15 @@ static int bind_parse_generic_name_attr_value_list( if (strncmp("counter", (char *)child->name, strlen("counter")) != 0) continue; - char *attr_name; - value_t value; - int status; - - attr_name = (char *)xmlGetProp(child, BAD_CAST "name"); + char *attr_name = (char *)xmlGetProp(child, BAD_CAST "name"); if (attr_name == NULL) { DEBUG("bind plugin: found without name."); continue; } + + value_t value; + int status; + if (ds_type == DS_TYPE_GAUGE) status = bind_xml_read_gauge(doc, child, &value.gauge); else @@ -681,9 +659,7 @@ static int bind_parse_generic_name_attr_value_list( static int bind_xml_stats_handle_zone(int version, xmlDoc *doc, /* {{{ */ xmlXPathContext *path_ctx, xmlNode *node, cb_view_t *view, time_t current_time) { - xmlXPathObject *path_obj; char *zone_name = NULL; - size_t j; if (version >= 3) { char *n = (char *)xmlGetProp(node, BAD_CAST "name"); @@ -695,7 +671,8 @@ static int bind_xml_stats_handle_zone(int version, xmlDoc *doc, /* {{{ */ xmlFree(n); xmlFree(c); } else { - path_obj = xmlXPathEvalExpression(BAD_CAST "name", path_ctx); + xmlXPathObject *path_obj = + xmlXPathEvalExpression(BAD_CAST "name", path_ctx); if (path_obj == NULL) { ERROR("bind plugin: xmlXPathEvalExpression failed."); return -1; @@ -716,13 +693,13 @@ static int bind_xml_stats_handle_zone(int version, xmlDoc *doc, /* {{{ */ return -1; } + size_t j; for (j = 0; j < view->zones_num; j++) { if (strcasecmp(zone_name, view->zones[j]) == 0) break; } xmlFree(zone_name); - zone_name = NULL; if (j >= view->zones_num) return 0; @@ -767,16 +744,14 @@ static int bind_xml_stats_handle_zone(int version, xmlDoc *doc, /* {{{ */ static int bind_xml_stats_search_zones(int version, xmlDoc *doc, /* {{{ */ xmlXPathContext *path_ctx, xmlNode *node, cb_view_t *view, time_t current_time) { - xmlXPathObject *zone_nodes = NULL; - xmlXPathContext *zone_path_context; - - zone_path_context = xmlXPathNewContext(doc); + xmlXPathContext *zone_path_context = xmlXPathNewContext(doc); if (zone_path_context == NULL) { ERROR("bind plugin: xmlXPathNewContext failed."); return -1; } - zone_nodes = xmlXPathEvalExpression(BAD_CAST "zones/zone", path_ctx); + xmlXPathObject *zone_nodes = + xmlXPathEvalExpression(BAD_CAST "zones/zone", path_ctx); if (zone_nodes == NULL) { ERROR("bind plugin: Cannot find any tags."); xmlXPathFreeContext(zone_path_context); @@ -821,8 +796,8 @@ static int bind_xml_stats_handle_view(int version, xmlDoc *doc, /* {{{ */ xmlFree(view_name); view_name = NULL; } else { - xmlXPathObject *path_obj; - path_obj = xmlXPathEvalExpression(BAD_CAST "name", path_ctx); + xmlXPathObject *path_obj = + xmlXPathEvalExpression(BAD_CAST "name", path_ctx); if (path_obj == NULL) { ERROR("bind plugin: xmlXPathEvalExpression failed."); return -1; @@ -931,18 +906,15 @@ static int bind_xml_stats_handle_view(int version, xmlDoc *doc, /* {{{ */ static int bind_xml_stats_search_views(int version, xmlDoc *doc, /* {{{ */ xmlXPathContext *xpathCtx, - xmlNode *statsnode, time_t current_time) { - xmlXPathObject *view_nodes = NULL; - xmlXPathContext *view_path_context; - - view_path_context = xmlXPathNewContext(doc); + xmlXPathContext *view_path_context = xmlXPathNewContext(doc); if (view_path_context == NULL) { ERROR("bind plugin: xmlXPathNewContext failed."); return -1; } - view_nodes = xmlXPathEvalExpression(BAD_CAST "views/view", xpathCtx); + xmlXPathObject *view_nodes = + xmlXPathEvalExpression(BAD_CAST "views/view", xpathCtx); if (view_nodes == NULL) { ERROR("bind plugin: Cannot find any tags."); xmlXPathFreeContext(view_path_context); @@ -950,9 +922,7 @@ static int bind_xml_stats_search_views(int version, xmlDoc *doc, /* {{{ */ } for (int i = 0; i < view_nodes->nodesetval->nodeNr; i++) { - xmlNode *node; - - node = view_nodes->nodesetval->nodeTab[i]; + xmlNode *node = view_nodes->nodesetval->nodeTab[i]; assert(node != NULL); view_path_context->node = node; @@ -967,8 +937,7 @@ static int bind_xml_stats_search_views(int version, xmlDoc *doc, /* {{{ */ } /* }}} int bind_xml_stats_search_views */ static void bind_xml_stats_v3(xmlDoc *doc, /* {{{ */ - xmlXPathContext *xpathCtx, xmlNode *statsnode, - time_t current_time) { + xmlXPathContext *xpathCtx, time_t current_time) { /* XPath: server/counters[@type='opcode'] * Variables: QUERY, IQUERY, NOTIFY, UPDATE, ... * Layout v3: @@ -1086,7 +1055,7 @@ static void bind_xml_stats_v3(xmlDoc *doc, /* {{{ */ } /* }}} bind_xml_stats_v3 */ static void bind_xml_stats_v1_v2(int version, xmlDoc *doc, /* {{{ */ - xmlXPathContext *xpathCtx, xmlNode *statsnode, + xmlXPathContext *xpathCtx, time_t current_time) { /* XPath: server/requests/opcode, server/counters[@type='opcode'] * Variables: QUERY, IQUERY, NOTIFY, UPDATE, ... @@ -1256,14 +1225,13 @@ static void bind_xml_stats_v1_v2(int version, xmlDoc *doc, /* {{{ */ static int bind_xml_stats(int version, xmlDoc *doc, /* {{{ */ xmlXPathContext *xpathCtx, xmlNode *statsnode) { time_t current_time = 0; - int status; xpathCtx->node = statsnode; /* TODO: Check `server/boot-time' to recognize server restarts. */ - status = bind_xml_read_timestamp("server/current-time", doc, xpathCtx, - ¤t_time); + int status = bind_xml_read_timestamp("server/current-time", doc, xpathCtx, + ¤t_time); if (status != 0) { ERROR("bind plugin: Reading `server/current-time' failed."); return -1; @@ -1271,9 +1239,9 @@ static int bind_xml_stats(int version, xmlDoc *doc, /* {{{ */ DEBUG("bind plugin: Current server time is %i.", (int)current_time); if (version == 3) { - bind_xml_stats_v3(doc, xpathCtx, statsnode, current_time); + bind_xml_stats_v3(doc, xpathCtx, current_time); } else { - bind_xml_stats_v1_v2(version, doc, xpathCtx, statsnode, current_time); + bind_xml_stats_v1_v2(version, doc, xpathCtx, current_time); } /* XPath: memory/summary @@ -1299,26 +1267,22 @@ static int bind_xml_stats(int version, xmlDoc *doc, /* {{{ */ } if (views_num > 0) - bind_xml_stats_search_views(version, doc, xpathCtx, statsnode, - current_time); + bind_xml_stats_search_views(version, doc, xpathCtx, current_time); return 0; } /* }}} int bind_xml_stats */ static int bind_xml(const char *data) /* {{{ */ { - xmlDoc *doc = NULL; - xmlXPathContext *xpathCtx = NULL; - xmlXPathObject *xpathObj = NULL; int ret = -1; - doc = xmlParseMemory(data, strlen(data)); + xmlDoc *doc = xmlParseMemory(data, strlen(data)); if (doc == NULL) { ERROR("bind plugin: xmlParseMemory failed."); return -1; } - xpathCtx = xmlXPathNewContext(doc); + xmlXPathContext *xpathCtx = xmlXPathNewContext(doc); if (xpathCtx == NULL) { ERROR("bind plugin: xmlXPathNewContext failed."); xmlFreeDoc(doc); @@ -1329,7 +1293,8 @@ static int bind_xml(const char *data) /* {{{ */ // version 3.* of statistics XML (since BIND9.9) // - xpathObj = xmlXPathEvalExpression(BAD_CAST "/statistics", xpathCtx); + xmlXPathObject *xpathObj = + xmlXPathEvalExpression(BAD_CAST "/statistics", xpathCtx); if (xpathObj == NULL || xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr == 0) { DEBUG("bind plugin: Statistics appears not to be v3"); @@ -1443,33 +1408,15 @@ static int bind_xml(const char *data) /* {{{ */ return ret; } /* }}} int bind_xml */ -static int bind_config_set_bool(const char *name, int *var, /* {{{ */ - oconfig_item_t *ci) { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) { - WARNING("bind plugin: The `%s' option needs " - "exactly one boolean argument.", - name); - return -1; - } - - if (ci->values[0].value.boolean) - *var = 1; - else - *var = 0; - return 0; -} /* }}} int bind_config_set_bool */ - static int bind_config_add_view_zone(cb_view_t *view, /* {{{ */ oconfig_item_t *ci) { - char **tmp; - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { WARNING("bind plugin: The `Zone' option needs " "exactly one string argument."); return -1; } - tmp = realloc(view->zones, sizeof(char *) * (view->zones_num + 1)); + char **tmp = realloc(view->zones, sizeof(char *) * (view->zones_num + 1)); if (tmp == NULL) { ERROR("bind plugin: realloc failed."); return -1; @@ -1488,14 +1435,12 @@ static int bind_config_add_view_zone(cb_view_t *view, /* {{{ */ static int bind_config_add_view(oconfig_item_t *ci) /* {{{ */ { - cb_view_t *tmp; - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { WARNING("bind plugin: `View' blocks need exactly one string argument."); return -1; } - tmp = realloc(views, sizeof(*views) * (views_num + 1)); + cb_view_t *tmp = realloc(views, sizeof(*views) * (views_num + 1)); if (tmp == NULL) { ERROR("bind plugin: realloc failed."); return -1; @@ -1521,11 +1466,11 @@ static int bind_config_add_view(oconfig_item_t *ci) /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp("QTypes", child->key) == 0) - bind_config_set_bool("QTypes", &tmp->qtypes, child); + cf_util_get_boolean(child, &tmp->qtypes); else if (strcasecmp("ResolverStats", child->key) == 0) - bind_config_set_bool("ResolverStats", &tmp->resolver_stats, child); + cf_util_get_boolean(child, &tmp->resolver_stats); else if (strcasecmp("CacheRRSets", child->key) == 0) - bind_config_set_bool("CacheRRSets", &tmp->cacherrsets, child); + cf_util_get_boolean(child, &tmp->cacherrsets); else if (strcasecmp("Zone", child->key) == 0) bind_config_add_view_zone(tmp, child); else { @@ -1545,27 +1490,19 @@ static int bind_config(oconfig_item_t *ci) /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp("Url", child->key) == 0) { - if ((child->values_num != 1) || - (child->values[0].type != OCONFIG_TYPE_STRING)) { - WARNING("bind plugin: The `Url' option needs " - "exactly one string argument."); - return -1; - } - - sfree(url); - url = strdup(child->values[0].value.string); + cf_util_get_string(child, &url); } else if (strcasecmp("OpCodes", child->key) == 0) - bind_config_set_bool("OpCodes", &global_opcodes, child); + cf_util_get_boolean(child, &global_opcodes); else if (strcasecmp("QTypes", child->key) == 0) - bind_config_set_bool("QTypes", &global_qtypes, child); + cf_util_get_boolean(child, &global_qtypes); else if (strcasecmp("ServerStats", child->key) == 0) - bind_config_set_bool("ServerStats", &global_server_stats, child); + cf_util_get_boolean(child, &global_server_stats); else if (strcasecmp("ZoneMaintStats", child->key) == 0) - bind_config_set_bool("ZoneMaintStats", &global_zone_maint_stats, child); + cf_util_get_boolean(child, &global_zone_maint_stats); else if (strcasecmp("ResolverStats", child->key) == 0) - bind_config_set_bool("ResolverStats", &global_resolver_stats, child); + cf_util_get_boolean(child, &global_resolver_stats); else if (strcasecmp("MemoryStats", child->key) == 0) - bind_config_set_bool("MemoryStats", &global_memory_stats, child); + cf_util_get_boolean(child, &global_memory_stats); else if (strcasecmp("View", child->key) == 0) bind_config_add_view(child); else if (strcasecmp("ParseTime", child->key) == 0) @@ -1610,8 +1547,6 @@ static int bind_init(void) /* {{{ */ static int bind_read(void) /* {{{ */ { - int status; - if (curl == NULL) { ERROR("bind plugin: I don't have a CURL object."); return -1; @@ -1626,7 +1561,7 @@ static int bind_read(void) /* {{{ */ return -1; } - status = bind_xml(bind_buffer); + int status = bind_xml(bind_buffer); if (status != 0) return -1; else diff --git a/src/ceph.c b/src/ceph.c index 5ea90499..26cf2155 100644 --- a/src/ceph.c +++ b/src/ceph.c @@ -148,7 +148,7 @@ enum perfcounter_type_d { }; /** Give user option to use default (long run = since daemon started) avg */ -static int long_run_latency_avg = 0; +static int long_run_latency_avg; /** * Give user option to use default type for special cases - @@ -161,10 +161,10 @@ static int long_run_latency_avg = 0; static int convert_special_metrics = 1; /** Array of daemons to monitor */ -static struct ceph_daemon **g_daemons = NULL; +static struct ceph_daemon **g_daemons; /** Number of elements in g_daemons */ -static size_t g_num_daemons = 0; +static size_t g_num_daemons; /** * A set of data that we build up in memory while parsing the JSON. @@ -251,7 +251,7 @@ static int ceph_cb_boolean(void *ctx, int bool_val) { return CEPH_CB_CONTINUE; } if (dest_size > dest_len) { \ sstrncpy((dest) + dest_len, (src), dest_size - dest_len); \ } \ - (dest)[dest_size - 1] = 0; \ + (dest)[dest_size - 1] = '\0'; \ } while (0) static int ceph_cb_number(void *ctx, const char *number_val, @@ -350,7 +350,7 @@ static int ceph_cb_map_key(void *ctx, const unsigned char *key, } memmove(state->key, key, sz - 1); - state->key[sz - 1] = 0; + state->key[sz - 1] = '\0'; return CEPH_CB_CONTINUE; } @@ -406,8 +406,8 @@ static int compact_ds_name(char *buffer, size_t buffer_size, char const *src) { size_t src_len; char *ptr = buffer; size_t ptr_size = buffer_size; - _Bool append_plus = 0; - _Bool append_minus = 0; + bool append_plus = false; + bool append_minus = false; if ((buffer == NULL) || (buffer_size <= strlen("Minus")) || (src == NULL)) return EINVAL; @@ -417,11 +417,11 @@ static int compact_ds_name(char *buffer, size_t buffer_size, char const *src) { /* Remove trailing "+" and "-". */ if (src_copy[src_len - 1] == '+') { - append_plus = 1; + append_plus = true; src_len--; src_copy[src_len] = 0; } else if (src_copy[src_len - 1] == '-') { - append_minus = 1; + append_minus = true; src_len--; src_copy[src_len] = 0; } @@ -472,19 +472,19 @@ static int compact_ds_name(char *buffer, size_t buffer_size, char const *src) { return 0; } -static _Bool has_suffix(char const *str, char const *suffix) { +static bool has_suffix(char const *str, char const *suffix) { size_t str_len = strlen(str); size_t suffix_len = strlen(suffix); size_t offset; if (suffix_len > str_len) - return 0; + return false; offset = str_len - suffix_len; if (strcmp(str + offset, suffix) == 0) - return 1; + return true; - return 0; + return false; } static void cut_suffix(char *buffer, size_t buffer_size, char const *str, @@ -1032,7 +1032,7 @@ static void cconn_close(struct cconn *io) { static int cconn_process_data(struct cconn *io, yajl_struct *yajl, yajl_handle hand) { int ret; - struct values_tmp *vtmp = calloc(1, sizeof(struct values_tmp) * 1); + struct values_tmp *vtmp = calloc(1, sizeof(*vtmp)); if (!vtmp) { return -ENOMEM; } @@ -1142,8 +1142,8 @@ static int cconn_validate_revents(struct cconn *io, int revents) { } /** Handle a network event for a connection */ -static int cconn_handle_event(struct cconn *io) { - int ret; +static ssize_t cconn_handle_event(struct cconn *io) { + ssize_t ret; switch (io->state) { case CSTATE_UNCONNECTED: ERROR("ceph plugin: cconn_handle_event(name=%s) got to illegal " @@ -1158,7 +1158,7 @@ static int cconn_handle_event(struct cconn *io) { size_t cmd_len = strlen(cmd); RETRY_ON_EINTR( ret, write(io->asok, ((char *)&cmd) + io->amt, cmd_len - io->amt)); - DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,amt=%d,ret=%d)", + DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,amt=%d,ret=%zd)", io->d->name, io->state, io->amt, ret); if (ret < 0) { return ret; @@ -1180,7 +1180,7 @@ static int cconn_handle_event(struct cconn *io) { case CSTATE_READ_VERSION: { RETRY_ON_EINTR(ret, read(io->asok, ((char *)(&io->d->version)) + io->amt, sizeof(io->d->version) - io->amt)); - DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)", + DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%zd)", io->d->name, io->state, ret); if (ret < 0) { return ret; @@ -1206,7 +1206,7 @@ static int cconn_handle_event(struct cconn *io) { case CSTATE_READ_AMT: { RETRY_ON_EINTR(ret, read(io->asok, ((char *)(&io->json_len)) + io->amt, sizeof(io->json_len) - io->amt)); - DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)", + DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%zd)", io->d->name, io->state, ret); if (ret < 0) { return ret; @@ -1227,7 +1227,7 @@ static int cconn_handle_event(struct cconn *io) { case CSTATE_READ_JSON: { RETRY_ON_EINTR(ret, read(io->asok, io->json + io->amt, io->json_len - io->amt)); - DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)", + DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%zd)", io->d->name, io->state, ret); if (ret < 0) { return ret; @@ -1296,8 +1296,8 @@ static int cconn_prepare(struct cconn *io, struct pollfd *fds) { */ static int milli_diff(const struct timeval *t1, const struct timeval *t2) { int64_t ret; - int sec_diff = t1->tv_sec - t2->tv_sec; - int usec_diff = t1->tv_usec - t2->tv_usec; + long sec_diff = t1->tv_sec - t2->tv_sec; + long usec_diff = t1->tv_usec - t2->tv_usec; ret = usec_diff / 1000; ret += (sec_diff * 1000); return (ret > INT_MAX) ? INT_MAX : ((ret < INT_MIN) ? INT_MIN : (int)ret); @@ -1305,8 +1305,9 @@ static int milli_diff(const struct timeval *t1, const struct timeval *t2) { /** This handles the actual network I/O to talk to the Ceph daemons. */ -static int cconn_main_loop(uint32_t request_type) { - int ret, some_unreachable = 0; +static ssize_t cconn_main_loop(uint32_t request_type) { + int some_unreachable = 0; + ssize_t ret; struct timeval end_tv; struct cconn io_array[g_num_daemons]; @@ -1343,7 +1344,7 @@ static int cconn_main_loop(uint32_t request_type) { struct cconn *io = io_array + i; ret = cconn_prepare(io, fds + nfds); if (ret < 0) { - WARNING("ceph plugin: cconn_prepare(name=%s,i=%zu,st=%d)=%d", + WARNING("ceph plugin: cconn_prepare(name=%s,i=%" PRIsz ",st=%d)=%zd", io->d->name, i, io->state, ret); cconn_close(io); io->request_type = ASOK_REQ_NONE; @@ -1367,7 +1368,7 @@ static int cconn_main_loop(uint32_t request_type) { } RETRY_ON_EINTR(ret, poll(fds, nfds, diff)); if (ret < 0) { - ERROR("ceph plugin: poll(2) error: %d", ret); + ERROR("ceph plugin: poll(2) error: %zd", ret); goto done; } for (int i = 0; i < nfds; ++i) { @@ -1388,7 +1389,7 @@ static int cconn_main_loop(uint32_t request_type) { ret = cconn_handle_event(io); if (ret) { WARNING("ceph plugin: cconn_handle_event(name=%s," - "i=%d,st=%d): error %d", + "i=%d,st=%d): error %zd", io->d->name, i, io->state, ret); cconn_close(io); io->request_type = ASOK_REQ_NONE; @@ -1409,7 +1410,7 @@ done: return ret; } -static int ceph_read(void) { return cconn_main_loop(ASOK_REQ_DATA); } +static int ceph_read(void) { return (int)cconn_main_loop(ASOK_REQ_DATA); } /******* lifecycle *******/ static int ceph_init(void) { @@ -1436,7 +1437,7 @@ static int ceph_init(void) { return ENOENT; } - return cconn_main_loop(ASOK_REQ_VERSION); + return (int)cconn_main_loop(ASOK_REQ_VERSION); } static int ceph_shutdown(void) { diff --git a/src/ceph_test.c b/src/ceph_test.c index 45467730..e4032929 100644 --- a/src/ceph_test.c +++ b/src/ceph_test.c @@ -39,7 +39,7 @@ static int test_handler(void *user, char const *val, char const *key) { size_t i; char status[1024]; - _Bool ok; + bool ok; /* special case for latency metrics. */ if (strcmp("filestore.example_latency", key) == 0) @@ -47,7 +47,7 @@ static int test_handler(void *user, char const *val, char const *key) { snprintf(status, sizeof(status), "unexpected call: test_handler(\"%s\") = \"%s\"", key, val); - ok = 0; + ok = false; for (i = 0; i < t->cases_num; i++) { if (strcmp(key, t->cases[i].key) != 0) @@ -57,12 +57,12 @@ static int test_handler(void *user, char const *val, char const *key) { snprintf(status, sizeof(status), "test_handler(\"%s\") = \"%s\", want \"%s\"", key, val, t->cases[i].value); - ok = 0; + ok = false; break; } snprintf(status, sizeof(status), "test_handler(\"%s\") = \"%s\"", key, val); - ok = 1; + ok = true; break; } diff --git a/src/cgroups.c b/src/cgroups.c index 18e489d0..9304216d 100644 --- a/src/cgroups.c +++ b/src/cgroups.c @@ -31,7 +31,7 @@ static char const *config_keys[] = {"CGroup", "IgnoreSelected"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); -static ignorelist_t *il_cgroup = NULL; +static ignorelist_t *il_cgroup; __attribute__((nonnull(1))) __attribute__((nonnull(2))) static void cgroups_submit_one(char const *plugin_instance, char const *type_instance, @@ -79,9 +79,7 @@ static int read_cpuacct_procs(const char *dirname, char const *cgroup_name, cgroup_name); fh = fopen(abs_path, "r"); if (fh == NULL) { - char errbuf[1024]; - ERROR("cgroups plugin: fopen (\"%s\") failed: %s", abs_path, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("cgroups plugin: fopen (\"%s\") failed: %s", abs_path, STRERRNO); return -1; } @@ -114,7 +112,7 @@ static int read_cpuacct_procs(const char *dirname, char const *cgroup_name, /* Strip colon off the first column, if found */ if (key[key_len - 1] == ':') - key[key_len - 1] = 0; + key[key_len - 1] = '\0'; status = parse_value(fields[1], &value, DS_TYPE_DERIVE); if (status != 0) @@ -183,7 +181,7 @@ static int cgroups_config(const char *key, const char *value) { static int cgroups_read(void) { cu_mount_t *mnt_list = NULL; - _Bool cgroup_found = 0; + bool cgroup_found = false; if (cu_mount_getlist(&mnt_list) == NULL) { ERROR("cgroups plugin: cu_mount_getlist failed."); @@ -201,7 +199,7 @@ static int cgroups_read(void) { walk_directory(mnt_ptr->dir, read_cpuacct_root, /* user_data = */ NULL, /* include_hidden = */ 0); - cgroup_found = 1; + cgroup_found = true; /* It doesn't make sense to check other cpuacct mount-points * (if any), they contain the same data. */ break; diff --git a/src/chrony.c b/src/chrony.c index 6fb369a2..913aab94 100644 --- a/src/chrony.c +++ b/src/chrony.c @@ -38,6 +38,11 @@ #include /* 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" @@ -440,6 +445,15 @@ static int chrony_recv_response(tChrony_Response *p_resp, } } +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 @@ -964,6 +978,9 @@ static int chrony_read(void) { 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) diff --git a/src/collectd-exec.pod b/src/collectd-exec.pod index c65966bd..b8786579 100644 --- a/src/collectd-exec.pod +++ b/src/collectd-exec.pod @@ -73,7 +73,7 @@ Each line beginning with a C<#> (hash mark) is ignored. =item B I [I] I Submits one or more values (identified by I, see below) to the -daemon which will dispatch it to all it's write-plugins. +daemon which will dispatch it to all its write-plugins. An I is of the form CBIB<->IBIB<->I> with both diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c index 89f73b83..06298494 100644 --- a/src/collectd-nagios.c +++ b/src/collectd-nagios.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -93,17 +94,17 @@ typedef struct range_s range_t; extern char *optarg; extern int optind, opterr, optopt; -static char *socket_file_g = NULL; -static char *value_string_g = NULL; -static char *hostname_g = NULL; +static char *socket_file_g; +static char *value_string_g; +static char *hostname_g; static range_t range_critical_g; static range_t range_warning_g; static int consolitation_g = CON_NONE; -static _Bool nan_is_error_g = 0; +static bool nan_is_error_g; -static char **match_ds_g = NULL; -static size_t match_ds_num_g = 0; +static char **match_ds_g; +static size_t match_ds_num_g; /* `strdup' is an XSI extension. I don't want to pull in all of XSI just for * that, so here's an own implementation.. It's easy enough. The GCC attributes @@ -115,7 +116,7 @@ cn_strdup(const char *str) /* {{{ */ char *ret; strsize = strlen(str) + 1; - ret = (char *)malloc(strsize); + ret = malloc(strsize); if (ret != NULL) memcpy(ret, str, strsize); return ret; @@ -129,13 +130,13 @@ static int filter_ds(size_t *values_num, double **values, if (match_ds_g == NULL) return RET_OKAY; - new_values = (gauge_t *)calloc(match_ds_num_g, sizeof(*new_values)); + new_values = calloc(match_ds_num_g, sizeof(*new_values)); if (new_values == NULL) { fprintf(stderr, "calloc failed: %s\n", strerror(errno)); return RET_UNKNOWN; } - new_names = (char **)calloc(match_ds_num_g, sizeof(*new_names)); + new_names = calloc(match_ds_num_g, sizeof(*new_names)); if (new_names == NULL) { fprintf(stderr, "calloc failed: %s\n", strerror(errno)); free(new_values); @@ -526,7 +527,7 @@ static int do_check(lcc_connection_t *connection) { int status; snprintf(ident_str, sizeof(ident_str), "%s/%s", hostname_g, value_string_g); - ident_str[sizeof(ident_str) - 1] = 0; + ident_str[sizeof(ident_str) - 1] = '\0'; status = lcc_string_to_identifier(connection, &ident, ident_str); if (status != 0) { @@ -637,7 +638,7 @@ int main(int argc, char **argv) { break; } case 'm': - nan_is_error_g = 1; + nan_is_error_g = true; break; default: usage(argv[0]); @@ -651,7 +652,7 @@ int main(int argc, char **argv) { } snprintf(address, sizeof(address), "unix:%s", socket_file_g); - address[sizeof(address) - 1] = 0; + address[sizeof(address) - 1] = '\0'; connection = NULL; status = lcc_connect(address, &connection); diff --git a/src/collectd-nagios.pod b/src/collectd-nagios.pod index e28ff4b8..4ff0bf67 100644 --- a/src/collectd-nagios.pod +++ b/src/collectd-nagios.pod @@ -107,7 +107,7 @@ consolidations simply ignore NaN values. =head1 RETURN VALUE As usual for Nagios plugins, this program writes a short, one line status -message to STDOUT and signals success or failure with it's return value. It +message to STDOUT and signals success or failure with its return value. It exits with a return value of B<0> for I, B<1> for I and B<2> for I. If the values are not available or some other error occurred, it returns B<3> for I. diff --git a/src/collectd-python.pod b/src/collectd-python.pod index 1f46f6f3..3e44d546 100644 --- a/src/collectd-python.pod +++ b/src/collectd-python.pod @@ -126,6 +126,13 @@ normally and spawning processes from Python will work as intended. =back +=item B I + +Imports the python script I and loads it into the collectd +python process. If your python script is not found, be sure its +directory exists in python's B. You can prepend to the +B using the B configuration option. + =item EB IE block This block may be used to pass on configuration settings to a Python module. diff --git a/src/collectd-snmp.pod b/src/collectd-snmp.pod index d615088e..9d508d17 100644 --- a/src/collectd-snmp.pod +++ b/src/collectd-snmp.pod @@ -10,23 +10,24 @@ collectd-snmp - Documentation of collectd's C # ... - Type "voltage" Table false - Instance "input_line1" + Type "voltage" + TypeInstance "input_line1" Scale 0.1 Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1" - Type "users" Table false - Instance "" + Type "users" Shift -1 Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0" - Type "if_octets" Table true - Instance "IF-MIB::ifDescr" + Type "if_octets" + TypeInstanceOID "IF-MIB::ifDescr" + #FilterOID "IF-MIB::ifOperStatus" + #FilterValues "1", "2" Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets" @@ -114,8 +115,9 @@ queried using the C SNMP command (see L) and transmitted to collectd. B value list is dispatched and, eventually, one file will be written. -When B is set to B, the OIDs given to B (see below) are -queried using the C SNMP command until the subtree is left. After all +When B
is set to B, the OIDs given to B, B, +B, B and B (see below) are queried using +the C SNMP command until the subtree is left. After all the lists (think: all columns of the table) have been read B values sets will be dispatches and, eventually, several files will be written. If you configure a B (see above) which needs more than one data source (for @@ -138,33 +140,66 @@ C and C. But, this is because of the B setting, not the B
setting. Since the semantic of B and B depends on this setting you -need to set it before setting them. Doing vice verse will result in undefined +need to set it before setting them. Doing vice versa will result in undefined behavior. -=item B I +=item B I -Sets the type-instance of the values that are dispatched. The meaning of this -setting depends on whether B
is set to I or I: +Use I as the plugin name of the values that are dispatched. +Defaults to C. -If B
is set to I, I is interpreted as an SNMP-prefix -that will return a list of values. Those values are then used as the actual -type-instance. An example would be the C subtree. -L from the SNMP distribution describes the format of OIDs. +=item B I -If B
is set to I and B is omitted, then "SUBID" will be -used as the instance. +Sets the plugin-instance of the values that are dispatched to I value. -If B
is set to I the actual string configured for I is -copied into the value-list. In this case I may be empty, i.Ee. -"". +When B
is set to I and B is set then this option +has no effect. -=item B I +Defaults to an empty string. + +=item B I + +Sets the type-instance of the values that are dispatched to I value. -If B
is set to I, you may feel the need to add something to the -instance of the files. If set, I is prepended to the instance as -determined by querying the agent. When B
is set to I this option +When B
is set to I and B is set then this option has no effect. +Defaults to an empty string. + +=item B I + +=item B I + +=item B I + +If B
is set to I, I is interpreted as an SNMP-prefix that will +return a list of values. Those values are then used as the actual type-instance, +plugin-instance or host of dispatched metrics. An example would be the +C subtree. L from the SNMP distribution describes +the format of OIDs. When option is set to empty string, then "SUBID" will be used +as the value. + +Prefix may be set for values with use of appropriate B, +B and B options. + +When B
is set to I these options has no effect. + +Defaults: When no one of these options is configured explicitly, +B defaults to an empty string. + +=item B + +=item B + +=item B + +These options are intented to be used together with B, +B and B respectively. + +If set, I is preprended to values received by querying the agent. + +When B
is set to I these options has no effect. + The C is an example where you need this setting: It has voltages of the inlets, outlets and the battery of an UPS. However, it doesn't provide a descriptive column for these voltages. In this case having 1, 2,E... as @@ -172,6 +207,25 @@ instances is not enough, because the inlet voltages and outlet voltages may both have the subids 1, 2,E... You can use this setting to distinguish between the different voltages. +=item B I + +Attention: this option exists for backwards compatibility only and will be +removed in next major release. Please use B / B +instead. + +The meaning of this setting depends on whether B
is set to I or +I. + +If B
is set to I, option behaves as B. +If B
is set to I, option behaves as B. + +Note what B
option must be set before setting B. + +=item B I + +Attention: this option exists for backwards compatibility only and will be +removed in next major release. Please use B instead. + =item B I [I ...] Configures the values to be queried from the SNMP host. The meaning slightly @@ -208,16 +262,39 @@ This value is not applied to counter-values. =item B I [, I ...] -The ignore values allows one to ignore Instances based on their name and the -patterns specified by the various values you've entered. The match is a +The ignore values allows one to ignore TypeInstances based on their name and +the patterns specified by the various values you've entered. The match is a glob-type shell matching. +When B
is set to I then this option has no effect. + =item B I The invertmatch value should be use in combination of the Ignore option. It changes the behaviour of the Ignore option, from a blacklist behaviour when InvertMatch is set to false, to a whitelist when specified to true. +=item B I + +=item B I [, I ...] + +=item B I + +When B
is set to I, these options allow to configure filtering +based on MIB values. + +The B declares I to fill table column with values. +The B declares values list to do match. Whether table row will be +collected or ignored depends on the B setting. +As with other plugins that use the daemon's ignorelist functionality, a string +that starts and ends with a slash is interpreted as a regular expression. + +If no selection is configured at all, B table rows are selected. + +When B
is set to I then these options has no effect. + +See B
and F for details. + =back =head2 The Host block diff --git a/src/collectd-tg.c b/src/collectd-tg.c index 48f2dc43..5210a221 100644 --- a/src/collectd-tg.c +++ b/src/collectd-tg.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -61,12 +62,12 @@ static const char *conf_service = NET_DEFAULT_PORT; static lcc_network_t *net; -static c_heap_t *values_heap = NULL; +static c_heap_t *values_heap; static struct sigaction sigint_action; static struct sigaction sigterm_action; -static _Bool loop = 1; +static bool loop = true; __attribute__((noreturn)) static void exit_usage(int exit_status) /* {{{ */ { @@ -94,9 +95,9 @@ __attribute__((noreturn)) static void exit_usage(int exit_status) /* {{{ */ exit(exit_status); } /* }}} void exit_usage */ -static void signal_handler(int signal) /* {{{ */ +static void signal_handler(int __attribute__((unused)) signal) /* {{{ */ { - loop = 0; + loop = false; } /* }}} void signal_handler */ #if HAVE_CLOCK_GETTIME @@ -104,7 +105,7 @@ static double dtime(void) /* {{{ */ { struct timespec ts = {0}; - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) perror("clock_gettime"); return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9; @@ -146,7 +147,8 @@ static int get_boundet_random(int min, int max) /* {{{ */ range = max - min; - return min + ((int)(((double)range) * ((double)random()) / (((double)RAND_MAX) + 1.0))); + return min + ((int)(((double)range) * ((double)random()) / + (((double)RAND_MAX) + 1.0))); } /* }}} int get_boundet_random */ static lcc_value_list_t *create_value_list(void) /* {{{ */ @@ -194,7 +196,7 @@ static lcc_value_list_t *create_value_list(void) /* {{{ */ strncpy(vl->identifier.type, (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive", sizeof(vl->identifier.type)); - vl->identifier.type[sizeof(vl->identifier.type) - 1] = 0; + vl->identifier.type[sizeof(vl->identifier.type) - 1] = '\0'; snprintf(vl->identifier.type_instance, sizeof(vl->identifier.type_instance), "ti%li", random()); diff --git a/src/collectd-threshold.pod b/src/collectd-threshold.pod index 35f8a9fc..14f2c8ce 100644 --- a/src/collectd-threshold.pod +++ b/src/collectd-threshold.pod @@ -40,7 +40,7 @@ Also, all values that match a threshold are considered to be relevant or "interesting". As a consequence collectd will issue a notification if they are not received for B iterations. The B configuration option is explained in section L. If, for example, -B is set to "2" (the default) and some hosts sends it's CPU statistics +B is set to "2" (the default) and some hosts sends its CPU statistics to the server every 60 seconds, a notification will be dispatched after about 120 seconds. It may take a little longer because the timeout is checked only once each B on the server. diff --git a/src/collectd-unixsock.pod b/src/collectd-unixsock.pod index b241a9f3..db7000a1 100644 --- a/src/collectd-unixsock.pod +++ b/src/collectd-unixsock.pod @@ -84,7 +84,7 @@ Example: =item B I [I] I Submits one or more values (identified by I, see below) to the -daemon which will dispatch it to all it's write-plugins. +daemon which will dispatch it to all its write-plugins. An I is of the form CBIB<->IBIB<->I> with both diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 5eb17c4d..f58d3b49 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -90,6 +90,7 @@ #@BUILD_PLUGIN_AGGREGATION_TRUE@LoadPlugin aggregation #@BUILD_PLUGIN_AMQP_TRUE@LoadPlugin amqp +#@BUILD_PLUGIN_AMQP1_TRUE@LoadPlugin amqp1 #@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache #@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups #@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors @@ -172,6 +173,7 @@ #@BUILD_PLUGIN_ORACLE_TRUE@LoadPlugin oracle #@BUILD_PLUGIN_OVS_EVENTS_TRUE@LoadPlugin ovs_events #@BUILD_PLUGIN_OVS_STATS_TRUE@LoadPlugin ovs_stats +#@BUILD_PLUGIN_PCIE_ERRORS_TRUE@LoadPlugin pcie_errors #@BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl #@BUILD_PLUGIN_PINBA_TRUE@LoadPlugin pinba #@BUILD_PLUGIN_PING_TRUE@LoadPlugin ping @@ -220,6 +222,7 @@ #@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 @@ -269,6 +272,29 @@ # # +# +# +# Host "localhost" +# Port "5672" +# User "guest" +# Password "guest" +# Address "collectd" +# RetryDelay 1 +# +# Format JSON +# PreSettle false +# +# +# Format JSON +# PreSettle true +# +# +# Format JSON +# PreSettle false +# +# +# + # # # URL "http://localhost/status?auto" @@ -631,6 +657,12 @@ # PauseConnect 5 # +# +# GPUIndex 0 +# GPUIndex 2 +# IgnoreSelected false +# + # # # EnableSSL true @@ -643,6 +675,7 @@ # SSLCACertificateFile "/path/to/root.pem" # SSLCertificateFile "/path/to/client.pem" # SSLCertificateKeyFile "/path/to/client.key" +# VerifyPeer true # # @@ -689,6 +722,9 @@ # NotifySensorNotPresent false # NotifyIPMIConnectionState false # SELEnabled false +# SELSensor "some_sensor" +# SELSensor "another_one" +# SELIgnoreSelected false # SELClearEvent false # # @@ -705,6 +741,9 @@ # NotifySensorNotPresent false # NotifyIPMIConnectionState false # SELEnabled false +# SELSensor "some_sensor" +# SELSensor "another_one" +# SELIgnoreSelected false # SELClearEvent false # # @@ -809,6 +848,8 @@ # RegisterType float # Type gauge # Instance "..." +# #Scale 1.0 +# #Shift 0.0 # # # @@ -1097,6 +1138,12 @@ # Bridges "br0" "br_ext" # +# +# Source "sysfs" +# ReportMasked false +# PersistentNotifications false +# + # # IncludeDir "/my/include/path" # BaseName "Collectd::Plugins" @@ -1126,6 +1173,7 @@ # Timeout 0.9 # TTL 255 # SourceAddress "1.2.3.4" +# AddressFamily "any" # Device "eth0" # MaxMissed -1 # @@ -1201,11 +1249,13 @@ # CollectFileDescriptor true # CollectContextSwitch true # CollectMemoryMaps true +# CollectDelayAccounting false # Process "name" # ProcessMatch "name" "regex" # # CollectFileDescriptor false # CollectContextSwitch false +# CollectDelayAccounting true # # # CollectFileDescriptor false @@ -1233,7 +1283,13 @@ # # Host "redis.example.com" # Port "6379" +# #Socket "/var/run/redis/redis.sock" # Timeout 2000 +# +# #Database 0 +# Type "queue_length" +# Instance "myqueue" +# # # @@ -1249,6 +1305,7 @@ # CollectMemory true # CollectDF true # CollectDisk true +# CollectHealth true # # @@ -1296,21 +1353,31 @@ # # -# Type "voltage" # Table false -# Instance "input_line1" +# Type "voltage" +# TypeInstance "input_line1" # Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1" # # -# Type "users" # Table false -# Instance "" +# Type "users" +# TypeInstance "" # Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0" # # +# Table true # Type "if_octets" +# TypeInstanceOID "IF-MIB::ifDescr" +# #TypeInstancePrefix "port" +# Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets" +# #FilterOID "IF-MIB::ifOperStatus" +# #FilterValues "1", "2" +# +# # Table true -# Instance "IF-MIB::ifDescr" +# Type "if_octets" +# Plugin "interface" +# PluginInstanceOID "IF-MIB::ifDescr" # Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets" # # @@ -1351,7 +1418,9 @@ # IndexOID "IF-MIB::ifIndex" # SizeOID "IF-MIB::ifNumber" # -# Instance true +# +# Source "PluginInstance" +# # Plugin "interface" # OIDs "IF-MIB::ifDescr" # @@ -1595,16 +1664,21 @@ # Connection "xen:///" # RefreshInterval 60 # Domain "name" +# ReportBlockDevices true +# ReportNetworkInterfaces true # BlockDevice "name:device" # BlockDeviceFormat target # BlockDeviceFormatBasename false # InterfaceDevice "name:device" # IgnoreSelected false # HostnameFormat name +# HostnameMetadataXPath "/instance/name/text()" +# HostnameMetadataNS "http://openstack.org/xmlns/libvirt/nova/1.0" # InterfaceFormat name # PluginInstanceFormat name # Instances 1 # ExtraStats "cpu_util disk disk_err domain_state fs_info job_stats_background pcpu perf vcpupin" +# PersistentNotification false # # @@ -1724,6 +1798,16 @@ # Attribute "foo" "bar" # +# +# Project "stackdriver-account" +# CredentialFile "/path/to/gcp-project-id-12345.json" +# Email "123456789012@developer.gserviceaccount.com" +# +# Label "project_id" "gcp-project-id" +# +# Url "https://monitoring.googleapis.com/v3" +# + # # # Host "localhost" diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 51be401b..a38a9986 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -530,9 +530,9 @@ are disabled by default. =head2 Plugin C The I can be used to communicate with other instances of -I or third party applications using an AMQP message broker. Values -are sent to or received from the broker, which handles routing, queueing and -possibly filtering out messages. +I or third party applications using an AMQP 0.9.1 message broker. +Values are sent to or received from the broker, which handles routing, +queueing and possibly filtering out messages. B @@ -738,6 +738,171 @@ is preserved, i.e. passed through. =back +=head2 Plugin C + +The I can be used to communicate with other instances of +I or third party applications using an AMQP 1.0 message +intermediary. Metric values or notifications are sent to the +messaging intermediary which may handle direct messaging or +queue based transfer. + +B + + + # Send values to an AMQP 1.0 intermediary + + Host "localhost" + Port "5672" + User "guest" + Password "guest" + Address "collectd" +# RetryDelay 1 + + Format "command" + PreSettle false + Notify false + # StoreRates false + # GraphitePrefix "collectd." + # GraphiteEscapeChar "_" + # GraphiteSeparateInstances false + # GraphiteAlwaysAppendDS false + # GraphitePreserveSeparator false + + + + +The plugin's configuration consists of a I that configures +communications to the AMQP 1.0 messaging bus and one or more I +corresponding to metric or event publishers to the messaging system. + +The address in the I block concatenated with the name given in the +I block starting tag will be used as the send-to address for +communications over the messaging link. + +The following options are accepted within each I block: + +=over 4 + +=item B I + +Hostname or IP-address of the AMQP 1.0 intermediary. Defaults to the +default behavior of the underlying communications library, +I, which is "localhost". + +=item B I + +Service name or port number on which the AMQP 1.0 intermediary accepts +connections. This argument must be a string, even if the numeric form +is used. Defaults to "5672". + +=item B I + +=item B I + +Credentials used to authenticate to the AMQP 1.0 intermediary. By +default "guest"/"guest" is used. + +=item B
I
+ +This option specifies the prefix for the send-to value in the message. +By default, "collectd" will be used. + +=item B I + +When the AMQP1 connection is lost, defines the time in seconds to wait +before attempting to reconnect. Defaults to 1, which implies attempt +to reconnect at 1 second intervals. + +=back + +The following options are accepted within each I block: + +=over 4 + +=item B B|B|B + +Selects the format in which messages are sent to the intermediary. If set to +B (the default), values are sent as C commands which are +identical to the syntax used by the I and I. In this +case, the C header field will be set to C. + +If set to B, the values are encoded in the I, +an easy and straight forward exchange format. The C header field +will be set to C. + +If set to B, values are encoded in the I format, which is +" \n". The C header field will be set to +C. + +A subscribing client I use the C header field to +determine how to decode the values. + +=item B B|B + +If set to B (the default), the plugin will wait for a message +acknowledgement from the messaging bus before sending the next +message. This indicates transfer of ownership to the messaging +system. If set to B, the plugin will not wait for a message +acknowledgement and the message may be dropped prior to transfer of +ownership. + +=item B B|B + +If set to B (the default), the plugin will service the +instance write call back as a value list. If set to B the +plugin will service the instance as a write notification callback +for alert formatting. + +=item B B|B + +Determines whether or not C, C and C data sources +are converted to a I (i.e. a C value). If set to B (the +default), no conversion is performed. Otherwise the conversion is performed +using the internal value cache. + +Please note that currently this option is only used if the B option has +been set to B. + +=item B + +A prefix can be added in the metric name when outputting in the I format. +It's added before the I name. +Metric name will be "" + +=item B + +A postfix can be added in the metric name when outputting in the I format. +It's added after the I name. +Metric name will be "" + +=item B + +Specify a character to replace dots (.) in the host part of the metric name. +In I metric name, dots are used as separators between different +metric parts (host, plugin, type). +Default is "_" (I). + +=item B B|B + +If set to B, the plugin instance and type instance will be in their own +path component, for example C. If set to B (the +default), the plugin and plugin instance (and likewise the type and type +instance) are put into one component, for example C. + +=item B B|B + +If set to B, append the name of the I (DS) to the "metric" +identifier. If set to B (the default), this is only done when there is +more than one DS. + +=item B B|B + +If set to B (the default) the C<.> (dot) character is replaced with +I. Otherwise, if set to B, the C<.> (dot) character +is preserved, i.e. passed through. + +=back + =head2 Plugin C To configure the C-plugin you first need to configure the Apache @@ -1497,6 +1662,10 @@ installed) to get the current CPU frequency. If this file does not exist make sure B (L) or a similar tool is installed and an "cpu governor" (that's a kernel module) is loaded. +If the system has the I 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 This plugin doesn't have any options. It reads CLOCK_BOOTTIME and @@ -1743,6 +1912,11 @@ plugin below on how matches are defined. If the B or B options are set to B, B blocks are optional. +=item B I + +Sets the interval (in seconds) in which the values will be collected from this +URL. By default the global B setting will be used. + =item B I The B option sets the overall timeout for HTTP requests to B, in @@ -1939,6 +2113,11 @@ Use I as the plugin instance when submitting values. May be overridden by B option inside B blocks. Defaults to an empty string (no plugin instance). +=item B I + +Sets the interval (in seconds) in which the values will be collected from this +URL. By default the global B setting will be used. + =item B I I If an XPath expression references namespaces, they must be specified @@ -3031,6 +3210,30 @@ Pause to apply between attempts of connection to gpsd in seconds (default 5 sec) =back +=head2 Plugin C + +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 + +If one or more of these options is specified, only GPUs at that index (as +determined by nvidia-utils through I) have statistics collected. +If no instance of this option is specified, all GPUs are monitored. + +=item B + +If set to true, all detected GPUs B the ones at indices specified by +B entries are collected. For greater clarity, setting IgnoreSelected +without any GPUIndex directives will result in B statistics being +collected. + +=back + =head2 Plugin C The I plugin provides an RPC interface to submit values to or query @@ -3094,6 +3297,13 @@ Whether to enable SSL for incoming connections. Default: false. Filenames specifying SSL certificate and key material to be used with SSL connections. +=item B B|B + +When enabled, a valid client certificate is required to connect to the server. +When disabled, a client certifiacte is not requested and any unsolicited client +certificate is accepted. +Enabled by default. + =back =back @@ -3464,8 +3674,23 @@ a notification is sent. Defaults to B. If system event log (SEL) is enabled, plugin will listen for sensor threshold and discrete events. When event is received the notification is sent. +SEL event filtering can be configured using B and B +config options. Defaults to B. +=item B I + +Selects sensors to get events from or to ignore, depending on B. + +See F for details. + +=item B I|I + +If no configuration is given, the B plugin will pass events from all +sensors. This option enables you to do that: By setting B +to I the effect of B is inverted: All events from selected +sensors are ignored and all events from other sensors are passed. + =item B I|I If SEL clear event is enabled, plugin will delete event from SEL list after @@ -4070,8 +4295,9 @@ which the sizes of physical memory vary. The B connects to a Modbus "slave" via Modbus/TCP or Modbus/RTU and reads register values. It supports reading single registers (unsigned 16Ebit -values), large integer values (unsigned 32Ebit values) and floating point -values (two registers interpreted as IEEE floats in big endian notation). +values), large integer values (unsigned 32Ebit and 64Ebit values) and +floating point values (two registers interpreted as IEEE floats in big endian +notation). B @@ -4081,6 +4307,8 @@ B RegisterCmd ReadHolding Type voltage Instance "input-1" + #Scale 1.0 + #Shift 0.0 @@ -4139,11 +4367,22 @@ Configures the base register to read from the device. If the option B has been set to B or B, this and the next register will be read (the register number is increased by one). -=item B B|B|B|B|B - -Specifies what kind of data is returned by the device. If the type is B, -B or B, two 16Ebit registers will be read and the data is -combined into one value. Defaults to B. +=item B B|B|B|B|B|B|B|B|B|B + +Specifies what kind of data is returned by the device. This defaults to +B. If the type is B, B, B, B, +B or B, two 16Ebit registers at B +and B will be read and the data is combined into one +32Evalue. For B, B and B the most significant +16Ebits are in the register at B and the least +significant 16Ebits are in the register at B. +For B, B, or B, the high and low order +registers are swapped with the most significant 16Ebits in +the B and the least significant 16Ebits in +B. If the type is B or B, four 16Ebit +registers at B, B, B and +B will be read and the data combined into one +64Evalue. =item B B|B @@ -4158,9 +4397,19 @@ supported. =item B I -Sets the type instance to use when dispatching the value to I. If +Sets the type instance to use when dispatching the value to I. If unset, an empty string (no type instance) is used. +=item B I + +The values taken from device are multiplied by I. The field is optional +and the default is B<1.0>. + +=item B I + +I is added to values from device after they have been multiplied by +B value. The field is optional and the default value is B<0.0>. + =back =item EB IE blocks @@ -5084,8 +5333,9 @@ When configuring with B only the basic statistics will be collected, namely octets, packets, and errors. These statistics are collected by the C plugin, too, so using both at the same time is no benefit. -When configured with B all counters B the basic ones, -so that no data needs to be collected twice if you use the C plugin. +When configured with B all counters B the basic ones +will be collected, so that no data needs to be collected twice if you use the +C plugin. This includes dropped packets, received multicast packets, collisions and a whole zoo of differentiated RX and TX errors. You can try the following command to get an idea of what awaits you: @@ -5227,6 +5477,12 @@ behavior is to let the kernel choose the appropriate interface. Be warned that the manual selection of an interface for unicast traffic is only necessary in rare cases. +=item B I + +Set the outgoing IP address for IP packets. This option can be used instead of +the I option to explicitly define the IP address which will be used +to send Packets to the remote server. + =item B I Sets the interval at which to re-resolve the DNS for the I. This is @@ -6019,6 +6275,7 @@ B Address "127.0.0.1" Socket "/var/run/openvswitch/db.sock" Bridges "br0" "br_ext" + InterfaceStats false The plugin provides the following configuration options: @@ -6052,6 +6309,59 @@ omitted or is empty then all OVS bridges will be monitored. Default: empty (monitor all bridges) +=item B B|B + +Indicates that the plugin should gather statistics for individual interfaces +in addition to ports. This can be useful when monitoring an OVS setup with +bond ports, where you might wish to know individual statistics for the +interfaces included in the bonds. Defaults to B. + +=back + +=head2 Plugin C + +The I plugin collects PCI Express errors from Device Status in Capability +structure and from Advanced Error Reporting Extended Capability where available. +At every read it polls config space of PCI Express devices and dispatches +notification for every error that is set. It checks for new errors at every read. +The device is indicated in plugin_instance according to format "domain:bus:dev.fn". +Errors are divided into categories indicated by type_instance: "correctable", and +for uncorrectable errors "non_fatal" or "fatal". +Fatal errors are reported as I and all others as I. + +B + + + Source "sysfs" + AccessDir "/sys/bus/pci" + ReportMasked false + PersistentNotifications false + + +B + +=over 4 + +=item B B|B + +Use B or B to read data from /sysfs or /proc. +The default value is B. + +=item B I + +Directory used to access device config space. It is optional and defaults to +/sys/bus/pci for B and to /proc/bus/pci for B. + +=item B B|B + +If true plugin will notify about errors that are set to masked in Error Mask register. +Such errors are not reported to the PCI Express Root Complex. Defaults to B. + +=item B B|B + +If false plugin will dispatch notification only on set/clear of error. +The ones already reported will be ignored. Defaults to B. + =back =head2 Plugin C @@ -6188,6 +6498,11 @@ long string is used so that the packet size of an ICMPv4 packet is exactly Sets the source address to use. I may either be a numerical network address or a network hostname. +=item B I + +Sets the address family to use. I may be "any", "ipv4" or "ipv6". This +option will be ignored if you set a B. + =item B I Sets the outgoing network device to be used. I has to specify an @@ -6860,18 +7175,21 @@ The statistics collected for matched processes are: - number of memory mapped files (under Linux) - io data (where available) - context switches (under Linux) - - minor and major pagefaults. + - minor and major pagefaults + - Delay Accounting information (Linux only, requires libmnl) B - CollectFileDescriptor true - CollectContextSwitch true + CollectFileDescriptor true + CollectContextSwitch true + CollectDelayAccounting false Process "name" ProcessMatch "name" "regex" - CollectFileDescriptor false - CollectContextSwitch false + CollectFileDescriptor false + CollectContextSwitch false + CollectDelayAccounting true CollectFileDescriptor false @@ -6901,6 +7219,18 @@ I must not contain slashes. Collect the number of context switches for matched processes. Disabled by default. +=item B I + +If enabled, collect Linux Delay Accounding information for matching processes. +Delay Accounting provides the time processes wait for the CPU to become +available, for I/O operations to finish, for pages to be swapped in and for +freed pages to be reclaimed. The metrics are reported as "seconds per second" +using the C type, e.g. C. +Disabled by default. + +This option is only available on Linux, requires the C library and +requires the C capability at runtime. + =item B I Collect number of file descriptors of matched processes. @@ -6914,9 +7244,12 @@ the Linux kernel. =back -Options B and B may be used inside -B and B blocks - then they affect corresponding match -only. Otherwise they set the default value for subsequent matches. +The B, B, +B and B options may be used inside +B and B blocks. When used there, these options affect +reporting the corresponding processes only. Outside of B and +B blocks these options set the default value for subsequent +matches. =head2 Plugin C @@ -6983,6 +7316,7 @@ multiple routers: CollectRegistrationTable true CollectDF true CollectDisk true + CollectHealth true @@ -7043,29 +7377,37 @@ Defaults to B. When enabled, the number of sectors written and bad blocks will be collected. Defaults to B. +=item B B|B + +When enabled, the health statistics will be collected. This includes the +voltage and temperature on supported hardware. +Defaults to B. + =back =head2 Plugin C -The I connects to one or more Redis servers and gathers -information about each server's state. For each server there is a I block -which configures the connection parameters for this node. +The I connects to one or more Redis servers, gathers +information about each server's state and executes user-defined queries. +For each server there is a I block which configures the connection +parameters and set of user-defined queries for this node. Host "localhost" Port "6379" + #Socket "/var/run/redis/redis.sock" Timeout 2000 + ReportCommandStats false + ReportCpuUsage true + #Database 0 Type "queue_length" Instance "myqueue" - + -The information shown in the synopsis above is the I -which is used by the plugin if no configuration is present. - =over 4 =item B I @@ -7073,7 +7415,9 @@ which is used by the plugin if no configuration is present. The B block identifies a new Redis node, that is a new Redis instance running in an specified host and port. The name for node is a canonical identifier which is used as I. It is limited to -64Echaracters in length. +128Echaracters in length. + +When no B is configured explicitly, plugin connects to "localhost:6379". =item B I @@ -7086,6 +7430,11 @@ The B option is the TCP port on which the Redis instance accepts 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 I + +Connect to Redis using the UNIX domain socket at I. If this +setting is given, the B and B settings are ignored. + =item B I Use I to authenticate when connecting to I. @@ -7093,25 +7442,47 @@ Use I to authenticate when connecting to I. =item B I The B option set the socket timeout for node response. Since the Redis -read function is blocking, you should keep this value as low as possible. Keep -in mind that the sum of all B values for all B should be lower -than B defined globally. +read function is blocking, you should keep this value as low as possible. +It is expected what B values should be lower than B defined +globally. + +Defaults to 2000 (2 seconds). + +=item B B|B + +Enables or disables reporting of statistics based on the command type, including +rate of command calls and average CPU time consumed by command processing. +Defaults to B. + +=item B B|B + +Enables or disables reporting of CPU consumption statistics. +Defaults to B. =item B I The B block identifies a query to execute against the redis server. -There may be an arbitrary number of queries to execute. +There may be an arbitrary number of queries to execute. Each query should +return single string or integer. =item B I -Within a query definition, a valid collectd type to use as when submitting +Within a query definition, a valid I to use as when submitting the result of the query. When not supplied, will default to B. +Currently only types with one datasource are supported. +See L for more details on types and their configuration. + =item B I Within a query definition, an optional type instance to use when submitting the result of the query. When not supplied will default to the escaped -command, up to 64 chars. +command, up to 128 chars. + +=item B I + +This index selects the Redis logical database to use for query. Defaults +to C<0>. =back @@ -7551,7 +7922,9 @@ B IndexOID "IF-MIB::ifIndex" SizeOID "IF-MIB::ifNumber" - Instance true + + Source "PluginInstance" + Plugin "interface" OIDs "IF-MIB::ifDescr" @@ -7562,12 +7935,44 @@ B OIDs "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
+ + + + Source "PluginInstance" + + Plugin "virt" + OIDs "LIBVIRT-HYPERVISOR-MIB::lvhAffinityDomainName" + + + Plugin "virt" + + Source "TypeInstance" + Regex "^vcpu_([0-9]{1,3})-cpu_[0-9]{1,3}$" + Group 1 + + OIDs "LIBVIRT-HYPERVISOR-MIB::lvhVCPUIndex" + + + Plugin "virt" + + Source "TypeInstance" + Regex "^vcpu_[0-9]{1,3}-cpu_([0-9]{1,3})$" + Group 1 + + OIDs "LIBVIRT-HYPERVISOR-MIB::lvhCPUIndex" + + + Plugin "virt" + Type "cpu_affinity" + OIDs "LIBVIRT-HYPERVISOR-MIB::lvhCPUAffinity" + +
There are two types of blocks that can be contained in the CPluginE snmp_agentE> block: B and B: -=head3 The B block +=head3 B block The B block defines a list OIDs that are to be handled. This block can define scalar or table OIDs. If B block is defined inside of B
@@ -7576,11 +7981,34 @@ The following options can be set: =over 4 -=item B I +=item B block + +B block contains all data needed for proper index build of snmp table. +In case more than +one table B block has B block present then multiple key index is +built. If B block defines scalar data type B has no effect and can +be omitted. + +=over 8 + +=item B I -When B is set to B, the value for requested OID is copied from -plugin instance field of corresponding collectd value. If B block defines -scalar data type B has no effect and can be omitted. +B can be set to one of the following values: "Hostname", "Plugin", +"PluginInstance", "Type", "TypeInstance". This value indicates which field of +corresponding collectd metric is taken as a SNMP table index. + +=item B I + +B option can also be used to parse strings or numbers out of +specific field. For example: type-instance field which is "vcpu1-cpu2" can be +parsed into two numeric fields CPU = 2 and VCPU = 1 and can be later used +as a table index. + +=item B I + +B number can be specified in case groups are used in regex. + +=back =item B I @@ -8124,13 +8552,14 @@ B Type "percent" Instance "dropped" - Index 1 + ValueFrom 1 Plugin "snortstats" Instance "eth0" Interval 600 Collect "snort-dropped" + #TimeFrom 0 @@ -8452,6 +8881,33 @@ dynamic number assigned by the kernel. Otherwise, CnE> is used if there is only one package and CnE-coreEmE> if there is more than one, where I is the n-th core of package I. +=item B I|I + +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 (the default): Restore the affinity by setting affinity to any/all +CPUs. + +B: 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 +L +L + =back =head2 Plugin C @@ -8721,6 +9177,40 @@ only on the host system. Only I is required. +Consider the following example config: + + + Connection "qemu:///system" + HostnameFormat "hostname" + InterfaceFormat "address" + PluginInstanceFormat "name" + + +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 is in nanoseconds. + =over 4 =item B I @@ -8813,7 +9303,7 @@ be set to C. Setting C will cause the I to be set to C. -=item B B +=item B B When the virt plugin logs data, it sets the hostname of the collected data according to this setting. The default is to use the guest name as provided by @@ -8823,7 +9313,11 @@ B means use the guest's UUID. This is useful if you want to track the same guest across migrations. B means to use the global B 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 though. + +B means use information from guest's metadata. Use +B and B to localize this information. You can also specify combinations of these fields. For example B means to concatenate the guest name and UUID (with a literal colon character @@ -8833,7 +9327,7 @@ At the moment of writing (collectd-5.5), hostname string is limited to 62 characters. In case when combination of fields exceeds 62 characters, hostname will be truncated without a warning. -=item B B|B
+=item B B|B
|B When the virt plugin logs interface data, it sets the name of the collected data according to this setting. The default is to use the path as provided by @@ -8843,23 +9337,42 @@ setting B. B
means use the interface's mac address. This is useful since the interface path might change between reboots of a guest or across migrations. -=item B B +B means use the interface's number in guest. + +=item B B When the virt plugin logs data, it sets the plugin_instance of the collected data according to this setting. The default is to not set the plugin_instance. B means use the guest's name as provided by the hypervisor. B means use the guest's UUID. +B means use information from guest's metadata. You can also specify combinations of the B and B fields. For example B means to concatenate the guest name and UUID (with a literal colon character between, thus I<"foo:1234-1234-1234-1234">). -=item B B +=item B B -How many read instances you want to use for this plugin. The default is one, -and the sensible setting is a multiple of the B value. -If you are not sure, just use the default setting. +When B is used in B or B, this +selects in which metadata namespace we will pick the hostname. The default is +I. + +=item B B + +When B is used in B or B, this +describes where the hostname is located in the libvirt metadata. The default is +I. + +=item B B|B + +Enabled by default. Allows to disable stats reporting of block devices for +whole plugin. + +=item B B|B + +Enabled by default. Allows to disable stats reporting of network interfaces for +whole plugin. =item B B @@ -8910,6 +9423,39 @@ B: I metrics can't be collected if I plugin is enabled. =back +=item B B|B + +Override default configuration to only send notifications when there is a change +in the lifecycle state of a domain. When set to true notifications will be sent +for every read cycle. Default is false. Does not affect the stats being +dispatched. + +=item B B + +How many read instances you want to use for this plugin. The default is one, +and the sensible setting is a multiple of the B value. + +This option is only useful when domains are specially tagged. +If you are not sure, just use the default setting. + +The reader instance will only query the domains with attached matching tag. +Tags should have the form of 'virt-X' where X is the reader instance number, +starting from 0. + +The special-purpose reader instance #0, guaranteed to be always present, +will query all the domains with missing or unrecognized tag, so no domain will +ever be left out. + +Domain tagging is done with a custom attribute in the libvirt domain metadata +section. Value is selected by an XPath I +expression in the I namespace. +(XPath and namespace values are not configurable yet). + +Tagging could be used by management applications to evenly spread the +load among the reader threads, or to pin on the same threads all +the libvirt domains which use the same shared storage, to minimize +the disruption in presence of storage outages. + =back =head2 Plugin C @@ -8963,6 +9509,7 @@ Synopsis: Protocol "tcp" LogSendErrors true Prefix "collectd" + UseTags false @@ -9000,13 +9547,20 @@ approach and logging errors fills syslog with unneeded messages. =item B I -When set, I is added in front of the host name. Dots and whitespace are -I escaped in this string (see B below). +When B is I, B value is added in front of the host name. +When B is I, B value is added in front of series name. + +Dots and whitespace are I escaped in this string (see B +below). =item B I -When set, I is appended to the host name. Dots and whitespace are -I escaped in this string (see B below). +When B is I, B value appended to the host name. +When B is I, B value appended to the end of series name +(before the first ; that separates the name from the tags). + +Dots and whitespace are I escaped in this string (see B +below). =item B I @@ -9028,6 +9582,8 @@ path component, for example C. If set to B (the default), the plugin and plugin instance (and likewise the type and type instance) are put into one component, for example C. +Option value is not used when B is I. + =item B B|B If set to B, append the name of the I (DS) to the "metric" @@ -9040,12 +9596,31 @@ If set to B (the default) the C<.> (dot) character is replaced with I. Otherwise, if set to B, the C<.> (dot) character is preserved, i.e. passed through. +Option value is not used when B is I. + =item B B|B If set to B, detect and remove duplicate components in Graphite metric names. For example, the metric name C will be shortened to C. +=item B B|B + +If set to B, Graphite metric names will be generated as tagged series. +This allows for much more flexibility than the traditional hierarchical layout. + +Example: +C + +You can use B option to add more tags by specifying it like +C<;tag1=value1;tag2=value2>. Note what tagging support was added since Graphite +version 1.1.x. + +If set to B, the B and B settings +are not used. + +Default value: B. + =back =head2 Plugin C @@ -9214,6 +9789,13 @@ B =over 4 +=item B I + +Bind to the hostname / address I. 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 I Port the embedded webserver should listen on. Defaults to B<9103>. @@ -9471,17 +10053,26 @@ been set to B. =item B (B=I only) A prefix can be added in the metric name when outputting in the I -format. It's added before the I name. +format. + +When B is I, prefix is added before the I name. Metric name will be CprefixEEhostEEpostfixEEpluginEEtypeEEnameE> +When B is I, prefix is added in front of series name. + =item B (B=I only) A postfix can be added in the metric name when outputting in the I -format. It's added after the I name. +format. + +When B is I, postfix is added after the I name. Metric name will be CprefixEEhostEEpostfixEEpluginEEtypeEEnameE> +When B is I, prefix value appended to the end of series +name (before the first ; that separates the name from the tags). + =item B (B=I only) Specify a character to replace dots (.) in the host part of the metric name. @@ -9496,6 +10087,8 @@ path component, for example C. If set to B (the default), the plugin and plugin instance (and likewise the type and type instance) are put into one component, for example C. +Option value is not used when B is I. + =item B B|B If set to B, append the name of the I (DS) to the "metric" @@ -9508,6 +10101,14 @@ If set to B (the default) the C<.> (dot) character is replaced with I. Otherwise, if set to B, the C<.> (dot) character is preserved, i.e. passed through. +Option value is not used when B is I. + +=item B B|B + +If set to B Graphite metric names will be generated as tagged series. + +Default value: B. + =item B B|B If set to B (the default), convert counter values to rates. If set to @@ -9868,6 +10469,133 @@ attribute for each metric being sent out to I. =back +=head2 Plugin C + +The C plugin writes metrics to the +I service. + +This plugin supports two authentication methods: When configured, credentials +are read from the JSON credentials file specified with B. +Alternatively, when running on +I (GCE), an I token is retrieved from the +I and used to authenticate to GCM. + +B + + + CredentialFile "/path/to/service_account.json" + + Label "project_id" "monitored_project" + + + +=over 4 + +=item B I + +Path to a JSON credentials file holding the credentials for a GCP service +account. + +If B is not specified, the plugin uses I. That means which credentials are used depends on the environment: + +=over 4 + +=item + +The environment variable C 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 command line utility are +stored. You can use C 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 option below. + +=back + +=item B I + +The I or the I of the I. The +I is a string identifying the GCP project, which you can chose +freely when creating a new project. The I is a 12-digit decimal +number. You can look up both on the I. + +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 I (GCE only) + +Choses the GCE I used for authentication. + +Each GCE instance has a C I but may also be +associated with additional I. This is often used to restrict +the permissions of services running on the GCE instance to the required +minimum. The I requires the +C scope. When multiple I are available, this option selects which one is used by +I. + +=item B I + +Configures the I to use when storing metrics. +More information on I and I are +available at L. + +This block takes one string argument, the I. Inside the block are +one or more B
expects a single string argument."); return 1; } tbl_t *tbl = realloc(tables, (tables_num + 1) * sizeof(*tables)); - if (NULL == tbl) { - char errbuf[1024]; - log_err("realloc failed: %s.", sstrerror(errno, errbuf, sizeof(errbuf))); + if (tbl == NULL) { + log_err("realloc failed: %s.", STRERRNO); return -1; } @@ -255,16 +237,16 @@ static int tbl_config_table(oconfig_item_t *ci) { tbl = tables + tables_num; tbl_setup(tbl, ci->values[0].value.string); - for (size_t i = 0; i < ((size_t)ci->children_num); ++i) { + for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *c = ci->children + i; - if (0 == strcasecmp(c->key, "Separator")) - tbl_config_set_s(c->key, &tbl->sep, c); - else if (0 == strcasecmp(c->key, "Plugin")) - tbl_config_set_s(c->key, &tbl->plugin_name, c); - else if (0 == strcasecmp(c->key, "Instance")) - tbl_config_set_s(c->key, &tbl->instance, c); - else if (0 == strcasecmp(c->key, "Result")) + if (strcasecmp(c->key, "Separator") == 0) + cf_util_get_string(c, &tbl->sep); + else if (strcasecmp(c->key, "Plugin") == 0) + cf_util_get_string(c, &tbl->plugin_name); + else if (strcasecmp(c->key, "Instance") == 0) + cf_util_get_string(c, &tbl->instance); + else if (strcasecmp(c->key, "Result") == 0) tbl_config_result(tbl, c); else log_warn("Ignoring unknown config key \"%s\" " @@ -273,25 +255,25 @@ static int tbl_config_table(oconfig_item_t *ci) { } int status = 0; - if (NULL == tbl->sep) { + if (tbl->sep == NULL) { log_err("Table \"%s\" does not specify any separator.", tbl->file); status = 1; } else { strunescape(tbl->sep, strlen(tbl->sep) + 1); } - if (NULL == tbl->instance) { + if (tbl->instance == NULL) { tbl->instance = sstrdup(tbl->file); replace_special(tbl->instance, strlen(tbl->instance)); } - if (NULL == tbl->results) { + if (tbl->results == NULL) { assert(tbl->results_num == 0); log_err("Table \"%s\" does not specify any (valid) results.", tbl->file); status = 1; } - if (0 != status) { + if (status != 0) { tbl_clear(tbl); return status; } @@ -316,7 +298,7 @@ static int tbl_config(oconfig_item_t *ci) { for (int i = 0; i < ci->children_num; ++i) { oconfig_item_t *c = ci->children + i; - if (0 == strcasecmp(c->key, "Table")) + if (strcasecmp(c->key, "Table") == 0) tbl_config_table(c); else log_warn("Ignoring unknown config key \"%s\".", c->key); @@ -333,14 +315,14 @@ static int tbl_prepare(tbl_t *tbl) { tbl_result_t *res = tbl->results + i; res->ds = plugin_get_ds(res->type); - if (NULL == res->ds) { + if (res->ds == NULL) { log_err("Unknown type \"%s\". See types.db(5) for details.", res->type); return -1; } if (res->values_num != res->ds->ds_num) { - log_err("Invalid type \"%s\". Expected %zu data source%s, " - "got %zu.", + log_err("Invalid type \"%s\". Expected %" PRIsz " data source%s, " + "got %" PRIsz ".", res->type, res->values_num, (1 == res->values_num) ? "" : "s", res->ds->ds_num); return -1; @@ -360,16 +342,13 @@ static int tbl_result_dispatch(tbl_t *tbl, tbl_result_t *res, char **fields, value_list_t vl = VALUE_LIST_INIT; value_t values[res->values_num]; - assert(NULL != res->ds); + assert(res->ds); assert(res->values_num == res->ds->ds_num); for (size_t i = 0; i < res->values_num; ++i) { - char *value; - assert(res->values[i] < fields_num); - value = fields[res->values[i]]; - - if (0 != parse_value(value, &values[i], res->ds->ds[i].type)) + char *value = fields[res->values[i]]; + if (parse_value(value, &values[i], res->ds->ds[i].type) != 0) return -1; } @@ -381,8 +360,8 @@ static int tbl_result_dispatch(tbl_t *tbl, tbl_result_t *res, char **fields, sstrncpy(vl.plugin_instance, tbl->instance, sizeof(vl.plugin_instance)); sstrncpy(vl.type, res->type, sizeof(vl.type)); - if (0 == res->instances_num) { - if (NULL != res->instance_prefix) + if (res->instances_num == 0) { + if (res->instance_prefix) sstrncpy(vl.type_instance, res->instance_prefix, sizeof(vl.type_instance)); } else { @@ -398,17 +377,15 @@ static int tbl_result_dispatch(tbl_t *tbl, tbl_result_t *res, char **fields, STATIC_ARRAY_SIZE(instances), "-"); instances_str[sizeof(instances_str) - 1] = '\0'; - vl.type_instance[sizeof(vl.type_instance) - 1] = '\0'; - if (NULL == res->instance_prefix) - strncpy(vl.type_instance, instances_str, sizeof(vl.type_instance)); + int r; + if (res->instance_prefix == NULL) + r = snprintf(vl.type_instance, sizeof(vl.type_instance), "%s", + instances_str); else - snprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s", - res->instance_prefix, instances_str); - - if ('\0' != vl.type_instance[sizeof(vl.type_instance) - 1]) { - vl.type_instance[sizeof(vl.type_instance) - 1] = '\0'; + r = snprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s", + res->instance_prefix, instances_str); + if ((size_t)r >= sizeof(vl.type_instance)) log_warn("Truncated type instance: %s.", vl.type_instance); - } } plugin_dispatch_values(&vl); @@ -417,15 +394,13 @@ static int tbl_result_dispatch(tbl_t *tbl, tbl_result_t *res, char **fields, static int tbl_parse_line(tbl_t *tbl, char *line, size_t len) { char *fields[tbl->max_colnum + 1]; - char *ptr, *saveptr; - size_t i = 0; - ptr = line; - saveptr = NULL; - while (NULL != (fields[i] = strtok_r(ptr, tbl->sep, &saveptr))) { + char *ptr = line; + char *saveptr = NULL; + while ((fields[i] = strtok_r(ptr, tbl->sep, &saveptr)) != NULL) { ptr = NULL; - ++i; + i++; if (i > tbl->max_colnum) break; @@ -433,14 +408,14 @@ static int tbl_parse_line(tbl_t *tbl, char *line, size_t len) { if (i <= tbl->max_colnum) { log_warn("Not enough columns in line " - "(expected at least %zu, got %zu).", + "(expected at least %" PRIsz ", got %" PRIsz ").", tbl->max_colnum + 1, i); return -1; } for (i = 0; i < tbl->results_num; ++i) - if (0 != tbl_result_dispatch(tbl, tbl->results + i, fields, - STATIC_ARRAY_SIZE(fields))) { + if (tbl_result_dispatch(tbl, tbl->results + i, fields, + STATIC_ARRAY_SIZE(fields)) != 0) { log_err("Failed to dispatch result."); continue; } @@ -448,34 +423,29 @@ static int tbl_parse_line(tbl_t *tbl, char *line, size_t len) { } /* tbl_parse_line */ static int tbl_read_table(tbl_t *tbl) { - FILE *fh; char buf[4096]; - fh = fopen(tbl->file, "r"); - if (NULL == fh) { - char errbuf[1024]; - log_err("Failed to open file \"%s\": %s.", tbl->file, - sstrerror(errno, errbuf, sizeof(errbuf))); + FILE *fh = fopen(tbl->file, "r"); + if (fh == NULL) { + log_err("Failed to open file \"%s\": %s.", tbl->file, STRERRNO); return -1; } buf[sizeof(buf) - 1] = '\0'; - while (NULL != fgets(buf, sizeof(buf), fh)) { - if ('\0' != buf[sizeof(buf) - 1]) { + while (fgets(buf, sizeof(buf), fh) != NULL) { + if (buf[sizeof(buf) - 1] != '\0') { buf[sizeof(buf) - 1] = '\0'; log_warn("Table %s: Truncated line: %s", tbl->file, buf); } - if (0 != tbl_parse_line(tbl, buf, sizeof(buf))) { + if (tbl_parse_line(tbl, buf, sizeof(buf)) != 0) { log_warn("Table %s: Failed to parse line: %s", tbl->file, buf); continue; } } - if (0 != ferror(fh)) { - char errbuf[1024]; - log_err("Failed to read from file \"%s\": %s.", tbl->file, - sstrerror(errno, errbuf, sizeof(errbuf))); + if (ferror(fh) != 0) { + log_err("Failed to read from file \"%s\": %s.", tbl->file, STRERRNO); fclose(fh); return -1; } @@ -491,18 +461,18 @@ static int tbl_read_table(tbl_t *tbl) { static int tbl_read(void) { int status = -1; - if (0 == tables_num) + if (tables_num == 0) return 0; for (size_t i = 0; i < tables_num; ++i) { tbl_t *tbl = tables + i; - if (0 != tbl_prepare(tbl)) { + if (tbl_prepare(tbl) != 0) { log_err("Failed to prepare and parse table \"%s\".", tbl->file); continue; } - if (0 == tbl_read_table(tbl)) + if (tbl_read_table(tbl) == 0) status = 0; tbl_finish(tbl); @@ -518,7 +488,7 @@ static int tbl_shutdown(void) { } /* tbl_shutdown */ static int tbl_init(void) { - if (0 == tables_num) + if (tables_num == 0) return 0; plugin_register_read("table", tbl_read); diff --git a/src/tail.c b/src/tail.c index fbba4788..df94580c 100644 --- a/src/tail.c +++ b/src/tail.c @@ -54,14 +54,17 @@ struct ctail_config_match_s { int flags; char *type; char *type_instance; - cdtime_t interval; latency_config_t latency; }; typedef struct ctail_config_match_s ctail_config_match_t; -static cu_tail_match_t **tail_match_list = NULL; -static size_t tail_match_list_num = 0; -static cdtime_t tail_match_list_intervals[255]; +static size_t tail_file_num; + +static int ctail_read(user_data_t *ud); + +static void ctail_match_free(void *arg) { + tail_match_destroy((cu_tail_match_t *)arg); +} /* void ctail_match_free */ static int ctail_config_add_match_dstype(ctail_config_match_t *cm, oconfig_item_t *ci) { @@ -92,7 +95,7 @@ static int ctail_config_add_match_dstype(ctail_config_match_t *cm, } 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) { @@ -134,10 +137,9 @@ static int ctail_config_add_match_dstype(ctail_config_match_t *cm, return 0; } /* int ctail_config_add_match_dstype */ -static int ctail_config_add_match(cu_tail_match_t *tm, - const char *plugin_name, +static int ctail_config_add_match(cu_tail_match_t *tm, const char *plugin_name, const char *plugin_instance, - oconfig_item_t *ci, cdtime_t interval) { + oconfig_item_t *ci) { ctail_config_match_t cm = {0}; int status; @@ -194,8 +196,8 @@ static int ctail_config_add_match(cu_tail_match_t *tm, // TODO(octo): there's nothing "simple" about the latency stuff … status = tail_match_add_match_simple( tm, cm.regex, cm.excluderegex, cm.flags, - (plugin_name != NULL) ? plugin_name : "tail", plugin_instance, - cm.type, cm.type_instance, cm.latency, interval); + (plugin_name != NULL) ? plugin_name : "tail", plugin_instance, cm.type, + cm.type_instance, cm.latency); if (status != 0) ERROR("tail plugin: tail_match_add_match_simple failed."); @@ -234,14 +236,13 @@ static int ctail_config_add_file(oconfig_item_t *ci) { int status = 0; if (strcasecmp("Plugin", option->key) == 0) - status = cf_util_get_string (option, &plugin_name); + status = cf_util_get_string(option, &plugin_name); else if (strcasecmp("Instance", option->key) == 0) status = cf_util_get_string(option, &plugin_instance); else if (strcasecmp("Interval", option->key) == 0) cf_util_get_cdtime(option, &interval); else if (strcasecmp("Match", option->key) == 0) { - status = ctail_config_add_match(tm, plugin_name, plugin_instance, option, - interval); + status = ctail_config_add_match(tm, plugin_name, plugin_instance, option); if (status == 0) num_matches++; /* Be mild with failed matches.. */ @@ -262,23 +263,15 @@ static int ctail_config_add_file(oconfig_item_t *ci) { ci->values[0].value.string); tail_match_destroy(tm); return -1; - } else { - cu_tail_match_t **temp; - - temp = realloc(tail_match_list, - sizeof(cu_tail_match_t *) * (tail_match_list_num + 1)); - if (temp == NULL) { - ERROR("tail plugin: realloc failed."); - tail_match_destroy(tm); - return -1; - } - - tail_match_list = temp; - tail_match_list[tail_match_list_num] = tm; - tail_match_list_intervals[tail_match_list_num] = interval; - tail_match_list_num++; } + char str[255]; + snprintf(str, sizeof(str), "tail-%zu", tail_file_num++); + + plugin_register_complex_read( + NULL, str, ctail_read, interval, + &(user_data_t){.data = tm, .free_func = ctail_match_free}); + return 0; } /* int ctail_config_add_file */ @@ -308,40 +301,6 @@ static int ctail_read(user_data_t *ud) { return 0; } /* int ctail_read */ -static int ctail_init(void) { - char str[255]; - - if (tail_match_list_num == 0) { - WARNING("tail plugin: File list is empty. Returning an error."); - return -1; - } - - for (size_t i = 0; i < tail_match_list_num; i++) { - snprintf(str, sizeof(str), "tail-%zu", i); - - plugin_register_complex_read(NULL, str, ctail_read, - tail_match_list_intervals[i], - &(user_data_t){ - .data = tail_match_list[i], - }); - } - - return 0; -} /* int ctail_init */ - -static int ctail_shutdown(void) { - for (size_t i = 0; i < tail_match_list_num; i++) { - tail_match_destroy(tail_match_list[i]); - tail_match_list[i] = NULL; - } - sfree(tail_match_list); - tail_match_list_num = 0; - - return 0; -} /* int ctail_shutdown */ - void module_register(void) { plugin_register_complex_config("tail", ctail_config); - plugin_register_init("tail", ctail_init); - plugin_register_shutdown("tail", ctail_shutdown); } /* void module_register */ diff --git a/src/tail_csv.c b/src/tail_csv.c index 2e3ac5f0..4c589716 100644 --- a/src/tail_csv.c +++ b/src/tail_csv.c @@ -50,14 +50,13 @@ struct instance_definition_s { cu_tail_t *tail; metric_definition_t **metric_list; size_t metric_list_len; - cdtime_t interval; ssize_t time_from; struct instance_definition_s *next; }; typedef struct instance_definition_s instance_definition_t; /* Private */ -static metric_definition_t *metric_head = NULL; +static metric_definition_t *metric_head; static int tcsv_submit(instance_definition_t *id, metric_definition_t *md, value_t v, cdtime_t t) { @@ -77,7 +76,6 @@ static int tcsv_submit(instance_definition_t *id, metric_definition_t *md, sstrncpy(vl.type_instance, md->instance, sizeof(vl.type_instance)); vl.time = t; - vl.interval = id->interval; return plugin_dispatch_values(&vl); } @@ -120,17 +118,17 @@ static int tcsv_read_metric(instance_definition_t *id, metric_definition_t *md, return tcsv_submit(id, md, v, t); } -static _Bool tcsv_check_index(ssize_t index, size_t fields_num, - char const *name) { +static bool tcsv_check_index(ssize_t index, size_t fields_num, + char const *name) { if (index < 0) - return 1; + return true; else if (((size_t)index) < fields_num) - return 1; + return true; ERROR("tail_csv plugin: Metric \"%s\": Request for index %zd when " - "only %zu fields are available.", + "only %" PRIsz " fields are available.", name, index, fields_num); - return 0; + return false; } static int tcsv_read_buffer(instance_definition_t *id, char *buffer, @@ -145,7 +143,7 @@ static int tcsv_read_buffer(instance_definition_t *id, char *buffer, while (buffer_size > 0) { if ((buffer[buffer_size - 1] == '\n') || (buffer[buffer_size - 1] == '\r')) { - buffer[buffer_size - 1] = 0; + buffer[buffer_size - 1] = '\0'; buffer_size--; } else { break; @@ -418,6 +416,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) { int status = 0; /* Registration variables */ + cdtime_t interval = 0; char cb_name[DATA_MAX_NAME_LEN]; id = calloc(1, sizeof(*id)); @@ -436,9 +435,6 @@ static int tcsv_config_add_file(oconfig_item_t *ci) { return status; } - /* Use default interval. */ - id->interval = plugin_get_interval(); - for (int i = 0; i < ci->children_num; ++i) { oconfig_item_t *option = ci->children + i; status = 0; @@ -448,7 +444,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) { else if (strcasecmp("Collect", option->key) == 0) status = tcsv_config_add_instance_collect(id, option); else if (strcasecmp("Interval", option->key) == 0) - cf_util_get_cdtime(option, &id->interval); + cf_util_get_cdtime(option, &interval); else if (strcasecmp("TimeFrom", option->key) == 0) status = tcsv_config_get_index(option, &id->time_from); else if (strcasecmp("Plugin", option->key) == 0) @@ -484,7 +480,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) { snprintf(cb_name, sizeof(cb_name), "tail_csv/%s", id->path); status = plugin_register_complex_read( - NULL, cb_name, tcsv_read, id->interval, + NULL, cb_name, tcsv_read, interval, &(user_data_t){ .data = id, .free_func = tcsv_instance_definition_destroy, }); @@ -513,7 +509,7 @@ static int tcsv_config(oconfig_item_t *ci) { } /* int tcsv_config */ static int tcsv_init(void) { /* {{{ */ - static _Bool have_init = 0; + static bool have_init; metric_definition_t *md; if (have_init) @@ -532,7 +528,7 @@ static int tcsv_init(void) { /* {{{ */ md->type, md->name); continue; } else if (ds->ds_num != 1) { - ERROR("tail_csv plugin: The type \"%s\" has %zu data sources. " + ERROR("tail_csv plugin: The type \"%s\" has %" PRIsz " data sources. " "Only types with a single data source are supported.", ds->type, ds->ds_num); continue; diff --git a/src/tape.c b/src/tape.c index f59b7ea6..26bd969a 100644 --- a/src/tape.c +++ b/src/tape.c @@ -36,7 +36,7 @@ #define MAX_NUMTAPE 256 extern kstat_ctl_t *kc; static kstat_t *ksp[MAX_NUMTAPE]; -static int numtape = 0; +static int numtape; static int tape_init(void) { kstat_t *ksp_chain; diff --git a/src/target_replace.c b/src/target_replace.c index 66fc98d4..887507e1 100644 --- a/src/target_replace.c +++ b/src/target_replace.c @@ -37,7 +37,7 @@ typedef struct tr_action_s tr_action_t; struct tr_action_s { regex_t re; char *replacement; - _Bool may_be_empty; + bool may_be_empty; tr_action_t *next; }; @@ -110,7 +110,7 @@ static void tr_meta_data_action_destroy(tr_meta_data_action_t *act) /* {{{ */ } /* }}} void tr_meta_data_action_destroy */ static int tr_config_add_action(tr_action_t **dest, /* {{{ */ - const oconfig_item_t *ci, _Bool may_be_empty) { + const oconfig_item_t *ci, bool may_be_empty) { tr_action_t *act; int status; @@ -172,7 +172,7 @@ static int tr_config_add_action(tr_action_t **dest, /* {{{ */ static int tr_config_add_meta_action(tr_meta_data_action_t **dest, /* {{{ */ const oconfig_item_t *ci, - _Bool should_delete) { + bool should_delete) { tr_meta_data_action_t *act; int status; @@ -262,7 +262,7 @@ static int tr_config_add_meta_action(tr_meta_data_action_t **dest, /* {{{ */ static int tr_action_invoke(tr_action_t *act_head, /* {{{ */ char *buffer_in, size_t buffer_in_size, - _Bool may_be_empty) { + bool may_be_empty) { int status; char buffer[DATA_MAX_NAME_LEN]; regmatch_t matches[8] = {[0] = {0}}; @@ -294,7 +294,8 @@ static int tr_action_invoke(tr_action_t *act_head, /* {{{ */ subst_status = subst(temp, sizeof(temp), buffer, (size_t)matches[0].rm_so, (size_t)matches[0].rm_eo, act->replacement); if (subst_status == NULL) { - ERROR("Target `replace': subst (buffer = %s, start = %zu, end = %zu, " + ERROR("Target `replace': subst (buffer = %s, start = %" PRIsz + ", end = %" PRIsz ", " "replacement = %s) failed.", buffer, (size_t)matches[0].rm_so, (size_t)matches[0].rm_eo, act->replacement); @@ -305,7 +306,7 @@ static int tr_action_invoke(tr_action_t *act_head, /* {{{ */ DEBUG("target_replace plugin: tr_action_invoke: -- buffer = %s;", buffer); } /* for (act = act_head; act != NULL; act = act->next) */ - if ((may_be_empty == 0) && (buffer[0] == 0)) { + if ((may_be_empty == false) && (buffer[0] == 0)) { WARNING("Target `replace': Replacement resulted in an empty string, " "which is not allowed for this buffer (`host' or `plugin')."); return 0; @@ -386,7 +387,8 @@ static int tr_meta_data_action_invoke(/* {{{ */ subst_status = subst(temp, sizeof(temp), value, (size_t)matches[0].rm_so, (size_t)matches[0].rm_eo, act->replacement); if (subst_status == NULL) { - ERROR("Target `replace': subst (value = %s, start = %zu, end = %zu, " + ERROR("Target `replace': subst (value = %s, start = %" PRIsz + ", end = %" PRIsz ", " "replacement = %s) failed.", value, (size_t)matches[0].rm_so, (size_t)matches[0].rm_eo, act->replacement); @@ -468,13 +470,13 @@ static int tr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */ if ((strcasecmp("Host", child->key) == 0) || (strcasecmp("Hostname", child->key) == 0)) status = tr_config_add_action(&data->host, child, - /* may be empty = */ 0); + /* may be empty = */ false); else if (strcasecmp("Plugin", child->key) == 0) status = tr_config_add_action(&data->plugin, child, - /* may be empty = */ 0); + /* may be empty = */ false); else if (strcasecmp("PluginInstance", child->key) == 0) status = tr_config_add_action(&data->plugin_instance, child, - /* may be empty = */ 1); + /* may be empty = */ true); #if 0 else if (strcasecmp ("Type", child->key) == 0) status = tr_config_add_action (&data->type, child, @@ -482,13 +484,13 @@ static int tr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */ #endif else if (strcasecmp("TypeInstance", child->key) == 0) status = tr_config_add_action(&data->type_instance, child, - /* may be empty = */ 1); + /* may be empty = */ true); else if (strcasecmp("MetaData", child->key) == 0) status = tr_config_add_meta_action(&data->meta, child, - /* should delete = */ 0); + /* should delete = */ false); else if (strcasecmp("DeleteMetaData", child->key) == 0) status = tr_config_add_meta_action(&data->meta, child, - /* should delete = */ 1); + /* should delete = */ true); else { ERROR("Target `replace': The `%s' configuration option is not understood " "and will be ignored.", @@ -544,11 +546,11 @@ static int tr_invoke(const data_set_t *ds, value_list_t *vl, /* {{{ */ #define HANDLE_FIELD(f, e) \ if (data->f != NULL) \ tr_action_invoke(data->f, vl->f, sizeof(vl->f), e) - HANDLE_FIELD(host, 0); - HANDLE_FIELD(plugin, 0); - HANDLE_FIELD(plugin_instance, 1); - /* HANDLE_FIELD (type, 0); */ - HANDLE_FIELD(type_instance, 1); + HANDLE_FIELD(host, false); + HANDLE_FIELD(plugin, false); + HANDLE_FIELD(plugin_instance, true); + /* HANDLE_FIELD (type, false); */ + HANDLE_FIELD(type_instance, true); return FC_TARGET_CONTINUE; } /* }}} int tr_invoke */ diff --git a/src/target_v5upgrade.c b/src/target_v5upgrade.c index 49f09f08..650f9a5e 100644 --- a/src/target_v5upgrade.c +++ b/src/target_v5upgrade.c @@ -213,15 +213,15 @@ static int v5_mysql_threads(const data_set_t *ds, value_list_t *vl) /* {{{ */ static int v5_zfs_arc_counts(const data_set_t *ds, value_list_t *vl) /* {{{ */ { value_list_t new_vl; - _Bool is_hits; + bool is_hits; if (vl->values_len != 4) return FC_TARGET_STOP; if (strcmp("hits", vl->type_instance) == 0) - is_hits = 1; + is_hits = true; else if (strcmp("misses", vl->type_instance) == 0) - is_hits = 0; + is_hits = false; else return FC_TARGET_STOP; diff --git a/src/tcpconns.c b/src/tcpconns.c index 9fdd16e9..5a3fd4ef 100644 --- a/src/tcpconns.c +++ b/src/tcpconns.c @@ -76,7 +76,6 @@ #endif #if KERNEL_LINUX -#include #include #if HAVE_LINUX_INET_DIAG_H #include @@ -206,7 +205,7 @@ static const char *tcp_state[] = {"CLOSED", "LISTEN", "SYN_SENT", "FIN_WAIT2", "TIME_WAIT"}; static kvm_t *kvmd; -static u_long inpcbtable_off = 0; +static u_long inpcbtable_off; struct inpcbtable *inpcbtable_ptr = NULL; #define TCP_STATE_LISTEN 1 @@ -262,9 +261,9 @@ static const char *config_keys[] = {"ListeningPorts", "LocalPort", "RemotePort", "AllPortsSummary"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); -static int port_collect_listening = 0; -static int port_collect_total = 0; -static port_entry_t *port_list_head = NULL; +static int port_collect_listening; +static int port_collect_total; +static port_entry_t *port_list_head; static uint32_t count_total[TCP_STATE_MAX + 1]; #if KERNEL_LINUX @@ -272,7 +271,7 @@ static uint32_t count_total[TCP_STATE_MAX + 1]; /* This depends on linux inet_diag_req because if this structure is missing, * sequence_number is useless and we get a compilation warning. */ -static uint32_t sequence_number = 0; +static uint32_t sequence_number; #endif static enum { SRC_DUNNO, SRC_NETLINK, SRC_PROC } linux_source = SRC_DUNNO; @@ -459,7 +458,7 @@ static int conn_read_netlink(void) { if (fd < 0) { ERROR("tcpconns plugin: conn_read_netlink: socket(AF_NETLINK, SOCK_RAW, " "NETLINK_INET_DIAG) failed: %s", - sstrerror(errno, buf, sizeof(buf))); + STRERRNO); return -1; } @@ -490,7 +489,7 @@ static int conn_read_netlink(void) { if (sendmsg(fd, &msg, 0) < 0) { ERROR("tcpconns plugin: conn_read_netlink: sendmsg(2) failed: %s", - sstrerror(errno, buf, sizeof(buf))); + STRERRNO); close(fd); return -1; } @@ -499,7 +498,6 @@ static int conn_read_netlink(void) { iov.iov_len = sizeof(buf); while (1) { - int status; struct nlmsghdr *h; memset(&msg, 0, sizeof(msg)); @@ -508,13 +506,13 @@ static int conn_read_netlink(void) { msg.msg_iov = &iov; msg.msg_iovlen = 1; - status = recvmsg(fd, (void *)&msg, /* flags = */ 0); + ssize_t status = recvmsg(fd, (void *)&msg, /* flags = */ 0); if (status < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; ERROR("tcpconns plugin: conn_read_netlink: recvmsg(2) failed: %s", - sstrerror(errno, buf, sizeof(buf))); + STRERRNO); close(fd); return -1; } else if (status == 0) { @@ -575,11 +573,11 @@ static int conn_handle_line(char *buffer) { uint8_t state; - int buffer_len = strlen(buffer); + size_t buffer_len = strlen(buffer); while ((buffer_len > 0) && (buffer[buffer_len - 1] < 32)) buffer[--buffer_len] = '\0'; - if (buffer_len <= 0) + if (buffer_len == 0) return -1; fields_len = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields)); diff --git a/src/teamspeak2.c b/src/teamspeak2.c index 4d68f61f..567e5fb8 100644 --- a/src/teamspeak2.c +++ b/src/teamspeak2.c @@ -46,14 +46,14 @@ typedef struct vserver_list_s { int port; struct vserver_list_s *next; } vserver_list_t; -static vserver_list_t *server_list = NULL; +static vserver_list_t *server_list; /* Host data */ -static char *config_host = NULL; -static char *config_port = NULL; +static char *config_host; +static char *config_port; -static FILE *global_read_fh = NULL; -static FILE *global_write_fh = NULL; +static FILE *global_read_fh; +static FILE *global_write_fh; /* Config data */ static const char *config_keys[] = {"Host", "Port", "Server"}; @@ -204,18 +204,14 @@ static int tss2_get_socket(FILE **ret_read_fh, FILE **ret_write_fh) { /* Create socket */ sd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); if (sd < 0) { - char errbuf[1024]; - WARNING("teamspeak2 plugin: socket failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("teamspeak2 plugin: socket failed: %s", STRERRNO); continue; } /* Try to connect */ status = connect(sd, ai_ptr->ai_addr, ai_ptr->ai_addrlen); if (status != 0) { - char errbuf[1024]; - WARNING("teamspeak2 plugin: connect failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("teamspeak2 plugin: connect failed: %s", STRERRNO); close(sd); sd = -1; continue; @@ -236,18 +232,14 @@ static int tss2_get_socket(FILE **ret_read_fh, FILE **ret_write_fh) { /* Create file objects from sockets */ global_read_fh = fdopen(sd, "r"); if (global_read_fh == NULL) { - char errbuf[1024]; - ERROR("teamspeak2 plugin: fdopen failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("teamspeak2 plugin: fdopen failed: %s", STRERRNO); close(sd); return -1; } global_write_fh = fdopen(sd, "w"); if (global_write_fh == NULL) { - char errbuf[1024]; - ERROR("teamspeak2 plugin: fdopen failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("teamspeak2 plugin: fdopen failed: %s", STRERRNO); tss2_close_socket(); return -1; } @@ -263,7 +255,7 @@ static int tss2_get_socket(FILE **ret_read_fh, FILE **ret_write_fh) { config_host ? config_host : DEFAULT_HOST, config_port ? config_port : DEFAULT_PORT); } - buffer[sizeof(buffer) - 1] = 0; + buffer[sizeof(buffer) - 1] = '\0'; if (memcmp("[TS]\r\n", buffer, 6) != 0) { ERROR("teamspeak2 plugin: Unexpected response when connecting " @@ -312,14 +304,12 @@ static int tss2_receive_line(FILE *fh, char *buffer, int buffer_size) { */ temp = fgets(buffer, buffer_size, fh); if (temp == NULL) { - char errbuf[1024]; - ERROR("teamspeak2 plugin: fgets failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("teamspeak2 plugin: fgets failed: %s", STRERRNO); tss2_close_socket(); return -1; } - buffer[buffer_size - 1] = 0; + buffer[buffer_size - 1] = '\0'; return 0; } /* int tss2_receive_line */ @@ -347,7 +337,7 @@ static int tss2_select_vserver(FILE *read_fh, FILE *write_fh, ERROR("teamspeak2 plugin: tss2_receive_line failed."); return -1; } - response[sizeof(response) - 1] = 0; + response[sizeof(response) - 1] = '\0'; /* Check answer */ if ((strncasecmp("OK", response, 2) == 0) && @@ -389,7 +379,7 @@ static int tss2_vserver_gapl(FILE *read_fh, FILE *write_fh, ERROR("teamspeak2 plugin: tss2_receive_line failed."); return -1; } - buffer[sizeof(buffer) - 1] = 0; + buffer[sizeof(buffer) - 1] = '\0'; if (strncmp("average_packet_loss=", buffer, strlen("average_packet_loss=")) == 0) { diff --git a/src/ted.c b/src/ted.c index 3b64b75f..b5fa4c1e 100644 --- a/src/ted.c +++ b/src/ted.c @@ -53,8 +53,8 @@ #define DEFAULT_DEVICE "/dev/ttyUSB0" -static char *conf_device = NULL; -static int conf_retries = 0; +static char *conf_device; +static int conf_retries; static int fd = -1; @@ -109,19 +109,15 @@ static int ted_read_value(double *ret_power, double *ret_voltage) { /* Some signal or something. Start over.. */ continue; } else if (status < 0) { - char errbuf[1024]; - ERROR("ted plugin: select failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("ted plugin: select failed: %s", STRERRNO); return -1; } receive_buffer_length = read(fd, receive_buffer, sizeof(receive_buffer)); if (receive_buffer_length < 0) { - char errbuf[1024]; if ((errno == EAGAIN) || (errno == EINTR)) continue; - ERROR("ted plugin: read(2) failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("ted plugin: read(2) failed: %s", STRERRNO); return -1; } else if (receive_buffer_length == 0) { /* Should we close the FD in this case? */ diff --git a/src/testing.h b/src/testing.h index d3da9db4..fd7e6c66 100644 --- a/src/testing.h +++ b/src/testing.h @@ -29,8 +29,8 @@ #include -static int fail_count__ = 0; -static int check_count__ = 0; +static int fail_count__; +static int check_count__; #ifndef DBL_PRECISION #define DBL_PRECISION 1e-12 @@ -56,7 +56,7 @@ static int check_count__ = 0; #define OK1(cond, text) \ do { \ - _Bool result = (cond); \ + bool result = (cond); \ LOG(result, text); \ if (!result) { \ return -1; \ @@ -100,6 +100,18 @@ static int check_count__ = 0; printf("ok %i - %s = %" PRIu64 "\n", ++check_count__, #actual, got__); \ } while (0) +#define EXPECT_EQ_PTR(expect, actual) \ + do { \ + void *want__ = expect; \ + void *got__ = actual; \ + if (got__ != want__) { \ + printf("not ok %i - %s = %p, want %p\n", ++check_count__, #actual, \ + got__, want__); \ + return -1; \ + } \ + printf("ok %i - %s = %p\n", ++check_count__, #actual, got__); \ + } while (0) + #define EXPECT_EQ_DOUBLE(expect, actual) \ do { \ double want__ = (double)expect; \ diff --git a/src/thermal.c b/src/thermal.c index 9da8fa5f..959fec64 100644 --- a/src/thermal.c +++ b/src/thermal.c @@ -35,7 +35,7 @@ static const char *config_keys[] = {"Device", "IgnoreSelected", static const char *const dirname_sysfs = "/sys/class/thermal"; static const char *const dirname_procfs = "/proc/acpi/thermal_zone"; -static _Bool force_procfs = 0; +static bool force_procfs; static ignorelist_t *device_list; enum dev_type { TEMP = 0, COOLING_DEV }; @@ -59,7 +59,7 @@ static int thermal_sysfs_device_read(const char __attribute__((unused)) * dir, const char *name, void __attribute__((unused)) * user_data) { char filename[PATH_MAX]; - _Bool success = 0; + bool success = false; value_t value; if (device_list && ignorelist_match(device_list, name)) @@ -69,13 +69,13 @@ static int thermal_sysfs_device_read(const char __attribute__((unused)) * dir, if (parse_value_file(filename, &value, DS_TYPE_GAUGE) == 0) { value.gauge /= 1000.0; thermal_submit(name, TEMP, value); - success = 1; + success = true; } snprintf(filename, sizeof(filename), "%s/%s/cur_state", dirname_sysfs, name); if (parse_value_file(filename, &value, DS_TYPE_GAUGE) == 0) { thermal_submit(name, COOLING_DEV, value); - success = 1; + success = true; } return success ? 0 : -1; @@ -157,9 +157,9 @@ static int thermal_config(const char *key, const char *value) { if (IS_TRUE(value)) ignorelist_set_invert(device_list, 0); } else if (strcasecmp(key, "ForceUseProcfs") == 0) { - force_procfs = 0; + force_procfs = false; if (IS_TRUE(value)) - force_procfs = 1; + force_procfs = true; } else { return -1; } diff --git a/src/threshold.c b/src/threshold.c index 79001334..9d343630 100644 --- a/src/threshold.c +++ b/src/threshold.c @@ -109,90 +109,6 @@ static int ut_threshold_add(const threshold_t *th) { /* {{{ */ * The following approximately two hundred functions are used to handle the * configuration and fill the threshold list. * {{{ */ -static int ut_config_type_datasource(threshold_t *th, oconfig_item_t *ci) { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - WARNING("threshold values: The `DataSource' option needs exactly one " - "string argument."); - return -1; - } - - sstrncpy(th->data_source, ci->values[0].value.string, - sizeof(th->data_source)); - - return 0; -} /* int ut_config_type_datasource */ - -static int ut_config_type_instance(threshold_t *th, oconfig_item_t *ci) { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - WARNING("threshold values: The `Instance' option needs exactly one " - "string argument."); - return -1; - } - - sstrncpy(th->type_instance, ci->values[0].value.string, - sizeof(th->type_instance)); - - return 0; -} /* int ut_config_type_instance */ - -static int ut_config_type_max(threshold_t *th, oconfig_item_t *ci) { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) { - WARNING("threshold values: The `%s' option needs exactly one " - "number argument.", - ci->key); - return -1; - } - - if (strcasecmp(ci->key, "WarningMax") == 0) - th->warning_max = ci->values[0].value.number; - else - th->failure_max = ci->values[0].value.number; - - return 0; -} /* int ut_config_type_max */ - -static int ut_config_type_min(threshold_t *th, oconfig_item_t *ci) { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) { - WARNING("threshold values: The `%s' option needs exactly one " - "number argument.", - ci->key); - return -1; - } - - if (strcasecmp(ci->key, "WarningMin") == 0) - th->warning_min = ci->values[0].value.number; - else - th->failure_min = ci->values[0].value.number; - - return 0; -} /* int ut_config_type_min */ - -static int ut_config_type_hits(threshold_t *th, oconfig_item_t *ci) { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) { - WARNING("threshold values: The `%s' option needs exactly one " - "number argument.", - ci->key); - return -1; - } - - th->hits = ci->values[0].value.number; - - return 0; -} /* int ut_config_type_hits */ - -static int ut_config_type_hysteresis(threshold_t *th, oconfig_item_t *ci) { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) { - WARNING("threshold values: The `%s' option needs exactly one " - "number argument.", - ci->key); - return -1; - } - - th->hysteresis = ci->values[0].value.number; - - return 0; -} /* int ut_config_type_hysteresis */ - static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) { threshold_t th; int status = 0; @@ -223,15 +139,19 @@ static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) { oconfig_item_t *option = ci->children + i; if (strcasecmp("Instance", option->key) == 0) - status = ut_config_type_instance(&th, option); + status = cf_util_get_string_buffer(option, th.type_instance, + sizeof(th.type_instance)); else if (strcasecmp("DataSource", option->key) == 0) - status = ut_config_type_datasource(&th, option); - else if ((strcasecmp("WarningMax", option->key) == 0) || - (strcasecmp("FailureMax", option->key) == 0)) - status = ut_config_type_max(&th, option); - else if ((strcasecmp("WarningMin", option->key) == 0) || - (strcasecmp("FailureMin", option->key) == 0)) - status = ut_config_type_min(&th, option); + status = cf_util_get_string_buffer(option, th.data_source, + sizeof(th.data_source)); + else if (strcasecmp("WarningMax", option->key) == 0) + status = cf_util_get_double(option, &th.warning_max); + else if (strcasecmp("FailureMax", option->key) == 0) + status = cf_util_get_double(option, &th.failure_max); + else if (strcasecmp("WarningMin", option->key) == 0) + status = cf_util_get_double(option, &th.warning_min); + else if (strcasecmp("FailureMin", option->key) == 0) + status = cf_util_get_double(option, &th.failure_min); else if (strcasecmp("Interesting", option->key) == 0) status = cf_util_get_flag(option, &th.flags, UT_FLAG_INTERESTING); else if (strcasecmp("Invert", option->key) == 0) @@ -243,9 +163,9 @@ static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) { else if (strcasecmp("Percentage", option->key) == 0) status = cf_util_get_flag(option, &th.flags, UT_FLAG_PERCENTAGE); else if (strcasecmp("Hits", option->key) == 0) - status = ut_config_type_hits(&th, option); + status = cf_util_get_int(option, &th.hits); else if (strcasecmp("Hysteresis", option->key) == 0) - status = ut_config_type_hysteresis(&th, option); + status = cf_util_get_double(option, &th.hysteresis); else { WARNING("threshold values: Option `%s' not allowed inside a `Type' " "block.", @@ -264,19 +184,6 @@ static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) { return status; } /* int ut_config_type */ -static int ut_config_plugin_instance(threshold_t *th, oconfig_item_t *ci) { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - WARNING("threshold values: The `Instance' option needs exactly one " - "string argument."); - return -1; - } - - sstrncpy(th->plugin_instance, ci->values[0].value.string, - sizeof(th->plugin_instance)); - - return 0; -} /* int ut_config_plugin_instance */ - static int ut_config_plugin(const threshold_t *th_orig, oconfig_item_t *ci) { threshold_t th; int status = 0; @@ -302,7 +209,8 @@ static int ut_config_plugin(const threshold_t *th_orig, oconfig_item_t *ci) { if (strcasecmp("Type", option->key) == 0) status = ut_config_type(&th, option); else if (strcasecmp("Instance", option->key) == 0) - status = ut_config_plugin_instance(&th, option); + status = cf_util_get_string_buffer(option, th.plugin_instance, + sizeof(th.plugin_instance)); else { WARNING("threshold values: Option `%s' not allowed inside a `Plugin' " "block.", @@ -401,7 +309,10 @@ static int ut_report_state(const data_set_t *ds, const value_list_t *vl, /* 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; @@ -459,6 +370,10 @@ static int ut_report_state(const data_set_t *ds, const value_list_t *vl, 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; @@ -547,7 +462,7 @@ static int ut_check_one_data_source( 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) { @@ -576,6 +491,7 @@ static int ut_check_one_data_source( 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; @@ -617,7 +533,8 @@ static int ut_check_one_data_source( * * 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, diff --git a/src/tokyotyrant.c b/src/tokyotyrant.c index 1534f51e..aca0a4e3 100644 --- a/src/tokyotyrant.c +++ b/src/tokyotyrant.c @@ -33,10 +33,10 @@ static const char *config_keys[] = {"Host", "Port"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); -static char *config_host = NULL; -static char *config_port = NULL; +static char *config_host; +static char *config_port; -static TCRDB *rdb = NULL; +static TCRDB *rdb; static int tt_config(const char *key, const char *value) { if (strcasecmp("Host", key) == 0) { diff --git a/src/turbostat.c b/src/turbostat.c index f3885158..4a7af4c7 100644 --- a/src/turbostat.c +++ b/src/turbostat.c @@ -49,6 +49,14 @@ #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. @@ -65,12 +73,12 @@ * * This value is automatically set if mperf or aperf go backward */ -static _Bool aperf_mperf_unstable; +static bool aperf_mperf_unstable; /* * If set, use kernel logical core numbering for all "per core" metrics. */ -static _Bool config_lcn; +static bool config_lcn; /* * Bitmask of the list of core C states supported by the processor. @@ -78,7 +86,7 @@ static _Bool config_lcn; */ static unsigned int do_core_cstate; static unsigned int config_core_cstate; -static _Bool apply_config_core_cstate; +static bool apply_config_core_cstate; /* * Bitmask of the list of pacages C states supported by the processor. @@ -86,15 +94,15 @@ static _Bool apply_config_core_cstate; */ static unsigned int do_pkg_cstate; static unsigned int config_pkg_cstate; -static _Bool apply_config_pkg_cstate; +static bool apply_config_pkg_cstate; /* * Boolean indicating if the processor supports 'I/O System-Management Interrupt * counter' */ -static _Bool do_smi; -static _Bool config_smi; -static _Bool apply_config_smi; +static bool do_smi; +static bool config_smi; +static bool apply_config_smi; /* * Boolean indicating if the processor supports 'Digital temperature sensor' @@ -105,9 +113,9 @@ static _Bool apply_config_smi; * might be wrong * - Temperatures above the tcc_activation_temp are not recorded */ -static _Bool do_dts; -static _Bool config_dts; -static _Bool apply_config_dts; +static bool do_dts; +static bool config_dts; +static bool apply_config_dts; /* * Boolean indicating if the processor supports 'Package thermal management' @@ -118,9 +126,9 @@ static _Bool apply_config_dts; * might be wrong * - Temperatures above the tcc_activation_temp are not recorded */ -static _Bool do_ptm; -static _Bool config_ptm; -static _Bool apply_config_ptm; +static bool do_ptm; +static bool config_ptm; +static bool apply_config_ptm; /* * Thermal Control Circuit Activation Temperature as configured by the user. @@ -129,14 +137,21 @@ static _Bool apply_config_ptm; */ 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 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 */ @@ -188,6 +203,10 @@ static struct pkg_data { 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; @@ -195,10 +214,10 @@ static struct pkg_data { #define DELTA_COUNTERS thread_delta, core_delta, package_delta #define ODD_COUNTERS thread_odd, core_odd, package_odd #define EVEN_COUNTERS thread_even, core_even, package_even -static _Bool is_even = 1; +static bool is_even = true; -static _Bool allocated = 0; -static _Bool initialized = 0; +static bool allocated; +static bool initialized; #define GET_THREAD(thread_base, thread_no, core_no, pkg_no) \ (thread_base + (pkg_no)*topology.num_cores * topology.num_threads + \ @@ -210,8 +229,8 @@ static _Bool initialized = 0; struct cpu_topology { unsigned int package_id; unsigned int core_id; - _Bool first_core_in_package; - _Bool first_thread_in_core; + bool first_core_in_package; + bool first_thread_in_core; }; static struct topology { @@ -233,6 +252,7 @@ static const char *config_keys[] = { "TCCActivationTemp", "RunningAveragePowerLimit", "LogicalCoreNames", + "RestoreAffinityPolicy", }; static const int config_keys_num = STATIC_ARRAY_SIZE(config_keys); @@ -243,10 +263,10 @@ static const int config_keys_num = STATIC_ARRAY_SIZE(config_keys); /* * Open a MSR device for reading * Can change the scheduling affinity of the current process if multiple_read is - * 1 + * true */ static int __attribute__((warn_unused_result)) -open_msr(unsigned int cpu, _Bool multiple_read) { +open_msr(unsigned int cpu, bool multiple_read) { char pathname[32]; int fd; @@ -395,6 +415,8 @@ get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) { 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); @@ -412,6 +434,18 @@ get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) { 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); @@ -442,6 +476,11 @@ static inline void delta_package(struct pkg_data *delta, 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; } /* @@ -487,7 +526,7 @@ delta_thread(struct thread_data *delta, const struct thread_data *new, "the entire interval. Fix this by running " "Linux-2.6.30 or later."); - aperf_mperf_unstable = 1; + aperf_mperf_unstable = true; } } @@ -627,9 +666,11 @@ static int submit_counters(struct thread_data *t, struct core_data *c, 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); @@ -640,6 +681,18 @@ static int submit_counters(struct thread_data *t, struct core_data *c, 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; } @@ -895,14 +948,14 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) { switch (model) { /* Atom (partial) */ case 0x27: - do_smi = 0; + do_smi = false; do_core_cstate = 0; do_pkg_cstate = (1 << 2) | (1 << 4) | (1 << 6); break; /* Silvermont */ case 0x37: /* BYT */ case 0x4D: /* AVN */ - do_smi = 1; + do_smi = true; do_core_cstate = (1 << 1) | (1 << 6); do_pkg_cstate = (1 << 6); break; @@ -912,7 +965,7 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) { Forest */ case 0x1F: /* Core i7 and i5 Processor - Nehalem */ case 0x2E: /* Nehalem-EX Xeon - Beckton */ - do_smi = 1; + do_smi = true; do_core_cstate = (1 << 3) | (1 << 6); do_pkg_cstate = (1 << 3) | (1 << 6) | (1 << 7); break; @@ -920,21 +973,21 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) { case 0x25: /* Westmere Client - Clarkdale, Arrandale */ case 0x2C: /* Westmere EP - Gulftown */ case 0x2F: /* Westmere-EX Xeon - Eagleton */ - do_smi = 1; + do_smi = true; do_core_cstate = (1 << 3) | (1 << 6); do_pkg_cstate = (1 << 3) | (1 << 6) | (1 << 7); break; /* Sandy Bridge */ case 0x2A: /* SNB */ case 0x2D: /* SNB Xeon */ - do_smi = 1; + do_smi = true; do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7); break; /* Ivy Bridge */ case 0x3A: /* IVB */ case 0x3E: /* IVB Xeon */ - do_smi = 1; + do_smi = true; do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7); break; @@ -942,31 +995,31 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) { case 0x3C: /* HSW */ case 0x3F: /* HSW */ case 0x46: /* HSW */ - do_smi = 1; + do_smi = true; do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7); break; case 0x45: /* HSW */ - do_smi = 1; + do_smi = true; do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10); break; - /* Broadwel */ + /* Broadwell */ case 0x4F: /* BDW */ case 0x56: /* BDX-DE */ - do_smi = 1; + do_smi = true; do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7); break; case 0x3D: /* BDW */ - do_smi = 1; + do_smi = true; do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10); break; default: - do_smi = 0; + do_smi = false; do_core_cstate = 0; do_pkg_cstate = 0; break; @@ -978,16 +1031,19 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) { case 0x45: /* HSW */ case 0x46: /* HSW */ case 0x3D: /* BDW */ + case 0x5E: /* SKL */ do_rapl = RAPL_PKG | RAPL_CORES | RAPL_GFX; break; case 0x3F: /* HSX */ 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 */ @@ -1022,6 +1078,7 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) { 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 @@ -1226,7 +1283,7 @@ static int __attribute__((warn_unused_result)) topology_probe(void) { if (ret < 0) goto err; else if ((unsigned int)ret == i) - cpu->first_core_in_package = 1; + cpu->first_core_in_package = true; ret = get_threads_on_core(i); if (ret < 0) @@ -1240,7 +1297,7 @@ static int __attribute__((warn_unused_result)) topology_probe(void) { if (ret < 0) goto err; else if ((unsigned int)ret == i) - cpu->first_thread_in_core = 1; + cpu->first_thread_in_core = true; DEBUG("turbostat plugin: cpu %d pkg %d core %d\n", i, cpu->package_id, cpu->core_id); @@ -1337,8 +1394,8 @@ static void initialize_counters(void) { } static void free_all_buffers(void) { - allocated = 0; - initialized = 0; + allocated = false; + initialized = false; CPU_FREE(cpu_present_set); cpu_present_set = NULL; @@ -1399,13 +1456,37 @@ static int setup_all_buffers(void) { DO_OR_GOTO_ERR(for_all_cpus(set_temperature_target, EVEN_COUNTERS)); DO_OR_GOTO_ERR(for_all_cpus(set_temperature_target, ODD_COUNTERS)); - allocated = 1; + allocated = true; return 0; err: free_all_buffers(); 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; @@ -1425,10 +1506,9 @@ static int turbostat_read(void) { } } - /* Saving the scheduling affinity, as it will be modified by get_counters */ - if (sched_getaffinity(0, cpu_saved_affinity_setsize, - cpu_saved_affinity_set) != 0) { - ERROR("turbostat plugin: Unable to save the CPU affinity"); + if (save_affinity() != 0) { + ERROR("turbostat plugin: Unable to save the CPU affinity. Please read the " + "docs about RestoreAffinityPolicy option."); return -1; } @@ -1436,8 +1516,8 @@ static int turbostat_read(void) { if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0) goto out; time_even = cdtime(); - is_even = 1; - initialized = 1; + is_even = true; + initialized = true; ret = 0; goto out; } @@ -1446,7 +1526,7 @@ static int turbostat_read(void) { if ((ret = for_all_cpus(get_counters, ODD_COUNTERS)) < 0) goto out; time_odd = cdtime(); - is_even = 0; + is_even = false; time_delta = time_odd - time_even; if ((ret = for_all_cpus_delta(ODD_COUNTERS, EVEN_COUNTERS)) < 0) goto out; @@ -1456,7 +1536,7 @@ static int turbostat_read(void) { if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0) goto out; time_even = cdtime(); - is_even = 1; + is_even = true; time_delta = time_even - time_odd; if ((ret = for_all_cpus_delta(EVEN_COUNTERS, ODD_COUNTERS)) < 0) goto out; @@ -1465,13 +1545,8 @@ static int turbostat_read(void) { } 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; } @@ -1553,7 +1628,7 @@ static int turbostat_config(const char *key, const char *value) { return -1; } config_core_cstate = (unsigned int)tmp_val; - apply_config_core_cstate = 1; + apply_config_core_cstate = true; } else if (strcasecmp("PackageCstates", key) == 0) { tmp_val = strtoul(value, &end, 0); if (*end != '\0' || tmp_val > UINT_MAX) { @@ -1561,16 +1636,16 @@ static int turbostat_config(const char *key, const char *value) { return -1; } config_pkg_cstate = (unsigned int)tmp_val; - apply_config_pkg_cstate = 1; + apply_config_pkg_cstate = true; } else if (strcasecmp("SystemManagementInterrupt", key) == 0) { config_smi = IS_TRUE(value); - apply_config_smi = 1; + apply_config_smi = true; } else if (strcasecmp("DigitalTemperatureSensor", key) == 0) { config_dts = IS_TRUE(value); - apply_config_dts = 1; + apply_config_dts = true; } else if (strcasecmp("PackageThermalManagement", key) == 0) { config_ptm = IS_TRUE(value); - apply_config_ptm = 1; + apply_config_ptm = true; } else if (strcasecmp("LogicalCoreNames", key) == 0) { config_lcn = IS_TRUE(value); } else if (strcasecmp("RunningAveragePowerLimit", key) == 0) { @@ -1580,7 +1655,7 @@ static int turbostat_config(const char *key, const char *value) { return -1; } config_rapl = (unsigned int)tmp_val; - apply_config_rapl = 1; + apply_config_rapl = true; } else if (strcasecmp("TCCActivationTemp", key) == 0) { tmp_val = strtoul(value, &end, 0); if (*end != '\0' || tmp_val > UINT_MAX) { @@ -1588,6 +1663,15 @@ static int turbostat_config(const char *key, const char *value) { 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; diff --git a/src/types.db b/src/types.db index 4ca57aa7..e9de64fc 100644 --- a/src/types.db +++ b/src/types.db @@ -31,6 +31,7 @@ clock_state value:GAUGE:0:U clock_stratum value:GAUGE:0:U compression uncompressed:DERIVE:0:U, compressed:DERIVE:0:U compression_ratio value:GAUGE:0:2 +commands value:DERIVE:0:U connections value:DERIVE:0:U conntrack value:GAUGE:0:4294967295 contextswitch value:DERIVE:0:U @@ -44,6 +45,7 @@ current value:GAUGE:U:U current_connections value:GAUGE:0:U current_sessions value:GAUGE:0:U delay value:GAUGE:-1000000:1000000 +delay_rate value:GAUGE:0:U derive value:DERIVE:0:U df used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623 df_complex value:GAUGE:0:U @@ -110,6 +112,7 @@ if_octets rx:DERIVE:0:U, tx:DERIVE:0:U if_packets rx:DERIVE:0:U, tx:DERIVE:0:U if_rx_dropped value:DERIVE:0:U if_rx_errors value:DERIVE:0:U +if_rx_nohandler value:DERIVE:0:U if_rx_octets value:DERIVE:0:U if_rx_packets value:DERIVE:0:U if_tx_dropped value:DERIVE:0:U @@ -206,9 +209,11 @@ ps_rss value:GAUGE:0:9223372036854775807 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 +redis_command_cputime value:DERIVE:0:U requests value:GAUGE:0:U response_code value:GAUGE:0:U response_time value:GAUGE:0:U @@ -235,6 +240,7 @@ spl value:GAUGE:U: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 @@ -253,7 +259,10 @@ total_sessions value:DERIVE:0:U 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 diff --git a/src/unixsock.c b/src/unixsock.c index 99e39eee..8c08e18a 100644 --- a/src/unixsock.c +++ b/src/unixsock.c @@ -55,14 +55,14 @@ static const char *config_keys[] = {"SocketFile", "SocketGroup", "SocketPerms", "DeleteSocket"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); -static int loop = 0; +static int loop; /* socket configuration */ static int sock_fd = -1; -static char *sock_file = NULL; -static char *sock_group = NULL; +static char *sock_file; +static char *sock_group; static int sock_perms = S_IRWXU | S_IRWXG; -static _Bool delete_socket = 0; +static bool delete_socket; static pthread_t listen_thread = (pthread_t)0; @@ -75,9 +75,7 @@ static int us_open_socket(void) { sock_fd = socket(PF_UNIX, SOCK_STREAM, 0); if (sock_fd < 0) { - char errbuf[1024]; - ERROR("unixsock plugin: socket failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("unixsock plugin: socket failed: %s", STRERRNO); return -1; } @@ -91,9 +89,8 @@ static int us_open_socket(void) { errno = 0; status = unlink(sa.sun_path); if ((status != 0) && (errno != ENOENT)) { - char errbuf[1024]; WARNING("unixsock plugin: Deleting socket file \"%s\" failed: %s", - sa.sun_path, sstrerror(errno, errbuf, sizeof(errbuf))); + sa.sun_path, STRERRNO); } else if (status == 0) { INFO("unixsock plugin: Successfully deleted socket file \"%s\".", sa.sun_path); @@ -102,9 +99,7 @@ static int us_open_socket(void) { status = bind(sock_fd, (struct sockaddr *)&sa, sizeof(sa)); if (status != 0) { - char errbuf[1024]; - sstrerror(errno, errbuf, sizeof(errbuf)); - ERROR("unixsock plugin: bind failed: %s", errbuf); + ERROR("unixsock plugin: bind failed: %s", STRERRNO); close(sock_fd); sock_fd = -1; return -1; @@ -112,9 +107,7 @@ static int us_open_socket(void) { status = chmod(sa.sun_path, sock_perms); if (status == -1) { - char errbuf[1024]; - ERROR("unixsock plugin: chmod failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("unixsock plugin: chmod failed: %s", STRERRNO); close(sock_fd); sock_fd = -1; return -1; @@ -122,9 +115,7 @@ static int us_open_socket(void) { status = listen(sock_fd, 8); if (status != 0) { - char errbuf[1024]; - ERROR("unixsock plugin: listen failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("unixsock plugin: listen failed: %s", STRERRNO); close(sock_fd); sock_fd = -1; return -1; @@ -147,9 +138,8 @@ static int us_open_socket(void) { status = getgrnam_r(grpname, &sg, grbuf, sizeof(grbuf), &g); if (status != 0) { - char errbuf[1024]; WARNING("unixsock plugin: getgrnam_r (%s) failed: %s", grpname, - sstrerror(status, errbuf, sizeof(errbuf))); + STRERROR(status)); break; } if (g == NULL) { @@ -159,10 +149,9 @@ static int us_open_socket(void) { if (chown((sock_file != NULL) ? sock_file : US_DEFAULT_PATH, (uid_t)-1, g->gr_gid) != 0) { - char errbuf[1024]; WARNING("unixsock plugin: chown (%s, -1, %i) failed: %s", (sock_file != NULL) ? sock_file : US_DEFAULT_PATH, (int)g->gr_gid, - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); } } while (0); @@ -182,18 +171,14 @@ static void *us_handle_client(void *arg) { fdout = dup(fdin); if (fdout < 0) { - char errbuf[1024]; - ERROR("unixsock plugin: dup failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("unixsock plugin: dup failed: %s", STRERRNO); close(fdin); pthread_exit((void *)1); } fhin = fdopen(fdin, "r"); if (fhin == NULL) { - char errbuf[1024]; - ERROR("unixsock plugin: fdopen failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("unixsock plugin: fdopen failed: %s", STRERRNO); close(fdin); close(fdout); pthread_exit((void *)1); @@ -202,9 +187,7 @@ static void *us_handle_client(void *arg) { fhout = fdopen(fdout, "w"); if (fhout == NULL) { - char errbuf[1024]; - ERROR("unixsock plugin: fdopen failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("unixsock plugin: fdopen failed: %s", STRERRNO); fclose(fhin); /* this closes fdin as well */ close(fdout); pthread_exit((void *)1); @@ -213,9 +196,7 @@ static void *us_handle_client(void *arg) { /* change output buffer to line buffered mode */ if (setvbuf(fhout, NULL, _IOLBF, 0) != 0) { - char errbuf[1024]; - ERROR("unixsock plugin: setvbuf failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("unixsock plugin: setvbuf failed: %s", STRERRNO); fclose(fhin); fclose(fhout); pthread_exit((void *)1); @@ -227,7 +208,6 @@ static void *us_handle_client(void *arg) { char buffer_copy[1024]; char *fields[128]; int fields_num; - int len; errno = 0; if (fgets(buffer, sizeof(buffer), fhin) == NULL) { @@ -235,14 +215,13 @@ static void *us_handle_client(void *arg) { continue; if (errno != 0) { - char errbuf[1024]; WARNING("unixsock plugin: failed to read from socket #%i: %s", - fileno(fhin), sstrerror(errno, errbuf, sizeof(errbuf))); + fileno(fhin), STRERRNO); } break; } - len = strlen(buffer); + size_t len = strlen(buffer); while ((len > 0) && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r'))) buffer[--len] = '\0'; @@ -276,9 +255,8 @@ static void *us_handle_client(void *arg) { cmd_handle_flush(fhout, buffer); } else { if (fprintf(fhout, "-1 Unknown command: %s\n", fields[0]) < 0) { - char errbuf[1024]; WARNING("unixsock plugin: failed to write to socket #%i: %s", - fileno(fhout), sstrerror(errno, errbuf, sizeof(errbuf))); + fileno(fhout), STRERRNO); break; } } @@ -308,13 +286,11 @@ static void *us_server_thread(void __attribute__((unused)) * arg) { DEBUG("unixsock plugin: Calling accept.."); status = accept(sock_fd, NULL, NULL); if (status < 0) { - char errbuf[1024]; if (errno == EINTR) continue; - ERROR("unixsock plugin: accept failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("unixsock plugin: accept failed: %s", STRERRNO); close(sock_fd); sock_fd = -1; pthread_attr_destroy(&th_attr); @@ -323,9 +299,7 @@ static void *us_server_thread(void __attribute__((unused)) * arg) { remote_fd = malloc(sizeof(*remote_fd)); if (remote_fd == NULL) { - char errbuf[1024]; - WARNING("unixsock plugin: malloc failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("unixsock plugin: malloc failed: %s", STRERRNO); close(status); continue; } @@ -336,9 +310,7 @@ static void *us_server_thread(void __attribute__((unused)) * arg) { status = plugin_thread_create(&th, &th_attr, us_handle_client, (void *)remote_fd, "unixsock conn"); if (status != 0) { - char errbuf[1024]; - WARNING("unixsock plugin: pthread_create failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("unixsock plugin: pthread_create failed: %s", STRERRNO); close(*remote_fd); free(remote_fd); continue; @@ -351,10 +323,8 @@ static void *us_server_thread(void __attribute__((unused)) * arg) { status = unlink((sock_file != NULL) ? sock_file : US_DEFAULT_PATH); if (status != 0) { - char errbuf[1024]; NOTICE("unixsock plugin: unlink (%s) failed: %s", - (sock_file != NULL) ? sock_file : US_DEFAULT_PATH, - sstrerror(errno, errbuf, sizeof(errbuf))); + (sock_file != NULL) ? sock_file : US_DEFAULT_PATH, STRERRNO); } return (void *)0; @@ -379,9 +349,9 @@ static int us_config(const char *key, const char *val) { sock_perms = (int)strtol(val, NULL, 8); } else if (strcasecmp(key, "DeleteSocket") == 0) { if (IS_TRUE(val)) - delete_socket = 1; + delete_socket = true; else - delete_socket = 0; + delete_socket = false; } else { return -1; } @@ -390,7 +360,7 @@ static int us_config(const char *key, const char *val) { } /* int us_config */ static int us_init(void) { - static int have_init = 0; + static int have_init; int status; @@ -404,9 +374,7 @@ static int us_init(void) { status = plugin_thread_create(&listen_thread, NULL, us_server_thread, NULL, "unixsock listen"); if (status != 0) { - char errbuf[1024]; - ERROR("unixsock plugin: pthread_create failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("unixsock plugin: pthread_create failed: %s", STRERRNO); return -1; } diff --git a/src/uptime.c b/src/uptime.c index 76eb0d50..43d72e53 100644 --- a/src/uptime.c +++ b/src/uptime.c @@ -52,6 +52,7 @@ /* * Global variables */ + #if HAVE_KSTAT_H #include #endif @@ -90,9 +91,7 @@ static time_t uptime_get_sys(void) { /* {{{ */ status = sysinfo(&info); if (status != 0) { - char errbuf[1024]; - ERROR("uptime plugin: Error calling sysinfo: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("uptime plugin: Error calling sysinfo: %s", STRERRNO); return -1; } @@ -151,9 +150,7 @@ static time_t uptime_get_sys(void) { /* {{{ */ status = sysctl(mib, STATIC_ARRAY_SIZE(mib), &boottv, &boottv_len, /* new_value = */ NULL, /* new_length = */ 0); if (status != 0) { - char errbuf[1024]; - ERROR("uptime plugin: No value read from sysctl interface: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("uptime plugin: No value read from sysctl interface: %s", STRERRNO); return -1; } @@ -173,9 +170,7 @@ static time_t uptime_get_sys(void) { /* {{{ */ status = perfstat_cpu_total(NULL, &cputotal, sizeof(perfstat_cpu_total_t), 1); if (status < 0) { - char errbuf[1024]; - ERROR("uptime plugin: perfstat_cpu_total: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("uptime plugin: perfstat_cpu_total: %s", STRERRNO); return -1; } diff --git a/src/utils_cmd_getthreshold.c b/src/utils_cmd_getthreshold.c index 78f9a75a..c1f3f562 100644 --- a/src/utils_cmd_getthreshold.c +++ b/src/utils_cmd_getthreshold.c @@ -36,9 +36,8 @@ #define print_to_socket(fh, ...) \ if (fprintf(fh, __VA_ARGS__) < 0) { \ - char errbuf[1024]; \ WARNING("handle_getthreshold: failed to write to socket #%i: %s", \ - fileno(fh), sstrerror(errno, errbuf, sizeof(errbuf))); \ + fileno(fh), STRERRNO); \ return -1; \ } @@ -152,7 +151,7 @@ int handle_getthreshold(FILE *fh, char *buffer) { i++; /* Print the response */ - print_to_socket(fh, "%zu Threshold found\n", i); + print_to_socket(fh, "%" PRIsz " Threshold found\n", i); if (threshold.host[0] != 0) print_to_socket(fh, "Host: %s\n", threshold.host); diff --git a/src/utils_cmd_getval.c b/src/utils_cmd_getval.c index 59046f69..f747d5b8 100644 --- a/src/utils_cmd_getval.c +++ b/src/utils_cmd_getval.c @@ -78,9 +78,8 @@ cmd_status_t cmd_parse_getval(size_t argc, char **argv, #define print_to_socket(fh, ...) \ do { \ if (fprintf(fh, __VA_ARGS__) < 0) { \ - char errbuf[1024]; \ WARNING("cmd_handle_getval: failed to write to socket #%i: %s", \ - fileno(fh), sstrerror(errno, errbuf, sizeof(errbuf))); \ + fileno(fh), STRERRNO); \ return -1; \ } \ fflush(fh); \ @@ -132,8 +131,8 @@ cmd_status_t cmd_handle_getval(FILE *fh, char *buffer) { } if (ds->ds_num != values_num) { - ERROR("ds[%s]->ds_num = %zu, " - "but uc_get_rate_by_name returned %zu values.", + ERROR("ds[%s]->ds_num = %" PRIsz ", " + "but uc_get_rate_by_name returned %" PRIsz " values.", ds->type, ds->ds_num, values_num); cmd_error(CMD_ERROR, &err, "Error reading value from cache."); sfree(values); @@ -141,7 +140,7 @@ cmd_status_t cmd_handle_getval(FILE *fh, char *buffer) { return CMD_ERROR; } - print_to_socket(fh, "%zu Value%s found\n", values_num, + print_to_socket(fh, "%" PRIsz " Value%s found\n", values_num, (values_num == 1) ? "" : "s"); for (size_t i = 0; i < values_num; i++) { print_to_socket(fh, "%s=", ds->ds[i].name); diff --git a/src/utils_cmd_listval.c b/src/utils_cmd_listval.c index 36406305..24ec9421 100644 --- a/src/utils_cmd_listval.c +++ b/src/utils_cmd_listval.c @@ -34,8 +34,6 @@ #include "utils_parse_option.h" cmd_status_t cmd_parse_listval(size_t argc, char **argv, - cmd_listval_t *ret_listval - __attribute__((unused)), const cmd_options_t *opts __attribute__((unused)), cmd_error_handler_t *err) { @@ -62,9 +60,8 @@ cmd_status_t cmd_parse_listval(size_t argc, char **argv, #define print_to_socket(fh, ...) \ do { \ if (fprintf(fh, __VA_ARGS__) < 0) { \ - char errbuf[1024]; \ WARNING("handle_listval: failed to write to socket #%i: %s", fileno(fh), \ - sstrerror(errno, errbuf, sizeof(errbuf))); \ + STRERRNO); \ free_everything_and_return(CMD_ERROR); \ } \ fflush(fh); \ @@ -104,7 +101,3 @@ cmd_status_t cmd_handle_listval(FILE *fh, char *buffer) { free_everything_and_return(CMD_OK); } /* cmd_status_t cmd_handle_listval */ - -void cmd_destroy_listval(cmd_listval_t *listval __attribute__((unused))) { - /* nothing to do */ -} /* void cmd_destroy_listval */ diff --git a/src/utils_cmd_listval.h b/src/utils_cmd_listval.h index 6abdeee3..6dbaabcb 100644 --- a/src/utils_cmd_listval.h +++ b/src/utils_cmd_listval.h @@ -32,12 +32,9 @@ #include "utils_cmds.h" cmd_status_t cmd_parse_listval(size_t argc, char **argv, - cmd_listval_t *ret_listval, const cmd_options_t *opts, cmd_error_handler_t *err); cmd_status_t cmd_handle_listval(FILE *fh, char *buffer); -void cmd_destroy_listval(cmd_listval_t *listval); - #endif /* UTILS_CMD_LISTVAL_H */ diff --git a/src/utils_cmd_putnotif.c b/src/utils_cmd_putnotif.c index 0085c62b..75a8fae8 100644 --- a/src/utils_cmd_putnotif.c +++ b/src/utils_cmd_putnotif.c @@ -35,9 +35,8 @@ #define print_to_socket(fh, ...) \ do { \ if (fprintf(fh, __VA_ARGS__) < 0) { \ - char errbuf[1024]; \ WARNING("handle_putnotif: failed to write to socket #%i: %s", \ - fileno(fh), sstrerror(errno, errbuf, sizeof(errbuf))); \ + fileno(fh), STRERRNO); \ return -1; \ } \ fflush(fh); \ diff --git a/src/utils_cmd_putval.c b/src/utils_cmd_putval.c index 6f1bc39e..b5b9065b 100644 --- a/src/utils_cmd_putval.c +++ b/src/utils_cmd_putval.c @@ -271,7 +271,7 @@ int cmd_create_putval(char *ret, size_t ret_len, /* {{{ */ escape_string(buffer_ident, sizeof(buffer_ident)); status = format_values(buffer_values, sizeof(buffer_values), ds, vl, - /* store rates = */ 0); + /* store rates = */ false); if (status != 0) return status; escape_string(buffer_values, sizeof(buffer_values)); diff --git a/src/utils_cmds.c b/src/utils_cmds.c index 055c987c..88fdfc7f 100644 --- a/src/utils_cmds.c +++ b/src/utils_cmds.c @@ -26,12 +26,12 @@ * Sebastian 'tokkee' Harl **/ -#include "utils_cmds.h" #include "daemon/common.h" #include "utils_cmd_flush.h" #include "utils_cmd_getval.h" #include "utils_cmd_listval.h" #include "utils_cmd_putval.h" +#include "utils_cmds.h" #include "utils_parse_option.h" #include @@ -206,8 +206,7 @@ cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd, cmd_parse_getval(argc - 1, argv + 1, &ret_cmd->cmd.getval, opts, err); } else if (strcasecmp("LISTVAL", command) == 0) { ret_cmd->type = CMD_LISTVAL; - status = - cmd_parse_listval(argc - 1, argv + 1, &ret_cmd->cmd.listval, opts, err); + status = cmd_parse_listval(argc - 1, argv + 1, opts, err); } else if (strcasecmp("PUTVAL", command) == 0) { ret_cmd->type = CMD_PUTVAL; status = @@ -252,7 +251,6 @@ void cmd_destroy(cmd_t *cmd) { cmd_destroy_getval(&cmd->cmd.getval); break; case CMD_LISTVAL: - cmd_destroy_listval(&cmd->cmd.listval); break; case CMD_PUTVAL: cmd_destroy_putval(&cmd->cmd.putval); @@ -301,9 +299,8 @@ void cmd_error_fh(void *ud, cmd_status_t status, const char *format, vsnprintf(buf, sizeof(buf), format, ap); buf[sizeof(buf) - 1] = '\0'; if (fprintf(fh, "%i %s\n", code, buf) < 0) { - char errbuf[1024]; WARNING("utils_cmds: failed to write to file-handle #%i: %s", fileno(fh), - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return; } diff --git a/src/utils_cmds.h b/src/utils_cmds.h index 26d5338d..f3882f54 100644 --- a/src/utils_cmds.h +++ b/src/utils_cmds.h @@ -39,13 +39,13 @@ typedef enum { CMD_PUTVAL = 4, } cmd_type_t; #define CMD_TO_STRING(type) \ - ((type) == CMD_FLUSH) ? "FLUSH" : ((type) == CMD_GETVAL) \ - ? "GETVAL" \ - : ((type) == CMD_LISTVAL) \ - ? "LISTVAL" \ - : ((type) == CMD_PUTVAL) \ - ? "PUTVAL" \ - : "UNKNOWN" + ((type) == CMD_FLUSH) \ + ? "FLUSH" \ + : ((type) == CMD_GETVAL) \ + ? "GETVAL" \ + : ((type) == CMD_LISTVAL) \ + ? "LISTVAL" \ + : ((type) == CMD_PUTVAL) ? "PUTVAL" : "UNKNOWN" typedef struct { double timeout; @@ -62,9 +62,6 @@ typedef struct { } cmd_getval_t; typedef struct { -} cmd_listval_t; - -typedef struct { /* The raw identifier as provided by the user. */ char *raw_identifier; @@ -86,7 +83,6 @@ typedef struct { union { cmd_flush_t flush; cmd_getval_t getval; - cmd_listval_t listval; cmd_putval_t putval; } cmd; } cmd_t; diff --git a/src/utils_cmds_test.c b/src/utils_cmds_test.c index bb35ce8c..93bf5129 100644 --- a/src/utils_cmds_test.c +++ b/src/utils_cmds_test.c @@ -191,7 +191,7 @@ DEF_TEST(parse) { cmd_status_t status; cmd_t cmd; - _Bool result; + bool result; memset(&cmd, 0, sizeof(cmd)); diff --git a/src/utils_config_cores.c b/src/utils_config_cores.c index 085e8abe..94657459 100644 --- a/src/utils_config_cores.c +++ b/src/utils_config_cores.c @@ -163,7 +163,7 @@ static int check_core_grouping(char *out, const char *in, size_t out_size, ERROR(UTIL_NAME ": Missing closing bracket ] in option %s.", in); return -EINVAL; } - if ((end - start) >= out_size) { + if ((size_t)(end - start) >= out_size) { ERROR(UTIL_NAME ": Out buffer is too small."); return -EINVAL; } diff --git a/src/utils_config_cores.h b/src/utils_config_cores.h index e22cbcfe..d45f8480 100644 --- a/src/utils_config_cores.h +++ b/src/utils_config_cores.h @@ -36,7 +36,7 @@ struct core_group_s { char *desc; - unsigned *cores; + unsigned int *cores; size_t num_cores; }; typedef struct core_group_s core_group_t; diff --git a/src/utils_curl_stats.c b/src/utils_curl_stats.c index 2a1d9de3..09856599 100644 --- a/src/utils_curl_stats.c +++ b/src/utils_curl_stats.c @@ -182,7 +182,7 @@ curl_stats_t *curl_stats_from_config(oconfig_item_t *ci) { oconfig_item_t *c = ci->children + i; size_t field; - _Bool enabled = 0; + bool enabled = 0; for (field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) { if (!strcasecmp(c->key, field_specs[field].config_key)) diff --git a/src/utils_db_query.c b/src/utils_db_query.c index 41f40d9c..73c1b45e 100644 --- a/src/utils_db_query.c +++ b/src/utils_db_query.c @@ -84,55 +84,29 @@ struct udb_query_preparation_area_s /* {{{ */ char *plugin; char *db_name; - cdtime_t interval; - udb_result_preparation_area_t *result_prep_areas; }; /* }}} */ /* * Config Private functions */ -static int udb_config_set_string(char **ret_string, /* {{{ */ - oconfig_item_t *ci) { - char *string; - - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - WARNING("db query utils: The `%s' config option " - "needs exactly one string argument.", - ci->key); - return -1; - } - - string = strdup(ci->values[0].value.string); - if (string == NULL) { - ERROR("db query utils: strdup failed."); - return -1; - } - - if (*ret_string != NULL) - free(*ret_string); - *ret_string = string; - - return 0; -} /* }}} int udb_config_set_string */ - static int udb_config_add_string(char ***ret_array, /* {{{ */ size_t *ret_array_len, oconfig_item_t *ci) { char **array; size_t array_len; if (ci->values_num < 1) { - WARNING("db query utils: The `%s' config option " - "needs at least one argument.", - ci->key); + P_WARNING("The `%s' config option " + "needs at least one argument.", + ci->key); return -1; } for (int i = 0; i < ci->values_num; i++) { if (ci->values[i].type != OCONFIG_TYPE_STRING) { - WARNING("db query utils: Argument %i to the `%s' option " - "is not a string.", - i + 1, ci->key); + P_WARNING("Argument %i to the `%s' option " + "is not a string.", + i + 1, ci->key); return -1; } } @@ -140,7 +114,7 @@ static int udb_config_add_string(char ***ret_array, /* {{{ */ array_len = *ret_array_len; array = realloc(*ret_array, sizeof(char *) * (array_len + ci->values_num)); if (array == NULL) { - ERROR("db query utils: realloc failed."); + P_ERROR("udb_config_add_string: realloc failed."); return -1; } *ret_array = array; @@ -148,7 +122,7 @@ static int udb_config_add_string(char ***ret_array, /* {{{ */ for (int i = 0; i < ci->values_num; i++) { array[array_len] = strdup(ci->values[i].value.string); if (array[array_len] == NULL) { - ERROR("db query utils: strdup failed."); + P_ERROR("udb_config_add_string: strdup failed."); *ret_array_len = array_len; return -1; } @@ -161,18 +135,19 @@ static int udb_config_add_string(char ***ret_array, /* {{{ */ static int udb_config_set_uint(unsigned int *ret_value, /* {{{ */ oconfig_item_t *ci) { - double tmp; if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) { - WARNING("db query utils: The `%s' config option " - "needs exactly one numeric argument.", - ci->key); + P_WARNING("The `%s' config option " + "needs exactly one numeric argument.", + ci->key); return -1; } - tmp = ci->values[0].value.number; - if ((tmp < 0.0) || (tmp > ((double)UINT_MAX))) + double tmp = ci->values[0].value.number; + if ((tmp < 0.0) || (tmp > ((double)UINT_MAX))) { + P_WARNING("The value given for the `%s` option is out of range.", ci->key); return -ERANGE; + } *ret_value = (unsigned int)(tmp + .5); return 0; @@ -194,7 +169,7 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */ vl.values = calloc(r->values_num, sizeof(*vl.values)); if (vl.values == NULL) { - ERROR("db query utils: calloc failed."); + P_ERROR("udb_result_submit: calloc failed."); return -1; } vl.values_len = r_area->ds->ds_num; @@ -203,17 +178,14 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */ char *value_str = r_area->values_buffer[i]; if (0 != parse_value(value_str, &vl.values[i], r_area->ds->ds[i].type)) { - ERROR("db query utils: udb_result_submit: Parsing `%s' as %s failed.", - value_str, DS_TYPE_TO_STRING(r_area->ds->ds[i].type)); + P_ERROR("udb_result_submit: Parsing `%s' as %s failed.", value_str, + DS_TYPE_TO_STRING(r_area->ds->ds[i].type)); errno = EINVAL; free(vl.values); return -1; } } - if (q_area->interval > 0) - vl.interval = q_area->interval; - sstrncpy(vl.host, q_area->host, sizeof(vl.host)); sstrncpy(vl.plugin, q_area->plugin, sizeof(vl.plugin)); sstrncpy(vl.type, r->type, sizeof(vl.type)); @@ -238,7 +210,7 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */ int status = strjoin(vl.type_instance, sizeof(vl.type_instance), r_area->instances_buffer, r->instances_num, "-"); if (status < 0) { - ERROR( + P_ERROR( "udb_result_submit: creating type_instance failed with status %d.", status); return status; @@ -249,25 +221,26 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */ int status = strjoin(tmp, sizeof(tmp), r_area->instances_buffer, r->instances_num, "-"); if (status < 0) { - ERROR( + P_ERROR( "udb_result_submit: creating type_instance failed with status %d.", status); return status; } - tmp[sizeof(tmp) - 1] = 0; + tmp[sizeof(tmp) - 1] = '\0'; snprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s", r->instance_prefix, tmp); } } - vl.type_instance[sizeof(vl.type_instance) - 1] = 0; + vl.type_instance[sizeof(vl.type_instance) - 1] = '\0'; /* }}} */ /* Annotate meta data. {{{ */ if (r->metadata_num > 0) { vl.meta = meta_data_create(); if (vl.meta == NULL) { - ERROR("db query utils:: meta_data_create failed."); + P_ERROR("udb_result_submit: meta_data_create failed."); + free(vl.values); return -ENOMEM; } @@ -275,9 +248,10 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */ int status = meta_data_add_string(vl.meta, r->metadata[i], r_area->metadata_buffer[i]); if (status != 0) { - ERROR("db query utils:: meta_data_add_string failed."); + P_ERROR("udb_result_submit: meta_data_add_string failed."); meta_data_destroy(vl.meta); vl.meta = NULL; + free(vl.values); return status; } } @@ -336,36 +310,35 @@ static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */ if ((r == NULL) || (prep_area == NULL)) return -EINVAL; +#if COLLECT_DEBUG + assert(prep_area->ds == NULL); + assert(prep_area->instances_pos == NULL); + assert(prep_area->values_pos == NULL); + assert(prep_area->metadata_pos == NULL); + assert(prep_area->instances_buffer == NULL); + assert(prep_area->values_buffer == NULL); + assert(prep_area->metadata_buffer == NULL); +#endif + #define BAIL_OUT(status) \ - prep_area->ds = NULL; \ - sfree(prep_area->instances_pos); \ - sfree(prep_area->values_pos); \ - sfree(prep_area->metadata_pos); \ - sfree(prep_area->instances_buffer); \ - sfree(prep_area->values_buffer); \ - sfree(prep_area->metadata_buffer); \ + udb_result_finish_result(r, prep_area); \ return (status) - /* Make sure previous preparations are cleaned up. */ - udb_result_finish_result(r, prep_area); - prep_area->instances_pos = NULL; - prep_area->values_pos = NULL; - prep_area->metadata_pos = NULL; - /* Read `ds' and check number of values {{{ */ prep_area->ds = plugin_get_ds(r->type); if (prep_area->ds == NULL) { - ERROR("db query utils: udb_result_prepare_result: Type `%s' is not " - "known by the daemon. See types.db(5) for details.", - r->type); + P_ERROR("udb_result_prepare_result: Type `%s' is not " + "known by the daemon. See types.db(5) for details.", + r->type); BAIL_OUT(-1); } if (prep_area->ds->ds_num != r->values_num) { - ERROR("db query utils: udb_result_prepare_result: The type `%s' " - "requires exactly %zu value%s, but the configuration specifies %zu.", - r->type, prep_area->ds->ds_num, - (prep_area->ds->ds_num == 1) ? "" : "s", r->values_num); + P_ERROR("udb_result_prepare_result: The type `%s' " + "requires exactly %" PRIsz + " value%s, but the configuration specifies %" PRIsz ".", + r->type, prep_area->ds->ds_num, + (prep_area->ds->ds_num == 1) ? "" : "s", r->values_num); BAIL_OUT(-1); } /* }}} */ @@ -374,41 +347,44 @@ static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */ * r->instances_buffer, r->values_buffer, and r->metadata_buffer {{{ */ if (r->instances_num > 0) { prep_area->instances_pos = - (size_t *)calloc(r->instances_num, sizeof(size_t)); + calloc(r->instances_num, sizeof(*prep_area->instances_pos)); if (prep_area->instances_pos == NULL) { - ERROR("db query utils: udb_result_prepare_result: calloc failed."); + P_ERROR("udb_result_prepare_result: calloc failed."); BAIL_OUT(-ENOMEM); } prep_area->instances_buffer = - (char **)calloc(r->instances_num, sizeof(char *)); + calloc(r->instances_num, sizeof(*prep_area->instances_buffer)); if (prep_area->instances_buffer == NULL) { - ERROR("db query utils: udb_result_prepare_result: calloc failed."); + P_ERROR("udb_result_prepare_result: calloc failed."); BAIL_OUT(-ENOMEM); } } /* if (r->instances_num > 0) */ - prep_area->values_pos = (size_t *)calloc(r->values_num, sizeof(size_t)); + prep_area->values_pos = calloc(r->values_num, sizeof(*prep_area->values_pos)); if (prep_area->values_pos == NULL) { - ERROR("db query utils: udb_result_prepare_result: calloc failed."); + P_ERROR("udb_result_prepare_result: calloc failed."); BAIL_OUT(-ENOMEM); } - prep_area->values_buffer = (char **)calloc(r->values_num, sizeof(char *)); + prep_area->values_buffer = + calloc(r->values_num, sizeof(*prep_area->values_buffer)); if (prep_area->values_buffer == NULL) { - ERROR("db query utils: udb_result_prepare_result: calloc failed."); + P_ERROR("udb_result_prepare_result: calloc failed."); BAIL_OUT(-ENOMEM); } - prep_area->metadata_pos = (size_t *)calloc(r->metadata_num, sizeof(size_t)); + prep_area->metadata_pos = + calloc(r->metadata_num, sizeof(*prep_area->metadata_pos)); if (prep_area->metadata_pos == NULL) { - ERROR("db query utils: udb_result_prepare_result: calloc failed."); + P_ERROR("udb_result_prepare_result: calloc failed."); BAIL_OUT(-ENOMEM); } - prep_area->metadata_buffer = (char **)calloc(r->metadata_num, sizeof(char *)); + prep_area->metadata_buffer = + calloc(r->metadata_num, sizeof(*prep_area->metadata_buffer)); if (prep_area->metadata_buffer == NULL) { - ERROR("db query utils: udb_result_prepare_result: calloc failed."); + P_ERROR("udb_result_prepare_result: calloc failed."); BAIL_OUT(-ENOMEM); } @@ -426,9 +402,9 @@ static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */ } if (j >= column_num) { - ERROR("db query utils: udb_result_prepare_result: " - "Column `%s' could not be found.", - r->instances[i]); + P_ERROR("udb_result_prepare_result: " + "Column `%s' could not be found.", + r->instances[i]); BAIL_OUT(-ENOENT); } } /* }}} for (i = 0; i < r->instances_num; i++) */ @@ -445,9 +421,9 @@ static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */ } if (j >= column_num) { - ERROR("db query utils: udb_result_prepare_result: " - "Column `%s' could not be found.", - r->values[i]); + P_ERROR("udb_result_prepare_result: " + "Column `%s' could not be found.", + r->values[i]); BAIL_OUT(-ENOENT); } } /* }}} for (i = 0; i < r->values_num; i++) */ @@ -464,9 +440,9 @@ static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */ } if (j >= column_num) { - ERROR("db query utils: udb_result_prepare_result: " - "Metadata column `%s' could not be found.", - r->values[i]); + P_ERROR("udb_result_prepare_result: " + "Metadata column `%s' could not be found.", + r->values[i]); BAIL_OUT(-ENOENT); } } /* }}} for (i = 0; i < r->metadata_num; i++) */ @@ -506,14 +482,14 @@ static int udb_result_create(const char *query_name, /* {{{ */ int status; if (ci->values_num != 0) { - WARNING("db query utils: The `Result' block doesn't accept " - "any arguments. Ignoring %i argument%s.", - ci->values_num, (ci->values_num == 1) ? "" : "s"); + P_WARNING("The `Result' block doesn't accept " + "any arguments. Ignoring %i argument%s.", + ci->values_num, (ci->values_num == 1) ? "" : "s"); } r = calloc(1, sizeof(*r)); if (r == NULL) { - ERROR("db query utils: calloc failed."); + P_ERROR("udb_result_create: calloc failed."); return -1; } r->type = NULL; @@ -529,9 +505,9 @@ static int udb_result_create(const char *query_name, /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp("Type", child->key) == 0) - status = udb_config_set_string(&r->type, child); + status = cf_util_get_string(child, &r->type); else if (strcasecmp("InstancePrefix", child->key) == 0) - status = udb_config_set_string(&r->instance_prefix, child); + status = cf_util_get_string(child, &r->instance_prefix); else if (strcasecmp("InstancesFrom", child->key) == 0) status = udb_config_add_string(&r->instances, &r->instances_num, child); else if (strcasecmp("ValuesFrom", child->key) == 0) @@ -539,8 +515,8 @@ static int udb_result_create(const char *query_name, /* {{{ */ else if (strcasecmp("MetadataFrom", child->key) == 0) status = udb_config_add_string(&r->metadata, &r->metadata_num, child); else { - WARNING("db query utils: Query `%s': Option `%s' not allowed here.", - query_name, child->key); + P_WARNING("Query `%s': Option `%s' not allowed here.", query_name, + child->key); status = -1; } @@ -551,15 +527,15 @@ static int udb_result_create(const char *query_name, /* {{{ */ /* Check that all necessary options have been given. */ while (status == 0) { if (r->type == NULL) { - WARNING("db query utils: `Type' not given for " - "result in query `%s'", - query_name); + P_WARNING("udb_result_create: `Type' not given for " + "result in query `%s'", + query_name); status = -1; } if (r->values == NULL) { - WARNING("db query utils: `ValuesFrom' not given for " - "result in query `%s'", - query_name); + P_WARNING("udb_result_create: `ValuesFrom' not given for " + "result in query `%s'", + query_name); status = -1; } @@ -622,14 +598,14 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */ query_list_len = *ret_query_list_len; if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - WARNING("db query utils: The `Query' block " - "needs exactly one string argument."); + P_WARNING("udb_result_create: The `Query' block " + "needs exactly one string argument."); return -1; } q = calloc(1, sizeof(*q)); if (q == NULL) { - ERROR("db query utils: calloc failed."); + P_ERROR("udb_query_create: calloc failed."); return -1; } q->min_version = 0; @@ -638,7 +614,7 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */ q->results = NULL; q->plugin_instance_from = NULL; - status = udb_config_set_string(&q->name, ci); + status = cf_util_get_string(ci, &q->name); if (status != 0) { sfree(q); return status; @@ -649,7 +625,7 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp("Statement", child->key) == 0) - status = udb_config_set_string(&q->statement, child); + status = cf_util_get_string(child, &q->statement); else if (strcasecmp("Result", child->key) == 0) status = udb_result_create(q->name, &q->results, child); else if (strcasecmp("MinVersion", child->key) == 0) @@ -657,19 +633,19 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */ else if (strcasecmp("MaxVersion", child->key) == 0) status = udb_config_set_uint(&q->max_version, child); else if (strcasecmp("PluginInstanceFrom", child->key) == 0) - status = udb_config_set_string(&q->plugin_instance_from, child); + status = cf_util_get_string(child, &q->plugin_instance_from); /* Call custom callbacks */ else if (cb != NULL) { status = (*cb)(q, child); if (status != 0) { - WARNING("db query utils: The configuration callback failed " - "to handle `%s'.", - child->key); + P_WARNING("The configuration callback failed " + "to handle `%s'.", + child->key); } } else { - WARNING("db query utils: Query `%s': Option `%s' not allowed here.", - q->name, child->key); + P_WARNING("Query `%s': Option `%s' not allowed here.", q->name, + child->key); status = -1; } @@ -680,12 +656,11 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */ /* Check that all necessary options have been given. */ if (status == 0) { if (q->statement == NULL) { - WARNING("db query utils: Query `%s': No `Statement' given.", q->name); + P_WARNING("Query `%s': No `Statement' given.", q->name); status = -1; } if (q->results == NULL) { - WARNING("db query utils: Query `%s': No (valid) `Result' block given.", - q->name); + P_WARNING("Query `%s': No (valid) `Result' block given.", q->name); status = -1; } } /* if (status == 0) */ @@ -697,7 +672,7 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */ temp = realloc(query_list, sizeof(*query_list) * (query_list_len + 1)); if (temp == NULL) { - ERROR("db query utils: realloc failed"); + P_ERROR("udb_query_create: realloc failed"); status = -1; } else { query_list = temp; @@ -737,8 +712,8 @@ int udb_query_pick_from_list_by_name(const char *name, /* {{{ */ if ((name == NULL) || (src_list == NULL) || (dst_list == NULL) || (dst_list_len == NULL)) { - ERROR("db query utils: udb_query_pick_from_list_by_name: " - "Invalid argument."); + P_ERROR("udb_query_pick_from_list_by_name: " + "Invalid argument."); return -EINVAL; } @@ -753,7 +728,7 @@ int udb_query_pick_from_list_by_name(const char *name, /* {{{ */ tmp_list_len = *dst_list_len; tmp_list = realloc(*dst_list, (tmp_list_len + 1) * sizeof(udb_query_t *)); if (tmp_list == NULL) { - ERROR("db query utils: realloc failed."); + P_ERROR("udb_query_pick_from_list_by_name: realloc failed."); return -ENOMEM; } @@ -767,12 +742,12 @@ int udb_query_pick_from_list_by_name(const char *name, /* {{{ */ } /* for (i = 0; i < src_list_len; i++) */ if (num_added <= 0) { - ERROR("db query utils: Cannot find query `%s'. Make sure the " - "block is above the database definition!", - name); + P_ERROR("Cannot find query `%s'. Make sure the " + "block is above the database definition!", + name); return -ENOENT; } else { - DEBUG("db query utils: Added %i versions of query `%s'.", num_added, name); + DEBUG("Added %i versions of query `%s'.", num_added, name); } return 0; @@ -785,15 +760,15 @@ int udb_query_pick_from_list(oconfig_item_t *ci, /* {{{ */ if ((ci == NULL) || (src_list == NULL) || (dst_list == NULL) || (dst_list_len == NULL)) { - ERROR("db query utils: udb_query_pick_from_list: " - "Invalid argument."); + P_ERROR("udb_query_pick_from_list: " + "Invalid argument."); return -EINVAL; } if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - ERROR("db query utils: The `%s' config option " - "needs exactly one string argument.", - ci->key); + P_ERROR("The `%s' config option " + "needs exactly one string argument.", + ci->key); return -1; } name = ci->values[0].value.string; @@ -858,8 +833,6 @@ void udb_query_finish_result(udb_query_t const *q, /* {{{ */ sfree(prep_area->plugin); sfree(prep_area->db_name); - prep_area->interval = 0; - for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL; r = r->next, r_area = r_area->next) { /* this may happen during error conditions of the caller */ @@ -882,17 +855,17 @@ int udb_query_handle_result(udb_query_t const *q, /* {{{ */ if ((prep_area->column_num < 1) || (prep_area->host == NULL) || (prep_area->plugin == NULL) || (prep_area->db_name == NULL)) { - ERROR("db query utils: Query `%s': Query is not prepared; " - "can't handle result.", - q->name); + P_ERROR("Query `%s': Query is not prepared; " + "can't handle result.", + q->name); return -EINVAL; } #if defined(COLLECT_DEBUG) && COLLECT_DEBUG /* {{{ */ do { for (size_t i = 0; i < prep_area->column_num; i++) { - DEBUG("db query utils: udb_query_handle_result (%s, %s): " - "column[%zu] = %s;", + DEBUG("udb_query_handle_result (%s, %s): " + "column[%" PRIsz "] = %s;", prep_area->db_name, q->name, i, column_values[i]); } } while (0); @@ -907,9 +880,9 @@ int udb_query_handle_result(udb_query_t const *q, /* {{{ */ } if (success == 0) { - ERROR("db query utils: udb_query_handle_result (%s, %s): " - "All results failed.", - prep_area->db_name, q->name); + P_ERROR("udb_query_handle_result (%s, %s): " + "All results failed.", + prep_area->db_name, q->name); return -1; } @@ -920,7 +893,7 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */ udb_query_preparation_area_t *prep_area, const char *host, const char *plugin, const char *db_name, char **column_names, - size_t column_num, cdtime_t interval) { + size_t column_num) { udb_result_preparation_area_t *r_area; udb_result_t *r; int status; @@ -928,19 +901,21 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */ if ((q == NULL) || (prep_area == NULL)) return -EINVAL; - udb_query_finish_result(q, prep_area); +#if COLLECT_DEBUG + assert(prep_area->column_num == 0); + assert(prep_area->host == NULL); + assert(prep_area->plugin == NULL); + assert(prep_area->db_name == NULL); +#endif prep_area->column_num = column_num; prep_area->host = strdup(host); prep_area->plugin = strdup(plugin); prep_area->db_name = strdup(db_name); - prep_area->interval = interval; - if ((prep_area->host == NULL) || (prep_area->plugin == NULL) || (prep_area->db_name == NULL)) { - ERROR("db query utils: Query `%s': Prepare failed: Out of memory.", - q->name); + P_ERROR("Query `%s': Prepare failed: Out of memory.", q->name); udb_query_finish_result(q, prep_area); return -ENOMEM; } @@ -948,8 +923,8 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */ #if defined(COLLECT_DEBUG) && COLLECT_DEBUG do { for (size_t i = 0; i < column_num; i++) { - DEBUG("db query utils: udb_query_prepare_result: " - "query = %s; column[%zu] = %s;", + DEBUG("udb_query_prepare_result: " + "query = %s; column[%" PRIsz "] = %s;", q->name, i, column_names[i]); } } while (0); @@ -967,9 +942,9 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */ } if (i >= column_num) { - ERROR("db query utils: udb_query_prepare_result: " - "Column `%s' from `PluginInstanceFrom' could not be found.", - q->plugin_instance_from); + P_ERROR("udb_query_prepare_result: " + "Column `%s' from `PluginInstanceFrom' could not be found.", + q->plugin_instance_from); udb_query_finish_result(q, prep_area); return -ENOENT; } @@ -979,9 +954,9 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */ for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL; r = r->next, r_area = r_area->next) { if (!r_area) { - ERROR("db query utils: Query `%s': Invalid number of result " - "preparation areas.", - q->name); + P_ERROR("Query `%s': Invalid number of result " + "preparation areas.", + q->name); udb_query_finish_result(q, prep_area); return -EINVAL; } diff --git a/src/utils_db_query.h b/src/utils_db_query.h index 4d6129a1..f173204c 100644 --- a/src/utils_db_query.h +++ b/src/utils_db_query.h @@ -71,7 +71,7 @@ int udb_query_prepare_result(udb_query_t const *q, udb_query_preparation_area_t *prep_area, const char *host, const char *plugin, const char *db_name, char **column_names, - size_t column_num, cdtime_t interval); + size_t column_num); int udb_query_handle_result(udb_query_t const *q, udb_query_preparation_area_t *prep_area, char **column_values); diff --git a/src/utils_deq.h b/src/utils_deq.h new file mode 100644 index 00000000..3182baae --- /dev/null +++ b/src/utils_deq.h @@ -0,0 +1,214 @@ +/** + * collectd - src/utils_deq.h + * Copyright(c) 2017 Red Hat 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: + * Andy Smith + */ + +#ifndef utils_deq_h +#define utils_deq_h 1 + +#include +#include +#include + +#define CT_ASSERT(exp) \ + { assert(exp); } + +#define NEW(t) (t *)malloc(sizeof(t)) +#define NEW_ARRAY(t, n) (t *)malloc(sizeof(t) * (n)) +#define NEW_PTR_ARRAY(t, n) (t **)malloc(sizeof(t *) * (n)) + +#define ZERO(p) memset(p, 0, sizeof(*p)) + +#define DEQ_DECLARE(i, d) \ + typedef struct { \ + i *head; \ + i *tail; \ + i *scratch; \ + size_t size; \ + } d + +#define DEQ_LINKS_N(n, t) \ + t *prev##n; \ + t *next##n +#define DEQ_LINKS(t) DEQ_LINKS_N(, t) +#define DEQ_EMPTY \ + { 0, 0, 0, 0 } + +#define DEQ_INIT(d) \ + do { \ + (d).head = 0; \ + (d).tail = 0; \ + (d).scratch = 0; \ + (d).size = 0; \ + } while (0) +#define DEQ_IS_EMPTY(d) ((d).head == 0) +#define DEQ_ITEM_INIT_N(n, i) \ + do { \ + (i)->next##n = 0; \ + (i)->prev##n = 0; \ + } while (0) +#define DEQ_ITEM_INIT(i) DEQ_ITEM_INIT_N(, i) +#define DEQ_HEAD(d) ((d).head) +#define DEQ_TAIL(d) ((d).tail) +#define DEQ_SIZE(d) ((d).size) +#define DEQ_NEXT_N(n, i) (i)->next##n +#define DEQ_NEXT(i) DEQ_NEXT_N(, i) +#define DEQ_PREV_N(n, i) (i)->prev##n +#define DEQ_PREV(i) DEQ_PREV_N(, i) +#define DEQ_MOVE(d1, d2) \ + do { \ + d2 = d1; \ + DEQ_INIT(d1); \ + } while (0) +/** + *@pre ptr points to first element of deq + *@post ptr points to first element of deq that passes test, or 0. Test should + *involve ptr. + */ +#define DEQ_FIND_N(n, ptr, test) \ + while ((ptr) && !(test)) \ + ptr = DEQ_NEXT_N(n, ptr); +#define DEQ_FIND(ptr, test) DEQ_FIND_N(, ptr, test) + +#define DEQ_INSERT_HEAD_N(n, d, i) \ + do { \ + CT_ASSERT((i)->next##n == 0); \ + CT_ASSERT((i)->prev##n == 0); \ + if ((d).head) { \ + (i)->next##n = (d).head; \ + (d).head->prev##n = i; \ + } else { \ + (d).tail = i; \ + (i)->next##n = 0; \ + CT_ASSERT((d).size == 0); \ + } \ + (i)->prev##n = 0; \ + (d).head = i; \ + (d).size++; \ + } while (0) +#define DEQ_INSERT_HEAD(d, i) DEQ_INSERT_HEAD_N(, d, i) + +#define DEQ_INSERT_TAIL_N(n, d, i) \ + do { \ + CT_ASSERT((i)->next##n == 0); \ + CT_ASSERT((i)->prev##n == 0); \ + if ((d).tail) { \ + (i)->prev##n = (d).tail; \ + (d).tail->next##n = i; \ + } else { \ + (d).head = i; \ + (i)->prev##n = 0; \ + CT_ASSERT((d).size == 0); \ + } \ + (i)->next##n = 0; \ + (d).tail = i; \ + (d).size++; \ + } while (0) +#define DEQ_INSERT_TAIL(d, i) DEQ_INSERT_TAIL_N(, d, i) + +#define DEQ_REMOVE_HEAD_N(n, d) \ + do { \ + CT_ASSERT((d).head); \ + if ((d).head) { \ + (d).scratch = (d).head; \ + (d).head = (d).head->next##n; \ + if ((d).head == 0) { \ + (d).tail = 0; \ + CT_ASSERT((d).size == 1); \ + } else \ + (d).head->prev##n = 0; \ + (d).size--; \ + (d).scratch->next##n = 0; \ + (d).scratch->prev##n = 0; \ + } \ + } while (0) +#define DEQ_REMOVE_HEAD(d) DEQ_REMOVE_HEAD_N(, d) + +#define DEQ_REMOVE_TAIL_N(n, d) \ + do { \ + CT_ASSERT((d).tail); \ + if ((d).tail) { \ + (d).scratch = (d).tail; \ + (d).tail = (d).tail->prev##n; \ + if ((d).tail == 0) { \ + (d).head = 0; \ + CT_ASSERT((d).size == 1); \ + } else \ + (d).tail->next##n = 0; \ + (d).size--; \ + (d).scratch->next##n = 0; \ + (d).scratch->prev##n = 0; \ + } \ + } while (0) +#define DEQ_REMOVE_TAIL(d) DEQ_REMOVE_TAIL_N(, d) + +#define DEQ_INSERT_AFTER_N(n, d, i, a) \ + do { \ + CT_ASSERT((i)->next##n == 0); \ + CT_ASSERT((i)->prev##n == 0); \ + CT_ASSERT(a); \ + if ((a)->next##n) \ + (a)->next##n->prev##n = (i); \ + else \ + (d).tail = (i); \ + (i)->next##n = (a)->next##n; \ + (i)->prev##n = (a); \ + (a)->next##n = (i); \ + (d).size++; \ + } while (0) +#define DEQ_INSERT_AFTER(d, i, a) DEQ_INSERT_AFTER_N(, d, i, a) + +#define DEQ_REMOVE_N(n, d, i) \ + do { \ + if ((i)->next##n) \ + (i)->next##n->prev##n = (i)->prev##n; \ + else \ + (d).tail = (i)->prev##n; \ + if ((i)->prev##n) \ + (i)->prev##n->next##n = (i)->next##n; \ + else \ + (d).head = (i)->next##n; \ + CT_ASSERT((d).size > 0); \ + (d).size--; \ + (i)->next##n = 0; \ + (i)->prev##n = 0; \ + CT_ASSERT((d).size || (!(d).head && !(d).tail)); \ + } while (0) +#define DEQ_REMOVE(d, i) DEQ_REMOVE_N(, d, i) + +#define DEQ_APPEND_N(n, d1, d2) \ + do { \ + if (!(d1).head) \ + (d1) = (d2); \ + else if ((d2).head) { \ + (d1).tail->next##n = (d2).head; \ + (d2).head->prev##n = (d1).tail; \ + (d1).tail = (d2).tail; \ + (d1).size += (d2).size; \ + } \ + DEQ_INIT(d2); \ + } while (0) +#define DEQ_APPEND(d1, d2) DEQ_APPEND_N(, d1, d2) + +#endif diff --git a/src/utils_dns.c b/src/utils_dns.c index e7e04f7c..7b20e139 100644 --- a/src/utils_dns.c +++ b/src/utils_dns.c @@ -158,16 +158,16 @@ typedef int(printer)(const char *, ...); */ #if HAVE_PCAP_H -static pcap_t *pcap_obj = NULL; +static pcap_t *pcap_obj; #endif -static ip_list_t *IgnoreList = NULL; +static ip_list_t *IgnoreList; #if HAVE_PCAP_H -static void (*Callback)(const rfc1035_header_t *) = NULL; +static void (*Callback)(const rfc1035_header_t *); -static int query_count_intvl = 0; -static int query_count_total = 0; +static int query_count_intvl; +static int query_count_total; #ifdef __OpenBSD__ static struct bpf_timeval last_ts; #else @@ -267,7 +267,7 @@ static int rfc1035NameUnpack(const char *buf, size_t sz, off_t *off, char *name, off_t no = 0; unsigned char c; size_t len; - static int loop_detect = 0; + static int loop_detect; if (loop_detect > 2) return 4; /* compression loop */ if (ns == 0) diff --git a/src/utils_dpdk.c b/src/utils_dpdk.c index fbdcaf88..3591eae1 100644 --- a/src/utils_dpdk.c +++ b/src/utils_dpdk.c @@ -43,7 +43,11 @@ #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 @@ -184,8 +188,13 @@ int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci) { 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) { @@ -215,20 +224,16 @@ int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci) { static int dpdk_shm_init(const char *name, size_t size, void **map) { DPDK_HELPER_TRACE(name); - char errbuf[ERR_BUF_SIZE]; - int fd = shm_open(name, O_CREAT | O_TRUNC | O_RDWR, 0666); if (fd < 0) { - WARNING("dpdk_shm_init: Failed to open %s as SHM:%s", name, - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("dpdk_shm_init: Failed to open %s as SHM:%s", name, STRERRNO); *map = NULL; return -1; } int ret = ftruncate(fd, size); if (ret != 0) { - WARNING("dpdk_shm_init: Failed to resize SHM:%s", - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("dpdk_shm_init: Failed to resize SHM:%s", STRERRNO); close(fd); *map = NULL; dpdk_shm_cleanup(name, size, NULL); @@ -237,8 +242,7 @@ static int dpdk_shm_init(const char *name, size_t size, void **map) { *map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (*map == MAP_FAILED) { - WARNING("dpdk_shm_init:Failed to mmap SHM:%s", - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("dpdk_shm_init:Failed to mmap SHM:%s", STRERRNO); close(fd); *map = NULL; dpdk_shm_cleanup(name, size, NULL); @@ -253,17 +257,16 @@ static int dpdk_shm_init(const char *name, size_t size, void **map) { static void dpdk_shm_cleanup(const char *name, size_t size, void *map) { DPDK_HELPER_TRACE(name); - char errbuf[ERR_BUF_SIZE]; /* * Call shm_unlink first, as 'name' might be no longer accessible after munmap */ if (shm_unlink(name)) - ERROR("shm_unlink failure %s", sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("shm_unlink failure %s", STRERRNO); if (map != NULL) { if (munmap(map, size)) - ERROR("munmap failure %s", sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("munmap failure %s", STRERRNO); } } @@ -287,7 +290,6 @@ int dpdk_helper_init(const char *name, size_t data_size, dpdk_helper_ctx_t **pphc) { dpdk_helper_ctx_t *phc = NULL; size_t shm_size = sizeof(dpdk_helper_ctx_t) + data_size; - char errbuf[ERR_BUF_SIZE]; if (pphc == NULL) { ERROR("%s:Invalid argument(pphc)", __FUNCTION__); @@ -311,8 +313,7 @@ int dpdk_helper_init(const char *name, size_t data_size, err = sem_init(&phc->sema_cmd_start, 1, 0); if (err != 0) { - ERROR("sema_cmd_start semaphore init failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("sema_cmd_start semaphore init failed: %s", STRERRNO); int errno_m = errno; dpdk_shm_cleanup(name, shm_size, (void *)phc); return -errno_m; @@ -320,8 +321,7 @@ int dpdk_helper_init(const char *name, size_t data_size, err = sem_init(&phc->sema_cmd_complete, 1, 0); if (err != 0) { - ERROR("sema_cmd_complete semaphore init failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("sema_cmd_complete semaphore init failed: %s", STRERRNO); sem_destroy(&phc->sema_cmd_start); int errno_m = errno; dpdk_shm_cleanup(name, shm_size, (void *)phc); @@ -356,7 +356,6 @@ void dpdk_helper_shutdown(dpdk_helper_ctx_t *phc) { } static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) { - char errbuf[ERR_BUF_SIZE]; if (phc == NULL) { ERROR("Invalid argument(phc)"); return -EINVAL; @@ -379,22 +378,19 @@ static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) { } if (pipe(phc->pipes) != 0) { - DEBUG("dpdk_helper_spawn: Could not create helper pipe: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + DEBUG("dpdk_helper_spawn: Could not create helper pipe: %s", STRERRNO); return -1; } int pipe0_flags = fcntl(phc->pipes[0], F_GETFL, 0); int pipe1_flags = fcntl(phc->pipes[1], F_GETFL, 0); if (pipe0_flags == -1 || pipe1_flags == -1) { - WARNING("dpdk_helper_spawn: error setting up pipe flags: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("dpdk_helper_spawn: error setting up pipe flags: %s", STRERRNO); } int pipe0_err = fcntl(phc->pipes[0], F_SETFL, pipe1_flags | O_NONBLOCK); int pipe1_err = fcntl(phc->pipes[1], F_SETFL, pipe0_flags | O_NONBLOCK); if (pipe0_err == -1 || pipe1_err == -1) { - WARNING("dpdk_helper_spawn: error setting up pipes: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("dpdk_helper_spawn: error setting up pipes: %s", STRERRNO); } pid_t pid = fork(); @@ -412,8 +408,7 @@ static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) { dpdk_helper_worker(phc); exit(0); } else { - ERROR("dpdk_helper_start: Failed to fork helper process: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("dpdk_helper_start: Failed to fork helper process: %s", STRERRNO); return -1; } @@ -436,7 +431,6 @@ static int dpdk_helper_exit(dpdk_helper_ctx_t *phc, static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc, enum DPDK_HELPER_STATUS status) { - char errbuf[ERR_BUF_SIZE]; DPDK_HELPER_TRACE(phc->shm_name); close(phc->pipes[1]); @@ -453,8 +447,7 @@ static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc, int err = kill(phc->pid, SIGKILL); if (err) { - ERROR("%s error sending kill to helper: %s", __FUNCTION__, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("%s error sending kill to helper: %s", __FUNCTION__, STRERRNO); } } } else { @@ -464,8 +457,7 @@ static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc, int err = kill(phc->pid, SIGKILL); if (err) { - ERROR("%s error sending kill to helper: %s", __FUNCTION__, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("%s error sending kill to helper: %s", __FUNCTION__, STRERRNO); } } @@ -633,10 +625,9 @@ static int dpdk_helper_worker(dpdk_helper_ctx_t *phc) { DPDK_CHILD_LOG("%s:%s:%d post sema_cmd_complete (pid=%lu)\n", phc->shm_name, __FUNCTION__, __LINE__, (long)getpid()); if (err) { - char errbuf[ERR_BUF_SIZE]; DPDK_CHILD_LOG("dpdk_helper_worker: error posting sema_cmd_complete " "semaphore (%s)\n", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); } #if COLLECT_DEBUG @@ -674,7 +665,6 @@ static const char *dpdk_helper_status_str(enum DPDK_HELPER_STATUS status) { static int dpdk_helper_status_check(dpdk_helper_ctx_t *phc) { DEBUG("%s:%s:%d pid=%u %s", phc->shm_name, __FUNCTION__, __LINE__, getpid(), dpdk_helper_status_str(phc->status)); - char errbuf[ERR_BUF_SIZE]; if (phc->status == DPDK_HELPER_GRACEFUL_QUIT) { return 0; @@ -684,8 +674,7 @@ static int dpdk_helper_status_check(dpdk_helper_ctx_t *phc) { __LINE__); int err = dpdk_helper_spawn(phc); if (err) { - ERROR("dpdkstat: error spawning helper %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("dpdkstat: error spawning helper %s", STRERRNO); } return -1; } @@ -697,8 +686,7 @@ static int dpdk_helper_status_check(dpdk_helper_ctx_t *phc) { __LINE__); int err = dpdk_helper_spawn(phc); if (err) { - ERROR("dpdkstat: error spawning helper %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("dpdkstat: error spawning helper %s", STRERRNO); } return -1; } @@ -723,9 +711,7 @@ static void dpdk_helper_check_pipe(dpdk_helper_ctx_t *phc) { data_avail); if (data_avail < 0) { if (errno != EINTR || errno != EAGAIN) { - char errbuf[ERR_BUF_SIZE]; - ERROR("%s: poll(2) failed: %s", phc->shm_name, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("%s: poll(2) failed: %s", phc->shm_name, STRERRNO); } } while (data_avail) { @@ -767,9 +753,8 @@ int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result, /* kick helper to process command */ int err = sem_post(&phc->sema_cmd_start); if (err) { - char errbuf[ERR_BUF_SIZE]; ERROR("dpdk_helper_worker: error posting sema_cmd_start semaphore (%s)", - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); } #if COLLECT_DEBUG @@ -855,7 +840,7 @@ uint128_t str_to_uint128(const char *str, int len) { return lcore_mask; } else { char low_str[DATA_MAX_NAME_LEN]; - char high_str[DATA_MAX_NAME_LEN]; + char high_str[DATA_MAX_NAME_LEN * 2]; memset(high_str, 0, sizeof(high_str)); memset(low_str, 0, sizeof(low_str)); @@ -876,8 +861,12 @@ uint128_t str_to_uint128(const char *str, int len) { return lcore_mask; } -uint8_t dpdk_helper_eth_dev_count() { +uint8_t dpdk_helper_eth_dev_count(void) { +#if RTE_VERSION < RTE_VERSION_NUM(18, 05, 0, 0) uint8_t ports = rte_eth_dev_count(); +#else + uint8_t ports = rte_eth_dev_count_avail(); +#endif if (ports == 0) { ERROR( "%s:%d: No DPDK ports available. Check bound devices to DPDK driver.\n", diff --git a/src/utils_dpdk.h b/src/utils_dpdk.h index f3b7e7f7..d4551d86 100644 --- a/src/utils_dpdk.h +++ b/src/utils_dpdk.h @@ -74,7 +74,7 @@ int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result, cdtime_t cmd_wait_time); void *dpdk_helper_priv_get(dpdk_helper_ctx_t *phc); int dpdk_helper_data_size_get(dpdk_helper_ctx_t *phc); -uint8_t dpdk_helper_eth_dev_count(); +uint8_t dpdk_helper_eth_dev_count(void); /* forward declaration of handler function that is called by helper from * child process. not implemented in helper. must be provided by client. */ diff --git a/src/utils_fbhash.c b/src/utils_fbhash.c index 366b44b8..07134635 100644 --- a/src/utils_fbhash.c +++ b/src/utils_fbhash.c @@ -102,7 +102,7 @@ static int fbh_read_file(fbhash_t *h) /* {{{ */ char *key_copy; char *value_copy; - buffer[sizeof(buffer) - 1] = 0; + buffer[sizeof(buffer) - 1] = '\0'; len = strlen(buffer); /* Remove trailing newline characters. */ diff --git a/src/utils_format_graphite.c b/src/utils_format_graphite.c index 87cead14..de3f0c2e 100644 --- a/src/utils_format_graphite.c +++ b/src/utils_format_graphite.c @@ -60,14 +60,14 @@ static int gr_format_values(char *ret, size_t ret_len, int ds_num, else if (rates != NULL) BUFFER_ADD("%f", rates[ds_num]); else if (ds->ds[ds_num].type == DS_TYPE_COUNTER) - BUFFER_ADD("%llu", vl->values[ds_num].counter); + BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_num].counter); else if (ds->ds[ds_num].type == DS_TYPE_DERIVE) BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive); else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE) BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute); else { - ERROR("gr_format_values plugin: Unknown data source type: %i", - ds->ds[ds_num].type); + P_ERROR("gr_format_values: Unknown data source type: %i", + ds->ds[ds_num].type); return -1; } @@ -77,7 +77,7 @@ static int gr_format_values(char *ret, size_t ret_len, int ds_num, } static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len, - char escape_char, _Bool preserve_separator) { + char escape_char, bool preserve_separator) { memset(dst, 0, dst_len); if (src == NULL) @@ -97,6 +97,86 @@ static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len, } } +static int gr_format_name_tagged(char *ret, int ret_len, value_list_t const *vl, + char const *ds_name, char const *prefix, + char const *postfix, char const escape_char, + unsigned int flags) { + char n_host[DATA_MAX_NAME_LEN]; + char n_plugin[DATA_MAX_NAME_LEN]; + char n_plugin_instance[DATA_MAX_NAME_LEN]; + char n_type[DATA_MAX_NAME_LEN]; + char n_type_instance[DATA_MAX_NAME_LEN]; + + char tmp_plugin[DATA_MAX_NAME_LEN + 8]; + char tmp_plugin_instance[DATA_MAX_NAME_LEN + 17]; + char tmp_type[DATA_MAX_NAME_LEN + 6]; + char tmp_type_instance[DATA_MAX_NAME_LEN + 15]; + char tmp_metric[3 * DATA_MAX_NAME_LEN + 2]; + char tmp_ds_name[DATA_MAX_NAME_LEN + 9]; + + if (prefix == NULL) + prefix = ""; + + if (postfix == NULL) + postfix = ""; + + gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, 1); + gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char, 1); + gr_copy_escape_part(n_plugin_instance, vl->plugin_instance, + sizeof(n_plugin_instance), escape_char, 1); + gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char, 1); + gr_copy_escape_part(n_type_instance, vl->type_instance, + sizeof(n_type_instance), escape_char, 1); + + snprintf(tmp_plugin, sizeof(tmp_plugin), ";plugin=%s", n_plugin); + + if (n_plugin_instance[0] != '\0') + snprintf(tmp_plugin_instance, sizeof(tmp_plugin_instance), + ";plugin_instance=%s", n_plugin_instance); + else + tmp_plugin_instance[0] = '\0'; + + if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) || strcmp(n_plugin, n_type) != 0) + snprintf(tmp_type, sizeof(tmp_type), ";type=%s", n_type); + else + tmp_type[0] = '\0'; + + if (n_type_instance[0] != '\0') { + if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) || + strcmp(n_plugin_instance, n_type_instance) != 0) + snprintf(tmp_type_instance, sizeof(tmp_type_instance), + ";type_instance=%s", n_type_instance); + else + tmp_type_instance[0] = '\0'; + } else + tmp_type_instance[0] = '\0'; + + /* Assert always_append_ds -> ds_name */ + assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL)); + if (ds_name != NULL) { + snprintf(tmp_ds_name, sizeof(tmp_ds_name), ";ds_name=%s", ds_name); + + if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0) + snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, ds_name); + else + snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s.%s", n_plugin, n_type, + ds_name); + } else { + tmp_ds_name[0] = '\0'; + + if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0) + snprintf(tmp_metric, sizeof(tmp_metric), "%s", n_plugin); + else + snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, n_type); + } + + snprintf(ret, ret_len, "%s%s%s;host=%s%s%s%s%s%s", prefix, tmp_metric, + postfix, n_host, tmp_plugin, tmp_plugin_instance, tmp_type, + tmp_type_instance, tmp_ds_name); + + return 0; +} + static int gr_format_name(char *ret, int ret_len, value_list_t const *vl, char const *ds_name, char const *prefix, char const *postfix, char const escape_char, @@ -116,7 +196,7 @@ static int gr_format_name(char *ret, int ret_len, value_list_t const *vl, if (postfix == NULL) postfix = ""; - _Bool preserve_separator = (flags & GRAPHITE_PRESERVE_SEPARATOR) ? 1 : 0; + bool preserve_separator = (flags & GRAPHITE_PRESERVE_SEPARATOR); gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, preserve_separator); @@ -183,7 +263,7 @@ int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds, if (flags & GRAPHITE_STORE_RATES) { rates = uc_get_rate(ds, vl); if (rates == NULL) { - ERROR("format_graphite: error with uc_get_rate"); + P_ERROR("format_graphite: error with uc_get_rate"); return -1; } } @@ -199,20 +279,31 @@ int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds, ds_name = ds->ds[i].name; /* Copy the identifier to `key' and escape it. */ - status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix, - escape_char, flags); - if (status != 0) { - ERROR("format_graphite: error with gr_format_name"); - sfree(rates); - return status; + if (flags & GRAPHITE_USE_TAGS) { + status = gr_format_name_tagged(key, sizeof(key), vl, ds_name, prefix, + postfix, escape_char, flags); + if (status != 0) { + P_ERROR("format_graphite: error with gr_format_name_tagged"); + sfree(rates); + return status; + } + } else { + status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix, + escape_char, flags); + if (status != 0) { + P_ERROR("format_graphite: error with gr_format_name"); + sfree(rates); + return status; + } } escape_graphite_string(key, escape_char); + /* Convert the values to an ASCII representation and put that into * `values'. */ status = gr_format_values(values, sizeof(values), i, ds, vl, rates); if (status != 0) { - ERROR("format_graphite: error with gr_format_values"); + P_ERROR("format_graphite: error with gr_format_values"); sfree(rates); return status; } @@ -222,16 +313,16 @@ int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds, (size_t)snprintf(message, sizeof(message), "%s %s %u\r\n", key, values, (unsigned int)CDTIME_T_TO_TIME_T(vl->time)); if (message_len >= sizeof(message)) { - ERROR("format_graphite: message buffer too small: " - "Need %zu bytes.", - message_len + 1); + P_ERROR("format_graphite: message buffer too small: " + "Need %" PRIsz " bytes.", + message_len + 1); sfree(rates); return -ENOMEM; } /* Append it in case we got multiple data set */ if ((buffer_pos + message_len) >= buffer_size) { - ERROR("format_graphite: target buffer too small"); + P_ERROR("format_graphite: target buffer too small"); sfree(rates); return -ENOMEM; } diff --git a/src/utils_format_graphite.h b/src/utils_format_graphite.h index de90c44c..60b89ae7 100644 --- a/src/utils_format_graphite.h +++ b/src/utils_format_graphite.h @@ -31,6 +31,7 @@ #define GRAPHITE_ALWAYS_APPEND_DS 0x04 #define GRAPHITE_DROP_DUPE_FIELDS 0x08 #define GRAPHITE_PRESERVE_SEPARATOR 0x10 +#define GRAPHITE_USE_TAGS 0x20 int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds, const value_list_t *vl, const char *prefix, diff --git a/src/utils_format_graphite_test.c b/src/utils_format_graphite_test.c index a82142fa..42efa681 100644 --- a/src/utils_format_graphite_test.c +++ b/src/utils_format_graphite_test.c @@ -124,6 +124,22 @@ DEF_TEST(metric_name) { .suffix = NULL, .want_name = "foo.example@com.test.single", }, + /* flag GRAPHITE_USE_TAGS */ + {.flags = GRAPHITE_USE_TAGS, + .want_name = "test.single;host=example.com;plugin=test;type=single"}, + {.plugin_instance = "f.o.o", + .type_instance = "b.a.r", + .flags = GRAPHITE_USE_TAGS, + .want_name = "test.single;host=example.com;plugin=test;plugin_instance=" + "f.o.o;type=single;type_instance=b.a.r"}, + {.flags = GRAPHITE_USE_TAGS ^ GRAPHITE_ALWAYS_APPEND_DS, + .want_name = "test.single.value;host=example.com;plugin=test;type=" + "single;ds_name=value"}, + {.plugin_instance = "foo", + .type_instance = "foo", + .flags = GRAPHITE_USE_TAGS ^ GRAPHITE_DROP_DUPE_FIELDS, + .want_name = "test.single;host=example.com;plugin=test;plugin_instance=" + "foo;type=single"}, }; for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { diff --git a/src/utils_format_json.c b/src/utils_format_json.c index 4ecbfbe2..25cbb0a7 100644 --- a/src/utils_format_json.c +++ b/src/utils_format_json.c @@ -58,7 +58,7 @@ static int json_escape_string(char *buffer, size_t buffer_size, /* {{{ */ #define BUFFER_ADD(c) \ do { \ if (dst_pos >= (buffer_size - 1)) { \ - buffer[buffer_size - 1] = 0; \ + buffer[buffer_size - 1] = '\0'; \ return -ENOMEM; \ } \ buffer[dst_pos] = (c); \ @@ -130,7 +130,7 @@ static int values_to_json(char *buffer, size_t buffer_size, /* {{{ */ else BUFFER_ADD("null"); } else if (ds->ds[i].type == DS_TYPE_COUNTER) - BUFFER_ADD("%llu", vl->values[i].counter); + BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[i].counter); else if (ds->ds[i].type == DS_TYPE_DERIVE) BUFFER_ADD("%" PRIi64, vl->values[i].derive); else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) @@ -145,7 +145,6 @@ static int values_to_json(char *buffer, size_t buffer_size, /* {{{ */ #undef BUFFER_ADD - DEBUG("format_json: values_to_json: buffer = %s;", buffer); sfree(rates); return 0; } /* }}} int values_to_json */ @@ -179,8 +178,6 @@ static int dstypes_to_json(char *buffer, size_t buffer_size, /* {{{ */ #undef BUFFER_ADD - DEBUG("format_json: dstypes_to_json: buffer = %s;", buffer); - return 0; } /* }}} int dstypes_to_json */ @@ -213,8 +210,6 @@ static int dsnames_to_json(char *buffer, size_t buffer_size, /* {{{ */ #undef BUFFER_ADD - DEBUG("format_json: dsnames_to_json: buffer = %s;", buffer); - return 0; } /* }}} int dsnames_to_json */ @@ -267,7 +262,7 @@ static int meta_data_keys_to_json(char *buffer, size_t buffer_size, /* {{{ */ if (meta_data_get_double(meta, key, &value) == 0) BUFFER_ADD(",\"%s\":%f", key, value); } else if (type == MD_TYPE_BOOLEAN) { - _Bool value = 0; + bool value = false; if (meta_data_get_boolean(meta, key, &value) == 0) BUFFER_ADD(",\"%s\":%s", key, value ? "true" : "false"); } @@ -378,8 +373,6 @@ static int value_list_to_json(char *buffer, size_t buffer_size, /* {{{ */ #undef BUFFER_ADD_KEYVAL #undef BUFFER_ADD - DEBUG("format_json: value_list_to_json: buffer = %s;", buffer); - return 0; } /* }}} int value_list_to_json */ diff --git a/src/utils_format_json_test.c b/src/utils_format_json_test.c index 389004d3..b230ef35 100644 --- a/src/utils_format_json_test.c +++ b/src/utils_format_json_test.c @@ -87,7 +87,7 @@ static int test_map_key(void *ctx, unsigned char const *key, } static int expect_label(char const *name, char const *got, char const *want) { - _Bool ok = (strcmp(got, want) == 0); + bool ok = (strcmp(got, want) == 0); char msg[1024]; if (ok) diff --git a/src/utils_format_kairosdb.c b/src/utils_format_kairosdb.c index 460f807a..d957bc80 100644 --- a/src/utils_format_kairosdb.c +++ b/src/utils_format_kairosdb.c @@ -69,7 +69,7 @@ static int kairosdb_escape_string(char *buffer, size_t buffer_size, /* {{{ */ #define BUFFER_ADD(c) \ do { \ if (dst_pos >= (buffer_size - 1)) { \ - buffer[buffer_size - 1] = 0; \ + buffer[buffer_size - 1] = '\0'; \ return -ENOMEM; \ } \ buffer[dst_pos] = (c); \ @@ -154,7 +154,7 @@ static int values_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */ BUFFER_ADD("[["); BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time)); BUFFER_ADD(","); - BUFFER_ADD("%llu", vl->values[ds_idx].counter); + BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_idx].counter); } else if (ds->ds[ds_idx].type == DS_TYPE_DERIVE) { BUFFER_ADD("[["); BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time)); @@ -356,5 +356,3 @@ int format_kairosdb_value_list(char *buffer, /* {{{ */ (*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 : */ diff --git a/src/utils_format_stackdriver.c b/src/utils_format_stackdriver.c new file mode 100644 index 00000000..afaa8ed8 --- /dev/null +++ b/src/utils_format_stackdriver.c @@ -0,0 +1,766 @@ +/** + * 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 + **/ + +#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 +#include +#if HAVE_YAJL_YAJL_VERSION_H +#include +#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 */ diff --git a/src/utils_format_stackdriver.h b/src/utils_format_stackdriver.h new file mode 100644 index 00000000..fee260e3 --- /dev/null +++ b/src/utils_format_stackdriver.h @@ -0,0 +1,79 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils_format_stackdriver_test.c b/src/utils_format_stackdriver_test.c new file mode 100644 index 00000000..1e96b65e --- /dev/null +++ b/src/utils_format_stackdriver_test.c @@ -0,0 +1,77 @@ +/** + * 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 + **/ + +#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; +} diff --git a/src/utils_gce.c b/src/utils_gce.c new file mode 100644 index 00000000..d43d1de5 --- /dev/null +++ b/src/utils_gce.c @@ -0,0 +1,284 @@ +/** + * 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 + **/ + +#include "collectd.h" + +#include "common.h" +#include "plugin.h" +#include "utils_gce.h" +#include "utils_oauth.h" +#include "utils_time.h" + +#include + +#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 */ diff --git a/src/utils_gce.h b/src/utils_gce.h new file mode 100644 index 00000000..2ee3f6eb --- /dev/null +++ b/src/utils_gce.h @@ -0,0 +1,52 @@ +/** + * 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 + **/ + +#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 diff --git a/src/utils_ignorelist.c b/src/utils_ignorelist.c index 29b2af21..b3851026 100644 --- a/src/utils_ignorelist.c +++ b/src/utils_ignorelist.c @@ -267,7 +267,7 @@ int ignorelist_add(ignorelist_t *il, const char *entry) { return ENOMEM; /* trim trailing slash */ - copy[strlen(copy) - 1] = 0; + copy[strlen(copy) - 1] = '\0'; status = ignorelist_append_regex(il, copy); sfree(copy); diff --git a/src/utils_latency.c b/src/utils_latency.c index 625fc42e..6e4f8732 100644 --- a/src/utils_latency.c +++ b/src/utils_latency.c @@ -65,7 +65,7 @@ struct latency_counter_s { * When a value above this range is added, Histogram's range is increased by * increasing the bin width (note that number of bins remains always at 1000). * This operation of increasing bin width is little expensive as each bin need -* to be visited to update it's count. To reduce frequent change of bin width, +* to be visited to update its count. To reduce frequent change of bin width, * new bin width will be the next nearest power of 2. Example: 2, 4, 8, 16, 32, * 64, 128, 256, 512, 1024, 2048, 5086, ... * @@ -156,7 +156,7 @@ void latency_counter_add(latency_counter_t *lc, cdtime_t latency) /* {{{ */ 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; } } diff --git a/src/utils_latency_config.c b/src/utils_latency_config.c index 5eb5b6d9..9a91f113 100644 --- a/src/utils_latency_config.c +++ b/src/utils_latency_config.c @@ -25,29 +25,28 @@ * Pavel Rochnyack */ -#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; @@ -57,31 +56,29 @@ static int latency_config_add_percentile(latency_config_t *conf, 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; @@ -94,22 +91,21 @@ static int latency_config_add_bucket(latency_config_t *conf, oconfig_item_t *ci, 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; @@ -117,9 +113,9 @@ int latency_config(latency_config_t *conf, oconfig_item_t *ci, 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; } diff --git a/src/utils_latency_config.h b/src/utils_latency_config.h index 7008fd00..3d2691a6 100644 --- a/src/utils_latency_config.h +++ b/src/utils_latency_config.h @@ -47,14 +47,13 @@ typedef struct { char *bucket_type; /* - _Bool lower; - _Bool upper; - _Bool avg; + bool lower; + bool upper; + bool avg; */ } 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); diff --git a/src/utils_latency_test.c b/src/utils_latency_test.c index 427a1592..42a6e87e 100644 --- a/src/utils_latency_test.c +++ b/src/utils_latency_test.c @@ -26,8 +26,8 @@ #define DBL_PRECISION 1e-6 -#include "common.h" /* for STATIC_ARRAY_SIZE */ #include "collectd.h" +#include "common.h" /* for STATIC_ARRAY_SIZE */ #include "testing.h" #include "utils_latency.h" @@ -52,7 +52,7 @@ DEF_TEST(simple) { CHECK_NOT_NULL(l = latency_counter_create()); for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { - printf("# case %zu: DOUBLE_TO_CDTIME_T(%g) = %" PRIu64 "\n", i, + printf("# case %" PRIsz ": DOUBLE_TO_CDTIME_T(%g) = %" PRIu64 "\n", i, cases[i].val, DOUBLE_TO_CDTIME_T(cases[i].val)); latency_counter_add(l, DOUBLE_TO_CDTIME_T(cases[i].val)); diff --git a/src/utils_lua.c b/src/utils_lua.c index 0990472b..11ac0010 100644 --- a/src/utils_lua.c +++ b/src/utils_lua.c @@ -24,12 +24,8 @@ * Florian Forster **/ -/* defines a macro using "sprintf". Although not used here, - * GCC will complain about the macro definition. */ -#define DONT_POISON_SPRINTF_YET - -#include "utils_lua.h" #include "common.h" +#include "utils_lua.h" static int ltoc_values(lua_State *L, /* {{{ */ const data_set_t *ds, value_t *ret_values) { @@ -57,8 +53,8 @@ static int ltoc_values(lua_State *L, /* {{{ */ } /* while (lua_next) */ if (i != ds->ds_num) { - WARNING("ltoc_values: invalid size for datasource \"%s\": expected %zu, " - "got %zu", + WARNING("ltoc_values: invalid size for datasource \"%s\": expected %" PRIsz + ", got %" PRIsz, ds->type, ds->ds_num, i); return -1; } diff --git a/src/utils_lua.h b/src/utils_lua.h index 61d9070e..e5a3d746 100644 --- a/src/utils_lua.h +++ b/src/utils_lua.h @@ -27,12 +27,9 @@ #ifndef UTILS_LUA_H #define UTILS_LUA_H 1 -#include "plugin.h" #include "collectd.h" +#include "plugin.h" -#ifndef DONT_POISON_SPRINTF_YET -#error "Files including utils_lua.h need to define DONT_POISON_SPRINTF_YET." -#endif #include /* diff --git a/src/utils_mount.c b/src/utils_mount.c index 49040aa6..279f8e2f 100644 --- a/src/utils_mount.c +++ b/src/utils_mount.c @@ -361,9 +361,7 @@ static cu_mount_t *cu_mount_listmntent(void) { struct tabmntent *mntlist; if (listmntent(&mntlist, COLLECTD_MNTTAB, NULL, NULL) < 0) { #if COLLECT_DEBUG - char errbuf[1024]; - DEBUG("utils_mount: calling listmntent() failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + DEBUG("utils_mount: calling listmntent() failed: %s", STRERRNO); #endif /* COLLECT_DEBUG */ } @@ -431,9 +429,7 @@ static cu_mount_t *cu_mount_getfsstat(void) { /* Get the number of mounted file systems */ if ((bufsize = CMD_STATFS(NULL, 0, FLAGS_STATFS)) < 1) { #if COLLECT_DEBUG - char errbuf[1024]; - DEBUG("utils_mount: getv?fsstat failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + DEBUG("utils_mount: getv?fsstat failed: %s", STRERRNO); #endif /* COLLECT_DEBUG */ return NULL; } @@ -446,9 +442,7 @@ static cu_mount_t *cu_mount_getfsstat(void) { if ((num = CMD_STATFS(buf, bufsize * sizeof(STRUCT_STATFS), FLAGS_STATFS)) < 1) { #if COLLECT_DEBUG - char errbuf[1024]; - DEBUG("utils_mount: getv?fsstat failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + DEBUG("utils_mount: getv?fsstat failed: %s", STRERRNO); #endif /* COLLECT_DEBUG */ free(buf); return NULL; @@ -495,9 +489,7 @@ static cu_mount_t *cu_mount_gen_getmntent(void) { DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB); if ((fp = fopen(COLLECTD_MNTTAB, "r")) == NULL) { - char errbuf[1024]; - ERROR("fopen (%s): %s", COLLECTD_MNTTAB, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("fopen (%s): %s", COLLECTD_MNTTAB, STRERRNO); return NULL; } @@ -527,7 +519,6 @@ static cu_mount_t *cu_mount_gen_getmntent(void) { return first; } /* static cu_mount_t *cu_mount_gen_getmntent (void) */ - /* #endif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT */ #elif HAVE_SEQ_GETMNTENT #warn "This version of `getmntent' hat not yet been implemented!" @@ -546,9 +537,7 @@ static cu_mount_t *cu_mount_getmntent(void) { DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB); if ((fp = setmntent(COLLECTD_MNTTAB, "r")) == NULL) { - char errbuf[1024]; - ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, STRERRNO); return NULL; } @@ -597,9 +586,7 @@ static cu_mount_t *cu_mount_getmntent(void) { DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB); if ((fp = setmntent(COLLECTD_MNTTAB, "r")) == NULL) { - char errbuf[1024]; - ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, STRERRNO); return NULL; } @@ -703,7 +690,7 @@ void cu_mount_freelist(cu_mount_t *list) { char *cu_mount_checkoption(char *line, const char *keyword, int full) { char *line2, *l2, *p1, *p2; - int l; + size_t l; if (line == NULL || keyword == NULL) { return NULL; diff --git a/src/utils_oauth.c b/src/utils_oauth.c new file mode 100644 index 00000000..d804b51a --- /dev/null +++ b/src/utils_oauth.c @@ -0,0 +1,637 @@ +/** + * 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 + **/ + +#include "collectd.h" + +#include "common.h" +#include "plugin.h" +#include "utils_oauth.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* + * 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 */ diff --git a/src/utils_oauth.h b/src/utils_oauth.h new file mode 100644 index 00000000..b93c87b8 --- /dev/null +++ b/src/utils_oauth.h @@ -0,0 +1,66 @@ +/** + * 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 + **/ + +#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 diff --git a/src/utils_oauth_test.c b/src/utils_oauth_test.c new file mode 100644 index 00000000..791564fb --- /dev/null +++ b/src/utils_oauth_test.c @@ -0,0 +1,149 @@ +/** + * 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 + **/ + +#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 */ diff --git a/src/utils_ovs.c b/src/utils_ovs.c index 4f487550..6fe6fe76 100644 --- a/src/utils_ovs.c +++ b/src/utils_ovs.c @@ -191,7 +191,7 @@ struct ovs_db_s { }; /* Global variables */ -static uint64_t ovs_uid = 0; +static uint64_t ovs_uid; static pthread_mutex_t ovs_uid_mutex = PTHREAD_MUTEX_INITIALIZER; /* Post an event to event thread. @@ -209,7 +209,7 @@ static void ovs_db_event_post(ovs_db_t *pdb, int event) { /* Check if POLL thread is still running. Returns * 1 if running otherwise 0 is returned */ -static _Bool ovs_db_poll_is_running(ovs_db_t *pdb) { +static bool ovs_db_poll_is_running(ovs_db_t *pdb) { int state = 0; pthread_mutex_lock(&pdb->poll_thread.mutex); state = pdb->poll_thread.state; @@ -560,7 +560,7 @@ static int ovs_db_json_data_process(ovs_db_t *pdb, const char *data, return -1; sstrncpy(sjson, data, len + 1); - OVS_DEBUG("[len=%zu] %s", len, sjson); + OVS_DEBUG("[len=%" PRIsz "] %s", len, sjson); /* parse json data */ jnode = yajl_tree_parse(sjson, yajl_errbuf, sizeof(yajl_errbuf)); @@ -609,9 +609,8 @@ static int ovs_db_json_data_process(ovs_db_t *pdb, const char *data, /* Allocate JSON reader instance */ static ovs_json_reader_t *ovs_json_reader_alloc() { - ovs_json_reader_t *jreader = NULL; - - if ((jreader = calloc(sizeof(ovs_json_reader_t), 1)) == NULL) + ovs_json_reader_t *jreader = calloc(1, sizeof(*jreader)); + if (jreader == NULL) return NULL; return jreader; @@ -720,13 +719,17 @@ static void ovs_db_reconnect(ovs_db_t *pdb) { if (pdb->unix_path[0] != '\0') { /* use UNIX socket instead of INET address */ node_info = pdb->unix_path; - result = calloc(1, sizeof(struct addrinfo)); - struct sockaddr_un *sa_unix = calloc(1, sizeof(struct sockaddr_un)); - if (result == NULL || sa_unix == NULL) { - sfree(result); - sfree(sa_unix); + + struct sockaddr_un *sa_unix = calloc(1, sizeof(*sa_unix)); + if (sa_unix == NULL) + return; + + result = calloc(1, sizeof(*result)); + if (result == NULL) { + free(sa_unix); return; } + result->ai_family = AF_UNIX; result->ai_socktype = SOCK_STREAM; result->ai_addrlen = sizeof(*sa_unix); @@ -751,17 +754,14 @@ static void ovs_db_reconnect(ovs_db_t *pdb) { } /* try to connect to the server */ for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) { - char errbuff[OVS_ERROR_BUFF_SIZE]; int sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sock < 0) { - sstrerror(errno, errbuff, sizeof(errbuff)); - OVS_DEBUG("socket(): %s", errbuff); + OVS_DEBUG("socket(): %s", STRERRNO); continue; } if (connect(sock, rp->ai_addr, rp->ai_addrlen) < 0) { close(sock); - sstrerror(errno, errbuff, sizeof(errbuff)); - OVS_DEBUG("connect(): %s [family=%d]", errbuff, rp->ai_family); + OVS_DEBUG("connect(): %s [family=%d]", STRERRNO, rp->ai_family); } else { /* send notification to event thread */ pdb->sock = sock; @@ -796,12 +796,10 @@ static void *ovs_poll_worker(void *arg) { /* poll data */ while (ovs_db_poll_is_running(pdb)) { - char errbuff[OVS_ERROR_BUFF_SIZE]; poll_fd.fd = pdb->sock; int poll_ret = poll(&poll_fd, 1, /* ms */ OVS_DB_POLL_TIMEOUT * 1000); if (poll_ret < 0) { - sstrerror(errno, errbuff, sizeof(errbuff)); - OVS_ERROR("poll(): %s", errbuff); + OVS_ERROR("poll(): %s", STRERRNO); break; } else if (poll_ret == 0) { OVS_DEBUG("poll(): timeout"); @@ -827,8 +825,7 @@ static void *ovs_poll_worker(void *arg) { char buff[OVS_DB_POLL_READ_BLOCK_SIZE]; ssize_t nbytes = recv(poll_fd.fd, buff, sizeof(buff), 0); if (nbytes < 0) { - sstrerror(errno, errbuff, sizeof(errbuff)); - OVS_ERROR("recv(): %s", errbuff); + OVS_ERROR("recv(): %s", STRERRNO); /* read error? Try to reconnect */ close(poll_fd.fd); continue; @@ -1007,6 +1004,8 @@ static int ovs_db_poll_thread_destroy(ovs_db_t *pdb) { ovs_db_t *ovs_db_init(const char *node, const char *service, const char *unix_path, ovs_db_callback_t *cb) { + int ret; + /* sanity check */ if (node == NULL || service == NULL || unix_path == NULL) return NULL; @@ -1052,16 +1051,29 @@ ovs_db_t *ovs_db_init(const char *node, const char *service, /* init event thread */ if (ovs_db_event_thread_init(pdb) < 0) { - ovs_db_destroy(pdb); - return NULL; + ret = ovs_db_destroy(pdb); + if (ret > 0) + goto failure; + else + return NULL; } /* init polling thread */ if (ovs_db_poll_thread_init(pdb) < 0) { - ovs_db_destroy(pdb); - return NULL; + ret = ovs_db_destroy(pdb); + if (ret > 0) { + ovs_db_event_thread_data_destroy(pdb); + goto failure; + } else { + return NULL; + } } return pdb; + +failure: + pthread_mutex_destroy(&pdb->mutex); + sfree(pdb); + return NULL; } int ovs_db_send_request(ovs_db_t *pdb, const char *method, const char *params, @@ -1112,7 +1124,7 @@ int ovs_db_send_request(ovs_db_t *pdb, const char *method, const char *params, if (cb) { /* register result callback */ - if ((new_cb = calloc(1, sizeof(ovs_callback_t))) == NULL) + if ((new_cb = calloc(1, sizeof(*new_cb))) == NULL) goto yajl_gen_failure; /* add new callback to front */ @@ -1170,7 +1182,7 @@ int ovs_db_table_cb_register(ovs_db_t *pdb, const char *tb_name, return -1; /* allocate new update callback */ - if ((new_cb = calloc(1, sizeof(ovs_callback_t))) == NULL) + if ((new_cb = calloc(1, sizeof(*new_cb))) == NULL) return -1; /* init YAJL generator */ @@ -1271,7 +1283,7 @@ int ovs_db_destroy(ovs_db_t *pdb) { /* try to lock the structure before releasing */ if ((ret = pthread_mutex_lock(&pdb->mutex))) { OVS_ERROR("pthread_mutex_lock() DB mutex lock failed (%d)", ret); - return -1; + return ret; } /* stop poll thread and destroy thread's private data */ @@ -1370,15 +1382,18 @@ yajl_val ovs_utils_get_map_value(yajl_val jval, const char *key) { /* check first element of the array */ str_val = YAJL_GET_STRING(array_values[0]); - if (strcmp("map", str_val) != 0) + if (str_val == NULL || strcmp("map", str_val) != 0) return NULL; /* try to find map value by map key */ + if (YAJL_GET_ARRAY(array_values[1]) == NULL) + return NULL; + map_len = YAJL_GET_ARRAY(array_values[1])->len; map_values = YAJL_GET_ARRAY(array_values[1])->values; for (size_t i = 0; i < map_len; i++) { /* check YAJL array */ - if (!YAJL_IS_ARRAY(map_values[i])) + if (!YAJL_IS_ARRAY(map_values[i]) || YAJL_GET_ARRAY(map_values[i]) == NULL) break; /* check a database pair value (2-element, first one represents a key @@ -1390,7 +1405,7 @@ yajl_val ovs_utils_get_map_value(yajl_val jval, const char *key) { /* return map value if given key equals map key */ str_val = YAJL_GET_STRING(array_values[0]); - if (strcmp(key, str_val) == 0) + if (str_val != NULL && strcmp(key, str_val) == 0) return array_values[1]; } return NULL; diff --git a/src/utils_rrdcreate.c b/src/utils_rrdcreate.c index 6cb54463..7f1f2354 100644 --- a/src/utils_rrdcreate.c +++ b/src/utils_rrdcreate.c @@ -61,7 +61,7 @@ static int rra_types_num = STATIC_ARRAY_SIZE(rra_types); static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER; #endif -static async_create_file_t *async_creation_list = NULL; +static async_create_file_t *async_creation_list; static pthread_mutex_t async_creation_lock = PTHREAD_MUTEX_INITIALIZER; /* @@ -96,7 +96,7 @@ static srrd_create_args_t *srrd_create_args_create(const char *filename, args = calloc(1, sizeof(*args)); if (args == NULL) { - ERROR("srrd_create_args_create: calloc failed."); + P_ERROR("srrd_create_args_create: calloc failed."); return NULL; } args->filename = NULL; @@ -106,14 +106,14 @@ static srrd_create_args_t *srrd_create_args_create(const char *filename, args->filename = strdup(filename); if (args->filename == NULL) { - ERROR("srrd_create_args_create: strdup failed."); + P_ERROR("srrd_create_args_create: strdup failed."); srrd_create_args_destroy(args); return NULL; } - args->argv = calloc((size_t)(argc + 1), sizeof(*args->argv)); + args->argv = calloc(argc + 1, sizeof(*args->argv)); if (args->argv == NULL) { - ERROR("srrd_create_args_create: calloc failed."); + P_ERROR("srrd_create_args_create: calloc failed."); srrd_create_args_destroy(args); return NULL; } @@ -121,7 +121,7 @@ static srrd_create_args_t *srrd_create_args_create(const char *filename, for (args->argc = 0; args->argc < argc; args->argc++) { args->argv[args->argc] = strdup(argv[args->argc]); if (args->argv[args->argc] == NULL) { - ERROR("srrd_create_args_create: strdup failed."); + P_ERROR("srrd_create_args_create: strdup failed."); srrd_create_args_destroy(args); return NULL; } @@ -212,7 +212,7 @@ static int rra_get(char ***ret, const value_list_t *vl, /* {{{ */ rra_types[j], cfg->xff, cdp_len, cdp_num); if ((status < 0) || ((size_t)status >= sizeof(buffer))) { - ERROR("rra_get: Buffer would have been truncated."); + P_ERROR("rra_get: Buffer would have been truncated."); continue; } @@ -251,9 +251,7 @@ static int ds_get(char ***ret, /* {{{ */ ds_def = calloc(ds->ds_num, sizeof(*ds_def)); if (ds_def == NULL) { - char errbuf[1024]; - ERROR("rrdtool plugin: calloc failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + P_ERROR("ds_get: calloc failed: %s", STRERRNO); return -1; } @@ -273,7 +271,7 @@ static int ds_get(char ***ret, /* {{{ */ else if (d->type == DS_TYPE_ABSOLUTE) type = "ABSOLUTE"; else { - ERROR("rrdtool plugin: Unknown DS type: %i", d->type); + P_ERROR("ds_get: Unknown DS type: %i", d->type); break; } @@ -337,8 +335,8 @@ static int srrd_create(const char *filename, /* {{{ */ status = rrd_create_r(filename_copy, pdp_step, last_up, argc, (void *)argv); if (status != 0) { - WARNING("rrdtool plugin: rrd_create_r (%s) failed: %s", filename, - rrd_get_error()); + P_WARNING("srrd_create: rrd_create_r (%s) failed: %s", filename, + rrd_get_error()); } sfree(filename_copy); @@ -362,7 +360,7 @@ static int srrd_create(const char *filename, /* {{{ */ new_argc = 6 + argc; new_argv = malloc((new_argc + 1) * sizeof(*new_argv)); if (new_argv == NULL) { - ERROR("rrdtool plugin: malloc failed."); + P_ERROR("srrd_create: malloc failed."); return -1; } @@ -390,8 +388,8 @@ static int srrd_create(const char *filename, /* {{{ */ pthread_mutex_unlock(&librrd_lock); if (status != 0) { - WARNING("rrdtool plugin: rrd_create (%s) failed: %s", filename, - rrd_get_error()); + P_WARNING("srrd_create: rrd_create (%s) failed: %s", filename, + rrd_get_error()); } sfree(new_argv); @@ -489,10 +487,11 @@ static void *srrd_create_thread(void *targs) /* {{{ */ status = lock_file(args->filename); if (status != 0) { if (status == EEXIST) - NOTICE("srrd_create_thread: File \"%s\" is already being created.", - args->filename); + P_NOTICE("srrd_create_thread: File \"%s\" is already being created.", + args->filename); else - ERROR("srrd_create_thread: Unable to lock file \"%s\".", args->filename); + P_ERROR("srrd_create_thread: Unable to lock file \"%s\".", + args->filename); srrd_create_args_destroy(args); return 0; } @@ -502,8 +501,8 @@ static void *srrd_create_thread(void *targs) /* {{{ */ status = srrd_create(tmpfile, args->pdp_step, args->last_up, args->argc, (void *)args->argv); if (status != 0) { - WARNING("srrd_create_thread: srrd_create (%s) returned status %i.", - args->filename, status); + P_WARNING("srrd_create_thread: srrd_create (%s) returned status %i.", + args->filename, status); unlink(tmpfile); unlock_file(args->filename); srrd_create_args_destroy(args); @@ -512,9 +511,8 @@ static void *srrd_create_thread(void *targs) /* {{{ */ status = rename(tmpfile, args->filename); if (status != 0) { - char errbuf[1024]; - ERROR("srrd_create_thread: rename (\"%s\", \"%s\") failed: %s", tmpfile, - args->filename, sstrerror(errno, errbuf, sizeof(errbuf))); + P_ERROR("srrd_create_thread: rename (\"%s\", \"%s\") failed: %s", tmpfile, + args->filename, STRERRNO); unlink(tmpfile); unlock_file(args->filename); srrd_create_args_destroy(args); @@ -559,9 +557,7 @@ static int srrd_create_async(const char *filename, /* {{{ */ status = pthread_create(&thread, &attr, srrd_create_thread, args); if (status != 0) { - char errbuf[1024]; - ERROR("srrd_create_async: pthread_create failed: %s", - sstrerror(status, errbuf, sizeof(errbuf))); + P_ERROR("srrd_create_async: pthread_create failed: %s", STRERROR(status)); pthread_attr_destroy(&attr); srrd_create_args_destroy(args); return status; @@ -592,12 +588,12 @@ int cu_rrd_create_file(const char *filename, /* {{{ */ return -1; if ((rra_num = rra_get(&rra_def, vl, cfg)) < 1) { - ERROR("cu_rrd_create_file failed: Could not calculate RRAs"); + P_ERROR("cu_rrd_create_file failed: Could not calculate RRAs"); return -1; } if ((ds_num = ds_get(&ds_def, ds, vl, cfg)) < 1) { - ERROR("cu_rrd_create_file failed: Could not calculate DSes"); + P_ERROR("cu_rrd_create_file failed: Could not calculate DSes"); rra_free(rra_num, rra_def); return -1; } @@ -605,9 +601,7 @@ int cu_rrd_create_file(const char *filename, /* {{{ */ argc = ds_num + rra_num; if ((argv = malloc(sizeof(*argv) * (argc + 1))) == NULL) { - char errbuf[1024]; - ERROR("cu_rrd_create_file failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + P_ERROR("cu_rrd_create_file failed: %s", STRERRNO); rra_free(rra_num, rra_def); ds_free(ds_num, ds_def); return -1; @@ -631,25 +625,25 @@ int cu_rrd_create_file(const char *filename, /* {{{ */ status = srrd_create_async(filename, stepsize, last_up, argc, (const char **)argv); if (status != 0) - WARNING("cu_rrd_create_file: srrd_create_async (%s) " - "returned status %i.", - filename, status); + P_WARNING("cu_rrd_create_file: srrd_create_async (%s) " + "returned status %i.", + filename, status); } else /* synchronous */ { status = lock_file(filename); if (status != 0) { if (status == EEXIST) - NOTICE("cu_rrd_create_file: File \"%s\" is already being created.", - filename); + P_NOTICE("cu_rrd_create_file: File \"%s\" is already being created.", + filename); else - ERROR("cu_rrd_create_file: Unable to lock file \"%s\".", filename); + P_ERROR("cu_rrd_create_file: Unable to lock file \"%s\".", filename); } else { status = srrd_create(filename, stepsize, last_up, argc, (const char **)argv); if (status != 0) { - WARNING("cu_rrd_create_file: srrd_create (%s) returned status %i.", - filename, status); + P_WARNING("cu_rrd_create_file: srrd_create (%s) returned status %i.", + filename, status); } else { DEBUG("cu_rrd_create_file: Successfully created RRD file \"%s\".", filename); diff --git a/src/utils_rrdcreate.h b/src/utils_rrdcreate.h index d5f9a12a..b2277e75 100644 --- a/src/utils_rrdcreate.h +++ b/src/utils_rrdcreate.h @@ -43,7 +43,7 @@ struct rrdcreate_config_s { char **consolidation_functions; size_t consolidation_functions_num; - _Bool async; + bool async; }; typedef struct rrdcreate_config_s rrdcreate_config_t; diff --git a/src/utils_tail.c b/src/utils_tail.c index cdab6a77..365bf558 100644 --- a/src/utils_tail.c +++ b/src/utils_tail.c @@ -43,15 +43,11 @@ struct cu_tail_s { 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) { - char errbuf[1024]; - ERROR("utils_tail: stat (%s) failed: %s", obj->file, - sstrerror(errno, errbuf, sizeof(errbuf))); + P_ERROR("utils_tail: stat (%s) failed: %s", obj->file, STRERRNO); return -1; } @@ -59,12 +55,10 @@ static int cu_tail_reopen(cu_tail_t *obj) { 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) { - char errbuf[1024]; - ERROR("utils_tail: fseek (%s) failed: %s", obj->file, - sstrerror(errno, errbuf, sizeof(errbuf))); + P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO); fclose(obj->fh); obj->fh = NULL; return -1; @@ -79,20 +73,16 @@ static int cu_tail_reopen(cu_tail_t *obj) { 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) { - char errbuf[1024]; - ERROR("utils_tail: fopen (%s) failed: %s", obj->file, - sstrerror(errno, errbuf, sizeof(errbuf))); + 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) { - char errbuf[1024]; - ERROR("utils_tail: fseek (%s) failed: %s", obj->file, - sstrerror(errno, errbuf, sizeof(errbuf))); + P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO); fclose(fh); return -1; } @@ -152,7 +142,7 @@ int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) { * be fine and we can return. */ clearerr(obj->fh); if (fgets(buf, buflen, obj->fh) != NULL) { - buf[buflen - 1] = 0; + buf[buflen - 1] = '\0'; return 0; } @@ -178,14 +168,13 @@ int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) { /* If we get here: file was re-opened and there may be more to read.. Let's * try again. */ if (fgets(buf, buflen, obj->fh) != NULL) { - buf[buflen - 1] = 0; + buf[buflen - 1] = '\0'; return 0; } if (ferror(obj->fh) != 0) { - char errbuf[1024]; WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file, - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); fclose(obj->fh); obj->fh = NULL; return -1; diff --git a/src/utils_tail_match.c b/src/utils_tail_match.c index 5134a6e1..ccab5ace 100644 --- a/src/utils_tail_match.c +++ b/src/utils_tail_match.c @@ -43,7 +43,6 @@ struct cu_tail_match_simple_s { char plugin_instance[DATA_MAX_NAME_LEN]; char type[DATA_MAX_NAME_LEN]; char type_instance[DATA_MAX_NAME_LEN]; - cdtime_t interval; latency_config_t latency_config; }; typedef struct cu_tail_match_simple_s cu_tail_match_simple_t; @@ -57,10 +56,7 @@ struct cu_tail_match_match_s { typedef struct cu_tail_match_match_s cu_tail_match_match_t; struct cu_tail_match_s { - int flags; cu_tail_t *tail; - - cdtime_t interval; cu_tail_match_match_t *matches; size_t matches_num; }; @@ -92,7 +88,6 @@ static int simple_submit_match(cu_match_t *match, void *user_data) { sstrncpy(vl.type, data->type, sizeof(vl.type)); sstrncpy(vl.type_instance, data->type_instance, sizeof(vl.type_instance)); - vl.interval = data->interval; plugin_dispatch_values(&vl); match_value_reset(match_value); @@ -111,7 +106,6 @@ static int latency_submit_match(cu_match_t *match, void *user_data) { sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin)); sstrncpy(vl.plugin_instance, data->plugin_instance, sizeof(vl.plugin_instance)); - vl.interval = data->interval; vl.time = cdtime(); /* Submit percentiles */ @@ -248,8 +242,6 @@ int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match, obj->matches = temp; obj->matches_num++; - DEBUG("tail_match_add_match interval %lf", - CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval)); temp = obj->matches + (obj->matches_num - 1); temp->match = match; @@ -264,8 +256,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, const char *excluderegex, int ds_type, const char *plugin, const char *plugin_instance, const char *type, const char *type_instance, - const latency_config_t latency_cfg, - const cdtime_t interval) { + const latency_config_t latency_cfg) { cu_match_t *match; cu_tail_match_simple_t *user_data; int status; @@ -290,8 +281,6 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, sstrncpy(user_data->type_instance, type_instance, sizeof(user_data->type_instance)); - user_data->interval = interval; - if ((ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && (ds_type & UTILS_MATCH_CF_GAUGE_DIST)) { status = latency_config_copy(&user_data->latency_config, latency_cfg); diff --git a/src/utils_tail_match.h b/src/utils_tail_match.h index 03b70e93..2d4c2531 100644 --- a/src/utils_tail_match.h +++ b/src/utils_tail_match.h @@ -80,9 +80,8 @@ void tail_match_destroy(cu_tail_match_t *obj); * When `tail_match_destroy' is called the `user_data' pointer is freed using * the `free_user_data' callback - if it is not NULL. * When using this interface the `tail_match' module doesn't dispatch any - * values - * itself - all that has to happen in either the match-callbacks or the - * submit_match callback. + * values itself - all that has to happen in either the match-callbacks or + * the submit_match callback. * * RETURN VALUE * Zero upon success, non-zero otherwise. @@ -98,14 +97,13 @@ int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match, * tail_match_add_match_simple * * DESCRIPTION - * A simplified version of `tail_match_add_match'. The regular expressen - * `regex' - * must match a number, which is then dispatched according to `ds_type'. See - * the `match_create_simple' function in utils_match.h for a description how - * this flag effects calculation of a new value. + * A simplified version of `tail_match_add_match'. The regular expression + * `regex' must match a number, which is then dispatched according to + * `ds_type'. + * See the `match_create_simple' function in utils_match.h for a description + * how this flag effects calculation of a new value. * The values gathered are dispatched by the tail_match module in this case. - * The - * passed `plugin', `plugin_instance', `type', and `type_instance' are + * The passed `plugin', `plugin_instance', `type', and `type_instance' are * directly used when submitting these values. * With excluderegex it is possible to exlude lines from the match. * The `latency_cfg' specifies configuration for submitting latency. @@ -117,8 +115,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, const char *excluderegex, int ds_type, const char *plugin, const char *plugin_instance, const char *type, const char *type_instance, - const latency_config_t latency_cfg, - const cdtime_t interval); + const latency_config_t latency_cfg); /* * NAME @@ -130,8 +127,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, * added `utils_match' objects. * After all lines have been read and processed, the submit_match callback is * called or, in case of tail_match_add_match_simple, the data is dispatched - * to - * the daemon directly. + * to the daemon directly. * * RETURN VALUE * Zero on success, nonzero on failure. diff --git a/src/utils_taskstats.c b/src/utils_taskstats.c new file mode 100644 index 00000000..f0d73334 --- /dev/null +++ b/src/utils_taskstats.c @@ -0,0 +1,306 @@ +/** + * collectd - src/utils_taskstats.c + * Copyright (C) 2017 Florian octo Forster + * + * ISC License (ISC) + * + * 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 octo Forster + */ + +#include "collectd.h" +#include "utils_taskstats.h" + +#include "common.h" +#include "plugin.h" +#include "utils_time.h" + +#include +#include +#include + +struct ts_s { + struct mnl_socket *nl; + pid_t pid; + uint32_t seq; + uint16_t genl_id_taskstats; + unsigned int port_id; +}; + +/* nlmsg_errno returns the errno encoded in nlh or zero if not an error. */ +static int nlmsg_errno(struct nlmsghdr *nlh, size_t sz) { + if (!mnl_nlmsg_ok(nlh, (int)sz)) { + ERROR("utils_taskstats: mnl_nlmsg_ok failed."); + return EPROTO; + } + + if (nlh->nlmsg_type != NLMSG_ERROR) { + return 0; + } + + struct nlmsgerr *nlerr = mnl_nlmsg_get_payload(nlh); + /* (struct nlmsgerr).error holds a negative errno. */ + return nlerr->error * (-1); +} + +static int get_taskstats_attr_cb(const struct nlattr *attr, void *data) { + struct taskstats *ret_taskstats = data; + + uint16_t type = mnl_attr_get_type(attr); + switch (type) { + case TASKSTATS_TYPE_STATS: + if (mnl_attr_get_payload_len(attr) != sizeof(*ret_taskstats)) { + ERROR("utils_taskstats: mnl_attr_get_payload_len(attr) = %" PRIu32 + ", want %zu", + mnl_attr_get_payload_len(attr), sizeof(*ret_taskstats)); + return MNL_CB_ERROR; + } + struct taskstats *ts = mnl_attr_get_payload(attr); + memmove(ret_taskstats, ts, sizeof(*ret_taskstats)); + return MNL_CB_OK; + + case TASKSTATS_TYPE_AGGR_PID: /* fall through */ + case TASKSTATS_TYPE_AGGR_TGID: + return mnl_attr_parse_nested(attr, get_taskstats_attr_cb, ret_taskstats); + + case TASKSTATS_TYPE_PID: /* fall through */ + case TASKSTATS_TYPE_TGID: + /* ignore */ + return MNL_CB_OK; + + default: + DEBUG("utils_taskstats: unknown attribute %" PRIu16 + ", want one of TASKSTATS_TYPE_AGGR_PID/TGID, TASKSTATS_TYPE_STATS", + type); + } + return MNL_CB_OK; +} + +static int get_taskstats_msg_cb(const struct nlmsghdr *nlh, void *data) { + return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_taskstats_attr_cb, + data); +} + +static int get_taskstats(ts_t *ts, uint32_t tgid, + struct taskstats *ret_taskstats) { + char buffer[MNL_SOCKET_BUFFER_SIZE]; + uint32_t seq = ts->seq++; + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buffer); + *nlh = (struct nlmsghdr){ + .nlmsg_len = nlh->nlmsg_len, + .nlmsg_type = ts->genl_id_taskstats, + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_seq = seq, + .nlmsg_pid = ts->pid, + }; + + struct genlmsghdr *genh = mnl_nlmsg_put_extra_header(nlh, sizeof(*genh)); + *genh = (struct genlmsghdr){ + .cmd = TASKSTATS_CMD_GET, + .version = TASKSTATS_GENL_VERSION, // or TASKSTATS_VERSION? + }; + + // mnl_attr_put_u32(nlh, TASKSTATS_CMD_ATTR_PID, tgid); + mnl_attr_put_u32(nlh, TASKSTATS_CMD_ATTR_TGID, tgid); + + if (mnl_socket_sendto(ts->nl, nlh, nlh->nlmsg_len) < 0) { + int status = errno; + ERROR("utils_taskstats: mnl_socket_sendto() = %s", STRERROR(status)); + return status; + } + + int status = mnl_socket_recvfrom(ts->nl, buffer, sizeof(buffer)); + if (status < 0) { + status = errno; + ERROR("utils_taskstats: mnl_socket_recvfrom() = %s", STRERROR(status)); + return status; + } else if (status == 0) { + ERROR("utils_taskstats: mnl_socket_recvfrom() = 0"); + return ECONNABORTED; + } + size_t buffer_size = (size_t)status; + + if ((status = nlmsg_errno((void *)buffer, buffer_size)) != 0) { + ERROR("utils_taskstats: TASKSTATS_CMD_GET(TASKSTATS_CMD_ATTR_TGID = " + "%" PRIu32 ") = %s", + (uint32_t)tgid, STRERROR(status)); + return status; + } + + status = mnl_cb_run(buffer, buffer_size, seq, ts->port_id, + get_taskstats_msg_cb, ret_taskstats); + if (status < MNL_CB_STOP) { + ERROR("utils_taskstats: Parsing message failed."); + return EPROTO; + } + + return 0; +} + +static int get_family_id_attr_cb(const struct nlattr *attr, void *data) { + uint16_t type = mnl_attr_get_type(attr); + if (type != CTRL_ATTR_FAMILY_ID) { + return MNL_CB_OK; + } + + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) { + ERROR("mnl_attr_validate() = %s", STRERRNO); + return MNL_CB_ERROR; + } + + uint16_t *ret_family_id = data; + *ret_family_id = mnl_attr_get_u16(attr); + return MNL_CB_STOP; +} + +static int get_family_id_msg_cb(const struct nlmsghdr *nlh, void *data) { + return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb, + data); +} + +/* get_family_id initializes ts->genl_id_taskstats. Returns 0 on success and + * an error code otherwise. */ +static int get_family_id(ts_t *ts) { + char buffer[MNL_SOCKET_BUFFER_SIZE]; + uint32_t seq = ts->seq++; + + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buffer); + *nlh = (struct nlmsghdr){ + .nlmsg_len = nlh->nlmsg_len, + .nlmsg_type = GENL_ID_CTRL, + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_seq = seq, + .nlmsg_pid = ts->pid, + }; + + struct genlmsghdr *genh = mnl_nlmsg_put_extra_header(nlh, sizeof(*genh)); + *genh = (struct genlmsghdr){ + .cmd = CTRL_CMD_GETFAMILY, .version = 0x01, + }; + + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, TASKSTATS_GENL_NAME); + + assert(genh->cmd == CTRL_CMD_GETFAMILY); + assert(genh->version == TASKSTATS_GENL_VERSION); + + if (mnl_socket_sendto(ts->nl, nlh, nlh->nlmsg_len) < 0) { + int status = errno; + ERROR("utils_taskstats: mnl_socket_sendto() = %s", STRERROR(status)); + return status; + } + + ts->genl_id_taskstats = 0; + while (42) { + int status = mnl_socket_recvfrom(ts->nl, buffer, sizeof(buffer)); + if (status < 0) { + status = errno; + ERROR("utils_taskstats: mnl_socket_recvfrom() = %s", STRERROR(status)); + return status; + } else if (status == 0) { + break; + } + size_t buffer_size = (size_t)status; + + if ((status = nlmsg_errno((void *)buffer, buffer_size)) != 0) { + ERROR("utils_taskstats: CTRL_CMD_GETFAMILY(\"%s\"): %s", + TASKSTATS_GENL_NAME, STRERROR(status)); + return status; + } + + status = mnl_cb_run(buffer, buffer_size, seq, ts->port_id, + get_family_id_msg_cb, &ts->genl_id_taskstats); + if (status < MNL_CB_STOP) { + ERROR("utils_taskstats: Parsing message failed."); + return EPROTO; + } else if (status == MNL_CB_STOP) { + break; + } + } + + if (ts->genl_id_taskstats == 0) { + ERROR("utils_taskstats: Netlink communication succeeded, but " + "genl_id_taskstats is still zero."); + return ENOENT; + } + + return 0; +} + +void ts_destroy(ts_t *ts) { + if (ts == NULL) { + return; + } + + if (ts->nl != NULL) { + mnl_socket_close(ts->nl); + ts->nl = NULL; + } + + sfree(ts); +} + +ts_t *ts_create(void) { + ts_t *ts = calloc(1, sizeof(*ts)); + if (ts == NULL) { + ERROR("utils_taskstats: calloc failed: %s", STRERRNO); + return NULL; + } + + if ((ts->nl = mnl_socket_open(NETLINK_GENERIC)) == NULL) { + ERROR("utils_taskstats: mnl_socket_open(NETLINK_GENERIC) = %s", STRERRNO); + ts_destroy(ts); + return NULL; + } + + if (mnl_socket_bind(ts->nl, 0, MNL_SOCKET_AUTOPID) != 0) { + ERROR("utils_taskstats: mnl_socket_bind() = %s", STRERRNO); + ts_destroy(ts); + return NULL; + } + + ts->pid = getpid(); + ts->port_id = mnl_socket_get_portid(ts->nl); + + int status = get_family_id(ts); + if (status != 0) { + ERROR("utils_taskstats: get_family_id() = %s", STRERROR(status)); + ts_destroy(ts); + return NULL; + } + + return ts; +} + +int ts_delay_by_tgid(ts_t *ts, uint32_t tgid, ts_delay_t *out) { + if ((ts == NULL) || (out == NULL)) { + return EINVAL; + } + + struct taskstats raw = {0}; + + int status = get_taskstats(ts, tgid, &raw); + if (status != 0) { + return status; + } + + *out = (ts_delay_t){ + .cpu_ns = raw.cpu_delay_total, + .blkio_ns = raw.blkio_delay_total, + .swapin_ns = raw.swapin_delay_total, + .freepages_ns = raw.freepages_delay_total, + }; + return 0; +} diff --git a/src/utils_taskstats.h b/src/utils_taskstats.h new file mode 100644 index 00000000..de07427c --- /dev/null +++ b/src/utils_taskstats.h @@ -0,0 +1,47 @@ +/** + * collectd - src/utils_taskstats.h + * Copyright (C) 2017 Florian octo Forster + * + * ISC License (ISC) + * + * 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 octo Forster + */ + +#ifndef UTILS_TASKSTATS_H +#define UTILS_TASKSTATS_H 1 + +#include "collectd.h" + +#include "utils_time.h" + +struct ts_s; +typedef struct ts_s ts_t; + +typedef struct { + uint64_t cpu_ns; + uint64_t blkio_ns; + uint64_t swapin_ns; + uint64_t freepages_ns; +} ts_delay_t; + +ts_t *ts_create(void); +void ts_destroy(ts_t *); + +/* ts_delay_by_tgid returns Linux delay accounting information for the task + * identified by tgid. Returns zero on success and an errno otherwise. */ +int ts_delay_by_tgid(ts_t *ts, uint32_t tgid, ts_delay_t *out); + +#endif /* UTILS_TASKSTATS_H */ diff --git a/src/utils_vl_lookup.c b/src/utils_vl_lookup.c index 052c4c0f..03e61f80 100644 --- a/src/utils_vl_lookup.c +++ b/src/utils_vl_lookup.c @@ -57,7 +57,7 @@ kstat_ctl_t *kc; struct part_match_s { char str[DATA_MAX_NAME_LEN]; regex_t regex; - _Bool is_regex; + bool is_regex; }; typedef struct part_match_s part_match_t; @@ -114,25 +114,25 @@ typedef struct by_type_entry_s by_type_entry_t; /* * Private functions */ -static _Bool lu_part_matches(part_match_t const *match, /* {{{ */ - char const *str) { +static bool lu_part_matches(part_match_t const *match, /* {{{ */ + char const *str) { if (match->is_regex) { /* Short cut popular catch-all regex. */ if (strcmp(".*", match->str) == 0) - return 1; + return true; int status = regexec(&match->regex, str, /* nmatch = */ 0, /* pmatch = */ NULL, /* flags = */ 0); if (status == 0) - return 1; + return true; else - return 0; + return false; } else if (strcmp(match->str, str) == 0) - return 1; + return true; else - return 0; -} /* }}} _Bool lu_part_matches */ + return false; +} /* }}} bool lu_part_matches */ static int lu_copy_ident_to_match_part(part_match_t *match_part, /* {{{ */ char const *ident_part) { @@ -141,7 +141,7 @@ static int lu_copy_ident_to_match_part(part_match_t *match_part, /* {{{ */ if ((len < 3) || (ident_part[0] != '/') || (ident_part[len - 1] != '/')) { sstrncpy(match_part->str, ident_part, sizeof(match_part->str)); - match_part->is_regex = 0; + match_part->is_regex = false; return 0; } @@ -160,7 +160,7 @@ static int lu_copy_ident_to_match_part(part_match_t *match_part, /* {{{ */ match_part->str, errbuf); return EINVAL; } - match_part->is_regex = 1; + match_part->is_regex = true; return 0; } /* }}} int lu_copy_ident_to_match_part */ @@ -335,7 +335,7 @@ static int lu_handle_user_class_list(lookup_t *obj, /* {{{ */ static by_type_entry_t *lu_search_by_type(lookup_t *obj, /* {{{ */ char const *type, - _Bool allocate_if_missing) { + bool allocate_if_missing) { by_type_entry_t *by_type; char *type_copy; int status; @@ -467,7 +467,7 @@ static void lu_destroy_user_class_list(lookup_t *obj, /* {{{ */ do { \ if (user_class_list->entry.match.field.is_regex) { \ regfree(&user_class_list->entry.match.field.regex); \ - user_class_list->entry.match.field.is_regex = 0; \ + user_class_list->entry.match.field.is_regex = false; \ } \ } while (0) @@ -576,7 +576,7 @@ int lookup_add(lookup_t *obj, /* {{{ */ by_type_entry_t *by_type = NULL; user_class_list_t *user_class_obj; - by_type = lu_search_by_type(obj, ident->type, /* allocate = */ 1); + by_type = lu_search_by_type(obj, ident->type, /* allocate = */ true); if (by_type == NULL) return -1; @@ -605,7 +605,7 @@ int lookup_search(lookup_t *obj, /* {{{ */ if ((obj == NULL) || (ds == NULL) || (vl == NULL)) return -EINVAL; - by_type = lu_search_by_type(obj, vl->type, /* allocate = */ 0); + by_type = lu_search_by_type(obj, vl->type, /* allocate = */ false); if (by_type == NULL) return 0; diff --git a/src/utils_vl_lookup_test.c b/src/utils_vl_lookup_test.c index 058015ed..1cb7b65b 100644 --- a/src/utils_vl_lookup_test.c +++ b/src/utils_vl_lookup_test.c @@ -29,8 +29,8 @@ #include "testing.h" #include "utils_vl_lookup.h" -static _Bool expect_new_obj = 0; -static _Bool have_new_obj = 0; +static bool expect_new_obj; +static bool have_new_obj; static lookup_identifier_t last_class_ident; static lookup_identifier_t last_obj_ident; @@ -75,7 +75,7 @@ static void *lookup_class_callback(data_set_t const *ds, value_list_t const *vl, strncpy(obj->type, vl->type, sizeof(obj->type)); strncpy(obj->type_instance, vl->type_instance, sizeof(obj->type_instance)); - have_new_obj = 1; + have_new_obj = true; return (void *)obj; } @@ -85,15 +85,15 @@ static int checked_lookup_add(lookup_t *obj, /* {{{ */ char const *plugin_instance, char const *type, char const *type_instance, unsigned int group_by) { - lookup_identifier_t ident; + lookup_identifier_t ident = {{0}}; void *user_class; - strncpy(ident.host, host, sizeof(ident.host)); - strncpy(ident.plugin, plugin, sizeof(ident.plugin)); + strncpy(ident.host, host, sizeof(ident.host) - 1); + strncpy(ident.plugin, plugin, sizeof(ident.plugin) - 1); strncpy(ident.plugin_instance, plugin_instance, - sizeof(ident.plugin_instance)); - strncpy(ident.type, type, sizeof(ident.type)); - strncpy(ident.type_instance, type_instance, sizeof(ident.type_instance)); + sizeof(ident.plugin_instance) - 1); + strncpy(ident.type, type, sizeof(ident.type) - 1); + strncpy(ident.type_instance, type_instance, sizeof(ident.type_instance) - 1); user_class = malloc(sizeof(ident)); memmove(user_class, &ident, sizeof(ident)); @@ -105,22 +105,22 @@ static int checked_lookup_add(lookup_t *obj, /* {{{ */ static int checked_lookup_search(lookup_t *obj, char const *host, char const *plugin, char const *plugin_instance, char const *type, - char const *type_instance, _Bool expect_new) { + char const *type_instance, bool expect_new) { int status; value_list_t vl = VALUE_LIST_INIT; data_set_t const *ds = &ds_unknown; - strncpy(vl.host, host, sizeof(vl.host)); - strncpy(vl.plugin, plugin, sizeof(vl.plugin)); - strncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); - strncpy(vl.type, type, sizeof(vl.type)); - strncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); + strncpy(vl.host, host, sizeof(vl.host) - 1); + strncpy(vl.plugin, plugin, sizeof(vl.plugin) - 1); + strncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance) - 1); + strncpy(vl.type, type, sizeof(vl.type) - 1); + strncpy(vl.type_instance, type_instance, sizeof(vl.type_instance) - 1); if (strcmp(vl.type, "test") == 0) ds = &ds_test; expect_new_obj = expect_new; - have_new_obj = 0; + have_new_obj = false; status = lookup_search(obj, ds, &vl); return status; diff --git a/src/uuid.c b/src/uuid.c index 1cb90270..c7878c74 100644 --- a/src/uuid.c +++ b/src/uuid.c @@ -37,18 +37,15 @@ #define UUID_PRINTABLE_COMPACT_LENGTH (UUID_RAW_LENGTH * 2) #define UUID_PRINTABLE_NORMAL_LENGTH (UUID_PRINTABLE_COMPACT_LENGTH + 4) -static char *uuidfile = NULL; +static char *uuidfile; static const char *config_keys[] = {"UUIDFile"}; static int looks_like_a_uuid(const char *uuid) { - int len; - if (!uuid) return 0; - len = strlen(uuid); - + size_t len = strlen(uuid); if (len < UUID_PRINTABLE_COMPACT_LENGTH) return 0; diff --git a/src/valgrind.suppress b/src/valgrind.suppress new file mode 100644 index 00000000..f4c3f343 --- /dev/null +++ b/src/valgrind.suppress @@ -0,0 +1,7 @@ +{ + libnl1_virt_initialization_unpreventable_leak + Memcheck:Leak + ... + obj:*libnl.so.1.* + ... +} \ No newline at end of file diff --git a/src/varnish.c b/src/varnish.c index 08260dc2..b515be89 100644 --- a/src/varnish.c +++ b/src/varnish.c @@ -50,50 +50,50 @@ typedef struct varnish_stats c_varnish_stats_t; struct user_config_s { char *instance; - _Bool collect_cache; - _Bool collect_connections; - _Bool collect_esi; - _Bool collect_backend; + bool collect_cache; + bool collect_connections; + bool collect_esi; + bool collect_backend; #ifdef HAVE_VARNISH_V3 - _Bool collect_dirdns; + bool collect_dirdns; #endif - _Bool collect_fetch; - _Bool collect_hcb; - _Bool collect_objects; + bool collect_fetch; + bool collect_hcb; + bool collect_objects; #if HAVE_VARNISH_V2 - _Bool collect_purge; + bool collect_purge; #else - _Bool collect_ban; + bool collect_ban; #endif - _Bool collect_session; - _Bool collect_shm; - _Bool collect_sms; + bool collect_session; + bool collect_shm; + bool collect_sms; #if HAVE_VARNISH_V2 - _Bool collect_sm; + bool collect_sm; #endif #if HAVE_VARNISH_V2 || HAVE_VARNISH_V4 || HAVE_VARNISH_V5 - _Bool collect_sma; + bool collect_sma; #endif - _Bool collect_struct; - _Bool collect_totals; + bool collect_struct; + bool collect_totals; #if HAVE_VARNISH_V3 || HAVE_VARNISH_V4 || HAVE_VARNISH_V5 - _Bool collect_uptime; + bool collect_uptime; #endif - _Bool collect_vcl; - _Bool collect_workers; + bool collect_vcl; + bool collect_workers; #if HAVE_VARNISH_V4 || HAVE_VARNISH_V5 - _Bool collect_vsm; - _Bool collect_lck; - _Bool collect_mempool; - _Bool collect_mgt; - _Bool collect_smf; - _Bool collect_vbe; - _Bool collect_mse; + bool collect_vsm; + bool collect_lck; + bool collect_mempool; + bool collect_mgt; + bool collect_smf; + bool collect_vbe; + bool collect_mse; #endif }; typedef struct user_config_s user_config_t; /* }}} */ -static _Bool have_instance = 0; +static bool have_instance; static int varnish_submit(const char *plugin_instance, /* {{{ */ const char *category, const char *type, @@ -1331,7 +1331,7 @@ static int varnish_read(user_data_t *ud) /* {{{ */ { #if HAVE_VARNISH_V3 || HAVE_VARNISH_V4 struct VSM_data *vd; - _Bool ok; + bool ok; const c_varnish_stats_t *stats; #elif HAVE_VARNISH_V5 struct vsm *vd; @@ -1480,45 +1480,45 @@ static int varnish_config_apply_default(user_config_t *conf) /* {{{ */ if (conf == NULL) return EINVAL; - conf->collect_backend = 1; - conf->collect_cache = 1; - conf->collect_connections = 1; + conf->collect_backend = true; + conf->collect_cache = true; + conf->collect_connections = true; #ifdef HAVE_VARNISH_V3 - conf->collect_dirdns = 0; + conf->collect_dirdns = false; #endif - conf->collect_esi = 0; - conf->collect_fetch = 0; - conf->collect_hcb = 0; - conf->collect_objects = 0; + conf->collect_esi = false; + conf->collect_fetch = false; + conf->collect_hcb = false; + conf->collect_objects = false; #if HAVE_VARNISH_V2 - conf->collect_purge = 0; + conf->collect_purge = false; #else - conf->collect_ban = 0; + conf->collect_ban = false; #endif - conf->collect_session = 0; - conf->collect_shm = 1; + conf->collect_session = false; + conf->collect_shm = true; #if HAVE_VARNISH_V2 - conf->collect_sm = 0; + conf->collect_sm = false; #endif #if HAVE_VARNISH_V2 || HAVE_VARNISH_V4 || HAVE_VARNISH_V5 - conf->collect_sma = 0; + conf->collect_sma = false; #endif - conf->collect_sms = 0; - conf->collect_struct = 0; - conf->collect_totals = 0; + conf->collect_sms = false; + conf->collect_struct = false; + conf->collect_totals = false; #if HAVE_VARNISH_V3 || HAVE_VARNISH_V4 || HAVE_VARNISH_V5 - conf->collect_uptime = 0; + conf->collect_uptime = false; #endif - conf->collect_vcl = 0; - conf->collect_workers = 0; + conf->collect_vcl = false; + conf->collect_workers = false; #if HAVE_VARNISH_V4 || HAVE_VARNISH_V5 - conf->collect_vsm = 0; - conf->collect_lck = 0; - conf->collect_mempool = 0; - conf->collect_mgt = 0; - conf->collect_smf = 0; - conf->collect_vbe = 0; - conf->collect_mse = 0; + conf->collect_vsm = false; + conf->collect_lck = false; + conf->collect_mempool = false; + conf->collect_mgt = false; + conf->collect_smf = false; + conf->collect_vbe = false; + conf->collect_mse = false; #endif return 0; @@ -1771,7 +1771,7 @@ static int varnish_config_instance(const oconfig_item_t *ci) /* {{{ */ .data = conf, .free_func = varnish_config_free, }); - have_instance = 1; + have_instance = true; return 0; } /* }}} int varnish_config_instance */ diff --git a/src/virt.c b/src/virt.c index 8a44c268..a3f94051 100644 --- a/src/virt.c +++ b/src/virt.c @@ -34,10 +34,19 @@ #include #include #include +#include /* Plugin name */ #define PLUGIN_NAME "virt" +/* Secure strcat macro assuring null termination. Parameter (n) is the size of + buffer (d), allowing this macro to be safe for static and dynamic buffers */ +#define SSTRNCAT(d, s, n) \ + do { \ + size_t _l = strlen(d); \ + sstrncpy((d) + _l, (s), (n)-_l); \ + } while (0) + #ifdef LIBVIR_CHECK_VERSION #if LIBVIR_CHECK_VERSION(0, 9, 2) @@ -59,6 +68,16 @@ #define HAVE_DOM_REASON_RUNNING_WAKEUP 1 #endif +/* + virConnectListAllDomains() appeared in 0.10.2 + Note that LIBVIR_CHECK_VERSION appeared a year later, so + in some systems which actually have virConnectListAllDomains() + we can't detect this. + */ +#if LIBVIR_CHECK_VERSION(0, 10, 2) +#define HAVE_LIST_ALL_DOMAINS 1 +#endif + #if LIBVIR_CHECK_VERSION(1, 0, 1) #define HAVE_DOM_REASON_PAUSED_SNAPSHOT 1 #endif @@ -90,6 +109,14 @@ #endif /* LIBVIR_CHECK_VERSION */ +/* structure used for aggregating notification-thread data*/ +typedef struct virt_notif_thread_s { + pthread_t event_loop_tid; + int domain_event_cb_id; + pthread_mutex_t active_mutex; /* protects 'is_active' member access*/ + bool is_active; +} virt_notif_thread_t; + static const char *config_keys[] = {"Connection", "RefreshInterval", @@ -102,14 +129,29 @@ static const char *config_keys[] = {"Connection", "IgnoreSelected", "HostnameFormat", + "HostnameMetadataNS", + "HostnameMetadataXPath", "InterfaceFormat", "PluginInstanceFormat", "Instances", "ExtraStats", + "PersistentNotification", + + "ReportBlockDevices", + "ReportNetworkInterfaces", NULL}; +/* PersistentNotification is false by default */ +static bool persistent_notification = false; + +static bool report_block_devices = true; +static bool report_network_interfaces = true; + +/* Thread used for handling libvirt notifications events */ +static virt_notif_thread_t notif_thread; + const char *domain_states[] = { [VIR_DOMAIN_NOSTATE] = "no state", [VIR_DOMAIN_RUNNING] = "the domain is running", @@ -124,7 +166,202 @@ const char *domain_states[] = { #endif }; +static int map_domain_event_to_state(int event) { + int ret; + switch (event) { + case VIR_DOMAIN_EVENT_STARTED: + ret = VIR_DOMAIN_RUNNING; + break; + case VIR_DOMAIN_EVENT_SUSPENDED: + ret = VIR_DOMAIN_PAUSED; + break; + case VIR_DOMAIN_EVENT_RESUMED: + ret = VIR_DOMAIN_RUNNING; + break; + case VIR_DOMAIN_EVENT_STOPPED: + ret = VIR_DOMAIN_SHUTOFF; + break; + case VIR_DOMAIN_EVENT_SHUTDOWN: + ret = VIR_DOMAIN_SHUTDOWN; + break; +#ifdef HAVE_DOM_STATE_PMSUSPENDED + case VIR_DOMAIN_EVENT_PMSUSPENDED: + ret = VIR_DOMAIN_PMSUSPENDED; + break; +#endif +#ifdef HAVE_DOM_REASON_CRASHED + case VIR_DOMAIN_EVENT_CRASHED: + ret = VIR_DOMAIN_CRASHED; + break; +#endif + default: + ret = VIR_DOMAIN_NOSTATE; + } + return ret; +} + #ifdef HAVE_DOM_REASON +static int map_domain_event_detail_to_reason(int event, int detail) { + int ret; + switch (event) { + case VIR_DOMAIN_EVENT_STARTED: + switch (detail) { + case VIR_DOMAIN_EVENT_STARTED_BOOTED: /* Normal startup from boot */ + ret = VIR_DOMAIN_RUNNING_BOOTED; + break; + case VIR_DOMAIN_EVENT_STARTED_MIGRATED: /* Incoming migration from another + host */ + ret = VIR_DOMAIN_RUNNING_MIGRATED; + break; + case VIR_DOMAIN_EVENT_STARTED_RESTORED: /* Restored from a state file */ + ret = VIR_DOMAIN_RUNNING_RESTORED; + break; + case VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT: /* Restored from snapshot */ + ret = VIR_DOMAIN_RUNNING_FROM_SNAPSHOT; + break; +#ifdef HAVE_DOM_REASON_RUNNING_WAKEUP + case VIR_DOMAIN_EVENT_STARTED_WAKEUP: /* Started due to wakeup event */ + ret = VIR_DOMAIN_RUNNING_WAKEUP; + break; +#endif + default: + ret = VIR_DOMAIN_RUNNING_UNKNOWN; + } + break; + case VIR_DOMAIN_EVENT_SUSPENDED: + switch (detail) { + case VIR_DOMAIN_EVENT_SUSPENDED_PAUSED: /* Normal suspend due to admin + pause */ + ret = VIR_DOMAIN_PAUSED_USER; + break; + case VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED: /* Suspended for offline + migration */ + ret = VIR_DOMAIN_PAUSED_MIGRATION; + break; + case VIR_DOMAIN_EVENT_SUSPENDED_IOERROR: /* Suspended due to a disk I/O + error */ + ret = VIR_DOMAIN_PAUSED_IOERROR; + break; + case VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG: /* Suspended due to a watchdog + firing */ + ret = VIR_DOMAIN_PAUSED_WATCHDOG; + break; + case VIR_DOMAIN_EVENT_SUSPENDED_RESTORED: /* Restored from paused state + file */ + ret = VIR_DOMAIN_PAUSED_UNKNOWN; + break; + case VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT: /* Restored from paused + snapshot */ + ret = VIR_DOMAIN_PAUSED_FROM_SNAPSHOT; + break; + case VIR_DOMAIN_EVENT_SUSPENDED_API_ERROR: /* Suspended after failure during + libvirt API call */ + ret = VIR_DOMAIN_PAUSED_UNKNOWN; + break; +#ifdef HAVE_DOM_REASON_POSTCOPY + case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY: /* Suspended for post-copy + migration */ + ret = VIR_DOMAIN_PAUSED_POSTCOPY; + break; + case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY_FAILED: /* Suspended after failed + post-copy */ + ret = VIR_DOMAIN_PAUSED_POSTCOPY_FAILED; + break; +#endif + default: + ret = VIR_DOMAIN_PAUSED_UNKNOWN; + } + break; + case VIR_DOMAIN_EVENT_RESUMED: + switch (detail) { + case VIR_DOMAIN_EVENT_RESUMED_UNPAUSED: /* Normal resume due to admin + unpause */ + ret = VIR_DOMAIN_RUNNING_UNPAUSED; + break; + case VIR_DOMAIN_EVENT_RESUMED_MIGRATED: /* Resumed for completion of + migration */ + ret = VIR_DOMAIN_RUNNING_MIGRATED; + break; + case VIR_DOMAIN_EVENT_RESUMED_FROM_SNAPSHOT: /* Resumed from snapshot */ + ret = VIR_DOMAIN_RUNNING_FROM_SNAPSHOT; + break; +#ifdef HAVE_DOM_REASON_POSTCOPY + case VIR_DOMAIN_EVENT_RESUMED_POSTCOPY: /* Resumed, but migration is still + running in post-copy mode */ + ret = VIR_DOMAIN_RUNNING_POSTCOPY; + break; +#endif + default: + ret = VIR_DOMAIN_RUNNING_UNKNOWN; + } + break; + case VIR_DOMAIN_EVENT_STOPPED: + switch (detail) { + case VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN: /* Normal shutdown */ + ret = VIR_DOMAIN_SHUTOFF_SHUTDOWN; + break; + case VIR_DOMAIN_EVENT_STOPPED_DESTROYED: /* Forced poweroff from host */ + ret = VIR_DOMAIN_SHUTOFF_DESTROYED; + break; + case VIR_DOMAIN_EVENT_STOPPED_CRASHED: /* Guest crashed */ + ret = VIR_DOMAIN_SHUTOFF_CRASHED; + break; + case VIR_DOMAIN_EVENT_STOPPED_MIGRATED: /* Migrated off to another host */ + ret = VIR_DOMAIN_SHUTOFF_MIGRATED; + break; + case VIR_DOMAIN_EVENT_STOPPED_SAVED: /* Saved to a state file */ + ret = VIR_DOMAIN_SHUTOFF_SAVED; + break; + case VIR_DOMAIN_EVENT_STOPPED_FAILED: /* Host emulator/mgmt failed */ + ret = VIR_DOMAIN_SHUTOFF_FAILED; + break; + case VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT: /* Offline snapshot loaded */ + ret = VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT; + break; + default: + ret = VIR_DOMAIN_SHUTOFF_UNKNOWN; + } + break; + case VIR_DOMAIN_EVENT_SHUTDOWN: + switch (detail) { + case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED: /* Guest finished shutdown + sequence */ + ret = VIR_DOMAIN_SHUTDOWN_USER; + break; + default: + ret = VIR_DOMAIN_SHUTDOWN_UNKNOWN; + } + break; +#ifdef HAVE_DOM_STATE_PMSUSPENDED + case VIR_DOMAIN_EVENT_PMSUSPENDED: + switch (detail) { + case VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY: /* Guest was PM suspended to + memory */ + ret = VIR_DOMAIN_PMSUSPENDED_UNKNOWN; + break; + case VIR_DOMAIN_EVENT_PMSUSPENDED_DISK: /* Guest was PM suspended to disk */ + ret = VIR_DOMAIN_PMSUSPENDED_DISK_UNKNOWN; + break; + default: + ret = VIR_DOMAIN_PMSUSPENDED_UNKNOWN; + } + break; +#endif + case VIR_DOMAIN_EVENT_CRASHED: + switch (detail) { + case VIR_DOMAIN_EVENT_CRASHED_PANICKED: /* Guest was panicked */ + ret = VIR_DOMAIN_CRASHED_PANICKED; + break; + default: + ret = VIR_DOMAIN_CRASHED_UNKNOWN; + } + break; + default: + ret = VIR_DOMAIN_NOSTATE_UNKNOWN; + } + return ret; +} + #define DOMAIN_STATE_REASON_MAX_SIZE 20 const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = { [VIR_DOMAIN_NOSTATE][VIR_DOMAIN_NOSTATE_UNKNOWN] = @@ -158,7 +395,6 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = { [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_POSTCOPY] = "running in post-copy migration mode", #endif - [VIR_DOMAIN_BLOCKED][VIR_DOMAIN_BLOCKED_UNKNOWN] = "the reason is unknown", @@ -198,7 +434,6 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = { [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_POSTCOPY_FAILED] = "paused after failed post-copy", #endif - [VIR_DOMAIN_SHUTDOWN][VIR_DOMAIN_SHUTDOWN_UNKNOWN] = "the reason is unknown", [VIR_DOMAIN_SHUTDOWN][VIR_DOMAIN_SHUTDOWN_USER] = @@ -237,12 +472,12 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = { 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 virConnectPtr conn = 0; -static char *conn_string = NULL; +static virConnectPtr conn; +static char *conn_string; static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC; /* Node information required for %CPU */ @@ -252,11 +487,11 @@ static virNodeInfo nodeinfo; static int interval = 60; /* List of domains, if specified. */ -static ignorelist_t *il_domains = NULL; +static ignorelist_t *il_domains; /* List of block devices, if specified. */ -static ignorelist_t *il_block_devices = NULL; +static ignorelist_t *il_block_devices; /* List of network interface devices, if specified. */ -static ignorelist_t *il_interface_devices = NULL; +static ignorelist_t *il_interface_devices; static int ignore_device_match(ignorelist_t *, const char *domname, const char *devpath); @@ -278,6 +513,7 @@ struct interface_device { typedef struct domain_s { virDomainPtr ptr; virDomainInfo info; + bool active; } domain_t; struct lv_read_state { @@ -293,7 +529,8 @@ struct lv_read_state { }; static void free_domains(struct lv_read_state *state); -static int add_domain(struct lv_read_state *state, virDomainPtr dom); +static int add_domain(struct lv_read_state *state, virDomainPtr dom, + bool active); static void free_block_devices(struct lv_read_state *state); static int add_block_device(struct lv_read_state *state, virDomainPtr dom, @@ -328,20 +565,29 @@ static int nr_instances = NR_INSTANCES_DEFAULT; static struct lv_user_data lv_read_user_data[NR_INSTANCES_MAX]; /* HostnameFormat. */ -#define HF_MAX_FIELDS 3 +#define HF_MAX_FIELDS 4 -enum hf_field { hf_none = 0, hf_hostname, hf_name, hf_uuid }; +enum hf_field { hf_none = 0, hf_hostname, hf_name, hf_uuid, hf_metadata }; static enum hf_field hostname_format[HF_MAX_FIELDS] = {hf_name}; /* PluginInstanceFormat */ -#define PLGINST_MAX_FIELDS 2 +#define PLGINST_MAX_FIELDS 3 -enum plginst_field { plginst_none = 0, plginst_name, plginst_uuid }; +enum plginst_field { + plginst_none = 0, + plginst_name, + plginst_uuid, + plginst_metadata +}; static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] = { plginst_none}; +/* HostnameMetadataNS && HostnameMetadataXPath */ +static char *hm_xpath; +static char *hm_ns; + /* BlockDeviceFormat */ enum bd_field { target, source }; @@ -401,7 +647,7 @@ static const struct ex_stats_item ex_stats_table[] = { }; /* BlockDeviceFormatBasename */ -_Bool blockdevice_format_basename = 0; +static bool blockdevice_format_basename; static enum bd_field blockdevice_format = target; static enum if_field interface_format = if_name; @@ -476,8 +722,96 @@ static int get_block_info(struct lv_block_info *binfo, ERROR(PLUGIN_NAME " plugin: %s failed: %s", (s), err->message); \ } while (0) +char *metadata_get_hostname(virDomainPtr dom) { + const char *xpath_str = NULL; + if (hm_xpath == NULL) + xpath_str = "/instance/name/text()"; + else + xpath_str = hm_xpath; + + const char *namespace = NULL; + if (hm_ns == NULL) { + namespace = "http://openstack.org/xmlns/libvirt/nova/1.0"; + } else { + namespace = hm_ns; + } + + char *metadata_str = virDomainGetMetadata( + dom, VIR_DOMAIN_METADATA_ELEMENT, namespace, VIR_DOMAIN_AFFECT_CURRENT); + if (metadata_str == NULL) { + return NULL; + } + + char *hostname = NULL; + xmlXPathContextPtr xpath_ctx = NULL; + xmlXPathObjectPtr xpath_obj = NULL; + xmlNodePtr xml_node = NULL; + + xmlDocPtr xml_doc = + xmlReadDoc((xmlChar *)metadata_str, NULL, NULL, XML_PARSE_NONET); + if (xml_doc == NULL) { + ERROR(PLUGIN_NAME " plugin: xmlReadDoc failed to read metadata"); + goto metadata_end; + } + + xpath_ctx = xmlXPathNewContext(xml_doc); + if (xpath_ctx == NULL) { + ERROR(PLUGIN_NAME " plugin: xmlXPathNewContext(%s) failed for metadata", + metadata_str); + goto metadata_end; + } + xpath_obj = xmlXPathEval((xmlChar *)xpath_str, xpath_ctx); + if (xpath_obj == NULL) { + ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) failed for metadata", + xpath_str); + goto metadata_end; + } + + if (xpath_obj->type != XPATH_NODESET) { + ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unexpected return type %d " + "(wanted %d) for metadata", + xpath_str, xpath_obj->type, XPATH_NODESET); + goto metadata_end; + } + + // TODO(sileht): We can support || operator by looping on nodes here + if (xpath_obj->nodesetval == NULL || xpath_obj->nodesetval->nodeNr != 1) { + WARNING(PLUGIN_NAME " plugin: xmlXPathEval(%s) return nodeset size=%i " + "expected=1 for metadata", + xpath_str, + (xpath_obj->nodesetval == NULL) ? 0 + : xpath_obj->nodesetval->nodeNr); + goto metadata_end; + } + + xml_node = xpath_obj->nodesetval->nodeTab[0]; + if (xml_node->type == XML_TEXT_NODE) { + hostname = strdup((const char *)xml_node->content); + } else if (xml_node->type == XML_ATTRIBUTE_NODE) { + hostname = strdup((const char *)xml_node->children->content); + } else { + ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unsupported node type %d", + xpath_str, xml_node->type); + goto metadata_end; + } + + if (hostname == NULL) { + ERROR(PLUGIN_NAME " plugin: strdup(%s) hostname failed", xpath_str); + goto metadata_end; + } + +metadata_end: + if (xpath_obj) + xmlXPathFreeObject(xpath_obj); + if (xpath_ctx) + xmlXPathFreeContext(xpath_ctx); + if (xml_doc) + xmlFreeDoc(xml_doc); + sfree(metadata_str); + return hostname; +} + static void init_value_list(value_list_t *vl, virDomainPtr dom) { - int n; const char *name; char uuid[VIR_UUID_STRING_BUFLEN]; @@ -490,44 +824,39 @@ static void init_value_list(value_list_t *vl, virDomainPtr dom) { if (hostname_format[i] == hf_none) continue; - n = DATA_MAX_NAME_LEN - strlen(vl->host) - 2; - - if (i > 0 && n >= 1) { - strncat(vl->host, ":", 1); - n--; - } + if (i > 0) + SSTRNCAT(vl->host, ":", sizeof(vl->host)); switch (hostname_format[i]) { case hf_none: break; case hf_hostname: - strncat(vl->host, hostname_g, n); + SSTRNCAT(vl->host, hostname_g, sizeof(vl->host)); break; case hf_name: name = virDomainGetName(dom); if (name) - strncat(vl->host, name, n); + SSTRNCAT(vl->host, name, sizeof(vl->host)); break; case hf_uuid: if (virDomainGetUUIDString(dom, uuid) == 0) - strncat(vl->host, uuid, n); + SSTRNCAT(vl->host, uuid, sizeof(vl->host)); + break; + case hf_metadata: + name = metadata_get_hostname(dom); + if (name) + SSTRNCAT(vl->host, name, sizeof(vl->host)); break; } } - vl->host[sizeof(vl->host) - 1] = '\0'; - /* Construct the plugin instance field according to PluginInstanceFormat. */ for (int i = 0; i < PLGINST_MAX_FIELDS; ++i) { if (plugin_instance_format[i] == plginst_none) continue; - n = sizeof(vl->plugin_instance) - strlen(vl->plugin_instance) - 2; - - if (i > 0 && n >= 1) { - strncat(vl->plugin_instance, ":", 1); - n--; - } + if (i > 0) + SSTRNCAT(vl->plugin_instance, ":", sizeof(vl->plugin_instance)); switch (plugin_instance_format[i]) { case plginst_none: @@ -535,17 +864,20 @@ static void init_value_list(value_list_t *vl, virDomainPtr dom) { case plginst_name: name = virDomainGetName(dom); if (name) - strncat(vl->plugin_instance, name, n); + SSTRNCAT(vl->plugin_instance, name, sizeof(vl->plugin_instance)); break; case plginst_uuid: if (virDomainGetUUIDString(dom, uuid) == 0) - strncat(vl->plugin_instance, uuid, n); + SSTRNCAT(vl->plugin_instance, uuid, sizeof(vl->plugin_instance)); + break; + case plginst_metadata: + name = metadata_get_hostname(dom); + if (name) + SSTRNCAT(vl->plugin_instance, name, sizeof(vl->plugin_instance)); break; } } - vl->plugin_instance[sizeof(vl->plugin_instance) - 1] = '\0'; - } /* void init_value_list */ static int init_notif(notification_t *notif, const virDomainPtr domain, @@ -554,7 +886,7 @@ static int init_notif(notification_t *notif, const virDomainPtr domain, 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; } @@ -633,10 +965,11 @@ static double cpu_ns_to_percent(unsigned int node_cpus, (time_diff_sec * node_cpus * NANOSEC_IN_SEC); } - DEBUG(PLUGIN_NAME ": node_cpus=%u cpu_time_old=%llu cpu_time_new=%llu" - "cpu_time_diff=%llu time_diff_sec=%f percent=%f", - node_cpus, cpu_time_old, cpu_time_new, cpu_time_diff, time_diff_sec, - percent); + 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, + (uint64_t)cpu_time_diff, time_diff_sec, percent); return percent; } @@ -727,25 +1060,26 @@ static unsigned int parse_ex_stats_flags(char **exstats, int numexstats) { } 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]); } } } return ex_stats_flags; } -static void domain_state_submit(virDomainPtr dom, int state, int reason) { - - if ((state < 0) || (state >= STATIC_ARRAY_SIZE(domain_states))) { - ERROR(PLUGIN_NAME ": Array index out of bounds: state=%d", state); +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 " plugin: Array index out of bounds: state=%d", state); return; } char msg[DATA_MAX_NAME_LEN]; const char *state_str = domain_states[state]; #ifdef HAVE_DOM_REASON - if ((reason < 0) || (reason >= STATIC_ARRAY_SIZE(domain_reasons[0]))) { - ERROR(PLUGIN_NAME ": Array index out of bounds: reason=%d", reason); + if ((reason < 0) || + ((size_t)reason >= STATIC_ARRAY_SIZE(domain_reasons[0]))) { + ERROR(PLUGIN_NAME " plugin: Array index out of bounds: reason=%d", reason); return; } @@ -754,8 +1088,8 @@ static void domain_state_submit(virDomainPtr dom, int state, int reason) { * 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 @@ -784,16 +1118,13 @@ static void domain_state_submit(virDomainPtr dom, int state, int reason) { 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); } -static int lv_config(const char *key, const char *value) { - if (virInitialize() != 0) - return 1; - +static int lv_init_ignorelists() { if (il_domains == NULL) il_domains = ignorelist_create(1); if (il_block_devices == NULL) @@ -801,6 +1132,19 @@ static int lv_config(const char *key, const char *value) { if (il_interface_devices == NULL) il_interface_devices = ignorelist_create(1); + if (!il_domains || !il_block_devices || !il_interface_devices) + return 1; + + return 0; +} + +static int lv_config(const char *key, const char *value) { + if (virInitialize() != 0) + return 1; + + if (lv_init_ignorelists() != 0) + return 1; + if (strcasecmp(key, "Connection") == 0) { char *tmp = strdup(value); if (tmp == NULL) { @@ -843,7 +1187,7 @@ static int lv_config(const char *key, const char *value) { return 0; } if (strcasecmp(key, "BlockDeviceFormatBasename") == 0) { - blockdevice_format_basename = IS_TRUE(value); + blockdevice_format_basename = IS_TRUE(value) ? true : false; return 0; } if (strcasecmp(key, "InterfaceDevice") == 0) { @@ -865,18 +1209,37 @@ static int lv_config(const char *key, const char *value) { return 0; } - if (strcasecmp(key, "HostnameFormat") == 0) { - char *value_copy; - char *fields[HF_MAX_FIELDS]; - int n; + if (strcasecmp(key, "HostnameMetadataNS") == 0) { + char *tmp = strdup(value); + if (tmp == NULL) { + ERROR(PLUGIN_NAME " plugin: HostnameMetadataNS strdup failed."); + return 1; + } + sfree(hm_ns); + hm_ns = tmp; + return 0; + } + + if (strcasecmp(key, "HostnameMetadataXPath") == 0) { + char *tmp = strdup(value); + if (tmp == NULL) { + ERROR(PLUGIN_NAME " plugin: HostnameMetadataXPath strdup failed."); + return 1; + } + sfree(hm_xpath); + hm_xpath = tmp; + return 0; + } - value_copy = strdup(value); + if (strcasecmp(key, "HostnameFormat") == 0) { + 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"); @@ -890,6 +1253,8 @@ static int lv_config(const char *key, const char *value) { hostname_format[i] = hf_name; else if (strcasecmp(fields[i], "uuid") == 0) hostname_format[i] = hf_uuid; + else if (strcasecmp(fields[i], "metadata") == 0) + hostname_format[i] = hf_metadata; else { ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", fields[i]); @@ -906,17 +1271,14 @@ static int lv_config(const char *key, const char *value) { } 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"); @@ -931,6 +1293,8 @@ static int lv_config(const char *key, const char *value) { plugin_instance_format[i] = plginst_name; else if (strcasecmp(fields[i], "uuid") == 0) plugin_instance_format[i] = plginst_uuid; + else if (strcasecmp(fields[i], "metadata") == 0) + plugin_instance_format[i] = plginst_metadata; else { ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s", fields[i]); @@ -1005,6 +1369,21 @@ static int lv_config(const char *key, const char *value) { } } + if (strcasecmp(key, "PersistentNotification") == 0) { + persistent_notification = IS_TRUE(value); + return 0; + } + + if (strcasecmp(key, "ReportBlockDevices") == 0) { + report_block_devices = IS_TRUE(value); + return 0; + } + + if (strcasecmp(key, "ReportNetworkInterfaces") == 0) { + report_network_interfaces = IS_TRUE(value); + return 0; + } + /* Unrecognised option. */ return -1; } @@ -1027,7 +1406,7 @@ static int lv_connect(void) { } int status = virNodeGetInfo(conn, &nodeinfo); if (status != 0) { - ERROR(PLUGIN_NAME ": virNodeGetInfo failed"); + ERROR(PLUGIN_NAME " plugin: virNodeGetInfo failed"); return -1; } } @@ -1053,7 +1432,7 @@ static int lv_domain_block_info(virDomainPtr dom, const char *path, return -1; } - virTypedParameterPtr params = calloc((size_t)nparams, sizeof(*params)); + virTypedParameterPtr params = calloc(nparams, sizeof(*params)); if (params == NULL) { ERROR("virt plugin: alloc(%i) for block=%s parameters failed.", nparams, path); @@ -1112,7 +1491,7 @@ static void vcpu_pin_submit(virDomainPtr dom, int max_cpus, int vcpu, unsigned char *cpu_maps, int cpu_map_len) { for (int cpu = 0; cpu < max_cpus; ++cpu) { char type_instance[DATA_MAX_NAME_LEN]; - _Bool is_set = VIR_CPU_USABLE(cpu_maps, cpu_map_len, vcpu, cpu) ? 1 : 0; + bool is_set = VIR_CPU_USABLE(cpu_maps, cpu_map_len, vcpu, cpu); snprintf(type_instance, sizeof(type_instance), "vcpu_%d-cpu_%d", vcpu, cpu); submit(dom, "cpu_affinity", type_instance, &(value_t){.gauge = is_set}, 1); @@ -1123,7 +1502,7 @@ static int get_vcpu_stats(virDomainPtr domain, unsigned short nr_virt_cpu) { int max_cpus = VIR_NODEINFO_MAXCPUS(nodeinfo); int cpu_map_len = VIR_CPU_MAPLEN(max_cpus); - virVcpuInfoPtr vinfo = calloc(nr_virt_cpu, sizeof(vinfo[0])); + virVcpuInfoPtr vinfo = calloc(nr_virt_cpu, sizeof(*vinfo)); if (vinfo == NULL) { ERROR(PLUGIN_NAME " plugin: calloc failed."); return -1; @@ -1165,7 +1544,7 @@ static int get_pcpu_stats(virDomainPtr dom) { return -1; } - virTypedParameterPtr param = calloc(nparams, sizeof(virTypedParameter)); + virTypedParameterPtr param = calloc(nparams, sizeof(*param)); if (param == NULL) { ERROR(PLUGIN_NAME " plugin: alloc(%i) for cpu parameters failed.", nparams); return -1; @@ -1201,6 +1580,15 @@ static int get_pcpu_stats(virDomainPtr dom) { #endif /* HAVE_CPU_STATS */ #ifdef HAVE_DOM_REASON + +static void domain_state_submit(virDomainPtr dom, int state, int reason) { + value_t values[] = { + {.gauge = (gauge_t)state}, {.gauge = (gauge_t)reason}, + }; + + submit(dom, "domain_state", NULL, values, STATIC_ARRAY_SIZE(values)); +} + static int get_domain_state(virDomainPtr domain) { int domain_state = 0; int domain_reason = 0; @@ -1213,15 +1601,35 @@ static int get_domain_state(virDomainPtr domain) { } domain_state_submit(domain, domain_state, domain_reason); + + return status; +} + +#ifdef HAVE_LIST_ALL_DOMAINS +static int get_domain_state_notify(virDomainPtr domain) { + int domain_state = 0; + int domain_reason = 0; + + int status = virDomainGetState(domain, &domain_state, &domain_reason, 0); + if (status != 0) { + ERROR(PLUGIN_NAME " plugin: virDomainGetState failed with status %i.", + status); + return status; + } + + if (persistent_notification) + domain_state_submit_notif(domain, domain_state, domain_reason); + return status; } +#endif /* HAVE_LIST_ALL_DOMAINS */ #endif /* HAVE_DOM_REASON */ static int get_memory_stats(virDomainPtr domain) { virDomainMemoryStatPtr minfo = - calloc(VIR_DOMAIN_MEMORY_STAT_NR, sizeof(virDomainMemoryStatStruct)); + calloc(VIR_DOMAIN_MEMORY_STAT_NR, sizeof(*minfo)); if (minfo == NULL) { - ERROR("virt plugin: malloc failed."); + ERROR("virt plugin: calloc failed."); return -1; } @@ -1313,7 +1721,7 @@ static int get_block_stats(struct block_device *block_dev) { #define NM_ADD_STR_ITEMS(_items, _size) \ do { \ - for (int _i = 0; _i < _size; ++_i) { \ + for (size_t _i = 0; _i < _size; ++_i) { \ DEBUG(PLUGIN_NAME \ " plugin: Adding notification metadata name=%s value=%s", \ _items[_i].name, _items[_i].value); \ @@ -1338,7 +1746,7 @@ static int fs_info_notify(virDomainPtr domain, virDomainFSInfoPtr fs_info) { {.name = "name", .value = fs_info->name}, {.name = "fstype", .value = fs_info->fstype}}; - for (int i = 0; i < fs_info->ndevAlias; ++i) { + for (size_t i = 0; i < fs_info->ndevAlias; ++i) { fs_dev_alias[i].name = "devAlias"; fs_dev_alias[i].value = fs_info->devAlias[i]; } @@ -1445,7 +1853,7 @@ static int get_job_stats(virDomainPtr domain) { static int get_domain_metrics(domain_t *domain) { if (!domain || !domain->ptr) { - ERROR(PLUGIN_NAME "plugin: get_domain_metrics: NULL pointer"); + ERROR(PLUGIN_NAME " plugin: get_domain_metrics: NULL pointer"); return -1; } @@ -1464,10 +1872,6 @@ static int get_domain_metrics(domain_t *domain) { * We need to get it from virDomainGetState. */ GET_STATS(get_domain_state, "domain reason", domain->ptr); -#else - /* virDomainGetState is not available. Submit 0, which corresponds to - * unknown reason. */ - domain_state_submit(domain->ptr, info.state, 0); #endif } @@ -1510,6 +1914,7 @@ static int get_domain_metrics(domain_t *domain) { /* Update cached virDomainInfo. It has to be done after cpu_submit */ memcpy(&domain->info, &info, sizeof(domain->info)); + return 0; } @@ -1558,6 +1963,192 @@ static int get_if_dev_stats(struct interface_device *if_dev) { return 0; } +static int domain_lifecycle_event_cb(__attribute__((unused)) virConnectPtr con_, + virDomainPtr dom, int event, int detail, + __attribute__((unused)) void *opaque) { + int domain_state = map_domain_event_to_state(event); + int domain_reason = 0; /* 0 means UNKNOWN reason for any state */ +#ifdef HAVE_DOM_REASON + domain_reason = map_domain_event_detail_to_reason(event, detail); +#endif + domain_state_submit_notif(dom, domain_state, domain_reason); + + return 0; +} + +static int register_event_impl(void) { + if (virEventRegisterDefaultImpl() < 0) { + virErrorPtr err = virGetLastError(); + ERROR(PLUGIN_NAME + " plugin: error while event implementation registering: %s", + err && err->message ? err->message : "Unknown error"); + return -1; + } + + return 0; +} + +static void virt_notif_thread_set_active(virt_notif_thread_t *thread_data, + const bool active) { + assert(thread_data != NULL); + pthread_mutex_lock(&thread_data->active_mutex); + thread_data->is_active = active; + pthread_mutex_unlock(&thread_data->active_mutex); +} + +static bool virt_notif_thread_is_active(virt_notif_thread_t *thread_data) { + bool active = false; + + assert(thread_data != NULL); + pthread_mutex_lock(&thread_data->active_mutex); + active = thread_data->is_active; + pthread_mutex_unlock(&thread_data->active_mutex); + + return active; +} + +/* worker function running default event implementation */ +static void *event_loop_worker(void *arg) { + virt_notif_thread_t *thread_data = (virt_notif_thread_t *)arg; + + while (virt_notif_thread_is_active(thread_data)) { + if (virEventRunDefaultImpl() < 0) { + virErrorPtr err = virGetLastError(); + ERROR(PLUGIN_NAME " plugin: failed to run event loop: %s\n", + err && err->message ? err->message : "Unknown error"); + } + } + + return NULL; +} + +static int virt_notif_thread_init(virt_notif_thread_t *thread_data) { + int ret; + + assert(thread_data != NULL); + ret = pthread_mutex_init(&thread_data->active_mutex, NULL); + if (ret != 0) { + ERROR(PLUGIN_NAME " plugin: Failed to initialize mutex, err %u", ret); + return ret; + } + + /** + * '0' and positive integers are meaningful ID's, therefore setting + * domain_event_cb_id to '-1' + */ + thread_data->domain_event_cb_id = -1; + pthread_mutex_lock(&thread_data->active_mutex); + thread_data->is_active = false; + pthread_mutex_unlock(&thread_data->active_mutex); + + return 0; +} + +/* register domain event callback and start event loop thread */ +static int start_event_loop(virt_notif_thread_t *thread_data) { + assert(thread_data != NULL); + thread_data->domain_event_cb_id = virConnectDomainEventRegisterAny( + conn, NULL, VIR_DOMAIN_EVENT_ID_LIFECYCLE, + VIR_DOMAIN_EVENT_CALLBACK(domain_lifecycle_event_cb), NULL, NULL); + if (thread_data->domain_event_cb_id == -1) { + ERROR(PLUGIN_NAME " plugin: error while callback registering"); + return -1; + } + + virt_notif_thread_set_active(thread_data, 1); + if (pthread_create(&thread_data->event_loop_tid, NULL, event_loop_worker, + thread_data)) { + ERROR(PLUGIN_NAME " plugin: failed event loop thread creation"); + virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id); + return -1; + } + + return 0; +} + +/* stop event loop thread and deregister callback */ +static void stop_event_loop(virt_notif_thread_t *thread_data) { + /* stopping loop and de-registering event handler*/ + virt_notif_thread_set_active(thread_data, 0); + if (conn != NULL && thread_data->domain_event_cb_id != -1) + virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id); + + if (pthread_join(notif_thread.event_loop_tid, NULL) != 0) + ERROR(PLUGIN_NAME " plugin: stopping notification thread failed"); +} + +static int persistent_domains_state_notification(void) { + int status = 0; + int n; +#ifdef HAVE_LIST_ALL_DOMAINS + virDomainPtr *domains = NULL; + n = virConnectListAllDomains(conn, &domains, + VIR_CONNECT_LIST_DOMAINS_PERSISTENT); + if (n < 0) { + VIRT_ERROR(conn, "reading list of persistent domains"); + status = -1; + } else { + DEBUG(PLUGIN_NAME " plugin: getting state of %i persistent domains", n); + /* Fetch each persistent domain's state and notify it */ + int n_notified = n; + for (int i = 0; i < n; ++i) { + status = get_domain_state_notify(domains[i]); + if (status != 0) { + n_notified--; + ERROR(PLUGIN_NAME " plugin: could not notify state of domain %s", + virDomainGetName(domains[i])); + } + virDomainFree(domains[i]); + } + + sfree(domains); + DEBUG(PLUGIN_NAME " plugin: notified state of %i persistent domains", + n_notified); + } +#else + n = virConnectNumOfDomains(conn); + if (n > 0) { + int *domids; + /* Get list of domains. */ + domids = calloc(n, sizeof(*domids)); + if (domids == NULL) { + ERROR(PLUGIN_NAME " plugin: calloc failed."); + return -1; + } + n = virConnectListDomains(conn, domids, n); + if (n < 0) { + VIRT_ERROR(conn, "reading list of domains"); + sfree(domids); + return -1; + } + /* Fetch info of each active domain and notify it */ + for (int i = 0; i < n; ++i) { + virDomainInfo info; + virDomainPtr dom = NULL; + dom = virDomainLookupByID(conn, domids[i]); + if (dom == NULL) { + VIRT_ERROR(conn, "virDomainLookupByID"); + /* Could be that the domain went away -- ignore it anyway. */ + continue; + } + status = virDomainGetInfo(dom, &info); + if (status == 0) + /* virDomainGetState is not available. Submit 0, which corresponds to + * unknown reason. */ + domain_state_submit_notif(dom, info.state, 0); + else + ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.", + status); + + virDomainFree(dom); + } + sfree(domids); + } +#endif + + return status; +} + static int lv_read(user_data_t *ud) { time_t t; struct lv_read_instance *inst = NULL; @@ -1571,9 +2162,19 @@ static int lv_read(user_data_t *ud) { inst = ud->data; state = &inst->read_state; + bool reconnect = conn == NULL ? true : false; + /* event implementation must be registered before connection is opened */ if (inst->id == 0) { + if (!persistent_notification && reconnect) + if (register_event_impl() != 0) + return -1; + if (lv_connect() < 0) return -1; + + if (!persistent_notification && reconnect && conn != NULL) + if (start_event_loop(¬if_thread) != 0) + return -1; } time(&t); @@ -1582,32 +2183,53 @@ static int lv_read(user_data_t *ud) { if ((last_refresh == (time_t)0) || ((interval > 0) && ((last_refresh + interval) <= t))) { if (refresh_lists(inst) != 0) { - if (inst->id == 0) + if (inst->id == 0) { + if (!persistent_notification) + stop_event_loop(¬if_thread); lv_disconnect(); + } return -1; } last_refresh = t; } -#if 0 - for (int i = 0; i < nr_domains; ++i) - fprintf (stderr, "domain %s\n", virDomainGetName (state->domains[i].ptr)); - for (int i = 0; i < nr_block_devices; ++i) - fprintf (stderr, "block device %d %s:%s\n", - i, virDomainGetName (block_devices[i].dom), - block_devices[i].path); - for (int i = 0; i < nr_interface_devices; ++i) - fprintf (stderr, "interface device %d %s:%s\n", - i, virDomainGetName (interface_devices[i].dom), - interface_devices[i].path); + /* persistent domains state notifications are handled by instance 0 */ + if (inst->id == 0 && persistent_notification) { + int status = persistent_domains_state_notification(); + if (status != 0) + DEBUG(PLUGIN_NAME " plugin: persistent_domains_state_notifications " + "returned with status %i", + status); + } + +#if COLLECT_DEBUG + for (int i = 0; i < state->nr_domains; ++i) + DEBUG(PLUGIN_NAME " plugin: domain %s", + virDomainGetName(state->domains[i].ptr)); + for (int i = 0; i < state->nr_block_devices; ++i) + DEBUG(PLUGIN_NAME " plugin: block device %d %s:%s", i, + virDomainGetName(state->block_devices[i].dom), + state->block_devices[i].path); + for (int i = 0; i < state->nr_interface_devices; ++i) + DEBUG(PLUGIN_NAME " plugin: interface device %d %s:%s", i, + virDomainGetName(state->interface_devices[i].dom), + state->interface_devices[i].path); #endif /* Get domains' metrics */ for (int i = 0; i < state->nr_domains; ++i) { - int status = get_domain_metrics(&state->domains[i]); + domain_t *dom = &state->domains[i]; + int status = 0; + if (dom->active) + status = get_domain_metrics(dom); +#ifdef HAVE_DOM_REASON + else + status = get_domain_state(dom->ptr); +#endif + if (status != 0) - ERROR(PLUGIN_NAME " failed to get metrics for domain=%s", - virDomainGetName(state->domains[i].ptr)); + ERROR(PLUGIN_NAME " plugin: failed to get metrics for domain=%s", + virDomainGetName(dom->ptr)); } /* Get block device stats for each domain. */ @@ -1615,7 +2237,7 @@ static int lv_read(user_data_t *ud) { 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->block_devices[i].dom)); } @@ -1624,10 +2246,11 @@ static int lv_read(user_data_t *ud) { 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; @@ -1639,7 +2262,7 @@ static int lv_init_instance(size_t i, plugin_read_cb callback) { memset(lv_ud, 0, sizeof(*lv_ud)); - snprintf(inst->tag, sizeof(inst->tag), "%s-%zu", PLUGIN_NAME, i); + snprintf(inst->tag, sizeof(inst->tag), "%s-%" PRIsz, PLUGIN_NAME, i); inst->id = i; user_data_t *ud = &(lv_ud->ud); @@ -1647,6 +2270,7 @@ static int lv_init_instance(size_t i, plugin_read_cb callback) { ud->free_func = NULL; INFO(PLUGIN_NAME " plugin: reader %s initialized", inst->tag); + return plugin_register_complex_read(NULL, inst->tag, callback, 0, ud); } @@ -1661,6 +2285,7 @@ static void lv_fini_instance(size_t i) { struct lv_read_state *state = &(inst->read_state); lv_clean_read_state(state); + INFO(PLUGIN_NAME " plugin: reader %s finalized", inst->tag); } @@ -1668,13 +2293,31 @@ static int lv_init(void) { if (virInitialize() != 0) return -1; + /* Init ignorelists if there was no explicit configuration */ + if (lv_init_ignorelists() != 0) + return -1; + + /* event implementation must be registered before connection is opened */ + if (!persistent_notification) + if (register_event_impl() != 0) + return -1; + if (lv_connect() != 0) return -1; + DEBUG(PLUGIN_NAME " plugin: starting event loop"); + + if (!persistent_notification) { + virt_notif_thread_init(¬if_thread); + if (start_event_loop(¬if_thread) != 0) + return -1; + } + DEBUG(PLUGIN_NAME " plugin: starting %i instances", nr_instances); for (int i = 0; i < nr_instances; ++i) - lv_init_instance(i, lv_read); + if (lv_init_instance(i, lv_read) != 0) + return -1; return 0; } @@ -1773,224 +2416,256 @@ static int lv_instance_include_domain(struct lv_read_instance *inst, return 0; } -/* - virConnectListAllDomains() appeared in 0.10.2 - Note that LIBVIR_CHECK_VERSION appeared a year later, so - in some systems which actually have virConnectListAllDomains() - we can't detect this. - */ -#ifdef LIBVIR_CHECK_VERSION -#if LIBVIR_CHECK_VERSION(0, 10, 2) -#define HAVE_LIST_ALL_DOMAINS 1 -#endif -#endif +static void lv_add_block_devices(struct lv_read_state *state, virDomainPtr dom, + const char *domname, + xmlXPathContextPtr xpath_ctx) { + const char *bd_xmlpath = "/domain/devices/disk/target[@dev]"; + if (blockdevice_format == source) + bd_xmlpath = "/domain/devices/disk/source[@dev]"; + + xmlXPathObjectPtr xpath_obj = + xmlXPathEval((const xmlChar *)bd_xmlpath, xpath_ctx); + + if (xpath_obj == NULL) + return; + + if (xpath_obj->type != XPATH_NODESET || xpath_obj->nodesetval == NULL) { + xmlXPathFreeObject(xpath_obj); + return; + } + + for (int j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) { + xmlNodePtr node = xpath_obj->nodesetval->nodeTab[j]; + if (!node) + continue; + + char *path = (char *)xmlGetProp(node, (xmlChar *)"dev"); + if (!path) + continue; + + if (ignore_device_match(il_block_devices, domname, path) == 0) + add_block_device(state, dom, path); + + xmlFree(path); + } + xmlXPathFreeObject(xpath_obj); +} + +static void lv_add_network_interfaces(struct lv_read_state *state, + virDomainPtr dom, const char *domname, + xmlXPathContextPtr xpath_ctx) { + xmlXPathObjectPtr xpath_obj = xmlXPathEval( + (xmlChar *)"/domain/devices/interface[target[@dev]]", xpath_ctx); + + if (xpath_obj == NULL) + return; + + if (xpath_obj->type != XPATH_NODESET || xpath_obj->nodesetval == NULL) { + xmlXPathFreeObject(xpath_obj); + return; + } + + xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval; + + for (int j = 0; j < xml_interfaces->nodeNr; ++j) { + char *path = NULL; + char *address = NULL; + + xmlNodePtr xml_interface = xml_interfaces->nodeTab[j]; + if (!xml_interface) + continue; + + for (xmlNodePtr child = xml_interface->children; child; + child = child->next) { + if (child->type != XML_ELEMENT_NODE) + continue; + + if (xmlStrEqual(child->name, (const xmlChar *)"target")) { + path = (char *)xmlGetProp(child, (const xmlChar *)"dev"); + if (!path) + continue; + } else if (xmlStrEqual(child->name, (const xmlChar *)"mac")) { + address = (char *)xmlGetProp(child, (const xmlChar *)"address"); + if (!address) + continue; + } + } + + if ((ignore_device_match(il_interface_devices, domname, path) == 0 && + ignore_device_match(il_interface_devices, domname, address) == 0)) { + add_interface_device(state, dom, path, address, j + 1); + } + + if (path) + xmlFree(path); + if (address) + xmlFree(address); + } + xmlXPathFreeObject(xpath_obj); +} static int refresh_lists(struct lv_read_instance *inst) { struct lv_read_state *state = &inst->read_state; int n; +#ifndef HAVE_LIST_ALL_DOMAINS n = virConnectNumOfDomains(conn); if (n < 0) { VIRT_ERROR(conn, "reading number of domains"); return -1; } +#endif lv_clean_read_state(state); - if (n > 0) { +#ifndef HAVE_LIST_ALL_DOMAINS + if (n == 0) + goto end; +#endif + #ifdef HAVE_LIST_ALL_DOMAINS - virDomainPtr *domains; - n = virConnectListAllDomains(conn, &domains, - VIR_CONNECT_LIST_DOMAINS_ACTIVE); + virDomainPtr *domains, *domains_inactive; + int m = virConnectListAllDomains(conn, &domains_inactive, + VIR_CONNECT_LIST_DOMAINS_INACTIVE); + n = virConnectListAllDomains(conn, &domains, VIR_CONNECT_LIST_DOMAINS_ACTIVE); #else - int *domids; - - /* Get list of domains. */ - domids = malloc(sizeof(*domids) * n); - if (domids == NULL) { - ERROR(PLUGIN_NAME " plugin: malloc failed."); - return -1; - } + /* Get list of domains. */ + int *domids = calloc(n, sizeof(*domids)); + if (domids == NULL) { + ERROR(PLUGIN_NAME " plugin: calloc failed."); + return -1; + } - n = virConnectListDomains(conn, domids, n); + n = virConnectListDomains(conn, domids, n); #endif - if (n < 0) { - VIRT_ERROR(conn, "reading list of domains"); + if (n < 0) { + VIRT_ERROR(conn, "reading list of domains"); #ifndef HAVE_LIST_ALL_DOMAINS - sfree(domids); + sfree(domids); +#else + for (int i = 0; i < m; ++i) + virDomainFree(domains_inactive[i]); + sfree(domains_inactive); #endif - return -1; + return -1; + } + +#ifdef HAVE_LIST_ALL_DOMAINS + for (int i = 0; i < m; ++i) + if (add_domain(state, domains_inactive[i], 0) < 0) { + ERROR(PLUGIN_NAME " plugin: malloc failed."); + virDomainFree(domains_inactive[i]); + domains_inactive[i] = NULL; + continue; } +#endif - /* Fetch each domain and add it to the list, unless ignore. */ - for (int i = 0; i < n; ++i) { - const char *name; - char *xml = NULL; - xmlDocPtr xml_doc = NULL; - xmlXPathContextPtr xpath_ctx = NULL; - xmlXPathObjectPtr xpath_obj = NULL; - char tag[PARTITION_TAG_MAX_LEN] = {'\0'}; - virDomainInfo info; - int status; + /* Fetch each domain and add it to the list, unless ignore. */ + for (int i = 0; i < n; ++i) { #ifdef HAVE_LIST_ALL_DOMAINS - virDomainPtr dom = domains[i]; + virDomainPtr dom = domains[i]; #else - virDomainPtr dom = NULL; - dom = virDomainLookupByID(conn, domids[i]); - if (dom == NULL) { - VIRT_ERROR(conn, "virDomainLookupByID"); - /* Could be that the domain went away -- ignore it anyway. */ - continue; - } + virDomainPtr dom = virDomainLookupByID(conn, domids[i]); + if (dom == NULL) { + VIRT_ERROR(conn, "virDomainLookupByID"); + /* Could be that the domain went away -- ignore it anyway. */ + continue; + } #endif - name = virDomainGetName(dom); - if (name == NULL) { - VIRT_ERROR(conn, "virDomainGetName"); - goto cont; - } - - status = virDomainGetInfo(dom, &info); - if (status != 0) { - ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.", - status); - continue; - } - - if (info.state != VIR_DOMAIN_RUNNING) { - DEBUG(PLUGIN_NAME " plugin: skipping inactive domain %s", name); - continue; - } - - if (il_domains && ignorelist_match(il_domains, name) != 0) - goto cont; - - /* Get a list of devices for this domain. */ - xml = virDomainGetXMLDesc(dom, 0); - if (!xml) { - VIRT_ERROR(conn, "virDomainGetXMLDesc"); - goto cont; - } - - /* Yuck, XML. Parse out the devices. */ - xml_doc = xmlReadDoc((xmlChar *)xml, NULL, NULL, XML_PARSE_NONET); - if (xml_doc == NULL) { - VIRT_ERROR(conn, "xmlReadDoc"); - goto cont; - } - - xpath_ctx = xmlXPathNewContext(xml_doc); - - if (lv_domain_get_tag(xpath_ctx, name, tag) < 0) { - ERROR(PLUGIN_NAME " plugin: lv_domain_get_tag failed."); - goto cont; - } - - if (!lv_instance_include_domain(inst, name, tag)) - goto cont; + if (add_domain(state, dom, 1) < 0) { + /* + * When domain is already tracked, then there is + * no problem with memory handling (will be freed + * with the rest of domains cached data) + * But in case of error like this (error occurred + * before adding domain to track) we have to take + * care it ourselves and call virDomainFree + */ + ERROR(PLUGIN_NAME " plugin: malloc failed."); + virDomainFree(dom); + continue; + } - if (add_domain(state, dom) < 0) { - ERROR(PLUGIN_NAME " plugin: malloc failed."); - goto cont; - } + const char *domname = virDomainGetName(dom); + if (domname == NULL) { + VIRT_ERROR(conn, "virDomainGetName"); + continue; + } - /* Block devices. */ - const char *bd_xmlpath = "/domain/devices/disk/target[@dev]"; - if (blockdevice_format == source) - bd_xmlpath = "/domain/devices/disk/source[@dev]"; - xpath_obj = xmlXPathEval((const xmlChar *)bd_xmlpath, xpath_ctx); + virDomainInfo info; + int status = virDomainGetInfo(dom, &info); + if (status != 0) { + ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.", + status); + continue; + } - if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET || - xpath_obj->nodesetval == NULL) - goto cont; + if (info.state != VIR_DOMAIN_RUNNING) { + DEBUG(PLUGIN_NAME " plugin: skipping inactive domain %s", domname); + continue; + } - for (int j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) { - xmlNodePtr node; - char *path = NULL; + if (ignorelist_match(il_domains, domname) != 0) + continue; - node = xpath_obj->nodesetval->nodeTab[j]; - if (!node) - continue; - path = (char *)xmlGetProp(node, (xmlChar *)"dev"); - if (!path) - continue; + /* Get a list of devices for this domain. */ + xmlDocPtr xml_doc = NULL; + xmlXPathContextPtr xpath_ctx = NULL; - if (il_block_devices && - ignore_device_match(il_block_devices, name, path) != 0) - goto cont2; + char *xml = virDomainGetXMLDesc(dom, 0); + if (!xml) { + VIRT_ERROR(conn, "virDomainGetXMLDesc"); + goto cont; + } - add_block_device(state, dom, path); - cont2: - if (path) - xmlFree(path); - } - xmlXPathFreeObject(xpath_obj); + /* Yuck, XML. Parse out the devices. */ + xml_doc = xmlReadDoc((xmlChar *)xml, NULL, NULL, XML_PARSE_NONET); + if (xml_doc == NULL) { + VIRT_ERROR(conn, "xmlReadDoc"); + goto cont; + } - /* Network interfaces. */ - xpath_obj = xmlXPathEval( - (xmlChar *)"/domain/devices/interface[target[@dev]]", xpath_ctx); - if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET || - xpath_obj->nodesetval == NULL) - goto cont; + xpath_ctx = xmlXPathNewContext(xml_doc); - xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval; + char tag[PARTITION_TAG_MAX_LEN] = {'\0'}; + if (lv_domain_get_tag(xpath_ctx, domname, tag) < 0) { + ERROR(PLUGIN_NAME " plugin: lv_domain_get_tag failed."); + goto cont; + } - for (int j = 0; j < xml_interfaces->nodeNr; ++j) { - char *path = NULL; - char *address = NULL; - xmlNodePtr xml_interface; + if (!lv_instance_include_domain(inst, domname, tag)) + goto cont; - xml_interface = xml_interfaces->nodeTab[j]; - if (!xml_interface) - continue; + /* Block devices. */ + if (report_block_devices) + lv_add_block_devices(state, dom, domname, xpath_ctx); - for (xmlNodePtr child = xml_interface->children; child; - child = child->next) { - if (child->type != XML_ELEMENT_NODE) - continue; - - if (xmlStrEqual(child->name, (const xmlChar *)"target")) { - path = (char *)xmlGetProp(child, (const xmlChar *)"dev"); - if (!path) - continue; - } else if (xmlStrEqual(child->name, (const xmlChar *)"mac")) { - address = (char *)xmlGetProp(child, (const xmlChar *)"address"); - if (!address) - continue; - } - } - - if (il_interface_devices && - (ignore_device_match(il_interface_devices, name, path) != 0 || - ignore_device_match(il_interface_devices, name, address) != 0)) - goto cont3; - - add_interface_device(state, dom, path, address, j + 1); - cont3: - if (path) - xmlFree(path); - if (address) - xmlFree(address); - } + /* Network interfaces. */ + if (report_network_interfaces) + lv_add_network_interfaces(state, dom, domname, xpath_ctx); - cont: - if (xpath_obj) - xmlXPathFreeObject(xpath_obj); - if (xpath_ctx) - xmlXPathFreeContext(xpath_ctx); - if (xml_doc) - xmlFreeDoc(xml_doc); - sfree(xml); - } + cont: + if (xpath_ctx) + xmlXPathFreeContext(xpath_ctx); + if (xml_doc) + xmlFreeDoc(xml_doc); + sfree(xml); + } #ifdef HAVE_LIST_ALL_DOMAINS - sfree(domains); + /* NOTE: domains_active and domains_inactive data will be cleared during + refresh of all domains (inside lv_clean_read_state function) so we need + to free here only allocated arrays */ + sfree(domains); + sfree(domains_inactive); #else - sfree(domids); + sfree(domids); + +end: #endif - } DEBUG(PLUGIN_NAME " plugin#%s: refreshing" " domains=%i block_devices=%i iface_devices=%i", @@ -2010,20 +2685,18 @@ static void free_domains(struct lv_read_state *state) { state->nr_domains = 0; } -static int add_domain(struct lv_read_state *state, virDomainPtr dom) { - domain_t *new_ptr; - int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1); +static int add_domain(struct lv_read_state *state, virDomainPtr dom, + bool active) { - 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; state->domains = new_ptr; state->domains[state->nr_domains].ptr = dom; + state->domains[state->nr_domains].active = active; memset(&state->domains[state->nr_domains].info, 0, sizeof(state->domains[state->nr_domains].info)); @@ -2042,20 +2715,15 @@ static void free_block_devices(struct lv_read_state *state) { 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; @@ -2082,61 +2750,62 @@ static void free_interface_devices(struct lv_read_state *state) { 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[15]; 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++; } static int ignore_device_match(ignorelist_t *il, const char *domname, const char *devpath) { - char *name; - int n, r; - if ((domname == NULL) || (devpath == NULL)) return 0; - n = strlen(domname) + strlen(devpath) + 2; - name = malloc(n); + size_t n = strlen(domname) + strlen(devpath) + 2; + char *name = malloc(n); if (name == NULL) { ERROR(PLUGIN_NAME " plugin: malloc failed."); return 0; } snprintf(name, n, "%s:%s", domname, devpath); - r = ignorelist_match(il, name); + int r = ignorelist_match(il, name); sfree(name); return r; } @@ -2146,6 +2815,11 @@ static int lv_shutdown(void) { lv_fini_instance(i); } + DEBUG(PLUGIN_NAME " plugin: stopping event loop"); + + if (!persistent_notification) + stop_event_loop(¬if_thread); + lv_disconnect(); ignorelist_free(il_domains); diff --git a/src/virt_test.c b/src/virt_test.c index 489a367d..458facaf 100644 --- a/src/virt_test.c +++ b/src/virt_test.c @@ -25,183 +25,71 @@ #include "testing.h" #include "virt.c" /* sic */ -#include +static virDomainPtr *domains; +static int nr_domains; -static const char minimal_xml[] = - "" - "" - "" - " " - ""; - -static const char minimal_metadata_xml[] = - "" - "" - "" - " " - " virt-0" - " " - ""; - -struct xml_state { - xmlDocPtr xml_doc; - xmlXPathContextPtr xpath_ctx; - xmlXPathObjectPtr xpath_obj; - char tag[PARTITION_TAG_MAX_LEN]; -}; - -static int init_state(struct xml_state *st, const char *xml) { - memset(st, 0, sizeof(*st)); - - st->xml_doc = xmlReadDoc((const xmlChar *)xml, NULL, NULL, XML_PARSE_NONET); - if (st->xml_doc == NULL) { - return -1; - } - st->xpath_ctx = xmlXPathNewContext(st->xml_doc); - if (st->xpath_ctx == NULL) { +static int setup(void) { + if (virInitialize() != 0) { + printf("ERROR: virInitialize() != 0\n"); return -1; } - return 0; -} -static void fini_state(struct xml_state *st) { - if (st->xpath_ctx) { - xmlXPathFreeContext(st->xpath_ctx); - st->xpath_ctx = NULL; - } - if (st->xml_doc) { - xmlFreeDoc(st->xml_doc); - st->xml_doc = NULL; + conn = virConnectOpen("test:///default"); + if (conn == NULL) { + printf("ERROR: virConnectOpen == NULL\n"); + return -1; } -} - -#define TAG "virt-0" - -DEF_TEST(lv_domain_get_tag_no_metadata_xml) { - int err; - struct xml_state st; - err = init_state(&st, minimal_xml); - EXPECT_EQ_INT(0, err); - - err = lv_domain_get_tag(st.xpath_ctx, "test", st.tag); - - EXPECT_EQ_INT(0, err); - EXPECT_EQ_STR("", st.tag); - - fini_state(&st); - return 0; -} - -DEF_TEST(lv_domain_get_tag_valid_xml) { - int err; - struct xml_state st; - err = init_state(&st, minimal_metadata_xml); - EXPECT_EQ_INT(0, err); - - err = lv_domain_get_tag(st.xpath_ctx, "test", st.tag); - - EXPECT_EQ_INT(0, err); - EXPECT_EQ_STR(TAG, st.tag); - - return 0; -} - -DEF_TEST(lv_default_instance_include_domain_without_tag) { - struct lv_read_instance *inst = NULL; - int ret; - - ret = lv_init_instance(0, lv_read); - inst = &(lv_read_user_data[0].inst); - EXPECT_EQ_STR("virt-0", inst->tag); - - ret = lv_instance_include_domain(inst, "testing", ""); - EXPECT_EQ_INT(1, ret); - - lv_fini_instance(0); - return 0; -} - -DEF_TEST(lv_regular_instance_skip_domain_without_tag) { - struct lv_read_instance *inst = NULL; - int ret; - - ret = lv_init_instance(1, lv_read); - inst = &(lv_read_user_data[1].inst); - EXPECT_EQ_STR("virt-1", inst->tag); - ret = lv_instance_include_domain(inst, "testing", ""); - EXPECT_EQ_INT(0, ret); - - lv_fini_instance(0); return 0; } -DEF_TEST(lv_include_domain_matching_tags) { - struct lv_read_instance *inst = NULL; - int ret; - - ret = lv_init_instance(0, lv_read); - inst = &(lv_read_user_data[0].inst); - EXPECT_EQ_STR("virt-0", inst->tag); - - ret = lv_instance_include_domain(inst, "testing", "virt-0"); - EXPECT_EQ_INT(1, ret); - - ret = lv_init_instance(1, lv_read); - inst = &(lv_read_user_data[1].inst); - EXPECT_EQ_STR("virt-1", inst->tag); - - ret = lv_instance_include_domain(inst, "testing", "virt-1"); - EXPECT_EQ_INT(1, ret); +static int teardown(void) { + if (domains) { + for (int i = 0; i < nr_domains; ++i) + virDomainFree(domains[i]); + sfree(domains); + } + nr_domains = 0; + if (conn != NULL) + virConnectClose(conn); - lv_fini_instance(0); - lv_fini_instance(1); return 0; } -DEF_TEST(lv_default_instance_include_domain_with_unknown_tag) { - struct lv_read_instance *inst = NULL; - int ret; - - ret = lv_init_instance(0, lv_read); - inst = &(lv_read_user_data[0].inst); - EXPECT_EQ_STR("virt-0", inst->tag); - - ret = lv_instance_include_domain(inst, "testing", "unknownFormat-tag"); - EXPECT_EQ_INT(1, ret); +#ifdef HAVE_LIST_ALL_DOMAINS +DEF_TEST(get_domain_state_notify) { + if (setup() == 0) { + nr_domains = virConnectListAllDomains(conn, &domains, + VIR_CONNECT_LIST_DOMAINS_PERSISTENT); + if (nr_domains <= 0) { + printf("ERROR: virConnectListAllDomains: nr_domains <= 0\n"); + return -1; + } + + int ret = get_domain_state_notify(domains[0]); + EXPECT_EQ_INT(0, ret); + } + teardown(); - lv_fini_instance(0); return 0; } +#endif -DEF_TEST(lv_regular_instance_skip_domain_with_unknown_tag) { - struct lv_read_instance *inst = NULL; - int ret; - - ret = lv_init_instance(1, lv_read); - inst = &(lv_read_user_data[1].inst); - EXPECT_EQ_STR("virt-1", inst->tag); - - ret = lv_instance_include_domain(inst, "testing", "unknownFormat-tag"); - EXPECT_EQ_INT(0, ret); +DEF_TEST(persistent_domains_state_notification) { + if (setup() == 0) { + int ret = persistent_domains_state_notification(); + EXPECT_EQ_INT(0, ret); + } + teardown(); - lv_fini_instance(0); return 0; } -#undef TAG int main(void) { - RUN_TEST(lv_domain_get_tag_no_metadata_xml); - RUN_TEST(lv_domain_get_tag_valid_xml); - - RUN_TEST(lv_default_instance_include_domain_without_tag); - RUN_TEST(lv_regular_instance_skip_domain_without_tag); - RUN_TEST(lv_include_domain_matching_tags); - RUN_TEST(lv_default_instance_include_domain_with_unknown_tag); - RUN_TEST(lv_regular_instance_skip_domain_with_unknown_tag); +#ifdef HAVE_LIST_ALL_DOMAINS + RUN_TEST(get_domain_state_notify); +#endif + RUN_TEST(persistent_domains_state_notification); END_TEST; } - -/* vim: set sw=2 sts=2 et : */ diff --git a/src/vmem.c b/src/vmem.c index ffe1826a..c7229756 100644 --- a/src/vmem.c +++ b/src/vmem.c @@ -33,7 +33,7 @@ static const char *config_keys[] = {"Verbose"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); -static int verbose_output = 0; +static int verbose_output; /* #endif KERNEL_LINUX */ #else @@ -104,9 +104,7 @@ static int vmem_read(void) { fh = fopen("/proc/vmstat", "r"); if (fh == NULL) { - char errbuf[1024]; - ERROR("vmem plugin: fopen (/proc/vmstat) failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("vmem plugin: fopen (/proc/vmstat) failed: %s", STRERRNO); return -1; } diff --git a/src/vserver.c b/src/vserver.c index 6f9d46bf..3c6d58cd 100644 --- a/src/vserver.c +++ b/src/vserver.c @@ -42,7 +42,7 @@ #error "No applicable input method." #endif -static int pagesize = 0; +static int pagesize; static int vserver_init(void) { /* XXX Should we check for getpagesize () in configure? @@ -124,9 +124,7 @@ static int vserver_read(void) { errno = 0; proc = opendir(PROCDIR); if (proc == NULL) { - char errbuf[1024]; - ERROR("vserver plugin: fopen (%s): %s", PROCDIR, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("vserver plugin: fopen (%s): %s", PROCDIR, STRERRNO); return -1; } @@ -146,13 +144,11 @@ static int vserver_read(void) { errno = 0; dent = readdir(proc); if (dent == NULL) { - char errbuf[4096]; - if (errno == 0) /* end of directory */ break; ERROR("vserver plugin: failed to read directory %s: %s", PROCDIR, - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); closedir(proc); return -1; } @@ -166,9 +162,7 @@ static int vserver_read(void) { status = stat(file, &statbuf); if (status != 0) { - char errbuf[4096]; - WARNING("vserver plugin: stat (%s) failed: %s", file, - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("vserver plugin: stat (%s) failed: %s", file, STRERRNO); continue; } @@ -181,9 +175,7 @@ static int vserver_read(void) { continue; if (NULL == (fh = fopen(file, "r"))) { - char errbuf[1024]; - ERROR("Cannot open '%s': %s", file, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("Cannot open '%s': %s", file, STRERRNO); } while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) { @@ -225,9 +217,7 @@ static int vserver_read(void) { continue; if (NULL == (fh = fopen(file, "r"))) { - char errbuf[1024]; - ERROR("Cannot open '%s': %s", file, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("Cannot open '%s': %s", file, STRERRNO); } while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) { @@ -271,9 +261,7 @@ static int vserver_read(void) { continue; if (NULL == (fh = fopen(file, "r"))) { - char errbuf[1024]; - ERROR("Cannot open '%s': %s", file, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("Cannot open '%s': %s", file, STRERRNO); } while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) { diff --git a/src/wireless.c b/src/wireless.c index 5552be64..4208d366 100644 --- a/src/wireless.c +++ b/src/wireless.c @@ -1,6 +1,6 @@ /** * 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"), @@ -29,7 +29,11 @@ #include "common.h" #include "plugin.h" -#if !KERNEL_LINUX +#if KERNEL_LINUX +#include +#include +#include +#else #error "No applicable input method." #endif @@ -86,12 +90,18 @@ static int wireless_read(void) { int numfields; int devices_found; - int len; + size_t len; /* there are a variety of names for the wireless device */ if ((fh = fopen(WIRELESS_PROC_FILE, "r")) == NULL) { - char errbuf[1024]; - WARNING("wireless: fopen: %s", sstrerror(errno, errbuf, sizeof(errbuf))); + 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; } @@ -143,9 +153,20 @@ static int wireless_read(void) { 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 diff --git a/src/write_graphite.c b/src/write_graphite.c index 7d7c0e8f..7624e243 100644 --- a/src/write_graphite.c +++ b/src/write_graphite.c @@ -38,6 +38,7 @@ * Protocol "udp" * LogSendErrors true * Prefix "collectd" + * UseTags true * * */ @@ -65,7 +66,7 @@ #endif #ifndef WG_DEFAULT_LOG_SEND_ERRORS -#define WG_DEFAULT_LOG_SEND_ERRORS 1 +#define WG_DEFAULT_LOG_SEND_ERRORS true #endif #ifndef WG_DEFAULT_ESCAPE @@ -92,7 +93,7 @@ struct wg_callback { char *node; char *service; char *protocol; - _Bool log_send_errors; + bool log_send_errors; char *prefix; char *postfix; char escape_char; @@ -111,7 +112,7 @@ struct wg_callback { /* Force reconnect useful for load balanced environments */ cdtime_t last_reconnect_time; cdtime_t reconnect_interval; - _Bool reconnect_interval_reached; + bool reconnect_interval_reached; }; /* wg_force_reconnect_check closes cb->sock_fd when it was open for longer @@ -131,7 +132,7 @@ static void wg_force_reconnect_check(struct wg_callback *cb) { close(cb->sock_fd); cb->sock_fd = -1; cb->last_reconnect_time = now; - cb->reconnect_interval_reached = 1; + cb->reconnect_interval_reached = true; INFO("write_graphite plugin: Connection closed after %.3f seconds.", CDTIME_T_TO_DOUBLE(now - cb->last_reconnect_time)); @@ -156,11 +157,9 @@ static int wg_send_buffer(struct wg_callback *cb) { status = swrite(cb->sock_fd, cb->send_buf, strlen(cb->send_buf)); if (status != 0) { if (cb->log_send_errors) { - char errbuf[1024]; ERROR("write_graphite plugin: send to %s:%s (%s) failed with status %zi " "(%s)", - cb->node, cb->service, cb->protocol, status, - sstrerror(errno, errbuf, sizeof(errbuf))); + cb->node, cb->service, cb->protocol, status, STRERRNO); } close(cb->sock_fd); @@ -177,7 +176,7 @@ static int wg_flush_nolock(cdtime_t timeout, struct wg_callback *cb) { int status; DEBUG("write_graphite plugin: wg_flush_nolock: timeout = %.3f; " - "send_buf_fill = %zu;", + "send_buf_fill = %" PRIsz ";", (double)timeout, cb->send_buf_fill); /* timeout == 0 => flush unconditionally */ @@ -238,9 +237,7 @@ static int wg_callback_init(struct wg_callback *cb) { cb->sock_fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); if (cb->sock_fd < 0) { - char errbuf[1024]; - snprintf(connerr, sizeof(connerr), "failed to open socket: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + snprintf(connerr, sizeof(connerr), "failed to open socket: %s", STRERRNO); continue; } @@ -248,10 +245,8 @@ static int wg_callback_init(struct wg_callback *cb) { status = connect(cb->sock_fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen); if (status != 0) { - char errbuf[1024]; - snprintf(connerr, sizeof(connerr), "failed to connect to remote " - "host: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + snprintf(connerr, sizeof(connerr), "failed to connect to remote host: %s", + STRERRNO); close(cb->sock_fd); cb->sock_fd = -1; continue; @@ -263,9 +258,6 @@ static int wg_callback_init(struct wg_callback *cb) { freeaddrinfo(ai_list); if (cb->sock_fd < 0) { - if (connerr[0] == '\0') - /* this should not happen but try to get a message anyway */ - sstrerror(errno, connerr, sizeof(connerr)); c_complain(LOG_ERR, &cb->init_complaint, "write_graphite plugin: Connecting to %s:%s via %s failed. " "The last error was: %s", @@ -283,7 +275,7 @@ static int wg_callback_init(struct wg_callback *cb) { if (!cb->reconnect_interval_reached || (cb->send_buf_free == 0)) wg_reset_buffer(cb); else - cb->reconnect_interval_reached = 0; + cb->reconnect_interval_reached = false; return 0; } @@ -382,7 +374,8 @@ static int wg_send_message(char const *message, struct wg_callback *cb) { cb->send_buf_fill += message_len; cb->send_buf_free -= message_len; - DEBUG("write_graphite plugin: [%s]:%s (%s) buf %zu/%zu (%.1f %%) \"%s\"", + DEBUG("write_graphite plugin: [%s]:%s (%s) buf %" PRIsz "/%" PRIsz + " (%.1f %%) \"%s\"", cb->node, cb->service, cb->protocol, cb->send_buf_fill, sizeof(cb->send_buf), 100.0 * ((double)cb->send_buf_fill) / ((double)sizeof(cb->send_buf)), @@ -474,7 +467,7 @@ static int wg_config_node(oconfig_item_t *ci) { cb->protocol = strdup(WG_DEFAULT_PROTOCOL); cb->last_reconnect_time = cdtime(); cb->reconnect_interval = 0; - cb->reconnect_interval_reached = 0; + cb->reconnect_interval_reached = false; cb->log_send_errors = WG_DEFAULT_LOG_SEND_ERRORS; cb->prefix = NULL; cb->postfix = NULL; @@ -526,6 +519,8 @@ static int wg_config_node(oconfig_item_t *ci) { cf_util_get_flag(child, &cb->format_flags, GRAPHITE_PRESERVE_SEPARATOR); else if (strcasecmp("DropDuplicateFields", child->key) == 0) cf_util_get_flag(child, &cb->format_flags, GRAPHITE_DROP_DUPE_FIELDS); + else if (strcasecmp("UseTags", child->key) == 0) + cf_util_get_flag(child, &cb->format_flags, GRAPHITE_USE_TAGS); else if (strcasecmp("EscapeCharacter", child->key) == 0) config_set_char(&cb->escape_char, child); else { diff --git a/src/write_http.c b/src/write_http.c index 87e518b6..ad0cb5e4 100644 --- a/src/write_http.c +++ b/src/write_http.c @@ -50,16 +50,16 @@ struct wh_callback_s { char *user; char *pass; char *credentials; - _Bool verify_peer; - _Bool verify_host; + bool verify_peer; + bool verify_host; char *cacert; char *capath; char *clientkey; char *clientcert; char *clientkeypass; long sslversion; - _Bool store_rates; - _Bool log_http_error; + bool store_rates; + bool log_http_error; int low_speed_limit; time_t low_speed_time; int timeout; @@ -68,8 +68,8 @@ struct wh_callback_s { #define WH_FORMAT_JSON 1 #define WH_FORMAT_KAIROSDB 2 int format; - _Bool send_metrics; - _Bool send_notifications; + bool send_metrics; + bool send_notifications; CURL *curl; struct curl_slist *headers; @@ -228,7 +228,7 @@ static int wh_flush_nolock(cdtime_t timeout, wh_callback_t *cb) /* {{{ */ int status; DEBUG("write_http plugin: wh_flush_nolock: timeout = %.3f; " - "send_buffer_fill = %zu;", + "send_buffer_fill = %" PRIsz ";", CDTIME_T_TO_DOUBLE(timeout), cb->send_buffer_fill); /* timeout == 0 => flush unconditionally */ @@ -380,7 +380,7 @@ static int wh_write_command(const data_set_t *ds, CDTIME_T_TO_DOUBLE(vl->interval), values); if (command_len >= sizeof(command)) { ERROR("write_http plugin: Command buffer too small: " - "Need %zu bytes.", + "Need %" PRIsz " bytes.", command_len + 1); return -1; } @@ -410,8 +410,8 @@ static int wh_write_command(const data_set_t *ds, cb->send_buffer_fill += command_len; cb->send_buffer_free -= command_len; - DEBUG("write_http plugin: <%s> buffer %zu/%zu (%g%%) \"%s\"", cb->location, - cb->send_buffer_fill, cb->send_buffer_size, + DEBUG("write_http plugin: <%s> buffer %" PRIsz "/%" PRIsz " (%g%%) \"%s\"", + cb->location, cb->send_buffer_fill, cb->send_buffer_size, 100.0 * ((double)cb->send_buffer_fill) / ((double)cb->send_buffer_size), command); @@ -452,8 +452,8 @@ static int wh_write_json(const data_set_t *ds, const value_list_t *vl, /* {{{ */ return status; } - DEBUG("write_http plugin: <%s> buffer %zu/%zu (%g%%)", cb->location, - cb->send_buffer_fill, cb->send_buffer_size, + DEBUG("write_http plugin: <%s> buffer %" PRIsz "/%" PRIsz " (%g%%)", + cb->location, cb->send_buffer_fill, cb->send_buffer_size, 100.0 * ((double)cb->send_buffer_fill) / ((double)cb->send_buffer_size)); @@ -501,8 +501,8 @@ static int wh_write_kairosdb(const data_set_t *ds, return status; } - DEBUG("write_http plugin: <%s> buffer %zu/%zu (%g%%)", cb->location, - cb->send_buffer_fill, cb->send_buffer_size, + DEBUG("write_http plugin: <%s> buffer %" PRIsz "/%" PRIsz " (%g%%)", + cb->location, cb->send_buffer_fill, cb->send_buffer_size, 100.0 * ((double)cb->send_buffer_fill) / ((double)cb->send_buffer_size)); @@ -624,16 +624,16 @@ static int wh_config_node(oconfig_item_t *ci) /* {{{ */ ERROR("write_http plugin: calloc failed."); return -1; } - cb->verify_peer = 1; - cb->verify_host = 1; + cb->verify_peer = true; + cb->verify_host = true; cb->format = WH_FORMAT_COMMAND; cb->sslversion = CURL_SSLVERSION_DEFAULT; cb->low_speed_limit = 0; cb->timeout = 0; - cb->log_http_error = 0; + cb->log_http_error = false; cb->headers = NULL; - cb->send_metrics = 1; - cb->send_notifications = 0; + cb->send_metrics = true; + cb->send_notifications = false; cb->data_ttl = 0; cb->metrics_prefix = strdup(WRITE_HTTP_DEFAULT_PREFIX); @@ -802,7 +802,8 @@ static int wh_config_node(oconfig_item_t *ci) /* {{{ */ /* Allocate the buffer. */ cb->send_buffer = malloc(cb->send_buffer_size); if (cb->send_buffer == NULL) { - ERROR("write_http plugin: malloc(%zu) failed.", cb->send_buffer_size); + ERROR("write_http plugin: malloc(%" PRIsz ") failed.", + cb->send_buffer_size); wh_callback_free(cb); return -1; } diff --git a/src/write_kafka.c b/src/write_kafka.c index 2baaf0e5..04e67b92 100644 --- a/src/write_kafka.c +++ b/src/write_kafka.c @@ -43,7 +43,7 @@ struct kafka_topic_context { #define KAFKA_FORMAT_GRAPHITE 2 uint8_t format; unsigned int graphite_flags; - _Bool store_rates; + bool store_rates; rd_kafka_topic_conf_t *conf; rd_kafka_topic_t *topic; rd_kafka_conf_t *kafka_conf; @@ -273,7 +273,7 @@ static void kafka_config_topic(rd_kafka_conf_t *conf, } tctx->escape_char = '.'; - tctx->store_rates = 1; + tctx->store_rates = true; tctx->format = KAFKA_FORMAT_JSON; tctx->key = NULL; @@ -383,6 +383,10 @@ static void kafka_config_topic(rd_kafka_conf_t *conf, status = cf_util_get_flag(child, &tctx->graphite_flags, GRAPHITE_PRESERVE_SEPARATOR); + } else if (strcasecmp("GraphiteUseTags", child->key) == 0) { + status = + cf_util_get_flag(child, &tctx->graphite_flags, GRAPHITE_USE_TAGS); + } else if (strcasecmp("GraphitePrefix", child->key) == 0) { status = cf_util_get_string(child, &tctx->prefix); } else if (strcasecmp("GraphitePostfix", child->key) == 0) { diff --git a/src/write_log.c b/src/write_log.c index fdc99ef3..52ad6104 100644 --- a/src/write_log.c +++ b/src/write_log.c @@ -96,7 +96,7 @@ static int wl_write(const data_set_t *ds, const value_list_t *vl, static int wl_config(oconfig_item_t *ci) /* {{{ */ { - _Bool format_seen = 0; + bool format_seen = false; for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; @@ -110,7 +110,7 @@ static int wl_config(oconfig_item_t *ci) /* {{{ */ if (format_seen) { WARNING("write_log plugin: Redefining option `%s'.", child->key); } - format_seen = 1; + format_seen = true; if (strcasecmp("Graphite", str) == 0) wl_format = WL_FORMAT_GRAPHITE; diff --git a/src/write_mongodb.c b/src/write_mongodb.c index 46b6d865..9cddc916 100644 --- a/src/write_mongodb.c +++ b/src/write_mongodb.c @@ -50,8 +50,8 @@ struct wm_node_s { char *user; char *passwd; - _Bool store_rates; - _Bool connected; + bool store_rates; + bool connected; mongoc_client_t *client; mongoc_database_t *database; @@ -63,7 +63,7 @@ typedef struct wm_node_s wm_node_t; * Functions */ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */ - const value_list_t *vl, _Bool store_rates) { + const value_list_t *vl, bool store_rates) { bson_t *ret; bson_t subarray; gauge_t *rates; @@ -96,7 +96,7 @@ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */ for (size_t i = 0; i < ds->ds_num; i++) { char key[16]; - snprintf(key, sizeof(key), "%zu", i); + snprintf(key, sizeof(key), "%" PRIsz, i); if (ds->ds[i].type == DS_TYPE_GAUGE) BSON_APPEND_DOUBLE(&subarray, key, vl->values[i].gauge); @@ -109,7 +109,7 @@ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */ else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) BSON_APPEND_INT64(&subarray, key, vl->values[i].absolute); else { - ERROR("write_mongodb plugin: Unknown ds_type %d for index %zu", + ERROR("write_mongodb plugin: Unknown ds_type %d for index %" PRIsz, ds->ds[i].type, i); bson_destroy(ret); return NULL; @@ -121,7 +121,7 @@ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */ for (size_t i = 0; i < ds->ds_num; i++) { char key[16]; - snprintf(key, sizeof(key), "%zu", i); + snprintf(key, sizeof(key), "%" PRIsz, i); if (store_rates) BSON_APPEND_UTF8(&subarray, key, "gauge"); @@ -134,7 +134,7 @@ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */ for (size_t i = 0; i < ds->ds_num; i++) { char key[16]; - snprintf(key, sizeof(key), "%zu", i); + snprintf(key, sizeof(key), "%" PRIsz, i); BSON_APPEND_UTF8(&subarray, key, ds->ds[i].name); } bson_append_array_end(ret, &subarray); /* }}} dsnames */ @@ -144,7 +144,7 @@ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */ size_t error_location; if (!bson_validate(ret, BSON_VALIDATE_UTF8, &error_location)) { ERROR("write_mongodb plugin: Error in generated BSON document " - "at byte %zu", + "at byte %" PRIsz, error_location); bson_destroy(ret); return NULL; @@ -170,7 +170,7 @@ static int wm_initialize(wm_node_t *node) /* {{{ */ "authentication string."); mongoc_client_destroy(node->client); node->client = NULL; - node->connected = 0; + node->connected = false; return -1; } @@ -179,7 +179,7 @@ static int wm_initialize(wm_node_t *node) /* {{{ */ ERROR("write_mongodb plugin: Authenticating to [%s]:%d for database " "\"%s\" as user \"%s\" failed.", node->host, node->port, node->db, node->user); - node->connected = 0; + node->connected = false; sfree(uri); return -1; } @@ -190,7 +190,7 @@ static int wm_initialize(wm_node_t *node) /* {{{ */ "authentication string."); mongoc_client_destroy(node->client); node->client = NULL; - node->connected = 0; + node->connected = false; return -1; } @@ -198,7 +198,7 @@ static int wm_initialize(wm_node_t *node) /* {{{ */ if (!node->client) { ERROR("write_mongodb plugin: Connecting to [%s]:%d failed.", node->host, node->port); - node->connected = 0; + node->connected = false; sfree(uri); return -1; } @@ -210,11 +210,11 @@ static int wm_initialize(wm_node_t *node) /* {{{ */ ERROR("write_mongodb plugin: error creating/getting database"); mongoc_client_destroy(node->client); node->client = NULL; - node->connected = 0; + node->connected = false; return -1; } - node->connected = 1; + node->connected = true; return 0; } /* }}} int wm_initialize */ @@ -248,7 +248,7 @@ static int wm_write(const data_set_t *ds, /* {{{ */ mongoc_client_destroy(node->client); node->database = NULL; node->client = NULL; - node->connected = 0; + node->connected = false; pthread_mutex_unlock(&node->lock); bson_destroy(bson_record); return -1; @@ -263,7 +263,7 @@ static int wm_write(const data_set_t *ds, /* {{{ */ mongoc_client_destroy(node->client); node->database = NULL; node->client = NULL; - node->connected = 0; + node->connected = false; pthread_mutex_unlock(&node->lock); bson_destroy(bson_record); mongoc_collection_destroy(collection); @@ -291,7 +291,7 @@ static void wm_config_free(void *ptr) /* {{{ */ mongoc_client_destroy(node->client); node->database = NULL; node->client = NULL; - node->connected = 0; + node->connected = false; sfree(node->host); sfree(node); @@ -312,7 +312,7 @@ static int wm_config_node(oconfig_item_t *ci) /* {{{ */ return ENOMEM; } node->port = MONGOC_DEFAULT_PORT; - node->store_rates = 1; + node->store_rates = true; pthread_mutex_init(&node->lock, /* attr = */ NULL); status = cf_util_get_string_buffer(ci, node->name, sizeof(node->name)); diff --git a/src/write_prometheus.c b/src/write_prometheus.c index 8554580f..3b32ac09 100644 --- a/src/write_prometheus.c +++ b/src/write_prometheus.c @@ -54,12 +54,13 @@ 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; static cdtime_t staleness_delta = PROMETHEUS_DEFAULT_STALENESS_DELTA; -/* Unfortunately, protoc-c doesn't export it's implementation of varint, so we +/* Unfortunately, protoc-c doesn't export its implementation of varint, so we * need to implement our own. */ static size_t varint(uint8_t buffer[static VARINT_UINT32_BYTES], uint32_t value) { @@ -244,9 +245,8 @@ static int http_handler(void *cls, struct MHD_Connection *connection, char const *accept = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT); - _Bool want_proto = - (accept != NULL) && - (strstr(accept, "application/vnd.google.protobuf") != NULL); + bool want_proto = (accept != NULL) && + (strstr(accept, "application/vnd.google.protobuf") != NULL); uint8_t scratch[4096] = {0}; ProtobufCBufferSimple simple = PROTOBUF_C_BUFFER_SIMPLE_INIT(scratch); @@ -689,7 +689,7 @@ static char *metric_family_name(data_set_t const *ds, value_list_t const *vl, * necessary. */ static Io__Prometheus__Client__MetricFamily * metric_family_get(data_set_t const *ds, value_list_t const *vl, size_t ds_index, - _Bool allocate) { + bool allocate) { char *name = metric_family_name(ds, vl, ds_index); if (name == NULL) { ERROR("write_prometheus plugin: Allocating metric family name failed."); @@ -747,7 +747,7 @@ static int prom_open_socket(int addrfamily) { 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, @@ -769,11 +769,9 @@ static int prom_open_socket(int addrfamily) { if (fd == -1) continue; - int tmp = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)) != 0) { - char errbuf[1024]; - WARNING("write_prometheus: setsockopt(SO_REUSEADDR) failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + 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; @@ -791,6 +789,15 @@ static int prom_open_socket(int addrfamily) { 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; } @@ -805,7 +812,9 @@ static struct MHD_Daemon *prom_start_daemon() { 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; } @@ -852,7 +861,15 @@ static int prom_config(oconfig_item_t *ci) { 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; @@ -880,7 +897,6 @@ static int prom_init() { 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", @@ -896,7 +912,7 @@ static int prom_write(data_set_t const *ds, value_list_t const *vl, for (size_t i = 0; i < ds->ds_num; i++) { Io__Prometheus__Client__MetricFamily *fam = - metric_family_get(ds, vl, i, /* allocate = */ 1); + metric_family_get(ds, vl, i, /* allocate = */ true); if (fam == NULL) continue; @@ -923,7 +939,7 @@ static int prom_missing(value_list_t const *vl, for (size_t i = 0; i < ds->ds_num; i++) { Io__Prometheus__Client__MetricFamily *fam = - metric_family_get(ds, vl, i, /* allocate = */ 0); + metric_family_get(ds, vl, i, /* allocate = */ false); if (fam == NULL) continue; @@ -973,6 +989,8 @@ static int prom_shutdown() { } pthread_mutex_unlock(&metrics_lock); + sfree(httpd_host); + return 0; } diff --git a/src/write_redis.c b/src/write_redis.c index 7dd5029c..72cb5946 100644 --- a/src/write_redis.c +++ b/src/write_redis.c @@ -46,7 +46,7 @@ struct wr_node_s { int database; int max_set_size; int max_set_duration; - _Bool store_rates; + bool store_rates; redisContext *conn; pthread_mutex_t lock; @@ -129,10 +129,10 @@ static int wr_write(const data_set_t *ds, /* {{{ */ if (node->max_set_duration > 0) { /* * remove element, scored less than 'current-max_set_duration' - * '(%d' indicates 'less than' in redis CLI. + * '(...' indicates 'less than' in redis CLI. */ - rr = redisCommand(node->conn, "ZREMRANGEBYSCORE %s -1 (%d", key, - (time - node->max_set_duration) + 1); + rr = redisCommand(node->conn, "ZREMRANGEBYSCORE %s -1 (%.9f", key, + (CDTIME_T_TO_DOUBLE(vl->time) - node->max_set_duration)); if (rr == NULL) WARNING("ZREMRANGEBYSCORE command error. key:%s message:%s", key, node->conn->errstr); @@ -184,14 +184,14 @@ static int wr_config_node(oconfig_item_t *ci) /* {{{ */ return ENOMEM; node->host = NULL; node->port = 0; - node->timeout.tv_sec = 0; - node->timeout.tv_usec = 1000; + node->timeout.tv_sec = 1; + node->timeout.tv_usec = 0; node->conn = NULL; node->prefix = NULL; node->database = 0; node->max_set_size = -1; node->max_set_duration = -1; - node->store_rates = 1; + node->store_rates = true; pthread_mutex_init(&node->lock, /* attr = */ NULL); status = cf_util_get_string_buffer(ci, node->name, sizeof(node->name)); @@ -213,8 +213,11 @@ static int wr_config_node(oconfig_item_t *ci) /* {{{ */ } } else if (strcasecmp("Timeout", child->key) == 0) { status = cf_util_get_int(child, &timeout); - if (status == 0) - node->timeout.tv_usec = timeout; + if (status == 0) { + node->timeout.tv_usec = timeout * 1000; + node->timeout.tv_sec = node->timeout.tv_usec / 1000000L; + node->timeout.tv_usec %= 1000000L; + } } else if (strcasecmp("Prefix", child->key) == 0) { status = cf_util_get_string(child, &node->prefix); } else if (strcasecmp("Database", child->key) == 0) { diff --git a/src/write_riemann.c b/src/write_riemann.c index 86f0c1fa..b35d10ee 100644 --- a/src/write_riemann.c +++ b/src/write_riemann.c @@ -48,11 +48,11 @@ struct riemann_host { char *name; char *event_service_prefix; pthread_mutex_t lock; - _Bool batch_mode; - _Bool notifications; - _Bool check_thresholds; - _Bool store_rates; - _Bool always_append_ds; + bool batch_mode; + bool notifications; + bool check_thresholds; + bool store_rates; + bool always_append_ds; char *node; int port; riemann_client_type_t client_type; @@ -181,9 +181,7 @@ static int wrr_send(struct riemann_host *host, riemann_message_t *msg) { return status; } -static riemann_message_t * -wrr_notification_to_message(struct riemann_host *host, /* {{{ */ - notification_t const *n) { +static riemann_message_t *wrr_notification_to_message(notification_t const *n) { riemann_message_t *msg; riemann_event_t *event; char service_buffer[6 * DATA_MAX_NAME_LEN]; @@ -271,7 +269,7 @@ wrr_notification_to_message(struct riemann_host *host, /* {{{ */ "host = \"%s\", service = \"%s\", state = \"%s\"", event->host, event->service, event->state); return msg; -} /* }}} riemann_message_t *wrr_notification_to_message */ +} static riemann_event_t * wrr_value_to_event(struct riemann_host const *host, /* {{{ */ @@ -362,7 +360,7 @@ wrr_value_to_event(struct riemann_host const *host, /* {{{ */ { char ds_index[DATA_MAX_NAME_LEN]; - snprintf(ds_index, sizeof(ds_index), "%zu", index); + snprintf(ds_index, sizeof(ds_index), "%" PRIsz, index); riemann_event_string_attribute_add(event, "ds_index", ds_index); } @@ -546,7 +544,7 @@ static int wrr_notification(const notification_t *n, user_data_t *ud) /* {{{ */ /* * Never batch for notifications, send them ASAP */ - msg = wrr_notification_to_message(host, n); + msg = wrr_notification_to_message(n); if (msg == NULL) return -1; @@ -632,11 +630,11 @@ static int wrr_config_node(oconfig_item_t *ci) /* {{{ */ host->reference_count = 1; host->node = NULL; host->port = 0; - host->notifications = 1; - host->check_thresholds = 0; - host->store_rates = 1; - host->always_append_ds = 0; - host->batch_mode = 1; + host->notifications = true; + host->check_thresholds = false; + host->store_rates = true; + host->always_append_ds = false; + host->batch_mode = true; host->batch_max = RIEMANN_BATCH_MAX; /* typical MSS */ host->batch_init = cdtime(); host->batch_timeout = 0; @@ -701,21 +699,13 @@ static int wrr_config_node(oconfig_item_t *ci) /* {{{ */ } else if (strcasecmp("Port", child->key) == 0) { host->port = cf_util_get_port_number(child); if (host->port == -1) { - ERROR("write_riemann plugin: Invalid argument " - "configured for the \"Port\" " - "option."); break; } } else if (strcasecmp("Protocol", child->key) == 0) { char tmp[16]; status = cf_util_get_string_buffer(child, tmp, sizeof(tmp)); - if (status != 0) { - ERROR("write_riemann plugin: cf_util_get_" - "string_buffer failed with " - "status %i.", - status); + if (status != 0) break; - } if (strcasecmp("UDP", tmp) == 0) host->client_type = RIEMANN_CLIENT_UDP; @@ -731,31 +721,16 @@ static int wrr_config_node(oconfig_item_t *ci) /* {{{ */ tmp); } else if (strcasecmp("TLSCAFile", child->key) == 0) { status = cf_util_get_string(child, &host->tls_ca_file); - if (status != 0) { - ERROR("write_riemann plugin: cf_util_get_" - "string_buffer failed with " - "status %i.", - status); + if (status != 0) break; - } } else if (strcasecmp("TLSCertFile", child->key) == 0) { status = cf_util_get_string(child, &host->tls_cert_file); - if (status != 0) { - ERROR("write_riemann plugin: cf_util_get_" - "string_buffer failed with " - "status %i.", - status); + if (status != 0) break; - } } else if (strcasecmp("TLSKeyFile", child->key) == 0) { status = cf_util_get_string(child, &host->tls_key_file); - if (status != 0) { - ERROR("write_riemann plugin: cf_util_get_" - "string_buffer failed with " - "status %i.", - status); + if (status != 0) break; - } } else if (strcasecmp("StoreRates", child->key) == 0) { status = cf_util_get_boolean(child, &host->store_rates); if (status != 0) diff --git a/src/write_riemann_threshold.c b/src/write_riemann_threshold.c index 35f3814a..9d8267dc 100644 --- a/src/write_riemann_threshold.c +++ b/src/write_riemann_threshold.c @@ -63,7 +63,7 @@ static int ut_check_one_data_source( 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) { @@ -73,8 +73,9 @@ static int ut_check_one_data_source( /* 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) && diff --git a/src/write_sensu.c b/src/write_sensu.c index bd7a56d0..6ea8106c 100644 --- a/src/write_sensu.c +++ b/src/write_sensu.c @@ -31,7 +31,6 @@ #include "common.h" #include "plugin.h" #include "utils_cache.h" - #include #include #include @@ -108,10 +107,10 @@ struct sensu_host { #define F_READY 0x01 uint8_t flags; pthread_mutex_t lock; - _Bool notifications; - _Bool metrics; - _Bool store_rates; - _Bool always_append_ds; + bool notifications; + bool metrics; + bool store_rates; + bool always_append_ds; char *separator; char *node; char *service; @@ -120,8 +119,8 @@ struct sensu_host { int reference_count; }; -static char *sensu_tags = NULL; -static char **sensu_attrs = NULL; +static char *sensu_tags; +static char **sensu_attrs; static size_t sensu_attrs_num; static int add_str_to_list(struct str_list *strs, @@ -310,8 +309,8 @@ static int sensu_format_name2(char *ret, int ret_len, const char *hostname, static void in_place_replace_sensu_name_reserved(char *orig_name) /* {{{ */ { - int len = strlen(orig_name); - for (int i = 0; i < len; i++) { + size_t len = strlen(orig_name); + for (size_t i = 0; i < len; i++) { // some plugins like ipmi generate special characters in metric name switch (orig_name[i]) { case '(': @@ -338,8 +337,7 @@ static void in_place_replace_sensu_name_reserved(char *orig_name) /* {{{ */ static char *sensu_value_to_json(struct sensu_host const *host, /* {{{ */ data_set_t const *ds, value_list_t const *vl, - size_t index, gauge_t const *rates, - int status) { + size_t index, gauge_t const *rates) { char name_buffer[5 * DATA_MAX_NAME_LEN]; char service_buffer[6 * DATA_MAX_NAME_LEN]; char *ret_str; @@ -455,7 +453,7 @@ static char *sensu_value_to_json(struct sensu_host const *host, /* {{{ */ // incorporate the data source index { char ds_index[DATA_MAX_NAME_LEN]; - snprintf(ds_index, sizeof(ds_index), "%zu", index); + snprintf(ds_index, sizeof(ds_index), "%" PRIsz, index); res = my_asprintf(&temp_str, "%s, \"collectd_data_source_index\": %s", ret_str, ds_index); free(ret_str); @@ -520,7 +518,8 @@ static char *sensu_value_to_json(struct sensu_host const *host, /* {{{ */ return NULL; } } else { - res = my_asprintf(&value_str, "%llu", vl->values[index].counter); + res = my_asprintf(&value_str, "%" PRIu64, + (uint64_t)vl->values[index].counter); if (res == -1) { free(ret_str); ERROR("write_sensu plugin: Unable to alloc memory"); @@ -627,7 +626,7 @@ static char *replace_str(const char *str, const char *old, /* {{{ */ r += newlen; p = q + oldlen; } - strncpy(r, p, strlen(p)); + sstrncpy(r, p, retlen + 1); return ret; } /* }}} char *replace_str */ @@ -880,11 +879,9 @@ static int sensu_send_msg(struct sensu_host *host, const char *msg) /* {{{ */ sensu_close_socket(host); if (status != 0) { - char errbuf[1024]; ERROR("write_sensu plugin: Sending to Sensu at %s:%s failed: %s", (host->node != NULL) ? host->node : SENSU_HOST, - (host->service != NULL) ? host->service : SENSU_PORT, - sstrerror(errno, errbuf, sizeof(errbuf))); + (host->service != NULL) ? host->service : SENSU_PORT, STRERRNO); return -1; } @@ -928,7 +925,7 @@ static int sensu_write(const data_set_t *ds, /* {{{ */ } } for (size_t i = 0; i < vl->values_len; i++) { - msg = sensu_value_to_json(host, ds, vl, (int)i, rates, statuses[i]); + msg = sensu_value_to_json(host, ds, vl, (int)i, rates); if (msg == NULL) { sfree(rates); pthread_mutex_unlock(&host->lock); @@ -1021,10 +1018,10 @@ static int sensu_config_node(oconfig_item_t *ci) /* {{{ */ host->reference_count = 1; host->node = NULL; host->service = NULL; - host->notifications = 0; - host->metrics = 0; - host->store_rates = 1; - host->always_append_ds = 0; + host->notifications = false; + host->metrics = false; + host->store_rates = true; + host->always_append_ds = false; host->metric_handlers.nb_strs = 0; host->metric_handlers.strs = NULL; host->notification_handlers.nb_strs = 0; @@ -1087,12 +1084,8 @@ static int sensu_config_node(oconfig_item_t *ci) /* {{{ */ break; } else if (strcasecmp("Port", child->key) == 0) { status = cf_util_get_service(child, &host->service); - if (status != 0) { - ERROR("write_sensu plugin: Invalid argument " - "configured for the \"Port\" " - "option."); + if (status != 0) break; - } } else if (strcasecmp("StoreRates", child->key) == 0) { status = cf_util_get_boolean(child, &host->store_rates); if (status != 0) @@ -1126,16 +1119,17 @@ static int sensu_config_node(oconfig_item_t *ci) /* {{{ */ return -1; } - if ((host->notification_handlers.nb_strs > 0) && (host->notifications == 0)) { + if ((host->notification_handlers.nb_strs > 0) && + (host->notifications == false)) { WARNING("write_sensu plugin: NotificationHandler given so forcing " "notifications to be enabled"); host->notifications = 1; } - if ((host->metric_handlers.nb_strs > 0) && (host->metrics == 0)) { + if ((host->metric_handlers.nb_strs > 0) && (host->metrics == false)) { WARNING("write_sensu plugin: MetricHandler given so forcing metrics to be " "enabled"); - host->metrics = 1; + host->metrics = true; } if (!(host->notifications || host->metrics)) { diff --git a/src/write_stackdriver.c b/src/write_stackdriver.c new file mode 100644 index 00000000..a1341d9d --- /dev/null +++ b/src/write_stackdriver.c @@ -0,0 +1,689 @@ +/** + * 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 + **/ + +#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 +#include +#include + +/* + * 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 */ diff --git a/src/write_tsdb.c b/src/write_tsdb.c index 5c43eda3..42f5d65b 100644 --- a/src/write_tsdb.c +++ b/src/write_tsdb.c @@ -79,8 +79,8 @@ struct wt_callback { char *service; char *host_tags; - _Bool store_rates; - _Bool always_append_ds; + bool store_rates; + bool always_append_ds; char send_buf[WT_SEND_BUF_SIZE]; size_t send_buf_free; @@ -89,13 +89,13 @@ struct wt_callback { pthread_mutex_t send_lock; - _Bool connect_failed_log_enabled; + bool connect_failed_log_enabled; int connect_dns_failed_attempts_remaining; cdtime_t next_random_ttl; }; -static cdtime_t resolve_interval = 0; -static cdtime_t resolve_jitter = 0; +static cdtime_t resolve_interval; +static cdtime_t resolve_jitter; /* * Functions @@ -112,9 +112,8 @@ static int wt_send_buffer(struct wt_callback *cb) { status = swrite(cb->sock_fd, cb->send_buf, strlen(cb->send_buf)); if (status != 0) { - char errbuf[1024]; ERROR("write_tsdb plugin: send failed with status %zi (%s)", status, - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); close(cb->sock_fd); cb->sock_fd = -1; @@ -130,7 +129,7 @@ static int wt_flush_nolock(cdtime_t timeout, struct wt_callback *cb) { int status; DEBUG("write_tsdb plugin: wt_flush_nolock: timeout = %.3f; " - "send_buf_fill = %zu;", + "send_buf_fill = %" PRIsz ";", (double)timeout, cb->send_buf_fill); /* timeout == 0 => flush unconditionally */ @@ -153,7 +152,7 @@ static int wt_flush_nolock(cdtime_t timeout, struct wt_callback *cb) { return status; } -static cdtime_t new_random_ttl() { +static cdtime_t new_random_ttl(void) { if (resolve_jitter == 0) return 0; @@ -242,10 +241,9 @@ static int wt_callback_init(struct wt_callback *cb) { } if (cb->sock_fd < 0) { - char errbuf[1024]; ERROR("write_tsdb plugin: Connecting to %s:%s failed. " "The last error was: %s", - node, service, sstrerror(errno, errbuf, sizeof(errbuf))); + node, service, STRERRNO); return -1; } @@ -315,7 +313,7 @@ static int wt_flush(cdtime_t timeout, static int wt_format_values(char *ret, size_t ret_len, int ds_num, const data_set_t *ds, const value_list_t *vl, - _Bool store_rates) { + bool store_rates) { size_t offset = 0; int status; gauge_t *rates = NULL; @@ -349,7 +347,7 @@ static int wt_format_values(char *ret, size_t ret_len, int ds_num, } BUFFER_ADD(GAUGE_FORMAT, rates[ds_num]); } else if (ds->ds[ds_num].type == DS_TYPE_COUNTER) - BUFFER_ADD("%llu", vl->values[ds_num].counter); + BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_num].counter); else if (ds->ds[ds_num].type == DS_TYPE_DERIVE) BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive); else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE) @@ -466,7 +464,7 @@ static int wt_send_message(const char *key, const char *value, cdtime_t time, if (message_len >= sizeof(message)) { ERROR("write_tsdb plugin: message buffer too small: " - "Need %zu bytes.", + "Need %" PRIsz " bytes.", message_len + 1); return -1; } @@ -499,8 +497,8 @@ static int wt_send_message(const char *key, const char *value, cdtime_t time, cb->send_buf_fill += message_len; cb->send_buf_free -= message_len; - DEBUG("write_tsdb plugin: [%s]:%s buf %zu/%zu (%.1f %%) \"%s\"", cb->node, - cb->service, cb->send_buf_fill, sizeof(cb->send_buf), + DEBUG("write_tsdb plugin: [%s]:%s buf %" PRIsz "/%" PRIsz " (%.1f %%) \"%s\"", + cb->node, cb->service, cb->send_buf_fill, sizeof(cb->send_buf), 100.0 * ((double)cb->send_buf_fill) / ((double)sizeof(cb->send_buf)), message); diff --git a/src/xencpu.c b/src/xencpu.c index 8cba476f..8f177803 100644 --- a/src/xencpu.c +++ b/src/xencpu.c @@ -56,7 +56,7 @@ static int xencpu_init(void) { xc_physinfo_t *physinfo; - physinfo = calloc(1, sizeof(xc_physinfo_t)); + physinfo = calloc(1, sizeof(*physinfo)); if (physinfo == NULL) { ERROR("xencpu plugin: calloc() for physinfo failed."); xc_interface_close(xc_handle); @@ -75,14 +75,14 @@ static int xencpu_init(void) { INFO("xencpu plugin: Found %" PRIu32 " processors.", num_cpus); - cpu_info = calloc(num_cpus, sizeof(xc_cpuinfo_t)); + cpu_info = calloc(num_cpus, sizeof(*cpu_info)); if (cpu_info == NULL) { ERROR("xencpu plugin: calloc() for num_cpus failed."); xc_interface_close(xc_handle); return ENOMEM; } - cpu_states = calloc(num_cpus, sizeof(value_to_rate_state_t)); + cpu_states = calloc(num_cpus, sizeof(*cpu_states)); if (cpu_states == NULL) { ERROR("xencpu plugin: calloc() for cpu_states failed."); xc_interface_close(xc_handle); diff --git a/src/zfs_arc.c b/src/zfs_arc.c index 6c66fb13..d1ee111b 100644 --- a/src/zfs_arc.c +++ b/src/zfs_arc.c @@ -207,9 +207,8 @@ static int za_read(void) { fh = fopen(ZOL_ARCSTATS_FILE, "r"); if (fh == NULL) { - char errbuf[1024]; ERROR("zfs_arc plugin: Opening \"%s\" failed: %s", ZOL_ARCSTATS_FILE, - sstrerror(errno, errbuf, sizeof(errbuf))); + STRERRNO); return -1; } diff --git a/src/zone.c b/src/zone.c index 3cd05347..16df4043 100644 --- a/src/zone.c +++ b/src/zone.c @@ -68,9 +68,7 @@ static int zone_read_procfile(char const *pidstr, char const *name, void *buf, } if (sread(fd, buf, bufsize) != 0) { - char errbuf[1024]; - ERROR("zone plugin: Reading \"%s\" failed: %s", procfile, - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("zone plugin: Reading \"%s\" failed: %s", procfile, STRERRNO); close(fd); return 1; } diff --git a/src/zookeeper.c b/src/zookeeper.c index 0b2507d8..a99bbc01 100644 --- a/src/zookeeper.c +++ b/src/zookeeper.c @@ -37,8 +37,8 @@ #define ZOOKEEPER_DEF_HOST "127.0.0.1" #define ZOOKEEPER_DEF_PORT "2181" -static char *zk_host = NULL; -static char *zk_port = NULL; +static char *zk_host; +static char *zk_port; static const char *config_keys[] = {"Host", "Port"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); @@ -99,28 +99,22 @@ static int zookeeper_connect(void) { status = getaddrinfo(host, port, &ai_hints, &ai_list); if (status != 0) { - char errbuf[1024]; INFO("getaddrinfo failed: %s", - (status == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf)) - : gai_strerror(status)); + (status == EAI_SYSTEM) ? STRERRNO : gai_strerror(status)); return -1; } for (struct addrinfo *ai = ai_list; ai != NULL; ai = ai->ai_next) { sk = socket(ai->ai_family, SOCK_STREAM, 0); if (sk < 0) { - char errbuf[1024]; - WARNING("zookeeper: socket(2) failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("zookeeper: socket(2) failed: %s", STRERRNO); continue; } status = (int)connect(sk, ai->ai_addr, ai->ai_addrlen); if (status != 0) { - char errbuf[1024]; close(sk); sk = -1; - WARNING("zookeeper: connect(2) failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + WARNING("zookeeper: connect(2) failed: %s", STRERRNO); continue; } @@ -144,9 +138,7 @@ static int zookeeper_query(char *buffer, size_t buffer_size) { status = (int)swrite(sk, "mntr\r\n", strlen("mntr\r\n")); if (status != 0) { - char errbuf[1024]; - ERROR("zookeeper: write(2) failed: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("zookeeper: write(2) failed: %s", STRERRNO); close(sk); return -1; } @@ -158,11 +150,9 @@ static int zookeeper_query(char *buffer, size_t buffer_size) { buffer_size - buffer_fill, /* flags = */ 0)) != 0) { if (status < 0) { - char errbuf[1024]; if ((errno == EAGAIN) || (errno == EINTR)) continue; - ERROR("zookeeper: Error reading from socket: %s", - sstrerror(errno, errbuf, sizeof(errbuf))); + ERROR("zookeeper: Error reading from socket: %s", STRERRNO); close(sk); return -1; }