From: Matthias Runge Date: Tue, 17 Sep 2019 12:00:47 +0000 (+0200) Subject: Merge pull request #3277 from abays/sysevent X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=680c10acef5e4bd8566a214e75eacf293c56154a;hp=d67f84e24b2520b5b1ccbbf1ff9d1f397e075417;p=collectd.git Merge pull request #3277 from abays/sysevent Use sstrncpy instead of strncpy in sysevent plugin --- diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 00000000..ab88daf6 --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,225 @@ +env: + LANG: C + CIRRUS_CLONE_DEPTH: 1 + DEFAULT_CONFIG_OPTS: --enable-debug --without-libstatgrab --disable-dependency-tracking + + +### +# make distcheck and other sanity checks +# +release_ready_task: + container: + image: collectd/ci:stretch_amd64 + lint_script: + - /checks/check-bashisms.sh + - /checks/check-pod.sh + configure_script: + - ./build.sh + - ./configure $DEFAULT_CONFIG_OPTS + checks_script: + - make -j2 -s distcheck DISTCHECK_CONFIGURE_FLAGS="${DEFAULT_CONFIG_OPTS}" + +### +# Default toolchain and build flags used in deb packages, on a range of Debian +# and Ubuntu releases (+ Debian/unstable) +# Most should succeed, and PRs shouldn't break them. +# +debian_default_toolchain_task: + matrix: + - allow_failures: false + container: + image: collectd/ci:jessie_amd64 + - allow_failures: false + container: + image: collectd/ci:stretch_amd64 + - allow_failures: false + container: + image: collectd/ci:stretch_i386 + - allow_failures: false + container: + image: collectd/ci:trusty_amd64 + - allow_failures: false + container: + image: collectd/ci:xenial_amd64 + # debian/unstable is expected to fail + - allow_failures: true + skip_notifications: true + only_if: $CIRRUS_BRANCH == 'master' + container: + image: collectd/ci:sid_amd64 + configure_script: + - ./build.sh + - gcc --version + - > + ./configure CC=gcc $DEFAULT_CONFIG_OPTS + CFLAGS="$(dpkg-buildflags --get CFLAGS)" + CPPLAGS="$(dpkg-buildflags --get CPPFLAGS)" + LDFLAGS="$(dpkg-buildflags --get LDFLAGS)" + build_script: + - make -j2 -sk + tests_script: + - make -j2 -sk check + - /checks/check-built-plugins.sh + +### +# Default toolchain and build flags used in RPM packages, on a range of RedHat +# and Fedora releases (+ Fedora/rawhide) +# Most should succeed, and PRs shouldn't break them. +# +redhat_default_toolchain_task: + matrix: + - allow_failures: false + skip_notifications: false + container: + image: collectd/ci:el6_x86_64 + - allow_failures: true + skip_notifications: true + container: + image: collectd/ci:el7_x86_64 # TODO: fix this platform + - allow_failures: true + skip_notifications: true + container: + image: collectd/ci:fedora28_x86_64 + # fedora/rawhide is expected to fail + - allow_failures: true + skip_notifications: true + only_if: $CIRRUS_BRANCH == 'master' + container: + image: collectd/ci:fedora_rawhide_x86_64 + configure_script: + - ./build.sh + - gcc --version + - ./configure CC=gcc $DEFAULT_CONFIG_OPTS CFLAGS="$(rpm --eval '%optflags')" + build_script: + - make -j2 -sk + tests_script: + - make -j2 -sk check + - /checks/check-built-plugins.sh + + +### +# Misc non-standard build environment & options on most recent released debian +# version. +# Some are expected to fail, others should always pass +non_standard_toolchains_task: + container: + image: collectd/ci:stretch_amd64 + only_if: $CIRRUS_PR == '' + + matrix: + + # build using clang with default build flags, should always pass + - env: + LABEL: clang + allow_failures: true # TODO: fix this platform + skip_notifications: true + configure_script: + - ./build.sh + - clang --version + - > + ./configure CC=clang CXX=clang++ + $DEFAULT_CONFIG_OPTS + CFLAGS="$(dpkg-buildflags --get CFLAGS)" + CPPLAGS="$(dpkg-buildflags --get CPPFLAGS)" + LDFLAGS="$(dpkg-buildflags --get LDFLAGS)" + build_script: + - make -j2 -sk + tests_script: + - make -j2 -sk check + + # build against libstatgrab, should always pass + - env: + LABEL: statgrab + allow_failures: false + skip_notifications: false + configure_script: + - ./build.sh + - gcc --version + - > + ./configure --with-libstatgrab --enable-debug + CFLAGS="$(dpkg-buildflags --get CFLAGS)" + CPPLAGS="$(dpkg-buildflags --get CPPFLAGS)" + LDFLAGS="$(dpkg-buildflags --get LDFLAGS)" + build_script: + - make -j2 -sk + tests_script: + - > + for i in cpu disk interface load memory swap users; do + if ! $(ldd ".libs/${i}.so" 2>/dev/null | grep -q 'libstatgrab.so'); then + echo "plugin $i NOT linked against libstatgrab" + exit 1 + fi + done + + # build using clang with a collection of strict build flags, will most + # probably always fail + - env: + LABEL: clang strict + allow_failures: true + skip_notifications: true + configure_script: + - ./build.sh + - clang --version + - > + ./configure CC=clang CXX=clang++ + $DEFAULT_CONFIG_OPTS + CFLAGS='-Wall + -Wno-error + -Wextra + -Wformat=2 + -Wformat-security + -Wformat-nonliteral + -Wmissing-include-dirs + -Wold-style-definition + -Wpointer-arith + -Winit-self + -Wmissing-prototypes + -Wimplicit-function-declaration + -Wmissing-declarations + -Wstrict-prototypes + -Wmissing-noreturn + -Wshadow + -Wendif-labels + -Wwrite-strings + -Wno-unused-parameter + -Wno-missing-field-initializers + -Wdate-time + -Wnested-externs + -Wno-typedef-redefinition + -Wno-gnu-variable-sized-type-not-at-end' + build_script: + - make -j2 -sk + tests_script: + - make -j2 -sk check + +### +# Build using a range of compilers, available in debian/unstable. NB: might +# fail because of changes to the distro, not the compiler used. +# +bleeding_edge_compilers_task: + container: + image: collectd/ci:sid_amd64 + only_if: $CIRRUS_BRANCH == 'master' + allow_failures: true + skip_notifications: true + env: + matrix: + CC: gcc-7 + CC: gcc-8 + CC: clang-6.0 + CC: clang-7 + CC: clang-8 + CC: clang-9 + configure_script: + - ./build.sh + - $CC --version + - > + ./configure CC=$CC + $DEFAULT_CONFIG_OPTS + CFLAGS="$(dpkg-buildflags --get CFLAGS)" + CPPLAGS="$(dpkg-buildflags --get CPPFLAGS)" + LDFLAGS="$(dpkg-buildflags --get LDFLAGS)" + build_script: + - make -j2 -sk + tests_script: + - make -j2 -sk check 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/.travis.yml b/.travis.yml index 97115d14..4ba4e2a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,84 +1,99 @@ +# Travis CI configuration file +# https://travis-ci.org/collectd/collectd +language: c + 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=" + - MAKEFLAGS="-j 2" + # 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=" + +matrix: + include: + - os: osx + osx_image: xcode10.1 + compiler: clang + env: + - CXX=clang++ + - PATH="/usr/local/opt/mysql-client/bin:$PATH" + - os: linux + dist: xenial + compiler: clang + - os: linux + dist: xenial + compiler: gcc -sudo: required -dist: trusty -compiler: - - gcc - - 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 - libcurl4-gnutls-dev - libdbi0-dev - libesmtp-dev - libganglia1-dev - libgcrypt11-dev - libglib2.0-dev - libgps-dev - libhiredis-dev - libi2c-dev - libldap2-dev - libltdl-dev - liblua50-dev - liblua5.1-0-dev - liblua5.2-dev - liblvm2-dev - libmemcached-dev - libmicrohttpd-dev - libmnl-dev - libmodbus-dev - libmosquitto0-dev - libmysqlclient-dev - libnotify-dev - libopenipmi-dev - liboping-dev - libow-dev - libpcap-dev - libperl-dev - libpq-dev - libprotobuf-c0-dev - librabbitmq-dev - librdkafka-dev - libriemann-client-dev - librrd-dev - libsensors4-dev - libsigrok-dev - libsnmp-dev - libstatgrab-dev - libtokyocabinet-dev - libtokyotyrant-dev - libudev-dev - libupsclient-dev - libvarnish-dev - libvirt-dev - libxen-dev - libxml2-dev - libyajl-dev - linux-libc-dev - perl - protobuf-c-compiler - python3-dev - python-dev - xfslibs-dev -before_script: autoreconf -fi + +before_script: autoreconf -vif + script: - if [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]]; then exit 0; fi - ./configure - - make -j $(nproc) - - make check + - make distcheck DISTCHECK_CONFIGURE_FLAGS="--disable-dependency-tracking --enable-debug" addons: + apt: + packages: + - autotools-dev + - iptables-dev + - libatasmart-dev + - libcap-dev + - libcurl4-gnutls-dev + - libdbi0-dev + - libesmtp-dev + - libganglia1-dev + - libgcrypt11-dev + - libglib2.0-dev + - libgps-dev + - libhiredis-dev + - libi2c-dev + - libldap2-dev + - libltdl-dev + - liblua50-dev + - liblua5.1-0-dev + - liblua5.2-dev + - liblvm2-dev + - libmemcached-dev + - libmicrohttpd-dev + - libmnl-dev + - libmodbus-dev + - libmosquitto-dev + - libmysqlclient-dev + - libnotify-dev + - libopenipmi-dev + - liboping-dev + - libow-dev + - libpcap-dev + - libperl-dev + - libpq-dev + - libprotobuf-c0-dev + - librabbitmq-dev + - librdkafka-dev + - libriemann-client-dev + - librrd-dev + - libsensors4-dev + - libsigrok-dev + - libsnmp-dev + - libstatgrab-dev + - libtokyocabinet-dev + - libtokyotyrant-dev + - libudev-dev + - libupsclient-dev + - libvarnish-dev + - libvirt-dev + - libxen-dev + - libxml2-dev + - libyajl-dev + - linux-libc-dev + - perl + - protobuf-c-compiler + - python3-dev + - python-dev + - xfslibs-dev coverity_scan: project: name: "collectd/collectd" @@ -87,3 +102,39 @@ addons: build_command_prepend: "./configure; make clean" build_command: "make -j $(nproc)" branch_pattern: coverity_scan + homebrew: + packages: + - curl + - glib + - hiredis + - libdbi + - libmemcached + - libmicrohttpd + - libmodbus + - libnotify + - liboping + - libpcap + - librdkafka + - libstatgrab + - libvirt + - lua + - mosquitto + - mysql-client + - net-snmp + - openldap + - perl + - protobuf + - protobuf-c + - python + - qpid-proton + - rabbitmq-c + - riemann-client + - rrdtool + - tokyo-cabinet + - varnish + - yajl + +git: + quiet: true + submodules: false + depth: 1 diff --git a/AUTHORS b/AUTHORS index 3d2e2e44..db580661 100644 --- a/AUTHORS +++ b/AUTHORS @@ -57,6 +57,7 @@ Andreas Henriksson - libmnl support in the netlink plugin. Andrew Bays + - procevent plugin. - sysevent plugin. Andy Parkins @@ -175,6 +176,9 @@ Jiri Tyr Julien Ammous - Lua plugin. +Shirly Radco + - write_syslog plugin. + Kevin Bowling - write_tsdb plugin for http://opentsdb.net/ diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..5a2fe301 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,18 @@ +# 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. + +/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 + +# Order is important; the last matching pattern takes the most +# precedence. +* @trusted-contributors diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 11969ded..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,59 +0,0 @@ -# Contribution guidelines - -Thanks for taking the time to contribute to the [collectd -project](https://collectd.org/)! This document tries to give some guidance to -make the process of contributing to *collectd* as pleasant as possible. - -## Bug reports - -Please report bugs as [GitHub -Issues](https://github.com/collectd/collectd/issues). Try to answer the -following questions: - -* Which version of *collectd* are you using? -* Which operating system (distribution) are you using at which version? -* What is the expected behavior / output? -* What is the actual (observed) behavior / output? -* How can we reproduce the problem you're having? -* If *collectd* crashes, try to get a - [stack trace](https://collectd.org/wiki/index.php/Core_file). - -Please monitor your issue for a couple of days and reply to questions. To keep -the project manageable, we have to do some housekeeping; meaning we will close -issues that have become stale. - -## Code contributions - -Please open a [GitHub Pull Request](https://github.com/collectd/collectd/pulls) -(PR) to contribute bug fixes, features, cleanups, new plugins, … Patches sent to -the mailing list have a tendency to fall through the cracks. - -* *Focus:* Fix *one thing* in your PR. The smaller your change, the faster it - will be reviewed and merged. -* *Coding style:* Please run `clang-format -style=file -i $FILE` after editing - `.c`, `.h` and `.proto` files. If you don't want to install *clang-format* - locally or your version produces a different result than the formatting - check on Github, use `contrib/format.sh` to format files using the same web - service used by our check. -* *Documentation:* New config options need to be documented in two places: the - manpage (`src/collectd.conf.pod`) and the example config - (`src/collectd.conf.in`). New plugins need to be added to the `README` file. -* *Continuous integration:* Once your PR is created, our continuous - integration environment will try to build it on a number of platforms. If - this reports a failure, please investigate and fix the problem. We will at - best do a very casual review for failing PRs. -* *Don't rebase:* Rebasing your branch destroys the review history. If a review - takes a long time, we may ask you to rebase on a more recent *master*, but - please don't do it without being asked. -* *types.db:* One of the most common mistakes made by new contributors is the - addition of (many) new *types* in the file `src/types.db`. The majority of - usecases can be met with one of the existing entries. If you plan to add new - entries to `src/types.db`, you should talk to us early in the design - process. - -## Other resources - -* [Mailing list](http://mailman.verplant.org/listinfo/collectd) -* [#collectd IRC channel](https://webchat.freenode.net/?channels=#collectd) - on *freenode*. -* [Old patch submission guideline](https://collectd.org/wiki/index.php/Submitting_patches) diff --git a/ChangeLog b/ChangeLog index e9a8415b..7ea5a07f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -72,6 +72,8 @@ newer libmicrohttpd. Thanks to Pavel Rochnyak. #2849 * Write Prometheus plugin: set "SO_REUSEADDRESS" on listening socket. Thanks to Pavel Rochnyak. #2570, #2673 + * Write Syslog plugin: The new "write_syslog" plugin writes value + lists as syslog messages. Thanks to Shirly Radco. #3019 2017-11-17, Version 5.8.0 * collectd: The core daemon is now completely licensed under the MIT diff --git a/Makefile.am b/Makefile.am index 61159c4f..5470b9b3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -236,8 +236,8 @@ collectd_SOURCES = \ src/daemon/filter_chain.h \ src/daemon/globals.c \ src/daemon/globals.h \ - src/daemon/meta_data.c \ - src/daemon/meta_data.h \ + src/utils/metadata/meta_data.c \ + src/utils/metadata/meta_data.h \ src/daemon/plugin.c \ src/daemon/plugin.h \ src/daemon/utils_cache.c \ @@ -276,7 +276,7 @@ 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 @@ -341,22 +341,22 @@ endif test_common_SOURCES = \ - src/daemon/common_test.c \ + src/utils/common/common_test.c \ src/testing.h test_common_LDADD = libplugin_mock.la test_meta_data_SOURCES = \ - src/daemon/meta_data_test.c \ + src/utils/metadata/meta_data_test.c \ src/testing.h test_meta_data_LDADD = libmetadata.la libplugin_mock.la test_utils_avltree_SOURCES = \ - src/daemon/utils_avltree_test.c \ + src/utils/avltree/avltree_test.c \ src/testing.h test_utils_avltree_LDADD = libavltree.la $(COMMON_LIBS) test_utils_heap_SOURCES = \ - src/daemon/utils_heap_test.c \ + src/utils/heap/heap_test.c \ src/testing.h test_utils_heap_LDADD = libheap.la $(COMMON_LIBS) @@ -372,30 +372,30 @@ test_utils_subst_SOURCES = \ test_utils_subst_LDADD = libplugin_mock.la test_utils_config_cores_SOURCES = \ - src/utils_config_cores_test.c \ + src/utils/config_cores/config_cores_test.c \ src/testing.h test_utils_config_cores_LDADD = libplugin_mock.la libavltree_la_SOURCES = \ - src/daemon/utils_avltree.c \ - src/daemon/utils_avltree.h + src/utils/avltree/avltree.c \ + src/utils/avltree/avltree.h libcommon_la_SOURCES = \ - src/daemon/common.c \ - src/daemon/common.h + src/utils/common/common.c \ + src/utils/common/common.h libcommon_la_LIBADD = $(COMMON_LIBS) libheap_la_SOURCES = \ - src/daemon/utils_heap.c \ - src/daemon/utils_heap.h + src/utils/heap/heap.c \ + src/utils/heap/heap.h libignorelist_la_SOURCES = \ - src/utils_ignorelist.c \ - src/utils_ignorelist.h + src/utils/ignorelist/ignorelist.c \ + src/utils/ignorelist/ignorelist.h libmetadata_la_SOURCES = \ - src/daemon/meta_data.c \ - src/daemon/meta_data.h + src/utils/metadata/meta_data.c \ + src/utils/metadata/meta_data.h libplugin_mock_la_SOURCES = \ src/daemon/plugin_mock.c \ @@ -409,11 +409,11 @@ libplugin_mock_la_CPPFLAGS = $(AM_CPPFLAGS) -DMOCK_TIME libplugin_mock_la_LIBADD = libcommon.la libignorelist.la $(COMMON_LIBS) libformat_graphite_la_SOURCES = \ - src/utils_format_graphite.c \ - src/utils_format_graphite.h + src/utils/format_graphite/format_graphite.c \ + src/utils/format_graphite/format_graphite.h test_format_graphite_SOURCES = \ - src/utils_format_graphite_test.c \ + src/utils/format_graphite/format_graphite_test.c \ src/testing.h test_format_graphite_LDADD = \ libformat_graphite.la \ @@ -422,8 +422,8 @@ test_format_graphite_LDADD = \ -lm libformat_json_la_SOURCES = \ - src/utils_format_json.c \ - src/utils_format_json.h + src/utils/format_json/format_json.c \ + src/utils/format_json/format_json.h libformat_json_la_CPPFLAGS = $(AM_CPPFLAGS) libformat_json_la_LDFLAGS = $(AM_LDFLAGS) libformat_json_la_LIBADD = @@ -435,7 +435,7 @@ libformat_json_la_LIBADD += $(BUILD_WITH_LIBYAJL_LIBS) check_PROGRAMS += test_format_json test_format_json_SOURCES = \ - src/utils_format_json_test.c \ + src/utils/format_json/format_json_test.c \ src/testing.h test_format_json_LDADD = \ libformat_json.la \ @@ -453,16 +453,16 @@ check_PROGRAMS += test_plugin_ceph endif liblatency_la_SOURCES = \ - src/utils_latency.c \ - src/utils_latency.h \ - src/utils_latency_config.c \ - src/utils_latency_config.h + src/utils/latency/latency.c \ + src/utils/latency/latency.h \ + src/utils/latency/latency_config.c \ + src/utils/latency/latency_config.h liblatency_la_LIBADD = \ libcommon.la \ -lm test_utils_latency_SOURCES = \ - src/utils_latency_test.c \ + src/utils/latency/latency_test.c \ src/testing.h test_utils_latency_LDADD = \ liblatency.la \ @@ -470,41 +470,41 @@ test_utils_latency_LDADD = \ -lm libcmds_la_SOURCES = \ - src/utils_cmds.c \ - src/utils_cmds.h \ - src/utils_cmd_flush.c \ - src/utils_cmd_flush.h \ - src/utils_cmd_getthreshold.c \ - src/utils_cmd_getthreshold.h \ - src/utils_cmd_getval.c \ - src/utils_cmd_getval.h \ - src/utils_cmd_listval.c \ - src/utils_cmd_listval.h \ - src/utils_cmd_putnotif.c \ - src/utils_cmd_putnotif.h \ - src/utils_cmd_putval.c \ - src/utils_cmd_putval.h \ - src/utils_parse_option.c \ - src/utils_parse_option.h + src/utils/cmds/cmds.c \ + src/utils/cmds/cmds.h \ + src/utils/cmds/flush.c \ + src/utils/cmds/flush.h \ + src/utils/cmds/getthreshold.c \ + src/utils/cmds/getthreshold.h \ + src/utils/cmds/getval.c \ + src/utils/cmds/getval.h \ + src/utils/cmds/listval.c \ + src/utils/cmds/listval.h \ + src/utils/cmds/putnotif.c \ + src/utils/cmds/putnotif.h \ + src/utils/cmds/putval.c \ + src/utils/cmds/putval.h \ + src/utils/cmds/parse_option.c \ + src/utils/cmds/parse_option.h libcmds_la_LIBADD = \ libcommon.la \ libmetadata.la \ -lm test_utils_cmds_SOURCES = \ - src/utils_cmds_test.c \ + src/utils/cmds/cmds_test.c \ src/testing.h test_utils_cmds_LDADD = \ libcmds.la \ libplugin_mock.la liblookup_la_SOURCES = \ - src/utils_vl_lookup.c \ - src/utils_vl_lookup.h + src/utils/lookup/vl_lookup.c \ + src/utils/lookup/vl_lookup.h liblookup_la_LIBADD = libavltree.la test_utils_vl_lookup_SOURCES = \ - src/utils_vl_lookup_test.c \ + src/utils/lookup/vl_lookup_test.c \ src/testing.h test_utils_vl_lookup_LDADD = \ liblookup.la \ @@ -514,11 +514,11 @@ test_utils_vl_lookup_LDADD += -lkstat endif libmount_la_SOURCES = \ - src/utils_mount.c \ - src/utils_mount.h + src/utils/mount/mount.c \ + src/utils/mount/mount.h test_utils_mount_SOURCES = \ - src/utils_mount_test.c \ + src/utils/mount/mount_test.c \ src/testing.h test_utils_mount_LDADD = \ libmount.la \ @@ -579,8 +579,8 @@ if BUILD_WITH_LIBSSL if BUILD_WITH_LIBYAJL2 noinst_LTLIBRARIES += liboauth.la liboauth_la_SOURCES = \ - src/utils_oauth.c \ - src/utils_oauth.h + src/utils/oauth/oauth.c \ + src/utils/oauth/oauth.h liboauth_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(BUILD_WITH_LIBCURL_CFLAGS) \ @@ -594,7 +594,7 @@ liboauth_la_LIBADD = \ check_PROGRAMS += test_utils_oauth TESTS += test_utils_oauth test_utils_oauth_SOURCES = \ - src/utils_oauth_test.c + src/utils/oauth/oauth_test.c test_utils_oauth_LDADD = \ liboauth.la \ libcommon.la \ @@ -602,8 +602,8 @@ test_utils_oauth_LDADD = \ noinst_LTLIBRARIES += libgce.la libgce_la_SOURCES = \ - src/utils_gce.c \ - src/utils_gce.h + src/utils/gce/gce.c \ + src/utils/gce/gce.h libgce_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(BUILD_WITH_LIBCURL_CFLAGS) @@ -616,8 +616,8 @@ endif if BUILD_WITH_LIBYAJL2 noinst_LTLIBRARIES += libformat_stackdriver.la libformat_stackdriver_la_SOURCES = \ - src/utils_format_stackdriver.c \ - src/utils_format_stackdriver.h + src/utils/format_stackdriver/format_stackdriver.c \ + src/utils/format_stackdriver/format_stackdriver.h libformat_stackdriver_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(BUILD_WITH_LIBYAJL_CPPFLAGS) @@ -629,7 +629,7 @@ libformat_stackdriver_la_LIBADD = \ check_PROGRAMS += test_format_stackdriver TESTS += test_format_stackdriver test_format_stackdriver_SOURCES = \ - src/utils_format_stackdriver_test.c \ + src/utils/format_stackdriver/format_stackdriver_test.c \ src/testing.h test_format_stackdriver_LDADD = \ libformat_stackdriver.la \ @@ -641,8 +641,8 @@ if BUILD_PLUGIN_AGGREGATION pkglib_LTLIBRARIES += aggregation.la aggregation_la_SOURCES = \ src/aggregation.c \ - src/utils_vl_lookup.c \ - src/utils_vl_lookup.h + src/utils/lookup/vl_lookup.c \ + src/utils/lookup/vl_lookup.h aggregation_la_LDFLAGS = $(PLUGIN_LDFLAGS) aggregation_la_LIBADD = -lm endif @@ -663,7 +663,7 @@ if BUILD_PLUGIN_AMQP1 pkglib_LTLIBRARIES += amqp1.la amqp1_la_SOURCES = \ src/amqp1.c \ - src/utils_deq.h + src/utils/deq/deq.h amqp1_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBQPIDPROTON_CPPFLAGS) amqp1_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBQPIDPROTON_LDFLAGS) amqp1_la_LIBADD = \ @@ -768,6 +768,12 @@ chrony_la_LDFLAGS = $(PLUGIN_LDFLAGS) chrony_la_LIBADD = -lm endif +if BUILD_PLUGIN_CHECK_UPTIME +pkglib_LTLIBRARIES += check_uptime.la +check_uptime_la_SOURCES = src/check_uptime.c +check_uptime_la_LDFLAGS = $(PLUGIN_LDFLAGS) +endif + if BUILD_PLUGIN_CONNTRACK pkglib_LTLIBRARIES += conntrack.la conntrack_la_SOURCES = src/conntrack.c @@ -827,10 +833,10 @@ if BUILD_PLUGIN_CURL pkglib_LTLIBRARIES += curl.la curl_la_SOURCES = \ src/curl.c \ - src/utils_curl_stats.c \ - src/utils_curl_stats.h \ - src/utils_match.c \ - src/utils_match.h + src/utils/curl_stats/curl_stats.c \ + src/utils/curl_stats/curl_stats.h \ + src/utils/match/match.c \ + src/utils/match/match.h curl_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS) curl_la_LDFLAGS = $(PLUGIN_LDFLAGS) curl_la_LIBADD = liblatency.la $(BUILD_WITH_LIBCURL_LIBS) @@ -840,15 +846,15 @@ if BUILD_PLUGIN_CURL_JSON pkglib_LTLIBRARIES += curl_json.la curl_json_la_SOURCES = \ src/curl_json.c \ - src/utils_curl_stats.c \ - src/utils_curl_stats.h + src/utils/curl_stats/curl_stats.c \ + src/utils/curl_stats/curl_stats.h curl_json_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS) curl_json_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS) curl_json_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS) curl_json_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBYAJL_LIBS) test_plugin_curl_json_SOURCES = src/curl_json_test.c \ - src/utils_curl_stats.c \ + src/utils/curl_stats/curl_stats.c \ src/daemon/configfile.c \ src/daemon/types_list.c test_plugin_curl_json_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS) @@ -861,8 +867,8 @@ if BUILD_PLUGIN_CURL_XML pkglib_LTLIBRARIES += curl_xml.la curl_xml_la_SOURCES = \ src/curl_xml.c \ - src/utils_curl_stats.c \ - src/utils_curl_stats.h + src/utils/curl_stats/curl_stats.c \ + src/utils/curl_stats/curl_stats.h curl_xml_la_CFLAGS = $(AM_CFLAGS) \ $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS) curl_xml_la_LDFLAGS = $(PLUGIN_LDFLAGS) @@ -873,8 +879,8 @@ if BUILD_PLUGIN_DBI pkglib_LTLIBRARIES += dbi.la dbi_la_SOURCES = \ src/dbi.c \ - src/utils_db_query.c \ - src/utils_db_query.h + src/utils/db_query/db_query.c \ + src/utils/db_query/db_query.h dbi_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBDBI_CPPFLAGS) dbi_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBDBI_LDFLAGS) dbi_la_LIBADD = $(BUILD_WITH_LIBDBI_LIBS) @@ -924,8 +930,8 @@ if BUILD_PLUGIN_DNS pkglib_LTLIBRARIES += dns.la dns_la_SOURCES = \ src/dns.c \ - src/utils_dns.c \ - src/utils_dns.h + src/utils/dns/dns.c \ + src/utils/dns/dns.h dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPCAP_CPPFLAGS) dns_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPCAP_LDFLAGS) dns_la_LIBADD = $(BUILD_WITH_LIBPCAP_LIBS) @@ -933,7 +939,7 @@ endif if BUILD_PLUGIN_DPDKEVENTS pkglib_LTLIBRARIES += dpdkevents.la -dpdkevents_la_SOURCES = src/dpdkevents.c src/utils_dpdk.c src/utils_dpdk.h +dpdkevents_la_SOURCES = src/dpdkevents.c src/utils/dpdk/dpdk.c src/utils/dpdk/dpdk.h dpdkevents_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS) dpdkevents_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS) dpdkevents_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS) @@ -942,7 +948,7 @@ endif if BUILD_PLUGIN_DPDKSTAT pkglib_LTLIBRARIES += dpdkstat.la -dpdkstat_la_SOURCES = src/dpdkstat.c src/utils_dpdk.c src/utils_dpdk.h +dpdkstat_la_SOURCES = src/dpdkstat.c src/utils/dpdk/dpdk.c src/utils/dpdk/dpdk.h dpdkstat_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS) dpdkstat_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS) dpdkstat_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS) @@ -1017,6 +1023,7 @@ 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 @@ -1053,8 +1060,8 @@ if BUILD_PLUGIN_INTEL_PMU pkglib_LTLIBRARIES += intel_pmu.la intel_pmu_la_SOURCES = \ src/intel_pmu.c \ - src/utils_config_cores.h \ - src/utils_config_cores.c + src/utils/config_cores/config_cores.h \ + src/utils/config_cores/config_cores.c intel_pmu_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBJEVENTS_CPPFLAGS) intel_pmu_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBJEVENTS_LDFLAGS) intel_pmu_la_LIBADD = $(BUILD_WITH_LIBJEVENTS_LIBS) @@ -1064,11 +1071,32 @@ if BUILD_PLUGIN_INTEL_RDT pkglib_LTLIBRARIES += intel_rdt.la intel_rdt_la_SOURCES = \ src/intel_rdt.c \ - src/utils_config_cores.h \ - src/utils_config_cores.c + src/utils/proc_pids/proc_pids.c \ + src/utils/proc_pids/proc_pids.h \ + src/utils/config_cores/config_cores.h \ + src/utils/config_cores/config_cores.c intel_rdt_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBPQOS_CPPFLAGS) intel_rdt_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPQOS_LDFLAGS) intel_rdt_la_LIBADD = $(BUILD_WITH_LIBPQOS_LIBS) + +test_plugin_intel_rdt_SOURCES = \ + src/intel_rdt_test.c \ + src/utils/config_cores/config_cores.c \ + src/utils/proc_pids/proc_pids.c \ + src/daemon/configfile.c \ + src/daemon/types_list.c +test_plugin_intel_rdt_CPPFLAGS = $(AM_CPPFLAGS) +test_plugin_intel_rdt_LDFLAGS = $(PLUGIN_LDFLAGS) +test_plugin_intel_rdt_LDADD = liboconfig.la libplugin_mock.la +check_PROGRAMS += test_plugin_intel_rdt +TESTS += test_plugin_intel_rdt + +test_utils_proc_pids_SOURCES = \ + src/utils/proc_pids/proc_pids_test.c \ + src/testing.h +test_utils_proc_pids_LDADD = libplugin_mock.la +check_PROGRAMS += test_utils_proc_pids +TESTS += test_utils_proc_pids endif if BUILD_PLUGIN_INTERFACE @@ -1259,8 +1287,8 @@ if BUILD_PLUGIN_MEMCACHEC pkglib_LTLIBRARIES += memcachec.la memcachec_la_SOURCES = \ src/memcachec.c \ - src/utils_match.c \ - src/utils_match.h + src/utils/match/match.c \ + src/utils/match/match.h memcachec_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMEMCACHED_CPPFLAGS) memcachec_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMEMCACHED_LDFLAGS) memcachec_la_LIBADD = liblatency.la $(BUILD_WITH_LIBMEMCACHED_LIBS) @@ -1369,6 +1397,21 @@ network_la_CPPFLAGS += $(GCRYPT_CPPFLAGS) network_la_LDFLAGS += $(GCRYPT_LDFLAGS) network_la_LIBADD += $(GCRYPT_LIBS) endif + +test_plugin_network_SOURCES = \ + src/network_test.c \ + src/utils_fbhash.c \ + src/daemon/configfile.c \ + src/daemon/types_list.c +test_plugin_network_CPPFLAGS = $(AM_CPPFLAGS) $(GCRYPT_CPPFLAGS) +test_plugin_network_LDFLAGS = $(PLUGIN_LDFLAGS) $(GCRYPT_LDFLAGS) +test_plugin_network_LDADD = \ + libavltree.la \ + liboconfig.la \ + libplugin_mock.la \ + libmetadata.la \ + $(GCRYPT_LIBS) +check_PROGRAMS += test_plugin_network endif if BUILD_PLUGIN_NFS @@ -1467,8 +1510,8 @@ if BUILD_PLUGIN_ORACLE pkglib_LTLIBRARIES += oracle.la oracle_la_SOURCES = \ src/oracle.c \ - src/utils_db_query.c \ - src/utils_db_query.h + src/utils/db_query/db_query.c \ + src/utils/db_query/db_query.h oracle_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_ORACLE_CPPFLAGS) oracle_la_LIBADD = $(BUILD_WITH_ORACLE_LIBS) oracle_la_LDFLAGS = $(PLUGIN_LDFLAGS) @@ -1478,8 +1521,8 @@ if BUILD_PLUGIN_OVS_EVENTS pkglib_LTLIBRARIES += ovs_events.la ovs_events_la_SOURCES = \ src/ovs_events.c \ - src/utils_ovs.c \ - src/utils_ovs.h + src/utils/ovs/ovs.c \ + src/utils/ovs/ovs.h ovs_events_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS) ovs_events_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS) ovs_events_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS) @@ -1489,8 +1532,8 @@ if BUILD_PLUGIN_OVS_STATS pkglib_LTLIBRARIES += ovs_stats.la ovs_stats_la_SOURCES = \ src/ovs_stats.c \ - src/utils_ovs.c \ - src/utils_ovs.h + src/utils/ovs/ovs.c \ + src/utils/ovs/ovs.h ovs_stats_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS) ovs_stats_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS) ovs_stats_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS) @@ -1560,8 +1603,8 @@ if BUILD_PLUGIN_POSTGRESQL pkglib_LTLIBRARIES += postgresql.la postgresql_la_SOURCES = \ src/postgresql.c \ - src/utils_db_query.c \ - src/utils_db_query.h + src/utils/db_query/db_query.c \ + src/utils/db_query/db_query.h postgresql_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPQ_CPPFLAGS) postgresql_la_LDFLAGS = $(PLUGIN_LDFLAGS) \ $(BUILD_WITH_LIBPQ_LDFLAGS) @@ -1588,8 +1631,8 @@ endif if HAVE_LIBMNL noinst_LTLIBRARIES += libtaskstats.la libtaskstats_la_SOURCES = \ - src/utils_taskstats.c \ - src/utils_taskstats.h + src/utils/taskstats/taskstats.c \ + src/utils/taskstats/taskstats.h libtaskstats_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS) libtaskstats_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS) endif @@ -1609,6 +1652,14 @@ processes_la_LIBADD += libtaskstats.la endif endif +if BUILD_PLUGIN_PROCEVENT +pkglib_LTLIBRARIES += procevent.la +procevent_la_SOURCES = src/procevent.c +procevent_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS) +procevent_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS) +procevent_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS) libignorelist.la +endif + if BUILD_PLUGIN_PROTOCOLS pkglib_LTLIBRARIES += protocols.la protocols_la_SOURCES = src/protocols.c @@ -1636,8 +1687,8 @@ if BUILD_PLUGIN_RRDCACHED pkglib_LTLIBRARIES += rrdcached.la rrdcached_la_SOURCES = \ src/rrdcached.c \ - src/utils_rrdcreate.c \ - src/utils_rrdcreate.h + src/utils/rrdcreate/rrdcreate.c \ + src/utils/rrdcreate/rrdcreate.h rrdcached_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS) rrdcached_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRRD_LDFLAGS) rrdcached_la_LIBADD = $(BUILD_WITH_LIBRRD_LIBS) @@ -1647,8 +1698,8 @@ if BUILD_PLUGIN_RRDTOOL pkglib_LTLIBRARIES += rrdtool.la rrdtool_la_SOURCES = \ src/rrdtool.c \ - src/utils_rrdcreate.c \ - src/utils_rrdcreate.h + src/utils/rrdcreate/rrdcreate.c \ + src/utils/rrdcreate/rrdcreate.h rrdtool_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS) rrdtool_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRRD_LDFLAGS) rrdtool_la_LIBADD = $(BUILD_WITH_LIBRRD_LIBS) @@ -1702,7 +1753,7 @@ 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/utils/avltree/avltree.c \ src/daemon/utils_llist.c \ src/daemon/configfile.c \ src/daemon/types_list.c @@ -1781,10 +1832,10 @@ if BUILD_PLUGIN_TAIL pkglib_LTLIBRARIES += tail.la tail_la_SOURCES = \ src/tail.c \ - src/utils_match.c \ - src/utils_match.h \ - src/utils_tail.c \ - src/utils_tail.h \ + src/utils/match/match.c \ + src/utils/match/match.h \ + src/utils/tail/tail.c \ + src/utils/tail/tail.h \ src/utils_tail_match.c \ src/utils_tail_match.h tail_la_LDFLAGS = $(PLUGIN_LDFLAGS) @@ -1795,8 +1846,8 @@ if BUILD_PLUGIN_TAIL_CSV pkglib_LTLIBRARIES += tail_csv.la tail_csv_la_SOURCES = \ src/tail_csv.c \ - src/utils_tail.c \ - src/utils_tail.h + src/utils/tail/tail.c \ + src/utils/tail/tail.h tail_csv_la_LDFLAGS = $(PLUGIN_LDFLAGS) endif @@ -1946,12 +1997,13 @@ virt_la_CFLAGS = $(AM_CFLAGS) \ virt_la_LDFLAGS = $(PLUGIN_LDFLAGS) virt_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS) -test_plugin_virt_SOURCES = src/virt_test.c +test_plugin_virt_SOURCES = src/virt_test.c src/daemon/configfile.c \ + src/daemon/types_list.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 \ +test_plugin_virt_LDADD = liboconfig.la libplugin_mock.la \ $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS) check_PROGRAMS += test_plugin_virt TESTS += test_plugin_virt @@ -1986,8 +2038,8 @@ if BUILD_PLUGIN_WRITE_HTTP pkglib_LTLIBRARIES += write_http.la write_http_la_SOURCES = \ src/write_http.c \ - src/utils_format_kairosdb.c \ - src/utils_format_kairosdb.h + src/utils/format_kairosdb/format_kairosdb.c \ + src/utils/format_kairosdb/format_kairosdb.h write_http_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS) write_http_la_LDFLAGS = $(PLUGIN_LDFLAGS) write_http_la_LIBADD = libformat_json.la $(BUILD_WITH_LIBCURL_LIBS) @@ -2017,6 +2069,7 @@ pkglib_LTLIBRARIES += write_mongodb.la write_mongodb_la_SOURCES = src/write_mongodb.c write_mongodb_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMONGOC_CFLAGS) write_mongodb_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMONGOC_LDFLAGS) +write_mongodb_la_LIBADD = $(BUILD_WITH_LIBMONGOC_LIBS) endif if BUILD_PLUGIN_WRITE_PROMETHEUS @@ -2063,6 +2116,12 @@ write_stackdriver_la_LIBADD = libformat_stackdriver.la libgce.la liboauth.la \ $(BUILD_WITH_LIBCURL_LIBS) endif +if BUILD_PLUGIN_WRITE_SYSLOG +pkglib_LTLIBRARIES += write_syslog.la +write_syslog_la_SOURCES = src/write_syslog.c +write_syslog_la_LDFLAGS = $(PLUGIN_LDFLAGS) +endif + if BUILD_PLUGIN_WRITE_TSDB pkglib_LTLIBRARIES += write_tsdb.la write_tsdb_la_SOURCES = src/write_tsdb.c @@ -2115,20 +2174,10 @@ am__v_POD2MAN_C_0 = @echo " POD2MAN " $@; am__v_POD2MAN_C_1 = .pod.1: - $(AM_V_POD2MAN_C)pod2man --release=$(VERSION) --center=$(PACKAGE) $< \ - >.pod2man.tmp.$$$$ 2>/dev/null && mv -f .pod2man.tmp.$$$$ $@ || true - @if grep '\' $@ >/dev/null 2>&1; \ - then \ - echo "$@ has some POD errors!"; false; \ - fi + $(AM_V_POD2MAN_C)pod2man --release=$(VERSION) --center=$(PACKAGE) $< $@ .pod.5: - $(AM_V_POD2MAN_C)pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< \ - >.pod2man.tmp.$$$$ 2>/dev/null && mv -f .pod2man.tmp.$$$$ $@ || true - @if grep '\' $@ >/dev/null 2>&1; \ - then \ - echo "$@ has some POD errors!"; false; \ - fi + $(AM_V_POD2MAN_C)pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< $@ V_PROTOC = $(v_protoc_@AM_V@) v_protoc_ = $(v_protoc_@AM_DEFAULT_V@) diff --git a/README b/README index dd1512e4..dd7a437c 100644 --- a/README +++ b/README @@ -347,6 +347,9 @@ Features - processes Process counts: Number of running, sleeping, zombie, ... processes. + - procevent + Listens for process starts and exits via netlink. + - protocols Counts various aspects of network protocols such as IP, TCP, UDP, etc. @@ -566,6 +569,10 @@ Features Sends data to Sensu, a stream processing and monitoring system, via the Sensu client local TCP socket. + - write_syslog + Sends data in syslog format, using TCP, where the message + contains the metric in human or JSON format. + - write_tsdb Sends data OpenTSDB, a scalable no master, no shared state time series database. diff --git a/bindings/perl/lib/Collectd/Plugins/OpenVZ.pm b/bindings/perl/lib/Collectd/Plugins/OpenVZ.pm index 4c7c3fe4..66359da1 100644 --- a/bindings/perl/lib/Collectd/Plugins/OpenVZ.pm +++ b/bindings/perl/lib/Collectd/Plugins/OpenVZ.pm @@ -110,9 +110,9 @@ sub cpu_read { $v{'type_instance'} = $cpu_instances[$key]; $v{'values'} = [ $counters[$key] ]; plugin_dispatch_values(\%v); + } } } -} sub df_read { my $veid = shift; @@ -137,7 +137,7 @@ sub df_read { $v{'type_instance'} = $val; $v{'values'} = [ $parts[5] * ($parts[6] - $parts[7]), $parts[5] * $parts[7] ]; plugin_dispatch_values(\%v); -} + } } sub load_read { @@ -180,7 +180,7 @@ sub processes_read { $v{'type_instance'} = $key; $v{'values'} = [ $ps_states->{$key} ]; plugin_dispatch_values(\%v); -} + } } sub users_read { diff --git a/configure.ac b/configure.ac index bd1478f5..9ff6408f 100644 --- a/configure.ac +++ b/configure.ac @@ -165,6 +165,7 @@ AC_CHECK_HEADERS_ONCE([ \ kstat.h \ kvm.h \ libgen.h \ + locale.h \ mntent.h \ mnttab.h \ netdb.h \ @@ -635,16 +636,6 @@ fi # }}} -# For the dns plugin -AC_CHECK_HEADERS([arpa/nameser.h]) -AC_CHECK_HEADERS([arpa/nameser_compat.h], [], [], - [[ - #if HAVE_ARPA_NAMESER_H - # include - #endif - ]] -) - AC_CHECK_HEADERS([net/if_arp.h], [], [], [[ #if HAVE_SYS_SOCKET_H @@ -752,33 +743,21 @@ test_cxx_flags() { # AC_CHECK_FUNCS_ONCE([ \ asprintf \ - closelog \ - getaddrinfo \ - getgrnam_r \ - getnameinfo \ getpwnam \ getpwnam_r \ - gettimeofday \ if_indextoname \ - openlog \ - regcomp \ - regerror \ - regexec \ - regfree \ - select \ setenv \ setgroups \ - strcasecmp \ - strdup \ - strncasecmp \ - sysconf + setlocale ] ) AC_FUNC_STRERROR_R -SAVE_CFLAGS="$CFLAGS" -CFLAGS="-Wall -Werror" +if test "x$GCC" = "xyes"; then + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Wall -Werror" +fi SAVE_LDFLAGS="$LDFLAGS" LDFLAGS="" if test "x$ac_system" = "xWindows"; then @@ -1583,7 +1562,7 @@ if test "x$have_getmntent" = "xlibc"; then AC_COMPILE_IFELSE( [ AC_LANG_PROGRAM( - [[#include "$srcdir/src/utils_mount.h"]], + [[#include "$srcdir/src/utils/mount/mount.h"]], [[ FILE *fh; struct mntent *me; @@ -1605,7 +1584,7 @@ if test "x$have_getmntent" = "xlibc"; then AC_COMPILE_IFELSE( [ AC_LANG_PROGRAM( - [[#include "$srcdir/src/utils_mount.h"]], + [[#include "$srcdir/src/utils/mount/mount.h"]], [[ FILE *fh; struct mnttab mt; @@ -2075,52 +2054,39 @@ if test "x$with_kvm_openfiles" = "xyes"; then fi # --with-cuda {{{ -# only CUDA provides the nvml.h header AC_ARG_WITH([cuda], [AS_HELP_STRING([--with-cuda@<:@=PREFIX@:>@], [Path to cuda.])], [ - if test "x$withval" = "xyes"; then + 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 if test "x$withval" = "xno"; then - with_cuda="no" else - with_cuda="yes" - CUDA_CFLAGS="$CUDA_CFLAGS -I$withval/include" - CUDA_LDFLAGS="$CUDA_LDFLAGS -L$withval/lib" - fi; fi + with_cuda="$withval" + fi ], - [ with_cuda="yes" - CUDA_CFLAGS="$CUDA_CFLAGS -I/opt/cuda/include" - CUDA_LDFLAGS="$CUDA_LDFLAGS -L/opt/cuda/lib64" - ] + [with_cuda="no"] ) -SAVE_CFLAGS="$CFLAGS" -SAVE_LDFLAGS="$LDFLAGS" -CFLAGS="$CFLAGS $CUDA_CFLAGS" -LDFLAGS="$LDFLAGS $CUDA_LDFLAGS" - 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 (header file missing)"] + [with_cuda="no (nvml.h not found)"] ) -fi -if test "x$with_cuda" = "xpkgconfig"; then - AC_CHECK_HEADERS([nvml.h], - [], - [with_cuda="no (header file missing)"] - ) + CPPFLAGS="$SAVE_CPPFLAGS" fi if test "x$with_cuda" = "xyes"; then - BUILD_WITH_CUDA_CFLAGS="$CUDA_CFLAGS" + BUILD_WITH_CUDA_CPPFLAGS="$CUDA_CPPFLAGS" BUILD_WITH_CUDA_LDFLAGS="$CUDA_LDFLAGS" BUILD_WITH_CUDA_LIBS="-lnvidia-ml" fi -AC_SUBST([BUILD_WITH_CUDA_CFLAGS]) +AC_SUBST([BUILD_WITH_CUDA_CPPFLAGS]) AC_SUBST([BUILD_WITH_CUDA_LDFLAGS]) AC_SUBST([BUILD_WITH_CUDA_LIBS]) @@ -2149,9 +2115,6 @@ CPPFLAGS="$CPPFLAGS $LIBAQUAERO5_CFLAGS" LDFLAGS="$LDFLAGS $LIBAQUAERO5_LDFLAGS" if test "x$with_libaquaero5" = "xyes"; then - if test "x$LIBAQUAERO5_CFLAGS" != "x"; then - AC_MSG_NOTICE([libaquaero5 CPPFLAGS: $LIBAQUAERO5_CFLAGS]) - fi AC_CHECK_HEADERS([libaquaero5.h], [with_libaquaero5="yes"], [with_libaquaero5="no (libaquaero5.h not found)"] @@ -2159,9 +2122,6 @@ if test "x$with_libaquaero5" = "xyes"; then fi if test "x$with_libaquaero5" = "xyes"; then - if test "x$LIBAQUAERO5_LDFLAGS" != "x"; then - AC_MSG_NOTICE([libaquaero5 LDFLAGS: $LIBAQUAERO5_LDFLAGS]) - fi AC_CHECK_LIB([aquaero5], libaquaero5_poll, [with_libaquaero5="yes"], [with_libaquaero5="no (symbol 'libaquaero5_poll' not found)"] @@ -2202,9 +2162,6 @@ CPPFLAGS="$CPPFLAGS $LIBHIREDIS_CPPFLAGS" LDFLAGS="$LDFLAGS $LIBHIREDIS_LDFLAGS" if test "x$with_libhiredis" = "xyes"; then - if test "x$LIBHIREDIS_CPPFLAGS" != "x"; then - AC_MSG_NOTICE([libhiredis CPPFLAGS: $LIBHIREDIS_CPPFLAGS]) - fi AC_CHECK_HEADERS([hiredis/hiredis.h], [with_libhiredis="yes"], [with_libhiredis="no (hiredis.h not found)"] @@ -2212,9 +2169,6 @@ if test "x$with_libhiredis" = "xyes"; then fi if test "x$with_libhiredis" = "xyes"; then - if test "x$LIBHIREDIS_LDFLAGS" != "x"; then - AC_MSG_NOTICE([libhiredis LDFLAGS: $LIBHIREDIS_LDFLAGS]) - fi AC_CHECK_LIB([hiredis], [redisCommand], [with_libhiredis="yes"], [with_libhiredis="no (symbol 'redisCommand' not found)"] @@ -2465,13 +2419,30 @@ if test "x$with_libdpdk" != "xno"; then fi if test "x$with_libdpdk" = "xyes"; then + SAVE_LIBS="$LIBS" + LIBS="$LIBDPDK_LIBS $LIBS" SAVE_LDFLAGS="$LDFLAGS" LDFLAGS="$LIBDPDK_LDFLAGS $LDFLAGS" - AC_CHECK_LIB([dpdk], [rte_eal_init], + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$LIBDPDK_CPPFLAGS $CPPFLAGS" + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$LIBDPDK_CFLAGS $CFLAGS" + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM( + [[ + #include + ]], + [[return rte_eal_init(0, NULL);]] + ) + ], [with_libdpdk="yes"], [with_libdpdk="no (symbol 'rte_eal_init' not found)"] ) + LIBS="$SAVE_LIBS" LDFLAGS="$SAVE_LDFLAGS" + CPPFLAGS="$SAVE_CPPFLAGS" + CFLAGS="$SAVE_CFLAGS" fi # }}} @@ -2635,9 +2606,6 @@ LDFLAGS="$LDFLAGS $GCRYPT_LDFLAGS" LIBS="$LIBS $GCRYPT_LIBS" if test "x$with_libgcrypt" = "xyes"; then - if test "x$GCRYPT_CPPFLAGS" != "x"; then - AC_MSG_NOTICE([gcrypt CPPFLAGS: $GCRYPT_CPPFLAGS]) - fi AC_CHECK_HEADERS([gcrypt.h], [with_libgcrypt="yes"], [with_libgcrypt="no (gcrypt.h not found)"] @@ -2974,18 +2942,6 @@ if test "x$with_java" = "xyes"; then fi; fi fi -if test "x$JAVA_CPPFLAGS" != "x"; then - AC_MSG_NOTICE([Building with JAVA_CPPFLAGS set to: $JAVA_CPPFLAGS]) -fi -if test "x$JAVA_CFLAGS" != "x"; then - AC_MSG_NOTICE([Building with JAVA_CFLAGS set to: $JAVA_CFLAGS]) -fi -if test "x$JAVA_LDFLAGS" != "x"; then - AC_MSG_NOTICE([Building with JAVA_LDFLAGS set to: $JAVA_LDFLAGS]) -fi -if test "x$JAVA_LIBS" != "x"; then - AC_MSG_NOTICE([Building with JAVA_LIBS set to: $JAVA_LIBS]) -fi if test "x$JAVAC" = "x"; then with_javac_path="$PATH" if test "x$with_java_home" != "x"; then @@ -3043,7 +2999,6 @@ fi if test "x$with_java" = "xyes"; then JAVA_LIBS="$JAVA_LIBS -ljvm" - AC_MSG_NOTICE([Building with JAVA_LIBS set to: $JAVA_LIBS]) fi CPPFLAGS="$SAVE_CPPFLAGS" @@ -3083,10 +3038,6 @@ CPPFLAGS="$CPPFLAGS $LIBLDAP_CPPFLAGS" LDFLAGS="$LDFLAGS $LIBLDAP_LDFLAGS" if test "x$with_libldap" = "xyes"; then - if test "x$LIBLDAP_CPPFLAGS" != "x"; then - AC_MSG_NOTICE([libldap CPPFLAGS: $LIBLDAP_CPPFLAGS]) - fi - AC_CHECK_HEADERS([ldap.h], [with_libldap="yes"], [with_libldap="no ('ldap.h' not found)"] @@ -3094,10 +3045,6 @@ if test "x$with_libldap" = "xyes"; then fi if test "x$with_libldap" = "xyes"; then - if test "x$LIBLDAP_LDFLAGS" != "x"; then - AC_MSG_NOTICE([libldap LDFLAGS: $LIBLDAP_LDFLAGS]) - fi - AC_CHECK_LIB([ldap], [ldap_initialize], [with_libldap="yes"], [with_libldap="no (symbol 'ldap_initialize' not found)"] @@ -3405,7 +3352,6 @@ AC_ARG_WITH([libmodbus], # configure using pkg-config if test "x$with_libmodbus" = "xuse_pkgconfig"; then - AC_MSG_NOTICE([Checking for libmodbus using $PKG_CONFIG]) $PKG_CONFIG --exists 'libmodbus' 2>/dev/null if test $? -ne 0; then with_libmodbus="no (pkg-config doesn't know libmodbus)" @@ -3488,10 +3434,6 @@ if test "x$with_libmongoc" = "xyes"; then CPPFLAGS="$CPPFLAGS $LIBMONGOC_CFLAGS" - if test "x$CPPFLAGS" != "x"; then - AC_MSG_NOTICE([libmongoc CPPFLAGS: $LIBMONGOC_CFLAGS]) - fi - AC_CHECK_HEADERS([mongoc.h], [with_libmongoc="yes"], [with_libmongoc="no ('mongoc.h' not found)"] @@ -3507,10 +3449,6 @@ if test "x$with_libmongoc" = "xyes"; then CPPFLAGS="$CPPFLAGS $LIBMONGOC_CFLAGS" LDFLAGS="$LDFLAGS $LIBMONGOC_LDFLAGS" - if test "x$LIBMONGOC_LDFLAGS" != "x"; then - AC_MSG_NOTICE([libmongoc LDFLAGS: $LIBMONGOC_LDFLAGS]) - fi - AC_CHECK_LIB([mongoc-1.0], [mongoc_init], [with_libmongoc="yes"], [with_libmongoc="no (symbol 'mongoc_init' not found)"] @@ -3523,10 +3461,12 @@ fi if test "x$with_libmongoc" = "xyes"; then BUILD_WITH_LIBMONGOC_CFLAGS="$LIBMONGOC_CFLAGS" BUILD_WITH_LIBMONGOC_LDFLAGS="$LIBMONGOC_LDFLAGS" + BUILD_WITH_LIBMONGOC_LIBS="$LIBMONGOC_LIBS" fi AC_SUBST([BUILD_WITH_LIBMONGOC_CFLAGS]) AC_SUBST([BUILD_WITH_LIBMONGOC_LDFLAGS]) +AC_SUBST([BUILD_WITH_LIBMONGOC_LIBS]) # }}} # --with-libmosquitto {{{ @@ -3812,9 +3752,6 @@ CPPFLAGS="$CPPFLAGS $LIBNETAPP_CPPFLAGS" LDFLAGS="$LDFLAGS $LIBNETAPP_LDFLAGS" if test "x$with_libnetapp" = "xyes"; then - if test "x$LIBNETAPP_CPPFLAGS" != "x"; then - AC_MSG_NOTICE([netapp CPPFLAGS: $LIBNETAPP_CPPFLAGS]) - fi AC_CHECK_HEADERS([netapp_api.h], [with_libnetapp="yes"], [with_libnetapp="no (netapp_api.h not found)"] @@ -3822,16 +3759,10 @@ if test "x$with_libnetapp" = "xyes"; then fi if test "x$with_libnetapp" = "xyes"; then - if test "x$LIBNETAPP_LDFLAGS" != "x"; then - AC_MSG_NOTICE([netapp LDFLAGS: $LIBNETAPP_LDFLAGS]) - fi - if test "x$LIBNETAPP_LIBS" = "x"; then LIBNETAPP_LIBS="$PTHREAD_LIBS -lxml -ladt -lssl -lm -lcrypto -lz" fi - AC_MSG_NOTICE([netapp LIBS: $LIBNETAPP_LIBS]) - AC_CHECK_LIB([netapp], [na_server_invoke_elem], [with_libnetapp="yes"], [with_libnetapp="no (symbol na_server_invoke_elem not found)"], @@ -3916,7 +3847,10 @@ if test "x$with_libnetsnmp" = "xyes"; then SAVE_CPPFLAGS="$CPPFLAGS" SAVE_LDFLAGS="$LDFLAGS" SAVE_LIBS="$LIBS" - CPPFLAGS="$CPPFLAGS $with_libnetsnmp_cppflags -Wall -Werror" + CPPFLAGS="$CPPFLAGS $with_libnetsnmp_cppflags" + if test "x$GCC" = "xyes"; then + CPPFLAGS="$CPPFLAGS -Wall -Werror" + fi LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags" LIBS="$LIBS -lnetsnmp" @@ -4016,7 +3950,13 @@ if test "x$with_libnetsnmpagent" = "xyes"; then ) AC_CHECK_LIB([netsnmpagent], [init_agent], - [with_libnetsnmpagent="yes"], + [ + # libnetsnmp can be built without mib loading support + AC_CHECK_LIB([netsnmp], [get_tree], + [with_libnetsnmpagent="yes"], + [with_libnetsnmpagent="no (libnetsnmp doesn't support mib loading)"] + ) + ], [with_libnetsnmpagent="no (libnetsnmpagent not found)"], [$libnetsnmphelpers] ) @@ -4025,6 +3965,8 @@ if test "x$with_libnetsnmpagent" = "xyes"; then fi if test "x$with_libnetsnmpagent" = "xyes"; then + BUILD_WITH_LIBNETSNMPAGENT_CPPFLAGS="$with_libnetsnmpagent_cppflags" + BUILD_WITH_LIBNETSNMPAGENT_LDFLAGS="$with_libnetsnmpagent_ldflags" BUILD_WITH_LIBNETSNMPAGENT_LIBS="-lnetsnmpagent $libnetsnmphelpers" fi @@ -4379,7 +4321,10 @@ if test "x$with_libperl" = "xyes"; then # (see issues #41 and #42) SAVE_CFLAGS="$CFLAGS" SAVE_LIBS="$LIBS" - CFLAGS="$CFLAGS $PERL_CFLAGS -Wall -Werror" + CFLAGS="$CFLAGS $PERL_CFLAGS" + if test "x$GCC" = "xyes"; then + CFLAGS="$CFLAGS -Wall -Werror" + fi LIBS="$LIBS $PERL_LIBS" AC_CACHE_CHECK([for broken Perl_load_module()], @@ -4819,7 +4764,7 @@ if test "$PYTHON_CONFIG" != ""; then if test $? -ne 0; then with_libpython="no" fi - LIBPYTHON_LIBS="`${PYTHON_CONFIG} --libs`" + LIBPYTHON_LIBS="`${PYTHON_CONFIG} --libs --embed`" || LIBPYTHON_LIBS="`${PYTHON_CONFIG} --libs`" if test $? -ne 0; then with_libpython="no" fi @@ -5268,6 +5213,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" @@ -5636,7 +5602,6 @@ fi # configure using pkg-config if test "x$with_libupsclient" = "xuse_pkgconfig"; then - AC_MSG_NOTICE([Checking for libupsclient using $PKG_CONFIG]) $PKG_CONFIG --exists 'libupsclient' 2>/dev/null if test $? -ne 0; then with_libupsclient="no (pkg-config doesn't know libupsclient)" @@ -5797,7 +5762,7 @@ if test "x$with_libxmms" = "xyes"; then fi if test "x$with_libxmms" = "xyes"; then - SAVE_CPPFLAGS="$CFLAGS" + SAVE_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$with_xmms_cflags" AC_CHECK_HEADER([xmmsctrl.h], @@ -6437,6 +6402,7 @@ plugin_pcie_errors="no" plugin_perl="no" plugin_pinba="no" plugin_processes="no" +plugin_procevent="no" plugin_protocols="no" plugin_python="no" plugin_serial="no" @@ -6513,6 +6479,7 @@ if test "x$ac_system" = "xLinux"; then if test "x$with_libyajl" = "xyes" && test "x$with_libyajl2" = "xyes"; then plugin_ovs_events="yes" plugin_ovs_stats="yes" + plugin_procevent="yes" fi if test "x$have_pci_regs_h" = "xyes"; then @@ -6547,6 +6514,7 @@ fi # FreeBSD if test "x$ac_system" = "xFreeBSD"; then + plugin_cpufreq="yes" plugin_disk="yes" plugin_zfs_arc="yes" fi @@ -6660,7 +6628,7 @@ if test "x$c_cv_have_one_getmntent" = "xyes"; then plugin_df="yes" fi -if test "x$c_cv_have_getmntent_r" = "xyes"; then +if test "x$have_getmntent_r" = "xyes"; then plugin_df="yes" fi @@ -6822,6 +6790,7 @@ AC_PLUGIN([bind], [$plugin_bind], [ISC Bind nameserv 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([check_uptime], [yes], [Notify about uptime reset]) AC_PLUGIN([conntrack], [$plugin_conntrack], [nf_conntrack statistics]) AC_PLUGIN([contextswitch], [$plugin_contextswitch], [context switch statistics]) AC_PLUGIN([cpu], [$plugin_cpu], [CPU usage statistics]) @@ -6910,6 +6879,7 @@ AC_PLUGIN([ping], [$with_liboping], [Network latency s AC_PLUGIN([postgresql], [$with_libpq], [PostgreSQL database statistics]) AC_PLUGIN([powerdns], [yes], [PowerDNS statistics]) AC_PLUGIN([processes], [$plugin_processes], [Process statistics]) +AC_PLUGIN([procevent], [$plugin_procevent], [Process event (start, stop) 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]) @@ -6954,7 +6924,6 @@ AC_PLUGIN([vserver], [$plugin_vserver], [Linux VServer sta 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]) @@ -6962,6 +6931,8 @@ AC_PLUGIN([write_prometheus], [$plugin_write_prometheus], [Prometheus write 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_stackdriver], [$plugin_write_stackdriver], [Google Stackdriver Monitoring output plugin]) +AC_PLUGIN([write_syslog], [yes], [Syslog 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]) @@ -7105,11 +7076,13 @@ AC_SUBST([LCC_VERSION_STRING]) AC_CONFIG_FILES([src/libcollectdclient/collectd/lcc_features.h]) -AM_CFLAGS="-Wall" -AM_CXXFLAGS="-Wall" -if test "x$enable_werror" != "xno"; then - AM_CFLAGS="$AM_CFLAGS -Werror" - AM_CXXFLAGS="$AM_CXXFLAGS -Werror" +if test "x$GCC" = "xyes"; then + AM_CFLAGS="-Wall" + AM_CXXFLAGS="-Wall" + if test "x$enable_werror" != "xno"; then + AM_CFLAGS="$AM_CFLAGS -Werror" + AM_CXXFLAGS="$AM_CXXFLAGS -Werror" + fi fi AC_SUBST([AM_CFLAGS]) @@ -7159,7 +7132,6 @@ AC_MSG_RESULT([ YACC . . . . . . . . $YACC]) AC_MSG_RESULT([ YFLAGS . . . . . . . $YFLAGS]) AC_MSG_RESULT() AC_MSG_RESULT([ Libraries:]) -AC_MSG_RESULT([ cuda . . . . . . . . $with_cuda]) AC_MSG_RESULT([ intel mic . . . . . . $with_mic]) AC_MSG_RESULT([ libaquaero5 . . . . . $with_libaquaero5]) AC_MSG_RESULT([ libatasmart . . . . . $with_libatasmart]) @@ -7193,6 +7165,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]) @@ -7249,6 +7222,7 @@ AC_MSG_RESULT([ bind . . . . . . . . $enable_bind]) AC_MSG_RESULT([ ceph . . . . . . . . $enable_ceph]) AC_MSG_RESULT([ cgroups . . . . . . . $enable_cgroups]) AC_MSG_RESULT([ chrony. . . . . . . . $enable_chrony]) +AC_MSG_RESULT([ check_uptime. . . . . $enable_check_uptime]) AC_MSG_RESULT([ conntrack . . . . . . $enable_conntrack]) AC_MSG_RESULT([ contextswitch . . . . $enable_contextswitch]) AC_MSG_RESULT([ cpu . . . . . . . . . $enable_cpu]) @@ -7336,6 +7310,7 @@ AC_MSG_RESULT([ ping . . . . . . . . $enable_ping]) AC_MSG_RESULT([ postgresql . . . . . $enable_postgresql]) AC_MSG_RESULT([ powerdns . . . . . . $enable_powerdns]) AC_MSG_RESULT([ processes . . . . . . $enable_processes]) +AC_MSG_RESULT([ procevent . . . . . . $enable_procevent]) AC_MSG_RESULT([ protocols . . . . . . $enable_protocols]) AC_MSG_RESULT([ python . . . . . . . $enable_python]) AC_MSG_RESULT([ redis . . . . . . . . $enable_redis]) @@ -7388,6 +7363,7 @@ 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_syslog . . . . $enable_write_syslog]) AC_MSG_RESULT([ write_tsdb . . . . . $enable_write_tsdb]) AC_MSG_RESULT([ xencpu . . . . . . . $enable_xencpu]) AC_MSG_RESULT([ xmms . . . . . . . . $enable_xmms]) diff --git a/contrib/collectd_network.py b/contrib/collectd_network.py index 809f19de..cb328f2b 100644 --- a/contrib/collectd_network.py +++ b/contrib/collectd_network.py @@ -16,7 +16,7 @@ Collectd network protocol implementation. """ -import socket,struct,sys +import socket,struct import platform if platform.python_version() < '2.8.0': # Python 2.7 and below io.StringIO does not like unicode 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/navigate.js b/contrib/collection3/share/navigate.js index 3bfe56ed..0cf15132 100644 --- a/contrib/collection3/share/navigate.js +++ b/contrib/collection3/share/navigate.js @@ -183,12 +183,11 @@ function nav_calculate_offset_x (obj) function nav_calculate_event_x (e) { var pos = 0; - var off = 0; if (!e || !e.target) return; - off = nav_calculate_offset_x (e.target); + nav_calculate_offset_x (e.target); if (e.pageX || e.pageY) { 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 1b71704f..43b0ec95 100644 --- a/contrib/docker/50docker-apt-conf +++ b/contrib/docker/50docker-apt-conf @@ -1,4 +1,4 @@ APT::Install-Recommends "1"; -APT::Install-Suggests "0"; +APT::Install-Suggests "1"; APT::Get::Assume-Yes "1"; APT::Get::AutomaticRemove "1"; diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index a691f2e6..fcb46b13 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:stable +FROM debian:stable-slim ENV DEBIAN_FRONTEND noninteractive COPY 50docker-apt-conf /etc/apt/apt.conf.d/ @@ -21,4 +21,5 @@ COPY collectd.conf.d /etc/collectd/collectd.conf.d ENV LD_PRELOAD /usr/src/rootfs_prefix/rootfs_prefix.so -CMD [ "/usr/sbin/collectd", "-f"] +ENTRYPOINT ["/usr/sbin/collectd"] +CMD ["-f"] diff --git a/contrib/php-collection/browser.js b/contrib/php-collection/browser.js index 4ddc424d..09673da8 100644 --- a/contrib/php-collection/browser.js +++ b/contrib/php-collection/browser.js @@ -106,8 +106,8 @@ function refillSelect(options, select) { newOption.setAttribute('style', 'font-style: italic'); newOption.appendChild(document.createTextNode('- please select -')); select.appendChild(newOption); - for (i = 0; i < optCnt; i++) { - newOption = document.createElement("option"); + for (var i = 0; i < optCnt; i++) { + var newOption = document.createElement("option"); newOption.value = options[i].firstChild ? options[i].firstChild.data : ''; if (keepSelection && newOption.value == oldValue) newOption.setAttribute('selected', 'selected'); @@ -311,7 +311,7 @@ function GraphAppend() { function ListOfGraph(response) { var graphs = response ? response.getElementsByTagName('graph') : null; if (graphs && graphs.length > 0) { - for (i = 0; i < graphs.length; i++) + for (var i = 0; i < graphs.length; i++) GraphDoAppend(graphs[i].getAttribute('host'), graphs[i].getAttribute('plugin'), graphs[i].getAttribute('plugin_instance'), graphs[i].getAttribute('type'), graphs[i].getAttribute('type_instance'), graphs[i].getAttribute('timespan'), graphs[i].getAttribute('tinyLegend') == '1', graphs[i].getAttribute('logarithmic') == '1'); @@ -326,7 +326,7 @@ function GraphDoAppend(host, plugin, pinst, type, tinst, timespan, tinyLegend, l var graph_id = 'graph_'+nextGraphId++; var graph_src = graph_url+'?host='+encodeURIComponent(host)+'&plugin='+encodeURIComponent(plugin)+'&plugin_instance='+encodeURIComponent(pinst)+'&type='+encodeURIComponent(type); var graph_alt = ''; - var grap_title = ''; + var graph_title = ''; if (tinst == '@') { graph_alt = host+'/'+plugin+(pinst.length > 0 ? '-'+pinst : '')+'/'+type; graph_title = type+' of '+plugin+(pinst.length > 0 ? '-'+pinst : '')+' plugin for '+host; @@ -346,11 +346,11 @@ function GraphDoAppend(host, plugin, pinst, type, tinst, timespan, tinyLegend, l graphList.push(graph_id+' '+encodeURIComponent(graph_alt)+(logarithmic ? '&logarithmic=1' : '')+(tinyLegend ? '&tinylegend=1' : '')+'×pan='+encodeURIComponent(timespan)); // Graph container - newGraph = document.createElement('div'); + var newGraph = document.createElement('div'); newGraph.setAttribute('class', 'graph'); newGraph.setAttribute('id', graph_id); // Graph cell + graph - newImg = document.createElement('img'); + var newImg = document.createElement('img'); newImg.setAttribute('src', graph_src); newImg.setAttribute('alt', graph_alt); newImg.setAttribute('title', graph_title); @@ -397,7 +397,7 @@ function GraphToggleTools(graph) { document.getElementById('ge_graphid').value = graph; document.getElementById('ge_tinylegend').checked = src_url.match(/&tinylegend=1/); document.getElementById('ge_logarithmic').checked = src_url.match(/&logarithmic=1/); - for (i = 0; i < ts_sel.options.length; i++) + for (var i = 0; i < ts_sel.options.length; i++) if (ts_sel.options[i].value == ts) { ts_sel.selectedIndex = i; break; @@ -513,7 +513,7 @@ function GraphAdjust(graph) { imgs[imgCnt].setAttribute('src', newSrc); var myList = Array(); - for (i = 0; i < graphList.length; i++) + for (var i = 0; i < graphList.length; i++) if (graphList[i].substring(0, graphId.length) == graphId && graphList[i].charAt(graphId.length) == ' ') { newSrc = graphList[i]; newSrc = newSrc.replace(/&logarithmic=[^&]*/, ''); @@ -544,7 +544,7 @@ function GraphRemove(graph) { document.getElementById('nograph').style.display = 'block'; var myList = Array(); - for (i = 0; i < graphList.length; i++) + for (var i = 0; i < graphList.length; i++) if (graphList[i].substring(0, graphId.length) == graphId && graphList[i].charAt(graphId.length) == ' ') continue; else @@ -558,7 +558,7 @@ function GraphMoveUp(graph) { var graphId = graph == null ? document.getElementById('ge_graphid').value : graph; var childCnt = graphs.childNodes.length; var prevGraph = null; - for (i = 0; i < childCnt; i++) + for (var i = 0; i < childCnt; i++) if (graphs.childNodes[i].nodeName == 'div' || graphs.childNodes[i].nodeName == 'DIV') { if (graphs.childNodes[i].id == 'nograph') { // Skip @@ -590,7 +590,7 @@ function GraphMoveDown(graph) { var childCnt = graphs.childNodes.length; var nextGraph = null; var myGraph = null; - for (i = 0; i < childCnt; i++) + for (var i = 0; i < childCnt; i++) if (graphs.childNodes[i].nodeName == 'div' || graphs.childNodes[i].nodeName == 'DIV') { if (graphs.childNodes[i].id == 'nograph') { // Skip @@ -603,12 +603,12 @@ function GraphMoveDown(graph) { break; } } - for (i = 0; i < graphList.length; i++) - if (graphList[i].substring(0, graphId.length) == graphId && graphList[i].charAt(graphId.length) == ' ') { - if (i+1 < graphList.length) { - var tmp = graphList[i+1]; - graphList[i+1] = graphList[i]; - graphList[i] = tmp; + for (var j = 0; j < graphList.length; j++) + if (graphList[j].substring(0, graphId.length) == graphId && graphList[j].charAt(graphId.length) == ' ') { + if (j+1 < graphList.length) { + var tmp = graphList[j+1]; + graphList[j+1] = graphList[i]; + graphList[j] = tmp; } break; } @@ -619,7 +619,7 @@ function GraphListFromCookie(lname) { if (document.cookie.length > 0) { var cname= 'graphLst'+lname+'='; var cookies = document.cookie.split('; '); - for (i = 0; i < cookies.length; i++) + for (var i = 0; i < cookies.length; i++) if (cookies[i].substring(0, cname.length) == cname) return cookies[i].substring(cname.length).split('/'); } @@ -663,7 +663,7 @@ function GraphListRefresh() { } else { select.removeAttribute('disabled'); for (i = 0; i < optCnt; i++) { - newOption = document.createElement("option"); + var newOption = document.createElement("option"); newOption.value = options[i][0]; if (newOption.value == oldValue) newOption.setAttribute('selected', 'selected'); @@ -705,7 +705,7 @@ function GraphSave() { if (graphList.length > 0) { // Save graph list to cookie var str = ''; - for (i = 0; i < graphList.length; i++) { + for (var i = 0; i < graphList.length; i++) { var g = graphList[i].indexOf(' '); if (i > 0) str += '/'; @@ -743,7 +743,7 @@ function GraphLoad() { // Load graph list from cookie var grLst = GraphListFromCookie(cname); var oldLength = graphList.length; - for (i = 0; i < grLst.length; i++) { + for (var i = 0; i < grLst.length; i++) { var host = ''; var plugin = ''; var pinst = ''; @@ -753,7 +753,7 @@ function GraphLoad() { var logarithmic = false; var tinyLegend = false; var graph = grLst[i].split('&'); - for (j = 0; j < graph.length; j++) + for (var j = 0; j < graph.length; j++) if (graph[j] == 'logarithmic=1') logarithmic = true; else if (graph[j] == 'tinylegend=1') diff --git a/contrib/redhat/collectd.spec b/contrib/redhat/collectd.spec index 537edf73..8bde54c2 100644 --- a/contrib/redhat/collectd.spec +++ b/contrib/redhat/collectd.spec @@ -116,12 +116,14 @@ %define with_openvpn 0%{!?_without_openvpn:1} %define with_ovs_events 0%{!?_without_ovs_events:1} %define with_ovs_stats 0%{!?_without_ovs_stats:1} +%define with_pcie_errors 0%{!?_without_pcie_errors:1} %define with_perl 0%{!?_without_perl:1} %define with_pinba 0%{!?_without_pinba:1} %define with_ping 0%{!?_without_ping:1} %define with_postgresql 0%{!?_without_postgresql:1} %define with_powerdns 0%{!?_without_powerdns:1} %define with_processes 0%{!?_without_processes:1} +%define with_procevent 0%{!?_without_procevent:1} %define with_protocols 0%{!?_without_protocols:1} %define with_python 0%{!?_without_python:1} %define with_redis 0%{!?_without_redis:1} @@ -161,7 +163,9 @@ %define with_write_prometheus 0%{!?_without_write_prometheus:1} %define with_write_redis 0%{!?_without_write_redis:1} %define with_write_riemann 0%{!?_without_write_riemann:1} +%define with_write_stackdriver 0%{!?_without_write_stackdriver:1} %define with_write_sensu 0%{!?_without_write_sensu:1} +%define with_write_syslog 0%{!?_without_write_syslog:1} %define with_write_tsdb 0%{!?_without_write_tsdb:1} %define with_xmms 0%{!?_without_xmms:0%{?_has_xmms}} %define with_zfs_arc 0%{!?_without_zfs_arc:1} @@ -214,6 +218,12 @@ %define with_xencpu 0%{!?_without_xencpu:0} # plugin zone disabled, requires Solaris %define with_zone 0%{!?_without_zone:0} +# plugin gpu_nvidia requires cuda-nvml-dev +# get it from https://developer.nvidia.com/cuda-downloads +# then install cuda-nvml-dev-10-1 or other version +%define with_gpu_nvidia 0%{!?_without_gpu_nvidia:0} +# not sure why this one's failing +%define with_write_stackdriver 0%{!?_without_write_stackdriver:0} # Plugins not buildable on RHEL < 6 %if 0%{?rhel} && 0%{?rhel} < 6 @@ -243,6 +253,7 @@ %define with_mqtt 0 %define with_ovs_events 0 %define with_ovs_stats 0 +%define with_procevent 0 %define with_redis 0 %define with_rrdcached 0 %define with_sysevent 0 @@ -253,8 +264,8 @@ Summary: Statistics collection and monitoring daemon Name: collectd -Version: 5.7.1 -Release: 9%{?dist} +Version: 5.9.0 +Release: 1%{?dist} URL: https://collectd.org Source: https://collectd.org/files/%{name}-%{version}.tar.bz2 License: GPLv2 @@ -742,6 +753,16 @@ The Perl plugin embeds a Perl interpreter into collectd and exposes the application programming interface (API) to Perl-scripts. %endif +%if %{with_pcie_errors} +%package pcie_errors +Summary: PCI Express errors plugin for collectd +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +%description pcie_errors +The pcie_errors plugin collects PCI Express errors from Device Status in Capability +structure and from Advanced Error Reporting Extended Capability. +%endif + %if %{with_pinba} %package pinba Summary: Pinba plugin for collectd @@ -775,6 +796,16 @@ The PostgreSQL plugin connects to and executes SQL statements on a PostgreSQL database. %endif +%if %{with_procevent} +%package procevent +Summary: Processes event plugin for collectd +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +BuildRequires: yajl-devel +%description procevent +Monitors process starts/stops via netlink library. +%endif + %if %{with_python} %package python Summary: Python plugin for collectd @@ -956,6 +987,27 @@ BuildRequires: riemann-c-client-devel >= 1.6 The riemann plugin submits values to Riemann, an event stream processor. %endif +%if %{with_write_stackdriver} +%package write_stackdriver +Summary: stackdriver plugin for collectd +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +BuildRequires: curl-devel, yajl-devel, openssl-devel +%description write_stackdriver +The write_stackdriver collectd plugin writes metrics to the +Google Stackdriver Monitoring service. +%endif + +%if %{with_gpu_nvidia} +%package gpu_nvidia +Summary: stackdriver plugin for collectd +Group: System Environment/Daemons +Requires: %{name}%{?_isa} = %{version}-%{release} +BuildRequires: cuda-nvml-dev-10-1 +%description gpu_nvidia +The gpu_nvidia collectd plugin collects NVidia GPU metrics. +%endif + %if %{with_xencpu} %package xencpu Summary: xencpu plugin for collectd @@ -1564,6 +1616,12 @@ Collectd utilities %define _with_perl --disable-perl %endif +%if %{with_pcie_errors} +%define _with_pcie_errors --enable-pcie_errors +%else +%define _with_pcie_errors --disable-pcie_errors +%endif + %if %{with_pf} %define _with_pf --enable-pf %else @@ -1600,6 +1658,12 @@ Collectd utilities %define _with_processes --disable-processes %endif +%if %{with_procevent} +%define _with_procevent --enable-procevent +%else +%define _with_procevent --disable-procevent +%endif + %if %{with_protocols} %define _with_protocols --enable-protocols %else @@ -1869,12 +1933,30 @@ Collectd utilities %define _with_write_riemann --disable-write_riemann %endif +%if %{with_write_stackdriver} +%define _with_write_stackdriver --enable-write_stackdriver +%else +%define _with_write_stackdriver --disable-write_stackdriver +%endif + +%if %{with_gpu_nvidia} +%define _with_gpu_nvidia --enable-gpu_nvidia +%else +%define _with_gpu_nvidia --disable-gpu_nvidia +%endif + %if %{with_write_sensu} %define _with_write_sensu --enable-write_sensu %else %define _with_write_sensu --disable-write_sensu %endif +%if %{with_write_syslog} +%define _with_write_syslog --enable-write_syslog +%else +%define _with_write_syslog --disable-write_syslog +%endif + %if %{with_write_tsdb} %define _with_write_tsdb --enable-write_tsdb %else @@ -2020,12 +2102,14 @@ Collectd utilities %{?_with_ovs_events} \ %{?_with_ovs_stats} \ %{?_with_perl} \ + %{?_with_pcie_errors} \ %{?_with_pf} \ %{?_with_pinba} \ %{?_with_ping} \ %{?_with_postgresql} \ %{?_with_powerdns} \ %{?_with_processes} \ + %{?_with_procevent} \ %{?_with_protocols} \ %{?_with_python} \ %{?_with_redis} \ @@ -2072,7 +2156,10 @@ Collectd utilities %{?_with_write_prometheus} \ %{?_with_write_redis} \ %{?_with_write_riemann} \ + %{?_with_write_stackdriver} \ + %{?_with_gpu_nvidia} \ %{?_with_write_sensu} \ + %{?_with_write_syslog} \ %{?_with_write_tsdb} \ %{?_with_xencpu} \ %{?_with_xmms} \ @@ -2407,6 +2494,9 @@ fi %if %{with_write_log} %{_libdir}/%{name}/write_log.so %endif +%if %{with_write_syslog} +%{_libdir}/%{name}/write_syslog.so +%endif %if %{with_write_sensu} %{_libdir}/%{name}/write_sensu.so %endif @@ -2675,6 +2765,11 @@ fi %{_libdir}/%{name}/perl.so %endif +%if %{with_pcie_errors} +%files pcie_errors +%{_libdir}/%{name}/pcie_errors.so +%endif + %if %{with_pinba} %files pinba %{_libdir}/%{name}/pinba.so @@ -2691,6 +2786,11 @@ fi %{_libdir}/%{name}/postgresql.so %endif +%if %{with_procevent} +%files procevent +%{_libdir}/%{name}/procevent.so +%endif + %if %{with_python} %files python %{_mandir}/man5/collectd-python* @@ -2773,6 +2873,16 @@ fi %{_libdir}/%{name}/write_riemann.so %endif +%if %{with_write_stackdriver} +%files write_stackdriver +%{_libdir}/%{name}/write_stackdriver.so +%endif + +%if %{with_gpu_nvidia} +%files write_gpu_nvidia +%{_libdir}/%{name}/write_gpu_nvidia.so +%endif + %if %{with_xencpu} %files xencpu %{_libdir}/%{name}/xencpu.so @@ -2795,6 +2905,11 @@ fi %doc contrib/ %changelog +* Fri Jun 14 2019 Fabien Wernli - 5.9.0-1 +- add code for write_stackdriver (disabled for now) +- add code for gpu_nvidia (disabled for now) +- add pcie_errors + * Thu Sep 28 2017 Jakub Jankowski - 5.7.1-9 - Fix mbmon/mcelog build options diff --git a/contrib/systemd.collectd.service b/contrib/systemd.collectd.service index c5b1142b..fe535bfe 100644 --- a/contrib/systemd.collectd.service +++ b/contrib/systemd.collectd.service @@ -8,8 +8,6 @@ Requires=local-fs.target network-online.target ExecStart=/usr/sbin/collectd EnvironmentFile=-/etc/sysconfig/collectd EnvironmentFile=-/etc/default/collectd -ProtectSystem=full -ProtectHome=true # A few plugins won't work without some privileges, which you'll have to # specify using the CapabilityBoundingSet directive below. diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 00000000..ca0f40f1 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,85 @@ +# Contribution guidelines + +Thanks for taking the time to contribute to the [collectd +project](https://collectd.org/)! This document tries to give some guidance to +make the process of contributing to *collectd* as pleasant as possible. + +## Bug reports + +Please report bugs as [GitHub +Issues](https://github.com/collectd/collectd/issues). Try to answer the +following questions: + +* Which version of *collectd* are you using? +* Which operating system (distribution) are you using at which version? +* What is the expected behavior / output? +* What is the actual (observed) behavior / output? +* How can we reproduce the problem you're having? +* If *collectd* crashes, try to get a + [stack trace](https://collectd.org/wiki/index.php/Core_file). + +Please monitor your issue for a couple of days and reply to questions. To keep +the project manageable, we have to do some housekeeping; meaning we will close +issues that have become stale. + +## Code contributions + +Please open a [GitHub Pull Request](https://github.com/collectd/collectd/pulls) +(PR) to contribute bug fixes, features, cleanups, new plugins, … Patches sent to +the mailing list have a tendency to fall through the cracks. + +* *Focus:* Fix *one thing* in your PR. The smaller your change, the faster it + will be reviewed and merged. +* *Coding style:* Please run `clang-format -style=file -i $FILE` after editing + `.c`, `.h` and `.proto` files. If you don't want to install *clang-format* + locally or your version produces a different result than the formatting + check on Github, use `contrib/format.sh` to format files using the same web + service used by our check. +* *Documentation:* New config options need to be documented in two places: the + manpage (`src/collectd.conf.pod`) and the example config + (`src/collectd.conf.in`). New plugins need to be added to the `README` file. +* *Continuous integration:* Once your PR is created, our continuous + integration environment will try to build it on a number of platforms. If + this reports a failure, please investigate and fix the problem. We will at + best do a very casual review for failing PRs. +* *Don't rebase:* Rebasing your branch destroys the review history. If a + review takes a long time, we may ask you to rebase on a more recent + *master*, but please don't do that without being asked. +* *types.db:* One of the most common mistakes made by new contributors is the + addition of (many) new *types* in the file `src/types.db`. The majority of + usecases can be met with one of the existing entries. If you plan to add new + entries to `src/types.db`, you should talk to us early in the design + process. + +### ChangeLog + +PRs need to have a one-line summary in the *PR description*. This information +is used to automatically generate release notes. If you got here after creating +the PR, you need to go to the *PR description* (shown as the first "comment" on +the PR, made by yourself) and *edit* that description. Editing a PR will +trigger the "ChangeLog" status to be updated. + +For the summary itself, follow this style: + +``` +ChangeLog: Foo plugin: A specific issue people had has been fixed. +``` + +The summary must be on a line of its own, with a "ChangeLog:" prefix at the +beginning of the line. The text should start with "Foo plugin" to give the +reader context for the information. Other common contexts are "collectd" for +the core daemon, "Build system", and "Documentation". Use past tense and +passive voice the for remainder, e.g. "a bug has been fixed", "a feature has +been added". + +Some PRs should be excluded from 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) +* [#collectd IRC channel](https://webchat.freenode.net/?channels=#collectd) + on *freenode*. +* [Old patch submission guideline](https://collectd.org/wiki/index.php/Submitting_patches) diff --git a/src/aggregation.c b/src/aggregation.c index a8021996..2c8ef880 100644 --- a/src/aggregation.c +++ b/src/aggregation.c @@ -26,12 +26,12 @@ #include "collectd.h" -#include "common.h" -#include "meta_data.h" #include "plugin.h" +#include "utils/common/common.h" +#include "utils/lookup/vl_lookup.h" +#include "utils/metadata/meta_data.h" #include "utils_cache.h" /* for uc_get_rate() */ #include "utils_subst.h" -#include "utils_vl_lookup.h" #define AGG_MATCHES_ALL(str) (strcmp("/.*/", str) == 0) #define AGG_FUNC_PLACEHOLDER "%{aggregation}" @@ -198,15 +198,17 @@ static int agg_instance_create_name(agg_instance_t *inst, /* {{{ */ sstrncpy(inst->ident.plugin_instance, AGG_FUNC_PLACEHOLDER, sizeof(inst->ident.plugin_instance)); else if (strcmp("", tmp_plugin) != 0) - snprintf(inst->ident.plugin_instance, sizeof(inst->ident.plugin_instance), - "%s-%s", tmp_plugin, AGG_FUNC_PLACEHOLDER); + ssnprintf(inst->ident.plugin_instance, + sizeof(inst->ident.plugin_instance), "%s-%s", tmp_plugin, + AGG_FUNC_PLACEHOLDER); else if (strcmp("", tmp_plugin_instance) != 0) - snprintf(inst->ident.plugin_instance, sizeof(inst->ident.plugin_instance), - "%s-%s", tmp_plugin_instance, AGG_FUNC_PLACEHOLDER); + ssnprintf(inst->ident.plugin_instance, + sizeof(inst->ident.plugin_instance), "%s-%s", + tmp_plugin_instance, AGG_FUNC_PLACEHOLDER); else - snprintf(inst->ident.plugin_instance, sizeof(inst->ident.plugin_instance), - "%s-%s-%s", tmp_plugin, tmp_plugin_instance, - AGG_FUNC_PLACEHOLDER); + ssnprintf(inst->ident.plugin_instance, + sizeof(inst->ident.plugin_instance), "%s-%s-%s", tmp_plugin, + tmp_plugin_instance, AGG_FUNC_PLACEHOLDER); } /* Type */ @@ -396,10 +398,9 @@ static int agg_instance_read(agg_instance_t *inst, cdtime_t t) /* {{{ */ READ_FUNC(average, (inst->sum / ((gauge_t)inst->num))); READ_FUNC(min, inst->min); READ_FUNC(max, inst->max); - READ_FUNC(stddev, - sqrt((((gauge_t)inst->num) * inst->squares_sum) - - (inst->sum * inst->sum)) / - ((gauge_t)inst->num)); + READ_FUNC(stddev, sqrt((((gauge_t)inst->num) * inst->squares_sum) - + (inst->sum * inst->sum)) / + ((gauge_t)inst->num)); } /* Reset internal state. */ diff --git a/src/amqp.c b/src/amqp.c index 281130b1..2077d57b 100644 --- a/src/amqp.c +++ b/src/amqp.c @@ -28,11 +28,11 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_cmd_putval.h" -#include "utils_format_graphite.h" -#include "utils_format_json.h" +#include "utils/cmds/putval.h" +#include "utils/common/common.h" +#include "utils/format_graphite/format_graphite.h" +#include "utils/format_json/format_json.h" #include #include @@ -217,23 +217,23 @@ static char *camqp_strerror(camqp_config_t *conf, /* {{{ */ if (r.reply.id == AMQP_CONNECTION_CLOSE_METHOD) { amqp_connection_close_t *m = r.reply.decoded; char *tmp = camqp_bytes_cstring(&m->reply_text); - snprintf(buffer, buffer_size, "Server connection error %d: %s", - m->reply_code, tmp); + ssnprintf(buffer, buffer_size, "Server connection error %d: %s", + m->reply_code, tmp); sfree(tmp); } else if (r.reply.id == AMQP_CHANNEL_CLOSE_METHOD) { amqp_channel_close_t *m = r.reply.decoded; char *tmp = camqp_bytes_cstring(&m->reply_text); - snprintf(buffer, buffer_size, "Server channel error %d: %s", - m->reply_code, tmp); + ssnprintf(buffer, buffer_size, "Server channel error %d: %s", + m->reply_code, tmp); sfree(tmp); } else { - snprintf(buffer, buffer_size, "Server error method %#" PRIx32, - r.reply.id); + ssnprintf(buffer, buffer_size, "Server error method %#" PRIx32, + r.reply.id); } break; default: - snprintf(buffer, buffer_size, "Unknown reply type %i", (int)r.reply_type); + ssnprintf(buffer, buffer_size, "Unknown reply type %i", (int)r.reply_type); } return buffer; @@ -320,16 +320,17 @@ static int camqp_setup_queue(camqp_config_t *conf) /* {{{ */ amqp_queue_declare_ok_t *qd_ret; amqp_basic_consume_ok_t *cm_ret; - qd_ret = amqp_queue_declare(conf->connection, - /* channel = */ CAMQP_CHANNEL, - /* queue = */ (conf->queue != NULL) - ? amqp_cstring_bytes(conf->queue) - : AMQP_EMPTY_BYTES, - /* passive = */ 0, - /* durable = */ conf->queue_durable, - /* exclusive = */ 0, - /* auto_delete = */ conf->queue_auto_delete, - /* arguments = */ AMQP_EMPTY_TABLE); + qd_ret = + amqp_queue_declare(conf->connection, + /* channel = */ CAMQP_CHANNEL, + /* queue = */ + (conf->queue != NULL) ? amqp_cstring_bytes(conf->queue) + : AMQP_EMPTY_BYTES, + /* passive = */ 0, + /* durable = */ conf->queue_durable, + /* exclusive = */ 0, + /* auto_delete = */ conf->queue_auto_delete, + /* arguments = */ AMQP_EMPTY_TABLE); if (qd_ret == NULL) { ERROR("amqp plugin: amqp_queue_declare failed."); camqp_close_connection(conf); @@ -353,15 +354,15 @@ static int camqp_setup_queue(camqp_config_t *conf) /* {{{ */ amqp_queue_bind_ok_t *qb_ret; assert(conf->queue != NULL); - qb_ret = - amqp_queue_bind(conf->connection, - /* channel = */ CAMQP_CHANNEL, - /* queue = */ amqp_cstring_bytes(conf->queue), - /* exchange = */ amqp_cstring_bytes(conf->exchange), - /* routing_key = */ (conf->routing_key != NULL) - ? amqp_cstring_bytes(conf->routing_key) - : AMQP_EMPTY_BYTES, - /* arguments = */ AMQP_EMPTY_TABLE); + qb_ret = amqp_queue_bind( + conf->connection, + /* channel = */ CAMQP_CHANNEL, + /* queue = */ amqp_cstring_bytes(conf->queue), + /* exchange = */ amqp_cstring_bytes(conf->exchange), + /* routing_key = */ + (conf->routing_key != NULL) ? amqp_cstring_bytes(conf->routing_key) + : AMQP_EMPTY_BYTES, + /* arguments = */ AMQP_EMPTY_TABLE); if ((qb_ret == NULL) && camqp_is_error(conf)) { char errbuf[1024]; ERROR("amqp plugin: amqp_queue_bind failed: %s", @@ -428,7 +429,7 @@ static int camqp_connect(camqp_config_t *conf) /* {{{ */ #ifdef HAVE_AMQP_TCP_SOCKET #define CLOSE_SOCKET() /* amqp_destroy_connection() closes the socket for us \ - */ + */ /* TODO: add support for SSL using amqp_ssl_socket_new * and related functions */ socket = amqp_tcp_socket_new(conf->connection); @@ -748,9 +749,9 @@ static int camqp_write(const data_set_t *ds, const value_list_t *vl, /* {{{ */ if (conf->routing_key != NULL) { sstrncpy(routing_key, conf->routing_key, sizeof(routing_key)); } else { - snprintf(routing_key, sizeof(routing_key), "collectd/%s/%s/%s/%s/%s", - vl->host, vl->plugin, vl->plugin_instance, vl->type, - vl->type_instance); + ssnprintf(routing_key, sizeof(routing_key), "collectd/%s/%s/%s/%s/%s", + vl->host, vl->plugin, vl->plugin_instance, vl->type, + vl->type_instance); /* Switch slashes (the only character forbidden by collectd) and dots * (the separation character used by AMQP). */ @@ -970,13 +971,13 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */ if (publish) { char cbname[128]; - snprintf(cbname, sizeof(cbname), "amqp/%s", conf->name); + ssnprintf(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; diff --git a/src/amqp1.c b/src/amqp1.c index 87bb50cf..67c96b75 100644 --- a/src/amqp1.c +++ b/src/amqp1.c @@ -26,12 +26,12 @@ #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/cmds/putval.h" +#include "utils/common/common.h" +#include "utils/deq/deq.h" +#include "utils/format_graphite/format_graphite.h" +#include "utils/format_json/format_json.h" #include "utils_random.h" #include @@ -624,13 +624,13 @@ static int amqp1_config_instance(oconfig_item_t *ci) /* {{{ */ return status; } else { char tpname[DATA_MAX_NAME_LEN]; - status = snprintf(tpname, sizeof(tpname), "amqp1/%s", instance->name); + status = ssnprintf(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); + status = ssnprintf(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; @@ -639,14 +639,16 @@ static int amqp1_config_instance(oconfig_item_t *ci) /* {{{ */ status = plugin_register_notification( tpname, amqp1_notify, &(user_data_t){ - .data = instance, .free_func = amqp1_config_instance_free, + .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, - }); + status = + plugin_register_write(tpname, amqp1_write, + &(user_data_t){ + .data = instance, + .free_func = amqp1_config_instance_free, + }); } if (status != 0) { diff --git a/src/apache.c b/src/apache.c index 5c67a388..4014bbaf 100644 --- a/src/apache.c +++ b/src/apache.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -224,7 +224,8 @@ static int config_add(oconfig_item_t *ci) { /* callback = */ apache_read_host, /* interval = */ 0, &(user_data_t){ - .data = st, .free_func = apache_free, + .data = st, + .free_func = apache_free, }); } /* int config_add */ diff --git a/src/apcups.c b/src/apcups.c index 2931d2c0..83a5c87a 100644 --- a/src/apcups.c +++ b/src/apcups.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" /* rrd_update_file */ -#include "plugin.h" /* plugin_register, plugin_submit */ +#include "plugin.h" /* plugin_register, plugin_submit */ +#include "utils/common/common.h" /* rrd_update_file */ #if HAVE_SYS_TYPES_H #include diff --git a/src/apple_sensors.c b/src/apple_sensors.c index f78c3da2..ad6e6c03 100644 --- a/src/apple_sensors.c +++ b/src/apple_sensors.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_MACH_MACH_TYPES_H #include diff --git a/src/aquaero.c b/src/aquaero.c index 937742b0..61dd76db 100644 --- a/src/aquaero.c +++ b/src/aquaero.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -81,8 +81,8 @@ static void aquaero_submit_array(const char *type, if (value_array[i] == AQ5_FLOAT_UNDEF) continue; - snprintf(type_instance, sizeof(type_instance), "%s%d", type_instance_prefix, - i + 1); + ssnprintf(type_instance, sizeof(type_instance), "%s%d", + type_instance_prefix, i + 1); aquaero_submit(type, type_instance, value_array[i]); } } @@ -130,7 +130,7 @@ static int aquaero_read(void) { (aq_data.fan_vrm_temp[i] != AQ5_FLOAT_UNDEF)) continue; - snprintf(type_instance, sizeof(type_instance), "fan%d", i + 1); + ssnprintf(type_instance, sizeof(type_instance), "fan%d", i + 1); aquaero_submit("fanspeed", type_instance, aq_data.fan_rpm[i]); aquaero_submit("percent", type_instance, aq_data.fan_duty[i]); @@ -139,7 +139,7 @@ static int aquaero_read(void) { /* Report the voltage reglator module (VRM) temperature with a * different type instance. */ - snprintf(type_instance, sizeof(type_instance), "fan%d-vrm", i + 1); + ssnprintf(type_instance, sizeof(type_instance), "fan%d-vrm", i + 1); aquaero_submit("temperature", type_instance, aq_data.fan_vrm_temp[i]); } diff --git a/src/ascent.c b/src/ascent.c index 22358658..6240e319 100644 --- a/src/ascent.c +++ b/src/ascent.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -499,8 +499,8 @@ static int ascent_init(void) /* {{{ */ static char credentials[1024]; int status; - status = snprintf(credentials, sizeof(credentials), "%s:%s", user, - (pass == NULL) ? "" : pass); + status = ssnprintf(credentials, sizeof(credentials), "%s:%s", user, + (pass == NULL) ? "" : pass); if ((status < 0) || ((size_t)status >= sizeof(credentials))) { ERROR("ascent plugin: ascent_init: Returning an error because the " "credentials have been truncated."); diff --git a/src/barometer.c b/src/barometer.c index a54d998a..fd733b49 100644 --- a/src/barometer.c +++ b/src/barometer.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include @@ -252,7 +252,7 @@ static averaging_t temperature_averaging; * @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", @@ -1309,8 +1309,8 @@ static int collectd_barometer_config(const char *key, const char *value) { } else if (strcasecmp(key, "Normalization") == 0) { int normalize_tmp = atoi(value); if (normalize_tmp < 0 || normalize_tmp > 2) { - WARNING("barometer: collectd_barometer_config: invalid normalization: %d", - normalize_tmp); + ERROR("barometer: collectd_barometer_config: invalid normalization: %d", + normalize_tmp); return 1; } config_normalize = normalize_tmp; diff --git a/src/battery.c b/src/battery.c index a74e7b64..20ec2612 100644 --- a/src/battery.c +++ b/src/battery.c @@ -25,8 +25,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_MACH_MACH_TYPES_H #include @@ -336,7 +336,7 @@ static int battery_read(void) /* {{{ */ return 0; } /* }}} int battery_read */ -/* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */ + /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */ #elif KERNEL_LINUX /* Reads a file which contains only a number (and optionally a trailing diff --git a/src/battery_statefs.c b/src/battery_statefs.c index 149512b9..d2c1ed9d 100644 --- a/src/battery_statefs.c +++ b/src/battery_statefs.c @@ -44,9 +44,9 @@ SOFTWARE. **/ -#include "common.h" -#include "plugin.h" #include "collectd.h" +#include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/bind.c b/src/bind.c index fe3480d0..a246f1aa 100644 --- a/src/bind.c +++ b/src/bind.c @@ -43,8 +43,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -1538,8 +1538,9 @@ static int bind_init(void) /* {{{ */ curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L); #ifdef HAVE_CURLOPT_TIMEOUT_MS curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, - (timeout >= 0) ? (long)timeout : (long)CDTIME_T_TO_MS( - plugin_get_interval())); + (timeout >= 0) + ? (long)timeout + : (long)CDTIME_T_TO_MS(plugin_get_interval())); #endif return 0; diff --git a/src/ceph.c b/src/ceph.c index c7fc576a..8048f5dd 100644 --- a/src/ceph.c +++ b/src/ceph.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -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; } @@ -990,7 +990,7 @@ static int cconn_connect(struct cconn *io) { return err; } address.sun_family = AF_UNIX; - snprintf(address.sun_path, sizeof(address.sun_path), "%s", io->d->asok_path); + ssnprintf(address.sun_path, sizeof(address.sun_path), "%s", io->d->asok_path); RETRY_ON_EINTR(err, connect(fd, (struct sockaddr *)&address, sizeof(struct sockaddr_un))); if (err < 0) { @@ -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; } @@ -1153,8 +1153,8 @@ static ssize_t cconn_handle_event(struct cconn *io) { return -EDOM; case CSTATE_WRITE_REQUEST: { char cmd[32]; - snprintf(cmd, sizeof(cmd), "%s%d%s", "{ \"prefix\": \"", io->request_type, - "\" }\n"); + ssnprintf(cmd, sizeof(cmd), "%s%d%s", "{ \"prefix\": \"", io->request_type, + "\" }\n"); size_t cmd_len = strlen(cmd); RETRY_ON_EINTR( ret, write(io->asok, ((char *)&cmd) + io->amt, cmd_len - io->amt)); diff --git a/src/cgroups.c b/src/cgroups.c index 7f24d12e..89252398 100644 --- a/src/cgroups.c +++ b/src/cgroups.c @@ -23,10 +23,10 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" -#include "utils_mount.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" +#include "utils/mount/mount.h" static char const *config_keys[] = {"CGroup", "IgnoreSelected"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); @@ -112,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) diff --git a/src/check_uptime.c b/src/check_uptime.c new file mode 100644 index 00000000..33363b54 --- /dev/null +++ b/src/check_uptime.c @@ -0,0 +1,273 @@ +/** + * collectd - src/check_uptime.c + * Copyright (C) 2007-2019 Florian Forster + * Copyright (C) 2019 Pavel V. Rochnyack + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Florian octo Forster + * Pavel Rochnyak + **/ + +#include "collectd.h" +#include "plugin.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" +#include "utils_cache.h" + +/* Types are registered only in `config` phase, so access is not protected by + * locks */ +c_avl_tree_t *types_tree = NULL; + +static int format_uptime(unsigned long uptime_sec, char *buf, size_t bufsize) { + + unsigned int uptime_days = uptime_sec / 24 / 3600; + uptime_sec -= uptime_days * 24 * 3600; + unsigned int uptime_hours = uptime_sec / 3600; + uptime_sec -= uptime_hours * 3600; + unsigned int uptime_mins = uptime_sec / 60; + uptime_sec -= uptime_mins * 60; + + int ret = 0; + if (uptime_days) { + ret += snprintf(buf + ret, bufsize - ret, " %u day(s)", uptime_days); + } + if (uptime_days || uptime_hours) { + ret += snprintf(buf + ret, bufsize - ret, " %u hour(s)", uptime_hours); + } + if (uptime_days || uptime_hours || uptime_mins) { + ret += snprintf(buf + ret, bufsize - ret, " %u min", uptime_mins); + } + ret += snprintf(buf + ret, bufsize - ret, " %lu sec.", uptime_sec); + return ret; +} + +static int cu_notify(enum cache_event_type_e event_type, const value_list_t *vl, + gauge_t old_uptime, gauge_t new_uptime) { + notification_t n; + NOTIFICATION_INIT_VL(&n, vl); + + int status; + char *buf = n.message; + size_t bufsize = sizeof(n.message); + + n.time = vl->time; + + const char *service = "Service"; + if (strcmp(vl->plugin, "uptime") == 0) + service = "Host"; + + switch (event_type) { + case CE_VALUE_NEW: + n.severity = NOTIF_OKAY; + status = snprintf(buf, bufsize, "%s is running.", service); + buf += status; + bufsize -= status; + break; + case CE_VALUE_UPDATE: + n.severity = NOTIF_WARNING; + status = snprintf(buf, bufsize, "%s just restarted.", service); + buf += status; + bufsize -= status; + break; + case CE_VALUE_EXPIRED: + n.severity = NOTIF_FAILURE; + status = snprintf(buf, bufsize, "%s is unreachable.", service); + buf += status; + bufsize -= status; + break; + } + + if (!isnan(old_uptime)) { + status = snprintf(buf, bufsize, " Uptime was:"); + buf += status; + bufsize -= status; + + status = format_uptime(old_uptime, buf, bufsize); + buf += status; + bufsize -= status; + + plugin_notification_meta_add_double(&n, "LastValue", old_uptime); + } + + if (!isnan(new_uptime)) { + status = snprintf(buf, bufsize, " Uptime now:"); + buf += status; + bufsize -= status; + + status = format_uptime(new_uptime, buf, bufsize); + buf += status; + bufsize -= status; + + plugin_notification_meta_add_double(&n, "CurrentValue", new_uptime); + } + + plugin_dispatch_notification(&n); + + plugin_notification_meta_free(n.meta); + return 0; +} + +static int cu_cache_event(cache_event_t *event, + __attribute__((unused)) user_data_t *ud) { + gauge_t values_history[2]; + + /* For CE_VALUE_EXPIRED */ + int ret; + value_t *values; + size_t values_num; + gauge_t old_uptime = NAN; + + switch (event->type) { + case CE_VALUE_NEW: + DEBUG("check_uptime: CE_VALUE_NEW, %s", event->value_list_name); + if (c_avl_get(types_tree, event->value_list->type, NULL) == 0) { + event->ret = 1; + assert(event->value_list->values_len > 0); + cu_notify(CE_VALUE_NEW, event->value_list, NAN /* old */, + event->value_list->values[0].gauge /* new */); + } + break; + case CE_VALUE_UPDATE: + DEBUG("check_uptime: CE_VALUE_UPDATE, %s", event->value_list_name); + if (uc_get_history_by_name(event->value_list_name, values_history, 2, 1)) { + ERROR("check_uptime plugin: Failed to get value history for %s.", + event->value_list_name); + } else { + if (!isnan(values_history[0]) && !isnan(values_history[1]) && + values_history[0] < values_history[1]) { + cu_notify(CE_VALUE_UPDATE, event->value_list, + values_history[1] /* old */, values_history[0] /* new */); + } + } + break; + case CE_VALUE_EXPIRED: + DEBUG("check_uptime: CE_VALUE_EXPIRED, %s", event->value_list_name); + ret = uc_get_value_by_name(event->value_list_name, &values, &values_num); + if (ret == 0) { + old_uptime = values[0].gauge; + sfree(values); + } + + cu_notify(CE_VALUE_EXPIRED, event->value_list, old_uptime, NAN /* new */); + break; + } + return 0; +} + +static int cu_config(oconfig_item_t *ci) { + if (types_tree == NULL) { + types_tree = c_avl_create((int (*)(const void *, const void *))strcmp); + if (types_tree == NULL) { + ERROR("check_uptime plugin: c_avl_create failed."); + return -1; + } + } + + for (int i = 0; i < ci->children_num; ++i) { + oconfig_item_t *child = ci->children + i; + if (strcasecmp("Type", child->key) == 0) { + if ((child->values_num != 1) || + (child->values[0].type != OCONFIG_TYPE_STRING)) { + WARNING("check_uptime plugin: The `Type' option needs exactly one " + "string argument."); + return -1; + } + char *type = child->values[0].value.string; + + if (c_avl_get(types_tree, type, NULL) == 0) { + ERROR("check_uptime plugin: Type `%s' already added.", type); + return -1; + } + + char *type_copy = strdup(type); + if (type_copy == NULL) { + ERROR("check_uptime plugin: strdup failed."); + return -1; + } + + int status = c_avl_insert(types_tree, type_copy, NULL); + if (status != 0) { + ERROR("check_uptime plugin: c_avl_insert failed."); + sfree(type_copy); + return -1; + } + } else + WARNING("check_uptime plugin: Ignore unknown config option `%s'.", + child->key); + } + + return 0; +} + +static int cu_init(void) { + if (types_tree == NULL) { + types_tree = c_avl_create((int (*)(const void *, const void *))strcmp); + if (types_tree == NULL) { + ERROR("check_uptime plugin: c_avl_create failed."); + return -1; + } + /* Default configuration */ + char *type = strdup("uptime"); + if (type == NULL) { + ERROR("check_uptime plugin: strdup failed."); + return -1; + } + int status = c_avl_insert(types_tree, type, NULL); + if (status != 0) { + ERROR("check_uptime plugin: c_avl_insert failed."); + sfree(type); + return -1; + } + } + + int ret = 0; + char *type; + void *val; + c_avl_iterator_t *iter = c_avl_get_iterator(types_tree); + while (c_avl_iterator_next(iter, (void *)&type, (void *)&val) == 0) { + data_set_t const *ds = plugin_get_ds(type); + if (ds == NULL) { + ERROR("check_uptime plugin: Failed to look up type \"%s\".", type); + ret = -1; + continue; + } + if (ds->ds_num != 1) { + ERROR("check_uptime plugin: The type \"%s\" has %" PRIsz " data sources. " + "Only types with a single GAUGE data source are supported.", + ds->type, ds->ds_num); + ret = -1; + continue; + } + if (ds->ds[0].type != DS_TYPE_GAUGE) { + ERROR("check_uptime plugin: The type \"%s\" has wrong data source type. " + "Only types with a single GAUGE data source are supported.", + ds->type); + ret = -1; + continue; + } + } + c_avl_iterator_destroy(iter); + + if (ret == 0) + plugin_register_cache_event("check_uptime", cu_cache_event, NULL); + + return ret; +} + +void module_register(void) { + plugin_register_complex_config("check_uptime", cu_config); + plugin_register_init("check_uptime", cu_init); +} diff --git a/src/chrony.c b/src/chrony.c index 913aab94..079a3357 100644 --- a/src/chrony.c +++ b/src/chrony.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" /* auxiliary functions */ -#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */ +#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */ +#include "utils/common/common.h" /* auxiliary functions */ #if HAVE_NETDB_H #include /* struct addrinfo */ @@ -111,7 +111,9 @@ typedef enum { #define ATTRIB_PACKED #endif -typedef struct ATTRIB_PACKED { int32_t value; } tFloat; +typedef struct ATTRIB_PACKED { + int32_t value; +} tFloat; typedef struct ATTRIB_PACKED { uint32_t tv_sec_high; @@ -148,7 +150,9 @@ typedef struct ATTRIB_PACKED { Amplification) */ } tChrony_Req_Tracking; -typedef struct ATTRIB_PACKED { uint32_t f_n_sources; } tChrony_Req_N_Sources; +typedef struct ATTRIB_PACKED { + uint32_t f_n_sources; +} tChrony_Req_N_Sources; typedef struct ATTRIB_PACKED { int32_t f_index; @@ -183,7 +187,9 @@ typedef struct ATTRIB_PACKED { } tChrony_Request; /* Chrony daemon response packets */ -typedef struct ATTRIB_PACKED { uint32_t f_n_sources; } tChrony_Resp_N_Sources; +typedef struct ATTRIB_PACKED { + uint32_t f_n_sources; +} tChrony_Resp_N_Sources; typedef struct ATTRIB_PACKED { union { diff --git a/src/collectd-lua.pod b/src/collectd-lua.pod index 7a256550..eccd71fb 100644 --- a/src/collectd-lua.pod +++ b/src/collectd-lua.pod @@ -72,8 +72,8 @@ These are used to collect the actual data. It is called once per interval (see the B configuration option of collectd). Usually it will call B to dispatch the values to collectd which will pass them on to all registered B. If this function -does not return 0 the plugin will be skipped for an increasing -amount of time until it returns normally again. +does not return 0, interval between its calls will grow until function returns +0 again. See the B configuration option of collectd. =item write functions @@ -90,12 +90,14 @@ The following functions are provided to Lua modules: =item register_read(callback) +Function to register read callbacks. The callback will be called without arguments. If this callback function does not return 0 the next call will be delayed by an increasing interval. -=item register_write +=item register_write(callback) +Function to register write callbacks. The callback function will be called with one argument passed, which will be a table of values. If this callback function does not return 0 next call will be delayed by @@ -136,8 +138,8 @@ A very simple write function might look like: To register those functions with collectd: - collectd.register_read(read) - collectd.register_write(write) + collectd.register_read(read) -- pass function as variable + collectd.register_write("write") -- pass by global-scope function name =back @@ -150,12 +152,12 @@ L, =head1 AUTHOR The C has been written by -Julien Ammous Ej.ammousatEgmail.comE, +Julien Ammous Ej.ammousEatEgmail.comE, Florian Forster EoctoEatEcollectd.orgE and -Ruben Kerkhof ErubenatErubenkerkhof.com and +Ruben Kerkhof ErubenEatErubenkerkhof.comE. This manpage has been written by Ruben Kerkhof -ErubenatErubenkerkhof.com. +ErubenEatErubenkerkhof.comE. It is based on the L manual page by Florian Forster EoctoEatEcollectd.orgE and Sebastian Harl EshEatEtokkee.orgE. diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c index 54be36aa..06298494 100644 --- a/src/collectd-nagios.c +++ b/src/collectd-nagios.c @@ -116,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; @@ -130,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); @@ -527,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) { @@ -652,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-snmp.pod b/src/collectd-snmp.pod index 9d508d17..493f5ec2 100644 --- a/src/collectd-snmp.pod +++ b/src/collectd-snmp.pod @@ -76,9 +76,11 @@ internal format and dispatches them. Depending on the write plugins you have loaded they may be written to disk or submitted to another instance or whatever you configured. -Because querying a host via SNMP may produce a timeout multiple threads are -used to query hosts in parallel. Depending on the number of hosts between one -and ten threads are used. +Because querying a host via SNMP may produce a timeout the "complex reads" +polling method is used. The ReadThreads parameter in the main configuration +influences the number of parallel polling jobs which can be undertaken. If +you expect timeouts or some polling to take a long time, you should increase +this parameter. Note that other plugins also use the same threads. =head1 CONFIGURATION @@ -338,7 +340,7 @@ Selects the authentication protocol for SNMPv3 security. =item B I -Sets the authentication passphrase for SNMPv3 security. +Sets the authentication passphrase for SNMPv3 security. =item B I|I @@ -346,7 +348,7 @@ Selects the privacy (encryption) protocol for SNMPv3 security. =item B I -Sets the privacy (encryption) passphrase for SNMPv3 security. +Sets the privacy (encryption) passphrase for SNMPv3 security. =item B I [I ...] diff --git a/src/collectd-tg.c b/src/collectd-tg.c index 2d83bbfe..92d1f012 100644 --- a/src/collectd-tg.c +++ b/src/collectd-tg.c @@ -43,7 +43,7 @@ #include #include -#include "utils_heap.h" +#include "utils/heap/heap.h" #include "collectd/client.h" #include "collectd/network.h" @@ -196,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.conf.in b/src/collectd.conf.in index 515820ad..f8c48080 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -102,6 +102,7 @@ #@BUILD_PLUGIN_CEPH_TRUE@LoadPlugin ceph #@BUILD_PLUGIN_CGROUPS_TRUE@LoadPlugin cgroups #@BUILD_PLUGIN_CHRONY_TRUE@LoadPlugin chrony +#@BUILD_PLUGIN_CHECK_UPTIME_TRUE@LoadPlugin check_uptime #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack #@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu @@ -180,6 +181,7 @@ #@BUILD_PLUGIN_POSTGRESQL_TRUE@LoadPlugin postgresql #@BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns #@BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes +#@BUILD_PLUGIN_PROCEVENT_TRUE@LoadPlugin procevent #@BUILD_PLUGIN_PROTOCOLS_TRUE@LoadPlugin protocols #@BUILD_PLUGIN_PYTHON_TRUE@LoadPlugin python #@BUILD_PLUGIN_REDIS_TRUE@LoadPlugin redis @@ -224,6 +226,7 @@ #@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_SYSLOG_TRUE@LoadPlugin write_syslog #@BUILD_PLUGIN_WRITE_TSDB_TRUE@LoadPlugin write_tsdb #@BUILD_PLUGIN_XENCPU_TRUE@LoadPlugin xencpu #@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms @@ -404,6 +407,7 @@ # # # URL "http://finance.google.com/finance?q=NYSE%3AAMD" +# AddressFamily "any" # User "foo" # Password "bar" # Digest false @@ -426,6 +430,7 @@ # # +# AddressFamily "any" # Instance "test_http_json" # # Type "gauge" @@ -462,6 +467,7 @@ # } ## See: http://wiki.apache.org/couchdb/Runtime_Statistics # +# AddressFamily "ipv4" # Instance "httpd" # # Type "http_requests" @@ -477,6 +483,7 @@ # ## Database status metrics: # +# AddressFamily "ipv6" # Instance "dbs" # # Type "gauge" @@ -492,6 +499,7 @@ # # +# AddressFamily "any" # Host "my_host" # #Plugin "stats" # Instance "some_instance" @@ -704,6 +712,7 @@ # # Cores "0-2" +# Processes "sshd" # # @@ -1264,6 +1273,12 @@ # # +# +# BufferLength 10 +# ProcessRegex "/^ovs.*$/" +# Process tuned +# + # # Value "/^Tcp:/" # IgnoreSelected false @@ -1672,16 +1687,20 @@ # 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" +# ExtraStats "cpu_util disk disk_err domain_state fs_info job_stats_background pcpu perf vcpu vcpupin disk_physical disk_allocation disk_capacity memory" # PersistentNotification false # @@ -1704,6 +1723,7 @@ # SeparateInstances false # PreserveSeparator false # DropDuplicateFields false +# ReverseHost false # # @@ -1812,6 +1832,18 @@ # Url "https://monitoring.googleapis.com/v3" # +# +# +# Host "localhost" +# Port "44514" +# Prefix "collectd" +# MessageFormat "human" +# HostTags "" +# StoreRates false +# AlwaysAppendDS false +# +# + # # # Host "localhost" diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 02580c8a..8dff63c1 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -1548,6 +1548,35 @@ at all, B cgroups are selected. =back +=head2 Plugin C + +The I designed to check and notify about host or service +status based on I metric. + +When new metric of I type appears in cache, OK notification is sent. +When new value for metric is less than previous value, WARNING notification is +sent about host/service restart. +When no new updates comes for metric and cache entry expires, then FAILURE +notification is sent about unreachable host or service. + +By default (when no explicit configuration), plugin checks for I metric. + +B + + + Type "uptime" + Type "my_uptime_type" + + +=over 4 + +=item B I + +Metric type to check for status/values. The type should consist single GAUGE +data source. + +=back + =head2 Plugin C The C plugin collects ntp data from a B server, such as clock @@ -1656,15 +1685,24 @@ Defaults to B. =head2 Plugin C -This plugin doesn't have any options. It reads +This plugin is available on Linux and FreeBSD only. It doesn't have any +options. On Linux it reads F (for the first CPU 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. +On Linux, 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. + +On FreeBSD it does a sysctl dev.cpu.0.freq and submits this as instance 0. +At this time FreeBSD only has one frequency setting for all cores. +See the BUGS section in the FreeBSD man page for cpufreq(4) for more details. + +On FreeBSD the plugin checks the success of sysctl dev.cpu.0.freq and +unregisters the plugin when this fails. A message will be logged to indicate +this. =head2 Plugin C @@ -1795,6 +1833,7 @@ finance page and dispatch the value to collectd. Plugin "quotes" URL "http://finance.google.com/finance?q=NYSE%3AAMD" + AddressFamily "any" User "foo" Password "bar" Digest false @@ -1835,6 +1874,18 @@ Defaults to C. URL of the web site to retrieve. Since a regular expression will be used to extract information from this data, non-binary data is a big plus here ;) +=item B I + +IP version to resolve URL to. Useful in cases when hostname in URL resolves +to both IPv4 and IPv6 addresses, and you are interested in using one of them +specifically. +Use C to enforce IPv4, C to enforce IPv6, or C to keep the +default behavior of resolving addresses to all IP versions your system allows. +If C is compiled without IPv6 support, using C will result in +a warning and fallback to C. +If C cannot be parsed, a warning will be printed and the whole B +block will be ignored. + =item B I Username to use if authorization is required to read the page. @@ -1946,6 +1997,7 @@ C<_stats> runtime statistics module of I + AddressFamily "any" Instance "httpd" Type "http_requests" @@ -1990,6 +2042,18 @@ The following options are valid within B blocks: =over 4 +=item B I + +IP version to resolve URL to. Useful in cases when hostname in URL resolves +to both IPv4 and IPv6 addresses, and you are interested in using one of them +specifically. +Use C to enforce IPv4, C to enforce IPv6, or C to keep the +default behavior of resolving addresses to all IP versions your system allows. +If C is compiled without IPv6 support, using C will result in +a warning and fallback to C. +If C cannot be parsed, a warning will be printed and the whole B +block will be ignored. + =item B I Use I as the host name when submitting values. Defaults to the global @@ -2061,6 +2125,7 @@ The B uses B (L) and B + AddressFamily "any" Host "my_host" #Plugin "curl_xml" Instance "some_instance" @@ -2097,6 +2162,18 @@ Within the B block the following options are accepted: =over 4 +=item B I + +IP version to resolve URL to. Useful in cases when hostname in URL resolves +to both IPv4 and IPv6 addresses, and you are interested in using one of them +specifically. +Use C to enforce IPv4, C to enforce IPv6, or C to keep the +default behavior of resolving addresses to all IP versions your system allows. +If C is compiled without IPv6 support, using C will result in +a warning and fallback to C. +If C cannot be parsed, a warning will be printed and the whole B +block will be ignored. + =item B I Use I as the host name when submitting values. Defaults to the global @@ -3498,6 +3575,7 @@ B Cores "0-2" "3,4,6" "8-10,15" + Processes "sshd,qemu-system-x86" "bash" B @@ -3513,11 +3591,10 @@ recommended to set interval higher than 1 sec. =item B I -All events are reported on a per core basis. Monitoring of the events can be -configured for group of cores (aggregated statistics). This field defines groups -of cores on which to monitor supported events. The field is represented as list -of strings with core group values. Each string represents a list of cores in a -group. Allowed formats are: +Monitoring of the events can be configured for group of cores +(aggregated statistics). This field defines groups of cores on which to monitor +supported events. The field is represented as list of strings with core group +values. Each string represents a list of cores in a group. Allowed formats are: 0,1,2,3 0-10,20-18 1,3,5-8,10,0x10-12 @@ -3525,6 +3602,15 @@ group. Allowed formats are: If an empty string is provided as value for this field default cores configuration is applied - a separate group is created for each core. +=item B I + +Monitoring of the events can be configured for group of processes +(aggregated statistics). This field defines groups of processes on which to +monitor supported events. The field is represented as list of strings with +process names group values. Each string represents a list of processes in a +group. Allowed format is: + sshd,bash,qemu + =back B By default global interval is used to retrieve statistics on monitored @@ -4444,6 +4530,12 @@ For Modbus/RTU, specifies the path to the serial device being used. For Modbus/RTU, specifies the baud rate of the serial device. Note, connections currently support only 8/N/1. +=item B I + +For Modbus/RTU, specifies the type of the serial device. +RS232, RS422 and RS485 are supported. Defaults to RS232. +Available only on Linux systems with libmodbus>=2.9.4. + =item B I Sets the interval (in seconds) in which the values will be collected from this @@ -5333,8 +5425,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: @@ -6274,6 +6367,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: @@ -6307,6 +6401,13 @@ 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 @@ -7242,6 +7343,40 @@ reporting the corresponding processes only. Outside of B and B blocks these options set the default value for subsequent matches. +=head2 Plugin C + +The I plugin monitors when processes start (EXEC) and stop (EXIT). + +B + + + BufferLength 10 + Process "name" + ProcessRegex "regex" + + +B + +=over 4 + +=item B I + +Maximum number of process events that can be stored in plugin's ring buffer. +By default, this is set to 10. Once an event has been read, its location +becomes available for storing a new event. + +=item B I + +Enumerate a process name to monitor. All processes that match this exact +name will be monitored for EXECs and EXITs. + +=item B I + +Enumerate a process pattern to monitor. All processes that match this +regular expression will be monitored for EXECs and EXITs. + +=back + =head2 Plugin C Collects a lot of information about various network protocols, such as I, @@ -8452,26 +8587,26 @@ Sets how the values are cumulated. I is one of: =item B -Calculate the average. +Calculate the average of all values matched during the interval. =item B -Use the smallest number only. +Report the smallest value matched during the interval. =item B -Use the greatest number only. +Report the greatest value matched during the interval. =item B -Use the last number found. +Report the last value matched during the interval. =item B -Use the last number found. The number is not reset at the end of an interval. -It is continously reported until another number is matched. This is intended -for cases in which only state changes are reported, for example a thermometer -that only reports the temperature when it changes. +Report the last matching value. The metric is I reset to C at the end +of an interval. It is continuously reported until another value is matched. +This is intended for cases in which only state changes are reported, for +example a thermometer that only reports the temperature when it changes. =item B @@ -8502,6 +8637,9 @@ Increase the internal counter by one. These B are the only ones that do not use the matched subexpression, but simply count the number of matched lines. Thus, you may use a regular expression without submatch in this case. +B is reset to I after every read, unlike other B +metrics which are reset to C. + =item B Type to do calculations based on the distribution of values, primarily @@ -8575,8 +8713,12 @@ The B and B types interpret the submatch as a floating point number, using L. The B and B types interpret the submatch as an unsigned integer using L. The B types interpret the submatch as a signed integer using -L. B and B do not use the submatch at all -and it may be omitted in this case. +L. B, B and B do not use the +submatch at all and it may be omitted in this case. + +The B types, unless noted otherwise, are reset to C after being +reported. In other words, B reports the average of all values +matched since the last metric was reported (or C if there was no match). =item B I @@ -9300,13 +9442,51 @@ surrounded by I and collectd was compiled with support for regexps. The default is to collect statistics for all domains and all their devices. -Example: +B B and B options are related to +corresponding B<*Format> options. Specifically, B filtering depends +on B setting - if user wants to filter block devices by +'target' name then B option has to be set to 'target' and +B option must be set to a valid block device target +name("/:hdb/"). Mixing formats and filter values from different worlds (i.e., +using 'target' name as B value with B set to +'source') may lead to unexpected results (all devices filtered out or all +visible, depending on the value of B option). +Similarly, option B is related to B setting +(i.e., when user wants to use MAC address as a filter then B +has to be set to 'address' - using wrong type here may filter out all of the +interfaces). + +B + +Ignore all I devices on any domain, but other block devices (eg. I) +will be collected: BlockDevice "/:hdb/" IgnoreSelected "true" + BlockDeviceFormat "target" -Ignore all I devices on any domain, but other block devices (eg. I) -will be collected. +B + +Collect metrics only for block device on 'baremetal0' domain when its +'source' matches given path: + + BlockDevice "baremetal0:/var/lib/libvirt/images/baremetal0.qcow2" + BlockDeviceFormat source + +As you can see it is possible to filter devices/interfaces using +various formats - for block devices 'target' or 'source' name can be +used. Interfaces can be filtered using 'name', 'address' or 'number'. + +B + +Collect metrics only for domains 'baremetal0' and 'baremetal1' and +ignore any other domain: + + Domain "baremetal0" + Domain "baremetal1" + +It is possible to filter multiple block devices/domains/interfaces by +adding multiple filtering entries in separate lines. =item B B|B @@ -9337,6 +9517,11 @@ to C. Setting C will cause the I to be set to C. +B this option determines also what field will be used for +filtering over block devices (filter value in B +will be applied to target or source). More info about filtering +block devices can be found in the description of B. + =item B B|B The B controls whether the full path or the @@ -9351,7 +9536,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 @@ -9364,6 +9549,9 @@ 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. 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 between, thus I<"foo:1234-1234-1234-1234">). @@ -9372,7 +9560,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 @@ -9382,23 +9570,47 @@ 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. + +B this option determines also what field will be used for +filtering over interface device (filter value in B +will be applied to name, address or number). More info about filtering +interfaces can be found in the description of B. + +=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 @@ -9420,9 +9632,7 @@ I<0.9.5> or later. =item B: report disk errors if any occured. Requires libvirt API version I<0.9.10> or later. -=item B: report domain state and reason in human-readable format as -a notification. If libvirt API version I<0.9.2> or later is available, domain -reason will be included in notification. +=item B: report domain state and reason as 'domain_state' metric. =item B: report file system information as a notification. Requires libvirt API version I<1.2.11> or later. Can be collected only if I @@ -9437,6 +9647,9 @@ Requires libvirt API version I<1.2.9> or later. a domain. Only one type of job statistics can be collected at the same time. Requires libvirt API version I<1.2.9> or later. +=item B: report statistics about memory usage details, provided +by libvirt virDomainMemoryStats() function. + =item B: report the physical user/system cpu time consumed by the hypervisor, per-vm. Requires libvirt API version I<0.9.11> or later. @@ -9445,16 +9658,57 @@ metrics they must be enabled for domain and supported by the platform. Requires libvirt API version I<1.3.3> or later. B: I metrics can't be collected if I plugin is enabled. +=item B: report domain virtual CPUs utilisation. + =item B: report pinning of domain VCPUs to host physical CPUs. +=item B: report 'disk_physical' statistic for disk device. +B: This statistic is only reported for disk devices with 'source' +property available. + +=item B: report 'disk_allocation' statistic for disk device. +B: This statistic is only reported for disk devices with 'source' +property available. + +=item B: report 'disk_capacity' statistic for disk device. +B: This statistic is only reported for disk devices with 'source' +property available. + =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 @@ -9509,6 +9763,7 @@ Synopsis: LogSendErrors true Prefix "collectd" UseTags false + ReverseHost false @@ -9620,6 +9875,30 @@ are not used. Default value: B. +=item B B|B + +If set to B, the (dot separated) parts of the B field of the +I will be rewritten in reverse order. The rewrite happens I +special characters are replaced with the B. + +This option might be convenient if the metrics are presented with Graphite in a +DNS like tree structure (probably without replacing dots in hostnames). + +Example: + Hostname "node3.cluster1.example.com" + LoadPlugin "cpu" + LoadPlugin "write_graphite" + + + EscapeCharacter "." + ReverseHost true + + + + result on the wire: com.example.cluster1.node3.cpu-0.cpu-idle 99.900993 1543010932 + +Default value: B. + =back =head2 Plugin C @@ -10595,6 +10874,141 @@ C. =back +=head2 Plugin C + +The C plugin writes data in I format log messages. +It implements the basic syslog protocol, RFC 5424, extends it with +content-based filtering, rich filtering capabilities, +flexible configuration options and adds features such as using TCP for transport. +The plugin can connect to a I daemon, like syslog-ng and rsyslog, that will +ingest metrics, transform and ship them to the specified output. +The plugin uses I over the "line based" protocol with a default port 44514. +The data will be sent in blocks of at most 1428 bytes to minimize the number of +network packets. + +Synopsis: + + + ResolveInterval 60 + ResolveJitter 60 + + Host "syslog-1.my.domain" + Port "44514" + Prefix "collectd" + MessageFormat "human" + HostTags "" + + + +The configuration consists of one or more EBEIE +blocks and global directives. + +Global directives are: + +=over 4 + +=item B I + +=item B I + +When I connects to a syslog node, it will request the hostname from +DNS. This can become a problem if the syslog node is unavailable or badly +configured because collectd will request DNS in order to reconnect for every +metric, which can flood your DNS. So you can cache the last value for +I seconds. +Defaults to the I of the I, e.g. 10Eseconds. + +You can also define a jitter, a random interval to wait in addition to +I. This prevents all your collectd servers to resolve the +hostname at the same time when the connection fails. +Defaults to the I of the I, e.g. 10Eseconds. + +B If the DNS resolution has already been successful when the socket +closes, the plugin will try to reconnect immediately with the cached +information. DNS is queried only when the socket is closed for a longer than +I + I seconds. + +=back + +Inside the B blocks, the following options are recognized: + +=over 4 + +=item B I
+ +Hostname or address to connect to. Defaults to C. + +=item B I + +Service name or port number to connect to. Defaults to C<44514>. + + +=item B I + +When set, I is added to the end of the metric. +It is intended to be used for adding additional metadata to tag the metric with. +Dots and whitespace are I escaped in this string. + +Examples: + +When MessageFormat is set to "human". + + ["prefix1" "example1"="example1_v"]["prefix2" "example2"="example2_v"]" + +When MessageFormat is set to "JSON", text should be in JSON format. +Escaping the quotation marks is required. + + HostTags "\"prefix1\": {\"example1\":\"example1_v\",\"example2\":\"example2_v\"}" + +=item B I + +I selects the format in which messages are sent to the +syslog deamon, human or JSON. Defaults to human. + +Syslog message format: + +VERSION ISOTIMESTAMP HOSTNAME APPLICATION PID MESSAGEID STRUCTURED-DATA MSG + +The difference between the message formats are in the STRUCTURED-DATA and MSG parts. + +Human format: + + <166>1 ISOTIMESTAMP HOSTNAME collectd PID MESSAGEID + ["collectd" "value": "v1" "plugin"="plugin_v" "plugin_instance"="plugin_instance_v" + "type_instance"="type_instance_v" "type"="type_v" "ds_name"="ds_name_v" "interval"="interval_v" ] + "host_tag_example"="host_tag_example_v" plugin_v.type_v.ds_name_v="v1" + +JSON format: + + <166>1 ISOTIMESTAMP HOSTNAME collectd PID MESSAGEID STRUCTURED-DATA + { + "collectd": { + "time": time_as_epoch, "interval": interval_v, "plugin": "plugin_v", + "plugin_instance": "plugin_instance_v", "type":"type_v", + "type_instance": "type_instance_v", "plugin_v": {"type_v": v1} + } , "host":"host_v", "host_tag_example": "host_tag_example_v" + } + +=item B B|B + +If set to B, convert counter values to rates. If set to B +(the default) counter values are stored as is, as an increasing +integer number. + +=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 I + +When set, I is added to all metrics names as a prefix. It is intended in +case you want to be able to define the source of the specific metric. Dots and +whitespace are I escaped in this string. + +=back + =head2 Plugin C This plugin collects metrics of hardware CPU load for machine running Xen @@ -11583,7 +11997,7 @@ be an FQDN. =head1 IGNORELISTS B are a generic framework to either ignore some metrics or report -specific metircs only. Plugins usually provide one or more options to specify +specific metrics only. Plugins usually provide one or more options to specify the items (mounts points, devices, ...) and the boolean option C. diff --git a/src/collectdctl.c b/src/collectdctl.c index 54c8081e..df83b50b 100644 --- a/src/collectdctl.c +++ b/src/collectdctl.c @@ -25,6 +25,7 @@ #include "config.h" #endif +#include #include #include #include @@ -80,6 +81,22 @@ extern char *optarg; extern int optind; +/* ssnprintf returns zero on success, one if truncation occurred + and a negative integer onerror. */ +static int _ssnprintf(char *str, size_t sz, const char *format, ...) { + va_list ap; + va_start(ap, format); + + int ret = vsnprintf(str, sz, format, ap); + + va_end(ap); + + if (ret < 0) { + return ret; + } + return (size_t)ret >= sz; +} /* int _ssnprintf */ + __attribute__((noreturn)) static void exit_usage(const char *name, int status) { fprintf( (status == 0) ? stdout : stderr, @@ -166,7 +183,7 @@ static int parse_identifier(lcc_connection_t *c, const char *value, } hostname[sizeof(hostname) - 1] = '\0'; - snprintf(ident_str, sizeof(ident_str), "%s/%s", hostname, value); + _ssnprintf(ident_str, sizeof(ident_str), "%s/%s", hostname, value); ident_str[sizeof(ident_str) - 1] = '\0'; } else { strncpy(ident_str, value, sizeof(ident_str)); @@ -276,8 +293,9 @@ static int flush(lcc_connection_t *c, int argc, char **argv) { value); BAIL_OUT(-1); } else if ((endptr != NULL) && (*endptr != '\0')) { - fprintf(stderr, "WARNING: Ignoring trailing garbage after timeout: " - "%s.\n", + fprintf(stderr, + "WARNING: Ignoring trailing garbage after timeout: " + "%s.\n", endptr); } } else if (strcasecmp(key, "plugin") == 0) { @@ -324,8 +342,9 @@ static int flush(lcc_connection_t *c, int argc, char **argv) { char id[1024]; lcc_identifier_to_string(c, id, sizeof(id), identifiers + j); - fprintf(stderr, "ERROR: Failed to flush plugin `%s', " - "identifier `%s': %s.\n", + fprintf(stderr, + "ERROR: Failed to flush plugin `%s', " + "identifier `%s': %s.\n", (plugins[i] == NULL) ? "(all)" : plugins[i], id, lcc_strerror(c)); } @@ -369,8 +388,9 @@ static int listval(lcc_connection_t *c, int argc, char **argv) { status = lcc_identifier_to_string(c, id, sizeof(id), ret_ident + i); if (status != 0) { - fprintf(stderr, "ERROR: listval: Failed to convert returned " - "identifier to a string: %s\n", + fprintf(stderr, + "ERROR: listval: Failed to convert returned " + "identifier to a string: %s\n", lcc_strerror(c)); continue; } @@ -428,8 +448,9 @@ static int putval(lcc_connection_t *c, int argc, char **argv) { value); return -1; } else if ((endptr != NULL) && (*endptr != '\0')) { - fprintf(stderr, "WARNING: Ignoring trailing garbage after " - "interval: %s.\n", + fprintf(stderr, + "WARNING: Ignoring trailing garbage after " + "interval: %s.\n", endptr); } } else { @@ -543,7 +564,7 @@ int main(int argc, char **argv) { switch (opt) { case 's': - snprintf(address, sizeof(address), "unix:%s", optarg); + _ssnprintf(address, sizeof(address), "unix:%s", optarg); address[sizeof(address) - 1] = '\0'; break; case 'h': diff --git a/src/collectdmon.c b/src/collectdmon.c index 0e2b0214..6affd3c7 100644 --- a/src/collectdmon.c +++ b/src/collectdmon.c @@ -255,8 +255,9 @@ static void check_respawn(void) { if (counter >= 10) { unsigned int time_left = 300; - syslog(LOG_ERR, "Error: collectd is respawning too fast - " - "disabled for %i seconds", + syslog(LOG_ERR, + "Error: collectd is respawning too fast - " + "disabled for %i seconds", time_left); while (((time_left = sleep(time_left)) > 0) && loop == 0) @@ -323,7 +324,8 @@ int main(int argc, char **argv) { } struct sigaction sa = { - .sa_handler = sig_int_term_handler, .sa_flags = 0, + .sa_handler = sig_int_term_handler, + .sa_flags = 0, }; sigemptyset(&sa.sa_mask); diff --git a/src/conntrack.c b/src/conntrack.c index 29c7003e..7b61eef0 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !KERNEL_LINUX #error "No applicable input method." diff --git a/src/contextswitch.c b/src/contextswitch.c index 35ac5a39..cf3d3dae 100644 --- a/src/contextswitch.c +++ b/src/contextswitch.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #ifdef HAVE_SYS_SYSCTL_H #include @@ -73,7 +73,7 @@ static int cs_read(void) { } cs_submit(value); -/* #endif HAVE_SYSCTLBYNAME */ + /* #endif HAVE_SYSCTLBYNAME */ #elif KERNEL_LINUX FILE *fh; @@ -116,7 +116,7 @@ static int cs_read(void) { if (status == -2) ERROR("contextswitch plugin: Unable to find context switch value."); -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_PERFSTAT int status = 0; diff --git a/src/cpu.c b/src/cpu.c index 2a697129..09d60fe4 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -29,8 +29,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #ifdef HAVE_MACH_KERN_RETURN_H #include @@ -251,7 +251,7 @@ static int init(void) { INFO("cpu plugin: Found %i processor%s.", (int)cpu_list_len, cpu_list_len == 1 ? "" : "s"); -/* #endif PROCESSOR_CPU_LOAD_INFO */ + /* #endif PROCESSOR_CPU_LOAD_INFO */ #elif defined(HAVE_LIBKSTAT) kstat_t *ksp_chain; @@ -267,7 +267,7 @@ static int init(void) { ksp_chain = ksp_chain->ks_next) if (strncmp(ksp_chain->ks_module, "cpu_stat", 8) == 0) ksp[numcpu++] = ksp_chain; -/* #endif HAVE_LIBKSTAT */ + /* #endif HAVE_LIBKSTAT */ #elif CAN_USE_SYSCTL size_t numcpu_size; @@ -282,7 +282,7 @@ static int init(void) { WARNING("cpu plugin: sysctl: %s", STRERRNO); return -1; } -/* #endif CAN_USE_SYSCTL */ + /* #endif CAN_USE_SYSCTL */ #elif defined(HAVE_SYSCTLBYNAME) size_t numcpu_size; @@ -307,7 +307,7 @@ static int init(void) { "%i)", numcpu); #endif -/* #endif HAVE_SYSCTLBYNAME */ + /* #endif HAVE_SYSCTLBYNAME */ #elif defined(HAVE_LIBSTATGRAB) /* nothing to initialize */ @@ -630,7 +630,7 @@ static int cpu_read(void) { cpu_stage(cpu, COLLECTD_CPU_STATE_IDLE, (derive_t)cpu_info.cpu_ticks[CPU_STATE_IDLE], now); } -/* }}} #endif PROCESSOR_CPU_LOAD_INFO */ + /* }}} #endif PROCESSOR_CPU_LOAD_INFO */ #elif defined(KERNEL_LINUX) /* {{{ */ int cpu; @@ -708,7 +708,7 @@ static int cpu_read(void) { cpu_stage(cpu, COLLECTD_CPU_STATE_NICE, (derive_t)nice_value, now); } fclose(fh); -/* }}} #endif defined(KERNEL_LINUX) */ + /* }}} #endif defined(KERNEL_LINUX) */ #elif defined(HAVE_LIBKSTAT) /* {{{ */ static cpu_stat_t cs; @@ -729,7 +729,7 @@ static int cpu_read(void) { cpu_stage(ksp[cpu]->ks_instance, COLLECTD_CPU_STATE_WAIT, (derive_t)cs.cpu_sysinfo.cpu[CPU_WAIT], now); } -/* }}} #endif defined(HAVE_LIBKSTAT) */ + /* }}} #endif defined(HAVE_LIBKSTAT) */ #elif CAN_USE_SYSCTL /* {{{ */ uint64_t cpuinfo[numcpu][CPUSTATES]; @@ -786,10 +786,10 @@ static int cpu_read(void) { cpu_stage(i, COLLECTD_CPU_STATE_INTERRUPT, (derive_t)cpuinfo[i][CP_INTR], now); } -/* }}} #endif CAN_USE_SYSCTL */ + /* }}} #endif CAN_USE_SYSCTL */ #elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES) /* {{{ \ - */ + */ long cpuinfo[maxcpu][CPUSTATES]; size_t cpuinfo_size; @@ -809,7 +809,7 @@ static int cpu_read(void) { cpu_stage(i, COLLECTD_CPU_STATE_INTERRUPT, (derive_t)cpuinfo[i][CP_INTR], now); } -/* }}} #endif HAVE_SYSCTL_KERN_CP_TIMES */ + /* }}} #endif HAVE_SYSCTL_KERN_CP_TIMES */ #elif defined(HAVE_SYSCTLBYNAME) /* {{{ */ long cpuinfo[CPUSTATES]; @@ -827,7 +827,7 @@ static int cpu_read(void) { cpu_stage(0, COLLECTD_CPU_STATE_SYSTEM, (derive_t)cpuinfo[CP_SYS], now); cpu_stage(0, COLLECTD_CPU_STATE_IDLE, (derive_t)cpuinfo[CP_IDLE], now); cpu_stage(0, COLLECTD_CPU_STATE_INTERRUPT, (derive_t)cpuinfo[CP_INTR], now); -/* }}} #endif HAVE_SYSCTLBYNAME */ + /* }}} #endif HAVE_SYSCTLBYNAME */ #elif defined(HAVE_LIBSTATGRAB) /* {{{ */ sg_cpu_stats *cs; @@ -844,7 +844,7 @@ static int cpu_read(void) { cpu_state(0, COLLECTD_CPU_STATE_SYSTEM, (derive_t)cs->kernel); cpu_state(0, COLLECTD_CPU_STATE_USER, (derive_t)cs->user); cpu_state(0, COLLECTD_CPU_STATE_WAIT, (derive_t)cs->iowait); -/* }}} #endif HAVE_LIBSTATGRAB */ + /* }}} #endif HAVE_LIBSTATGRAB */ #elif defined(HAVE_PERFSTAT) /* {{{ */ perfstat_id_t id; diff --git a/src/cpufreq.c b/src/cpufreq.c index a81cf462..f95b2828 100644 --- a/src/cpufreq.c +++ b/src/cpufreq.c @@ -22,9 +22,15 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" +#if KERNEL_FREEBSD +#include +#include +#endif + +#if KERNEL_LINUX #define MAX_AVAIL_FREQS 20 static int num_cpu; @@ -37,7 +43,7 @@ struct cpu_data_t { static bool report_p_stats = false; static void cpufreq_stats_init(void) { - cpu_data = calloc(num_cpu, sizeof(struct cpu_data_t)); + cpu_data = calloc(num_cpu, sizeof(*cpu_data)); if (cpu_data == NULL) return; @@ -71,8 +77,10 @@ static void cpufreq_stats_init(void) { } return; } +#endif /* KERNEL_LINUX */ static int cpufreq_init(void) { +#if KERNEL_LINUX char filename[PATH_MAX]; num_cpu = 0; @@ -96,6 +104,16 @@ static int cpufreq_init(void) { if (num_cpu == 0) plugin_unregister_read("cpufreq"); +#elif KERNEL_FREEBSD + char mib[] = "dev.cpu.0.freq"; + int cpufreq; + size_t cf_len = sizeof(cpufreq); + + if (sysctlbyname(mib, &cpufreq, &cf_len, NULL, 0) != 0) { + WARNING("cpufreq plugin: sysctl \"%s\" failed.", mib); + plugin_unregister_read("cpufreq"); + } +#endif return 0; } /* int cpufreq_init */ @@ -116,6 +134,7 @@ static void cpufreq_submit(int cpu_num, const char *type, plugin_dispatch_values(&vl); } +#if KERNEL_LINUX static void cpufreq_read_stats(int cpu) { char filename[PATH_MAX]; /* Read total transitions for cpu frequency */ @@ -184,8 +203,10 @@ static void cpufreq_read_stats(int cpu) { } fclose(fh); } +#endif /* KERNEL_LINUX */ static int cpufreq_read(void) { +#if KERNEL_LINUX for (int cpu = 0; cpu < num_cpu; cpu++) { char filename[PATH_MAX]; /* Read cpu frequency */ @@ -206,6 +227,23 @@ static int cpufreq_read(void) { if (report_p_stats) cpufreq_read_stats(cpu); } +#elif KERNEL_FREEBSD + /* FreeBSD currently only has 1 freq setting. See BUGS in cpufreq(4) */ + char mib[] = "dev.cpu.0.freq"; + int cpufreq; + size_t cf_len = sizeof(cpufreq); + + if (sysctlbyname(mib, &cpufreq, &cf_len, NULL, 0) != 0) { + WARNING("cpufreq plugin: sysctl \"%s\" failed.", mib); + return 0; + } + + value_t v; + /* convert Mhz to Hz */ + v.gauge = cpufreq * 1000000.0; + + cpufreq_submit(0, "cpufreq", NULL, &v); +#endif return 0; } /* int cpufreq_read */ diff --git a/src/cpusleep.c b/src/cpusleep.c index aa14cc12..ee26b0b3 100644 --- a/src/cpusleep.c +++ b/src/cpusleep.c @@ -28,13 +28,13 @@ * CPU sleep is reported in milliseconds of sleep per second of wall * time. For that, the time difference between BOOT and MONOTONIC clocks * is reported using derive type. -**/ + **/ #include "collectd.h" -#include -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" +#include static void cpusleep_submit(derive_t cpu_sleep) { value_list_t vl = VALUE_LIST_INIT; diff --git a/src/cpython.h b/src/cpython.h index 38951c0f..11e64fa6 100644 --- a/src/cpython.h +++ b/src/cpython.h @@ -181,7 +181,7 @@ extern PyTypeObject ValuesType; typedef struct { PluginData data; - PyObject *meta; /* dict */ + PyObject *meta; /* dict */ int severity; char message[NOTIF_MAX_MSG_LEN]; } Notification; diff --git a/src/csv.c b/src/csv.c index 88726bba..953473fc 100644 --- a/src/csv.c +++ b/src/csv.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" /* diff --git a/src/curl.c b/src/curl.c index 4925ad09..7eb48058 100644 --- a/src/curl.c +++ b/src/curl.c @@ -23,10 +23,10 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_curl_stats.h" -#include "utils_match.h" +#include "utils/common/common.h" +#include "utils/curl_stats/curl_stats.h" +#include "utils/match/match.h" #include "utils_time.h" #include @@ -57,6 +57,7 @@ struct web_page_s /* {{{ */ char *instance; char *url; + int address_family; char *user; char *pass; char *credentials; @@ -345,6 +346,7 @@ static int cc_page_init_curl(web_page_t *wp) /* {{{ */ curl_easy_setopt(wp->curl, CURLOPT_ERRORBUFFER, wp->curl_errbuf); curl_easy_setopt(wp->curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(wp->curl, CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(wp->curl, CURLOPT_IPRESOLVE, wp->address_family); if (wp->user != NULL) { #ifdef HAVE_CURLOPT_USERNAME @@ -411,6 +413,7 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */ } page->plugin_name = NULL; page->url = NULL; + page->address_family = CURL_IPRESOLVE_WHATEVER; page->user = NULL; page->pass = NULL; page->digest = false; @@ -437,7 +440,34 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */ status = cf_util_get_string(child, &page->plugin_name); else if (strcasecmp("URL", child->key) == 0) status = cf_util_get_string(child, &page->url); - else if (strcasecmp("User", child->key) == 0) + else if (strcasecmp("AddressFamily", child->key) == 0) { + char *af = NULL; + status = cf_util_get_string(child, &af); + if (status != 0 || af == NULL) { + WARNING("curl plugin: Cannot parse value of `%s' " + "for instance `%s'.", + child->key, page->instance); + } else if (strcasecmp("any", af) == 0) { + page->address_family = CURL_IPRESOLVE_WHATEVER; + } else if (strcasecmp("ipv4", af) == 0) { + page->address_family = CURL_IPRESOLVE_V4; + } else if (strcasecmp("ipv6", af) == 0) { + /* If curl supports ipv6, use it. If not, log a warning and + * fall back to default - don't set status to non-zero. + */ + curl_version_info_data *curl_info = curl_version_info(CURLVERSION_NOW); + if (curl_info->features & CURL_VERSION_IPV6) + page->address_family = CURL_IPRESOLVE_V6; + else + WARNING("curl plugin: IPv6 not supported by this libCURL. " + "Using fallback `any'."); + } else { + WARNING("curl plugin: Unsupported value of `%s' " + "for instance `%s'.", + child->key, page->instance); + status = -1; + } + } else if (strcasecmp("User", child->key) == 0) status = cf_util_get_string(child, &page->user); else if (strcasecmp("Password", child->key) == 0) status = cf_util_get_string(child, &page->pass); @@ -511,7 +541,8 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */ plugin_register_complex_read(/* group = */ NULL, cb_name, cc_read_page, interval, &(user_data_t){ - .data = page, .free_func = cc_web_page_free, + .data = page, + .free_func = cc_web_page_free, }); sfree(cb_name); diff --git a/src/curl_json.c b/src/curl_json.c index dedfed05..edfaf00e 100644 --- a/src/curl_json.c +++ b/src/curl_json.c @@ -23,11 +23,11 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" +#include "utils/curl_stats/curl_stats.h" #include "utils_complain.h" -#include "utils_curl_stats.h" #include #include @@ -88,6 +88,7 @@ struct cj_s /* {{{ */ char *sock; char *url; + int address_family; char *user; char *pass; char *credentials; @@ -236,7 +237,7 @@ static int cj_cb_number(void *ctx, const char *number, yajl_len_t number_len) { /* Create a null-terminated version of the string. */ char buffer[number_len + 1]; memcpy(buffer, number, number_len); - buffer[sizeof(buffer) - 1] = 0; + buffer[sizeof(buffer) - 1] = '\0'; if (db->state[db->depth].entry == NULL || db->state[db->depth].entry->type != KEY) { @@ -272,7 +273,7 @@ static int cj_cb_map_key(void *ctx, unsigned char const *in_name, char name[in_name_len + 1]; memmove(name, in_name, in_name_len); - name[sizeof(name) - 1] = 0; + name[sizeof(name) - 1] = '\0'; if (cj_load_key(ctx, name) != 0) return CJ_CB_ABORT; @@ -582,6 +583,7 @@ static int cj_init_curl(cj_t *db) /* {{{ */ curl_easy_setopt(db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf); curl_easy_setopt(db->curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(db->curl, CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(db->curl, CURLOPT_IPRESOLVE, db->address_family); if (db->user != NULL) { #ifdef HAVE_CURLOPT_USERNAME @@ -649,6 +651,7 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */ } db->timeout = -1; + db->address_family = CURL_IPRESOLVE_WHATEVER; if (strcasecmp("URL", ci->key) == 0) status = cf_util_get_string(ci, &db->url); @@ -702,6 +705,31 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */ db->stats = curl_stats_from_config(child); if (db->stats == NULL) status = -1; + } else if (db->url && strcasecmp("AddressFamily", child->key) == 0) { + char *af = NULL; + status = cf_util_get_string(child, &af); + if (status != 0 || af == NULL) { + WARNING("curl_json plugin: Cannot parse value of `%s' for URL `%s'.", + child->key, db->url); + } else if (strcasecmp("any", af) == 0) { + db->address_family = CURL_IPRESOLVE_WHATEVER; + } else if (strcasecmp("ipv4", af) == 0) { + db->address_family = CURL_IPRESOLVE_V4; + } else if (strcasecmp("ipv6", af) == 0) { + /* If curl supports ipv6, use it. If not, log a warning and + * fall back to default - don't set status to non-zero. + */ + curl_version_info_data *curl_info = curl_version_info(CURLVERSION_NOW); + if (curl_info->features & CURL_VERSION_IPV6) + db->address_family = CURL_IPRESOLVE_V6; + else + WARNING("curl_json plugin: IPv6 not supported by this libCURL. " + "Using fallback `any'."); + } else { + WARNING("curl_json plugin: Unsupported value of `%s' for URL `%s'.", + child->key, db->url); + status = -1; + } } else { WARNING("curl_json plugin: Option `%s' not allowed here.", child->key); status = -1; @@ -735,7 +763,8 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */ plugin_register_complex_read(/* group = */ NULL, cb_name, cj_read, interval, &(user_data_t){ - .data = db, .free_func = cj_free, + .data = db, + .free_func = cj_free, }); sfree(cb_name); } else { diff --git a/src/curl_xml.c b/src/curl_xml.c index 0bed05a5..4524ac33 100644 --- a/src/curl_xml.c +++ b/src/curl_xml.c @@ -21,9 +21,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_curl_stats.h" +#include "utils/common/common.h" +#include "utils/curl_stats/curl_stats.h" #include "utils_llist.h" #include @@ -76,6 +76,7 @@ struct cx_s /* {{{ */ char *host; char *url; + int address_family; char *user; char *pass; char *credentials; @@ -696,8 +697,8 @@ static int cx_config_add_namespace(cx_t *db, /* {{{ */ return EINVAL; } - cx_namespace_t *ns = realloc( - db->namespaces, sizeof(*db->namespaces) * (db->namespaces_num + 1)); + cx_namespace_t *ns = realloc(db->namespaces, sizeof(*db->namespaces) * + (db->namespaces_num + 1)); if (ns == NULL) { ERROR("curl_xml plugin: realloc failed."); return ENOMEM; @@ -736,6 +737,7 @@ static int cx_init_curl(cx_t *db) /* {{{ */ curl_easy_setopt(db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf); curl_easy_setopt(db->curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(db->curl, CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(db->curl, CURLOPT_IPRESOLVE, db->address_family); if (db->user != NULL) { #ifdef HAVE_CURLOPT_USERNAME @@ -814,6 +816,7 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */ } db->timeout = -1; + db->address_family = CURL_IPRESOLVE_WHATEVER; int status = cf_util_get_string(ci, &db->url); if (status != 0) { @@ -863,6 +866,31 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */ db->stats = curl_stats_from_config(child); if (db->stats == NULL) status = -1; + } else if (strcasecmp("AddressFamily", child->key) == 0) { + char *af = NULL; + status = cf_util_get_string(child, &af); + if (status != 0 || af == NULL) { + WARNING("curl_xml plugin: Cannot parse value of `%s' for URL `%s'.", + child->key, db->url); + } else if (strcasecmp("any", af) == 0) { + db->address_family = CURL_IPRESOLVE_WHATEVER; + } else if (strcasecmp("ipv4", af) == 0) { + db->address_family = CURL_IPRESOLVE_V4; + } else if (strcasecmp("ipv6", af) == 0) { + /* If curl supports ipv6, use it. If not, log a warning and + * fall back to default - don't set status to non-zero. + */ + curl_version_info_data *curl_info = curl_version_info(CURLVERSION_NOW); + if (curl_info->features & CURL_VERSION_IPV6) + db->address_family = CURL_IPRESOLVE_V6; + else + WARNING("curl_xml plugin: IPv6 not supported by this libCURL. " + "Using fallback `any'."); + } else { + WARNING("curl_xml plugin: Unsupported value of `%s' for URL `%s'.", + child->key, db->url); + status = -1; + } } else { WARNING("curl_xml plugin: Option `%s' not allowed here.", child->key); status = -1; @@ -897,7 +925,8 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */ plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read, /* interval = */ interval, &(user_data_t){ - .data = db, .free_func = cx_free, + .data = db, + .free_func = cx_free, }); sfree(cb_name); return 0; diff --git a/src/daemon/cmd.c b/src/daemon/cmd.c index 7b779955..09aee6ab 100644 --- a/src/daemon/cmd.c +++ b/src/daemon/cmd.c @@ -24,7 +24,7 @@ #include "cmd.h" #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include static void *do_flush(void __attribute__((unused)) * arg) { @@ -182,7 +182,7 @@ int main(int argc, char **argv) { #ifdef KERNEL_LINUX && notify_upstart() == 0 && notify_systemd() == 0 #endif - ) { + ) { pid_t pid; if ((pid = fork()) == -1) { /* error */ diff --git a/src/daemon/collectd.c b/src/daemon/collectd.c index f1a49237..78d410ca 100644 --- a/src/daemon/collectd.c +++ b/src/daemon/collectd.c @@ -28,9 +28,9 @@ #include "cmd.h" #include "collectd.h" -#include "common.h" #include "configfile.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -307,11 +307,12 @@ static int do_shutdown(void) { static void read_cmdline(int argc, char **argv, struct cmdline_config *config) { /* read options */ while (1) { - int c = getopt(argc, argv, "BhtTC:" + int c = getopt(argc, argv, + "BhtTC:" #if COLLECT_DAEMON - "fP:" + "fP:" #endif - ); + ); if (c == -1) break; @@ -357,8 +358,7 @@ static int configure_collectd(struct cmdline_config *config) { * Also, this will automatically load modules. */ if (cf_read(config->configfile)) { - fprintf(stderr, "Error: Reading the config file failed!\n" - "Read the logs for details.\n"); + fprintf(stderr, "Error: Parsing the config file failed!\n"); return 1; } @@ -392,7 +392,9 @@ void stop_collectd(void) { loop++; } struct cmdline_config init_config(int argc, char **argv) { struct cmdline_config config = { - .daemonize = true, .create_basedir = true, .configfile = CONFIGFILE, + .daemonize = true, + .create_basedir = true, + .configfile = CONFIGFILE, }; read_cmdline(argc, argv, &config); diff --git a/src/daemon/common.c b/src/daemon/common.c deleted file mode 100644 index 99f48caf..00000000 --- a/src/daemon/common.c +++ /dev/null @@ -1,1586 +0,0 @@ -/** - * collectd - src/common.c - * Copyright (C) 2005-2014 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - * Niki W. Waibel - * Sebastian Harl - * Michał Mirosław -**/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" -#include "utils_cache.h" - -/* for getaddrinfo */ -#include -#include - -#include - -#if HAVE_NETINET_IN_H -#include -#endif - -#if HAVE_NETINET_TCP_H -#include -#endif - -/* for ntohl and htonl */ -#if HAVE_ARPA_INET_H -#include -#endif - -#if HAVE_CAPABILITY -#include -#endif - -#if HAVE_KSTAT_H -#include -#endif - -#ifdef HAVE_LIBKSTAT -extern kstat_ctl_t *kc; -#endif - -#if !defined(MSG_DONTWAIT) -#if defined(MSG_NONBLOCK) -/* AIX doesn't have MSG_DONTWAIT */ -#define MSG_DONTWAIT MSG_NONBLOCK -#else -/* Windows doesn't have MSG_DONTWAIT or MSG_NONBLOCK */ -#define MSG_DONTWAIT 0 -#endif /* defined(MSG_NONBLOCK) */ -#endif /* !defined(MSG_DONTWAIT) */ - -#if !HAVE_GETPWNAM_R && defined(HAVE_GETPWNAM) -static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER; -#endif - -#if !HAVE_STRERROR_R -static pthread_mutex_t strerror_r_lock = PTHREAD_MUTEX_INITIALIZER; -#endif - -char *sstrncpy(char *dest, const char *src, size_t n) { - strncpy(dest, src, n); - dest[n - 1] = '\0'; - - return dest; -} /* char *sstrncpy */ - -char *ssnprintf_alloc(char const *format, ...) /* {{{ */ -{ - char static_buffer[1024] = ""; - char *alloc_buffer; - size_t alloc_buffer_size; - int status; - va_list ap; - - /* Try printing into the static buffer. In many cases it will be - * sufficiently large and we can simply return a strdup() of this - * buffer. */ - va_start(ap, format); - status = vsnprintf(static_buffer, sizeof(static_buffer), format, ap); - va_end(ap); - if (status < 0) - return NULL; - - /* "status" does not include the null byte. */ - alloc_buffer_size = (size_t)(status + 1); - if (alloc_buffer_size <= sizeof(static_buffer)) - return strdup(static_buffer); - - /* Allocate a buffer large enough to hold the string. */ - alloc_buffer = calloc(1, alloc_buffer_size); - if (alloc_buffer == NULL) - return NULL; - - /* Print again into this new buffer. */ - va_start(ap, format); - status = vsnprintf(alloc_buffer, alloc_buffer_size, format, ap); - va_end(ap); - if (status < 0) { - sfree(alloc_buffer); - return NULL; - } - - return alloc_buffer; -} /* }}} char *ssnprintf_alloc */ - -char *sstrdup(const char *s) { - char *r; - size_t sz; - - if (s == NULL) - return NULL; - - /* Do not use `strdup' here, because it's not specified in POSIX. It's - * ``only'' an XSI extension. */ - sz = strlen(s) + 1; - r = malloc(sz); - if (r == NULL) { - ERROR("sstrdup: Out of memory."); - exit(3); - } - memcpy(r, s, sz); - - return r; -} /* char *sstrdup */ - -/* Even though Posix requires "strerror_r" to return an "int", - * some systems (e.g. the GNU libc) return a "char *" _and_ - * ignore the second argument ... -tokkee */ -char *sstrerror(int errnum, char *buf, size_t buflen) { - buf[0] = '\0'; - -#if !HAVE_STRERROR_R - { - char *temp; - - pthread_mutex_lock(&strerror_r_lock); - - temp = strerror(errnum); - sstrncpy(buf, temp, buflen); - - pthread_mutex_unlock(&strerror_r_lock); - } -/* #endif !HAVE_STRERROR_R */ - -#elif STRERROR_R_CHAR_P - { - char *temp; - temp = strerror_r(errnum, buf, buflen); - if (buf[0] == '\0') { - if ((temp != NULL) && (temp != buf) && (temp[0] != '\0')) - sstrncpy(buf, temp, buflen); - else - sstrncpy(buf, "strerror_r did not return " - "an error message", - buflen); - } - } -/* #endif STRERROR_R_CHAR_P */ - -#else - if (strerror_r(errnum, buf, buflen) != 0) { - snprintf(buf, buflen, "Error #%i; " - "Additionally, strerror_r failed.", - errnum); - } -#endif /* STRERROR_R_CHAR_P */ - - return buf; -} /* char *sstrerror */ - -void *smalloc(size_t size) { - void *r; - - if ((r = malloc(size)) == NULL) { - ERROR("Not enough memory."); - exit(3); - } - - return r; -} /* void *smalloc */ - -#if 0 -void sfree (void **ptr) -{ - if (ptr == NULL) - return; - - if (*ptr != NULL) - free (*ptr); - - *ptr = NULL; -} -#endif - -int sread(int fd, void *buf, size_t count) { - char *ptr; - size_t nleft; - ssize_t status; - - ptr = (char *)buf; - nleft = count; - - while (nleft > 0) { - status = read(fd, (void *)ptr, nleft); - - if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR))) - continue; - - if (status < 0) - return status; - - if (status == 0) { - DEBUG("Received EOF from fd %i. ", fd); - return -1; - } - - assert((0 > status) || (nleft >= (size_t)status)); - - nleft = nleft - ((size_t)status); - ptr = ptr + ((size_t)status); - } - - return 0; -} - -int swrite(int fd, const void *buf, size_t count) { - const char *ptr; - size_t nleft; - ssize_t status; - struct pollfd pfd; - - ptr = (const char *)buf; - nleft = count; - - if (fd < 0) { - errno = EINVAL; - return errno; - } - - /* checking for closed peer connection */ - pfd.fd = fd; - pfd.events = POLLIN | POLLHUP; - pfd.revents = 0; - if (poll(&pfd, 1, 0) > 0) { - char buffer[32]; - if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) { - /* if recv returns zero (even though poll() said there is data to be - * read), that means the connection has been closed */ - errno = ECONNRESET; - return -1; - } - } - - while (nleft > 0) { - status = write(fd, (const void *)ptr, nleft); - - if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR))) - continue; - - if (status < 0) - return errno ? errno : status; - - nleft = nleft - ((size_t)status); - ptr = ptr + ((size_t)status); - } - - return 0; -} - -int strsplit(char *string, char **fields, size_t size) { - size_t i; - char *ptr; - char *saveptr; - - i = 0; - ptr = string; - saveptr = NULL; - while ((fields[i] = strtok_r(ptr, " \t\r\n", &saveptr)) != NULL) { - ptr = NULL; - i++; - - if (i >= size) - break; - } - - return (int)i; -} - -int strjoin(char *buffer, size_t buffer_size, char **fields, size_t fields_num, - const char *sep) { - size_t avail = 0; - char *ptr = buffer; - size_t sep_len = 0; - - size_t buffer_req = 0; - - if (((fields_num != 0) && (fields == NULL)) || - ((buffer_size != 0) && (buffer == NULL))) - return -EINVAL; - - if (buffer != NULL) - buffer[0] = 0; - - if (buffer_size != 0) - avail = buffer_size - 1; - - if (sep != NULL) - sep_len = strlen(sep); - - for (size_t i = 0; i < fields_num; i++) { - size_t field_len = strlen(fields[i]); - - if (i != 0) - buffer_req += sep_len; - buffer_req += field_len; - - if ((i != 0) && (sep_len > 0)) { - if (sep_len >= avail) { - /* prevent subsequent iterations from writing to the - * buffer. */ - avail = 0; - continue; - } - - memcpy(ptr, sep, sep_len); - - ptr += sep_len; - avail -= sep_len; - } - - if (field_len > avail) - field_len = avail; - - memcpy(ptr, fields[i], field_len); - ptr += field_len; - - avail -= field_len; - if (ptr != NULL) - *ptr = 0; - } - - return (int)buffer_req; -} - -int escape_string(char *buffer, size_t buffer_size) { - char *temp; - size_t j; - - /* Check if we need to escape at all first */ - temp = strpbrk(buffer, " \t\"\\"); - if (temp == NULL) - return 0; - - if (buffer_size < 3) - return EINVAL; - - temp = calloc(1, buffer_size); - if (temp == NULL) - return ENOMEM; - - temp[0] = '"'; - j = 1; - - for (size_t i = 0; i < buffer_size; i++) { - if (buffer[i] == 0) { - break; - } else if ((buffer[i] == '"') || (buffer[i] == '\\')) { - if (j > (buffer_size - 4)) - break; - temp[j] = '\\'; - temp[j + 1] = buffer[i]; - j += 2; - } else { - if (j > (buffer_size - 3)) - break; - temp[j] = buffer[i]; - j++; - } - } - - assert((j + 1) < buffer_size); - temp[j] = '"'; - temp[j + 1] = 0; - - sstrncpy(buffer, temp, buffer_size); - sfree(temp); - return 0; -} /* int escape_string */ - -int strunescape(char *buf, size_t buf_len) { - for (size_t i = 0; (i < buf_len) && (buf[i] != '\0'); ++i) { - if (buf[i] != '\\') - continue; - - if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) { - P_ERROR("string unescape: backslash found at end of string."); - /* Ensure null-byte at the end of the buffer. */ - buf[i] = 0; - return -1; - } - - switch (buf[i + 1]) { - case 't': - buf[i] = '\t'; - break; - case 'n': - buf[i] = '\n'; - break; - case 'r': - buf[i] = '\r'; - break; - default: - buf[i] = buf[i + 1]; - break; - } - - /* Move everything after the position one position to the left. - * Add a null-byte as last character in the buffer. */ - memmove(buf + i + 1, buf + i + 2, buf_len - i - 2); - buf[buf_len - 1] = 0; - } - return 0; -} /* int strunescape */ - -size_t strstripnewline(char *buffer) { - size_t buffer_len = strlen(buffer); - - while (buffer_len > 0) { - if ((buffer[buffer_len - 1] != '\n') && (buffer[buffer_len - 1] != '\r')) - break; - buffer_len--; - buffer[buffer_len] = 0; - } - - return buffer_len; -} /* size_t strstripnewline */ - -int escape_slashes(char *buffer, size_t buffer_size) { - size_t buffer_len; - - buffer_len = strlen(buffer); - - if (buffer_len <= 1) { - if (strcmp("/", buffer) == 0) { - if (buffer_size < 5) - return -1; - sstrncpy(buffer, "root", buffer_size); - } - return 0; - } - - /* Move one to the left */ - if (buffer[0] == '/') { - memmove(buffer, buffer + 1, buffer_len); - buffer_len--; - } - - for (size_t i = 0; i < buffer_len; i++) { - if (buffer[i] == '/') - buffer[i] = '_'; - } - - return 0; -} /* int escape_slashes */ - -void replace_special(char *buffer, size_t buffer_size) { - for (size_t i = 0; i < buffer_size; i++) { - if (buffer[i] == 0) - return; - if ((!isalnum((int)buffer[i])) && (buffer[i] != '-')) - buffer[i] = '_'; - } -} /* void replace_special */ - -int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta) { - struct timeval *larger; - struct timeval *smaller; - - int status; - - NORMALIZE_TIMEVAL(tv0); - NORMALIZE_TIMEVAL(tv1); - - if ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec == tv1.tv_usec)) { - if (delta != NULL) { - delta->tv_sec = 0; - delta->tv_usec = 0; - } - return 0; - } - - if ((tv0.tv_sec < tv1.tv_sec) || - ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec < tv1.tv_usec))) { - larger = &tv1; - smaller = &tv0; - status = -1; - } else { - larger = &tv0; - smaller = &tv1; - status = 1; - } - - if (delta != NULL) { - delta->tv_sec = larger->tv_sec - smaller->tv_sec; - - if (smaller->tv_usec <= larger->tv_usec) - delta->tv_usec = larger->tv_usec - smaller->tv_usec; - else { - --delta->tv_sec; - delta->tv_usec = 1000000 + larger->tv_usec - smaller->tv_usec; - } - } - - assert((delta == NULL) || - ((0 <= delta->tv_usec) && (delta->tv_usec < 1000000))); - - return status; -} /* int timeval_cmp */ - -int check_create_dir(const char *file_orig) { - struct stat statbuf; - - char file_copy[PATH_MAX]; - char dir[PATH_MAX]; - char *fields[16]; - int fields_num; - char *ptr; - char *saveptr; - int last_is_file = 1; - int path_is_absolute = 0; - size_t len; - - /* - * Sanity checks first - */ - if (file_orig == NULL) - return -1; - - if ((len = strlen(file_orig)) < 1) - return -1; - else if (len >= sizeof(file_copy)) { - ERROR("check_create_dir: name (%s) is too long.", file_orig); - return -1; - } - - /* - * If `file_orig' ends in a slash the last component is a directory, - * otherwise it's a file. Act accordingly.. - */ - if (file_orig[len - 1] == '/') - last_is_file = 0; - if (file_orig[0] == '/') - path_is_absolute = 1; - - /* - * Create a copy for `strtok_r' to destroy - */ - sstrncpy(file_copy, file_orig, sizeof(file_copy)); - - /* - * Break into components. This will eat up several slashes in a row and - * remove leading and trailing slashes.. - */ - ptr = file_copy; - saveptr = NULL; - fields_num = 0; - while ((fields[fields_num] = strtok_r(ptr, "/", &saveptr)) != NULL) { - ptr = NULL; - fields_num++; - - if (fields_num >= 16) - break; - } - - /* - * For each component, do.. - */ - for (int i = 0; i < (fields_num - last_is_file); i++) { - /* - * Do not create directories that start with a dot. This - * prevents `../../' attacks and other likely malicious - * behavior. - */ - if (fields[i][0] == '.') { - P_ERROR("Cowardly refusing to create a directory that " - "begins with a `.' (dot): `%s'", - file_orig); - return -2; - } - - /* - * Join the components together again - */ - dir[0] = '/'; - if (strjoin(dir + path_is_absolute, - (size_t)(sizeof(dir) - path_is_absolute), fields, - (size_t)(i + 1), "/") < 0) { - P_ERROR("strjoin failed: `%s', component #%i", file_orig, i); - return -1; - } - - while (42) { - if ((stat(dir, &statbuf) == -1) && (lstat(dir, &statbuf) == -1)) { - if (errno == ENOENT) { - if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0) - break; - - /* this might happen, if a different thread created - * the directory in the meantime - * => call stat() again to check for S_ISDIR() */ - if (EEXIST == errno) - continue; - - P_ERROR("check_create_dir: mkdir (%s): %s", dir, STRERRNO); - return -1; - } else { - P_ERROR("check_create_dir: stat (%s): %s", dir, STRERRNO); - return -1; - } - } else if (!S_ISDIR(statbuf.st_mode)) { - P_ERROR("check_create_dir: `%s' exists but is not " - "a directory!", - dir); - return -1; - } - break; - } - } - - return 0; -} /* check_create_dir */ - -#ifdef HAVE_LIBKSTAT -int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name) { - char ident[128]; - - *ksp_ptr = NULL; - - if (kc == NULL) - return -1; - - snprintf(ident, sizeof(ident), "%s,%i,%s", module, instance, name); - - *ksp_ptr = kstat_lookup(kc, module, instance, name); - if (*ksp_ptr == NULL) { - P_ERROR("get_kstat: Cound not find kstat %s", ident); - return -1; - } - - if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) { - P_ERROR("get_kstat: kstat %s has wrong type", ident); - *ksp_ptr = NULL; - return -1; - } - -#ifdef assert - assert(*ksp_ptr != NULL); - assert((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED); -#endif - - if (kstat_read(kc, *ksp_ptr, NULL) == -1) { - P_ERROR("get_kstat: kstat %s could not be read", ident); - return -1; - } - - if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) { - P_ERROR("get_kstat: kstat %s has wrong type", ident); - return -1; - } - - return 0; -} - -long long get_kstat_value(kstat_t *ksp, char *name) { - kstat_named_t *kn; - long long retval = -1LL; - - if (ksp == NULL) { - P_ERROR("get_kstat_value (\"%s\"): ksp is NULL.", name); - return -1LL; - } else if (ksp->ks_type != KSTAT_TYPE_NAMED) { - P_ERROR("get_kstat_value (\"%s\"): ksp->ks_type (%#x) " - "is not KSTAT_TYPE_NAMED (%#x).", - name, (unsigned int)ksp->ks_type, (unsigned int)KSTAT_TYPE_NAMED); - return -1LL; - } - - if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, name)) == NULL) - return -1LL; - - if (kn->data_type == KSTAT_DATA_INT32) - retval = (long long)kn->value.i32; - else if (kn->data_type == KSTAT_DATA_UINT32) - retval = (long long)kn->value.ui32; - else if (kn->data_type == KSTAT_DATA_INT64) - retval = - (long long)kn->value.i64; /* According to ANSI C99 `long long' must hold - at least 64 bits */ - else if (kn->data_type == KSTAT_DATA_UINT64) - retval = (long long)kn->value.ui64; /* XXX: Might overflow! */ - else - P_WARNING("get_kstat_value: Not a numeric value: %s", name); - - return retval; -} -#endif /* HAVE_LIBKSTAT */ - -#ifndef HAVE_HTONLL -unsigned long long ntohll(unsigned long long n) { -#if BYTE_ORDER == BIG_ENDIAN - return n; -#else - return (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32); -#endif -} /* unsigned long long ntohll */ - -unsigned long long htonll(unsigned long long n) { -#if BYTE_ORDER == BIG_ENDIAN - return n; -#else - return (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32); -#endif -} /* unsigned long long htonll */ -#endif /* HAVE_HTONLL */ - -#if FP_LAYOUT_NEED_NOTHING -/* Well, we need nothing.. */ -/* #endif FP_LAYOUT_NEED_NOTHING */ - -#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP -#if FP_LAYOUT_NEED_ENDIANFLIP -#define FP_CONVERT(A) \ - ((((uint64_t)(A)&0xff00000000000000LL) >> 56) | \ - (((uint64_t)(A)&0x00ff000000000000LL) >> 40) | \ - (((uint64_t)(A)&0x0000ff0000000000LL) >> 24) | \ - (((uint64_t)(A)&0x000000ff00000000LL) >> 8) | \ - (((uint64_t)(A)&0x00000000ff000000LL) << 8) | \ - (((uint64_t)(A)&0x0000000000ff0000LL) << 24) | \ - (((uint64_t)(A)&0x000000000000ff00LL) << 40) | \ - (((uint64_t)(A)&0x00000000000000ffLL) << 56)) -#else -#define FP_CONVERT(A) \ - ((((uint64_t)(A)&0xffffffff00000000LL) >> 32) | \ - (((uint64_t)(A)&0x00000000ffffffffLL) << 32)) -#endif - -double ntohd(double d) { - union { - uint8_t byte[8]; - uint64_t integer; - double floating; - } ret; - - ret.floating = d; - - /* NAN in x86 byte order */ - if ((ret.byte[0] == 0x00) && (ret.byte[1] == 0x00) && (ret.byte[2] == 0x00) && - (ret.byte[3] == 0x00) && (ret.byte[4] == 0x00) && (ret.byte[5] == 0x00) && - (ret.byte[6] == 0xf8) && (ret.byte[7] == 0x7f)) { - return NAN; - } else { - uint64_t tmp; - - tmp = ret.integer; - ret.integer = FP_CONVERT(tmp); - return ret.floating; - } -} /* double ntohd */ - -double htond(double d) { - union { - uint8_t byte[8]; - uint64_t integer; - double floating; - } ret; - - if (isnan(d)) { - ret.byte[0] = ret.byte[1] = ret.byte[2] = ret.byte[3] = 0x00; - ret.byte[4] = ret.byte[5] = 0x00; - ret.byte[6] = 0xf8; - ret.byte[7] = 0x7f; - return ret.floating; - } else { - uint64_t tmp; - - ret.floating = d; - tmp = FP_CONVERT(ret.integer); - ret.integer = tmp; - return ret.floating; - } -} /* double htond */ -#endif /* FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP */ - -int format_name(char *ret, int ret_len, const char *hostname, - const char *plugin, const char *plugin_instance, - const char *type, const char *type_instance) { - char *buffer; - size_t buffer_size; - - buffer = ret; - buffer_size = (size_t)ret_len; - -#define APPEND(str) \ - do { \ - size_t l = strlen(str); \ - if (l >= buffer_size) \ - return ENOBUFS; \ - memcpy(buffer, (str), l); \ - buffer += l; \ - buffer_size -= l; \ - } while (0) - - assert(plugin != NULL); - assert(type != NULL); - - APPEND(hostname); - APPEND("/"); - APPEND(plugin); - if ((plugin_instance != NULL) && (plugin_instance[0] != 0)) { - APPEND("-"); - APPEND(plugin_instance); - } - APPEND("/"); - APPEND(type); - if ((type_instance != NULL) && (type_instance[0] != 0)) { - APPEND("-"); - APPEND(type_instance); - } - assert(buffer_size > 0); - buffer[0] = 0; - -#undef APPEND - return 0; -} /* int format_name */ - -int format_values(char *ret, size_t ret_len, /* {{{ */ - const data_set_t *ds, const value_list_t *vl, - bool store_rates) { - size_t offset = 0; - int status; - gauge_t *rates = NULL; - - assert(0 == strcmp(ds->type, vl->type)); - - memset(ret, 0, ret_len); - -#define BUFFER_ADD(...) \ - do { \ - status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \ - if (status < 1) { \ - sfree(rates); \ - return -1; \ - } else if (((size_t)status) >= (ret_len - offset)) { \ - sfree(rates); \ - return -1; \ - } else \ - offset += ((size_t)status); \ - } while (0) - - BUFFER_ADD("%.3f", CDTIME_T_TO_DOUBLE(vl->time)); - - for (size_t i = 0; i < ds->ds_num; i++) { - if (ds->ds[i].type == DS_TYPE_GAUGE) - BUFFER_ADD(":" GAUGE_FORMAT, vl->values[i].gauge); - else if (store_rates) { - if (rates == NULL) - rates = uc_get_rate(ds, vl); - if (rates == NULL) { - WARNING("format_values: uc_get_rate failed."); - return -1; - } - BUFFER_ADD(":" GAUGE_FORMAT, rates[i]); - } else if (ds->ds[i].type == DS_TYPE_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) - BUFFER_ADD(":%" PRIu64, vl->values[i].absolute); - else { - ERROR("format_values: Unknown data source type: %i", ds->ds[i].type); - sfree(rates); - return -1; - } - } /* for ds->ds_num */ - -#undef BUFFER_ADD - - sfree(rates); - return 0; -} /* }}} int format_values */ - -int parse_identifier(char *str, char **ret_host, char **ret_plugin, - char **ret_plugin_instance, char **ret_type, - char **ret_type_instance, char *default_host) { - char *hostname = NULL; - char *plugin = NULL; - char *plugin_instance = NULL; - char *type = NULL; - char *type_instance = NULL; - - hostname = str; - if (hostname == NULL) - return -1; - - plugin = strchr(hostname, '/'); - if (plugin == NULL) - return -1; - *plugin = '\0'; - plugin++; - - type = strchr(plugin, '/'); - if (type == NULL) { - if (default_host == NULL) - return -1; - /* else: no host specified; use default */ - type = plugin; - plugin = hostname; - hostname = default_host; - } else { - *type = '\0'; - type++; - } - - plugin_instance = strchr(plugin, '-'); - if (plugin_instance != NULL) { - *plugin_instance = '\0'; - plugin_instance++; - } - - type_instance = strchr(type, '-'); - if (type_instance != NULL) { - *type_instance = '\0'; - type_instance++; - } - - *ret_host = hostname; - *ret_plugin = plugin; - *ret_plugin_instance = plugin_instance; - *ret_type = type; - *ret_type_instance = type_instance; - return 0; -} /* int parse_identifier */ - -int parse_identifier_vl(const char *str, value_list_t *vl) /* {{{ */ -{ - char str_copy[6 * DATA_MAX_NAME_LEN]; - char *host = NULL; - char *plugin = NULL; - char *plugin_instance = NULL; - char *type = NULL; - char *type_instance = NULL; - int status; - - if ((str == NULL) || (vl == NULL)) - return EINVAL; - - sstrncpy(str_copy, str, sizeof(str_copy)); - - status = parse_identifier(str_copy, &host, &plugin, &plugin_instance, &type, - &type_instance, - /* default_host = */ NULL); - if (status != 0) - return status; - - sstrncpy(vl->host, host, sizeof(vl->host)); - sstrncpy(vl->plugin, plugin, sizeof(vl->plugin)); - sstrncpy(vl->plugin_instance, - (plugin_instance != NULL) ? plugin_instance : "", - sizeof(vl->plugin_instance)); - sstrncpy(vl->type, type, sizeof(vl->type)); - sstrncpy(vl->type_instance, (type_instance != NULL) ? type_instance : "", - sizeof(vl->type_instance)); - - return 0; -} /* }}} int parse_identifier_vl */ - -int parse_value(const char *value_orig, value_t *ret_value, int ds_type) { - char *value; - char *endptr = NULL; - size_t value_len; - - if (value_orig == NULL) - return EINVAL; - - value = strdup(value_orig); - if (value == NULL) - return ENOMEM; - value_len = strlen(value); - - while ((value_len > 0) && isspace((int)value[value_len - 1])) { - value[value_len - 1] = 0; - value_len--; - } - - switch (ds_type) { - case DS_TYPE_COUNTER: - ret_value->counter = (counter_t)strtoull(value, &endptr, 0); - break; - - case DS_TYPE_GAUGE: - ret_value->gauge = (gauge_t)strtod(value, &endptr); - break; - - case DS_TYPE_DERIVE: - ret_value->derive = (derive_t)strtoll(value, &endptr, 0); - break; - - case DS_TYPE_ABSOLUTE: - ret_value->absolute = (absolute_t)strtoull(value, &endptr, 0); - break; - - default: - sfree(value); - P_ERROR("parse_value: Invalid data source type: %i.", ds_type); - return -1; - } - - if (value == endptr) { - P_ERROR("parse_value: Failed to parse string as %s: \"%s\".", - DS_TYPE_TO_STRING(ds_type), value); - sfree(value); - return -1; - } else if ((NULL != endptr) && ('\0' != *endptr)) - P_INFO("parse_value: Ignoring trailing garbage \"%s\" after %s value. " - "Input string was \"%s\".", - endptr, DS_TYPE_TO_STRING(ds_type), value_orig); - - sfree(value); - return 0; -} /* int parse_value */ - -int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds) { - size_t i; - char *dummy; - char *ptr; - char *saveptr; - - if ((buffer == NULL) || (vl == NULL) || (ds == NULL)) - return EINVAL; - - i = 0; - dummy = buffer; - saveptr = NULL; - vl->time = 0; - while ((ptr = strtok_r(dummy, ":", &saveptr)) != NULL) { - dummy = NULL; - - if (i >= vl->values_len) { - /* Make sure i is invalid. */ - i = 0; - break; - } - - if (vl->time == 0) { - if (strcmp("N", ptr) == 0) - vl->time = cdtime(); - else { - char *endptr = NULL; - double tmp; - - errno = 0; - tmp = strtod(ptr, &endptr); - if ((errno != 0) /* Overflow */ - || (endptr == ptr) /* Invalid string */ - || (endptr == NULL) /* This should not happen */ - || (*endptr != 0)) /* Trailing chars */ - return -1; - - vl->time = DOUBLE_TO_CDTIME_T(tmp); - } - - continue; - } - - if ((strcmp("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE)) - vl->values[i].gauge = NAN; - else if (0 != parse_value(ptr, &vl->values[i], ds->ds[i].type)) - return -1; - - i++; - } /* while (strtok_r) */ - - if ((ptr != NULL) || (i == 0)) - return -1; - return 0; -} /* int parse_values */ - -int parse_value_file(char const *path, value_t *ret_value, int ds_type) { - FILE *fh; - char buffer[256]; - - fh = fopen(path, "r"); - if (fh == NULL) - return -1; - - if (fgets(buffer, sizeof(buffer), fh) == NULL) { - fclose(fh); - return -1; - } - - fclose(fh); - - strstripnewline(buffer); - - return parse_value(buffer, ret_value, ds_type); -} /* int parse_value_file */ - -#if !HAVE_GETPWNAM_R -int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen, - struct passwd **pwbufp) { -#ifndef HAVE_GETPWNAM - return -1; -#else - int status = 0; - struct passwd *pw; - - memset(pwbuf, '\0', sizeof(struct passwd)); - - pthread_mutex_lock(&getpwnam_r_lock); - - do { - pw = getpwnam(name); - if (pw == NULL) { - status = (errno != 0) ? errno : ENOENT; - break; - } - -#define GETPWNAM_COPY_MEMBER(member) \ - if (pw->member != NULL) { \ - int len = strlen(pw->member); \ - if (len >= buflen) { \ - status = ENOMEM; \ - break; \ - } \ - sstrncpy(buf, pw->member, buflen); \ - pwbuf->member = buf; \ - buf += (len + 1); \ - buflen -= (len + 1); \ - } - GETPWNAM_COPY_MEMBER(pw_name); - GETPWNAM_COPY_MEMBER(pw_passwd); - GETPWNAM_COPY_MEMBER(pw_gecos); - GETPWNAM_COPY_MEMBER(pw_dir); - GETPWNAM_COPY_MEMBER(pw_shell); - - pwbuf->pw_uid = pw->pw_uid; - pwbuf->pw_gid = pw->pw_gid; - - if (pwbufp != NULL) - *pwbufp = pwbuf; - } while (0); - - pthread_mutex_unlock(&getpwnam_r_lock); - - return status; -#endif /* HAVE_GETPWNAM */ -} /* int getpwnam_r */ -#endif /* !HAVE_GETPWNAM_R */ - -int notification_init(notification_t *n, int severity, const char *message, - const char *host, const char *plugin, - const char *plugin_instance, const char *type, - const char *type_instance) { - memset(n, '\0', sizeof(notification_t)); - - n->severity = severity; - - if (message != NULL) - sstrncpy(n->message, message, sizeof(n->message)); - if (host != NULL) - sstrncpy(n->host, host, sizeof(n->host)); - if (plugin != NULL) - sstrncpy(n->plugin, plugin, sizeof(n->plugin)); - if (plugin_instance != NULL) - sstrncpy(n->plugin_instance, plugin_instance, sizeof(n->plugin_instance)); - if (type != NULL) - sstrncpy(n->type, type, sizeof(n->type)); - if (type_instance != NULL) - sstrncpy(n->type_instance, type_instance, sizeof(n->type_instance)); - - return 0; -} /* int notification_init */ - -int walk_directory(const char *dir, dirwalk_callback_f callback, - void *user_data, int include_hidden) { - struct dirent *ent; - DIR *dh; - int success; - int failure; - - success = 0; - failure = 0; - - if ((dh = opendir(dir)) == NULL) { - P_ERROR("walk_directory: Cannot open '%s': %s", dir, STRERRNO); - return -1; - } - - while ((ent = readdir(dh)) != NULL) { - int status; - - if (include_hidden) { - if ((strcmp(".", ent->d_name) == 0) || (strcmp("..", ent->d_name) == 0)) - continue; - } else /* if (!include_hidden) */ - { - if (ent->d_name[0] == '.') - continue; - } - - status = (*callback)(dir, ent->d_name, user_data); - if (status != 0) - failure++; - else - success++; - } - - closedir(dh); - - if ((success == 0) && (failure > 0)) - return -1; - return 0; -} - -ssize_t read_file_contents(const char *filename, char *buf, size_t bufsize) { - FILE *fh; - ssize_t ret; - - fh = fopen(filename, "r"); - if (fh == NULL) - return -1; - - ret = (ssize_t)fread(buf, 1, bufsize, fh); - if ((ret == 0) && (ferror(fh) != 0)) { - P_ERROR("read_file_contents: Reading file \"%s\" failed.", filename); - ret = -1; - } - - fclose(fh); - return ret; -} - -counter_t counter_diff(counter_t old_value, counter_t new_value) { - counter_t diff; - - if (old_value > new_value) { - if (old_value <= 4294967295U) - diff = (4294967295U - old_value) + new_value + 1; - else - diff = (18446744073709551615ULL - old_value) + new_value + 1; - } else { - diff = new_value - old_value; - } - - return diff; -} /* counter_t counter_diff */ - -int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */ - rate_to_value_state_t *state, int ds_type, cdtime_t t) { - gauge_t delta_gauge; - cdtime_t delta_t; - - if (ds_type == DS_TYPE_GAUGE) { - state->last_value.gauge = rate; - state->last_time = t; - - *ret_value = state->last_value; - return 0; - } - - /* Counter and absolute can't handle negative rates. Reset "last time" - * to zero, so that the next valid rate will re-initialize the - * structure. */ - if ((rate < 0.0) && - ((ds_type == DS_TYPE_COUNTER) || (ds_type == DS_TYPE_ABSOLUTE))) { - memset(state, 0, sizeof(*state)); - return EINVAL; - } - - /* Another invalid state: The time is not increasing. */ - if (t <= state->last_time) { - memset(state, 0, sizeof(*state)); - return EINVAL; - } - - delta_t = t - state->last_time; - delta_gauge = (rate * CDTIME_T_TO_DOUBLE(delta_t)) + state->residual; - - /* Previous value is invalid. */ - if (state->last_time == 0) /* {{{ */ - { - if (ds_type == DS_TYPE_DERIVE) { - state->last_value.derive = (derive_t)rate; - state->residual = rate - ((gauge_t)state->last_value.derive); - } else if (ds_type == DS_TYPE_COUNTER) { - state->last_value.counter = (counter_t)rate; - state->residual = rate - ((gauge_t)state->last_value.counter); - } else if (ds_type == DS_TYPE_ABSOLUTE) { - state->last_value.absolute = (absolute_t)rate; - state->residual = rate - ((gauge_t)state->last_value.absolute); - } else { - assert(23 == 42); - } - - state->last_time = t; - return EAGAIN; - } /* }}} */ - - if (ds_type == DS_TYPE_DERIVE) { - derive_t delta_derive = (derive_t)delta_gauge; - - state->last_value.derive += delta_derive; - state->residual = delta_gauge - ((gauge_t)delta_derive); - } else if (ds_type == DS_TYPE_COUNTER) { - counter_t delta_counter = (counter_t)delta_gauge; - - state->last_value.counter += delta_counter; - state->residual = delta_gauge - ((gauge_t)delta_counter); - } else if (ds_type == DS_TYPE_ABSOLUTE) { - absolute_t delta_absolute = (absolute_t)delta_gauge; - - state->last_value.absolute = delta_absolute; - state->residual = delta_gauge - ((gauge_t)delta_absolute); - } else { - assert(23 == 42); - } - - state->last_time = t; - *ret_value = state->last_value; - return 0; -} /* }}} value_t rate_to_value */ - -int value_to_rate(gauge_t *ret_rate, /* {{{ */ - value_t value, int ds_type, cdtime_t t, - value_to_rate_state_t *state) { - gauge_t interval; - - /* Another invalid state: The time is not increasing. */ - if (t <= state->last_time) { - memset(state, 0, sizeof(*state)); - return EINVAL; - } - - interval = CDTIME_T_TO_DOUBLE(t - state->last_time); - - /* Previous value is invalid. */ - if (state->last_time == 0) { - state->last_value = value; - state->last_time = t; - return EAGAIN; - } - - switch (ds_type) { - case DS_TYPE_DERIVE: { - derive_t diff = value.derive - state->last_value.derive; - *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); - break; - } - case DS_TYPE_GAUGE: { - *ret_rate = value.gauge; - break; - } - case DS_TYPE_COUNTER: { - counter_t diff = counter_diff(state->last_value.counter, value.counter); - *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); - break; - } - case DS_TYPE_ABSOLUTE: { - absolute_t diff = value.absolute; - *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); - break; - } - default: - return EINVAL; - } - - state->last_value = value; - state->last_time = t; - return 0; -} /* }}} value_t rate_to_value */ - -int service_name_to_port_number(const char *service_name) { - struct addrinfo *ai_list; - int status; - int service_number; - - if (service_name == NULL) - return -1; - - struct addrinfo ai_hints = {.ai_family = AF_UNSPEC}; - - status = getaddrinfo(/* node = */ NULL, service_name, &ai_hints, &ai_list); - if (status != 0) { - P_ERROR("service_name_to_port_number: getaddrinfo failed: %s", - gai_strerror(status)); - return -1; - } - - service_number = -1; - for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL; - ai_ptr = ai_ptr->ai_next) { - if (ai_ptr->ai_family == AF_INET) { - struct sockaddr_in *sa; - - sa = (void *)ai_ptr->ai_addr; - service_number = (int)ntohs(sa->sin_port); - } else if (ai_ptr->ai_family == AF_INET6) { - struct sockaddr_in6 *sa; - - sa = (void *)ai_ptr->ai_addr; - service_number = (int)ntohs(sa->sin6_port); - } - - if ((service_number > 0) && (service_number <= 65535)) - break; - } - - freeaddrinfo(ai_list); - - if ((service_number > 0) && (service_number <= 65535)) - return service_number; - return -1; -} /* int service_name_to_port_number */ - -void set_sock_opts(int sockfd) /* {{{ */ -{ - int status; - int socktype; - - status = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &socktype, - &(socklen_t){sizeof(socktype)}); - if (status != 0) { - P_WARNING("set_sock_opts: failed to determine socket type"); - return; - } - - if (socktype == SOCK_STREAM) { - status = - setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &(int){1}, sizeof(int)); - if (status != 0) - P_WARNING("set_sock_opts: failed to set socket keepalive flag"); - -#ifdef TCP_KEEPIDLE - int tcp_keepidle = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 100 + 1); - status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepidle, - sizeof(tcp_keepidle)); - if (status != 0) - P_WARNING("set_sock_opts: failed to set socket tcp keepalive time"); -#endif - -#ifdef TCP_KEEPINTVL - int tcp_keepintvl = - ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 1000 + 1); - status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepintvl, - sizeof(tcp_keepintvl)); - if (status != 0) - P_WARNING("set_sock_opts: failed to set socket tcp keepalive interval"); -#endif - } -} /* }}} void set_sock_opts */ - -int strtoderive(const char *string, derive_t *ret_value) /* {{{ */ -{ - derive_t tmp; - char *endptr; - - if ((string == NULL) || (ret_value == NULL)) - return EINVAL; - - errno = 0; - endptr = NULL; - tmp = (derive_t)strtoll(string, &endptr, /* base = */ 0); - if ((endptr == string) || (errno != 0)) - return -1; - - *ret_value = tmp; - return 0; -} /* }}} int strtoderive */ - -int strtogauge(const char *string, gauge_t *ret_value) /* {{{ */ -{ - gauge_t tmp; - char *endptr = NULL; - - if ((string == NULL) || (ret_value == NULL)) - return EINVAL; - - errno = 0; - endptr = NULL; - tmp = (gauge_t)strtod(string, &endptr); - if (errno != 0) - return errno; - else if ((endptr == NULL) || (*endptr != 0)) - return EINVAL; - - *ret_value = tmp; - return 0; -} /* }}} int strtogauge */ - -int strarray_add(char ***ret_array, size_t *ret_array_len, - char const *str) /* {{{ */ -{ - char **array; - size_t array_len = *ret_array_len; - - if (str == NULL) - return EINVAL; - - array = realloc(*ret_array, (array_len + 1) * sizeof(*array)); - if (array == NULL) - return ENOMEM; - *ret_array = array; - - array[array_len] = strdup(str); - if (array[array_len] == NULL) - return ENOMEM; - - array_len++; - *ret_array_len = array_len; - return 0; -} /* }}} int strarray_add */ - -void strarray_free(char **array, size_t array_len) /* {{{ */ -{ - for (size_t i = 0; i < array_len; i++) - sfree(array[i]); - sfree(array); -} /* }}} void strarray_free */ - -#if HAVE_CAPABILITY -int check_capability(int arg) /* {{{ */ -{ - cap_value_t cap_value = (cap_value_t)arg; - cap_t cap; - cap_flag_value_t cap_flag_value; - - if (!CAP_IS_SUPPORTED(cap_value)) - return -1; - - if (!(cap = cap_get_proc())) { - P_ERROR("check_capability: cap_get_proc failed."); - return -1; - } - - if (cap_get_flag(cap, cap_value, CAP_EFFECTIVE, &cap_flag_value) < 0) { - P_ERROR("check_capability: cap_get_flag failed."); - cap_free(cap); - return -1; - } - cap_free(cap); - - return cap_flag_value != CAP_SET; -} /* }}} int check_capability */ -#else -int check_capability(__attribute__((unused)) int arg) /* {{{ */ -{ - P_WARNING("check_capability: unsupported capability implementation. " - "Some plugin(s) may require elevated privileges to work properly."); - return 0; -} /* }}} int check_capability */ -#endif /* HAVE_CAPABILITY */ diff --git a/src/daemon/common.h b/src/daemon/common.h deleted file mode 100644 index addf06d3..00000000 --- a/src/daemon/common.h +++ /dev/null @@ -1,396 +0,0 @@ -/** - * collectd - src/common.h - * Copyright (C) 2005-2014 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - * Niki W. Waibel -**/ - -#ifndef COMMON_H -#define COMMON_H - -#include "collectd.h" - -#include "plugin.h" - -#if HAVE_PWD_H -#include -#endif - -#define sfree(ptr) \ - do { \ - free(ptr); \ - (ptr) = NULL; \ - } while (0) - -#define STATIC_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) - -#define IS_TRUE(s) \ - ((strcasecmp("true", (s)) == 0) || (strcasecmp("yes", (s)) == 0) || \ - (strcasecmp("on", (s)) == 0)) -#define IS_FALSE(s) \ - ((strcasecmp("false", (s)) == 0) || (strcasecmp("no", (s)) == 0) || \ - (strcasecmp("off", (s)) == 0)) - -struct rate_to_value_state_s { - value_t last_value; - cdtime_t last_time; - gauge_t residual; -}; -typedef struct rate_to_value_state_s rate_to_value_state_t; - -struct value_to_rate_state_s { - value_t last_value; - cdtime_t last_time; -}; -typedef struct value_to_rate_state_s value_to_rate_state_t; - -char *sstrncpy(char *dest, const char *src, size_t n); - -__attribute__((format(printf, 1, 2))) char *ssnprintf_alloc(char const *format, - ...); - -char *sstrdup(const char *s); -void *smalloc(size_t size); -char *sstrerror(int errnum, char *buf, size_t buflen); - -#ifndef ERRBUF_SIZE -#define ERRBUF_SIZE 256 -#endif - -#define STRERROR(e) sstrerror((e), (char[ERRBUF_SIZE]){0}, ERRBUF_SIZE) -#define STRERRNO STRERROR(errno) - -/* - * NAME - * sread - * - * DESCRIPTION - * Reads exactly `n' bytes or fails. Syntax and other behavior is analogous - * to `read(2)'. - * - * PARAMETERS - * `fd' File descriptor to write to. - * `buf' Buffer that is to be written. - * `count' Number of bytes in the buffer. - * - * RETURN VALUE - * Zero upon success or non-zero if an error occurred. `errno' is set in this - * case. - */ -int sread(int fd, void *buf, size_t count); - -/* - * NAME - * swrite - * - * DESCRIPTION - * Writes exactly `n' bytes or fails. Syntax and other behavior is analogous - * to `write(2)'. - * - * PARAMETERS - * `fd' File descriptor to write to. - * `buf' Buffer that is to be written. - * `count' Number of bytes in the buffer. - * - * RETURN VALUE - * Zero upon success or non-zero if an error occurred. `errno' is set in this - * case. - */ -int swrite(int fd, const void *buf, size_t count); - -/* - * NAME - * strsplit - * - * DESCRIPTION - * Splits a string into parts and stores pointers to the parts in `fields'. - * The characters split at are: " ", "\t", "\r", and "\n". - * - * PARAMETERS - * `string' String to split. This string will be modified. `fields' will - * contain pointers to parts of this string, so free'ing it - * will destroy `fields' as well. - * `fields' Array of strings where pointers to the parts will be stored. - * `size' Number of elements in the array. No more than `size' - * pointers will be stored in `fields'. - * - * RETURN VALUE - * Returns the number of parts stored in `fields'. - */ -int strsplit(char *string, char **fields, size_t size); - -/* - * NAME - * strjoin - * - * DESCRIPTION - * Joins together several parts of a string using `sep' as a separator. This - * is equivalent to the Perl built-in `join'. - * - * PARAMETERS - * `dst' Buffer where the result is stored. Can be NULL if you need to - * determine the required buffer size only. - * `dst_len' Length of the destination buffer. No more than this many - * bytes will be written to the memory pointed to by `dst', - * including the trailing null-byte. Must be zero if dst is - * NULL. - * `fields' Array of strings to be joined. - * `fields_num' Number of elements in the `fields' array. - * `sep' String to be inserted between any two elements of `fields'. - * This string is neither prepended nor appended to the result. - * Instead of passing "" (empty string) one can pass NULL. - * - * RETURN VALUE - * Returns the number of characters in the resulting string, excluding a - * tailing null byte. If this value is greater than or equal to "dst_len", the - * result in "dst" is truncated (but still null terminated). On error a - * negative value is returned. - */ -int strjoin(char *dst, size_t dst_len, char **fields, size_t fields_num, - const char *sep); - -/* - * NAME - * escape_slashes - * - * DESCRIPTION - * Removes slashes ("/") from "buffer". If buffer contains a single slash, - * the result will be "root". Leading slashes are removed. All other slashes - * are replaced with underscores ("_"). - * This function is used by plugin_dispatch_values() to escape all parts of - * the identifier. - * - * PARAMETERS - * `buffer' String to be escaped. - * `buffer_size' Size of the buffer. No more then this many bytes will be - * written to `buffer', including the trailing null-byte. - * - * RETURN VALUE - * Returns zero upon success and a value smaller than zero upon failure. - */ -int escape_slashes(char *buffer, size_t buffer_size); - -/** - * NAME - * escape_string - * - * DESCRIPTION - * escape_string quotes and escapes a string to be usable with collectd's - * plain text protocol. "simple" strings are left as they are, for example if - * buffer is 'simple' before the call, it will remain 'simple'. However, if - * buffer contains 'more "complex"' before the call, the returned buffer will - * contain '"more \"complex\""'. - * - * If the buffer is too small to contain the escaped string, the string will - * be truncated. However, leading and trailing double quotes, as well as an - * ending null byte are guaranteed. - * - * RETURN VALUE - * Returns zero on success, even if the string was truncated. Non-zero on - * failure. - */ -int escape_string(char *buffer, size_t buffer_size); - -/* - * NAME - * replace_special - * - * DESCRIPTION - * Replaces any special characters (anything that's not alpha-numeric or a - * dash) with an underscore. - * - * E.g. "foo$bar&" would become "foo_bar_". - * - * PARAMETERS - * `buffer' String to be handled. - * `buffer_size' Length of the string. The function returns after - * encountering a null-byte or reading this many bytes. - */ -void replace_special(char *buffer, size_t buffer_size); - -/* - * NAME - * strunescape - * - * DESCRIPTION - * Replaces any escaped characters in a string with the appropriate special - * characters. The following escaped characters are recognized: - * - * \t -> - * \n -> - * \r -> - * - * For all other escacped characters only the backslash will be removed. - * - * PARAMETERS - * `buf' String to be unescaped. - * `buf_len' Length of the string, including the terminating null-byte. - * - * RETURN VALUE - * Returns zero upon success, a value less than zero else. - */ -int strunescape(char *buf, size_t buf_len); - -/** - * Removed trailing newline characters (CR and LF) from buffer, which must be - * null terminated. Returns the length of the resulting string. - */ -__attribute__((nonnull(1))) size_t strstripnewline(char *buffer); - -/* - * NAME - * timeval_cmp - * - * DESCRIPTION - * Compare the two time values `tv0' and `tv1' and store the absolut value - * of the difference in the time value pointed to by `delta' if it does not - * equal NULL. - * - * RETURN VALUE - * Returns an integer less than, equal to, or greater than zero if `tv0' is - * less than, equal to, or greater than `tv1' respectively. - */ -int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta); - -/* make sure tv_usec stores less than a second */ -#define NORMALIZE_TIMEVAL(tv) \ - do { \ - (tv).tv_sec += (tv).tv_usec / 1000000; \ - (tv).tv_usec = (tv).tv_usec % 1000000; \ - } while (0) - -/* make sure tv_sec stores less than a second */ -#define NORMALIZE_TIMESPEC(tv) \ - do { \ - (tv).tv_sec += (tv).tv_nsec / 1000000000; \ - (tv).tv_nsec = (tv).tv_nsec % 1000000000; \ - } while (0) - -int check_create_dir(const char *file_orig); - -#ifdef HAVE_LIBKSTAT -#if HAVE_KSTAT_H -#include -#endif -int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name); -long long get_kstat_value(kstat_t *ksp, char *name); -#endif - -#ifndef HAVE_HTONLL -unsigned long long ntohll(unsigned long long n); -unsigned long long htonll(unsigned long long n); -#endif - -#if FP_LAYOUT_NEED_NOTHING -#define ntohd(d) (d) -#define htond(d) (d) -#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP -double ntohd(double d); -double htond(double d); -#else -#error \ - "Don't know how to convert between host and network representation of doubles." -#endif - -int format_name(char *ret, int ret_len, const char *hostname, - const char *plugin, const char *plugin_instance, - const char *type, const char *type_instance); -#define FORMAT_VL(ret, ret_len, vl) \ - format_name(ret, ret_len, (vl)->host, (vl)->plugin, (vl)->plugin_instance, \ - (vl)->type, (vl)->type_instance) -int format_values(char *ret, size_t ret_len, const data_set_t *ds, - const value_list_t *vl, bool store_rates); - -int parse_identifier(char *str, char **ret_host, char **ret_plugin, - char **ret_plugin_instance, char **ret_type, - char **ret_type_instance, char *default_host); -int parse_identifier_vl(const char *str, value_list_t *vl); -int parse_value(const char *value, value_t *ret_value, int ds_type); -int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds); - -/* parse_value_file reads "path" and parses its content as an integer or - * floating point, depending on "ds_type". On success, the value is stored in - * "ret_value" and zero is returned. On failure, a non-zero value is returned. - */ -int parse_value_file(char const *path, value_t *ret_value, int ds_type); - -#if !HAVE_GETPWNAM_R -struct passwd; -int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen, - struct passwd **pwbufp); -#endif - -int notification_init(notification_t *n, int severity, const char *message, - const char *host, const char *plugin, - const char *plugin_instance, const char *type, - const char *type_instance); -#define NOTIFICATION_INIT_VL(n, vl) \ - notification_init(n, NOTIF_FAILURE, NULL, (vl)->host, (vl)->plugin, \ - (vl)->plugin_instance, (vl)->type, (vl)->type_instance) - -typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename, - void *user_data); -int walk_directory(const char *dir, dirwalk_callback_f callback, - void *user_data, int hidden); -/* Returns the number of bytes read or negative on error. */ -ssize_t read_file_contents(char const *filename, char *buf, size_t bufsize); - -counter_t counter_diff(counter_t old_value, counter_t new_value); - -/* Convert a rate back to a value_t. When converting to a derive_t, counter_t - * or absolute_t, take fractional residuals into account. This is important - * when scaling counters, for example. - * Returns zero on success. Returns EAGAIN when called for the first time; in - * this case the value_t is invalid and the next call should succeed. Other - * return values indicate an error. */ -int rate_to_value(value_t *ret_value, gauge_t rate, - rate_to_value_state_t *state, int ds_type, cdtime_t t); - -int value_to_rate(gauge_t *ret_rate, value_t value, int ds_type, cdtime_t t, - value_to_rate_state_t *state); - -/* Converts a service name (a string) to a port number - * (in the range [1-65535]). Returns less than zero on error. */ -int service_name_to_port_number(const char *service_name); - -/* Sets various, non-default, socket options */ -void set_sock_opts(int sockfd); - -/** Parse a string to a derive_t value. Returns zero on success or non-zero on - * failure. If failure is returned, ret_value is not touched. */ -int strtoderive(const char *string, derive_t *ret_value); - -/** Parse a string to a gauge_t value. Returns zero on success or non-zero on - * failure. If failure is returned, ret_value is not touched. */ -int strtogauge(const char *string, gauge_t *ret_value); - -int strarray_add(char ***ret_array, size_t *ret_array_len, char const *str); -void strarray_free(char **array, size_t array_len); - -/** Check if the current process benefits from the capability passed in - * argument. Returns zero if it does, less than zero if it doesn't or on error. - * See capabilities(7) for the list of possible capabilities. - * */ -int check_capability(int arg); - -#endif /* COMMON_H */ diff --git a/src/daemon/common_test.c b/src/daemon/common_test.c deleted file mode 100644 index af2840e5..00000000 --- a/src/daemon/common_test.c +++ /dev/null @@ -1,376 +0,0 @@ -/** - * collectd - src/tests/test_common.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "common.h" -#include "testing.h" - -#if HAVE_KSTAT_H -#include -#endif - -#if HAVE_LIBKSTAT -kstat_ctl_t *kc; -#endif /* HAVE_LIBKSTAT */ - -DEF_TEST(sstrncpy) { - char buffer[16] = ""; - char *ptr = &buffer[4]; - char *ret; - - buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff; - buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff; - - ret = sstrncpy(ptr, "foobar", 8); - OK(ret == ptr); - EXPECT_EQ_STR("foobar", ptr); - OK(buffer[3] == buffer[12]); - - ret = sstrncpy(ptr, "abc", 8); - OK(ret == ptr); - EXPECT_EQ_STR("abc", ptr); - OK(buffer[3] == buffer[12]); - - ret = sstrncpy(ptr, "collectd", 8); - OK(ret == ptr); - OK(ptr[7] == 0); - EXPECT_EQ_STR("collect", ptr); - OK(buffer[3] == buffer[12]); - - return 0; -} - -DEF_TEST(sstrdup) { - char *ptr; - - ptr = sstrdup("collectd"); - OK(ptr != NULL); - EXPECT_EQ_STR("collectd", ptr); - - sfree(ptr); - - ptr = sstrdup(NULL); - OK(ptr == NULL); - - return 0; -} - -DEF_TEST(strsplit) { - char buffer[32]; - char *fields[8]; - int status; - - strncpy(buffer, "foo bar", sizeof(buffer)); - status = strsplit(buffer, fields, 8); - OK(status == 2); - EXPECT_EQ_STR("foo", fields[0]); - EXPECT_EQ_STR("bar", fields[1]); - - strncpy(buffer, "foo \t bar", sizeof(buffer)); - status = strsplit(buffer, fields, 8); - OK(status == 2); - EXPECT_EQ_STR("foo", fields[0]); - EXPECT_EQ_STR("bar", fields[1]); - - strncpy(buffer, "one two\tthree\rfour\nfive", sizeof(buffer)); - status = strsplit(buffer, fields, 8); - OK(status == 5); - EXPECT_EQ_STR("one", fields[0]); - EXPECT_EQ_STR("two", fields[1]); - EXPECT_EQ_STR("three", fields[2]); - EXPECT_EQ_STR("four", fields[3]); - EXPECT_EQ_STR("five", fields[4]); - - strncpy(buffer, "\twith trailing\n", sizeof(buffer)); - status = strsplit(buffer, fields, 8); - OK(status == 2); - EXPECT_EQ_STR("with", fields[0]); - EXPECT_EQ_STR("trailing", fields[1]); - - strncpy(buffer, "1 2 3 4 5 6 7 8 9 10 11 12 13", sizeof(buffer)); - status = strsplit(buffer, fields, 8); - OK(status == 8); - EXPECT_EQ_STR("7", fields[6]); - EXPECT_EQ_STR("8", fields[7]); - - strncpy(buffer, "single", sizeof(buffer)); - status = strsplit(buffer, fields, 8); - OK(status == 1); - EXPECT_EQ_STR("single", fields[0]); - - strncpy(buffer, "", sizeof(buffer)); - status = strsplit(buffer, fields, 8); - OK(status == 0); - - return 0; -} - -DEF_TEST(strjoin) { - struct { - char **fields; - size_t fields_num; - char *separator; - - int want_return; - char *want_buffer; - } cases[] = { - /* Normal case. */ - {(char *[]){"foo", "bar"}, 2, "!", 7, "foo!bar"}, - /* One field only. */ - {(char *[]){"foo"}, 1, "!", 3, "foo"}, - /* No fields at all. */ - {NULL, 0, "!", 0, ""}, - /* Longer separator. */ - {(char *[]){"foo", "bar"}, 2, "rcht", 10, "foorchtbar"}, - /* Empty separator. */ - {(char *[]){"foo", "bar"}, 2, "", 6, "foobar"}, - /* NULL separator. */ - {(char *[]){"foo", "bar"}, 2, NULL, 6, "foobar"}, - /* buffer not large enough -> string is truncated. */ - {(char *[]){"aaaaaa", "bbbbbb", "c!"}, 3, "-", 16, "aaaaaa-bbbbbb-c"}, - /* buffer not large enough -> last field fills buffer completely. */ - {(char *[]){"aaaaaaa", "bbbbbbb", "!"}, 3, "-", 17, "aaaaaaa-bbbbbbb"}, - /* buffer not large enough -> string does *not* end in separator. */ - {(char *[]){"aaaa", "bbbb", "cccc", "!"}, 4, "-", 16, "aaaa-bbbb-cccc"}, - /* buffer not large enough -> string does not end with partial - separator. */ - {(char *[]){"aaaaaa", "bbbbbb", "!"}, 3, "+-", 17, "aaaaaa+-bbbbbb"}, - }; - - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { - char buffer[16]; - int status; - - memset(buffer, 0xFF, sizeof(buffer)); - status = strjoin(buffer, sizeof(buffer), cases[i].fields, - cases[i].fields_num, cases[i].separator); - EXPECT_EQ_INT(cases[i].want_return, status); - EXPECT_EQ_STR(cases[i].want_buffer, buffer); - } - - /* use (NULL, 0) to determine required buffer size. */ - EXPECT_EQ_INT(3, strjoin(NULL, 0, (char *[]){"a", "b"}, 2, "-")); - - return 0; -} - -DEF_TEST(escape_slashes) { - struct { - char *str; - char *want; - } cases[] = { - {"foo/bar/baz", "foo_bar_baz"}, - {"/like/a/path", "like_a_path"}, - {"trailing/slash/", "trailing_slash_"}, - {"foo//bar", "foo__bar"}, - }; - - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { - char buffer[32]; - - strncpy(buffer, cases[i].str, sizeof(buffer)); - OK(escape_slashes(buffer, sizeof(buffer)) == 0); - EXPECT_EQ_STR(cases[i].want, buffer); - } - - return 0; -} - -DEF_TEST(escape_string) { - struct { - char *str; - char *want; - } cases[] = { - {"foobar", "foobar"}, - {"f00bar", "f00bar"}, - {"foo bar", "\"foo bar\""}, - {"foo \"bar\"", "\"foo \\\"bar\\\"\""}, - {"012345678901234", "012345678901234"}, - {"012345 78901234", "\"012345 789012\""}, - {"012345 78901\"34", "\"012345 78901\""}, - }; - - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { - char buffer[16]; - - strncpy(buffer, cases[i].str, sizeof(buffer)); - OK(escape_string(buffer, sizeof(buffer)) == 0); - EXPECT_EQ_STR(cases[i].want, buffer); - } - - return 0; -} - -DEF_TEST(strunescape) { - char buffer[16]; - int status; - - strncpy(buffer, "foo\\tbar", sizeof(buffer)); - status = strunescape(buffer, sizeof(buffer)); - OK(status == 0); - EXPECT_EQ_STR("foo\tbar", buffer); - - strncpy(buffer, "\\tfoo\\r\\n", sizeof(buffer)); - status = strunescape(buffer, sizeof(buffer)); - OK(status == 0); - EXPECT_EQ_STR("\tfoo\r\n", buffer); - - strncpy(buffer, "With \\\"quotes\\\"", sizeof(buffer)); - status = strunescape(buffer, sizeof(buffer)); - OK(status == 0); - EXPECT_EQ_STR("With \"quotes\"", buffer); - - /* Backslash before null byte */ - strncpy(buffer, "\\tbackslash end\\", sizeof(buffer)); - status = strunescape(buffer, sizeof(buffer)); - OK(status != 0); - EXPECT_EQ_STR("\tbackslash end", buffer); - return 0; - - /* Backslash at buffer end */ - strncpy(buffer, "\\t3\\56", sizeof(buffer)); - status = strunescape(buffer, 4); - OK(status != 0); - OK(buffer[0] == '\t'); - OK(buffer[1] == '3'); - OK(buffer[2] == 0); - OK(buffer[3] == 0); - OK(buffer[4] == '5'); - OK(buffer[5] == '6'); - OK(buffer[6] == '7'); - - return 0; -} - -DEF_TEST(parse_values) { - struct { - char buffer[64]; - int status; - gauge_t value; - } cases[] = { - {"1435044576:42", 0, 42.0}, {"1435044576:42:23", -1, NAN}, - {"1435044576:U", 0, NAN}, {"N:12.3", 0, 12.3}, - {"N:42.0:23", -1, NAN}, {"N:U", 0, NAN}, - {"T:42.0", -1, NAN}, - }; - - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { - data_source_t dsrc = { - .name = "value", .type = DS_TYPE_GAUGE, .min = 0.0, .max = NAN, - }; - data_set_t ds = { - .type = "example", .ds_num = 1, .ds = &dsrc, - }; - - value_t v = { - .gauge = NAN, - }; - value_list_t vl = { - .values = &v, - .values_len = 1, - .time = 0, - .interval = 0, - .host = "example.com", - .plugin = "common_test", - .type = "example", - .meta = NULL, - }; - - int status = parse_values(cases[i].buffer, &vl, &ds); - EXPECT_EQ_INT(cases[i].status, status); - if (status != 0) - continue; - - EXPECT_EQ_DOUBLE(cases[i].value, vl.values[0].gauge); - } - - return 0; -} - -DEF_TEST(value_to_rate) { - struct { - time_t t0; - time_t t1; - int ds_type; - value_t v0; - value_t v1; - gauge_t want; - } cases[] = { - {0, 10, DS_TYPE_DERIVE, {.derive = 0}, {.derive = 1000}, NAN}, - {10, 20, DS_TYPE_DERIVE, {.derive = 1000}, {.derive = 2000}, 100.0}, - {20, 30, DS_TYPE_DERIVE, {.derive = 2000}, {.derive = 1800}, -20.0}, - {0, 10, DS_TYPE_COUNTER, {.counter = 0}, {.counter = 1000}, NAN}, - {10, 20, DS_TYPE_COUNTER, {.counter = 1000}, {.counter = 5000}, 400.0}, - /* 32bit wrap-around. */ - {20, - 30, - DS_TYPE_COUNTER, - {.counter = 4294967238ULL}, - {.counter = 42}, - 10.0}, - /* 64bit wrap-around. */ - {30, - 40, - DS_TYPE_COUNTER, - {.counter = 18446744073709551558ULL}, - {.counter = 42}, - 10.0}, - }; - - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { - cdtime_t t0 = TIME_T_TO_CDTIME_T(cases[i].t0); - value_to_rate_state_t state = { - .last_value = cases[i].v0, .last_time = t0, - }; - gauge_t got; - - if (cases[i].t0 == 0) { - EXPECT_EQ_INT(EAGAIN, - value_to_rate(&got, cases[i].v1, cases[i].ds_type, - TIME_T_TO_CDTIME_T(cases[i].t1), &state)); - continue; - } - - EXPECT_EQ_INT(0, value_to_rate(&got, cases[i].v1, cases[i].ds_type, - TIME_T_TO_CDTIME_T(cases[i].t1), &state)); - EXPECT_EQ_DOUBLE(cases[i].want, got); - } - - return 0; -} - -int main(void) { - RUN_TEST(sstrncpy); - RUN_TEST(sstrdup); - RUN_TEST(strsplit); - RUN_TEST(strjoin); - RUN_TEST(escape_slashes); - RUN_TEST(escape_string); - RUN_TEST(strunescape); - RUN_TEST(parse_values); - RUN_TEST(value_to_rate); - - END_TEST; -} diff --git a/src/daemon/configfile.c b/src/daemon/configfile.c index 8750bef8..3a7364ed 100644 --- a/src/daemon/configfile.c +++ b/src/daemon/configfile.c @@ -29,11 +29,11 @@ #include "liboconfig/oconfig.h" -#include "common.h" #include "configfile.h" #include "filter_chain.h" #include "plugin.h" #include "types_list.h" +#include "utils/common/common.h" #if HAVE_WORDEXP_H #include @@ -137,40 +137,58 @@ static cf_callback_t *cf_search(const char *type) { return cf_cb; } -static int cf_dispatch(const char *type, const char *orig_key, - const char *orig_value) { - cf_callback_t *cf_cb; - plugin_ctx_t old_ctx; - char *key; - char *value; - int ret; - int i = 0; +static int cf_dispatch_option(const cf_callback_t *cf_cb, oconfig_item_t *ci) { + const char *plugin = cf_cb->type; + const char *orig_key = ci->key; if (orig_key == NULL) return EINVAL; - DEBUG("type = %s, key = %s, value = %s", ESCAPE_NULL(type), orig_key, - ESCAPE_NULL(orig_value)); + /* (Re)construct string value for option */ + char buffer[4096]; + int buffer_free = sizeof(buffer); + char *buffer_ptr = buffer; - if ((cf_cb = cf_search(type)) == NULL) { - WARNING("Found a configuration for the `%s' plugin, but " - "the plugin isn't loaded or didn't register " - "a configuration callback.", - type); - return -1; + for (int i = 0; i < ci->values_num; i++) { + int status = -1; + + if (ci->values[i].type == OCONFIG_TYPE_STRING) + status = + ssnprintf(buffer_ptr, buffer_free, " %s", ci->values[i].value.string); + else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) + status = ssnprintf(buffer_ptr, buffer_free, " %lf", + ci->values[i].value.number); + else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) + status = ssnprintf(buffer_ptr, buffer_free, " %s", + ci->values[i].value.boolean ? "true" : "false"); + + if ((status < 0) || (status >= buffer_free)) + return -1; + buffer_free -= status; + buffer_ptr += status; } - if ((key = strdup(orig_key)) == NULL) + /* skip the initial space */ + const char *orig_value = buffer + 1; + + DEBUG("plugin = %s, key = %s, value = %s", ESCAPE_NULL(plugin), orig_key, + ESCAPE_NULL(orig_value)); + + char *key = strdup(orig_key); + if (key == NULL) return 1; - if ((value = strdup(orig_value)) == NULL) { + + char *value = strdup(orig_value); + if (value == NULL) { free(key); return 2; } - ret = -1; + int ret = -1; - old_ctx = plugin_set_ctx(cf_cb->ctx); + plugin_ctx_t old_ctx = plugin_set_ctx(cf_cb->ctx); + int i; for (i = 0; i < cf_cb->keys_num; i++) { if ((cf_cb->keys[i] != NULL) && (strcasecmp(cf_cb->keys[i], key) == 0)) { ret = (*cf_cb->callback)(key, value); @@ -181,13 +199,13 @@ static int cf_dispatch(const char *type, const char *orig_key, plugin_set_ctx(old_ctx); if (i >= cf_cb->keys_num) - WARNING("Plugin `%s' did not register for value `%s'.", type, key); + WARNING("Plugin `%s' did not register for value `%s'.", plugin, key); free(key); free(value); return ret; -} /* int cf_dispatch */ +} /* int cf_dispatch_option */ static int dispatch_global_option(const oconfig_item_t *ci) { if (ci->values_num != 1) { @@ -200,7 +218,7 @@ static int dispatch_global_option(const oconfig_item_t *ci) { return global_option_set(ci->key, ci->values[0].value.string, 0); else if (ci->values[0].type == OCONFIG_TYPE_NUMBER) { char tmp[128]; - snprintf(tmp, sizeof(tmp), "%lf", ci->values[0].value.number); + ssnprintf(tmp, sizeof(tmp), "%lf", ci->values[0].value.number); return global_option_set(ci->key, tmp, 0); } else if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN) { if (ci->values[0].value.boolean) @@ -267,7 +285,8 @@ static int dispatch_loadplugin(oconfig_item_t *ci) { /* default to the global interval set before loading this plugin */ plugin_ctx_t ctx = { - .interval = cf_get_default_interval(), .name = strdup(name), + .interval = cf_get_default_interval(), + .name = strdup(name), }; if (ctx.name == NULL) return ENOMEM; @@ -298,38 +317,6 @@ static int dispatch_loadplugin(oconfig_item_t *ci) { return ret_val; } /* int dispatch_value_loadplugin */ -static int dispatch_value_plugin(const char *plugin, oconfig_item_t *ci) { - char buffer[4096]; - char *buffer_ptr; - int buffer_free; - - buffer_ptr = buffer; - buffer_free = sizeof(buffer); - - for (int i = 0; i < ci->values_num; i++) { - int status = -1; - - if (ci->values[i].type == OCONFIG_TYPE_STRING) - status = - snprintf(buffer_ptr, buffer_free, " %s", ci->values[i].value.string); - else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) - status = - snprintf(buffer_ptr, buffer_free, " %lf", ci->values[i].value.number); - else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) - status = snprintf(buffer_ptr, buffer_free, " %s", - ci->values[i].value.boolean ? "true" : "false"); - - if ((status < 0) || (status >= buffer_free)) - return -1; - buffer_free -= status; - buffer_ptr += status; - } - /* skip the initial space */ - buffer_ptr = buffer + 1; - - return cf_dispatch(plugin, ci->key, buffer_ptr); -} /* int dispatch_value_plugin */ - static int dispatch_value(oconfig_item_t *ci) { int ret = 0; @@ -363,65 +350,84 @@ static int dispatch_block_plugin(oconfig_item_t *ci) { return -1; } - const char *name = ci->values[0].value.string; - if (strcmp("libvirt", name) == 0) { + const char *plugin_name = ci->values[0].value.string; + if (strcmp("libvirt", plugin_name) == 0) { /* TODO(octo): Remove this legacy. */ WARNING("The \"libvirt\" plugin has been renamed to \"virt\" to avoid " "problems with the build system. " "Your configuration is still using the old name. " "Please change it to use \"virt\" as soon as possible. " "This compatibility code will go away eventually."); - name = "virt"; + plugin_name = "virt"; } - if (IS_TRUE(global_option_get("AutoLoadPlugin"))) { + bool plugin_loaded = plugin_is_loaded(plugin_name); + + if (!plugin_loaded && IS_TRUE(global_option_get("AutoLoadPlugin"))) { plugin_ctx_t ctx = {0}; - plugin_ctx_t old_ctx; - int status; /* default to the global interval set before loading this plugin */ ctx.interval = cf_get_default_interval(); - ctx.name = strdup(name); + ctx.name = strdup(plugin_name); - old_ctx = plugin_set_ctx(ctx); - status = plugin_load(name, /* flags = */ false); + plugin_ctx_t old_ctx = plugin_set_ctx(ctx); + int status = plugin_load(plugin_name, /* flags = */ false); /* reset to the "global" context */ plugin_set_ctx(old_ctx); if (status != 0) { - ERROR("Automatically loading plugin \"%s\" failed " + ERROR("Automatically loading plugin `%s' failed " "with status %i.", - name, status); + plugin_name, status); return status; } + plugin_loaded = true; + } + + if (!plugin_loaded) { + WARNING("There is configuration for the `%s' plugin, but the plugin isn't " + "loaded. Please check your configuration.", + plugin_name); + + /* Try to be backward-compatible with previous versions */ + return 0; } /* Check for a complex callback first */ for (cf_complex_callback_t *cb = complex_callback_head; cb != NULL; cb = cb->next) { - if (strcasecmp(name, cb->type) == 0) { - plugin_ctx_t old_ctx; - int ret_val; - - old_ctx = plugin_set_ctx(cb->ctx); - ret_val = (cb->callback(ci)); + if (strcasecmp(plugin_name, cb->type) == 0) { + plugin_ctx_t old_ctx = plugin_set_ctx(cb->ctx); + int ret_val = (cb->callback(ci)); plugin_set_ctx(old_ctx); return ret_val; } } /* Hm, no complex plugin found. Dispatch the values one by one */ + cf_callback_t *cf_cb = cf_search(plugin_name); + if (cf_cb == NULL) { + WARNING("Found a configuration for the `%s' plugin, but " + "the plugin didn't register a configuration callback.", + plugin_name); + return -1; + } + for (int i = 0; i < ci->children_num; i++) { - if (ci->children[i].children == NULL) - dispatch_value_plugin(name, ci->children + i); - else { + if (ci->children[i].children == NULL) { + oconfig_item_t *child = ci->children + i; + int ret = cf_dispatch_option(cf_cb, child); + if (ret != 0) { + ERROR("Plugin `%s' failed to handle option `%s', return code: %i", + plugin_name, child->key, ret); + return ret; + } + } else { WARNING("There is a `%s' block within the " - "configuration for the %s plugin. " - "The plugin either only expects " - "\"simple\" configuration statements " - "or wasn't loaded using `LoadPlugin'." - " Please check your configuration.", - ci->children[i].key, name); + "configuration for the `%s' plugin. " + "The plugin only expects \"simple\" configuration options. " + "Blocks are not supported. Please check your configuration.", + ci->children[i].key, plugin_name); } } @@ -473,9 +479,9 @@ static int cf_ci_replace_child(oconfig_item_t *dst, oconfig_item_t *src, return 0; } - temp = realloc(dst->children, - sizeof(oconfig_item_t) * - (dst->children_num + src->children_num - 1)); + temp = + realloc(dst->children, sizeof(oconfig_item_t) * + (dst->children_num + src->children_num - 1)); if (temp == NULL) { ERROR("configfile: realloc failed."); return -1; @@ -514,9 +520,8 @@ static int cf_ci_append_children(oconfig_item_t *dst, oconfig_item_t *src) { if ((src == NULL) || (src->children_num == 0)) return 0; - temp = - realloc(dst->children, - sizeof(oconfig_item_t) * (dst->children_num + src->children_num)); + temp = realloc(dst->children, sizeof(oconfig_item_t) * + (dst->children_num + src->children_num)); if (temp == NULL) { ERROR("configfile: realloc failed."); return -1; @@ -664,7 +669,7 @@ static oconfig_item_t *cf_read_dir(const char *dir, const char *pattern, if ((de->d_name[0] == '.') || (de->d_name[0] == 0)) continue; - status = snprintf(name, sizeof(name), "%s/%s", dir, de->d_name); + status = ssnprintf(name, sizeof(name), "%s/%s", dir, de->d_name); if ((status < 0) || ((size_t)status >= sizeof(name))) { ERROR("configfile: Not including `%s/%s' because its" " name is too long.", @@ -804,7 +809,7 @@ static oconfig_item_t *cf_read_generic(const char *path, const char *pattern, return root; } /* oconfig_item_t *cf_read_generic */ -/* #endif HAVE_WORDEXP_H */ + /* #endif HAVE_WORDEXP_H */ #else /* if !HAVE_WORDEXP_H */ static oconfig_item_t *cf_read_generic(const char *path, const char *pattern, @@ -1064,7 +1069,7 @@ int cf_util_get_string(const oconfig_item_t *ci, char **ret_string) /* {{{ */ } /* }}} int cf_util_get_string */ /* Assures the config option is a string and copies it to the provided buffer. - * Assures null-termination. */ + * Assures NUL-termination. */ int cf_util_get_string_buffer(const oconfig_item_t *ci, char *buffer, /* {{{ */ size_t buffer_size) { if ((ci == NULL) || (buffer == NULL) || (buffer_size < 1)) @@ -1076,7 +1081,7 @@ int cf_util_get_string_buffer(const oconfig_item_t *ci, char *buffer, /* {{{ */ } strncpy(buffer, ci->values[0].value.string, buffer_size); - buffer[buffer_size - 1] = 0; + buffer[buffer_size - 1] = '\0'; return 0; } /* }}} int cf_util_get_string_buffer */ @@ -1233,7 +1238,7 @@ int cf_util_get_service(const oconfig_item_t *ci, char **ret_string) /* {{{ */ P_ERROR("cf_util_get_service: Out of memory."); return -1; } - snprintf(service, 6, "%i", port); + ssnprintf(service, 6, "%i", port); sfree(*ret_string); *ret_string = service; diff --git a/src/daemon/filter_chain.c b/src/daemon/filter_chain.c index a0a76876..d5e14a34 100644 --- a/src/daemon/filter_chain.c +++ b/src/daemon/filter_chain.c @@ -26,10 +26,10 @@ #include "collectd.h" -#include "common.h" #include "configfile.h" #include "filter_chain.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_complain.h" /* diff --git a/src/daemon/globals.c b/src/daemon/globals.c index 5c6749ff..85f1b11b 100644 --- a/src/daemon/globals.c +++ b/src/daemon/globals.c @@ -21,8 +21,14 @@ * DEALINGS IN THE SOFTWARE. **/ -#include "common.h" +// clang-format off +/* + * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris + * See Github Issue #3193 for details + */ +#include "utils/common/common.h" #include "globals.h" +// clang-format on #if HAVE_KSTAT_H #include diff --git a/src/daemon/meta_data.c b/src/daemon/meta_data.c deleted file mode 100644 index 08f682e9..00000000 --- a/src/daemon/meta_data.c +++ /dev/null @@ -1,747 +0,0 @@ -/** - * collectd - src/meta_data.c - * Copyright (C) 2008-2011 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "meta_data.h" -#include "plugin.h" - -#define MD_MAX_NONSTRING_CHARS 128 - -/* - * Data types - */ -union meta_value_u { - char *mv_string; - int64_t mv_signed_int; - uint64_t mv_unsigned_int; - double mv_double; - bool mv_boolean; -}; -typedef union meta_value_u meta_value_t; - -struct meta_entry_s; -typedef struct meta_entry_s meta_entry_t; -struct meta_entry_s { - char *key; - meta_value_t value; - int type; - meta_entry_t *next; -}; - -struct meta_data_s { - meta_entry_t *head; - pthread_mutex_t lock; -}; - -/* - * Private functions - */ -static char *md_strdup(const char *orig) /* {{{ */ -{ - size_t sz; - char *dest; - - if (orig == NULL) - return NULL; - - sz = strlen(orig) + 1; - dest = malloc(sz); - if (dest == NULL) - return NULL; - - memcpy(dest, orig, sz); - - return dest; -} /* }}} char *md_strdup */ - -static meta_entry_t *md_entry_alloc(const char *key) /* {{{ */ -{ - meta_entry_t *e; - - e = calloc(1, sizeof(*e)); - if (e == NULL) { - ERROR("md_entry_alloc: calloc failed."); - return NULL; - } - - e->key = md_strdup(key); - if (e->key == NULL) { - free(e); - ERROR("md_entry_alloc: md_strdup failed."); - return NULL; - } - - e->type = 0; - e->next = NULL; - - return e; -} /* }}} meta_entry_t *md_entry_alloc */ - -/* XXX: The lock on md must be held while calling this function! */ -static meta_entry_t *md_entry_clone_contents(const meta_entry_t *orig) /* {{{ */ -{ - meta_entry_t *copy; - - /* WARNINGS : - * - we do not check that orig != NULL here. You should have done it before. - * - we do not set copy->next. DO NOT FORGET TO SET copy->next IN YOUR - * FUNCTION - */ - - copy = md_entry_alloc(orig->key); - if (copy == NULL) - return NULL; - copy->type = orig->type; - if (copy->type == MD_TYPE_STRING) - copy->value.mv_string = strdup(orig->value.mv_string); - else - copy->value = orig->value; - - return copy; -} /* }}} meta_entry_t *md_entry_clone_contents */ - -static meta_entry_t *md_entry_clone(const meta_entry_t *orig) /* {{{ */ -{ - meta_entry_t *copy; - - if (orig == NULL) - return NULL; - - copy = md_entry_clone_contents(orig); - - copy->next = md_entry_clone(orig->next); - return copy; -} /* }}} meta_entry_t *md_entry_clone */ - -static void md_entry_free(meta_entry_t *e) /* {{{ */ -{ - if (e == NULL) - return; - - free(e->key); - - if (e->type == MD_TYPE_STRING) - free(e->value.mv_string); - - if (e->next != NULL) - md_entry_free(e->next); - - free(e); -} /* }}} void md_entry_free */ - -static int md_entry_insert(meta_data_t *md, meta_entry_t *e) /* {{{ */ -{ - meta_entry_t *this; - meta_entry_t *prev; - - if ((md == NULL) || (e == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - prev = NULL; - this = md->head; - while (this != NULL) { - if (strcasecmp(e->key, this->key) == 0) - break; - - prev = this; - this = this->next; - } - - if (this == NULL) { - /* This key does not exist yet. */ - if (md->head == NULL) - md->head = e; - else { - assert(prev != NULL); - prev->next = e; - } - - e->next = NULL; - } else /* (this != NULL) */ - { - if (prev == NULL) - md->head = e; - else - prev->next = e; - - e->next = this->next; - } - - pthread_mutex_unlock(&md->lock); - - if (this != NULL) { - this->next = NULL; - md_entry_free(this); - } - - return 0; -} /* }}} int md_entry_insert */ - -/* XXX: The lock on md must be held while calling this function! */ -static int md_entry_insert_clone(meta_data_t *md, meta_entry_t *orig) /* {{{ */ -{ - meta_entry_t *e; - meta_entry_t *this; - meta_entry_t *prev; - - /* WARNINGS : - * - we do not check that md and e != NULL here. You should have done it - * before. - * - we do not use the lock. You should have set it before. - */ - - e = md_entry_clone_contents(orig); - - prev = NULL; - this = md->head; - while (this != NULL) { - if (strcasecmp(e->key, this->key) == 0) - break; - - prev = this; - this = this->next; - } - - if (this == NULL) { - /* This key does not exist yet. */ - if (md->head == NULL) - md->head = e; - else { - assert(prev != NULL); - prev->next = e; - } - - e->next = NULL; - } else /* (this != NULL) */ - { - if (prev == NULL) - md->head = e; - else - prev->next = e; - - e->next = this->next; - } - - if (this != NULL) { - this->next = NULL; - md_entry_free(this); - } - - return 0; -} /* }}} int md_entry_insert_clone */ - -/* XXX: The lock on md must be held while calling this function! */ -static meta_entry_t *md_entry_lookup(meta_data_t *md, /* {{{ */ - const char *key) { - meta_entry_t *e; - - if ((md == NULL) || (key == NULL)) - return NULL; - - for (e = md->head; e != NULL; e = e->next) - if (strcasecmp(key, e->key) == 0) - break; - - return e; -} /* }}} meta_entry_t *md_entry_lookup */ - -/* - * Each value_list_t*, as it is going through the system, is handled by exactly - * one thread. Plugins which pass a value_list_t* to another thread, e.g. the - * rrdtool plugin, must create a copy first. The meta data within a - * value_list_t* is not thread safe and doesn't need to be. - * - * The meta data associated with cache entries are a different story. There, we - * need to ensure exclusive locking to prevent leaks and other funky business. - * This is ensured by the uc_meta_data_get_*() functions. - */ - -/* - * Public functions - */ -meta_data_t *meta_data_create(void) /* {{{ */ -{ - meta_data_t *md; - - md = calloc(1, sizeof(*md)); - if (md == NULL) { - ERROR("meta_data_create: calloc failed."); - return NULL; - } - - pthread_mutex_init(&md->lock, /* attr = */ NULL); - - return md; -} /* }}} meta_data_t *meta_data_create */ - -meta_data_t *meta_data_clone(meta_data_t *orig) /* {{{ */ -{ - meta_data_t *copy; - - if (orig == NULL) - return NULL; - - copy = meta_data_create(); - if (copy == NULL) - return NULL; - - pthread_mutex_lock(&orig->lock); - copy->head = md_entry_clone(orig->head); - pthread_mutex_unlock(&orig->lock); - - return copy; -} /* }}} meta_data_t *meta_data_clone */ - -int meta_data_clone_merge(meta_data_t **dest, meta_data_t *orig) /* {{{ */ -{ - if (orig == NULL) - return 0; - - if (*dest == NULL) { - *dest = meta_data_clone(orig); - return 0; - } - - pthread_mutex_lock(&orig->lock); - for (meta_entry_t *e = orig->head; e != NULL; e = e->next) { - md_entry_insert_clone((*dest), e); - } - pthread_mutex_unlock(&orig->lock); - - return 0; -} /* }}} int meta_data_clone_merge */ - -void meta_data_destroy(meta_data_t *md) /* {{{ */ -{ - if (md == NULL) - return; - - md_entry_free(md->head); - pthread_mutex_destroy(&md->lock); - free(md); -} /* }}} void meta_data_destroy */ - -int meta_data_exists(meta_data_t *md, const char *key) /* {{{ */ -{ - if ((md == NULL) || (key == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - for (meta_entry_t *e = md->head; e != NULL; e = e->next) { - if (strcasecmp(key, e->key) == 0) { - pthread_mutex_unlock(&md->lock); - return 1; - } - } - - pthread_mutex_unlock(&md->lock); - return 0; -} /* }}} int meta_data_exists */ - -int meta_data_type(meta_data_t *md, const char *key) /* {{{ */ -{ - if ((md == NULL) || (key == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - for (meta_entry_t *e = md->head; e != NULL; e = e->next) { - if (strcasecmp(key, e->key) == 0) { - pthread_mutex_unlock(&md->lock); - return e->type; - } - } - - pthread_mutex_unlock(&md->lock); - return 0; -} /* }}} int meta_data_type */ - -int meta_data_toc(meta_data_t *md, char ***toc) /* {{{ */ -{ - int i = 0, count = 0; - - if ((md == NULL) || (toc == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - for (meta_entry_t *e = md->head; e != NULL; e = e->next) - ++count; - - if (count == 0) { - pthread_mutex_unlock(&md->lock); - return count; - } - - *toc = calloc(count, sizeof(**toc)); - for (meta_entry_t *e = md->head; e != NULL; e = e->next) - (*toc)[i++] = strdup(e->key); - - pthread_mutex_unlock(&md->lock); - return count; -} /* }}} int meta_data_toc */ - -int meta_data_delete(meta_data_t *md, const char *key) /* {{{ */ -{ - meta_entry_t *this; - meta_entry_t *prev; - - if ((md == NULL) || (key == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - prev = NULL; - this = md->head; - while (this != NULL) { - if (strcasecmp(key, this->key) == 0) - break; - - prev = this; - this = this->next; - } - - if (this == NULL) { - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - if (prev == NULL) - md->head = this->next; - else - prev->next = this->next; - - pthread_mutex_unlock(&md->lock); - - this->next = NULL; - md_entry_free(this); - - return 0; -} /* }}} int meta_data_delete */ - -/* - * Add functions - */ -int meta_data_add_string(meta_data_t *md, /* {{{ */ - const char *key, const char *value) { - meta_entry_t *e; - - if ((md == NULL) || (key == NULL) || (value == NULL)) - return -EINVAL; - - e = md_entry_alloc(key); - if (e == NULL) - return -ENOMEM; - - e->value.mv_string = md_strdup(value); - if (e->value.mv_string == NULL) { - ERROR("meta_data_add_string: md_strdup failed."); - md_entry_free(e); - return -ENOMEM; - } - e->type = MD_TYPE_STRING; - - return md_entry_insert(md, e); -} /* }}} int meta_data_add_string */ - -int meta_data_add_signed_int(meta_data_t *md, /* {{{ */ - const char *key, int64_t value) { - meta_entry_t *e; - - if ((md == NULL) || (key == NULL)) - return -EINVAL; - - e = md_entry_alloc(key); - if (e == NULL) - return -ENOMEM; - - e->value.mv_signed_int = value; - e->type = MD_TYPE_SIGNED_INT; - - return md_entry_insert(md, e); -} /* }}} int meta_data_add_signed_int */ - -int meta_data_add_unsigned_int(meta_data_t *md, /* {{{ */ - const char *key, uint64_t value) { - meta_entry_t *e; - - if ((md == NULL) || (key == NULL)) - return -EINVAL; - - e = md_entry_alloc(key); - if (e == NULL) - return -ENOMEM; - - e->value.mv_unsigned_int = value; - e->type = MD_TYPE_UNSIGNED_INT; - - return md_entry_insert(md, e); -} /* }}} int meta_data_add_unsigned_int */ - -int meta_data_add_double(meta_data_t *md, /* {{{ */ - const char *key, double value) { - meta_entry_t *e; - - if ((md == NULL) || (key == NULL)) - return -EINVAL; - - e = md_entry_alloc(key); - if (e == NULL) - return -ENOMEM; - - e->value.mv_double = value; - e->type = MD_TYPE_DOUBLE; - - return md_entry_insert(md, e); -} /* }}} int meta_data_add_double */ - -int meta_data_add_boolean(meta_data_t *md, /* {{{ */ - const char *key, bool value) { - meta_entry_t *e; - - if ((md == NULL) || (key == NULL)) - return -EINVAL; - - e = md_entry_alloc(key); - if (e == NULL) - return -ENOMEM; - - e->value.mv_boolean = value; - e->type = MD_TYPE_BOOLEAN; - - return md_entry_insert(md, e); -} /* }}} int meta_data_add_boolean */ - -/* - * Get functions - */ -int meta_data_get_string(meta_data_t *md, /* {{{ */ - const char *key, char **value) { - meta_entry_t *e; - char *temp; - - if ((md == NULL) || (key == NULL) || (value == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - e = md_entry_lookup(md, key); - if (e == NULL) { - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - if (e->type != MD_TYPE_STRING) { - ERROR("meta_data_get_string: Type mismatch for key `%s'", e->key); - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - temp = md_strdup(e->value.mv_string); - if (temp == NULL) { - pthread_mutex_unlock(&md->lock); - ERROR("meta_data_get_string: md_strdup failed."); - return -ENOMEM; - } - - pthread_mutex_unlock(&md->lock); - - *value = temp; - - return 0; -} /* }}} int meta_data_get_string */ - -int meta_data_get_signed_int(meta_data_t *md, /* {{{ */ - const char *key, int64_t *value) { - meta_entry_t *e; - - if ((md == NULL) || (key == NULL) || (value == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - e = md_entry_lookup(md, key); - if (e == NULL) { - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - if (e->type != MD_TYPE_SIGNED_INT) { - ERROR("meta_data_get_signed_int: Type mismatch for key `%s'", e->key); - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - *value = e->value.mv_signed_int; - - pthread_mutex_unlock(&md->lock); - return 0; -} /* }}} int meta_data_get_signed_int */ - -int meta_data_get_unsigned_int(meta_data_t *md, /* {{{ */ - const char *key, uint64_t *value) { - meta_entry_t *e; - - if ((md == NULL) || (key == NULL) || (value == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - e = md_entry_lookup(md, key); - if (e == NULL) { - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - if (e->type != MD_TYPE_UNSIGNED_INT) { - ERROR("meta_data_get_unsigned_int: Type mismatch for key `%s'", e->key); - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - *value = e->value.mv_unsigned_int; - - pthread_mutex_unlock(&md->lock); - return 0; -} /* }}} int meta_data_get_unsigned_int */ - -int meta_data_get_double(meta_data_t *md, /* {{{ */ - const char *key, double *value) { - meta_entry_t *e; - - if ((md == NULL) || (key == NULL) || (value == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - e = md_entry_lookup(md, key); - if (e == NULL) { - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - if (e->type != MD_TYPE_DOUBLE) { - ERROR("meta_data_get_double: Type mismatch for key `%s'", e->key); - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - *value = e->value.mv_double; - - pthread_mutex_unlock(&md->lock); - return 0; -} /* }}} int meta_data_get_double */ - -int meta_data_get_boolean(meta_data_t *md, /* {{{ */ - const char *key, bool *value) { - meta_entry_t *e; - - if ((md == NULL) || (key == NULL) || (value == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - e = md_entry_lookup(md, key); - if (e == NULL) { - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - if (e->type != MD_TYPE_BOOLEAN) { - ERROR("meta_data_get_boolean: Type mismatch for key `%s'", e->key); - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - *value = e->value.mv_boolean; - - pthread_mutex_unlock(&md->lock); - return 0; -} /* }}} int meta_data_get_boolean */ - -int meta_data_as_string(meta_data_t *md, /* {{{ */ - const char *key, char **value) { - meta_entry_t *e; - const char *actual; - char buffer[MD_MAX_NONSTRING_CHARS]; /* For non-string types. */ - char *temp; - int type; - - if ((md == NULL) || (key == NULL) || (value == NULL)) - return -EINVAL; - - pthread_mutex_lock(&md->lock); - - e = md_entry_lookup(md, key); - if (e == NULL) { - pthread_mutex_unlock(&md->lock); - return -ENOENT; - } - - type = e->type; - - switch (type) { - case MD_TYPE_STRING: - actual = e->value.mv_string; - break; - case MD_TYPE_SIGNED_INT: - snprintf(buffer, sizeof(buffer), "%" PRIi64, e->value.mv_signed_int); - actual = buffer; - break; - case MD_TYPE_UNSIGNED_INT: - snprintf(buffer, sizeof(buffer), "%" PRIu64, e->value.mv_unsigned_int); - actual = buffer; - break; - case MD_TYPE_DOUBLE: - snprintf(buffer, sizeof(buffer), GAUGE_FORMAT, e->value.mv_double); - actual = buffer; - break; - case MD_TYPE_BOOLEAN: - actual = e->value.mv_boolean ? "true" : "false"; - break; - default: - pthread_mutex_unlock(&md->lock); - ERROR("meta_data_as_string: unknown type %d for key `%s'", type, key); - return -ENOENT; - } - - pthread_mutex_unlock(&md->lock); - - temp = md_strdup(actual); - if (temp == NULL) { - ERROR("meta_data_as_string: md_strdup failed for key `%s'.", key); - return -ENOMEM; - } - - *value = temp; - - return 0; -} /* }}} int meta_data_as_string */ diff --git a/src/daemon/meta_data.h b/src/daemon/meta_data.h deleted file mode 100644 index 203b1460..00000000 --- a/src/daemon/meta_data.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * collectd - src/meta_data.h - * Copyright (C) 2008-2011 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef META_DATA_H -#define META_DATA_H - -#include "collectd.h" - -/* - * Defines - */ -#define MD_TYPE_STRING 1 -#define MD_TYPE_SIGNED_INT 2 -#define MD_TYPE_UNSIGNED_INT 3 -#define MD_TYPE_DOUBLE 4 -#define MD_TYPE_BOOLEAN 5 - -struct meta_data_s; -typedef struct meta_data_s meta_data_t; - -meta_data_t *meta_data_create(void); -meta_data_t *meta_data_clone(meta_data_t *orig); -int meta_data_clone_merge(meta_data_t **dest, meta_data_t *orig); -void meta_data_destroy(meta_data_t *md); - -int meta_data_exists(meta_data_t *md, const char *key); -int meta_data_type(meta_data_t *md, const char *key); -int meta_data_toc(meta_data_t *md, char ***toc); -int meta_data_delete(meta_data_t *md, const char *key); - -int meta_data_add_string(meta_data_t *md, const char *key, const char *value); -int meta_data_add_signed_int(meta_data_t *md, const char *key, int64_t value); -int meta_data_add_unsigned_int(meta_data_t *md, const char *key, - uint64_t value); -int meta_data_add_double(meta_data_t *md, const char *key, double value); -int meta_data_add_boolean(meta_data_t *md, const char *key, bool value); - -int meta_data_get_string(meta_data_t *md, const char *key, char **value); -int meta_data_get_signed_int(meta_data_t *md, const char *key, int64_t *value); -int meta_data_get_unsigned_int(meta_data_t *md, const char *key, - uint64_t *value); -int meta_data_get_double(meta_data_t *md, const char *key, double *value); -int meta_data_get_boolean(meta_data_t *md, const char *key, bool *value); - -/* Returns the value as a string, regardless of the type. */ -int meta_data_as_string(meta_data_t *md, const char *key, char **value); - -#endif /* META_DATA_H */ diff --git a/src/daemon/meta_data_test.c b/src/daemon/meta_data_test.c deleted file mode 100644 index ca808364..00000000 --- a/src/daemon/meta_data_test.c +++ /dev/null @@ -1,117 +0,0 @@ -/** - * collectd - src/daemon/meta_data_test.c - * Copyright (C) 2015 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "collectd.h" - -#include "common.h" /* for STATIC_ARRAY_SIZE */ - -#include "meta_data.h" -#include "testing.h" - -DEF_TEST(base) { - meta_data_t *m; - - char *s; - int64_t si; - uint64_t ui; - double d; - bool b; - - CHECK_NOT_NULL(m = meta_data_create()); - - /* all of these are absent */ - OK(meta_data_get_string(m, "string", &s) != 0); - OK(meta_data_get_signed_int(m, "signed_int", &si) != 0); - OK(meta_data_get_unsigned_int(m, "unsigned_int", &ui) != 0); - OK(meta_data_get_double(m, "double", &d) != 0); - OK(meta_data_get_boolean(m, "boolean", &b) != 0); - - /* populate structure */ - CHECK_ZERO(meta_data_add_string(m, "string", "foobar")); - OK(meta_data_exists(m, "string")); - OK(meta_data_type(m, "string") == MD_TYPE_STRING); - - CHECK_ZERO(meta_data_add_signed_int(m, "signed_int", -1)); - OK(meta_data_exists(m, "signed_int")); - OK(meta_data_type(m, "signed_int") == MD_TYPE_SIGNED_INT); - - CHECK_ZERO(meta_data_add_unsigned_int(m, "unsigned_int", 1)); - OK(meta_data_exists(m, "unsigned_int")); - OK(meta_data_type(m, "unsigned_int") == MD_TYPE_UNSIGNED_INT); - - CHECK_ZERO(meta_data_add_double(m, "double", 47.11)); - OK(meta_data_exists(m, "double")); - OK(meta_data_type(m, "double") == MD_TYPE_DOUBLE); - - CHECK_ZERO(meta_data_add_boolean(m, "boolean", 1)); - OK(meta_data_exists(m, "boolean")); - OK(meta_data_type(m, "boolean") == MD_TYPE_BOOLEAN); - - /* retrieve and check all values */ - CHECK_ZERO(meta_data_get_string(m, "string", &s)); - EXPECT_EQ_STR("foobar", s); - sfree(s); - - CHECK_ZERO(meta_data_get_signed_int(m, "signed_int", &si)); - EXPECT_EQ_INT(-1, (int)si); - - CHECK_ZERO(meta_data_get_unsigned_int(m, "unsigned_int", &ui)); - EXPECT_EQ_INT(1, (int)ui); - - CHECK_ZERO(meta_data_get_double(m, "double", &d)); - EXPECT_EQ_DOUBLE(47.11, d); - - CHECK_ZERO(meta_data_get_boolean(m, "boolean", &b)); - OK1(b, "b evaluates to true"); - - /* retrieving the wrong type always fails */ - EXPECT_EQ_INT(-2, meta_data_get_boolean(m, "string", &b)); - EXPECT_EQ_INT(-2, meta_data_get_string(m, "signed_int", &s)); - EXPECT_EQ_INT(-2, meta_data_get_string(m, "unsigned_int", &s)); - EXPECT_EQ_INT(-2, meta_data_get_string(m, "double", &s)); - EXPECT_EQ_INT(-2, meta_data_get_string(m, "boolean", &s)); - - /* replace existing keys */ - CHECK_ZERO(meta_data_add_signed_int(m, "string", 666)); - OK(meta_data_type(m, "string") == MD_TYPE_SIGNED_INT); - - CHECK_ZERO(meta_data_add_signed_int(m, "signed_int", 666)); - CHECK_ZERO(meta_data_get_signed_int(m, "signed_int", &si)); - EXPECT_EQ_INT(666, (int)si); - - /* deleting keys */ - CHECK_ZERO(meta_data_delete(m, "signed_int")); - EXPECT_EQ_INT(-2, meta_data_delete(m, "doesnt exist")); - - meta_data_destroy(m); - return 0; -} - -int main(void) { - RUN_TEST(base); - - END_TEST; -} diff --git a/src/daemon/plugin.c b/src/daemon/plugin.c index c2017aca..52cb0a4b 100644 --- a/src/daemon/plugin.c +++ b/src/daemon/plugin.c @@ -30,14 +30,14 @@ #include "collectd.h" -#include "common.h" #include "configfile.h" #include "filter_chain.h" #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" +#include "utils/heap/heap.h" #include "utils_cache.h" #include "utils_complain.h" -#include "utils_heap.h" #include "utils_llist.h" #include "utils_random.h" #include "utils_time.h" @@ -85,6 +85,14 @@ struct read_func_s { }; typedef struct read_func_s read_func_t; +struct cache_event_func_s { + plugin_cache_event_cb callback; + char *name; + user_data_t user_data; + plugin_ctx_t plugin_ctx; +}; +typedef struct cache_event_func_s cache_event_func_t; + struct write_queue_s; typedef struct write_queue_s write_queue_t; struct write_queue_s { @@ -112,6 +120,9 @@ static llist_t *list_shutdown; static llist_t *list_log; static llist_t *list_notification; +static size_t list_cache_event_num; +static cache_event_func_t list_cache_event[32]; + static fc_chain_t *pre_cache_chain; static fc_chain_t *post_cache_chain; @@ -263,8 +274,6 @@ static void destroy_read_heap(void) /* {{{ */ static int register_callback(llist_t **list, /* {{{ */ const char *name, callback_func_t *cf) { - llentry_t *le; - char *key; if (*list == NULL) { *list = llist_create(); @@ -276,14 +285,14 @@ static int register_callback(llist_t **list, /* {{{ */ } } - key = strdup(name); + char *key = strdup(name); if (key == NULL) { ERROR("plugin: register_callback: strdup failed."); destroy_callback(cf); return -1; } - le = llist_search(*list, name); + llentry_t *le = llist_search(*list, name); if (le == NULL) { le = llentry_create(key, cf); if (le == NULL) { @@ -296,9 +305,7 @@ static int register_callback(llist_t **list, /* {{{ */ llist_append(*list, le); } else { - callback_func_t *old_cf; - - old_cf = le->value; + callback_func_t *old_cf = le->value; le->value = cf; P_WARNING("register_callback: " @@ -320,7 +327,6 @@ static void log_list_callbacks(llist_t **list, /* {{{ */ int i; llentry_t *le; int n; - char **keys; n = llist_size(*list); if (n == 0) { @@ -328,11 +334,9 @@ static void log_list_callbacks(llist_t **list, /* {{{ */ return; } - keys = calloc(n, sizeof(char *)); - + char **keys = calloc(n, sizeof(*keys)); if (keys == NULL) { ERROR("%s: failed to allocate memory for list of callbacks", comment); - return; } @@ -369,7 +373,8 @@ static int create_register_callback(llist_t **list, /* {{{ */ cf->cf_callback = callback; if (ud == NULL) { cf->cf_udata = (user_data_t){ - .data = NULL, .free_func = NULL, + .data = NULL, + .free_func = NULL, }; } else { cf->cf_udata = *ud; @@ -630,7 +635,7 @@ static void start_read_threads(size_t num) /* {{{ */ if (read_threads != NULL) return; - read_threads = (pthread_t *)calloc(num, sizeof(pthread_t)); + read_threads = calloc(num, sizeof(*read_threads)); if (read_threads == NULL) { ERROR("plugin: start_read_threads: calloc failed."); return; @@ -649,7 +654,8 @@ static void start_read_threads(size_t num) /* {{{ */ } char name[THREAD_NAME_MAX]; - snprintf(name, sizeof(name), "reader#%" PRIu64, (uint64_t)read_threads_num); + ssnprintf(name, sizeof(name), "reader#%" PRIu64, + (uint64_t)read_threads_num); set_thread_name(read_threads[read_threads_num], name); read_threads_num++; @@ -819,7 +825,7 @@ static void start_write_threads(size_t num) /* {{{ */ if (write_threads != NULL) return; - write_threads = (pthread_t *)calloc(num, sizeof(pthread_t)); + write_threads = calloc(num, sizeof(*write_threads)); if (write_threads == NULL) { ERROR("plugin: start_write_threads: calloc failed."); return; @@ -838,8 +844,8 @@ static void start_write_threads(size_t num) /* {{{ */ } char name[THREAD_NAME_MAX]; - snprintf(name, sizeof(name), "writer#%" PRIu64, - (uint64_t)write_threads_num); + ssnprintf(name, sizeof(name), "writer#%" PRIu64, + (uint64_t)write_threads_num); set_thread_name(write_threads[write_threads_num], name); write_threads_num++; @@ -908,15 +914,13 @@ void plugin_set_dir(const char *dir) { ERROR("plugin_set_dir: strdup(\"%s\") failed", dir); } -static bool plugin_is_loaded(char const *name) { - int status; - +bool plugin_is_loaded(char const *name) { if (plugins_loaded == NULL) plugins_loaded = c_avl_create((int (*)(const void *, const void *))strcasecmp); assert(plugins_loaded != NULL); - status = c_avl_get(plugins_loaded, name, /* ret_value = */ NULL); + int status = c_avl_get(plugins_loaded, name, /* ret_value = */ NULL); return status == 0; } @@ -1297,8 +1301,10 @@ EXPORT int plugin_register_flush(const char *name, plugin_flush_cb callback, /* name = */ flush_name, /* callback = */ plugin_flush_timeout_callback, /* interval = */ ctx.flush_interval, - /* user data = */ &(user_data_t){ - .data = cb, .free_func = plugin_flush_timeout_callback_free, + /* user data = */ + &(user_data_t){ + .data = cb, + .free_func = plugin_flush_timeout_callback_free, }); sfree(flush_name); @@ -1313,6 +1319,60 @@ EXPORT int plugin_register_missing(const char *name, plugin_missing_cb callback, return create_register_callback(&list_missing, name, (void *)callback, ud); } /* int plugin_register_missing */ +EXPORT int plugin_register_cache_event(const char *name, + plugin_cache_event_cb callback, + user_data_t const *ud) { + + if (name == NULL || callback == NULL) + return EINVAL; + + char *name_copy = strdup(name); + if (name_copy == NULL) { + P_ERROR("plugin_register_cache_event: strdup failed."); + free_userdata(ud); + return ENOMEM; + } + + if (list_cache_event_num >= 32) { + P_ERROR("plugin_register_cache_event: Too much cache event callbacks tried " + "to be registered."); + free_userdata(ud); + return ENOMEM; + } + + for (size_t i = 0; i < list_cache_event_num; i++) { + cache_event_func_t *cef = &list_cache_event[i]; + if (!cef->callback) + continue; + + if (strcmp(name, cef->name) == 0) { + P_ERROR("plugin_register_cache_event: a callback named `%s' already " + "registered!", + name); + free_userdata(ud); + return -1; + } + } + + user_data_t user_data; + if (ud == NULL) { + user_data = (user_data_t){ + .data = NULL, .free_func = NULL, + }; + } else { + user_data = *ud; + } + + list_cache_event[list_cache_event_num] = + (cache_event_func_t){.callback = callback, + .name = name_copy, + .user_data = user_data, + .plugin_ctx = plugin_get_ctx()}; + list_cache_event_num++; + + return 0; +} /* int plugin_register_cache_event */ + EXPORT int plugin_register_shutdown(const char *name, int (*callback)(void)) { return create_register_callback(&list_shutdown, name, (void *)callback, NULL); } /* int plugin_register_shutdown */ @@ -1514,6 +1574,32 @@ EXPORT int plugin_unregister_missing(const char *name) { return plugin_unregister(list_missing, name); } +EXPORT int plugin_unregister_cache_event(const char *name) { + for (size_t i = 0; i < list_cache_event_num; i++) { + cache_event_func_t *cef = &list_cache_event[i]; + if (!cef->callback) + continue; + if (strcmp(name, cef->name) == 0) { + /* Mark callback as inactive, so mask in cache entries remains actual */ + cef->callback = NULL; + sfree(cef->name); + free_userdata(&cef->user_data); + } + } + return 0; +} + +static void destroy_cache_event_callbacks() { + for (size_t i = 0; i < list_cache_event_num; i++) { + cache_event_func_t *cef = &list_cache_event[i]; + if (!cef->callback) + continue; + cef->callback = NULL; + sfree(cef->name); + free_userdata(&cef->user_data); + } +} + EXPORT int plugin_unregister_shutdown(const char *name) { return plugin_unregister(list_shutdown, name); } @@ -1858,6 +1944,7 @@ EXPORT int plugin_shutdown_all(void) { * the data isn't freed twice. */ destroy_all_callbacks(&list_flush); destroy_all_callbacks(&list_missing); + destroy_cache_event_callbacks(); destroy_all_callbacks(&list_write); destroy_all_callbacks(&list_notification); @@ -1898,6 +1985,82 @@ EXPORT int plugin_dispatch_missing(const value_list_t *vl) /* {{{ */ return 0; } /* int }}} plugin_dispatch_missing */ +void plugin_dispatch_cache_event(enum cache_event_type_e event_type, + unsigned long callbacks_mask, const char *name, + const value_list_t *vl) { + switch (event_type) { + case CE_VALUE_NEW: + callbacks_mask = 0; + for (size_t i = 0; i < list_cache_event_num; i++) { + cache_event_func_t *cef = &list_cache_event[i]; + plugin_cache_event_cb callback = cef->callback; + + if (!callback) + continue; + + cache_event_t event = (cache_event_t){.type = event_type, + .value_list = vl, + .value_list_name = name, + .ret = 0}; + + plugin_ctx_t old_ctx = plugin_set_ctx(cef->plugin_ctx); + int status = (*callback)(&event, &cef->user_data); + plugin_set_ctx(old_ctx); + + if (status != 0) { + ERROR("plugin_dispatch_cache_event: Callback \"%s\" failed with status " + "%i for event NEW.", + cef->name, status); + } else { + if (event.ret) { + DEBUG( + "plugin_dispatch_cache_event: Callback \"%s\" subscribed to %s.", + cef->name, name); + callbacks_mask |= (1 << (i)); + } else { + DEBUG("plugin_dispatch_cache_event: Callback \"%s\" ignores %s.", + cef->name, name); + } + } + } + + if (callbacks_mask) + uc_set_callbacks_mask(name, callbacks_mask); + + break; + case CE_VALUE_UPDATE: + case CE_VALUE_EXPIRED: + for (size_t i = 0; i < list_cache_event_num; i++) { + cache_event_func_t *cef = &list_cache_event[i]; + plugin_cache_event_cb callback = cef->callback; + + if (!callback) + continue; + + if (callbacks_mask && (1 << (i)) == 0) + continue; + + cache_event_t event = (cache_event_t){.type = event_type, + .value_list = vl, + .value_list_name = name, + .ret = 0}; + + plugin_ctx_t old_ctx = plugin_set_ctx(cef->plugin_ctx); + int status = (*callback)(&event, &cef->user_data); + plugin_set_ctx(old_ctx); + + if (status != 0) { + ERROR("plugin_dispatch_cache_event: Callback \"%s\" failed with status " + "%i for event %s.", + cef->name, status, + ((event_type == CE_VALUE_UPDATE) ? "UPDATE" : "EXPIRED")); + } + } + break; + } + return; +} + static int plugin_dispatch_values_internal(value_list_t *vl) { int status; static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC; diff --git a/src/daemon/plugin.h b/src/daemon/plugin.h index 616889ad..af3693dd 100644 --- a/src/daemon/plugin.h +++ b/src/daemon/plugin.h @@ -31,7 +31,7 @@ #include "collectd.h" #include "configfile.h" -#include "meta_data.h" +#include "utils/metadata/meta_data.h" #include "utils_time.h" #include @@ -171,6 +171,15 @@ struct user_data_s { }; typedef struct user_data_s user_data_t; +enum cache_event_type_e { CE_VALUE_NEW, CE_VALUE_UPDATE, CE_VALUE_EXPIRED }; + +typedef struct cache_event_s { + enum cache_event_type_e type; + const value_list_t *value_list; + const char *value_list_name; + int ret; +} cache_event_t; + struct plugin_ctx_s { char *name; cdtime_t interval; @@ -192,6 +201,11 @@ typedef int (*plugin_flush_cb)(cdtime_t timeout, const char *identifier, * callbacks should be called, greater than zero if no more callbacks should be * called. */ typedef int (*plugin_missing_cb)(const value_list_t *, user_data_t *); +/* "cache event" callback. CE_VALUE_NEW events are sent to all registered + * callbacks. Callback should check if it interested in further CE_VALUE_UPDATE + * and CE_VALUE_EXPIRED events for metric and set event->ret = 1 if so. + */ +typedef int (*plugin_cache_event_cb)(cache_event_t *, user_data_t *); typedef void (*plugin_log_cb)(int severity, const char *message, user_data_t *); typedef int (*plugin_shutdown_cb)(void); typedef int (*plugin_notification_cb)(const notification_t *, user_data_t *); @@ -233,6 +247,7 @@ void plugin_set_dir(const char *dir); * this case. */ int plugin_load(const char *name, bool global); +bool plugin_is_loaded(char const *name); int plugin_init_all(void); void plugin_read_all(void); @@ -294,6 +309,9 @@ int plugin_register_flush(const char *name, plugin_flush_cb callback, user_data_t const *user_data); int plugin_register_missing(const char *name, plugin_missing_cb callback, user_data_t const *user_data); +int plugin_register_cache_event(const char *name, + plugin_cache_event_cb callback, + user_data_t const *ud); int plugin_register_shutdown(const char *name, plugin_shutdown_cb callback); int plugin_register_data_set(const data_set_t *ds); int plugin_register_log(const char *name, plugin_log_cb callback, @@ -310,6 +328,7 @@ int plugin_unregister_read_group(const char *group); int plugin_unregister_write(const char *name); int plugin_unregister_flush(const char *name); int plugin_unregister_missing(const char *name); +int plugin_unregister_cache_event(const char *name); int plugin_unregister_shutdown(const char *name); int plugin_unregister_data_set(const char *name); int plugin_unregister_log(const char *name); @@ -380,6 +399,9 @@ __attribute__((sentinel)) int plugin_dispatch_multivalue(value_list_t const *vl, int store_type, ...); int plugin_dispatch_missing(const value_list_t *vl); +void plugin_dispatch_cache_event(enum cache_event_type_e event_type, + unsigned long callbacks_mask, const char *name, + const value_list_t *vl); int plugin_dispatch_notification(const notification_t *notif); diff --git a/src/daemon/plugin_mock.c b/src/daemon/plugin_mock.c index 1624f0ea..69d5e28a 100644 --- a/src/daemon/plugin_mock.c +++ b/src/daemon/plugin_mock.c @@ -41,6 +41,8 @@ void plugin_set_dir(const char *dir) { /* nop */ int plugin_load(const char *name, bool global) { return ENOTSUP; } +bool plugin_is_loaded(const char *name) { return false; } + int plugin_register_config(const char *name, int (*callback)(const char *key, const char *val), const char **keys, int keys_num) { @@ -67,6 +69,13 @@ int plugin_register_write(__attribute__((unused)) const char *name, return ENOTSUP; } +int plugin_register_flush(__attribute__((unused)) const char *name, + __attribute__((unused)) plugin_flush_cb callback, + __attribute__((unused)) + user_data_t const *user_data) { + return ENOTSUP; +} + int plugin_register_missing(const char *name, plugin_missing_cb callback, user_data_t const *ud) { return ENOTSUP; @@ -85,6 +94,31 @@ int plugin_register_shutdown(const char *name, int (*callback)(void)) { int plugin_register_data_set(const data_set_t *ds) { return ENOTSUP; } +int plugin_register_notification(__attribute__((unused)) const char *name, + __attribute__((unused)) + plugin_notification_cb callback, + __attribute__((unused)) + user_data_t const *user_data) { + return ENOTSUP; +} + +#define DECLARE_UNREGISTER(t) \ + int plugin_unregister_##t(__attribute__((unused)) char const *name) { \ + return ENOTSUP; \ + } +DECLARE_UNREGISTER(config) +DECLARE_UNREGISTER(complex_config) +DECLARE_UNREGISTER(init) +DECLARE_UNREGISTER(read) +DECLARE_UNREGISTER(read_group) +DECLARE_UNREGISTER(write) +DECLARE_UNREGISTER(flush) +DECLARE_UNREGISTER(missing) +DECLARE_UNREGISTER(shutdown) +DECLARE_UNREGISTER(data_set) +DECLARE_UNREGISTER(log) +DECLARE_UNREGISTER(notification) + int plugin_dispatch_values(value_list_t const *vl) { return ENOTSUP; } int plugin_dispatch_notification(__attribute__((unused)) @@ -198,6 +232,14 @@ plugin_ctx_t plugin_set_ctx(plugin_ctx_t ctx) { cdtime_t plugin_get_interval(void) { return mock_context.interval; } +int plugin_thread_create(__attribute__((unused)) pthread_t *thread, + __attribute__((unused)) const pthread_attr_t *attr, + __attribute__((unused)) void *(*start_routine)(void *), + __attribute__((unused)) void *arg, + __attribute__((unused)) char const *name) { + return ENOTSUP; +} + /* TODO(octo): this function is actually from filter_chain.h, but in order not * to tumble down that rabbit hole, we're declaring it here. A better solution * would be to hard-code the top-level config keys in daemon/collectd.c to avoid diff --git a/src/daemon/types_list.c b/src/daemon/types_list.c index 1ccf10bc..7eb3cb2f 100644 --- a/src/daemon/types_list.c +++ b/src/daemon/types_list.c @@ -26,7 +26,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "configfile.h" #include "plugin.h" @@ -96,8 +96,6 @@ static int parse_ds(data_source_t *dsrc, char *buf, size_t buf_len) { static void parse_line(char *buf) { char *fields[64]; size_t fields_num; - data_set_t *ds; - fields_num = strsplit(buf, fields, 64); if (fields_num < 2) return; @@ -106,33 +104,27 @@ static void parse_line(char *buf) { if (fields[0][0] == '#') return; - ds = calloc(1, sizeof(*ds)); - if (ds == NULL) - return; + data_set_t ds = {{0}}; - sstrncpy(ds->type, fields[0], sizeof(ds->type)); + sstrncpy(ds.type, fields[0], sizeof(ds.type)); - ds->ds_num = fields_num - 1; - ds->ds = (data_source_t *)calloc(ds->ds_num, sizeof(data_source_t)); - if (ds->ds == NULL) { - sfree(ds); + ds.ds_num = fields_num - 1; + ds.ds = calloc(ds.ds_num, sizeof(*ds.ds)); + if (ds.ds == NULL) return; - } - for (size_t i = 0; i < ds->ds_num; i++) - if (parse_ds(ds->ds + i, fields[i + 1], strlen(fields[i + 1])) != 0) { + for (size_t i = 0; i < ds.ds_num; i++) + if (parse_ds(ds.ds + i, fields[i + 1], strlen(fields[i + 1])) != 0) { ERROR("types_list: parse_line: Cannot parse data source #%" PRIsz " of data set %s", - i, ds->type); - sfree(ds->ds); - sfree(ds); + i, ds.type); + sfree(ds.ds); return; } - plugin_register_data_set(ds); + plugin_register_data_set(&ds); - sfree(ds->ds); - sfree(ds); + sfree(ds.ds); } /* void parse_line */ static void parse_file(FILE *fh) { diff --git a/src/daemon/utils_avltree.c b/src/daemon/utils_avltree.c deleted file mode 100644 index 568d68c1..00000000 --- a/src/daemon/utils_avltree.c +++ /dev/null @@ -1,648 +0,0 @@ -/** - * collectd - src/utils_avltree.c - * Copyright (C) 2006,2007 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include -#include - -#include "utils_avltree.h" - -#define BALANCE(n) \ - ((((n)->left == NULL) ? 0 : (n)->left->height) - \ - (((n)->right == NULL) ? 0 : (n)->right->height)) - -/* - * private data types - */ -struct c_avl_node_s { - void *key; - void *value; - - int height; - struct c_avl_node_s *left; - struct c_avl_node_s *right; - struct c_avl_node_s *parent; -}; -typedef struct c_avl_node_s c_avl_node_t; - -struct c_avl_tree_s { - c_avl_node_t *root; - int (*compare)(const void *, const void *); - int size; -}; - -struct c_avl_iterator_s { - c_avl_tree_t *tree; - c_avl_node_t *node; -}; - -/* - * private functions - */ -#if 0 -static void verify_tree (c_avl_node_t *n) -{ - if (n == NULL) - return; - - verify_tree (n->left); - verify_tree (n->right); - - assert ((BALANCE (n) >= -1) && (BALANCE (n) <= 1)); - assert ((n->parent == NULL) || (n->parent->right == n) || (n->parent->left == n)); -} /* void verify_tree */ -#else -#define verify_tree(n) /**/ -#endif - -static void free_node(c_avl_node_t *n) { - if (n == NULL) - return; - - if (n->left != NULL) - free_node(n->left); - if (n->right != NULL) - free_node(n->right); - - free(n); -} - -static int calc_height(c_avl_node_t *n) { - int height_left; - int height_right; - - if (n == NULL) - return 0; - - height_left = (n->left == NULL) ? 0 : n->left->height; - height_right = (n->right == NULL) ? 0 : n->right->height; - - return ((height_left > height_right) ? height_left : height_right) + 1; -} /* int calc_height */ - -static c_avl_node_t *search(c_avl_tree_t *t, const void *key) { - c_avl_node_t *n; - int cmp; - - n = t->root; - while (n != NULL) { - cmp = t->compare(key, n->key); - if (cmp == 0) - return n; - else if (cmp < 0) - n = n->left; - else - n = n->right; - } - - return NULL; -} - -/* (x) (y) - * / \ / \ - * (y) /\ /\ (x) - * / \ /_c\ ==> / a\ / \ - * /\ /\ /____\/\ /\ - * / a\ /_b\ /_b\ /_c\ - * /____\ - */ -static c_avl_node_t *rotate_right(c_avl_tree_t *t, c_avl_node_t *x) { - c_avl_node_t *p; - c_avl_node_t *y; - c_avl_node_t *b; - - assert(x != NULL); - assert(x->left != NULL); - - p = x->parent; - y = x->left; - b = y->right; - - x->left = b; - if (b != NULL) - b->parent = x; - - x->parent = y; - y->right = x; - - y->parent = p; - assert((p == NULL) || (p->left == x) || (p->right == x)); - if (p == NULL) - t->root = y; - else if (p->left == x) - p->left = y; - else - p->right = y; - - x->height = calc_height(x); - y->height = calc_height(y); - - return y; -} /* void rotate_right */ - -/* - * (x) (y) - * / \ / \ - * /\ (y) (x) /\ - * /_a\ / \ ==> / \ / c\ - * /\ /\ /\ /\/____\ - * /_b\ / c\ /_a\ /_b\ - * /____\ - */ -static c_avl_node_t *rotate_left(c_avl_tree_t *t, c_avl_node_t *x) { - c_avl_node_t *p; - c_avl_node_t *y; - c_avl_node_t *b; - - assert(x != NULL); - assert(x->right != NULL); - - p = x->parent; - y = x->right; - b = y->left; - - x->right = b; - if (b != NULL) - b->parent = x; - - x->parent = y; - y->left = x; - - y->parent = p; - assert((p == NULL) || (p->left == x) || (p->right == x)); - if (p == NULL) - t->root = y; - else if (p->left == x) - p->left = y; - else - p->right = y; - - x->height = calc_height(x); - y->height = calc_height(y); - - return y; -} /* void rotate_left */ - -static c_avl_node_t *rotate_left_right(c_avl_tree_t *t, c_avl_node_t *x) { - rotate_left(t, x->left); - return rotate_right(t, x); -} /* void rotate_left_right */ - -static c_avl_node_t *rotate_right_left(c_avl_tree_t *t, c_avl_node_t *x) { - rotate_right(t, x->right); - return rotate_left(t, x); -} /* void rotate_right_left */ - -static void rebalance(c_avl_tree_t *t, c_avl_node_t *n) { - int b_top; - int b_bottom; - - while (n != NULL) { - b_top = BALANCE(n); - assert((b_top >= -2) && (b_top <= 2)); - - if (b_top == -2) { - assert(n->right != NULL); - b_bottom = BALANCE(n->right); - assert((b_bottom >= -1) && (b_bottom <= 1)); - if (b_bottom == 1) - n = rotate_right_left(t, n); - else - n = rotate_left(t, n); - } else if (b_top == 2) { - assert(n->left != NULL); - b_bottom = BALANCE(n->left); - assert((b_bottom >= -1) && (b_bottom <= 1)); - if (b_bottom == -1) - n = rotate_left_right(t, n); - else - n = rotate_right(t, n); - } else { - int height = calc_height(n); - if (height == n->height) - break; - n->height = height; - } - - assert(n->height == calc_height(n)); - - n = n->parent; - } /* while (n != NULL) */ -} /* void rebalance */ - -static c_avl_node_t *c_avl_node_next(c_avl_node_t *n) { - c_avl_node_t *r; /* return node */ - - if (n == NULL) { - return NULL; - } - - /* If we can't descent any further, we have to backtrack to the first - * parent that's bigger than we, i. e. who's _left_ child we are. */ - if (n->right == NULL) { - r = n->parent; - while ((r != NULL) && (r->parent != NULL)) { - if (r->left == n) - break; - n = r; - r = n->parent; - } - - /* n->right == NULL && r == NULL => t is root and has no next - * r->left != n => r->right = n => r->parent == NULL */ - if ((r == NULL) || (r->left != n)) { - assert((r == NULL) || (r->parent == NULL)); - return NULL; - } else { - assert(r->left == n); - return r; - } - } else { - r = n->right; - while (r->left != NULL) - r = r->left; - } - - return r; -} /* c_avl_node_t *c_avl_node_next */ - -static c_avl_node_t *c_avl_node_prev(c_avl_node_t *n) { - c_avl_node_t *r; /* return node */ - - if (n == NULL) { - return NULL; - } - - /* If we can't descent any further, we have to backtrack to the first - * parent that's smaller than we, i. e. who's _right_ child we are. */ - if (n->left == NULL) { - r = n->parent; - while ((r != NULL) && (r->parent != NULL)) { - if (r->right == n) - break; - n = r; - r = n->parent; - } - - /* n->left == NULL && r == NULL => t is root and has no next - * r->right != n => r->left = n => r->parent == NULL */ - if ((r == NULL) || (r->right != n)) { - assert((r == NULL) || (r->parent == NULL)); - return NULL; - } else { - assert(r->right == n); - return r; - } - } else { - r = n->left; - while (r->right != NULL) - r = r->right; - } - - return r; -} /* c_avl_node_t *c_avl_node_prev */ - -static int _remove(c_avl_tree_t *t, c_avl_node_t *n) { - assert((t != NULL) && (n != NULL)); - - if ((n->left != NULL) && (n->right != NULL)) { - c_avl_node_t *r; /* replacement node */ - if (BALANCE(n) > 0) /* left subtree is higher */ - { - assert(n->left != NULL); - r = c_avl_node_prev(n); - - } else /* right subtree is higher */ - { - assert(n->right != NULL); - r = c_avl_node_next(n); - } - - assert((r->left == NULL) || (r->right == NULL)); - - /* copy content */ - n->key = r->key; - n->value = r->value; - - n = r; - } - - assert((n->left == NULL) || (n->right == NULL)); - - if ((n->left == NULL) && (n->right == NULL)) { - /* Deleting a leave is easy */ - if (n->parent == NULL) { - assert(t->root == n); - t->root = NULL; - } else { - assert((n->parent->left == n) || (n->parent->right == n)); - if (n->parent->left == n) - n->parent->left = NULL; - else - n->parent->right = NULL; - - rebalance(t, n->parent); - } - - free_node(n); - } else if (n->left == NULL) { - assert(BALANCE(n) == -1); - assert((n->parent == NULL) || (n->parent->left == n) || - (n->parent->right == n)); - if (n->parent == NULL) { - assert(t->root == n); - t->root = n->right; - } else if (n->parent->left == n) { - n->parent->left = n->right; - } else { - n->parent->right = n->right; - } - n->right->parent = n->parent; - - if (n->parent != NULL) - rebalance(t, n->parent); - - n->right = NULL; - free_node(n); - } else if (n->right == NULL) { - assert(BALANCE(n) == 1); - assert((n->parent == NULL) || (n->parent->left == n) || - (n->parent->right == n)); - if (n->parent == NULL) { - assert(t->root == n); - t->root = n->left; - } else if (n->parent->left == n) { - n->parent->left = n->left; - } else { - n->parent->right = n->left; - } - n->left->parent = n->parent; - - if (n->parent != NULL) - rebalance(t, n->parent); - - n->left = NULL; - free_node(n); - } else { - assert(0); - } - - return 0; -} /* void *_remove */ - -/* - * public functions - */ -c_avl_tree_t *c_avl_create(int (*compare)(const void *, const void *)) { - c_avl_tree_t *t; - - if (compare == NULL) - return NULL; - - if ((t = malloc(sizeof(*t))) == NULL) - return NULL; - - t->root = NULL; - t->compare = compare; - t->size = 0; - - return t; -} - -void c_avl_destroy(c_avl_tree_t *t) { - if (t == NULL) - return; - free_node(t->root); - free(t); -} - -int c_avl_insert(c_avl_tree_t *t, void *key, void *value) { - c_avl_node_t *new; - c_avl_node_t *nptr; - int cmp; - - if ((new = malloc(sizeof(*new))) == NULL) - return -1; - - new->key = key; - new->value = value; - new->height = 1; - new->left = NULL; - new->right = NULL; - - if (t->root == NULL) { - new->parent = NULL; - t->root = new; - t->size = 1; - return 0; - } - - nptr = t->root; - while (42) { - cmp = t->compare(nptr->key, new->key); - if (cmp == 0) { - free_node(new); - return 1; - } else if (cmp < 0) { - /* nptr < new */ - if (nptr->right == NULL) { - nptr->right = new; - new->parent = nptr; - rebalance(t, nptr); - break; - } else { - nptr = nptr->right; - } - } else /* if (cmp > 0) */ - { - /* nptr > new */ - if (nptr->left == NULL) { - nptr->left = new; - new->parent = nptr; - rebalance(t, nptr); - break; - } else { - nptr = nptr->left; - } - } - } /* while (42) */ - - verify_tree(t->root); - ++t->size; - return 0; -} /* int c_avl_insert */ - -int c_avl_remove(c_avl_tree_t *t, const void *key, void **rkey, void **rvalue) { - c_avl_node_t *n; - int status; - - assert(t != NULL); - - n = search(t, key); - if (n == NULL) - return -1; - - if (rkey != NULL) - *rkey = n->key; - if (rvalue != NULL) - *rvalue = n->value; - - status = _remove(t, n); - verify_tree(t->root); - --t->size; - return status; -} /* void *c_avl_remove */ - -int c_avl_get(c_avl_tree_t *t, const void *key, void **value) { - c_avl_node_t *n; - - assert(t != NULL); - - n = search(t, key); - if (n == NULL) - return -1; - - if (value != NULL) - *value = n->value; - - return 0; -} - -int c_avl_pick(c_avl_tree_t *t, void **key, void **value) { - c_avl_node_t *n; - c_avl_node_t *p; - - assert(t != NULL); - - if ((key == NULL) || (value == NULL)) - return -1; - if (t->root == NULL) - return -1; - - n = t->root; - while ((n->left != NULL) || (n->right != NULL)) { - if (n->left == NULL) { - n = n->right; - continue; - } else if (n->right == NULL) { - n = n->left; - continue; - } - - if (n->left->height > n->right->height) - n = n->left; - else - n = n->right; - } - - p = n->parent; - if (p == NULL) - t->root = NULL; - else if (p->left == n) - p->left = NULL; - else - p->right = NULL; - - *key = n->key; - *value = n->value; - - free_node(n); - --t->size; - rebalance(t, p); - - return 0; -} /* int c_avl_pick */ - -c_avl_iterator_t *c_avl_get_iterator(c_avl_tree_t *t) { - c_avl_iterator_t *iter; - - if (t == NULL) - return NULL; - - iter = calloc(1, sizeof(*iter)); - if (iter == NULL) - return NULL; - iter->tree = t; - - return iter; -} /* c_avl_iterator_t *c_avl_get_iterator */ - -int c_avl_iterator_next(c_avl_iterator_t *iter, void **key, void **value) { - c_avl_node_t *n; - - if ((iter == NULL) || (key == NULL) || (value == NULL)) - return -1; - - if (iter->node == NULL) { - for (n = iter->tree->root; n != NULL; n = n->left) - if (n->left == NULL) - break; - iter->node = n; - } else { - n = c_avl_node_next(iter->node); - } - - if (n == NULL) - return -1; - - iter->node = n; - *key = n->key; - *value = n->value; - - return 0; -} /* int c_avl_iterator_next */ - -int c_avl_iterator_prev(c_avl_iterator_t *iter, void **key, void **value) { - c_avl_node_t *n; - - if ((iter == NULL) || (key == NULL) || (value == NULL)) - return -1; - - if (iter->node == NULL) { - for (n = iter->tree->root; n != NULL; n = n->right) - if (n->right == NULL) - break; - iter->node = n; - } else { - n = c_avl_node_prev(iter->node); - } - - if (n == NULL) - return -1; - - iter->node = n; - *key = n->key; - *value = n->value; - - return 0; -} /* int c_avl_iterator_prev */ - -void c_avl_iterator_destroy(c_avl_iterator_t *iter) { free(iter); } - -int c_avl_size(c_avl_tree_t *t) { - if (t == NULL) - return 0; - return t->size; -} diff --git a/src/daemon/utils_avltree.h b/src/daemon/utils_avltree.h deleted file mode 100644 index 3f52b931..00000000 --- a/src/daemon/utils_avltree.h +++ /dev/null @@ -1,169 +0,0 @@ -/** - * collectd - src/utils_avltree.h - * Copyright (C) 2006,2007 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_AVLTREE_H -#define UTILS_AVLTREE_H 1 - -struct c_avl_tree_s; -typedef struct c_avl_tree_s c_avl_tree_t; - -struct c_avl_iterator_s; -typedef struct c_avl_iterator_s c_avl_iterator_t; - -/* - * NAME - * c_avl_create - * - * DESCRIPTION - * Allocates a new AVL-tree. - * - * PARAMETERS - * `compare' The function-pointer `compare' is used to compare two keys. It - * has to return less than zero if its first argument is smaller - * then the second argument, more than zero if the first argument - * is bigger than the second argument and zero if they are equal. - * If your keys are char-pointers, you can use the `strcmp' - * function from the libc here. - * - * RETURN VALUE - * A c_avl_tree_t-pointer upon success or NULL upon failure. - */ -c_avl_tree_t *c_avl_create(int (*compare)(const void *, const void *)); - -/* - * NAME - * c_avl_destroy - * - * DESCRIPTION - * Deallocates an AVL-tree. Stored value- and key-pointer are lost, but of - * course not freed. - */ -void c_avl_destroy(c_avl_tree_t *t); - -/* - * NAME - * c_avl_insert - * - * DESCRIPTION - * Stores the key-value-pair in the AVL-tree pointed to by `t'. - * - * PARAMETERS - * `t' AVL-tree to store the data in. - * `key' Key used to store the value under. This is used to get back to - * the value again. The pointer is stored in an internal structure - * and _not_ copied. So the memory pointed to may _not_ be freed - * before this entry is removed. You can use the `rkey' argument - * to `avl_remove' to get the original pointer back and free it. - * `value' Value to be stored. - * - * RETURN VALUE - * Zero upon success, non-zero otherwise. It's less than zero if an error - * occurred or greater than zero if the key is already stored in the tree. - */ -int c_avl_insert(c_avl_tree_t *t, void *key, void *value); - -/* - * NAME - * c_avl_remove - * - * DESCRIPTION - * Removes a key-value-pair from the tree t. The stored key and value may be - * returned in `rkey' and `rvalue'. - * - * PARAMETERS - * `t' AVL-tree to remove key-value-pair from. - * `key' Key to identify the entry. - * `rkey' Pointer to a pointer in which to store the key. May be NULL. - * Since the `key' pointer is not copied when creating an entry, - * the pointer may not be available anymore from outside the tree. - * You can use this argument to get the actual pointer back and - * free the memory pointed to by it. - * `rvalue' Pointer to a pointer in which to store the value. May be NULL. - * - * RETURN VALUE - * Zero upon success or non-zero if the key isn't found in the tree. - */ -int c_avl_remove(c_avl_tree_t *t, const void *key, void **rkey, void **rvalue); - -/* - * NAME - * c_avl_get - * - * DESCRIPTION - * Retrieve the `value' belonging to `key'. - * - * PARAMETERS - * `t' AVL-tree to get the value from. - * `key' Key to identify the entry. - * `value' Pointer to a pointer in which to store the value. May be NULL. - * - * RETURN VALUE - * Zero upon success or non-zero if the key isn't found in the tree. - */ -int c_avl_get(c_avl_tree_t *t, const void *key, void **value); - -/* - * NAME - * c_avl_pick - * - * DESCRIPTION - * Remove a (pseudo-)random element from the tree and return its `key' and - * `value'. Entries are not returned in any particular order. This function - * is intended for cache-flushes that don't care about the order but simply - * want to remove all elements, one at a time. - * - * PARAMETERS - * `t' AVL-tree to get the value from. - * `key' Pointer to a pointer in which to store the key. - * `value' Pointer to a pointer in which to store the value. - * - * RETURN VALUE - * Zero upon success or non-zero if the tree is empty or key or value is - * NULL. - */ -int c_avl_pick(c_avl_tree_t *t, void **key, void **value); - -c_avl_iterator_t *c_avl_get_iterator(c_avl_tree_t *t); -int c_avl_iterator_next(c_avl_iterator_t *iter, void **key, void **value); -int c_avl_iterator_prev(c_avl_iterator_t *iter, void **key, void **value); -void c_avl_iterator_destroy(c_avl_iterator_t *iter); - -/* - * NAME - * c_avl_size - * - * DESCRIPTION - * Return the size (number of nodes) of the specified tree. - * - * PARAMETERS - * `t' AVL-tree to get the size of. - * - * RETURN VALUE - * Number of nodes in the tree, 0 if the tree is empty or NULL. - */ -int c_avl_size(c_avl_tree_t *t); - -#endif /* UTILS_AVLTREE_H */ diff --git a/src/daemon/utils_avltree_test.c b/src/daemon/utils_avltree_test.c deleted file mode 100644 index 4be49416..00000000 --- a/src/daemon/utils_avltree_test.c +++ /dev/null @@ -1,180 +0,0 @@ -/** - * collectd - src/tests/test_utils_avltree.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "collectd.h" -#include "common.h" /* STATIC_ARRAY_SIZE */ - -#include "testing.h" -#include "utils_avltree.h" - -static int compare_total_count; - -#define RESET_COUNTS() \ - do { \ - compare_total_count = 0; \ - } while (0) - -static int compare_callback(void const *v0, void const *v1) { - assert(v0 != NULL); - assert(v1 != NULL); - - compare_total_count++; - return strcmp(v0, v1); -} - -struct kv_t { - char *key; - char *value; -}; - -static int kv_compare(const void *a_ptr, const void *b_ptr) { - return strcmp(((struct kv_t *)a_ptr)->key, ((struct kv_t *)b_ptr)->key); -} - -DEF_TEST(success) { - struct kv_t cases[] = { - {"Eeph7chu", "vai1reiV"}, {"igh3Paiz", "teegh1Ee"}, - {"caip6Uu8", "ooteQu8n"}, {"Aech6vah", "AijeeT0l"}, - {"Xah0et2L", "gah8Taep"}, {"BocaeB8n", "oGaig8io"}, - {"thai8AhM", "ohjeFo3f"}, {"ohth6ieC", "hoo8ieWo"}, - {"aej7Woow", "phahuC2s"}, {"Hai8ier2", "Yie6eimi"}, - {"phuXi3Li", "JaiF7ieb"}, {"Shaig5ef", "aihi5Zai"}, - {"voh6Aith", "Oozaeto0"}, {"zaiP5kie", "seep5veM"}, - {"pae7ba7D", "chie8Ojo"}, {"Gou2ril3", "ouVoo0ha"}, - {"lo3Thee3", "ahDu4Zuj"}, {"Rah8kohv", "ieShoc7E"}, - {"ieN5engi", "Aevou1ah"}, {"ooTe4OhP", "aingai5Y"}, - }; - - struct kv_t sorted_cases[STATIC_ARRAY_SIZE(cases)]; - memcpy(sorted_cases, cases, sizeof(cases)); - qsort(sorted_cases, STATIC_ARRAY_SIZE(cases), sizeof(struct kv_t), - kv_compare); - - c_avl_tree_t *t; - - RESET_COUNTS(); - CHECK_NOT_NULL(t = c_avl_create(compare_callback)); - - /* insert */ - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { - char *key; - char *value; - - CHECK_NOT_NULL(key = strdup(cases[i].key)); - CHECK_NOT_NULL(value = strdup(cases[i].value)); - - CHECK_ZERO(c_avl_insert(t, key, value)); - EXPECT_EQ_INT((int)(i + 1), c_avl_size(t)); - } - - /* Key already exists. */ - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) - EXPECT_EQ_INT(1, c_avl_insert(t, cases[i].key, cases[i].value)); - - /* get */ - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { - char *value_ret = NULL; - - CHECK_ZERO(c_avl_get(t, cases[i].key, (void *)&value_ret)); - EXPECT_EQ_STR(cases[i].value, value_ret); - } - - /* iterate forward */ - { - c_avl_iterator_t *iter = c_avl_get_iterator(t); - char *key; - char *value; - size_t i = 0; - while (c_avl_iterator_next(iter, (void **)&key, (void **)&value) == 0) { - EXPECT_EQ_STR(sorted_cases[i].key, key); - EXPECT_EQ_STR(sorted_cases[i].value, value); - i++; - } - c_avl_iterator_destroy(iter); - EXPECT_EQ_INT(i, STATIC_ARRAY_SIZE(cases)); - } - - /* iterate backward */ - { - c_avl_iterator_t *iter = c_avl_get_iterator(t); - char *key; - char *value; - size_t i = 0; - while (c_avl_iterator_prev(iter, (void **)&key, (void **)&value) == 0) { - EXPECT_EQ_STR(sorted_cases[STATIC_ARRAY_SIZE(cases) - 1 - i].key, key); - EXPECT_EQ_STR(sorted_cases[STATIC_ARRAY_SIZE(cases) - 1 - i].value, - value); - i++; - } - c_avl_iterator_destroy(iter); - EXPECT_EQ_INT(i, STATIC_ARRAY_SIZE(cases)); - } - - /* remove half */ - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases) / 2; i++) { - char *key = NULL; - char *value = NULL; - - int expected_size = (int)(STATIC_ARRAY_SIZE(cases) - (i + 1)); - - CHECK_ZERO(c_avl_remove(t, cases[i].key, (void *)&key, (void *)&value)); - - EXPECT_EQ_STR(cases[i].key, key); - EXPECT_EQ_STR(cases[i].value, value); - - free(key); - free(value); - - EXPECT_EQ_INT(expected_size, c_avl_size(t)); - } - - /* pick the other half */ - for (size_t i = STATIC_ARRAY_SIZE(cases) / 2; i < STATIC_ARRAY_SIZE(cases); - i++) { - char *key = NULL; - char *value = NULL; - - int expected_size = (int)(STATIC_ARRAY_SIZE(cases) - (i + 1)); - - EXPECT_EQ_INT(expected_size + 1, c_avl_size(t)); - EXPECT_EQ_INT(0, c_avl_pick(t, (void *)&key, (void *)&value)); - - free(key); - free(value); - - EXPECT_EQ_INT(expected_size, c_avl_size(t)); - } - - c_avl_destroy(t); - - return 0; -} - -int main(void) { - RUN_TEST(success); - - END_TEST; -} diff --git a/src/daemon/utils_cache.c b/src/daemon/utils_cache.c index e09b0999..672b01f1 100644 --- a/src/daemon/utils_cache.c +++ b/src/daemon/utils_cache.c @@ -28,10 +28,10 @@ #include "collectd.h" -#include "common.h" -#include "meta_data.h" #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" +#include "utils/metadata/meta_data.h" #include "utils_cache.h" #include @@ -67,6 +67,7 @@ typedef struct cache_entry_s { size_t history_length; meta_data_t *meta; + unsigned long callbacks_mask; } cache_entry_t; struct uc_iter_s { @@ -140,18 +141,15 @@ static void uc_check_range(const data_set_t *ds, cache_entry_t *ce) { static int uc_insert(const data_set_t *ds, const value_list_t *vl, const char *key) { - char *key_copy; - cache_entry_t *ce; - /* `cache_lock' has been locked by `uc_update' */ - key_copy = strdup(key); + char *key_copy = strdup(key); if (key_copy == NULL) { ERROR("uc_insert: strdup failed."); return -1; } - ce = cache_alloc(ds->ds_num); + cache_entry_t *ce = cache_alloc(ds->ds_num); if (ce == NULL) { sfree(key_copy); ERROR("uc_insert: cache_alloc (%" PRIsz ") failed.", ds->ds_num); @@ -203,6 +201,10 @@ static int uc_insert(const data_set_t *ds, const value_list_t *vl, ce->interval = vl->interval; ce->state = STATE_UNKNOWN; + if (vl->meta != NULL) { + ce->meta = meta_data_clone(vl->meta); + } + if (c_avl_insert(cache_tree, key_copy, ce) != 0) { sfree(key_copy); ERROR("uc_insert: c_avl_insert failed."); @@ -226,6 +228,7 @@ int uc_check_timeout(void) { char *key; cdtime_t time; cdtime_t interval; + unsigned long callbacks_mask; } *expired = NULL; size_t expired_num = 0; @@ -251,6 +254,7 @@ int uc_check_timeout(void) { expired[expired_num].key = strdup(key); expired[expired_num].time = ce->last_time; expired[expired_num].interval = ce->interval; + expired[expired_num].callbacks_mask = ce->callbacks_mask; if (expired[expired_num].key == NULL) { ERROR("uc_check_timeout: strdup failed."); @@ -275,7 +279,8 @@ int uc_check_timeout(void) { * plugin calls the cache interface. */ for (size_t i = 0; i < expired_num; i++) { value_list_t vl = { - .time = expired[i].time, .interval = expired[i].interval, + .time = expired[i].time, + .interval = expired[i].interval, }; if (parse_identifier_vl(expired[i].key, &vl) != 0) { @@ -285,6 +290,10 @@ int uc_check_timeout(void) { } plugin_dispatch_missing(&vl); + + if (expired[i].callbacks_mask) + plugin_dispatch_cache_event(CE_VALUE_EXPIRED, expired[i].callbacks_mask, + expired[i].key, &vl); } /* for (i = 0; i < expired_num; i++) */ /* Now actually remove all the values from the cache. We don't re-evaluate @@ -314,8 +323,6 @@ int uc_check_timeout(void) { int uc_update(const data_set_t *ds, const value_list_t *vl) { char name[6 * DATA_MAX_NAME_LEN]; - cache_entry_t *ce = NULL; - int status; if (FORMAT_VL(name, sizeof(name), vl) != 0) { ERROR("uc_update: FORMAT_VL failed."); @@ -324,11 +331,16 @@ int uc_update(const data_set_t *ds, const value_list_t *vl) { pthread_mutex_lock(&cache_lock); - status = c_avl_get(cache_tree, name, (void *)&ce); + cache_entry_t *ce = NULL; + int status = c_avl_get(cache_tree, name, (void *)&ce); if (status != 0) /* entry does not yet exist */ { status = uc_insert(ds, vl, name); pthread_mutex_unlock(&cache_lock); + + if (status == 0) + plugin_dispatch_cache_event(CE_VALUE_NEW, 0 /* mask */, name, vl); + return status; } @@ -403,11 +415,32 @@ int uc_update(const data_set_t *ds, const value_list_t *vl) { ce->last_update = cdtime(); ce->interval = vl->interval; + /* Check if cache entry has registered callbacks */ + unsigned long callbacks_mask = ce->callbacks_mask; + pthread_mutex_unlock(&cache_lock); + if (callbacks_mask) + plugin_dispatch_cache_event(CE_VALUE_UPDATE, callbacks_mask, name, vl); + return 0; } /* int uc_update */ +int uc_set_callbacks_mask(const char *name, unsigned long mask) { + pthread_mutex_lock(&cache_lock); + cache_entry_t *ce = NULL; + int status = c_avl_get(cache_tree, name, (void *)&ce); + if (status != 0) { /* Ouch, just created entry disappeared ?! */ + ERROR("uc_set_callbacks_mask: Couldn't find %s entry!", name); + pthread_mutex_unlock(&cache_lock); + return -1; + } + DEBUG("uc_set_callbacks_mask: set mask for \"%s\" to %lu.", name, mask); + ce->callbacks_mask = mask; + pthread_mutex_unlock(&cache_lock); + return 0; +} + int uc_get_rate_by_name(const char *name, gauge_t **ret_values, size_t *ret_values_num) { gauge_t *ret = NULL; @@ -826,9 +859,7 @@ int uc_inc_hits(const data_set_t *ds, const value_list_t *vl, int step) { * Iterator interface */ uc_iter_t *uc_get_iterator(void) { - uc_iter_t *iter; - - iter = (uc_iter_t *)calloc(1, sizeof(*iter)); + uc_iter_t *iter = calloc(1, sizeof(*iter)); if (iter == NULL) return NULL; @@ -891,13 +922,12 @@ int uc_iterator_get_values(uc_iter_t *iter, value_t **ret_values, if ((iter == NULL) || (iter->entry == NULL) || (ret_values == NULL) || (ret_num == NULL)) return -1; - *ret_values = calloc(iter->entry->values_num, sizeof(*iter->entry->values_raw)); if (*ret_values == NULL) return -1; for (size_t i = 0; i < iter->entry->values_num; ++i) - *ret_values[i] = iter->entry->values_raw[i]; + (*ret_values)[i] = iter->entry->values_raw[i]; *ret_num = iter->entry->values_num; diff --git a/src/daemon/utils_cache.h b/src/daemon/utils_cache.h index d3ea9362..a0692216 100644 --- a/src/daemon/utils_cache.h +++ b/src/daemon/utils_cache.h @@ -56,6 +56,8 @@ int uc_get_hits(const data_set_t *ds, const value_list_t *vl); int uc_set_hits(const data_set_t *ds, const value_list_t *vl, int hits); int uc_inc_hits(const data_set_t *ds, const value_list_t *vl, int step); +int uc_set_callbacks_mask(const char *name, unsigned long callbacks_mask); + int uc_get_history(const data_set_t *ds, const value_list_t *vl, gauge_t *ret_history, size_t num_steps, size_t num_ds); int uc_get_history_by_name(const char *name, gauge_t *ret_history, diff --git a/src/daemon/utils_heap.c b/src/daemon/utils_heap.c deleted file mode 100644 index 3cecd893..00000000 --- a/src/daemon/utils_heap.c +++ /dev/null @@ -1,207 +0,0 @@ -/** - * collectd - src/utils_heap.c - * Copyright (C) 2009 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include -#include -#include -#include - -#include "utils_heap.h" - -struct c_heap_s { - pthread_mutex_t lock; - int (*compare)(const void *, const void *); - - void **list; - size_t list_len; /* # entries used */ - size_t list_size; /* # entries allocated */ -}; - -enum reheap_direction { DIR_UP, DIR_DOWN }; - -static void reheap(c_heap_t *h, size_t root, enum reheap_direction dir) { - size_t left; - size_t right; - size_t min; - int status; - - /* Calculate the positions of the children */ - left = (2 * root) + 1; - if (left >= h->list_len) - left = 0; - - right = (2 * root) + 2; - if (right >= h->list_len) - right = 0; - - /* Check which one of the children is smaller. */ - if ((left == 0) && (right == 0)) - return; - else if (left == 0) - min = right; - else if (right == 0) - min = left; - else { - status = h->compare(h->list[left], h->list[right]); - if (status > 0) - min = right; - else - min = left; - } - - status = h->compare(h->list[root], h->list[min]); - if (status <= 0) { - /* We didn't need to change anything, so the rest of the tree should be - * okay now. */ - return; - } else /* if (status > 0) */ - { - void *tmp; - - tmp = h->list[root]; - h->list[root] = h->list[min]; - h->list[min] = tmp; - } - - if ((dir == DIR_UP) && (root == 0)) - return; - - if (dir == DIR_UP) - reheap(h, (root - 1) / 2, dir); - else if (dir == DIR_DOWN) - reheap(h, min, dir); -} /* void reheap */ - -c_heap_t *c_heap_create(int (*compare)(const void *, const void *)) { - c_heap_t *h; - - if (compare == NULL) - return NULL; - - h = calloc(1, sizeof(*h)); - if (h == NULL) - return NULL; - - pthread_mutex_init(&h->lock, /* attr = */ NULL); - h->compare = compare; - - h->list = NULL; - h->list_len = 0; - h->list_size = 0; - - return h; -} /* c_heap_t *c_heap_create */ - -void c_heap_destroy(c_heap_t *h) { - if (h == NULL) - return; - - h->list_len = 0; - h->list_size = 0; - free(h->list); - h->list = NULL; - - pthread_mutex_destroy(&h->lock); - - free(h); -} /* void c_heap_destroy */ - -int c_heap_insert(c_heap_t *h, void *ptr) { - size_t index; - - if ((h == NULL) || (ptr == NULL)) - return -EINVAL; - - pthread_mutex_lock(&h->lock); - - assert(h->list_len <= h->list_size); - if (h->list_len == h->list_size) { - void **tmp; - - tmp = realloc(h->list, (h->list_size + 16) * sizeof(*h->list)); - if (tmp == NULL) { - pthread_mutex_unlock(&h->lock); - return -ENOMEM; - } - - h->list = tmp; - h->list_size += 16; - } - - /* Insert the new node as a leaf. */ - index = h->list_len; - h->list[index] = ptr; - h->list_len++; - - /* Reorganize the heap from bottom up. */ - reheap(h, /* parent of this node = */ (index - 1) / 2, DIR_UP); - - pthread_mutex_unlock(&h->lock); - return 0; -} /* int c_heap_insert */ - -void *c_heap_get_root(c_heap_t *h) { - void *ret = NULL; - - if (h == NULL) - return NULL; - - pthread_mutex_lock(&h->lock); - - if (h->list_len == 0) { - pthread_mutex_unlock(&h->lock); - return NULL; - } else if (h->list_len == 1) { - ret = h->list[0]; - h->list[0] = NULL; - h->list_len = 0; - } else /* if (h->list_len > 1) */ - { - ret = h->list[0]; - h->list[0] = h->list[h->list_len - 1]; - h->list[h->list_len - 1] = NULL; - h->list_len--; - - reheap(h, /* root = */ 0, DIR_DOWN); - } - - /* free some memory */ - if ((h->list_len + 32) < h->list_size) { - void **tmp; - - tmp = realloc(h->list, (h->list_len + 16) * sizeof(*h->list)); - if (tmp != NULL) { - h->list = tmp; - h->list_size = h->list_len + 16; - } - } - - pthread_mutex_unlock(&h->lock); - - return ret; -} /* void *c_heap_get_root */ diff --git a/src/daemon/utils_heap.h b/src/daemon/utils_heap.h deleted file mode 100644 index d2d70cdc..00000000 --- a/src/daemon/utils_heap.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * collectd - src/utils_heap.h - * Copyright (C) 2009 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_HEAP_H -#define UTILS_HEAP_H 1 - -struct c_heap_s; -typedef struct c_heap_s c_heap_t; - -/* - * NAME - * c_heap_create - * - * DESCRIPTION - * Allocates a new heap. - * - * PARAMETERS - * `compare' The function-pointer `compare' is used to compare two keys. It - * has to return less than zero if its first argument is smaller - * then the second argument, more than zero if the first argument - * is bigger than the second argument and zero if they are equal. - * If your keys are char-pointers, you can use the `strcmp' - * function from the libc here. - * - * RETURN VALUE - * A c_heap_t-pointer upon success or NULL upon failure. - */ -c_heap_t *c_heap_create(int (*compare)(const void *, const void *)); - -/* - * NAME - * c_heap_destroy - * - * DESCRIPTION - * Deallocates a heap. Stored value- and key-pointer are lost, but of course - * not freed. - */ -void c_heap_destroy(c_heap_t *h); - -/* - * NAME - * c_heap_insert - * - * DESCRIPTION - * Stores the key-value-pair in the heap pointed to by `h'. - * - * PARAMETERS - * `h' Heap to store the data in. - * `ptr' Value to be stored. This is typically a pointer to a data - * structure. The data structure is of course *not* copied and may - * not be free'd before the pointer has been removed from the heap - * again. - * - * RETURN VALUE - * Zero upon success, non-zero otherwise. It's less than zero if an error - * occurred or greater than zero if the key is already stored in the tree. - */ -int c_heap_insert(c_heap_t *h, void *ptr); - -/* - * NAME - * c_heap_get_root - * - * DESCRIPTION - * Removes the value at the root of the heap and returns both, key and value. - * - * PARAMETERS - * `h' Heap to remove key-value-pair from. - * - * RETURN VALUE - * The pointer passed to `c_heap_insert' or NULL if there are no more - * elements in the heap (or an error occurred). - */ -void *c_heap_get_root(c_heap_t *h); - -#endif /* UTILS_HEAP_H */ diff --git a/src/daemon/utils_heap_test.c b/src/daemon/utils_heap_test.c deleted file mode 100644 index 827c090f..00000000 --- a/src/daemon/utils_heap_test.c +++ /dev/null @@ -1,78 +0,0 @@ -/** - * collectd - src/tests/test_utils_heap.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "collectd.h" - -#include "testing.h" -#include "utils_heap.h" - -static int compare(void const *v0, void const *v1) { - int const *i0 = v0; - int const *i1 = v1; - - if ((*i0) < (*i1)) - return -1; - else if ((*i0) > (*i1)) - return 1; - else - return 0; -} - -DEF_TEST(simple) { - int values[] = {9, 5, 6, 1, 3, 4, 0, 8, 2, 7}; - c_heap_t *h; - - CHECK_NOT_NULL(h = c_heap_create(compare)); - for (int i = 0; i < 10; i++) - CHECK_ZERO(c_heap_insert(h, &values[i])); - - for (int i = 0; i < 5; i++) { - int *ret = NULL; - CHECK_NOT_NULL(ret = c_heap_get_root(h)); - OK(*ret == i); - } - - CHECK_ZERO(c_heap_insert(h, &values[6] /* = 0 */)); - CHECK_ZERO(c_heap_insert(h, &values[3] /* = 1 */)); - CHECK_ZERO(c_heap_insert(h, &values[8] /* = 2 */)); - CHECK_ZERO(c_heap_insert(h, &values[4] /* = 3 */)); - CHECK_ZERO(c_heap_insert(h, &values[5] /* = 4 */)); - - for (int i = 0; i < 10; i++) { - int *ret = NULL; - CHECK_NOT_NULL(ret = c_heap_get_root(h)); - OK(*ret == i); - } - - c_heap_destroy(h); - return 0; -} - -int main(void) { - RUN_TEST(simple); - - END_TEST; -} diff --git a/src/daemon/utils_random.h b/src/daemon/utils_random.h index 75bdc12e..ff0022eb 100644 --- a/src/daemon/utils_random.h +++ b/src/daemon/utils_random.h @@ -24,6 +24,9 @@ * Florian Forster **/ +#ifndef UTILS_RANDOM_H +#define UTILS_RANDOM_H 1 + /** * Returns a random double value in the range [0..1), i.e. excluding 1. * @@ -46,3 +49,5 @@ uint32_t cdrand_u(void); * outside the intended range. This function is thread- and reentrant-safe. */ long cdrand_range(long min, long max); + +#endif /* !UTILS_RANDOM_H */ diff --git a/src/daemon/utils_subst.c b/src/daemon/utils_subst.c index 28924e44..0b5f00d1 100644 --- a/src/daemon/utils_subst.c +++ b/src/daemon/utils_subst.c @@ -30,7 +30,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "utils_subst.h" char *subst(char *buf, size_t buflen, const char *string, size_t off1, diff --git a/src/daemon/utils_subst_test.c b/src/daemon/utils_subst_test.c index 20560966..0e582306 100644 --- a/src/daemon/utils_subst_test.c +++ b/src/daemon/utils_subst_test.c @@ -25,7 +25,7 @@ */ #include "collectd.h" -#include "common.h" /* for STATIC_ARRAY_SIZE */ +#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */ #include "testing.h" #include "utils_subst.h" diff --git a/src/daemon/utils_threshold.c b/src/daemon/utils_threshold.c index 8c033411..52af648f 100644 --- a/src/daemon/utils_threshold.c +++ b/src/daemon/utils_threshold.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" #include "utils_threshold.h" #include diff --git a/src/daemon/utils_time.c b/src/daemon/utils_time.c index 4637122e..5dd7d0e1 100644 --- a/src/daemon/utils_time.c +++ b/src/daemon/utils_time.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_time.h" #ifndef DEFAULT_MOCK_TIME diff --git a/src/dbi.c b/src/dbi.c index 899c802c..8466bd11 100644 --- a/src/dbi.c +++ b/src/dbi.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_db_query.h" +#include "utils/common/common.h" +#include "utils/db_query/db_query.h" #include diff --git a/src/df.c b/src/df.c index 8877b740..5b3fbd28 100644 --- a/src/df.c +++ b/src/df.c @@ -23,10 +23,10 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" -#include "utils_mount.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" +#include "utils/mount/mount.h" #if HAVE_STATVFS #if HAVE_SYS_STATVFS_H diff --git a/src/disk.c b/src/disk.c index 7edf44c7..c02b6aba 100644 --- a/src/disk.c +++ b/src/disk.c @@ -23,9 +23,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #if HAVE_MACH_MACH_TYPES_H #include @@ -59,9 +59,6 @@ #include #endif -#if HAVE_LIMITS_H -#include -#endif #ifndef UINT_MAX #define UINT_MAX 4294967295U #endif @@ -209,7 +206,7 @@ static int disk_init(void) { io_master_port = MACH_PORT_NULL; return -1; } -/* #endif HAVE_IOKIT_IOKITLIB_H */ + /* #endif HAVE_IOKIT_IOKITLIB_H */ #elif KERNEL_LINUX #if HAVE_LIBUDEV_H @@ -221,7 +218,7 @@ static int disk_init(void) { } } #endif /* HAVE_LIBUDEV_H */ -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif KERNEL_FREEBSD int rv; @@ -236,7 +233,7 @@ static int disk_init(void) { ERROR("geom_stats_open() failed, returned %d", rv); return -1; } -/* #endif KERNEL_FREEBSD */ + /* #endif KERNEL_FREEBSD */ #elif HAVE_LIBKSTAT kstat_t *ksp_chain; @@ -275,7 +272,8 @@ static void disk_submit(const char *plugin_instance, const char *type, derive_t read, derive_t write) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = read}, {.derive = write}, + {.derive = read}, + {.derive = write}, }; vl.values = values; @@ -292,7 +290,8 @@ static void submit_io_time(char const *plugin_instance, derive_t io_time, derive_t weighted_time) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = io_time}, {.derive = weighted_time}, + {.derive = io_time}, + {.derive = weighted_time}, }; vl.values = values; @@ -493,10 +492,11 @@ static int disk_read(void) { sstrncpy(disk_name, props_disk_name_bsd, sizeof(disk_name)); else { ERROR("disk plugin: can't find bsd disk name."); - snprintf(disk_name, sizeof(disk_name), "%i-%i", disk_major, disk_minor); + ssnprintf(disk_name, sizeof(disk_name), "%i-%i", disk_major, + disk_minor); } } else - snprintf(disk_name, sizeof(disk_name), "%i-%i", disk_major, disk_minor); + ssnprintf(disk_name, sizeof(disk_name), "%i-%i", disk_major, disk_minor); DEBUG("disk plugin: disk_name = \"%s\"", disk_name); @@ -532,7 +532,7 @@ static int disk_read(void) { disk_submit(disk_name, "disk_time", read_tme / 1000, write_tme / 1000); } IOObjectRelease(disk_list); -/* #endif HAVE_IOKIT_IOKITLIB_H */ + /* #endif HAVE_IOKIT_IOKITLIB_H */ #elif KERNEL_FREEBSD int retry, dirty; @@ -701,7 +701,7 @@ static int disk_read(void) { break; if (ds == NULL) { - if ((ds = (diskstats_t *)calloc(1, sizeof(diskstats_t))) == NULL) + if ((ds = calloc(1, sizeof(*ds))) == NULL) continue; if ((ds->name = strdup(disk_name)) == NULL) { @@ -896,7 +896,7 @@ static int disk_read(void) { free(missing_ds); } fclose(fh); -/* #endif defined(KERNEL_LINUX) */ + /* #endif defined(KERNEL_LINUX) */ #elif HAVE_LIBKSTAT #if HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_NWRITES && HAVE_KSTAT_IO_T_WTIME @@ -944,7 +944,7 @@ static int disk_read(void) { disk_submit(ksp[i]->ks_name, "disk_ops", kio.KIO_ROPS, kio.KIO_WOPS); } } -/* #endif defined(HAVE_LIBKSTAT) */ + /* #endif defined(HAVE_LIBKSTAT) */ #elif defined(HAVE_LIBSTATGRAB) sg_disk_io_stats *ds; @@ -971,7 +971,7 @@ static int disk_read(void) { disk_submit(name, "disk_octets", ds->read_bytes, ds->write_bytes); ds++; } -/* #endif defined(HAVE_LIBSTATGRAB) */ + /* #endif defined(HAVE_LIBSTATGRAB) */ #elif defined(HAVE_PERFSTAT) derive_t read_sectors; @@ -989,9 +989,8 @@ static int disk_read(void) { } if (numdisk != pnumdisk || stat_disk == NULL) { - if (stat_disk != NULL) - free(stat_disk); - stat_disk = (perfstat_disk_t *)calloc(numdisk, sizeof(perfstat_disk_t)); + free(stat_disk); + stat_disk = calloc(numdisk, sizeof(*stat_disk)); } pnumdisk = numdisk; diff --git a/src/dns.c b/src/dns.c index bd6820fa..dad0be25 100644 --- a/src/dns.c +++ b/src/dns.c @@ -26,10 +26,10 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" -#include "utils_dns.h" +#include "utils/dns/dns.h" #include #include @@ -324,7 +324,8 @@ static void submit_derive(const char *type, const char *type_instance, static void submit_octets(derive_t queries, derive_t responses) { value_t values[] = { - {.derive = queries}, {.derive = responses}, + {.derive = queries}, + {.derive = responses}, }; value_list_t vl = VALUE_LIST_INIT; diff --git a/src/dpdkevents.c b/src/dpdkevents.c index 2a44b2c1..4c8196ae 100644 --- a/src/dpdkevents.c +++ b/src/dpdkevents.c @@ -32,12 +32,12 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "semaphore.h" #include "sys/mman.h" -#include "utils_dpdk.h" +#include "utils/dpdk/dpdk.h" #include "utils_time.h" #include @@ -511,17 +511,17 @@ static int dpdk_events_link_status_dispatch(dpdk_helper_ctx_t *phc) { char dev_name[DATA_MAX_NAME_LEN]; if (ec->config.link_status.port_name[i][0] != 0) { - snprintf(dev_name, sizeof(dev_name), "%s", - ec->config.link_status.port_name[i]); + ssnprintf(dev_name, sizeof(dev_name), "%s", + ec->config.link_status.port_name[i]); } else { - snprintf(dev_name, sizeof(dev_name), "port.%d", i); + ssnprintf(dev_name, sizeof(dev_name), "port.%d", i); } if (ec->config.link_status.notify) { int sev = ec->link_info[i].link_status ? NOTIF_OKAY : NOTIF_WARNING; char msg[DATA_MAX_NAME_LEN]; - snprintf(msg, sizeof(msg), "Link Status: %s", - ec->link_info[i].link_status ? "UP" : "DOWN"); + ssnprintf(msg, sizeof(msg), "Link Status: %s", + ec->link_info[i].link_status ? "UP" : "DOWN"); dpdk_events_notification_dispatch(sev, dev_name, ec->link_info[i].read_time, msg); } else { @@ -557,7 +557,7 @@ static void dpdk_events_keep_alive_dispatch(dpdk_helper_ctx_t *phc) { } char core_name[DATA_MAX_NAME_LEN]; - snprintf(core_name, sizeof(core_name), "lcore%u", i); + ssnprintf(core_name, sizeof(core_name), "lcore%u", i); if (!ec->config.keep_alive.send_updated || (ec->core_info[i].lcore_state != @@ -572,34 +572,34 @@ static void dpdk_events_keep_alive_dispatch(dpdk_helper_ctx_t *phc) { switch (ec->config.keep_alive.shm->core_state[i]) { case RTE_KA_STATE_ALIVE: sev = NOTIF_OKAY; - snprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: ALIVE", i); + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: ALIVE", i); break; case RTE_KA_STATE_MISSING: - snprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: MISSING", i); + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: MISSING", i); sev = NOTIF_WARNING; break; case RTE_KA_STATE_DEAD: - snprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: DEAD", i); + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: DEAD", i); sev = NOTIF_FAILURE; break; case RTE_KA_STATE_UNUSED: - snprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: UNUSED", i); + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: UNUSED", i); sev = NOTIF_OKAY; break; case RTE_KA_STATE_GONE: - snprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: GONE", i); + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: GONE", i); sev = NOTIF_FAILURE; break; case RTE_KA_STATE_DOZING: - snprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: DOZING", i); + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: DOZING", i); sev = NOTIF_OKAY; break; case RTE_KA_STATE_SLEEP: - snprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: SLEEP", i); + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: SLEEP", i); sev = NOTIF_OKAY; break; default: - snprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: UNKNOWN", i); + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: UNKNOWN", i); sev = NOTIF_FAILURE; } diff --git a/src/dpdkstat.c b/src/dpdkstat.c index 59ab9760..ae93e53b 100644 --- a/src/dpdkstat.c +++ b/src/dpdkstat.c @@ -32,8 +32,8 @@ #include "collectd.h" -#include "common.h" -#include "utils_dpdk.h" +#include "utils/common/common.h" +#include "utils/dpdk/dpdk.h" #include #include @@ -395,9 +395,9 @@ static int dpdk_stats_counters_dispatch(dpdk_helper_ctx_t *phc) { char dev_name[64]; if (ctx->config.port_name[i][0] != 0) { - snprintf(dev_name, sizeof(dev_name), "%s", ctx->config.port_name[i]); + ssnprintf(dev_name, sizeof(dev_name), "%s", ctx->config.port_name[i]); } else { - snprintf(dev_name, sizeof(dev_name), "port.%d", i); + ssnprintf(dev_name, sizeof(dev_name), "port.%d", i); } DEBUG(" === Dispatch stats for port %d (name=%s; stats_count=%d)", i, diff --git a/src/drbd.c b/src/drbd.c index 69dc4ef9..0f54dd5e 100644 --- a/src/drbd.c +++ b/src/drbd.c @@ -36,8 +36,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" static const char *drbd_stats = "/proc/drbd"; static const char *drbd_names[] = { diff --git a/src/email.c b/src/email.c index f8a94fb6..53602053 100644 --- a/src/email.c +++ b/src/email.c @@ -40,8 +40,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -174,16 +174,18 @@ static int email_config(const char *key, const char *value) { long int tmp = strtol(value, NULL, 0); if (tmp < 1) { - fprintf(stderr, "email plugin: `MaxConns' was set to invalid " - "value %li, will use default %i.\n", + fprintf(stderr, + "email plugin: `MaxConns' was set to invalid " + "value %li, will use default %i.\n", tmp, MAX_CONNS); ERROR("email plugin: `MaxConns' was set to invalid " "value %li, will use default %i.\n", tmp, MAX_CONNS); max_conns = MAX_CONNS; } else if (tmp > MAX_CONNS_LIMIT) { - fprintf(stderr, "email plugin: `MaxConns' was set to invalid " - "value %li, will use hardcoded limit %i.\n", + fprintf(stderr, + "email plugin: `MaxConns' was set to invalid " + "value %li, will use hardcoded limit %i.\n", tmp, MAX_CONNS_LIMIT); ERROR("email plugin: `MaxConns' was set to invalid " "value %li, will use hardcoded limit %i.\n", diff --git a/src/entropy.c b/src/entropy.c index c7b5b3f3..94a291ec 100644 --- a/src/entropy.c +++ b/src/entropy.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !KERNEL_LINUX #error "No applicable input method." diff --git a/src/ethstat.c b/src/ethstat.c index 0d4c7e15..f8bc5b54 100644 --- a/src/ethstat.c +++ b/src/ethstat.c @@ -24,9 +24,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" #include "utils_complain.h" #if HAVE_SYS_IOCTL_H diff --git a/src/exec.c b/src/exec.c index 26b8fa7e..9574f2c4 100644 --- a/src/exec.c +++ b/src/exec.c @@ -28,11 +28,11 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" -#include "utils_cmd_putnotif.h" -#include "utils_cmd_putval.h" +#include "utils/cmds/putnotif.h" +#include "utils/cmds/putval.h" #include #include @@ -754,8 +754,9 @@ static void *exec_notification_one(void *arg) /* {{{ */ else if (n->severity == NOTIF_OKAY) severity = "OKAY"; - fprintf(fh, "Severity: %s\n" - "Time: %.3f\n", + fprintf(fh, + "Severity: %s\n" + "Time: %.3f\n", severity, CDTIME_T_TO_DOUBLE(n->time)); /* Print the optional fields */ diff --git a/src/fhcount.c b/src/fhcount.c index 9bcb9115..93ad903e 100644 --- a/src/fhcount.c +++ b/src/fhcount.c @@ -19,8 +19,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" static const char *config_keys[] = {"ValuesAbsolute", "ValuesPercentage"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); diff --git a/src/filecount.c b/src/filecount.c index 9091ff55..5acd47b3 100644 --- a/src/filecount.c +++ b/src/filecount.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/fscache.c b/src/fscache.c index dd36b8b9..e875da8d 100644 --- a/src/fscache.c +++ b/src/fscache.c @@ -21,11 +21,11 @@ #include "collectd.h" +#include "plugin.h" +#include "utils/common/common.h" #include /* a header needed for FILE */ #include /* used for atoi */ #include /* a header needed for scanf function */ -#include "common.h" -#include "plugin.h" #if !KERNEL_LINUX #error "This module only supports the Linux implementation of fscache" diff --git a/src/gmond.c b/src/gmond.c index 291dfadb..03dedcca 100644 --- a/src/gmond.c +++ b/src/gmond.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" #if HAVE_NETDB_H #include @@ -397,8 +397,8 @@ static staging_entry_t *staging_entry_get(const char *host, /* {{{ */ if (staging_tree == NULL) return NULL; - snprintf(key, sizeof(key), "%s/%s/%s", host, type, - (type_instance != NULL) ? type_instance : ""); + ssnprintf(key, sizeof(key), "%s/%s/%s", host, type, + (type_instance != NULL) ? type_instance : ""); se = NULL; status = c_avl_get(staging_tree, key, (void *)&se); @@ -413,7 +413,7 @@ static staging_entry_t *staging_entry_get(const char *host, /* {{{ */ sstrncpy(se->key, key, sizeof(se->key)); se->flags = 0; - se->vl.values = (value_t *)calloc(values_len, sizeof(*se->vl.values)); + se->vl.values = calloc(values_len, sizeof(*se->vl.values)); if (se->vl.values == NULL) { sfree(se); return NULL; @@ -743,8 +743,8 @@ static void *mc_receive_thread(void *arg) /* {{{ */ return (void *)-1; } - mc_receive_sockets = (struct pollfd *)calloc(mc_receive_sockets_num, - sizeof(*mc_receive_sockets)); + mc_receive_sockets = + calloc(mc_receive_sockets_num, sizeof(*mc_receive_sockets)); if (mc_receive_sockets == NULL) { ERROR("gmond plugin: calloc failed."); for (size_t i = 0; i < mc_receive_sockets_num; i++) diff --git a/src/gps.c b/src/gps.c index b22c3a2e..4d651768 100644 --- a/src/gps.c +++ b/src/gps.c @@ -27,8 +27,8 @@ **/ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_time.h" #define CGPS_TRUE 1 diff --git a/src/gpu_nvidia.c b/src/gpu_nvidia.c index 812cfeb0..f176795b 100644 --- a/src/gpu_nvidia.c +++ b/src/gpu_nvidia.c @@ -21,8 +21,8 @@ SOFTWARE. */ #include "daemon/collectd.h" -#include "daemon/common.h" #include "daemon/plugin.h" +#include "utils/common/common.h" #include #include @@ -54,7 +54,8 @@ static char *nv_errline = ""; #define KEY_IGNORESELECTED "IgnoreSelected" static const char *config_keys[] = { - KEY_GPUINDEX, KEY_IGNORESELECTED, + KEY_GPUINDEX, + KEY_IGNORESELECTED, }; static const unsigned int n_config_keys = STATIC_ARRAY_SIZE(config_keys); diff --git a/src/grpc.cc b/src/grpc.cc index 17168ec2..d9577a46 100644 --- a/src/grpc.cc +++ b/src/grpc.cc @@ -41,8 +41,8 @@ extern "C" { #include #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "daemon/utils_cache.h" } @@ -747,7 +747,8 @@ static int c_grpc_config_server(oconfig_item_t *ci) { auto callback_name = grpc::string("grpc/") + addr; user_data_t ud = { - .data = client, .free_func = c_grpc_destroy_write_callback, + .data = client, + .free_func = c_grpc_destroy_write_callback, }; plugin_register_write(callback_name.c_str(), c_grpc_write, &ud); diff --git a/src/hddtemp.c b/src/hddtemp.c index 80daf15b..96b4f0c5 100644 --- a/src/hddtemp.c +++ b/src/hddtemp.c @@ -33,8 +33,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include /* for basename */ diff --git a/src/hugepages.c b/src/hugepages.c index dd897356..e066300b 100644 --- a/src/hugepages.c +++ b/src/hugepages.c @@ -30,8 +30,8 @@ #include "collectd.h" -#include "common.h" /* auxiliary functions */ -#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */ +#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */ +#include "utils/common/common.h" /* auxiliary functions */ static const char g_plugin_name[] = "hugepages"; diff --git a/src/intel_pmu.c b/src/intel_pmu.c index c9bbb50c..f04f8871 100644 --- a/src/intel_pmu.c +++ b/src/intel_pmu.c @@ -27,9 +27,9 @@ **/ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" -#include "utils_config_cores.h" +#include "utils/config_cores/config_cores.h" #include #include @@ -267,7 +267,7 @@ static int pmu_config_hw_events(oconfig_item_t *ci) { return -EINVAL; } - g_ctx.hw_events = calloc(ci->values_num, sizeof(char *)); + g_ctx.hw_events = calloc(ci->values_num, sizeof(*g_ctx.hw_events)); if (g_ctx.hw_events == NULL) { ERROR(PMU_PLUGIN ": Failed to allocate hw events."); return -ENOMEM; @@ -446,7 +446,7 @@ static int pmu_add_events(struct eventlist *el, uint32_t type, /* Allocate memory for event struct that contains array of efd structs for all cores */ struct event *e = - calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1); + calloc(1, sizeof(struct event) + sizeof(struct efd) * el->num_cpus); if (e == NULL) { ERROR(PMU_PLUGIN ": Failed to allocate event structure"); return -ENOMEM; @@ -482,7 +482,7 @@ static int pmu_add_hw_events(struct eventlist *el, char **e, size_t count) { /* Allocate memory for event struct that contains array of efd structs for all cores */ struct event *e = - calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1); + calloc(1, sizeof(struct event) + sizeof(struct efd) * el->num_cpus); if (e == NULL) { free(events); return -ENOMEM; diff --git a/src/intel_rdt.c b/src/intel_rdt.c index 62ce9b80..99c3ed0d 100644 --- a/src/intel_rdt.c +++ b/src/intel_rdt.c @@ -1,7 +1,7 @@ /** * collectd - src/intel_rdt.c * - * Copyright(c) 2016-2018 Intel Corporation. All rights reserved. + * Copyright(c) 2016-2019 Intel Corporation. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,30 +23,68 @@ * * Authors: * Serhiy Pshyk + * Starzyk, Mateusz + * Wojciech Andralojc + * Michał Aleksiński **/ #include "collectd.h" -#include "common.h" -#include "utils_config_cores.h" - +#include "utils/common/common.h" +#include "utils/config_cores/config_cores.h" +#include "utils/proc_pids/proc_pids.h" #include #define RDT_PLUGIN "intel_rdt" +/* libpqos v2.0 or newer is required for process monitoring*/ +#undef LIBPQOS2 +#if defined(PQOS_VERSION) && PQOS_VERSION >= 20000 +#define LIBPQOS2 +#endif + +#define RDT_PLUGIN "intel_rdt" + #define RDT_MAX_SOCKETS 8 #define RDT_MAX_SOCKET_CORES 64 #define RDT_MAX_CORES (RDT_MAX_SOCKET_CORES * RDT_MAX_SOCKETS) +#ifdef LIBPQOS2 +/* + * Process name inside comm file is limited to 16 chars. + * More info here: http://man7.org/linux/man-pages/man5/proc.5.html + */ +#define RDT_MAX_NAMES_GROUPS 64 +#define RDT_PROC_PATH "/proc" +#endif /* LIBPQOS2 */ + typedef enum { UNKNOWN = 0, CONFIGURATION_ERROR, } rdt_config_status; +#ifdef LIBPQOS2 +struct rdt_name_group_s { + char *desc; + size_t num_names; + char **names; + proc_pids_t **proc_pids; + size_t monitored_pids_count; + enum pqos_mon_event events; +}; +typedef struct rdt_name_group_s rdt_name_group_t; +#endif /* LIBPQOS2 */ + struct rdt_ctx_s { core_groups_list_t cores; enum pqos_mon_event events[RDT_MAX_CORES]; - struct pqos_mon_data *pgroups[RDT_MAX_CORES]; - size_t num_groups; + struct pqos_mon_data *pcgroups[RDT_MAX_CORES]; +#ifdef LIBPQOS2 + rdt_name_group_t ngroups[RDT_MAX_NAMES_GROUPS]; + struct pqos_mon_data *pngroups[RDT_MAX_NAMES_GROUPS]; + size_t num_ngroups; + proc_pids_t **proc_pids; + size_t num_proc_pids; +#endif /* LIBPQOS2 */ const struct pqos_cpuinfo *pqos_cpu; const struct pqos_cap *pqos_cap; const struct pqos_capability *cap_mon; @@ -57,6 +95,40 @@ static rdt_ctx_t *g_rdt; static rdt_config_status g_state = UNKNOWN; +static int g_interface = -1; + +static void rdt_submit_derive(const char *cgroup, const char *type, + const char *type_instance, derive_t value) { + value_list_t vl = VALUE_LIST_INIT; + + vl.values = &(value_t){.derive = value}; + vl.values_len = 1; + + sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin)); + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup); + sstrncpy(vl.type, type, sizeof(vl.type)); + if (type_instance) + sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); + + plugin_dispatch_values(&vl); +} + +static void rdt_submit_gauge(const char *cgroup, const char *type, + const char *type_instance, gauge_t value) { + value_list_t vl = VALUE_LIST_INIT; + + vl.values = &(value_t){.gauge = value}; + vl.values_len = 1; + + sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin)); + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup); + sstrncpy(vl.type, type, sizeof(vl.type)); + if (type_instance) + sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); + + plugin_dispatch_values(&vl); +} + #if COLLECT_DEBUG static void rdt_dump_cgroups(void) { char cores[RDT_MAX_CORES * 4]; @@ -65,15 +137,15 @@ static void rdt_dump_cgroups(void) { return; DEBUG(RDT_PLUGIN ": Core Groups Dump"); - DEBUG(RDT_PLUGIN ": groups count: %" PRIsz, g_rdt->num_groups); + DEBUG(RDT_PLUGIN ": groups count: %" PRIsz, g_rdt->cores.num_cgroups); - for (size_t i = 0; i < g_rdt->num_groups; i++) { + for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) { core_group_t *cgroup = g_rdt->cores.cgroups + i; memset(cores, 0, sizeof(cores)); - for (int j = 0; j < cgroup->num_cores; j++) { - snprintf(cores + strlen(cores), sizeof(cores) - strlen(cores) - 1, " %d", - cgroup->cores[j]); + for (size_t j = 0; j < cgroup->num_cores; j++) { + ssnprintf(cores + strlen(cores), sizeof(cores) - strlen(cores) - 1, " %d", + cgroup->cores[j]); } DEBUG(RDT_PLUGIN ": group[%zu]:", i); @@ -85,40 +157,744 @@ static void rdt_dump_cgroups(void) { return; } +#ifdef LIBPQOS2 +static void rdt_dump_ngroups(void) { + + char names[DATA_MAX_NAME_LEN]; + + if (g_rdt == NULL) + return; + + DEBUG(RDT_PLUGIN ": Process Names Groups Dump"); + DEBUG(RDT_PLUGIN ": groups count: %" PRIsz, g_rdt->num_ngroups); + + for (size_t i = 0; i < g_rdt->num_ngroups; i++) { + memset(names, 0, sizeof(names)); + for (size_t j = 0; j < g_rdt->ngroups[i].num_names; j++) + ssnprintf(names + strlen(names), sizeof(names) - strlen(names) - 1, " %s", + g_rdt->ngroups[i].names[j]); + + DEBUG(RDT_PLUGIN ": group[%d]:", (int)i); + DEBUG(RDT_PLUGIN ": description: %s", g_rdt->ngroups[i].desc); + DEBUG(RDT_PLUGIN ": process names:%s", names); + DEBUG(RDT_PLUGIN ": events: 0x%X", g_rdt->ngroups[i].events); + } + + return; +} +#endif /* LIBPQOS2 */ + static inline double bytes_to_kb(const double bytes) { return bytes / 1024.0; } static inline double bytes_to_mb(const double bytes) { return bytes / (1024.0 * 1024.0); } -static void rdt_dump_data(void) { +static void rdt_dump_cores_data(void) { +/* + * CORE - monitored group of cores + * RMID - Resource Monitoring ID associated with the monitored group + * This is not available for monitoring with resource control + * LLC - last level cache occupancy + * MBL - local memory bandwidth + * MBR - remote memory bandwidth + */ +#ifdef LIBPQOS2 + if (g_interface == PQOS_INTER_OS_RESCTRL_MON) { + DEBUG(RDT_PLUGIN ": CORE LLC[KB] MBL[MB] MBR[MB]"); + } else { + DEBUG(RDT_PLUGIN ": CORE RMID LLC[KB] MBL[MB] MBR[MB]"); + } +#else + DEBUG(RDT_PLUGIN ": CORE RMID LLC[KB] MBL[MB] MBR[MB]"); +#endif /* LIBPQOS2 */ + + for (int i = 0; i < g_rdt->cores.num_cgroups; i++) { + const struct pqos_event_values *pv = &g_rdt->pcgroups[i]->values; + + double llc = bytes_to_kb(pv->llc); + double mbr = bytes_to_mb(pv->mbm_remote_delta); + double mbl = bytes_to_mb(pv->mbm_local_delta); +#ifdef LIBPQOS2 + if (g_interface == PQOS_INTER_OS_RESCTRL_MON) { + DEBUG(RDT_PLUGIN ": [%s] %10.1f %10.1f %10.1f", + g_rdt->cores.cgroups[i].desc, llc, mbl, mbr); + } else { + DEBUG(RDT_PLUGIN ": [%s] %8u %10.1f %10.1f %10.1f", + g_rdt->cores.cgroups[i].desc, g_rdt->pcgroups[i]->poll_ctx[0].rmid, + llc, mbl, mbr); + } +#else + DEBUG(RDT_PLUGIN ": [%s] %8u %10.1f %10.1f %10.1f", + g_rdt->cores.cgroups[i].desc, g_rdt->pcgroups[i]->poll_ctx[0].rmid, + llc, mbl, mbr); +#endif /* LIBPQOS2 */ + } +} + +#ifdef LIBPQOS2 +static void rdt_dump_pids_data(void) { /* - * CORE - monitored group of cores - * RMID - Resource Monitoring ID associated with the monitored group + * NAME - monitored group of processes + * PIDs - list of PID numbers in the NAME group * LLC - last level cache occupancy * MBL - local memory bandwidth * MBR - remote memory bandwidth */ - DEBUG(" CORE RMID LLC[KB] MBL[MB] MBR[MB]"); - for (int i = 0; i < g_rdt->num_groups; i++) { - const struct pqos_event_values *pv = &g_rdt->pgroups[i]->values; + DEBUG(RDT_PLUGIN ": NAME PIDs"); + char pids[DATA_MAX_NAME_LEN]; + for (size_t i = 0; i < g_rdt->num_ngroups; ++i) { + memset(pids, 0, sizeof(pids)); + for (size_t j = 0; j < g_rdt->ngroups[i].num_names; ++j) { + pids_list_t *list = g_rdt->ngroups[i].proc_pids[j]->curr; + for (size_t k = 0; k < list->size; k++) + ssnprintf(pids + strlen(pids), sizeof(pids) - strlen(pids) - 1, " %u", + list->pids[k]); + } + DEBUG(RDT_PLUGIN ": [%s] %s", g_rdt->ngroups[i].desc, pids); + } + + DEBUG(RDT_PLUGIN ": NAME LLC[KB] MBL[MB] MBR[MB]"); + for (size_t i = 0; i < g_rdt->num_ngroups; i++) { + + const struct pqos_event_values *pv = &g_rdt->pngroups[i]->values; double llc = bytes_to_kb(pv->llc); double mbr = bytes_to_mb(pv->mbm_remote_delta); double mbl = bytes_to_mb(pv->mbm_local_delta); - DEBUG(" [%s] %8u %10.1f %10.1f %10.1f", g_rdt->cores.cgroups[i].desc, - g_rdt->pgroups[i]->poll_ctx[0].rmid, llc, mbl, mbr); + DEBUG(RDT_PLUGIN ": [%s] %10.1f %10.1f %10.1f", g_rdt->ngroups[i].desc, + llc, mbl, mbr); + } +} +#endif /* LIBPQOS2 */ +#endif /* COLLECT_DEBUG */ + +#ifdef LIBPQOS2 +static int isdupstr(const char *names[], const size_t size, const char *name) { + for (size_t i = 0; i < size; i++) + if (strncmp(names[i], name, (size_t)MAX_PROC_NAME_LEN) == 0) + return 1; + + return 0; +} + +/* + * NAME + * strlisttoarray + * + * DESCRIPTION + * Converts string representing list of strings into array of strings. + * Allowed format is: + * name,name1,name2,name3 + * + * PARAMETERS + * `str_list' String representing list of strings. + * `names' Array to put extracted strings into. + * `names_num' Variable to put number of extracted strings. + * + * RETURN VALUE + * Number of elements placed into names. + */ +static int strlisttoarray(char *str_list, char ***names, size_t *names_num) { + char *saveptr = NULL; + + if (str_list == NULL || names == NULL) + return -EINVAL; + + if (strstr(str_list, ",,")) { + /* strtok ignores empty words between separators. + * This condition handles that by rejecting strings + * with consecutive seprators */ + ERROR(RDT_PLUGIN ": Empty process name"); + return -EINVAL; + } + + for (;;) { + char *token = strtok_r(str_list, ",", &saveptr); + if (token == NULL) + break; + + str_list = NULL; + + while (isspace(*token)) + token++; + + if (*token == '\0') + continue; + + if ((isdupstr((const char **)*names, *names_num, token))) { + ERROR(RDT_PLUGIN ": Duplicated process name \'%s\'", token); + + return -EINVAL; + } else { + if (0 != strarray_add(names, names_num, token)) { + ERROR(RDT_PLUGIN ": Error allocating process name string"); + return -ENOMEM; + } + } + } + + return 0; +} + +/* + * NAME + * ngroup_cmp + * + * DESCRIPTION + * Function to compare names in two name groups. + * + * PARAMETERS + * `ng_a' Pointer to name group a. + * `ng_b' Pointer to name group b. + * + * RETURN VALUE + * 1 if both groups contain the same names + * 0 if none of their names match + * -1 if some but not all names match + */ +static int ngroup_cmp(const rdt_name_group_t *ng_a, + const rdt_name_group_t *ng_b) { + unsigned found = 0; + + assert(ng_a != NULL); + assert(ng_b != NULL); + + const size_t sz_a = (unsigned)ng_a->num_names; + const size_t sz_b = (unsigned)ng_b->num_names; + const char **tab_a = (const char **)ng_a->names; + const char **tab_b = (const char **)ng_b->names; + + for (size_t i = 0; i < sz_a; i++) { + for (size_t j = 0; j < sz_b; j++) + if (strncmp(tab_a[i], tab_b[j], (size_t)MAX_PROC_NAME_LEN) == 0) + found++; + } + /* if no names are the same */ + if (!found) + return 0; + /* if group contains same names */ + if (sz_a == sz_b && sz_b == (size_t)found) + return 1; + /* if not all names are the same */ + return -1; +} + +/* + * NAME + * oconfig_to_ngroups + * + * DESCRIPTION + * Function to set the descriptions and names for each process names group. + * Takes a config option containing list of strings that are used to set + * process group values. + * + * PARAMETERS + * `item' Config option containing process names groups. + * `groups' Table of process name groups to set values in. + * `max_groups' Maximum number of process name groups allowed. + * + * RETURN VALUE + * On success, the number of name groups set up. On error, appropriate + * negative error value. + */ +static int oconfig_to_ngroups(const oconfig_item_t *item, + rdt_name_group_t *groups, + const size_t max_groups) { + int index = 0; + + assert(groups != NULL); + assert(max_groups > 0); + assert(item != NULL); + + for (int j = 0; j < item->values_num; j++) { + int ret; + char value[DATA_MAX_NAME_LEN]; + + if ((item->values[j].value.string == NULL) || + (strlen(item->values[j].value.string) == 0)) { + ERROR(RDT_PLUGIN ": Error - empty group"); + return -EINVAL; + } + + sstrncpy(value, item->values[j].value.string, sizeof(value)); + + ret = strlisttoarray(value, &groups[index].names, &groups[index].num_names); + if (ret != 0 || groups[index].num_names == 0) { + ERROR(RDT_PLUGIN ": Error parsing process names group (%s)", + item->values[j].value.string); + return -EINVAL; + } + + /* set group description info */ + groups[index].desc = sstrdup(item->values[j].value.string); + if (groups[index].desc == NULL) { + ERROR(RDT_PLUGIN ": Error allocating name group description"); + return -ENOMEM; + } + + groups[index].proc_pids = NULL; + groups[index].monitored_pids_count = 0; + + index++; + + if (index >= (const int)max_groups) { + WARNING(RDT_PLUGIN ": Too many process names groups configured"); + return index; + } + } + + return index; +} + +/* + * NAME + * rdt_free_ngroups + * + * DESCRIPTION + * Function to deallocate memory allocated for name groups. + * + * PARAMETERS + * `rdt' Pointer to rdt context + */ +static void rdt_free_ngroups(rdt_ctx_t *rdt) { + for (int i = 0; i < RDT_MAX_NAMES_GROUPS; i++) { + if (rdt->ngroups[i].desc) + DEBUG(RDT_PLUGIN ": Freeing pids \'%s\' group\'s data...", + rdt->ngroups[i].desc); + sfree(rdt->ngroups[i].desc); + strarray_free(rdt->ngroups[i].names, rdt->ngroups[i].num_names); + + if (rdt->ngroups[i].proc_pids) + proc_pids_free(rdt->ngroups[i].proc_pids, rdt->ngroups[i].num_names); + + rdt->ngroups[i].num_names = 0; + sfree(rdt->pngroups[i]); + } + if (rdt->proc_pids) + sfree(rdt->proc_pids); + + rdt->num_ngroups = 0; +} + +/* + * NAME + * rdt_config_ngroups + * + * DESCRIPTION + * Reads name groups configuration. + * + * PARAMETERS + * `rdt` Pointer to rdt context + * `item' Config option containing process names groups. + * + * RETURN VALUE + * 0 on success. Negative number on error. + */ +static int rdt_config_ngroups(rdt_ctx_t *rdt, const oconfig_item_t *item) { + int n = 0; + enum pqos_mon_event events = 0; + + if (item == NULL) { + DEBUG(RDT_PLUGIN ": ngroups_config: Invalid argument."); + return -EINVAL; + } + + DEBUG(RDT_PLUGIN ": Process names groups [%d]:", item->values_num); + for (int j = 0; j < item->values_num; j++) { + if (item->values[j].type != OCONFIG_TYPE_STRING) { + ERROR(RDT_PLUGIN + ": given process names group value is not a string [idx=%d]", + j); + return -EINVAL; + } + DEBUG(RDT_PLUGIN ": [%d]: %s", j, item->values[j].value.string); + } + + n = oconfig_to_ngroups(item, rdt->ngroups, RDT_MAX_NAMES_GROUPS); + if (n < 0) { + rdt_free_ngroups(rdt); + ERROR(RDT_PLUGIN ": Error parsing process name groups configuration."); + return -EINVAL; + } + + /* validate configured process name values */ + for (int group_idx = 0; group_idx < n; group_idx++) { + DEBUG(RDT_PLUGIN ": checking group [%d]: %s", group_idx, + rdt->ngroups[group_idx].desc); + for (size_t name_idx = 0; name_idx < rdt->ngroups[group_idx].num_names; + name_idx++) { + DEBUG(RDT_PLUGIN ": checking process name [%zu]: %s", name_idx, + rdt->ngroups[group_idx].names[name_idx]); + if (!proc_pids_is_name_valid(rdt->ngroups[group_idx].names[name_idx])) { + ERROR(RDT_PLUGIN ": Process name group '%s' contains invalid name '%s'", + rdt->ngroups[group_idx].desc, + rdt->ngroups[group_idx].names[name_idx]); + rdt_free_ngroups(rdt); + return -EINVAL; + } + } + } + + if (n == 0) { + ERROR(RDT_PLUGIN ": Empty process name groups configured."); + return -EINVAL; + } + + /* Get all available events on this platform */ + for (unsigned i = 0; i < rdt->cap_mon->u.mon->num_events; i++) + events |= rdt->cap_mon->u.mon->events[i].type; + + events &= ~(PQOS_PERF_EVENT_LLC_MISS); + + DEBUG(RDT_PLUGIN ": Available events to monitor: %#x", events); + + rdt->num_ngroups = n; + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + int found = ngroup_cmp(&rdt->ngroups[j], &rdt->ngroups[i]); + if (found != 0) { + rdt_free_ngroups(rdt); + ERROR(RDT_PLUGIN + ": Cannot monitor same process name in different groups."); + return -EINVAL; + } + } + + rdt->ngroups[i].events = events; + rdt->pngroups[i] = calloc(1, sizeof(*rdt->pngroups[i])); + if (rdt->pngroups[i] == NULL) { + rdt_free_ngroups(rdt); + ERROR(RDT_PLUGIN + ": Failed to allocate memory for process name monitoring data."); + return -ENOMEM; + } + } + + return 0; +} + +/* + * NAME + * rdt_refresh_ngroup + * + * DESCRIPTION + * Refresh pids monitored by name group. + * + * PARAMETERS + * `ngroup` Pointer to name group. + * `group_mon_data' PQoS monitoring context. + * + * RETURN VALUE + * 0 on success. Negative number on error. + */ +static int rdt_refresh_ngroup(rdt_name_group_t *ngroup, + struct pqos_mon_data *group_mon_data) { + + int result = 0; + + if (NULL == ngroup) + return -1; + + if (NULL == ngroup->proc_pids) { + ERROR(RDT_PLUGIN + ": rdt_refresh_ngroup: \'%s\' uninitialized process pids array.", + ngroup->desc); + + return -1; + } + + DEBUG(RDT_PLUGIN ": rdt_refresh_ngroup: \'%s\' process names group.", + ngroup->desc); + + proc_pids_t **proc_pids = ngroup->proc_pids; + pids_list_t added_pids; + pids_list_t removed_pids; + + memset(&added_pids, 0, sizeof(added_pids)); + memset(&removed_pids, 0, sizeof(removed_pids)); + + for (size_t i = 0; i < ngroup->num_names; ++i) { + int diff_result = pids_list_diff(proc_pids[i], &added_pids, &removed_pids); + if (0 != diff_result) { + ERROR(RDT_PLUGIN + ": rdt_refresh_ngroup: \'%s\'. Error [%d] during PID diff.", + ngroup->desc, diff_result); + result = -1; + goto cleanup; + } + } + + DEBUG(RDT_PLUGIN ": rdt_refresh_ngroup: \'%s\' process names group, added: " + "%u, removed: %u.", + ngroup->desc, (unsigned)added_pids.size, (unsigned)removed_pids.size); + + if (added_pids.size > 0) { + + /* no pids are monitored for this group yet: start monitoring */ + if (0 == ngroup->monitored_pids_count) { + + int start_result = + pqos_mon_start_pids(added_pids.size, added_pids.pids, ngroup->events, + (void *)ngroup->desc, group_mon_data); + if (PQOS_RETVAL_OK == start_result) { + ngroup->monitored_pids_count = added_pids.size; + } else { + ERROR(RDT_PLUGIN ": rdt_refresh_ngroup: \'%s\'. Error [%d] while " + "STARTING pids monitoring", + ngroup->desc, start_result); + result = -1; + goto pqos_error_recovery; + } + + } else { + + int add_result = + pqos_mon_add_pids(added_pids.size, added_pids.pids, group_mon_data); + if (PQOS_RETVAL_OK == add_result) + ngroup->monitored_pids_count += added_pids.size; + else { + ERROR(RDT_PLUGIN + ": rdt_refresh_ngroup: \'%s\'. Error [%d] while ADDING pids.", + ngroup->desc, add_result); + result = -1; + goto pqos_error_recovery; + } + } + } + + if (removed_pids.size > 0) { + + /* all pids are removed: stop monitoring */ + if (removed_pids.size == ngroup->monitored_pids_count) { + /* all pids for this group are lost: stop monitoring */ + int stop_result = pqos_mon_stop(group_mon_data); + if (PQOS_RETVAL_OK != stop_result) { + ERROR(RDT_PLUGIN ": rdt_refresh_ngroup: \'%s\'. Error [%d] while " + "STOPPING monitoring", + ngroup->desc, stop_result); + result = -1; + goto pqos_error_recovery; + } + ngroup->monitored_pids_count = 0; + } else { + int remove_result = pqos_mon_remove_pids( + removed_pids.size, removed_pids.pids, group_mon_data); + if (PQOS_RETVAL_OK == remove_result) { + ngroup->monitored_pids_count -= removed_pids.size; + } else { + ERROR(RDT_PLUGIN + ": rdt_refresh_ngroup: \'%s\'. Error [%d] while REMOVING pids.", + ngroup->desc, remove_result); + result = -1; + goto pqos_error_recovery; + } + } } + + goto cleanup; + +pqos_error_recovery: + /* Why? + * Resources might be temporary unavailable. + * + * How? + * Collectd will halt the reading thread for this + * plugin if it returns an error. + * Consecutive errors will be increasing the read period + * up to 1 day interval. + * On pqos error stop monitoring current group + * and reset the proc_pids array + * monitoring will be restarted on next collectd read cycle + */ + DEBUG(RDT_PLUGIN ": rdt_refresh_ngroup: \'%s\' group RESET after error.", + ngroup->desc); + pqos_mon_stop(group_mon_data); + for (size_t i = 0; i < ngroup->num_names; ++i) + if (ngroup->proc_pids[i]->curr) + ngroup->proc_pids[i]->curr->size = 0; + + ngroup->monitored_pids_count = 0; + +cleanup: + pids_list_clear(&added_pids); + pids_list_clear(&removed_pids); + + return result; } + +/* + * NAME + * read_pids_data + * + * DESCRIPTION + * Poll monitoring statistics for name groups + * + * RETURN VALUE + * 0 on success. Negative number on error. + */ +static int read_pids_data() { + + if (0 == g_rdt->num_ngroups) { + DEBUG(RDT_PLUGIN ": read_pids_data: not configured - PIDs read skipped"); + return 0; + } + + DEBUG(RDT_PLUGIN ": read_pids_data: Scanning active groups"); + struct pqos_mon_data *active_groups[RDT_MAX_NAMES_GROUPS] = {0}; + size_t active_group_idx = 0; + for (size_t pngroups_idx = 0; + pngroups_idx < STATIC_ARRAY_SIZE(g_rdt->pngroups); ++pngroups_idx) + if (0 != g_rdt->ngroups[pngroups_idx].monitored_pids_count) + active_groups[active_group_idx++] = g_rdt->pngroups[pngroups_idx]; + + int ret = 0; + + if (0 == active_group_idx) { + DEBUG(RDT_PLUGIN ": read_pids_data: no active groups - PIDs read skipped"); + goto groups_refresh; + } + + DEBUG(RDT_PLUGIN ": read_pids_data: PIDs data polling"); + + int poll_result = pqos_mon_poll(active_groups, active_group_idx); + if (poll_result != PQOS_RETVAL_OK) { + ERROR(RDT_PLUGIN ": read_pids_data: Failed to poll monitoring data for " + "pids. Error [%d].", + poll_result); + ret = -poll_result; + goto groups_refresh; + } + + for (size_t i = 0; i < g_rdt->num_ngroups; i++) { + enum pqos_mon_event mbm_events = + (PQOS_MON_EVENT_LMEM_BW | PQOS_MON_EVENT_TMEM_BW | + PQOS_MON_EVENT_RMEM_BW); + + if (g_rdt->pngroups[i] == NULL || + g_rdt->ngroups[i].monitored_pids_count == 0) + continue; + + const struct pqos_event_values *pv = &g_rdt->pngroups[i]->values; + + /* Submit only monitored events data */ + + if (g_rdt->ngroups[i].events & PQOS_MON_EVENT_L3_OCCUP) + rdt_submit_gauge(g_rdt->ngroups[i].desc, "bytes", "llc", pv->llc); + + if (g_rdt->ngroups[i].events & PQOS_PERF_EVENT_IPC) + rdt_submit_gauge(g_rdt->ngroups[i].desc, "ipc", NULL, pv->ipc); + + if (g_rdt->ngroups[i].events & mbm_events) { + rdt_submit_derive(g_rdt->ngroups[i].desc, "memory_bandwidth", "local", + pv->mbm_local_delta); + rdt_submit_derive(g_rdt->ngroups[i].desc, "memory_bandwidth", "remote", + pv->mbm_remote_delta); + } + } + +#if COLLECT_DEBUG + rdt_dump_pids_data(); #endif /* COLLECT_DEBUG */ +groups_refresh: + ret = proc_pids_update(RDT_PROC_PATH, g_rdt->proc_pids, g_rdt->num_proc_pids); + if (0 != ret) { + ERROR(RDT_PLUGIN ": Initial update of proc pids failed"); + return ret; + } + + for (size_t i = 0; i < g_rdt->num_ngroups; i++) { + int refresh_result = + rdt_refresh_ngroup(&(g_rdt->ngroups[i]), g_rdt->pngroups[i]); + + if (0 != refresh_result) { + ERROR(RDT_PLUGIN ": read_pids_data: NGroup %zu refresh failed. Error: %d", + i, refresh_result); + if (0 == ret) { + /* refresh error will be escalated only if there were no + * errors before. + */ + ret = refresh_result; + } + } + } + + assert(ret <= 0); + return ret; +} + +/* + * NAME + * rdt_init_pids_monitoring + * + * DESCRIPTION + * Initialize pids monitoring for all name groups + */ +static void rdt_init_pids_monitoring() { + for (size_t group_idx = 0; group_idx < g_rdt->num_ngroups; group_idx++) { + /* + * Each group must have not-null proc_pids array. + * Initial refresh is not mandatory for proper + * PIDs statistics detection. + */ + rdt_name_group_t *ng = &g_rdt->ngroups[group_idx]; + int init_result = + proc_pids_init((const char **)ng->names, ng->num_names, &ng->proc_pids); + if (0 != init_result) { + ERROR(RDT_PLUGIN + ": Initialization of proc_pids for group %zu failed. Error: %d", + group_idx, init_result); + continue; + } + + /* update global proc_pids table */ + proc_pids_t **proc_pids = + realloc(g_rdt->proc_pids, (g_rdt->num_proc_pids + ng->num_names) * + sizeof(*g_rdt->proc_pids)); + if (NULL == proc_pids) { + ERROR(RDT_PLUGIN ": Alloc error\n"); + continue; + } + + for (size_t i = 0; i < ng->num_names; i++) + proc_pids[g_rdt->num_proc_pids + i] = ng->proc_pids[i]; + + g_rdt->proc_pids = proc_pids; + g_rdt->num_proc_pids += ng->num_names; + } + + if (g_rdt->num_ngroups > 0) { + int update_result = + proc_pids_update(RDT_PROC_PATH, g_rdt->proc_pids, g_rdt->num_proc_pids); + if (0 != update_result) + ERROR(RDT_PLUGIN ": Initial update of proc pids failed"); + } + + for (size_t group_idx = 0; group_idx < g_rdt->num_ngroups; group_idx++) { + int refresh_result = rdt_refresh_ngroup(&(g_rdt->ngroups[group_idx]), + g_rdt->pngroups[group_idx]); + if (0 != refresh_result) + ERROR(RDT_PLUGIN ": Initial refresh of group %zu failed. Error: %d", + group_idx, refresh_result); + } +} +#endif /* LIBPQOS2 */ +/* + * NAME + * rdt_free_cgroups + * + * DESCRIPTION + * Function to deallocate memory allocated for core groups. + */ static void rdt_free_cgroups(void) { config_cores_cleanup(&g_rdt->cores); for (int i = 0; i < RDT_MAX_CORES; i++) { - sfree(g_rdt->pgroups[i]); + sfree(g_rdt->pcgroups[i]); } + g_rdt->cores.num_cgroups = 0; } static int rdt_default_cgroups(void) { @@ -137,7 +913,7 @@ static int rdt_default_cgroups(void) { char desc[DATA_MAX_NAME_LEN]; /* set core group info */ - cgroup->cores = calloc(1, sizeof(*(cgroup->cores))); + cgroup->cores = calloc(1, sizeof(*cgroup->cores)); if (cgroup->cores == NULL) { ERROR(RDT_PLUGIN ": Error allocating cores array"); rdt_free_cgroups(); @@ -146,7 +922,7 @@ static int rdt_default_cgroups(void) { cgroup->num_cores = 1; cgroup->cores[0] = i; - snprintf(desc, sizeof(desc), "%d", g_rdt->pqos_cpu->cores[i].lcore); + ssnprintf(desc, sizeof(desc), "%d", g_rdt->pqos_cpu->cores[i].lcore); cgroup->desc = strdup(desc); if (cgroup->desc == NULL) { ERROR(RDT_PLUGIN ": Error allocating core group description"); @@ -214,9 +990,9 @@ static int rdt_config_cgroups(oconfig_item_t *item) { g_rdt->pqos_cpu->num_cores); DEBUG(RDT_PLUGIN ": Available events to monitor: %#x", events); - g_rdt->num_groups = n; - for (size_t i = 0; i < n; i++) { - for (size_t j = 0; j < i; j++) { + g_rdt->cores.num_cgroups = n; + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { int found = 0; found = config_cores_cmp_cgroups(&g_rdt->cores.cgroups[j], &g_rdt->cores.cgroups[i]); @@ -228,8 +1004,8 @@ static int rdt_config_cgroups(oconfig_item_t *item) { } g_rdt->events[i] = events; - g_rdt->pgroups[i] = calloc(1, sizeof(*g_rdt->pgroups[i])); - if (g_rdt->pgroups[i] == NULL) { + g_rdt->pcgroups[i] = calloc(1, sizeof(*g_rdt->pcgroups[i])); + if (g_rdt->pcgroups[i] == NULL) { rdt_free_cgroups(); ERROR(RDT_PLUGIN ": Failed to allocate memory for monitoring data."); return -ENOMEM; @@ -260,14 +1036,34 @@ static int rdt_preinit(void) { struct pqos_config pqos = {.fd_log = -1, .callback_log = rdt_pqos_log, .context_log = NULL, - .verbose = 0}; + .verbose = 0, +#ifdef LIBPQOS2 + .interface = PQOS_INTER_OS_RESCTRL_MON}; + DEBUG(RDT_PLUGIN ": Initializing PQoS with RESCTRL interface"); +#else + .interface = PQOS_INTER_MSR}; + DEBUG(RDT_PLUGIN ": Initializing PQoS with MSR interface"); +#endif ret = pqos_init(&pqos); + DEBUG(RDT_PLUGIN ": PQoS initialization result: [%d]", ret); + +#ifdef LIBPQOS2 + if (ret == PQOS_RETVAL_INTER) { + pqos.interface = PQOS_INTER_MSR; + DEBUG(RDT_PLUGIN ": Initializing PQoS with MSR interface"); + ret = pqos_init(&pqos); + DEBUG(RDT_PLUGIN ": PQoS initialization result: [%d]", ret); + } +#endif + if (ret != PQOS_RETVAL_OK) { ERROR(RDT_PLUGIN ": Error initializing PQoS library!"); goto rdt_preinit_error1; } + g_interface = pqos.interface; + ret = pqos_cap_get(&g_rdt->pqos_cap, &g_rdt->pqos_cpu); if (ret != PQOS_RETVAL_OK) { ERROR(RDT_PLUGIN ": Error retrieving PQoS capabilities."); @@ -296,7 +1092,6 @@ rdt_preinit_error2: pqos_fini(); rdt_preinit_error1: - sfree(g_rdt); return -1; @@ -309,25 +1104,66 @@ static int rdt_config(oconfig_item_t *ci) { reports a failure in configuration and aborts */ - return (0); + return 0; } for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; - if (strcasecmp("Cores", child->key) == 0) { - if (rdt_config_cgroups(child) != 0) { + if (strncasecmp("Cores", child->key, (size_t)strlen("Cores")) == 0) { + if (g_rdt->cores.num_cgroups > 0) { + ERROR(RDT_PLUGIN + ": Configuration parameter \"%s\" can be used only once.", + child->key); + g_state = CONFIGURATION_ERROR; + } else if (rdt_config_cgroups(child) != 0) g_state = CONFIGURATION_ERROR; + + if (g_state == CONFIGURATION_ERROR) /* if we return -1 at this point collectd reports a failure in configuration and aborts */ - return (0); - } + return 0; #if COLLECT_DEBUG rdt_dump_cgroups(); #endif /* COLLECT_DEBUG */ + } else if (strncasecmp("Processes", child->key, + (size_t)strlen("Processes")) == 0) { +#ifdef LIBPQOS2 + if (g_interface != PQOS_INTER_OS_RESCTRL_MON) { + ERROR(RDT_PLUGIN ": Configuration parameter \"%s\" not supported. " + "Resctrl monitoring is needed for PIDs monitoring.", + child->key); + g_state = CONFIGURATION_ERROR; + } + + else if (g_rdt->num_ngroups > 0) { + ERROR(RDT_PLUGIN + ": Configuration parameter \"%s\" can be used only once.", + child->key); + g_state = CONFIGURATION_ERROR; + } + + else if (rdt_config_ngroups(g_rdt, child) != 0) + g_state = CONFIGURATION_ERROR; + + if (g_state == CONFIGURATION_ERROR) + /* if we return -1 at this point collectd + reports a failure in configuration and + aborts + */ + return 0; + +#if COLLECT_DEBUG + rdt_dump_ngroups(); +#endif /* COLLECT_DEBUG */ +#else /* !LIBPQOS2 */ + ERROR(RDT_PLUGIN ": Configuration parameter \"%s\" not supported, please " + "recompile collectd with libpqos version 2.0 or newer.", + child->key); +#endif /* LIBPQOS2 */ } else { ERROR(RDT_PLUGIN ": Unknown configuration parameter \"%s\".", child->key); } @@ -336,64 +1172,30 @@ static int rdt_config(oconfig_item_t *ci) { return 0; } -static void rdt_submit_derive(const char *cgroup, const char *type, - const char *type_instance, derive_t value) { - value_list_t vl = VALUE_LIST_INIT; - - vl.values = &(value_t){.derive = value}; - vl.values_len = 1; - - sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin)); - snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup); - sstrncpy(vl.type, type, sizeof(vl.type)); - if (type_instance) - sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); - - plugin_dispatch_values(&vl); -} - -static void rdt_submit_gauge(const char *cgroup, const char *type, - const char *type_instance, gauge_t value) { - value_list_t vl = VALUE_LIST_INIT; - - vl.values = &(value_t){.gauge = value}; - vl.values_len = 1; +static int read_cores_data() { - sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin)); - snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup); - sstrncpy(vl.type, type, sizeof(vl.type)); - if (type_instance) - sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); - - plugin_dispatch_values(&vl); -} - -static int rdt_read(__attribute__((unused)) user_data_t *ud) { - int ret; - - if (g_rdt == NULL) { - ERROR(RDT_PLUGIN ": rdt_read: plugin not initialized."); - return -EINVAL; + if (0 == g_rdt->cores.num_cgroups) { + DEBUG(RDT_PLUGIN ": read_cores_data: not configured - Cores read skipped"); + return 0; } + DEBUG(RDT_PLUGIN ": read_cores_data: Cores data poll"); - ret = pqos_mon_poll(&g_rdt->pgroups[0], (unsigned)g_rdt->num_groups); + int ret = + pqos_mon_poll(&g_rdt->pcgroups[0], (unsigned)g_rdt->cores.num_cgroups); if (ret != PQOS_RETVAL_OK) { - ERROR(RDT_PLUGIN ": Failed to poll monitoring data."); + ERROR(RDT_PLUGIN ": read_cores_data: Failed to poll monitoring data for " + "cores. Error [%d].", + ret); return -1; } -#if COLLECT_DEBUG - rdt_dump_data(); -#endif /* COLLECT_DEBUG */ - - for (size_t i = 0; i < g_rdt->num_groups; i++) { + for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) { core_group_t *cgroup = g_rdt->cores.cgroups + i; - enum pqos_mon_event mbm_events = (PQOS_MON_EVENT_LMEM_BW | PQOS_MON_EVENT_TMEM_BW | PQOS_MON_EVENT_RMEM_BW); - const struct pqos_event_values *pv = &g_rdt->pgroups[i]->values; + const struct pqos_event_values *pv = &g_rdt->pcgroups[i]->values; /* Submit only monitored events data */ @@ -411,30 +1213,74 @@ static int rdt_read(__attribute__((unused)) user_data_t *ud) { } } +#if COLLECT_DEBUG + rdt_dump_cores_data(); +#endif /* COLLECT_DEBUG */ + return 0; } -static int rdt_init(void) { - int ret; +static int rdt_read(__attribute__((unused)) user_data_t *ud) { - if (g_state == CONFIGURATION_ERROR) - return -1; + if (g_rdt == NULL) { + ERROR(RDT_PLUGIN ": rdt_read: plugin not initialized."); + return -EINVAL; + } - ret = rdt_preinit(); - if (ret != 0) - return ret; + int cores_read_result = read_cores_data(); + +#ifdef LIBPQOS2 + int pids_read_result = read_pids_data(); +#endif /* LIBPQOS2 */ + + if (0 != cores_read_result) + return cores_read_result; + +#ifdef LIBPQOS2 + if (0 != pids_read_result) + return pids_read_result; +#endif /* LIBPQOS2 */ - /* Start monitoring */ - for (size_t i = 0; i < g_rdt->num_groups; i++) { + return 0; +} + +static void rdt_init_cores_monitoring() { + for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) { core_group_t *cg = g_rdt->cores.cgroups + i; - ret = pqos_mon_start(cg->num_cores, cg->cores, g_rdt->events[i], - (void *)cg->desc, g_rdt->pgroups[i]); + int mon_start_result = + pqos_mon_start(cg->num_cores, cg->cores, g_rdt->events[i], + (void *)cg->desc, g_rdt->pcgroups[i]); - if (ret != PQOS_RETVAL_OK) - ERROR(RDT_PLUGIN ": Error starting monitoring group %s (pqos status=%d)", - cg->desc, ret); + if (mon_start_result != PQOS_RETVAL_OK) + ERROR(RDT_PLUGIN + ": Error starting cores monitoring group %s (pqos status=%d)", + cg->desc, mon_start_result); } +} + +static int rdt_init(void) { + + if (g_state == CONFIGURATION_ERROR) { + if (g_rdt != NULL) { + if (g_rdt->cores.num_cgroups > 0) + rdt_free_cgroups(); +#ifdef LIBPQOS2 + if (g_rdt->num_ngroups > 0) + rdt_free_ngroups(g_rdt); +#endif + } + return -1; + } + + int rdt_preinint_result = rdt_preinit(); + if (rdt_preinint_result != 0) + return rdt_preinint_result; + + rdt_init_cores_monitoring(); +#ifdef LIBPQOS2 + rdt_init_pids_monitoring(); +#endif /* LIBPQOS2 */ return 0; } @@ -447,16 +1293,24 @@ static int rdt_shutdown(void) { if (g_rdt == NULL) return 0; - /* Stop monitoring */ - for (size_t i = 0; i < g_rdt->num_groups; i++) { - pqos_mon_stop(g_rdt->pgroups[i]); + /* Stop monitoring cores */ + for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) { + pqos_mon_stop(g_rdt->pcgroups[i]); } +/* Stop pids monitoring */ +#ifdef LIBPQOS2 + for (size_t i = 0; i < g_rdt->num_ngroups; i++) + pqos_mon_stop(g_rdt->pngroups[i]); +#endif + ret = pqos_fini(); if (ret != PQOS_RETVAL_OK) ERROR(RDT_PLUGIN ": Error shutting down PQoS library."); - rdt_free_cgroups(); +#ifdef LIBPQOS2 + rdt_free_ngroups(g_rdt); +#endif /* LIBPQOS2 */ sfree(g_rdt); return 0; diff --git a/src/intel_rdt_test.c b/src/intel_rdt_test.c new file mode 100644 index 00000000..fee83841 --- /dev/null +++ b/src/intel_rdt_test.c @@ -0,0 +1,309 @@ +#include "intel_rdt.c" /* sic */ +#include "testing.h" + +/*************************************************************************** + * PQOS mocks + */ +int pqos_mon_reset(void) { return 0; } +int pqos_mon_assoc_get(const unsigned lcore, pqos_rmid_t *rmid) { return 0; } +int pqos_mon_start(const unsigned num_cores, const unsigned *cores, + const enum pqos_mon_event event, void *context, + struct pqos_mon_data *group) { + return 0; +} +#if PQOS_VERSION >= 30000 +int pqos_mon_start_pids(const unsigned num_pids, const pid_t *pids, + const enum pqos_mon_event event, void *context, + struct pqos_mon_data *group) { + return 0; +} +int pqos_mon_add_pids(const unsigned num_pids, const pid_t *pids, + struct pqos_mon_data *group) { + return 0; +} +int pqos_mon_remove_pids(const unsigned num_pids, const pid_t *pids, + struct pqos_mon_data *group) { + return 0; +} + +#else +int pqos_mon_start_pid(const pid_t pids, const enum pqos_mon_event event, + void *context, struct pqos_mon_data *group) { + return 0; +} +#endif +int pqos_mon_stop(struct pqos_mon_data *group) { return 0; } +int pqos_mon_poll(struct pqos_mon_data **groups, const unsigned num_groups) { + return 0; +} + +#if PQOS_VERSION >= 30000 +int pqos_alloc_reset(const enum pqos_cdp_config l3_cdp_cfg, + const enum pqos_cdp_config l2_cdp_cfg, + const enum pqos_mba_config mba_cfg) { + return 0; +} +#elif PQOS_VERSION >= 20000 +int pqos_alloc_reset(const enum pqos_cdp_config l3_cdp_cfg, + const enum pqos_cdp_config l2_cdp_cfg) { + return 0; +} +#else +int pqos_alloc_reset(const enum pqos_cdp_config l3_cdp_cfg) { return 0; } +#endif +int pqos_alloc_assoc_set(const unsigned lcore, const unsigned class_id) { + return 0; +} +int pqos_alloc_assoc_get(const unsigned lcore, unsigned *class_id) { return 0; } +int pqos_alloc_assoc_set_pid(const pid_t task, const unsigned class_id) { + return 0; +} +int pqos_alloc_assoc_get_pid(const pid_t task, unsigned *class_id) { return 0; } +int pqos_alloc_assign(const unsigned technology, const unsigned *core_array, + const unsigned core_num, unsigned *class_id) { + return 0; +} +int pqos_alloc_release(const unsigned *core_array, const unsigned core_num) { + return 0; +} +int pqos_alloc_assign_pid(const unsigned technology, const pid_t *task_array, + const unsigned task_num, unsigned *class_id) { + return 0; +} +int pqos_alloc_release_pid(const pid_t *task_array, const unsigned task_num) { + return 0; +} +int pqos_init(const struct pqos_config *config) { return 0; } +int pqos_fini(void) { return 0; } +int pqos_cap_get_type(const struct pqos_cap *cap, const enum pqos_cap_type type, + const struct pqos_capability **cap_item) { + return 0; +} +int pqos_cap_get(const struct pqos_cap **cap, const struct pqos_cpuinfo **cpu) { + return 0; +} + +#ifdef LIBPQOS2 +/*************************************************************************** + * helper functions + */ +rdt_ctx_t *stub_rdt_setup() { + + rdt_ctx_t *rdt = calloc(1, sizeof(*rdt)); + struct pqos_cpuinfo *pqos_cpu = calloc(1, sizeof(*pqos_cpu)); + struct pqos_cap *pqos_cap = calloc(1, sizeof(*pqos_cap)); + struct pqos_cap_mon *mon = calloc(1, sizeof(*mon)); + struct pqos_capability *cap_mon = calloc(1, sizeof(*cap_mon)); + + cap_mon->u.mon = mon; + rdt->pqos_cap = pqos_cap; + rdt->pqos_cpu = pqos_cpu; + rdt->cap_mon = cap_mon; + + return rdt; +} + +void stub_rdt_teardown(rdt_ctx_t *rdt) { + free(rdt->cap_mon->u.mon); + free((void *)rdt->cap_mon); + free((void *)rdt->pqos_cpu); + free((void *)rdt->pqos_cap); + free(rdt); +} + +/*************************************************************************** + * tests + */ +DEF_TEST(rdt_config_ngroups__one_process) { + /* setup */ + rdt_ctx_t *rdt = stub_rdt_setup(); + + oconfig_value_t values[] = { + {.value.string = "proc1", .type = OCONFIG_TYPE_STRING}, + }; + oconfig_item_t config_item = { + .values = values, + .values_num = STATIC_ARRAY_SIZE(values), + }; + + /* check */ + int result = rdt_config_ngroups(rdt, &config_item); + EXPECT_EQ_INT(0, result); + EXPECT_EQ_STR(values[0].value.string, rdt->ngroups[0].desc); + EXPECT_EQ_INT(1, rdt->num_ngroups); + + /* cleanup */ + rdt_free_ngroups(rdt); + stub_rdt_teardown(rdt); + + return 0; +} + +DEF_TEST(rdt_config_ngroups__two_groups) { + /* setup */ + rdt_ctx_t *rdt = stub_rdt_setup(); + + oconfig_value_t values[] = { + {.value.string = "proc11,proc12,proc13", .type = OCONFIG_TYPE_STRING}, + {.value.string = "proc21,proc22,proc23", .type = OCONFIG_TYPE_STRING}, + }; + oconfig_item_t config_item = { + .values = values, + .values_num = STATIC_ARRAY_SIZE(values), + }; + + /* check */ + int result = rdt_config_ngroups(rdt, &config_item); + EXPECT_EQ_INT(0, result); + EXPECT_EQ_INT(2, rdt->num_ngroups); + EXPECT_EQ_STR("proc11,proc12,proc13", rdt->ngroups[0].desc); + EXPECT_EQ_STR("proc21,proc22,proc23", rdt->ngroups[1].desc); + EXPECT_EQ_STR("proc11", rdt->ngroups[0].names[0]); + EXPECT_EQ_STR("proc12", rdt->ngroups[0].names[1]); + EXPECT_EQ_STR("proc13", rdt->ngroups[0].names[2]); + EXPECT_EQ_STR("proc21", rdt->ngroups[1].names[0]); + EXPECT_EQ_STR("proc22", rdt->ngroups[1].names[1]); + EXPECT_EQ_STR("proc23", rdt->ngroups[1].names[2]); + + /* cleanup */ + rdt_free_ngroups(rdt); + stub_rdt_teardown(rdt); + + return 0; +} + +DEF_TEST(rdt_config_ngroups__too_long_proc_name) { + /* setup */ + rdt_ctx_t *rdt = stub_rdt_setup(); + + oconfig_value_t values[] = { + {.value.string = "_seventeen_chars_", .type = OCONFIG_TYPE_STRING}, + }; + oconfig_item_t config_item = { + .values = values, + .values_num = STATIC_ARRAY_SIZE(values), + }; + + /* check */ + int result = rdt_config_ngroups(rdt, &config_item); + EXPECT_EQ_INT(-EINVAL, result); + + /* cleanup */ + stub_rdt_teardown(rdt); + + return 0; +} + +DEF_TEST(rdt_config_ngroups__duplicate_proc_name_between_groups) { + /* setup */ + rdt_ctx_t *rdt = stub_rdt_setup(); + + oconfig_value_t values[] = { + {.value.string = "proc11,proc12,proc", .type = OCONFIG_TYPE_STRING}, + {.value.string = "proc21,proc,proc23", .type = OCONFIG_TYPE_STRING}, + }; + oconfig_item_t config_item = { + .values = values, + .values_num = STATIC_ARRAY_SIZE(values), + }; + + /* check */ + int result = rdt_config_ngroups(rdt, &config_item); + EXPECT_EQ_INT(-EINVAL, result); + + /* cleanup */ + stub_rdt_teardown(rdt); + + return 0; +} + +DEF_TEST(rdt_config_ngroups__duplicate_proc_name_in_group) { + /* setup */ + rdt_ctx_t *rdt = stub_rdt_setup(); + + oconfig_value_t values[] = { + {.value.string = "proc11,proc,proc,proc14", .type = OCONFIG_TYPE_STRING}, + }; + oconfig_item_t config_item = { + .values = values, + .values_num = STATIC_ARRAY_SIZE(values), + }; + + /* check */ + int result = rdt_config_ngroups(rdt, &config_item); + EXPECT_EQ_INT(-EINVAL, result); + + /* cleanup */ + stub_rdt_teardown(rdt); + + return 0; +} + +DEF_TEST(rdt_config_ngroups__empty_group) { + /* setup */ + rdt_ctx_t *rdt = stub_rdt_setup(); + + oconfig_value_t values[] = { + {.value.string = "proc11,proc12,proc13", .type = OCONFIG_TYPE_STRING}, + {.value.string = "", .type = OCONFIG_TYPE_STRING}, + + }; + oconfig_item_t config_item = { + .values = values, + .values_num = STATIC_ARRAY_SIZE(values), + }; + + /* check */ + int result = rdt_config_ngroups(rdt, &config_item); + EXPECT_EQ_INT(-EINVAL, result); + + /* cleanup */ + stub_rdt_teardown(rdt); + + return 0; +} + +DEF_TEST(rdt_config_ngroups__empty_proc_name) { + /* setup */ + rdt_ctx_t *rdt = stub_rdt_setup(); + + oconfig_value_t values[] = { + {.value.string = "proc11,,proc13", .type = OCONFIG_TYPE_STRING}, + }; + oconfig_item_t config_item = { + .values = values, + .values_num = STATIC_ARRAY_SIZE(values), + }; + + /* check */ + int result = rdt_config_ngroups(rdt, &config_item); + EXPECT_EQ_INT(-EINVAL, result); + + /* cleanup */ + stub_rdt_teardown(rdt); + + return 0; +} + +int main(void) { + RUN_TEST(rdt_config_ngroups__one_process); + RUN_TEST(rdt_config_ngroups__two_groups); + RUN_TEST(rdt_config_ngroups__too_long_proc_name); + RUN_TEST(rdt_config_ngroups__duplicate_proc_name_between_groups); + RUN_TEST(rdt_config_ngroups__duplicate_proc_name_in_group); + RUN_TEST(rdt_config_ngroups__empty_group); + RUN_TEST(rdt_config_ngroups__empty_proc_name); + END_TEST; +} + +#else +DEF_TEST(pqos12_test_stub) { + EXPECT_EQ_INT(0, 0); + return 0; +} + +int main(void) { + RUN_TEST(pqos12_test_stub); + END_TEST; +} +#endif diff --git a/src/interface.c b/src/interface.c index 86110b0c..b0d9eebb 100644 --- a/src/interface.c +++ b/src/interface.c @@ -24,9 +24,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #if HAVE_SYS_TYPES_H #include @@ -82,7 +82,9 @@ static int pnif; * (Module-)Global variables */ static const char *config_keys[] = { - "Interface", "IgnoreSelected", "ReportInactive", + "Interface", + "IgnoreSelected", + "ReportInactive", }; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); @@ -160,7 +162,8 @@ static void if_submit(const char *dev, const char *type, derive_t rx, derive_t tx) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = rx}, {.derive = tx}, + {.derive = rx}, + {.derive = tx}, }; if (ignorelist_match(ignorelist, dev) != 0) @@ -188,7 +191,7 @@ static int interface_read(void) { #define IFA_TX_PACKT ifi_opackets #define IFA_RX_ERROR ifi_ierrors #define IFA_TX_ERROR ifi_oerrors -/* #endif HAVE_STRUCT_IF_DATA */ + /* #endif HAVE_STRUCT_IF_DATA */ #elif HAVE_STRUCT_NET_DEVICE_STATS #define IFA_DATA net_device_stats @@ -226,7 +229,7 @@ static int interface_read(void) { } freeifaddrs(if_list); -/* #endif HAVE_GETIFADDRS */ + /* #endif HAVE_GETIFADDRS */ #elif KERNEL_LINUX FILE *fh; @@ -282,7 +285,7 @@ static int interface_read(void) { } fclose(fh); -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_LIBKSTAT derive_t rx; @@ -297,8 +300,8 @@ static int interface_read(void) { continue; if (unique_name) - snprintf(iname, sizeof(iname), "%s_%d_%s", ksp[i]->ks_module, - ksp[i]->ks_instance, ksp[i]->ks_name); + ssnprintf(iname, sizeof(iname), "%s_%d_%s", ksp[i]->ks_module, + ksp[i]->ks_instance, ksp[i]->ks_name); else sstrncpy(iname, ksp[i]->ks_name, sizeof(iname)); @@ -332,7 +335,7 @@ static int interface_read(void) { if ((rx != -1LL) || (tx != -1LL)) if_submit(iname, "if_errors", rx, tx); } -/* #endif HAVE_LIBKSTAT */ + /* #endif HAVE_LIBKSTAT */ #elif defined(HAVE_LIBSTATGRAB) sg_network_io_stats *ios; @@ -345,7 +348,7 @@ static int interface_read(void) { continue; if_submit(ios[i].interface_name, "if_octets", ios[i].rx, ios[i].tx); } -/* #endif HAVE_LIBSTATGRAB */ + /* #endif HAVE_LIBSTATGRAB */ #elif defined(HAVE_PERFSTAT) perfstat_id_t id; diff --git a/src/ipc.c b/src/ipc.c index 6e888c46..93eddaf8 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if KERNEL_LINUX /* _GNU_SOURCE is needed for struct shm_info.used_ids on musl libc */ @@ -173,8 +173,8 @@ static int ipc_init(void) /* {{{ */ pagesize_g = sysconf(_SC_PAGESIZE); return 0; } -/* }}} */ -/* #endif KERNEL_LINUX */ + /* }}} */ + /* #endif KERNEL_LINUX */ #elif KERNEL_AIX static caddr_t ipc_get_info(cid_t cid, int cmd, int version, int stsize, diff --git a/src/ipmi.c b/src/ipmi.c index fb99bad1..db0e775f 100644 --- a/src/ipmi.c +++ b/src/ipmi.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include @@ -111,9 +111,9 @@ static void c_ipmi_error(c_ipmi_instance_t *st, const char *func, int status) { } if (errbuf[0] == 0) { - snprintf(errbuf, sizeof(errbuf), "Unknown error %#x", status); + ssnprintf(errbuf, sizeof(errbuf), "Unknown error %#x", status); } - errbuf[sizeof(errbuf) - 1] = 0; + errbuf[sizeof(errbuf) - 1] = '\0'; ERROR("ipmi plugin: %s failed for `%s`: %s", func, st->name, errbuf); } /* void c_ipmi_error */ @@ -201,8 +201,8 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err, sstrncpy(n.type_instance, list_item->type_instance, sizeof(n.type_instance)); sstrncpy(n.type, list_item->sensor_type, sizeof(n.type)); - snprintf(n.message, sizeof(n.message), "sensor %s not present", - list_item->sensor_name); + ssnprintf(n.message, sizeof(n.message), "sensor %s not present", + list_item->sensor_name); plugin_dispatch_notification(&n); } @@ -254,8 +254,8 @@ static void sensor_read_handler(ipmi_sensor_t *sensor, int err, sstrncpy(n.type_instance, list_item->type_instance, sizeof(n.type_instance)); sstrncpy(n.type, list_item->sensor_type, sizeof(n.type)); - snprintf(n.message, sizeof(n.message), "sensor %s present", - list_item->sensor_name); + ssnprintf(n.message, sizeof(n.message), "sensor %s present", + list_item->sensor_name); plugin_dispatch_notification(&n); } @@ -310,10 +310,11 @@ static void sensor_get_name(ipmi_sensor_t *sensor, char *buffer, int buf_len) { return; ipmi_sensor_get_name(sensor, temp, sizeof(temp)); - temp[sizeof(temp) - 1] = 0; + temp[sizeof(temp) - 1] = '\0'; if (entity_id_string != NULL && strlen(temp)) - snprintf(sensor_name, sizeof(sensor_name), "%s %s", temp, entity_id_string); + ssnprintf(sensor_name, sizeof(sensor_name), "%s %s", temp, + entity_id_string); else if (entity_id_string != NULL) sstrncpy(sensor_name, entity_id_string, sizeof(sensor_name)); else @@ -338,8 +339,8 @@ static void sensor_get_name(ipmi_sensor_t *sensor, char *buffer, int buf_len) { sensor_id_ptr = strstr(temp, "("); if (sensor_id_ptr != NULL) { /* `sensor_id_ptr' now points to "(123)". */ - snprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr, - sensor_id_ptr); + ssnprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr, + sensor_id_ptr); } /* else: don't touch sensor_name. */ } @@ -476,7 +477,7 @@ static int sensor_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) { return 0; } - list_item = (c_ipmi_sensor_list_t *)calloc(1, sizeof(c_ipmi_sensor_list_t)); + list_item = calloc(1, sizeof(*list_item)); if (list_item == NULL) { pthread_mutex_unlock(&st->sensor_list_lock); return -1; @@ -493,8 +494,8 @@ static int sensor_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) { /* if sensor provides the percentage value, use "percent" collectd type and add the `percent` to the type instance of the reported value */ if (ipmi_sensor_get_percentage(sensor)) { - snprintf(list_item->type_instance, sizeof(list_item->type_instance), - "percent-%s", sensor_name_ptr); + ssnprintf(list_item->type_instance, sizeof(list_item->type_instance), + "percent-%s", sensor_name_ptr); type = "percent"; } else { /* use type instance as a name of the sensor */ @@ -514,8 +515,8 @@ static int sensor_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) { sstrncpy(n.type_instance, list_item->type_instance, sizeof(n.type_instance)); sstrncpy(n.type, list_item->sensor_type, sizeof(n.type)); - snprintf(n.message, sizeof(n.message), "sensor %s added", - list_item->sensor_name); + ssnprintf(n.message, sizeof(n.message), "sensor %s added", + list_item->sensor_name); plugin_dispatch_notification(&n); } @@ -561,8 +562,8 @@ static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) { sstrncpy(n.type_instance, list_item->type_instance, sizeof(n.type_instance)); sstrncpy(n.type, list_item->sensor_type, sizeof(n.type)); - snprintf(n.message, sizeof(n.message), "sensor %s removed", - list_item->sensor_name); + ssnprintf(n.message, sizeof(n.message), "sensor %s removed", + list_item->sensor_name); plugin_dispatch_notification(&n); } @@ -674,13 +675,13 @@ static int sensor_threshold_event_handler( ipmi_get_reading_name(event_type, sensor_type, offset); sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance)); if (value_present != IPMI_NO_VALUES_PRESENT) - snprintf(n.message, sizeof(n.message), - "sensor %s received event: %s, value is %f", n.type_instance, - event_state, value); + ssnprintf(n.message, sizeof(n.message), + "sensor %s received event: %s, value is %f", n.type_instance, + event_state, value); else - snprintf(n.message, sizeof(n.message), - "sensor %s received event: %s, value not provided", - n.type_instance, event_state); + ssnprintf(n.message, sizeof(n.message), + "sensor %s received event: %s, value not provided", + n.type_instance, event_state); DEBUG("Threshold event received for sensor %s", n.type_instance); @@ -699,7 +700,7 @@ static int sensor_threshold_event_handler( /* both values present, so fall-through to add raw value too */ case IPMI_RAW_VALUE_PRESENT: { char buf[DATA_MAX_NAME_LEN] = {0}; - snprintf(buf, sizeof(buf), "0x%2.2x", raw_value); + ssnprintf(buf, sizeof(buf), "0x%2.2x", raw_value); plugin_notification_meta_add_string(&n, "raw", buf); } break; default: @@ -741,8 +742,8 @@ static int sensor_discrete_event_handler(ipmi_sensor_t *sensor, const char *event_state = ipmi_get_reading_name(event_type, sensor_type, offset); sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance)); - snprintf(n.message, sizeof(n.message), "sensor %s received event: %s", - n.type_instance, event_state); + ssnprintf(n.message, sizeof(n.message), "sensor %s received event: %s", + n.type_instance, event_state); DEBUG("Discrete event received for sensor %s", n.type_instance); @@ -1253,7 +1254,7 @@ static int c_ipmi_init(void) { /* The `st->name` is used as "domain name" for ipmi_open_domain(). * That value should be unique, so we do plugin_register_complex_read() * at first as it checks the uniqueness. */ - snprintf(callback_name, sizeof(callback_name), "ipmi/%s", st->name); + ssnprintf(callback_name, sizeof(callback_name), "ipmi/%s", st->name); user_data_t ud = { .data = st, diff --git a/src/iptables.c b/src/iptables.c index 225ed2c1..ea2d2404 100644 --- a/src/iptables.c +++ b/src/iptables.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -221,8 +221,8 @@ static int submit6_match(const struct ip6t_entry_match *match, sstrncpy(vl.plugin, "ip6tables", sizeof(vl.plugin)); - status = snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%s", - chain->table, chain->chain); + status = ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%s", + chain->table, chain->chain); if ((status < 1) || ((unsigned int)status >= sizeof(vl.plugin_instance))) return 0; @@ -230,8 +230,8 @@ static int submit6_match(const struct ip6t_entry_match *match, sstrncpy(vl.type_instance, chain->name, sizeof(vl.type_instance)); } else { if (chain->rule_type == RTYPE_NUM) - snprintf(vl.type_instance, sizeof(vl.type_instance), "%i", - chain->rule.num); + ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%i", + chain->rule.num); else sstrncpy(vl.type_instance, (char *)match->data, sizeof(vl.type_instance)); } @@ -269,8 +269,8 @@ static int submit_match(const struct ipt_entry_match *match, sstrncpy(vl.plugin, "iptables", sizeof(vl.plugin)); - status = snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%s", - chain->table, chain->chain); + status = ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%s", + chain->table, chain->chain); if ((status < 1) || ((unsigned int)status >= sizeof(vl.plugin_instance))) return 0; @@ -278,8 +278,8 @@ static int submit_match(const struct ipt_entry_match *match, sstrncpy(vl.type_instance, chain->name, sizeof(vl.type_instance)); } else { if (chain->rule_type == RTYPE_NUM) - snprintf(vl.type_instance, sizeof(vl.type_instance), "%i", - chain->rule.num); + ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%i", + chain->rule.num); else sstrncpy(vl.type_instance, (char *)match->data, sizeof(vl.type_instance)); } diff --git a/src/ipvs.c b/src/ipvs.c index 0afc7494..0dcc8b36 100644 --- a/src/ipvs.c +++ b/src/ipvs.c @@ -32,8 +32,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_ARPA_INET_H #include @@ -207,7 +207,8 @@ static void cipvs_submit_connections(const char *pi, const char *ti, static void cipvs_submit_if(const char *pi, const char *t, const char *ti, derive_t rx, derive_t tx) { value_t values[] = { - {.derive = rx}, {.derive = tx}, + {.derive = rx}, + {.derive = tx}, }; value_list_t vl = VALUE_LIST_INIT; diff --git a/src/irq.c b/src/irq.c index aa6ac04b..91563560 100644 --- a/src/irq.c +++ b/src/irq.c @@ -23,9 +23,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #if !KERNEL_LINUX #error "No applicable input method." @@ -138,7 +138,7 @@ static int irq_read(void) { if (irq_name_len == 4 && (strncmp(irq_name, "FIQ:", 4) == 0)) continue; - irq_name[irq_name_len - 1] = 0; + irq_name[irq_name_len - 1] = '\0'; irq_name_len--; irq_value = 0; diff --git a/src/java.c b/src/java.c index 3f4b3d1a..41f49092 100644 --- a/src/java.c +++ b/src/java.c @@ -23,9 +23,9 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -1057,7 +1057,7 @@ static int jtoc_values_array(JNIEnv *jvm_env, /* {{{ */ BAIL_OUT(-1); } - values = (value_t *)calloc(values_num, sizeof(value_t)); + values = calloc(values_num, sizeof(*values)); if (values == NULL) { ERROR("java plugin: jtoc_values_array: calloc failed."); BAIL_OUT(-1); @@ -1306,7 +1306,8 @@ static jint JNICALL cjni_api_register_read(JNIEnv *jvm_env, /* {{{ */ /* group = */ NULL, cbi->name, cjni_read, /* interval = */ 0, &(user_data_t){ - .data = cbi, .free_func = cjni_callback_info_destroy, + .data = cbi, + .free_func = cjni_callback_info_destroy, }); (*jvm_env)->DeleteLocalRef(jvm_env, o_read); @@ -1325,11 +1326,11 @@ static jint JNICALL cjni_api_register_write(JNIEnv *jvm_env, /* {{{ */ DEBUG("java plugin: Registering new write callback: %s", cbi->name); - plugin_register_write( - cbi->name, cjni_write, - &(user_data_t){ - .data = cbi, .free_func = cjni_callback_info_destroy, - }); + plugin_register_write(cbi->name, cjni_write, + &(user_data_t){ + .data = cbi, + .free_func = cjni_callback_info_destroy, + }); (*jvm_env)->DeleteLocalRef(jvm_env, o_write); @@ -1347,11 +1348,11 @@ static jint JNICALL cjni_api_register_flush(JNIEnv *jvm_env, /* {{{ */ DEBUG("java plugin: Registering new flush callback: %s", cbi->name); - plugin_register_flush( - cbi->name, cjni_flush, - &(user_data_t){ - .data = cbi, .free_func = cjni_callback_info_destroy, - }); + plugin_register_flush(cbi->name, cjni_flush, + &(user_data_t){ + .data = cbi, + .free_func = cjni_callback_info_destroy, + }); (*jvm_env)->DeleteLocalRef(jvm_env, o_flush); @@ -1377,7 +1378,8 @@ static jint JNICALL cjni_api_register_log(JNIEnv *jvm_env, /* {{{ */ plugin_register_log(cbi->name, cjni_log, &(user_data_t){ - .data = cbi, .free_func = cjni_callback_info_destroy, + .data = cbi, + .free_func = cjni_callback_info_destroy, }); (*jvm_env)->DeleteLocalRef(jvm_env, o_log); @@ -1397,11 +1399,11 @@ static jint JNICALL cjni_api_register_notification(JNIEnv *jvm_env, /* {{{ */ DEBUG("java plugin: Registering new notification callback: %s", cbi->name); - plugin_register_notification( - cbi->name, cjni_notification, - &(user_data_t){ - .data = cbi, .free_func = cjni_callback_info_destroy, - }); + plugin_register_notification(cbi->name, cjni_notification, + &(user_data_t){ + .data = cbi, + .free_func = cjni_callback_info_destroy, + }); (*jvm_env)->DeleteLocalRef(jvm_env, o_notification); @@ -1543,16 +1545,19 @@ static JNINativeMethod jni_api_functions[] = /* {{{ */ "(Ljava/lang/String;Lorg/collectd/api/CollectdLogInterface;)I", cjni_api_register_log}, - {"registerNotification", "(Ljava/lang/String;Lorg/collectd/api/" - "CollectdNotificationInterface;)I", + {"registerNotification", + "(Ljava/lang/String;Lorg/collectd/api/" + "CollectdNotificationInterface;)I", cjni_api_register_notification}, - {"registerMatch", "(Ljava/lang/String;Lorg/collectd/api/" - "CollectdMatchFactoryInterface;)I", + {"registerMatch", + "(Ljava/lang/String;Lorg/collectd/api/" + "CollectdMatchFactoryInterface;)I", cjni_api_register_match}, - {"registerTarget", "(Ljava/lang/String;Lorg/collectd/api/" - "CollectdTargetFactoryInterface;)I", + {"registerTarget", + "(Ljava/lang/String;Lorg/collectd/api/" + "CollectdTargetFactoryInterface;)I", cjni_api_register_target}, {"log", "(ILjava/lang/String;)V", cjni_api_log}, diff --git a/src/libcollectdclient/client.c b/src/libcollectdclient/client.c index df6fd960..a8bbe2f7 100644 --- a/src/libcollectdclient/client.c +++ b/src/libcollectdclient/client.c @@ -80,22 +80,22 @@ * is very useful to add formatted stuff to the end of a buffer. */ #define SSTRCPY(d, s) \ do { \ - strncpy((d), (s), sizeof(d)); \ - (d)[sizeof(d) - 1] = 0; \ + strncpy((d), (s), sizeof(d) - 1); \ + (d)[sizeof(d) - 1] = '\0'; \ } while (0) #define SSTRCAT(d, s) \ do { \ size_t _l = strlen(d); \ strncpy((d) + _l, (s), sizeof(d) - _l); \ - (d)[sizeof(d) - 1] = 0; \ + (d)[sizeof(d) - 1] = '\0'; \ } while (0) #define SSTRCATF(d, ...) \ do { \ char _b[sizeof(d)]; \ snprintf(_b, sizeof(_b), __VA_ARGS__); \ - _b[sizeof(_b) - 1] = 0; \ + _b[sizeof(_b) - 1] = '\0'; \ SSTRCAT((d), _b); \ } while (0) @@ -147,7 +147,7 @@ static char *sstrerror(int errnum, char *buf, size_t buflen) { #if !HAVE_STRERROR_R snprintf(buf, buflen, "Error #%i; strerror_r is not available.", errnum); -/* #endif !HAVE_STRERROR_R */ + /* #endif !HAVE_STRERROR_R */ #elif STRERROR_R_CHAR_P { @@ -157,22 +157,24 @@ static char *sstrerror(int errnum, char *buf, size_t buflen) { if ((temp != NULL) && (temp != buf) && (temp[0] != 0)) strncpy(buf, temp, buflen); else - strncpy(buf, "strerror_r did not return " - "an error message", + strncpy(buf, + "strerror_r did not return " + "an error message", buflen); } } -/* #endif STRERROR_R_CHAR_P */ + /* #endif STRERROR_R_CHAR_P */ #else if (strerror_r(errnum, buf, buflen) != 0) { - snprintf(buf, buflen, "Error #%i; " - "Additionally, strerror_r failed.", + snprintf(buf, buflen, + "Error #%i; " + "Additionally, strerror_r failed.", errnum); } #endif /* STRERROR_R_CHAR_P */ - buf[buflen - 1] = 0; + buf[buflen - 1] = '\0'; return buf; } /* char *sstrerror */ @@ -183,7 +185,7 @@ static int lcc_set_errno(lcc_connection_t *c, int err) /* {{{ */ return -1; sstrerror(err, c->errbuf, sizeof(c->errbuf)); - c->errbuf[sizeof(c->errbuf) - 1] = 0; + c->errbuf[sizeof(c->errbuf) - 1] = '\0'; return 0; } /* }}} int lcc_set_errno */ @@ -244,7 +246,7 @@ static void lcc_chomp(char *str) /* {{{ */ while (str_len > 0) { if (str[str_len - 1] >= 32) break; - str[str_len - 1] = 0; + str[str_len - 1] = '\0'; str_len--; } } /* }}} void lcc_chomp */ @@ -308,7 +310,7 @@ static int lcc_receive(lcc_connection_t *c, /* {{{ */ /* Now copy the message. */ strncpy(res.message, ptr, sizeof(res.message)); - res.message[sizeof(res.message) - 1] = 0; + res.message[sizeof(res.message) - 1] = '\0'; /* Error or no lines follow: We're done. */ if (res.status <= 0) { @@ -624,7 +626,7 @@ int lcc_getval(lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */ snprintf(command, sizeof(command), "GETVAL %s", lcc_strescape(ident_esc, ident_str, sizeof(ident_esc))); - command[sizeof(command) - 1] = 0; + command[sizeof(command) - 1] = '\0'; /* Send talk to the daemon.. */ status = lcc_sendreceive(c, command, &res); @@ -930,7 +932,7 @@ int lcc_identifier_to_string(lcc_connection_t *c, /* {{{ */ ident->type_instance); } - string[string_size - 1] = 0; + string[string_size - 1] = '\0'; return 0; } /* }}} int lcc_identifier_to_string */ diff --git a/src/libcollectdclient/network_parse.c b/src/libcollectdclient/network_parse.c index a8f6bd61..73ef0247 100644 --- a/src/libcollectdclient/network_parse.c +++ b/src/libcollectdclient/network_parse.c @@ -154,11 +154,12 @@ static int parse_string(void *payload, size_t payload_size, char *out, size_t out_size) { char *in = payload; - if ((payload_size < 1) || (in[payload_size - 1] != 0) || + if ((payload_size < 1) || (in[payload_size - 1] != '\0') || (payload_size > out_size)) return EINVAL; - strncpy(out, in, out_size); + strncpy(out, in, out_size - 1); + out[out_size - 1] = '\0'; return 0; } @@ -292,7 +293,8 @@ static double ntohd(double val) /* {{{ */ static int parse_values(void *payload, size_t payload_size, lcc_value_list_t *state) { buffer_t *b = &(buffer_t){ - .data = payload, .len = payload_size, + .data = payload, + .len = payload_size, }; uint16_t n; @@ -303,8 +305,8 @@ static int parse_values(void *payload, size_t payload_size, return EINVAL; state->values_len = (size_t)n; - state->values = calloc(sizeof(*state->values), state->values_len); - state->values_types = calloc(sizeof(*state->values_types), state->values_len); + state->values = calloc(state->values_len, sizeof(*state->values)); + state->values_types = calloc(state->values_len, sizeof(*state->values_types)); if ((state->values == NULL) || (state->values_types == NULL)) { return ENOMEM; } @@ -400,7 +402,8 @@ static int parse_sign_sha256(void *signature, size_t signature_len, } buffer_t *b = &(buffer_t){ - .data = signature, .len = signature_len, + .data = signature, + .len = signature_len, }; uint8_t hash[32]; @@ -458,7 +461,8 @@ static int parse_encrypt_aes256(void *data, size_t data_size, } buffer_t *b = &(buffer_t){ - .data = data, .len = data_size, + .data = data, + .len = data_size, }; uint16_t username_len; @@ -507,7 +511,8 @@ static int parse_encrypt_aes256(void *data, size_t data_size, static int network_parse(void *data, size_t data_size, lcc_security_level_t sl, lcc_network_parse_options_t const *opts) { buffer_t *b = &(buffer_t){ - .data = data, .len = data_size, + .data = data, + .len = data_size, }; lcc_value_list_t state = {0}; diff --git a/src/libcollectdclient/network_parse_test.c b/src/libcollectdclient/network_parse_test.c index a638642c..519a2c53 100644 --- a/src/libcollectdclient/network_parse_test.c +++ b/src/libcollectdclient/network_parse_test.c @@ -245,16 +245,17 @@ static int test_network_parse() { uint8_t buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT]; size_t buffer_size = sizeof(buffer); if (decode_string(raw_packet_data[i], buffer, &buffer_size)) { - fprintf(stderr, "lcc_network_parse(raw_packet_data[%" PRIsz "]):" - " decoding string failed\n", + fprintf(stderr, + "lcc_network_parse(raw_packet_data[%" PRIsz "]):" + " decoding string failed\n", i); return -1; } - int status = - lcc_network_parse(buffer, buffer_size, (lcc_network_parse_options_t){ - .writer = nop_writer, - }); + int status = lcc_network_parse(buffer, buffer_size, + (lcc_network_parse_options_t){ + .writer = nop_writer, + }); if (status != 0) { fprintf(stderr, "lcc_network_parse(raw_packet_data[%" PRIsz "]) = %d, want 0\n", @@ -466,7 +467,8 @@ static int test_decrypt_aes256() { int status = decrypt_aes256( &(buffer_t){ - .data = ciphertext, .len = ciphertext_len, + .data = ciphertext, + .len = ciphertext_len, }, iv, iv_len, "admin"); if (status != 0) { diff --git a/src/liboconfig/aux_types.h b/src/liboconfig/aux_types.h index 25b81ab6..68edce80 100644 --- a/src/liboconfig/aux_types.h +++ b/src/liboconfig/aux_types.h @@ -1,17 +1,15 @@ #ifndef AUX_TYPES_H #define AUX_TYPES_H 1 -struct statement_list_s -{ - oconfig_item_t *statement; - int statement_num; +struct statement_list_s { + oconfig_item_t *statement; + int statement_num; }; typedef struct statement_list_s statement_list_t; -struct argument_list_s -{ - oconfig_value_t *argument; - int argument_num; +struct argument_list_s { + oconfig_value_t *argument; + int argument_num; }; typedef struct argument_list_s argument_list_t; diff --git a/src/liboconfig/oconfig.c b/src/liboconfig/oconfig.c index 0ffda3aa..e05ced87 100644 --- a/src/liboconfig/oconfig.c +++ b/src/liboconfig/oconfig.c @@ -115,8 +115,7 @@ oconfig_item_t *oconfig_clone(const oconfig_item_t *ci_orig) { if (ci_orig->values_num > 0) /* {{{ */ { - ci_copy->values = (oconfig_value_t *)calloc((size_t)ci_orig->values_num, - sizeof(*ci_copy->values)); + ci_copy->values = calloc(ci_orig->values_num, sizeof(*ci_copy->values)); if (ci_copy->values == NULL) { fprintf(stderr, "calloc failed.\n"); free(ci_copy->key); @@ -144,8 +143,8 @@ oconfig_item_t *oconfig_clone(const oconfig_item_t *ci_orig) { if (ci_orig->children_num > 0) /* {{{ */ { - ci_copy->children = (oconfig_item_t *)calloc((size_t)ci_orig->children_num, - sizeof(*ci_copy->children)); + ci_copy->children = + calloc(ci_orig->children_num, sizeof(*ci_copy->children)); if (ci_copy->children == NULL) { fprintf(stderr, "calloc failed.\n"); oconfig_free(ci_copy); diff --git a/src/load.c b/src/load.c index 858d9bea..a94ee960 100644 --- a/src/load.c +++ b/src/load.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -61,7 +61,7 @@ static const char *config_keys[] = {"ReportRelative"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); static int load_config(const char *key, const char *value) { - if (strcasecmp(key, "ReportRelative") == 0) + if (strcasecmp(key, "ReportRelative") == 0) { #ifdef _SC_NPROCESSORS_ONLN report_relative_load = IS_TRUE(value); #else @@ -69,6 +69,8 @@ static int load_config(const char *key, const char *value) { "is not available, because I can't determine the " "number of CPUS on this system. Sorry."); #endif + return 0; + } return -1; } static void load_submit(gauge_t snum, gauge_t mnum, gauge_t lnum) { @@ -89,7 +91,9 @@ static void load_submit(gauge_t snum, gauge_t mnum, gauge_t lnum) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.gauge = snum}, {.gauge = mnum}, {.gauge = lnum}, + {.gauge = snum}, + {.gauge = mnum}, + {.gauge = lnum}, }; vl.values = values; @@ -114,7 +118,7 @@ static int load_read(void) { else { WARNING("load: getloadavg failed: %s", STRERRNO); } -/* #endif HAVE_GETLOADAVG */ + /* #endif HAVE_GETLOADAVG */ #elif defined(KERNEL_LINUX) gauge_t snum, mnum, lnum; @@ -149,7 +153,7 @@ static int load_read(void) { lnum = atof(fields[2]); load_submit(snum, mnum, lnum); -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_LIBSTATGRAB gauge_t snum, mnum, lnum; @@ -162,7 +166,7 @@ static int load_read(void) { mnum = ls->min5; lnum = ls->min15; load_submit(snum, mnum, lnum); -/* #endif HAVE_LIBSTATGRAB */ + /* #endif HAVE_LIBSTATGRAB */ #elif HAVE_PERFSTAT gauge_t snum, mnum, lnum; @@ -178,7 +182,7 @@ static int load_read(void) { mnum = (float)cputotal.loadavg[1] / (float)(1 << SBITS); lnum = (float)cputotal.loadavg[2] / (float)(1 << SBITS); load_submit(snum, mnum, lnum); -/* #endif HAVE_PERFSTAT */ + /* #endif HAVE_PERFSTAT */ #else #error "No applicable input method." diff --git a/src/log_logstash.c b/src/log_logstash.c index 8f3063f3..b04aaddd 100644 --- a/src/log_logstash.c +++ b/src/log_logstash.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -182,22 +182,14 @@ err: static void log_logstash_log(int severity, const char *msg, user_data_t __attribute__((unused)) * user_data) { - yajl_gen g; -#if !defined(HAVE_YAJL_V2) - yajl_gen_config conf = {}; - - conf.beautify = 0; -#endif - if (severity > log_level) return; #if HAVE_YAJL_V2 - g = yajl_gen_alloc(NULL); + yajl_gen g = yajl_gen_alloc(NULL); #else - g = yajl_gen_alloc(&conf, NULL); + yajl_gen g = yajl_gen_alloc(&(yajl_gen_config){0}, NULL); #endif - if (g == NULL) { fprintf(stderr, "Could not allocate JSON generator.\n"); return; diff --git a/src/logfile.c b/src/logfile.c index fa56a1bc..ef75052a 100644 --- a/src/logfile.c +++ b/src/logfile.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if COLLECT_DEBUG static int log_level = LOG_DEBUG; @@ -52,8 +52,8 @@ static int logfile_config(const char *key, const char *value) { log_level = parse_log_severity(value); if (log_level < 0) { log_level = LOG_INFO; - ERROR("logfile: invalid loglevel [%s] defaulting to 'info'", value); - return 1; + WARNING("logfile: invalid loglevel [%s] defaulting to 'info'", value); + return 0; } } else if (0 == strcasecmp(key, "File")) { sfree(log_file); diff --git a/src/lpar.c b/src/lpar.c index df18b525..1c8685ed 100644 --- a/src/lpar.c +++ b/src/lpar.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -224,10 +224,10 @@ static int lpar_read(void) { if (pool_busy_cpus < 0.0) pool_busy_cpus = 0.0; - snprintf(typinst, sizeof(typinst), "pool-%X-busy", lparstats.pool_id); + ssnprintf(typinst, sizeof(typinst), "pool-%X-busy", lparstats.pool_id); lpar_submit(typinst, pool_busy_cpus); - snprintf(typinst, sizeof(typinst), "pool-%X-idle", lparstats.pool_id); + ssnprintf(typinst, sizeof(typinst), "pool-%X-idle", lparstats.pool_id); lpar_submit(typinst, pool_idle_cpus); } diff --git a/src/lua.c b/src/lua.c index f66d8526..3f48a55d 100644 --- a/src/lua.c +++ b/src/lua.c @@ -29,8 +29,8 @@ **/ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_lua.h" /* Include the Lua API header files. */ @@ -40,15 +40,17 @@ #include +#define PLUGIN_READ 1 +#define PLUGIN_WRITE 2 + typedef struct lua_script_s { - char *script_path; lua_State *lua_state; struct lua_script_s *next; } lua_script_t; typedef struct { lua_State *lua_state; - const char *lua_function_name; + char *lua_function_name; pthread_mutex_t lock; int callback_id; } clua_callback_data_t; @@ -80,18 +82,14 @@ static int clua_load_callback(lua_State *L, int callback_ref) /* {{{ */ * garbage collector. */ static int clua_store_thread(lua_State *L, int idx) /* {{{ */ { - if (idx < 0) - idx += lua_gettop(L) + 1; - - /* Copy the thread pointer */ - lua_pushvalue(L, idx); /* +1 = 3 */ - if (!lua_isthread(L, -1)) { - lua_pop(L, 3); /* -3 = 0 */ + if (!lua_isthread(L, idx)) { return -1; } + /* Copy the thread pointer */ + lua_pushvalue(L, idx); + luaL_ref(L, LUA_REGISTRYINDEX); - lua_pop(L, 1); /* -1 = 0 */ return 0; } /* }}} int clua_store_thread */ @@ -263,61 +261,46 @@ static int lua_cb_dispatch_values(lua_State *L) /* {{{ */ return 0; } /* }}} lua_cb_dispatch_values */ -static int lua_cb_register_read(lua_State *L) /* {{{ */ +static void lua_cb_free(void *data) { + clua_callback_data_t *cb = data; + free(cb->lua_function_name); + pthread_mutex_destroy(&cb->lock); + free(cb); +} + +static int lua_cb_register_generic(lua_State *L, int type) /* {{{ */ { int nargs = lua_gettop(L); if (nargs != 1) return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs); - luaL_checktype(L, 1, LUA_TFUNCTION); - - char function_name[DATA_MAX_NAME_LEN]; - snprintf(function_name, sizeof(function_name), "lua/%s", lua_tostring(L, 1)); - - int callback_id = clua_store_callback(L, 1); - if (callback_id < 0) - return luaL_error(L, "%s", "Storing callback function failed"); - - lua_State *thread = lua_newthread(L); - if (thread == NULL) - return luaL_error(L, "%s", "lua_newthread failed"); - clua_store_thread(L, -1); - lua_pop(L, 1); - - clua_callback_data_t *cb = calloc(1, sizeof(*cb)); - if (cb == NULL) - return luaL_error(L, "%s", "calloc failed"); - - cb->lua_state = thread; - cb->callback_id = callback_id; - cb->lua_function_name = strdup(function_name); - pthread_mutex_init(&cb->lock, NULL); - - int status = plugin_register_complex_read(/* group = */ "lua", - /* name = */ function_name, - /* callback = */ clua_read, - /* interval = */ 0, - &(user_data_t){ - .data = cb, - }); + char subname[DATA_MAX_NAME_LEN]; + if (!lua_isfunction(L, 1) && lua_isstring(L, 1)) { + const char *fname = lua_tostring(L, 1); + ssnprintf(subname, sizeof(subname), "%s()", fname); - if (status != 0) - return luaL_error(L, "%s", "plugin_register_complex_read failed"); - return 0; -} /* }}} int lua_cb_register_read */ - -static int lua_cb_register_write(lua_State *L) /* {{{ */ -{ - int nargs = lua_gettop(L); - - if (nargs != 1) - return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs); + lua_getglobal(L, fname); // Push function into stack + lua_remove(L, 1); // Remove string from stack + if (!lua_isfunction(L, -1)) { + return luaL_error(L, "Unable to find function '%s'", fname); + } + } else { + lua_getfield(L, LUA_REGISTRYINDEX, "collectd:callback_num"); + int tmp = lua_tointeger(L, -1); + ssnprintf(subname, sizeof(subname), "callback_%d", tmp); + lua_pop(L, 1); // Remove old value from stack + lua_pushinteger(L, tmp + 1); + lua_setfield(L, LUA_REGISTRYINDEX, "collectd:callback_num"); // pops value + } luaL_checktype(L, 1, LUA_TFUNCTION); - char function_name[DATA_MAX_NAME_LEN] = ""; - snprintf(function_name, sizeof(function_name), "lua/%s", lua_tostring(L, 1)); + lua_getfield(L, LUA_REGISTRYINDEX, "collectd:script_path"); + char function_name[DATA_MAX_NAME_LEN]; + ssnprintf(function_name, sizeof(function_name), "lua/%s/%s", + lua_tostring(L, -1), subname); + lua_pop(L, 1); int callback_id = clua_store_callback(L, 1); if (callback_id < 0) @@ -338,16 +321,42 @@ static int lua_cb_register_write(lua_State *L) /* {{{ */ cb->lua_function_name = strdup(function_name); pthread_mutex_init(&cb->lock, NULL); - int status = plugin_register_write(/* name = */ function_name, - /* callback = */ clua_write, - &(user_data_t){ - .data = cb, - }); + if (PLUGIN_READ == type) { + int status = plugin_register_complex_read(/* group = */ "lua", + /* name = */ function_name, + /* callback = */ clua_read, + /* interval = */ 0, + &(user_data_t){ + .data = cb, + .free_func = lua_cb_free, + }); + + if (status != 0) + return luaL_error(L, "%s", "plugin_register_complex_read failed"); + return 0; + } else if (PLUGIN_WRITE == type) { + int status = plugin_register_write(/* name = */ function_name, + /* callback = */ clua_write, + &(user_data_t){ + .data = cb, + .free_func = lua_cb_free, + }); + + if (status != 0) + return luaL_error(L, "%s", "plugin_register_write failed"); + return 0; + } else { + return luaL_error(L, "%s", "lua_cb_register_generic unsupported type"); + } +} /* }}} int lua_cb_register_generic */ - if (status != 0) - return luaL_error(L, "%s", "plugin_register_write failed"); - return 0; -} /* }}} int lua_cb_register_write */ +static int lua_cb_register_read(lua_State *L) { + return lua_cb_register_generic(L, PLUGIN_READ); +} + +static int lua_cb_register_write(lua_State *L) { + return lua_cb_register_generic(L, PLUGIN_WRITE); +} static const luaL_Reg collectdlib[] = { {"log_debug", lua_cb_log_debug}, @@ -382,7 +391,6 @@ static void lua_script_free(lua_script_t *script) /* {{{ */ script->lua_state = NULL; } - sfree(script->script_path); sfree(script); lua_script_free(next); @@ -446,14 +454,7 @@ static int lua_script_load(const char *script_path) /* {{{ */ return status; } - script->script_path = strdup(script_path); - if (script->script_path == NULL) { - ERROR("Lua plugin: strdup failed."); - lua_script_free(script); - return -1; - } - - status = luaL_loadfile(script->lua_state, script->script_path); + status = luaL_loadfile(script->lua_state, script_path); if (status != 0) { ERROR("Lua plugin: luaL_loadfile failed: %s", lua_tostring(script->lua_state, -1)); @@ -462,6 +463,11 @@ static int lua_script_load(const char *script_path) /* {{{ */ return -1; } + lua_pushstring(script->lua_state, script_path); + lua_setfield(script->lua_state, LUA_REGISTRYINDEX, "collectd:script_path"); + lua_pushinteger(script->lua_state, 0); + lua_setfield(script->lua_state, LUA_REGISTRYINDEX, "collectd:callback_num"); + status = lua_pcall(script->lua_state, /* nargs = */ 0, /* nresults = */ LUA_MULTRET, @@ -474,11 +480,8 @@ static int lua_script_load(const char *script_path) /* {{{ */ "In addition, no error message could be retrieved from the stack.", status); else - ERROR("Lua plugin: Executing script \"%s\" failed:\n%s", - script->script_path, errmsg); - - lua_script_free(script); - return -1; + ERROR("Lua plugin: Executing script \"%s\" failed: %s", script_path, + errmsg); } /* Append this script to the global list of scripts. */ @@ -492,6 +495,9 @@ static int lua_script_load(const char *script_path) /* {{{ */ scripts = script; } + if (status != 0) + return -1; + return 0; } /* }}} int lua_script_load */ @@ -525,7 +531,7 @@ static int lua_config_script(const oconfig_item_t *ci) /* {{{ */ if (base_path[0] == '\0') sstrncpy(abs_path, rel_path, sizeof(abs_path)); else - snprintf(abs_path, sizeof(abs_path), "%s/%s", base_path, rel_path); + ssnprintf(abs_path, sizeof(abs_path), "%s/%s", base_path, rel_path); DEBUG("Lua plugin: abs_path = \"%s\";", abs_path); diff --git a/src/lvm.c b/src/lvm.c index 3ec79dea..3077c93c 100644 --- a/src/lvm.c +++ b/src/lvm.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -81,7 +81,8 @@ static void report_lv_utilization(lv_t lv, char const *vg_name, return; used_bytes = lv_size * (used_percent_unscaled * PERCENT_SCALE_FACTOR); - snprintf(plugin_instance, sizeof(plugin_instance), "%s-%s", vg_name, lv_name); + ssnprintf(plugin_instance, sizeof(plugin_instance), "%s-%s", vg_name, + lv_name); lvm_submit(plugin_instance, "used", used_bytes); lvm_submit(plugin_instance, "free", lv_size - used_bytes); } diff --git a/src/madwifi.c b/src/madwifi.c index 60ac3c8c..723f9923 100644 --- a/src/madwifi.c +++ b/src/madwifi.c @@ -88,9 +88,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include @@ -534,7 +534,8 @@ static void submit_derive(const char *dev, const char *type, const char *ti1, static void submit_derive2(const char *dev, const char *type, const char *ti1, const char *ti2, derive_t val1, derive_t val2) { value_t values[] = { - {.derive = val1}, {.derive = val2}, + {.derive = val1}, + {.derive = val2}, }; submit(dev, type, ti1, ti2, values, STATIC_ARRAY_SIZE(values)); diff --git a/src/madwifi.h b/src/madwifi.h index d6a5e355..3a7c7f0d 100644 --- a/src/madwifi.h +++ b/src/madwifi.h @@ -36,166 +36,165 @@ #ifndef MADWIFI_H #define MADWIFI_H -#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */ -#define IEEE80211_RATE_VAL 0x7f -#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ -#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ - +#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */ +#define IEEE80211_RATE_VAL 0x7f +#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ +#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ /* * Per/node (station) statistics available when operating as an AP. */ struct ieee80211_nodestats { - u_int32_t ns_rx_data; /* rx data frames */ - u_int32_t ns_rx_mgmt; /* rx management frames */ - u_int32_t ns_rx_ctrl; /* rx control frames */ - u_int32_t ns_rx_ucast; /* rx unicast frames */ - u_int32_t ns_rx_mcast; /* rx multi/broadcast frames */ - u_int64_t ns_rx_bytes; /* rx data count (bytes) */ - u_int64_t ns_rx_beacons; /* rx beacon frames */ - u_int32_t ns_rx_proberesp; /* rx probe response frames */ + u_int32_t ns_rx_data; /* rx data frames */ + u_int32_t ns_rx_mgmt; /* rx management frames */ + u_int32_t ns_rx_ctrl; /* rx control frames */ + u_int32_t ns_rx_ucast; /* rx unicast frames */ + u_int32_t ns_rx_mcast; /* rx multi/broadcast frames */ + u_int64_t ns_rx_bytes; /* rx data count (bytes) */ + u_int64_t ns_rx_beacons; /* rx beacon frames */ + u_int32_t ns_rx_proberesp; /* rx probe response frames */ - u_int32_t ns_rx_dup; /* rx discard because it's a dup */ - u_int32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */ - u_int32_t ns_rx_wepfail; /* rx wep processing failed */ - u_int32_t ns_rx_demicfail; /* rx demic failed */ - u_int32_t ns_rx_decap; /* rx decapsulation failed */ - u_int32_t ns_rx_defrag; /* rx defragmentation failed */ - u_int32_t ns_rx_disassoc; /* rx disassociation */ - u_int32_t ns_rx_deauth; /* rx deauthentication */ - u_int32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */ - u_int32_t ns_rx_unauth; /* rx on unauthorized port */ - u_int32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */ + u_int32_t ns_rx_dup; /* rx discard because it's a dup */ + u_int32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */ + u_int32_t ns_rx_wepfail; /* rx wep processing failed */ + u_int32_t ns_rx_demicfail; /* rx demic failed */ + u_int32_t ns_rx_decap; /* rx decapsulation failed */ + u_int32_t ns_rx_defrag; /* rx defragmentation failed */ + u_int32_t ns_rx_disassoc; /* rx disassociation */ + u_int32_t ns_rx_deauth; /* rx deauthentication */ + u_int32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */ + u_int32_t ns_rx_unauth; /* rx on unauthorized port */ + u_int32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */ - u_int32_t ns_tx_data; /* tx data frames */ - u_int32_t ns_tx_mgmt; /* tx management frames */ - u_int32_t ns_tx_ucast; /* tx unicast frames */ - u_int32_t ns_tx_mcast; /* tx multi/broadcast frames */ - u_int64_t ns_tx_bytes; /* tx data count (bytes) */ - u_int32_t ns_tx_probereq; /* tx probe request frames */ - u_int32_t ns_tx_uapsd; /* tx on uapsd queue */ + u_int32_t ns_tx_data; /* tx data frames */ + u_int32_t ns_tx_mgmt; /* tx management frames */ + u_int32_t ns_tx_ucast; /* tx unicast frames */ + u_int32_t ns_tx_mcast; /* tx multi/broadcast frames */ + u_int64_t ns_tx_bytes; /* tx data count (bytes) */ + u_int32_t ns_tx_probereq; /* tx probe request frames */ + u_int32_t ns_tx_uapsd; /* tx on uapsd queue */ - u_int32_t ns_tx_novlantag; /* tx discard due to no tag */ - u_int32_t ns_tx_vlanmismatch; /* tx discard due to of bad tag */ + u_int32_t ns_tx_novlantag; /* tx discard due to no tag */ + u_int32_t ns_tx_vlanmismatch; /* tx discard due to of bad tag */ - u_int32_t ns_tx_eosplost; /* uapsd EOSP retried out */ + u_int32_t ns_tx_eosplost; /* uapsd EOSP retried out */ - u_int32_t ns_ps_discard; /* ps discard due to of age */ + u_int32_t ns_ps_discard; /* ps discard due to of age */ - u_int32_t ns_uapsd_triggers; /* uapsd triggers */ + u_int32_t ns_uapsd_triggers; /* uapsd triggers */ - /* MIB-related state */ - u_int32_t ns_tx_assoc; /* [re]associations */ - u_int32_t ns_tx_assoc_fail; /* [re]association failures */ - u_int32_t ns_tx_auth; /* [re]authentications */ - u_int32_t ns_tx_auth_fail; /* [re]authentication failures*/ - u_int32_t ns_tx_deauth; /* deauthentications */ - u_int32_t ns_tx_deauth_code; /* last deauth reason */ - u_int32_t ns_tx_disassoc; /* disassociations */ - u_int32_t ns_tx_disassoc_code; /* last disassociation reason */ - u_int32_t ns_psq_drops; /* power save queue drops */ + /* MIB-related state */ + u_int32_t ns_tx_assoc; /* [re]associations */ + u_int32_t ns_tx_assoc_fail; /* [re]association failures */ + u_int32_t ns_tx_auth; /* [re]authentications */ + u_int32_t ns_tx_auth_fail; /* [re]authentication failures*/ + u_int32_t ns_tx_deauth; /* deauthentications */ + u_int32_t ns_tx_deauth_code; /* last deauth reason */ + u_int32_t ns_tx_disassoc; /* disassociations */ + u_int32_t ns_tx_disassoc_code; /* last disassociation reason */ + u_int32_t ns_psq_drops; /* power save queue drops */ }; /* * Summary statistics. */ struct ieee80211_stats { - u_int32_t is_rx_badversion; /* rx frame with bad version */ - u_int32_t is_rx_tooshort; /* rx frame too short */ - u_int32_t is_rx_wrongbss; /* rx from wrong bssid */ - u_int32_t is_rx_dup; /* rx discard due to it's a dup */ - u_int32_t is_rx_wrongdir; /* rx w/ wrong direction */ - u_int32_t is_rx_mcastecho; /* rx discard due to of mcast echo */ - u_int32_t is_rx_notassoc; /* rx discard due to sta !assoc */ - u_int32_t is_rx_noprivacy; /* rx w/ wep but privacy off */ - u_int32_t is_rx_unencrypted; /* rx w/o wep and privacy on */ - u_int32_t is_rx_wepfail; /* rx wep processing failed */ - u_int32_t is_rx_decap; /* rx decapsulation failed */ - u_int32_t is_rx_mgtdiscard; /* rx discard mgt frames */ - u_int32_t is_rx_ctl; /* rx discard ctrl frames */ - u_int32_t is_rx_beacon; /* rx beacon frames */ - u_int32_t is_rx_rstoobig; /* rx rate set truncated */ - u_int32_t is_rx_elem_missing; /* rx required element missing*/ - u_int32_t is_rx_elem_toobig; /* rx element too big */ - u_int32_t is_rx_elem_toosmall; /* rx element too small */ - u_int32_t is_rx_elem_unknown; /* rx element unknown */ - u_int32_t is_rx_badchan; /* rx frame w/ invalid chan */ - u_int32_t is_rx_chanmismatch; /* rx frame chan mismatch */ - u_int32_t is_rx_nodealloc; /* rx frame dropped */ - u_int32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */ - u_int32_t is_rx_auth_unsupported;/* rx w/ unsupported auth alg */ - u_int32_t is_rx_auth_fail; /* rx sta auth failure */ - u_int32_t is_rx_auth_countermeasures;/* rx auth discard due to CM */ - u_int32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */ - u_int32_t is_rx_assoc_notauth; /* rx assoc w/o auth */ - u_int32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */ - u_int32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */ - u_int32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */ - u_int32_t is_rx_deauth; /* rx deauthentication */ - u_int32_t is_rx_disassoc; /* rx disassociation */ - u_int32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/ - u_int32_t is_rx_nobuf; /* rx failed for lack of buf */ - u_int32_t is_rx_decryptcrc; /* rx decrypt failed on crc */ - u_int32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/ - u_int32_t is_rx_bad_auth; /* rx bad auth request */ - u_int32_t is_rx_unauth; /* rx on unauthorized port */ - u_int32_t is_rx_badkeyid; /* rx w/ incorrect keyid */ - u_int32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */ - u_int32_t is_rx_ccmpformat; /* rx format bad (CCMP) */ - u_int32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */ - u_int32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */ - u_int32_t is_rx_tkipformat; /* rx format bad (TKIP) */ - u_int32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */ - u_int32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */ - u_int32_t is_rx_badcipher; /* rx failed due to of key type */ - u_int32_t is_rx_nocipherctx; /* rx failed due to key !setup */ - u_int32_t is_rx_acl; /* rx discard due to of acl policy */ - u_int32_t is_rx_ffcnt; /* rx fast frames */ - u_int32_t is_rx_badathtnl; /* driver key alloc failed */ - u_int32_t is_tx_nobuf; /* tx failed for lack of buf */ - u_int32_t is_tx_nonode; /* tx failed for no node */ - u_int32_t is_tx_unknownmgt; /* tx of unknown mgt frame */ - u_int32_t is_tx_badcipher; /* tx failed due to of key type */ - u_int32_t is_tx_nodefkey; /* tx failed due to no defkey */ - u_int32_t is_tx_noheadroom; /* tx failed due to no space */ - u_int32_t is_tx_ffokcnt; /* tx fast frames sent success */ - u_int32_t is_tx_fferrcnt; /* tx fast frames sent success */ - u_int32_t is_scan_active; /* active scans started */ - u_int32_t is_scan_passive; /* passive scans started */ - u_int32_t is_node_timeout; /* nodes timed out inactivity */ - u_int32_t is_crypto_nomem; /* no memory for crypto ctx */ - u_int32_t is_crypto_tkip; /* tkip crypto done in s/w */ - u_int32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */ - u_int32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */ - u_int32_t is_crypto_tkipcm; /* tkip counter measures */ - u_int32_t is_crypto_ccmp; /* ccmp crypto done in s/w */ - u_int32_t is_crypto_wep; /* wep crypto done in s/w */ - u_int32_t is_crypto_setkey_cipher;/* cipher rejected key */ - u_int32_t is_crypto_setkey_nokey;/* no key index for setkey */ - u_int32_t is_crypto_delkey; /* driver key delete failed */ - u_int32_t is_crypto_badcipher; /* unknown cipher */ - u_int32_t is_crypto_nocipher; /* cipher not available */ - u_int32_t is_crypto_attachfail; /* cipher attach failed */ - u_int32_t is_crypto_swfallback; /* cipher fallback to s/w */ - u_int32_t is_crypto_keyfail; /* driver key alloc failed */ - u_int32_t is_crypto_enmicfail; /* en-MIC failed */ - u_int32_t is_ibss_capmismatch; /* merge failed-cap mismatch */ - u_int32_t is_ibss_norate; /* merge failed-rate mismatch */ - u_int32_t is_ps_unassoc; /* ps-poll for unassoc. sta */ - u_int32_t is_ps_badaid; /* ps-poll w/ incorrect aid */ - u_int32_t is_ps_qempty; /* ps-poll w/ nothing to send */ + u_int32_t is_rx_badversion; /* rx frame with bad version */ + u_int32_t is_rx_tooshort; /* rx frame too short */ + u_int32_t is_rx_wrongbss; /* rx from wrong bssid */ + u_int32_t is_rx_dup; /* rx discard due to it's a dup */ + u_int32_t is_rx_wrongdir; /* rx w/ wrong direction */ + u_int32_t is_rx_mcastecho; /* rx discard due to of mcast echo */ + u_int32_t is_rx_notassoc; /* rx discard due to sta !assoc */ + u_int32_t is_rx_noprivacy; /* rx w/ wep but privacy off */ + u_int32_t is_rx_unencrypted; /* rx w/o wep and privacy on */ + u_int32_t is_rx_wepfail; /* rx wep processing failed */ + u_int32_t is_rx_decap; /* rx decapsulation failed */ + u_int32_t is_rx_mgtdiscard; /* rx discard mgt frames */ + u_int32_t is_rx_ctl; /* rx discard ctrl frames */ + u_int32_t is_rx_beacon; /* rx beacon frames */ + u_int32_t is_rx_rstoobig; /* rx rate set truncated */ + u_int32_t is_rx_elem_missing; /* rx required element missing*/ + u_int32_t is_rx_elem_toobig; /* rx element too big */ + u_int32_t is_rx_elem_toosmall; /* rx element too small */ + u_int32_t is_rx_elem_unknown; /* rx element unknown */ + u_int32_t is_rx_badchan; /* rx frame w/ invalid chan */ + u_int32_t is_rx_chanmismatch; /* rx frame chan mismatch */ + u_int32_t is_rx_nodealloc; /* rx frame dropped */ + u_int32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */ + u_int32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */ + u_int32_t is_rx_auth_fail; /* rx sta auth failure */ + u_int32_t is_rx_auth_countermeasures; /* rx auth discard due to CM */ + u_int32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */ + u_int32_t is_rx_assoc_notauth; /* rx assoc w/o auth */ + u_int32_t is_rx_assoc_capmismatch; /* rx assoc w/ cap mismatch */ + u_int32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */ + u_int32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */ + u_int32_t is_rx_deauth; /* rx deauthentication */ + u_int32_t is_rx_disassoc; /* rx disassociation */ + u_int32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/ + u_int32_t is_rx_nobuf; /* rx failed for lack of buf */ + u_int32_t is_rx_decryptcrc; /* rx decrypt failed on crc */ + u_int32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/ + u_int32_t is_rx_bad_auth; /* rx bad auth request */ + u_int32_t is_rx_unauth; /* rx on unauthorized port */ + u_int32_t is_rx_badkeyid; /* rx w/ incorrect keyid */ + u_int32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */ + u_int32_t is_rx_ccmpformat; /* rx format bad (CCMP) */ + u_int32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */ + u_int32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */ + u_int32_t is_rx_tkipformat; /* rx format bad (TKIP) */ + u_int32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */ + u_int32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */ + u_int32_t is_rx_badcipher; /* rx failed due to of key type */ + u_int32_t is_rx_nocipherctx; /* rx failed due to key !setup */ + u_int32_t is_rx_acl; /* rx discard due to of acl policy */ + u_int32_t is_rx_ffcnt; /* rx fast frames */ + u_int32_t is_rx_badathtnl; /* driver key alloc failed */ + u_int32_t is_tx_nobuf; /* tx failed for lack of buf */ + u_int32_t is_tx_nonode; /* tx failed for no node */ + u_int32_t is_tx_unknownmgt; /* tx of unknown mgt frame */ + u_int32_t is_tx_badcipher; /* tx failed due to of key type */ + u_int32_t is_tx_nodefkey; /* tx failed due to no defkey */ + u_int32_t is_tx_noheadroom; /* tx failed due to no space */ + u_int32_t is_tx_ffokcnt; /* tx fast frames sent success */ + u_int32_t is_tx_fferrcnt; /* tx fast frames sent success */ + u_int32_t is_scan_active; /* active scans started */ + u_int32_t is_scan_passive; /* passive scans started */ + u_int32_t is_node_timeout; /* nodes timed out inactivity */ + u_int32_t is_crypto_nomem; /* no memory for crypto ctx */ + u_int32_t is_crypto_tkip; /* tkip crypto done in s/w */ + u_int32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */ + u_int32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */ + u_int32_t is_crypto_tkipcm; /* tkip counter measures */ + u_int32_t is_crypto_ccmp; /* ccmp crypto done in s/w */ + u_int32_t is_crypto_wep; /* wep crypto done in s/w */ + u_int32_t is_crypto_setkey_cipher; /* cipher rejected key */ + u_int32_t is_crypto_setkey_nokey; /* no key index for setkey */ + u_int32_t is_crypto_delkey; /* driver key delete failed */ + u_int32_t is_crypto_badcipher; /* unknown cipher */ + u_int32_t is_crypto_nocipher; /* cipher not available */ + u_int32_t is_crypto_attachfail; /* cipher attach failed */ + u_int32_t is_crypto_swfallback; /* cipher fallback to s/w */ + u_int32_t is_crypto_keyfail; /* driver key alloc failed */ + u_int32_t is_crypto_enmicfail; /* en-MIC failed */ + u_int32_t is_ibss_capmismatch; /* merge failed-cap mismatch */ + u_int32_t is_ibss_norate; /* merge failed-rate mismatch */ + u_int32_t is_ps_unassoc; /* ps-poll for unassoc. sta */ + u_int32_t is_ps_badaid; /* ps-poll w/ incorrect aid */ + u_int32_t is_ps_qempty; /* ps-poll w/ nothing to send */ }; /* * Retrieve per-node statistics. */ struct ieee80211req_sta_stats { - union { - /* NB: explicitly force 64-bit alignment */ - u_int8_t macaddr[IEEE80211_ADDR_LEN]; - u_int64_t pad; - } is_u; - struct ieee80211_nodestats is_stats; + union { + /* NB: explicitly force 64-bit alignment */ + u_int8_t macaddr[IEEE80211_ADDR_LEN]; + u_int64_t pad; + } is_u; + struct ieee80211_nodestats is_stats; }; /* @@ -203,105 +202,103 @@ struct ieee80211req_sta_stats { * to retrieve other data like stats, unicast key, etc. */ struct ieee80211req_sta_info { - u_int16_t isi_len; /* length (mult of 4) */ - u_int16_t isi_freq; /* MHz */ - u_int16_t isi_flags; /* channel flags */ - u_int16_t isi_state; /* state flags */ - u_int8_t isi_authmode; /* authentication algorithm */ - u_int8_t isi_rssi; - u_int16_t isi_capinfo; /* capabilities */ - u_int8_t isi_athflags; /* Atheros capabilities */ - u_int8_t isi_erp; /* ERP element */ - u_int8_t isi_macaddr[IEEE80211_ADDR_LEN]; - u_int8_t isi_nrates; /* negotiated rates */ - u_int8_t isi_rates[IEEE80211_RATE_MAXSIZE]; - u_int8_t isi_txrate; /* index to isi_rates[] */ - u_int16_t isi_ie_len; /* IE length */ - u_int16_t isi_associd; /* assoc response */ - u_int16_t isi_txpower; /* current tx power */ - u_int16_t isi_vlan; /* vlan tag */ - u_int16_t isi_txseqs[17]; /* seq to be transmitted */ - u_int16_t isi_rxseqs[17]; /* seq previous for qos frames*/ - u_int16_t isi_inact; /* inactivity timer */ - u_int8_t isi_uapsd; /* UAPSD queues */ - u_int8_t isi_opmode; /* sta operating mode */ + u_int16_t isi_len; /* length (mult of 4) */ + u_int16_t isi_freq; /* MHz */ + u_int16_t isi_flags; /* channel flags */ + u_int16_t isi_state; /* state flags */ + u_int8_t isi_authmode; /* authentication algorithm */ + u_int8_t isi_rssi; + u_int16_t isi_capinfo; /* capabilities */ + u_int8_t isi_athflags; /* Atheros capabilities */ + u_int8_t isi_erp; /* ERP element */ + u_int8_t isi_macaddr[IEEE80211_ADDR_LEN]; + u_int8_t isi_nrates; /* negotiated rates */ + u_int8_t isi_rates[IEEE80211_RATE_MAXSIZE]; + u_int8_t isi_txrate; /* index to isi_rates[] */ + u_int16_t isi_ie_len; /* IE length */ + u_int16_t isi_associd; /* assoc response */ + u_int16_t isi_txpower; /* current tx power */ + u_int16_t isi_vlan; /* vlan tag */ + u_int16_t isi_txseqs[17]; /* seq to be transmitted */ + u_int16_t isi_rxseqs[17]; /* seq previous for qos frames*/ + u_int16_t isi_inact; /* inactivity timer */ + u_int8_t isi_uapsd; /* UAPSD queues */ + u_int8_t isi_opmode; /* sta operating mode */ - /* XXX frag state? */ - /* variable length IE data */ + /* XXX frag state? */ + /* variable length IE data */ }; - struct ath_stats { - u_int32_t ast_watchdog; /* device reset by watchdog */ - u_int32_t ast_hardware; /* fatal hardware error interrupts */ - u_int32_t ast_bmiss; /* beacon miss interrupts */ - u_int32_t ast_rxorn; /* rx overrun interrupts */ - u_int32_t ast_rxeol; /* rx eol interrupts */ - u_int32_t ast_txurn; /* tx underrun interrupts */ - u_int32_t ast_mib; /* mib interrupts */ - u_int32_t ast_tx_packets; /* packet sent on the interface */ - u_int32_t ast_tx_mgmt; /* management frames transmitted */ - u_int32_t ast_tx_discard; /* frames discarded prior to assoc */ - u_int32_t ast_tx_invalid; /* frames discarded due to is device gone */ - u_int32_t ast_tx_qstop; /* tx queue stopped because it's full */ - u_int32_t ast_tx_encap; /* tx encapsulation failed */ - u_int32_t ast_tx_nonode; /* tx failed due to of no node */ - u_int32_t ast_tx_nobuf; /* tx failed due to of no tx buffer (data) */ - u_int32_t ast_tx_nobufmgt; /* tx failed due to of no tx buffer (mgmt)*/ - u_int32_t ast_tx_xretries; /* tx failed due to of too many retries */ - u_int32_t ast_tx_fifoerr; /* tx failed due to of FIFO underrun */ - u_int32_t ast_tx_filtered; /* tx failed due to xmit filtered */ - u_int32_t ast_tx_shortretry; /* tx on-chip retries (short) */ - u_int32_t ast_tx_longretry; /* tx on-chip retries (long) */ - u_int32_t ast_tx_badrate; /* tx failed due to of bogus xmit rate */ - u_int32_t ast_tx_noack; /* tx frames with no ack marked */ - u_int32_t ast_tx_rts; /* tx frames with rts enabled */ - u_int32_t ast_tx_cts; /* tx frames with cts enabled */ - u_int32_t ast_tx_shortpre; /* tx frames with short preamble */ - u_int32_t ast_tx_altrate; /* tx frames with alternate rate */ - u_int32_t ast_tx_protect; /* tx frames with protection */ - u_int32_t ast_rx_orn; /* rx failed due to of desc overrun */ - u_int32_t ast_rx_crcerr; /* rx failed due to of bad CRC */ - u_int32_t ast_rx_fifoerr; /* rx failed due to of FIFO overrun */ - u_int32_t ast_rx_badcrypt; /* rx failed due to of decryption */ - u_int32_t ast_rx_badmic; /* rx failed due to of MIC failure */ - u_int32_t ast_rx_phyerr; /* rx PHY error summary count */ - u_int32_t ast_rx_phy[32]; /* rx PHY error per-code counts */ - u_int32_t ast_rx_tooshort; /* rx discarded due to frame too short */ - u_int32_t ast_rx_toobig; /* rx discarded due to frame too large */ - u_int32_t ast_rx_nobuf; /* rx setup failed due to of no skbuff */ - u_int32_t ast_rx_packets; /* packet recv on the interface */ - u_int32_t ast_rx_mgt; /* management frames received */ - u_int32_t ast_rx_ctl; /* control frames received */ - int8_t ast_tx_rssi; /* tx rssi of last ack */ - int8_t ast_rx_rssi; /* rx rssi from histogram */ - u_int32_t ast_be_xmit; /* beacons transmitted */ - u_int32_t ast_be_nobuf; /* no skbuff available for beacon */ - u_int32_t ast_per_cal; /* periodic calibration calls */ - u_int32_t ast_per_calfail; /* periodic calibration failed */ - u_int32_t ast_per_rfgain; /* periodic calibration rfgain reset */ - u_int32_t ast_rate_calls; /* rate control checks */ - u_int32_t ast_rate_raise; /* rate control raised xmit rate */ - u_int32_t ast_rate_drop; /* rate control dropped xmit rate */ - u_int32_t ast_ant_defswitch; /* rx/default antenna switches */ - u_int32_t ast_ant_txswitch; /* tx antenna switches */ - u_int32_t ast_ant_rx[8]; /* rx frames with antenna */ - u_int32_t ast_ant_tx[8]; /* tx frames with antenna */ + u_int32_t ast_watchdog; /* device reset by watchdog */ + u_int32_t ast_hardware; /* fatal hardware error interrupts */ + u_int32_t ast_bmiss; /* beacon miss interrupts */ + u_int32_t ast_rxorn; /* rx overrun interrupts */ + u_int32_t ast_rxeol; /* rx eol interrupts */ + u_int32_t ast_txurn; /* tx underrun interrupts */ + u_int32_t ast_mib; /* mib interrupts */ + u_int32_t ast_tx_packets; /* packet sent on the interface */ + u_int32_t ast_tx_mgmt; /* management frames transmitted */ + u_int32_t ast_tx_discard; /* frames discarded prior to assoc */ + u_int32_t ast_tx_invalid; /* frames discarded due to is device gone */ + u_int32_t ast_tx_qstop; /* tx queue stopped because it's full */ + u_int32_t ast_tx_encap; /* tx encapsulation failed */ + u_int32_t ast_tx_nonode; /* tx failed due to of no node */ + u_int32_t ast_tx_nobuf; /* tx failed due to of no tx buffer (data) */ + u_int32_t ast_tx_nobufmgt; /* tx failed due to of no tx buffer (mgmt)*/ + u_int32_t ast_tx_xretries; /* tx failed due to of too many retries */ + u_int32_t ast_tx_fifoerr; /* tx failed due to of FIFO underrun */ + u_int32_t ast_tx_filtered; /* tx failed due to xmit filtered */ + u_int32_t ast_tx_shortretry; /* tx on-chip retries (short) */ + u_int32_t ast_tx_longretry; /* tx on-chip retries (long) */ + u_int32_t ast_tx_badrate; /* tx failed due to of bogus xmit rate */ + u_int32_t ast_tx_noack; /* tx frames with no ack marked */ + u_int32_t ast_tx_rts; /* tx frames with rts enabled */ + u_int32_t ast_tx_cts; /* tx frames with cts enabled */ + u_int32_t ast_tx_shortpre; /* tx frames with short preamble */ + u_int32_t ast_tx_altrate; /* tx frames with alternate rate */ + u_int32_t ast_tx_protect; /* tx frames with protection */ + u_int32_t ast_rx_orn; /* rx failed due to of desc overrun */ + u_int32_t ast_rx_crcerr; /* rx failed due to of bad CRC */ + u_int32_t ast_rx_fifoerr; /* rx failed due to of FIFO overrun */ + u_int32_t ast_rx_badcrypt; /* rx failed due to of decryption */ + u_int32_t ast_rx_badmic; /* rx failed due to of MIC failure */ + u_int32_t ast_rx_phyerr; /* rx PHY error summary count */ + u_int32_t ast_rx_phy[32]; /* rx PHY error per-code counts */ + u_int32_t ast_rx_tooshort; /* rx discarded due to frame too short */ + u_int32_t ast_rx_toobig; /* rx discarded due to frame too large */ + u_int32_t ast_rx_nobuf; /* rx setup failed due to of no skbuff */ + u_int32_t ast_rx_packets; /* packet recv on the interface */ + u_int32_t ast_rx_mgt; /* management frames received */ + u_int32_t ast_rx_ctl; /* control frames received */ + int8_t ast_tx_rssi; /* tx rssi of last ack */ + int8_t ast_rx_rssi; /* rx rssi from histogram */ + u_int32_t ast_be_xmit; /* beacons transmitted */ + u_int32_t ast_be_nobuf; /* no skbuff available for beacon */ + u_int32_t ast_per_cal; /* periodic calibration calls */ + u_int32_t ast_per_calfail; /* periodic calibration failed */ + u_int32_t ast_per_rfgain; /* periodic calibration rfgain reset */ + u_int32_t ast_rate_calls; /* rate control checks */ + u_int32_t ast_rate_raise; /* rate control raised xmit rate */ + u_int32_t ast_rate_drop; /* rate control dropped xmit rate */ + u_int32_t ast_ant_defswitch; /* rx/default antenna switches */ + u_int32_t ast_ant_txswitch; /* tx antenna switches */ + u_int32_t ast_ant_rx[8]; /* rx frames with antenna */ + u_int32_t ast_ant_tx[8]; /* tx frames with antenna */ }; -#define SIOCGATHSTATS (SIOCDEVPRIVATE+0) -#define SIOCGATHDIAG (SIOCDEVPRIVATE+1) -#define SIOCGATHRADARSIG (SIOCDEVPRIVATE+2) -#define SIOCGATHHALDIAG (SIOCDEVPRIVATE+3) -#define SIOCG80211STATS (SIOCDEVPRIVATE+2) +#define SIOCGATHSTATS (SIOCDEVPRIVATE + 0) +#define SIOCGATHDIAG (SIOCDEVPRIVATE + 1) +#define SIOCGATHRADARSIG (SIOCDEVPRIVATE + 2) +#define SIOCGATHHALDIAG (SIOCDEVPRIVATE + 3) +#define SIOCG80211STATS (SIOCDEVPRIVATE + 2) /* NB: require in+out parameters so cannot use wireless extensions, yech */ -#define IEEE80211_IOCTL_GETKEY (SIOCDEVPRIVATE+3) -#define IEEE80211_IOCTL_GETWPAIE (SIOCDEVPRIVATE+4) -#define IEEE80211_IOCTL_STA_STATS (SIOCDEVPRIVATE+5) -#define IEEE80211_IOCTL_STA_INFO (SIOCDEVPRIVATE+6) -#define SIOC80211IFCREATE (SIOCDEVPRIVATE+7) -#define SIOC80211IFDESTROY (SIOCDEVPRIVATE+8) -#define IEEE80211_IOCTL_SCAN_RESULTS (SIOCDEVPRIVATE+9) - +#define IEEE80211_IOCTL_GETKEY (SIOCDEVPRIVATE + 3) +#define IEEE80211_IOCTL_GETWPAIE (SIOCDEVPRIVATE + 4) +#define IEEE80211_IOCTL_STA_STATS (SIOCDEVPRIVATE + 5) +#define IEEE80211_IOCTL_STA_INFO (SIOCDEVPRIVATE + 6) +#define SIOC80211IFCREATE (SIOCDEVPRIVATE + 7) +#define SIOC80211IFDESTROY (SIOCDEVPRIVATE + 8) +#define IEEE80211_IOCTL_SCAN_RESULTS (SIOCDEVPRIVATE + 9) #endif diff --git a/src/match_empty_counter.c b/src/match_empty_counter.c index 27817fe5..799e09d5 100644 --- a/src/match_empty_counter.c +++ b/src/match_empty_counter.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" /* * internal helper functions @@ -73,9 +73,9 @@ static int mec_match(__attribute__((unused)) const data_set_t *ds, /* {{{ */ } /* }}} int mec_match */ void module_register(void) { - fc_register_match( - "empty_counter", - (match_proc_t){ - .create = mec_create, .destroy = mec_destroy, .match = mec_match, - }); + fc_register_match("empty_counter", (match_proc_t){ + .create = mec_create, + .destroy = mec_destroy, + .match = mec_match, + }); } /* module_register */ diff --git a/src/match_hashed.c b/src/match_hashed.c index c0554b23..4911ee2c 100644 --- a/src/match_hashed.c +++ b/src/match_hashed.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" /* * private data types diff --git a/src/match_regex.c b/src/match_regex.c index ed1e3295..99f0709b 100644 --- a/src/match_regex.c +++ b/src/match_regex.c @@ -33,9 +33,9 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" -#include "meta_data.h" +#include "utils/common/common.h" +#include "utils/metadata/meta_data.h" #include "utils_llist.h" #include @@ -151,7 +151,7 @@ static int mr_add_regex(mr_regex_t **re_head, const char *re_str, /* {{{ */ if (status != 0) { char errmsg[1024]; regerror(status, &re->re, errmsg, sizeof(errmsg)); - errmsg[sizeof(errmsg) - 1] = 0; + errmsg[sizeof(errmsg) - 1] = '\0'; log_err("Compiling regex `%s' for `%s' failed: %s.", re->re_str, option, errmsg); sfree(re->re_str); @@ -336,21 +336,22 @@ static int mr_match(const data_set_t __attribute__((unused)) * ds, /* {{{ */ if (mr_match_regexen(m->type_instance, vl->type_instance) == FC_MATCH_NO_MATCH) return nomatch_value; - if (vl->meta != NULL) { - for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next) { - mr_regex_t *meta_re = (mr_regex_t *)e->value; - char *value; - int status = meta_data_get_string(vl->meta, e->key, &value); - if (status == (-ENOENT)) /* key is not present */ - return nomatch_value; - if (status != 0) /* some other problem */ - continue; /* error will have already been printed. */ - if (mr_match_regexen(meta_re, value) == FC_MATCH_NO_MATCH) { - sfree(value); - return nomatch_value; - } + for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next) { + mr_regex_t *meta_re = (mr_regex_t *)e->value; + char *value; + int status; + if (vl->meta == NULL) + return nomatch_value; + status = meta_data_get_string(vl->meta, e->key, &value); + if (status == (-ENOENT)) /* key is not present */ + return nomatch_value; + if (status != 0) /* some other problem */ + continue; /* error will have already been printed. */ + if (mr_match_regexen(meta_re, value) == FC_MATCH_NO_MATCH) { sfree(value); + return nomatch_value; } + sfree(value); } return match_value; diff --git a/src/match_timediff.c b/src/match_timediff.c index c80694de..172b3126 100644 --- a/src/match_timediff.c +++ b/src/match_timediff.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" #define SATISFY_ALL 0 #define SATISFY_ANY 1 diff --git a/src/match_value.c b/src/match_value.c index 44cea268..7b9da693 100644 --- a/src/match_value.c +++ b/src/match_value.c @@ -31,8 +31,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" #include "utils_cache.h" #define SATISFY_ALL 0 diff --git a/src/mbmon.c b/src/mbmon.c index 63a300de..50955f3c 100644 --- a/src/mbmon.c +++ b/src/mbmon.c @@ -25,8 +25,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/mcelog.c b/src/mcelog.c index 4e514004..d71195f0 100644 --- a/src/mcelog.c +++ b/src/mcelog.c @@ -31,7 +31,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "utils_llist.h" #include @@ -90,14 +90,16 @@ static int socket_reinit(socket_adapter_t *self); static int socket_receive(socket_adapter_t *self, FILE **p_file); static mcelog_config_t g_mcelog_config = { - .logfile = "/var/log/mcelog", .persist = false, + .logfile = "/var/log/mcelog", + .persist = false, }; static socket_adapter_t socket_adapter = { .sock_fd = -1, .unix_sock = { - .sun_family = AF_UNIX, .sun_path = "/var/run/mcelog-client", + .sun_family = AF_UNIX, + .sun_path = "/var/run/mcelog-client", }, .lock = PTHREAD_RWLOCK_INITIALIZER, .close = socket_close, @@ -523,7 +525,8 @@ static int socket_receive(socket_adapter_t *self, FILE **pp_file) { int res = -1; pthread_rwlock_rdlock(&self->lock); struct pollfd poll_fd = { - .fd = self->sock_fd, .events = POLLIN | POLLPRI, + .fd = self->sock_fd, + .events = POLLIN | POLLPRI, }; if ((res = poll(&poll_fd, 1, MCELOG_POLL_TIMEOUT)) <= 0) { diff --git a/src/md.c b/src/md.c index 0a015c7b..643dabd7 100644 --- a/src/md.c +++ b/src/md.c @@ -21,9 +21,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include diff --git a/src/memcachec.c b/src/memcachec.c index f293aa1b..eefcfb73 100644 --- a/src/memcachec.c +++ b/src/memcachec.c @@ -23,9 +23,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_match.h" +#include "utils/common/common.h" +#include "utils/match/match.h" #include diff --git a/src/memcached.c b/src/memcached.c index 4ff70f7c..c00f53e4 100644 --- a/src/memcached.c +++ b/src/memcached.c @@ -32,8 +32,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -171,7 +171,8 @@ static int memcached_connect_inet(memcached_t *st) { /* Wait until connection establishes */ struct pollfd pollfd = { - .fd = fd, .events = POLLOUT, + .fd = fd, + .events = POLLOUT, }; do status = poll(&pollfd, 1, MEMCACHED_CONNECT_TIMEOUT); @@ -226,7 +227,8 @@ static int memcached_query_daemon(char *buffer, size_t buffer_size, } struct pollfd pollfd = { - .fd = st->fd, .events = POLLOUT, + .fd = st->fd, + .events = POLLOUT, }; do @@ -346,7 +348,8 @@ static void submit_derive2(const char *type, const char *type_inst, derive_t value0, derive_t value1, memcached_t *st) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = value0}, {.derive = value1}, + {.derive = value0}, + {.derive = value1}, }; memcached_init_vl(&vl, st); @@ -377,7 +380,8 @@ static void submit_gauge2(const char *type, const char *type_inst, gauge_t value0, gauge_t value1, memcached_t *st) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.gauge = value0}, {.gauge = value1}, + {.gauge = value0}, + {.gauge = value1}, }; memcached_init_vl(&vl, st); @@ -509,6 +513,13 @@ static int memcached_read(user_data_t *user_data) { } /* + * Number of secs since the server started + */ + else if (FIELD_IS("uptime")) { + submit_gauge("uptime", NULL, atof(fields[2]), st); + } + + /* * Number of bytes used and available (total - used) */ else if (FIELD_IS("bytes")) { @@ -685,7 +696,8 @@ static int memcached_add_read_callback(memcached_t *st) { /* callback = */ memcached_read, /* interval = */ 0, &(user_data_t){ - .data = st, .free_func = memcached_free, + .data = st, + .free_func = memcached_free, }); } /* int memcached_add_read_callback */ diff --git a/src/memory.c b/src/memory.c index cc95496a..10bccdee 100644 --- a/src/memory.c +++ b/src/memory.c @@ -25,8 +25,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #ifdef HAVE_SYS_SYSCTL_H #include @@ -118,7 +118,7 @@ static int memory_init(void) { #if HAVE_HOST_STATISTICS port_host = mach_host_self(); host_page_size(port_host, &pagesize); -/* #endif HAVE_HOST_STATISTICS */ + /* #endif HAVE_HOST_STATISTICS */ #elif HAVE_SYSCTLBYNAME /* no init stuff */ @@ -140,7 +140,7 @@ static int memory_init(void) { return -1; } -/* #endif HAVE_LIBKSTAT */ + /* #endif HAVE_LIBKSTAT */ #elif HAVE_SYSCTL pagesize = getpagesize(); @@ -148,7 +148,7 @@ static int memory_init(void) { ERROR("memory plugin: Invalid pagesize: %i", pagesize); return -1; } -/* #endif HAVE_SYSCTL */ + /* #endif HAVE_SYSCTL */ #elif HAVE_LIBSTATGRAB /* no init stuff */ @@ -218,7 +218,7 @@ static int memory_read_internal(value_list_t *vl) { MEMORY_SUBMIT("wired", wired, "active", active, "inactive", inactive, "free", free); -/* #endif HAVE_HOST_STATISTICS */ + /* #endif HAVE_HOST_STATISTICS */ #elif HAVE_SYSCTLBYNAME /* @@ -259,7 +259,7 @@ static int memory_read_internal(value_list_t *vl) { (gauge_t)sysctl_vals[3], "active", (gauge_t)sysctl_vals[4], "inactive", (gauge_t)sysctl_vals[5], "cache", (gauge_t)sysctl_vals[6]); -/* #endif HAVE_SYSCTLBYNAME */ + /* #endif HAVE_SYSCTLBYNAME */ #elif KERNEL_LINUX FILE *fh; @@ -334,7 +334,7 @@ static int memory_read_internal(value_list_t *vl) { else MEMORY_SUBMIT("used", mem_used, "buffered", mem_buffered, "cached", mem_cached, "free", mem_free, "slab", mem_slab_total); -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_LIBKSTAT /* Most of the additions here were taken as-is from the k9toolkit from @@ -406,7 +406,7 @@ static int memory_read_internal(value_list_t *vl) { MEMORY_SUBMIT("used", (gauge_t)mem_used, "free", (gauge_t)mem_free, "locked", (gauge_t)mem_lock, "kernel", (gauge_t)mem_kern, "arc", (gauge_t)arcsize, "unusable", (gauge_t)mem_unus); -/* #endif HAVE_LIBKSTAT */ + /* #endif HAVE_LIBKSTAT */ #elif HAVE_SYSCTL int mib[] = {CTL_VM, VM_METER}; @@ -430,7 +430,7 @@ static int memory_read_internal(value_list_t *vl) { MEMORY_SUBMIT("active", mem_active, "inactive", mem_inactive, "free", mem_free); -/* #endif HAVE_SYSCTL */ + /* #endif HAVE_SYSCTL */ #elif HAVE_LIBSTATGRAB sg_mem_stats *ios; @@ -441,7 +441,7 @@ static int memory_read_internal(value_list_t *vl) { MEMORY_SUBMIT("used", (gauge_t)ios->used, "cached", (gauge_t)ios->cache, "free", (gauge_t)ios->free); -/* #endif HAVE_LIBSTATGRAB */ + /* #endif HAVE_LIBSTATGRAB */ #elif HAVE_PERFSTAT perfstat_memory_total_t pmemory = {0}; diff --git a/src/mic.c b/src/mic.c index 4f4a9bac..584d2e2b 100644 --- a/src/mic.c +++ b/src/mic.c @@ -21,9 +21,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include @@ -132,7 +132,7 @@ static void mic_submit_memory_use(int micnumber, const char *type_instance, vl.values_len = 1; strncpy(vl.plugin, "mic", sizeof(vl.plugin)); - snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber); + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber); strncpy(vl.type, "memory", sizeof(vl.type)); strncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); @@ -164,7 +164,7 @@ static void mic_submit_temp(int micnumber, const char *type, gauge_t value) { vl.values_len = 1; strncpy(vl.plugin, "mic", sizeof(vl.plugin)); - snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber); + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber); strncpy(vl.type, "temperature", sizeof(vl.type)); strncpy(vl.type_instance, type, sizeof(vl.type_instance)); @@ -206,10 +206,10 @@ static void mic_submit_cpu(int micnumber, const char *type_instance, int core, strncpy(vl.plugin, "mic", sizeof(vl.plugin)); if (core < 0) /* global aggregation */ - snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber); + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber); else /* per-core statistics */ - snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i-cpu-%i", - micnumber, core); + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i-cpu-%i", + micnumber, core); strncpy(vl.type, "cpu", sizeof(vl.type)); strncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); @@ -258,7 +258,7 @@ static void mic_submit_power(int micnumber, const char *type, vl.values_len = 1; strncpy(vl.plugin, "mic", sizeof(vl.plugin)); - snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber); + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", micnumber); strncpy(vl.type, type, sizeof(vl.type)); strncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); diff --git a/src/modbus.c b/src/modbus.c index 0a4f40ca..e36f3dae 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -22,9 +22,9 @@ #include "collectd.h" -#include "common.h" #include "configfile.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -97,6 +97,12 @@ enum mb_conntype_e /* {{{ */ MBCONN_RTU }; /* }}} */ typedef enum mb_conntype_e mb_conntype_t; +enum mb_uarttype_e /* {{{ */ +{ UARTTYPE_RS232, + UARTTYPE_RS422, + UARTTYPE_RS485 }; /* }}} */ +typedef enum mb_uarttype_e mb_uarttype_t; + struct mb_data_s; typedef struct mb_data_s mb_data_t; struct mb_data_s /* {{{ */ @@ -126,8 +132,9 @@ struct mb_host_s /* {{{ */ char host[DATA_MAX_NAME_LEN]; char node[NI_MAXHOST]; /* TCP hostname or RTU serial device */ /* char service[NI_MAXSERV]; */ - int port; /* for Modbus/TCP */ - int baudrate; /* for Modbus/RTU */ + int port; /* for Modbus/TCP */ + int baudrate; /* for Modbus/RTU */ + mb_uarttype_t uarttype; /* UART type for Modbus/RTU */ mb_conntype_t conntype; mb_slave_t *slaves; @@ -252,7 +259,7 @@ static int mb_submit(mb_host_t *host, mb_slave_t *slave, /* {{{ */ return EINVAL; if (slave->instance[0] == 0) - snprintf(slave->instance, sizeof(slave->instance), "slave_%i", slave->id); + ssnprintf(slave->instance, sizeof(slave->instance), "slave_%i", slave->id); vl.values = &value; vl.values_len = 1; @@ -334,7 +341,7 @@ static int mb_init_connection(mb_host_t *host) /* {{{ */ host->is_connected = true; return 0; } /* }}} int mb_init_connection */ -/* #endif LEGACY_LIBMODBUS */ + /* #endif LEGACY_LIBMODBUS */ #else /* if !LEGACY_LIBMODBUS */ /* Version 2.9.2 */ @@ -387,6 +394,22 @@ static int mb_init_connection(mb_host_t *host) /* {{{ */ return status; } +#if defined(linux) && LIBMODBUS_VERSION_CHECK(2, 9, 4) + switch (host->uarttype) { + case UARTTYPE_RS485: + if (modbus_rtu_set_serial_mode(host->connection, MODBUS_RTU_RS485)) + DEBUG("Modbus plugin: Setting RS485 mode failed."); + break; + case UARTTYPE_RS422: + /* libmodbus doesn't say anything about full-duplex symmetric RS422 UART */ + break; + case UARTTYPE_RS232: + break; + default: + DEBUG("Modbus plugin: Invalid UART type!."); + } +#endif /* defined(linux) && LIBMODBUS_VERSION_CHECK(2, 9, 4) */ + return 0; } /* }}} int mb_init_connection */ #endif /* !LEGACY_LIBMODBUS */ @@ -983,11 +1006,35 @@ static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */ status = -1; } else if (strcasecmp("Device", child->key) == 0) { status = cf_util_get_string_buffer(child, host->node, sizeof(host->node)); - if (status == 0) + if (status == 0) { host->conntype = MBCONN_RTU; + host->uarttype = UARTTYPE_RS232; + } } else if (strcasecmp("Baudrate", child->key) == 0) status = cf_util_get_int(child, &host->baudrate); - else if (strcasecmp("Interval", child->key) == 0) + else if (strcasecmp("UARTType", child->key) == 0) { +#if defined(linux) && !LEGACY_LIBMODBUS && LIBMODBUS_VERSION_CHECK(2, 9, 4) + char buffer[NI_MAXHOST]; + status = cf_util_get_string_buffer(child, buffer, sizeof(buffer)); + if (status != 0) + break; + if (strncmp(buffer, "RS485", 6) == 0) + host->uarttype = UARTTYPE_RS485; + else if (strncmp(buffer, "RS422", 6) == 0) + host->uarttype = UARTTYPE_RS422; + else if (strncmp(buffer, "RS232", 6) == 0) + host->uarttype = UARTTYPE_RS232; + else { + ERROR("Modbus plugin: The UARTType \"%s\" is unknown.", buffer); + status = -1; + break; + } +#else + ERROR("Modbus plugin: Option `UARTType' not supported. Please " + "upgrade libmodbus to at least 2.9.4"); + return -1; +#endif + } else if (strcasecmp("Interval", child->key) == 0) status = cf_util_get_cdtime(child, &interval); else if (strcasecmp("Slave", child->key) == 0) /* Don't set status: Gracefully continue if a slave fails. */ @@ -1025,13 +1072,14 @@ static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */ if (status == 0) { char name[1024]; - snprintf(name, sizeof(name), "modbus-%s", host->host); + ssnprintf(name, sizeof(name), "modbus-%s", host->host); plugin_register_complex_read(/* group = */ NULL, name, /* callback = */ mb_read, /* interval = */ interval, &(user_data_t){ - .data = host, .free_func = host_free, + .data = host, + .free_func = host_free, }); } else { host_free(host); diff --git a/src/mqtt.c b/src/mqtt.c index 48c34edc..a44f4c82 100644 --- a/src/mqtt.c +++ b/src/mqtt.c @@ -31,8 +31,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_complain.h" #include @@ -462,7 +462,7 @@ static int format_topic(char *buf, size_t buf_len, data_set_t const *ds, if (status != 0) return status; - status = snprintf(buf, buf_len, "%s/%s", conf->topic_prefix, name); + status = ssnprintf(buf, buf_len, "%s/%s", conf->topic_prefix, name); if ((status < 0) || (((size_t)status) >= buf_len)) return ENOMEM; @@ -599,7 +599,7 @@ static int mqtt_config_publisher(oconfig_item_t *ci) { ERROR("mqtt plugin: Unknown config option: %s", child->key); } - snprintf(cb_name, sizeof(cb_name), "mqtt/%s", conf->name); + ssnprintf(cb_name, sizeof(cb_name), "mqtt/%s", conf->name); plugin_register_write(cb_name, mqtt_write, &(user_data_t){ .data = conf, diff --git a/src/multimeter.c b/src/multimeter.c index ca9b15d4..5a7d5a2a 100644 --- a/src/multimeter.c +++ b/src/multimeter.c @@ -24,8 +24,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H #include diff --git a/src/mysql.c b/src/mysql.c index e7ffb489..aafd4dbd 100644 --- a/src/mysql.c +++ b/src/mysql.c @@ -29,8 +29,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #ifdef HAVE_MYSQL_H #include @@ -218,14 +218,15 @@ static int mysql_config_database(oconfig_item_t *ci) /* {{{ */ (db->database != NULL) ? db->database : ""); if (db->instance != NULL) - snprintf(cb_name, sizeof(cb_name), "mysql-%s", db->instance); + ssnprintf(cb_name, sizeof(cb_name), "mysql-%s", db->instance); else sstrncpy(cb_name, "mysql", sizeof(cb_name)); plugin_register_complex_read( /* group = */ NULL, cb_name, mysql_read, /* interval = */ 0, &(user_data_t){ - .data = db, .free_func = mysql_database_free, + .data = db, + .free_func = mysql_database_free, }); } else { mysql_database_free(db); @@ -354,7 +355,8 @@ static void derive_submit(const char *type, const char *type_instance, static void traffic_submit(derive_t rx, derive_t tx, mysql_database_t *db) { value_t values[] = { - {.derive = rx}, {.derive = tx}, + {.derive = rx}, + {.derive = tx}, }; submit("mysql_octets", NULL, values, STATIC_ARRAY_SIZE(values), db); @@ -500,15 +502,15 @@ static int mysql_read_slave_stats(mysql_database_t *db, MYSQL *con) { if (((io == NULL) || (strcasecmp(io, "yes") != 0)) && (db->slave_io_running)) { n.severity = NOTIF_WARNING; - snprintf(n.message, sizeof(n.message), - "slave I/O thread not started or not connected to master"); + ssnprintf(n.message, sizeof(n.message), + "slave I/O thread not started or not connected to master"); plugin_dispatch_notification(&n); db->slave_io_running = false; } else if (((io != NULL) && (strcasecmp(io, "yes") == 0)) && (!db->slave_io_running)) { n.severity = NOTIF_OKAY; - snprintf(n.message, sizeof(n.message), - "slave I/O thread started and connected to master"); + ssnprintf(n.message, sizeof(n.message), + "slave I/O thread started and connected to master"); plugin_dispatch_notification(&n); db->slave_io_running = true; } @@ -516,13 +518,13 @@ static int mysql_read_slave_stats(mysql_database_t *db, MYSQL *con) { if (((sql == NULL) || (strcasecmp(sql, "yes") != 0)) && (db->slave_sql_running)) { n.severity = NOTIF_WARNING; - snprintf(n.message, sizeof(n.message), "slave SQL thread not started"); + ssnprintf(n.message, sizeof(n.message), "slave SQL thread not started"); plugin_dispatch_notification(&n); db->slave_sql_running = false; } else if (((sql != NULL) && (strcasecmp(sql, "yes") == 0)) && (!db->slave_sql_running)) { n.severity = NOTIF_OKAY; - snprintf(n.message, sizeof(n.message), "slave SQL thread started"); + ssnprintf(n.message, sizeof(n.message), "slave SQL thread started"); plugin_dispatch_notification(&n); db->slave_sql_running = true; } @@ -905,6 +907,8 @@ static int mysql_read(user_data_t *ud) { } else if (strncmp(key, "Slow_queries", strlen("Slow_queries")) == 0) { derive_submit("mysql_slow_queries", NULL, val, db); + } else if (strcmp(key, "Uptime") == 0) { + gauge_submit("uptime", NULL, val, db); } } mysql_free_result(res); diff --git a/src/netapp.c b/src/netapp.c index 62600ca2..43aaa7a7 100644 --- a/src/netapp.c +++ b/src/netapp.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include @@ -643,7 +643,8 @@ static int submit_two_derive(const char *host, derive_t val0, derive_t val1, cdtime_t timestamp, cdtime_t interval) { value_t values[] = { - {.derive = val0}, {.derive = val1}, + {.derive = val0}, + {.derive = val1}, }; return submit_values(host, plugin_inst, type, type_inst, values, @@ -666,7 +667,8 @@ static int submit_two_gauge(const char *host, const char *plugin_inst, /* {{{ */ gauge_t val0, gauge_t val1, cdtime_t timestamp, cdtime_t interval) { value_t values[] = { - {.gauge = val0}, {.gauge = val1}, + {.gauge = val0}, + {.gauge = val1}, }; return submit_values(host, plugin_inst, type, type_inst, values, @@ -773,14 +775,13 @@ static int submit_volume_perf_data(const char *hostname, /* {{{ */ if ((hostname == NULL) || (old_data == NULL) || (new_data == NULL)) return -1; - snprintf(plugin_instance, sizeof(plugin_instance), "volume-%s", - old_data->name); + ssnprintf(plugin_instance, sizeof(plugin_instance), "volume-%s", + old_data->name); /* Check for and submit disk-octet values */ if (HAS_ALL_FLAGS(old_data->flags, CFG_VOLUME_PERF_IO) && - HAS_ALL_FLAGS(new_data->flags, - HAVE_VOLUME_PERF_BYTES_READ | - HAVE_VOLUME_PERF_BYTES_WRITE)) { + HAS_ALL_FLAGS(new_data->flags, HAVE_VOLUME_PERF_BYTES_READ | + HAVE_VOLUME_PERF_BYTES_WRITE)) { submit_two_derive( hostname, plugin_instance, "disk_octets", /* type instance = */ NULL, (derive_t)new_data->read_bytes, (derive_t)new_data->write_bytes, @@ -798,15 +799,15 @@ static int submit_volume_perf_data(const char *hostname, /* {{{ */ } /* Check for, calculate and submit disk-latency values */ - if (HAS_ALL_FLAGS(old_data->flags, - CFG_VOLUME_PERF_LATENCY | HAVE_VOLUME_PERF_OPS_READ | - HAVE_VOLUME_PERF_OPS_WRITE | - HAVE_VOLUME_PERF_LATENCY_READ | - HAVE_VOLUME_PERF_LATENCY_WRITE) && - HAS_ALL_FLAGS(new_data->flags, - HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE | - HAVE_VOLUME_PERF_LATENCY_READ | - HAVE_VOLUME_PERF_LATENCY_WRITE)) { + if (HAS_ALL_FLAGS(old_data->flags, CFG_VOLUME_PERF_LATENCY | + HAVE_VOLUME_PERF_OPS_READ | + HAVE_VOLUME_PERF_OPS_WRITE | + HAVE_VOLUME_PERF_LATENCY_READ | + HAVE_VOLUME_PERF_LATENCY_WRITE) && + HAS_ALL_FLAGS(new_data->flags, HAVE_VOLUME_PERF_OPS_READ | + HAVE_VOLUME_PERF_OPS_WRITE | + HAVE_VOLUME_PERF_LATENCY_READ | + HAVE_VOLUME_PERF_LATENCY_WRITE)) { gauge_t latency_per_op_read; gauge_t latency_per_op_write; @@ -1404,11 +1405,10 @@ static int cna_submit_volume_usage_data(const char *hostname, /* {{{ */ uint64_t snap_reserve_free = v->snap_reserved; uint64_t snap_norm_used = v->snap_used; - snprintf(plugin_instance, sizeof(plugin_instance), "volume-%s", v->name); + ssnprintf(plugin_instance, sizeof(plugin_instance), "volume-%s", v->name); - if (HAS_ALL_FLAGS(v->flags, - HAVE_VOLUME_USAGE_SNAP_USED | - HAVE_VOLUME_USAGE_SNAP_RSVD)) { + if (HAS_ALL_FLAGS(v->flags, HAVE_VOLUME_USAGE_SNAP_USED | + HAVE_VOLUME_USAGE_SNAP_RSVD)) { if (v->snap_reserved > v->snap_used) { snap_reserve_free = v->snap_reserved - v->snap_used; snap_reserve_used = v->snap_used; @@ -1422,9 +1422,8 @@ static int cna_submit_volume_usage_data(const char *hostname, /* {{{ */ /* The space used by snapshots but not reserved for them is included in * both, norm_used and snap_norm_used. If possible, subtract this here. */ - if (HAS_ALL_FLAGS(v->flags, - HAVE_VOLUME_USAGE_NORM_USED | - HAVE_VOLUME_USAGE_SNAP_USED)) { + if (HAS_ALL_FLAGS(v->flags, HAVE_VOLUME_USAGE_NORM_USED | + HAVE_VOLUME_USAGE_SNAP_USED)) { if (norm_used >= snap_norm_used) norm_used -= snap_norm_used; else { @@ -1466,9 +1465,8 @@ static int cna_submit_volume_usage_data(const char *hostname, /* {{{ */ "df_complex", "snap_reserved", (double)snap_reserve_free, /* timestamp = */ 0, interval); - if (HAS_ALL_FLAGS(v->flags, - HAVE_VOLUME_USAGE_SNAP_USED | - HAVE_VOLUME_USAGE_SNAP_RSVD)) + if (HAS_ALL_FLAGS(v->flags, HAVE_VOLUME_USAGE_SNAP_USED | + HAVE_VOLUME_USAGE_SNAP_RSVD)) submit_double(hostname, /* plugin instance = */ plugin_instance, "df_complex", "snap_reserve_used", (double)snap_reserve_used, /* timestamp = */ 0, interval); @@ -1498,12 +1496,13 @@ static int cna_change_volume_status(const char *hostname, /* {{{ */ if ((v->flags & IS_VOLUME_USAGE_OFFLINE) != 0) { n.severity = NOTIF_OKAY; - snprintf(n.message, sizeof(n.message), "Volume %s is now online.", v->name); + ssnprintf(n.message, sizeof(n.message), "Volume %s is now online.", + v->name); v->flags &= ~IS_VOLUME_USAGE_OFFLINE; } else { n.severity = NOTIF_WARNING; - snprintf(n.message, sizeof(n.message), "Volume %s is now offline.", - v->name); + ssnprintf(n.message, sizeof(n.message), "Volume %s is now offline.", + v->name); v->flags |= IS_VOLUME_USAGE_OFFLINE; } @@ -1832,8 +1831,8 @@ static int cna_handle_quota_data(const host_config_t *host, /* {{{ */ if (volume_name == NULL) continue; - snprintf(plugin_instance, sizeof(plugin_instance), "quota-%s-%s", - volume_name, tree_name); + ssnprintf(plugin_instance, sizeof(plugin_instance), "quota-%s-%s", + volume_name, tree_name); value = na_child_get_uint64(elem_quota, "disk-used", UINT64_MAX); if (value != UINT64_MAX) { @@ -1945,8 +1944,8 @@ static int cna_handle_snapvault_data(const char *hostname, /* {{{ */ continue; /* possible TODO: make plugin instance configurable */ - snprintf(plugin_instance, sizeof(plugin_instance), "snapvault-%s", - dest_path); + ssnprintf(plugin_instance, sizeof(plugin_instance), "snapvault-%s", + dest_path); submit_double(hostname, plugin_instance, /* type = */ "delay", NULL, (double)value, /* timestamp = */ 0, interval); @@ -2787,17 +2786,18 @@ static int cna_register_host(host_config_t *host) /* {{{ */ char cb_name[256]; if (host->vfiler) - snprintf(cb_name, sizeof(cb_name), "netapp-%s-%s", host->name, - host->vfiler); + ssnprintf(cb_name, sizeof(cb_name), "netapp-%s-%s", host->name, + host->vfiler); else - snprintf(cb_name, sizeof(cb_name), "netapp-%s", host->name); + ssnprintf(cb_name, sizeof(cb_name), "netapp-%s", host->name); plugin_register_complex_read( /* group = */ NULL, cb_name, /* callback = */ cna_read, /* interval = */ host->interval, &(user_data_t){ - .data = host, .free_func = (void *)free_host_config, + .data = host, + .free_func = (void *)free_host_config, }); return 0; @@ -2974,7 +2974,7 @@ static int cna_init(void) /* {{{ */ char err[256] = {0}; if (!na_startup(err, sizeof(err))) { - err[sizeof(err) - 1] = 0; + err[sizeof(err) - 1] = '\0'; ERROR("netapp plugin: Error initializing netapp API: %s", err); return 1; } diff --git a/src/netlink.c b/src/netlink.c index a1f52a45..b998cf31 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -27,8 +27,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -198,7 +198,8 @@ static void submit_two(const char *dev, const char *type, const char *type_instance, derive_t rx, derive_t tx) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = rx}, {.derive = tx}, + {.derive = rx}, + {.derive = tx}, }; vl.values = values; @@ -509,8 +510,8 @@ static int qos_filter_cb(const struct nlmsghdr *nlh, void *args) { if (strcmp(tc_type, "filter") == 0) numberic_id = tm->tcm_parent; - snprintf(tc_inst, sizeof(tc_inst), "%s-%x:%x", kind, numberic_id >> 16, - numberic_id & 0x0000FFFF); + ssnprintf(tc_inst, sizeof(tc_inst), "%s-%x:%x", kind, numberic_id >> 16, + numberic_id & 0x0000FFFF); } DEBUG("netlink plugin: qos_filter_cb: got %s for %s (%i).", tc_type, dev, @@ -541,8 +542,8 @@ static int qos_filter_cb(const struct nlmsghdr *nlh, void *args) { stats_submitted = true; - int r = snprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type, - tc_inst); + int r = ssnprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type, + tc_inst); if ((size_t)r >= sizeof(type_instance)) { ERROR("netlink plugin: type_instance truncated to %zu bytes, need %d", sizeof(type_instance), r); @@ -580,8 +581,8 @@ static int qos_filter_cb(const struct nlmsghdr *nlh, void *args) { if (!stats_submitted && ts != NULL) { char type_instance[DATA_MAX_NAME_LEN]; - int r = snprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type, - tc_inst); + int r = ssnprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type, + tc_inst); if ((size_t)r >= sizeof(type_instance)) { ERROR("netlink plugin: type_instance truncated to %zu bytes, need %d", sizeof(type_instance), r); @@ -637,7 +638,7 @@ static int ir_config(const char *key, const char *value) { } else if ((strcasecmp(key, "QDisc") == 0) || (strcasecmp(key, "Class") == 0) || (strcasecmp(key, "Filter") == 0)) { - if ((fields_num < 1) || (fields_num > 2)) { + if (fields_num > 2) { ERROR("netlink plugin: Invalid number of fields for option " "`%s'. Got %i, expected 1 or 2.", key, fields_num); diff --git a/src/network.c b/src/network.c index 64c6d24b..8acf84b1 100644 --- a/src/network.c +++ b/src/network.c @@ -27,8 +27,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include "utils_complain.h" #include "utils_fbhash.h" @@ -381,7 +381,7 @@ static bool check_send_notify_okay(const notification_t *n) /* {{{ */ LOG_ERR, &complain_forwarding, "network plugin: A notification has been received via the network " "and forwarding is enabled. Forwarding of notifications is currently " - "not supported, because there is not loop-deteciton available. " + "not supported, because there is not loop-detection available. " "Please contact the collectd mailing list if you need this " "feature."); } @@ -403,7 +403,7 @@ static int network_dispatch_values(value_list_t *vl, /* {{{ */ #if COLLECT_DEBUG char name[6 * DATA_MAX_NAME_LEN]; FORMAT_VL(name, sizeof(name), vl); - name[sizeof(name) - 1] = 0; + name[sizeof(name) - 1] = '\0'; DEBUG("network plugin: network_dispatch_values: " "NOT dispatching %s.", name); @@ -1108,7 +1108,7 @@ static int parse_part_sign_sha256(sockent_t *se, /* {{{ */ return 0; } /* }}} int parse_part_sign_sha256 */ -/* #endif HAVE_GCRYPT_H */ + /* #endif HAVE_GCRYPT_H */ #else /* if !HAVE_GCRYPT_H */ static int parse_part_sign_sha256(sockent_t *se, /* {{{ */ @@ -1263,7 +1263,7 @@ static int parse_part_encr_aes256(sockent_t *se, /* {{{ */ return 0; } /* }}} int parse_part_encr_aes256 */ -/* #endif HAVE_GCRYPT_H */ + /* #endif HAVE_GCRYPT_H */ #else /* if !HAVE_GCRYPT_H */ static int parse_part_encr_aes256(sockent_t *se, /* {{{ */ @@ -1668,7 +1668,7 @@ static int network_set_interface(const sockent_t *se, ERROR("network plugin: setsockopt (bind-if): %s", STRERRNO); return -1; } -/* #endif HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */ + /* #endif HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */ #else WARNING("network plugin: Cannot set the interface on a unicast " @@ -2640,7 +2640,7 @@ static int network_write(const data_set_t *ds, const value_list_t *vl, #if COLLECT_DEBUG char name[6 * DATA_MAX_NAME_LEN]; FORMAT_VL(name, sizeof(name), vl); - name[sizeof(name) - 1] = 0; + name[sizeof(name) - 1] = '\0'; DEBUG("network plugin: network_write: " "NOT sending %s.", name); diff --git a/src/network_test.c b/src/network_test.c new file mode 100644 index 00000000..ae4875cc --- /dev/null +++ b/src/network_test.c @@ -0,0 +1,255 @@ +/** + * Copyright (C) 2019 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 + **/ + +#define TEST_PLUGIN_NETWORK 1 + +#include "network.c" /* (sic) */ + +#include "testing.h" + +char *raw_packet_data[] = { + "0000000e6c6f63616c686f7374000008000c1513676ac3a6e0970009000c00000002800000" + "000002000973776170000004000973776170000005000966726565000006000f0001010000" + "0080ff610f420008000c1513676ac3a8fc120004000c737761705f696f0000050007696e00" + "0006000f00010200000000000000000008000c1513676ac3a9077d000500086f7574000006" + "000f00010200000000000000000008000c1513676ac3bd2a8c0002000e696e746572666163" + "65000003000965746830000004000e69665f6f637465747300000500050000060018000202" + "02000000000000000000000000000000000008000c1513676ac3bd5a970004000e69665f65" + "72726f7273000006001800020202000000000000000000000000000000000008000c151367" + "6ac3bd7fea000300076c6f000004000e69665f6f6374657473000006001800020202000000" + "000009e79c000000000009e79c0008000c1513676ac3bdaae60003000a776c616e30000006" + "001800020202000000001009fa5400000000011cf6670008000c1513676ac3bdb0e0000400" + "0e69665f6572726f7273000006001800020202000000000000000000000000000000000008" + "000c1513676ac3bd3d6d0003000965746830000004000f69665f7061636b65747300000600" + "1800020202000000000000000000000000000000000008000c1513676ac3bdae290003000a" + "776c616e300000060018000202020000000000032f8f00000000000205e50008000c151367" + "6ac3bdbb7b0003000c646f636b657230000006001800020202000000000000000000000000" + "000000000008000c1513676ac3bda0db000300076c6f000004000e69665f6572726f727300" + "0006001800020202000000000000000000000000000000000008000c1513676ac3bdbde800" + "03000c646f636b657230000006001800020202000000000000000000000000000000000008" + "000c1513676ac3bd8d8e000300076c6f000004000f69665f7061636b657473000006001800" + "0202020000000000000c9c0000000000000c9c0008000c1513676ac3bdb90b0003000c646f" + "636b657230000004000e69665f6f6374657473000006001800020202000000000000000000" + "000000000000000008000c1513676ac469b10f0002000e70726f6365737365730000030005" + "000004000d70735f7374617465000005000c7a6f6d62696573000006000f00010100000000" + "000000000008000c1513676ac469a4a30005000d736c656570696e67000006000f00010100" + "00000000006e400008000c1513676ac469c6320005000b706167696e67000006000f000101" + "00000000000000000008000c1513676ac469f06e0005000c626c6f636b6564000006000f00" + "010100000000000000000008000c1513676ac4698af40005000c72756e6e696e6700000600" + "0f00010100000000000000000008000c1513676ac469bbe10005000c73746f707065640000" + "06000f00010100000000000000000008000c1513676ac46b8e710004000e666f726b5f7261" + "74650000050005000006000f0001020000000000001bcf0008000c1513676d437f12960002" + "00086370750000030006300000040008637075000005000b73797374656d000006000f0001" + "0200000000000021870008000c1513676d437f36020005000969646c65000006000f000102" + "000000000005847a0008000c1513676d437f979b0005000977616974000006000f00010200" + "000000000005210008000c1513676d43802ff60005000c736f6674697271000006000f0001" + "02000000000000001f0008000c1513676d43803b3a0005000a737465616c000006000f0001" + "020000000000000000", + "0000000e6c6f63616c686f7374000008000c1513676d4380551f0009000c00000002800000" + "00000200086370750000030006310000040008637075000005000975736572000006000f00" + "01020000000000007cad0008000c1513676d43805dbe000500096e696365000006000f0001" + "0200000000000001de0008000c1513676d4380697d0005000b73797374656d000006000f00" + "01020000000000001ce80008000c1513676d438072bd0005000969646c65000006000f0001" + "02000000000005931c0008000c1513676d43807c430005000977616974000006000f000102" + "000000000000094b0008000c1513676d43808cee0005000c736f6674697271000006000f00" + "010200000000000000120008000c1513676d4380843a0005000e696e746572727570740000" + "06000f00010200000000000000000008000c1513676d438096230005000a737465616c0000" + "06000f00010200000000000000000008000c1513676d4380aa9c0003000632000005000975" + "736572000006000f00010200000000000089580008000c1513676d4380b29f000500096e69" + "6365000006000f00010200000000000003610008000c1513676d4380c44c0005000969646c" + "65000006000f000102000000000005873d0008000c1513676d4380bc0f0005000b73797374" + "656d000006000f000102000000000000201d0008000c1513676d4380cea400050009776169" + "74000006000f00010200000000000005810008000c1513676d4380d7370005000e696e7465" + "7272757074000006000f00010200000000000000000008000c1513676d4380ea830005000a" + "737465616c000006000f00010200000000000000000008000c1513676d437eef6200030006" + "3000000500096e696365000006000f00010200000000000003920008000c1513676d4380e0" + "260003000632000005000c736f6674697271000006000f0001020000000000000016000800" + "0c1513676d438101410003000633000005000975736572000006000f000102000000000000" + "7d8a0008000c1513676d438109f5000500096e696365000006000f00010200000000000004" + "350008000c1513676d4380244b0003000630000005000e696e74657272757074000006000f" + "00010200000000000000000008000c1513676d438122070003000633000005000969646c65" + "000006000f0001020000000000058eb60008000c1513676d43812e83000500097761697400" + "0006000f0001020000000000000ca80008000c1513676d438141480005000c736f66746972" + "71000006000f000102000000000000001e0008000c1513676d43814a5d0005000a73746561" + "6c000006000f00010200000000000000000008000c1513676d4381149e0005000b73797374" + "656d000006000f0001020000000000001b9a0008000c1513676d437ea86000030006300000" + "05000975736572000006000f00010200000000000089a80008000c1513676d438138190003" + "000633000005000e696e74657272757074000006000f00010200000000000000000008000c" + "1513676d438a9ca00002000e696e74657266616365000003000965746830000004000e6966" + "5f6f6374657473000005000500000600180002020200000000000000000000000000000000" + "0008000c1513676d438aea760004000f69665f7061636b6574730000060018000202020000" + "00000000000000000000000000000008000c1513676d438b214d0004000e69665f6572726f" + "727300000600180002020200000000000000000000000000000000", + "0000000e6c6f63616c686f7374000008000c1513676d438aac590009000c00000002800000" + "000002000764660000030009726f6f74000004000f64665f636f6d706c6578000005000966" + "726565000006000f0001010000004c077e57420008000c1513676d438b6ada0005000d7265" + "736572766564000006000f00010100000000338116420008000c1513676d438b7a17000200" + "0e696e7465726661636500000300076c6f000004000e69665f6f6374657473000005000500" + "0006001800020202000000000009ecf5000000000009ecf50008000c1513676d438b757800" + "02000764660000030009726f6f74000004000f64665f636f6d706c65780000050009757365" + "64000006000f000101000000e0a41b26420008000c1513676d438b8ed20002000e696e7465" + "726661636500000300076c6f000004000e69665f6572726f72730000050005000006001800" + "020202000000000000000000000000000000000008000c1513676d438b86bf0004000f6966" + "5f7061636b6574730000060018000202020000000000000c9d0000000000000c9d0008000c" + "1513676d438bb3e60003000a776c616e300000060018000202020000000000032fab000000" + "00000205ed0008000c1513676d438bd62e0003000c646f636b657230000004000e69665f6f" + "6374657473000006001800020202000000000000000000000000000000000008000c151367" + "6d438bbc8f0003000a776c616e30000004000e69665f6572726f7273000006001800020202" + "000000000000000000000000000000000008000c1513676d438bdf030003000c646f636b65" + "7230000004000f69665f7061636b6574730000060018000202020000000000000000000000" + "00000000000008000c1513676d438baaf10003000a776c616e30000004000e69665f6f6374" + "65747300000600180002020200000000100a042300000000011cfa460008000c1513676d43" + "8c5f100002000764660000030009626f6f74000004000f64665f636f6d706c657800000500" + "0966726565000006000f0001010000000010e198410008000c1513676d438c689c0005000d" + "7265736572766564000006000f00010100000000804c68410008000c1513676d438c70ce00" + "05000975736564000006000f0001010000000020ea9e410008000c1513676d438be7bc0002" + "000e696e74657266616365000003000c646f636b657230000004000e69665f6572726f7273" + "0000050005000006001800020202000000000000000000000000000000000008000c151367" + "6d43beca8c0002000c656e74726f70790000030005000004000c656e74726f707900000600" + "0f0001010000000000088f400008000c1513676d43bf1d13000200096c6f61640000040009" + "6c6f6164000006002100030101019a9999999999a93f666666666666d63f5c8fc2f5285cdf" + "3f0008000c1513676d43c02b85000200096469736b00000300087364610000040010646973" + "6b5f6f63746574730000060018000202020000000075887800000000005b6f3c000008000c" + "1513676d43c06d1f0004000d6469736b5f6f7073000006001800020202000000000003cbbd" + "000000000001c0510008000c1513676d43c08b6a0004000e6469736b5f74696d6500000600" + "1800020202000000000000003f00000000000001720008000c1513676d43c0a5fb00040010" + "6469736b5f6d65726765640000060018000202020000000000001285000000000000f80100" + "08000c1513676d43c0c8b4000300097364613100000400106469736b5f6f63746574730000" + "060018000202020000000001107c000000000000003c00", + "0000000e6c6f63616c686f7374000008000c1513676d43c0d00a0009000c00000002800000" + "00000200096469736b000003000973646131000004000d6469736b5f6f7073000006001800" + "020202000000000000029b00000000000000080008000c1513676d43c0d7b20004000e6469" + "736b5f74696d650000060018000202020000000000000004000000000000000f0008000c15" + "13676d43c0df73000400106469736b5f6d6572676564000006001800020202000000000000" + "0fb400000000000000010008000c1513676d43c0f87c000300097364613200000400106469" + "736b5f6f637465747300000600180002020200000000000008000000000000000000000800" + "0c1513676d43c1003e0004000d6469736b5f6f707300000600180002020200000000000000" + "0200000000000000000008000c1513676d43c107bf000400106469736b5f6d657267656400" + "0006001800020202000000000000000000000000000000000008000c1513676d43c12fa400" + "03000973646135000004000d6469736b5f6f7073000006001800020202000000000003c867" + "000000000001aef20008000c1513676d43c13d5e000400106469736b5f6d65726765640000" + "0600180002020200000000000002d1000000000000f8000008000c1513676d43c136a90004" + "000e6469736b5f74696d65000006001800020202000000000000003f000000000000011c00" + "08000c1513676d43c1740500030009646d2d3000000400106469736b5f6f63746574730000" + "060018000202020000000074596400000000005b6f00000008000c1513676d43c179c70004" + "000d6469736b5f6f7073000006001800020202000000000003cae4000000000002b0f30008" + "000c1513676d43c18abe000400106469736b5f6d6572676564000006001800020202000000" + "000000000000000000000000000008000c1513676d43c181b90004000e6469736b5f74696d" + "650000060018000202020000000000000040000000000000013e0008000c1513676d43c1a9" + "5e00030009646d2d3100000400106469736b5f6f6374657473000006001800020202000000" + "00000e000000000000000000000008000c1513676d43c1b7ea0004000e6469736b5f74696d" + "65000006001800020202000000000000000200000000000000000008000c1513676d43c1b0" + "3e0004000d6469736b5f6f707300000600180002020200000000000000e000000000000000" + "000008000c1513676d43c1c00d000400106469736b5f6d6572676564000006001800020202" + "000000000000000000000000000000000008000c1513676d43c12818000300097364613500" + "000400106469736b5f6f637465747300000600180002020200000000746c6400000000005b" + "6f00000008000c1513676d43d320a80002000c62617474657279000003000630000004000b" + "636861726765000006000f0001018fc2f5285c2f58400008000c1513676d43d36fd6000400" + "0c63757272656e74000006000f00010100000000000000800008000c1513676d43d3cdb600" + "04000c766f6c74616765000006000f000101736891ed7cbf28400008000c1513676d43d59d" + "d60002000869727100000300050000040008697271000005000630000006000f0001020000" + "0000000000110008000c1513676d43d5d2cf0005000631000006000f000102000000000000" + "00100008000c1513676d43d5fe820005000638000006000f00010200000000000000010008" + "000c1513676d43d635440005000639000006000f00010200000000000035210008000c1513" + "676d43d66265000500073132000006000f0001020000000000000790", + "0000000e6c6f63616c686f7374000008000c1513676d43d68e940009000c00000002800000" + "0000020008697271000004000869727100000500073136000006000f000102000000000000" + "00210008000c1513676d43d69be20002000a7573657273000004000a757365727300000500" + "05000006000f00010100000000000010400008000c1513676d43d6aa5d0002000869727100" + "0004000869727100000500073233000006000f00010200000000000000250008000c151367" + "6d43d6c7dc000500073431000006000f000102000000000000ff7d0008000c1513676d43d6" + "e23d000500073432000006000f00010200000000000008070008000c1513676d43d9aa3a00" + "0500073437000006000f0001020000000000079a260008000c1513676d43d9cca900050007" + "3438000006000f00010200000000000000c70008000c1513676d43d9ea5d00050007343900" + "0006000f00010200000000000004c20008000c1513676d43da050e00050007353000000600" + "0f000102000000000000001c0008000c1513676d43da1efa000500084e4d49000006000f00" + "010200000000000000000008000c1513676d43da3c82000500084c4f43000006000f000102" + "000000000018d3080008000c1513676d43da544e00050008535055000006000f0001020000" + "0000000000000008000c1513676d43da6cca00050008504d49000006000f00010200000000" + "000000000008000c1513676d43da885400050008495749000006000f000102000000000000" + "a9da0008000c1513676d43daa23a00050008525452000006000f0001020000000000000003" + "0008000c1513676d43dabaed00050008524553000006000f00010200000000000ac8360008" + "000c1513676d43dad4150005000843414c000006000f000102000000000000191f0008000c" + "1513676d43daeef300050008544c42000006000f000102000000000003dbdc0008000c1513" + "676d43db11410005000854524d000006000f00010200000000000000000008000c1513676d" + "43db292c00050008544852000006000f00010200000000000000000008000c1513676d43db" + "411d000500084d4345000006000f00010200000000000000000008000c1513676d43db5b59" + "000500084d4350000006000f000102000000000000003c0008000c1513676d43db68010005" + "0008455252000006000f00010200000000000000000008000c1513676d43db758a00050008" + "4d4953000006000f00010200000000000000000008000c1513676d43dd2e800002000b6d65" + "6d6f7279000004000b6d656d6f7279000005000975736564000006000f00010100000000fe" + "bbe0410008000c1513676d43dd3f4b0005000d6275666665726564000006000f0001010000" + "000070fbc8410008000c1513676d43dd48700005000b636163686564000006000f00010100" + "000000c008df410008000c1513676d43dd51c60005000966726565000006000f0001010000" + "0080481d05420008000c1513676d43dec7e300020009737761700000040009737761700000" + "05000975736564000006000f00010100000000000000000008000c1513676d43ded4490005" + "000966726565000006000f00010100000080ff610f420008000c1513676d43dedcfd000500" + "0b636163686564000006000f00010100000000000000000008000c1513676d43d715e30002" + "0008697271000004000869727100000500073434000006000f0001020000000000031b6100" + "08000c1513676d43d73116000500073435000006000f00010200000000000000180008000c" + "1513676d43ee00150002000973776170000004000c737761705f696f0000050007696e0000" + "06000f0001020000000000000000", +}; + +static int decode_string(char const *in, uint8_t *out, size_t *out_size) { + size_t in_size = strlen(in); + if (*out_size < (in_size / 2)) + return -1; + *out_size = in_size / 2; + + for (size_t i = 0; i < *out_size; i++) { + char tmp[] = {in[2 * i], in[2 * i + 1], 0}; + out[i] = (uint8_t)strtoul(tmp, NULL, 16); + } + + return 0; +} + +DEF_TEST(parse_packet) { + sockent_t se = { + .data.server = + (struct sockent_server){ +#if HAVE_GCRYPT_H + .cypher = NULL, + .userdb = NULL, + .security_level = SECURITY_LEVEL_NONE, +#endif + }, + }; + + for (size_t i = 0; i < sizeof(raw_packet_data) / sizeof(raw_packet_data[0]); + i++) { + uint8_t buffer[network_config_packet_size]; + size_t buffer_size = sizeof(buffer); + + EXPECT_EQ_INT(0, decode_string(raw_packet_data[i], buffer, &buffer_size)); + EXPECT_EQ_INT(0, parse_packet(&se, buffer, buffer_size, 0, NULL)); + } + EXPECT_EQ_INT(139, (int)stats_values_dispatched); + + return 0; +} + +int main() { + RUN_TEST(parse_packet); + + END_TEST; +} diff --git a/src/nfs.c b/src/nfs.c index 481aa79e..97d91259 100644 --- a/src/nfs.c +++ b/src/nfs.c @@ -24,8 +24,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_KSTAT_H #include @@ -322,7 +322,7 @@ static int nfs_config(const char *key, const char *value) { #if KERNEL_LINUX static int nfs_init(void) { return 0; } -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_LIBKSTAT static int nfs_init(void) { @@ -594,7 +594,7 @@ static int nfs_read(void) { return 0; } -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_LIBKSTAT static int nfs_read(void) { diff --git a/src/nginx.c b/src/nginx.c index e5ca89c0..0da66ce2 100644 --- a/src/nginx.c +++ b/src/nginx.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -122,8 +122,8 @@ static int init(void) { curl_easy_setopt(curl, CURLOPT_PASSWORD, (pass == NULL) ? "" : pass); #else static char credentials[1024]; - int status = snprintf(credentials, sizeof(credentials), "%s:%s", user, - pass == NULL ? "" : pass); + int status = ssnprintf(credentials, sizeof(credentials), "%s:%s", user, + pass == NULL ? "" : pass); if ((status < 0) || ((size_t)status >= sizeof(credentials))) { ERROR("nginx plugin: Credentials would have been truncated."); return -1; diff --git a/src/notify_desktop.c b/src/notify_desktop.c index e391cf27..e430b681 100644 --- a/src/notify_desktop.c +++ b/src/notify_desktop.c @@ -30,8 +30,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -93,16 +93,16 @@ static int c_notify(const notification_t *n, timeout = fail_timeout; } - snprintf(summary, sizeof(summary), "collectd %s notification", - (NOTIF_FAILURE == n->severity) - ? "FAILURE" - : (NOTIF_WARNING == n->severity) - ? "WARNING" - : (NOTIF_OKAY == n->severity) ? "OKAY" : "UNKNOWN"); + ssnprintf(summary, sizeof(summary), "collectd %s notification", + (NOTIF_FAILURE == n->severity) + ? "FAILURE" + : (NOTIF_WARNING == n->severity) + ? "WARNING" + : (NOTIF_OKAY == n->severity) ? "OKAY" : "UNKNOWN"); notification = notify_notification_new(summary, n->message, NULL #if NOTIFY_CHECK_VERSION(0, 7, 0) - ); + ); #else , NULL); diff --git a/src/notify_email.c b/src/notify_email.c index bb36ff27..0e140e4e 100644 --- a/src/notify_email.c +++ b/src/notify_email.c @@ -24,8 +24,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -104,8 +104,8 @@ static void monitor_cb(const char *buf, int buflen, int writing, static int notify_email_init(void) { char server[MAXSTRING]; - snprintf(server, sizeof(server), "%s:%i", - (smtp_host == NULL) ? DEFAULT_SMTP_HOST : smtp_host, smtp_port); + ssnprintf(server, sizeof(server), "%s:%i", + (smtp_host == NULL) ? DEFAULT_SMTP_HOST : smtp_host, smtp_port); pthread_mutex_lock(&session_lock); @@ -214,16 +214,16 @@ static int notify_email_notification(const notification_t *n, char *buf_ptr = buf; int buf_len = sizeof(buf); - snprintf(severity, sizeof(severity), "%s", - (n->severity == NOTIF_FAILURE) - ? "FAILURE" - : ((n->severity == NOTIF_WARNING) - ? "WARNING" - : ((n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN"))); + ssnprintf(severity, sizeof(severity), "%s", + (n->severity == NOTIF_FAILURE) + ? "FAILURE" + : ((n->severity == NOTIF_WARNING) + ? "WARNING" + : ((n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN"))); - snprintf(subject, sizeof(subject), - (email_subject == NULL) ? DEFAULT_SMTP_SUBJECT : email_subject, - severity, n->host); + ssnprintf(subject, sizeof(subject), + (email_subject == NULL) ? DEFAULT_SMTP_SUBJECT : email_subject, + severity, n->host); localtime_r(&CDTIME_T_TO_TIME_T(n->time), ×tamp_tm); strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", @@ -231,15 +231,15 @@ static int notify_email_notification(const notification_t *n, timestamp_str[sizeof(timestamp_str) - 1] = '\0'; /* Let's make RFC822 message text with \r\n EOLs */ - int status = snprintf(buf, buf_len, - "MIME-Version: 1.0\r\n" - "Content-Type: text/plain; charset=\"US-ASCII\"\r\n" - "Content-Transfer-Encoding: 8bit\r\n" - "Subject: %s\r\n" - "\r\n" - "%s - %s@%s\r\n" - "\r\n", - subject, timestamp_str, severity, n->host); + int status = ssnprintf(buf, buf_len, + "MIME-Version: 1.0\r\n" + "Content-Type: text/plain; charset=\"US-ASCII\"\r\n" + "Content-Transfer-Encoding: 8bit\r\n" + "Subject: %s\r\n" + "\r\n" + "%s - %s@%s\r\n" + "\r\n", + subject, timestamp_str, severity, n->host); if (status > 0) { buf_ptr += status; @@ -248,7 +248,7 @@ static int notify_email_notification(const notification_t *n, #define APPEND(format, value) \ if ((buf_len > 0) && (strlen(value) > 0)) { \ - status = snprintf(buf_ptr, buf_len, format "\r\n", value); \ + status = ssnprintf(buf_ptr, buf_len, format "\r\n", value); \ if (status > 0) { \ buf_ptr += status; \ buf_len -= status; \ diff --git a/src/notify_nagios.c b/src/notify_nagios.c index 68f6e2a7..79926b52 100644 --- a/src/notify_nagios.c +++ b/src/notify_nagios.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #define NAGIOS_OK 0 #define NAGIOS_WARNING 1 diff --git a/src/ntpd.c b/src/ntpd.c index 0b824ba2..33acc69f 100644 --- a/src/ntpd.c +++ b/src/ntpd.c @@ -29,8 +29,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_NETDB_H #include diff --git a/src/numa.c b/src/numa.c index c68fb869..dfe7a6dc 100644 --- a/src/numa.c +++ b/src/numa.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !KERNEL_LINUX #error "No applicable input method." diff --git a/src/nut.c b/src/nut.c index 997d1a50..552359e7 100644 --- a/src/nut.c +++ b/src/nut.c @@ -27,8 +27,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -100,8 +100,10 @@ static int nut_add_ups(const char *name) { /* name = */ cb_name, /* callback = */ nut_read, /* interval = */ 0, - /* user_data = */ &(user_data_t){ - .data = ups, .free_func = free_nut_ups_t, + /* user_data = */ + &(user_data_t){ + .data = ups, + .free_func = free_nut_ups_t, }); sfree(cb_name); diff --git a/src/olsrd.c b/src/olsrd.c index df052889..c8b8b7a9 100644 --- a/src/olsrd.c +++ b/src/olsrd.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/onewire.c b/src/onewire.c index 49c6aa37..729a05c7 100644 --- a/src/onewire.c +++ b/src/onewire.c @@ -21,9 +21,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include @@ -297,9 +297,9 @@ static int cow_read_values(const char *path, const char *name, char file[4096]; char *endptr; - snprintf(file, sizeof(file), "%s/%s", path, - family_info->features[i].filename); - file[sizeof(file) - 1] = 0; + ssnprintf(file, sizeof(file), "%s/%s", path, + family_info->features[i].filename); + file[sizeof(file) - 1] = '\0'; buffer = NULL; buffer_size = 0; @@ -348,11 +348,11 @@ static int cow_read_ds2409(const char *path) { char subpath[4096]; int status; - status = snprintf(subpath, sizeof(subpath), "%s/main", path); + status = ssnprintf(subpath, sizeof(subpath), "%s/main", path); if ((status > 0) && (status < (int)sizeof(subpath))) cow_read_bus(subpath); - status = snprintf(subpath, sizeof(subpath), "%s/aux", path); + status = ssnprintf(subpath, sizeof(subpath), "%s/aux", path); if ((status > 0) && (status < (int)sizeof(subpath))) cow_read_bus(subpath); @@ -384,9 +384,9 @@ static int cow_read_bus(const char *path) { dummy = NULL; if (strcmp("/", path) == 0) - status = snprintf(subpath, sizeof(subpath), "/%s", buffer_ptr); + status = ssnprintf(subpath, sizeof(subpath), "/%s", buffer_ptr); else - status = snprintf(subpath, sizeof(subpath), "%s/%s", path, buffer_ptr); + status = ssnprintf(subpath, sizeof(subpath), "%s/%s", path, buffer_ptr); if ((status <= 0) || (status >= (int)sizeof(subpath))) continue; diff --git a/src/openldap.c b/src/openldap.c index 3897cd16..cd72cdbe 100644 --- a/src/openldap.c +++ b/src/openldap.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if defined(__APPLE__) #pragma clang diagnostic push @@ -306,8 +306,8 @@ static int cldap_read_host(user_data_t *ud) /* {{{ */ if ((olmbdb_list = ldap_get_values_len(st->ld, e, "olmBDBEntryCache")) != NULL) { olmbdb_data = *olmbdb_list[0]; - snprintf(typeinst, sizeof(typeinst), "bdbentrycache-%s", - nc_data.bv_val); + ssnprintf(typeinst, sizeof(typeinst), "bdbentrycache-%s", + nc_data.bv_val); cldap_submit_gauge("cache_size", typeinst, atoll(olmbdb_data.bv_val), st); ldap_value_free_len(olmbdb_list); @@ -316,7 +316,8 @@ static int cldap_read_host(user_data_t *ud) /* {{{ */ if ((olmbdb_list = ldap_get_values_len(st->ld, e, "olmBDBDNCache")) != NULL) { olmbdb_data = *olmbdb_list[0]; - snprintf(typeinst, sizeof(typeinst), "bdbdncache-%s", nc_data.bv_val); + ssnprintf(typeinst, sizeof(typeinst), "bdbdncache-%s", + nc_data.bv_val); cldap_submit_gauge("cache_size", typeinst, atoll(olmbdb_data.bv_val), st); ldap_value_free_len(olmbdb_list); @@ -325,8 +326,8 @@ static int cldap_read_host(user_data_t *ud) /* {{{ */ if ((olmbdb_list = ldap_get_values_len(st->ld, e, "olmBDBIDLCache")) != NULL) { olmbdb_data = *olmbdb_list[0]; - snprintf(typeinst, sizeof(typeinst), "bdbidlcache-%s", - nc_data.bv_val); + ssnprintf(typeinst, sizeof(typeinst), "bdbidlcache-%s", + nc_data.bv_val); cldap_submit_gauge("cache_size", typeinst, atoll(olmbdb_data.bv_val), st); ldap_value_free_len(olmbdb_list); @@ -462,16 +463,17 @@ static int cldap_config_add(oconfig_item_t *ci) /* {{{ */ char callback_name[3 * DATA_MAX_NAME_LEN] = {0}; - snprintf(callback_name, sizeof(callback_name), "openldap/%s/%s", - (st->host != NULL) ? st->host : hostname_g, - (st->name != NULL) ? st->name : "default"); + ssnprintf(callback_name, sizeof(callback_name), "openldap/%s/%s", + (st->host != NULL) ? st->host : hostname_g, + (st->name != NULL) ? st->name : "default"); return plugin_register_complex_read(/* group = */ NULL, /* name = */ callback_name, /* callback = */ cldap_read_host, /* interval = */ 0, &(user_data_t){ - .data = st, .free_func = cldap_free, + .data = st, + .free_func = cldap_free, }); } /* }}} int cldap_config_add */ diff --git a/src/openvpn.c b/src/openvpn.c index 193a9b43..b0e4bf40 100644 --- a/src/openvpn.c +++ b/src/openvpn.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" /** * There is two main kinds of OpenVPN status file: @@ -139,7 +139,8 @@ static void iostats_submit(const char *pinst, const char *tinst, derive_t rx, derive_t tx) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = rx}, {.derive = tx}, + {.derive = rx}, + {.derive = tx}, }; /* NOTE ON THE NEW NAMING SCHEMA: @@ -165,7 +166,8 @@ static void compression_submit(const char *pinst, const char *tinst, derive_t uncompressed, derive_t compressed) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = uncompressed}, {.derive = compressed}, + {.derive = uncompressed}, + {.derive = compressed}, }; vl.values = values; @@ -502,14 +504,15 @@ static int openvpn_config(const char *key, const char *value) { /* callback = */ openvpn_read, /* interval = */ 0, &(user_data_t){ - .data = instance, .free_func = openvpn_free, + .data = instance, + .free_func = openvpn_free, }); if (status == EINVAL) { - WARNING("openvpn plugin: status filename \"%s\" " - "already used, please choose a " - "different one.", - status_name); + ERROR("openvpn plugin: status filename \"%s\" " + "already used, please choose a " + "different one.", + status_name); return -1; } diff --git a/src/oracle.c b/src/oracle.c index 1b17d9c3..cebc7a2f 100644 --- a/src/oracle.c +++ b/src/oracle.c @@ -47,9 +47,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_db_query.h" +#include "utils/common/common.h" +#include "utils/db_query/db_query.h" #include @@ -107,7 +107,7 @@ static void o_report_error(const char *where, /* {{{ */ status = OCIErrorGet(eh, (ub4)record_number, /* sqlstate = */ NULL, &error_code, (text *)&buffer[0], (ub4)sizeof(buffer), OCI_HTYPE_ERROR); - buffer[sizeof(buffer) - 1] = 0; + buffer[sizeof(buffer) - 1] = '\0'; if (status == OCI_NO_DATA) return; @@ -247,9 +247,7 @@ static int o_config_add_database(oconfig_item_t *ci) /* {{{ */ } /* while (status == 0) */ while ((status == 0) && (db->queries_num > 0)) { - db->q_prep_areas = (udb_query_preparation_area_t **)calloc( - db->queries_num, sizeof(*db->q_prep_areas)); - + db->q_prep_areas = calloc(db->queries_num, sizeof(*db->q_prep_areas)); if (db->q_prep_areas == NULL) { WARNING("oracle plugin: calloc failed"); status = -1; @@ -642,7 +640,7 @@ static int o_read_database(o_database_t *db) /* {{{ */ if ((status != OCI_SUCCESS) && (status != OCI_SUCCESS_WITH_INFO)) { char errfunc[256]; - snprintf(errfunc, sizeof(errfunc), "OCILogon(\"%s\")", db->connect_id); + ssnprintf(errfunc, sizeof(errfunc), "OCILogon(\"%s\")", db->connect_id); o_report_error("o_read_database", db->name, NULL, errfunc, oci_error); DEBUG("oracle plugin: OCILogon (%s): db->oci_service_context = %p;", diff --git a/src/ovs_events.c b/src/ovs_events.c index ba3238b6..ae8ac3db 100644 --- a/src/ovs_events.c +++ b/src/ovs_events.c @@ -30,9 +30,9 @@ #include "collectd.h" -#include "common.h" /* auxiliary functions */ +#include "utils/common/common.h" /* auxiliary functions */ -#include "utils_ovs.h" /* OVS helpers */ +#include "utils/ovs/ovs.h" /* OVS helpers */ #define OVS_EVENTS_IFACE_NAME_SIZE 128 #define OVS_EVENTS_IFACE_UUID_SIZE 64 @@ -158,8 +158,8 @@ static char *ovs_events_get_select_params() { return NULL; } opt_buff = new_buff; - int ret = snprintf(opt_buff + buff_off, buff_size - buff_off, option_fmt, - iface->name); + int ret = ssnprintf(opt_buff + buff_off, buff_size - buff_off, option_fmt, + iface->name); if (ret < 0) { sfree(opt_buff); return NULL; @@ -339,9 +339,9 @@ ovs_events_dispatch_notification(const ovs_events_iface_info_t *ifinfo) { } /* fill the notification data */ - snprintf(n.message, sizeof(n.message), - "link state of \"%s\" interface has been changed to \"%s\"", - ifinfo->name, msg_link_status); + ssnprintf(n.message, sizeof(n.message), + "link state of \"%s\" interface has been changed to \"%s\"", + ifinfo->name, msg_link_status); sstrncpy(n.host, hostname_g, sizeof(n.host)); sstrncpy(n.plugin_instance, ifinfo->name, sizeof(n.plugin_instance)); sstrncpy(n.type, "gauge", sizeof(n.type)); diff --git a/src/ovs_stats.c b/src/ovs_stats.c index a629ec6a..e22e851e 100644 --- a/src/ovs_stats.c +++ b/src/ovs_stats.c @@ -28,9 +28,9 @@ * Taras Chornyi */ -#include "common.h" +#include "utils/common/common.h" -#include "utils_ovs.h" /* OvS helpers */ +#include "utils/ovs/ovs.h" /* OvS helpers */ /* Plugin name */ static const char plugin_name[] = "ovs_stats"; @@ -63,6 +63,7 @@ typedef enum iface_counter { tx_512_to_1023_packets, tx_1024_to_1522_packets, tx_1523_to_max_packets, + rx_multicast_packets, tx_multicast_packets, rx_broadcast_packets, tx_broadcast_packets, @@ -70,6 +71,21 @@ typedef enum iface_counter { rx_oversize_errors, rx_fragmented_errors, rx_jabber_errors, + rx_error_bytes, + rx_l3_l4_xsum_error, + rx_management_dropped, + rx_mbuf_allocation_errors, + rx_total_bytes, + rx_total_missed_packets, + rx_undersize_errors, + rx_management_packets, + tx_management_packets, + rx_good_bytes, + tx_good_bytes, + rx_good_packets, + tx_good_packets, + rx_total_packets, + tx_total_packets, __iface_counter_max } iface_counter; @@ -78,15 +94,21 @@ typedef enum iface_counter { #define PORT_NAME_SIZE_MAX 255 #define UUID_SIZE 64 -typedef struct port_s { - char name[PORT_NAME_SIZE_MAX]; /* Port name */ - char port_uuid[UUID_SIZE]; /* Port table _uuid */ +typedef struct interface_s { + char name[PORT_NAME_SIZE_MAX]; /* Interface name */ char iface_uuid[UUID_SIZE]; /* Interface table uuid */ char ex_iface_id[UUID_SIZE]; /* External iface id */ char ex_vm_id[UUID_SIZE]; /* External vm id */ - int64_t stats[IFACE_COUNTER_COUNT]; /* Port statistics */ - struct bridge_list_s *br; /* Pointer to bridge */ - struct port_s *next; /* Next port */ + int64_t stats[IFACE_COUNTER_COUNT]; /* Statistics for interface */ + struct interface_s *next; /* Next interface for associated port */ +} interface_list_t; + +typedef struct port_s { + char name[PORT_NAME_SIZE_MAX]; /* Port name */ + char port_uuid[UUID_SIZE]; /* Port table _uuid */ + struct bridge_list_s *br; /* Pointer to bridge */ + struct interface_s *iface; /* Pointer to first interface */ + struct port_s *next; /* Next port */ } port_list_t; typedef struct bridge_list_s { @@ -123,6 +145,7 @@ static const char *const iface_counter_table[IFACE_COUNTER_COUNT] = { cnt_str(tx_512_to_1023_packets), cnt_str(tx_1024_to_1522_packets), cnt_str(tx_1523_to_max_packets), + cnt_str(rx_multicast_packets), cnt_str(tx_multicast_packets), cnt_str(rx_broadcast_packets), cnt_str(tx_broadcast_packets), @@ -130,6 +153,21 @@ static const char *const iface_counter_table[IFACE_COUNTER_COUNT] = { cnt_str(rx_oversize_errors), cnt_str(rx_fragmented_errors), cnt_str(rx_jabber_errors), + cnt_str(rx_error_bytes), + cnt_str(rx_l3_l4_xsum_error), + cnt_str(rx_management_dropped), + cnt_str(rx_mbuf_allocation_errors), + cnt_str(rx_total_bytes), + cnt_str(rx_total_missed_packets), + cnt_str(rx_undersize_errors), + cnt_str(rx_management_packets), + cnt_str(tx_management_packets), + cnt_str(rx_good_bytes), + cnt_str(tx_good_bytes), + cnt_str(rx_good_packets), + cnt_str(tx_good_packets), + cnt_str(rx_total_packets), + cnt_str(tx_total_packets), }; #undef cnt_str @@ -162,6 +200,9 @@ static ovs_stats_config_t ovs_stats_cfg = { .ovs_db_serv = "6640", /* use default OVS DB service */ }; +/* flag indicating whether or not to publish individual interface statistics */ +static bool interface_stats = false; + static iface_counter ovs_stats_counter_name_to_type(const char *counter) { iface_counter index = not_supported; @@ -223,6 +264,266 @@ static void ovs_stats_submit_two(const char *dev, const char *type, plugin_dispatch_values(&vl); } +static void ovs_stats_submit_interfaces(port_list_t *port) { + char devname[PORT_NAME_SIZE_MAX * 2]; + + bridge_list_t *bridge = port->br; + for (interface_list_t *iface = port->iface; iface != NULL; + iface = iface->next) { + meta_data_t *meta = meta_data_create(); + if (meta != NULL) { + meta_data_add_string(meta, "uuid", iface->iface_uuid); + + if (strlen(iface->ex_vm_id)) + meta_data_add_string(meta, "vm-uuid", iface->ex_vm_id); + + if (strlen(iface->ex_iface_id)) + meta_data_add_string(meta, "iface-id", iface->ex_iface_id); + } + strjoin(devname, sizeof(devname), + (char *[]){ + bridge->name, + port->name, + iface->name, + }, + 3, "."); + ovs_stats_submit_one(devname, "if_collisions", NULL, + iface->stats[collisions], meta); + ovs_stats_submit_two(devname, "if_dropped", NULL, iface->stats[rx_dropped], + iface->stats[tx_dropped], meta); + ovs_stats_submit_two(devname, "if_errors", NULL, iface->stats[rx_errors], + iface->stats[tx_errors], meta); + ovs_stats_submit_two(devname, "if_packets", NULL, iface->stats[rx_packets], + iface->stats[tx_packets], meta); + ovs_stats_submit_one(devname, "if_rx_errors", "crc", + iface->stats[rx_crc_err], meta); + ovs_stats_submit_one(devname, "if_rx_errors", "frame", + iface->stats[rx_frame_err], meta); + ovs_stats_submit_one(devname, "if_rx_errors", "over", + iface->stats[rx_over_err], meta); + ovs_stats_submit_one(devname, "if_rx_octets", NULL, iface->stats[rx_bytes], + meta); + ovs_stats_submit_one(devname, "if_tx_octets", NULL, iface->stats[tx_bytes], + meta); + ovs_stats_submit_two(devname, "if_packets", "1_to_64_packets", + iface->stats[rx_1_to_64_packets], + iface->stats[tx_1_to_64_packets], meta); + ovs_stats_submit_two(devname, "if_packets", "65_to_127_packets", + iface->stats[rx_65_to_127_packets], + iface->stats[tx_65_to_127_packets], meta); + ovs_stats_submit_two(devname, "if_packets", "128_to_255_packets", + iface->stats[rx_128_to_255_packets], + iface->stats[tx_128_to_255_packets], meta); + ovs_stats_submit_two(devname, "if_packets", "256_to_511_packets", + iface->stats[rx_256_to_511_packets], + iface->stats[tx_256_to_511_packets], meta); + ovs_stats_submit_two(devname, "if_packets", "512_to_1023_packets", + iface->stats[rx_512_to_1023_packets], + iface->stats[tx_512_to_1023_packets], meta); + ovs_stats_submit_two(devname, "if_packets", "1024_to_1522_packets", + iface->stats[rx_1024_to_1522_packets], + iface->stats[tx_1024_to_1522_packets], meta); + ovs_stats_submit_two(devname, "if_packets", "1523_to_max_packets", + iface->stats[rx_1523_to_max_packets], + iface->stats[tx_1523_to_max_packets], meta); + ovs_stats_submit_two(devname, "if_packets", "broadcast_packets", + iface->stats[rx_broadcast_packets], + iface->stats[tx_broadcast_packets], meta); + ovs_stats_submit_one(devname, "if_rx_errors", "rx_undersized_errors", + iface->stats[rx_undersized_errors], meta); + ovs_stats_submit_one(devname, "if_rx_errors", "rx_oversize_errors", + iface->stats[rx_oversize_errors], meta); + ovs_stats_submit_one(devname, "if_rx_errors", "rx_fragmented_errors", + iface->stats[rx_fragmented_errors], meta); + ovs_stats_submit_one(devname, "if_rx_errors", "rx_jabber_errors", + iface->stats[rx_jabber_errors], meta); + ovs_stats_submit_one(devname, "if_rx_octets", "rx_error_bytes", + iface->stats[rx_error_bytes], meta); + ovs_stats_submit_one(devname, "if_errors", "rx_l3_l4_xsum_error", + iface->stats[rx_l3_l4_xsum_error], meta); + ovs_stats_submit_one(devname, "if_dropped", "rx_management_dropped", + iface->stats[rx_management_dropped], meta); + ovs_stats_submit_one(devname, "if_errors", "rx_mbuf_allocation_errors", + iface->stats[rx_mbuf_allocation_errors], meta); + ovs_stats_submit_one(devname, "if_octets", "rx_total_bytes", + iface->stats[rx_total_bytes], meta); + ovs_stats_submit_one(devname, "if_packets", "rx_total_missed_packets", + iface->stats[rx_total_missed_packets], meta); + ovs_stats_submit_one(devname, "if_rx_errors", "rx_undersize_errors", + iface->stats[rx_undersize_errors], meta); + ovs_stats_submit_two(devname, "if_packets", "management_packets", + iface->stats[rx_management_packets], + iface->stats[tx_management_packets], meta); + ovs_stats_submit_two(devname, "if_packets", "multicast_packets", + iface->stats[rx_multicast_packets], + iface->stats[tx_multicast_packets], meta); + ovs_stats_submit_two(devname, "if_octets", "good_bytes", + iface->stats[rx_good_bytes], + iface->stats[tx_good_bytes], meta); + ovs_stats_submit_two(devname, "if_packets", "good_packets", + iface->stats[rx_good_packets], + iface->stats[tx_good_packets], meta); + ovs_stats_submit_two(devname, "if_packets", "total_packets", + iface->stats[rx_total_packets], + iface->stats[tx_total_packets], meta); + + meta_data_destroy(meta); + } +} + +static int ovs_stats_get_port_stat_value(port_list_t *port, + iface_counter index) { + if (port == NULL) + return 0; + + int value = 0; + + for (interface_list_t *iface = port->iface; iface != NULL; + iface = iface->next) { + value = value + iface->stats[index]; + } + + return value; +} + +static void ovs_stats_submit_port(port_list_t *port) { + char devname[PORT_NAME_SIZE_MAX * 2]; + + meta_data_t *meta = meta_data_create(); + if (meta != NULL) { + char key_str[DATA_MAX_NAME_LEN]; + int i = 0; + + for (interface_list_t *iface = port->iface; iface != NULL; + iface = iface->next) { + ssnprintf(key_str, sizeof(key_str), "uuid%d", i); + meta_data_add_string(meta, key_str, iface->iface_uuid); + + if (strlen(iface->ex_vm_id)) { + ssnprintf(key_str, sizeof(key_str), "vm-uuid%d", i); + meta_data_add_string(meta, key_str, iface->ex_vm_id); + } + + if (strlen(iface->ex_iface_id)) { + ssnprintf(key_str, sizeof(key_str), "iface-id%d", i); + meta_data_add_string(meta, key_str, iface->ex_iface_id); + } + + i++; + } + } + bridge_list_t *bridge = port->br; + ssnprintf(devname, sizeof(devname), "%s.%s", bridge->name, port->name); + ovs_stats_submit_one(devname, "if_collisions", NULL, + ovs_stats_get_port_stat_value(port, collisions), meta); + ovs_stats_submit_two(devname, "if_dropped", NULL, + ovs_stats_get_port_stat_value(port, rx_dropped), + ovs_stats_get_port_stat_value(port, tx_dropped), meta); + ovs_stats_submit_two(devname, "if_errors", NULL, + ovs_stats_get_port_stat_value(port, rx_errors), + ovs_stats_get_port_stat_value(port, tx_errors), meta); + ovs_stats_submit_two(devname, "if_packets", NULL, + ovs_stats_get_port_stat_value(port, rx_packets), + ovs_stats_get_port_stat_value(port, tx_packets), meta); + ovs_stats_submit_one(devname, "if_rx_errors", "crc", + ovs_stats_get_port_stat_value(port, rx_crc_err), meta); + ovs_stats_submit_one(devname, "if_rx_errors", "frame", + ovs_stats_get_port_stat_value(port, rx_frame_err), meta); + ovs_stats_submit_one(devname, "if_rx_errors", "over", + ovs_stats_get_port_stat_value(port, rx_over_err), meta); + ovs_stats_submit_one(devname, "if_rx_octets", NULL, + ovs_stats_get_port_stat_value(port, rx_bytes), meta); + ovs_stats_submit_one(devname, "if_tx_octets", NULL, + ovs_stats_get_port_stat_value(port, tx_bytes), meta); + ovs_stats_submit_two(devname, "if_packets", "1_to_64_packets", + ovs_stats_get_port_stat_value(port, rx_1_to_64_packets), + ovs_stats_get_port_stat_value(port, tx_1_to_64_packets), + meta); + ovs_stats_submit_two( + devname, "if_packets", "65_to_127_packets", + ovs_stats_get_port_stat_value(port, rx_65_to_127_packets), + ovs_stats_get_port_stat_value(port, tx_65_to_127_packets), meta); + ovs_stats_submit_two( + devname, "if_packets", "128_to_255_packets", + ovs_stats_get_port_stat_value(port, rx_128_to_255_packets), + ovs_stats_get_port_stat_value(port, tx_128_to_255_packets), meta); + ovs_stats_submit_two( + devname, "if_packets", "256_to_511_packets", + ovs_stats_get_port_stat_value(port, rx_256_to_511_packets), + ovs_stats_get_port_stat_value(port, tx_256_to_511_packets), meta); + ovs_stats_submit_two( + devname, "if_packets", "512_to_1023_packets", + ovs_stats_get_port_stat_value(port, rx_512_to_1023_packets), + ovs_stats_get_port_stat_value(port, tx_512_to_1023_packets), meta); + ovs_stats_submit_two( + devname, "if_packets", "1024_to_1522_packets", + ovs_stats_get_port_stat_value(port, rx_1024_to_1522_packets), + ovs_stats_get_port_stat_value(port, tx_1024_to_1522_packets), meta); + ovs_stats_submit_two( + devname, "if_packets", "1523_to_max_packets", + ovs_stats_get_port_stat_value(port, rx_1523_to_max_packets), + ovs_stats_get_port_stat_value(port, tx_1523_to_max_packets), meta); + ovs_stats_submit_two( + devname, "if_packets", "broadcast_packets", + ovs_stats_get_port_stat_value(port, rx_broadcast_packets), + ovs_stats_get_port_stat_value(port, tx_broadcast_packets), meta); + ovs_stats_submit_one( + devname, "if_rx_errors", "rx_undersized_errors", + ovs_stats_get_port_stat_value(port, rx_undersized_errors), meta); + ovs_stats_submit_one(devname, "if_rx_errors", "rx_oversize_errors", + ovs_stats_get_port_stat_value(port, rx_oversize_errors), + meta); + ovs_stats_submit_one( + devname, "if_rx_errors", "rx_fragmented_errors", + ovs_stats_get_port_stat_value(port, rx_fragmented_errors), meta); + ovs_stats_submit_one(devname, "if_rx_errors", "rx_jabber_errors", + ovs_stats_get_port_stat_value(port, rx_jabber_errors), + meta); + ovs_stats_submit_one(devname, "if_rx_octets", "rx_error_bytes", + ovs_stats_get_port_stat_value(port, rx_error_bytes), + meta); + ovs_stats_submit_one(devname, "if_errors", "rx_l3_l4_xsum_error", + ovs_stats_get_port_stat_value(port, rx_l3_l4_xsum_error), + meta); + ovs_stats_submit_one( + devname, "if_dropped", "rx_management_dropped", + ovs_stats_get_port_stat_value(port, rx_management_dropped), meta); + ovs_stats_submit_one( + devname, "if_errors", "rx_mbuf_allocation_errors", + ovs_stats_get_port_stat_value(port, rx_mbuf_allocation_errors), meta); + ovs_stats_submit_one(devname, "if_octets", "rx_total_bytes", + ovs_stats_get_port_stat_value(port, rx_total_bytes), + meta); + ovs_stats_submit_one( + devname, "if_packets", "rx_total_missed_packets", + ovs_stats_get_port_stat_value(port, rx_total_missed_packets), meta); + ovs_stats_submit_one(devname, "if_rx_errors", "rx_undersize_errors", + ovs_stats_get_port_stat_value(port, rx_undersize_errors), + meta); + ovs_stats_submit_two( + devname, "if_packets", "management_packets", + ovs_stats_get_port_stat_value(port, rx_management_packets), + ovs_stats_get_port_stat_value(port, tx_management_packets), meta); + ovs_stats_submit_two( + devname, "if_packets", "multicast_packets", + ovs_stats_get_port_stat_value(port, rx_multicast_packets), + ovs_stats_get_port_stat_value(port, tx_multicast_packets), meta); + ovs_stats_submit_two(devname, "if_octets", "good_bytes", + ovs_stats_get_port_stat_value(port, rx_good_bytes), + ovs_stats_get_port_stat_value(port, tx_good_bytes), + meta); + ovs_stats_submit_two(devname, "if_packets", "good_packets", + ovs_stats_get_port_stat_value(port, rx_good_packets), + ovs_stats_get_port_stat_value(port, tx_good_packets), + meta); + ovs_stats_submit_two(devname, "if_packets", "total_packets", + ovs_stats_get_port_stat_value(port, rx_total_packets), + ovs_stats_get_port_stat_value(port, tx_total_packets), + meta); + + meta_data_destroy(meta); +} + static port_list_t *ovs_stats_get_port(const char *uuid) { if (uuid == NULL) return NULL; @@ -234,17 +535,69 @@ static port_list_t *ovs_stats_get_port(const char *uuid) { return NULL; } -static port_list_t *ovs_stats_get_port_by_name(const char *name) { - if (name == NULL) +static port_list_t *ovs_stats_get_port_by_interface_uuid(const char *uuid) { + if (uuid == NULL) return NULL; - for (port_list_t *port = g_port_list_head; port != NULL; port = port->next) - if ((strncmp(port->name, name, strlen(port->name)) == 0) && - strlen(name) == strlen(port->name)) - return port; + for (port_list_t *port = g_port_list_head; port != NULL; port = port->next) { + for (interface_list_t *iface = port->iface; iface != NULL; + iface = iface->next) { + if (strncmp(iface->iface_uuid, uuid, strlen(uuid)) == 0) + return port; + } + } + return NULL; +} + +static interface_list_t *ovs_stats_get_port_interface(port_list_t *port, + const char *uuid) { + if (port == NULL || uuid == NULL) + return NULL; + + for (interface_list_t *iface = port->iface; iface != NULL; + iface = iface->next) { + if (strncmp(iface->iface_uuid, uuid, strlen(uuid)) == 0) + return iface; + } + return NULL; +} + +static interface_list_t *ovs_stats_get_interface(const char *uuid) { + if (uuid == NULL) + return NULL; + + for (port_list_t *port = g_port_list_head; port != NULL; port = port->next) { + for (interface_list_t *iface = port->iface; iface != NULL; + iface = iface->next) { + if (strncmp(iface->iface_uuid, uuid, strlen(uuid)) == 0) + return iface; + } + } return NULL; } +static interface_list_t *ovs_stats_new_port_interface(port_list_t *port, + const char *uuid) { + if (uuid == NULL) + return NULL; + + interface_list_t *iface = ovs_stats_get_port_interface(port, uuid); + + if (iface == NULL) { + iface = calloc(1, sizeof(*iface)); + if (iface == NULL) { + ERROR("%s: Error allocating interface", plugin_name); + return NULL; + } + memset(iface->stats, -1, sizeof(int64_t[IFACE_COUNTER_COUNT])); + sstrncpy(iface->iface_uuid, uuid, sizeof(iface->iface_uuid)); + interface_list_t *iface_head = port->iface; + iface->next = iface_head; + port->iface = iface; + } + return iface; +} + /* Create or get port by port uuid */ static port_list_t *ovs_stats_new_port(bridge_list_t *bridge, const char *uuid) { @@ -254,22 +607,17 @@ static port_list_t *ovs_stats_new_port(bridge_list_t *bridge, port_list_t *port = ovs_stats_get_port(uuid); if (port == NULL) { - port = (port_list_t *)calloc(1, sizeof(port_list_t)); - if (!port) { + port = calloc(1, sizeof(*port)); + if (port == NULL) { ERROR("%s: Error allocating port", plugin_name); return NULL; } - memset(port->stats, -1, sizeof(int64_t[IFACE_COUNTER_COUNT])); sstrncpy(port->port_uuid, uuid, sizeof(port->port_uuid)); - pthread_mutex_lock(&g_stats_lock); port->next = g_port_list_head; g_port_list_head = port; - pthread_mutex_unlock(&g_stats_lock); } if (bridge != NULL) { - pthread_mutex_lock(&g_stats_lock); port->br = bridge; - pthread_mutex_unlock(&g_stats_lock); } return port; } @@ -288,36 +636,51 @@ static bridge_list_t *ovs_stats_get_bridge(bridge_list_t *head, return NULL; } +/* Check if bridge is configured to be monitored in config file */ +static int ovs_stats_is_monitored_bridge(const char *br_name) { + /* if no bridges are configured, return true */ + if (g_monitored_bridge_list_head == NULL) + return 1; + + /* check if given bridge exists */ + if (ovs_stats_get_bridge(g_monitored_bridge_list_head, br_name) != NULL) + return 1; + + return 0; +} + /* Delete bridge */ static int ovs_stats_del_bridge(yajl_val bridge) { const char *old[] = {"old", NULL}; const char *name[] = {"name", NULL}; - yajl_val row; - - if (bridge && YAJL_IS_OBJECT(bridge)) { - row = yajl_tree_get(bridge, old, yajl_t_object); - if (row && YAJL_IS_OBJECT(row)) { - yajl_val br_name = yajl_tree_get(row, name, yajl_t_string); - if (br_name && YAJL_IS_STRING(br_name)) { - bridge_list_t *prev_br = g_bridge_list_head; - for (bridge_list_t *br = g_bridge_list_head; br != NULL; - prev_br = br, br = br->next) { - if ((strncmp(br->name, br_name->u.string, strlen(br->name)) == 0) && - strlen(br->name) == strlen(br_name->u.string)) { - if (br == g_bridge_list_head) - g_bridge_list_head = br->next; - else - prev_br->next = br->next; - sfree(br->name); - sfree(br); - break; - } - } - } - } - } else + if (!bridge || !YAJL_IS_OBJECT(bridge)) { WARNING("%s: Incorrect data for deleting bridge", plugin_name); + return 0; + } + + yajl_val row = yajl_tree_get(bridge, old, yajl_t_object); + if (!row || !YAJL_IS_OBJECT(row)) + return 0; + + yajl_val br_name = yajl_tree_get(row, name, yajl_t_string); + if (!br_name || !YAJL_IS_STRING(br_name)) + return 0; + + bridge_list_t *prev_br = g_bridge_list_head; + for (bridge_list_t *br = g_bridge_list_head; br != NULL; + prev_br = br, br = br->next) { + if ((strncmp(br->name, br_name->u.string, strlen(br->name)) == 0) && + strlen(br->name) == strlen(br_name->u.string)) { + if (br == g_bridge_list_head) + g_bridge_list_head = br->next; + else + prev_br->next = br->next; + sfree(br->name); + sfree(br); + break; + } + } return 0; } @@ -326,64 +689,68 @@ static int ovs_stats_update_bridge(yajl_val bridge) { const char *new[] = {"new", NULL}; const char *name[] = {"name", NULL}; const char *ports[] = {"ports", NULL}; - bridge_list_t *br = NULL; - - if (bridge && YAJL_IS_OBJECT(bridge)) { - yajl_val row = yajl_tree_get(bridge, new, yajl_t_object); - if (row && YAJL_IS_OBJECT(row)) { - yajl_val br_name = yajl_tree_get(row, name, yajl_t_string); - yajl_val br_ports = yajl_tree_get(row, ports, yajl_t_array); - if (br_name && YAJL_IS_STRING(br_name)) { - br = ovs_stats_get_bridge(g_bridge_list_head, YAJL_GET_STRING(br_name)); - pthread_mutex_lock(&g_stats_lock); - if (br == NULL) { - br = calloc(1, sizeof(*br)); - if (!br) { - pthread_mutex_unlock(&g_stats_lock); - ERROR("%s: calloc(%zu) failed.", plugin_name, sizeof(*br)); - return -1; - } - char *tmp = YAJL_GET_STRING(br_name); - if (tmp != NULL) - br->name = strdup(tmp); - if (br->name == NULL) { - sfree(br); - pthread_mutex_unlock(&g_stats_lock); - ERROR("%s: strdup failed.", plugin_name); - return -1; - } - br->next = g_bridge_list_head; - g_bridge_list_head = br; - } - pthread_mutex_unlock(&g_stats_lock); - } - if (br_ports && YAJL_IS_ARRAY(br_ports)) { - char *tmp = YAJL_GET_STRING(br_ports->u.array.values[0]); - if (tmp != NULL && strcmp("set", tmp) == 0) { - yajl_val *array = YAJL_GET_ARRAY(br_ports)->values; - size_t array_len = YAJL_GET_ARRAY(br_ports)->len; - if (array != NULL && array_len > 0 && YAJL_IS_ARRAY(array[1])) { - if (YAJL_GET_ARRAY(array[1]) == NULL) - goto failure; - else { - yajl_val *ports_arr = YAJL_GET_ARRAY(array[1])->values; - size_t ports_num = YAJL_GET_ARRAY(array[1])->len; - for (size_t i = 0; i < ports_num && ports_arr != NULL; i++) { - tmp = YAJL_GET_STRING(ports_arr[i]->u.array.values[1]); - if (tmp != NULL) - ovs_stats_new_port(br, tmp); - else - goto failure; - } - } - } - } else - ovs_stats_new_port(br, YAJL_GET_STRING(br_ports->u.array.values[1])); + if (!bridge || !YAJL_IS_OBJECT(bridge)) + goto failure; + + yajl_val row = yajl_tree_get(bridge, new, yajl_t_object); + if (!row || !YAJL_IS_OBJECT(row)) + return 0; + + yajl_val br_name = yajl_tree_get(row, name, yajl_t_string); + if (!br_name || !YAJL_IS_STRING(br_name)) + return 0; + + if (!ovs_stats_is_monitored_bridge(YAJL_GET_STRING(br_name))) + return 0; + + bridge_list_t *br = + ovs_stats_get_bridge(g_bridge_list_head, YAJL_GET_STRING(br_name)); + if (br == NULL) { + br = calloc(1, sizeof(*br)); + if (br == NULL) { + ERROR("%s: calloc(%zu) failed.", plugin_name, sizeof(*br)); + return -1; + } + + char *tmp = YAJL_GET_STRING(br_name); + if (tmp != NULL) + br->name = strdup(tmp); + + if (br->name == NULL) { + sfree(br); + ERROR("%s: strdup failed.", plugin_name); + return -1; + } + + br->next = g_bridge_list_head; + g_bridge_list_head = br; + } + + yajl_val br_ports = yajl_tree_get(row, ports, yajl_t_array); + if (!br_ports || !YAJL_IS_ARRAY(br_ports)) + return 0; + + char *tmp = YAJL_GET_STRING(br_ports->u.array.values[0]); + if (tmp != NULL && strcmp("set", tmp) == 0) { + yajl_val *array = YAJL_GET_ARRAY(br_ports)->values; + size_t array_len = YAJL_GET_ARRAY(br_ports)->len; + if (array != NULL && array_len > 0 && YAJL_IS_ARRAY(array[1])) { + if (YAJL_GET_ARRAY(array[1]) == NULL) + goto failure; + + yajl_val *ports_arr = YAJL_GET_ARRAY(array[1])->values; + size_t ports_num = YAJL_GET_ARRAY(array[1])->len; + for (size_t i = 0; i < ports_num && ports_arr != NULL; i++) { + tmp = YAJL_GET_STRING(ports_arr[i]->u.array.values[1]); + if (tmp != NULL) + ovs_stats_new_port(br, tmp); + else + goto failure; } } } else { - goto failure; + ovs_stats_new_port(br, YAJL_GET_STRING(br_ports->u.array.values[1])); } return 0; @@ -420,31 +787,32 @@ static void ovs_stats_bridge_table_change_cb(yajl_val jupdates) { } */ const char *path[] = {"Bridge", NULL}; - yajl_val bridges = yajl_tree_get(jupdates, path, yajl_t_object); - if (bridges && YAJL_IS_OBJECT(bridges)) { - for (size_t i = 0; i < YAJL_GET_OBJECT(bridges)->len; i++) { - yajl_val bridge = YAJL_GET_OBJECT(bridges)->values[i]; - ovs_stats_update_bridge(bridge); - } + if (!bridges || !YAJL_IS_OBJECT(bridges)) + return; + + pthread_mutex_lock(&g_stats_lock); + for (size_t i = 0; i < YAJL_GET_OBJECT(bridges)->len; i++) { + yajl_val bridge = YAJL_GET_OBJECT(bridges)->values[i]; + ovs_stats_update_bridge(bridge); } + pthread_mutex_unlock(&g_stats_lock); } /* Handle Bridge Table delete event */ static void ovs_stats_bridge_table_delete_cb(yajl_val jupdates) { const char *path[] = {"Bridge", NULL}; yajl_val bridges = yajl_tree_get(jupdates, path, yajl_t_object); - yajl_val bridge; - if (bridges && YAJL_IS_OBJECT(bridges)) { - pthread_mutex_lock(&g_stats_lock); - for (size_t i = 0; i < YAJL_GET_OBJECT(bridges)->len; i++) { - bridge = YAJL_GET_OBJECT(bridges)->values[i]; - ovs_stats_del_bridge(bridge); - } - pthread_mutex_unlock(&g_stats_lock); + if (!bridges || !YAJL_IS_OBJECT(bridges)) + return; + + pthread_mutex_lock(&g_stats_lock); + for (size_t i = 0; i < YAJL_GET_OBJECT(bridges)->len; i++) { + yajl_val bridge = YAJL_GET_OBJECT(bridges)->values[i]; + ovs_stats_del_bridge(bridge); } - return; + pthread_mutex_unlock(&g_stats_lock); } /* Handle JSON with Bridge table initial values */ @@ -457,32 +825,62 @@ static void ovs_stats_bridge_table_result_cb(yajl_val jresult, return; } -/* Update port name */ +/* Update port name and interface UUID(s)*/ static int ovs_stats_update_port(const char *uuid, yajl_val port) { const char *new[] = {"new", NULL}; const char *name[] = {"name", NULL}; - yajl_val row; - port_list_t *portentry = NULL; - if (port && YAJL_IS_OBJECT(port)) { - row = yajl_tree_get(port, new, yajl_t_object); - if (row && YAJL_IS_OBJECT(row)) { - yajl_val port_name = yajl_tree_get(row, name, yajl_t_string); - if (port_name && YAJL_IS_STRING(port_name)) { - portentry = ovs_stats_get_port(uuid); - if (portentry == NULL) - portentry = ovs_stats_new_port(NULL, uuid); - if (portentry) { - pthread_mutex_lock(&g_stats_lock); - sstrncpy(portentry->name, YAJL_GET_STRING(port_name), - sizeof(portentry->name)); - pthread_mutex_unlock(&g_stats_lock); - } - } - } - } else { + + if (!port || !YAJL_IS_OBJECT(port)) { ERROR("Incorrect JSON Port data"); return -1; } + + yajl_val row = yajl_tree_get(port, new, yajl_t_object); + if (!row || !YAJL_IS_OBJECT(row)) + return 0; + + yajl_val port_name = yajl_tree_get(row, name, yajl_t_string); + if (!port_name || !YAJL_IS_STRING(port_name)) + return 0; + + /* Create or get port by port uuid */ + port_list_t *portentry = ovs_stats_new_port(NULL, uuid); + if (!portentry) + return 0; + + sstrncpy(portentry->name, YAJL_GET_STRING(port_name), + sizeof(portentry->name)); + + yajl_val ifaces_root = ovs_utils_get_value_by_key(row, "interfaces"); + char *ifaces_root_key = + YAJL_GET_STRING(YAJL_GET_ARRAY(ifaces_root)->values[0]); + + if (strcmp("set", ifaces_root_key) == 0) { + // ifaces_root is ["set", [[ "uuid", "" ], [ "uuid", + // "" ], ... ]] + yajl_val ifaces_list = YAJL_GET_ARRAY(ifaces_root)->values[1]; + + // ifaces_list is [[ "uuid", "" ], [ "uuid", + // "" ], ... ]] + for (size_t i = 0; i < YAJL_GET_ARRAY(ifaces_list)->len; i++) { + yajl_val iface_tuple = YAJL_GET_ARRAY(ifaces_list)->values[i]; + + // iface_tuple is [ "uuid", "" ] + char *iface_uuid_str = + YAJL_GET_STRING(YAJL_GET_ARRAY(iface_tuple)->values[1]); + + // Also checks if interface already registered + ovs_stats_new_port_interface(portentry, iface_uuid_str); + } + } else { + // ifaces_root is [ "uuid", "" ] + char *iface_uuid_str = + YAJL_GET_STRING(YAJL_GET_ARRAY(ifaces_root)->values[1]); + + // Also checks if interface already registered + ovs_stats_new_port_interface(portentry, iface_uuid_str); + } + return 0; } @@ -496,6 +894,14 @@ static int ovs_stats_del_port(const char *uuid) { g_port_list_head = port->next; else prev_port->next = port->next; + + for (interface_list_t *iface = port->iface; iface != NULL; + iface = port->iface) { + interface_list_t *del = iface; + port->iface = iface->next; + sfree(del); + } + sfree(port); break; } @@ -522,13 +928,15 @@ static void ovs_stats_port_table_change_cb(yajl_val jupdates) { */ const char *path[] = {"Port", NULL}; yajl_val ports = yajl_tree_get(jupdates, path, yajl_t_object); - yajl_val port; - if (ports && YAJL_IS_OBJECT(ports)) { - for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) { - port = YAJL_GET_OBJECT(ports)->values[i]; - ovs_stats_update_port(YAJL_GET_OBJECT(ports)->keys[i], port); - } + if (!ports || !YAJL_IS_OBJECT(ports)) + return; + + pthread_mutex_lock(&g_stats_lock); + for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) { + yajl_val port = YAJL_GET_OBJECT(ports)->values[i]; + ovs_stats_update_port(YAJL_GET_OBJECT(ports)->keys[i], port); } + pthread_mutex_unlock(&g_stats_lock); return; } @@ -545,69 +953,75 @@ static void ovs_stats_port_table_result_cb(yajl_val jresult, yajl_val jerror) { static void ovs_stats_port_table_delete_cb(yajl_val jupdates) { const char *path[] = {"Port", NULL}; yajl_val ports = yajl_tree_get(jupdates, path, yajl_t_object); + if (!ports || !YAJL_IS_OBJECT(ports)) + return; + pthread_mutex_lock(&g_stats_lock); - if (ports && YAJL_IS_OBJECT(ports)) - for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) { - ovs_stats_del_port(YAJL_GET_OBJECT(ports)->keys[i]); - } + for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) { + ovs_stats_del_port(YAJL_GET_OBJECT(ports)->keys[i]); + } pthread_mutex_unlock(&g_stats_lock); return; } /* Update interface statistics */ -static int ovs_stats_update_iface_stats(port_list_t *port, yajl_val stats) { - yajl_val stat; - iface_counter counter_index = 0; - char *counter_name = NULL; - int64_t counter_value = 0; - if (stats && YAJL_IS_ARRAY(stats)) - for (size_t i = 0; i < YAJL_GET_ARRAY(stats)->len; i++) { - stat = YAJL_GET_ARRAY(stats)->values[i]; - if (!YAJL_IS_ARRAY(stat)) - return -1; - counter_name = YAJL_GET_STRING(YAJL_GET_ARRAY(stat)->values[0]); - counter_index = ovs_stats_counter_name_to_type(counter_name); - counter_value = YAJL_GET_INTEGER(YAJL_GET_ARRAY(stat)->values[1]); - if (counter_index == not_supported) - continue; - port->stats[counter_index] = counter_value; - } +static int ovs_stats_update_iface_stats(interface_list_t *iface, + yajl_val stats) { + + if (!stats || !YAJL_IS_ARRAY(stats)) + return 0; + + for (size_t i = 0; i < YAJL_GET_ARRAY(stats)->len; i++) { + yajl_val stat = YAJL_GET_ARRAY(stats)->values[i]; + if (!YAJL_IS_ARRAY(stat)) + return -1; + + char *counter_name = YAJL_GET_STRING(YAJL_GET_ARRAY(stat)->values[0]); + iface_counter counter_index = ovs_stats_counter_name_to_type(counter_name); + int64_t counter_value = YAJL_GET_INTEGER(YAJL_GET_ARRAY(stat)->values[1]); + if (counter_index == not_supported) + continue; + + iface->stats[counter_index] = counter_value; + } return 0; } /* Update interface external_ids */ -static int ovs_stats_update_iface_ext_ids(port_list_t *port, yajl_val ext_ids) { - yajl_val ext_id; - char *key; - char *value; - - if (ext_ids && YAJL_IS_ARRAY(ext_ids)) - for (size_t i = 0; i < YAJL_GET_ARRAY(ext_ids)->len; i++) { - ext_id = YAJL_GET_ARRAY(ext_ids)->values[i]; - if (!YAJL_IS_ARRAY(ext_id)) - return -1; - key = YAJL_GET_STRING(YAJL_GET_ARRAY(ext_id)->values[0]); - value = YAJL_GET_STRING(YAJL_GET_ARRAY(ext_id)->values[1]); - if (key && value) { - if (strncmp(key, "iface-id", strlen(key)) == 0) - sstrncpy(port->ex_iface_id, value, sizeof(port->ex_iface_id)); - else if (strncmp(key, "vm-uuid", strlen(key)) == 0) - sstrncpy(port->ex_vm_id, value, sizeof(port->ex_vm_id)); +static int ovs_stats_update_iface_ext_ids(interface_list_t *iface, + yajl_val ext_ids) { + + if (!ext_ids || !YAJL_IS_ARRAY(ext_ids)) + return 0; + + for (size_t i = 0; i < YAJL_GET_ARRAY(ext_ids)->len; i++) { + yajl_val ext_id = YAJL_GET_ARRAY(ext_ids)->values[i]; + if (!YAJL_IS_ARRAY(ext_id)) + return -1; + + char *key = YAJL_GET_STRING(YAJL_GET_ARRAY(ext_id)->values[0]); + char *value = YAJL_GET_STRING(YAJL_GET_ARRAY(ext_id)->values[1]); + if (key && value) { + if (strncmp(key, "iface-id", strlen(key)) == 0) { + sstrncpy(iface->ex_iface_id, value, sizeof(iface->ex_iface_id)); + } else if (strncmp(key, "vm-uuid", strlen(key)) == 0) { + sstrncpy(iface->ex_vm_id, value, sizeof(iface->ex_vm_id)); } } + } return 0; } /* Get interface statistic and external_ids */ -static int ovs_stats_update_iface(yajl_val iface) { - if (!iface || !YAJL_IS_OBJECT(iface)) { - ERROR("ovs_stats plugin: incorrect JSON port data"); +static int ovs_stats_update_iface(yajl_val iface_obj) { + if (!iface_obj || !YAJL_IS_OBJECT(iface_obj)) { + ERROR("ovs_stats plugin: incorrect JSON interface data"); return -1; } - yajl_val row = ovs_utils_get_value_by_key(iface, "new"); + yajl_val row = ovs_utils_get_value_by_key(iface_obj, "new"); if (!row || !YAJL_IS_OBJECT(row)) return 0; @@ -615,13 +1029,26 @@ static int ovs_stats_update_iface(yajl_val iface) { if (!iface_name || !YAJL_IS_STRING(iface_name)) return 0; - port_list_t *port = ovs_stats_get_port_by_name(YAJL_GET_STRING(iface_name)); - if (port == NULL) + yajl_val iface_uuid = ovs_utils_get_value_by_key(row, "_uuid"); + if (!iface_uuid || !YAJL_IS_ARRAY(iface_uuid) || + YAJL_GET_ARRAY(iface_uuid)->len != 2) + return 0; + + char *iface_uuid_str = YAJL_GET_STRING(YAJL_GET_ARRAY(iface_uuid)->values[1]); + if (iface_uuid_str == NULL) { + ERROR("ovs_stats plugin: incorrect JSON interface data"); + return -1; + } + + interface_list_t *iface = ovs_stats_get_interface(iface_uuid_str); + if (iface == NULL) return 0; + sstrncpy(iface->name, YAJL_GET_STRING(iface_name), sizeof(iface->name)); + yajl_val iface_stats = ovs_utils_get_value_by_key(row, "statistics"); yajl_val iface_ext_ids = ovs_utils_get_value_by_key(row, "external_ids"); - yajl_val iface_uuid = ovs_utils_get_value_by_key(row, "_uuid"); + /* * { "statistics": [ @@ -641,21 +1068,43 @@ static int ovs_stats_update_iface(yajl_val iface) { } Check that statistics is an array with 2 elements */ + if (iface_stats && YAJL_IS_ARRAY(iface_stats) && YAJL_GET_ARRAY(iface_stats)->len == 2) - ovs_stats_update_iface_stats(port, YAJL_GET_ARRAY(iface_stats)->values[1]); + ovs_stats_update_iface_stats(iface, YAJL_GET_ARRAY(iface_stats)->values[1]); + if (iface_ext_ids && YAJL_IS_ARRAY(iface_ext_ids)) - ovs_stats_update_iface_ext_ids(port, + ovs_stats_update_iface_ext_ids(iface, YAJL_GET_ARRAY(iface_ext_ids)->values[1]); - if (iface_uuid && YAJL_IS_ARRAY(iface_uuid) && - YAJL_GET_ARRAY(iface_uuid)->len == 2 && - YAJL_GET_STRING(YAJL_GET_ARRAY(iface_uuid)->values[1]) != NULL) - sstrncpy(port->iface_uuid, - YAJL_GET_STRING(YAJL_GET_ARRAY(iface_uuid)->values[1]), - sizeof(port->iface_uuid)); - else { - ERROR("ovs_stats plugin: incorrect JSON interface data"); - return -1; + + return 0; +} + +/* Delete interface */ +static int ovs_stats_del_interface(const char *uuid) { + port_list_t *port = ovs_stats_get_port_by_interface_uuid(uuid); + + if (port == NULL) + return 0; + + interface_list_t *prev_iface = NULL; + + for (interface_list_t *iface = port->iface; iface != NULL; + iface = port->iface) { + if (strncmp(iface->iface_uuid, uuid, strlen(iface->iface_uuid))) { + + interface_list_t *del = iface; + + if (prev_iface == NULL) + port->iface = iface->next; + else + prev_iface->next = iface->next; + + sfree(del); + break; + } else { + prev_iface = iface; + } } return 0; @@ -714,12 +1163,16 @@ static void ovs_stats_interface_table_change_cb(yajl_val jupdates) { } */ const char *path[] = {"Interface", NULL}; - yajl_val ports = yajl_tree_get(jupdates, path, yajl_t_object); + yajl_val interfaces = yajl_tree_get(jupdates, path, yajl_t_object); + if (!interfaces || !YAJL_IS_OBJECT(interfaces)) + return; + pthread_mutex_lock(&g_stats_lock); - if (ports && YAJL_IS_OBJECT(ports)) - for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) - ovs_stats_update_iface(YAJL_GET_OBJECT(ports)->values[i]); + for (size_t i = 0; i < YAJL_GET_OBJECT(interfaces)->len; i++) { + ovs_stats_update_iface(YAJL_GET_OBJECT(interfaces)->values[i]); + } pthread_mutex_unlock(&g_stats_lock); + return; } @@ -733,6 +1186,22 @@ static void ovs_stats_interface_table_result_cb(yajl_val jresult, return; } +/* Handle Interface Table delete event */ +static void ovs_stats_interface_table_delete_cb(yajl_val jupdates) { + const char *path[] = {"Interface", NULL}; + yajl_val interfaces = yajl_tree_get(jupdates, path, yajl_t_object); + if (!interfaces || !YAJL_IS_OBJECT(interfaces)) + return; + + pthread_mutex_lock(&g_stats_lock); + for (size_t i = 0; i < YAJL_GET_OBJECT(interfaces)->len; i++) { + ovs_stats_del_interface(YAJL_GET_OBJECT(interfaces)->keys[i]); + } + pthread_mutex_unlock(&g_stats_lock); + + return; +} + /* Setup OVS DB table callbacks */ static void ovs_stats_initialize(ovs_db_t *pdb) { const char *bridge_columns[] = {"name", "ports", NULL}; @@ -766,25 +1235,23 @@ static void ovs_stats_initialize(ovs_db_t *pdb) { ovs_stats_interface_table_result_cb, OVS_DB_TABLE_CB_FLAG_INITIAL | OVS_DB_TABLE_CB_FLAG_INSERT | OVS_DB_TABLE_CB_FLAG_MODIFY); -} - -/* Check if bridge is configured to be monitored in config file */ -static int ovs_stats_is_monitored_bridge(const char *br_name) { - /* if no bridges are configured, return true */ - if (g_monitored_bridge_list_head == NULL) - return 1; - - /* check if given bridge exists */ - if (ovs_stats_get_bridge(g_monitored_bridge_list_head, br_name) != NULL) - return 1; - return 0; + ovs_db_table_cb_register(pdb, "Interface", interface_columns, + ovs_stats_interface_table_delete_cb, NULL, + OVS_DB_TABLE_CB_FLAG_DELETE); } /* Delete all ports from port list */ static void ovs_stats_free_port_list(port_list_t *head) { for (port_list_t *i = head; i != NULL;) { port_list_t *del = i; + + for (interface_list_t *iface = i->iface; iface != NULL; iface = i->iface) { + interface_list_t *del2 = iface; + i->iface = iface->next; + sfree(del2); + } + i = i->next; sfree(del); } @@ -850,7 +1317,7 @@ static int ovs_stats_plugin_config(oconfig_item_t *ci) { char const *br_name = child->values[j].value.string; if ((bridge = ovs_stats_get_bridge(g_monitored_bridge_list_head, br_name)) == NULL) { - if ((bridge = calloc(1, sizeof(bridge_list_t))) == NULL) { + if ((bridge = calloc(1, sizeof(*bridge))) == NULL) { ERROR("%s: Error allocating memory for bridge", plugin_name); goto cleanup_fail; } else { @@ -871,6 +1338,11 @@ static int ovs_stats_plugin_config(oconfig_item_t *ci) { } } } + } else if (strcasecmp("InterfaceStats", child->key) == 0) { + if (cf_util_get_boolean(child, &interface_stats) != 0) { + ERROR("%s: parse '%s' option failed", plugin_name, child->key); + return -1; + } } else { WARNING("%s: option '%s' not allowed here", plugin_name, child->key); goto cleanup_fail; @@ -909,89 +1381,22 @@ static int ovs_stats_plugin_init(void) { /* OvS stats read callback. Read bridge/port information and submit it*/ static int ovs_stats_plugin_read(__attribute__((unused)) user_data_t *ud) { - bridge_list_t *bridge; - port_list_t *port; - char devname[PORT_NAME_SIZE_MAX * 2]; - pthread_mutex_lock(&g_stats_lock); - for (bridge = g_bridge_list_head; bridge != NULL; bridge = bridge->next) { - if (ovs_stats_is_monitored_bridge(bridge->name)) { - for (port = g_port_list_head; port != NULL; port = port->next) - if (port->br == bridge) { - if (strlen(port->name) == 0) - /* Skip port w/o name. This is possible when read callback - * is called after Interface Table update callback but before - * Port table Update callback. Will add this port on next read */ - continue; - meta_data_t *meta = meta_data_create(); - if (meta != NULL) { - meta_data_add_string(meta, "uuid", port->iface_uuid); - if (strlen(port->ex_vm_id)) - meta_data_add_string(meta, "vm-uuid", port->ex_vm_id); - if (strlen(port->ex_iface_id)) - meta_data_add_string(meta, "iface-id", port->ex_iface_id); - } - snprintf(devname, sizeof(devname), "%s.%s", bridge->name, port->name); - ovs_stats_submit_one(devname, "if_collisions", NULL, - port->stats[collisions], meta); - ovs_stats_submit_two(devname, "if_dropped", NULL, - port->stats[rx_dropped], port->stats[tx_dropped], - meta); - ovs_stats_submit_two(devname, "if_errors", NULL, - port->stats[rx_errors], port->stats[tx_errors], - meta); - ovs_stats_submit_two(devname, "if_packets", NULL, - port->stats[rx_packets], port->stats[tx_packets], - meta); - ovs_stats_submit_one(devname, "if_rx_errors", "crc", - port->stats[rx_crc_err], meta); - ovs_stats_submit_one(devname, "if_rx_errors", "frame", - port->stats[rx_frame_err], meta); - ovs_stats_submit_one(devname, "if_rx_errors", "over", - port->stats[rx_over_err], meta); - ovs_stats_submit_one(devname, "if_rx_octets", NULL, - port->stats[rx_bytes], meta); - ovs_stats_submit_one(devname, "if_tx_octets", NULL, - port->stats[tx_bytes], meta); - ovs_stats_submit_two(devname, "if_packets", "1_to_64_packets", - port->stats[rx_1_to_64_packets], - port->stats[tx_1_to_64_packets], meta); - ovs_stats_submit_two(devname, "if_packets", "65_to_127_packets", - port->stats[rx_65_to_127_packets], - port->stats[tx_65_to_127_packets], meta); - ovs_stats_submit_two(devname, "if_packets", "128_to_255_packets", - port->stats[rx_128_to_255_packets], - port->stats[tx_128_to_255_packets], meta); - ovs_stats_submit_two(devname, "if_packets", "256_to_511_packets", - port->stats[rx_256_to_511_packets], - port->stats[tx_256_to_511_packets], meta); - ovs_stats_submit_two(devname, "if_packets", "512_to_1023_packets", - port->stats[rx_512_to_1023_packets], - port->stats[tx_512_to_1023_packets], meta); - ovs_stats_submit_two(devname, "if_packets", "1024_to_1522_packets", - port->stats[rx_1024_to_1522_packets], - port->stats[tx_1024_to_1522_packets], meta); - ovs_stats_submit_two(devname, "if_packets", "1523_to_max_packets", - port->stats[rx_1523_to_max_packets], - port->stats[tx_1523_to_max_packets], meta); - ovs_stats_submit_two(devname, "if_packets", "broadcast_packets", - port->stats[rx_broadcast_packets], - port->stats[tx_broadcast_packets], meta); - ovs_stats_submit_one(devname, "if_multicast", "tx_multicast_packets", - port->stats[tx_multicast_packets], meta); - ovs_stats_submit_one(devname, "if_rx_errors", "rx_undersized_errors", - port->stats[rx_undersized_errors], meta); - ovs_stats_submit_one(devname, "if_rx_errors", "rx_oversize_errors", - port->stats[rx_oversize_errors], meta); - ovs_stats_submit_one(devname, "if_rx_errors", "rx_fragmented_errors", - port->stats[rx_fragmented_errors], meta); - ovs_stats_submit_one(devname, "if_rx_errors", "rx_jabber_errors", - port->stats[rx_jabber_errors], meta); - - meta_data_destroy(meta); - } - } else + for (port_list_t *port = g_port_list_head; port != NULL; port = port->next) { + if (strlen(port->name) == 0) + /* Skip port w/o name. This is possible when read callback + * is called after Interface Table update callback but before + * Port table Update callback. Will add this port on next read */ continue; + + /* Skip port if it has no bridge */ + if (!port->br) + continue; + + ovs_stats_submit_port(port); + + if (interface_stats) + ovs_stats_submit_interfaces(port); } pthread_mutex_unlock(&g_stats_lock); return 0; diff --git a/src/pcie_errors.c b/src/pcie_errors.c index b239a8c5..b7282eac 100644 --- a/src/pcie_errors.c +++ b/src/pcie_errors.c @@ -27,7 +27,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "utils_llist.h" #include @@ -492,7 +492,7 @@ static void pcie_dispatch_uncorrectable_errors(pcie_device_t *dev, /* Find offset of PCI Express Capability Structure * in PCI configuration space. * Returns offset, -1 if not found. -**/ + **/ static int pcie_find_cap_exp(pcie_device_t *dev) { int pos = pcie_read8(dev, PCI_CAPABILITY_LIST) & ~3; @@ -515,7 +515,7 @@ static int pcie_find_cap_exp(pcie_device_t *dev) { /* Find offset of Advanced Error Reporting Capability. * Returns AER offset, -1 if not found. -**/ + **/ static int pcie_find_ecap_aer(pcie_device_t *dev) { int pos = PCIE_ECAP_OFFSET; uint32_t header = pcie_read32(dev, pos); diff --git a/src/perl.c b/src/perl.c index fffbc21d..0a4ae71d 100644 --- a/src/perl.c +++ b/src/perl.c @@ -47,8 +47,8 @@ #endif /* DEBUG */ /* ... while we want the definition found in plugin.h. */ -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "filter_chain.h" @@ -863,9 +863,9 @@ static int oconfig_item2hv(pTHX_ oconfig_item_t *ci, HV *hash) { static char *get_module_name(char *buf, size_t buf_len, const char *module) { int status = 0; if (base_name[0] == '\0') - status = snprintf(buf, buf_len, "%s", module); + status = ssnprintf(buf, buf_len, "%s", module); else - status = snprintf(buf, buf_len, "%s::%s", base_name, module); + status = ssnprintf(buf, buf_len, "%s::%s", base_name, module); if ((status < 0) || ((unsigned int)status >= buf_len)) return NULL; return buf; diff --git a/src/pf.c b/src/pf.c index 88a4c2d0..9681d366 100644 --- a/src/pf.c +++ b/src/pf.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_SYS_IOCTL_H #include diff --git a/src/pinba.c b/src/pinba.c index 9f571d04..61d226c4 100644 --- a/src/pinba.c +++ b/src/pinba.c @@ -25,8 +25,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include diff --git a/src/ping.c b/src/ping.c index ffb16910..203d2230 100644 --- a/src/ping.c +++ b/src/ping.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_complain.h" #include diff --git a/src/postgresql.c b/src/postgresql.c index ae887a17..4e608be1 100644 --- a/src/postgresql.c +++ b/src/postgresql.c @@ -32,13 +32,13 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "plugin.h" +#include "utils/db_query/db_query.h" #include "utils_cache.h" #include "utils_complain.h" -#include "utils_db_query.h" #include #include @@ -58,7 +58,7 @@ * is ignored. */ #define C_PSQL_PAR_APPEND(buf, buf_len, parameter, value) \ if ((0 < (buf_len)) && (NULL != (value)) && ('\0' != *(value))) { \ - int s = snprintf(buf, buf_len, " %s = '%s'", parameter, value); \ + int s = ssnprintf(buf, buf_len, " %s = '%s'", parameter, value); \ if (0 < s) { \ buf += s; \ buf_len -= s; \ @@ -323,7 +323,7 @@ static int c_psql_connect(c_psql_database_t *db) { if ((!db) || (!db->database)) return -1; - status = snprintf(buf, buf_len, "dbname = '%s'", db->database); + status = ssnprintf(buf, buf_len, "dbname = '%s'", db->database); if (0 < status) { buf += status; buf_len -= status; @@ -426,8 +426,8 @@ static PGresult *c_psql_exec_query_params(c_psql_database_t *db, udb_query_t *q, params[i] = db->user; break; case C_PSQL_PARAM_INTERVAL: - snprintf(interval, sizeof(interval), "%.3f", - CDTIME_T_TO_DOUBLE(plugin_get_interval())); + ssnprintf(interval, sizeof(interval), "%.3f", + CDTIME_T_TO_DOUBLE(plugin_get_interval())); params[i] = interval; break; case C_PSQL_PARAM_INSTANCE: @@ -511,14 +511,14 @@ static int c_psql_exec_query(c_psql_database_t *db, udb_query_t *q, } column_num = PQnfields(res); - column_names = (char **)calloc(column_num, sizeof(char *)); - if (NULL == column_names) { + column_names = calloc(column_num, sizeof(*column_names)); + if (column_names == NULL) { log_err("calloc failed."); BAIL_OUT(-1); } - column_values = (char **)calloc(column_num, sizeof(char *)); - if (NULL == column_values) { + column_values = calloc(column_num, sizeof(*column_values)); + if (column_values == NULL) { log_err("calloc failed."); BAIL_OUT(-1); } @@ -632,7 +632,7 @@ static char *values_name_to_sqlarray(const data_set_t *ds, char *string, str_len = string_len; for (size_t i = 0; i < ds->ds_num; ++i) { - int status = snprintf(str_ptr, str_len, ",'%s'", ds->ds[i].name); + int status = ssnprintf(str_ptr, str_len, ",'%s'", ds->ds[i].name); if (status < 1) return NULL; @@ -670,10 +670,10 @@ static char *values_type_to_sqlarray(const data_set_t *ds, char *string, int status; if (store_rates) - status = snprintf(str_ptr, str_len, ",'gauge'"); + status = ssnprintf(str_ptr, str_len, ",'gauge'"); else - status = snprintf(str_ptr, str_len, ",'%s'", - DS_TYPE_TO_STRING(ds->ds[i].type)); + status = ssnprintf(str_ptr, str_len, ",'%s'", + DS_TYPE_TO_STRING(ds->ds[i].type)); if (status < 1) { str_len = 0; @@ -725,7 +725,7 @@ static char *values_to_sqlarray(const data_set_t *ds, const value_list_t *vl, if (ds->ds[i].type == DS_TYPE_GAUGE) status = - snprintf(str_ptr, str_len, "," GAUGE_FORMAT, vl->values[i].gauge); + ssnprintf(str_ptr, str_len, "," GAUGE_FORMAT, vl->values[i].gauge); else if (store_rates) { if (rates == NULL) rates = uc_get_rate(ds, vl); @@ -735,14 +735,14 @@ static char *values_to_sqlarray(const data_set_t *ds, const value_list_t *vl, return NULL; } - status = snprintf(str_ptr, str_len, ",%lf", rates[i]); + status = ssnprintf(str_ptr, str_len, ",%lf", rates[i]); } else if (ds->ds[i].type == DS_TYPE_COUNTER) - status = snprintf(str_ptr, str_len, ",%" PRIu64, - (uint64_t)vl->values[i].counter); + status = ssnprintf(str_ptr, str_len, ",%" PRIu64, + (uint64_t)vl->values[i].counter); else if (ds->ds[i].type == DS_TYPE_DERIVE) - status = snprintf(str_ptr, str_len, ",%" PRIi64, vl->values[i].derive); + status = ssnprintf(str_ptr, str_len, ",%" PRIi64, vl->values[i].derive); else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) - status = snprintf(str_ptr, str_len, ",%" PRIu64, vl->values[i].absolute); + status = ssnprintf(str_ptr, str_len, ",%" PRIu64, vl->values[i].absolute); if (status < 1) { str_len = 0; @@ -940,7 +940,7 @@ static int c_psql_shutdown(void) { if (db->writers_num > 0) { char cb_name[DATA_MAX_NAME_LEN]; - snprintf(cb_name, sizeof(cb_name), "postgresql-%s", db->database); + ssnprintf(cb_name, sizeof(cb_name), "postgresql-%s", db->database); if (!had_flush) { plugin_unregister_flush("postgresql"); @@ -976,9 +976,9 @@ static int config_query_param_add(udb_query_t *q, oconfig_item_t *ci) { c_psql_param_t *tmp; data = udb_query_get_user_data(q); - if (NULL == data) { + if (data == NULL) { data = calloc(1, sizeof(*data)); - if (NULL == data) { + if (data == NULL) { log_err("Out of memory."); return -1; } @@ -989,7 +989,7 @@ static int config_query_param_add(udb_query_t *q, oconfig_item_t *ci) { } tmp = realloc(data->params, (data->params_num + 1) * sizeof(*data->params)); - if (NULL == tmp) { + if (tmp == NULL) { log_err("Out of memory."); return -1; } @@ -1176,9 +1176,7 @@ static int c_psql_config_database(oconfig_item_t *ci) { } if (db->queries_num > 0) { - db->q_prep_areas = (udb_query_preparation_area_t **)calloc( - db->queries_num, sizeof(*db->q_prep_areas)); - + db->q_prep_areas = calloc(db->queries_num, sizeof(*db->q_prep_areas)); if (db->q_prep_areas == NULL) { log_err("Out of memory."); c_psql_database_delete(db); @@ -1201,7 +1199,7 @@ static int c_psql_config_database(oconfig_item_t *ci) { } } - snprintf(cb_name, sizeof(cb_name), "postgresql-%s", db->instance); + ssnprintf(cb_name, sizeof(cb_name), "postgresql-%s", db->instance); user_data_t ud = {.data = db, .free_func = c_psql_database_delete}; diff --git a/src/powerdns.c b/src/powerdns.c index 644d0ae7..9079719c 100644 --- a/src/powerdns.c +++ b/src/powerdns.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_llist.h" #include @@ -89,48 +89,48 @@ all-outqueries counts the number of outgoing UDP queries since starting answers-slow counts the number of queries answered after 1 second answers0-1 counts the number of queries answered within 1 millisecond answers1-10 counts the number of queries answered within 10 -milliseconds + milliseconds answers10-100 counts the number of queries answered within 100 -milliseconds + milliseconds answers100-1000 counts the number of queries answered within 1 second cache-bytes size of the cache in bytes (since 3.3.1) cache-entries shows the number of entries in the cache cache-hits counts the number of cache hits since starting, this does -not include hits that got answered from the packet-cache + not include hits that got answered from the packet-cache cache-misses counts the number of cache misses since starting case-mismatches counts the number of mismatches in character case since -starting + starting chain-resends number of queries chained to existing outstanding query client-parse-errors counts number of client packets that could not be parsed concurrent-queries shows the number of MThreads currently running dlg-only-drops number of records dropped because of delegation only -setting + setting dont-outqueries number of outgoing queries dropped because of 'dont-query' -setting (since 3.3) + setting (since 3.3) edns-ping-matches number of servers that sent a valid EDNS PING respons edns-ping-mismatches number of servers that sent an invalid EDNS PING response failed-host-entries number of servers that failed to resolve ipv6-outqueries number of outgoing queries over IPv6 ipv6-questions counts all End-user initiated queries with the RD bit set, -received over IPv6 UDP + received over IPv6 UDP malloc-bytes returns the number of bytes allocated by the process -(broken, always returns 0) + (broken, always returns 0) max-mthread-stack maximum amount of thread stack ever used negcache-entries shows the number of entries in the Negative answer cache no-packet-error number of errorneous received packets noedns-outqueries number of queries sent out without EDNS noerror-answers counts the number of times it answered NOERROR since -starting + starting noping-outqueries number of queries sent out without ENDS PING nsset-invalidations number of times an nsset was dropped because it no longer -worked + worked nsspeeds-entries shows the number of entries in the NS speeds map nxdomain-answers counts the number of times it answered NXDOMAIN since -starting + starting outgoing-timeouts counts the number of timeouts on outgoing UDP queries -since starting + since starting over-capacity-drops questions dropped because over maximum concurrent query -limit (since 3.2) + limit (since 3.2) packetcache-bytes size of the packet cache in bytes (since 3.3.1) packetcache-entries size of packet cache (since 3.2) packetcache-hits packet cache hits (since 3.2) @@ -139,32 +139,32 @@ policy-drops packets dropped because of (Lua) policy decision qa-latency shows the current latency average questions counts all end-user initiated queries with the RD bit set resource-limits counts number of queries that could not be performed -because of resource limits + because of resource limits security-status security status based on security polling server-parse-errors counts number of server replied packets that could not be -parsed + parsed servfail-answers counts the number of times it answered SERVFAIL since -starting + starting spoof-prevents number of times PowerDNS considered itself spoofed, and -dropped the data + dropped the data sys-msec number of CPU milliseconds spent in 'system' mode tcp-client-overflow number of times an IP address was denied TCP access -because it already had too many connections + because it already had too many connections tcp-clients counts the number of currently active TCP/IP clients tcp-outqueries counts the number of outgoing TCP queries since starting tcp-questions counts all incoming TCP queries (since starting) throttle-entries shows the number of entries in the throttle map throttled-out counts the number of throttled outgoing UDP queries since -starting + starting throttled-outqueries idem to throttled-out unauthorized-tcp number of TCP questions denied because of allow-from -restrictions + restrictions unauthorized-udp number of UDP questions denied because of allow-from -restrictions + restrictions unexpected-packets number of answers from remote servers that were unexpected -(might point to spoofing) + (might point to spoofing) unreachables number of times nameservers were unreachable since -starting + starting uptime number of seconds process has been running (since 3.1.5) user-msec number of CPU milliseconds spent in 'user' mode }}} */ @@ -301,6 +301,7 @@ static statname_lookup_t lookup_table[] = /* {{{ */ {"unauthorized-tcp", "counter", "denied-unauthorized_tcp"}, {"unauthorized-udp", "counter", "denied-unauthorized_udp"}, {"unexpected-packets", "dns_answer", "unexpected"}, + {"unreachables", "counter", "unreachables"}, {"uptime", "uptime", NULL}}; /* }}} */ static int lookup_table_length = STATIC_ARRAY_SIZE(lookup_table); @@ -469,7 +470,7 @@ static int powerdns_get_data_dgram(list_item_t *item, char **ret_buffer) { } memcpy(buffer, temp, buffer_size - 1); - buffer[buffer_size - 1] = 0; + buffer[buffer_size - 1] = '\0'; *ret_buffer = buffer; return 0; diff --git a/src/processes.c b/src/processes.c index aa7cfa39..f83913af 100644 --- a/src/processes.c +++ b/src/processes.c @@ -38,12 +38,12 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_LIBTASKSTATS +#include "utils/taskstats/taskstats.h" #include "utils_complain.h" -#include "utils_taskstats.h" #endif /* Include header files for the mach system, if they exist.. */ @@ -216,7 +216,7 @@ typedef struct process_entry_s { typedef struct procstat_entry_s { unsigned long id; - unsigned long age; + unsigned char age; derive_t vmem_minflt_counter; derive_t vmem_majflt_counter; @@ -587,7 +587,8 @@ static void ps_list_add(const char *name, const char *cmdline, entry->cpu_system_counter); #if HAVE_LIBTASKSTATS - ps_update_delay(ps, pse, entry); + if (entry->has_delay) + ps_update_delay(ps, pse, entry); #endif } } @@ -616,7 +617,7 @@ static void ps_list_reset(void) { pse_prev = NULL; pse = ps->instances; while (pse != NULL) { - if (pse->age > 10) { + if (pse->age > 0) { DEBUG("Removing this procstat entry cause it's too old: " "id = %lu; name = %s;", pse->id, ps->name); @@ -631,7 +632,7 @@ static void ps_list_reset(void) { pse = pse_prev->next; } } else { - pse->age++; + pse->age = 1; pse_prev = pse; pse = pse->next; } @@ -756,7 +757,7 @@ static int ps_init(void) { pset_list_len = 0; return -1; } -/* #endif HAVE_THREAD_INFO */ + /* #endif HAVE_THREAD_INFO */ #elif KERNEL_LINUX pagesize_g = sysconf(_SC_PAGESIZE); @@ -770,13 +771,13 @@ static int ps_init(void) { } } #endif -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_LIBKVM_GETPROCS && \ (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD) pagesize = getpagesize(); -/* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD || - * HAVE_STRUCT_KINFO_PROC_OPENBSD) */ + /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD || + * HAVE_STRUCT_KINFO_PROC_OPENBSD) */ #elif HAVE_PROCINFO_H pagesize = getpagesize(); @@ -1577,7 +1578,6 @@ static char *ps_get_cmdline(long pid, return NULL; } - info.pr_psargs[sizeof(info.pr_psargs) - 1] = 0; sstrncpy(buffer, info.pr_psargs, buffer_size); return buffer; @@ -1681,7 +1681,7 @@ static int ps_read_process(long pid, process_entry_t *ps, char *state) { /* * TODO: context switch counters for Solaris -*/ + */ ps->cswitch_vol = -1; ps->cswitch_invol = -1; @@ -2013,7 +2013,7 @@ static int ps_read(void) { for (ps = list_head_g; ps != NULL; ps = ps->next) ps_submit_proc_list(ps); -/* #endif HAVE_THREAD_INFO */ + /* #endif HAVE_THREAD_INFO */ #elif KERNEL_LINUX int running = 0; @@ -2095,7 +2095,7 @@ static int ps_read(void) { ps_submit_proc_list(ps_ptr); read_fork_rate(); -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD int running = 0; @@ -2250,7 +2250,7 @@ static int ps_read(void) { for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next) ps_submit_proc_list(ps_ptr); -/* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */ + /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */ #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD int running = 0; @@ -2394,7 +2394,7 @@ static int ps_read(void) { for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next) ps_submit_proc_list(ps_ptr); -/* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD */ + /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD */ #elif HAVE_PROCINFO_H /* AIX */ @@ -2533,7 +2533,7 @@ static int ps_read(void) { for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) ps_submit_proc_list(ps); -/* #endif HAVE_PROCINFO_H */ + /* #endif HAVE_PROCINFO_H */ #elif KERNEL_SOLARIS /* diff --git a/src/procevent.c b/src/procevent.c new file mode 100644 index 00000000..ab000dbd --- /dev/null +++ b/src/procevent.c @@ -0,0 +1,1303 @@ +/** + * collectd - src/procevent.c + * + * 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: + * Red Hat NFVPE + * Andrew Bays + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" +#include "utils_complain.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#if HAVE_YAJL_YAJL_VERSION_H +#include +#endif +#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1) +#define HAVE_YAJL_V2 1 +#endif + +#define PROCEVENT_EXITED 0 +#define PROCEVENT_STARTED 1 +#define PROCEVENT_FIELDS 3 // pid, status, timestamp +#define BUFSIZE 512 +#define PROCDIR "/proc" +#define RBUF_PROC_ID_INDEX 0 +#define RBUF_PROC_STATUS_INDEX 1 +#define RBUF_TIME_INDEX 2 + +#define PROCEVENT_DOMAIN_FIELD "domain" +#define PROCEVENT_DOMAIN_VALUE "fault" +#define PROCEVENT_EVENT_ID_FIELD "eventId" +#define PROCEVENT_EVENT_NAME_FIELD "eventName" +#define PROCEVENT_EVENT_NAME_DOWN_VALUE "down" +#define PROCEVENT_EVENT_NAME_UP_VALUE "up" +#define PROCEVENT_LAST_EPOCH_MICROSEC_FIELD "lastEpochMicrosec" +#define PROCEVENT_PRIORITY_FIELD "priority" +#define PROCEVENT_PRIORITY_VALUE "high" +#define PROCEVENT_REPORTING_ENTITY_NAME_FIELD "reportingEntityName" +#define PROCEVENT_REPORTING_ENTITY_NAME_VALUE "collectd procevent plugin" +#define PROCEVENT_SEQUENCE_FIELD "sequence" +#define PROCEVENT_SEQUENCE_VALUE "0" +#define PROCEVENT_SOURCE_NAME_FIELD "sourceName" +#define PROCEVENT_START_EPOCH_MICROSEC_FIELD "startEpochMicrosec" +#define PROCEVENT_VERSION_FIELD "version" +#define PROCEVENT_VERSION_VALUE "1.0" + +#define PROCEVENT_ALARM_CONDITION_FIELD "alarmCondition" +#define PROCEVENT_ALARM_INTERFACE_A_FIELD "alarmInterfaceA" +#define PROCEVENT_EVENT_SEVERITY_FIELD "eventSeverity" +#define PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE "CRITICAL" +#define PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE "NORMAL" +#define PROCEVENT_EVENT_SOURCE_TYPE_FIELD "eventSourceType" +#define PROCEVENT_EVENT_SOURCE_TYPE_VALUE "process" +#define PROCEVENT_FAULT_FIELDS_FIELD "faultFields" +#define PROCEVENT_FAULT_FIELDS_VERSION_FIELD "faultFieldsVersion" +#define PROCEVENT_FAULT_FIELDS_VERSION_VALUE "1.0" +#define PROCEVENT_SPECIFIC_PROBLEM_FIELD "specificProblem" +#define PROCEVENT_SPECIFIC_PROBLEM_DOWN_VALUE "down" +#define PROCEVENT_SPECIFIC_PROBLEM_UP_VALUE "up" +#define PROCEVENT_VF_STATUS_FIELD "vfStatus" +#define PROCEVENT_VF_STATUS_CRITICAL_VALUE "Ready to terminate" +#define PROCEVENT_VF_STATUS_NORMAL_VALUE "Active" + +/* + * Private data types + */ + +typedef struct { + int head; + int tail; + int maxLen; + cdtime_t **buffer; +} circbuf_t; + +struct processlist_s { + char *process; + + long pid; + int32_t last_status; + + struct processlist_s *next; +}; +typedef struct processlist_s processlist_t; + +/* + * Private variables + */ +static ignorelist_t *ignorelist = NULL; + +static int procevent_netlink_thread_loop = 0; +static int procevent_netlink_thread_error = 0; +static pthread_t procevent_netlink_thread_id; +static int procevent_dequeue_thread_loop = 0; +static pthread_t procevent_dequeue_thread_id; +static pthread_mutex_t procevent_thread_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t procevent_data_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t procevent_cond = PTHREAD_COND_INITIALIZER; +static int nl_sock = -1; +static int buffer_length; +static circbuf_t ring; +static processlist_t *processlist_head = NULL; +static int event_id = 0; + +static const char *config_keys[] = {"BufferLength", "Process", "ProcessRegex"}; +static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); + +/* + * Private functions + */ + +static int gen_message_payload(int state, long pid, char *process, + cdtime_t timestamp, char **buf) { + const unsigned char *buf2; + yajl_gen g; + char json_str[DATA_MAX_NAME_LEN]; + +#if !defined(HAVE_YAJL_V2) + yajl_gen_config conf = {0}; +#endif + +#if HAVE_YAJL_V2 + size_t len; + g = yajl_gen_alloc(NULL); + yajl_gen_config(g, yajl_gen_beautify, 0); +#else + unsigned int len; + g = yajl_gen_alloc(&conf, NULL); +#endif + + yajl_gen_clear(g); + + // *** BEGIN common event header *** + + if (yajl_gen_map_open(g) != yajl_gen_status_ok) + goto err; + + // domain + if (yajl_gen_string(g, (u_char *)PROCEVENT_DOMAIN_FIELD, + strlen(PROCEVENT_DOMAIN_FIELD)) != yajl_gen_status_ok) + goto err; + + if (yajl_gen_string(g, (u_char *)PROCEVENT_DOMAIN_VALUE, + strlen(PROCEVENT_DOMAIN_VALUE)) != yajl_gen_status_ok) + goto err; + + // eventId + if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_ID_FIELD, + strlen(PROCEVENT_EVENT_ID_FIELD)) != yajl_gen_status_ok) + goto err; + + event_id = event_id + 1; + if (snprintf(json_str, sizeof(json_str), "%d", event_id) < 0) { + goto err; + } + + if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) { + goto err; + } + + // eventName + if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_NAME_FIELD, + strlen(PROCEVENT_EVENT_NAME_FIELD)) != yajl_gen_status_ok) + goto err; + + if (snprintf(json_str, sizeof(json_str), "process %s (%ld) %s", process, pid, + (state == 0 ? PROCEVENT_EVENT_NAME_DOWN_VALUE + : PROCEVENT_EVENT_NAME_UP_VALUE)) < 0) { + goto err; + } + + if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) != + yajl_gen_status_ok) { + goto err; + } + + // lastEpochMicrosec + if (yajl_gen_string(g, (u_char *)PROCEVENT_LAST_EPOCH_MICROSEC_FIELD, + strlen(PROCEVENT_LAST_EPOCH_MICROSEC_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (snprintf(json_str, sizeof(json_str), "%" PRIu64, + CDTIME_T_TO_US(cdtime())) < 0) { + goto err; + } + + if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) { + goto err; + } + + // priority + if (yajl_gen_string(g, (u_char *)PROCEVENT_PRIORITY_FIELD, + strlen(PROCEVENT_PRIORITY_FIELD)) != yajl_gen_status_ok) + goto err; + + if (yajl_gen_string(g, (u_char *)PROCEVENT_PRIORITY_VALUE, + strlen(PROCEVENT_PRIORITY_VALUE)) != yajl_gen_status_ok) + goto err; + + // reportingEntityName + if (yajl_gen_string(g, (u_char *)PROCEVENT_REPORTING_ENTITY_NAME_FIELD, + strlen(PROCEVENT_REPORTING_ENTITY_NAME_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (yajl_gen_string(g, (u_char *)PROCEVENT_REPORTING_ENTITY_NAME_VALUE, + strlen(PROCEVENT_REPORTING_ENTITY_NAME_VALUE)) != + yajl_gen_status_ok) + goto err; + + // sequence + if (yajl_gen_string(g, (u_char *)PROCEVENT_SEQUENCE_FIELD, + strlen(PROCEVENT_SEQUENCE_FIELD)) != yajl_gen_status_ok) + goto err; + + if (yajl_gen_number(g, PROCEVENT_SEQUENCE_VALUE, + strlen(PROCEVENT_SEQUENCE_VALUE)) != yajl_gen_status_ok) + goto err; + + // sourceName + if (yajl_gen_string(g, (u_char *)PROCEVENT_SOURCE_NAME_FIELD, + strlen(PROCEVENT_SOURCE_NAME_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (yajl_gen_string(g, (u_char *)process, strlen(process)) != + yajl_gen_status_ok) + goto err; + + // startEpochMicrosec + if (yajl_gen_string(g, (u_char *)PROCEVENT_START_EPOCH_MICROSEC_FIELD, + strlen(PROCEVENT_START_EPOCH_MICROSEC_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (snprintf(json_str, sizeof(json_str), "%" PRIu64, + CDTIME_T_TO_US(timestamp)) < 0) { + goto err; + } + + if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) { + goto err; + } + + // version + if (yajl_gen_string(g, (u_char *)PROCEVENT_VERSION_FIELD, + strlen(PROCEVENT_VERSION_FIELD)) != yajl_gen_status_ok) + goto err; + + if (yajl_gen_number(g, PROCEVENT_VERSION_VALUE, + strlen(PROCEVENT_VERSION_VALUE)) != yajl_gen_status_ok) + goto err; + + // *** END common event header *** + + // *** BEGIN fault fields *** + + if (yajl_gen_string(g, (u_char *)PROCEVENT_FAULT_FIELDS_FIELD, + strlen(PROCEVENT_FAULT_FIELDS_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (yajl_gen_map_open(g) != yajl_gen_status_ok) + goto err; + + // alarmCondition + if (yajl_gen_string(g, (u_char *)PROCEVENT_ALARM_CONDITION_FIELD, + strlen(PROCEVENT_ALARM_CONDITION_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (snprintf(json_str, sizeof(json_str), "process %s (%ld) state change", + process, pid) < 0) { + goto err; + } + + if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) != + yajl_gen_status_ok) { + goto err; + } + + // alarmInterfaceA + if (yajl_gen_string(g, (u_char *)PROCEVENT_ALARM_INTERFACE_A_FIELD, + strlen(PROCEVENT_ALARM_INTERFACE_A_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (yajl_gen_string(g, (u_char *)process, strlen(process)) != + yajl_gen_status_ok) + goto err; + + // eventSeverity + if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SEVERITY_FIELD, + strlen(PROCEVENT_EVENT_SEVERITY_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (yajl_gen_string( + g, + (u_char *)(state == 0 ? PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE + : PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE), + strlen((state == 0 ? PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE + : PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE))) != + yajl_gen_status_ok) + goto err; + + // eventSourceType + if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SOURCE_TYPE_FIELD, + strlen(PROCEVENT_EVENT_SOURCE_TYPE_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SOURCE_TYPE_VALUE, + strlen(PROCEVENT_EVENT_SOURCE_TYPE_VALUE)) != + yajl_gen_status_ok) + goto err; + + // faultFieldsVersion + if (yajl_gen_string(g, (u_char *)PROCEVENT_FAULT_FIELDS_VERSION_FIELD, + strlen(PROCEVENT_FAULT_FIELDS_VERSION_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (yajl_gen_number(g, PROCEVENT_FAULT_FIELDS_VERSION_VALUE, + strlen(PROCEVENT_FAULT_FIELDS_VERSION_VALUE)) != + yajl_gen_status_ok) + goto err; + + // specificProblem + if (yajl_gen_string(g, (u_char *)PROCEVENT_SPECIFIC_PROBLEM_FIELD, + strlen(PROCEVENT_SPECIFIC_PROBLEM_FIELD)) != + yajl_gen_status_ok) + goto err; + + if (snprintf(json_str, sizeof(json_str), "process %s (%ld) %s", process, pid, + (state == 0 ? PROCEVENT_SPECIFIC_PROBLEM_DOWN_VALUE + : PROCEVENT_SPECIFIC_PROBLEM_UP_VALUE)) < 0) { + goto err; + } + + if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) != + yajl_gen_status_ok) { + goto err; + } + + // vfStatus + if (yajl_gen_string(g, (u_char *)PROCEVENT_VF_STATUS_FIELD, + strlen(PROCEVENT_VF_STATUS_FIELD)) != yajl_gen_status_ok) + goto err; + + if (yajl_gen_string( + g, + (u_char *)(state == 0 ? PROCEVENT_VF_STATUS_CRITICAL_VALUE + : PROCEVENT_VF_STATUS_NORMAL_VALUE), + strlen((state == 0 ? PROCEVENT_VF_STATUS_CRITICAL_VALUE + : PROCEVENT_VF_STATUS_NORMAL_VALUE))) != + yajl_gen_status_ok) + goto err; + + // *** END fault fields *** + + // close fault and header fields + if (yajl_gen_map_close(g) != yajl_gen_status_ok || + yajl_gen_map_close(g) != yajl_gen_status_ok) + goto err; + + if (yajl_gen_get_buf(g, &buf2, &len) != yajl_gen_status_ok) + goto err; + + *buf = strdup((char *)buf2); + + if (*buf == NULL) { + ERROR("procevent plugin: strdup failed during gen_message_payload: %s", + STRERRNO); + goto err; + } + + yajl_gen_free(g); + + return 0; + +err: + yajl_gen_free(g); + ERROR("procevent plugin: gen_message_payload failed to generate JSON"); + return -1; +} + +// Does /proc//comm contain a process name we are interested in? +// NOTE: Caller MUST hold procevent_data_lock when calling this function +static processlist_t *process_check(long pid) { + char file[BUFSIZE]; + + int len = snprintf(file, sizeof(file), PROCDIR "/%ld/comm", pid); + + if ((len < 0) || (len >= BUFSIZE)) { + WARNING("procevent process_check: process name too large"); + return NULL; + } + + FILE *fh; + + if (NULL == (fh = fopen(file, "r"))) { + // No /proc//comm for this pid, just ignore + DEBUG("procevent plugin: no comm file available for pid %ld", pid); + return NULL; + } + + char buffer[BUFSIZE]; + int retval = fscanf(fh, "%[^\n]", buffer); + + if (retval < 0) { + WARNING("procevent process_check: unable to read comm file for pid %ld", + pid); + fclose(fh); + return NULL; + } + + // Now that we have the process name in the buffer, check if we are + // even interested in it + if (ignorelist_match(ignorelist, buffer) != 0) { + DEBUG("procevent process_check: ignoring process %s (%ld)", buffer, pid); + fclose(fh); + return NULL; + } + + if (fh != NULL) { + fclose(fh); + fh = NULL; + } + + // + // Go through the processlist linked list and look for the process name + // in /proc//comm. If found: + // 1. If pl->pid is -1, then set pl->pid to (and return that object) + // 2. If pl->pid is not -1, then another process was already + // found. If == pl->pid, this is an old match, so do nothing. + // If the is different, however, make a new processlist_t and + // associate with it (with the same process name as the existing). + // + + processlist_t *match = NULL; + + for (processlist_t *pl = processlist_head; pl != NULL; pl = pl->next) { + + int is_match = (strcmp(buffer, pl->process) == 0 ? 1 : 0); + + if (is_match == 1) { + DEBUG("procevent plugin: process %ld name match for %s", pid, buffer); + + if (pl->pid == pid) { + // this is a match, and we've already stored the exact pid/name combo + DEBUG("procevent plugin: found exact match with name %s, PID %ld for " + "incoming PID %ld", + pl->process, pl->pid, pid); + match = pl; + break; + } else if (pl->pid == -1) { + // this is a match, and we've found a candidate processlist_t to store + // this new pid/name combo + DEBUG("procevent plugin: reusing pl object with PID %ld for incoming " + "PID %ld", + pl->pid, pid); + pl->pid = pid; + match = pl; + break; + } else if (pl->pid != -1) { + // this is a match, but another instance of this process has already + // claimed this pid/name combo, + // so keep looking + DEBUG("procevent plugin: found pl object with matching name for " + "incoming PID %ld, but object is in use by PID %ld", + pid, pl->pid); + match = pl; + continue; + } + } + } + + if (match == NULL || + (match != NULL && match->pid != -1 && match->pid != pid)) { + // if there wasn't an existing match, OR + // if there was a match but the associated processlist_t object already + // contained a pid/name combo, + // then make a new one and add it to the linked list + + DEBUG("procevent plugin: allocating new processlist_t object for PID %ld " + "(%s)", + pid, buffer); + + processlist_t *pl2 = calloc(1, sizeof(*pl2)); + if (pl2 == NULL) { + ERROR("procevent plugin: calloc failed during process_check: %s", + STRERRNO); + return NULL; + } + + char *process = strdup(buffer); + if (process == NULL) { + sfree(pl2); + ERROR("procevent plugin: strdup failed during process_check: %s", + STRERRNO); + return NULL; + } + + pl2->process = process; + pl2->pid = pid; + pl2->next = processlist_head; + processlist_head = pl2; + + match = pl2; + } + + return match; +} + +// Does our map have this PID or name? +// NOTE: Caller MUST hold procevent_data_lock when calling this function +static processlist_t *process_map_check(long pid, char *process) { + for (processlist_t *pl = processlist_head; pl != NULL; pl = pl->next) { + int match_pid = 0; + + if (pid > 0) { + if (pl->pid == pid) + match_pid = 1; + } + + int match_process = 0; + + if (process != NULL) { + if (strcmp(pl->process, process) == 0) + match_process = 1; + } + + int match = 0; + + if ((pid > 0 && process == NULL && match_pid == 1) || + (pid < 0 && process != NULL && match_process == 1) || + (pid > 0 && process != NULL && match_pid == 1 && match_process == 1)) { + match = 1; + } + + if (match == 1) { + return pl; + } + } + + return NULL; +} + +static int process_map_refresh(void) { + errno = 0; + DIR *proc = opendir(PROCDIR); + + if (proc == NULL) { + ERROR("procevent plugin: fopen (%s): %s", PROCDIR, STRERRNO); + return -1; + } + + while (42) { + errno = 0; + struct dirent *dent = readdir(proc); + if (dent == NULL) { + if (errno == 0) /* end of directory */ + break; + + ERROR("procevent plugin: failed to read directory %s: %s", PROCDIR, + STRERRNO); + closedir(proc); + return -1; + } + + if (dent->d_name[0] == '.') + continue; + + char file[BUFSIZE]; + + int len = snprintf(file, sizeof(file), PROCDIR "/%s", dent->d_name); + if ((len < 0) || (len >= BUFSIZE)) + continue; + + struct stat statbuf; + + int status = stat(file, &statbuf); + if (status != 0) { + WARNING("procevent plugin: stat (%s) failed: %s", file, STRERRNO); + continue; + } + + if (!S_ISDIR(statbuf.st_mode)) + continue; + + len = snprintf(file, sizeof(file), PROCDIR "/%s/comm", dent->d_name); + if ((len < 0) || (len >= BUFSIZE)) + continue; + + int not_number = 0; + + for (int i = 0; i < strlen(dent->d_name); i++) { + if (!isdigit(dent->d_name[i])) { + not_number = 1; + break; + } + } + + if (not_number != 0) + continue; + + // Check if we need to store this pid/name combo in our processlist_t linked + // list + int this_pid = atoi(dent->d_name); + pthread_mutex_lock(&procevent_data_lock); + processlist_t *pl = process_check(this_pid); + pthread_mutex_unlock(&procevent_data_lock); + + if (pl != NULL) + DEBUG("procevent plugin: process map refreshed for PID %d and name %s", + this_pid, pl->process); + } + + closedir(proc); + + return 0; +} + +static int nl_connect() { + struct sockaddr_nl sa_nl = { + .nl_family = AF_NETLINK, + .nl_groups = CN_IDX_PROC, + .nl_pid = getpid(), + }; + + nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (nl_sock == -1) { + ERROR("procevent plugin: socket open failed: %d", errno); + return -1; + } + + int rc = bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl)); + if (rc == -1) { + ERROR("procevent plugin: socket bind failed: %d", errno); + close(nl_sock); + nl_sock = -1; + return -1; + } + + return 0; +} + +static int set_proc_ev_listen(bool enable) { + struct __attribute__((aligned(NLMSG_ALIGNTO))) { + struct nlmsghdr nl_hdr; + struct __attribute__((__packed__)) { + struct cn_msg cn_msg; + enum proc_cn_mcast_op cn_mcast; + }; + } nlcn_msg; + + memset(&nlcn_msg, 0, sizeof(nlcn_msg)); + nlcn_msg.nl_hdr.nlmsg_len = sizeof(nlcn_msg); + nlcn_msg.nl_hdr.nlmsg_pid = getpid(); + nlcn_msg.nl_hdr.nlmsg_type = NLMSG_DONE; + + nlcn_msg.cn_msg.id.idx = CN_IDX_PROC; + nlcn_msg.cn_msg.id.val = CN_VAL_PROC; + nlcn_msg.cn_msg.len = sizeof(enum proc_cn_mcast_op); + + nlcn_msg.cn_mcast = enable ? PROC_CN_MCAST_LISTEN : PROC_CN_MCAST_IGNORE; + + int rc = send(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0); + if (rc == -1) { + ERROR("procevent plugin: subscribing to netlink process events failed: %d", + errno); + return -1; + } + + return 0; +} + +// Read from netlink socket and write to ring buffer +static int read_event() { + int recv_flags = MSG_DONTWAIT; + struct __attribute__((aligned(NLMSG_ALIGNTO))) { + struct nlmsghdr nl_hdr; + struct __attribute__((__packed__)) { + struct cn_msg cn_msg; + struct proc_event proc_ev; + }; + } nlcn_msg; + + if (nl_sock == -1) + return 0; + + while (42) { + pthread_mutex_lock(&procevent_thread_lock); + + if (procevent_netlink_thread_loop <= 0) { + pthread_mutex_unlock(&procevent_thread_lock); + return 0; + } + + pthread_mutex_unlock(&procevent_thread_lock); + + int status = recv(nl_sock, &nlcn_msg, sizeof(nlcn_msg), recv_flags); + + if (status == 0) { + return 0; + } else if (status < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + pthread_mutex_lock(&procevent_data_lock); + + // There was nothing more to receive for now, so... + // If ring head does not equal ring tail, then there is data + // in the ring buffer for the dequeue thread to read, so + // signal it + if (ring.head != ring.tail) + pthread_cond_signal(&procevent_cond); + + pthread_mutex_unlock(&procevent_data_lock); + + // Since there was nothing to receive, set recv to block and + // try again + recv_flags = 0; + continue; + } else if (errno != EINTR) { + ERROR("procevent plugin: socket receive error: %d", errno); + return -1; + } else { + // Interrupt, so just continue and try again + continue; + } + } + + // We successfully received a message, so don't block on the next + // read in case there are more (and if there aren't, it will be + // handled above in the EWOULDBLOCK error-checking) + recv_flags = MSG_DONTWAIT; + + int proc_id = -1; + int proc_status = -1; + + switch (nlcn_msg.proc_ev.what) { + case PROC_EVENT_EXEC: + proc_status = PROCEVENT_STARTED; + proc_id = nlcn_msg.proc_ev.event_data.exec.process_pid; + break; + case PROC_EVENT_EXIT: + proc_id = nlcn_msg.proc_ev.event_data.exit.process_pid; + proc_status = PROCEVENT_EXITED; + break; + default: + // Otherwise not of interest + break; + } + + // If we're interested in this process status event, place the event + // in the ring buffer for consumption by the dequeue (dispatch) thread. + + if (proc_status != -1) { + pthread_mutex_lock(&procevent_data_lock); + + int next = ring.head + 1; + if (next >= ring.maxLen) + next = 0; + + if (next == ring.tail) { + // Buffer is full, signal the dequeue thread to process the buffer + // and clean it out, and then sleep + WARNING("procevent plugin: ring buffer full"); + + pthread_cond_signal(&procevent_cond); + pthread_mutex_unlock(&procevent_data_lock); + + usleep(1000); + continue; + } else { + DEBUG("procevent plugin: Process %d status is now %s at %llu", proc_id, + (proc_status == PROCEVENT_EXITED ? "EXITED" : "STARTED"), + (unsigned long long)cdtime()); + + ring.buffer[ring.head][RBUF_PROC_ID_INDEX] = proc_id; + ring.buffer[ring.head][RBUF_PROC_STATUS_INDEX] = proc_status; + ring.buffer[ring.head][RBUF_TIME_INDEX] = cdtime(); + + ring.head = next; + } + + pthread_mutex_unlock(&procevent_data_lock); + } + } + + return 0; +} + +static void procevent_dispatch_notification(long pid, gauge_t value, + char *process, cdtime_t timestamp) { + + notification_t n = { + .severity = (value == 1 ? NOTIF_OKAY : NOTIF_FAILURE), + .time = cdtime(), + .plugin = "procevent", + .type = "gauge", + .type_instance = "process_status", + }; + + sstrncpy(n.host, hostname_g, sizeof(n.host)); + sstrncpy(n.plugin_instance, process, sizeof(n.plugin_instance)); + + char *buf = NULL; + gen_message_payload(value, pid, process, timestamp, &buf); + + int status = plugin_notification_meta_add_string(&n, "ves", buf); + + if (status < 0) { + sfree(buf); + ERROR("procevent plugin: unable to set notification VES metadata: %s", + STRERRNO); + return; + } + + DEBUG("procevent plugin: notification VES metadata: %s", + n.meta->nm_value.nm_string); + + DEBUG("procevent plugin: dispatching state %d for PID %ld (%s)", (int)value, + pid, process); + + plugin_dispatch_notification(&n); + plugin_notification_meta_free(n.meta); + + // strdup'd in gen_message_payload + if (buf != NULL) + sfree(buf); +} + +// Read from ring buffer and dispatch to write plugins +static void read_ring_buffer() { + pthread_mutex_lock(&procevent_data_lock); + + // If there's currently nothing to read from the buffer, + // then wait + if (ring.head == ring.tail) + pthread_cond_wait(&procevent_cond, &procevent_data_lock); + + while (ring.head != ring.tail) { + int next = ring.tail + 1; + + if (next >= ring.maxLen) + next = 0; + + if (ring.buffer[ring.tail][RBUF_PROC_STATUS_INDEX] == PROCEVENT_EXITED) { + processlist_t *pl = process_map_check(ring.buffer[ring.tail][0], NULL); + + if (pl != NULL) { + // This process is of interest to us, so publish its EXITED status + procevent_dispatch_notification( + ring.buffer[ring.tail][RBUF_PROC_ID_INDEX], + ring.buffer[ring.tail][RBUF_PROC_STATUS_INDEX], pl->process, + ring.buffer[ring.tail][RBUF_TIME_INDEX]); + DEBUG( + "procevent plugin: PID %ld (%s) EXITED, removing PID from process " + "list", + pl->pid, pl->process); + pl->pid = -1; + pl->last_status = -1; + } + } else if (ring.buffer[ring.tail][RBUF_PROC_STATUS_INDEX] == + PROCEVENT_STARTED) { + // a new process has started, so check if we should monitor it + processlist_t *pl = process_check(ring.buffer[ring.tail][0]); + + // If we had already seen this process name and pid combo before, + // and the last message was a "process started" message, don't send + // the notfication again + + if (pl != NULL && pl->last_status != PROCEVENT_STARTED) { + // This process is of interest to us, so publish its STARTED status + procevent_dispatch_notification( + ring.buffer[ring.tail][RBUF_PROC_ID_INDEX], + ring.buffer[ring.tail][RBUF_PROC_STATUS_INDEX], pl->process, + ring.buffer[ring.tail][RBUF_TIME_INDEX]); + + pl->last_status = PROCEVENT_STARTED; + + DEBUG("procevent plugin: PID %ld (%s) STARTED, adding PID to process " + "list", + pl->pid, pl->process); + } + } + + ring.tail = next; + } + + pthread_mutex_unlock(&procevent_data_lock); +} + +// Entry point for thread responsible for listening +// to netlink socket and writing data to ring buffer +static void *procevent_netlink_thread(void *arg) /* {{{ */ +{ + pthread_mutex_lock(&procevent_thread_lock); + + while (procevent_netlink_thread_loop > 0) { + pthread_mutex_unlock(&procevent_thread_lock); + + int status = read_event(); + + pthread_mutex_lock(&procevent_thread_lock); + + if (status < 0) { + procevent_netlink_thread_error = 1; + break; + } + } /* while (procevent_netlink_thread_loop > 0) */ + + pthread_mutex_unlock(&procevent_thread_lock); + + return (void *)0; +} /* }}} void *procevent_netlink_thread */ + +// Entry point for thread responsible for reading from +// ring buffer and dispatching notifications +static void *procevent_dequeue_thread(void *arg) /* {{{ */ +{ + pthread_mutex_lock(&procevent_thread_lock); + + while (procevent_dequeue_thread_loop > 0) { + pthread_mutex_unlock(&procevent_thread_lock); + + read_ring_buffer(); + + pthread_mutex_lock(&procevent_thread_lock); + } /* while (procevent_dequeue_thread_loop > 0) */ + + pthread_mutex_unlock(&procevent_thread_lock); + + return (void *)0; +} /* }}} void *procevent_dequeue_thread */ + +static int start_netlink_thread(void) /* {{{ */ +{ + pthread_mutex_lock(&procevent_thread_lock); + + if (procevent_netlink_thread_loop != 0) { + pthread_mutex_unlock(&procevent_thread_lock); + return 0; + } + + int status; + + if (nl_sock == -1) { + status = nl_connect(); + + if (status != 0) { + pthread_mutex_unlock(&procevent_thread_lock); + return status; + } + + status = set_proc_ev_listen(true); + if (status == -1) { + pthread_mutex_unlock(&procevent_thread_lock); + return status; + } + } + + DEBUG("procevent plugin: socket created and bound"); + + procevent_netlink_thread_loop = 1; + procevent_netlink_thread_error = 0; + + status = plugin_thread_create(&procevent_netlink_thread_id, /* attr = */ NULL, + procevent_netlink_thread, + /* arg = */ (void *)0, "procevent"); + if (status != 0) { + procevent_netlink_thread_loop = 0; + ERROR("procevent plugin: Starting netlink thread failed."); + pthread_mutex_unlock(&procevent_thread_lock); + + int status2 = close(nl_sock); + + if (status2 != 0) { + ERROR("procevent plugin: failed to close socket %d: %d (%s)", nl_sock, + status2, STRERRNO); + } + + nl_sock = -1; + + return -1; + } + + pthread_mutex_unlock(&procevent_thread_lock); + + return status; +} /* }}} int start_netlink_thread */ + +static int start_dequeue_thread(void) /* {{{ */ +{ + pthread_mutex_lock(&procevent_thread_lock); + + if (procevent_dequeue_thread_loop != 0) { + pthread_mutex_unlock(&procevent_thread_lock); + return 0; + } + + procevent_dequeue_thread_loop = 1; + + int status = plugin_thread_create(&procevent_dequeue_thread_id, + /* attr = */ NULL, procevent_dequeue_thread, + /* arg = */ (void *)0, "procevent"); + if (status != 0) { + procevent_dequeue_thread_loop = 0; + ERROR("procevent plugin: Starting dequeue thread failed."); + pthread_mutex_unlock(&procevent_thread_lock); + return -1; + } + + pthread_mutex_unlock(&procevent_thread_lock); + + return status; +} /* }}} int start_dequeue_thread */ + +static int start_threads(void) /* {{{ */ +{ + int status = start_netlink_thread(); + int status2 = start_dequeue_thread(); + + if (status != 0) + return status; + else + return status2; +} /* }}} int start_threads */ + +static int stop_netlink_thread(int shutdown) /* {{{ */ +{ + int socket_status; + + if (nl_sock != -1) { + socket_status = close(nl_sock); + if (socket_status != 0) { + ERROR("procevent plugin: failed to close socket %d: %d (%s)", nl_sock, + socket_status, strerror(errno)); + } + + nl_sock = -1; + } else + socket_status = 0; + + pthread_mutex_lock(&procevent_thread_lock); + + if (procevent_netlink_thread_loop == 0) { + pthread_mutex_unlock(&procevent_thread_lock); + return -1; + } + + // Set thread termination status + procevent_netlink_thread_loop = 0; + pthread_mutex_unlock(&procevent_thread_lock); + + // Let threads waiting on access to the data know to move + // on such that they'll see the thread's termination status + pthread_cond_broadcast(&procevent_cond); + + int thread_status; + + if (shutdown == 1) { + // Calling pthread_cancel here in + // the case of a shutdown just assures that the thread is + // gone and that the process has been fully terminated. + + DEBUG("procevent plugin: Canceling netlink thread for process shutdown"); + + thread_status = pthread_cancel(procevent_netlink_thread_id); + + if (thread_status != 0 && thread_status != ESRCH) { + ERROR("procevent plugin: Unable to cancel netlink thread: %d", + thread_status); + thread_status = -1; + } else + thread_status = 0; + } else { + thread_status = + pthread_join(procevent_netlink_thread_id, /* return = */ NULL); + if (thread_status != 0 && thread_status != ESRCH) { + ERROR("procevent plugin: Stopping netlink thread failed."); + thread_status = -1; + } else + thread_status = 0; + } + + pthread_mutex_lock(&procevent_thread_lock); + memset(&procevent_netlink_thread_id, 0, sizeof(procevent_netlink_thread_id)); + procevent_netlink_thread_error = 0; + pthread_mutex_unlock(&procevent_thread_lock); + + DEBUG("procevent plugin: Finished requesting stop of netlink thread"); + + if (socket_status != 0) + return socket_status; + else + return thread_status; +} /* }}} int stop_netlink_thread */ + +static int stop_dequeue_thread() /* {{{ */ +{ + pthread_mutex_lock(&procevent_thread_lock); + + if (procevent_dequeue_thread_loop == 0) { + pthread_mutex_unlock(&procevent_thread_lock); + return -1; + } + + procevent_dequeue_thread_loop = 0; + pthread_mutex_unlock(&procevent_thread_lock); + + pthread_cond_broadcast(&procevent_cond); + + // Calling pthread_cancel here just assures that the thread is + // gone and that the process has been fully terminated. + + DEBUG("procevent plugin: Canceling dequeue thread for process shutdown"); + + int status = pthread_cancel(procevent_dequeue_thread_id); + + if (status != 0 && status != ESRCH) { + ERROR("procevent plugin: Unable to cancel dequeue thread: %d", status); + status = -1; + } else + status = 0; + + pthread_mutex_lock(&procevent_thread_lock); + memset(&procevent_dequeue_thread_id, 0, sizeof(procevent_dequeue_thread_id)); + pthread_mutex_unlock(&procevent_thread_lock); + + DEBUG("procevent plugin: Finished requesting stop of dequeue thread"); + + return status; +} /* }}} int stop_dequeue_thread */ + +static int stop_threads() /* {{{ */ +{ + int status = stop_netlink_thread(1); + int status2 = stop_dequeue_thread(); + + if (status != 0) + return status; + else + return status2; +} /* }}} int stop_threads */ + +static int procevent_init(void) /* {{{ */ +{ + ring.head = 0; + ring.tail = 0; + ring.maxLen = buffer_length; + ring.buffer = (cdtime_t **)calloc(buffer_length, sizeof(cdtime_t *)); + + for (int i = 0; i < buffer_length; i++) { + ring.buffer[i] = (cdtime_t *)calloc(PROCEVENT_FIELDS, sizeof(cdtime_t)); + } + + int status = process_map_refresh(); + + if (status == -1) { + ERROR("procevent plugin: Initial process mapping failed."); + return -1; + } + + if (ignorelist == NULL) { + NOTICE("procevent plugin: No processes have been configured."); + return -1; + } + + return start_threads(); +} /* }}} int procevent_init */ + +static int procevent_config(const char *key, const char *value) /* {{{ */ +{ + if (ignorelist == NULL) + ignorelist = ignorelist_create(/* invert = */ 1); + + if (ignorelist == NULL) { + return -1; + } + + if (strcasecmp(key, "BufferLength") == 0) { + buffer_length = atoi(value); + } else if (strcasecmp(key, "Process") == 0) { + ignorelist_add(ignorelist, value); + } else if (strcasecmp(key, "ProcessRegex") == 0) { +#if HAVE_REGEX_H + int status = ignorelist_add(ignorelist, value); + + if (status != 0) { + ERROR("procevent plugin: invalid regular expression: %s", value); + return 1; + } +#else + WARNING("procevent plugin: The plugin has been compiled without support " + "for the \"ProcessRegex\" option."); +#endif + } else { + return -1; + } + + return 0; +} /* }}} int procevent_config */ + +static int procevent_read(void) /* {{{ */ +{ + pthread_mutex_lock(&procevent_thread_lock); + + if (procevent_netlink_thread_error != 0) { + + pthread_mutex_unlock(&procevent_thread_lock); + + ERROR("procevent plugin: The netlink thread had a problem. Restarting it."); + + stop_netlink_thread(0); + + start_netlink_thread(); + + return -1; + } /* if (procevent_netlink_thread_error != 0) */ + + pthread_mutex_unlock(&procevent_thread_lock); + + return 0; +} /* }}} int procevent_read */ + +static int procevent_shutdown(void) /* {{{ */ +{ + DEBUG("procevent plugin: Shutting down threads."); + + int status = stop_threads(); + + for (int i = 0; i < buffer_length; i++) { + free(ring.buffer[i]); + } + + free(ring.buffer); + + processlist_t *pl = processlist_head; + while (pl != NULL) { + processlist_t *pl_next; + + pl_next = pl->next; + + sfree(pl->process); + sfree(pl); + + pl = pl_next; + } + + ignorelist_free(ignorelist); + + return status; +} /* }}} int procevent_shutdown */ + +void module_register(void) { + plugin_register_config("procevent", procevent_config, config_keys, + config_keys_num); + plugin_register_init("procevent", procevent_init); + plugin_register_read("procevent", procevent_read); + plugin_register_shutdown("procevent", procevent_shutdown); +} /* void module_register */ diff --git a/src/protocols.c b/src/protocols.c index 36b1d83b..7bfa663c 100644 --- a/src/protocols.c +++ b/src/protocols.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #if !KERNEL_LINUX #error "No applicable input method." @@ -41,7 +41,8 @@ * Global variables */ static const char *config_keys[] = { - "Value", "IgnoreSelected", + "Value", + "IgnoreSelected", }; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); @@ -154,8 +155,8 @@ static int read_file(const char *path) { if (values_list != NULL) { char match_name[2 * DATA_MAX_NAME_LEN]; - snprintf(match_name, sizeof(match_name), "%s:%s", key_buffer, - key_fields[i]); + ssnprintf(match_name, sizeof(match_name), "%s:%s", key_buffer, + key_fields[i]); if (ignorelist_match(values_list, match_name)) continue; diff --git a/src/pyconfig.c b/src/pyconfig.c index 4ba7e0d2..5f51b937 100644 --- a/src/pyconfig.c +++ b/src/pyconfig.c @@ -29,7 +29,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "cpython.h" diff --git a/src/python.c b/src/python.c index 64db6985..70db6b6f 100644 --- a/src/python.c +++ b/src/python.c @@ -31,7 +31,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "cpython.h" @@ -285,7 +285,7 @@ static void cpy_build_name(char *buf, size_t size, PyObject *callback, PyObject *mod = NULL; if (name != NULL) { - snprintf(buf, size, "python.%s", name); + ssnprintf(buf, size, "python.%s", name); return; } @@ -294,14 +294,14 @@ static void cpy_build_name(char *buf, size_t size, PyObject *callback, module = cpy_unicode_or_bytes_to_string(&mod); if (module != NULL) { - snprintf(buf, size, "python.%s", module); + ssnprintf(buf, size, "python.%s", module); Py_XDECREF(mod); PyErr_Clear(); return; } Py_XDECREF(mod); - snprintf(buf, size, "python.%p", callback); + ssnprintf(buf, size, "python.%p", callback); PyErr_Clear(); } @@ -367,7 +367,7 @@ void cpy_log_exception(const char *context) { continue; if (cpy[strlen(cpy) - 1] == '\n') - cpy[strlen(cpy) - 1] = 0; + cpy[strlen(cpy) - 1] = '\0'; Py_BEGIN_ALLOW_THREADS; ERROR("%s", cpy); @@ -703,8 +703,9 @@ static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) { for (size_t i = 0; i < ds->ds_num; ++i) { tuple = PyTuple_New(4); PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name)); - PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes( - DS_TYPE_TO_STRING(ds->ds[i].type))); + PyTuple_SET_ITEM( + tuple, 1, + cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type))); PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min)); PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max)); PyList_SET_ITEM(list, i, tuple); @@ -774,7 +775,8 @@ static PyObject *cpy_register_generic_userdata(void *reg, void *handler, register_function(buf, handler, &(user_data_t){ - .data = c, .free_func = cpy_destroy_user_data, + .data = c, + .free_func = cpy_destroy_user_data, }); ++cpy_num_callbacks; @@ -817,7 +819,8 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args, /* group = */ "python", buf, cpy_read_callback, DOUBLE_TO_CDTIME_T(interval), &(user_data_t){ - .data = c, .free_func = cpy_destroy_user_data, + .data = c, + .free_func = cpy_destroy_user_data, }); ++cpy_num_callbacks; return cpy_string_to_unicode_or_bytes(buf); @@ -1133,7 +1136,11 @@ static void *cpy_interactive(void *pipefd) { cpy_log_exception("interactive session init"); } cur_sig = PyOS_setsig(SIGINT, python_sigint_handler); +#if PY_VERSION_HEX < 0x03070000 PyOS_AfterFork(); +#else + PyOS_AfterFork_Child(); +#endif PyEval_InitThreads(); close(*(int *)pipefd); PyRun_InteractiveLoop(stdin, ""); @@ -1197,8 +1204,9 @@ static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) { values = PyTuple_New(ci->values_num); /* New reference. */ for (int i = 0; i < ci->values_num; ++i) { if (ci->values[i].type == OCONFIG_TYPE_STRING) { - PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes( - ci->values[i].value.string)); + PyTuple_SET_ITEM( + values, i, + cpy_string_to_unicode_or_bytes(ci->values[i].value.string)); } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) { PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number)); diff --git a/src/pyvalues.c b/src/pyvalues.c index 301df442..967fecfd 100644 --- a/src/pyvalues.c +++ b/src/pyvalues.c @@ -29,7 +29,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "cpython.h" diff --git a/src/redis.c b/src/redis.c index e24abd54..77ce5fb6 100644 --- a/src/redis.c +++ b/src/redis.c @@ -22,8 +22,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -115,7 +115,7 @@ static int redis_node_add(redis_node_t *rn) /* {{{ */ redis_have_instances = true; char cb_name[sizeof("redis/") + DATA_MAX_NAME_LEN]; - snprintf(cb_name, sizeof(cb_name), "redis/%s", rn->name); + ssnprintf(cb_name, sizeof(cb_name), "redis/%s", rn->name); return plugin_register_complex_read( /* group = */ "redis", @@ -123,7 +123,8 @@ static int redis_node_add(redis_node_t *rn) /* {{{ */ /* callback = */ redis_read, /* interval = */ 0, &(user_data_t){ - .data = rn, .free_func = redis_node_free, + .data = rn, + .free_func = redis_node_free, }); } /* }}} */ @@ -486,7 +487,7 @@ static int redis_db_stats(const char *node, char const *info_line) /* {{{ */ char *str; int i; - snprintf(field_name, sizeof(field_name), "db%d:keys=", db); + ssnprintf(field_name, sizeof(field_name), "db%d:keys=", db); str = strstr(info_line, field_name); if (!str) @@ -502,7 +503,7 @@ static int redis_db_stats(const char *node, char const *info_line) /* {{{ */ return -1; } - snprintf(db_id, sizeof(db_id), "%d", db); + ssnprintf(db_id, sizeof(db_id), "%d", db); redis_submit(node, "records", db_id, val); } return 0; diff --git a/src/routeros.c b/src/routeros.c index 1286805f..ece865b6 100644 --- a/src/routeros.c +++ b/src/routeros.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -53,7 +53,8 @@ static void cr_submit_io(cr_data_t *rd, const char *type, /* {{{ */ const char *type_instance, derive_t rx, derive_t tx) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = rx}, {.derive = tx}, + {.derive = rx}, + {.derive = tx}, }; vl.values = values; @@ -150,8 +151,8 @@ static void submit_regtable(cr_data_t *rd, /* {{{ */ name = "default"; /*** RX ***/ - snprintf(type_instance, sizeof(type_instance), "%s-%s-rx", r->interface, - name); + ssnprintf(type_instance, sizeof(type_instance), "%s-%s-rx", r->interface, + name); cr_submit_gauge(rd, "bitrate", type_instance, (gauge_t)(1000000.0 * r->rx_rate)); cr_submit_gauge(rd, "signal_power", type_instance, @@ -159,8 +160,8 @@ static void submit_regtable(cr_data_t *rd, /* {{{ */ cr_submit_gauge(rd, "signal_quality", type_instance, (gauge_t)r->rx_ccq); /*** TX ***/ - snprintf(type_instance, sizeof(type_instance), "%s-%s-tx", r->interface, - name); + ssnprintf(type_instance, sizeof(type_instance), "%s-%s-tx", r->interface, + name); cr_submit_gauge(rd, "bitrate", type_instance, (gauge_t)(1000000.0 * r->tx_rate)); cr_submit_gauge(rd, "signal_power", type_instance, @@ -168,7 +169,7 @@ static void submit_regtable(cr_data_t *rd, /* {{{ */ cr_submit_gauge(rd, "signal_quality", type_instance, (gauge_t)r->tx_ccq); /*** RX / TX ***/ - snprintf(type_instance, sizeof(type_instance), "%s-%s", r->interface, name); + ssnprintf(type_instance, sizeof(type_instance), "%s-%s", r->interface, name); cr_submit_io(rd, "if_octets", type_instance, (derive_t)r->rx_bytes, (derive_t)r->tx_bytes); cr_submit_gauge(rd, "snr", type_instance, (gauge_t)r->signal_to_noise); @@ -438,11 +439,12 @@ static int cr_config_router(oconfig_item_t *ci) /* {{{ */ return status; } - snprintf(read_name, sizeof(read_name), "routeros/%s", router_data->node); + ssnprintf(read_name, sizeof(read_name), "routeros/%s", router_data->node); return plugin_register_complex_read( /* group = */ NULL, read_name, cr_read, /* interval = */ 0, &(user_data_t){ - .data = router_data, .free_func = (void *)cr_free_data, + .data = router_data, + .free_func = (void *)cr_free_data, }); } /* }}} int cr_config_router */ diff --git a/src/rrdcached.c b/src/rrdcached.c index 8b742bb5..1e75ff83 100644 --- a/src/rrdcached.c +++ b/src/rrdcached.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_rrdcreate.h" +#include "utils/common/common.h" +#include "utils/rrdcreate/rrdcreate.h" #undef HAVE_CONFIG_H #include @@ -41,44 +41,36 @@ static char *datadir; static char *daemon_address; static bool config_create_files = true; static bool config_collect_stats = true; -static rrdcreate_config_t rrdcreate_config = { - /* stepsize = */ 0, - /* heartbeat = */ 0, - /* rrarows = */ 1200, - /* xff = */ 0.1, - - /* timespans = */ NULL, - /* timespans_num = */ 0, - - /* consolidation_functions = */ NULL, - /* consolidation_functions_num = */ 0, - - /* async = */ 0}; +static rrdcreate_config_t rrdcreate_config = {.stepsize = 0, + .heartbeat = 0, + .rrarows = 1200, + .xff = 0.1, + .timespans = NULL, + .timespans_num = 0, + .consolidation_functions = NULL, + .consolidation_functions_num = 0, + .async = 0}; /* * Prototypes. */ static int rc_write(const data_set_t *ds, const value_list_t *vl, - user_data_t __attribute__((unused)) * user_data); + __attribute__((unused)) user_data_t *ud); static int rc_flush(__attribute__((unused)) cdtime_t timeout, const char *identifier, __attribute__((unused)) user_data_t *ud); static int value_list_to_string(char *buffer, int buffer_len, const data_set_t *ds, const value_list_t *vl) { - int offset; - int status; - time_t t; - - assert(0 == strcmp(ds->type, vl->type)); + assert(strcmp(ds->type, vl->type) == 0); memset(buffer, '\0', buffer_len); - t = CDTIME_T_TO_TIME_T(vl->time); - status = snprintf(buffer, buffer_len, "%lu", (unsigned long)t); + int status = + ssnprintf(buffer, buffer_len, "%.6f", CDTIME_T_TO_DOUBLE(vl->time)); if ((status < 1) || (status >= buffer_len)) return -1; - offset = status; + int offset = status; for (size_t i = 0; i < ds->ds_num; i++) { if ((ds->ds[i].type != DS_TYPE_COUNTER) && @@ -88,17 +80,17 @@ static int value_list_to_string(char *buffer, int buffer_len, return -1; if (ds->ds[i].type == DS_TYPE_COUNTER) { - status = snprintf(buffer + offset, buffer_len - offset, ":%" PRIu64, - (uint64_t)vl->values[i].counter); + status = ssnprintf(buffer + offset, buffer_len - offset, ":%" PRIu64, + (uint64_t)vl->values[i].counter); } else if (ds->ds[i].type == DS_TYPE_GAUGE) { - status = snprintf(buffer + offset, buffer_len - offset, ":%f", - vl->values[i].gauge); + status = ssnprintf(buffer + offset, buffer_len - offset, ":%f", + vl->values[i].gauge); } else if (ds->ds[i].type == DS_TYPE_DERIVE) { - status = snprintf(buffer + offset, buffer_len - offset, ":%" PRIi64, - vl->values[i].derive); + status = ssnprintf(buffer + offset, buffer_len - offset, ":%" PRIi64, + vl->values[i].derive); } else /* if (ds->ds[i].type == DS_TYPE_ABSOLUTE) */ { - status = snprintf(buffer + offset, buffer_len - offset, ":%" PRIu64, - vl->values[i].absolute); + status = ssnprintf(buffer + offset, buffer_len - offset, ":%" PRIu64, + vl->values[i].absolute); } if ((status < 1) || (status >= (buffer_len - offset))) @@ -113,8 +105,6 @@ static int value_list_to_string(char *buffer, int buffer_len, static int value_list_to_filename(char *buffer, size_t buffer_size, value_list_t const *vl) { char const suffix[] = ".rrd"; - int status; - size_t len; if (datadir != NULL) { size_t datadir_len = strlen(datadir) + 1; @@ -124,17 +114,17 @@ static int value_list_to_filename(char *buffer, size_t buffer_size, sstrncpy(buffer, datadir, buffer_size); buffer[datadir_len - 1] = '/'; - buffer[datadir_len] = 0; + buffer[datadir_len] = '\0'; buffer += datadir_len; buffer_size -= datadir_len; } - status = FORMAT_VL(buffer, buffer_size, vl); + int status = FORMAT_VL(buffer, buffer_size, vl); if (status != 0) return status; - len = strlen(buffer); + size_t len = strlen(buffer); assert(len < buffer_size); buffer += len; buffer_size -= len; @@ -147,10 +137,9 @@ static int value_list_to_filename(char *buffer, size_t buffer_size, } /* int value_list_to_filename */ static int rc_config_get_int_positive(oconfig_item_t const *ci, int *ret) { - int status; int tmp = 0; - status = cf_util_get_int(ci, &tmp); + int status = cf_util_get_int(ci, &tmp); if (status != 0) return status; if (tmp < 0) @@ -161,8 +150,6 @@ static int rc_config_get_int_positive(oconfig_item_t const *ci, int *ret) { } /* int rc_config_get_int_positive */ static int rc_config_get_xff(oconfig_item_t const *ci, double *ret) { - double value; - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) { ERROR("rrdcached plugin: The \"%s\" needs exactly one numeric argument " "in the range [0.0, 1.0)", @@ -170,7 +157,7 @@ static int rc_config_get_xff(oconfig_item_t const *ci, double *ret) { return EINVAL; } - value = ci->values[0].value.number; + double value = ci->values[0].value.number; if ((value >= 0.0) && (value < 1.0)) { *ret = value; return 0; @@ -183,14 +170,12 @@ static int rc_config_get_xff(oconfig_item_t const *ci, double *ret) { } /* int rc_config_get_xff */ static int rc_config_add_timespan(int timespan) { - int *tmp; - if (timespan <= 0) return EINVAL; - tmp = realloc(rrdcreate_config.timespans, - sizeof(*rrdcreate_config.timespans) * - (rrdcreate_config.timespans_num + 1)); + int *tmp = realloc(rrdcreate_config.timespans, + sizeof(*rrdcreate_config.timespans) * + (rrdcreate_config.timespans_num + 1)); if (tmp == NULL) return ENOMEM; rrdcreate_config.timespans = tmp; @@ -262,12 +247,10 @@ static int rc_config(oconfig_item_t *ci) { } /* int rc_config */ static int try_reconnect(void) { - int status; - rrdc_disconnect(); rrd_clear_error(); - status = rrdc_connect(daemon_address); + int status = rrdc_connect(daemon_address); if (status != 0) { ERROR("rrdcached plugin: Failed to reconnect to RRDCacheD " "at %s: %s (status=%d)", @@ -282,9 +265,6 @@ static int try_reconnect(void) { } /* int try_reconnect */ static int rc_read(void) { - int status; - rrdc_stats_t *head; - bool retried = false; value_list_t vl = VALUE_LIST_INIT; vl.values = &(value_t){.gauge = NAN}; @@ -302,7 +282,7 @@ static int rc_read(void) { sstrncpy(vl.plugin, "rrdcached", sizeof(vl.plugin)); rrd_clear_error(); - status = rrdc_connect(daemon_address); + int status = rrdc_connect(daemon_address); if (status != 0) { ERROR("rrdcached plugin: Failed to connect to RRDCacheD " "at %s: %s (status=%d)", @@ -310,6 +290,9 @@ static int rc_read(void) { return -1; } + rrdc_stats_t *head; + bool retried = false; + while (42) { /* The RRD client lib does not provide any means for checking a * connection, hence we'll have to retry upon failed operations. */ @@ -390,7 +373,6 @@ static int rc_write(const data_set_t *ds, const value_list_t *vl, user_data_t __attribute__((unused)) * user_data) { char filename[PATH_MAX]; char values[512]; - char *values_array[2]; int status; bool retried = false; @@ -415,9 +397,6 @@ static int rc_write(const data_set_t *ds, const value_list_t *vl, return -1; } - values_array[0] = values; - values_array[1] = NULL; - if (config_create_files) { struct stat statbuf; @@ -446,6 +425,11 @@ static int rc_write(const data_set_t *ds, const value_list_t *vl, return -1; } + char *values_array[2] = { + [0] = values, + [1] = NULL, + }; + while (42) { /* The RRD client lib does not provide any means for checking a * connection, hence we'll have to retry upon failed operations. */ @@ -472,20 +456,18 @@ static int rc_write(const data_set_t *ds, const value_list_t *vl, static int rc_flush(__attribute__((unused)) cdtime_t timeout, /* {{{ */ const char *identifier, __attribute__((unused)) user_data_t *ud) { - char filename[PATH_MAX + 1]; - int status; - bool retried = false; - if (identifier == NULL) return EINVAL; + char filename[PATH_MAX + 1]; + if (datadir != NULL) - snprintf(filename, sizeof(filename), "%s/%s.rrd", datadir, identifier); + ssnprintf(filename, sizeof(filename), "%s/%s.rrd", datadir, identifier); else - snprintf(filename, sizeof(filename), "%s.rrd", identifier); + ssnprintf(filename, sizeof(filename), "%s.rrd", identifier); rrd_clear_error(); - status = rrdc_connect(daemon_address); + int status = rrdc_connect(daemon_address); if (status != 0) { ERROR("rrdcached plugin: Failed to connect to RRDCacheD " "at %s: %s (status=%d)", @@ -493,6 +475,8 @@ static int rc_flush(__attribute__((unused)) cdtime_t timeout, /* {{{ */ return -1; } + bool retried = false; + while (42) { /* The RRD client lib does not provide any means for checking a * connection, hence we'll have to retry upon failed operations. */ diff --git a/src/rrdtool.c b/src/rrdtool.c index f6290d73..bd5943c5 100644 --- a/src/rrdtool.c +++ b/src/rrdtool.c @@ -25,11 +25,11 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" +#include "utils/rrdcreate/rrdcreate.h" #include "utils_random.h" -#include "utils_rrdcreate.h" #include @@ -120,7 +120,7 @@ static int srrd_update(char *filename, char *template, int argc, return status; } /* int srrd_update */ -/* #endif HAVE_THREADSAFE_LIBRRD */ + /* #endif HAVE_THREADSAFE_LIBRRD */ #else /* !HAVE_THREADSAFE_LIBRRD */ static int srrd_update(char *filename, char *template, int argc, @@ -173,7 +173,7 @@ static int value_list_to_string_multiple(char *buffer, int buffer_len, memset(buffer, '\0', buffer_len); tt = CDTIME_T_TO_TIME_T(vl->time); - status = snprintf(buffer, buffer_len, "%u", (unsigned int)tt); + status = ssnprintf(buffer, buffer_len, "%u", (unsigned int)tt); if ((status < 1) || (status >= buffer_len)) return -1; offset = status; @@ -186,17 +186,17 @@ static int value_list_to_string_multiple(char *buffer, int buffer_len, return -1; if (ds->ds[i].type == DS_TYPE_COUNTER) - status = snprintf(buffer + offset, buffer_len - offset, ":%" PRIu64, - (uint64_t)vl->values[i].counter); + status = ssnprintf(buffer + offset, buffer_len - offset, ":%" PRIu64, + (uint64_t)vl->values[i].counter); else if (ds->ds[i].type == DS_TYPE_GAUGE) - status = snprintf(buffer + offset, buffer_len - offset, ":" GAUGE_FORMAT, - vl->values[i].gauge); + status = ssnprintf(buffer + offset, buffer_len - offset, ":" GAUGE_FORMAT, + vl->values[i].gauge); else if (ds->ds[i].type == DS_TYPE_DERIVE) - status = snprintf(buffer + offset, buffer_len - offset, ":%" PRIi64, - vl->values[i].derive); + status = ssnprintf(buffer + offset, buffer_len - offset, ":%" PRIi64, + vl->values[i].derive); else /*if (ds->ds[i].type == DS_TYPE_ABSOLUTE) */ - status = snprintf(buffer + offset, buffer_len - offset, ":%" PRIu64, - vl->values[i].absolute); + status = ssnprintf(buffer + offset, buffer_len - offset, ":%" PRIu64, + vl->values[i].absolute); if ((status < 1) || (status >= (buffer_len - offset))) return -1; @@ -218,20 +218,20 @@ static int value_list_to_string(char *buffer, int buffer_len, tt = CDTIME_T_TO_TIME_T(vl->time); switch (ds->ds[0].type) { case DS_TYPE_DERIVE: - status = snprintf(buffer, buffer_len, "%u:%" PRIi64, (unsigned)tt, - vl->values[0].derive); + status = ssnprintf(buffer, buffer_len, "%u:%" PRIi64, (unsigned)tt, + vl->values[0].derive); break; case DS_TYPE_GAUGE: - status = snprintf(buffer, buffer_len, "%u:" GAUGE_FORMAT, (unsigned)tt, - vl->values[0].gauge); + status = ssnprintf(buffer, buffer_len, "%u:" GAUGE_FORMAT, (unsigned)tt, + vl->values[0].gauge); break; case DS_TYPE_COUNTER: - status = snprintf(buffer, buffer_len, "%u:%" PRIu64, (unsigned)tt, - (uint64_t)vl->values[0].counter); + status = ssnprintf(buffer, buffer_len, "%u:%" PRIu64, (unsigned)tt, + (uint64_t)vl->values[0].counter); break; case DS_TYPE_ABSOLUTE: - status = snprintf(buffer, buffer_len, "%u:%" PRIu64, (unsigned)tt, - vl->values[0].absolute); + status = ssnprintf(buffer, buffer_len, "%u:%" PRIu64, (unsigned)tt, + vl->values[0].absolute); break; default: return EINVAL; @@ -566,10 +566,10 @@ static int rrd_cache_flush_identifier(cdtime_t timeout, now = cdtime(); if (datadir == NULL) - snprintf(key, sizeof(key), "%s.rrd", identifier); + ssnprintf(key, sizeof(key), "%s.rrd", identifier); else - snprintf(key, sizeof(key), "%s/%s.rrd", datadir, identifier); - key[sizeof(key) - 1] = 0; + ssnprintf(key, sizeof(key), "%s/%s.rrd", datadir, identifier); + key[sizeof(key) - 1] = '\0'; status = c_avl_get(cache, key, (void *)&rc); if (status != 0) { diff --git a/src/sensors.c b/src/sensors.c index 6106df39..61868e88 100644 --- a/src/sensors.c +++ b/src/sensors.c @@ -35,9 +35,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #if defined(HAVE_SENSORS_SENSORS_H) #include @@ -47,90 +47,6 @@ #define SENSORS_API_VERSION 0x000 #endif -/* - * The sensors library prior to version 3.0 (internal version 0x400) didn't - * report the type of values, only a name. The following lists are there to - * convert from the names to the type. They are not used with the new - * interface. - */ -#if SENSORS_API_VERSION < 0x400 -static char *sensor_type_name_map[] = { -#define SENSOR_TYPE_VOLTAGE 0 - "voltage", -#define SENSOR_TYPE_FANSPEED 1 - "fanspeed", -#define SENSOR_TYPE_TEMPERATURE 2 - "temperature", -#define SENSOR_TYPE_POWER 3 - "power", -#define SENSOR_TYPE_UNKNOWN 4 - NULL}; - -struct sensors_labeltypes_s { - char *label; - int type; -}; -typedef struct sensors_labeltypes_s sensors_labeltypes_t; - -/* finite list of known labels extracted from lm_sensors */ -static sensors_labeltypes_t known_features[] = { - {"fan1", SENSOR_TYPE_FANSPEED}, - {"fan2", SENSOR_TYPE_FANSPEED}, - {"fan3", SENSOR_TYPE_FANSPEED}, - {"fan4", SENSOR_TYPE_FANSPEED}, - {"fan5", SENSOR_TYPE_FANSPEED}, - {"fan6", SENSOR_TYPE_FANSPEED}, - {"fan7", SENSOR_TYPE_FANSPEED}, - {"AIN2", SENSOR_TYPE_VOLTAGE}, - {"AIN1", SENSOR_TYPE_VOLTAGE}, - {"in10", SENSOR_TYPE_VOLTAGE}, - {"in9", SENSOR_TYPE_VOLTAGE}, - {"in8", SENSOR_TYPE_VOLTAGE}, - {"in7", SENSOR_TYPE_VOLTAGE}, - {"in6", SENSOR_TYPE_VOLTAGE}, - {"in5", SENSOR_TYPE_VOLTAGE}, - {"in4", SENSOR_TYPE_VOLTAGE}, - {"in3", SENSOR_TYPE_VOLTAGE}, - {"in2", SENSOR_TYPE_VOLTAGE}, - {"in0", SENSOR_TYPE_VOLTAGE}, - {"CPU_Temp", SENSOR_TYPE_TEMPERATURE}, - {"remote_temp", SENSOR_TYPE_TEMPERATURE}, - {"temp1", SENSOR_TYPE_TEMPERATURE}, - {"temp2", SENSOR_TYPE_TEMPERATURE}, - {"temp3", SENSOR_TYPE_TEMPERATURE}, - {"temp4", SENSOR_TYPE_TEMPERATURE}, - {"temp5", SENSOR_TYPE_TEMPERATURE}, - {"temp6", SENSOR_TYPE_TEMPERATURE}, - {"temp7", SENSOR_TYPE_TEMPERATURE}, - {"temp", SENSOR_TYPE_TEMPERATURE}, - {"Vccp2", SENSOR_TYPE_VOLTAGE}, - {"Vccp1", SENSOR_TYPE_VOLTAGE}, - {"vdd", SENSOR_TYPE_VOLTAGE}, - {"vid5", SENSOR_TYPE_VOLTAGE}, - {"vid4", SENSOR_TYPE_VOLTAGE}, - {"vid3", SENSOR_TYPE_VOLTAGE}, - {"vid2", SENSOR_TYPE_VOLTAGE}, - {"vid1", SENSOR_TYPE_VOLTAGE}, - {"vid", SENSOR_TYPE_VOLTAGE}, - {"vin4", SENSOR_TYPE_VOLTAGE}, - {"vin3", SENSOR_TYPE_VOLTAGE}, - {"vin2", SENSOR_TYPE_VOLTAGE}, - {"vin1", SENSOR_TYPE_VOLTAGE}, - {"voltbatt", SENSOR_TYPE_VOLTAGE}, - {"volt12", SENSOR_TYPE_VOLTAGE}, - {"volt5", SENSOR_TYPE_VOLTAGE}, - {"vrm", SENSOR_TYPE_VOLTAGE}, - {"5.0V", SENSOR_TYPE_VOLTAGE}, - {"5V", SENSOR_TYPE_VOLTAGE}, - {"3.3V", SENSOR_TYPE_VOLTAGE}, - {"2.5V", SENSOR_TYPE_VOLTAGE}, - {"2.0V", SENSOR_TYPE_VOLTAGE}, - {"12V", SENSOR_TYPE_VOLTAGE}, - {"power1", SENSOR_TYPE_POWER}}; -static int known_features_num = STATIC_ARRAY_SIZE(known_features); -/* end new naming */ -#endif /* SENSORS_API_VERSION < 0x400 */ - static const char *config_keys[] = {"Sensor", "IgnoreSelected", "SensorConfigFile", "UseLabels"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); @@ -149,7 +65,7 @@ typedef struct featurelist { static char *conffile = SENSORS_CONF_PATH; /* #endif SENSORS_API_VERSION < 0x400 */ -#elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) +#elif (SENSORS_API_VERSION >= 0x400) typedef struct featurelist { const sensors_chip_name *chip; const sensors_feature *feature; @@ -159,46 +75,11 @@ typedef struct featurelist { static char *conffile; static bool use_labels; -/* #endif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */ - -#else /* if SENSORS_API_VERSION >= 0x500 */ -#error "This version of libsensors is not supported yet. Please report this " \ - "as bug." #endif static featurelist_t *first_feature; static ignorelist_t *sensor_list; -#if SENSORS_API_VERSION < 0x400 -/* full chip name logic borrowed from lm_sensors */ -static int sensors_snprintf_chip_name(char *buf, size_t buf_size, - const sensors_chip_name *chip) { - int status = -1; - - if (chip->bus == SENSORS_CHIP_NAME_BUS_ISA) { - status = snprintf(buf, buf_size, "%s-isa-%04x", chip->prefix, chip->addr); - } else if (chip->bus == SENSORS_CHIP_NAME_BUS_DUMMY) { - status = snprintf(buf, buf_size, "%s-%s-%04x", chip->prefix, chip->busname, - chip->addr); - } else { - status = snprintf(buf, buf_size, "%s-i2c-%d-%02x", chip->prefix, chip->bus, - chip->addr); - } - - return status; -} /* int sensors_snprintf_chip_name */ - -static int sensors_feature_name_to_type(const char *name) { - /* Yes, this is slow, but it's only ever done during initialization, so - * it's a one time cost.. */ - for (int i = 0; i < known_features_num; i++) - if (strcasecmp(known_features[i].label, name) == 0) - return known_features[i].type; - - return SENSOR_TYPE_UNKNOWN; -} /* int sensors_feature_name_to_type */ -#endif - static int sensors_config(const char *key, const char *value) { if (sensor_list == NULL) sensor_list = ignorelist_create(1); @@ -223,7 +104,7 @@ static int sensors_config(const char *key, const char *value) { if (IS_TRUE(value)) ignorelist_set_invert(sensor_list, 0); } -#if (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) +#if (SENSORS_API_VERSION >= 0x400) else if (strcasecmp(key, "UseLabels") == 0) { use_labels = IS_TRUE(value); } @@ -349,9 +230,9 @@ static int sensors_load_conf(void) { last_feature = fl; } /* while sensors_get_all_features */ } /* while sensors_get_detected_chips */ -/* #endif SENSORS_API_VERSION < 0x400 */ + /* #endif SENSORS_API_VERSION < 0x400 */ -#elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) +#elif (SENSORS_API_VERSION >= 0x400) chip_num = 0; while ((chip = sensors_get_detected_chips(NULL, &chip_num)) != NULL) { const sensors_feature *feature; @@ -414,7 +295,7 @@ static int sensors_load_conf(void) { } /* while (subfeature) */ } /* while (feature) */ } /* while (chip) */ -#endif /* (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */ +#endif /* (SENSORS_API_VERSION >= 0x400) */ if (first_feature == NULL) { sensors_cleanup(); @@ -487,9 +368,9 @@ static int sensors_read(void) { sensors_submit(plugin_instance, sensor_type_name_map[fl->type], type_instance, value); } /* for fl = first_feature .. NULL */ -/* #endif SENSORS_API_VERSION < 0x400 */ + /* #endif SENSORS_API_VERSION < 0x400 */ -#elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) +#elif (SENSORS_API_VERSION >= 0x400) for (featurelist_t *fl = first_feature; fl != NULL; fl = fl->next) { double value; int status; @@ -536,7 +417,7 @@ static int sensors_read(void) { sensors_submit(plugin_instance, type, type_instance, value); } /* for fl = first_feature .. NULL */ -#endif /* (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */ +#endif /* (SENSORS_API_VERSION >= 0x400) */ return 0; } /* int sensors_read */ diff --git a/src/serial.c b/src/serial.c index 8bbd94c0..ae6f443a 100644 --- a/src/serial.c +++ b/src/serial.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !KERNEL_LINUX #error "No applicable input method." @@ -33,7 +33,8 @@ static void serial_submit(const char *type_instance, derive_t rx, derive_t tx) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = rx}, {.derive = tx}, + {.derive = rx}, + {.derive = tx}, }; vl.values = values; @@ -79,7 +80,7 @@ static int serial_read(void) { continue; if (fields[0][len - 1] != ':') continue; - fields[0][len - 1] = 0; + fields[0][len - 1] = '\0'; for (int i = 1; i < numfields; i++) { len = strlen(fields[i]); diff --git a/src/sigrok.c b/src/sigrok.c index eeab8c95..07bd1c81 100644 --- a/src/sigrok.c +++ b/src/sigrok.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -247,10 +247,10 @@ static int sigrok_init_driver(struct config_device *cfdev, } cfdev->sdi = devlist->data; g_slist_free(devlist); - snprintf(hwident, sizeof(hwident), "%s %s %s", - cfdev->sdi->vendor ? cfdev->sdi->vendor : "", - cfdev->sdi->model ? cfdev->sdi->model : "", - cfdev->sdi->version ? cfdev->sdi->version : ""); + ssnprintf(hwident, sizeof(hwident), "%s %s %s", + cfdev->sdi->vendor ? cfdev->sdi->vendor : "", + cfdev->sdi->model ? cfdev->sdi->model : "", + cfdev->sdi->version ? cfdev->sdi->version : ""); INFO("sigrok plugin: Device \"%s\" is a %s", cfdev->name, hwident); if (sr_dev_open(cfdev->sdi) != SR_OK) diff --git a/src/smart.c b/src/smart.c index 62cbb4f1..627c16d5 100644 --- a/src/smart.c +++ b/src/smart.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include #include @@ -116,9 +116,9 @@ static void handle_attribute(SkDisk *d, const SkSmartAttributeParsedData *a, sstrncpy(notif.host, hostname_g, sizeof(notif.host)); sstrncpy(notif.plugin_instance, name, sizeof(notif.plugin_instance)); sstrncpy(notif.type_instance, a->name, sizeof(notif.type_instance)); - snprintf(notif.message, sizeof(notif.message), - "attribute %s is below allowed threshold (%d < %d)", a->name, - a->current_value, a->threshold); + ssnprintf(notif.message, sizeof(notif.message), + "attribute %s is below allowed threshold (%d < %d)", a->name, + a->current_value, a->threshold); plugin_dispatch_notification(¬if); } } diff --git a/src/snmp.c b/src/snmp.c index af26fbd8..aeb04fdd 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -26,10 +26,10 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include "utils_complain.h" -#include "utils_ignorelist.h" #include #include @@ -171,7 +171,7 @@ static int csnmp_oid_to_string(char *buffer, size_t buffer_size, char *oid_str_ptr[MAX_OID_LEN]; for (size_t i = 0; i < o->oid_len; i++) { - snprintf(oid_str[i], sizeof(oid_str[i]), "%lu", (unsigned long)o->oid[i]); + ssnprintf(oid_str[i], sizeof(oid_str[i]), "%lu", (unsigned long)o->oid[i]); oid_str_ptr[i] = oid_str[i]; } @@ -871,12 +871,13 @@ static int csnmp_config_add_host(oconfig_item_t *ci) { "= %i }", hd->name, hd->address, hd->community, hd->version); - snprintf(cb_name, sizeof(cb_name), "snmp-%s", hd->name); + ssnprintf(cb_name, sizeof(cb_name), "snmp-%s", hd->name); status = plugin_register_complex_read( /* group = */ NULL, cb_name, csnmp_read_host, interval, &(user_data_t){ - .data = hd, .free_func = csnmp_host_definition_destroy, + .data = hd, + .free_func = csnmp_host_definition_destroy, }); if (status != 0) { ERROR("snmp plugin: Registering complex read function failed."); @@ -1140,13 +1141,13 @@ static int csnmp_strvbcopy_hexstring(char *dst, /* {{{ */ for (size_t i = 0; i < vb->val_len; i++) { int status; - status = snprintf(buffer_ptr, buffer_free, (i == 0) ? "%02x" : ":%02x", - (unsigned int)vb->val.bitstring[i]); + status = ssnprintf(buffer_ptr, buffer_free, (i == 0) ? "%02x" : ":%02x", + (unsigned int)vb->val.bitstring[i]); assert(status >= 0); if (((size_t)status) >= buffer_free) /* truncated */ { - dst[dst_size - 1] = 0; + dst[dst_size - 1] = '\0'; return ENOMEM; } else /* if (status < buffer_free) */ { @@ -1173,10 +1174,10 @@ static int csnmp_strvbcopy(char *dst, /* {{{ */ else if (vb->type == ASN_BIT_STR) src = (char *)vb->val.bitstring; else if (vb->type == ASN_IPADDRESS) { - return snprintf(dst, dst_size, - "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 "", - (uint8_t)vb->val.string[0], (uint8_t)vb->val.string[1], - (uint8_t)vb->val.string[2], (uint8_t)vb->val.string[3]); + return ssnprintf(dst, dst_size, + "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 "", + (uint8_t)vb->val.string[0], (uint8_t)vb->val.string[1], + (uint8_t)vb->val.string[2], (uint8_t)vb->val.string[3]); } else { dst[0] = 0; return EINVAL; @@ -1193,7 +1194,7 @@ static int csnmp_strvbcopy(char *dst, /* {{{ */ dst[i] = src[i]; } dst[num_chars] = 0; - dst[dst_size - 1] = 0; + dst[dst_size - 1] = '\0'; if (dst_size <= vb->val_len) return ENOMEM; @@ -1234,7 +1235,7 @@ static csnmp_cell_char_t *csnmp_get_char_cell(const struct variable_list *vb, value_t val = csnmp_value_list_to_value( vb, DS_TYPE_COUNTER, /* scale = */ 1.0, /* shift = */ 0.0, hd->name, dd->name); - snprintf(il->value, sizeof(il->value), "%" PRIu64, (uint64_t)val.counter); + ssnprintf(il->value, sizeof(il->value), "%" PRIu64, (uint64_t)val.counter); } return il; @@ -1479,7 +1480,7 @@ static int csnmp_dispatch_table(host_definition_t *host, if (data->host.prefix == NULL) sstrncpy(vl.host, temp, sizeof(vl.host)); else - snprintf(vl.host, sizeof(vl.host), "%s%s", data->host.prefix, temp); + ssnprintf(vl.host, sizeof(vl.host), "%s%s", data->host.prefix, temp); } else { sstrncpy(vl.host, host->name, sizeof(vl.host)); } @@ -1495,8 +1496,8 @@ static int csnmp_dispatch_table(host_definition_t *host, if (data->type_instance.prefix == NULL) sstrncpy(vl.type_instance, temp, sizeof(vl.type_instance)); else - snprintf(vl.type_instance, sizeof(vl.type_instance), "%s%s", - data->type_instance.prefix, temp); + ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s%s", + data->type_instance.prefix, temp); } else if (data->type_instance.value) { sstrncpy(vl.type_instance, data->type_instance.value, sizeof(vl.type_instance)); @@ -1513,8 +1514,8 @@ static int csnmp_dispatch_table(host_definition_t *host, if (data->plugin_instance.prefix == NULL) sstrncpy(vl.plugin_instance, temp, sizeof(vl.plugin_instance)); else - snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s%s", - data->plugin_instance.prefix, temp); + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s%s", + data->plugin_instance.prefix, temp); } else if (data->plugin_instance.value) { sstrncpy(vl.plugin_instance, data->plugin_instance.value, sizeof(vl.plugin_instance)); diff --git a/src/snmp_agent.c b/src/snmp_agent.c index 1c7191ff..bb4a7e42 100644 --- a/src/snmp_agent.c +++ b/src/snmp_agent.c @@ -29,8 +29,8 @@ #include "collectd.h" -#include "common.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" #include "utils_cache.h" #include "utils_llist.h" #include @@ -172,7 +172,7 @@ static int snmp_agent_oid_to_string(char *buf, size_t buf_size, char *oid_str_ptr[MAX_OID_LEN]; for (size_t i = 0; i < o->oid_len; i++) { - snprintf(oid_str[i], sizeof(oid_str[i]), "%lu", (unsigned long)o->oid[i]); + ssnprintf(oid_str[i], sizeof(oid_str[i]), "%lu", (unsigned long)o->oid[i]); oid_str_ptr[i] = oid_str[i]; } @@ -736,13 +736,14 @@ static void snmp_agent_table_data_remove(data_definition_t *dd, if (index == NULL) snmp_agent_oid_to_string(index_str, sizeof(index_str), index_oid); else - snprintf(index_str, sizeof(index_str), "%d", *index); + ssnprintf(index_str, sizeof(index_str), "%d", *index); notification_t n = { .severity = NOTIF_WARNING, .time = cdtime(), .plugin = PLUGIN_NAME}; sstrncpy(n.host, hostname_g, sizeof(n.host)); - snprintf(n.message, sizeof(n.message), - "Removed data row from table %s with index %s", td->name, index_str); + ssnprintf(n.message, sizeof(n.message), + "Removed data row from table %s with index %s", td->name, + index_str); DEBUG(PLUGIN_NAME ": %s", n.message); plugin_dispatch_notification(&n); @@ -960,7 +961,7 @@ static int snmp_agent_build_name(char **name, c_avl_tree_t *tokens) { strncat(out, tok->str, DATA_MAX_NAME_LEN - strlen(out) - 1); if (tok->key != NULL) { if (tok->key->type == ASN_INTEGER) { - snprintf(str, sizeof(str), "%ld", *tok->key->val.integer); + ssnprintf(str, sizeof(str), "%ld", *tok->key->val.integer); strncat(out, str, DATA_MAX_NAME_LEN - strlen(out) - 1); } else /* OCTET_STR */ strncat(out, (char *)tok->key->val.string, @@ -1013,7 +1014,7 @@ static int snmp_agent_format_name(char *name, int name_len, } if (td->index_keys[i].type == ASN_INTEGER) { - snprintf(str, sizeof(str), "%ld", *key->val.integer); + ssnprintf(str, sizeof(str), "%ld", *key->val.integer); fields[source] = str; } else /* OCTET_STR */ fields[source] = (char *)key->val.string; @@ -1836,7 +1837,7 @@ static int snmp_agent_set_vardata(void *data, size_t *data_len, u_char asn_type, case ASN_OCTET_STR: if (type == DS_TYPE_GAUGE) { char buf[DATA_MAX_NAME_LEN]; - snprintf(buf, sizeof(buf), "%.2f", val->gauge); + ssnprintf(buf, sizeof(buf), "%.2f", val->gauge); if (*data_len < strlen(buf)) return -EINVAL; *data_len = strlen(buf); @@ -1995,13 +1996,13 @@ static int snmp_agent_update_index(data_definition_t *dd, if (index == NULL) snmp_agent_oid_to_string(index_str, sizeof(index_str), index_oid); else - snprintf(index_str, sizeof(index_str), "%d", *index); + ssnprintf(index_str, sizeof(index_str), "%d", *index); notification_t n = { .severity = NOTIF_OKAY, .time = cdtime(), .plugin = PLUGIN_NAME}; sstrncpy(n.host, hostname_g, sizeof(n.host)); - snprintf(n.message, sizeof(n.message), - "Data added to table %s with index %s", td->name, index_str); + ssnprintf(n.message, sizeof(n.message), + "Data added to table %s with index %s", td->name, index_str); DEBUG(PLUGIN_NAME ": %s", n.message); plugin_dispatch_notification(&n); @@ -2202,8 +2203,7 @@ static int snmp_agent_register_oid(oid_t *oid, Netsnmp_Node_Handler *handler) { if (c_avl_get(g_agent->registered_oids, (void *)oid, NULL) == 0) return OID_EXISTS; else { - oid_t *new_oid = calloc(1, sizeof(*oid)); - + oid_t *new_oid = calloc(1, sizeof(*new_oid)); if (new_oid == NULL) { ERROR(PLUGIN_NAME ": Could not allocate memory to register new OID"); return -ENOMEM; diff --git a/src/statsd.c b/src/statsd.c index 1558ec84..9050596f 100644 --- a/src/statsd.c +++ b/src/statsd.c @@ -26,10 +26,10 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_avltree.h" -#include "utils_latency.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" +#include "utils/latency/latency.h" #include #include @@ -606,9 +606,8 @@ static int statsd_config_timer_percentile(oconfig_item_t *ci) /* {{{ */ return ERANGE; } - tmp = - realloc(conf_timer_percentile, - sizeof(*conf_timer_percentile) * (conf_timer_percentile_num + 1)); + tmp = realloc(conf_timer_percentile, sizeof(*conf_timer_percentile) * + (conf_timer_percentile_num + 1)); if (tmp == NULL) { ERROR("statsd plugin: realloc failed."); return ENOMEM; diff --git a/src/swap.c b/src/swap.c index 929df6db..61c9e284 100644 --- a/src/swap.c +++ b/src/swap.c @@ -37,8 +37,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_SYS_SWAP_H #include @@ -150,12 +150,12 @@ static int swap_init(void) /* {{{ */ { #if KERNEL_LINUX pagesize = (derive_t)sysconf(_SC_PAGESIZE); -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS /* getpagesize(3C) tells me this does not fail.. */ pagesize = (derive_t)getpagesize(); -/* #endif HAVE_SWAPCTL */ + /* #endif HAVE_SWAPCTL */ #elif defined(VM_SWAPUSAGE) /* No init stuff */ @@ -177,7 +177,7 @@ static int swap_init(void) /* {{{ */ ERROR("swap plugin: kvm_openfiles failed, %s", errbuf); return -1; } -/* #endif HAVE_LIBKVM_GETSWAPINFO */ + /* #endif HAVE_LIBKVM_GETSWAPINFO */ #elif HAVE_LIBSTATGRAB /* No init stuff */ diff --git a/src/synproxy.c b/src/synproxy.c index 6c6b997d..d51077f2 100644 --- a/src/synproxy.c +++ b/src/synproxy.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !KERNEL_LINUX #error "No applicable input method." diff --git a/src/syslog.c b/src/syslog.c index beb82456..ee3d9f93 100644 --- a/src/syslog.c +++ b/src/syslog.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_SYSLOG_H #include @@ -41,7 +41,8 @@ static int log_level = LOG_INFO; static int notif_severity; static const char *config_keys[] = { - "LogLevel", "NotifyLevel", + "LogLevel", + "NotifyLevel", }; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); @@ -50,13 +51,14 @@ static int sl_config(const char *key, const char *value) { log_level = parse_log_severity(value); if (log_level < 0) { log_level = LOG_INFO; - ERROR("syslog: invalid loglevel [%s] defaulting to 'info'", value); - return 1; + WARNING("syslog: invalid loglevel [%s] defaulting to 'info'", value); } } else if (strcasecmp(key, "NotifyLevel") == 0) { notif_severity = parse_notif_severity(value); - if (notif_severity < 0) + if (notif_severity < 0) { + ERROR("syslog: invalid notification severity [%s]", value); return 1; + } } return 0; diff --git a/src/table.c b/src/table.c index 492bea61..f181de94 100644 --- a/src/table.c +++ b/src/table.c @@ -30,7 +30,7 @@ #include "collectd.h" -#include "common.h" +#include "utils/common/common.h" #include "plugin.h" diff --git a/src/tail.c b/src/tail.c index df94580c..8c9dfbe5 100644 --- a/src/tail.c +++ b/src/tail.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_latency_config.h" +#include "utils/common/common.h" +#include "utils/latency/latency_config.h" #include "utils_tail_match.h" /* diff --git a/src/tail_csv.c b/src/tail_csv.c index 48f3d743..ab8bf6d5 100644 --- a/src/tail_csv.c +++ b/src/tail_csv.c @@ -23,9 +23,9 @@ #include "collectd.h" -#include "common.h" /* auxiliary functions */ -#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */ -#include "utils_tail.h" +#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */ +#include "utils/common/common.h" /* auxiliary functions */ +#include "utils/tail/tail.h" #include #include @@ -143,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; @@ -482,7 +482,8 @@ static int tcsv_config_add_file(oconfig_item_t *ci) { status = plugin_register_complex_read( NULL, cb_name, tcsv_read, interval, &(user_data_t){ - .data = id, .free_func = tcsv_instance_definition_destroy, + .data = id, + .free_func = tcsv_instance_definition_destroy, }); if (status != 0) { ERROR("tail_csv plugin: Registering complex read function failed."); diff --git a/src/tape.c b/src/tape.c index 26bd969a..c95ebfb4 100644 --- a/src/tape.c +++ b/src/tape.c @@ -22,8 +22,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if !HAVE_LIBKSTAT #error "No applicable input method." @@ -63,7 +63,8 @@ static void tape_submit(const char *plugin_instance, const char *type, derive_t read, derive_t write) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = read}, {.derive = write}, + {.derive = read}, + {.derive = write}, }; vl.values = values; diff --git a/src/target_notification.c b/src/target_notification.c index f83a904a..6d8059ea 100644 --- a/src/target_notification.c +++ b/src/target_notification.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" #include "utils_cache.h" #include "utils_subst.h" diff --git a/src/target_replace.c b/src/target_replace.c index 887507e1..62928c09 100644 --- a/src/target_replace.c +++ b/src/target_replace.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" #include "utils_subst.h" #include diff --git a/src/target_scale.c b/src/target_scale.c index 1cc5d793..4a9e9df4 100644 --- a/src/target_scale.c +++ b/src/target_scale.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" +#include "utils/common/common.h" #include "utils_cache.h" diff --git a/src/target_set.c b/src/target_set.c index 9629e0ed..4bbd2c0d 100644 --- a/src/target_set.c +++ b/src/target_set.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" -#include "meta_data.h" +#include "utils/common/common.h" +#include "utils/metadata/meta_data.h" #include "utils_subst.h" struct ts_key_list_s { diff --git a/src/target_v5upgrade.c b/src/target_v5upgrade.c index 650f9a5e..c2fe1922 100644 --- a/src/target_v5upgrade.c +++ b/src/target_v5upgrade.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "filter_chain.h" #include "plugin.h" +#include "utils/common/common.h" static void v5_swap_instances(value_list_t *vl) /* {{{ */ { diff --git a/src/tcpconns.c b/src/tcpconns.c index 5a3fd4ef..ae08b882 100644 --- a/src/tcpconns.c +++ b/src/tcpconns.c @@ -59,8 +59,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if defined(__OpenBSD__) #define HAVE_KVM_GETFILES 1 @@ -413,7 +413,7 @@ static int conn_handle_ports(uint16_t port_local, uint16_t port_remote, #if TCP_STATE_MIN > 0 || (state < TCP_STATE_MIN) #endif - ) { + ) { NOTICE("tcpconns plugin: Ignoring connection with " "unknown state 0x%02" PRIx8 ".", state); @@ -630,10 +630,10 @@ static int conn_read_file(const char *file) { return 0; } /* int conn_read_file */ -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_SYSCTLBYNAME -/* #endif HAVE_SYSCTLBYNAME */ + /* #endif HAVE_SYSCTLBYNAME */ #elif HAVE_LIBKVM_NLIST #endif /* HAVE_LIBKVM_NLIST */ @@ -729,7 +729,7 @@ static int conn_read(void) { return 0; } /* int conn_read */ -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_SYSCTLBYNAME static int conn_read(void) { @@ -848,8 +848,8 @@ static int conn_read(void) { return 0; } -/* int conn_read */ -/* #endif HAVE_KVM_GETFILES */ + /* int conn_read */ + /* #endif HAVE_KVM_GETFILES */ #elif HAVE_LIBKVM_NLIST static int kread(u_long addr, void *buf, int size) { @@ -966,7 +966,7 @@ static int conn_read(void) { return 0; } -/* #endif HAVE_LIBKVM_NLIST */ + /* #endif HAVE_LIBKVM_NLIST */ #elif KERNEL_AIX diff --git a/src/teamspeak2.c b/src/teamspeak2.c index 6d0cdbb8..42cc54eb 100644 --- a/src/teamspeak2.c +++ b/src/teamspeak2.c @@ -23,8 +23,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -132,7 +132,8 @@ static void tss2_submit_io(const char *plugin_instance, const char *type, */ value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = rx}, {.derive = tx}, + {.derive = rx}, + {.derive = tx}, }; vl.values = values; @@ -255,7 +256,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 " @@ -309,7 +310,7 @@ static int tss2_receive_line(FILE *fh, char *buffer, int buffer_size) { return -1; } - buffer[buffer_size - 1] = 0; + buffer[buffer_size - 1] = '\0'; return 0; } /* int tss2_receive_line */ @@ -337,7 +338,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) && @@ -379,7 +380,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 b5fa4c1e..ff650a8d 100644 --- a/src/ted.c +++ b/src/ted.c @@ -36,8 +36,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H #include @@ -243,7 +243,7 @@ static int ted_config(const char *key, const char *value) { tmp = atoi(value); if (tmp < 0) { - WARNING("ted plugin: Invalid retry count: %i", tmp); + ERROR("ted plugin: Invalid retry count: %i", tmp); return 1; } conf_retries = tmp; diff --git a/src/thermal.c b/src/thermal.c index 959fec64..5f448363 100644 --- a/src/thermal.c +++ b/src/thermal.c @@ -21,9 +21,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_ignorelist.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #if !KERNEL_LINUX #error "This module is for Linux only." diff --git a/src/threshold.c b/src/threshold.c index 9d343630..e74dfc2c 100644 --- a/src/threshold.c +++ b/src/threshold.c @@ -25,9 +25,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" #include "utils_cache.h" #include "utils_threshold.h" @@ -335,22 +335,22 @@ static int ut_report_state(const data_set_t *ds, const value_list_t *vl, n.time = vl->time; - status = snprintf(buf, bufsize, "Host %s, plugin %s", vl->host, vl->plugin); + status = ssnprintf(buf, bufsize, "Host %s, plugin %s", vl->host, vl->plugin); buf += status; bufsize -= status; if (vl->plugin_instance[0] != '\0') { - status = snprintf(buf, bufsize, " (instance %s)", vl->plugin_instance); + status = ssnprintf(buf, bufsize, " (instance %s)", vl->plugin_instance); buf += status; bufsize -= status; } - status = snprintf(buf, bufsize, " type %s", vl->type); + status = ssnprintf(buf, bufsize, " type %s", vl->type); buf += status; bufsize -= status; if (vl->type_instance[0] != '\0') { - status = snprintf(buf, bufsize, " (instance %s)", vl->type_instance); + status = ssnprintf(buf, bufsize, " (instance %s)", vl->type_instance); buf += status; bufsize -= status; } @@ -365,11 +365,12 @@ static int ut_report_state(const data_set_t *ds, const value_list_t *vl, /* Send an okay notification */ if (state == STATE_OKAY) { if (state_old == STATE_MISSING) - snprintf(buf, bufsize, ": Value is no longer missing."); + ssnprintf(buf, bufsize, ": Value is no longer missing."); else - snprintf(buf, bufsize, ": All data sources are within range again. " - "Current value of \"%s\" is %f.", - ds->ds[ds_index].name, values[ds_index]); + ssnprintf(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."); @@ -383,21 +384,22 @@ static int ut_report_state(const data_set_t *ds, const value_list_t *vl, if (th->flags & UT_FLAG_INVERT) { if (!isnan(min) && !isnan(max)) { - snprintf(buf, bufsize, - ": Data source \"%s\" is currently " - "%f. That is within the %s region of %f%s and %f%s.", - ds->ds[ds_index].name, values[ds_index], - (state == STATE_ERROR) ? "failure" : "warning", min, - ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "", max, - ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : ""); + ssnprintf(buf, bufsize, + ": Data source \"%s\" is currently " + "%f. That is within the %s region of %f%s and %f%s.", + ds->ds[ds_index].name, values[ds_index], + (state == STATE_ERROR) ? "failure" : "warning", min, + ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "", max, + ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : ""); } else { - snprintf(buf, bufsize, ": Data source \"%s\" is currently " - "%f. That is %s the %s threshold of %f%s.", - ds->ds[ds_index].name, values[ds_index], - isnan(min) ? "below" : "above", - (state == STATE_ERROR) ? "failure" : "warning", - isnan(min) ? max : min, - ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : ""); + ssnprintf(buf, bufsize, + ": Data source \"%s\" is currently " + "%f. That is %s the %s threshold of %f%s.", + ds->ds[ds_index].name, values[ds_index], + isnan(min) ? "below" : "above", + (state == STATE_ERROR) ? "failure" : "warning", + isnan(min) ? max : min, + ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : ""); } } else if (th->flags & UT_FLAG_PERCENTAGE) { gauge_t value; @@ -416,21 +418,22 @@ static int ut_report_state(const data_set_t *ds, const value_list_t *vl, else value = 100.0 * values[ds_index] / sum; - snprintf(buf, bufsize, - ": Data source \"%s\" is currently " - "%g (%.2f%%). That is %s the %s threshold of %.2f%%.", - ds->ds[ds_index].name, values[ds_index], value, - (value < min) ? "below" : "above", - (state == STATE_ERROR) ? "failure" : "warning", - (value < min) ? min : max); + ssnprintf(buf, bufsize, + ": Data source \"%s\" is currently " + "%g (%.2f%%). That is %s the %s threshold of %.2f%%.", + ds->ds[ds_index].name, values[ds_index], value, + (value < min) ? "below" : "above", + (state == STATE_ERROR) ? "failure" : "warning", + (value < min) ? min : max); } else /* is not inverted */ { - snprintf(buf, bufsize, ": Data source \"%s\" is currently " - "%f. That is %s the %s threshold of %f.", - ds->ds[ds_index].name, values[ds_index], - (values[ds_index] < min) ? "below" : "above", - (state == STATE_ERROR) ? "failure" : "warning", - (values[ds_index] < min) ? min : max); + ssnprintf(buf, bufsize, + ": Data source \"%s\" is currently " + "%f. That is %s the %s threshold of %f.", + ds->ds[ds_index].name, values[ds_index], + (values[ds_index] < min) ? "below" : "above", + (state == STATE_ERROR) ? "failure" : "warning", + (values[ds_index] < min) ? min : max); } } @@ -689,9 +692,9 @@ static int ut_missing(const value_list_t *vl, FORMAT_VL(identifier, sizeof(identifier), vl); NOTIFICATION_INIT_VL(&n, vl); - snprintf(n.message, sizeof(n.message), - "%s has not been updated for %.3f seconds.", identifier, - CDTIME_T_TO_DOUBLE(missing_time)); + ssnprintf(n.message, sizeof(n.message), + "%s has not been updated for %.3f seconds.", identifier, + CDTIME_T_TO_DOUBLE(missing_time)); n.time = now; plugin_dispatch_notification(&n); diff --git a/src/tokyotyrant.c b/src/tokyotyrant.c index aca0a4e3..59b7a342 100644 --- a/src/tokyotyrant.c +++ b/src/tokyotyrant.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include diff --git a/src/turbostat.c b/src/turbostat.c index 4a7af4c7..9465ddc0 100644 --- a/src/turbostat.c +++ b/src/turbostat.c @@ -37,8 +37,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_time.h" #include "msr-index.h" @@ -1434,9 +1434,9 @@ static void free_all_buffers(void) { package_delta = NULL; } -/********************** - * Collectd functions * - **********************/ + /********************** + * Collectd functions * + **********************/ #define DO_OR_GOTO_ERR(something) \ do { \ @@ -1463,7 +1463,7 @@ err: return ret; } -int save_affinity(void) { +static int save_affinity(void) { if (affinity_policy == policy_restore_affinity) { /* Try to save the scheduling affinity, as it will be modified by * get_counters(). @@ -1476,7 +1476,7 @@ int save_affinity(void) { return 0; } -void restore_affinity(void) { +static 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, diff --git a/src/types.db b/src/types.db index 98f6a7cb..6c08936e 100644 --- a/src/types.db +++ b/src/types.db @@ -51,6 +51,8 @@ df used:GAUGE:0:1125899906842623, free:GAUGE:0:112589990684 df_complex value:GAUGE:0:U df_inodes value:GAUGE:0:U dilution_of_precision value:GAUGE:0:U +disk_allocation value:GAUGE:0:U +disk_capacity value:GAUGE:0:U disk_error value:GAUGE:0:U disk_io_time io_time:DERIVE:0:U, weighted_io_time:DERIVE:0:U disk_latency read:GAUGE:0:U, write:GAUGE:0:U @@ -58,6 +60,7 @@ disk_merged read:DERIVE:0:U, write:DERIVE:0:U disk_octets read:DERIVE:0:U, write:DERIVE:0:U disk_ops read:DERIVE:0:U, write:DERIVE:0:U disk_ops_complex value:DERIVE:0:U +disk_physical value:GAUGE:0:U disk_time read:DERIVE:0:U, write:DERIVE:0:U dns_answer value:DERIVE:0:U dns_notify value:DERIVE:0:U diff --git a/src/unixsock.c b/src/unixsock.c index 8c08e18a..6ff54997 100644 --- a/src/unixsock.c +++ b/src/unixsock.c @@ -26,15 +26,15 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" - -#include "utils_cmd_flush.h" -#include "utils_cmd_getthreshold.h" -#include "utils_cmd_getval.h" -#include "utils_cmd_listval.h" -#include "utils_cmd_putnotif.h" -#include "utils_cmd_putval.h" +#include "utils/common/common.h" + +#include "utils/cmds/flush.h" +#include "utils/cmds/getthreshold.h" +#include "utils/cmds/getval.h" +#include "utils/cmds/listval.h" +#include "utils/cmds/putnotif.h" +#include "utils/cmds/putval.h" #include #include diff --git a/src/uptime.c b/src/uptime.c index 43d72e53..0892bda3 100644 --- a/src/uptime.c +++ b/src/uptime.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if KERNEL_LINUX #include @@ -96,7 +96,7 @@ static time_t uptime_get_sys(void) { /* {{{ */ } result = (time_t)info.uptime; -/* #endif KERNEL_LINUX */ + /* #endif KERNEL_LINUX */ #elif HAVE_LIBKSTAT kstat_t *ksp; @@ -136,7 +136,7 @@ static time_t uptime_get_sys(void) { /* {{{ */ } result = time(NULL) - (time_t)knp->value.ui32; -/* #endif HAVE_LIBKSTAT */ + /* #endif HAVE_LIBKSTAT */ #elif HAVE_SYS_SYSCTL_H struct timeval boottv = {0}; @@ -161,7 +161,7 @@ static time_t uptime_get_sys(void) { /* {{{ */ } result = time(NULL) - boottv.tv_sec; -/* #endif HAVE_SYS_SYSCTL_H */ + /* #endif HAVE_SYS_SYSCTL_H */ #elif HAVE_PERFSTAT int status; diff --git a/src/users.c b/src/users.c index 6bc7cc34..768d4881 100644 --- a/src/users.c +++ b/src/users.c @@ -27,8 +27,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_STATGRAB_H #include @@ -71,7 +71,7 @@ static int users_read(void) { endutxent(); users_submit(users); -/* #endif HAVE_GETUTXENT */ + /* #endif HAVE_GETUTXENT */ #elif HAVE_GETUTENT unsigned int users = 0; @@ -89,7 +89,7 @@ static int users_read(void) { endutent(); users_submit(users); -/* #endif HAVE_GETUTENT */ + /* #endif HAVE_GETUTENT */ #elif HAVE_LIBSTATGRAB sg_user_stats *us; @@ -109,7 +109,7 @@ static int users_read(void) { #else us->num_entries); #endif -/* #endif HAVE_LIBSTATGRAB */ + /* #endif HAVE_LIBSTATGRAB */ #else #error "No applicable input method." diff --git a/src/utils/avltree/avltree.c b/src/utils/avltree/avltree.c new file mode 100644 index 00000000..af6efed9 --- /dev/null +++ b/src/utils/avltree/avltree.c @@ -0,0 +1,648 @@ +/** + * collectd - src/utils_avltree.c + * Copyright (C) 2006,2007 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include +#include + +#include "utils/avltree/avltree.h" + +#define BALANCE(n) \ + ((((n)->left == NULL) ? 0 : (n)->left->height) - \ + (((n)->right == NULL) ? 0 : (n)->right->height)) + +/* + * private data types + */ +struct c_avl_node_s { + void *key; + void *value; + + int height; + struct c_avl_node_s *left; + struct c_avl_node_s *right; + struct c_avl_node_s *parent; +}; +typedef struct c_avl_node_s c_avl_node_t; + +struct c_avl_tree_s { + c_avl_node_t *root; + int (*compare)(const void *, const void *); + int size; +}; + +struct c_avl_iterator_s { + c_avl_tree_t *tree; + c_avl_node_t *node; +}; + +/* + * private functions + */ +#if 0 +static void verify_tree (c_avl_node_t *n) +{ + if (n == NULL) + return; + + verify_tree (n->left); + verify_tree (n->right); + + assert ((BALANCE (n) >= -1) && (BALANCE (n) <= 1)); + assert ((n->parent == NULL) || (n->parent->right == n) || (n->parent->left == n)); +} /* void verify_tree */ +#else +#define verify_tree(n) /**/ +#endif + +static void free_node(c_avl_node_t *n) { + if (n == NULL) + return; + + if (n->left != NULL) + free_node(n->left); + if (n->right != NULL) + free_node(n->right); + + free(n); +} + +static int calc_height(c_avl_node_t *n) { + int height_left; + int height_right; + + if (n == NULL) + return 0; + + height_left = (n->left == NULL) ? 0 : n->left->height; + height_right = (n->right == NULL) ? 0 : n->right->height; + + return ((height_left > height_right) ? height_left : height_right) + 1; +} /* int calc_height */ + +static c_avl_node_t *search(c_avl_tree_t *t, const void *key) { + c_avl_node_t *n; + int cmp; + + n = t->root; + while (n != NULL) { + cmp = t->compare(key, n->key); + if (cmp == 0) + return n; + else if (cmp < 0) + n = n->left; + else + n = n->right; + } + + return NULL; +} + +/* (x) (y) + * / \ / \ + * (y) /\ /\ (x) + * / \ /_c\ ==> / a\ / \ + * /\ /\ /____\/\ /\ + * / a\ /_b\ /_b\ /_c\ + * /____\ + */ +static c_avl_node_t *rotate_right(c_avl_tree_t *t, c_avl_node_t *x) { + c_avl_node_t *p; + c_avl_node_t *y; + c_avl_node_t *b; + + assert(x != NULL); + assert(x->left != NULL); + + p = x->parent; + y = x->left; + b = y->right; + + x->left = b; + if (b != NULL) + b->parent = x; + + x->parent = y; + y->right = x; + + y->parent = p; + assert((p == NULL) || (p->left == x) || (p->right == x)); + if (p == NULL) + t->root = y; + else if (p->left == x) + p->left = y; + else + p->right = y; + + x->height = calc_height(x); + y->height = calc_height(y); + + return y; +} /* void rotate_right */ + +/* + * (x) (y) + * / \ / \ + * /\ (y) (x) /\ + * /_a\ / \ ==> / \ / c\ + * /\ /\ /\ /\/____\ + * /_b\ / c\ /_a\ /_b\ + * /____\ + */ +static c_avl_node_t *rotate_left(c_avl_tree_t *t, c_avl_node_t *x) { + c_avl_node_t *p; + c_avl_node_t *y; + c_avl_node_t *b; + + assert(x != NULL); + assert(x->right != NULL); + + p = x->parent; + y = x->right; + b = y->left; + + x->right = b; + if (b != NULL) + b->parent = x; + + x->parent = y; + y->left = x; + + y->parent = p; + assert((p == NULL) || (p->left == x) || (p->right == x)); + if (p == NULL) + t->root = y; + else if (p->left == x) + p->left = y; + else + p->right = y; + + x->height = calc_height(x); + y->height = calc_height(y); + + return y; +} /* void rotate_left */ + +static c_avl_node_t *rotate_left_right(c_avl_tree_t *t, c_avl_node_t *x) { + rotate_left(t, x->left); + return rotate_right(t, x); +} /* void rotate_left_right */ + +static c_avl_node_t *rotate_right_left(c_avl_tree_t *t, c_avl_node_t *x) { + rotate_right(t, x->right); + return rotate_left(t, x); +} /* void rotate_right_left */ + +static void rebalance(c_avl_tree_t *t, c_avl_node_t *n) { + int b_top; + int b_bottom; + + while (n != NULL) { + b_top = BALANCE(n); + assert((b_top >= -2) && (b_top <= 2)); + + if (b_top == -2) { + assert(n->right != NULL); + b_bottom = BALANCE(n->right); + assert((b_bottom >= -1) && (b_bottom <= 1)); + if (b_bottom == 1) + n = rotate_right_left(t, n); + else + n = rotate_left(t, n); + } else if (b_top == 2) { + assert(n->left != NULL); + b_bottom = BALANCE(n->left); + assert((b_bottom >= -1) && (b_bottom <= 1)); + if (b_bottom == -1) + n = rotate_left_right(t, n); + else + n = rotate_right(t, n); + } else { + int height = calc_height(n); + if (height == n->height) + break; + n->height = height; + } + + assert(n->height == calc_height(n)); + + n = n->parent; + } /* while (n != NULL) */ +} /* void rebalance */ + +static c_avl_node_t *c_avl_node_next(c_avl_node_t *n) { + c_avl_node_t *r; /* return node */ + + if (n == NULL) { + return NULL; + } + + /* If we can't descent any further, we have to backtrack to the first + * parent that's bigger than we, i. e. who's _left_ child we are. */ + if (n->right == NULL) { + r = n->parent; + while ((r != NULL) && (r->parent != NULL)) { + if (r->left == n) + break; + n = r; + r = n->parent; + } + + /* n->right == NULL && r == NULL => t is root and has no next + * r->left != n => r->right = n => r->parent == NULL */ + if ((r == NULL) || (r->left != n)) { + assert((r == NULL) || (r->parent == NULL)); + return NULL; + } else { + assert(r->left == n); + return r; + } + } else { + r = n->right; + while (r->left != NULL) + r = r->left; + } + + return r; +} /* c_avl_node_t *c_avl_node_next */ + +static c_avl_node_t *c_avl_node_prev(c_avl_node_t *n) { + c_avl_node_t *r; /* return node */ + + if (n == NULL) { + return NULL; + } + + /* If we can't descent any further, we have to backtrack to the first + * parent that's smaller than we, i. e. who's _right_ child we are. */ + if (n->left == NULL) { + r = n->parent; + while ((r != NULL) && (r->parent != NULL)) { + if (r->right == n) + break; + n = r; + r = n->parent; + } + + /* n->left == NULL && r == NULL => t is root and has no next + * r->right != n => r->left = n => r->parent == NULL */ + if ((r == NULL) || (r->right != n)) { + assert((r == NULL) || (r->parent == NULL)); + return NULL; + } else { + assert(r->right == n); + return r; + } + } else { + r = n->left; + while (r->right != NULL) + r = r->right; + } + + return r; +} /* c_avl_node_t *c_avl_node_prev */ + +static int _remove(c_avl_tree_t *t, c_avl_node_t *n) { + assert((t != NULL) && (n != NULL)); + + if ((n->left != NULL) && (n->right != NULL)) { + c_avl_node_t *r; /* replacement node */ + if (BALANCE(n) > 0) /* left subtree is higher */ + { + assert(n->left != NULL); + r = c_avl_node_prev(n); + + } else /* right subtree is higher */ + { + assert(n->right != NULL); + r = c_avl_node_next(n); + } + + assert((r->left == NULL) || (r->right == NULL)); + + /* copy content */ + n->key = r->key; + n->value = r->value; + + n = r; + } + + assert((n->left == NULL) || (n->right == NULL)); + + if ((n->left == NULL) && (n->right == NULL)) { + /* Deleting a leave is easy */ + if (n->parent == NULL) { + assert(t->root == n); + t->root = NULL; + } else { + assert((n->parent->left == n) || (n->parent->right == n)); + if (n->parent->left == n) + n->parent->left = NULL; + else + n->parent->right = NULL; + + rebalance(t, n->parent); + } + + free_node(n); + } else if (n->left == NULL) { + assert(BALANCE(n) == -1); + assert((n->parent == NULL) || (n->parent->left == n) || + (n->parent->right == n)); + if (n->parent == NULL) { + assert(t->root == n); + t->root = n->right; + } else if (n->parent->left == n) { + n->parent->left = n->right; + } else { + n->parent->right = n->right; + } + n->right->parent = n->parent; + + if (n->parent != NULL) + rebalance(t, n->parent); + + n->right = NULL; + free_node(n); + } else if (n->right == NULL) { + assert(BALANCE(n) == 1); + assert((n->parent == NULL) || (n->parent->left == n) || + (n->parent->right == n)); + if (n->parent == NULL) { + assert(t->root == n); + t->root = n->left; + } else if (n->parent->left == n) { + n->parent->left = n->left; + } else { + n->parent->right = n->left; + } + n->left->parent = n->parent; + + if (n->parent != NULL) + rebalance(t, n->parent); + + n->left = NULL; + free_node(n); + } else { + assert(0); + } + + return 0; +} /* void *_remove */ + +/* + * public functions + */ +c_avl_tree_t *c_avl_create(int (*compare)(const void *, const void *)) { + c_avl_tree_t *t; + + if (compare == NULL) + return NULL; + + if ((t = malloc(sizeof(*t))) == NULL) + return NULL; + + t->root = NULL; + t->compare = compare; + t->size = 0; + + return t; +} + +void c_avl_destroy(c_avl_tree_t *t) { + if (t == NULL) + return; + free_node(t->root); + free(t); +} + +int c_avl_insert(c_avl_tree_t *t, void *key, void *value) { + c_avl_node_t *new; + c_avl_node_t *nptr; + int cmp; + + if ((new = malloc(sizeof(*new))) == NULL) + return -1; + + new->key = key; + new->value = value; + new->height = 1; + new->left = NULL; + new->right = NULL; + + if (t->root == NULL) { + new->parent = NULL; + t->root = new; + t->size = 1; + return 0; + } + + nptr = t->root; + while (42) { + cmp = t->compare(nptr->key, new->key); + if (cmp == 0) { + free_node(new); + return 1; + } else if (cmp < 0) { + /* nptr < new */ + if (nptr->right == NULL) { + nptr->right = new; + new->parent = nptr; + rebalance(t, nptr); + break; + } else { + nptr = nptr->right; + } + } else /* if (cmp > 0) */ + { + /* nptr > new */ + if (nptr->left == NULL) { + nptr->left = new; + new->parent = nptr; + rebalance(t, nptr); + break; + } else { + nptr = nptr->left; + } + } + } /* while (42) */ + + verify_tree(t->root); + ++t->size; + return 0; +} /* int c_avl_insert */ + +int c_avl_remove(c_avl_tree_t *t, const void *key, void **rkey, void **rvalue) { + c_avl_node_t *n; + int status; + + assert(t != NULL); + + n = search(t, key); + if (n == NULL) + return -1; + + if (rkey != NULL) + *rkey = n->key; + if (rvalue != NULL) + *rvalue = n->value; + + status = _remove(t, n); + verify_tree(t->root); + --t->size; + return status; +} /* void *c_avl_remove */ + +int c_avl_get(c_avl_tree_t *t, const void *key, void **value) { + c_avl_node_t *n; + + assert(t != NULL); + + n = search(t, key); + if (n == NULL) + return -1; + + if (value != NULL) + *value = n->value; + + return 0; +} + +int c_avl_pick(c_avl_tree_t *t, void **key, void **value) { + c_avl_node_t *n; + c_avl_node_t *p; + + assert(t != NULL); + + if ((key == NULL) || (value == NULL)) + return -1; + if (t->root == NULL) + return -1; + + n = t->root; + while ((n->left != NULL) || (n->right != NULL)) { + if (n->left == NULL) { + n = n->right; + continue; + } else if (n->right == NULL) { + n = n->left; + continue; + } + + if (n->left->height > n->right->height) + n = n->left; + else + n = n->right; + } + + p = n->parent; + if (p == NULL) + t->root = NULL; + else if (p->left == n) + p->left = NULL; + else + p->right = NULL; + + *key = n->key; + *value = n->value; + + free_node(n); + --t->size; + rebalance(t, p); + + return 0; +} /* int c_avl_pick */ + +c_avl_iterator_t *c_avl_get_iterator(c_avl_tree_t *t) { + c_avl_iterator_t *iter; + + if (t == NULL) + return NULL; + + iter = calloc(1, sizeof(*iter)); + if (iter == NULL) + return NULL; + iter->tree = t; + + return iter; +} /* c_avl_iterator_t *c_avl_get_iterator */ + +int c_avl_iterator_next(c_avl_iterator_t *iter, void **key, void **value) { + c_avl_node_t *n; + + if ((iter == NULL) || (key == NULL) || (value == NULL)) + return -1; + + if (iter->node == NULL) { + for (n = iter->tree->root; n != NULL; n = n->left) + if (n->left == NULL) + break; + iter->node = n; + } else { + n = c_avl_node_next(iter->node); + } + + if (n == NULL) + return -1; + + iter->node = n; + *key = n->key; + *value = n->value; + + return 0; +} /* int c_avl_iterator_next */ + +int c_avl_iterator_prev(c_avl_iterator_t *iter, void **key, void **value) { + c_avl_node_t *n; + + if ((iter == NULL) || (key == NULL) || (value == NULL)) + return -1; + + if (iter->node == NULL) { + for (n = iter->tree->root; n != NULL; n = n->right) + if (n->right == NULL) + break; + iter->node = n; + } else { + n = c_avl_node_prev(iter->node); + } + + if (n == NULL) + return -1; + + iter->node = n; + *key = n->key; + *value = n->value; + + return 0; +} /* int c_avl_iterator_prev */ + +void c_avl_iterator_destroy(c_avl_iterator_t *iter) { free(iter); } + +int c_avl_size(c_avl_tree_t *t) { + if (t == NULL) + return 0; + return t->size; +} diff --git a/src/utils/avltree/avltree.h b/src/utils/avltree/avltree.h new file mode 100644 index 00000000..3f52b931 --- /dev/null +++ b/src/utils/avltree/avltree.h @@ -0,0 +1,169 @@ +/** + * collectd - src/utils_avltree.h + * Copyright (C) 2006,2007 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_AVLTREE_H +#define UTILS_AVLTREE_H 1 + +struct c_avl_tree_s; +typedef struct c_avl_tree_s c_avl_tree_t; + +struct c_avl_iterator_s; +typedef struct c_avl_iterator_s c_avl_iterator_t; + +/* + * NAME + * c_avl_create + * + * DESCRIPTION + * Allocates a new AVL-tree. + * + * PARAMETERS + * `compare' The function-pointer `compare' is used to compare two keys. It + * has to return less than zero if its first argument is smaller + * then the second argument, more than zero if the first argument + * is bigger than the second argument and zero if they are equal. + * If your keys are char-pointers, you can use the `strcmp' + * function from the libc here. + * + * RETURN VALUE + * A c_avl_tree_t-pointer upon success or NULL upon failure. + */ +c_avl_tree_t *c_avl_create(int (*compare)(const void *, const void *)); + +/* + * NAME + * c_avl_destroy + * + * DESCRIPTION + * Deallocates an AVL-tree. Stored value- and key-pointer are lost, but of + * course not freed. + */ +void c_avl_destroy(c_avl_tree_t *t); + +/* + * NAME + * c_avl_insert + * + * DESCRIPTION + * Stores the key-value-pair in the AVL-tree pointed to by `t'. + * + * PARAMETERS + * `t' AVL-tree to store the data in. + * `key' Key used to store the value under. This is used to get back to + * the value again. The pointer is stored in an internal structure + * and _not_ copied. So the memory pointed to may _not_ be freed + * before this entry is removed. You can use the `rkey' argument + * to `avl_remove' to get the original pointer back and free it. + * `value' Value to be stored. + * + * RETURN VALUE + * Zero upon success, non-zero otherwise. It's less than zero if an error + * occurred or greater than zero if the key is already stored in the tree. + */ +int c_avl_insert(c_avl_tree_t *t, void *key, void *value); + +/* + * NAME + * c_avl_remove + * + * DESCRIPTION + * Removes a key-value-pair from the tree t. The stored key and value may be + * returned in `rkey' and `rvalue'. + * + * PARAMETERS + * `t' AVL-tree to remove key-value-pair from. + * `key' Key to identify the entry. + * `rkey' Pointer to a pointer in which to store the key. May be NULL. + * Since the `key' pointer is not copied when creating an entry, + * the pointer may not be available anymore from outside the tree. + * You can use this argument to get the actual pointer back and + * free the memory pointed to by it. + * `rvalue' Pointer to a pointer in which to store the value. May be NULL. + * + * RETURN VALUE + * Zero upon success or non-zero if the key isn't found in the tree. + */ +int c_avl_remove(c_avl_tree_t *t, const void *key, void **rkey, void **rvalue); + +/* + * NAME + * c_avl_get + * + * DESCRIPTION + * Retrieve the `value' belonging to `key'. + * + * PARAMETERS + * `t' AVL-tree to get the value from. + * `key' Key to identify the entry. + * `value' Pointer to a pointer in which to store the value. May be NULL. + * + * RETURN VALUE + * Zero upon success or non-zero if the key isn't found in the tree. + */ +int c_avl_get(c_avl_tree_t *t, const void *key, void **value); + +/* + * NAME + * c_avl_pick + * + * DESCRIPTION + * Remove a (pseudo-)random element from the tree and return its `key' and + * `value'. Entries are not returned in any particular order. This function + * is intended for cache-flushes that don't care about the order but simply + * want to remove all elements, one at a time. + * + * PARAMETERS + * `t' AVL-tree to get the value from. + * `key' Pointer to a pointer in which to store the key. + * `value' Pointer to a pointer in which to store the value. + * + * RETURN VALUE + * Zero upon success or non-zero if the tree is empty or key or value is + * NULL. + */ +int c_avl_pick(c_avl_tree_t *t, void **key, void **value); + +c_avl_iterator_t *c_avl_get_iterator(c_avl_tree_t *t); +int c_avl_iterator_next(c_avl_iterator_t *iter, void **key, void **value); +int c_avl_iterator_prev(c_avl_iterator_t *iter, void **key, void **value); +void c_avl_iterator_destroy(c_avl_iterator_t *iter); + +/* + * NAME + * c_avl_size + * + * DESCRIPTION + * Return the size (number of nodes) of the specified tree. + * + * PARAMETERS + * `t' AVL-tree to get the size of. + * + * RETURN VALUE + * Number of nodes in the tree, 0 if the tree is empty or NULL. + */ +int c_avl_size(c_avl_tree_t *t); + +#endif /* UTILS_AVLTREE_H */ diff --git a/src/utils/avltree/avltree_test.c b/src/utils/avltree/avltree_test.c new file mode 100644 index 00000000..8cbcb13c --- /dev/null +++ b/src/utils/avltree/avltree_test.c @@ -0,0 +1,180 @@ +/** + * collectd - src/tests/test_utils_avltree.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "collectd.h" +#include "utils/common/common.h" /* STATIC_ARRAY_SIZE */ + +#include "testing.h" +#include "utils/avltree/avltree.h" + +static int compare_total_count; + +#define RESET_COUNTS() \ + do { \ + compare_total_count = 0; \ + } while (0) + +static int compare_callback(void const *v0, void const *v1) { + assert(v0 != NULL); + assert(v1 != NULL); + + compare_total_count++; + return strcmp(v0, v1); +} + +struct kv_t { + char *key; + char *value; +}; + +static int kv_compare(const void *a_ptr, const void *b_ptr) { + return strcmp(((struct kv_t *)a_ptr)->key, ((struct kv_t *)b_ptr)->key); +} + +DEF_TEST(success) { + struct kv_t cases[] = { + {"Eeph7chu", "vai1reiV"}, {"igh3Paiz", "teegh1Ee"}, + {"caip6Uu8", "ooteQu8n"}, {"Aech6vah", "AijeeT0l"}, + {"Xah0et2L", "gah8Taep"}, {"BocaeB8n", "oGaig8io"}, + {"thai8AhM", "ohjeFo3f"}, {"ohth6ieC", "hoo8ieWo"}, + {"aej7Woow", "phahuC2s"}, {"Hai8ier2", "Yie6eimi"}, + {"phuXi3Li", "JaiF7ieb"}, {"Shaig5ef", "aihi5Zai"}, + {"voh6Aith", "Oozaeto0"}, {"zaiP5kie", "seep5veM"}, + {"pae7ba7D", "chie8Ojo"}, {"Gou2ril3", "ouVoo0ha"}, + {"lo3Thee3", "ahDu4Zuj"}, {"Rah8kohv", "ieShoc7E"}, + {"ieN5engi", "Aevou1ah"}, {"ooTe4OhP", "aingai5Y"}, + }; + + struct kv_t sorted_cases[STATIC_ARRAY_SIZE(cases)]; + memcpy(sorted_cases, cases, sizeof(cases)); + qsort(sorted_cases, STATIC_ARRAY_SIZE(cases), sizeof(struct kv_t), + kv_compare); + + c_avl_tree_t *t; + + RESET_COUNTS(); + CHECK_NOT_NULL(t = c_avl_create(compare_callback)); + + /* insert */ + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { + char *key; + char *value; + + CHECK_NOT_NULL(key = strdup(cases[i].key)); + CHECK_NOT_NULL(value = strdup(cases[i].value)); + + CHECK_ZERO(c_avl_insert(t, key, value)); + EXPECT_EQ_INT((int)(i + 1), c_avl_size(t)); + } + + /* Key already exists. */ + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) + EXPECT_EQ_INT(1, c_avl_insert(t, cases[i].key, cases[i].value)); + + /* get */ + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { + char *value_ret = NULL; + + CHECK_ZERO(c_avl_get(t, cases[i].key, (void *)&value_ret)); + EXPECT_EQ_STR(cases[i].value, value_ret); + } + + /* iterate forward */ + { + c_avl_iterator_t *iter = c_avl_get_iterator(t); + char *key; + char *value; + size_t i = 0; + while (c_avl_iterator_next(iter, (void **)&key, (void **)&value) == 0) { + EXPECT_EQ_STR(sorted_cases[i].key, key); + EXPECT_EQ_STR(sorted_cases[i].value, value); + i++; + } + c_avl_iterator_destroy(iter); + EXPECT_EQ_INT(i, STATIC_ARRAY_SIZE(cases)); + } + + /* iterate backward */ + { + c_avl_iterator_t *iter = c_avl_get_iterator(t); + char *key; + char *value; + size_t i = 0; + while (c_avl_iterator_prev(iter, (void **)&key, (void **)&value) == 0) { + EXPECT_EQ_STR(sorted_cases[STATIC_ARRAY_SIZE(cases) - 1 - i].key, key); + EXPECT_EQ_STR(sorted_cases[STATIC_ARRAY_SIZE(cases) - 1 - i].value, + value); + i++; + } + c_avl_iterator_destroy(iter); + EXPECT_EQ_INT(i, STATIC_ARRAY_SIZE(cases)); + } + + /* remove half */ + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases) / 2; i++) { + char *key = NULL; + char *value = NULL; + + int expected_size = (int)(STATIC_ARRAY_SIZE(cases) - (i + 1)); + + CHECK_ZERO(c_avl_remove(t, cases[i].key, (void *)&key, (void *)&value)); + + EXPECT_EQ_STR(cases[i].key, key); + EXPECT_EQ_STR(cases[i].value, value); + + free(key); + free(value); + + EXPECT_EQ_INT(expected_size, c_avl_size(t)); + } + + /* pick the other half */ + for (size_t i = STATIC_ARRAY_SIZE(cases) / 2; i < STATIC_ARRAY_SIZE(cases); + i++) { + char *key = NULL; + char *value = NULL; + + int expected_size = (int)(STATIC_ARRAY_SIZE(cases) - (i + 1)); + + EXPECT_EQ_INT(expected_size + 1, c_avl_size(t)); + EXPECT_EQ_INT(0, c_avl_pick(t, (void *)&key, (void *)&value)); + + free(key); + free(value); + + EXPECT_EQ_INT(expected_size, c_avl_size(t)); + } + + c_avl_destroy(t); + + return 0; +} + +int main(void) { + RUN_TEST(success); + + END_TEST; +} diff --git a/src/utils/cmds/cmds.c b/src/utils/cmds/cmds.c new file mode 100644 index 00000000..036cefa1 --- /dev/null +++ b/src/utils/cmds/cmds.c @@ -0,0 +1,308 @@ +/** + * collectd - src/utils_cmds.c + * Copyright (C) 2008 Florian Forster + * Copyright (C) 2016 Sebastian 'tokkee' Harl + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + * Sebastian 'tokkee' Harl + **/ + +#include "utils/cmds/cmds.h" +#include "utils/cmds/flush.h" +#include "utils/cmds/getval.h" +#include "utils/cmds/listval.h" +#include "utils/cmds/parse_option.h" +#include "utils/cmds/putval.h" +#include "utils/common/common.h" + +#include +#include + +static cmd_options_t default_options = { + /* identifier_default_host = */ NULL, +}; + +/* + * private helper functions + */ + +static cmd_status_t cmd_split(char *buffer, size_t *ret_len, char ***ret_fields, + cmd_error_handler_t *err) { + char *field; + bool in_field, in_quotes; + + size_t estimate, len; + char **fields; + + estimate = 0; + in_field = false; + for (char *string = buffer; *string != '\0'; ++string) { + /* Make a quick worst-case estimate of the number of fields by + * counting spaces and ignoring quotation marks. */ + if (!isspace((int)*string)) { + if (!in_field) { + estimate++; + in_field = true; + } + } else { + in_field = false; + } + } + + /* fields will be NULL-terminated */ + fields = malloc((estimate + 1) * sizeof(*fields)); + if (fields == NULL) { + cmd_error(CMD_ERROR, err, "malloc failed."); + return CMD_ERROR; + } + +#define END_FIELD() \ + do { \ + *field = '\0'; \ + field = NULL; \ + in_field = false; \ + } while (0) +#define NEW_FIELD() \ + do { \ + field = string; \ + in_field = true; \ + assert(len < estimate); \ + fields[len] = field; \ + field++; \ + len++; \ + } while (0) + + len = 0; + field = NULL; + in_field = false; + in_quotes = false; + for (char *string = buffer; *string != '\0'; string++) { + if (isspace((int)string[0])) { + if (!in_quotes) { + if (in_field) + END_FIELD(); + + /* skip space */ + continue; + } + } else if (string[0] == '"') { + /* Note: Two consecutive quoted fields not separated by space are + * treated as different fields. This is the collectd 5.x behavior + * around splitting fields. */ + + if (in_quotes) { + /* end of quoted field */ + if (!in_field) /* empty quoted string */ + NEW_FIELD(); + END_FIELD(); + in_quotes = false; + continue; + } + + in_quotes = true; + /* if (! in_field): add new field on next iteration + * else: quoted string following an unquoted string (one field) + * in either case: skip quotation mark */ + continue; + } else if ((string[0] == '\\') && in_quotes) { + /* Outside of quotes, a backslash is a regular character (mostly + * for backward compatibility). */ + + if (string[1] == '\0') { + free(fields); + cmd_error(CMD_PARSE_ERROR, err, "Backslash at end of string."); + return CMD_PARSE_ERROR; + } + + /* un-escape the next character; skip backslash */ + string++; + } + + if (!in_field) + NEW_FIELD(); + else { + *field = string[0]; + field++; + } + } + + if (in_quotes) { + free(fields); + cmd_error(CMD_PARSE_ERROR, err, "Unterminated quoted string."); + return CMD_PARSE_ERROR; + } + +#undef NEW_FIELD +#undef END_FIELD + + fields[len] = NULL; + if (ret_len != NULL) + *ret_len = len; + if (ret_fields != NULL) + *ret_fields = fields; + else + free(fields); + return CMD_OK; +} /* int cmd_split */ + +/* + * public API + */ + +void cmd_error(cmd_status_t status, cmd_error_handler_t *err, + const char *format, ...) { + va_list ap; + + if ((err == NULL) || (err->cb == NULL)) + return; + + va_start(ap, format); + err->cb(err->ud, status, format, ap); + va_end(ap); +} /* void cmd_error */ + +cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd, + const cmd_options_t *opts, cmd_error_handler_t *err) { + char *command = NULL; + cmd_status_t status; + + if ((argc < 1) || (argv == NULL) || (ret_cmd == NULL)) { + errno = EINVAL; + cmd_error(CMD_ERROR, err, "Missing command."); + return CMD_ERROR; + } + + if (opts == NULL) + opts = &default_options; + + memset(ret_cmd, 0, sizeof(*ret_cmd)); + command = argv[0]; + if (strcasecmp("FLUSH", command) == 0) { + ret_cmd->type = CMD_FLUSH; + status = + cmd_parse_flush(argc - 1, argv + 1, &ret_cmd->cmd.flush, opts, err); + } else if (strcasecmp("GETVAL", command) == 0) { + ret_cmd->type = CMD_GETVAL; + status = + 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, opts, err); + } else if (strcasecmp("PUTVAL", command) == 0) { + ret_cmd->type = CMD_PUTVAL; + status = + cmd_parse_putval(argc - 1, argv + 1, &ret_cmd->cmd.putval, opts, err); + } else { + ret_cmd->type = CMD_UNKNOWN; + cmd_error(CMD_UNKNOWN_COMMAND, err, "Unknown command `%s'.", command); + return CMD_UNKNOWN_COMMAND; + } + + if (status != CMD_OK) + ret_cmd->type = CMD_UNKNOWN; + return status; +} /* cmd_status_t cmd_parsev */ + +cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts, + cmd_error_handler_t *err) { + char **fields = NULL; + size_t fields_num = 0; + cmd_status_t status; + + if ((status = cmd_split(buffer, &fields_num, &fields, err)) != CMD_OK) + return status; + + status = cmd_parsev(fields_num, fields, ret_cmd, opts, err); + free(fields); + return status; +} /* cmd_status_t cmd_parse */ + +void cmd_destroy(cmd_t *cmd) { + if (cmd == NULL) + return; + + switch (cmd->type) { + case CMD_UNKNOWN: + /* nothing to do */ + break; + case CMD_FLUSH: + cmd_destroy_flush(&cmd->cmd.flush); + break; + case CMD_GETVAL: + cmd_destroy_getval(&cmd->cmd.getval); + break; + case CMD_LISTVAL: + break; + case CMD_PUTVAL: + cmd_destroy_putval(&cmd->cmd.putval); + break; + } +} /* void cmd_destroy */ + +cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value, + cmd_error_handler_t *err) { + char *key, *value; + + if (field == NULL) { + errno = EINVAL; + cmd_error(CMD_ERROR, err, "Invalid argument to cmd_parse_option."); + return CMD_ERROR; + } + key = value = field; + + /* Look for the equal sign. */ + while (isalnum((int)value[0]) || (value[0] == '_') || (value[0] == ':')) + value++; + if ((value[0] != '=') || (value == key)) { + /* Whether this is a fatal error is up to the caller. */ + return CMD_NO_OPTION; + } + *value = '\0'; + value++; + + if (ret_key != NULL) + *ret_key = key; + if (ret_value != NULL) + *ret_value = value; + + return CMD_OK; +} /* cmd_status_t cmd_parse_option */ + +void cmd_error_fh(void *ud, cmd_status_t status, const char *format, + va_list ap) { + FILE *fh = ud; + int code = -1; + char buf[1024]; + + if (status == CMD_OK) + code = 0; + + vsnprintf(buf, sizeof(buf), format, ap); + buf[sizeof(buf) - 1] = '\0'; + if (fprintf(fh, "%i %s\n", code, buf) < 0) { + WARNING("utils_cmds: failed to write to file-handle #%i: %s", fileno(fh), + STRERRNO); + return; + } + + fflush(fh); +} /* void cmd_error_fh */ diff --git a/src/utils/cmds/cmds.h b/src/utils/cmds/cmds.h new file mode 100644 index 00000000..f3882f54 --- /dev/null +++ b/src/utils/cmds/cmds.h @@ -0,0 +1,209 @@ +/** + * collectd - src/utils_cmds.h + * Copyright (C) 2016 Sebastian 'tokkee' Harl + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Sebastian 'tokkee' Harl + **/ + +#ifndef UTILS_CMDS_H +#define UTILS_CMDS_H 1 + +#include "plugin.h" + +#include + +typedef enum { + CMD_UNKNOWN = 0, + CMD_FLUSH = 1, + CMD_GETVAL = 2, + CMD_LISTVAL = 3, + 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" + +typedef struct { + double timeout; + + char **plugins; + size_t plugins_num; + identifier_t *identifiers; + size_t identifiers_num; +} cmd_flush_t; + +typedef struct { + char *raw_identifier; + identifier_t identifier; +} cmd_getval_t; + +typedef struct { + /* The raw identifier as provided by the user. */ + char *raw_identifier; + + /* An array of the fully parsed identifier and all value lists, and their + * options as provided by the user. */ + value_list_t *vl; + size_t vl_num; +} cmd_putval_t; + +/* + * NAME + * cmd_t + * + * DESCRIPTION + * The representation of a fully parsed command. + */ +typedef struct { + cmd_type_t type; + union { + cmd_flush_t flush; + cmd_getval_t getval; + cmd_putval_t putval; + } cmd; +} cmd_t; + +/* + * NAME + * cmd_options_t + * + * DESCRIPTIONS + * Optional settings for tuning the parser behavior. + */ +typedef struct { + /* identifier_default_host: If non-NULL, the hostname is optional and will + * default to the specified value. */ + char *identifier_default_host; +} cmd_options_t; + +/* + * NAME + * cmd_status_t + * + * DESCRIPTION + * Status codes describing the parse result. + */ +typedef enum { + CMD_OK = 0, + CMD_ERROR = -1, + CMD_PARSE_ERROR = -2, + CMD_UNKNOWN_COMMAND = -3, + + /* Not necessarily fatal errors. */ + CMD_NO_OPTION = 1, +} cmd_status_t; + +/* + * NAME + * cmd_error_handler_t + * + * DESCRIPTION + * An error handler describes a callback to be invoked when the parser + * encounters an error. The user data pointer will be passed to the callback + * as the first argument. + */ +typedef struct { + void (*cb)(void *, cmd_status_t, const char *, va_list); + void *ud; +} cmd_error_handler_t; + +/* + * NAME: + * cmd_error + * + * DESCRIPTION + * Reports an error via the specified error handler (if set). + */ +void cmd_error(cmd_status_t status, cmd_error_handler_t *err, + const char *format, ...); + +/* + * NAME + * cmd_parse + * + * DESCRIPTION + * Parse a command string and populate a command object. + * + * PARAMETERS + * `buffer' The command string to be parsed. + * `ret_cmd' The parse result will be stored at this location. + * `opts' Parser options. If NULL, defaults will be used. + * `err' An optional error handler to invoke on error. + * + * RETURN VALUE + * CMD_OK on success or the respective error code otherwise. + */ +cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts, + cmd_error_handler_t *err); + +cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd, + const cmd_options_t *opts, cmd_error_handler_t *err); + +void cmd_destroy(cmd_t *cmd); + +/* + * NAME + * cmd_parse_option + * + * DESCRIPTION + * Parses a command option which must be of the form: + * name=value with \ and spaces + * + * PARAMETERS + * `field' The parsed input field with any quotes removed and special + * characters unescaped. + * `ret_key' The parsed key will be stored at this location. + * `ret_value' The parsed value will be stored at this location. + * + * RETURN VALUE + * CMD_OK on success or an error code otherwise. + * CMD_NO_OPTION if `field' does not represent an option at all (missing + * equal sign). + */ +cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value, + cmd_error_handler_t *err); + +/* + * NAME + * cmd_error_fh + * + * DESCRIPTION + * An error callback writing the message to an open file handle using the + * format expected by the unixsock or exec plugins. + * + * PARAMETERS + * `ud' Error handler user-data pointer. This must be an open + * file-handle (FILE *). + * `status' The error status code. + * `format' Printf-style format string. + * `ap' Variable argument list providing the arguments for the format + * string. + */ +void cmd_error_fh(void *ud, cmd_status_t status, const char *format, + va_list ap); + +#endif /* UTILS_CMDS_H */ diff --git a/src/utils/cmds/cmds_test.c b/src/utils/cmds/cmds_test.c new file mode 100644 index 00000000..edbf5c95 --- /dev/null +++ b/src/utils/cmds/cmds_test.c @@ -0,0 +1,324 @@ +/** + * collectd - src/tests/utils_cmds_test.c + * Copyright (C) 2016 Sebastian 'tokkee' Harl + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Sebastian 'tokkee' Harl + **/ + +// clang-format off +/* + * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris + * See Github Issue #3193 for details + */ +#include "utils/common/common.h" +#include "testing.h" +#include "utils/cmds/cmds.h" +// clang-format on + +static void error_cb(void *ud, cmd_status_t status, const char *format, + va_list ap) { + if (status == CMD_OK) + return; + + printf("ERROR[%d]: ", status); + vprintf(format, ap); + printf("\n"); + fflush(stdout); +} /* void error_cb */ + +static cmd_options_t default_host_opts = { + /* identifier_default_host = */ "dummy-host", +}; + +static struct { + char *input; + cmd_options_t *opts; + cmd_status_t expected_status; + cmd_type_t expected_type; +} parse_data[] = { + /* Valid FLUSH commands. */ + { + "FLUSH", + NULL, + CMD_OK, + CMD_FLUSH, + }, + { + "FLUSH identifier=myhost/magic/MAGIC", + NULL, + CMD_OK, + CMD_FLUSH, + }, + { + "FLUSH identifier=magic/MAGIC", + &default_host_opts, + CMD_OK, + CMD_FLUSH, + }, + { + "FLUSH timeout=123 plugin=\"A\"", + NULL, + CMD_OK, + CMD_FLUSH, + }, + /* Invalid FLUSH commands. */ + { + /* Missing hostname; no default. */ + "FLUSH identifier=magic/MAGIC", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + /* Missing 'identifier' key. */ + "FLUSH myhost/magic/MAGIC", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + /* Invalid timeout. */ + "FLUSH timeout=A", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + /* Invalid identifier. */ + "FLUSH identifier=invalid", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + /* Invalid option. */ + "FLUSH invalid=option", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + + /* Valid GETVAL commands. */ + { + "GETVAL myhost/magic/MAGIC", + NULL, + CMD_OK, + CMD_GETVAL, + }, + { + "GETVAL magic/MAGIC", + &default_host_opts, + CMD_OK, + CMD_GETVAL, + }, + + /* Invalid GETVAL commands. */ + { + "GETVAL magic/MAGIC", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + "GETVAL", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + "GETVAL invalid", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + + /* Valid LISTVAL commands. */ + { + "LISTVAL", + NULL, + CMD_OK, + CMD_LISTVAL, + }, + + /* Invalid LISTVAL commands. */ + { + "LISTVAL invalid", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + + /* Valid PUTVAL commands. */ + { + "PUTVAL magic/MAGIC N:42", + &default_host_opts, + CMD_OK, + CMD_PUTVAL, + }, + { + "PUTVAL myhost/magic/MAGIC N:42", + NULL, + CMD_OK, + CMD_PUTVAL, + }, + { + "PUTVAL myhost/magic/MAGIC 1234:42", + NULL, + CMD_OK, + CMD_PUTVAL, + }, + { + "PUTVAL myhost/magic/MAGIC 1234:42 2345:23", + NULL, + CMD_OK, + CMD_PUTVAL, + }, + { + "PUTVAL myhost/magic/MAGIC interval=2 1234:42", + NULL, + CMD_OK, + CMD_PUTVAL, + }, + { + "PUTVAL myhost/magic/MAGIC interval=2 1234:42 interval=5 2345:23", + NULL, + CMD_OK, + CMD_PUTVAL, + }, + + /* Invalid PUTVAL commands. */ + { + "PUTVAL magic/MAGIC N:42", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + "PUTVAL", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + "PUTVAL invalid N:42", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + "PUTVAL myhost/magic/MAGIC A:42", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + "PUTVAL myhost/magic/MAGIC 1234:A", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + "PUTVAL myhost/magic/MAGIC", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + "PUTVAL 1234:A", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { + "PUTVAL myhost/magic/UNKNOWN 1234:42", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + /* + * As of collectd 5.x, PUTVAL accepts invalid options. + { + "PUTVAL myhost/magic/MAGIC invalid=2 1234:42", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + */ + + /* Invalid commands. */ + { + "INVALID", + NULL, + CMD_UNKNOWN_COMMAND, + CMD_UNKNOWN, + }, + { + "INVALID interval=2", + NULL, + CMD_UNKNOWN_COMMAND, + CMD_UNKNOWN, + }, +}; + +DEF_TEST(parse) { + cmd_error_handler_t err = {error_cb, NULL}; + int test_result = 0; + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(parse_data); i++) { + char *input = strdup(parse_data[i].input); + + char description[1024]; + cmd_status_t status; + cmd_t cmd; + + bool result; + + memset(&cmd, 0, sizeof(cmd)); + + status = cmd_parse(input, &cmd, parse_data[i].opts, &err); + ssnprintf(description, sizeof(description), + "cmd_parse (\"%s\", opts=%p) = " + "%d (type=%d [%s]); want %d " + "(type=%d [%s])", + parse_data[i].input, parse_data[i].opts, status, cmd.type, + CMD_TO_STRING(cmd.type), parse_data[i].expected_status, + parse_data[i].expected_type, + CMD_TO_STRING(parse_data[i].expected_type)); + result = (status == parse_data[i].expected_status) && + (cmd.type == parse_data[i].expected_type); + LOG(result, description); + + /* Run all tests before failing. */ + if (!result) + test_result = -1; + + cmd_destroy(&cmd); + free(input); + } + + return test_result; +} + +int main(int argc, char **argv) { + RUN_TEST(parse); + END_TEST; +} diff --git a/src/utils/cmds/flush.c b/src/utils/cmds/flush.c new file mode 100644 index 00000000..d080d481 --- /dev/null +++ b/src/utils/cmds/flush.c @@ -0,0 +1,177 @@ +/** + * collectd - src/utils_cmd_flush.c + * Copyright (C) 2008, 2016 Sebastian Harl + * Copyright (C) 2008 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Sebastian "tokkee" Harl + * Florian "octo" Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/cmds/flush.h" +#include "utils/common/common.h" + +cmd_status_t cmd_parse_flush(size_t argc, char **argv, cmd_flush_t *ret_flush, + const cmd_options_t *opts, + cmd_error_handler_t *err) { + + if ((ret_flush == NULL) || (opts == NULL)) { + errno = EINVAL; + cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_flush."); + return CMD_ERROR; + } + + for (size_t i = 0; i < argc; i++) { + char *opt_key; + char *opt_value; + int status; + + opt_key = NULL; + opt_value = NULL; + status = cmd_parse_option(argv[i], &opt_key, &opt_value, err); + if (status != 0) { + if (status == CMD_NO_OPTION) + cmd_error(CMD_PARSE_ERROR, err, "Invalid option string `%s'.", argv[i]); + cmd_destroy_flush(ret_flush); + return CMD_PARSE_ERROR; + } + + if (strcasecmp("plugin", opt_key) == 0) { + strarray_add(&ret_flush->plugins, &ret_flush->plugins_num, opt_value); + } else if (strcasecmp("identifier", opt_key) == 0) { + identifier_t *id = + realloc(ret_flush->identifiers, + (ret_flush->identifiers_num + 1) * sizeof(*id)); + if (id == NULL) { + cmd_error(CMD_ERROR, err, "realloc failed."); + cmd_destroy_flush(ret_flush); + return CMD_ERROR; + } + + ret_flush->identifiers = id; + id = ret_flush->identifiers + ret_flush->identifiers_num; + ret_flush->identifiers_num++; + if (parse_identifier(opt_value, &id->host, &id->plugin, + &id->plugin_instance, &id->type, &id->type_instance, + opts->identifier_default_host) != 0) { + cmd_error(CMD_PARSE_ERROR, err, "Invalid identifier `%s'.", opt_value); + cmd_destroy_flush(ret_flush); + return CMD_PARSE_ERROR; + } + } else if (strcasecmp("timeout", opt_key) == 0) { + char *endptr; + + errno = 0; + endptr = NULL; + ret_flush->timeout = strtod(opt_value, &endptr); + + if ((endptr == opt_value) || (errno != 0) || + (!isfinite(ret_flush->timeout))) { + cmd_error(CMD_PARSE_ERROR, err, + "Invalid value for option `timeout': %s", opt_value); + cmd_destroy_flush(ret_flush); + return CMD_PARSE_ERROR; + } else if (ret_flush->timeout < 0.0) { + ret_flush->timeout = 0.0; + } + } else { + cmd_error(CMD_PARSE_ERROR, err, "Cannot parse option `%s'.", opt_key); + cmd_destroy_flush(ret_flush); + return CMD_PARSE_ERROR; + } + } + + return CMD_OK; +} /* cmd_status_t cmd_parse_flush */ + +cmd_status_t cmd_handle_flush(FILE *fh, char *buffer) { + cmd_error_handler_t err = {cmd_error_fh, fh}; + cmd_t cmd; + + int success = 0; + int error = 0; + int status; + + if ((fh == NULL) || (buffer == NULL)) + return -1; + + DEBUG("utils_cmd_flush: cmd_handle_flush (fh = %p, buffer = %s);", (void *)fh, + buffer); + + if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK) + return status; + if (cmd.type != CMD_FLUSH) { + cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.", + CMD_TO_STRING(cmd.type)); + cmd_destroy(&cmd); + return CMD_UNKNOWN_COMMAND; + } + + for (size_t i = 0; (i == 0) || (i < cmd.cmd.flush.plugins_num); i++) { + char *plugin = NULL; + + if (cmd.cmd.flush.plugins_num != 0) + plugin = cmd.cmd.flush.plugins[i]; + + for (size_t j = 0; (j == 0) || (j < cmd.cmd.flush.identifiers_num); j++) { + char *identifier = NULL; + char buf[1024]; + + if (cmd.cmd.flush.identifiers_num != 0) { + identifier_t *id = cmd.cmd.flush.identifiers + j; + if (format_name(buf, sizeof(buf), id->host, id->plugin, + id->plugin_instance, id->type, + id->type_instance) != 0) { + error++; + continue; + } + identifier = buf; + } + + if (plugin_flush(plugin, DOUBLE_TO_CDTIME_T(cmd.cmd.flush.timeout), + identifier) == 0) + success++; + else + error++; + } + } + + cmd_error(CMD_OK, &err, "Done: %i successful, %i errors", success, error); + + cmd_destroy(&cmd); + return 0; +#undef PRINT_TO_SOCK +} /* cmd_status_t cmd_handle_flush */ + +void cmd_destroy_flush(cmd_flush_t *flush) { + if (flush == NULL) + return; + + strarray_free(flush->plugins, flush->plugins_num); + flush->plugins = NULL; + flush->plugins_num = 0; + + sfree(flush->identifiers); + flush->identifiers_num = 0; +} /* void cmd_destroy_flush */ diff --git a/src/utils/cmds/flush.h b/src/utils/cmds/flush.h new file mode 100644 index 00000000..794ac1c9 --- /dev/null +++ b/src/utils/cmds/flush.h @@ -0,0 +1,42 @@ +/** + * collectd - src/utils_cmd_flush.h + * Copyright (C) 2008, 2016 Sebastian Harl + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Sebastian "tokkee" Harl + **/ + +#ifndef UTILS_CMD_FLUSH_H +#define UTILS_CMD_FLUSH_H 1 + +#include "utils/cmds/cmds.h" + +#include + +cmd_status_t cmd_parse_flush(size_t argc, char **argv, cmd_flush_t *ret_flush, + const cmd_options_t *opts, + cmd_error_handler_t *err); + +cmd_status_t cmd_handle_flush(FILE *fh, char *buffer); + +void cmd_destroy_flush(cmd_flush_t *flush); + +#endif /* UTILS_CMD_FLUSH_H */ diff --git a/src/utils/cmds/getthreshold.c b/src/utils/cmds/getthreshold.c new file mode 100644 index 00000000..0d4e2f1f --- /dev/null +++ b/src/utils/cmds/getthreshold.c @@ -0,0 +1,182 @@ +/** + * collectd - src/utils_cmd_getthreshold.c + * Copyright (C) 2008,2009 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" + +#include "utils/avltree/avltree.h" +#include "utils/cmds/getthreshold.h" +#include "utils/cmds/parse_option.h" /* for `parse_string' */ +#include "utils_threshold.h" + +#define print_to_socket(fh, ...) \ + if (fprintf(fh, __VA_ARGS__) < 0) { \ + WARNING("handle_getthreshold: failed to write to socket #%i: %s", \ + fileno(fh), STRERRNO); \ + return -1; \ + } + +int handle_getthreshold(FILE *fh, char *buffer) { + char *command; + char *identifier; + char *identifier_copy; + + char *host; + char *plugin; + char *plugin_instance; + char *type; + char *type_instance; + + threshold_t threshold; + + int status; + size_t i; + + if ((fh == NULL) || (buffer == NULL)) + return -1; + + DEBUG("utils_cmd_getthreshold: handle_getthreshold (fh = %p, buffer = %s);", + (void *)fh, buffer); + + command = NULL; + status = parse_string(&buffer, &command); + if (status != 0) { + print_to_socket(fh, "-1 Cannot parse command.\n"); + return -1; + } + assert(command != NULL); + + if (strcasecmp("GETTHRESHOLD", command) != 0) { + print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command); + return -1; + } + + identifier = NULL; + status = parse_string(&buffer, &identifier); + if (status != 0) { + print_to_socket(fh, "-1 Cannot parse identifier.\n"); + return -1; + } + assert(identifier != NULL); + + if (*buffer != 0) { + print_to_socket(fh, "-1 Garbage after end of command: %s\n", buffer); + return -1; + } + + /* parse_identifier() modifies its first argument, + * returning pointers into it */ + identifier_copy = sstrdup(identifier); + + status = parse_identifier(identifier_copy, &host, &plugin, &plugin_instance, + &type, &type_instance, + /* default_host = */ NULL); + if (status != 0) { + DEBUG("handle_getthreshold: Cannot parse identifier `%s'.", identifier); + print_to_socket(fh, "-1 Cannot parse identifier `%s'.\n", identifier); + sfree(identifier_copy); + return -1; + } + + value_list_t vl = {.values = NULL}; + sstrncpy(vl.host, host, sizeof(vl.host)); + sstrncpy(vl.plugin, plugin, sizeof(vl.plugin)); + if (plugin_instance != NULL) + sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); + sstrncpy(vl.type, type, sizeof(vl.type)); + if (type_instance != NULL) + sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); + sfree(identifier_copy); + + status = ut_search_threshold(&vl, &threshold); + if (status == ENOENT) { + print_to_socket(fh, "-1 No threshold found for identifier %s\n", + identifier); + return 0; + } else if (status != 0) { + print_to_socket(fh, "-1 Error while looking up threshold: %i\n", status); + return -1; + } + + /* Lets count the number of lines we'll return. */ + i = 0; + if (threshold.host[0] != 0) + i++; + if (threshold.plugin[0] != 0) + i++; + if (threshold.plugin_instance[0] != 0) + i++; + if (threshold.type[0] != 0) + i++; + if (threshold.type_instance[0] != 0) + i++; + if (threshold.data_source[0] != 0) + i++; + if (!isnan(threshold.warning_min)) + i++; + if (!isnan(threshold.warning_max)) + i++; + if (!isnan(threshold.failure_min)) + i++; + if (!isnan(threshold.failure_max)) + i++; + if (threshold.hysteresis > 0.0) + i++; + if (threshold.hits > 1) + i++; + + /* Print the response */ + print_to_socket(fh, "%" PRIsz " Threshold found\n", i); + + if (threshold.host[0] != 0) + print_to_socket(fh, "Host: %s\n", threshold.host); + if (threshold.plugin[0] != 0) + print_to_socket(fh, "Plugin: %s\n", threshold.plugin); + if (threshold.plugin_instance[0] != 0) + print_to_socket(fh, "Plugin Instance: %s\n", threshold.plugin_instance); + if (threshold.type[0] != 0) + print_to_socket(fh, "Type: %s\n", threshold.type); + if (threshold.type_instance[0] != 0) + print_to_socket(fh, "Type Instance: %s\n", threshold.type_instance); + if (threshold.data_source[0] != 0) + print_to_socket(fh, "Data Source: %s\n", threshold.data_source); + if (!isnan(threshold.warning_min)) + print_to_socket(fh, "Warning Min: %g\n", threshold.warning_min); + if (!isnan(threshold.warning_max)) + print_to_socket(fh, "Warning Max: %g\n", threshold.warning_max); + if (!isnan(threshold.failure_min)) + print_to_socket(fh, "Failure Min: %g\n", threshold.failure_min); + if (!isnan(threshold.failure_max)) + print_to_socket(fh, "Failure Max: %g\n", threshold.failure_max); + if (threshold.hysteresis > 0.0) + print_to_socket(fh, "Hysteresis: %g\n", threshold.hysteresis); + if (threshold.hits > 1) + print_to_socket(fh, "Hits: %i\n", threshold.hits); + + return 0; +} /* int handle_getthreshold */ diff --git a/src/utils/cmds/getthreshold.h b/src/utils/cmds/getthreshold.h new file mode 100644 index 00000000..78700ee2 --- /dev/null +++ b/src/utils/cmds/getthreshold.h @@ -0,0 +1,34 @@ +/** + * collectd - src/utils_cmd_getthreshold.h + * Copyright (C) 2009 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_CMD_GETTHRESHOLD_H +#define UTILS_CMD_GETTHRESHOLD_H 1 + +#include + +int handle_getthreshold(FILE *fh, char *buffer); + +#endif /* UTILS_CMD_GETTHRESHOLD_H */ diff --git a/src/utils/cmds/getval.c b/src/utils/cmds/getval.c new file mode 100644 index 00000000..f2586e12 --- /dev/null +++ b/src/utils/cmds/getval.c @@ -0,0 +1,165 @@ +/** + * collectd - src/utils_cmd_getval.c + * Copyright (C) 2008 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" + +#include "utils/cmds/getval.h" +#include "utils/cmds/parse_option.h" +#include "utils_cache.h" + +cmd_status_t cmd_parse_getval(size_t argc, char **argv, + cmd_getval_t *ret_getval, + const cmd_options_t *opts, + cmd_error_handler_t *err) { + char *identifier_copy; + int status; + + if ((ret_getval == NULL) || (opts == NULL)) { + errno = EINVAL; + cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_getval."); + return CMD_ERROR; + } + + if (argc != 1) { + if (argc == 0) + cmd_error(CMD_PARSE_ERROR, err, "Missing identifier."); + else + cmd_error(CMD_PARSE_ERROR, err, "Garbage after identifier: `%s'.", + argv[1]); + return CMD_PARSE_ERROR; + } + + /* parse_identifier() modifies its first argument, + * returning pointers into it */ + identifier_copy = sstrdup(argv[0]); + + status = parse_identifier( + argv[0], &ret_getval->identifier.host, &ret_getval->identifier.plugin, + &ret_getval->identifier.plugin_instance, &ret_getval->identifier.type, + &ret_getval->identifier.type_instance, opts->identifier_default_host); + if (status != 0) { + DEBUG("cmd_parse_getval: Cannot parse identifier `%s'.", identifier_copy); + cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.", + identifier_copy); + sfree(identifier_copy); + return CMD_PARSE_ERROR; + } + + ret_getval->raw_identifier = identifier_copy; + return CMD_OK; +} /* cmd_status_t cmd_parse_getval */ + +#define print_to_socket(fh, ...) \ + do { \ + if (fprintf(fh, __VA_ARGS__) < 0) { \ + WARNING("cmd_handle_getval: failed to write to socket #%i: %s", \ + fileno(fh), STRERRNO); \ + return -1; \ + } \ + fflush(fh); \ + } while (0) + +cmd_status_t cmd_handle_getval(FILE *fh, char *buffer) { + cmd_error_handler_t err = {cmd_error_fh, fh}; + cmd_status_t status; + cmd_t cmd; + + gauge_t *values; + size_t values_num; + + const data_set_t *ds; + + if ((fh == NULL) || (buffer == NULL)) + return -1; + + DEBUG("utils_cmd_getval: cmd_handle_getval (fh = %p, buffer = %s);", + (void *)fh, buffer); + + if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK) + return status; + if (cmd.type != CMD_GETVAL) { + cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.", + CMD_TO_STRING(cmd.type)); + cmd_destroy(&cmd); + return CMD_UNKNOWN_COMMAND; + } + + ds = plugin_get_ds(cmd.cmd.getval.identifier.type); + if (ds == NULL) { + DEBUG("cmd_handle_getval: plugin_get_ds (%s) == NULL;", + cmd.cmd.getval.identifier.type); + cmd_error(CMD_ERROR, &err, "Type `%s' is unknown.\n", + cmd.cmd.getval.identifier.type); + cmd_destroy(&cmd); + return -1; + } + + values = NULL; + values_num = 0; + status = + uc_get_rate_by_name(cmd.cmd.getval.raw_identifier, &values, &values_num); + if (status != 0) { + cmd_error(CMD_ERROR, &err, "No such value."); + cmd_destroy(&cmd); + return CMD_ERROR; + } + + if (ds->ds_num != values_num) { + 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); + cmd_destroy(&cmd); + return CMD_ERROR; + } + + 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); + if (isnan(values[i])) { + print_to_socket(fh, "NaN\n"); + } else { + print_to_socket(fh, "%12e\n", values[i]); + } + } + + sfree(values); + cmd_destroy(&cmd); + + return CMD_OK; +} /* cmd_status_t cmd_handle_getval */ + +void cmd_destroy_getval(cmd_getval_t *getval) { + if (getval == NULL) + return; + + sfree(getval->raw_identifier); +} /* void cmd_destroy_getval */ diff --git a/src/utils/cmds/getval.h b/src/utils/cmds/getval.h new file mode 100644 index 00000000..f2f1c2eb --- /dev/null +++ b/src/utils/cmds/getval.h @@ -0,0 +1,43 @@ +/** + * collectd - src/utils_cmd_getval.h + * Copyright (C) 2008 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_CMD_GETVAL_H +#define UTILS_CMD_GETVAL_H 1 + +#include + +#include "utils/cmds/cmds.h" + +cmd_status_t cmd_parse_getval(size_t argc, char **argv, + cmd_getval_t *ret_getval, + const cmd_options_t *opts, + cmd_error_handler_t *err); + +cmd_status_t cmd_handle_getval(FILE *fh, char *buffer); + +void cmd_destroy_getval(cmd_getval_t *getval); + +#endif /* UTILS_CMD_GETVAL_H */ diff --git a/src/utils/cmds/listval.c b/src/utils/cmds/listval.c new file mode 100644 index 00000000..287786bb --- /dev/null +++ b/src/utils/cmds/listval.c @@ -0,0 +1,103 @@ +/** + * collectd - src/utils_cmd_listval.c + * Copyright (C) 2008 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" + +#include "utils/cmds/listval.h" +#include "utils/cmds/parse_option.h" +#include "utils_cache.h" + +cmd_status_t cmd_parse_listval(size_t argc, char **argv, + const cmd_options_t *opts + __attribute__((unused)), + cmd_error_handler_t *err) { + if (argc != 0) { + cmd_error(CMD_PARSE_ERROR, err, "Garbage after end of command: `%s'.", + argv[0]); + return CMD_PARSE_ERROR; + } + + return CMD_OK; +} /* cmd_status_t cmd_parse_listval */ + +#define free_everything_and_return(status) \ + do { \ + for (size_t j = 0; j < number; j++) { \ + sfree(names[j]); \ + names[j] = NULL; \ + } \ + sfree(names); \ + sfree(times); \ + return status; \ + } while (0) + +#define print_to_socket(fh, ...) \ + do { \ + if (fprintf(fh, __VA_ARGS__) < 0) { \ + WARNING("handle_listval: failed to write to socket #%i: %s", fileno(fh), \ + STRERRNO); \ + free_everything_and_return(CMD_ERROR); \ + } \ + fflush(fh); \ + } while (0) + +cmd_status_t cmd_handle_listval(FILE *fh, char *buffer) { + cmd_error_handler_t err = {cmd_error_fh, fh}; + cmd_status_t status; + cmd_t cmd; + + char **names = NULL; + cdtime_t *times = NULL; + size_t number = 0; + + DEBUG("utils_cmd_listval: handle_listval (fh = %p, buffer = %s);", (void *)fh, + buffer); + + if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK) + return status; + if (cmd.type != CMD_LISTVAL) { + cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.", + CMD_TO_STRING(cmd.type)); + free_everything_and_return(CMD_UNKNOWN_COMMAND); + } + + status = uc_get_names(&names, ×, &number); + if (status != 0) { + DEBUG("command listval: uc_get_names failed with status %i", status); + cmd_error(CMD_ERROR, &err, "uc_get_names failed."); + free_everything_and_return(CMD_ERROR); + } + + print_to_socket(fh, "%i Value%s found\n", (int)number, + (number == 1) ? "" : "s"); + for (size_t i = 0; i < number; i++) + print_to_socket(fh, "%.3f %s\n", CDTIME_T_TO_DOUBLE(times[i]), names[i]); + + free_everything_and_return(CMD_OK); +} /* cmd_status_t cmd_handle_listval */ diff --git a/src/utils/cmds/listval.h b/src/utils/cmds/listval.h new file mode 100644 index 00000000..6fff019e --- /dev/null +++ b/src/utils/cmds/listval.h @@ -0,0 +1,40 @@ +/** + * collectd - src/utils_cmd_listval.h + * Copyright (C) 2008 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_CMD_LISTVAL_H +#define UTILS_CMD_LISTVAL_H 1 + +#include + +#include "utils/cmds/cmds.h" + +cmd_status_t cmd_parse_listval(size_t argc, char **argv, + const cmd_options_t *opts, + cmd_error_handler_t *err); + +cmd_status_t cmd_handle_listval(FILE *fh, char *buffer); + +#endif /* UTILS_CMD_LISTVAL_H */ diff --git a/src/utils/cmds/parse_option.c b/src/utils/cmds/parse_option.c new file mode 100644 index 00000000..e6b516d6 --- /dev/null +++ b/src/utils/cmds/parse_option.c @@ -0,0 +1,148 @@ +/** + * collectd - src/utils_parse_option.c + * Copyright (C) 2008 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "utils/cmds/parse_option.h" + +int parse_string(char **ret_buffer, char **ret_string) { + char *buffer; + char *string; + + buffer = *ret_buffer; + + /* Eat up leading spaces. */ + string = buffer; + while (isspace((int)*string)) + string++; + if (*string == 0) + return 1; + + /* A quoted string */ + if (*string == '"') { + char *dst; + + string++; + if (*string == 0) + return 1; + + dst = string; + buffer = string; + while ((*buffer != '"') && (*buffer != 0)) { + /* Un-escape backslashes */ + if (*buffer == '\\') { + buffer++; + /* Catch a backslash at the end of buffer */ + if (*buffer == 0) + return -1; + } + *dst = *buffer; + buffer++; + dst++; + } + /* No quote sign has been found */ + if (*buffer == 0) + return -1; + + *dst = 0; + dst++; + *buffer = 0; + buffer++; + + /* Check for trailing spaces. */ + if ((*buffer != 0) && !isspace((int)*buffer)) + return -1; + } else /* an unquoted string */ + { + buffer = string; + while ((*buffer != 0) && !isspace((int)*buffer)) + buffer++; + if (*buffer != 0) { + *buffer = 0; + buffer++; + } + } + + /* Eat up trailing spaces */ + while (isspace((int)*buffer)) + buffer++; + + *ret_buffer = buffer; + *ret_string = string; + + return 0; +} /* int parse_string */ + +/* + * parse_option + * ------------ + * Parses an ``option'' as used with the unixsock and exec commands. An + * option is of the form: + * name0="value" + * name1="value with \"quotes\"" + * name2="value \\ backslash" + * However, if the value does *not* contain a space character, you can skip + * the quotes. + */ +int parse_option(char **ret_buffer, char **ret_key, char **ret_value) { + char *buffer; + char *key; + char *value; + int status; + + buffer = *ret_buffer; + + /* Eat up leading spaces */ + key = buffer; + while (isspace((int)*key)) + key++; + if (*key == 0) + return 1; + + /* Look for the equal sign */ + buffer = key; + while (isalnum((int)*buffer) || *buffer == '_' || *buffer == ':') + buffer++; + if ((*buffer != '=') || (buffer == key)) + return 1; + *buffer = 0; + buffer++; + /* Empty values must be written as "" */ + if (isspace((int)*buffer) || (*buffer == 0)) + return -1; + + status = parse_string(&buffer, &value); + if (status != 0) + return -1; + + /* NB: parse_string will have eaten up all trailing spaces. */ + + *ret_buffer = buffer; + *ret_key = key; + *ret_value = value; + + return 0; +} /* int parse_option */ diff --git a/src/utils/cmds/parse_option.h b/src/utils/cmds/parse_option.h new file mode 100644 index 00000000..3dd0a792 --- /dev/null +++ b/src/utils/cmds/parse_option.h @@ -0,0 +1,33 @@ +/** + * collectd - src/utils_parse_option.h + * Copyright (C) 2008 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_PARSE_OPTION +#define UTILS_PARSE_OPTION 1 + +int parse_string(char **ret_buffer, char **ret_string); +int parse_option(char **ret_buffer, char **ret_key, char **ret_value); + +#endif /* UTILS_PARSE_OPTION */ diff --git a/src/utils/cmds/putnotif.c b/src/utils/cmds/putnotif.c new file mode 100644 index 00000000..e1fecf84 --- /dev/null +++ b/src/utils/cmds/putnotif.c @@ -0,0 +1,181 @@ +/** + * collectd - src/utils_cmd_putnotif.c + * Copyright (C) 2008 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" + +#include "utils/cmds/parse_option.h" +#include "utils/cmds/putnotif.h" + +#define print_to_socket(fh, ...) \ + do { \ + if (fprintf(fh, __VA_ARGS__) < 0) { \ + WARNING("handle_putnotif: failed to write to socket #%i: %s", \ + fileno(fh), STRERRNO); \ + return -1; \ + } \ + fflush(fh); \ + } while (0) + +static int set_option_severity(notification_t *n, const char *value) { + if (strcasecmp(value, "Failure") == 0) + n->severity = NOTIF_FAILURE; + else if (strcasecmp(value, "Warning") == 0) + n->severity = NOTIF_WARNING; + else if (strcasecmp(value, "Okay") == 0) + n->severity = NOTIF_OKAY; + else + return -1; + + return 0; +} /* int set_option_severity */ + +static int set_option_time(notification_t *n, const char *value) { + char *endptr = NULL; + double tmp; + + errno = 0; + tmp = strtod(value, &endptr); + if ((errno != 0) /* Overflow */ + || (endptr == value) /* Invalid string */ + || (endptr == NULL) /* This should not happen */ + || (*endptr != 0)) /* Trailing chars */ + return -1; + + n->time = DOUBLE_TO_CDTIME_T(tmp); + + return 0; +} /* int set_option_time */ + +static int set_option(notification_t *n, const char *option, + const char *value) { + if ((n == NULL) || (option == NULL) || (value == NULL)) + return -1; + + DEBUG("utils_cmd_putnotif: set_option (option = %s, value = %s);", option, + value); + + /* Add a meta option in the form: : */ + if (option[0] != '\0' && option[1] == ':') { + /* Refuse empty key */ + if (option[2] == '\0') + return 1; + + if (option[0] == 's') + return plugin_notification_meta_add_string(n, option + 2, value); + else + return 1; + } + + if (strcasecmp("severity", option) == 0) + return set_option_severity(n, value); + else if (strcasecmp("time", option) == 0) + return set_option_time(n, value); + else if (strcasecmp("message", option) == 0) + sstrncpy(n->message, value, sizeof(n->message)); + else if (strcasecmp("host", option) == 0) + sstrncpy(n->host, value, sizeof(n->host)); + else if (strcasecmp("plugin", option) == 0) + sstrncpy(n->plugin, value, sizeof(n->plugin)); + else if (strcasecmp("plugin_instance", option) == 0) + sstrncpy(n->plugin_instance, value, sizeof(n->plugin_instance)); + else if (strcasecmp("type", option) == 0) + sstrncpy(n->type, value, sizeof(n->type)); + else if (strcasecmp("type_instance", option) == 0) + sstrncpy(n->type_instance, value, sizeof(n->type_instance)); + else + return 1; + + return 0; +} /* int set_option */ + +int handle_putnotif(FILE *fh, char *buffer) { + char *command; + notification_t n = {0}; + int status; + + if ((fh == NULL) || (buffer == NULL)) + return -1; + + DEBUG("utils_cmd_putnotif: handle_putnotif (fh = %p, buffer = %s);", + (void *)fh, buffer); + + command = NULL; + status = parse_string(&buffer, &command); + if (status != 0) { + print_to_socket(fh, "-1 Cannot parse command.\n"); + return -1; + } + assert(command != NULL); + + if (strcasecmp("PUTNOTIF", command) != 0) { + print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command); + return -1; + } + + status = 0; + while (*buffer != 0) { + char *key; + char *value; + + status = parse_option(&buffer, &key, &value); + if (status != 0) { + print_to_socket(fh, "-1 Malformed option.\n"); + break; + } + + status = set_option(&n, key, value); + if (status != 0) { + print_to_socket(fh, "-1 Error parsing option `%s'\n", key); + break; + } + } /* for (i) */ + + /* Check for required fields and complain if anything is missing. */ + if ((status == 0) && (n.severity == 0)) { + print_to_socket(fh, "-1 Option `severity' missing.\n"); + status = -1; + } + if ((status == 0) && (n.time == 0)) { + print_to_socket(fh, "-1 Option `time' missing.\n"); + status = -1; + } + if ((status == 0) && (strlen(n.message) == 0)) { + print_to_socket(fh, "-1 No message or message of length 0 given.\n"); + status = -1; + } + + /* If status is still zero the notification is fine and we can finally + * dispatch it. */ + if (status == 0) { + plugin_dispatch_notification(&n); + print_to_socket(fh, "0 Success\n"); + } + + return 0; +} /* int handle_putnotif */ diff --git a/src/utils/cmds/putnotif.h b/src/utils/cmds/putnotif.h new file mode 100644 index 00000000..7ad0f1a5 --- /dev/null +++ b/src/utils/cmds/putnotif.h @@ -0,0 +1,34 @@ +/** + * collectd - src/utils_cmd_putnotif.h + * Copyright (C) 2008 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_CMD_PUTNOTIF_H +#define UTILS_CMD_PUTNOTIF_H 1 + +#include + +int handle_putnotif(FILE *fh, char *buffer); + +#endif /* UTILS_CMD_PUTNOTIF_H */ diff --git a/src/utils/cmds/putval.c b/src/utils/cmds/putval.c new file mode 100644 index 00000000..b6d5ccc6 --- /dev/null +++ b/src/utils/cmds/putval.c @@ -0,0 +1,285 @@ +/** + * collectd - src/utils_cmd_putval.c + * Copyright (C) 2007-2009 Florian octo Forster + * Copyright (C) 2016 Sebastian tokkee Harl + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + * Sebastian tokkee Harl + **/ + +#include "collectd.h" + +#include "utils/cmds/putval.h" +#include "utils/common/common.h" + +/* + * private helper functions + */ + +static int set_option(value_list_t *vl, const char *key, const char *value) { + if ((vl == NULL) || (key == NULL) || (value == NULL)) + return -1; + + if (strcasecmp("interval", key) == 0) { + double tmp; + char *endptr; + + endptr = NULL; + errno = 0; + tmp = strtod(value, &endptr); + + if ((errno == 0) && (endptr != NULL) && (endptr != value) && (tmp > 0.0)) + vl->interval = DOUBLE_TO_CDTIME_T(tmp); + } else + return 1; + + return 0; +} /* int set_option */ + +/* + * public API + */ + +cmd_status_t cmd_parse_putval(size_t argc, char **argv, + cmd_putval_t *ret_putval, + const cmd_options_t *opts, + cmd_error_handler_t *err) { + cmd_status_t result; + + char *identifier; + char *hostname; + char *plugin; + char *plugin_instance; + char *type; + char *type_instance; + int status; + + char *identifier_copy; + + const data_set_t *ds; + value_list_t vl = VALUE_LIST_INIT; + + if ((ret_putval == NULL) || (opts == NULL)) { + errno = EINVAL; + cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_putval."); + return CMD_ERROR; + } + + if (argc < 2) { + cmd_error(CMD_PARSE_ERROR, err, "Missing identifier and/or value-list."); + return CMD_PARSE_ERROR; + } + + identifier = argv[0]; + + /* parse_identifier() modifies its first argument, returning pointers into + * it; retain the old value for later. */ + identifier_copy = sstrdup(identifier); + + status = + parse_identifier(identifier, &hostname, &plugin, &plugin_instance, &type, + &type_instance, opts->identifier_default_host); + if (status != 0) { + DEBUG("cmd_handle_putval: Cannot parse identifier `%s'.", identifier_copy); + cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.", + identifier_copy); + sfree(identifier_copy); + return CMD_PARSE_ERROR; + } + + if ((strlen(hostname) >= sizeof(vl.host)) || + (strlen(plugin) >= sizeof(vl.plugin)) || + ((plugin_instance != NULL) && + (strlen(plugin_instance) >= sizeof(vl.plugin_instance))) || + ((type_instance != NULL) && + (strlen(type_instance) >= sizeof(vl.type_instance)))) { + cmd_error(CMD_PARSE_ERROR, err, "Identifier too long."); + sfree(identifier_copy); + return CMD_PARSE_ERROR; + } + + sstrncpy(vl.host, hostname, sizeof(vl.host)); + sstrncpy(vl.plugin, plugin, sizeof(vl.plugin)); + sstrncpy(vl.type, type, sizeof(vl.type)); + if (plugin_instance != NULL) + sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); + if (type_instance != NULL) + sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); + + ds = plugin_get_ds(type); + if (ds == NULL) { + cmd_error(CMD_PARSE_ERROR, err, "1 Type `%s' isn't defined.", type); + sfree(identifier_copy); + return CMD_PARSE_ERROR; + } + + hostname = NULL; + plugin = NULL; + plugin_instance = NULL; + type = NULL; + type_instance = NULL; + + ret_putval->raw_identifier = identifier_copy; + if (ret_putval->raw_identifier == NULL) { + cmd_error(CMD_ERROR, err, "malloc failed."); + cmd_destroy_putval(ret_putval); + sfree(vl.values); + return CMD_ERROR; + } + + /* All the remaining fields are part of the option list. */ + result = CMD_OK; + for (size_t i = 1; i < argc; ++i) { + value_list_t *tmp; + + char *key = NULL; + char *value = NULL; + + status = cmd_parse_option(argv[i], &key, &value, err); + if (status == CMD_OK) { + assert(key != NULL); + assert(value != NULL); + set_option(&vl, key, value); + continue; + } else if (status != CMD_NO_OPTION) { + /* parse_option failed, buffer has been modified. + * => we need to abort */ + result = status; + break; + } + /* else: cmd_parse_option did not find an option; treat this as a + * value list. */ + + vl.values_len = ds->ds_num; + vl.values = calloc(vl.values_len, sizeof(*vl.values)); + if (vl.values == NULL) { + cmd_error(CMD_ERROR, err, "malloc failed."); + result = CMD_ERROR; + break; + } + + status = parse_values(argv[i], &vl, ds); + if (status != 0) { + cmd_error(CMD_PARSE_ERROR, err, "Parsing the values string failed."); + result = CMD_PARSE_ERROR; + vl.values_len = 0; + sfree(vl.values); + break; + } + + tmp = realloc(ret_putval->vl, + (ret_putval->vl_num + 1) * sizeof(*ret_putval->vl)); + if (tmp == NULL) { + cmd_error(CMD_ERROR, err, "realloc failed."); + cmd_destroy_putval(ret_putval); + result = CMD_ERROR; + vl.values_len = 0; + sfree(vl.values); + break; + } + + ret_putval->vl = tmp; + ret_putval->vl_num++; + memcpy(&ret_putval->vl[ret_putval->vl_num - 1], &vl, sizeof(vl)); + + /* pointer is now owned by ret_putval->vl[] */ + vl.values_len = 0; + vl.values = NULL; + } /* while (*buffer != 0) */ + /* Done parsing the options. */ + + if (result != CMD_OK) + cmd_destroy_putval(ret_putval); + + return result; +} /* cmd_status_t cmd_parse_putval */ + +void cmd_destroy_putval(cmd_putval_t *putval) { + if (putval == NULL) + return; + + sfree(putval->raw_identifier); + + for (size_t i = 0; i < putval->vl_num; ++i) { + sfree(putval->vl[i].values); + meta_data_destroy(putval->vl[i].meta); + putval->vl[i].meta = NULL; + } + sfree(putval->vl); + putval->vl = NULL; + putval->vl_num = 0; +} /* void cmd_destroy_putval */ + +cmd_status_t cmd_handle_putval(FILE *fh, char *buffer) { + cmd_error_handler_t err = {cmd_error_fh, fh}; + cmd_t cmd; + + int status; + + DEBUG("utils_cmd_putval: cmd_handle_putval (fh = %p, buffer = %s);", + (void *)fh, buffer); + + if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK) + return status; + if (cmd.type != CMD_PUTVAL) { + cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.", + CMD_TO_STRING(cmd.type)); + cmd_destroy(&cmd); + return CMD_UNKNOWN_COMMAND; + } + + for (size_t i = 0; i < cmd.cmd.putval.vl_num; ++i) + plugin_dispatch_values(&cmd.cmd.putval.vl[i]); + + if (fh != stdout) + cmd_error(CMD_OK, &err, "Success: %i %s been dispatched.", + (int)cmd.cmd.putval.vl_num, + (cmd.cmd.putval.vl_num == 1) ? "value has" : "values have"); + + cmd_destroy(&cmd); + return CMD_OK; +} /* int cmd_handle_putval */ + +int cmd_create_putval(char *ret, size_t ret_len, /* {{{ */ + const data_set_t *ds, const value_list_t *vl) { + char buffer_ident[6 * DATA_MAX_NAME_LEN]; + char buffer_values[1024]; + int status; + + status = FORMAT_VL(buffer_ident, sizeof(buffer_ident), vl); + if (status != 0) + return status; + escape_string(buffer_ident, sizeof(buffer_ident)); + + status = format_values(buffer_values, sizeof(buffer_values), ds, vl, + /* store rates = */ false); + if (status != 0) + return status; + escape_string(buffer_values, sizeof(buffer_values)); + + snprintf(ret, ret_len, "PUTVAL %s interval=%.3f %s", buffer_ident, + (vl->interval > 0) ? CDTIME_T_TO_DOUBLE(vl->interval) + : CDTIME_T_TO_DOUBLE(plugin_get_interval()), + buffer_values); + + return 0; +} /* }}} int cmd_create_putval */ diff --git a/src/utils/cmds/putval.h b/src/utils/cmds/putval.h new file mode 100644 index 00000000..f6c3e3b8 --- /dev/null +++ b/src/utils/cmds/putval.h @@ -0,0 +1,47 @@ +/** + * collectd - src/utils_cmd_putval.h + * Copyright (C) 2007 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_CMD_PUTVAL_H +#define UTILS_CMD_PUTVAL_H 1 + +#include "plugin.h" +#include "utils/cmds/cmds.h" + +#include + +cmd_status_t cmd_parse_putval(size_t argc, char **argv, + cmd_putval_t *ret_putval, + const cmd_options_t *opts, + cmd_error_handler_t *err); + +cmd_status_t cmd_handle_putval(FILE *fh, char *buffer); + +void cmd_destroy_putval(cmd_putval_t *putval); + +int cmd_create_putval(char *ret, size_t ret_len, const data_set_t *ds, + const value_list_t *vl); + +#endif /* UTILS_CMD_PUTVAL_H */ diff --git a/src/utils/common/common.c b/src/utils/common/common.c new file mode 100644 index 00000000..2d961d43 --- /dev/null +++ b/src/utils/common/common.c @@ -0,0 +1,1606 @@ +/** + * collectd - src/common.c + * Copyright (C) 2005-2014 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + * Niki W. Waibel + * Sebastian Harl + * Michał Mirosław + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils_cache.h" + +/* for getaddrinfo */ +#include +#include + +#include + +#if HAVE_NETINET_IN_H +#include +#endif + +#if HAVE_NETINET_TCP_H +#include +#endif + +/* for ntohl and htonl */ +#if HAVE_ARPA_INET_H +#include +#endif + +#if HAVE_CAPABILITY +#include +#endif + +#if HAVE_KSTAT_H +#include +#endif + +#ifdef HAVE_LIBKSTAT +extern kstat_ctl_t *kc; +#endif + +#if !defined(MSG_DONTWAIT) +#if defined(MSG_NONBLOCK) +/* AIX doesn't have MSG_DONTWAIT */ +#define MSG_DONTWAIT MSG_NONBLOCK +#else +/* Windows doesn't have MSG_DONTWAIT or MSG_NONBLOCK */ +#define MSG_DONTWAIT 0 +#endif /* defined(MSG_NONBLOCK) */ +#endif /* !defined(MSG_DONTWAIT) */ + +#if !HAVE_GETPWNAM_R && defined(HAVE_GETPWNAM) +static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +#if !HAVE_STRERROR_R +static pthread_mutex_t strerror_r_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +char *sstrncpy(char *dest, const char *src, size_t n) { + strncpy(dest, src, n); + dest[n - 1] = '\0'; + + return dest; +} /* char *sstrncpy */ + +/* ssnprintf returns zero on success, one if truncation occurred + and a negative integer onerror. */ +int ssnprintf(char *str, size_t sz, const char *format, ...) { + va_list ap; + va_start(ap, format); + + int ret = vsnprintf(str, sz, format, ap); + + va_end(ap); + + if (ret < 0) { + return ret; + } + return (size_t)ret >= sz; +} /* int ssnprintf */ + +char *ssnprintf_alloc(char const *format, ...) /* {{{ */ +{ + char static_buffer[1024] = ""; + char *alloc_buffer; + size_t alloc_buffer_size; + int status; + va_list ap; + + /* Try printing into the static buffer. In many cases it will be + * sufficiently large and we can simply return a strdup() of this + * buffer. */ + va_start(ap, format); + status = vsnprintf(static_buffer, sizeof(static_buffer), format, ap); + va_end(ap); + if (status < 0) + return NULL; + + /* "status" does not include the null byte. */ + alloc_buffer_size = (size_t)(status + 1); + if (alloc_buffer_size <= sizeof(static_buffer)) + return strdup(static_buffer); + + /* Allocate a buffer large enough to hold the string. */ + alloc_buffer = calloc(1, alloc_buffer_size); + if (alloc_buffer == NULL) + return NULL; + + /* Print again into this new buffer. */ + va_start(ap, format); + status = vsnprintf(alloc_buffer, alloc_buffer_size, format, ap); + va_end(ap); + if (status < 0) { + sfree(alloc_buffer); + return NULL; + } + + return alloc_buffer; +} /* }}} char *ssnprintf_alloc */ + +char *sstrdup(const char *s) { + char *r; + size_t sz; + + if (s == NULL) + return NULL; + + /* Do not use `strdup' here, because it's not specified in POSIX. It's + * ``only'' an XSI extension. */ + sz = strlen(s) + 1; + r = malloc(sz); + if (r == NULL) { + ERROR("sstrdup: Out of memory."); + exit(3); + } + memcpy(r, s, sz); + + return r; +} /* char *sstrdup */ + +/* Even though Posix requires "strerror_r" to return an "int", + * some systems (e.g. the GNU libc) return a "char *" _and_ + * ignore the second argument ... -tokkee */ +char *sstrerror(int errnum, char *buf, size_t buflen) { + buf[0] = '\0'; + +#if !HAVE_STRERROR_R + { + char *temp; + + pthread_mutex_lock(&strerror_r_lock); + + temp = strerror(errnum); + sstrncpy(buf, temp, buflen); + + pthread_mutex_unlock(&strerror_r_lock); + } + /* #endif !HAVE_STRERROR_R */ + +#elif STRERROR_R_CHAR_P + { + char *temp; + temp = strerror_r(errnum, buf, buflen); + if (buf[0] == '\0') { + if ((temp != NULL) && (temp != buf) && (temp[0] != '\0')) + sstrncpy(buf, temp, buflen); + else + sstrncpy(buf, + "strerror_r did not return " + "an error message", + buflen); + } + } + /* #endif STRERROR_R_CHAR_P */ + +#else + if (strerror_r(errnum, buf, buflen) != 0) { + snprintf(buf, buflen, + "Error #%i; " + "Additionally, strerror_r failed.", + errnum); + } +#endif /* STRERROR_R_CHAR_P */ + + return buf; +} /* char *sstrerror */ + +void *smalloc(size_t size) { + void *r; + + if ((r = malloc(size)) == NULL) { + ERROR("Not enough memory."); + exit(3); + } + + return r; +} /* void *smalloc */ + +#if 0 +void sfree (void **ptr) +{ + if (ptr == NULL) + return; + + if (*ptr != NULL) + free (*ptr); + + *ptr = NULL; +} +#endif + +int sread(int fd, void *buf, size_t count) { + char *ptr; + size_t nleft; + ssize_t status; + + ptr = (char *)buf; + nleft = count; + + while (nleft > 0) { + status = read(fd, (void *)ptr, nleft); + + if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR))) + continue; + + if (status < 0) + return status; + + if (status == 0) { + DEBUG("Received EOF from fd %i. ", fd); + return -1; + } + + assert((0 > status) || (nleft >= (size_t)status)); + + nleft = nleft - ((size_t)status); + ptr = ptr + ((size_t)status); + } + + return 0; +} + +int swrite(int fd, const void *buf, size_t count) { + const char *ptr; + size_t nleft; + ssize_t status; + struct pollfd pfd; + + ptr = (const char *)buf; + nleft = count; + + if (fd < 0) { + errno = EINVAL; + return errno; + } + + /* checking for closed peer connection */ + pfd.fd = fd; + pfd.events = POLLIN | POLLHUP; + pfd.revents = 0; + if (poll(&pfd, 1, 0) > 0) { + char buffer[32]; + if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) { + /* if recv returns zero (even though poll() said there is data to be + * read), that means the connection has been closed */ + errno = ECONNRESET; + return -1; + } + } + + while (nleft > 0) { + status = write(fd, (const void *)ptr, nleft); + + if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR))) + continue; + + if (status < 0) + return errno ? errno : status; + + nleft = nleft - ((size_t)status); + ptr = ptr + ((size_t)status); + } + + return 0; +} + +int strsplit(char *string, char **fields, size_t size) { + size_t i; + char *ptr; + char *saveptr; + + i = 0; + ptr = string; + saveptr = NULL; + while ((fields[i] = strtok_r(ptr, " \t\r\n", &saveptr)) != NULL) { + ptr = NULL; + i++; + + if (i >= size) + break; + } + + return (int)i; +} + +int strjoin(char *buffer, size_t buffer_size, char **fields, size_t fields_num, + const char *sep) { + size_t avail = 0; + char *ptr = buffer; + size_t sep_len = 0; + + size_t buffer_req = 0; + + if (((fields_num != 0) && (fields == NULL)) || + ((buffer_size != 0) && (buffer == NULL))) + return -EINVAL; + + if (buffer != NULL) + buffer[0] = 0; + + if (buffer_size != 0) + avail = buffer_size - 1; + + if (sep != NULL) + sep_len = strlen(sep); + + for (size_t i = 0; i < fields_num; i++) { + size_t field_len = strlen(fields[i]); + + if (i != 0) + buffer_req += sep_len; + buffer_req += field_len; + + if (buffer_size == 0) + continue; + + if ((i != 0) && (sep_len > 0)) { + if (sep_len >= avail) { + /* prevent subsequent iterations from writing to the + * buffer. */ + avail = 0; + continue; + } + + memcpy(ptr, sep, sep_len); + + ptr += sep_len; + avail -= sep_len; + } + + if (field_len > avail) + field_len = avail; + + memcpy(ptr, fields[i], field_len); + ptr += field_len; + + avail -= field_len; + if (ptr != NULL) + *ptr = 0; + } + + return (int)buffer_req; +} + +int escape_string(char *buffer, size_t buffer_size) { + char *temp; + size_t j; + + /* Check if we need to escape at all first */ + temp = strpbrk(buffer, " \t\"\\"); + if (temp == NULL) + return 0; + + if (buffer_size < 3) + return EINVAL; + + temp = calloc(1, buffer_size); + if (temp == NULL) + return ENOMEM; + + temp[0] = '"'; + j = 1; + + for (size_t i = 0; i < buffer_size; i++) { + if (buffer[i] == 0) { + break; + } else if ((buffer[i] == '"') || (buffer[i] == '\\')) { + if (j > (buffer_size - 4)) + break; + temp[j] = '\\'; + temp[j + 1] = buffer[i]; + j += 2; + } else { + if (j > (buffer_size - 3)) + break; + temp[j] = buffer[i]; + j++; + } + } + + assert((j + 1) < buffer_size); + temp[j] = '"'; + temp[j + 1] = 0; + + sstrncpy(buffer, temp, buffer_size); + sfree(temp); + return 0; +} /* int escape_string */ + +int strunescape(char *buf, size_t buf_len) { + for (size_t i = 0; (i < buf_len) && (buf[i] != '\0'); ++i) { + if (buf[i] != '\\') + continue; + + if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) { + P_ERROR("string unescape: backslash found at end of string."); + /* Ensure null-byte at the end of the buffer. */ + buf[i] = 0; + return -1; + } + + switch (buf[i + 1]) { + case 't': + buf[i] = '\t'; + break; + case 'n': + buf[i] = '\n'; + break; + case 'r': + buf[i] = '\r'; + break; + default: + buf[i] = buf[i + 1]; + break; + } + + /* Move everything after the position one position to the left. + * Add a null-byte as last character in the buffer. */ + memmove(buf + i + 1, buf + i + 2, buf_len - i - 2); + buf[buf_len - 1] = '\0'; + } + return 0; +} /* int strunescape */ + +size_t strstripnewline(char *buffer) { + size_t buffer_len = strlen(buffer); + + while (buffer_len > 0) { + if ((buffer[buffer_len - 1] != '\n') && (buffer[buffer_len - 1] != '\r')) + break; + buffer_len--; + buffer[buffer_len] = 0; + } + + return buffer_len; +} /* size_t strstripnewline */ + +int escape_slashes(char *buffer, size_t buffer_size) { + size_t buffer_len; + + buffer_len = strlen(buffer); + + if (buffer_len <= 1) { + if (strcmp("/", buffer) == 0) { + if (buffer_size < 5) + return -1; + sstrncpy(buffer, "root", buffer_size); + } + return 0; + } + + /* Move one to the left */ + if (buffer[0] == '/') { + memmove(buffer, buffer + 1, buffer_len); + buffer_len--; + } + + for (size_t i = 0; i < buffer_len; i++) { + if (buffer[i] == '/') + buffer[i] = '_'; + } + + return 0; +} /* int escape_slashes */ + +void replace_special(char *buffer, size_t buffer_size) { + for (size_t i = 0; i < buffer_size; i++) { + if (buffer[i] == 0) + return; + if ((!isalnum((int)buffer[i])) && (buffer[i] != '-')) + buffer[i] = '_'; + } +} /* void replace_special */ + +int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta) { + struct timeval *larger; + struct timeval *smaller; + + int status; + + NORMALIZE_TIMEVAL(tv0); + NORMALIZE_TIMEVAL(tv1); + + if ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec == tv1.tv_usec)) { + if (delta != NULL) { + delta->tv_sec = 0; + delta->tv_usec = 0; + } + return 0; + } + + if ((tv0.tv_sec < tv1.tv_sec) || + ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec < tv1.tv_usec))) { + larger = &tv1; + smaller = &tv0; + status = -1; + } else { + larger = &tv0; + smaller = &tv1; + status = 1; + } + + if (delta != NULL) { + delta->tv_sec = larger->tv_sec - smaller->tv_sec; + + if (smaller->tv_usec <= larger->tv_usec) + delta->tv_usec = larger->tv_usec - smaller->tv_usec; + else { + --delta->tv_sec; + delta->tv_usec = 1000000 + larger->tv_usec - smaller->tv_usec; + } + } + + assert((delta == NULL) || + ((0 <= delta->tv_usec) && (delta->tv_usec < 1000000))); + + return status; +} /* int timeval_cmp */ + +int check_create_dir(const char *file_orig) { + struct stat statbuf; + + char file_copy[PATH_MAX]; + char dir[PATH_MAX]; + char *fields[16]; + int fields_num; + char *ptr; + char *saveptr; + int last_is_file = 1; + int path_is_absolute = 0; + size_t len; + + /* + * Sanity checks first + */ + if (file_orig == NULL) + return -1; + + if ((len = strlen(file_orig)) < 1) + return -1; + else if (len >= sizeof(file_copy)) { + ERROR("check_create_dir: name (%s) is too long.", file_orig); + return -1; + } + + /* + * If `file_orig' ends in a slash the last component is a directory, + * otherwise it's a file. Act accordingly.. + */ + if (file_orig[len - 1] == '/') + last_is_file = 0; + if (file_orig[0] == '/') + path_is_absolute = 1; + + /* + * Create a copy for `strtok_r' to destroy + */ + sstrncpy(file_copy, file_orig, sizeof(file_copy)); + + /* + * Break into components. This will eat up several slashes in a row and + * remove leading and trailing slashes.. + */ + ptr = file_copy; + saveptr = NULL; + fields_num = 0; + while ((fields[fields_num] = strtok_r(ptr, "/", &saveptr)) != NULL) { + ptr = NULL; + fields_num++; + + if (fields_num >= 16) + break; + } + + /* + * For each component, do.. + */ + for (int i = 0; i < (fields_num - last_is_file); i++) { + /* + * Do not create directories that start with a dot. This + * prevents `../../' attacks and other likely malicious + * behavior. + */ + if (fields[i][0] == '.') { + P_ERROR("Cowardly refusing to create a directory that " + "begins with a `.' (dot): `%s'", + file_orig); + return -2; + } + + /* + * Join the components together again + */ + dir[0] = '/'; + if (strjoin(dir + path_is_absolute, + (size_t)(sizeof(dir) - path_is_absolute), fields, + (size_t)(i + 1), "/") < 0) { + P_ERROR("strjoin failed: `%s', component #%i", file_orig, i); + return -1; + } + + while (42) { + if ((stat(dir, &statbuf) == -1) && (lstat(dir, &statbuf) == -1)) { + if (errno == ENOENT) { + if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0) + break; + + /* this might happen, if a different thread created + * the directory in the meantime + * => call stat() again to check for S_ISDIR() */ + if (EEXIST == errno) + continue; + + P_ERROR("check_create_dir: mkdir (%s): %s", dir, STRERRNO); + return -1; + } else { + P_ERROR("check_create_dir: stat (%s): %s", dir, STRERRNO); + return -1; + } + } else if (!S_ISDIR(statbuf.st_mode)) { + P_ERROR("check_create_dir: `%s' exists but is not " + "a directory!", + dir); + return -1; + } + break; + } + } + + return 0; +} /* check_create_dir */ + +#ifdef HAVE_LIBKSTAT +int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name) { + char ident[128]; + + *ksp_ptr = NULL; + + if (kc == NULL) + return -1; + + snprintf(ident, sizeof(ident), "%s,%i,%s", module, instance, name); + + *ksp_ptr = kstat_lookup(kc, module, instance, name); + if (*ksp_ptr == NULL) { + P_ERROR("get_kstat: Cound not find kstat %s", ident); + return -1; + } + + if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) { + P_ERROR("get_kstat: kstat %s has wrong type", ident); + *ksp_ptr = NULL; + return -1; + } + +#ifdef assert + assert(*ksp_ptr != NULL); + assert((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED); +#endif + + if (kstat_read(kc, *ksp_ptr, NULL) == -1) { + P_ERROR("get_kstat: kstat %s could not be read", ident); + return -1; + } + + if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) { + P_ERROR("get_kstat: kstat %s has wrong type", ident); + return -1; + } + + return 0; +} + +long long get_kstat_value(kstat_t *ksp, char *name) { + kstat_named_t *kn; + long long retval = -1LL; + + if (ksp == NULL) { + P_ERROR("get_kstat_value (\"%s\"): ksp is NULL.", name); + return -1LL; + } else if (ksp->ks_type != KSTAT_TYPE_NAMED) { + P_ERROR("get_kstat_value (\"%s\"): ksp->ks_type (%#x) " + "is not KSTAT_TYPE_NAMED (%#x).", + name, (unsigned int)ksp->ks_type, (unsigned int)KSTAT_TYPE_NAMED); + return -1LL; + } + + if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, name)) == NULL) + return -1LL; + + if (kn->data_type == KSTAT_DATA_INT32) + retval = (long long)kn->value.i32; + else if (kn->data_type == KSTAT_DATA_UINT32) + retval = (long long)kn->value.ui32; + else if (kn->data_type == KSTAT_DATA_INT64) + retval = (long long)kn->value.i64; /* According to ANSI C99 `long long' must + hold at least 64 bits */ + else if (kn->data_type == KSTAT_DATA_UINT64) + retval = (long long)kn->value.ui64; /* XXX: Might overflow! */ + else + P_WARNING("get_kstat_value: Not a numeric value: %s", name); + + return retval; +} +#endif /* HAVE_LIBKSTAT */ + +#ifndef HAVE_HTONLL +unsigned long long ntohll(unsigned long long n) { +#if BYTE_ORDER == BIG_ENDIAN + return n; +#else + return (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32); +#endif +} /* unsigned long long ntohll */ + +unsigned long long htonll(unsigned long long n) { +#if BYTE_ORDER == BIG_ENDIAN + return n; +#else + return (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32); +#endif +} /* unsigned long long htonll */ +#endif /* HAVE_HTONLL */ + +#if FP_LAYOUT_NEED_NOTHING + /* Well, we need nothing.. */ + /* #endif FP_LAYOUT_NEED_NOTHING */ + +#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP +#if FP_LAYOUT_NEED_ENDIANFLIP +#define FP_CONVERT(A) \ + ((((uint64_t)(A)&0xff00000000000000LL) >> 56) | \ + (((uint64_t)(A)&0x00ff000000000000LL) >> 40) | \ + (((uint64_t)(A)&0x0000ff0000000000LL) >> 24) | \ + (((uint64_t)(A)&0x000000ff00000000LL) >> 8) | \ + (((uint64_t)(A)&0x00000000ff000000LL) << 8) | \ + (((uint64_t)(A)&0x0000000000ff0000LL) << 24) | \ + (((uint64_t)(A)&0x000000000000ff00LL) << 40) | \ + (((uint64_t)(A)&0x00000000000000ffLL) << 56)) +#else +#define FP_CONVERT(A) \ + ((((uint64_t)(A)&0xffffffff00000000LL) >> 32) | \ + (((uint64_t)(A)&0x00000000ffffffffLL) << 32)) +#endif + +double ntohd(double d) { + union { + uint8_t byte[8]; + uint64_t integer; + double floating; + } ret; + + ret.floating = d; + + /* NAN in x86 byte order */ + if ((ret.byte[0] == 0x00) && (ret.byte[1] == 0x00) && (ret.byte[2] == 0x00) && + (ret.byte[3] == 0x00) && (ret.byte[4] == 0x00) && (ret.byte[5] == 0x00) && + (ret.byte[6] == 0xf8) && (ret.byte[7] == 0x7f)) { + return NAN; + } else { + uint64_t tmp; + + tmp = ret.integer; + ret.integer = FP_CONVERT(tmp); + return ret.floating; + } +} /* double ntohd */ + +double htond(double d) { + union { + uint8_t byte[8]; + uint64_t integer; + double floating; + } ret; + + if (isnan(d)) { + ret.byte[0] = ret.byte[1] = ret.byte[2] = ret.byte[3] = 0x00; + ret.byte[4] = ret.byte[5] = 0x00; + ret.byte[6] = 0xf8; + ret.byte[7] = 0x7f; + return ret.floating; + } else { + uint64_t tmp; + + ret.floating = d; + tmp = FP_CONVERT(ret.integer); + ret.integer = tmp; + return ret.floating; + } +} /* double htond */ +#endif /* FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP */ + +int format_name(char *ret, int ret_len, const char *hostname, + const char *plugin, const char *plugin_instance, + const char *type, const char *type_instance) { + char *buffer; + size_t buffer_size; + + buffer = ret; + buffer_size = (size_t)ret_len; + +#define APPEND(str) \ + do { \ + size_t l = strlen(str); \ + if (l >= buffer_size) \ + return ENOBUFS; \ + memcpy(buffer, (str), l); \ + buffer += l; \ + buffer_size -= l; \ + } while (0) + + assert(plugin != NULL); + assert(type != NULL); + + APPEND(hostname); + APPEND("/"); + APPEND(plugin); + if ((plugin_instance != NULL) && (plugin_instance[0] != 0)) { + APPEND("-"); + APPEND(plugin_instance); + } + APPEND("/"); + APPEND(type); + if ((type_instance != NULL) && (type_instance[0] != 0)) { + APPEND("-"); + APPEND(type_instance); + } + assert(buffer_size > 0); + buffer[0] = 0; + +#undef APPEND + return 0; +} /* int format_name */ + +int format_values(char *ret, size_t ret_len, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + bool store_rates) { + size_t offset = 0; + int status; + gauge_t *rates = NULL; + + assert(0 == strcmp(ds->type, vl->type)); + + memset(ret, 0, ret_len); + +#define BUFFER_ADD(...) \ + do { \ + status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \ + if (status < 1) { \ + sfree(rates); \ + return -1; \ + } else if (((size_t)status) >= (ret_len - offset)) { \ + sfree(rates); \ + return -1; \ + } else \ + offset += ((size_t)status); \ + } while (0) + + BUFFER_ADD("%.3f", CDTIME_T_TO_DOUBLE(vl->time)); + + for (size_t i = 0; i < ds->ds_num; i++) { + if (ds->ds[i].type == DS_TYPE_GAUGE) + BUFFER_ADD(":" GAUGE_FORMAT, vl->values[i].gauge); + else if (store_rates) { + if (rates == NULL) + rates = uc_get_rate(ds, vl); + if (rates == NULL) { + WARNING("format_values: uc_get_rate failed."); + return -1; + } + BUFFER_ADD(":" GAUGE_FORMAT, rates[i]); + } else if (ds->ds[i].type == DS_TYPE_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) + BUFFER_ADD(":%" PRIu64, vl->values[i].absolute); + else { + ERROR("format_values: Unknown data source type: %i", ds->ds[i].type); + sfree(rates); + return -1; + } + } /* for ds->ds_num */ + +#undef BUFFER_ADD + + sfree(rates); + return 0; +} /* }}} int format_values */ + +int parse_identifier(char *str, char **ret_host, char **ret_plugin, + char **ret_plugin_instance, char **ret_type, + char **ret_type_instance, char *default_host) { + char *hostname = NULL; + char *plugin = NULL; + char *plugin_instance = NULL; + char *type = NULL; + char *type_instance = NULL; + + hostname = str; + if (hostname == NULL) + return -1; + + plugin = strchr(hostname, '/'); + if (plugin == NULL) + return -1; + *plugin = '\0'; + plugin++; + + type = strchr(plugin, '/'); + if (type == NULL) { + if (default_host == NULL) + return -1; + /* else: no host specified; use default */ + type = plugin; + plugin = hostname; + hostname = default_host; + } else { + *type = '\0'; + type++; + } + + plugin_instance = strchr(plugin, '-'); + if (plugin_instance != NULL) { + *plugin_instance = '\0'; + plugin_instance++; + } + + type_instance = strchr(type, '-'); + if (type_instance != NULL) { + *type_instance = '\0'; + type_instance++; + } + + *ret_host = hostname; + *ret_plugin = plugin; + *ret_plugin_instance = plugin_instance; + *ret_type = type; + *ret_type_instance = type_instance; + return 0; +} /* int parse_identifier */ + +int parse_identifier_vl(const char *str, value_list_t *vl) /* {{{ */ +{ + char str_copy[6 * DATA_MAX_NAME_LEN]; + char *host = NULL; + char *plugin = NULL; + char *plugin_instance = NULL; + char *type = NULL; + char *type_instance = NULL; + int status; + + if ((str == NULL) || (vl == NULL)) + return EINVAL; + + sstrncpy(str_copy, str, sizeof(str_copy)); + + status = parse_identifier(str_copy, &host, &plugin, &plugin_instance, &type, + &type_instance, + /* default_host = */ NULL); + if (status != 0) + return status; + + sstrncpy(vl->host, host, sizeof(vl->host)); + sstrncpy(vl->plugin, plugin, sizeof(vl->plugin)); + sstrncpy(vl->plugin_instance, + (plugin_instance != NULL) ? plugin_instance : "", + sizeof(vl->plugin_instance)); + sstrncpy(vl->type, type, sizeof(vl->type)); + sstrncpy(vl->type_instance, (type_instance != NULL) ? type_instance : "", + sizeof(vl->type_instance)); + + return 0; +} /* }}} int parse_identifier_vl */ + +int parse_value(const char *value_orig, value_t *ret_value, int ds_type) { + char *value; + char *endptr = NULL; + size_t value_len; + + if (value_orig == NULL) + return EINVAL; + + value = strdup(value_orig); + if (value == NULL) + return ENOMEM; + value_len = strlen(value); + + while ((value_len > 0) && isspace((int)value[value_len - 1])) { + value[value_len - 1] = '\0'; + value_len--; + } + + switch (ds_type) { + case DS_TYPE_COUNTER: + ret_value->counter = (counter_t)strtoull(value, &endptr, 0); + break; + + case DS_TYPE_GAUGE: + ret_value->gauge = (gauge_t)strtod(value, &endptr); + break; + + case DS_TYPE_DERIVE: + ret_value->derive = (derive_t)strtoll(value, &endptr, 0); + break; + + case DS_TYPE_ABSOLUTE: + ret_value->absolute = (absolute_t)strtoull(value, &endptr, 0); + break; + + default: + sfree(value); + P_ERROR("parse_value: Invalid data source type: %i.", ds_type); + return -1; + } + + if (value == endptr) { + P_ERROR("parse_value: Failed to parse string as %s: \"%s\".", + DS_TYPE_TO_STRING(ds_type), value); + sfree(value); + return -1; + } else if ((NULL != endptr) && ('\0' != *endptr)) + P_INFO("parse_value: Ignoring trailing garbage \"%s\" after %s value. " + "Input string was \"%s\".", + endptr, DS_TYPE_TO_STRING(ds_type), value_orig); + + sfree(value); + return 0; +} /* int parse_value */ + +int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds) { + size_t i; + char *dummy; + char *ptr; + char *saveptr; + + if ((buffer == NULL) || (vl == NULL) || (ds == NULL)) + return EINVAL; + + i = 0; + dummy = buffer; + saveptr = NULL; + vl->time = 0; + while ((ptr = strtok_r(dummy, ":", &saveptr)) != NULL) { + dummy = NULL; + + if (i >= vl->values_len) { + /* Make sure i is invalid. */ + i = 0; + break; + } + + if (vl->time == 0) { + if (strcmp("N", ptr) == 0) + vl->time = cdtime(); + else { + char *endptr = NULL; + double tmp; + + errno = 0; + tmp = strtod(ptr, &endptr); + if ((errno != 0) /* Overflow */ + || (endptr == ptr) /* Invalid string */ + || (endptr == NULL) /* This should not happen */ + || (*endptr != 0)) /* Trailing chars */ + return -1; + + vl->time = DOUBLE_TO_CDTIME_T(tmp); + } + + continue; + } + + if ((strcmp("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE)) + vl->values[i].gauge = NAN; + else if (0 != parse_value(ptr, &vl->values[i], ds->ds[i].type)) + return -1; + + i++; + } /* while (strtok_r) */ + + if ((ptr != NULL) || (i == 0)) + return -1; + return 0; +} /* int parse_values */ + +int parse_value_file(char const *path, value_t *ret_value, int ds_type) { + FILE *fh; + char buffer[256]; + + fh = fopen(path, "r"); + if (fh == NULL) + return -1; + + if (fgets(buffer, sizeof(buffer), fh) == NULL) { + fclose(fh); + return -1; + } + + fclose(fh); + + strstripnewline(buffer); + + return parse_value(buffer, ret_value, ds_type); +} /* int parse_value_file */ + +#if !HAVE_GETPWNAM_R +int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen, + struct passwd **pwbufp) { +#ifndef HAVE_GETPWNAM + return -1; +#else + int status = 0; + struct passwd *pw; + + memset(pwbuf, '\0', sizeof(struct passwd)); + + pthread_mutex_lock(&getpwnam_r_lock); + + do { + pw = getpwnam(name); + if (pw == NULL) { + status = (errno != 0) ? errno : ENOENT; + break; + } + +#define GETPWNAM_COPY_MEMBER(member) \ + if (pw->member != NULL) { \ + int len = strlen(pw->member); \ + if (len >= buflen) { \ + status = ENOMEM; \ + break; \ + } \ + sstrncpy(buf, pw->member, buflen); \ + pwbuf->member = buf; \ + buf += (len + 1); \ + buflen -= (len + 1); \ + } + GETPWNAM_COPY_MEMBER(pw_name); + GETPWNAM_COPY_MEMBER(pw_passwd); + GETPWNAM_COPY_MEMBER(pw_gecos); + GETPWNAM_COPY_MEMBER(pw_dir); + GETPWNAM_COPY_MEMBER(pw_shell); + + pwbuf->pw_uid = pw->pw_uid; + pwbuf->pw_gid = pw->pw_gid; + + if (pwbufp != NULL) + *pwbufp = pwbuf; + } while (0); + + pthread_mutex_unlock(&getpwnam_r_lock); + + return status; +#endif /* HAVE_GETPWNAM */ +} /* int getpwnam_r */ +#endif /* !HAVE_GETPWNAM_R */ + +int notification_init(notification_t *n, int severity, const char *message, + const char *host, const char *plugin, + const char *plugin_instance, const char *type, + const char *type_instance) { + memset(n, '\0', sizeof(notification_t)); + + n->severity = severity; + + if (message != NULL) + sstrncpy(n->message, message, sizeof(n->message)); + if (host != NULL) + sstrncpy(n->host, host, sizeof(n->host)); + if (plugin != NULL) + sstrncpy(n->plugin, plugin, sizeof(n->plugin)); + if (plugin_instance != NULL) + sstrncpy(n->plugin_instance, plugin_instance, sizeof(n->plugin_instance)); + if (type != NULL) + sstrncpy(n->type, type, sizeof(n->type)); + if (type_instance != NULL) + sstrncpy(n->type_instance, type_instance, sizeof(n->type_instance)); + + return 0; +} /* int notification_init */ + +int walk_directory(const char *dir, dirwalk_callback_f callback, + void *user_data, int include_hidden) { + struct dirent *ent; + DIR *dh; + int success; + int failure; + + success = 0; + failure = 0; + + if ((dh = opendir(dir)) == NULL) { + P_ERROR("walk_directory: Cannot open '%s': %s", dir, STRERRNO); + return -1; + } + + while ((ent = readdir(dh)) != NULL) { + int status; + + if (include_hidden) { + if ((strcmp(".", ent->d_name) == 0) || (strcmp("..", ent->d_name) == 0)) + continue; + } else /* if (!include_hidden) */ + { + if (ent->d_name[0] == '.') + continue; + } + + status = (*callback)(dir, ent->d_name, user_data); + if (status != 0) + failure++; + else + success++; + } + + closedir(dh); + + if ((success == 0) && (failure > 0)) + return -1; + return 0; +} + +ssize_t read_file_contents(const char *filename, char *buf, size_t bufsize) { + FILE *fh; + ssize_t ret; + + fh = fopen(filename, "r"); + if (fh == NULL) + return -1; + + ret = (ssize_t)fread(buf, 1, bufsize, fh); + if ((ret == 0) && (ferror(fh) != 0)) { + P_ERROR("read_file_contents: Reading file \"%s\" failed.", filename); + ret = -1; + } + + fclose(fh); + return ret; +} + +counter_t counter_diff(counter_t old_value, counter_t new_value) { + counter_t diff; + + if (old_value > new_value) { + if (old_value <= 4294967295U) + diff = (4294967295U - old_value) + new_value + 1; + else + diff = (18446744073709551615ULL - old_value) + new_value + 1; + } else { + diff = new_value - old_value; + } + + return diff; +} /* counter_t counter_diff */ + +int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */ + rate_to_value_state_t *state, int ds_type, cdtime_t t) { + gauge_t delta_gauge; + cdtime_t delta_t; + + if (ds_type == DS_TYPE_GAUGE) { + state->last_value.gauge = rate; + state->last_time = t; + + *ret_value = state->last_value; + return 0; + } + + /* Counter and absolute can't handle negative rates. Reset "last time" + * to zero, so that the next valid rate will re-initialize the + * structure. */ + if ((rate < 0.0) && + ((ds_type == DS_TYPE_COUNTER) || (ds_type == DS_TYPE_ABSOLUTE))) { + memset(state, 0, sizeof(*state)); + return EINVAL; + } + + /* Another invalid state: The time is not increasing. */ + if (t <= state->last_time) { + memset(state, 0, sizeof(*state)); + return EINVAL; + } + + delta_t = t - state->last_time; + delta_gauge = (rate * CDTIME_T_TO_DOUBLE(delta_t)) + state->residual; + + /* Previous value is invalid. */ + if (state->last_time == 0) /* {{{ */ + { + if (ds_type == DS_TYPE_DERIVE) { + state->last_value.derive = (derive_t)rate; + state->residual = rate - ((gauge_t)state->last_value.derive); + } else if (ds_type == DS_TYPE_COUNTER) { + state->last_value.counter = (counter_t)rate; + state->residual = rate - ((gauge_t)state->last_value.counter); + } else if (ds_type == DS_TYPE_ABSOLUTE) { + state->last_value.absolute = (absolute_t)rate; + state->residual = rate - ((gauge_t)state->last_value.absolute); + } else { + assert(23 == 42); + } + + state->last_time = t; + return EAGAIN; + } /* }}} */ + + if (ds_type == DS_TYPE_DERIVE) { + derive_t delta_derive = (derive_t)delta_gauge; + + state->last_value.derive += delta_derive; + state->residual = delta_gauge - ((gauge_t)delta_derive); + } else if (ds_type == DS_TYPE_COUNTER) { + counter_t delta_counter = (counter_t)delta_gauge; + + state->last_value.counter += delta_counter; + state->residual = delta_gauge - ((gauge_t)delta_counter); + } else if (ds_type == DS_TYPE_ABSOLUTE) { + absolute_t delta_absolute = (absolute_t)delta_gauge; + + state->last_value.absolute = delta_absolute; + state->residual = delta_gauge - ((gauge_t)delta_absolute); + } else { + assert(23 == 42); + } + + state->last_time = t; + *ret_value = state->last_value; + return 0; +} /* }}} value_t rate_to_value */ + +int value_to_rate(gauge_t *ret_rate, /* {{{ */ + value_t value, int ds_type, cdtime_t t, + value_to_rate_state_t *state) { + gauge_t interval; + + /* Another invalid state: The time is not increasing. */ + if (t <= state->last_time) { + memset(state, 0, sizeof(*state)); + return EINVAL; + } + + interval = CDTIME_T_TO_DOUBLE(t - state->last_time); + + /* Previous value is invalid. */ + if (state->last_time == 0) { + state->last_value = value; + state->last_time = t; + return EAGAIN; + } + + switch (ds_type) { + case DS_TYPE_DERIVE: { + derive_t diff = value.derive - state->last_value.derive; + *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); + break; + } + case DS_TYPE_GAUGE: { + *ret_rate = value.gauge; + break; + } + case DS_TYPE_COUNTER: { + counter_t diff = counter_diff(state->last_value.counter, value.counter); + *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); + break; + } + case DS_TYPE_ABSOLUTE: { + absolute_t diff = value.absolute; + *ret_rate = ((gauge_t)diff) / ((gauge_t)interval); + break; + } + default: + return EINVAL; + } + + state->last_value = value; + state->last_time = t; + return 0; +} /* }}} value_t rate_to_value */ + +int service_name_to_port_number(const char *service_name) { + struct addrinfo *ai_list; + int status; + int service_number; + + if (service_name == NULL) + return -1; + + struct addrinfo ai_hints = {.ai_family = AF_UNSPEC}; + + status = getaddrinfo(/* node = */ NULL, service_name, &ai_hints, &ai_list); + if (status != 0) { + P_ERROR("service_name_to_port_number: getaddrinfo failed: %s", + gai_strerror(status)); + return -1; + } + + service_number = -1; + for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL; + ai_ptr = ai_ptr->ai_next) { + if (ai_ptr->ai_family == AF_INET) { + struct sockaddr_in *sa; + + sa = (void *)ai_ptr->ai_addr; + service_number = (int)ntohs(sa->sin_port); + } else if (ai_ptr->ai_family == AF_INET6) { + struct sockaddr_in6 *sa; + + sa = (void *)ai_ptr->ai_addr; + service_number = (int)ntohs(sa->sin6_port); + } + + if (service_number > 0) + break; + } + + freeaddrinfo(ai_list); + + if (service_number > 0) + return service_number; + return -1; +} /* int service_name_to_port_number */ + +void set_sock_opts(int sockfd) /* {{{ */ +{ + int status; + int socktype; + + status = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &socktype, + &(socklen_t){sizeof(socktype)}); + if (status != 0) { + P_WARNING("set_sock_opts: failed to determine socket type"); + return; + } + + if (socktype == SOCK_STREAM) { + status = + setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &(int){1}, sizeof(int)); + if (status != 0) + P_WARNING("set_sock_opts: failed to set socket keepalive flag"); + +#ifdef TCP_KEEPIDLE + int tcp_keepidle = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 100 + 1); + status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepidle, + sizeof(tcp_keepidle)); + if (status != 0) + P_WARNING("set_sock_opts: failed to set socket tcp keepalive time"); +#endif + +#ifdef TCP_KEEPINTVL + int tcp_keepintvl = + ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 1000 + 1); + status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepintvl, + sizeof(tcp_keepintvl)); + if (status != 0) + P_WARNING("set_sock_opts: failed to set socket tcp keepalive interval"); +#endif + } +} /* }}} void set_sock_opts */ + +int strtoderive(const char *string, derive_t *ret_value) /* {{{ */ +{ + derive_t tmp; + char *endptr; + + if ((string == NULL) || (ret_value == NULL)) + return EINVAL; + + errno = 0; + endptr = NULL; + tmp = (derive_t)strtoll(string, &endptr, /* base = */ 0); + if ((endptr == string) || (errno != 0)) + return -1; + + *ret_value = tmp; + return 0; +} /* }}} int strtoderive */ + +int strtogauge(const char *string, gauge_t *ret_value) /* {{{ */ +{ + gauge_t tmp; + char *endptr = NULL; + + if ((string == NULL) || (ret_value == NULL)) + return EINVAL; + + errno = 0; + endptr = NULL; + tmp = (gauge_t)strtod(string, &endptr); + if (errno != 0) + return errno; + else if ((endptr == NULL) || (*endptr != 0)) + return EINVAL; + + *ret_value = tmp; + return 0; +} /* }}} int strtogauge */ + +int strarray_add(char ***ret_array, size_t *ret_array_len, + char const *str) /* {{{ */ +{ + char **array; + size_t array_len = *ret_array_len; + + if (str == NULL) + return EINVAL; + + array = realloc(*ret_array, (array_len + 1) * sizeof(*array)); + if (array == NULL) + return ENOMEM; + *ret_array = array; + + array[array_len] = strdup(str); + if (array[array_len] == NULL) + return ENOMEM; + + array_len++; + *ret_array_len = array_len; + return 0; +} /* }}} int strarray_add */ + +void strarray_free(char **array, size_t array_len) /* {{{ */ +{ + for (size_t i = 0; i < array_len; i++) + sfree(array[i]); + sfree(array); +} /* }}} void strarray_free */ + +#if HAVE_CAPABILITY +int check_capability(int arg) /* {{{ */ +{ + cap_value_t cap_value = (cap_value_t)arg; + cap_t cap; + cap_flag_value_t cap_flag_value; + + if (!CAP_IS_SUPPORTED(cap_value)) + return -1; + + if (!(cap = cap_get_proc())) { + P_ERROR("check_capability: cap_get_proc failed."); + return -1; + } + + if (cap_get_flag(cap, cap_value, CAP_EFFECTIVE, &cap_flag_value) < 0) { + P_ERROR("check_capability: cap_get_flag failed."); + cap_free(cap); + return -1; + } + cap_free(cap); + + return cap_flag_value != CAP_SET; +} /* }}} int check_capability */ +#else +int check_capability(__attribute__((unused)) int arg) /* {{{ */ +{ + P_WARNING("check_capability: unsupported capability implementation. " + "Some plugin(s) may require elevated privileges to work properly."); + return 0; +} /* }}} int check_capability */ +#endif /* HAVE_CAPABILITY */ diff --git a/src/utils/common/common.h b/src/utils/common/common.h new file mode 100644 index 00000000..1ca65054 --- /dev/null +++ b/src/utils/common/common.h @@ -0,0 +1,399 @@ +/** + * collectd - src/common.h + * Copyright (C) 2005-2014 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + * Niki W. Waibel + **/ + +#ifndef COMMON_H +#define COMMON_H + +#include "collectd.h" + +#include "plugin.h" + +#if HAVE_PWD_H +#include +#endif + +#define sfree(ptr) \ + do { \ + free(ptr); \ + (ptr) = NULL; \ + } while (0) + +#define STATIC_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +#define IS_TRUE(s) \ + ((strcasecmp("true", (s)) == 0) || (strcasecmp("yes", (s)) == 0) || \ + (strcasecmp("on", (s)) == 0)) +#define IS_FALSE(s) \ + ((strcasecmp("false", (s)) == 0) || (strcasecmp("no", (s)) == 0) || \ + (strcasecmp("off", (s)) == 0)) + +struct rate_to_value_state_s { + value_t last_value; + cdtime_t last_time; + gauge_t residual; +}; +typedef struct rate_to_value_state_s rate_to_value_state_t; + +struct value_to_rate_state_s { + value_t last_value; + cdtime_t last_time; +}; +typedef struct value_to_rate_state_s value_to_rate_state_t; + +char *sstrncpy(char *dest, const char *src, size_t n); + +__attribute__((format(printf, 3, 4))) int ssnprintf(char *str, size_t size, + char const *format, ...); + +__attribute__((format(printf, 1, 2))) char *ssnprintf_alloc(char const *format, + ...); + +char *sstrdup(const char *s); +void *smalloc(size_t size); +char *sstrerror(int errnum, char *buf, size_t buflen); + +#ifndef ERRBUF_SIZE +#define ERRBUF_SIZE 256 +#endif + +#define STRERROR(e) sstrerror((e), (char[ERRBUF_SIZE]){0}, ERRBUF_SIZE) +#define STRERRNO STRERROR(errno) + +/* + * NAME + * sread + * + * DESCRIPTION + * Reads exactly `n' bytes or fails. Syntax and other behavior is analogous + * to `read(2)'. + * + * PARAMETERS + * `fd' File descriptor to write to. + * `buf' Buffer that is to be written. + * `count' Number of bytes in the buffer. + * + * RETURN VALUE + * Zero upon success or non-zero if an error occurred. `errno' is set in this + * case. + */ +int sread(int fd, void *buf, size_t count); + +/* + * NAME + * swrite + * + * DESCRIPTION + * Writes exactly `n' bytes or fails. Syntax and other behavior is analogous + * to `write(2)'. + * + * PARAMETERS + * `fd' File descriptor to write to. + * `buf' Buffer that is to be written. + * `count' Number of bytes in the buffer. + * + * RETURN VALUE + * Zero upon success or non-zero if an error occurred. `errno' is set in this + * case. + */ +int swrite(int fd, const void *buf, size_t count); + +/* + * NAME + * strsplit + * + * DESCRIPTION + * Splits a string into parts and stores pointers to the parts in `fields'. + * The characters split at are: " ", "\t", "\r", and "\n". + * + * PARAMETERS + * `string' String to split. This string will be modified. `fields' will + * contain pointers to parts of this string, so free'ing it + * will destroy `fields' as well. + * `fields' Array of strings where pointers to the parts will be stored. + * `size' Number of elements in the array. No more than `size' + * pointers will be stored in `fields'. + * + * RETURN VALUE + * Returns the number of parts stored in `fields'. + */ +int strsplit(char *string, char **fields, size_t size); + +/* + * NAME + * strjoin + * + * DESCRIPTION + * Joins together several parts of a string using `sep' as a separator. This + * is equivalent to the Perl built-in `join'. + * + * PARAMETERS + * `dst' Buffer where the result is stored. Can be NULL if you need to + * determine the required buffer size only. + * `dst_len' Length of the destination buffer. No more than this many + * bytes will be written to the memory pointed to by `dst', + * including the trailing null-byte. Must be zero if dst is + * NULL. + * `fields' Array of strings to be joined. + * `fields_num' Number of elements in the `fields' array. + * `sep' String to be inserted between any two elements of `fields'. + * This string is neither prepended nor appended to the result. + * Instead of passing "" (empty string) one can pass NULL. + * + * RETURN VALUE + * Returns the number of characters in the resulting string, excluding a + * tailing null byte. If this value is greater than or equal to "dst_len", the + * result in "dst" is truncated (but still null terminated). On error a + * negative value is returned. + */ +int strjoin(char *dst, size_t dst_len, char **fields, size_t fields_num, + const char *sep); + +/* + * NAME + * escape_slashes + * + * DESCRIPTION + * Removes slashes ("/") from "buffer". If buffer contains a single slash, + * the result will be "root". Leading slashes are removed. All other slashes + * are replaced with underscores ("_"). + * This function is used by plugin_dispatch_values() to escape all parts of + * the identifier. + * + * PARAMETERS + * `buffer' String to be escaped. + * `buffer_size' Size of the buffer. No more then this many bytes will be + * written to `buffer', including the trailing null-byte. + * + * RETURN VALUE + * Returns zero upon success and a value smaller than zero upon failure. + */ +int escape_slashes(char *buffer, size_t buffer_size); + +/** + * NAME + * escape_string + * + * DESCRIPTION + * escape_string quotes and escapes a string to be usable with collectd's + * plain text protocol. "simple" strings are left as they are, for example if + * buffer is 'simple' before the call, it will remain 'simple'. However, if + * buffer contains 'more "complex"' before the call, the returned buffer will + * contain '"more \"complex\""'. + * + * If the buffer is too small to contain the escaped string, the string will + * be truncated. However, leading and trailing double quotes, as well as an + * ending null byte are guaranteed. + * + * RETURN VALUE + * Returns zero on success, even if the string was truncated. Non-zero on + * failure. + */ +int escape_string(char *buffer, size_t buffer_size); + +/* + * NAME + * replace_special + * + * DESCRIPTION + * Replaces any special characters (anything that's not alpha-numeric or a + * dash) with an underscore. + * + * E.g. "foo$bar&" would become "foo_bar_". + * + * PARAMETERS + * `buffer' String to be handled. + * `buffer_size' Length of the string. The function returns after + * encountering a null-byte or reading this many bytes. + */ +void replace_special(char *buffer, size_t buffer_size); + +/* + * NAME + * strunescape + * + * DESCRIPTION + * Replaces any escaped characters in a string with the appropriate special + * characters. The following escaped characters are recognized: + * + * \t -> + * \n -> + * \r -> + * + * For all other escacped characters only the backslash will be removed. + * + * PARAMETERS + * `buf' String to be unescaped. + * `buf_len' Length of the string, including the terminating null-byte. + * + * RETURN VALUE + * Returns zero upon success, a value less than zero else. + */ +int strunescape(char *buf, size_t buf_len); + +/** + * Removed trailing newline characters (CR and LF) from buffer, which must be + * null terminated. Returns the length of the resulting string. + */ +__attribute__((nonnull(1))) size_t strstripnewline(char *buffer); + +/* + * NAME + * timeval_cmp + * + * DESCRIPTION + * Compare the two time values `tv0' and `tv1' and store the absolut value + * of the difference in the time value pointed to by `delta' if it does not + * equal NULL. + * + * RETURN VALUE + * Returns an integer less than, equal to, or greater than zero if `tv0' is + * less than, equal to, or greater than `tv1' respectively. + */ +int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta); + +/* make sure tv_usec stores less than a second */ +#define NORMALIZE_TIMEVAL(tv) \ + do { \ + (tv).tv_sec += (tv).tv_usec / 1000000; \ + (tv).tv_usec = (tv).tv_usec % 1000000; \ + } while (0) + +/* make sure tv_sec stores less than a second */ +#define NORMALIZE_TIMESPEC(tv) \ + do { \ + (tv).tv_sec += (tv).tv_nsec / 1000000000; \ + (tv).tv_nsec = (tv).tv_nsec % 1000000000; \ + } while (0) + +int check_create_dir(const char *file_orig); + +#ifdef HAVE_LIBKSTAT +#if HAVE_KSTAT_H +#include +#endif +int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name); +long long get_kstat_value(kstat_t *ksp, char *name); +#endif + +#ifndef HAVE_HTONLL +unsigned long long ntohll(unsigned long long n); +unsigned long long htonll(unsigned long long n); +#endif + +#if FP_LAYOUT_NEED_NOTHING +#define ntohd(d) (d) +#define htond(d) (d) +#elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP +double ntohd(double d); +double htond(double d); +#else +#error \ + "Don't know how to convert between host and network representation of doubles." +#endif + +int format_name(char *ret, int ret_len, const char *hostname, + const char *plugin, const char *plugin_instance, + const char *type, const char *type_instance); +#define FORMAT_VL(ret, ret_len, vl) \ + format_name(ret, ret_len, (vl)->host, (vl)->plugin, (vl)->plugin_instance, \ + (vl)->type, (vl)->type_instance) +int format_values(char *ret, size_t ret_len, const data_set_t *ds, + const value_list_t *vl, bool store_rates); + +int parse_identifier(char *str, char **ret_host, char **ret_plugin, + char **ret_plugin_instance, char **ret_type, + char **ret_type_instance, char *default_host); +int parse_identifier_vl(const char *str, value_list_t *vl); +int parse_value(const char *value, value_t *ret_value, int ds_type); +int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds); + +/* parse_value_file reads "path" and parses its content as an integer or + * floating point, depending on "ds_type". On success, the value is stored in + * "ret_value" and zero is returned. On failure, a non-zero value is returned. + */ +int parse_value_file(char const *path, value_t *ret_value, int ds_type); + +#if !HAVE_GETPWNAM_R +struct passwd; +int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen, + struct passwd **pwbufp); +#endif + +int notification_init(notification_t *n, int severity, const char *message, + const char *host, const char *plugin, + const char *plugin_instance, const char *type, + const char *type_instance); +#define NOTIFICATION_INIT_VL(n, vl) \ + notification_init(n, NOTIF_FAILURE, NULL, (vl)->host, (vl)->plugin, \ + (vl)->plugin_instance, (vl)->type, (vl)->type_instance) + +typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename, + void *user_data); +int walk_directory(const char *dir, dirwalk_callback_f callback, + void *user_data, int hidden); +/* Returns the number of bytes read or negative on error. */ +ssize_t read_file_contents(char const *filename, char *buf, size_t bufsize); + +counter_t counter_diff(counter_t old_value, counter_t new_value); + +/* Convert a rate back to a value_t. When converting to a derive_t, counter_t + * or absolute_t, take fractional residuals into account. This is important + * when scaling counters, for example. + * Returns zero on success. Returns EAGAIN when called for the first time; in + * this case the value_t is invalid and the next call should succeed. Other + * return values indicate an error. */ +int rate_to_value(value_t *ret_value, gauge_t rate, + rate_to_value_state_t *state, int ds_type, cdtime_t t); + +int value_to_rate(gauge_t *ret_rate, value_t value, int ds_type, cdtime_t t, + value_to_rate_state_t *state); + +/* Converts a service name (a string) to a port number + * (in the range [1-65535]). Returns less than zero on error. */ +int service_name_to_port_number(const char *service_name); + +/* Sets various, non-default, socket options */ +void set_sock_opts(int sockfd); + +/** Parse a string to a derive_t value. Returns zero on success or non-zero on + * failure. If failure is returned, ret_value is not touched. */ +int strtoderive(const char *string, derive_t *ret_value); + +/** Parse a string to a gauge_t value. Returns zero on success or non-zero on + * failure. If failure is returned, ret_value is not touched. */ +int strtogauge(const char *string, gauge_t *ret_value); + +int strarray_add(char ***ret_array, size_t *ret_array_len, char const *str); +void strarray_free(char **array, size_t array_len); + +/** Check if the current process benefits from the capability passed in + * argument. Returns zero if it does, less than zero if it doesn't or on error. + * See capabilities(7) for the list of possible capabilities. + * */ +int check_capability(int arg); + +#endif /* COMMON_H */ diff --git a/src/utils/common/common_test.c b/src/utils/common/common_test.c new file mode 100644 index 00000000..4f15c16f --- /dev/null +++ b/src/utils/common/common_test.c @@ -0,0 +1,390 @@ +/** + * collectd - src/tests/test_common.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +// clang-format off +/* + * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris + * See Github Issue #3193 for details + */ +#include "utils/common/common.h" +#include "testing.h" +// clang-format on + +#if HAVE_KSTAT_H +#include +#endif + +#if HAVE_LIBKSTAT +kstat_ctl_t *kc; +#endif /* HAVE_LIBKSTAT */ + +DEF_TEST(sstrncpy) { + char buffer[16] = ""; + char *ptr = &buffer[4]; + char *ret; + + buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff; + buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff; + + ret = sstrncpy(ptr, "foobar", 8); + OK(ret == ptr); + EXPECT_EQ_STR("foobar", ptr); + OK(buffer[3] == buffer[12]); + + ret = sstrncpy(ptr, "abc", 8); + OK(ret == ptr); + EXPECT_EQ_STR("abc", ptr); + OK(buffer[3] == buffer[12]); + + ret = sstrncpy(ptr, "collectd", 8); + OK(ret == ptr); + OK(ptr[7] == 0); + EXPECT_EQ_STR("collect", ptr); + OK(buffer[3] == buffer[12]); + + return 0; +} + +DEF_TEST(sstrdup) { + char *ptr; + + ptr = sstrdup("collectd"); + OK(ptr != NULL); + EXPECT_EQ_STR("collectd", ptr); + + sfree(ptr); + + ptr = sstrdup(NULL); + OK(ptr == NULL); + + return 0; +} + +DEF_TEST(strsplit) { + char buffer[32]; + char *fields[8]; + int status; + + strncpy(buffer, "foo bar", sizeof(buffer)); + status = strsplit(buffer, fields, 8); + OK(status == 2); + EXPECT_EQ_STR("foo", fields[0]); + EXPECT_EQ_STR("bar", fields[1]); + + strncpy(buffer, "foo \t bar", sizeof(buffer)); + status = strsplit(buffer, fields, 8); + OK(status == 2); + EXPECT_EQ_STR("foo", fields[0]); + EXPECT_EQ_STR("bar", fields[1]); + + strncpy(buffer, "one two\tthree\rfour\nfive", sizeof(buffer)); + status = strsplit(buffer, fields, 8); + OK(status == 5); + EXPECT_EQ_STR("one", fields[0]); + EXPECT_EQ_STR("two", fields[1]); + EXPECT_EQ_STR("three", fields[2]); + EXPECT_EQ_STR("four", fields[3]); + EXPECT_EQ_STR("five", fields[4]); + + strncpy(buffer, "\twith trailing\n", sizeof(buffer)); + status = strsplit(buffer, fields, 8); + OK(status == 2); + EXPECT_EQ_STR("with", fields[0]); + EXPECT_EQ_STR("trailing", fields[1]); + + strncpy(buffer, "1 2 3 4 5 6 7 8 9 10 11 12 13", sizeof(buffer)); + status = strsplit(buffer, fields, 8); + OK(status == 8); + EXPECT_EQ_STR("7", fields[6]); + EXPECT_EQ_STR("8", fields[7]); + + strncpy(buffer, "single", sizeof(buffer)); + status = strsplit(buffer, fields, 8); + OK(status == 1); + EXPECT_EQ_STR("single", fields[0]); + + strncpy(buffer, "", sizeof(buffer)); + status = strsplit(buffer, fields, 8); + OK(status == 0); + + return 0; +} + +DEF_TEST(strjoin) { + struct { + char **fields; + size_t fields_num; + char *separator; + + int want_return; + char *want_buffer; + } cases[] = { + /* Normal case. */ + {(char *[]){"foo", "bar"}, 2, "!", 7, "foo!bar"}, + /* One field only. */ + {(char *[]){"foo"}, 1, "!", 3, "foo"}, + /* No fields at all. */ + {NULL, 0, "!", 0, ""}, + /* Longer separator. */ + {(char *[]){"foo", "bar"}, 2, "rcht", 10, "foorchtbar"}, + /* Empty separator. */ + {(char *[]){"foo", "bar"}, 2, "", 6, "foobar"}, + /* NULL separator. */ + {(char *[]){"foo", "bar"}, 2, NULL, 6, "foobar"}, + /* buffer not large enough -> string is truncated. */ + {(char *[]){"aaaaaa", "bbbbbb", "c!"}, 3, "-", 16, "aaaaaa-bbbbbb-c"}, + /* buffer not large enough -> last field fills buffer completely. */ + {(char *[]){"aaaaaaa", "bbbbbbb", "!"}, 3, "-", 17, "aaaaaaa-bbbbbbb"}, + /* buffer not large enough -> string does *not* end in separator. */ + {(char *[]){"aaaa", "bbbb", "cccc", "!"}, 4, "-", 16, "aaaa-bbbb-cccc"}, + /* buffer not large enough -> string does not end with partial + separator. */ + {(char *[]){"aaaaaa", "bbbbbb", "!"}, 3, "+-", 17, "aaaaaa+-bbbbbb"}, + }; + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { + char buffer[16]; + int status; + + memset(buffer, 0xFF, sizeof(buffer)); + status = strjoin(buffer, sizeof(buffer), cases[i].fields, + cases[i].fields_num, cases[i].separator); + EXPECT_EQ_INT(cases[i].want_return, status); + EXPECT_EQ_STR(cases[i].want_buffer, buffer); + + /* use (NULL, 0) to determine required buffer size. */ + EXPECT_EQ_INT(cases[i].want_return, + strjoin(NULL, 0, cases[i].fields, cases[i].fields_num, + cases[i].separator)); + } + + return 0; +} + +DEF_TEST(escape_slashes) { + struct { + char *str; + char *want; + } cases[] = { + {"foo/bar/baz", "foo_bar_baz"}, + {"/like/a/path", "like_a_path"}, + {"trailing/slash/", "trailing_slash_"}, + {"foo//bar", "foo__bar"}, + }; + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { + char buffer[32]; + + strncpy(buffer, cases[i].str, sizeof(buffer)); + OK(escape_slashes(buffer, sizeof(buffer)) == 0); + EXPECT_EQ_STR(cases[i].want, buffer); + } + + return 0; +} + +DEF_TEST(escape_string) { + struct { + char *str; + char *want; + } cases[] = { + {"foobar", "foobar"}, + {"f00bar", "f00bar"}, + {"foo bar", "\"foo bar\""}, + {"foo \"bar\"", "\"foo \\\"bar\\\"\""}, + {"012345678901234", "012345678901234"}, + {"012345 78901234", "\"012345 789012\""}, + {"012345 78901\"34", "\"012345 78901\""}, + }; + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { + char buffer[16]; + + strncpy(buffer, cases[i].str, sizeof(buffer)); + OK(escape_string(buffer, sizeof(buffer)) == 0); + EXPECT_EQ_STR(cases[i].want, buffer); + } + + return 0; +} + +DEF_TEST(strunescape) { + char buffer[16]; + int status; + + strncpy(buffer, "foo\\tbar", sizeof(buffer)); + status = strunescape(buffer, sizeof(buffer)); + OK(status == 0); + EXPECT_EQ_STR("foo\tbar", buffer); + + strncpy(buffer, "\\tfoo\\r\\n", sizeof(buffer)); + status = strunescape(buffer, sizeof(buffer)); + OK(status == 0); + EXPECT_EQ_STR("\tfoo\r\n", buffer); + + strncpy(buffer, "With \\\"quotes\\\"", sizeof(buffer)); + status = strunescape(buffer, sizeof(buffer)); + OK(status == 0); + EXPECT_EQ_STR("With \"quotes\"", buffer); + + /* Backslash before null byte */ + strncpy(buffer, "\\tbackslash end\\", sizeof(buffer)); + status = strunescape(buffer, sizeof(buffer)); + OK(status != 0); + EXPECT_EQ_STR("\tbackslash end", buffer); + return 0; + + /* Backslash at buffer end */ + strncpy(buffer, "\\t3\\56", sizeof(buffer)); + status = strunescape(buffer, 4); + OK(status != 0); + OK(buffer[0] == '\t'); + OK(buffer[1] == '3'); + OK(buffer[2] == 0); + OK(buffer[3] == 0); + OK(buffer[4] == '5'); + OK(buffer[5] == '6'); + OK(buffer[6] == '7'); + + return 0; +} + +DEF_TEST(parse_values) { + struct { + char buffer[64]; + int status; + gauge_t value; + } cases[] = { + {"1435044576:42", 0, 42.0}, {"1435044576:42:23", -1, NAN}, + {"1435044576:U", 0, NAN}, {"N:12.3", 0, 12.3}, + {"N:42.0:23", -1, NAN}, {"N:U", 0, NAN}, + {"T:42.0", -1, NAN}, + }; + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { + data_source_t dsrc = { + .name = "value", + .type = DS_TYPE_GAUGE, + .min = 0.0, + .max = NAN, + }; + data_set_t ds = { + .type = "example", + .ds_num = 1, + .ds = &dsrc, + }; + + value_t v = { + .gauge = NAN, + }; + value_list_t vl = { + .values = &v, + .values_len = 1, + .time = 0, + .interval = 0, + .host = "example.com", + .plugin = "common_test", + .type = "example", + .meta = NULL, + }; + + int status = parse_values(cases[i].buffer, &vl, &ds); + EXPECT_EQ_INT(cases[i].status, status); + if (status != 0) + continue; + + EXPECT_EQ_DOUBLE(cases[i].value, vl.values[0].gauge); + } + + return 0; +} + +DEF_TEST(value_to_rate) { + struct { + time_t t0; + time_t t1; + int ds_type; + value_t v0; + value_t v1; + gauge_t want; + } cases[] = { + {0, 10, DS_TYPE_DERIVE, {.derive = 0}, {.derive = 1000}, NAN}, + {10, 20, DS_TYPE_DERIVE, {.derive = 1000}, {.derive = 2000}, 100.0}, + {20, 30, DS_TYPE_DERIVE, {.derive = 2000}, {.derive = 1800}, -20.0}, + {0, 10, DS_TYPE_COUNTER, {.counter = 0}, {.counter = 1000}, NAN}, + {10, 20, DS_TYPE_COUNTER, {.counter = 1000}, {.counter = 5000}, 400.0}, + /* 32bit wrap-around. */ + {20, + 30, + DS_TYPE_COUNTER, + {.counter = 4294967238ULL}, + {.counter = 42}, + 10.0}, + /* 64bit wrap-around. */ + {30, + 40, + DS_TYPE_COUNTER, + {.counter = 18446744073709551558ULL}, + {.counter = 42}, + 10.0}, + }; + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { + cdtime_t t0 = TIME_T_TO_CDTIME_T(cases[i].t0); + value_to_rate_state_t state = { + .last_value = cases[i].v0, + .last_time = t0, + }; + gauge_t got; + + if (cases[i].t0 == 0) { + EXPECT_EQ_INT(EAGAIN, + value_to_rate(&got, cases[i].v1, cases[i].ds_type, + TIME_T_TO_CDTIME_T(cases[i].t1), &state)); + continue; + } + + EXPECT_EQ_INT(0, value_to_rate(&got, cases[i].v1, cases[i].ds_type, + TIME_T_TO_CDTIME_T(cases[i].t1), &state)); + EXPECT_EQ_DOUBLE(cases[i].want, got); + } + + return 0; +} + +int main(void) { + RUN_TEST(sstrncpy); + RUN_TEST(sstrdup); + RUN_TEST(strsplit); + RUN_TEST(strjoin); + RUN_TEST(escape_slashes); + RUN_TEST(escape_string); + RUN_TEST(strunescape); + RUN_TEST(parse_values); + RUN_TEST(value_to_rate); + + END_TEST; +} diff --git a/src/utils/config_cores/config_cores.c b/src/utils/config_cores/config_cores.c new file mode 100644 index 00000000..b6dedbc9 --- /dev/null +++ b/src/utils/config_cores/config_cores.c @@ -0,0 +1,372 @@ +/** + * collectd - src/utils_config_cores.c + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Kamil Wiatrowski + **/ + +#include "collectd.h" + +#include "utils/common/common.h" + +#include "utils/config_cores/config_cores.h" + +#define UTIL_NAME "utils_config_cores" + +#define MAX_SOCKETS 8 +#define MAX_SOCKET_CORES 64 +#define MAX_CORES (MAX_SOCKET_CORES * MAX_SOCKETS) + +static inline _Bool is_in_list(unsigned val, const unsigned *list, size_t len) { + for (size_t i = 0; i < len; i++) + if (list[i] == val) + return 1; + return 0; +} + +static int str_to_uint(const char *s, unsigned *n) { + if (s == NULL || n == NULL) + return -EINVAL; + char *endptr = NULL; + + *n = (unsigned)strtoul(s, &endptr, 0); + if (*s == '\0' || *endptr != '\0') { + ERROR(UTIL_NAME ": Failed to parse '%s' into unsigned number", s); + return -EINVAL; + } + + return 0; +} + +/* + * NAME + * str_list_to_nums + * + * DESCRIPTION + * Converts string of characters representing list of numbers into array of + * numbers. Allowed formats are: + * 0,1,2,3 + * 0-10,20-18 + * 1,3,5-8,10,0x10-12 + * + * Numbers can be in decimal or hexadecimal format. + * + * PARAMETERS + * `s' String representing list of unsigned numbers. + * `nums' Array to put converted numeric values into. + * `nums_len' Maximum number of elements that nums can accommodate. + * + * RETURN VALUE + * Number of elements placed into nums. + */ +static size_t str_list_to_nums(char *s, unsigned *nums, size_t nums_len) { + char *saveptr = NULL; + char *token; + size_t idx = 0; + + while ((token = strtok_r(s, ",", &saveptr))) { + char *pos; + unsigned start, end = 0; + s = NULL; + + while (isspace(*token)) + token++; + if (*token == '\0') + continue; + + pos = strchr(token, '-'); + if (pos) { + *pos = '\0'; + } + + if (str_to_uint(token, &start)) + return 0; + + if (pos) { + if (str_to_uint(pos + 1, &end)) + return 0; + } else { + end = start; + } + + if (start > end) { + unsigned swap = start; + start = end; + end = swap; + } + + for (unsigned i = start; i <= end; i++) { + if (is_in_list(i, nums, idx)) + continue; + if (idx >= nums_len) { + WARNING(UTIL_NAME ": exceeded the cores number limit: %" PRIsz, + nums_len); + return idx; + } + nums[idx] = i; + idx++; + } + } + return idx; +} + +/* + * NAME + * check_core_grouping + * + * DESCRIPTION + * Look for [...] brackets in *in string and if found copy the + * part between brackets into *out string and set grouped to 0. + * Otherwise grouped is set to 1 and input is copied without leading + * whitespaces. + * + * PARAMETERS + * `out' Output string to store result. + * `in' Input string to be parsed and copied. + * `out_size' Maximum number of elements that out can accommodate. + * `grouped' Set by function depending if cores should be grouped or not. + * + * RETURN VALUE + * Zero upon success or non-zero if an error occurred. + */ +static int check_core_grouping(char *out, const char *in, size_t out_size, + _Bool *grouped) { + const char *start = in; + char *end; + while (isspace(*start)) + ++start; + if (start[0] == '[') { + *grouped = 0; + ++start; + end = strchr(start, ']'); + if (end == NULL) { + ERROR(UTIL_NAME ": Missing closing bracket ] in option %s.", in); + return -EINVAL; + } + if ((size_t)(end - start) >= out_size) { + ERROR(UTIL_NAME ": Out buffer is too small."); + return -EINVAL; + } + sstrncpy(out, start, end - start + 1); + DEBUG(UTIL_NAME ": Mask for individual (not aggregated) cores: %s", out); + } else { + *grouped = 1; + sstrncpy(out, start, out_size); + } + return 0; +} + +int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl) { + if (ci == NULL || cgl == NULL) + return -EINVAL; + if (ci->values_num == 0 || ci->values_num > MAX_CORES) + return -EINVAL; + core_group_t cgroups[MAX_CORES] = {{0}}; + size_t cg_idx = 0; /* index for cgroups array */ + int ret = 0; + + for (int i = 0; i < ci->values_num; i++) { + if (ci->values[i].type != OCONFIG_TYPE_STRING) { + WARNING(UTIL_NAME ": The %s option requires string arguments.", ci->key); + return -EINVAL; + } + } + + if (ci->values_num == 1 && ci->values[0].value.string && + strlen(ci->values[0].value.string) == 0) + return 0; + + for (int i = 0; i < ci->values_num; i++) { + size_t n; + _Bool grouped = 1; + char str[DATA_MAX_NAME_LEN]; + unsigned cores[MAX_CORES] = {0}; + + if (cg_idx >= STATIC_ARRAY_SIZE(cgroups)) { + ERROR(UTIL_NAME + ": Configuration exceeds maximum number of cores: %" PRIsz, + STATIC_ARRAY_SIZE(cgroups)); + ret = -EINVAL; + goto parse_error; + } + if ((ci->values[i].value.string == NULL) || + (strlen(ci->values[i].value.string) == 0)) { + ERROR(UTIL_NAME ": Failed to parse parameters for %s option.", ci->key); + ret = -EINVAL; + goto parse_error; + } + + ret = check_core_grouping(str, ci->values[i].value.string, sizeof(str), + &grouped); + if (ret != 0) { + ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i, + ci->values[i].value.string); + goto parse_error; + } + n = str_list_to_nums(str, cores, STATIC_ARRAY_SIZE(cores)); + if (n == 0) { + ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i, + ci->values[i].value.string); + ret = -EINVAL; + goto parse_error; + } + + if (grouped) { + cgroups[cg_idx].desc = strdup(ci->values[i].value.string); + if (cgroups[cg_idx].desc == NULL) { + ERROR(UTIL_NAME ": Failed to allocate description."); + ret = -ENOMEM; + goto parse_error; + } + + cgroups[cg_idx].cores = calloc(n, sizeof(*cgroups[cg_idx].cores)); + if (cgroups[cg_idx].cores == NULL) { + ERROR(UTIL_NAME ": Failed to allocate cores for cgroup."); + ret = -ENOMEM; + goto parse_error; + } + + for (size_t j = 0; j < n; j++) + cgroups[cg_idx].cores[j] = cores[j]; + + cgroups[cg_idx].num_cores = n; + cg_idx++; + } else { + for (size_t j = 0; j < n && cg_idx < STATIC_ARRAY_SIZE(cgroups); j++) { + char desc[DATA_MAX_NAME_LEN]; + ssnprintf(desc, sizeof(desc), "%u", cores[j]); + + cgroups[cg_idx].desc = strdup(desc); + if (cgroups[cg_idx].desc == NULL) { + ERROR(UTIL_NAME ": Failed to allocate desc for core %u.", cores[j]); + ret = -ENOMEM; + goto parse_error; + } + + cgroups[cg_idx].cores = calloc(1, sizeof(*(cgroups[cg_idx].cores))); + if (cgroups[cg_idx].cores == NULL) { + ERROR(UTIL_NAME ": Failed to allocate cgroup for core %u.", cores[j]); + ret = -ENOMEM; + goto parse_error; + } + cgroups[cg_idx].num_cores = 1; + cgroups[cg_idx].cores[0] = cores[j]; + cg_idx++; + } + } + } + + cgl->cgroups = calloc(cg_idx, sizeof(*cgl->cgroups)); + if (cgl->cgroups == NULL) { + ERROR(UTIL_NAME ": Failed to allocate core groups."); + ret = -ENOMEM; + goto parse_error; + } + + cgl->num_cgroups = cg_idx; + for (size_t i = 0; i < cg_idx; i++) + cgl->cgroups[i] = cgroups[i]; + + return 0; + +parse_error: + + cg_idx = 0; + while (cg_idx < STATIC_ARRAY_SIZE(cgroups) && cgroups[cg_idx].desc != NULL) { + sfree(cgroups[cg_idx].desc); + sfree(cgroups[cg_idx].cores); + cg_idx++; + } + return ret; +} + +int config_cores_default(int num_cores, core_groups_list_t *cgl) { + if (cgl == NULL || num_cores < 0 || num_cores > MAX_CORES) + return -EINVAL; + + cgl->cgroups = calloc(num_cores, sizeof(*(cgl->cgroups))); + if (cgl->cgroups == NULL) { + ERROR(UTIL_NAME ": Failed to allocate memory for core groups."); + return -ENOMEM; + } + cgl->num_cgroups = num_cores; + + for (int i = 0; i < num_cores; i++) { + char desc[DATA_MAX_NAME_LEN]; + ssnprintf(desc, sizeof(desc), "%d", i); + + cgl->cgroups[i].cores = calloc(1, sizeof(*(cgl->cgroups[i].cores))); + if (cgl->cgroups[i].cores == NULL) { + ERROR(UTIL_NAME ": Failed to allocate default cores for cgroup %d.", i); + config_cores_cleanup(cgl); + return -ENOMEM; + } + cgl->cgroups[i].num_cores = 1; + cgl->cgroups[i].cores[0] = i; + + cgl->cgroups[i].desc = strdup(desc); + if (cgl->cgroups[i].desc == NULL) { + ERROR(UTIL_NAME ": Failed to allocate description for cgroup %d.", i); + config_cores_cleanup(cgl); + return -ENOMEM; + } + } + return 0; +} + +void config_cores_cleanup(core_groups_list_t *cgl) { + if (cgl == NULL) + return; + for (size_t i = 0; i < cgl->num_cgroups; i++) { + sfree(cgl->cgroups[i].desc); + sfree(cgl->cgroups[i].cores); + } + sfree(cgl->cgroups); + cgl->num_cgroups = 0; +} + +int config_cores_cmp_cgroups(const core_group_t *cg_a, + const core_group_t *cg_b) { + size_t found = 0; + + assert(cg_a != NULL); + assert(cg_b != NULL); + + const size_t sz_a = cg_a->num_cores; + const size_t sz_b = cg_b->num_cores; + const unsigned *tab_a = cg_a->cores; + const unsigned *tab_b = cg_b->cores; + + for (size_t i = 0; i < sz_a; i++) + if (is_in_list(tab_a[i], tab_b, sz_b)) + found++; + + /* if no cores are the same */ + if (!found) + return 0; + /* if group contains same cores */ + if (sz_a == sz_b && sz_b == found) + return 1; + /* if not all cores are the same */ + return -1; +} diff --git a/src/utils/config_cores/config_cores.h b/src/utils/config_cores/config_cores.h new file mode 100644 index 00000000..d45f8480 --- /dev/null +++ b/src/utils/config_cores/config_cores.h @@ -0,0 +1,136 @@ +/** + * collectd - src/utils_config_cores.h + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Kamil Wiatrowski + **/ + +#ifndef UTILS_CONFIG_CORES_H +#define UTILS_CONFIG_CORES_H 1 + +#include "configfile.h" + +#ifndef PRIsz +#define PRIsz "zu" +#endif /* PRIsz */ + +struct core_group_s { + char *desc; + unsigned int *cores; + size_t num_cores; +}; +typedef struct core_group_s core_group_t; + +struct core_groups_list_s { + core_group_t *cgroups; + size_t num_cgroups; +}; +typedef struct core_groups_list_s core_groups_list_t; + +/* + * NAME + * config_cores_parse + * + * DESCRIPTION + * Convert strings from config item into list of core groups. + * + * PARAMETERS + * `ci' Pointer to config item. + * `cgl' Pointer to core groups list to be filled. + * + * RETURN VALUE + * Zero upon success or non-zero if an error occurred. + * + * NOTES + * In case of an error, *cgl is not modified. + * Numbers can be in decimal or hexadecimal format. + * The memory allocated for *cgroups in list needs to be freed + * with config_cores_cleanup. + * + * EXAMPLES + * If config is "0-3" "[4-15]" it means that cores 0-3 are aggregated + * into one group and cores 4 to 15 are stored individualily in + * separate groups. Examples of allowed formats: + * "0,3,4" "10-15" - cores collected into two groups + * "0" "0x3" "7" - 3 cores, each in individual group + * "[32-63]" - 32 cores, each in individual group + * + * For empty string "" *cgl is not modified and zero is returned. + */ +int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl); + +/* + * NAME + * config_cores_default + * + * DESCRIPTION + * Set number of cores starting from zero into individual + * core groups in *cgl list. + * + * PARAMETERS + * `num_cores' Number of cores to be configured. + * `cgl' Pointer to core groups list. + * + * RETURN VALUE + * Zero upon success or non-zero if an error occurred. + * + * NOTES + * The memory allocated for *cgroups in list needs to be freed + * with config_cores_cleanup. In case of error the memory is + * freed by the function itself. + */ +int config_cores_default(int num_cores, core_groups_list_t *cgl); + +/* + * NAME + * config_cores_cleanup + * + * DESCRIPTION + * Free the memory allocated for cgroups and set + * num_cgroups to zero. + * + * PARAMETERS + * `cgl' Pointer to core groups list. + */ +void config_cores_cleanup(core_groups_list_t *cgl); + +/* + * NAME + * config_cores_cmp_cgroups + * + * DESCRIPTION + * Function to compare cores in 2 core groups. + * + * PARAMETERS + * `cg_a' Pointer to core group a. + * `cg_b' Pointer to core group b. + * + * RETURN VALUE + * 1 if both groups contain the same cores + * 0 if none of their cores match + * -1 if some but not all cores match + */ +int config_cores_cmp_cgroups(const core_group_t *cg_a, + const core_group_t *cg_b); + +#endif /* UTILS_CONFIG_CORES_H */ diff --git a/src/utils/config_cores/config_cores_test.c b/src/utils/config_cores/config_cores_test.c new file mode 100644 index 00000000..8b4f4c4c --- /dev/null +++ b/src/utils/config_cores/config_cores_test.c @@ -0,0 +1,249 @@ +/** + * collectd - src/utils_config_cores_test.c + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Kamil Wiatrowski + **/ + +#include "collectd.h" + +#include "testing.h" +#include "utils/config_cores/config_cores.c" /* sic */ + +oconfig_value_t test_cfg_values[] = {{{"0"}, OCONFIG_TYPE_STRING}, + {{"1-2"}, OCONFIG_TYPE_STRING}, + {{"[3-4]"}, OCONFIG_TYPE_STRING}}; + +oconfig_item_t test_cfg = { + "Cores", test_cfg_values, STATIC_ARRAY_SIZE(test_cfg_values), NULL, NULL, + 0}; + +static int compare_with_test_config(core_groups_list_t *cgl) { + if (cgl->num_cgroups == 4 && cgl->cgroups[0].num_cores == 1 && + strcmp("0", cgl->cgroups[0].desc) == 0 && cgl->cgroups[0].cores[0] == 0 && + cgl->cgroups[1].num_cores == 2 && + strcmp("1-2", cgl->cgroups[1].desc) == 0 && + cgl->cgroups[1].cores[0] == 1 && cgl->cgroups[1].cores[1] == 2 && + cgl->cgroups[2].num_cores == 1 && + strcmp("3", cgl->cgroups[2].desc) == 0 && cgl->cgroups[2].cores[0] == 3 && + cgl->cgroups[3].num_cores == 1 && + strcmp("4", cgl->cgroups[3].desc) == 0 && cgl->cgroups[3].cores[0] == 4) + return 0; + + return -1; +} + +DEF_TEST(string_to_uint) { + int ret = 0; + char *s = "13", *s1 = "0xd", *s2 = "g"; + unsigned n = 0; + + ret = str_to_uint(s, &n); + EXPECT_EQ_INT(0, ret); + EXPECT_EQ_INT(13, n); + + ret = str_to_uint(s1, &n); + EXPECT_EQ_INT(0, ret); + EXPECT_EQ_INT(13, n); + + ret = str_to_uint(s2, &n); + OK(ret < 0); + + ret = str_to_uint(NULL, &n); + OK(ret < 0); + return 0; +} + +DEF_TEST(cores_list_to_numbers) { + size_t n = 0; + unsigned nums[MAX_CORES]; + char str[64] = ""; + + n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); + EXPECT_EQ_INT(0, n); + + strncpy(str, "1", STATIC_ARRAY_SIZE(str)); + n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); + EXPECT_EQ_INT(1, n); + EXPECT_EQ_INT(1, nums[0]); + + strncpy(str, "0,2-3", STATIC_ARRAY_SIZE(str)); + n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); + EXPECT_EQ_INT(3, n); + EXPECT_EQ_INT(0, nums[0]); + EXPECT_EQ_INT(2, nums[1]); + EXPECT_EQ_INT(3, nums[2]); + + strncpy(str, "11-0xa", STATIC_ARRAY_SIZE(str)); + n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); + EXPECT_EQ_INT(2, n); + EXPECT_EQ_INT(10, nums[0]); + EXPECT_EQ_INT(11, nums[1]); + + snprintf(str, sizeof(str), "0-%d", (MAX_CORES - 1)); + n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); + EXPECT_EQ_INT(MAX_CORES, n); + EXPECT_EQ_INT(0, nums[0]); + EXPECT_EQ_INT(MAX_CORES - 1, nums[MAX_CORES - 1]); + + /* Should return 0 for incorrect syntax. */ + strncpy(str, "5g", STATIC_ARRAY_SIZE(str)); + n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); + EXPECT_EQ_INT(0, n); + return 0; +} + +DEF_TEST(check_grouped_cores) { + int ret = 0; + _Bool grouped; + char src[64] = "[5-15]"; + char dest[64]; + + ret = check_core_grouping(dest, src, sizeof(dest), &grouped); + EXPECT_EQ_INT(0, ret); + EXPECT_EQ_INT(0, grouped); + EXPECT_EQ_STR("5-15", dest); + + strncpy(src, " 5-15", STATIC_ARRAY_SIZE(src)); + ret = check_core_grouping(dest, src, sizeof(dest), &grouped); + EXPECT_EQ_INT(0, ret); + EXPECT_EQ_INT(1, grouped); + EXPECT_EQ_STR("5-15", dest); + return 0; +} + +DEF_TEST(cores_option_parse) { + int ret = 0; + core_groups_list_t cgl = {0}; + + ret = config_cores_parse(&test_cfg, &cgl); + EXPECT_EQ_INT(0, ret); + CHECK_NOT_NULL(cgl.cgroups); + EXPECT_EQ_INT(0, compare_with_test_config(&cgl)); + + config_cores_cleanup(&cgl); + return 0; +} + +DEF_TEST(cores_option_parse_fail) { + int ret = 0; + core_groups_list_t cgl = {0}; + /* Wrong value, missing closing bracket ] */ + oconfig_value_t values = {{"[0-15"}, OCONFIG_TYPE_STRING}; + oconfig_item_t cfg = {"Cores", &values, 1, NULL, NULL, 0}; + + ret = config_cores_parse(&cfg, &cgl); + EXPECT_EQ_INT(-EINVAL, ret); + EXPECT_EQ_INT(0, cgl.num_cgroups); + OK(NULL == cgl.cgroups); + return 0; +} + +DEF_TEST(cores_default_list) { + int ret = 0; + core_groups_list_t cgl = {0}; + + ret = config_cores_default(2, &cgl); + EXPECT_EQ_INT(0, ret); + EXPECT_EQ_INT(2, cgl.num_cgroups); + CHECK_NOT_NULL(cgl.cgroups); + + CHECK_NOT_NULL(cgl.cgroups[0].cores); + CHECK_NOT_NULL(cgl.cgroups[0].desc); + EXPECT_EQ_STR("0", cgl.cgroups[0].desc); + EXPECT_EQ_INT(1, cgl.cgroups[0].num_cores); + EXPECT_EQ_INT(0, cgl.cgroups[0].cores[0]); + + CHECK_NOT_NULL(cgl.cgroups[1].cores); + CHECK_NOT_NULL(cgl.cgroups[1].desc); + EXPECT_EQ_STR("1", cgl.cgroups[1].desc); + EXPECT_EQ_INT(1, cgl.cgroups[1].num_cores); + EXPECT_EQ_INT(1, cgl.cgroups[1].cores[0]); + + config_cores_cleanup(&cgl); + return 0; +} + +DEF_TEST(cores_default_list_fail) { + int ret = 0; + core_groups_list_t cgl = {0}; + + ret = config_cores_default(-1, &cgl); + OK(ret < 0); + ret = config_cores_default(MAX_CORES + 1, &cgl); + OK(ret < 0); + ret = config_cores_default(1, NULL); + OK(ret < 0); + return 0; +} + +DEF_TEST(cores_group_cleanup) { + core_groups_list_t cgl; + cgl.cgroups = calloc(1, sizeof(*cgl.cgroups)); + CHECK_NOT_NULL(cgl.cgroups); + cgl.num_cgroups = 1; + cgl.cgroups[0].desc = strdup("1"); + cgl.cgroups[0].cores = calloc(1, sizeof(*cgl.cgroups[0].cores)); + CHECK_NOT_NULL(cgl.cgroups[0].cores); + cgl.cgroups[0].cores[0] = 1; + cgl.cgroups[0].num_cores = 1; + + config_cores_cleanup(&cgl); + OK(NULL == cgl.cgroups); + EXPECT_EQ_INT(0, cgl.num_cgroups); + return 0; +} + +DEF_TEST(cores_group_cmp) { + unsigned cores_mock[] = {0, 1, 2}; + core_group_t group_mock = {"0,1,2", cores_mock, 3}; + unsigned cores_mock_2[] = {2, 3}; + core_group_t group_mock_2 = {"2,3", cores_mock_2, 2}; + + int ret = config_cores_cmp_cgroups(&group_mock, &group_mock); + EXPECT_EQ_INT(1, ret); + + ret = config_cores_cmp_cgroups(&group_mock, &group_mock_2); + EXPECT_EQ_INT(-1, ret); + + cores_mock_2[0] = 4; + ret = config_cores_cmp_cgroups(&group_mock, &group_mock_2); + EXPECT_EQ_INT(0, ret); + return 0; +} + +int main(void) { + RUN_TEST(string_to_uint); + RUN_TEST(cores_list_to_numbers); + RUN_TEST(check_grouped_cores); + + RUN_TEST(cores_group_cleanup); + RUN_TEST(cores_option_parse); + RUN_TEST(cores_option_parse_fail); + RUN_TEST(cores_default_list); + RUN_TEST(cores_default_list_fail); + + RUN_TEST(cores_group_cmp); + + END_TEST; +} diff --git a/src/utils/crc32/crc32.c b/src/utils/crc32/crc32.c new file mode 100644 index 00000000..afc566a9 --- /dev/null +++ b/src/utils/crc32/crc32.c @@ -0,0 +1,107 @@ +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + */ + +#include +#include + +uint32_t crc32_buffer(const unsigned char *, size_t); +static unsigned int crc32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL}; + +/* Return a 32-bit CRC of the contents of the buffer. */ + +uint32_t crc32_buffer(const unsigned char *s, size_t len) { + uint32_t ret; + + ret = 0; + for (size_t i = 0; i < len; i++) + ret = crc32_tab[(ret ^ s[i]) & 0xff] ^ (ret >> 8); + return ret; +} diff --git a/src/utils/crc32/crc32.h b/src/utils/crc32/crc32.h new file mode 100644 index 00000000..8e2c2126 --- /dev/null +++ b/src/utils/crc32/crc32.h @@ -0,0 +1,32 @@ +/** + * collectd - src/utils_crc32.h + * Copyright (C) 2014 Pierre-Yves Ritschard + * + * 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: + * Pierre-Yves Ritschard + */ + +#ifndef UTILS_CRC32_H +#define UTILS_CRC32_H 1 + +uint32_t crc32_buffer(const unsigned char *, size_t); + +#endif diff --git a/src/utils/curl_stats/curl_stats.c b/src/utils/curl_stats/curl_stats.c new file mode 100644 index 00000000..a8971719 --- /dev/null +++ b/src/utils/curl_stats/curl_stats.c @@ -0,0 +1,252 @@ +/** + * collectd - src/utils_curl_stats.c + * Copyright (C) 2015 Sebastian 'tokkee' Harl + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Sebastian Harl + **/ + +#include "collectd.h" + +#include "utils/common/common.h" +#include "utils/curl_stats/curl_stats.h" + +#include +#include + +struct curl_stats_s { + bool total_time; + bool namelookup_time; + bool connect_time; + bool pretransfer_time; + bool size_upload; + bool size_download; + bool speed_download; + bool speed_upload; + bool header_size; + bool request_size; + bool content_length_download; + bool content_length_upload; + bool starttransfer_time; + bool redirect_time; + bool redirect_count; + bool num_connects; + bool appconnect_time; +}; + +/* + * Private functions + */ + +static int dispatch_gauge(CURL *curl, CURLINFO info, value_list_t *vl) { + CURLcode code; + value_t v; + + code = curl_easy_getinfo(curl, info, &v.gauge); + if (code != CURLE_OK) + return -1; + + vl->values = &v; + vl->values_len = 1; + + return plugin_dispatch_values(vl); +} /* dispatch_gauge */ + +/* dispatch a speed, in bytes/second */ +static int dispatch_speed(CURL *curl, CURLINFO info, value_list_t *vl) { + CURLcode code; + value_t v; + + code = curl_easy_getinfo(curl, info, &v.gauge); + if (code != CURLE_OK) + return -1; + + v.gauge *= 8; + + vl->values = &v; + vl->values_len = 1; + + return plugin_dispatch_values(vl); +} /* dispatch_speed */ + +/* dispatch a size/count, reported as a long value */ +static int dispatch_size(CURL *curl, CURLINFO info, value_list_t *vl) { + CURLcode code; + value_t v; + long raw; + + code = curl_easy_getinfo(curl, info, &raw); + if (code != CURLE_OK) + return -1; + + v.gauge = (double)raw; + + vl->values = &v; + vl->values_len = 1; + + return plugin_dispatch_values(vl); +} /* dispatch_size */ + +static struct { + const char *name; + const char *config_key; + size_t offset; + + int (*dispatcher)(CURL *, CURLINFO, value_list_t *); + const char *type; + CURLINFO info; +} field_specs[] = { +#define SPEC(name, config_key, dispatcher, type, info) \ + { #name, config_key, offsetof(curl_stats_t, name), dispatcher, type, info } + + SPEC(total_time, "TotalTime", dispatch_gauge, "duration", + CURLINFO_TOTAL_TIME), + SPEC(namelookup_time, "NamelookupTime", dispatch_gauge, "duration", + CURLINFO_NAMELOOKUP_TIME), + SPEC(connect_time, "ConnectTime", dispatch_gauge, "duration", + CURLINFO_CONNECT_TIME), + SPEC(pretransfer_time, "PretransferTime", dispatch_gauge, "duration", + CURLINFO_PRETRANSFER_TIME), + SPEC(size_upload, "SizeUpload", dispatch_gauge, "bytes", + CURLINFO_SIZE_UPLOAD), + SPEC(size_download, "SizeDownload", dispatch_gauge, "bytes", + CURLINFO_SIZE_DOWNLOAD), + SPEC(speed_download, "SpeedDownload", dispatch_speed, "bitrate", + CURLINFO_SPEED_DOWNLOAD), + SPEC(speed_upload, "SpeedUpload", dispatch_speed, "bitrate", + CURLINFO_SPEED_UPLOAD), + SPEC(header_size, "HeaderSize", dispatch_size, "bytes", + CURLINFO_HEADER_SIZE), + SPEC(request_size, "RequestSize", dispatch_size, "bytes", + CURLINFO_REQUEST_SIZE), + SPEC(content_length_download, "ContentLengthDownload", dispatch_gauge, + "bytes", CURLINFO_CONTENT_LENGTH_DOWNLOAD), + SPEC(content_length_upload, "ContentLengthUpload", dispatch_gauge, "bytes", + CURLINFO_CONTENT_LENGTH_UPLOAD), + SPEC(starttransfer_time, "StarttransferTime", dispatch_gauge, "duration", + CURLINFO_STARTTRANSFER_TIME), + SPEC(redirect_time, "RedirectTime", dispatch_gauge, "duration", + CURLINFO_REDIRECT_TIME), + SPEC(redirect_count, "RedirectCount", dispatch_size, "count", + CURLINFO_REDIRECT_COUNT), + SPEC(num_connects, "NumConnects", dispatch_size, "count", + CURLINFO_NUM_CONNECTS), +#ifdef HAVE_CURLINFO_APPCONNECT_TIME + SPEC(appconnect_time, "AppconnectTime", dispatch_gauge, "duration", + CURLINFO_APPCONNECT_TIME), +#endif + +#undef SPEC +}; + +static void enable_field(curl_stats_t *s, size_t offset) { + *(bool *)((char *)s + offset) = true; +} /* enable_field */ + +static bool field_enabled(curl_stats_t *s, size_t offset) { + return *(bool *)((char *)s + offset); +} /* field_enabled */ + +/* + * Public API + */ +curl_stats_t *curl_stats_from_config(oconfig_item_t *ci) { + curl_stats_t *s; + + if (ci == NULL) + return NULL; + + s = calloc(1, sizeof(*s)); + if (s == NULL) + return NULL; + + for (int i = 0; i < ci->children_num; ++i) { + oconfig_item_t *c = ci->children + i; + size_t field; + + bool enabled = 0; + + for (field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) { + if (!strcasecmp(c->key, field_specs[field].config_key)) + break; + if (!strcasecmp(c->key, field_specs[field].name)) + break; + } + if (field >= STATIC_ARRAY_SIZE(field_specs)) { + ERROR("curl stats: Unknown field name %s", c->key); + free(s); + return NULL; + } + + if (cf_util_get_boolean(c, &enabled) != 0) { + free(s); + return NULL; + } + if (enabled) + enable_field(s, field_specs[field].offset); + } + + return s; +} /* curl_stats_from_config */ + +void curl_stats_destroy(curl_stats_t *s) { + if (s != NULL) + free(s); +} /* curl_stats_destroy */ + +int curl_stats_dispatch(curl_stats_t *s, CURL *curl, const char *hostname, + const char *plugin, const char *plugin_instance) { + value_list_t vl = VALUE_LIST_INIT; + + if (s == NULL) + return 0; + if ((curl == NULL) || (plugin == NULL)) { + ERROR("curl stats: dispatch() called with missing arguments " + "(curl=%p; plugin=%s)", + curl, plugin == NULL ? "" : plugin); + return -1; + } + + if (hostname != NULL) + sstrncpy(vl.host, hostname, sizeof(vl.host)); + sstrncpy(vl.plugin, plugin, sizeof(vl.plugin)); + if (plugin_instance != NULL) + sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); + + for (size_t field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) { + int status; + + if (!field_enabled(s, field_specs[field].offset)) + continue; + + sstrncpy(vl.type, field_specs[field].type, sizeof(vl.type)); + sstrncpy(vl.type_instance, field_specs[field].name, + sizeof(vl.type_instance)); + + vl.values = NULL; + vl.values_len = 0; + status = field_specs[field].dispatcher(curl, field_specs[field].info, &vl); + if (status < 0) + return status; + } + + return 0; +} /* curl_stats_dispatch */ diff --git a/src/utils/curl_stats/curl_stats.h b/src/utils/curl_stats/curl_stats.h new file mode 100644 index 00000000..3f83aab9 --- /dev/null +++ b/src/utils/curl_stats/curl_stats.h @@ -0,0 +1,56 @@ +/** + * collectd - src/utils_curl_stats.h + * Copyright (C) 2015 Sebastian 'tokkee' Harl + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Sebastian Harl + **/ + +#ifndef UTILS_CURL_STATS_H +#define UTILS_CURL_STATS_H 1 + +#include "plugin.h" + +#include + +struct curl_stats_s; +typedef struct curl_stats_s curl_stats_t; + +/* + * curl_stats_from_config allocates and constructs a cURL statistics object + * from the specified configuration which is expected to be a single block of + * boolean options named after cURL information fields. The boolean value + * indicates whether to collect the respective information. + * + * See http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html + */ +curl_stats_t *curl_stats_from_config(oconfig_item_t *ci); + +void curl_stats_destroy(curl_stats_t *s); + +/* + * curl_stats_dispatch dispatches performance values from the the specified + * cURL session to the daemon. + */ +int curl_stats_dispatch(curl_stats_t *s, CURL *curl, const char *hostname, + const char *plugin, const char *plugin_instance); + +#endif /* UTILS_CURL_STATS_H */ diff --git a/src/utils/db_query/db_query.c b/src/utils/db_query/db_query.c new file mode 100644 index 00000000..fa43fe56 --- /dev/null +++ b/src/utils/db_query/db_query.c @@ -0,0 +1,1036 @@ +/** + * collectd - src/utils_db_query.c + * Copyright (C) 2008,2009 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils/db_query/db_query.h" + +/* + * Data types + */ +struct udb_result_s; /* {{{ */ +typedef struct udb_result_s udb_result_t; +struct udb_result_s { + char *type; + char *instance_prefix; + char **instances; + size_t instances_num; + char **values; + size_t values_num; + char **metadata; + size_t metadata_num; + + udb_result_t *next; +}; /* }}} */ + +struct udb_query_s /* {{{ */ +{ + char *name; + char *statement; + void *user_data; + char *plugin_instance_from; + + unsigned int min_version; + unsigned int max_version; + + udb_result_t *results; +}; /* }}} */ + +struct udb_result_preparation_area_s /* {{{ */ +{ + const data_set_t *ds; + size_t *instances_pos; + size_t *values_pos; + size_t *metadata_pos; + char **instances_buffer; + char **values_buffer; + char **metadata_buffer; + char *plugin_instance; + + struct udb_result_preparation_area_s *next; +}; /* }}} */ +typedef struct udb_result_preparation_area_s udb_result_preparation_area_t; + +struct udb_query_preparation_area_s /* {{{ */ +{ + size_t column_num; + size_t plugin_instance_pos; + char *host; + char *plugin; + char *db_name; + + udb_result_preparation_area_t *result_prep_areas; +}; /* }}} */ + +/* + * Config Private functions + */ +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) { + 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) { + P_WARNING("Argument %i to the `%s' option " + "is not a string.", + i + 1, ci->key); + return -1; + } + } + + array_len = *ret_array_len; + array = realloc(*ret_array, sizeof(char *) * (array_len + ci->values_num)); + if (array == NULL) { + P_ERROR("udb_config_add_string: realloc failed."); + return -1; + } + *ret_array = array; + + for (int i = 0; i < ci->values_num; i++) { + array[array_len] = strdup(ci->values[i].value.string); + if (array[array_len] == NULL) { + P_ERROR("udb_config_add_string: strdup failed."); + *ret_array_len = array_len; + return -1; + } + array_len++; + } + + *ret_array_len = array_len; + return 0; +} /* }}} int udb_config_add_string */ + +static int udb_config_set_uint(unsigned int *ret_value, /* {{{ */ + oconfig_item_t *ci) { + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) { + P_WARNING("The `%s' config option " + "needs exactly one numeric argument.", + ci->key); + return -1; + } + + 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; +} /* }}} int udb_config_set_uint */ + +/* + * Result private functions + */ +static int udb_result_submit(udb_result_t *r, /* {{{ */ + udb_result_preparation_area_t *r_area, + udb_query_t const *q, + udb_query_preparation_area_t *q_area) { + value_list_t vl = VALUE_LIST_INIT; + + assert(r != NULL); + assert(r_area->ds != NULL); + assert(((size_t)r_area->ds->ds_num) == r->values_num); + assert(r->values_num > 0); + + vl.values = calloc(r->values_num, sizeof(*vl.values)); + if (vl.values == NULL) { + P_ERROR("udb_result_submit: calloc failed."); + return -1; + } + vl.values_len = r_area->ds->ds_num; + + for (size_t i = 0; i < r->values_num; i++) { + char *value_str = r_area->values_buffer[i]; + + if (0 != parse_value(value_str, &vl.values[i], 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; + } + } + + 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)); + + /* Set vl.plugin_instance */ + if (q->plugin_instance_from != NULL) { + sstrncpy(vl.plugin_instance, r_area->plugin_instance, + sizeof(vl.plugin_instance)); + } else { + sstrncpy(vl.plugin_instance, q_area->db_name, sizeof(vl.plugin_instance)); + } + + /* Set vl.type_instance {{{ */ + if (r->instances_num == 0) { + if (r->instance_prefix == NULL) + vl.type_instance[0] = 0; + else + sstrncpy(vl.type_instance, r->instance_prefix, sizeof(vl.type_instance)); + } else /* if ((r->instances_num > 0) */ + { + if (r->instance_prefix == NULL) { + int status = strjoin(vl.type_instance, sizeof(vl.type_instance), + r_area->instances_buffer, r->instances_num, "-"); + if (status < 0) { + P_ERROR( + "udb_result_submit: creating type_instance failed with status %d.", + status); + return status; + } + } else { + char tmp[DATA_MAX_NAME_LEN]; + + int status = strjoin(tmp, sizeof(tmp), r_area->instances_buffer, + r->instances_num, "-"); + if (status < 0) { + P_ERROR( + "udb_result_submit: creating type_instance failed with status %d.", + status); + return status; + } + tmp[sizeof(tmp) - 1] = '\0'; + + ssnprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s", + r->instance_prefix, tmp); + } + } + 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) { + P_ERROR("udb_result_submit: meta_data_create failed."); + free(vl.values); + return -ENOMEM; + } + + for (size_t i = 0; i < r->metadata_num; i++) { + int status = meta_data_add_string(vl.meta, r->metadata[i], + r_area->metadata_buffer[i]); + if (status != 0) { + P_ERROR("udb_result_submit: meta_data_add_string failed."); + meta_data_destroy(vl.meta); + vl.meta = NULL; + free(vl.values); + return status; + } + } + } + /* }}} */ + + plugin_dispatch_values(&vl); + + if (r->metadata_num > 0) { + meta_data_destroy(vl.meta); + vl.meta = NULL; + } + sfree(vl.values); + return 0; +} /* }}} void udb_result_submit */ + +static void udb_result_finish_result(udb_result_t const *r, /* {{{ */ + udb_result_preparation_area_t *prep_area) { + if ((r == NULL) || (prep_area == NULL)) + return; + + 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); +} /* }}} void udb_result_finish_result */ + +static int udb_result_handle_result(udb_result_t *r, /* {{{ */ + udb_query_preparation_area_t *q_area, + udb_result_preparation_area_t *r_area, + udb_query_t const *q, + char **column_values) { + assert(r && q_area && r_area); + + for (size_t i = 0; i < r->instances_num; i++) + r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]]; + + for (size_t i = 0; i < r->values_num; i++) + r_area->values_buffer[i] = column_values[r_area->values_pos[i]]; + + for (size_t i = 0; i < r->metadata_num; i++) + r_area->metadata_buffer[i] = column_values[r_area->metadata_pos[i]]; + + if (q->plugin_instance_from) + r_area->plugin_instance = column_values[q_area->plugin_instance_pos]; + + return udb_result_submit(r, r_area, q, q_area); +} /* }}} int udb_result_handle_result */ + +static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */ + udb_result_preparation_area_t *prep_area, + char **column_names, size_t column_num) { + 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) \ + udb_result_finish_result(r, prep_area); \ + return (status) + + /* Read `ds' and check number of values {{{ */ + prep_area->ds = plugin_get_ds(r->type); + if (prep_area->ds == NULL) { + 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) { + 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); + } + /* }}} */ + + /* Allocate r->instances_pos, r->values_pos, r->metadata_post, + * r->instances_buffer, r->values_buffer, and r->metadata_buffer {{{ */ + if (r->instances_num > 0) { + prep_area->instances_pos = + calloc(r->instances_num, sizeof(*prep_area->instances_pos)); + if (prep_area->instances_pos == NULL) { + P_ERROR("udb_result_prepare_result: calloc failed."); + BAIL_OUT(-ENOMEM); + } + + prep_area->instances_buffer = + calloc(r->instances_num, sizeof(*prep_area->instances_buffer)); + if (prep_area->instances_buffer == NULL) { + P_ERROR("udb_result_prepare_result: calloc failed."); + BAIL_OUT(-ENOMEM); + } + } /* if (r->instances_num > 0) */ + + prep_area->values_pos = calloc(r->values_num, sizeof(*prep_area->values_pos)); + if (prep_area->values_pos == NULL) { + P_ERROR("udb_result_prepare_result: calloc failed."); + BAIL_OUT(-ENOMEM); + } + + prep_area->values_buffer = + calloc(r->values_num, sizeof(*prep_area->values_buffer)); + if (prep_area->values_buffer == NULL) { + P_ERROR("udb_result_prepare_result: calloc failed."); + BAIL_OUT(-ENOMEM); + } + + prep_area->metadata_pos = + calloc(r->metadata_num, sizeof(*prep_area->metadata_pos)); + if (prep_area->metadata_pos == NULL) { + P_ERROR("udb_result_prepare_result: calloc failed."); + BAIL_OUT(-ENOMEM); + } + + prep_area->metadata_buffer = + calloc(r->metadata_num, sizeof(*prep_area->metadata_buffer)); + if (prep_area->metadata_buffer == NULL) { + P_ERROR("udb_result_prepare_result: calloc failed."); + BAIL_OUT(-ENOMEM); + } + + /* }}} */ + + /* Determine the position of the plugin instance column {{{ */ + for (size_t i = 0; i < r->instances_num; i++) { + size_t j; + + for (j = 0; j < column_num; j++) { + if (strcasecmp(r->instances[i], column_names[j]) == 0) { + prep_area->instances_pos[i] = j; + break; + } + } + + if (j >= column_num) { + 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++) */ + + /* Determine the position of the value columns {{{ */ + for (size_t i = 0; i < r->values_num; i++) { + size_t j; + + for (j = 0; j < column_num; j++) { + if (strcasecmp(r->values[i], column_names[j]) == 0) { + prep_area->values_pos[i] = j; + break; + } + } + + if (j >= column_num) { + 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++) */ + + /* Determine the position of the metadata columns {{{ */ + for (size_t i = 0; i < r->metadata_num; i++) { + size_t j; + + for (j = 0; j < column_num; j++) { + if (strcasecmp(r->metadata[i], column_names[j]) == 0) { + prep_area->metadata_pos[i] = j; + break; + } + } + + if (j >= column_num) { + 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++) */ + +#undef BAIL_OUT + return 0; +} /* }}} int udb_result_prepare_result */ + +static void udb_result_free(udb_result_t *r) /* {{{ */ +{ + if (r == NULL) + return; + + sfree(r->type); + sfree(r->instance_prefix); + + for (size_t i = 0; i < r->instances_num; i++) + sfree(r->instances[i]); + sfree(r->instances); + + for (size_t i = 0; i < r->values_num; i++) + sfree(r->values[i]); + sfree(r->values); + + for (size_t i = 0; i < r->metadata_num; i++) + sfree(r->metadata[i]); + sfree(r->metadata); + + udb_result_free(r->next); + + sfree(r); +} /* }}} void udb_result_free */ + +static int udb_result_create(const char *query_name, /* {{{ */ + udb_result_t **r_head, oconfig_item_t *ci) { + udb_result_t *r; + int status; + + if (ci->values_num != 0) { + 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) { + P_ERROR("udb_result_create: calloc failed."); + return -1; + } + r->type = NULL; + r->instance_prefix = NULL; + r->instances = NULL; + r->values = NULL; + r->metadata = NULL; + r->next = NULL; + + /* Fill the `udb_result_t' structure.. */ + status = 0; + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("Type", child->key) == 0) + status = cf_util_get_string(child, &r->type); + else if (strcasecmp("InstancePrefix", child->key) == 0) + 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) + status = udb_config_add_string(&r->values, &r->values_num, child); + else if (strcasecmp("MetadataFrom", child->key) == 0) + status = udb_config_add_string(&r->metadata, &r->metadata_num, child); + else { + P_WARNING("Query `%s': Option `%s' not allowed here.", query_name, + child->key); + status = -1; + } + + if (status != 0) + break; + } + + /* Check that all necessary options have been given. */ + while (status == 0) { + if (r->type == NULL) { + P_WARNING("udb_result_create: `Type' not given for " + "result in query `%s'", + query_name); + status = -1; + } + if (r->values == NULL) { + P_WARNING("udb_result_create: `ValuesFrom' not given for " + "result in query `%s'", + query_name); + status = -1; + } + + break; + } /* while (status == 0) */ + + if (status != 0) { + udb_result_free(r); + return -1; + } + + /* If all went well, add this result to the list of results. */ + if (*r_head == NULL) { + *r_head = r; + } else { + udb_result_t *last; + + last = *r_head; + while (last->next != NULL) + last = last->next; + + last->next = r; + } + + return 0; +} /* }}} int udb_result_create */ + +/* + * Query private functions + */ +static void udb_query_free_one(udb_query_t *q) /* {{{ */ +{ + if (q == NULL) + return; + + sfree(q->name); + sfree(q->statement); + sfree(q->plugin_instance_from); + + udb_result_free(q->results); + + sfree(q); +} /* }}} void udb_query_free_one */ + +/* + * Query public functions + */ +int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */ + size_t *ret_query_list_len, oconfig_item_t *ci, + udb_query_create_callback_t cb) { + udb_query_t **query_list; + size_t query_list_len; + + udb_query_t *q; + int status; + + if ((ret_query_list == NULL) || (ret_query_list_len == NULL)) + return -EINVAL; + query_list = *ret_query_list; + query_list_len = *ret_query_list_len; + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { + P_WARNING("udb_result_create: The `Query' block " + "needs exactly one string argument."); + return -1; + } + + q = calloc(1, sizeof(*q)); + if (q == NULL) { + P_ERROR("udb_query_create: calloc failed."); + return -1; + } + q->min_version = 0; + q->max_version = UINT_MAX; + q->statement = NULL; + q->results = NULL; + q->plugin_instance_from = NULL; + + status = cf_util_get_string(ci, &q->name); + if (status != 0) { + sfree(q); + return status; + } + + /* Fill the `udb_query_t' structure.. */ + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("Statement", child->key) == 0) + 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) + status = udb_config_set_uint(&q->min_version, child); + else if (strcasecmp("MaxVersion", child->key) == 0) + status = udb_config_set_uint(&q->max_version, child); + else if (strcasecmp("PluginInstanceFrom", child->key) == 0) + status = cf_util_get_string(child, &q->plugin_instance_from); + + /* Call custom callbacks */ + else if (cb != NULL) { + status = (*cb)(q, child); + if (status != 0) { + P_WARNING("The configuration callback failed " + "to handle `%s'.", + child->key); + } + } else { + P_WARNING("Query `%s': Option `%s' not allowed here.", q->name, + child->key); + status = -1; + } + + if (status != 0) + break; + } + + /* Check that all necessary options have been given. */ + if (status == 0) { + if (q->statement == NULL) { + P_WARNING("Query `%s': No `Statement' given.", q->name); + status = -1; + } + if (q->results == NULL) { + P_WARNING("Query `%s': No (valid) `Result' block given.", q->name); + status = -1; + } + } /* if (status == 0) */ + + /* If all went well, add this query to the list of queries within the + * database structure. */ + if (status == 0) { + udb_query_t **temp; + + temp = realloc(query_list, sizeof(*query_list) * (query_list_len + 1)); + if (temp == NULL) { + P_ERROR("udb_query_create: realloc failed"); + status = -1; + } else { + query_list = temp; + query_list[query_list_len] = q; + query_list_len++; + } + } + + if (status != 0) { + udb_query_free_one(q); + return -1; + } + + *ret_query_list = query_list; + *ret_query_list_len = query_list_len; + + return 0; +} /* }}} int udb_query_create */ + +void udb_query_free(udb_query_t **query_list, size_t query_list_len) /* {{{ */ +{ + if (query_list == NULL) + return; + + for (size_t i = 0; i < query_list_len; i++) + udb_query_free_one(query_list[i]); + + sfree(query_list); +} /* }}} void udb_query_free */ + +int udb_query_pick_from_list_by_name(const char *name, /* {{{ */ + udb_query_t **src_list, + size_t src_list_len, + udb_query_t ***dst_list, + size_t *dst_list_len) { + int num_added; + + if ((name == NULL) || (src_list == NULL) || (dst_list == NULL) || + (dst_list_len == NULL)) { + P_ERROR("udb_query_pick_from_list_by_name: " + "Invalid argument."); + return -EINVAL; + } + + num_added = 0; + for (size_t i = 0; i < src_list_len; i++) { + udb_query_t **tmp_list; + size_t tmp_list_len; + + if (strcasecmp(name, src_list[i]->name) != 0) + continue; + + tmp_list_len = *dst_list_len; + tmp_list = realloc(*dst_list, (tmp_list_len + 1) * sizeof(udb_query_t *)); + if (tmp_list == NULL) { + P_ERROR("udb_query_pick_from_list_by_name: realloc failed."); + return -ENOMEM; + } + + tmp_list[tmp_list_len] = src_list[i]; + tmp_list_len++; + + *dst_list = tmp_list; + *dst_list_len = tmp_list_len; + + num_added++; + } /* for (i = 0; i < src_list_len; i++) */ + + if (num_added <= 0) { + P_ERROR("Cannot find query `%s'. Make sure the " + "block is above the database definition!", + name); + return -ENOENT; + } else { + DEBUG("Added %i versions of query `%s'.", num_added, name); + } + + return 0; +} /* }}} int udb_query_pick_from_list_by_name */ + +int udb_query_pick_from_list(oconfig_item_t *ci, /* {{{ */ + udb_query_t **src_list, size_t src_list_len, + udb_query_t ***dst_list, size_t *dst_list_len) { + const char *name; + + if ((ci == NULL) || (src_list == NULL) || (dst_list == NULL) || + (dst_list_len == NULL)) { + P_ERROR("udb_query_pick_from_list: " + "Invalid argument."); + return -EINVAL; + } + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { + P_ERROR("The `%s' config option " + "needs exactly one string argument.", + ci->key); + return -1; + } + name = ci->values[0].value.string; + + return udb_query_pick_from_list_by_name(name, src_list, src_list_len, + dst_list, dst_list_len); +} /* }}} int udb_query_pick_from_list */ + +const char *udb_query_get_name(udb_query_t *q) /* {{{ */ +{ + if (q == NULL) + return NULL; + + return q->name; +} /* }}} const char *udb_query_get_name */ + +const char *udb_query_get_statement(udb_query_t *q) /* {{{ */ +{ + if (q == NULL) + return NULL; + + return q->statement; +} /* }}} const char *udb_query_get_statement */ + +void udb_query_set_user_data(udb_query_t *q, void *user_data) /* {{{ */ +{ + if (q == NULL) + return; + + q->user_data = user_data; +} /* }}} void udb_query_set_user_data */ + +void *udb_query_get_user_data(udb_query_t *q) /* {{{ */ +{ + if (q == NULL) + return NULL; + + return q->user_data; +} /* }}} void *udb_query_get_user_data */ + +int udb_query_check_version(udb_query_t *q, unsigned int version) /* {{{ */ +{ + if (q == NULL) + return -EINVAL; + + if ((version < q->min_version) || (version > q->max_version)) + return 0; + + return 1; +} /* }}} int udb_query_check_version */ + +void udb_query_finish_result(udb_query_t const *q, /* {{{ */ + udb_query_preparation_area_t *prep_area) { + udb_result_preparation_area_t *r_area; + udb_result_t *r; + + if ((q == NULL) || (prep_area == NULL)) + return; + + prep_area->column_num = 0; + sfree(prep_area->host); + sfree(prep_area->plugin); + sfree(prep_area->db_name); + + 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 */ + if (r_area == NULL) + break; + udb_result_finish_result(r, r_area); + } +} /* }}} void udb_query_finish_result */ + +int udb_query_handle_result(udb_query_t const *q, /* {{{ */ + udb_query_preparation_area_t *prep_area, + char **column_values) { + udb_result_preparation_area_t *r_area; + udb_result_t *r; + int success; + int status; + + if ((q == NULL) || (prep_area == NULL)) + return -EINVAL; + + if ((prep_area->column_num < 1) || (prep_area->host == NULL) || + (prep_area->plugin == NULL) || (prep_area->db_name == NULL)) { + 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("udb_query_handle_result (%s, %s): " + "column[%" PRIsz "] = %s;", + prep_area->db_name, q->name, i, column_values[i]); + } + } while (0); +#endif /* }}} */ + + success = 0; + for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL; + r = r->next, r_area = r_area->next) { + status = udb_result_handle_result(r, prep_area, r_area, q, column_values); + if (status == 0) + success++; + } + + if (success == 0) { + P_ERROR("udb_query_handle_result (%s, %s): " + "All results failed.", + prep_area->db_name, q->name); + return -1; + } + + return 0; +} /* }}} int udb_query_handle_result */ + +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) { + udb_result_preparation_area_t *r_area; + udb_result_t *r; + int status; + + if ((q == NULL) || (prep_area == NULL)) + return -EINVAL; + +#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); + + if ((prep_area->host == NULL) || (prep_area->plugin == NULL) || + (prep_area->db_name == NULL)) { + P_ERROR("Query `%s': Prepare failed: Out of memory.", q->name); + udb_query_finish_result(q, prep_area); + return -ENOMEM; + } + +#if defined(COLLECT_DEBUG) && COLLECT_DEBUG + do { + for (size_t i = 0; i < column_num; i++) { + DEBUG("udb_query_prepare_result: " + "query = %s; column[%" PRIsz "] = %s;", + q->name, i, column_names[i]); + } + } while (0); +#endif + + /* Determine the position of the PluginInstance column {{{ */ + if (q->plugin_instance_from != NULL) { + size_t i; + + for (i = 0; i < column_num; i++) { + if (strcasecmp(q->plugin_instance_from, column_names[i]) == 0) { + prep_area->plugin_instance_pos = i; + break; + } + } + + if (i >= column_num) { + 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; + } + } + /* }}} */ + + for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL; + r = r->next, r_area = r_area->next) { + if (!r_area) { + P_ERROR("Query `%s': Invalid number of result " + "preparation areas.", + q->name); + udb_query_finish_result(q, prep_area); + return -EINVAL; + } + + status = udb_result_prepare_result(r, r_area, column_names, column_num); + if (status != 0) { + udb_query_finish_result(q, prep_area); + return status; + } + } + + return 0; +} /* }}} int udb_query_prepare_result */ + +udb_query_preparation_area_t * +udb_query_allocate_preparation_area(udb_query_t *q) /* {{{ */ +{ + udb_query_preparation_area_t *q_area; + udb_result_preparation_area_t **next_r_area; + udb_result_t *r; + + q_area = calloc(1, sizeof(*q_area)); + if (q_area == NULL) + return NULL; + + next_r_area = &q_area->result_prep_areas; + for (r = q->results; r != NULL; r = r->next) { + udb_result_preparation_area_t *r_area; + + r_area = calloc(1, sizeof(*r_area)); + if (r_area == NULL) { + udb_result_preparation_area_t *a = q_area->result_prep_areas; + + while (a != NULL) { + udb_result_preparation_area_t *next = a->next; + sfree(a); + a = next; + } + + free(q_area); + return NULL; + } + + *next_r_area = r_area; + next_r_area = &r_area->next; + } + + return q_area; +} /* }}} udb_query_preparation_area_t *udb_query_allocate_preparation_area */ + +void udb_query_delete_preparation_area( + udb_query_preparation_area_t *q_area) /* {{{ */ +{ + udb_result_preparation_area_t *r_area; + + if (q_area == NULL) + return; + + r_area = q_area->result_prep_areas; + while (r_area != NULL) { + udb_result_preparation_area_t *area = r_area; + + r_area = r_area->next; + + sfree(area->instances_pos); + sfree(area->values_pos); + sfree(area->instances_buffer); + sfree(area->values_buffer); + free(area); + } + + sfree(q_area->host); + sfree(q_area->plugin); + sfree(q_area->db_name); + + free(q_area); +} /* }}} void udb_query_delete_preparation_area */ diff --git a/src/utils/db_query/db_query.h b/src/utils/db_query/db_query.h new file mode 100644 index 00000000..f173204c --- /dev/null +++ b/src/utils/db_query/db_query.h @@ -0,0 +1,85 @@ +/** + * collectd - src/utils_db_query.h + * Copyright (C) 2008,2009 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_DB_QUERY_H +#define UTILS_DB_QUERY_H 1 + +/* + * Data types + */ +struct udb_query_s; +typedef struct udb_query_s udb_query_t; + +struct udb_query_preparation_area_s; +typedef struct udb_query_preparation_area_s udb_query_preparation_area_t; + +typedef int (*udb_query_create_callback_t)(udb_query_t *q, oconfig_item_t *ci); + +/* + * Public functions + */ +int udb_query_create(udb_query_t ***ret_query_list, size_t *ret_query_list_len, + oconfig_item_t *ci, udb_query_create_callback_t cb); +void udb_query_free(udb_query_t **query_list, size_t query_list_len); + +int udb_query_pick_from_list_by_name(const char *name, udb_query_t **src_list, + size_t src_list_len, + udb_query_t ***dst_list, + size_t *dst_list_len); +int udb_query_pick_from_list(oconfig_item_t *ci, udb_query_t **src_list, + size_t src_list_len, udb_query_t ***dst_list, + size_t *dst_list_len); + +const char *udb_query_get_name(udb_query_t *q); +const char *udb_query_get_statement(udb_query_t *q); + +void udb_query_set_user_data(udb_query_t *q, void *user_data); +void *udb_query_get_user_data(udb_query_t *q); + +/* + * udb_query_check_version + * + * Returns 0 if the query is NOT suitable for `version' and >0 if the + * query IS suitable. + */ +int udb_query_check_version(udb_query_t *q, unsigned int version); + +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); +int udb_query_handle_result(udb_query_t const *q, + udb_query_preparation_area_t *prep_area, + char **column_values); +void udb_query_finish_result(udb_query_t const *q, + udb_query_preparation_area_t *prep_area); + +udb_query_preparation_area_t * +udb_query_allocate_preparation_area(udb_query_t *q); +void udb_query_delete_preparation_area(udb_query_preparation_area_t *q_area); + +#endif /* UTILS_DB_QUERY_H */ diff --git a/src/utils/deq/deq.h b/src/utils/deq/deq.h new file mode 100644 index 00000000..3182baae --- /dev/null +++ b/src/utils/deq/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/dns.c b/src/utils/dns/dns.c new file mode 100644 index 00000000..981828d7 --- /dev/null +++ b/src/utils/dns/dns.c @@ -0,0 +1,1077 @@ +/* + * collectd - src/utils_dns.c + * Copyright (C) 2006 Florian octo Forster + * Copyright (C) 2002 The Measurement Factory, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of The Measurement Factory nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * The Measurement Factory, Inc. + * Florian octo Forster + */ + +#define _DEFAULT_SOURCE +#define _BSD_SOURCE + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" + +#if HAVE_NET_IF_ARP_H +#include +#endif +#if HAVE_NET_IF_H +#include +#endif +#if HAVE_NET_PPP_DEFS_H +#include +#endif +#if HAVE_NET_IF_PPP_H +#include +#endif + +#if HAVE_NETINET_IN_SYSTM_H +#include +#endif +#if HAVE_NETINET_IN_H +#include +#endif +#if HAVE_NETINET_IP6_H +#include +#endif +#if HAVE_NETINET_IF_ETHER_H +#include +#endif +#if HAVE_NETINET_IP_H +#include +#endif +#ifdef HAVE_NETINET_IP_VAR_H +#include +#endif +#if HAVE_NETINET_UDP_H +#include +#endif + +#if HAVE_ARPA_INET_H +#include +#endif + +#if HAVE_NETDB_H +#include +#endif + +#if HAVE_PCAP_H +#include +#endif + +#define PCAP_SNAPLEN 1460 +#ifndef ETHER_HDR_LEN +#define ETHER_ADDR_LEN 6 +#define ETHER_TYPE_LEN 2 +#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) +#endif +#ifndef ETHERTYPE_8021Q +#define ETHERTYPE_8021Q 0x8100 +#endif +#ifndef ETHERTYPE_IPV6 +#define ETHERTYPE_IPV6 0x86DD +#endif + +#ifndef PPP_ADDRESS_VAL +#define PPP_ADDRESS_VAL 0xff /* The address byte value */ +#endif +#ifndef PPP_CONTROL_VAL +#define PPP_CONTROL_VAL 0x03 /* The control byte value */ +#endif + +#if HAVE_STRUCT_UDPHDR_UH_DPORT && HAVE_STRUCT_UDPHDR_UH_SPORT +#define UDP_DEST uh_dport +#define UDP_SRC uh_sport +#elif HAVE_STRUCT_UDPHDR_DEST && HAVE_STRUCT_UDPHDR_SOURCE +#define UDP_DEST dest +#define UDP_SRC source +#else +#error "`struct udphdr' is unusable." +#endif + +#if HAVE_NETINET_IP6_H && HAVE_STRUCT_IP6_EXT +#define HAVE_IPV6 1 +#endif + +#include "utils/dns/dns.h" + +/* + * Type definitions + */ +struct ip_list_s { + struct in6_addr addr; + void *data; + struct ip_list_s *next; +}; +typedef struct ip_list_s ip_list_t; + +typedef int(printer)(const char *, ...); + +/* + * flags/features for non-interactive mode + */ + +#ifndef T_A6 +#define T_A6 38 +#endif +#ifndef T_SRV +#define T_SRV 33 +#endif + +/* + * Global variables + */ + +#if HAVE_PCAP_H +static pcap_t *pcap_obj; +#endif + +static ip_list_t *IgnoreList; + +#if HAVE_PCAP_H +static void (*Callback)(const rfc1035_header_t *); + +static int query_count_intvl; +static int query_count_total; +#ifdef __OpenBSD__ +static struct bpf_timeval last_ts; +#else +static struct timeval last_ts; +#endif /* __OpenBSD__ */ +#endif /* HAVE_PCAP_H */ + +static int cmp_in6_addr(const struct in6_addr *a, const struct in6_addr *b) { + int i; + + assert(sizeof(struct in6_addr) == 16); + + for (i = 0; i < 16; i++) + if (a->s6_addr[i] != b->s6_addr[i]) + break; + + if (i >= 16) + return 0; + + return a->s6_addr[i] > b->s6_addr[i] ? 1 : -1; +} /* int cmp_addrinfo */ + +static inline int ignore_list_match(const struct in6_addr *addr) { + for (ip_list_t *ptr = IgnoreList; ptr != NULL; ptr = ptr->next) + if (cmp_in6_addr(addr, &ptr->addr) == 0) + return 1; + return 0; +} /* int ignore_list_match */ + +static void ignore_list_add(const struct in6_addr *addr) { + ip_list_t *new; + + if (ignore_list_match(addr) != 0) + return; + + new = malloc(sizeof(*new)); + if (new == NULL) { + perror("malloc"); + return; + } + + memcpy(&new->addr, addr, sizeof(struct in6_addr)); + new->next = IgnoreList; + + IgnoreList = new; +} /* void ignore_list_add */ + +void ignore_list_add_name(const char *name) { + struct addrinfo *ai_list; + struct in6_addr addr; + int status; + + status = getaddrinfo(name, NULL, NULL, &ai_list); + if (status != 0) + return; + + for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL; + ai_ptr = ai_ptr->ai_next) { + if (ai_ptr->ai_family == AF_INET) { + memset(&addr, '\0', sizeof(addr)); + addr.s6_addr[10] = 0xFF; + addr.s6_addr[11] = 0xFF; + memcpy(addr.s6_addr + 12, + &((struct sockaddr_in *)ai_ptr->ai_addr)->sin_addr, 4); + + ignore_list_add(&addr); + } else { + ignore_list_add(&((struct sockaddr_in6 *)ai_ptr->ai_addr)->sin6_addr); + } + } /* for */ + + freeaddrinfo(ai_list); +} + +#if HAVE_PCAP_H +static void in6_addr_from_buffer(struct in6_addr *ia, const void *buf, + size_t buf_len, int family) { + memset(ia, 0, sizeof(struct in6_addr)); + if ((AF_INET == family) && (sizeof(uint32_t) == buf_len)) { + ia->s6_addr[10] = 0xFF; + ia->s6_addr[11] = 0xFF; + memcpy(ia->s6_addr + 12, buf, buf_len); + } else if ((AF_INET6 == family) && (sizeof(struct in6_addr) == buf_len)) { + memcpy(ia, buf, buf_len); + } +} /* void in6_addr_from_buffer */ + +void dnstop_set_pcap_obj(pcap_t *po) { pcap_obj = po; } + +void dnstop_set_callback(void (*cb)(const rfc1035_header_t *)) { + Callback = cb; +} + +#define RFC1035_MAXLABELSZ 63 +static int rfc1035NameUnpack(const char *buf, size_t sz, off_t *off, char *name, + size_t ns) { + off_t no = 0; + unsigned char c; + size_t len; + static int loop_detect; + if (loop_detect > 2) + return 4; /* compression loop */ + if (ns == 0) + return 4; /* probably compression loop */ + do { + if ((*off) >= ((off_t)sz)) + break; + c = *(buf + (*off)); + if (c > 191) { + /* blasted compression */ + int rc; + unsigned short s; + off_t ptr; + memcpy(&s, buf + (*off), sizeof(s)); + s = ntohs(s); + (*off) += sizeof(s); + /* Sanity check */ + if ((*off) >= ((off_t)sz)) + return 1; /* message too short */ + ptr = s & 0x3FFF; + /* Make sure the pointer is inside this message */ + if (ptr >= ((off_t)sz)) + return 2; /* bad compression ptr */ + if (ptr < DNS_MSG_HDR_SZ) + return 2; /* bad compression ptr */ + loop_detect++; + rc = rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no); + loop_detect--; + return rc; + } else if (c > RFC1035_MAXLABELSZ) { + /* + * "(The 10 and 01 combinations are reserved for future use.)" + */ + return 3; /* reserved label/compression flags */ + } else { + (*off)++; + len = (size_t)c; + if (len == 0) + break; + if (len > (ns - 1)) + len = ns - 1; + if ((*off) + len > sz) + return 4; /* message is too short */ + if (no + len + 1 > ns) + return 5; /* qname would overflow name buffer */ + memcpy(name + no, buf + (*off), len); + (*off) += len; + no += len; + *(name + (no++)) = '.'; + } + } while (c > 0); + if (no > 0) + *(name + no - 1) = '\0'; + /* make sure we didn't allow someone to overflow the name buffer */ + assert(no <= ((off_t)ns)); + return 0; +} + +static int handle_dns(const char *buf, int len) { + rfc1035_header_t qh; + uint16_t us; + off_t offset; + char *t; + int status; + + /* The DNS header is 12 bytes long */ + if (len < DNS_MSG_HDR_SZ) + return 0; + + memcpy(&us, buf + 0, 2); + qh.id = ntohs(us); + + memcpy(&us, buf + 2, 2); + us = ntohs(us); + qh.qr = (us >> 15) & 0x01; + qh.opcode = (us >> 11) & 0x0F; + qh.aa = (us >> 10) & 0x01; + qh.tc = (us >> 9) & 0x01; + qh.rd = (us >> 8) & 0x01; + qh.ra = (us >> 7) & 0x01; + qh.z = (us >> 6) & 0x01; + qh.ad = (us >> 5) & 0x01; + qh.cd = (us >> 4) & 0x01; + qh.rcode = us & 0x0F; + + memcpy(&us, buf + 4, 2); + qh.qdcount = ntohs(us); + + memcpy(&us, buf + 6, 2); + qh.ancount = ntohs(us); + + memcpy(&us, buf + 8, 2); + qh.nscount = ntohs(us); + + memcpy(&us, buf + 10, 2); + qh.arcount = ntohs(us); + + offset = DNS_MSG_HDR_SZ; + memset(qh.qname, '\0', MAX_QNAME_SZ); + status = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ); + if (status != 0) { + INFO("utils_dns: handle_dns: rfc1035NameUnpack failed " + "with status %i.", + status); + return 0; + } + if ('\0' == qh.qname[0]) + sstrncpy(qh.qname, ".", sizeof(qh.qname)); + while ((t = strchr(qh.qname, '\n'))) + *t = ' '; + while ((t = strchr(qh.qname, '\r'))) + *t = ' '; + for (t = qh.qname; *t; t++) + *t = tolower((int)*t); + + memcpy(&us, buf + offset, 2); + qh.qtype = ntohs(us); + memcpy(&us, buf + offset + 2, 2); + qh.qclass = ntohs(us); + + qh.length = (uint16_t)len; + + if (Callback != NULL) + Callback(&qh); + + return 1; +} + +static int handle_udp(const struct udphdr *udp, int len) { + char buf[PCAP_SNAPLEN]; + if ((ntohs(udp->UDP_DEST) != 53) && (ntohs(udp->UDP_SRC) != 53)) + return 0; + memcpy(buf, udp + 1, len - sizeof(*udp)); + if (0 == handle_dns(buf, len - sizeof(*udp))) + return 0; + return 1; +} + +#if HAVE_IPV6 +static int handle_ipv6(struct ip6_hdr *ipv6, int len) { + char buf[PCAP_SNAPLEN]; + unsigned int offset; + int nexthdr; + + struct in6_addr c_src_addr; + uint16_t payload_len; + + if (0 > len) + return 0; + + offset = sizeof(struct ip6_hdr); + nexthdr = ipv6->ip6_nxt; + c_src_addr = ipv6->ip6_src; + payload_len = ntohs(ipv6->ip6_plen); + + if (ignore_list_match(&c_src_addr)) + return 0; + + /* Parse extension headers. This only handles the standard headers, as + * defined in RFC 2460, correctly. Fragments are discarded. */ + while ((IPPROTO_ROUTING == nexthdr) /* routing header */ + || (IPPROTO_HOPOPTS == nexthdr) /* Hop-by-Hop options. */ + || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */ + || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */ + || (IPPROTO_AH == nexthdr) /* destination options. */ + || (IPPROTO_ESP == nexthdr)) /* encapsulating security payload. */ + { + struct ip6_ext ext_hdr; + uint16_t ext_hdr_len; + + /* Catch broken packets */ + if ((offset + sizeof(struct ip6_ext)) > (unsigned int)len) + return 0; + + /* Cannot handle fragments. */ + if (IPPROTO_FRAGMENT == nexthdr) + return 0; + + memcpy(&ext_hdr, (char *)ipv6 + offset, sizeof(struct ip6_ext)); + nexthdr = ext_hdr.ip6e_nxt; + ext_hdr_len = (8 * (ntohs(ext_hdr.ip6e_len) + 1)); + + /* This header is longer than the packets payload.. WTF? */ + if (ext_hdr_len > payload_len) + return 0; + + offset += ext_hdr_len; + payload_len -= ext_hdr_len; + } /* while */ + + /* Catch broken and empty packets */ + if (((offset + payload_len) > (unsigned int)len) || (payload_len == 0) || + (payload_len > PCAP_SNAPLEN)) + return 0; + + if (IPPROTO_UDP != nexthdr) + return 0; + + memcpy(buf, (char *)ipv6 + offset, payload_len); + if (handle_udp((struct udphdr *)buf, payload_len) == 0) + return 0; + + return 1; /* Success */ +} /* int handle_ipv6 */ + /* #endif HAVE_IPV6 */ + +#else /* if !HAVE_IPV6 */ +static int handle_ipv6(__attribute__((unused)) void *pkg, + __attribute__((unused)) int len) { + return 0; +} +#endif /* !HAVE_IPV6 */ + +static int handle_ip(const struct ip *ip, int len) { + char buf[PCAP_SNAPLEN]; + int offset = ip->ip_hl << 2; + struct in6_addr c_src_addr; + struct in6_addr c_dst_addr; + + if (ip->ip_v == 6) + return handle_ipv6((void *)ip, len); + + in6_addr_from_buffer(&c_src_addr, &ip->ip_src.s_addr, + sizeof(ip->ip_src.s_addr), AF_INET); + in6_addr_from_buffer(&c_dst_addr, &ip->ip_dst.s_addr, + sizeof(ip->ip_dst.s_addr), AF_INET); + if (ignore_list_match(&c_src_addr)) + return 0; + if (IPPROTO_UDP != ip->ip_p) + return 0; + memcpy(buf, ((char *)ip) + offset, len - offset); + if (0 == handle_udp((struct udphdr *)buf, len - offset)) + return 0; + return 1; +} + +#if HAVE_NET_IF_PPP_H +static int handle_ppp(const u_char *pkt, int len) { + char buf[PCAP_SNAPLEN]; + unsigned short us; + unsigned short proto; + if (len < 2) + return 0; + if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) { + pkt += 2; /* ACFC not used */ + len -= 2; + } + if (len < 2) + return 0; + if (*pkt % 2) { + proto = *pkt; /* PFC is used */ + pkt++; + len--; + } else { + memcpy(&us, pkt, sizeof(us)); + proto = ntohs(us); + pkt += 2; + len -= 2; + } + if (ETHERTYPE_IP != proto && PPP_IP != proto) + return 0; + memcpy(buf, pkt, len); + return handle_ip((struct ip *)buf, len); +} +#endif /* HAVE_NET_IF_PPP_H */ + +static int handle_null(const u_char *pkt, int len) { + unsigned int family; + memcpy(&family, pkt, sizeof(family)); + if (AF_INET != family) + return 0; + return handle_ip((struct ip *)(pkt + 4), len - 4); +} + +#ifdef DLT_LOOP +static int handle_loop(const u_char *pkt, int len) { + unsigned int family; + memcpy(&family, pkt, sizeof(family)); + if (AF_INET != ntohl(family)) + return 0; + return handle_ip((struct ip *)(pkt + 4), len - 4); +} + +#endif + +#ifdef DLT_RAW +static int handle_raw(const u_char *pkt, int len) { + return handle_ip((struct ip *)pkt, len); +} + +#endif + +static int handle_ether(const u_char *pkt, int len) { + char buf[PCAP_SNAPLEN]; + struct ether_header *e = (void *)pkt; + unsigned short etype = ntohs(e->ether_type); + if (len < ETHER_HDR_LEN) + return 0; + pkt += ETHER_HDR_LEN; + len -= ETHER_HDR_LEN; + if (ETHERTYPE_8021Q == etype) { + etype = ntohs(*(unsigned short *)(pkt + 2)); + pkt += 4; + len -= 4; + } + if ((ETHERTYPE_IP != etype) && (ETHERTYPE_IPV6 != etype)) + return 0; + memcpy(buf, pkt, len); + if (ETHERTYPE_IPV6 == etype) + return handle_ipv6((void *)buf, len); + else + return handle_ip((struct ip *)buf, len); +} + +#ifdef DLT_LINUX_SLL +static int handle_linux_sll(const u_char *pkt, int len) { + struct sll_header { + uint16_t pkt_type; + uint16_t dev_type; + uint16_t addr_len; + uint8_t addr[8]; + uint16_t proto_type; + } * hdr; + uint16_t etype; + + if ((0 > len) || ((unsigned int)len < sizeof(struct sll_header))) + return 0; + + hdr = (struct sll_header *)pkt; + pkt = (u_char *)(hdr + 1); + len -= sizeof(struct sll_header); + + etype = ntohs(hdr->proto_type); + + if ((ETHERTYPE_IP != etype) && (ETHERTYPE_IPV6 != etype)) + return 0; + + if (ETHERTYPE_IPV6 == etype) + return handle_ipv6((void *)pkt, len); + else + return handle_ip((struct ip *)pkt, len); +} +#endif /* DLT_LINUX_SLL */ + +/* public function */ +void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, + const u_char *pkt) { + int status; + + if (hdr->caplen < ETHER_HDR_LEN) + return; + + switch (pcap_datalink(pcap_obj)) { + case DLT_EN10MB: + status = handle_ether(pkt, hdr->caplen); + break; +#if HAVE_NET_IF_PPP_H + case DLT_PPP: + status = handle_ppp(pkt, hdr->caplen); + break; +#endif +#ifdef DLT_LOOP + case DLT_LOOP: + status = handle_loop(pkt, hdr->caplen); + break; +#endif +#ifdef DLT_RAW + case DLT_RAW: + status = handle_raw(pkt, hdr->caplen); + break; +#endif +#ifdef DLT_LINUX_SLL + case DLT_LINUX_SLL: + status = handle_linux_sll(pkt, hdr->caplen); + break; +#endif + case DLT_NULL: + status = handle_null(pkt, hdr->caplen); + break; + + default: + ERROR("handle_pcap: unsupported data link type %d", + pcap_datalink(pcap_obj)); + status = 0; + break; + } /* switch (pcap_datalink(pcap_obj)) */ + + if (0 == status) + return; + + query_count_intvl++; + query_count_total++; + last_ts = hdr->ts; +} +#endif /* HAVE_PCAP_H */ + +const char *qtype_str(int t) { + static char buf[32]; + // clang-format off + /* + Built (with minor cleanup) from glibc-2.29 by + cat resolv/arpa/nameser.h | grep "ns_t_" | \ + perl -ne '/ns_t_(\S+)\ =\ (\d+)/; print " case $2:\n return \"".uc($1)."\";\n";' + */ + // clang-format on + switch (t) { + case 1: + return "A"; + case 2: + return "NS"; + case 3: + return "MD"; + case 4: + return "MF"; + case 5: + return "CNAME"; + case 6: + return "SOA"; + case 7: + return "MB"; + case 8: + return "MG"; + case 9: + return "MR"; + case 10: + return "NULL"; + case 11: + return "WKS"; + case 12: + return "PTR"; + case 13: + return "HINFO"; + case 14: + return "MINFO"; + case 15: + return "MX"; + case 16: + return "TXT"; + case 17: + return "RP"; + case 18: + return "AFSDB"; + case 19: + return "X25"; + case 20: + return "ISDN"; + case 21: + return "RT"; + case 22: + return "NSAP"; + case 23: + return "NSAP-PTR"; + case 24: + return "SIG"; + case 25: + return "KEY"; + case 26: + return "PX"; + case 27: + return "GPOS"; + case 28: + return "AAAA"; + case 29: + return "LOC"; + case 30: + return "NXT"; + case 31: + return "EID"; + case 32: + return "NIMLOC"; + case 33: + return "SRV"; + case 34: + return "ATMA"; + case 35: + return "NAPTR"; + case 36: + return "KX"; + case 37: + return "CERT"; + case 38: + return "A6"; + case 39: + return "DNAME"; + case 40: + return "SINK"; + case 41: + return "OPT"; + case 42: + return "APL"; + case 43: + return "DS"; + case 44: + return "SSHFP"; + case 45: + return "IPSECKEY"; + case 46: + return "RRSIG"; + case 47: + return "NSEC"; + case 48: + return "DNSKEY"; + case 49: + return "DHCID"; + case 50: + return "NSEC3"; + case 51: + return "NSEC3PARAM"; + case 52: + return "TLSA"; + case 53: + return "SMIMEA"; + case 55: + return "HIP"; + case 56: + return "NINFO"; + case 57: + return "RKEY"; + case 58: + return "TALINK"; + case 59: + return "CDS"; + case 60: + return "CDNSKEY"; + case 61: + return "OPENPGPKEY"; + case 62: + return "CSYNC"; + case 99: + return "SPF"; + case 100: + return "UINFO"; + case 101: + return "UID"; + case 102: + return "GID"; + case 103: + return "UNSPEC"; + case 104: + return "NID"; + case 105: + return "L32"; + case 106: + return "L64"; + case 107: + return "LP"; + case 108: + return "EUI48"; + case 109: + return "EUI64"; + case 249: + return "TKEY"; + case 250: + return "TSIG"; + case 251: + return "IXFR"; + case 252: + return "AXFR"; + case 253: + return "MAILB"; + case 254: + return "MAILA"; + case 255: + return "ANY"; + case 256: + return "URI"; + case 257: + return "CAA"; + case 258: + return "AVC"; + case 32768: + return "TA"; + case 32769: + return "DLV"; + default: + ssnprintf(buf, sizeof(buf), "#%i", t); + return buf; + } /* switch (t) */ +} + +const char *opcode_str(int o) { + static char buf[30]; + switch (o) { + case 0: + return "Query"; + case 1: + return "Iquery"; + case 2: + return "Status"; + case 4: + return "Notify"; + case 5: + return "Update"; + default: + ssnprintf(buf, sizeof(buf), "Opcode%d", o); + return buf; + } +} + +const char *rcode_str(int rcode) { + static char buf[32]; + /* RFC2136 rcodes */ + // clang-format off + /* + Built (with minor cleanup) from glibc-2.29 by + cat resolv/arpa/nameser.h | grep "ns_r_" | \ + perl -ne '/ns_r_(\S+)\ =\ (\d+)/; print " case $2:\n return \"".uc($1)."\";\n";' + + https://tools.ietf.org/html/rfc2671 assigns EDNS Extended RCODE "16" to "BADVERS" + https://tools.ietf.org/html/rfc2845 declares 0..15 as DNS RCODE and 16 is BADSIG. + */ + // clang-format on + switch (rcode) { + case 1: + return "FORMERR"; + case 2: + return "SERVFAIL"; + case 3: + return "NXDOMAIN"; + case 4: + return "NOTIMPL"; + case 5: + return "REFUSED"; + case 6: + return "YXDOMAIN"; + case 7: + return "YXRRSET"; + case 8: + return "NXRRSET"; + case 9: + return "NOTAUTH"; + case 10: + return "NOTZONE"; + case 11: + return "MAX"; + case 16: + return "BADSIG"; + case 17: + return "BADKEY"; + case 18: + return "BADTIME"; + default: + ssnprintf(buf, sizeof(buf), "RCode%i", rcode); + return buf; + } +} /* const char *rcode_str (int rcode) */ + +#if 0 +static int +main(int argc, char *argv[]) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + int x; + struct stat sb; + int readfile_state = 0; + struct bpf_program fp; + + port53 = htons(53); + SubReport = Sources_report; + ignore_addr.s_addr = 0; + progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]); + srandom(time(NULL)); + ResetCounters(); + + while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) { + switch (x) { + case 'a': + anon_flag = 1; + break; + case 's': + sld_flag = 1; + break; + case 't': + nld_flag = 1; + break; + case 'p': + promisc_flag = 0; + break; + case 'b': + bpf_program_str = strdup(optarg); + break; + case 'i': + ignore_addr.s_addr = inet_addr(optarg); + break; + case 'f': + set_filter(optarg); + break; + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + device = strdup(argv[0]); + + if (0 == stat(device, &sb)) + readfile_state = 1; + if (readfile_state) { + pcap_obj = pcap_open_offline(device, errbuf); + } else { + pcap_obj = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf); + } + if (NULL == pcap_obj) { + fprintf(stderr, "pcap_open_*: %s\n", errbuf); + exit(1); + } + + if (0 == isatty(1)) { + if (0 == readfile_state) { + fprintf(stderr, "Non-interactive mode requires savefile argument\n"); + exit(1); + } + interactive = 0; + print_func = printf; + } + + memset(&fp, '\0', sizeof(fp)); + x = pcap_compile(pcap_obj, &fp, bpf_program_str, 1, 0); + if (x < 0) { + fprintf(stderr, "pcap_compile failed\n"); + exit(1); + } + x = pcap_setfilter(pcap_obj, &fp); + if (x < 0) { + fprintf(stderr, "pcap_setfilter failed\n"); + exit(1); + } + + /* + * non-blocking call added for Mac OS X bugfix. Sent by Max Horn. + * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html + */ + x = pcap_setnonblock(pcap_obj, 1, errbuf); + if (x < 0) { + fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf); + exit(1); + } + + switch (pcap_datalink(pcap_obj)) { + case DLT_EN10MB: + handle_datalink = handle_ether; + break; +#if HAVE_NET_IF_PPP_H + case DLT_PPP: + handle_datalink = handle_ppp; + break; +#endif +#ifdef DLT_LOOP + case DLT_LOOP: + handle_datalink = handle_loop; + break; +#endif +#ifdef DLT_RAW + case DLT_RAW: + handle_datalink = handle_raw; + break; +#endif + case DLT_NULL: + handle_datalink = handle_null; + break; + default: + fprintf(stderr, "unsupported data link type %d\n", + pcap_datalink(pcap_obj)); + return 1; + break; + } + if (interactive) { + init_curses(); + while (0 == Quit) { + if (readfile_state < 2) { + /* + * On some OSes select() might return 0 even when + * there are packets to process. Thus, we always + * ignore its return value and just call pcap_dispatch() + * anyway. + */ + if (0 == readfile_state) /* interactive */ + pcap_select(pcap_obj, 1, 0); + x = pcap_dispatch(pcap_obj, 50, handle_pcap, NULL); + } + if (0 == x && 1 == readfile_state) { + /* block on keyboard until user quits */ + readfile_state++; + nodelay(w, 0); + } + keyboard(); + cron_pre(); + report(); + cron_post(); + } + endwin(); /* klin, Thu Nov 28 08:56:51 2002 */ + } else { + while (pcap_dispatch(pcap_obj, 50, handle_pcap, NULL)) + (void) 0; + cron_pre(); + Sources_report(); print_func("\n"); + Destinatioreport(); print_func("\n"); + Qtypes_report(); print_func("\n"); + Opcodes_report(); print_func("\n"); + Tld_report(); print_func("\n"); + Sld_report(); print_func("\n"); + Nld_report(); print_func("\n"); + SldBySource_report(); + } + + pcap_close(pcap_obj); + return 0; +} /* static int main(int argc, char *argv[]) */ +#endif diff --git a/src/utils/dns/dns.h b/src/utils/dns/dns.h new file mode 100644 index 00000000..9cc49b6c --- /dev/null +++ b/src/utils/dns/dns.h @@ -0,0 +1,90 @@ +/* + * collectd - src/utils_dns.h + * Copyright (C) 2006 Florian octo Forster + * Copyright (C) 2002 The Measurement Factory, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of The Measurement Factory nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * The Measurement Factory, Inc. + * Florian octo Forster + */ + +#ifndef COLLECTD_UTILS_DNS_H +#define COLLECTD_UTILS_DNS_H 1 + +#include "config.h" + +#include + +#if HAVE_PCAP_H +#include +#endif + +#define DNS_MSG_HDR_SZ 12 + +#define T_MAX 65536 +#define MAX_QNAME_SZ 512 + +struct rfc1035_header_s { + uint16_t id; + unsigned int qr : 1; + unsigned int opcode : 4; + unsigned int aa : 1; + unsigned int tc : 1; + unsigned int rd : 1; + unsigned int ra : 1; + unsigned int z : 1; + unsigned int ad : 1; + unsigned int cd : 1; + unsigned int rcode : 4; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; + uint16_t qtype; + uint16_t qclass; + char qname[MAX_QNAME_SZ]; + uint16_t length; +}; +typedef struct rfc1035_header_s rfc1035_header_t; + +#if HAVE_PCAP_H +void dnstop_set_pcap_obj(pcap_t *po); +#endif +void dnstop_set_callback(void (*cb)(const rfc1035_header_t *)); + +void ignore_list_add_name(const char *name); +#if HAVE_PCAP_H +void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, + const u_char *pkt); +#endif + +const char *qtype_str(int t); +const char *opcode_str(int o); +const char *rcode_str(int r); + +#endif /* !COLLECTD_UTILS_DNS_H */ diff --git a/src/utils/dpdk/dpdk.c b/src/utils/dpdk/dpdk.c new file mode 100644 index 00000000..7d9f7abc --- /dev/null +++ b/src/utils/dpdk/dpdk.c @@ -0,0 +1,886 @@ +/* + * collectd - src/utils_dpdk.c + * MIT License + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Maryam Tahhan + * Harry van Haaren + * Taras Chornyi + * Serhiy Pshyk + * Krzysztof Matczak + */ + +#include "collectd.h" + +#include +#include +#include + +#include +#include +#include + +#include "utils/common/common.h" +#include "utils/dpdk/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 +// we need to limit DPDK_MAX_BUFFER_SIZE value. +#define DPDK_MAX_BUFFER_SIZE 896 +#define DPDK_CDM_DEFAULT_TIMEOUT 10000 + +enum DPDK_HELPER_STATUS { + DPDK_HELPER_NOT_INITIALIZED = 0, + DPDK_HELPER_INITIALIZING, + DPDK_HELPER_WAITING_ON_PRIMARY, + DPDK_HELPER_INITIALIZING_EAL, + DPDK_HELPER_ALIVE_SENDING_EVENTS, + DPDK_HELPER_GRACEFUL_QUIT, +}; + +#define DPDK_HELPER_TRACE(_name) \ + DEBUG("%s:%s:%d pid=%ld", _name, __FUNCTION__, __LINE__, (long)getpid()) + +struct dpdk_helper_ctx_s { + + dpdk_eal_config_t eal_config; + int eal_initialized; + + size_t shm_size; + char shm_name[DATA_MAX_NAME_LEN]; + + sem_t sema_cmd_start; + sem_t sema_cmd_complete; + cdtime_t cmd_wait_time; + + pid_t pid; + int pipes[2]; + int status; + + int cmd; + int cmd_result; + + char priv_data[]; +}; + +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); + +static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc); +static int dpdk_helper_worker(dpdk_helper_ctx_t *phc); +static int dpdk_helper_eal_init(dpdk_helper_ctx_t *phc); +static int dpdk_helper_cmd_wait(dpdk_helper_ctx_t *phc, pid_t ppid); +static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc, + enum DPDK_HELPER_STATUS status); +static int dpdk_helper_exit(dpdk_helper_ctx_t *phc, + enum DPDK_HELPER_STATUS status); +static int dpdk_helper_status_check(dpdk_helper_ctx_t *phc); +static void dpdk_helper_config_default(dpdk_helper_ctx_t *phc); +static const char *dpdk_helper_status_str(enum DPDK_HELPER_STATUS status); + +static void dpdk_helper_config_default(dpdk_helper_ctx_t *phc) { + if (phc == NULL) + return; + + DPDK_HELPER_TRACE(phc->shm_name); + + ssnprintf(phc->eal_config.coremask, DATA_MAX_NAME_LEN, "%s", "0xf"); + ssnprintf(phc->eal_config.memory_channels, DATA_MAX_NAME_LEN, "%s", "1"); + ssnprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN, "%s", + DPDK_DEFAULT_RTE_CONFIG); +} + +int dpdk_helper_eal_config_set(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) { + if (phc == NULL) { + ERROR("Invalid argument (phc)"); + return -EINVAL; + } + + DPDK_HELPER_TRACE(phc->shm_name); + + if (ec == NULL) { + ERROR("Invalid argument (ec)"); + return -EINVAL; + } + + memcpy(&phc->eal_config, ec, sizeof(phc->eal_config)); + + return 0; +} + +int dpdk_helper_eal_config_get(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) { + if (phc == NULL) { + ERROR("Invalid argument (phc)"); + return -EINVAL; + } + + DPDK_HELPER_TRACE(phc->shm_name); + + if (ec == NULL) { + ERROR("Invalid argument (ec)"); + return -EINVAL; + } + + memcpy(ec, &phc->eal_config, sizeof(*ec)); + + return 0; +} + +int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci) { + DPDK_HELPER_TRACE(phc->shm_name); + + if (phc == NULL) { + ERROR("Invalid argument (phc)"); + return -EINVAL; + } + + if (ci == NULL) { + ERROR("Invalid argument (ci)"); + return -EINVAL; + } + + int status = 0; + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("Coremask", child->key) == 0) { + status = cf_util_get_string_buffer(child, phc->eal_config.coremask, + sizeof(phc->eal_config.coremask)); + DEBUG("dpdk_common: EAL:Coremask %s", phc->eal_config.coremask); + } else if (strcasecmp("MemoryChannels", child->key) == 0) { + status = + cf_util_get_string_buffer(child, phc->eal_config.memory_channels, + sizeof(phc->eal_config.memory_channels)); + DEBUG("dpdk_common: EAL:Memory Channels %s", + phc->eal_config.memory_channels); + } else if (strcasecmp("SocketMemory", child->key) == 0) { + status = cf_util_get_string_buffer(child, phc->eal_config.socket_memory, + sizeof(phc->eal_config.socket_memory)); + DEBUG("dpdk_common: EAL:Socket memory %s", phc->eal_config.socket_memory); + } else if (strcasecmp("FilePrefix", child->key) == 0) { + char prefix[DATA_MAX_NAME_LEN]; + + status = cf_util_get_string_buffer(child, prefix, sizeof(prefix)); + if (status == 0) { +#if RTE_VERSION <= RTE_VERSION_NUM(18, 5, 0, 0) + ssnprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN, + "/var/run/.%s_config", prefix); +#else + ssnprintf(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) { + status = cf_util_get_string_buffer(child, phc->eal_config.log_level, + sizeof(phc->eal_config.log_level)); + DEBUG("dpdk_common: EAL:LogLevel %s", phc->eal_config.log_level); + } else if (strcasecmp("RteDriverLibPath", child->key) == 0) { + status = cf_util_get_string_buffer( + child, phc->eal_config.rte_driver_lib_path, + sizeof(phc->eal_config.rte_driver_lib_path)); + DEBUG("dpdk_common: EAL:RteDriverLibPath %s", + phc->eal_config.rte_driver_lib_path); + } else { + ERROR("dpdk_common: Invalid '%s' configuration option", child->key); + status = -EINVAL; + } + + if (status != 0) { + ERROR("dpdk_common: Parsing EAL configuration failed"); + break; + } + } + + return status; +} + +static int dpdk_shm_init(const char *name, size_t size, void **map) { + DPDK_HELPER_TRACE(name); + + 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, STRERRNO); + *map = NULL; + return -1; + } + + int ret = ftruncate(fd, size); + if (ret != 0) { + WARNING("dpdk_shm_init: Failed to resize SHM:%s", STRERRNO); + close(fd); + *map = NULL; + dpdk_shm_cleanup(name, size, NULL); + return -1; + } + + *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", STRERRNO); + close(fd); + *map = NULL; + dpdk_shm_cleanup(name, size, NULL); + return -1; + } + /* File descriptor no longer needed for shared memory operations */ + close(fd); + memset(*map, 0, size); + + return 0; +} + +static void dpdk_shm_cleanup(const char *name, size_t size, void *map) { + DPDK_HELPER_TRACE(name); + + /* + * Call shm_unlink first, as 'name' might be no longer accessible after munmap + */ + if (shm_unlink(name)) + ERROR("shm_unlink failure %s", STRERRNO); + + if (map != NULL) { + if (munmap(map, size)) + ERROR("munmap failure %s", STRERRNO); + } +} + +void *dpdk_helper_priv_get(dpdk_helper_ctx_t *phc) { + if (phc) + return phc->priv_data; + + return NULL; +} + +int dpdk_helper_data_size_get(dpdk_helper_ctx_t *phc) { + if (phc == NULL) { + DPDK_CHILD_LOG("Invalid argument(phc)\n"); + return -EINVAL; + } + + return phc->shm_size - sizeof(dpdk_helper_ctx_t); +} + +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; + + if (pphc == NULL) { + ERROR("%s:Invalid argument(pphc)", __FUNCTION__); + return -EINVAL; + } + + if (name == NULL) { + ERROR("%s:Invalid argument(name)", __FUNCTION__); + return -EINVAL; + } + + DPDK_HELPER_TRACE(name); + + /* Allocate dpdk_helper_ctx_t and + * initialize a POSIX SHared Memory (SHM) object. + */ + int err = dpdk_shm_init(name, shm_size, (void **)&phc); + if (err != 0) { + return -errno; + } + + err = sem_init(&phc->sema_cmd_start, 1, 0); + if (err != 0) { + ERROR("sema_cmd_start semaphore init failed: %s", STRERRNO); + int errno_m = errno; + dpdk_shm_cleanup(name, shm_size, (void *)phc); + return -errno_m; + } + + err = sem_init(&phc->sema_cmd_complete, 1, 0); + if (err != 0) { + 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); + return -errno_m; + } + + phc->shm_size = shm_size; + sstrncpy(phc->shm_name, name, sizeof(phc->shm_name)); + + dpdk_helper_config_default(phc); + + *pphc = phc; + + return 0; +} + +void dpdk_helper_shutdown(dpdk_helper_ctx_t *phc) { + if (phc == NULL) + return; + + DPDK_HELPER_TRACE(phc->shm_name); + + close(phc->pipes[1]); + + if (phc->status != DPDK_HELPER_NOT_INITIALIZED) { + dpdk_helper_exit_command(phc, DPDK_HELPER_GRACEFUL_QUIT); + } + + sem_destroy(&phc->sema_cmd_start); + sem_destroy(&phc->sema_cmd_complete); + dpdk_shm_cleanup(phc->shm_name, phc->shm_size, (void *)phc); +} + +static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) { + if (phc == NULL) { + ERROR("Invalid argument(phc)"); + return -EINVAL; + } + + DPDK_HELPER_TRACE(phc->shm_name); + + phc->eal_initialized = 0; + phc->cmd_wait_time = MS_TO_CDTIME_T(DPDK_CDM_DEFAULT_TIMEOUT); + + /* + * Create a pipe for helper stdout back to collectd. This is necessary for + * logging EAL failures, as rte_eal_init() calls rte_panic(). + */ + if (phc->pipes[1]) { + DEBUG("dpdk_helper_spawn: collectd closing helper pipe %d", phc->pipes[1]); + } else { + DEBUG("dpdk_helper_spawn: collectd helper pipe %d, not closing", + phc->pipes[1]); + } + + if (pipe(phc->pipes) != 0) { + 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", 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", STRERRNO); + } + + pid_t pid = fork(); + if (pid > 0) { + phc->pid = pid; + close(phc->pipes[1]); + DEBUG("%s:dpdk_helper_spawn: helper pid %lu", phc->shm_name, + (long)phc->pid); + } else if (pid == 0) { + /* Replace stdout with a pipe to collectd. */ + close(phc->pipes[0]); + close(STDOUT_FILENO); + dup2(phc->pipes[1], STDOUT_FILENO); + DPDK_CHILD_TRACE(phc->shm_name); + dpdk_helper_worker(phc); + exit(0); + } else { + ERROR("dpdk_helper_start: Failed to fork helper process: %s", STRERRNO); + return -1; + } + + return 0; +} + +static int dpdk_helper_exit(dpdk_helper_ctx_t *phc, + enum DPDK_HELPER_STATUS status) { + DPDK_CHILD_LOG("%s:%s:%d %s\n", phc->shm_name, __FUNCTION__, __LINE__, + dpdk_helper_status_str(status)); + + close(phc->pipes[1]); + + phc->status = status; + + exit(0); + + return 0; +} + +static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc, + enum DPDK_HELPER_STATUS status) { + DPDK_HELPER_TRACE(phc->shm_name); + + close(phc->pipes[1]); + + if (phc->status == DPDK_HELPER_ALIVE_SENDING_EVENTS) { + phc->status = status; + DEBUG("%s:%s:%d %s", phc->shm_name, __FUNCTION__, __LINE__, + dpdk_helper_status_str(status)); + + int ret = dpdk_helper_command(phc, DPDK_CMD_QUIT, NULL, 0); + if (ret != 0) { + DEBUG("%s:%s:%d kill helper (pid=%lu)", phc->shm_name, __FUNCTION__, + __LINE__, (long)phc->pid); + + int err = kill(phc->pid, SIGKILL); + if (err) { + ERROR("%s error sending kill to helper: %s", __FUNCTION__, STRERRNO); + } + } + } else { + + DEBUG("%s:%s:%d kill helper (pid=%lu)", phc->shm_name, __FUNCTION__, + __LINE__, (long)phc->pid); + + int err = kill(phc->pid, SIGKILL); + if (err) { + ERROR("%s error sending kill to helper: %s", __FUNCTION__, STRERRNO); + } + } + + return 0; +} + +static int dpdk_helper_eal_init(dpdk_helper_ctx_t *phc) { + phc->status = DPDK_HELPER_INITIALIZING_EAL; + DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_INITIALIZING_EAL (start)\n", + phc->shm_name, __FUNCTION__, __LINE__); + + char *argp[DPDK_EAL_ARGC * 2 + 1]; + int argc = 0; + + /* EAL config must be initialized */ + assert(phc->eal_config.coremask[0] != 0); + assert(phc->eal_config.memory_channels[0] != 0); + assert(phc->eal_config.file_prefix[0] != 0); + + argp[argc++] = "collectd-dpdk"; + + argp[argc++] = "-c"; + argp[argc++] = phc->eal_config.coremask; + + argp[argc++] = "-n"; + argp[argc++] = phc->eal_config.memory_channels; + + if (strcasecmp(phc->eal_config.socket_memory, "") != 0) { + argp[argc++] = "--socket-mem"; + argp[argc++] = phc->eal_config.socket_memory; + } + + if (strcasecmp(phc->eal_config.file_prefix, DPDK_DEFAULT_RTE_CONFIG) != 0) { + argp[argc++] = "--file-prefix"; + argp[argc++] = phc->eal_config.file_prefix; + } + + argp[argc++] = "--proc-type"; + argp[argc++] = "secondary"; + + if (strcasecmp(phc->eal_config.log_level, "") != 0) { + argp[argc++] = "--log-level"; + argp[argc++] = phc->eal_config.log_level; + } + if (strcasecmp(phc->eal_config.rte_driver_lib_path, "") != 0) { + argp[argc++] = "-d"; + argp[argc++] = phc->eal_config.rte_driver_lib_path; + } + + assert(argc <= (DPDK_EAL_ARGC * 2 + 1)); + + int ret = rte_eal_init(argc, argp); + + if (ret < 0) { + + phc->eal_initialized = 0; + + DPDK_CHILD_LOG("dpdk_helper_eal_init: ERROR initializing EAL ret=%d\n", + ret); + + printf("dpdk_helper_eal_init: EAL arguments: "); + for (int i = 0; i < argc; i++) { + printf("%s ", argp[i]); + } + printf("\n"); + + return ret; + } + + phc->eal_initialized = 1; + + DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_INITIALIZING_EAL (done)\n", + phc->shm_name, __FUNCTION__, __LINE__); + + return 0; +} + +static int dpdk_helper_cmd_wait(dpdk_helper_ctx_t *phc, pid_t ppid) { + DPDK_CHILD_TRACE(phc->shm_name); + + struct timespec ts; + cdtime_t now = cdtime(); + cdtime_t cmd_wait_time = MS_TO_CDTIME_T(1500) + phc->cmd_wait_time * 2; + ts = CDTIME_T_TO_TIMESPEC(now + cmd_wait_time); + + int ret = sem_timedwait(&phc->sema_cmd_start, &ts); + DPDK_CHILD_LOG("%s:%s:%d pid=%lu got sema_cmd_start (ret=%d, errno=%d)\n", + phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(), ret, + errno); + + if (phc->cmd == DPDK_CMD_QUIT) { + DPDK_CHILD_LOG("%s:%s:%d pid=%lu exiting\n", phc->shm_name, __FUNCTION__, + __LINE__, (long)getpid()); + exit(0); + } else if (ret == -1 && errno == ETIMEDOUT) { + if (phc->status == DPDK_HELPER_ALIVE_SENDING_EVENTS) { + DPDK_CHILD_LOG("%s:dpdk_helper_cmd_wait: sem timedwait()" + " timeout, did collectd terminate?\n", + phc->shm_name); + dpdk_helper_exit(phc, DPDK_HELPER_GRACEFUL_QUIT); + } + } +#if COLLECT_DEBUG + int val = 0; + if (sem_getvalue(&phc->sema_cmd_start, &val) == 0) + DPDK_CHILD_LOG("%s:%s:%d pid=%lu wait sema_cmd_start (value=%d)\n", + phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(), val); +#endif + + /* Parent PID change means collectd died so quit the helper process. */ + if (ppid != getppid()) { + DPDK_CHILD_LOG("dpdk_helper_cmd_wait: parent PID changed, quitting.\n"); + dpdk_helper_exit(phc, DPDK_HELPER_GRACEFUL_QUIT); + } + + /* Checking for DPDK primary process. */ + if (!rte_eal_primary_proc_alive(phc->eal_config.file_prefix)) { + if (phc->eal_initialized) { + DPDK_CHILD_LOG( + "%s:dpdk_helper_cmd_wait: no primary alive but EAL initialized:" + " quitting.\n", + phc->shm_name); + dpdk_helper_exit(phc, DPDK_HELPER_NOT_INITIALIZED); + } + + phc->status = DPDK_HELPER_WAITING_ON_PRIMARY; + DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_WAITING_ON_PRIMARY\n", phc->shm_name, + __FUNCTION__, __LINE__); + + return -1; + } + + if (!phc->eal_initialized) { + int ret = dpdk_helper_eal_init(phc); + if (ret != 0) { + DPDK_CHILD_LOG("Error initializing EAL\n"); + dpdk_helper_exit(phc, DPDK_HELPER_NOT_INITIALIZED); + } + phc->status = DPDK_HELPER_ALIVE_SENDING_EVENTS; + DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_ALIVE_SENDING_EVENTS\n", phc->shm_name, + __FUNCTION__, __LINE__); + return -1; + } + + return 0; +} + +static int dpdk_helper_worker(dpdk_helper_ctx_t *phc) { + DPDK_CHILD_TRACE(phc->shm_name); + + pid_t ppid = getppid(); + + while (1) { + if (dpdk_helper_cmd_wait(phc, ppid) == 0) { + DPDK_CHILD_LOG("%s:%s:%d DPDK command handle (cmd=%d, pid=%lu)\n", + phc->shm_name, __FUNCTION__, __LINE__, phc->cmd, + (long)getpid()); + phc->cmd_result = dpdk_helper_command_handler(phc, phc->cmd); + } else { + phc->cmd_result = -1; + } + + /* now kick collectd to get results */ + int err = sem_post(&phc->sema_cmd_complete); + DPDK_CHILD_LOG("%s:%s:%d post sema_cmd_complete (pid=%lu)\n", phc->shm_name, + __FUNCTION__, __LINE__, (long)getpid()); + if (err) { + DPDK_CHILD_LOG("dpdk_helper_worker: error posting sema_cmd_complete " + "semaphore (%s)\n", + STRERRNO); + } + +#if COLLECT_DEBUG + int val = 0; + if (sem_getvalue(&phc->sema_cmd_complete, &val) == 0) + DPDK_CHILD_LOG("%s:%s:%d pid=%lu sema_cmd_complete (value=%d)\n", + phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(), + val); +#endif + + } /* while(1) */ + + return 0; +} + +static const char *dpdk_helper_status_str(enum DPDK_HELPER_STATUS status) { + switch (status) { + case DPDK_HELPER_ALIVE_SENDING_EVENTS: + return "DPDK_HELPER_ALIVE_SENDING_EVENTS"; + case DPDK_HELPER_WAITING_ON_PRIMARY: + return "DPDK_HELPER_WAITING_ON_PRIMARY"; + case DPDK_HELPER_INITIALIZING: + return "DPDK_HELPER_INITIALIZING"; + case DPDK_HELPER_INITIALIZING_EAL: + return "DPDK_HELPER_INITIALIZING_EAL"; + case DPDK_HELPER_GRACEFUL_QUIT: + return "DPDK_HELPER_GRACEFUL_QUIT"; + case DPDK_HELPER_NOT_INITIALIZED: + return "DPDK_HELPER_NOT_INITIALIZED"; + default: + return "UNKNOWN"; + } +} + +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)); + + if (phc->status == DPDK_HELPER_GRACEFUL_QUIT) { + return 0; + } else if (phc->status == DPDK_HELPER_NOT_INITIALIZED) { + phc->status = DPDK_HELPER_INITIALIZING; + DEBUG("%s:%s:%d DPDK_HELPER_INITIALIZING", phc->shm_name, __FUNCTION__, + __LINE__); + int err = dpdk_helper_spawn(phc); + if (err) { + ERROR("dpdkstat: error spawning helper %s", STRERRNO); + } + return -1; + } + + pid_t ws = waitpid(phc->pid, NULL, WNOHANG); + if (ws != 0) { + phc->status = DPDK_HELPER_INITIALIZING; + DEBUG("%s:%s:%d DPDK_HELPER_INITIALIZING", phc->shm_name, __FUNCTION__, + __LINE__); + int err = dpdk_helper_spawn(phc); + if (err) { + ERROR("dpdkstat: error spawning helper %s", STRERRNO); + } + return -1; + } + + if (phc->status == DPDK_HELPER_INITIALIZING_EAL) { + return -1; + } + + return 0; +} + +static void dpdk_helper_check_pipe(dpdk_helper_ctx_t *phc) { + char buf[DPDK_MAX_BUFFER_SIZE]; + char out[DPDK_MAX_BUFFER_SIZE]; + + /* non blocking check on helper logging pipe */ + struct pollfd fds = { + .fd = phc->pipes[0], + .events = POLLIN, + }; + int data_avail = poll(&fds, 1, 0); + DEBUG("%s:dpdk_helper_check_pipe: poll data_avail=%d", phc->shm_name, + data_avail); + if (data_avail < 0) { + if (errno != EINTR || errno != EAGAIN) { + ERROR("%s: poll(2) failed: %s", phc->shm_name, STRERRNO); + } + } + while (data_avail) { + int nbytes = read(phc->pipes[0], buf, (sizeof(buf) - 1)); + DEBUG("%s:dpdk_helper_check_pipe: read nbytes=%d", phc->shm_name, nbytes); + if (nbytes <= 0) + break; + buf[nbytes] = '\0'; + sstrncpy(out, buf, sizeof(out)); + DEBUG("%s: helper process:\n%s", phc->shm_name, out); + } +} + +int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result, + cdtime_t cmd_wait_time) { + if (phc == NULL) { + ERROR("Invalid argument(phc)"); + return -EINVAL; + } + + DEBUG("%s:%s:%d pid=%lu, cmd=%d", phc->shm_name, __FUNCTION__, __LINE__, + (long)getpid(), cmd); + + phc->cmd_wait_time = cmd_wait_time; + + int ret = dpdk_helper_status_check(phc); + + dpdk_helper_check_pipe(phc); + + if (ret != 0) { + return ret; + } + + DEBUG("%s: DPDK command execute (cmd=%d)", phc->shm_name, cmd); + + phc->cmd_result = 0; + phc->cmd = cmd; + + /* kick helper to process command */ + int err = sem_post(&phc->sema_cmd_start); + if (err) { + ERROR("dpdk_helper_worker: error posting sema_cmd_start semaphore (%s)", + STRERRNO); + } + +#if COLLECT_DEBUG + int val = 0; + if (sem_getvalue(&phc->sema_cmd_start, &val) == 0) + DEBUG("%s:dpdk_helper_command: post sema_cmd_start (value=%d)", + phc->shm_name, val); +#endif + + if (phc->cmd != DPDK_CMD_QUIT) { + + /* wait for helper to complete processing */ + struct timespec ts; + cdtime_t now = cdtime(); + + if (phc->status != DPDK_HELPER_ALIVE_SENDING_EVENTS) { + cmd_wait_time = MS_TO_CDTIME_T(DPDK_CDM_DEFAULT_TIMEOUT); + } + + ts = CDTIME_T_TO_TIMESPEC(now + cmd_wait_time); + ret = sem_timedwait(&phc->sema_cmd_complete, &ts); + if (ret == -1 && errno == ETIMEDOUT) { + DPDK_HELPER_TRACE(phc->shm_name); + DEBUG("%s:sema_cmd_start: timeout in collectd thread: is a DPDK Primary " + "running?", + phc->shm_name); + return -ETIMEDOUT; + } + +#if COLLECT_DEBUG + val = 0; + if (sem_getvalue(&phc->sema_cmd_complete, &val) == 0) + DEBUG("%s:dpdk_helper_command: wait sema_cmd_complete (value=%d)", + phc->shm_name, val); +#endif + + if (result) { + *result = phc->cmd_result; + } + } + + dpdk_helper_check_pipe(phc); + + DEBUG("%s: DPDK command complete (cmd=%d, result=%d)", phc->shm_name, + phc->cmd, phc->cmd_result); + + return 0; +} + +uint64_t strtoull_safe(const char *str, int *err) { + uint64_t val = 0; + char *endptr; + int res = 0; + + val = strtoull(str, &endptr, 16); + if (*endptr) { + ERROR("%s Failed to parse the value %s, endptr=%c", __FUNCTION__, str, + *endptr); + res = -EINVAL; + } + if (err != NULL) + *err = res; + return val; +} + +uint128_t str_to_uint128(const char *str, int len) { + uint128_t lcore_mask; + int err = 0; + + memset(&lcore_mask, 0, sizeof(lcore_mask)); + + if (len <= 2 || strncmp(str, "0x", 2) != 0) { + ERROR("%s Value %s should be represened in hexadecimal format", + __FUNCTION__, str); + return lcore_mask; + } + /* If str is <= 64 bit long ('0x' + 16 chars = 18 chars) then + * conversion is straightforward. Otherwise str is splitted into 64b long + * blocks */ + if (len <= 18) { + lcore_mask.low = strtoull_safe(str, &err); + if (err) + return lcore_mask; + } else { + char low_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)); + + strncpy(high_str, str, len - 16); + strncpy(low_str, str + len - 16, 16); + + lcore_mask.low = strtoull_safe(low_str, &err); + if (err) + return lcore_mask; + + lcore_mask.high = strtoull_safe(high_str, &err); + if (err) { + lcore_mask.low = 0; + return lcore_mask; + } + } + return lcore_mask; +} + +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", + __FUNCTION__, __LINE__); + return ports; + } + + if (ports > RTE_MAX_ETHPORTS) { + ERROR("%s:%d: Number of DPDK ports (%u) is greater than " + "RTE_MAX_ETHPORTS=%d. Ignoring extra ports\n", + __FUNCTION__, __LINE__, ports, RTE_MAX_ETHPORTS); + ports = RTE_MAX_ETHPORTS; + } + + return ports; +} diff --git a/src/utils/dpdk/dpdk.h b/src/utils/dpdk/dpdk.h new file mode 100644 index 00000000..d4551d86 --- /dev/null +++ b/src/utils/dpdk/dpdk.h @@ -0,0 +1,90 @@ +/* + * collectd - src/utils_dpdk.h + * MIT License + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Maryam Tahhan + * Harry van Haaren + * Taras Chornyi + * Serhiy Pshyk + * Krzysztof Matczak + */ + +#ifndef UTILS_DPDK_H +#define UTILS_DPDK_H + +#include + +#define ERR_BUF_SIZE 1024 + +enum DPDK_CMD { + DPDK_CMD_NONE = 0, + DPDK_CMD_QUIT, + DPDK_CMD_INIT, + DPDK_CMD_GET_STATS, + DPDK_CMD_GET_EVENTS, + __DPDK_CMD_LAST, +}; + +struct dpdk_eal_config_s { + char coremask[DATA_MAX_NAME_LEN]; + char memory_channels[DATA_MAX_NAME_LEN]; + char socket_memory[DATA_MAX_NAME_LEN]; + char file_prefix[DATA_MAX_NAME_LEN]; + char log_level[DATA_MAX_NAME_LEN]; + char rte_driver_lib_path[PATH_MAX]; +}; +typedef struct dpdk_eal_config_s dpdk_eal_config_t; + +struct uint128_s { + u_int64_t high; + u_int64_t low; +}; +typedef struct uint128_s uint128_t; + +typedef struct dpdk_helper_ctx_s dpdk_helper_ctx_t; + +int dpdk_helper_init(const char *name, size_t data_size, + dpdk_helper_ctx_t **pphc); +void dpdk_helper_shutdown(dpdk_helper_ctx_t *phc); +int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci); +int dpdk_helper_eal_config_set(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec); +int dpdk_helper_eal_config_get(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec); +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(void); + +/* forward declaration of handler function that is called by helper from + * child process. not implemented in helper. must be provided by client. */ +int dpdk_helper_command_handler(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd); + +uint128_t str_to_uint128(const char *str, int len); + +/* logging functions that should be used in child process */ +#define DPDK_CHILD_LOG(...) fprintf(stdout, __VA_ARGS__) +#define DPDK_CHILD_TRACE(_name) \ + fprintf(stdout, "%s:%s:%d pid=%u\n", _name, __FUNCTION__, __LINE__, getpid()) + +#endif /* UTILS_DPDK_H */ diff --git a/src/utils/format_graphite/format_graphite.c b/src/utils/format_graphite/format_graphite.c new file mode 100644 index 00000000..ffef3e2f --- /dev/null +++ b/src/utils/format_graphite/format_graphite.c @@ -0,0 +1,377 @@ +/** + * collectd - src/utils_format_graphite.c + * Copyright (C) 2012 Thomas Meson + * Copyright (C) 2012 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Thomas Meson + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" + +#include "utils/format_graphite/format_graphite.h" +#include "utils_cache.h" + +#define GRAPHITE_FORBIDDEN " \t\"\\:!/()\n\r" + +/* Utils functions to format data sets in graphite format. + * Largely taken from write_graphite.c as it remains the same formatting */ + +/* helper function for reverse_hostname */ +void reverse_string(char *r_host, int len) { + for (int i = 0, j = len - 1; i < j; i++, j--) { + char t = r_host[i]; + r_host[i] = r_host[j]; + r_host[j] = t; + } +} + +void reverse_hostname(char *r_host, char const *orig_host) { + int len_host = strlen(orig_host); + + /* put reversed hostname into working copy */ + for (int i = 0; i < len_host; i++) + r_host[i] = orig_host[len_host - 1 - i]; + r_host[len_host] = '\0'; + + /* reverse labels (except last) */ + int p = 0; + for (int i = 0; i < len_host; i++) + if (r_host[i] == '.') { + reverse_string(&r_host[p], i - p); + p = i + 1; + } + + /* reverse last label */ + reverse_string(&r_host[p], len_host - p); +} + +static int gr_format_values(char *ret, size_t ret_len, int ds_num, + const data_set_t *ds, const value_list_t *vl, + gauge_t const *rates) { + size_t offset = 0; + int status; + + assert(0 == strcmp(ds->type, vl->type)); + + memset(ret, 0, ret_len); + +#define BUFFER_ADD(...) \ + do { \ + status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \ + if (status < 1) { \ + return -1; \ + } else if (((size_t)status) >= (ret_len - offset)) { \ + return -1; \ + } else \ + offset += ((size_t)status); \ + } while (0) + + if (ds->ds[ds_num].type == DS_TYPE_GAUGE) + BUFFER_ADD(GAUGE_FORMAT, vl->values[ds_num].gauge); + else if (rates != NULL) + BUFFER_ADD("%f", rates[ds_num]); + else if (ds->ds[ds_num].type == DS_TYPE_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 { + P_ERROR("gr_format_values: Unknown data source type: %i", + ds->ds[ds_num].type); + return -1; + } + +#undef BUFFER_ADD + + return 0; +} + +static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len, + char escape_char, bool preserve_separator) { + memset(dst, 0, dst_len); + + if (src == NULL) + return; + + for (size_t i = 0; i < dst_len; i++) { + if (src[i] == 0) { + dst[i] = 0; + break; + } + + if ((!preserve_separator && (src[i] == '.')) || isspace((int)src[i]) || + iscntrl((int)src[i])) + dst[i] = escape_char; + else + dst[i] = src[i]; + } +} + +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 = ""; + + if (flags & GRAPHITE_REVERSE_HOST) { + char r_host[DATA_MAX_NAME_LEN]; + reverse_hostname(r_host, vl->host); + gr_copy_escape_part(n_host, r_host, sizeof(n_host), escape_char, 1); + } else { + 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, + 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[2 * DATA_MAX_NAME_LEN + 1]; + char tmp_type[2 * DATA_MAX_NAME_LEN + 1]; + + if (prefix == NULL) + prefix = ""; + + if (postfix == NULL) + postfix = ""; + + bool preserve_separator = (flags & GRAPHITE_PRESERVE_SEPARATOR); + + if (flags & GRAPHITE_REVERSE_HOST) { + char r_host[DATA_MAX_NAME_LEN]; + reverse_hostname(r_host, vl->host); + gr_copy_escape_part(n_host, r_host, sizeof(n_host), escape_char, + preserve_separator); + } else { + gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, + preserve_separator); + } + gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char, + preserve_separator); + gr_copy_escape_part(n_plugin_instance, vl->plugin_instance, + sizeof(n_plugin_instance), escape_char, + preserve_separator); + gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char, + preserve_separator); + gr_copy_escape_part(n_type_instance, vl->type_instance, + sizeof(n_type_instance), escape_char, preserve_separator); + + if (n_plugin_instance[0] != '\0') + snprintf(tmp_plugin, sizeof(tmp_plugin), "%s%c%s", n_plugin, + (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-', + n_plugin_instance); + else + sstrncpy(tmp_plugin, n_plugin, sizeof(tmp_plugin)); + + if (n_type_instance[0] != '\0') { + if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0) + sstrncpy(tmp_type, n_type_instance, sizeof(tmp_type)); + else + snprintf(tmp_type, sizeof(tmp_type), "%s%c%s", n_type, + (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-', + n_type_instance); + } else + sstrncpy(tmp_type, n_type, sizeof(tmp_type)); + + /* Assert always_append_ds -> ds_name */ + assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL)); + if (ds_name != NULL) { + if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && + strcmp(tmp_plugin, tmp_type) == 0) + snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix, + tmp_plugin, ds_name); + else + snprintf(ret, ret_len, "%s%s%s.%s.%s.%s", prefix, n_host, postfix, + tmp_plugin, tmp_type, ds_name); + } else + snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix, tmp_plugin, + tmp_type); + + return 0; +} + +static void escape_graphite_string(char *buffer, char escape_char) { + assert(strchr(GRAPHITE_FORBIDDEN, escape_char) == NULL); + + for (char *head = buffer + strcspn(buffer, GRAPHITE_FORBIDDEN); *head != '\0'; + head += strcspn(head, GRAPHITE_FORBIDDEN)) + *head = escape_char; +} + +int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds, + value_list_t const *vl, char const *prefix, + char const *postfix, char const escape_char, + unsigned int flags) { + int status = 0; + int buffer_pos = 0; + + gauge_t *rates = NULL; + if (flags & GRAPHITE_STORE_RATES) { + rates = uc_get_rate(ds, vl); + if (rates == NULL) { + P_ERROR("format_graphite: error with uc_get_rate"); + return -1; + } + } + + for (size_t i = 0; i < ds->ds_num; i++) { + char const *ds_name = NULL; + char key[10 * DATA_MAX_NAME_LEN]; + char values[512]; + size_t message_len; + char message[1024]; + + if ((flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds->ds_num > 1)) + ds_name = ds->ds[i].name; + + /* Copy the identifier to `key' and escape it. */ + 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) { + P_ERROR("format_graphite: error with gr_format_values"); + sfree(rates); + return status; + } + + /* Compute the graphite command */ + message_len = + (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)) { + 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) { + P_ERROR("format_graphite: target buffer too small"); + sfree(rates); + return -ENOMEM; + } + memcpy((void *)(buffer + buffer_pos), message, message_len); + buffer_pos += message_len; + buffer[buffer_pos] = '\0'; + } + sfree(rates); + return status; +} /* int format_graphite */ diff --git a/src/utils/format_graphite/format_graphite.h b/src/utils/format_graphite/format_graphite.h new file mode 100644 index 00000000..4df7db3d --- /dev/null +++ b/src/utils/format_graphite/format_graphite.h @@ -0,0 +1,42 @@ +/** + * collectd - src/utils_format_graphite.h + * Copyright (C) 2012 Thomas Meson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Thomas Meson + **/ + +#ifndef UTILS_FORMAT_GRAPHITE_H +#define UTILS_FORMAT_GRAPHITE_H 1 + +#include "collectd.h" + +#include "plugin.h" + +#define GRAPHITE_STORE_RATES 0x01 +#define GRAPHITE_SEPARATE_INSTANCES 0x02 +#define GRAPHITE_ALWAYS_APPEND_DS 0x04 +#define GRAPHITE_DROP_DUPE_FIELDS 0x08 +#define GRAPHITE_PRESERVE_SEPARATOR 0x10 +#define GRAPHITE_USE_TAGS 0x20 +#define GRAPHITE_REVERSE_HOST 0x40 + +int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds, + const value_list_t *vl, const char *prefix, + const char *postfix, const char escape_char, + unsigned int flags); + +#endif /* UTILS_FORMAT_GRAPHITE_H */ diff --git a/src/utils/format_graphite/format_graphite_test.c b/src/utils/format_graphite/format_graphite_test.c new file mode 100644 index 00000000..e01317b2 --- /dev/null +++ b/src/utils/format_graphite/format_graphite_test.c @@ -0,0 +1,207 @@ +/** + * collectd - src/utils_format_graphite_test.c + * Copyright (C) 2016 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "collectd.h" + +#include "testing.h" +#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */ +#include "utils/format_graphite/format_graphite.h" + +static data_set_t ds_single = { + .type = "single", + .ds_num = 1, + .ds = &(data_source_t){"value", DS_TYPE_GAUGE, NAN, NAN}, +}; + +/* +static data_set_t ds_double = { + .type = "double", + .ds_num = 2, + .ds = + (data_source_t[]){ + {"one", DS_TYPE_DERIVE, 0, NAN}, {"two", DS_TYPE_DERIVE, 0, NAN}, + }, +}; +*/ + +DEF_TEST(metric_name) { + struct { + const char *plugin_instance; + const char *type_instance; + const char *prefix; + const char *suffix; + unsigned int flags; + const char *want_name; + } cases[] = { + { + .want_name = "example@com.test.single", + }, + /* plugin and type instances */ + { + .plugin_instance = "foo", + .type_instance = "bar", + .want_name = "example@com.test-foo.single-bar", + }, + { + .plugin_instance = NULL, + .type_instance = "bar", + .want_name = "example@com.test.single-bar", + }, + { + .plugin_instance = "foo", + .type_instance = NULL, + .want_name = "example@com.test-foo.single", + }, + /* special chars */ + { + .plugin_instance = "foo (test)", + .type_instance = "test: \"hello\"", + .want_name = "example@com.test-foo@@test@.single-test@@@hello@", + }, + /* flag GRAPHITE_SEPARATE_INSTANCES */ + { + .plugin_instance = "foo", + .type_instance = "bar", + .flags = GRAPHITE_SEPARATE_INSTANCES, + .want_name = "example@com.test.foo.single.bar", + }, + /* flag GRAPHITE_ALWAYS_APPEND_DS */ + { + .plugin_instance = "foo", + .type_instance = "bar", + .flags = GRAPHITE_ALWAYS_APPEND_DS, + .want_name = "example@com.test-foo.single-bar.value", + }, + /* flag GRAPHITE_PRESERVE_SEPARATOR */ + { + .plugin_instance = "f.o.o", + .type_instance = "b.a.r", + .flags = 0, + .want_name = "example@com.test-f@o@o.single-b@a@r", + }, + { + .plugin_instance = "f.o.o", + .type_instance = "b.a.r", + .flags = GRAPHITE_PRESERVE_SEPARATOR, + .want_name = "example.com.test-f.o.o.single-b.a.r", + }, + /* prefix and suffix */ + { + .prefix = "foo.", + .suffix = ".bar", + .want_name = "foo.example@com.bar.test.single", + }, + { + .prefix = NULL, + .suffix = ".bar", + .want_name = "example@com.bar.test.single", + }, + { + .prefix = "foo.", + .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++) { + value_list_t vl = { + .values = &(value_t){.gauge = 42}, + .values_len = 1, + .time = TIME_T_TO_CDTIME_T_STATIC(1480063672), + .interval = TIME_T_TO_CDTIME_T_STATIC(10), + .host = "example.com", + .plugin = "test", + .type = "single", + }; + + char want[1024]; + ssnprintf(want, sizeof(want), "%s 42 1480063672\r\n", cases[i].want_name); + + if (cases[i].plugin_instance != NULL) + sstrncpy(vl.plugin_instance, cases[i].plugin_instance, + sizeof(vl.plugin_instance)); + if (cases[i].type_instance != NULL) + sstrncpy(vl.type_instance, cases[i].type_instance, + sizeof(vl.type_instance)); + + char got[1024]; + EXPECT_EQ_INT(0, format_graphite(got, sizeof(got), &ds_single, &vl, + cases[i].prefix, cases[i].suffix, '@', + cases[i].flags)); + EXPECT_EQ_STR(want, got); + } + + return 0; +} + +DEF_TEST(null_termination) { + value_list_t vl = { + .values = &(value_t){.gauge = 1337}, + .values_len = 1, + .time = TIME_T_TO_CDTIME_T_STATIC(1480063672), + .interval = TIME_T_TO_CDTIME_T_STATIC(10), + .host = "example.com", + .plugin = "test", + .type = "single", + }; + char const *want = "example_com.test.single 1337 1480063672\r\n"; + + char buffer[128]; + for (size_t i = 0; i < sizeof(buffer); i++) + buffer[i] = (char)i; + + EXPECT_EQ_INT(0, format_graphite(buffer, sizeof(buffer), &ds_single, &vl, + NULL, NULL, '_', 0)); + EXPECT_EQ_STR(want, buffer); + EXPECT_EQ_INT(0, buffer[strlen(want)]); + for (size_t i = strlen(want) + 1; i < sizeof(buffer); i++) + EXPECT_EQ_INT((int)i, (int)buffer[i]); + + return 0; +} + +int main(void) { + RUN_TEST(metric_name); + RUN_TEST(null_termination); + + END_TEST; +} diff --git a/src/utils/format_json/format_json.c b/src/utils/format_json/format_json.c new file mode 100644 index 00000000..ac3729ac --- /dev/null +++ b/src/utils/format_json/format_json.c @@ -0,0 +1,695 @@ +/** + * collectd - src/utils_format_json.c + * Copyright (C) 2009-2015 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "utils/format_json/format_json.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils_cache.h" + +#if HAVE_LIBYAJL +#include +#include +#if HAVE_YAJL_YAJL_VERSION_H +#include +#endif +#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1) +#define HAVE_YAJL_V2 1 +#endif +#endif + +static int json_escape_string(char *buffer, size_t buffer_size, /* {{{ */ + const char *string) { + size_t dst_pos; + + if ((buffer == NULL) || (string == NULL)) + return -EINVAL; + + if (buffer_size < 3) + return -ENOMEM; + + dst_pos = 0; + +#define BUFFER_ADD(c) \ + do { \ + if (dst_pos >= (buffer_size - 1)) { \ + buffer[buffer_size - 1] = '\0'; \ + return -ENOMEM; \ + } \ + buffer[dst_pos] = (c); \ + dst_pos++; \ + } while (0) + + /* Escape special characters */ + BUFFER_ADD('"'); + for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) { + if ((string[src_pos] == '"') || (string[src_pos] == '\\')) { + BUFFER_ADD('\\'); + BUFFER_ADD(string[src_pos]); + } else if (string[src_pos] <= 0x001F) + BUFFER_ADD('?'); + else + BUFFER_ADD(string[src_pos]); + } /* for */ + BUFFER_ADD('"'); + buffer[dst_pos] = 0; + +#undef BUFFER_ADD + + return 0; +} /* }}} int json_escape_string */ + +static int values_to_json(char *buffer, size_t buffer_size, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + int store_rates) { + size_t offset = 0; + gauge_t *rates = NULL; + + memset(buffer, 0, buffer_size); + +#define BUFFER_ADD(...) \ + do { \ + int status; \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) { \ + sfree(rates); \ + return -1; \ + } else if (((size_t)status) >= (buffer_size - offset)) { \ + sfree(rates); \ + return -ENOMEM; \ + } else \ + offset += ((size_t)status); \ + } while (0) + + BUFFER_ADD("["); + for (size_t i = 0; i < ds->ds_num; i++) { + if (i > 0) + BUFFER_ADD(","); + + if (ds->ds[i].type == DS_TYPE_GAUGE) { + if (isfinite(vl->values[i].gauge)) + BUFFER_ADD(JSON_GAUGE_FORMAT, vl->values[i].gauge); + else + BUFFER_ADD("null"); + } else if (store_rates) { + if (rates == NULL) + rates = uc_get_rate(ds, vl); + if (rates == NULL) { + WARNING("utils_format_json: uc_get_rate failed."); + sfree(rates); + return -1; + } + + if (isfinite(rates[i])) + BUFFER_ADD(JSON_GAUGE_FORMAT, rates[i]); + else + BUFFER_ADD("null"); + } else if (ds->ds[i].type == DS_TYPE_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) + BUFFER_ADD("%" PRIu64, vl->values[i].absolute); + else { + ERROR("format_json: Unknown data source type: %i", ds->ds[i].type); + sfree(rates); + return -1; + } + } /* for ds->ds_num */ + BUFFER_ADD("]"); + +#undef BUFFER_ADD + + sfree(rates); + return 0; +} /* }}} int values_to_json */ + +static int dstypes_to_json(char *buffer, size_t buffer_size, /* {{{ */ + const data_set_t *ds) { + size_t offset = 0; + + memset(buffer, 0, buffer_size); + +#define BUFFER_ADD(...) \ + do { \ + int status; \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) \ + return -1; \ + else if (((size_t)status) >= (buffer_size - offset)) \ + return -ENOMEM; \ + else \ + offset += ((size_t)status); \ + } while (0) + + BUFFER_ADD("["); + for (size_t i = 0; i < ds->ds_num; i++) { + if (i > 0) + BUFFER_ADD(","); + + BUFFER_ADD("\"%s\"", DS_TYPE_TO_STRING(ds->ds[i].type)); + } /* for ds->ds_num */ + BUFFER_ADD("]"); + +#undef BUFFER_ADD + + return 0; +} /* }}} int dstypes_to_json */ + +static int dsnames_to_json(char *buffer, size_t buffer_size, /* {{{ */ + const data_set_t *ds) { + size_t offset = 0; + + memset(buffer, 0, buffer_size); + +#define BUFFER_ADD(...) \ + do { \ + int status; \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) \ + return -1; \ + else if (((size_t)status) >= (buffer_size - offset)) \ + return -ENOMEM; \ + else \ + offset += ((size_t)status); \ + } while (0) + + BUFFER_ADD("["); + for (size_t i = 0; i < ds->ds_num; i++) { + if (i > 0) + BUFFER_ADD(","); + + BUFFER_ADD("\"%s\"", ds->ds[i].name); + } /* for ds->ds_num */ + BUFFER_ADD("]"); + +#undef BUFFER_ADD + + return 0; +} /* }}} int dsnames_to_json */ + +static int meta_data_keys_to_json(char *buffer, size_t buffer_size, /* {{{ */ + meta_data_t *meta, char **keys, + size_t keys_num) { + size_t offset = 0; + int status; + + buffer[0] = 0; + +#define BUFFER_ADD(...) \ + do { \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) \ + return -1; \ + else if (((size_t)status) >= (buffer_size - offset)) \ + return -ENOMEM; \ + else \ + offset += ((size_t)status); \ + } while (0) + + for (size_t i = 0; i < keys_num; ++i) { + int type; + char *key = keys[i]; + + type = meta_data_type(meta, key); + if (type == MD_TYPE_STRING) { + char *value = NULL; + if (meta_data_get_string(meta, key, &value) == 0) { + char temp[512] = ""; + + status = json_escape_string(temp, sizeof(temp), value); + sfree(value); + if (status != 0) + return status; + + BUFFER_ADD(",\"%s\":%s", key, temp); + } + } else if (type == MD_TYPE_SIGNED_INT) { + int64_t value = 0; + if (meta_data_get_signed_int(meta, key, &value) == 0) + BUFFER_ADD(",\"%s\":%" PRIi64, key, value); + } else if (type == MD_TYPE_UNSIGNED_INT) { + uint64_t value = 0; + if (meta_data_get_unsigned_int(meta, key, &value) == 0) + BUFFER_ADD(",\"%s\":%" PRIu64, key, value); + } else if (type == MD_TYPE_DOUBLE) { + double value = 0.0; + if (meta_data_get_double(meta, key, &value) == 0) + BUFFER_ADD(",\"%s\":%f", key, value); + } else if (type == MD_TYPE_BOOLEAN) { + bool value = false; + if (meta_data_get_boolean(meta, key, &value) == 0) + BUFFER_ADD(",\"%s\":%s", key, value ? "true" : "false"); + } + } /* for (keys) */ + + if (offset == 0) + return ENOENT; + + buffer[0] = '{'; /* replace leading ',' */ + BUFFER_ADD("}"); + +#undef BUFFER_ADD + + return 0; +} /* }}} int meta_data_keys_to_json */ + +static int meta_data_to_json(char *buffer, size_t buffer_size, /* {{{ */ + meta_data_t *meta) { + char **keys = NULL; + size_t keys_num; + int status; + + if ((buffer == NULL) || (buffer_size == 0) || (meta == NULL)) + return EINVAL; + + status = meta_data_toc(meta, &keys); + if (status <= 0) + return status; + keys_num = (size_t)status; + + status = meta_data_keys_to_json(buffer, buffer_size, meta, keys, keys_num); + + for (size_t i = 0; i < keys_num; ++i) + sfree(keys[i]); + sfree(keys); + + return status; +} /* }}} int meta_data_to_json */ + +static int value_list_to_json(char *buffer, size_t buffer_size, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + int store_rates) { + char temp[512]; + size_t offset = 0; + int status; + + memset(buffer, 0, buffer_size); + +#define BUFFER_ADD(...) \ + do { \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) \ + return -1; \ + else if (((size_t)status) >= (buffer_size - offset)) \ + return -ENOMEM; \ + else \ + offset += ((size_t)status); \ + } while (0) + + /* All value lists have a leading comma. The first one will be replaced with + * a square bracket in `format_json_finalize'. */ + BUFFER_ADD(",{"); + + status = values_to_json(temp, sizeof(temp), ds, vl, store_rates); + if (status != 0) + return status; + BUFFER_ADD("\"values\":%s", temp); + + status = dstypes_to_json(temp, sizeof(temp), ds); + if (status != 0) + return status; + BUFFER_ADD(",\"dstypes\":%s", temp); + + status = dsnames_to_json(temp, sizeof(temp), ds); + if (status != 0) + return status; + BUFFER_ADD(",\"dsnames\":%s", temp); + + BUFFER_ADD(",\"time\":%.3f", CDTIME_T_TO_DOUBLE(vl->time)); + BUFFER_ADD(",\"interval\":%.3f", CDTIME_T_TO_DOUBLE(vl->interval)); + +#define BUFFER_ADD_KEYVAL(key, value) \ + do { \ + status = json_escape_string(temp, sizeof(temp), (value)); \ + if (status != 0) \ + return status; \ + BUFFER_ADD(",\"%s\":%s", (key), temp); \ + } while (0) + + BUFFER_ADD_KEYVAL("host", vl->host); + BUFFER_ADD_KEYVAL("plugin", vl->plugin); + BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance); + BUFFER_ADD_KEYVAL("type", vl->type); + BUFFER_ADD_KEYVAL("type_instance", vl->type_instance); + + if (vl->meta != NULL) { + char meta_buffer[buffer_size]; + memset(meta_buffer, 0, sizeof(meta_buffer)); + status = meta_data_to_json(meta_buffer, sizeof(meta_buffer), vl->meta); + if (status != 0) + return status; + + BUFFER_ADD(",\"meta\":%s", meta_buffer); + } /* if (vl->meta != NULL) */ + + BUFFER_ADD("}"); + +#undef BUFFER_ADD_KEYVAL +#undef BUFFER_ADD + + return 0; +} /* }}} int value_list_to_json */ + +static int format_json_value_list_nocheck(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, + size_t *ret_buffer_free, + const data_set_t *ds, + const value_list_t *vl, + int store_rates, size_t temp_size) { + char temp[temp_size]; + int status; + + status = value_list_to_json(temp, sizeof(temp), ds, vl, store_rates); + if (status != 0) + return status; + temp_size = strlen(temp); + + memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1); + (*ret_buffer_fill) += temp_size; + (*ret_buffer_free) -= temp_size; + + return 0; +} /* }}} int format_json_value_list_nocheck */ + +int format_json_initialize(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, size_t *ret_buffer_free) { + size_t buffer_fill; + size_t buffer_free; + + if ((buffer == NULL) || (ret_buffer_fill == NULL) || + (ret_buffer_free == NULL)) + return -EINVAL; + + buffer_fill = *ret_buffer_fill; + buffer_free = *ret_buffer_free; + + buffer_free = buffer_fill + buffer_free; + buffer_fill = 0; + + if (buffer_free < 3) + return -ENOMEM; + + memset(buffer, 0, buffer_free); + *ret_buffer_fill = buffer_fill; + *ret_buffer_free = buffer_free; + + return 0; +} /* }}} int format_json_initialize */ + +int format_json_finalize(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, size_t *ret_buffer_free) { + size_t pos; + + if ((buffer == NULL) || (ret_buffer_fill == NULL) || + (ret_buffer_free == NULL)) + return -EINVAL; + + if (*ret_buffer_free < 2) + return -ENOMEM; + + /* Replace the leading comma added in `value_list_to_json' with a square + * bracket. */ + if (buffer[0] != ',') + return -EINVAL; + buffer[0] = '['; + + pos = *ret_buffer_fill; + buffer[pos] = ']'; + buffer[pos + 1] = 0; + + (*ret_buffer_fill)++; + (*ret_buffer_free)--; + + return 0; +} /* }}} int format_json_finalize */ + +int format_json_value_list(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, size_t *ret_buffer_free, + const data_set_t *ds, const value_list_t *vl, + int store_rates) { + if ((buffer == NULL) || (ret_buffer_fill == NULL) || + (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL)) + return -EINVAL; + + if (*ret_buffer_free < 3) + return -ENOMEM; + + return format_json_value_list_nocheck(buffer, ret_buffer_fill, + ret_buffer_free, ds, vl, store_rates, + (*ret_buffer_free) - 2); +} /* }}} int format_json_value_list */ + +#if HAVE_LIBYAJL +static int json_add_string(yajl_gen g, char const *str) /* {{{ */ +{ + if (str == NULL) + return (int)yajl_gen_null(g); + + return (int)yajl_gen_string(g, (const unsigned char *)str, + (unsigned int)strlen(str)); +} /* }}} int json_add_string */ + +#define JSON_ADD(g, str) \ + do { \ + yajl_gen_status status = json_add_string(g, str); \ + if (status != yajl_gen_status_ok) { \ + return -1; \ + } \ + } while (0) + +#define JSON_ADDF(g, format, ...) \ + do { \ + char *str = ssnprintf_alloc(format, __VA_ARGS__); \ + yajl_gen_status status = json_add_string(g, str); \ + free(str); \ + if (status != yajl_gen_status_ok) { \ + return -1; \ + } \ + } while (0) + +#define CHECK_SUCCESS(cmd) \ + do { \ + yajl_gen_status s = (cmd); \ + if (s != yajl_gen_status_ok) { \ + return (int)s; \ + } \ + } while (0) + +static int format_json_meta(yajl_gen g, notification_meta_t *meta) /* {{{ */ +{ + if (meta == NULL) + return 0; + + JSON_ADD(g, meta->name); + switch (meta->type) { + case NM_TYPE_STRING: + JSON_ADD(g, meta->nm_value.nm_string); + break; + case NM_TYPE_SIGNED_INT: + JSON_ADDF(g, "%" PRIi64, meta->nm_value.nm_signed_int); + break; + case NM_TYPE_UNSIGNED_INT: + JSON_ADDF(g, "%" PRIu64, meta->nm_value.nm_unsigned_int); + break; + case NM_TYPE_DOUBLE: + JSON_ADDF(g, JSON_GAUGE_FORMAT, meta->nm_value.nm_double); + break; + case NM_TYPE_BOOLEAN: + JSON_ADD(g, meta->nm_value.nm_boolean ? "true" : "false"); + break; + default: + ERROR("format_json_meta: unknown meta data type %d (name \"%s\")", + meta->type, meta->name); + CHECK_SUCCESS(yajl_gen_null(g)); + } + + return format_json_meta(g, meta->next); +} /* }}} int format_json_meta */ + +static int format_time(yajl_gen g, cdtime_t t) /* {{{ */ +{ + char buffer[RFC3339NANO_SIZE] = ""; + + if (rfc3339nano(buffer, sizeof(buffer), t) != 0) + return -1; + + JSON_ADD(g, buffer); + return 0; +} /* }}} int format_time */ + +static int format_alert(yajl_gen g, notification_t const *n) /* {{{ */ +{ + CHECK_SUCCESS(yajl_gen_array_open(g)); /* BEGIN array */ + CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN alert */ + + /* + * labels + */ + JSON_ADD(g, "labels"); + CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN labels */ + + JSON_ADD(g, "alertname"); + if (strncmp(n->plugin, n->type, strlen(n->plugin)) == 0) + JSON_ADDF(g, "collectd_%s", n->type); + else + JSON_ADDF(g, "collectd_%s_%s", n->plugin, n->type); + + JSON_ADD(g, "instance"); + JSON_ADD(g, n->host); + + /* mangling of plugin instance and type instance into labels is copied from + * the Prometheus collectd exporter. */ + if (strlen(n->plugin_instance) > 0) { + JSON_ADD(g, n->plugin); + JSON_ADD(g, n->plugin_instance); + } + if (strlen(n->type_instance) > 0) { + if (strlen(n->plugin_instance) > 0) + JSON_ADD(g, "type"); + else + JSON_ADD(g, n->plugin); + JSON_ADD(g, n->type_instance); + } + + JSON_ADD(g, "severity"); + JSON_ADD(g, (n->severity == NOTIF_FAILURE) + ? "FAILURE" + : (n->severity == NOTIF_WARNING) + ? "WARNING" + : (n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN"); + + JSON_ADD(g, "service"); + JSON_ADD(g, "collectd"); + + CHECK_SUCCESS(yajl_gen_map_close(g)); /* END labels */ + + /* + * annotations + */ + JSON_ADD(g, "annotations"); + CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN annotations */ + + JSON_ADD(g, "summary"); + JSON_ADD(g, n->message); + + if (format_json_meta(g, n->meta) != 0) { + return -1; + } + + CHECK_SUCCESS(yajl_gen_map_close(g)); /* END annotations */ + + JSON_ADD(g, "startsAt"); + if (format_time(g, n->time) != 0) { + return -1; + } + + CHECK_SUCCESS(yajl_gen_map_close(g)); /* END alert */ + CHECK_SUCCESS(yajl_gen_array_close(g)); /* END array */ + + return 0; +} /* }}} format_alert */ + +/* + * Format (prometheus/alertmanager v1): + * + * [{ + * "labels": { + * "alertname": "collectd_cpu", + * "instance": "host.example.com", + * "severity": "FAILURE", + * "service": "collectd", + * "cpu": "0", + * "type": "wait" + * }, + * "annotations": { + * "summary": "...", + * // meta + * }, + * "startsAt": , + * "endsAt": , // not used + * }] + */ +int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */ + notification_t const *n) { + yajl_gen g; + unsigned char const *out; +#if HAVE_YAJL_V2 + size_t unused_out_len; +#else + unsigned int unused_out_len; +#endif + + if ((buffer == NULL) || (n == NULL)) + return EINVAL; + +#if HAVE_YAJL_V2 + g = yajl_gen_alloc(NULL); + if (g == NULL) + return -1; +#if COLLECT_DEBUG + yajl_gen_config(g, yajl_gen_beautify, 1); + yajl_gen_config(g, yajl_gen_validate_utf8, 1); +#endif + +#else /* !HAVE_YAJL_V2 */ + yajl_gen_config conf = {0}; +#if COLLECT_DEBUG + conf.beautify = 1; + conf.indentString = " "; +#endif + g = yajl_gen_alloc(&conf, NULL); + if (g == NULL) + return -1; +#endif + + if (format_alert(g, n) != 0) { + yajl_gen_clear(g); + yajl_gen_free(g); + return -1; + } + + /* copy to output buffer */ + if (yajl_gen_get_buf(g, &out, &unused_out_len) != yajl_gen_status_ok) { + yajl_gen_clear(g); + yajl_gen_free(g); + return -1; + } + sstrncpy(buffer, (void *)out, buffer_size); + + yajl_gen_clear(g); + yajl_gen_free(g); + return 0; +} /* }}} format_json_notification */ +#else +int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */ + notification_t const *n) { + ERROR("format_json_notification: Not available (requires libyajl)."); + return ENOTSUP; +} /* }}} int format_json_notification */ +#endif diff --git a/src/utils/format_json/format_json.h b/src/utils/format_json/format_json.h new file mode 100644 index 00000000..d3d02160 --- /dev/null +++ b/src/utils/format_json/format_json.h @@ -0,0 +1,48 @@ +/** + * collectd - src/utils_format_json.h + * Copyright (C) 2009 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_FORMAT_JSON_H +#define UTILS_FORMAT_JSON_H 1 + +#include "collectd.h" + +#include "plugin.h" + +#ifndef JSON_GAUGE_FORMAT +#define JSON_GAUGE_FORMAT GAUGE_FORMAT +#endif + +int format_json_initialize(char *buffer, size_t *ret_buffer_fill, + size_t *ret_buffer_free); +int format_json_value_list(char *buffer, size_t *ret_buffer_fill, + size_t *ret_buffer_free, const data_set_t *ds, + const value_list_t *vl, int store_rates); +int format_json_finalize(char *buffer, size_t *ret_buffer_fill, + size_t *ret_buffer_free); +int format_json_notification(char *buffer, size_t buffer_size, + notification_t const *n); + +#endif /* UTILS_FORMAT_JSON_H */ diff --git a/src/utils/format_json/format_json_test.c b/src/utils/format_json/format_json_test.c new file mode 100644 index 00000000..30c89bb5 --- /dev/null +++ b/src/utils/format_json/format_json_test.c @@ -0,0 +1,184 @@ +/** + * collectd - src/utils_format_json_test.c + * Copyright (C) 2015 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +/* Workaround for Solaris 10 defining label_t + * Issue #1301 + */ + +#include "config.h" +#if KERNEL_SOLARIS +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200112L +#endif +#undef __EXTENSIONS__ +#endif + +#include "collectd.h" + +#include "testing.h" +#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */ +#include "utils/format_json/format_json.h" + +#include +#include +#if HAVE_YAJL_YAJL_VERSION_H +#include +#endif +#if YAJL_MAJOR > 1 +#define HAVE_YAJL_V2 1 +#endif + +typedef struct { + char const *key; + char const *value; +} label_t; + +typedef struct { + label_t *expected_labels; + size_t expected_labels_num; + + label_t *current_label; +} test_case_t; + +#if HAVE_YAJL_V2 +static int test_map_key(void *ctx, unsigned char const *key, size_t key_len) +#else +static int test_map_key(void *ctx, unsigned char const *key, + unsigned int key_len) +#endif +{ + test_case_t *c = ctx; + size_t i; + + c->current_label = NULL; + for (i = 0; i < c->expected_labels_num; i++) { + label_t *l = c->expected_labels + i; + if ((strlen(l->key) == key_len) && + (strncmp(l->key, (char const *)key, key_len) == 0)) { + c->current_label = l; + break; + } + } + + return 1; /* continue */ +} + +static int expect_label(char const *name, char const *got, char const *want) { + bool ok = (strcmp(got, want) == 0); + char msg[1024]; + + if (ok) + snprintf(msg, sizeof(msg), "label[\"%s\"] = \"%s\"", name, got); + else + snprintf(msg, sizeof(msg), "label[\"%s\"] = \"%s\", want \"%s\"", name, got, + want); + + OK1(ok, msg); + return 0; +} + +#if HAVE_YAJL_V2 +static int test_string(void *ctx, unsigned char const *value, size_t value_len) +#else +static int test_string(void *ctx, unsigned char const *value, + unsigned int value_len) +#endif +{ + test_case_t *c = ctx; + + if (c->current_label != NULL) { + label_t *l = c->current_label; + char *got; + int status; + + got = malloc(value_len + 1); + memmove(got, value, value_len); + got[value_len] = 0; + + status = expect_label(l->key, got, l->value); + + free(got); + + if (status != 0) + return 0; /* abort */ + } + + return 1; /* continue */ +} + +static int expect_json_labels(char *json, label_t *labels, size_t labels_num) { + yajl_callbacks funcs = { + .yajl_string = test_string, + .yajl_map_key = test_map_key, + }; + + test_case_t c = {labels, labels_num, NULL}; + + yajl_handle hndl; +#if HAVE_YAJL_V2 + CHECK_NOT_NULL(hndl = yajl_alloc(&funcs, /* alloc = */ NULL, &c)); +#else + CHECK_NOT_NULL( + hndl = yajl_alloc(&funcs, /* config = */ NULL, /* alloc = */ NULL, &c)); +#endif + OK(yajl_parse(hndl, (unsigned char *)json, strlen(json)) == yajl_status_ok); + + yajl_free(hndl); + return 0; +} + +DEF_TEST(notification) { + label_t labels[] = { + {"summary", "this is a message"}, + {"alertname", "collectd_unit_test"}, + {"instance", "example.com"}, + {"service", "collectd"}, + {"unit", "case"}, + }; + + /* 1448284606.125 ^= 1555083754651779072 */ + notification_t n = {NOTIF_WARNING, + 1555083754651779072ULL, + "this is a message", + "example.com", + "unit", + "", + "test", + "case", + NULL}; + + char got[1024]; + CHECK_ZERO(format_json_notification(got, sizeof(got), &n)); + // printf ("got = \"%s\";\n", got); + + return expect_json_labels(got, labels, STATIC_ARRAY_SIZE(labels)); +} + +int main(void) { + RUN_TEST(notification); + + END_TEST; +} diff --git a/src/utils/format_kairosdb/format_kairosdb.c b/src/utils/format_kairosdb/format_kairosdb.c new file mode 100644 index 00000000..3f29fcd9 --- /dev/null +++ b/src/utils/format_kairosdb/format_kairosdb.c @@ -0,0 +1,358 @@ +/** + * collectd - src/utils_format_kairosdb.c + * Copyright (C) 2016 Aurelien beorn Rougemont + * + * 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: + * Aurelien beorn Rougemont + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" + +#include "utils/format_kairosdb/format_kairosdb.h" +#include "utils_cache.h" + +/* This is the KAIROSDB format for write_http output + * + * Target format + * [ + * { + * "name":"collectd.vmem" + * "datapoints": + * [ + * [1453897164060, 97.000000] + * ], + * "tags": + * { + * "host": "fqdn.domain.tld", + * "plugin_instance": "vmpage_number", + * "type": "kernel_stack", + * "ds": "value" + * "" + * } + * } + * ] + */ + +static int kairosdb_escape_string(char *buffer, size_t buffer_size, /* {{{ */ + const char *string) { + size_t dst_pos; + + if ((buffer == NULL) || (string == NULL)) + return -EINVAL; + + if (buffer_size < 3) + return -ENOMEM; + + dst_pos = 0; + +#define BUFFER_ADD(c) \ + do { \ + if (dst_pos >= (buffer_size - 1)) { \ + buffer[buffer_size - 1] = '\0'; \ + return -ENOMEM; \ + } \ + buffer[dst_pos] = (c); \ + dst_pos++; \ + } while (0) + + /* Escape special characters */ + /* authorize -_. and alpha num but also escapes " */ + BUFFER_ADD('"'); + for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) { + if (isalnum(string[src_pos]) || 0x2d == string[src_pos] || + 0x2e == string[src_pos] || 0x5f == string[src_pos]) + BUFFER_ADD(tolower(string[src_pos])); + } /* for */ + BUFFER_ADD('"'); + buffer[dst_pos] = 0; + +#undef BUFFER_ADD + + return 0; +} /* }}} int kairosdb_escape_string */ + +static int values_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + int store_rates, size_t ds_idx) { + size_t offset = 0; + gauge_t *rates = NULL; + + memset(buffer, 0, buffer_size); + +#define BUFFER_ADD(...) \ + do { \ + int status; \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) { \ + sfree(rates); \ + return -1; \ + } else if (((size_t)status) >= (buffer_size - offset)) { \ + sfree(rates); \ + return -ENOMEM; \ + } else \ + offset += ((size_t)status); \ + } while (0) + + if (ds->ds[ds_idx].type == DS_TYPE_GAUGE) { + if (isfinite(vl->values[ds_idx].gauge)) { + BUFFER_ADD("[["); + BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time)); + BUFFER_ADD(","); + BUFFER_ADD(JSON_GAUGE_FORMAT, vl->values[ds_idx].gauge); + } else { + DEBUG("utils_format_kairosdb: invalid vl->values[ds_idx].gauge for " + "%s|%s|%s|%s|%s", + vl->plugin, vl->plugin_instance, vl->type, vl->type_instance, + ds->ds[ds_idx].name); + return -1; + } + } else if (store_rates) { + if (rates == NULL) + rates = uc_get_rate(ds, vl); + if (rates == NULL) { + WARNING("utils_format_kairosdb: uc_get_rate failed for %s|%s|%s|%s|%s", + vl->plugin, vl->plugin_instance, vl->type, vl->type_instance, + ds->ds[ds_idx].name); + + return -1; + } + + if (isfinite(rates[ds_idx])) { + BUFFER_ADD("[["); + BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time)); + BUFFER_ADD(","); + BUFFER_ADD(JSON_GAUGE_FORMAT, rates[ds_idx]); + } else { + WARNING("utils_format_kairosdb: invalid rates[ds_idx] for %s|%s|%s|%s|%s", + vl->plugin, vl->plugin_instance, vl->type, vl->type_instance, + ds->ds[ds_idx].name); + sfree(rates); + return -1; + } + } else if (ds->ds[ds_idx].type == DS_TYPE_COUNTER) { + BUFFER_ADD("[["); + BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time)); + BUFFER_ADD(","); + 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)); + BUFFER_ADD(","); + BUFFER_ADD("%" PRIi64, vl->values[ds_idx].derive); + } else if (ds->ds[ds_idx].type == DS_TYPE_ABSOLUTE) { + BUFFER_ADD("[["); + BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time)); + BUFFER_ADD(","); + BUFFER_ADD("%" PRIu64, vl->values[ds_idx].absolute); + } else { + ERROR("format_kairosdb: Unknown data source type: %i", ds->ds[ds_idx].type); + sfree(rates); + return -1; + } + BUFFER_ADD("]]"); + +#undef BUFFER_ADD + + DEBUG("format_kairosdb: values_to_kairosdb: buffer = %s;", buffer); + sfree(rates); + return 0; +} /* }}} int values_to_kairosdb */ + +static int value_list_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + int store_rates, + char const *const *http_attrs, + size_t http_attrs_num, int data_ttl, + char const *metrics_prefix) { + char temp[512]; + size_t offset = 0; + int status; + + memset(buffer, 0, buffer_size); + +#define BUFFER_ADD(...) \ + do { \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) \ + return -1; \ + else if (((size_t)status) >= (buffer_size - offset)) \ + return -ENOMEM; \ + else \ + offset += ((size_t)status); \ + } while (0) + +#define BUFFER_ADD_KEYVAL(key, value) \ + do { \ + status = kairosdb_escape_string(temp, sizeof(temp), (value)); \ + if (status != 0) \ + return status; \ + BUFFER_ADD(",\"%s\": %s", (key), temp); \ + } while (0) + + for (size_t i = 0; i < ds->ds_num; i++) { + /* All value lists have a leading comma. The first one will be replaced with + * a square bracket in `format_kairosdb_finalize'. */ + BUFFER_ADD(",{\"name\":\""); + + if (metrics_prefix != NULL) { + BUFFER_ADD("%s.", metrics_prefix); + } + + BUFFER_ADD("%s", vl->plugin); + + status = values_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates, i); + if (status != 0) + return status; + + BUFFER_ADD("\", \"datapoints\": %s", temp); + + /* + * Now adds meta data to metric as tags + */ + + memset(temp, 0, sizeof(temp)); + + if (data_ttl != 0) + BUFFER_ADD(", \"ttl\": %i", data_ttl); + + BUFFER_ADD(", \"tags\":\{"); + + BUFFER_ADD("\"host\": \"%s\"", vl->host); + for (size_t j = 0; j < http_attrs_num; j += 2) { + BUFFER_ADD(", \"%s\":", http_attrs[j]); + BUFFER_ADD(" \"%s\"", http_attrs[j + 1]); + } + + if (strlen(vl->plugin_instance)) + BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance); + BUFFER_ADD_KEYVAL("type", vl->type); + if (strlen(vl->type_instance)) + BUFFER_ADD_KEYVAL("type_instance", vl->type_instance); + if (ds->ds_num != 1) + BUFFER_ADD_KEYVAL("ds", ds->ds[i].name); + BUFFER_ADD("}}"); + } /* for ds->ds_num */ + +#undef BUFFER_ADD_KEYVAL +#undef BUFFER_ADD + + DEBUG("format_kairosdb: value_list_to_kairosdb: buffer = %s;", buffer); + + return 0; +} /* }}} int value_list_to_kairosdb */ + +static int format_kairosdb_value_list_nocheck( + char *buffer, /* {{{ */ + size_t *ret_buffer_fill, size_t *ret_buffer_free, const data_set_t *ds, + const value_list_t *vl, int store_rates, size_t temp_size, + char const *const *http_attrs, size_t http_attrs_num, int data_ttl, + char const *metrics_prefix) { + char temp[temp_size]; + int status; + + status = value_list_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates, + http_attrs, http_attrs_num, data_ttl, + metrics_prefix); + if (status != 0) + return status; + temp_size = strlen(temp); + + memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1); + (*ret_buffer_fill) += temp_size; + (*ret_buffer_free) -= temp_size; + + return 0; +} /* }}} int format_kairosdb_value_list_nocheck */ + +int format_kairosdb_initialize(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, + size_t *ret_buffer_free) { + size_t buffer_fill; + size_t buffer_free; + + if ((buffer == NULL) || (ret_buffer_fill == NULL) || + (ret_buffer_free == NULL)) + return -EINVAL; + + buffer_fill = *ret_buffer_fill; + buffer_free = *ret_buffer_free; + + buffer_free = buffer_fill + buffer_free; + buffer_fill = 0; + + if (buffer_free < 3) + return -ENOMEM; + + memset(buffer, 0, buffer_free); + *ret_buffer_fill = buffer_fill; + *ret_buffer_free = buffer_free; + + return 0; +} /* }}} int format_kairosdb_initialize */ + +int format_kairosdb_finalize(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, size_t *ret_buffer_free) { + size_t pos; + + if ((buffer == NULL) || (ret_buffer_fill == NULL) || + (ret_buffer_free == NULL)) + return -EINVAL; + + if (*ret_buffer_free < 2) + return -ENOMEM; + + /* Replace the leading comma added in `value_list_to_kairosdb' with a square + * bracket. */ + if (buffer[0] != ',') + return -EINVAL; + buffer[0] = '['; + + pos = *ret_buffer_fill; + buffer[pos] = ']'; + buffer[pos + 1] = 0; + + (*ret_buffer_fill)++; + (*ret_buffer_free)--; + + return 0; +} /* }}} int format_kairosdb_finalize */ + +int format_kairosdb_value_list(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, size_t *ret_buffer_free, + const data_set_t *ds, const value_list_t *vl, + int store_rates, char const *const *http_attrs, + size_t http_attrs_num, int data_ttl, + char const *metrics_prefix) { + if ((buffer == NULL) || (ret_buffer_fill == NULL) || + (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL)) + return -EINVAL; + + if (*ret_buffer_free < 3) + return -ENOMEM; + + return format_kairosdb_value_list_nocheck( + buffer, ret_buffer_fill, ret_buffer_free, ds, vl, store_rates, + (*ret_buffer_free) - 2, http_attrs, http_attrs_num, data_ttl, + metrics_prefix); +} /* }}} int format_kairosdb_value_list */ diff --git a/src/utils/format_kairosdb/format_kairosdb.h b/src/utils/format_kairosdb/format_kairosdb.h new file mode 100644 index 00000000..7b9e0e7a --- /dev/null +++ b/src/utils/format_kairosdb/format_kairosdb.h @@ -0,0 +1,49 @@ +/** + * collectd - src/utils_format_kairosdb.h + * Copyright (C) 2016 Aurelien Rougemont + * + * 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: + * Aurelien beorn Rougemont + **/ + +#ifndef UTILS_FORMAT_KAIROSDB_H +#define UTILS_FORMAT_KAIROSDB_H 1 + +#include "collectd.h" + +#include "plugin.h" + +#ifndef JSON_GAUGE_FORMAT +#define JSON_GAUGE_FORMAT GAUGE_FORMAT +#endif + +int format_kairosdb_initialize(char *buffer, size_t *ret_buffer_fill, + size_t *ret_buffer_free); +int format_kairosdb_value_list(char *buffer, size_t *ret_buffer_fill, + size_t *ret_buffer_free, const data_set_t *ds, + const value_list_t *vl, int store_rates, + char const *const *http_attrs, + size_t http_attrs_num, int data_ttl, + char const *metrics_prefix); +int format_kairosdb_finalize(char *buffer, size_t *ret_buffer_fill, + size_t *ret_buffer_free); + +#endif /* UTILS_FORMAT_KAIROSDB_H */ diff --git a/src/utils/format_stackdriver/format_stackdriver.c b/src/utils/format_stackdriver/format_stackdriver.c new file mode 100644 index 00000000..ccb1c77d --- /dev/null +++ b/src/utils/format_stackdriver/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/format_stackdriver.h" + +#include "plugin.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.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; + ssnprintf(integer, sizeof(integer), "%" PRIi64, diff); + break; + } + case DS_TYPE_COUNTER: { + counter_t diff = counter_diff((counter_t)start_value, v.counter); + ssnprintf(integer, sizeof(integer), "%llu", diff); + break; + } + case DS_TYPE_ABSOLUTE: { + ssnprintf(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) { + ssnprintf(buffer, buffer_size, GCM_PREFIX "%s/%s_%s", vl->plugin, vl->type, + ds_name); + } else { + ssnprintf(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]; + ssnprintf(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/format_stackdriver.h b/src/utils/format_stackdriver/format_stackdriver.h new file mode 100644 index 00000000..fee260e3 --- /dev/null +++ b/src/utils/format_stackdriver/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/format_stackdriver_test.c b/src/utils/format_stackdriver/format_stackdriver_test.c new file mode 100644 index 00000000..0a4f268b --- /dev/null +++ b/src/utils/format_stackdriver/format_stackdriver_test.c @@ -0,0 +1,82 @@ +/** + * 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/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/gce.c b/src/utils/gce/gce.c new file mode 100644 index 00000000..48aacd98 --- /dev/null +++ b/src/utils/gce/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 "plugin.h" +#include "utils/common/common.h" +#include "utils/gce/gce.h" +#include "utils/oauth/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]; + + ssnprintf(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; + } + + ssnprintf(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/gce.h b/src/utils/gce/gce.h new file mode 100644 index 00000000..2ee3f6eb --- /dev/null +++ b/src/utils/gce/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/heap/heap.c b/src/utils/heap/heap.c new file mode 100644 index 00000000..2fe2bcb7 --- /dev/null +++ b/src/utils/heap/heap.c @@ -0,0 +1,207 @@ +/** + * collectd - src/utils_heap.c + * Copyright (C) 2009 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include +#include +#include +#include + +#include "utils/heap/heap.h" + +struct c_heap_s { + pthread_mutex_t lock; + int (*compare)(const void *, const void *); + + void **list; + size_t list_len; /* # entries used */ + size_t list_size; /* # entries allocated */ +}; + +enum reheap_direction { DIR_UP, DIR_DOWN }; + +static void reheap(c_heap_t *h, size_t root, enum reheap_direction dir) { + size_t left; + size_t right; + size_t min; + int status; + + /* Calculate the positions of the children */ + left = (2 * root) + 1; + if (left >= h->list_len) + left = 0; + + right = (2 * root) + 2; + if (right >= h->list_len) + right = 0; + + /* Check which one of the children is smaller. */ + if ((left == 0) && (right == 0)) + return; + else if (left == 0) + min = right; + else if (right == 0) + min = left; + else { + status = h->compare(h->list[left], h->list[right]); + if (status > 0) + min = right; + else + min = left; + } + + status = h->compare(h->list[root], h->list[min]); + if (status <= 0) { + /* We didn't need to change anything, so the rest of the tree should be + * okay now. */ + return; + } else /* if (status > 0) */ + { + void *tmp; + + tmp = h->list[root]; + h->list[root] = h->list[min]; + h->list[min] = tmp; + } + + if ((dir == DIR_UP) && (root == 0)) + return; + + if (dir == DIR_UP) + reheap(h, (root - 1) / 2, dir); + else if (dir == DIR_DOWN) + reheap(h, min, dir); +} /* void reheap */ + +c_heap_t *c_heap_create(int (*compare)(const void *, const void *)) { + c_heap_t *h; + + if (compare == NULL) + return NULL; + + h = calloc(1, sizeof(*h)); + if (h == NULL) + return NULL; + + pthread_mutex_init(&h->lock, /* attr = */ NULL); + h->compare = compare; + + h->list = NULL; + h->list_len = 0; + h->list_size = 0; + + return h; +} /* c_heap_t *c_heap_create */ + +void c_heap_destroy(c_heap_t *h) { + if (h == NULL) + return; + + h->list_len = 0; + h->list_size = 0; + free(h->list); + h->list = NULL; + + pthread_mutex_destroy(&h->lock); + + free(h); +} /* void c_heap_destroy */ + +int c_heap_insert(c_heap_t *h, void *ptr) { + size_t index; + + if ((h == NULL) || (ptr == NULL)) + return -EINVAL; + + pthread_mutex_lock(&h->lock); + + assert(h->list_len <= h->list_size); + if (h->list_len == h->list_size) { + void **tmp; + + tmp = realloc(h->list, (h->list_size + 16) * sizeof(*h->list)); + if (tmp == NULL) { + pthread_mutex_unlock(&h->lock); + return -ENOMEM; + } + + h->list = tmp; + h->list_size += 16; + } + + /* Insert the new node as a leaf. */ + index = h->list_len; + h->list[index] = ptr; + h->list_len++; + + /* Reorganize the heap from bottom up. */ + reheap(h, /* parent of this node = */ (index - 1) / 2, DIR_UP); + + pthread_mutex_unlock(&h->lock); + return 0; +} /* int c_heap_insert */ + +void *c_heap_get_root(c_heap_t *h) { + void *ret = NULL; + + if (h == NULL) + return NULL; + + pthread_mutex_lock(&h->lock); + + if (h->list_len == 0) { + pthread_mutex_unlock(&h->lock); + return NULL; + } else if (h->list_len == 1) { + ret = h->list[0]; + h->list[0] = NULL; + h->list_len = 0; + } else /* if (h->list_len > 1) */ + { + ret = h->list[0]; + h->list[0] = h->list[h->list_len - 1]; + h->list[h->list_len - 1] = NULL; + h->list_len--; + + reheap(h, /* root = */ 0, DIR_DOWN); + } + + /* free some memory */ + if ((h->list_len + 32) < h->list_size) { + void **tmp; + + tmp = realloc(h->list, (h->list_len + 16) * sizeof(*h->list)); + if (tmp != NULL) { + h->list = tmp; + h->list_size = h->list_len + 16; + } + } + + pthread_mutex_unlock(&h->lock); + + return ret; +} /* void *c_heap_get_root */ diff --git a/src/utils/heap/heap.h b/src/utils/heap/heap.h new file mode 100644 index 00000000..d2d70cdc --- /dev/null +++ b/src/utils/heap/heap.h @@ -0,0 +1,99 @@ +/** + * collectd - src/utils_heap.h + * Copyright (C) 2009 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_HEAP_H +#define UTILS_HEAP_H 1 + +struct c_heap_s; +typedef struct c_heap_s c_heap_t; + +/* + * NAME + * c_heap_create + * + * DESCRIPTION + * Allocates a new heap. + * + * PARAMETERS + * `compare' The function-pointer `compare' is used to compare two keys. It + * has to return less than zero if its first argument is smaller + * then the second argument, more than zero if the first argument + * is bigger than the second argument and zero if they are equal. + * If your keys are char-pointers, you can use the `strcmp' + * function from the libc here. + * + * RETURN VALUE + * A c_heap_t-pointer upon success or NULL upon failure. + */ +c_heap_t *c_heap_create(int (*compare)(const void *, const void *)); + +/* + * NAME + * c_heap_destroy + * + * DESCRIPTION + * Deallocates a heap. Stored value- and key-pointer are lost, but of course + * not freed. + */ +void c_heap_destroy(c_heap_t *h); + +/* + * NAME + * c_heap_insert + * + * DESCRIPTION + * Stores the key-value-pair in the heap pointed to by `h'. + * + * PARAMETERS + * `h' Heap to store the data in. + * `ptr' Value to be stored. This is typically a pointer to a data + * structure. The data structure is of course *not* copied and may + * not be free'd before the pointer has been removed from the heap + * again. + * + * RETURN VALUE + * Zero upon success, non-zero otherwise. It's less than zero if an error + * occurred or greater than zero if the key is already stored in the tree. + */ +int c_heap_insert(c_heap_t *h, void *ptr); + +/* + * NAME + * c_heap_get_root + * + * DESCRIPTION + * Removes the value at the root of the heap and returns both, key and value. + * + * PARAMETERS + * `h' Heap to remove key-value-pair from. + * + * RETURN VALUE + * The pointer passed to `c_heap_insert' or NULL if there are no more + * elements in the heap (or an error occurred). + */ +void *c_heap_get_root(c_heap_t *h); + +#endif /* UTILS_HEAP_H */ diff --git a/src/utils/heap/heap_test.c b/src/utils/heap/heap_test.c new file mode 100644 index 00000000..169824e7 --- /dev/null +++ b/src/utils/heap/heap_test.c @@ -0,0 +1,78 @@ +/** + * collectd - src/tests/test_utils_heap.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "collectd.h" + +#include "testing.h" +#include "utils/heap/heap.h" + +static int compare(void const *v0, void const *v1) { + int const *i0 = v0; + int const *i1 = v1; + + if ((*i0) < (*i1)) + return -1; + else if ((*i0) > (*i1)) + return 1; + else + return 0; +} + +DEF_TEST(simple) { + int values[] = {9, 5, 6, 1, 3, 4, 0, 8, 2, 7}; + c_heap_t *h; + + CHECK_NOT_NULL(h = c_heap_create(compare)); + for (int i = 0; i < 10; i++) + CHECK_ZERO(c_heap_insert(h, &values[i])); + + for (int i = 0; i < 5; i++) { + int *ret = NULL; + CHECK_NOT_NULL(ret = c_heap_get_root(h)); + OK(*ret == i); + } + + CHECK_ZERO(c_heap_insert(h, &values[6] /* = 0 */)); + CHECK_ZERO(c_heap_insert(h, &values[3] /* = 1 */)); + CHECK_ZERO(c_heap_insert(h, &values[8] /* = 2 */)); + CHECK_ZERO(c_heap_insert(h, &values[4] /* = 3 */)); + CHECK_ZERO(c_heap_insert(h, &values[5] /* = 4 */)); + + for (int i = 0; i < 10; i++) { + int *ret = NULL; + CHECK_NOT_NULL(ret = c_heap_get_root(h)); + OK(*ret == i); + } + + c_heap_destroy(h); + return 0; +} + +int main(void) { + RUN_TEST(simple); + + END_TEST; +} diff --git a/src/utils/ignorelist/ignorelist.c b/src/utils/ignorelist/ignorelist.c new file mode 100644 index 00000000..9e1b9e3a --- /dev/null +++ b/src/utils/ignorelist/ignorelist.c @@ -0,0 +1,309 @@ +/** + * collectd - src/utils_ignorelist.c + * Copyright (C) 2006 Lubos Stanek + * Copyright (C) 2008 Florian Forster + * + * This program is free software; you can redistribute it and/ + * or modify it under the terms of the GNU General Public Li- + * cence as published by the Free Software Foundation; either + * version 2 of the Licence, or any later version. + * + * This program is distributed in the hope that it will be use- + * ful, but WITHOUT ANY WARRANTY; without even the implied war- + * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public Licence for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Lubos Stanek + * Florian Forster + **/ +/** + * ignorelist handles plugin's list of configured collectable + * entries with global ignore action + **/ +/** + * Usage: + * + * Define plugin's global pointer variable of type ignorelist_t: + * ignorelist_t *myconfig_ignore; + * If you know the state of the global ignore (IgnoreSelected), + * allocate the variable with: + * myconfig_ignore = ignorelist_create (YourKnownIgnore); + * If you do not know the state of the global ignore, + * initialize the global variable and set the ignore flag later: + * myconfig_ignore = ignorelist_init (); + * Append single entries in your cf_register'ed callback function: + * ignorelist_add (myconfig_ignore, newentry); + * When you hit the IgnoreSelected config option, + * offer it to the list: + * ignorelist_ignore (myconfig_ignore, instantly_got_value_of_ignore); + * That is all for the ignorelist initialization. + * Later during read and write (plugin's registered functions) get + * the information whether this entry would be collected or not: + * if (ignorelist_match (myconfig_ignore, thisentry)) + * return; + **/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" + +/* + * private prototypes + */ +struct ignorelist_item_s { +#if HAVE_REGEX_H + regex_t *rmatch; /* regular expression entry identification */ +#endif + char *smatch; /* string entry identification */ + struct ignorelist_item_s *next; +}; +typedef struct ignorelist_item_s ignorelist_item_t; + +struct ignorelist_s { + int ignore; /* ignore entries */ + ignorelist_item_t *head; /* pointer to the first entry */ +}; + +/* *** *** *** ********************************************* *** *** *** */ +/* *** *** *** *** *** *** private functions *** *** *** *** *** *** */ +/* *** *** *** ********************************************* *** *** *** */ + +static inline void ignorelist_append(ignorelist_t *il, + ignorelist_item_t *item) { + assert((il != NULL) && (item != NULL)); + + item->next = il->head; + il->head = item; +} + +#if HAVE_REGEX_H +static int ignorelist_append_regex(ignorelist_t *il, const char *re_str) { + regex_t *re; + ignorelist_item_t *entry; + int status; + + re = calloc(1, sizeof(*re)); + if (re == NULL) { + ERROR("ignorelist_append_regex: calloc failed."); + return ENOMEM; + } + + status = regcomp(re, re_str, REG_EXTENDED); + if (status != 0) { + char errbuf[1024]; + (void)regerror(status, re, errbuf, sizeof(errbuf)); + ERROR("utils_ignorelist: regcomp failed: %s", errbuf); + ERROR("ignorelist_append_regex: Compiling regular expression \"%s\" " + "failed: %s", + re_str, errbuf); + sfree(re); + return status; + } + + entry = calloc(1, sizeof(*entry)); + if (entry == NULL) { + ERROR("ignorelist_append_regex: calloc failed."); + regfree(re); + sfree(re); + return ENOMEM; + } + entry->rmatch = re; + + ignorelist_append(il, entry); + return 0; +} /* int ignorelist_append_regex */ +#endif + +static int ignorelist_append_string(ignorelist_t *il, const char *entry) { + ignorelist_item_t *new; + + /* create new entry */ + if ((new = calloc(1, sizeof(*new))) == NULL) { + ERROR("cannot allocate new entry"); + return 1; + } + new->smatch = sstrdup(entry); + + /* append new entry */ + ignorelist_append(il, new); + + return 0; +} /* int ignorelist_append_string(ignorelist_t *il, const char *entry) */ + +#if HAVE_REGEX_H +/* + * check list for entry regex match + * return 1 if found + */ +static int ignorelist_match_regex(ignorelist_item_t *item, const char *entry) { + assert((item != NULL) && (item->rmatch != NULL) && (entry != NULL) && + (strlen(entry) > 0)); + + /* match regex */ + if (regexec(item->rmatch, entry, 0, NULL, 0) == 0) + return 1; + + return 0; +} /* int ignorelist_match_regex (ignorelist_item_t *item, const char *entry) */ +#endif + +/* + * check list for entry string match + * return 1 if found + */ +static int ignorelist_match_string(ignorelist_item_t *item, const char *entry) { + assert((item != NULL) && (item->smatch != NULL) && (entry != NULL) && + (strlen(entry) > 0)); + + if (strcmp(entry, item->smatch) == 0) + return 1; + + return 0; +} /* int ignorelist_match_string (ignorelist_item_t *item, const char *entry) */ + +/* *** *** *** ******************************************** *** *** *** */ +/* *** *** *** *** *** *** public functions *** *** *** *** *** *** */ +/* *** *** *** ******************************************** *** *** *** */ + +/* + * create the ignorelist_t with known ignore state + * return pointer to ignorelist_t + */ +ignorelist_t *ignorelist_create(int invert) { + ignorelist_t *il; + + il = calloc(1, sizeof(*il)); + if (il == NULL) + return NULL; + + /* + * ->ignore == 0 => collect + * ->ignore == 1 => ignore + */ + il->ignore = invert ? 0 : 1; + + return il; +} /* ignorelist_t *ignorelist_create (int ignore) */ + +/* + * free memory used by ignorelist_t + */ +void ignorelist_free(ignorelist_t *il) { + ignorelist_item_t *this; + ignorelist_item_t *next; + + if (il == NULL) + return; + + for (this = il->head; this != NULL; this = next) { + next = this->next; +#if HAVE_REGEX_H + if (this->rmatch != NULL) { + regfree(this->rmatch); + sfree(this->rmatch); + this->rmatch = NULL; + } +#endif + if (this->smatch != NULL) { + sfree(this->smatch); + this->smatch = NULL; + } + sfree(this); + } + + sfree(il); +} /* void ignorelist_destroy (ignorelist_t *il) */ + +/* + * set ignore state of the ignorelist_t + */ +void ignorelist_set_invert(ignorelist_t *il, int invert) { + if (il == NULL) { + DEBUG("ignore call with ignorelist_t == NULL"); + return; + } + + il->ignore = invert ? 0 : 1; +} /* void ignorelist_set_invert (ignorelist_t *il, int ignore) */ + +/* + * append entry into ignorelist_t + * return 0 for success + */ +int ignorelist_add(ignorelist_t *il, const char *entry) { + size_t len; + + if (il == NULL) { + DEBUG("add called with ignorelist_t == NULL"); + return 1; + } + + len = strlen(entry); + + /* append nothing */ + if (len == 0) { + DEBUG("not appending: empty entry"); + return 1; + } + +#if HAVE_REGEX_H + /* regex string is enclosed in "/.../" */ + if ((len > 2) && (entry[0] == '/') && entry[len - 1] == '/') { + char *copy; + int status; + + /* skip leading slash */ + copy = strdup(entry + 1); + if (copy == NULL) + return ENOMEM; + + /* trim trailing slash */ + copy[strlen(copy) - 1] = '\0'; + + status = ignorelist_append_regex(il, copy); + sfree(copy); + return status; + } +#endif + + return ignorelist_append_string(il, entry); +} /* int ignorelist_add (ignorelist_t *il, const char *entry) */ + +/* + * check list for entry + * return 1 for ignored entry + */ +int ignorelist_match(ignorelist_t *il, const char *entry) { + /* if no entries, collect all */ + if ((il == NULL) || (il->head == NULL)) + return 0; + + if ((entry == NULL) || (strlen(entry) == 0)) + return 0; + + /* traverse list and check entries */ + for (ignorelist_item_t *traverse = il->head; traverse != NULL; + traverse = traverse->next) { +#if HAVE_REGEX_H + if (traverse->rmatch != NULL) { + if (ignorelist_match_regex(traverse, entry)) + return il->ignore; + } else +#endif + { + if (ignorelist_match_string(traverse, entry)) + return il->ignore; + } + } /* for traverse */ + + return 1 - il->ignore; +} /* int ignorelist_match (ignorelist_t *il, const char *entry) */ diff --git a/src/utils/ignorelist/ignorelist.h b/src/utils/ignorelist/ignorelist.h new file mode 100644 index 00000000..a7fa86d5 --- /dev/null +++ b/src/utils/ignorelist/ignorelist.h @@ -0,0 +1,69 @@ +/** + * collectd - src/utils_ignorelist.h + * Copyright (C) 2006 Lubos Stanek + * + * This program is free software; you can redistribute it and/ + * or modify it under the terms of the GNU General Public Li- + * cence as published by the Free Software Foundation; either + * version 2 of the Licence, or any later version. + * + * This program is distributed in the hope that it will be use- + * ful, but WITHOUT ANY WARRANTY; without even the implied war- + * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public Licence for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Lubos Stanek + **/ +/** + * ignorelist handles plugin's list of configured collectable + * entries with global ignore action + **/ + +#ifndef UTILS_IGNORELIST_H +#define UTILS_IGNORELIST_H 1 + +#include "collectd.h" + +#if HAVE_REGEX_H +#include +#endif + +/* public prototypes */ + +struct ignorelist_s; +typedef struct ignorelist_s ignorelist_t; + +/* + * create the ignorelist_t with known ignore state + * return pointer to ignorelist_t + */ +ignorelist_t *ignorelist_create(int invert); + +/* + * free memory used by ignorelist_t + */ +void ignorelist_free(ignorelist_t *il); + +/* + * set ignore state of the ignorelist_t + */ +void ignorelist_set_invert(ignorelist_t *il, int invert); + +/* + * append entry to ignorelist_t + * returns zero on success, non-zero upon failure. + */ +int ignorelist_add(ignorelist_t *il, const char *entry); + +/* + * check list for entry + * return 1 for ignored entry + */ +int ignorelist_match(ignorelist_t *il, const char *entry); + +#endif /* UTILS_IGNORELIST_H */ diff --git a/src/utils/latency/latency.c b/src/utils/latency/latency.c new file mode 100644 index 00000000..e32c97b8 --- /dev/null +++ b/src/utils/latency/latency.c @@ -0,0 +1,344 @@ +/** + * collectd - src/utils_latency.c + * Copyright (C) 2013 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils/latency/latency.h" + +#include +#include + +#ifndef LLONG_MAX +#define LLONG_MAX 9223372036854775807LL +#endif + +#ifndef HISTOGRAM_DEFAULT_BIN_WIDTH +/* 1048576 = 2^20 ^= 1/1024 s */ +#define HISTOGRAM_DEFAULT_BIN_WIDTH 1048576 +#endif + +struct latency_counter_s { + cdtime_t start_time; + + cdtime_t sum; + size_t num; + + cdtime_t min; + cdtime_t max; + + cdtime_t bin_width; + int histogram[HISTOGRAM_NUM_BINS]; +}; + +/* + * Histogram represents the distribution of data, it has a list of "bins". + * Each bin represents an interval and has a count (frequency) of + * number of values fall within its interval. + * + * Histogram's range is determined by the number of bins and the bin width, + * There are 1000 bins and all bins have the same width of default 1 + * millisecond. 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 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, ... + * + * So, if the required bin width is 300, then new bin width will be 512 as it is + * the next nearest power of 2. + */ +static void change_bin_width(latency_counter_t *lc, cdtime_t latency) /* {{{ */ +{ + /* This function is called because the new value is above histogram's range. + * First find the required bin width: + * requiredBinWidth = (value + 1) / numBins + * then get the next nearest power of 2 + * newBinWidth = 2^(ceil(log2(requiredBinWidth))) + */ + double required_bin_width = + ((double)(latency + 1)) / ((double)HISTOGRAM_NUM_BINS); + double required_bin_width_logbase2 = log(required_bin_width) / log(2.0); + cdtime_t new_bin_width = + (cdtime_t)(pow(2.0, ceil(required_bin_width_logbase2)) + .5); + cdtime_t old_bin_width = lc->bin_width; + + lc->bin_width = new_bin_width; + + /* bin_width has been increased, now iterate through all bins and move the + * old bin's count to new bin. */ + if (lc->num > 0) // if the histogram has data then iterate else skip + { + double width_change_ratio = + ((double)old_bin_width) / ((double)new_bin_width); + + for (size_t i = 0; i < HISTOGRAM_NUM_BINS; i++) { + size_t new_bin = (size_t)(((double)i) * width_change_ratio); + if (i == new_bin) + continue; + assert(new_bin < i); + + lc->histogram[new_bin] += lc->histogram[i]; + lc->histogram[i] = 0; + } + } + + DEBUG("utils_latency: change_bin_width: latency = %.3f; " + "old_bin_width = %.3f; new_bin_width = %.3f;", + CDTIME_T_TO_DOUBLE(latency), CDTIME_T_TO_DOUBLE(old_bin_width), + CDTIME_T_TO_DOUBLE(new_bin_width)); +} /* }}} void change_bin_width */ + +latency_counter_t *latency_counter_create(void) /* {{{ */ +{ + latency_counter_t *lc; + + lc = calloc(1, sizeof(*lc)); + if (lc == NULL) + return NULL; + + lc->bin_width = HISTOGRAM_DEFAULT_BIN_WIDTH; + latency_counter_reset(lc); + return lc; +} /* }}} latency_counter_t *latency_counter_create */ + +void latency_counter_destroy(latency_counter_t *lc) /* {{{ */ +{ + sfree(lc); +} /* }}} void latency_counter_destroy */ + +void latency_counter_add(latency_counter_t *lc, cdtime_t latency) /* {{{ */ +{ + cdtime_t bin; + + if ((lc == NULL) || (latency == 0) || (latency > ((cdtime_t)LLONG_MAX))) + return; + + lc->sum += latency; + lc->num++; + + if ((lc->min == 0) && (lc->max == 0)) + lc->min = lc->max = latency; + if (lc->min > latency) + lc->min = latency; + if (lc->max < latency) + lc->max = latency; + + /* A latency of _exactly_ 1.0 ms is stored in the buffer 0, so + * subtract one from the cdtime_t value so that exactly 1.0 ms get sorted + * accordingly. */ + bin = (latency - 1) / lc->bin_width; + if (bin >= HISTOGRAM_NUM_BINS) { + change_bin_width(lc, latency); + bin = (latency - 1) / lc->bin_width; + if (bin >= HISTOGRAM_NUM_BINS) { + P_ERROR("latency_counter_add: Invalid bin: %" PRIu64, bin); + return; + } + } + lc->histogram[bin]++; +} /* }}} void latency_counter_add */ + +void latency_counter_reset(latency_counter_t *lc) /* {{{ */ +{ + if (lc == NULL) + return; + + cdtime_t bin_width = lc->bin_width; + cdtime_t max_bin = (lc->max - 1) / lc->bin_width; + +/* + If max latency is REDUCE_THRESHOLD times less than histogram's range, + then cut it in half. REDUCE_THRESHOLD must be >= 2. + Value of 4 is selected to reduce frequent changes of bin width. +*/ +#define REDUCE_THRESHOLD 4 + if ((lc->num > 0) && (lc->bin_width >= HISTOGRAM_DEFAULT_BIN_WIDTH * 2) && + (max_bin < HISTOGRAM_NUM_BINS / REDUCE_THRESHOLD)) { + /* new bin width will be the previous power of 2 */ + bin_width = bin_width / 2; + + DEBUG("utils_latency: latency_counter_reset: max_latency = %.3f; " + "max_bin = %" PRIu64 "; old_bin_width = %.3f; new_bin_width = %.3f;", + CDTIME_T_TO_DOUBLE(lc->max), max_bin, + CDTIME_T_TO_DOUBLE(lc->bin_width), CDTIME_T_TO_DOUBLE(bin_width)); + } + + memset(lc, 0, sizeof(*lc)); + + /* preserve bin width */ + lc->bin_width = bin_width; + lc->start_time = cdtime(); +} /* }}} void latency_counter_reset */ + +cdtime_t latency_counter_get_min(latency_counter_t *lc) /* {{{ */ +{ + if (lc == NULL) + return 0; + return lc->min; +} /* }}} cdtime_t latency_counter_get_min */ + +cdtime_t latency_counter_get_max(latency_counter_t *lc) /* {{{ */ +{ + if (lc == NULL) + return 0; + return lc->max; +} /* }}} cdtime_t latency_counter_get_max */ + +cdtime_t latency_counter_get_sum(latency_counter_t *lc) /* {{{ */ +{ + if (lc == NULL) + return 0; + return lc->sum; +} /* }}} cdtime_t latency_counter_get_sum */ + +size_t latency_counter_get_num(latency_counter_t *lc) /* {{{ */ +{ + if (lc == NULL) + return 0; + return lc->num; +} /* }}} size_t latency_counter_get_num */ + +cdtime_t latency_counter_get_average(latency_counter_t *lc) /* {{{ */ +{ + double average; + + if ((lc == NULL) || (lc->num == 0)) + return 0; + + average = CDTIME_T_TO_DOUBLE(lc->sum) / ((double)lc->num); + return DOUBLE_TO_CDTIME_T(average); +} /* }}} cdtime_t latency_counter_get_average */ + +cdtime_t latency_counter_get_percentile(latency_counter_t *lc, /* {{{ */ + double percent) { + double percent_upper; + double percent_lower; + double p; + cdtime_t latency_lower; + cdtime_t latency_interpolated; + int sum; + size_t i; + + if ((lc == NULL) || (lc->num == 0) || !((percent > 0.0) && (percent < 100.0))) + return 0; + + /* Find index i so that at least "percent" events are within i+1 ms. */ + percent_upper = 0.0; + percent_lower = 0.0; + sum = 0; + for (i = 0; i < HISTOGRAM_NUM_BINS; i++) { + percent_lower = percent_upper; + sum += lc->histogram[i]; + if (sum == 0) + percent_upper = 0.0; + else + percent_upper = 100.0 * ((double)sum) / ((double)lc->num); + + if (percent_upper >= percent) + break; + } + + if (i >= HISTOGRAM_NUM_BINS) + return 0; + + assert(percent_upper >= percent); + assert(percent_lower < percent); + + if (i == 0) + return lc->bin_width; + + latency_lower = ((cdtime_t)i) * lc->bin_width; + p = (percent - percent_lower) / (percent_upper - percent_lower); + + latency_interpolated = + latency_lower + DOUBLE_TO_CDTIME_T(p * CDTIME_T_TO_DOUBLE(lc->bin_width)); + + DEBUG("latency_counter_get_percentile: latency_interpolated = %.3f", + CDTIME_T_TO_DOUBLE(latency_interpolated)); + return latency_interpolated; +} /* }}} cdtime_t latency_counter_get_percentile */ + +double latency_counter_get_rate(const latency_counter_t *lc, /* {{{ */ + cdtime_t lower, cdtime_t upper, + const cdtime_t now) { + if ((lc == NULL) || (lc->num == 0)) + return NAN; + + if (upper && (upper < lower)) + return NAN; + if (lower == upper) + return 0; + + /* Buckets have an exclusive lower bound and an inclusive upper bound. That + * means that the first bucket, index 0, represents (0-bin_width]. That means + * that latency==bin_width needs to result in bin=0, that's why we need to + * subtract one before dividing by bin_width. */ + cdtime_t lower_bin = 0; + if (lower) + /* lower is *exclusive* => determine bucket for lower+1 */ + lower_bin = ((lower + 1) - 1) / lc->bin_width; + + /* lower is greater than the longest latency observed => rate is zero. */ + if (lower_bin >= HISTOGRAM_NUM_BINS) + return 0; + + cdtime_t upper_bin = HISTOGRAM_NUM_BINS - 1; + if (upper) + upper_bin = (upper - 1) / lc->bin_width; + + if (upper_bin >= HISTOGRAM_NUM_BINS) { + upper_bin = HISTOGRAM_NUM_BINS - 1; + upper = 0; + } + + double sum = 0; + for (size_t i = lower_bin; i <= upper_bin; i++) + sum += lc->histogram[i]; + + if (lower) { + /* Approximate ratio of requests in lower_bin, that fall between + * lower_bin_boundary and lower. This ratio is then subtracted from sum to + * increase accuracy. */ + cdtime_t lower_bin_boundary = lower_bin * lc->bin_width; + assert(lower >= lower_bin_boundary); + double lower_ratio = + (double)(lower - lower_bin_boundary) / ((double)lc->bin_width); + sum -= lower_ratio * lc->histogram[lower_bin]; + } + + if (upper) { + /* As above: approximate ratio of requests in upper_bin, that fall between + * upper and upper_bin_boundary. */ + cdtime_t upper_bin_boundary = (upper_bin + 1) * lc->bin_width; + assert(upper <= upper_bin_boundary); + double ratio = (double)(upper_bin_boundary - upper) / (double)lc->bin_width; + sum -= ratio * lc->histogram[upper_bin]; + } + + return sum / (CDTIME_T_TO_DOUBLE(now - lc->start_time)); +} /* }}} double latency_counter_get_rate */ diff --git a/src/utils/latency/latency.h b/src/utils/latency/latency.h new file mode 100644 index 00000000..6c2c8aee --- /dev/null +++ b/src/utils/latency/latency.h @@ -0,0 +1,68 @@ +/** + * collectd - src/utils_latency.h + * Copyright (C) 2013 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian Forster + **/ + +#ifndef UTILS_LATENCY_LATENCY_H +#define UTILS_LATENCY_LATENCY_H 1 + +#include "collectd.h" + +#include "utils_time.h" + +#ifndef HISTOGRAM_NUM_BINS +#define HISTOGRAM_NUM_BINS 1000 +#endif + +struct latency_counter_s; +typedef struct latency_counter_s latency_counter_t; + +latency_counter_t *latency_counter_create(void); +void latency_counter_destroy(latency_counter_t *lc); + +void latency_counter_add(latency_counter_t *lc, cdtime_t latency); +void latency_counter_reset(latency_counter_t *lc); + +cdtime_t latency_counter_get_min(latency_counter_t *lc); +cdtime_t latency_counter_get_max(latency_counter_t *lc); +cdtime_t latency_counter_get_sum(latency_counter_t *lc); +size_t latency_counter_get_num(latency_counter_t *lc); +cdtime_t latency_counter_get_average(latency_counter_t *lc); +cdtime_t latency_counter_get_percentile(latency_counter_t *lc, double percent); + +/* + * NAME + * latency_counter_get_rate(counter,lower,upper,now) + * + * DESCRIPTION + * Calculates rate of latency values fall within requested interval. + * Interval specified as (lower,upper], i.e. the lower boundary is exclusive, + * the upper boundary is inclusive. + * When lower is zero, then the interval is (0, upper]. + * When upper is zero, then the interval is (lower, infinity). + */ +double latency_counter_get_rate(const latency_counter_t *lc, cdtime_t lower, + cdtime_t upper, const cdtime_t now); + +#endif /* UTILS_LATENCY_LATENCY_H */ diff --git a/src/utils/latency/latency_config.c b/src/utils/latency/latency_config.c new file mode 100644 index 00000000..7d614e78 --- /dev/null +++ b/src/utils/latency/latency_config.c @@ -0,0 +1,158 @@ +/** + * collectd - src/utils_latency_config.c + * Copyright (C) 2013-2016 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + * Pavel Rochnyack + */ + +#include "collectd.h" +#include "utils/common/common.h" +#include "utils/latency/latency_config.h" + +static int latency_config_add_percentile(latency_config_t *conf, + 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)) { + 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) { + P_ERROR("realloc failed."); + return ENOMEM; + } + conf->percentile = tmp; + conf->percentile[conf->percentile_num] = percent; + conf->percentile_num++; + + return 0; +} /* int latency_config_add_percentile */ + +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)) { + 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) { + P_ERROR("MIN must be less than MAX in \"%s\".", ci->key); + return ERANGE; + } + + if (ci->values[0].value.number < 0) { + 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) { + P_ERROR("realloc failed."); + return ENOMEM; + } + conf->buckets = tmp; + conf->buckets[conf->buckets_num].lower_bound = + DOUBLE_TO_CDTIME_T(ci->values[0].value.number); + conf->buckets[conf->buckets_num].upper_bound = + DOUBLE_TO_CDTIME_T(ci->values[1].value.number); + conf->buckets_num++; + + return 0; +} /* int latency_config_add_bucket */ + +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); + else if (strcasecmp("Bucket", child->key) == 0) + status = latency_config_add_bucket(conf, child); + else if (strcasecmp("BucketType", child->key) == 0) + status = cf_util_get_string(child, &conf->bucket_type); + else + P_WARNING("\"%s\" is not a valid option within a \"%s\" block.", + child->key, ci->key); + + if (status != 0) + return status; + } + + if ((status == 0) && (conf->percentile_num == 0) && + (conf->buckets_num == 0)) { + P_ERROR("The \"%s\" block must contain at least one " + "\"Percentile\" or \"Bucket\" option.", + ci->key); + return EINVAL; + } + + return 0; +} + +int latency_config_copy(latency_config_t *dst, const latency_config_t src) { + *dst = (latency_config_t){ + .percentile_num = src.percentile_num, + .buckets_num = src.buckets_num, + }; + + dst->percentile = calloc(dst->percentile_num, sizeof(*dst->percentile)); + dst->buckets = calloc(dst->buckets_num, sizeof(*dst->buckets)); + + if ((dst->percentile == NULL) || (dst->buckets == NULL)) { + latency_config_free(*dst); + return ENOMEM; + } + + if (src.bucket_type != NULL) { + dst->bucket_type = strdup(src.bucket_type); + if (dst->bucket_type == NULL) { + latency_config_free(*dst); + return ENOMEM; + } + } + + memmove(dst->percentile, src.percentile, + dst->percentile_num * sizeof(*dst->percentile)); + memmove(dst->buckets, src.buckets, dst->buckets_num * sizeof(*dst->buckets)); + + return 0; +} /* int latency_config_copy */ + +void latency_config_free(latency_config_t conf) { + sfree(conf.percentile); + sfree(conf.buckets); + sfree(conf.bucket_type); +} /* void latency_config_free */ diff --git a/src/utils/latency/latency_config.h b/src/utils/latency/latency_config.h new file mode 100644 index 00000000..3d2691a6 --- /dev/null +++ b/src/utils/latency/latency_config.h @@ -0,0 +1,62 @@ +/** + * collectd - src/utils_latency_config.c + * Copyright (C) 2013-2016 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + * Pavel Rochnyack + */ + +#ifndef UTILS_LATENCY_CONFIG_H +#define UTILS_LATENCY_CONFIG_H 1 + +#include "collectd.h" + +#include "liboconfig/oconfig.h" +#include "utils_time.h" + +typedef struct { + cdtime_t lower_bound; + cdtime_t upper_bound; +} latency_bucket_t; + +typedef struct { + double *percentile; + size_t percentile_num; + + latency_bucket_t *buckets; + size_t buckets_num; + char *bucket_type; + + /* + bool lower; + bool upper; + bool avg; + */ +} latency_config_t; + +int latency_config(latency_config_t *conf, oconfig_item_t *ci); + +int latency_config_copy(latency_config_t *dst, const latency_config_t src); + +void latency_config_free(latency_config_t conf); + +#endif /* UTILS_LATENCY_CONFIG_H */ diff --git a/src/utils/latency/latency_test.c b/src/utils/latency/latency_test.c new file mode 100644 index 00000000..89544631 --- /dev/null +++ b/src/utils/latency/latency_test.c @@ -0,0 +1,251 @@ +/** + * collectd - src/utils_latency_test.c + * Copyright (C) 2015 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#define DBL_PRECISION 1e-6 + +#include "collectd.h" +#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */ + +#include "testing.h" +#include "utils/latency/latency.h" +#include "utils_time.h" + +DEF_TEST(simple) { + struct { + double val; + double min; + double max; + double sum; + double avg; + } cases[] = { + /* val min max sum avg */ + {0.5, 0.5, 0.5, 0.5, 0.5}, {0.3, 0.3, 0.5, 0.8, 0.4}, + {0.7, 0.3, 0.7, 1.5, 0.5}, {2.5, 0.3, 2.5, 4.0, 1.0}, + {99, 0.3, 99, 103, 20.6}, + /* { -1, 0.3, 99, 103, 20.6}, see issue #1139 */ + }; + latency_counter_t *l; + + CHECK_NOT_NULL(l = latency_counter_create()); + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); 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)); + + EXPECT_EQ_DOUBLE(cases[i].min, + CDTIME_T_TO_DOUBLE(latency_counter_get_min(l))); + EXPECT_EQ_DOUBLE(cases[i].max, + CDTIME_T_TO_DOUBLE(latency_counter_get_max(l))); + EXPECT_EQ_DOUBLE(cases[i].sum, + CDTIME_T_TO_DOUBLE(latency_counter_get_sum(l))); + EXPECT_EQ_DOUBLE(cases[i].avg, + CDTIME_T_TO_DOUBLE(latency_counter_get_average(l))); + } + + latency_counter_destroy(l); + return 0; +} + +DEF_TEST(percentile) { + latency_counter_t *l; + + CHECK_NOT_NULL(l = latency_counter_create()); + + for (size_t i = 0; i < 100; i++) { + latency_counter_add(l, TIME_T_TO_CDTIME_T(((time_t)i) + 1)); + } + + EXPECT_EQ_DOUBLE(1.0, CDTIME_T_TO_DOUBLE(latency_counter_get_min(l))); + EXPECT_EQ_DOUBLE(100.0, CDTIME_T_TO_DOUBLE(latency_counter_get_max(l))); + EXPECT_EQ_DOUBLE(100.0 * 101.0 / 2.0, + CDTIME_T_TO_DOUBLE(latency_counter_get_sum(l))); + EXPECT_EQ_DOUBLE(50.5, CDTIME_T_TO_DOUBLE(latency_counter_get_average(l))); + + EXPECT_EQ_DOUBLE(50.0, + CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 50.0))); + EXPECT_EQ_DOUBLE(80.0, + CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 80.0))); + EXPECT_EQ_DOUBLE(95.0, + CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 95.0))); + EXPECT_EQ_DOUBLE(99.0, + CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 99.0))); + + CHECK_ZERO(latency_counter_get_percentile(l, -1.0)); + CHECK_ZERO(latency_counter_get_percentile(l, 101.0)); + + latency_counter_destroy(l); + return 0; +} + +DEF_TEST(get_rate) { + /* We re-declare the struct here so we can inspect its content. */ + struct { + cdtime_t start_time; + cdtime_t sum; + size_t num; + cdtime_t min; + cdtime_t max; + cdtime_t bin_width; + int histogram[HISTOGRAM_NUM_BINS]; + } * peek; + latency_counter_t *l; + + CHECK_NOT_NULL(l = latency_counter_create()); + peek = (void *)l; + + for (time_t i = 1; i <= 125; i++) { + latency_counter_add(l, TIME_T_TO_CDTIME_T(i)); + } + + /* We expect a bucket width of 125ms. */ + EXPECT_EQ_UINT64(DOUBLE_TO_CDTIME_T(0.125), peek->bin_width); + + struct { + size_t index; + int want; + } bucket_cases[] = { + {0, 0}, /* (0.000-0.125] */ + {1, 0}, /* (0.125-0.250] */ + {2, 0}, /* (0.250-0.375] */ + {3, 0}, /* (0.375-0.500] */ + {4, 0}, /* (0.500-0.625] */ + {5, 0}, /* (0.625-0.750] */ + {6, 0}, /* (0.750-0.875] */ + {7, 1}, /* (0.875-1.000] */ + {8, 0}, /* (1.000-1.125] */ + {9, 0}, /* (1.125-1.250] */ + {10, 0}, /* (1.250-1.375] */ + {11, 0}, /* (1.375-1.500] */ + {12, 0}, /* (1.500-1.625] */ + {13, 0}, /* (1.625-1.750] */ + {14, 0}, /* (1.750-1.875] */ + {15, 1}, /* (1.875-2.000] */ + {16, 0}, /* (2.000-2.125] */ + }; + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(bucket_cases); i++) { + size_t index = bucket_cases[i].index; + EXPECT_EQ_INT(bucket_cases[i].want, peek->histogram[index]); + } + + struct { + cdtime_t lower_bound; + cdtime_t upper_bound; + double want; + } cases[] = { + { + // bucket 6 is zero + DOUBLE_TO_CDTIME_T_STATIC(0.750), + DOUBLE_TO_CDTIME_T_STATIC(0.875), + 0.00, + }, + { + // bucket 7 contains the t=1 update + DOUBLE_TO_CDTIME_T_STATIC(0.875), + DOUBLE_TO_CDTIME_T_STATIC(1.000), + 1.00, + }, + { + // range: bucket 7 - bucket 15; contains the t=1 and t=2 updates + DOUBLE_TO_CDTIME_T_STATIC(0.875), + DOUBLE_TO_CDTIME_T_STATIC(2.000), + 2.00, + }, + { + // lower bucket is only partially applied + DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)), + DOUBLE_TO_CDTIME_T_STATIC(2.000), + 1.75, + }, + { + // upper bucket is only partially applied + DOUBLE_TO_CDTIME_T_STATIC(0.875), + DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), + 1.75, + }, + { + // both buckets are only partially applied + DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)), + DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), + 1.50, + }, + { + // lower bound is unspecified + 0, + DOUBLE_TO_CDTIME_T_STATIC(2.000), + 2.00, + }, + { + // upper bound is unspecified + DOUBLE_TO_CDTIME_T_STATIC(125.000 - 0.125), + 0, + 1.00, + }, + { + // overflow test: upper >> longest latency + DOUBLE_TO_CDTIME_T_STATIC(1.000), + DOUBLE_TO_CDTIME_T_STATIC(999999), + 124.00, + }, + { + // overflow test: lower > longest latency + DOUBLE_TO_CDTIME_T_STATIC(130), + 0, + 0.00, + }, + { + // lower > upper => error + DOUBLE_TO_CDTIME_T_STATIC(10), + DOUBLE_TO_CDTIME_T_STATIC(9), + NAN, + }, + { + // lower == upper => zero + DOUBLE_TO_CDTIME_T_STATIC(9), + DOUBLE_TO_CDTIME_T_STATIC(9), + 0.00, + }, + }; + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { + cdtime_t now = peek->start_time + TIME_T_TO_CDTIME_T(1); + EXPECT_EQ_DOUBLE(cases[i].want, + latency_counter_get_rate(l, cases[i].lower_bound, + cases[i].upper_bound, now)); + } + + latency_counter_destroy(l); + return 0; +} + +int main(void) { + RUN_TEST(simple); + RUN_TEST(percentile); + RUN_TEST(get_rate); + + END_TEST; +} diff --git a/src/utils/lookup/vl_lookup.c b/src/utils/lookup/vl_lookup.c new file mode 100644 index 00000000..38d81ca4 --- /dev/null +++ b/src/utils/lookup/vl_lookup.c @@ -0,0 +1,630 @@ +/** + * collectd - src/utils_vl_lookup.c + * Copyright (C) 2012 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian Forster + **/ + +#include "collectd.h" + +#include +#include + +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" +#include "utils/lookup/vl_lookup.h" + +#if HAVE_KSTAT_H +#include +#endif + +#if HAVE_LIBKSTAT +kstat_ctl_t *kc; +#endif /* HAVE_LIBKSTAT */ + +#if BUILD_TEST +#define sstrncpy strncpy +#define plugin_log(s, ...) \ + do { \ + printf("[severity %i] ", s); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } while (0) +#endif + +/* + * Types + */ +struct part_match_s { + char str[DATA_MAX_NAME_LEN]; + regex_t regex; + bool is_regex; +}; +typedef struct part_match_s part_match_t; + +struct identifier_match_s { + part_match_t host; + part_match_t plugin; + part_match_t plugin_instance; + part_match_t type; + part_match_t type_instance; + + unsigned int group_by; +}; +typedef struct identifier_match_s identifier_match_t; + +struct lookup_s { + c_avl_tree_t *by_type_tree; + + lookup_class_callback_t cb_user_class; + lookup_obj_callback_t cb_user_obj; + lookup_free_class_callback_t cb_free_class; + lookup_free_obj_callback_t cb_free_obj; +}; + +struct user_obj_s; +typedef struct user_obj_s user_obj_t; +struct user_obj_s { + void *user_obj; + lookup_identifier_t ident; + + user_obj_t *next; +}; + +struct user_class_s { + pthread_mutex_t lock; + void *user_class; + identifier_match_t match; + user_obj_t *user_obj_list; /* list of user_obj */ +}; +typedef struct user_class_s user_class_t; + +struct user_class_list_s; +typedef struct user_class_list_s user_class_list_t; +struct user_class_list_s { + user_class_t entry; + user_class_list_t *next; +}; + +struct by_type_entry_s { + c_avl_tree_t *by_plugin_tree; /* plugin -> user_class_list_t */ + user_class_list_t *wildcard_plugin_list; +}; +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) { + if (match->is_regex) { + /* Short cut popular catch-all regex. */ + if (strcmp(".*", match->str) == 0) + return true; + + int status = regexec(&match->regex, str, + /* nmatch = */ 0, /* pmatch = */ NULL, + /* flags = */ 0); + if (status == 0) + return true; + else + return false; + } else if (strcmp(match->str, str) == 0) + return true; + else + return false; +} /* }}} bool lu_part_matches */ + +static int lu_copy_ident_to_match_part(part_match_t *match_part, /* {{{ */ + char const *ident_part) { + size_t len = strlen(ident_part); + int status; + + if ((len < 3) || (ident_part[0] != '/') || (ident_part[len - 1] != '/')) { + sstrncpy(match_part->str, ident_part, sizeof(match_part->str)); + match_part->is_regex = false; + return 0; + } + + /* Copy string without the leading slash. */ + sstrncpy(match_part->str, ident_part + 1, sizeof(match_part->str)); + assert(sizeof(match_part->str) > len); + /* strip trailing slash */ + match_part->str[len - 2] = 0; + + status = regcomp(&match_part->regex, match_part->str, + /* flags = */ REG_EXTENDED); + if (status != 0) { + char errbuf[1024]; + regerror(status, &match_part->regex, errbuf, sizeof(errbuf)); + ERROR("utils_vl_lookup: Compiling regular expression \"%s\" failed: %s", + match_part->str, errbuf); + return EINVAL; + } + match_part->is_regex = true; + + return 0; +} /* }}} int lu_copy_ident_to_match_part */ + +static int lu_copy_ident_to_match(identifier_match_t *match, /* {{{ */ + lookup_identifier_t const *ident, + unsigned int group_by) { + memset(match, 0, sizeof(*match)); + + match->group_by = group_by; + +#define COPY_FIELD(field) \ + do { \ + int status = lu_copy_ident_to_match_part(&match->field, ident->field); \ + if (status != 0) \ + return status; \ + } while (0) + + COPY_FIELD(host); + COPY_FIELD(plugin); + COPY_FIELD(plugin_instance); + COPY_FIELD(type); + COPY_FIELD(type_instance); + +#undef COPY_FIELD + + return 0; +} /* }}} int lu_copy_ident_to_match */ + +/* user_class->lock must be held when calling this function */ +static void *lu_create_user_obj(lookup_t *obj, /* {{{ */ + data_set_t const *ds, value_list_t const *vl, + user_class_t *user_class) { + user_obj_t *user_obj; + + user_obj = calloc(1, sizeof(*user_obj)); + if (user_obj == NULL) { + ERROR("utils_vl_lookup: calloc failed."); + return NULL; + } + user_obj->next = NULL; + + user_obj->user_obj = obj->cb_user_class(ds, vl, user_class->user_class); + if (user_obj->user_obj == NULL) { + sfree(user_obj); + WARNING("utils_vl_lookup: User-provided constructor failed."); + return NULL; + } + +#define COPY_FIELD(field, group_mask) \ + do { \ + if (user_class->match.field.is_regex && \ + ((user_class->match.group_by & group_mask) == 0)) \ + sstrncpy(user_obj->ident.field, "/.*/", sizeof(user_obj->ident.field)); \ + else \ + sstrncpy(user_obj->ident.field, vl->field, \ + sizeof(user_obj->ident.field)); \ + } while (0) + + COPY_FIELD(host, LU_GROUP_BY_HOST); + COPY_FIELD(plugin, LU_GROUP_BY_PLUGIN); + COPY_FIELD(plugin_instance, LU_GROUP_BY_PLUGIN_INSTANCE); + COPY_FIELD(type, 0); + COPY_FIELD(type_instance, LU_GROUP_BY_TYPE_INSTANCE); + +#undef COPY_FIELD + + if (user_class->user_obj_list == NULL) { + user_class->user_obj_list = user_obj; + } else { + user_obj_t *last = user_class->user_obj_list; + while (last->next != NULL) + last = last->next; + last->next = user_obj; + } + + return user_obj; +} /* }}} void *lu_create_user_obj */ + +/* user_class->lock must be held when calling this function */ +static user_obj_t *lu_find_user_obj(user_class_t *user_class, /* {{{ */ + value_list_t const *vl) { + user_obj_t *ptr; + + for (ptr = user_class->user_obj_list; ptr != NULL; ptr = ptr->next) { + if (user_class->match.host.is_regex && + (user_class->match.group_by & LU_GROUP_BY_HOST) && + (strcmp(vl->host, ptr->ident.host) != 0)) + continue; + if (user_class->match.plugin.is_regex && + (user_class->match.group_by & LU_GROUP_BY_PLUGIN) && + (strcmp(vl->plugin, ptr->ident.plugin) != 0)) + continue; + if (user_class->match.plugin_instance.is_regex && + (user_class->match.group_by & LU_GROUP_BY_PLUGIN_INSTANCE) && + (strcmp(vl->plugin_instance, ptr->ident.plugin_instance) != 0)) + continue; + if (user_class->match.type_instance.is_regex && + (user_class->match.group_by & LU_GROUP_BY_TYPE_INSTANCE) && + (strcmp(vl->type_instance, ptr->ident.type_instance) != 0)) + continue; + + return ptr; + } + + return NULL; +} /* }}} user_obj_t *lu_find_user_obj */ + +static int lu_handle_user_class(lookup_t *obj, /* {{{ */ + data_set_t const *ds, value_list_t const *vl, + user_class_t *user_class) { + user_obj_t *user_obj; + int status; + + assert(strcmp(vl->type, user_class->match.type.str) == 0); + assert(user_class->match.plugin.is_regex || + (strcmp(vl->plugin, user_class->match.plugin.str)) == 0); + + if (!lu_part_matches(&user_class->match.type_instance, vl->type_instance) || + !lu_part_matches(&user_class->match.plugin_instance, + vl->plugin_instance) || + !lu_part_matches(&user_class->match.plugin, vl->plugin) || + !lu_part_matches(&user_class->match.host, vl->host)) + return 1; + + pthread_mutex_lock(&user_class->lock); + user_obj = lu_find_user_obj(user_class, vl); + if (user_obj == NULL) { + /* call lookup_class_callback_t() and insert into the list of user objects. + */ + user_obj = lu_create_user_obj(obj, ds, vl, user_class); + if (user_obj == NULL) { + pthread_mutex_unlock(&user_class->lock); + return -1; + } + } + pthread_mutex_unlock(&user_class->lock); + + status = obj->cb_user_obj(ds, vl, user_class->user_class, user_obj->user_obj); + if (status != 0) { + ERROR("utils_vl_lookup: The user object callback failed with status %i.", + status); + /* Returning a negative value means: abort! */ + if (status < 0) + return status; + else + return 1; + } + + return 0; +} /* }}} int lu_handle_user_class */ + +static int lu_handle_user_class_list(lookup_t *obj, /* {{{ */ + data_set_t const *ds, + value_list_t const *vl, + user_class_list_t *user_class_list) { + user_class_list_t *ptr; + int retval = 0; + + for (ptr = user_class_list; ptr != NULL; ptr = ptr->next) { + int status; + + status = lu_handle_user_class(obj, ds, vl, &ptr->entry); + if (status < 0) + return status; + else if (status == 0) + retval++; + } + + return retval; +} /* }}} int lu_handle_user_class_list */ + +static by_type_entry_t *lu_search_by_type(lookup_t *obj, /* {{{ */ + char const *type, + bool allocate_if_missing) { + by_type_entry_t *by_type; + char *type_copy; + int status; + + status = c_avl_get(obj->by_type_tree, type, (void *)&by_type); + if (status == 0) + return by_type; + + if (!allocate_if_missing) + return NULL; + + type_copy = strdup(type); + if (type_copy == NULL) { + ERROR("utils_vl_lookup: strdup failed."); + return NULL; + } + + by_type = calloc(1, sizeof(*by_type)); + if (by_type == NULL) { + ERROR("utils_vl_lookup: calloc failed."); + sfree(type_copy); + return NULL; + } + by_type->wildcard_plugin_list = NULL; + + by_type->by_plugin_tree = + c_avl_create((int (*)(const void *, const void *))strcmp); + if (by_type->by_plugin_tree == NULL) { + ERROR("utils_vl_lookup: c_avl_create failed."); + sfree(by_type); + sfree(type_copy); + return NULL; + } + + status = c_avl_insert(obj->by_type_tree, + /* key = */ type_copy, /* value = */ by_type); + assert(status <= 0); /* >0 => entry exists => race condition. */ + if (status != 0) { + ERROR("utils_vl_lookup: c_avl_insert failed."); + c_avl_destroy(by_type->by_plugin_tree); + sfree(by_type); + sfree(type_copy); + return NULL; + } + + return by_type; +} /* }}} by_type_entry_t *lu_search_by_type */ + +static int lu_add_by_plugin(by_type_entry_t *by_type, /* {{{ */ + user_class_list_t *user_class_list) { + user_class_list_t *ptr = NULL; + identifier_match_t const *match = &user_class_list->entry.match; + + /* Lookup user_class_list from the per-plugin structure. If this is the first + * user_class to be added, the block returns immediately. Otherwise they will + * set "ptr" to non-NULL. */ + if (match->plugin.is_regex) { + if (by_type->wildcard_plugin_list == NULL) { + by_type->wildcard_plugin_list = user_class_list; + return 0; + } + + ptr = by_type->wildcard_plugin_list; + } /* if (plugin is wildcard) */ + else /* (plugin is not wildcard) */ + { + int status; + + status = + c_avl_get(by_type->by_plugin_tree, match->plugin.str, (void *)&ptr); + + if (status != 0) /* plugin not yet in tree */ + { + char *plugin_copy = strdup(match->plugin.str); + + if (plugin_copy == NULL) { + ERROR("utils_vl_lookup: strdup failed."); + sfree(user_class_list); + return ENOMEM; + } + + status = + c_avl_insert(by_type->by_plugin_tree, plugin_copy, user_class_list); + if (status != 0) { + ERROR("utils_vl_lookup: c_avl_insert(\"%s\") failed with status %i.", + plugin_copy, status); + sfree(plugin_copy); + sfree(user_class_list); + return status; + } else { + return 0; + } + } /* if (plugin not yet in tree) */ + } /* if (plugin is not wildcard) */ + + assert(ptr != NULL); + + while (ptr->next != NULL) + ptr = ptr->next; + ptr->next = user_class_list; + + return 0; +} /* }}} int lu_add_by_plugin */ + +static void lu_destroy_user_obj(lookup_t *obj, /* {{{ */ + user_obj_t *user_obj) { + while (user_obj != NULL) { + user_obj_t *next = user_obj->next; + + if (obj->cb_free_obj != NULL) + obj->cb_free_obj(user_obj->user_obj); + user_obj->user_obj = NULL; + + sfree(user_obj); + user_obj = next; + } +} /* }}} void lu_destroy_user_obj */ + +static void lu_destroy_user_class_list(lookup_t *obj, /* {{{ */ + user_class_list_t *user_class_list) { + while (user_class_list != NULL) { + user_class_list_t *next = user_class_list->next; + + if (obj->cb_free_class != NULL) + obj->cb_free_class(user_class_list->entry.user_class); + user_class_list->entry.user_class = NULL; + +#define CLEAR_FIELD(field) \ + do { \ + if (user_class_list->entry.match.field.is_regex) { \ + regfree(&user_class_list->entry.match.field.regex); \ + user_class_list->entry.match.field.is_regex = false; \ + } \ + } while (0) + + CLEAR_FIELD(host); + CLEAR_FIELD(plugin); + CLEAR_FIELD(plugin_instance); + CLEAR_FIELD(type); + CLEAR_FIELD(type_instance); + +#undef CLEAR_FIELD + + lu_destroy_user_obj(obj, user_class_list->entry.user_obj_list); + user_class_list->entry.user_obj_list = NULL; + pthread_mutex_destroy(&user_class_list->entry.lock); + + sfree(user_class_list); + user_class_list = next; + } +} /* }}} void lu_destroy_user_class_list */ + +static void lu_destroy_by_type(lookup_t *obj, /* {{{ */ + by_type_entry_t *by_type) { + + while (42) { + char *plugin = NULL; + user_class_list_t *user_class_list = NULL; + int status; + + status = c_avl_pick(by_type->by_plugin_tree, (void *)&plugin, + (void *)&user_class_list); + if (status != 0) + break; + + DEBUG("utils_vl_lookup: lu_destroy_by_type: Destroying plugin \"%s\".", + plugin); + sfree(plugin); + lu_destroy_user_class_list(obj, user_class_list); + } + + c_avl_destroy(by_type->by_plugin_tree); + by_type->by_plugin_tree = NULL; + + lu_destroy_user_class_list(obj, by_type->wildcard_plugin_list); + by_type->wildcard_plugin_list = NULL; + + sfree(by_type); +} /* }}} int lu_destroy_by_type */ + +/* + * Public functions + */ +lookup_t *lookup_create(lookup_class_callback_t cb_user_class, /* {{{ */ + lookup_obj_callback_t cb_user_obj, + lookup_free_class_callback_t cb_free_class, + lookup_free_obj_callback_t cb_free_obj) { + lookup_t *obj = calloc(1, sizeof(*obj)); + if (obj == NULL) { + ERROR("utils_vl_lookup: calloc failed."); + return NULL; + } + + obj->by_type_tree = c_avl_create((int (*)(const void *, const void *))strcmp); + if (obj->by_type_tree == NULL) { + ERROR("utils_vl_lookup: c_avl_create failed."); + sfree(obj); + return NULL; + } + + obj->cb_user_class = cb_user_class; + obj->cb_user_obj = cb_user_obj; + obj->cb_free_class = cb_free_class; + obj->cb_free_obj = cb_free_obj; + + return obj; +} /* }}} lookup_t *lookup_create */ + +void lookup_destroy(lookup_t *obj) /* {{{ */ +{ + int status; + + if (obj == NULL) + return; + + while (42) { + char *type = NULL; + by_type_entry_t *by_type = NULL; + + status = c_avl_pick(obj->by_type_tree, (void *)&type, (void *)&by_type); + if (status != 0) + break; + + DEBUG("utils_vl_lookup: lookup_destroy: Destroying type \"%s\".", type); + sfree(type); + lu_destroy_by_type(obj, by_type); + } + + c_avl_destroy(obj->by_type_tree); + obj->by_type_tree = NULL; + + sfree(obj); +} /* }}} void lookup_destroy */ + +int lookup_add(lookup_t *obj, /* {{{ */ + lookup_identifier_t const *ident, unsigned int group_by, + void *user_class) { + by_type_entry_t *by_type = NULL; + user_class_list_t *user_class_obj; + + by_type = lu_search_by_type(obj, ident->type, /* allocate = */ true); + if (by_type == NULL) + return -1; + + user_class_obj = calloc(1, sizeof(*user_class_obj)); + if (user_class_obj == NULL) { + ERROR("utils_vl_lookup: calloc failed."); + return ENOMEM; + } + pthread_mutex_init(&user_class_obj->entry.lock, /* attr = */ NULL); + user_class_obj->entry.user_class = user_class; + lu_copy_ident_to_match(&user_class_obj->entry.match, ident, group_by); + user_class_obj->entry.user_obj_list = NULL; + user_class_obj->next = NULL; + + return lu_add_by_plugin(by_type, user_class_obj); +} /* }}} int lookup_add */ + +/* returns the number of successful calls to the callback function */ +int lookup_search(lookup_t *obj, /* {{{ */ + data_set_t const *ds, value_list_t const *vl) { + by_type_entry_t *by_type = NULL; + user_class_list_t *user_class_list = NULL; + int retval = 0; + int status; + + if ((obj == NULL) || (ds == NULL) || (vl == NULL)) + return -EINVAL; + + by_type = lu_search_by_type(obj, vl->type, /* allocate = */ false); + if (by_type == NULL) + return 0; + + status = + c_avl_get(by_type->by_plugin_tree, vl->plugin, (void *)&user_class_list); + if (status == 0) { + status = lu_handle_user_class_list(obj, ds, vl, user_class_list); + if (status < 0) + return status; + retval += status; + } + + if (by_type->wildcard_plugin_list != NULL) { + status = + lu_handle_user_class_list(obj, ds, vl, by_type->wildcard_plugin_list); + if (status < 0) + return status; + retval += status; + } + + return retval; +} /* }}} lookup_search */ diff --git a/src/utils/lookup/vl_lookup.h b/src/utils/lookup/vl_lookup.h new file mode 100644 index 00000000..90a4ee52 --- /dev/null +++ b/src/utils/lookup/vl_lookup.h @@ -0,0 +1,87 @@ +/** + * collectd - src/utils_vl_lookup.h + * Copyright (C) 2012 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian Forster + **/ + +#ifndef UTILS_VL_LOOKUP_H +#define UTILS_VL_LOOKUP_H 1 + +#include "plugin.h" + +/* + * Types + */ +struct lookup_s; +typedef struct lookup_s lookup_t; + +/* Given a user_class, constructs a new user_obj. */ +typedef void *(*lookup_class_callback_t)(data_set_t const *ds, + value_list_t const *vl, + void *user_class); + +/* Given a user_class and a ds/vl combination, does stuff with the data. + * This is the main working horse of the module. */ +typedef int (*lookup_obj_callback_t)(data_set_t const *ds, + value_list_t const *vl, void *user_class, + void *user_obj); + +/* Used to free user_class pointers. May be NULL in which case nothing is + * freed. */ +typedef void (*lookup_free_class_callback_t)(void *user_class); + +/* Used to free user_obj pointers. May be NULL in which case nothing is + * freed. */ +typedef void (*lookup_free_obj_callback_t)(void *user_obj); + +struct lookup_identifier_s { + char host[DATA_MAX_NAME_LEN]; + char plugin[DATA_MAX_NAME_LEN]; + char plugin_instance[DATA_MAX_NAME_LEN]; + char type[DATA_MAX_NAME_LEN]; + char type_instance[DATA_MAX_NAME_LEN]; +}; +typedef struct lookup_identifier_s lookup_identifier_t; + +#define LU_GROUP_BY_HOST 0x01 +#define LU_GROUP_BY_PLUGIN 0x02 +#define LU_GROUP_BY_PLUGIN_INSTANCE 0x04 +/* #define LU_GROUP_BY_TYPE 0x00 */ +#define LU_GROUP_BY_TYPE_INSTANCE 0x10 + +/* + * Functions + */ +__attribute__((nonnull(1, 2))) +lookup_t *lookup_create(lookup_class_callback_t, lookup_obj_callback_t, + lookup_free_class_callback_t, + lookup_free_obj_callback_t); +void lookup_destroy(lookup_t *obj); + +int lookup_add(lookup_t *obj, lookup_identifier_t const *ident, + unsigned int group_by, void *user_class); + +/* TODO(octo): Pass lookup_obj_callback_t to lookup_search()? */ +int lookup_search(lookup_t *obj, data_set_t const *ds, value_list_t const *vl); + +#endif /* UTILS_VL_LOOKUP_H */ diff --git a/src/utils/lookup/vl_lookup_test.c b/src/utils/lookup/vl_lookup_test.c new file mode 100644 index 00000000..2dd53ce3 --- /dev/null +++ b/src/utils/lookup/vl_lookup_test.c @@ -0,0 +1,242 @@ +/** + * collectd - src/tests/test_utils_vl_lookup.c + * Copyright (C) 2012 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian Forster + **/ + +#include "collectd.h" + +#include "testing.h" +#include "utils/lookup/vl_lookup.h" + +static bool expect_new_obj; +static bool have_new_obj; + +static lookup_identifier_t last_class_ident; +static lookup_identifier_t last_obj_ident; + +static data_source_t dsrc_test = {"value", DS_TYPE_DERIVE, 0.0, NAN}; +static data_set_t const ds_test = {"test", 1, &dsrc_test}; + +static data_source_t dsrc_unknown = {"value", DS_TYPE_DERIVE, 0.0, NAN}; +static data_set_t const ds_unknown = {"unknown", 1, &dsrc_unknown}; + +static int lookup_obj_callback(data_set_t const *ds, value_list_t const *vl, + void *user_class, void *user_obj) { + lookup_identifier_t *class = user_class; + lookup_identifier_t *obj = user_obj; + + OK1(expect_new_obj == have_new_obj, + (expect_new_obj ? "New obj is created." : "Updating existing obj.")); + + memcpy(&last_class_ident, class, sizeof(last_class_ident)); + memcpy(&last_obj_ident, obj, sizeof(last_obj_ident)); + + if (strcmp(obj->plugin_instance, "failure") == 0) + return -1; + + return 0; +} + +static void *lookup_class_callback(data_set_t const *ds, value_list_t const *vl, + void *user_class) { + lookup_identifier_t *class = user_class; + lookup_identifier_t *obj; + + assert(expect_new_obj); + + memcpy(&last_class_ident, class, sizeof(last_class_ident)); + + obj = malloc(sizeof(*obj)); + strncpy(obj->host, vl->host, sizeof(obj->host)); + strncpy(obj->plugin, vl->plugin, sizeof(obj->plugin)); + strncpy(obj->plugin_instance, vl->plugin_instance, + sizeof(obj->plugin_instance)); + strncpy(obj->type, vl->type, sizeof(obj->type)); + strncpy(obj->type_instance, vl->type_instance, sizeof(obj->type_instance)); + + have_new_obj = true; + + return (void *)obj; +} + +static int checked_lookup_add(lookup_t *obj, /* {{{ */ + char const *host, char const *plugin, + char const *plugin_instance, char const *type, + char const *type_instance, + unsigned int group_by) { + lookup_identifier_t ident = {{0}}; + void *user_class; + + 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) - 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)); + + OK(lookup_add(obj, &ident, group_by, user_class) == 0); + return 0; +} /* }}} int checked_lookup_add */ + +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) { + int status; + value_list_t vl = VALUE_LIST_INIT; + data_set_t const *ds = &ds_unknown; + + 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 = false; + + status = lookup_search(obj, ds, &vl); + return status; +} + +DEF_TEST(group_by_specific_host) { + lookup_t *obj; + CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback, + (void *)free, (void *)free)); + + checked_lookup_add(obj, "/.*/", "test", "", "test", "/.*/", LU_GROUP_BY_HOST); + checked_lookup_search(obj, "host0", "test", "", "test", "0", + /* expect new = */ 1); + checked_lookup_search(obj, "host0", "test", "", "test", "1", + /* expect new = */ 0); + checked_lookup_search(obj, "host1", "test", "", "test", "0", + /* expect new = */ 1); + checked_lookup_search(obj, "host1", "test", "", "test", "1", + /* expect new = */ 0); + + lookup_destroy(obj); + return 0; +} + +DEF_TEST(group_by_any_host) { + lookup_t *obj; + CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback, + (void *)free, (void *)free)); + + checked_lookup_add(obj, "/.*/", "/.*/", "/.*/", "test", "/.*/", + LU_GROUP_BY_HOST); + checked_lookup_search(obj, "host0", "plugin0", "", "test", "0", + /* expect new = */ 1); + checked_lookup_search(obj, "host0", "plugin0", "", "test", "1", + /* expect new = */ 0); + checked_lookup_search(obj, "host0", "plugin1", "", "test", "0", + /* expect new = */ 0); + checked_lookup_search(obj, "host0", "plugin1", "", "test", "1", + /* expect new = */ 0); + checked_lookup_search(obj, "host1", "plugin0", "", "test", "0", + /* expect new = */ 1); + checked_lookup_search(obj, "host1", "plugin0", "", "test", "1", + /* expect new = */ 0); + checked_lookup_search(obj, "host1", "plugin1", "", "test", "0", + /* expect new = */ 0); + checked_lookup_search(obj, "host1", "plugin1", "", "test", "1", + /* expect new = */ 0); + + lookup_destroy(obj); + return 0; +} + +DEF_TEST(multiple_lookups) { + lookup_t *obj; + int status; + + CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback, + (void *)free, (void *)free)); + + checked_lookup_add(obj, "/.*/", "plugin0", "", "test", "/.*/", + LU_GROUP_BY_HOST); + checked_lookup_add(obj, "/.*/", "/.*/", "", "test", "ti0", LU_GROUP_BY_HOST); + + status = checked_lookup_search(obj, "host0", "plugin1", "", "test", "", + /* expect new = */ 0); + assert(status == 0); + status = checked_lookup_search(obj, "host0", "plugin0", "", "test", "", + /* expect new = */ 1); + assert(status == 1); + status = checked_lookup_search(obj, "host0", "plugin1", "", "test", "ti0", + /* expect new = */ 1); + assert(status == 1); + status = checked_lookup_search(obj, "host0", "plugin0", "", "test", "ti0", + /* expect new = */ 0); + assert(status == 2); + + lookup_destroy(obj); + return 0; +} + +DEF_TEST(regex) { + lookup_t *obj; + CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback, + (void *)free, (void *)free)); + + checked_lookup_add(obj, "/^db[0-9]\\./", "cpu", "/.*/", "cpu", "/.*/", + LU_GROUP_BY_TYPE_INSTANCE); + checked_lookup_search(obj, "db0.example.com", "cpu", "0", "cpu", "user", + /* expect new = */ 1); + checked_lookup_search(obj, "db0.example.com", "cpu", "0", "cpu", "idle", + /* expect new = */ 1); + checked_lookup_search(obj, "db0.example.com", "cpu", "1", "cpu", "user", + /* expect new = */ 0); + checked_lookup_search(obj, "db0.example.com", "cpu", "1", "cpu", "idle", + /* expect new = */ 0); + checked_lookup_search(obj, "app0.example.com", "cpu", "0", "cpu", "user", + /* expect new = */ 0); + checked_lookup_search(obj, "app0.example.com", "cpu", "0", "cpu", "idle", + /* expect new = */ 0); + checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "user", + /* expect new = */ 0); + checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "idle", + /* expect new = */ 0); + checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "system", + /* expect new = */ 1); + + lookup_destroy(obj); + return 0; +} + +int main(int argc, char **argv) /* {{{ */ +{ + RUN_TEST(group_by_specific_host); + RUN_TEST(group_by_any_host); + RUN_TEST(multiple_lookups); + RUN_TEST(regex); + + END_TEST; +} /* }}} int main */ diff --git a/src/utils/match/match.c b/src/utils/match/match.c new file mode 100644 index 00000000..ca6f1aaa --- /dev/null +++ b/src/utils/match/match.c @@ -0,0 +1,376 @@ +/** + * collectd - src/utils_match.c + * Copyright (C) 2008-2014 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" + +#include "utils/match/match.h" + +#include + +#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02 +#define UTILS_MATCH_FLAGS_REGEX 0x04 + +struct cu_match_s { + regex_t regex; + regex_t excluderegex; + int flags; + + int (*callback)(const char *str, char *const *matches, size_t matches_num, + void *user_data); + void *user_data; + void (*free)(void *user_data); +}; + +/* + * Private functions + */ +static char *match_substr(const char *str, int begin, int end) { + char *ret; + size_t ret_len; + + if ((begin < 0) || (end < 0) || (begin >= end)) + return NULL; + if ((size_t)end > (strlen(str) + 1)) { + ERROR("utils_match: match_substr: `end' points after end of string."); + return NULL; + } + + ret_len = end - begin; + ret = malloc(ret_len + 1); + if (ret == NULL) { + ERROR("utils_match: match_substr: malloc failed."); + return NULL; + } + + sstrncpy(ret, str + begin, ret_len + 1); + return ret; +} /* char *match_substr */ + +static int default_callback(const char __attribute__((unused)) * str, + char *const *matches, size_t matches_num, + void *user_data) { + cu_match_value_t *data = (cu_match_value_t *)user_data; + + if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) { + gauge_t value; + char *endptr = NULL; + + if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC) { + data->value.gauge = isnan(data->value.gauge) ? 1 : data->value.gauge + 1; + data->values_num++; + return 0; + } + + if (matches_num < 2) + return -1; + + value = (gauge_t)strtod(matches[1], &endptr); + if (matches[1] == endptr) + return -1; + + if (data->ds_type & UTILS_MATCH_CF_GAUGE_DIST) { + latency_counter_add(data->latency, DOUBLE_TO_CDTIME_T(value)); + data->values_num++; + return 0; + } + + if ((data->values_num == 0) || + (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST) || + (data->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) { + data->value.gauge = value; + } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE) { + double f = ((double)data->values_num) / ((double)(data->values_num + 1)); + data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f)); + } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN) { + if (data->value.gauge > value) + data->value.gauge = value; + } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX) { + if (data->value.gauge < value) + data->value.gauge = value; + } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD) { + data->value.gauge += value; + } else { + ERROR("utils_match: default_callback: obj->ds_type is invalid!"); + return -1; + } + + data->values_num++; + } else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER) { + counter_t value; + char *endptr = NULL; + + if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC) { + data->value.counter++; + data->values_num++; + return 0; + } + + if (matches_num < 2) + return -1; + + value = (counter_t)strtoull(matches[1], &endptr, 0); + if (matches[1] == endptr) + return -1; + + if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET) + data->value.counter = value; + else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD) + data->value.counter += value; + else { + ERROR("utils_match: default_callback: obj->ds_type is invalid!"); + return -1; + } + + data->values_num++; + } else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE) { + derive_t value; + char *endptr = NULL; + + if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC) { + data->value.derive++; + data->values_num++; + return 0; + } + + if (matches_num < 2) + return -1; + + value = (derive_t)strtoll(matches[1], &endptr, 0); + if (matches[1] == endptr) + return -1; + + if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET) + data->value.derive = value; + else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD) + data->value.derive += value; + else { + ERROR("utils_match: default_callback: obj->ds_type is invalid!"); + return -1; + } + + data->values_num++; + } else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE) { + absolute_t value; + char *endptr = NULL; + + if (matches_num < 2) + return -1; + + value = (absolute_t)strtoull(matches[1], &endptr, 0); + if (matches[1] == endptr) + return -1; + + if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET) + data->value.absolute = value; + else { + ERROR("utils_match: default_callback: obj->ds_type is invalid!"); + return -1; + } + + data->values_num++; + } else { + ERROR("utils_match: default_callback: obj->ds_type is invalid!"); + return -1; + } + + return 0; +} /* int default_callback */ + +static void match_simple_free(void *data) { + cu_match_value_t *user_data = (cu_match_value_t *)data; + if (user_data->latency) + latency_counter_destroy(user_data->latency); + + free(data); +} /* void match_simple_free */ + +/* + * Public functions + */ +cu_match_t * +match_create_callback(const char *regex, const char *excluderegex, + int (*callback)(const char *str, char *const *matches, + size_t matches_num, void *user_data), + void *user_data, + void (*free_user_data)(void *user_data)) { + cu_match_t *obj; + int status; + + DEBUG("utils_match: match_create_callback: regex = %s, excluderegex = %s", + regex, excluderegex); + + obj = calloc(1, sizeof(*obj)); + if (obj == NULL) + return NULL; + + status = regcomp(&obj->regex, regex, REG_EXTENDED | REG_NEWLINE); + if (status != 0) { + ERROR("Compiling the regular expression \"%s\" failed.", regex); + sfree(obj); + return NULL; + } + obj->flags |= UTILS_MATCH_FLAGS_REGEX; + + if (excluderegex && strcmp(excluderegex, "") != 0) { + status = regcomp(&obj->excluderegex, excluderegex, REG_EXTENDED); + if (status != 0) { + ERROR("Compiling the excluding regular expression \"%s\" failed.", + excluderegex); + sfree(obj); + return NULL; + } + obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX; + } + + obj->callback = callback; + obj->user_data = user_data; + obj->free = free_user_data; + + return obj; +} /* cu_match_t *match_create_callback */ + +cu_match_t *match_create_simple(const char *regex, const char *excluderegex, + int match_ds_type) { + cu_match_value_t *user_data; + cu_match_t *obj; + + user_data = calloc(1, sizeof(*user_data)); + if (user_data == NULL) + return NULL; + user_data->ds_type = match_ds_type; + + if ((match_ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && + (match_ds_type & UTILS_MATCH_CF_GAUGE_DIST)) { + user_data->latency = latency_counter_create(); + if (user_data->latency == NULL) { + ERROR("match_create_simple(): latency_counter_create() failed."); + free(user_data); + return NULL; + } + } + + obj = match_create_callback(regex, excluderegex, default_callback, user_data, + match_simple_free); + if (obj == NULL) { + if (user_data->latency) + latency_counter_destroy(user_data->latency); + + sfree(user_data); + return NULL; + } + return obj; +} /* cu_match_t *match_create_simple */ + +void match_value_reset(cu_match_value_t *mv) { + if (mv == NULL) + return; + + /* Reset GAUGE metrics only and except GAUGE_PERSIST. */ + if ((mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && + !(mv->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) { + mv->value.gauge = (mv->ds_type & UTILS_MATCH_CF_GAUGE_INC) ? 0 : NAN; + mv->values_num = 0; + } +} /* }}} void match_value_reset */ + +void match_destroy(cu_match_t *obj) { + if (obj == NULL) + return; + + if (obj->flags & UTILS_MATCH_FLAGS_REGEX) + regfree(&obj->regex); + if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) + regfree(&obj->excluderegex); + if ((obj->user_data != NULL) && (obj->free != NULL)) + (*obj->free)(obj->user_data); + + sfree(obj); +} /* void match_destroy */ + +int match_apply(cu_match_t *obj, const char *str) { + int status; + regmatch_t re_match[32]; + char *matches[32] = {0}; + size_t matches_num; + + if ((obj == NULL) || (str == NULL)) + return -1; + + if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) { + status = + regexec(&obj->excluderegex, str, STATIC_ARRAY_SIZE(re_match), re_match, + /* eflags = */ 0); + /* Regex did match, so exclude this line */ + if (status == 0) { + DEBUG("ExludeRegex matched, don't count that line\n"); + return 0; + } + } + + status = regexec(&obj->regex, str, STATIC_ARRAY_SIZE(re_match), re_match, + /* eflags = */ 0); + + /* Regex did not match */ + if (status != 0) + return 0; + + for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE(matches); + matches_num++) { + if ((re_match[matches_num].rm_so < 0) || (re_match[matches_num].rm_eo < 0)) + break; + + matches[matches_num] = match_substr(str, re_match[matches_num].rm_so, + re_match[matches_num].rm_eo); + if (matches[matches_num] == NULL) { + status = -1; + break; + } + } + + if (status != 0) { + ERROR("utils_match: match_apply: match_substr failed."); + } else { + status = obj->callback(str, matches, matches_num, obj->user_data); + if (status != 0) { + ERROR("utils_match: match_apply: callback failed."); + } + } + + for (size_t i = 0; i < matches_num; i++) { + sfree(matches[i]); + } + + return status; +} /* int match_apply */ + +void *match_get_user_data(cu_match_t *obj) { + if (obj == NULL) + return NULL; + return obj->user_data; +} /* void *match_get_user_data */ diff --git a/src/utils/match/match.h b/src/utils/match/match.h new file mode 100644 index 00000000..c4aee0a1 --- /dev/null +++ b/src/utils/match/match.h @@ -0,0 +1,179 @@ +/** + * collectd - src/utils_match.h + * Copyright (C) 2008-2014 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_MATCH_H +#define UTILS_MATCH_H 1 + +#include "plugin.h" +#include "utils/latency/latency.h" + +/* + * Each type may have 12 sub-types + * 0x1000 = 1000000000000 + * ^ <- Type bit + * ^^^^^^^^^^^^ <- Subtype bits + */ +#define UTILS_MATCH_DS_TYPE_GAUGE 0x1000 +#define UTILS_MATCH_DS_TYPE_COUNTER 0x2000 +#define UTILS_MATCH_DS_TYPE_DERIVE 0x4000 +#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x8000 + +#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01 +#define UTILS_MATCH_CF_GAUGE_MIN 0x02 +#define UTILS_MATCH_CF_GAUGE_MAX 0x04 +#define UTILS_MATCH_CF_GAUGE_LAST 0x08 +#define UTILS_MATCH_CF_GAUGE_INC 0x10 +#define UTILS_MATCH_CF_GAUGE_ADD 0x20 +#define UTILS_MATCH_CF_GAUGE_PERSIST 0x40 +#define UTILS_MATCH_CF_GAUGE_DIST 0x80 + +#define UTILS_MATCH_CF_COUNTER_SET 0x01 +#define UTILS_MATCH_CF_COUNTER_ADD 0x02 +#define UTILS_MATCH_CF_COUNTER_INC 0x04 + +#define UTILS_MATCH_CF_DERIVE_SET 0x01 +#define UTILS_MATCH_CF_DERIVE_ADD 0x02 +#define UTILS_MATCH_CF_DERIVE_INC 0x04 + +#define UTILS_MATCH_CF_ABSOLUTE_SET 0x01 +#define UTILS_MATCH_CF_ABSOLUTE_ADD 0x02 +#define UTILS_MATCH_CF_ABSOLUTE_INC 0x04 + +/* + * Data types + */ +struct cu_match_s; +typedef struct cu_match_s cu_match_t; + +struct cu_match_value_s { + int ds_type; + value_t value; + unsigned int values_num; + latency_counter_t *latency; +}; +typedef struct cu_match_value_s cu_match_value_t; + +/* + * Prototypes + */ +/* + * NAME + * match_create_callback + * + * DESCRIPTION + * Creates a new `cu_match_t' object which will use the regular expression + * `regex' to match lines, see the `match_apply' method below. If the line + * matches, the callback passed in `callback' will be called along with the + * pointer `user_pointer'. + * The string that's passed to the callback depends on the regular expression: + * If the regular expression includes a sub-match, i. e. something like + * "value=([0-9][0-9]*)" + * then only the submatch (the part in the parenthesis) will be passed to the + * callback. If there is no submatch, then the entire string is passed to the + * callback. + * The optional `excluderegex' allows to exclude the line from the match, if + * the excluderegex matches. + * When `match_destroy' is called the `user_data' pointer is freed using + * the `free_user_data' callback - if it is not NULL. + */ +cu_match_t * +match_create_callback(const char *regex, const char *excluderegex, + int (*callback)(const char *str, char *const *matches, + size_t matches_num, void *user_data), + void *user_data, void (*free_user_data)(void *user_data)); + +/* + * NAME + * match_create_simple + * + * DESCRIPTION + * Creates a new `cu_match_t' with a default callback. The user data for that + * default callback will be a `cu_match_value_t' structure, with + * `ds_type' copied to the structure. The default callback will handle the + * string as containing a number (see strtoll(3) and strtod(3)) and store that + * number in the `value' member. How that is done depends on `ds_type': + * + * UTILS_MATCH_DS_TYPE_GAUGE + * The function will search for a floating point number in the string and + * store it in value.gauge. + * UTILS_MATCH_DS_TYPE_COUNTER_SET + * The function will search for an integer in the string and store it in + * value.counter. + * UTILS_MATCH_DS_TYPE_COUNTER_ADD + * The function will search for an integer in the string and add it to the + * value in value.counter. + * UTILS_MATCH_DS_TYPE_COUNTER_INC + * The function will not search for anything in the string and increase + * value.counter by one. + */ +cu_match_t *match_create_simple(const char *regex, const char *excluderegex, + int ds_type); + +/* + * NAME + * match_value_reset + * + * DESCRIPTION + * Resets the internal state, if applicable. This function must be called + * after each iteration for "simple" matches, usually after dispatching the + * metrics. + */ +void match_value_reset(cu_match_value_t *mv); + +/* + * NAME + * match_destroy + * + * DESCRIPTION + * Destroys the object and frees all internal resources. + */ +void match_destroy(cu_match_t *obj); + +/* + * NAME + * match_apply + * + * DESCRIPTION + * Tries to match the string `str' with the regular expression of `obj'. If + * the string matches, calls the callback in `obj' with the (sub-)match. + * + * The user_data pointer passed to `match_create_callback' is NOT freed + * automatically. The `cu_match_value_t' structure allocated by + * `match_create_callback' is freed automatically. + */ +int match_apply(cu_match_t *obj, const char *str); + +/* + * NAME + * match_get_user_data + * + * DESCRIPTION + * Returns the pointer passed to `match_create_callback' or a pointer to the + * `cu_match_value_t' structure allocated by `match_create_simple'. + */ +void *match_get_user_data(cu_match_t *obj); + +#endif /* UTILS_MATCH_H */ diff --git a/src/utils/metadata/meta_data.c b/src/utils/metadata/meta_data.c new file mode 100644 index 00000000..963aebbe --- /dev/null +++ b/src/utils/metadata/meta_data.c @@ -0,0 +1,747 @@ +/** + * collectd - src/meta_data.c + * Copyright (C) 2008-2011 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils/metadata/meta_data.h" + +#define MD_MAX_NONSTRING_CHARS 128 + +/* + * Data types + */ +union meta_value_u { + char *mv_string; + int64_t mv_signed_int; + uint64_t mv_unsigned_int; + double mv_double; + bool mv_boolean; +}; +typedef union meta_value_u meta_value_t; + +struct meta_entry_s; +typedef struct meta_entry_s meta_entry_t; +struct meta_entry_s { + char *key; + meta_value_t value; + int type; + meta_entry_t *next; +}; + +struct meta_data_s { + meta_entry_t *head; + pthread_mutex_t lock; +}; + +/* + * Private functions + */ +static char *md_strdup(const char *orig) /* {{{ */ +{ + size_t sz; + char *dest; + + if (orig == NULL) + return NULL; + + sz = strlen(orig) + 1; + dest = malloc(sz); + if (dest == NULL) + return NULL; + + memcpy(dest, orig, sz); + + return dest; +} /* }}} char *md_strdup */ + +static meta_entry_t *md_entry_alloc(const char *key) /* {{{ */ +{ + meta_entry_t *e; + + e = calloc(1, sizeof(*e)); + if (e == NULL) { + ERROR("md_entry_alloc: calloc failed."); + return NULL; + } + + e->key = md_strdup(key); + if (e->key == NULL) { + free(e); + ERROR("md_entry_alloc: md_strdup failed."); + return NULL; + } + + e->type = 0; + e->next = NULL; + + return e; +} /* }}} meta_entry_t *md_entry_alloc */ + +/* XXX: The lock on md must be held while calling this function! */ +static meta_entry_t *md_entry_clone_contents(const meta_entry_t *orig) /* {{{ */ +{ + meta_entry_t *copy; + + /* WARNINGS : + * - we do not check that orig != NULL here. You should have done it before. + * - we do not set copy->next. DO NOT FORGET TO SET copy->next IN YOUR + * FUNCTION + */ + + copy = md_entry_alloc(orig->key); + if (copy == NULL) + return NULL; + copy->type = orig->type; + if (copy->type == MD_TYPE_STRING) + copy->value.mv_string = strdup(orig->value.mv_string); + else + copy->value = orig->value; + + return copy; +} /* }}} meta_entry_t *md_entry_clone_contents */ + +static meta_entry_t *md_entry_clone(const meta_entry_t *orig) /* {{{ */ +{ + meta_entry_t *copy; + + if (orig == NULL) + return NULL; + + copy = md_entry_clone_contents(orig); + + copy->next = md_entry_clone(orig->next); + return copy; +} /* }}} meta_entry_t *md_entry_clone */ + +static void md_entry_free(meta_entry_t *e) /* {{{ */ +{ + if (e == NULL) + return; + + free(e->key); + + if (e->type == MD_TYPE_STRING) + free(e->value.mv_string); + + if (e->next != NULL) + md_entry_free(e->next); + + free(e); +} /* }}} void md_entry_free */ + +static int md_entry_insert(meta_data_t *md, meta_entry_t *e) /* {{{ */ +{ + meta_entry_t *this; + meta_entry_t *prev; + + if ((md == NULL) || (e == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + prev = NULL; + this = md->head; + while (this != NULL) { + if (strcasecmp(e->key, this->key) == 0) + break; + + prev = this; + this = this->next; + } + + if (this == NULL) { + /* This key does not exist yet. */ + if (md->head == NULL) + md->head = e; + else { + assert(prev != NULL); + prev->next = e; + } + + e->next = NULL; + } else /* (this != NULL) */ + { + if (prev == NULL) + md->head = e; + else + prev->next = e; + + e->next = this->next; + } + + pthread_mutex_unlock(&md->lock); + + if (this != NULL) { + this->next = NULL; + md_entry_free(this); + } + + return 0; +} /* }}} int md_entry_insert */ + +/* XXX: The lock on md must be held while calling this function! */ +static int md_entry_insert_clone(meta_data_t *md, meta_entry_t *orig) /* {{{ */ +{ + meta_entry_t *e; + meta_entry_t *this; + meta_entry_t *prev; + + /* WARNINGS : + * - we do not check that md and e != NULL here. You should have done it + * before. + * - we do not use the lock. You should have set it before. + */ + + e = md_entry_clone_contents(orig); + + prev = NULL; + this = md->head; + while (this != NULL) { + if (strcasecmp(e->key, this->key) == 0) + break; + + prev = this; + this = this->next; + } + + if (this == NULL) { + /* This key does not exist yet. */ + if (md->head == NULL) + md->head = e; + else { + assert(prev != NULL); + prev->next = e; + } + + e->next = NULL; + } else /* (this != NULL) */ + { + if (prev == NULL) + md->head = e; + else + prev->next = e; + + e->next = this->next; + } + + if (this != NULL) { + this->next = NULL; + md_entry_free(this); + } + + return 0; +} /* }}} int md_entry_insert_clone */ + +/* XXX: The lock on md must be held while calling this function! */ +static meta_entry_t *md_entry_lookup(meta_data_t *md, /* {{{ */ + const char *key) { + meta_entry_t *e; + + if ((md == NULL) || (key == NULL)) + return NULL; + + for (e = md->head; e != NULL; e = e->next) + if (strcasecmp(key, e->key) == 0) + break; + + return e; +} /* }}} meta_entry_t *md_entry_lookup */ + +/* + * Each value_list_t*, as it is going through the system, is handled by exactly + * one thread. Plugins which pass a value_list_t* to another thread, e.g. the + * rrdtool plugin, must create a copy first. The meta data within a + * value_list_t* is not thread safe and doesn't need to be. + * + * The meta data associated with cache entries are a different story. There, we + * need to ensure exclusive locking to prevent leaks and other funky business. + * This is ensured by the uc_meta_data_get_*() functions. + */ + +/* + * Public functions + */ +meta_data_t *meta_data_create(void) /* {{{ */ +{ + meta_data_t *md; + + md = calloc(1, sizeof(*md)); + if (md == NULL) { + ERROR("meta_data_create: calloc failed."); + return NULL; + } + + pthread_mutex_init(&md->lock, /* attr = */ NULL); + + return md; +} /* }}} meta_data_t *meta_data_create */ + +meta_data_t *meta_data_clone(meta_data_t *orig) /* {{{ */ +{ + meta_data_t *copy; + + if (orig == NULL) + return NULL; + + copy = meta_data_create(); + if (copy == NULL) + return NULL; + + pthread_mutex_lock(&orig->lock); + copy->head = md_entry_clone(orig->head); + pthread_mutex_unlock(&orig->lock); + + return copy; +} /* }}} meta_data_t *meta_data_clone */ + +int meta_data_clone_merge(meta_data_t **dest, meta_data_t *orig) /* {{{ */ +{ + if (orig == NULL) + return 0; + + if (*dest == NULL) { + *dest = meta_data_clone(orig); + return 0; + } + + pthread_mutex_lock(&orig->lock); + for (meta_entry_t *e = orig->head; e != NULL; e = e->next) { + md_entry_insert_clone((*dest), e); + } + pthread_mutex_unlock(&orig->lock); + + return 0; +} /* }}} int meta_data_clone_merge */ + +void meta_data_destroy(meta_data_t *md) /* {{{ */ +{ + if (md == NULL) + return; + + md_entry_free(md->head); + pthread_mutex_destroy(&md->lock); + free(md); +} /* }}} void meta_data_destroy */ + +int meta_data_exists(meta_data_t *md, const char *key) /* {{{ */ +{ + if ((md == NULL) || (key == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + for (meta_entry_t *e = md->head; e != NULL; e = e->next) { + if (strcasecmp(key, e->key) == 0) { + pthread_mutex_unlock(&md->lock); + return 1; + } + } + + pthread_mutex_unlock(&md->lock); + return 0; +} /* }}} int meta_data_exists */ + +int meta_data_type(meta_data_t *md, const char *key) /* {{{ */ +{ + if ((md == NULL) || (key == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + for (meta_entry_t *e = md->head; e != NULL; e = e->next) { + if (strcasecmp(key, e->key) == 0) { + pthread_mutex_unlock(&md->lock); + return e->type; + } + } + + pthread_mutex_unlock(&md->lock); + return 0; +} /* }}} int meta_data_type */ + +int meta_data_toc(meta_data_t *md, char ***toc) /* {{{ */ +{ + int i = 0, count = 0; + + if ((md == NULL) || (toc == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + for (meta_entry_t *e = md->head; e != NULL; e = e->next) + ++count; + + if (count == 0) { + pthread_mutex_unlock(&md->lock); + return count; + } + + *toc = calloc(count, sizeof(**toc)); + for (meta_entry_t *e = md->head; e != NULL; e = e->next) + (*toc)[i++] = strdup(e->key); + + pthread_mutex_unlock(&md->lock); + return count; +} /* }}} int meta_data_toc */ + +int meta_data_delete(meta_data_t *md, const char *key) /* {{{ */ +{ + meta_entry_t *this; + meta_entry_t *prev; + + if ((md == NULL) || (key == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + prev = NULL; + this = md->head; + while (this != NULL) { + if (strcasecmp(key, this->key) == 0) + break; + + prev = this; + this = this->next; + } + + if (this == NULL) { + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + if (prev == NULL) + md->head = this->next; + else + prev->next = this->next; + + pthread_mutex_unlock(&md->lock); + + this->next = NULL; + md_entry_free(this); + + return 0; +} /* }}} int meta_data_delete */ + +/* + * Add functions + */ +int meta_data_add_string(meta_data_t *md, /* {{{ */ + const char *key, const char *value) { + meta_entry_t *e; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return -EINVAL; + + e = md_entry_alloc(key); + if (e == NULL) + return -ENOMEM; + + e->value.mv_string = md_strdup(value); + if (e->value.mv_string == NULL) { + ERROR("meta_data_add_string: md_strdup failed."); + md_entry_free(e); + return -ENOMEM; + } + e->type = MD_TYPE_STRING; + + return md_entry_insert(md, e); +} /* }}} int meta_data_add_string */ + +int meta_data_add_signed_int(meta_data_t *md, /* {{{ */ + const char *key, int64_t value) { + meta_entry_t *e; + + if ((md == NULL) || (key == NULL)) + return -EINVAL; + + e = md_entry_alloc(key); + if (e == NULL) + return -ENOMEM; + + e->value.mv_signed_int = value; + e->type = MD_TYPE_SIGNED_INT; + + return md_entry_insert(md, e); +} /* }}} int meta_data_add_signed_int */ + +int meta_data_add_unsigned_int(meta_data_t *md, /* {{{ */ + const char *key, uint64_t value) { + meta_entry_t *e; + + if ((md == NULL) || (key == NULL)) + return -EINVAL; + + e = md_entry_alloc(key); + if (e == NULL) + return -ENOMEM; + + e->value.mv_unsigned_int = value; + e->type = MD_TYPE_UNSIGNED_INT; + + return md_entry_insert(md, e); +} /* }}} int meta_data_add_unsigned_int */ + +int meta_data_add_double(meta_data_t *md, /* {{{ */ + const char *key, double value) { + meta_entry_t *e; + + if ((md == NULL) || (key == NULL)) + return -EINVAL; + + e = md_entry_alloc(key); + if (e == NULL) + return -ENOMEM; + + e->value.mv_double = value; + e->type = MD_TYPE_DOUBLE; + + return md_entry_insert(md, e); +} /* }}} int meta_data_add_double */ + +int meta_data_add_boolean(meta_data_t *md, /* {{{ */ + const char *key, bool value) { + meta_entry_t *e; + + if ((md == NULL) || (key == NULL)) + return -EINVAL; + + e = md_entry_alloc(key); + if (e == NULL) + return -ENOMEM; + + e->value.mv_boolean = value; + e->type = MD_TYPE_BOOLEAN; + + return md_entry_insert(md, e); +} /* }}} int meta_data_add_boolean */ + +/* + * Get functions + */ +int meta_data_get_string(meta_data_t *md, /* {{{ */ + const char *key, char **value) { + meta_entry_t *e; + char *temp; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + e = md_entry_lookup(md, key); + if (e == NULL) { + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + if (e->type != MD_TYPE_STRING) { + ERROR("meta_data_get_string: Type mismatch for key `%s'", e->key); + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + temp = md_strdup(e->value.mv_string); + if (temp == NULL) { + pthread_mutex_unlock(&md->lock); + ERROR("meta_data_get_string: md_strdup failed."); + return -ENOMEM; + } + + pthread_mutex_unlock(&md->lock); + + *value = temp; + + return 0; +} /* }}} int meta_data_get_string */ + +int meta_data_get_signed_int(meta_data_t *md, /* {{{ */ + const char *key, int64_t *value) { + meta_entry_t *e; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + e = md_entry_lookup(md, key); + if (e == NULL) { + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + if (e->type != MD_TYPE_SIGNED_INT) { + ERROR("meta_data_get_signed_int: Type mismatch for key `%s'", e->key); + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + *value = e->value.mv_signed_int; + + pthread_mutex_unlock(&md->lock); + return 0; +} /* }}} int meta_data_get_signed_int */ + +int meta_data_get_unsigned_int(meta_data_t *md, /* {{{ */ + const char *key, uint64_t *value) { + meta_entry_t *e; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + e = md_entry_lookup(md, key); + if (e == NULL) { + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + if (e->type != MD_TYPE_UNSIGNED_INT) { + ERROR("meta_data_get_unsigned_int: Type mismatch for key `%s'", e->key); + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + *value = e->value.mv_unsigned_int; + + pthread_mutex_unlock(&md->lock); + return 0; +} /* }}} int meta_data_get_unsigned_int */ + +int meta_data_get_double(meta_data_t *md, /* {{{ */ + const char *key, double *value) { + meta_entry_t *e; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + e = md_entry_lookup(md, key); + if (e == NULL) { + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + if (e->type != MD_TYPE_DOUBLE) { + ERROR("meta_data_get_double: Type mismatch for key `%s'", e->key); + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + *value = e->value.mv_double; + + pthread_mutex_unlock(&md->lock); + return 0; +} /* }}} int meta_data_get_double */ + +int meta_data_get_boolean(meta_data_t *md, /* {{{ */ + const char *key, bool *value) { + meta_entry_t *e; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + e = md_entry_lookup(md, key); + if (e == NULL) { + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + if (e->type != MD_TYPE_BOOLEAN) { + ERROR("meta_data_get_boolean: Type mismatch for key `%s'", e->key); + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + *value = e->value.mv_boolean; + + pthread_mutex_unlock(&md->lock); + return 0; +} /* }}} int meta_data_get_boolean */ + +int meta_data_as_string(meta_data_t *md, /* {{{ */ + const char *key, char **value) { + meta_entry_t *e; + const char *actual; + char buffer[MD_MAX_NONSTRING_CHARS]; /* For non-string types. */ + char *temp; + int type; + + if ((md == NULL) || (key == NULL) || (value == NULL)) + return -EINVAL; + + pthread_mutex_lock(&md->lock); + + e = md_entry_lookup(md, key); + if (e == NULL) { + pthread_mutex_unlock(&md->lock); + return -ENOENT; + } + + type = e->type; + + switch (type) { + case MD_TYPE_STRING: + actual = e->value.mv_string; + break; + case MD_TYPE_SIGNED_INT: + snprintf(buffer, sizeof(buffer), "%" PRIi64, e->value.mv_signed_int); + actual = buffer; + break; + case MD_TYPE_UNSIGNED_INT: + snprintf(buffer, sizeof(buffer), "%" PRIu64, e->value.mv_unsigned_int); + actual = buffer; + break; + case MD_TYPE_DOUBLE: + snprintf(buffer, sizeof(buffer), GAUGE_FORMAT, e->value.mv_double); + actual = buffer; + break; + case MD_TYPE_BOOLEAN: + actual = e->value.mv_boolean ? "true" : "false"; + break; + default: + pthread_mutex_unlock(&md->lock); + ERROR("meta_data_as_string: unknown type %d for key `%s'", type, key); + return -ENOENT; + } + + pthread_mutex_unlock(&md->lock); + + temp = md_strdup(actual); + if (temp == NULL) { + ERROR("meta_data_as_string: md_strdup failed for key `%s'.", key); + return -ENOMEM; + } + + *value = temp; + + return 0; +} /* }}} int meta_data_as_string */ diff --git a/src/utils/metadata/meta_data.h b/src/utils/metadata/meta_data.h new file mode 100644 index 00000000..203b1460 --- /dev/null +++ b/src/utils/metadata/meta_data.h @@ -0,0 +1,71 @@ +/** + * collectd - src/meta_data.h + * Copyright (C) 2008-2011 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef META_DATA_H +#define META_DATA_H + +#include "collectd.h" + +/* + * Defines + */ +#define MD_TYPE_STRING 1 +#define MD_TYPE_SIGNED_INT 2 +#define MD_TYPE_UNSIGNED_INT 3 +#define MD_TYPE_DOUBLE 4 +#define MD_TYPE_BOOLEAN 5 + +struct meta_data_s; +typedef struct meta_data_s meta_data_t; + +meta_data_t *meta_data_create(void); +meta_data_t *meta_data_clone(meta_data_t *orig); +int meta_data_clone_merge(meta_data_t **dest, meta_data_t *orig); +void meta_data_destroy(meta_data_t *md); + +int meta_data_exists(meta_data_t *md, const char *key); +int meta_data_type(meta_data_t *md, const char *key); +int meta_data_toc(meta_data_t *md, char ***toc); +int meta_data_delete(meta_data_t *md, const char *key); + +int meta_data_add_string(meta_data_t *md, const char *key, const char *value); +int meta_data_add_signed_int(meta_data_t *md, const char *key, int64_t value); +int meta_data_add_unsigned_int(meta_data_t *md, const char *key, + uint64_t value); +int meta_data_add_double(meta_data_t *md, const char *key, double value); +int meta_data_add_boolean(meta_data_t *md, const char *key, bool value); + +int meta_data_get_string(meta_data_t *md, const char *key, char **value); +int meta_data_get_signed_int(meta_data_t *md, const char *key, int64_t *value); +int meta_data_get_unsigned_int(meta_data_t *md, const char *key, + uint64_t *value); +int meta_data_get_double(meta_data_t *md, const char *key, double *value); +int meta_data_get_boolean(meta_data_t *md, const char *key, bool *value); + +/* Returns the value as a string, regardless of the type. */ +int meta_data_as_string(meta_data_t *md, const char *key, char **value); + +#endif /* META_DATA_H */ diff --git a/src/utils/metadata/meta_data_test.c b/src/utils/metadata/meta_data_test.c new file mode 100644 index 00000000..b9ff86d5 --- /dev/null +++ b/src/utils/metadata/meta_data_test.c @@ -0,0 +1,117 @@ +/** + * collectd - src/daemon/meta_data_test.c + * Copyright (C) 2015 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "collectd.h" + +#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */ + +#include "testing.h" +#include "utils/metadata/meta_data.h" + +DEF_TEST(base) { + meta_data_t *m; + + char *s; + int64_t si; + uint64_t ui; + double d; + bool b; + + CHECK_NOT_NULL(m = meta_data_create()); + + /* all of these are absent */ + OK(meta_data_get_string(m, "string", &s) != 0); + OK(meta_data_get_signed_int(m, "signed_int", &si) != 0); + OK(meta_data_get_unsigned_int(m, "unsigned_int", &ui) != 0); + OK(meta_data_get_double(m, "double", &d) != 0); + OK(meta_data_get_boolean(m, "boolean", &b) != 0); + + /* populate structure */ + CHECK_ZERO(meta_data_add_string(m, "string", "foobar")); + OK(meta_data_exists(m, "string")); + OK(meta_data_type(m, "string") == MD_TYPE_STRING); + + CHECK_ZERO(meta_data_add_signed_int(m, "signed_int", -1)); + OK(meta_data_exists(m, "signed_int")); + OK(meta_data_type(m, "signed_int") == MD_TYPE_SIGNED_INT); + + CHECK_ZERO(meta_data_add_unsigned_int(m, "unsigned_int", 1)); + OK(meta_data_exists(m, "unsigned_int")); + OK(meta_data_type(m, "unsigned_int") == MD_TYPE_UNSIGNED_INT); + + CHECK_ZERO(meta_data_add_double(m, "double", 47.11)); + OK(meta_data_exists(m, "double")); + OK(meta_data_type(m, "double") == MD_TYPE_DOUBLE); + + CHECK_ZERO(meta_data_add_boolean(m, "boolean", 1)); + OK(meta_data_exists(m, "boolean")); + OK(meta_data_type(m, "boolean") == MD_TYPE_BOOLEAN); + + /* retrieve and check all values */ + CHECK_ZERO(meta_data_get_string(m, "string", &s)); + EXPECT_EQ_STR("foobar", s); + sfree(s); + + CHECK_ZERO(meta_data_get_signed_int(m, "signed_int", &si)); + EXPECT_EQ_INT(-1, (int)si); + + CHECK_ZERO(meta_data_get_unsigned_int(m, "unsigned_int", &ui)); + EXPECT_EQ_INT(1, (int)ui); + + CHECK_ZERO(meta_data_get_double(m, "double", &d)); + EXPECT_EQ_DOUBLE(47.11, d); + + CHECK_ZERO(meta_data_get_boolean(m, "boolean", &b)); + OK1(b, "b evaluates to true"); + + /* retrieving the wrong type always fails */ + EXPECT_EQ_INT(-2, meta_data_get_boolean(m, "string", &b)); + EXPECT_EQ_INT(-2, meta_data_get_string(m, "signed_int", &s)); + EXPECT_EQ_INT(-2, meta_data_get_string(m, "unsigned_int", &s)); + EXPECT_EQ_INT(-2, meta_data_get_string(m, "double", &s)); + EXPECT_EQ_INT(-2, meta_data_get_string(m, "boolean", &s)); + + /* replace existing keys */ + CHECK_ZERO(meta_data_add_signed_int(m, "string", 666)); + OK(meta_data_type(m, "string") == MD_TYPE_SIGNED_INT); + + CHECK_ZERO(meta_data_add_signed_int(m, "signed_int", 666)); + CHECK_ZERO(meta_data_get_signed_int(m, "signed_int", &si)); + EXPECT_EQ_INT(666, (int)si); + + /* deleting keys */ + CHECK_ZERO(meta_data_delete(m, "signed_int")); + EXPECT_EQ_INT(-2, meta_data_delete(m, "doesnt exist")); + + meta_data_destroy(m); + return 0; +} + +int main(void) { + RUN_TEST(base); + + END_TEST; +} diff --git a/src/utils/mount/mount.c b/src/utils/mount/mount.c new file mode 100644 index 00000000..1d362e1d --- /dev/null +++ b/src/utils/mount/mount.c @@ -0,0 +1,765 @@ +/** + * collectd - src/utils_mount.c + * Copyright (C) 2005,2006 Niki W. Waibel + * + * This program is free software; you can redistribute it and/ + * or modify it under the terms of the GNU General Public Li- + * cence as published by the Free Software Foundation; either + * version 2 of the Licence, or any later version. + * + * This program is distributed in the hope that it will be use- + * ful, but WITHOUT ANY WARRANTY; without even the implied war- + * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public Licence for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Niki W. Waibel + **/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#define _GNU_SOURCE + +#include "collectd.h" + +#include "utils/mount/mount.h" + +#if HAVE_XFS_XQM_H +#include +#define XFS_SUPER_MAGIC_STR "XFSB" +#define XFS_SUPER_MAGIC2_STR "BSFX" +#endif + +#include "plugin.h" /* ERROR() macro */ +#include "utils/common/common.h" /* sstrncpy() et alii */ + +#if HAVE_GETVFSSTAT +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_STATVFS_H +#include +#endif +/* #endif HAVE_GETVFSSTAT */ + +#elif HAVE_GETFSSTAT +#if HAVE_SYS_PARAM_H +#include +#endif +#if HAVE_SYS_UCRED_H +#include +#endif +#if HAVE_SYS_MOUNT_H +#include +#endif +#endif /* HAVE_GETFSSTAT */ + +#if HAVE_MNTENT_H +#include +#endif +#if HAVE_SYS_MNTTAB_H +#include +#endif + +#if HAVE_PATHS_H +#include +#endif + +#ifdef COLLECTD_MNTTAB +#undef COLLECTD_MNTTAB +#endif + +#if defined(_PATH_MOUNTED) /* glibc */ +#define COLLECTD_MNTTAB _PATH_MOUNTED +#elif defined(MNTTAB) /* Solaris */ +#define COLLECTD_MNTTAB MNTTAB +#elif defined(MNT_MNTTAB) +#define COLLECTD_MNTTAB MNT_MNTTAB +#elif defined(MNTTABNAME) +#define COLLECTD_MNTTAB MNTTABNAME +#elif defined(KMTAB) +#define COLLECTD_MNTTAB KMTAB +#else +#define COLLECTD_MNTTAB "/etc/mnttab" +#endif + +/* *** *** *** ********************************************* *** *** *** */ +/* *** *** *** *** *** *** private functions *** *** *** *** *** *** */ +/* *** *** *** ********************************************* *** *** *** */ + +/* stolen from quota-3.13 (quota-tools) */ + +#define PROC_PARTITIONS "/proc/partitions" +#define DEVLABELDIR "/dev" +#define UUID 1 +#define VOL 2 + +static struct uuidCache_s { + struct uuidCache_s *next; + char uuid[16]; + char *label; + char *device; +} *uuidCache = NULL; + +#define EXT2_SUPER_MAGIC 0xEF53 +struct ext2_super_block { + unsigned char s_dummy1[56]; + unsigned char s_magic[2]; + unsigned char s_dummy2[46]; + unsigned char s_uuid[16]; + char s_volume_name[16]; +}; +#define ext2magic(s) \ + ((unsigned int)s.s_magic[0] + (((unsigned int)s.s_magic[1]) << 8)) + +#if HAVE_XFS_XQM_H +struct xfs_super_block { + unsigned char s_magic[4]; + unsigned char s_dummy[28]; + unsigned char s_uuid[16]; + unsigned char s_dummy2[60]; + char s_fsname[12]; +}; +#endif /* HAVE_XFS_XQM_H */ + +#define REISER_SUPER_MAGIC "ReIsEr2Fs" +struct reiserfs_super_block { + unsigned char s_dummy1[52]; + unsigned char s_magic[10]; + unsigned char s_dummy2[22]; + unsigned char s_uuid[16]; + char s_volume_name[16]; +}; + +/* for now, only ext2 and xfs are supported */ +static int get_label_uuid(const char *device, char **label, char *uuid) { + /* start with ext2 and xfs tests, taken from mount_guess_fstype */ + /* should merge these later */ + int fd, rv = 1; + size_t namesize; + struct ext2_super_block e2sb; +#if HAVE_XFS_XQM_H + struct xfs_super_block xfsb; +#endif + struct reiserfs_super_block reisersb; + + fd = open(device, O_RDONLY); + if (fd == -1) { + return rv; + } + + if (lseek(fd, 1024, SEEK_SET) == 1024 && + read(fd, (char *)&e2sb, sizeof(e2sb)) == sizeof(e2sb) && + ext2magic(e2sb) == EXT2_SUPER_MAGIC) { + memcpy(uuid, e2sb.s_uuid, sizeof(e2sb.s_uuid)); + namesize = sizeof(e2sb.s_volume_name); + *label = smalloc(namesize + 1); + sstrncpy(*label, e2sb.s_volume_name, namesize); + rv = 0; +#if HAVE_XFS_XQM_H + } else if (lseek(fd, 0, SEEK_SET) == 0 && + read(fd, (char *)&xfsb, sizeof(xfsb)) == sizeof(xfsb) && + (strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC_STR, 4) == 0 || + strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC2_STR, 4) == 0)) { + memcpy(uuid, xfsb.s_uuid, sizeof(xfsb.s_uuid)); + namesize = sizeof(xfsb.s_fsname); + *label = smalloc(namesize + 1); + sstrncpy(*label, xfsb.s_fsname, namesize); + rv = 0; +#endif /* HAVE_XFS_XQM_H */ + } else if (lseek(fd, 65536, SEEK_SET) == 65536 && + read(fd, (char *)&reisersb, sizeof(reisersb)) == + sizeof(reisersb) && + !strncmp((char *)&reisersb.s_magic, REISER_SUPER_MAGIC, 9)) { + memcpy(uuid, reisersb.s_uuid, sizeof(reisersb.s_uuid)); + namesize = sizeof(reisersb.s_volume_name); + *label = smalloc(namesize + 1); + sstrncpy(*label, reisersb.s_volume_name, namesize); + rv = 0; + } + close(fd); + return rv; +} + +static void uuidcache_addentry(char *device, char *label, char *uuid) { + struct uuidCache_s *last; + + if (!uuidCache) { + last = uuidCache = smalloc(sizeof(*uuidCache)); + } else { + for (last = uuidCache; last->next; last = last->next) + ; + last->next = smalloc(sizeof(*uuidCache)); + last = last->next; + } + last->next = NULL; + last->device = device; + last->label = label; + memcpy(last->uuid, uuid, sizeof(last->uuid)); +} + +static void uuidcache_init(void) { + char line[100]; + char *s; + int ma, mi, sz; + static char ptname[100]; + FILE *procpt; + char uuid[16], *label = NULL; + char device[110]; + int handleOnFirst; + + if (uuidCache) { + return; + } + + procpt = fopen(PROC_PARTITIONS, "r"); + if (procpt == NULL) { + return; + } + + for (int firstPass = 1; firstPass >= 0; firstPass--) { + fseek(procpt, 0, SEEK_SET); + while (fgets(line, sizeof(line), procpt)) { + if (sscanf(line, " %d %d %d %[^\n ]", &ma, &mi, &sz, ptname) != 4) { + continue; + } + + /* skip extended partitions (heuristic: size 1) */ + if (sz == 1) { + continue; + } + + /* look only at md devices on first pass */ + handleOnFirst = !strncmp(ptname, "md", 2); + if (firstPass != handleOnFirst) { + continue; + } + + /* skip entire disk (minor 0, 64, ... on ide; + 0, 16, ... on sd) */ + /* heuristic: partition name ends in a digit */ + + for (s = ptname; *s; s++) + ; + + if (isdigit((int)s[-1])) { + /* + * Note: this is a heuristic only - there is no reason + * why these devices should live in /dev. + * Perhaps this directory should be specifiable by option. + * One might for example have /devlabel with links to /dev + * for the devices that may be accessed in this way. + * (This is useful, if the cdrom on /dev/hdc must not + * be accessed.) + */ + snprintf(device, sizeof(device), "%s/%s", DEVLABELDIR, ptname); + if (!get_label_uuid(device, &label, uuid)) { + uuidcache_addentry(sstrdup(device), label, uuid); + } + } + } + } + fclose(procpt); +} + +static unsigned char fromhex(char c) { + if (isdigit((int)c)) { + return c - '0'; + } else if (islower((int)c)) { + return c - 'a' + 10; + } else { + return c - 'A' + 10; + } +} + +static char *get_spec_by_x(int n, const char *t) { + struct uuidCache_s *uc; + + uuidcache_init(); + uc = uuidCache; + + while (uc) { + switch (n) { + case UUID: + if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) { + return sstrdup(uc->device); + } + break; + case VOL: + if (!strcmp(t, uc->label)) { + return sstrdup(uc->device); + } + break; + } + uc = uc->next; + } + return NULL; +} + +static char *get_spec_by_uuid(const char *s) { + char uuid[16]; + + if (strlen(s) != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' || + s[23] != '-') { + goto bad_uuid; + } + + for (int i = 0; i < 16; i++) { + if (*s == '-') { + s++; + } + if (!isxdigit((int)s[0]) || !isxdigit((int)s[1])) { + goto bad_uuid; + } + uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1])); + s += 2; + } + return get_spec_by_x(UUID, uuid); + +bad_uuid: + DEBUG("utils_mount: Found an invalid UUID: %s", s); + return NULL; +} + +static char *get_spec_by_volume_label(const char *s) { + return get_spec_by_x(VOL, s); +} + +static char *get_device_name(const char *optstr) { + char *rc; + + if (optstr == NULL) { + return NULL; + } else if (strncmp(optstr, "UUID=", 5) == 0) { + DEBUG("utils_mount: TODO: check UUID= code!"); + rc = get_spec_by_uuid(optstr + 5); + } else if (strncmp(optstr, "LABEL=", 6) == 0) { + DEBUG("utils_mount: TODO: check LABEL= code!"); + rc = get_spec_by_volume_label(optstr + 6); + } else { + rc = sstrdup(optstr); + } + + if (!rc) { + DEBUG("utils_mount: Error checking device name: optstr = %s", optstr); + } + return rc; +} + +/* What weird OS is this..? I can't find any info with google :/ -octo */ +#if HAVE_LISTMNTENT && 0 +static cu_mount_t *cu_mount_listmntent(void) { + cu_mount_t *last = *list; + struct mntent *mnt; + + struct tabmntent *mntlist; + if (listmntent(&mntlist, COLLECTD_MNTTAB, NULL, NULL) < 0) { +#if COLLECT_DEBUG + DEBUG("utils_mount: calling listmntent() failed: %s", STRERRNO); +#endif /* COLLECT_DEBUG */ + } + + for (struct tabmntent *p = mntlist; p; p = p->next) { + char *loop = NULL, *device = NULL; + + mnt = p->ment; + loop = cu_mount_getoptionvalue(mnt->mnt_opts, "loop="); + if (loop == NULL) { /* no loop= mount */ + device = get_device_name(mnt->mnt_fsname); + if (device == NULL) { + DEBUG("utils_mount: can't get devicename for fs (%s) %s (%s)" + ": ignored", + mnt->mnt_type, mnt->mnt_dir, mnt->mnt_fsname); + continue; + } + } else { + device = loop; + } + if (*list == NULL) { + *list = (cu_mount_t *)smalloc(sizeof(cu_mount_t)); + last = *list; + } else { + while (last->next != NULL) { /* is last really last? */ + last = last->next; + } + last->next = (cu_mount_t *)smalloc(sizeof(cu_mount_t)); + last = last->next; + } + last->dir = sstrdup(mnt->mnt_dir); + last->spec_device = sstrdup(mnt->mnt_fsname); + last->device = device; + last->type = sstrdup(mnt->mnt_type); + last->options = sstrdup(mnt->mnt_opts); + last->next = NULL; + } /* for(p = mntlist; p; p = p->next) */ + + return last; +} /* cu_mount_t *cu_mount_listmntent(void) */ +/* #endif HAVE_LISTMNTENT */ + +/* 4.4BSD and Mac OS X (getfsstat) or NetBSD (getvfsstat) */ +#elif HAVE_GETVFSSTAT || HAVE_GETFSSTAT +static cu_mount_t *cu_mount_getfsstat(void) { +#if HAVE_GETFSSTAT +#define STRUCT_STATFS struct statfs +#define CMD_STATFS getfsstat +#define FLAGS_STATFS MNT_NOWAIT +/* #endif HAVE_GETFSSTAT */ +#elif HAVE_GETVFSSTAT +#define STRUCT_STATFS struct statvfs +#define CMD_STATFS getvfsstat +#define FLAGS_STATFS ST_NOWAIT +#endif /* HAVE_GETVFSSTAT */ + + int bufsize; + STRUCT_STATFS *buf; + + int num; + + cu_mount_t *first = NULL; + cu_mount_t *last = NULL; + cu_mount_t *new = NULL; + + /* Get the number of mounted file systems */ + if ((bufsize = CMD_STATFS(NULL, 0, FLAGS_STATFS)) < 1) { +#if COLLECT_DEBUG + DEBUG("utils_mount: getv?fsstat failed: %s", STRERRNO); +#endif /* COLLECT_DEBUG */ + return NULL; + } + + if ((buf = calloc(bufsize, sizeof(*buf))) == NULL) + return NULL; + + /* The bufsize needs to be passed in bytes. Really. This is not in the + * manpage.. -octo */ + if ((num = CMD_STATFS(buf, bufsize * sizeof(STRUCT_STATFS), FLAGS_STATFS)) < + 1) { +#if COLLECT_DEBUG + DEBUG("utils_mount: getv?fsstat failed: %s", STRERRNO); +#endif /* COLLECT_DEBUG */ + free(buf); + return NULL; + } + + for (int i = 0; i < num; i++) { + if ((new = calloc(1, sizeof(*new))) == NULL) + break; + + /* Copy values from `struct mnttab' */ + new->dir = sstrdup(buf[i].f_mntonname); + new->spec_device = sstrdup(buf[i].f_mntfromname); + new->type = sstrdup(buf[i].f_fstypename); + new->options = NULL; + new->device = get_device_name(new->options); + new->next = NULL; + + /* Append to list */ + if (first == NULL) { + first = new; + last = new; + } else { + last->next = new; + last = new; + } + } + + free(buf); + + return first; +} +/* #endif HAVE_GETVFSSTAT || HAVE_GETFSSTAT */ + +/* Solaris (SunOS 10): int getmntent(FILE *fp, struct mnttab *mp); */ +#elif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT +static cu_mount_t *cu_mount_gen_getmntent(void) { + struct mnttab mt; + FILE *fp; + + cu_mount_t *first = NULL; + cu_mount_t *last = NULL; + cu_mount_t *new = NULL; + + DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB); + + if ((fp = fopen(COLLECTD_MNTTAB, "r")) == NULL) { + ERROR("fopen (%s): %s", COLLECTD_MNTTAB, STRERRNO); + return NULL; + } + + while (getmntent(fp, &mt) == 0) { + if ((new = calloc(1, sizeof(*new))) == NULL) + break; + + /* Copy values from `struct mnttab' */ + new->dir = sstrdup(mt.mnt_mountp); + new->spec_device = sstrdup(mt.mnt_special); + new->type = sstrdup(mt.mnt_fstype); + new->options = sstrdup(mt.mnt_mntopts); + new->device = get_device_name(new->options); + new->next = NULL; + + /* Append to list */ + if (first == NULL) { + first = new; + last = new; + } else { + last->next = new; + last = new; + } + } + + fclose(fp); + + return first; +} /* static cu_mount_t *cu_mount_gen_getmntent (void) */ + +#elif HAVE_SEQ_GETMNTENT +#warn "This version of `getmntent' hat not yet been implemented!" + /* #endif HAVE_SEQ_GETMNTENT */ + +#elif HAVE_GETMNTENT_R +static cu_mount_t *cu_mount_getmntent(void) { + FILE *fp; + struct mntent me; + char mntbuf[1024]; + + cu_mount_t *first = NULL; + cu_mount_t *last = NULL; + cu_mount_t *new = NULL; + + DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB); + + if ((fp = setmntent(COLLECTD_MNTTAB, "r")) == NULL) { + ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, STRERRNO); + return NULL; + } + + while (getmntent_r(fp, &me, mntbuf, sizeof(mntbuf))) { + if ((new = calloc(1, sizeof(*new))) == NULL) + break; + + /* Copy values from `struct mntent *' */ + new->dir = sstrdup(me.mnt_dir); + new->spec_device = sstrdup(me.mnt_fsname); + new->type = sstrdup(me.mnt_type); + new->options = sstrdup(me.mnt_opts); + new->device = get_device_name(new->options); + new->next = NULL; + + DEBUG("utils_mount: new = {dir = %s, spec_device = %s, type = %s, options " + "= %s, device = %s}", + new->dir, new->spec_device, new->type, new->options, new->device); + + /* Append to list */ + if (first == NULL) { + first = new; + last = new; + } else { + last->next = new; + last = new; + } + } + + endmntent(fp); + + DEBUG("utils_mount: return 0x%p", (void *)first); + + return first; +} /* HAVE_GETMNTENT_R */ + +#elif HAVE_ONE_GETMNTENT +static cu_mount_t *cu_mount_getmntent(void) { + FILE *fp; + struct mntent *me; + + cu_mount_t *first = NULL; + cu_mount_t *last = NULL; + cu_mount_t *new = NULL; + + DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB); + + if ((fp = setmntent(COLLECTD_MNTTAB, "r")) == NULL) { + ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, STRERRNO); + return NULL; + } + + while ((me = getmntent(fp)) != NULL) { + if ((new = calloc(1, sizeof(*new))) == NULL) + break; + + /* Copy values from `struct mntent *' */ + new->dir = sstrdup(me->mnt_dir); + new->spec_device = sstrdup(me->mnt_fsname); + new->type = sstrdup(me->mnt_type); + new->options = sstrdup(me->mnt_opts); + new->device = get_device_name(new->options); + new->next = NULL; + + DEBUG("utils_mount: new = {dir = %s, spec_device = %s, type = %s, options " + "= %s, device = %s}", + new->dir, new->spec_device, new->type, new->options, new->device); + + /* Append to list */ + if (first == NULL) { + first = new; + last = new; + } else { + last->next = new; + last = new; + } + } + + endmntent(fp); + + DEBUG("utils_mount: return 0x%p", (void *)first); + + return first; +} +#endif /* HAVE_ONE_GETMNTENT */ + +/* *** *** *** ******************************************** *** *** *** */ +/* *** *** *** *** *** *** public functions *** *** *** *** *** *** */ +/* *** *** *** ******************************************** *** *** *** */ + +cu_mount_t *cu_mount_getlist(cu_mount_t **list) { + cu_mount_t *new; + cu_mount_t *first = NULL; + cu_mount_t *last = NULL; + + if (list == NULL) + return NULL; + + if (*list != NULL) { + first = *list; + last = first; + while (last->next != NULL) + last = last->next; + } + +#if HAVE_LISTMNTENT && 0 + new = cu_mount_listmntent(); +#elif HAVE_GETVFSSTAT || HAVE_GETFSSTAT + new = cu_mount_getfsstat(); +#elif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT + new = cu_mount_gen_getmntent(); +#elif HAVE_SEQ_GETMNTENT +#error "This version of `getmntent' hat not yet been implemented!" +#elif HAVE_GETMNTENT_R + new = cu_mount_getmntent(); +#elif HAVE_ONE_GETMNTENT + new = cu_mount_getmntent(); +#else +#error "Could not determine how to find mountpoints." +#endif + + if (first != NULL) { + last->next = new; + } else { + first = new; + last = new; + *list = first; + } + + while ((last != NULL) && (last->next != NULL)) + last = last->next; + + return last; +} /* cu_mount_t *cu_mount_getlist(cu_mount_t **list) */ + +void cu_mount_freelist(cu_mount_t *list) { + cu_mount_t *next; + + for (cu_mount_t *this = list; this != NULL; this = next) { + next = this->next; + + sfree(this->dir); + sfree(this->spec_device); + sfree(this->device); + sfree(this->type); + sfree(this->options); + sfree(this); + } +} /* void cu_mount_freelist(cu_mount_t *list) */ + +char *cu_mount_checkoption(char *line, const char *keyword, int full) { + char *line2, *l2, *p1, *p2; + size_t l; + + if (line == NULL || keyword == NULL) { + return NULL; + } + + if (full != 0) { + full = 1; + } + + line2 = sstrdup(line); + l2 = line2; + while (*l2 != '\0') { + if (*l2 == ',') { + *l2 = '\0'; + } + l2++; + } + + l = strlen(keyword); + p1 = line - 1; + p2 = strchr(line, ','); + do { + if (strncmp(line2 + (p1 - line) + 1, keyword, l + full) == 0) { + free(line2); + return p1 + 1; + } + p1 = p2; + if (p1 != NULL) { + p2 = strchr(p1 + 1, ','); + } + } while (p1 != NULL); + + free(line2); + return NULL; +} /* char *cu_mount_checkoption(char *line, char *keyword, int full) */ + +char *cu_mount_getoptionvalue(char *line, const char *keyword) { + char *r; + + r = cu_mount_checkoption(line, keyword, 0); + if (r != NULL) { + char *p; + r += strlen(keyword); + p = strchr(r, ','); + if (p == NULL) { + return sstrdup(r); + } else { + char *m; + if ((p - r) == 1) { + return NULL; + } + m = smalloc(p - r + 1); + sstrncpy(m, r, p - r + 1); + return m; + } + } + return r; +} /* char *cu_mount_getoptionvalue(char *line, const char *keyword) */ + +int cu_mount_type(const char *type) { + if (strcmp(type, "ext3") == 0) + return CUMT_EXT3; + if (strcmp(type, "ext2") == 0) + return CUMT_EXT2; + if (strcmp(type, "ufs") == 0) + return CUMT_UFS; + if (strcmp(type, "vxfs") == 0) + return CUMT_VXFS; + if (strcmp(type, "zfs") == 0) + return CUMT_ZFS; + return CUMT_UNKNOWN; +} /* int cu_mount_type(const char *type) */ diff --git a/src/utils/mount/mount.h b/src/utils/mount/mount.h new file mode 100644 index 00000000..05adcc3e --- /dev/null +++ b/src/utils/mount/mount.h @@ -0,0 +1,186 @@ +/** + * collectd - src/utils/mount/mount.h + * Copyright (C) 2005,2006 Niki W. Waibel + * + * This program is free software; you can redistribute it and/ + * or modify it under the terms of the GNU General Public Li- + * cence as published by the Free Software Foundation; either + * version 2 of the Licence, or any later version. + * + * This program is distributed in the hope that it will be use- + * ful, but WITHOUT ANY WARRANTY; without even the implied war- + * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public Licence for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Niki W. Waibel + **/ + +/* See below for instructions how to use the public functions. */ + +#ifndef COLLECTD_UTILS_MOUNT_H +#define COLLECTD_UTILS_MOUNT_H 1 + +#include +#if HAVE_FS_INFO_H +#include +#endif +#if HAVE_FSHELP_H +#include +#endif +#if HAVE_PATHS_H +#include +#endif +#if HAVE_MNTENT_H +#include +#endif +#if HAVE_MNTTAB_H +#include +#endif +#if HAVE_SYS_FSTYP_H +#include +#endif +#if HAVE_SYS_FS_TYPES_H +#include +#endif +#if HAVE_SYS_MNTENT_H +#include +#endif +#if HAVE_SYS_MNTTAB_H +#include +#endif +#if HAVE_SYS_MOUNT_H +#include +#endif +#if HAVE_SYS_STATFS_H +#include +#endif +#if HAVE_SYS_VFS_H +#include +#endif +#if HAVE_SYS_VFSTAB_H +#include +#endif + +/* Collectd Utils Mount Type */ +#define CUMT_UNKNOWN (0) +#define CUMT_EXT2 (1) +#define CUMT_EXT3 (2) +#define CUMT_XFS (3) +#define CUMT_UFS (4) +#define CUMT_VXFS (5) +#define CUMT_ZFS (6) + +typedef struct _cu_mount_t cu_mount_t; +struct _cu_mount_t { + char *dir; /* "/sys" or "/" */ + char *spec_device; /* "LABEL=/" or "none" or "proc" or "/dev/hda1" */ + char *device; /* "none" or "proc" or "/dev/hda1" */ + char *type; /* "sysfs" or "ext3" */ + char *options; /* "rw,noatime,commit=600,quota,grpquota" */ + cu_mount_t *next; +}; + +cu_mount_t *cu_mount_getlist(cu_mount_t **list); +/* + DESCRIPTION + The cu_mount_getlist() function creates a list + of all mountpoints. + + If *list is NULL, a new list is created and *list is + set to point to the first entry. + + If *list is not NULL, the list of mountpoints is appended + and *list is not changed. + + RETURN VALUE + The cu_mount_getlist() function returns a pointer to + the last entry of the list, or NULL if an error has + occured. + + NOTES + In case of an error, *list is not modified. +*/ + +void cu_mount_freelist(cu_mount_t *list); +/* + DESCRIPTION + The cu_mount_freelist() function free()s all memory + allocated by *list and *list itself as well. +*/ + +char *cu_mount_checkoption(char *line, const char *keyword, int full); +/* + DESCRIPTION + The cu_mount_checkoption() function is a replacement of + char *hasmntopt(const struct mntent *mnt, const char *opt). + In fact hasmntopt() just looks for the first occurrence of the + characters at opt in mnt->mnt_opts. cu_mount_checkoption() + checks for the *option* keyword in line, starting at the + first character of line or after a ','. + + If full is not 0 then also the end of keyword has to match + either the end of line or a ',' after keyword. + + RETURN VALUE + The cu_mount_checkoption() function returns a pointer into + string line if a match of keyword is found. If no match is + found cu_mount_checkoption() returns NULL. + + NOTES + Do *not* try to free() the pointer which is returned! It is + just part of the string line. + + full should be set to 0 when matching options like: rw, quota, + noatime. Set full to 1 when matching options like: loop=, + gid=, commit=. + + EXAMPLES + If line is "rw,usrquota,grpquota", keyword is "quota", NULL + will be returned (independend of full). + + If line is "rw,usrquota,grpquota", keyword is "usrquota", + a pointer to "usrquota,grpquota" is returned (independend + of full). + + If line is "rw,loop=/dev/loop1,quota", keyword is "loop=" + and full is 0, then a pointer to "loop=/dev/loop1,quota" + is returned. If full is not 0 then NULL is returned. But + maybe you might want to try cu_mount_getoptionvalue()... +*/ + +char *cu_mount_getoptionvalue(char *line, const char *keyword); +/* + DESCRIPTION + The cu_mount_getoptionvalue() function can be used to grab + a VALUE out of a mount option (line) like: + loop=VALUE + whereas "loop=" is the keyword. + + RETURN VALUE + If the cu_mount_getoptionvalue() function can find the option + keyword in line, then memory is allocated for the value of + that option and a pointer to that value is returned. + + If the option keyword is not found, cu_mount_getoptionvalue() + returns NULL; + + NOTES + Internally it calls cu_mount_checkoption(), then it + allocates memory for VALUE and returns a pointer to that + string. So *do not forget* to free() the memory returned + after use!!! +*/ + +int cu_mount_type(const char *type); +/* + DESCRIPTION + + RETURN VALUE +*/ + +#endif /* !COLLECTD_UTILS_MOUNT_H */ diff --git a/src/utils/mount/mount_test.c b/src/utils/mount/mount_test.c new file mode 100644 index 00000000..e34e677c --- /dev/null +++ b/src/utils/mount/mount_test.c @@ -0,0 +1,115 @@ +/** + * collectd - src/tests/test_utils_mount.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "collectd.h" + +#include "testing.h" +#include "utils/common/common.h" +#include "utils/mount/mount.h" + +#if HAVE_KSTAT_H +#include +#endif + +#if HAVE_LIBKSTAT +kstat_ctl_t *kc; +#endif /* HAVE_LIBKSTAT */ + +DEF_TEST(cu_mount_checkoption) { + char line_opts[] = "foo=one,bar=two,qux=three"; + char *foo = strstr(line_opts, "foo"); + char *bar = strstr(line_opts, "bar"); + char *qux = strstr(line_opts, "qux"); + + char line_bool[] = "one,two,three"; + char *one = strstr(line_bool, "one"); + char *two = strstr(line_bool, "two"); + char *three = strstr(line_bool, "three"); + + /* Normal operation */ + OK(foo == cu_mount_checkoption(line_opts, "foo", 0)); + OK(bar == cu_mount_checkoption(line_opts, "bar", 0)); + OK(qux == cu_mount_checkoption(line_opts, "qux", 0)); + OK(NULL == cu_mount_checkoption(line_opts, "unknown", 0)); + + OK(one == cu_mount_checkoption(line_bool, "one", 0)); + OK(two == cu_mount_checkoption(line_bool, "two", 0)); + OK(three == cu_mount_checkoption(line_bool, "three", 0)); + OK(NULL == cu_mount_checkoption(line_bool, "four", 0)); + + /* Shorter and longer parts */ + OK(foo == cu_mount_checkoption(line_opts, "fo", 0)); + OK(bar == cu_mount_checkoption(line_opts, "bar=", 0)); + OK(qux == cu_mount_checkoption(line_opts, "qux=thr", 0)); + + OK(one == cu_mount_checkoption(line_bool, "o", 0)); + OK(two == cu_mount_checkoption(line_bool, "tw", 0)); + OK(three == cu_mount_checkoption(line_bool, "thr", 0)); + + /* "full" flag */ + OK(one == cu_mount_checkoption(line_bool, "one", 1)); + OK(two == cu_mount_checkoption(line_bool, "two", 1)); + OK(three == cu_mount_checkoption(line_bool, "three", 1)); + OK(NULL == cu_mount_checkoption(line_bool, "four", 1)); + + OK(NULL == cu_mount_checkoption(line_bool, "o", 1)); + OK(NULL == cu_mount_checkoption(line_bool, "tw", 1)); + OK(NULL == cu_mount_checkoption(line_bool, "thr", 1)); + + return 0; +} +DEF_TEST(cu_mount_getoptionvalue) { + char line_opts[] = "foo=one,bar=two,qux=three"; + char line_bool[] = "one,two,three"; + char *v; + + EXPECT_EQ_STR("one", v = cu_mount_getoptionvalue(line_opts, "foo=")); + sfree(v); + EXPECT_EQ_STR("two", v = cu_mount_getoptionvalue(line_opts, "bar=")); + sfree(v); + EXPECT_EQ_STR("three", v = cu_mount_getoptionvalue(line_opts, "qux=")); + sfree(v); + OK(NULL == (v = cu_mount_getoptionvalue(line_opts, "unknown="))); + sfree(v); + + EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "one")); + sfree(v); + EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "two")); + sfree(v); + EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "three")); + sfree(v); + OK(NULL == (v = cu_mount_getoptionvalue(line_bool, "four"))); + sfree(v); + + return 0; +} + +int main(void) { + RUN_TEST(cu_mount_checkoption); + RUN_TEST(cu_mount_getoptionvalue); + + END_TEST; +} diff --git a/src/utils/oauth/oauth.c b/src/utils/oauth/oauth.c new file mode 100644 index 00000000..a7ae75d0 --- /dev/null +++ b/src/utils/oauth/oauth.c @@ -0,0 +1,638 @@ +/** + * 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 "plugin.h" +#include "utils/common/common.h" +#include "utils/oauth/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 = + ssnprintf(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 = ssnprintf(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 = ssnprintf(buffer, buffer_size, "%s.%s.%s", header, claim, signature); + if (status < 1) + return -1; + else if ((size_t)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; + } + + ssnprintf(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]; + ssnprintf(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/oauth.h b/src/utils/oauth/oauth.h new file mode 100644 index 00000000..b93c87b8 --- /dev/null +++ b/src/utils/oauth/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/oauth_test.c b/src/utils/oauth/oauth_test.c new file mode 100644 index 00000000..36f21255 --- /dev/null +++ b/src/utils/oauth/oauth_test.c @@ -0,0 +1,157 @@ +/** + * 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/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/ovs.c b/src/utils/ovs/ovs.c new file mode 100644 index 00000000..52a590b1 --- /dev/null +++ b/src/utils/ovs/ovs.c @@ -0,0 +1,1414 @@ +/** + * collectd - src/utils_ovs.c + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + *of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to + *do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + *all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Volodymyr Mytnyk + **/ + +/* clang-format off */ +/* + * OVS DB API internal architecture diagram + * +------------------------------------------------------------------------------+ + * |OVS plugin |OVS utils | + * | | +------------------------+ | + * | | | echo handler | JSON request/ | + * | | +--+ (ovs_db_table_echo_cb) +<---+---------+ update event/ | + * | | | | | | | result | + * | | | +------------------------+ | | | + * | | | | +----+---+--------+ | + * | +----------+ | | +------------------------+ | | | | | + * | | update | | | | update handler | | | YAJL | JSON | | + * | | callback +<-------+(ovs_db_table_update_cp)+<---+ | parser | reader | | + * | +----------+ | | | | | | | | | + * | | | +------------------------+ | +--------+---+----+ | + * | | | | ^ | + * | +----------+ | | +------------------------+ | | | + * | | result | | | | result handler | | | | + * | | callback +<-------+ (ovs_db_result_cb) +<---+ JSON raw | | + * | +----------+ | | | | data | | + * | | | +------------------------+ | | + * | | | | | + * | | | +------------------+ +------------+----+ | + * | +----------+ | | |thread| | |thread| | | + * | | init | | | | | reconnect | | | + * | | callback +<---------+ EVENT WORKER +<------------+ POLL WORKER | | + * | +----------+ | | +------------------+ +--------+--------+ | + * | | | ^ | + * +----------------+-------------------------------------------------------------+ + * | | + * JSON|echo reply raw|data + * v v + * +-------------------+----------------------------------------------+-----------+ + * | TCP/UNIX socket | + * +------------------------------------------------------------------------------- + */ +/* clang-format on */ + +/* collectd headers */ +#include "collectd.h" + +#include "utils/common/common.h" + +/* private headers */ +#include "utils/ovs/ovs.h" + +/* system libraries */ +#if HAVE_NETDB_H +#include +#endif +#if HAVE_ARPA_INET_H +#include +#endif +#if HAVE_POLL_H +#include +#endif +#if HAVE_SYS_UN_H +#include +#endif + +#include + +#define OVS_ERROR(fmt, ...) \ + do { \ + ERROR("ovs_utils: " fmt, ##__VA_ARGS__); \ + } while (0) +#define OVS_DEBUG(fmt, ...) \ + do { \ + DEBUG("%s:%d:%s(): " fmt, __FILE__, __LINE__, __FUNCTION__, \ + ##__VA_ARGS__); \ + } while (0) + +#define OVS_DB_POLL_TIMEOUT 1 /* poll receive timeout (sec) */ +#define OVS_DB_POLL_READ_BLOCK_SIZE 512 /* read block size (bytes) */ +#define OVS_DB_DEFAULT_DB_NAME "Open_vSwitch" + +#define OVS_DB_EVENT_NONE 0 +#define OVS_DB_EVENT_TIMEOUT 5 /* event thread timeout (sec) */ +#define OVS_DB_EVENT_TERMINATE 1 +#define OVS_DB_EVENT_CONN_ESTABLISHED 2 +#define OVS_DB_EVENT_CONN_TERMINATED 3 + +#define OVS_DB_POLL_STATE_RUNNING 1 +#define OVS_DB_POLL_STATE_EXITING 2 + +#define OVS_DB_SEND_REQ_TIMEOUT 5 /* send request timeout (sec) */ + +#define OVS_YAJL_CALL(func, ...) \ + do { \ + yajl_gen_ret = yajl_gen_status_ok; \ + if ((yajl_gen_ret = func(__VA_ARGS__)) != yajl_gen_status_ok) \ + goto yajl_gen_failure; \ + } while (0) +#define OVS_YAJL_ERROR_BUFFER_SIZE 1024 +#define OVS_ERROR_BUFF_SIZE 512 +#define OVS_UID_STR_SIZE 17 /* 64-bit HEX string len + '\0' */ + +/* JSON reader internal data */ +struct ovs_json_reader_s { + char *buff_ptr; + size_t buff_size; + size_t buff_offset; + size_t json_offset; +}; +typedef struct ovs_json_reader_s ovs_json_reader_t; + +/* Result callback declaration */ +struct ovs_result_cb_s { + sem_t sync; + ovs_db_result_cb_t call; +}; +typedef struct ovs_result_cb_s ovs_result_cb_t; + +/* Table callback declaration */ +struct ovs_table_cb_s { + ovs_db_table_cb_t call; +}; +typedef struct ovs_table_cb_s ovs_table_cb_t; + +/* Callback declaration */ +struct ovs_callback_s { + uint64_t uid; + union { + ovs_result_cb_t result; + ovs_table_cb_t table; + }; + struct ovs_callback_s *next; + struct ovs_callback_s *prev; +}; +typedef struct ovs_callback_s ovs_callback_t; + +/* Event thread data declaration */ +struct ovs_event_thread_s { + pthread_t tid; + pthread_mutex_t mutex; + pthread_cond_t cond; + int value; +}; +typedef struct ovs_event_thread_s ovs_event_thread_t; + +/* Poll thread data declaration */ +struct ovs_poll_thread_s { + pthread_t tid; + pthread_mutex_t mutex; + int state; +}; +typedef struct ovs_poll_thread_s ovs_poll_thread_t; + +/* OVS DB internal data declaration */ +struct ovs_db_s { + ovs_poll_thread_t poll_thread; + ovs_event_thread_t event_thread; + pthread_mutex_t mutex; + ovs_callback_t *remote_cb; + ovs_db_callback_t cb; + char service[OVS_DB_ADDR_SERVICE_SIZE]; + char node[OVS_DB_ADDR_NODE_SIZE]; + char unix_path[OVS_DB_ADDR_NODE_SIZE]; + int sock; +}; + +/* Global variables */ +static uint64_t ovs_uid; +static pthread_mutex_t ovs_uid_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Post an event to event thread. + * Possible events are: + * OVS_DB_EVENT_TERMINATE + * OVS_DB_EVENT_CONN_ESTABLISHED + * OVS_DB_EVENT_CONN_TERMINATED + */ +static void ovs_db_event_post(ovs_db_t *pdb, int event) { + pthread_mutex_lock(&pdb->event_thread.mutex); + pdb->event_thread.value = event; + pthread_mutex_unlock(&pdb->event_thread.mutex); + pthread_cond_signal(&pdb->event_thread.cond); +} + +/* 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) { + int state = 0; + pthread_mutex_lock(&pdb->poll_thread.mutex); + state = pdb->poll_thread.state; + pthread_mutex_unlock(&pdb->poll_thread.mutex); + return state == OVS_DB_POLL_STATE_RUNNING; +} + +/* Generate unique identifier (UID). It is used by OVS DB API + * to set "id" field for any OVS DB JSON request. */ +static uint64_t ovs_uid_generate() { + uint64_t new_uid; + pthread_mutex_lock(&ovs_uid_mutex); + new_uid = ++ovs_uid; + pthread_mutex_unlock(&ovs_uid_mutex); + return new_uid; +} + +/* + * Callback API. These function are used to store + * registered callbacks in OVS DB API. + */ + +/* Add new callback into OVS DB object */ +static void ovs_db_callback_add(ovs_db_t *pdb, ovs_callback_t *new_cb) { + pthread_mutex_lock(&pdb->mutex); + if (pdb->remote_cb) + pdb->remote_cb->prev = new_cb; + new_cb->next = pdb->remote_cb; + new_cb->prev = NULL; + pdb->remote_cb = new_cb; + pthread_mutex_unlock(&pdb->mutex); +} + +/* Remove callback from OVS DB object */ +static void ovs_db_callback_remove(ovs_db_t *pdb, ovs_callback_t *del_cb) { + pthread_mutex_lock(&pdb->mutex); + ovs_callback_t *pre_cb = del_cb->prev; + ovs_callback_t *next_cb = del_cb->next; + + if (next_cb) + next_cb->prev = del_cb->prev; + + if (pre_cb) + pre_cb->next = del_cb->next; + else + pdb->remote_cb = del_cb->next; + + free(del_cb); + pthread_mutex_unlock(&pdb->mutex); +} + +/* Remove all callbacks form OVS DB object */ +static void ovs_db_callback_remove_all(ovs_db_t *pdb) { + pthread_mutex_lock(&pdb->mutex); + while (pdb->remote_cb != NULL) { + ovs_callback_t *del_cb = pdb->remote_cb; + pdb->remote_cb = del_cb->next; + sfree(del_cb); + } + pthread_mutex_unlock(&pdb->mutex); +} + +/* Get/find callback in OVS DB object by UID. Returns pointer + * to requested callback otherwise NULL is returned. + * + * IMPORTANT NOTE: + * The OVS DB mutex MUST be locked by the caller + * to make sure that returned callback is still valid. + */ +static ovs_callback_t *ovs_db_callback_get(ovs_db_t *pdb, uint64_t uid) { + for (ovs_callback_t *cb = pdb->remote_cb; cb != NULL; cb = cb->next) + if (cb->uid == uid) + return cb; + return NULL; +} + +/* Send all requested data to the socket. Returns 0 if + * ALL request data has been sent otherwise negative value + * is returned */ +static int ovs_db_data_send(const ovs_db_t *pdb, const char *data, size_t len) { + ssize_t nbytes = 0; + size_t rem = len; + size_t off = 0; + + while (rem > 0) { + if ((nbytes = send(pdb->sock, data + off, rem, 0)) <= 0) + return -1; + rem -= (size_t)nbytes; + off += (size_t)nbytes; + } + return 0; +} + +/* + * YAJL (Yet Another JSON Library) helper functions + * Documentation (https://lloyd.github.io/yajl/) + */ + +/* Add null-terminated string into YAJL generator handle (JSON object). + * Similar function to yajl_gen_string() but takes null-terminated string + * instead of string and its length. + * + * jgen - YAJL generator handle allocated by yajl_gen_alloc() + * string - Null-terminated string + */ +static yajl_gen_status ovs_yajl_gen_tstring(yajl_gen hander, + const char *string) { + return yajl_gen_string(hander, (const unsigned char *)string, strlen(string)); +} + +/* Add YAJL value into YAJL generator handle (JSON object) + * + * jgen - YAJL generator handle allocated by yajl_gen_alloc() + * jval - YAJL value usually returned by yajl_tree_get() + */ +static yajl_gen_status ovs_yajl_gen_val(yajl_gen jgen, yajl_val jval) { + size_t array_len = 0; + yajl_val *jvalues = NULL; + yajl_val jobj_value = NULL; + const char *obj_key = NULL; + size_t obj_len = 0; + yajl_gen_status yajl_gen_ret = yajl_gen_status_ok; + + if (jval == NULL) + return yajl_gen_generation_complete; + + if (YAJL_IS_STRING(jval)) + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, YAJL_GET_STRING(jval)); + else if (YAJL_IS_DOUBLE(jval)) + OVS_YAJL_CALL(yajl_gen_double, jgen, YAJL_GET_DOUBLE(jval)); + else if (YAJL_IS_INTEGER(jval)) + OVS_YAJL_CALL(yajl_gen_double, jgen, YAJL_GET_INTEGER(jval)); + else if (YAJL_IS_TRUE(jval)) + OVS_YAJL_CALL(yajl_gen_bool, jgen, 1); + else if (YAJL_IS_FALSE(jval)) + OVS_YAJL_CALL(yajl_gen_bool, jgen, 0); + else if (YAJL_IS_NULL(jval)) + OVS_YAJL_CALL(yajl_gen_null, jgen); + else if (YAJL_IS_ARRAY(jval)) { + /* create new array and add all elements into the array */ + array_len = YAJL_GET_ARRAY(jval)->len; + jvalues = YAJL_GET_ARRAY(jval)->values; + OVS_YAJL_CALL(yajl_gen_array_open, jgen); + for (size_t i = 0; i < array_len; i++) + OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jvalues[i]); + OVS_YAJL_CALL(yajl_gen_array_close, jgen); + } else if (YAJL_IS_OBJECT(jval)) { + /* create new object and add all elements into the object */ + OVS_YAJL_CALL(yajl_gen_map_open, jgen); + obj_len = YAJL_GET_OBJECT(jval)->len; + for (size_t i = 0; i < obj_len; i++) { + obj_key = YAJL_GET_OBJECT(jval)->keys[i]; + jobj_value = YAJL_GET_OBJECT(jval)->values[i]; + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, obj_key); + OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jobj_value); + } + OVS_YAJL_CALL(yajl_gen_map_close, jgen); + } else { + OVS_ERROR("%s() unsupported value type %d (skip)", __FUNCTION__, + (int)(jval)->type); + goto yajl_gen_failure; + } + return yajl_gen_status_ok; + +yajl_gen_failure: + OVS_ERROR("%s() error to generate value", __FUNCTION__); + return yajl_gen_ret; +} + +/* OVS DB echo request handler. When OVS DB sends + * "echo" request to the client, client should generate + * "echo" replay with the same content received in the + * request */ +static int ovs_db_table_echo_cb(const ovs_db_t *pdb, yajl_val jnode) { + yajl_val jparams; + yajl_val jid; + yajl_gen jgen; + size_t resp_len = 0; + const char *resp = NULL; + const char *params_path[] = {"params", NULL}; + const char *id_path[] = {"id", NULL}; + yajl_gen_status yajl_gen_ret; + + if ((jgen = yajl_gen_alloc(NULL)) == NULL) + return -1; + + /* check & get request attributes */ + if ((jparams = yajl_tree_get(jnode, params_path, yajl_t_array)) == NULL || + ((jid = yajl_tree_get(jnode, id_path, yajl_t_any)) == NULL)) { + OVS_ERROR("parse echo request failed"); + goto yajl_gen_failure; + } + + /* generate JSON echo response */ + OVS_YAJL_CALL(yajl_gen_map_open, jgen); + + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "result"); + OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jparams); + + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "error"); + OVS_YAJL_CALL(yajl_gen_null, jgen); + + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "id"); + OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jid); + + OVS_YAJL_CALL(yajl_gen_map_close, jgen); + OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)&resp, + &resp_len); + + /* send the response */ + OVS_DEBUG("response: %s", resp); + if (ovs_db_data_send(pdb, resp, resp_len) < 0) { + OVS_ERROR("send echo reply failed"); + goto yajl_gen_failure; + } + /* clean up and return success */ + yajl_gen_clear(jgen); + return 0; + +yajl_gen_failure: + /* release memory */ + yajl_gen_clear(jgen); + return -1; +} + +/* Get OVS DB registered callback by YAJL val. The YAJL + * value should be YAJL string (UID). Returns NULL if + * callback hasn't been found. See also ovs_db_callback_get() + * description for addition info. + */ +static ovs_callback_t *ovs_db_table_callback_get(ovs_db_t *pdb, yajl_val jid) { + char *endptr = NULL; + const char *suid = NULL; + uint64_t uid; + + if (jid && YAJL_IS_STRING(jid)) { + suid = YAJL_GET_STRING(jid); + uid = (uint64_t)strtoul(suid, &endptr, 16); + if (*endptr == '\0' && uid) + return ovs_db_callback_get(pdb, uid); + } + + return NULL; +} + +/* OVS DB table update event handler. + * This callback is called by POLL thread if OVS DB + * table update callback is received from the DB + * server. Once registered callback found, it's called + * by this handler. */ +static int ovs_db_table_update_cb(ovs_db_t *pdb, yajl_val jnode) { + ovs_callback_t *cb = NULL; + yajl_val jvalue; + yajl_val jparams; + yajl_val jtable_updates; + const char *params_path[] = {"params", NULL}; + const char *id_path[] = {"id", NULL}; + + /* check & get request attributes */ + if ((jparams = yajl_tree_get(jnode, params_path, yajl_t_array)) == NULL || + (yajl_tree_get(jnode, id_path, yajl_t_null) == NULL)) { + OVS_ERROR("invalid OVS DB request received"); + return -1; + } + + /* check array length: [, ] */ + if ((YAJL_GET_ARRAY(jparams) == NULL) || + (YAJL_GET_ARRAY(jparams)->len != 2)) { + OVS_ERROR("invalid OVS DB request received"); + return -1; + } + + jvalue = YAJL_GET_ARRAY(jparams)->values[0]; + jtable_updates = YAJL_GET_ARRAY(jparams)->values[1]; + if ((!YAJL_IS_OBJECT(jtable_updates)) || (!YAJL_IS_STRING(jvalue))) { + OVS_ERROR("invalid OVS DB request id or table update received"); + return -1; + } + + /* find registered callback based on */ + pthread_mutex_lock(&pdb->mutex); + cb = ovs_db_table_callback_get(pdb, jvalue); + if (cb == NULL || cb->table.call == NULL) { + OVS_ERROR("No OVS DB table update callback found"); + pthread_mutex_unlock(&pdb->mutex); + return -1; + } + + /* call registered callback */ + cb->table.call(jtable_updates); + pthread_mutex_unlock(&pdb->mutex); + return 0; +} + +/* OVS DB result request handler. + * This callback is called by POLL thread if OVS DB + * result reply is received from the DB server. + * Once registered callback found, it's called + * by this handler. */ +static int ovs_db_result_cb(ovs_db_t *pdb, yajl_val jnode) { + ovs_callback_t *cb = NULL; + yajl_val jresult; + yajl_val jerror; + yajl_val jid; + const char *result_path[] = {"result", NULL}; + const char *error_path[] = {"error", NULL}; + const char *id_path[] = {"id", NULL}; + + jresult = yajl_tree_get(jnode, result_path, yajl_t_any); + jerror = yajl_tree_get(jnode, error_path, yajl_t_any); + jid = yajl_tree_get(jnode, id_path, yajl_t_string); + + /* check & get result attributes */ + if (!jresult || !jerror || !jid) + return -1; + + /* try to find registered callback */ + pthread_mutex_lock(&pdb->mutex); + cb = ovs_db_table_callback_get(pdb, jid); + if (cb != NULL && cb->result.call != NULL) { + /* call registered callback */ + cb->result.call(jresult, jerror); + /* unlock owner of the reply */ + sem_post(&cb->result.sync); + } + + pthread_mutex_unlock(&pdb->mutex); + return 0; +} + +/* Handle JSON data (one request) and call + * appropriate event OVS DB handler. Currently, + * update callback 'ovs_db_table_update_cb' and + * result callback 'ovs_db_result_cb' is supported. + */ +static int ovs_db_json_data_process(ovs_db_t *pdb, const char *data, + size_t len) { + const char *method = NULL; + char yajl_errbuf[OVS_YAJL_ERROR_BUFFER_SIZE]; + const char *method_path[] = {"method", NULL}; + const char *result_path[] = {"result", NULL}; + char *sjson = NULL; + yajl_val jnode, jval; + + /* duplicate the data to make null-terminated string + * required for yajl_tree_parse() */ + if ((sjson = calloc(1, len + 1)) == NULL) + return -1; + + sstrncpy(sjson, data, len + 1); + OVS_DEBUG("[len=%" PRIsz "] %s", len, sjson); + + /* parse json data */ + jnode = yajl_tree_parse(sjson, yajl_errbuf, sizeof(yajl_errbuf)); + if (jnode == NULL) { + OVS_ERROR("yajl_tree_parse() %s", yajl_errbuf); + sfree(sjson); + return -1; + } + + /* get method name */ + if ((jval = yajl_tree_get(jnode, method_path, yajl_t_string)) != NULL) { + if ((method = YAJL_GET_STRING(jval)) == NULL) { + yajl_tree_free(jnode); + sfree(sjson); + return -1; + } + if (strcmp("echo", method) == 0) { + /* echo request from the server */ + if (ovs_db_table_echo_cb(pdb, jnode) < 0) + OVS_ERROR("handle echo request failed"); + } else if (strcmp("update", method) == 0) { + /* update notification */ + if (ovs_db_table_update_cb(pdb, jnode) < 0) + OVS_ERROR("handle update notification failed"); + } + } else if ((jval = yajl_tree_get(jnode, result_path, yajl_t_any)) != NULL) { + /* result notification */ + if (ovs_db_result_cb(pdb, jnode) < 0) + OVS_ERROR("handle result reply failed"); + } else + OVS_ERROR("connot find method or result failed"); + + /* release memory */ + yajl_tree_free(jnode); + sfree(sjson); + return 0; +} + +/* + * JSON reader implementation. + * + * This module process raw JSON data (byte stream) and + * returns fully-fledged JSON data which can be processed + * (parsed) by YAJL later. + */ + +/* Allocate JSON reader instance */ +static ovs_json_reader_t *ovs_json_reader_alloc() { + ovs_json_reader_t *jreader = calloc(1, sizeof(*jreader)); + if (jreader == NULL) + return NULL; + + return jreader; +} + +/* Push raw data into into the JSON reader for processing */ +static int ovs_json_reader_push_data(ovs_json_reader_t *jreader, + const char *data, size_t data_len) { + char *new_buff = NULL; + size_t available = jreader->buff_size - jreader->buff_offset; + + /* check/update required memory space */ + if (available < data_len) { + OVS_DEBUG("Reallocate buffer [size=%d, available=%d required=%d]", + (int)jreader->buff_size, (int)available, (int)data_len); + + /* allocate new chunk of memory */ + new_buff = realloc(jreader->buff_ptr, (jreader->buff_size + data_len)); + if (new_buff == NULL) + return -1; + + /* point to new allocated memory */ + jreader->buff_ptr = new_buff; + jreader->buff_size += data_len; + } + + /* store input data */ + memcpy(jreader->buff_ptr + jreader->buff_offset, data, data_len); + jreader->buff_offset += data_len; + return 0; +} + +/* Pop one fully-fledged JSON if already exists. Returns 0 if + * completed JSON already exists otherwise negative value is + * returned */ +static int ovs_json_reader_pop(ovs_json_reader_t *jreader, + const char **json_ptr, size_t *json_len_ptr) { + size_t nbraces = 0; + size_t json_len = 0; + char *json = NULL; + + /* search open/close brace */ + for (size_t i = jreader->json_offset; i < jreader->buff_offset; i++) { + if (jreader->buff_ptr[i] == '{') { + nbraces++; + } else if (jreader->buff_ptr[i] == '}') + if (nbraces) + if (!(--nbraces)) { + /* JSON data */ + *json_ptr = jreader->buff_ptr + jreader->json_offset; + *json_len_ptr = json_len + 1; + jreader->json_offset = i + 1; + return 0; + } + + /* increase JSON data length */ + if (nbraces) + json_len++; + } + + if (jreader->json_offset) { + if (jreader->json_offset < jreader->buff_offset) { + /* shift data to the beginning of the buffer + * and zero rest of the buffer data */ + json = &jreader->buff_ptr[jreader->json_offset]; + json_len = jreader->buff_offset - jreader->json_offset; + for (size_t i = 0; i < jreader->buff_size; i++) + jreader->buff_ptr[i] = ((i < json_len) ? (json[i]) : (0)); + jreader->buff_offset = json_len; + } else + /* reset the buffer */ + jreader->buff_offset = 0; + + /* data is at the beginning of the buffer */ + jreader->json_offset = 0; + } + + return -1; +} + +/* Reset JSON reader. It is useful when start processing + * new raw data. E.g.: in case of lost stream connection. + */ +static void ovs_json_reader_reset(ovs_json_reader_t *jreader) { + if (jreader) { + jreader->buff_offset = 0; + jreader->json_offset = 0; + } +} + +/* Release internal data allocated for JSON reader */ +static void ovs_json_reader_free(ovs_json_reader_t *jreader) { + if (jreader) { + free(jreader->buff_ptr); + free(jreader); + } +} + +/* Reconnect to OVS DB and call the OVS DB post connection init callback + * if connection has been established. + */ +static void ovs_db_reconnect(ovs_db_t *pdb) { + const char *node_info = pdb->node; + struct addrinfo *result; + + if (pdb->unix_path[0] != '\0') { + /* use UNIX socket instead of INET address */ + node_info = pdb->unix_path; + + 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); + result->ai_addr = (struct sockaddr *)sa_unix; + sa_unix->sun_family = result->ai_family; + sstrncpy(sa_unix->sun_path, pdb->unix_path, sizeof(sa_unix->sun_path)); + } else { + /* inet socket address */ + struct addrinfo hints; + + /* setup criteria for selecting the socket address */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + /* get socket addresses */ + int ret = getaddrinfo(pdb->node, pdb->service, &hints, &result); + if (ret != 0) { + OVS_ERROR("getaddrinfo(): %s", gai_strerror(ret)); + return; + } + } + /* try to connect to the server */ + for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) { + int sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sock < 0) { + OVS_DEBUG("socket(): %s", STRERRNO); + continue; + } + if (connect(sock, rp->ai_addr, rp->ai_addrlen) < 0) { + close(sock); + OVS_DEBUG("connect(): %s [family=%d]", STRERRNO, rp->ai_family); + } else { + /* send notification to event thread */ + pdb->sock = sock; + ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_ESTABLISHED); + break; + } + } + + if (pdb->sock < 0) + OVS_ERROR("connect to \"%s\" failed", node_info); + + freeaddrinfo(result); +} + +/* POLL worker thread. + * It listens on OVS DB connection for incoming + * requests/reply/events etc. Also, it reconnects to OVS DB + * if connection has been lost. + */ +static void *ovs_poll_worker(void *arg) { + ovs_db_t *pdb = (ovs_db_t *)arg; /* pointer to OVS DB */ + ovs_json_reader_t *jreader = NULL; + struct pollfd poll_fd = { + .fd = pdb->sock, + .events = POLLIN | POLLPRI, + .revents = 0, + }; + + /* create JSON reader instance */ + if ((jreader = ovs_json_reader_alloc()) == NULL) { + OVS_ERROR("initialize json reader failed"); + return NULL; + } + + /* poll data */ + while (ovs_db_poll_is_running(pdb)) { + poll_fd.fd = pdb->sock; + int poll_ret = poll(&poll_fd, 1, /* ms */ OVS_DB_POLL_TIMEOUT * 1000); + if (poll_ret < 0) { + OVS_ERROR("poll(): %s", STRERRNO); + break; + } else if (poll_ret == 0) { + OVS_DEBUG("poll(): timeout"); + if (pdb->sock < 0) + /* invalid fd, so try to reconnect */ + ovs_db_reconnect(pdb); + continue; + } + if (poll_fd.revents & POLLNVAL) { + /* invalid file descriptor, clean-up */ + ovs_db_callback_remove_all(pdb); + ovs_json_reader_reset(jreader); + /* setting poll FD to -1 tells poll() call to ignore this FD. + * In that case poll() call will return timeout all the time */ + pdb->sock = (-1); + } else if ((poll_fd.revents & POLLERR) || (poll_fd.revents & POLLHUP)) { + /* connection is broken */ + close(poll_fd.fd); + ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED); + OVS_ERROR("poll() peer closed its end of the channel"); + } else if ((poll_fd.revents & POLLIN) || (poll_fd.revents & POLLPRI)) { + /* read incoming data */ + char buff[OVS_DB_POLL_READ_BLOCK_SIZE]; + ssize_t nbytes = recv(poll_fd.fd, buff, sizeof(buff), 0); + if (nbytes < 0) { + OVS_ERROR("recv(): %s", STRERRNO); + /* read error? Try to reconnect */ + close(poll_fd.fd); + continue; + } else if (nbytes == 0) { + close(poll_fd.fd); + ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED); + OVS_ERROR("recv() peer has performed an orderly shutdown"); + continue; + } + /* read incoming data */ + size_t json_len = 0; + const char *json = NULL; + OVS_DEBUG("recv(): received %zd bytes of data", nbytes); + ovs_json_reader_push_data(jreader, buff, nbytes); + while (!ovs_json_reader_pop(jreader, &json, &json_len)) + /* process JSON data */ + ovs_db_json_data_process(pdb, json, json_len); + } + } + + OVS_DEBUG("poll thread has been completed"); + ovs_json_reader_free(jreader); + return NULL; +} + +/* EVENT worker thread. + * Perform task based on incoming events. This + * task can be done asynchronously which allows to + * handle OVS DB callback like 'init_cb'. + */ +static void *ovs_event_worker(void *arg) { + ovs_db_t *pdb = (ovs_db_t *)arg; + + while (pdb->event_thread.value != OVS_DB_EVENT_TERMINATE) { + /* wait for an event */ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += (OVS_DB_EVENT_TIMEOUT); + int ret = pthread_cond_timedwait(&pdb->event_thread.cond, + &pdb->event_thread.mutex, &ts); + if (!ret || ret == ETIMEDOUT) { + /* handle the event */ + OVS_DEBUG("handle event %d", pdb->event_thread.value); + switch (pdb->event_thread.value) { + case OVS_DB_EVENT_CONN_ESTABLISHED: + if (pdb->cb.post_conn_init) + pdb->cb.post_conn_init(pdb); + /* reset event */ + pdb->event_thread.value = OVS_DB_EVENT_NONE; + break; + case OVS_DB_EVENT_CONN_TERMINATED: + if (pdb->cb.post_conn_terminate) + pdb->cb.post_conn_terminate(); + /* reset event */ + pdb->event_thread.value = OVS_DB_EVENT_NONE; + break; + case OVS_DB_EVENT_NONE: + /* wait timeout */ + OVS_DEBUG("no event received (timeout)"); + break; + default: + OVS_DEBUG("unknown event received"); + break; + } + } else { + /* unexpected error */ + OVS_ERROR("pthread_cond_timedwait() failed"); + break; + } + } + + OVS_DEBUG("event thread has been completed"); + return NULL; +} + +/* Initialize EVENT thread */ +static int ovs_db_event_thread_init(ovs_db_t *pdb) { + pdb->event_thread.tid = (pthread_t){0}; + /* init event thread condition variable */ + if (pthread_cond_init(&pdb->event_thread.cond, NULL)) { + return -1; + } + /* init event thread mutex */ + if (pthread_mutex_init(&pdb->event_thread.mutex, NULL)) { + pthread_cond_destroy(&pdb->event_thread.cond); + return -1; + } + /* Hold the event thread mutex. It ensures that no events + * will be lost while thread is still starting. Once event + * thread is started and ready to accept events, it will release + * the mutex */ + if (pthread_mutex_lock(&pdb->event_thread.mutex)) { + pthread_mutex_destroy(&pdb->event_thread.mutex); + pthread_cond_destroy(&pdb->event_thread.cond); + return -1; + } + /* start event thread */ + pthread_t tid; + if (plugin_thread_create(&tid, NULL, ovs_event_worker, pdb, + "utils_ovs:event") != 0) { + pthread_mutex_unlock(&pdb->event_thread.mutex); + pthread_mutex_destroy(&pdb->event_thread.mutex); + pthread_cond_destroy(&pdb->event_thread.cond); + return -1; + } + pdb->event_thread.tid = tid; + return 0; +} + +/* Terminate EVENT thread */ +static int ovs_db_event_thread_terminate(ovs_db_t *pdb) { + if (pthread_equal(pdb->event_thread.tid, (pthread_t){0})) { + /* already terminated */ + return 0; + } + ovs_db_event_post(pdb, OVS_DB_EVENT_TERMINATE); + if (pthread_join(pdb->event_thread.tid, NULL) != 0) + return -1; + /* Event thread always holds the thread mutex when + * performs some task (handles event) and releases it when + * while sleeping. Thus, if event thread exits, the mutex + * remains locked */ + pdb->event_thread.tid = (pthread_t){0}; + pthread_mutex_unlock(&pdb->event_thread.mutex); + return 0; +} + +/* Destroy EVENT thread private data */ +static void ovs_db_event_thread_data_destroy(ovs_db_t *pdb) { + /* destroy mutex */ + pthread_mutex_destroy(&pdb->event_thread.mutex); + pthread_cond_destroy(&pdb->event_thread.cond); +} + +/* Initialize POLL thread */ +static int ovs_db_poll_thread_init(ovs_db_t *pdb) { + pdb->poll_thread.tid = (pthread_t){0}; + /* init event thread mutex */ + if (pthread_mutex_init(&pdb->poll_thread.mutex, NULL)) { + return -1; + } + /* start poll thread */ + pthread_t tid; + pdb->poll_thread.state = OVS_DB_POLL_STATE_RUNNING; + if (plugin_thread_create(&tid, NULL, ovs_poll_worker, pdb, + "utils_ovs:poll") != 0) { + pthread_mutex_destroy(&pdb->poll_thread.mutex); + return -1; + } + pdb->poll_thread.tid = tid; + return 0; +} + +/* Destroy POLL thread */ +/* XXX: Must hold pdb->mutex when calling! */ +static int ovs_db_poll_thread_destroy(ovs_db_t *pdb) { + if (pthread_equal(pdb->poll_thread.tid, (pthread_t){0})) { + /* already destroyed */ + return 0; + } + /* change thread state */ + pthread_mutex_lock(&pdb->poll_thread.mutex); + pdb->poll_thread.state = OVS_DB_POLL_STATE_EXITING; + pthread_mutex_unlock(&pdb->poll_thread.mutex); + /* join the thread */ + if (pthread_join(pdb->poll_thread.tid, NULL) != 0) + return -1; + pthread_mutex_destroy(&pdb->poll_thread.mutex); + pdb->poll_thread.tid = (pthread_t){0}; + return 0; +} + +/* + * Public OVS DB API implementation + */ + +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; + + /* allocate db data & fill it */ + ovs_db_t *pdb = calloc(1, sizeof(*pdb)); + if (pdb == NULL) + return NULL; + pdb->sock = -1; + + /* store the OVS DB address */ + sstrncpy(pdb->node, node, sizeof(pdb->node)); + sstrncpy(pdb->service, service, sizeof(pdb->service)); + sstrncpy(pdb->unix_path, unix_path, sizeof(pdb->unix_path)); + + /* setup OVS DB callbacks */ + if (cb) + pdb->cb = *cb; + + /* init OVS DB mutex attributes */ + pthread_mutexattr_t mutex_attr; + if (pthread_mutexattr_init(&mutex_attr)) { + OVS_ERROR("OVS DB mutex attribute init failed"); + sfree(pdb); + return NULL; + } + /* set OVS DB mutex as recursive */ + if (pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE)) { + OVS_ERROR("Failed to set OVS DB mutex as recursive"); + pthread_mutexattr_destroy(&mutex_attr); + sfree(pdb); + return NULL; + } + /* init OVS DB mutex */ + if (pthread_mutex_init(&pdb->mutex, &mutex_attr)) { + OVS_ERROR("OVS DB mutex init failed"); + pthread_mutexattr_destroy(&mutex_attr); + sfree(pdb); + return NULL; + } + /* destroy mutex attributes */ + pthread_mutexattr_destroy(&mutex_attr); + + /* init event thread */ + if (ovs_db_event_thread_init(pdb) < 0) { + ret = ovs_db_destroy(pdb); + if (ret > 0) + goto failure; + else + return NULL; + } + + /* init polling thread */ + if (ovs_db_poll_thread_init(pdb) < 0) { + 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, + ovs_db_result_cb_t cb) { + int ret = 0; + yajl_gen_status yajl_gen_ret; + yajl_val jparams; + yajl_gen jgen; + ovs_callback_t *new_cb = NULL; + uint64_t uid; + char uid_buff[OVS_UID_STR_SIZE]; + const char *req = NULL; + size_t req_len = 0; + struct timespec ts; + + /* sanity check */ + if (!pdb || !method || !params) + return -1; + + if ((jgen = yajl_gen_alloc(NULL)) == NULL) + return -1; + + /* try to parse params */ + if ((jparams = yajl_tree_parse(params, NULL, 0)) == NULL) { + OVS_ERROR("params is not a JSON string"); + yajl_gen_clear(jgen); + return -1; + } + + /* generate method field */ + OVS_YAJL_CALL(yajl_gen_map_open, jgen); + + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "method"); + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, method); + + /* generate params field */ + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "params"); + OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jparams); + yajl_tree_free(jparams); + + /* generate id field */ + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "id"); + uid = ovs_uid_generate(); + ssnprintf(uid_buff, sizeof(uid_buff), "%" PRIX64, uid); + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_buff); + + OVS_YAJL_CALL(yajl_gen_map_close, jgen); + + if (cb) { + /* register result callback */ + if ((new_cb = calloc(1, sizeof(*new_cb))) == NULL) + goto yajl_gen_failure; + + /* add new callback to front */ + sem_init(&new_cb->result.sync, 0, 0); + new_cb->result.call = cb; + new_cb->uid = uid; + ovs_db_callback_add(pdb, new_cb); + } + + /* send the request */ + OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)&req, &req_len); + OVS_DEBUG("%s", req); + if (!ovs_db_data_send(pdb, req, req_len)) { + if (cb) { + /* wait for result */ + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += OVS_DB_SEND_REQ_TIMEOUT; + if (sem_timedwait(&new_cb->result.sync, &ts) < 0) { + OVS_ERROR("%s() no replay received within %d sec", __FUNCTION__, + OVS_DB_SEND_REQ_TIMEOUT); + ret = (-1); + } + } + } else { + OVS_ERROR("ovs_db_data_send() failed"); + ret = (-1); + } + +yajl_gen_failure: + if (new_cb) { + /* destroy callback */ + sem_destroy(&new_cb->result.sync); + ovs_db_callback_remove(pdb, new_cb); + } + + /* release memory */ + yajl_gen_clear(jgen); + return (yajl_gen_ret != yajl_gen_status_ok) ? (-1) : ret; +} + +int ovs_db_table_cb_register(ovs_db_t *pdb, const char *tb_name, + const char **tb_column, + ovs_db_table_cb_t update_cb, + ovs_db_result_cb_t result_cb, unsigned int flags) { + yajl_gen jgen; + yajl_gen_status yajl_gen_ret; + ovs_callback_t *new_cb = NULL; + char uid_str[OVS_UID_STR_SIZE]; + char *params; + size_t params_len; + int ovs_db_ret = 0; + + /* sanity check */ + if (pdb == NULL || tb_name == NULL || update_cb == NULL) + return -1; + + /* allocate new update callback */ + if ((new_cb = calloc(1, sizeof(*new_cb))) == NULL) + return -1; + + /* init YAJL generator */ + if ((jgen = yajl_gen_alloc(NULL)) == NULL) { + sfree(new_cb); + return -1; + } + + /* add new callback to front */ + new_cb->table.call = update_cb; + new_cb->uid = ovs_uid_generate(); + ovs_db_callback_add(pdb, new_cb); + + /* make update notification request + * [, , ] */ + OVS_YAJL_CALL(yajl_gen_array_open, jgen); + { + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, OVS_DB_DEFAULT_DB_NAME); + + /* uid string */ + ssnprintf(uid_str, sizeof(uid_str), "%" PRIX64, new_cb->uid); + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_str); + + /* */ + OVS_YAJL_CALL(yajl_gen_map_open, jgen); + { + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, tb_name); + OVS_YAJL_CALL(yajl_gen_array_open, jgen); + { + /* */ + OVS_YAJL_CALL(yajl_gen_map_open, jgen); + { + if (tb_column) { + /* columns within the table to be monitored */ + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "columns"); + OVS_YAJL_CALL(yajl_gen_array_open, jgen); + for (; *tb_column; tb_column++) + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, *tb_column); + OVS_YAJL_CALL(yajl_gen_array_close, jgen); + } + /* specify select option */ + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "select"); + { + OVS_YAJL_CALL(yajl_gen_map_open, jgen); + { + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "initial"); + OVS_YAJL_CALL(yajl_gen_bool, jgen, + flags & OVS_DB_TABLE_CB_FLAG_INITIAL); + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "insert"); + OVS_YAJL_CALL(yajl_gen_bool, jgen, + flags & OVS_DB_TABLE_CB_FLAG_INSERT); + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "delete"); + OVS_YAJL_CALL(yajl_gen_bool, jgen, + flags & OVS_DB_TABLE_CB_FLAG_DELETE); + OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "modify"); + OVS_YAJL_CALL(yajl_gen_bool, jgen, + flags & OVS_DB_TABLE_CB_FLAG_MODIFY); + } + OVS_YAJL_CALL(yajl_gen_map_close, jgen); + } + } + OVS_YAJL_CALL(yajl_gen_map_close, jgen); + } + OVS_YAJL_CALL(yajl_gen_array_close, jgen); + } + OVS_YAJL_CALL(yajl_gen_map_close, jgen); + } + OVS_YAJL_CALL(yajl_gen_array_close, jgen); + + /* make a request to subscribe to given table */ + OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)¶ms, + ¶ms_len); + if (ovs_db_send_request(pdb, "monitor", params, result_cb) < 0) { + OVS_ERROR("Failed to subscribe to \"%s\" table", tb_name); + ovs_db_ret = (-1); + } + +yajl_gen_failure: + /* release memory */ + yajl_gen_clear(jgen); + return ovs_db_ret; +} + +int ovs_db_destroy(ovs_db_t *pdb) { + int ovs_db_ret = 0; + int ret = 0; + + /* sanity check */ + if (pdb == NULL) + return -1; + + /* stop event thread */ + if (ovs_db_event_thread_terminate(pdb) < 0) { + OVS_ERROR("stop event thread failed"); + ovs_db_ret = -1; + } + + /* 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 ret; + } + + /* stop poll thread and destroy thread's private data */ + if (ovs_db_poll_thread_destroy(pdb) < 0) { + OVS_ERROR("destroy poll thread failed"); + ovs_db_ret = -1; + } + + /* destroy event thread private data */ + ovs_db_event_thread_data_destroy(pdb); + + pthread_mutex_unlock(&pdb->mutex); + + /* unsubscribe callbacks */ + ovs_db_callback_remove_all(pdb); + + /* close connection */ + if (pdb->sock >= 0) + close(pdb->sock); + + /* release DB handler */ + pthread_mutex_destroy(&pdb->mutex); + sfree(pdb); + return ovs_db_ret; +} + +/* + * Public OVS utils API implementation + */ + +/* Get YAJL value by key from YAJL dictionary + * + * EXAMPLE: + * { + * "key_a" : + * "key_b" : + * } + */ +yajl_val ovs_utils_get_value_by_key(yajl_val jval, const char *key) { + const char *obj_key = NULL; + + /* check params */ + if (!YAJL_IS_OBJECT(jval) || (key == NULL)) + return NULL; + + /* find a value by key */ + for (size_t i = 0; i < YAJL_GET_OBJECT(jval)->len; i++) { + obj_key = YAJL_GET_OBJECT(jval)->keys[i]; + if (strcmp(obj_key, key) == 0) + return YAJL_GET_OBJECT(jval)->values[i]; + } + + return NULL; +} + +/* Get OVS DB map value by given map key + * + * FROM RFC7047: + * + * + * A 2-element JSON array that represents a pair within a database + * map. The first element is an that represents the key, and + * the second element is an that represents the value. + * + * + * A 2-element JSON array that represents a database map value. The + * first element of the array must be the string "map", and the + * second element must be an array of zero or more s giving the + * values in the map. All of the s must have the same key and + * value types. + * + * EXAMPLE: + * [ + * "map", [ + * [ "key_a", ], [ "key_b", ], ... + * ] + * ] + */ +yajl_val ovs_utils_get_map_value(yajl_val jval, const char *key) { + size_t map_len = 0; + size_t array_len = 0; + yajl_val *map_values = NULL; + yajl_val *array_values = NULL; + const char *str_val = NULL; + + /* check YAJL array */ + if (!YAJL_IS_ARRAY(jval) || (key == NULL)) + return NULL; + + /* check a database map value (2-element, first one should be a string */ + array_len = YAJL_GET_ARRAY(jval)->len; + array_values = YAJL_GET_ARRAY(jval)->values; + if ((array_len != 2) || (!YAJL_IS_STRING(array_values[0])) || + (!YAJL_IS_ARRAY(array_values[1]))) + return NULL; + + /* check first element of the array */ + str_val = YAJL_GET_STRING(array_values[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]) || YAJL_GET_ARRAY(map_values[i]) == NULL) + break; + + /* check a database pair value (2-element, first one represents a key + * and it should be a string in our case */ + array_len = YAJL_GET_ARRAY(map_values[i])->len; + array_values = YAJL_GET_ARRAY(map_values[i])->values; + if ((array_len != 2) || (!YAJL_IS_STRING(array_values[0]))) + break; + + /* return map value if given key equals map key */ + str_val = YAJL_GET_STRING(array_values[0]); + if (str_val != NULL && strcmp(key, str_val) == 0) + return array_values[1]; + } + return NULL; +} diff --git a/src/utils/ovs/ovs.h b/src/utils/ovs/ovs.h new file mode 100644 index 00000000..c93322ea --- /dev/null +++ b/src/utils/ovs/ovs.h @@ -0,0 +1,236 @@ +/** + * collectd - src/utils_ovs.h + * + * Copyright(c) 2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Volodymyr Mytnyk + * + * Description: + * The OVS util module provides the following features: + * - Implements the OVS DB communication transport specified + * by RFC7047: + * * Connect/disconnect to OVS DB; + * * Recovery mechanism in case of OVS DB connection lost; + * * Subscription mechanism to OVS DB table update events + * (insert/modify/delete); + * * Send custom JSON request to OVS DB (poll table data, etc.) + * * Handling of echo request from OVS DB server to verify the + * liveness of the connection. + * - Provides YAJL helpers functions. + * + * OVS DB API User Guide: + * All OVS DB function/structure names begins from 'ovs_db_*' prefix. To + * start using OVS DB API, client (plugin) should initialize the OVS DB + * object (`ovs_db_t') by calling `ovs_db_init' function. It initializes + * internal data and creates two main workers (threads). The result of the + * function is a pointer to new OVS DB object which can be used by other + * OVS DB API later and must be released by `ovs_db_destroy' function if + * the object isn't needed anymore. + * Once OVS DB API is initialized, the `init_cb' callback is called if + * the connection to OVS DB has been established. This callback is called + * every time the OVS DB is reconnected. So, if the client registers table + * update event callbacks or does any other OVS DB setup that can be lost + * after OVS DB reconnecting, it should be done in `init_cb' callback. + * The `ovs_db_table_cb_register` function is used to register OVS DB + * table update event callback and receive the table update notification + * when requested event occurs (registered callback is called). See + * function API for more info. + * To send custom JSON-RPC request to OVS DB, the `ovs_db_send_request' + * function is used. Please note, that connection to OVS DB should be + * established otherwise the function will return error. + * To verify the liveness of established connection, the OVS DB server + * sends echo request to the client with a given interval. The OVS utils + * takes care about this request and handles it properly. + **/ + +#ifndef UTILS_OVS_H +#define UTILS_OVS_H + +#include +#include + +/* Forward declaration */ +typedef struct ovs_db_s ovs_db_t; + +/* OVS DB callback type declaration */ +typedef void (*ovs_db_table_cb_t)(yajl_val jupdates); +typedef void (*ovs_db_result_cb_t)(yajl_val jresult, yajl_val jerror); + +/* OVS DB structures */ +struct ovs_db_callback_s { + /* + * This callback is called when OVS DB connection + * has been established and ready to use. Client + * can use this callback to configure OVS DB, e.g. + * to subscribe to table update notification or poll + * some OVS DB data. This field can be NULL. + */ + void (*post_conn_init)(ovs_db_t *pdb); + /* + * This callback is called when OVS DB connection + * has been lost. This field can be NULL. + */ + void (*post_conn_terminate)(void); +}; +typedef struct ovs_db_callback_s ovs_db_callback_t; + +/* OVS DB defines */ +#define OVS_DB_ADDR_NODE_SIZE 256 +#define OVS_DB_ADDR_SERVICE_SIZE 128 +#define OVS_DB_ADDR_UNIX_SIZE 108 + +/* OVS DB prototypes */ + +/* + * NAME + * ovs_db_init + * + * DESCRIPTION + * Initialize OVS DB internal data. The `ovs_db_destroy' function + * shall destroy the returned object. + * + * PARAMETERS + * `node' OVS DB Address. + * `service' OVS DB service name. + * `unix' OVS DB unix socket path. + * `cb' OVS DB callbacks. + * + * RETURN VALUE + * New ovs_db_t object upon success or NULL if an error occurred. + */ +ovs_db_t *ovs_db_init(const char *node, const char *service, + const char *unix_path, ovs_db_callback_t *cb); + +/* + * NAME + * ovs_db_destroy + * + * DESCRIPTION + * Destroy OVS DB object referenced by `pdb'. + * + * PARAMETERS + * `pdb' Pointer to OVS DB object. + * + * RETURN VALUE + * Zero upon success or non-zero if an error occurred. + */ +int ovs_db_destroy(ovs_db_t *pdb); + +/* + * NAME + * ovs_db_send_request + * + * DESCRIPTION + * Send JSON request to OVS DB server. + * + * PARAMETERS + * `pdb' Pointer to OVS DB object. + * `method' Request method name. + * `params' Method params to be sent (JSON value as a string). + * `cb' Result callback of the request. If NULL, the request + * is sent asynchronously. + * + * RETURN VALUE + * Zero upon success or non-zero if an error occurred. + */ +int ovs_db_send_request(ovs_db_t *pdb, const char *method, const char *params, + ovs_db_result_cb_t cb); + +/* callback types */ +#define OVS_DB_TABLE_CB_FLAG_INITIAL 0x01U +#define OVS_DB_TABLE_CB_FLAG_INSERT 0x02U +#define OVS_DB_TABLE_CB_FLAG_DELETE 0x04U +#define OVS_DB_TABLE_CB_FLAG_MODIFY 0x08U +#define OVS_DB_TABLE_CB_FLAG_ALL 0x0FU + +/* + * NAME + * ovs_db_table_cb_register + * + * DESCRIPTION + * Subscribe a callback on OVS DB table event. It allows to + * receive notifications (`update_cb' callback is called) of + * changes to requested table. + * + * PARAMETERS + * `pdb' Pointer to OVS DB object. + * `tb_name' OVS DB Table name to be monitored. + * `tb_column' OVS DB Table columns to be monitored. Last + * element in the array should be NULL. + * `update_cb' Callback function that is called when + * requested table columns are changed. + * `cb' Result callback of the request. If NULL, the call + * becomes asynchronous. + * Useful, if OVS_DB_TABLE_CB_FLAG_INITIAL is set. + * `flags' Bit mask of: + * OVS_DB_TABLE_CB_FLAG_INITIAL Receive initial values in + * result callback. + * OVS_DB_TABLE_CB_FLAG_INSERT Receive table insert events. + * OVS_DB_TABLE_CB_FLAG_DELETE Receive table remove events. + * OVS_DB_TABLE_CB_FLAG_MODIFY Receive table update events. + * OVS_DB_TABLE_CB_FLAG_ALL Receive all events. + * + * RETURN VALUE + * Zero upon success or non-zero if an error occurred. + */ +int ovs_db_table_cb_register(ovs_db_t *pdb, const char *tb_name, + const char **tb_column, + ovs_db_table_cb_t update_cb, + ovs_db_result_cb_t result_cb, unsigned int flags); + +/* + * OVS utils API + */ + +/* + * NAME + * ovs_utils_get_value_by_key + * + * DESCRIPTION + * Get YAJL value by object name. + * + * PARAMETERS + * `jval' YAJL object value. + * `key' Object key name. + * + * RETURN VALUE + * YAJL value upon success or NULL if key not found. + */ +yajl_val ovs_utils_get_value_by_key(yajl_val jval, const char *key); + +/* + * NAME + * ovs_utils_get_map_value + * + * DESCRIPTION + * Get OVS DB map value by given map key (rfc7047, "Notation" section). + * + * PARAMETERS + * `jval' A 2-element YAJL array that represents a OVS DB map value. + * `key' OVS DB map key name. + * + * RETURN VALUE + * YAJL value upon success or NULL if key not found. + */ +yajl_val ovs_utils_get_map_value(yajl_val jval, const char *key); + +#endif diff --git a/src/utils/proc_pids/proc_pids.c b/src/utils/proc_pids/proc_pids.c new file mode 100644 index 00000000..fb30172c --- /dev/null +++ b/src/utils/proc_pids/proc_pids.c @@ -0,0 +1,361 @@ +/** + * collectd - src/utils/proc_pids/proc_pids.c + * + * Copyright(c) 2018-2019 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Starzyk, Mateusz + * Wojciech Andralojc + * Michał Aleksiński + **/ + +#include "collectd.h" +#include "utils/common/common.h" +#include "utils/proc_pids/proc_pids.h" + +#define UTIL_NAME "utils_proc_pids" + +void pids_list_free(pids_list_t *list) { + assert(list); + + sfree(list->pids); + sfree(list); +} + +int proc_pids_is_name_valid(const char *name) { + + if (name != NULL) { + unsigned len = strlen(name); + if (len > 0 && len <= MAX_PROC_NAME_LEN) + return 1; + else { + DEBUG(UTIL_NAME + ": Process name \'%s\' is too long. Max supported len is %d chars.", + name, MAX_PROC_NAME_LEN); + } + } + + return 0; +} + +int pids_list_add_pid(pids_list_t *list, const pid_t pid) { + assert(list); + + if (list->allocated == list->size) { + size_t new_allocated = list->allocated + 1 + list->allocated / 10; + pid_t *new_pids = realloc(list->pids, sizeof(pid_t) * new_allocated); + + if (NULL == new_pids) { + ERROR(UTIL_NAME ": Alloc error\n"); + return -1; + } + + list->pids = new_pids; + list->allocated = new_allocated; + } + + list->pids[list->size] = pid; + list->size++; + + return 0; +} + +int pids_list_add_list(pids_list_t *dst, pids_list_t *src) { + assert(dst); + assert(src); + + if (dst->allocated < dst->size + src->size) { + pid_t *new_pids = + realloc(dst->pids, sizeof(pid_t) * (dst->size + src->size)); + + if (NULL == new_pids) { + ERROR(UTIL_NAME ": Alloc error\n"); + return -1; + } + + dst->allocated = dst->size + src->size; + dst->pids = new_pids; + } + + memcpy(dst->pids + dst->size, src->pids, src->size * sizeof(*(src->pids))); + dst->size += src->size; + + return 0; +} + +int pids_list_clear(pids_list_t *list) { + assert(list); + + if (list->pids != NULL) + sfree(list->pids); + + list->size = 0; + list->allocated = 0; + + return 0; +} + +int pids_list_contains_pid(pids_list_t *list, const pid_t pid) { + assert(list); + + for (int i = 0; i < list->size; i++) + if (list->pids[i] == pid) + return 1; + + return 0; +} + +/* + * NAME + * read_proc_name + * + * DESCRIPTION + * Reads process name from given pid directory. + * Strips new-line character (\n). + * + * PARAMETERS + * `procfs_path' Path to systems proc directory (e.g. /proc) + * `pid_entry' Dirent for PID directory + * `name' Output buffer for process name, recommended proc_comm. + * `out_size' Output buffer size, recommended sizeof(proc_comm) + * + * RETURN VALUE + * On success, the number of read bytes (includes stripped \n). + * -1 on file open error + */ +static int read_proc_name(const char *procfs_path, + const struct dirent *pid_entry, char *name, + const size_t out_size) { + assert(pid_entry); + assert(name); + assert(out_size); + memset(name, 0, out_size); + + const char *comm_file_name = "comm"; + + char *path = ssnprintf_alloc("%s/%s/%s", procfs_path, pid_entry->d_name, + comm_file_name); + if (path == NULL) + return -1; + FILE *f = fopen(path, "r"); + if (f == NULL) { + ERROR(UTIL_NAME ": Failed to open comm file, error: %d\n", errno); + sfree(path); + return -1; + } + size_t read_length = fread(name, sizeof(char), out_size, f); + name[out_size - 1] = '\0'; + fclose(f); + sfree(path); + /* strip new line ending */ + char *newline = strchr(name, '\n'); + if (newline) { + *newline = '\0'; + } + + return read_length; +} + +/* + * NAME + * get_pid_number + * + * DESCRIPTION + * Gets pid number for given /proc/pid directory entry or + * returns error if input directory does not hold PID information. + * + * PARAMETERS + * `entry' Dirent for PID directory + * `pid' PID number to be filled + * + * RETURN VALUE + * 0 on success. -1 on error. + */ +static int get_pid_number(struct dirent *entry, pid_t *pid) { + char *tmp_end; /* used for strtoul error check*/ + + if (pid == NULL || entry == NULL) + return -1; + + if (entry->d_type != DT_DIR) + return -1; + + /* trying to get pid number from directory name*/ + *pid = strtoul(entry->d_name, &tmp_end, 10); + if (*tmp_end != '\0') { + return -1; /* conversion failed, not proc-pid */ + } + /* all checks passed, marking as success */ + return 0; +} + +int proc_pids_init(const char **procs_names_array, + const size_t procs_names_array_size, + proc_pids_t **proc_pids[]) { + + proc_pids_t **proc_pids_array; + assert(proc_pids); + assert(NULL == *proc_pids); + + /* Copy procs names to output array. Initialize pids list with NULL value. */ + proc_pids_array = calloc(procs_names_array_size, sizeof(*proc_pids_array)); + + if (NULL == proc_pids_array) + return -1; + + for (size_t i = 0; i < procs_names_array_size; ++i) { + proc_pids_array[i] = calloc(1, sizeof(**proc_pids_array)); + if (NULL == proc_pids_array[i]) + goto proc_pids_init_error; + + sstrncpy(proc_pids_array[i]->process_name, procs_names_array[i], + STATIC_ARRAY_SIZE(proc_pids_array[i]->process_name)); + proc_pids_array[i]->prev = NULL; + proc_pids_array[i]->curr = NULL; + } + + *proc_pids = proc_pids_array; + + return 0; +proc_pids_init_error: + if (NULL != proc_pids_array) { + for (size_t i = 0; i < procs_names_array_size; ++i) { + free(proc_pids_array[i]); + } + free(proc_pids_array); + } + return -1; +} + +static void swap_proc_pids(proc_pids_t **proc_pids, size_t proc_pids_num) { + for (size_t i = 0; i < proc_pids_num; i++) { + pids_list_t *swap = proc_pids[i]->prev; + proc_pids[i]->prev = proc_pids[i]->curr; + proc_pids[i]->curr = swap; + } +} + +int proc_pids_update(const char *procfs_path, proc_pids_t **proc_pids, + size_t proc_pids_num) { + assert(procfs_path); + assert(proc_pids); + + DIR *proc_dir = opendir(procfs_path); + if (proc_dir == NULL) { + ERROR(UTIL_NAME ": Could not open %s directory, error: %d", procfs_path, + errno); + return -1; + } + + swap_proc_pids(proc_pids, proc_pids_num); + + for (size_t i = 0; i < proc_pids_num; i++) { + if (NULL == proc_pids[i]->curr) + proc_pids[i]->curr = calloc(1, sizeof(*(proc_pids[i]->curr))); + + if (NULL == proc_pids[i]->curr) { + ERROR(UTIL_NAME ": Alloc error\n"); + goto update_error; + } + + proc_pids[i]->curr->size = 0; + } + + /* Go through procfs and find PIDS and their comms */ + struct dirent *entry; + while ((entry = readdir(proc_dir)) != NULL) { + pid_t pid; + int pid_conversion = get_pid_number(entry, &pid); + if (pid_conversion < 0) + continue; + + proc_comm_t comm; + int read_result = + read_proc_name(procfs_path, entry, comm, sizeof(proc_comm_t)); + if (read_result <= 0) + continue; + + /* Try to find comm in input procs array */ + for (size_t i = 0; i < proc_pids_num; ++i) { + if (0 == + strncmp(comm, proc_pids[i]->process_name, STATIC_ARRAY_SIZE(comm))) + pids_list_add_pid(proc_pids[i]->curr, pid); + } + } + + int close_result = closedir(proc_dir); + if (0 != close_result) { + ERROR(UTIL_NAME ": failed to close /proc directory, error: %d", errno); + goto update_error; + } + return 0; + +update_error: + swap_proc_pids(proc_pids, proc_pids_num); + return -1; +} + +int pids_list_diff(proc_pids_t *proc, pids_list_t *added, + pids_list_t *removed) { + assert(proc); + assert(added); + assert(removed); + + added->size = 0; + removed->size = 0; + + if (NULL == proc->prev || 0 == proc->prev->size) { + /* append all PIDs from curr to added*/ + return pids_list_add_list(added, proc->curr); + } else if (NULL == proc->curr || 0 == proc->curr->size) { + /* append all PIDs from prev to removed*/ + return pids_list_add_list(removed, proc->prev); + } + + for (int i = 0; i < proc->prev->size; i++) + if (0 == pids_list_contains_pid(proc->curr, proc->prev->pids[i])) { + int add_result = pids_list_add_pid(removed, proc->prev->pids[i]); + if (add_result < 0) + return add_result; + } + + for (int i = 0; i < proc->curr->size; i++) + if (0 == pids_list_contains_pid(proc->prev, proc->curr->pids[i])) { + int add_result = pids_list_add_pid(added, proc->curr->pids[i]); + if (add_result < 0) + return add_result; + } + + return 0; +} + +int proc_pids_free(proc_pids_t *proc_pids[], size_t proc_pids_num) { + for (size_t i = 0; i < proc_pids_num; i++) { + if (NULL != proc_pids[i]->curr) + pids_list_free(proc_pids[i]->curr); + if (NULL != proc_pids[i]->prev) + pids_list_free(proc_pids[i]->prev); + sfree(proc_pids[i]); + } + sfree(proc_pids); + + return 0; +} diff --git a/src/utils/proc_pids/proc_pids.h b/src/utils/proc_pids/proc_pids.h new file mode 100644 index 00000000..9170398f --- /dev/null +++ b/src/utils/proc_pids/proc_pids.h @@ -0,0 +1,231 @@ +/** + * collectd - src/utils/proc_pids/proc_pids.h + * + * Copyright(c) 2018-2019 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Starzyk, Mateusz + * Wojciech Andralojc + * Michał Aleksiński + **/ + +#ifndef UTILS_PROC_PIDS_PROC_PIDS_H +#define UTILS_PROC_PIDS_PROC_PIDS_H 1 + +#include +#include + +/* + * Process name inside comm file is limited to 16 chars. + * More info here: http://man7.org/linux/man-pages/man5/proc.5.html + */ +#define MAX_PROC_NAME_LEN 16 + +/* Helper typedef for process name array + * Extra 1 char is added for string null termination. + */ +typedef char proc_comm_t[MAX_PROC_NAME_LEN + 1]; + +/* List of pids. */ +typedef struct pids_list_s { + pid_t *pids; + size_t size; + size_t allocated; +} pids_list_t; + +/* Holds process name and list of pids assigned to that name */ +typedef struct proc_pids_s { + proc_comm_t process_name; + pids_list_t *prev; + pids_list_t *curr; +} proc_pids_t; + +/* + * NAME + * pids_list_free + * + * DESCRIPTION + * Free all elements of given pids list + * + * PARAMETERS + * `list' Head of target pids_list. + */ +void pids_list_free(pids_list_t *list); + +/* + * NAME + * pids_list_add_pid + * + * DESCRIPTION + * Adds pid at the end of the pids array. + * Reallocates memory for new pid element, it is up to user to free it. + * + * PARAMETERS + * `list' Target pids_list. + * `pid' Pid to be added. + * + * RETURN VALUE + * On success, returns 0. + * -1 on memory allocation error. + */ +int pids_list_add_pid(pids_list_t *list, const pid_t pid); + +/* + * NAME + * pids_list_clear + * + * DESCRIPTION + * Remove all pids from the list + * + * PARAMETERS + * `list' Target pids_list. + * + * RETURN VALUE + * On success, return 0 + */ +int pids_list_clear(pids_list_t *list); + +/* + * NAME + * pids_list_add_list + * + * DESCRIPTION + * Adds pids list at the end of the pids list. + * Allocates memory for new pid elements, it is up to user to free it. + * + * PARAMETERS + * `dst' Target PIDs list. + * `src' Source PIDs list. + * + * RETURN VALUE + * On success, returns 0. + * -1 on memory allocation error. + */ +int pids_list_add_list(pids_list_t *dst, pids_list_t *src); + +/* + * NAME + * pids_list_contains_pid + * + * DESCRIPTION + * Tests if pids list contains specific pid. + * + * PARAMETERS + * `list' pids_list to check. + * `pid' Pid to be searched for. + * + * RETURN VALUE + * If PID found in list, returns 1, + * Otherwise returns 0. + */ +int pids_list_contains_pid(pids_list_t *list, const pid_t pid); + +/* + * NAME + * pids_list_diff + * + * DESCRIPTION + * Searches for differences in two given lists + * + * PARAMETERS + * `proc' List of pids + * `added' New pids which appeared + * `removed' Result array storing pids which disappeared + * RETURN VALUE + * 0 on success. Negative number on error. + */ +int pids_list_diff(proc_pids_t *proc, pids_list_t *added, pids_list_t *removed); + +/* + * NAME + * proc_pids_is_name_valid + * + * DESCRIPTION + * Checks if given string is valid process name. + * + * PARAMETERS + * `name' null-terminated char array + * + * RETURN VALUE + * If given name is a valid process name, returns 1, + * Otherwise returns 0. + */ +int proc_pids_is_name_valid(const char *name); + +/* + * NAME + * proc_pids_init + * + * DESCRIPTION + * Helper function to properly initialize array of proc_pids. + * Allocates memory for proc_pids structs. + * + * PARAMETERS + * `procs_names_array' Array of null-terminated strings with + * process' names to be copied to new array + * `procs_names_array_size' procs_names_array element count + * `proc_pids' Address of pointer, under which new + * array of proc_pids will be allocated. + * Must be NULL. + * RETURN VALUE + * 0 on success. Negative number on error: + * -1: allocation error + */ +int proc_pids_init(const char **procs_names_array, + const size_t procs_names_array_size, + proc_pids_t **proc_pids[]); + +/* + * NAME + * proc_pids_update + * + * DESCRIPTION + * Updates PIDs matching processes's names. + * Searches all PID directories in /proc fs and updates current pids_list. + * + * PARAMETERS + * `procfs_path' Path to systems proc directory (e.g. /proc) + * `proc_pids' Array of proc_pids pointers to be updated. + * `proc_pids_num' proc_pids element count + * + * RETURN VALUE + * 0 on success. -1 on error. + */ +int proc_pids_update(const char *procfs_path, proc_pids_t *proc_pids[], + size_t proc_pids_num); + +/* + * NAME + * proc_pids_free + * + * DESCRIPTION + * Releses memory allocatd for proc_pids + * + * PARAMETERS + * `proc_pids' Array of proc_pids + * `proc_pids_num' proc_pids element count + * + * RETURN VALUE + * 0 on success. -1 on error. + */ +int proc_pids_free(proc_pids_t *proc_pids[], size_t proc_pids_num); + +#endif /* UTILS_PROC_PIDS_PROC_PIDS_H */ diff --git a/src/utils/proc_pids/proc_pids_test.c b/src/utils/proc_pids/proc_pids_test.c new file mode 100644 index 00000000..42d17f49 --- /dev/null +++ b/src/utils/proc_pids/proc_pids_test.c @@ -0,0 +1,511 @@ +// clang-format off +/* + * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris + * See Github Issue #3193 for details + */ +#include "utils/proc_pids/proc_pids.c" /* sic */ +#include "testing.h" +// clang-format on +#include + +/*************************************************************************** + * helper functions + */ + +typedef struct stub_proc_pid { + proc_comm_t comm; + pid_t pid; +} stub_proc_pid_t; + +static const char *proc_fs = "/tmp/procfs_stub"; + +/* + * NAME + * stub_procfs_setup + * + * DESCRIPTION + * Prepares testing environment by creating temporary + * PID/comm file structure. + * + * PARAMETERS + * `proc_pids_array' Array of stub_proc_pid_t structs. Represents + * which PIDs should hold given process name. + * `proc_pids_array_length' Element count of input array. + * + * RETURN VALUE + * 0 on success. + * -1 on base dir creation error. + * -2 on comm file creation error. + * -3 on comm file write error. + */ +int stub_procfs_setup(const stub_proc_pid_t *proc_pids_array, + const size_t proc_pids_array_length) { + if (mkdir(proc_fs, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) + return -1; + char path[256]; + + for (size_t i = 0; i < proc_pids_array_length; ++i) { + memset(path, 0, sizeof(path)); + snprintf(path, STATIC_ARRAY_SIZE(path), "%s/%d", proc_fs, + proc_pids_array[i].pid); + mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + strncat(path, "/comm", STATIC_ARRAY_SIZE(path) - strlen(path) - 1); + + FILE *fp = fopen(path, "w"); + if (!fp) + return -2; + + size_t slen = strlen(proc_pids_array[i].comm); + size_t wlen = fwrite(proc_pids_array[i].comm, sizeof(char), slen, fp); + fclose(fp); + + if (slen != wlen) + return -3; + } + return 0; +} + +/* + * NAME + * stub_procfs_teardown + * + * DESCRIPTION + * Clears testing environment: removes stub proc files. + * NOTE - This function could be implemented by usage of nftw, but this + * would require #define _XOPEN_SOURCE 500, which + * messes up intel_rdt includes. + * + * RETURN VALUE + * system command result + */ +int stub_procfs_teardown() { + char cmd[256]; + sstrncpy(cmd, "rm -rf ", STATIC_ARRAY_SIZE(cmd)); + strncat(cmd, proc_fs, STATIC_ARRAY_SIZE(cmd) - strlen(cmd) - 1); + return system(cmd); +} + +/* Max PID value. More info: + * http://web.archive.org/web/20111209081734/http://research.cs.wisc.edu/condor/condorg/linux_scalability.html + */ +#define MAX_PID 4194304 +#define MAX_PID_STR "4194304" + +/*************************************************************************** + * tests + */ +DEF_TEST(proc_pids_init__on_nullptr) { + /* setup */ + const char *procs_names_array[] = {"proc1", "proc2", "proc3"}; + const size_t procs_names_array_size = STATIC_ARRAY_SIZE(procs_names_array); + proc_pids_t **proc_pids_array = NULL; + + /* check */ + int result = proc_pids_init(procs_names_array, procs_names_array_size, + &proc_pids_array); + EXPECT_EQ_INT(0, result); + for (size_t i = 0; i < procs_names_array_size; ++i) + EXPECT_EQ_STR(procs_names_array[i], proc_pids_array[i]->process_name); + + /* cleanup */ + proc_pids_free(proc_pids_array, procs_names_array_size); + return 0; +} + +DEF_TEST(pid_list_add_pid__empty_list) { + /* setup */ + pids_list_t *proc_pids_instance = calloc(1, sizeof(*proc_pids_instance)); + pid_t pid = 1234; + + /* check */ + pids_list_add_pid(proc_pids_instance, pid); + EXPECT_EQ_INT(pid, proc_pids_instance->pids[0]); + + /* cleanup */ + pids_list_free(proc_pids_instance); + return 0; +} + +DEF_TEST(pid_list_add_pid__non_empty_list) { + /* setup */ + pids_list_t *proc_pids_instance = calloc(1, sizeof(*proc_pids_instance)); + pid_t pids[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007}; + + /* check */ + for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids); ++i) + pids_list_add_pid(proc_pids_instance, pids[i]); + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids); ++i) { + EXPECT_EQ_INT(pids[i], proc_pids_instance->pids[i]); + } + + /* cleanup */ + pids_list_free(proc_pids_instance); + return 0; +} + +DEF_TEST(pids_list_add_pids_list__non_empty_lists) { + /* setup */ + pid_t pids_array_1[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007}; + pid_t pids_array_2[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007}; + pids_list_t *pids_list_1 = calloc(1, sizeof(*pids_list_1)); + pids_list_t *pids_list_2 = calloc(1, sizeof(*pids_list_2)); + for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_1); ++i) { + pids_list_add_pid(pids_list_1, pids_array_1[i]); + pids_list_add_pid(pids_list_2, pids_array_2[i]); + } + + /* check */ + int result = pids_list_add_list(pids_list_1, pids_list_2); + EXPECT_EQ_INT(0, result); + EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_2) + + STATIC_ARRAY_SIZE(pids_array_1), + pids_list_1->size); + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_1); ++i) { + EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array_1[i])); + EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array_2[i])); + } + + /* setup */ + pids_list_free(pids_list_1); + pids_list_free(pids_list_2); + return 0; +} + +DEF_TEST(pids_list_add_pids_list__add_to_empty) { + /* setup */ + pid_t pids_array[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007}; + pids_list_t *pids_list_1 = calloc(1, sizeof(*pids_list_1)); + pids_list_t *pids_list_2 = calloc(1, sizeof(*pids_list_2)); + for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array); ++i) + pids_list_add_pid(pids_list_2, pids_array[i]); + + /* check */ + int result = pids_list_add_list(pids_list_1, pids_list_2); + EXPECT_EQ_INT(0, result); + EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array), pids_list_1->size); + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array); ++i) + EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array[i])); + + /* setup */ + pids_list_free(pids_list_1); + pids_list_free(pids_list_2); + return 0; +} + +DEF_TEST(get_pid_number__valid_dir) { + /* setup */ + struct dirent d; + sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name)); + d.d_type = DT_DIR; + pid_t pid = 0; + + /* check */ + int pid_conversion = get_pid_number(&d, &pid); + + EXPECT_EQ_INT(0, pid_conversion); + EXPECT_EQ_INT(MAX_PID, pid); + + /* cleanup */ + return 0; +} + +DEF_TEST(get_pid_number__invalid_dir_name) { + /* setup */ + struct dirent d; + sstrncpy(d.d_name, "invalid", STATIC_ARRAY_SIZE(d.d_name)); + d.d_type = DT_DIR; + pid_t pid = 0; + + /* check */ + int pid_conversion = get_pid_number(&d, &pid); + + EXPECT_EQ_INT(-1, pid_conversion); + EXPECT_EQ_INT(0, pid); + + /* cleanup */ + return 0; +} + +DEF_TEST(read_proc_name__valid_name) { + /* setup */ + stub_proc_pid_t pp_stubs[] = {{"proc1", MAX_PID}}; + stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs)); + struct dirent d; + sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name)); + d.d_type = DT_DIR; + + /* check */ + proc_comm_t comm; + int read_result = read_proc_name(proc_fs, &d, comm, STATIC_ARRAY_SIZE(comm)); + + EXPECT_EQ_INT(strlen(pp_stubs[0].comm), read_result); + EXPECT_EQ_STR(pp_stubs[0].comm, comm); + + /* cleanup */ + stub_procfs_teardown(); + return 0; +} + +DEF_TEST(read_proc_name__invalid_name) { + /* setup */ + struct dirent d; + sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name)); + d.d_type = DT_DIR; + + /* check */ + proc_comm_t comm; + int read_result = read_proc_name(proc_fs, &d, comm, STATIC_ARRAY_SIZE(comm)); + + EXPECT_EQ_INT(-1, read_result); + + /* cleanup */ + return 0; +} + +DEF_TEST(proc_pids_update__one_proc_many_pid) { + /* setup */ + const char *proc_names[] = {"proc1"}; + stub_proc_pid_t pp_stubs[] = {{"proc1", 1007}, + {"proc1", 1008}, + {"proc1", 1009}, + {"proc2", 1010}, + {"proc3", 1011}}; + proc_pids_t **proc_pids = NULL; + int result; + stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs)); + + result = + proc_pids_init(proc_names, STATIC_ARRAY_SIZE(proc_names), &proc_pids); + EXPECT_EQ_INT(0, result); + + /* check */ + result = proc_pids_update(proc_fs, proc_pids, STATIC_ARRAY_SIZE(proc_names)); + EXPECT_EQ_INT(0, result); + + /* proc name check */ + EXPECT_EQ_STR(proc_names[0], proc_pids[0]->process_name); + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(pp_stubs); ++i) { + if (0 == strcmp(pp_stubs[i].comm, proc_names[0])) + /* check if proc struct has correct pids */ + EXPECT_EQ_INT(pids_list_contains_pid(proc_pids[0]->curr, pp_stubs[i].pid), + 1); + else + /* check if proc struct has no incorrect pids */ + EXPECT_EQ_INT(pids_list_contains_pid(proc_pids[0]->curr, pp_stubs[i].pid), + 0); + } + + /* cleanup */ + proc_pids_free(proc_pids, STATIC_ARRAY_SIZE(proc_names)); + stub_procfs_teardown(); + return 0; +} + +DEF_TEST(proc_pids_update__many_proc_many_pid) { + /* setup */ + const char *proc_names[] = {"proc1", "proc2", "proc3"}; + stub_proc_pid_t pp_stubs[] = { + {"proc1", 1007}, {"proc1", 1008}, {"proc1", 1009}, {"proc2", 2007}, + {"proc2", 2008}, {"proc2", 2009}, {"proc3", 3007}, {"proc3", 3008}, + {"proc3", 3009}, {"proc4", 4007}, {"proc4", 4008}, {"proc4", 4009}, + {"proc5", 5007}, {"proc5", 5008}, {"proc5", 5009}}; + proc_pids_t **proc_pids = NULL; + int result; + stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs)); + + result = + proc_pids_init(proc_names, STATIC_ARRAY_SIZE(proc_names), &proc_pids); + EXPECT_EQ_INT(0, result); + + /* check */ + result = proc_pids_update(proc_fs, proc_pids, STATIC_ARRAY_SIZE(proc_names)); + EXPECT_EQ_INT(0, result); + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(proc_names); ++i) { + + /* proc name check */ + EXPECT_EQ_STR(proc_names[i], proc_pids[i]->process_name); + + for (size_t j = 0; j < STATIC_ARRAY_SIZE(pp_stubs); ++j) { + if (0 == strcmp(pp_stubs[j].comm, proc_names[i])) + /* check if proc struct has correct pids */ + EXPECT_EQ_INT( + pids_list_contains_pid(proc_pids[i]->curr, pp_stubs[j].pid), 1); + else + /* check if proc struct has no incorrect pids */ + EXPECT_EQ_INT( + pids_list_contains_pid(proc_pids[i]->curr, pp_stubs[j].pid), 0); + } + } + + /* cleanup */ + proc_pids_free(proc_pids, STATIC_ARRAY_SIZE(proc_names)); + stub_procfs_teardown(); + return 0; +} + +DEF_TEST(pids_list_diff__all_changed) { + /* setup */ + pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007}; + pid_t pids_array_after[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007}; + proc_pids_t proc_pids; + pids_list_t curr; + pids_list_t prev; + + prev.pids = pids_array_before; + prev.size = STATIC_ARRAY_SIZE(pids_array_before); + prev.allocated = prev.size; + curr.pids = pids_array_after; + curr.size = STATIC_ARRAY_SIZE(pids_array_after); + curr.allocated = curr.size; + proc_pids.curr = &curr; + proc_pids.prev = &prev; + + pids_list_t *new_pids = calloc(1, sizeof(*new_pids)); + pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids)); + + /* check */ + int result = pids_list_diff(&proc_pids, new_pids, lost_pids); + EXPECT_EQ_INT(0, result); + EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_before), lost_pids->size); + EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_after), new_pids->size); + + for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_before); ++i) { + EXPECT_EQ_INT(1, pids_list_contains_pid(new_pids, pids_array_after[i])); + EXPECT_EQ_INT(1, pids_list_contains_pid(lost_pids, pids_array_before[i])); + } + + /* cleanup */ + pids_list_free(new_pids); + pids_list_free(lost_pids); + + return 0; +} + +DEF_TEST(pids_list_diff__nothing_changed) { + /* setup */ + pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007}; + proc_pids_t proc_pids; + pids_list_t curr; + pids_list_t prev; + + prev.pids = pids_array_before; + prev.size = STATIC_ARRAY_SIZE(pids_array_before); + prev.allocated = prev.size; + curr.pids = pids_array_before; + curr.size = STATIC_ARRAY_SIZE(pids_array_before); + curr.allocated = curr.size; + proc_pids.curr = &curr; + proc_pids.prev = &prev; + + pids_list_t *new_pids = calloc(1, sizeof(*new_pids)); + pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids)); + + /* check */ + int result = pids_list_diff(&proc_pids, new_pids, lost_pids); + EXPECT_EQ_INT(0, result); + EXPECT_EQ_INT(0, lost_pids->size); + EXPECT_EQ_INT(0, new_pids->size); + + /* cleanup */ + pids_list_free(lost_pids); + pids_list_free(new_pids); + + return 0; +} + +DEF_TEST(pids_list_diff__one_added) { + /* setup */ + pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007}; + pid_t pids_array_after[] = {1000, 1001, 1002, 1003, 1004, + 1005, 1006, 1007, 1008}; + proc_pids_t proc_pids; + pids_list_t curr; + pids_list_t prev; + + prev.pids = pids_array_before; + prev.size = STATIC_ARRAY_SIZE(pids_array_before); + prev.allocated = prev.size; + curr.pids = pids_array_after; + curr.size = STATIC_ARRAY_SIZE(pids_array_after); + curr.allocated = curr.size; + proc_pids.curr = &curr; + proc_pids.prev = &prev; + + pids_list_t *new_pids = calloc(1, sizeof(*new_pids)); + pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids)); + + /* check */ + int result = pids_list_diff(&proc_pids, new_pids, lost_pids); + EXPECT_EQ_INT(0, result); + EXPECT_EQ_INT(0, lost_pids->size); + EXPECT_EQ_INT(1, new_pids->size); + EXPECT_EQ_INT(1008, new_pids->pids[0]); + + /* cleanup */ + pids_list_free(lost_pids); + pids_list_free(new_pids); + + return 0; +} + +DEF_TEST(pids_list_diff__one_removed) { + /* setup */ + pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, + 1005, 1006, 1007, 1008}; + pid_t pids_array_after[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007}; + + proc_pids_t proc_pids; + pids_list_t curr; + pids_list_t prev; + + prev.pids = pids_array_before; + prev.size = STATIC_ARRAY_SIZE(pids_array_before); + prev.allocated = prev.size; + curr.pids = pids_array_after; + curr.size = STATIC_ARRAY_SIZE(pids_array_after); + curr.allocated = curr.size; + proc_pids.curr = &curr; + proc_pids.prev = &prev; + + pids_list_t *new_pids = calloc(1, sizeof(*new_pids)); + pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids)); + + /* check */ + int result = pids_list_diff(&proc_pids, new_pids, lost_pids); + EXPECT_EQ_INT(0, result); + EXPECT_EQ_INT(0, new_pids->size); + EXPECT_EQ_INT(1, lost_pids->size); + EXPECT_EQ_INT(1008, lost_pids->pids[0]); + + /* cleanup */ + pids_list_free(lost_pids); + pids_list_free(new_pids); + + return 0; +} + +int main(void) { + stub_procfs_teardown(); + RUN_TEST(proc_pids_init__on_nullptr); + RUN_TEST(pid_list_add_pid__empty_list); + RUN_TEST(pid_list_add_pid__non_empty_list); + RUN_TEST(pids_list_add_pids_list__non_empty_lists); + RUN_TEST(pids_list_add_pids_list__add_to_empty); + RUN_TEST(get_pid_number__valid_dir); + RUN_TEST(get_pid_number__invalid_dir_name); + RUN_TEST(read_proc_name__valid_name); + RUN_TEST(read_proc_name__invalid_name); + RUN_TEST(proc_pids_update__one_proc_many_pid); + RUN_TEST(proc_pids_update__many_proc_many_pid); + RUN_TEST(pids_list_diff__all_changed); + RUN_TEST(pids_list_diff__nothing_changed); + RUN_TEST(pids_list_diff__one_added); + RUN_TEST(pids_list_diff__one_removed); + stub_procfs_teardown(); + END_TEST; +} diff --git a/src/utils/rrdcreate/rrdcreate.c b/src/utils/rrdcreate/rrdcreate.c new file mode 100644 index 00000000..f543e11c --- /dev/null +++ b/src/utils/rrdcreate/rrdcreate.c @@ -0,0 +1,660 @@ +/** + * collectd - src/utils_rrdcreate.c + * Copyright (C) 2006-2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "utils/common/common.h" +#include "utils/rrdcreate/rrdcreate.h" + +#include +#include + +struct srrd_create_args_s { + char *filename; + unsigned long pdp_step; + time_t last_up; + int argc; + char **argv; +}; +typedef struct srrd_create_args_s srrd_create_args_t; + +struct async_create_file_s; +typedef struct async_create_file_s async_create_file_t; +struct async_create_file_s { + char *filename; + async_create_file_t *next; +}; + +/* + * Private variables + */ +static int rra_timespans[] = {3600, 86400, 604800, 2678400, 31622400}; +static int rra_timespans_num = STATIC_ARRAY_SIZE(rra_timespans); + +static const char *const rra_types[] = {"AVERAGE", "MIN", "MAX"}; +static int rra_types_num = STATIC_ARRAY_SIZE(rra_types); + +#if !defined(HAVE_THREADSAFE_LIBRRD) +static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +static async_create_file_t *async_creation_list; +static pthread_mutex_t async_creation_lock = PTHREAD_MUTEX_INITIALIZER; + +/* + * Private functions + */ +static void rra_free(int rra_num, char **rra_def) /* {{{ */ +{ + for (int i = 0; i < rra_num; i++) { + sfree(rra_def[i]); + } + sfree(rra_def); +} /* }}} void rra_free */ + +static void srrd_create_args_destroy(srrd_create_args_t *args) { + if (args == NULL) + return; + + sfree(args->filename); + if (args->argv != NULL) { + for (int i = 0; i < args->argc; i++) + sfree(args->argv[i]); + sfree(args->argv); + } + sfree(args); +} /* void srrd_create_args_destroy */ + +static srrd_create_args_t *srrd_create_args_create(const char *filename, + unsigned long pdp_step, + time_t last_up, int argc, + const char **argv) { + srrd_create_args_t *args; + + args = calloc(1, sizeof(*args)); + if (args == NULL) { + P_ERROR("srrd_create_args_create: calloc failed."); + return NULL; + } + args->filename = NULL; + args->pdp_step = pdp_step; + args->last_up = last_up; + args->argv = NULL; + + args->filename = strdup(filename); + if (args->filename == NULL) { + P_ERROR("srrd_create_args_create: strdup failed."); + srrd_create_args_destroy(args); + return NULL; + } + + args->argv = calloc(argc + 1, sizeof(*args->argv)); + if (args->argv == NULL) { + P_ERROR("srrd_create_args_create: calloc failed."); + srrd_create_args_destroy(args); + return NULL; + } + + for (args->argc = 0; args->argc < argc; args->argc++) { + args->argv[args->argc] = strdup(argv[args->argc]); + if (args->argv[args->argc] == NULL) { + P_ERROR("srrd_create_args_create: strdup failed."); + srrd_create_args_destroy(args); + return NULL; + } + } + assert(args->argc == argc); + args->argv[args->argc] = NULL; + + return args; +} /* srrd_create_args_t *srrd_create_args_create */ + +/* * * * * * * * * * + * WARNING: Magic * + * * * * * * * * * */ +static int rra_get(char ***ret, const value_list_t *vl, /* {{{ */ + const rrdcreate_config_t *cfg) { + char **rra_def; + int rra_num; + + int *rts; + int rts_num; + + int rra_max; + + int cdp_num; + int cdp_len; + + /* The stepsize we use here: If it is user-set, use it. If not, use the + * interval of the value-list. */ + int ss; + + if (cfg->rrarows <= 0) { + *ret = NULL; + return -1; + } + + if ((cfg->xff < 0) || (cfg->xff >= 1.0)) { + *ret = NULL; + return -1; + } + + if (cfg->stepsize > 0) + ss = cfg->stepsize; + else + ss = (int)CDTIME_T_TO_TIME_T(vl->interval); + if (ss <= 0) { + *ret = NULL; + return -1; + } + + /* Use the configured timespans or fall back to the built-in defaults */ + if (cfg->timespans_num != 0) { + rts = cfg->timespans; + rts_num = cfg->timespans_num; + } else { + rts = rra_timespans; + rts_num = rra_timespans_num; + } + + rra_max = rts_num * rra_types_num; + assert(rra_max > 0); + + if ((rra_def = calloc(rra_max + 1, sizeof(*rra_def))) == NULL) + return -1; + rra_num = 0; + + cdp_len = 0; + for (int i = 0; i < rts_num; i++) { + int span = rts[i]; + + if ((span / ss) < cfg->rrarows) + span = ss * cfg->rrarows; + + if (cdp_len == 0) + cdp_len = 1; + else + cdp_len = (int)floor(((double)span) / ((double)(cfg->rrarows * ss))); + + cdp_num = (int)ceil(((double)span) / ((double)(cdp_len * ss))); + + for (int j = 0; j < rra_types_num; j++) { + char buffer[128]; + int status; + + if (rra_num >= rra_max) + break; + + status = ssnprintf(buffer, sizeof(buffer), "RRA:%s:%.10f:%u:%u", + rra_types[j], cfg->xff, cdp_len, cdp_num); + + if ((status < 0) || ((size_t)status >= sizeof(buffer))) { + P_ERROR("rra_get: Buffer would have been truncated."); + continue; + } + + rra_def[rra_num++] = sstrdup(buffer); + } + } + + if (rra_num <= 0) { + sfree(rra_def); + return 0; + } + + *ret = rra_def; + return rra_num; +} /* }}} int rra_get */ + +static void ds_free(int ds_num, char **ds_def) /* {{{ */ +{ + for (int i = 0; i < ds_num; i++) + if (ds_def[i] != NULL) + free(ds_def[i]); + free(ds_def); +} /* }}} void ds_free */ + +static int ds_get(char ***ret, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + const rrdcreate_config_t *cfg) { + char **ds_def; + size_t ds_num; + + char min[32]; + char max[32]; + char buffer[128]; + + assert(ds->ds_num > 0); + + ds_def = calloc(ds->ds_num, sizeof(*ds_def)); + if (ds_def == NULL) { + P_ERROR("ds_get: calloc failed: %s", STRERRNO); + return -1; + } + + for (ds_num = 0; ds_num < ds->ds_num; ds_num++) { + data_source_t *d = ds->ds + ds_num; + const char *type; + int status; + + ds_def[ds_num] = NULL; + + if (d->type == DS_TYPE_COUNTER) + type = "COUNTER"; + else if (d->type == DS_TYPE_GAUGE) + type = "GAUGE"; + else if (d->type == DS_TYPE_DERIVE) + type = "DERIVE"; + else if (d->type == DS_TYPE_ABSOLUTE) + type = "ABSOLUTE"; + else { + P_ERROR("ds_get: Unknown DS type: %i", d->type); + break; + } + + if (isnan(d->min)) { + sstrncpy(min, "U", sizeof(min)); + } else + ssnprintf(min, sizeof(min), "%f", d->min); + + if (isnan(d->max)) { + sstrncpy(max, "U", sizeof(max)); + } else + ssnprintf(max, sizeof(max), "%f", d->max); + + status = ssnprintf( + buffer, sizeof(buffer), "DS:%s:%s:%i:%s:%s", d->name, type, + (cfg->heartbeat > 0) ? cfg->heartbeat + : (int)CDTIME_T_TO_TIME_T(2 * vl->interval), + min, max); + if ((status < 1) || ((size_t)status >= sizeof(buffer))) + break; + + ds_def[ds_num] = sstrdup(buffer); + } /* for ds_num = 0 .. ds->ds_num */ + + if (ds_num != ds->ds_num) { + ds_free(ds_num, ds_def); + return -1; + } + + if (ds_num == 0) { + sfree(ds_def); + return 0; + } + + *ret = ds_def; + return ds_num; +} /* }}} int ds_get */ + +#if HAVE_THREADSAFE_LIBRRD +static int srrd_create(const char *filename, /* {{{ */ + unsigned long pdp_step, time_t last_up, int argc, + const char **argv) { + int status; + char *filename_copy; + + if ((filename == NULL) || (argv == NULL)) + return -EINVAL; + + /* Some versions of librrd don't have the `const' qualifier for the first + * argument, so we have to copy the pointer here to avoid warnings. It sucks, + * but what else can we do? :( -octo */ + filename_copy = strdup(filename); + if (filename_copy == NULL) { + ERROR("srrd_create: strdup failed."); + return -ENOMEM; + } + + optind = 0; /* bug in librrd? */ + rrd_clear_error(); + + status = rrd_create_r(filename_copy, pdp_step, last_up, argc, (void *)argv); + + if (status != 0) { + P_WARNING("srrd_create: rrd_create_r (%s) failed: %s", filename, + rrd_get_error()); + } + + sfree(filename_copy); + + return status; +} /* }}} int srrd_create */ + /* #endif HAVE_THREADSAFE_LIBRRD */ + +#else /* !HAVE_THREADSAFE_LIBRRD */ +static int srrd_create(const char *filename, /* {{{ */ + unsigned long pdp_step, time_t last_up, int argc, + const char **argv) { + int status; + + int new_argc; + char **new_argv; + + char pdp_step_str[16]; + char last_up_str[16]; + + new_argc = 6 + argc; + new_argv = malloc((new_argc + 1) * sizeof(*new_argv)); + if (new_argv == NULL) { + P_ERROR("srrd_create: malloc failed."); + return -1; + } + + if (last_up == 0) + last_up = time(NULL) - 10; + + ssnprintf(pdp_step_str, sizeof(pdp_step_str), "%lu", pdp_step); + ssnprintf(last_up_str, sizeof(last_up_str), "%lu", (unsigned long)last_up); + + new_argv[0] = "create"; + new_argv[1] = (void *)filename; + new_argv[2] = "-s"; + new_argv[3] = pdp_step_str; + new_argv[4] = "-b"; + new_argv[5] = last_up_str; + + memcpy(new_argv + 6, argv, argc * sizeof(char *)); + new_argv[new_argc] = NULL; + + pthread_mutex_lock(&librrd_lock); + optind = 0; /* bug in librrd? */ + rrd_clear_error(); + + status = rrd_create(new_argc, new_argv); + pthread_mutex_unlock(&librrd_lock); + + if (status != 0) { + P_WARNING("srrd_create: rrd_create (%s) failed: %s", filename, + rrd_get_error()); + } + + sfree(new_argv); + + return status; +} /* }}} int srrd_create */ +#endif /* !HAVE_THREADSAFE_LIBRRD */ + +static int lock_file(char const *filename) /* {{{ */ +{ + async_create_file_t *ptr; + struct stat sb; + int status; + + pthread_mutex_lock(&async_creation_lock); + + for (ptr = async_creation_list; ptr != NULL; ptr = ptr->next) + if (strcmp(filename, ptr->filename) == 0) + break; + + if (ptr != NULL) { + pthread_mutex_unlock(&async_creation_lock); + return EEXIST; + } + + status = stat(filename, &sb); + if ((status == 0) || (errno != ENOENT)) { + pthread_mutex_unlock(&async_creation_lock); + return EEXIST; + } + + ptr = malloc(sizeof(*ptr)); + if (ptr == NULL) { + pthread_mutex_unlock(&async_creation_lock); + return ENOMEM; + } + + ptr->filename = strdup(filename); + if (ptr->filename == NULL) { + pthread_mutex_unlock(&async_creation_lock); + sfree(ptr); + return ENOMEM; + } + + ptr->next = async_creation_list; + async_creation_list = ptr; + + pthread_mutex_unlock(&async_creation_lock); + + return 0; +} /* }}} int lock_file */ + +static int unlock_file(char const *filename) /* {{{ */ +{ + async_create_file_t *this; + async_create_file_t *prev; + + pthread_mutex_lock(&async_creation_lock); + + prev = NULL; + for (this = async_creation_list; this != NULL; this = this->next) { + if (strcmp(filename, this->filename) == 0) + break; + prev = this; + } + + if (this == NULL) { + pthread_mutex_unlock(&async_creation_lock); + return ENOENT; + } + + if (prev == NULL) { + assert(this == async_creation_list); + async_creation_list = this->next; + } else { + assert(this == prev->next); + prev->next = this->next; + } + this->next = NULL; + + pthread_mutex_unlock(&async_creation_lock); + + sfree(this->filename); + sfree(this); + + return 0; +} /* }}} int unlock_file */ + +static void *srrd_create_thread(void *targs) /* {{{ */ +{ + srrd_create_args_t *args = targs; + char tmpfile[PATH_MAX]; + int status; + + status = lock_file(args->filename); + if (status != 0) { + if (status == EEXIST) + P_NOTICE("srrd_create_thread: File \"%s\" is already being created.", + args->filename); + else + P_ERROR("srrd_create_thread: Unable to lock file \"%s\".", + args->filename); + srrd_create_args_destroy(args); + return 0; + } + + ssnprintf(tmpfile, sizeof(tmpfile), "%s.async", args->filename); + + status = srrd_create(tmpfile, args->pdp_step, args->last_up, args->argc, + (void *)args->argv); + if (status != 0) { + 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); + return 0; + } + + status = rename(tmpfile, args->filename); + if (status != 0) { + 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); + return 0; + } + + DEBUG("srrd_create_thread: Successfully created RRD file \"%s\".", + args->filename); + + unlock_file(args->filename); + srrd_create_args_destroy(args); + + return 0; +} /* }}} void *srrd_create_thread */ + +static int srrd_create_async(const char *filename, /* {{{ */ + unsigned long pdp_step, time_t last_up, int argc, + const char **argv) { + srrd_create_args_t *args; + pthread_t thread; + pthread_attr_t attr; + int status; + + DEBUG("srrd_create_async: Creating \"%s\" in the background.", filename); + + args = srrd_create_args_create(filename, pdp_step, last_up, argc, argv); + if (args == NULL) + return -1; + + status = pthread_attr_init(&attr); + if (status != 0) { + srrd_create_args_destroy(args); + return -1; + } + + status = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (status != 0) { + pthread_attr_destroy(&attr); + srrd_create_args_destroy(args); + return -1; + } + + status = pthread_create(&thread, &attr, srrd_create_thread, args); + if (status != 0) { + P_ERROR("srrd_create_async: pthread_create failed: %s", STRERROR(status)); + pthread_attr_destroy(&attr); + srrd_create_args_destroy(args); + return status; + } + + pthread_attr_destroy(&attr); + /* args is freed in srrd_create_thread(). */ + return 0; +} /* }}} int srrd_create_async */ + +/* + * Public functions + */ +int cu_rrd_create_file(const char *filename, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + const rrdcreate_config_t *cfg) { + char **argv; + int argc; + char **rra_def = NULL; + int rra_num; + char **ds_def = NULL; + int ds_num; + int status = 0; + time_t last_up; + unsigned long stepsize; + + if (check_create_dir(filename)) + return -1; + + if ((rra_num = rra_get(&rra_def, vl, cfg)) < 1) { + P_ERROR("cu_rrd_create_file failed: Could not calculate RRAs"); + return -1; + } + + if ((ds_num = ds_get(&ds_def, ds, vl, cfg)) < 1) { + P_ERROR("cu_rrd_create_file failed: Could not calculate DSes"); + rra_free(rra_num, rra_def); + return -1; + } + + argc = ds_num + rra_num; + + if ((argv = malloc(sizeof(*argv) * (argc + 1))) == NULL) { + P_ERROR("cu_rrd_create_file failed: %s", STRERRNO); + rra_free(rra_num, rra_def); + ds_free(ds_num, ds_def); + return -1; + } + + memcpy(argv, ds_def, ds_num * sizeof(char *)); + memcpy(argv + ds_num, rra_def, rra_num * sizeof(char *)); + argv[ds_num + rra_num] = NULL; + + last_up = CDTIME_T_TO_TIME_T(vl->time); + if (last_up <= 0) + last_up = time(NULL); + last_up -= 1; + + if (cfg->stepsize > 0) + stepsize = cfg->stepsize; + else + stepsize = (unsigned long)CDTIME_T_TO_TIME_T(vl->interval); + + if (cfg->async) { + status = srrd_create_async(filename, stepsize, last_up, argc, + (const char **)argv); + if (status != 0) + 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) + P_NOTICE("cu_rrd_create_file: File \"%s\" is already being created.", + filename); + else + 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) { + 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); + } + unlock_file(filename); + } + } + + free(argv); + ds_free(ds_num, ds_def); + rra_free(rra_num, rra_def); + + return status; +} /* }}} int cu_rrd_create_file */ diff --git a/src/utils/rrdcreate/rrdcreate.h b/src/utils/rrdcreate/rrdcreate.h new file mode 100644 index 00000000..b2277e75 --- /dev/null +++ b/src/utils/rrdcreate/rrdcreate.h @@ -0,0 +1,53 @@ +/** + * collectd - src/utils_rrdcreate.h + * Copyright (C) 2008-2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#ifndef UTILS_RRDCREATE_H +#define UTILS_RRDCREATE_H 1 + +#include "plugin.h" + +#include + +struct rrdcreate_config_s { + unsigned long stepsize; + int heartbeat; + int rrarows; + double xff; + + int *timespans; + size_t timespans_num; + + char **consolidation_functions; + size_t consolidation_functions_num; + + bool async; +}; +typedef struct rrdcreate_config_s rrdcreate_config_t; + +int cu_rrd_create_file(const char *filename, const data_set_t *ds, + const value_list_t *vl, const rrdcreate_config_t *cfg); + +#endif /* UTILS_RRDCREATE_H */ diff --git a/src/utils/tail/tail.c b/src/utils/tail/tail.c new file mode 100644 index 00000000..db34a72b --- /dev/null +++ b/src/utils/tail/tail.c @@ -0,0 +1,224 @@ +/** + * collectd - src/utils_tail.c + * Copyright (C) 2007-2008 C-Ware, Inc. + * Copyright (C) 2008 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: + * Luke Heberling + * Florian Forster + * + * Description: + * Encapsulates useful code for plugins which must watch for appends to + * the end of a file. + **/ + +#include "collectd.h" + +#include "utils/common/common.h" +#include "utils/tail/tail.h" + +struct cu_tail_s { + char *file; + FILE *fh; + struct stat stat; +}; + +static int cu_tail_reopen(cu_tail_t *obj) { + int seek_end = 0; + struct stat stat_buf = {0}; + + int status = stat(obj->file, &stat_buf); + if (status != 0) { + P_ERROR("utils_tail: stat (%s) failed: %s", obj->file, STRERRNO); + return -1; + } + + /* The file is already open.. */ + 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) { + P_INFO("utils_tail: File `%s' was truncated.", obj->file); + status = fseek(obj->fh, 0, SEEK_SET); + if (status != 0) { + P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO); + fclose(obj->fh); + obj->fh = NULL; + return -1; + } + } + memcpy(&obj->stat, &stat_buf, sizeof(struct stat)); + return 1; + } + + /* Seek to the end if we re-open the same file again or the file opened + * is the first at all or the first after an error */ + if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino)) + seek_end = 1; + + FILE *fh = fopen(obj->file, "r"); + if (fh == NULL) { + 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) { + P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO); + fclose(fh); + return -1; + } + } + + if (obj->fh != NULL) + fclose(obj->fh); + obj->fh = fh; + memcpy(&obj->stat, &stat_buf, sizeof(struct stat)); + + return 0; +} /* int cu_tail_reopen */ + +cu_tail_t *cu_tail_create(const char *file) { + cu_tail_t *obj; + + obj = calloc(1, sizeof(*obj)); + if (obj == NULL) + return NULL; + + obj->file = strdup(file); + if (obj->file == NULL) { + free(obj); + return NULL; + } + + obj->fh = NULL; + + return obj; +} /* cu_tail_t *cu_tail_create */ + +int cu_tail_destroy(cu_tail_t *obj) { + if (obj->fh != NULL) + fclose(obj->fh); + free(obj->file); + free(obj); + + return 0; +} /* int cu_tail_destroy */ + +int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) { + int status; + + if (buflen < 1) { + ERROR("utils_tail: cu_tail_readline: buflen too small: %i bytes.", buflen); + return -1; + } + + if (obj->fh == NULL) { + status = cu_tail_reopen(obj); + if (status < 0) + return status; + } + assert(obj->fh != NULL); + + /* Try to read from the filehandle. If that succeeds, everything appears to + * be fine and we can return. */ + clearerr(obj->fh); + if (fgets(buf, buflen, obj->fh) != NULL) { + buf[buflen - 1] = '\0'; + return 0; + } + + /* Check if we encountered an error */ + if (ferror(obj->fh) != 0) { + /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */ + fclose(obj->fh); + obj->fh = NULL; + } + /* else: eof -> check if the file was moved away and reopen the new file if + * so.. */ + + status = cu_tail_reopen(obj); + /* error -> return with error */ + if (status < 0) + return status; + /* file end reached and file not reopened -> nothing more to read */ + else if (status > 0) { + buf[0] = 0; + return 0; + } + + /* 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'; + return 0; + } + + if (ferror(obj->fh) != 0) { + WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file, + STRERRNO); + fclose(obj->fh); + obj->fh = NULL; + return -1; + } + + /* EOf, well, apparently the new file is empty.. */ + buf[0] = 0; + return 0; +} /* int cu_tail_readline */ + +int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback, + void *data) { + int status; + + while (42) { + size_t len; + + status = cu_tail_readline(obj, buf, buflen); + if (status != 0) { + ERROR("utils_tail: cu_tail_read: cu_tail_readline " + "failed."); + break; + } + + /* check for EOF */ + if (buf[0] == 0) + break; + + len = strlen(buf); + while (len > 0) { + if (buf[len - 1] != '\n') + break; + buf[len - 1] = '\0'; + len--; + } + + status = callback(data, buf, buflen); + if (status != 0) { + ERROR("utils_tail: cu_tail_read: callback returned " + "status %i.", + status); + break; + } + } + + return status; +} /* int cu_tail_read */ diff --git a/src/utils/tail/tail.h b/src/utils/tail/tail.h new file mode 100644 index 00000000..73a6de21 --- /dev/null +++ b/src/utils/tail/tail.h @@ -0,0 +1,88 @@ +/** + * collectd - src/utils_tail.h + * Copyright (C) 2007-2008 C-Ware, 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. + * + * Author: + * Luke Heberling + * + * DESCRIPTION + * Facilitates reading information that is appended to a file, taking into + * account that the file may be rotated and a new file created under the + * same name. + **/ + +#ifndef UTILS_TAIL_H +#define UTILS_TAIL_H 1 + +struct cu_tail_s; +typedef struct cu_tail_s cu_tail_t; + +typedef int tailfunc_t(void *data, char *buf, int buflen); + +/* + * NAME + * cu_tail_create + * + * DESCRIPTION + * Allocates a new tail object.. + * + * PARAMETERS + * `file' The name of the file to be tailed. + */ +cu_tail_t *cu_tail_create(const char *file); + +/* + * cu_tail_destroy + * + * Takes a tail object returned by `cu_tail_create' and destroys it, freeing + * all internal memory. + * + * Returns 0 when successful and non-zero otherwise. + */ +int cu_tail_destroy(cu_tail_t *obj); + +/* + * cu_tail_readline + * + * Reads from the file until `buflen' characters are read, a newline + * character is read, or an eof condition is encountered. `buf' is + * always null-terminated on successful return and isn't touched when non-zero + * is returned. + * + * You can check if the EOF condition is reached by looking at the buffer: If + * the length of the string stored in the buffer is zero, EOF occurred. + * Otherwise at least the newline character will be in the buffer. + * + * Returns 0 when successful and non-zero otherwise. + */ +int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen); + +/* + * cu_tail_readline + * + * Reads from the file until eof condition or an error is encountered. + * + * Returns 0 when successful and non-zero otherwise. + */ +int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback, + void *data); + +#endif /* UTILS_TAIL_H */ diff --git a/src/utils/taskstats/taskstats.c b/src/utils/taskstats/taskstats.c new file mode 100644 index 00000000..348f93f3 --- /dev/null +++ b/src/utils/taskstats/taskstats.c @@ -0,0 +1,307 @@ +/** + * 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/taskstats.h" + +#include "plugin.h" +#include "utils/common/common.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/taskstats.h b/src/utils/taskstats/taskstats.h new file mode 100644 index 00000000..de07427c --- /dev/null +++ b/src/utils/taskstats/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_cmd_flush.c b/src/utils_cmd_flush.c deleted file mode 100644 index b180a501..00000000 --- a/src/utils_cmd_flush.c +++ /dev/null @@ -1,177 +0,0 @@ -/** - * collectd - src/utils_cmd_flush.c - * Copyright (C) 2008, 2016 Sebastian Harl - * Copyright (C) 2008 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Sebastian "tokkee" Harl - * Florian "octo" Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" -#include "utils_cmd_flush.h" - -cmd_status_t cmd_parse_flush(size_t argc, char **argv, cmd_flush_t *ret_flush, - const cmd_options_t *opts, - cmd_error_handler_t *err) { - - if ((ret_flush == NULL) || (opts == NULL)) { - errno = EINVAL; - cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_flush."); - return CMD_ERROR; - } - - for (size_t i = 0; i < argc; i++) { - char *opt_key; - char *opt_value; - int status; - - opt_key = NULL; - opt_value = NULL; - status = cmd_parse_option(argv[i], &opt_key, &opt_value, err); - if (status != 0) { - if (status == CMD_NO_OPTION) - cmd_error(CMD_PARSE_ERROR, err, "Invalid option string `%s'.", argv[i]); - cmd_destroy_flush(ret_flush); - return CMD_PARSE_ERROR; - } - - if (strcasecmp("plugin", opt_key) == 0) { - strarray_add(&ret_flush->plugins, &ret_flush->plugins_num, opt_value); - } else if (strcasecmp("identifier", opt_key) == 0) { - identifier_t *id = - realloc(ret_flush->identifiers, - (ret_flush->identifiers_num + 1) * sizeof(*id)); - if (id == NULL) { - cmd_error(CMD_ERROR, err, "realloc failed."); - cmd_destroy_flush(ret_flush); - return CMD_ERROR; - } - - ret_flush->identifiers = id; - id = ret_flush->identifiers + ret_flush->identifiers_num; - ret_flush->identifiers_num++; - if (parse_identifier(opt_value, &id->host, &id->plugin, - &id->plugin_instance, &id->type, &id->type_instance, - opts->identifier_default_host) != 0) { - cmd_error(CMD_PARSE_ERROR, err, "Invalid identifier `%s'.", opt_value); - cmd_destroy_flush(ret_flush); - return CMD_PARSE_ERROR; - } - } else if (strcasecmp("timeout", opt_key) == 0) { - char *endptr; - - errno = 0; - endptr = NULL; - ret_flush->timeout = strtod(opt_value, &endptr); - - if ((endptr == opt_value) || (errno != 0) || - (!isfinite(ret_flush->timeout))) { - cmd_error(CMD_PARSE_ERROR, err, - "Invalid value for option `timeout': %s", opt_value); - cmd_destroy_flush(ret_flush); - return CMD_PARSE_ERROR; - } else if (ret_flush->timeout < 0.0) { - ret_flush->timeout = 0.0; - } - } else { - cmd_error(CMD_PARSE_ERROR, err, "Cannot parse option `%s'.", opt_key); - cmd_destroy_flush(ret_flush); - return CMD_PARSE_ERROR; - } - } - - return CMD_OK; -} /* cmd_status_t cmd_parse_flush */ - -cmd_status_t cmd_handle_flush(FILE *fh, char *buffer) { - cmd_error_handler_t err = {cmd_error_fh, fh}; - cmd_t cmd; - - int success = 0; - int error = 0; - int status; - - if ((fh == NULL) || (buffer == NULL)) - return -1; - - DEBUG("utils_cmd_flush: cmd_handle_flush (fh = %p, buffer = %s);", (void *)fh, - buffer); - - if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK) - return status; - if (cmd.type != CMD_FLUSH) { - cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.", - CMD_TO_STRING(cmd.type)); - cmd_destroy(&cmd); - return CMD_UNKNOWN_COMMAND; - } - - for (size_t i = 0; (i == 0) || (i < cmd.cmd.flush.plugins_num); i++) { - char *plugin = NULL; - - if (cmd.cmd.flush.plugins_num != 0) - plugin = cmd.cmd.flush.plugins[i]; - - for (size_t j = 0; (j == 0) || (j < cmd.cmd.flush.identifiers_num); j++) { - char *identifier = NULL; - char buf[1024]; - - if (cmd.cmd.flush.identifiers_num != 0) { - identifier_t *id = cmd.cmd.flush.identifiers + j; - if (format_name(buf, sizeof(buf), id->host, id->plugin, - id->plugin_instance, id->type, - id->type_instance) != 0) { - error++; - continue; - } - identifier = buf; - } - - if (plugin_flush(plugin, DOUBLE_TO_CDTIME_T(cmd.cmd.flush.timeout), - identifier) == 0) - success++; - else - error++; - } - } - - cmd_error(CMD_OK, &err, "Done: %i successful, %i errors", success, error); - - cmd_destroy(&cmd); - return 0; -#undef PRINT_TO_SOCK -} /* cmd_status_t cmd_handle_flush */ - -void cmd_destroy_flush(cmd_flush_t *flush) { - if (flush == NULL) - return; - - strarray_free(flush->plugins, flush->plugins_num); - flush->plugins = NULL; - flush->plugins_num = 0; - - sfree(flush->identifiers); - flush->identifiers_num = 0; -} /* void cmd_destroy_flush */ diff --git a/src/utils_cmd_flush.h b/src/utils_cmd_flush.h deleted file mode 100644 index 129aa85e..00000000 --- a/src/utils_cmd_flush.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * collectd - src/utils_cmd_flush.h - * Copyright (C) 2008, 2016 Sebastian Harl - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Sebastian "tokkee" Harl - **/ - -#ifndef UTILS_CMD_FLUSH_H -#define UTILS_CMD_FLUSH_H 1 - -#include "utils_cmds.h" - -#include - -cmd_status_t cmd_parse_flush(size_t argc, char **argv, cmd_flush_t *ret_flush, - const cmd_options_t *opts, - cmd_error_handler_t *err); - -cmd_status_t cmd_handle_flush(FILE *fh, char *buffer); - -void cmd_destroy_flush(cmd_flush_t *flush); - -#endif /* UTILS_CMD_FLUSH_H */ diff --git a/src/utils_cmd_getthreshold.c b/src/utils_cmd_getthreshold.c deleted file mode 100644 index c1f3f562..00000000 --- a/src/utils_cmd_getthreshold.c +++ /dev/null @@ -1,182 +0,0 @@ -/** - * collectd - src/utils_cmd_getthreshold.c - * Copyright (C) 2008,2009 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#include "utils_avltree.h" -#include "utils_cmd_getthreshold.h" -#include "utils_parse_option.h" /* for `parse_string' */ -#include "utils_threshold.h" - -#define print_to_socket(fh, ...) \ - if (fprintf(fh, __VA_ARGS__) < 0) { \ - WARNING("handle_getthreshold: failed to write to socket #%i: %s", \ - fileno(fh), STRERRNO); \ - return -1; \ - } - -int handle_getthreshold(FILE *fh, char *buffer) { - char *command; - char *identifier; - char *identifier_copy; - - char *host; - char *plugin; - char *plugin_instance; - char *type; - char *type_instance; - - threshold_t threshold; - - int status; - size_t i; - - if ((fh == NULL) || (buffer == NULL)) - return -1; - - DEBUG("utils_cmd_getthreshold: handle_getthreshold (fh = %p, buffer = %s);", - (void *)fh, buffer); - - command = NULL; - status = parse_string(&buffer, &command); - if (status != 0) { - print_to_socket(fh, "-1 Cannot parse command.\n"); - return -1; - } - assert(command != NULL); - - if (strcasecmp("GETTHRESHOLD", command) != 0) { - print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command); - return -1; - } - - identifier = NULL; - status = parse_string(&buffer, &identifier); - if (status != 0) { - print_to_socket(fh, "-1 Cannot parse identifier.\n"); - return -1; - } - assert(identifier != NULL); - - if (*buffer != 0) { - print_to_socket(fh, "-1 Garbage after end of command: %s\n", buffer); - return -1; - } - - /* parse_identifier() modifies its first argument, - * returning pointers into it */ - identifier_copy = sstrdup(identifier); - - status = parse_identifier(identifier_copy, &host, &plugin, &plugin_instance, - &type, &type_instance, - /* default_host = */ NULL); - if (status != 0) { - DEBUG("handle_getthreshold: Cannot parse identifier `%s'.", identifier); - print_to_socket(fh, "-1 Cannot parse identifier `%s'.\n", identifier); - sfree(identifier_copy); - return -1; - } - - value_list_t vl = {.values = NULL}; - sstrncpy(vl.host, host, sizeof(vl.host)); - sstrncpy(vl.plugin, plugin, sizeof(vl.plugin)); - if (plugin_instance != NULL) - sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); - sstrncpy(vl.type, type, sizeof(vl.type)); - if (type_instance != NULL) - sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); - sfree(identifier_copy); - - status = ut_search_threshold(&vl, &threshold); - if (status == ENOENT) { - print_to_socket(fh, "-1 No threshold found for identifier %s\n", - identifier); - return 0; - } else if (status != 0) { - print_to_socket(fh, "-1 Error while looking up threshold: %i\n", status); - return -1; - } - - /* Lets count the number of lines we'll return. */ - i = 0; - if (threshold.host[0] != 0) - i++; - if (threshold.plugin[0] != 0) - i++; - if (threshold.plugin_instance[0] != 0) - i++; - if (threshold.type[0] != 0) - i++; - if (threshold.type_instance[0] != 0) - i++; - if (threshold.data_source[0] != 0) - i++; - if (!isnan(threshold.warning_min)) - i++; - if (!isnan(threshold.warning_max)) - i++; - if (!isnan(threshold.failure_min)) - i++; - if (!isnan(threshold.failure_max)) - i++; - if (threshold.hysteresis > 0.0) - i++; - if (threshold.hits > 1) - i++; - - /* Print the response */ - print_to_socket(fh, "%" PRIsz " Threshold found\n", i); - - if (threshold.host[0] != 0) - print_to_socket(fh, "Host: %s\n", threshold.host); - if (threshold.plugin[0] != 0) - print_to_socket(fh, "Plugin: %s\n", threshold.plugin); - if (threshold.plugin_instance[0] != 0) - print_to_socket(fh, "Plugin Instance: %s\n", threshold.plugin_instance); - if (threshold.type[0] != 0) - print_to_socket(fh, "Type: %s\n", threshold.type); - if (threshold.type_instance[0] != 0) - print_to_socket(fh, "Type Instance: %s\n", threshold.type_instance); - if (threshold.data_source[0] != 0) - print_to_socket(fh, "Data Source: %s\n", threshold.data_source); - if (!isnan(threshold.warning_min)) - print_to_socket(fh, "Warning Min: %g\n", threshold.warning_min); - if (!isnan(threshold.warning_max)) - print_to_socket(fh, "Warning Max: %g\n", threshold.warning_max); - if (!isnan(threshold.failure_min)) - print_to_socket(fh, "Failure Min: %g\n", threshold.failure_min); - if (!isnan(threshold.failure_max)) - print_to_socket(fh, "Failure Max: %g\n", threshold.failure_max); - if (threshold.hysteresis > 0.0) - print_to_socket(fh, "Hysteresis: %g\n", threshold.hysteresis); - if (threshold.hits > 1) - print_to_socket(fh, "Hits: %i\n", threshold.hits); - - return 0; -} /* int handle_getthreshold */ diff --git a/src/utils_cmd_getthreshold.h b/src/utils_cmd_getthreshold.h deleted file mode 100644 index 78700ee2..00000000 --- a/src/utils_cmd_getthreshold.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * collectd - src/utils_cmd_getthreshold.h - * Copyright (C) 2009 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_CMD_GETTHRESHOLD_H -#define UTILS_CMD_GETTHRESHOLD_H 1 - -#include - -int handle_getthreshold(FILE *fh, char *buffer); - -#endif /* UTILS_CMD_GETTHRESHOLD_H */ diff --git a/src/utils_cmd_getval.c b/src/utils_cmd_getval.c deleted file mode 100644 index f747d5b8..00000000 --- a/src/utils_cmd_getval.c +++ /dev/null @@ -1,165 +0,0 @@ -/** - * collectd - src/utils_cmd_getval.c - * Copyright (C) 2008 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#include "utils_cache.h" -#include "utils_cmd_getval.h" -#include "utils_parse_option.h" - -cmd_status_t cmd_parse_getval(size_t argc, char **argv, - cmd_getval_t *ret_getval, - const cmd_options_t *opts, - cmd_error_handler_t *err) { - char *identifier_copy; - int status; - - if ((ret_getval == NULL) || (opts == NULL)) { - errno = EINVAL; - cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_getval."); - return CMD_ERROR; - } - - if (argc != 1) { - if (argc == 0) - cmd_error(CMD_PARSE_ERROR, err, "Missing identifier."); - else - cmd_error(CMD_PARSE_ERROR, err, "Garbage after identifier: `%s'.", - argv[1]); - return CMD_PARSE_ERROR; - } - - /* parse_identifier() modifies its first argument, - * returning pointers into it */ - identifier_copy = sstrdup(argv[0]); - - status = parse_identifier( - argv[0], &ret_getval->identifier.host, &ret_getval->identifier.plugin, - &ret_getval->identifier.plugin_instance, &ret_getval->identifier.type, - &ret_getval->identifier.type_instance, opts->identifier_default_host); - if (status != 0) { - DEBUG("cmd_parse_getval: Cannot parse identifier `%s'.", identifier_copy); - cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.", - identifier_copy); - sfree(identifier_copy); - return CMD_PARSE_ERROR; - } - - ret_getval->raw_identifier = identifier_copy; - return CMD_OK; -} /* cmd_status_t cmd_parse_getval */ - -#define print_to_socket(fh, ...) \ - do { \ - if (fprintf(fh, __VA_ARGS__) < 0) { \ - WARNING("cmd_handle_getval: failed to write to socket #%i: %s", \ - fileno(fh), STRERRNO); \ - return -1; \ - } \ - fflush(fh); \ - } while (0) - -cmd_status_t cmd_handle_getval(FILE *fh, char *buffer) { - cmd_error_handler_t err = {cmd_error_fh, fh}; - cmd_status_t status; - cmd_t cmd; - - gauge_t *values; - size_t values_num; - - const data_set_t *ds; - - if ((fh == NULL) || (buffer == NULL)) - return -1; - - DEBUG("utils_cmd_getval: cmd_handle_getval (fh = %p, buffer = %s);", - (void *)fh, buffer); - - if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK) - return status; - if (cmd.type != CMD_GETVAL) { - cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.", - CMD_TO_STRING(cmd.type)); - cmd_destroy(&cmd); - return CMD_UNKNOWN_COMMAND; - } - - ds = plugin_get_ds(cmd.cmd.getval.identifier.type); - if (ds == NULL) { - DEBUG("cmd_handle_getval: plugin_get_ds (%s) == NULL;", - cmd.cmd.getval.identifier.type); - cmd_error(CMD_ERROR, &err, "Type `%s' is unknown.\n", - cmd.cmd.getval.identifier.type); - cmd_destroy(&cmd); - return -1; - } - - values = NULL; - values_num = 0; - status = - uc_get_rate_by_name(cmd.cmd.getval.raw_identifier, &values, &values_num); - if (status != 0) { - cmd_error(CMD_ERROR, &err, "No such value."); - cmd_destroy(&cmd); - return CMD_ERROR; - } - - if (ds->ds_num != values_num) { - 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); - cmd_destroy(&cmd); - return CMD_ERROR; - } - - 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); - if (isnan(values[i])) { - print_to_socket(fh, "NaN\n"); - } else { - print_to_socket(fh, "%12e\n", values[i]); - } - } - - sfree(values); - cmd_destroy(&cmd); - - return CMD_OK; -} /* cmd_status_t cmd_handle_getval */ - -void cmd_destroy_getval(cmd_getval_t *getval) { - if (getval == NULL) - return; - - sfree(getval->raw_identifier); -} /* void cmd_destroy_getval */ diff --git a/src/utils_cmd_getval.h b/src/utils_cmd_getval.h deleted file mode 100644 index 5c03fa4c..00000000 --- a/src/utils_cmd_getval.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * collectd - src/utils_cmd_getval.h - * Copyright (C) 2008 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_CMD_GETVAL_H -#define UTILS_CMD_GETVAL_H 1 - -#include - -#include "utils_cmds.h" - -cmd_status_t cmd_parse_getval(size_t argc, char **argv, - cmd_getval_t *ret_getval, - const cmd_options_t *opts, - cmd_error_handler_t *err); - -cmd_status_t cmd_handle_getval(FILE *fh, char *buffer); - -void cmd_destroy_getval(cmd_getval_t *getval); - -#endif /* UTILS_CMD_GETVAL_H */ diff --git a/src/utils_cmd_listval.c b/src/utils_cmd_listval.c deleted file mode 100644 index 24ec9421..00000000 --- a/src/utils_cmd_listval.c +++ /dev/null @@ -1,103 +0,0 @@ -/** - * collectd - src/utils_cmd_listval.c - * Copyright (C) 2008 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#include "utils_cache.h" -#include "utils_cmd_listval.h" -#include "utils_parse_option.h" - -cmd_status_t cmd_parse_listval(size_t argc, char **argv, - const cmd_options_t *opts - __attribute__((unused)), - cmd_error_handler_t *err) { - if (argc != 0) { - cmd_error(CMD_PARSE_ERROR, err, "Garbage after end of command: `%s'.", - argv[0]); - return CMD_PARSE_ERROR; - } - - return CMD_OK; -} /* cmd_status_t cmd_parse_listval */ - -#define free_everything_and_return(status) \ - do { \ - for (size_t j = 0; j < number; j++) { \ - sfree(names[j]); \ - names[j] = NULL; \ - } \ - sfree(names); \ - sfree(times); \ - return status; \ - } while (0) - -#define print_to_socket(fh, ...) \ - do { \ - if (fprintf(fh, __VA_ARGS__) < 0) { \ - WARNING("handle_listval: failed to write to socket #%i: %s", fileno(fh), \ - STRERRNO); \ - free_everything_and_return(CMD_ERROR); \ - } \ - fflush(fh); \ - } while (0) - -cmd_status_t cmd_handle_listval(FILE *fh, char *buffer) { - cmd_error_handler_t err = {cmd_error_fh, fh}; - cmd_status_t status; - cmd_t cmd; - - char **names = NULL; - cdtime_t *times = NULL; - size_t number = 0; - - DEBUG("utils_cmd_listval: handle_listval (fh = %p, buffer = %s);", (void *)fh, - buffer); - - if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK) - return status; - if (cmd.type != CMD_LISTVAL) { - cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.", - CMD_TO_STRING(cmd.type)); - free_everything_and_return(CMD_UNKNOWN_COMMAND); - } - - status = uc_get_names(&names, ×, &number); - if (status != 0) { - DEBUG("command listval: uc_get_names failed with status %i", status); - cmd_error(CMD_ERROR, &err, "uc_get_names failed."); - free_everything_and_return(CMD_ERROR); - } - - print_to_socket(fh, "%i Value%s found\n", (int)number, - (number == 1) ? "" : "s"); - for (size_t i = 0; i < number; i++) - print_to_socket(fh, "%.3f %s\n", CDTIME_T_TO_DOUBLE(times[i]), names[i]); - - free_everything_and_return(CMD_OK); -} /* cmd_status_t cmd_handle_listval */ diff --git a/src/utils_cmd_listval.h b/src/utils_cmd_listval.h deleted file mode 100644 index 6dbaabcb..00000000 --- a/src/utils_cmd_listval.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * collectd - src/utils_cmd_listval.h - * Copyright (C) 2008 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_CMD_LISTVAL_H -#define UTILS_CMD_LISTVAL_H 1 - -#include - -#include "utils_cmds.h" - -cmd_status_t cmd_parse_listval(size_t argc, char **argv, - const cmd_options_t *opts, - cmd_error_handler_t *err); - -cmd_status_t cmd_handle_listval(FILE *fh, char *buffer); - -#endif /* UTILS_CMD_LISTVAL_H */ diff --git a/src/utils_cmd_putnotif.c b/src/utils_cmd_putnotif.c deleted file mode 100644 index 75a8fae8..00000000 --- a/src/utils_cmd_putnotif.c +++ /dev/null @@ -1,181 +0,0 @@ -/** - * collectd - src/utils_cmd_putnotif.c - * Copyright (C) 2008 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#include "utils_cmd_putnotif.h" -#include "utils_parse_option.h" - -#define print_to_socket(fh, ...) \ - do { \ - if (fprintf(fh, __VA_ARGS__) < 0) { \ - WARNING("handle_putnotif: failed to write to socket #%i: %s", \ - fileno(fh), STRERRNO); \ - return -1; \ - } \ - fflush(fh); \ - } while (0) - -static int set_option_severity(notification_t *n, const char *value) { - if (strcasecmp(value, "Failure") == 0) - n->severity = NOTIF_FAILURE; - else if (strcasecmp(value, "Warning") == 0) - n->severity = NOTIF_WARNING; - else if (strcasecmp(value, "Okay") == 0) - n->severity = NOTIF_OKAY; - else - return -1; - - return 0; -} /* int set_option_severity */ - -static int set_option_time(notification_t *n, const char *value) { - char *endptr = NULL; - double tmp; - - errno = 0; - tmp = strtod(value, &endptr); - if ((errno != 0) /* Overflow */ - || (endptr == value) /* Invalid string */ - || (endptr == NULL) /* This should not happen */ - || (*endptr != 0)) /* Trailing chars */ - return -1; - - n->time = DOUBLE_TO_CDTIME_T(tmp); - - return 0; -} /* int set_option_time */ - -static int set_option(notification_t *n, const char *option, - const char *value) { - if ((n == NULL) || (option == NULL) || (value == NULL)) - return -1; - - DEBUG("utils_cmd_putnotif: set_option (option = %s, value = %s);", option, - value); - - /* Add a meta option in the form: : */ - if (option[0] != '\0' && option[1] == ':') { - /* Refuse empty key */ - if (option[2] == '\0') - return 1; - - if (option[0] == 's') - return plugin_notification_meta_add_string(n, option + 2, value); - else - return 1; - } - - if (strcasecmp("severity", option) == 0) - return set_option_severity(n, value); - else if (strcasecmp("time", option) == 0) - return set_option_time(n, value); - else if (strcasecmp("message", option) == 0) - sstrncpy(n->message, value, sizeof(n->message)); - else if (strcasecmp("host", option) == 0) - sstrncpy(n->host, value, sizeof(n->host)); - else if (strcasecmp("plugin", option) == 0) - sstrncpy(n->plugin, value, sizeof(n->plugin)); - else if (strcasecmp("plugin_instance", option) == 0) - sstrncpy(n->plugin_instance, value, sizeof(n->plugin_instance)); - else if (strcasecmp("type", option) == 0) - sstrncpy(n->type, value, sizeof(n->type)); - else if (strcasecmp("type_instance", option) == 0) - sstrncpy(n->type_instance, value, sizeof(n->type_instance)); - else - return 1; - - return 0; -} /* int set_option */ - -int handle_putnotif(FILE *fh, char *buffer) { - char *command; - notification_t n = {0}; - int status; - - if ((fh == NULL) || (buffer == NULL)) - return -1; - - DEBUG("utils_cmd_putnotif: handle_putnotif (fh = %p, buffer = %s);", - (void *)fh, buffer); - - command = NULL; - status = parse_string(&buffer, &command); - if (status != 0) { - print_to_socket(fh, "-1 Cannot parse command.\n"); - return -1; - } - assert(command != NULL); - - if (strcasecmp("PUTNOTIF", command) != 0) { - print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command); - return -1; - } - - status = 0; - while (*buffer != 0) { - char *key; - char *value; - - status = parse_option(&buffer, &key, &value); - if (status != 0) { - print_to_socket(fh, "-1 Malformed option.\n"); - break; - } - - status = set_option(&n, key, value); - if (status != 0) { - print_to_socket(fh, "-1 Error parsing option `%s'\n", key); - break; - } - } /* for (i) */ - - /* Check for required fields and complain if anything is missing. */ - if ((status == 0) && (n.severity == 0)) { - print_to_socket(fh, "-1 Option `severity' missing.\n"); - status = -1; - } - if ((status == 0) && (n.time == 0)) { - print_to_socket(fh, "-1 Option `time' missing.\n"); - status = -1; - } - if ((status == 0) && (strlen(n.message) == 0)) { - print_to_socket(fh, "-1 No message or message of length 0 given.\n"); - status = -1; - } - - /* If status is still zero the notification is fine and we can finally - * dispatch it. */ - if (status == 0) { - plugin_dispatch_notification(&n); - print_to_socket(fh, "0 Success\n"); - } - - return 0; -} /* int handle_putnotif */ diff --git a/src/utils_cmd_putnotif.h b/src/utils_cmd_putnotif.h deleted file mode 100644 index 7ad0f1a5..00000000 --- a/src/utils_cmd_putnotif.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * collectd - src/utils_cmd_putnotif.h - * Copyright (C) 2008 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_CMD_PUTNOTIF_H -#define UTILS_CMD_PUTNOTIF_H 1 - -#include - -int handle_putnotif(FILE *fh, char *buffer); - -#endif /* UTILS_CMD_PUTNOTIF_H */ diff --git a/src/utils_cmd_putval.c b/src/utils_cmd_putval.c deleted file mode 100644 index b5b9065b..00000000 --- a/src/utils_cmd_putval.c +++ /dev/null @@ -1,285 +0,0 @@ -/** - * collectd - src/utils_cmd_putval.c - * Copyright (C) 2007-2009 Florian octo Forster - * Copyright (C) 2016 Sebastian tokkee Harl - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - * Sebastian tokkee Harl - **/ - -#include "collectd.h" - -#include "common.h" -#include "utils_cmd_putval.h" - -/* - * private helper functions - */ - -static int set_option(value_list_t *vl, const char *key, const char *value) { - if ((vl == NULL) || (key == NULL) || (value == NULL)) - return -1; - - if (strcasecmp("interval", key) == 0) { - double tmp; - char *endptr; - - endptr = NULL; - errno = 0; - tmp = strtod(value, &endptr); - - if ((errno == 0) && (endptr != NULL) && (endptr != value) && (tmp > 0.0)) - vl->interval = DOUBLE_TO_CDTIME_T(tmp); - } else - return 1; - - return 0; -} /* int set_option */ - -/* - * public API - */ - -cmd_status_t cmd_parse_putval(size_t argc, char **argv, - cmd_putval_t *ret_putval, - const cmd_options_t *opts, - cmd_error_handler_t *err) { - cmd_status_t result; - - char *identifier; - char *hostname; - char *plugin; - char *plugin_instance; - char *type; - char *type_instance; - int status; - - char *identifier_copy; - - const data_set_t *ds; - value_list_t vl = VALUE_LIST_INIT; - - if ((ret_putval == NULL) || (opts == NULL)) { - errno = EINVAL; - cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_putval."); - return CMD_ERROR; - } - - if (argc < 2) { - cmd_error(CMD_PARSE_ERROR, err, "Missing identifier and/or value-list."); - return CMD_PARSE_ERROR; - } - - identifier = argv[0]; - - /* parse_identifier() modifies its first argument, returning pointers into - * it; retain the old value for later. */ - identifier_copy = sstrdup(identifier); - - status = - parse_identifier(identifier, &hostname, &plugin, &plugin_instance, &type, - &type_instance, opts->identifier_default_host); - if (status != 0) { - DEBUG("cmd_handle_putval: Cannot parse identifier `%s'.", identifier_copy); - cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.", - identifier_copy); - sfree(identifier_copy); - return CMD_PARSE_ERROR; - } - - if ((strlen(hostname) >= sizeof(vl.host)) || - (strlen(plugin) >= sizeof(vl.plugin)) || - ((plugin_instance != NULL) && - (strlen(plugin_instance) >= sizeof(vl.plugin_instance))) || - ((type_instance != NULL) && - (strlen(type_instance) >= sizeof(vl.type_instance)))) { - cmd_error(CMD_PARSE_ERROR, err, "Identifier too long."); - sfree(identifier_copy); - return CMD_PARSE_ERROR; - } - - sstrncpy(vl.host, hostname, sizeof(vl.host)); - sstrncpy(vl.plugin, plugin, sizeof(vl.plugin)); - sstrncpy(vl.type, type, sizeof(vl.type)); - if (plugin_instance != NULL) - sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); - if (type_instance != NULL) - sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); - - ds = plugin_get_ds(type); - if (ds == NULL) { - cmd_error(CMD_PARSE_ERROR, err, "1 Type `%s' isn't defined.", type); - sfree(identifier_copy); - return CMD_PARSE_ERROR; - } - - hostname = NULL; - plugin = NULL; - plugin_instance = NULL; - type = NULL; - type_instance = NULL; - - ret_putval->raw_identifier = identifier_copy; - if (ret_putval->raw_identifier == NULL) { - cmd_error(CMD_ERROR, err, "malloc failed."); - cmd_destroy_putval(ret_putval); - sfree(vl.values); - return CMD_ERROR; - } - - /* All the remaining fields are part of the option list. */ - result = CMD_OK; - for (size_t i = 1; i < argc; ++i) { - value_list_t *tmp; - - char *key = NULL; - char *value = NULL; - - status = cmd_parse_option(argv[i], &key, &value, err); - if (status == CMD_OK) { - assert(key != NULL); - assert(value != NULL); - set_option(&vl, key, value); - continue; - } else if (status != CMD_NO_OPTION) { - /* parse_option failed, buffer has been modified. - * => we need to abort */ - result = status; - break; - } - /* else: cmd_parse_option did not find an option; treat this as a - * value list. */ - - vl.values_len = ds->ds_num; - vl.values = calloc(vl.values_len, sizeof(*vl.values)); - if (vl.values == NULL) { - cmd_error(CMD_ERROR, err, "malloc failed."); - result = CMD_ERROR; - break; - } - - status = parse_values(argv[i], &vl, ds); - if (status != 0) { - cmd_error(CMD_PARSE_ERROR, err, "Parsing the values string failed."); - result = CMD_PARSE_ERROR; - vl.values_len = 0; - sfree(vl.values); - break; - } - - tmp = realloc(ret_putval->vl, - (ret_putval->vl_num + 1) * sizeof(*ret_putval->vl)); - if (tmp == NULL) { - cmd_error(CMD_ERROR, err, "realloc failed."); - cmd_destroy_putval(ret_putval); - result = CMD_ERROR; - vl.values_len = 0; - sfree(vl.values); - break; - } - - ret_putval->vl = tmp; - ret_putval->vl_num++; - memcpy(&ret_putval->vl[ret_putval->vl_num - 1], &vl, sizeof(vl)); - - /* pointer is now owned by ret_putval->vl[] */ - vl.values_len = 0; - vl.values = NULL; - } /* while (*buffer != 0) */ - /* Done parsing the options. */ - - if (result != CMD_OK) - cmd_destroy_putval(ret_putval); - - return result; -} /* cmd_status_t cmd_parse_putval */ - -void cmd_destroy_putval(cmd_putval_t *putval) { - if (putval == NULL) - return; - - sfree(putval->raw_identifier); - - for (size_t i = 0; i < putval->vl_num; ++i) { - sfree(putval->vl[i].values); - meta_data_destroy(putval->vl[i].meta); - putval->vl[i].meta = NULL; - } - sfree(putval->vl); - putval->vl = NULL; - putval->vl_num = 0; -} /* void cmd_destroy_putval */ - -cmd_status_t cmd_handle_putval(FILE *fh, char *buffer) { - cmd_error_handler_t err = {cmd_error_fh, fh}; - cmd_t cmd; - - int status; - - DEBUG("utils_cmd_putval: cmd_handle_putval (fh = %p, buffer = %s);", - (void *)fh, buffer); - - if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK) - return status; - if (cmd.type != CMD_PUTVAL) { - cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.", - CMD_TO_STRING(cmd.type)); - cmd_destroy(&cmd); - return CMD_UNKNOWN_COMMAND; - } - - for (size_t i = 0; i < cmd.cmd.putval.vl_num; ++i) - plugin_dispatch_values(&cmd.cmd.putval.vl[i]); - - if (fh != stdout) - cmd_error(CMD_OK, &err, "Success: %i %s been dispatched.", - (int)cmd.cmd.putval.vl_num, - (cmd.cmd.putval.vl_num == 1) ? "value has" : "values have"); - - cmd_destroy(&cmd); - return CMD_OK; -} /* int cmd_handle_putval */ - -int cmd_create_putval(char *ret, size_t ret_len, /* {{{ */ - const data_set_t *ds, const value_list_t *vl) { - char buffer_ident[6 * DATA_MAX_NAME_LEN]; - char buffer_values[1024]; - int status; - - status = FORMAT_VL(buffer_ident, sizeof(buffer_ident), vl); - if (status != 0) - return status; - escape_string(buffer_ident, sizeof(buffer_ident)); - - status = format_values(buffer_values, sizeof(buffer_values), ds, vl, - /* store rates = */ false); - if (status != 0) - return status; - escape_string(buffer_values, sizeof(buffer_values)); - - snprintf(ret, ret_len, "PUTVAL %s interval=%.3f %s", buffer_ident, - (vl->interval > 0) ? CDTIME_T_TO_DOUBLE(vl->interval) - : CDTIME_T_TO_DOUBLE(plugin_get_interval()), - buffer_values); - - return 0; -} /* }}} int cmd_create_putval */ diff --git a/src/utils_cmd_putval.h b/src/utils_cmd_putval.h deleted file mode 100644 index bc6e193e..00000000 --- a/src/utils_cmd_putval.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * collectd - src/utils_cmd_putval.h - * Copyright (C) 2007 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_CMD_PUTVAL_H -#define UTILS_CMD_PUTVAL_H 1 - -#include "plugin.h" -#include "utils_cmds.h" - -#include - -cmd_status_t cmd_parse_putval(size_t argc, char **argv, - cmd_putval_t *ret_putval, - const cmd_options_t *opts, - cmd_error_handler_t *err); - -cmd_status_t cmd_handle_putval(FILE *fh, char *buffer); - -void cmd_destroy_putval(cmd_putval_t *putval); - -int cmd_create_putval(char *ret, size_t ret_len, const data_set_t *ds, - const value_list_t *vl); - -#endif /* UTILS_CMD_PUTVAL_H */ diff --git a/src/utils_cmds.c b/src/utils_cmds.c deleted file mode 100644 index 88fdfc7f..00000000 --- a/src/utils_cmds.c +++ /dev/null @@ -1,308 +0,0 @@ -/** - * collectd - src/utils_cmds.c - * Copyright (C) 2008 Florian Forster - * Copyright (C) 2016 Sebastian 'tokkee' Harl - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - * Sebastian 'tokkee' Harl - **/ - -#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 -#include - -static cmd_options_t default_options = { - /* identifier_default_host = */ NULL, -}; - -/* - * private helper functions - */ - -static cmd_status_t cmd_split(char *buffer, size_t *ret_len, char ***ret_fields, - cmd_error_handler_t *err) { - char *field; - bool in_field, in_quotes; - - size_t estimate, len; - char **fields; - - estimate = 0; - in_field = false; - for (char *string = buffer; *string != '\0'; ++string) { - /* Make a quick worst-case estimate of the number of fields by - * counting spaces and ignoring quotation marks. */ - if (!isspace((int)*string)) { - if (!in_field) { - estimate++; - in_field = true; - } - } else { - in_field = false; - } - } - - /* fields will be NULL-terminated */ - fields = malloc((estimate + 1) * sizeof(*fields)); - if (fields == NULL) { - cmd_error(CMD_ERROR, err, "malloc failed."); - return CMD_ERROR; - } - -#define END_FIELD() \ - do { \ - *field = '\0'; \ - field = NULL; \ - in_field = false; \ - } while (0) -#define NEW_FIELD() \ - do { \ - field = string; \ - in_field = true; \ - assert(len < estimate); \ - fields[len] = field; \ - field++; \ - len++; \ - } while (0) - - len = 0; - field = NULL; - in_field = false; - in_quotes = false; - for (char *string = buffer; *string != '\0'; string++) { - if (isspace((int)string[0])) { - if (!in_quotes) { - if (in_field) - END_FIELD(); - - /* skip space */ - continue; - } - } else if (string[0] == '"') { - /* Note: Two consecutive quoted fields not separated by space are - * treated as different fields. This is the collectd 5.x behavior - * around splitting fields. */ - - if (in_quotes) { - /* end of quoted field */ - if (!in_field) /* empty quoted string */ - NEW_FIELD(); - END_FIELD(); - in_quotes = false; - continue; - } - - in_quotes = true; - /* if (! in_field): add new field on next iteration - * else: quoted string following an unquoted string (one field) - * in either case: skip quotation mark */ - continue; - } else if ((string[0] == '\\') && in_quotes) { - /* Outside of quotes, a backslash is a regular character (mostly - * for backward compatibility). */ - - if (string[1] == '\0') { - free(fields); - cmd_error(CMD_PARSE_ERROR, err, "Backslash at end of string."); - return CMD_PARSE_ERROR; - } - - /* un-escape the next character; skip backslash */ - string++; - } - - if (!in_field) - NEW_FIELD(); - else { - *field = string[0]; - field++; - } - } - - if (in_quotes) { - free(fields); - cmd_error(CMD_PARSE_ERROR, err, "Unterminated quoted string."); - return CMD_PARSE_ERROR; - } - -#undef NEW_FIELD -#undef END_FIELD - - fields[len] = NULL; - if (ret_len != NULL) - *ret_len = len; - if (ret_fields != NULL) - *ret_fields = fields; - else - free(fields); - return CMD_OK; -} /* int cmd_split */ - -/* - * public API - */ - -void cmd_error(cmd_status_t status, cmd_error_handler_t *err, - const char *format, ...) { - va_list ap; - - if ((err == NULL) || (err->cb == NULL)) - return; - - va_start(ap, format); - err->cb(err->ud, status, format, ap); - va_end(ap); -} /* void cmd_error */ - -cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd, - const cmd_options_t *opts, cmd_error_handler_t *err) { - char *command = NULL; - cmd_status_t status; - - if ((argc < 1) || (argv == NULL) || (ret_cmd == NULL)) { - errno = EINVAL; - cmd_error(CMD_ERROR, err, "Missing command."); - return CMD_ERROR; - } - - if (opts == NULL) - opts = &default_options; - - memset(ret_cmd, 0, sizeof(*ret_cmd)); - command = argv[0]; - if (strcasecmp("FLUSH", command) == 0) { - ret_cmd->type = CMD_FLUSH; - status = - cmd_parse_flush(argc - 1, argv + 1, &ret_cmd->cmd.flush, opts, err); - } else if (strcasecmp("GETVAL", command) == 0) { - ret_cmd->type = CMD_GETVAL; - status = - 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, opts, err); - } else if (strcasecmp("PUTVAL", command) == 0) { - ret_cmd->type = CMD_PUTVAL; - status = - cmd_parse_putval(argc - 1, argv + 1, &ret_cmd->cmd.putval, opts, err); - } else { - ret_cmd->type = CMD_UNKNOWN; - cmd_error(CMD_UNKNOWN_COMMAND, err, "Unknown command `%s'.", command); - return CMD_UNKNOWN_COMMAND; - } - - if (status != CMD_OK) - ret_cmd->type = CMD_UNKNOWN; - return status; -} /* cmd_status_t cmd_parsev */ - -cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts, - cmd_error_handler_t *err) { - char **fields = NULL; - size_t fields_num = 0; - cmd_status_t status; - - if ((status = cmd_split(buffer, &fields_num, &fields, err)) != CMD_OK) - return status; - - status = cmd_parsev(fields_num, fields, ret_cmd, opts, err); - free(fields); - return status; -} /* cmd_status_t cmd_parse */ - -void cmd_destroy(cmd_t *cmd) { - if (cmd == NULL) - return; - - switch (cmd->type) { - case CMD_UNKNOWN: - /* nothing to do */ - break; - case CMD_FLUSH: - cmd_destroy_flush(&cmd->cmd.flush); - break; - case CMD_GETVAL: - cmd_destroy_getval(&cmd->cmd.getval); - break; - case CMD_LISTVAL: - break; - case CMD_PUTVAL: - cmd_destroy_putval(&cmd->cmd.putval); - break; - } -} /* void cmd_destroy */ - -cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value, - cmd_error_handler_t *err) { - char *key, *value; - - if (field == NULL) { - errno = EINVAL; - cmd_error(CMD_ERROR, err, "Invalid argument to cmd_parse_option."); - return CMD_ERROR; - } - key = value = field; - - /* Look for the equal sign. */ - while (isalnum((int)value[0]) || (value[0] == '_') || (value[0] == ':')) - value++; - if ((value[0] != '=') || (value == key)) { - /* Whether this is a fatal error is up to the caller. */ - return CMD_NO_OPTION; - } - *value = '\0'; - value++; - - if (ret_key != NULL) - *ret_key = key; - if (ret_value != NULL) - *ret_value = value; - - return CMD_OK; -} /* cmd_status_t cmd_parse_option */ - -void cmd_error_fh(void *ud, cmd_status_t status, const char *format, - va_list ap) { - FILE *fh = ud; - int code = -1; - char buf[1024]; - - if (status == CMD_OK) - code = 0; - - vsnprintf(buf, sizeof(buf), format, ap); - buf[sizeof(buf) - 1] = '\0'; - if (fprintf(fh, "%i %s\n", code, buf) < 0) { - WARNING("utils_cmds: failed to write to file-handle #%i: %s", fileno(fh), - STRERRNO); - return; - } - - fflush(fh); -} /* void cmd_error_fh */ diff --git a/src/utils_cmds.h b/src/utils_cmds.h deleted file mode 100644 index f3882f54..00000000 --- a/src/utils_cmds.h +++ /dev/null @@ -1,209 +0,0 @@ -/** - * collectd - src/utils_cmds.h - * Copyright (C) 2016 Sebastian 'tokkee' Harl - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Sebastian 'tokkee' Harl - **/ - -#ifndef UTILS_CMDS_H -#define UTILS_CMDS_H 1 - -#include "plugin.h" - -#include - -typedef enum { - CMD_UNKNOWN = 0, - CMD_FLUSH = 1, - CMD_GETVAL = 2, - CMD_LISTVAL = 3, - 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" - -typedef struct { - double timeout; - - char **plugins; - size_t plugins_num; - identifier_t *identifiers; - size_t identifiers_num; -} cmd_flush_t; - -typedef struct { - char *raw_identifier; - identifier_t identifier; -} cmd_getval_t; - -typedef struct { - /* The raw identifier as provided by the user. */ - char *raw_identifier; - - /* An array of the fully parsed identifier and all value lists, and their - * options as provided by the user. */ - value_list_t *vl; - size_t vl_num; -} cmd_putval_t; - -/* - * NAME - * cmd_t - * - * DESCRIPTION - * The representation of a fully parsed command. - */ -typedef struct { - cmd_type_t type; - union { - cmd_flush_t flush; - cmd_getval_t getval; - cmd_putval_t putval; - } cmd; -} cmd_t; - -/* - * NAME - * cmd_options_t - * - * DESCRIPTIONS - * Optional settings for tuning the parser behavior. - */ -typedef struct { - /* identifier_default_host: If non-NULL, the hostname is optional and will - * default to the specified value. */ - char *identifier_default_host; -} cmd_options_t; - -/* - * NAME - * cmd_status_t - * - * DESCRIPTION - * Status codes describing the parse result. - */ -typedef enum { - CMD_OK = 0, - CMD_ERROR = -1, - CMD_PARSE_ERROR = -2, - CMD_UNKNOWN_COMMAND = -3, - - /* Not necessarily fatal errors. */ - CMD_NO_OPTION = 1, -} cmd_status_t; - -/* - * NAME - * cmd_error_handler_t - * - * DESCRIPTION - * An error handler describes a callback to be invoked when the parser - * encounters an error. The user data pointer will be passed to the callback - * as the first argument. - */ -typedef struct { - void (*cb)(void *, cmd_status_t, const char *, va_list); - void *ud; -} cmd_error_handler_t; - -/* - * NAME: - * cmd_error - * - * DESCRIPTION - * Reports an error via the specified error handler (if set). - */ -void cmd_error(cmd_status_t status, cmd_error_handler_t *err, - const char *format, ...); - -/* - * NAME - * cmd_parse - * - * DESCRIPTION - * Parse a command string and populate a command object. - * - * PARAMETERS - * `buffer' The command string to be parsed. - * `ret_cmd' The parse result will be stored at this location. - * `opts' Parser options. If NULL, defaults will be used. - * `err' An optional error handler to invoke on error. - * - * RETURN VALUE - * CMD_OK on success or the respective error code otherwise. - */ -cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts, - cmd_error_handler_t *err); - -cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd, - const cmd_options_t *opts, cmd_error_handler_t *err); - -void cmd_destroy(cmd_t *cmd); - -/* - * NAME - * cmd_parse_option - * - * DESCRIPTION - * Parses a command option which must be of the form: - * name=value with \ and spaces - * - * PARAMETERS - * `field' The parsed input field with any quotes removed and special - * characters unescaped. - * `ret_key' The parsed key will be stored at this location. - * `ret_value' The parsed value will be stored at this location. - * - * RETURN VALUE - * CMD_OK on success or an error code otherwise. - * CMD_NO_OPTION if `field' does not represent an option at all (missing - * equal sign). - */ -cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value, - cmd_error_handler_t *err); - -/* - * NAME - * cmd_error_fh - * - * DESCRIPTION - * An error callback writing the message to an open file handle using the - * format expected by the unixsock or exec plugins. - * - * PARAMETERS - * `ud' Error handler user-data pointer. This must be an open - * file-handle (FILE *). - * `status' The error status code. - * `format' Printf-style format string. - * `ap' Variable argument list providing the arguments for the format - * string. - */ -void cmd_error_fh(void *ud, cmd_status_t status, const char *format, - va_list ap); - -#endif /* UTILS_CMDS_H */ diff --git a/src/utils_cmds_test.c b/src/utils_cmds_test.c deleted file mode 100644 index 93bf5129..00000000 --- a/src/utils_cmds_test.c +++ /dev/null @@ -1,224 +0,0 @@ -/** - * collectd - src/tests/utils_cmds_test.c - * Copyright (C) 2016 Sebastian 'tokkee' Harl - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Sebastian 'tokkee' Harl - **/ - -#include "common.h" -#include "testing.h" -#include "utils_cmds.h" - -static void error_cb(void *ud, cmd_status_t status, const char *format, - va_list ap) { - if (status == CMD_OK) - return; - - printf("ERROR[%d]: ", status); - vprintf(format, ap); - printf("\n"); - fflush(stdout); -} /* void error_cb */ - -static cmd_options_t default_host_opts = { - /* identifier_default_host = */ "dummy-host", -}; - -static struct { - char *input; - cmd_options_t *opts; - cmd_status_t expected_status; - cmd_type_t expected_type; -} parse_data[] = { - /* Valid FLUSH commands. */ - { - "FLUSH", NULL, CMD_OK, CMD_FLUSH, - }, - { - "FLUSH identifier=myhost/magic/MAGIC", NULL, CMD_OK, CMD_FLUSH, - }, - { - "FLUSH identifier=magic/MAGIC", &default_host_opts, CMD_OK, CMD_FLUSH, - }, - { - "FLUSH timeout=123 plugin=\"A\"", NULL, CMD_OK, CMD_FLUSH, - }, - /* Invalid FLUSH commands. */ - { - /* Missing hostname; no default. */ - "FLUSH identifier=magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - /* Missing 'identifier' key. */ - "FLUSH myhost/magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - /* Invalid timeout. */ - "FLUSH timeout=A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - /* Invalid identifier. */ - "FLUSH identifier=invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - /* Invalid option. */ - "FLUSH invalid=option", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - - /* Valid GETVAL commands. */ - { - "GETVAL myhost/magic/MAGIC", NULL, CMD_OK, CMD_GETVAL, - }, - { - "GETVAL magic/MAGIC", &default_host_opts, CMD_OK, CMD_GETVAL, - }, - - /* Invalid GETVAL commands. */ - { - "GETVAL magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - "GETVAL", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - "GETVAL invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - - /* Valid LISTVAL commands. */ - { - "LISTVAL", NULL, CMD_OK, CMD_LISTVAL, - }, - - /* Invalid LISTVAL commands. */ - { - "LISTVAL invalid", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - - /* Valid PUTVAL commands. */ - { - "PUTVAL magic/MAGIC N:42", &default_host_opts, CMD_OK, CMD_PUTVAL, - }, - { - "PUTVAL myhost/magic/MAGIC N:42", NULL, CMD_OK, CMD_PUTVAL, - }, - { - "PUTVAL myhost/magic/MAGIC 1234:42", NULL, CMD_OK, CMD_PUTVAL, - }, - { - "PUTVAL myhost/magic/MAGIC 1234:42 2345:23", NULL, CMD_OK, CMD_PUTVAL, - }, - { - "PUTVAL myhost/magic/MAGIC interval=2 1234:42", NULL, CMD_OK, - CMD_PUTVAL, - }, - { - "PUTVAL myhost/magic/MAGIC interval=2 1234:42 interval=5 2345:23", NULL, - CMD_OK, CMD_PUTVAL, - }, - - /* Invalid PUTVAL commands. */ - { - "PUTVAL magic/MAGIC N:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - "PUTVAL", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - "PUTVAL invalid N:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - "PUTVAL myhost/magic/MAGIC A:42", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - "PUTVAL myhost/magic/MAGIC 1234:A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - "PUTVAL myhost/magic/MAGIC", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - "PUTVAL 1234:A", NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, - }, - { - "PUTVAL myhost/magic/UNKNOWN 1234:42", NULL, CMD_PARSE_ERROR, - CMD_UNKNOWN, - }, - /* - * As of collectd 5.x, PUTVAL accepts invalid options. - { - "PUTVAL myhost/magic/MAGIC invalid=2 1234:42", - NULL, - CMD_PARSE_ERROR, - CMD_UNKNOWN, - }, - */ - - /* Invalid commands. */ - { - "INVALID", NULL, CMD_UNKNOWN_COMMAND, CMD_UNKNOWN, - }, - { - "INVALID interval=2", NULL, CMD_UNKNOWN_COMMAND, CMD_UNKNOWN, - }, -}; - -DEF_TEST(parse) { - cmd_error_handler_t err = {error_cb, NULL}; - int test_result = 0; - - for (size_t i = 0; i < STATIC_ARRAY_SIZE(parse_data); i++) { - char *input = strdup(parse_data[i].input); - - char description[1024]; - cmd_status_t status; - cmd_t cmd; - - bool result; - - memset(&cmd, 0, sizeof(cmd)); - - status = cmd_parse(input, &cmd, parse_data[i].opts, &err); - snprintf(description, sizeof(description), "cmd_parse (\"%s\", opts=%p) = " - "%d (type=%d [%s]); want %d " - "(type=%d [%s])", - parse_data[i].input, parse_data[i].opts, status, cmd.type, - CMD_TO_STRING(cmd.type), parse_data[i].expected_status, - parse_data[i].expected_type, - CMD_TO_STRING(parse_data[i].expected_type)); - result = (status == parse_data[i].expected_status) && - (cmd.type == parse_data[i].expected_type); - LOG(result, description); - - /* Run all tests before failing. */ - if (!result) - test_result = -1; - - cmd_destroy(&cmd); - free(input); - } - - return test_result; -} - -int main(int argc, char **argv) { - RUN_TEST(parse); - END_TEST; -} diff --git a/src/utils_config_cores.c b/src/utils_config_cores.c deleted file mode 100644 index 94657459..00000000 --- a/src/utils_config_cores.c +++ /dev/null @@ -1,372 +0,0 @@ -/** - * collectd - src/utils_config_cores.c - * - * Copyright(c) 2018 Intel Corporation. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Authors: - * Kamil Wiatrowski - **/ - -#include "collectd.h" - -#include "common.h" - -#include "utils_config_cores.h" - -#define UTIL_NAME "utils_config_cores" - -#define MAX_SOCKETS 8 -#define MAX_SOCKET_CORES 64 -#define MAX_CORES (MAX_SOCKET_CORES * MAX_SOCKETS) - -static inline _Bool is_in_list(unsigned val, const unsigned *list, size_t len) { - for (size_t i = 0; i < len; i++) - if (list[i] == val) - return 1; - return 0; -} - -static int str_to_uint(const char *s, unsigned *n) { - if (s == NULL || n == NULL) - return -EINVAL; - char *endptr = NULL; - - *n = (unsigned)strtoul(s, &endptr, 0); - if (*s == '\0' || *endptr != '\0') { - ERROR(UTIL_NAME ": Failed to parse '%s' into unsigned number", s); - return -EINVAL; - } - - return 0; -} - -/* - * NAME - * str_list_to_nums - * - * DESCRIPTION - * Converts string of characters representing list of numbers into array of - * numbers. Allowed formats are: - * 0,1,2,3 - * 0-10,20-18 - * 1,3,5-8,10,0x10-12 - * - * Numbers can be in decimal or hexadecimal format. - * - * PARAMETERS - * `s' String representing list of unsigned numbers. - * `nums' Array to put converted numeric values into. - * `nums_len' Maximum number of elements that nums can accommodate. - * - * RETURN VALUE - * Number of elements placed into nums. - */ -static size_t str_list_to_nums(char *s, unsigned *nums, size_t nums_len) { - char *saveptr = NULL; - char *token; - size_t idx = 0; - - while ((token = strtok_r(s, ",", &saveptr))) { - char *pos; - unsigned start, end = 0; - s = NULL; - - while (isspace(*token)) - token++; - if (*token == '\0') - continue; - - pos = strchr(token, '-'); - if (pos) { - *pos = '\0'; - } - - if (str_to_uint(token, &start)) - return 0; - - if (pos) { - if (str_to_uint(pos + 1, &end)) - return 0; - } else { - end = start; - } - - if (start > end) { - unsigned swap = start; - start = end; - end = swap; - } - - for (unsigned i = start; i <= end; i++) { - if (is_in_list(i, nums, idx)) - continue; - if (idx >= nums_len) { - WARNING(UTIL_NAME ": exceeded the cores number limit: %" PRIsz, - nums_len); - return idx; - } - nums[idx] = i; - idx++; - } - } - return idx; -} - -/* - * NAME - * check_core_grouping - * - * DESCRIPTION - * Look for [...] brackets in *in string and if found copy the - * part between brackets into *out string and set grouped to 0. - * Otherwise grouped is set to 1 and input is copied without leading - * whitespaces. - * - * PARAMETERS - * `out' Output string to store result. - * `in' Input string to be parsed and copied. - * `out_size' Maximum number of elements that out can accommodate. - * `grouped' Set by function depending if cores should be grouped or not. - * - * RETURN VALUE - * Zero upon success or non-zero if an error occurred. - */ -static int check_core_grouping(char *out, const char *in, size_t out_size, - _Bool *grouped) { - const char *start = in; - char *end; - while (isspace(*start)) - ++start; - if (start[0] == '[') { - *grouped = 0; - ++start; - end = strchr(start, ']'); - if (end == NULL) { - ERROR(UTIL_NAME ": Missing closing bracket ] in option %s.", in); - return -EINVAL; - } - if ((size_t)(end - start) >= out_size) { - ERROR(UTIL_NAME ": Out buffer is too small."); - return -EINVAL; - } - sstrncpy(out, start, end - start + 1); - DEBUG(UTIL_NAME ": Mask for individual (not aggregated) cores: %s", out); - } else { - *grouped = 1; - sstrncpy(out, start, out_size); - } - return 0; -} - -int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl) { - if (ci == NULL || cgl == NULL) - return -EINVAL; - if (ci->values_num == 0 || ci->values_num > MAX_CORES) - return -EINVAL; - core_group_t cgroups[MAX_CORES] = {{0}}; - size_t cg_idx = 0; /* index for cgroups array */ - int ret = 0; - - for (int i = 0; i < ci->values_num; i++) { - if (ci->values[i].type != OCONFIG_TYPE_STRING) { - WARNING(UTIL_NAME ": The %s option requires string arguments.", ci->key); - return -EINVAL; - } - } - - if (ci->values_num == 1 && ci->values[0].value.string && - strlen(ci->values[0].value.string) == 0) - return 0; - - for (int i = 0; i < ci->values_num; i++) { - size_t n; - _Bool grouped = 1; - char str[DATA_MAX_NAME_LEN]; - unsigned cores[MAX_CORES] = {0}; - - if (cg_idx >= STATIC_ARRAY_SIZE(cgroups)) { - ERROR(UTIL_NAME - ": Configuration exceeds maximum number of cores: %" PRIsz, - STATIC_ARRAY_SIZE(cgroups)); - ret = -EINVAL; - goto parse_error; - } - if ((ci->values[i].value.string == NULL) || - (strlen(ci->values[i].value.string) == 0)) { - ERROR(UTIL_NAME ": Failed to parse parameters for %s option.", ci->key); - ret = -EINVAL; - goto parse_error; - } - - ret = check_core_grouping(str, ci->values[i].value.string, sizeof(str), - &grouped); - if (ret != 0) { - ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i, - ci->values[i].value.string); - goto parse_error; - } - n = str_list_to_nums(str, cores, STATIC_ARRAY_SIZE(cores)); - if (n == 0) { - ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i, - ci->values[i].value.string); - ret = -EINVAL; - goto parse_error; - } - - if (grouped) { - cgroups[cg_idx].desc = strdup(ci->values[i].value.string); - if (cgroups[cg_idx].desc == NULL) { - ERROR(UTIL_NAME ": Failed to allocate description."); - ret = -ENOMEM; - goto parse_error; - } - - cgroups[cg_idx].cores = calloc(n, sizeof(*cgroups[cg_idx].cores)); - if (cgroups[cg_idx].cores == NULL) { - ERROR(UTIL_NAME ": Failed to allocate cores for cgroup."); - ret = -ENOMEM; - goto parse_error; - } - - for (size_t j = 0; j < n; j++) - cgroups[cg_idx].cores[j] = cores[j]; - - cgroups[cg_idx].num_cores = n; - cg_idx++; - } else { - for (size_t j = 0; j < n && cg_idx < STATIC_ARRAY_SIZE(cgroups); j++) { - char desc[DATA_MAX_NAME_LEN]; - snprintf(desc, sizeof(desc), "%u", cores[j]); - - cgroups[cg_idx].desc = strdup(desc); - if (cgroups[cg_idx].desc == NULL) { - ERROR(UTIL_NAME ": Failed to allocate desc for core %u.", cores[j]); - ret = -ENOMEM; - goto parse_error; - } - - cgroups[cg_idx].cores = calloc(1, sizeof(*(cgroups[cg_idx].cores))); - if (cgroups[cg_idx].cores == NULL) { - ERROR(UTIL_NAME ": Failed to allocate cgroup for core %u.", cores[j]); - ret = -ENOMEM; - goto parse_error; - } - cgroups[cg_idx].num_cores = 1; - cgroups[cg_idx].cores[0] = cores[j]; - cg_idx++; - } - } - } - - cgl->cgroups = calloc(cg_idx, sizeof(*cgl->cgroups)); - if (cgl->cgroups == NULL) { - ERROR(UTIL_NAME ": Failed to allocate core groups."); - ret = -ENOMEM; - goto parse_error; - } - - cgl->num_cgroups = cg_idx; - for (size_t i = 0; i < cg_idx; i++) - cgl->cgroups[i] = cgroups[i]; - - return 0; - -parse_error: - - cg_idx = 0; - while (cg_idx < STATIC_ARRAY_SIZE(cgroups) && cgroups[cg_idx].desc != NULL) { - sfree(cgroups[cg_idx].desc); - sfree(cgroups[cg_idx].cores); - cg_idx++; - } - return ret; -} - -int config_cores_default(int num_cores, core_groups_list_t *cgl) { - if (cgl == NULL || num_cores < 0 || num_cores > MAX_CORES) - return -EINVAL; - - cgl->cgroups = calloc(num_cores, sizeof(*(cgl->cgroups))); - if (cgl->cgroups == NULL) { - ERROR(UTIL_NAME ": Failed to allocate memory for core groups."); - return -ENOMEM; - } - cgl->num_cgroups = num_cores; - - for (int i = 0; i < num_cores; i++) { - char desc[DATA_MAX_NAME_LEN]; - snprintf(desc, sizeof(desc), "%d", i); - - cgl->cgroups[i].cores = calloc(1, sizeof(*(cgl->cgroups[i].cores))); - if (cgl->cgroups[i].cores == NULL) { - ERROR(UTIL_NAME ": Failed to allocate default cores for cgroup %d.", i); - config_cores_cleanup(cgl); - return -ENOMEM; - } - cgl->cgroups[i].num_cores = 1; - cgl->cgroups[i].cores[0] = i; - - cgl->cgroups[i].desc = strdup(desc); - if (cgl->cgroups[i].desc == NULL) { - ERROR(UTIL_NAME ": Failed to allocate description for cgroup %d.", i); - config_cores_cleanup(cgl); - return -ENOMEM; - } - } - return 0; -} - -void config_cores_cleanup(core_groups_list_t *cgl) { - if (cgl == NULL) - return; - for (size_t i = 0; i < cgl->num_cgroups; i++) { - sfree(cgl->cgroups[i].desc); - sfree(cgl->cgroups[i].cores); - } - sfree(cgl->cgroups); - cgl->num_cgroups = 0; -} - -int config_cores_cmp_cgroups(const core_group_t *cg_a, - const core_group_t *cg_b) { - size_t found = 0; - - assert(cg_a != NULL); - assert(cg_b != NULL); - - const size_t sz_a = cg_a->num_cores; - const size_t sz_b = cg_b->num_cores; - const unsigned *tab_a = cg_a->cores; - const unsigned *tab_b = cg_b->cores; - - for (size_t i = 0; i < sz_a; i++) - if (is_in_list(tab_a[i], tab_b, sz_b)) - found++; - - /* if no cores are the same */ - if (!found) - return 0; - /* if group contains same cores */ - if (sz_a == sz_b && sz_b == found) - return 1; - /* if not all cores are the same */ - return -1; -} diff --git a/src/utils_config_cores.h b/src/utils_config_cores.h deleted file mode 100644 index d45f8480..00000000 --- a/src/utils_config_cores.h +++ /dev/null @@ -1,136 +0,0 @@ -/** - * collectd - src/utils_config_cores.h - * - * Copyright(c) 2018 Intel Corporation. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Authors: - * Kamil Wiatrowski - **/ - -#ifndef UTILS_CONFIG_CORES_H -#define UTILS_CONFIG_CORES_H 1 - -#include "configfile.h" - -#ifndef PRIsz -#define PRIsz "zu" -#endif /* PRIsz */ - -struct core_group_s { - char *desc; - unsigned int *cores; - size_t num_cores; -}; -typedef struct core_group_s core_group_t; - -struct core_groups_list_s { - core_group_t *cgroups; - size_t num_cgroups; -}; -typedef struct core_groups_list_s core_groups_list_t; - -/* - * NAME - * config_cores_parse - * - * DESCRIPTION - * Convert strings from config item into list of core groups. - * - * PARAMETERS - * `ci' Pointer to config item. - * `cgl' Pointer to core groups list to be filled. - * - * RETURN VALUE - * Zero upon success or non-zero if an error occurred. - * - * NOTES - * In case of an error, *cgl is not modified. - * Numbers can be in decimal or hexadecimal format. - * The memory allocated for *cgroups in list needs to be freed - * with config_cores_cleanup. - * - * EXAMPLES - * If config is "0-3" "[4-15]" it means that cores 0-3 are aggregated - * into one group and cores 4 to 15 are stored individualily in - * separate groups. Examples of allowed formats: - * "0,3,4" "10-15" - cores collected into two groups - * "0" "0x3" "7" - 3 cores, each in individual group - * "[32-63]" - 32 cores, each in individual group - * - * For empty string "" *cgl is not modified and zero is returned. - */ -int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl); - -/* - * NAME - * config_cores_default - * - * DESCRIPTION - * Set number of cores starting from zero into individual - * core groups in *cgl list. - * - * PARAMETERS - * `num_cores' Number of cores to be configured. - * `cgl' Pointer to core groups list. - * - * RETURN VALUE - * Zero upon success or non-zero if an error occurred. - * - * NOTES - * The memory allocated for *cgroups in list needs to be freed - * with config_cores_cleanup. In case of error the memory is - * freed by the function itself. - */ -int config_cores_default(int num_cores, core_groups_list_t *cgl); - -/* - * NAME - * config_cores_cleanup - * - * DESCRIPTION - * Free the memory allocated for cgroups and set - * num_cgroups to zero. - * - * PARAMETERS - * `cgl' Pointer to core groups list. - */ -void config_cores_cleanup(core_groups_list_t *cgl); - -/* - * NAME - * config_cores_cmp_cgroups - * - * DESCRIPTION - * Function to compare cores in 2 core groups. - * - * PARAMETERS - * `cg_a' Pointer to core group a. - * `cg_b' Pointer to core group b. - * - * RETURN VALUE - * 1 if both groups contain the same cores - * 0 if none of their cores match - * -1 if some but not all cores match - */ -int config_cores_cmp_cgroups(const core_group_t *cg_a, - const core_group_t *cg_b); - -#endif /* UTILS_CONFIG_CORES_H */ diff --git a/src/utils_config_cores_test.c b/src/utils_config_cores_test.c deleted file mode 100644 index 2c6f5b60..00000000 --- a/src/utils_config_cores_test.c +++ /dev/null @@ -1,249 +0,0 @@ -/** - * collectd - src/utils_config_cores_test.c - * - * Copyright(c) 2018 Intel Corporation. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Authors: - * Kamil Wiatrowski - **/ - -#include "collectd.h" - -#include "testing.h" -#include "utils_config_cores.c" /* sic */ - -oconfig_value_t test_cfg_values[] = {{{"0"}, OCONFIG_TYPE_STRING}, - {{"1-2"}, OCONFIG_TYPE_STRING}, - {{"[3-4]"}, OCONFIG_TYPE_STRING}}; - -oconfig_item_t test_cfg = { - "Cores", test_cfg_values, STATIC_ARRAY_SIZE(test_cfg_values), NULL, NULL, - 0}; - -static int compare_with_test_config(core_groups_list_t *cgl) { - if (cgl->num_cgroups == 4 && cgl->cgroups[0].num_cores == 1 && - strcmp("0", cgl->cgroups[0].desc) == 0 && cgl->cgroups[0].cores[0] == 0 && - cgl->cgroups[1].num_cores == 2 && - strcmp("1-2", cgl->cgroups[1].desc) == 0 && - cgl->cgroups[1].cores[0] == 1 && cgl->cgroups[1].cores[1] == 2 && - cgl->cgroups[2].num_cores == 1 && - strcmp("3", cgl->cgroups[2].desc) == 0 && cgl->cgroups[2].cores[0] == 3 && - cgl->cgroups[3].num_cores == 1 && - strcmp("4", cgl->cgroups[3].desc) == 0 && cgl->cgroups[3].cores[0] == 4) - return 0; - - return -1; -} - -DEF_TEST(string_to_uint) { - int ret = 0; - char *s = "13", *s1 = "0xd", *s2 = "g"; - unsigned n = 0; - - ret = str_to_uint(s, &n); - EXPECT_EQ_INT(0, ret); - EXPECT_EQ_INT(13, n); - - ret = str_to_uint(s1, &n); - EXPECT_EQ_INT(0, ret); - EXPECT_EQ_INT(13, n); - - ret = str_to_uint(s2, &n); - OK(ret < 0); - - ret = str_to_uint(NULL, &n); - OK(ret < 0); - return 0; -} - -DEF_TEST(cores_list_to_numbers) { - size_t n = 0; - unsigned nums[MAX_CORES]; - char str[64] = ""; - - n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); - EXPECT_EQ_INT(0, n); - - strncpy(str, "1", STATIC_ARRAY_SIZE(str)); - n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); - EXPECT_EQ_INT(1, n); - EXPECT_EQ_INT(1, nums[0]); - - strncpy(str, "0,2-3", STATIC_ARRAY_SIZE(str)); - n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); - EXPECT_EQ_INT(3, n); - EXPECT_EQ_INT(0, nums[0]); - EXPECT_EQ_INT(2, nums[1]); - EXPECT_EQ_INT(3, nums[2]); - - strncpy(str, "11-0xa", STATIC_ARRAY_SIZE(str)); - n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); - EXPECT_EQ_INT(2, n); - EXPECT_EQ_INT(10, nums[0]); - EXPECT_EQ_INT(11, nums[1]); - - snprintf(str, sizeof(str), "0-%d", (MAX_CORES - 1)); - n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); - EXPECT_EQ_INT(MAX_CORES, n); - EXPECT_EQ_INT(0, nums[0]); - EXPECT_EQ_INT(MAX_CORES - 1, nums[MAX_CORES - 1]); - - /* Should return 0 for incorrect syntax. */ - strncpy(str, "5g", STATIC_ARRAY_SIZE(str)); - n = str_list_to_nums(str, nums, STATIC_ARRAY_SIZE(nums)); - EXPECT_EQ_INT(0, n); - return 0; -} - -DEF_TEST(check_grouped_cores) { - int ret = 0; - _Bool grouped; - char src[64] = "[5-15]"; - char dest[64]; - - ret = check_core_grouping(dest, src, sizeof(dest), &grouped); - EXPECT_EQ_INT(0, ret); - EXPECT_EQ_INT(0, grouped); - EXPECT_EQ_STR("5-15", dest); - - strncpy(src, " 5-15", STATIC_ARRAY_SIZE(src)); - ret = check_core_grouping(dest, src, sizeof(dest), &grouped); - EXPECT_EQ_INT(0, ret); - EXPECT_EQ_INT(1, grouped); - EXPECT_EQ_STR("5-15", dest); - return 0; -} - -DEF_TEST(cores_option_parse) { - int ret = 0; - core_groups_list_t cgl = {0}; - - ret = config_cores_parse(&test_cfg, &cgl); - EXPECT_EQ_INT(0, ret); - CHECK_NOT_NULL(cgl.cgroups); - EXPECT_EQ_INT(0, compare_with_test_config(&cgl)); - - config_cores_cleanup(&cgl); - return 0; -} - -DEF_TEST(cores_option_parse_fail) { - int ret = 0; - core_groups_list_t cgl = {0}; - /* Wrong value, missing closing bracket ] */ - oconfig_value_t values = {{"[0-15"}, OCONFIG_TYPE_STRING}; - oconfig_item_t cfg = {"Cores", &values, 1, NULL, NULL, 0}; - - ret = config_cores_parse(&cfg, &cgl); - EXPECT_EQ_INT(-EINVAL, ret); - EXPECT_EQ_INT(0, cgl.num_cgroups); - OK(NULL == cgl.cgroups); - return 0; -} - -DEF_TEST(cores_default_list) { - int ret = 0; - core_groups_list_t cgl = {0}; - - ret = config_cores_default(2, &cgl); - EXPECT_EQ_INT(0, ret); - EXPECT_EQ_INT(2, cgl.num_cgroups); - CHECK_NOT_NULL(cgl.cgroups); - - CHECK_NOT_NULL(cgl.cgroups[0].cores); - CHECK_NOT_NULL(cgl.cgroups[0].desc); - EXPECT_EQ_STR("0", cgl.cgroups[0].desc); - EXPECT_EQ_INT(1, cgl.cgroups[0].num_cores); - EXPECT_EQ_INT(0, cgl.cgroups[0].cores[0]); - - CHECK_NOT_NULL(cgl.cgroups[1].cores); - CHECK_NOT_NULL(cgl.cgroups[1].desc); - EXPECT_EQ_STR("1", cgl.cgroups[1].desc); - EXPECT_EQ_INT(1, cgl.cgroups[1].num_cores); - EXPECT_EQ_INT(1, cgl.cgroups[1].cores[0]); - - config_cores_cleanup(&cgl); - return 0; -} - -DEF_TEST(cores_default_list_fail) { - int ret = 0; - core_groups_list_t cgl = {0}; - - ret = config_cores_default(-1, &cgl); - OK(ret < 0); - ret = config_cores_default(MAX_CORES + 1, &cgl); - OK(ret < 0); - ret = config_cores_default(1, NULL); - OK(ret < 0); - return 0; -} - -DEF_TEST(cores_group_cleanup) { - core_groups_list_t cgl; - cgl.cgroups = calloc(1, sizeof(*cgl.cgroups)); - CHECK_NOT_NULL(cgl.cgroups); - cgl.num_cgroups = 1; - cgl.cgroups[0].desc = strdup("1"); - cgl.cgroups[0].cores = calloc(1, sizeof(*cgl.cgroups[0].cores)); - CHECK_NOT_NULL(cgl.cgroups[0].cores); - cgl.cgroups[0].cores[0] = 1; - cgl.cgroups[0].num_cores = 1; - - config_cores_cleanup(&cgl); - OK(NULL == cgl.cgroups); - EXPECT_EQ_INT(0, cgl.num_cgroups); - return 0; -} - -DEF_TEST(cores_group_cmp) { - unsigned cores_mock[] = {0, 1, 2}; - core_group_t group_mock = {"0,1,2", cores_mock, 3}; - unsigned cores_mock_2[] = {2, 3}; - core_group_t group_mock_2 = {"2,3", cores_mock_2, 2}; - - int ret = config_cores_cmp_cgroups(&group_mock, &group_mock); - EXPECT_EQ_INT(1, ret); - - ret = config_cores_cmp_cgroups(&group_mock, &group_mock_2); - EXPECT_EQ_INT(-1, ret); - - cores_mock_2[0] = 4; - ret = config_cores_cmp_cgroups(&group_mock, &group_mock_2); - EXPECT_EQ_INT(0, ret); - return 0; -} - -int main(void) { - RUN_TEST(string_to_uint); - RUN_TEST(cores_list_to_numbers); - RUN_TEST(check_grouped_cores); - - RUN_TEST(cores_group_cleanup); - RUN_TEST(cores_option_parse); - RUN_TEST(cores_option_parse_fail); - RUN_TEST(cores_default_list); - RUN_TEST(cores_default_list_fail); - - RUN_TEST(cores_group_cmp); - - END_TEST; -} diff --git a/src/utils_crc32.c b/src/utils_crc32.c deleted file mode 100644 index afc566a9..00000000 --- a/src/utils_crc32.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or - * code or tables extracted from it, as desired without restriction. - * - * First, the polynomial itself and its table of feedback terms. The - * polynomial is - * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 - * - * Note that we take it "backwards" and put the highest-order term in - * the lowest-order bit. The X^32 term is "implied"; the LSB is the - * X^31 term, etc. The X^0 term (usually shown as "+1") results in - * the MSB being 1 - * - * Note that the usual hardware shift register implementation, which - * is what we're using (we're merely optimizing it by doing eight-bit - * chunks at a time) shifts bits into the lowest-order term. In our - * implementation, that means shifting towards the right. Why do we - * do it this way? Because the calculated CRC must be transmitted in - * order from highest-order term to lowest-order term. UARTs transmit - * characters in order from LSB to MSB. By storing the CRC this way - * we hand it to the UART in the order low-byte to high-byte; the UART - * sends each low-bit to hight-bit; and the result is transmission bit - * by bit from highest- to lowest-order term without requiring any bit - * shuffling on our part. Reception works similarly - * - * The feedback terms table consists of 256, 32-bit entries. Notes - * - * The table can be generated at runtime if desired; code to do so - * is shown later. It might not be obvious, but the feedback - * terms simply represent the results of eight shift/xor opera - * tions for all combinations of data and CRC register values - * - * The values must be right-shifted by eight bits by the "updcrc - * logic; the shift must be unsigned (bring in zeroes). On some - * hardware you could probably optimize the shift in assembler by - * using byte-swap instructions - * polynomial $edb88320 - */ - -#include -#include - -uint32_t crc32_buffer(const unsigned char *, size_t); -static unsigned int crc32_tab[] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL}; - -/* Return a 32-bit CRC of the contents of the buffer. */ - -uint32_t crc32_buffer(const unsigned char *s, size_t len) { - uint32_t ret; - - ret = 0; - for (size_t i = 0; i < len; i++) - ret = crc32_tab[(ret ^ s[i]) & 0xff] ^ (ret >> 8); - return ret; -} diff --git a/src/utils_crc32.h b/src/utils_crc32.h deleted file mode 100644 index 8e2c2126..00000000 --- a/src/utils_crc32.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * collectd - src/utils_crc32.h - * Copyright (C) 2014 Pierre-Yves Ritschard - * - * 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: - * Pierre-Yves Ritschard - */ - -#ifndef UTILS_CRC32_H -#define UTILS_CRC32_H 1 - -uint32_t crc32_buffer(const unsigned char *, size_t); - -#endif diff --git a/src/utils_curl_stats.c b/src/utils_curl_stats.c deleted file mode 100644 index 09856599..00000000 --- a/src/utils_curl_stats.c +++ /dev/null @@ -1,252 +0,0 @@ -/** - * collectd - src/utils_curl_stats.c - * Copyright (C) 2015 Sebastian 'tokkee' Harl - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Sebastian Harl - **/ - -#include "collectd.h" - -#include "common.h" -#include "utils_curl_stats.h" - -#include -#include - -struct curl_stats_s { - bool total_time; - bool namelookup_time; - bool connect_time; - bool pretransfer_time; - bool size_upload; - bool size_download; - bool speed_download; - bool speed_upload; - bool header_size; - bool request_size; - bool content_length_download; - bool content_length_upload; - bool starttransfer_time; - bool redirect_time; - bool redirect_count; - bool num_connects; - bool appconnect_time; -}; - -/* - * Private functions - */ - -static int dispatch_gauge(CURL *curl, CURLINFO info, value_list_t *vl) { - CURLcode code; - value_t v; - - code = curl_easy_getinfo(curl, info, &v.gauge); - if (code != CURLE_OK) - return -1; - - vl->values = &v; - vl->values_len = 1; - - return plugin_dispatch_values(vl); -} /* dispatch_gauge */ - -/* dispatch a speed, in bytes/second */ -static int dispatch_speed(CURL *curl, CURLINFO info, value_list_t *vl) { - CURLcode code; - value_t v; - - code = curl_easy_getinfo(curl, info, &v.gauge); - if (code != CURLE_OK) - return -1; - - v.gauge *= 8; - - vl->values = &v; - vl->values_len = 1; - - return plugin_dispatch_values(vl); -} /* dispatch_speed */ - -/* dispatch a size/count, reported as a long value */ -static int dispatch_size(CURL *curl, CURLINFO info, value_list_t *vl) { - CURLcode code; - value_t v; - long raw; - - code = curl_easy_getinfo(curl, info, &raw); - if (code != CURLE_OK) - return -1; - - v.gauge = (double)raw; - - vl->values = &v; - vl->values_len = 1; - - return plugin_dispatch_values(vl); -} /* dispatch_size */ - -static struct { - const char *name; - const char *config_key; - size_t offset; - - int (*dispatcher)(CURL *, CURLINFO, value_list_t *); - const char *type; - CURLINFO info; -} field_specs[] = { -#define SPEC(name, config_key, dispatcher, type, info) \ - { #name, config_key, offsetof(curl_stats_t, name), dispatcher, type, info } - - SPEC(total_time, "TotalTime", dispatch_gauge, "duration", - CURLINFO_TOTAL_TIME), - SPEC(namelookup_time, "NamelookupTime", dispatch_gauge, "duration", - CURLINFO_NAMELOOKUP_TIME), - SPEC(connect_time, "ConnectTime", dispatch_gauge, "duration", - CURLINFO_CONNECT_TIME), - SPEC(pretransfer_time, "PretransferTime", dispatch_gauge, "duration", - CURLINFO_PRETRANSFER_TIME), - SPEC(size_upload, "SizeUpload", dispatch_gauge, "bytes", - CURLINFO_SIZE_UPLOAD), - SPEC(size_download, "SizeDownload", dispatch_gauge, "bytes", - CURLINFO_SIZE_DOWNLOAD), - SPEC(speed_download, "SpeedDownload", dispatch_speed, "bitrate", - CURLINFO_SPEED_DOWNLOAD), - SPEC(speed_upload, "SpeedUpload", dispatch_speed, "bitrate", - CURLINFO_SPEED_UPLOAD), - SPEC(header_size, "HeaderSize", dispatch_size, "bytes", - CURLINFO_HEADER_SIZE), - SPEC(request_size, "RequestSize", dispatch_size, "bytes", - CURLINFO_REQUEST_SIZE), - SPEC(content_length_download, "ContentLengthDownload", dispatch_gauge, - "bytes", CURLINFO_CONTENT_LENGTH_DOWNLOAD), - SPEC(content_length_upload, "ContentLengthUpload", dispatch_gauge, "bytes", - CURLINFO_CONTENT_LENGTH_UPLOAD), - SPEC(starttransfer_time, "StarttransferTime", dispatch_gauge, "duration", - CURLINFO_STARTTRANSFER_TIME), - SPEC(redirect_time, "RedirectTime", dispatch_gauge, "duration", - CURLINFO_REDIRECT_TIME), - SPEC(redirect_count, "RedirectCount", dispatch_size, "count", - CURLINFO_REDIRECT_COUNT), - SPEC(num_connects, "NumConnects", dispatch_size, "count", - CURLINFO_NUM_CONNECTS), -#ifdef HAVE_CURLINFO_APPCONNECT_TIME - SPEC(appconnect_time, "AppconnectTime", dispatch_gauge, "duration", - CURLINFO_APPCONNECT_TIME), -#endif - -#undef SPEC -}; - -static void enable_field(curl_stats_t *s, size_t offset) { - *(bool *)((char *)s + offset) = true; -} /* enable_field */ - -static bool field_enabled(curl_stats_t *s, size_t offset) { - return *(bool *)((char *)s + offset); -} /* field_enabled */ - -/* - * Public API - */ -curl_stats_t *curl_stats_from_config(oconfig_item_t *ci) { - curl_stats_t *s; - - if (ci == NULL) - return NULL; - - s = calloc(1, sizeof(*s)); - if (s == NULL) - return NULL; - - for (int i = 0; i < ci->children_num; ++i) { - oconfig_item_t *c = ci->children + i; - size_t field; - - bool enabled = 0; - - for (field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) { - if (!strcasecmp(c->key, field_specs[field].config_key)) - break; - if (!strcasecmp(c->key, field_specs[field].name)) - break; - } - if (field >= STATIC_ARRAY_SIZE(field_specs)) { - ERROR("curl stats: Unknown field name %s", c->key); - free(s); - return NULL; - } - - if (cf_util_get_boolean(c, &enabled) != 0) { - free(s); - return NULL; - } - if (enabled) - enable_field(s, field_specs[field].offset); - } - - return s; -} /* curl_stats_from_config */ - -void curl_stats_destroy(curl_stats_t *s) { - if (s != NULL) - free(s); -} /* curl_stats_destroy */ - -int curl_stats_dispatch(curl_stats_t *s, CURL *curl, const char *hostname, - const char *plugin, const char *plugin_instance) { - value_list_t vl = VALUE_LIST_INIT; - - if (s == NULL) - return 0; - if ((curl == NULL) || (plugin == NULL)) { - ERROR("curl stats: dispatch() called with missing arguments " - "(curl=%p; plugin=%s)", - curl, plugin == NULL ? "" : plugin); - return -1; - } - - if (hostname != NULL) - sstrncpy(vl.host, hostname, sizeof(vl.host)); - sstrncpy(vl.plugin, plugin, sizeof(vl.plugin)); - if (plugin_instance != NULL) - sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); - - for (size_t field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) { - int status; - - if (!field_enabled(s, field_specs[field].offset)) - continue; - - sstrncpy(vl.type, field_specs[field].type, sizeof(vl.type)); - sstrncpy(vl.type_instance, field_specs[field].name, - sizeof(vl.type_instance)); - - vl.values = NULL; - vl.values_len = 0; - status = field_specs[field].dispatcher(curl, field_specs[field].info, &vl); - if (status < 0) - return status; - } - - return 0; -} /* curl_stats_dispatch */ diff --git a/src/utils_curl_stats.h b/src/utils_curl_stats.h deleted file mode 100644 index 3f83aab9..00000000 --- a/src/utils_curl_stats.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * collectd - src/utils_curl_stats.h - * Copyright (C) 2015 Sebastian 'tokkee' Harl - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Sebastian Harl - **/ - -#ifndef UTILS_CURL_STATS_H -#define UTILS_CURL_STATS_H 1 - -#include "plugin.h" - -#include - -struct curl_stats_s; -typedef struct curl_stats_s curl_stats_t; - -/* - * curl_stats_from_config allocates and constructs a cURL statistics object - * from the specified configuration which is expected to be a single block of - * boolean options named after cURL information fields. The boolean value - * indicates whether to collect the respective information. - * - * See http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html - */ -curl_stats_t *curl_stats_from_config(oconfig_item_t *ci); - -void curl_stats_destroy(curl_stats_t *s); - -/* - * curl_stats_dispatch dispatches performance values from the the specified - * cURL session to the daemon. - */ -int curl_stats_dispatch(curl_stats_t *s, CURL *curl, const char *hostname, - const char *plugin, const char *plugin_instance); - -#endif /* UTILS_CURL_STATS_H */ diff --git a/src/utils_db_query.c b/src/utils_db_query.c deleted file mode 100644 index 0279a471..00000000 --- a/src/utils_db_query.c +++ /dev/null @@ -1,1033 +0,0 @@ -/** - * collectd - src/utils_db_query.c - * Copyright (C) 2008,2009 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" -#include "utils_db_query.h" - -/* - * Data types - */ -struct udb_result_s; /* {{{ */ -typedef struct udb_result_s udb_result_t; -struct udb_result_s { - char *type; - char *instance_prefix; - char **instances; - size_t instances_num; - char **values; - size_t values_num; - char **metadata; - size_t metadata_num; - - udb_result_t *next; -}; /* }}} */ - -struct udb_query_s /* {{{ */ -{ - char *name; - char *statement; - void *user_data; - char *plugin_instance_from; - - unsigned int min_version; - unsigned int max_version; - - udb_result_t *results; -}; /* }}} */ - -struct udb_result_preparation_area_s /* {{{ */ -{ - const data_set_t *ds; - size_t *instances_pos; - size_t *values_pos; - size_t *metadata_pos; - char **instances_buffer; - char **values_buffer; - char **metadata_buffer; - char *plugin_instance; - - struct udb_result_preparation_area_s *next; -}; /* }}} */ -typedef struct udb_result_preparation_area_s udb_result_preparation_area_t; - -struct udb_query_preparation_area_s /* {{{ */ -{ - size_t column_num; - size_t plugin_instance_pos; - char *host; - char *plugin; - char *db_name; - - udb_result_preparation_area_t *result_prep_areas; -}; /* }}} */ - -/* - * Config Private functions - */ -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) { - 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) { - P_WARNING("Argument %i to the `%s' option " - "is not a string.", - i + 1, ci->key); - return -1; - } - } - - array_len = *ret_array_len; - array = realloc(*ret_array, sizeof(char *) * (array_len + ci->values_num)); - if (array == NULL) { - P_ERROR("udb_config_add_string: realloc failed."); - return -1; - } - *ret_array = array; - - for (int i = 0; i < ci->values_num; i++) { - array[array_len] = strdup(ci->values[i].value.string); - if (array[array_len] == NULL) { - P_ERROR("udb_config_add_string: strdup failed."); - *ret_array_len = array_len; - return -1; - } - array_len++; - } - - *ret_array_len = array_len; - return 0; -} /* }}} int udb_config_add_string */ - -static int udb_config_set_uint(unsigned int *ret_value, /* {{{ */ - oconfig_item_t *ci) { - - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) { - P_WARNING("The `%s' config option " - "needs exactly one numeric argument.", - ci->key); - return -1; - } - - 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; -} /* }}} int udb_config_set_uint */ - -/* - * Result private functions - */ -static int udb_result_submit(udb_result_t *r, /* {{{ */ - udb_result_preparation_area_t *r_area, - udb_query_t const *q, - udb_query_preparation_area_t *q_area) { - value_list_t vl = VALUE_LIST_INIT; - - assert(r != NULL); - assert(r_area->ds != NULL); - assert(((size_t)r_area->ds->ds_num) == r->values_num); - assert(r->values_num > 0); - - vl.values = calloc(r->values_num, sizeof(*vl.values)); - if (vl.values == NULL) { - P_ERROR("udb_result_submit: calloc failed."); - return -1; - } - vl.values_len = r_area->ds->ds_num; - - for (size_t i = 0; i < r->values_num; i++) { - char *value_str = r_area->values_buffer[i]; - - if (0 != parse_value(value_str, &vl.values[i], 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; - } - } - - 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)); - - /* Set vl.plugin_instance */ - if (q->plugin_instance_from != NULL) { - sstrncpy(vl.plugin_instance, r_area->plugin_instance, - sizeof(vl.plugin_instance)); - } else { - sstrncpy(vl.plugin_instance, q_area->db_name, sizeof(vl.plugin_instance)); - } - - /* Set vl.type_instance {{{ */ - if (r->instances_num == 0) { - if (r->instance_prefix == NULL) - vl.type_instance[0] = 0; - else - sstrncpy(vl.type_instance, r->instance_prefix, sizeof(vl.type_instance)); - } else /* if ((r->instances_num > 0) */ - { - if (r->instance_prefix == NULL) { - int status = strjoin(vl.type_instance, sizeof(vl.type_instance), - r_area->instances_buffer, r->instances_num, "-"); - if (status < 0) { - P_ERROR( - "udb_result_submit: creating type_instance failed with status %d.", - status); - return status; - } - } else { - char tmp[DATA_MAX_NAME_LEN]; - - int status = strjoin(tmp, sizeof(tmp), r_area->instances_buffer, - r->instances_num, "-"); - if (status < 0) { - P_ERROR( - "udb_result_submit: creating type_instance failed with status %d.", - status); - return status; - } - 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; - /* }}} */ - - /* Annotate meta data. {{{ */ - if (r->metadata_num > 0) { - vl.meta = meta_data_create(); - if (vl.meta == NULL) { - P_ERROR("udb_result_submit: meta_data_create failed."); - free(vl.values); - return -ENOMEM; - } - - for (size_t i = 0; i < r->metadata_num; i++) { - int status = meta_data_add_string(vl.meta, r->metadata[i], - r_area->metadata_buffer[i]); - if (status != 0) { - P_ERROR("udb_result_submit: meta_data_add_string failed."); - meta_data_destroy(vl.meta); - vl.meta = NULL; - free(vl.values); - return status; - } - } - } - /* }}} */ - - plugin_dispatch_values(&vl); - - if (r->metadata_num > 0) { - meta_data_destroy(vl.meta); - vl.meta = NULL; - } - sfree(vl.values); - return 0; -} /* }}} void udb_result_submit */ - -static void udb_result_finish_result(udb_result_t const *r, /* {{{ */ - udb_result_preparation_area_t *prep_area) { - if ((r == NULL) || (prep_area == NULL)) - return; - - 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); -} /* }}} void udb_result_finish_result */ - -static int udb_result_handle_result(udb_result_t *r, /* {{{ */ - udb_query_preparation_area_t *q_area, - udb_result_preparation_area_t *r_area, - udb_query_t const *q, - char **column_values) { - assert(r && q_area && r_area); - - for (size_t i = 0; i < r->instances_num; i++) - r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]]; - - for (size_t i = 0; i < r->values_num; i++) - r_area->values_buffer[i] = column_values[r_area->values_pos[i]]; - - for (size_t i = 0; i < r->metadata_num; i++) - r_area->metadata_buffer[i] = column_values[r_area->metadata_pos[i]]; - - if (q->plugin_instance_from) - r_area->plugin_instance = column_values[q_area->plugin_instance_pos]; - - return udb_result_submit(r, r_area, q, q_area); -} /* }}} int udb_result_handle_result */ - -static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */ - udb_result_preparation_area_t *prep_area, - char **column_names, size_t column_num) { - 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) \ - udb_result_finish_result(r, prep_area); \ - return (status) - - /* Read `ds' and check number of values {{{ */ - prep_area->ds = plugin_get_ds(r->type); - if (prep_area->ds == NULL) { - 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) { - 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); - } - /* }}} */ - - /* Allocate r->instances_pos, r->values_pos, r->metadata_post, - * 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)); - if (prep_area->instances_pos == NULL) { - P_ERROR("udb_result_prepare_result: calloc failed."); - BAIL_OUT(-ENOMEM); - } - - prep_area->instances_buffer = - (char **)calloc(r->instances_num, sizeof(char *)); - if (prep_area->instances_buffer == NULL) { - 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)); - if (prep_area->values_pos == NULL) { - P_ERROR("udb_result_prepare_result: calloc failed."); - BAIL_OUT(-ENOMEM); - } - - prep_area->values_buffer = (char **)calloc(r->values_num, sizeof(char *)); - if (prep_area->values_buffer == NULL) { - P_ERROR("udb_result_prepare_result: calloc failed."); - BAIL_OUT(-ENOMEM); - } - - prep_area->metadata_pos = (size_t *)calloc(r->metadata_num, sizeof(size_t)); - if (prep_area->metadata_pos == NULL) { - P_ERROR("udb_result_prepare_result: calloc failed."); - BAIL_OUT(-ENOMEM); - } - - prep_area->metadata_buffer = (char **)calloc(r->metadata_num, sizeof(char *)); - if (prep_area->metadata_buffer == NULL) { - P_ERROR("udb_result_prepare_result: calloc failed."); - BAIL_OUT(-ENOMEM); - } - - /* }}} */ - - /* Determine the position of the plugin instance column {{{ */ - for (size_t i = 0; i < r->instances_num; i++) { - size_t j; - - for (j = 0; j < column_num; j++) { - if (strcasecmp(r->instances[i], column_names[j]) == 0) { - prep_area->instances_pos[i] = j; - break; - } - } - - if (j >= column_num) { - 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++) */ - - /* Determine the position of the value columns {{{ */ - for (size_t i = 0; i < r->values_num; i++) { - size_t j; - - for (j = 0; j < column_num; j++) { - if (strcasecmp(r->values[i], column_names[j]) == 0) { - prep_area->values_pos[i] = j; - break; - } - } - - if (j >= column_num) { - 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++) */ - - /* Determine the position of the metadata columns {{{ */ - for (size_t i = 0; i < r->metadata_num; i++) { - size_t j; - - for (j = 0; j < column_num; j++) { - if (strcasecmp(r->metadata[i], column_names[j]) == 0) { - prep_area->metadata_pos[i] = j; - break; - } - } - - if (j >= column_num) { - 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++) */ - -#undef BAIL_OUT - return 0; -} /* }}} int udb_result_prepare_result */ - -static void udb_result_free(udb_result_t *r) /* {{{ */ -{ - if (r == NULL) - return; - - sfree(r->type); - sfree(r->instance_prefix); - - for (size_t i = 0; i < r->instances_num; i++) - sfree(r->instances[i]); - sfree(r->instances); - - for (size_t i = 0; i < r->values_num; i++) - sfree(r->values[i]); - sfree(r->values); - - for (size_t i = 0; i < r->metadata_num; i++) - sfree(r->metadata[i]); - sfree(r->metadata); - - udb_result_free(r->next); - - sfree(r); -} /* }}} void udb_result_free */ - -static int udb_result_create(const char *query_name, /* {{{ */ - udb_result_t **r_head, oconfig_item_t *ci) { - udb_result_t *r; - int status; - - if (ci->values_num != 0) { - 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) { - P_ERROR("udb_result_create: calloc failed."); - return -1; - } - r->type = NULL; - r->instance_prefix = NULL; - r->instances = NULL; - r->values = NULL; - r->metadata = NULL; - r->next = NULL; - - /* Fill the `udb_result_t' structure.. */ - status = 0; - for (int i = 0; i < ci->children_num; i++) { - oconfig_item_t *child = ci->children + i; - - if (strcasecmp("Type", child->key) == 0) - status = cf_util_get_string(child, &r->type); - else if (strcasecmp("InstancePrefix", child->key) == 0) - 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) - status = udb_config_add_string(&r->values, &r->values_num, child); - else if (strcasecmp("MetadataFrom", child->key) == 0) - status = udb_config_add_string(&r->metadata, &r->metadata_num, child); - else { - P_WARNING("Query `%s': Option `%s' not allowed here.", query_name, - child->key); - status = -1; - } - - if (status != 0) - break; - } - - /* Check that all necessary options have been given. */ - while (status == 0) { - if (r->type == NULL) { - P_WARNING("udb_result_create: `Type' not given for " - "result in query `%s'", - query_name); - status = -1; - } - if (r->values == NULL) { - P_WARNING("udb_result_create: `ValuesFrom' not given for " - "result in query `%s'", - query_name); - status = -1; - } - - break; - } /* while (status == 0) */ - - if (status != 0) { - udb_result_free(r); - return -1; - } - - /* If all went well, add this result to the list of results. */ - if (*r_head == NULL) { - *r_head = r; - } else { - udb_result_t *last; - - last = *r_head; - while (last->next != NULL) - last = last->next; - - last->next = r; - } - - return 0; -} /* }}} int udb_result_create */ - -/* - * Query private functions - */ -static void udb_query_free_one(udb_query_t *q) /* {{{ */ -{ - if (q == NULL) - return; - - sfree(q->name); - sfree(q->statement); - sfree(q->plugin_instance_from); - - udb_result_free(q->results); - - sfree(q); -} /* }}} void udb_query_free_one */ - -/* - * Query public functions - */ -int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */ - size_t *ret_query_list_len, oconfig_item_t *ci, - udb_query_create_callback_t cb) { - udb_query_t **query_list; - size_t query_list_len; - - udb_query_t *q; - int status; - - if ((ret_query_list == NULL) || (ret_query_list_len == NULL)) - return -EINVAL; - query_list = *ret_query_list; - query_list_len = *ret_query_list_len; - - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - P_WARNING("udb_result_create: The `Query' block " - "needs exactly one string argument."); - return -1; - } - - q = calloc(1, sizeof(*q)); - if (q == NULL) { - P_ERROR("udb_query_create: calloc failed."); - return -1; - } - q->min_version = 0; - q->max_version = UINT_MAX; - q->statement = NULL; - q->results = NULL; - q->plugin_instance_from = NULL; - - status = cf_util_get_string(ci, &q->name); - if (status != 0) { - sfree(q); - return status; - } - - /* Fill the `udb_query_t' structure.. */ - for (int i = 0; i < ci->children_num; i++) { - oconfig_item_t *child = ci->children + i; - - if (strcasecmp("Statement", child->key) == 0) - 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) - status = udb_config_set_uint(&q->min_version, child); - else if (strcasecmp("MaxVersion", child->key) == 0) - status = udb_config_set_uint(&q->max_version, child); - else if (strcasecmp("PluginInstanceFrom", child->key) == 0) - status = cf_util_get_string(child, &q->plugin_instance_from); - - /* Call custom callbacks */ - else if (cb != NULL) { - status = (*cb)(q, child); - if (status != 0) { - P_WARNING("The configuration callback failed " - "to handle `%s'.", - child->key); - } - } else { - P_WARNING("Query `%s': Option `%s' not allowed here.", q->name, - child->key); - status = -1; - } - - if (status != 0) - break; - } - - /* Check that all necessary options have been given. */ - if (status == 0) { - if (q->statement == NULL) { - P_WARNING("Query `%s': No `Statement' given.", q->name); - status = -1; - } - if (q->results == NULL) { - P_WARNING("Query `%s': No (valid) `Result' block given.", q->name); - status = -1; - } - } /* if (status == 0) */ - - /* If all went well, add this query to the list of queries within the - * database structure. */ - if (status == 0) { - udb_query_t **temp; - - temp = realloc(query_list, sizeof(*query_list) * (query_list_len + 1)); - if (temp == NULL) { - P_ERROR("udb_query_create: realloc failed"); - status = -1; - } else { - query_list = temp; - query_list[query_list_len] = q; - query_list_len++; - } - } - - if (status != 0) { - udb_query_free_one(q); - return -1; - } - - *ret_query_list = query_list; - *ret_query_list_len = query_list_len; - - return 0; -} /* }}} int udb_query_create */ - -void udb_query_free(udb_query_t **query_list, size_t query_list_len) /* {{{ */ -{ - if (query_list == NULL) - return; - - for (size_t i = 0; i < query_list_len; i++) - udb_query_free_one(query_list[i]); - - sfree(query_list); -} /* }}} void udb_query_free */ - -int udb_query_pick_from_list_by_name(const char *name, /* {{{ */ - udb_query_t **src_list, - size_t src_list_len, - udb_query_t ***dst_list, - size_t *dst_list_len) { - int num_added; - - if ((name == NULL) || (src_list == NULL) || (dst_list == NULL) || - (dst_list_len == NULL)) { - P_ERROR("udb_query_pick_from_list_by_name: " - "Invalid argument."); - return -EINVAL; - } - - num_added = 0; - for (size_t i = 0; i < src_list_len; i++) { - udb_query_t **tmp_list; - size_t tmp_list_len; - - if (strcasecmp(name, src_list[i]->name) != 0) - continue; - - tmp_list_len = *dst_list_len; - tmp_list = realloc(*dst_list, (tmp_list_len + 1) * sizeof(udb_query_t *)); - if (tmp_list == NULL) { - P_ERROR("udb_query_pick_from_list_by_name: realloc failed."); - return -ENOMEM; - } - - tmp_list[tmp_list_len] = src_list[i]; - tmp_list_len++; - - *dst_list = tmp_list; - *dst_list_len = tmp_list_len; - - num_added++; - } /* for (i = 0; i < src_list_len; i++) */ - - if (num_added <= 0) { - P_ERROR("Cannot find query `%s'. Make sure the " - "block is above the database definition!", - name); - return -ENOENT; - } else { - DEBUG("Added %i versions of query `%s'.", num_added, name); - } - - return 0; -} /* }}} int udb_query_pick_from_list_by_name */ - -int udb_query_pick_from_list(oconfig_item_t *ci, /* {{{ */ - udb_query_t **src_list, size_t src_list_len, - udb_query_t ***dst_list, size_t *dst_list_len) { - const char *name; - - if ((ci == NULL) || (src_list == NULL) || (dst_list == NULL) || - (dst_list_len == NULL)) { - P_ERROR("udb_query_pick_from_list: " - "Invalid argument."); - return -EINVAL; - } - - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - P_ERROR("The `%s' config option " - "needs exactly one string argument.", - ci->key); - return -1; - } - name = ci->values[0].value.string; - - return udb_query_pick_from_list_by_name(name, src_list, src_list_len, - dst_list, dst_list_len); -} /* }}} int udb_query_pick_from_list */ - -const char *udb_query_get_name(udb_query_t *q) /* {{{ */ -{ - if (q == NULL) - return NULL; - - return q->name; -} /* }}} const char *udb_query_get_name */ - -const char *udb_query_get_statement(udb_query_t *q) /* {{{ */ -{ - if (q == NULL) - return NULL; - - return q->statement; -} /* }}} const char *udb_query_get_statement */ - -void udb_query_set_user_data(udb_query_t *q, void *user_data) /* {{{ */ -{ - if (q == NULL) - return; - - q->user_data = user_data; -} /* }}} void udb_query_set_user_data */ - -void *udb_query_get_user_data(udb_query_t *q) /* {{{ */ -{ - if (q == NULL) - return NULL; - - return q->user_data; -} /* }}} void *udb_query_get_user_data */ - -int udb_query_check_version(udb_query_t *q, unsigned int version) /* {{{ */ -{ - if (q == NULL) - return -EINVAL; - - if ((version < q->min_version) || (version > q->max_version)) - return 0; - - return 1; -} /* }}} int udb_query_check_version */ - -void udb_query_finish_result(udb_query_t const *q, /* {{{ */ - udb_query_preparation_area_t *prep_area) { - udb_result_preparation_area_t *r_area; - udb_result_t *r; - - if ((q == NULL) || (prep_area == NULL)) - return; - - prep_area->column_num = 0; - sfree(prep_area->host); - sfree(prep_area->plugin); - sfree(prep_area->db_name); - - 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 */ - if (r_area == NULL) - break; - udb_result_finish_result(r, r_area); - } -} /* }}} void udb_query_finish_result */ - -int udb_query_handle_result(udb_query_t const *q, /* {{{ */ - udb_query_preparation_area_t *prep_area, - char **column_values) { - udb_result_preparation_area_t *r_area; - udb_result_t *r; - int success; - int status; - - if ((q == NULL) || (prep_area == NULL)) - return -EINVAL; - - if ((prep_area->column_num < 1) || (prep_area->host == NULL) || - (prep_area->plugin == NULL) || (prep_area->db_name == NULL)) { - 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("udb_query_handle_result (%s, %s): " - "column[%" PRIsz "] = %s;", - prep_area->db_name, q->name, i, column_values[i]); - } - } while (0); -#endif /* }}} */ - - success = 0; - for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL; - r = r->next, r_area = r_area->next) { - status = udb_result_handle_result(r, prep_area, r_area, q, column_values); - if (status == 0) - success++; - } - - if (success == 0) { - P_ERROR("udb_query_handle_result (%s, %s): " - "All results failed.", - prep_area->db_name, q->name); - return -1; - } - - return 0; -} /* }}} int udb_query_handle_result */ - -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) { - udb_result_preparation_area_t *r_area; - udb_result_t *r; - int status; - - if ((q == NULL) || (prep_area == NULL)) - return -EINVAL; - -#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); - - if ((prep_area->host == NULL) || (prep_area->plugin == NULL) || - (prep_area->db_name == NULL)) { - P_ERROR("Query `%s': Prepare failed: Out of memory.", q->name); - udb_query_finish_result(q, prep_area); - return -ENOMEM; - } - -#if defined(COLLECT_DEBUG) && COLLECT_DEBUG - do { - for (size_t i = 0; i < column_num; i++) { - DEBUG("udb_query_prepare_result: " - "query = %s; column[%" PRIsz "] = %s;", - q->name, i, column_names[i]); - } - } while (0); -#endif - - /* Determine the position of the PluginInstance column {{{ */ - if (q->plugin_instance_from != NULL) { - size_t i; - - for (i = 0; i < column_num; i++) { - if (strcasecmp(q->plugin_instance_from, column_names[i]) == 0) { - prep_area->plugin_instance_pos = i; - break; - } - } - - if (i >= column_num) { - 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; - } - } - /* }}} */ - - for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL; - r = r->next, r_area = r_area->next) { - if (!r_area) { - P_ERROR("Query `%s': Invalid number of result " - "preparation areas.", - q->name); - udb_query_finish_result(q, prep_area); - return -EINVAL; - } - - status = udb_result_prepare_result(r, r_area, column_names, column_num); - if (status != 0) { - udb_query_finish_result(q, prep_area); - return status; - } - } - - return 0; -} /* }}} int udb_query_prepare_result */ - -udb_query_preparation_area_t * -udb_query_allocate_preparation_area(udb_query_t *q) /* {{{ */ -{ - udb_query_preparation_area_t *q_area; - udb_result_preparation_area_t **next_r_area; - udb_result_t *r; - - q_area = calloc(1, sizeof(*q_area)); - if (q_area == NULL) - return NULL; - - next_r_area = &q_area->result_prep_areas; - for (r = q->results; r != NULL; r = r->next) { - udb_result_preparation_area_t *r_area; - - r_area = calloc(1, sizeof(*r_area)); - if (r_area == NULL) { - udb_result_preparation_area_t *a = q_area->result_prep_areas; - - while (a != NULL) { - udb_result_preparation_area_t *next = a->next; - sfree(a); - a = next; - } - - free(q_area); - return NULL; - } - - *next_r_area = r_area; - next_r_area = &r_area->next; - } - - return q_area; -} /* }}} udb_query_preparation_area_t *udb_query_allocate_preparation_area */ - -void udb_query_delete_preparation_area( - udb_query_preparation_area_t *q_area) /* {{{ */ -{ - udb_result_preparation_area_t *r_area; - - if (q_area == NULL) - return; - - r_area = q_area->result_prep_areas; - while (r_area != NULL) { - udb_result_preparation_area_t *area = r_area; - - r_area = r_area->next; - - sfree(area->instances_pos); - sfree(area->values_pos); - sfree(area->instances_buffer); - sfree(area->values_buffer); - free(area); - } - - sfree(q_area->host); - sfree(q_area->plugin); - sfree(q_area->db_name); - - free(q_area); -} /* }}} void udb_query_delete_preparation_area */ diff --git a/src/utils_db_query.h b/src/utils_db_query.h deleted file mode 100644 index f173204c..00000000 --- a/src/utils_db_query.h +++ /dev/null @@ -1,85 +0,0 @@ -/** - * collectd - src/utils_db_query.h - * Copyright (C) 2008,2009 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_DB_QUERY_H -#define UTILS_DB_QUERY_H 1 - -/* - * Data types - */ -struct udb_query_s; -typedef struct udb_query_s udb_query_t; - -struct udb_query_preparation_area_s; -typedef struct udb_query_preparation_area_s udb_query_preparation_area_t; - -typedef int (*udb_query_create_callback_t)(udb_query_t *q, oconfig_item_t *ci); - -/* - * Public functions - */ -int udb_query_create(udb_query_t ***ret_query_list, size_t *ret_query_list_len, - oconfig_item_t *ci, udb_query_create_callback_t cb); -void udb_query_free(udb_query_t **query_list, size_t query_list_len); - -int udb_query_pick_from_list_by_name(const char *name, udb_query_t **src_list, - size_t src_list_len, - udb_query_t ***dst_list, - size_t *dst_list_len); -int udb_query_pick_from_list(oconfig_item_t *ci, udb_query_t **src_list, - size_t src_list_len, udb_query_t ***dst_list, - size_t *dst_list_len); - -const char *udb_query_get_name(udb_query_t *q); -const char *udb_query_get_statement(udb_query_t *q); - -void udb_query_set_user_data(udb_query_t *q, void *user_data); -void *udb_query_get_user_data(udb_query_t *q); - -/* - * udb_query_check_version - * - * Returns 0 if the query is NOT suitable for `version' and >0 if the - * query IS suitable. - */ -int udb_query_check_version(udb_query_t *q, unsigned int version); - -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); -int udb_query_handle_result(udb_query_t const *q, - udb_query_preparation_area_t *prep_area, - char **column_values); -void udb_query_finish_result(udb_query_t const *q, - udb_query_preparation_area_t *prep_area); - -udb_query_preparation_area_t * -udb_query_allocate_preparation_area(udb_query_t *q); -void udb_query_delete_preparation_area(udb_query_preparation_area_t *q_area); - -#endif /* UTILS_DB_QUERY_H */ diff --git a/src/utils_deq.h b/src/utils_deq.h deleted file mode 100644 index 3182baae..00000000 --- a/src/utils_deq.h +++ /dev/null @@ -1,214 +0,0 @@ -/** - * 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 deleted file mode 100644 index 7b20e139..00000000 --- a/src/utils_dns.c +++ /dev/null @@ -1,1171 +0,0 @@ -/* - * collectd - src/utils_dns.c - * Copyright (C) 2006 Florian octo Forster - * Copyright (C) 2002 The Measurement Factory, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of The Measurement Factory nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Authors: - * The Measurement Factory, Inc. - * Florian octo Forster - */ - -#define _DEFAULT_SOURCE -#define _BSD_SOURCE - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#if HAVE_NET_IF_ARP_H -#include -#endif -#if HAVE_NET_IF_H -#include -#endif -#if HAVE_NET_PPP_DEFS_H -#include -#endif -#if HAVE_NET_IF_PPP_H -#include -#endif - -#if HAVE_NETINET_IN_SYSTM_H -#include -#endif -#if HAVE_NETINET_IN_H -#include -#endif -#if HAVE_NETINET_IP6_H -#include -#endif -#if HAVE_NETINET_IF_ETHER_H -#include -#endif -#if HAVE_NETINET_IP_H -#include -#endif -#ifdef HAVE_NETINET_IP_VAR_H -#include -#endif -#if HAVE_NETINET_UDP_H -#include -#endif - -#if HAVE_ARPA_INET_H -#include -#endif -#if HAVE_ARPA_NAMESER_H -#include -#endif -#if HAVE_ARPA_NAMESER_COMPAT_H -#include -#endif - -#if HAVE_NETDB_H -#include -#endif - -#if HAVE_PCAP_H -#include -#endif - -#define PCAP_SNAPLEN 1460 -#ifndef ETHER_HDR_LEN -#define ETHER_ADDR_LEN 6 -#define ETHER_TYPE_LEN 2 -#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) -#endif -#ifndef ETHERTYPE_8021Q -#define ETHERTYPE_8021Q 0x8100 -#endif -#ifndef ETHERTYPE_IPV6 -#define ETHERTYPE_IPV6 0x86DD -#endif - -#ifndef PPP_ADDRESS_VAL -#define PPP_ADDRESS_VAL 0xff /* The address byte value */ -#endif -#ifndef PPP_CONTROL_VAL -#define PPP_CONTROL_VAL 0x03 /* The control byte value */ -#endif - -#if HAVE_STRUCT_UDPHDR_UH_DPORT && HAVE_STRUCT_UDPHDR_UH_SPORT -#define UDP_DEST uh_dport -#define UDP_SRC uh_sport -#elif HAVE_STRUCT_UDPHDR_DEST && HAVE_STRUCT_UDPHDR_SOURCE -#define UDP_DEST dest -#define UDP_SRC source -#else -#error "`struct udphdr' is unusable." -#endif - -#if HAVE_NETINET_IP6_H && HAVE_STRUCT_IP6_EXT -#define HAVE_IPV6 1 -#endif - -#include "utils_dns.h" - -/* - * Type definitions - */ -struct ip_list_s { - struct in6_addr addr; - void *data; - struct ip_list_s *next; -}; -typedef struct ip_list_s ip_list_t; - -typedef int(printer)(const char *, ...); - -/* - * flags/features for non-interactive mode - */ - -#ifndef T_A6 -#define T_A6 38 -#endif -#ifndef T_SRV -#define T_SRV 33 -#endif - -/* - * Global variables - */ - -#if HAVE_PCAP_H -static pcap_t *pcap_obj; -#endif - -static ip_list_t *IgnoreList; - -#if HAVE_PCAP_H -static void (*Callback)(const rfc1035_header_t *); - -static int query_count_intvl; -static int query_count_total; -#ifdef __OpenBSD__ -static struct bpf_timeval last_ts; -#else -static struct timeval last_ts; -#endif /* __OpenBSD__ */ -#endif /* HAVE_PCAP_H */ - -static int cmp_in6_addr(const struct in6_addr *a, const struct in6_addr *b) { - int i; - - assert(sizeof(struct in6_addr) == 16); - - for (i = 0; i < 16; i++) - if (a->s6_addr[i] != b->s6_addr[i]) - break; - - if (i >= 16) - return 0; - - return a->s6_addr[i] > b->s6_addr[i] ? 1 : -1; -} /* int cmp_addrinfo */ - -static inline int ignore_list_match(const struct in6_addr *addr) { - for (ip_list_t *ptr = IgnoreList; ptr != NULL; ptr = ptr->next) - if (cmp_in6_addr(addr, &ptr->addr) == 0) - return 1; - return 0; -} /* int ignore_list_match */ - -static void ignore_list_add(const struct in6_addr *addr) { - ip_list_t *new; - - if (ignore_list_match(addr) != 0) - return; - - new = malloc(sizeof(*new)); - if (new == NULL) { - perror("malloc"); - return; - } - - memcpy(&new->addr, addr, sizeof(struct in6_addr)); - new->next = IgnoreList; - - IgnoreList = new; -} /* void ignore_list_add */ - -void ignore_list_add_name(const char *name) { - struct addrinfo *ai_list; - struct in6_addr addr; - int status; - - status = getaddrinfo(name, NULL, NULL, &ai_list); - if (status != 0) - return; - - for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL; - ai_ptr = ai_ptr->ai_next) { - if (ai_ptr->ai_family == AF_INET) { - memset(&addr, '\0', sizeof(addr)); - addr.s6_addr[10] = 0xFF; - addr.s6_addr[11] = 0xFF; - memcpy(addr.s6_addr + 12, - &((struct sockaddr_in *)ai_ptr->ai_addr)->sin_addr, 4); - - ignore_list_add(&addr); - } else { - ignore_list_add(&((struct sockaddr_in6 *)ai_ptr->ai_addr)->sin6_addr); - } - } /* for */ - - freeaddrinfo(ai_list); -} - -#if HAVE_PCAP_H -static void in6_addr_from_buffer(struct in6_addr *ia, const void *buf, - size_t buf_len, int family) { - memset(ia, 0, sizeof(struct in6_addr)); - if ((AF_INET == family) && (sizeof(uint32_t) == buf_len)) { - ia->s6_addr[10] = 0xFF; - ia->s6_addr[11] = 0xFF; - memcpy(ia->s6_addr + 12, buf, buf_len); - } else if ((AF_INET6 == family) && (sizeof(struct in6_addr) == buf_len)) { - memcpy(ia, buf, buf_len); - } -} /* void in6_addr_from_buffer */ - -void dnstop_set_pcap_obj(pcap_t *po) { pcap_obj = po; } - -void dnstop_set_callback(void (*cb)(const rfc1035_header_t *)) { - Callback = cb; -} - -#define RFC1035_MAXLABELSZ 63 -static int rfc1035NameUnpack(const char *buf, size_t sz, off_t *off, char *name, - size_t ns) { - off_t no = 0; - unsigned char c; - size_t len; - static int loop_detect; - if (loop_detect > 2) - return 4; /* compression loop */ - if (ns == 0) - return 4; /* probably compression loop */ - do { - if ((*off) >= ((off_t)sz)) - break; - c = *(buf + (*off)); - if (c > 191) { - /* blasted compression */ - int rc; - unsigned short s; - off_t ptr; - memcpy(&s, buf + (*off), sizeof(s)); - s = ntohs(s); - (*off) += sizeof(s); - /* Sanity check */ - if ((*off) >= ((off_t)sz)) - return 1; /* message too short */ - ptr = s & 0x3FFF; - /* Make sure the pointer is inside this message */ - if (ptr >= ((off_t)sz)) - return 2; /* bad compression ptr */ - if (ptr < DNS_MSG_HDR_SZ) - return 2; /* bad compression ptr */ - loop_detect++; - rc = rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no); - loop_detect--; - return rc; - } else if (c > RFC1035_MAXLABELSZ) { - /* - * "(The 10 and 01 combinations are reserved for future use.)" - */ - return 3; /* reserved label/compression flags */ - } else { - (*off)++; - len = (size_t)c; - if (len == 0) - break; - if (len > (ns - 1)) - len = ns - 1; - if ((*off) + len > sz) - return 4; /* message is too short */ - if (no + len + 1 > ns) - return 5; /* qname would overflow name buffer */ - memcpy(name + no, buf + (*off), len); - (*off) += len; - no += len; - *(name + (no++)) = '.'; - } - } while (c > 0); - if (no > 0) - *(name + no - 1) = '\0'; - /* make sure we didn't allow someone to overflow the name buffer */ - assert(no <= ((off_t)ns)); - return 0; -} - -static int handle_dns(const char *buf, int len) { - rfc1035_header_t qh; - uint16_t us; - off_t offset; - char *t; - int status; - - /* The DNS header is 12 bytes long */ - if (len < DNS_MSG_HDR_SZ) - return 0; - - memcpy(&us, buf + 0, 2); - qh.id = ntohs(us); - - memcpy(&us, buf + 2, 2); - us = ntohs(us); - qh.qr = (us >> 15) & 0x01; - qh.opcode = (us >> 11) & 0x0F; - qh.aa = (us >> 10) & 0x01; - qh.tc = (us >> 9) & 0x01; - qh.rd = (us >> 8) & 0x01; - qh.ra = (us >> 7) & 0x01; - qh.z = (us >> 6) & 0x01; - qh.ad = (us >> 5) & 0x01; - qh.cd = (us >> 4) & 0x01; - qh.rcode = us & 0x0F; - - memcpy(&us, buf + 4, 2); - qh.qdcount = ntohs(us); - - memcpy(&us, buf + 6, 2); - qh.ancount = ntohs(us); - - memcpy(&us, buf + 8, 2); - qh.nscount = ntohs(us); - - memcpy(&us, buf + 10, 2); - qh.arcount = ntohs(us); - - offset = DNS_MSG_HDR_SZ; - memset(qh.qname, '\0', MAX_QNAME_SZ); - status = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ); - if (status != 0) { - INFO("utils_dns: handle_dns: rfc1035NameUnpack failed " - "with status %i.", - status); - return 0; - } - if ('\0' == qh.qname[0]) - sstrncpy(qh.qname, ".", sizeof(qh.qname)); - while ((t = strchr(qh.qname, '\n'))) - *t = ' '; - while ((t = strchr(qh.qname, '\r'))) - *t = ' '; - for (t = qh.qname; *t; t++) - *t = tolower((int)*t); - - memcpy(&us, buf + offset, 2); - qh.qtype = ntohs(us); - memcpy(&us, buf + offset + 2, 2); - qh.qclass = ntohs(us); - - qh.length = (uint16_t)len; - - if (Callback != NULL) - Callback(&qh); - - return 1; -} - -static int handle_udp(const struct udphdr *udp, int len) { - char buf[PCAP_SNAPLEN]; - if ((ntohs(udp->UDP_DEST) != 53) && (ntohs(udp->UDP_SRC) != 53)) - return 0; - memcpy(buf, udp + 1, len - sizeof(*udp)); - if (0 == handle_dns(buf, len - sizeof(*udp))) - return 0; - return 1; -} - -#if HAVE_IPV6 -static int handle_ipv6(struct ip6_hdr *ipv6, int len) { - char buf[PCAP_SNAPLEN]; - unsigned int offset; - int nexthdr; - - struct in6_addr c_src_addr; - uint16_t payload_len; - - if (0 > len) - return 0; - - offset = sizeof(struct ip6_hdr); - nexthdr = ipv6->ip6_nxt; - c_src_addr = ipv6->ip6_src; - payload_len = ntohs(ipv6->ip6_plen); - - if (ignore_list_match(&c_src_addr)) - return 0; - - /* Parse extension headers. This only handles the standard headers, as - * defined in RFC 2460, correctly. Fragments are discarded. */ - while ((IPPROTO_ROUTING == nexthdr) /* routing header */ - || (IPPROTO_HOPOPTS == nexthdr) /* Hop-by-Hop options. */ - || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */ - || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */ - || (IPPROTO_AH == nexthdr) /* destination options. */ - || (IPPROTO_ESP == nexthdr)) /* encapsulating security payload. */ - { - struct ip6_ext ext_hdr; - uint16_t ext_hdr_len; - - /* Catch broken packets */ - if ((offset + sizeof(struct ip6_ext)) > (unsigned int)len) - return 0; - - /* Cannot handle fragments. */ - if (IPPROTO_FRAGMENT == nexthdr) - return 0; - - memcpy(&ext_hdr, (char *)ipv6 + offset, sizeof(struct ip6_ext)); - nexthdr = ext_hdr.ip6e_nxt; - ext_hdr_len = (8 * (ntohs(ext_hdr.ip6e_len) + 1)); - - /* This header is longer than the packets payload.. WTF? */ - if (ext_hdr_len > payload_len) - return 0; - - offset += ext_hdr_len; - payload_len -= ext_hdr_len; - } /* while */ - - /* Catch broken and empty packets */ - if (((offset + payload_len) > (unsigned int)len) || (payload_len == 0) || - (payload_len > PCAP_SNAPLEN)) - return 0; - - if (IPPROTO_UDP != nexthdr) - return 0; - - memcpy(buf, (char *)ipv6 + offset, payload_len); - if (handle_udp((struct udphdr *)buf, payload_len) == 0) - return 0; - - return 1; /* Success */ -} /* int handle_ipv6 */ -/* #endif HAVE_IPV6 */ - -#else /* if !HAVE_IPV6 */ -static int handle_ipv6(__attribute__((unused)) void *pkg, - __attribute__((unused)) int len) { - return 0; -} -#endif /* !HAVE_IPV6 */ - -static int handle_ip(const struct ip *ip, int len) { - char buf[PCAP_SNAPLEN]; - int offset = ip->ip_hl << 2; - struct in6_addr c_src_addr; - struct in6_addr c_dst_addr; - - if (ip->ip_v == 6) - return handle_ipv6((void *)ip, len); - - in6_addr_from_buffer(&c_src_addr, &ip->ip_src.s_addr, - sizeof(ip->ip_src.s_addr), AF_INET); - in6_addr_from_buffer(&c_dst_addr, &ip->ip_dst.s_addr, - sizeof(ip->ip_dst.s_addr), AF_INET); - if (ignore_list_match(&c_src_addr)) - return 0; - if (IPPROTO_UDP != ip->ip_p) - return 0; - memcpy(buf, ((char *)ip) + offset, len - offset); - if (0 == handle_udp((struct udphdr *)buf, len - offset)) - return 0; - return 1; -} - -#if HAVE_NET_IF_PPP_H -static int handle_ppp(const u_char *pkt, int len) { - char buf[PCAP_SNAPLEN]; - unsigned short us; - unsigned short proto; - if (len < 2) - return 0; - if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) { - pkt += 2; /* ACFC not used */ - len -= 2; - } - if (len < 2) - return 0; - if (*pkt % 2) { - proto = *pkt; /* PFC is used */ - pkt++; - len--; - } else { - memcpy(&us, pkt, sizeof(us)); - proto = ntohs(us); - pkt += 2; - len -= 2; - } - if (ETHERTYPE_IP != proto && PPP_IP != proto) - return 0; - memcpy(buf, pkt, len); - return handle_ip((struct ip *)buf, len); -} -#endif /* HAVE_NET_IF_PPP_H */ - -static int handle_null(const u_char *pkt, int len) { - unsigned int family; - memcpy(&family, pkt, sizeof(family)); - if (AF_INET != family) - return 0; - return handle_ip((struct ip *)(pkt + 4), len - 4); -} - -#ifdef DLT_LOOP -static int handle_loop(const u_char *pkt, int len) { - unsigned int family; - memcpy(&family, pkt, sizeof(family)); - if (AF_INET != ntohl(family)) - return 0; - return handle_ip((struct ip *)(pkt + 4), len - 4); -} - -#endif - -#ifdef DLT_RAW -static int handle_raw(const u_char *pkt, int len) { - return handle_ip((struct ip *)pkt, len); -} - -#endif - -static int handle_ether(const u_char *pkt, int len) { - char buf[PCAP_SNAPLEN]; - struct ether_header *e = (void *)pkt; - unsigned short etype = ntohs(e->ether_type); - if (len < ETHER_HDR_LEN) - return 0; - pkt += ETHER_HDR_LEN; - len -= ETHER_HDR_LEN; - if (ETHERTYPE_8021Q == etype) { - etype = ntohs(*(unsigned short *)(pkt + 2)); - pkt += 4; - len -= 4; - } - if ((ETHERTYPE_IP != etype) && (ETHERTYPE_IPV6 != etype)) - return 0; - memcpy(buf, pkt, len); - if (ETHERTYPE_IPV6 == etype) - return handle_ipv6((void *)buf, len); - else - return handle_ip((struct ip *)buf, len); -} - -#ifdef DLT_LINUX_SLL -static int handle_linux_sll(const u_char *pkt, int len) { - struct sll_header { - uint16_t pkt_type; - uint16_t dev_type; - uint16_t addr_len; - uint8_t addr[8]; - uint16_t proto_type; - } * hdr; - uint16_t etype; - - if ((0 > len) || ((unsigned int)len < sizeof(struct sll_header))) - return 0; - - hdr = (struct sll_header *)pkt; - pkt = (u_char *)(hdr + 1); - len -= sizeof(struct sll_header); - - etype = ntohs(hdr->proto_type); - - if ((ETHERTYPE_IP != etype) && (ETHERTYPE_IPV6 != etype)) - return 0; - - if (ETHERTYPE_IPV6 == etype) - return handle_ipv6((void *)pkt, len); - else - return handle_ip((struct ip *)pkt, len); -} -#endif /* DLT_LINUX_SLL */ - -/* public function */ -void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, - const u_char *pkt) { - int status; - - if (hdr->caplen < ETHER_HDR_LEN) - return; - - switch (pcap_datalink(pcap_obj)) { - case DLT_EN10MB: - status = handle_ether(pkt, hdr->caplen); - break; -#if HAVE_NET_IF_PPP_H - case DLT_PPP: - status = handle_ppp(pkt, hdr->caplen); - break; -#endif -#ifdef DLT_LOOP - case DLT_LOOP: - status = handle_loop(pkt, hdr->caplen); - break; -#endif -#ifdef DLT_RAW - case DLT_RAW: - status = handle_raw(pkt, hdr->caplen); - break; -#endif -#ifdef DLT_LINUX_SLL - case DLT_LINUX_SLL: - status = handle_linux_sll(pkt, hdr->caplen); - break; -#endif - case DLT_NULL: - status = handle_null(pkt, hdr->caplen); - break; - - default: - ERROR("handle_pcap: unsupported data link type %d", - pcap_datalink(pcap_obj)); - status = 0; - break; - } /* switch (pcap_datalink(pcap_obj)) */ - - if (0 == status) - return; - - query_count_intvl++; - query_count_total++; - last_ts = hdr->ts; -} -#endif /* HAVE_PCAP_H */ - -const char *qtype_str(int t) { - static char buf[32]; - switch (t) { -#if (defined(__NAMESER)) && (__NAMESER >= 19991001) - case ns_t_a: - return "A"; - case ns_t_ns: - return "NS"; - case ns_t_md: - return "MD"; - case ns_t_mf: - return "MF"; - case ns_t_cname: - return "CNAME"; - case ns_t_soa: - return "SOA"; - case ns_t_mb: - return "MB"; - case ns_t_mg: - return "MG"; - case ns_t_mr: - return "MR"; - case ns_t_null: - return "NULL"; - case ns_t_wks: - return "WKS"; - case ns_t_ptr: - return "PTR"; - case ns_t_hinfo: - return "HINFO"; - case ns_t_minfo: - return "MINFO"; - case ns_t_mx: - return "MX"; - case ns_t_txt: - return "TXT"; - case ns_t_rp: - return "RP"; - case ns_t_afsdb: - return "AFSDB"; - case ns_t_x25: - return "X25"; - case ns_t_isdn: - return "ISDN"; - case ns_t_rt: - return "RT"; - case ns_t_nsap: - return "NSAP"; - case ns_t_nsap_ptr: - return "NSAP-PTR"; - case ns_t_sig: - return "SIG"; - case ns_t_key: - return "KEY"; - case ns_t_px: - return "PX"; - case ns_t_gpos: - return "GPOS"; - case ns_t_aaaa: - return "AAAA"; - case ns_t_loc: - return "LOC"; - case ns_t_nxt: - return "NXT"; - case ns_t_eid: - return "EID"; - case ns_t_nimloc: - return "NIMLOC"; - case ns_t_srv: - return "SRV"; - case ns_t_atma: - return "ATMA"; - case ns_t_naptr: - return "NAPTR"; - case ns_t_opt: - return "OPT"; -#if __NAMESER >= 19991006 - case ns_t_kx: - return "KX"; - case ns_t_cert: - return "CERT"; - case ns_t_a6: - return "A6"; - case ns_t_dname: - return "DNAME"; - case ns_t_sink: - return "SINK"; - case ns_t_tsig: - return "TSIG"; -#endif -#if __NAMESER >= 20090302 - case ns_t_apl: - return "APL"; - case ns_t_ds: - return "DS"; - case ns_t_sshfp: - return "SSHFP"; - case ns_t_ipseckey: - return "IPSECKEY"; - case ns_t_rrsig: - return "RRSIG"; - case ns_t_nsec: - return "NSEC"; - case ns_t_dnskey: - return "DNSKEY"; - case ns_t_dhcid: - return "DHCID"; - case ns_t_nsec3: - return "NSEC3"; - case ns_t_nsec3param: - return "NSEC3PARAM"; - case ns_t_hip: - return "HIP"; - case ns_t_spf: - return "SPF"; - case ns_t_ixfr: - return "IXFR"; -#endif - case ns_t_axfr: - return "AXFR"; - case ns_t_mailb: - return "MAILB"; - case ns_t_maila: - return "MAILA"; - case ns_t_any: - return "ANY"; -#if __NAMESER >= 19991006 - case ns_t_zxfr: - return "ZXFR"; -#endif -#if __NAMESER >= 20090302 - case ns_t_dlv: - return "DLV"; -#endif -/* #endif __NAMESER >= 19991001 */ -#elif (defined(__BIND)) && (__BIND >= 19950621) - case T_A: - return "A"; /* 1 ... */ - case T_NS: - return "NS"; - case T_MD: - return "MD"; - case T_MF: - return "MF"; - case T_CNAME: - return "CNAME"; - case T_SOA: - return "SOA"; - case T_MB: - return "MB"; - case T_MG: - return "MG"; - case T_MR: - return "MR"; - case T_NULL: - return "NULL"; - case T_WKS: - return "WKS"; - case T_PTR: - return "PTR"; - case T_HINFO: - return "HINFO"; - case T_MINFO: - return "MINFO"; - case T_MX: - return "MX"; - case T_TXT: - return "TXT"; - case T_RP: - return "RP"; - case T_AFSDB: - return "AFSDB"; - case T_X25: - return "X25"; - case T_ISDN: - return "ISDN"; - case T_RT: - return "RT"; - case T_NSAP: - return "NSAP"; - case T_NSAP_PTR: - return "NSAP_PTR"; - case T_SIG: - return "SIG"; - case T_KEY: - return "KEY"; - case T_PX: - return "PX"; - case T_GPOS: - return "GPOS"; - case T_AAAA: - return "AAAA"; - case T_LOC: - return "LOC"; - case T_NXT: - return "NXT"; - case T_EID: - return "EID"; - case T_NIMLOC: - return "NIMLOC"; - case T_SRV: - return "SRV"; - case T_ATMA: - return "ATMA"; - case T_NAPTR: - return "NAPTR"; /* ... 35 */ -#if (__BIND >= 19960801) - case T_KX: - return "KX"; /* 36 ... */ - case T_CERT: - return "CERT"; - case T_A6: - return "A6"; - case T_DNAME: - return "DNAME"; - case T_SINK: - return "SINK"; - case T_OPT: - return "OPT"; - case T_APL: - return "APL"; - case T_DS: - return "DS"; - case T_SSHFP: - return "SSHFP"; - case T_RRSIG: - return "RRSIG"; - case T_NSEC: - return "NSEC"; - case T_DNSKEY: - return "DNSKEY"; /* ... 48 */ - case T_TKEY: - return "TKEY"; /* 249 */ -#endif /* __BIND >= 19960801 */ - case T_TSIG: - return "TSIG"; /* 250 ... */ - case T_IXFR: - return "IXFR"; - case T_AXFR: - return "AXFR"; - case T_MAILB: - return "MAILB"; - case T_MAILA: - return "MAILA"; - case T_ANY: - return "ANY"; /* ... 255 */ -#endif /* __BIND >= 19950621 */ - default: - snprintf(buf, sizeof(buf), "#%i", t); - return buf; - } /* switch (t) */ -} - -const char *opcode_str(int o) { - static char buf[30]; - switch (o) { - case 0: - return "Query"; - case 1: - return "Iquery"; - case 2: - return "Status"; - case 4: - return "Notify"; - case 5: - return "Update"; - default: - snprintf(buf, sizeof(buf), "Opcode%d", o); - return buf; - } -} - -const char *rcode_str(int rcode) { - static char buf[32]; - switch (rcode) { -#if (defined(__NAMESER)) && (__NAMESER >= 19991006) - case ns_r_noerror: - return "NOERROR"; - case ns_r_formerr: - return "FORMERR"; - case ns_r_servfail: - return "SERVFAIL"; - case ns_r_nxdomain: - return "NXDOMAIN"; - case ns_r_notimpl: - return "NOTIMPL"; - case ns_r_refused: - return "REFUSED"; - case ns_r_yxdomain: - return "YXDOMAIN"; - case ns_r_yxrrset: - return "YXRRSET"; - case ns_r_nxrrset: - return "NXRRSET"; - case ns_r_notauth: - return "NOTAUTH"; - case ns_r_notzone: - return "NOTZONE"; - case ns_r_max: - return "MAX"; - case ns_r_badsig: - return "BADSIG"; - case ns_r_badkey: - return "BADKEY"; - case ns_r_badtime: - return "BADTIME"; -/* #endif __NAMESER >= 19991006 */ -#elif (defined(__BIND)) && (__BIND >= 19950621) - case NOERROR: - return "NOERROR"; - case FORMERR: - return "FORMERR"; - case SERVFAIL: - return "SERVFAIL"; - case NXDOMAIN: - return "NXDOMAIN"; - case NOTIMP: - return "NOTIMP"; - case REFUSED: - return "REFUSED"; -#if defined(YXDOMAIN) && defined(NXRRSET) - case YXDOMAIN: - return "YXDOMAIN"; - case YXRRSET: - return "YXRRSET"; - case NXRRSET: - return "NXRRSET"; - case NOTAUTH: - return "NOTAUTH"; - case NOTZONE: - return "NOTZONE"; -#endif /* RFC2136 rcodes */ -#endif /* __BIND >= 19950621 */ - default: - snprintf(buf, sizeof(buf), "RCode%i", rcode); - return buf; - } -} /* const char *rcode_str (int rcode) */ - -#if 0 -static int -main(int argc, char *argv[]) -{ - char errbuf[PCAP_ERRBUF_SIZE]; - int x; - struct stat sb; - int readfile_state = 0; - struct bpf_program fp; - - port53 = htons(53); - SubReport = Sources_report; - ignore_addr.s_addr = 0; - progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]); - srandom(time(NULL)); - ResetCounters(); - - while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) { - switch (x) { - case 'a': - anon_flag = 1; - break; - case 's': - sld_flag = 1; - break; - case 't': - nld_flag = 1; - break; - case 'p': - promisc_flag = 0; - break; - case 'b': - bpf_program_str = strdup(optarg); - break; - case 'i': - ignore_addr.s_addr = inet_addr(optarg); - break; - case 'f': - set_filter(optarg); - break; - default: - usage(); - break; - } - } - argc -= optind; - argv += optind; - - if (argc < 1) - usage(); - device = strdup(argv[0]); - - if (0 == stat(device, &sb)) - readfile_state = 1; - if (readfile_state) { - pcap_obj = pcap_open_offline(device, errbuf); - } else { - pcap_obj = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf); - } - if (NULL == pcap_obj) { - fprintf(stderr, "pcap_open_*: %s\n", errbuf); - exit(1); - } - - if (0 == isatty(1)) { - if (0 == readfile_state) { - fprintf(stderr, "Non-interactive mode requires savefile argument\n"); - exit(1); - } - interactive = 0; - print_func = printf; - } - - memset(&fp, '\0', sizeof(fp)); - x = pcap_compile(pcap_obj, &fp, bpf_program_str, 1, 0); - if (x < 0) { - fprintf(stderr, "pcap_compile failed\n"); - exit(1); - } - x = pcap_setfilter(pcap_obj, &fp); - if (x < 0) { - fprintf(stderr, "pcap_setfilter failed\n"); - exit(1); - } - - /* - * non-blocking call added for Mac OS X bugfix. Sent by Max Horn. - * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html - */ - x = pcap_setnonblock(pcap_obj, 1, errbuf); - if (x < 0) { - fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf); - exit(1); - } - - switch (pcap_datalink(pcap_obj)) { - case DLT_EN10MB: - handle_datalink = handle_ether; - break; -#if HAVE_NET_IF_PPP_H - case DLT_PPP: - handle_datalink = handle_ppp; - break; -#endif -#ifdef DLT_LOOP - case DLT_LOOP: - handle_datalink = handle_loop; - break; -#endif -#ifdef DLT_RAW - case DLT_RAW: - handle_datalink = handle_raw; - break; -#endif - case DLT_NULL: - handle_datalink = handle_null; - break; - default: - fprintf(stderr, "unsupported data link type %d\n", - pcap_datalink(pcap_obj)); - return 1; - break; - } - if (interactive) { - init_curses(); - while (0 == Quit) { - if (readfile_state < 2) { - /* - * On some OSes select() might return 0 even when - * there are packets to process. Thus, we always - * ignore its return value and just call pcap_dispatch() - * anyway. - */ - if (0 == readfile_state) /* interactive */ - pcap_select(pcap_obj, 1, 0); - x = pcap_dispatch(pcap_obj, 50, handle_pcap, NULL); - } - if (0 == x && 1 == readfile_state) { - /* block on keyboard until user quits */ - readfile_state++; - nodelay(w, 0); - } - keyboard(); - cron_pre(); - report(); - cron_post(); - } - endwin(); /* klin, Thu Nov 28 08:56:51 2002 */ - } else { - while (pcap_dispatch(pcap_obj, 50, handle_pcap, NULL)) - (void) 0; - cron_pre(); - Sources_report(); print_func("\n"); - Destinatioreport(); print_func("\n"); - Qtypes_report(); print_func("\n"); - Opcodes_report(); print_func("\n"); - Tld_report(); print_func("\n"); - Sld_report(); print_func("\n"); - Nld_report(); print_func("\n"); - SldBySource_report(); - } - - pcap_close(pcap_obj); - return 0; -} /* static int main(int argc, char *argv[]) */ -#endif diff --git a/src/utils_dns.h b/src/utils_dns.h deleted file mode 100644 index 9d9b75fd..00000000 --- a/src/utils_dns.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * collectd - src/utils_dns.h - * Copyright (C) 2006 Florian octo Forster - * Copyright (C) 2002 The Measurement Factory, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of The Measurement Factory nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Authors: - * The Measurement Factory, Inc. - * Florian octo Forster - */ - -#ifndef COLLECTD_UTILS_DNS_H -#define COLLECTD_UTILS_DNS_H 1 - -#include "config.h" - -#include -#include - -#if HAVE_PCAP_H -#include -#endif - -#define DNS_MSG_HDR_SZ 12 - -#define T_MAX 65536 -#define MAX_QNAME_SZ 512 - -struct rfc1035_header_s { - uint16_t id; - unsigned int qr : 1; - unsigned int opcode : 4; - unsigned int aa : 1; - unsigned int tc : 1; - unsigned int rd : 1; - unsigned int ra : 1; - unsigned int z : 1; - unsigned int ad : 1; - unsigned int cd : 1; - unsigned int rcode : 4; - uint16_t qdcount; - uint16_t ancount; - uint16_t nscount; - uint16_t arcount; - uint16_t qtype; - uint16_t qclass; - char qname[MAX_QNAME_SZ]; - uint16_t length; -}; -typedef struct rfc1035_header_s rfc1035_header_t; - -#if HAVE_PCAP_H -void dnstop_set_pcap_obj(pcap_t *po); -#endif -void dnstop_set_callback(void (*cb)(const rfc1035_header_t *)); - -void ignore_list_add_name(const char *name); -#if HAVE_PCAP_H -void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, - const u_char *pkt); -#endif - -const char *qtype_str(int t); -const char *opcode_str(int o); -const char *rcode_str(int r); - -#endif /* !COLLECTD_UTILS_DNS_H */ diff --git a/src/utils_dpdk.c b/src/utils_dpdk.c deleted file mode 100644 index 3591eae1..00000000 --- a/src/utils_dpdk.c +++ /dev/null @@ -1,885 +0,0 @@ -/* - * collectd - src/utils_dpdk.c - * MIT License - * - * Copyright(c) 2016 Intel Corporation. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Authors: - * Maryam Tahhan - * Harry van Haaren - * Taras Chornyi - * Serhiy Pshyk - * Krzysztof Matczak - */ - -#include "collectd.h" - -#include -#include -#include - -#include -#include -#include - -#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 -// we need to limit DPDK_MAX_BUFFER_SIZE value. -#define DPDK_MAX_BUFFER_SIZE 896 -#define DPDK_CDM_DEFAULT_TIMEOUT 10000 - -enum DPDK_HELPER_STATUS { - DPDK_HELPER_NOT_INITIALIZED = 0, - DPDK_HELPER_INITIALIZING, - DPDK_HELPER_WAITING_ON_PRIMARY, - DPDK_HELPER_INITIALIZING_EAL, - DPDK_HELPER_ALIVE_SENDING_EVENTS, - DPDK_HELPER_GRACEFUL_QUIT, -}; - -#define DPDK_HELPER_TRACE(_name) \ - DEBUG("%s:%s:%d pid=%ld", _name, __FUNCTION__, __LINE__, (long)getpid()) - -struct dpdk_helper_ctx_s { - - dpdk_eal_config_t eal_config; - int eal_initialized; - - size_t shm_size; - char shm_name[DATA_MAX_NAME_LEN]; - - sem_t sema_cmd_start; - sem_t sema_cmd_complete; - cdtime_t cmd_wait_time; - - pid_t pid; - int pipes[2]; - int status; - - int cmd; - int cmd_result; - - char priv_data[]; -}; - -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); - -static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc); -static int dpdk_helper_worker(dpdk_helper_ctx_t *phc); -static int dpdk_helper_eal_init(dpdk_helper_ctx_t *phc); -static int dpdk_helper_cmd_wait(dpdk_helper_ctx_t *phc, pid_t ppid); -static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc, - enum DPDK_HELPER_STATUS status); -static int dpdk_helper_exit(dpdk_helper_ctx_t *phc, - enum DPDK_HELPER_STATUS status); -static int dpdk_helper_status_check(dpdk_helper_ctx_t *phc); -static void dpdk_helper_config_default(dpdk_helper_ctx_t *phc); -static const char *dpdk_helper_status_str(enum DPDK_HELPER_STATUS status); - -static void dpdk_helper_config_default(dpdk_helper_ctx_t *phc) { - if (phc == NULL) - return; - - DPDK_HELPER_TRACE(phc->shm_name); - - snprintf(phc->eal_config.coremask, DATA_MAX_NAME_LEN, "%s", "0xf"); - snprintf(phc->eal_config.memory_channels, DATA_MAX_NAME_LEN, "%s", "1"); - snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN, "%s", - DPDK_DEFAULT_RTE_CONFIG); -} - -int dpdk_helper_eal_config_set(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) { - if (phc == NULL) { - ERROR("Invalid argument (phc)"); - return -EINVAL; - } - - DPDK_HELPER_TRACE(phc->shm_name); - - if (ec == NULL) { - ERROR("Invalid argument (ec)"); - return -EINVAL; - } - - memcpy(&phc->eal_config, ec, sizeof(phc->eal_config)); - - return 0; -} - -int dpdk_helper_eal_config_get(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) { - if (phc == NULL) { - ERROR("Invalid argument (phc)"); - return -EINVAL; - } - - DPDK_HELPER_TRACE(phc->shm_name); - - if (ec == NULL) { - ERROR("Invalid argument (ec)"); - return -EINVAL; - } - - memcpy(ec, &phc->eal_config, sizeof(*ec)); - - return 0; -} - -int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci) { - DPDK_HELPER_TRACE(phc->shm_name); - - if (phc == NULL) { - ERROR("Invalid argument (phc)"); - return -EINVAL; - } - - if (ci == NULL) { - ERROR("Invalid argument (ci)"); - return -EINVAL; - } - - int status = 0; - for (int i = 0; i < ci->children_num; i++) { - oconfig_item_t *child = ci->children + i; - - if (strcasecmp("Coremask", child->key) == 0) { - status = cf_util_get_string_buffer(child, phc->eal_config.coremask, - sizeof(phc->eal_config.coremask)); - DEBUG("dpdk_common: EAL:Coremask %s", phc->eal_config.coremask); - } else if (strcasecmp("MemoryChannels", child->key) == 0) { - status = - cf_util_get_string_buffer(child, phc->eal_config.memory_channels, - sizeof(phc->eal_config.memory_channels)); - DEBUG("dpdk_common: EAL:Memory Channels %s", - phc->eal_config.memory_channels); - } else if (strcasecmp("SocketMemory", child->key) == 0) { - status = cf_util_get_string_buffer(child, phc->eal_config.socket_memory, - sizeof(phc->eal_config.socket_memory)); - DEBUG("dpdk_common: EAL:Socket memory %s", phc->eal_config.socket_memory); - } else if (strcasecmp("FilePrefix", child->key) == 0) { - char prefix[DATA_MAX_NAME_LEN]; - - 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) { - status = cf_util_get_string_buffer(child, phc->eal_config.log_level, - sizeof(phc->eal_config.log_level)); - DEBUG("dpdk_common: EAL:LogLevel %s", phc->eal_config.log_level); - } else if (strcasecmp("RteDriverLibPath", child->key) == 0) { - status = cf_util_get_string_buffer( - child, phc->eal_config.rte_driver_lib_path, - sizeof(phc->eal_config.rte_driver_lib_path)); - DEBUG("dpdk_common: EAL:RteDriverLibPath %s", - phc->eal_config.rte_driver_lib_path); - } else { - ERROR("dpdk_common: Invalid '%s' configuration option", child->key); - status = -EINVAL; - } - - if (status != 0) { - ERROR("dpdk_common: Parsing EAL configuration failed"); - break; - } - } - - return status; -} - -static int dpdk_shm_init(const char *name, size_t size, void **map) { - DPDK_HELPER_TRACE(name); - - 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, STRERRNO); - *map = NULL; - return -1; - } - - int ret = ftruncate(fd, size); - if (ret != 0) { - WARNING("dpdk_shm_init: Failed to resize SHM:%s", STRERRNO); - close(fd); - *map = NULL; - dpdk_shm_cleanup(name, size, NULL); - return -1; - } - - *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", STRERRNO); - close(fd); - *map = NULL; - dpdk_shm_cleanup(name, size, NULL); - return -1; - } - /* File descriptor no longer needed for shared memory operations */ - close(fd); - memset(*map, 0, size); - - return 0; -} - -static void dpdk_shm_cleanup(const char *name, size_t size, void *map) { - DPDK_HELPER_TRACE(name); - - /* - * Call shm_unlink first, as 'name' might be no longer accessible after munmap - */ - if (shm_unlink(name)) - ERROR("shm_unlink failure %s", STRERRNO); - - if (map != NULL) { - if (munmap(map, size)) - ERROR("munmap failure %s", STRERRNO); - } -} - -void *dpdk_helper_priv_get(dpdk_helper_ctx_t *phc) { - if (phc) - return phc->priv_data; - - return NULL; -} - -int dpdk_helper_data_size_get(dpdk_helper_ctx_t *phc) { - if (phc == NULL) { - DPDK_CHILD_LOG("Invalid argument(phc)\n"); - return -EINVAL; - } - - return phc->shm_size - sizeof(dpdk_helper_ctx_t); -} - -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; - - if (pphc == NULL) { - ERROR("%s:Invalid argument(pphc)", __FUNCTION__); - return -EINVAL; - } - - if (name == NULL) { - ERROR("%s:Invalid argument(name)", __FUNCTION__); - return -EINVAL; - } - - DPDK_HELPER_TRACE(name); - - /* Allocate dpdk_helper_ctx_t and - * initialize a POSIX SHared Memory (SHM) object. - */ - int err = dpdk_shm_init(name, shm_size, (void **)&phc); - if (err != 0) { - return -errno; - } - - err = sem_init(&phc->sema_cmd_start, 1, 0); - if (err != 0) { - ERROR("sema_cmd_start semaphore init failed: %s", STRERRNO); - int errno_m = errno; - dpdk_shm_cleanup(name, shm_size, (void *)phc); - return -errno_m; - } - - err = sem_init(&phc->sema_cmd_complete, 1, 0); - if (err != 0) { - 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); - return -errno_m; - } - - phc->shm_size = shm_size; - sstrncpy(phc->shm_name, name, sizeof(phc->shm_name)); - - dpdk_helper_config_default(phc); - - *pphc = phc; - - return 0; -} - -void dpdk_helper_shutdown(dpdk_helper_ctx_t *phc) { - if (phc == NULL) - return; - - DPDK_HELPER_TRACE(phc->shm_name); - - close(phc->pipes[1]); - - if (phc->status != DPDK_HELPER_NOT_INITIALIZED) { - dpdk_helper_exit_command(phc, DPDK_HELPER_GRACEFUL_QUIT); - } - - sem_destroy(&phc->sema_cmd_start); - sem_destroy(&phc->sema_cmd_complete); - dpdk_shm_cleanup(phc->shm_name, phc->shm_size, (void *)phc); -} - -static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) { - if (phc == NULL) { - ERROR("Invalid argument(phc)"); - return -EINVAL; - } - - DPDK_HELPER_TRACE(phc->shm_name); - - phc->eal_initialized = 0; - phc->cmd_wait_time = MS_TO_CDTIME_T(DPDK_CDM_DEFAULT_TIMEOUT); - - /* - * Create a pipe for helper stdout back to collectd. This is necessary for - * logging EAL failures, as rte_eal_init() calls rte_panic(). - */ - if (phc->pipes[1]) { - DEBUG("dpdk_helper_spawn: collectd closing helper pipe %d", phc->pipes[1]); - } else { - DEBUG("dpdk_helper_spawn: collectd helper pipe %d, not closing", - phc->pipes[1]); - } - - if (pipe(phc->pipes) != 0) { - 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", 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", STRERRNO); - } - - pid_t pid = fork(); - if (pid > 0) { - phc->pid = pid; - close(phc->pipes[1]); - DEBUG("%s:dpdk_helper_spawn: helper pid %lu", phc->shm_name, - (long)phc->pid); - } else if (pid == 0) { - /* Replace stdout with a pipe to collectd. */ - close(phc->pipes[0]); - close(STDOUT_FILENO); - dup2(phc->pipes[1], STDOUT_FILENO); - DPDK_CHILD_TRACE(phc->shm_name); - dpdk_helper_worker(phc); - exit(0); - } else { - ERROR("dpdk_helper_start: Failed to fork helper process: %s", STRERRNO); - return -1; - } - - return 0; -} - -static int dpdk_helper_exit(dpdk_helper_ctx_t *phc, - enum DPDK_HELPER_STATUS status) { - DPDK_CHILD_LOG("%s:%s:%d %s\n", phc->shm_name, __FUNCTION__, __LINE__, - dpdk_helper_status_str(status)); - - close(phc->pipes[1]); - - phc->status = status; - - exit(0); - - return 0; -} - -static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc, - enum DPDK_HELPER_STATUS status) { - DPDK_HELPER_TRACE(phc->shm_name); - - close(phc->pipes[1]); - - if (phc->status == DPDK_HELPER_ALIVE_SENDING_EVENTS) { - phc->status = status; - DEBUG("%s:%s:%d %s", phc->shm_name, __FUNCTION__, __LINE__, - dpdk_helper_status_str(status)); - - int ret = dpdk_helper_command(phc, DPDK_CMD_QUIT, NULL, 0); - if (ret != 0) { - DEBUG("%s:%s:%d kill helper (pid=%lu)", phc->shm_name, __FUNCTION__, - __LINE__, (long)phc->pid); - - int err = kill(phc->pid, SIGKILL); - if (err) { - ERROR("%s error sending kill to helper: %s", __FUNCTION__, STRERRNO); - } - } - } else { - - DEBUG("%s:%s:%d kill helper (pid=%lu)", phc->shm_name, __FUNCTION__, - __LINE__, (long)phc->pid); - - int err = kill(phc->pid, SIGKILL); - if (err) { - ERROR("%s error sending kill to helper: %s", __FUNCTION__, STRERRNO); - } - } - - return 0; -} - -static int dpdk_helper_eal_init(dpdk_helper_ctx_t *phc) { - phc->status = DPDK_HELPER_INITIALIZING_EAL; - DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_INITIALIZING_EAL (start)\n", - phc->shm_name, __FUNCTION__, __LINE__); - - char *argp[DPDK_EAL_ARGC * 2 + 1]; - int argc = 0; - - /* EAL config must be initialized */ - assert(phc->eal_config.coremask[0] != 0); - assert(phc->eal_config.memory_channels[0] != 0); - assert(phc->eal_config.file_prefix[0] != 0); - - argp[argc++] = "collectd-dpdk"; - - argp[argc++] = "-c"; - argp[argc++] = phc->eal_config.coremask; - - argp[argc++] = "-n"; - argp[argc++] = phc->eal_config.memory_channels; - - if (strcasecmp(phc->eal_config.socket_memory, "") != 0) { - argp[argc++] = "--socket-mem"; - argp[argc++] = phc->eal_config.socket_memory; - } - - if (strcasecmp(phc->eal_config.file_prefix, DPDK_DEFAULT_RTE_CONFIG) != 0) { - argp[argc++] = "--file-prefix"; - argp[argc++] = phc->eal_config.file_prefix; - } - - argp[argc++] = "--proc-type"; - argp[argc++] = "secondary"; - - if (strcasecmp(phc->eal_config.log_level, "") != 0) { - argp[argc++] = "--log-level"; - argp[argc++] = phc->eal_config.log_level; - } - if (strcasecmp(phc->eal_config.rte_driver_lib_path, "") != 0) { - argp[argc++] = "-d"; - argp[argc++] = phc->eal_config.rte_driver_lib_path; - } - - assert(argc <= (DPDK_EAL_ARGC * 2 + 1)); - - int ret = rte_eal_init(argc, argp); - - if (ret < 0) { - - phc->eal_initialized = 0; - - DPDK_CHILD_LOG("dpdk_helper_eal_init: ERROR initializing EAL ret=%d\n", - ret); - - printf("dpdk_helper_eal_init: EAL arguments: "); - for (int i = 0; i < argc; i++) { - printf("%s ", argp[i]); - } - printf("\n"); - - return ret; - } - - phc->eal_initialized = 1; - - DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_INITIALIZING_EAL (done)\n", - phc->shm_name, __FUNCTION__, __LINE__); - - return 0; -} - -static int dpdk_helper_cmd_wait(dpdk_helper_ctx_t *phc, pid_t ppid) { - DPDK_CHILD_TRACE(phc->shm_name); - - struct timespec ts; - cdtime_t now = cdtime(); - cdtime_t cmd_wait_time = MS_TO_CDTIME_T(1500) + phc->cmd_wait_time * 2; - ts = CDTIME_T_TO_TIMESPEC(now + cmd_wait_time); - - int ret = sem_timedwait(&phc->sema_cmd_start, &ts); - DPDK_CHILD_LOG("%s:%s:%d pid=%lu got sema_cmd_start (ret=%d, errno=%d)\n", - phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(), ret, - errno); - - if (phc->cmd == DPDK_CMD_QUIT) { - DPDK_CHILD_LOG("%s:%s:%d pid=%lu exiting\n", phc->shm_name, __FUNCTION__, - __LINE__, (long)getpid()); - exit(0); - } else if (ret == -1 && errno == ETIMEDOUT) { - if (phc->status == DPDK_HELPER_ALIVE_SENDING_EVENTS) { - DPDK_CHILD_LOG("%s:dpdk_helper_cmd_wait: sem timedwait()" - " timeout, did collectd terminate?\n", - phc->shm_name); - dpdk_helper_exit(phc, DPDK_HELPER_GRACEFUL_QUIT); - } - } -#if COLLECT_DEBUG - int val = 0; - if (sem_getvalue(&phc->sema_cmd_start, &val) == 0) - DPDK_CHILD_LOG("%s:%s:%d pid=%lu wait sema_cmd_start (value=%d)\n", - phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(), val); -#endif - - /* Parent PID change means collectd died so quit the helper process. */ - if (ppid != getppid()) { - DPDK_CHILD_LOG("dpdk_helper_cmd_wait: parent PID changed, quitting.\n"); - dpdk_helper_exit(phc, DPDK_HELPER_GRACEFUL_QUIT); - } - - /* Checking for DPDK primary process. */ - if (!rte_eal_primary_proc_alive(phc->eal_config.file_prefix)) { - if (phc->eal_initialized) { - DPDK_CHILD_LOG( - "%s:dpdk_helper_cmd_wait: no primary alive but EAL initialized:" - " quitting.\n", - phc->shm_name); - dpdk_helper_exit(phc, DPDK_HELPER_NOT_INITIALIZED); - } - - phc->status = DPDK_HELPER_WAITING_ON_PRIMARY; - DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_WAITING_ON_PRIMARY\n", phc->shm_name, - __FUNCTION__, __LINE__); - - return -1; - } - - if (!phc->eal_initialized) { - int ret = dpdk_helper_eal_init(phc); - if (ret != 0) { - DPDK_CHILD_LOG("Error initializing EAL\n"); - dpdk_helper_exit(phc, DPDK_HELPER_NOT_INITIALIZED); - } - phc->status = DPDK_HELPER_ALIVE_SENDING_EVENTS; - DPDK_CHILD_LOG("%s:%s:%d DPDK_HELPER_ALIVE_SENDING_EVENTS\n", phc->shm_name, - __FUNCTION__, __LINE__); - return -1; - } - - return 0; -} - -static int dpdk_helper_worker(dpdk_helper_ctx_t *phc) { - DPDK_CHILD_TRACE(phc->shm_name); - - pid_t ppid = getppid(); - - while (1) { - if (dpdk_helper_cmd_wait(phc, ppid) == 0) { - DPDK_CHILD_LOG("%s:%s:%d DPDK command handle (cmd=%d, pid=%lu)\n", - phc->shm_name, __FUNCTION__, __LINE__, phc->cmd, - (long)getpid()); - phc->cmd_result = dpdk_helper_command_handler(phc, phc->cmd); - } else { - phc->cmd_result = -1; - } - - /* now kick collectd to get results */ - int err = sem_post(&phc->sema_cmd_complete); - DPDK_CHILD_LOG("%s:%s:%d post sema_cmd_complete (pid=%lu)\n", phc->shm_name, - __FUNCTION__, __LINE__, (long)getpid()); - if (err) { - DPDK_CHILD_LOG("dpdk_helper_worker: error posting sema_cmd_complete " - "semaphore (%s)\n", - STRERRNO); - } - -#if COLLECT_DEBUG - int val = 0; - if (sem_getvalue(&phc->sema_cmd_complete, &val) == 0) - DPDK_CHILD_LOG("%s:%s:%d pid=%lu sema_cmd_complete (value=%d)\n", - phc->shm_name, __FUNCTION__, __LINE__, (long)getpid(), - val); -#endif - - } /* while(1) */ - - return 0; -} - -static const char *dpdk_helper_status_str(enum DPDK_HELPER_STATUS status) { - switch (status) { - case DPDK_HELPER_ALIVE_SENDING_EVENTS: - return "DPDK_HELPER_ALIVE_SENDING_EVENTS"; - case DPDK_HELPER_WAITING_ON_PRIMARY: - return "DPDK_HELPER_WAITING_ON_PRIMARY"; - case DPDK_HELPER_INITIALIZING: - return "DPDK_HELPER_INITIALIZING"; - case DPDK_HELPER_INITIALIZING_EAL: - return "DPDK_HELPER_INITIALIZING_EAL"; - case DPDK_HELPER_GRACEFUL_QUIT: - return "DPDK_HELPER_GRACEFUL_QUIT"; - case DPDK_HELPER_NOT_INITIALIZED: - return "DPDK_HELPER_NOT_INITIALIZED"; - default: - return "UNKNOWN"; - } -} - -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)); - - if (phc->status == DPDK_HELPER_GRACEFUL_QUIT) { - return 0; - } else if (phc->status == DPDK_HELPER_NOT_INITIALIZED) { - phc->status = DPDK_HELPER_INITIALIZING; - DEBUG("%s:%s:%d DPDK_HELPER_INITIALIZING", phc->shm_name, __FUNCTION__, - __LINE__); - int err = dpdk_helper_spawn(phc); - if (err) { - ERROR("dpdkstat: error spawning helper %s", STRERRNO); - } - return -1; - } - - pid_t ws = waitpid(phc->pid, NULL, WNOHANG); - if (ws != 0) { - phc->status = DPDK_HELPER_INITIALIZING; - DEBUG("%s:%s:%d DPDK_HELPER_INITIALIZING", phc->shm_name, __FUNCTION__, - __LINE__); - int err = dpdk_helper_spawn(phc); - if (err) { - ERROR("dpdkstat: error spawning helper %s", STRERRNO); - } - return -1; - } - - if (phc->status == DPDK_HELPER_INITIALIZING_EAL) { - return -1; - } - - return 0; -} - -static void dpdk_helper_check_pipe(dpdk_helper_ctx_t *phc) { - char buf[DPDK_MAX_BUFFER_SIZE]; - char out[DPDK_MAX_BUFFER_SIZE]; - - /* non blocking check on helper logging pipe */ - struct pollfd fds = { - .fd = phc->pipes[0], .events = POLLIN, - }; - int data_avail = poll(&fds, 1, 0); - DEBUG("%s:dpdk_helper_check_pipe: poll data_avail=%d", phc->shm_name, - data_avail); - if (data_avail < 0) { - if (errno != EINTR || errno != EAGAIN) { - ERROR("%s: poll(2) failed: %s", phc->shm_name, STRERRNO); - } - } - while (data_avail) { - int nbytes = read(phc->pipes[0], buf, (sizeof(buf) - 1)); - DEBUG("%s:dpdk_helper_check_pipe: read nbytes=%d", phc->shm_name, nbytes); - if (nbytes <= 0) - break; - buf[nbytes] = '\0'; - sstrncpy(out, buf, sizeof(out)); - DEBUG("%s: helper process:\n%s", phc->shm_name, out); - } -} - -int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result, - cdtime_t cmd_wait_time) { - if (phc == NULL) { - ERROR("Invalid argument(phc)"); - return -EINVAL; - } - - DEBUG("%s:%s:%d pid=%lu, cmd=%d", phc->shm_name, __FUNCTION__, __LINE__, - (long)getpid(), cmd); - - phc->cmd_wait_time = cmd_wait_time; - - int ret = dpdk_helper_status_check(phc); - - dpdk_helper_check_pipe(phc); - - if (ret != 0) { - return ret; - } - - DEBUG("%s: DPDK command execute (cmd=%d)", phc->shm_name, cmd); - - phc->cmd_result = 0; - phc->cmd = cmd; - - /* kick helper to process command */ - int err = sem_post(&phc->sema_cmd_start); - if (err) { - ERROR("dpdk_helper_worker: error posting sema_cmd_start semaphore (%s)", - STRERRNO); - } - -#if COLLECT_DEBUG - int val = 0; - if (sem_getvalue(&phc->sema_cmd_start, &val) == 0) - DEBUG("%s:dpdk_helper_command: post sema_cmd_start (value=%d)", - phc->shm_name, val); -#endif - - if (phc->cmd != DPDK_CMD_QUIT) { - - /* wait for helper to complete processing */ - struct timespec ts; - cdtime_t now = cdtime(); - - if (phc->status != DPDK_HELPER_ALIVE_SENDING_EVENTS) { - cmd_wait_time = MS_TO_CDTIME_T(DPDK_CDM_DEFAULT_TIMEOUT); - } - - ts = CDTIME_T_TO_TIMESPEC(now + cmd_wait_time); - ret = sem_timedwait(&phc->sema_cmd_complete, &ts); - if (ret == -1 && errno == ETIMEDOUT) { - DPDK_HELPER_TRACE(phc->shm_name); - DEBUG("%s:sema_cmd_start: timeout in collectd thread: is a DPDK Primary " - "running?", - phc->shm_name); - return -ETIMEDOUT; - } - -#if COLLECT_DEBUG - val = 0; - if (sem_getvalue(&phc->sema_cmd_complete, &val) == 0) - DEBUG("%s:dpdk_helper_command: wait sema_cmd_complete (value=%d)", - phc->shm_name, val); -#endif - - if (result) { - *result = phc->cmd_result; - } - } - - dpdk_helper_check_pipe(phc); - - DEBUG("%s: DPDK command complete (cmd=%d, result=%d)", phc->shm_name, - phc->cmd, phc->cmd_result); - - return 0; -} - -uint64_t strtoull_safe(const char *str, int *err) { - uint64_t val = 0; - char *endptr; - int res = 0; - - val = strtoull(str, &endptr, 16); - if (*endptr) { - ERROR("%s Failed to parse the value %s, endptr=%c", __FUNCTION__, str, - *endptr); - res = -EINVAL; - } - if (err != NULL) - *err = res; - return val; -} - -uint128_t str_to_uint128(const char *str, int len) { - uint128_t lcore_mask; - int err = 0; - - memset(&lcore_mask, 0, sizeof(lcore_mask)); - - if (len <= 2 || strncmp(str, "0x", 2) != 0) { - ERROR("%s Value %s should be represened in hexadecimal format", - __FUNCTION__, str); - return lcore_mask; - } - /* If str is <= 64 bit long ('0x' + 16 chars = 18 chars) then - * conversion is straightforward. Otherwise str is splitted into 64b long - * blocks */ - if (len <= 18) { - lcore_mask.low = strtoull_safe(str, &err); - if (err) - return lcore_mask; - } else { - char low_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)); - - strncpy(high_str, str, len - 16); - strncpy(low_str, str + len - 16, 16); - - lcore_mask.low = strtoull_safe(low_str, &err); - if (err) - return lcore_mask; - - lcore_mask.high = strtoull_safe(high_str, &err); - if (err) { - lcore_mask.low = 0; - return lcore_mask; - } - } - return lcore_mask; -} - -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", - __FUNCTION__, __LINE__); - return ports; - } - - if (ports > RTE_MAX_ETHPORTS) { - ERROR("%s:%d: Number of DPDK ports (%u) is greater than " - "RTE_MAX_ETHPORTS=%d. Ignoring extra ports\n", - __FUNCTION__, __LINE__, ports, RTE_MAX_ETHPORTS); - ports = RTE_MAX_ETHPORTS; - } - - return ports; -} diff --git a/src/utils_dpdk.h b/src/utils_dpdk.h deleted file mode 100644 index d4551d86..00000000 --- a/src/utils_dpdk.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * collectd - src/utils_dpdk.h - * MIT License - * - * Copyright(c) 2016 Intel Corporation. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Authors: - * Maryam Tahhan - * Harry van Haaren - * Taras Chornyi - * Serhiy Pshyk - * Krzysztof Matczak - */ - -#ifndef UTILS_DPDK_H -#define UTILS_DPDK_H - -#include - -#define ERR_BUF_SIZE 1024 - -enum DPDK_CMD { - DPDK_CMD_NONE = 0, - DPDK_CMD_QUIT, - DPDK_CMD_INIT, - DPDK_CMD_GET_STATS, - DPDK_CMD_GET_EVENTS, - __DPDK_CMD_LAST, -}; - -struct dpdk_eal_config_s { - char coremask[DATA_MAX_NAME_LEN]; - char memory_channels[DATA_MAX_NAME_LEN]; - char socket_memory[DATA_MAX_NAME_LEN]; - char file_prefix[DATA_MAX_NAME_LEN]; - char log_level[DATA_MAX_NAME_LEN]; - char rte_driver_lib_path[PATH_MAX]; -}; -typedef struct dpdk_eal_config_s dpdk_eal_config_t; - -struct uint128_s { - u_int64_t high; - u_int64_t low; -}; -typedef struct uint128_s uint128_t; - -typedef struct dpdk_helper_ctx_s dpdk_helper_ctx_t; - -int dpdk_helper_init(const char *name, size_t data_size, - dpdk_helper_ctx_t **pphc); -void dpdk_helper_shutdown(dpdk_helper_ctx_t *phc); -int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci); -int dpdk_helper_eal_config_set(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec); -int dpdk_helper_eal_config_get(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec); -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(void); - -/* forward declaration of handler function that is called by helper from - * child process. not implemented in helper. must be provided by client. */ -int dpdk_helper_command_handler(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd); - -uint128_t str_to_uint128(const char *str, int len); - -/* logging functions that should be used in child process */ -#define DPDK_CHILD_LOG(...) fprintf(stdout, __VA_ARGS__) -#define DPDK_CHILD_TRACE(_name) \ - fprintf(stdout, "%s:%s:%d pid=%u\n", _name, __FUNCTION__, __LINE__, getpid()) - -#endif /* UTILS_DPDK_H */ diff --git a/src/utils_fbhash.c b/src/utils_fbhash.c index 366b44b8..7c342e53 100644 --- a/src/utils_fbhash.c +++ b/src/utils_fbhash.c @@ -28,7 +28,7 @@ #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" #include "utils_fbhash.h" struct fbhash_s { @@ -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 deleted file mode 100644 index de3f0c2e..00000000 --- a/src/utils_format_graphite.c +++ /dev/null @@ -1,335 +0,0 @@ -/** - * collectd - src/utils_format_graphite.c - * Copyright (C) 2012 Thomas Meson - * Copyright (C) 2012 Florian octo Forster - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; only version 2 of the License is applicable. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: - * Thomas Meson - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#include "utils_cache.h" -#include "utils_format_graphite.h" - -#define GRAPHITE_FORBIDDEN " \t\"\\:!/()\n\r" - -/* Utils functions to format data sets in graphite format. - * Largely taken from write_graphite.c as it remains the same formatting */ - -static int gr_format_values(char *ret, size_t ret_len, int ds_num, - const data_set_t *ds, const value_list_t *vl, - gauge_t const *rates) { - size_t offset = 0; - int status; - - assert(0 == strcmp(ds->type, vl->type)); - - memset(ret, 0, ret_len); - -#define BUFFER_ADD(...) \ - do { \ - status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \ - if (status < 1) { \ - return -1; \ - } else if (((size_t)status) >= (ret_len - offset)) { \ - return -1; \ - } else \ - offset += ((size_t)status); \ - } while (0) - - if (ds->ds[ds_num].type == DS_TYPE_GAUGE) - BUFFER_ADD(GAUGE_FORMAT, vl->values[ds_num].gauge); - else if (rates != NULL) - BUFFER_ADD("%f", rates[ds_num]); - else if (ds->ds[ds_num].type == DS_TYPE_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 { - P_ERROR("gr_format_values: Unknown data source type: %i", - ds->ds[ds_num].type); - return -1; - } - -#undef BUFFER_ADD - - return 0; -} - -static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len, - char escape_char, bool preserve_separator) { - memset(dst, 0, dst_len); - - if (src == NULL) - return; - - for (size_t i = 0; i < dst_len; i++) { - if (src[i] == 0) { - dst[i] = 0; - break; - } - - if ((!preserve_separator && (src[i] == '.')) || isspace((int)src[i]) || - iscntrl((int)src[i])) - dst[i] = escape_char; - else - dst[i] = src[i]; - } -} - -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, - 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[2 * DATA_MAX_NAME_LEN + 1]; - char tmp_type[2 * DATA_MAX_NAME_LEN + 1]; - - if (prefix == NULL) - prefix = ""; - - if (postfix == NULL) - postfix = ""; - - bool preserve_separator = (flags & GRAPHITE_PRESERVE_SEPARATOR); - - gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, - preserve_separator); - gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char, - preserve_separator); - gr_copy_escape_part(n_plugin_instance, vl->plugin_instance, - sizeof(n_plugin_instance), escape_char, - preserve_separator); - gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char, - preserve_separator); - gr_copy_escape_part(n_type_instance, vl->type_instance, - sizeof(n_type_instance), escape_char, preserve_separator); - - if (n_plugin_instance[0] != '\0') - snprintf(tmp_plugin, sizeof(tmp_plugin), "%s%c%s", n_plugin, - (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-', - n_plugin_instance); - else - sstrncpy(tmp_plugin, n_plugin, sizeof(tmp_plugin)); - - if (n_type_instance[0] != '\0') { - if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0) - sstrncpy(tmp_type, n_type_instance, sizeof(tmp_type)); - else - snprintf(tmp_type, sizeof(tmp_type), "%s%c%s", n_type, - (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-', - n_type_instance); - } else - sstrncpy(tmp_type, n_type, sizeof(tmp_type)); - - /* Assert always_append_ds -> ds_name */ - assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL)); - if (ds_name != NULL) { - if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && - strcmp(tmp_plugin, tmp_type) == 0) - snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix, - tmp_plugin, ds_name); - else - snprintf(ret, ret_len, "%s%s%s.%s.%s.%s", prefix, n_host, postfix, - tmp_plugin, tmp_type, ds_name); - } else - snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix, tmp_plugin, - tmp_type); - - return 0; -} - -static void escape_graphite_string(char *buffer, char escape_char) { - assert(strchr(GRAPHITE_FORBIDDEN, escape_char) == NULL); - - for (char *head = buffer + strcspn(buffer, GRAPHITE_FORBIDDEN); *head != '\0'; - head += strcspn(head, GRAPHITE_FORBIDDEN)) - *head = escape_char; -} - -int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds, - value_list_t const *vl, char const *prefix, - char const *postfix, char const escape_char, - unsigned int flags) { - int status = 0; - int buffer_pos = 0; - - gauge_t *rates = NULL; - if (flags & GRAPHITE_STORE_RATES) { - rates = uc_get_rate(ds, vl); - if (rates == NULL) { - P_ERROR("format_graphite: error with uc_get_rate"); - return -1; - } - } - - for (size_t i = 0; i < ds->ds_num; i++) { - char const *ds_name = NULL; - char key[10 * DATA_MAX_NAME_LEN]; - char values[512]; - size_t message_len; - char message[1024]; - - if ((flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds->ds_num > 1)) - ds_name = ds->ds[i].name; - - /* Copy the identifier to `key' and escape it. */ - 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) { - P_ERROR("format_graphite: error with gr_format_values"); - sfree(rates); - return status; - } - - /* Compute the graphite command */ - message_len = - (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)) { - 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) { - P_ERROR("format_graphite: target buffer too small"); - sfree(rates); - return -ENOMEM; - } - memcpy((void *)(buffer + buffer_pos), message, message_len); - buffer_pos += message_len; - buffer[buffer_pos] = '\0'; - } - sfree(rates); - return status; -} /* int format_graphite */ diff --git a/src/utils_format_graphite.h b/src/utils_format_graphite.h deleted file mode 100644 index 60b89ae7..00000000 --- a/src/utils_format_graphite.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * collectd - src/utils_format_graphite.h - * Copyright (C) 2012 Thomas Meson - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; only version 2 of the License is applicable. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: - * Thomas Meson - **/ - -#ifndef UTILS_FORMAT_GRAPHITE_H -#define UTILS_FORMAT_GRAPHITE_H 1 - -#include "collectd.h" - -#include "plugin.h" - -#define GRAPHITE_STORE_RATES 0x01 -#define GRAPHITE_SEPARATE_INSTANCES 0x02 -#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, - const char *postfix, const char escape_char, - unsigned int flags); - -#endif /* UTILS_FORMAT_GRAPHITE_H */ diff --git a/src/utils_format_graphite_test.c b/src/utils_format_graphite_test.c deleted file mode 100644 index 42efa681..00000000 --- a/src/utils_format_graphite_test.c +++ /dev/null @@ -1,207 +0,0 @@ -/** - * collectd - src/utils_format_graphite_test.c - * Copyright (C) 2016 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "collectd.h" - -#include "common.h" /* for STATIC_ARRAY_SIZE */ -#include "testing.h" -#include "utils_format_graphite.h" - -static data_set_t ds_single = { - .type = "single", - .ds_num = 1, - .ds = &(data_source_t){"value", DS_TYPE_GAUGE, NAN, NAN}, -}; - -/* -static data_set_t ds_double = { - .type = "double", - .ds_num = 2, - .ds = - (data_source_t[]){ - {"one", DS_TYPE_DERIVE, 0, NAN}, {"two", DS_TYPE_DERIVE, 0, NAN}, - }, -}; -*/ - -DEF_TEST(metric_name) { - struct { - const char *plugin_instance; - const char *type_instance; - const char *prefix; - const char *suffix; - unsigned int flags; - const char *want_name; - } cases[] = { - { - .want_name = "example@com.test.single", - }, - /* plugin and type instances */ - { - .plugin_instance = "foo", - .type_instance = "bar", - .want_name = "example@com.test-foo.single-bar", - }, - { - .plugin_instance = NULL, - .type_instance = "bar", - .want_name = "example@com.test.single-bar", - }, - { - .plugin_instance = "foo", - .type_instance = NULL, - .want_name = "example@com.test-foo.single", - }, - /* special chars */ - { - .plugin_instance = "foo (test)", - .type_instance = "test: \"hello\"", - .want_name = "example@com.test-foo@@test@.single-test@@@hello@", - }, - /* flag GRAPHITE_SEPARATE_INSTANCES */ - { - .plugin_instance = "foo", - .type_instance = "bar", - .flags = GRAPHITE_SEPARATE_INSTANCES, - .want_name = "example@com.test.foo.single.bar", - }, - /* flag GRAPHITE_ALWAYS_APPEND_DS */ - { - .plugin_instance = "foo", - .type_instance = "bar", - .flags = GRAPHITE_ALWAYS_APPEND_DS, - .want_name = "example@com.test-foo.single-bar.value", - }, - /* flag GRAPHITE_PRESERVE_SEPARATOR */ - { - .plugin_instance = "f.o.o", - .type_instance = "b.a.r", - .flags = 0, - .want_name = "example@com.test-f@o@o.single-b@a@r", - }, - { - .plugin_instance = "f.o.o", - .type_instance = "b.a.r", - .flags = GRAPHITE_PRESERVE_SEPARATOR, - .want_name = "example.com.test-f.o.o.single-b.a.r", - }, - /* prefix and suffix */ - { - .prefix = "foo.", - .suffix = ".bar", - .want_name = "foo.example@com.bar.test.single", - }, - { - .prefix = NULL, - .suffix = ".bar", - .want_name = "example@com.bar.test.single", - }, - { - .prefix = "foo.", - .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++) { - value_list_t vl = { - .values = &(value_t){.gauge = 42}, - .values_len = 1, - .time = TIME_T_TO_CDTIME_T_STATIC(1480063672), - .interval = TIME_T_TO_CDTIME_T_STATIC(10), - .host = "example.com", - .plugin = "test", - .type = "single", - }; - - char want[1024]; - snprintf(want, sizeof(want), "%s 42 1480063672\r\n", cases[i].want_name); - - if (cases[i].plugin_instance != NULL) - sstrncpy(vl.plugin_instance, cases[i].plugin_instance, - sizeof(vl.plugin_instance)); - if (cases[i].type_instance != NULL) - sstrncpy(vl.type_instance, cases[i].type_instance, - sizeof(vl.type_instance)); - - char got[1024]; - EXPECT_EQ_INT(0, format_graphite(got, sizeof(got), &ds_single, &vl, - cases[i].prefix, cases[i].suffix, '@', - cases[i].flags)); - EXPECT_EQ_STR(want, got); - } - - return 0; -} - -DEF_TEST(null_termination) { - value_list_t vl = { - .values = &(value_t){.gauge = 1337}, - .values_len = 1, - .time = TIME_T_TO_CDTIME_T_STATIC(1480063672), - .interval = TIME_T_TO_CDTIME_T_STATIC(10), - .host = "example.com", - .plugin = "test", - .type = "single", - }; - char const *want = "example_com.test.single 1337 1480063672\r\n"; - - char buffer[128]; - for (size_t i = 0; i < sizeof(buffer); i++) - buffer[i] = (char)i; - - EXPECT_EQ_INT(0, format_graphite(buffer, sizeof(buffer), &ds_single, &vl, - NULL, NULL, '_', 0)); - EXPECT_EQ_STR(want, buffer); - EXPECT_EQ_INT(0, buffer[strlen(want)]); - for (size_t i = strlen(want) + 1; i < sizeof(buffer); i++) - EXPECT_EQ_INT((int)i, (int)buffer[i]); - - return 0; -} - -int main(void) { - RUN_TEST(metric_name); - RUN_TEST(null_termination); - - END_TEST; -} diff --git a/src/utils_format_json.c b/src/utils_format_json.c deleted file mode 100644 index 49aa2299..00000000 --- a/src/utils_format_json.c +++ /dev/null @@ -1,696 +0,0 @@ -/** - * collectd - src/utils_format_json.c - * Copyright (C) 2009-2015 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "utils_format_json.h" - -#include "common.h" -#include "plugin.h" -#include "utils_cache.h" - -#if HAVE_LIBYAJL -#include -#include -#if HAVE_YAJL_YAJL_VERSION_H -#include -#endif -#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1) -#define HAVE_YAJL_V2 1 -#endif -#endif - -static int json_escape_string(char *buffer, size_t buffer_size, /* {{{ */ - const char *string) { - size_t dst_pos; - - if ((buffer == NULL) || (string == NULL)) - return -EINVAL; - - if (buffer_size < 3) - return -ENOMEM; - - dst_pos = 0; - -#define BUFFER_ADD(c) \ - do { \ - if (dst_pos >= (buffer_size - 1)) { \ - buffer[buffer_size - 1] = 0; \ - return -ENOMEM; \ - } \ - buffer[dst_pos] = (c); \ - dst_pos++; \ - } while (0) - - /* Escape special characters */ - BUFFER_ADD('"'); - for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) { - if ((string[src_pos] == '"') || (string[src_pos] == '\\')) { - BUFFER_ADD('\\'); - BUFFER_ADD(string[src_pos]); - } else if (string[src_pos] <= 0x001F) - BUFFER_ADD('?'); - else - BUFFER_ADD(string[src_pos]); - } /* for */ - BUFFER_ADD('"'); - buffer[dst_pos] = 0; - -#undef BUFFER_ADD - - return 0; -} /* }}} int json_escape_string */ - -static int values_to_json(char *buffer, size_t buffer_size, /* {{{ */ - const data_set_t *ds, const value_list_t *vl, - int store_rates) { - size_t offset = 0; - gauge_t *rates = NULL; - - memset(buffer, 0, buffer_size); - -#define BUFFER_ADD(...) \ - do { \ - int status; \ - status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ - if (status < 1) { \ - sfree(rates); \ - return -1; \ - } else if (((size_t)status) >= (buffer_size - offset)) { \ - sfree(rates); \ - return -ENOMEM; \ - } else \ - offset += ((size_t)status); \ - } while (0) - - BUFFER_ADD("["); - for (size_t i = 0; i < ds->ds_num; i++) { - if (i > 0) - BUFFER_ADD(","); - - if (ds->ds[i].type == DS_TYPE_GAUGE) { - if (isfinite(vl->values[i].gauge)) - BUFFER_ADD(JSON_GAUGE_FORMAT, vl->values[i].gauge); - else - BUFFER_ADD("null"); - } else if (store_rates) { - if (rates == NULL) - rates = uc_get_rate(ds, vl); - if (rates == NULL) { - WARNING("utils_format_json: uc_get_rate failed."); - sfree(rates); - return -1; - } - - if (isfinite(rates[i])) - BUFFER_ADD(JSON_GAUGE_FORMAT, rates[i]); - else - BUFFER_ADD("null"); - } else if (ds->ds[i].type == DS_TYPE_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) - BUFFER_ADD("%" PRIu64, vl->values[i].absolute); - else { - ERROR("format_json: Unknown data source type: %i", ds->ds[i].type); - sfree(rates); - return -1; - } - } /* for ds->ds_num */ - BUFFER_ADD("]"); - -#undef BUFFER_ADD - - sfree(rates); - return 0; -} /* }}} int values_to_json */ - -static int dstypes_to_json(char *buffer, size_t buffer_size, /* {{{ */ - const data_set_t *ds) { - size_t offset = 0; - - memset(buffer, 0, buffer_size); - -#define BUFFER_ADD(...) \ - do { \ - int status; \ - status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ - if (status < 1) \ - return -1; \ - else if (((size_t)status) >= (buffer_size - offset)) \ - return -ENOMEM; \ - else \ - offset += ((size_t)status); \ - } while (0) - - BUFFER_ADD("["); - for (size_t i = 0; i < ds->ds_num; i++) { - if (i > 0) - BUFFER_ADD(","); - - BUFFER_ADD("\"%s\"", DS_TYPE_TO_STRING(ds->ds[i].type)); - } /* for ds->ds_num */ - BUFFER_ADD("]"); - -#undef BUFFER_ADD - - return 0; -} /* }}} int dstypes_to_json */ - -static int dsnames_to_json(char *buffer, size_t buffer_size, /* {{{ */ - const data_set_t *ds) { - size_t offset = 0; - - memset(buffer, 0, buffer_size); - -#define BUFFER_ADD(...) \ - do { \ - int status; \ - status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ - if (status < 1) \ - return -1; \ - else if (((size_t)status) >= (buffer_size - offset)) \ - return -ENOMEM; \ - else \ - offset += ((size_t)status); \ - } while (0) - - BUFFER_ADD("["); - for (size_t i = 0; i < ds->ds_num; i++) { - if (i > 0) - BUFFER_ADD(","); - - BUFFER_ADD("\"%s\"", ds->ds[i].name); - } /* for ds->ds_num */ - BUFFER_ADD("]"); - -#undef BUFFER_ADD - - return 0; -} /* }}} int dsnames_to_json */ - -static int meta_data_keys_to_json(char *buffer, size_t buffer_size, /* {{{ */ - meta_data_t *meta, char **keys, - size_t keys_num) { - size_t offset = 0; - int status; - - buffer[0] = 0; - -#define BUFFER_ADD(...) \ - do { \ - status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ - if (status < 1) \ - return -1; \ - else if (((size_t)status) >= (buffer_size - offset)) \ - return -ENOMEM; \ - else \ - offset += ((size_t)status); \ - } while (0) - - for (size_t i = 0; i < keys_num; ++i) { - int type; - char *key = keys[i]; - - type = meta_data_type(meta, key); - if (type == MD_TYPE_STRING) { - char *value = NULL; - if (meta_data_get_string(meta, key, &value) == 0) { - char temp[512] = ""; - - status = json_escape_string(temp, sizeof(temp), value); - sfree(value); - if (status != 0) - return status; - - BUFFER_ADD(",\"%s\":%s", key, temp); - } - } else if (type == MD_TYPE_SIGNED_INT) { - int64_t value = 0; - if (meta_data_get_signed_int(meta, key, &value) == 0) - BUFFER_ADD(",\"%s\":%" PRIi64, key, value); - } else if (type == MD_TYPE_UNSIGNED_INT) { - uint64_t value = 0; - if (meta_data_get_unsigned_int(meta, key, &value) == 0) - BUFFER_ADD(",\"%s\":%" PRIu64, key, value); - } else if (type == MD_TYPE_DOUBLE) { - double value = 0.0; - if (meta_data_get_double(meta, key, &value) == 0) - BUFFER_ADD(",\"%s\":%f", key, value); - } else if (type == MD_TYPE_BOOLEAN) { - bool value = false; - if (meta_data_get_boolean(meta, key, &value) == 0) - BUFFER_ADD(",\"%s\":%s", key, value ? "true" : "false"); - } - } /* for (keys) */ - - if (offset == 0) - return ENOENT; - - buffer[0] = '{'; /* replace leading ',' */ - BUFFER_ADD("}"); - -#undef BUFFER_ADD - - return 0; -} /* }}} int meta_data_keys_to_json */ - -static int meta_data_to_json(char *buffer, size_t buffer_size, /* {{{ */ - meta_data_t *meta) { - char **keys = NULL; - size_t keys_num; - int status; - - if ((buffer == NULL) || (buffer_size == 0) || (meta == NULL)) - return EINVAL; - - status = meta_data_toc(meta, &keys); - if (status <= 0) - return status; - keys_num = (size_t)status; - - status = meta_data_keys_to_json(buffer, buffer_size, meta, keys, keys_num); - - for (size_t i = 0; i < keys_num; ++i) - sfree(keys[i]); - sfree(keys); - - return status; -} /* }}} int meta_data_to_json */ - -static int value_list_to_json(char *buffer, size_t buffer_size, /* {{{ */ - const data_set_t *ds, const value_list_t *vl, - int store_rates) { - char temp[512]; - size_t offset = 0; - int status; - - memset(buffer, 0, buffer_size); - -#define BUFFER_ADD(...) \ - do { \ - status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ - if (status < 1) \ - return -1; \ - else if (((size_t)status) >= (buffer_size - offset)) \ - return -ENOMEM; \ - else \ - offset += ((size_t)status); \ - } while (0) - - /* All value lists have a leading comma. The first one will be replaced with - * a square bracket in `format_json_finalize'. */ - BUFFER_ADD(",{"); - - status = values_to_json(temp, sizeof(temp), ds, vl, store_rates); - if (status != 0) - return status; - BUFFER_ADD("\"values\":%s", temp); - - status = dstypes_to_json(temp, sizeof(temp), ds); - if (status != 0) - return status; - BUFFER_ADD(",\"dstypes\":%s", temp); - - status = dsnames_to_json(temp, sizeof(temp), ds); - if (status != 0) - return status; - BUFFER_ADD(",\"dsnames\":%s", temp); - - BUFFER_ADD(",\"time\":%.3f", CDTIME_T_TO_DOUBLE(vl->time)); - BUFFER_ADD(",\"interval\":%.3f", CDTIME_T_TO_DOUBLE(vl->interval)); - -#define BUFFER_ADD_KEYVAL(key, value) \ - do { \ - status = json_escape_string(temp, sizeof(temp), (value)); \ - if (status != 0) \ - return status; \ - BUFFER_ADD(",\"%s\":%s", (key), temp); \ - } while (0) - - BUFFER_ADD_KEYVAL("host", vl->host); - BUFFER_ADD_KEYVAL("plugin", vl->plugin); - BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance); - BUFFER_ADD_KEYVAL("type", vl->type); - BUFFER_ADD_KEYVAL("type_instance", vl->type_instance); - - if (vl->meta != NULL) { - char meta_buffer[buffer_size]; - memset(meta_buffer, 0, sizeof(meta_buffer)); - status = meta_data_to_json(meta_buffer, sizeof(meta_buffer), vl->meta); - if (status != 0) - return status; - - BUFFER_ADD(",\"meta\":%s", meta_buffer); - } /* if (vl->meta != NULL) */ - - BUFFER_ADD("}"); - -#undef BUFFER_ADD_KEYVAL -#undef BUFFER_ADD - - return 0; -} /* }}} int value_list_to_json */ - -static int format_json_value_list_nocheck(char *buffer, /* {{{ */ - size_t *ret_buffer_fill, - size_t *ret_buffer_free, - const data_set_t *ds, - const value_list_t *vl, - int store_rates, size_t temp_size) { - char temp[temp_size]; - int status; - - status = value_list_to_json(temp, sizeof(temp), ds, vl, store_rates); - if (status != 0) - return status; - temp_size = strlen(temp); - - memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1); - (*ret_buffer_fill) += temp_size; - (*ret_buffer_free) -= temp_size; - - return 0; -} /* }}} int format_json_value_list_nocheck */ - -int format_json_initialize(char *buffer, /* {{{ */ - size_t *ret_buffer_fill, size_t *ret_buffer_free) { - size_t buffer_fill; - size_t buffer_free; - - if ((buffer == NULL) || (ret_buffer_fill == NULL) || - (ret_buffer_free == NULL)) - return -EINVAL; - - buffer_fill = *ret_buffer_fill; - buffer_free = *ret_buffer_free; - - buffer_free = buffer_fill + buffer_free; - buffer_fill = 0; - - if (buffer_free < 3) - return -ENOMEM; - - memset(buffer, 0, buffer_free); - *ret_buffer_fill = buffer_fill; - *ret_buffer_free = buffer_free; - - return 0; -} /* }}} int format_json_initialize */ - -int format_json_finalize(char *buffer, /* {{{ */ - size_t *ret_buffer_fill, size_t *ret_buffer_free) { - size_t pos; - - if ((buffer == NULL) || (ret_buffer_fill == NULL) || - (ret_buffer_free == NULL)) - return -EINVAL; - - if (*ret_buffer_free < 2) - return -ENOMEM; - - /* Replace the leading comma added in `value_list_to_json' with a square - * bracket. */ - if (buffer[0] != ',') - return -EINVAL; - buffer[0] = '['; - - pos = *ret_buffer_fill; - buffer[pos] = ']'; - buffer[pos + 1] = 0; - - (*ret_buffer_fill)++; - (*ret_buffer_free)--; - - return 0; -} /* }}} int format_json_finalize */ - -int format_json_value_list(char *buffer, /* {{{ */ - size_t *ret_buffer_fill, size_t *ret_buffer_free, - const data_set_t *ds, const value_list_t *vl, - int store_rates) { - if ((buffer == NULL) || (ret_buffer_fill == NULL) || - (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL)) - return -EINVAL; - - if (*ret_buffer_free < 3) - return -ENOMEM; - - return format_json_value_list_nocheck(buffer, ret_buffer_fill, - ret_buffer_free, ds, vl, store_rates, - (*ret_buffer_free) - 2); -} /* }}} int format_json_value_list */ - -#if HAVE_LIBYAJL -static int json_add_string(yajl_gen g, char const *str) /* {{{ */ -{ - if (str == NULL) - return (int)yajl_gen_null(g); - - return (int)yajl_gen_string(g, (const unsigned char *)str, - (unsigned int)strlen(str)); -} /* }}} int json_add_string */ - -#define JSON_ADD(g, str) \ - do { \ - yajl_gen_status status = json_add_string(g, str); \ - if (status != yajl_gen_status_ok) { \ - return -1; \ - } \ - } while (0) - -#define JSON_ADDF(g, format, ...) \ - do { \ - char *str = ssnprintf_alloc(format, __VA_ARGS__); \ - yajl_gen_status status = json_add_string(g, str); \ - free(str); \ - if (status != yajl_gen_status_ok) { \ - return -1; \ - } \ - } while (0) - -#define CHECK_SUCCESS(cmd) \ - do { \ - yajl_gen_status s = (cmd); \ - if (s != yajl_gen_status_ok) { \ - return (int)s; \ - } \ - } while (0) - -static int format_json_meta(yajl_gen g, notification_meta_t *meta) /* {{{ */ -{ - if (meta == NULL) - return 0; - - JSON_ADD(g, meta->name); - switch (meta->type) { - case NM_TYPE_STRING: - JSON_ADD(g, meta->nm_value.nm_string); - break; - case NM_TYPE_SIGNED_INT: - JSON_ADDF(g, "%" PRIi64, meta->nm_value.nm_signed_int); - break; - case NM_TYPE_UNSIGNED_INT: - JSON_ADDF(g, "%" PRIu64, meta->nm_value.nm_unsigned_int); - break; - case NM_TYPE_DOUBLE: - JSON_ADDF(g, JSON_GAUGE_FORMAT, meta->nm_value.nm_double); - break; - case NM_TYPE_BOOLEAN: - JSON_ADD(g, meta->nm_value.nm_boolean ? "true" : "false"); - break; - default: - ERROR("format_json_meta: unknown meta data type %d (name \"%s\")", - meta->type, meta->name); - CHECK_SUCCESS(yajl_gen_null(g)); - } - - return format_json_meta(g, meta->next); -} /* }}} int format_json_meta */ - -static int format_time(yajl_gen g, cdtime_t t) /* {{{ */ -{ - char buffer[RFC3339NANO_SIZE] = ""; - - if (rfc3339nano(buffer, sizeof(buffer), t) != 0) - return -1; - - JSON_ADD(g, buffer); - return 0; -} /* }}} int format_time */ - -static int format_alert(yajl_gen g, notification_t const *n) /* {{{ */ -{ - CHECK_SUCCESS(yajl_gen_array_open(g)); /* BEGIN array */ - CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN alert */ - - /* - * labels - */ - JSON_ADD(g, "labels"); - CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN labels */ - - JSON_ADD(g, "alertname"); - if (strncmp(n->plugin, n->type, strlen(n->plugin)) == 0) - JSON_ADDF(g, "collectd_%s", n->type); - else - JSON_ADDF(g, "collectd_%s_%s", n->plugin, n->type); - - JSON_ADD(g, "instance"); - JSON_ADD(g, n->host); - - /* mangling of plugin instance and type instance into labels is copied from - * the Prometheus collectd exporter. */ - if (strlen(n->plugin_instance) > 0) { - JSON_ADD(g, n->plugin); - JSON_ADD(g, n->plugin_instance); - } - if (strlen(n->type_instance) > 0) { - if (strlen(n->plugin_instance) > 0) - JSON_ADD(g, "type"); - else - JSON_ADD(g, n->plugin); - JSON_ADD(g, n->type_instance); - } - - JSON_ADD(g, "severity"); - JSON_ADD(g, - (n->severity == NOTIF_FAILURE) - ? "FAILURE" - : (n->severity == NOTIF_WARNING) - ? "WARNING" - : (n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN"); - - JSON_ADD(g, "service"); - JSON_ADD(g, "collectd"); - - CHECK_SUCCESS(yajl_gen_map_close(g)); /* END labels */ - - /* - * annotations - */ - JSON_ADD(g, "annotations"); - CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN annotations */ - - JSON_ADD(g, "summary"); - JSON_ADD(g, n->message); - - if (format_json_meta(g, n->meta) != 0) { - return -1; - } - - CHECK_SUCCESS(yajl_gen_map_close(g)); /* END annotations */ - - JSON_ADD(g, "startsAt"); - if (format_time(g, n->time) != 0) { - return -1; - } - - CHECK_SUCCESS(yajl_gen_map_close(g)); /* END alert */ - CHECK_SUCCESS(yajl_gen_array_close(g)); /* END array */ - - return 0; -} /* }}} format_alert */ - -/* - * Format (prometheus/alertmanager v1): - * - * [{ - * "labels": { - * "alertname": "collectd_cpu", - * "instance": "host.example.com", - * "severity": "FAILURE", - * "service": "collectd", - * "cpu": "0", - * "type": "wait" - * }, - * "annotations": { - * "summary": "...", - * // meta - * }, - * "startsAt": , - * "endsAt": , // not used - * }] - */ -int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */ - notification_t const *n) { - yajl_gen g; - unsigned char const *out; -#if HAVE_YAJL_V2 - size_t unused_out_len; -#else - unsigned int unused_out_len; -#endif - - if ((buffer == NULL) || (n == NULL)) - return EINVAL; - -#if HAVE_YAJL_V2 - g = yajl_gen_alloc(NULL); - if (g == NULL) - return -1; -#if COLLECT_DEBUG - yajl_gen_config(g, yajl_gen_beautify, 1); - yajl_gen_config(g, yajl_gen_validate_utf8, 1); -#endif - -#else /* !HAVE_YAJL_V2 */ - yajl_gen_config conf = {0}; -#if COLLECT_DEBUG - conf.beautify = 1; - conf.indentString = " "; -#endif - g = yajl_gen_alloc(&conf, NULL); - if (g == NULL) - return -1; -#endif - - if (format_alert(g, n) != 0) { - yajl_gen_clear(g); - yajl_gen_free(g); - return -1; - } - - /* copy to output buffer */ - if (yajl_gen_get_buf(g, &out, &unused_out_len) != yajl_gen_status_ok) { - yajl_gen_clear(g); - yajl_gen_free(g); - return -1; - } - sstrncpy(buffer, (void *)out, buffer_size); - - yajl_gen_clear(g); - yajl_gen_free(g); - return 0; -} /* }}} format_json_notification */ -#else -int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */ - notification_t const *n) { - ERROR("format_json_notification: Not available (requires libyajl)."); - return ENOTSUP; -} /* }}} int format_json_notification */ -#endif diff --git a/src/utils_format_json.h b/src/utils_format_json.h deleted file mode 100644 index d3d02160..00000000 --- a/src/utils_format_json.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * collectd - src/utils_format_json.h - * Copyright (C) 2009 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_FORMAT_JSON_H -#define UTILS_FORMAT_JSON_H 1 - -#include "collectd.h" - -#include "plugin.h" - -#ifndef JSON_GAUGE_FORMAT -#define JSON_GAUGE_FORMAT GAUGE_FORMAT -#endif - -int format_json_initialize(char *buffer, size_t *ret_buffer_fill, - size_t *ret_buffer_free); -int format_json_value_list(char *buffer, size_t *ret_buffer_fill, - size_t *ret_buffer_free, const data_set_t *ds, - const value_list_t *vl, int store_rates); -int format_json_finalize(char *buffer, size_t *ret_buffer_fill, - size_t *ret_buffer_free); -int format_json_notification(char *buffer, size_t buffer_size, - notification_t const *n); - -#endif /* UTILS_FORMAT_JSON_H */ diff --git a/src/utils_format_json_test.c b/src/utils_format_json_test.c deleted file mode 100644 index b230ef35..00000000 --- a/src/utils_format_json_test.c +++ /dev/null @@ -1,183 +0,0 @@ -/** - * collectd - src/utils_format_json_test.c - * Copyright (C) 2015 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -/* Workaround for Solaris 10 defining label_t - * Issue #1301 - */ - -#include "config.h" -#if KERNEL_SOLARIS -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200112L -#endif -#undef __EXTENSIONS__ -#endif - -#include "collectd.h" - -#include "common.h" /* for STATIC_ARRAY_SIZE */ -#include "testing.h" -#include "utils_format_json.h" - -#include -#include -#if HAVE_YAJL_YAJL_VERSION_H -#include -#endif -#if YAJL_MAJOR > 1 -#define HAVE_YAJL_V2 1 -#endif - -typedef struct { - char const *key; - char const *value; -} label_t; - -typedef struct { - label_t *expected_labels; - size_t expected_labels_num; - - label_t *current_label; -} test_case_t; - -#if HAVE_YAJL_V2 -static int test_map_key(void *ctx, unsigned char const *key, size_t key_len) -#else -static int test_map_key(void *ctx, unsigned char const *key, - unsigned int key_len) -#endif -{ - test_case_t *c = ctx; - size_t i; - - c->current_label = NULL; - for (i = 0; i < c->expected_labels_num; i++) { - label_t *l = c->expected_labels + i; - if ((strlen(l->key) == key_len) && - (strncmp(l->key, (char const *)key, key_len) == 0)) { - c->current_label = l; - break; - } - } - - return 1; /* continue */ -} - -static int expect_label(char const *name, char const *got, char const *want) { - bool ok = (strcmp(got, want) == 0); - char msg[1024]; - - if (ok) - snprintf(msg, sizeof(msg), "label[\"%s\"] = \"%s\"", name, got); - else - snprintf(msg, sizeof(msg), "label[\"%s\"] = \"%s\", want \"%s\"", name, got, - want); - - OK1(ok, msg); - return 0; -} - -#if HAVE_YAJL_V2 -static int test_string(void *ctx, unsigned char const *value, size_t value_len) -#else -static int test_string(void *ctx, unsigned char const *value, - unsigned int value_len) -#endif -{ - test_case_t *c = ctx; - - if (c->current_label != NULL) { - label_t *l = c->current_label; - char *got; - int status; - - got = malloc(value_len + 1); - memmove(got, value, value_len); - got[value_len] = 0; - - status = expect_label(l->key, got, l->value); - - free(got); - - if (status != 0) - return 0; /* abort */ - } - - return 1; /* continue */ -} - -static int expect_json_labels(char *json, label_t *labels, size_t labels_num) { - yajl_callbacks funcs = { - .yajl_string = test_string, .yajl_map_key = test_map_key, - }; - - test_case_t c = {labels, labels_num, NULL}; - - yajl_handle hndl; -#if HAVE_YAJL_V2 - CHECK_NOT_NULL(hndl = yajl_alloc(&funcs, /* alloc = */ NULL, &c)); -#else - CHECK_NOT_NULL( - hndl = yajl_alloc(&funcs, /* config = */ NULL, /* alloc = */ NULL, &c)); -#endif - OK(yajl_parse(hndl, (unsigned char *)json, strlen(json)) == yajl_status_ok); - - yajl_free(hndl); - return 0; -} - -DEF_TEST(notification) { - label_t labels[] = { - {"summary", "this is a message"}, - {"alertname", "collectd_unit_test"}, - {"instance", "example.com"}, - {"service", "collectd"}, - {"unit", "case"}, - }; - - /* 1448284606.125 ^= 1555083754651779072 */ - notification_t n = {NOTIF_WARNING, - 1555083754651779072ULL, - "this is a message", - "example.com", - "unit", - "", - "test", - "case", - NULL}; - - char got[1024]; - CHECK_ZERO(format_json_notification(got, sizeof(got), &n)); - // printf ("got = \"%s\";\n", got); - - return expect_json_labels(got, labels, STATIC_ARRAY_SIZE(labels)); -} - -int main(void) { - RUN_TEST(notification); - - END_TEST; -} diff --git a/src/utils_format_kairosdb.c b/src/utils_format_kairosdb.c deleted file mode 100644 index 49989062..00000000 --- a/src/utils_format_kairosdb.c +++ /dev/null @@ -1,358 +0,0 @@ -/** - * collectd - src/utils_format_kairosdb.c - * Copyright (C) 2016 Aurelien beorn Rougemont - * - * 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: - * Aurelien beorn Rougemont - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#include "utils_cache.h" -#include "utils_format_kairosdb.h" - -/* This is the KAIROSDB format for write_http output - * - * Target format - * [ - * { - * "name":"collectd.vmem" - * "datapoints": - * [ - * [1453897164060, 97.000000] - * ], - * "tags": - * { - * "host": "fqdn.domain.tld", - * "plugin_instance": "vmpage_number", - * "type": "kernel_stack", - * "ds": "value" - * "" - * } - * } - * ] - */ - -static int kairosdb_escape_string(char *buffer, size_t buffer_size, /* {{{ */ - const char *string) { - size_t dst_pos; - - if ((buffer == NULL) || (string == NULL)) - return -EINVAL; - - if (buffer_size < 3) - return -ENOMEM; - - dst_pos = 0; - -#define BUFFER_ADD(c) \ - do { \ - if (dst_pos >= (buffer_size - 1)) { \ - buffer[buffer_size - 1] = 0; \ - return -ENOMEM; \ - } \ - buffer[dst_pos] = (c); \ - dst_pos++; \ - } while (0) - - /* Escape special characters */ - /* authorize -_. and alpha num but also escapes " */ - BUFFER_ADD('"'); - for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) { - if (isalnum(string[src_pos]) || 0x2d == string[src_pos] || - 0x2e == string[src_pos] || 0x5f == string[src_pos]) - BUFFER_ADD(tolower(string[src_pos])); - } /* for */ - BUFFER_ADD('"'); - buffer[dst_pos] = 0; - -#undef BUFFER_ADD - - return 0; -} /* }}} int kairosdb_escape_string */ - -static int values_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */ - const data_set_t *ds, const value_list_t *vl, - int store_rates, size_t ds_idx) { - size_t offset = 0; - gauge_t *rates = NULL; - - memset(buffer, 0, buffer_size); - -#define BUFFER_ADD(...) \ - do { \ - int status; \ - status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ - if (status < 1) { \ - sfree(rates); \ - return -1; \ - } else if (((size_t)status) >= (buffer_size - offset)) { \ - sfree(rates); \ - return -ENOMEM; \ - } else \ - offset += ((size_t)status); \ - } while (0) - - if (ds->ds[ds_idx].type == DS_TYPE_GAUGE) { - if (isfinite(vl->values[ds_idx].gauge)) { - BUFFER_ADD("[["); - BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time)); - BUFFER_ADD(","); - BUFFER_ADD(JSON_GAUGE_FORMAT, vl->values[ds_idx].gauge); - } else { - DEBUG("utils_format_kairosdb: invalid vl->values[ds_idx].gauge for " - "%s|%s|%s|%s|%s", - vl->plugin, vl->plugin_instance, vl->type, vl->type_instance, - ds->ds[ds_idx].name); - return -1; - } - } else if (store_rates) { - if (rates == NULL) - rates = uc_get_rate(ds, vl); - if (rates == NULL) { - WARNING("utils_format_kairosdb: uc_get_rate failed for %s|%s|%s|%s|%s", - vl->plugin, vl->plugin_instance, vl->type, vl->type_instance, - ds->ds[ds_idx].name); - - return -1; - } - - if (isfinite(rates[ds_idx])) { - BUFFER_ADD("[["); - BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time)); - BUFFER_ADD(","); - BUFFER_ADD(JSON_GAUGE_FORMAT, rates[ds_idx]); - } else { - WARNING("utils_format_kairosdb: invalid rates[ds_idx] for %s|%s|%s|%s|%s", - vl->plugin, vl->plugin_instance, vl->type, vl->type_instance, - ds->ds[ds_idx].name); - sfree(rates); - return -1; - } - } else if (ds->ds[ds_idx].type == DS_TYPE_COUNTER) { - BUFFER_ADD("[["); - BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time)); - BUFFER_ADD(","); - 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)); - BUFFER_ADD(","); - BUFFER_ADD("%" PRIi64, vl->values[ds_idx].derive); - } else if (ds->ds[ds_idx].type == DS_TYPE_ABSOLUTE) { - BUFFER_ADD("[["); - BUFFER_ADD("%" PRIu64, CDTIME_T_TO_MS(vl->time)); - BUFFER_ADD(","); - BUFFER_ADD("%" PRIu64, vl->values[ds_idx].absolute); - } else { - ERROR("format_kairosdb: Unknown data source type: %i", ds->ds[ds_idx].type); - sfree(rates); - return -1; - } - BUFFER_ADD("]]"); - -#undef BUFFER_ADD - - DEBUG("format_kairosdb: values_to_kairosdb: buffer = %s;", buffer); - sfree(rates); - return 0; -} /* }}} int values_to_kairosdb */ - -static int value_list_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */ - const data_set_t *ds, const value_list_t *vl, - int store_rates, - char const *const *http_attrs, - size_t http_attrs_num, int data_ttl, - char const *metrics_prefix) { - char temp[512]; - size_t offset = 0; - int status; - - memset(buffer, 0, buffer_size); - -#define BUFFER_ADD(...) \ - do { \ - status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ - if (status < 1) \ - return -1; \ - else if (((size_t)status) >= (buffer_size - offset)) \ - return -ENOMEM; \ - else \ - offset += ((size_t)status); \ - } while (0) - -#define BUFFER_ADD_KEYVAL(key, value) \ - do { \ - status = kairosdb_escape_string(temp, sizeof(temp), (value)); \ - if (status != 0) \ - return status; \ - BUFFER_ADD(",\"%s\": %s", (key), temp); \ - } while (0) - - for (size_t i = 0; i < ds->ds_num; i++) { - /* All value lists have a leading comma. The first one will be replaced with - * a square bracket in `format_kairosdb_finalize'. */ - BUFFER_ADD(",{\"name\":\""); - - if (metrics_prefix != NULL) { - BUFFER_ADD("%s.", metrics_prefix); - } - - BUFFER_ADD("%s", vl->plugin); - - status = values_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates, i); - if (status != 0) - return status; - - BUFFER_ADD("\", \"datapoints\": %s", temp); - - /* - * Now adds meta data to metric as tags - */ - - memset(temp, 0, sizeof(temp)); - - if (data_ttl != 0) - BUFFER_ADD(", \"ttl\": %i", data_ttl); - - BUFFER_ADD(", \"tags\":\{"); - - BUFFER_ADD("\"host\": \"%s\"", vl->host); - for (size_t j = 0; j < http_attrs_num; j += 2) { - BUFFER_ADD(", \"%s\":", http_attrs[j]); - BUFFER_ADD(" \"%s\"", http_attrs[j + 1]); - } - - if (strlen(vl->plugin_instance)) - BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance); - BUFFER_ADD_KEYVAL("type", vl->type); - if (strlen(vl->type_instance)) - BUFFER_ADD_KEYVAL("type_instance", vl->type_instance); - if (ds->ds_num != 1) - BUFFER_ADD_KEYVAL("ds", ds->ds[i].name); - BUFFER_ADD("}}"); - } /* for ds->ds_num */ - -#undef BUFFER_ADD_KEYVAL -#undef BUFFER_ADD - - DEBUG("format_kairosdb: value_list_to_kairosdb: buffer = %s;", buffer); - - return 0; -} /* }}} int value_list_to_kairosdb */ - -static int format_kairosdb_value_list_nocheck( - char *buffer, /* {{{ */ - size_t *ret_buffer_fill, size_t *ret_buffer_free, const data_set_t *ds, - const value_list_t *vl, int store_rates, size_t temp_size, - char const *const *http_attrs, size_t http_attrs_num, int data_ttl, - char const *metrics_prefix) { - char temp[temp_size]; - int status; - - status = value_list_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates, - http_attrs, http_attrs_num, data_ttl, - metrics_prefix); - if (status != 0) - return status; - temp_size = strlen(temp); - - memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1); - (*ret_buffer_fill) += temp_size; - (*ret_buffer_free) -= temp_size; - - return 0; -} /* }}} int format_kairosdb_value_list_nocheck */ - -int format_kairosdb_initialize(char *buffer, /* {{{ */ - size_t *ret_buffer_fill, - size_t *ret_buffer_free) { - size_t buffer_fill; - size_t buffer_free; - - if ((buffer == NULL) || (ret_buffer_fill == NULL) || - (ret_buffer_free == NULL)) - return -EINVAL; - - buffer_fill = *ret_buffer_fill; - buffer_free = *ret_buffer_free; - - buffer_free = buffer_fill + buffer_free; - buffer_fill = 0; - - if (buffer_free < 3) - return -ENOMEM; - - memset(buffer, 0, buffer_free); - *ret_buffer_fill = buffer_fill; - *ret_buffer_free = buffer_free; - - return 0; -} /* }}} int format_kairosdb_initialize */ - -int format_kairosdb_finalize(char *buffer, /* {{{ */ - size_t *ret_buffer_fill, size_t *ret_buffer_free) { - size_t pos; - - if ((buffer == NULL) || (ret_buffer_fill == NULL) || - (ret_buffer_free == NULL)) - return -EINVAL; - - if (*ret_buffer_free < 2) - return -ENOMEM; - - /* Replace the leading comma added in `value_list_to_kairosdb' with a square - * bracket. */ - if (buffer[0] != ',') - return -EINVAL; - buffer[0] = '['; - - pos = *ret_buffer_fill; - buffer[pos] = ']'; - buffer[pos + 1] = 0; - - (*ret_buffer_fill)++; - (*ret_buffer_free)--; - - return 0; -} /* }}} int format_kairosdb_finalize */ - -int format_kairosdb_value_list(char *buffer, /* {{{ */ - size_t *ret_buffer_fill, size_t *ret_buffer_free, - const data_set_t *ds, const value_list_t *vl, - int store_rates, char const *const *http_attrs, - size_t http_attrs_num, int data_ttl, - char const *metrics_prefix) { - if ((buffer == NULL) || (ret_buffer_fill == NULL) || - (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL)) - return -EINVAL; - - if (*ret_buffer_free < 3) - return -ENOMEM; - - return format_kairosdb_value_list_nocheck( - buffer, ret_buffer_fill, ret_buffer_free, ds, vl, store_rates, - (*ret_buffer_free) - 2, http_attrs, http_attrs_num, data_ttl, - metrics_prefix); -} /* }}} int format_kairosdb_value_list */ diff --git a/src/utils_format_kairosdb.h b/src/utils_format_kairosdb.h deleted file mode 100644 index 7b9e0e7a..00000000 --- a/src/utils_format_kairosdb.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * collectd - src/utils_format_kairosdb.h - * Copyright (C) 2016 Aurelien Rougemont - * - * 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: - * Aurelien beorn Rougemont - **/ - -#ifndef UTILS_FORMAT_KAIROSDB_H -#define UTILS_FORMAT_KAIROSDB_H 1 - -#include "collectd.h" - -#include "plugin.h" - -#ifndef JSON_GAUGE_FORMAT -#define JSON_GAUGE_FORMAT GAUGE_FORMAT -#endif - -int format_kairosdb_initialize(char *buffer, size_t *ret_buffer_fill, - size_t *ret_buffer_free); -int format_kairosdb_value_list(char *buffer, size_t *ret_buffer_fill, - size_t *ret_buffer_free, const data_set_t *ds, - const value_list_t *vl, int store_rates, - char const *const *http_attrs, - size_t http_attrs_num, int data_ttl, - char const *metrics_prefix); -int format_kairosdb_finalize(char *buffer, size_t *ret_buffer_fill, - size_t *ret_buffer_free); - -#endif /* UTILS_FORMAT_KAIROSDB_H */ diff --git a/src/utils_format_stackdriver.c b/src/utils_format_stackdriver.c deleted file mode 100644 index afaa8ed8..00000000 --- a/src/utils_format_stackdriver.c +++ /dev/null @@ -1,766 +0,0 @@ -/** - * 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 deleted file mode 100644 index fee260e3..00000000 --- a/src/utils_format_stackdriver.h +++ /dev/null @@ -1,79 +0,0 @@ -/** - * 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 deleted file mode 100644 index 1e96b65e..00000000 --- a/src/utils_format_stackdriver_test.c +++ /dev/null @@ -1,77 +0,0 @@ -/** - * 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 deleted file mode 100644 index d43d1de5..00000000 --- a/src/utils_gce.c +++ /dev/null @@ -1,284 +0,0 @@ -/** - * 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 deleted file mode 100644 index 2ee3f6eb..00000000 --- a/src/utils_gce.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * 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 deleted file mode 100644 index 29b2af21..00000000 --- a/src/utils_ignorelist.c +++ /dev/null @@ -1,309 +0,0 @@ -/** - * collectd - src/utils_ignorelist.c - * Copyright (C) 2006 Lubos Stanek - * Copyright (C) 2008 Florian Forster - * - * This program is free software; you can redistribute it and/ - * or modify it under the terms of the GNU General Public Li- - * cence as published by the Free Software Foundation; either - * version 2 of the Licence, or any later version. - * - * This program is distributed in the hope that it will be use- - * ful, but WITHOUT ANY WARRANTY; without even the implied war- - * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public Licence for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: - * Lubos Stanek - * Florian Forster - **/ -/** - * ignorelist handles plugin's list of configured collectable - * entries with global ignore action - **/ -/** - * Usage: - * - * Define plugin's global pointer variable of type ignorelist_t: - * ignorelist_t *myconfig_ignore; - * If you know the state of the global ignore (IgnoreSelected), - * allocate the variable with: - * myconfig_ignore = ignorelist_create (YourKnownIgnore); - * If you do not know the state of the global ignore, - * initialize the global variable and set the ignore flag later: - * myconfig_ignore = ignorelist_init (); - * Append single entries in your cf_register'ed callback function: - * ignorelist_add (myconfig_ignore, newentry); - * When you hit the IgnoreSelected config option, - * offer it to the list: - * ignorelist_ignore (myconfig_ignore, instantly_got_value_of_ignore); - * That is all for the ignorelist initialization. - * Later during read and write (plugin's registered functions) get - * the information whether this entry would be collected or not: - * if (ignorelist_match (myconfig_ignore, thisentry)) - * return; - **/ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include "common.h" -#include "plugin.h" -#include "utils_ignorelist.h" - -/* - * private prototypes - */ -struct ignorelist_item_s { -#if HAVE_REGEX_H - regex_t *rmatch; /* regular expression entry identification */ -#endif - char *smatch; /* string entry identification */ - struct ignorelist_item_s *next; -}; -typedef struct ignorelist_item_s ignorelist_item_t; - -struct ignorelist_s { - int ignore; /* ignore entries */ - ignorelist_item_t *head; /* pointer to the first entry */ -}; - -/* *** *** *** ********************************************* *** *** *** */ -/* *** *** *** *** *** *** private functions *** *** *** *** *** *** */ -/* *** *** *** ********************************************* *** *** *** */ - -static inline void ignorelist_append(ignorelist_t *il, - ignorelist_item_t *item) { - assert((il != NULL) && (item != NULL)); - - item->next = il->head; - il->head = item; -} - -#if HAVE_REGEX_H -static int ignorelist_append_regex(ignorelist_t *il, const char *re_str) { - regex_t *re; - ignorelist_item_t *entry; - int status; - - re = calloc(1, sizeof(*re)); - if (re == NULL) { - ERROR("ignorelist_append_regex: calloc failed."); - return ENOMEM; - } - - status = regcomp(re, re_str, REG_EXTENDED); - if (status != 0) { - char errbuf[1024]; - (void)regerror(status, re, errbuf, sizeof(errbuf)); - ERROR("utils_ignorelist: regcomp failed: %s", errbuf); - ERROR("ignorelist_append_regex: Compiling regular expression \"%s\" " - "failed: %s", - re_str, errbuf); - sfree(re); - return status; - } - - entry = calloc(1, sizeof(*entry)); - if (entry == NULL) { - ERROR("ignorelist_append_regex: calloc failed."); - regfree(re); - sfree(re); - return ENOMEM; - } - entry->rmatch = re; - - ignorelist_append(il, entry); - return 0; -} /* int ignorelist_append_regex */ -#endif - -static int ignorelist_append_string(ignorelist_t *il, const char *entry) { - ignorelist_item_t *new; - - /* create new entry */ - if ((new = calloc(1, sizeof(*new))) == NULL) { - ERROR("cannot allocate new entry"); - return 1; - } - new->smatch = sstrdup(entry); - - /* append new entry */ - ignorelist_append(il, new); - - return 0; -} /* int ignorelist_append_string(ignorelist_t *il, const char *entry) */ - -#if HAVE_REGEX_H -/* - * check list for entry regex match - * return 1 if found - */ -static int ignorelist_match_regex(ignorelist_item_t *item, const char *entry) { - assert((item != NULL) && (item->rmatch != NULL) && (entry != NULL) && - (strlen(entry) > 0)); - - /* match regex */ - if (regexec(item->rmatch, entry, 0, NULL, 0) == 0) - return 1; - - return 0; -} /* int ignorelist_match_regex (ignorelist_item_t *item, const char *entry) */ -#endif - -/* - * check list for entry string match - * return 1 if found - */ -static int ignorelist_match_string(ignorelist_item_t *item, const char *entry) { - assert((item != NULL) && (item->smatch != NULL) && (entry != NULL) && - (strlen(entry) > 0)); - - if (strcmp(entry, item->smatch) == 0) - return 1; - - return 0; -} /* int ignorelist_match_string (ignorelist_item_t *item, const char *entry) */ - -/* *** *** *** ******************************************** *** *** *** */ -/* *** *** *** *** *** *** public functions *** *** *** *** *** *** */ -/* *** *** *** ******************************************** *** *** *** */ - -/* - * create the ignorelist_t with known ignore state - * return pointer to ignorelist_t - */ -ignorelist_t *ignorelist_create(int invert) { - ignorelist_t *il; - - il = calloc(1, sizeof(*il)); - if (il == NULL) - return NULL; - - /* - * ->ignore == 0 => collect - * ->ignore == 1 => ignore - */ - il->ignore = invert ? 0 : 1; - - return il; -} /* ignorelist_t *ignorelist_create (int ignore) */ - -/* - * free memory used by ignorelist_t - */ -void ignorelist_free(ignorelist_t *il) { - ignorelist_item_t *this; - ignorelist_item_t *next; - - if (il == NULL) - return; - - for (this = il->head; this != NULL; this = next) { - next = this->next; -#if HAVE_REGEX_H - if (this->rmatch != NULL) { - regfree(this->rmatch); - sfree(this->rmatch); - this->rmatch = NULL; - } -#endif - if (this->smatch != NULL) { - sfree(this->smatch); - this->smatch = NULL; - } - sfree(this); - } - - sfree(il); -} /* void ignorelist_destroy (ignorelist_t *il) */ - -/* - * set ignore state of the ignorelist_t - */ -void ignorelist_set_invert(ignorelist_t *il, int invert) { - if (il == NULL) { - DEBUG("ignore call with ignorelist_t == NULL"); - return; - } - - il->ignore = invert ? 0 : 1; -} /* void ignorelist_set_invert (ignorelist_t *il, int ignore) */ - -/* - * append entry into ignorelist_t - * return 0 for success - */ -int ignorelist_add(ignorelist_t *il, const char *entry) { - size_t len; - - if (il == NULL) { - DEBUG("add called with ignorelist_t == NULL"); - return 1; - } - - len = strlen(entry); - - /* append nothing */ - if (len == 0) { - DEBUG("not appending: empty entry"); - return 1; - } - -#if HAVE_REGEX_H - /* regex string is enclosed in "/.../" */ - if ((len > 2) && (entry[0] == '/') && entry[len - 1] == '/') { - char *copy; - int status; - - /* skip leading slash */ - copy = strdup(entry + 1); - if (copy == NULL) - return ENOMEM; - - /* trim trailing slash */ - copy[strlen(copy) - 1] = 0; - - status = ignorelist_append_regex(il, copy); - sfree(copy); - return status; - } -#endif - - return ignorelist_append_string(il, entry); -} /* int ignorelist_add (ignorelist_t *il, const char *entry) */ - -/* - * check list for entry - * return 1 for ignored entry - */ -int ignorelist_match(ignorelist_t *il, const char *entry) { - /* if no entries, collect all */ - if ((il == NULL) || (il->head == NULL)) - return 0; - - if ((entry == NULL) || (strlen(entry) == 0)) - return 0; - - /* traverse list and check entries */ - for (ignorelist_item_t *traverse = il->head; traverse != NULL; - traverse = traverse->next) { -#if HAVE_REGEX_H - if (traverse->rmatch != NULL) { - if (ignorelist_match_regex(traverse, entry)) - return il->ignore; - } else -#endif - { - if (ignorelist_match_string(traverse, entry)) - return il->ignore; - } - } /* for traverse */ - - return 1 - il->ignore; -} /* int ignorelist_match (ignorelist_t *il, const char *entry) */ diff --git a/src/utils_ignorelist.h b/src/utils_ignorelist.h deleted file mode 100644 index a7fa86d5..00000000 --- a/src/utils_ignorelist.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * collectd - src/utils_ignorelist.h - * Copyright (C) 2006 Lubos Stanek - * - * This program is free software; you can redistribute it and/ - * or modify it under the terms of the GNU General Public Li- - * cence as published by the Free Software Foundation; either - * version 2 of the Licence, or any later version. - * - * This program is distributed in the hope that it will be use- - * ful, but WITHOUT ANY WARRANTY; without even the implied war- - * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public Licence for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: - * Lubos Stanek - **/ -/** - * ignorelist handles plugin's list of configured collectable - * entries with global ignore action - **/ - -#ifndef UTILS_IGNORELIST_H -#define UTILS_IGNORELIST_H 1 - -#include "collectd.h" - -#if HAVE_REGEX_H -#include -#endif - -/* public prototypes */ - -struct ignorelist_s; -typedef struct ignorelist_s ignorelist_t; - -/* - * create the ignorelist_t with known ignore state - * return pointer to ignorelist_t - */ -ignorelist_t *ignorelist_create(int invert); - -/* - * free memory used by ignorelist_t - */ -void ignorelist_free(ignorelist_t *il); - -/* - * set ignore state of the ignorelist_t - */ -void ignorelist_set_invert(ignorelist_t *il, int invert); - -/* - * append entry to ignorelist_t - * returns zero on success, non-zero upon failure. - */ -int ignorelist_add(ignorelist_t *il, const char *entry); - -/* - * check list for entry - * return 1 for ignored entry - */ -int ignorelist_match(ignorelist_t *il, const char *entry); - -#endif /* UTILS_IGNORELIST_H */ diff --git a/src/utils_latency.c b/src/utils_latency.c deleted file mode 100644 index 6e4f8732..00000000 --- a/src/utils_latency.c +++ /dev/null @@ -1,344 +0,0 @@ -/** - * collectd - src/utils_latency.c - * Copyright (C) 2013 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" -#include "utils_latency.h" - -#include -#include - -#ifndef LLONG_MAX -#define LLONG_MAX 9223372036854775807LL -#endif - -#ifndef HISTOGRAM_DEFAULT_BIN_WIDTH -/* 1048576 = 2^20 ^= 1/1024 s */ -#define HISTOGRAM_DEFAULT_BIN_WIDTH 1048576 -#endif - -struct latency_counter_s { - cdtime_t start_time; - - cdtime_t sum; - size_t num; - - cdtime_t min; - cdtime_t max; - - cdtime_t bin_width; - int histogram[HISTOGRAM_NUM_BINS]; -}; - -/* -* Histogram represents the distribution of data, it has a list of "bins". -* Each bin represents an interval and has a count (frequency) of -* number of values fall within its interval. -* -* Histogram's range is determined by the number of bins and the bin width, -* There are 1000 bins and all bins have the same width of default 1 millisecond. -* 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 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, ... -* -* So, if the required bin width is 300, then new bin width will be 512 as it is -* the next nearest power of 2. -*/ -static void change_bin_width(latency_counter_t *lc, cdtime_t latency) /* {{{ */ -{ - /* This function is called because the new value is above histogram's range. - * First find the required bin width: - * requiredBinWidth = (value + 1) / numBins - * then get the next nearest power of 2 - * newBinWidth = 2^(ceil(log2(requiredBinWidth))) - */ - double required_bin_width = - ((double)(latency + 1)) / ((double)HISTOGRAM_NUM_BINS); - double required_bin_width_logbase2 = log(required_bin_width) / log(2.0); - cdtime_t new_bin_width = - (cdtime_t)(pow(2.0, ceil(required_bin_width_logbase2)) + .5); - cdtime_t old_bin_width = lc->bin_width; - - lc->bin_width = new_bin_width; - - /* bin_width has been increased, now iterate through all bins and move the - * old bin's count to new bin. */ - if (lc->num > 0) // if the histogram has data then iterate else skip - { - double width_change_ratio = - ((double)old_bin_width) / ((double)new_bin_width); - - for (size_t i = 0; i < HISTOGRAM_NUM_BINS; i++) { - size_t new_bin = (size_t)(((double)i) * width_change_ratio); - if (i == new_bin) - continue; - assert(new_bin < i); - - lc->histogram[new_bin] += lc->histogram[i]; - lc->histogram[i] = 0; - } - } - - DEBUG("utils_latency: change_bin_width: latency = %.3f; " - "old_bin_width = %.3f; new_bin_width = %.3f;", - CDTIME_T_TO_DOUBLE(latency), CDTIME_T_TO_DOUBLE(old_bin_width), - CDTIME_T_TO_DOUBLE(new_bin_width)); -} /* }}} void change_bin_width */ - -latency_counter_t *latency_counter_create(void) /* {{{ */ -{ - latency_counter_t *lc; - - lc = calloc(1, sizeof(*lc)); - if (lc == NULL) - return NULL; - - lc->bin_width = HISTOGRAM_DEFAULT_BIN_WIDTH; - latency_counter_reset(lc); - return lc; -} /* }}} latency_counter_t *latency_counter_create */ - -void latency_counter_destroy(latency_counter_t *lc) /* {{{ */ -{ - sfree(lc); -} /* }}} void latency_counter_destroy */ - -void latency_counter_add(latency_counter_t *lc, cdtime_t latency) /* {{{ */ -{ - cdtime_t bin; - - if ((lc == NULL) || (latency == 0) || (latency > ((cdtime_t)LLONG_MAX))) - return; - - lc->sum += latency; - lc->num++; - - if ((lc->min == 0) && (lc->max == 0)) - lc->min = lc->max = latency; - if (lc->min > latency) - lc->min = latency; - if (lc->max < latency) - lc->max = latency; - - /* A latency of _exactly_ 1.0 ms is stored in the buffer 0, so - * subtract one from the cdtime_t value so that exactly 1.0 ms get sorted - * accordingly. */ - bin = (latency - 1) / lc->bin_width; - if (bin >= HISTOGRAM_NUM_BINS) { - change_bin_width(lc, latency); - bin = (latency - 1) / lc->bin_width; - if (bin >= HISTOGRAM_NUM_BINS) { - P_ERROR("latency_counter_add: Invalid bin: %" PRIu64, bin); - return; - } - } - lc->histogram[bin]++; -} /* }}} void latency_counter_add */ - -void latency_counter_reset(latency_counter_t *lc) /* {{{ */ -{ - if (lc == NULL) - return; - - cdtime_t bin_width = lc->bin_width; - cdtime_t max_bin = (lc->max - 1) / lc->bin_width; - -/* - If max latency is REDUCE_THRESHOLD times less than histogram's range, - then cut it in half. REDUCE_THRESHOLD must be >= 2. - Value of 4 is selected to reduce frequent changes of bin width. -*/ -#define REDUCE_THRESHOLD 4 - if ((lc->num > 0) && (lc->bin_width >= HISTOGRAM_DEFAULT_BIN_WIDTH * 2) && - (max_bin < HISTOGRAM_NUM_BINS / REDUCE_THRESHOLD)) { - /* new bin width will be the previous power of 2 */ - bin_width = bin_width / 2; - - DEBUG("utils_latency: latency_counter_reset: max_latency = %.3f; " - "max_bin = %" PRIu64 "; old_bin_width = %.3f; new_bin_width = %.3f;", - CDTIME_T_TO_DOUBLE(lc->max), max_bin, - CDTIME_T_TO_DOUBLE(lc->bin_width), CDTIME_T_TO_DOUBLE(bin_width)); - } - - memset(lc, 0, sizeof(*lc)); - - /* preserve bin width */ - lc->bin_width = bin_width; - lc->start_time = cdtime(); -} /* }}} void latency_counter_reset */ - -cdtime_t latency_counter_get_min(latency_counter_t *lc) /* {{{ */ -{ - if (lc == NULL) - return 0; - return lc->min; -} /* }}} cdtime_t latency_counter_get_min */ - -cdtime_t latency_counter_get_max(latency_counter_t *lc) /* {{{ */ -{ - if (lc == NULL) - return 0; - return lc->max; -} /* }}} cdtime_t latency_counter_get_max */ - -cdtime_t latency_counter_get_sum(latency_counter_t *lc) /* {{{ */ -{ - if (lc == NULL) - return 0; - return lc->sum; -} /* }}} cdtime_t latency_counter_get_sum */ - -size_t latency_counter_get_num(latency_counter_t *lc) /* {{{ */ -{ - if (lc == NULL) - return 0; - return lc->num; -} /* }}} size_t latency_counter_get_num */ - -cdtime_t latency_counter_get_average(latency_counter_t *lc) /* {{{ */ -{ - double average; - - if ((lc == NULL) || (lc->num == 0)) - return 0; - - average = CDTIME_T_TO_DOUBLE(lc->sum) / ((double)lc->num); - return DOUBLE_TO_CDTIME_T(average); -} /* }}} cdtime_t latency_counter_get_average */ - -cdtime_t latency_counter_get_percentile(latency_counter_t *lc, /* {{{ */ - double percent) { - double percent_upper; - double percent_lower; - double p; - cdtime_t latency_lower; - cdtime_t latency_interpolated; - int sum; - size_t i; - - if ((lc == NULL) || (lc->num == 0) || !((percent > 0.0) && (percent < 100.0))) - return 0; - - /* Find index i so that at least "percent" events are within i+1 ms. */ - percent_upper = 0.0; - percent_lower = 0.0; - sum = 0; - for (i = 0; i < HISTOGRAM_NUM_BINS; i++) { - percent_lower = percent_upper; - sum += lc->histogram[i]; - if (sum == 0) - percent_upper = 0.0; - else - percent_upper = 100.0 * ((double)sum) / ((double)lc->num); - - if (percent_upper >= percent) - break; - } - - if (i >= HISTOGRAM_NUM_BINS) - return 0; - - assert(percent_upper >= percent); - assert(percent_lower < percent); - - if (i == 0) - return lc->bin_width; - - latency_lower = ((cdtime_t)i) * lc->bin_width; - p = (percent - percent_lower) / (percent_upper - percent_lower); - - latency_interpolated = - latency_lower + DOUBLE_TO_CDTIME_T(p * CDTIME_T_TO_DOUBLE(lc->bin_width)); - - DEBUG("latency_counter_get_percentile: latency_interpolated = %.3f", - CDTIME_T_TO_DOUBLE(latency_interpolated)); - return latency_interpolated; -} /* }}} cdtime_t latency_counter_get_percentile */ - -double latency_counter_get_rate(const latency_counter_t *lc, /* {{{ */ - cdtime_t lower, cdtime_t upper, - const cdtime_t now) { - if ((lc == NULL) || (lc->num == 0)) - return NAN; - - if (upper && (upper < lower)) - return NAN; - if (lower == upper) - return 0; - - /* Buckets have an exclusive lower bound and an inclusive upper bound. That - * means that the first bucket, index 0, represents (0-bin_width]. That means - * that latency==bin_width needs to result in bin=0, that's why we need to - * subtract one before dividing by bin_width. */ - cdtime_t lower_bin = 0; - if (lower) - /* lower is *exclusive* => determine bucket for lower+1 */ - lower_bin = ((lower + 1) - 1) / lc->bin_width; - - /* lower is greater than the longest latency observed => rate is zero. */ - if (lower_bin >= HISTOGRAM_NUM_BINS) - return 0; - - cdtime_t upper_bin = HISTOGRAM_NUM_BINS - 1; - if (upper) - upper_bin = (upper - 1) / lc->bin_width; - - if (upper_bin >= HISTOGRAM_NUM_BINS) { - upper_bin = HISTOGRAM_NUM_BINS - 1; - upper = 0; - } - - double sum = 0; - for (size_t i = lower_bin; i <= upper_bin; i++) - sum += lc->histogram[i]; - - if (lower) { - /* Approximate ratio of requests in lower_bin, that fall between - * lower_bin_boundary and lower. This ratio is then subtracted from sum to - * increase accuracy. */ - cdtime_t lower_bin_boundary = lower_bin * lc->bin_width; - assert(lower >= lower_bin_boundary); - double lower_ratio = - (double)(lower - lower_bin_boundary) / ((double)lc->bin_width); - sum -= lower_ratio * lc->histogram[lower_bin]; - } - - if (upper) { - /* As above: approximate ratio of requests in upper_bin, that fall between - * upper and upper_bin_boundary. */ - cdtime_t upper_bin_boundary = (upper_bin + 1) * lc->bin_width; - assert(upper <= upper_bin_boundary); - double ratio = (double)(upper_bin_boundary - upper) / (double)lc->bin_width; - sum -= ratio * lc->histogram[upper_bin]; - } - - return sum / (CDTIME_T_TO_DOUBLE(now - lc->start_time)); -} /* }}} double latency_counter_get_rate */ diff --git a/src/utils_latency.h b/src/utils_latency.h deleted file mode 100644 index 9d878dab..00000000 --- a/src/utils_latency.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * collectd - src/utils_latency.h - * Copyright (C) 2013 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian Forster - **/ - -#include "collectd.h" - -#include "utils_time.h" - -#ifndef HISTOGRAM_NUM_BINS -#define HISTOGRAM_NUM_BINS 1000 -#endif - -struct latency_counter_s; -typedef struct latency_counter_s latency_counter_t; - -latency_counter_t *latency_counter_create(void); -void latency_counter_destroy(latency_counter_t *lc); - -void latency_counter_add(latency_counter_t *lc, cdtime_t latency); -void latency_counter_reset(latency_counter_t *lc); - -cdtime_t latency_counter_get_min(latency_counter_t *lc); -cdtime_t latency_counter_get_max(latency_counter_t *lc); -cdtime_t latency_counter_get_sum(latency_counter_t *lc); -size_t latency_counter_get_num(latency_counter_t *lc); -cdtime_t latency_counter_get_average(latency_counter_t *lc); -cdtime_t latency_counter_get_percentile(latency_counter_t *lc, double percent); - -/* - * NAME - * latency_counter_get_rate(counter,lower,upper,now) - * - * DESCRIPTION - * Calculates rate of latency values fall within requested interval. - * Interval specified as (lower,upper], i.e. the lower boundary is exclusive, - * the upper boundary is inclusive. - * When lower is zero, then the interval is (0, upper]. - * When upper is zero, then the interval is (lower, infinity). - */ -double latency_counter_get_rate(const latency_counter_t *lc, cdtime_t lower, - cdtime_t upper, const cdtime_t now); diff --git a/src/utils_latency_config.c b/src/utils_latency_config.c deleted file mode 100644 index 9a91f113..00000000 --- a/src/utils_latency_config.c +++ /dev/null @@ -1,157 +0,0 @@ -/** - * collectd - src/utils_latency_config.c - * Copyright (C) 2013-2016 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - * Pavel Rochnyack - */ - -#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) { - double percent; - int status = cf_util_get_double(ci, &percent); - if (status != 0) - return status; - - if ((percent <= 0.0) || (percent >= 100)) { - 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) { - P_ERROR("realloc failed."); - return ENOMEM; - } - conf->percentile = tmp; - conf->percentile[conf->percentile_num] = percent; - conf->percentile_num++; - - return 0; -} /* int latency_config_add_percentile */ - -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)) { - 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) { - P_ERROR("MIN must be less than MAX in \"%s\".", ci->key); - return ERANGE; - } - - if (ci->values[0].value.number < 0) { - 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) { - P_ERROR("realloc failed."); - return ENOMEM; - } - conf->buckets = tmp; - conf->buckets[conf->buckets_num].lower_bound = - DOUBLE_TO_CDTIME_T(ci->values[0].value.number); - conf->buckets[conf->buckets_num].upper_bound = - DOUBLE_TO_CDTIME_T(ci->values[1].value.number); - conf->buckets_num++; - - return 0; -} /* int latency_config_add_bucket */ - -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); - else if (strcasecmp("Bucket", child->key) == 0) - status = latency_config_add_bucket(conf, child); - else if (strcasecmp("BucketType", child->key) == 0) - status = cf_util_get_string(child, &conf->bucket_type); - else - P_WARNING("\"%s\" is not a valid option within a \"%s\" block.", - child->key, ci->key); - - if (status != 0) - return status; - } - - if ((status == 0) && (conf->percentile_num == 0) && - (conf->buckets_num == 0)) { - P_ERROR("The \"%s\" block must contain at least one " - "\"Percentile\" or \"Bucket\" option.", - ci->key); - return EINVAL; - } - - return 0; -} - -int latency_config_copy(latency_config_t *dst, const latency_config_t src) { - *dst = (latency_config_t){ - .percentile_num = src.percentile_num, .buckets_num = src.buckets_num, - }; - - dst->percentile = calloc(dst->percentile_num, sizeof(*dst->percentile)); - dst->buckets = calloc(dst->buckets_num, sizeof(*dst->buckets)); - - if ((dst->percentile == NULL) || (dst->buckets == NULL)) { - latency_config_free(*dst); - return ENOMEM; - } - - if (src.bucket_type != NULL) { - dst->bucket_type = strdup(src.bucket_type); - if (dst->bucket_type == NULL) { - latency_config_free(*dst); - return ENOMEM; - } - } - - memmove(dst->percentile, src.percentile, - dst->percentile_num * sizeof(*dst->percentile)); - memmove(dst->buckets, src.buckets, dst->buckets_num * sizeof(*dst->buckets)); - - return 0; -} /* int latency_config_copy */ - -void latency_config_free(latency_config_t conf) { - sfree(conf.percentile); - sfree(conf.buckets); - sfree(conf.bucket_type); -} /* void latency_config_free */ diff --git a/src/utils_latency_config.h b/src/utils_latency_config.h deleted file mode 100644 index 3d2691a6..00000000 --- a/src/utils_latency_config.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * collectd - src/utils_latency_config.c - * Copyright (C) 2013-2016 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - * Pavel Rochnyack - */ - -#ifndef UTILS_LATENCY_CONFIG_H -#define UTILS_LATENCY_CONFIG_H 1 - -#include "collectd.h" - -#include "liboconfig/oconfig.h" -#include "utils_time.h" - -typedef struct { - cdtime_t lower_bound; - cdtime_t upper_bound; -} latency_bucket_t; - -typedef struct { - double *percentile; - size_t percentile_num; - - latency_bucket_t *buckets; - size_t buckets_num; - char *bucket_type; - - /* - bool lower; - bool upper; - bool avg; - */ -} latency_config_t; - -int latency_config(latency_config_t *conf, oconfig_item_t *ci); - -int latency_config_copy(latency_config_t *dst, const latency_config_t src); - -void latency_config_free(latency_config_t conf); - -#endif /* UTILS_LATENCY_CONFIG_H */ diff --git a/src/utils_latency_test.c b/src/utils_latency_test.c deleted file mode 100644 index 42a6e87e..00000000 --- a/src/utils_latency_test.c +++ /dev/null @@ -1,234 +0,0 @@ -/** - * collectd - src/utils_latency_test.c - * Copyright (C) 2015 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#define DBL_PRECISION 1e-6 - -#include "collectd.h" -#include "common.h" /* for STATIC_ARRAY_SIZE */ - -#include "testing.h" -#include "utils_latency.h" -#include "utils_time.h" - -DEF_TEST(simple) { - struct { - double val; - double min; - double max; - double sum; - double avg; - } cases[] = { - /* val min max sum avg */ - {0.5, 0.5, 0.5, 0.5, 0.5}, {0.3, 0.3, 0.5, 0.8, 0.4}, - {0.7, 0.3, 0.7, 1.5, 0.5}, {2.5, 0.3, 2.5, 4.0, 1.0}, - {99, 0.3, 99, 103, 20.6}, - /* { -1, 0.3, 99, 103, 20.6}, see issue #1139 */ - }; - latency_counter_t *l; - - CHECK_NOT_NULL(l = latency_counter_create()); - - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); 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)); - - EXPECT_EQ_DOUBLE(cases[i].min, - CDTIME_T_TO_DOUBLE(latency_counter_get_min(l))); - EXPECT_EQ_DOUBLE(cases[i].max, - CDTIME_T_TO_DOUBLE(latency_counter_get_max(l))); - EXPECT_EQ_DOUBLE(cases[i].sum, - CDTIME_T_TO_DOUBLE(latency_counter_get_sum(l))); - EXPECT_EQ_DOUBLE(cases[i].avg, - CDTIME_T_TO_DOUBLE(latency_counter_get_average(l))); - } - - latency_counter_destroy(l); - return 0; -} - -DEF_TEST(percentile) { - latency_counter_t *l; - - CHECK_NOT_NULL(l = latency_counter_create()); - - for (size_t i = 0; i < 100; i++) { - latency_counter_add(l, TIME_T_TO_CDTIME_T(((time_t)i) + 1)); - } - - EXPECT_EQ_DOUBLE(1.0, CDTIME_T_TO_DOUBLE(latency_counter_get_min(l))); - EXPECT_EQ_DOUBLE(100.0, CDTIME_T_TO_DOUBLE(latency_counter_get_max(l))); - EXPECT_EQ_DOUBLE(100.0 * 101.0 / 2.0, - CDTIME_T_TO_DOUBLE(latency_counter_get_sum(l))); - EXPECT_EQ_DOUBLE(50.5, CDTIME_T_TO_DOUBLE(latency_counter_get_average(l))); - - EXPECT_EQ_DOUBLE(50.0, - CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 50.0))); - EXPECT_EQ_DOUBLE(80.0, - CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 80.0))); - EXPECT_EQ_DOUBLE(95.0, - CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 95.0))); - EXPECT_EQ_DOUBLE(99.0, - CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 99.0))); - - CHECK_ZERO(latency_counter_get_percentile(l, -1.0)); - CHECK_ZERO(latency_counter_get_percentile(l, 101.0)); - - latency_counter_destroy(l); - return 0; -} - -DEF_TEST(get_rate) { - /* We re-declare the struct here so we can inspect its content. */ - struct { - cdtime_t start_time; - cdtime_t sum; - size_t num; - cdtime_t min; - cdtime_t max; - cdtime_t bin_width; - int histogram[HISTOGRAM_NUM_BINS]; - } * peek; - latency_counter_t *l; - - CHECK_NOT_NULL(l = latency_counter_create()); - peek = (void *)l; - - for (time_t i = 1; i <= 125; i++) { - latency_counter_add(l, TIME_T_TO_CDTIME_T(i)); - } - - /* We expect a bucket width of 125ms. */ - EXPECT_EQ_UINT64(DOUBLE_TO_CDTIME_T(0.125), peek->bin_width); - - struct { - size_t index; - int want; - } bucket_cases[] = { - {0, 0}, /* (0.000-0.125] */ - {1, 0}, /* (0.125-0.250] */ - {2, 0}, /* (0.250-0.375] */ - {3, 0}, /* (0.375-0.500] */ - {4, 0}, /* (0.500-0.625] */ - {5, 0}, /* (0.625-0.750] */ - {6, 0}, /* (0.750-0.875] */ - {7, 1}, /* (0.875-1.000] */ - {8, 0}, /* (1.000-1.125] */ - {9, 0}, /* (1.125-1.250] */ - {10, 0}, /* (1.250-1.375] */ - {11, 0}, /* (1.375-1.500] */ - {12, 0}, /* (1.500-1.625] */ - {13, 0}, /* (1.625-1.750] */ - {14, 0}, /* (1.750-1.875] */ - {15, 1}, /* (1.875-2.000] */ - {16, 0}, /* (2.000-2.125] */ - }; - - for (size_t i = 0; i < STATIC_ARRAY_SIZE(bucket_cases); i++) { - size_t index = bucket_cases[i].index; - EXPECT_EQ_INT(bucket_cases[i].want, peek->histogram[index]); - } - - struct { - cdtime_t lower_bound; - cdtime_t upper_bound; - double want; - } cases[] = { - { - // bucket 6 is zero - DOUBLE_TO_CDTIME_T_STATIC(0.750), DOUBLE_TO_CDTIME_T_STATIC(0.875), - 0.00, - }, - { - // bucket 7 contains the t=1 update - DOUBLE_TO_CDTIME_T_STATIC(0.875), DOUBLE_TO_CDTIME_T_STATIC(1.000), - 1.00, - }, - { - // range: bucket 7 - bucket 15; contains the t=1 and t=2 updates - DOUBLE_TO_CDTIME_T_STATIC(0.875), DOUBLE_TO_CDTIME_T_STATIC(2.000), - 2.00, - }, - { - // lower bucket is only partially applied - DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)), - DOUBLE_TO_CDTIME_T_STATIC(2.000), 1.75, - }, - { - // upper bucket is only partially applied - DOUBLE_TO_CDTIME_T_STATIC(0.875), - DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), 1.75, - }, - { - // both buckets are only partially applied - DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)), - DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), 1.50, - }, - { - // lower bound is unspecified - 0, DOUBLE_TO_CDTIME_T_STATIC(2.000), 2.00, - }, - { - // upper bound is unspecified - DOUBLE_TO_CDTIME_T_STATIC(125.000 - 0.125), 0, 1.00, - }, - { - // overflow test: upper >> longest latency - DOUBLE_TO_CDTIME_T_STATIC(1.000), DOUBLE_TO_CDTIME_T_STATIC(999999), - 124.00, - }, - { - // overflow test: lower > longest latency - DOUBLE_TO_CDTIME_T_STATIC(130), 0, 0.00, - }, - { - // lower > upper => error - DOUBLE_TO_CDTIME_T_STATIC(10), DOUBLE_TO_CDTIME_T_STATIC(9), NAN, - }, - { - // lower == upper => zero - DOUBLE_TO_CDTIME_T_STATIC(9), DOUBLE_TO_CDTIME_T_STATIC(9), 0.00, - }, - }; - - for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { - cdtime_t now = peek->start_time + TIME_T_TO_CDTIME_T(1); - EXPECT_EQ_DOUBLE(cases[i].want, - latency_counter_get_rate(l, cases[i].lower_bound, - cases[i].upper_bound, now)); - } - - latency_counter_destroy(l); - return 0; -} - -int main(void) { - RUN_TEST(simple); - RUN_TEST(percentile); - RUN_TEST(get_rate); - - END_TEST; -} diff --git a/src/utils_lua.c b/src/utils_lua.c index 11ac0010..d0de9085 100644 --- a/src/utils_lua.c +++ b/src/utils_lua.c @@ -24,7 +24,7 @@ * Florian Forster **/ -#include "common.h" +#include "utils/common/common.h" #include "utils_lua.h" static int ltoc_values(lua_State *L, /* {{{ */ diff --git a/src/utils_match.c b/src/utils_match.c deleted file mode 100644 index d3edb57c..00000000 --- a/src/utils_match.c +++ /dev/null @@ -1,376 +0,0 @@ -/** - * collectd - src/utils_match.c - * Copyright (C) 2008-2014 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "plugin.h" - -#include "utils_match.h" - -#include - -#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02 -#define UTILS_MATCH_FLAGS_REGEX 0x04 - -struct cu_match_s { - regex_t regex; - regex_t excluderegex; - int flags; - - int (*callback)(const char *str, char *const *matches, size_t matches_num, - void *user_data); - void *user_data; - void (*free)(void *user_data); -}; - -/* - * Private functions - */ -static char *match_substr(const char *str, int begin, int end) { - char *ret; - size_t ret_len; - - if ((begin < 0) || (end < 0) || (begin >= end)) - return NULL; - if ((size_t)end > (strlen(str) + 1)) { - ERROR("utils_match: match_substr: `end' points after end of string."); - return NULL; - } - - ret_len = end - begin; - ret = malloc(ret_len + 1); - if (ret == NULL) { - ERROR("utils_match: match_substr: malloc failed."); - return NULL; - } - - sstrncpy(ret, str + begin, ret_len + 1); - return ret; -} /* char *match_substr */ - -static int default_callback(const char __attribute__((unused)) * str, - char *const *matches, size_t matches_num, - void *user_data) { - cu_match_value_t *data = (cu_match_value_t *)user_data; - - if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) { - gauge_t value; - char *endptr = NULL; - - if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC) { - data->value.gauge = isnan(data->value.gauge) ? 1 : data->value.gauge + 1; - data->values_num++; - return 0; - } - - if (matches_num < 2) - return -1; - - value = (gauge_t)strtod(matches[1], &endptr); - if (matches[1] == endptr) - return -1; - - if (data->ds_type & UTILS_MATCH_CF_GAUGE_DIST) { - latency_counter_add(data->latency, DOUBLE_TO_CDTIME_T(value)); - data->values_num++; - return 0; - } - - if ((data->values_num == 0) || - (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST) || - (data->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) { - data->value.gauge = value; - } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE) { - double f = ((double)data->values_num) / ((double)(data->values_num + 1)); - data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f)); - } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN) { - if (data->value.gauge > value) - data->value.gauge = value; - } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX) { - if (data->value.gauge < value) - data->value.gauge = value; - } else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD) { - data->value.gauge += value; - } else { - ERROR("utils_match: default_callback: obj->ds_type is invalid!"); - return -1; - } - - data->values_num++; - } else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER) { - counter_t value; - char *endptr = NULL; - - if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC) { - data->value.counter++; - data->values_num++; - return 0; - } - - if (matches_num < 2) - return -1; - - value = (counter_t)strtoull(matches[1], &endptr, 0); - if (matches[1] == endptr) - return -1; - - if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET) - data->value.counter = value; - else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD) - data->value.counter += value; - else { - ERROR("utils_match: default_callback: obj->ds_type is invalid!"); - return -1; - } - - data->values_num++; - } else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE) { - derive_t value; - char *endptr = NULL; - - if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC) { - data->value.derive++; - data->values_num++; - return 0; - } - - if (matches_num < 2) - return -1; - - value = (derive_t)strtoll(matches[1], &endptr, 0); - if (matches[1] == endptr) - return -1; - - if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET) - data->value.derive = value; - else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD) - data->value.derive += value; - else { - ERROR("utils_match: default_callback: obj->ds_type is invalid!"); - return -1; - } - - data->values_num++; - } else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE) { - absolute_t value; - char *endptr = NULL; - - if (matches_num < 2) - return -1; - - value = (absolute_t)strtoull(matches[1], &endptr, 0); - if (matches[1] == endptr) - return -1; - - if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET) - data->value.absolute = value; - else { - ERROR("utils_match: default_callback: obj->ds_type is invalid!"); - return -1; - } - - data->values_num++; - } else { - ERROR("utils_match: default_callback: obj->ds_type is invalid!"); - return -1; - } - - return 0; -} /* int default_callback */ - -static void match_simple_free(void *data) { - cu_match_value_t *user_data = (cu_match_value_t *)data; - if (user_data->latency) - latency_counter_destroy(user_data->latency); - - free(data); -} /* void match_simple_free */ - -/* - * Public functions - */ -cu_match_t * -match_create_callback(const char *regex, const char *excluderegex, - int (*callback)(const char *str, char *const *matches, - size_t matches_num, void *user_data), - void *user_data, - void (*free_user_data)(void *user_data)) { - cu_match_t *obj; - int status; - - DEBUG("utils_match: match_create_callback: regex = %s, excluderegex = %s", - regex, excluderegex); - - obj = calloc(1, sizeof(*obj)); - if (obj == NULL) - return NULL; - - status = regcomp(&obj->regex, regex, REG_EXTENDED | REG_NEWLINE); - if (status != 0) { - ERROR("Compiling the regular expression \"%s\" failed.", regex); - sfree(obj); - return NULL; - } - obj->flags |= UTILS_MATCH_FLAGS_REGEX; - - if (excluderegex && strcmp(excluderegex, "") != 0) { - status = regcomp(&obj->excluderegex, excluderegex, REG_EXTENDED); - if (status != 0) { - ERROR("Compiling the excluding regular expression \"%s\" failed.", - excluderegex); - sfree(obj); - return NULL; - } - obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX; - } - - obj->callback = callback; - obj->user_data = user_data; - obj->free = free_user_data; - - return obj; -} /* cu_match_t *match_create_callback */ - -cu_match_t *match_create_simple(const char *regex, const char *excluderegex, - int match_ds_type) { - cu_match_value_t *user_data; - cu_match_t *obj; - - user_data = calloc(1, sizeof(*user_data)); - if (user_data == NULL) - return NULL; - user_data->ds_type = match_ds_type; - - if ((match_ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && - (match_ds_type & UTILS_MATCH_CF_GAUGE_DIST)) { - user_data->latency = latency_counter_create(); - if (user_data->latency == NULL) { - ERROR("match_create_simple(): latency_counter_create() failed."); - free(user_data); - return NULL; - } - } - - obj = match_create_callback(regex, excluderegex, default_callback, user_data, - match_simple_free); - if (obj == NULL) { - if (user_data->latency) - latency_counter_destroy(user_data->latency); - - sfree(user_data); - return NULL; - } - return obj; -} /* cu_match_t *match_create_simple */ - -void match_value_reset(cu_match_value_t *mv) { - if (mv == NULL) - return; - - /* Reset GAUGE metrics only and except GAUGE_PERSIST. */ - if ((mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) && - !(mv->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST)) { - mv->value.gauge = (mv->ds_type & UTILS_MATCH_CF_GAUGE_INC) ? 0 : NAN; - mv->values_num = 0; - } -} /* }}} void match_value_reset */ - -void match_destroy(cu_match_t *obj) { - if (obj == NULL) - return; - - if (obj->flags & UTILS_MATCH_FLAGS_REGEX) - regfree(&obj->regex); - if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) - regfree(&obj->excluderegex); - if ((obj->user_data != NULL) && (obj->free != NULL)) - (*obj->free)(obj->user_data); - - sfree(obj); -} /* void match_destroy */ - -int match_apply(cu_match_t *obj, const char *str) { - int status; - regmatch_t re_match[32]; - char *matches[32] = {0}; - size_t matches_num; - - if ((obj == NULL) || (str == NULL)) - return -1; - - if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) { - status = - regexec(&obj->excluderegex, str, STATIC_ARRAY_SIZE(re_match), re_match, - /* eflags = */ 0); - /* Regex did match, so exclude this line */ - if (status == 0) { - DEBUG("ExludeRegex matched, don't count that line\n"); - return 0; - } - } - - status = regexec(&obj->regex, str, STATIC_ARRAY_SIZE(re_match), re_match, - /* eflags = */ 0); - - /* Regex did not match */ - if (status != 0) - return 0; - - for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE(matches); - matches_num++) { - if ((re_match[matches_num].rm_so < 0) || (re_match[matches_num].rm_eo < 0)) - break; - - matches[matches_num] = match_substr(str, re_match[matches_num].rm_so, - re_match[matches_num].rm_eo); - if (matches[matches_num] == NULL) { - status = -1; - break; - } - } - - if (status != 0) { - ERROR("utils_match: match_apply: match_substr failed."); - } else { - status = obj->callback(str, matches, matches_num, obj->user_data); - if (status != 0) { - ERROR("utils_match: match_apply: callback failed."); - } - } - - for (size_t i = 0; i < matches_num; i++) { - sfree(matches[i]); - } - - return status; -} /* int match_apply */ - -void *match_get_user_data(cu_match_t *obj) { - if (obj == NULL) - return NULL; - return obj->user_data; -} /* void *match_get_user_data */ diff --git a/src/utils_match.h b/src/utils_match.h deleted file mode 100644 index 1cff1eb7..00000000 --- a/src/utils_match.h +++ /dev/null @@ -1,179 +0,0 @@ -/** - * collectd - src/utils_match.h - * Copyright (C) 2008-2014 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_MATCH_H -#define UTILS_MATCH_H 1 - -#include "plugin.h" -#include "utils_latency.h" - -/* - * Each type may have 12 sub-types - * 0x1000 = 1000000000000 - * ^ <- Type bit - * ^^^^^^^^^^^^ <- Subtype bits - */ -#define UTILS_MATCH_DS_TYPE_GAUGE 0x1000 -#define UTILS_MATCH_DS_TYPE_COUNTER 0x2000 -#define UTILS_MATCH_DS_TYPE_DERIVE 0x4000 -#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x8000 - -#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01 -#define UTILS_MATCH_CF_GAUGE_MIN 0x02 -#define UTILS_MATCH_CF_GAUGE_MAX 0x04 -#define UTILS_MATCH_CF_GAUGE_LAST 0x08 -#define UTILS_MATCH_CF_GAUGE_INC 0x10 -#define UTILS_MATCH_CF_GAUGE_ADD 0x20 -#define UTILS_MATCH_CF_GAUGE_PERSIST 0x40 -#define UTILS_MATCH_CF_GAUGE_DIST 0x80 - -#define UTILS_MATCH_CF_COUNTER_SET 0x01 -#define UTILS_MATCH_CF_COUNTER_ADD 0x02 -#define UTILS_MATCH_CF_COUNTER_INC 0x04 - -#define UTILS_MATCH_CF_DERIVE_SET 0x01 -#define UTILS_MATCH_CF_DERIVE_ADD 0x02 -#define UTILS_MATCH_CF_DERIVE_INC 0x04 - -#define UTILS_MATCH_CF_ABSOLUTE_SET 0x01 -#define UTILS_MATCH_CF_ABSOLUTE_ADD 0x02 -#define UTILS_MATCH_CF_ABSOLUTE_INC 0x04 - -/* - * Data types - */ -struct cu_match_s; -typedef struct cu_match_s cu_match_t; - -struct cu_match_value_s { - int ds_type; - value_t value; - unsigned int values_num; - latency_counter_t *latency; -}; -typedef struct cu_match_value_s cu_match_value_t; - -/* - * Prototypes - */ -/* - * NAME - * match_create_callback - * - * DESCRIPTION - * Creates a new `cu_match_t' object which will use the regular expression - * `regex' to match lines, see the `match_apply' method below. If the line - * matches, the callback passed in `callback' will be called along with the - * pointer `user_pointer'. - * The string that's passed to the callback depends on the regular expression: - * If the regular expression includes a sub-match, i. e. something like - * "value=([0-9][0-9]*)" - * then only the submatch (the part in the parenthesis) will be passed to the - * callback. If there is no submatch, then the entire string is passed to the - * callback. - * The optional `excluderegex' allows to exclude the line from the match, if - * the excluderegex matches. - * When `match_destroy' is called the `user_data' pointer is freed using - * the `free_user_data' callback - if it is not NULL. - */ -cu_match_t * -match_create_callback(const char *regex, const char *excluderegex, - int (*callback)(const char *str, char *const *matches, - size_t matches_num, void *user_data), - void *user_data, void (*free_user_data)(void *user_data)); - -/* - * NAME - * match_create_simple - * - * DESCRIPTION - * Creates a new `cu_match_t' with a default callback. The user data for that - * default callback will be a `cu_match_value_t' structure, with - * `ds_type' copied to the structure. The default callback will handle the - * string as containing a number (see strtoll(3) and strtod(3)) and store that - * number in the `value' member. How that is done depends on `ds_type': - * - * UTILS_MATCH_DS_TYPE_GAUGE - * The function will search for a floating point number in the string and - * store it in value.gauge. - * UTILS_MATCH_DS_TYPE_COUNTER_SET - * The function will search for an integer in the string and store it in - * value.counter. - * UTILS_MATCH_DS_TYPE_COUNTER_ADD - * The function will search for an integer in the string and add it to the - * value in value.counter. - * UTILS_MATCH_DS_TYPE_COUNTER_INC - * The function will not search for anything in the string and increase - * value.counter by one. - */ -cu_match_t *match_create_simple(const char *regex, const char *excluderegex, - int ds_type); - -/* - * NAME - * match_value_reset - * - * DESCRIPTION - * Resets the internal state, if applicable. This function must be called - * after each iteration for "simple" matches, usually after dispatching the - * metrics. - */ -void match_value_reset(cu_match_value_t *mv); - -/* - * NAME - * match_destroy - * - * DESCRIPTION - * Destroys the object and frees all internal resources. - */ -void match_destroy(cu_match_t *obj); - -/* - * NAME - * match_apply - * - * DESCRIPTION - * Tries to match the string `str' with the regular expression of `obj'. If - * the string matches, calls the callback in `obj' with the (sub-)match. - * - * The user_data pointer passed to `match_create_callback' is NOT freed - * automatically. The `cu_match_value_t' structure allocated by - * `match_create_callback' is freed automatically. - */ -int match_apply(cu_match_t *obj, const char *str); - -/* - * NAME - * match_get_user_data - * - * DESCRIPTION - * Returns the pointer passed to `match_create_callback' or a pointer to the - * `cu_match_value_t' structure allocated by `match_create_simple'. - */ -void *match_get_user_data(cu_match_t *obj); - -#endif /* UTILS_MATCH_H */ diff --git a/src/utils_mount.c b/src/utils_mount.c deleted file mode 100644 index 279f8e2f..00000000 --- a/src/utils_mount.c +++ /dev/null @@ -1,765 +0,0 @@ -/** - * collectd - src/utils_mount.c - * Copyright (C) 2005,2006 Niki W. Waibel - * - * This program is free software; you can redistribute it and/ - * or modify it under the terms of the GNU General Public Li- - * cence as published by the Free Software Foundation; either - * version 2 of the Licence, or any later version. - * - * This program is distributed in the hope that it will be use- - * ful, but WITHOUT ANY WARRANTY; without even the implied war- - * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public Licence for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: - * Niki W. Waibel -**/ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#define _GNU_SOURCE - -#include "collectd.h" - -#include "utils_mount.h" - -#if HAVE_XFS_XQM_H -#include -#define XFS_SUPER_MAGIC_STR "XFSB" -#define XFS_SUPER_MAGIC2_STR "BSFX" -#endif - -#include "common.h" /* sstrncpy() et alii */ -#include "plugin.h" /* ERROR() macro */ - -#if HAVE_GETVFSSTAT -#if HAVE_SYS_TYPES_H -#include -#endif -#if HAVE_SYS_STATVFS_H -#include -#endif -/* #endif HAVE_GETVFSSTAT */ - -#elif HAVE_GETFSSTAT -#if HAVE_SYS_PARAM_H -#include -#endif -#if HAVE_SYS_UCRED_H -#include -#endif -#if HAVE_SYS_MOUNT_H -#include -#endif -#endif /* HAVE_GETFSSTAT */ - -#if HAVE_MNTENT_H -#include -#endif -#if HAVE_SYS_MNTTAB_H -#include -#endif - -#if HAVE_PATHS_H -#include -#endif - -#ifdef COLLECTD_MNTTAB -#undef COLLECTD_MNTTAB -#endif - -#if defined(_PATH_MOUNTED) /* glibc */ -#define COLLECTD_MNTTAB _PATH_MOUNTED -#elif defined(MNTTAB) /* Solaris */ -#define COLLECTD_MNTTAB MNTTAB -#elif defined(MNT_MNTTAB) -#define COLLECTD_MNTTAB MNT_MNTTAB -#elif defined(MNTTABNAME) -#define COLLECTD_MNTTAB MNTTABNAME -#elif defined(KMTAB) -#define COLLECTD_MNTTAB KMTAB -#else -#define COLLECTD_MNTTAB "/etc/mnttab" -#endif - -/* *** *** *** ********************************************* *** *** *** */ -/* *** *** *** *** *** *** private functions *** *** *** *** *** *** */ -/* *** *** *** ********************************************* *** *** *** */ - -/* stolen from quota-3.13 (quota-tools) */ - -#define PROC_PARTITIONS "/proc/partitions" -#define DEVLABELDIR "/dev" -#define UUID 1 -#define VOL 2 - -static struct uuidCache_s { - struct uuidCache_s *next; - char uuid[16]; - char *label; - char *device; -} *uuidCache = NULL; - -#define EXT2_SUPER_MAGIC 0xEF53 -struct ext2_super_block { - unsigned char s_dummy1[56]; - unsigned char s_magic[2]; - unsigned char s_dummy2[46]; - unsigned char s_uuid[16]; - char s_volume_name[16]; -}; -#define ext2magic(s) \ - ((unsigned int)s.s_magic[0] + (((unsigned int)s.s_magic[1]) << 8)) - -#if HAVE_XFS_XQM_H -struct xfs_super_block { - unsigned char s_magic[4]; - unsigned char s_dummy[28]; - unsigned char s_uuid[16]; - unsigned char s_dummy2[60]; - char s_fsname[12]; -}; -#endif /* HAVE_XFS_XQM_H */ - -#define REISER_SUPER_MAGIC "ReIsEr2Fs" -struct reiserfs_super_block { - unsigned char s_dummy1[52]; - unsigned char s_magic[10]; - unsigned char s_dummy2[22]; - unsigned char s_uuid[16]; - char s_volume_name[16]; -}; - -/* for now, only ext2 and xfs are supported */ -static int get_label_uuid(const char *device, char **label, char *uuid) { - /* start with ext2 and xfs tests, taken from mount_guess_fstype */ - /* should merge these later */ - int fd, rv = 1; - size_t namesize; - struct ext2_super_block e2sb; -#if HAVE_XFS_XQM_H - struct xfs_super_block xfsb; -#endif - struct reiserfs_super_block reisersb; - - fd = open(device, O_RDONLY); - if (fd == -1) { - return rv; - } - - if (lseek(fd, 1024, SEEK_SET) == 1024 && - read(fd, (char *)&e2sb, sizeof(e2sb)) == sizeof(e2sb) && - ext2magic(e2sb) == EXT2_SUPER_MAGIC) { - memcpy(uuid, e2sb.s_uuid, sizeof(e2sb.s_uuid)); - namesize = sizeof(e2sb.s_volume_name); - *label = smalloc(namesize + 1); - sstrncpy(*label, e2sb.s_volume_name, namesize); - rv = 0; -#if HAVE_XFS_XQM_H - } else if (lseek(fd, 0, SEEK_SET) == 0 && - read(fd, (char *)&xfsb, sizeof(xfsb)) == sizeof(xfsb) && - (strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC_STR, 4) == 0 || - strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC2_STR, 4) == 0)) { - memcpy(uuid, xfsb.s_uuid, sizeof(xfsb.s_uuid)); - namesize = sizeof(xfsb.s_fsname); - *label = smalloc(namesize + 1); - sstrncpy(*label, xfsb.s_fsname, namesize); - rv = 0; -#endif /* HAVE_XFS_XQM_H */ - } else if (lseek(fd, 65536, SEEK_SET) == 65536 && - read(fd, (char *)&reisersb, sizeof(reisersb)) == - sizeof(reisersb) && - !strncmp((char *)&reisersb.s_magic, REISER_SUPER_MAGIC, 9)) { - memcpy(uuid, reisersb.s_uuid, sizeof(reisersb.s_uuid)); - namesize = sizeof(reisersb.s_volume_name); - *label = smalloc(namesize + 1); - sstrncpy(*label, reisersb.s_volume_name, namesize); - rv = 0; - } - close(fd); - return rv; -} - -static void uuidcache_addentry(char *device, char *label, char *uuid) { - struct uuidCache_s *last; - - if (!uuidCache) { - last = uuidCache = smalloc(sizeof(*uuidCache)); - } else { - for (last = uuidCache; last->next; last = last->next) - ; - last->next = smalloc(sizeof(*uuidCache)); - last = last->next; - } - last->next = NULL; - last->device = device; - last->label = label; - memcpy(last->uuid, uuid, sizeof(last->uuid)); -} - -static void uuidcache_init(void) { - char line[100]; - char *s; - int ma, mi, sz; - static char ptname[100]; - FILE *procpt; - char uuid[16], *label = NULL; - char device[110]; - int handleOnFirst; - - if (uuidCache) { - return; - } - - procpt = fopen(PROC_PARTITIONS, "r"); - if (procpt == NULL) { - return; - } - - for (int firstPass = 1; firstPass >= 0; firstPass--) { - fseek(procpt, 0, SEEK_SET); - while (fgets(line, sizeof(line), procpt)) { - if (sscanf(line, " %d %d %d %[^\n ]", &ma, &mi, &sz, ptname) != 4) { - continue; - } - - /* skip extended partitions (heuristic: size 1) */ - if (sz == 1) { - continue; - } - - /* look only at md devices on first pass */ - handleOnFirst = !strncmp(ptname, "md", 2); - if (firstPass != handleOnFirst) { - continue; - } - - /* skip entire disk (minor 0, 64, ... on ide; - 0, 16, ... on sd) */ - /* heuristic: partition name ends in a digit */ - - for (s = ptname; *s; s++) - ; - - if (isdigit((int)s[-1])) { - /* - * Note: this is a heuristic only - there is no reason - * why these devices should live in /dev. - * Perhaps this directory should be specifiable by option. - * One might for example have /devlabel with links to /dev - * for the devices that may be accessed in this way. - * (This is useful, if the cdrom on /dev/hdc must not - * be accessed.) - */ - snprintf(device, sizeof(device), "%s/%s", DEVLABELDIR, ptname); - if (!get_label_uuid(device, &label, uuid)) { - uuidcache_addentry(sstrdup(device), label, uuid); - } - } - } - } - fclose(procpt); -} - -static unsigned char fromhex(char c) { - if (isdigit((int)c)) { - return c - '0'; - } else if (islower((int)c)) { - return c - 'a' + 10; - } else { - return c - 'A' + 10; - } -} - -static char *get_spec_by_x(int n, const char *t) { - struct uuidCache_s *uc; - - uuidcache_init(); - uc = uuidCache; - - while (uc) { - switch (n) { - case UUID: - if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) { - return sstrdup(uc->device); - } - break; - case VOL: - if (!strcmp(t, uc->label)) { - return sstrdup(uc->device); - } - break; - } - uc = uc->next; - } - return NULL; -} - -static char *get_spec_by_uuid(const char *s) { - char uuid[16]; - - if (strlen(s) != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' || - s[23] != '-') { - goto bad_uuid; - } - - for (int i = 0; i < 16; i++) { - if (*s == '-') { - s++; - } - if (!isxdigit((int)s[0]) || !isxdigit((int)s[1])) { - goto bad_uuid; - } - uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1])); - s += 2; - } - return get_spec_by_x(UUID, uuid); - -bad_uuid: - DEBUG("utils_mount: Found an invalid UUID: %s", s); - return NULL; -} - -static char *get_spec_by_volume_label(const char *s) { - return get_spec_by_x(VOL, s); -} - -static char *get_device_name(const char *optstr) { - char *rc; - - if (optstr == NULL) { - return NULL; - } else if (strncmp(optstr, "UUID=", 5) == 0) { - DEBUG("utils_mount: TODO: check UUID= code!"); - rc = get_spec_by_uuid(optstr + 5); - } else if (strncmp(optstr, "LABEL=", 6) == 0) { - DEBUG("utils_mount: TODO: check LABEL= code!"); - rc = get_spec_by_volume_label(optstr + 6); - } else { - rc = sstrdup(optstr); - } - - if (!rc) { - DEBUG("utils_mount: Error checking device name: optstr = %s", optstr); - } - return rc; -} - -/* What weird OS is this..? I can't find any info with google :/ -octo */ -#if HAVE_LISTMNTENT && 0 -static cu_mount_t *cu_mount_listmntent(void) { - cu_mount_t *last = *list; - struct mntent *mnt; - - struct tabmntent *mntlist; - if (listmntent(&mntlist, COLLECTD_MNTTAB, NULL, NULL) < 0) { -#if COLLECT_DEBUG - DEBUG("utils_mount: calling listmntent() failed: %s", STRERRNO); -#endif /* COLLECT_DEBUG */ - } - - for (struct tabmntent *p = mntlist; p; p = p->next) { - char *loop = NULL, *device = NULL; - - mnt = p->ment; - loop = cu_mount_getoptionvalue(mnt->mnt_opts, "loop="); - if (loop == NULL) { /* no loop= mount */ - device = get_device_name(mnt->mnt_fsname); - if (device == NULL) { - DEBUG("utils_mount: can't get devicename for fs (%s) %s (%s)" - ": ignored", - mnt->mnt_type, mnt->mnt_dir, mnt->mnt_fsname); - continue; - } - } else { - device = loop; - } - if (*list == NULL) { - *list = (cu_mount_t *)smalloc(sizeof(cu_mount_t)); - last = *list; - } else { - while (last->next != NULL) { /* is last really last? */ - last = last->next; - } - last->next = (cu_mount_t *)smalloc(sizeof(cu_mount_t)); - last = last->next; - } - last->dir = sstrdup(mnt->mnt_dir); - last->spec_device = sstrdup(mnt->mnt_fsname); - last->device = device; - last->type = sstrdup(mnt->mnt_type); - last->options = sstrdup(mnt->mnt_opts); - last->next = NULL; - } /* for(p = mntlist; p; p = p->next) */ - - return last; -} /* cu_mount_t *cu_mount_listmntent(void) */ -/* #endif HAVE_LISTMNTENT */ - -/* 4.4BSD and Mac OS X (getfsstat) or NetBSD (getvfsstat) */ -#elif HAVE_GETVFSSTAT || HAVE_GETFSSTAT -static cu_mount_t *cu_mount_getfsstat(void) { -#if HAVE_GETFSSTAT -#define STRUCT_STATFS struct statfs -#define CMD_STATFS getfsstat -#define FLAGS_STATFS MNT_NOWAIT -/* #endif HAVE_GETFSSTAT */ -#elif HAVE_GETVFSSTAT -#define STRUCT_STATFS struct statvfs -#define CMD_STATFS getvfsstat -#define FLAGS_STATFS ST_NOWAIT -#endif /* HAVE_GETVFSSTAT */ - - int bufsize; - STRUCT_STATFS *buf; - - int num; - - cu_mount_t *first = NULL; - cu_mount_t *last = NULL; - cu_mount_t *new = NULL; - - /* Get the number of mounted file systems */ - if ((bufsize = CMD_STATFS(NULL, 0, FLAGS_STATFS)) < 1) { -#if COLLECT_DEBUG - DEBUG("utils_mount: getv?fsstat failed: %s", STRERRNO); -#endif /* COLLECT_DEBUG */ - return NULL; - } - - if ((buf = calloc(bufsize, sizeof(*buf))) == NULL) - return NULL; - - /* The bufsize needs to be passed in bytes. Really. This is not in the - * manpage.. -octo */ - if ((num = CMD_STATFS(buf, bufsize * sizeof(STRUCT_STATFS), FLAGS_STATFS)) < - 1) { -#if COLLECT_DEBUG - DEBUG("utils_mount: getv?fsstat failed: %s", STRERRNO); -#endif /* COLLECT_DEBUG */ - free(buf); - return NULL; - } - - for (int i = 0; i < num; i++) { - if ((new = calloc(1, sizeof(*new))) == NULL) - break; - - /* Copy values from `struct mnttab' */ - new->dir = sstrdup(buf[i].f_mntonname); - new->spec_device = sstrdup(buf[i].f_mntfromname); - new->type = sstrdup(buf[i].f_fstypename); - new->options = NULL; - new->device = get_device_name(new->options); - new->next = NULL; - - /* Append to list */ - if (first == NULL) { - first = new; - last = new; - } else { - last->next = new; - last = new; - } - } - - free(buf); - - return first; -} -/* #endif HAVE_GETVFSSTAT || HAVE_GETFSSTAT */ - -/* Solaris (SunOS 10): int getmntent(FILE *fp, struct mnttab *mp); */ -#elif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT -static cu_mount_t *cu_mount_gen_getmntent(void) { - struct mnttab mt; - FILE *fp; - - cu_mount_t *first = NULL; - cu_mount_t *last = NULL; - cu_mount_t *new = NULL; - - DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB); - - if ((fp = fopen(COLLECTD_MNTTAB, "r")) == NULL) { - ERROR("fopen (%s): %s", COLLECTD_MNTTAB, STRERRNO); - return NULL; - } - - while (getmntent(fp, &mt) == 0) { - if ((new = calloc(1, sizeof(*new))) == NULL) - break; - - /* Copy values from `struct mnttab' */ - new->dir = sstrdup(mt.mnt_mountp); - new->spec_device = sstrdup(mt.mnt_special); - new->type = sstrdup(mt.mnt_fstype); - new->options = sstrdup(mt.mnt_mntopts); - new->device = get_device_name(new->options); - new->next = NULL; - - /* Append to list */ - if (first == NULL) { - first = new; - last = new; - } else { - last->next = new; - last = new; - } - } - - fclose(fp); - - return first; -} /* static cu_mount_t *cu_mount_gen_getmntent (void) */ - -#elif HAVE_SEQ_GETMNTENT -#warn "This version of `getmntent' hat not yet been implemented!" -/* #endif HAVE_SEQ_GETMNTENT */ - -#elif HAVE_GETMNTENT_R -static cu_mount_t *cu_mount_getmntent(void) { - FILE *fp; - struct mntent me; - char mntbuf[1024]; - - cu_mount_t *first = NULL; - cu_mount_t *last = NULL; - cu_mount_t *new = NULL; - - DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB); - - if ((fp = setmntent(COLLECTD_MNTTAB, "r")) == NULL) { - ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, STRERRNO); - return NULL; - } - - while (getmntent_r(fp, &me, mntbuf, sizeof(mntbuf))) { - if ((new = calloc(1, sizeof(*new))) == NULL) - break; - - /* Copy values from `struct mntent *' */ - new->dir = sstrdup(me.mnt_dir); - new->spec_device = sstrdup(me.mnt_fsname); - new->type = sstrdup(me.mnt_type); - new->options = sstrdup(me.mnt_opts); - new->device = get_device_name(new->options); - new->next = NULL; - - DEBUG("utils_mount: new = {dir = %s, spec_device = %s, type = %s, options " - "= %s, device = %s}", - new->dir, new->spec_device, new->type, new->options, new->device); - - /* Append to list */ - if (first == NULL) { - first = new; - last = new; - } else { - last->next = new; - last = new; - } - } - - endmntent(fp); - - DEBUG("utils_mount: return 0x%p", (void *)first); - - return first; -} /* HAVE_GETMNTENT_R */ - -#elif HAVE_ONE_GETMNTENT -static cu_mount_t *cu_mount_getmntent(void) { - FILE *fp; - struct mntent *me; - - cu_mount_t *first = NULL; - cu_mount_t *last = NULL; - cu_mount_t *new = NULL; - - DEBUG("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB); - - if ((fp = setmntent(COLLECTD_MNTTAB, "r")) == NULL) { - ERROR("setmntent (%s): %s", COLLECTD_MNTTAB, STRERRNO); - return NULL; - } - - while ((me = getmntent(fp)) != NULL) { - if ((new = calloc(1, sizeof(*new))) == NULL) - break; - - /* Copy values from `struct mntent *' */ - new->dir = sstrdup(me->mnt_dir); - new->spec_device = sstrdup(me->mnt_fsname); - new->type = sstrdup(me->mnt_type); - new->options = sstrdup(me->mnt_opts); - new->device = get_device_name(new->options); - new->next = NULL; - - DEBUG("utils_mount: new = {dir = %s, spec_device = %s, type = %s, options " - "= %s, device = %s}", - new->dir, new->spec_device, new->type, new->options, new->device); - - /* Append to list */ - if (first == NULL) { - first = new; - last = new; - } else { - last->next = new; - last = new; - } - } - - endmntent(fp); - - DEBUG("utils_mount: return 0x%p", (void *)first); - - return first; -} -#endif /* HAVE_ONE_GETMNTENT */ - -/* *** *** *** ******************************************** *** *** *** */ -/* *** *** *** *** *** *** public functions *** *** *** *** *** *** */ -/* *** *** *** ******************************************** *** *** *** */ - -cu_mount_t *cu_mount_getlist(cu_mount_t **list) { - cu_mount_t *new; - cu_mount_t *first = NULL; - cu_mount_t *last = NULL; - - if (list == NULL) - return NULL; - - if (*list != NULL) { - first = *list; - last = first; - while (last->next != NULL) - last = last->next; - } - -#if HAVE_LISTMNTENT && 0 - new = cu_mount_listmntent(); -#elif HAVE_GETVFSSTAT || HAVE_GETFSSTAT - new = cu_mount_getfsstat(); -#elif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT - new = cu_mount_gen_getmntent(); -#elif HAVE_SEQ_GETMNTENT -#error "This version of `getmntent' hat not yet been implemented!" -#elif HAVE_GETMNTENT_R - new = cu_mount_getmntent(); -#elif HAVE_ONE_GETMNTENT - new = cu_mount_getmntent(); -#else -#error "Could not determine how to find mountpoints." -#endif - - if (first != NULL) { - last->next = new; - } else { - first = new; - last = new; - *list = first; - } - - while ((last != NULL) && (last->next != NULL)) - last = last->next; - - return last; -} /* cu_mount_t *cu_mount_getlist(cu_mount_t **list) */ - -void cu_mount_freelist(cu_mount_t *list) { - cu_mount_t *next; - - for (cu_mount_t *this = list; this != NULL; this = next) { - next = this->next; - - sfree(this->dir); - sfree(this->spec_device); - sfree(this->device); - sfree(this->type); - sfree(this->options); - sfree(this); - } -} /* void cu_mount_freelist(cu_mount_t *list) */ - -char *cu_mount_checkoption(char *line, const char *keyword, int full) { - char *line2, *l2, *p1, *p2; - size_t l; - - if (line == NULL || keyword == NULL) { - return NULL; - } - - if (full != 0) { - full = 1; - } - - line2 = sstrdup(line); - l2 = line2; - while (*l2 != '\0') { - if (*l2 == ',') { - *l2 = '\0'; - } - l2++; - } - - l = strlen(keyword); - p1 = line - 1; - p2 = strchr(line, ','); - do { - if (strncmp(line2 + (p1 - line) + 1, keyword, l + full) == 0) { - free(line2); - return p1 + 1; - } - p1 = p2; - if (p1 != NULL) { - p2 = strchr(p1 + 1, ','); - } - } while (p1 != NULL); - - free(line2); - return NULL; -} /* char *cu_mount_checkoption(char *line, char *keyword, int full) */ - -char *cu_mount_getoptionvalue(char *line, const char *keyword) { - char *r; - - r = cu_mount_checkoption(line, keyword, 0); - if (r != NULL) { - char *p; - r += strlen(keyword); - p = strchr(r, ','); - if (p == NULL) { - return sstrdup(r); - } else { - char *m; - if ((p - r) == 1) { - return NULL; - } - m = smalloc(p - r + 1); - sstrncpy(m, r, p - r + 1); - return m; - } - } - return r; -} /* char *cu_mount_getoptionvalue(char *line, const char *keyword) */ - -int cu_mount_type(const char *type) { - if (strcmp(type, "ext3") == 0) - return CUMT_EXT3; - if (strcmp(type, "ext2") == 0) - return CUMT_EXT2; - if (strcmp(type, "ufs") == 0) - return CUMT_UFS; - if (strcmp(type, "vxfs") == 0) - return CUMT_VXFS; - if (strcmp(type, "zfs") == 0) - return CUMT_ZFS; - return CUMT_UNKNOWN; -} /* int cu_mount_type(const char *type) */ diff --git a/src/utils_mount.h b/src/utils_mount.h deleted file mode 100644 index 0ad7d020..00000000 --- a/src/utils_mount.h +++ /dev/null @@ -1,186 +0,0 @@ -/** - * collectd - src/utils_mount.h - * Copyright (C) 2005,2006 Niki W. Waibel - * - * This program is free software; you can redistribute it and/ - * or modify it under the terms of the GNU General Public Li- - * cence as published by the Free Software Foundation; either - * version 2 of the Licence, or any later version. - * - * This program is distributed in the hope that it will be use- - * ful, but WITHOUT ANY WARRANTY; without even the implied war- - * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public Licence for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: - * Niki W. Waibel -**/ - -/* See below for instructions how to use the public functions. */ - -#ifndef COLLECTD_UTILS_MOUNT_H -#define COLLECTD_UTILS_MOUNT_H 1 - -#include -#if HAVE_FS_INFO_H -#include -#endif -#if HAVE_FSHELP_H -#include -#endif -#if HAVE_PATHS_H -#include -#endif -#if HAVE_MNTENT_H -#include -#endif -#if HAVE_MNTTAB_H -#include -#endif -#if HAVE_SYS_FSTYP_H -#include -#endif -#if HAVE_SYS_FS_TYPES_H -#include -#endif -#if HAVE_SYS_MNTENT_H -#include -#endif -#if HAVE_SYS_MNTTAB_H -#include -#endif -#if HAVE_SYS_MOUNT_H -#include -#endif -#if HAVE_SYS_STATFS_H -#include -#endif -#if HAVE_SYS_VFS_H -#include -#endif -#if HAVE_SYS_VFSTAB_H -#include -#endif - -/* Collectd Utils Mount Type */ -#define CUMT_UNKNOWN (0) -#define CUMT_EXT2 (1) -#define CUMT_EXT3 (2) -#define CUMT_XFS (3) -#define CUMT_UFS (4) -#define CUMT_VXFS (5) -#define CUMT_ZFS (6) - -typedef struct _cu_mount_t cu_mount_t; -struct _cu_mount_t { - char *dir; /* "/sys" or "/" */ - char *spec_device; /* "LABEL=/" or "none" or "proc" or "/dev/hda1" */ - char *device; /* "none" or "proc" or "/dev/hda1" */ - char *type; /* "sysfs" or "ext3" */ - char *options; /* "rw,noatime,commit=600,quota,grpquota" */ - cu_mount_t *next; -}; - -cu_mount_t *cu_mount_getlist(cu_mount_t **list); -/* - DESCRIPTION - The cu_mount_getlist() function creates a list - of all mountpoints. - - If *list is NULL, a new list is created and *list is - set to point to the first entry. - - If *list is not NULL, the list of mountpoints is appended - and *list is not changed. - - RETURN VALUE - The cu_mount_getlist() function returns a pointer to - the last entry of the list, or NULL if an error has - occured. - - NOTES - In case of an error, *list is not modified. -*/ - -void cu_mount_freelist(cu_mount_t *list); -/* - DESCRIPTION - The cu_mount_freelist() function free()s all memory - allocated by *list and *list itself as well. -*/ - -char *cu_mount_checkoption(char *line, const char *keyword, int full); -/* - DESCRIPTION - The cu_mount_checkoption() function is a replacement of - char *hasmntopt(const struct mntent *mnt, const char *opt). - In fact hasmntopt() just looks for the first occurrence of the - characters at opt in mnt->mnt_opts. cu_mount_checkoption() - checks for the *option* keyword in line, starting at the - first character of line or after a ','. - - If full is not 0 then also the end of keyword has to match - either the end of line or a ',' after keyword. - - RETURN VALUE - The cu_mount_checkoption() function returns a pointer into - string line if a match of keyword is found. If no match is - found cu_mount_checkoption() returns NULL. - - NOTES - Do *not* try to free() the pointer which is returned! It is - just part of the string line. - - full should be set to 0 when matching options like: rw, quota, - noatime. Set full to 1 when matching options like: loop=, - gid=, commit=. - - EXAMPLES - If line is "rw,usrquota,grpquota", keyword is "quota", NULL - will be returned (independend of full). - - If line is "rw,usrquota,grpquota", keyword is "usrquota", - a pointer to "usrquota,grpquota" is returned (independend - of full). - - If line is "rw,loop=/dev/loop1,quota", keyword is "loop=" - and full is 0, then a pointer to "loop=/dev/loop1,quota" - is returned. If full is not 0 then NULL is returned. But - maybe you might want to try cu_mount_getoptionvalue()... -*/ - -char *cu_mount_getoptionvalue(char *line, const char *keyword); -/* - DESCRIPTION - The cu_mount_getoptionvalue() function can be used to grab - a VALUE out of a mount option (line) like: - loop=VALUE - whereas "loop=" is the keyword. - - RETURN VALUE - If the cu_mount_getoptionvalue() function can find the option - keyword in line, then memory is allocated for the value of - that option and a pointer to that value is returned. - - If the option keyword is not found, cu_mount_getoptionvalue() - returns NULL; - - NOTES - Internally it calls cu_mount_checkoption(), then it - allocates memory for VALUE and returns a pointer to that - string. So *do not forget* to free() the memory returned - after use!!! -*/ - -int cu_mount_type(const char *type); -/* - DESCRIPTION - - RETURN VALUE -*/ - -#endif /* !COLLECTD_UTILS_MOUNT_H */ diff --git a/src/utils_mount_test.c b/src/utils_mount_test.c deleted file mode 100644 index e8f30094..00000000 --- a/src/utils_mount_test.c +++ /dev/null @@ -1,115 +0,0 @@ -/** - * collectd - src/tests/test_utils_mount.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "collectd.h" - -#include "common.h" -#include "testing.h" -#include "utils_mount.h" - -#if HAVE_KSTAT_H -#include -#endif - -#if HAVE_LIBKSTAT -kstat_ctl_t *kc; -#endif /* HAVE_LIBKSTAT */ - -DEF_TEST(cu_mount_checkoption) { - char line_opts[] = "foo=one,bar=two,qux=three"; - char *foo = strstr(line_opts, "foo"); - char *bar = strstr(line_opts, "bar"); - char *qux = strstr(line_opts, "qux"); - - char line_bool[] = "one,two,three"; - char *one = strstr(line_bool, "one"); - char *two = strstr(line_bool, "two"); - char *three = strstr(line_bool, "three"); - - /* Normal operation */ - OK(foo == cu_mount_checkoption(line_opts, "foo", 0)); - OK(bar == cu_mount_checkoption(line_opts, "bar", 0)); - OK(qux == cu_mount_checkoption(line_opts, "qux", 0)); - OK(NULL == cu_mount_checkoption(line_opts, "unknown", 0)); - - OK(one == cu_mount_checkoption(line_bool, "one", 0)); - OK(two == cu_mount_checkoption(line_bool, "two", 0)); - OK(three == cu_mount_checkoption(line_bool, "three", 0)); - OK(NULL == cu_mount_checkoption(line_bool, "four", 0)); - - /* Shorter and longer parts */ - OK(foo == cu_mount_checkoption(line_opts, "fo", 0)); - OK(bar == cu_mount_checkoption(line_opts, "bar=", 0)); - OK(qux == cu_mount_checkoption(line_opts, "qux=thr", 0)); - - OK(one == cu_mount_checkoption(line_bool, "o", 0)); - OK(two == cu_mount_checkoption(line_bool, "tw", 0)); - OK(three == cu_mount_checkoption(line_bool, "thr", 0)); - - /* "full" flag */ - OK(one == cu_mount_checkoption(line_bool, "one", 1)); - OK(two == cu_mount_checkoption(line_bool, "two", 1)); - OK(three == cu_mount_checkoption(line_bool, "three", 1)); - OK(NULL == cu_mount_checkoption(line_bool, "four", 1)); - - OK(NULL == cu_mount_checkoption(line_bool, "o", 1)); - OK(NULL == cu_mount_checkoption(line_bool, "tw", 1)); - OK(NULL == cu_mount_checkoption(line_bool, "thr", 1)); - - return 0; -} -DEF_TEST(cu_mount_getoptionvalue) { - char line_opts[] = "foo=one,bar=two,qux=three"; - char line_bool[] = "one,two,three"; - char *v; - - EXPECT_EQ_STR("one", v = cu_mount_getoptionvalue(line_opts, "foo=")); - sfree(v); - EXPECT_EQ_STR("two", v = cu_mount_getoptionvalue(line_opts, "bar=")); - sfree(v); - EXPECT_EQ_STR("three", v = cu_mount_getoptionvalue(line_opts, "qux=")); - sfree(v); - OK(NULL == (v = cu_mount_getoptionvalue(line_opts, "unknown="))); - sfree(v); - - EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "one")); - sfree(v); - EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "two")); - sfree(v); - EXPECT_EQ_STR("", v = cu_mount_getoptionvalue(line_bool, "three")); - sfree(v); - OK(NULL == (v = cu_mount_getoptionvalue(line_bool, "four"))); - sfree(v); - - return 0; -} - -int main(void) { - RUN_TEST(cu_mount_checkoption); - RUN_TEST(cu_mount_getoptionvalue); - - END_TEST; -} diff --git a/src/utils_oauth.c b/src/utils_oauth.c deleted file mode 100644 index d804b51a..00000000 --- a/src/utils_oauth.c +++ /dev/null @@ -1,637 +0,0 @@ -/** - * 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 deleted file mode 100644 index b93c87b8..00000000 --- a/src/utils_oauth.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * 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 deleted file mode 100644 index 791564fb..00000000 --- a/src/utils_oauth_test.c +++ /dev/null @@ -1,149 +0,0 @@ -/** - * 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 deleted file mode 100644 index 4ca86aeb..00000000 --- a/src/utils_ovs.c +++ /dev/null @@ -1,1409 +0,0 @@ -/** - * collectd - src/utils_ovs.c - * - * Copyright(c) 2016 Intel Corporation. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - *of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to - *do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - *all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Authors: - * Volodymyr Mytnyk - **/ - -/* clang-format off */ -/* - * OVS DB API internal architecture diagram - * +------------------------------------------------------------------------------+ - * |OVS plugin |OVS utils | - * | | +------------------------+ | - * | | | echo handler | JSON request/ | - * | | +--+ (ovs_db_table_echo_cb) +<---+---------+ update event/ | - * | | | | | | | result | - * | | | +------------------------+ | | | - * | | | | +----+---+--------+ | - * | +----------+ | | +------------------------+ | | | | | - * | | update | | | | update handler | | | YAJL | JSON | | - * | | callback +<-------+(ovs_db_table_update_cp)+<---+ | parser | reader | | - * | +----------+ | | | | | | | | | - * | | | +------------------------+ | +--------+---+----+ | - * | | | | ^ | - * | +----------+ | | +------------------------+ | | | - * | | result | | | | result handler | | | | - * | | callback +<-------+ (ovs_db_result_cb) +<---+ JSON raw | | - * | +----------+ | | | | data | | - * | | | +------------------------+ | | - * | | | | | - * | | | +------------------+ +------------+----+ | - * | +----------+ | | |thread| | |thread| | | - * | | init | | | | | reconnect | | | - * | | callback +<---------+ EVENT WORKER +<------------+ POLL WORKER | | - * | +----------+ | | +------------------+ +--------+--------+ | - * | | | ^ | - * +----------------+-------------------------------------------------------------+ - * | | - * JSON|echo reply raw|data - * v v - * +-------------------+----------------------------------------------+-----------+ - * | TCP/UNIX socket | - * +------------------------------------------------------------------------------- - */ -/* clang-format on */ - -/* collectd headers */ -#include "collectd.h" - -#include "common.h" - -/* private headers */ -#include "utils_ovs.h" - -/* system libraries */ -#if HAVE_NETDB_H -#include -#endif -#if HAVE_ARPA_INET_H -#include -#endif -#if HAVE_POLL_H -#include -#endif -#if HAVE_SYS_UN_H -#include -#endif - -#include - -#define OVS_ERROR(fmt, ...) \ - do { \ - ERROR("ovs_utils: " fmt, ##__VA_ARGS__); \ - } while (0) -#define OVS_DEBUG(fmt, ...) \ - do { \ - DEBUG("%s:%d:%s(): " fmt, __FILE__, __LINE__, __FUNCTION__, \ - ##__VA_ARGS__); \ - } while (0) - -#define OVS_DB_POLL_TIMEOUT 1 /* poll receive timeout (sec) */ -#define OVS_DB_POLL_READ_BLOCK_SIZE 512 /* read block size (bytes) */ -#define OVS_DB_DEFAULT_DB_NAME "Open_vSwitch" - -#define OVS_DB_EVENT_NONE 0 -#define OVS_DB_EVENT_TIMEOUT 5 /* event thread timeout (sec) */ -#define OVS_DB_EVENT_TERMINATE 1 -#define OVS_DB_EVENT_CONN_ESTABLISHED 2 -#define OVS_DB_EVENT_CONN_TERMINATED 3 - -#define OVS_DB_POLL_STATE_RUNNING 1 -#define OVS_DB_POLL_STATE_EXITING 2 - -#define OVS_DB_SEND_REQ_TIMEOUT 5 /* send request timeout (sec) */ - -#define OVS_YAJL_CALL(func, ...) \ - do { \ - yajl_gen_ret = yajl_gen_status_ok; \ - if ((yajl_gen_ret = func(__VA_ARGS__)) != yajl_gen_status_ok) \ - goto yajl_gen_failure; \ - } while (0) -#define OVS_YAJL_ERROR_BUFFER_SIZE 1024 -#define OVS_ERROR_BUFF_SIZE 512 -#define OVS_UID_STR_SIZE 17 /* 64-bit HEX string len + '\0' */ - -/* JSON reader internal data */ -struct ovs_json_reader_s { - char *buff_ptr; - size_t buff_size; - size_t buff_offset; - size_t json_offset; -}; -typedef struct ovs_json_reader_s ovs_json_reader_t; - -/* Result callback declaration */ -struct ovs_result_cb_s { - sem_t sync; - ovs_db_result_cb_t call; -}; -typedef struct ovs_result_cb_s ovs_result_cb_t; - -/* Table callback declaration */ -struct ovs_table_cb_s { - ovs_db_table_cb_t call; -}; -typedef struct ovs_table_cb_s ovs_table_cb_t; - -/* Callback declaration */ -struct ovs_callback_s { - uint64_t uid; - union { - ovs_result_cb_t result; - ovs_table_cb_t table; - }; - struct ovs_callback_s *next; - struct ovs_callback_s *prev; -}; -typedef struct ovs_callback_s ovs_callback_t; - -/* Event thread data declaration */ -struct ovs_event_thread_s { - pthread_t tid; - pthread_mutex_t mutex; - pthread_cond_t cond; - int value; -}; -typedef struct ovs_event_thread_s ovs_event_thread_t; - -/* Poll thread data declaration */ -struct ovs_poll_thread_s { - pthread_t tid; - pthread_mutex_t mutex; - int state; -}; -typedef struct ovs_poll_thread_s ovs_poll_thread_t; - -/* OVS DB internal data declaration */ -struct ovs_db_s { - ovs_poll_thread_t poll_thread; - ovs_event_thread_t event_thread; - pthread_mutex_t mutex; - ovs_callback_t *remote_cb; - ovs_db_callback_t cb; - char service[OVS_DB_ADDR_SERVICE_SIZE]; - char node[OVS_DB_ADDR_NODE_SIZE]; - char unix_path[OVS_DB_ADDR_NODE_SIZE]; - int sock; -}; - -/* Global variables */ -static uint64_t ovs_uid; -static pthread_mutex_t ovs_uid_mutex = PTHREAD_MUTEX_INITIALIZER; - -/* Post an event to event thread. - * Possible events are: - * OVS_DB_EVENT_TERMINATE - * OVS_DB_EVENT_CONN_ESTABLISHED - * OVS_DB_EVENT_CONN_TERMINATED - */ -static void ovs_db_event_post(ovs_db_t *pdb, int event) { - pthread_mutex_lock(&pdb->event_thread.mutex); - pdb->event_thread.value = event; - pthread_mutex_unlock(&pdb->event_thread.mutex); - pthread_cond_signal(&pdb->event_thread.cond); -} - -/* 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) { - int state = 0; - pthread_mutex_lock(&pdb->poll_thread.mutex); - state = pdb->poll_thread.state; - pthread_mutex_unlock(&pdb->poll_thread.mutex); - return state == OVS_DB_POLL_STATE_RUNNING; -} - -/* Generate unique identifier (UID). It is used by OVS DB API - * to set "id" field for any OVS DB JSON request. */ -static uint64_t ovs_uid_generate() { - uint64_t new_uid; - pthread_mutex_lock(&ovs_uid_mutex); - new_uid = ++ovs_uid; - pthread_mutex_unlock(&ovs_uid_mutex); - return new_uid; -} - -/* - * Callback API. These function are used to store - * registered callbacks in OVS DB API. - */ - -/* Add new callback into OVS DB object */ -static void ovs_db_callback_add(ovs_db_t *pdb, ovs_callback_t *new_cb) { - pthread_mutex_lock(&pdb->mutex); - if (pdb->remote_cb) - pdb->remote_cb->prev = new_cb; - new_cb->next = pdb->remote_cb; - new_cb->prev = NULL; - pdb->remote_cb = new_cb; - pthread_mutex_unlock(&pdb->mutex); -} - -/* Remove callback from OVS DB object */ -static void ovs_db_callback_remove(ovs_db_t *pdb, ovs_callback_t *del_cb) { - pthread_mutex_lock(&pdb->mutex); - ovs_callback_t *pre_cb = del_cb->prev; - ovs_callback_t *next_cb = del_cb->next; - - if (next_cb) - next_cb->prev = del_cb->prev; - - if (pre_cb) - pre_cb->next = del_cb->next; - else - pdb->remote_cb = del_cb->next; - - free(del_cb); - pthread_mutex_unlock(&pdb->mutex); -} - -/* Remove all callbacks form OVS DB object */ -static void ovs_db_callback_remove_all(ovs_db_t *pdb) { - pthread_mutex_lock(&pdb->mutex); - while (pdb->remote_cb != NULL) { - ovs_callback_t *del_cb = pdb->remote_cb; - pdb->remote_cb = del_cb->next; - sfree(del_cb); - } - pthread_mutex_unlock(&pdb->mutex); -} - -/* Get/find callback in OVS DB object by UID. Returns pointer - * to requested callback otherwise NULL is returned. - * - * IMPORTANT NOTE: - * The OVS DB mutex MUST be locked by the caller - * to make sure that returned callback is still valid. - */ -static ovs_callback_t *ovs_db_callback_get(ovs_db_t *pdb, uint64_t uid) { - for (ovs_callback_t *cb = pdb->remote_cb; cb != NULL; cb = cb->next) - if (cb->uid == uid) - return cb; - return NULL; -} - -/* Send all requested data to the socket. Returns 0 if - * ALL request data has been sent otherwise negative value - * is returned */ -static int ovs_db_data_send(const ovs_db_t *pdb, const char *data, size_t len) { - ssize_t nbytes = 0; - size_t rem = len; - size_t off = 0; - - while (rem > 0) { - if ((nbytes = send(pdb->sock, data + off, rem, 0)) <= 0) - return -1; - rem -= (size_t)nbytes; - off += (size_t)nbytes; - } - return 0; -} - -/* - * YAJL (Yet Another JSON Library) helper functions - * Documentation (https://lloyd.github.io/yajl/) - */ - -/* Add null-terminated string into YAJL generator handle (JSON object). - * Similar function to yajl_gen_string() but takes null-terminated string - * instead of string and its length. - * - * jgen - YAJL generator handle allocated by yajl_gen_alloc() - * string - Null-terminated string - */ -static yajl_gen_status ovs_yajl_gen_tstring(yajl_gen hander, - const char *string) { - return yajl_gen_string(hander, (const unsigned char *)string, strlen(string)); -} - -/* Add YAJL value into YAJL generator handle (JSON object) - * - * jgen - YAJL generator handle allocated by yajl_gen_alloc() - * jval - YAJL value usually returned by yajl_tree_get() - */ -static yajl_gen_status ovs_yajl_gen_val(yajl_gen jgen, yajl_val jval) { - size_t array_len = 0; - yajl_val *jvalues = NULL; - yajl_val jobj_value = NULL; - const char *obj_key = NULL; - size_t obj_len = 0; - yajl_gen_status yajl_gen_ret = yajl_gen_status_ok; - - if (jval == NULL) - return yajl_gen_generation_complete; - - if (YAJL_IS_STRING(jval)) - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, YAJL_GET_STRING(jval)); - else if (YAJL_IS_DOUBLE(jval)) - OVS_YAJL_CALL(yajl_gen_double, jgen, YAJL_GET_DOUBLE(jval)); - else if (YAJL_IS_INTEGER(jval)) - OVS_YAJL_CALL(yajl_gen_double, jgen, YAJL_GET_INTEGER(jval)); - else if (YAJL_IS_TRUE(jval)) - OVS_YAJL_CALL(yajl_gen_bool, jgen, 1); - else if (YAJL_IS_FALSE(jval)) - OVS_YAJL_CALL(yajl_gen_bool, jgen, 0); - else if (YAJL_IS_NULL(jval)) - OVS_YAJL_CALL(yajl_gen_null, jgen); - else if (YAJL_IS_ARRAY(jval)) { - /* create new array and add all elements into the array */ - array_len = YAJL_GET_ARRAY(jval)->len; - jvalues = YAJL_GET_ARRAY(jval)->values; - OVS_YAJL_CALL(yajl_gen_array_open, jgen); - for (size_t i = 0; i < array_len; i++) - OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jvalues[i]); - OVS_YAJL_CALL(yajl_gen_array_close, jgen); - } else if (YAJL_IS_OBJECT(jval)) { - /* create new object and add all elements into the object */ - OVS_YAJL_CALL(yajl_gen_map_open, jgen); - obj_len = YAJL_GET_OBJECT(jval)->len; - for (size_t i = 0; i < obj_len; i++) { - obj_key = YAJL_GET_OBJECT(jval)->keys[i]; - jobj_value = YAJL_GET_OBJECT(jval)->values[i]; - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, obj_key); - OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jobj_value); - } - OVS_YAJL_CALL(yajl_gen_map_close, jgen); - } else { - OVS_ERROR("%s() unsupported value type %d (skip)", __FUNCTION__, - (int)(jval)->type); - goto yajl_gen_failure; - } - return yajl_gen_status_ok; - -yajl_gen_failure: - OVS_ERROR("%s() error to generate value", __FUNCTION__); - return yajl_gen_ret; -} - -/* OVS DB echo request handler. When OVS DB sends - * "echo" request to the client, client should generate - * "echo" replay with the same content received in the - * request */ -static int ovs_db_table_echo_cb(const ovs_db_t *pdb, yajl_val jnode) { - yajl_val jparams; - yajl_val jid; - yajl_gen jgen; - size_t resp_len = 0; - const char *resp = NULL; - const char *params_path[] = {"params", NULL}; - const char *id_path[] = {"id", NULL}; - yajl_gen_status yajl_gen_ret; - - if ((jgen = yajl_gen_alloc(NULL)) == NULL) - return -1; - - /* check & get request attributes */ - if ((jparams = yajl_tree_get(jnode, params_path, yajl_t_array)) == NULL || - ((jid = yajl_tree_get(jnode, id_path, yajl_t_any)) == NULL)) { - OVS_ERROR("parse echo request failed"); - goto yajl_gen_failure; - } - - /* generate JSON echo response */ - OVS_YAJL_CALL(yajl_gen_map_open, jgen); - - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "result"); - OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jparams); - - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "error"); - OVS_YAJL_CALL(yajl_gen_null, jgen); - - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "id"); - OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jid); - - OVS_YAJL_CALL(yajl_gen_map_close, jgen); - OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)&resp, - &resp_len); - - /* send the response */ - OVS_DEBUG("response: %s", resp); - if (ovs_db_data_send(pdb, resp, resp_len) < 0) { - OVS_ERROR("send echo reply failed"); - goto yajl_gen_failure; - } - /* clean up and return success */ - yajl_gen_clear(jgen); - return 0; - -yajl_gen_failure: - /* release memory */ - yajl_gen_clear(jgen); - return -1; -} - -/* Get OVS DB registered callback by YAJL val. The YAJL - * value should be YAJL string (UID). Returns NULL if - * callback hasn't been found. See also ovs_db_callback_get() - * description for addition info. - */ -static ovs_callback_t *ovs_db_table_callback_get(ovs_db_t *pdb, yajl_val jid) { - char *endptr = NULL; - const char *suid = NULL; - uint64_t uid; - - if (jid && YAJL_IS_STRING(jid)) { - suid = YAJL_GET_STRING(jid); - uid = (uint64_t)strtoul(suid, &endptr, 16); - if (*endptr == '\0' && uid) - return ovs_db_callback_get(pdb, uid); - } - - return NULL; -} - -/* OVS DB table update event handler. - * This callback is called by POLL thread if OVS DB - * table update callback is received from the DB - * server. Once registered callback found, it's called - * by this handler. */ -static int ovs_db_table_update_cb(ovs_db_t *pdb, yajl_val jnode) { - ovs_callback_t *cb = NULL; - yajl_val jvalue; - yajl_val jparams; - yajl_val jtable_updates; - const char *params_path[] = {"params", NULL}; - const char *id_path[] = {"id", NULL}; - - /* check & get request attributes */ - if ((jparams = yajl_tree_get(jnode, params_path, yajl_t_array)) == NULL || - (yajl_tree_get(jnode, id_path, yajl_t_null) == NULL)) { - OVS_ERROR("invalid OVS DB request received"); - return -1; - } - - /* check array length: [, ] */ - if ((YAJL_GET_ARRAY(jparams) == NULL) || - (YAJL_GET_ARRAY(jparams)->len != 2)) { - OVS_ERROR("invalid OVS DB request received"); - return -1; - } - - jvalue = YAJL_GET_ARRAY(jparams)->values[0]; - jtable_updates = YAJL_GET_ARRAY(jparams)->values[1]; - if ((!YAJL_IS_OBJECT(jtable_updates)) || (!YAJL_IS_STRING(jvalue))) { - OVS_ERROR("invalid OVS DB request id or table update received"); - return -1; - } - - /* find registered callback based on */ - pthread_mutex_lock(&pdb->mutex); - cb = ovs_db_table_callback_get(pdb, jvalue); - if (cb == NULL || cb->table.call == NULL) { - OVS_ERROR("No OVS DB table update callback found"); - pthread_mutex_unlock(&pdb->mutex); - return -1; - } - - /* call registered callback */ - cb->table.call(jtable_updates); - pthread_mutex_unlock(&pdb->mutex); - return 0; -} - -/* OVS DB result request handler. - * This callback is called by POLL thread if OVS DB - * result reply is received from the DB server. - * Once registered callback found, it's called - * by this handler. */ -static int ovs_db_result_cb(ovs_db_t *pdb, yajl_val jnode) { - ovs_callback_t *cb = NULL; - yajl_val jresult; - yajl_val jerror; - yajl_val jid; - const char *result_path[] = {"result", NULL}; - const char *error_path[] = {"error", NULL}; - const char *id_path[] = {"id", NULL}; - - jresult = yajl_tree_get(jnode, result_path, yajl_t_any); - jerror = yajl_tree_get(jnode, error_path, yajl_t_any); - jid = yajl_tree_get(jnode, id_path, yajl_t_string); - - /* check & get result attributes */ - if (!jresult || !jerror || !jid) - return -1; - - /* try to find registered callback */ - pthread_mutex_lock(&pdb->mutex); - cb = ovs_db_table_callback_get(pdb, jid); - if (cb != NULL && cb->result.call != NULL) { - /* call registered callback */ - cb->result.call(jresult, jerror); - /* unlock owner of the reply */ - sem_post(&cb->result.sync); - } - - pthread_mutex_unlock(&pdb->mutex); - return 0; -} - -/* Handle JSON data (one request) and call - * appropriate event OVS DB handler. Currently, - * update callback 'ovs_db_table_update_cb' and - * result callback 'ovs_db_result_cb' is supported. - */ -static int ovs_db_json_data_process(ovs_db_t *pdb, const char *data, - size_t len) { - const char *method = NULL; - char yajl_errbuf[OVS_YAJL_ERROR_BUFFER_SIZE]; - const char *method_path[] = {"method", NULL}; - const char *result_path[] = {"result", NULL}; - char *sjson = NULL; - yajl_val jnode, jval; - - /* duplicate the data to make null-terminated string - * required for yajl_tree_parse() */ - if ((sjson = calloc(1, len + 1)) == NULL) - return -1; - - sstrncpy(sjson, data, len + 1); - OVS_DEBUG("[len=%" PRIsz "] %s", len, sjson); - - /* parse json data */ - jnode = yajl_tree_parse(sjson, yajl_errbuf, sizeof(yajl_errbuf)); - if (jnode == NULL) { - OVS_ERROR("yajl_tree_parse() %s", yajl_errbuf); - sfree(sjson); - return -1; - } - - /* get method name */ - if ((jval = yajl_tree_get(jnode, method_path, yajl_t_string)) != NULL) { - if ((method = YAJL_GET_STRING(jval)) == NULL) { - yajl_tree_free(jnode); - sfree(sjson); - return -1; - } - if (strcmp("echo", method) == 0) { - /* echo request from the server */ - if (ovs_db_table_echo_cb(pdb, jnode) < 0) - OVS_ERROR("handle echo request failed"); - } else if (strcmp("update", method) == 0) { - /* update notification */ - if (ovs_db_table_update_cb(pdb, jnode) < 0) - OVS_ERROR("handle update notification failed"); - } - } else if ((jval = yajl_tree_get(jnode, result_path, yajl_t_any)) != NULL) { - /* result notification */ - if (ovs_db_result_cb(pdb, jnode) < 0) - OVS_ERROR("handle result reply failed"); - } else - OVS_ERROR("connot find method or result failed"); - - /* release memory */ - yajl_tree_free(jnode); - sfree(sjson); - return 0; -} - -/* - * JSON reader implementation. - * - * This module process raw JSON data (byte stream) and - * returns fully-fledged JSON data which can be processed - * (parsed) by YAJL later. - */ - -/* 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) - return NULL; - - return jreader; -} - -/* Push raw data into into the JSON reader for processing */ -static int ovs_json_reader_push_data(ovs_json_reader_t *jreader, - const char *data, size_t data_len) { - char *new_buff = NULL; - size_t available = jreader->buff_size - jreader->buff_offset; - - /* check/update required memory space */ - if (available < data_len) { - OVS_DEBUG("Reallocate buffer [size=%d, available=%d required=%d]", - (int)jreader->buff_size, (int)available, (int)data_len); - - /* allocate new chunk of memory */ - new_buff = realloc(jreader->buff_ptr, (jreader->buff_size + data_len)); - if (new_buff == NULL) - return -1; - - /* point to new allocated memory */ - jreader->buff_ptr = new_buff; - jreader->buff_size += data_len; - } - - /* store input data */ - memcpy(jreader->buff_ptr + jreader->buff_offset, data, data_len); - jreader->buff_offset += data_len; - return 0; -} - -/* Pop one fully-fledged JSON if already exists. Returns 0 if - * completed JSON already exists otherwise negative value is - * returned */ -static int ovs_json_reader_pop(ovs_json_reader_t *jreader, - const char **json_ptr, size_t *json_len_ptr) { - size_t nbraces = 0; - size_t json_len = 0; - char *json = NULL; - - /* search open/close brace */ - for (size_t i = jreader->json_offset; i < jreader->buff_offset; i++) { - if (jreader->buff_ptr[i] == '{') { - nbraces++; - } else if (jreader->buff_ptr[i] == '}') - if (nbraces) - if (!(--nbraces)) { - /* JSON data */ - *json_ptr = jreader->buff_ptr + jreader->json_offset; - *json_len_ptr = json_len + 1; - jreader->json_offset = i + 1; - return 0; - } - - /* increase JSON data length */ - if (nbraces) - json_len++; - } - - if (jreader->json_offset) { - if (jreader->json_offset < jreader->buff_offset) { - /* shift data to the beginning of the buffer - * and zero rest of the buffer data */ - json = &jreader->buff_ptr[jreader->json_offset]; - json_len = jreader->buff_offset - jreader->json_offset; - for (size_t i = 0; i < jreader->buff_size; i++) - jreader->buff_ptr[i] = ((i < json_len) ? (json[i]) : (0)); - jreader->buff_offset = json_len; - } else - /* reset the buffer */ - jreader->buff_offset = 0; - - /* data is at the beginning of the buffer */ - jreader->json_offset = 0; - } - - return -1; -} - -/* Reset JSON reader. It is useful when start processing - * new raw data. E.g.: in case of lost stream connection. - */ -static void ovs_json_reader_reset(ovs_json_reader_t *jreader) { - if (jreader) { - jreader->buff_offset = 0; - jreader->json_offset = 0; - } -} - -/* Release internal data allocated for JSON reader */ -static void ovs_json_reader_free(ovs_json_reader_t *jreader) { - if (jreader) { - free(jreader->buff_ptr); - free(jreader); - } -} - -/* Reconnect to OVS DB and call the OVS DB post connection init callback - * if connection has been established. - */ -static void ovs_db_reconnect(ovs_db_t *pdb) { - const char *node_info = pdb->node; - struct addrinfo *result; - - 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); - return; - } - result->ai_family = AF_UNIX; - result->ai_socktype = SOCK_STREAM; - result->ai_addrlen = sizeof(*sa_unix); - result->ai_addr = (struct sockaddr *)sa_unix; - sa_unix->sun_family = result->ai_family; - sstrncpy(sa_unix->sun_path, pdb->unix_path, sizeof(sa_unix->sun_path)); - } else { - /* inet socket address */ - struct addrinfo hints; - - /* setup criteria for selecting the socket address */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - /* get socket addresses */ - int ret = getaddrinfo(pdb->node, pdb->service, &hints, &result); - if (ret != 0) { - OVS_ERROR("getaddrinfo(): %s", gai_strerror(ret)); - return; - } - } - /* try to connect to the server */ - for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) { - int sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (sock < 0) { - OVS_DEBUG("socket(): %s", STRERRNO); - continue; - } - if (connect(sock, rp->ai_addr, rp->ai_addrlen) < 0) { - close(sock); - OVS_DEBUG("connect(): %s [family=%d]", STRERRNO, rp->ai_family); - } else { - /* send notification to event thread */ - pdb->sock = sock; - ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_ESTABLISHED); - break; - } - } - - if (pdb->sock < 0) - OVS_ERROR("connect to \"%s\" failed", node_info); - - freeaddrinfo(result); -} - -/* POLL worker thread. - * It listens on OVS DB connection for incoming - * requests/reply/events etc. Also, it reconnects to OVS DB - * if connection has been lost. - */ -static void *ovs_poll_worker(void *arg) { - ovs_db_t *pdb = (ovs_db_t *)arg; /* pointer to OVS DB */ - ovs_json_reader_t *jreader = NULL; - struct pollfd poll_fd = { - .fd = pdb->sock, .events = POLLIN | POLLPRI, .revents = 0, - }; - - /* create JSON reader instance */ - if ((jreader = ovs_json_reader_alloc()) == NULL) { - OVS_ERROR("initialize json reader failed"); - return NULL; - } - - /* poll data */ - while (ovs_db_poll_is_running(pdb)) { - poll_fd.fd = pdb->sock; - int poll_ret = poll(&poll_fd, 1, /* ms */ OVS_DB_POLL_TIMEOUT * 1000); - if (poll_ret < 0) { - OVS_ERROR("poll(): %s", STRERRNO); - break; - } else if (poll_ret == 0) { - OVS_DEBUG("poll(): timeout"); - if (pdb->sock < 0) - /* invalid fd, so try to reconnect */ - ovs_db_reconnect(pdb); - continue; - } - if (poll_fd.revents & POLLNVAL) { - /* invalid file descriptor, clean-up */ - ovs_db_callback_remove_all(pdb); - ovs_json_reader_reset(jreader); - /* setting poll FD to -1 tells poll() call to ignore this FD. - * In that case poll() call will return timeout all the time */ - pdb->sock = (-1); - } else if ((poll_fd.revents & POLLERR) || (poll_fd.revents & POLLHUP)) { - /* connection is broken */ - close(poll_fd.fd); - ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED); - OVS_ERROR("poll() peer closed its end of the channel"); - } else if ((poll_fd.revents & POLLIN) || (poll_fd.revents & POLLPRI)) { - /* read incoming data */ - char buff[OVS_DB_POLL_READ_BLOCK_SIZE]; - ssize_t nbytes = recv(poll_fd.fd, buff, sizeof(buff), 0); - if (nbytes < 0) { - OVS_ERROR("recv(): %s", STRERRNO); - /* read error? Try to reconnect */ - close(poll_fd.fd); - continue; - } else if (nbytes == 0) { - close(poll_fd.fd); - ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED); - OVS_ERROR("recv() peer has performed an orderly shutdown"); - continue; - } - /* read incoming data */ - size_t json_len = 0; - const char *json = NULL; - OVS_DEBUG("recv(): received %zd bytes of data", nbytes); - ovs_json_reader_push_data(jreader, buff, nbytes); - while (!ovs_json_reader_pop(jreader, &json, &json_len)) - /* process JSON data */ - ovs_db_json_data_process(pdb, json, json_len); - } - } - - OVS_DEBUG("poll thread has been completed"); - ovs_json_reader_free(jreader); - return NULL; -} - -/* EVENT worker thread. - * Perform task based on incoming events. This - * task can be done asynchronously which allows to - * handle OVS DB callback like 'init_cb'. - */ -static void *ovs_event_worker(void *arg) { - ovs_db_t *pdb = (ovs_db_t *)arg; - - while (pdb->event_thread.value != OVS_DB_EVENT_TERMINATE) { - /* wait for an event */ - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += (OVS_DB_EVENT_TIMEOUT); - int ret = pthread_cond_timedwait(&pdb->event_thread.cond, - &pdb->event_thread.mutex, &ts); - if (!ret || ret == ETIMEDOUT) { - /* handle the event */ - OVS_DEBUG("handle event %d", pdb->event_thread.value); - switch (pdb->event_thread.value) { - case OVS_DB_EVENT_CONN_ESTABLISHED: - if (pdb->cb.post_conn_init) - pdb->cb.post_conn_init(pdb); - /* reset event */ - pdb->event_thread.value = OVS_DB_EVENT_NONE; - break; - case OVS_DB_EVENT_CONN_TERMINATED: - if (pdb->cb.post_conn_terminate) - pdb->cb.post_conn_terminate(); - /* reset event */ - pdb->event_thread.value = OVS_DB_EVENT_NONE; - break; - case OVS_DB_EVENT_NONE: - /* wait timeout */ - OVS_DEBUG("no event received (timeout)"); - break; - default: - OVS_DEBUG("unknown event received"); - break; - } - } else { - /* unexpected error */ - OVS_ERROR("pthread_cond_timedwait() failed"); - break; - } - } - - OVS_DEBUG("event thread has been completed"); - return NULL; -} - -/* Initialize EVENT thread */ -static int ovs_db_event_thread_init(ovs_db_t *pdb) { - pdb->event_thread.tid = (pthread_t){0}; - /* init event thread condition variable */ - if (pthread_cond_init(&pdb->event_thread.cond, NULL)) { - return -1; - } - /* init event thread mutex */ - if (pthread_mutex_init(&pdb->event_thread.mutex, NULL)) { - pthread_cond_destroy(&pdb->event_thread.cond); - return -1; - } - /* Hold the event thread mutex. It ensures that no events - * will be lost while thread is still starting. Once event - * thread is started and ready to accept events, it will release - * the mutex */ - if (pthread_mutex_lock(&pdb->event_thread.mutex)) { - pthread_mutex_destroy(&pdb->event_thread.mutex); - pthread_cond_destroy(&pdb->event_thread.cond); - return -1; - } - /* start event thread */ - pthread_t tid; - if (plugin_thread_create(&tid, NULL, ovs_event_worker, pdb, - "utils_ovs:event") != 0) { - pthread_mutex_unlock(&pdb->event_thread.mutex); - pthread_mutex_destroy(&pdb->event_thread.mutex); - pthread_cond_destroy(&pdb->event_thread.cond); - return -1; - } - pdb->event_thread.tid = tid; - return 0; -} - -/* Terminate EVENT thread */ -static int ovs_db_event_thread_terminate(ovs_db_t *pdb) { - if (pthread_equal(pdb->event_thread.tid, (pthread_t){0})) { - /* already terminated */ - return 0; - } - ovs_db_event_post(pdb, OVS_DB_EVENT_TERMINATE); - if (pthread_join(pdb->event_thread.tid, NULL) != 0) - return -1; - /* Event thread always holds the thread mutex when - * performs some task (handles event) and releases it when - * while sleeping. Thus, if event thread exits, the mutex - * remains locked */ - pdb->event_thread.tid = (pthread_t){0}; - pthread_mutex_unlock(&pdb->event_thread.mutex); - return 0; -} - -/* Destroy EVENT thread private data */ -static void ovs_db_event_thread_data_destroy(ovs_db_t *pdb) { - /* destroy mutex */ - pthread_mutex_destroy(&pdb->event_thread.mutex); - pthread_cond_destroy(&pdb->event_thread.cond); -} - -/* Initialize POLL thread */ -static int ovs_db_poll_thread_init(ovs_db_t *pdb) { - pdb->poll_thread.tid = (pthread_t){0}; - /* init event thread mutex */ - if (pthread_mutex_init(&pdb->poll_thread.mutex, NULL)) { - return -1; - } - /* start poll thread */ - pthread_t tid; - pdb->poll_thread.state = OVS_DB_POLL_STATE_RUNNING; - if (plugin_thread_create(&tid, NULL, ovs_poll_worker, pdb, - "utils_ovs:poll") != 0) { - pthread_mutex_destroy(&pdb->poll_thread.mutex); - return -1; - } - pdb->poll_thread.tid = tid; - return 0; -} - -/* Destroy POLL thread */ -/* XXX: Must hold pdb->mutex when calling! */ -static int ovs_db_poll_thread_destroy(ovs_db_t *pdb) { - if (pthread_equal(pdb->poll_thread.tid, (pthread_t){0})) { - /* already destroyed */ - return 0; - } - /* change thread state */ - pthread_mutex_lock(&pdb->poll_thread.mutex); - pdb->poll_thread.state = OVS_DB_POLL_STATE_EXITING; - pthread_mutex_unlock(&pdb->poll_thread.mutex); - /* join the thread */ - if (pthread_join(pdb->poll_thread.tid, NULL) != 0) - return -1; - pthread_mutex_destroy(&pdb->poll_thread.mutex); - pdb->poll_thread.tid = (pthread_t){0}; - return 0; -} - -/* - * Public OVS DB API implementation - */ - -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; - - /* allocate db data & fill it */ - ovs_db_t *pdb = calloc(1, sizeof(*pdb)); - if (pdb == NULL) - return NULL; - pdb->sock = -1; - - /* store the OVS DB address */ - sstrncpy(pdb->node, node, sizeof(pdb->node)); - sstrncpy(pdb->service, service, sizeof(pdb->service)); - sstrncpy(pdb->unix_path, unix_path, sizeof(pdb->unix_path)); - - /* setup OVS DB callbacks */ - if (cb) - pdb->cb = *cb; - - /* init OVS DB mutex attributes */ - pthread_mutexattr_t mutex_attr; - if (pthread_mutexattr_init(&mutex_attr)) { - OVS_ERROR("OVS DB mutex attribute init failed"); - sfree(pdb); - return NULL; - } - /* set OVS DB mutex as recursive */ - if (pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE)) { - OVS_ERROR("Failed to set OVS DB mutex as recursive"); - pthread_mutexattr_destroy(&mutex_attr); - sfree(pdb); - return NULL; - } - /* init OVS DB mutex */ - if (pthread_mutex_init(&pdb->mutex, &mutex_attr)) { - OVS_ERROR("OVS DB mutex init failed"); - pthread_mutexattr_destroy(&mutex_attr); - sfree(pdb); - return NULL; - } - /* destroy mutex attributes */ - pthread_mutexattr_destroy(&mutex_attr); - - /* init event thread */ - if (ovs_db_event_thread_init(pdb) < 0) { - ret = ovs_db_destroy(pdb); - if (ret > 0) - goto failure; - else - return NULL; - } - - /* init polling thread */ - if (ovs_db_poll_thread_init(pdb) < 0) { - 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, - ovs_db_result_cb_t cb) { - int ret = 0; - yajl_gen_status yajl_gen_ret; - yajl_val jparams; - yajl_gen jgen; - ovs_callback_t *new_cb = NULL; - uint64_t uid; - char uid_buff[OVS_UID_STR_SIZE]; - const char *req = NULL; - size_t req_len = 0; - struct timespec ts; - - /* sanity check */ - if (!pdb || !method || !params) - return -1; - - if ((jgen = yajl_gen_alloc(NULL)) == NULL) - return -1; - - /* try to parse params */ - if ((jparams = yajl_tree_parse(params, NULL, 0)) == NULL) { - OVS_ERROR("params is not a JSON string"); - yajl_gen_clear(jgen); - return -1; - } - - /* generate method field */ - OVS_YAJL_CALL(yajl_gen_map_open, jgen); - - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "method"); - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, method); - - /* generate params field */ - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "params"); - OVS_YAJL_CALL(ovs_yajl_gen_val, jgen, jparams); - yajl_tree_free(jparams); - - /* generate id field */ - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "id"); - uid = ovs_uid_generate(); - snprintf(uid_buff, sizeof(uid_buff), "%" PRIX64, uid); - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_buff); - - OVS_YAJL_CALL(yajl_gen_map_close, jgen); - - if (cb) { - /* register result callback */ - if ((new_cb = calloc(1, sizeof(ovs_callback_t))) == NULL) - goto yajl_gen_failure; - - /* add new callback to front */ - sem_init(&new_cb->result.sync, 0, 0); - new_cb->result.call = cb; - new_cb->uid = uid; - ovs_db_callback_add(pdb, new_cb); - } - - /* send the request */ - OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)&req, &req_len); - OVS_DEBUG("%s", req); - if (!ovs_db_data_send(pdb, req, req_len)) { - if (cb) { - /* wait for result */ - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += OVS_DB_SEND_REQ_TIMEOUT; - if (sem_timedwait(&new_cb->result.sync, &ts) < 0) { - OVS_ERROR("%s() no replay received within %d sec", __FUNCTION__, - OVS_DB_SEND_REQ_TIMEOUT); - ret = (-1); - } - } - } else { - OVS_ERROR("ovs_db_data_send() failed"); - ret = (-1); - } - -yajl_gen_failure: - if (new_cb) { - /* destroy callback */ - sem_destroy(&new_cb->result.sync); - ovs_db_callback_remove(pdb, new_cb); - } - - /* release memory */ - yajl_gen_clear(jgen); - return (yajl_gen_ret != yajl_gen_status_ok) ? (-1) : ret; -} - -int ovs_db_table_cb_register(ovs_db_t *pdb, const char *tb_name, - const char **tb_column, - ovs_db_table_cb_t update_cb, - ovs_db_result_cb_t result_cb, unsigned int flags) { - yajl_gen jgen; - yajl_gen_status yajl_gen_ret; - ovs_callback_t *new_cb = NULL; - char uid_str[OVS_UID_STR_SIZE]; - char *params; - size_t params_len; - int ovs_db_ret = 0; - - /* sanity check */ - if (pdb == NULL || tb_name == NULL || update_cb == NULL) - return -1; - - /* allocate new update callback */ - if ((new_cb = calloc(1, sizeof(ovs_callback_t))) == NULL) - return -1; - - /* init YAJL generator */ - if ((jgen = yajl_gen_alloc(NULL)) == NULL) { - sfree(new_cb); - return -1; - } - - /* add new callback to front */ - new_cb->table.call = update_cb; - new_cb->uid = ovs_uid_generate(); - ovs_db_callback_add(pdb, new_cb); - - /* make update notification request - * [, , ] */ - OVS_YAJL_CALL(yajl_gen_array_open, jgen); - { - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, OVS_DB_DEFAULT_DB_NAME); - - /* uid string */ - snprintf(uid_str, sizeof(uid_str), "%" PRIX64, new_cb->uid); - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_str); - - /* */ - OVS_YAJL_CALL(yajl_gen_map_open, jgen); - { - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, tb_name); - OVS_YAJL_CALL(yajl_gen_array_open, jgen); - { - /* */ - OVS_YAJL_CALL(yajl_gen_map_open, jgen); - { - if (tb_column) { - /* columns within the table to be monitored */ - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "columns"); - OVS_YAJL_CALL(yajl_gen_array_open, jgen); - for (; *tb_column; tb_column++) - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, *tb_column); - OVS_YAJL_CALL(yajl_gen_array_close, jgen); - } - /* specify select option */ - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "select"); - { - OVS_YAJL_CALL(yajl_gen_map_open, jgen); - { - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "initial"); - OVS_YAJL_CALL(yajl_gen_bool, jgen, - flags & OVS_DB_TABLE_CB_FLAG_INITIAL); - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "insert"); - OVS_YAJL_CALL(yajl_gen_bool, jgen, - flags & OVS_DB_TABLE_CB_FLAG_INSERT); - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "delete"); - OVS_YAJL_CALL(yajl_gen_bool, jgen, - flags & OVS_DB_TABLE_CB_FLAG_DELETE); - OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, "modify"); - OVS_YAJL_CALL(yajl_gen_bool, jgen, - flags & OVS_DB_TABLE_CB_FLAG_MODIFY); - } - OVS_YAJL_CALL(yajl_gen_map_close, jgen); - } - } - OVS_YAJL_CALL(yajl_gen_map_close, jgen); - } - OVS_YAJL_CALL(yajl_gen_array_close, jgen); - } - OVS_YAJL_CALL(yajl_gen_map_close, jgen); - } - OVS_YAJL_CALL(yajl_gen_array_close, jgen); - - /* make a request to subscribe to given table */ - OVS_YAJL_CALL(yajl_gen_get_buf, jgen, (const unsigned char **)¶ms, - ¶ms_len); - if (ovs_db_send_request(pdb, "monitor", params, result_cb) < 0) { - OVS_ERROR("Failed to subscribe to \"%s\" table", tb_name); - ovs_db_ret = (-1); - } - -yajl_gen_failure: - /* release memory */ - yajl_gen_clear(jgen); - return ovs_db_ret; -} - -int ovs_db_destroy(ovs_db_t *pdb) { - int ovs_db_ret = 0; - int ret = 0; - - /* sanity check */ - if (pdb == NULL) - return -1; - - /* stop event thread */ - if (ovs_db_event_thread_terminate(pdb) < 0) { - OVS_ERROR("stop event thread failed"); - ovs_db_ret = -1; - } - - /* 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 ret; - } - - /* stop poll thread and destroy thread's private data */ - if (ovs_db_poll_thread_destroy(pdb) < 0) { - OVS_ERROR("destroy poll thread failed"); - ovs_db_ret = -1; - } - - /* destroy event thread private data */ - ovs_db_event_thread_data_destroy(pdb); - - pthread_mutex_unlock(&pdb->mutex); - - /* unsubscribe callbacks */ - ovs_db_callback_remove_all(pdb); - - /* close connection */ - if (pdb->sock >= 0) - close(pdb->sock); - - /* release DB handler */ - pthread_mutex_destroy(&pdb->mutex); - sfree(pdb); - return ovs_db_ret; -} - -/* - * Public OVS utils API implementation - */ - -/* Get YAJL value by key from YAJL dictionary - * - * EXAMPLE: - * { - * "key_a" : - * "key_b" : - * } - */ -yajl_val ovs_utils_get_value_by_key(yajl_val jval, const char *key) { - const char *obj_key = NULL; - - /* check params */ - if (!YAJL_IS_OBJECT(jval) || (key == NULL)) - return NULL; - - /* find a value by key */ - for (size_t i = 0; i < YAJL_GET_OBJECT(jval)->len; i++) { - obj_key = YAJL_GET_OBJECT(jval)->keys[i]; - if (strcmp(obj_key, key) == 0) - return YAJL_GET_OBJECT(jval)->values[i]; - } - - return NULL; -} - -/* Get OVS DB map value by given map key - * - * FROM RFC7047: - * - * - * A 2-element JSON array that represents a pair within a database - * map. The first element is an that represents the key, and - * the second element is an that represents the value. - * - * - * A 2-element JSON array that represents a database map value. The - * first element of the array must be the string "map", and the - * second element must be an array of zero or more s giving the - * values in the map. All of the s must have the same key and - * value types. - * - * EXAMPLE: - * [ - * "map", [ - * [ "key_a", ], [ "key_b", ], ... - * ] - * ] - */ -yajl_val ovs_utils_get_map_value(yajl_val jval, const char *key) { - size_t map_len = 0; - size_t array_len = 0; - yajl_val *map_values = NULL; - yajl_val *array_values = NULL; - const char *str_val = NULL; - - /* check YAJL array */ - if (!YAJL_IS_ARRAY(jval) || (key == NULL)) - return NULL; - - /* check a database map value (2-element, first one should be a string */ - array_len = YAJL_GET_ARRAY(jval)->len; - array_values = YAJL_GET_ARRAY(jval)->values; - if ((array_len != 2) || (!YAJL_IS_STRING(array_values[0])) || - (!YAJL_IS_ARRAY(array_values[1]))) - return NULL; - - /* check first element of the array */ - str_val = YAJL_GET_STRING(array_values[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]) || YAJL_GET_ARRAY(map_values[i]) == NULL) - break; - - /* check a database pair value (2-element, first one represents a key - * and it should be a string in our case */ - array_len = YAJL_GET_ARRAY(map_values[i])->len; - array_values = YAJL_GET_ARRAY(map_values[i])->values; - if ((array_len != 2) || (!YAJL_IS_STRING(array_values[0]))) - break; - - /* return map value if given key equals map key */ - str_val = YAJL_GET_STRING(array_values[0]); - if (str_val != NULL && strcmp(key, str_val) == 0) - return array_values[1]; - } - return NULL; -} diff --git a/src/utils_ovs.h b/src/utils_ovs.h deleted file mode 100644 index 52c2f915..00000000 --- a/src/utils_ovs.h +++ /dev/null @@ -1,236 +0,0 @@ -/** - * collectd - src/utils_ovs.h - * - * Copyright(c) 2016 Intel Corporation. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Authors: - * Volodymyr Mytnyk - * - * Description: - * The OVS util module provides the following features: - * - Implements the OVS DB communication transport specified - * by RFC7047: - * * Connect/disconnect to OVS DB; - * * Recovery mechanism in case of OVS DB connection lost; - * * Subscription mechanism to OVS DB table update events - * (insert/modify/delete); - * * Send custom JSON request to OVS DB (poll table data, etc.) - * * Handling of echo request from OVS DB server to verify the - * liveness of the connection. - * - Provides YAJL helpers functions. - * - * OVS DB API User Guide: - * All OVS DB function/structure names begins from 'ovs_db_*' prefix. To - * start using OVS DB API, client (plugin) should initialize the OVS DB - * object (`ovs_db_t') by calling `ovs_db_init' function. It initializes - * internal data and creates two main workers (threads). The result of the - * function is a pointer to new OVS DB object which can be used by other - * OVS DB API later and must be released by `ovs_db_destroy' function if - * the object isn't needed anymore. - * Once OVS DB API is initialized, the `init_cb' callback is called if - * the connection to OVS DB has been established. This callback is called - * every time the OVS DB is reconnected. So, if the client registers table - * update event callbacks or does any other OVS DB setup that can be lost - * after OVS DB reconnecting, it should be done in `init_cb' callback. - * The `ovs_db_table_cb_register` function is used to register OVS DB - * table update event callback and receive the table update notification - * when requested event occurs (registered callback is called). See - * function API for more info. - * To send custom JSON-RPC request to OVS DB, the `ovs_db_send_request' - * function is used. Please note, that connection to OVS DB should be - * established otherwise the function will return error. - * To verify the liveness of established connection, the OVS DB server - * sends echo request to the client with a given interval. The OVS utils - * takes care about this request and handles it properly. - **/ - -#ifndef UTILS_OVS_H -#define UTILS_OVS_H - -#include -#include - -/* Forward declaration */ -typedef struct ovs_db_s ovs_db_t; - -/* OVS DB callback type declaration */ -typedef void (*ovs_db_table_cb_t)(yajl_val jupdates); -typedef void (*ovs_db_result_cb_t)(yajl_val jresult, yajl_val jerror); - -/* OVS DB structures */ -struct ovs_db_callback_s { - /* - * This callback is called when OVS DB connection - * has been established and ready to use. Client - * can use this callback to configure OVS DB, e.g. - * to subscribe to table update notification or poll - * some OVS DB data. This field can be NULL. - */ - void (*post_conn_init)(ovs_db_t *pdb); - /* - * This callback is called when OVS DB connection - * has been lost. This field can be NULL. - */ - void (*post_conn_terminate)(void); -}; -typedef struct ovs_db_callback_s ovs_db_callback_t; - -/* OVS DB defines */ -#define OVS_DB_ADDR_NODE_SIZE 256 -#define OVS_DB_ADDR_SERVICE_SIZE 128 -#define OVS_DB_ADDR_UNIX_SIZE 108 - -/* OVS DB prototypes */ - -/* - * NAME - * ovs_db_init - * - * DESCRIPTION - * Initialize OVS DB internal data. The `ovs_db_destroy' function - * shall destroy the returned object. - * - * PARAMETERS - * `node' OVS DB Address. - * `service' OVS DB service name. - * `unix' OVS DB unix socket path. - * `cb' OVS DB callbacks. - * - * RETURN VALUE - * New ovs_db_t object upon success or NULL if an error occurred. - */ -ovs_db_t *ovs_db_init(const char *node, const char *service, - const char *unix_path, ovs_db_callback_t *cb); - -/* - * NAME - * ovs_db_destroy - * - * DESCRIPTION - * Destroy OVS DB object referenced by `pdb'. - * - * PARAMETERS - * `pdb' Pointer to OVS DB object. - * - * RETURN VALUE - * Zero upon success or non-zero if an error occurred. - */ -int ovs_db_destroy(ovs_db_t *pdb); - -/* - * NAME - * ovs_db_send_request - * - * DESCRIPTION - * Send JSON request to OVS DB server. - * - * PARAMETERS - * `pdb' Pointer to OVS DB object. - * `method' Request method name. - * `params' Method params to be sent (JSON value as a string). - * `cb' Result callback of the request. If NULL, the request - * is sent asynchronously. - * - * RETURN VALUE - * Zero upon success or non-zero if an error occurred. - */ -int ovs_db_send_request(ovs_db_t *pdb, const char *method, const char *params, - ovs_db_result_cb_t cb); - -/* callback types */ -#define OVS_DB_TABLE_CB_FLAG_INITIAL 0x01U -#define OVS_DB_TABLE_CB_FLAG_INSERT 0x02U -#define OVS_DB_TABLE_CB_FLAG_DELETE 0x04U -#define OVS_DB_TABLE_CB_FLAG_MODIFY 0x08U -#define OVS_DB_TABLE_CB_FLAG_ALL 0x0FU - -/* - * NAME - * ovs_db_table_cb_register - * - * DESCRIPTION - * Subscribe a callback on OVS DB table event. It allows to - * receive notifications (`update_cb' callback is called) of - * changes to requested table. - * - * PARAMETERS - * `pdb' Pointer to OVS DB object. - * `tb_name' OVS DB Table name to be monitored. - * `tb_column' OVS DB Table columns to be monitored. Last - * element in the array should be NULL. - * `update_cb' Callback function that is called when - * requested table columns are changed. - * `cb' Result callback of the request. If NULL, the call - * becomes asynchronous. - * Useful, if OVS_DB_TABLE_CB_FLAG_INITIAL is set. - * `flags' Bit mask of: - * OVS_DB_TABLE_CB_FLAG_INITIAL Receive initial values in - * result callback. - * OVS_DB_TABLE_CB_FLAG_INSERT Receive table insert events. - * OVS_DB_TABLE_CB_FLAG_DELETE Receive table remove events. - * OVS_DB_TABLE_CB_FLAG_MODIFY Receive table update events. - * OVS_DB_TABLE_CB_FLAG_ALL Receive all events. - * - * RETURN VALUE - * Zero upon success or non-zero if an error occurred. - */ -int ovs_db_table_cb_register(ovs_db_t *pdb, const char *tb_name, - const char **tb_column, - ovs_db_table_cb_t update_cb, - ovs_db_result_cb_t result_cb, unsigned int flags); - -/* - * OVS utils API - */ - -/* - * NAME - * ovs_utils_get_value_by_key - * - * DESCRIPTION - * Get YAJL value by object name. - * - * PARAMETERS - * `jval' YAJL object value. - * `key' Object key name. - * - * RETURN VALUE - * YAJL value upon success or NULL if key not found. - */ -yajl_val ovs_utils_get_value_by_key(yajl_val jval, const char *key); - -/* - * NAME - * ovs_utils_get_map_value - * - * DESCRIPTION - * Get OVS DB map value by given map key (rfc7047, "Notation" section). - * - * PARAMETERS - * `jval' A 2-element YAJL array that represents a OVS DB map value. - * `key' OVS DB map key name. - * - * RETURN VALUE - * YAJL value upon success or NULL if key not found. - */ -yajl_val ovs_utils_get_map_value(yajl_val jval, const char *key); - -#endif diff --git a/src/utils_parse_option.c b/src/utils_parse_option.c deleted file mode 100644 index 005715c9..00000000 --- a/src/utils_parse_option.c +++ /dev/null @@ -1,148 +0,0 @@ -/** - * collectd - src/utils_parse_option.c - * Copyright (C) 2008 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "utils_parse_option.h" - -int parse_string(char **ret_buffer, char **ret_string) { - char *buffer; - char *string; - - buffer = *ret_buffer; - - /* Eat up leading spaces. */ - string = buffer; - while (isspace((int)*string)) - string++; - if (*string == 0) - return 1; - - /* A quoted string */ - if (*string == '"') { - char *dst; - - string++; - if (*string == 0) - return 1; - - dst = string; - buffer = string; - while ((*buffer != '"') && (*buffer != 0)) { - /* Un-escape backslashes */ - if (*buffer == '\\') { - buffer++; - /* Catch a backslash at the end of buffer */ - if (*buffer == 0) - return -1; - } - *dst = *buffer; - buffer++; - dst++; - } - /* No quote sign has been found */ - if (*buffer == 0) - return -1; - - *dst = 0; - dst++; - *buffer = 0; - buffer++; - - /* Check for trailing spaces. */ - if ((*buffer != 0) && !isspace((int)*buffer)) - return -1; - } else /* an unquoted string */ - { - buffer = string; - while ((*buffer != 0) && !isspace((int)*buffer)) - buffer++; - if (*buffer != 0) { - *buffer = 0; - buffer++; - } - } - - /* Eat up trailing spaces */ - while (isspace((int)*buffer)) - buffer++; - - *ret_buffer = buffer; - *ret_string = string; - - return 0; -} /* int parse_string */ - -/* - * parse_option - * ------------ - * Parses an ``option'' as used with the unixsock and exec commands. An - * option is of the form: - * name0="value" - * name1="value with \"quotes\"" - * name2="value \\ backslash" - * However, if the value does *not* contain a space character, you can skip - * the quotes. - */ -int parse_option(char **ret_buffer, char **ret_key, char **ret_value) { - char *buffer; - char *key; - char *value; - int status; - - buffer = *ret_buffer; - - /* Eat up leading spaces */ - key = buffer; - while (isspace((int)*key)) - key++; - if (*key == 0) - return 1; - - /* Look for the equal sign */ - buffer = key; - while (isalnum((int)*buffer) || *buffer == '_' || *buffer == ':') - buffer++; - if ((*buffer != '=') || (buffer == key)) - return 1; - *buffer = 0; - buffer++; - /* Empty values must be written as "" */ - if (isspace((int)*buffer) || (*buffer == 0)) - return -1; - - status = parse_string(&buffer, &value); - if (status != 0) - return -1; - - /* NB: parse_string will have eaten up all trailing spaces. */ - - *ret_buffer = buffer; - *ret_key = key; - *ret_value = value; - - return 0; -} /* int parse_option */ diff --git a/src/utils_parse_option.h b/src/utils_parse_option.h deleted file mode 100644 index 3dd0a792..00000000 --- a/src/utils_parse_option.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * collectd - src/utils_parse_option.h - * Copyright (C) 2008 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_PARSE_OPTION -#define UTILS_PARSE_OPTION 1 - -int parse_string(char **ret_buffer, char **ret_string); -int parse_option(char **ret_buffer, char **ret_key, char **ret_value); - -#endif /* UTILS_PARSE_OPTION */ diff --git a/src/utils_rrdcreate.c b/src/utils_rrdcreate.c deleted file mode 100644 index 8f92cfd3..00000000 --- a/src/utils_rrdcreate.c +++ /dev/null @@ -1,660 +0,0 @@ -/** - * collectd - src/utils_rrdcreate.c - * Copyright (C) 2006-2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" - -#include "common.h" -#include "utils_rrdcreate.h" - -#include -#include - -struct srrd_create_args_s { - char *filename; - unsigned long pdp_step; - time_t last_up; - int argc; - char **argv; -}; -typedef struct srrd_create_args_s srrd_create_args_t; - -struct async_create_file_s; -typedef struct async_create_file_s async_create_file_t; -struct async_create_file_s { - char *filename; - async_create_file_t *next; -}; - -/* - * Private variables - */ -static int rra_timespans[] = {3600, 86400, 604800, 2678400, 31622400}; -static int rra_timespans_num = STATIC_ARRAY_SIZE(rra_timespans); - -static const char *const rra_types[] = {"AVERAGE", "MIN", "MAX"}; -static int rra_types_num = STATIC_ARRAY_SIZE(rra_types); - -#if !defined(HAVE_THREADSAFE_LIBRRD) -static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER; -#endif - -static async_create_file_t *async_creation_list; -static pthread_mutex_t async_creation_lock = PTHREAD_MUTEX_INITIALIZER; - -/* - * Private functions - */ -static void rra_free(int rra_num, char **rra_def) /* {{{ */ -{ - for (int i = 0; i < rra_num; i++) { - sfree(rra_def[i]); - } - sfree(rra_def); -} /* }}} void rra_free */ - -static void srrd_create_args_destroy(srrd_create_args_t *args) { - if (args == NULL) - return; - - sfree(args->filename); - if (args->argv != NULL) { - for (int i = 0; i < args->argc; i++) - sfree(args->argv[i]); - sfree(args->argv); - } - sfree(args); -} /* void srrd_create_args_destroy */ - -static srrd_create_args_t *srrd_create_args_create(const char *filename, - unsigned long pdp_step, - time_t last_up, int argc, - const char **argv) { - srrd_create_args_t *args; - - args = calloc(1, sizeof(*args)); - if (args == NULL) { - P_ERROR("srrd_create_args_create: calloc failed."); - return NULL; - } - args->filename = NULL; - args->pdp_step = pdp_step; - args->last_up = last_up; - args->argv = NULL; - - args->filename = strdup(filename); - if (args->filename == NULL) { - 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)); - if (args->argv == NULL) { - P_ERROR("srrd_create_args_create: calloc failed."); - srrd_create_args_destroy(args); - return NULL; - } - - for (args->argc = 0; args->argc < argc; args->argc++) { - args->argv[args->argc] = strdup(argv[args->argc]); - if (args->argv[args->argc] == NULL) { - P_ERROR("srrd_create_args_create: strdup failed."); - srrd_create_args_destroy(args); - return NULL; - } - } - assert(args->argc == argc); - args->argv[args->argc] = NULL; - - return args; -} /* srrd_create_args_t *srrd_create_args_create */ - -/* * * * * * * * * * - * WARNING: Magic * - * * * * * * * * * */ -static int rra_get(char ***ret, const value_list_t *vl, /* {{{ */ - const rrdcreate_config_t *cfg) { - char **rra_def; - int rra_num; - - int *rts; - int rts_num; - - int rra_max; - - int cdp_num; - int cdp_len; - - /* The stepsize we use here: If it is user-set, use it. If not, use the - * interval of the value-list. */ - int ss; - - if (cfg->rrarows <= 0) { - *ret = NULL; - return -1; - } - - if ((cfg->xff < 0) || (cfg->xff >= 1.0)) { - *ret = NULL; - return -1; - } - - if (cfg->stepsize > 0) - ss = cfg->stepsize; - else - ss = (int)CDTIME_T_TO_TIME_T(vl->interval); - if (ss <= 0) { - *ret = NULL; - return -1; - } - - /* Use the configured timespans or fall back to the built-in defaults */ - if (cfg->timespans_num != 0) { - rts = cfg->timespans; - rts_num = cfg->timespans_num; - } else { - rts = rra_timespans; - rts_num = rra_timespans_num; - } - - rra_max = rts_num * rra_types_num; - assert(rra_max > 0); - - if ((rra_def = calloc(rra_max + 1, sizeof(*rra_def))) == NULL) - return -1; - rra_num = 0; - - cdp_len = 0; - for (int i = 0; i < rts_num; i++) { - int span = rts[i]; - - if ((span / ss) < cfg->rrarows) - span = ss * cfg->rrarows; - - if (cdp_len == 0) - cdp_len = 1; - else - cdp_len = (int)floor(((double)span) / ((double)(cfg->rrarows * ss))); - - cdp_num = (int)ceil(((double)span) / ((double)(cdp_len * ss))); - - for (int j = 0; j < rra_types_num; j++) { - char buffer[128]; - int status; - - if (rra_num >= rra_max) - break; - - status = snprintf(buffer, sizeof(buffer), "RRA:%s:%.10f:%u:%u", - rra_types[j], cfg->xff, cdp_len, cdp_num); - - if ((status < 0) || ((size_t)status >= sizeof(buffer))) { - P_ERROR("rra_get: Buffer would have been truncated."); - continue; - } - - rra_def[rra_num++] = sstrdup(buffer); - } - } - - if (rra_num <= 0) { - sfree(rra_def); - return 0; - } - - *ret = rra_def; - return rra_num; -} /* }}} int rra_get */ - -static void ds_free(int ds_num, char **ds_def) /* {{{ */ -{ - for (int i = 0; i < ds_num; i++) - if (ds_def[i] != NULL) - free(ds_def[i]); - free(ds_def); -} /* }}} void ds_free */ - -static int ds_get(char ***ret, /* {{{ */ - const data_set_t *ds, const value_list_t *vl, - const rrdcreate_config_t *cfg) { - char **ds_def; - size_t ds_num; - - char min[32]; - char max[32]; - char buffer[128]; - - assert(ds->ds_num > 0); - - ds_def = calloc(ds->ds_num, sizeof(*ds_def)); - if (ds_def == NULL) { - P_ERROR("ds_get: calloc failed: %s", STRERRNO); - return -1; - } - - for (ds_num = 0; ds_num < ds->ds_num; ds_num++) { - data_source_t *d = ds->ds + ds_num; - const char *type; - int status; - - ds_def[ds_num] = NULL; - - if (d->type == DS_TYPE_COUNTER) - type = "COUNTER"; - else if (d->type == DS_TYPE_GAUGE) - type = "GAUGE"; - else if (d->type == DS_TYPE_DERIVE) - type = "DERIVE"; - else if (d->type == DS_TYPE_ABSOLUTE) - type = "ABSOLUTE"; - else { - P_ERROR("ds_get: Unknown DS type: %i", d->type); - break; - } - - if (isnan(d->min)) { - sstrncpy(min, "U", sizeof(min)); - } else - snprintf(min, sizeof(min), "%f", d->min); - - if (isnan(d->max)) { - sstrncpy(max, "U", sizeof(max)); - } else - snprintf(max, sizeof(max), "%f", d->max); - - status = snprintf( - buffer, sizeof(buffer), "DS:%s:%s:%i:%s:%s", d->name, type, - (cfg->heartbeat > 0) ? cfg->heartbeat - : (int)CDTIME_T_TO_TIME_T(2 * vl->interval), - min, max); - if ((status < 1) || ((size_t)status >= sizeof(buffer))) - break; - - ds_def[ds_num] = sstrdup(buffer); - } /* for ds_num = 0 .. ds->ds_num */ - - if (ds_num != ds->ds_num) { - ds_free(ds_num, ds_def); - return -1; - } - - if (ds_num == 0) { - sfree(ds_def); - return 0; - } - - *ret = ds_def; - return ds_num; -} /* }}} int ds_get */ - -#if HAVE_THREADSAFE_LIBRRD -static int srrd_create(const char *filename, /* {{{ */ - unsigned long pdp_step, time_t last_up, int argc, - const char **argv) { - int status; - char *filename_copy; - - if ((filename == NULL) || (argv == NULL)) - return -EINVAL; - - /* Some versions of librrd don't have the `const' qualifier for the first - * argument, so we have to copy the pointer here to avoid warnings. It sucks, - * but what else can we do? :( -octo */ - filename_copy = strdup(filename); - if (filename_copy == NULL) { - ERROR("srrd_create: strdup failed."); - return -ENOMEM; - } - - optind = 0; /* bug in librrd? */ - rrd_clear_error(); - - status = rrd_create_r(filename_copy, pdp_step, last_up, argc, (void *)argv); - - if (status != 0) { - P_WARNING("srrd_create: rrd_create_r (%s) failed: %s", filename, - rrd_get_error()); - } - - sfree(filename_copy); - - return status; -} /* }}} int srrd_create */ -/* #endif HAVE_THREADSAFE_LIBRRD */ - -#else /* !HAVE_THREADSAFE_LIBRRD */ -static int srrd_create(const char *filename, /* {{{ */ - unsigned long pdp_step, time_t last_up, int argc, - const char **argv) { - int status; - - int new_argc; - char **new_argv; - - char pdp_step_str[16]; - char last_up_str[16]; - - new_argc = 6 + argc; - new_argv = malloc((new_argc + 1) * sizeof(*new_argv)); - if (new_argv == NULL) { - P_ERROR("srrd_create: malloc failed."); - return -1; - } - - if (last_up == 0) - last_up = time(NULL) - 10; - - snprintf(pdp_step_str, sizeof(pdp_step_str), "%lu", pdp_step); - snprintf(last_up_str, sizeof(last_up_str), "%lu", (unsigned long)last_up); - - new_argv[0] = "create"; - new_argv[1] = (void *)filename; - new_argv[2] = "-s"; - new_argv[3] = pdp_step_str; - new_argv[4] = "-b"; - new_argv[5] = last_up_str; - - memcpy(new_argv + 6, argv, argc * sizeof(char *)); - new_argv[new_argc] = NULL; - - pthread_mutex_lock(&librrd_lock); - optind = 0; /* bug in librrd? */ - rrd_clear_error(); - - status = rrd_create(new_argc, new_argv); - pthread_mutex_unlock(&librrd_lock); - - if (status != 0) { - P_WARNING("srrd_create: rrd_create (%s) failed: %s", filename, - rrd_get_error()); - } - - sfree(new_argv); - - return status; -} /* }}} int srrd_create */ -#endif /* !HAVE_THREADSAFE_LIBRRD */ - -static int lock_file(char const *filename) /* {{{ */ -{ - async_create_file_t *ptr; - struct stat sb; - int status; - - pthread_mutex_lock(&async_creation_lock); - - for (ptr = async_creation_list; ptr != NULL; ptr = ptr->next) - if (strcmp(filename, ptr->filename) == 0) - break; - - if (ptr != NULL) { - pthread_mutex_unlock(&async_creation_lock); - return EEXIST; - } - - status = stat(filename, &sb); - if ((status == 0) || (errno != ENOENT)) { - pthread_mutex_unlock(&async_creation_lock); - return EEXIST; - } - - ptr = malloc(sizeof(*ptr)); - if (ptr == NULL) { - pthread_mutex_unlock(&async_creation_lock); - return ENOMEM; - } - - ptr->filename = strdup(filename); - if (ptr->filename == NULL) { - pthread_mutex_unlock(&async_creation_lock); - sfree(ptr); - return ENOMEM; - } - - ptr->next = async_creation_list; - async_creation_list = ptr; - - pthread_mutex_unlock(&async_creation_lock); - - return 0; -} /* }}} int lock_file */ - -static int unlock_file(char const *filename) /* {{{ */ -{ - async_create_file_t *this; - async_create_file_t *prev; - - pthread_mutex_lock(&async_creation_lock); - - prev = NULL; - for (this = async_creation_list; this != NULL; this = this->next) { - if (strcmp(filename, this->filename) == 0) - break; - prev = this; - } - - if (this == NULL) { - pthread_mutex_unlock(&async_creation_lock); - return ENOENT; - } - - if (prev == NULL) { - assert(this == async_creation_list); - async_creation_list = this->next; - } else { - assert(this == prev->next); - prev->next = this->next; - } - this->next = NULL; - - pthread_mutex_unlock(&async_creation_lock); - - sfree(this->filename); - sfree(this); - - return 0; -} /* }}} int unlock_file */ - -static void *srrd_create_thread(void *targs) /* {{{ */ -{ - srrd_create_args_t *args = targs; - char tmpfile[PATH_MAX]; - int status; - - status = lock_file(args->filename); - if (status != 0) { - if (status == EEXIST) - P_NOTICE("srrd_create_thread: File \"%s\" is already being created.", - args->filename); - else - P_ERROR("srrd_create_thread: Unable to lock file \"%s\".", - args->filename); - srrd_create_args_destroy(args); - return 0; - } - - snprintf(tmpfile, sizeof(tmpfile), "%s.async", args->filename); - - status = srrd_create(tmpfile, args->pdp_step, args->last_up, args->argc, - (void *)args->argv); - if (status != 0) { - 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); - return 0; - } - - status = rename(tmpfile, args->filename); - if (status != 0) { - 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); - return 0; - } - - DEBUG("srrd_create_thread: Successfully created RRD file \"%s\".", - args->filename); - - unlock_file(args->filename); - srrd_create_args_destroy(args); - - return 0; -} /* }}} void *srrd_create_thread */ - -static int srrd_create_async(const char *filename, /* {{{ */ - unsigned long pdp_step, time_t last_up, int argc, - const char **argv) { - srrd_create_args_t *args; - pthread_t thread; - pthread_attr_t attr; - int status; - - DEBUG("srrd_create_async: Creating \"%s\" in the background.", filename); - - args = srrd_create_args_create(filename, pdp_step, last_up, argc, argv); - if (args == NULL) - return -1; - - status = pthread_attr_init(&attr); - if (status != 0) { - srrd_create_args_destroy(args); - return -1; - } - - status = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (status != 0) { - pthread_attr_destroy(&attr); - srrd_create_args_destroy(args); - return -1; - } - - status = pthread_create(&thread, &attr, srrd_create_thread, args); - if (status != 0) { - P_ERROR("srrd_create_async: pthread_create failed: %s", STRERROR(status)); - pthread_attr_destroy(&attr); - srrd_create_args_destroy(args); - return status; - } - - pthread_attr_destroy(&attr); - /* args is freed in srrd_create_thread(). */ - return 0; -} /* }}} int srrd_create_async */ - -/* - * Public functions - */ -int cu_rrd_create_file(const char *filename, /* {{{ */ - const data_set_t *ds, const value_list_t *vl, - const rrdcreate_config_t *cfg) { - char **argv; - int argc; - char **rra_def = NULL; - int rra_num; - char **ds_def = NULL; - int ds_num; - int status = 0; - time_t last_up; - unsigned long stepsize; - - if (check_create_dir(filename)) - return -1; - - if ((rra_num = rra_get(&rra_def, vl, cfg)) < 1) { - P_ERROR("cu_rrd_create_file failed: Could not calculate RRAs"); - return -1; - } - - if ((ds_num = ds_get(&ds_def, ds, vl, cfg)) < 1) { - P_ERROR("cu_rrd_create_file failed: Could not calculate DSes"); - rra_free(rra_num, rra_def); - return -1; - } - - argc = ds_num + rra_num; - - if ((argv = malloc(sizeof(*argv) * (argc + 1))) == NULL) { - P_ERROR("cu_rrd_create_file failed: %s", STRERRNO); - rra_free(rra_num, rra_def); - ds_free(ds_num, ds_def); - return -1; - } - - memcpy(argv, ds_def, ds_num * sizeof(char *)); - memcpy(argv + ds_num, rra_def, rra_num * sizeof(char *)); - argv[ds_num + rra_num] = NULL; - - last_up = CDTIME_T_TO_TIME_T(vl->time); - if (last_up <= 0) - last_up = time(NULL); - last_up -= 1; - - if (cfg->stepsize > 0) - stepsize = cfg->stepsize; - else - stepsize = (unsigned long)CDTIME_T_TO_TIME_T(vl->interval); - - if (cfg->async) { - status = srrd_create_async(filename, stepsize, last_up, argc, - (const char **)argv); - if (status != 0) - 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) - P_NOTICE("cu_rrd_create_file: File \"%s\" is already being created.", - filename); - else - 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) { - 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); - } - unlock_file(filename); - } - } - - free(argv); - ds_free(ds_num, ds_def); - rra_free(rra_num, rra_def); - - return status; -} /* }}} int cu_rrd_create_file */ diff --git a/src/utils_rrdcreate.h b/src/utils_rrdcreate.h deleted file mode 100644 index b2277e75..00000000 --- a/src/utils_rrdcreate.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * collectd - src/utils_rrdcreate.h - * Copyright (C) 2008-2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - **/ - -#ifndef UTILS_RRDCREATE_H -#define UTILS_RRDCREATE_H 1 - -#include "plugin.h" - -#include - -struct rrdcreate_config_s { - unsigned long stepsize; - int heartbeat; - int rrarows; - double xff; - - int *timespans; - size_t timespans_num; - - char **consolidation_functions; - size_t consolidation_functions_num; - - bool async; -}; -typedef struct rrdcreate_config_s rrdcreate_config_t; - -int cu_rrd_create_file(const char *filename, const data_set_t *ds, - const value_list_t *vl, const rrdcreate_config_t *cfg); - -#endif /* UTILS_RRDCREATE_H */ diff --git a/src/utils_tail.c b/src/utils_tail.c deleted file mode 100644 index 55a32879..00000000 --- a/src/utils_tail.c +++ /dev/null @@ -1,224 +0,0 @@ -/** - * collectd - src/utils_tail.c - * Copyright (C) 2007-2008 C-Ware, Inc. - * Copyright (C) 2008 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: - * Luke Heberling - * Florian Forster - * - * Description: - * Encapsulates useful code for plugins which must watch for appends to - * the end of a file. - **/ - -#include "collectd.h" - -#include "common.h" -#include "utils_tail.h" - -struct cu_tail_s { - char *file; - FILE *fh; - struct stat stat; -}; - -static int cu_tail_reopen(cu_tail_t *obj) { - int seek_end = 0; - struct stat stat_buf = {0}; - - int status = stat(obj->file, &stat_buf); - if (status != 0) { - P_ERROR("utils_tail: stat (%s) failed: %s", obj->file, STRERRNO); - return -1; - } - - /* The file is already open.. */ - 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) { - P_INFO("utils_tail: File `%s' was truncated.", obj->file); - status = fseek(obj->fh, 0, SEEK_SET); - if (status != 0) { - P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO); - fclose(obj->fh); - obj->fh = NULL; - return -1; - } - } - memcpy(&obj->stat, &stat_buf, sizeof(struct stat)); - return 1; - } - - /* Seek to the end if we re-open the same file again or the file opened - * is the first at all or the first after an error */ - if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino)) - seek_end = 1; - - FILE *fh = fopen(obj->file, "r"); - if (fh == NULL) { - 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) { - P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO); - fclose(fh); - return -1; - } - } - - if (obj->fh != NULL) - fclose(obj->fh); - obj->fh = fh; - memcpy(&obj->stat, &stat_buf, sizeof(struct stat)); - - return 0; -} /* int cu_tail_reopen */ - -cu_tail_t *cu_tail_create(const char *file) { - cu_tail_t *obj; - - obj = calloc(1, sizeof(*obj)); - if (obj == NULL) - return NULL; - - obj->file = strdup(file); - if (obj->file == NULL) { - free(obj); - return NULL; - } - - obj->fh = NULL; - - return obj; -} /* cu_tail_t *cu_tail_create */ - -int cu_tail_destroy(cu_tail_t *obj) { - if (obj->fh != NULL) - fclose(obj->fh); - free(obj->file); - free(obj); - - return 0; -} /* int cu_tail_destroy */ - -int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) { - int status; - - if (buflen < 1) { - ERROR("utils_tail: cu_tail_readline: buflen too small: %i bytes.", buflen); - return -1; - } - - if (obj->fh == NULL) { - status = cu_tail_reopen(obj); - if (status < 0) - return status; - } - assert(obj->fh != NULL); - - /* Try to read from the filehandle. If that succeeds, everything appears to - * be fine and we can return. */ - clearerr(obj->fh); - if (fgets(buf, buflen, obj->fh) != NULL) { - buf[buflen - 1] = 0; - return 0; - } - - /* Check if we encountered an error */ - if (ferror(obj->fh) != 0) { - /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */ - fclose(obj->fh); - obj->fh = NULL; - } - /* else: eof -> check if the file was moved away and reopen the new file if - * so.. */ - - status = cu_tail_reopen(obj); - /* error -> return with error */ - if (status < 0) - return status; - /* file end reached and file not reopened -> nothing more to read */ - else if (status > 0) { - buf[0] = 0; - return 0; - } - - /* 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; - return 0; - } - - if (ferror(obj->fh) != 0) { - WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file, - STRERRNO); - fclose(obj->fh); - obj->fh = NULL; - return -1; - } - - /* EOf, well, apparently the new file is empty.. */ - buf[0] = 0; - return 0; -} /* int cu_tail_readline */ - -int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback, - void *data) { - int status; - - while (42) { - size_t len; - - status = cu_tail_readline(obj, buf, buflen); - if (status != 0) { - ERROR("utils_tail: cu_tail_read: cu_tail_readline " - "failed."); - break; - } - - /* check for EOF */ - if (buf[0] == 0) - break; - - len = strlen(buf); - while (len > 0) { - if (buf[len - 1] != '\n') - break; - buf[len - 1] = '\0'; - len--; - } - - status = callback(data, buf, buflen); - if (status != 0) { - ERROR("utils_tail: cu_tail_read: callback returned " - "status %i.", - status); - break; - } - } - - return status; -} /* int cu_tail_read */ diff --git a/src/utils_tail.h b/src/utils_tail.h deleted file mode 100644 index 73a6de21..00000000 --- a/src/utils_tail.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * collectd - src/utils_tail.h - * Copyright (C) 2007-2008 C-Ware, 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. - * - * Author: - * Luke Heberling - * - * DESCRIPTION - * Facilitates reading information that is appended to a file, taking into - * account that the file may be rotated and a new file created under the - * same name. - **/ - -#ifndef UTILS_TAIL_H -#define UTILS_TAIL_H 1 - -struct cu_tail_s; -typedef struct cu_tail_s cu_tail_t; - -typedef int tailfunc_t(void *data, char *buf, int buflen); - -/* - * NAME - * cu_tail_create - * - * DESCRIPTION - * Allocates a new tail object.. - * - * PARAMETERS - * `file' The name of the file to be tailed. - */ -cu_tail_t *cu_tail_create(const char *file); - -/* - * cu_tail_destroy - * - * Takes a tail object returned by `cu_tail_create' and destroys it, freeing - * all internal memory. - * - * Returns 0 when successful and non-zero otherwise. - */ -int cu_tail_destroy(cu_tail_t *obj); - -/* - * cu_tail_readline - * - * Reads from the file until `buflen' characters are read, a newline - * character is read, or an eof condition is encountered. `buf' is - * always null-terminated on successful return and isn't touched when non-zero - * is returned. - * - * You can check if the EOF condition is reached by looking at the buffer: If - * the length of the string stored in the buffer is zero, EOF occurred. - * Otherwise at least the newline character will be in the buffer. - * - * Returns 0 when successful and non-zero otherwise. - */ -int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen); - -/* - * cu_tail_readline - * - * Reads from the file until eof condition or an error is encountered. - * - * Returns 0 when successful and non-zero otherwise. - */ -int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback, - void *data); - -#endif /* UTILS_TAIL_H */ diff --git a/src/utils_tail_match.c b/src/utils_tail_match.c index ccab5ace..0e5a8611 100644 --- a/src/utils_tail_match.c +++ b/src/utils_tail_match.c @@ -31,11 +31,11 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_latency_config.h" -#include "utils_match.h" -#include "utils_tail.h" +#include "utils/common/common.h" +#include "utils/latency/latency_config.h" +#include "utils/match/match.h" +#include "utils/tail/tail.h" #include "utils_tail_match.h" struct cu_tail_match_simple_s { diff --git a/src/utils_tail_match.h b/src/utils_tail_match.h index 2d4c2531..771c2410 100644 --- a/src/utils_tail_match.h +++ b/src/utils_tail_match.h @@ -33,8 +33,11 @@ * regular expressions. */ -#include "utils_latency_config.h" -#include "utils_match.h" +#ifndef UTILS_TAIL_MATCH_H +#define UTILS_TAIL_MATCH_H 1 + +#include "utils/latency/latency_config.h" +#include "utils/match/match.h" struct cu_tail_match_s; typedef struct cu_tail_match_s cu_tail_match_t; @@ -131,5 +134,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, * * RETURN VALUE * Zero on success, nonzero on failure. -*/ + */ int tail_match_read(cu_tail_match_t *obj); + +#endif /* UTILS_TAIL_MATCH_H */ diff --git a/src/utils_taskstats.c b/src/utils_taskstats.c deleted file mode 100644 index f0d73334..00000000 --- a/src/utils_taskstats.c +++ /dev/null @@ -1,306 +0,0 @@ -/** - * 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 deleted file mode 100644 index de07427c..00000000 --- a/src/utils_taskstats.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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 deleted file mode 100644 index 03e61f80..00000000 --- a/src/utils_vl_lookup.c +++ /dev/null @@ -1,630 +0,0 @@ -/** - * collectd - src/utils_vl_lookup.c - * Copyright (C) 2012 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian Forster - **/ - -#include "collectd.h" - -#include -#include - -#include "common.h" -#include "utils_avltree.h" -#include "utils_vl_lookup.h" - -#if HAVE_KSTAT_H -#include -#endif - -#if HAVE_LIBKSTAT -kstat_ctl_t *kc; -#endif /* HAVE_LIBKSTAT */ - -#if BUILD_TEST -#define sstrncpy strncpy -#define plugin_log(s, ...) \ - do { \ - printf("[severity %i] ", s); \ - printf(__VA_ARGS__); \ - printf("\n"); \ - } while (0) -#endif - -/* - * Types - */ -struct part_match_s { - char str[DATA_MAX_NAME_LEN]; - regex_t regex; - bool is_regex; -}; -typedef struct part_match_s part_match_t; - -struct identifier_match_s { - part_match_t host; - part_match_t plugin; - part_match_t plugin_instance; - part_match_t type; - part_match_t type_instance; - - unsigned int group_by; -}; -typedef struct identifier_match_s identifier_match_t; - -struct lookup_s { - c_avl_tree_t *by_type_tree; - - lookup_class_callback_t cb_user_class; - lookup_obj_callback_t cb_user_obj; - lookup_free_class_callback_t cb_free_class; - lookup_free_obj_callback_t cb_free_obj; -}; - -struct user_obj_s; -typedef struct user_obj_s user_obj_t; -struct user_obj_s { - void *user_obj; - lookup_identifier_t ident; - - user_obj_t *next; -}; - -struct user_class_s { - pthread_mutex_t lock; - void *user_class; - identifier_match_t match; - user_obj_t *user_obj_list; /* list of user_obj */ -}; -typedef struct user_class_s user_class_t; - -struct user_class_list_s; -typedef struct user_class_list_s user_class_list_t; -struct user_class_list_s { - user_class_t entry; - user_class_list_t *next; -}; - -struct by_type_entry_s { - c_avl_tree_t *by_plugin_tree; /* plugin -> user_class_list_t */ - user_class_list_t *wildcard_plugin_list; -}; -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) { - if (match->is_regex) { - /* Short cut popular catch-all regex. */ - if (strcmp(".*", match->str) == 0) - return true; - - int status = regexec(&match->regex, str, - /* nmatch = */ 0, /* pmatch = */ NULL, - /* flags = */ 0); - if (status == 0) - return true; - else - return false; - } else if (strcmp(match->str, str) == 0) - return true; - else - return false; -} /* }}} bool lu_part_matches */ - -static int lu_copy_ident_to_match_part(part_match_t *match_part, /* {{{ */ - char const *ident_part) { - size_t len = strlen(ident_part); - int status; - - if ((len < 3) || (ident_part[0] != '/') || (ident_part[len - 1] != '/')) { - sstrncpy(match_part->str, ident_part, sizeof(match_part->str)); - match_part->is_regex = false; - return 0; - } - - /* Copy string without the leading slash. */ - sstrncpy(match_part->str, ident_part + 1, sizeof(match_part->str)); - assert(sizeof(match_part->str) > len); - /* strip trailing slash */ - match_part->str[len - 2] = 0; - - status = regcomp(&match_part->regex, match_part->str, - /* flags = */ REG_EXTENDED); - if (status != 0) { - char errbuf[1024]; - regerror(status, &match_part->regex, errbuf, sizeof(errbuf)); - ERROR("utils_vl_lookup: Compiling regular expression \"%s\" failed: %s", - match_part->str, errbuf); - return EINVAL; - } - match_part->is_regex = true; - - return 0; -} /* }}} int lu_copy_ident_to_match_part */ - -static int lu_copy_ident_to_match(identifier_match_t *match, /* {{{ */ - lookup_identifier_t const *ident, - unsigned int group_by) { - memset(match, 0, sizeof(*match)); - - match->group_by = group_by; - -#define COPY_FIELD(field) \ - do { \ - int status = lu_copy_ident_to_match_part(&match->field, ident->field); \ - if (status != 0) \ - return status; \ - } while (0) - - COPY_FIELD(host); - COPY_FIELD(plugin); - COPY_FIELD(plugin_instance); - COPY_FIELD(type); - COPY_FIELD(type_instance); - -#undef COPY_FIELD - - return 0; -} /* }}} int lu_copy_ident_to_match */ - -/* user_class->lock must be held when calling this function */ -static void *lu_create_user_obj(lookup_t *obj, /* {{{ */ - data_set_t const *ds, value_list_t const *vl, - user_class_t *user_class) { - user_obj_t *user_obj; - - user_obj = calloc(1, sizeof(*user_obj)); - if (user_obj == NULL) { - ERROR("utils_vl_lookup: calloc failed."); - return NULL; - } - user_obj->next = NULL; - - user_obj->user_obj = obj->cb_user_class(ds, vl, user_class->user_class); - if (user_obj->user_obj == NULL) { - sfree(user_obj); - WARNING("utils_vl_lookup: User-provided constructor failed."); - return NULL; - } - -#define COPY_FIELD(field, group_mask) \ - do { \ - if (user_class->match.field.is_regex && \ - ((user_class->match.group_by & group_mask) == 0)) \ - sstrncpy(user_obj->ident.field, "/.*/", sizeof(user_obj->ident.field)); \ - else \ - sstrncpy(user_obj->ident.field, vl->field, \ - sizeof(user_obj->ident.field)); \ - } while (0) - - COPY_FIELD(host, LU_GROUP_BY_HOST); - COPY_FIELD(plugin, LU_GROUP_BY_PLUGIN); - COPY_FIELD(plugin_instance, LU_GROUP_BY_PLUGIN_INSTANCE); - COPY_FIELD(type, 0); - COPY_FIELD(type_instance, LU_GROUP_BY_TYPE_INSTANCE); - -#undef COPY_FIELD - - if (user_class->user_obj_list == NULL) { - user_class->user_obj_list = user_obj; - } else { - user_obj_t *last = user_class->user_obj_list; - while (last->next != NULL) - last = last->next; - last->next = user_obj; - } - - return user_obj; -} /* }}} void *lu_create_user_obj */ - -/* user_class->lock must be held when calling this function */ -static user_obj_t *lu_find_user_obj(user_class_t *user_class, /* {{{ */ - value_list_t const *vl) { - user_obj_t *ptr; - - for (ptr = user_class->user_obj_list; ptr != NULL; ptr = ptr->next) { - if (user_class->match.host.is_regex && - (user_class->match.group_by & LU_GROUP_BY_HOST) && - (strcmp(vl->host, ptr->ident.host) != 0)) - continue; - if (user_class->match.plugin.is_regex && - (user_class->match.group_by & LU_GROUP_BY_PLUGIN) && - (strcmp(vl->plugin, ptr->ident.plugin) != 0)) - continue; - if (user_class->match.plugin_instance.is_regex && - (user_class->match.group_by & LU_GROUP_BY_PLUGIN_INSTANCE) && - (strcmp(vl->plugin_instance, ptr->ident.plugin_instance) != 0)) - continue; - if (user_class->match.type_instance.is_regex && - (user_class->match.group_by & LU_GROUP_BY_TYPE_INSTANCE) && - (strcmp(vl->type_instance, ptr->ident.type_instance) != 0)) - continue; - - return ptr; - } - - return NULL; -} /* }}} user_obj_t *lu_find_user_obj */ - -static int lu_handle_user_class(lookup_t *obj, /* {{{ */ - data_set_t const *ds, value_list_t const *vl, - user_class_t *user_class) { - user_obj_t *user_obj; - int status; - - assert(strcmp(vl->type, user_class->match.type.str) == 0); - assert(user_class->match.plugin.is_regex || - (strcmp(vl->plugin, user_class->match.plugin.str)) == 0); - - if (!lu_part_matches(&user_class->match.type_instance, vl->type_instance) || - !lu_part_matches(&user_class->match.plugin_instance, - vl->plugin_instance) || - !lu_part_matches(&user_class->match.plugin, vl->plugin) || - !lu_part_matches(&user_class->match.host, vl->host)) - return 1; - - pthread_mutex_lock(&user_class->lock); - user_obj = lu_find_user_obj(user_class, vl); - if (user_obj == NULL) { - /* call lookup_class_callback_t() and insert into the list of user objects. - */ - user_obj = lu_create_user_obj(obj, ds, vl, user_class); - if (user_obj == NULL) { - pthread_mutex_unlock(&user_class->lock); - return -1; - } - } - pthread_mutex_unlock(&user_class->lock); - - status = obj->cb_user_obj(ds, vl, user_class->user_class, user_obj->user_obj); - if (status != 0) { - ERROR("utils_vl_lookup: The user object callback failed with status %i.", - status); - /* Returning a negative value means: abort! */ - if (status < 0) - return status; - else - return 1; - } - - return 0; -} /* }}} int lu_handle_user_class */ - -static int lu_handle_user_class_list(lookup_t *obj, /* {{{ */ - data_set_t const *ds, - value_list_t const *vl, - user_class_list_t *user_class_list) { - user_class_list_t *ptr; - int retval = 0; - - for (ptr = user_class_list; ptr != NULL; ptr = ptr->next) { - int status; - - status = lu_handle_user_class(obj, ds, vl, &ptr->entry); - if (status < 0) - return status; - else if (status == 0) - retval++; - } - - return retval; -} /* }}} int lu_handle_user_class_list */ - -static by_type_entry_t *lu_search_by_type(lookup_t *obj, /* {{{ */ - char const *type, - bool allocate_if_missing) { - by_type_entry_t *by_type; - char *type_copy; - int status; - - status = c_avl_get(obj->by_type_tree, type, (void *)&by_type); - if (status == 0) - return by_type; - - if (!allocate_if_missing) - return NULL; - - type_copy = strdup(type); - if (type_copy == NULL) { - ERROR("utils_vl_lookup: strdup failed."); - return NULL; - } - - by_type = calloc(1, sizeof(*by_type)); - if (by_type == NULL) { - ERROR("utils_vl_lookup: calloc failed."); - sfree(type_copy); - return NULL; - } - by_type->wildcard_plugin_list = NULL; - - by_type->by_plugin_tree = - c_avl_create((int (*)(const void *, const void *))strcmp); - if (by_type->by_plugin_tree == NULL) { - ERROR("utils_vl_lookup: c_avl_create failed."); - sfree(by_type); - sfree(type_copy); - return NULL; - } - - status = c_avl_insert(obj->by_type_tree, - /* key = */ type_copy, /* value = */ by_type); - assert(status <= 0); /* >0 => entry exists => race condition. */ - if (status != 0) { - ERROR("utils_vl_lookup: c_avl_insert failed."); - c_avl_destroy(by_type->by_plugin_tree); - sfree(by_type); - sfree(type_copy); - return NULL; - } - - return by_type; -} /* }}} by_type_entry_t *lu_search_by_type */ - -static int lu_add_by_plugin(by_type_entry_t *by_type, /* {{{ */ - user_class_list_t *user_class_list) { - user_class_list_t *ptr = NULL; - identifier_match_t const *match = &user_class_list->entry.match; - - /* Lookup user_class_list from the per-plugin structure. If this is the first - * user_class to be added, the block returns immediately. Otherwise they will - * set "ptr" to non-NULL. */ - if (match->plugin.is_regex) { - if (by_type->wildcard_plugin_list == NULL) { - by_type->wildcard_plugin_list = user_class_list; - return 0; - } - - ptr = by_type->wildcard_plugin_list; - } /* if (plugin is wildcard) */ - else /* (plugin is not wildcard) */ - { - int status; - - status = - c_avl_get(by_type->by_plugin_tree, match->plugin.str, (void *)&ptr); - - if (status != 0) /* plugin not yet in tree */ - { - char *plugin_copy = strdup(match->plugin.str); - - if (plugin_copy == NULL) { - ERROR("utils_vl_lookup: strdup failed."); - sfree(user_class_list); - return ENOMEM; - } - - status = - c_avl_insert(by_type->by_plugin_tree, plugin_copy, user_class_list); - if (status != 0) { - ERROR("utils_vl_lookup: c_avl_insert(\"%s\") failed with status %i.", - plugin_copy, status); - sfree(plugin_copy); - sfree(user_class_list); - return status; - } else { - return 0; - } - } /* if (plugin not yet in tree) */ - } /* if (plugin is not wildcard) */ - - assert(ptr != NULL); - - while (ptr->next != NULL) - ptr = ptr->next; - ptr->next = user_class_list; - - return 0; -} /* }}} int lu_add_by_plugin */ - -static void lu_destroy_user_obj(lookup_t *obj, /* {{{ */ - user_obj_t *user_obj) { - while (user_obj != NULL) { - user_obj_t *next = user_obj->next; - - if (obj->cb_free_obj != NULL) - obj->cb_free_obj(user_obj->user_obj); - user_obj->user_obj = NULL; - - sfree(user_obj); - user_obj = next; - } -} /* }}} void lu_destroy_user_obj */ - -static void lu_destroy_user_class_list(lookup_t *obj, /* {{{ */ - user_class_list_t *user_class_list) { - while (user_class_list != NULL) { - user_class_list_t *next = user_class_list->next; - - if (obj->cb_free_class != NULL) - obj->cb_free_class(user_class_list->entry.user_class); - user_class_list->entry.user_class = NULL; - -#define CLEAR_FIELD(field) \ - do { \ - if (user_class_list->entry.match.field.is_regex) { \ - regfree(&user_class_list->entry.match.field.regex); \ - user_class_list->entry.match.field.is_regex = false; \ - } \ - } while (0) - - CLEAR_FIELD(host); - CLEAR_FIELD(plugin); - CLEAR_FIELD(plugin_instance); - CLEAR_FIELD(type); - CLEAR_FIELD(type_instance); - -#undef CLEAR_FIELD - - lu_destroy_user_obj(obj, user_class_list->entry.user_obj_list); - user_class_list->entry.user_obj_list = NULL; - pthread_mutex_destroy(&user_class_list->entry.lock); - - sfree(user_class_list); - user_class_list = next; - } -} /* }}} void lu_destroy_user_class_list */ - -static void lu_destroy_by_type(lookup_t *obj, /* {{{ */ - by_type_entry_t *by_type) { - - while (42) { - char *plugin = NULL; - user_class_list_t *user_class_list = NULL; - int status; - - status = c_avl_pick(by_type->by_plugin_tree, (void *)&plugin, - (void *)&user_class_list); - if (status != 0) - break; - - DEBUG("utils_vl_lookup: lu_destroy_by_type: Destroying plugin \"%s\".", - plugin); - sfree(plugin); - lu_destroy_user_class_list(obj, user_class_list); - } - - c_avl_destroy(by_type->by_plugin_tree); - by_type->by_plugin_tree = NULL; - - lu_destroy_user_class_list(obj, by_type->wildcard_plugin_list); - by_type->wildcard_plugin_list = NULL; - - sfree(by_type); -} /* }}} int lu_destroy_by_type */ - -/* - * Public functions - */ -lookup_t *lookup_create(lookup_class_callback_t cb_user_class, /* {{{ */ - lookup_obj_callback_t cb_user_obj, - lookup_free_class_callback_t cb_free_class, - lookup_free_obj_callback_t cb_free_obj) { - lookup_t *obj = calloc(1, sizeof(*obj)); - if (obj == NULL) { - ERROR("utils_vl_lookup: calloc failed."); - return NULL; - } - - obj->by_type_tree = c_avl_create((int (*)(const void *, const void *))strcmp); - if (obj->by_type_tree == NULL) { - ERROR("utils_vl_lookup: c_avl_create failed."); - sfree(obj); - return NULL; - } - - obj->cb_user_class = cb_user_class; - obj->cb_user_obj = cb_user_obj; - obj->cb_free_class = cb_free_class; - obj->cb_free_obj = cb_free_obj; - - return obj; -} /* }}} lookup_t *lookup_create */ - -void lookup_destroy(lookup_t *obj) /* {{{ */ -{ - int status; - - if (obj == NULL) - return; - - while (42) { - char *type = NULL; - by_type_entry_t *by_type = NULL; - - status = c_avl_pick(obj->by_type_tree, (void *)&type, (void *)&by_type); - if (status != 0) - break; - - DEBUG("utils_vl_lookup: lookup_destroy: Destroying type \"%s\".", type); - sfree(type); - lu_destroy_by_type(obj, by_type); - } - - c_avl_destroy(obj->by_type_tree); - obj->by_type_tree = NULL; - - sfree(obj); -} /* }}} void lookup_destroy */ - -int lookup_add(lookup_t *obj, /* {{{ */ - lookup_identifier_t const *ident, unsigned int group_by, - void *user_class) { - by_type_entry_t *by_type = NULL; - user_class_list_t *user_class_obj; - - by_type = lu_search_by_type(obj, ident->type, /* allocate = */ true); - if (by_type == NULL) - return -1; - - user_class_obj = calloc(1, sizeof(*user_class_obj)); - if (user_class_obj == NULL) { - ERROR("utils_vl_lookup: calloc failed."); - return ENOMEM; - } - pthread_mutex_init(&user_class_obj->entry.lock, /* attr = */ NULL); - user_class_obj->entry.user_class = user_class; - lu_copy_ident_to_match(&user_class_obj->entry.match, ident, group_by); - user_class_obj->entry.user_obj_list = NULL; - user_class_obj->next = NULL; - - return lu_add_by_plugin(by_type, user_class_obj); -} /* }}} int lookup_add */ - -/* returns the number of successful calls to the callback function */ -int lookup_search(lookup_t *obj, /* {{{ */ - data_set_t const *ds, value_list_t const *vl) { - by_type_entry_t *by_type = NULL; - user_class_list_t *user_class_list = NULL; - int retval = 0; - int status; - - if ((obj == NULL) || (ds == NULL) || (vl == NULL)) - return -EINVAL; - - by_type = lu_search_by_type(obj, vl->type, /* allocate = */ false); - if (by_type == NULL) - return 0; - - status = - c_avl_get(by_type->by_plugin_tree, vl->plugin, (void *)&user_class_list); - if (status == 0) { - status = lu_handle_user_class_list(obj, ds, vl, user_class_list); - if (status < 0) - return status; - retval += status; - } - - if (by_type->wildcard_plugin_list != NULL) { - status = - lu_handle_user_class_list(obj, ds, vl, by_type->wildcard_plugin_list); - if (status < 0) - return status; - retval += status; - } - - return retval; -} /* }}} lookup_search */ diff --git a/src/utils_vl_lookup.h b/src/utils_vl_lookup.h deleted file mode 100644 index 90a4ee52..00000000 --- a/src/utils_vl_lookup.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * collectd - src/utils_vl_lookup.h - * Copyright (C) 2012 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian Forster - **/ - -#ifndef UTILS_VL_LOOKUP_H -#define UTILS_VL_LOOKUP_H 1 - -#include "plugin.h" - -/* - * Types - */ -struct lookup_s; -typedef struct lookup_s lookup_t; - -/* Given a user_class, constructs a new user_obj. */ -typedef void *(*lookup_class_callback_t)(data_set_t const *ds, - value_list_t const *vl, - void *user_class); - -/* Given a user_class and a ds/vl combination, does stuff with the data. - * This is the main working horse of the module. */ -typedef int (*lookup_obj_callback_t)(data_set_t const *ds, - value_list_t const *vl, void *user_class, - void *user_obj); - -/* Used to free user_class pointers. May be NULL in which case nothing is - * freed. */ -typedef void (*lookup_free_class_callback_t)(void *user_class); - -/* Used to free user_obj pointers. May be NULL in which case nothing is - * freed. */ -typedef void (*lookup_free_obj_callback_t)(void *user_obj); - -struct lookup_identifier_s { - char host[DATA_MAX_NAME_LEN]; - char plugin[DATA_MAX_NAME_LEN]; - char plugin_instance[DATA_MAX_NAME_LEN]; - char type[DATA_MAX_NAME_LEN]; - char type_instance[DATA_MAX_NAME_LEN]; -}; -typedef struct lookup_identifier_s lookup_identifier_t; - -#define LU_GROUP_BY_HOST 0x01 -#define LU_GROUP_BY_PLUGIN 0x02 -#define LU_GROUP_BY_PLUGIN_INSTANCE 0x04 -/* #define LU_GROUP_BY_TYPE 0x00 */ -#define LU_GROUP_BY_TYPE_INSTANCE 0x10 - -/* - * Functions - */ -__attribute__((nonnull(1, 2))) -lookup_t *lookup_create(lookup_class_callback_t, lookup_obj_callback_t, - lookup_free_class_callback_t, - lookup_free_obj_callback_t); -void lookup_destroy(lookup_t *obj); - -int lookup_add(lookup_t *obj, lookup_identifier_t const *ident, - unsigned int group_by, void *user_class); - -/* TODO(octo): Pass lookup_obj_callback_t to lookup_search()? */ -int lookup_search(lookup_t *obj, data_set_t const *ds, value_list_t const *vl); - -#endif /* UTILS_VL_LOOKUP_H */ diff --git a/src/utils_vl_lookup_test.c b/src/utils_vl_lookup_test.c deleted file mode 100644 index 27bfddf0..00000000 --- a/src/utils_vl_lookup_test.c +++ /dev/null @@ -1,242 +0,0 @@ -/** - * collectd - src/tests/test_utils_vl_lookup.c - * Copyright (C) 2012 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian Forster - **/ - -#include "collectd.h" - -#include "testing.h" -#include "utils_vl_lookup.h" - -static bool expect_new_obj; -static bool have_new_obj; - -static lookup_identifier_t last_class_ident; -static lookup_identifier_t last_obj_ident; - -static data_source_t dsrc_test = {"value", DS_TYPE_DERIVE, 0.0, NAN}; -static data_set_t const ds_test = {"test", 1, &dsrc_test}; - -static data_source_t dsrc_unknown = {"value", DS_TYPE_DERIVE, 0.0, NAN}; -static data_set_t const ds_unknown = {"unknown", 1, &dsrc_unknown}; - -static int lookup_obj_callback(data_set_t const *ds, value_list_t const *vl, - void *user_class, void *user_obj) { - lookup_identifier_t *class = user_class; - lookup_identifier_t *obj = user_obj; - - OK1(expect_new_obj == have_new_obj, - (expect_new_obj ? "New obj is created." : "Updating existing obj.")); - - memcpy(&last_class_ident, class, sizeof(last_class_ident)); - memcpy(&last_obj_ident, obj, sizeof(last_obj_ident)); - - if (strcmp(obj->plugin_instance, "failure") == 0) - return -1; - - return 0; -} - -static void *lookup_class_callback(data_set_t const *ds, value_list_t const *vl, - void *user_class) { - lookup_identifier_t *class = user_class; - lookup_identifier_t *obj; - - assert(expect_new_obj); - - memcpy(&last_class_ident, class, sizeof(last_class_ident)); - - obj = malloc(sizeof(*obj)); - strncpy(obj->host, vl->host, sizeof(obj->host)); - strncpy(obj->plugin, vl->plugin, sizeof(obj->plugin)); - strncpy(obj->plugin_instance, vl->plugin_instance, - sizeof(obj->plugin_instance)); - strncpy(obj->type, vl->type, sizeof(obj->type)); - strncpy(obj->type_instance, vl->type_instance, sizeof(obj->type_instance)); - - have_new_obj = true; - - return (void *)obj; -} - -static int checked_lookup_add(lookup_t *obj, /* {{{ */ - char const *host, char const *plugin, - char const *plugin_instance, char const *type, - char const *type_instance, - unsigned int group_by) { - lookup_identifier_t ident; - void *user_class; - - strncpy(ident.host, host, sizeof(ident.host)); - strncpy(ident.plugin, plugin, sizeof(ident.plugin)); - 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)); - - user_class = malloc(sizeof(ident)); - memmove(user_class, &ident, sizeof(ident)); - - OK(lookup_add(obj, &ident, group_by, user_class) == 0); - return 0; -} /* }}} int checked_lookup_add */ - -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) { - 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)); - - if (strcmp(vl.type, "test") == 0) - ds = &ds_test; - - expect_new_obj = expect_new; - have_new_obj = false; - - status = lookup_search(obj, ds, &vl); - return status; -} - -DEF_TEST(group_by_specific_host) { - lookup_t *obj; - CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback, - (void *)free, (void *)free)); - - checked_lookup_add(obj, "/.*/", "test", "", "test", "/.*/", LU_GROUP_BY_HOST); - checked_lookup_search(obj, "host0", "test", "", "test", "0", - /* expect new = */ 1); - checked_lookup_search(obj, "host0", "test", "", "test", "1", - /* expect new = */ 0); - checked_lookup_search(obj, "host1", "test", "", "test", "0", - /* expect new = */ 1); - checked_lookup_search(obj, "host1", "test", "", "test", "1", - /* expect new = */ 0); - - lookup_destroy(obj); - return 0; -} - -DEF_TEST(group_by_any_host) { - lookup_t *obj; - CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback, - (void *)free, (void *)free)); - - checked_lookup_add(obj, "/.*/", "/.*/", "/.*/", "test", "/.*/", - LU_GROUP_BY_HOST); - checked_lookup_search(obj, "host0", "plugin0", "", "test", "0", - /* expect new = */ 1); - checked_lookup_search(obj, "host0", "plugin0", "", "test", "1", - /* expect new = */ 0); - checked_lookup_search(obj, "host0", "plugin1", "", "test", "0", - /* expect new = */ 0); - checked_lookup_search(obj, "host0", "plugin1", "", "test", "1", - /* expect new = */ 0); - checked_lookup_search(obj, "host1", "plugin0", "", "test", "0", - /* expect new = */ 1); - checked_lookup_search(obj, "host1", "plugin0", "", "test", "1", - /* expect new = */ 0); - checked_lookup_search(obj, "host1", "plugin1", "", "test", "0", - /* expect new = */ 0); - checked_lookup_search(obj, "host1", "plugin1", "", "test", "1", - /* expect new = */ 0); - - lookup_destroy(obj); - return 0; -} - -DEF_TEST(multiple_lookups) { - lookup_t *obj; - int status; - - CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback, - (void *)free, (void *)free)); - - checked_lookup_add(obj, "/.*/", "plugin0", "", "test", "/.*/", - LU_GROUP_BY_HOST); - checked_lookup_add(obj, "/.*/", "/.*/", "", "test", "ti0", LU_GROUP_BY_HOST); - - status = checked_lookup_search(obj, "host0", "plugin1", "", "test", "", - /* expect new = */ 0); - assert(status == 0); - status = checked_lookup_search(obj, "host0", "plugin0", "", "test", "", - /* expect new = */ 1); - assert(status == 1); - status = checked_lookup_search(obj, "host0", "plugin1", "", "test", "ti0", - /* expect new = */ 1); - assert(status == 1); - status = checked_lookup_search(obj, "host0", "plugin0", "", "test", "ti0", - /* expect new = */ 0); - assert(status == 2); - - lookup_destroy(obj); - return 0; -} - -DEF_TEST(regex) { - lookup_t *obj; - CHECK_NOT_NULL(obj = lookup_create(lookup_class_callback, lookup_obj_callback, - (void *)free, (void *)free)); - - checked_lookup_add(obj, "/^db[0-9]\\./", "cpu", "/.*/", "cpu", "/.*/", - LU_GROUP_BY_TYPE_INSTANCE); - checked_lookup_search(obj, "db0.example.com", "cpu", "0", "cpu", "user", - /* expect new = */ 1); - checked_lookup_search(obj, "db0.example.com", "cpu", "0", "cpu", "idle", - /* expect new = */ 1); - checked_lookup_search(obj, "db0.example.com", "cpu", "1", "cpu", "user", - /* expect new = */ 0); - checked_lookup_search(obj, "db0.example.com", "cpu", "1", "cpu", "idle", - /* expect new = */ 0); - checked_lookup_search(obj, "app0.example.com", "cpu", "0", "cpu", "user", - /* expect new = */ 0); - checked_lookup_search(obj, "app0.example.com", "cpu", "0", "cpu", "idle", - /* expect new = */ 0); - checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "user", - /* expect new = */ 0); - checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "idle", - /* expect new = */ 0); - checked_lookup_search(obj, "db1.example.com", "cpu", "0", "cpu", "system", - /* expect new = */ 1); - - lookup_destroy(obj); - return 0; -} - -int main(int argc, char **argv) /* {{{ */ -{ - RUN_TEST(group_by_specific_host); - RUN_TEST(group_by_any_host); - RUN_TEST(multiple_lookups); - RUN_TEST(regex); - - END_TEST; -} /* }}} int main */ diff --git a/src/uuid.c b/src/uuid.c index c7878c74..60d09b51 100644 --- a/src/uuid.c +++ b/src/uuid.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_SYS_SYSCTL_H #include diff --git a/src/varnish.c b/src/varnish.c index b515be89..f4c70af8 100644 --- a/src/varnish.c +++ b/src/varnish.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if HAVE_VARNISH_V4 || HAVE_VARNISH_V5 #include @@ -107,8 +107,8 @@ static int varnish_submit(const char *plugin_instance, /* {{{ */ if (plugin_instance == NULL) plugin_instance = "default"; - snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%s", - plugin_instance, category); + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%s", + plugin_instance, category); sstrncpy(vl.type, type, sizeof(vl.type)); @@ -1546,7 +1546,8 @@ static int varnish_init(void) /* {{{ */ /* callback = */ varnish_read, /* interval = */ 0, &(user_data_t){ - .data = conf, .free_func = varnish_config_free, + .data = conf, + .free_func = varnish_config_free, }); return 0; @@ -1751,7 +1752,7 @@ static int varnish_config_instance(const oconfig_item_t *ci) /* {{{ */ !conf->collect_mgt && !conf->collect_lck && !conf->collect_mempool && !conf->collect_mse #endif - ) { + ) { WARNING("Varnish plugin: No metric has been configured for " "instance \"%s\". Disabling this instance.", (conf->instance == NULL) ? "localhost" : conf->instance); @@ -1759,8 +1760,8 @@ static int varnish_config_instance(const oconfig_item_t *ci) /* {{{ */ return EINVAL; } - snprintf(callback_name, sizeof(callback_name), "varnish/%s", - (conf->instance == NULL) ? "localhost" : conf->instance); + ssnprintf(callback_name, sizeof(callback_name), "varnish/%s", + (conf->instance == NULL) ? "localhost" : conf->instance); plugin_register_complex_read( /* group = */ "varnish", @@ -1768,7 +1769,8 @@ static int varnish_config_instance(const oconfig_item_t *ci) /* {{{ */ /* callback = */ varnish_read, /* interval = */ 0, &(user_data_t){ - .data = conf, .free_func = varnish_config_free, + .data = conf, + .free_func = varnish_config_free, }); have_instance = true; diff --git a/src/virt.c b/src/virt.c index c6ac5905..01c7c777 100644 --- a/src/virt.c +++ b/src/virt.c @@ -22,10 +22,10 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" +#include "utils/ignorelist/ignorelist.h" #include "utils_complain.h" -#include "utils_ignorelist.h" #include /* for basename(3) */ #include @@ -69,9 +69,10 @@ #endif /* - virConnectListAllDomains() appeared in 0.10.2 - Note that LIBVIR_CHECK_VERSION appeared a year later, so - in some systems which actually have virConnectListAllDomains() + virConnectListAllDomains() appeared in 0.10.2 (Sep 2012) + Note that LIBVIR_CHECK_VERSION appeared a year later (Dec 2013, + libvirt-1.2.0), + so in some systems which actually have virConnectListAllDomains() we can't detect this. */ #if LIBVIR_CHECK_VERSION(0, 10, 2) @@ -107,6 +108,9 @@ #define HAVE_DOM_REASON_POSTCOPY 1 #endif +#if LIBVIR_CHECK_VERSION(4, 10, 0) +#define HAVE_DOM_REASON_SHUTOFF_DAEMON 1 +#endif #endif /* LIBVIR_CHECK_VERSION */ /* structure used for aggregating notification-thread data*/ @@ -117,44 +121,26 @@ typedef struct virt_notif_thread_s { bool is_active; } virt_notif_thread_t; -static const char *config_keys[] = {"Connection", - - "RefreshInterval", - - "Domain", - "BlockDevice", - "BlockDeviceFormat", - "BlockDeviceFormatBasename", - "InterfaceDevice", - "IgnoreSelected", - - "HostnameFormat", - "InterfaceFormat", - - "PluginInstanceFormat", - - "Instances", - "ExtraStats", - "PersistentNotification", - 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", - [VIR_DOMAIN_BLOCKED] = "the domain is blocked on resource", - [VIR_DOMAIN_PAUSED] = "the domain is paused by user", - [VIR_DOMAIN_SHUTDOWN] = "the domain is being shut down", - [VIR_DOMAIN_SHUTOFF] = "the domain is shut off", - [VIR_DOMAIN_CRASHED] = "the domain is crashed", + [VIR_DOMAIN_NOSTATE] = "no state", + [VIR_DOMAIN_RUNNING] = "the domain is running", + [VIR_DOMAIN_BLOCKED] = "the domain is blocked on resource", + [VIR_DOMAIN_PAUSED] = "the domain is paused by user", + [VIR_DOMAIN_SHUTDOWN] = "the domain is being shut down", + [VIR_DOMAIN_SHUTOFF] = "the domain is shut off", + [VIR_DOMAIN_CRASHED] = "the domain is crashed", #ifdef HAVE_DOM_STATE_PMSUSPENDED - [VIR_DOMAIN_PMSUSPENDED] = - "the domain is suspended by guest power management", + [VIR_DOMAIN_PMSUSPENDED] = + "the domain is suspended by guest power management", #endif }; @@ -318,6 +304,16 @@ static int map_domain_event_detail_to_reason(int event, int detail) { switch (detail) { case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED: /* Guest finished shutdown sequence */ +#ifdef LIBVIR_CHECK_VERSION +#if LIBVIR_CHECK_VERSION(3, 4, 0) + case VIR_DOMAIN_EVENT_SHUTDOWN_GUEST: /* Domain finished shutting down after + request from the guest itself (e.g. + hardware-specific action) */ + case VIR_DOMAIN_EVENT_SHUTDOWN_HOST: /* Domain finished shutting down after + request from the host (e.g. killed + by a signal) */ +#endif +#endif ret = VIR_DOMAIN_SHUTDOWN_USER; break; default: @@ -356,108 +352,103 @@ static int map_domain_event_detail_to_reason(int event, int detail) { #define DOMAIN_STATE_REASON_MAX_SIZE 20 const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = { - [VIR_DOMAIN_NOSTATE][VIR_DOMAIN_NOSTATE_UNKNOWN] = - "the reason is unknown", - - [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_UNKNOWN] = - "the reason is unknown", - [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_BOOTED] = - "normal startup from boot", - [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_MIGRATED] = - "migrated from another host", - [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_RESTORED] = - "restored from a state file", - [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_FROM_SNAPSHOT] = - "restored from snapshot", - [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_UNPAUSED] = - "returned from paused state", - [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_MIGRATION_CANCELED] = - "returned from migration", - [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_SAVE_CANCELED] = - "returned from failed save process", + [VIR_DOMAIN_NOSTATE][VIR_DOMAIN_NOSTATE_UNKNOWN] = "the reason is unknown", + + [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_UNKNOWN] = "the reason is unknown", + [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_BOOTED] = + "normal startup from boot", + [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_MIGRATED] = + "migrated from another host", + [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_RESTORED] = + "restored from a state file", + [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_FROM_SNAPSHOT] = + "restored from snapshot", + [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_UNPAUSED] = + "returned from paused state", + [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_MIGRATION_CANCELED] = + "returned from migration", + [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_SAVE_CANCELED] = + "returned from failed save process", #ifdef HAVE_DOM_REASON_RUNNING_WAKEUP - [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_WAKEUP] = - "returned from pmsuspended due to wakeup event", + [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_WAKEUP] = + "returned from pmsuspended due to wakeup event", #endif #ifdef HAVE_DOM_REASON_CRASHED - [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_CRASHED] = - "resumed from crashed", + [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_CRASHED] = "resumed from crashed", #endif #ifdef HAVE_DOM_REASON_POSTCOPY - [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", - - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_UNKNOWN] = - "the reason is unknown", - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_USER] = "paused on user request", - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_MIGRATION] = - "paused for offline migration", - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_SAVE] = "paused for save", - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_DUMP] = - "paused for offline core dump", - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_IOERROR] = - "paused due to a disk I/O error", - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_WATCHDOG] = - "paused due to a watchdog event", - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_FROM_SNAPSHOT] = - "paused after restoring from snapshot", + [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", + + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_UNKNOWN] = "the reason is unknown", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_USER] = "paused on user request", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_MIGRATION] = + "paused for offline migration", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_SAVE] = "paused for save", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_DUMP] = + "paused for offline core dump", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_IOERROR] = + "paused due to a disk I/O error", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_WATCHDOG] = + "paused due to a watchdog event", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_FROM_SNAPSHOT] = + "paused after restoring from snapshot", #ifdef HAVE_DOM_REASON_PAUSED_SHUTTING_DOWN - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_SHUTTING_DOWN] = - "paused during shutdown process", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_SHUTTING_DOWN] = + "paused during shutdown process", #endif #ifdef HAVE_DOM_REASON_PAUSED_SNAPSHOT - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_SNAPSHOT] = - "paused while creating a snapshot", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_SNAPSHOT] = + "paused while creating a snapshot", #endif #ifdef HAVE_DOM_REASON_PAUSED_CRASHED - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_CRASHED] = - "paused due to a guest crash", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_CRASHED] = + "paused due to a guest crash", #endif #ifdef HAVE_DOM_REASON_PAUSED_STARTING_UP - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_STARTING_UP] = - "the domain is being started", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_STARTING_UP] = + "the domain is being started", #endif #ifdef HAVE_DOM_REASON_POSTCOPY - [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_POSTCOPY] = - "paused for post-copy migration", - [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] = - "shutting down on user request", - - [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_UNKNOWN] = - "the reason is unknown", - [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_SHUTDOWN] = "normal shutdown", - [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_DESTROYED] = "forced poweroff", - [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_CRASHED] = "domain crashed", - [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_MIGRATED] = - "migrated to another host", - [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_SAVED] = "saved to a file", - [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_FAILED] = - "domain failed to start", - [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT] = - "restored from a snapshot which was taken while domain was shutoff", - - [VIR_DOMAIN_CRASHED][VIR_DOMAIN_CRASHED_UNKNOWN] = - "the reason is unknown", + [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_POSTCOPY] = + "paused for post-copy migration", + [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] = + "shutting down on user request", + + [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_UNKNOWN] = "the reason is unknown", + [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_SHUTDOWN] = "normal shutdown", + [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_DESTROYED] = "forced poweroff", + [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_CRASHED] = "domain crashed", + [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_MIGRATED] = + "migrated to another host", + [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_SAVED] = "saved to a file", + [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_FAILED] = "domain failed to start", + [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT] = + "restored from a snapshot which was taken while domain was shutoff", +#ifdef HAVE_DOM_REASON_SHUTOFF_DAEMON + [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_DAEMON] = + "daemon decides to kill domain during reconnection processing", +#endif + + [VIR_DOMAIN_CRASHED][VIR_DOMAIN_CRASHED_UNKNOWN] = "the reason is unknown", #ifdef VIR_DOMAIN_CRASHED_PANICKED - [VIR_DOMAIN_CRASHED][VIR_DOMAIN_CRASHED_PANICKED] = "domain panicked", + [VIR_DOMAIN_CRASHED][VIR_DOMAIN_CRASHED_PANICKED] = "domain panicked", #endif #ifdef HAVE_DOM_STATE_PMSUSPENDED - [VIR_DOMAIN_PMSUSPENDED][VIR_DOMAIN_PMSUSPENDED_UNKNOWN] = - "the reason is unknown", + [VIR_DOMAIN_PMSUSPENDED][VIR_DOMAIN_PMSUSPENDED_UNKNOWN] = + "the reason is unknown", #endif }; #endif /* HAVE_DOM_REASON */ -#define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1) #define NANOSEC_IN_SEC 1e9 #define GET_STATS(_f, _name, ...) \ @@ -492,6 +483,7 @@ static int ignore_device_match(ignorelist_t *, const char *domname, struct block_device { virDomainPtr dom; /* domain */ char *path; /* name of block device */ + bool has_source; /* information whether source is defined or not */ }; /* Actual list of network interfaces found on last refresh. */ @@ -526,7 +518,7 @@ static int add_domain(struct lv_read_state *state, virDomainPtr dom, 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); + const char *path, bool has_source); static void free_interface_devices(struct lv_read_state *state); static int add_interface_device(struct lv_read_state *state, virDomainPtr dom, @@ -557,20 +549,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 }; @@ -599,6 +600,11 @@ enum ex_stats { ex_stats_job_stats_completed = 1 << 8, ex_stats_job_stats_background = 1 << 9, #endif + ex_stats_disk_allocation = 1 << 10, + ex_stats_disk_capacity = 1 << 11, + ex_stats_disk_physical = 1 << 12, + ex_stats_memory = 1 << 13, + ex_stats_vcpu = 1 << 14 }; static unsigned int extra_stats = ex_stats_none; @@ -626,6 +632,11 @@ static const struct ex_stats_item ex_stats_table[] = { {"job_stats_completed", ex_stats_job_stats_completed}, {"job_stats_background", ex_stats_job_stats_background}, #endif + {"disk_allocation", ex_stats_disk_allocation}, + {"disk_capacity", ex_stats_disk_capacity}, + {"disk_physical", ex_stats_disk_physical}, + {"memory", ex_stats_memory}, + {"vcpu", ex_stats_vcpu}, {NULL, ex_stats_none}, }; @@ -638,8 +649,10 @@ static enum if_field interface_format = if_name; static time_t last_refresh = (time_t)0; static int refresh_lists(struct lv_read_instance *inst); +static int register_event_impl(void); +static int start_event_loop(virt_notif_thread_t *thread_data); -struct lv_block_info { +struct lv_block_stats { virDomainBlockStatsStruct bi; long long rd_total_times; @@ -649,50 +662,56 @@ struct lv_block_info { long long fl_total_times; }; -static void init_block_info(struct lv_block_info *binfo) { - if (binfo == NULL) +static void init_block_stats(struct lv_block_stats *bstats) { + if (bstats == NULL) return; - binfo->bi.rd_req = -1; - binfo->bi.wr_req = -1; - binfo->bi.rd_bytes = -1; - binfo->bi.wr_bytes = -1; + bstats->bi.rd_req = -1; + bstats->bi.wr_req = -1; + bstats->bi.rd_bytes = -1; + bstats->bi.wr_bytes = -1; + + bstats->rd_total_times = -1; + bstats->wr_total_times = -1; + bstats->fl_req = -1; + bstats->fl_total_times = -1; +} - binfo->rd_total_times = -1; - binfo->wr_total_times = -1; - binfo->fl_req = -1; - binfo->fl_total_times = -1; +static void init_block_info(virDomainBlockInfoPtr binfo) { + binfo->allocation = -1; + binfo->capacity = -1; + binfo->physical = -1; } #ifdef HAVE_BLOCK_STATS_FLAGS -#define GET_BLOCK_INFO_VALUE(NAME, FIELD) \ +#define GET_BLOCK_STATS_VALUE(NAME, FIELD) \ if (!strcmp(param[i].field, NAME)) { \ - binfo->FIELD = param[i].value.l; \ + bstats->FIELD = param[i].value.l; \ continue; \ } -static int get_block_info(struct lv_block_info *binfo, - virTypedParameterPtr param, int nparams) { - if (binfo == NULL || param == NULL) +static int get_block_stats(struct lv_block_stats *bstats, + virTypedParameterPtr param, int nparams) { + if (bstats == NULL || param == NULL) return -1; for (int i = 0; i < nparams; ++i) { /* ignore type. Everything must be LLONG anyway. */ - GET_BLOCK_INFO_VALUE("rd_operations", bi.rd_req); - GET_BLOCK_INFO_VALUE("wr_operations", bi.wr_req); - GET_BLOCK_INFO_VALUE("rd_bytes", bi.rd_bytes); - GET_BLOCK_INFO_VALUE("wr_bytes", bi.wr_bytes); - GET_BLOCK_INFO_VALUE("rd_total_times", rd_total_times); - GET_BLOCK_INFO_VALUE("wr_total_times", wr_total_times); - GET_BLOCK_INFO_VALUE("flush_operations", fl_req); - GET_BLOCK_INFO_VALUE("flush_total_times", fl_total_times); + GET_BLOCK_STATS_VALUE("rd_operations", bi.rd_req); + GET_BLOCK_STATS_VALUE("wr_operations", bi.wr_req); + GET_BLOCK_STATS_VALUE("rd_bytes", bi.rd_bytes); + GET_BLOCK_STATS_VALUE("wr_bytes", bi.wr_bytes); + GET_BLOCK_STATS_VALUE("rd_total_times", rd_total_times); + GET_BLOCK_STATS_VALUE("wr_total_times", wr_total_times); + GET_BLOCK_STATS_VALUE("flush_operations", fl_req); + GET_BLOCK_STATS_VALUE("flush_total_times", fl_total_times); } return 0; } -#undef GET_BLOCK_INFO_VALUE +#undef GET_BLOCK_STATS_VALUE #endif /* HAVE_BLOCK_STATS_FLAGS */ @@ -705,6 +724,105 @@ static int get_block_info(struct lv_block_info *binfo, ERROR(PLUGIN_NAME " plugin: %s failed: %s", (s), err->message); \ } while (0) +enum metadata_set_type_e { META_APPEND_HOST, META_APPEND_PLUGIN_INSTANCE }; + +static void set_field_from_metadata(value_list_t *vl, virDomainPtr dom, + enum metadata_set_type_e field) { + 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"; + } // namespace =hm_ns; + else { + namespace = hm_ns; + } + + char *metadata_str = virDomainGetMetadata( + dom, VIR_DOMAIN_METADATA_ELEMENT, namespace, VIR_DOMAIN_AFFECT_CURRENT); + if (metadata_str == NULL) { + return; + } + + const char *value = 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) { + value = (const char *)xml_node->content; + } else if (xml_node->type == XML_ATTRIBUTE_NODE) { + value = (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 (value == NULL) + goto metadata_end; + + switch (field) { + case META_APPEND_HOST: + SSTRNCAT(vl->host, value, sizeof(vl->host)); + break; + case META_APPEND_PLUGIN_INSTANCE: + SSTRNCAT(vl->plugin_instance, value, sizeof(vl->plugin_instance)); + break; + } + +metadata_end: + if (xpath_obj) + xmlXPathFreeObject(xpath_obj); + if (xpath_ctx) + xmlXPathFreeContext(xpath_ctx); + if (xml_doc) + xmlFreeDoc(xml_doc); + sfree(metadata_str); +} + static void init_value_list(value_list_t *vl, virDomainPtr dom) { const char *name; char uuid[VIR_UUID_STRING_BUFLEN]; @@ -736,6 +854,9 @@ static void init_value_list(value_list_t *vl, virDomainPtr dom) { if (virDomainGetUUIDString(dom, uuid) == 0) SSTRNCAT(vl->host, uuid, sizeof(vl->host)); break; + case hf_metadata: + set_field_from_metadata(vl, dom, META_APPEND_HOST); + break; } } @@ -759,9 +880,11 @@ static void init_value_list(value_list_t *vl, virDomainPtr dom) { if (virDomainGetUUIDString(dom, uuid) == 0) SSTRNCAT(vl->plugin_instance, uuid, sizeof(vl->plugin_instance)); break; + case plginst_metadata: + set_field_from_metadata(vl, dom, META_APPEND_PLUGIN_INSTANCE); + break; } } - } /* void init_value_list */ static int init_notif(notification_t *notif, const virDomainPtr domain, @@ -814,13 +937,14 @@ static void memory_submit(virDomainPtr dom, gauge_t value) { static void memory_stats_submit(gauge_t value, virDomainPtr dom, int tag_index) { - static const char *tags[] = {"swap_in", "swap_out", "major_fault", - "minor_fault", "unused", "available", - "actual_balloon", "rss", "usable", - "last_update"}; + static const char *tags[] = {"swap_in", "swap_out", "major_fault", + "minor_fault", "unused", "available", + "actual_balloon", "rss", "usable", + "last_update", "disk_caches"}; if ((tag_index < 0) || (tag_index >= (int)STATIC_ARRAY_SIZE(tags))) { - ERROR("virt plugin: Array index out of bounds: tag_index = %d", tag_index); + ERROR(PLUGIN_NAME " plugin: Array index out of bounds: tag_index = %d", + tag_index); return; } @@ -830,7 +954,8 @@ static void memory_stats_submit(gauge_t value, virDomainPtr dom, static void submit_derive2(const char *type, derive_t v0, derive_t v1, virDomainPtr dom, const char *devname) { value_t values[] = { - {.derive = v0}, {.derive = v1}, + {.derive = v0}, + {.derive = v1}, }; submit(dom, type, devname, values, STATIC_ARRAY_SIZE(values)); @@ -850,7 +975,7 @@ static double cpu_ns_to_percent(unsigned int node_cpus, } DEBUG(PLUGIN_NAME " plugin: node_cpus=%u cpu_time_old=%" PRIu64 - " cpu_time_new=%" PRIu64 "cpu_time_diff=%" 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); @@ -882,12 +1007,13 @@ static void vcpu_submit(derive_t value, virDomainPtr dom, int vcpu_nr, const char *type) { char type_instance[DATA_MAX_NAME_LEN]; - snprintf(type_instance, sizeof(type_instance), "%d", vcpu_nr); + ssnprintf(type_instance, sizeof(type_instance), "%d", vcpu_nr); submit(dom, type, type_instance, &(value_t){.derive = value}, 1); } -static void disk_submit(struct lv_block_info *binfo, virDomainPtr dom, - const char *dev) { +static void disk_block_stats_submit(struct lv_block_stats *bstats, + virDomainPtr dom, const char *dev, + virDomainBlockInfoPtr binfo) { char *dev_copy = strdup(dev); const char *type_instance = dev_copy; @@ -903,37 +1029,61 @@ static void disk_submit(struct lv_block_info *binfo, virDomainPtr dom, } char flush_type_instance[DATA_MAX_NAME_LEN]; - snprintf(flush_type_instance, sizeof(flush_type_instance), "flush-%s", - type_instance); + ssnprintf(flush_type_instance, sizeof(flush_type_instance), "flush-%s", + type_instance); - if ((binfo->bi.rd_req != -1) && (binfo->bi.wr_req != -1)) - submit_derive2("disk_ops", (derive_t)binfo->bi.rd_req, - (derive_t)binfo->bi.wr_req, dom, type_instance); + if ((bstats->bi.rd_req != -1) && (bstats->bi.wr_req != -1)) + submit_derive2("disk_ops", (derive_t)bstats->bi.rd_req, + (derive_t)bstats->bi.wr_req, dom, type_instance); - if ((binfo->bi.rd_bytes != -1) && (binfo->bi.wr_bytes != -1)) - submit_derive2("disk_octets", (derive_t)binfo->bi.rd_bytes, - (derive_t)binfo->bi.wr_bytes, dom, type_instance); + if ((bstats->bi.rd_bytes != -1) && (bstats->bi.wr_bytes != -1)) + submit_derive2("disk_octets", (derive_t)bstats->bi.rd_bytes, + (derive_t)bstats->bi.wr_bytes, dom, type_instance); if (extra_stats & ex_stats_disk) { - if ((binfo->rd_total_times != -1) && (binfo->wr_total_times != -1)) - submit_derive2("disk_time", (derive_t)binfo->rd_total_times, - (derive_t)binfo->wr_total_times, dom, type_instance); + if ((bstats->rd_total_times != -1) && (bstats->wr_total_times != -1)) + submit_derive2("disk_time", (derive_t)bstats->rd_total_times, + (derive_t)bstats->wr_total_times, dom, type_instance); - if (binfo->fl_req != -1) + if (bstats->fl_req != -1) submit(dom, "total_requests", flush_type_instance, - &(value_t){.derive = (derive_t)binfo->fl_req}, 1); - if (binfo->fl_total_times != -1) { - derive_t value = binfo->fl_total_times / 1000; // ns -> ms + &(value_t){.derive = (derive_t)bstats->fl_req}, 1); + if (bstats->fl_total_times != -1) { + derive_t value = bstats->fl_total_times / 1000; // ns -> ms submit(dom, "total_time_in_ms", flush_type_instance, &(value_t){.derive = value}, 1); } } + /* disk_allocation, disk_capacity and disk_physical are stored only + * if corresponding extrastats are set in collectd configuration file */ + if ((extra_stats & ex_stats_disk_allocation) && binfo->allocation != -1) + submit(dom, "disk_allocation", type_instance, + &(value_t){.gauge = (gauge_t)binfo->allocation}, 1); + + if ((extra_stats & ex_stats_disk_capacity) && binfo->capacity != -1) + submit(dom, "disk_capacity", type_instance, + &(value_t){.gauge = (gauge_t)binfo->capacity}, 1); + + if ((extra_stats & ex_stats_disk_physical) && binfo->physical != -1) + submit(dom, "disk_physical", type_instance, + &(value_t){.gauge = (gauge_t)binfo->physical}, 1); + sfree(dev_copy); } -static unsigned int parse_ex_stats_flags(char **exstats, int numexstats) { +/** + * Function for parsing ExtraStats configuration options. + * Result of parsing is stored under 'out_parsed_flags' pointer. + * + * Returns 0 in case of success and 1 in case of parsing error + */ +static int parse_ex_stats_flags(unsigned int *out_parsed_flags, char **exstats, + int numexstats) { unsigned int ex_stats_flags = ex_stats_none; + + assert(out_parsed_flags != NULL); + for (int i = 0; i < numexstats; i++) { for (int j = 0; ex_stats_table[j].name != NULL; j++) { if (strcasecmp(exstats[i], ex_stats_table[j].name) == 0) { @@ -946,10 +1096,13 @@ static unsigned int parse_ex_stats_flags(char **exstats, int numexstats) { if (ex_stats_table[j + 1].name == NULL) { ERROR(PLUGIN_NAME " plugin: Unmatched ExtraStats option: %s", exstats[i]); + return 1; } } } - return ex_stats_flags; + + *out_parsed_flags = ex_stats_flags; + return 0; } static void domain_state_submit_notif(virDomainPtr dom, int state, int reason) { @@ -980,8 +1133,8 @@ static void domain_state_submit_notif(virDomainPtr dom, int state, int reason) { const char *reason_str = "N/A"; #endif - snprintf(msg, sizeof(msg), "Domain state: %s. Reason: %s", state_str, - reason_str); + ssnprintf(msg, sizeof(msg), "Domain state: %s. Reason: %s", state_str, + reason_str); int severity; switch (state) { @@ -1008,10 +1161,7 @@ static void domain_state_submit_notif(virDomainPtr dom, int state, int reason) { 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) @@ -1019,191 +1169,261 @@ static int lv_config(const char *key, const char *value) { if (il_interface_devices == NULL) il_interface_devices = ignorelist_create(1); - if (strcasecmp(key, "Connection") == 0) { - char *tmp = strdup(value); - if (tmp == NULL) { - ERROR(PLUGIN_NAME " plugin: Connection strdup failed."); - return 1; - } - sfree(conn_string); - conn_string = tmp; - return 0; - } + if (!il_domains || !il_block_devices || !il_interface_devices) + return 1; - if (strcasecmp(key, "RefreshInterval") == 0) { - char *eptr = NULL; - interval = strtol(value, &eptr, 10); - if (eptr == NULL || *eptr != '\0') - return 1; - return 0; - } + return 0; +} - if (strcasecmp(key, "Domain") == 0) { - if (ignorelist_add(il_domains, value)) - return 1; - return 0; +/* Validates config option that may take multiple strings arguments. + * Returns 0 on success, -1 otherwise */ +static int check_config_multiple_string_entry(const oconfig_item_t *ci) { + if (ci == NULL) { + ERROR(PLUGIN_NAME " plugin: ci oconfig_item can't be NULL"); + return -1; } - if (strcasecmp(key, "BlockDevice") == 0) { - if (ignorelist_add(il_block_devices, value)) - return 1; - return 0; + + if (ci->values_num < 1) { + ERROR(PLUGIN_NAME + " plugin: the '%s' option requires at least one string argument", + ci->key); + return -1; } - if (strcasecmp(key, "BlockDeviceFormat") == 0) { - if (strcasecmp(value, "target") == 0) - blockdevice_format = target; - else if (strcasecmp(value, "source") == 0) - blockdevice_format = source; - else { - ERROR(PLUGIN_NAME " plugin: unknown BlockDeviceFormat: %s", value); + for (int i = 0; i < ci->values_num; ++i) { + if (ci->values[i].type != OCONFIG_TYPE_STRING) { + ERROR(PLUGIN_NAME + " plugin: one of the '%s' options is not a valid string", + ci->key); return -1; } - return 0; - } - if (strcasecmp(key, "BlockDeviceFormatBasename") == 0) { - blockdevice_format_basename = IS_TRUE(value) ? true : false; - return 0; - } - if (strcasecmp(key, "InterfaceDevice") == 0) { - if (ignorelist_add(il_interface_devices, value)) - return 1; - return 0; } - if (strcasecmp(key, "IgnoreSelected") == 0) { - if (IS_TRUE(value)) { - ignorelist_set_invert(il_domains, 0); - ignorelist_set_invert(il_block_devices, 0); - ignorelist_set_invert(il_interface_devices, 0); - } else { - ignorelist_set_invert(il_domains, 1); - ignorelist_set_invert(il_block_devices, 1); - ignorelist_set_invert(il_interface_devices, 1); - } - return 0; + return 0; +} + +static int lv_config(oconfig_item_t *ci) { + if (lv_init_ignorelists() != 0) { + ERROR(PLUGIN_NAME " plugin: lv_init_ignorelist failed."); + return -1; } - if (strcasecmp(key, "HostnameFormat") == 0) { - char *value_copy = strdup(value); - if (value_copy == NULL) { - ERROR(PLUGIN_NAME " plugin: strdup failed."); - return -1; - } + for (int i = 0; i < ci->children_num; ++i) { + oconfig_item_t *c = ci->children + i; - 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"); - return -1; - } + if (strcasecmp(c->key, "Connection") == 0) { + if (cf_util_get_string(c, &conn_string) != 0 || conn_string == NULL) + return -1; - for (int i = 0; i < n; ++i) { - if (strcasecmp(fields[i], "hostname") == 0) - hostname_format[i] = hf_hostname; - else if (strcasecmp(fields[i], "name") == 0) - hostname_format[i] = hf_name; - else if (strcasecmp(fields[i], "uuid") == 0) - hostname_format[i] = hf_uuid; + continue; + } else if (strcasecmp(c->key, "RefreshInterval") == 0) { + if (cf_util_get_int(c, &interval) != 0) + return -1; + + continue; + } else if (strcasecmp(c->key, "Domain") == 0) { + char *domain_name = NULL; + if (cf_util_get_string(c, &domain_name) != 0) + return -1; + + if (ignorelist_add(il_domains, domain_name)) { + ERROR(PLUGIN_NAME " plugin: Adding '%s' to domain-ignorelist failed", + domain_name); + sfree(domain_name); + return -1; + } + + sfree(domain_name); + continue; + } else if (strcasecmp(c->key, "BlockDevice") == 0) { + char *device_name = NULL; + if (cf_util_get_string(c, &device_name) != 0) + return -1; + + if (ignorelist_add(il_block_devices, device_name) != 0) { + ERROR(PLUGIN_NAME + " plugin: Adding '%s' to block-device-ignorelist failed", + device_name); + sfree(device_name); + return -1; + } + + sfree(device_name); + continue; + } else if (strcasecmp(c->key, "BlockDeviceFormat") == 0) { + char *device_format = NULL; + if (cf_util_get_string(c, &device_format) != 0) + return -1; + + if (strcasecmp(device_format, "target") == 0) + blockdevice_format = target; + else if (strcasecmp(device_format, "source") == 0) + blockdevice_format = source; else { - ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", - fields[i]); - sfree(value_copy); + ERROR(PLUGIN_NAME " plugin: unknown BlockDeviceFormat: %s", + device_format); + sfree(device_format); return -1; } - } - sfree(value_copy); - for (int i = n; i < HF_MAX_FIELDS; ++i) - hostname_format[i] = hf_none; + sfree(device_format); + continue; + } else if (strcasecmp(c->key, "BlockDeviceFormatBasename") == 0) { + if (cf_util_get_boolean(c, &blockdevice_format_basename) != 0) + return -1; - return 0; - } + continue; + } else if (strcasecmp(c->key, "InterfaceDevice") == 0) { + char *interface_name = NULL; + if (cf_util_get_string(c, &interface_name) != 0) + return -1; - if (strcasecmp(key, "PluginInstanceFormat") == 0) { - char *value_copy = strdup(value); - if (value_copy == NULL) { - ERROR(PLUGIN_NAME " plugin: strdup failed."); - return -1; - } + if (ignorelist_add(il_interface_devices, interface_name)) { + ERROR(PLUGIN_NAME " plugin: Adding '%s' to interface-ignorelist failed", + interface_name); + sfree(interface_name); + return -1; + } - 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"); - return -1; - } + sfree(interface_name); + continue; + } else if (strcasecmp(c->key, "IgnoreSelected") == 0) { + bool ignore_selected = false; + if (cf_util_get_boolean(c, &ignore_selected) != 0) + return -1; - for (int i = 0; i < n; ++i) { - if (strcasecmp(fields[i], "none") == 0) { - plugin_instance_format[i] = plginst_none; - break; - } else if (strcasecmp(fields[i], "name") == 0) - plugin_instance_format[i] = plginst_name; - else if (strcasecmp(fields[i], "uuid") == 0) - plugin_instance_format[i] = plginst_uuid; - else { - ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s", - fields[i]); - sfree(value_copy); + if (ignore_selected) { + ignorelist_set_invert(il_domains, 0); + ignorelist_set_invert(il_block_devices, 0); + ignorelist_set_invert(il_interface_devices, 0); + } else { + ignorelist_set_invert(il_domains, 1); + ignorelist_set_invert(il_block_devices, 1); + ignorelist_set_invert(il_interface_devices, 1); + } + + continue; + } else if (strcasecmp(c->key, "HostnameMetadataNS") == 0) { + if (cf_util_get_string(c, &hm_ns) != 0) + return -1; + + continue; + } else if (strcasecmp(c->key, "HostnameMetadataXPath") == 0) { + if (cf_util_get_string(c, &hm_xpath) != 0) + return -1; + + continue; + } else if (strcasecmp(c->key, "HostnameFormat") == 0) { + /* this option can take multiple strings arguments in one config line*/ + if (check_config_multiple_string_entry(c) != 0) { + ERROR(PLUGIN_NAME " plugin: Could not get 'HostnameFormat' parameter"); return -1; } - } - sfree(value_copy); - for (int i = n; i < PLGINST_MAX_FIELDS; ++i) - plugin_instance_format[i] = plginst_none; + const int params_num = c->values_num; + for (int i = 0; i < params_num; ++i) { + const char *param_name = c->values[i].value.string; + if (strcasecmp(param_name, "hostname") == 0) + hostname_format[i] = hf_hostname; + else if (strcasecmp(param_name, "name") == 0) + hostname_format[i] = hf_name; + else if (strcasecmp(param_name, "uuid") == 0) + hostname_format[i] = hf_uuid; + else if (strcasecmp(param_name, "metadata") == 0) + hostname_format[i] = hf_metadata; + else { + ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s", + param_name); + return -1; + } + } - return 0; - } + for (int i = params_num; i < HF_MAX_FIELDS; ++i) + hostname_format[i] = hf_none; - if (strcasecmp(key, "InterfaceFormat") == 0) { - if (strcasecmp(value, "name") == 0) - interface_format = if_name; - else if (strcasecmp(value, "address") == 0) - interface_format = if_address; - else if (strcasecmp(value, "number") == 0) - interface_format = if_number; - else { - ERROR(PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", value); - return -1; - } - return 0; - } + continue; + } else if (strcasecmp(c->key, "PluginInstanceFormat") == 0) { + /* this option can handle list of string parameters in one line*/ + if (check_config_multiple_string_entry(c) != 0) { + ERROR(PLUGIN_NAME + " plugin: Could not get 'PluginInstanceFormat' parameter"); + return -1; + } - if (strcasecmp(key, "Instances") == 0) { - char *eptr = NULL; - double val = strtod(value, &eptr); + const int params_num = c->values_num; + for (int i = 0; i < params_num; ++i) { + const char *param_name = c->values[i].value.string; + if (strcasecmp(param_name, "none") == 0) { + plugin_instance_format[i] = plginst_none; + break; + } else if (strcasecmp(param_name, "name") == 0) + plugin_instance_format[i] = plginst_name; + else if (strcasecmp(param_name, "uuid") == 0) + plugin_instance_format[i] = plginst_uuid; + else if (strcasecmp(param_name, "metadata") == 0) + plugin_instance_format[i] = plginst_metadata; + else { + ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s", + param_name); + + return -1; + } + } - if (*eptr != '\0') { - ERROR(PLUGIN_NAME " plugin: Invalid value for Instances = '%s'", value); - return 1; - } - if (val <= 0) { - ERROR(PLUGIN_NAME " plugin: Instances <= 0 makes no sense."); - return 1; - } - if (val > NR_INSTANCES_MAX) { - ERROR(PLUGIN_NAME " plugin: Instances=%f > NR_INSTANCES_MAX=%i" - " use a lower setting or recompile the plugin.", - val, NR_INSTANCES_MAX); - return 1; - } + for (int i = params_num; i < PLGINST_MAX_FIELDS; ++i) + plugin_instance_format[i] = plginst_none; - nr_instances = (int)val; - DEBUG(PLUGIN_NAME " plugin: configured %i instances", nr_instances); - return 0; - } + continue; + } else if (strcasecmp(c->key, "InterfaceFormat") == 0) { + char *format = NULL; + if (cf_util_get_string(c, &format) != 0) + return -1; + + if (strcasecmp(format, "name") == 0) + interface_format = if_name; + else if (strcasecmp(format, "address") == 0) + interface_format = if_address; + else if (strcasecmp(format, "number") == 0) + interface_format = if_number; + else { + ERROR(PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", format); + sfree(format); + return -1; + } + + sfree(format); + continue; + } else if (strcasecmp(c->key, "Instances") == 0) { + if (cf_util_get_int(c, &nr_instances) != 0) + return -1; + + if (nr_instances <= 0) { + ERROR(PLUGIN_NAME " plugin: Instances <= 0 makes no sense."); + return -1; + } + if (nr_instances > NR_INSTANCES_MAX) { + ERROR(PLUGIN_NAME " plugin: Instances=%i > NR_INSTANCES_MAX=%i" + " use a lower setting or recompile the plugin.", + nr_instances, NR_INSTANCES_MAX); + return -1; + } + + DEBUG(PLUGIN_NAME " plugin: configured %i instances", nr_instances); + continue; + } else if (strcasecmp(c->key, "ExtraStats") == 0) { + char *ex_str = NULL; + + if (cf_util_get_string(c, &ex_str) != 0) + return -1; - if (strcasecmp(key, "ExtraStats") == 0) { - char *localvalue = strdup(value); - if (localvalue != NULL) { char *exstats[EX_STATS_MAX_FIELDS]; - int numexstats = - strsplit(localvalue, exstats, STATIC_ARRAY_SIZE(exstats)); - extra_stats = parse_ex_stats_flags(exstats, numexstats); - sfree(localvalue); + int numexstats = strsplit(ex_str, exstats, STATIC_ARRAY_SIZE(exstats)); + int status = parse_ex_stats_flags(&extra_stats, exstats, numexstats); + sfree(ex_str); + if (status != 0) { + ERROR(PLUGIN_NAME " plugin: parsing 'ExtraStats' option failed"); + return status; + } #ifdef HAVE_JOB_STATS if ((extra_stats & ex_stats_job_stats_completed) && @@ -1211,23 +1431,44 @@ static int lv_config(const char *key, const char *value) { ERROR(PLUGIN_NAME " plugin: Invalid job stats configuration. Only one " "type of job statistics can be collected at the same " "time"); - return 1; + return -1; } #endif - } - } - if (strcasecmp(key, "PersistentNotification") == 0) { - persistent_notification = IS_TRUE(value); - return 0; + /* ExtraStats parsed successfully */ + continue; + } else if (strcasecmp(c->key, "PersistentNotification") == 0) { + if (cf_util_get_boolean(c, &persistent_notification) != 0) + return -1; + + continue; + } else if (strcasecmp(c->key, "ReportBlockDevices") == 0) { + if (cf_util_get_boolean(c, &report_block_devices) != 0) + return -1; + + continue; + } else if (strcasecmp(c->key, "ReportNetworkInterfaces") == 0) { + if (cf_util_get_boolean(c, &report_network_interfaces) != 0) + return -1; + + continue; + } else { + /* Unrecognised option. */ + ERROR(PLUGIN_NAME " plugin: Unrecognized option: '%s'", c->key); + return -1; + } } - /* Unrecognised option. */ - return -1; + return 0; } static int lv_connect(void) { if (conn == NULL) { + /* event implementation must be registered before connection is opened */ + if (!persistent_notification) + if (register_event_impl() != 0) + return -1; + /* `conn_string == NULL' is acceptable */ #ifdef HAVE_FS_INFO /* virDomainGetFSInfo requires full read-write access connection */ @@ -1245,8 +1486,17 @@ static int lv_connect(void) { int status = virNodeGetInfo(conn, &nodeinfo); if (status != 0) { ERROR(PLUGIN_NAME " plugin: virNodeGetInfo failed"); + virConnectClose(conn); + conn = NULL; return -1; } + + if (!persistent_notification) + if (start_event_loop(¬if_thread) != 0) { + virConnectClose(conn); + conn = NULL; + return -1; + } } c_release(LOG_NOTICE, &conn_complain, PLUGIN_NAME " plugin: Connection established."); @@ -1260,8 +1510,8 @@ static void lv_disconnect(void) { WARNING(PLUGIN_NAME " plugin: closed connection to libvirt"); } -static int lv_domain_block_info(virDomainPtr dom, const char *path, - struct lv_block_info *binfo) { +static int lv_domain_block_stats(virDomainPtr dom, const char *path, + struct lv_block_stats *bstats) { #ifdef HAVE_BLOCK_STATS_FLAGS int nparams = 0; if (virDomainBlockStatsFlags(dom, path, NULL, &nparams, 0) < 0 || @@ -1270,10 +1520,10 @@ 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); + ERROR(PLUGIN_NAME " plugin: alloc(%i) for block=%s parameters failed.", + nparams, path); return -1; } @@ -1281,14 +1531,14 @@ static int lv_domain_block_info(virDomainPtr dom, const char *path, if (virDomainBlockStatsFlags(dom, path, params, &nparams, 0) < 0) { VIRT_ERROR(conn, "getting the disk params values"); } else { - rc = get_block_info(binfo, params, nparams); + rc = get_block_stats(bstats, params, nparams); } virTypedParamsClear(params, nparams); sfree(params); return rc; #else - return virDomainBlockStats(dom, path, &(binfo->bi), sizeof(binfo->bi)); + return virDomainBlockStats(dom, path, &(bstats->bi), sizeof(bstats->bi)); #endif /* HAVE_BLOCK_STATS_FLAGS */ } @@ -1313,8 +1563,17 @@ static int get_perf_events(virDomainPtr domain) { int status = virDomainListGetStats(domain_array, VIR_DOMAIN_STATS_PERF, &stats, 0); if (status == -1) { - ERROR("virt plugin: virDomainListGetStats failed with status %i.", status); - return status; + ERROR(PLUGIN_NAME " plugin: virDomainListGetStats failed with status %i.", + status); + + virErrorPtr err = virGetLastError(); + if (err->code == VIR_ERR_NO_SUPPORT) { + ERROR(PLUGIN_NAME + " plugin: Disabled unsupported ExtraStats selector: perf"); + extra_stats &= ~(ex_stats_perf); + } + + return -1; } for (int i = 0; i < status; ++i) @@ -1331,26 +1590,32 @@ static void vcpu_pin_submit(virDomainPtr dom, int max_cpus, int vcpu, char type_instance[DATA_MAX_NAME_LEN]; 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); + ssnprintf(type_instance, sizeof(type_instance), "vcpu_%d-cpu_%d", vcpu, + cpu); submit(dom, "cpu_affinity", type_instance, &(value_t){.gauge = is_set}, 1); } } 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; } - unsigned char *cpumaps = calloc(nr_virt_cpu, cpu_map_len); - if (cpumaps == NULL) { - ERROR(PLUGIN_NAME " plugin: calloc failed."); - sfree(vinfo); - return -1; + int cpu_map_len = 0; + unsigned char *cpumaps = NULL; + if (extra_stats & ex_stats_vcpupin) { + cpu_map_len = VIR_CPU_MAPLEN(max_cpus); + cpumaps = calloc(nr_virt_cpu, cpu_map_len); + + if (cpumaps == NULL) { + ERROR(PLUGIN_NAME " plugin: calloc failed."); + sfree(vinfo); + return -1; + } } int status = @@ -1358,13 +1623,26 @@ static int get_vcpu_stats(virDomainPtr domain, unsigned short nr_virt_cpu) { if (status < 0) { ERROR(PLUGIN_NAME " plugin: virDomainGetVcpus failed with status %i.", status); + + virErrorPtr err = virGetLastError(); + if (err->code == VIR_ERR_NO_SUPPORT) { + if (extra_stats & ex_stats_vcpu) + ERROR(PLUGIN_NAME + " plugin: Disabled unsupported ExtraStats selector: vcpu"); + if (extra_stats & ex_stats_vcpupin) + ERROR(PLUGIN_NAME + " plugin: Disabled unsupported ExtraStats selector: vcpupin"); + extra_stats &= ~(ex_stats_vcpu | ex_stats_vcpupin); + } + sfree(cpumaps); sfree(vinfo); - return status; + return -1; } for (int i = 0; i < nr_virt_cpu; ++i) { - vcpu_submit(vinfo[i].cpuTime, domain, vinfo[i].number, "virt_vcpu"); + if (extra_stats & ex_stats_vcpu) + vcpu_submit(vinfo[i].cpuTime, domain, vinfo[i].number, "virt_vcpu"); if (extra_stats & ex_stats_vcpupin) vcpu_pin_submit(domain, max_cpus, i, cpumaps, cpu_map_len); } @@ -1379,10 +1657,18 @@ static int get_pcpu_stats(virDomainPtr dom) { int nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0); if (nparams < 0) { VIRT_ERROR(conn, "getting the CPU params count"); + + virErrorPtr err = virGetLastError(); + if (err->code == VIR_ERR_NO_SUPPORT) { + ERROR(PLUGIN_NAME + " plugin: Disabled unsupported ExtraStats selector: pcpu"); + extra_stats &= ~(ex_stats_pcpu); + } + 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; @@ -1418,16 +1704,7 @@ 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) { +static int submit_domain_state(virDomainPtr domain) { int domain_state = 0; int domain_reason = 0; @@ -1438,9 +1715,14 @@ static int get_domain_state(virDomainPtr domain) { return status; } - domain_state_submit(domain, domain_state, domain_reason); + value_t values[] = { + {.gauge = (gauge_t)domain_state}, + {.gauge = (gauge_t)domain_reason}, + }; - return status; + submit(domain, "domain_state", NULL, values, STATIC_ARRAY_SIZE(values)); + + return 0; } #ifdef HAVE_LIST_ALL_DOMAINS @@ -1455,8 +1737,7 @@ static int get_domain_state_notify(virDomainPtr domain) { return status; } - if (persistent_notification) - domain_state_submit_notif(domain, domain_state, domain_reason); + domain_state_submit_notif(domain, domain_state, domain_reason); return status; } @@ -1465,23 +1746,66 @@ static int get_domain_state_notify(virDomainPtr domain) { 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(PLUGIN_NAME " plugin: calloc failed."); return -1; } int mem_stats = virDomainMemoryStats(domain, minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0); if (mem_stats < 0) { - ERROR("virt plugin: virDomainMemoryStats failed with mem_stats %i.", + ERROR(PLUGIN_NAME " plugin: virDomainMemoryStats failed with mem_stats %i.", mem_stats); sfree(minfo); - return mem_stats; + + virErrorPtr err = virGetLastError(); + if (err->code == VIR_ERR_NO_SUPPORT) { + ERROR(PLUGIN_NAME + " plugin: Disabled unsupported ExtraStats selector: memory"); + extra_stats &= ~(ex_stats_memory); + } + + return -1; + } + + derive_t swap_in = -1; + derive_t swap_out = -1; + derive_t min_flt = -1; + derive_t maj_flt = -1; + + for (int i = 0; i < mem_stats; i++) { + if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_IN) + swap_in = minfo[i].val; + else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_OUT) + swap_out = minfo[i].val; + else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT) + min_flt = minfo[i].val; + else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT) + maj_flt = minfo[i].val; +#ifdef LIBVIR_CHECK_VERSION +#if LIBVIR_CHECK_VERSION(2, 1, 0) + else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_LAST_UPDATE) + /* Skip 'last_update' reporting as that is not memory but timestamp */ + continue; +#endif +#endif + else + memory_stats_submit((gauge_t)minfo[i].val * 1024, domain, minfo[i].tag); } - for (int i = 0; i < mem_stats; i++) - memory_stats_submit((gauge_t)minfo[i].val * 1024, domain, minfo[i].tag); + if (swap_in > 0 || swap_out > 0) { + submit(domain, "swap_io", "in", &(value_t){.gauge = swap_in}, 1); + submit(domain, "swap_io", "out", &(value_t){.gauge = swap_out}, 1); + } + + if (min_flt > 0 || maj_flt > 0) { + value_t values[] = { + {.gauge = (gauge_t)min_flt}, + {.gauge = (gauge_t)maj_flt}, + }; + submit(domain, "ps_pagefaults", NULL, values, STATIC_ARRAY_SIZE(values)); + } sfree(minfo); return 0; @@ -1500,6 +1824,15 @@ static int get_disk_err(virDomainPtr domain) { if (disk_err_count == -1) { ERROR(PLUGIN_NAME " plugin: failed to get preferred size of disk errors array"); + + virErrorPtr err = virGetLastError(); + + if (err->code == VIR_ERR_NO_SUPPORT) { + ERROR(PLUGIN_NAME + " plugin: Disabled unsupported ExtraStats selector: disk_err"); + extra_stats &= ~(ex_stats_disk_err); + } + return -1; } @@ -1527,22 +1860,57 @@ static int get_disk_err(virDomainPtr domain) { } #endif /* HAVE_DISK_ERR */ -static int get_block_stats(struct block_device *block_dev) { - +static int get_block_device_stats(struct block_device *block_dev) { if (!block_dev) { ERROR(PLUGIN_NAME " plugin: get_block_stats NULL pointer"); return -1; } - struct lv_block_info binfo; + virDomainBlockInfo binfo; init_block_info(&binfo); - if (lv_domain_block_info(block_dev->dom, block_dev->path, &binfo) < 0) { - ERROR(PLUGIN_NAME " plugin: lv_domain_block_info failed"); + /* Fetching block info stats only if needed*/ + if (extra_stats & (ex_stats_disk_allocation | ex_stats_disk_capacity | + ex_stats_disk_physical)) { + /* Block info statistics can be only fetched from devices with 'source' + * defined */ + if (block_dev->has_source) { + if (virDomainGetBlockInfo(block_dev->dom, block_dev->path, &binfo, 0) < + 0) { + ERROR(PLUGIN_NAME " plugin: virDomainGetBlockInfo failed for path: %s", + block_dev->path); + + virErrorPtr err = virGetLastError(); + if (err->code == VIR_ERR_NO_SUPPORT) { + + if (extra_stats & ex_stats_disk_allocation) + ERROR(PLUGIN_NAME " plugin: Disabled unsupported ExtraStats " + "selector: disk_allocation"); + if (extra_stats & ex_stats_disk_capacity) + ERROR(PLUGIN_NAME " plugin: Disabled unsupported ExtraStats " + "selector: disk_capacity"); + if (extra_stats & ex_stats_disk_physical) + ERROR(PLUGIN_NAME " plugin: Disabled unsupported ExtraStats " + "selector: disk_physical"); + + extra_stats &= ~(ex_stats_disk_allocation | ex_stats_disk_capacity | + ex_stats_disk_physical); + } + + return -1; + } + } + } + + struct lv_block_stats bstats; + init_block_stats(&bstats); + + if (lv_domain_block_stats(block_dev->dom, block_dev->path, &bstats) < 0) { + ERROR(PLUGIN_NAME " plugin: lv_domain_block_stats failed"); return -1; } - disk_submit(&binfo, block_dev->dom, block_dev->path); + disk_block_stats_submit(&bstats, block_dev->dom, block_dev->path, &binfo); return 0; } @@ -1615,7 +1983,15 @@ static int get_fs_info(virDomainPtr domain) { if (mount_points_cnt == -1) { ERROR(PLUGIN_NAME " plugin: virDomainGetFSInfo failed: %d", mount_points_cnt); - return mount_points_cnt; + + virErrorPtr err = virGetLastError(); + if (err->code == VIR_ERR_NO_SUPPORT) { + ERROR(PLUGIN_NAME + " plugin: Disabled unsupported ExtraStats selector: fs_info"); + extra_stats &= ~(ex_stats_fs_info); + } + + return -1; } for (int i = 0; i < mount_points_cnt; ++i) { @@ -1662,7 +2038,6 @@ static void job_stats_submit(virDomainPtr domain, virTypedParameterPtr param) { } static int get_job_stats(virDomainPtr domain) { - int ret = 0; int job_type = 0; int nparams = 0; virTypedParameterPtr params = NULL; @@ -1670,10 +2045,24 @@ static int get_job_stats(virDomainPtr domain) { ? VIR_DOMAIN_JOB_STATS_COMPLETED : 0; - ret = virDomainGetJobStats(domain, &job_type, ¶ms, &nparams, flags); + int ret = virDomainGetJobStats(domain, &job_type, ¶ms, &nparams, flags); if (ret != 0) { ERROR(PLUGIN_NAME " plugin: virDomainGetJobStats failed: %d", ret); - return ret; + + virErrorPtr err = virGetLastError(); + // VIR_ERR_INVALID_ARG returned when VIR_DOMAIN_JOB_STATS_COMPLETED flag is + // not supported by driver + if (err->code == VIR_ERR_NO_SUPPORT || err->code == VIR_ERR_INVALID_ARG) { + if (extra_stats & ex_stats_job_stats_completed) + ERROR(PLUGIN_NAME " plugin: Disabled unsupported ExtraStats selector: " + "job_stats_completed"); + if (extra_stats & ex_stats_job_stats_background) + ERROR(PLUGIN_NAME " plugin: Disabled unsupported ExtraStats selector: " + "job_stats_background"); + extra_stats &= + ~(ex_stats_job_stats_completed | ex_stats_job_stats_background); + } + return -1; } DEBUG(PLUGIN_NAME " plugin: job_type=%d nparams=%d", job_type, nparams); @@ -1685,7 +2074,7 @@ static int get_job_stats(virDomainPtr domain) { } virTypedParamsFree(params, nparams); - return ret; + return 0; } #endif /* HAVE_JOB_STATS */ @@ -1709,7 +2098,7 @@ static int get_domain_metrics(domain_t *domain) { * however it doesn't provide a reason for entering particular state. * We need to get it from virDomainGetState. */ - GET_STATS(get_domain_state, "domain reason", domain->ptr); + GET_STATS(submit_domain_state, "domain reason", domain->ptr); #endif } @@ -1726,8 +2115,10 @@ static int get_domain_metrics(domain_t *domain) { memory_submit(domain->ptr, (gauge_t)info.memory * 1024); - GET_STATS(get_vcpu_stats, "vcpu stats", domain->ptr, info.nrVirtCpu); - GET_STATS(get_memory_stats, "memory stats", domain->ptr); + if (extra_stats & (ex_stats_vcpu | ex_stats_vcpupin)) + GET_STATS(get_vcpu_stats, "vcpu stats", domain->ptr, info.nrVirtCpu); + if (extra_stats & ex_stats_memory) + GET_STATS(get_memory_stats, "memory stats", domain->ptr); #ifdef HAVE_PERF_STATS if (extra_stats & ex_stats_perf) @@ -1814,6 +2205,9 @@ static int domain_lifecycle_event_cb(__attribute__((unused)) virConnectPtr con_, return 0; } +static void virt_eventloop_timeout_cb(int timer ATTRIBUTE_UNUSED, + void *timer_info) {} + static int register_event_impl(void) { if (virEventRegisterDefaultImpl() < 0) { virErrorPtr err = virGetLastError(); @@ -1823,6 +2217,14 @@ static int register_event_impl(void) { return -1; } + if (virEventAddTimeout(CDTIME_T_TO_MS(plugin_get_interval()), + virt_eventloop_timeout_cb, NULL, NULL) < 0) { + virErrorPtr err = virGetLastError(); + ERROR(PLUGIN_NAME " plugin: virEventAddTimeout failed: %s", + err && err->message ? err->message : "Unknown error"); + return -1; + } + return 0; } @@ -1861,10 +2263,9 @@ static void *event_loop_worker(void *arg) { } 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); + + int 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; @@ -1893,11 +2294,15 @@ static int start_event_loop(virt_notif_thread_t *thread_data) { return -1; } + DEBUG(PLUGIN_NAME " plugin: starting event loop"); + 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"); + virt_notif_thread_set_active(thread_data, 0); virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id); + thread_data->domain_event_cb_id = -1; return -1; } @@ -1906,13 +2311,21 @@ static int start_event_loop(virt_notif_thread_t *thread_data) { /* 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"); + DEBUG(PLUGIN_NAME " plugin: stopping event loop"); + + /* Stopping loop */ + if (virt_notif_thread_is_active(thread_data)) { + virt_notif_thread_set_active(thread_data, 0); + if (pthread_join(notif_thread.event_loop_tid, NULL) != 0) + ERROR(PLUGIN_NAME " plugin: stopping notification thread failed"); + } + + /* ... and de-registering event handler */ + if (conn != NULL && thread_data->domain_event_cb_id != -1) { + virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id); + thread_data->domain_event_cb_id = -1; + } } static int persistent_domains_state_notification(void) { @@ -1988,33 +2401,41 @@ static int persistent_domains_state_notification(void) { } static int lv_read(user_data_t *ud) { - time_t t; - struct lv_read_instance *inst = NULL; - struct lv_read_state *state = NULL; - if (ud->data == NULL) { ERROR(PLUGIN_NAME " plugin: NULL userdata"); return -1; } - 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; + struct lv_read_instance *inst = ud->data; + struct lv_read_state *state = &inst->read_state; + if (inst->id == 0) if (lv_connect() < 0) return -1; - if (!persistent_notification && reconnect && conn != NULL) - if (start_event_loop(¬if_thread) != 0) - return -1; + /* Wait until inst#0 establish connection */ + if (conn == NULL) { + DEBUG(PLUGIN_NAME " plugin#%s: Wait until inst#0 establish connection", + inst->tag); + return 0; + } + + int ret = virConnectIsAlive(conn); + if (ret == 0) { /* Connection lost */ + if (inst->id == 0) { + c_complain(LOG_ERR, &conn_complain, + PLUGIN_NAME " plugin: Lost connection."); + + if (!persistent_notification) + stop_event_loop(¬if_thread); + + lv_disconnect(); + last_refresh = 0; + } + return -1; } + time_t t; time(&t); /* Need to refresh domain or device lists? */ @@ -2061,8 +2482,8 @@ static int lv_read(user_data_t *ud) { if (dom->active) status = get_domain_metrics(dom); #ifdef HAVE_DOM_REASON - else - status = get_domain_state(dom->ptr); + else if (extra_stats & ex_stats_domain_state) + status = submit_domain_state(dom->ptr); #endif if (status != 0) @@ -2072,7 +2493,7 @@ static int lv_read(user_data_t *ud) { /* Get block device stats for each domain. */ for (int i = 0; i < state->nr_block_devices; ++i) { - int status = get_block_stats(&state->block_devices[i]); + int status = get_block_device_stats(&state->block_devices[i]); if (status != 0) ERROR(PLUGIN_NAME " plugin: failed to get stats for block device (%s) in domain %s", @@ -2100,7 +2521,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-%" PRIsz, PLUGIN_NAME, i); + ssnprintf(inst->tag, sizeof(inst->tag), "%s-%" PRIsz, PLUGIN_NAME, i); inst->id = i; user_data_t *ud = &(lv_ud->ud); @@ -2131,21 +2552,15 @@ static int lv_init(void) { if (virInitialize() != 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) + /* Init ignorelists if there was no explicit configuration */ + if (lv_init_ignorelists() != 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) + if (!persistent_notification) + if (virt_notif_thread_init(¬if_thread) != 0) return -1; - } + + lv_connect(); DEBUG(PLUGIN_NAME " plugin: starting %i instances", nr_instances); @@ -2176,8 +2591,8 @@ static int lv_domain_get_tag(xmlXPathContextPtr xpath_ctx, const char *dom_name, goto done; } - snprintf(xpath_str, sizeof(xpath_str), "/domain/metadata/%s:%s/text()", - METADATA_VM_PARTITION_PREFIX, METADATA_VM_PARTITION_ELEMENT); + ssnprintf(xpath_str, sizeof(xpath_str), "/domain/metadata/%s:%s/text()", + METADATA_VM_PARTITION_PREFIX, METADATA_VM_PARTITION_ELEMENT); xpath_obj = xmlXPathEvalExpression((xmlChar *)xpath_str, xpath_ctx); if (xpath_obj == NULL) { ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) failed on domain %s", @@ -2250,6 +2665,174 @@ static int lv_instance_include_domain(struct lv_read_instance *inst, return 0; } +static void lv_add_block_devices(struct lv_read_state *state, virDomainPtr dom, + const char *domname, + xmlXPathContextPtr xpath_ctx) { + xmlXPathObjectPtr xpath_obj = + xmlXPathEval((const xmlChar *)"/domain/devices/disk", xpath_ctx); + + if (xpath_obj == NULL) { + DEBUG(PLUGIN_NAME " plugin: no disk xpath-object found for domain %s", + domname); + return; + } + + if (xpath_obj->type != XPATH_NODESET || xpath_obj->nodesetval == NULL) { + DEBUG(PLUGIN_NAME " plugin: no disk node found for domain %s", domname); + goto cleanup; + } + + xmlNodeSetPtr xml_block_devices = xpath_obj->nodesetval; + for (int i = 0; i < xml_block_devices->nodeNr; ++i) { + xmlNodePtr xml_device = xpath_obj->nodesetval->nodeTab[i]; + char *path_str = NULL; + char *source_str = NULL; + + if (!xml_device) + continue; + + /* Fetching path and source for block device */ + for (xmlNodePtr child = xml_device->children; child; child = child->next) { + if (child->type != XML_ELEMENT_NODE) + continue; + + /* we are interested only in either "target" or "source" elements */ + if (xmlStrEqual(child->name, (const xmlChar *)"target")) + path_str = (char *)xmlGetProp(child, (const xmlChar *)"dev"); + else if (xmlStrEqual(child->name, (const xmlChar *)"source")) { + /* name of the source is located in "dev" or "file" element (it depends + * on type of source). Trying "dev" at first*/ + source_str = (char *)xmlGetProp(child, (const xmlChar *)"dev"); + if (!source_str) + source_str = (char *)xmlGetProp(child, (const xmlChar *)"file"); + } + /* ignoring any other element*/ + } + + /* source_str will be interpreted as a device path if blockdevice_format + * param is set to 'source'. */ + const char *device_path = + (blockdevice_format == source) ? source_str : path_str; + + if (!device_path) { + /* no path found and we can't add block_device without it */ + WARNING(PLUGIN_NAME " plugin: could not generate device path for disk in " + "domain %s - disk device will be ignored in reports", + domname); + goto cont; + } + + if (ignore_device_match(il_block_devices, domname, device_path) == 0) { + /* we only have to store information whether 'source' exists or not */ + bool has_source = (source_str != NULL) ? true : false; + + add_block_device(state, dom, device_path, has_source); + } + + cont: + if (path_str) + xmlFree(path_str); + + if (source_str) + xmlFree(source_str); + } + +cleanup: + 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; + const int itf_number = j + 1; + + 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; + } + } + + bool device_ignored = false; + switch (interface_format) { + case if_name: + if (ignore_device_match(il_interface_devices, domname, path) != 0) + device_ignored = true; + break; + case if_address: + if (ignore_device_match(il_interface_devices, domname, address) != 0) + device_ignored = true; + break; + case if_number: { + char number_string[4]; + ssnprintf(number_string, sizeof(number_string), "%d", itf_number); + if (ignore_device_match(il_interface_devices, domname, number_string) != + 0) + device_ignored = true; + } break; + default: + ERROR(PLUGIN_NAME " plugin: Unknown interface_format option: %d", + interface_format); + } + + if (!device_ignored) + add_interface_device(state, dom, path, address, itf_number); + + if (path) + xmlFree(path); + if (address) + xmlFree(address); + } + xmlXPathFreeObject(xpath_obj); +} + +static bool is_domain_ignored(virDomainPtr dom) { + const char *domname = virDomainGetName(dom); + + if (domname == NULL) { + VIRT_ERROR(conn, "virDomainGetName failed, ignoring domain"); + return true; + } + + if (ignorelist_match(il_domains, domname) != 0) { + DEBUG(PLUGIN_NAME + " plugin: ignoring domain '%s' because of ignorelist option", + domname); + return true; + } + + return false; +} + static int refresh_lists(struct lv_read_instance *inst) { struct lv_read_state *state = &inst->read_state; int n; @@ -2275,10 +2858,8 @@ static int refresh_lists(struct lv_read_instance *inst) { VIR_CONNECT_LIST_DOMAINS_INACTIVE); n = virConnectListAllDomains(conn, &domains, VIR_CONNECT_LIST_DOMAINS_ACTIVE); #else - int *domids; - /* Get list of domains. */ - domids = calloc(n, sizeof(*domids)); + int *domids = calloc(n, sizeof(*domids)); if (domids == NULL) { ERROR(PLUGIN_NAME " plugin: calloc failed."); return -1; @@ -2301,8 +2882,9 @@ static int refresh_lists(struct lv_read_instance *inst) { #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."); + if (is_domain_ignored(domains_inactive[i]) || + add_domain(state, domains_inactive[i], 0) < 0) { + /* domain ignored or failed during adding to domains list*/ virDomainFree(domains_inactive[i]); domains_inactive[i] = NULL; continue; @@ -2311,20 +2893,11 @@ static int refresh_lists(struct lv_read_instance *inst) { /* 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; #ifdef HAVE_LIST_ALL_DOMAINS virDomainPtr dom = domains[i]; #else - virDomainPtr dom = NULL; - dom = virDomainLookupByID(conn, domids[i]); + virDomainPtr dom = virDomainLookupByID(conn, domids[i]); if (dom == NULL) { VIRT_ERROR(conn, "virDomainLookupByID"); /* Could be that the domain went away -- ignore it anyway. */ @@ -2332,8 +2905,10 @@ static int refresh_lists(struct lv_read_instance *inst) { } #endif - if (add_domain(state, dom, 1) < 0) { + if (is_domain_ignored(dom) || add_domain(state, dom, 1) < 0) { /* + * domain ignored or failed during adding to domains list + * * When domain is already tracked, then there is * no problem with memory handling (will be freed * with the rest of domains cached data) @@ -2341,18 +2916,18 @@ static int refresh_lists(struct lv_read_instance *inst) { * before adding domain to track) we have to take * care it ourselves and call virDomainFree */ - ERROR(PLUGIN_NAME " plugin: malloc failed."); virDomainFree(dom); - goto cont; + continue; } - name = virDomainGetName(dom); - if (name == NULL) { + const char *domname = virDomainGetName(dom); + if (domname == NULL) { VIRT_ERROR(conn, "virDomainGetName"); - goto cont; + continue; } - status = virDomainGetInfo(dom, &info); + virDomainInfo info; + int status = virDomainGetInfo(dom, &info); if (status != 0) { ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.", status); @@ -2360,15 +2935,15 @@ static int refresh_lists(struct lv_read_instance *inst) { } if (info.state != VIR_DOMAIN_RUNNING) { - DEBUG(PLUGIN_NAME " plugin: skipping inactive domain %s", name); + DEBUG(PLUGIN_NAME " plugin: skipping inactive domain %s", domname); continue; } - if (il_domains && ignorelist_match(il_domains, name) != 0) - goto cont; - /* Get a list of devices for this domain. */ - xml = virDomainGetXMLDesc(dom, 0); + xmlDocPtr xml_doc = NULL; + xmlXPathContextPtr xpath_ctx = NULL; + + char *xml = virDomainGetXMLDesc(dom, 0); if (!xml) { VIRT_ERROR(conn, "virDomainGetXMLDesc"); goto cont; @@ -2383,96 +2958,24 @@ static int refresh_lists(struct lv_read_instance *inst) { xpath_ctx = xmlXPathNewContext(xml_doc); - if (lv_domain_get_tag(xpath_ctx, name, tag) < 0) { + 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; } - if (!lv_instance_include_domain(inst, name, tag)) + if (!lv_instance_include_domain(inst, domname, tag)) goto cont; /* 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); - - if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET || - xpath_obj->nodesetval == NULL) - goto cont; - - for (int j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) { - xmlNodePtr node; - char *path = NULL; - - node = xpath_obj->nodesetval->nodeTab[j]; - if (!node) - continue; - path = (char *)xmlGetProp(node, (xmlChar *)"dev"); - if (!path) - continue; - - if (il_block_devices && - ignore_device_match(il_block_devices, name, path) != 0) - goto cont2; - - add_block_device(state, dom, path); - cont2: - if (path) - xmlFree(path); - } - xmlXPathFreeObject(xpath_obj); + if (report_block_devices) + lv_add_block_devices(state, dom, domname, xpath_ctx); /* 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; - - 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_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 (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); - } + 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) @@ -2512,12 +3015,13 @@ static void free_domains(struct lv_read_state *state) { static int add_domain(struct lv_read_state *state, virDomainPtr dom, bool active) { - int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1); domain_t *new_ptr = realloc(state->domains, new_size); - if (new_ptr == NULL) + if (new_ptr == NULL) { + ERROR(PLUGIN_NAME " plugin: realloc failed in add_domain()"); return -1; + } state->domains = new_ptr; state->domains[state->nr_domains].ptr = dom; @@ -2539,7 +3043,7 @@ 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) { + const char *path, bool has_source) { char *path_copy = strdup(path); if (!path_copy) @@ -2556,6 +3060,7 @@ static int add_block_device(struct lv_read_state *state, virDomainPtr dom, state->block_devices = new_ptr; state->block_devices[state->nr_block_devices].dom = dom; state->block_devices[state->nr_block_devices].path = path_copy; + state->block_devices[state->nr_block_devices].has_source = has_source; return state->nr_block_devices++; } @@ -2590,7 +3095,7 @@ static int add_interface_device(struct lv_read_state *state, virDomainPtr dom, } char number_string[21]; - snprintf(number_string, sizeof(number_string), "interface-%u", number); + ssnprintf(number_string, sizeof(number_string), "interface-%u", number); char *number_copy = strdup(number_string); if (!number_copy) { sfree(path_copy); @@ -2620,20 +3125,17 @@ static int add_interface_device(struct lv_read_state *state, virDomainPtr dom, static int ignore_device_match(ignorelist_t *il, const char *domname, const char *devpath) { - char *name; - int r; - if ((domname == NULL) || (devpath == NULL)) return 0; size_t n = strlen(domname) + strlen(devpath) + 2; - name = malloc(n); + 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); + ssnprintf(name, n, "%s:%s", domname, devpath); + int r = ignorelist_match(il, name); sfree(name); return r; } @@ -2643,8 +3145,6 @@ static int lv_shutdown(void) { lv_fini_instance(i); } - DEBUG(PLUGIN_NAME " plugin: stopping event loop"); - if (!persistent_notification) stop_event_loop(¬if_thread); @@ -2661,7 +3161,7 @@ static int lv_shutdown(void) { } void module_register(void) { - plugin_register_config(PLUGIN_NAME, lv_config, config_keys, NR_CONFIG_KEYS); + plugin_register_complex_config("virt", lv_config); plugin_register_init(PLUGIN_NAME, lv_init); plugin_register_shutdown(PLUGIN_NAME, lv_shutdown); } diff --git a/src/vmem.c b/src/vmem.c index c7229756..a4112bbb 100644 --- a/src/vmem.c +++ b/src/vmem.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if KERNEL_LINUX static const char *config_keys[] = {"Verbose"}; @@ -60,7 +60,8 @@ static void submit(const char *plugin_instance, const char *type, static void submit_two(const char *plugin_instance, const char *type, const char *type_instance, derive_t c0, derive_t c1) { value_t values[] = { - {.derive = c0}, {.derive = c1}, + {.derive = c0}, + {.derive = c1}, }; submit(plugin_instance, type, type_instance, values, diff --git a/src/vserver.c b/src/vserver.c index 3c6d58cd..424a2189 100644 --- a/src/vserver.c +++ b/src/vserver.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -57,7 +57,8 @@ static void traffic_submit(const char *plugin_instance, derive_t tx) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.derive = rx}, {.derive = tx}, + {.derive = rx}, + {.derive = tx}, }; vl.values = values; @@ -74,7 +75,9 @@ static void load_submit(const char *plugin_instance, gauge_t snum, gauge_t mnum, gauge_t lnum) { value_list_t vl = VALUE_LIST_INIT; value_t values[] = { - {.gauge = snum}, {.gauge = mnum}, {.gauge = lnum}, + {.gauge = snum}, + {.gauge = mnum}, + {.gauge = lnum}, }; vl.values = values; diff --git a/src/wireless.c b/src/wireless.c index 4208d366..d49f1d30 100644 --- a/src/wireless.c +++ b/src/wireless.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #if KERNEL_LINUX #include diff --git a/src/write_graphite.c b/src/write_graphite.c index 7624e243..dc3b2c74 100644 --- a/src/write_graphite.c +++ b/src/write_graphite.c @@ -29,7 +29,7 @@ * Based on the write_http plugin. **/ -/* write_graphite plugin configuation example +/* write_graphite plugin configuration example * * * @@ -39,17 +39,18 @@ * LogSendErrors true * Prefix "collectd" * UseTags true + * ReverseHost false * * */ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" +#include "utils/format_graphite/format_graphite.h" #include "utils_complain.h" -#include "utils_format_graphite.h" #include @@ -521,6 +522,8 @@ static int wg_config_node(oconfig_item_t *ci) { 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("ReverseHost", child->key) == 0) + cf_util_get_flag(child, &cb->format_flags, GRAPHITE_REVERSE_HOST); else if (strcasecmp("EscapeCharacter", child->key) == 0) config_set_char(&cb->escape_char, child); else { @@ -549,7 +552,8 @@ static int wg_config_node(oconfig_item_t *ci) { plugin_register_write(callback_name, wg_write, &(user_data_t){ - .data = cb, .free_func = wg_callback_free, + .data = cb, + .free_func = wg_callback_free, }); plugin_register_flush(callback_name, wg_flush, &(user_data_t){.data = cb}); diff --git a/src/write_http.c b/src/write_http.c index ad0cb5e4..7cd19c3b 100644 --- a/src/write_http.c +++ b/src/write_http.c @@ -25,10 +25,10 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_format_json.h" -#include "utils_format_kairosdb.h" +#include "utils/common/common.h" +#include "utils/format_json/format_json.h" +#include "utils/format_kairosdb/format_kairosdb.h" #include @@ -815,7 +815,8 @@ static int wh_config_node(oconfig_item_t *ci) /* {{{ */ callback_name, cb->location); user_data_t user_data = { - .data = cb, .free_func = wh_callback_free, + .data = cb, + .free_func = wh_callback_free, }; if (cb->send_metrics) { diff --git a/src/write_kafka.c b/src/write_kafka.c index 04e67b92..09bb639a 100644 --- a/src/write_kafka.c +++ b/src/write_kafka.c @@ -26,11 +26,11 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_cmd_putval.h" -#include "utils_format_graphite.h" -#include "utils_format_json.h" +#include "utils/cmds/putval.h" +#include "utils/common/common.h" +#include "utils/format_graphite/format_graphite.h" +#include "utils/format_json/format_json.h" #include "utils_random.h" #include @@ -97,7 +97,7 @@ static uint32_t kafka_hash(const char *keydata, size_t keylen) { #define KAFKA_RANDOM_KEY_BUFFER \ (char[KAFKA_RANDOM_KEY_SIZE]) { "" } static char *kafka_random_key(char buffer[static KAFKA_RANDOM_KEY_SIZE]) { - snprintf(buffer, KAFKA_RANDOM_KEY_SIZE, "%08" PRIX32, cdrand_u()); + ssnprintf(buffer, KAFKA_RANDOM_KEY_SIZE, "%08" PRIX32, cdrand_u()); return buffer; } @@ -410,14 +410,14 @@ static void kafka_config_topic(rd_kafka_conf_t *conf, rd_kafka_topic_conf_set_partitioner_cb(tctx->conf, kafka_partition); rd_kafka_topic_conf_set_opaque(tctx->conf, tctx); - snprintf(callback_name, sizeof(callback_name), "write_kafka/%s", - tctx->topic_name); + ssnprintf(callback_name, sizeof(callback_name), "write_kafka/%s", + tctx->topic_name); - status = plugin_register_write( - callback_name, kafka_write, - &(user_data_t){ - .data = tctx, .free_func = kafka_topic_context_free, - }); + status = plugin_register_write(callback_name, kafka_write, + &(user_data_t){ + .data = tctx, + .free_func = kafka_topic_context_free, + }); if (status != 0) { WARNING("write_kafka plugin: plugin_register_write (\"%s\") " "failed with status %i.", diff --git a/src/write_log.c b/src/write_log.c index 52ad6104..3e143168 100644 --- a/src/write_log.c +++ b/src/write_log.c @@ -27,11 +27,11 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" -#include "utils_format_graphite.h" -#include "utils_format_json.h" +#include "utils/format_graphite/format_graphite.h" +#include "utils/format_json/format_json.h" #include diff --git a/src/write_mongodb.c b/src/write_mongodb.c index 9cddc916..b43906e6 100644 --- a/src/write_mongodb.c +++ b/src/write_mongodb.c @@ -32,8 +32,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include @@ -370,11 +370,11 @@ static int wm_config_node(oconfig_item_t *ci) /* {{{ */ snprintf(cb_name, sizeof(cb_name), "write_mongodb/%s", node->name); - status = - plugin_register_write(cb_name, wm_write, - &(user_data_t){ - .data = node, .free_func = wm_config_free, - }); + status = plugin_register_write(cb_name, wm_write, + &(user_data_t){ + .data = node, + .free_func = wm_config_free, + }); INFO("write_mongodb plugin: registered write plugin %s %d", cb_name, status); } diff --git a/src/write_prometheus.c b/src/write_prometheus.c index 53a07095..b9040223 100644 --- a/src/write_prometheus.c +++ b/src/write_prometheus.c @@ -26,9 +26,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" #include "utils_complain.h" #include "utils_time.h" @@ -156,7 +156,8 @@ static char *format_labels(char *buffer, size_t buffer_size, #define LABEL_BUFFER_SIZE (LABEL_KEY_SIZE + LABEL_VALUE_SIZE + 4) char *labels[3] = { - (char[LABEL_BUFFER_SIZE]){0}, (char[LABEL_BUFFER_SIZE]){0}, + (char[LABEL_BUFFER_SIZE]){0}, + (char[LABEL_BUFFER_SIZE]){0}, (char[LABEL_BUFFER_SIZE]){0}, }; @@ -164,8 +165,8 @@ static char *format_labels(char *buffer, size_t buffer_size, * know that they are sane. */ for (size_t i = 0; i < m->n_label; i++) { char value[LABEL_VALUE_SIZE]; - snprintf(labels[i], LABEL_BUFFER_SIZE, "%s=\"%s\"", m->label[i]->name, - escape_label_value(value, sizeof(value), m->label[i]->value)); + ssnprintf(labels[i], LABEL_BUFFER_SIZE, "%s=\"%s\"", m->label[i]->name, + escape_label_value(value, sizeof(value), m->label[i]->value)); } strjoin(buffer, buffer_size, labels, m->n_label, ","); @@ -183,13 +184,13 @@ static void format_text(ProtobufCBuffer *buffer) { while (c_avl_iterator_next(iter, (void *)&unused_name, (void *)&fam) == 0) { char line[1024]; /* 4x DATA_MAX_NAME_LEN? */ - snprintf(line, sizeof(line), "# HELP %s %s\n", fam->name, fam->help); + ssnprintf(line, sizeof(line), "# HELP %s %s\n", fam->name, fam->help); buffer->append(buffer, strlen(line), (uint8_t *)line); - snprintf(line, sizeof(line), "# TYPE %s %s\n", fam->name, - (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__GAUGE) - ? "gauge" - : "counter"); + ssnprintf(line, sizeof(line), "# TYPE %s %s\n", fam->name, + (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__GAUGE) + ? "gauge" + : "counter"); buffer->append(buffer, strlen(line), (uint8_t *)line); for (size_t i = 0; i < fam->n_metric; i++) { @@ -199,17 +200,17 @@ static void format_text(ProtobufCBuffer *buffer) { char timestamp_ms[24] = ""; if (m->has_timestamp_ms) - snprintf(timestamp_ms, sizeof(timestamp_ms), " %" PRIi64, - m->timestamp_ms); + ssnprintf(timestamp_ms, sizeof(timestamp_ms), " %" PRIi64, + m->timestamp_ms); if (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__GAUGE) - snprintf(line, sizeof(line), "%s{%s} " GAUGE_FORMAT "%s\n", fam->name, - format_labels(labels, sizeof(labels), m), m->gauge->value, - timestamp_ms); + ssnprintf(line, sizeof(line), "%s{%s} " GAUGE_FORMAT "%s\n", fam->name, + format_labels(labels, sizeof(labels), m), m->gauge->value, + timestamp_ms); else /* if (fam->type == IO__PROMETHEUS__CLIENT__METRIC_TYPE__COUNTER) */ - snprintf(line, sizeof(line), "%s{%s} %.0f%s\n", fam->name, - format_labels(labels, sizeof(labels), m), m->counter->value, - timestamp_ms); + ssnprintf(line, sizeof(line), "%s{%s} %.0f%s\n", fam->name, + format_labels(labels, sizeof(labels), m), m->counter->value, + timestamp_ms); buffer->append(buffer, strlen(line), (uint8_t *)line); } @@ -217,8 +218,8 @@ static void format_text(ProtobufCBuffer *buffer) { c_avl_iterator_destroy(iter); char server[1024]; - snprintf(server, sizeof(server), "\n# collectd/write_prometheus %s at %s\n", - PACKAGE_VERSION, hostname_g); + ssnprintf(server, sizeof(server), "\n# collectd/write_prometheus %s at %s\n", + PACKAGE_VERSION, hostname_g); buffer->append(buffer, strlen(server), (uint8_t *)server); pthread_mutex_unlock(&metrics_lock); @@ -635,7 +636,7 @@ metric_family_create(char *name, data_set_t const *ds, value_list_t const *vl, msg->name = name; char help[1024]; - snprintf( + ssnprintf( help, sizeof(help), "write_prometheus plugin: '%s' Type: '%s', Dstype: '%s', Dsname: '%s'", vl->plugin, vl->type, DS_TYPE_TO_STRING(ds->ds[ds_index].type), @@ -722,7 +723,7 @@ metric_family_get(data_set_t const *ds, value_list_t const *vl, size_t ds_index, int status = c_avl_insert(metrics, fam->name, fam); if (status != 0) { - ERROR("write_prometheus plugin: Adding \"%s\" failed.", name); + ERROR("write_prometheus plugin: Adding \"%s\" failed.", fam->name); metric_family_destroy(fam); return NULL; } @@ -744,7 +745,7 @@ static void prom_logger(__attribute__((unused)) void *arg, char const *fmt, static int prom_open_socket(int addrfamily) { /* {{{ */ char service[NI_MAXSERV]; - snprintf(service, sizeof(service), "%hu", httpd_port); + ssnprintf(service, sizeof(service), "%hu", httpd_port); struct addrinfo *res; int status = getaddrinfo(httpd_host, service, @@ -760,7 +761,12 @@ static int prom_open_socket(int addrfamily) { int fd = -1; for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) { - fd = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC, 0); + int flags = ai->ai_socktype; +#ifdef SOCK_CLOEXEC + flags |= SOCK_CLOEXEC; +#endif + + fd = socket(ai->ai_family, flags, 0); if (fd == -1) continue; diff --git a/src/write_redis.c b/src/write_redis.c index 72cb5946..32005cd6 100644 --- a/src/write_redis.c +++ b/src/write_redis.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include @@ -71,9 +71,10 @@ static int wr_write(const data_set_t *ds, /* {{{ */ status = FORMAT_VL(ident, sizeof(ident), vl); if (status != 0) return status; - snprintf(key, sizeof(key), "%s%s", - (node->prefix != NULL) ? node->prefix : REDIS_DEFAULT_PREFIX, ident); - snprintf(time, sizeof(time), "%.9f", CDTIME_T_TO_DOUBLE(vl->time)); + ssnprintf(key, sizeof(key), "%s%s", + (node->prefix != NULL) ? node->prefix : REDIS_DEFAULT_PREFIX, + ident); + ssnprintf(time, sizeof(time), "%.9f", CDTIME_T_TO_DOUBLE(vl->time)); value_size = sizeof(value); value_ptr = &value[0]; @@ -239,13 +240,13 @@ static int wr_config_node(oconfig_item_t *ci) /* {{{ */ if (status == 0) { char cb_name[sizeof("write_redis/") + DATA_MAX_NAME_LEN]; - snprintf(cb_name, sizeof(cb_name), "write_redis/%s", node->name); + ssnprintf(cb_name, sizeof(cb_name), "write_redis/%s", node->name); - status = - plugin_register_write(cb_name, wr_write, - &(user_data_t){ - .data = node, .free_func = wr_config_free, - }); + status = plugin_register_write(cb_name, wr_write, + &(user_data_t){ + .data = node, + .free_func = wr_config_free, + }); } if (status != 0) diff --git a/src/write_riemann.c b/src/write_riemann.c index b35d10ee..201ac516 100644 --- a/src/write_riemann.c +++ b/src/write_riemann.c @@ -30,8 +30,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include "utils_complain.h" #include "write_riemann_threshold.h" @@ -291,17 +291,18 @@ wrr_value_to_event(struct riemann_host const *host, /* {{{ */ vl->type_instance); if (host->always_append_ds || (ds->ds_num > 1)) { if (host->event_service_prefix == NULL) - snprintf(service_buffer, sizeof(service_buffer), "%s/%s", &name_buffer[1], - ds->ds[index].name); + ssnprintf(service_buffer, sizeof(service_buffer), "%s/%s", + &name_buffer[1], ds->ds[index].name); else - snprintf(service_buffer, sizeof(service_buffer), "%s%s/%s", - host->event_service_prefix, &name_buffer[1], ds->ds[index].name); + ssnprintf(service_buffer, sizeof(service_buffer), "%s%s/%s", + host->event_service_prefix, &name_buffer[1], + ds->ds[index].name); } else { if (host->event_service_prefix == NULL) sstrncpy(service_buffer, &name_buffer[1], sizeof(service_buffer)); else - snprintf(service_buffer, sizeof(service_buffer), "%s%s", - host->event_service_prefix, &name_buffer[1]); + ssnprintf(service_buffer, sizeof(service_buffer), "%s%s", + host->event_service_prefix, &name_buffer[1]); } riemann_event_set( @@ -349,8 +350,8 @@ wrr_value_to_event(struct riemann_host const *host, /* {{{ */ if ((ds->ds[index].type != DS_TYPE_GAUGE) && (rates != NULL)) { char ds_type[DATA_MAX_NAME_LEN]; - snprintf(ds_type, sizeof(ds_type), "%s:rate", - DS_TYPE_TO_STRING(ds->ds[index].type)); + ssnprintf(ds_type, sizeof(ds_type), "%s:rate", + DS_TYPE_TO_STRING(ds->ds[index].type)); riemann_event_string_attribute_add(event, "ds_type", ds_type); } else { riemann_event_string_attribute_add(event, "ds_type", @@ -360,7 +361,7 @@ wrr_value_to_event(struct riemann_host const *host, /* {{{ */ { char ds_index[DATA_MAX_NAME_LEN]; - snprintf(ds_index, sizeof(ds_index), "%" PRIsz, index); + ssnprintf(ds_index, sizeof(ds_index), "%" PRIsz, index); riemann_event_string_attribute_add(event, "ds_index", ds_index); } @@ -392,6 +393,23 @@ wrr_value_to_event(struct riemann_host const *host, /* {{{ */ RIEMANN_EVENT_FIELD_NONE); } + if (vl->meta) { + char **toc; + int n = meta_data_toc(vl->meta, &toc); + + for (int i = 0; i < n; i++) { + char *key = toc[i]; + char *value; + + if (0 == meta_data_as_string(vl->meta, key, &value)) { + riemann_event_string_attribute_add(event, key, value); + free(value); + } + } + + free(toc); + } + DEBUG("write_riemann plugin: Successfully created message for metric: " "host = \"%s\", service = \"%s\"", event->host, event->service); @@ -777,8 +795,8 @@ static int wrr_config_node(oconfig_item_t *ci) /* {{{ */ return status; } - snprintf(callback_name, sizeof(callback_name), "write_riemann/%s", - host->name); + ssnprintf(callback_name, sizeof(callback_name), "write_riemann/%s", + host->name); user_data_t ud = {.data = host, .free_func = wrr_free}; diff --git a/src/write_riemann_threshold.c b/src/write_riemann_threshold.c index 9d8267dc..041ed7d5 100644 --- a/src/write_riemann_threshold.c +++ b/src/write_riemann_threshold.c @@ -27,9 +27,9 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" +#include "utils/common/common.h" #include "utils_cache.h" #include "utils_threshold.h" #include "write_riemann_threshold.h" diff --git a/src/write_sensu.c b/src/write_sensu.c index 6ea8106c..7d08fb5f 100644 --- a/src/write_sensu.c +++ b/src/write_sensu.c @@ -28,8 +28,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include #include @@ -1221,10 +1221,10 @@ static int sensu_config(oconfig_item_t *ci) /* {{{ */ continue; status = add_str_to_list(&sensu_tags_arr, tmp); + DEBUG("write_sensu plugin: Got tag: %s", tmp); sfree(tmp); if (status != 0) continue; - DEBUG("write_sensu plugin: Got tag: %s", tmp); } else { WARNING("write_sensu plugin: Ignoring unknown " "configuration option \"%s\" at top level.", diff --git a/src/write_stackdriver.c b/src/write_stackdriver.c index a1341d9d..dfa1d7c0 100644 --- a/src/write_stackdriver.c +++ b/src/write_stackdriver.c @@ -22,12 +22,12 @@ #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 "utils/common/common.h" +#include "utils/format_stackdriver/format_stackdriver.h" +#include "utils/gce/gce.h" +#include "utils/oauth/oauth.h" #include #include @@ -109,8 +109,8 @@ static char *wg_get_authorization_header(wg_callback_t *cb) { /* {{{ */ return NULL; } - status = snprintf(authorization_header, sizeof(authorization_header), - "Authorization: Bearer %s", access_token); + status = ssnprintf(authorization_header, sizeof(authorization_header), + "Authorization: Bearer %s", access_token); if ((status < 1) || ((size_t)status >= sizeof(authorization_header))) return NULL; @@ -160,9 +160,9 @@ static char *api_error_string(api_error_t *err, char *buffer, 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); + ssnprintf(buffer, buffer_size, "API error %d", err->code); } else { - snprintf(buffer, buffer_size, "API error %d: %s", err->code, err->message); + ssnprintf(buffer, buffer_size, "API error %d: %s", err->code, err->message); } return buffer; @@ -251,8 +251,8 @@ static int do_post(wg_callback_t *cb, char const *url, void const *payload, 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); + ssnprintf(url, sizeof(url), "%s/projects/%s/metricDescriptors", cb->url, + cb->project); wg_memory_t response = {0}; int status = do_post(cb, url, payload, &response); @@ -273,7 +273,8 @@ static int wg_call_metricdescriptor_create(wg_callback_t *cb, 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); + ssnprintf(url, sizeof(url), "%s/projects/%s/timeSeries", cb->url, + cb->project); wg_memory_t response = {0}; int status = do_post(cb, url, payload, &response); diff --git a/src/write_syslog.c b/src/write_syslog.c new file mode 100644 index 00000000..92c5ddeb --- /dev/null +++ b/src/write_syslog.c @@ -0,0 +1,639 @@ +/** + * collectd - src/write_syslog.c + * Copyright (C) 2012 Pierre-Yves Ritschard + * Copyright (C) 2011 Scott Sanders + * Copyright (C) 2009 Paul Sadauskas + * Copyright (C) 2009 Doug MacEachern + * Copyright (C) 2007-2012 Florian octo Forster + * Copyright (C) 2013-2014 Limelight Networks, Inc. + * Copyright (C) 2019 Shirly Radco + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Based on the write_graphite plugin. Authors: + * Florian octo Forster + * Doug MacEachern + * Paul Sadauskas + * Scott Sanders + * Pierre-Yves Ritschard + * Based on the write_tsdb plugin. Authors: + * Brett Hawn + * Kevin Bowling + * write_syslog. Authors: + * Shirly Radco + **/ + +/* write_syslog plugin configuration example + * + * + * + * Host "localhost" + * Port "44514" + * Prefix "collectd" + * MessageFormat "human" + * HostTags "["prefix1" "example1"="example1_v"] + * + * + * + */ + +#include "collectd.h" +#include "utils/common/common.h" + +#include "plugin.h" +#include "utils_cache.h" +#include "utils_random.h" + +#include + +#define WS_DEFAULT_NODE "localhost" + +#define WS_DEFAULT_SERVICE "44514" + +#define WS_DEFAULT_FORMAT "human" + +#define WS_DEFAULT_PREFIX "collectd" + +#define WS_DEFAULT_ESCAPE '.' + +/* Ethernet - (IPv6 + TCP) = 1500 - (40 + 32) = 1428 */ +#define WS_SEND_BUF_SIZE 1428 + +/* + * Private variables + */ +struct ws_callback { + struct addrinfo *ai; + cdtime_t ai_last_update; + int sock_fd; + + char *node; + char *service; + char *host_tags; + char *msg_format; + char *metrics_prefix; + bool store_rates; + bool always_append_ds; + + char send_buf[WS_SEND_BUF_SIZE]; + size_t send_buf_free; + size_t send_buf_fill; + cdtime_t send_buf_init_time; + + pthread_mutex_t send_lock; + + bool connect_failed_log_enabled; + int connect_dns_failed_attempts_remaining; + cdtime_t next_random_ttl; +}; + +static cdtime_t resolve_interval; +static cdtime_t resolve_jitter; + +/* + * Functions + */ +static void ws_reset_buffer(struct ws_callback *cb) { + memset(cb->send_buf, 0, sizeof(cb->send_buf)); + cb->send_buf_free = sizeof(cb->send_buf); + cb->send_buf_fill = 0; + cb->send_buf_init_time = cdtime(); +} + +static int ws_send_buffer(struct ws_callback *cb) { + ssize_t status = 0; + + status = swrite(cb->sock_fd, cb->send_buf, strlen(cb->send_buf)); + if (status != 0) { + ERROR("write_syslog plugin: send failed with status %zi (%s)", status, + STRERRNO); + + if (cb->sock_fd > 0) { + close(cb->sock_fd); + cb->sock_fd = -1; + } + + return -1; + } + + return 0; +} + +/* NOTE: You must hold cb->send_lock when calling this function! */ +static int ws_flush_nolock(cdtime_t timeout, struct ws_callback *cb) { + int status; + + DEBUG("write_syslog plugin: ws_flush_nolock: timeout = %.3f; " + "send_buf_fill = %" PRIsz ";", + (double)timeout, cb->send_buf_fill); + + /* timeout == 0 => flush unconditionally */ + if (timeout > 0) { + cdtime_t now; + + now = cdtime(); + if ((cb->send_buf_init_time + timeout) > now) + return 0; + } + + if (cb->send_buf_fill == 0) { + cb->send_buf_init_time = cdtime(); + return 0; + } + + status = ws_send_buffer(cb); + ws_reset_buffer(cb); + + return status; +} + +static cdtime_t new_random_ttl(void) { + if (resolve_jitter == 0) + return 0; + + return (cdtime_t)cdrand_range(0, (long)resolve_jitter); +} + +static int ws_callback_init(struct ws_callback *cb) { + int status; + cdtime_t now; + + const char *node = cb->node ? cb->node : WS_DEFAULT_NODE; + const char *service = cb->service ? cb->service : WS_DEFAULT_SERVICE; + + if (cb->sock_fd > 0) + return 0; + + now = cdtime(); + if (cb->ai) { + /* When we are here, we still have the IP in cache. + * If we have remaining attempts without calling the DNS, we update the + * last_update date so we keep the info until next time. + * If there is no more attempts, we need to flush the cache. + */ + + if ((cb->ai_last_update + resolve_interval + cb->next_random_ttl) < now) { + cb->next_random_ttl = new_random_ttl(); + if (cb->connect_dns_failed_attempts_remaining > 0) { + /* Warning : this is run under send_lock mutex. + * This is why we do not use another mutex here. + * */ + cb->ai_last_update = now; + cb->connect_dns_failed_attempts_remaining--; + } else { + freeaddrinfo(cb->ai); + cb->ai = NULL; + } + } + } + + if (cb->ai == NULL) { + if ((cb->ai_last_update + resolve_interval + cb->next_random_ttl) >= now) { + DEBUG("write_syslog plugin: too many getaddrinfo(%s, %s) failures", node, + service); + return -1; + } + cb->ai_last_update = now; + cb->next_random_ttl = new_random_ttl(); + + struct addrinfo ai_hints = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_ADDRCONFIG, + .ai_socktype = SOCK_STREAM, + }; + + status = getaddrinfo(node, service, &ai_hints, &cb->ai); + if (status != 0) { + if (cb->ai) { + freeaddrinfo(cb->ai); + cb->ai = NULL; + } + if (cb->connect_failed_log_enabled) { + ERROR("write_syslog plugin: getaddrinfo(%s, %s) failed: %s", node, + service, gai_strerror(status)); + cb->connect_failed_log_enabled = 0; + } + return -1; + } + } + + assert(cb->ai != NULL); + for (struct addrinfo *ai = cb->ai; ai != NULL; ai = ai->ai_next) { + cb->sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (cb->sock_fd < 0) + continue; + + set_sock_opts(cb->sock_fd); + + status = connect(cb->sock_fd, ai->ai_addr, ai->ai_addrlen); + if (status != 0) { + close(cb->sock_fd); + cb->sock_fd = -1; + continue; + } + + break; + } + + if (cb->sock_fd < 0) { + ERROR("write_syslog plugin: Connecting to %s:%s failed. " + "The last error was: %s", + node, service, STRERRNO); + return -1; + } + + if (cb->connect_failed_log_enabled == 0) { + INFO("write_syslog plugin: Connecting to %s:%s succeeded.", node, service); + cb->connect_failed_log_enabled = 1; + } + cb->connect_dns_failed_attempts_remaining = 1; + + ws_reset_buffer(cb); + + return 0; +} + +static void ws_callback_free(void *data) { + struct ws_callback *cb; + + if (data == NULL) + return; + + cb = data; + + pthread_mutex_lock(&cb->send_lock); + + ws_flush_nolock(0, cb); + + close(cb->sock_fd); + cb->sock_fd = -1; + + sfree(cb->node); + sfree(cb->service); + sfree(cb->host_tags); + sfree(cb->msg_format); + sfree(cb->metrics_prefix); + + pthread_mutex_unlock(&cb->send_lock); + pthread_mutex_destroy(&cb->send_lock); + + sfree(cb); +} + +static int ws_flush(cdtime_t timeout, + const char *identifier __attribute__((unused)), + user_data_t *user_data) { + struct ws_callback *cb; + int status; + + if (user_data == NULL) + return -EINVAL; + + cb = user_data->data; + + pthread_mutex_lock(&cb->send_lock); + + if (cb->sock_fd < 0) { + status = ws_callback_init(cb); + if (status != 0) { + ERROR("write_syslog plugin: ws_callback_init failed."); + pthread_mutex_unlock(&cb->send_lock); + return -1; + } + } + + status = ws_flush_nolock(timeout, cb); + pthread_mutex_unlock(&cb->send_lock); + + return status; +} + +static int ws_format_values(char *ret, size_t ret_len, int ds_num, + const data_set_t *ds, const value_list_t *vl, + bool store_rates) { + size_t offset = 0; + int status; + gauge_t *rates = NULL; + + assert(strcmp(ds->type, vl->type) == 0); + + memset(ret, 0, ret_len); + +#define BUFFER_ADD(...) \ + do { \ + status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__); \ + if (status < 1) { \ + sfree(rates); \ + return -1; \ + } else if (((size_t)status) >= (ret_len - offset)) { \ + sfree(rates); \ + return -1; \ + } else \ + offset += ((size_t)status); \ + } while (0) + + if (ds->ds[ds_num].type == DS_TYPE_GAUGE) + BUFFER_ADD(GAUGE_FORMAT, vl->values[ds_num].gauge); + else if (store_rates) { + if (rates == NULL) + rates = uc_get_rate(ds, vl); + if (rates == NULL) { + WARNING("format_values: " + "uc_get_rate failed."); + return -1; + } + BUFFER_ADD(GAUGE_FORMAT, rates[ds_num]); + } else if (ds->ds[ds_num].type == DS_TYPE_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("format_values plugin: Unknown data source type: %i", + ds->ds[ds_num].type); + sfree(rates); + return -1; + } + +#undef BUFFER_ADD + + sfree(rates); + return 0; +} + +static int ws_format_name(char *ret, int ret_len, const value_list_t *vl, + const struct ws_callback *cb, const char *ds_name) { + + if (ds_name != NULL) { + snprintf(ret, ret_len, "%s.%s", vl->type, ds_name); + } else { /* ds_name == NULL */ + snprintf(ret, ret_len, "%s", vl->type); + } + + return 0; +} + +static int ws_send_message(const char *key, const char *value, cdtime_t time, + struct ws_callback *cb, const char *plugin, + const char *plugin_instance, + const char *type_instance, const char *type, + const char *ds_name, cdtime_t interval, + const char *host) { + int status; + size_t message_len; + char message[1024]; + char rfc3339_timestamp[64]; + const char *host_tags = cb->host_tags ? cb->host_tags : ""; + const char *host_tags_json_prefix = ""; + const char *metrics_prefix = + cb->metrics_prefix ? cb->metrics_prefix : WS_DEFAULT_PREFIX; + const char *msg_format = cb->msg_format ? cb->msg_format : WS_DEFAULT_FORMAT; + int pid; + + pid = getpid(); + + rfc3339_local(rfc3339_timestamp, sizeof(rfc3339_timestamp), time); + + /* skip if value is NaN */ + if (value[0] == 'n') + return 0; + + if (strcasecmp("JSON", msg_format) == 0) { + if (cb->host_tags) { + host_tags_json_prefix = ","; + } + status = snprintf( + /* The metric key-values are are part of the syslog msg, in json + format */ + message, sizeof(message), + "<166>1 %s %s collectd %d - - {\"time\":%.0f, \"%s\":{ \"%s\":{ " + "\"%s\":%s }, " + "\"plugin\":\"%s\", \"plugin_instance\":\"%s\", " + "\"type_instance\":\"%s\"," + " \"type\":\"%s\", \"interval\":%.0f }, \"hostname\":\"%s\" %s " + "%s}\n", + rfc3339_timestamp, host, pid, CDTIME_T_TO_DOUBLE(time), metrics_prefix, + plugin, key, value, plugin, plugin_instance, type_instance, type, + CDTIME_T_TO_DOUBLE(interval), host, host_tags_json_prefix, host_tags); + } else { + status = snprintf( + /* The metric key-values are part of the syslog structrude data, + * MessageFormat = "human" */ + message, sizeof(message), + "<166>1 %s %s collectd %d - [%s value=\"%s\"" + " plugin=\"%s\" plugin_instance=\"%s\"" + " type_instance=\"%s\" type=\"%s\"" + " ds_name=\"%s\" interval=\"%.0f\"] %s %s.%s=\"%s\"\n", + rfc3339_timestamp, host, pid, metrics_prefix, value, plugin, + plugin_instance, type_instance, type, ds_name, + CDTIME_T_TO_DOUBLE(interval), host_tags, plugin, key, value); + } + if (status < 0) + return -1; + message_len = (size_t)status; + + if (message_len >= sizeof(message)) { + ERROR("write_syslog plugin: message buffer too small: " + "Need %" PRIsz " bytes.", + message_len + 1); + return -1; + } + + pthread_mutex_lock(&cb->send_lock); + + if (cb->sock_fd < 0) { + status = ws_callback_init(cb); + if (status != 0) { + ERROR("write_syslog plugin: ws_callback_init failed."); + pthread_mutex_unlock(&cb->send_lock); + return -1; + } + } + + if (message_len >= cb->send_buf_free) { + status = ws_flush_nolock(0, cb); + if (status != 0) { + pthread_mutex_unlock(&cb->send_lock); + return status; + } + } + + /* Assert that we have enough space for this message. */ + assert(message_len < cb->send_buf_free); + + /* `message_len + 1' because `message_len' does not include the + * trailing null byte. Neither does `send_buffer_fill'. */ + memcpy(cb->send_buf + cb->send_buf_fill, message, message_len + 1); + cb->send_buf_fill += message_len; + cb->send_buf_free -= message_len; + + DEBUG("write_syslog 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); + + pthread_mutex_unlock(&cb->send_lock); + + return 0; +} + +static int ws_write_messages(const data_set_t *ds, const value_list_t *vl, + struct ws_callback *cb) { + char key[10 * DATA_MAX_NAME_LEN]; + char values[512]; + + int status; + + if (0 != strcmp(ds->type, vl->type)) { + ERROR("write_syslog plugin: DS type does not match " + "value list type"); + return -1; + } + + for (size_t i = 0; i < ds->ds_num; i++) { + const char *ds_name = NULL; + + if (cb->always_append_ds || (ds->ds_num > 1)) + ds_name = ds->ds[i].name; + + /* Copy the identifier to 'key' and escape it. */ + status = ws_format_name(key, sizeof(key), vl, cb, ds_name); + if (status != 0) { + ERROR("write_syslog plugin: error with format_name"); + return status; + } + + escape_string(key, sizeof(key)); + /* Convert the values to an ASCII representation and put that into + * 'values'. */ + status = + ws_format_values(values, sizeof(values), i, ds, vl, cb->store_rates); + if (status != 0) { + ERROR("write_syslog plugin: error with " + "ws_format_values"); + return status; + } + + /* Send the message to tcp */ + status = ws_send_message(key, values, vl->time, cb, vl->plugin, + vl->plugin_instance, vl->type_instance, vl->type, + ds_name, vl->interval, vl->host); + if (status != 0) { + ERROR("write_syslog plugin: error with " + "ws_send_message"); + return status; + } + } + + return 0; +} + +static int ws_write(const data_set_t *ds, const value_list_t *vl, + user_data_t *user_data) { + struct ws_callback *cb; + int status; + + if (user_data == NULL) + return EINVAL; + + cb = user_data->data; + + status = ws_write_messages(ds, vl, cb); + + return status; +} + +static int ws_config_tsd(oconfig_item_t *ci) { + struct ws_callback *cb; + char callback_name[DATA_MAX_NAME_LEN]; + + cb = calloc(1, sizeof(*cb)); + if (cb == NULL) { + ERROR("write_syslog plugin: calloc failed."); + return -1; + } + cb->sock_fd = -1; + cb->connect_failed_log_enabled = 1; + cb->next_random_ttl = new_random_ttl(); + + pthread_mutex_init(&cb->send_lock, NULL); + + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("Host", child->key) == 0) + cf_util_get_string(child, &cb->node); + else if (strcasecmp("Port", child->key) == 0) + cf_util_get_service(child, &cb->service); + else if (strcasecmp("MessageFormat", child->key) == 0) + cf_util_get_string(child, &cb->msg_format); + else if (strcasecmp("HostTags", child->key) == 0) + cf_util_get_string(child, &cb->host_tags); + else if (strcasecmp("StoreRates", child->key) == 0) + cf_util_get_boolean(child, &cb->store_rates); + else if (strcasecmp("AlwaysAppendDS", child->key) == 0) + cf_util_get_boolean(child, &cb->always_append_ds); + else if (strcasecmp("Prefix", child->key) == 0) + cf_util_get_string(child, &cb->metrics_prefix); + else { + ERROR("write_syslog plugin: Invalid configuration " + "option: %s.", + child->key); + return -1; + } + } + + snprintf(callback_name, sizeof(callback_name), "write_syslog/%s/%s", + cb->node != NULL ? cb->node : WS_DEFAULT_NODE, + cb->service != NULL ? cb->service : WS_DEFAULT_SERVICE); + + user_data_t user_data = {.data = cb, .free_func = ws_callback_free}; + + plugin_register_write(callback_name, ws_write, &user_data); + + user_data.free_func = NULL; + plugin_register_flush(callback_name, ws_flush, &user_data); + + return 0; +} + +static int ws_config(oconfig_item_t *ci) { + if ((resolve_interval == 0) && (resolve_jitter == 0)) + resolve_interval = resolve_jitter = plugin_get_interval(); + + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("Node", child->key) == 0) { + if (ws_config_tsd(child) < 0) + return -1; + } else if (strcasecmp("ResolveInterval", child->key) == 0) + cf_util_get_cdtime(child, &resolve_interval); + else if (strcasecmp("ResolveJitter", child->key) == 0) + cf_util_get_cdtime(child, &resolve_jitter); + else { + ERROR("write_syslog plugin: Invalid configuration " + "option: %s.", + child->key); + return -1; + } + } + + return 0; +} + +void module_register(void) { + plugin_register_complex_config("write_syslog", ws_config); +} diff --git a/src/write_tsdb.c b/src/write_tsdb.c index 42f5d65b..f8f4cb91 100644 --- a/src/write_tsdb.c +++ b/src/write_tsdb.c @@ -43,8 +43,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include "utils_cache.h" #include "utils_random.h" diff --git a/src/xencpu.c b/src/xencpu.c index 8cba476f..e63a7664 100644 --- a/src/xencpu.c +++ b/src/xencpu.c @@ -21,8 +21,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include @@ -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/xmms.c b/src/xmms.c index 3e3a3c3c..2d550b42 100644 --- a/src/xmms.c +++ b/src/xmms.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include diff --git a/src/zfs_arc.c b/src/zfs_arc.c index d1ee111b..be93b9db 100644 --- a/src/zfs_arc.c +++ b/src/zfs_arc.c @@ -29,8 +29,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" /* * Global variables @@ -127,7 +127,7 @@ static long long get_zfs_value(kstat_t *dummy __attribute__((unused)), size_t valuelen = sizeof(value); int rv; - snprintf(buffer, sizeof(buffer), "%s%s", zfs_arcstat, name); + ssnprintf(buffer, sizeof(buffer), "%s%s", zfs_arcstat, name); rv = sysctlbyname(buffer, (void *)&value, &valuelen, /* new value = */ NULL, /* new length = */ (size_t)0); if (rv == 0) @@ -231,23 +231,6 @@ static int za_read(void) { return -1; } - // Ignore the first two lines because they contain information about - // the rest of the file. - // See kstat_seq_show_headers module/spl/spl-kstat.c of the spl kernel - // module. - if (fgets(buffer, sizeof(buffer), fh) == NULL) { - ERROR("zfs_arc plugin: \"%s\" does not contain a single line.", - ZOL_ARCSTATS_FILE); - fclose(fh); - return (-1); - } - if (fgets(buffer, sizeof(buffer), fh) == NULL) { - ERROR("zfs_arc plugin: \"%s\" does not contain at least two lines.", - ZOL_ARCSTATS_FILE); - fclose(fh); - return (-1); - } - while (fgets(buffer, sizeof(buffer), fh) != NULL) { char *fields[3]; value_t v; diff --git a/src/zone.c b/src/zone.c index 16df4043..cd804f74 100644 --- a/src/zone.c +++ b/src/zone.c @@ -33,13 +33,13 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include -#include "utils_avltree.h" +#include "utils/avltree/avltree.h" #define MAX_PROCFS_PATH 40 #define FRC2PCT(pp) (((float)(pp)) / 0x8000 * 100) diff --git a/src/zookeeper.c b/src/zookeeper.c index a99bbc01..9c70ea57 100644 --- a/src/zookeeper.c +++ b/src/zookeeper.c @@ -26,8 +26,8 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #include #include