--- /dev/null
+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
cscope.*
# Unit tests
-src/daemon/test-suite.log
+test-suite.log
src/tests/
-src/test-suite.log
test_*
# src/daemon/...
+# 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"
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
--- /dev/null
+# Code ownership information.
+# See https://help.github.com/articles/about-code-owners/ for details.
+
+# These owners will be the default owners for everything in the repo. Unless a
+# later match takes precedence, # @trusted-contributors will be requested for
+# review when someone opens a pull request.
+* @trusted-contributors
+
+/src/intel_pmu.c @kwiatrox @sunkuranganath
+/src/intel_rdt.c @kwiatrox @sunkuranganath
+/src/ipmi.c @anaudx @rjablonx
+/src/mcelog.c @kwiatrox @sunkuranganath
+/src/virt.c @anaudx @rjablonx
+# TODO(#2926): Add the following owners:
+#/src/redfish.c @kkepka @mkobyli
+++ /dev/null
-# 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)
test_utils_time \
test_utils_vl_lookup \
test_libcollectd_network_parse \
- test_utils_config_cores
+ test_utils_config_cores \
+ test_utils_proc_pids
TESTS = $(check_PROGRAMS)
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 \
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)
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
+test_utils_proc_pids_SOURCES = \
+ src/utils/proc_pids/proc_pids_test.c \
+ src/testing.h
+test_utils_proc_pids_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 \
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 \
-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 =
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 \
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 \
-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 \
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 \
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) \
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 \
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)
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)
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 \
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
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 = \
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)
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)
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)
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)
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)
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)
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)
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
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)
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
endif
if BUILD_PLUGIN_INTERFACE
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)
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)
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)
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)
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)
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
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)
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)
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
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)
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
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
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)
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
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 '\<POD ERRORS\>' $@ >/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 '\<POD ERRORS\>' $@ >/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@)
AC_COMPILE_IFELSE(
[
AC_LANG_PROGRAM(
- [[#include "$srcdir/src/utils_mount.h"]],
+ [[#include "$srcdir/src/utils/mount/mount.h"]],
[[
FILE *fh;
struct mntent *me;
AC_COMPILE_IFELSE(
[
AC_LANG_PROGRAM(
- [[#include "$srcdir/src/utils_mount.h"]],
+ [[#include "$srcdir/src/utils/mount/mount.h"]],
[[
FILE *fh;
struct mnttab mt;
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])
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)"]
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)"]
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)"]
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)"]
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 <rte_eal.h>
+ ]],
+ [[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
# }}}
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)"]
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
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"
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)"]
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)"]
# 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)"
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)"]
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)"]
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 {{{
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)"]
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)"],
fi
if test "x$with_libsensors" = "xyes"; then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_sensors_cppflags"
+ AC_PREPROC_IFELSE(
+ [
+ AC_LANG_SOURCE(
+ [[
+ #include <sensors/sensors.h>
+ #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"
# 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)"
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
AC_PLUGIN([wireless], [$plugin_wireless], [Wireless statistics])
AC_PLUGIN([write_graphite], [yes], [Graphite / Carbon output plugin])
AC_PLUGIN([write_http], [$with_libcurl], [HTTP output plugin])
-AC_PLUGIN([write_stackdriver], [$plugin_write_stackdriver], [Google Stackdriver Monitoring output plugin])
AC_PLUGIN([write_kafka], [$with_librdkafka], [Kafka output plugin])
AC_PLUGIN([write_log], [yes], [Log output plugin])
AC_PLUGIN([write_mongodb], [$with_libmongoc], [MongoDB output plugin])
AC_PLUGIN([write_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_tsdb], [yes], [TSDB output plugin])
AC_PLUGIN([xencpu], [$plugin_xencpu], [Xen Host CPU usage])
AC_PLUGIN([xmms], [$with_libxmms], [XMMS statistics])
AC_MSG_RESULT([ YFLAGS . . . . . . . $YFLAGS])
AC_MSG_RESULT()
AC_MSG_RESULT([ Libraries:])
-AC_MSG_RESULT([ cuda . . . . . . . . $with_cuda])
AC_MSG_RESULT([ intel mic . . . . . . $with_mic])
AC_MSG_RESULT([ libaquaero5 . . . . . $with_libaquaero5])
AC_MSG_RESULT([ libatasmart . . . . . $with_libatasmart])
AC_MSG_RESULT([ 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])
if (param ('debug'))
{
print <<HTTP;
-Content-Type: text/plain
+Content-Type: text/plain; charset=utf-8
HTTP
- $ContentType = 'text/plain';
+ $ContentType = 'text/plain; charset=utf-8';
}
if ($GraphWidth)
RRDFormat "%5.1lf%%"
DSName "value Signal quality"
</Type>
+<Type smart_temperature>
+ DataSources value
+ DSName value Temp
+ RRDTitle "Temperature ({instance})"
+ RRDVerticalLabel "°Celsius"
+ RRDFormat "%4.1lf°C"
+</Type>
<Type snr>
DataSources value
RRDTitle "Signal / noise ratio ({instance})"
+form {
+ display: flex;
+ margin-bottom: 10px;
+}
+
+fieldset {
+ margin-left: 0;
+}
+
div.graph
{
}
APT::Install-Recommends "1";
-APT::Install-Suggests "0";
+APT::Install-Suggests "1";
APT::Get::Assume-Yes "1";
APT::Get::AutomaticRemove "1";
-FROM debian:stable
+FROM debian:stable-slim
ENV DEBIAN_FRONTEND noninteractive
COPY 50docker-apt-conf /etc/apt/apt.conf.d/
ENV LD_PRELOAD /usr/src/rootfs_prefix/rootfs_prefix.so
-CMD [ "/usr/sbin/collectd", "-f"]
+ENTRYPOINT ["/usr/sbin/collectd"]
+CMD ["-f"]
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.
--- /dev/null
+# 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)
#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}"
#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 <amqp.h>
#include <amqp_framing.h>
#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 <proton/condition.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <curl/curl.h>
#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 <sys/types.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_MACH_MACH_TYPES_H
#include <mach/mach_types.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <libaquaero5.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <curl/curl.h>
#include <libxml/parser.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include <fcntl.h>
* @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",
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_MACH_MACH_TYPES_H
#include <mach/mach_types.h>
**/
-#include "common.h"
-#include "plugin.h"
#include "collectd.h"
+#include "plugin.h"
+#include "utils/common/common.h"
#include <stdio.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <time.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <arpa/inet.h>
#include <errno.h>
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,
}
memmove(state->key, key, sz - 1);
- state->key[sz - 1] = 0;
+ state->key[sz - 1] = '\0';
return CEPH_CB_CONTINUE;
}
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;
}
#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);
/* 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)
#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 <netdb.h> /* struct addrinfo */
=head1 AUTHOR
The C<Lua plugin> has been written by
-Julien Ammous E<lt>j.ammous<nbsp>atE<nbsp>gmail.comE<gt>,
+Julien Ammous E<lt>j.ammousE<nbsp>atE<nbsp>gmail.comE<gt>,
Florian Forster E<lt>octoE<nbsp>atE<nbsp>collectd.orgE<gt> and
-Ruben Kerkhof E<lt>ruben<nbsp>atE<nbsp>rubenkerkhof.com<gt> and
+Ruben Kerkhof E<lt>rubenE<nbsp>atE<nbsp>rubenkerkhof.comE<gt>.
This manpage has been written by Ruben Kerkhof
-E<lt>ruben<nbsp>atE<nbsp>rubenkerkhof.com<gt>.
+E<lt>rubenE<nbsp>atE<nbsp>rubenkerkhof.comE<gt>.
It is based on the L<collectd-perl(5)> manual page by
Florian Forster E<lt>octoE<nbsp>atE<nbsp>collectd.orgE<gt> and
Sebastian Harl E<lt>shE<nbsp>atE<nbsp>tokkee.orgE<gt>.
char *ret;
strsize = strlen(str) + 1;
- ret = (char *)malloc(strsize);
+ ret = malloc(strsize);
if (ret != NULL)
memcpy(ret, str, strsize);
return ret;
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);
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) {
}
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);
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
=item B<AuthPassphrase> I<Passphrase>
-Sets the authentication passphrase for SNMPv3 security.
+Sets the authentication passphrase for SNMPv3 security.
=item B<PrivacyProtocol> I<AES>|I<DES>
=item B<PrivacyPassphrase> I<Passphrase>
-Sets the privacy (encryption) passphrase for SNMPv3 security.
+Sets the privacy (encryption) passphrase for SNMPv3 security.
=item B<Collect> I<Data> [I<Data> ...]
#include <time.h>
#include <unistd.h>
-#include "utils_heap.h"
+#include "utils/heap/heap.h"
#include "collectd/client.h"
#include "collectd/network.h"
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());
#<Plugin "intel_rdt">
# Cores "0-2"
+# Processes "sshd"
#</Plugin>
#<Plugin interface>
# 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 vcpupin disk_physical disk_allocation disk_capacity"
# PersistentNotification false
#</Plugin>
<Plugin "intel_rdt">
Cores "0-2" "3,4,6" "8-10,15"
+ Processes "sshd,qemu-system-x86" "bash"
</Plugin>
B<Options:>
=item B<Cores> I<cores groups>
-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
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<Processes> I<process names groups>
+
+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<Note:> By default global interval is used to retrieve statistics on monitored
For Modbus/RTU, specifies the baud rate of the serial device.
Note, connections currently support only 8/N/1.
+=item B<UARTType> I<UARTType>
+
+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<Interval> I<Interval>
Sets the interval (in seconds) in which the values will be collected from this
namely octets, packets, and errors. These statistics are collected by
the C<interface> plugin, too, so using both at the same time is no benefit.
-When configured with B<VerboseInterface> all counters B<except> the basic ones,
-so that no data needs to be collected twice if you use the C<interface> plugin.
+When configured with B<VerboseInterface> all counters B<except> the basic ones
+will be collected, so that no data needs to be collected twice if you use the
+C<interface> 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:
Address "127.0.0.1"
Socket "/var/run/openvswitch/db.sock"
Bridges "br0" "br_ext"
+ InterfaceStats false
</Plugin>
The plugin provides the following configuration options:
Default: empty (monitor all bridges)
+=item B<InterfaceStats> B<false>|B<true>
+
+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<false>.
+
=back
=head2 Plugin C<pcie_errors>
=item B<GaugeAverage>
-Calculate the average.
+Calculate the average of all values matched during the interval.
=item B<GaugeMin>
-Use the smallest number only.
+Report the smallest value matched during the interval.
=item B<GaugeMax>
-Use the greatest number only.
+Report the greatest value matched during the interval.
=item B<GaugeLast>
-Use the last number found.
+Report the last value matched during the interval.
=item B<GaugePersist>
-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<not> reset to C<NaN> 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<CounterSet>
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<GaugeInc> is reset to I<zero> after every read, unlike other B<Gauge*>
+metrics which are reset to C<NaN>.
+
=item B<Distribution>
Type to do calculations based on the distribution of values, primarily
point number, using L<strtod(3)>. The B<Counter*> and B<AbsoluteSet> types
interpret the submatch as an unsigned integer using L<strtoull(3)>. The
B<Derive*> types interpret the submatch as a signed integer using
-L<strtoll(3)>. B<CounterInc> and B<DeriveInc> do not use the submatch at all
-and it may be omitted in this case.
+L<strtoll(3)>. B<CounterInc>, B<DeriveInc> and B<GaugeInc> do not use the
+submatch at all and it may be omitted in this case.
+
+The B<Gauge*> types, unless noted otherwise, are reset to C<NaN> after being
+reported. In other words, B<GaugeAverage> reports the average of all values
+matched since the last metric was reported (or C<NaN> if there was no match).
=item B<Type> I<Type>
The default is to collect statistics for all domains and all their devices.
-Example:
+B<Note:> B<BlockDevice> and B<InterfaceDevice> options are related to
+corresponding B<*Format> options. Specifically, B<BlockDevice> filtering depends
+on B<BlockDeviceFormat> setting - if user wants to filter block devices by
+'target' name then B<BlockDeviceFormat> option has to be set to 'target' and
+B<BlockDevice> 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<BlockDevice> value with B<BlockDeviceFormat> set to
+'source') may lead to unexpected results (all devices filtered out or all
+visible, depending on the value of B<IgnoreSelected> option).
+Similarly, option B<InterfaceDevice> is related to B<InterfaceFormat> setting
+(i.e., when user wants to use MAC address as a filter then B<InterfaceFormat>
+has to be set to 'address' - using wrong type here may filter out all of the
+interfaces).
+
+B<Example 1:>
+
+Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
+will be collected:
BlockDevice "/:hdb/"
IgnoreSelected "true"
+ BlockDeviceFormat "target"
-Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
-will be collected.
+B<Example 2:>
+
+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<Example 3:>
+
+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<BlockDeviceFormat> B<target>|B<source>
Setting C<BlockDeviceFormat source> will cause the I<type instance> to be set
to C<var_lib_libvirt_images_image1.qcow2>.
+B<Note:> this option determines also what field will be used for
+filtering over block devices (filter value in B<BlockDevice>
+will be applied to target or source). More info about filtering
+block devices can be found in the description of B<BlockDevice>.
+
=item B<BlockDeviceFormatBasename> B<false>|B<true>
The B<BlockDeviceFormatBasename> controls whether the full path or the
Setting C<BlockDeviceFormatBasename true> will cause the I<type instance> to be
set to C<image1.qcow2>.
-=item B<HostnameFormat> B<name|uuid|hostname|...>
+=item B<HostnameFormat> B<name|uuid|hostname|metadata...>
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
useful on its own because all guests will appear to have the same name. This is
useful in conjunction with B<PluginInstanceFormat> though.
+B<metadata> means use information from guest's metadata. Use
+B<HostnameMetadataNS> and B<HostnameMetadataXPath> to localize this information.
+
You can also specify combinations of these fields. For example B<name uuid>
means to concatenate the guest name and UUID (with a literal colon character
between, thus I<"foo:1234-1234-1234-1234">).
characters. In case when combination of fields exceeds 62 characters,
hostname will be truncated without a warning.
-=item B<InterfaceFormat> B<name>|B<address>
+=item B<InterfaceFormat> B<name>|B<address>|B<number>
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
B<address> 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<PluginInstanceFormat> B<name|uuid|none>
+B<number> means use the interface's number in guest.
+
+B<Note:> this option determines also what field will be used for
+filtering over interface device (filter value in B<InterfaceDevice>
+will be applied to name, address or number). More info about filtering
+interfaces can be found in the description of B<InterfaceDevice>.
+
+=item B<PluginInstanceFormat> B<name|uuid|metadata|none>
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<name> means use the guest's name as provided by the hypervisor.
B<uuid> means use the guest's UUID.
+B<metadata> means use information from guest's metadata.
You can also specify combinations of the B<name> and B<uuid> fields.
For example B<name uuid> means to concatenate the guest name and UUID
(with a literal colon character between, thus I<"foo:1234-1234-1234-1234">).
-=item B<Instances> B<integer>
+=item B<HostnameMetadataNS> B<string>
-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<ReadThreads> value.
-If you are not sure, just use the default setting.
+When B<metadata> is used in B<HostnameFormat> or B<PluginInstanceFormat>, this
+selects in which metadata namespace we will pick the hostname. The default is
+I<http://openstack.org/xmlns/libvirt/nova/1.0>.
+
+=item B<HostnameMetadataXPath> B<string>
+
+When B<metadata> is used in B<HostnameFormat> or B<PluginInstanceFormat>, this
+describes where the hostname is located in the libvirt metadata. The default is
+I</instance/name/text()>.
+
+=item B<ReportBlockDevices> B<true>|B<false>
+
+Enabled by default. Allows to disable stats reporting of block devices for
+whole plugin.
+
+=item B<ReportNetworkInterfaces> B<true>|B<false>
+
+Enabled by default. Allows to disable stats reporting of network interfaces for
+whole plugin.
=item B<ExtraStats> B<string>
=item B<vcpupin>: report pinning of domain VCPUs to host physical CPUs.
+=item B<disk_physical>: report 'disk_physical' statistic for disk device.
+B<Note>: This statistic is only reported for disk devices with 'source'
+property available.
+
+=item B<disk_allocation>: report 'disk_allocation' statistic for disk device.
+B<Note>: This statistic is only reported for disk devices with 'source'
+property available.
+
+=item B<disk_capacity>: report 'disk_capacity' statistic for disk device.
+B<Note>: This statistic is only reported for disk devices with 'source'
+property available.
+
=back
=item B<PersistentNotification> B<true>|B<false>
+
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<Instances> B<integer>
+
+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<ReadThreads> 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</domain/metadata/ovirtmap/tag/text()>
+expression in the I<http://ovirt.org/ovirtmap/tag/1.0> 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<vmem>
=head1 IGNORELISTS
B<Ignorelists> 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<IgnoreSelected>.
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#ifdef HAVE_MACH_KERN_RETURN_H
#include <mach/kern_return.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#define MAX_AVAIL_FREQS 20
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;
#include "collectd.h"
-#include <time.h>
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
+#include <time.h>
static void cpusleep_submit(derive_t cpu_sleep) {
value_list_t vl = VALUE_LIST_INIT;
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
/*
#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 <curl/curl.h>
#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 <sys/types.h>
#include <sys/un.h>
/* 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) {
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;
#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 <libxml/parser.h>
#include "cmd.h"
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include <sys/un.h>
static void *do_flush(void __attribute__((unused)) * arg) {
#include "cmd.h"
#include "collectd.h"
-#include "common.h"
#include "configfile.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <sys/types.h>
* 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;
}
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- * Niki W. Waibel <niki.waibel@gmx.net>
- * Sebastian Harl <sh at tokkee.org>
- * Michał Mirosław <mirq-linux at rere.qmqm.pl>
-**/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_cache.h"
-
-/* for getaddrinfo */
-#include <netdb.h>
-#include <sys/types.h>
-
-#include <poll.h>
-
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
-#if HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-
-/* for ntohl and htonl */
-#if HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-
-#if HAVE_CAPABILITY
-#include <sys/capability.h>
-#endif
-
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- * Niki W. Waibel <niki.waibel@gmx.net>
-**/
-
-#ifndef COMMON_H
-#define COMMON_H
-
-#include "collectd.h"
-
-#include "plugin.h"
-
-#if HAVE_PWD_H
-#include <pwd.h>
-#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 -> <tab>
- * \n -> <newline>
- * \r -> <carriage return>
- *
- * 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 <kstat.h>
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- */
-
-#include "common.h"
-#include "testing.h"
-
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#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;
-}
#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 <wordexp.h>
}
/* Hm, no complex plugin found. Dispatch the values one by one */
- for (int i = 0; i < ci->children_num; i++) {
- if (ci->children[i].children == NULL)
- dispatch_value_plugin(name, ci->children + i);
- else {
+ for (int i = 0, ret = 0; i < ci->children_num; i++) {
+ if (ci->children[i].children == NULL) {
+ ret = dispatch_value_plugin(name, ci->children + i);
+ if (ret != 0)
+ return ret;
+ } else {
WARNING("There is a `%s' block within the "
"configuration for the %s plugin. "
"The plugin either only expects "
} /* }}} 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))
}
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 */
#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"
/*
* DEALINGS IN THE SOFTWARE.
**/
-#include "common.h"
#include "globals.h"
+#include "utils/common/common.h"
#if HAVE_KSTAT_H
#include <kstat.h>
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- */
-
-#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;
-}
#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"
int i;
llentry_t *le;
int n;
- char **keys;
n = llist_size(*list);
if (n == 0) {
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;
}
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;
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;
#include "collectd.h"
#include "configfile.h"
-#include "meta_data.h"
+#include "utils/metadata/meta_data.h"
#include "utils_time.h"
#include <inttypes.h>
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "configfile.h"
#include "plugin.h"
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;
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) {
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#include <assert.h>
-#include <stdlib.h>
-
-#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;
-}
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- */
-
-#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;
-}
#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 <assert.h>
* 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;
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <pthread.h>
-#include <stdlib.h>
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- */
-
-#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;
-}
#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,
*/
#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"
#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 <pthread.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_time.h"
#ifndef DEFAULT_MOCK_TIME
#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 <dbi/dbi.h>
#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
#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 <mach/mach_types.h>
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) {
}
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;
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
-#include "utils_dns.h"
+#include "utils/dns/dns.h"
#include <poll.h>
#include <pcap.h>
#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 <rte_config.h>
#include "collectd.h"
-#include "common.h"
-#include "utils_dpdk.h"
+#include "utils/common/common.h"
+#include "utils/dpdk/dpdk.h"
#include <rte_config.h>
#include <rte_ethdev.h>
#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[] = {
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <stddef.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#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
#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 <grp.h>
#include <pwd.h>
#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);
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <dirent.h>
#include <fcntl.h>
#include "collectd.h"
+#include "plugin.h"
+#include "utils/common/common.h"
#include <stdio.h> /* a header needed for FILE */
#include <stdlib.h> /* used for atoi */
#include <string.h> /* 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"
#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 <netdb.h>
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;
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++)
**/
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_time.h"
#define CGPS_TRUE 1
*/
#include "daemon/collectd.h"
-#include "daemon/common.h"
#include "daemon/plugin.h"
+#include "utils/common/common.h"
#include <nvml.h>
#include <stdint.h>
#include <stdbool.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "daemon/utils_cache.h"
}
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <assert.h>
#include <libgen.h> /* for basename */
#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";
**/
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
-#include "utils_config_cores.h"
+#include "utils/config_cores/config_cores.h"
#include <jevents.h>
#include <jsession.h>
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;
/* 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;
/* 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;
/**
* 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
*
* Authors:
* Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ * Starzyk, Mateusz <mateuszx.starzyk@intel.com>
+ * Wojciech Andralojc <wojciechx.andralojc@intel.com>
+ * Michał Aleksiński <michalx.aleksinski@intel.com>
**/
#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 <pqos.h>
#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;
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));
+ 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;
+
+ 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);
+}
+
#if COLLECT_DEBUG
static void rdt_dump_cgroups(void) {
char cores[RDT_MAX_CORES * 4];
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++) {
+ for (size_t j = 0; j < cgroup->num_cores; j++) {
snprintf(cores + strlen(cores), sizeof(cores) - strlen(cores) - 1, " %d",
cgroup->cores[j]);
}
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++)
+ snprintf(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++)
+ snprintf(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\' in group \'%s\'",
+ token, str_list);
+ 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) {
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();
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]);
}
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;
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.");
pqos_fini();
rdt_preinit_error1:
-
sfree(g_rdt);
return -1;
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);
}
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;
-
- 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;
+static int read_cores_data() {
- 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 */
}
}
+#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;
- /* Start monitoring */
- for (size_t i = 0; i < g_rdt->num_groups; i++) {
+#ifdef LIBPQOS2
+ if (0 != pids_read_result)
+ return pids_read_result;
+#endif /* LIBPQOS2 */
+
+ 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 (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 (ret != PQOS_RETVAL_OK)
- ERROR(RDT_PLUGIN ": Error starting monitoring group %s (pqos status=%d)",
- cg->desc, ret);
+ 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;
}
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;
--- /dev/null
+#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
#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 <sys/types.h>
#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 */
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <OpenIPMI/ipmi_auth.h>
#include <OpenIPMI/ipmi_conn.h>
if (errbuf[0] == 0) {
snprintf(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 */
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);
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;
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <libiptc/libip6tc.h>
#include <libiptc/libiptc.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#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."
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;
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <jni.h>
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);
* 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)
}
#endif /* STRERROR_R_CHAR_P */
- buf[buflen - 1] = 0;
+ buf[buflen - 1] = '\0';
return buf;
} /* char *sstrerror */
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 */
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 */
/* 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) {
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);
ident->type_instance);
}
- string[string_size - 1] = 0;
+ string[string_size - 1] = '\0';
return 0;
} /* }}} int lcc_identifier_to_string */
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;
}
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;
}
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);
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);
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <unistd.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <sys/types.h>
#include <yajl/yajl_common.h>
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;
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if COLLECT_DEBUG
static int log_level = LOG_DEBUG;
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <libperfstat.h>
#include <sys/protosw.h>
**/
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_lua.h"
/* Include the Lua API header files. */
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;
return 0;
} /* }}} lua_cb_dispatch_values */
+static void lua_cb_free(void *data) {
+ clua_callback_data_t *cb = data;
+ free(cb->lua_function_name);
+ free(cb);
+}
+
static int lua_cb_register_read(lua_State *L) /* {{{ */
{
int nargs = lua_gettop(L);
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,
- });
+ 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");
int status = plugin_register_write(/* name = */ function_name,
/* callback = */ clua_write,
&(user_data_t){
- .data = cb,
+ .data = cb, .free_func = lua_cb_free,
});
if (status != 0)
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <lvm2app.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <dirent.h>
#include <sys/ioctl.h>
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
/*
* internal helper functions
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
/*
* private data types
#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 <regex.h>
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);
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
#define SATISFY_ALL 0
#define SATISFY_ANY 1
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#define SATISFY_ALL 0
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <netinet/in.h>
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "utils_llist.h"
#include <poll.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <sys/ioctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_match.h"
+#include "utils/common/common.h"
+#include "utils/match/match.h"
#include <libmemcached/memcached.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <netinet/in.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <MicAccessApi.h>
#include <MicAccessErrorTypes.h>
#include "collectd.h"
-#include "common.h"
#include "configfile.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <modbus.h>
#include <netdb.h>
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 /* {{{ */
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;
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 */
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. */
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_complain.h"
#include <mosquitto.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#ifdef HAVE_MYSQL_H
#include <mysql.h>
#include "collectd.h"
-#include "common.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <netapp_api.h>
#include <netapp_errno.h>
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;
}
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <asm/types.h>
#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"
#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);
#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);
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_KSTAT_H
#include <kstat.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <curl/curl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <glib.h>
#include <libnotify/notify.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <auth-client.h>
#include <libesmtp.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#define NAGIOS_OK 0
#define NAGIOS_WARNING 1
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_NETDB_H
#include <netdb.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <upsclient.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <netinet/in.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <owcapi.h>
#include <regex.h>
snprintf(file, sizeof(file), "%s/%s", path,
family_info->features[i].filename);
- file[sizeof(file) - 1] = 0;
+ file[sizeof(file) - 1] = '\0';
buffer = NULL;
buffer_size = 0;
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if defined(__APPLE__)
#pragma clang diagnostic push
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
/**
* There is two main kinds of OpenVPN status file:
#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 <oci.h>
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;
} /* 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;
#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
* Taras Chornyi <tarasx.chornyi@intel.com>
*/
-#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";
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,
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;
#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 {
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),
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
.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;
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) {
+ snprintf(key_str, sizeof(key_str), "uuid%d", i);
+ meta_data_add_string(meta, key_str, iface->iface_uuid);
+
+ if (strlen(iface->ex_vm_id)) {
+ snprintf(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)) {
+ snprintf(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;
+ snprintf(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;
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) {
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;
}
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;
}
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;
}
*/
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 */
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", "<some_uuid>" ], [ "uuid",
+ // "<another_uuid>" ], ... ]]
+ yajl_val ifaces_list = YAJL_GET_ARRAY(ifaces_root)->values[1];
+
+ // ifaces_list is [[ "uuid", "<some_uuid>" ], [ "uuid",
+ // "<another_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", "<some_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", "<some_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;
}
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;
}
*/
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;
}
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;
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": [
}
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;
}
*/
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;
}
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};
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);
}
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 {
}
}
}
+ } 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;
/* 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;
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "utils_llist.h"
#include <linux/pci_regs.h>
#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"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <poll.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_complain.h"
#include <netinet/in.h>
#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 <libpq-fe.h>
#include <pg_config_manual.h>
}
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);
}
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;
}
}
tmp = realloc(data->params, (data->params_num + 1) * sizeof(*data->params));
- if (NULL == tmp) {
+ if (tmp == NULL) {
log_err("Out of memory.");
return -1;
}
}
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);
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_llist.h"
#include <errno.h>
}
memcpy(buffer, temp, buffer_size - 1);
- buffer[buffer_size - 1] = 0;
+ buffer[buffer_size - 1] = '\0';
*ret_buffer = buffer;
return 0;
#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.. */
typedef struct procstat_entry_s {
unsigned long id;
- unsigned long age;
+ unsigned char age;
derive_t vmem_minflt_counter;
derive_t vmem_majflt_counter;
entry->cpu_system_counter);
#if HAVE_LIBTASKSTATS
- ps_update_delay(ps, pse, entry);
+ if (entry->has_delay)
+ ps_update_delay(ps, pse, entry);
#endif
}
}
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);
pse = pse_prev->next;
}
} else {
- pse->age++;
+ pse->age = 1;
pse_prev = pse;
pse = pse->next;
}
return NULL;
}
- info.pr_psargs[sizeof(info.pr_psargs) - 1] = 0;
sstrncpy(buffer, info.pr_psargs, buffer_size);
return buffer;
#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."
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "cpython.h"
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "cpython.h"
continue;
if (cpy[strlen(cpy) - 1] == '\n')
- cpy[strlen(cpy) - 1] = 0;
+ cpy[strlen(cpy) - 1] = '\0';
Py_BEGIN_ALLOW_THREADS;
ERROR("%s", cpy);
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, "<stdin>");
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "cpython.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <hiredis/hiredis.h>
#include <sys/time.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <routeros_api.h>
#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 <rrd.h>
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 =
+ snprintf(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) &&
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;
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;
} /* 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)
} /* 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)",
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;
} /* 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;
} /* 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)",
} /* 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};
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)",
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. */
user_data_t __attribute__((unused)) * user_data) {
char filename[PATH_MAX];
char values[512];
- char *values_array[2];
int status;
bool retried = false;
return -1;
}
- values_array[0] = values;
- values_array[1] = NULL;
-
if (config_create_files) {
struct stat statbuf;
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. */
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);
else
snprintf(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)",
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. */
#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 <rrd.h>
snprintf(key, sizeof(key), "%s.rrd", identifier);
else
snprintf(key, sizeof(key), "%s/%s.rrd", datadir, identifier);
- key[sizeof(key) - 1] = 0;
+ key[sizeof(key) - 1] = '\0';
status = c_avl_get(cache, key, (void *)&rc);
if (status != 0) {
#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 <sensors/sensors.h>
#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);
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;
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);
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);
}
} /* while sensors_get_detected_chips */
/* #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;
} /* 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();
} /* for fl = first_feature .. NULL */
/* #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;
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 */
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !KERNEL_LINUX
#error "No applicable input method."
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]);
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <stdio.h>
#include <stdlib.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
#include <atasmart.h>
#include <libudev.h>
#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 <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
if (((size_t)status) >= buffer_free) /* truncated */
{
- dst[dst_size - 1] = 0;
+ dst[dst_size - 1] = '\0';
return ENOMEM;
} else /* if (status < buffer_free) */
{
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;
#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 <regex.h>
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;
#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 <netdb.h>
#include <poll.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_SYS_SWAP_H
#include <sys/swap.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !KERNEL_LINUX
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_SYSLOG_H
#include <syslog.h>
#include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
#include "plugin.h"
#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"
/*
#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 <fcntl.h>
#include <stdlib.h>
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;
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if !HAVE_LIBKSTAT
#error "No applicable input method."
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include "utils_subst.h"
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
#include "utils_subst.h"
#include <regex.h>
#include "collectd.h"
-#include "common.h"
#include "filter_chain.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#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 {
#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) /* {{{ */
{
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if defined(__OpenBSD__)
#define HAVE_KVM_GETFILES 1
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <arpa/inet.h>
#include <netdb.h>
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 "
return -1;
}
- buffer[buffer_size - 1] = 0;
+ buffer[buffer_size - 1] = '\0';
return 0;
} /* int tss2_receive_line */
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) &&
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) {
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#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."
#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 "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include <tcrdb.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_time.h"
#include "msr-index.h"
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().
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,
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
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
#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 <sys/stat.h>
#include <sys/un.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if KERNEL_LINUX
#include <sys/sysinfo.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_STATGRAB_H
#include <statgrab.h>
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#include <assert.h>
+#include <stdlib.h>
+
+#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;
+}
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ */
+
+#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;
+}
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ * Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+#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 <stdbool.h>
+#include <string.h>
+
+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 */
--- /dev/null
+/**
+ * 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 <sh at tokkee.org>
+ **/
+
+#ifndef UTILS_CMDS_H
+#define UTILS_CMDS_H 1
+
+#include "plugin.h"
+
+#include <stdarg.h>
+
+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 */
--- /dev/null
+/**
+ * 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 <sh at tokkee.org>
+ **/
+
+#include "testing.h"
+#include "utils/cmds/cmds.h"
+#include "utils/common/common.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;
+}
--- /dev/null
+/**
+ * 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 <sh at tokkee.org>
+ * Florian "octo" Forster <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <sh at tokkee.org>
+ **/
+
+#ifndef UTILS_CMD_FLUSH_H
+#define UTILS_CMD_FLUSH_H 1
+
+#include "utils/cmds/cmds.h"
+
+#include <stdio.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);
+
+cmd_status_t cmd_handle_flush(FILE *fh, char *buffer);
+
+void cmd_destroy_flush(cmd_flush_t *flush);
+
+#endif /* UTILS_CMD_FLUSH_H */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#ifndef UTILS_CMD_GETTHRESHOLD_H
+#define UTILS_CMD_GETTHRESHOLD_H 1
+
+#include <stdio.h>
+
+int handle_getthreshold(FILE *fh, char *buffer);
+
+#endif /* UTILS_CMD_GETTHRESHOLD_H */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#ifndef UTILS_CMD_GETVAL_H
+#define UTILS_CMD_GETVAL_H 1
+
+#include <stdio.h>
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#ifndef UTILS_CMD_LISTVAL_H
+#define UTILS_CMD_LISTVAL_H 1
+
+#include <stdio.h>
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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: <type>:<key> */
+ 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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#ifndef UTILS_CMD_PUTNOTIF_H
+#define UTILS_CMD_PUTNOTIF_H 1
+
+#include <stdio.h>
+
+int handle_putnotif(FILE *fh, char *buffer);
+
+#endif /* UTILS_CMD_PUTNOTIF_H */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ * Sebastian tokkee Harl <sh at tokkee.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#ifndef UTILS_CMD_PUTVAL_H
+#define UTILS_CMD_PUTVAL_H 1
+
+#include "plugin.h"
+#include "utils/cmds/cmds.h"
+
+#include <stdio.h>
+
+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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ * Niki W. Waibel <niki.waibel@gmx.net>
+ * Sebastian Harl <sh at tokkee.org>
+ * Michał Mirosław <mirq-linux at rere.qmqm.pl>
+**/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils_cache.h"
+
+/* for getaddrinfo */
+#include <netdb.h>
+#include <sys/types.h>
+
+#include <poll.h>
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+/* for ntohl and htonl */
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#if HAVE_CAPABILITY
+#include <sys/capability.h>
+#endif
+
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#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 (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) && (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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ * Niki W. Waibel <niki.waibel@gmx.net>
+**/
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "collectd.h"
+
+#include "plugin.h"
+
+#if HAVE_PWD_H
+#include <pwd.h>
+#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 -> <tab>
+ * \n -> <newline>
+ * \r -> <carriage return>
+ *
+ * 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 <kstat.h>
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ */
+
+#include "testing.h"
+#include "utils/common/common.h"
+
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#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;
+}
--- /dev/null
+/**
+ * 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 <kamilx.wiatrowski@intel.com>
+ **/
+
+#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];
+ 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;
+}
--- /dev/null
+/**
+ * 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 <kamilx.wiatrowski@intel.com>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <kamilx.wiatrowski@intel.com>
+ **/
+
+#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;
+}
--- /dev/null
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+
+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;
+}
--- /dev/null
+/**
+ * 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 <pyr at spootnik.org>
+ */
+
+#ifndef UTILS_CRC32_H
+#define UTILS_CRC32_H 1
+
+uint32_t crc32_buffer(const unsigned char *, size_t);
+
+#endif
--- /dev/null
+/**
+ * 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 <sh@tokkee.org>
+ **/
+
+#include "collectd.h"
+
+#include "utils/common/common.h"
+#include "utils/curl_stats/curl_stats.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+
+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 ? "<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 */
--- /dev/null
+/**
+ * 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 <sh@tokkee.org>
+ **/
+
+#ifndef UTILS_CURL_STATS_H
+#define UTILS_CURL_STATS_H 1
+
+#include "plugin.h"
+
+#include <curl/curl.h>
+
+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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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';
+
+ 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 =
+ 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 <Query> "
+ "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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <ansmith@redhat.com>
+ */
+
+#ifndef utils_deq_h
+#define utils_deq_h 1
+
+#include <assert.h>
+#include <memory.h>
+#include <stdlib.h>
+
+#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
--- /dev/null
+/*
+ * 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. <http://www.measurement-factory.com/>
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#if HAVE_NET_IF_ARP_H
+#include <net/if_arp.h>
+#endif
+#if HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#if HAVE_NET_PPP_DEFS_H
+#include <net/ppp_defs.h>
+#endif
+#if HAVE_NET_IF_PPP_H
+#include <net/if_ppp.h>
+#endif
+
+#if HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP6_H
+#include <netinet/ip6.h>
+#endif
+#if HAVE_NETINET_IF_ETHER_H
+#include <netinet/if_ether.h>
+#endif
+#if HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+#ifdef HAVE_NETINET_IP_VAR_H
+#include <netinet/ip_var.h>
+#endif
+#if HAVE_NETINET_UDP_H
+#include <netinet/udp.h>
+#endif
+
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#if HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#if HAVE_ARPA_NAMESER_COMPAT_H
+#include <arpa/nameser_compat.h>
+#endif
+
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#if HAVE_PCAP_H
+#include <pcap.h>
+#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];
+ 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
--- /dev/null
+/*
+ * 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. <http://www.measurement-factory.com/>
+ * Florian octo Forster <octo at collectd.org>
+ */
+
+#ifndef COLLECTD_UTILS_DNS_H
+#define COLLECTD_UTILS_DNS_H 1
+
+#include "config.h"
+
+#include <arpa/nameser.h>
+#include <stdint.h>
+
+#if HAVE_PCAP_H
+#include <pcap.h>
+#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 */
--- /dev/null
+/*
+ * 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 <maryam.tahhan@intel.com>
+ * Harry van Haaren <harry.van.haaren@intel.com>
+ * Taras Chornyi <tarasx.chornyi@intel.com>
+ * Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ * Krzysztof Matczak <krzysztofx.matczak@intel.com>
+ */
+
+#include "collectd.h"
+
+#include <poll.h>
+#include <semaphore.h>
+#include <sys/mman.h>
+
+#include <rte_config.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+
+#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);
+
+ 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;
+}
--- /dev/null
+/*
+ * 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 <maryam.tahhan@intel.com>
+ * Harry van Haaren <harry.van.haaren@intel.com>
+ * Taras Chornyi <tarasx.chornyi@intel.com>
+ * Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ * Krzysztof Matczak <krzysztofx.matczak@intel.com>
+ */
+
+#ifndef UTILS_DPDK_H
+#define UTILS_DPDK_H
+
+#include <rte_version.h>
+
+#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 */
--- /dev/null
+/**
+ * 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 <zllak at hycik.org>
+ * Florian octo Forster <octo at collectd.org>
+ **/
+
+#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) {
+ char t;
+ for (int i = 0; i < len / 2; i++) {
+ int j = len - i - 1;
+ 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) {
+ int len_host = strlen(vl->host);
+ char r_host[len_host + 1];
+ 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) {
+ int len_host = strlen(vl->host);
+ char r_host[len_host + 1];
+ 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 */
--- /dev/null
+/**
+ * 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 <zllak at hycik.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ */
+
+#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];
+ 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;
+}
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 <yajl/yajl_common.h>
+#include <yajl/yajl_gen.h>
+#if HAVE_YAJL_YAJL_VERSION_H
+#include <yajl/yajl_version.h>
+#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": <rfc3339 time>,
+ * "endsAt": <rfc3339 time>, // 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
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ */
+
+/* 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 <yajl/yajl_common.h>
+#include <yajl/yajl_parse.h>
+#if HAVE_YAJL_YAJL_VERSION_H
+#include <yajl/yajl_version.h>
+#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;
+}
--- /dev/null
+/**
+ * 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 <beorn at gandi dot net>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <beorn at gandi dot net>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * collectd - src/utils_format_stackdriver.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "utils/format_stackdriver/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 <yajl/yajl_gen.h>
+#include <yajl/yajl_parse.h>
+#if HAVE_YAJL_YAJL_VERSION_H
+#include <yajl/yajl_version.h>
+#endif
+
+struct sd_output_s {
+ sd_resource_t *res;
+ yajl_gen gen;
+ c_avl_tree_t *staged;
+ c_avl_tree_t *metric_descriptors;
+};
+
+struct sd_label_s {
+ char *key;
+ char *value;
+};
+typedef struct sd_label_s sd_label_t;
+
+struct sd_resource_s {
+ char *type;
+
+ sd_label_t *labels;
+ size_t labels_num;
+};
+
+static int json_string(yajl_gen gen, char const *s) /* {{{ */
+{
+ yajl_gen_status status =
+ yajl_gen_string(gen, (unsigned char const *)s, strlen(s));
+ if (status != yajl_gen_status_ok)
+ return (int)status;
+
+ return 0;
+} /* }}} int json_string */
+
+static int json_time(yajl_gen gen, cdtime_t t) {
+ char buffer[64];
+
+ size_t status = rfc3339(buffer, sizeof(buffer), t);
+ if (status != 0) {
+ return status;
+ }
+
+ return json_string(gen, buffer);
+} /* }}} int json_time */
+
+/* MonitoredResource
+ *
+ * {
+ * "type": "library.googleapis.com/book",
+ * "labels": {
+ * "/genre": "fiction",
+ * "/media": "paper"
+ * "/title": "The Old Man and the Sea"
+ * }
+ * }
+ */
+static int format_gcm_resource(yajl_gen gen, sd_resource_t *res) /* {{{ */
+{
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "type") || json_string(gen, res->type);
+ if (status != 0)
+ return status;
+
+ if (res->labels_num != 0) {
+ status = json_string(gen, "labels");
+ if (status != 0)
+ return status;
+
+ yajl_gen_map_open(gen);
+ for (size_t i = 0; i < res->labels_num; i++) {
+ status = json_string(gen, res->labels[i].key) ||
+ json_string(gen, res->labels[i].value);
+ if (status != 0)
+ return status;
+ }
+ yajl_gen_map_close(gen);
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_gcm_resource */
+
+/* TypedValue
+ *
+ * {
+ * // Union field, only one of the following:
+ * "int64Value": string,
+ * "doubleValue": number,
+ * }
+ */
+static int format_typed_value(yajl_gen gen, int ds_type, value_t v,
+ int64_t start_value) {
+ char integer[32];
+
+ yajl_gen_map_open(gen);
+
+ switch (ds_type) {
+ case DS_TYPE_GAUGE: {
+ int status = json_string(gen, "doubleValue");
+ if (status != 0)
+ return status;
+
+ status = (int)yajl_gen_double(gen, (double)v.gauge);
+ if (status != yajl_gen_status_ok)
+ return status;
+
+ yajl_gen_map_close(gen);
+ return 0;
+ }
+ case DS_TYPE_DERIVE: {
+ derive_t diff = v.derive - (derive_t)start_value;
+ snprintf(integer, sizeof(integer), "%" PRIi64, diff);
+ break;
+ }
+ case DS_TYPE_COUNTER: {
+ counter_t diff = counter_diff((counter_t)start_value, v.counter);
+ snprintf(integer, sizeof(integer), "%llu", diff);
+ break;
+ }
+ case DS_TYPE_ABSOLUTE: {
+ snprintf(integer, sizeof(integer), "%" PRIu64, v.absolute);
+ break;
+ }
+ default: {
+ ERROR("format_typed_value: unknown value type %d.", ds_type);
+ return EINVAL;
+ }
+ }
+
+ int status = json_string(gen, "int64Value") || json_string(gen, integer);
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_typed_value */
+
+/* MetricKind
+ *
+ * enum(
+ * "CUMULATIVE",
+ * "GAUGE"
+ * )
+*/
+static int format_metric_kind(yajl_gen gen, int ds_type) {
+ switch (ds_type) {
+ case DS_TYPE_GAUGE:
+ case DS_TYPE_ABSOLUTE:
+ return json_string(gen, "GAUGE");
+ case DS_TYPE_COUNTER:
+ case DS_TYPE_DERIVE:
+ return json_string(gen, "CUMULATIVE");
+ default:
+ ERROR("format_metric_kind: unknown value type %d.", ds_type);
+ return EINVAL;
+ }
+}
+
+/* ValueType
+ *
+ * enum(
+ * "DOUBLE",
+ * "INT64"
+ * )
+*/
+static int format_value_type(yajl_gen gen, int ds_type) {
+ return json_string(gen, (ds_type == DS_TYPE_GAUGE) ? "DOUBLE" : "INT64");
+}
+
+static int metric_type(char *buffer, size_t buffer_size, data_set_t const *ds,
+ value_list_t const *vl, int ds_index) {
+ /* {{{ */
+ char const *ds_name = ds->ds[ds_index].name;
+
+#define GCM_PREFIX "custom.googleapis.com/collectd/"
+ if ((ds_index != 0) || strcmp("value", ds_name) != 0) {
+ snprintf(buffer, buffer_size, GCM_PREFIX "%s/%s_%s", vl->plugin, vl->type,
+ ds_name);
+ } else {
+ snprintf(buffer, buffer_size, GCM_PREFIX "%s/%s", vl->plugin, vl->type);
+ }
+
+ char const *whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789_/";
+ char *ptr = buffer + strlen(GCM_PREFIX);
+ size_t ok_len;
+ while ((ok_len = strspn(ptr, whitelist)) != strlen(ptr)) {
+ ptr[ok_len] = '_';
+ ptr += ok_len;
+ }
+
+ return 0;
+} /* }}} int metric_type */
+
+/* The metric type, including its DNS name prefix. The type is not URL-encoded.
+ * All user-defined custom metric types have the DNS name custom.googleapis.com.
+ * Metric types should use a natural hierarchical grouping. */
+static int format_metric_type(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index) {
+ /* {{{ */
+ char buffer[4 * DATA_MAX_NAME_LEN];
+ metric_type(buffer, sizeof(buffer), ds, vl, ds_index);
+
+ return json_string(gen, buffer);
+} /* }}} int format_metric_type */
+
+/* TimeInterval
+ *
+ * {
+ * "endTime": string,
+ * "startTime": string,
+ * }
+ */
+static int format_time_interval(yajl_gen gen, int ds_type,
+ value_list_t const *vl, cdtime_t start_time) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "endTime") || json_time(gen, vl->time);
+ if (status != 0)
+ return status;
+
+ if ((ds_type == DS_TYPE_DERIVE) || (ds_type == DS_TYPE_COUNTER)) {
+ int status = json_string(gen, "startTime") || json_time(gen, start_time);
+ if (status != 0)
+ return status;
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_time_interval */
+
+/* read_cumulative_state reads the start time and start value of cumulative
+ * (i.e. DERIVE or COUNTER) metrics from the cache. If a metric is seen for the
+ * first time, or when a DERIVE metric is reset, the start time is (re)set to
+ * vl->time. */
+static int read_cumulative_state(data_set_t const *ds, value_list_t const *vl,
+ int ds_index, cdtime_t *ret_start_time,
+ int64_t *ret_start_value) {
+ int ds_type = ds->ds[ds_index].type;
+ if ((ds_type != DS_TYPE_DERIVE) && (ds_type != DS_TYPE_COUNTER)) {
+ return 0;
+ }
+
+ char start_value_key[DATA_MAX_NAME_LEN];
+ snprintf(start_value_key, sizeof(start_value_key),
+ "stackdriver:start_value[%d]", ds_index);
+
+ int status =
+ uc_meta_data_get_signed_int(vl, start_value_key, ret_start_value);
+ if ((status == 0) && ((ds_type != DS_TYPE_DERIVE) ||
+ (*ret_start_value <= vl->values[ds_index].derive))) {
+ return uc_meta_data_get_unsigned_int(vl, "stackdriver:start_time",
+ ret_start_time);
+ }
+
+ if (ds_type == DS_TYPE_DERIVE) {
+ *ret_start_value = vl->values[ds_index].derive;
+ } else {
+ *ret_start_value = (int64_t)vl->values[ds_index].counter;
+ }
+ *ret_start_time = vl->time;
+
+ status = uc_meta_data_add_signed_int(vl, start_value_key, *ret_start_value);
+ if (status != 0) {
+ return status;
+ }
+ return uc_meta_data_add_unsigned_int(vl, "stackdriver:start_time",
+ *ret_start_time);
+} /* int read_cumulative_state */
+
+/* Point
+ *
+ * {
+ * "interval": {
+ * object(TimeInterval)
+ * },
+ * "value": {
+ * object(TypedValue)
+ * },
+ * }
+ */
+static int format_point(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index,
+ cdtime_t start_time, int64_t start_value) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int ds_type = ds->ds[ds_index].type;
+
+ int status =
+ json_string(gen, "interval") ||
+ format_time_interval(gen, ds_type, vl, start_time) ||
+ json_string(gen, "value") ||
+ format_typed_value(gen, ds_type, vl->values[ds_index], start_value);
+ if (status != 0)
+ return status;
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_point */
+
+/* Metric
+ *
+ * {
+ * "type": string,
+ * "labels": {
+ * string: string,
+ * ...
+ * },
+ * }
+ */
+static int format_metric(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "type") ||
+ format_metric_type(gen, ds, vl, ds_index) ||
+ json_string(gen, "labels");
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_map_open(gen);
+ status = json_string(gen, "host") || json_string(gen, vl->host) ||
+ json_string(gen, "plugin_instance") ||
+ json_string(gen, vl->plugin_instance) ||
+ json_string(gen, "type_instance") ||
+ json_string(gen, vl->type_instance);
+ if (status != 0) {
+ return status;
+ }
+ yajl_gen_map_close(gen);
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_metric */
+
+/* TimeSeries
+ *
+ * {
+ * "metric": {
+ * object(Metric)
+ * },
+ * "resource": {
+ * object(MonitoredResource)
+ * },
+ * "metricKind": enum(MetricKind),
+ * "valueType": enum(ValueType),
+ * "points": [
+ * {
+ * object(Point)
+ * }
+ * ],
+ * }
+ */
+/* format_time_series formats a TimeSeries object. Returns EAGAIN when a
+ * cumulative metric is seen for the first time and cannot be sent to
+ * Stackdriver due to lack of state. */
+static int format_time_series(yajl_gen gen, data_set_t const *ds,
+ value_list_t const *vl, int ds_index,
+ sd_resource_t *res) {
+ int ds_type = ds->ds[ds_index].type;
+
+ cdtime_t start_time = 0;
+ int64_t start_value = 0;
+ int status =
+ read_cumulative_state(ds, vl, ds_index, &start_time, &start_value);
+ if (status != 0) {
+ return status;
+ }
+ if (start_time == vl->time) {
+ /* for cumulative metrics, the interval must not be zero. */
+ return EAGAIN;
+ }
+
+ yajl_gen_map_open(gen);
+
+ status = json_string(gen, "metric") || format_metric(gen, ds, vl, ds_index) ||
+ json_string(gen, "resource") || format_gcm_resource(gen, res) ||
+ json_string(gen, "metricKind") || format_metric_kind(gen, ds_type) ||
+ json_string(gen, "valueType") || format_value_type(gen, ds_type) ||
+ json_string(gen, "points");
+ if (status != 0)
+ return status;
+
+ yajl_gen_array_open(gen);
+
+ status = format_point(gen, ds, vl, ds_index, start_time, start_value);
+ if (status != 0)
+ return status;
+
+ yajl_gen_array_close(gen);
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_time_series */
+
+/* Request body
+ *
+ * {
+ * "timeSeries": [
+ * {
+ * object(TimeSeries)
+ * }
+ * ],
+ * }
+ */
+static int sd_output_initialize(sd_output_t *out) /* {{{ */
+{
+ yajl_gen_map_open(out->gen);
+
+ int status = json_string(out->gen, "timeSeries");
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_array_open(out->gen);
+ return 0;
+} /* }}} int sd_output_initialize */
+
+static int sd_output_finalize(sd_output_t *out) /* {{{ */
+{
+ yajl_gen_array_close(out->gen);
+ yajl_gen_map_close(out->gen);
+
+ return 0;
+} /* }}} int sd_output_finalize */
+
+static void sd_output_reset_staged(sd_output_t *out) /* {{{ */
+{
+ void *key = NULL;
+
+ while (c_avl_pick(out->staged, &key, &(void *){NULL}) == 0)
+ sfree(key);
+} /* }}} void sd_output_reset_staged */
+
+sd_output_t *sd_output_create(sd_resource_t *res) /* {{{ */
+{
+ sd_output_t *out = calloc(1, sizeof(*out));
+ if (out == NULL)
+ return NULL;
+
+ out->res = res;
+
+ out->gen = yajl_gen_alloc(/* funcs = */ NULL);
+ if (out->gen == NULL) {
+ sd_output_destroy(out);
+ return NULL;
+ }
+
+ out->staged = c_avl_create((void *)strcmp);
+ if (out->staged == NULL) {
+ sd_output_destroy(out);
+ return NULL;
+ }
+
+ out->metric_descriptors = c_avl_create((void *)strcmp);
+ if (out->metric_descriptors == NULL) {
+ sd_output_destroy(out);
+ return NULL;
+ }
+
+ sd_output_initialize(out);
+
+ return out;
+} /* }}} sd_output_t *sd_output_create */
+
+void sd_output_destroy(sd_output_t *out) /* {{{ */
+{
+ if (out == NULL)
+ return;
+
+ if (out->metric_descriptors != NULL) {
+ void *key = NULL;
+ while (c_avl_pick(out->metric_descriptors, &key, &(void *){NULL}) == 0) {
+ sfree(key);
+ }
+ c_avl_destroy(out->metric_descriptors);
+ out->metric_descriptors = NULL;
+ }
+
+ if (out->staged != NULL) {
+ sd_output_reset_staged(out);
+ c_avl_destroy(out->staged);
+ out->staged = NULL;
+ }
+
+ if (out->gen != NULL) {
+ yajl_gen_free(out->gen);
+ out->gen = NULL;
+ }
+
+ if (out->res != NULL) {
+ sd_resource_destroy(out->res);
+ out->res = NULL;
+ }
+
+ sfree(out);
+} /* }}} void sd_output_destroy */
+
+int sd_output_add(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl) /* {{{ */
+{
+ /* first, check that we have all appropriate metric descriptors. */
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ char buffer[4 * DATA_MAX_NAME_LEN];
+ metric_type(buffer, sizeof(buffer), ds, vl, i);
+
+ if (c_avl_get(out->metric_descriptors, buffer, NULL) != 0) {
+ return ENOENT;
+ }
+ }
+
+ char key[6 * DATA_MAX_NAME_LEN];
+ int status = FORMAT_VL(key, sizeof(key), vl);
+ if (status != 0) {
+ ERROR("sd_output_add: FORMAT_VL failed with status %d.", status);
+ return status;
+ }
+
+ if (c_avl_get(out->staged, key, NULL) == 0) {
+ return EEXIST;
+ }
+
+ _Bool staged = 0;
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ int status = format_time_series(out->gen, ds, vl, i, out->res);
+ if (status == EAGAIN) {
+ /* first instance of a cumulative metric */
+ continue;
+ }
+ if (status != 0) {
+ ERROR("sd_output_add: format_time_series failed with status %d.", status);
+ return status;
+ }
+ staged = 1;
+ }
+
+ if (staged) {
+ c_avl_insert(out->staged, strdup(key), NULL);
+ }
+
+ size_t json_buffer_size = 0;
+ yajl_gen_get_buf(out->gen, &(unsigned char const *){NULL}, &json_buffer_size);
+ if (json_buffer_size > 65535)
+ return ENOBUFS;
+
+ return 0;
+} /* }}} int sd_output_add */
+
+int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl) {
+ /* {{{ */
+ for (size_t i = 0; i < ds->ds_num; i++) {
+ char buffer[4 * DATA_MAX_NAME_LEN];
+ metric_type(buffer, sizeof(buffer), ds, vl, i);
+
+ char *key = strdup(buffer);
+ int status = c_avl_insert(out->metric_descriptors, key, NULL);
+ if (status != 0) {
+ sfree(key);
+ return status;
+ }
+ }
+
+ return 0;
+} /* }}} int sd_output_register_metric */
+
+char *sd_output_reset(sd_output_t *out) /* {{{ */
+{
+ sd_output_finalize(out);
+
+ unsigned char const *json_buffer = NULL;
+ yajl_gen_get_buf(out->gen, &json_buffer, &(size_t){0});
+ char *ret = strdup((void const *)json_buffer);
+
+ sd_output_reset_staged(out);
+
+ yajl_gen_free(out->gen);
+ out->gen = yajl_gen_alloc(/* funcs = */ NULL);
+
+ sd_output_initialize(out);
+
+ return ret;
+} /* }}} char *sd_output_reset */
+
+sd_resource_t *sd_resource_create(char const *type) /* {{{ */
+{
+ sd_resource_t *res = malloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+ memset(res, 0, sizeof(*res));
+
+ res->type = strdup(type);
+ if (res->type == NULL) {
+ sfree(res);
+ return NULL;
+ }
+
+ res->labels = NULL;
+ res->labels_num = 0;
+
+ return res;
+} /* }}} sd_resource_t *sd_resource_create */
+
+void sd_resource_destroy(sd_resource_t *res) /* {{{ */
+{
+ if (res == NULL)
+ return;
+
+ for (size_t i = 0; i < res->labels_num; i++) {
+ sfree(res->labels[i].key);
+ sfree(res->labels[i].value);
+ }
+ sfree(res->labels);
+ sfree(res->type);
+ sfree(res);
+} /* }}} void sd_resource_destroy */
+
+int sd_resource_add_label(sd_resource_t *res, char const *key,
+ char const *value) /* {{{ */
+{
+ if ((res == NULL) || (key == NULL) || (value == NULL))
+ return EINVAL;
+
+ sd_label_t *l =
+ realloc(res->labels, sizeof(*res->labels) * (res->labels_num + 1));
+ if (l == NULL)
+ return ENOMEM;
+
+ res->labels = l;
+ l = res->labels + res->labels_num;
+
+ l->key = strdup(key);
+ l->value = strdup(value);
+ if ((l->key == NULL) || (l->value == NULL)) {
+ sfree(l->key);
+ sfree(l->value);
+ return ENOMEM;
+ }
+
+ res->labels_num++;
+ return 0;
+} /* }}} int sd_resource_add_label */
+
+/* LabelDescriptor
+ *
+ * {
+ * "key": string,
+ * "valueType": enum(ValueType),
+ * "description": string,
+ * }
+ */
+static int format_label_descriptor(yajl_gen gen, char const *key) {
+ /* {{{ */
+ yajl_gen_map_open(gen);
+
+ int status = json_string(gen, "key") || json_string(gen, key) ||
+ json_string(gen, "valueType") || json_string(gen, "STRING");
+ if (status != 0) {
+ return status;
+ }
+
+ yajl_gen_map_close(gen);
+ return 0;
+} /* }}} int format_label_descriptor */
+
+/* MetricDescriptor
+ *
+ * {
+ * "name": string,
+ * "type": string,
+ * "labels": [
+ * {
+ * object(LabelDescriptor)
+ * }
+ * ],
+ * "metricKind": enum(MetricKind),
+ * "valueType": enum(ValueType),
+ * "unit": string,
+ * "description": string,
+ * "displayName": string,
+ * }
+ */
+int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
+ data_set_t const *ds, value_list_t const *vl,
+ int ds_index) {
+ /* {{{ */
+ yajl_gen gen = yajl_gen_alloc(/* funcs = */ NULL);
+ if (gen == NULL) {
+ return ENOMEM;
+ }
+
+ int ds_type = ds->ds[ds_index].type;
+
+ yajl_gen_map_open(gen);
+
+ int status =
+ json_string(gen, "type") || format_metric_type(gen, ds, vl, ds_index) ||
+ json_string(gen, "metricKind") || format_metric_kind(gen, ds_type) ||
+ json_string(gen, "valueType") || format_value_type(gen, ds_type) ||
+ json_string(gen, "labels");
+ if (status != 0) {
+ yajl_gen_free(gen);
+ return status;
+ }
+
+ char const *labels[] = {"host", "plugin_instance", "type_instance"};
+ yajl_gen_array_open(gen);
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE(labels); i++) {
+ int status = format_label_descriptor(gen, labels[i]);
+ if (status != 0) {
+ yajl_gen_free(gen);
+ return status;
+ }
+ }
+
+ yajl_gen_array_close(gen);
+ yajl_gen_map_close(gen);
+
+ unsigned char const *tmp = NULL;
+ yajl_gen_get_buf(gen, &tmp, &(size_t){0});
+ sstrncpy(buffer, (void const *)tmp, buffer_size);
+
+ yajl_gen_free(gen);
+ return 0;
+} /* }}} int sd_format_metric_descriptor */
--- /dev/null
+/**
+ * collectd - src/utils_format_stackdriver.h
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_FORMAT_STACKDRIVER_H
+#define UTILS_FORMAT_STACKDRIVER_H 1
+
+#include "collectd.h"
+#include "plugin.h"
+
+/* sd_output_t is a buffer to which value_list_t* can be added and from which
+ * an appropriately formatted char* can be read. */
+struct sd_output_s;
+typedef struct sd_output_s sd_output_t;
+
+/* sd_resource_t represents a MonitoredResource. */
+struct sd_resource_s;
+typedef struct sd_resource_s sd_resource_t;
+
+sd_output_t *sd_output_create(sd_resource_t *res);
+
+/* sd_output_destroy frees all memory used by out, including the
+ * sd_resource_t* passed to sd_output_create. */
+void sd_output_destroy(sd_output_t *out);
+
+/* sd_output_add adds a value_list_t* to "out".
+ *
+ * Return values:
+ * - 0 Success
+ * - ENOBUFS Success, but the buffer should be flushed soon.
+ * - EEXIST The value list is already encoded in the buffer.
+ * Flush the buffer, then call sd_output_add again.
+ * - ENOENT First time we encounter this metric. Create a metric descriptor
+ * using the Stackdriver API and then call
+ * sd_output_register_metric.
+ */
+int sd_output_add(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl);
+
+/* sd_output_register_metric adds the metric descriptor which vl maps to, to
+ * the list of known metric descriptors. */
+int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
+ value_list_t const *vl);
+
+/* sd_output_reset resets the output and returns the previous content of the
+ * buffer. It is the caller's responsibility to call free() with the returned
+ * pointer. */
+char *sd_output_reset(sd_output_t *out);
+
+sd_resource_t *sd_resource_create(char const *type);
+void sd_resource_destroy(sd_resource_t *res);
+int sd_resource_add_label(sd_resource_t *res, char const *key,
+ char const *value);
+
+/* sd_format_metric_descriptor creates the payload for a
+ * projects.metricDescriptors.create() request. */
+int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
+ data_set_t const *ds, value_list_t const *vl,
+ int ds_index);
+
+#endif /* UTILS_FORMAT_STACKDRIVER_H */
--- /dev/null
+/**
+ * collectd - src/utils_format_stackdriver_test.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "testing.h"
+#include "utils/format_stackdriver/format_stackdriver.h"
+
+DEF_TEST(sd_format_metric_descriptor) {
+ value_list_t vl = {
+ .host = "example.com", .plugin = "unit-test", .type = "example",
+ };
+ char got[1024];
+
+ data_set_t ds_single = {
+ .type = "example",
+ .ds_num = 1,
+ .ds =
+ &(data_source_t){
+ .name = "value", .type = DS_TYPE_GAUGE, .min = NAN, .max = NAN,
+ },
+ };
+ EXPECT_EQ_INT(
+ 0, sd_format_metric_descriptor(got, sizeof(got), &ds_single, &vl, 0));
+ char const *want_single =
+ "{\"type\":\"custom.googleapis.com/collectd/unit_test/"
+ "example\",\"metricKind\":\"GAUGE\",\"valueType\":\"DOUBLE\",\"labels\":["
+ "{\"key\":\"host\",\"valueType\":\"STRING\"},{\"key\":\"plugin_"
+ "instance\",\"valueType\":\"STRING\"},{\"key\":\"type_instance\","
+ "\"valueType\":\"STRING\"}]}";
+ EXPECT_EQ_STR(want_single, got);
+
+ data_set_t ds_double = {
+ .type = "example",
+ .ds_num = 2,
+ .ds =
+ (data_source_t[]){
+ {.name = "one", .type = DS_TYPE_DERIVE, .min = 0, .max = NAN},
+ {.name = "two", .type = DS_TYPE_DERIVE, .min = 0, .max = NAN},
+ },
+ };
+ EXPECT_EQ_INT(
+ 0, sd_format_metric_descriptor(got, sizeof(got), &ds_double, &vl, 0));
+ char const *want_double =
+ "{\"type\":\"custom.googleapis.com/collectd/unit_test/"
+ "example_one\",\"metricKind\":\"CUMULATIVE\",\"valueType\":\"INT64\","
+ "\"labels\":[{\"key\":\"host\",\"valueType\":\"STRING\"},{\"key\":"
+ "\"plugin_instance\",\"valueType\":\"STRING\"},{\"key\":\"type_"
+ "instance\",\"valueType\":\"STRING\"}]}";
+ EXPECT_EQ_STR(want_double, got);
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ RUN_TEST(sd_format_metric_descriptor);
+
+ END_TEST;
+}
--- /dev/null
+/**
+ * collectd - src/utils_gce.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/gce/gce.h"
+#include "utils/oauth/oauth.h"
+#include "utils_time.h"
+
+#include <curl/curl.h>
+
+#ifndef GCP_METADATA_PREFIX
+#define GCP_METADATA_PREFIX "http://metadata.google.internal/computeMetadata/v1"
+#endif
+#ifndef GCE_METADATA_HEADER
+#define GCE_METADATA_HEADER "Metadata-Flavor: Google"
+#endif
+
+#ifndef GCE_INSTANCE_ID_URL
+#define GCE_INSTANCE_ID_URL GCP_METADATA_PREFIX "/instance/id"
+#endif
+#ifndef GCE_PROJECT_NUM_URL
+#define GCE_PROJECT_NUM_URL GCP_METADATA_PREFIX "/project/numeric-project-id"
+#endif
+#ifndef GCE_PROJECT_ID_URL
+#define GCE_PROJECT_ID_URL GCP_METADATA_PREFIX "/project/project-id"
+#endif
+#ifndef GCE_ZONE_URL
+#define GCE_ZONE_URL GCP_METADATA_PREFIX "/instance/zone"
+#endif
+
+#ifndef GCE_DEFAULT_SERVICE_ACCOUNT
+#define GCE_DEFAULT_SERVICE_ACCOUNT "default"
+#endif
+
+#ifndef GCE_SCOPE_URL
+#define GCE_SCOPE_URL_FORMAT \
+ GCP_METADATA_PREFIX "/instance/service-accounts/%s/scopes"
+#endif
+#ifndef GCE_TOKEN_URL
+#define GCE_TOKEN_URL_FORMAT \
+ GCP_METADATA_PREFIX "/instance/service-accounts/%s/token"
+#endif
+
+struct blob_s {
+ char *data;
+ size_t size;
+};
+typedef struct blob_s blob_t;
+
+static int on_gce = -1;
+
+static char *token = NULL;
+static char *token_email = NULL;
+static cdtime_t token_valid_until = 0;
+static pthread_mutex_t token_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static size_t write_callback(void *contents, size_t size, size_t nmemb,
+ void *ud) /* {{{ */
+{
+ size_t realsize = size * nmemb;
+ blob_t *blob = ud;
+
+ if ((0x7FFFFFF0 < blob->size) || (0x7FFFFFF0 - blob->size < realsize)) {
+ ERROR("utils_gce: write_callback: integer overflow");
+ return 0;
+ }
+
+ blob->data = realloc(blob->data, blob->size + realsize + 1);
+ if (blob->data == NULL) {
+ /* out of memory! */
+ ERROR(
+ "utils_gce: write_callback: not enough memory (realloc returned NULL)");
+ return 0;
+ }
+
+ memcpy(blob->data + blob->size, contents, realsize);
+ blob->size += realsize;
+ blob->data[blob->size] = 0;
+
+ return realsize;
+} /* }}} size_t write_callback */
+
+/* read_url will issue a GET request for the given URL, setting the magic GCE
+ * metadata header in the process. On success, the response body is returned
+ * and it's the caller's responsibility to free it. On failure, an error is
+ * logged and NULL is returned. */
+static char *read_url(char const *url) /* {{{ */
+{
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ ERROR("utils_gce: curl_easy_init failed.");
+ return NULL;
+ }
+
+ struct curl_slist *headers = curl_slist_append(NULL, GCE_METADATA_HEADER);
+
+ char curl_errbuf[CURL_ERROR_SIZE];
+ blob_t blob = {0};
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &blob);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+
+ int status = curl_easy_perform(curl);
+ if (status != CURLE_OK) {
+ ERROR("utils_gce: fetching %s failed: %s", url, curl_errbuf);
+ sfree(blob.data);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ return NULL;
+ }
+
+ long http_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+ if ((http_code < 200) || (http_code >= 300)) {
+ ERROR("write_gcm plugin: fetching %s failed: HTTP error %ld", url,
+ http_code);
+ sfree(blob.data);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ return NULL;
+ }
+
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ return blob.data;
+} /* }}} char *read_url */
+
+_Bool gce_check(void) /* {{{ */
+{
+ if (on_gce != -1)
+ return on_gce == 1;
+
+ DEBUG("utils_gce: Checking whether I'm running on GCE ...");
+
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ ERROR("utils_gce: curl_easy_init failed.");
+ return 0;
+ }
+
+ struct curl_slist *headers = curl_slist_append(NULL, GCE_METADATA_HEADER);
+
+ char curl_errbuf[CURL_ERROR_SIZE];
+ blob_t blob = {NULL, 0};
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_callback);
+ curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &blob);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_URL, GCP_METADATA_PREFIX "/");
+
+ int status = curl_easy_perform(curl);
+ if ((status != CURLE_OK) || (blob.data == NULL) ||
+ (strstr(blob.data, "Metadata-Flavor: Google") == NULL)) {
+ DEBUG("utils_gce: ... no (%s)",
+ (status != CURLE_OK)
+ ? "curl_easy_perform failed"
+ : (blob.data == NULL) ? "blob.data == NULL"
+ : "Metadata-Flavor header not found");
+ sfree(blob.data);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ on_gce = 0;
+ return 0;
+ }
+ sfree(blob.data);
+
+ long http_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+ if ((http_code < 200) || (http_code >= 300)) {
+ DEBUG("utils_gce: ... no (HTTP status %ld)", http_code);
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ on_gce = 0;
+ return 0;
+ }
+
+ DEBUG("utils_gce: ... yes");
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(headers);
+ on_gce = 1;
+ return 1;
+} /* }}} _Bool gce_check */
+
+char *gce_project_id(void) /* {{{ */
+{
+ return read_url(GCE_PROJECT_ID_URL);
+} /* }}} char *gce_project_id */
+
+char *gce_instance_id(void) /* {{{ */
+{
+ return read_url(GCE_INSTANCE_ID_URL);
+} /* }}} char *gce_instance_id */
+
+char *gce_zone(void) /* {{{ */
+{
+ return read_url(GCE_ZONE_URL);
+} /* }}} char *gce_instance_id */
+
+char *gce_scope(char const *email) /* {{{ */
+{
+ char url[1024];
+
+ snprintf(url, sizeof(url), GCE_SCOPE_URL_FORMAT,
+ (email != NULL) ? email : GCE_DEFAULT_SERVICE_ACCOUNT);
+
+ return read_url(url);
+} /* }}} char *gce_scope */
+
+int gce_access_token(char const *email, char *buffer,
+ size_t buffer_size) /* {{{ */
+{
+ char url[1024];
+ char *json;
+ cdtime_t now = cdtime();
+
+ pthread_mutex_lock(&token_lock);
+
+ if (email == NULL)
+ email = GCE_DEFAULT_SERVICE_ACCOUNT;
+
+ if ((token_email != NULL) && (strcmp(email, token_email) == 0) &&
+ (token_valid_until > now)) {
+ sstrncpy(buffer, token, buffer_size);
+ pthread_mutex_unlock(&token_lock);
+ return 0;
+ }
+
+ snprintf(url, sizeof(url), GCE_TOKEN_URL_FORMAT, email);
+ json = read_url(url);
+ if (json == NULL) {
+ pthread_mutex_unlock(&token_lock);
+ return -1;
+ }
+
+ char tmp[256];
+ cdtime_t expires_in = 0;
+ int status = oauth_parse_json_token(json, tmp, sizeof(tmp), &expires_in);
+ sfree(json);
+ if (status != 0) {
+ pthread_mutex_unlock(&token_lock);
+ return status;
+ }
+
+ sfree(token);
+ token = strdup(tmp);
+
+ sfree(token_email);
+ token_email = strdup(email);
+
+ /* let tokens expire a bit early */
+ expires_in = (expires_in * 95) / 100;
+ token_valid_until = now + expires_in;
+
+ sstrncpy(buffer, token, buffer_size);
+ pthread_mutex_unlock(&token_lock);
+ return 0;
+} /* }}} char *gce_token */
--- /dev/null
+/**
+ * collectd - src/utils_gce.h
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_GCE_H
+#define UTILS_GCE_H 1
+
+/* gce_check returns 1 when running on Google Compute Engine (GCE) and 0
+ * otherwise. */
+_Bool gce_check(void);
+
+/* gce_project_id returns the project ID of the instance, as configured when
+ * creating the project.
+ * For example "example-project-a". */
+char *gce_project_id(void);
+
+/* gce_instance_id returns the unique ID of the GCE instance. */
+char *gce_instance_id(void);
+
+/* gce_zone returns the zone in which the GCE instance runs. */
+char *gce_zone(void);
+
+/* gce_scope returns the list of scopes for the given service account (or the
+ * default service account when NULL is passed). */
+char *gce_scope(char const *email);
+
+/* gce_access_token acquires an OAuth access token for the given service account
+ * (or
+ * the default service account when NULL is passed) and stores it in buffer.
+ * Access tokens are automatically cached and renewed when they expire. Returns
+ * zero on success, non-zero otherwise. */
+int gce_access_token(char const *email, char *buffer, size_t buffer_size);
+
+#endif
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ */
+
+#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;
+}
--- /dev/null
+/**
+ * collectd - src/utils_ignorelist.c
+ * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
+ * Copyright (C) 2008 Florian Forster <octo at collectd.org>
+ *
+ * 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 <lubek at users.sourceforge.net>
+ * Florian Forster <octo at collectd.org>
+ **/
+/**
+ * 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) */
--- /dev/null
+/**
+ * collectd - src/utils_ignorelist.h
+ * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
+ *
+ * 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 <lubek at users.sourceforge.net>
+ **/
+/**
+ * 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 <regex.h>
+#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 */
--- /dev/null
+/**
+ * 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 <ff at octo.it>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/latency/latency.h"
+
+#include <limits.h>
+#include <math.h>
+
+#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 */
--- /dev/null
+/**
+ * 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 <ff at octo.it>
+ **/
+
+#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);
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ * Pavel Rochnyack <pavel2000 at ngs.ru>
+ */
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ * Pavel Rochnyack <pavel2000 at ngs.ru>
+ */
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ */
+
+#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;
+}
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include <pthread.h>
+#include <regex.h>
+
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
+#include "utils/lookup/vl_lookup.h"
+
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#include "utils/match/match.h"
+
+#include <regex.h>
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ */
+
+#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;
+}
--- /dev/null
+/**
+ * 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 <niki.waibel@gmx.net>
+**/
+
+#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 <xfs/xqm.h>
+#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 <sys/types.h>
+#endif
+#if HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+/* #endif HAVE_GETVFSSTAT */
+
+#elif HAVE_GETFSSTAT
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#if HAVE_SYS_UCRED_H
+#include <sys/ucred.h>
+#endif
+#if HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#endif /* HAVE_GETFSSTAT */
+
+#if HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#if HAVE_SYS_MNTTAB_H
+#include <sys/mnttab.h>
+#endif
+
+#if HAVE_PATHS_H
+#include <paths.h>
+#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) */
--- /dev/null
+/**
+ * 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 <niki.waibel@gmx.net>
+**/
+
+/* See below for instructions how to use the public functions. */
+
+#ifndef COLLECTD_UTILS_MOUNT_H
+#define COLLECTD_UTILS_MOUNT_H 1
+
+#include <stdio.h>
+#if HAVE_FS_INFO_H
+#include <fs_info.h>
+#endif
+#if HAVE_FSHELP_H
+#include <fshelp.h>
+#endif
+#if HAVE_PATHS_H
+#include <paths.h>
+#endif
+#if HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#if HAVE_MNTTAB_H
+#include <mnttab.h>
+#endif
+#if HAVE_SYS_FSTYP_H
+#include <sys/fstyp.h>
+#endif
+#if HAVE_SYS_FS_TYPES_H
+#include <sys/fs_types.h>
+#endif
+#if HAVE_SYS_MNTENT_H
+#include <sys/mntent.h>
+#endif
+#if HAVE_SYS_MNTTAB_H
+#include <sys/mnttab.h>
+#endif
+#if HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#if HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
+#if HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+#if HAVE_SYS_VFSTAB_H
+#include <sys/vfstab.h>
+#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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ */
+
+#include "collectd.h"
+
+#include "testing.h"
+#include "utils/common/common.h"
+#include "utils/mount/mount.h"
+
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#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;
+}
--- /dev/null
+/**
+ * collectd - src/utils_oauth.c
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/oauth/oauth.h"
+
+#include <curl/curl.h>
+
+#include <yajl/yajl_tree.h>
+#include <yajl/yajl_version.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/sha.h>
+
+/*
+ * Private variables
+ */
+#define GOOGLE_TOKEN_URL "https://accounts.google.com/o/oauth2/token"
+
+/* Max send buffer size, since there will be only one writer thread and
+ * monitoring api supports up to 100K bytes in one request, 64K is reasonable
+ */
+#define MAX_BUFFER_SIZE 65536
+#define MAX_ENCODE_SIZE 2048
+
+struct oauth_s {
+ char *url;
+ char *iss;
+ char *aud;
+ char *scope;
+
+ EVP_PKEY *key;
+
+ char *token;
+ cdtime_t valid_until;
+};
+
+struct memory_s {
+ char *memory;
+ size_t size;
+};
+typedef struct memory_s memory_t;
+
+#define OAUTH_GRANT_TYPE "urn:ietf:params:oauth:grant-type:jwt-bearer"
+#define OAUTH_EXPIRATION_TIME TIME_T_TO_CDTIME_T(3600)
+#define OAUTH_HEADER "{\"alg\":\"RS256\",\"typ\":\"JWT\"}"
+
+static const char OAUTH_CLAIM_FORMAT[] = "{"
+ "\"iss\":\"%s\","
+ "\"scope\":\"%s\","
+ "\"aud\":\"%s\","
+ "\"exp\":%lu,"
+ "\"iat\":%lu"
+ "}";
+
+static size_t write_memory(void *contents, size_t size, size_t nmemb, /* {{{ */
+ void *userp) {
+ size_t realsize = size * nmemb;
+ memory_t *mem = (memory_t *)userp;
+ char *tmp;
+
+ if (0x7FFFFFF0 < mem->size || 0x7FFFFFF0 - mem->size < realsize) {
+ ERROR("integer overflow");
+ return 0;
+ }
+
+ tmp = (char *)realloc((void *)mem->memory, mem->size + realsize + 1);
+ if (tmp == NULL) {
+ /* out of memory! */
+ ERROR("write_memory: not enough memory (realloc returned NULL)");
+ return 0;
+ }
+ mem->memory = tmp;
+
+ memcpy(&(mem->memory[mem->size]), contents, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+
+ return realsize;
+} /* }}} size_t write_memory */
+
+/* Base64-encodes "s" and stores the result in buffer.
+ * Returns zero on success, non-zero otherwise. */
+static int base64_encode_n(char const *s, size_t s_size, /* {{{ */
+ char *buffer, size_t buffer_size) {
+ BIO *b64;
+ BUF_MEM *bptr;
+ int status;
+ size_t i;
+
+ /* Set up the memory-base64 chain */
+ b64 = BIO_new(BIO_f_base64());
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ b64 = BIO_push(b64, BIO_new(BIO_s_mem()));
+
+ /* Write data to the chain */
+ BIO_write(b64, (void const *)s, s_size);
+ status = BIO_flush(b64);
+ if (status != 1) {
+ ERROR("utils_oauth: base64_encode: BIO_flush() failed.");
+ BIO_free_all(b64);
+ return -1;
+ }
+
+ /* Never fails */
+ BIO_get_mem_ptr(b64, &bptr);
+
+ if (buffer_size <= bptr->length) {
+ ERROR("utils_oauth: base64_encode: Buffer too small.");
+ BIO_free_all(b64);
+ return -1;
+ }
+
+ /* Copy data to buffer. */
+ memcpy(buffer, bptr->data, bptr->length);
+ buffer[bptr->length] = 0;
+
+ /* replace + with -, / with _ and remove padding = at the end */
+ for (i = 0; i < bptr->length; i++) {
+ if (buffer[i] == '+') {
+ buffer[i] = '-';
+ } else if (buffer[i] == '/') {
+ buffer[i] = '_';
+ } else if (buffer[i] == '=') {
+ buffer[i] = 0;
+ }
+ }
+
+ BIO_free_all(b64);
+ return 0;
+} /* }}} int base64_encode_n */
+
+/* Base64-encodes "s" and stores the result in buffer.
+ * Returns zero on success, non-zero otherwise. */
+static int base64_encode(char const *s, /* {{{ */
+ char *buffer, size_t buffer_size) {
+ return base64_encode_n(s, strlen(s), buffer, buffer_size);
+} /* }}} int base64_encode */
+
+/* get_header returns the base64 encoded OAuth header. */
+static int get_header(char *buffer, size_t buffer_size) /* {{{ */
+{
+ char header[] = OAUTH_HEADER;
+
+ return base64_encode(header, buffer, buffer_size);
+} /* }}} int get_header */
+
+/* get_claim constructs an OAuth claim and returns it as base64 encoded string.
+ */
+static int get_claim(oauth_t *auth, char *buffer, size_t buffer_size) /* {{{ */
+{
+ char claim[buffer_size];
+ cdtime_t exp;
+ cdtime_t iat;
+ int status;
+
+ iat = cdtime();
+ exp = iat + OAUTH_EXPIRATION_TIME;
+
+ /* create the claim set */
+ status =
+ snprintf(claim, sizeof(claim), OAUTH_CLAIM_FORMAT, auth->iss, auth->scope,
+ auth->aud, (unsigned long)CDTIME_T_TO_TIME_T(exp),
+ (unsigned long)CDTIME_T_TO_TIME_T(iat));
+ if (status < 1)
+ return -1;
+ else if ((size_t)status >= sizeof(claim))
+ return ENOMEM;
+
+ DEBUG("utils_oauth: get_claim() = %s", claim);
+
+ return base64_encode(claim, buffer, buffer_size);
+} /* }}} int get_claim */
+
+/* get_signature signs header and claim with pkey and returns the signature in
+ * buffer. */
+static int get_signature(char *buffer, size_t buffer_size, /* {{{ */
+ char const *header, char const *claim,
+ EVP_PKEY *pkey) {
+ char payload[buffer_size];
+ size_t payload_len;
+ char signature[buffer_size];
+ unsigned int signature_size;
+ int status;
+
+ /* Make the string to sign */
+ payload_len = snprintf(payload, sizeof(payload), "%s.%s", header, claim);
+ if (payload_len < 1) {
+ return -1;
+ } else if (payload_len >= sizeof(payload)) {
+ return ENOMEM;
+ }
+
+ /* Create the signature */
+ signature_size = EVP_PKEY_size(pkey);
+ if (signature_size > sizeof(signature)) {
+ ERROR("utils_oauth: Signature is too large (%u bytes).", signature_size);
+ return -1;
+ }
+
+ EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+
+ /* EVP_SignInit(3SSL) claims this is a void function, but in fact it returns
+ * an int. We're not going to rely on this, though. */
+ EVP_SignInit(ctx, EVP_sha256());
+
+ status = EVP_SignUpdate(ctx, payload, payload_len);
+ if (status != 1) {
+ char errbuf[1024];
+ ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+ ERROR("utils_oauth: EVP_SignUpdate failed: %s", errbuf);
+
+ EVP_MD_CTX_free(ctx);
+ return -1;
+ }
+
+ status =
+ EVP_SignFinal(ctx, (unsigned char *)signature, &signature_size, pkey);
+ if (status != 1) {
+ char errbuf[1024];
+ ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+ ERROR("utils_oauth: EVP_SignFinal failed: %s", errbuf);
+
+ EVP_MD_CTX_free(ctx);
+ return -1;
+ }
+
+ EVP_MD_CTX_free(ctx);
+
+ return base64_encode_n(signature, (size_t)signature_size, buffer,
+ buffer_size);
+} /* }}} int get_signature */
+
+static int get_assertion(oauth_t *auth, char *buffer,
+ size_t buffer_size) /* {{{ */
+{
+ char header[buffer_size];
+ char claim[buffer_size];
+ char signature[buffer_size];
+ int status;
+
+ status = get_header(header, sizeof(header));
+ if (status != 0)
+ return -1;
+
+ status = get_claim(auth, claim, sizeof(claim));
+ if (status != 0)
+ return -1;
+
+ status =
+ get_signature(signature, sizeof(signature), header, claim, auth->key);
+ if (status != 0)
+ return -1;
+
+ status = snprintf(buffer, buffer_size, "%s.%s.%s", header, claim, signature);
+ if (status < 1)
+ return -1;
+ else if ((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;
+ }
+
+ snprintf(post_data, sizeof(post_data), "grant_type=%s&assertion=%s",
+ OAUTH_GRANT_TYPE, assertion);
+
+ curl = curl_easy_init();
+ if (curl == NULL) {
+ ERROR("utils_oauth: curl_easy_init failed.");
+ return -1;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
+ curl_easy_setopt(curl, CURLOPT_URL, auth->url);
+
+ status = curl_easy_perform(curl);
+ if (status != CURLE_OK) {
+ ERROR("utils_oauth: curl_easy_perform failed with status %i: %s", status,
+ curl_errbuf);
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return -1;
+ } else {
+ long http_code = 0;
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+ if ((http_code < 200) || (http_code >= 300)) {
+ ERROR("utils_oauth: POST request to %s failed: HTTP error %ld", auth->url,
+ http_code);
+ if (data.memory != NULL)
+ INFO("utils_oauth: Server replied: %s", data.memory);
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return -1;
+ }
+ }
+
+ status = oauth_parse_json_token(data.memory, access_token,
+ sizeof(access_token), &expires_in);
+ if (status != 0) {
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return -1;
+ }
+
+ sfree(auth->token);
+ auth->token = strdup(access_token);
+ if (auth->token == NULL) {
+ ERROR("utils_oauth: strdup failed");
+ auth->valid_until = 0;
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+ return -1;
+ }
+
+ INFO("utils_oauth: OAuth2 access token is valid for %.3fs",
+ CDTIME_T_TO_DOUBLE(expires_in));
+ auth->valid_until = now + expires_in;
+
+ sfree(data.memory);
+ curl_easy_cleanup(curl);
+
+ return 0;
+} /* }}} int new_token */
+
+static int renew_token(oauth_t *auth) /* {{{ */
+{
+ /* Renew OAuth token 30 seconds *before* it expires. */
+ cdtime_t const slack = TIME_T_TO_CDTIME_T(30);
+
+ if (auth->valid_until > (cdtime() + slack))
+ return 0;
+
+ return new_token(auth);
+} /* }}} int renew_token */
+
+static oauth_t *oauth_create(char const *url, char const *iss,
+ char const *scope, char const *aud,
+ EVP_PKEY *key) /* {{{ */
+{
+ oauth_t *auth;
+
+ if ((url == NULL) || (iss == NULL) || (scope == NULL) || (aud == NULL) ||
+ (key == NULL))
+ return NULL;
+
+ auth = malloc(sizeof(*auth));
+ if (auth == NULL)
+ return NULL;
+ memset(auth, 0, sizeof(*auth));
+
+ auth->url = strdup(url);
+ auth->iss = strdup(iss);
+ auth->scope = strdup(scope);
+ auth->aud = strdup(aud);
+
+ if ((auth->url == NULL) || (auth->iss == NULL) || (auth->scope == NULL) ||
+ (auth->aud == NULL)) {
+ oauth_destroy(auth);
+ return NULL;
+ }
+
+ auth->key = key;
+
+ return auth;
+} /* }}} oauth_t *oauth_create */
+
+/*
+ * Public
+ */
+oauth_google_t oauth_create_google_json(char const *buffer, char const *scope) {
+ char errbuf[1024];
+ yajl_val root = yajl_tree_parse(buffer, errbuf, sizeof(errbuf));
+ if (root == NULL) {
+ ERROR("utils_oauth: oauth_create_google_json: parse error %s", errbuf);
+ return (oauth_google_t){NULL};
+ }
+
+ yajl_val field_project =
+ yajl_tree_get(root, (char const *[]){"project_id", NULL}, yajl_t_string);
+ if (field_project == NULL) {
+ ERROR("utils_oauth: oauth_create_google_json: project_id field not found");
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+ char const *project_id = YAJL_GET_STRING(field_project);
+
+ yajl_val field_iss = yajl_tree_get(
+ root, (char const *[]){"client_email", NULL}, yajl_t_string);
+ if (field_iss == NULL) {
+ ERROR(
+ "utils_oauth: oauth_create_google_json: client_email field not found");
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ yajl_val field_token_uri =
+ yajl_tree_get(root, (char const *[]){"token_uri", NULL}, yajl_t_string);
+ char const *token_uri = (field_token_uri != NULL)
+ ? YAJL_GET_STRING(field_token_uri)
+ : GOOGLE_TOKEN_URL;
+
+ yajl_val field_priv_key =
+ yajl_tree_get(root, (char const *[]){"private_key", NULL}, yajl_t_string);
+ if (field_priv_key == NULL) {
+ ERROR("utils_oauth: oauth_create_google_json: private_key field not found");
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ BIO *bp = BIO_new_mem_buf(YAJL_GET_STRING(field_priv_key), -1);
+ EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
+ if (pkey == NULL) {
+ char errbuf[1024];
+ ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+ ERROR(
+ "utils_oauth: oauth_create_google_json: parsing private key failed: %s",
+ errbuf);
+ BIO_free(bp);
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ BIO_free(bp);
+
+ oauth_t *oauth = oauth_create(token_uri, YAJL_GET_STRING(field_iss), scope,
+ token_uri, pkey);
+ if (oauth == NULL) {
+ yajl_tree_free(root);
+ return (oauth_google_t){NULL};
+ }
+
+ oauth_google_t ret = {
+ .project_id = strdup(project_id), .oauth = oauth,
+ };
+
+ yajl_tree_free(root);
+ return ret;
+} /* oauth_google_t oauth_create_google_json */
+
+oauth_google_t oauth_create_google_file(char const *path,
+ char const *scope) { /* {{{ */
+ int fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return (oauth_google_t){NULL};
+
+ struct stat st = {0};
+ if (fstat(fd, &st) != 0) {
+ close(fd);
+ return (oauth_google_t){NULL};
+ }
+
+ size_t buf_size = (size_t)st.st_size;
+ char *buf = calloc(1, buf_size + 1);
+ if (buf == NULL) {
+ close(fd);
+ return (oauth_google_t){NULL};
+ }
+
+ if (sread(fd, buf, buf_size) != 0) {
+ free(buf);
+ close(fd);
+ return (oauth_google_t){NULL};
+ }
+ close(fd);
+ buf[buf_size] = 0;
+
+ oauth_google_t ret = oauth_create_google_json(buf, scope);
+
+ free(buf);
+ return ret;
+} /* }}} oauth_google_t oauth_create_google_file */
+
+/* oauth_create_google_default checks for JSON credentials in well-known
+ * positions, similar to gcloud and other tools. */
+oauth_google_t oauth_create_google_default(char const *scope) {
+ char const *app_creds;
+ if ((app_creds = getenv("GOOGLE_APPLICATION_CREDENTIALS")) != NULL) {
+ oauth_google_t ret = oauth_create_google_file(app_creds, scope);
+ if (ret.oauth == NULL) {
+ ERROR("The environment variable GOOGLE_APPLICATION_CREDENTIALS is set to "
+ "\"%s\" but that file could not be read.",
+ app_creds);
+ } else {
+ return ret;
+ }
+ }
+
+ char const *home;
+ if ((home = getenv("HOME")) != NULL) {
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path),
+ "%s/.config/gcloud/application_default_credentials.json", home);
+
+ oauth_google_t ret = oauth_create_google_file(path, scope);
+ if (ret.oauth != NULL) {
+ return ret;
+ }
+ }
+
+ return (oauth_google_t){NULL};
+} /* }}} oauth_google_t oauth_create_google_default */
+
+void oauth_destroy(oauth_t *auth) /* {{{ */
+{
+ if (auth == NULL)
+ return;
+
+ sfree(auth->url);
+ sfree(auth->iss);
+ sfree(auth->scope);
+ sfree(auth->aud);
+
+ if (auth->key != NULL) {
+ EVP_PKEY_free(auth->key);
+ auth->key = NULL;
+ }
+
+ sfree(auth);
+} /* }}} void oauth_destroy */
+
+int oauth_access_token(oauth_t *auth, char *buffer,
+ size_t buffer_size) /* {{{ */
+{
+ int status;
+
+ if (auth == NULL)
+ return EINVAL;
+
+ status = renew_token(auth);
+ if (status != 0)
+ return status;
+ assert(auth->token != NULL);
+
+ sstrncpy(buffer, auth->token, buffer_size);
+ return 0;
+} /* }}} int oauth_access_token */
--- /dev/null
+/**
+ * collectd - src/utils_oauth.h
+ * ISC license
+ *
+ * Copyright (C) 2017 Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at collectd.org>
+ **/
+
+#ifndef UTILS_OAUTH_H
+#define UTILS_OAUTH_H
+
+#include "collectd.h"
+#include "utils_time.h"
+
+#ifndef GOOGLE_OAUTH_URL
+#define GOOGLE_OAUTH_URL "https://www.googleapis.com/oauth2/v3/token"
+#endif
+
+struct oauth_s;
+typedef struct oauth_s oauth_t;
+
+int oauth_parse_json_token(char const *json, char *out_access_token,
+ size_t access_token_size, cdtime_t *expires_in);
+
+typedef struct {
+ char *project_id;
+ oauth_t *oauth;
+} oauth_google_t;
+
+/* oauth_create_google_json creates an OAuth object from JSON encoded
+ * credentials. */
+oauth_google_t oauth_create_google_json(char const *json, char const *scope);
+
+/* oauth_create_google_file reads path, which contains JSON encoded service
+ * account credentials, and returns an OAuth object. */
+oauth_google_t oauth_create_google_file(char const *path, char const *scope);
+
+/* oauth_create_google_default looks for service account credentials in a couple
+ * of well-known places and returns an OAuth object if found. The well known
+ * locations are:
+ *
+ * - ${GOOGLE_APPLICATION_CREDENTIALS}
+ * - ${HOME}/.config/gcloud/application_default_credentials.json
+ */
+oauth_google_t oauth_create_google_default(char const *scope);
+
+/* oauth_destroy frees all resources associated with an OAuth object. */
+void oauth_destroy(oauth_t *auth);
+
+int oauth_access_token(oauth_t *auth, char *buffer, size_t buffer_size);
+
+#endif
--- /dev/null
+/**
+ * collectd - src/tests/utils_oauth_test.c
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <octo at google.com>
+ **/
+
+#include "testing.h"
+#include "utils/oauth/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 */
--- /dev/null
+/**
+ * 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 <volodymyrx.mytnyk@intel.com>
+ **/
+
+/* 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 <netdb.h>
+#endif
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#if HAVE_POLL_H
+#include <poll.h>
+#endif
+#if HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#include <semaphore.h>
+
+#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: [<json-value>, <table-updates>] */
+ 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 <json-value> */
+ 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();
+ 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(*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
+ * [<db-name>, <json-value>, <monitor-requests>] */
+ OVS_YAJL_CALL(yajl_gen_array_open, jgen);
+ {
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, OVS_DB_DEFAULT_DB_NAME);
+
+ /* uid string <json-value> */
+ snprintf(uid_str, sizeof(uid_str), "%" PRIX64, new_cb->uid);
+ OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_str);
+
+ /* <monitor-requests> */
+ 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);
+ {
+ /* <monitor-request> */
+ 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" : <YAJL return value>
+ * "key_b" : <YAJL return value>
+ * }
+ */
+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:
+ *
+ * <pair>
+ * A 2-element JSON array that represents a pair within a database
+ * map. The first element is an <atom> that represents the key, and
+ * the second element is an <atom> that represents the value.
+ *
+ * <map>
+ * 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 <pair>s giving the
+ * values in the map. All of the <pair>s must have the same key and
+ * value types.
+ *
+ * EXAMPLE:
+ * [
+ * "map", [
+ * [ "key_a", <YAJL value>], [ "key_b", <YAJL value>], ...
+ * ]
+ * ]
+ */
+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;
+}
--- /dev/null
+/**
+ * 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 <volodymyrx.mytnyk@intel.com>
+ *
+ * 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 <yajl/yajl_gen.h>
+#include <yajl/yajl_tree.h>
+
+/* 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
--- /dev/null
+/**
+ * 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 <mateuszx.starzyk@intel.com>
+ * Wojciech Andralojc <wojciechx.andralojc@intel.com>
+ * Michał Aleksiński <michalx.aleksinski@intel.com>
+ **/
+
+#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;
+}
--- /dev/null
+/**
+ * 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 <mateuszx.starzyk@intel.com>
+ * Wojciech Andralojc <wojciechx.andralojc@intel.com>
+ * Michał Aleksiński <michalx.aleksinski@intel.com>
+ **/
+
+#include <dirent.h>
+#include <sys/types.h>
+
+/*
+ * 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);
--- /dev/null
+#include "testing.h"
+#include "utils/proc_pids/proc_pids.c" /* sic */
+#include <sys/stat.h>
+
+/***************************************************************************
+ * 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;
+}
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "utils/common/common.h"
+#include "utils/rrdcreate/rrdcreate.h"
+
+#include <pthread.h>
+#include <rrd.h>
+
+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 = 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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ **/
+
+#ifndef UTILS_RRDCREATE_H
+#define UTILS_RRDCREATE_H 1
+
+#include "plugin.h"
+
+#include <stddef.h>
+
+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 */
--- /dev/null
+/**
+ * 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 <lukeh at c-ware.com>
+ * Florian Forster <octo at collectd.org>
+ *
+ * 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 */
--- /dev/null
+/**
+ * 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 <lukeh at c-ware.com>
+ *
+ * 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 */
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ */
+
+#include "collectd.h"
+#include "utils/taskstats/taskstats.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils_time.h"
+
+#include <libmnl/libmnl.h>
+#include <linux/genetlink.h>
+#include <linux/taskstats.h>
+
+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;
+}
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ */
+
+#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 */
+++ /dev/null
-/**
- * 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 <sh at tokkee.org>
- * Florian "octo" Forster <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <sh at tokkee.org>
- **/
-
-#ifndef UTILS_CMD_FLUSH_H
-#define UTILS_CMD_FLUSH_H 1
-
-#include "utils_cmds.h"
-
-#include <stdio.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);
-
-cmd_status_t cmd_handle_flush(FILE *fh, char *buffer);
-
-void cmd_destroy_flush(cmd_flush_t *flush);
-
-#endif /* UTILS_CMD_FLUSH_H */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#ifndef UTILS_CMD_GETTHRESHOLD_H
-#define UTILS_CMD_GETTHRESHOLD_H 1
-
-#include <stdio.h>
-
-int handle_getthreshold(FILE *fh, char *buffer);
-
-#endif /* UTILS_CMD_GETTHRESHOLD_H */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#ifndef UTILS_CMD_GETVAL_H
-#define UTILS_CMD_GETVAL_H 1
-
-#include <stdio.h>
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#ifndef UTILS_CMD_LISTVAL_H
-#define UTILS_CMD_LISTVAL_H 1
-
-#include <stdio.h>
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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: <type>:<key> */
- 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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#ifndef UTILS_CMD_PUTNOTIF_H
-#define UTILS_CMD_PUTNOTIF_H 1
-
-#include <stdio.h>
-
-int handle_putnotif(FILE *fh, char *buffer);
-
-#endif /* UTILS_CMD_PUTNOTIF_H */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- * Sebastian tokkee Harl <sh at tokkee.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#ifndef UTILS_CMD_PUTVAL_H
-#define UTILS_CMD_PUTVAL_H 1
-
-#include "plugin.h"
-#include "utils_cmds.h"
-
-#include <stdio.h>
-
-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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- * Sebastian 'tokkee' Harl <sh at tokkee.org>
- **/
-
-#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 <stdbool.h>
-#include <string.h>
-
-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 */
+++ /dev/null
-/**
- * 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 <sh at tokkee.org>
- **/
-
-#ifndef UTILS_CMDS_H
-#define UTILS_CMDS_H 1
-
-#include "plugin.h"
-
-#include <stdarg.h>
-
-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 */
+++ /dev/null
-/**
- * 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 <sh at tokkee.org>
- **/
-
-#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;
-}
+++ /dev/null
-/**
- * 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 <kamilx.wiatrowski@intel.com>
- **/
-
-#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;
-}
+++ /dev/null
-/**
- * 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 <kamilx.wiatrowski@intel.com>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <kamilx.wiatrowski@intel.com>
- **/
-
-#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;
-}
+++ /dev/null
-/*
- * 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 <stddef.h>
-#include <stdint.h>
-
-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;
-}
+++ /dev/null
-/**
- * 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 <pyr at spootnik.org>
- */
-
-#ifndef UTILS_CRC32_H
-#define UTILS_CRC32_H 1
-
-uint32_t crc32_buffer(const unsigned char *, size_t);
-
-#endif
+++ /dev/null
-/**
- * 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 <sh@tokkee.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "utils_curl_stats.h"
-
-#include <stdbool.h>
-#include <stddef.h>
-
-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 ? "<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 */
+++ /dev/null
-/**
- * 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 <sh@tokkee.org>
- **/
-
-#ifndef UTILS_CURL_STATS_H
-#define UTILS_CURL_STATS_H 1
-
-#include "plugin.h"
-
-#include <curl/curl.h>
-
-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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 <Query> "
- "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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <ansmith@redhat.com>
- */
-
-#ifndef utils_deq_h
-#define utils_deq_h 1
-
-#include <assert.h>
-#include <memory.h>
-#include <stdlib.h>
-
-#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
+++ /dev/null
-/*
- * 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. <http://www.measurement-factory.com/>
- * Florian octo Forster <octo at collectd.org>
- */
-
-#define _DEFAULT_SOURCE
-#define _BSD_SOURCE
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#if HAVE_NET_IF_ARP_H
-#include <net/if_arp.h>
-#endif
-#if HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#if HAVE_NET_PPP_DEFS_H
-#include <net/ppp_defs.h>
-#endif
-#if HAVE_NET_IF_PPP_H
-#include <net/if_ppp.h>
-#endif
-
-#if HAVE_NETINET_IN_SYSTM_H
-#include <netinet/in_systm.h>
-#endif
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#if HAVE_NETINET_IP6_H
-#include <netinet/ip6.h>
-#endif
-#if HAVE_NETINET_IF_ETHER_H
-#include <netinet/if_ether.h>
-#endif
-#if HAVE_NETINET_IP_H
-#include <netinet/ip.h>
-#endif
-#ifdef HAVE_NETINET_IP_VAR_H
-#include <netinet/ip_var.h>
-#endif
-#if HAVE_NETINET_UDP_H
-#include <netinet/udp.h>
-#endif
-
-#if HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#if HAVE_ARPA_NAMESER_H
-#include <arpa/nameser.h>
-#endif
-#if HAVE_ARPA_NAMESER_COMPAT_H
-#include <arpa/nameser_compat.h>
-#endif
-
-#if HAVE_NETDB_H
-#include <netdb.h>
-#endif
-
-#if HAVE_PCAP_H
-#include <pcap.h>
-#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
+++ /dev/null
-/*
- * 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. <http://www.measurement-factory.com/>
- * Florian octo Forster <octo at collectd.org>
- */
-
-#ifndef COLLECTD_UTILS_DNS_H
-#define COLLECTD_UTILS_DNS_H 1
-
-#include "config.h"
-
-#include <arpa/nameser.h>
-#include <stdint.h>
-
-#if HAVE_PCAP_H
-#include <pcap.h>
-#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 */
+++ /dev/null
-/*
- * 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 <maryam.tahhan@intel.com>
- * Harry van Haaren <harry.van.haaren@intel.com>
- * Taras Chornyi <tarasx.chornyi@intel.com>
- * Serhiy Pshyk <serhiyx.pshyk@intel.com>
- * Krzysztof Matczak <krzysztofx.matczak@intel.com>
- */
-
-#include "collectd.h"
-
-#include <poll.h>
-#include <semaphore.h>
-#include <sys/mman.h>
-
-#include <rte_config.h>
-#include <rte_eal.h>
-#include <rte_ethdev.h>
-
-#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;
-}
+++ /dev/null
-/*
- * 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 <maryam.tahhan@intel.com>
- * Harry van Haaren <harry.van.haaren@intel.com>
- * Taras Chornyi <tarasx.chornyi@intel.com>
- * Serhiy Pshyk <serhiyx.pshyk@intel.com>
- * Krzysztof Matczak <krzysztofx.matczak@intel.com>
- */
-
-#ifndef UTILS_DPDK_H
-#define UTILS_DPDK_H
-
-#include <rte_version.h>
-
-#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 */
#include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
#include "utils_fbhash.h"
struct fbhash_s {
char *key_copy;
char *value_copy;
- buffer[sizeof(buffer) - 1] = 0;
+ buffer[sizeof(buffer) - 1] = '\0';
len = strlen(buffer);
/* Remove trailing newline characters. */
+++ /dev/null
-/**
- * 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 <zllak at hycik.org>
- * Florian octo Forster <octo at collectd.org>
- **/
-
-#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 */
-
-/* helper function for reverse_hostname */
-void reverse_string(char *r_host, int len) {
- char t;
- for (int i = 0; i < len / 2; i++) {
- int j = len - i - 1;
- 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) {
- int len_host = strlen(vl->host);
- char r_host[len_host + 1];
- 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) {
- int len_host = strlen(vl->host);
- char r_host[len_host + 1];
- 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 */
+++ /dev/null
-/**
- * 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 <zllak at hycik.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- */
-
-#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;
-}
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "utils_format_json.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_cache.h"
-
-#if HAVE_LIBYAJL
-#include <yajl/yajl_common.h>
-#include <yajl/yajl_gen.h>
-#if HAVE_YAJL_YAJL_VERSION_H
-#include <yajl/yajl_version.h>
-#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": <rfc3339 time>,
- * "endsAt": <rfc3339 time>, // 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
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- */
-
-/* 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 <yajl/yajl_common.h>
-#include <yajl/yajl_parse.h>
-#if HAVE_YAJL_YAJL_VERSION_H
-#include <yajl/yajl_version.h>
-#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;
-}
+++ /dev/null
-/**
- * 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 <beorn at gandi dot net>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <beorn at gandi dot net>
- **/
-
-#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 */
+++ /dev/null
-/**
- * collectd - src/utils_format_stackdriver.c
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "utils_format_stackdriver.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_avltree.h"
-#include "utils_cache.h"
-#include "utils_time.h"
-
-#include <yajl/yajl_gen.h>
-#include <yajl/yajl_parse.h>
-#if HAVE_YAJL_YAJL_VERSION_H
-#include <yajl/yajl_version.h>
-#endif
-
-struct sd_output_s {
- sd_resource_t *res;
- yajl_gen gen;
- c_avl_tree_t *staged;
- c_avl_tree_t *metric_descriptors;
-};
-
-struct sd_label_s {
- char *key;
- char *value;
-};
-typedef struct sd_label_s sd_label_t;
-
-struct sd_resource_s {
- char *type;
-
- sd_label_t *labels;
- size_t labels_num;
-};
-
-static int json_string(yajl_gen gen, char const *s) /* {{{ */
-{
- yajl_gen_status status =
- yajl_gen_string(gen, (unsigned char const *)s, strlen(s));
- if (status != yajl_gen_status_ok)
- return (int)status;
-
- return 0;
-} /* }}} int json_string */
-
-static int json_time(yajl_gen gen, cdtime_t t) {
- char buffer[64];
-
- size_t status = rfc3339(buffer, sizeof(buffer), t);
- if (status != 0) {
- return status;
- }
-
- return json_string(gen, buffer);
-} /* }}} int json_time */
-
-/* MonitoredResource
- *
- * {
- * "type": "library.googleapis.com/book",
- * "labels": {
- * "/genre": "fiction",
- * "/media": "paper"
- * "/title": "The Old Man and the Sea"
- * }
- * }
- */
-static int format_gcm_resource(yajl_gen gen, sd_resource_t *res) /* {{{ */
-{
- yajl_gen_map_open(gen);
-
- int status = json_string(gen, "type") || json_string(gen, res->type);
- if (status != 0)
- return status;
-
- if (res->labels_num != 0) {
- status = json_string(gen, "labels");
- if (status != 0)
- return status;
-
- yajl_gen_map_open(gen);
- for (size_t i = 0; i < res->labels_num; i++) {
- status = json_string(gen, res->labels[i].key) ||
- json_string(gen, res->labels[i].value);
- if (status != 0)
- return status;
- }
- yajl_gen_map_close(gen);
- }
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_gcm_resource */
-
-/* TypedValue
- *
- * {
- * // Union field, only one of the following:
- * "int64Value": string,
- * "doubleValue": number,
- * }
- */
-static int format_typed_value(yajl_gen gen, int ds_type, value_t v,
- int64_t start_value) {
- char integer[32];
-
- yajl_gen_map_open(gen);
-
- switch (ds_type) {
- case DS_TYPE_GAUGE: {
- int status = json_string(gen, "doubleValue");
- if (status != 0)
- return status;
-
- status = (int)yajl_gen_double(gen, (double)v.gauge);
- if (status != yajl_gen_status_ok)
- return status;
-
- yajl_gen_map_close(gen);
- return 0;
- }
- case DS_TYPE_DERIVE: {
- derive_t diff = v.derive - (derive_t)start_value;
- snprintf(integer, sizeof(integer), "%" PRIi64, diff);
- break;
- }
- case DS_TYPE_COUNTER: {
- counter_t diff = counter_diff((counter_t)start_value, v.counter);
- snprintf(integer, sizeof(integer), "%llu", diff);
- break;
- }
- case DS_TYPE_ABSOLUTE: {
- snprintf(integer, sizeof(integer), "%" PRIu64, v.absolute);
- break;
- }
- default: {
- ERROR("format_typed_value: unknown value type %d.", ds_type);
- return EINVAL;
- }
- }
-
- int status = json_string(gen, "int64Value") || json_string(gen, integer);
- if (status != 0) {
- return status;
- }
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_typed_value */
-
-/* MetricKind
- *
- * enum(
- * "CUMULATIVE",
- * "GAUGE"
- * )
-*/
-static int format_metric_kind(yajl_gen gen, int ds_type) {
- switch (ds_type) {
- case DS_TYPE_GAUGE:
- case DS_TYPE_ABSOLUTE:
- return json_string(gen, "GAUGE");
- case DS_TYPE_COUNTER:
- case DS_TYPE_DERIVE:
- return json_string(gen, "CUMULATIVE");
- default:
- ERROR("format_metric_kind: unknown value type %d.", ds_type);
- return EINVAL;
- }
-}
-
-/* ValueType
- *
- * enum(
- * "DOUBLE",
- * "INT64"
- * )
-*/
-static int format_value_type(yajl_gen gen, int ds_type) {
- return json_string(gen, (ds_type == DS_TYPE_GAUGE) ? "DOUBLE" : "INT64");
-}
-
-static int metric_type(char *buffer, size_t buffer_size, data_set_t const *ds,
- value_list_t const *vl, int ds_index) {
- /* {{{ */
- char const *ds_name = ds->ds[ds_index].name;
-
-#define GCM_PREFIX "custom.googleapis.com/collectd/"
- if ((ds_index != 0) || strcmp("value", ds_name) != 0) {
- snprintf(buffer, buffer_size, GCM_PREFIX "%s/%s_%s", vl->plugin, vl->type,
- ds_name);
- } else {
- snprintf(buffer, buffer_size, GCM_PREFIX "%s/%s", vl->plugin, vl->type);
- }
-
- char const *whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789_/";
- char *ptr = buffer + strlen(GCM_PREFIX);
- size_t ok_len;
- while ((ok_len = strspn(ptr, whitelist)) != strlen(ptr)) {
- ptr[ok_len] = '_';
- ptr += ok_len;
- }
-
- return 0;
-} /* }}} int metric_type */
-
-/* The metric type, including its DNS name prefix. The type is not URL-encoded.
- * All user-defined custom metric types have the DNS name custom.googleapis.com.
- * Metric types should use a natural hierarchical grouping. */
-static int format_metric_type(yajl_gen gen, data_set_t const *ds,
- value_list_t const *vl, int ds_index) {
- /* {{{ */
- char buffer[4 * DATA_MAX_NAME_LEN];
- metric_type(buffer, sizeof(buffer), ds, vl, ds_index);
-
- return json_string(gen, buffer);
-} /* }}} int format_metric_type */
-
-/* TimeInterval
- *
- * {
- * "endTime": string,
- * "startTime": string,
- * }
- */
-static int format_time_interval(yajl_gen gen, int ds_type,
- value_list_t const *vl, cdtime_t start_time) {
- /* {{{ */
- yajl_gen_map_open(gen);
-
- int status = json_string(gen, "endTime") || json_time(gen, vl->time);
- if (status != 0)
- return status;
-
- if ((ds_type == DS_TYPE_DERIVE) || (ds_type == DS_TYPE_COUNTER)) {
- int status = json_string(gen, "startTime") || json_time(gen, start_time);
- if (status != 0)
- return status;
- }
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_time_interval */
-
-/* read_cumulative_state reads the start time and start value of cumulative
- * (i.e. DERIVE or COUNTER) metrics from the cache. If a metric is seen for the
- * first time, or when a DERIVE metric is reset, the start time is (re)set to
- * vl->time. */
-static int read_cumulative_state(data_set_t const *ds, value_list_t const *vl,
- int ds_index, cdtime_t *ret_start_time,
- int64_t *ret_start_value) {
- int ds_type = ds->ds[ds_index].type;
- if ((ds_type != DS_TYPE_DERIVE) && (ds_type != DS_TYPE_COUNTER)) {
- return 0;
- }
-
- char start_value_key[DATA_MAX_NAME_LEN];
- snprintf(start_value_key, sizeof(start_value_key),
- "stackdriver:start_value[%d]", ds_index);
-
- int status =
- uc_meta_data_get_signed_int(vl, start_value_key, ret_start_value);
- if ((status == 0) && ((ds_type != DS_TYPE_DERIVE) ||
- (*ret_start_value <= vl->values[ds_index].derive))) {
- return uc_meta_data_get_unsigned_int(vl, "stackdriver:start_time",
- ret_start_time);
- }
-
- if (ds_type == DS_TYPE_DERIVE) {
- *ret_start_value = vl->values[ds_index].derive;
- } else {
- *ret_start_value = (int64_t)vl->values[ds_index].counter;
- }
- *ret_start_time = vl->time;
-
- status = uc_meta_data_add_signed_int(vl, start_value_key, *ret_start_value);
- if (status != 0) {
- return status;
- }
- return uc_meta_data_add_unsigned_int(vl, "stackdriver:start_time",
- *ret_start_time);
-} /* int read_cumulative_state */
-
-/* Point
- *
- * {
- * "interval": {
- * object(TimeInterval)
- * },
- * "value": {
- * object(TypedValue)
- * },
- * }
- */
-static int format_point(yajl_gen gen, data_set_t const *ds,
- value_list_t const *vl, int ds_index,
- cdtime_t start_time, int64_t start_value) {
- /* {{{ */
- yajl_gen_map_open(gen);
-
- int ds_type = ds->ds[ds_index].type;
-
- int status =
- json_string(gen, "interval") ||
- format_time_interval(gen, ds_type, vl, start_time) ||
- json_string(gen, "value") ||
- format_typed_value(gen, ds_type, vl->values[ds_index], start_value);
- if (status != 0)
- return status;
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_point */
-
-/* Metric
- *
- * {
- * "type": string,
- * "labels": {
- * string: string,
- * ...
- * },
- * }
- */
-static int format_metric(yajl_gen gen, data_set_t const *ds,
- value_list_t const *vl, int ds_index) {
- /* {{{ */
- yajl_gen_map_open(gen);
-
- int status = json_string(gen, "type") ||
- format_metric_type(gen, ds, vl, ds_index) ||
- json_string(gen, "labels");
- if (status != 0) {
- return status;
- }
-
- yajl_gen_map_open(gen);
- status = json_string(gen, "host") || json_string(gen, vl->host) ||
- json_string(gen, "plugin_instance") ||
- json_string(gen, vl->plugin_instance) ||
- json_string(gen, "type_instance") ||
- json_string(gen, vl->type_instance);
- if (status != 0) {
- return status;
- }
- yajl_gen_map_close(gen);
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_metric */
-
-/* TimeSeries
- *
- * {
- * "metric": {
- * object(Metric)
- * },
- * "resource": {
- * object(MonitoredResource)
- * },
- * "metricKind": enum(MetricKind),
- * "valueType": enum(ValueType),
- * "points": [
- * {
- * object(Point)
- * }
- * ],
- * }
- */
-/* format_time_series formats a TimeSeries object. Returns EAGAIN when a
- * cumulative metric is seen for the first time and cannot be sent to
- * Stackdriver due to lack of state. */
-static int format_time_series(yajl_gen gen, data_set_t const *ds,
- value_list_t const *vl, int ds_index,
- sd_resource_t *res) {
- int ds_type = ds->ds[ds_index].type;
-
- cdtime_t start_time = 0;
- int64_t start_value = 0;
- int status =
- read_cumulative_state(ds, vl, ds_index, &start_time, &start_value);
- if (status != 0) {
- return status;
- }
- if (start_time == vl->time) {
- /* for cumulative metrics, the interval must not be zero. */
- return EAGAIN;
- }
-
- yajl_gen_map_open(gen);
-
- status = json_string(gen, "metric") || format_metric(gen, ds, vl, ds_index) ||
- json_string(gen, "resource") || format_gcm_resource(gen, res) ||
- json_string(gen, "metricKind") || format_metric_kind(gen, ds_type) ||
- json_string(gen, "valueType") || format_value_type(gen, ds_type) ||
- json_string(gen, "points");
- if (status != 0)
- return status;
-
- yajl_gen_array_open(gen);
-
- status = format_point(gen, ds, vl, ds_index, start_time, start_value);
- if (status != 0)
- return status;
-
- yajl_gen_array_close(gen);
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_time_series */
-
-/* Request body
- *
- * {
- * "timeSeries": [
- * {
- * object(TimeSeries)
- * }
- * ],
- * }
- */
-static int sd_output_initialize(sd_output_t *out) /* {{{ */
-{
- yajl_gen_map_open(out->gen);
-
- int status = json_string(out->gen, "timeSeries");
- if (status != 0) {
- return status;
- }
-
- yajl_gen_array_open(out->gen);
- return 0;
-} /* }}} int sd_output_initialize */
-
-static int sd_output_finalize(sd_output_t *out) /* {{{ */
-{
- yajl_gen_array_close(out->gen);
- yajl_gen_map_close(out->gen);
-
- return 0;
-} /* }}} int sd_output_finalize */
-
-static void sd_output_reset_staged(sd_output_t *out) /* {{{ */
-{
- void *key = NULL;
-
- while (c_avl_pick(out->staged, &key, &(void *){NULL}) == 0)
- sfree(key);
-} /* }}} void sd_output_reset_staged */
-
-sd_output_t *sd_output_create(sd_resource_t *res) /* {{{ */
-{
- sd_output_t *out = calloc(1, sizeof(*out));
- if (out == NULL)
- return NULL;
-
- out->res = res;
-
- out->gen = yajl_gen_alloc(/* funcs = */ NULL);
- if (out->gen == NULL) {
- sd_output_destroy(out);
- return NULL;
- }
-
- out->staged = c_avl_create((void *)strcmp);
- if (out->staged == NULL) {
- sd_output_destroy(out);
- return NULL;
- }
-
- out->metric_descriptors = c_avl_create((void *)strcmp);
- if (out->metric_descriptors == NULL) {
- sd_output_destroy(out);
- return NULL;
- }
-
- sd_output_initialize(out);
-
- return out;
-} /* }}} sd_output_t *sd_output_create */
-
-void sd_output_destroy(sd_output_t *out) /* {{{ */
-{
- if (out == NULL)
- return;
-
- if (out->metric_descriptors != NULL) {
- void *key = NULL;
- while (c_avl_pick(out->metric_descriptors, &key, &(void *){NULL}) == 0) {
- sfree(key);
- }
- c_avl_destroy(out->metric_descriptors);
- out->metric_descriptors = NULL;
- }
-
- if (out->staged != NULL) {
- sd_output_reset_staged(out);
- c_avl_destroy(out->staged);
- out->staged = NULL;
- }
-
- if (out->gen != NULL) {
- yajl_gen_free(out->gen);
- out->gen = NULL;
- }
-
- if (out->res != NULL) {
- sd_resource_destroy(out->res);
- out->res = NULL;
- }
-
- sfree(out);
-} /* }}} void sd_output_destroy */
-
-int sd_output_add(sd_output_t *out, data_set_t const *ds,
- value_list_t const *vl) /* {{{ */
-{
- /* first, check that we have all appropriate metric descriptors. */
- for (size_t i = 0; i < ds->ds_num; i++) {
- char buffer[4 * DATA_MAX_NAME_LEN];
- metric_type(buffer, sizeof(buffer), ds, vl, i);
-
- if (c_avl_get(out->metric_descriptors, buffer, NULL) != 0) {
- return ENOENT;
- }
- }
-
- char key[6 * DATA_MAX_NAME_LEN];
- int status = FORMAT_VL(key, sizeof(key), vl);
- if (status != 0) {
- ERROR("sd_output_add: FORMAT_VL failed with status %d.", status);
- return status;
- }
-
- if (c_avl_get(out->staged, key, NULL) == 0) {
- return EEXIST;
- }
-
- _Bool staged = 0;
- for (size_t i = 0; i < ds->ds_num; i++) {
- int status = format_time_series(out->gen, ds, vl, i, out->res);
- if (status == EAGAIN) {
- /* first instance of a cumulative metric */
- continue;
- }
- if (status != 0) {
- ERROR("sd_output_add: format_time_series failed with status %d.", status);
- return status;
- }
- staged = 1;
- }
-
- if (staged) {
- c_avl_insert(out->staged, strdup(key), NULL);
- }
-
- size_t json_buffer_size = 0;
- yajl_gen_get_buf(out->gen, &(unsigned char const *){NULL}, &json_buffer_size);
- if (json_buffer_size > 65535)
- return ENOBUFS;
-
- return 0;
-} /* }}} int sd_output_add */
-
-int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
- value_list_t const *vl) {
- /* {{{ */
- for (size_t i = 0; i < ds->ds_num; i++) {
- char buffer[4 * DATA_MAX_NAME_LEN];
- metric_type(buffer, sizeof(buffer), ds, vl, i);
-
- char *key = strdup(buffer);
- int status = c_avl_insert(out->metric_descriptors, key, NULL);
- if (status != 0) {
- sfree(key);
- return status;
- }
- }
-
- return 0;
-} /* }}} int sd_output_register_metric */
-
-char *sd_output_reset(sd_output_t *out) /* {{{ */
-{
- sd_output_finalize(out);
-
- unsigned char const *json_buffer = NULL;
- yajl_gen_get_buf(out->gen, &json_buffer, &(size_t){0});
- char *ret = strdup((void const *)json_buffer);
-
- sd_output_reset_staged(out);
-
- yajl_gen_free(out->gen);
- out->gen = yajl_gen_alloc(/* funcs = */ NULL);
-
- sd_output_initialize(out);
-
- return ret;
-} /* }}} char *sd_output_reset */
-
-sd_resource_t *sd_resource_create(char const *type) /* {{{ */
-{
- sd_resource_t *res = malloc(sizeof(*res));
- if (res == NULL)
- return NULL;
- memset(res, 0, sizeof(*res));
-
- res->type = strdup(type);
- if (res->type == NULL) {
- sfree(res);
- return NULL;
- }
-
- res->labels = NULL;
- res->labels_num = 0;
-
- return res;
-} /* }}} sd_resource_t *sd_resource_create */
-
-void sd_resource_destroy(sd_resource_t *res) /* {{{ */
-{
- if (res == NULL)
- return;
-
- for (size_t i = 0; i < res->labels_num; i++) {
- sfree(res->labels[i].key);
- sfree(res->labels[i].value);
- }
- sfree(res->labels);
- sfree(res->type);
- sfree(res);
-} /* }}} void sd_resource_destroy */
-
-int sd_resource_add_label(sd_resource_t *res, char const *key,
- char const *value) /* {{{ */
-{
- if ((res == NULL) || (key == NULL) || (value == NULL))
- return EINVAL;
-
- sd_label_t *l =
- realloc(res->labels, sizeof(*res->labels) * (res->labels_num + 1));
- if (l == NULL)
- return ENOMEM;
-
- res->labels = l;
- l = res->labels + res->labels_num;
-
- l->key = strdup(key);
- l->value = strdup(value);
- if ((l->key == NULL) || (l->value == NULL)) {
- sfree(l->key);
- sfree(l->value);
- return ENOMEM;
- }
-
- res->labels_num++;
- return 0;
-} /* }}} int sd_resource_add_label */
-
-/* LabelDescriptor
- *
- * {
- * "key": string,
- * "valueType": enum(ValueType),
- * "description": string,
- * }
- */
-static int format_label_descriptor(yajl_gen gen, char const *key) {
- /* {{{ */
- yajl_gen_map_open(gen);
-
- int status = json_string(gen, "key") || json_string(gen, key) ||
- json_string(gen, "valueType") || json_string(gen, "STRING");
- if (status != 0) {
- return status;
- }
-
- yajl_gen_map_close(gen);
- return 0;
-} /* }}} int format_label_descriptor */
-
-/* MetricDescriptor
- *
- * {
- * "name": string,
- * "type": string,
- * "labels": [
- * {
- * object(LabelDescriptor)
- * }
- * ],
- * "metricKind": enum(MetricKind),
- * "valueType": enum(ValueType),
- * "unit": string,
- * "description": string,
- * "displayName": string,
- * }
- */
-int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
- data_set_t const *ds, value_list_t const *vl,
- int ds_index) {
- /* {{{ */
- yajl_gen gen = yajl_gen_alloc(/* funcs = */ NULL);
- if (gen == NULL) {
- return ENOMEM;
- }
-
- int ds_type = ds->ds[ds_index].type;
-
- yajl_gen_map_open(gen);
-
- int status =
- json_string(gen, "type") || format_metric_type(gen, ds, vl, ds_index) ||
- json_string(gen, "metricKind") || format_metric_kind(gen, ds_type) ||
- json_string(gen, "valueType") || format_value_type(gen, ds_type) ||
- json_string(gen, "labels");
- if (status != 0) {
- yajl_gen_free(gen);
- return status;
- }
-
- char const *labels[] = {"host", "plugin_instance", "type_instance"};
- yajl_gen_array_open(gen);
-
- for (size_t i = 0; i < STATIC_ARRAY_SIZE(labels); i++) {
- int status = format_label_descriptor(gen, labels[i]);
- if (status != 0) {
- yajl_gen_free(gen);
- return status;
- }
- }
-
- yajl_gen_array_close(gen);
- yajl_gen_map_close(gen);
-
- unsigned char const *tmp = NULL;
- yajl_gen_get_buf(gen, &tmp, &(size_t){0});
- sstrncpy(buffer, (void const *)tmp, buffer_size);
-
- yajl_gen_free(gen);
- return 0;
-} /* }}} int sd_format_metric_descriptor */
+++ /dev/null
-/**
- * collectd - src/utils_format_stackdriver.h
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_FORMAT_STACKDRIVER_H
-#define UTILS_FORMAT_STACKDRIVER_H 1
-
-#include "collectd.h"
-#include "plugin.h"
-
-/* sd_output_t is a buffer to which value_list_t* can be added and from which
- * an appropriately formatted char* can be read. */
-struct sd_output_s;
-typedef struct sd_output_s sd_output_t;
-
-/* sd_resource_t represents a MonitoredResource. */
-struct sd_resource_s;
-typedef struct sd_resource_s sd_resource_t;
-
-sd_output_t *sd_output_create(sd_resource_t *res);
-
-/* sd_output_destroy frees all memory used by out, including the
- * sd_resource_t* passed to sd_output_create. */
-void sd_output_destroy(sd_output_t *out);
-
-/* sd_output_add adds a value_list_t* to "out".
- *
- * Return values:
- * - 0 Success
- * - ENOBUFS Success, but the buffer should be flushed soon.
- * - EEXIST The value list is already encoded in the buffer.
- * Flush the buffer, then call sd_output_add again.
- * - ENOENT First time we encounter this metric. Create a metric descriptor
- * using the Stackdriver API and then call
- * sd_output_register_metric.
- */
-int sd_output_add(sd_output_t *out, data_set_t const *ds,
- value_list_t const *vl);
-
-/* sd_output_register_metric adds the metric descriptor which vl maps to, to
- * the list of known metric descriptors. */
-int sd_output_register_metric(sd_output_t *out, data_set_t const *ds,
- value_list_t const *vl);
-
-/* sd_output_reset resets the output and returns the previous content of the
- * buffer. It is the caller's responsibility to call free() with the returned
- * pointer. */
-char *sd_output_reset(sd_output_t *out);
-
-sd_resource_t *sd_resource_create(char const *type);
-void sd_resource_destroy(sd_resource_t *res);
-int sd_resource_add_label(sd_resource_t *res, char const *key,
- char const *value);
-
-/* sd_format_metric_descriptor creates the payload for a
- * projects.metricDescriptors.create() request. */
-int sd_format_metric_descriptor(char *buffer, size_t buffer_size,
- data_set_t const *ds, value_list_t const *vl,
- int ds_index);
-
-#endif /* UTILS_FORMAT_STACKDRIVER_H */
+++ /dev/null
-/**
- * collectd - src/utils_format_stackdriver_test.c
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "testing.h"
-#include "utils_format_stackdriver.h"
-
-DEF_TEST(sd_format_metric_descriptor) {
- value_list_t vl = {
- .host = "example.com", .plugin = "unit-test", .type = "example",
- };
- char got[1024];
-
- data_set_t ds_single = {
- .type = "example",
- .ds_num = 1,
- .ds =
- &(data_source_t){
- .name = "value", .type = DS_TYPE_GAUGE, .min = NAN, .max = NAN,
- },
- };
- EXPECT_EQ_INT(
- 0, sd_format_metric_descriptor(got, sizeof(got), &ds_single, &vl, 0));
- char const *want_single =
- "{\"type\":\"custom.googleapis.com/collectd/unit_test/"
- "example\",\"metricKind\":\"GAUGE\",\"valueType\":\"DOUBLE\",\"labels\":["
- "{\"key\":\"host\",\"valueType\":\"STRING\"},{\"key\":\"plugin_"
- "instance\",\"valueType\":\"STRING\"},{\"key\":\"type_instance\","
- "\"valueType\":\"STRING\"}]}";
- EXPECT_EQ_STR(want_single, got);
-
- data_set_t ds_double = {
- .type = "example",
- .ds_num = 2,
- .ds =
- (data_source_t[]){
- {.name = "one", .type = DS_TYPE_DERIVE, .min = 0, .max = NAN},
- {.name = "two", .type = DS_TYPE_DERIVE, .min = 0, .max = NAN},
- },
- };
- EXPECT_EQ_INT(
- 0, sd_format_metric_descriptor(got, sizeof(got), &ds_double, &vl, 0));
- char const *want_double =
- "{\"type\":\"custom.googleapis.com/collectd/unit_test/"
- "example_one\",\"metricKind\":\"CUMULATIVE\",\"valueType\":\"INT64\","
- "\"labels\":[{\"key\":\"host\",\"valueType\":\"STRING\"},{\"key\":"
- "\"plugin_instance\",\"valueType\":\"STRING\"},{\"key\":\"type_"
- "instance\",\"valueType\":\"STRING\"}]}";
- EXPECT_EQ_STR(want_double, got);
- return 0;
-}
-
-int main(int argc, char **argv) {
- RUN_TEST(sd_format_metric_descriptor);
-
- END_TEST;
-}
+++ /dev/null
-/**
- * collectd - src/utils_gce.c
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_gce.h"
-#include "utils_oauth.h"
-#include "utils_time.h"
-
-#include <curl/curl.h>
-
-#ifndef GCP_METADATA_PREFIX
-#define GCP_METADATA_PREFIX "http://metadata.google.internal/computeMetadata/v1"
-#endif
-#ifndef GCE_METADATA_HEADER
-#define GCE_METADATA_HEADER "Metadata-Flavor: Google"
-#endif
-
-#ifndef GCE_INSTANCE_ID_URL
-#define GCE_INSTANCE_ID_URL GCP_METADATA_PREFIX "/instance/id"
-#endif
-#ifndef GCE_PROJECT_NUM_URL
-#define GCE_PROJECT_NUM_URL GCP_METADATA_PREFIX "/project/numeric-project-id"
-#endif
-#ifndef GCE_PROJECT_ID_URL
-#define GCE_PROJECT_ID_URL GCP_METADATA_PREFIX "/project/project-id"
-#endif
-#ifndef GCE_ZONE_URL
-#define GCE_ZONE_URL GCP_METADATA_PREFIX "/instance/zone"
-#endif
-
-#ifndef GCE_DEFAULT_SERVICE_ACCOUNT
-#define GCE_DEFAULT_SERVICE_ACCOUNT "default"
-#endif
-
-#ifndef GCE_SCOPE_URL
-#define GCE_SCOPE_URL_FORMAT \
- GCP_METADATA_PREFIX "/instance/service-accounts/%s/scopes"
-#endif
-#ifndef GCE_TOKEN_URL
-#define GCE_TOKEN_URL_FORMAT \
- GCP_METADATA_PREFIX "/instance/service-accounts/%s/token"
-#endif
-
-struct blob_s {
- char *data;
- size_t size;
-};
-typedef struct blob_s blob_t;
-
-static int on_gce = -1;
-
-static char *token = NULL;
-static char *token_email = NULL;
-static cdtime_t token_valid_until = 0;
-static pthread_mutex_t token_lock = PTHREAD_MUTEX_INITIALIZER;
-
-static size_t write_callback(void *contents, size_t size, size_t nmemb,
- void *ud) /* {{{ */
-{
- size_t realsize = size * nmemb;
- blob_t *blob = ud;
-
- if ((0x7FFFFFF0 < blob->size) || (0x7FFFFFF0 - blob->size < realsize)) {
- ERROR("utils_gce: write_callback: integer overflow");
- return 0;
- }
-
- blob->data = realloc(blob->data, blob->size + realsize + 1);
- if (blob->data == NULL) {
- /* out of memory! */
- ERROR(
- "utils_gce: write_callback: not enough memory (realloc returned NULL)");
- return 0;
- }
-
- memcpy(blob->data + blob->size, contents, realsize);
- blob->size += realsize;
- blob->data[blob->size] = 0;
-
- return realsize;
-} /* }}} size_t write_callback */
-
-/* read_url will issue a GET request for the given URL, setting the magic GCE
- * metadata header in the process. On success, the response body is returned
- * and it's the caller's responsibility to free it. On failure, an error is
- * logged and NULL is returned. */
-static char *read_url(char const *url) /* {{{ */
-{
- CURL *curl = curl_easy_init();
- if (!curl) {
- ERROR("utils_gce: curl_easy_init failed.");
- return NULL;
- }
-
- struct curl_slist *headers = curl_slist_append(NULL, GCE_METADATA_HEADER);
-
- char curl_errbuf[CURL_ERROR_SIZE];
- blob_t blob = {0};
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &blob);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(curl, CURLOPT_URL, url);
-
- int status = curl_easy_perform(curl);
- if (status != CURLE_OK) {
- ERROR("utils_gce: fetching %s failed: %s", url, curl_errbuf);
- sfree(blob.data);
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- return NULL;
- }
-
- long http_code = 0;
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
- if ((http_code < 200) || (http_code >= 300)) {
- ERROR("write_gcm plugin: fetching %s failed: HTTP error %ld", url,
- http_code);
- sfree(blob.data);
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- return NULL;
- }
-
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- return blob.data;
-} /* }}} char *read_url */
-
-_Bool gce_check(void) /* {{{ */
-{
- if (on_gce != -1)
- return on_gce == 1;
-
- DEBUG("utils_gce: Checking whether I'm running on GCE ...");
-
- CURL *curl = curl_easy_init();
- if (!curl) {
- ERROR("utils_gce: curl_easy_init failed.");
- return 0;
- }
-
- struct curl_slist *headers = curl_slist_append(NULL, GCE_METADATA_HEADER);
-
- char curl_errbuf[CURL_ERROR_SIZE];
- blob_t blob = {NULL, 0};
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_callback);
- curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &blob);
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(curl, CURLOPT_URL, GCP_METADATA_PREFIX "/");
-
- int status = curl_easy_perform(curl);
- if ((status != CURLE_OK) || (blob.data == NULL) ||
- (strstr(blob.data, "Metadata-Flavor: Google") == NULL)) {
- DEBUG("utils_gce: ... no (%s)",
- (status != CURLE_OK)
- ? "curl_easy_perform failed"
- : (blob.data == NULL) ? "blob.data == NULL"
- : "Metadata-Flavor header not found");
- sfree(blob.data);
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- on_gce = 0;
- return 0;
- }
- sfree(blob.data);
-
- long http_code = 0;
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
- if ((http_code < 200) || (http_code >= 300)) {
- DEBUG("utils_gce: ... no (HTTP status %ld)", http_code);
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- on_gce = 0;
- return 0;
- }
-
- DEBUG("utils_gce: ... yes");
- curl_easy_cleanup(curl);
- curl_slist_free_all(headers);
- on_gce = 1;
- return 1;
-} /* }}} _Bool gce_check */
-
-char *gce_project_id(void) /* {{{ */
-{
- return read_url(GCE_PROJECT_ID_URL);
-} /* }}} char *gce_project_id */
-
-char *gce_instance_id(void) /* {{{ */
-{
- return read_url(GCE_INSTANCE_ID_URL);
-} /* }}} char *gce_instance_id */
-
-char *gce_zone(void) /* {{{ */
-{
- return read_url(GCE_ZONE_URL);
-} /* }}} char *gce_instance_id */
-
-char *gce_scope(char const *email) /* {{{ */
-{
- char url[1024];
-
- snprintf(url, sizeof(url), GCE_SCOPE_URL_FORMAT,
- (email != NULL) ? email : GCE_DEFAULT_SERVICE_ACCOUNT);
-
- return read_url(url);
-} /* }}} char *gce_scope */
-
-int gce_access_token(char const *email, char *buffer,
- size_t buffer_size) /* {{{ */
-{
- char url[1024];
- char *json;
- cdtime_t now = cdtime();
-
- pthread_mutex_lock(&token_lock);
-
- if (email == NULL)
- email = GCE_DEFAULT_SERVICE_ACCOUNT;
-
- if ((token_email != NULL) && (strcmp(email, token_email) == 0) &&
- (token_valid_until > now)) {
- sstrncpy(buffer, token, buffer_size);
- pthread_mutex_unlock(&token_lock);
- return 0;
- }
-
- snprintf(url, sizeof(url), GCE_TOKEN_URL_FORMAT, email);
- json = read_url(url);
- if (json == NULL) {
- pthread_mutex_unlock(&token_lock);
- return -1;
- }
-
- char tmp[256];
- cdtime_t expires_in = 0;
- int status = oauth_parse_json_token(json, tmp, sizeof(tmp), &expires_in);
- sfree(json);
- if (status != 0) {
- pthread_mutex_unlock(&token_lock);
- return status;
- }
-
- sfree(token);
- token = strdup(tmp);
-
- sfree(token_email);
- token_email = strdup(email);
-
- /* let tokens expire a bit early */
- expires_in = (expires_in * 95) / 100;
- token_valid_until = now + expires_in;
-
- sstrncpy(buffer, token, buffer_size);
- pthread_mutex_unlock(&token_lock);
- return 0;
-} /* }}} char *gce_token */
+++ /dev/null
-/**
- * collectd - src/utils_gce.h
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_GCE_H
-#define UTILS_GCE_H 1
-
-/* gce_check returns 1 when running on Google Compute Engine (GCE) and 0
- * otherwise. */
-_Bool gce_check(void);
-
-/* gce_project_id returns the project ID of the instance, as configured when
- * creating the project.
- * For example "example-project-a". */
-char *gce_project_id(void);
-
-/* gce_instance_id returns the unique ID of the GCE instance. */
-char *gce_instance_id(void);
-
-/* gce_zone returns the zone in which the GCE instance runs. */
-char *gce_zone(void);
-
-/* gce_scope returns the list of scopes for the given service account (or the
- * default service account when NULL is passed). */
-char *gce_scope(char const *email);
-
-/* gce_access_token acquires an OAuth access token for the given service account
- * (or
- * the default service account when NULL is passed) and stores it in buffer.
- * Access tokens are automatically cached and renewed when they expire. Returns
- * zero on success, non-zero otherwise. */
-int gce_access_token(char const *email, char *buffer, size_t buffer_size);
-
-#endif
+++ /dev/null
-/**
- * collectd - src/utils_ignorelist.c
- * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
- * Copyright (C) 2008 Florian Forster <octo at collectd.org>
- *
- * 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 <lubek at users.sourceforge.net>
- * Florian Forster <octo at collectd.org>
- **/
-/**
- * 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) */
+++ /dev/null
-/**
- * collectd - src/utils_ignorelist.h
- * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
- *
- * 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 <lubek at users.sourceforge.net>
- **/
-/**
- * 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 <regex.h>
-#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 */
+++ /dev/null
-/**
- * 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 <ff at octo.it>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_latency.h"
-
-#include <limits.h>
-#include <math.h>
-
-#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 */
+++ /dev/null
-/**
- * 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 <ff at octo.it>
- **/
-
-#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);
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- * Pavel Rochnyack <pavel2000 at ngs.ru>
- */
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- * Pavel Rochnyack <pavel2000 at ngs.ru>
- */
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- */
-
-#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;
-}
* Florian Forster <octo at collectd.org>
**/
-#include "common.h"
+#include "utils/common/common.h"
#include "utils_lua.h"
static int ltoc_values(lua_State *L, /* {{{ */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_match.h"
-
-#include <regex.h>
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <niki.waibel@gmx.net>
-**/
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define _GNU_SOURCE
-
-#include "collectd.h"
-
-#include "utils_mount.h"
-
-#if HAVE_XFS_XQM_H
-#include <xfs/xqm.h>
-#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 <sys/types.h>
-#endif
-#if HAVE_SYS_STATVFS_H
-#include <sys/statvfs.h>
-#endif
-/* #endif HAVE_GETVFSSTAT */
-
-#elif HAVE_GETFSSTAT
-#if HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#if HAVE_SYS_UCRED_H
-#include <sys/ucred.h>
-#endif
-#if HAVE_SYS_MOUNT_H
-#include <sys/mount.h>
-#endif
-#endif /* HAVE_GETFSSTAT */
-
-#if HAVE_MNTENT_H
-#include <mntent.h>
-#endif
-#if HAVE_SYS_MNTTAB_H
-#include <sys/mnttab.h>
-#endif
-
-#if HAVE_PATHS_H
-#include <paths.h>
-#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) */
+++ /dev/null
-/**
- * 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 <niki.waibel@gmx.net>
-**/
-
-/* See below for instructions how to use the public functions. */
-
-#ifndef COLLECTD_UTILS_MOUNT_H
-#define COLLECTD_UTILS_MOUNT_H 1
-
-#include <stdio.h>
-#if HAVE_FS_INFO_H
-#include <fs_info.h>
-#endif
-#if HAVE_FSHELP_H
-#include <fshelp.h>
-#endif
-#if HAVE_PATHS_H
-#include <paths.h>
-#endif
-#if HAVE_MNTENT_H
-#include <mntent.h>
-#endif
-#if HAVE_MNTTAB_H
-#include <mnttab.h>
-#endif
-#if HAVE_SYS_FSTYP_H
-#include <sys/fstyp.h>
-#endif
-#if HAVE_SYS_FS_TYPES_H
-#include <sys/fs_types.h>
-#endif
-#if HAVE_SYS_MNTENT_H
-#include <sys/mntent.h>
-#endif
-#if HAVE_SYS_MNTTAB_H
-#include <sys/mnttab.h>
-#endif
-#if HAVE_SYS_MOUNT_H
-#include <sys/mount.h>
-#endif
-#if HAVE_SYS_STATFS_H
-#include <sys/statfs.h>
-#endif
-#if HAVE_SYS_VFS_H
-#include <sys/vfs.h>
-#endif
-#if HAVE_SYS_VFSTAB_H
-#include <sys/vfstab.h>
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- */
-
-#include "collectd.h"
-
-#include "common.h"
-#include "testing.h"
-#include "utils_mount.h"
-
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#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;
-}
+++ /dev/null
-/**
- * collectd - src/utils_oauth.c
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_oauth.h"
-
-#include <curl/curl.h>
-
-#include <yajl/yajl_tree.h>
-#include <yajl/yajl_version.h>
-
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-#include <openssl/pkcs12.h>
-#include <openssl/sha.h>
-
-/*
- * Private variables
- */
-#define GOOGLE_TOKEN_URL "https://accounts.google.com/o/oauth2/token"
-
-/* Max send buffer size, since there will be only one writer thread and
- * monitoring api supports up to 100K bytes in one request, 64K is reasonable
- */
-#define MAX_BUFFER_SIZE 65536
-#define MAX_ENCODE_SIZE 2048
-
-struct oauth_s {
- char *url;
- char *iss;
- char *aud;
- char *scope;
-
- EVP_PKEY *key;
-
- char *token;
- cdtime_t valid_until;
-};
-
-struct memory_s {
- char *memory;
- size_t size;
-};
-typedef struct memory_s memory_t;
-
-#define OAUTH_GRANT_TYPE "urn:ietf:params:oauth:grant-type:jwt-bearer"
-#define OAUTH_EXPIRATION_TIME TIME_T_TO_CDTIME_T(3600)
-#define OAUTH_HEADER "{\"alg\":\"RS256\",\"typ\":\"JWT\"}"
-
-static const char OAUTH_CLAIM_FORMAT[] = "{"
- "\"iss\":\"%s\","
- "\"scope\":\"%s\","
- "\"aud\":\"%s\","
- "\"exp\":%lu,"
- "\"iat\":%lu"
- "}";
-
-static size_t write_memory(void *contents, size_t size, size_t nmemb, /* {{{ */
- void *userp) {
- size_t realsize = size * nmemb;
- memory_t *mem = (memory_t *)userp;
- char *tmp;
-
- if (0x7FFFFFF0 < mem->size || 0x7FFFFFF0 - mem->size < realsize) {
- ERROR("integer overflow");
- return 0;
- }
-
- tmp = (char *)realloc((void *)mem->memory, mem->size + realsize + 1);
- if (tmp == NULL) {
- /* out of memory! */
- ERROR("write_memory: not enough memory (realloc returned NULL)");
- return 0;
- }
- mem->memory = tmp;
-
- memcpy(&(mem->memory[mem->size]), contents, realsize);
- mem->size += realsize;
- mem->memory[mem->size] = 0;
-
- return realsize;
-} /* }}} size_t write_memory */
-
-/* Base64-encodes "s" and stores the result in buffer.
- * Returns zero on success, non-zero otherwise. */
-static int base64_encode_n(char const *s, size_t s_size, /* {{{ */
- char *buffer, size_t buffer_size) {
- BIO *b64;
- BUF_MEM *bptr;
- int status;
- size_t i;
-
- /* Set up the memory-base64 chain */
- b64 = BIO_new(BIO_f_base64());
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- b64 = BIO_push(b64, BIO_new(BIO_s_mem()));
-
- /* Write data to the chain */
- BIO_write(b64, (void const *)s, s_size);
- status = BIO_flush(b64);
- if (status != 1) {
- ERROR("utils_oauth: base64_encode: BIO_flush() failed.");
- BIO_free_all(b64);
- return -1;
- }
-
- /* Never fails */
- BIO_get_mem_ptr(b64, &bptr);
-
- if (buffer_size <= bptr->length) {
- ERROR("utils_oauth: base64_encode: Buffer too small.");
- BIO_free_all(b64);
- return -1;
- }
-
- /* Copy data to buffer. */
- memcpy(buffer, bptr->data, bptr->length);
- buffer[bptr->length] = 0;
-
- /* replace + with -, / with _ and remove padding = at the end */
- for (i = 0; i < bptr->length; i++) {
- if (buffer[i] == '+') {
- buffer[i] = '-';
- } else if (buffer[i] == '/') {
- buffer[i] = '_';
- } else if (buffer[i] == '=') {
- buffer[i] = 0;
- }
- }
-
- BIO_free_all(b64);
- return 0;
-} /* }}} int base64_encode_n */
-
-/* Base64-encodes "s" and stores the result in buffer.
- * Returns zero on success, non-zero otherwise. */
-static int base64_encode(char const *s, /* {{{ */
- char *buffer, size_t buffer_size) {
- return base64_encode_n(s, strlen(s), buffer, buffer_size);
-} /* }}} int base64_encode */
-
-/* get_header returns the base64 encoded OAuth header. */
-static int get_header(char *buffer, size_t buffer_size) /* {{{ */
-{
- char header[] = OAUTH_HEADER;
-
- return base64_encode(header, buffer, buffer_size);
-} /* }}} int get_header */
-
-/* get_claim constructs an OAuth claim and returns it as base64 encoded string.
- */
-static int get_claim(oauth_t *auth, char *buffer, size_t buffer_size) /* {{{ */
-{
- char claim[buffer_size];
- cdtime_t exp;
- cdtime_t iat;
- int status;
-
- iat = cdtime();
- exp = iat + OAUTH_EXPIRATION_TIME;
-
- /* create the claim set */
- status =
- snprintf(claim, sizeof(claim), OAUTH_CLAIM_FORMAT, auth->iss, auth->scope,
- auth->aud, (unsigned long)CDTIME_T_TO_TIME_T(exp),
- (unsigned long)CDTIME_T_TO_TIME_T(iat));
- if (status < 1)
- return -1;
- else if ((size_t)status >= sizeof(claim))
- return ENOMEM;
-
- DEBUG("utils_oauth: get_claim() = %s", claim);
-
- return base64_encode(claim, buffer, buffer_size);
-} /* }}} int get_claim */
-
-/* get_signature signs header and claim with pkey and returns the signature in
- * buffer. */
-static int get_signature(char *buffer, size_t buffer_size, /* {{{ */
- char const *header, char const *claim,
- EVP_PKEY *pkey) {
- char payload[buffer_size];
- size_t payload_len;
- char signature[buffer_size];
- unsigned int signature_size;
- int status;
-
- /* Make the string to sign */
- payload_len = snprintf(payload, sizeof(payload), "%s.%s", header, claim);
- if (payload_len < 1) {
- return -1;
- } else if (payload_len >= sizeof(payload)) {
- return ENOMEM;
- }
-
- /* Create the signature */
- signature_size = EVP_PKEY_size(pkey);
- if (signature_size > sizeof(signature)) {
- ERROR("utils_oauth: Signature is too large (%u bytes).", signature_size);
- return -1;
- }
-
- EVP_MD_CTX *ctx = EVP_MD_CTX_new();
-
- /* EVP_SignInit(3SSL) claims this is a void function, but in fact it returns
- * an int. We're not going to rely on this, though. */
- EVP_SignInit(ctx, EVP_sha256());
-
- status = EVP_SignUpdate(ctx, payload, payload_len);
- if (status != 1) {
- char errbuf[1024];
- ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
- ERROR("utils_oauth: EVP_SignUpdate failed: %s", errbuf);
-
- EVP_MD_CTX_free(ctx);
- return -1;
- }
-
- status =
- EVP_SignFinal(ctx, (unsigned char *)signature, &signature_size, pkey);
- if (status != 1) {
- char errbuf[1024];
- ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
- ERROR("utils_oauth: EVP_SignFinal failed: %s", errbuf);
-
- EVP_MD_CTX_free(ctx);
- return -1;
- }
-
- EVP_MD_CTX_free(ctx);
-
- return base64_encode_n(signature, (size_t)signature_size, buffer,
- buffer_size);
-} /* }}} int get_signature */
-
-static int get_assertion(oauth_t *auth, char *buffer,
- size_t buffer_size) /* {{{ */
-{
- char header[buffer_size];
- char claim[buffer_size];
- char signature[buffer_size];
- int status;
-
- status = get_header(header, sizeof(header));
- if (status != 0)
- return -1;
-
- status = get_claim(auth, claim, sizeof(claim));
- if (status != 0)
- return -1;
-
- status =
- get_signature(signature, sizeof(signature), header, claim, auth->key);
- if (status != 0)
- return -1;
-
- status = snprintf(buffer, buffer_size, "%s.%s.%s", header, claim, signature);
- if (status < 1)
- return -1;
- else if (status >= buffer_size)
- return ENOMEM;
-
- return 0;
-} /* }}} int get_assertion */
-
-int oauth_parse_json_token(char const *json, /* {{{ */
- char *out_access_token, size_t access_token_size,
- cdtime_t *expires_in) {
- time_t expire_in_seconds = 0;
- yajl_val root;
- yajl_val token_val;
- yajl_val expire_val;
- char errbuf[1024];
- const char *token_path[] = {"access_token", NULL};
- const char *expire_path[] = {"expires_in", NULL};
-
- root = yajl_tree_parse(json, errbuf, sizeof(errbuf));
- if (root == NULL) {
- ERROR("utils_oauth: oauth_parse_json_token: parse error %s", errbuf);
- return -1;
- }
-
- token_val = yajl_tree_get(root, token_path, yajl_t_string);
- if (token_val == NULL) {
- ERROR("utils_oauth: oauth_parse_json_token: access token field not found");
- yajl_tree_free(root);
- return -1;
- }
- sstrncpy(out_access_token, YAJL_GET_STRING(token_val), access_token_size);
-
- expire_val = yajl_tree_get(root, expire_path, yajl_t_number);
- if (expire_val == NULL) {
- ERROR("utils_oauth: oauth_parse_json_token: expire field found");
- yajl_tree_free(root);
- return -1;
- }
- expire_in_seconds = (time_t)YAJL_GET_INTEGER(expire_val);
- DEBUG("oauth_parse_json_token: expires_in %lu",
- (unsigned long)expire_in_seconds);
-
- *expires_in = TIME_T_TO_CDTIME_T(expire_in_seconds);
- yajl_tree_free(root);
- return 0;
-} /* }}} int oauth_parse_json_token */
-
-static int new_token(oauth_t *auth) /* {{{ */
-{
- CURL *curl;
- char assertion[1024];
- char post_data[1024];
- memory_t data;
- char access_token[256];
- cdtime_t expires_in;
- cdtime_t now;
- char curl_errbuf[CURL_ERROR_SIZE];
- int status = 0;
-
- data.size = 0;
- data.memory = NULL;
-
- now = cdtime();
-
- status = get_assertion(auth, assertion, sizeof(assertion));
- if (status != 0) {
- ERROR("utils_oauth: Failed to get token using service account %s.",
- auth->iss);
- return -1;
- }
-
- snprintf(post_data, sizeof(post_data), "grant_type=%s&assertion=%s",
- OAUTH_GRANT_TYPE, assertion);
-
- curl = curl_easy_init();
- if (curl == NULL) {
- ERROR("utils_oauth: curl_easy_init failed.");
- return -1;
- }
-
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
- curl_easy_setopt(curl, CURLOPT_POST, 1L);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
- curl_easy_setopt(curl, CURLOPT_URL, auth->url);
-
- status = curl_easy_perform(curl);
- if (status != CURLE_OK) {
- ERROR("utils_oauth: curl_easy_perform failed with status %i: %s", status,
- curl_errbuf);
-
- sfree(data.memory);
- curl_easy_cleanup(curl);
-
- return -1;
- } else {
- long http_code = 0;
-
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
- if ((http_code < 200) || (http_code >= 300)) {
- ERROR("utils_oauth: POST request to %s failed: HTTP error %ld", auth->url,
- http_code);
- if (data.memory != NULL)
- INFO("utils_oauth: Server replied: %s", data.memory);
-
- sfree(data.memory);
- curl_easy_cleanup(curl);
-
- return -1;
- }
- }
-
- status = oauth_parse_json_token(data.memory, access_token,
- sizeof(access_token), &expires_in);
- if (status != 0) {
- sfree(data.memory);
- curl_easy_cleanup(curl);
-
- return -1;
- }
-
- sfree(auth->token);
- auth->token = strdup(access_token);
- if (auth->token == NULL) {
- ERROR("utils_oauth: strdup failed");
- auth->valid_until = 0;
-
- sfree(data.memory);
- curl_easy_cleanup(curl);
- return -1;
- }
-
- INFO("utils_oauth: OAuth2 access token is valid for %.3fs",
- CDTIME_T_TO_DOUBLE(expires_in));
- auth->valid_until = now + expires_in;
-
- sfree(data.memory);
- curl_easy_cleanup(curl);
-
- return 0;
-} /* }}} int new_token */
-
-static int renew_token(oauth_t *auth) /* {{{ */
-{
- /* Renew OAuth token 30 seconds *before* it expires. */
- cdtime_t const slack = TIME_T_TO_CDTIME_T(30);
-
- if (auth->valid_until > (cdtime() + slack))
- return 0;
-
- return new_token(auth);
-} /* }}} int renew_token */
-
-static oauth_t *oauth_create(char const *url, char const *iss,
- char const *scope, char const *aud,
- EVP_PKEY *key) /* {{{ */
-{
- oauth_t *auth;
-
- if ((url == NULL) || (iss == NULL) || (scope == NULL) || (aud == NULL) ||
- (key == NULL))
- return NULL;
-
- auth = malloc(sizeof(*auth));
- if (auth == NULL)
- return NULL;
- memset(auth, 0, sizeof(*auth));
-
- auth->url = strdup(url);
- auth->iss = strdup(iss);
- auth->scope = strdup(scope);
- auth->aud = strdup(aud);
-
- if ((auth->url == NULL) || (auth->iss == NULL) || (auth->scope == NULL) ||
- (auth->aud == NULL)) {
- oauth_destroy(auth);
- return NULL;
- }
-
- auth->key = key;
-
- return auth;
-} /* }}} oauth_t *oauth_create */
-
-/*
- * Public
- */
-oauth_google_t oauth_create_google_json(char const *buffer, char const *scope) {
- char errbuf[1024];
- yajl_val root = yajl_tree_parse(buffer, errbuf, sizeof(errbuf));
- if (root == NULL) {
- ERROR("utils_oauth: oauth_create_google_json: parse error %s", errbuf);
- return (oauth_google_t){NULL};
- }
-
- yajl_val field_project =
- yajl_tree_get(root, (char const *[]){"project_id", NULL}, yajl_t_string);
- if (field_project == NULL) {
- ERROR("utils_oauth: oauth_create_google_json: project_id field not found");
- yajl_tree_free(root);
- return (oauth_google_t){NULL};
- }
- char const *project_id = YAJL_GET_STRING(field_project);
-
- yajl_val field_iss = yajl_tree_get(
- root, (char const *[]){"client_email", NULL}, yajl_t_string);
- if (field_iss == NULL) {
- ERROR(
- "utils_oauth: oauth_create_google_json: client_email field not found");
- yajl_tree_free(root);
- return (oauth_google_t){NULL};
- }
-
- yajl_val field_token_uri =
- yajl_tree_get(root, (char const *[]){"token_uri", NULL}, yajl_t_string);
- char const *token_uri = (field_token_uri != NULL)
- ? YAJL_GET_STRING(field_token_uri)
- : GOOGLE_TOKEN_URL;
-
- yajl_val field_priv_key =
- yajl_tree_get(root, (char const *[]){"private_key", NULL}, yajl_t_string);
- if (field_priv_key == NULL) {
- ERROR("utils_oauth: oauth_create_google_json: private_key field not found");
- yajl_tree_free(root);
- return (oauth_google_t){NULL};
- }
-
- BIO *bp = BIO_new_mem_buf(YAJL_GET_STRING(field_priv_key), -1);
- EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
- if (pkey == NULL) {
- char errbuf[1024];
- ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
- ERROR(
- "utils_oauth: oauth_create_google_json: parsing private key failed: %s",
- errbuf);
- BIO_free(bp);
- yajl_tree_free(root);
- return (oauth_google_t){NULL};
- }
-
- BIO_free(bp);
-
- oauth_t *oauth = oauth_create(token_uri, YAJL_GET_STRING(field_iss), scope,
- token_uri, pkey);
- if (oauth == NULL) {
- yajl_tree_free(root);
- return (oauth_google_t){NULL};
- }
-
- oauth_google_t ret = {
- .project_id = strdup(project_id), .oauth = oauth,
- };
-
- yajl_tree_free(root);
- return ret;
-} /* oauth_google_t oauth_create_google_json */
-
-oauth_google_t oauth_create_google_file(char const *path,
- char const *scope) { /* {{{ */
- int fd = open(path, O_RDONLY);
- if (fd == -1)
- return (oauth_google_t){NULL};
-
- struct stat st = {0};
- if (fstat(fd, &st) != 0) {
- close(fd);
- return (oauth_google_t){NULL};
- }
-
- size_t buf_size = (size_t)st.st_size;
- char *buf = calloc(1, buf_size + 1);
- if (buf == NULL) {
- close(fd);
- return (oauth_google_t){NULL};
- }
-
- if (sread(fd, buf, buf_size) != 0) {
- free(buf);
- close(fd);
- return (oauth_google_t){NULL};
- }
- close(fd);
- buf[buf_size] = 0;
-
- oauth_google_t ret = oauth_create_google_json(buf, scope);
-
- free(buf);
- return ret;
-} /* }}} oauth_google_t oauth_create_google_file */
-
-/* oauth_create_google_default checks for JSON credentials in well-known
- * positions, similar to gcloud and other tools. */
-oauth_google_t oauth_create_google_default(char const *scope) {
- char const *app_creds;
- if ((app_creds = getenv("GOOGLE_APPLICATION_CREDENTIALS")) != NULL) {
- oauth_google_t ret = oauth_create_google_file(app_creds, scope);
- if (ret.oauth == NULL) {
- ERROR("The environment variable GOOGLE_APPLICATION_CREDENTIALS is set to "
- "\"%s\" but that file could not be read.",
- app_creds);
- } else {
- return ret;
- }
- }
-
- char const *home;
- if ((home = getenv("HOME")) != NULL) {
- char path[PATH_MAX];
- snprintf(path, sizeof(path),
- "%s/.config/gcloud/application_default_credentials.json", home);
-
- oauth_google_t ret = oauth_create_google_file(path, scope);
- if (ret.oauth != NULL) {
- return ret;
- }
- }
-
- return (oauth_google_t){NULL};
-} /* }}} oauth_google_t oauth_create_google_default */
-
-void oauth_destroy(oauth_t *auth) /* {{{ */
-{
- if (auth == NULL)
- return;
-
- sfree(auth->url);
- sfree(auth->iss);
- sfree(auth->scope);
- sfree(auth->aud);
-
- if (auth->key != NULL) {
- EVP_PKEY_free(auth->key);
- auth->key = NULL;
- }
-
- sfree(auth);
-} /* }}} void oauth_destroy */
-
-int oauth_access_token(oauth_t *auth, char *buffer,
- size_t buffer_size) /* {{{ */
-{
- int status;
-
- if (auth == NULL)
- return EINVAL;
-
- status = renew_token(auth);
- if (status != 0)
- return status;
- assert(auth->token != NULL);
-
- sstrncpy(buffer, auth->token, buffer_size);
- return 0;
-} /* }}} int oauth_access_token */
+++ /dev/null
-/**
- * collectd - src/utils_oauth.h
- * ISC license
- *
- * Copyright (C) 2017 Florian Forster
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at collectd.org>
- **/
-
-#ifndef UTILS_OAUTH_H
-#define UTILS_OAUTH_H
-
-#include "collectd.h"
-#include "utils_time.h"
-
-#ifndef GOOGLE_OAUTH_URL
-#define GOOGLE_OAUTH_URL "https://www.googleapis.com/oauth2/v3/token"
-#endif
-
-struct oauth_s;
-typedef struct oauth_s oauth_t;
-
-int oauth_parse_json_token(char const *json, char *out_access_token,
- size_t access_token_size, cdtime_t *expires_in);
-
-typedef struct {
- char *project_id;
- oauth_t *oauth;
-} oauth_google_t;
-
-/* oauth_create_google_json creates an OAuth object from JSON encoded
- * credentials. */
-oauth_google_t oauth_create_google_json(char const *json, char const *scope);
-
-/* oauth_create_google_file reads path, which contains JSON encoded service
- * account credentials, and returns an OAuth object. */
-oauth_google_t oauth_create_google_file(char const *path, char const *scope);
-
-/* oauth_create_google_default looks for service account credentials in a couple
- * of well-known places and returns an OAuth object if found. The well known
- * locations are:
- *
- * - ${GOOGLE_APPLICATION_CREDENTIALS}
- * - ${HOME}/.config/gcloud/application_default_credentials.json
- */
-oauth_google_t oauth_create_google_default(char const *scope);
-
-/* oauth_destroy frees all resources associated with an OAuth object. */
-void oauth_destroy(oauth_t *auth);
-
-int oauth_access_token(oauth_t *auth, char *buffer, size_t buffer_size);
-
-#endif
+++ /dev/null
-/**
- * collectd - src/tests/utils_oauth_test.c
- * Copyright (C) 2015 Google Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Florian Forster <octo at google.com>
- **/
-
-#include "testing.h"
-#include "utils_oauth.h"
-
-struct {
- char *json;
- int status;
- char *access_token;
- cdtime_t expires_in;
-} cases[] = {
- {
- "{\"access_token\":\"MaeC6kaePhie1ree\",\"expires_in\":3600}",
- /* status = */ 0, "MaeC6kaePhie1ree", TIME_T_TO_CDTIME_T_STATIC(3600),
- },
- {
- "{\"token_type\":\"Bearer\",\"expires_in\":1800,\"access_token\":"
- "\"aeThiebee2gushuY\"}",
- /* status = */ 0, "aeThiebee2gushuY", TIME_T_TO_CDTIME_T_STATIC(1800),
- },
- {
- "{\"ignored_key\":\"uaph5aewaeghi1Ge\",\"expires_in\":3600}",
- /* status = */ -1, NULL, 0,
- },
- {
- /* expires_in missing */
- "{\"access_token\":\"shaephohbie9Ahch\"}",
- /* status = */ -1, NULL, 0,
- },
-};
-
-DEF_TEST(simple) /* {{{ */
-{
- size_t i;
- _Bool success = 1;
-
- for (i = 0; i < (sizeof(cases) / sizeof(cases[0])); i++) {
- char buffer[1024];
- cdtime_t expires_in;
-
- EXPECT_EQ_INT(cases[i].status,
- oauth_parse_json_token(cases[i].json, buffer, sizeof(buffer),
- &expires_in));
- if (cases[i].status != 0)
- continue;
-
- EXPECT_EQ_STR(cases[i].access_token, buffer);
- EXPECT_EQ_UINT64(cases[i].expires_in, expires_in);
- }
-
- return success ? 0 : -1;
-} /* }}} simple */
-
-DEF_TEST(oauth_create_google_json) {
- char const *in =
- "{\"type\": \"service_account\","
- "\"project_id\":\"collectd.org:unit-test\","
- "\"private_key_id\": \"ed7b4eb6c1b61a7bedab5bcafff374f7fc820698\","
- "\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n"
- "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNvS71Lr2WIEqx\\n"
- "U766iJGORVVib0FnHhOf/0FEI4Hw+tF11vP3LZj0AyQFIi/h2l2EDXOr43C6Gt+K\\n"
- "0stsyaWvRNzeQa+dUFY5A/ZEtdvYVPq7KudML5Hs9DNmWFlM/iIfQyIUJ+vHv7fe\\n"
- "pJGgu4ZgSkNWehmWj3qiRzIvYxKvDIQizqPZNlTh+33KQcT2x+ErkuB3snQu8hSK\\n"
- "HAg2sCvORqKGOvN9F4bAqXt5T0NVjGy4YXeuif1p/Np/GH6Ys1p+etgGwvIimXIv\\n"
- "jFL9K/ZtrTOcFdy4R5bwrj2piCZa2T5H6fupVp2tVgIuS53r2fEaBMLD97oAvwZ3\\n"
- "9XPxG1NLAgMBAAECggEACgHroKcrN1FkdgyzSIKFG1evCBCOV17kqHyI5wYXzNTT\\n"
- "zyNrZDjBFGQkt+U0/AucTznnnahSCZNuD+QiBgLRqYgJevwp99Z6YzVDS438Xsuq\\n"
- "Ezmf3O+sGEu78Pys11cTP38LT3yuS4iSqo9Jus5JrTG05dDJoYO4J4rxW3xlDRj8\\n"
- "lQUimXI+S9skaSusf0oErDrjuQG9dxmhnGcSEX+rIe9G0UygTNuI0KKGJ8jmnPz5\\n"
- "OS+sM8qrKcnjrvENFWKLb11HlliHkh6dILoO5rvf5DR+XGKM7BFAsdWg6oI7SFGh\\n"
- "S6zGZ0jUR7QAugrjbTlDOCnAuZ+Mbc/4yHZ3u5PlcQKBgQDuvH1ds1YmmbOllOK5\\n"
- "JtkdjCUUyH1bgkMrmcg/KkRARPRHQvfAioZsC6d0fa6jq0kTW/3Zu14IsVXgM8xK\\n"
- "fuNSp8LdY+NCtJnfvdLaChgAwZaQLX4qgV0qYw8iLv5ifa4ZY0qaZioJCzkv57y1\\n"
- "KkavYvITboO7aUSa441Zko9c+wKBgQDcndg0QpWH6JMz/FkCf/KDyW/cUODfKXhP\\n"
- "5p9eTcVlfDL2sAb2RzVhvKZcuWXVwnfaDP0oBj2/SBLGx0idUb+VHdM/IGiLroyK\\n"
- "pAHpNM//dowiGL1qPPOLXrzF/vn+w4t2Dqggfcqu52SzRiyaxUtSMnNyyyU19cO+\\n"
- "pb7wAS5x8QKBgCW7WL0UeQtEw6Xp8CN/RlVrLvkn7tglsGQVvBZvobXesBULOokN\\n"
- "28z70o2Qx6dKjRQoN+jPuj75eC8lQKaNg3Qu25eOD/8c+CzqnYakjcKg1iEXb5dc\\n"
- "NtNaMKwgbUg3wOp2TPY2K3KeeX1ezO59LgrOQqBbmSpnqtYoHNEJXus9AoGAWl/y\\n"
- "9J2eIdm9i5tBX0vIrgHz5/3d0K1tUtX3zSrwxT0Wp4W+pF7RWGNuhyePtvx+Gn4d\\n"
- "qqq72sMMpg93CLM3Vz+rjP2atjXf7t92xPDUkCMhDsqxtXaYkixSCo4EHUA/vjIM\\n"
- "35qIUBQMZYBGv3Q5AcgXERx09uDhuhSt3iWtwBECgYAHFnCh8fKsJbQrVN10tU/h\\n"
- "ofVx0KZkUpBz8eNQPuxt4aY+LyWsKVKtnduw2WdumuOY66cUN1lsi8Bz/cq1dhPt\\n"
- "Oc2S7pqjbu2Q1Oqx+/yr6jqsvKaSxHmcpbWQBsGn6UaWZgYZcAtQBcqDAp7pylwj\\n"
- "tejRh0NB8d81H5Dli1Qfzw==\\n"
- "-----END PRIVATE KEY-----\\n\","
- "\"client_email\":\"example-sacct@unit-test.iam.gserviceaccount.com\", "
- "\"client_id\": \"109958449193027604084\","
- "\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\","
- "\"token_uri\":\"https://accounts.google.com/o/oauth2/token\","
- "\"auth_provider_x509_cert_url\":"
- "\"https://www.googleapis.com/oauth2/v1/certs\","
- "\"client_x509_cert_url\":\"https://www.googleapis.com/robot/v1/"
- "metadata/x509/example-sacct%40ssc-serv-dev.iam.gserviceaccount.com\"}";
-
- oauth_google_t ret =
- oauth_create_google_json(in, "https://collectd.org/example.scope");
-
- EXPECT_EQ_STR("collectd.org:unit-test", ret.project_id);
-
- CHECK_NOT_NULL(ret.oauth);
- struct {
- char *url;
- char *iss;
- char *aud;
- char *scope;
- } *obj = (void *)ret.oauth;
-
- EXPECT_EQ_STR("https://accounts.google.com/o/oauth2/token", obj->url);
- EXPECT_EQ_STR("example-sacct@unit-test.iam.gserviceaccount.com", obj->iss);
- EXPECT_EQ_STR("https://collectd.org/example.scope", obj->scope);
-
- free(ret.project_id);
- oauth_destroy(ret.oauth);
-
- return 0;
-}
-
-int main(int argc, char **argv) /* {{{ */
-{
- RUN_TEST(simple);
- RUN_TEST(oauth_create_google_json);
-
- END_TEST;
-} /* }}} int main */
+++ /dev/null
-/**
- * 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 <volodymyrx.mytnyk@intel.com>
- **/
-
-/* 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 <netdb.h>
-#endif
-#if HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#if HAVE_POLL_H
-#include <poll.h>
-#endif
-#if HAVE_SYS_UN_H
-#include <sys/un.h>
-#endif
-
-#include <semaphore.h>
-
-#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: [<json-value>, <table-updates>] */
- 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 <json-value> */
- 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
- * [<db-name>, <json-value>, <monitor-requests>] */
- OVS_YAJL_CALL(yajl_gen_array_open, jgen);
- {
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, OVS_DB_DEFAULT_DB_NAME);
-
- /* uid string <json-value> */
- snprintf(uid_str, sizeof(uid_str), "%" PRIX64, new_cb->uid);
- OVS_YAJL_CALL(ovs_yajl_gen_tstring, jgen, uid_str);
-
- /* <monitor-requests> */
- 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);
- {
- /* <monitor-request> */
- 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" : <YAJL return value>
- * "key_b" : <YAJL return value>
- * }
- */
-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:
- *
- * <pair>
- * A 2-element JSON array that represents a pair within a database
- * map. The first element is an <atom> that represents the key, and
- * the second element is an <atom> that represents the value.
- *
- * <map>
- * 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 <pair>s giving the
- * values in the map. All of the <pair>s must have the same key and
- * value types.
- *
- * EXAMPLE:
- * [
- * "map", [
- * [ "key_a", <YAJL value>], [ "key_b", <YAJL value>], ...
- * ]
- * ]
- */
-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;
-}
+++ /dev/null
-/**
- * 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 <volodymyrx.mytnyk@intel.com>
- *
- * 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 <yajl/yajl_gen.h>
-#include <yajl/yajl_tree.h>
-
-/* 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
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "utils_rrdcreate.h"
-
-#include <pthread.h>
-#include <rrd.h>
-
-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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#ifndef UTILS_RRDCREATE_H
-#define UTILS_RRDCREATE_H 1
-
-#include "plugin.h"
-
-#include <stddef.h>
-
-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 */
+++ /dev/null
-/**
- * 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 <lukeh at c-ware.com>
- * Florian Forster <octo at collectd.org>
- *
- * 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 */
+++ /dev/null
-/**
- * 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 <lukeh at c-ware.com>
- *
- * 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 */
#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 {
* regular expressions.
*/
-#include "utils_latency_config.h"
-#include "utils_match.h"
+#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;
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- */
-
-#include "collectd.h"
-#include "utils_taskstats.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_time.h"
-
-#include <libmnl/libmnl.h>
-#include <linux/genetlink.h>
-#include <linux/taskstats.h>
-
-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;
-}
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- */
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include <pthread.h>
-#include <regex.h>
-
-#include "common.h"
-#include "utils_avltree.h"
-#include "utils_vl_lookup.h"
-
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
+++ /dev/null
-/**
- * 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 <octo at collectd.org>
- **/
-
-#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 */
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if HAVE_VARNISH_V4 || HAVE_VARNISH_V5
#include <vapi/vsc.h>
#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 <libgen.h> /* for basename(3) */
#include <libvirt/libvirt.h>
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;
};
#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, ...) \
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. */
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,
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 };
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
};
static unsigned int extra_stats = ex_stats_none;
{"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},
{NULL, ex_stats_none},
};
static int refresh_lists(struct lv_read_instance *inst);
-struct lv_block_info {
+struct lv_block_stats {
virDomainBlockStatsStruct bi;
long long rd_total_times;
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 */
ERROR(PLUGIN_NAME " plugin: %s failed: %s", (s), err->message); \
} while (0)
+static char *metadata_get_hostname(virDomainPtr dom) {
+ const char *xpath_str = NULL;
+ if (hm_xpath == NULL)
+ xpath_str = "/instance/name/text()";
+ else
+ xpath_str = hm_xpath;
+
+ const char *namespace = NULL;
+ if (hm_ns == NULL) {
+ namespace = "http://openstack.org/xmlns/libvirt/nova/1.0";
+ } else {
+ namespace = hm_ns;
+ }
+
+ char *metadata_str = virDomainGetMetadata(
+ dom, VIR_DOMAIN_METADATA_ELEMENT, namespace, VIR_DOMAIN_AFFECT_CURRENT);
+ if (metadata_str == NULL) {
+ return NULL;
+ }
+
+ char *hostname = NULL;
+ xmlXPathContextPtr xpath_ctx = NULL;
+ xmlXPathObjectPtr xpath_obj = NULL;
+ xmlNodePtr xml_node = NULL;
+
+ xmlDocPtr xml_doc =
+ xmlReadDoc((xmlChar *)metadata_str, NULL, NULL, XML_PARSE_NONET);
+ if (xml_doc == NULL) {
+ ERROR(PLUGIN_NAME " plugin: xmlReadDoc failed to read metadata");
+ goto metadata_end;
+ }
+
+ xpath_ctx = xmlXPathNewContext(xml_doc);
+ if (xpath_ctx == NULL) {
+ ERROR(PLUGIN_NAME " plugin: xmlXPathNewContext(%s) failed for metadata",
+ metadata_str);
+ goto metadata_end;
+ }
+ xpath_obj = xmlXPathEval((xmlChar *)xpath_str, xpath_ctx);
+ if (xpath_obj == NULL) {
+ ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) failed for metadata",
+ xpath_str);
+ goto metadata_end;
+ }
+
+ if (xpath_obj->type != XPATH_NODESET) {
+ ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unexpected return type %d "
+ "(wanted %d) for metadata",
+ xpath_str, xpath_obj->type, XPATH_NODESET);
+ goto metadata_end;
+ }
+
+ // TODO(sileht): We can support || operator by looping on nodes here
+ if (xpath_obj->nodesetval == NULL || xpath_obj->nodesetval->nodeNr != 1) {
+ WARNING(PLUGIN_NAME " plugin: xmlXPathEval(%s) return nodeset size=%i "
+ "expected=1 for metadata",
+ xpath_str,
+ (xpath_obj->nodesetval == NULL) ? 0
+ : xpath_obj->nodesetval->nodeNr);
+ goto metadata_end;
+ }
+
+ xml_node = xpath_obj->nodesetval->nodeTab[0];
+ if (xml_node->type == XML_TEXT_NODE) {
+ hostname = strdup((const char *)xml_node->content);
+ } else if (xml_node->type == XML_ATTRIBUTE_NODE) {
+ hostname = strdup((const char *)xml_node->children->content);
+ } else {
+ ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unsupported node type %d",
+ xpath_str, xml_node->type);
+ goto metadata_end;
+ }
+
+ if (hostname == NULL) {
+ ERROR(PLUGIN_NAME " plugin: strdup(%s) hostname failed", xpath_str);
+ goto metadata_end;
+ }
+
+metadata_end:
+ if (xpath_obj)
+ xmlXPathFreeObject(xpath_obj);
+ if (xpath_ctx)
+ xmlXPathFreeContext(xpath_ctx);
+ if (xml_doc)
+ xmlFreeDoc(xml_doc);
+ sfree(metadata_str);
+ return hostname;
+}
+
static void init_value_list(value_list_t *vl, virDomainPtr dom) {
const char *name;
char uuid[VIR_UUID_STRING_BUFLEN];
if (virDomainGetUUIDString(dom, uuid) == 0)
SSTRNCAT(vl->host, uuid, sizeof(vl->host));
break;
+ case hf_metadata:
+ name = metadata_get_hostname(dom);
+ if (name)
+ SSTRNCAT(vl->host, name, sizeof(vl->host));
+ break;
}
}
if (virDomainGetUUIDString(dom, uuid) == 0)
SSTRNCAT(vl->plugin_instance, uuid, sizeof(vl->plugin_instance));
break;
+ case plginst_metadata:
+ name = metadata_get_hostname(dom);
+ if (name)
+ SSTRNCAT(vl->plugin_instance, name, sizeof(vl->plugin_instance));
+ break;
}
}
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;
snprintf(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) {
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) {
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)
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) &&
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) {
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 ||
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);
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 */
}
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;
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;
static int get_memory_stats(virDomainPtr domain) {
virDomainMemoryStatPtr minfo =
- calloc(VIR_DOMAIN_MEMORY_STAT_NR, sizeof(virDomainMemoryStatStruct));
+ calloc(VIR_DOMAIN_MEMORY_STAT_NR, sizeof(*minfo));
if (minfo == NULL) {
- ERROR("virt plugin: malloc failed.");
+ ERROR("virt plugin: calloc failed.");
return -1;
}
}
#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);
+ 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;
}
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;
}
/* 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) {
/* 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",
if (virInitialize() != 0)
return -1;
+ /* Init ignorelists if there was no explicit configuration */
+ if (lv_init_ignorelists() != 0)
+ return -1;
+
/* event implementation must be registered before connection is opened */
if (!persistent_notification)
if (register_event_impl() != 0)
if (lv_connect() != 0)
return -1;
- DEBUG(PLUGIN_NAME " plugin: starting event loop");
-
if (!persistent_notification) {
virt_notif_thread_init(¬if_thread);
if (start_event_loop(¬if_thread) != 0)
return 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];
+ snprintf(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;
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;
#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;
/* 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. */
}
#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)
* 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);
}
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;
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)
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;
}
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)
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++;
}
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);
+ int r = ignorelist_match(il, name);
sfree(name);
return r;
}
lv_fini_instance(i);
}
- DEBUG(PLUGIN_NAME " plugin: stopping event loop");
-
if (!persistent_notification)
stop_event_loop(¬if_thread);
}
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);
}
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if KERNEL_LINUX
static const char *config_keys[] = {"Verbose"};
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <dirent.h>
#include <sys/types.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#if KERNEL_LINUX
#include <linux/if.h>
#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 <netdb.h>
#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 <curl/curl.h>
#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 <errno.h>
#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 <netdb.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include <mongoc.h>
#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"
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;
}
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;
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <hiredis/hiredis.h>
#include <sys/time.h>
#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"
#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"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include <arpa/inet.h>
#include <errno.h>
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.",
#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 <curl/curl.h>
#include <pthread.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include "utils_cache.h"
#include "utils_random.h"
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <xenctrl.h>
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);
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);
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <xmms/xmmsctrl.h>
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
/*
* Global variables
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <procfs.h>
#include <zone.h>
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
#define MAX_PROCFS_PATH 40
#define FRC2PCT(pp) (((float)(pp)) / 0x8000 * 100)
#include "collectd.h"
-#include "common.h"
#include "plugin.h"
+#include "utils/common/common.h"
#include <netdb.h>
#include <netinet/in.h>