Merge branch 'collectd-5.8'
authorFlorian Forster <octo@collectd.org>
Wed, 20 Feb 2019 09:10:35 +0000 (10:10 +0100)
committerFlorian Forster <octo@collectd.org>
Wed, 20 Feb 2019 09:10:35 +0000 (10:10 +0100)
408 files changed:
.cirrus.yml [new file with mode: 0644]
.github/issue_template.md
.gitignore
.gitmodules [new file with mode: 0644]
.travis.yml
AUTHORS
CODEOWNERS [new file with mode: 0644]
CONTRIBUTING.md [deleted file]
Makefile.am
README
build.sh
configure.ac
contrib/collection3/bin/graph.cgi
contrib/collection3/etc/collection.conf
contrib/collection3/share/style.css
contrib/docker/50docker-apt-conf
contrib/docker/Dockerfile
contrib/docker/rootfs_prefix/rootfs_prefix.c
contrib/exec-munin.px
contrib/exec-nagios.px
contrib/php-collection/functions.php
contrib/redhat/collectd.spec
contrib/sles10.1/collectd.spec
contrib/snmp-probe-host.px
contrib/systemd.collectd.service
docs/CONTRIBUTING.md [new file with mode: 0644]
docs/README.virt.md
docs/review_comments.md [new file with mode: 0644]
docs/review_comments_example.png [new file with mode: 0644]
gnulib [new submodule]
src/aggregation.c
src/amqp.c
src/amqp1.c [new file with mode: 0644]
src/apache.c
src/apcups.c
src/apple_sensors.c
src/aquaero.c
src/ascent.c
src/barometer.c
src/battery.c
src/battery_statefs.c
src/bind.c
src/ceph.c
src/ceph_test.c
src/cgroups.c
src/chrony.c
src/collectd-exec.pod
src/collectd-lua.pod
src/collectd-nagios.c
src/collectd-nagios.pod
src/collectd-python.pod
src/collectd-snmp.pod
src/collectd-tg.c
src/collectd-threshold.pod
src/collectd-unixsock.pod
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.pod
src/collectdmon.c
src/conntrack.c
src/contextswitch.c
src/cpu.c
src/cpufreq.c
src/cpusleep.c
src/csv.c
src/curl.c
src/curl_json.c
src/curl_json_test.c
src/curl_xml.c
src/daemon/cmd.c [new file with mode: 0644]
src/daemon/cmd.h [new file with mode: 0644]
src/daemon/cmd_windows.c [new file with mode: 0755]
src/daemon/collectd.c
src/daemon/collectd.h
src/daemon/common.c [deleted file]
src/daemon/common.h [deleted file]
src/daemon/common_test.c [deleted file]
src/daemon/configfile.c
src/daemon/configfile.h
src/daemon/filter_chain.c
src/daemon/globals.c
src/daemon/globals.h
src/daemon/meta_data.c [deleted file]
src/daemon/meta_data.h [deleted file]
src/daemon/meta_data_test.c [deleted file]
src/daemon/plugin.c
src/daemon/plugin.h
src/daemon/plugin_mock.c
src/daemon/types_list.c
src/daemon/utils_avltree.c [deleted file]
src/daemon/utils_avltree.h [deleted file]
src/daemon/utils_avltree_test.c [deleted file]
src/daemon/utils_cache.c
src/daemon/utils_cache.h
src/daemon/utils_cache_mock.c
src/daemon/utils_complain.c
src/daemon/utils_complain.h
src/daemon/utils_heap.c [deleted file]
src/daemon/utils_heap.h [deleted file]
src/daemon/utils_heap_test.c [deleted file]
src/daemon/utils_random.c
src/daemon/utils_subst.c
src/daemon/utils_subst.h
src/daemon/utils_subst_test.c
src/daemon/utils_threshold.c
src/daemon/utils_time.c
src/dbi.c
src/df.c
src/disk.c
src/dns.c
src/dpdkevents.c
src/dpdkstat.c
src/drbd.c
src/email.c
src/entropy.c
src/ethstat.c
src/exec.c
src/fhcount.c
src/filecount.c
src/fscache.c
src/gmond.c
src/gps.c
src/gpu_nvidia.c [new file with mode: 0644]
src/grpc.cc
src/hddtemp.c
src/hugepages.c
src/intel_pmu.c
src/intel_rdt.c
src/interface.c
src/ipc.c
src/ipmi.c
src/iptables.c
src/ipvs.c
src/irq.c
src/java.c
src/libcollectdclient/client.c
src/libcollectdclient/collectd/network.h
src/libcollectdclient/collectd/server.h
src/libcollectdclient/network.c
src/libcollectdclient/network_buffer.c
src/libcollectdclient/network_parse.c
src/libcollectdclient/network_parse_test.c
src/libcollectdclient/server.c
src/liboconfig/oconfig.c
src/liboconfig/parser.y
src/liboconfig/scanner.l
src/load.c
src/log_logstash.c
src/logfile.c
src/lpar.c
src/lua.c
src/lvm.c
src/madwifi.c
src/match_empty_counter.c
src/match_hashed.c
src/match_regex.c
src/match_timediff.c
src/match_value.c
src/mbmon.c
src/mcelog.c
src/md.c
src/memcachec.c
src/memcached.c
src/memory.c
src/mic.c
src/modbus.c
src/mqtt.c
src/msr-index.h
src/multimeter.c
src/mysql.c
src/netapp.c
src/netlink.c
src/network.c
src/nfs.c
src/nginx.c
src/notify_desktop.c
src/notify_email.c
src/notify_nagios.c
src/ntpd.c
src/numa.c
src/nut.c
src/olsrd.c
src/onewire.c
src/openldap.c
src/openvpn.c
src/oracle.c
src/ovs_events.c
src/ovs_stats.c
src/pcie_errors.c [new file with mode: 0644]
src/pcie_errors_test.c [new file with mode: 0644]
src/perl.c
src/pf.c
src/pinba.c
src/ping.c
src/postgresql.c
src/powerdns.c
src/processes.c
src/protocols.c
src/pyconfig.c
src/python.c
src/pyvalues.c
src/redis.c
src/routeros.c
src/rrdcached.c
src/rrdtool.c
src/sensors.c
src/serial.c
src/sigrok.c
src/smart.c
src/snmp.c
src/snmp_agent.c
src/snmp_agent_test.c [new file with mode: 0644]
src/statsd.c
src/swap.c
src/synproxy.c
src/syslog.c
src/table.c
src/tail.c
src/tail_csv.c
src/tape.c
src/target_notification.c
src/target_replace.c
src/target_scale.c
src/target_set.c
src/target_v5upgrade.c
src/tcpconns.c
src/teamspeak2.c
src/ted.c
src/testing.h
src/thermal.c
src/threshold.c
src/tokyotyrant.c
src/turbostat.c
src/types.db
src/unixsock.c
src/uptime.c
src/users.c
src/utils/avltree/avltree.c [new file with mode: 0644]
src/utils/avltree/avltree.h [new file with mode: 0644]
src/utils/avltree/avltree_test.c [new file with mode: 0644]
src/utils/cmds/cmds.c [new file with mode: 0644]
src/utils/cmds/cmds.h [new file with mode: 0644]
src/utils/cmds/cmds_test.c [new file with mode: 0644]
src/utils/cmds/flush.c [new file with mode: 0644]
src/utils/cmds/flush.h [new file with mode: 0644]
src/utils/cmds/getthreshold.c [new file with mode: 0644]
src/utils/cmds/getthreshold.h [new file with mode: 0644]
src/utils/cmds/getval.c [new file with mode: 0644]
src/utils/cmds/getval.h [new file with mode: 0644]
src/utils/cmds/listval.c [new file with mode: 0644]
src/utils/cmds/listval.h [new file with mode: 0644]
src/utils/cmds/parse_option.c [new file with mode: 0644]
src/utils/cmds/parse_option.h [new file with mode: 0644]
src/utils/cmds/putnotif.c [new file with mode: 0644]
src/utils/cmds/putnotif.h [new file with mode: 0644]
src/utils/cmds/putval.c [new file with mode: 0644]
src/utils/cmds/putval.h [new file with mode: 0644]
src/utils/common/common.c [new file with mode: 0644]
src/utils/common/common.h [new file with mode: 0644]
src/utils/common/common_test.c [new file with mode: 0644]
src/utils/config_cores/config_cores.c [new file with mode: 0644]
src/utils/config_cores/config_cores.h [new file with mode: 0644]
src/utils/config_cores/config_cores_test.c [new file with mode: 0644]
src/utils/crc32/crc32.c [new file with mode: 0644]
src/utils/crc32/crc32.h [new file with mode: 0644]
src/utils/curl_stats/curl_stats.c [new file with mode: 0644]
src/utils/curl_stats/curl_stats.h [new file with mode: 0644]
src/utils/db_query/db_query.c [new file with mode: 0644]
src/utils/db_query/db_query.h [new file with mode: 0644]
src/utils/deq/deq.h [new file with mode: 0644]
src/utils/dns/dns.c [new file with mode: 0644]
src/utils/dns/dns.h [new file with mode: 0644]
src/utils/dpdk/dpdk.c [new file with mode: 0644]
src/utils/dpdk/dpdk.h [new file with mode: 0644]
src/utils/format_graphite/format_graphite.c [new file with mode: 0644]
src/utils/format_graphite/format_graphite.h [new file with mode: 0644]
src/utils/format_graphite/format_graphite_test.c [new file with mode: 0644]
src/utils/format_json/format_json.c [new file with mode: 0644]
src/utils/format_json/format_json.h [new file with mode: 0644]
src/utils/format_json/format_json_test.c [new file with mode: 0644]
src/utils/format_kairosdb/format_kairosdb.c [new file with mode: 0644]
src/utils/format_kairosdb/format_kairosdb.h [new file with mode: 0644]
src/utils/format_stackdriver/format_stackdriver.c [new file with mode: 0644]
src/utils/format_stackdriver/format_stackdriver.h [new file with mode: 0644]
src/utils/format_stackdriver/format_stackdriver_test.c [new file with mode: 0644]
src/utils/gce/gce.c [new file with mode: 0644]
src/utils/gce/gce.h [new file with mode: 0644]
src/utils/heap/heap.c [new file with mode: 0644]
src/utils/heap/heap.h [new file with mode: 0644]
src/utils/heap/heap_test.c [new file with mode: 0644]
src/utils/ignorelist/ignorelist.c [new file with mode: 0644]
src/utils/ignorelist/ignorelist.h [new file with mode: 0644]
src/utils/latency/latency.c [new file with mode: 0644]
src/utils/latency/latency.h [new file with mode: 0644]
src/utils/latency/latency_config.c [new file with mode: 0644]
src/utils/latency/latency_config.h [new file with mode: 0644]
src/utils/latency/latency_test.c [new file with mode: 0644]
src/utils/lookup/vl_lookup.c [new file with mode: 0644]
src/utils/lookup/vl_lookup.h [new file with mode: 0644]
src/utils/lookup/vl_lookup_test.c [new file with mode: 0644]
src/utils/match/match.c [new file with mode: 0644]
src/utils/match/match.h [new file with mode: 0644]
src/utils/metadata/meta_data.c [new file with mode: 0644]
src/utils/metadata/meta_data.h [new file with mode: 0644]
src/utils/metadata/meta_data_test.c [new file with mode: 0644]
src/utils/mount/mount.c [new file with mode: 0644]
src/utils/mount/mount.h [new file with mode: 0644]
src/utils/mount/mount_test.c [new file with mode: 0644]
src/utils/oauth/oauth.c [new file with mode: 0644]
src/utils/oauth/oauth.h [new file with mode: 0644]
src/utils/oauth/oauth_test.c [new file with mode: 0644]
src/utils/ovs/ovs.c [new file with mode: 0644]
src/utils/ovs/ovs.h [new file with mode: 0644]
src/utils/rrdcreate/rrdcreate.c [new file with mode: 0644]
src/utils/rrdcreate/rrdcreate.h [new file with mode: 0644]
src/utils/tail/tail.c [new file with mode: 0644]
src/utils/tail/tail.h [new file with mode: 0644]
src/utils/taskstats/taskstats.c [new file with mode: 0644]
src/utils/taskstats/taskstats.h [new file with mode: 0644]
src/utils_cmd_flush.c [deleted file]
src/utils_cmd_flush.h [deleted file]
src/utils_cmd_getthreshold.c [deleted file]
src/utils_cmd_getthreshold.h [deleted file]
src/utils_cmd_getval.c [deleted file]
src/utils_cmd_getval.h [deleted file]
src/utils_cmd_listval.c [deleted file]
src/utils_cmd_listval.h [deleted file]
src/utils_cmd_putnotif.c [deleted file]
src/utils_cmd_putnotif.h [deleted file]
src/utils_cmd_putval.c [deleted file]
src/utils_cmd_putval.h [deleted file]
src/utils_cmds.c [deleted file]
src/utils_cmds.h [deleted file]
src/utils_cmds_test.c [deleted file]
src/utils_config_cores.c [deleted file]
src/utils_config_cores.h [deleted file]
src/utils_config_cores_test.c [deleted file]
src/utils_crc32.c [deleted file]
src/utils_crc32.h [deleted file]
src/utils_curl_stats.c [deleted file]
src/utils_curl_stats.h [deleted file]
src/utils_db_query.c [deleted file]
src/utils_db_query.h [deleted file]
src/utils_dns.c [deleted file]
src/utils_dns.h [deleted file]
src/utils_dpdk.c [deleted file]
src/utils_dpdk.h [deleted file]
src/utils_fbhash.c
src/utils_format_graphite.c [deleted file]
src/utils_format_graphite.h [deleted file]
src/utils_format_graphite_test.c [deleted file]
src/utils_format_json.c [deleted file]
src/utils_format_json.h [deleted file]
src/utils_format_json_test.c [deleted file]
src/utils_format_kairosdb.c [deleted file]
src/utils_format_kairosdb.h [deleted file]
src/utils_ignorelist.c [deleted file]
src/utils_ignorelist.h [deleted file]
src/utils_latency.c [deleted file]
src/utils_latency.h [deleted file]
src/utils_latency_config.c [deleted file]
src/utils_latency_config.h [deleted file]
src/utils_latency_test.c [deleted file]
src/utils_lua.c
src/utils_lua.h
src/utils_match.c [deleted file]
src/utils_match.h [deleted file]
src/utils_mount.c [deleted file]
src/utils_mount.h [deleted file]
src/utils_mount_test.c [deleted file]
src/utils_ovs.c [deleted file]
src/utils_ovs.h [deleted file]
src/utils_parse_option.c [deleted file]
src/utils_parse_option.h [deleted file]
src/utils_rrdcreate.c [deleted file]
src/utils_rrdcreate.h [deleted file]
src/utils_tail.c [deleted file]
src/utils_tail.h [deleted file]
src/utils_tail_match.c
src/utils_tail_match.h
src/utils_vl_lookup.c [deleted file]
src/utils_vl_lookup.h [deleted file]
src/utils_vl_lookup_test.c [deleted file]
src/uuid.c
src/valgrind.suppress [new file with mode: 0644]
src/varnish.c
src/virt.c
src/virt_test.c
src/vmem.c
src/vserver.c
src/wireless.c
src/write_graphite.c
src/write_http.c
src/write_kafka.c
src/write_log.c
src/write_mongodb.c
src/write_prometheus.c
src/write_redis.c
src/write_riemann.c
src/write_riemann_threshold.c
src/write_sensu.c
src/write_stackdriver.c [new file with mode: 0644]
src/write_tsdb.c
src/xencpu.c
src/xmms.c
src/zfs_arc.c
src/zone.c
src/zookeeper.c

diff --git a/.cirrus.yml b/.cirrus.yml
new file mode 100644 (file)
index 0000000..c6a38da
--- /dev/null
@@ -0,0 +1,251 @@
+env:
+  LANG: C
+  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 -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: true
+      skip_notifications: true
+      container:
+        image: collectd/ci:wheezy_amd64 # TODO: fix this platform
+    - 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 -sk
+  tests_script:
+    - make -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:fedora26_x86_64
+    - 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 -sk
+  tests_script:
+    - make -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
+          $DEFAULT_CONFIG_OPTS
+          CFLAGS="$(dpkg-buildflags --get CFLAGS)"
+          CPPLAGS="$(dpkg-buildflags --get CPPFLAGS)"
+          LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"
+      build_script:
+        - make -sk
+      tests_script:
+        - make -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 -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 against musl-libc using gcc wrapper, expected to fail
+    - env:
+        LABEL: musl libc
+      allow_failures: true
+      skip_notifications: true
+      configure_script:
+        - ./build.sh
+        - musl-gcc --version
+        - >
+          ./configure CC=musl-gcc
+          $DEFAULT_CONFIG_OPTS
+          CFLAGS="$(dpkg-buildflags --get CFLAGS)"
+          CPPLAGS="$(dpkg-buildflags --get CPPFLAGS)"
+          LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"
+      build_script:
+        - make -sk
+      tests_script:
+        - make -sk check
+
+    # 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
+          $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 -sk
+      tests_script:
+        - make -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 -sk
+  tests_script:
+    - make -sk check
index 49d24b3..05d2987 100644 (file)
@@ -1,5 +1,6 @@
 *   Version of collectd:
 *   Operating system / distribution:
+*   Kernel version (if applicable):
 
 ## Expected behavior
 
index 2911069..afdbe5c 100644 (file)
@@ -99,9 +99,8 @@ bindings/perl/pm_to_blib
 cscope.*
 
 # Unit tests
-src/daemon/test-suite.log
+test-suite.log
 src/tests/
-src/test-suite.log
 test_*
 
 # src/daemon/...
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..009ae43
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "gnulib"]
+       path = gnulib
+       url = git://git.savannah.gnu.org/gnulib.git
index fc250cb..4ba4e2a 100644 (file)
-sudo: required
-dist: trusty
-compiler:
-  - gcc
-  - clang
+# Travis CI configuration file
+# https://travis-ci.org/collectd/collectd
 language: c
+
+env:
+  global:
+    - 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
+
 before_install:
-  - sudo apt-get update -qq
-  - sudo apt-get install -qq --no-install-recommends
-      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
-      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
-      librrd-dev
-      libsensors4-dev
-      libsigrok-dev
-      libsnmp-dev
-      libstatgrab-dev
-      libtokyocabinet-dev
-      libtokyotyrant-dev
-      libudev-dev
-      libupsclient-dev
-      libvarnish-dev
-      libvirt-dev
-      libxml2-dev
-      libyajl-dev
-      linux-libc-dev
-      perl
-      protobuf-c-compiler
-      python-dev
-script: sh build.sh && ./configure && make distcheck
+  # 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
+
+before_script: autoreconf -vif
+
+script:
+  - if [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]]; then exit 0; fi
+  - ./configure
+  - 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"
+      description: "Build submitted via Travis CI"
+    notification_email: collectd-changes@verplant.org
+    build_command_prepend: "./configure; make clean"
+    build_command: "make -j $(nproc)"
+    branch_pattern: coverity_scan
+  homebrew:
+    packages:
+    - curl
+    - glib
+    - hiredis
+    - libdbi
+    - libmemcached
+    - libmicrohttpd
+    - libmodbus
+    - libnotify
+    - liboping
+    - libpcap
+    - librdkafka
+    - libstatgrab
+    - libvirt
+    - lua
+    - mosquitto
+    - mysql-client
+    - net-snmp
+    - openldap
+    - perl
+    - protobuf
+    - protobuf-c
+    - python
+    - qpid-proton
+    - rabbitmq-c
+    - riemann-client
+    - rrdtool
+    - tokyo-cabinet
+    - varnish
+    - yajl
+
+git:
+  quiet: true
+  submodules: false
+  depth: 1
diff --git a/AUTHORS b/AUTHORS
index 4df743c..409655a 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -59,6 +59,9 @@ Andreas Henriksson <andreas at fatal.se>
 Andy Parkins <andyp at fussylogic.co.uk>
  - battery plugin: sysfs code.
 
+Andy Smith <ansmith at redhat.com>
+ - AMQP 1.0 plugin.
+
 Anthony Dewhurst <dewhurst at gmail.com>
  - zfs_arc plugin.
 
@@ -286,7 +289,7 @@ Scott Sanders <scott at jssjr.com>
  - Write-Graphite plugin.
 
 Sebastien Pahl <sebastien.pahl at dotcloud.com>
- - AMQP plugin.
+ - AMQP 0.9 plugin.
 
 Serhiy Pshyk <serhiyx.pshyk at intel.com>
  - intel_pmu plugin
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644 (file)
index 0000000..55fbd04
--- /dev/null
@@ -0,0 +1,15 @@
+# Code ownership information.
+# See https://help.github.com/articles/about-code-owners/ for details.
+
+# These owners will be the default owners for everything in the repo. Unless a
+# later match takes precedence, # @trusted-contributors will be requested for
+# review when someone opens a pull request.
+*       @trusted-contributors
+
+/src/intel_pmu.c       @kwiatrox @sunkuranganath
+/src/intel_rdt.c       @kwiatrox @sunkuranganath
+/src/ipmi.c            @anaudx @rjablonx
+/src/mcelog.c          @kwiatrox @sunkuranganath
+/src/virt.c            @anaudx @rjablonx
+# TODO(#2926): Add the following owners:
+#/src/redfish.c                @kkepka @mkobyli
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644 (file)
index 11969de..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-# Contribution guidelines
-
-Thanks for taking the time to contribute to the [collectd
-project](https://collectd.org/)! This document tries to give some guidance to
-make the process of contributing to *collectd* as pleasant as possible.
-
-## Bug reports
-
-Please report bugs as [GitHub
-Issues](https://github.com/collectd/collectd/issues). Try to answer the
-following questions:
-
-*   Which version of *collectd* are you using?
-*   Which operating system (distribution) are you using at which version?
-*   What is the expected behavior / output?
-*   What is the actual (observed) behavior / output?
-*   How can we reproduce the problem you're having?
-*   If *collectd* crashes, try to get a
-    [stack trace](https://collectd.org/wiki/index.php/Core_file).
-
-Please monitor your issue for a couple of days and reply to questions. To keep
-the project manageable, we have to do some housekeeping; meaning we will close
-issues that have become stale.
-
-## Code contributions
-
-Please open a [GitHub Pull Request](https://github.com/collectd/collectd/pulls)
-(PR) to contribute bug fixes, features, cleanups, new plugins, … Patches sent to
-the mailing list have a tendency to fall through the cracks.
-
-*   *Focus:* Fix *one thing* in your PR. The smaller your change, the faster it
-    will be reviewed and merged.
-*   *Coding style:* Please run `clang-format -style=file -i $FILE` after editing
-    `.c`, `.h` and `.proto` files. If you don't want to install *clang-format*
-    locally or your version produces a different result than the formatting
-    check on Github, use `contrib/format.sh` to format files using the same web
-    service used by our check.
-*   *Documentation:* New config options need to be documented in two places: the
-    manpage (`src/collectd.conf.pod`) and the example config
-    (`src/collectd.conf.in`). New plugins need to be added to the `README` file.
-*   *Continuous integration:* Once your PR is created, our continuous
-    integration environment will try to build it on a number of platforms. If
-    this reports a failure, please investigate and fix the problem. We will at
-    best do a very casual review for failing PRs.
-*   *Don't rebase:* Rebasing your branch destroys the review history. If a review
-    takes a long time, we may ask you to rebase on a more recent *master*, but
-    please don't do it without being asked.
-*   *types.db:* One of the most common mistakes made by new contributors is the
-    addition of (many) new *types* in the file `src/types.db`. The majority of
-    usecases can be met with one of the existing entries. If you plan to add new
-    entries to `src/types.db`, you should talk to us early in the design
-    process.
-
-## Other resources
-
-*   [Mailing list](http://mailman.verplant.org/listinfo/collectd)
-*   [#collectd IRC channel](https://webchat.freenode.net/?channels=#collectd)
-    on *freenode*.
-*   [Old patch submission guideline](https://collectd.org/wiki/index.php/Submitting_patches)
index e1fa333..f4d4fa9 100644 (file)
@@ -1,6 +1,15 @@
 ACLOCAL_AMFLAGS = -I m4
 AM_YFLAGS = -d
 
+if BUILD_WIN32
+cpkgdatadir=$(datadir)
+cpkglibdir=$(libdir)/plugins
+cpkglocalstatedir=${localstatedir}
+else
+cpkgdatadir=$(pkgdatadir)
+cpkglibdir=$(pkglibdir)
+cpkglocalstatedir=${localstatedir}/lib/${PACKAGE_NAME}
+endif
 
 BUILT_SOURCES = \
        src/libcollectdclient/collectd/lcc_features.h \
@@ -61,6 +70,7 @@ EXTRA_DIST = \
        src/types.db \
        src/types.db.pod \
        src/valgrind.FreeBSD.suppress \
+       src/valgrind.suppress \
        testwrapper.sh \
        version-gen.sh
 
@@ -98,7 +108,13 @@ pkginclude_HEADERS = \
 
 lib_LTLIBRARIES = libcollectdclient.la
 
+if BUILD_WIN32
+# TODO: Build all executables on Windows as well.
+sbin_PROGRAMS = \
+        collectd
 
+bin_PROGRAMS =
+else
 sbin_PROGRAMS = \
        collectd \
        collectdmon
@@ -108,6 +124,7 @@ bin_PROGRAMS = \
        collectd-nagios \
        collectd-tg \
        collectdctl
+endif # BUILD_WIN32
 
 
 noinst_LTLIBRARIES = \
@@ -150,7 +167,7 @@ TESTS = $(check_PROGRAMS)
 LOG_COMPILER = env VALGRIND="@VALGRIND@" $(abs_srcdir)/testwrapper.sh
 
 
-jardir = $(pkgdatadir)/java
+jardir = $(cpkgdatadir)/java
 
 pkglib_LTLIBRARIES =
 
@@ -159,6 +176,9 @@ PLUGIN_LDFLAGS = \
        -module \
        -avoid-version \
        -export-symbols-regex '\<module_register\>'
+if BUILD_WIN32
+PLUGIN_LDFLAGS += -shared -no-undefined -lcollectd -L.
+endif
 
 
 AM_CPPFLAGS = \
@@ -166,13 +186,26 @@ AM_CPPFLAGS = \
        -DPREFIX='"${prefix}"' \
        -DCONFIGFILE='"${sysconfdir}/${PACKAGE_NAME}.conf"' \
        -DLOCALSTATEDIR='"${localstatedir}"' \
-       -DPKGLOCALSTATEDIR='"${localstatedir}/lib/${PACKAGE_NAME}"' \
-       -DPLUGINDIR='"${pkglibdir}"' \
-       -DPKGDATADIR='"${pkgdatadir}"'
+       -DPKGLOCALSTATEDIR='"${cpkglocalstatedir}"' \
+       -DPLUGINDIR='"${cpkglibdir}"' \
+       -DPKGDATADIR='"${cpkgdatadir}"'
+if BUILD_WIN32
+AM_CPPFLAGS += -DNOGDI
+endif
 
+COMMON_DEPS =
+if BUILD_WIN32
+COMMON_DEPS += collectd.exe
+endif
 
 # Link to these libraries..
 COMMON_LIBS = $(PTHREAD_LIBS)
+if BUILD_WIN32
+COMMON_LIBS += -lws2_32
+endif
+if BUILD_WITH_GNULIB
+COMMON_LIBS += -lgnu
+endif
 if BUILD_WITH_CAPABILITY
 COMMON_LIBS += -lcap
 endif
@@ -194,6 +227,7 @@ endif
 
 
 collectd_SOURCES = \
+       src/daemon/cmd.h \
        src/daemon/collectd.c \
        src/daemon/collectd.h \
        src/daemon/configfile.c \
@@ -202,8 +236,8 @@ collectd_SOURCES = \
        src/daemon/filter_chain.h \
        src/daemon/globals.c \
        src/daemon/globals.h \
-       src/daemon/meta_data.c \
-       src/daemon/meta_data.h \
+       src/utils/metadata/meta_data.c \
+       src/utils/metadata/meta_data.h \
        src/daemon/plugin.c \
        src/daemon/plugin.h \
        src/daemon/utils_cache.c \
@@ -236,6 +270,13 @@ collectd_LDADD = \
        $(COMMON_LIBS) \
        $(DLOPEN_LIBS)
 
+if BUILD_WIN32
+collectd_SOURCES += src/daemon/cmd_windows.c
+collectd_LDFLAGS += -ldl -Wl,--out-implib,libcollectd.a
+else
+collectd_SOURCES += src/daemon/cmd.c
+endif
+       
 if BUILD_FEATURE_DAEMON
 collectd_CPPFLAGS += -DPIDFILE='"${localstatedir}/run/${PACKAGE_NAME}.pid"'
 endif
@@ -247,6 +288,9 @@ collectd_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
 collectd_LDADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
 endif
 
+if BUILD_WIN32
+collectd_LDFLAGS += -Wl,--out-implib,libcollectd.a
+endif
 
 collectdmon_SOURCES = src/collectdmon.c
 
@@ -297,22 +341,22 @@ endif
 
 
 test_common_SOURCES = \
-       src/daemon/common_test.c \
+       src/utils/common/common_test.c \
        src/testing.h
 test_common_LDADD = libplugin_mock.la
 
 test_meta_data_SOURCES = \
-       src/daemon/meta_data_test.c \
+       src/utils/metadata/meta_data_test.c \
        src/testing.h
 test_meta_data_LDADD = libmetadata.la libplugin_mock.la
 
 test_utils_avltree_SOURCES = \
-       src/daemon/utils_avltree_test.c \
+       src/utils/avltree/avltree_test.c \
        src/testing.h
 test_utils_avltree_LDADD = libavltree.la $(COMMON_LIBS)
 
 test_utils_heap_SOURCES = \
-       src/daemon/utils_heap_test.c \
+       src/utils/heap/heap_test.c \
        src/testing.h
 test_utils_heap_LDADD = libheap.la $(COMMON_LIBS)
 
@@ -328,30 +372,30 @@ test_utils_subst_SOURCES = \
 test_utils_subst_LDADD = libplugin_mock.la
 
 test_utils_config_cores_SOURCES = \
-       src/utils_config_cores_test.c \
+       src/utils/config_cores/config_cores_test.c \
        src/testing.h
 test_utils_config_cores_LDADD = libplugin_mock.la
 
 libavltree_la_SOURCES = \
-       src/daemon/utils_avltree.c \
-       src/daemon/utils_avltree.h
+       src/utils/avltree/avltree.c \
+       src/utils/avltree/avltree.h
 
 libcommon_la_SOURCES = \
-       src/daemon/common.c \
-       src/daemon/common.h
+       src/utils/common/common.c \
+       src/utils/common/common.h
 libcommon_la_LIBADD = $(COMMON_LIBS)
 
 libheap_la_SOURCES = \
-       src/daemon/utils_heap.c \
-       src/daemon/utils_heap.h
+       src/utils/heap/heap.c \
+       src/utils/heap/heap.h
 
 libignorelist_la_SOURCES = \
-       src/utils_ignorelist.c \
-       src/utils_ignorelist.h
+       src/utils/ignorelist/ignorelist.c \
+       src/utils/ignorelist/ignorelist.h
 
 libmetadata_la_SOURCES = \
-       src/daemon/meta_data.c \
-       src/daemon/meta_data.h
+       src/utils/metadata/meta_data.c \
+       src/utils/metadata/meta_data.h
 
 libplugin_mock_la_SOURCES = \
        src/daemon/plugin_mock.c \
@@ -365,11 +409,11 @@ libplugin_mock_la_CPPFLAGS = $(AM_CPPFLAGS) -DMOCK_TIME
 libplugin_mock_la_LIBADD = libcommon.la libignorelist.la $(COMMON_LIBS)
 
 libformat_graphite_la_SOURCES = \
-       src/utils_format_graphite.c \
-       src/utils_format_graphite.h
+       src/utils/format_graphite/format_graphite.c \
+       src/utils/format_graphite/format_graphite.h
 
 test_format_graphite_SOURCES = \
-       src/utils_format_graphite_test.c \
+       src/utils/format_graphite/format_graphite_test.c \
        src/testing.h
 test_format_graphite_LDADD = \
        libformat_graphite.la \
@@ -378,8 +422,8 @@ test_format_graphite_LDADD = \
        -lm
 
 libformat_json_la_SOURCES = \
-       src/utils_format_json.c \
-       src/utils_format_json.h
+       src/utils/format_json/format_json.c \
+       src/utils/format_json/format_json.h
 libformat_json_la_CPPFLAGS  = $(AM_CPPFLAGS)
 libformat_json_la_LDFLAGS   = $(AM_LDFLAGS)
 libformat_json_la_LIBADD    =
@@ -391,7 +435,7 @@ libformat_json_la_LIBADD   += $(BUILD_WITH_LIBYAJL_LIBS)
 check_PROGRAMS += test_format_json
 
 test_format_json_SOURCES = \
-       src/utils_format_json_test.c \
+       src/utils/format_json/format_json_test.c \
        src/testing.h
 test_format_json_LDADD = \
        libformat_json.la \
@@ -409,16 +453,16 @@ check_PROGRAMS += test_plugin_ceph
 endif
 
 liblatency_la_SOURCES = \
-       src/utils_latency.c \
-       src/utils_latency.h \
-       src/utils_latency_config.c \
-       src/utils_latency_config.h
+       src/utils/latency/latency.c \
+       src/utils/latency/latency.h \
+       src/utils/latency/latency_config.c \
+       src/utils/latency/latency_config.h
 liblatency_la_LIBADD = \
        libcommon.la \
        -lm
 
 test_utils_latency_SOURCES = \
-       src/utils_latency_test.c \
+       src/utils/latency/latency_test.c \
        src/testing.h
 test_utils_latency_LDADD = \
        liblatency.la \
@@ -426,41 +470,41 @@ test_utils_latency_LDADD = \
        -lm
 
 libcmds_la_SOURCES = \
-       src/utils_cmds.c \
-       src/utils_cmds.h \
-       src/utils_cmd_flush.c \
-       src/utils_cmd_flush.h \
-       src/utils_cmd_getthreshold.c \
-       src/utils_cmd_getthreshold.h \
-       src/utils_cmd_getval.c \
-       src/utils_cmd_getval.h \
-       src/utils_cmd_listval.c \
-       src/utils_cmd_listval.h \
-       src/utils_cmd_putnotif.c \
-       src/utils_cmd_putnotif.h \
-       src/utils_cmd_putval.c \
-       src/utils_cmd_putval.h \
-       src/utils_parse_option.c \
-       src/utils_parse_option.h
+       src/utils/cmds/cmds.c \
+       src/utils/cmds/cmds.h \
+       src/utils/cmds/flush.c \
+       src/utils/cmds/flush.h \
+       src/utils/cmds/getthreshold.c \
+       src/utils/cmds/getthreshold.h \
+       src/utils/cmds/getval.c \
+       src/utils/cmds/getval.h \
+       src/utils/cmds/listval.c \
+       src/utils/cmds/listval.h \
+       src/utils/cmds/putnotif.c \
+       src/utils/cmds/putnotif.h \
+       src/utils/cmds/putval.c \
+       src/utils/cmds/putval.h \
+       src/utils/cmds/parse_option.c \
+       src/utils/cmds/parse_option.h
 libcmds_la_LIBADD = \
        libcommon.la \
        libmetadata.la \
        -lm
 
 test_utils_cmds_SOURCES = \
-       src/utils_cmds_test.c \
+       src/utils/cmds/cmds_test.c \
        src/testing.h
 test_utils_cmds_LDADD = \
        libcmds.la \
        libplugin_mock.la
 
 liblookup_la_SOURCES = \
-       src/utils_vl_lookup.c \
-       src/utils_vl_lookup.h
+       src/utils/lookup/vl_lookup.c \
+       src/utils/lookup/vl_lookup.h
 liblookup_la_LIBADD = libavltree.la
 
 test_utils_vl_lookup_SOURCES = \
-       src/utils_vl_lookup_test.c \
+       src/utils/lookup/vl_lookup_test.c \
        src/testing.h
 test_utils_vl_lookup_LDADD = \
        liblookup.la \
@@ -470,11 +514,11 @@ test_utils_vl_lookup_LDADD += -lkstat
 endif
 
 libmount_la_SOURCES = \
-       src/utils_mount.c \
-       src/utils_mount.h
+       src/utils/mount/mount.c \
+       src/utils/mount/mount.h
 
 test_utils_mount_SOURCES = \
-       src/utils_mount_test.c \
+       src/utils/mount/mount_test.c \
        src/testing.h
 test_utils_mount_LDADD = \
        libmount.la \
@@ -498,6 +542,10 @@ libcollectdclient_la_CPPFLAGS = \
        -I$(srcdir)/src/daemon
 libcollectdclient_la_LDFLAGS = -version-info 2:0:1
 libcollectdclient_la_LIBADD = -lm
+if BUILD_WIN32
+libcollectdclient_la_LDFLAGS += -shared -no-undefined
+libcollectdclient_la_LIBADD += -lgnu -lws2_32 -liphlpapi
+endif
 if BUILD_WITH_LIBGCRYPT
 libcollectdclient_la_CPPFLAGS += $(GCRYPT_CPPFLAGS)
 libcollectdclient_la_LDFLAGS += $(GCRYPT_LDFLAGS)
@@ -526,13 +574,75 @@ liboconfig_la_SOURCES = \
 liboconfig_la_CPPFLAGS = -I$(srcdir)/src/liboconfig $(AM_CPPFLAGS)
 liboconfig_la_LDFLAGS = -avoid-version $(LEXLIB)
 
+if BUILD_WITH_LIBCURL
+if BUILD_WITH_LIBSSL
+if BUILD_WITH_LIBYAJL2
+noinst_LTLIBRARIES += liboauth.la
+liboauth_la_SOURCES = \
+       src/utils/oauth/oauth.c \
+       src/utils/oauth/oauth.h
+liboauth_la_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       $(BUILD_WITH_LIBCURL_CFLAGS) \
+       $(BUILD_WITH_LIBSSL_CFLAGS) \
+       $(BUILD_WITH_LIBYAJL_CPPFLAGS)
+liboauth_la_LIBADD = \
+       $(BUILD_WITH_LIBCURL_LIBS) \
+       $(BUILD_WITH_LIBSSL_LIBS) \
+       $(BUILD_WITH_LIBYAJL_LIBS)
+
+check_PROGRAMS += test_utils_oauth
+TESTS += test_utils_oauth
+test_utils_oauth_SOURCES = \
+       src/utils/oauth/oauth_test.c
+test_utils_oauth_LDADD = \
+       liboauth.la \
+       libcommon.la \
+       libplugin_mock.la
+
+noinst_LTLIBRARIES += libgce.la
+libgce_la_SOURCES = \
+       src/utils/gce/gce.c \
+       src/utils/gce/gce.h
+libgce_la_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       $(BUILD_WITH_LIBCURL_CFLAGS)
+libgce_la_LIBADD = \
+       $(BUILD_WITH_LIBCURL_LIBS)
+endif
+endif
+endif
+
+if BUILD_WITH_LIBYAJL2
+noinst_LTLIBRARIES += libformat_stackdriver.la
+libformat_stackdriver_la_SOURCES = \
+       src/utils/format_stackdriver/format_stackdriver.c \
+       src/utils/format_stackdriver/format_stackdriver.h
+libformat_stackdriver_la_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       $(BUILD_WITH_LIBYAJL_CPPFLAGS)
+libformat_stackdriver_la_LIBADD = \
+       libavltree.la \
+       $(BUILD_WITH_LIBSSL_LIBS) \
+       $(BUILD_WITH_LIBYAJL_LIBS)
+
+check_PROGRAMS += test_format_stackdriver
+TESTS += test_format_stackdriver
+test_format_stackdriver_SOURCES = \
+       src/utils/format_stackdriver/format_stackdriver_test.c \
+       src/testing.h
+test_format_stackdriver_LDADD = \
+       libformat_stackdriver.la \
+       libplugin_mock.la \
+       -lm
+endif
 
 if BUILD_PLUGIN_AGGREGATION
 pkglib_LTLIBRARIES += aggregation.la
 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
@@ -549,6 +659,20 @@ amqp_la_LIBADD = \
        libformat_json.la
 endif
 
+if BUILD_PLUGIN_AMQP1
+pkglib_LTLIBRARIES += amqp1.la
+amqp1_la_SOURCES = \
+       src/amqp1.c \
+       src/utils/deq/deq.h
+amqp1_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBQPIDPROTON_CPPFLAGS)
+amqp1_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBQPIDPROTON_LDFLAGS)
+amqp1_la_LIBADD = \
+       $(BUILD_WITH_LIBQPIDPROTON_LIBS) \
+       libcmds.la \
+       libformat_graphite.la \
+       libformat_json.la
+endif
+
 if BUILD_PLUGIN_APACHE
 pkglib_LTLIBRARIES += apache.la
 apache_la_SOURCES = src/apache.c
@@ -599,7 +723,7 @@ if BUILD_PLUGIN_BAROMETER
 pkglib_LTLIBRARIES += barometer.la
 barometer_la_SOURCES = src/barometer.c
 barometer_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-barometer_la_LIBADD = -lm
+barometer_la_LIBADD = -lm $(BUILD_WITH_LIBI2C_LIBS)
 endif
 
 if BUILD_PLUGIN_BATTERY
@@ -703,10 +827,10 @@ if BUILD_PLUGIN_CURL
 pkglib_LTLIBRARIES += curl.la
 curl_la_SOURCES = \
        src/curl.c \
-       src/utils_curl_stats.c \
-       src/utils_curl_stats.h \
-       src/utils_match.c \
-       src/utils_match.h
+       src/utils/curl_stats/curl_stats.c \
+       src/utils/curl_stats/curl_stats.h \
+       src/utils/match/match.c \
+       src/utils/match/match.h
 curl_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
 curl_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 curl_la_LIBADD = liblatency.la $(BUILD_WITH_LIBCURL_LIBS)
@@ -716,15 +840,15 @@ if BUILD_PLUGIN_CURL_JSON
 pkglib_LTLIBRARIES += curl_json.la
 curl_json_la_SOURCES = \
        src/curl_json.c \
-       src/utils_curl_stats.c \
-       src/utils_curl_stats.h
+       src/utils/curl_stats/curl_stats.c \
+       src/utils/curl_stats/curl_stats.h
 curl_json_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
 curl_json_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
 curl_json_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
 curl_json_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBYAJL_LIBS)
 
 test_plugin_curl_json_SOURCES = src/curl_json_test.c \
-                               src/utils_curl_stats.c \
+                               src/utils/curl_stats/curl_stats.c \
                                src/daemon/configfile.c \
                                src/daemon/types_list.c
 test_plugin_curl_json_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
@@ -737,8 +861,8 @@ if BUILD_PLUGIN_CURL_XML
 pkglib_LTLIBRARIES += curl_xml.la
 curl_xml_la_SOURCES = \
        src/curl_xml.c \
-       src/utils_curl_stats.c \
-       src/utils_curl_stats.h
+       src/utils/curl_stats/curl_stats.c \
+       src/utils/curl_stats/curl_stats.h
 curl_xml_la_CFLAGS = $(AM_CFLAGS) \
                $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
 curl_xml_la_LDFLAGS = $(PLUGIN_LDFLAGS)
@@ -749,8 +873,8 @@ if BUILD_PLUGIN_DBI
 pkglib_LTLIBRARIES += dbi.la
 dbi_la_SOURCES = \
        src/dbi.c \
-       src/utils_db_query.c \
-       src/utils_db_query.h
+       src/utils/db_query/db_query.c \
+       src/utils/db_query/db_query.h
 dbi_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBDBI_CPPFLAGS)
 dbi_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBDBI_LDFLAGS)
 dbi_la_LIBADD = $(BUILD_WITH_LIBDBI_LIBS)
@@ -800,8 +924,8 @@ if BUILD_PLUGIN_DNS
 pkglib_LTLIBRARIES += dns.la
 dns_la_SOURCES = \
        src/dns.c \
-       src/utils_dns.c \
-       src/utils_dns.h
+       src/utils/dns/dns.c \
+       src/utils/dns/dns.h
 dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPCAP_CPPFLAGS)
 dns_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPCAP_LDFLAGS)
 dns_la_LIBADD = $(BUILD_WITH_LIBPCAP_LIBS)
@@ -809,7 +933,7 @@ endif
 
 if BUILD_PLUGIN_DPDKEVENTS
 pkglib_LTLIBRARIES += dpdkevents.la
-dpdkevents_la_SOURCES = src/dpdkevents.c src/utils_dpdk.c src/utils_dpdk.h
+dpdkevents_la_SOURCES = src/dpdkevents.c src/utils/dpdk/dpdk.c src/utils/dpdk/dpdk.h
 dpdkevents_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS)
 dpdkevents_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS)
 dpdkevents_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS)
@@ -818,7 +942,7 @@ endif
 
 if BUILD_PLUGIN_DPDKSTAT
 pkglib_LTLIBRARIES += dpdkstat.la
-dpdkstat_la_SOURCES = src/dpdkstat.c src/utils_dpdk.c src/utils_dpdk.h
+dpdkstat_la_SOURCES = src/dpdkstat.c src/utils/dpdk/dpdk.c src/utils/dpdk/dpdk.h
 dpdkstat_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS)
 dpdkstat_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS)
 dpdkstat_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS)
@@ -890,6 +1014,14 @@ gps_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBGPS_LDFLAGS)
 gps_la_LIBADD = -lpthread $(BUILD_WITH_LIBGPS_LIBS)
 endif
 
+if BUILD_PLUGIN_GPU_NVIDIA
+pkglib_LTLIBRARIES += gpu_nvidia.la
+gpu_nvidia_la_SOURCES = src/gpu_nvidia.c
+gpu_nvidia_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) $(BUILD_WITH_GPU_CUDA_CPPFLAGS)
+gpu_nvidia_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_GPU_CUDA_LDFLAGS)
+gpu_nvidia_la_LIBADD = $(BUILD_WITH_CUDA_LIBS)
+endif
+
 if BUILD_PLUGIN_GRPC
 pkglib_LTLIBRARIES += grpc.la
 grpc_la_SOURCES = src/grpc.cc
@@ -922,8 +1054,8 @@ if BUILD_PLUGIN_INTEL_PMU
 pkglib_LTLIBRARIES += intel_pmu.la
 intel_pmu_la_SOURCES = \
        src/intel_pmu.c \
-       src/utils_config_cores.h \
-       src/utils_config_cores.c
+       src/utils/config_cores/config_cores.h \
+       src/utils/config_cores/config_cores.c
 intel_pmu_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBJEVENTS_CPPFLAGS)
 intel_pmu_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBJEVENTS_LDFLAGS)
 intel_pmu_la_LIBADD = $(BUILD_WITH_LIBJEVENTS_LIBS)
@@ -933,8 +1065,8 @@ if BUILD_PLUGIN_INTEL_RDT
 pkglib_LTLIBRARIES += intel_rdt.la
 intel_rdt_la_SOURCES = \
        src/intel_rdt.c \
-       src/utils_config_cores.h \
-       src/utils_config_cores.c
+       src/utils/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)
@@ -1025,6 +1157,7 @@ if BUILD_PLUGIN_LOGFILE
 pkglib_LTLIBRARIES += logfile.la
 logfile_la_SOURCES = src/logfile.c
 logfile_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+logfile_la_DEPENDENCIES = $(COMMON_DEPS)
 endif
 
 if BUILD_PLUGIN_LOG_LOGSTASH
@@ -1127,8 +1260,8 @@ if BUILD_PLUGIN_MEMCACHEC
 pkglib_LTLIBRARIES += memcachec.la
 memcachec_la_SOURCES = \
        src/memcachec.c \
-       src/utils_match.c \
-       src/utils_match.h
+       src/utils/match/match.c \
+       src/utils/match/match.h
 memcachec_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMEMCACHED_CPPFLAGS)
 memcachec_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMEMCACHED_LDFLAGS)
 memcachec_la_LIBADD = liblatency.la $(BUILD_WITH_LIBMEMCACHED_LIBS)
@@ -1335,8 +1468,8 @@ if BUILD_PLUGIN_ORACLE
 pkglib_LTLIBRARIES += oracle.la
 oracle_la_SOURCES = \
        src/oracle.c \
-       src/utils_db_query.c \
-       src/utils_db_query.h
+       src/utils/db_query/db_query.c \
+       src/utils/db_query/db_query.h
 oracle_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_ORACLE_CPPFLAGS)
 oracle_la_LIBADD = $(BUILD_WITH_ORACLE_LIBS)
 oracle_la_LDFLAGS = $(PLUGIN_LDFLAGS)
@@ -1346,8 +1479,8 @@ if BUILD_PLUGIN_OVS_EVENTS
 pkglib_LTLIBRARIES += ovs_events.la
 ovs_events_la_SOURCES = \
        src/ovs_events.c \
-       src/utils_ovs.c \
-       src/utils_ovs.h
+       src/utils/ovs/ovs.c \
+       src/utils/ovs/ovs.h
 ovs_events_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
 ovs_events_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
 ovs_events_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
@@ -1357,13 +1490,31 @@ if BUILD_PLUGIN_OVS_STATS
 pkglib_LTLIBRARIES += ovs_stats.la
 ovs_stats_la_SOURCES = \
        src/ovs_stats.c \
-       src/utils_ovs.c \
-       src/utils_ovs.h
+       src/utils/ovs/ovs.c \
+       src/utils/ovs/ovs.h
 ovs_stats_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
 ovs_stats_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
 ovs_stats_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
 endif
 
+if BUILD_PLUGIN_PCIE_ERRORS
+pkglib_LTLIBRARIES += pcie_errors.la
+pcie_errors_la_SOURCES = src/pcie_errors.c
+pcie_errors_la_CPPFLAGS = $(AM_CPPFLAGS)
+pcie_errors_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+
+test_plugin_pcie_errors_SOURCES = \
+       src/pcie_errors_test.c \
+       src/daemon/utils_llist.c \
+       src/daemon/configfile.c \
+       src/daemon/types_list.c
+test_plugin_pcie_errors_CPPFLAGS = $(AM_CPPFLAGS)
+test_plugin_pcie_errors_LDFLAGS = $(PLUGIN_LDFLAGS)
+test_plugin_pcie_errors_LDADD = liboconfig.la libplugin_mock.la
+check_PROGRAMS += test_plugin_pcie_errors
+TESTS += test_plugin_pcie_errors
+endif
+
 if BUILD_PLUGIN_PERL
 pkglib_LTLIBRARIES += perl.la
 perl_la_SOURCES = src/perl.c
@@ -1410,8 +1561,8 @@ if BUILD_PLUGIN_POSTGRESQL
 pkglib_LTLIBRARIES += postgresql.la
 postgresql_la_SOURCES = \
        src/postgresql.c \
-       src/utils_db_query.c \
-       src/utils_db_query.h
+       src/utils/db_query/db_query.c \
+       src/utils/db_query/db_query.h
 postgresql_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPQ_CPPFLAGS)
 postgresql_la_LDFLAGS = $(PLUGIN_LDFLAGS) \
        $(BUILD_WITH_LIBPQ_LDFLAGS)
@@ -1435,14 +1586,28 @@ python_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBPYTHON_CPPFLAGS)
 python_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBPYTHON_LDFLAGS)
 endif
 
+if HAVE_LIBMNL
+noinst_LTLIBRARIES += libtaskstats.la
+libtaskstats_la_SOURCES = \
+       src/utils/taskstats/taskstats.c \
+       src/utils/taskstats/taskstats.h
+libtaskstats_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
+libtaskstats_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS)
+endif
+
 if BUILD_PLUGIN_PROCESSES
 pkglib_LTLIBRARIES += processes.la
 processes_la_SOURCES = src/processes.c
+processes_la_CPPFLAGS = $(AM_CPPFLAGS)
 processes_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 processes_la_LIBADD =
 if BUILD_WITH_LIBKVM_GETPROCS
 processes_la_LIBADD += -lkvm
 endif
+if HAVE_LIBMNL
+processes_la_CPPFLAGS += -DHAVE_LIBTASKSTATS=1
+processes_la_LIBADD += libtaskstats.la
+endif
 endif
 
 if BUILD_PLUGIN_PROTOCOLS
@@ -1472,8 +1637,8 @@ if BUILD_PLUGIN_RRDCACHED
 pkglib_LTLIBRARIES += rrdcached.la
 rrdcached_la_SOURCES = \
        src/rrdcached.c \
-       src/utils_rrdcreate.c \
-       src/utils_rrdcreate.h
+       src/utils/rrdcreate/rrdcreate.c \
+       src/utils/rrdcreate/rrdcreate.h
 rrdcached_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS)
 rrdcached_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRRD_LDFLAGS)
 rrdcached_la_LIBADD = $(BUILD_WITH_LIBRRD_LIBS)
@@ -1483,8 +1648,8 @@ if BUILD_PLUGIN_RRDTOOL
 pkglib_LTLIBRARIES += rrdtool.la
 rrdtool_la_SOURCES = \
        src/rrdtool.c \
-       src/utils_rrdcreate.c \
-       src/utils_rrdcreate.h
+       src/utils/rrdcreate/rrdcreate.c \
+       src/utils/rrdcreate/rrdcreate.h
 rrdtool_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBRRD_CFLAGS)
 rrdtool_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRRD_LDFLAGS)
 rrdtool_la_LIBADD = $(BUILD_WITH_LIBRRD_LIBS)
@@ -1527,7 +1692,7 @@ pkglib_LTLIBRARIES += snmp.la
 snmp_la_SOURCES = src/snmp.c
 snmp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBNETSNMP_CPPFLAGS)
 snmp_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBNETSNMP_LDFLAGS)
-snmp_la_LIBADD = $(BUILD_WITH_LIBNETSNMP_LIBS)
+snmp_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBNETSNMP_LIBS)
 endif
 
 if BUILD_PLUGIN_SNMP_AGENT
@@ -1536,6 +1701,23 @@ snmp_agent_la_SOURCES = src/snmp_agent.c
 snmp_agent_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBNETSNMPAGENT_CPPFLAGS)
 snmp_agent_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBNETSNMPAGENT_LDFLAGS)
 snmp_agent_la_LIBADD = $(BUILD_WITH_LIBNETSNMPAGENT_LIBS)
+
+test_plugin_snmp_agent_SOURCES = src/snmp_agent_test.c \
+                                 src/utils/avltree/avltree.c \
+                                 src/daemon/utils_llist.c \
+                                 src/daemon/configfile.c \
+                                 src/daemon/types_list.c
+test_plugin_snmp_agent_CPPFLAGS = $(AM_CPPFLAGS) \
+       $(BUILD_WITH_LIBNETSNMPAGENT_CPPFLAGS)
+test_plugin_snmp_agent_LDFLAGS = $(PLUGIN_LDFLAGS) \
+       $(BUILD_WITH_LIBNETSNMPAGENT_LDFLAGS)
+test_plugin_snmp_agent_LDADD = liboconfig.la libplugin_mock.la \
+       $(BUILD_WITH_LIBNETSNMPAGENT_LIBS) $(BUILD_WITH_LIBNETSNMP_LIBS)
+
+check_PROGRAMS += test_plugin_snmp_agent
+TESTS += test_plugin_snmp_agent
+
+
 endif
 
 if BUILD_PLUGIN_STATSD
@@ -1592,10 +1774,10 @@ if BUILD_PLUGIN_TAIL
 pkglib_LTLIBRARIES += tail.la
 tail_la_SOURCES = \
        src/tail.c \
-       src/utils_match.c \
-       src/utils_match.h \
-       src/utils_tail.c \
-       src/utils_tail.h \
+       src/utils/match/match.c \
+       src/utils/match/match.h \
+       src/utils/tail/tail.c \
+       src/utils/tail/tail.h \
        src/utils_tail_match.c \
        src/utils_tail_match.h
 tail_la_LDFLAGS = $(PLUGIN_LDFLAGS)
@@ -1606,8 +1788,8 @@ if BUILD_PLUGIN_TAIL_CSV
 pkglib_LTLIBRARIES += tail_csv.la
 tail_csv_la_SOURCES = \
        src/tail_csv.c \
-       src/utils_tail.c \
-       src/utils_tail.h
+       src/utils/tail/tail.c \
+       src/utils/tail/tail.h
 tail_csv_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
@@ -1757,18 +1939,15 @@ virt_la_CFLAGS = $(AM_CFLAGS) \
 virt_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 virt_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
 
-# TODO: enable once we support only modern libvirts which depends on libnl-3
-# the libvirt on wheezy is linked in libnl v1, and there is a small leak here,
-# triggered by the library initialization. There are no means to avoid it,
-# and libvirt switched to libnl3 anyway
-#test_plugin_virt_SOURCES = src/virt_test.c
-#test_plugin_virt_CPPFLAGS = $(AM_CPPFLAGS) \
-#      $(BUILD_WITH_LIBVIRT_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
-#test_plugin_virt_LDFLAGS = $(PLUGIN_LDFLAGS)
-#test_plugin_virt_LDADD = libplugin_mock.la \
-#      $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
-#check_PROGRAMS += test_plugin_virt
-#TESTS += test_plugin_virt
+test_plugin_virt_SOURCES = src/virt_test.c
+test_plugin_virt_CPPFLAGS = $(AM_CPPFLAGS) \
+       $(BUILD_WITH_LIBVIRT_CPPFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
+test_plugin_virt_LDFLAGS = $(PLUGIN_LDFLAGS) \
+       $(BUILD_WITH_LIBVIRT_LDFLAGS) $(BUILD_WITH_LIBXML2_LDFLAGS)
+test_plugin_virt_LDADD = libplugin_mock.la \
+       $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
+check_PROGRAMS += test_plugin_virt
+TESTS += test_plugin_virt
 endif
 
 if BUILD_PLUGIN_VMEM
@@ -1800,8 +1979,8 @@ if BUILD_PLUGIN_WRITE_HTTP
 pkglib_LTLIBRARIES += write_http.la
 write_http_la_SOURCES = \
        src/write_http.c \
-       src/utils_format_kairosdb.c \
-       src/utils_format_kairosdb.h
+       src/utils/format_kairosdb/format_kairosdb.c \
+       src/utils/format_kairosdb/format_kairosdb.h
 write_http_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
 write_http_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 write_http_la_LIBADD = libformat_json.la $(BUILD_WITH_LIBCURL_LIBS)
@@ -1869,6 +2048,15 @@ write_sensu_la_SOURCES = src/write_sensu.c
 write_sensu_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
+if BUILD_PLUGIN_WRITE_STACKDRIVER
+pkglib_LTLIBRARIES += write_stackdriver.la
+write_stackdriver_la_SOURCES = src/write_stackdriver.c
+write_stackdriver_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+write_stackdriver_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
+write_stackdriver_la_LIBADD = libformat_stackdriver.la libgce.la liboauth.la \
+                     $(BUILD_WITH_LIBCURL_LIBS)
+endif
+
 if BUILD_PLUGIN_WRITE_TSDB
 pkglib_LTLIBRARIES += write_tsdb.la
 write_tsdb_la_SOURCES = src/write_tsdb.c
@@ -1921,20 +2109,10 @@ am__v_POD2MAN_C_0 = @echo "  POD2MAN " $@;
 am__v_POD2MAN_C_1 =
 
 .pod.1:
-       $(AM_V_POD2MAN_C)pod2man --release=$(VERSION) --center=$(PACKAGE) $< \
-               >.pod2man.tmp.$$$$ 2>/dev/null && mv -f .pod2man.tmp.$$$$ $@ || true
-       @if grep '\<POD ERRORS\>' $@ >/dev/null 2>&1; \
-       then \
-               echo "$@ has some POD errors!"; false; \
-       fi
+       $(AM_V_POD2MAN_C)pod2man --errors=die --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 --errors=die --section=5 --release=$(VERSION) --center=$(PACKAGE) $< $@
 
 V_PROTOC = $(v_protoc_@AM_V@)
 v_protoc_ = $(v_protoc_@AM_DEFAULT_V@)
@@ -1984,19 +2162,19 @@ install-exec-hook:
        $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
        if test -e $(DESTDIR)$(sysconfdir)/collectd.conf; \
        then \
-               $(INSTALL) -m 0640 $(srcdir)/src/collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf.pkg-orig; \
+               $(INSTALL) -m 0640 $(builddir)/src/collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf.pkg-orig; \
        else \
-               $(INSTALL) -m 0640 $(srcdir)/src/collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \
+               $(INSTALL) -m 0640 $(builddir)/src/collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \
        fi; \
-       $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)
-       $(INSTALL) -m 0644 $(srcdir)/src/types.db $(DESTDIR)$(pkgdatadir)/types.db;
+       $(mkinstalldirs) $(DESTDIR)$(cpkgdatadir)
+       $(INSTALL) -m 0644 $(srcdir)/src/types.db $(DESTDIR)$(cpkgdatadir)/types.db;
        $(INSTALL) -m 0644 $(srcdir)/src/postgresql_default.conf \
-               $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
+               $(DESTDIR)$(cpkgdatadir)/postgresql_default.conf;
 
 uninstall-hook:
-       rm -f $(DESTDIR)$(pkgdatadir)/types.db;
+       rm -f $(DESTDIR)$(cpkgdatadir)/types.db;
        rm -f $(DESTDIR)$(sysconfdir)/collectd.conf
-       rm -f $(DESTDIR)$(pkgdatadir)/postgresql_default.conf;
+       rm -f $(DESTDIR)$(cpkgdatadir)/postgresql_default.conf;
 
 all-local: @PERL_BINDINGS@
 
@@ -2076,3 +2254,4 @@ generic-jmx.jar: $(JAVA_TIMESTAMP_FILE)
 
 jar_DATA = collectd-api.jar generic-jmx.jar
 endif
+
diff --git a/README b/README
index ca86c84..8776054 100644 (file)
--- a/README
+++ b/README
@@ -100,6 +100,9 @@ Features
       Collect DPDK interface statistics.
       See docs/BUILD.dpdkstat.md for detailed build instructions.
 
+      This plugin should be compiled with compiler defenses enabled, for
+      example -fstack-protector.
+
     - drbd
       Collect individual drbd resource statistics.
 
@@ -132,6 +135,9 @@ Features
     - gps
       Monitor gps related data through gpsd.
 
+    - gpu_nvidia
+      Monitor NVIDIA GPU statistics available through NVML.
+
     - hddtemp
       Hard disk temperatures using hddtempd.
 
@@ -140,6 +146,9 @@ Features
       hugepages can be found here:
       https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt.
 
+      This plugin should be compiled with compiler defenses enabled, for
+      example -fstack-protector.
+
     - intel_pmu
       The intel_pmu plugin reads performance counters provided by the Linux
       kernel perf interface. The plugin uses jevents library to resolve named
@@ -308,6 +317,10 @@ Features
       OVS documentation.
       <http://openvswitch.org/support/dist-docs/INSTALL.rst.html>
 
+    - pcie_errors
+      Read errors from PCI Express Device Status and AER extended capabilities.
+      <https://www.design-reuse.com/articles/38374/pcie-error-logging-and-handling-on-a-typical-soc.html>
+
     - perl
       The perl plugin implements a Perl-interpreter into collectd. You can
       write your own plugins in Perl and return arbitrary values using this
@@ -459,7 +472,11 @@ Features
 
     - amqp
       Sends JSON-encoded data to an Advanced Message Queuing Protocol (AMQP)
-      server, such as RabbitMQ.
+      0.9.1 server, such as RabbitMQ.
+
+    - amqp1
+      Sends JSON-encoded data to an Advanced Message Queuing Protocol (AMQP)
+      1.0 server, such as Qpid Dispatch Router or Apache Artemis Broker.
 
     - csv
       Write to comma separated values (CSV) files. This needs lots of
@@ -735,6 +752,10 @@ Prerequisites
     particular.
     <http://developer.apple.com/corefoundation/>
 
+  * CUDA (optional)
+    Used by the `gpu_nvidia' plugin
+    <https://developer.nvidia.com/cuda-downloads>
+
   * libatasmart (optional)
     Used by the `smart' plugin.
     <http://git.0pointer.de/?p=libatasmart.git>
@@ -902,8 +923,14 @@ Prerequisites
     are supported.
     <http://www.python.org/>
 
+  * libqpid-proton (optional)
+    Used by the `amqp1' plugin for AMQP 1.0 connections, for example to
+    Qdrouterd.
+    <http://qpid.apache.org/>
+
   * librabbitmq (optional; also called “rabbitmq-c”)
-    Used by the `amqp' plugin for AMQP connections, for example to RabbitMQ.
+    Used by the `amqp' plugin for AMQP 0.9.1 connections, for example to
+    RabbitMQ.
     <http://hg.rabbitmq.com/rabbitmq-c/>
 
   * librdkafka (optional; also called “rdkafka”)
@@ -981,9 +1008,8 @@ Configuring / Compiling / Installing
 ------------------------------------
 
   To configure, build and install collectd with the default settings, run
-  `./configure && make && make install'.  For detailed, generic instructions
-  see INSTALL. For a complete list of configure options and their description,
-  run `./configure --help'.
+  `./configure && make && make install'.  For a complete list of configure
+  options and their description, run `./configure --help'.
 
   By default, the configure script will check for all build dependencies and
   disable all plugins whose requirements cannot be fulfilled (any other plugin
@@ -1025,6 +1051,37 @@ To generate the `configure` script, you'll need the following dependencies:
 The `build.sh' script takes no arguments.
 
 
+Building on Windows
+-----------------------------------------------
+
+Collectd can be built on Windows using Cygwin, and the result is a binary that
+runs natively on Windows. That is, Cygwin is only needed for building, not running,
+collectd.
+
+You will need to install the following Cygwin packages:
+- automake
+- bison
+- flex
+- git
+- libtool
+- make
+- mingw64-x86_64-dlfcn
+- mingw64-x86_64-gcc-core
+- mingw64-x86_64-zlib
+- pkg-config
+
+To build, just run the `build.sh' script in your Cygwin terminal. By default, it installs
+to "C:/Program Files/collectd". You can change the location by setting the INSTALL_DIR
+variable:
+
+$ export INSTALL_DIR="C:/some/other/install/directory"
+$ ./build.sh
+
+or:
+
+$ INSTALL_DIR="C:/some/other/install/directory" ./build.sh
+
+
 Crosscompiling
 --------------
 
index 40f5361..c0ccce3 100755 (executable)
--- a/build.sh
+++ b/build.sh
-#! /bin/sh
+#!/bin/sh
 
 GLOBAL_ERROR_INDICATOR=0
 
-check_for_application ()
+check_for_application()
 {
-       for PROG in "$@"
-       do
-               which "$PROG" >/dev/null 2>&1
-               if test $? -ne 0; then
-                       cat >&2 <<EOF
+    for PROG in "$@"
+    do
+        which "$PROG" >/dev/null 2>&1
+        if test $? -ne 0; then
+            cat >&2 <<EOF
 WARNING: \`$PROG' not found!
     Please make sure that \`$PROG' is installed and is in one of the
     directories listed in the PATH environment variable.
 EOF
-                       GLOBAL_ERROR_INDICATOR=1
-               fi
-       done
+            GLOBAL_ERROR_INDICATOR=1
+        fi
+    done
 }
 
-check_for_application lex bison autoheader aclocal automake autoconf pkg-config
-
-libtoolize=""
-libtoolize --version >/dev/null 2>/dev/null
-if test $? -eq 0
-then
-       libtoolize=libtoolize
-else
-       glibtoolize --version >/dev/null 2>/dev/null
-       if test $? -eq 0
-       then
-               libtoolize=glibtoolize
-       else
-               cat >&2 <<EOF
+setup_libtool()
+{
+    libtoolize=""
+    libtoolize --version >/dev/null 2>/dev/null
+    if test $? -eq 0; then
+        libtoolize=libtoolize
+    else
+        glibtoolize --version >/dev/null 2>/dev/null
+        if test $? -eq 0; then
+            libtoolize=glibtoolize
+        else
+            cat >&2 <<EOF
 WARNING: Neither \`libtoolize' nor \`glibtoolize' have been found!
     Please make sure that one of them is installed and is in one of the
     directories listed in the PATH environment variable.
 EOF
-               GLOBAL_ERROR_INDICATOR=1
+            GLOBAL_ERROR_INDICATOR=1
+        fi
+    fi
+
+    if test "$GLOBAL_ERROR_INDICATOR" != "0"; then
+        exit 1
+    fi
+}
+
+build()
+{
+    echo "Building..."
+    check_for_application lex bison autoheader aclocal automake autoconf pkg-config
+    setup_libtool
+
+    set -x
+    autoheader \
+    && aclocal -I m4 \
+    && $libtoolize --copy --force \
+    && automake --add-missing --copy \
+    && autoconf
+}
+
+build_cygwin()
+{
+    echo "Building for Cygwin..."
+    check_for_application aclocal autoconf autoheader automake bison flex git make pkg-config x86_64-w64-mingw32-gcc
+    setup_libtool
+
+    set -e
+
+    : ${INSTALL_DIR:="C:/PROGRA~1/collectd"}
+    : ${LIBDIR:="${INSTALL_DIR}"}
+    : ${BINDIR:="${INSTALL_DIR}"}
+    : ${SBINDIR:="${INSTALL_DIR}"}
+    : ${SYSCONFDIR:="${INSTALL_DIR}"}
+    : ${LOCALSTATEDIR:="${INSTALL_DIR}"}
+    : ${DATAROOTDIR:="${INSTALL_DIR}"}
+    : ${DATADIR:="${INSTALL_DIR}"}
+
+    echo "Installing collectd to ${INSTALL_DIR}."
+    TOP_SRCDIR="$(pwd)"
+    MINGW_ROOT="$(x86_64-w64-mingw32-gcc -print-sysroot)/mingw"
+    export GNULIB_DIR="${TOP_SRCDIR}/gnulib/build/gllib"
+
+    export CC="x86_64-w64-mingw32-gcc"
+
+    if [ -d "${TOP_SRCDIR}/gnulib/build" ]; then
+        echo "Assuming that gnulib is already built, because gnulib/build exists."
+    else
+        git submodule init
+        git submodule update
+        cd gnulib
+        ./gnulib-tool \
+          --create-testdir \
+          --source-base=lib \
+          --dir=${TOP_SRCDIR}/gnulib/build \
+          canonicalize-lgpl \
+          fcntl-h \
+          fnmatch \
+          getsockopt \
+          gettimeofday \
+          nanosleep \
+          netdb \
+          net_if \
+          poll \
+          recv \
+          regex \
+          sendto \
+          setlocale \
+          strtok_r \
+          sys_resource \
+          sys_socket \
+          sys_stat \
+          sys_wait \
+          time_r
+
+        cd ${TOP_SRCDIR}/gnulib/build
+        ./configure --host="mingw32" LIBS="-lws2_32 -lpthread"
+        make 
+        cd gllib
+
+        # We have to rebuild libgnu.a to get the list of *.o files to build a dll later
+        rm libgnu.a
+        OBJECT_LIST=`make V=1 | grep "ar" | cut -d' ' -f4-`
+        $CC -shared -o libgnu.dll $OBJECT_LIST -lws2_32 -lpthread
+        rm libgnu.a # get rid of it, to use libgnu.dll
        fi
- fi
+    cd "${TOP_SRCDIR}"
 
-if test "$GLOBAL_ERROR_INDICATOR" != "0"
-then
-       exit 1
-fi
+    set -x
+    autoreconf --install
+
+    export LDFLAGS="-L${GNULIB_DIR}"
+    export LIBS="-lgnu"
+    export CFLAGS="-Drestrict=__restrict -I${GNULIB_DIR}"
+
+    ./configure \
+      --prefix="${INSTALL_DIR}" \
+      --libdir="${LIBDIR}" \
+      --bindir="${BINDIR}" \
+      --sbindir="${SBINDIR}" \
+      --sysconfdir="${SYSCONFDIR}" \
+      --localstatedir="${LOCALSTATEDIR}" \
+      --datarootdir="${DATAROOTDIR}" \
+      --datarootdir="${DATADIR}" \
+      --disable-all-plugins \
+      --host="mingw32" \
+      --enable-logfile \
+      --enable-match_regex \
+      --enable-target_replace \
+      --enable-target_set
+
+    cp ${GNULIB_DIR}/../config.h src/gnulib_config.h
+    echo "#include <config.h.in>" >> src/gnulib_config.h
 
-set -x
+    cp libtool libtool_bak
+    sed -i "s%\$LTCC \$LTCFLAGS\(.*cwrapper.*\)%\$LTCC \1%" libtool
+
+    make
+    make install
+
+    cp "${GNULIB_DIR}/libgnu.dll" "${INSTALL_DIR}"
+    cp "${MINGW_ROOT}/bin/zlib1.dll" "${INSTALL_DIR}"
+    cp "${MINGW_ROOT}/bin/libwinpthread-1.dll" "${INSTALL_DIR}"
+    cp "${MINGW_ROOT}/bin/libdl.dll" "${INSTALL_DIR}"
+
+    echo "Done."
+}
+
+os_name="$(uname)"
+if test "${os_name#CYGWIN}" != "$os_name"; then
+    build_cygwin
+else
+    build
+fi
 
-autoheader \
-&& aclocal \
-&& $libtoolize --copy --force \
-&& automake --add-missing --copy \
-&& autoconf
index 0d1b49d..06381fc 100644 (file)
@@ -99,6 +99,10 @@ case $host_os in
     AC_DEFINE([KERNEL_SOLARIS], [1], [True if program is to be compiled for a Solaris kernel])
     ac_system="Solaris"
     ;;
+  *mingw32*)
+    AC_DEFINE([KERNEL_WIN32], [1], [True if program is to be compiled for a Windows kernel])
+    ac_system="Windows"
+    ;;
   *)
     ac_system="unknown"
     ;;
@@ -111,6 +115,7 @@ AM_CONDITIONAL([BUILD_FREEBSD], [test "x$ac_system" = "xFreeBSD"])
 AM_CONDITIONAL([BUILD_LINUX], [test "x$ac_system" = "xLinux"])
 AM_CONDITIONAL([BUILD_OPENBSD], [test "x$ac_system" = "xOpenBSD"])
 AM_CONDITIONAL([BUILD_SOLARIS], [test "x$ac_system" = "xSolaris"])
+AM_CONDITIONAL([BUILD_WIN32], [test "x$ac_system" = "xWindows"])
 
 if test "x$ac_system" = "xSolaris"; then
   AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], [1], [Define to enforce POSIX thread semantics under Solaris.])
@@ -550,6 +555,12 @@ if test "x$ac_system" = "xLinux"; then
     AC_DEFINE([HAVE_CAPABILITY], [1], [Define to 1 if you have cap_get_proc() (-lcap).])
   fi
 
+  # For pcie_errors plugin
+  AC_CHECK_HEADERS([linux/pci_regs.h],
+    [have_pci_regs_h="yes"],
+    [have_pci_regs_h="no (linux/pci_regs.h not found)"]
+  )
+
 else
   have_linux_raid_md_u_h="no"
   have_linux_wireless_h="no"
@@ -745,6 +756,7 @@ AC_CHECK_FUNCS_ONCE([ \
     getaddrinfo \
     getgrnam_r \
     getnameinfo \
+    getpwnam \
     getpwnam_r \
     gettimeofday \
     if_indextoname \
@@ -766,9 +778,12 @@ AC_CHECK_FUNCS_ONCE([ \
 AC_FUNC_STRERROR_R
 
 SAVE_CFLAGS="$CFLAGS"
-# Emulate behavior of src/Makefile.am
-if test "x$GCC" = "xyes"; then
-  CFLAGS="$CFLAGS -Wall -Werror"
+CFLAGS="-Wall -Werror"
+SAVE_LDFLAGS="$LDFLAGS"
+LDFLAGS=""
+if test "x$ac_system" = "xWindows"; then
+  # This is exported from build.sh
+  LDFLAGS="$LDFLAGS -L${GNULIB_DIR}"
 fi
 
 AC_CACHE_CHECK([for strtok_r],
@@ -842,6 +857,7 @@ if test "x$c_cv_have_strtok_r_default" = "xno"; then
 fi
 
 CFLAGS="$SAVE_CFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
 if test "x$c_cv_have_strtok_r_reentrant" = "xyes"; then
   CFLAGS="$CFLAGS -D_REENTRANT=1"
 fi
@@ -851,11 +867,17 @@ AC_CHECK_FUNCS([socket],
   [
     AC_CHECK_LIB([socket], [socket],
       [socket_needs_socket="yes"],
-      [AC_MSG_ERROR([cannot find socket() in libsocket])]
+      [
+        AC_CHECK_LIB([gnu], [rpl_socket],
+          [socket_needs_gnulib="yes"],
+          [AC_MSG_ERROR([cannot find socket() in libsocket])]
+        )
+      ]
     )
   ]
 )
 AM_CONDITIONAL([BUILD_WITH_LIBSOCKET], [test "x$socket_needs_socket" = "xyes"])
+AM_CONDITIONAL([BUILD_WITH_GNULIB], [test "x$socket_needs_gnulib" = "xyes"])
 
 clock_gettime_needs_posix4="no"
 AC_CHECK_FUNCS([clock_gettime],
@@ -1567,7 +1589,7 @@ if test "x$have_getmntent" = "xlibc"; then
               struct mntent *me;
               fh = setmntent ("/etc/mtab", "r");
               me = getmntent (fh);
-              return(me->mnt_passno);
+              return me->mnt_passno;
             ]]
           )
         ],
@@ -1590,7 +1612,7 @@ if test "x$have_getmntent" = "xlibc"; then
               int status;
               fh = fopen ("/etc/mnttab", "r");
               status = getmntent (fh, &mt);
-              return(status);
+              return status;
             ]]
           )
         ],
@@ -1878,14 +1900,23 @@ fi
 
 # libi2c-dev
 if test "x$ac_system" = "xLinux"; then
+  with_libi2c_libs=""
+  AC_CHECK_HEADERS([i2c/smbus.h],
+    [with_libi2c_libs="-li2c"]
+  )
   AC_CHECK_DECL([i2c_smbus_read_i2c_block_data],
     [with_libi2c="yes"],
     [with_libi2c="no (symbol i2c_smbus_read_i2c_block_data not found - have you installed libi2c-dev ?)"],
     [[
       #include <stdlib.h>
       #include <linux/i2c-dev.h>
+      #if HAVE_I2C_SMBUS_H
+      # include <i2c/smbus.h>
+      #endif
     ]]
   )
+  BUILD_WITH_LIBI2C_LIBS="$with_libi2c_libs"
+  AC_SUBST([BUILD_WITH_LIBI2C_LIBS])
 else
   with_libi2c="no (Linux only)"
 fi
@@ -2043,6 +2074,45 @@ if test "x$with_kvm_openfiles" = "xyes"; then
   with_libkvm="yes"
 fi
 
+# --with-cuda {{{
+AC_ARG_WITH([cuda],
+  [AS_HELP_STRING([--with-cuda@<:@=PREFIX@:>@], [Path to cuda.])],
+  [
+    if test "x$withval" != "xno" && test "x$withval" != "xyes"; then
+      with_cuda_cppflags="-I$withval/include"
+      with_cuda_ldflags="-I$withval/lib"
+      with_cuda="yes"
+    else
+      with_cuda="$withval"
+    fi
+  ],
+  [with_cuda="no"]
+)
+
+if test "x$with_cuda" = "xyes"; then
+  SAVE_CPPFLAGS="$CPPFLAGS"
+  CPPFLAGS="$CPPFLAGS $with_cuda_cppflags"
+
+  AC_CHECK_HEADERS([nvml.h],
+    [with_cuda="yes"],
+    [with_cuda="no (nvml.h not found)"]
+  )
+
+  CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+if test "x$with_cuda" = "xyes"; then
+  BUILD_WITH_CUDA_CPPFLAGS="$CUDA_CPPFLAGS"
+  BUILD_WITH_CUDA_LDFLAGS="$CUDA_LDFLAGS"
+  BUILD_WITH_CUDA_LIBS="-lnvidia-ml"
+fi
+
+AC_SUBST([BUILD_WITH_CUDA_CPPFLAGS])
+AC_SUBST([BUILD_WITH_CUDA_LDFLAGS])
+AC_SUBST([BUILD_WITH_CUDA_LIBS])
+
+# }}}
+
 # --with-libaquaero5 {{{
 AC_ARG_WITH([libaquaero5],
   [AS_HELP_STRING([--with-libaquaero5@<:@=PREFIX@:>@], [Path to aquatools-ng source code.])],
@@ -2066,9 +2136,6 @@ CPPFLAGS="$CPPFLAGS $LIBAQUAERO5_CFLAGS"
 LDFLAGS="$LDFLAGS $LIBAQUAERO5_LDFLAGS"
 
 if test "x$with_libaquaero5" = "xyes"; then
-  if test "x$LIBAQUAERO5_CFLAGS" != "x"; then
-    AC_MSG_NOTICE([libaquaero5 CPPFLAGS: $LIBAQUAERO5_CFLAGS])
-  fi
   AC_CHECK_HEADERS([libaquaero5.h],
     [with_libaquaero5="yes"],
     [with_libaquaero5="no (libaquaero5.h not found)"]
@@ -2076,9 +2143,6 @@ if test "x$with_libaquaero5" = "xyes"; then
 fi
 
 if test "x$with_libaquaero5" = "xyes"; then
-  if test "x$LIBAQUAERO5_LDFLAGS" != "x"; then
-    AC_MSG_NOTICE([libaquaero5 LDFLAGS: $LIBAQUAERO5_LDFLAGS])
-  fi
   AC_CHECK_LIB([aquaero5], libaquaero5_poll,
     [with_libaquaero5="yes"],
     [with_libaquaero5="no (symbol 'libaquaero5_poll' not found)"]
@@ -2119,9 +2183,6 @@ CPPFLAGS="$CPPFLAGS $LIBHIREDIS_CPPFLAGS"
 LDFLAGS="$LDFLAGS $LIBHIREDIS_LDFLAGS"
 
 if test "x$with_libhiredis" = "xyes"; then
-  if test "x$LIBHIREDIS_CPPFLAGS" != "x"; then
-    AC_MSG_NOTICE([libhiredis CPPFLAGS: $LIBHIREDIS_CPPFLAGS])
-  fi
   AC_CHECK_HEADERS([hiredis/hiredis.h],
     [with_libhiredis="yes"],
     [with_libhiredis="no (hiredis.h not found)"]
@@ -2129,9 +2190,6 @@ if test "x$with_libhiredis" = "xyes"; then
 fi
 
 if test "x$with_libhiredis" = "xyes"; then
-  if test "x$LIBHIREDIS_LDFLAGS" != "x"; then
-    AC_MSG_NOTICE([libhiredis LDFLAGS: $LIBHIREDIS_LDFLAGS])
-  fi
   AC_CHECK_LIB([hiredis], [redisCommand],
     [with_libhiredis="yes"],
     [with_libhiredis="no (symbol 'redisCommand' not found)"]
@@ -2273,6 +2331,8 @@ fi
 
 AC_SUBST(BUILD_WITH_LIBCURL_CFLAGS)
 AC_SUBST(BUILD_WITH_LIBCURL_LIBS)
+
+AM_CONDITIONAL([BUILD_WITH_LIBCURL], [test "x$with_libcurl" = "xyes"])
 # }}}
 
 # --with-libdbi {{{
@@ -2567,9 +2627,6 @@ LDFLAGS="$LDFLAGS $GCRYPT_LDFLAGS"
 LIBS="$LIBS $GCRYPT_LIBS"
 
 if test "x$with_libgcrypt" = "xyes"; then
-  if test "x$GCRYPT_CPPFLAGS" != "x"; then
-    AC_MSG_NOTICE([gcrypt CPPFLAGS: $GCRYPT_CPPFLAGS])
-  fi
   AC_CHECK_HEADERS([gcrypt.h],
     [with_libgcrypt="yes"],
     [with_libgcrypt="no (gcrypt.h not found)"]
@@ -2652,6 +2709,7 @@ AC_ARG_WITH([libgrpc++],
     if test "x$withval" != "xno" && test "x$withval" != "xyes"; then
       with_libgrpcpp_cppflags="-I$withval/include"
       with_libgrpcpp_ldflags="-L$withval/lib"
+      with_libgrpcpp_bin="$withval/bin"
       with_libgrpcpp="yes"
     fi
     if test "x$withval" = "xno"; then
@@ -2731,7 +2789,11 @@ AC_SUBST([BUILD_WITH_LIBGRPCPP_LIBS])
 # }}}
 
 AC_ARG_VAR([GRPC_CPP_PLUGIN], [path to the grpc_cpp_plugin binary])
-AC_PATH_PROG([GRPC_CPP_PLUGIN], [grpc_cpp_plugin])
+if test "x$with_libgrpcpp_bin" = "x"; then
+  AC_PATH_PROG([GRPC_CPP_PLUGIN], [grpc_cpp_plugin])
+else
+  AC_PATH_PROG([GRPC_CPP_PLUGIN], [grpc_cpp_plugin], [], "$with_libgrpcpp_bin:$PATH")
+fi
 AM_CONDITIONAL([HAVE_GRPC_CPP], [test "x$GRPC_CPP_PLUGIN" != "x"])
 
 # --with-libiptc {{{
@@ -2901,18 +2963,6 @@ if test "x$with_java" = "xyes"; then
   fi; fi
 fi
 
-if test "x$JAVA_CPPFLAGS" != "x"; then
-  AC_MSG_NOTICE([Building with JAVA_CPPFLAGS set to: $JAVA_CPPFLAGS])
-fi
-if test "x$JAVA_CFLAGS" != "x"; then
-  AC_MSG_NOTICE([Building with JAVA_CFLAGS set to: $JAVA_CFLAGS])
-fi
-if test "x$JAVA_LDFLAGS" != "x"; then
-  AC_MSG_NOTICE([Building with JAVA_LDFLAGS set to: $JAVA_LDFLAGS])
-fi
-if test "x$JAVA_LIBS" != "x"; then
-  AC_MSG_NOTICE([Building with JAVA_LIBS set to: $JAVA_LIBS])
-fi
 if test "x$JAVAC" = "x"; then
   with_javac_path="$PATH"
   if test "x$with_java_home" != "x"; then
@@ -2970,7 +3020,6 @@ fi
 
 if test "x$with_java" = "xyes"; then
   JAVA_LIBS="$JAVA_LIBS -ljvm"
-  AC_MSG_NOTICE([Building with JAVA_LIBS set to: $JAVA_LIBS])
 fi
 
 CPPFLAGS="$SAVE_CPPFLAGS"
@@ -3010,10 +3059,6 @@ CPPFLAGS="$CPPFLAGS $LIBLDAP_CPPFLAGS"
 LDFLAGS="$LDFLAGS $LIBLDAP_LDFLAGS"
 
 if test "x$with_libldap" = "xyes"; then
-  if test "x$LIBLDAP_CPPFLAGS" != "x"; then
-    AC_MSG_NOTICE([libldap CPPFLAGS: $LIBLDAP_CPPFLAGS])
-  fi
-
   AC_CHECK_HEADERS([ldap.h],
     [with_libldap="yes"],
     [with_libldap="no ('ldap.h' not found)"]
@@ -3021,10 +3066,6 @@ if test "x$with_libldap" = "xyes"; then
 fi
 
 if test "x$with_libldap" = "xyes"; then
-  if test "x$LIBLDAP_LDFLAGS" != "x"; then
-    AC_MSG_NOTICE([libldap LDFLAGS: $LIBLDAP_LDFLAGS])
-  fi
-
   AC_CHECK_LIB([ldap], [ldap_initialize],
     [with_libldap="yes"],
     [with_libldap="no (symbol 'ldap_initialize' not found)"]
@@ -3061,18 +3102,33 @@ else
           PKG_CHECK_MODULES([LUA], [lua5.3],
             [with_liblua="yes"],
             [
-              PKG_CHECK_MODULES([LUA], [lua-5.2],
+              PKG_CHECK_MODULES([LUA], [lua53],
                 [with_liblua="yes"],
                 [
-                  PKG_CHECK_MODULES([LUA], [lua5.2],
+                  PKG_CHECK_MODULES([LUA], [lua-5.2],
                     [with_liblua="yes"],
                     [
-                      PKG_CHECK_MODULES([LUA], [lua-5.1],
+                      PKG_CHECK_MODULES([LUA], [lua5.2],
                         [with_liblua="yes"],
                         [
-                          PKG_CHECK_MODULES([LUA], [lua5.1],
+                          PKG_CHECK_MODULES([LUA], [lua52],
                             [with_liblua="yes"],
-                            [with_liblua="no (pkg-config cannot find liblua)"]
+                            [
+                              PKG_CHECK_MODULES([LUA], [lua-5.1],
+                                [with_liblua="yes"],
+                                [
+                                  PKG_CHECK_MODULES([LUA], [lua5.1],
+                                    [with_liblua="yes"],
+                                    [
+                                      PKG_CHECK_MODULES([LUA], [lua51],
+                                        [with_liblua="yes"],
+                                        [with_liblua="no (pkg-config cannot find liblua)"]
+                                      )
+                                    ]
+                                  )
+                                ]
+                              )
+                            ]
                           )
                         ]
                       )
@@ -3317,7 +3373,6 @@ AC_ARG_WITH([libmodbus],
 
 # configure using pkg-config
 if test "x$with_libmodbus" = "xuse_pkgconfig"; then
-  AC_MSG_NOTICE([Checking for libmodbus using $PKG_CONFIG])
   $PKG_CONFIG --exists 'libmodbus' 2>/dev/null
   if test $? -ne 0; then
     with_libmodbus="no (pkg-config doesn't know libmodbus)"
@@ -3400,10 +3455,6 @@ if test "x$with_libmongoc" = "xyes"; then
 
   CPPFLAGS="$CPPFLAGS $LIBMONGOC_CFLAGS"
 
-  if test "x$CPPFLAGS" != "x"; then
-    AC_MSG_NOTICE([libmongoc CPPFLAGS: $LIBMONGOC_CFLAGS])
-  fi
-
   AC_CHECK_HEADERS([mongoc.h],
     [with_libmongoc="yes"],
     [with_libmongoc="no ('mongoc.h' not found)"]
@@ -3419,10 +3470,6 @@ if test "x$with_libmongoc" = "xyes"; then
   CPPFLAGS="$CPPFLAGS $LIBMONGOC_CFLAGS"
   LDFLAGS="$LDFLAGS $LIBMONGOC_LDFLAGS"
 
-  if test "x$LIBMONGOC_LDFLAGS" != "x"; then
-    AC_MSG_NOTICE([libmongoc LDFLAGS: $LIBMONGOC_LDFLAGS])
-  fi
-
   AC_CHECK_LIB([mongoc-1.0], [mongoc_init],
     [with_libmongoc="yes"],
     [with_libmongoc="no (symbol 'mongoc_init' not found)"]
@@ -3674,6 +3721,18 @@ if test "x$with_libmnl" = "xyes"; then
     [[#include <linux/if_link.h>]]
   )
 
+  AC_CHECK_MEMBERS([struct rtnl_link_stats.rx_nohandler],
+    [],
+    [],
+    [[#include <linux/if_link.h>]]
+  )
+
+  AC_CHECK_MEMBERS([struct rtnl_link_stats64.rx_nohandler],
+    [],
+    [],
+    [[#include <linux/if_link.h>]]
+  )
+
   AC_CHECK_LIB([mnl], [mnl_nlmsg_get_payload],
     [with_libmnl="yes"],
     [with_libmnl="no (symbol 'mnl_nlmsg_get_payload' not found)"],
@@ -3687,6 +3746,7 @@ if test "x$with_libmnl" = "xyes"; then
 fi
 AC_SUBST([BUILD_WITH_LIBMNL_CFLAGS])
 AC_SUBST([BUILD_WITH_LIBMNL_LIBS])
+AM_CONDITIONAL([HAVE_LIBMNL], [test "x$with_libmnl" = "xyes"])
 # }}}
 
 # --with-libnetapp {{{
@@ -3713,9 +3773,6 @@ CPPFLAGS="$CPPFLAGS $LIBNETAPP_CPPFLAGS"
 LDFLAGS="$LDFLAGS $LIBNETAPP_LDFLAGS"
 
 if test "x$with_libnetapp" = "xyes"; then
-  if test "x$LIBNETAPP_CPPFLAGS" != "x"; then
-    AC_MSG_NOTICE([netapp CPPFLAGS: $LIBNETAPP_CPPFLAGS])
-  fi
   AC_CHECK_HEADERS([netapp_api.h],
     [with_libnetapp="yes"],
     [with_libnetapp="no (netapp_api.h not found)"]
@@ -3723,16 +3780,10 @@ if test "x$with_libnetapp" = "xyes"; then
 fi
 
 if test "x$with_libnetapp" = "xyes"; then
-  if test "x$LIBNETAPP_LDFLAGS" != "x"; then
-    AC_MSG_NOTICE([netapp LDFLAGS: $LIBNETAPP_LDFLAGS])
-  fi
-
   if test "x$LIBNETAPP_LIBS" = "x"; then
     LIBNETAPP_LIBS="$PTHREAD_LIBS -lxml -ladt -lssl -lm -lcrypto -lz"
   fi
 
-  AC_MSG_NOTICE([netapp LIBS: $LIBNETAPP_LIBS])
-
   AC_CHECK_LIB([netapp], [na_server_invoke_elem],
     [with_libnetapp="yes"],
     [with_libnetapp="no (symbol na_server_invoke_elem not found)"],
@@ -3794,7 +3845,7 @@ if test "x$with_libnetsnmp" = "xyes"; then
   LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags"
 
   AC_CHECK_LIB([netsnmp], [init_snmp],
-    [with_libnetsmp="yes"],
+    [with_libnetsnmp="yes"],
     [with_libnetsnmp="no (libnetsnmp not found)"]
   )
 
@@ -3802,6 +3853,62 @@ if test "x$with_libnetsnmp" = "xyes"; then
 fi
 
 if test "x$with_libnetsnmp" = "xyes"; then
+  SAVE_LDFLAGS="$LDFLAGS"
+  LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags"
+
+  AC_CHECK_LIB([netsnmp], [netsnmp_get_version],
+    [with_libnetsnmp="yes"],
+    [with_libnetsnmp="no (couldn't get libnetsnmp version)"]
+  )
+
+  LDFLAGS="$SAVE_LDFLAGS"
+fi
+
+if test "x$with_libnetsnmp" = "xyes"; then
+  SAVE_CPPFLAGS="$CPPFLAGS"
+  SAVE_LDFLAGS="$LDFLAGS"
+  SAVE_LIBS="$LIBS"
+  CPPFLAGS="$CPPFLAGS $with_libnetsnmp_cppflags -Wall -Werror"
+  LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags"
+  LIBS="$LIBS -lnetsnmp"
+
+  AC_CACHE_CHECK([whether netsnmp library has old API],
+    [c_cv_have_netsnmp_old_api],
+    [
+      AC_LINK_IFELSE(
+        [
+          AC_LANG_PROGRAM(
+            [[
+              #include <net-snmp/net-snmp-config.h>
+              #include <net-snmp/net-snmp-includes.h>
+            ]],
+            [[
+              netsnmp_variable_list *key = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);;
+              int val;
+              u_char type = ASN_INTEGER;
+              snmp_set_var_value(key, &val, sizeof(val));
+              snmp_set_var_typed_value(key, type, &val, sizeof(val));
+              return 0;
+            ]]
+          )
+        ],
+        [c_cv_have_netsnmp_old_api="no"],
+        [c_cv_have_netsnmp_old_api="yes"]
+      )
+    ]
+  )
+
+  if test "x$c_cv_have_netsnmp_old_api" = "xyes"; then
+    AC_DEFINE([HAVE_NETSNMP_OLD_API], [1],
+              ["Define 1 if you have old netsnmp API]")
+  fi
+
+  CPPFLAGS="$SAVE_CPPFLAGS"
+  LDFLAGS="$SAVE_LDFLAGS"
+  LIBS="$SAVE_LIBS"
+fi
+
+if test "x$with_libnetsnmp" = "xyes"; then
   BUILD_WITH_LIBNETSNMP_CPPFLAGS="$with_libnetsnmp_cppflags"
   BUILD_WITH_LIBNETSNMP_LDFLAGS="$with_libnetsnmp_ldflags"
   BUILD_WITH_LIBNETSNMP_LIBS="-lnetsnmp"
@@ -3812,7 +3919,7 @@ AC_SUBST([BUILD_WITH_LIBNETSNMP_LDFLAGS])
 AC_SUBST([BUILD_WITH_LIBNETSNMP_LIBS])
 # }}}
 
-# --with-libnetsmpagent {{{
+# --with-libnetsnmpagent {{{
 AC_ARG_WITH([libnetsnmpagent],
   [AS_HELP_STRING([--with-libnetsnmpagent@<:@=PREFIX@:>@], [Path to libnetsnmpagent.])],
   [
@@ -4094,7 +4201,7 @@ if test "x$with_libpcap" = "xyes"; then
             [[#include <pcap.h>]],
             [[
               int val = PCAP_ERROR_IFACE_NOT_UP;
-              return(val);
+              return val;
             ]]
           )
         ],
@@ -4699,6 +4806,56 @@ if test "$with_libpython" != "xno"; then
 fi
 # }}} --with-libpython
 
+# --with-libqpid_proton {{{
+AC_ARG_WITH([libqpid_proton],
+  [AS_HELP_STRING([--with-libqpid_proton@<:@=PREFIX@:>@], [Path to libqpid_proton.])],
+  [
+    if test "x$withval" != "xno" && test "x$withval" != "xyes"; then
+      with_libqpid_proton_cppflags="-I$withval/include"
+      with_libqpid_proton_ldflags="-L$withval/lib"
+      with_libqpid_proton="yes"
+    else
+      with_libqpid_proton="$withval"
+    fi
+  ],
+  [with_libqpid_proton="yes"]
+)
+
+if test "x$with_libqpid_proton" = "xyes"; then
+  SAVE_CPPFLAGS="$CPPFLAGS"
+  CPPFLAGS="$CPPFLAGS $with_libqpid_proton_cppflags"
+
+  AC_CHECK_HEADERS([proton/proactor.h],
+    [with_libqpid_proton="yes"],
+    [with_libqpid_proton="no (proton/proactor.h not found)"]
+  )
+
+  CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+
+if test "x$with_libqpid_proton" = "xyes"; then
+  SAVE_LDFLAGS="$LDFLAGS"
+  LDFLAGS="$LDFLAGS $with_libqpid_proton_ldflags"
+
+  AC_CHECK_LIB([qpid-proton], [pn_connection],
+    [with_libqpid_proton="yes"],
+    [with_libqpid_proton="no (Symbol 'pn_connection' not found)"])
+
+  LDFLAGS="$SAVE_LDFLAGS"
+fi
+
+if test "x$with_libqpid_proton" = "xyes"; then
+  BUILD_WITH_LIBQPIDPROTON_CPPFLAGS="$with_libqpid_proton_cppflags"
+  BUILD_WITH_LIBQPIDPROTON_LDFLAGS="$with_libqpid_proton_ldflags"
+  BUILD_WITH_LIBQPIDPROTON_LIBS="-lqpid-proton"
+fi
+
+AC_SUBST(BUILD_WITH_LIBQPIDPROTON_CPPFLAGS)
+AC_SUBST(BUILD_WITH_LIBQPIDPROTON_LDFLAGS)
+AC_SUBST(BUILD_WITH_LIBQPIDPROTON_LIBS)
+
+# }}}
+
 # --with-librabbitmq {{{
 AC_ARG_WITH([librabbitmq],
   [AS_HELP_STRING([--with-librabbitmq@<:@=PREFIX@:>@], [Path to librabbitmq.])],
@@ -5063,6 +5220,27 @@ if test "x$with_libsensors" = "xyes"; then
 fi
 
 if test "x$with_libsensors" = "xyes"; then
+  SAVE_CPPFLAGS="$CPPFLAGS"
+  CPPFLAGS="$CPPFLAGS $with_sensors_cppflags"
+  AC_PREPROC_IFELSE(
+    [
+      AC_LANG_SOURCE(
+        [[
+          #include <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"
@@ -5083,6 +5261,55 @@ PKG_CHECK_MODULES([LIBSIGROK], [libsigrok < 0.4],
 )
 # }}}
 
+# --with-libssl {{{
+with_libssl_cflags=""
+with_libssl_ldflags=""
+AC_ARG_WITH([libssl], [AS_HELP_STRING([--with-libssl@<:@=PREFIX@:>@], [Path to libssl.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"; then
+               with_libssl_cppflags="-I$withval/include"
+               with_libssl_ldflags="-L$withval/lib"
+               with_libssl="yes"
+       else
+               with_libssl="$withval"
+       fi
+],
+[
+       with_libssl="yes"
+])
+if test "x$with_libssl" = "xyes"; then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libssl_cppflags"
+
+  AC_CHECK_HEADERS([openssl/sha.h openssl/blowfish.h openssl/rand.h],
+    [with_libssl="yes"],
+    [with_libssl="no (ssl header not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libssl" = "xyes"; then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CPPFLAGS="$CPPFLAGS $with_libssl_cppflags"
+       LDFLAGS="$LDFLAGS $with_libssl_ldflags"
+
+       AC_CHECK_LIB([ssl], [OPENSSL_init_ssl], [with_libssl="yes"], [with_libssl="no (Symbol 'SSL_library_init' not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libssl" = "xyes"; then
+       BUILD_WITH_LIBSSL_CFLAGS="$with_libssl_cflags"
+       BUILD_WITH_LIBSSL_LDFLAGS="$with_libssl_ldflags"
+       BUILD_WITH_LIBSSL_LIBS="-lssl -lcrypto"
+       AC_SUBST([BUILD_WITH_LIBSSL_CFLAGS])
+       AC_SUBST([BUILD_WITH_LIBSSL_LDFLAGS])
+       AC_SUBST([BUILD_WITH_LIBSSL_LIBS])
+       AC_DEFINE([HAVE_LIBSSL], [1], [Define if libssl is present and usable.])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBSSL, test "x$with_libssl" = "xyes")
+# }}}
+
 # --with-libstatgrab {{{
 AC_ARG_WITH([libstatgrab],
   [AS_HELP_STRING([--with-libstatgrab@<:@=PREFIX@:>@], [Path to libstatgrab.])],
@@ -5382,7 +5609,6 @@ fi
 
 # configure using pkg-config
 if test "x$with_libupsclient" = "xuse_pkgconfig"; then
-  AC_MSG_NOTICE([Checking for libupsclient using $PKG_CONFIG])
   $PKG_CONFIG --exists 'libupsclient' 2>/dev/null
   if test $? -ne 0; then
     with_libupsclient="no (pkg-config doesn't know libupsclient)"
@@ -5638,6 +5864,7 @@ AC_SUBST([BUILD_WITH_LIBYAJL_LDFLAGS])
 AC_SUBST([BUILD_WITH_LIBYAJL_LIBS])
 
 AM_CONDITIONAL([BUILD_WITH_LIBYAJL], [test "x$with_libyajl" = "xyes"])
+AM_CONDITIONAL([BUILD_WITH_LIBYAJL2], [test "x$with_libyajl$with_libyajl2" = "xyesyes"])
 # }}}
 
 # --with-mic {{{
@@ -5769,32 +5996,41 @@ AC_SUBST([BUILD_WITH_LIBVARNISH_CFLAGS])
 AC_SUBST([BUILD_WITH_LIBVARNISH_LIBS])
 # }}}
 
-# pkg-config --exists 'libxml-2.0'; pkg-config --exists libvirt {{{
-$PKG_CONFIG --exists 'libxml-2.0' 2>/dev/null
-if test $? -eq 0; then
-  with_libxml2="yes"
-else
-  with_libxml2="no (pkg-config doesn't know libxml-2.0)"
-fi
-
-$PKG_CONFIG --exists libvirt 2>/dev/null
-if test $? = 0; then
-  with_libvirt="yes"
-else
-  with_libvirt="no (pkg-config doesn't know libvirt)"
-fi
-
-if test "x$with_libxml2" = "xyes"; then
-  with_libxml2_cflags="`$PKG_CONFIG --cflags libxml-2.0`"
-  if test $? -ne 0; then
-    with_libxml2="no"
-  fi
-
-  with_libxml2_ldflags="`$PKG_CONFIG --libs libxml-2.0`"
-  if test $? -ne 0; then
-    with_libxml2="no"
-  fi
-fi
+# --with-libxml2 {{{
+AC_ARG_WITH(libxml2,
+  [AS_HELP_STRING([--with-libxml2@<:@=PREFIX@:>@], [Path to libxml2.])],
+  [
+    if test "x$withval" = "xno"; then
+      with_libxml2="no"
+    else if test "x$withval" = "xyes"; then
+      $PKG_CONFIG --exists 'libxml-2.0' 2>/dev/null
+      if test $? -eq 0; then
+        with_libxml2="yes"
+        with_libxml2_cflags="`$PKG_CONFIG --cflags libxml-2.0`"
+        with_libxml2_ldflags="`$PKG_CONFIG --libs libxml-2.0`"
+      else
+        with_libxml2="no (pkg-config doesn't know libxml-2.0)"
+      fi
+    else
+      with_libxml2="yes"
+      with_libxml2_cflags="-I$withval/include"
+      with_libxml2_ldflags="-L$withval/lib"
+    fi; fi
+  ],
+  dnl  if no argument --with-libxml2 was passed, find the library locations
+  dnl  with pkg-config just like above, when --with-libxml2=yes.
+  [
+    with_libxml2="yes"
+    $PKG_CONFIG --exists 'libxml-2.0' 2>/dev/null
+    if test $? -eq 0; then
+      with_libxml2="yes"
+      with_libxml2_cflags="`$PKG_CONFIG --cflags libxml-2.0`"
+      with_libxml2_ldflags="`$PKG_CONFIG --libs libxml-2.0`"
+    else
+      with_libxml2="no (pkg-config doesn't know libxml-2.0)"
+    fi
+  ]
+)
 
 if test "x$with_libxml2" = "xyes"; then
   SAVE_CPPFLAGS="$CPPFLAGS"
@@ -5827,6 +6063,15 @@ fi
 
 AC_SUBST([BUILD_WITH_LIBXML2_CFLAGS])
 AC_SUBST([BUILD_WITH_LIBXML2_LIBS])
+# }}}
+
+# pkg-config --exists libvirt {{{
+$PKG_CONFIG --exists libvirt 2>/dev/null
+if test $? = 0; then
+  with_libvirt="yes"
+else
+  with_libvirt="no (pkg-config doesn't know libvirt)"
+fi
 
 if test "x$with_libvirt" = "xyes"; then
   with_libvirt_cflags="`$PKG_CONFIG --cflags libvirt`"
@@ -6141,6 +6386,7 @@ plugin_ethstat="no"
 plugin_fhcount="no"
 plugin_fscache="no"
 plugin_gps="no"
+plugin_gpu_nvidia="no"
 plugin_grpc="no"
 plugin_hugepages="no"
 plugin_intel_pmu="no"
@@ -6159,6 +6405,7 @@ plugin_nfs="no"
 plugin_numa="no"
 plugin_ovs_events="no"
 plugin_ovs_stats="no"
+plugin_pcie_errors="no"
 plugin_perl="no"
 plugin_pinba="no"
 plugin_processes="no"
@@ -6180,6 +6427,7 @@ plugin_vmem="no"
 plugin_vserver="no"
 plugin_wireless="no"
 plugin_write_prometheus="no"
+plugin_write_stackdriver="no"
 plugin_xencpu="no"
 plugin_zfs_arc="no"
 plugin_zone="no"
@@ -6237,6 +6485,10 @@ if test "x$ac_system" = "xLinux"; then
     plugin_ovs_events="yes"
     plugin_ovs_stats="yes"
   fi
+
+  if test "x$have_pci_regs_h" = "xyes"; then
+    plugin_pcie_errors="yes"
+  fi
 fi
 
 if test "x$ac_system" = "xOpenBSD"; then
@@ -6334,6 +6586,10 @@ if test "x$with_libcurl" = "xyes" && test "x$with_libyajl" = "xyes"; then
   plugin_curl_json="yes"
 fi
 
+if test "x$with_libcurl" = "xyes" && test "x$with_libssl" = "xyes" && test "x$with_libyajl" = "xyes" && test "x$with_libyajl2" = "xyes"; then
+  plugin_write_stackdriver="yes"
+fi
+
 if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"; then
   plugin_curl_xml="yes"
 fi
@@ -6397,8 +6653,18 @@ if test "x$with_libgps" = "xyes"; then
   plugin_gps="yes"
 fi
 
-if test "x$with_libgrpcpp" = "xyes" && test "x$with_libprotobuf" = "xyes" && test "x$have_protoc3" = "xyes" && test "x$GRPC_CPP_PLUGIN" != "x"; then
-  plugin_grpc="yes"
+plugin_grpc="yes"
+if test "x$GRPC_CPP_PLUGIN" = "x"; then
+  plugin_grpc="no (grpc_cpp_plugin not found)"
+fi
+if test "x$have_protoc3" != "xyes"; then
+  plugin_grpc="no (protoc3 not found)"
+fi
+if test "x$with_libprotobuf" != "xyes"; then
+  plugin_grpc="no (libprotobuf not found)"
+fi
+if test "x$with_libgrpcpp" != "xyes"; then
+  plugin_grpc="no (libgrpc++ not found)"
 fi
 
 if test "x$have_getifaddrs" = "xyes"; then
@@ -6512,161 +6778,165 @@ AC_ARG_ENABLE([all-plugins],
 
 m4_divert_once([HELP_ENABLE], [])
 
-AC_PLUGIN([aggregation],         [yes],                     [Aggregation plugin])
-AC_PLUGIN([amqp],                [$with_librabbitmq],       [AMQP output plugin])
-AC_PLUGIN([apache],              [$with_libcurl],           [Apache httpd statistics])
-AC_PLUGIN([apcups],              [yes],                     [Statistics of UPSes by APC])
-AC_PLUGIN([apple_sensors],       [$with_libiokit],          [Apple hardware sensors])
-AC_PLUGIN([aquaero],             [$with_libaquaero5],       [Aquaero hardware sensors])
-AC_PLUGIN([ascent],              [$plugin_ascent],          [AscentEmu player statistics])
-AC_PLUGIN([barometer],           [$plugin_barometer],       [Barometer sensor on I2C])
-AC_PLUGIN([battery],             [$plugin_battery],         [Battery statistics])
-AC_PLUGIN([bind],                [$plugin_bind],            [ISC Bind nameserver statistics])
-AC_PLUGIN([ceph],                [$plugin_ceph],            [Ceph daemon statistics])
-AC_PLUGIN([cgroups],             [$plugin_cgroups],         [CGroups CPU usage accounting])
-AC_PLUGIN([chrony],              [yes],                     [Chrony statistics])
-AC_PLUGIN([conntrack],           [$plugin_conntrack],       [nf_conntrack statistics])
-AC_PLUGIN([contextswitch],       [$plugin_contextswitch],   [context switch statistics])
-AC_PLUGIN([cpu],                 [$plugin_cpu],             [CPU usage statistics])
-AC_PLUGIN([cpufreq],             [$plugin_cpufreq],         [CPU frequency statistics])
-AC_PLUGIN([cpusleep],            [$plugin_cpusleep],        [CPU sleep statistics])
-AC_PLUGIN([csv],                 [yes],                     [CSV output plugin])
-AC_PLUGIN([curl],                [$with_libcurl],           [CURL generic web statistics])
-AC_PLUGIN([curl_json],           [$plugin_curl_json],       [CouchDB statistics])
-AC_PLUGIN([curl_xml],            [$plugin_curl_xml],        [CURL generic xml statistics])
-AC_PLUGIN([dbi],                 [$with_libdbi],            [General database statistics])
-AC_PLUGIN([df],                  [$plugin_df],              [Filesystem usage statistics])
-AC_PLUGIN([disk],                [$plugin_disk],            [Disk usage statistics])
-AC_PLUGIN([dns],                 [$with_libpcap],           [DNS traffic analysis])
-AC_PLUGIN([dpdkevents],          [$plugin_dpdkevents],      [Events from DPDK])
-AC_PLUGIN([dpdkstat],            [$plugin_dpdkstat],        [Stats from DPDK])
-AC_PLUGIN([drbd],                [$plugin_drbd],            [DRBD statistics])
-AC_PLUGIN([email],               [yes],                     [EMail statistics])
-AC_PLUGIN([entropy],             [$plugin_entropy],         [Entropy statistics])
-AC_PLUGIN([ethstat],             [$plugin_ethstat],         [Stats from NIC driver])
-AC_PLUGIN([exec],                [yes],                     [Execution of external programs])
-AC_PLUGIN([fhcount],             [$plugin_fhcount],         [File handles statistics])
-AC_PLUGIN([filecount],           [yes],                     [Count files in directories])
-AC_PLUGIN([fscache],             [$plugin_fscache],         [fscache statistics])
-AC_PLUGIN([gmond],               [$with_libganglia],        [Ganglia plugin])
-AC_PLUGIN([gps],                 [$plugin_gps],             [GPS plugin])
-AC_PLUGIN([grpc],                [$plugin_grpc],            [gRPC plugin])
-AC_PLUGIN([hddtemp],             [yes],                     [Query hddtempd])
-AC_PLUGIN([hugepages],           [$plugin_hugepages],       [Hugepages statistics])
-AC_PLUGIN([intel_pmu],           [$with_libjevents],        [Intel performance monitor plugin])
-AC_PLUGIN([intel_rdt],           [$with_libpqos],           [Intel RDT monitor plugin])
-AC_PLUGIN([interface],           [$plugin_interface],       [Interface traffic statistics])
-AC_PLUGIN([ipc],                 [$plugin_ipc],             [IPC statistics])
-AC_PLUGIN([ipmi],                [$plugin_ipmi],            [IPMI sensor statistics])
-AC_PLUGIN([iptables],            [$with_libiptc],           [IPTables rule counters])
-AC_PLUGIN([ipvs],                [$plugin_ipvs],            [IPVS connection statistics])
-AC_PLUGIN([irq],                 [$plugin_irq],             [IRQ statistics])
-AC_PLUGIN([java],                [$with_java],              [Embed the Java Virtual Machine])
-AC_PLUGIN([load],                [$plugin_load],            [System load])
-AC_PLUGIN([log_logstash],        [$plugin_log_logstash],    [Logstash json_event compatible logging])
-AC_PLUGIN([logfile],             [yes],                     [File logging plugin])
-AC_PLUGIN([lpar],                [$with_perfstat],          [AIX logical partitions statistics])
-AC_PLUGIN([lua],                 [$with_liblua],            [Lua plugin])
-AC_PLUGIN([lvm],                 [$with_liblvm2app],        [LVM statistics])
-AC_PLUGIN([madwifi],             [$have_linux_wireless_h],  [Madwifi wireless statistics])
-AC_PLUGIN([match_empty_counter], [yes],                     [The empty counter match])
-AC_PLUGIN([match_hashed],        [yes],                     [The hashed match])
-AC_PLUGIN([match_regex],         [yes],                     [The regex match])
-AC_PLUGIN([match_timediff],      [yes],                     [The timediff match])
-AC_PLUGIN([match_value],         [yes],                     [The value match])
-AC_PLUGIN([mbmon],               [yes],                     [Query mbmond])
-AC_PLUGIN([mcelog],              [$plugin_mcelog],          [Machine Check Exceptions notifications])
-AC_PLUGIN([md],                  [$have_linux_raid_md_u_h], [md (Linux software RAID) devices])
-AC_PLUGIN([memcachec],           [$with_libmemcached],      [memcachec statistics])
-AC_PLUGIN([memcached],           [yes],                     [memcached statistics])
-AC_PLUGIN([memory],              [$plugin_memory],          [Memory usage])
-AC_PLUGIN([mic],                 [$with_mic],               [Intel Many Integrated Core stats])
-AC_PLUGIN([modbus],              [$with_libmodbus],         [Modbus plugin])
-AC_PLUGIN([mqtt],                [$with_libmosquitto],      [MQTT output plugin])
-AC_PLUGIN([multimeter],          [$plugin_multimeter],      [Read multimeter values])
-AC_PLUGIN([mysql],               [$with_libmysql],          [MySQL statistics])
-AC_PLUGIN([netapp],              [$with_libnetapp],         [NetApp plugin])
-AC_PLUGIN([netlink],             [$with_libmnl],            [Enhanced Linux network statistics])
-AC_PLUGIN([network],             [yes],                     [Network communication plugin])
-AC_PLUGIN([nfs],                 [$plugin_nfs],             [NFS statistics])
-AC_PLUGIN([nginx],               [$with_libcurl],           [nginx statistics])
-AC_PLUGIN([notify_desktop],      [$with_libnotify],         [Desktop notifications])
-AC_PLUGIN([notify_email],        [$with_libesmtp],          [Email notifier])
-AC_PLUGIN([notify_nagios],       [yes],                     [Nagios notification plugin])
-AC_PLUGIN([ntpd],                [yes],                     [NTPd statistics])
-AC_PLUGIN([numa],                [$plugin_numa],            [NUMA virtual memory statistics])
-AC_PLUGIN([nut],                 [$with_libupsclient],      [Network UPS tools statistics])
-AC_PLUGIN([olsrd],               [yes],                     [olsrd statistics])
-AC_PLUGIN([onewire],             [$with_libowcapi],         [OneWire sensor statistics])
-AC_PLUGIN([openldap],            [$with_libldap],           [OpenLDAP statistics])
-AC_PLUGIN([openvpn],             [yes],                     [OpenVPN client statistics])
-AC_PLUGIN([oracle],              [$with_oracle],            [Oracle plugin])
-AC_PLUGIN([ovs_events],          [$plugin_ovs_events],      [OVS events plugin])
-AC_PLUGIN([ovs_stats],           [$plugin_ovs_stats],       [OVS statistics plugin])
-AC_PLUGIN([perl],                [$plugin_perl],            [Embed a Perl interpreter])
-AC_PLUGIN([pf],                  [$have_net_pfvar_h],       [BSD packet filter (PF) statistics])
+AC_PLUGIN([aggregation],         [yes],                       [Aggregation plugin])
+AC_PLUGIN([amqp],                [$with_librabbitmq],         [AMQP output plugin])
+AC_PLUGIN([amqp1],               [$with_libqpid_proton],      [AMQP 1.0 output plugin])
+AC_PLUGIN([apache],              [$with_libcurl],             [Apache httpd statistics])
+AC_PLUGIN([apcups],              [yes],                       [Statistics of UPSes by APC])
+AC_PLUGIN([apple_sensors],       [$with_libiokit],            [Apple hardware sensors])
+AC_PLUGIN([aquaero],             [$with_libaquaero5],         [Aquaero hardware sensors])
+AC_PLUGIN([ascent],              [$plugin_ascent],            [AscentEmu player statistics])
+AC_PLUGIN([barometer],           [$plugin_barometer],         [Barometer sensor on I2C])
+AC_PLUGIN([battery],             [$plugin_battery],           [Battery statistics])
+AC_PLUGIN([bind],                [$plugin_bind],              [ISC Bind nameserver statistics])
+AC_PLUGIN([ceph],                [$plugin_ceph],              [Ceph daemon statistics])
+AC_PLUGIN([cgroups],             [$plugin_cgroups],           [CGroups CPU usage accounting])
+AC_PLUGIN([chrony],              [yes],                       [Chrony statistics])
+AC_PLUGIN([conntrack],           [$plugin_conntrack],         [nf_conntrack statistics])
+AC_PLUGIN([contextswitch],       [$plugin_contextswitch],     [context switch statistics])
+AC_PLUGIN([cpu],                 [$plugin_cpu],               [CPU usage statistics])
+AC_PLUGIN([cpufreq],             [$plugin_cpufreq],           [CPU frequency statistics])
+AC_PLUGIN([cpusleep],            [$plugin_cpusleep],          [CPU sleep statistics])
+AC_PLUGIN([csv],                 [yes],                       [CSV output plugin])
+AC_PLUGIN([curl],                [$with_libcurl],             [CURL generic web statistics])
+AC_PLUGIN([curl_json],           [$plugin_curl_json],         [CouchDB statistics])
+AC_PLUGIN([curl_xml],            [$plugin_curl_xml],          [CURL generic xml statistics])
+AC_PLUGIN([dbi],                 [$with_libdbi],              [General database statistics])
+AC_PLUGIN([df],                  [$plugin_df],                [Filesystem usage statistics])
+AC_PLUGIN([disk],                [$plugin_disk],              [Disk usage statistics])
+AC_PLUGIN([dns],                 [$with_libpcap],             [DNS traffic analysis])
+AC_PLUGIN([dpdkevents],          [$plugin_dpdkevents],        [Events from DPDK])
+AC_PLUGIN([dpdkstat],            [$plugin_dpdkstat],          [Stats from DPDK])
+AC_PLUGIN([drbd],                [$plugin_drbd],              [DRBD statistics])
+AC_PLUGIN([email],               [yes],                       [EMail statistics])
+AC_PLUGIN([entropy],             [$plugin_entropy],           [Entropy statistics])
+AC_PLUGIN([ethstat],             [$plugin_ethstat],           [Stats from NIC driver])
+AC_PLUGIN([exec],                [yes],                       [Execution of external programs])
+AC_PLUGIN([fhcount],             [$plugin_fhcount],           [File handles statistics])
+AC_PLUGIN([filecount],           [yes],                       [Count files in directories])
+AC_PLUGIN([fscache],             [$plugin_fscache],           [fscache statistics])
+AC_PLUGIN([gmond],               [$with_libganglia],          [Ganglia plugin])
+AC_PLUGIN([gps],                 [$plugin_gps],               [GPS plugin])
+AC_PLUGIN([gpu_nvidia],          [$with_cuda],                [NVIDIA GPU plugin])
+AC_PLUGIN([grpc],                [$plugin_grpc],              [gRPC plugin])
+AC_PLUGIN([hddtemp],             [yes],                       [Query hddtempd])
+AC_PLUGIN([hugepages],           [$plugin_hugepages],         [Hugepages statistics])
+AC_PLUGIN([intel_pmu],           [$with_libjevents],          [Intel performance monitor plugin])
+AC_PLUGIN([intel_rdt],           [$with_libpqos],             [Intel RDT monitor plugin])
+AC_PLUGIN([interface],           [$plugin_interface],         [Interface traffic statistics])
+AC_PLUGIN([ipc],                 [$plugin_ipc],               [IPC statistics])
+AC_PLUGIN([ipmi],                [$plugin_ipmi],              [IPMI sensor statistics])
+AC_PLUGIN([iptables],            [$with_libiptc],             [IPTables rule counters])
+AC_PLUGIN([ipvs],                [$plugin_ipvs],              [IPVS connection statistics])
+AC_PLUGIN([irq],                 [$plugin_irq],               [IRQ statistics])
+AC_PLUGIN([java],                [$with_java],                [Embed the Java Virtual Machine])
+AC_PLUGIN([load],                [$plugin_load],              [System load])
+AC_PLUGIN([log_logstash],        [$plugin_log_logstash],      [Logstash json_event compatible logging])
+AC_PLUGIN([logfile],             [yes],                       [File logging plugin])
+AC_PLUGIN([lpar],                [$with_perfstat],            [AIX logical partitions statistics])
+AC_PLUGIN([lua],                 [$with_liblua],              [Lua plugin])
+AC_PLUGIN([lvm],                 [$with_liblvm2app],          [LVM statistics])
+AC_PLUGIN([madwifi],             [$have_linux_wireless_h],    [Madwifi wireless statistics])
+AC_PLUGIN([match_empty_counter], [yes],                       [The empty counter match])
+AC_PLUGIN([match_hashed],        [yes],                       [The hashed match])
+AC_PLUGIN([match_regex],         [yes],                       [The regex match])
+AC_PLUGIN([match_timediff],      [yes],                       [The timediff match])
+AC_PLUGIN([match_value],         [yes],                       [The value match])
+AC_PLUGIN([mbmon],               [yes],                       [Query mbmond])
+AC_PLUGIN([mcelog],              [$plugin_mcelog],            [Machine Check Exceptions notifications])
+AC_PLUGIN([md],                  [$have_linux_raid_md_u_h],   [md (Linux software RAID) devices])
+AC_PLUGIN([memcachec],           [$with_libmemcached],        [memcachec statistics])
+AC_PLUGIN([memcached],           [yes],                       [memcached statistics])
+AC_PLUGIN([memory],              [$plugin_memory],            [Memory usage])
+AC_PLUGIN([mic],                 [$with_mic],                 [Intel Many Integrated Core stats])
+AC_PLUGIN([modbus],              [$with_libmodbus],           [Modbus plugin])
+AC_PLUGIN([mqtt],                [$with_libmosquitto],        [MQTT output plugin])
+AC_PLUGIN([multimeter],          [$plugin_multimeter],        [Read multimeter values])
+AC_PLUGIN([mysql],               [$with_libmysql],            [MySQL statistics])
+AC_PLUGIN([netapp],              [$with_libnetapp],           [NetApp plugin])
+AC_PLUGIN([netlink],             [$with_libmnl],              [Enhanced Linux network statistics])
+AC_PLUGIN([network],             [yes],                       [Network communication plugin])
+AC_PLUGIN([nfs],                 [$plugin_nfs],               [NFS statistics])
+AC_PLUGIN([nginx],               [$with_libcurl],             [nginx statistics])
+AC_PLUGIN([notify_desktop],      [$with_libnotify],           [Desktop notifications])
+AC_PLUGIN([notify_email],        [$with_libesmtp],            [Email notifier])
+AC_PLUGIN([notify_nagios],       [yes],                       [Nagios notification plugin])
+AC_PLUGIN([ntpd],                [yes],                       [NTPd statistics])
+AC_PLUGIN([numa],                [$plugin_numa],              [NUMA virtual memory statistics])
+AC_PLUGIN([nut],                 [$with_libupsclient],        [Network UPS tools statistics])
+AC_PLUGIN([olsrd],               [yes],                       [olsrd statistics])
+AC_PLUGIN([onewire],             [$with_libowcapi],           [OneWire sensor statistics])
+AC_PLUGIN([openldap],            [$with_libldap],             [OpenLDAP statistics])
+AC_PLUGIN([openvpn],             [yes],                       [OpenVPN client statistics])
+AC_PLUGIN([oracle],              [$with_oracle],              [Oracle plugin])
+AC_PLUGIN([ovs_events],          [$plugin_ovs_events],        [OVS events plugin])
+AC_PLUGIN([ovs_stats],           [$plugin_ovs_stats],         [OVS statistics plugin])
+AC_PLUGIN([pcie_errors],         [$plugin_pcie_errors],       [PCIe errors plugin])
+AC_PLUGIN([perl],                [$plugin_perl],              [Embed a Perl interpreter])
+AC_PLUGIN([pf],                  [$have_net_pfvar_h],         [BSD packet filter (PF) statistics])
 # FIXME: Check for libevent, too.
-AC_PLUGIN([pinba],               [$plugin_pinba],           [Pinba statistics])
-AC_PLUGIN([ping],                [$with_liboping],          [Network latency statistics])
-AC_PLUGIN([postgresql],          [$with_libpq],             [PostgreSQL database statistics])
-AC_PLUGIN([powerdns],            [yes],                     [PowerDNS statistics])
-AC_PLUGIN([processes],           [$plugin_processes],       [Process statistics])
-AC_PLUGIN([protocols],           [$plugin_protocols],       [Protocol (IP, TCP, ...) statistics])
-AC_PLUGIN([python],              [$plugin_python],          [Embed a Python interpreter])
-AC_PLUGIN([redis],               [$with_libhiredis],        [Redis plugin])
-AC_PLUGIN([routeros],            [$with_librouteros],       [RouterOS plugin])
-AC_PLUGIN([rrdcached],           [$librrd_rrdc_update],     [RRDTool output plugin])
-AC_PLUGIN([rrdtool],             [$with_librrd],            [RRDTool output plugin])
-AC_PLUGIN([sensors],             [$with_libsensors],        [lm_sensors statistics])
-AC_PLUGIN([serial],              [$plugin_serial],          [serial port traffic])
-AC_PLUGIN([sigrok],              [$with_libsigrok],         [sigrok acquisition sources])
-AC_PLUGIN([smart],               [$plugin_smart],           [SMART statistics])
-AC_PLUGIN([snmp],                [$with_libnetsnmp],        [SNMP querying plugin])
-AC_PLUGIN([snmp_agent],          [$with_libnetsnmpagent],   [SNMP agent plugin])
-AC_PLUGIN([statsd],              [yes],                     [StatsD plugin])
-AC_PLUGIN([swap],                [$plugin_swap],            [Swap usage statistics])
-AC_PLUGIN([synproxy],            [$plugin_synproxy],        [Synproxy stats plugin])
-AC_PLUGIN([syslog],              [$have_syslog],            [Syslog logging plugin])
-AC_PLUGIN([table],               [yes],                     [Parsing of tabular data])
-AC_PLUGIN([tail],                [yes],                     [Parsing of logfiles])
-AC_PLUGIN([tail_csv],            [yes],                     [Parsing of CSV files])
-AC_PLUGIN([tape],                [$plugin_tape],            [Tape drive statistics])
-AC_PLUGIN([target_notification], [yes],                     [The notification target])
-AC_PLUGIN([target_replace],      [yes],                     [The replace target])
-AC_PLUGIN([target_scale],        [yes],                     [The scale target])
-AC_PLUGIN([target_set],          [yes],                     [The set target])
-AC_PLUGIN([target_v5upgrade],    [yes],                     [The v5upgrade target])
-AC_PLUGIN([tcpconns],            [$plugin_tcpconns],        [TCP connection statistics])
-AC_PLUGIN([teamspeak2],          [yes],                     [TeamSpeak2 server statistics])
-AC_PLUGIN([ted],                 [$plugin_ted],             [Read The Energy Detective values])
-AC_PLUGIN([thermal],             [$plugin_thermal],         [Linux ACPI thermal zone statistics])
-AC_PLUGIN([threshold],           [yes],                     [Threshold checking plugin])
-AC_PLUGIN([tokyotyrant],         [$with_libtokyotyrant],    [TokyoTyrant database statistics])
-AC_PLUGIN([turbostat],           [$plugin_turbostat],       [Advanced statistic on Intel cpu states])
-AC_PLUGIN([unixsock],            [yes],                     [Unixsock communication plugin])
-AC_PLUGIN([uptime],              [$plugin_uptime],          [Uptime statistics])
-AC_PLUGIN([users],               [$plugin_users],           [User statistics])
-AC_PLUGIN([uuid],                [yes],                     [UUID as hostname plugin])
-AC_PLUGIN([varnish],             [$with_libvarnish],        [Varnish cache statistics])
-AC_PLUGIN([virt],                [$plugin_virt],            [Virtual machine statistics])
-AC_PLUGIN([vmem],                [$plugin_vmem],            [Virtual memory statistics])
-AC_PLUGIN([vserver],             [$plugin_vserver],         [Linux VServer statistics])
-AC_PLUGIN([wireless],            [$plugin_wireless],        [Wireless statistics])
-AC_PLUGIN([write_graphite],      [yes],                     [Graphite / Carbon output plugin])
-AC_PLUGIN([write_http],          [$with_libcurl],           [HTTP output plugin])
-AC_PLUGIN([write_kafka],         [$with_librdkafka],        [Kafka output plugin])
-AC_PLUGIN([write_log],           [yes],                     [Log output plugin])
-AC_PLUGIN([write_mongodb],       [$with_libmongoc],         [MongoDB output plugin])
-AC_PLUGIN([write_prometheus],    [$plugin_write_prometheus], [Prometheus write plugin])
-AC_PLUGIN([write_redis],         [$with_libhiredis],        [Redis output plugin])
-AC_PLUGIN([write_riemann],       [$with_libriemann_client], [Riemann output plugin])
-AC_PLUGIN([write_sensu],         [yes],                     [Sensu output plugin])
-AC_PLUGIN([write_tsdb],          [yes],                     [TSDB output plugin])
-AC_PLUGIN([xencpu],              [$plugin_xencpu],          [Xen Host CPU usage])
-AC_PLUGIN([xmms],                [$with_libxmms],           [XMMS statistics])
-AC_PLUGIN([zfs_arc],             [$plugin_zfs_arc],         [ZFS ARC statistics])
-AC_PLUGIN([zone],                [$plugin_zone],            [Solaris container statistics])
-AC_PLUGIN([zookeeper],           [yes],                     [Zookeeper statistics])
+AC_PLUGIN([pinba],               [$plugin_pinba],             [Pinba statistics])
+AC_PLUGIN([ping],                [$with_liboping],            [Network latency statistics])
+AC_PLUGIN([postgresql],          [$with_libpq],               [PostgreSQL database statistics])
+AC_PLUGIN([powerdns],            [yes],                       [PowerDNS statistics])
+AC_PLUGIN([processes],           [$plugin_processes],         [Process statistics])
+AC_PLUGIN([protocols],           [$plugin_protocols],         [Protocol (IP, TCP, ...) statistics])
+AC_PLUGIN([python],              [$plugin_python],            [Embed a Python interpreter])
+AC_PLUGIN([redis],               [$with_libhiredis],          [Redis plugin])
+AC_PLUGIN([routeros],            [$with_librouteros],         [RouterOS plugin])
+AC_PLUGIN([rrdcached],           [$librrd_rrdc_update],       [RRDTool output plugin])
+AC_PLUGIN([rrdtool],             [$with_librrd],              [RRDTool output plugin])
+AC_PLUGIN([sensors],             [$with_libsensors],          [lm_sensors statistics])
+AC_PLUGIN([serial],              [$plugin_serial],            [serial port traffic])
+AC_PLUGIN([sigrok],              [$with_libsigrok],           [sigrok acquisition sources])
+AC_PLUGIN([smart],               [$plugin_smart],             [SMART statistics])
+AC_PLUGIN([snmp],                [$with_libnetsnmp],          [SNMP querying plugin])
+AC_PLUGIN([snmp_agent],          [$with_libnetsnmpagent],     [SNMP agent plugin])
+AC_PLUGIN([statsd],              [yes],                       [StatsD plugin])
+AC_PLUGIN([swap],                [$plugin_swap],              [Swap usage statistics])
+AC_PLUGIN([synproxy],            [$plugin_synproxy],          [Synproxy stats plugin])
+AC_PLUGIN([syslog],              [$have_syslog],              [Syslog logging plugin])
+AC_PLUGIN([table],               [yes],                       [Parsing of tabular data])
+AC_PLUGIN([tail],                [yes],                       [Parsing of logfiles])
+AC_PLUGIN([tail_csv],            [yes],                       [Parsing of CSV files])
+AC_PLUGIN([tape],                [$plugin_tape],              [Tape drive statistics])
+AC_PLUGIN([target_notification], [yes],                       [The notification target])
+AC_PLUGIN([target_replace],      [yes],                       [The replace target])
+AC_PLUGIN([target_scale],        [yes],                       [The scale target])
+AC_PLUGIN([target_set],          [yes],                       [The set target])
+AC_PLUGIN([target_v5upgrade],    [yes],                       [The v5upgrade target])
+AC_PLUGIN([tcpconns],            [$plugin_tcpconns],          [TCP connection statistics])
+AC_PLUGIN([teamspeak2],          [yes],                       [TeamSpeak2 server statistics])
+AC_PLUGIN([ted],                 [$plugin_ted],               [Read The Energy Detective values])
+AC_PLUGIN([thermal],             [$plugin_thermal],           [Linux ACPI thermal zone statistics])
+AC_PLUGIN([threshold],           [yes],                       [Threshold checking plugin])
+AC_PLUGIN([tokyotyrant],         [$with_libtokyotyrant],      [TokyoTyrant database statistics])
+AC_PLUGIN([turbostat],           [$plugin_turbostat],         [Advanced statistic on Intel cpu states])
+AC_PLUGIN([unixsock],            [yes],                       [Unixsock communication plugin])
+AC_PLUGIN([uptime],              [$plugin_uptime],            [Uptime statistics])
+AC_PLUGIN([users],               [$plugin_users],             [User statistics])
+AC_PLUGIN([uuid],                [yes],                       [UUID as hostname plugin])
+AC_PLUGIN([varnish],             [$with_libvarnish],          [Varnish cache statistics])
+AC_PLUGIN([virt],                [$plugin_virt],              [Virtual machine statistics])
+AC_PLUGIN([vmem],                [$plugin_vmem],              [Virtual memory statistics])
+AC_PLUGIN([vserver],             [$plugin_vserver],           [Linux VServer statistics])
+AC_PLUGIN([wireless],            [$plugin_wireless],          [Wireless statistics])
+AC_PLUGIN([write_graphite],      [yes],                       [Graphite / Carbon output plugin])
+AC_PLUGIN([write_http],          [$with_libcurl],             [HTTP output plugin])
+AC_PLUGIN([write_stackdriver],   [$plugin_write_stackdriver], [Google Stackdriver Monitoring output plugin])
+AC_PLUGIN([write_kafka],         [$with_librdkafka],          [Kafka output plugin])
+AC_PLUGIN([write_log],           [yes],                       [Log output plugin])
+AC_PLUGIN([write_mongodb],       [$with_libmongoc],           [MongoDB output plugin])
+AC_PLUGIN([write_prometheus],    [$plugin_write_prometheus],  [Prometheus write plugin])
+AC_PLUGIN([write_redis],         [$with_libhiredis],          [Redis output plugin])
+AC_PLUGIN([write_riemann],       [$with_libriemann_client],   [Riemann output plugin])
+AC_PLUGIN([write_sensu],         [yes],                       [Sensu output plugin])
+AC_PLUGIN([write_tsdb],          [yes],                       [TSDB output plugin])
+AC_PLUGIN([xencpu],              [$plugin_xencpu],            [Xen Host CPU usage])
+AC_PLUGIN([xmms],                [$with_libxmms],             [XMMS statistics])
+AC_PLUGIN([zfs_arc],             [$plugin_zfs_arc],           [ZFS ARC statistics])
+AC_PLUGIN([zone],                [$plugin_zone],              [Solaris container statistics])
+AC_PLUGIN([zookeeper],           [yes],                       [Zookeeper statistics])
 
 dnl Default configuration file
 # Load either syslog or logfile
@@ -6891,6 +7161,7 @@ AC_MSG_RESULT([    libnetapp . . . . . . $with_libnetapp])
 AC_MSG_RESULT([    libnetsnmp  . . . . . $with_libnetsnmp])
 AC_MSG_RESULT([    libnetsnmpagent . . . $with_libnetsnmpagent])
 AC_MSG_RESULT([    libnotify . . . . . . $with_libnotify])
+AC_MSG_RESULT([    libnvidia-ml  . . . . $with_cuda])
 AC_MSG_RESULT([    libopenipmi . . . . . $with_libopenipmipthread])
 AC_MSG_RESULT([    liboping  . . . . . . $with_liboping])
 AC_MSG_RESULT([    libowcapi . . . . . . $with_libowcapi])
@@ -6902,6 +7173,7 @@ AC_MSG_RESULT([    libpqos . . . . . . . $with_libpqos])
 AC_MSG_RESULT([    libprotobuf . . . . . $with_libprotobuf])
 AC_MSG_RESULT([    libprotobuf-c . . . . $with_libprotobuf_c])
 AC_MSG_RESULT([    libpython . . . . . . $with_libpython])
+AC_MSG_RESULT([    libqpid-proton .  . . $with_libqpid_proton])
 AC_MSG_RESULT([    librabbitmq . . . . . $with_librabbitmq])
 AC_MSG_RESULT([    libriemann-client . . $with_libriemann_client])
 AC_MSG_RESULT([    librdkafka  . . . . . $with_librdkafka])
@@ -6909,6 +7181,7 @@ AC_MSG_RESULT([    librouteros . . . . . $with_librouteros])
 AC_MSG_RESULT([    librrd  . . . . . . . $with_librrd])
 AC_MSG_RESULT([    libsensors  . . . . . $with_libsensors])
 AC_MSG_RESULT([    libsigrok   . . . . . $with_libsigrok])
+AC_MSG_RESULT([    libssl  . . . . . . . $with_libssl])
 AC_MSG_RESULT([    libstatgrab . . . . . $with_libstatgrab])
 AC_MSG_RESULT([    libtokyotyrant  . . . $with_libtokyotyrant])
 AC_MSG_RESULT([    libudev . . . . . . . $with_libudev])
@@ -6933,6 +7206,7 @@ AC_MSG_RESULT()
 AC_MSG_RESULT([  Modules:])
 AC_MSG_RESULT([    aggregation . . . . . $enable_aggregation])
 AC_MSG_RESULT([    amqp    . . . . . . . $enable_amqp])
+AC_MSG_RESULT([    amqp1   . . . . . . . $enable_amqp1])
 AC_MSG_RESULT([    apache  . . . . . . . $enable_apache])
 AC_MSG_RESULT([    apcups  . . . . . . . $enable_apcups])
 AC_MSG_RESULT([    apple_sensors . . . . $enable_apple_sensors])
@@ -6969,6 +7243,7 @@ AC_MSG_RESULT([    filecount . . . . . . $enable_filecount])
 AC_MSG_RESULT([    fscache . . . . . . . $enable_fscache])
 AC_MSG_RESULT([    gmond . . . . . . . . $enable_gmond])
 AC_MSG_RESULT([    gps . . . . . . . . . $enable_gps])
+AC_MSG_RESULT([    gpu_nvidia  . . . . . $enable_gpu_nvidia])
 AC_MSG_RESULT([    grpc  . . . . . . . . $enable_grpc])
 AC_MSG_RESULT([    hddtemp . . . . . . . $enable_hddtemp])
 AC_MSG_RESULT([    hugepages . . . . . . $enable_hugepages])
@@ -7022,6 +7297,7 @@ AC_MSG_RESULT([    openvpn . . . . . . . $enable_openvpn])
 AC_MSG_RESULT([    oracle  . . . . . . . $enable_oracle])
 AC_MSG_RESULT([    ovs_events  . . . . . $enable_ovs_events])
 AC_MSG_RESULT([    ovs_stats . . . . . . $enable_ovs_stats])
+AC_MSG_RESULT([    pcie_errors . . . . . $enable_pcie_errors])
 AC_MSG_RESULT([    perl  . . . . . . . . $enable_perl])
 AC_MSG_RESULT([    pf  . . . . . . . . . $enable_pf])
 AC_MSG_RESULT([    pinba . . . . . . . . $enable_pinba])
@@ -7079,6 +7355,7 @@ AC_MSG_RESULT([    write_prometheus. . . $enable_write_prometheus])
 AC_MSG_RESULT([    write_redis . . . . . $enable_write_redis])
 AC_MSG_RESULT([    write_riemann . . . . $enable_write_riemann])
 AC_MSG_RESULT([    write_sensu . . . . . $enable_write_sensu])
+AC_MSG_RESULT([    write_stackdriver . . $enable_write_stackdriver])
 AC_MSG_RESULT([    write_tsdb  . . . . . $enable_write_tsdb])
 AC_MSG_RESULT([    xencpu  . . . . . . . $enable_xencpu])
 AC_MSG_RESULT([    xmms  . . . . . . . . $enable_xmms])
@@ -7096,3 +7373,4 @@ if test "x$dependency_warning" = "xyes"; then
 fi
 
 # vim: set fdm=marker sw=2 sts=2 ts=2 et :
+
index 2b3f2fe..ba189de 100755 (executable)
@@ -140,10 +140,10 @@ sub main
   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)
index 3e19bfc..5d5e4eb 100644 (file)
@@ -618,6 +618,13 @@ GraphWidth 400
   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})"
index 8c12951..9c3f0ed 100644 (file)
@@ -1,3 +1,12 @@
+form {
+  display: flex;
+  margin-bottom: 10px;
+}
+
+fieldset {
+  margin-left: 0;
+}
+
 div.graph
 {
 }
index 3f898b3..43b0ec9 100644 (file)
@@ -1,4 +1,4 @@
-APT::Install-Recommends "0";
-APT::Install-Suggests "0";
+APT::Install-Recommends "1";
+APT::Install-Suggests "1";
 APT::Get::Assume-Yes "1";
 APT::Get::AutomaticRemove "1";
index a691f2e..fcb46b1 100644 (file)
@@ -1,4 +1,4 @@
-FROM debian:stable
+FROM debian:stable-slim
 
 ENV DEBIAN_FRONTEND noninteractive
 COPY 50docker-apt-conf /etc/apt/apt.conf.d/
@@ -21,4 +21,5 @@ COPY collectd.conf.d /etc/collectd/collectd.conf.d
 
 ENV LD_PRELOAD /usr/src/rootfs_prefix/rootfs_prefix.so
 
-CMD [ "/usr/sbin/collectd", "-f"]
+ENTRYPOINT ["/usr/sbin/collectd"]
+CMD ["-f"]
index 2b83151..fdacf74 100644 (file)
@@ -1,3 +1,33 @@
+/**
+ * collectd - contrib/docker/rootfs_prefix/rootfs_prefix.c
+ * Copyright (C) 2016-2018  Marc Fournier
+ * Copyright (C) 2016-2018  Ruben Kerkhof
+ *
+ * MIT License:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Marc Fournier <marc.fournier at camptocamp.com>
+ *   Ruben Kerkhof <ruben at rubenkerkhof.com>
+ **/
+
 #define _GNU_SOURCE
 
 #include <dirent.h>
index 3e62ce0..5309cc6 100755 (executable)
@@ -56,7 +56,7 @@ exit (0);
 
 =head1 CONFIGURATION
 
-This script reads it's configuration from F</etc/exec-munin.conf>. The
+This script reads its configuration from F</etc/exec-munin.conf>. The
 configuration is read using C<Config::General> which understands a Apache-like
 config syntax, so it's very similar to the F<collectd.conf> syntax, too.
 
index ec13b0a..b9758ec 100755 (executable)
@@ -36,7 +36,7 @@ exit (0);
 
 =head1 CONFIGURATION
 
-This script reads it's configuration from F</etc/exec-nagios.conf>. The
+This script reads its configuration from F</etc/exec-nagios.conf>. The
 configuration is read using C<Config::General> which understands a Apache-like
 config syntax, so it's very similar to the F<collectd.conf> syntax, too.
 
index fa2badc..c063d57 100644 (file)
@@ -536,7 +536,7 @@ function rrd_get_color($code, $line = true) {
 }
 
 /**
- * Draw RRD file based on it's structure
+ * Draw RRD file based on its structure
  * @host
  * @plugin
  * @pinst
@@ -635,7 +635,7 @@ function collectd_draw_rrd($host, $plugin, $pinst = null, $type, $tinst = null,
 }
 
 /**
- * Draw RRD file based on it's structure
+ * Draw RRD file based on its structure
  * @timespan
  * @host
  * @plugin
index d84b457..6f86b7e 100644 (file)
 %global _hardened_build 1
 %{?perl_default_filter}
 
+# disable collectd debug by default
+%bcond_with debug
+
 # plugins enabled by default
 %define with_aggregation 0%{!?_without_aggregation:1}
 %define with_amqp 0%{!?_without_amqp:1}
+%define with_amqp1 0%{!?_without_amqp1:1}
 %define with_apache 0%{!?_without_apache:1}
 %define with_apcups 0%{!?_without_apcups:1}
 %define with_ascent 0%{!?_without_ascent:1}
 Summary:       Statistics collection and monitoring daemon
 Name:          collectd
 Version:       5.7.1
-Release:       8%{?dist}
+Release:       9%{?dist}
 URL:           https://collectd.org
 Source:                https://collectd.org/files/%{name}-%{version}.tar.bz2
 License:       GPLv2
@@ -277,13 +281,24 @@ every 10 seconds by default.
 
 %if %{with_amqp}
 %package amqp
-Summary:       AMQP plugin for collectd
+Summary:       AMQP 0.9 plugin for collectd
 Group:         System Environment/Daemons
 Requires:      %{name}%{?_isa} = %{version}-%{release}
 BuildRequires: librabbitmq-devel
 %description amqp
-The AMQP plugin transmits or receives values collected by collectd via the
-Advanced Message Queuing Protocol (AMQP).
+The AMQP 0.9 plugin transmits or receives values collected by collectd via the
+Advanced Message Queuing Protocol v0.9 (AMQP).
+%endif
+
+%if %{with_amqp1}
+%package amqp1
+Summary:       AMQP 1.0 plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: qpid-proton-c-devel
+%description amqp1
+The AMQP 1.0 plugin transmits or receives values collected by collectd via the
+Advanced Message Queuing Protocol v1.0 (AMQP1).
 %endif
 
 %if %{with_apache}
@@ -1015,6 +1030,12 @@ Collectd utilities
 %define _with_amqp --disable-amqp
 %endif
 
+%if %{with_amqp1}
+%define _with_amqp1 --enable-amqp1
+%else
+%define _with_amqp1 --disable-amqp1
+%endif
+
 %if %{with_apache}
 %define _with_apache --enable-apache
 %else
@@ -1360,7 +1381,7 @@ Collectd utilities
 %if %{with_mcelog}
 %define _with_mcelog --enable-mcelog
 %else
-%define _with_mbmon --disable-mcelog
+%define _with_mcelog --disable-mcelog
 %endif
 
 %if %{with_md}
@@ -1872,8 +1893,15 @@ Collectd utilities
 %define _with_zookeeper --disable-zookeeper
 %endif
 
+%if %{with debug}
+%define _feature_debug --enable-debug
+%else
+%define _feature_debug --disable-debug
+%endif
+
 %configure CFLAGS="%{optflags} -DLT_LAZY_OR_NOW=\"RTLD_LAZY|RTLD_GLOBAL\"" \
        %{?_python_config} \
+       %{?_feature_debug} \
        --disable-static \
        --enable-all-plugins=yes \
        --enable-match_empty_counter \
@@ -1888,6 +1916,7 @@ Collectd utilities
        --enable-target_v5upgrade \
        %{?_with_aggregation} \
        %{?_with_amqp} \
+       %{?_with_amqp1} \
        %{?_with_apache} \
        %{?_with_apcups} \
        %{?_with_apple_sensors} \
@@ -2399,6 +2428,11 @@ fi
 %{_libdir}/%{name}/amqp.so
 %endif
 
+%if %{with_amqp1}
+%files amqp1
+%{_libdir}/%{name}/amqp1.so
+%endif
+
 %if %{with_apache}
 %files apache
 %{_libdir}/%{name}/apache.so
@@ -2737,6 +2771,9 @@ fi
 %doc contrib/
 
 %changelog
+* Thu Sep 28 2017 Jakub Jankowski <shasta@toxcorp.com> - 5.7.1-9
+- Fix mbmon/mcelog build options
+
 * Thu Sep 28 2017 xakru <calvinxakru@gmail.com> - 5.7.1-8
 - Add new libcollectdclient/network_parse
 - Add new libcollectdclient/server
index 2d558bd..82d709a 100644 (file)
@@ -14,7 +14,7 @@ Vendor:               Florian octo Forster <octo@verplant.org>
 
 %description
 collectd is a small daemon written in C for performance.  It reads various
-system  statistics  and updates  RRD files,  creating  them if neccessary.
+system  statistics  and updates  RRD files,  creating  them if necessary.
 Since the daemon doesn't need to startup every time it wants to update the
 files it's very fast and easy on the system. Also, the statistics are very
 fine grained since the files are updated every 10 seconds.
index d1a7a88..9776af6 100755 (executable)
@@ -306,9 +306,9 @@ snmp-probe-host.px - Find out what information an SNMP device provides.
 The C<snmp-probe-host.px> script can be used to automatically generate SNMP
 configuration snippets for collectd's snmp plugin (see L<collectd-snmp(5)>).
 
-This script parses the collectd configuration and detecs all "data" blocks that
+This script parses the collectd configuration and detects all "data" blocks that
 are defined for the SNMP plugin. It then queries the device specified on the
-command line for all OIDs and registeres which OIDs could be answered correctly
+command line for all OIDs and registers which OIDs could be answered correctly
 and which resulted in an error. With that information the script figures out
 which "data" blocks can be used with this hosts and prints an appropriate
 "host" block to standard output.
index 9c037a4..c5b1142 100644 (file)
@@ -22,6 +22,7 @@ ProtectHome=true
 #   intel_pmu       CAP_SYS_ADMIN
 #   iptables        CAP_NET_ADMIN
 #   ping            CAP_NET_RAW
+#   processes       CAP_NET_ADMIN  (CollectDelayAccounting only)
 #   smart           CAP_SYS_RAWIO
 #   turbostat       CAP_SYS_RAWIO
 #
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..ca0f40f
--- /dev/null
@@ -0,0 +1,85 @@
+# Contribution guidelines
+
+Thanks for taking the time to contribute to the [collectd
+project](https://collectd.org/)! This document tries to give some guidance to
+make the process of contributing to *collectd* as pleasant as possible.
+
+## Bug reports
+
+Please report bugs as [GitHub
+Issues](https://github.com/collectd/collectd/issues). Try to answer the
+following questions:
+
+*   Which version of *collectd* are you using?
+*   Which operating system (distribution) are you using at which version?
+*   What is the expected behavior / output?
+*   What is the actual (observed) behavior / output?
+*   How can we reproduce the problem you're having?
+*   If *collectd* crashes, try to get a
+    [stack trace](https://collectd.org/wiki/index.php/Core_file).
+
+Please monitor your issue for a couple of days and reply to questions. To keep
+the project manageable, we have to do some housekeeping; meaning we will close
+issues that have become stale.
+
+## Code contributions
+
+Please open a [GitHub Pull Request](https://github.com/collectd/collectd/pulls)
+(PR) to contribute bug fixes, features, cleanups, new plugins, … Patches sent to
+the mailing list have a tendency to fall through the cracks.
+
+*   *Focus:* Fix *one thing* in your PR. The smaller your change, the faster it
+    will be reviewed and merged.
+*   *Coding style:* Please run `clang-format -style=file -i $FILE` after editing
+    `.c`, `.h` and `.proto` files. If you don't want to install *clang-format*
+    locally or your version produces a different result than the formatting
+    check on Github, use `contrib/format.sh` to format files using the same web
+    service used by our check.
+*   *Documentation:* New config options need to be documented in two places: the
+    manpage (`src/collectd.conf.pod`) and the example config
+    (`src/collectd.conf.in`). New plugins need to be added to the `README` file.
+*   *Continuous integration:* Once your PR is created, our continuous
+    integration environment will try to build it on a number of platforms. If
+    this reports a failure, please investigate and fix the problem. We will at
+    best do a very casual review for failing PRs.
+*   *Don't rebase:* Rebasing your branch destroys the review history. If a
+    review takes a long time, we may ask you to rebase on a more recent
+    *master*, but please don't do that without being asked.
+*   *types.db:* One of the most common mistakes made by new contributors is the
+    addition of (many) new *types* in the file `src/types.db`. The majority of
+    usecases can be met with one of the existing entries. If you plan to add new
+    entries to `src/types.db`, you should talk to us early in the design
+    process.
+
+### ChangeLog
+
+PRs need to have a one-line summary in the *PR description*. This information
+is used to automatically generate release notes. If you got here after creating
+the PR, you need to go to the *PR description* (shown as the first "comment" on
+the PR, made by yourself) and *edit* that description. Editing a PR will
+trigger the "ChangeLog" status to be updated.
+
+For the summary itself, follow this style:
+
+```
+ChangeLog: Foo plugin: A specific issue people had has been fixed.
+```
+
+The summary must be on a line of its own, with a "ChangeLog:" prefix at the
+beginning of the line. The text should start with "Foo plugin" to give the
+reader context for the information. Other common contexts are "collectd" for
+the core daemon, "Build system", and "Documentation". Use past tense and
+passive voice the for remainder, e.g. "a bug has been fixed", "a feature has
+been added".
+
+Some PRs should be excluded from the release notes, e.g. changes to project
+internal documentation (such as this file). Those changes are not interesting
+for external users of the project and would reduce the value of the release
+notes. Maintainers may use the `Unlisted Change` label to mark those PRs.
+
+## Other resources
+
+*   [Mailing list](http://mailman.verplant.org/listinfo/collectd)
+*   [#collectd IRC channel](https://webchat.freenode.net/?channels=#collectd)
+    on *freenode*.
+*   [Old patch submission guideline](https://collectd.org/wiki/index.php/Submitting_patches)
index a80e9ea..9a63a18 100644 (file)
@@ -43,7 +43,7 @@ Collectd will just use the domain tags, but never enforces or requires them.
 It is up to an external entity, like a software management system,
 to attach and manage the tags to the domain.
 
-Please note that unless you have such tag-aware management sofware,
+Please note that unless you have such tag-aware management software,
 it most likely make no sense to enable more than one reader instance on your
 setup.
 
@@ -179,8 +179,8 @@ API, but it is rather a byproduct of how libvirt and QEMU interact.
 
 Whenever we query more than one VM, we should take care to avoid that one blocked VM prevent other,
 well behaving VMs to be queried. We don't want one rogue VM to disrupt well-behaving VMs.
-Unfortunately, any way we enumerate VMs, either implicitely, using the libvirt bulk stats API,
-or explicitely, listing all libvirt domains and query each one in turn, we may unpredictably encounter
+Unfortunately, any way we enumerate VMs, either implicitly, using the libvirt bulk stats API,
+or explicitly, listing all libvirt domains and query each one in turn, we may unpredictably encounter
 one unresponsive VM.
 
 There are many possible approaches to deal with this issue. The virt plugin supports
@@ -237,4 +237,3 @@ The QEMU core, including the handling of the QMP protocol, is single-threaded.
 All the above combined make it possible for a client to block forever waiting for one QMP
 request, if QEMU itself is blocked. The most likely cause of block is I/O, and this is especially
 true considering how QEMU is used in a datacenter.
-
diff --git a/docs/review_comments.md b/docs/review_comments.md
new file mode 100644 (file)
index 0000000..9bad458
--- /dev/null
@@ -0,0 +1,96 @@
+# Code Review Comments
+
+This is a collection of frequent code review comments, collected here for
+reference and discussed in more depth than a typical code review would allow.
+
+The intended use for this document is to point to it from a code review to make
+a point quickly while still providing the contributor with enough information
+to resolve the issue. For example, a good review comment would be:
+
+![Please initialize variables at declaration. Link to comment.](review_comments_example.png)
+
+A link to each paragraph is provided at the beginning for easy copy'n'pasting.
+
+## Initialize variables
+
+→ [https://collectd.org/review-comments#initialize-variables](https://collectd.org/review-comments#initialize-variables)
+
+Initialize variables when declaring them. By default, C does not initialize
+local variables when they are defined. If a code path ends up reading the
+variable before it is initialized, for example because a loop body is never
+executed, it will read random data, causing undefined behavior. Worst case,
+pointers will point to random memory causing a segmentation fault.
+
+**Examples:**
+
+```c
+/* Initialize scalar with to literal: */
+int status = 0;
+
+/* Initialize pointer with function call: */
+char *buffer = calloc(1, buffer_size);
+
+/* Initialize struct with struct initializer: */
+struct addrinfo ai = {
+  .ai_family = AF_UNSPEC,
+  .ai_flags = AI_ADDRCONFIG,
+  .ai_socktype = SOCK_STREAM,
+};
+
+/* Initialize struct with zero: */
+struct stat statbuf = {0};
+```
+
+In the last example, `{0}` is the universal struct initializer that, in theory,
+should be able to zero-initialize any struct. In practise, however, some
+compilers don't implement this correctly and will get confused when the first
+member is a struct or a union. Our *continuous integration* framework will
+catch these cases.
+
+## Define variables on first use
+
+→ [https://collectd.org/review-comments#define-variables-on-first-use](https://collectd.org/review-comments#define-variables-on-first-use)
+
+Local variables should be defined when they are first used, ideally when they
+can be initialized. For example:
+
+```c
+struct foo *f = calloc(1, sizeof(*f));
+if (f == NULL) {
+  return ENOMEM;
+}
+
+/* GOOD: status defiened and initialized late. */
+int status = function_call(f);
+```
+
+Sometimes variables are initialized by passing a pointer to them to a function.
+In that case define them as close to the function call as you can and
+zero-initialize them. The function may only partially initialize a struct or
+not initialize a struct at all in some circumstances.
+
+**Example:**
+
+```c
+char const *path = determine_path();
+
+struct stat s = {0};
+int status = stat(path, &s);
+```
+
+Old C standards (C89 and ealier) required variables to be defined at the
+beginning of a scope block. The following *bad* style is still frequently
+found:
+
+```c
+/* BAD: local variables defined at beginning of block. */
+struct foo *f;
+int status;
+
+f = calloc(1, sizeof(*f));
+if (f == NULL) {
+  return ENOMEM;
+}
+
+status = function_call(f);
+```
diff --git a/docs/review_comments_example.png b/docs/review_comments_example.png
new file mode 100644 (file)
index 0000000..81eb458
Binary files /dev/null and b/docs/review_comments_example.png differ
diff --git a/gnulib b/gnulib
new file mode 160000 (submodule)
index 0000000..2f8140b
--- /dev/null
+++ b/gnulib
@@ -0,0 +1 @@
+Subproject commit 2f8140bc8ce5501e31dcc665b42b5df64f84c20c
index 0ed97ae..089ff1d 100644 (file)
 
 #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}"
@@ -48,12 +48,12 @@ struct aggregation_s /* {{{ */
   char *set_plugin_instance;
   char *set_type_instance;
 
-  _Bool calc_num;
-  _Bool calc_sum;
-  _Bool calc_average;
-  _Bool calc_min;
-  _Bool calc_max;
-  _Bool calc_stddev;
+  bool calc_num;
+  bool calc_sum;
+  bool calc_average;
+  bool calc_min;
+  bool calc_max;
+  bool calc_stddev;
 }; /* }}} */
 typedef struct aggregation_s aggregation_t;
 
@@ -83,27 +83,25 @@ struct agg_instance_s /* {{{ */
   agg_instance_t *next;
 }; /* }}} */
 
-static lookup_t *lookup = NULL;
+static lookup_t *lookup;
 
 static pthread_mutex_t agg_instance_list_lock = PTHREAD_MUTEX_INITIALIZER;
-static agg_instance_t *agg_instance_list_head = NULL;
+static agg_instance_t *agg_instance_list_head;
 
-static _Bool agg_is_regex(char const *str) /* {{{ */
+static bool agg_is_regex(char const *str) /* {{{ */
 {
-  size_t len;
-
   if (str == NULL)
-    return 0;
+    return false;
 
-  len = strlen(str);
+  size_t len = strlen(str);
   if (len < 3)
-    return 0;
+    return false;
 
   if ((str[0] == '/') && (str[len - 1] == '/'))
-    return 1;
+    return true;
   else
-    return 0;
-} /* }}} _Bool agg_is_regex */
+    return false;
+} /* }}} bool agg_is_regex */
 
 static void agg_destroy(aggregation_t *agg) /* {{{ */
 {
@@ -227,11 +225,9 @@ static int agg_instance_create_name(agg_instance_t *inst, /* {{{ */
 static agg_instance_t *agg_instance_create(data_set_t const *ds, /* {{{ */
                                            value_list_t const *vl,
                                            aggregation_t *agg) {
-  agg_instance_t *inst;
-
   DEBUG("aggregation plugin: Creating new instance.");
 
-  inst = calloc(1, sizeof(*inst));
+  agg_instance_t *inst = calloc(1, sizeof(*inst));
   if (inst == NULL) {
     ERROR("aggregation plugin: calloc() failed.");
     return NULL;
@@ -282,8 +278,6 @@ static agg_instance_t *agg_instance_create(data_set_t const *ds, /* {{{ */
  * and non-zero otherwise. */
 static int agg_instance_update(agg_instance_t *inst, /* {{{ */
                                data_set_t const *ds, value_list_t const *vl) {
-  gauge_t *rate;
-
   if (ds->ds_num != 1) {
     ERROR("aggregation plugin: The \"%s\" type (data set) has more than one "
           "data source. This is currently not supported by this plugin. "
@@ -292,7 +286,7 @@ static int agg_instance_update(agg_instance_t *inst, /* {{{ */
     return EINVAL;
   }
 
-  rate = uc_get_rate(ds, vl);
+  gauge_t *rate = uc_get_rate(ds, vl);
   if (rate == NULL) {
     char ident[6 * DATA_MAX_NAME_LEN];
     FORMAT_VL(ident, sizeof(ident), vl);
@@ -328,16 +322,15 @@ static int agg_instance_read_func(agg_instance_t *inst, /* {{{ */
                                   rate_to_value_state_t *state,
                                   value_list_t *vl, char const *pi_prefix,
                                   cdtime_t t) {
-  value_t v;
-  int status;
-
   if (pi_prefix[0] != 0)
     subst_string(vl->plugin_instance, sizeof(vl->plugin_instance), pi_prefix,
                  AGG_FUNC_PLACEHOLDER, func);
   else
     sstrncpy(vl->plugin_instance, func, sizeof(vl->plugin_instance));
 
-  status = rate_to_value(&v, rate, state, inst->ds_type, t);
+  value_t v;
+
+  int status = rate_to_value(&v, rate, state, inst->ds_type, t);
   if (status != 0) {
     /* If this is the first iteration and rate_to_value() was asked to return a
      * COUNTER or a DERIVE, it will return EAGAIN. Catch this and handle
@@ -473,8 +466,6 @@ static void agg_lookup_free_obj_callback(void *user_obj) /* {{{ */
 static int agg_config_handle_group_by(oconfig_item_t const *ci, /* {{{ */
                                       aggregation_t *agg) {
   for (int i = 0; i < ci->values_num; i++) {
-    char const *value;
-
     if (ci->values[i].type != OCONFIG_TYPE_STRING) {
       ERROR("aggregation plugin: Argument %i of the \"GroupBy\" option "
             "is not a string.",
@@ -482,7 +473,7 @@ static int agg_config_handle_group_by(oconfig_item_t const *ci, /* {{{ */
       continue;
     }
 
-    value = ci->values[i].value.string;
+    const char *value = ci->values[i].value.string;
 
     if (strcasecmp("Host", value) == 0)
       agg->group_by |= LU_GROUP_BY_HOST;
@@ -580,7 +571,7 @@ static int agg_config_aggregation(oconfig_item_t *ci) /* {{{ */
     agg->regex_fields |= LU_GROUP_BY_TYPE_INSTANCE;
 
   /* Sanity checking */
-  _Bool is_valid = 1;
+  bool is_valid = true;
   if (strcmp("/.*/", agg->ident.type) == 0) /* {{{ */
   {
     ERROR("aggregation plugin: It appears you did not specify the required "
@@ -589,13 +580,13 @@ static int agg_config_aggregation(oconfig_item_t *ci) /* {{{ */
           "Type \"%s\", TypeInstance \"%s\")",
           agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
           agg->ident.type, agg->ident.type_instance);
-    is_valid = 0;
+    is_valid = false;
   } else if (strchr(agg->ident.type, '/') != NULL) {
     ERROR("aggregation plugin: The \"Type\" may not contain the '/' "
           "character. Especially, it may not be a regex. The current "
           "value is \"%s\".",
           agg->ident.type);
-    is_valid = 0;
+    is_valid = false;
   } /* }}} */
 
   /* Check that there is at least one regex field without a grouping. {{{ */
@@ -608,7 +599,7 @@ static int agg_config_aggregation(oconfig_item_t *ci) /* {{{ */
           "Type \"%s\", TypeInstance \"%s\")",
           agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
           agg->ident.type, agg->ident.type_instance);
-    is_valid = 0;
+    is_valid = false;
   } /* }}} */
 
   /* Check that all grouping fields are regular expressions. {{{ */
@@ -620,7 +611,7 @@ static int agg_config_aggregation(oconfig_item_t *ci) /* {{{ */
           "Type \"%s\", TypeInstance \"%s\")",
           agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
           agg->ident.type, agg->ident.type_instance);
-    is_valid = 0;
+    is_valid = false;
   } /* }}} */
 
   if (!agg->calc_num && !agg->calc_sum && !agg->calc_average /* {{{ */
@@ -631,7 +622,7 @@ static int agg_config_aggregation(oconfig_item_t *ci) /* {{{ */
           "Type \"%s\", TypeInstance \"%s\")",
           agg->ident.host, agg->ident.plugin, agg->ident.plugin_instance,
           agg->ident.type, agg->ident.type_instance);
-    is_valid = 0;
+    is_valid = false;
   } /* }}} */
 
   if (!is_valid) { /* {{{ */
@@ -687,11 +678,8 @@ static int agg_config(oconfig_item_t *ci) /* {{{ */
 
 static int agg_read(void) /* {{{ */
 {
-  cdtime_t t;
-  int success;
-
-  t = cdtime();
-  success = 0;
+  cdtime_t t = cdtime();
+  int success = 0;
 
   pthread_mutex_lock(&agg_instance_list_lock);
 
@@ -708,9 +696,7 @@ static int agg_read(void) /* {{{ */
 
   for (agg_instance_t *this = agg_instance_list_head; this != NULL;
        this = this->next) {
-    int status;
-
-    status = agg_instance_read(this, t);
+    int status = agg_instance_read(this, t);
     if (status != 0)
       WARNING("aggregation plugin: Reading an aggregation instance "
               "failed with status %i.",
@@ -726,9 +712,7 @@ static int agg_read(void) /* {{{ */
 
 static int agg_write(data_set_t const *ds, value_list_t const *vl, /* {{{ */
                      __attribute__((unused)) user_data_t *user_data) {
-  _Bool created_by_aggregation = 0;
-  int status;
-
+  bool created_by_aggregation = false;
   /* Ignore values that were created by the aggregation plugin to avoid weird
    * effects. */
   (void)meta_data_get_boolean(vl->meta, "aggregation:created",
@@ -736,6 +720,8 @@ static int agg_write(data_set_t const *ds, value_list_t const *vl, /* {{{ */
   if (created_by_aggregation)
     return 0;
 
+  int status;
+
   if (lookup == NULL)
     status = ENOENT;
   else {
index 467b7ff..9eb5165 100644 (file)
 
 #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>
@@ -66,7 +66,7 @@ int amqp_socket_close(amqp_socket_t *);
  * Data types
  */
 struct camqp_config_s {
-  _Bool publish;
+  bool publish;
   char *name;
 
   char *host;
@@ -83,7 +83,7 @@ struct camqp_config_s {
 
   /* publish only */
   uint8_t delivery_mode;
-  _Bool store_rates;
+  bool store_rates;
   int format;
   /* publish & graphite format only */
   char *prefix;
@@ -94,8 +94,8 @@ struct camqp_config_s {
   /* subscribe only */
   char *exchange_type;
   char *queue;
-  _Bool queue_durable;
-  _Bool queue_auto_delete;
+  bool queue_durable;
+  bool queue_auto_delete;
 
   amqp_connection_state_t connection;
   pthread_mutex_t lock;
@@ -111,9 +111,9 @@ static const char *def_user = "guest";
 static const char *def_password = "guest";
 static const char *def_exchange = "amq.fanout";
 
-static pthread_t *subscriber_threads = NULL;
-static size_t subscriber_threads_num = 0;
-static _Bool subscriber_threads_running = 1;
+static pthread_t *subscriber_threads;
+static size_t subscriber_threads_num;
+static bool subscriber_threads_running = true;
 
 #define CONF(c, f) (((c)->f != NULL) ? (c)->f : def_##f)
 
@@ -176,16 +176,16 @@ static char *camqp_bytes_cstring(amqp_bytes_t *in) /* {{{ */
   return ret;
 } /* }}} char *camqp_bytes_cstring */
 
-static _Bool camqp_is_error(camqp_config_t *conf) /* {{{ */
+static bool camqp_is_error(camqp_config_t *conf) /* {{{ */
 {
   amqp_rpc_reply_t r;
 
   r = amqp_get_rpc_reply(conf->connection);
   if (r.reply_type == AMQP_RESPONSE_NORMAL)
-    return 0;
+    return false;
 
-  return 1;
-} /* }}} _Bool camqp_is_error */
+  return true;
+} /* }}} bool camqp_is_error */
 
 static char *camqp_strerror(camqp_config_t *conf, /* {{{ */
                             char *buffer, size_t buffer_size) {
@@ -396,7 +396,7 @@ static int camqp_setup_queue(camqp_config_t *conf) /* {{{ */
 
 static int camqp_connect(camqp_config_t *conf) /* {{{ */
 {
-  static time_t last_connect_time = 0;
+  static time_t last_connect_time;
 
   amqp_rpc_reply_t reply;
   int status;
@@ -441,10 +441,8 @@ static int camqp_connect(camqp_config_t *conf) /* {{{ */
 
   status = amqp_socket_open(socket, CONF(conf, host), conf->port);
   if (status < 0) {
-    char errbuf[1024];
     status *= -1;
-    ERROR("amqp plugin: amqp_socket_open failed: %s",
-          sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("amqp plugin: amqp_socket_open failed: %s", STRERROR(status));
     amqp_destroy_connection(conf->connection);
     conf->connection = NULL;
     return status;
@@ -454,10 +452,8 @@ static int camqp_connect(camqp_config_t *conf) /* {{{ */
   /* this interface is deprecated as of rabbitmq-c 0.4 */
   sockfd = amqp_open_socket(CONF(conf, host), conf->port);
   if (sockfd < 0) {
-    char errbuf[1024];
     status = (-1) * sockfd;
-    ERROR("amqp plugin: amqp_open_socket failed: %s",
-          sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("amqp plugin: amqp_open_socket failed: %s", STRERROR(status));
     amqp_destroy_connection(conf->connection);
     conf->connection = NULL;
     return status;
@@ -507,7 +503,7 @@ static int camqp_connect(camqp_config_t *conf) /* {{{ */
 
 static int camqp_shutdown(void) /* {{{ */
 {
-  DEBUG("amqp plugin: Shutting down %zu subscriber threads.",
+  DEBUG("amqp plugin: Shutting down %" PRIsz " subscriber threads.",
         subscriber_threads_num);
 
   subscriber_threads_running = 0;
@@ -545,10 +541,8 @@ static int camqp_read_body(camqp_config_t *conf, /* {{{ */
   while (received < body_size) {
     status = amqp_simple_wait_frame(conf->connection, &frame);
     if (status < 0) {
-      char errbuf[1024];
       status = (-1) * status;
-      ERROR("amqp plugin: amqp_simple_wait_frame failed: %s",
-            sstrerror(status, errbuf, sizeof(errbuf)));
+      ERROR("amqp plugin: amqp_simple_wait_frame failed: %s", STRERROR(status));
       camqp_close_connection(conf);
       return status;
     }
@@ -597,10 +591,8 @@ static int camqp_read_header(camqp_config_t *conf) /* {{{ */
 
   status = amqp_simple_wait_frame(conf->connection, &frame);
   if (status < 0) {
-    char errbuf[1024];
     status = (-1) * status;
-    ERROR("amqp plugin: amqp_simple_wait_frame failed: %s",
-          sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("amqp plugin: amqp_simple_wait_frame failed: %s", STRERROR(status));
     camqp_close_connection(conf);
     return status;
   }
@@ -693,9 +685,7 @@ static int camqp_subscribe_init(camqp_config_t *conf) /* {{{ */
   status = plugin_thread_create(tmp, /* attr = */ NULL, camqp_subscribe_thread,
                                 conf, "amqp subscribe");
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("amqp plugin: pthread_create failed: %s",
-          sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("amqp plugin: pthread_create failed: %s", STRERROR(status));
     return status;
   }
 
@@ -835,7 +825,7 @@ static int camqp_config_set_format(oconfig_item_t *ci, /* {{{ */
 } /* }}} int config_set_string */
 
 static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */
-                                   _Bool publish) {
+                                   bool publish) {
   camqp_config_t *conf;
   int status;
 
@@ -860,7 +850,7 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */
 
   /* publish only */
   conf->delivery_mode = CAMQP_DM_VOLATILE;
-  conf->store_rates = 0;
+  conf->store_rates = false;
   conf->graphite_flags = 0;
   /* publish & graphite only */
   conf->prefix = NULL;
@@ -869,8 +859,8 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */
   /* subscribe only */
   conf->exchange_type = NULL;
   conf->queue = NULL;
-  conf->queue_durable = 0;
-  conf->queue_auto_delete = 1;
+  conf->queue_durable = false;
+  conf->queue_auto_delete = true;
   /* general */
   conf->connection = NULL;
   pthread_mutex_init(&conf->lock, /* attr = */ NULL);
@@ -912,7 +902,7 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */
     else if (strcasecmp("RoutingKey", child->key) == 0)
       status = cf_util_get_string(child, &conf->routing_key);
     else if ((strcasecmp("Persistent", child->key) == 0) && publish) {
-      _Bool tmp = 0;
+      bool tmp = 0;
       status = cf_util_get_boolean(child, &tmp);
       if (tmp)
         conf->delivery_mode = CAMQP_DM_PERSISTENT;
@@ -982,10 +972,11 @@ static int camqp_config_connection(oconfig_item_t *ci, /* {{{ */
     char cbname[128];
     snprintf(cbname, sizeof(cbname), "amqp/%s", conf->name);
 
-    status = plugin_register_write(
-        cbname, camqp_write, &(user_data_t){
-                                 .data = conf, .free_func = camqp_config_free,
-                             });
+    status =
+        plugin_register_write(cbname, camqp_write,
+                              &(user_data_t){
+                                  .data = conf, .free_func = camqp_config_free,
+                              });
     if (status != 0) {
       camqp_config_free(conf);
       return status;
@@ -1007,9 +998,9 @@ static int camqp_config(oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("Publish", child->key) == 0)
-      camqp_config_connection(child, /* publish = */ 1);
+      camqp_config_connection(child, /* publish = */ true);
     else if (strcasecmp("Subscribe", child->key) == 0)
-      camqp_config_connection(child, /* publish = */ 0);
+      camqp_config_connection(child, /* publish = */ false);
     else
       WARNING("amqp plugin: Ignoring unknown config option \"%s\".",
               child->key);
diff --git a/src/amqp1.c b/src/amqp1.c
new file mode 100644 (file)
index 0000000..a7fd26b
--- /dev/null
@@ -0,0 +1,773 @@
+/**
+ * collectd - src/amqp1.c
+ * Copyright(c) 2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Andy Smith <ansmith@redhat.com>
+ */
+
+#include "collectd.h"
+
+#include "plugin.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 <proton/connection.h>
+#include <proton/delivery.h>
+#include <proton/link.h>
+#include <proton/message.h>
+#include <proton/proactor.h>
+#include <proton/sasl.h>
+#include <proton/session.h>
+#include <proton/transport.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define BUFSIZE 8192
+#define AMQP1_FORMAT_JSON 0
+#define AMQP1_FORMAT_COMMAND 1
+#define AMQP1_FORMAT_GRAPHITE 2
+
+typedef struct amqp1_config_transport_s {
+  DEQ_LINKS(struct amqp1_config_transport_s);
+  char *name;
+  char *host;
+  char *port;
+  char *user;
+  char *password;
+  char *address;
+  int retry_delay;
+} amqp1_config_transport_t;
+
+typedef struct amqp1_config_instance_s {
+  DEQ_LINKS(struct amqp1_config_instance_s);
+  char *name;
+  bool notify;
+  uint8_t format;
+  unsigned int graphite_flags;
+  bool store_rates;
+  char *prefix;
+  char *postfix;
+  char escape_char;
+  bool pre_settle;
+  char send_to[1024];
+} amqp1_config_instance_t;
+
+DEQ_DECLARE(amqp1_config_instance_t, amqp1_config_instance_list_t);
+
+typedef struct cd_message_s {
+  DEQ_LINKS(struct cd_message_s);
+  pn_rwbytes_t mbuf;
+  amqp1_config_instance_t *instance;
+} cd_message_t;
+
+DEQ_DECLARE(cd_message_t, cd_message_list_t);
+
+/*
+ * Globals
+ */
+static pn_connection_t *conn;
+static pn_link_t *sender;
+static pn_proactor_t *proactor;
+static pthread_mutex_t send_lock;
+static cd_message_list_t out_messages;
+static uint64_t cd_tag = 1;
+static uint64_t acknowledged;
+static amqp1_config_transport_t *transport;
+static bool stopping;
+static bool event_thread_running;
+static pthread_t event_thread_id;
+
+/*
+ * Functions
+ */
+static void cd_message_free(cd_message_t *cdm) {
+  free(cdm->mbuf.start);
+  free(cdm);
+} /* }}} void cd_message_free */
+
+static int amqp1_send_out_messages(pn_link_t *link) /* {{{ */
+{
+  uint64_t dtag;
+  cd_message_list_t to_send;
+  cd_message_t *cdm;
+  int link_credit = pn_link_credit(link);
+  int event_count = 0;
+  pn_delivery_t *dlv;
+
+  if (stopping) {
+    return 0;
+  }
+
+  DEQ_INIT(to_send);
+
+  pthread_mutex_lock(&send_lock);
+
+  if (link_credit > 0) {
+    dtag = cd_tag;
+    cdm = DEQ_HEAD(out_messages);
+    while (cdm) {
+      DEQ_REMOVE_HEAD(out_messages);
+      DEQ_INSERT_TAIL(to_send, cdm);
+      if (DEQ_SIZE(to_send) == (size_t)link_credit)
+        break;
+      cdm = DEQ_HEAD(out_messages);
+    }
+    cd_tag += DEQ_SIZE(to_send);
+  }
+
+  pthread_mutex_unlock(&send_lock);
+
+  /* message is already formatted and encoded */
+  cdm = DEQ_HEAD(to_send);
+  while (cdm) {
+    DEQ_REMOVE_HEAD(to_send);
+    dtag++;
+    dlv = pn_delivery(link, pn_dtag((const char *)&dtag, sizeof(dtag)));
+    pn_link_send(link, cdm->mbuf.start, cdm->mbuf.size);
+    pn_link_advance(link);
+    if (cdm->instance->pre_settle == true) {
+      pn_delivery_settle(dlv);
+    }
+    event_count++;
+    cd_message_free(cdm);
+    cdm = DEQ_HEAD(to_send);
+  }
+
+  return event_count;
+} /* }}} int amqp1_send_out_messages */
+
+static void check_condition(pn_event_t *e, pn_condition_t *cond) /* {{{ */
+{
+  if (pn_condition_is_set(cond)) {
+    ERROR("amqp1 plugin: %s: %s: %s", pn_event_type_name(pn_event_type(e)),
+          pn_condition_get_name(cond), pn_condition_get_description(cond));
+    pn_connection_close(pn_event_connection(e));
+    conn = NULL;
+  }
+} /* }}} void check_condition */
+
+static bool handle(pn_event_t *event) /* {{{ */
+{
+
+  switch (pn_event_type(event)) {
+
+  case PN_CONNECTION_INIT: {
+    conn = pn_event_connection(event);
+    pn_connection_set_container(conn, transport->name);
+    pn_connection_open(conn);
+    pn_session_t *ssn = pn_session(conn);
+    pn_session_open(ssn);
+    sender = pn_sender(ssn, "cd-sender");
+    pn_link_set_snd_settle_mode(sender, PN_SND_MIXED);
+    pn_link_open(sender);
+    break;
+  }
+
+  case PN_LINK_FLOW: {
+    /* peer has given us credit, send outbound messages */
+    amqp1_send_out_messages(sender);
+    break;
+  }
+
+  case PN_DELIVERY: {
+    /* acknowledgement from peer that a message was delivered */
+    pn_delivery_t *dlv = pn_event_delivery(event);
+    if (pn_delivery_remote_state(dlv) == PN_ACCEPTED) {
+      pn_delivery_settle(dlv);
+      acknowledged++;
+    }
+    break;
+  }
+
+  case PN_CONNECTION_WAKE: {
+    if (!stopping) {
+      amqp1_send_out_messages(sender);
+    }
+    break;
+  }
+
+  case PN_TRANSPORT_CLOSED: {
+    check_condition(event, pn_transport_condition(pn_event_transport(event)));
+    break;
+  }
+
+  case PN_CONNECTION_REMOTE_CLOSE: {
+    check_condition(event,
+                    pn_session_remote_condition(pn_event_session(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+  }
+
+  case PN_SESSION_REMOTE_CLOSE: {
+    check_condition(event,
+                    pn_session_remote_condition(pn_event_session(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+  }
+
+  case PN_LINK_REMOTE_CLOSE:
+  case PN_LINK_REMOTE_DETACH: {
+    check_condition(event, pn_link_remote_condition(pn_event_link(event)));
+    pn_connection_close(pn_event_connection(event));
+    break;
+  }
+
+  case PN_PROACTOR_INACTIVE: {
+    return false;
+  }
+
+  default:
+    break;
+  }
+  return true;
+} /* }}} bool handle */
+
+static void *event_thread(void __attribute__((unused)) * arg) /* {{{ */
+{
+  char addr[PN_MAX_ADDR];
+  cd_message_t *cdm;
+
+  /* setup proactor */
+  proactor = pn_proactor();
+  pn_proactor_addr(addr, sizeof(addr), transport->host, transport->port);
+
+  while (!stopping) {
+    /* make connection */
+    conn = pn_connection();
+    if (transport->user != NULL) {
+      pn_connection_set_user(conn, transport->user);
+      pn_connection_set_password(conn, transport->password);
+    }
+    pn_proactor_connect(proactor, conn, addr);
+
+    bool engine_running = true;
+    while (engine_running && !stopping) {
+      pn_event_batch_t *events = pn_proactor_wait(proactor);
+      pn_event_t *e;
+      while ((e = pn_event_batch_next(events))) {
+        engine_running = handle(e);
+        if (!engine_running) {
+          break;
+        }
+      }
+      pn_proactor_done(proactor, events);
+    }
+
+    pn_proactor_release_connection(conn);
+
+    DEBUG("amqp1 plugin: retrying connection");
+    int delay = transport->retry_delay;
+    while (delay-- > 0 && !stopping) {
+      sleep(1.0);
+    }
+  }
+
+  pn_proactor_disconnect(proactor, NULL);
+
+  /* Free the remaining out_messages */
+  cdm = DEQ_HEAD(out_messages);
+  while (cdm) {
+    DEQ_REMOVE_HEAD(out_messages);
+    cd_message_free(cdm);
+    cdm = DEQ_HEAD(out_messages);
+  }
+
+  event_thread_running = false;
+
+  return NULL;
+} /* }}} void event_thread */
+
+static int encqueue(cd_message_t *cdm,
+                    amqp1_config_instance_t *instance) /* {{{ */
+{
+  /* encode message */
+  pn_message_t *message = pn_message();
+  pn_message_set_address(message, instance->send_to);
+  pn_data_t *body = pn_message_body(message);
+  pn_data_clear(body);
+  pn_data_put_binary(body, pn_bytes(cdm->mbuf.size, cdm->mbuf.start));
+  pn_data_exit(body);
+
+  /* put_binary copies and stores so ok to use mbuf */
+  cdm->mbuf.size = BUFSIZE;
+
+  int status;
+  char *start;
+  while ((status = pn_message_encode(message, cdm->mbuf.start,
+                                     &cdm->mbuf.size)) == PN_OVERFLOW) {
+    DEBUG("amqp1 plugin: increasing message buffer size %zu", cdm->mbuf.size);
+    cdm->mbuf.size *= 2;
+    start = realloc(cdm->mbuf.start, cdm->mbuf.size);
+    if (start == NULL) {
+      status = -1;
+      break;
+    } else {
+      cdm->mbuf.start = start;
+    }
+  }
+
+  if (status != 0) {
+    ERROR("amqp1 plugin: error encoding message: %s",
+          pn_error_text(pn_message_error(message)));
+    pn_message_free(message);
+    return -1;
+  }
+
+  pthread_mutex_lock(&send_lock);
+  DEQ_INSERT_TAIL(out_messages, cdm);
+  pthread_mutex_unlock(&send_lock);
+
+  pn_message_free(message);
+
+  /* activate the sender */
+  if (conn) {
+    pn_connection_wake(conn);
+  }
+
+  return 0;
+} /* }}} int encqueue */
+
+static int amqp1_notify(notification_t const *n,
+                        user_data_t *user_data) /* {{{ */
+{
+  int status = 0;
+  size_t bfree = BUFSIZE;
+  size_t bfill = 0;
+  size_t bufsize = BUFSIZE;
+
+  if (n == NULL || user_data == NULL)
+    return EINVAL;
+
+  amqp1_config_instance_t *instance = user_data->data;
+
+  if (instance->notify != true) {
+    ERROR("amqp1 plugin: write notification failed");
+  }
+
+  cd_message_t *cdm = malloc(sizeof(*cdm));
+  if (cdm == NULL) {
+    ERROR("amqp1 plugin: notify failed");
+    return -1;
+  }
+
+  DEQ_ITEM_INIT(cdm);
+  char *start = malloc(bufsize);
+  if (start == NULL) {
+    ERROR("amqp1 plugin: malloc failed");
+    free(cdm);
+    return -1;
+  }
+  cdm->mbuf.size = bufsize;
+  cdm->mbuf.start = start;
+  cdm->instance = instance;
+
+  switch (instance->format) {
+  case AMQP1_FORMAT_JSON:
+    format_json_initialize(cdm->mbuf.start, &bfill, &bfree);
+    status = format_json_notification(cdm->mbuf.start, bufsize, n);
+    if (status != 0) {
+      ERROR("amqp1 plugin: formatting notification failed");
+      cd_message_free(cdm);
+      return status;
+    }
+    cdm->mbuf.size = strlen(cdm->mbuf.start);
+    if (cdm->mbuf.size >= BUFSIZE) {
+      ERROR("amqp1 plugin: notify format json failed");
+      cd_message_free(cdm);
+      return -1;
+    }
+    break;
+  default:
+    ERROR("amqp1 plugin: Invalid notify format (%i).", instance->format);
+    cd_message_free(cdm);
+    return -1;
+  }
+
+  /* encode message and place on outbound queue */
+  status = encqueue(cdm, instance);
+  if (status != 0) {
+    ERROR("amqp1 plugin: notify enqueue failed");
+    cd_message_free(cdm);
+  }
+  return status;
+
+} /* }}} int amqp1_notify */
+
+static int amqp1_write(const data_set_t *ds, const value_list_t *vl, /* {{{ */
+                       user_data_t *user_data) {
+  int status = 0;
+  size_t bfree = BUFSIZE;
+  size_t bfill = 0;
+  size_t bufsize = BUFSIZE;
+
+  if (ds == NULL || vl == NULL || transport == NULL || user_data == NULL)
+    return EINVAL;
+
+  amqp1_config_instance_t *instance = user_data->data;
+
+  if (instance->notify != false) {
+    ERROR("amqp1 plugin: write failed");
+  }
+
+  cd_message_t *cdm = malloc(sizeof(*cdm));
+  if (cdm == NULL) {
+    ERROR("amqp1 plugin: malloc failed.");
+    return -1;
+  }
+  DEQ_ITEM_INIT(cdm);
+  char *start = malloc(bufsize);
+  if (start == NULL) {
+    ERROR("amqp1 plugin: malloc failed.");
+    free(cdm);
+    return -1;
+  }
+  cdm->mbuf.size = bufsize;
+  cdm->mbuf.start = start;
+  cdm->instance = instance;
+
+  switch (instance->format) {
+  case AMQP1_FORMAT_COMMAND:
+    status = cmd_create_putval((char *)cdm->mbuf.start, bufsize, ds, vl);
+    if (status != 0) {
+      ERROR("amqp1 plugin: cmd_create_putval failed with status %i.", status);
+      cd_message_free(cdm);
+      return status;
+    }
+    cdm->mbuf.size = strlen(cdm->mbuf.start);
+    if (cdm->mbuf.size >= BUFSIZE) {
+      ERROR("amqp1 plugin: format cmd failed");
+      cd_message_free(cdm);
+      return -1;
+    }
+    break;
+  case AMQP1_FORMAT_JSON:
+    format_json_initialize((char *)cdm->mbuf.start, &bfill, &bfree);
+    format_json_value_list((char *)cdm->mbuf.start, &bfill, &bfree, ds, vl,
+                           instance->store_rates);
+    status = format_json_finalize((char *)cdm->mbuf.start, &bfill, &bfree);
+    if (status != 0) {
+      ERROR("amqp1 plugin: format_json_finalize failed with status %i.",
+            status);
+      cd_message_free(cdm);
+      return status;
+    }
+    cdm->mbuf.size = strlen(cdm->mbuf.start);
+    if (cdm->mbuf.size >= BUFSIZE) {
+      ERROR("amqp1 plugin: format json failed");
+      cd_message_free(cdm);
+      return -1;
+    }
+    break;
+  case AMQP1_FORMAT_GRAPHITE:
+    status = format_graphite((char *)cdm->mbuf.start, bufsize, ds, vl,
+                             instance->prefix, instance->postfix,
+                             instance->escape_char, instance->graphite_flags);
+    if (status != 0) {
+      ERROR("amqp1 plugin: format_graphite failed with status %i.", status);
+      cd_message_free(cdm);
+      return status;
+    }
+    cdm->mbuf.size = strlen(cdm->mbuf.start);
+    if (cdm->mbuf.size >= BUFSIZE) {
+      ERROR("amqp1 plugin: format graphite failed");
+      cd_message_free(cdm);
+      return -1;
+    }
+    break;
+  default:
+    ERROR("amqp1 plugin: Invalid write format (%i).", instance->format);
+    cd_message_free(cdm);
+    return -1;
+  }
+
+  /* encode message and place on outbound queue */
+  status = encqueue(cdm, instance);
+  if (status != 0) {
+    ERROR("amqp1 plugin: write enqueue failed");
+    cd_message_free(cdm);
+  }
+  return status;
+
+} /* }}} int amqp1_write */
+
+static void amqp1_config_transport_free(void *ptr) /* {{{ */
+{
+  amqp1_config_transport_t *transport = ptr;
+
+  if (transport == NULL)
+    return;
+
+  sfree(transport->name);
+  sfree(transport->host);
+  sfree(transport->port);
+  sfree(transport->user);
+  sfree(transport->password);
+  sfree(transport->address);
+
+  sfree(transport);
+} /* }}} void amqp1_config_transport_free */
+
+static void amqp1_config_instance_free(void *ptr) /* {{{ */
+{
+  amqp1_config_instance_t *instance = ptr;
+
+  if (instance == NULL)
+    return;
+
+  sfree(instance->name);
+  sfree(instance->prefix);
+  sfree(instance->postfix);
+
+  sfree(instance);
+} /* }}} void amqp1_config_instance_free */
+
+static int amqp1_config_instance(oconfig_item_t *ci) /* {{{ */
+{
+  amqp1_config_instance_t *instance = calloc(1, sizeof(*instance));
+  if (instance == NULL) {
+    ERROR("amqp1 plugin: calloc failed.");
+    return ENOMEM;
+  }
+
+  int status = cf_util_get_string(ci, &instance->name);
+  if (status != 0) {
+    sfree(instance);
+    return status;
+  }
+
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp("PreSettle", child->key) == 0)
+      status = cf_util_get_boolean(child, &instance->pre_settle);
+    else if (strcasecmp("Notify", child->key) == 0)
+      status = cf_util_get_boolean(child, &instance->notify);
+    else if (strcasecmp("Format", child->key) == 0) {
+      char *key = NULL;
+      status = cf_util_get_string(child, &key);
+      if (status != 0)
+        return status;
+      assert(key != NULL);
+      if (strcasecmp(key, "Command") == 0) {
+        instance->format = AMQP1_FORMAT_COMMAND;
+      } else if (strcasecmp(key, "Graphite") == 0) {
+        instance->format = AMQP1_FORMAT_GRAPHITE;
+      } else if (strcasecmp(key, "JSON") == 0) {
+        instance->format = AMQP1_FORMAT_JSON;
+      } else {
+        WARNING("amqp1 plugin: Invalid format string: %s", key);
+      }
+      sfree(key);
+    } else if (strcasecmp("StoreRates", child->key) == 0)
+      status = cf_util_get_boolean(child, &instance->store_rates);
+    else if (strcasecmp("GraphiteSeparateInstances", child->key) == 0)
+      status = cf_util_get_flag(child, &instance->graphite_flags,
+                                GRAPHITE_SEPARATE_INSTANCES);
+    else if (strcasecmp("GraphiteAlwaysAppendDS", child->key) == 0)
+      status = cf_util_get_flag(child, &instance->graphite_flags,
+                                GRAPHITE_ALWAYS_APPEND_DS);
+    else if (strcasecmp("GraphitePreserveSeparator", child->key) == 0)
+      status = cf_util_get_flag(child, &instance->graphite_flags,
+                                GRAPHITE_PRESERVE_SEPARATOR);
+    else if (strcasecmp("GraphitePrefix", child->key) == 0)
+      status = cf_util_get_string(child, &instance->prefix);
+    else if (strcasecmp("GraphitePostfix", child->key) == 0)
+      status = cf_util_get_string(child, &instance->postfix);
+    else if (strcasecmp("GraphiteEscapeChar", child->key) == 0) {
+      char *tmp_buff = NULL;
+      status = cf_util_get_string(child, &tmp_buff);
+      if (status == 0) {
+        if (strlen(tmp_buff) > 1)
+          WARNING("amqp1 plugin: The option \"GraphiteEscapeChar\" handles "
+                  "only one character. Others will be ignored.");
+        instance->escape_char = tmp_buff[0];
+      }
+      sfree(tmp_buff);
+    } else
+      WARNING("amqp1 plugin: Ignoring unknown "
+              "instance configuration option "
+              "\"%s\".",
+              child->key);
+    if (status != 0)
+      break;
+  }
+
+  if (status != 0) {
+    amqp1_config_instance_free(instance);
+    return status;
+  } else {
+    char tpname[DATA_MAX_NAME_LEN];
+    status = snprintf(tpname, sizeof(tpname), "amqp1/%s", instance->name);
+    if ((status < 0) || (size_t)status >= sizeof(tpname)) {
+      ERROR("amqp1 plugin: Instance name would have been truncated.");
+      return -1;
+    }
+    status = snprintf(instance->send_to, sizeof(instance->send_to), "/%s/%s",
+                      transport->address, instance->name);
+    if ((status < 0) || (size_t)status >= sizeof(instance->send_to)) {
+      ERROR("amqp1 plugin: send_to address would have been truncated.");
+      return -1;
+    }
+    if (instance->notify) {
+      status = plugin_register_notification(
+          tpname, amqp1_notify,
+          &(user_data_t){
+              .data = instance, .free_func = amqp1_config_instance_free,
+          });
+    } else {
+      status = plugin_register_write(
+          tpname, amqp1_write,
+          &(user_data_t){
+              .data = instance, .free_func = amqp1_config_instance_free,
+          });
+    }
+
+    if (status != 0) {
+      amqp1_config_instance_free(instance);
+    }
+  }
+
+  return status;
+} /* }}} int amqp1_config_instance */
+
+static int amqp1_config_transport(oconfig_item_t *ci) /* {{{ */
+{
+  transport = calloc(1, sizeof(*transport));
+  if (transport == NULL) {
+    ERROR("amqp1 plugin: calloc failed.");
+    return ENOMEM;
+  }
+
+  /* Initialize transport configuration {{{ */
+  transport->retry_delay = 1;
+
+  int status = cf_util_get_string(ci, &transport->name);
+  if (status != 0) {
+    sfree(transport);
+    return status;
+  }
+
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp("Host", child->key) == 0)
+      status = cf_util_get_string(child, &transport->host);
+    else if (strcasecmp("Port", child->key) == 0)
+      status = cf_util_get_string(child, &transport->port);
+    else if (strcasecmp("User", child->key) == 0)
+      status = cf_util_get_string(child, &transport->user);
+    else if (strcasecmp("Password", child->key) == 0)
+      status = cf_util_get_string(child, &transport->password);
+    else if (strcasecmp("Address", child->key) == 0)
+      status = cf_util_get_string(child, &transport->address);
+    else if (strcasecmp("RetryDelay", child->key) == 0)
+      status = cf_util_get_int(child, &transport->retry_delay);
+    else if (strcasecmp("Instance", child->key) == 0)
+      amqp1_config_instance(child);
+    else
+      WARNING("amqp1 plugin: Ignoring unknown "
+              "transport configuration option "
+              "\"%s\".",
+              child->key);
+
+    if (status != 0)
+      break;
+  }
+
+  if (status != 0) {
+    amqp1_config_transport_free(transport);
+  }
+  return status;
+} /* }}} int amqp1_config_transport */
+
+static int amqp1_config(oconfig_item_t *ci) /* {{{ */
+{
+
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp("Transport", child->key) == 0)
+      amqp1_config_transport(child);
+    else
+      WARNING("amqp1 plugin: Ignoring unknown config option \"%s\".",
+              child->key);
+  }
+
+  return 0;
+} /* }}} int amqp1_config */
+
+static int amqp1_init(void) /* {{{ */
+{
+  if (transport == NULL) {
+    ERROR("amqp1: init failed, no transport configured");
+    return -1;
+  }
+
+  if (proactor == NULL) {
+    pthread_mutex_init(&send_lock, /* attr = */ NULL);
+    /* start_thread */
+    int status =
+        plugin_thread_create(&event_thread_id, NULL /* no attributes */,
+                             event_thread, NULL /* no argument */, "handle");
+    if (status != 0) {
+      ERROR("amqp1 plugin: pthread_create failed: %s", STRERRNO);
+    } else {
+      event_thread_running = true;
+    }
+  }
+  return 0;
+} /* }}} int amqp1_init */
+
+static int amqp1_shutdown(void) /* {{{ */
+{
+  stopping = true;
+
+  /* Stop the proactor thread */
+  if (event_thread_running) {
+    DEBUG("amqp1 plugin: Shutting down proactor thread.");
+    pn_connection_wake(conn);
+  }
+  pthread_join(event_thread_id, NULL /* no return value */);
+  memset(&event_thread_id, 0, sizeof(event_thread_id));
+
+  DEBUG("amqp1 plugin: proactor thread exited.");
+
+  if (transport) {
+    amqp1_config_transport_free(transport);
+  }
+
+  return 0;
+} /* }}} int amqp1_shutdown */
+
+void module_register(void) {
+  plugin_register_complex_config("amqp1", amqp1_config);
+  plugin_register_init("amqp1", amqp1_init);
+  plugin_register_shutdown("amqp1", amqp1_shutdown);
+} /* void module_register */
index 07b2b57..d64e547 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <curl/curl.h>
 
@@ -40,8 +40,8 @@ struct apache_s {
   char *url;
   char *user;
   char *pass;
-  _Bool verify_peer;
-  _Bool verify_host;
+  bool verify_peer;
+  bool verify_host;
   char *cacert;
   char *ssl_ciphers;
   char *server; /* user specific server type */
@@ -82,23 +82,19 @@ static void apache_free(void *arg) {
 
 static size_t apache_curl_callback(void *buf, size_t size, size_t nmemb,
                                    void *user_data) {
-  size_t len = size * nmemb;
-  apache_t *st;
-
-  st = user_data;
+  apache_t *st = user_data;
   if (st == NULL) {
     ERROR("apache plugin: apache_curl_callback: "
           "user_data pointer is NULL.");
     return 0;
   }
 
+  size_t len = size * nmemb;
   if (len == 0)
     return len;
 
   if ((st->apache_buffer_fill + len) >= st->apache_buffer_size) {
-    char *temp;
-
-    temp = realloc(st->apache_buffer, st->apache_buffer_fill + len + 1);
+    char *temp = realloc(st->apache_buffer, st->apache_buffer_fill + len + 1);
     if (temp == NULL) {
       ERROR("apache plugin: realloc failed.");
       return 0;
@@ -116,16 +112,14 @@ static size_t apache_curl_callback(void *buf, size_t size, size_t nmemb,
 
 static size_t apache_header_callback(void *buf, size_t size, size_t nmemb,
                                      void *user_data) {
-  size_t len = size * nmemb;
-  apache_t *st;
-
-  st = user_data;
+  apache_t *st = user_data;
   if (st == NULL) {
     ERROR("apache plugin: apache_header_callback: "
           "user_data pointer is NULL.");
     return 0;
   }
 
+  size_t len = size * nmemb;
   if (len == 0)
     return len;
 
@@ -158,10 +152,7 @@ static size_t apache_header_callback(void *buf, size_t size, size_t nmemb,
  * </Plugin>
  */
 static int config_add(oconfig_item_t *ci) {
-  apache_t *st;
-  int status;
-
-  st = calloc(1, sizeof(*st));
+  apache_t *st = calloc(1, sizeof(*st));
   if (st == NULL) {
     ERROR("apache plugin: calloc failed.");
     return -1;
@@ -169,7 +160,7 @@ static int config_add(oconfig_item_t *ci) {
 
   st->timeout = -1;
 
-  status = cf_util_get_string(ci, &st->name);
+  int status = cf_util_get_string(ci, &st->name);
   if (status != 0) {
     sfree(st);
     return status;
@@ -238,8 +229,6 @@ static int config_add(oconfig_item_t *ci) {
 } /* int config_add */
 
 static int config(oconfig_item_t *ci) {
-  int status = 0;
-
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
 
@@ -253,7 +242,7 @@ static int config(oconfig_item_t *ci) {
               child->key);
   } /* for (ci->children) */
 
-  return status;
+  return 0;
 } /* int config */
 
 /* initialize curl for each host */
@@ -308,10 +297,8 @@ static int init_host(apache_t *st) /* {{{ */
                      (st->pass == NULL) ? "" : st->pass);
 #else
     static char credentials[1024];
-    int status;
-
-    status = snprintf(credentials, sizeof(credentials), "%s:%s", st->user,
-                      (st->pass == NULL) ? "" : st->pass);
+    int status = snprintf(credentials, sizeof(credentials), "%s:%s", st->user,
+                          (st->pass == NULL) ? "" : st->pass);
     if ((status < 0) || ((size_t)status >= sizeof(credentials))) {
       ERROR("apache plugin: init_host: Returning an error "
             "because the credentials have been "
@@ -479,28 +466,13 @@ static void submit_scoreboard(char *buf, apache_t *st) {
 
 static int apache_read_host(user_data_t *user_data) /* {{{ */
 {
-  char *ptr;
-  char *saveptr;
-  char *line;
-
-  char *fields[4];
-  int fields_num;
-
-  apache_t *st;
-
-  st = user_data->data;
-
-  int status;
-
-  char *content_type;
-  static const char *text_plain = "text/plain";
+  apache_t *st = user_data->data;
 
   assert(st->url != NULL);
   /* (Assured by `config_add') */
 
   if (st->curl == NULL) {
-    status = init_host(st);
-    if (status != 0)
+    if (init_host(st) != 0)
       return -1;
   }
   assert(st->curl != NULL);
@@ -521,7 +493,10 @@ static int apache_read_host(user_data_t *user_data) /* {{{ */
     st->server_type = APACHE;
   }
 
-  status = curl_easy_getinfo(st->curl, CURLINFO_CONTENT_TYPE, &content_type);
+  char *content_type;
+  static const char *text_plain = "text/plain";
+  int status =
+      curl_easy_getinfo(st->curl, CURLINFO_CONTENT_TYPE, &content_type);
   if ((status == CURLE_OK) && (content_type != NULL) &&
       (strncasecmp(content_type, text_plain, strlen(text_plain)) != 0)) {
     WARNING("apache plugin: `Content-Type' response header is not `%s' "
@@ -530,11 +505,14 @@ static int apache_read_host(user_data_t *user_data) /* {{{ */
             text_plain, content_type);
   }
 
-  ptr = st->apache_buffer;
-  saveptr = NULL;
+  char *ptr = st->apache_buffer;
+  char *saveptr = NULL;
+  char *line;
   while ((line = strtok_r(ptr, "\n\r", &saveptr)) != NULL) {
     ptr = NULL;
-    fields_num = strsplit(line, fields, STATIC_ARRAY_SIZE(fields));
+    char *fields[4];
+
+    int fields_num = strsplit(line, fields, STATIC_ARRAY_SIZE(fields));
 
     if (fields_num == 3) {
       if ((strcmp(fields[0], "Total") == 0) &&
index 406c164..83a5c87 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h" /* rrd_update_file */
-#include "plugin.h" /* plugin_register, plugin_submit */
+#include "plugin.h"              /* plugin_register, plugin_submit */
+#include "utils/common/common.h" /* rrd_update_file */
 
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -70,16 +70,16 @@ typedef struct {
  * Private variables
  */
 /* Default values for contacting daemon */
-static char *conf_node = NULL;
-static char *conf_service = NULL;
+static char *conf_node;
+static char *conf_service;
 /* Defaults to false for backwards compatibility. */
-static _Bool conf_report_seconds = 0;
-static _Bool conf_persistent_conn = 1;
+static bool conf_report_seconds;
+static bool conf_persistent_conn = true;
 
 static int global_sockfd = -1;
 
-static int count_retries = 0;
-static int count_iterations = 0;
+static int count_retries;
+static int count_iterations;
 
 static int net_shutdown(int *fd) {
   uint16_t packet_size = 0;
@@ -119,10 +119,8 @@ static int net_open(char const *node, char const *service) {
 
   status = getaddrinfo(node, service, &ai_hints, &ai_return);
   if (status != 0) {
-    char errbuf[1024];
     INFO("apcups plugin: getaddrinfo failed: %s",
-         (status == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                : gai_strerror(status));
+         (status == EAI_SYSTEM) ? STRERRNO : gai_strerror(status));
     return -1;
   }
 
@@ -147,9 +145,7 @@ static int net_open(char const *node, char const *service) {
 
   if (status != 0) /* `connect(2)' failed */
   {
-    char errbuf[1024];
-    INFO("apcups plugin: connect failed: %s",
-         sstrerror(errno, errbuf, sizeof(errbuf)));
+    INFO("apcups plugin: connect failed: %s", STRERRNO);
     close(sd);
     return -1;
   }
@@ -240,7 +236,8 @@ static int apc_query_server(char const *node, char const *service,
   char recvline[1024];
   char *tokptr;
   char *toksaveptr;
-  int try = 0;
+  int try
+    = 0;
   int status;
 
 #if APCMAIN
@@ -265,7 +262,8 @@ static int apc_query_server(char const *node, char const *service,
       /* net_send closes the socket on error. */
       assert(global_sockfd < 0);
       if (try == 0) {
-        try++;
+        try
+          ++;
         count_retries++;
         continue;
       }
@@ -287,7 +285,7 @@ static int apc_query_server(char const *node, char const *service,
            "first %i iterations. Will close the socket "
            "in future iterations.",
            count_retries, count_iterations);
-    conf_persistent_conn = 0;
+    conf_persistent_conn = false;
   }
 
   while ((n = net_recv(&global_sockfd, recvline, sizeof(recvline) - 1)) > 0) {
@@ -341,9 +339,7 @@ static int apc_query_server(char const *node, char const *service,
     net_shutdown(&global_sockfd);
 
   if (n < 0) {
-    char errbuf[1024];
-    ERROR("apcups plugin: Reading from socket failed: %s",
-          sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("apcups plugin: Reading from socket failed: %s", STRERROR(status));
     return -1;
   }
 
@@ -351,7 +347,7 @@ static int apc_query_server(char const *node, char const *service,
 }
 
 static int apcups_config(oconfig_item_t *ci) {
-  _Bool persistent_conn_set = 0;
+  bool persistent_conn_set = false;
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
@@ -364,7 +360,7 @@ static int apcups_config(oconfig_item_t *ci) {
       cf_util_get_boolean(child, &conf_report_seconds);
     else if (strcasecmp(child->key, "PersistentConnection") == 0) {
       cf_util_get_boolean(child, &conf_persistent_conn);
-      persistent_conn_set = 1;
+      persistent_conn_set = true;
     } else
       ERROR("apcups plugin: Unknown config option \"%s\".", child->key);
   }
@@ -376,7 +372,7 @@ static int apcups_config(oconfig_item_t *ci) {
              "Apcupsd NIS socket timeout is %.3f seconds, "
              "PersistentConnection disabled by default.",
              interval, APCUPS_SERVER_TIMEOUT);
-      conf_persistent_conn = 0;
+      conf_persistent_conn = false;
     }
   }
 
@@ -424,8 +420,8 @@ static int apcups_read(void) {
   int status = apc_query_server(conf_node, conf_service, &apcups_detail);
 
   if (status != 0) {
-    DEBUG("apcups plugin: apc_query_server (\"%s\", \"%s\") = %d",
-          conf_node, conf_service, status);
+    DEBUG("apcups plugin: apc_query_server (\"%s\", \"%s\") = %d", conf_node,
+          conf_service, status);
     return status;
   }
 
index f78c3da..ad6e6c0 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_MACH_MACH_TYPES_H
 #include <mach/mach_types.h>
index 7783561..dfa2804 100644 (file)
@@ -21,8 +21,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <libaquaero5.h>
 
@@ -30,7 +30,7 @@
  * Private variables
  */
 /* Default values for contacting daemon */
-static char *conf_device = NULL;
+static char *conf_device;
 
 static int aquaero_config(oconfig_item_t *ci) {
   for (int i = 0; i < ci->children_num; i++) {
@@ -94,19 +94,15 @@ static int aquaero_read(void) {
   char type_instance[DATA_MAX_NAME_LEN];
 
   if (libaquaero5_poll(conf_device, &aq_data, &err_msg) < 0) {
-    char errbuf[1024];
     ERROR("aquaero plugin: Failed to poll device \"%s\": %s (%s)",
-          conf_device ? conf_device : "default", err_msg,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          conf_device ? conf_device : "default", err_msg, STRERRNO);
     return -1;
   }
 
   if (libaquaero5_getsettings(conf_device, &aq_sett, &err_msg) < 0) {
-    char errbuf[1024];
     ERROR("aquaero plugin: Failed to get settings "
           "for device \"%s\": %s (%s)",
-          conf_device ? conf_device : "default", err_msg,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          conf_device ? conf_device : "default", err_msg, STRERRNO);
     return -1;
   }
 
index 36694f6..e5589bf 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <curl/curl.h>
 #include <libxml/parser.h>
@@ -87,19 +87,19 @@ typedef struct player_info_s player_info_t;
 #define PLAYER_INFO_STATIC_INIT                                                \
   { -1, -1, -1, -1, -1 }
 
-static char *url = NULL;
-static char *user = NULL;
-static char *pass = NULL;
-static char *verify_peer = NULL;
-static char *verify_host = NULL;
-static char *cacert = NULL;
-static char *timeout = NULL;
+static char *url;
+static char *user;
+static char *pass;
+static char *verify_peer;
+static char *verify_host;
+static char *cacert;
+static char *timeout;
 
-static CURL *curl = NULL;
+static CURL *curl;
 
-static char *ascent_buffer = NULL;
-static size_t ascent_buffer_size = 0;
-static size_t ascent_buffer_fill = 0;
+static char *ascent_buffer;
+static size_t ascent_buffer_size;
+static size_t ascent_buffer_fill;
 static char ascent_curl_error[CURL_ERROR_SIZE];
 
 static const char *config_keys[] = {
index f698005..468a237 100644 (file)
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 
 #include <fcntl.h>
 #include <linux/i2c-dev.h>
+#if HAVE_I2C_SMBUS_H
+#include <i2c/smbus.h>
+#endif
 #include <math.h>
 #include <stdint.h>
 #include <sys/ioctl.h>
@@ -177,23 +180,23 @@ static const char *config_keys[] = {
 
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static char *config_device = NULL; /**< I2C bus device */
-static int config_oversample = 1;  /**< averaging window */
+static char *config_device;       /**< I2C bus device */
+static int config_oversample = 1; /**< averaging window */
 
-static double config_press_offset = 0.0; /**< pressure offset */
-static double config_temp_offset = 0.0;  /**< temperature offset */
+static double config_press_offset; /**< pressure offset */
+static double config_temp_offset;  /**< temperature offset */
 
 static double config_altitude = NAN; /**< altitude */
-static int config_normalize = 0;     /**< normalization method */
+static int config_normalize;         /**< normalization method */
 
-static _Bool configured = 0; /**< the whole plugin config status */
+static bool configured; /**< the whole plugin config status */
 
 static int i2c_bus_fd = -1; /**< I2C bus device FD */
 
 static enum Sensor_type sensor_type =
     Sensor_none; /**< detected/used sensor type */
 
-static __s32 mpl3115_oversample = 0; /**< MPL3115 CTRL1 oversample setting */
+static __s32 mpl3115_oversample; /**< MPL3115 CTRL1 oversample setting */
 
 // BMP085 configuration
 static unsigned bmp085_oversampling; /**< BMP085 oversampling (0-3) */
@@ -226,7 +229,7 @@ static short bmp085_MD;
 /*  Used only for MPL115. MPL3115 supports real oversampling in the device so */
 /*  no need for any postprocessing. */
 
-static _Bool avg_initialized = 0; /**< already initialized by real values */
+static bool avg_initialized; /**< already initialized by real values */
 
 typedef struct averaging_s {
   long int *ring_buffer;
@@ -235,8 +238,8 @@ typedef struct averaging_s {
   int ring_buffer_head;
 } averaging_t;
 
-static averaging_t pressure_averaging = {NULL, 0, 0L, 0};
-static averaging_t temperature_averaging = {NULL, 0, 0L, 0};
+static averaging_t pressure_averaging;
+static averaging_t temperature_averaging;
 
 /**
  * Create / allocate averaging buffer
@@ -249,7 +252,7 @@ static averaging_t temperature_averaging = {NULL, 0, 0L, 0};
  * @return Zero when successful
  */
 static int averaging_create(averaging_t *avg, int size) {
-  avg->ring_buffer = calloc((size_t)size, sizeof(*avg->ring_buffer));
+  avg->ring_buffer = calloc(size, sizeof(*avg->ring_buffer));
   if (avg->ring_buffer == NULL) {
     ERROR("barometer: averaging_create - ring buffer allocation of size %d "
           "failed",
@@ -313,11 +316,11 @@ static double averaging_add_sample(averaging_t *avg, long int sample) {
 typedef struct temperature_list_s {
   char *sensor_name;               /**< sensor name/reference */
   size_t num_values;               /**< number of values (usually one) */
-  _Bool initialized;               /**< sensor already provides data */
+  bool initialized;                /**< sensor already provides data */
   struct temperature_list_s *next; /**< next in the list */
 } temperature_list_t;
 
-static temperature_list_t *temp_list = NULL;
+static temperature_list_t *temp_list;
 
 /*
  * Add new sensor to the temperature reference list
@@ -412,16 +415,16 @@ static int get_reference_temperature(double *result) {
         continue;
       }
 
-      DEBUG(
-          "barometer: get_reference_temperature - initialize \"%s\", %zu vals",
-          list->sensor_name, values_num);
+      DEBUG("barometer: get_reference_temperature - initialize \"%s\", %" PRIsz
+            " vals",
+            list->sensor_name, values_num);
 
       list->initialized = 1;
       list->num_values = values_num;
 
       for (size_t i = 0; i < values_num; ++i) {
-        DEBUG("barometer: get_reference_temperature - rate %zu: %lf **", i,
-              values[i]);
+        DEBUG("barometer: get_reference_temperature - rate %" PRIsz ": %lf **",
+              i, values[i]);
         if (!isnan(values[i])) {
           avg_sum += values[i];
           ++avg_num;
@@ -444,7 +447,7 @@ static int get_reference_temperature(double *result) {
     }
 
     for (size_t i = 0; i < REF_TEMP_AVG_NUM * list->num_values; ++i) {
-      DEBUG("barometer: get_reference_temperature - history %zu: %lf", i,
+      DEBUG("barometer: get_reference_temperature - history %" PRIsz ": %lf", i,
             values_history[i]);
       if (!isnan(values_history[i])) {
         avg_sum += values_history[i];
@@ -464,8 +467,9 @@ static int get_reference_temperature(double *result) {
       }
 
       for (size_t i = 0; i < values_num; ++i) {
-        DEBUG("barometer: get_reference_temperature - rate last %zu: %lf **", i,
-              values[i]);
+        DEBUG("barometer: get_reference_temperature - rate last %" PRIsz
+              ": %lf **",
+              i, values[i]);
         if (!isnan(values[i])) {
           avg_sum += values[i];
           ++avg_num;
@@ -514,12 +518,11 @@ static int get_reference_temperature(double *result) {
  */
 static int MPL115_detect(void) {
   __s32 res;
-  char errbuf[1024];
 
   if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL115_I2C_ADDRESS) < 0) {
     ERROR("barometer: MPL115_detect problem setting i2c slave address to "
           "0x%02X: %s",
-          MPL115_I2C_ADDRESS, sstrerror(errno, errbuf, sizeof(errbuf)));
+          MPL115_I2C_ADDRESS, STRERRNO);
     return 0;
   }
 
@@ -548,14 +551,11 @@ static int MPL115_read_coeffs(void) {
   int8_t sic12MSB, sic12LSB, sic11MSB, sic11LSB, sic22MSB, sic22LSB;
   int16_t sia0, sib1, sib2, sic12, sic11, sic22;
 
-  char errbuf[1024];
-
   res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, MPL115_ADDR_COEFFS,
                                       STATIC_ARRAY_SIZE(mpl115_coeffs),
                                       mpl115_coeffs);
   if (res < 0) {
-    ERROR("barometer: MPL115_read_coeffs - problem reading data: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("barometer: MPL115_read_coeffs - problem reading data: %s", STRERRNO);
     return -1;
   }
 
@@ -657,7 +657,6 @@ static int MPL115_read_averaged(double *pressure, double *temperature) {
   int conv_temperature;
   double adc_pressure;
   double adc_temperature;
-  char errbuf[1024];
 
   *pressure = 0.0;
   *temperature = 0.0;
@@ -674,11 +673,11 @@ static int MPL115_read_averaged(double *pressure, double *temperature) {
     if (retries > 0) {
       ERROR("barometer: MPL115_read_averaged - requesting conversion: %s, "
             "will retry at most %d more times",
-            sstrerror(errno, errbuf, sizeof(errbuf)), retries);
+            STRERRNO, retries);
     } else {
       ERROR("barometer: MPL115_read_averaged - requesting conversion: %s, "
             "too many failed retries",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
       return -1;
     }
   }
@@ -697,11 +696,11 @@ static int MPL115_read_averaged(double *pressure, double *temperature) {
     if (retries > 0) {
       ERROR("barometer: MPL115_read_averaged - reading conversion: %s, "
             "will retry at most %d more times",
-            sstrerror(errno, errbuf, sizeof(errbuf)), retries);
+            STRERRNO, retries);
     } else {
       ERROR("barometer: MPL115_read_averaged - reading conversion: %s, "
             "too many failed retries",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
       return -1;
     }
   }
@@ -738,12 +737,11 @@ static int MPL115_read_averaged(double *pressure, double *temperature) {
  */
 static int MPL3115_detect(void) {
   __s32 res;
-  char errbuf[1024];
 
   if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL3115_I2C_ADDRESS) < 0) {
     ERROR("barometer: MPL3115_detect problem setting i2c slave address to "
           "0x%02X: %s",
-          MPL3115_I2C_ADDRESS, sstrerror(errno, errbuf, sizeof(errbuf)));
+          MPL3115_I2C_ADDRESS, STRERRNO);
     return 0;
   }
 
@@ -810,21 +808,18 @@ static int MPL3115_read(double *pressure, double *temperature) {
   __s32 ctrl;
   __u8 data[MPL3115_NUM_CONV_VALS];
   long int tmp_value = 0;
-  char errbuf[1024];
 
   /* Set Active - activate the device from standby */
   res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_CTRL_REG1);
   if (res < 0) {
-    ERROR("barometer: MPL3115_read - cannot read CTRL_REG1: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("barometer: MPL3115_read - cannot read CTRL_REG1: %s", STRERRNO);
     return 1;
   }
   ctrl = res;
   res = i2c_smbus_write_byte_data(i2c_bus_fd, MPL3115_REG_CTRL_REG1,
                                   ctrl | MPL3115_CTRL_REG1_SBYB);
   if (res < 0) {
-    ERROR("barometer: MPL3115_read - problem activating: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("barometer: MPL3115_read - problem activating: %s", STRERRNO);
     return 1;
   }
 
@@ -835,7 +830,7 @@ static int MPL3115_read(double *pressure, double *temperature) {
   res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_STATUS);
   if (res < 0) {
     ERROR("barometer: MPL3115_read - cannot read status register: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return 1;
   }
 
@@ -848,7 +843,7 @@ static int MPL3115_read(double *pressure, double *temperature) {
     res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_STATUS);
     if (res < 0) {
       ERROR("barometer: MPL3115_read - cannot read status register: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
       return 1;
     }
   }
@@ -857,8 +852,7 @@ static int MPL3115_read(double *pressure, double *temperature) {
   res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, MPL3115_REG_OUT_P_MSB,
                                       MPL3115_NUM_CONV_VALS, data);
   if (res < 0) {
-    ERROR("barometer: MPL3115_read - cannot read data registers: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("barometer: MPL3115_read - cannot read data registers: %s", STRERRNO);
     return 1;
   }
 
@@ -888,7 +882,6 @@ static int MPL3115_read(double *pressure, double *temperature) {
 static int MPL3115_init_sensor(void) {
   __s32 res;
   __s8 offset;
-  char errbuf[1024];
 
   /* Reset the sensor. It will reset immediately without ACKing */
   /* the transaction, so no error handling here. */
@@ -906,7 +899,7 @@ static int MPL3115_init_sensor(void) {
   res = i2c_smbus_write_byte_data(i2c_bus_fd, MPL3115_REG_OFF_T, offset);
   if (res < 0) {
     ERROR("barometer: MPL3115_init_sensor - problem setting temp offset: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return -1;
   }
 
@@ -917,7 +910,7 @@ static int MPL3115_init_sensor(void) {
   if (res < 0) {
     ERROR(
         "barometer: MPL3115_init_sensor - problem setting pressure offset: %s",
-        sstrerror(errno, errbuf, sizeof(errbuf)));
+        STRERRNO);
     return -1;
   }
 
@@ -927,7 +920,7 @@ static int MPL3115_init_sensor(void) {
                                       MPL3115_PT_DATA_TDEF);
   if (res < 0) {
     ERROR("barometer: MPL3115_init_sensor - problem setting PT_DATA_CFG: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return -1;
   }
 
@@ -936,7 +929,7 @@ static int MPL3115_init_sensor(void) {
                                   mpl3115_oversample);
   if (res < 0) {
     ERROR("barometer: MPL3115_init_sensor - problem configuring CTRL_REG1: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return -1;
   }
 
@@ -954,12 +947,11 @@ static int MPL3115_init_sensor(void) {
  */
 static int BMP085_detect(void) {
   __s32 res;
-  char errbuf[1024];
 
   if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, BMP085_I2C_ADDRESS) < 0) {
     ERROR("barometer: BMP085_detect - problem setting i2c slave address to "
           "0x%02X: %s",
-          BMP085_I2C_ADDRESS, sstrerror(errno, errbuf, sizeof(errbuf)));
+          BMP085_I2C_ADDRESS, STRERRNO);
     return 0;
   }
 
@@ -971,7 +963,7 @@ static int BMP085_detect(void) {
     res = i2c_smbus_read_byte_data(i2c_bus_fd, BMP085_ADDR_VERSION);
     if (res < 0) {
       ERROR("barometer: BMP085_detect - problem checking chip version: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
       return 0;
     }
     DEBUG("barometer: BMP085_detect - chip version ML:0x%02X AL:0x%02X",
@@ -1033,13 +1025,11 @@ static void BMP085_adjust_oversampling(void) {
 static int BMP085_read_coeffs(void) {
   __s32 res;
   __u8 coeffs[BMP085_NUM_COEFFS];
-  char errbuf[1024];
 
   res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, BMP085_ADDR_COEFFS,
                                       BMP085_NUM_COEFFS, coeffs);
   if (res < 0) {
-    ERROR("barometer: BMP085_read_coeffs - problem reading data: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("barometer: BMP085_read_coeffs - problem reading data: %s", STRERRNO);
     return -1;
   }
 
@@ -1140,15 +1130,13 @@ static int BMP085_read(double *pressure, double *temperature) {
   long adc_pressure;
   long adc_temperature;
 
-  char errbuf[1024];
-
   /* start conversion of temperature */
   res = i2c_smbus_write_byte_data(i2c_bus_fd, BMP085_ADDR_CTRL_REG,
                                   BMP085_CMD_CONVERT_TEMP);
   if (res < 0) {
     ERROR("barometer: BMP085_read - problem requesting temperature conversion: "
           "%s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return 1;
   }
 
@@ -1158,7 +1146,7 @@ static int BMP085_read(double *pressure, double *temperature) {
       i2c_smbus_read_i2c_block_data(i2c_bus_fd, BMP085_ADDR_CONV, 2, measBuff);
   if (res < 0) {
     ERROR("barometer: BMP085_read - problem reading temperature data: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return 1;
   }
 
@@ -1169,7 +1157,7 @@ static int BMP085_read(double *pressure, double *temperature) {
                                   bmp085_cmdCnvPress);
   if (res < 0) {
     ERROR("barometer: BMP085_read - problem requesting pressure conversion: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return 1;
   }
 
@@ -1179,7 +1167,7 @@ static int BMP085_read(double *pressure, double *temperature) {
       i2c_smbus_read_i2c_block_data(i2c_bus_fd, BMP085_ADDR_CONV, 3, measBuff);
   if (res < 0) {
     ERROR("barometer: BMP085_read - problem reading pressure data: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return 1;
   }
 
@@ -1407,7 +1395,7 @@ static int MPL115_collectd_barometer_read(void) {
             config_oversample - 1);
       usleep(20000);
     }
-    avg_initialized = 1;
+    avg_initialized = true;
   }
 
   result = MPL115_read_averaged(&pressure, &temperature);
@@ -1571,7 +1559,6 @@ static int BMP085_collectd_barometer_read(void) {
  * @return Zero when successful.
  */
 static int collectd_barometer_init(void) {
-  char errbuf[1024];
 
   DEBUG("barometer: collectd_barometer_init");
 
@@ -1596,7 +1583,7 @@ static int collectd_barometer_init(void) {
   if (i2c_bus_fd < 0) {
     ERROR("barometer: collectd_barometer_init problem opening I2C bus device "
           "\"%s\": %s (is loaded mod i2c-dev?)",
-          config_device, sstrerror(errno, errbuf, sizeof(errbuf)));
+          config_device, STRERRNO);
     return -1;
   }
 
@@ -1651,7 +1638,7 @@ static int collectd_barometer_init(void) {
     return -1;
   }
 
-  configured = 1;
+  configured = true;
   return 0;
 }
 
index b6dea0f..8e6c4b2 100644 (file)
@@ -25,8 +25,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_MACH_MACH_TYPES_H
 #include <mach/mach_types.h>
@@ -72,9 +72,9 @@
 int battery_read_statefs(
     void); /* defined in battery_statefs; used by StateFS backend */
 
-static _Bool report_percent = 0;
-static _Bool report_degraded = 0;
-static _Bool query_statefs = 0;
+static bool report_percent;
+static bool report_degraded;
+static bool query_statefs;
 
 static void battery_submit2(char const *plugin_instance, /* {{{ */
                             char const *type, char const *type_instance,
@@ -410,7 +410,7 @@ static int read_sysfs_callback(char const *dir, /* {{{ */
   char const *plugin_instance;
   char buffer[32];
   gauge_t v = NAN;
-  _Bool discharging = 0;
+  bool discharging = false;
   int status;
 
   /* Ignore non-battery directories, such as AC power. */
@@ -424,7 +424,7 @@ static int read_sysfs_callback(char const *dir, /* {{{ */
   (void)sysfs_file_to_buffer(dir, power_supply, "status", buffer,
                              sizeof(buffer));
   if (strcasecmp("Discharging", buffer) == 0)
-    discharging = 1;
+    discharging = true;
 
   /* FIXME: This is a dirty hack for backwards compatibility: The battery
    * plugin, for a very long time, has had the plugin_instance
@@ -522,8 +522,8 @@ static int read_acpi_callback(char const *dir, /* {{{ */
   gauge_t capacity_charged = NAN;
   gauge_t capacity_full = NAN;
   gauge_t capacity_design = NAN;
-  _Bool charging = 0;
-  _Bool is_current = 0;
+  bool charging = false;
+  bool is_current = false;
 
   char const *plugin_instance;
   char filename[PATH_MAX];
@@ -560,9 +560,9 @@ static int read_acpi_callback(char const *dir, /* {{{ */
     if ((strcmp(fields[0], "charging") == 0) &&
         (strcmp(fields[1], "state:") == 0)) {
       if (strcmp(fields[2], "charging") == 0)
-        charging = 1;
+        charging = true;
       else
-        charging = 0;
+        charging = false;
       continue;
     }
 
@@ -575,7 +575,7 @@ static int read_acpi_callback(char const *dir, /* {{{ */
       strtogauge(fields[2], &power);
 
       if ((numfields >= 4) && (strcmp("mA", fields[3]) == 0))
-        is_current = 1;
+        is_current = true;
     } else if ((strcmp(fields[0], "remaining") == 0) &&
                (strcmp(fields[1], "capacity:") == 0))
       strtogauge(fields[2], &capacity_charged);
index 149512b..d2c1ed9 100644 (file)
@@ -44,9 +44,9 @@ SOFTWARE.
 
  **/
 
-#include "common.h"
-#include "plugin.h"
 #include "collectd.h"
+#include "plugin.h"
+#include "utils/common/common.h"
 
 #include <stdio.h>
 
index 990e2ca..29f65d4 100644 (file)
@@ -43,8 +43,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <time.h>
 
@@ -73,9 +73,9 @@ typedef int (*list_callback_t)(const char *name, value_t value,
 struct cb_view_s {
   char *name;
 
-  int qtypes;
-  int resolver_stats;
-  int cacherrsets;
+  _Bool qtypes;
+  _Bool resolver_stats;
+  _Bool cacherrsets;
 
   char **zones;
   size_t zones_num;
@@ -104,25 +104,25 @@ typedef struct list_info_ptr_s list_info_ptr_t;
 
 /* FIXME: Enabled by default for backwards compatibility. */
 /* TODO: Remove time parsing code. */
-static _Bool config_parse_time = 1;
-
-static char *url = NULL;
-static int global_opcodes = 1;
-static int global_qtypes = 1;
-static int global_server_stats = 1;
-static int global_zone_maint_stats = 1;
-static int global_resolver_stats = 0;
-static int global_memory_stats = 1;
+static bool config_parse_time = true;
+
+static char *url;
+static _Bool global_opcodes = 1;
+static _Bool global_qtypes = 1;
+static _Bool global_server_stats = 1;
+static _Bool global_zone_maint_stats = 1;
+static _Bool global_resolver_stats;
+static _Bool global_memory_stats = 1;
 static int timeout = -1;
 
-static cb_view_t *views = NULL;
-static size_t views_num = 0;
+static cb_view_t *views;
+static size_t views_num;
 
-static CURL *curl = NULL;
+static CURL *curl;
 
-static char *bind_buffer = NULL;
-static size_t bind_buffer_size = 0;
-static size_t bind_buffer_fill = 0;
+static char *bind_buffer;
+static size_t bind_buffer_size;
+static size_t bind_buffer_fill;
 static char bind_curl_error[CURL_ERROR_SIZE];
 
 /* Translation table for the `nsstats' values. */
@@ -274,9 +274,7 @@ static size_t bind_curl_callback(void *buf, size_t size, /* {{{ */
     return len;
 
   if ((bind_buffer_fill + len) >= bind_buffer_size) {
-    char *temp;
-
-    temp = realloc(bind_buffer, bind_buffer_fill + len + 1);
+    char *temp = realloc(bind_buffer, bind_buffer_fill + len + 1);
     if (temp == NULL) {
       ERROR("bind plugin: realloc failed.");
       return 0;
@@ -335,20 +333,16 @@ static int bind_xml_list_callback(const char *name, /* {{{ */
 
 static int bind_xml_read_derive(xmlDoc *doc, xmlNode *node, /* {{{ */
                                 derive_t *ret_value) {
-  char *str_ptr;
-  value_t value;
-  int status;
-
-  str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+  char *str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
   if (str_ptr == NULL) {
     ERROR("bind plugin: bind_xml_read_derive: xmlNodeListGetString failed.");
     return -1;
   }
 
-  status = parse_value(str_ptr, &value, DS_TYPE_DERIVE);
+  value_t value;
+
+  int status = parse_value(str_ptr, &value, DS_TYPE_DERIVE);
   if (status != 0) {
-    ERROR("bind plugin: Parsing string \"%s\" to derive value failed.",
-          str_ptr);
     xmlFree(str_ptr);
     return -1;
   }
@@ -360,17 +354,15 @@ static int bind_xml_read_derive(xmlDoc *doc, xmlNode *node, /* {{{ */
 
 static int bind_xml_read_gauge(xmlDoc *doc, xmlNode *node, /* {{{ */
                                gauge_t *ret_value) {
-  char *str_ptr, *end_ptr;
-  double value;
-
-  str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+  char *str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
   if (str_ptr == NULL) {
     ERROR("bind plugin: bind_xml_read_gauge: xmlNodeListGetString failed.");
     return -1;
   }
 
+  char *end_ptr;
   errno = 0;
-  value = strtod(str_ptr, &end_ptr);
+  double value = strtod(str_ptr, &end_ptr);
   xmlFree(str_ptr);
   if (str_ptr == end_ptr || errno) {
     if (errno && (value < 0))
@@ -389,13 +381,8 @@ static int bind_xml_read_gauge(xmlDoc *doc, xmlNode *node, /* {{{ */
 static int bind_xml_read_timestamp(const char *xpath_expression, /* {{{ */
                                    xmlDoc *doc, xmlXPathContext *xpathCtx,
                                    time_t *ret_value) {
-  xmlXPathObject *xpathObj = NULL;
-  xmlNode *node;
-  char *str_ptr;
-  char *tmp;
-  struct tm tm = {0};
-
-  xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
+  xmlXPathObject *xpathObj =
+      xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
   if (xpathObj == NULL) {
     ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
           xpath_expression);
@@ -413,7 +400,7 @@ static int bind_xml_read_timestamp(const char *xpath_expression, /* {{{ */
            xpath_expression, xpathObj->nodesetval->nodeNr);
   }
 
-  node = xpathObj->nodesetval->nodeTab[0];
+  xmlNode *node = xpathObj->nodesetval->nodeTab[0];
 
   if (node->xmlChildrenNode == NULL) {
     ERROR("bind plugin: bind_xml_read_timestamp: "
@@ -422,14 +409,15 @@ static int bind_xml_read_timestamp(const char *xpath_expression, /* {{{ */
     return -1;
   }
 
-  str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+  char *str_ptr = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
   if (str_ptr == NULL) {
     ERROR("bind plugin: bind_xml_read_timestamp: xmlNodeListGetString failed.");
     xmlXPathFreeObject(xpathObj);
     return -1;
   }
 
-  tmp = strptime(str_ptr, "%Y-%m-%dT%T", &tm);
+  struct tm tm = {0};
+  char *tmp = strptime(str_ptr, "%Y-%m-%dT%T", &tm);
   xmlFree(str_ptr);
   if (tmp == NULL) {
     ERROR("bind plugin: bind_xml_read_timestamp: strptime failed.");
@@ -440,18 +428,14 @@ static int bind_xml_read_timestamp(const char *xpath_expression, /* {{{ */
 #if HAVE_TIMEGM
   time_t t = timegm(&tm);
   if (t == ((time_t)-1)) {
-    char errbuf[1024];
-    ERROR("bind plugin: timegm() failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("bind plugin: timegm() failed: %s", STRERRNO);
     return -1;
   }
   *ret_value = t;
 #else
   time_t t = mktime(&tm);
   if (t == ((time_t)-1)) {
-    char errbuf[1024];
-    ERROR("bind plugin: mktime() failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("bind plugin: mktime() failed: %s", STRERRNO);
     return -1;
   }
   /* mktime assumes that tm is local time. Luckily, it also sets timezone to
@@ -478,25 +462,23 @@ static int bind_parse_generic_name_value(const char *xpath_expression, /* {{{ */
                                          void *user_data, xmlDoc *doc,
                                          xmlXPathContext *xpathCtx,
                                          time_t current_time, int ds_type) {
-  xmlXPathObject *xpathObj = NULL;
-  int num_entries;
-
-  xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
+  xmlXPathObject *xpathObj =
+      xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
   if (xpathObj == NULL) {
     ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
           xpath_expression);
     return -1;
   }
 
-  num_entries = 0;
+  int num_entries = 0;
   /* Iterate over all matching nodes. */
   for (int i = 0; xpathObj->nodesetval && (i < xpathObj->nodesetval->nodeNr);
        i++) {
+
     xmlNode *name_node = NULL;
     xmlNode *counter = NULL;
-    xmlNode *parent;
 
-    parent = xpathObj->nodesetval->nodeTab[i];
+    xmlNode *parent = xpathObj->nodesetval->nodeTab[i];
     DEBUG("bind plugin: bind_parse_generic_name_value: parent->name = %s;",
           (char *)parent->name);
 
@@ -559,32 +541,29 @@ static int bind_parse_generic_value_list(const char *xpath_expression, /* {{{ */
                                          void *user_data, xmlDoc *doc,
                                          xmlXPathContext *xpathCtx,
                                          time_t current_time, int ds_type) {
-  xmlXPathObject *xpathObj = NULL;
-  int num_entries;
-
-  xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
+  xmlXPathObject *xpathObj =
+      xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
   if (xpathObj == NULL) {
     ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
           xpath_expression);
     return -1;
   }
 
-  num_entries = 0;
+  int num_entries = 0;
   /* Iterate over all matching nodes. */
   for (int i = 0; xpathObj->nodesetval && (i < xpathObj->nodesetval->nodeNr);
        i++) {
     /* Iterate over all child nodes. */
     for (xmlNode *child = xpathObj->nodesetval->nodeTab[i]->xmlChildrenNode;
          child != NULL; child = child->next) {
-      char *node_name;
-      value_t value;
-      int status;
 
       if (child->type != XML_ELEMENT_NODE)
         continue;
 
-      node_name = (char *)child->name;
+      char *node_name = (char *)child->name;
 
+      value_t value;
+      int status;
       if (ds_type == DS_TYPE_GAUGE)
         status = bind_xml_read_gauge(doc, child, &value.gauge);
       else
@@ -621,17 +600,16 @@ static int bind_parse_generic_name_attr_value_list(
     const char *xpath_expression, /* {{{ */
     list_callback_t list_callback, void *user_data, xmlDoc *doc,
     xmlXPathContext *xpathCtx, time_t current_time, int ds_type) {
-  xmlXPathObject *xpathObj = NULL;
-  int num_entries;
 
-  xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
+  xmlXPathObject *xpathObj =
+      xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
   if (xpathObj == NULL) {
     ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
           xpath_expression);
     return -1;
   }
 
-  num_entries = 0;
+  int num_entries = 0;
   /* Iterate over all matching nodes. */
   for (int i = 0; xpathObj->nodesetval && (i < xpathObj->nodesetval->nodeNr);
        i++) {
@@ -644,15 +622,15 @@ static int bind_parse_generic_name_attr_value_list(
       if (strncmp("counter", (char *)child->name, strlen("counter")) != 0)
         continue;
 
-      char *attr_name;
-      value_t value;
-      int status;
-
-      attr_name = (char *)xmlGetProp(child, BAD_CAST "name");
+      char *attr_name = (char *)xmlGetProp(child, BAD_CAST "name");
       if (attr_name == NULL) {
         DEBUG("bind plugin: found <counter> without name.");
         continue;
       }
+
+      value_t value;
+      int status;
+
       if (ds_type == DS_TYPE_GAUGE)
         status = bind_xml_read_gauge(doc, child, &value.gauge);
       else
@@ -681,9 +659,7 @@ static int bind_parse_generic_name_attr_value_list(
 static int bind_xml_stats_handle_zone(int version, xmlDoc *doc, /* {{{ */
                                       xmlXPathContext *path_ctx, xmlNode *node,
                                       cb_view_t *view, time_t current_time) {
-  xmlXPathObject *path_obj;
   char *zone_name = NULL;
-  size_t j;
 
   if (version >= 3) {
     char *n = (char *)xmlGetProp(node, BAD_CAST "name");
@@ -695,7 +671,8 @@ static int bind_xml_stats_handle_zone(int version, xmlDoc *doc, /* {{{ */
     xmlFree(n);
     xmlFree(c);
   } else {
-    path_obj = xmlXPathEvalExpression(BAD_CAST "name", path_ctx);
+    xmlXPathObject *path_obj =
+        xmlXPathEvalExpression(BAD_CAST "name", path_ctx);
     if (path_obj == NULL) {
       ERROR("bind plugin: xmlXPathEvalExpression failed.");
       return -1;
@@ -716,13 +693,13 @@ static int bind_xml_stats_handle_zone(int version, xmlDoc *doc, /* {{{ */
     return -1;
   }
 
+  size_t j;
   for (j = 0; j < view->zones_num; j++) {
     if (strcasecmp(zone_name, view->zones[j]) == 0)
       break;
   }
 
   xmlFree(zone_name);
-  zone_name = NULL;
 
   if (j >= view->zones_num)
     return 0;
@@ -767,16 +744,14 @@ static int bind_xml_stats_handle_zone(int version, xmlDoc *doc, /* {{{ */
 static int bind_xml_stats_search_zones(int version, xmlDoc *doc, /* {{{ */
                                        xmlXPathContext *path_ctx, xmlNode *node,
                                        cb_view_t *view, time_t current_time) {
-  xmlXPathObject *zone_nodes = NULL;
-  xmlXPathContext *zone_path_context;
-
-  zone_path_context = xmlXPathNewContext(doc);
+  xmlXPathContext *zone_path_context = xmlXPathNewContext(doc);
   if (zone_path_context == NULL) {
     ERROR("bind plugin: xmlXPathNewContext failed.");
     return -1;
   }
 
-  zone_nodes = xmlXPathEvalExpression(BAD_CAST "zones/zone", path_ctx);
+  xmlXPathObject *zone_nodes =
+      xmlXPathEvalExpression(BAD_CAST "zones/zone", path_ctx);
   if (zone_nodes == NULL) {
     ERROR("bind plugin: Cannot find any <view> tags.");
     xmlXPathFreeContext(zone_path_context);
@@ -821,8 +796,8 @@ static int bind_xml_stats_handle_view(int version, xmlDoc *doc, /* {{{ */
     xmlFree(view_name);
     view_name = NULL;
   } else {
-    xmlXPathObject *path_obj;
-    path_obj = xmlXPathEvalExpression(BAD_CAST "name", path_ctx);
+    xmlXPathObject *path_obj =
+        xmlXPathEvalExpression(BAD_CAST "name", path_ctx);
     if (path_obj == NULL) {
       ERROR("bind plugin: xmlXPathEvalExpression failed.");
       return -1;
@@ -931,18 +906,15 @@ static int bind_xml_stats_handle_view(int version, xmlDoc *doc, /* {{{ */
 
 static int bind_xml_stats_search_views(int version, xmlDoc *doc, /* {{{ */
                                        xmlXPathContext *xpathCtx,
-                                       xmlNode *statsnode,
                                        time_t current_time) {
-  xmlXPathObject *view_nodes = NULL;
-  xmlXPathContext *view_path_context;
-
-  view_path_context = xmlXPathNewContext(doc);
+  xmlXPathContext *view_path_context = xmlXPathNewContext(doc);
   if (view_path_context == NULL) {
     ERROR("bind plugin: xmlXPathNewContext failed.");
     return -1;
   }
 
-  view_nodes = xmlXPathEvalExpression(BAD_CAST "views/view", xpathCtx);
+  xmlXPathObject *view_nodes =
+      xmlXPathEvalExpression(BAD_CAST "views/view", xpathCtx);
   if (view_nodes == NULL) {
     ERROR("bind plugin: Cannot find any <view> tags.");
     xmlXPathFreeContext(view_path_context);
@@ -950,9 +922,7 @@ static int bind_xml_stats_search_views(int version, xmlDoc *doc, /* {{{ */
   }
 
   for (int i = 0; i < view_nodes->nodesetval->nodeNr; i++) {
-    xmlNode *node;
-
-    node = view_nodes->nodesetval->nodeTab[i];
+    xmlNode *node = view_nodes->nodesetval->nodeTab[i];
     assert(node != NULL);
 
     view_path_context->node = node;
@@ -967,8 +937,7 @@ static int bind_xml_stats_search_views(int version, xmlDoc *doc, /* {{{ */
 } /* }}} int bind_xml_stats_search_views */
 
 static void bind_xml_stats_v3(xmlDoc *doc, /* {{{ */
-                              xmlXPathContext *xpathCtx, xmlNode *statsnode,
-                              time_t current_time) {
+                              xmlXPathContext *xpathCtx, time_t current_time) {
   /* XPath:     server/counters[@type='opcode']
    * Variables: QUERY, IQUERY, NOTIFY, UPDATE, ...
    * Layout v3:
@@ -1086,7 +1055,7 @@ static void bind_xml_stats_v3(xmlDoc *doc, /* {{{ */
 } /* }}} bind_xml_stats_v3 */
 
 static void bind_xml_stats_v1_v2(int version, xmlDoc *doc, /* {{{ */
-                                 xmlXPathContext *xpathCtx, xmlNode *statsnode,
+                                 xmlXPathContext *xpathCtx,
                                  time_t current_time) {
   /* XPath:     server/requests/opcode, server/counters[@type='opcode']
    * Variables: QUERY, IQUERY, NOTIFY, UPDATE, ...
@@ -1256,14 +1225,13 @@ static void bind_xml_stats_v1_v2(int version, xmlDoc *doc, /* {{{ */
 static int bind_xml_stats(int version, xmlDoc *doc, /* {{{ */
                           xmlXPathContext *xpathCtx, xmlNode *statsnode) {
   time_t current_time = 0;
-  int status;
 
   xpathCtx->node = statsnode;
 
   /* TODO: Check `server/boot-time' to recognize server restarts. */
 
-  status = bind_xml_read_timestamp("server/current-time", doc, xpathCtx,
-                                   &current_time);
+  int status = bind_xml_read_timestamp("server/current-time", doc, xpathCtx,
+                                       &current_time);
   if (status != 0) {
     ERROR("bind plugin: Reading `server/current-time' failed.");
     return -1;
@@ -1271,9 +1239,9 @@ static int bind_xml_stats(int version, xmlDoc *doc, /* {{{ */
   DEBUG("bind plugin: Current server time is %i.", (int)current_time);
 
   if (version == 3) {
-    bind_xml_stats_v3(doc, xpathCtx, statsnode, current_time);
+    bind_xml_stats_v3(doc, xpathCtx, current_time);
   } else {
-    bind_xml_stats_v1_v2(version, doc, xpathCtx, statsnode, current_time);
+    bind_xml_stats_v1_v2(version, doc, xpathCtx, current_time);
   }
 
   /* XPath:  memory/summary
@@ -1299,26 +1267,22 @@ static int bind_xml_stats(int version, xmlDoc *doc, /* {{{ */
   }
 
   if (views_num > 0)
-    bind_xml_stats_search_views(version, doc, xpathCtx, statsnode,
-                                current_time);
+    bind_xml_stats_search_views(version, doc, xpathCtx, current_time);
 
   return 0;
 } /* }}} int bind_xml_stats */
 
 static int bind_xml(const char *data) /* {{{ */
 {
-  xmlDoc *doc = NULL;
-  xmlXPathContext *xpathCtx = NULL;
-  xmlXPathObject *xpathObj = NULL;
   int ret = -1;
 
-  doc = xmlParseMemory(data, strlen(data));
+  xmlDoc *doc = xmlParseMemory(data, strlen(data));
   if (doc == NULL) {
     ERROR("bind plugin: xmlParseMemory failed.");
     return -1;
   }
 
-  xpathCtx = xmlXPathNewContext(doc);
+  xmlXPathContext *xpathCtx = xmlXPathNewContext(doc);
   if (xpathCtx == NULL) {
     ERROR("bind plugin: xmlXPathNewContext failed.");
     xmlFreeDoc(doc);
@@ -1329,7 +1293,8 @@ static int bind_xml(const char *data) /* {{{ */
   // version 3.* of statistics XML (since BIND9.9)
   //
 
-  xpathObj = xmlXPathEvalExpression(BAD_CAST "/statistics", xpathCtx);
+  xmlXPathObject *xpathObj =
+      xmlXPathEvalExpression(BAD_CAST "/statistics", xpathCtx);
   if (xpathObj == NULL || xpathObj->nodesetval == NULL ||
       xpathObj->nodesetval->nodeNr == 0) {
     DEBUG("bind plugin: Statistics appears not to be v3");
@@ -1443,33 +1408,15 @@ static int bind_xml(const char *data) /* {{{ */
   return ret;
 } /* }}} int bind_xml */
 
-static int bind_config_set_bool(const char *name, int *var, /* {{{ */
-                                oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) {
-    WARNING("bind plugin: The `%s' option needs "
-            "exactly one boolean argument.",
-            name);
-    return -1;
-  }
-
-  if (ci->values[0].value.boolean)
-    *var = 1;
-  else
-    *var = 0;
-  return 0;
-} /* }}} int bind_config_set_bool */
-
 static int bind_config_add_view_zone(cb_view_t *view, /* {{{ */
                                      oconfig_item_t *ci) {
-  char **tmp;
-
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
     WARNING("bind plugin: The `Zone' option needs "
             "exactly one string argument.");
     return -1;
   }
 
-  tmp = realloc(view->zones, sizeof(char *) * (view->zones_num + 1));
+  char **tmp = realloc(view->zones, sizeof(char *) * (view->zones_num + 1));
   if (tmp == NULL) {
     ERROR("bind plugin: realloc failed.");
     return -1;
@@ -1488,14 +1435,12 @@ static int bind_config_add_view_zone(cb_view_t *view, /* {{{ */
 
 static int bind_config_add_view(oconfig_item_t *ci) /* {{{ */
 {
-  cb_view_t *tmp;
-
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
     WARNING("bind plugin: `View' blocks need exactly one string argument.");
     return -1;
   }
 
-  tmp = realloc(views, sizeof(*views) * (views_num + 1));
+  cb_view_t *tmp = realloc(views, sizeof(*views) * (views_num + 1));
   if (tmp == NULL) {
     ERROR("bind plugin: realloc failed.");
     return -1;
@@ -1521,11 +1466,11 @@ static int bind_config_add_view(oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("QTypes", child->key) == 0)
-      bind_config_set_bool("QTypes", &tmp->qtypes, child);
+      cf_util_get_boolean(child, &tmp->qtypes);
     else if (strcasecmp("ResolverStats", child->key) == 0)
-      bind_config_set_bool("ResolverStats", &tmp->resolver_stats, child);
+      cf_util_get_boolean(child, &tmp->resolver_stats);
     else if (strcasecmp("CacheRRSets", child->key) == 0)
-      bind_config_set_bool("CacheRRSets", &tmp->cacherrsets, child);
+      cf_util_get_boolean(child, &tmp->cacherrsets);
     else if (strcasecmp("Zone", child->key) == 0)
       bind_config_add_view_zone(tmp, child);
     else {
@@ -1545,27 +1490,19 @@ static int bind_config(oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("Url", child->key) == 0) {
-      if ((child->values_num != 1) ||
-          (child->values[0].type != OCONFIG_TYPE_STRING)) {
-        WARNING("bind plugin: The `Url' option needs "
-                "exactly one string argument.");
-        return -1;
-      }
-
-      sfree(url);
-      url = strdup(child->values[0].value.string);
+      cf_util_get_string(child, &url);
     } else if (strcasecmp("OpCodes", child->key) == 0)
-      bind_config_set_bool("OpCodes", &global_opcodes, child);
+      cf_util_get_boolean(child, &global_opcodes);
     else if (strcasecmp("QTypes", child->key) == 0)
-      bind_config_set_bool("QTypes", &global_qtypes, child);
+      cf_util_get_boolean(child, &global_qtypes);
     else if (strcasecmp("ServerStats", child->key) == 0)
-      bind_config_set_bool("ServerStats", &global_server_stats, child);
+      cf_util_get_boolean(child, &global_server_stats);
     else if (strcasecmp("ZoneMaintStats", child->key) == 0)
-      bind_config_set_bool("ZoneMaintStats", &global_zone_maint_stats, child);
+      cf_util_get_boolean(child, &global_zone_maint_stats);
     else if (strcasecmp("ResolverStats", child->key) == 0)
-      bind_config_set_bool("ResolverStats", &global_resolver_stats, child);
+      cf_util_get_boolean(child, &global_resolver_stats);
     else if (strcasecmp("MemoryStats", child->key) == 0)
-      bind_config_set_bool("MemoryStats", &global_memory_stats, child);
+      cf_util_get_boolean(child, &global_memory_stats);
     else if (strcasecmp("View", child->key) == 0)
       bind_config_add_view(child);
     else if (strcasecmp("ParseTime", child->key) == 0)
@@ -1610,8 +1547,6 @@ static int bind_init(void) /* {{{ */
 
 static int bind_read(void) /* {{{ */
 {
-  int status;
-
   if (curl == NULL) {
     ERROR("bind plugin: I don't have a CURL object.");
     return -1;
@@ -1626,7 +1561,7 @@ static int bind_read(void) /* {{{ */
     return -1;
   }
 
-  status = bind_xml(bind_buffer);
+  int status = bind_xml(bind_buffer);
   if (status != 0)
     return -1;
   else
index 5ea9049..19a09d8 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <arpa/inet.h>
 #include <errno.h>
@@ -148,7 +148,7 @@ enum perfcounter_type_d {
 };
 
 /** Give user option to use default (long run = since daemon started) avg */
-static int long_run_latency_avg = 0;
+static int long_run_latency_avg;
 
 /**
  * Give user option to use default type for special cases -
@@ -161,10 +161,10 @@ static int long_run_latency_avg = 0;
 static int convert_special_metrics = 1;
 
 /** Array of daemons to monitor */
-static struct ceph_daemon **g_daemons = NULL;
+static struct ceph_daemon **g_daemons;
 
 /** Number of elements in g_daemons */
-static size_t g_num_daemons = 0;
+static size_t g_num_daemons;
 
 /**
  * A set of data that we build up in memory while parsing the JSON.
@@ -251,7 +251,7 @@ static int ceph_cb_boolean(void *ctx, int bool_val) { return CEPH_CB_CONTINUE; }
     if (dest_size > dest_len) {                                                \
       sstrncpy((dest) + dest_len, (src), dest_size - dest_len);                \
     }                                                                          \
-    (dest)[dest_size - 1] = 0;                                                 \
+    (dest)[dest_size - 1] = '\0';                                              \
   } while (0)
 
 static int ceph_cb_number(void *ctx, const char *number_val,
@@ -350,7 +350,7 @@ static int ceph_cb_map_key(void *ctx, const unsigned char *key,
   }
 
   memmove(state->key, key, sz - 1);
-  state->key[sz - 1] = 0;
+  state->key[sz - 1] = '\0';
 
   return CEPH_CB_CONTINUE;
 }
@@ -406,8 +406,8 @@ static int compact_ds_name(char *buffer, size_t buffer_size, char const *src) {
   size_t src_len;
   char *ptr = buffer;
   size_t ptr_size = buffer_size;
-  _Bool append_plus = 0;
-  _Bool append_minus = 0;
+  bool append_plus = false;
+  bool append_minus = false;
 
   if ((buffer == NULL) || (buffer_size <= strlen("Minus")) || (src == NULL))
     return EINVAL;
@@ -417,11 +417,11 @@ static int compact_ds_name(char *buffer, size_t buffer_size, char const *src) {
 
   /* Remove trailing "+" and "-". */
   if (src_copy[src_len - 1] == '+') {
-    append_plus = 1;
+    append_plus = true;
     src_len--;
     src_copy[src_len] = 0;
   } else if (src_copy[src_len - 1] == '-') {
-    append_minus = 1;
+    append_minus = true;
     src_len--;
     src_copy[src_len] = 0;
   }
@@ -472,19 +472,19 @@ static int compact_ds_name(char *buffer, size_t buffer_size, char const *src) {
   return 0;
 }
 
-static _Bool has_suffix(char const *str, char const *suffix) {
+static bool has_suffix(char const *str, char const *suffix) {
   size_t str_len = strlen(str);
   size_t suffix_len = strlen(suffix);
   size_t offset;
 
   if (suffix_len > str_len)
-    return 0;
+    return false;
   offset = str_len - suffix_len;
 
   if (strcmp(str + offset, suffix) == 0)
-    return 1;
+    return true;
 
-  return 0;
+  return false;
 }
 
 static void cut_suffix(char *buffer, size_t buffer_size, char const *str,
@@ -1032,7 +1032,7 @@ static void cconn_close(struct cconn *io) {
 static int cconn_process_data(struct cconn *io, yajl_struct *yajl,
                               yajl_handle hand) {
   int ret;
-  struct values_tmp *vtmp = calloc(1, sizeof(struct values_tmp) * 1);
+  struct values_tmp *vtmp = calloc(1, sizeof(*vtmp));
   if (!vtmp) {
     return -ENOMEM;
   }
@@ -1142,8 +1142,8 @@ static int cconn_validate_revents(struct cconn *io, int revents) {
 }
 
 /** Handle a network event for a connection */
-static int cconn_handle_event(struct cconn *io) {
-  int ret;
+static ssize_t cconn_handle_event(struct cconn *io) {
+  ssize_t ret;
   switch (io->state) {
   case CSTATE_UNCONNECTED:
     ERROR("ceph plugin: cconn_handle_event(name=%s) got to illegal "
@@ -1158,7 +1158,7 @@ static int cconn_handle_event(struct cconn *io) {
     size_t cmd_len = strlen(cmd);
     RETRY_ON_EINTR(
         ret, write(io->asok, ((char *)&cmd) + io->amt, cmd_len - io->amt));
-    DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,amt=%d,ret=%d)",
+    DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,amt=%d,ret=%zd)",
           io->d->name, io->state, io->amt, ret);
     if (ret < 0) {
       return ret;
@@ -1180,7 +1180,7 @@ static int cconn_handle_event(struct cconn *io) {
   case CSTATE_READ_VERSION: {
     RETRY_ON_EINTR(ret, read(io->asok, ((char *)(&io->d->version)) + io->amt,
                              sizeof(io->d->version) - io->amt));
-    DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)",
+    DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%zd)",
           io->d->name, io->state, ret);
     if (ret < 0) {
       return ret;
@@ -1206,7 +1206,7 @@ static int cconn_handle_event(struct cconn *io) {
   case CSTATE_READ_AMT: {
     RETRY_ON_EINTR(ret, read(io->asok, ((char *)(&io->json_len)) + io->amt,
                              sizeof(io->json_len) - io->amt));
-    DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)",
+    DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%zd)",
           io->d->name, io->state, ret);
     if (ret < 0) {
       return ret;
@@ -1227,7 +1227,7 @@ static int cconn_handle_event(struct cconn *io) {
   case CSTATE_READ_JSON: {
     RETRY_ON_EINTR(ret,
                    read(io->asok, io->json + io->amt, io->json_len - io->amt));
-    DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)",
+    DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%zd)",
           io->d->name, io->state, ret);
     if (ret < 0) {
       return ret;
@@ -1296,8 +1296,8 @@ static int cconn_prepare(struct cconn *io, struct pollfd *fds) {
  */
 static int milli_diff(const struct timeval *t1, const struct timeval *t2) {
   int64_t ret;
-  int sec_diff = t1->tv_sec - t2->tv_sec;
-  int usec_diff = t1->tv_usec - t2->tv_usec;
+  long sec_diff = t1->tv_sec - t2->tv_sec;
+  long usec_diff = t1->tv_usec - t2->tv_usec;
   ret = usec_diff / 1000;
   ret += (sec_diff * 1000);
   return (ret > INT_MAX) ? INT_MAX : ((ret < INT_MIN) ? INT_MIN : (int)ret);
@@ -1305,8 +1305,9 @@ static int milli_diff(const struct timeval *t1, const struct timeval *t2) {
 
 /** This handles the actual network I/O to talk to the Ceph daemons.
  */
-static int cconn_main_loop(uint32_t request_type) {
-  int ret, some_unreachable = 0;
+static ssize_t cconn_main_loop(uint32_t request_type) {
+  int some_unreachable = 0;
+  ssize_t ret;
   struct timeval end_tv;
   struct cconn io_array[g_num_daemons];
 
@@ -1343,7 +1344,7 @@ static int cconn_main_loop(uint32_t request_type) {
       struct cconn *io = io_array + i;
       ret = cconn_prepare(io, fds + nfds);
       if (ret < 0) {
-        WARNING("ceph plugin: cconn_prepare(name=%s,i=%zu,st=%d)=%d",
+        WARNING("ceph plugin: cconn_prepare(name=%s,i=%" PRIsz ",st=%d)=%zd",
                 io->d->name, i, io->state, ret);
         cconn_close(io);
         io->request_type = ASOK_REQ_NONE;
@@ -1367,7 +1368,7 @@ static int cconn_main_loop(uint32_t request_type) {
     }
     RETRY_ON_EINTR(ret, poll(fds, nfds, diff));
     if (ret < 0) {
-      ERROR("ceph plugin: poll(2) error: %d", ret);
+      ERROR("ceph plugin: poll(2) error: %zd", ret);
       goto done;
     }
     for (int i = 0; i < nfds; ++i) {
@@ -1388,7 +1389,7 @@ static int cconn_main_loop(uint32_t request_type) {
         ret = cconn_handle_event(io);
         if (ret) {
           WARNING("ceph plugin: cconn_handle_event(name=%s,"
-                  "i=%d,st=%d): error %d",
+                  "i=%d,st=%d): error %zd",
                   io->d->name, i, io->state, ret);
           cconn_close(io);
           io->request_type = ASOK_REQ_NONE;
@@ -1409,7 +1410,7 @@ done:
   return ret;
 }
 
-static int ceph_read(void) { return cconn_main_loop(ASOK_REQ_DATA); }
+static int ceph_read(void) { return (int)cconn_main_loop(ASOK_REQ_DATA); }
 
 /******* lifecycle *******/
 static int ceph_init(void) {
@@ -1436,7 +1437,7 @@ static int ceph_init(void) {
     return ENOENT;
   }
 
-  return cconn_main_loop(ASOK_REQ_VERSION);
+  return (int)cconn_main_loop(ASOK_REQ_VERSION);
 }
 
 static int ceph_shutdown(void) {
index 4546773..e403292 100644 (file)
@@ -39,7 +39,7 @@ static int test_handler(void *user, char const *val, char const *key) {
   size_t i;
 
   char status[1024];
-  _Bool ok;
+  bool ok;
 
   /* special case for latency metrics. */
   if (strcmp("filestore.example_latency", key) == 0)
@@ -47,7 +47,7 @@ static int test_handler(void *user, char const *val, char const *key) {
 
   snprintf(status, sizeof(status),
            "unexpected call: test_handler(\"%s\") = \"%s\"", key, val);
-  ok = 0;
+  ok = false;
 
   for (i = 0; i < t->cases_num; i++) {
     if (strcmp(key, t->cases[i].key) != 0)
@@ -57,12 +57,12 @@ static int test_handler(void *user, char const *val, char const *key) {
       snprintf(status, sizeof(status),
                "test_handler(\"%s\") = \"%s\", want \"%s\"", key, val,
                t->cases[i].value);
-      ok = 0;
+      ok = false;
       break;
     }
 
     snprintf(status, sizeof(status), "test_handler(\"%s\") = \"%s\"", key, val);
-    ok = 1;
+    ok = true;
     break;
   }
 
index 18e489d..8925239 100644 (file)
 
 #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);
 
-static ignorelist_t *il_cgroup = NULL;
+static ignorelist_t *il_cgroup;
 
 __attribute__((nonnull(1))) __attribute__((nonnull(2))) static void
 cgroups_submit_one(char const *plugin_instance, char const *type_instance,
@@ -79,9 +79,7 @@ static int read_cpuacct_procs(const char *dirname, char const *cgroup_name,
            cgroup_name);
   fh = fopen(abs_path, "r");
   if (fh == NULL) {
-    char errbuf[1024];
-    ERROR("cgroups plugin: fopen (\"%s\") failed: %s", abs_path,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("cgroups plugin: fopen (\"%s\") failed: %s", abs_path, STRERRNO);
     return -1;
   }
 
@@ -114,7 +112,7 @@ static int read_cpuacct_procs(const char *dirname, char const *cgroup_name,
 
     /* Strip colon off the first column, if found */
     if (key[key_len - 1] == ':')
-      key[key_len - 1] = 0;
+      key[key_len - 1] = '\0';
 
     status = parse_value(fields[1], &value, DS_TYPE_DERIVE);
     if (status != 0)
@@ -183,7 +181,7 @@ static int cgroups_config(const char *key, const char *value) {
 
 static int cgroups_read(void) {
   cu_mount_t *mnt_list = NULL;
-  _Bool cgroup_found = 0;
+  bool cgroup_found = false;
 
   if (cu_mount_getlist(&mnt_list) == NULL) {
     ERROR("cgroups plugin: cu_mount_getlist failed.");
@@ -201,7 +199,7 @@ static int cgroups_read(void) {
     walk_directory(mnt_ptr->dir, read_cpuacct_root,
                    /* user_data = */ NULL,
                    /* include_hidden = */ 0);
-    cgroup_found = 1;
+    cgroup_found = true;
     /* It doesn't make sense to check other cpuacct mount-points
      * (if any), they contain the same data. */
     break;
index 6fb369a..65e3c4c 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h" /* auxiliary functions */
-#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+#include "plugin.h"              /* plugin_register_*, plugin_dispatch_values */
+#include "utils/common/common.h" /* auxiliary functions */
 
 #if HAVE_NETDB_H
 #include <netdb.h> /* struct addrinfo */
 #include <arpa/inet.h> /* ntohs/ntohl */
 #endif
 
+/* AIX doesn't have MSG_DONTWAIT */
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT MSG_NONBLOCK
+#endif
+
 #define CONFIG_KEY_HOST "Host"
 #define CONFIG_KEY_PORT "Port"
 #define CONFIG_KEY_TIMEOUT "Timeout"
@@ -440,6 +445,15 @@ static int chrony_recv_response(tChrony_Response *p_resp,
   }
 }
 
+static void chrony_flush_recv_queue(void) {
+  char buf[1];
+
+  if (g_chrony_is_connected) {
+    while (recv(g_chrony_socket, buf, sizeof(buf), MSG_DONTWAIT) > 0)
+      ;
+  }
+}
+
 static int chrony_query(const int p_command, tChrony_Request *p_req,
                         tChrony_Response *p_resp, size_t *p_resp_size) {
   /* Check connection. We simply perform one try as collectd already handles
@@ -964,6 +978,9 @@ static int chrony_read(void) {
     g_chrony_seq_is_initialized = 1;
   }
 
+  /* Ignore late responses that may have been received */
+  chrony_flush_recv_queue();
+
   /* Get daemon stats */
   rc = chrony_request_daemon_stats();
   if (rc != CHRONY_RC_OK)
index c65966b..b878657 100644 (file)
@@ -73,7 +73,7 @@ Each line beginning with a C<#> (hash mark) is ignored.
 =item B<PUTVAL> I<Identifier> [I<OptionList>] I<Valuelist>
 
 Submits one or more values (identified by I<Identifier>, see below) to the
-daemon which will dispatch it to all it's write-plugins.
+daemon which will dispatch it to all its write-plugins.
 
 An I<Identifier> is of the form
 C<I<host>B</>I<plugin>B<->I<instance>B</>I<type>B<->I<instance>> with both
index 7a25655..f5e43aa 100644 (file)
@@ -150,12 +150,12 @@ L<lua(1)>,
 =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>.
index 89f73b8..0629849 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -93,17 +94,17 @@ typedef struct range_s range_t;
 extern char *optarg;
 extern int optind, opterr, optopt;
 
-static char *socket_file_g = NULL;
-static char *value_string_g = NULL;
-static char *hostname_g = NULL;
+static char *socket_file_g;
+static char *value_string_g;
+static char *hostname_g;
 
 static range_t range_critical_g;
 static range_t range_warning_g;
 static int consolitation_g = CON_NONE;
-static _Bool nan_is_error_g = 0;
+static bool nan_is_error_g;
 
-static char **match_ds_g = NULL;
-static size_t match_ds_num_g = 0;
+static char **match_ds_g;
+static size_t match_ds_num_g;
 
 /* `strdup' is an XSI extension. I don't want to pull in all of XSI just for
  * that, so here's an own implementation.. It's easy enough. The GCC attributes
@@ -115,7 +116,7 @@ cn_strdup(const char *str) /* {{{ */
   char *ret;
 
   strsize = strlen(str) + 1;
-  ret = (char *)malloc(strsize);
+  ret = malloc(strsize);
   if (ret != NULL)
     memcpy(ret, str, strsize);
   return ret;
@@ -129,13 +130,13 @@ static int filter_ds(size_t *values_num, double **values,
   if (match_ds_g == NULL)
     return RET_OKAY;
 
-  new_values = (gauge_t *)calloc(match_ds_num_g, sizeof(*new_values));
+  new_values = calloc(match_ds_num_g, sizeof(*new_values));
   if (new_values == NULL) {
     fprintf(stderr, "calloc failed: %s\n", strerror(errno));
     return RET_UNKNOWN;
   }
 
-  new_names = (char **)calloc(match_ds_num_g, sizeof(*new_names));
+  new_names = calloc(match_ds_num_g, sizeof(*new_names));
   if (new_names == NULL) {
     fprintf(stderr, "calloc failed: %s\n", strerror(errno));
     free(new_values);
@@ -526,7 +527,7 @@ static int do_check(lcc_connection_t *connection) {
   int status;
 
   snprintf(ident_str, sizeof(ident_str), "%s/%s", hostname_g, value_string_g);
-  ident_str[sizeof(ident_str) - 1] = 0;
+  ident_str[sizeof(ident_str) - 1] = '\0';
 
   status = lcc_string_to_identifier(connection, &ident, ident_str);
   if (status != 0) {
@@ -637,7 +638,7 @@ int main(int argc, char **argv) {
       break;
     }
     case 'm':
-      nan_is_error_g = 1;
+      nan_is_error_g = true;
       break;
     default:
       usage(argv[0]);
@@ -651,7 +652,7 @@ int main(int argc, char **argv) {
   }
 
   snprintf(address, sizeof(address), "unix:%s", socket_file_g);
-  address[sizeof(address) - 1] = 0;
+  address[sizeof(address) - 1] = '\0';
 
   connection = NULL;
   status = lcc_connect(address, &connection);
index e28ff4b..4ff0bf6 100644 (file)
@@ -107,7 +107,7 @@ consolidations simply ignore NaN values.
 =head1 RETURN VALUE
 
 As usual for Nagios plugins, this program writes a short, one line status
-message to STDOUT and signals success or failure with it's return value. It
+message to STDOUT and signals success or failure with its return value. It
 exits with a return value of B<0> for I<success>, B<1> for I<warning> and B<2>
 for I<critical>. If the values are not available or some other error occurred,
 it returns B<3> for I<unknown>.
index 1f46f6f..3e44d54 100644 (file)
@@ -126,6 +126,13 @@ normally and spawning processes from Python will work as intended.
 
 =back
 
+=item B<Import> I<Name>
+
+Imports the python script I<Name> and loads it into the collectd
+python process. If your python script is not found, be sure its
+directory exists in python's B<sys.path>. You can prepend to the
+B<sys.path> using the B<ModulePath> configuration option.
+
 =item E<lt>B<Module> I<Name>E<gt> block
 
 This block may be used to pass on configuration settings to a Python module.
index d615088..493f5ec 100644 (file)
@@ -10,23 +10,24 @@ collectd-snmp - Documentation of collectd's C<snmp plugin>
   # ...
   <Plugin snmp>
     <Data "powerplus_voltge_input">
-      Type "voltage"
       Table false
-      Instance "input_line1"
+      Type "voltage"
+      TypeInstance "input_line1"
       Scale 0.1
       Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1"
     </Data>
     <Data "hr_users">
-      Type "users"
       Table false
-      Instance ""
+      Type "users"
       Shift -1
       Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0"
     </Data>
     <Data "std_traffic">
-      Type "if_octets"
       Table true
-      Instance "IF-MIB::ifDescr"
+      Type "if_octets"
+      TypeInstanceOID "IF-MIB::ifDescr"
+      #FilterOID "IF-MIB::ifOperStatus"
+      #FilterValues "1", "2"
       Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
     </Data>
 
@@ -75,9 +76,11 @@ internal format and dispatches them. Depending on the write plugins you have
 loaded they may be written to disk or submitted to another instance or
 whatever you configured.
 
-Because querying a host via SNMP may produce a timeout multiple threads are
-used to query hosts in parallel. Depending on the number of hosts between one
-and ten threads are used.
+Because querying a host via SNMP may produce a timeout the "complex reads"
+polling method is used. The ReadThreads parameter in the main configuration
+influences the number of parallel polling jobs which can be undertaken. If
+you expect timeouts or some polling to take a long time, you should increase
+this parameter. Note that other plugins also use the same threads.
 
 =head1 CONFIGURATION
 
@@ -114,8 +117,9 @@ queried using the C<GET> SNMP command (see L<snmpget(1)>) and transmitted to
 collectd. B<One> value list is dispatched and, eventually, one file will be
 written.
 
-When B<Table> is set to B<true>, the OIDs given to B<Values> (see below) are
-queried using the C<GETNEXT> SNMP command until the subtree is left. After all
+When B<Table> is set to B<true>, the OIDs given to B<Values>, B<TypeInstanceOID>,
+B<PluginInstanceOID>, B<HostOID> and B<FilterOID> (see below) are queried using
+the C<GETNEXT> SNMP command until the subtree is left. After all
 the lists (think: all columns of the table) have been read B<several> values
 sets will be dispatches and, eventually, several files will be written. If you
 configure a B<Type> (see above) which needs more than one data source (for
@@ -138,33 +142,66 @@ C<IF-MIB::ifHCInOctets> and C<IF-MIB::ifHCOutOctets>. But, this is because of
 the B<Type> setting, not the B<Table> setting.
 
 Since the semantic of B<Instance> and B<Values> depends on this setting you
-need to set it before setting them. Doing vice verse will result in undefined
+need to set it before setting them. Doing vice versa will result in undefined
 behavior.
 
-=item B<Instance> I<Instance>
+=item B<Plugin> I<Plugin>
 
-Sets the type-instance of the values that are dispatched. The meaning of this
-setting depends on whether B<Table> is set to I<true> or I<false>:
+Use I<Plugin> as the plugin name of the values that are dispatched.
+Defaults to C<snmp>.
 
-If B<Table> is set to I<true>, I<Instance> is interpreted as an SNMP-prefix
-that will return a list of values. Those values are then used as the actual
-type-instance. An example would be the C<IF-MIB::ifDescr> subtree.
-L<variables(5)> from the SNMP distribution describes the format of OIDs.
+=item B<PluginInstance> I<Instance>
 
-If B<Table> is set to I<true> and B<Instance> is omitted, then "SUBID" will be
-used as the instance.
+Sets the plugin-instance of the values that are dispatched to I<Instance> value.
 
-If B<Table> is set to I<false> the actual string configured for I<Instance> is
-copied into the value-list. In this case I<Instance> may be empty, i.E<nbsp>e.
-"".
+When B<Table> is set to I<true> and B<PluginInstanceOID> is set then this option
+has no effect.
 
-=item B<InstancePrefix> I<String>
+Defaults to an empty string.
+
+=item B<TypeInstance> I<Instance>
+
+Sets the type-instance of the values that are dispatched to I<Instance> value.
 
-If B<Table> is set to I<true>, you may feel the need to add something to the
-instance of the files. If set, I<String> is prepended to the instance as
-determined by querying the agent. When B<Table> is set to I<false> this option
+When B<Table> is set to I<true> and B<TypeInstanceOID> is set then this option
 has no effect.
 
+Defaults to an empty string.
+
+=item B<TypeInstanceOID> I<OID>
+
+=item B<PluginInstanceOID> I<OID>
+
+=item B<HostOID> I<OID>
+
+If B<Table> is set to I<true>, I<OID> is interpreted as an SNMP-prefix that will
+return a list of values. Those values are then used as the actual type-instance,
+plugin-instance or host of dispatched metrics. An example would be the
+C<IF-MIB::ifDescr> subtree. L<variables(5)> from the SNMP distribution describes
+the format of OIDs. When option is set to empty string, then "SUBID" will be used
+as the value.
+
+Prefix may be set for values with use of appropriate B<TypeInstancePrefix>,
+B<PluginInstancePrefix> and B<HostPrefix> options.
+
+When B<Table> is set to I<false> these options has no effect.
+
+Defaults: When no one of these options is configured explicitly,
+B<TypeInstanceOID> defaults to an empty string.
+
+=item B<TypeInstancePrefix>
+
+=item B<PluginInstancePrefix>
+
+=item B<HostPrefix>
+
+These options are intented to be used together with B<TypeInstanceOID>,
+B<PluginInstanceOID> and B<HostOID> respectively.
+
+If set, I<String> is preprended to values received by querying the agent.
+
+When B<Table> is set to I<false> these options has no effect.
+
 The C<UPS-MIB> is an example where you need this setting: It has voltages of
 the inlets, outlets and the battery of an UPS. However, it doesn't provide a
 descriptive column for these voltages. In this case having 1, 2,E<nbsp>... as
@@ -172,6 +209,25 @@ instances is not enough, because the inlet voltages and outlet voltages may
 both have the subids 1, 2,E<nbsp>... You can use this setting to distinguish
 between the different voltages.
 
+=item B<Instance> I<Instance>
+
+Attention: this option exists for backwards compatibility only and will be
+removed in next major release. Please use B<TypeInstance> / B<TypeInstanceOID>
+instead.
+
+The meaning of this setting depends on whether B<Table> is set to I<true> or
+I<false>.
+
+If B<Table> is set to I<true>, option behaves as B<TypeInstanceOID>.
+If B<Table> is set to I<false>, option behaves as B<TypeInstance>.
+
+Note what B<Table> option must be set before setting B<Instance>.
+
+=item B<InstancePrefix> I<String>
+
+Attention: this option exists for backwards compatibility only and will be
+removed in next major release. Please use B<TypeInstancePrefix> instead.
+
 =item B<Values> I<OID> [I<OID> ...]
 
 Configures the values to be queried from the SNMP host. The meaning slightly
@@ -208,16 +264,39 @@ This value is not applied to counter-values.
 
 =item B<Ignore> I<Value> [, I<Value> ...]
 
-The ignore values allows one to ignore Instances based on their name and the
-patterns specified by the various values you've entered. The match is a
+The ignore values allows one to ignore TypeInstances based on their name and
+the patterns specified by the various values you've entered. The match is a
 glob-type shell matching.
 
+When B<Table> is set to I<false> then this option has no effect.
+
 =item B<InvertMatch> I<true|false(default)>
 
 The invertmatch value should be use in combination of the Ignore option.
 It changes the behaviour of the Ignore option, from a blacklist behaviour
 when InvertMatch is set to false, to a whitelist when specified to true.
 
+=item B<FilterOID> I<OID>
+
+=item B<FilterValues> I<Value> [, I<Value> ...]
+
+=item B<FilterIgnoreSelected> I<true|false(default)>
+
+When B<Table> is set to I<true>, these options allow to configure filtering
+based on MIB values.
+
+The B<FilterOID> declares I<OID> to fill table column with values.
+The B<FilterValues> declares values list to do match. Whether table row will be
+collected or ignored depends on the B<FilterIgnoreSelected> setting.
+As with other plugins that use the daemon's ignorelist functionality, a string
+that starts and ends with a slash is interpreted as a regular expression.
+
+If no selection is configured at all, B<all> table rows are selected.
+
+When B<Table> is set to I<false> then these options has no effect.
+
+See B<Table> and F</"IGNORELISTS"> for details.
+
 =back
 
 =head2 The Host block
@@ -261,7 +340,7 @@ Selects the authentication protocol for SNMPv3 security.
 
 =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>
 
@@ -269,7 +348,7 @@ Selects the privacy (encryption) protocol for SNMPv3 security.
 
 =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> ...]
 
index 48f2dc4..92d1f01 100644 (file)
@@ -35,6 +35,7 @@
 #include <errno.h>
 #include <math.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -42,7 +43,7 @@
 #include <time.h>
 #include <unistd.h>
 
-#include "utils_heap.h"
+#include "utils/heap/heap.h"
 
 #include "collectd/client.h"
 #include "collectd/network.h"
@@ -61,12 +62,12 @@ static const char *conf_service = NET_DEFAULT_PORT;
 
 static lcc_network_t *net;
 
-static c_heap_t *values_heap = NULL;
+static c_heap_t *values_heap;
 
 static struct sigaction sigint_action;
 static struct sigaction sigterm_action;
 
-static _Bool loop = 1;
+static bool loop = true;
 
 __attribute__((noreturn)) static void exit_usage(int exit_status) /* {{{ */
 {
@@ -94,9 +95,9 @@ __attribute__((noreturn)) static void exit_usage(int exit_status) /* {{{ */
   exit(exit_status);
 } /* }}} void exit_usage */
 
-static void signal_handler(int signal) /* {{{ */
+static void signal_handler(int __attribute__((unused)) signal) /* {{{ */
 {
-  loop = 0;
+  loop = false;
 } /* }}} void signal_handler */
 
 #if HAVE_CLOCK_GETTIME
@@ -104,7 +105,7 @@ static double dtime(void) /* {{{ */
 {
   struct timespec ts = {0};
 
-  if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+  if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
     perror("clock_gettime");
 
   return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
@@ -146,7 +147,8 @@ static int get_boundet_random(int min, int max) /* {{{ */
 
   range = max - min;
 
-  return min + ((int)(((double)range) * ((double)random()) / (((double)RAND_MAX) + 1.0)));
+  return min + ((int)(((double)range) * ((double)random()) /
+                      (((double)RAND_MAX) + 1.0)));
 } /* }}} int get_boundet_random */
 
 static lcc_value_list_t *create_value_list(void) /* {{{ */
@@ -194,7 +196,7 @@ static lcc_value_list_t *create_value_list(void) /* {{{ */
   strncpy(vl->identifier.type,
           (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive",
           sizeof(vl->identifier.type));
-  vl->identifier.type[sizeof(vl->identifier.type) - 1] = 0;
+  vl->identifier.type[sizeof(vl->identifier.type) - 1] = '\0';
   snprintf(vl->identifier.type_instance, sizeof(vl->identifier.type_instance),
            "ti%li", random());
 
index 35f8a9f..14f2c8c 100644 (file)
@@ -40,7 +40,7 @@ Also, all values that match a threshold are considered to be relevant or
 "interesting". As a consequence collectd will issue a notification if they are
 not received for B<Timeout> iterations. The B<Timeout> configuration option is
 explained in section L<collectd.conf(5)/"GLOBAL OPTIONS">. If, for example,
-B<Timeout> is set to "2" (the default) and some hosts sends it's CPU statistics
+B<Timeout> is set to "2" (the default) and some hosts sends its CPU statistics
 to the server every 60 seconds, a notification will be dispatched after about
 120 seconds. It may take a little longer because the timeout is checked only
 once each B<Interval> on the server.
index b241a9f..db7000a 100644 (file)
@@ -84,7 +84,7 @@ Example:
 =item B<PUTVAL> I<Identifier> [I<OptionList>] I<Valuelist>
 
 Submits one or more values (identified by I<Identifier>, see below) to the
-daemon which will dispatch it to all it's write-plugins.
+daemon which will dispatch it to all its write-plugins.
 
 An I<Identifier> is of the form
 C<I<host>B</>I<plugin>B<->I<instance>B</>I<type>B<->I<instance>> with both
index 5eb17c4..f58d3b4 100644 (file)
@@ -90,6 +90,7 @@
 
 #@BUILD_PLUGIN_AGGREGATION_TRUE@LoadPlugin aggregation
 #@BUILD_PLUGIN_AMQP_TRUE@LoadPlugin amqp
+#@BUILD_PLUGIN_AMQP1_TRUE@LoadPlugin amqp1
 #@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
 #@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
 #@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors
 #@BUILD_PLUGIN_ORACLE_TRUE@LoadPlugin oracle
 #@BUILD_PLUGIN_OVS_EVENTS_TRUE@LoadPlugin ovs_events
 #@BUILD_PLUGIN_OVS_STATS_TRUE@LoadPlugin ovs_stats
+#@BUILD_PLUGIN_PCIE_ERRORS_TRUE@LoadPlugin pcie_errors
 #@BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl
 #@BUILD_PLUGIN_PINBA_TRUE@LoadPlugin pinba
 #@BUILD_PLUGIN_PING_TRUE@LoadPlugin ping
 #@BUILD_PLUGIN_WRITE_REDIS_TRUE@LoadPlugin write_redis
 #@BUILD_PLUGIN_WRITE_RIEMANN_TRUE@LoadPlugin write_riemann
 #@BUILD_PLUGIN_WRITE_SENSU_TRUE@LoadPlugin write_sensu
+#@BUILD_PLUGIN_WRITE_STACKDRIVER_TRUE@LoadPlugin write_stackdriver
 #@BUILD_PLUGIN_WRITE_TSDB_TRUE@LoadPlugin write_tsdb
 #@BUILD_PLUGIN_XENCPU_TRUE@LoadPlugin xencpu
 #@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms
 #  </Publish>
 #</Plugin>
 
+#<Plugin amqp1>
+#  <Transport "name">
+#    Host "localhost"
+#    Port "5672"
+#    User "guest"
+#    Password "guest"
+#    Address "collectd"
+#    RetryDelay 1
+#    <Instance "log">
+#        Format JSON
+#        PreSettle false
+#    </Instance>
+#    <Instance "notify">
+#        Format JSON
+#        PreSettle true
+#    </Instance>
+#    <Instance "telemetry">
+#        Format JSON
+#        PreSettle false
+#    </Instance>
+#  </Transport>
+#</Plugin>
+
 #<Plugin apache>
 #  <Instance "local">
 #    URL "http://localhost/status?auto"
 #  PauseConnect 5
 #</Plugin>
 
+#<Plugin gpu_nvidia>
+#   GPUIndex 0
+#   GPUIndex 2
+#   IgnoreSelected false
+#</Plugin>
+
 #<Plugin grpc>
 #      <Server "example.com" "50051">
 #              EnableSSL true
 #              SSLCACertificateFile "/path/to/root.pem"
 #              SSLCertificateFile "/path/to/client.pem"
 #              SSLCertificateKeyFile "/path/to/client.key"
+#              VerifyPeer true
 #      </Listen>
 #</Plugin>
 
 #              NotifySensorNotPresent false
 #              NotifyIPMIConnectionState false
 #              SELEnabled false
+#              SELSensor "some_sensor"
+#              SELSensor "another_one"
+#              SELIgnoreSelected false
 #              SELClearEvent false
 #      </Instance>
 #      <Instance "remote">
 #              NotifySensorNotPresent false
 #              NotifyIPMIConnectionState false
 #              SELEnabled false
+#              SELSensor "some_sensor"
+#              SELSensor "another_one"
+#              SELIgnoreSelected false
 #              SELClearEvent false
 #      </Instance>
 #</Plugin>
 #              RegisterType float
 #              Type gauge
 #              Instance "..."
+#              #Scale 1.0
+#              #Shift 0.0
 #      </Data>
 #
 #      <Host "name">
 #  Bridges "br0" "br_ext"
 #</Plugin>
 
+#<Plugin pcie_errors>
+#  Source "sysfs"
+#  ReportMasked false
+#  PersistentNotifications false
+#</Plugin>
+
 #<Plugin perl>
 #      IncludeDir "/my/include/path"
 #      BaseName "Collectd::Plugins"
 #      Timeout 0.9
 #      TTL 255
 #      SourceAddress "1.2.3.4"
+#      AddressFamily "any"
 #      Device "eth0"
 #      MaxMissed -1
 #</Plugin>
 #      CollectFileDescriptor true
 #      CollectContextSwitch true
 #      CollectMemoryMaps true
+#      CollectDelayAccounting false
 #      Process "name"
 #      ProcessMatch "name" "regex"
 #      <Process "collectd">
 #              CollectFileDescriptor false
 #              CollectContextSwitch false
+#              CollectDelayAccounting true
 #      </Process>
 #      <ProcessMatch "name" "regex">
 #              CollectFileDescriptor false
 #   <Node example>
 #      Host "redis.example.com"
 #      Port "6379"
+#      #Socket "/var/run/redis/redis.sock"
 #      Timeout 2000
+#      <Query "LLEN myqueue">
+#        #Database 0
+#        Type "queue_length"
+#        Instance "myqueue"
+#      <Query>
 #   </Node>
 #</Plugin>
 
 #              CollectMemory true
 #              CollectDF true
 #              CollectDisk true
+#              CollectHealth true
 #      </Router>
 #</Plugin>
 
 
 #<Plugin snmp>
 #   <Data "powerplus_voltge_input">
-#       Type "voltage"
 #       Table false
-#       Instance "input_line1"
+#       Type "voltage"
+#       TypeInstance "input_line1"
 #       Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1"
 #   </Data>
 #   <Data "hr_users">
-#       Type "users"
 #       Table false
-#       Instance ""
+#       Type "users"
+#       TypeInstance ""
 #       Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0"
 #   </Data>
 #   <Data "std_traffic">
+#       Table true
 #       Type "if_octets"
+#       TypeInstanceOID "IF-MIB::ifDescr"
+#       #TypeInstancePrefix "port"
+#       Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
+#       #FilterOID "IF-MIB::ifOperStatus"
+#       #FilterValues "1", "2"
+#   </Data>
+#   <Data "interface_traffic">
 #       Table true
-#       Instance "IF-MIB::ifDescr"
+#       Type "if_octets"
+#       Plugin "interface"
+#       PluginInstanceOID "IF-MIB::ifDescr"
 #       Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
 #   </Data>
 #
 #    IndexOID "IF-MIB::ifIndex"
 #    SizeOID "IF-MIB::ifNumber"
 #    <Data "ifDescr">
-#      Instance true
+#      <IndexKey>
+#        Source "PluginInstance"
+#      </IndexKey>
 #      Plugin "interface"
 #      OIDs "IF-MIB::ifDescr"
 #    </Data>
 #      Connection "xen:///"
 #      RefreshInterval 60
 #      Domain "name"
+#      ReportBlockDevices true
+#      ReportNetworkInterfaces true
 #      BlockDevice "name:device"
 #      BlockDeviceFormat target
 #      BlockDeviceFormatBasename false
 #      InterfaceDevice "name:device"
 #      IgnoreSelected false
 #      HostnameFormat name
+#      HostnameMetadataXPath "/instance/name/text()"
+#      HostnameMetadataNS "http://openstack.org/xmlns/libvirt/nova/1.0"
 #      InterfaceFormat name
 #      PluginInstanceFormat name
 #      Instances 1
 #      ExtraStats "cpu_util disk disk_err domain_state fs_info job_stats_background pcpu perf vcpupin"
+#      PersistentNotification false
 #</Plugin>
 
 #<Plugin vmem>
 #      Attribute "foo" "bar"
 #</Plugin>
 
+#<Plugin write_stackdriver>
+#  Project "stackdriver-account"
+#  CredentialFile "/path/to/gcp-project-id-12345.json"
+#  Email "123456789012@developer.gserviceaccount.com"
+#  <Resource "global">
+#    Label "project_id" "gcp-project-id"
+#  </Resource>
+#  Url "https://monitoring.googleapis.com/v3"
+#</Plugin>
+
 #<Plugin write_tsdb>
 #      <Node>
 #              Host "localhost"
index 51be401..88fa37c 100644 (file)
@@ -530,9 +530,9 @@ are disabled by default.
 =head2 Plugin C<amqp>
 
 The I<AMQP plugin> can be used to communicate with other instances of
-I<collectd> or third party applications using an AMQP message broker. Values
-are sent to or received from the broker, which handles routing, queueing and
-possibly filtering out messages.
+I<collectd> or third party applications using an AMQP 0.9.1 message broker.
+Values are sent to or received from the broker, which handles routing,
+queueing and possibly filtering out messages.
 
 B<Synopsis:>
 
@@ -738,6 +738,171 @@ is preserved, i.e. passed through.
 
 =back
 
+=head2 Plugin C<amqp1>
+
+The I<AMQP1 plugin> can be used to communicate with other instances of
+I<collectd> or third party applications using an AMQP 1.0 message
+intermediary. Metric values or notifications are sent to the
+messaging intermediary which may handle direct messaging or
+queue based transfer.
+
+B<Synopsis:>
+
+ <Plugin "amqp1">
+   # Send values to an AMQP 1.0 intermediary
+  <Transport "name">
+    Host "localhost"
+    Port "5672"
+    User "guest"
+    Password "guest"
+    Address "collectd"
+#    RetryDelay 1
+    <Instance "some_name">
+        Format "command"
+        PreSettle false
+        Notify false
+ #      StoreRates false
+ #      GraphitePrefix "collectd."
+ #      GraphiteEscapeChar "_"
+ #      GraphiteSeparateInstances false
+ #      GraphiteAlwaysAppendDS false
+ #      GraphitePreserveSeparator false
+    </Instance>
+  </Transport>
+ </Plugin>
+
+The plugin's configuration consists of a I<Transport> that configures
+communications to the AMQP 1.0 messaging bus and one or more I<Instance>
+corresponding to metric or event publishers to the messaging system.
+
+The address in the I<Transport> block concatenated with the name given in the
+I<Instance> block starting tag will be used as the send-to address for
+communications over the messaging link.
+
+The following options are accepted within each I<Transport> block:
+
+=over 4
+
+=item B<Host> I<Host>
+
+Hostname or IP-address of the AMQP 1.0 intermediary. Defaults to the
+default behavior of the underlying communications library,
+I<libqpid-proton>, which is "localhost".
+
+=item B<Port> I<Port>
+
+Service name or port number on which the AMQP 1.0 intermediary accepts
+connections. This argument must be a string, even if the numeric form
+is used. Defaults to "5672".
+
+=item B<User> I<User>
+
+=item B<Password> I<Password>
+
+Credentials used to authenticate to the AMQP 1.0 intermediary. By
+default "guest"/"guest" is used.
+
+=item B<Address> I<Address>
+
+This option specifies the prefix for the send-to value in the message.
+By default, "collectd" will be used.
+
+=item B<RetryDelay> I<RetryDelay>
+
+When the AMQP1 connection is lost, defines the time in seconds to wait
+before attempting to reconnect. Defaults to 1, which implies attempt
+to reconnect at 1 second intervals.
+
+=back
+
+The following options are accepted within each I<Instance> block:
+
+=over 4
+
+=item B<Format> B<Command>|B<JSON>|B<Graphite>
+
+Selects the format in which messages are sent to the intermediary. If set to
+B<Command> (the default), values are sent as C<PUTVAL> commands which are
+identical to the syntax used by the I<Exec> and I<UnixSock plugins>. In this
+case, the C<Content-Type> header field will be set to C<text/collectd>.
+
+If set to B<JSON>, the values are encoded in the I<JavaScript Object Notation>,
+an easy and straight forward exchange format. The C<Content-Type> header field
+will be set to C<application/json>.
+
+If set to B<Graphite>, values are encoded in the I<Graphite> format, which is
+"<metric> <value> <timestamp>\n". The C<Content-Type> header field will be set to
+C<text/graphite>.
+
+A subscribing client I<should> use the C<Content-Type> header field to
+determine how to decode the values.
+
+=item B<PreSettle> B<true>|B<false>
+
+If set to B<false> (the default), the plugin will wait for a message
+acknowledgement from the messaging bus before sending the next
+message. This indicates transfer of ownership to the messaging
+system. If set to B<true>, the plugin will not wait for a message
+acknowledgement and the message may be dropped prior to transfer of
+ownership.
+
+=item B<Notify> B<true>|B<false>
+
+If set to B<false> (the default), the plugin will service the
+instance write call back as a value list. If set to B<true> the
+plugin will service the instance as a write notification callback
+for alert formatting.
+
+=item B<StoreRates> B<true>|B<false>
+
+Determines whether or not C<COUNTER>, C<DERIVE> and C<ABSOLUTE> data sources
+are converted to a I<rate> (i.e. a C<GAUGE> value). If set to B<false> (the
+default), no conversion is performed. Otherwise the conversion is performed
+using the internal value cache.
+
+Please note that currently this option is only used if the B<Format> option has
+been set to B<JSON>.
+
+=item B<GraphitePrefix>
+
+A prefix can be added in the metric name when outputting in the I<Graphite> format.
+It's added before the I<Host> name.
+Metric name will be "<prefix><host><postfix><plugin><type><name>"
+
+=item B<GraphitePostfix>
+
+A postfix can be added in the metric name when outputting in the I<Graphite> format.
+It's added after the I<Host> name.
+Metric name will be "<prefix><host><postfix><plugin><type><name>"
+
+=item B<GraphiteEscapeChar>
+
+Specify a character to replace dots (.) in the host part of the metric name.
+In I<Graphite> metric name, dots are used as separators between different
+metric parts (host, plugin, type).
+Default is "_" (I<Underscore>).
+
+=item B<GraphiteSeparateInstances> B<true>|B<false>
+
+If set to B<true>, the plugin instance and type instance will be in their own
+path component, for example C<host.cpu.0.cpu.idle>. If set to B<false> (the
+default), the plugin and plugin instance (and likewise the type and type
+instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
+
+=item B<GraphiteAlwaysAppendDS> B<true>|B<false>
+
+If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
+identifier. If set to B<false> (the default), this is only done when there is
+more than one DS.
+
+=item B<GraphitePreserveSeparator> B<false>|B<true>
+
+If set to B<false> (the default) the C<.> (dot) character is replaced with
+I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
+is preserved, i.e. passed through.
+
+=back
+
 =head2 Plugin C<apache>
 
 To configure the C<apache>-plugin you first need to configure the Apache
@@ -1497,6 +1662,10 @@ installed) to get the current CPU frequency. If this file does not exist make
 sure B<cpufreqd> (L<http://cpufreqd.sourceforge.net/>) or a similar tool is
 installed and an "cpu governor" (that's a kernel module) is loaded.
 
+If the system has the I<cpufreq-stats> kernel module loaded, this plugin reports
+the rate of p-state (cpu frequency) transitions and the percentage of time spent
+in each p-state.
+
 =head2 Plugin C<cpusleep>
 
 This plugin doesn't have any options. It reads CLOCK_BOOTTIME and
@@ -1743,6 +1912,11 @@ plugin below on how matches are defined. If the B<MeasureResponseTime> or
 B<MeasureResponseCode> options are set to B<true>, B<Match> blocks are
 optional.
 
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+URL. By default the global B<Interval> setting will be used.
+
 =item B<Timeout> I<Milliseconds>
 
 The B<Timeout> option sets the overall timeout for HTTP requests to B<URL>, in
@@ -1939,6 +2113,11 @@ Use I<Instance> as the plugin instance when submitting values.
 May be overridden by B<PluginInstanceFrom> option inside B<XPath> blocks.
 Defaults to an empty string (no plugin instance).
 
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+URL. By default the global B<Interval> setting will be used.
+
 =item B<Namespace> I<Prefix> I<URL>
 
 If an XPath expression references namespaces, they must be specified
@@ -3031,6 +3210,30 @@ Pause to apply between attempts of connection to gpsd in seconds (default 5 sec)
 
 =back
 
+=head2 Plugin C<gpu_nvidia>
+
+Efficiently collects various statistics from the system's NVIDIA GPUs using the
+NVML library. Currently collected are fan speed, core temperature, percent
+load, percent memory used, compute and memory frequencies, and power
+consumption.
+
+=over 4
+
+=item B<GPUIndex>
+
+If one or more of these options is specified, only GPUs at that index (as
+determined by nvidia-utils through I<nvidia-smi>) have statistics collected.
+If no instance of this option is specified, all GPUs are monitored.
+
+=item B<IgnoreSelected>
+
+If set to true, all detected GPUs B<except> the ones at indices specified by
+B<GPUIndex> entries are collected. For greater clarity, setting IgnoreSelected
+without any GPUIndex directives will result in B<no> statistics being
+collected.
+
+=back
+
 =head2 Plugin C<grpc>
 
 The I<grpc> plugin provides an RPC interface to submit values to or query
@@ -3094,6 +3297,13 @@ Whether to enable SSL for incoming connections. Default: false.
 Filenames specifying SSL certificate and key material to be used with SSL
 connections.
 
+=item B<VerifyPeer> B<true>|B<false>
+
+When enabled, a valid client certificate is required to connect to the server.
+When disabled, a client certifiacte is not requested and any unsolicited client
+certificate is accepted.
+Enabled by default.
+
 =back
 
 =back
@@ -3464,8 +3674,23 @@ a notification is sent. Defaults to B<false>.
 
 If system event log (SEL) is enabled, plugin will listen for sensor threshold
 and discrete events. When event is received the notification is sent.
+SEL event filtering can be configured using B<SELSensor> and B<SELIgnoreSelected>
+config options.
 Defaults to B<false>.
 
+=item B<SELSensor> I<SELSensor>
+
+Selects sensors to get events from or to ignore, depending on B<SELIgnoreSelected>.
+
+See F</"IGNORELISTS"> for details.
+
+=item B<SELIgnoreSelected> I<true>|I<false>
+
+If no configuration is given, the B<ipmi> plugin will pass events from all
+sensors. This option enables you to do that: By setting B<SELIgnoreSelected>
+to I<true> the effect of B<SELSensor> is inverted: All events from selected
+sensors are ignored and all events from other sensors are passed.
+
 =item B<SELClearEvent> I<true>|I<false>
 
 If SEL clear event is enabled, plugin will delete event from SEL list after
@@ -4070,8 +4295,9 @@ which the sizes of physical memory vary.
 
 The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP or Modbus/RTU and
 reads register values. It supports reading single registers (unsigned 16E<nbsp>bit
-values), large integer values (unsigned 32E<nbsp>bit values) and floating point
-values (two registers interpreted as IEEE floats in big endian notation).
+values), large integer values (unsigned 32E<nbsp>bit and 64E<nbsp>bit values) and
+floating point values (two registers interpreted as IEEE floats in big endian
+notation).
 
 B<Synopsis:>
 
@@ -4081,6 +4307,8 @@ B<Synopsis:>
    RegisterCmd ReadHolding
    Type voltage
    Instance "input-1"
+   #Scale 1.0
+   #Shift 0.0
  </Data>
 
  <Data "voltage-input-2">
@@ -4139,11 +4367,22 @@ Configures the base register to read from the device. If the option
 B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
 register will be read (the register number is increased by one).
 
-=item B<RegisterType> B<Int16>|B<Int32>|B<Uint16>|B<Uint32>|B<Float>
-
-Specifies what kind of data is returned by the device. If the type is B<Int32>,
-B<Uint32> or B<Float>, two 16E<nbsp>bit registers will be read and the data is
-combined into one value. Defaults to B<Uint16>.
+=item B<RegisterType> B<Int16>|B<Int32>|B<Int64>|B<Uint16>|B<Uint32>|B<UInt64>|B<Float>|B<Int32LE>|B<Uint32LE>|B<FloatLE>
+
+Specifies what kind of data is returned by the device. This defaults to
+B<Uint16>.  If the type is B<Int32>, B<Int32LE>, B<Uint32>, B<Uint32LE>,
+B<Float> or B<FloatLE>, two 16E<nbsp>bit registers at B<RegisterBase>
+and B<RegisterBase+1> will be read and the data is combined into one
+32E<nbsp>value. For B<Int32>, B<Uint32> and B<Float> the most significant
+16E<nbsp>bits are in the register at B<RegisterBase> and the least
+significant 16E<nbsp>bits are in the register at B<RegisterBase+1>.
+For B<Int32LE>, B<Uint32LE>, or B<Float32LE>, the high and low order
+registers are swapped with the most significant 16E<nbsp>bits in
+the B<RegisterBase+1> and the least significant 16E<nbsp>bits in
+B<RegisterBase>. If the type is B<Int64> or B<UInt64>, four 16E<nbsp>bit
+registers at B<RegisterBase>, B<RegisterBase+1>, B<RegisterBase+2> and
+B<RegisterBase+3> will be read and the data combined into one
+64E<nbsp>value.
 
 =item B<RegisterCmd> B<ReadHolding>|B<ReadInput>
 
@@ -4158,9 +4397,19 @@ supported.
 
 =item B<Instance> I<Instance>
 
-Sets the type instance to use when dispatching the value to I<collectd>. If
+Sets the type instance to use when dispatching the value to I<Instance>. If
 unset, an empty string (no type instance) is used.
 
+=item B<Scale> I<Value>
+
+The values taken from device are multiplied by I<Value>. The field is optional
+and the default is B<1.0>.
+
+=item B<Shift> I<Value>
+
+I<Value> is added to values from device after they have been multiplied by
+B<Scale> value. The field is optional and the default value is B<0.0>.
+
 =back
 
 =item E<lt>B<Host> I<Name>E<gt> blocks
@@ -5084,8 +5333,9 @@ When configuring with B<Interface> only the basic statistics will be collected,
 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:
@@ -5227,6 +5477,12 @@ behavior is to let the kernel choose the appropriate interface. Be warned
 that the manual selection of an interface for unicast traffic is only
 necessary in rare cases.
 
+=item B<BindAddress> I<IP Address>
+
+Set the outgoing IP address for IP packets. This option can be used instead of
+the I<Interface> option to explicitly define the IP address which will be used
+to send Packets to the remote server. 
+
 =item B<ResolveInterval> I<Seconds>
 
 Sets the interval at which to re-resolve the DNS for the I<Host>. This is
@@ -6019,6 +6275,7 @@ B<Synopsis:>
    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:
@@ -6052,6 +6309,59 @@ omitted or is empty then all OVS bridges will be monitored.
 
 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>
+
+The I<pcie_errors> plugin collects PCI Express errors from Device Status in Capability
+structure and from Advanced Error Reporting Extended Capability where available.
+At every read it polls config space of PCI Express devices and dispatches
+notification for every error that is set. It checks for new errors at every read.
+The device is indicated in plugin_instance according to format "domain:bus:dev.fn".
+Errors are divided into categories indicated by type_instance: "correctable", and
+for uncorrectable errors "non_fatal" or "fatal".
+Fatal errors are reported as I<NOTIF_FAILURE> and all others as I<NOTIF_WARNING>.
+
+B<Synopsis:>
+
+  <Plugin "pcie_errors">
+    Source "sysfs"
+    AccessDir "/sys/bus/pci"
+    ReportMasked false
+    PersistentNotifications false
+  </Plugin>
+
+B<Options:>
+
+=over 4
+
+=item B<Source> B<sysfs>|B<proc>
+
+Use B<sysfs> or B<proc> to read data from /sysfs or /proc.
+The default value is B<sysfs>.
+
+=item B<AccessDir> I<dir>
+
+Directory used to access device config space. It is optional and defaults to
+/sys/bus/pci for B<sysfs> and to /proc/bus/pci for B<proc>.
+
+=item B<ReportMasked> B<false>|B<true>
+
+If true plugin will notify about errors that are set to masked in Error Mask register.
+Such errors are not reported to the PCI Express Root Complex. Defaults to B<false>.
+
+=item B<PersistentNotifications> B<false>|B<true>
+
+If false plugin will dispatch notification only on set/clear of error.
+The ones already reported will be ignored. Defaults to B<false>.
+
 =back
 
 =head2 Plugin C<perl>
@@ -6188,6 +6498,11 @@ long string is used so that the packet size of an ICMPv4 packet is exactly
 Sets the source address to use. I<host> may either be a numerical network
 address or a network hostname.
 
+=item B<AddressFamily> I<af>
+
+Sets the address family to use. I<af> may be "any", "ipv4" or "ipv6". This
+option will be ignored if you set a B<SourceAddress>.
+
 =item B<Device> I<name>
 
 Sets the outgoing network device to be used. I<name> has to specify an
@@ -6860,18 +7175,21 @@ The statistics collected for matched processes are:
  - number of memory mapped files (under Linux)
  - io data (where available)
  - context switches (under Linux)
- - minor and major pagefaults.
+ - minor and major pagefaults
+ - Delay Accounting information (Linux only, requires libmnl)
 
 B<Synopsis:>
 
  <Plugin processes>
-   CollectFileDescriptor true
-   CollectContextSwitch true
+   CollectFileDescriptor  true
+   CollectContextSwitch   true
+   CollectDelayAccounting false
    Process "name"
    ProcessMatch "name" "regex"
    <Process "collectd">
-     CollectFileDescriptor false
-     CollectContextSwitch false
+     CollectFileDescriptor  false
+     CollectContextSwitch   false
+     CollectDelayAccounting true
    </Process>
    <ProcessMatch "name" "regex">
      CollectFileDescriptor false
@@ -6901,6 +7219,18 @@ I<name> must not contain slashes.
 Collect the number of context switches for matched processes.
 Disabled by default.
 
+=item B<CollectDelayAccounting> I<Boolean>
+
+If enabled, collect Linux Delay Accounding information for matching processes.
+Delay Accounting provides the time processes wait for the CPU to become
+available, for I/O operations to finish, for pages to be swapped in and for
+freed pages to be reclaimed. The metrics are reported as "seconds per second"
+using the C<delay_rate> type, e.g. C<delay_rate-delay-cpu>.
+Disabled by default.
+
+This option is only available on Linux, requires the C<libmnl> library and
+requires the C<CAP_NET_ADMIN> capability at runtime.
+
 =item B<CollectFileDescriptor> I<Boolean>
 
 Collect number of file descriptors of matched processes.
@@ -6914,9 +7244,12 @@ the Linux kernel.
 
 =back
 
-Options B<CollectContextSwitch> and B<CollectFileDescriptor> may be used inside
-B<Process> and B<ProcessMatch> blocks - then they affect corresponding match
-only. Otherwise they set the default value for subsequent matches.
+The B<CollectContextSwitch>, B<CollectDelayAccounting>,
+B<CollectFileDescriptor> and B<CollectMemoryMaps> options may be used inside
+B<Process> and B<ProcessMatch> blocks. When used there, these options affect
+reporting the corresponding processes only. Outside of B<Process> and
+B<ProcessMatch> blocks these options set the default value for subsequent
+matches.
 
 =head2 Plugin C<protocols>
 
@@ -6983,6 +7316,7 @@ multiple routers:
       CollectRegistrationTable true
       CollectDF true
       CollectDisk true
+      CollectHealth true
     </Router>
   </Plugin>
 
@@ -7043,29 +7377,37 @@ Defaults to B<false>.
 When enabled, the number of sectors written and bad blocks will be collected.
 Defaults to B<false>.
 
+=item B<CollectHealth> B<true>|B<false>
+
+When enabled, the health statistics will be collected. This includes the
+voltage and temperature on supported hardware.
+Defaults to B<false>.
+
 =back
 
 =head2 Plugin C<redis>
 
-The I<Redis plugin> connects to one or more Redis servers and gathers
-information about each server's state. For each server there is a I<Node> block
-which configures the connection parameters for this node.
+The I<Redis plugin> connects to one or more Redis servers, gathers
+information about each server's state and executes user-defined queries.
+For each server there is a I<Node> block which configures the connection
+parameters and set of user-defined queries for this node.
 
   <Plugin redis>
     <Node "example">
         Host "localhost"
         Port "6379"
+        #Socket "/var/run/redis/redis.sock"
         Timeout 2000
+        ReportCommandStats false
+        ReportCpuUsage true
         <Query "LLEN myqueue">
+          #Database 0
           Type "queue_length"
           Instance "myqueue"
-        <Query>
+        </Query>
     </Node>
   </Plugin>
 
-The information shown in the synopsis above is the I<default configuration>
-which is used by the plugin if no configuration is present.
-
 =over 4
 
 =item B<Node> I<Nodename>
@@ -7073,7 +7415,9 @@ which is used by the plugin if no configuration is present.
 The B<Node> block identifies a new Redis node, that is a new Redis instance
 running in an specified host and port. The name for node is a canonical
 identifier which is used as I<plugin instance>. It is limited to
-64E<nbsp>characters in length.
+128E<nbsp>characters in length.
+
+When no B<Node> is configured explicitly, plugin connects to "localhost:6379".
 
 =item B<Host> I<Hostname>
 
@@ -7086,6 +7430,11 @@ The B<Port> option is the TCP port on which the Redis instance accepts
 connections. Either a service name of a port number may be given. Please note
 that numerical port numbers must be given as a string, too.
 
+=item B<Socket> I<Path>
+
+Connect to Redis using the UNIX domain socket at I<Path>. If this
+setting is given, the B<Hostname> and B<Port> settings are ignored.
+
 =item B<Password> I<Password>
 
 Use I<Password> to authenticate when connecting to I<Redis>.
@@ -7093,25 +7442,47 @@ Use I<Password> to authenticate when connecting to I<Redis>.
 =item B<Timeout> I<Milliseconds>
 
 The B<Timeout> option set the socket timeout for node response. Since the Redis
-read function is blocking, you should keep this value as low as possible. Keep
-in mind that the sum of all B<Timeout> values for all B<Nodes> should be lower
-than B<Interval> defined globally.
+read function is blocking, you should keep this value as low as possible.
+It is expected what B<Timeout> values should be lower than B<Interval> defined
+globally.
+
+Defaults to 2000 (2 seconds).
+
+=item B<ReportCommandStats> B<false>|B<true>
+
+Enables or disables reporting of statistics based on the command type, including
+rate of command calls and average CPU time consumed by command processing.
+Defaults to B<false>.
+
+=item B<ReportCpuUsage> B<true>|B<false>
+
+Enables or disables reporting of CPU consumption statistics.
+Defaults to B<true>.
 
 =item B<Query> I<Querystring>
 
 The B<Query> block identifies a query to execute against the redis server.
-There may be an arbitrary number of queries to execute.
+There may be an arbitrary number of queries to execute. Each query should
+return single string or integer.
 
 =item B<Type> I<Collectd type>
 
-Within a query definition, a valid collectd type to use as when submitting
+Within a query definition, a valid I<collectd type> to use as when submitting
 the result of the query. When not supplied, will default to B<gauge>.
 
+Currently only types with one datasource are supported.
+See L<types.db(5)> for more details on types and their configuration.
+
 =item B<Instance> I<Type instance>
 
 Within a query definition, an optional type instance to use when submitting
 the result of the query. When not supplied will default to the escaped
-command, up to 64 chars.
+command, up to 128 chars.
+
+=item B<Database> I<Index>
+
+This index selects the Redis logical database to use for query. Defaults
+to C<0>.
 
 =back
 
@@ -7551,7 +7922,9 @@ B<Synopsis:>
       IndexOID "IF-MIB::ifIndex"
       SizeOID "IF-MIB::ifNumber"
       <Data "ifDescr">
-        Instance true
+        <IndexKey>
+          Source "PluginInstance"
+        </IndexKey>
         Plugin "interface"
         OIDs "IF-MIB::ifDescr"
       </Data>
@@ -7562,12 +7935,44 @@ B<Synopsis:>
         OIDs "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
       </Data>
     </Table>
+    <Table "CPUAffinityTable">
+      <Data "DomainName">
+        <IndexKey>
+          Source "PluginInstance"
+        </IndexKey>
+        Plugin "virt"
+        OIDs "LIBVIRT-HYPERVISOR-MIB::lvhAffinityDomainName"
+      </Data>
+      <Data "VCPU">
+        Plugin "virt"
+        <IndexKey>
+          Source "TypeInstance"
+          Regex "^vcpu_([0-9]{1,3})-cpu_[0-9]{1,3}$"
+          Group 1
+        </IndexKey>
+        OIDs "LIBVIRT-HYPERVISOR-MIB::lvhVCPUIndex"
+      </Data>
+      <Data "CPU">
+        Plugin "virt"
+        <IndexKey>
+          Source "TypeInstance"
+          Regex "^vcpu_[0-9]{1,3}-cpu_([0-9]{1,3})$"
+          Group 1
+        </IndexKey>
+        OIDs "LIBVIRT-HYPERVISOR-MIB::lvhCPUIndex"
+      </Data>
+      <Data "CPUAffinity">
+        Plugin "virt"
+        Type "cpu_affinity"
+        OIDs "LIBVIRT-HYPERVISOR-MIB::lvhCPUAffinity"
+      </Data>
+    </Table>
   </Plugin>
 
 There are two types of blocks that can be contained in the
 C<E<lt>PluginE<nbsp> snmp_agentE<gt>> block: B<Data> and B<Table>:
 
-=head3 The B<Data> block
+=head3 B<Data> block
 
 The B<Data> block defines a list OIDs that are to be handled. This block can
 define scalar or table OIDs. If B<Data> block is defined inside of B<Table>
@@ -7576,11 +7981,34 @@ The following options can be set:
 
 =over 4
 
-=item B<Instance> I<true|false>
+=item B<IndexKey> block
+
+B<IndexKey> block contains all data needed for proper index build of snmp table.
+In case more than
+one table B<Data> block has B<IndexKey> block present then multiple key index is
+built. If B<Data> block defines scalar data type B<IndexKey> has no effect and can
+be omitted.
+
+=over 8
+
+=item B<Source> I<String>
 
-When B<Instance> is set to B<true>, the value for requested OID is copied from
-plugin instance field of corresponding collectd value. If B<Data> block defines
-scalar data type B<Instance> has no effect and can be omitted.
+B<Source> can be set to one of the following values: "Hostname", "Plugin",
+"PluginInstance", "Type", "TypeInstance". This value indicates which field of
+corresponding collectd metric is taken as a SNMP table index.
+
+=item B<Regex> I<String>
+
+B<Regex> option can also be used to parse strings or numbers out of
+specific field. For example: type-instance field which is "vcpu1-cpu2" can be
+parsed into two numeric fields CPU = 2 and VCPU = 1 and can be later used
+as a table index.
+
+=item B<Group> I<Number>
+
+B<Group> number can be specified in case groups are used in regex.
+
+=back
 
 =item B<Plugin> I<String>
 
@@ -8124,13 +8552,14 @@ B<Synopsis:>
    <Metric "snort-dropped">
        Type "percent"
        Instance "dropped"
-       Index 1
+       ValueFrom 1
    </Metric>
    <File "/var/log/snort/snort.stats">
        Plugin "snortstats"
        Instance "eth0"
        Interval 600
        Collect "snort-dropped"
+       #TimeFrom 0
    </File>
  </Plugin>
 
@@ -8452,6 +8881,33 @@ dynamic number assigned by the kernel. Otherwise, C<coreE<lt>nE<gt>> is used
 if there is only one package and C<pkgE<lt>nE<gt>-coreE<lt>mE<gt>> if there is
 more than one, where I<n> is the n-th core of package I<m>.
 
+=item B<RestoreAffinityPolicy> I<AllCPUs>|I<Restore>
+
+Reading data from CPU has side-effect: collectd process's CPU affinity mask
+changes. After reading data is completed, affinity mask needs to be restored.
+This option allows to set restore policy.
+
+B<AllCPUs> (the default): Restore the affinity by setting affinity to any/all
+CPUs.
+
+B<Restore>: Save affinity using sched_getaffinity() before reading data and
+restore it after.
+
+On some systems, sched_getaffinity() will fail due to inconsistency of the CPU
+set size between userspace and kernel. In these cases plugin will detect the
+unsuccessful call and fail with an error, preventing data collection.
+Most of configurations does not need to save affinity as Collectd process is
+allowed to run on any/all available CPUs.
+
+If you need to save and restore affinity and get errors like 'Unable to save
+the CPU affinity', setting 'possible_cpus' kernel boot option may also help.
+
+See following links for details:
+
+L<https://github.com/collectd/collectd/issues/1593>
+L<https://sourceware.org/bugzilla/show_bug.cgi?id=15630>
+L<https://bugzilla.kernel.org/show_bug.cgi?id=151821>
+
 =back
 
 =head2 Plugin C<unixsock>
@@ -8721,6 +9177,40 @@ only on the host system.
 
 Only I<Connection> is required.
 
+Consider the following example config:
+
+ <Plugin "virt">
+   Connection "qemu:///system"
+   HostnameFormat "hostname"
+   InterfaceFormat "address"
+   PluginInstanceFormat "name"
+ </Plugin>
+
+It will generate the following values:
+
+  node42.example.com/virt-instance-0006f26c/disk_octets-vda
+  node42.example.com/virt-instance-0006f26c/disk_ops-vda
+  node42.example.com/virt-instance-0006f26c/if_dropped-ca:fe:ca:fe:ca:fe
+  node42.example.com/virt-instance-0006f26c/if_errors-ca:fe:ca:fe:ca:fe
+  node42.example.com/virt-instance-0006f26c/if_octets-ca:fe:ca:fe:ca:fe
+  node42.example.com/virt-instance-0006f26c/if_packets-ca:fe:ca:fe:ca:fe
+  node42.example.com/virt-instance-0006f26c/memory-actual_balloon
+  node42.example.com/virt-instance-0006f26c/memory-available
+  node42.example.com/virt-instance-0006f26c/memory-last_update
+  node42.example.com/virt-instance-0006f26c/memory-major_fault
+  node42.example.com/virt-instance-0006f26c/memory-minor_fault
+  node42.example.com/virt-instance-0006f26c/memory-rss
+  node42.example.com/virt-instance-0006f26c/memory-swap_in
+  node42.example.com/virt-instance-0006f26c/memory-swap_out
+  node42.example.com/virt-instance-0006f26c/memory-total
+  node42.example.com/virt-instance-0006f26c/memory-unused
+  node42.example.com/virt-instance-0006f26c/memory-usable
+  node42.example.com/virt-instance-0006f26c/virt_cpu_total
+  node42.example.com/virt-instance-0006f26c/virt_vcpu-0
+
+You can get information on the metric's units from the online libvirt documentation.
+For instance, I<virt_cpu_total> is in nanoseconds.
+
 =over 4
 
 =item B<Connection> I<uri>
@@ -8813,7 +9303,7 @@ be set to C<var_lib_libvirt_images_image1.qcow2>.
 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
@@ -8823,7 +9313,11 @@ B<uuid> means use the guest's UUID. This is useful if you want to track the
 same guest across migrations.
 
 B<hostname> means to use the global B<Hostname> setting, which is probably not
-useful on its own because all guests will appear to have the same name.
+useful on its own because all guests will appear to have the same name. This is
+useful in conjunction with B<PluginInstanceFormat> though.
+
+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
@@ -8833,7 +9327,7 @@ At the moment of writing (collectd-5.5), hostname string is limited to 62
 characters. In case when combination of fields exceeds 62 characters,
 hostname will be truncated without a warning.
 
-=item B<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
@@ -8843,23 +9337,42 @@ setting B<name>.
 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.
+
+=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>
 
@@ -8910,6 +9423,39 @@ B<Note>: I<perf> metrics can't be collected if I<intel_rdt> plugin is enabled.
 
 =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>
@@ -8963,6 +9509,7 @@ Synopsis:
      Protocol "tcp"
      LogSendErrors true
      Prefix "collectd"
+     UseTags false
    </Node>
  </Plugin>
 
@@ -9000,13 +9547,20 @@ approach and logging errors fills syslog with unneeded messages.
 
 =item B<Prefix> I<String>
 
-When set, I<String> is added in front of the host name. Dots and whitespace are
-I<not> escaped in this string (see B<EscapeCharacter> below).
+When B<UseTags> is I<false>, B<Prefix> value is added in front of the host name.
+When B<UseTags> is I<true>, B<Prefix> value is added in front of series name.
+
+Dots and whitespace are I<not> escaped in this string (see B<EscapeCharacter>
+below).
 
 =item B<Postfix> I<String>
 
-When set, I<String> is appended to the host name. Dots and whitespace are
-I<not> escaped in this string (see B<EscapeCharacter> below).
+When B<UseTags> is I<false>, B<Postfix> value appended to the host name.
+When B<UseTags> is I<true>, B<Postgix> value appended to the end of series name
+(before the first ; that separates the name from the tags).
+
+Dots and whitespace are I<not> escaped in this string (see B<EscapeCharacter>
+below).
 
 =item B<EscapeCharacter> I<Char>
 
@@ -9028,6 +9582,8 @@ path component, for example C<host.cpu.0.cpu.idle>. If set to B<false> (the
 default), the plugin and plugin instance (and likewise the type and type
 instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
 
+Option value is not used when B<UseTags> is I<true>.
+
 =item B<AlwaysAppendDS> B<false>|B<true>
 
 If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
@@ -9040,12 +9596,31 @@ If set to B<false> (the default) the C<.> (dot) character is replaced with
 I<EscapeCharacter>. Otherwise, if set to B<true>, the C<.> (dot) character
 is preserved, i.e. passed through.
 
+Option value is not used when B<UseTags> is I<true>.
+
 =item B<DropDuplicateFields> B<false>|B<true>
 
 If set to B<true>, detect and remove duplicate components in Graphite metric
 names. For example, the metric name  C<host.load.load.shortterm> will
 be shortened to C<host.load.shortterm>.
 
+=item B<UseTags> B<false>|B<true>
+
+If set to B<true>, Graphite metric names will be generated as tagged series.
+This allows for much more flexibility than the traditional hierarchical layout.
+
+Example:
+C<test.single;host=example.com;plugin=test;plugin_instance=foo;type=single;type_instance=bar>
+
+You can use B<Postfix> option to add more tags by specifying it like
+C<;tag1=value1;tag2=value2>. Note what tagging support was added since Graphite
+version 1.1.x.
+
+If set to B<true>, the B<SeparateInstances> and B<PreserveSeparator> settings
+are not used.
+
+Default value: B<false>.
+
 =back
 
 =head2 Plugin C<write_log>
@@ -9214,6 +9789,13 @@ B<Options:>
 
 =over 4
 
+=item B<Host> I<Host>
+
+Bind to the hostname / address I<Host>. By default, the plugin will bind to the
+"any" address, i.e. accept packets sent to any of the hosts addresses.
+
+This option is supported only for libmicrohttpd newer than 0.9.0.
+
 =item B<Port> I<Port>
 
 Port the embedded webserver should listen on. Defaults to B<9103>.
@@ -9471,17 +10053,26 @@ been set to B<JSON>.
 =item B<GraphitePrefix> (B<Format>=I<Graphite> only)
 
 A prefix can be added in the metric name when outputting in the I<Graphite>
-format. It's added before the I<Host> name.
+format.
+
+When B<GraphiteUseTags> is I<false>, prefix is added before the I<Host> name.
 Metric name will be
 C<E<lt>prefixE<gt>E<lt>hostE<gt>E<lt>postfixE<gt>E<lt>pluginE<gt>E<lt>typeE<gt>E<lt>nameE<gt>>
 
+When B<GraphiteUseTags> is I<true>, prefix is added in front of series name.
+
 =item B<GraphitePostfix> (B<Format>=I<Graphite> only)
 
 A postfix can be added in the metric name when outputting in the I<Graphite>
-format. It's added after the I<Host> name.
+format.
+
+When B<GraphiteUseTags> is I<false>, postfix is added after the I<Host> name.
 Metric name will be
 C<E<lt>prefixE<gt>E<lt>hostE<gt>E<lt>postfixE<gt>E<lt>pluginE<gt>E<lt>typeE<gt>E<lt>nameE<gt>>
 
+When B<GraphiteUseTags> is I<true>, prefix value appended to the end of series
+name (before the first ; that separates the name from the tags).
+
 =item B<GraphiteEscapeChar> (B<Format>=I<Graphite> only)
 
 Specify a character to replace dots (.) in the host part of the metric name.
@@ -9496,6 +10087,8 @@ path component, for example C<host.cpu.0.cpu.idle>. If set to B<false> (the
 default), the plugin and plugin instance (and likewise the type and type
 instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
 
+Option value is not used when B<GraphiteUseTags> is I<true>.
+
 =item B<GraphiteAlwaysAppendDS> B<true>|B<false>
 
 If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
@@ -9508,6 +10101,14 @@ If set to B<false> (the default) the C<.> (dot) character is replaced with
 I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
 is preserved, i.e. passed through.
 
+Option value is not used when B<GraphiteUseTags> is I<true>.
+
+=item B<GraphiteUseTags> B<false>|B<true>
+
+If set to B<true> Graphite metric names will be generated as tagged series.
+
+Default value: B<false>.
+
 =item B<StoreRates> B<true>|B<false>
 
 If set to B<true> (the default), convert counter values to rates. If set to
@@ -9868,6 +10469,133 @@ attribute for each metric being sent out to I<Sensu>.
 
 =back
 
+=head2 Plugin C<write_stackdriver>
+
+The C<write_stackdriver> plugin writes metrics to the
+I<Google Stackdriver Monitoring> service.
+
+This plugin supports two authentication methods: When configured, credentials
+are read from the JSON credentials file specified with B<CredentialFile>.
+Alternatively, when running on
+I<Google Compute Engine> (GCE), an I<OAuth> token is retrieved from the
+I<metadata server> and used to authenticate to GCM.
+
+B<Synopsis:>
+
+ <Plugin write_stackdriver>
+   CredentialFile "/path/to/service_account.json"
+   <Resource "global">
+     Label "project_id" "monitored_project"
+   </Resource>
+ </Plugin>
+
+=over 4
+
+=item B<CredentialFile> I<file>
+
+Path to a JSON credentials file holding the credentials for a GCP service
+account.
+
+If B<CredentialFile> is not specified, the plugin uses I<Application Default
+Credentials>. That means which credentials are used depends on the environment:
+
+=over 4
+
+=item
+
+The environment variable C<GOOGLE_APPLICATION_CREDENTIALS> is checked. If this
+variable is specified it should point to a JSON file that defines the
+credentials.
+
+=item
+
+The path C<${HOME}/.config/gcloud/application_default_credentials.json> is
+checked. This where credentials used by the I<gcloud> command line utility are
+stored. You can use C<gcloud auth application-default login> to create these
+credentials.
+
+Please note that these credentials are often of your personal account, not a
+service account, and are therefore unfit to be used in a production
+environment.
+
+=item
+
+When running on GCE, the built-in service account associated with the virtual
+machine instance is used.
+See also the B<Email> option below.
+
+=back
+
+=item B<Project> I<Project>
+
+The I<Project ID> or the I<Project Number> of the I<Stackdriver Account>. The
+I<Project ID> is a string identifying the GCP project, which you can chose
+freely when creating a new project. The I<Project Number> is a 12-digit decimal
+number. You can look up both on the I<Developer Console>.
+
+This setting is optional. If not set, the project ID is read from the
+credentials file or determined from the GCE's metadata service.
+
+=item B<Email> I<Email> (GCE only)
+
+Choses the GCE I<Service Account> used for authentication.
+
+Each GCE instance has a C<default> I<Service Account> but may also be
+associated with additional I<Service Accounts>. This is often used to restrict
+the permissions of services running on the GCE instance to the required
+minimum. The I<write_stackdriver plugin> requires the
+C<https://www.googleapis.com/auth/monitoring> scope. When multiple I<Service
+Accounts> are available, this option selects which one is used by
+I<write_stackdriver plugin>.
+
+=item B<Resource> I<ResourceType>
+
+Configures the I<Monitored Resource> to use when storing metrics.
+More information on I<Monitored Resources> and I<Monitored Resource Types> are
+available at L<https://cloud.google.com/monitoring/api/resources>.
+
+This block takes one string argument, the I<ResourceType>. Inside the block are
+one or more B<Label> options which configure the resource labels.
+
+This block is optional. The default value depends on the runtime environment:
+on GCE, the C<gce_instance> resource type is used, otherwise the C<global>
+resource type ist used:
+
+=over 4
+
+=item
+
+B<On GCE>, defaults to the equivalent of this config:
+
+  <Resource "gce_instance">
+    Label "project_id" "<project_id>"
+    Label "instance_id" "<instance_id>"
+    Label "zone" "<zone>"
+  </Resource>
+
+The values for I<project_id>, I<instance_id> and I<zone> are read from the GCE
+metadata service.
+
+=item
+
+B<Elsewhere>, i.e. not on GCE, defaults to the equivalent of this config:
+
+  <Resource "global">
+    Label "project_id" "<Project>"
+  </Resource>
+
+Where I<Project> refers to the value of the B<Project> option or the project ID
+inferred from the B<CredentialFile>.
+
+=back
+
+=item B<Url> I<Url>
+
+URL of the I<Stackdriver Monitoring> API. Defaults to
+C<https://monitoring.googleapis.com/v3>.
+
+=back
+
 =head2 Plugin C<xencpu>
 
 This plugin collects metrics of hardware CPU load for machine running Xen
@@ -10856,7 +11584,7 @@ be an FQDN.
 =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>.
 
index 1dd899b..60707a1 100644 (file)
@@ -69,7 +69,7 @@ Output usage information and exit.
 
 =head1 PLUGINS
 
-As noted above, the real power of collectd lies within it's plugins. A
+As noted above, the real power of collectd lies within its plugins. A
 (hopefully complete) list of plugins and short descriptions can be found in the
 F<README> file that is distributed with the sourcecode. If you're using a
 package it's a good bet to search somewhere near F</usr/share/doc/collectd>.
index 4a7d1a6..0e2b021 100644 (file)
 #define WCOREDUMP(s) 0
 #endif /* ! WCOREDUMP */
 
-static int loop = 0;
-static int restart = 0;
+static int loop;
+static int restart;
 
-static const char *pidfile = NULL;
-static pid_t collectd_pid = 0;
+static const char *pidfile;
+static pid_t collectd_pid;
 
 __attribute__((noreturn)) static void exit_usage(const char *name) {
   printf("Usage: %s <options> [-- <collectd options>]\n"
@@ -95,26 +95,26 @@ __attribute__((noreturn)) static void exit_usage(const char *name) {
 } /* exit_usage */
 
 static int pidfile_create(void) {
-  FILE *file = NULL;
+  FILE *file;
 
-  if (NULL == pidfile)
+  if (pidfile == NULL)
     pidfile = COLLECTDMON_PIDFILE;
 
-  if (NULL == (file = fopen(pidfile, "w"))) {
+  if ((file = fopen(pidfile, "w")) == NULL) {
     syslog(LOG_ERR, "Error: couldn't open PID-file (%s) for writing: %s",
            pidfile, strerror(errno));
     return -1;
   }
 
-  fprintf(file, "%i\n", (int)getpid());
+  fprintf(file, "%d\n", (int)getpid());
   fclose(file);
   return 0;
 } /* pidfile_create */
 
 static int pidfile_delete(void) {
-  assert(NULL != pidfile);
+  assert(pidfile);
 
-  if (0 != unlink(pidfile)) {
+  if (unlink(pidfile) != 0) {
     syslog(LOG_ERR, "Error: couldn't delete PID-file (%s): %s", pidfile,
            strerror(errno));
     return -1;
@@ -123,41 +123,37 @@ static int pidfile_delete(void) {
 } /* pidfile_remove */
 
 static int daemonize(void) {
-  struct rlimit rl;
-  int dev_null;
-
-  pid_t pid = 0;
-  int i = 0;
-
-  if (0 != chdir("/")) {
+  if (chdir("/") != 0) {
     fprintf(stderr, "Error: chdir() failed: %s\n", strerror(errno));
     return -1;
   }
 
-  if (0 != getrlimit(RLIMIT_NOFILE, &rl)) {
+  struct rlimit rl;
+  if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
     fprintf(stderr, "Error: getrlimit() failed: %s\n", strerror(errno));
     return -1;
   }
 
-  if (0 > (pid = fork())) {
+  pid_t pid = fork();
+  if (pid < 0) {
     fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno));
     return -1;
   } else if (pid != 0) {
     exit(0);
   }
 
-  if (0 != pidfile_create())
+  if (pidfile_create() != 0)
     return -1;
 
   setsid();
 
-  if (RLIM_INFINITY == rl.rlim_max)
+  if (rl.rlim_max == RLIM_INFINITY)
     rl.rlim_max = 1024;
 
-  for (i = 0; i < (int)rl.rlim_max; ++i)
+  for (int i = 0; i < (int)rl.rlim_max; ++i)
     close(i);
 
-  dev_null = open("/dev/null", O_RDWR);
+  int dev_null = open("/dev/null", O_RDWR);
   if (dev_null == -1) {
     syslog(LOG_ERR, "Error: couldn't open /dev/null: %s", strerror(errno));
     return -1;
@@ -192,9 +188,9 @@ static int daemonize(void) {
 } /* daemonize */
 
 static int collectd_start(char **argv) {
-  pid_t pid = 0;
+  pid_t pid = fork();
 
-  if (0 > (pid = fork())) {
+  if (pid < 0) {
     syslog(LOG_ERR, "Error: fork() failed: %s", strerror(errno));
     return -1;
   } else if (pid != 0) {
@@ -208,10 +204,10 @@ static int collectd_start(char **argv) {
 } /* collectd_start */
 
 static int collectd_stop(void) {
-  if (0 == collectd_pid)
+  if (collectd_pid == 0)
     return 0;
 
-  if (0 != kill(collectd_pid, SIGTERM)) {
+  if (kill(collectd_pid, SIGTERM) != 0) {
     syslog(LOG_ERR, "Error: kill() failed: %s", strerror(errno));
     return -1;
   }
@@ -230,7 +226,7 @@ static void sig_hup_handler(int __attribute__((unused)) signo) {
 
 static void log_status(int status) {
   if (WIFEXITED(status)) {
-    if (0 == WEXITSTATUS(status))
+    if (WEXITSTATUS(status) == 0)
       syslog(LOG_INFO, "Info: collectd terminated with exit status %i",
              WEXITSTATUS(status));
     else
@@ -246,24 +242,24 @@ static void log_status(int status) {
 static void check_respawn(void) {
   time_t t = time(NULL);
 
-  static time_t timestamp = 0;
-  static int counter = 0;
+  static time_t timestamp;
+  static int counter;
 
-  if ((t - 120) < timestamp)
+  if (timestamp >= t - 120)
     ++counter;
   else {
     timestamp = t;
     counter = 0;
   }
 
-  if (10 < counter) {
+  if (counter >= 10) {
     unsigned int time_left = 300;
 
     syslog(LOG_ERR, "Error: collectd is respawning too fast - "
                     "disabled for %i seconds",
            time_left);
 
-    while ((0 < (time_left = sleep(time_left))) && (0 == loop))
+    while (((time_left = sleep(time_left)) > 0) && loop == 0)
       ;
   }
   return;
@@ -274,15 +270,13 @@ int main(int argc, char **argv) {
   char *collectd = NULL;
   char **collectd_argv = NULL;
 
-  struct sigaction sa;
-
   int i = 0;
 
   /* parse command line options */
   while (42) {
     int c = getopt(argc, argv, "hc:P:");
 
-    if (-1 == c)
+    if (c == -1)
       break;
 
     switch (c) {
@@ -299,19 +293,19 @@ int main(int argc, char **argv) {
   }
 
   for (i = optind; i < argc; ++i)
-    if (0 == strcmp(argv[i], "-f"))
+    if (strcmp(argv[i], "-f") == 0)
       break;
 
   /* i < argc => -f already present */
   collectd_argc = 1 + argc - optind + ((i < argc) ? 0 : 1);
-  collectd_argv = (char **)calloc(collectd_argc + 1, sizeof(char *));
+  collectd_argv = calloc(collectd_argc + 1, sizeof(*collectd_argv));
 
-  if (NULL == collectd_argv) {
+  if (collectd_argv == NULL) {
     fprintf(stderr, "Out of memory.");
     return 3;
   }
 
-  collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd;
+  collectd_argv[0] = (collectd == NULL) ? "collectd" : collectd;
 
   if (i == argc)
     collectd_argv[collectd_argc - 1] = "-f";
@@ -323,22 +317,23 @@ int main(int argc, char **argv) {
 
   openlog("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON);
 
-  if (-1 == daemonize()) {
+  if (daemonize() == -1) {
     free(collectd_argv);
     return 1;
   }
 
-  sa.sa_handler = sig_int_term_handler;
-  sa.sa_flags = 0;
+  struct sigaction sa = {
+      .sa_handler = sig_int_term_handler, .sa_flags = 0,
+  };
   sigemptyset(&sa.sa_mask);
 
-  if (0 != sigaction(SIGINT, &sa, NULL)) {
+  if (sigaction(SIGINT, &sa, NULL) != 0) {
     syslog(LOG_ERR, "Error: sigaction() failed: %s", strerror(errno));
     free(collectd_argv);
     return 1;
   }
 
-  if (0 != sigaction(SIGTERM, &sa, NULL)) {
+  if (sigaction(SIGTERM, &sa, NULL) != 0) {
     syslog(LOG_ERR, "Error: sigaction() failed: %s", strerror(errno));
     free(collectd_argv);
     return 1;
@@ -346,24 +341,24 @@ int main(int argc, char **argv) {
 
   sa.sa_handler = sig_hup_handler;
 
-  if (0 != sigaction(SIGHUP, &sa, NULL)) {
+  if (sigaction(SIGHUP, &sa, NULL) != 0) {
     syslog(LOG_ERR, "Error: sigaction() failed: %s", strerror(errno));
     free(collectd_argv);
     return 1;
   }
 
-  while (0 == loop) {
+  while (loop == 0) {
     int status = 0;
 
-    if (0 != collectd_start(collectd_argv)) {
+    if (collectd_start(collectd_argv) != 0) {
       syslog(LOG_ERR, "Error: failed to start collectd.");
       break;
     }
 
-    assert(0 < collectd_pid);
+    assert(collectd_pid >= 0);
     while ((collectd_pid != waitpid(collectd_pid, &status, 0)) &&
-           (EINTR == errno))
-      if ((0 != loop) || (0 != restart))
+           errno == EINTR)
+      if (loop != 0 || restart != 0)
         collectd_stop();
 
     collectd_pid = 0;
@@ -371,10 +366,10 @@ int main(int argc, char **argv) {
     log_status(status);
     check_respawn();
 
-    if (0 != restart) {
+    if (restart != 0) {
       syslog(LOG_INFO, "Info: restarting collectd");
       restart = 0;
-    } else if (0 == loop)
+    } else if (loop == 0)
       syslog(LOG_WARNING, "Warning: restarting collectd");
   }
 
index 3cd2dee..7b61eef 100644 (file)
@@ -23,8 +23,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if !KERNEL_LINUX
 #error "No applicable input method."
@@ -41,7 +41,7 @@ static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
     Each table/chain combo that will be queried goes into this list
 */
 
-static int old_files = 0;
+static int old_files;
 
 static int conntrack_config(const char *key, const char *value) {
   if (strcmp(key, "OldFiles") == 0)
index 461fb0a..acf3a74 100644 (file)
@@ -23,8 +23,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #ifdef HAVE_SYS_SYSCTL_H
 #include <sys/sysctl.h>
@@ -85,8 +85,7 @@ static int cs_read(void) {
 
   fh = fopen("/proc/stat", "r");
   if (fh == NULL) {
-    ERROR("contextswitch plugin: unable to open /proc/stat: %s",
-          sstrerror(errno, buffer, sizeof(buffer)));
+    ERROR("contextswitch plugin: unable to open /proc/stat: %s", STRERRNO);
     return -1;
   }
 
@@ -126,9 +125,7 @@ static int cs_read(void) {
   status =
       perfstat_cpu_total(NULL, &perfcputotal, sizeof(perfstat_cpu_total_t), 1);
   if (status < 0) {
-    char errbuf[1024];
-    ERROR("contextswitch plugin: perfstat_cpu_total: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("contextswitch plugin: perfstat_cpu_total: %s", STRERRNO);
     return -1;
   }
 
index 6b1b7a6..e7f3c18 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -29,8 +29,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #ifdef HAVE_MACH_KERN_RETURN_H
 #include <mach/kern_return.h>
@@ -183,23 +183,23 @@ static int pnumcpu;
 struct cpu_state_s {
   value_to_rate_state_t conv;
   gauge_t rate;
-  _Bool has_value;
+  bool has_value;
 };
 typedef struct cpu_state_s cpu_state_t;
 
-static cpu_state_t *cpu_states = NULL;
-static size_t cpu_states_num = 0; /* #cpu_states allocated */
+static cpu_state_t *cpu_states;
+static size_t cpu_states_num; /* #cpu_states allocated */
 
 /* Highest CPU number in the current iteration. Used by the dispatch logic to
  * determine how many CPUs there were. Reset to 0 by cpu_reset(). */
-static size_t global_cpu_num = 0;
+static size_t global_cpu_num;
 
-static _Bool report_by_cpu = 1;
-static _Bool report_by_state = 1;
-static _Bool report_percent = 0;
-static _Bool report_num_cpu = 0;
-static _Bool report_guest = 0;
-static _Bool subtract_guest = 1;
+static bool report_by_cpu = true;
+static bool report_by_state = true;
+static bool report_percent;
+static bool report_num_cpu;
+static bool report_guest;
+static bool subtract_guest = true;
 
 static const char *config_keys[] = {"ReportByCpu",      "ReportByState",
                                     "ReportNumCpu",     "ValuesPercentage",
@@ -209,17 +209,17 @@ static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 static int cpu_config(char const *key, char const *value) /* {{{ */
 {
   if (strcasecmp(key, "ReportByCpu") == 0)
-    report_by_cpu = IS_TRUE(value) ? 1 : 0;
+    report_by_cpu = IS_TRUE(value);
   else if (strcasecmp(key, "ValuesPercentage") == 0)
-    report_percent = IS_TRUE(value) ? 1 : 0;
+    report_percent = IS_TRUE(value);
   else if (strcasecmp(key, "ReportByState") == 0)
-    report_by_state = IS_TRUE(value) ? 1 : 0;
+    report_by_state = IS_TRUE(value);
   else if (strcasecmp(key, "ReportNumCpu") == 0)
-    report_num_cpu = IS_TRUE(value) ? 1 : 0;
+    report_num_cpu = IS_TRUE(value);
   else if (strcasecmp(key, "ReportGuestState") == 0)
-    report_guest = IS_TRUE(value) ? 1 : 0;
+    report_guest = IS_TRUE(value);
   else if (strcasecmp(key, "SubtractGuestState") == 0)
-    subtract_guest = IS_TRUE(value) ? 1 : 0;
+    subtract_guest = IS_TRUE(value);
   else
     return -1;
 
@@ -279,8 +279,7 @@ static int init(void) {
 
   status = sysctl(mib, STATIC_ARRAY_SIZE(mib), &numcpu, &numcpu_size, NULL, 0);
   if (status == -1) {
-    char errbuf[1024];
-    WARNING("cpu plugin: sysctl: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("cpu plugin: sysctl: %s", STRERRNO);
     return -1;
   }
 /* #endif CAN_USE_SYSCTL */
@@ -291,9 +290,7 @@ static int init(void) {
   numcpu_size = sizeof(numcpu);
 
   if (sysctlbyname("hw.ncpu", &numcpu, &numcpu_size, NULL, 0) < 0) {
-    char errbuf[1024];
-    WARNING("cpu plugin: sysctlbyname(hw.ncpu): %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("cpu plugin: sysctlbyname(hw.ncpu): %s", STRERRNO);
     return -1;
   }
 
@@ -301,9 +298,7 @@ static int init(void) {
   numcpu_size = sizeof(maxcpu);
 
   if (sysctlbyname("kern.smp.maxcpus", &maxcpu, &numcpu_size, NULL, 0) < 0) {
-    char errbuf[1024];
-    WARNING("cpu plugin: sysctlbyname(kern.smp.maxcpus): %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("cpu plugin: sysctlbyname(kern.smp.maxcpus): %s", STRERRNO);
     return -1;
   }
 #else
@@ -436,7 +431,7 @@ static void aggregate(gauge_t *sum_by_state) /* {{{ */
     }
 
     if (!isnan(this_cpu_states[COLLECTD_CPU_STATE_ACTIVE].rate))
-      this_cpu_states[COLLECTD_CPU_STATE_ACTIVE].has_value = 1;
+      this_cpu_states[COLLECTD_CPU_STATE_ACTIVE].has_value = true;
 
     RATE_ADD(sum_by_state[COLLECTD_CPU_STATE_ACTIVE],
              this_cpu_states[COLLECTD_CPU_STATE_ACTIVE].rate);
@@ -447,9 +442,7 @@ static void aggregate(gauge_t *sum_by_state) /* {{{ */
   perfstat_cpu_total_t cputotal = {0};
 
   if (!perfstat_cpu_total(NULL, &cputotal, sizeof(cputotal), 1)) {
-    char errbuf[1024];
-    WARNING("cpu plugin: perfstat_cpu_total: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("cpu plugin: perfstat_cpu_total: %s", STRERRNO);
     return;
   }
 
@@ -512,7 +505,7 @@ static void cpu_commit_num_cpu(gauge_t value) /* {{{ */
 static void cpu_reset(void) /* {{{ */
 {
   for (size_t i = 0; i < cpu_states_num; i++)
-    cpu_states[i].has_value = 0;
+    cpu_states[i].has_value = false;
 
   global_cpu_num = 0;
 } /* }}} void cpu_reset */
@@ -595,7 +588,7 @@ static int cpu_stage(size_t cpu_num, size_t state, derive_t d,
     return status;
 
   s->rate = rate;
-  s->has_value = 1;
+  s->has_value = true;
   return 0;
 } /* }}} int cpu_stage */
 
@@ -648,9 +641,7 @@ static int cpu_read(void) {
   int numfields;
 
   if ((fh = fopen("/proc/stat", "r")) == NULL) {
-    char errbuf[1024];
-    ERROR("cpu plugin: fopen (/proc/stat) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("cpu plugin: fopen (/proc/stat) failed: %s", STRERRNO);
     return -1;
   }
 
@@ -763,9 +754,7 @@ static int cpu_read(void) {
       status = sysctl(mib, STATIC_ARRAY_SIZE(mib), cpuinfo[i], &cpuinfo_size,
                       NULL, 0);
       if (status == -1) {
-        char errbuf[1024];
-        ERROR("cpu plugin: sysctl failed: %s.",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("cpu plugin: sysctl failed: %s.", STRERRNO);
         return -1;
       }
     }
@@ -780,9 +769,7 @@ static int cpu_read(void) {
     status = sysctl(mib, STATIC_ARRAY_SIZE(mib), &cpuinfo_tmp, &cpuinfo_size,
                     NULL, 0);
     if (status == -1) {
-      char errbuf[1024];
-      ERROR("cpu plugin: sysctl failed: %s.",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("cpu plugin: sysctl failed: %s.", STRERRNO);
       return -1;
     }
 
@@ -810,9 +797,7 @@ static int cpu_read(void) {
 
   cpuinfo_size = sizeof(cpuinfo);
   if (sysctlbyname("kern.cp_times", &cpuinfo, &cpuinfo_size, NULL, 0) < 0) {
-    char errbuf[1024];
-    ERROR("cpu plugin: sysctlbyname failed: %s.",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("cpu plugin: sysctlbyname failed: %s.", STRERRNO);
     return -1;
   }
 
@@ -833,9 +818,7 @@ static int cpu_read(void) {
   cpuinfo_size = sizeof(cpuinfo);
 
   if (sysctlbyname("kern.cp_time", &cpuinfo, &cpuinfo_size, NULL, 0) < 0) {
-    char errbuf[1024];
-    ERROR("cpu plugin: sysctlbyname failed: %s.",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("cpu plugin: sysctlbyname failed: %s.", STRERRNO);
     return -1;
   }
 
@@ -869,9 +852,7 @@ static int cpu_read(void) {
 
   numcpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
   if (numcpu == -1) {
-    char errbuf[1024];
-    WARNING("cpu plugin: perfstat_cpu: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("cpu plugin: perfstat_cpu: %s", STRERRNO);
     return -1;
   }
 
@@ -883,9 +864,7 @@ static int cpu_read(void) {
 
   id.name[0] = '\0';
   if ((cpus = perfstat_cpu(&id, perfcpu, sizeof(perfstat_cpu_t), numcpu)) < 0) {
-    char errbuf[1024];
-    WARNING("cpu plugin: perfstat_cpu: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("cpu plugin: perfstat_cpu: %s", STRERRNO);
     return -1;
   }
 
index 0139947..35ec07f 100644 (file)
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
-static int num_cpu = 0;
+#define MAX_AVAIL_FREQS 20
+
+static int num_cpu;
+
+struct cpu_data_t {
+  value_to_rate_state_t time_state[MAX_AVAIL_FREQS];
+} * cpu_data;
+
+/* Flags denoting capability of reporting CPU frequency statistics. */
+static bool report_p_stats = false;
+
+static void cpufreq_stats_init(void) {
+  cpu_data = calloc(num_cpu, sizeof(*cpu_data));
+  if (cpu_data == NULL)
+    return;
+
+  report_p_stats = true;
+
+  /* Check for stats module and disable if not present. */
+  for (int i = 0; i < num_cpu; i++) {
+    char filename[PATH_MAX];
+
+    snprintf(filename, sizeof(filename),
+             "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", i);
+    if (access(filename, R_OK)) {
+      NOTICE("cpufreq plugin: File %s not exists or no access. P-State "
+             "statistics will not be reported. Check if `cpufreq-stats' kernel "
+             "module is loaded.",
+             filename);
+      report_p_stats = false;
+      break;
+    }
+
+    snprintf(filename, sizeof(filename),
+             "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", i);
+    if (access(filename, R_OK)) {
+      NOTICE("cpufreq plugin: File %s not exists or no access. P-State "
+             "statistics will not be reported. Check if `cpufreq-stats' kernel "
+             "module is loaded.",
+             filename);
+      report_p_stats = false;
+      break;
+    }
+  }
+  return;
+}
 
 static int cpufreq_init(void) {
-  int status;
-  char filename[256];
+  char filename[PATH_MAX];
 
   num_cpu = 0;
 
   while (1) {
-    status = snprintf(filename, sizeof(filename),
-                      "/sys/devices/system/cpu/cpu%d/cpufreq/"
-                      "scaling_cur_freq",
-                      num_cpu);
+    int status = snprintf(filename, sizeof(filename),
+                          "/sys/devices/system/cpu/cpu%d/cpufreq/"
+                          "scaling_cur_freq",
+                          num_cpu);
     if ((status < 1) || ((unsigned int)status >= sizeof(filename)))
       break;
 
@@ -48,6 +92,7 @@ static int cpufreq_init(void) {
   }
 
   INFO("cpufreq plugin: Found %d CPU%s", num_cpu, (num_cpu == 1) ? "" : "s");
+  cpufreq_stats_init();
 
   if (num_cpu == 0)
     plugin_unregister_read("cpufreq");
@@ -55,23 +100,97 @@ static int cpufreq_init(void) {
   return 0;
 } /* int cpufreq_init */
 
-static void cpufreq_submit(int cpu_num, value_t value) {
+static void cpufreq_submit(int cpu_num, const char *type,
+                           const char *type_instance, value_t *value) {
   value_list_t vl = VALUE_LIST_INIT;
 
-  vl.values = &value;
+  vl.values = value;
   vl.values_len = 1;
   sstrncpy(vl.plugin, "cpufreq", sizeof(vl.plugin));
-  sstrncpy(vl.type, "cpufreq", sizeof(vl.type));
-  snprintf(vl.type_instance, sizeof(vl.type_instance), "%i", cpu_num);
+  snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", cpu_num);
+  if (type != NULL)
+    sstrncpy(vl.type, type, sizeof(vl.type));
+  if (type_instance != NULL)
+    sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
 
   plugin_dispatch_values(&vl);
 }
 
+static void cpufreq_read_stats(int cpu) {
+  char filename[PATH_MAX];
+  /* Read total transitions for cpu frequency */
+  snprintf(filename, sizeof(filename),
+           "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", cpu);
+
+  value_t v;
+  if (parse_value_file(filename, &v, DS_TYPE_DERIVE) != 0) {
+    ERROR("cpufreq plugin: Reading \"%s\" failed.", filename);
+    return;
+  }
+  cpufreq_submit(cpu, "transitions", NULL, &v);
+
+  /* Determine percentage time in each state for cpu during previous
+   * interval. */
+  snprintf(filename, sizeof(filename),
+           "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpu);
+
+  FILE *fh = fopen(filename, "r");
+  if (fh == NULL) {
+    ERROR("cpufreq plugin: Reading \"%s\" failed.", filename);
+    return;
+  }
+
+  int state_index = 0;
+  cdtime_t now = cdtime();
+  char buffer[DATA_MAX_NAME_LEN];
+
+  while (fgets(buffer, sizeof(buffer), fh) != NULL) {
+    unsigned int frequency;
+    unsigned long long time;
+
+    /*
+     * State time units is 10ms. To get rate of seconds per second
+     * we have to divide by 100. To get percents we have to multiply it
+     * by 100 back. So, just use parsed value directly.
+     */
+    if (!sscanf(buffer, "%u%llu", &frequency, &time)) {
+      ERROR("cpufreq plugin: Reading \"%s\" failed.", filename);
+      break;
+    }
+
+    char state[DATA_MAX_NAME_LEN];
+    snprintf(state, sizeof(state), "%u", frequency);
+
+    if (state_index >= MAX_AVAIL_FREQS) {
+      NOTICE("cpufreq plugin: Found too many frequency states (%d > %d). "
+             "Plugin needs to be recompiled. Please open a bug report for "
+             "this.",
+             (state_index + 1), MAX_AVAIL_FREQS);
+      break;
+    }
+
+    gauge_t g;
+    if (value_to_rate(&g, (value_t){.derive = time}, DS_TYPE_DERIVE, now,
+                      &(cpu_data[cpu].time_state[state_index])) == 0) {
+      /*
+       * Due to some inaccuracy reported value can be a bit greatrer than 100.1.
+       * That produces gaps on charts.
+       */
+      if (g > 100.1)
+        g = 100.1;
+      cpufreq_submit(cpu, "percent", state, &(value_t){.gauge = g});
+    }
+    state_index++;
+  }
+  fclose(fh);
+}
+
 static int cpufreq_read(void) {
-  for (int i = 0; i < num_cpu; i++) {
+  for (int cpu = 0; cpu < num_cpu; cpu++) {
     char filename[PATH_MAX];
+    /* Read cpu frequency */
     snprintf(filename, sizeof(filename),
-             "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
+             "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu);
 
     value_t v;
     if (parse_value_file(filename, &v, DS_TYPE_GAUGE) != 0) {
@@ -82,9 +201,11 @@ static int cpufreq_read(void) {
     /* convert kHz to Hz */
     v.gauge *= 1000.0;
 
-    cpufreq_submit(i, v);
-  }
+    cpufreq_submit(cpu, "cpufreq", NULL, &v);
 
+    if (report_p_stats)
+      cpufreq_read_stats(cpu);
+  }
   return 0;
 } /* int cpufreq_read */
 
index aa14cc1..b5cbe66 100644 (file)
@@ -32,9 +32,9 @@
 
 #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;
index debe095..953473f 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -23,8 +23,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 
 /*
@@ -33,9 +33,9 @@
 static const char *config_keys[] = {"DataDir", "StoreRates"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static char *datadir = NULL;
-static int store_rates = 0;
-static int use_stdio = 0;
+static char *datadir;
+static int store_rates;
+static int use_stdio;
 
 static int value_list_to_string(char *buffer, int buffer_len,
                                 const data_set_t *ds, const value_list_t *vl) {
@@ -74,8 +74,8 @@ static int value_list_to_string(char *buffer, int buffer_len,
       }
       status = snprintf(buffer + offset, buffer_len - offset, ",%lf", rates[i]);
     } else if (ds->ds[i].type == DS_TYPE_COUNTER) {
-      status = snprintf(buffer + offset, buffer_len - offset, ",%llu",
-                        vl->values[i].counter);
+      status = snprintf(buffer + offset, buffer_len - offset, ",%" PRIu64,
+                        (uint64_t)vl->values[i].counter);
     } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
       status = snprintf(buffer + offset, buffer_len - offset, ",%" PRIi64,
                         vl->values[i].derive);
@@ -161,9 +161,7 @@ static int csv_create_file(const char *filename, const data_set_t *ds) {
 
   csv = fopen(filename, "w");
   if (csv == NULL) {
-    char errbuf[1024];
-    ERROR("csv plugin: fopen (%s) failed: %s", filename,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("csv plugin: fopen (%s) failed: %s", filename, STRERRNO);
     return -1;
   }
 
@@ -192,12 +190,12 @@ static int csv_config(const char *key, const char *value) {
     }
     datadir = strdup(value);
     if (datadir != NULL) {
-      int len = strlen(datadir);
+      size_t len = strlen(datadir);
       while ((len > 0) && (datadir[len - 1] == '/')) {
         len--;
         datadir[len] = '\0';
       }
-      if (len <= 0) {
+      if (len == 0) {
         free(datadir);
         datadir = NULL;
       }
@@ -258,9 +256,7 @@ static int csv_write(const data_set_t *ds, const value_list_t *vl,
       if (csv_create_file(filename, ds))
         return -1;
     } else {
-      char errbuf[1024];
-      ERROR("stat(%s) failed: %s", filename,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("stat(%s) failed: %s", filename, STRERRNO);
       return -1;
     }
   } else if (!S_ISREG(statbuf.st_mode)) {
@@ -270,9 +266,7 @@ static int csv_write(const data_set_t *ds, const value_list_t *vl,
 
   csv = fopen(filename, "a");
   if (csv == NULL) {
-    char errbuf[1024];
-    ERROR("csv plugin: fopen (%s) failed: %s", filename,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("csv plugin: fopen (%s) failed: %s", filename, STRERRNO);
     return -1;
   }
   csv_fd = fileno(csv);
@@ -283,9 +277,7 @@ static int csv_write(const data_set_t *ds, const value_list_t *vl,
 
   status = fcntl(csv_fd, F_SETLK, &fl);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("csv plugin: flock (%s) failed: %s", filename,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("csv plugin: flock (%s) failed: %s", filename, STRERRNO);
     fclose(csv);
     return -1;
   }
index 35ec1f8..9ad3dc8 100644 (file)
 
 #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>
@@ -60,14 +60,14 @@ struct web_page_s /* {{{ */
   char *user;
   char *pass;
   char *credentials;
-  _Bool digest;
-  _Bool verify_peer;
-  _Bool verify_host;
+  bool digest;
+  bool verify_peer;
+  bool verify_host;
   char *cacert;
   struct curl_slist *headers;
   char *post_body;
-  _Bool response_time;
-  _Bool response_code;
+  bool response_time;
+  bool response_code;
   int timeout;
   curl_stats_t *stats;
 
@@ -78,19 +78,13 @@ struct web_page_s /* {{{ */
   size_t buffer_fill;
 
   web_match_t *matches;
-
-  web_page_t *next;
 }; /* }}} */
 
 /*
- * Global variables;
- */
-/* static CURLM *curl = NULL; */
-static web_page_t *pages_g = NULL;
-
-/*
  * Private functions
  */
+static int cc_read_page(user_data_t *ud);
+
 static size_t cc_curl_callback(void *buf, /* {{{ */
                                size_t size, size_t nmemb, void *user_data) {
   web_page_t *wp;
@@ -138,8 +132,9 @@ static void cc_web_match_free(web_match_t *wm) /* {{{ */
   sfree(wm);
 } /* }}} void cc_web_match_free */
 
-static void cc_web_page_free(web_page_t *wp) /* {{{ */
+static void cc_web_page_free(void *arg) /* {{{ */
 {
+  web_page_t *wp = (web_page_t *)arg;
   if (wp == NULL)
     return;
 
@@ -162,7 +157,6 @@ static void cc_web_page_free(web_page_t *wp) /* {{{ */
   sfree(wp->buffer);
 
   cc_web_match_free(wp->matches);
-  cc_web_page_free(wp->next);
   sfree(wp);
 } /* }}} void cc_web_page_free */
 
@@ -401,6 +395,7 @@ static int cc_page_init_curl(web_page_t *wp) /* {{{ */
 
 static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
 {
+  cdtime_t interval = 0;
   web_page_t *page;
   int status;
 
@@ -418,11 +413,11 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
   page->url = NULL;
   page->user = NULL;
   page->pass = NULL;
-  page->digest = 0;
-  page->verify_peer = 1;
-  page->verify_host = 1;
-  page->response_time = 0;
-  page->response_code = 0;
+  page->digest = false;
+  page->verify_peer = true;
+  page->verify_host = true;
+  page->response_time = false;
+  page->response_code = false;
   page->timeout = -1;
   page->stats = NULL;
 
@@ -465,6 +460,8 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
       status = cc_config_append_string("Header", &page->headers, child);
     else if (strcasecmp("Post", child->key) == 0)
       status = cf_util_get_string(child, &page->post_body);
+    else if (strcasecmp("Interval", child->key) == 0)
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Timeout", child->key) == 0)
       status = cf_util_get_int(child, &page->timeout);
     else if (strcasecmp("Statistics", child->key) == 0) {
@@ -508,17 +505,15 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
     return status;
   }
 
-  /* Add the new page to the linked list */
-  if (pages_g == NULL)
-    pages_g = page;
-  else {
-    web_page_t *prev;
+  /* If all went well, register this page for reading */
+  char *cb_name = ssnprintf_alloc("curl-%s-%s", page->instance, page->url);
 
-    prev = pages_g;
-    while (prev->next != NULL)
-      prev = prev->next;
-    prev->next = page;
-  }
+  plugin_register_complex_read(/* group = */ NULL, cb_name, cc_read_page,
+                               interval,
+                               &(user_data_t){
+                                   .data = page, .free_func = cc_web_page_free,
+                               });
+  sfree(cb_name);
 
   return 0;
 } /* }}} int cc_config_add_page */
@@ -557,10 +552,6 @@ static int cc_config(oconfig_item_t *ci) /* {{{ */
 
 static int cc_init(void) /* {{{ */
 {
-  if (pages_g == NULL) {
-    INFO("curl plugin: No pages have been defined.");
-    return -1;
-  }
   curl_global_init(CURL_GLOBAL_SSL);
   return 0;
 } /* }}} int cc_init */
@@ -609,8 +600,16 @@ static void cc_submit_response_time(const web_page_t *wp, /* {{{ */
   plugin_dispatch_values(&vl);
 } /* }}} void cc_submit_response_time */
 
-static int cc_read_page(web_page_t *wp) /* {{{ */
+static int cc_read_page(user_data_t *ud) /* {{{ */
 {
+
+  if ((ud == NULL) || (ud->data == NULL)) {
+    ERROR("curl plugin: cc_read_page: Invalid user data.");
+    return -1;
+  }
+
+  web_page_t *wp = (web_page_t *)ud->data;
+
   int status;
   cdtime_t start = 0;
 
@@ -667,25 +666,7 @@ static int cc_read_page(web_page_t *wp) /* {{{ */
   return 0;
 } /* }}} int cc_read_page */
 
-static int cc_read(void) /* {{{ */
-{
-  for (web_page_t *wp = pages_g; wp != NULL; wp = wp->next)
-    cc_read_page(wp);
-
-  return 0;
-} /* }}} int cc_read */
-
-static int cc_shutdown(void) /* {{{ */
-{
-  cc_web_page_free(pages_g);
-  pages_g = NULL;
-
-  return 0;
-} /* }}} int cc_shutdown */
-
 void module_register(void) {
   plugin_register_complex_config("curl", cc_config);
   plugin_register_init("curl", cc_init);
-  plugin_register_read("curl", cc_read);
-  plugin_register_shutdown("curl", cc_shutdown);
 } /* void module_register */
index a2f287a..a26664f 100644 (file)
 
 #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>
@@ -74,7 +74,7 @@ typedef struct {
  * exists for this part of the JSON structure. */
 typedef struct {
   cj_tree_entry_t *entry;
-  _Bool in_array;
+  bool in_array;
   int index;
   char name[DATA_MAX_NAME_LEN];
 } cj_state_t;
@@ -91,13 +91,12 @@ struct cj_s /* {{{ */
   char *user;
   char *pass;
   char *credentials;
-  _Bool digest;
-  _Bool verify_peer;
-  _Bool verify_host;
+  bool digest;
+  bool verify_peer;
+  bool verify_host;
   char *cacert;
   struct curl_slist *headers;
   char *post_body;
-  cdtime_t interval;
   int timeout;
   curl_stats_t *stats;
 
@@ -237,7 +236,7 @@ static int cj_cb_number(void *ctx, const char *number, yajl_len_t number_len) {
   /* Create a null-terminated version of the string. */
   char buffer[number_len + 1];
   memcpy(buffer, number, number_len);
-  buffer[sizeof(buffer) - 1] = 0;
+  buffer[sizeof(buffer) - 1] = '\0';
 
   if (db->state[db->depth].entry == NULL ||
       db->state[db->depth].entry->type != KEY) {
@@ -256,7 +255,6 @@ static int cj_cb_number(void *ctx, const char *number, yajl_len_t number_len) {
   value_t vt;
   int status = parse_value(buffer, &vt, type);
   if (status != 0) {
-    NOTICE("curl_json plugin: Unable to parse number: \"%s\"", buffer);
     cj_advance_array(ctx);
     return CJ_CB_CONTINUE;
   }
@@ -274,7 +272,7 @@ static int cj_cb_map_key(void *ctx, unsigned char const *in_name,
   char name[in_name_len + 1];
 
   memmove(name, in_name, in_name_len);
-  name[sizeof(name) - 1] = 0;
+  name[sizeof(name) - 1] = '\0';
 
   if (cj_load_key(ctx, name) != 0)
     return CJ_CB_ABORT;
@@ -325,7 +323,7 @@ static int cj_cb_start_array(void *ctx) {
     return CJ_CB_ABORT;
   }
   db->depth++;
-  db->state[db->depth].in_array = 1;
+  db->state[db->depth].in_array = true;
   db->state[db->depth].index = 0;
 
   cj_load_key(db, "0");
@@ -335,7 +333,7 @@ static int cj_cb_start_array(void *ctx) {
 
 static int cj_cb_end_array(void *ctx) {
   cj_t *db = (cj_t *)ctx;
-  db->state[db->depth].in_array = 0;
+  db->state[db->depth].in_array = false;
   return cj_cb_end(ctx);
 }
 
@@ -624,9 +622,6 @@ static int cj_init_curl(cj_t *db) /* {{{ */
 #ifdef HAVE_CURLOPT_TIMEOUT_MS
   if (db->timeout >= 0)
     curl_easy_setopt(db->curl, CURLOPT_TIMEOUT_MS, (long)db->timeout);
-  else if (db->interval > 0)
-    curl_easy_setopt(db->curl, CURLOPT_TIMEOUT_MS,
-                     (long)CDTIME_T_TO_MS(db->interval));
   else
     curl_easy_setopt(db->curl, CURLOPT_TIMEOUT_MS,
                      (long)CDTIME_T_TO_MS(plugin_get_interval()));
@@ -639,6 +634,7 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */
 {
   cj_t *db;
   int status = 0;
+  cdtime_t interval = 0;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
     WARNING("curl_json plugin: The `URL' block "
@@ -699,7 +695,7 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */
     else if (strcasecmp("Key", child->key) == 0)
       status = cj_config_add_key(db, child);
     else if (strcasecmp("Interval", child->key) == 0)
-      status = cf_util_get_cdtime(child, &db->interval);
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Timeout", child->key) == 0)
       status = cf_util_get_int(child, &db->timeout);
     else if (strcasecmp("Statistics", child->key) == 0) {
@@ -737,8 +733,7 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */
     cb_name = ssnprintf_alloc("curl_json-%s-%s", db->instance,
                               db->url ? db->url : db->sock);
 
-    plugin_register_complex_read(/* group = */ NULL, cb_name, cj_read,
-                                 /* interval = */ db->interval,
+    plugin_register_complex_read(/* group = */ NULL, cb_name, cj_read, interval,
                                  &(user_data_t){
                                      .data = db, .free_func = cj_free,
                                  });
@@ -816,15 +811,11 @@ static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
   sstrncpy(vl.plugin_instance, db->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, key->type, sizeof(vl.type));
 
-  if (db->interval > 0)
-    vl.interval = db->interval;
-
   plugin_dispatch_values(&vl);
 } /* }}} int cj_submit_impl */
 
 static int cj_sock_perform(cj_t *db) /* {{{ */
 {
-  char errbuf[1024];
   struct sockaddr_un sa_unix = {
       .sun_family = AF_UNIX,
   };
@@ -835,8 +826,7 @@ static int cj_sock_perform(cj_t *db) /* {{{ */
     return -1;
   if (connect(fd, (struct sockaddr *)&sa_unix, sizeof(sa_unix)) < 0) {
     ERROR("curl_json plugin: connect(%s) failed: %s",
-          (db->sock != NULL) ? db->sock : "<null>",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          (db->sock != NULL) ? db->sock : "<null>", STRERRNO);
     close(fd);
     return -1;
   }
@@ -847,8 +837,7 @@ static int cj_sock_perform(cj_t *db) /* {{{ */
     red = read(fd, buffer, sizeof(buffer));
     if (red < 0) {
       ERROR("curl_json plugin: read(%s) failed: %s",
-            (db->sock != NULL) ? db->sock : "<null>",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            (db->sock != NULL) ? db->sock : "<null>", STRERRNO);
       close(fd);
       return -1;
     }
index e8f8f6b..6c8640c 100644 (file)
@@ -139,7 +139,7 @@ DEF_TEST(parse) {
   return 0;
 }
 
-int main(int argc, char **argv) {
+int main(void) {
   cj_submit = test_submit;
 
   RUN_TEST(parse);
index c99e3f1..ed70f69 100644 (file)
@@ -21,9 +21,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_curl_stats.h"
+#include "utils/common/common.h"
+#include "utils/curl_stats/curl_stats.h"
 #include "utils_llist.h"
 
 #include <libxml/parser.h>
@@ -79,9 +79,9 @@ struct cx_s /* {{{ */
   char *user;
   char *pass;
   char *credentials;
-  _Bool digest;
-  _Bool verify_peer;
-  _Bool verify_host;
+  bool digest;
+  bool verify_peer;
+  bool verify_host;
   char *cacert;
   char *post_body;
   int timeout;
@@ -240,8 +240,8 @@ static int cx_check_type(const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */
   }
 
   if (ds->ds_num != xpath->values_len) {
-    WARNING("curl_xml plugin: DataSet `%s' requires %zu values, but config "
-            "talks about %zu",
+    WARNING("curl_xml plugin: DataSet `%s' requires %" PRIsz
+            " values, but config talks about %" PRIsz,
             xpath->type, ds->ds_num, xpath->values_len);
     return -1;
   }
@@ -823,6 +823,8 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
     return status;
   }
 
+  cdtime_t interval = 0;
+
   /* Fill the `cx_t' structure.. */
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
@@ -853,6 +855,8 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string(child, &db->post_body);
     else if (strcasecmp("Namespace", child->key) == 0)
       status = cx_config_add_namespace(db, child);
+    else if (strcasecmp("Interval", child->key) == 0)
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Timeout", child->key) == 0)
       status = cf_util_get_int(child, &db->timeout);
     else if (strcasecmp("Statistics", child->key) == 0) {
@@ -891,7 +895,7 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
   char *cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url);
 
   plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read,
-                               /* interval = */ 0,
+                               /* interval = */ interval,
                                &(user_data_t){
                                    .data = db, .free_func = cx_free,
                                });
diff --git a/src/daemon/cmd.c b/src/daemon/cmd.c
new file mode 100644 (file)
index 0000000..49f9272
--- /dev/null
@@ -0,0 +1,271 @@
+/**
+ * collectd - src/daemon/cmd.c
+ * Copyright (C) 2005-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.
+ **/
+
+#include "cmd.h"
+#include "collectd.h"
+
+#include "utils/common/common.h"
+#include <sys/un.h>
+
+static void *do_flush(void __attribute__((unused)) * arg) {
+  INFO("Flushing all data.");
+  plugin_flush(/* plugin = */ NULL,
+               /* timeout = */ 0,
+               /* ident = */ NULL);
+  INFO("Finished flushing all data.");
+  pthread_exit(NULL);
+  return NULL;
+}
+
+static void sig_int_handler(int __attribute__((unused)) signal) {
+  stop_collectd();
+}
+
+static void sig_term_handler(int __attribute__((unused)) signal) {
+  stop_collectd();
+}
+
+static void sig_usr1_handler(int __attribute__((unused)) signal) {
+  pthread_t thread;
+  pthread_attr_t attr;
+
+  /* flushing the data might take a while,
+   * so it should be done asynchronously */
+  pthread_attr_init(&attr);
+  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+  pthread_create(&thread, &attr, do_flush, NULL);
+  pthread_attr_destroy(&attr);
+}
+
+#if COLLECT_DAEMON
+static int pidfile_create(void) {
+  FILE *fh;
+  const char *file = global_option_get("PIDFile");
+
+  if ((fh = fopen(file, "w")) == NULL) {
+    ERROR("fopen (%s): %s", file, STRERRNO);
+    return 1;
+  }
+
+  fprintf(fh, "%i\n", (int)getpid());
+  fclose(fh);
+
+  return 0;
+} /* static int pidfile_create (const char *file) */
+
+static int pidfile_remove(void) {
+  const char *file = global_option_get("PIDFile");
+  if (file == NULL)
+    return 0;
+
+  return unlink(file);
+} /* static int pidfile_remove (const char *file) */
+#endif /* COLLECT_DAEMON */
+
+#ifdef KERNEL_LINUX
+static int notify_upstart(void) {
+  char const *upstart_job = getenv("UPSTART_JOB");
+
+  if (upstart_job == NULL)
+    return 0;
+
+  if (strcmp(upstart_job, "collectd") != 0) {
+    WARNING("Environment specifies unexpected UPSTART_JOB=\"%s\", expected "
+            "\"collectd\". Ignoring the variable.",
+            upstart_job);
+    return 0;
+  }
+
+  NOTICE("Upstart detected, stopping now to signal readiness.");
+  raise(SIGSTOP);
+  unsetenv("UPSTART_JOB");
+
+  return 1;
+}
+
+static int notify_systemd(void) {
+  size_t su_size;
+  const char *notifysocket = getenv("NOTIFY_SOCKET");
+  if (notifysocket == NULL)
+    return 0;
+
+  if ((strlen(notifysocket) < 2) ||
+      ((notifysocket[0] != '@') && (notifysocket[0] != '/'))) {
+    ERROR("invalid notification socket NOTIFY_SOCKET=\"%s\": path must be "
+          "absolute",
+          notifysocket);
+    return 0;
+  }
+  NOTICE("Systemd detected, trying to signal readiness.");
+
+  unsetenv("NOTIFY_SOCKET");
+
+  int fd;
+#if defined(SOCK_CLOEXEC)
+  fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, /* protocol = */ 0);
+#else
+  fd = socket(AF_UNIX, SOCK_DGRAM, /* protocol = */ 0);
+#endif
+  if (fd < 0) {
+    ERROR("creating UNIX socket failed: %s", STRERRNO);
+    return 0;
+  }
+
+  struct sockaddr_un su = {0};
+  su.sun_family = AF_UNIX;
+  if (notifysocket[0] != '@') {
+    /* regular UNIX socket */
+    sstrncpy(su.sun_path, notifysocket, sizeof(su.sun_path));
+    su_size = sizeof(su);
+  } else {
+    /* Linux abstract namespace socket: specify address as "\0foo", i.e.
+     * start with a null byte. Since null bytes have no special meaning in
+     * that case, we have to set su_size correctly to cover only the bytes
+     * that are part of the address. */
+    sstrncpy(su.sun_path, notifysocket, sizeof(su.sun_path));
+    su.sun_path[0] = 0;
+    su_size = sizeof(sa_family_t) + strlen(notifysocket);
+    if (su_size > sizeof(su))
+      su_size = sizeof(su);
+  }
+
+  const char buffer[] = "READY=1\n";
+  if (sendto(fd, buffer, strlen(buffer), MSG_NOSIGNAL, (void *)&su,
+             (socklen_t)su_size) < 0) {
+    ERROR("sendto(\"%s\") failed: %s", notifysocket, STRERRNO);
+    close(fd);
+    return 0;
+  }
+
+  unsetenv("NOTIFY_SOCKET");
+  close(fd);
+  return 1;
+}
+#endif /* KERNEL_LINUX */
+
+int main(int argc, char **argv) {
+  struct cmdline_config config = init_config(argc, argv);
+
+#if COLLECT_DAEMON
+  /*
+   * fork off child
+   */
+  struct sigaction sig_chld_action = {.sa_handler = SIG_IGN};
+
+  sigaction(SIGCHLD, &sig_chld_action, NULL);
+
+  /*
+   * Only daemonize if we're not being supervised
+   * by upstart or systemd (when using Linux).
+   */
+  if (config.daemonize
+#ifdef KERNEL_LINUX
+      && notify_upstart() == 0 && notify_systemd() == 0
+#endif
+      ) {
+    pid_t pid;
+    if ((pid = fork()) == -1) {
+      /* error */
+      fprintf(stderr, "fork: %s", STRERRNO);
+      return 1;
+    } else if (pid != 0) {
+      /* parent */
+      /* printf ("Running (PID %i)\n", pid); */
+      return 0;
+    }
+
+    /* Detach from session */
+    setsid();
+
+    /* Write pidfile */
+    if (pidfile_create())
+      exit(2);
+
+    /* close standard descriptors */
+    close(2);
+    close(1);
+    close(0);
+
+    int status = open("/dev/null", O_RDWR);
+    if (status != 0) {
+      ERROR("Error: Could not connect `STDIN' to `/dev/null' (status %d)",
+            status);
+      return 1;
+    }
+
+    status = dup(0);
+    if (status != 1) {
+      ERROR("Error: Could not connect `STDOUT' to `/dev/null' (status %d)",
+            status);
+      return 1;
+    }
+
+    status = dup(0);
+    if (status != 2) {
+      ERROR("Error: Could not connect `STDERR' to `/dev/null', (status %d)",
+            status);
+      return 1;
+    }
+  }    /* if (config.daemonize) */
+#endif /* COLLECT_DAEMON */
+
+  struct sigaction sig_pipe_action = {.sa_handler = SIG_IGN};
+
+  sigaction(SIGPIPE, &sig_pipe_action, NULL);
+
+  /*
+   * install signal handlers
+   */
+  struct sigaction sig_int_action = {.sa_handler = sig_int_handler};
+
+  if (sigaction(SIGINT, &sig_int_action, NULL) != 0) {
+    ERROR("Error: Failed to install a signal handler for signal INT: %s",
+          STRERRNO);
+    return 1;
+  }
+
+  struct sigaction sig_term_action = {.sa_handler = sig_term_handler};
+
+  if (sigaction(SIGTERM, &sig_term_action, NULL) != 0) {
+    ERROR("Error: Failed to install a signal handler for signal TERM: %s",
+          STRERRNO);
+    return 1;
+  }
+
+  struct sigaction sig_usr1_action = {.sa_handler = sig_usr1_handler};
+
+  if (sigaction(SIGUSR1, &sig_usr1_action, NULL) != 0) {
+    ERROR("Error: Failed to install a signal handler for signal USR1: %s",
+          STRERRNO);
+    return 1;
+  }
+
+  int exit_status = run_loop(config.test_readall);
+
+#if COLLECT_DAEMON
+  if (config.daemonize)
+    pidfile_remove();
+#endif /* COLLECT_DAEMON */
+
+  return exit_status;
+} /* int main */
diff --git a/src/daemon/cmd.h b/src/daemon/cmd.h
new file mode 100644 (file)
index 0000000..152ee63
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * collectd - src/daemon/cmd.h
+ * Copyright (C) 2018  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ **/
+
+#ifndef CMD_H
+#define CMD_H
+
+#include <stdbool.h>
+
+struct cmdline_config {
+  bool test_config;
+  bool test_readall;
+  bool create_basedir;
+  const char *configfile;
+  bool daemonize;
+};
+
+void stop_collectd(void);
+struct cmdline_config init_config(int argc, char **argv);
+int run_loop(bool test_readall);
+
+#endif /* CMD_H */
diff --git a/src/daemon/cmd_windows.c b/src/daemon/cmd_windows.c
new file mode 100755 (executable)
index 0000000..2542be5
--- /dev/null
@@ -0,0 +1,40 @@
+/**\r
+ * collectd - src/collectd_windows.c\r
+ * Copyright (C) 2017  Google LLC\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a\r
+ * copy of this software and associated documentation files (the "Software"),\r
+ * to deal in the Software without restriction, including without limitation\r
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+ * and/or sell copies of the Software, and to permit persons to whom the\r
+ * Software is furnished to do so, subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in\r
+ * all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\r
+ * DEALINGS IN THE SOFTWARE.\r
+ **/\r
+\r
+#include "cmd.h"\r
+#include "plugin.h"\r
+#include <stdio.h>\r
+#include <windows.h>\r
+\r
+int main(int argc, char **argv) {\r
+  WSADATA wsaData;\r
+  WORD wVersionRequested = MAKEWORD(2, 2);\r
+  int err = WSAStartup(wVersionRequested, &wsaData);\r
+  if (err != 0) {\r
+    ERROR("WSAStartup failed with error: %d\n", err);\r
+    return 1;\r
+  }\r
+\r
+  struct cmdline_config config = init_config(argc, argv);\r
+  return run_loop(config.test_readall);\r
+}\r
index 140e7cc..ae2d327 100644 (file)
  *   Alvaro Barcellos <alvaro.barcellos at gmail.com>
  **/
 
+#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>
-#include <sys/un.h>
 
 #if HAVE_LOCALE_H
 #include <locale.h>
 #define COLLECTD_LOCALE "C"
 #endif
 
-static int loop = 0;
-
-static void *do_flush(void __attribute__((unused)) * arg) {
-  INFO("Flushing all data.");
-  plugin_flush(/* plugin = */ NULL,
-               /* timeout = */ 0,
-               /* ident = */ NULL);
-  INFO("Finished flushing all data.");
-  pthread_exit(NULL);
-  return NULL;
-}
-
-static void sig_int_handler(int __attribute__((unused)) signal) { loop++; }
-
-static void sig_term_handler(int __attribute__((unused)) signal) { loop++; }
+#ifdef WIN32
+#undef COLLECT_DAEMON
+#include <unistd.h>
+#undef gethostname
+#include <locale.h>
+#include <winsock2.h>
+#endif
 
-static void sig_usr1_handler(int __attribute__((unused)) signal) {
-  pthread_t thread;
-  pthread_attr_t attr;
-
-  /* flushing the data might take a while,
-   * so it should be done asynchronously */
-  pthread_attr_init(&attr);
-  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-  pthread_create(&thread, &attr, do_flush, NULL);
-  pthread_attr_destroy(&attr);
-}
+static int loop;
 
 static int init_hostname(void) {
   const char *str = global_option_get("Hostname");
-  if ((str != NULL) && (str[0] != 0)) {
+  if (str && str[0] != '\0') {
     hostname_set(str);
     return 0;
   }
 
+#ifdef WIN32
+  long hostname_len = NI_MAXHOST;
+#else
   long hostname_len = sysconf(_SC_HOST_NAME_MAX);
   if (hostname_len == -1) {
     hostname_len = NI_MAXHOST;
   }
+#endif /* WIN32 */
   char hostname[hostname_len];
 
   if (gethostname(hostname, hostname_len) != 0) {
@@ -118,8 +104,7 @@ static int init_hostname(void) {
     return -1;
   }
 
-  for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
-       ai_ptr = ai_ptr->ai_next) {
+  for (struct addrinfo *ai_ptr = ai_list; ai_ptr; ai_ptr = ai_ptr->ai_next) {
     if (ai_ptr->ai_canonname == NULL)
       continue;
 
@@ -132,13 +117,11 @@ static int init_hostname(void) {
 } /* int init_hostname */
 
 static int init_global_variables(void) {
-  char const *str;
-
   interval_g = cf_get_default_interval();
   assert(interval_g > 0);
   DEBUG("interval_g = %.3f;", CDTIME_T_TO_DOUBLE(interval_g));
 
-  str = global_option_get("Timeout");
+  const char *str = global_option_get("Timeout");
   if (str == NULL)
     str = "2";
   timeout_g = atoi(str);
@@ -156,19 +139,14 @@ static int init_global_variables(void) {
   return 0;
 } /* int init_global_variables */
 
-static int change_basedir(const char *orig_dir, _Bool create) {
-  char *dir;
-  size_t dirlen;
-  int status;
-
-  dir = strdup(orig_dir);
+static int change_basedir(const char *orig_dir, bool create) {
+  char *dir = strdup(orig_dir);
   if (dir == NULL) {
-    char errbuf[1024];
-    ERROR("strdup failed: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("strdup failed: %s", STRERRNO);
     return -1;
   }
 
-  dirlen = strlen(dir);
+  size_t dirlen = strlen(dir);
   while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
     dir[--dirlen] = '\0';
 
@@ -177,32 +155,26 @@ static int change_basedir(const char *orig_dir, _Bool create) {
     return -1;
   }
 
-  status = chdir(dir);
+  int status = chdir(dir);
   if (status == 0) {
     free(dir);
     return 0;
   } else if (!create || (errno != ENOENT)) {
-    char errbuf[1024];
-    ERROR("change_basedir: chdir (%s): %s", dir,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("change_basedir: chdir (%s): %s", dir, STRERRNO);
     free(dir);
     return -1;
   }
 
   status = mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("change_basedir: mkdir (%s): %s", dir,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("change_basedir: mkdir (%s): %s", dir, STRERRNO);
     free(dir);
     return -1;
   }
 
   status = chdir(dir);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("change_basedir: chdir (%s): %s", dir,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("change_basedir: chdir (%s): %s", dir, STRERRNO);
     free(dir);
     return -1;
   }
@@ -218,8 +190,7 @@ static void update_kstat(void) {
     if ((kc = kstat_open()) == NULL)
       ERROR("Unable to open kstat control structure");
   } else {
-    kid_t kid;
-    kid = kstat_chain_update(kc);
+    kid_t kid = kstat_chain_update(kc);
     if (kid > 0) {
       INFO("kstat chain has been updated");
       plugin_init_all();
@@ -232,9 +203,6 @@ static void update_kstat(void) {
 } /* static void update_kstat (void) */
 #endif /* HAVE_LIBKSTAT */
 
-/* TODO
- * Remove all settings but `-f' and `-C'
- */
 __attribute__((noreturn)) static void exit_usage(int status) {
   printf("Usage: " PACKAGE_NAME " [OPTIONS]\n\n"
 
@@ -299,13 +267,9 @@ static int do_init(void) {
 
 static int do_loop(void) {
   cdtime_t interval = cf_get_default_interval();
-  cdtime_t wait_until;
-
-  wait_until = cdtime() + interval;
+  cdtime_t wait_until = cdtime() + interval;
 
   while (loop == 0) {
-    cdtime_t now;
-
 #if HAVE_LIBKSTAT
     update_kstat();
 #endif
@@ -313,7 +277,7 @@ static int do_loop(void) {
     /* Issue all plugins */
     plugin_read_all();
 
-    now = cdtime();
+    cdtime_t now = cdtime();
     if (now >= wait_until) {
       WARNING("Not sleeping because the next interval is "
               "%.3f seconds in the past!",
@@ -327,8 +291,7 @@ static int do_loop(void) {
 
     while ((loop == 0) && (nanosleep(&ts_wait, &ts_wait) != 0)) {
       if (errno != EINTR) {
-        char errbuf[1024];
-        ERROR("nanosleep failed: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("nanosleep failed: %s", STRERRNO);
         return -1;
       }
     }
@@ -341,155 +304,33 @@ static int do_shutdown(void) {
   return plugin_shutdown_all();
 } /* int do_shutdown */
 
-#if COLLECT_DAEMON
-static int pidfile_create(void) {
-  FILE *fh;
-  const char *file = global_option_get("PIDFile");
-
-  if ((fh = fopen(file, "w")) == NULL) {
-    char errbuf[1024];
-    ERROR("fopen (%s): %s", file, sstrerror(errno, errbuf, sizeof(errbuf)));
-    return 1;
-  }
-
-  fprintf(fh, "%i\n", (int)getpid());
-  fclose(fh);
-
-  return 0;
-} /* static int pidfile_create (const char *file) */
-
-static int pidfile_remove(void) {
-  const char *file = global_option_get("PIDFile");
-  if (file == NULL)
-    return 0;
-
-  return unlink(file);
-} /* static int pidfile_remove (const char *file) */
-#endif /* COLLECT_DAEMON */
-
-#ifdef KERNEL_LINUX
-static int notify_upstart(void) {
-  char const *upstart_job = getenv("UPSTART_JOB");
-
-  if (upstart_job == NULL)
-    return 0;
-
-  if (strcmp(upstart_job, "collectd") != 0) {
-    WARNING("Environment specifies unexpected UPSTART_JOB=\"%s\", expected "
-            "\"collectd\". Ignoring the variable.",
-            upstart_job);
-    return 0;
-  }
-
-  NOTICE("Upstart detected, stopping now to signal readyness.");
-  raise(SIGSTOP);
-  unsetenv("UPSTART_JOB");
-
-  return 1;
-}
-
-static int notify_systemd(void) {
-  int fd;
-  const char *notifysocket;
-  struct sockaddr_un su = {0};
-  size_t su_size;
-  char buffer[] = "READY=1\n";
-
-  notifysocket = getenv("NOTIFY_SOCKET");
-  if (notifysocket == NULL)
-    return 0;
-
-  if ((strlen(notifysocket) < 2) ||
-      ((notifysocket[0] != '@') && (notifysocket[0] != '/'))) {
-    ERROR("invalid notification socket NOTIFY_SOCKET=\"%s\": path must be "
-          "absolute",
-          notifysocket);
-    return 0;
-  }
-  NOTICE("Systemd detected, trying to signal readyness.");
-
-  unsetenv("NOTIFY_SOCKET");
-
-#if defined(SOCK_CLOEXEC)
-  fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, /* protocol = */ 0);
-#else
-  fd = socket(AF_UNIX, SOCK_DGRAM, /* protocol = */ 0);
-#endif
-  if (fd < 0) {
-    char errbuf[1024];
-    ERROR("creating UNIX socket failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    return 0;
-  }
-
-  su.sun_family = AF_UNIX;
-  if (notifysocket[0] != '@') {
-    /* regular UNIX socket */
-    sstrncpy(su.sun_path, notifysocket, sizeof(su.sun_path));
-    su_size = sizeof(su);
-  } else {
-    /* Linux abstract namespace socket: specify address as "\0foo", i.e.
-     * start with a null byte. Since null bytes have no special meaning in
-     * that case, we have to set su_size correctly to cover only the bytes
-     * that are part of the address. */
-    sstrncpy(su.sun_path, notifysocket, sizeof(su.sun_path));
-    su.sun_path[0] = 0;
-    su_size = sizeof(sa_family_t) + strlen(notifysocket);
-    if (su_size > sizeof(su))
-      su_size = sizeof(su);
-  }
-
-  if (sendto(fd, buffer, strlen(buffer), MSG_NOSIGNAL, (void *)&su,
-             (socklen_t)su_size) < 0) {
-    char errbuf[1024];
-    ERROR("sendto(\"%s\") failed: %s", notifysocket,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    close(fd);
-    return 0;
-  }
-
-  unsetenv("NOTIFY_SOCKET");
-  close(fd);
-  return 1;
-}
-#endif /* KERNEL_LINUX */
-
-struct cmdline_config {
-  _Bool test_config;
-  _Bool test_readall;
-  _Bool create_basedir;
-  const char *configfile;
-  _Bool daemonize;
-};
-
-void read_cmdline(int argc, char **argv, struct cmdline_config *config) {
+static void read_cmdline(int argc, char **argv, struct cmdline_config *config) {
   /* read options */
   while (1) {
-    int c;
-    c = getopt(argc, argv, "BhtTC:"
+    int c = getopt(argc, argv, "BhtTC:"
 #if COLLECT_DAEMON
-                           "fP:"
+                               "fP:"
 #endif
-               );
+                   );
 
     if (c == -1)
       break;
 
     switch (c) {
     case 'B':
-      config->create_basedir = 0;
+      config->create_basedir = false;
       break;
     case 'C':
       config->configfile = optarg;
       break;
     case 't':
-      config->test_config = 1;
+      config->test_config = true;
       break;
     case 'T':
-      config->test_readall = 1;
+      config->test_readall = true;
       global_option_set("ReadThreads", "-1", 1);
 #if COLLECT_DAEMON
-      config->daemonize = 0;
+      config->daemonize = false;
 #endif /* COLLECT_DAEMON */
       break;
 #if COLLECT_DAEMON
@@ -497,20 +338,18 @@ void read_cmdline(int argc, char **argv, struct cmdline_config *config) {
       global_option_set("PIDFile", optarg, 1);
       break;
     case 'f':
-      config->daemonize = 0;
+      config->daemonize = false;
       break;
 #endif /* COLLECT_DAEMON */
     case 'h':
       exit_usage(0);
-      break;
     default:
       exit_usage(1);
     } /* switch (c) */
   }   /* while (1) */
 }
 
-int configure_collectd(struct cmdline_config *config) {
-  const char *basedir;
+static int configure_collectd(struct cmdline_config *config) {
   /*
    * Read options from the config file, the environment and the command
    * line (in that order, with later options overwriting previous ones in
@@ -518,8 +357,7 @@ int configure_collectd(struct cmdline_config *config) {
    * Also, this will automatically load modules.
    */
   if (cf_read(config->configfile)) {
-    fprintf(stderr, "Error: Reading the config file failed!\n"
-                    "Read the logs for details.\n");
+    fprintf(stderr, "Error: Parsing the config file failed!\n");
     return 1;
   }
 
@@ -527,6 +365,7 @@ int configure_collectd(struct cmdline_config *config) {
    * Change directory. We do this _after_ reading the config and loading
    * modules to relative paths work as expected.
    */
+  const char *basedir;
   if ((basedir = global_option_get("BaseDir")) == NULL) {
     fprintf(stderr,
             "Don't have a basedir to use. This should not happen. Ever.");
@@ -548,138 +387,38 @@ int configure_collectd(struct cmdline_config *config) {
   return 0;
 }
 
-int main(int argc, char **argv) {
-#if COLLECT_DAEMON
-  pid_t pid;
-#endif
-  int exit_status = 0;
+void stop_collectd(void) { loop++; }
 
+struct cmdline_config init_config(int argc, char **argv) {
   struct cmdline_config config = {
-      .daemonize = 1, .create_basedir = 1, .configfile = CONFIGFILE,
+      .daemonize = true, .create_basedir = true, .configfile = CONFIGFILE,
   };
 
   read_cmdline(argc, argv, &config);
 
   if (config.test_config)
-    return 0;
+    exit(EXIT_SUCCESS);
 
   if (optind < argc)
     exit_usage(1);
 
   plugin_init_ctx();
 
-  int status;
-  if ((status = configure_collectd(&config)) != 0)
+  if (configure_collectd(&config) != 0)
     exit(EXIT_FAILURE);
 
-#if COLLECT_DAEMON
-  /*
-   * fork off child
-   */
-  struct sigaction sig_chld_action = {.sa_handler = SIG_IGN};
-
-  sigaction(SIGCHLD, &sig_chld_action, NULL);
-
-  /*
-   * Only daemonize if we're not being supervised
-   * by upstart or systemd (when using Linux).
-   */
-  if (config.daemonize
-#ifdef KERNEL_LINUX
-      && notify_upstart() == 0 && notify_systemd() == 0
-#endif
-      ) {
-    int status;
-
-    if ((pid = fork()) == -1) {
-      /* error */
-      char errbuf[1024];
-      fprintf(stderr, "fork: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
-      return 1;
-    } else if (pid != 0) {
-      /* parent */
-      /* printf ("Running (PID %i)\n", pid); */
-      return 0;
-    }
-
-    /* Detach from session */
-    setsid();
-
-    /* Write pidfile */
-    if (pidfile_create())
-      exit(2);
-
-    /* close standard descriptors */
-    close(2);
-    close(1);
-    close(0);
-
-    status = open("/dev/null", O_RDWR);
-    if (status != 0) {
-      ERROR("Error: Could not connect `STDIN' to `/dev/null' (status %d)",
-            status);
-      return 1;
-    }
-
-    status = dup(0);
-    if (status != 1) {
-      ERROR("Error: Could not connect `STDOUT' to `/dev/null' (status %d)",
-            status);
-      return 1;
-    }
-
-    status = dup(0);
-    if (status != 2) {
-      ERROR("Error: Could not connect `STDERR' to `/dev/null', (status %d)",
-            status);
-      return 1;
-    }
-  }    /* if (config.daemonize) */
-#endif /* COLLECT_DAEMON */
-
-  struct sigaction sig_pipe_action = {.sa_handler = SIG_IGN};
-
-  sigaction(SIGPIPE, &sig_pipe_action, NULL);
-
-  /*
-   * install signal handlers
-   */
-  struct sigaction sig_int_action = {.sa_handler = sig_int_handler};
-
-  if (0 != sigaction(SIGINT, &sig_int_action, NULL)) {
-    char errbuf[1024];
-    ERROR("Error: Failed to install a signal handler for signal INT: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    return 1;
-  }
-
-  struct sigaction sig_term_action = {.sa_handler = sig_term_handler};
-
-  if (0 != sigaction(SIGTERM, &sig_term_action, NULL)) {
-    char errbuf[1024];
-    ERROR("Error: Failed to install a signal handler for signal TERM: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    return 1;
-  }
-
-  struct sigaction sig_usr1_action = {.sa_handler = sig_usr1_handler};
+  return config;
+}
 
-  if (0 != sigaction(SIGUSR1, &sig_usr1_action, NULL)) {
-    char errbuf[1024];
-    ERROR("Error: Failed to install a signal handler for signal USR1: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    return 1;
-  }
+int run_loop(bool test_readall) {
+  int exit_status = 0;
 
-  /*
-   * run the actual loops
-   */
   if (do_init() != 0) {
     ERROR("Error: one or more plugin init callbacks failed.");
     exit_status = 1;
   }
 
-  if (config.test_readall) {
+  if (test_readall) {
     if (plugin_read_all_once() != 0) {
       ERROR("Error: one or more plugin read callbacks failed.");
       exit_status = 1;
@@ -697,10 +436,5 @@ int main(int argc, char **argv) {
     exit_status = 1;
   }
 
-#if COLLECT_DAEMON
-  if (config.daemonize)
-    pidfile_remove();
-#endif /* COLLECT_DAEMON */
-
   return exit_status;
-} /* int main */
+} /* int run_loop */
index 0558aa4..07597d3 100644 (file)
 #ifndef COLLECTD_H
 #define COLLECTD_H
 
+#ifdef WIN32
+typedef int uid_t;
+#include "gnulib_config.h"
+#endif
+
 #if HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -38,6 +43,7 @@
 #include <limits.h>
 #include <signal.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <sys/isa_defs.h>
 #endif
 
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
 #ifndef BYTE_ORDER
 #if defined(_BYTE_ORDER)
 #define BYTE_ORDER _BYTE_ORDER
 #endif
 #endif
 
-#if HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
 #ifndef PACKAGE_NAME
 #define PACKAGE_NAME "collectd"
 #endif
 #define __attribute__(x) /**/
 #endif
 
-#if defined(COLLECT_DEBUG) && COLLECT_DEBUG && defined(__GNUC__) && __GNUC__
-#undef strcpy
-#undef strcat
-#undef strtok
-#pragma GCC poison strcpy strcat strtok
-#endif
-
-/*
- * Special hack for the perl plugin: Because the later included perl.h defines
- * a macro which is never used, but contains `sprintf', we cannot poison that
- * identifies just yet. The parl plugin will do that itself once perl.h is
- * included.
- */
-#ifndef DONT_POISON_SPRINTF_YET
-#if defined(COLLECT_DEBUG) && COLLECT_DEBUG && defined(__GNUC__) && __GNUC__
-#undef sprintf
-#pragma GCC poison sprintf
-#endif
-#endif
-
 #ifndef GAUGE_FORMAT
 #define GAUGE_FORMAT "%.15g"
 #endif
diff --git a/src/daemon/common.c b/src/daemon/common.c
deleted file mode 100644 (file)
index eb9f590..0000000
+++ /dev/null
@@ -1,1588 +0,0 @@
-/**
- * collectd - src/common.c
- * Copyright (C) 2005-2014  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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>
-**/
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#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
-
-/* AIX doesn't have MSG_DONTWAIT */
-#ifndef MSG_DONTWAIT
-#define MSG_DONTWAIT MSG_NONBLOCK
-#endif
-
-#if !HAVE_GETPWNAM_R
-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)) {
-      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[512];
-  char dir[512];
-  int dir_len = 512;
-  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))
-    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] == '.') {
-      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)(dir_len - path_is_absolute),
-                fields, (size_t)(i + 1), "/") < 0) {
-      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;
-
-          char errbuf[1024];
-          ERROR("check_create_dir: mkdir (%s): %s", dir,
-                sstrerror(errno, errbuf, sizeof(errbuf)));
-          return -1;
-        } else {
-          char errbuf[1024];
-          ERROR("check_create_dir: stat (%s): %s", dir,
-                sstrerror(errno, errbuf, sizeof(errbuf)));
-          return -1;
-        }
-      } else if (!S_ISDIR(statbuf.st_mode)) {
-        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) {
-    ERROR("get_kstat: Cound not find kstat %s", ident);
-    return -1;
-  }
-
-  if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
-    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) {
-    ERROR("get_kstat: kstat %s could not be read", ident);
-    return -1;
-  }
-
-  if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
-    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) {
-    ERROR("get_kstat_value (\"%s\"): ksp is NULL.", name);
-    return -1LL;
-  } else if (ksp->ks_type != KSTAT_TYPE_NAMED) {
-    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
-    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(":%llu", 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);
-    ERROR("parse_value: Invalid data source type: %i.", ds_type);
-    return -1;
-  }
-
-  if (value == endptr) {
-    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))
-    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) {
-  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;
-} /* 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) {
-    char errbuf[1024];
-    ERROR("walk_directory: Cannot open '%s': %s", dir,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    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)) {
-    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) {
-    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) {
-    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)
-      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)
-      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)
-      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())) {
-    ERROR("check_capability: cap_get_proc failed.");
-    return -1;
-  }
-
-  if (cap_get_flag(cap, cap_value, CAP_EFFECTIVE, &cap_flag_value) < 0) {
-    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) /* {{{ */
-{
-  WARNING("check_capability: unsupported capability implementation. "
-          "Some plugin(s) may require elevated privileges to work properly.");
-  return 0;
-} /* }}} int check_capability */
-#endif /* HAVE_CAPABILITY */
diff --git a/src/daemon/common.h b/src/daemon/common.h
deleted file mode 100644 (file)
index aae1909..0000000
+++ /dev/null
@@ -1,390 +0,0 @@
-/**
- * collectd - src/common.h
- * Copyright (C) 2005-2014  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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);
-
-/*
- * 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
-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 absoltue_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);
-
-#ifdef HAVE_SYS_CAPABILITY_H
-/** 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 /* HAVE_SYS_CAPABILITY_H */
-
-#endif /* COMMON_H */
diff --git a/src/daemon/common_test.c b/src/daemon/common_test.c
deleted file mode 100644 (file)
index 93a19d1..0000000
+++ /dev/null
@@ -1,378 +0,0 @@
-/**
- * collectd - src/tests/test_common.c
- * Copyright (C) 2013       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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(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;
-}
index f5086ae..735be83 100644 (file)
 
 #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>
@@ -76,7 +76,7 @@ typedef struct cf_value_map_s {
 typedef struct cf_global_option_s {
   const char *key;
   char *value;
-  _Bool from_cli; /* value set from CLI */
+  bool from_cli; /* value set from CLI */
   const char *def;
 } cf_global_option_t;
 
@@ -91,8 +91,8 @@ static int dispatch_block_plugin(oconfig_item_t *ci);
 /*
  * Private variables
  */
-static cf_callback_t *first_callback = NULL;
-static cf_complex_callback_t *complex_callback_head = NULL;
+static cf_callback_t *first_callback;
+static cf_complex_callback_t *complex_callback_head;
 
 static cf_value_map_t cf_value_map[] = {{"TypesDB", dispatch_value_typesdb},
                                         {"PluginDir", dispatch_value_plugindir},
@@ -190,8 +190,12 @@ static int cf_dispatch(const char *type, const char *orig_key,
 } /* int cf_dispatch */
 
 static int dispatch_global_option(const oconfig_item_t *ci) {
-  if (ci->values_num != 1)
+  if (ci->values_num != 1) {
+    ERROR("configfile: Global option `%s' needs exactly one argument.",
+          ci->key);
     return -1;
+  }
+
   if (ci->values[0].type == OCONFIG_TYPE_STRING)
     return global_option_set(ci->key, ci->values[0].value.string, 0);
   else if (ci->values[0].type == OCONFIG_TYPE_NUMBER) {
@@ -205,6 +209,8 @@ static int dispatch_global_option(const oconfig_item_t *ci) {
       return global_option_set(ci->key, "false", 0);
   }
 
+  ERROR("configfile: Global option `%s' argument has unknown type.", ci->key);
+
   return -1;
 } /* int dispatch_global_option */
 
@@ -234,37 +240,37 @@ static int dispatch_value_typesdb(oconfig_item_t *ci) {
 static int dispatch_value_plugindir(oconfig_item_t *ci) {
   assert(strcasecmp(ci->key, "PluginDir") == 0);
 
-  if (ci->values_num != 1)
-    return -1;
-  if (ci->values[0].type != OCONFIG_TYPE_STRING)
+  if (ci->values_num != 1 || ci->values[0].type != OCONFIG_TYPE_STRING) {
+    ERROR("configfile: The `PluginDir' option needs exactly one string "
+          "argument.");
     return -1;
+  }
 
   plugin_set_dir(ci->values[0].value.string);
   return 0;
 }
 
 static int dispatch_loadplugin(oconfig_item_t *ci) {
-  const char *name;
-  _Bool global = 0;
-  plugin_ctx_t ctx = {0};
-  plugin_ctx_t old_ctx;
-  int ret_val;
+  bool global = false;
 
   assert(strcasecmp(ci->key, "LoadPlugin") == 0);
 
-  if (ci->values_num != 1)
-    return -1;
-  if (ci->values[0].type != OCONFIG_TYPE_STRING)
+  if (ci->values_num != 1 || ci->values[0].type != OCONFIG_TYPE_STRING) {
+    ERROR("configfile: The `LoadPlugin' block needs exactly one string "
+          "argument.");
     return -1;
+  }
 
-  name = ci->values[0].value.string;
+  const char *name = ci->values[0].value.string;
   if (strcmp("libvirt", name) == 0)
     name = "virt";
 
   /* default to the global interval set before loading this plugin */
-  ctx.interval = cf_get_default_interval();
-  ctx.flush_interval = 0;
-  ctx.flush_timeout = 0;
+  plugin_ctx_t ctx = {
+      .interval = cf_get_default_interval(), .name = strdup(name),
+  };
+  if (ctx.name == NULL)
+    return ENOMEM;
 
   for (int i = 0; i < ci->children_num; ++i) {
     oconfig_item_t *child = ci->children + i;
@@ -280,12 +286,12 @@ static int dispatch_loadplugin(oconfig_item_t *ci) {
     else {
       WARNING("Ignoring unknown LoadPlugin option \"%s\" "
               "for plugin \"%s\"",
-              child->key, ci->values[0].value.string);
+              child->key, name);
     }
   }
 
-  old_ctx = plugin_set_ctx(ctx);
-  ret_val = plugin_load(name, global);
+  plugin_ctx_t old_ctx = plugin_set_ctx(ctx);
+  int ret_val = plugin_load(name, global);
   /* reset to the "global" context */
   plugin_set_ctx(old_ctx);
 
@@ -333,6 +339,9 @@ static int dispatch_value(oconfig_item_t *ci) {
       break;
     }
 
+  if (ret != 0)
+    return ret;
+
   for (int i = 0; i < cf_global_options_num; i++)
     if (strcasecmp(cf_global_options[i].key, ci->key) == 0) {
       ret = dispatch_global_option(ci);
@@ -343,16 +352,18 @@ static int dispatch_value(oconfig_item_t *ci) {
 } /* int dispatch_value */
 
 static int dispatch_block_plugin(oconfig_item_t *ci) {
-  const char *name;
+  assert(strcasecmp(ci->key, "Plugin") == 0);
 
-  if (strcasecmp(ci->key, "Plugin") != 0)
-    return -1;
-  if (ci->values_num < 1)
+  if (ci->values_num < 1) {
+    ERROR("configfile: The `Plugin' block requires arguments.");
     return -1;
-  if (ci->values[0].type != OCONFIG_TYPE_STRING)
+  }
+  if (ci->values[0].type != OCONFIG_TYPE_STRING) {
+    ERROR("configfile: First argument of `Plugin' block should be a string.");
     return -1;
+  }
 
-  name = ci->values[0].value.string;
+  const char *name = ci->values[0].value.string;
   if (strcmp("libvirt", name) == 0) {
     /* TODO(octo): Remove this legacy. */
     WARNING("The \"libvirt\" plugin has been renamed to \"virt\" to avoid "
@@ -370,9 +381,10 @@ static int dispatch_block_plugin(oconfig_item_t *ci) {
 
     /* default to the global interval set before loading this plugin */
     ctx.interval = cf_get_default_interval();
+    ctx.name = strdup(name);
 
     old_ctx = plugin_set_ctx(ctx);
-    status = plugin_load(name, /* flags = */ 0);
+    status = plugin_load(name, /* flags = */ false);
     /* reset to the "global" context */
     plugin_set_ctx(old_ctx);
 
@@ -399,10 +411,12 @@ static int dispatch_block_plugin(oconfig_item_t *ci) {
   }
 
   /* 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 "
@@ -634,9 +648,7 @@ static oconfig_item_t *cf_read_dir(const char *dir, const char *pattern,
 
   dh = opendir(dir);
   if (dh == NULL) {
-    char errbuf[1024];
-    ERROR("configfile: opendir failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("configfile: opendir failed: %s", STRERRNO);
     return NULL;
   }
 
@@ -765,9 +777,7 @@ static oconfig_item_t *cf_read_generic(const char *path, const char *pattern,
 
     status = stat(path_ptr, &statbuf);
     if (status != 0) {
-      char errbuf[1024];
-      WARNING("configfile: stat (%s) failed: %s", path_ptr,
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("configfile: stat (%s) failed: %s", path_ptr, STRERRNO);
       continue;
     }
 
@@ -813,9 +823,7 @@ static oconfig_item_t *cf_read_generic(const char *path, const char *pattern,
 
   status = stat(path, &statbuf);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("configfile: stat (%s) failed: %s", path,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("configfile: stat (%s) failed: %s", path, STRERRNO);
     return NULL;
   }
 
@@ -832,7 +840,7 @@ static oconfig_item_t *cf_read_generic(const char *path, const char *pattern,
 /*
  * Public functions
  */
-int global_option_set(const char *option, const char *value, _Bool from_cli) {
+int global_option_set(const char *option, const char *value, bool from_cli) {
   int i;
   DEBUG("option = %s; value = %s;", option, value);
 
@@ -1041,16 +1049,12 @@ int cf_read(const char *filename) {
  * success. */
 int cf_util_get_string(const oconfig_item_t *ci, char **ret_string) /* {{{ */
 {
-  char *string;
-
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    ERROR("cf_util_get_string: The %s option requires "
-          "exactly one string argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one string argument.", ci->key);
     return -1;
   }
 
-  string = strdup(ci->values[0].value.string);
+  char *string = strdup(ci->values[0].value.string);
   if (string == NULL)
     return -1;
 
@@ -1062,21 +1066,19 @@ int cf_util_get_string(const oconfig_item_t *ci, char **ret_string) /* {{{ */
 } /* }}} int cf_util_get_string */
 
 /* Assures the config option is a string and copies it to the provided buffer.
- * Assures null-termination. */
+ * Assures NUL-termination. */
 int cf_util_get_string_buffer(const oconfig_item_t *ci, char *buffer, /* {{{ */
                               size_t buffer_size) {
   if ((ci == NULL) || (buffer == NULL) || (buffer_size < 1))
     return EINVAL;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    ERROR("cf_util_get_string_buffer: The %s option requires "
-          "exactly one string argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one string argument.", ci->key);
     return -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 */
@@ -1088,9 +1090,7 @@ int cf_util_get_int(const oconfig_item_t *ci, int *ret_value) /* {{{ */
     return EINVAL;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    ERROR("cf_util_get_int: The %s option requires "
-          "exactly one numeric argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one numeric argument.", ci->key);
     return -1;
   }
 
@@ -1105,9 +1105,7 @@ int cf_util_get_double(const oconfig_item_t *ci, double *ret_value) /* {{{ */
     return EINVAL;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    ERROR("cf_util_get_double: The %s option requires "
-          "exactly one numeric argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one numeric argument.", ci->key);
     return -1;
   }
 
@@ -1116,37 +1114,35 @@ int cf_util_get_double(const oconfig_item_t *ci, double *ret_value) /* {{{ */
   return 0;
 } /* }}} int cf_util_get_double */
 
-int cf_util_get_boolean(const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
+int cf_util_get_boolean(const oconfig_item_t *ci, bool *ret_bool) /* {{{ */
 {
   if ((ci == NULL) || (ret_bool == NULL))
     return EINVAL;
 
   if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_BOOLEAN) &&
                                 (ci->values[0].type != OCONFIG_TYPE_STRING))) {
-    ERROR("cf_util_get_boolean: The %s option requires "
-          "exactly one boolean argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one boolean argument.", ci->key);
     return -1;
   }
 
   switch (ci->values[0].type) {
   case OCONFIG_TYPE_BOOLEAN:
-    *ret_bool = ci->values[0].value.boolean ? 1 : 0;
+    *ret_bool = ci->values[0].value.boolean ? true : false;
     break;
   case OCONFIG_TYPE_STRING:
-    WARNING("cf_util_get_boolean: Using string value `%s' for boolean option "
-            "`%s' is deprecated and will be removed in future releases. "
-            "Use unquoted true or false instead.",
-            ci->values[0].value.string, ci->key);
+    P_WARNING("Using string value `%s' for boolean option `%s' is deprecated "
+              "and will be removed in future releases. Use unquoted true or "
+              "false instead.",
+              ci->values[0].value.string, ci->key);
 
     if (IS_TRUE(ci->values[0].value.string))
-      *ret_bool = 1;
+      *ret_bool = true;
     else if (IS_FALSE(ci->values[0].value.string))
-      *ret_bool = 0;
+      *ret_bool = false;
     else {
-      ERROR("cf_util_get_boolean: Cannot parse string value `%s' of the `%s' "
-            "option as a boolean value.",
-            ci->values[0].value.string, ci->key);
+      P_ERROR("Cannot parse string value `%s' of the `%s' option as a boolean "
+              "value.",
+              ci->values[0].value.string, ci->key);
       return -1;
     }
     break;
@@ -1158,12 +1154,11 @@ int cf_util_get_boolean(const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
 int cf_util_get_flag(const oconfig_item_t *ci, /* {{{ */
                      unsigned int *ret_value, unsigned int flag) {
   int status;
-  _Bool b;
 
   if (ret_value == NULL)
     return EINVAL;
 
-  b = 0;
+  bool b = false;
   status = cf_util_get_boolean(ci, &b);
   if (status != 0)
     return status;
@@ -1188,9 +1183,7 @@ int cf_util_get_port_number(const oconfig_item_t *ci) /* {{{ */
 
   if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_STRING) &&
                                 (ci->values[0].type != OCONFIG_TYPE_NUMBER))) {
-    ERROR("cf_util_get_port_number: The \"%s\" option requires "
-          "exactly one string argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one string argument.", ci->key);
     return -1;
   }
 
@@ -1200,11 +1193,9 @@ int cf_util_get_port_number(const oconfig_item_t *ci) /* {{{ */
   assert(ci->values[0].type == OCONFIG_TYPE_NUMBER);
   tmp = (int)(ci->values[0].value.number + 0.5);
   if ((tmp < 1) || (tmp > 65535)) {
-    ERROR("cf_util_get_port_number: The \"%s\" option requires "
-          "a service name or a port number. The number "
-          "you specified, %i, is not in the valid "
-          "range of 1-65535.",
-          ci->key, tmp);
+    P_ERROR("The `%s' option requires a service name or a port number. The "
+            "number you specified, %i, is not in the valid range of 1-65535.",
+            ci->key, tmp);
     return -1;
   }
 
@@ -1218,18 +1209,15 @@ int cf_util_get_service(const oconfig_item_t *ci, char **ret_string) /* {{{ */
   int status;
 
   if (ci->values_num != 1) {
-    ERROR("cf_util_get_service: The %s option requires exactly "
-          "one argument.",
-          ci->key);
+    P_ERROR("The `%s` option requires exactly one argument.", ci->key);
     return -1;
   }
 
   if (ci->values[0].type == OCONFIG_TYPE_STRING)
     return cf_util_get_string(ci, ret_string);
   if (ci->values[0].type != OCONFIG_TYPE_NUMBER) {
-    ERROR("cf_util_get_service: The %s option requires "
-          "exactly one string or numeric argument.",
-          ci->key);
+    P_ERROR("The `%s` option requires exactly one string or numeric argument.",
+            ci->key);
   }
 
   port = 0;
@@ -1237,16 +1225,14 @@ int cf_util_get_service(const oconfig_item_t *ci, char **ret_string) /* {{{ */
   if (status != 0)
     return status;
   else if ((port < 1) || (port > 65535)) {
-    ERROR("cf_util_get_service: The port number given "
-          "for the %s option is out of "
-          "range (%i).",
-          ci->key, port);
+    P_ERROR("The port number given for the `%s` option is out of range (%i).",
+            ci->key, port);
     return -1;
   }
 
   service = malloc(6);
   if (service == NULL) {
-    ERROR("cf_util_get_service: Out of memory.");
+    P_ERROR("cf_util_get_service: Out of memory.");
     return -1;
   }
   snprintf(service, 6, "%i", port);
@@ -1263,16 +1249,13 @@ int cf_util_get_cdtime(const oconfig_item_t *ci, cdtime_t *ret_value) /* {{{ */
     return EINVAL;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    ERROR("cf_util_get_cdtime: The %s option requires "
-          "exactly one numeric argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one numeric argument.", ci->key);
     return -1;
   }
 
   if (ci->values[0].value.number < 0.0) {
-    ERROR("cf_util_get_cdtime: The numeric argument of the %s "
-          "option must not be negative.",
-          ci->key);
+    P_ERROR("The numeric argument of the `%s' option must not be negative.",
+            ci->key);
     return -1;
   }
 
index 7cebb97..108609c 100644 (file)
@@ -89,7 +89,7 @@ int cf_register_complex(const char *type, int (*callback)(oconfig_item_t *));
  */
 int cf_read(const char *filename);
 
-int global_option_set(const char *option, const char *value, _Bool from_cli);
+int global_option_set(const char *option, const char *value, bool from_cli);
 const char *global_option_get(const char *option);
 long global_option_get_long(const char *option, long default_value);
 
@@ -115,7 +115,7 @@ int cf_util_get_double(const oconfig_item_t *ci, double *ret_value);
 
 /* Assures the config option is a boolean and assignes it to `ret_bool'.
  * Otherwise, `ret_bool' is not changed and non-zero is returned. */
-int cf_util_get_boolean(const oconfig_item_t *ci, _Bool *ret_bool);
+int cf_util_get_boolean(const oconfig_item_t *ci, bool *ret_bool);
 
 /* Assures the config option is a boolean and set or unset the given flag in
  * `ret_value' as appropriate. Returns non-zero on error. */
index 1352f5b..d5e14a3 100644 (file)
 
 #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"
 
 /*
@@ -693,7 +693,7 @@ static int fc_bit_write_invoke(const data_set_t *ds, /* {{{ */
 
 static int fc_init_once(void) /* {{{ */
 {
-  static int done = 0;
+  static int done;
   target_proc_t tproc = {0};
 
   if (done != 0)
index 5c6749f..a76a44c 100644 (file)
@@ -21,8 +21,8 @@
  * DEALINGS IN THE SOFTWARE.
  **/
 
-#include "common.h"
 #include "globals.h"
+#include "utils/common/common.h"
 
 #if HAVE_KSTAT_H
 #include <kstat.h>
index bc11d6b..9ec72f0 100644 (file)
 #define DATA_MAX_NAME_LEN 128
 #endif
 
+#ifndef PRIsz
+#ifdef WIN32
+#define PRIsz "Iu"
+#else
+#define PRIsz "zu"
+#endif /* WIN32 */
+#endif /* !PRIsz */
+
 /* Type for time as used by "utils_time.h" */
 typedef uint64_t cdtime_t;
 
diff --git a/src/daemon/meta_data.c b/src/daemon/meta_data.c
deleted file mode 100644 (file)
index 4d59b71..0000000
+++ /dev/null
@@ -1,747 +0,0 @@
-/**
- * collectd - src/meta_data.c
- * Copyright (C) 2008-2011  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/daemon/meta_data.h b/src/daemon/meta_data.h
deleted file mode 100644 (file)
index 50fdb8d..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * collectd - src/meta_data.h
- * Copyright (C) 2008-2011  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/daemon/meta_data_test.c b/src/daemon/meta_data_test.c
deleted file mode 100644 (file)
index bcd457d..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * collectd - src/daemon/meta_data_test.c
- * Copyright (C) 2015       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- */
-
-#include "common.h" /* for STATIC_ARRAY_SIZE */
-#include "collectd.h"
-
-#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;
-}
index 9b75f69..b4e5ae7 100644 (file)
 
 #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"
 
+#ifdef WIN32
+#define EXPORT __declspec(dllexport)
+#include <sys/stat.h>
+#include <unistd.h>
+#else
+#define EXPORT
+#endif
+
 #if HAVE_PTHREAD_NP_H
 #include <pthread_np.h> /* for pthread_set_name_np(3) */
 #endif
@@ -94,7 +102,7 @@ typedef struct flush_callback_s flush_callback_t;
 /*
  * Private variables
  */
-static c_avl_tree_t *plugins_loaded = NULL;
+static c_avl_tree_t *plugins_loaded;
 
 static llist_t *list_init;
 static llist_t *list_write;
@@ -104,42 +112,43 @@ static llist_t *list_shutdown;
 static llist_t *list_log;
 static llist_t *list_notification;
 
-static fc_chain_t *pre_cache_chain = NULL;
-static fc_chain_t *post_cache_chain = NULL;
+static fc_chain_t *pre_cache_chain;
+static fc_chain_t *post_cache_chain;
 
 static c_avl_tree_t *data_sets;
 
-static char *plugindir = NULL;
+static char *plugindir;
 
 #ifndef DEFAULT_MAX_READ_INTERVAL
 #define DEFAULT_MAX_READ_INTERVAL TIME_T_TO_CDTIME_T_STATIC(86400)
 #endif
-static c_heap_t *read_heap = NULL;
+static c_heap_t *read_heap;
 static llist_t *read_list;
 static int read_loop = 1;
 static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t read_cond = PTHREAD_COND_INITIALIZER;
-static pthread_t *read_threads = NULL;
-static size_t read_threads_num = 0;
+static pthread_t *read_threads;
+static size_t read_threads_num;
 static cdtime_t max_read_interval = DEFAULT_MAX_READ_INTERVAL;
 
 static write_queue_t *write_queue_head;
 static write_queue_t *write_queue_tail;
-static long write_queue_length = 0;
-static _Bool write_loop = 1;
+static long write_queue_length;
+static bool write_loop = true;
 static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t write_cond = PTHREAD_COND_INITIALIZER;
-static pthread_t *write_threads = NULL;
-static size_t write_threads_num = 0;
+static pthread_t *write_threads;
+static size_t write_threads_num;
 
 static pthread_key_t plugin_ctx_key;
-static _Bool plugin_ctx_key_initialized = 0;
+static bool plugin_ctx_key_initialized;
 
-static long write_limit_high = 0;
-static long write_limit_low = 0;
+static long write_limit_high;
+static long write_limit_low;
 
-static derive_t stats_values_dropped = 0;
-static _Bool record_statistics = 0;
+static pthread_mutex_t statistics_lock = PTHREAD_MUTEX_INITIALIZER;
+static derive_t stats_values_dropped;
+static bool record_statistics;
 
 /*
  * Static functions
@@ -292,10 +301,10 @@ static int register_callback(llist_t **list, /* {{{ */
     old_cf = le->value;
     le->value = cf;
 
-    WARNING("plugin: register_callback: "
-            "a callback named `%s' already exists - "
-            "overwriting the old entry!",
-            name);
+    P_WARNING("register_callback: "
+              "a callback named `%s' already exists - "
+              "overwriting the old entry!",
+              name);
 
     destroy_callback(old_cf);
     sfree(key);
@@ -311,19 +320,16 @@ static void log_list_callbacks(llist_t **list, /* {{{ */
   int i;
   llentry_t *le;
   int n;
-  char **keys;
 
   n = llist_size(*list);
   if (n == 0) {
-    INFO("%s [none]", comment);
+    INFO("%s: [none]", comment);
     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;
   }
 
@@ -346,19 +352,22 @@ static void log_list_callbacks(llist_t **list, /* {{{ */
 static int create_register_callback(llist_t **list, /* {{{ */
                                     const char *name, void *callback,
                                     user_data_t const *ud) {
-  callback_func_t *cf;
 
-  cf = calloc(1, sizeof(*cf));
+  if (name == NULL || callback == NULL)
+    return EINVAL;
+
+  callback_func_t *cf = calloc(1, sizeof(*cf));
   if (cf == NULL) {
     free_userdata(ud);
     ERROR("plugin: create_register_callback: calloc failed.");
-    return -1;
+    return ENOMEM;
   }
 
   cf->cf_callback = callback;
   if (ud == NULL) {
-    cf->cf_udata.data = NULL;
-    cf->cf_udata.free_func = NULL;
+    cf->cf_udata = (user_data_t){
+        .data = NULL, .free_func = NULL,
+    };
   } else {
     cf->cf_udata = *ud;
   }
@@ -389,50 +398,44 @@ static int plugin_unregister(llist_t *list, const char *name) /* {{{ */
   return 0;
 } /* }}} int plugin_unregister */
 
-/*
- * (Try to) load the shared object `file'. Won't complain if it isn't a shared
- * object, but it will bitch about a shared object not having a
- * ``module_register'' symbol..
- */
-static int plugin_load_file(const char *file, _Bool global) {
-  void (*reg_handle)(void);
-
+/* plugin_load_file loads the shared object "file" and calls its
+ * "module_register" function. Returns zero on success, non-zero otherwise. */
+static int plugin_load_file(char const *file, bool global) {
   int flags = RTLD_NOW;
   if (global)
     flags |= RTLD_GLOBAL;
 
   void *dlh = dlopen(file, flags);
-
   if (dlh == NULL) {
     char errbuf[1024] = "";
 
     snprintf(errbuf, sizeof(errbuf),
-             "dlopen (\"%s\") failed: %s. "
-             "The most common cause for this problem is "
-             "missing dependencies. Use ldd(1) to check "
-             "the dependencies of the plugin "
-             "/ shared object.",
+             "dlopen(\"%s\") failed: %s. "
+             "The most common cause for this problem is missing dependencies. "
+             "Use ldd(1) to check the dependencies of the plugin / shared "
+             "object.",
              file, dlerror());
 
-    ERROR("%s", errbuf);
-    /* Make sure this is printed to STDERR in any case, but also
-     * make sure it's printed only once. */
-    if (list_log != NULL)
-      fprintf(stderr, "ERROR: %s\n", errbuf);
+    /* This error is printed to STDERR unconditionally. If list_log is NULL,
+     * plugin_log() will also print to STDERR. We avoid duplicate output by
+     * checking that the list of log handlers, list_log, is not NULL. */
+    fprintf(stderr, "ERROR: %s\n", errbuf);
+    if (list_log != NULL) {
+      ERROR("%s", errbuf);
+    }
 
-    return 1;
+    return ENOENT;
   }
 
-  reg_handle = (void (*)(void))dlsym(dlh, "module_register");
+  void (*reg_handle)(void) = dlsym(dlh, "module_register");
   if (reg_handle == NULL) {
-    WARNING("Couldn't find symbol \"module_register\" in \"%s\": %s\n", file,
-            dlerror());
+    ERROR("Couldn't find symbol \"module_register\" in \"%s\": %s\n", file,
+          dlerror());
     dlclose(dlh);
-    return -1;
+    return ENOENT;
   }
 
   (*reg_handle)();
-
   return 0;
 }
 
@@ -610,9 +613,7 @@ static void set_thread_name(pthread_t tid, char const *name) {
 #if defined(HAVE_PTHREAD_SETNAME_NP)
   int status = pthread_setname_np(tid, n);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("set_thread_name(\"%s\"): %s", n,
-          sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("set_thread_name(\"%s\"): %s", n, STRERROR(status));
   }
 #else /* if defined(HAVE_PTHREAD_SET_NAME_NP) */
   pthread_set_name_np(tid, n);
@@ -626,7 +627,7 @@ static void start_read_threads(size_t num) /* {{{ */
   if (read_threads != NULL)
     return;
 
-  read_threads = (pthread_t *)calloc(num, sizeof(pthread_t));
+  read_threads = calloc(num, sizeof(*read_threads));
   if (read_threads == NULL) {
     ERROR("plugin: start_read_threads: calloc failed.");
     return;
@@ -638,15 +639,14 @@ static void start_read_threads(size_t num) /* {{{ */
                                 /* attr = */ NULL, plugin_read_thread,
                                 /* arg = */ NULL);
     if (status != 0) {
-      char errbuf[1024];
-      ERROR("plugin: start_read_threads: pthread_create failed "
-            "with status %i (%s).",
-            status, sstrerror(status, errbuf, sizeof(errbuf)));
+      ERROR("plugin: start_read_threads: pthread_create failed with status %i "
+            "(%s).",
+            status, STRERROR(status));
       return;
     }
 
     char name[THREAD_NAME_MAX];
-    snprintf(name, sizeof(name), "reader#%zu", read_threads_num);
+    snprintf(name, sizeof(name), "reader#%" PRIu64, (uint64_t)read_threads_num);
     set_thread_name(read_threads[read_threads_num], name);
 
     read_threads_num++;
@@ -657,7 +657,7 @@ static void stop_read_threads(void) {
   if (read_threads == NULL)
     return;
 
-  INFO("collectd: Stopping %zu read threads.", read_threads_num);
+  INFO("collectd: Stopping %" PRIsz " read threads.", read_threads_num);
 
   pthread_mutex_lock(&read_lock);
   read_loop = 0;
@@ -719,25 +719,8 @@ plugin_value_list_clone(value_list_t const *vl_orig) /* {{{ */
     vl->time = cdtime();
 
   /* Fill in the interval from the thread context, if it is zero. */
-  if (vl->interval == 0) {
-    plugin_ctx_t ctx = plugin_get_ctx();
-
-    if (ctx.interval != 0)
-      vl->interval = ctx.interval;
-    else {
-      char name[6 * DATA_MAX_NAME_LEN];
-      FORMAT_VL(name, sizeof(name), vl);
-      ERROR("plugin_value_list_clone: Unable to determine "
-            "interval from context for "
-            "value list \"%s\". "
-            "This indicates a broken plugin. "
-            "Please report this problem to the "
-            "collectd mailing list or at "
-            "<http://collectd.org/bugs/>.",
-            name);
-      vl->interval = cf_get_default_interval();
-    }
-  }
+  if (vl->interval == 0)
+    vl->interval = plugin_get_interval();
 
   return vl;
 } /* }}} value_list_t *plugin_value_list_clone */
@@ -833,7 +816,7 @@ static void start_write_threads(size_t num) /* {{{ */
   if (write_threads != NULL)
     return;
 
-  write_threads = (pthread_t *)calloc(num, sizeof(pthread_t));
+  write_threads = calloc(num, sizeof(*write_threads));
   if (write_threads == NULL) {
     ERROR("plugin: start_write_threads: calloc failed.");
     return;
@@ -845,15 +828,15 @@ static void start_write_threads(size_t num) /* {{{ */
                                 /* attr = */ NULL, plugin_write_thread,
                                 /* arg = */ NULL);
     if (status != 0) {
-      char errbuf[1024];
-      ERROR("plugin: start_write_threads: pthread_create failed "
-            "with status %i (%s).",
-            status, sstrerror(status, errbuf, sizeof(errbuf)));
+      ERROR("plugin: start_write_threads: pthread_create failed with status %i "
+            "(%s).",
+            status, STRERROR(status));
       return;
     }
 
     char name[THREAD_NAME_MAX];
-    snprintf(name, sizeof(name), "writer#%zu", write_threads_num);
+    snprintf(name, sizeof(name), "writer#%" PRIu64,
+             (uint64_t)write_threads_num);
     set_thread_name(write_threads[write_threads_num], name);
 
     write_threads_num++;
@@ -868,10 +851,10 @@ static void stop_write_threads(void) /* {{{ */
   if (write_threads == NULL)
     return;
 
-  INFO("collectd: Stopping %zu write threads.", write_threads_num);
+  INFO("collectd: Stopping %" PRIsz " write threads.", write_threads_num);
 
   pthread_mutex_lock(&write_lock);
-  write_loop = 0;
+  write_loop = false;
   DEBUG("plugin: stop_write_threads: Signalling `write_cond'");
   pthread_cond_broadcast(&write_cond);
   pthread_mutex_unlock(&write_lock);
@@ -900,7 +883,7 @@ static void stop_write_threads(void) /* {{{ */
   pthread_mutex_unlock(&write_lock);
 
   if (i > 0) {
-    WARNING("plugin: %zu value list%s left after shutting down "
+    WARNING("plugin: %" PRIsz " value list%s left after shutting down "
             "the write threads.",
             i, (i == 1) ? " was" : "s were");
   }
@@ -922,7 +905,7 @@ void plugin_set_dir(const char *dir) {
     ERROR("plugin_set_dir: strdup(\"%s\") failed", dir);
 }
 
-static _Bool plugin_is_loaded(char const *name) {
+static bool plugin_is_loaded(char const *name) {
   int status;
 
   if (plugins_loaded == NULL)
@@ -964,7 +947,12 @@ static void plugin_free_loaded(void) {
 }
 
 #define BUFSIZE 512
-int plugin_load(char const *plugin_name, _Bool global) {
+#ifdef WIN32
+#define SHLIB_SUFFIX ".dll"
+#else
+#define SHLIB_SUFFIX ".so"
+#endif
+int plugin_load(char const *plugin_name, bool global) {
   DIR *dh;
   const char *dir;
   char filename[BUFSIZE] = "";
@@ -998,20 +986,19 @@ int plugin_load(char const *plugin_name, _Bool global) {
    */
   if ((strcasecmp("perl", plugin_name) == 0) ||
       (strcasecmp("python", plugin_name) == 0))
-    global = 1;
+    global = true;
 
-  /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
+  /* `cpu' should not match `cpufreq'. To solve this we add SHLIB_SUFFIX to the
    * type when matching the filename */
-  status = snprintf(typename, sizeof(typename), "%s.so", plugin_name);
+  status = snprintf(typename, sizeof(typename), "%s" SHLIB_SUFFIX, plugin_name);
   if ((status < 0) || ((size_t)status >= sizeof(typename))) {
-    WARNING("plugin_load: Filename too long: \"%s.so\"", plugin_name);
+    WARNING("plugin_load: Filename too long: \"%s" SHLIB_SUFFIX "\"",
+            plugin_name);
     return -1;
   }
 
   if ((dh = opendir(dir)) == NULL) {
-    char errbuf[1024];
-    ERROR("plugin_load: opendir (%s) failed: %s", dir,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("plugin_load: opendir (%s) failed: %s", dir, STRERRNO);
     return -1;
   }
 
@@ -1026,9 +1013,7 @@ int plugin_load(char const *plugin_name, _Bool global) {
     }
 
     if (lstat(filename, &statbuf) == -1) {
-      char errbuf[1024];
-      WARNING("plugin_load: stat (\"%s\") failed: %s", filename,
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("plugin_load: stat (\"%s\") failed: %s", filename, STRERRNO);
       continue;
     } else if (!S_ISREG(statbuf.st_mode)) {
       /* don't follow symlinks */
@@ -1061,19 +1046,20 @@ int plugin_load(char const *plugin_name, _Bool global) {
 /*
  * The `register_*' functions follow
  */
-int plugin_register_config(const char *name,
-                           int (*callback)(const char *key, const char *val),
-                           const char **keys, int keys_num) {
+EXPORT int plugin_register_config(const char *name,
+                                  int (*callback)(const char *key,
+                                                  const char *val),
+                                  const char **keys, int keys_num) {
   cf_register(name, callback, keys, keys_num);
   return 0;
 } /* int plugin_register_config */
 
-int plugin_register_complex_config(const char *type,
-                                   int (*callback)(oconfig_item_t *)) {
+EXPORT int plugin_register_complex_config(const char *type,
+                                          int (*callback)(oconfig_item_t *)) {
   return cf_register_complex(type, callback);
 } /* int plugin_register_complex_config */
 
-int plugin_register_init(const char *name, int (*callback)(void)) {
+EXPORT int plugin_register_init(const char *name, int (*callback)(void)) {
   return create_register_callback(&list_init, name, (void *)callback, NULL);
 } /* plugin_register_init */
 
@@ -1125,9 +1111,9 @@ static int plugin_insert_read(read_func_t *rf) {
   le = llist_search(read_list, rf->rf_name);
   if (le != NULL) {
     pthread_mutex_unlock(&read_lock);
-    WARNING("The read function \"%s\" is already registered. "
-            "Check for duplicates in your configuration!",
-            rf->rf_name);
+    P_WARNING("The read function \"%s\" is already registered. "
+              "Check for duplicates in your configuration!",
+              rf->rf_name);
     return EINVAL;
   }
 
@@ -1155,7 +1141,7 @@ static int plugin_insert_read(read_func_t *rf) {
   return 0;
 } /* int plugin_insert_read */
 
-int plugin_register_read(const char *name, int (*callback)(void)) {
+EXPORT int plugin_register_read(const char *name, int (*callback)(void)) {
   read_func_t *rf;
   int status;
 
@@ -1173,6 +1159,7 @@ int plugin_register_read(const char *name, int (*callback)(void)) {
   rf->rf_name = strdup(name);
   rf->rf_type = RF_SIMPLE;
   rf->rf_interval = plugin_get_interval();
+  rf->rf_ctx.interval = rf->rf_interval;
 
   status = plugin_insert_read(rf);
   if (status != 0) {
@@ -1183,9 +1170,10 @@ int plugin_register_read(const char *name, int (*callback)(void)) {
   return status;
 } /* int plugin_register_read */
 
-int plugin_register_complex_read(const char *group, const char *name,
-                                 plugin_read_cb callback, cdtime_t interval,
-                                 user_data_t const *user_data) {
+EXPORT int plugin_register_complex_read(const char *group, const char *name,
+                                        plugin_read_cb callback,
+                                        cdtime_t interval,
+                                        user_data_t const *user_data) {
   read_func_t *rf;
   int status;
 
@@ -1214,6 +1202,7 @@ int plugin_register_complex_read(const char *group, const char *name,
   }
 
   rf->rf_ctx = plugin_get_ctx();
+  rf->rf_ctx.interval = rf->rf_interval;
 
   status = plugin_insert_read(rf);
   if (status != 0) {
@@ -1225,8 +1214,8 @@ int plugin_register_complex_read(const char *group, const char *name,
   return status;
 } /* int plugin_register_complex_read */
 
-int plugin_register_write(const char *name, plugin_write_cb callback,
-                          user_data_t const *ud) {
+EXPORT int plugin_register_write(const char *name, plugin_write_cb callback,
+                                 user_data_t const *ud) {
   return create_register_callback(&list_write, name, (void *)callback, ud);
 } /* int plugin_register_write */
 
@@ -1267,8 +1256,8 @@ static char *plugin_flush_callback_name(const char *name) {
   return flush_name;
 } /* static char *plugin_flush_callback_name */
 
-int plugin_register_flush(const char *name, plugin_flush_cb callback,
-                          user_data_t const *ud) {
+EXPORT int plugin_register_flush(const char *name, plugin_flush_cb callback,
+                                 user_data_t const *ud) {
   int status;
   plugin_ctx_t ctx = plugin_get_ctx();
 
@@ -1316,12 +1305,12 @@ int plugin_register_flush(const char *name, plugin_flush_cb callback,
   return 0;
 } /* int plugin_register_flush */
 
-int plugin_register_missing(const char *name, plugin_missing_cb callback,
-                            user_data_t const *ud) {
+EXPORT int plugin_register_missing(const char *name, plugin_missing_cb callback,
+                                   user_data_t const *ud) {
   return create_register_callback(&list_missing, name, (void *)callback, ud);
 } /* int plugin_register_missing */
 
-int plugin_register_shutdown(const char *name, int (*callback)(void)) {
+EXPORT int plugin_register_shutdown(const char *name, int (*callback)(void)) {
   return create_register_callback(&list_shutdown, name, (void *)callback, NULL);
 } /* int plugin_register_shutdown */
 
@@ -1344,7 +1333,7 @@ static void plugin_free_data_sets(void) {
   data_sets = NULL;
 } /* void plugin_free_data_sets */
 
-int plugin_register_data_set(const data_set_t *ds) {
+EXPORT int plugin_register_data_set(const data_set_t *ds) {
   data_set_t *ds_copy;
 
   if ((data_sets != NULL) && (c_avl_get(data_sets, ds->type, NULL) == 0)) {
@@ -1373,33 +1362,33 @@ int plugin_register_data_set(const data_set_t *ds) {
   return c_avl_insert(data_sets, (void *)ds_copy->type, (void *)ds_copy);
 } /* int plugin_register_data_set */
 
-int plugin_register_log(const char *name, plugin_log_cb callback,
-                        user_data_t const *ud) {
+EXPORT int plugin_register_log(const char *name, plugin_log_cb callback,
+                               user_data_t const *ud) {
   return create_register_callback(&list_log, name, (void *)callback, ud);
 } /* int plugin_register_log */
 
-int plugin_register_notification(const char *name,
-                                 plugin_notification_cb callback,
-                                 user_data_t const *ud) {
+EXPORT int plugin_register_notification(const char *name,
+                                        plugin_notification_cb callback,
+                                        user_data_t const *ud) {
   return create_register_callback(&list_notification, name, (void *)callback,
                                   ud);
 } /* int plugin_register_log */
 
-int plugin_unregister_config(const char *name) {
+EXPORT int plugin_unregister_config(const char *name) {
   cf_unregister(name);
   return 0;
 } /* int plugin_unregister_config */
 
-int plugin_unregister_complex_config(const char *name) {
+EXPORT int plugin_unregister_complex_config(const char *name) {
   cf_unregister_complex(name);
   return 0;
 } /* int plugin_unregister_complex_config */
 
-int plugin_unregister_init(const char *name) {
+EXPORT int plugin_unregister_init(const char *name) {
   return plugin_unregister(list_init, name);
 }
 
-int plugin_unregister_read(const char *name) /* {{{ */
+EXPORT int plugin_unregister_read(const char *name) /* {{{ */
 {
   llentry_t *le;
   read_func_t *rf;
@@ -1436,7 +1425,7 @@ int plugin_unregister_read(const char *name) /* {{{ */
   return 0;
 } /* }}} int plugin_unregister_read */
 
-void plugin_log_available_writers(void) {
+EXPORT void plugin_log_available_writers(void) {
   log_list_callbacks(&list_write, "Available write targets:");
 }
 
@@ -1448,7 +1437,7 @@ static int compare_read_func_group(llentry_t *e, void *ud) /* {{{ */
   return strcmp(rf->rf_group, (const char *)group);
 } /* }}} int compare_read_func_group */
 
-int plugin_unregister_read_group(const char *group) /* {{{ */
+EXPORT int plugin_unregister_read_group(const char *group) /* {{{ */
 {
   llentry_t *le;
   read_func_t *rf;
@@ -1498,11 +1487,11 @@ int plugin_unregister_read_group(const char *group) /* {{{ */
   return 0;
 } /* }}} int plugin_unregister_read_group */
 
-int plugin_unregister_write(const char *name) {
+EXPORT int plugin_unregister_write(const char *name) {
   return plugin_unregister(list_write, name);
 }
 
-int plugin_unregister_flush(const char *name) {
+EXPORT int plugin_unregister_flush(const char *name) {
   plugin_ctx_t ctx = plugin_get_ctx();
 
   if (ctx.flush_interval != 0) {
@@ -1518,15 +1507,15 @@ int plugin_unregister_flush(const char *name) {
   return plugin_unregister(list_flush, name);
 }
 
-int plugin_unregister_missing(const char *name) {
+EXPORT int plugin_unregister_missing(const char *name) {
   return plugin_unregister(list_missing, name);
 }
 
-int plugin_unregister_shutdown(const char *name) {
+EXPORT int plugin_unregister_shutdown(const char *name) {
   return plugin_unregister(list_shutdown, name);
 }
 
-int plugin_unregister_data_set(const char *name) {
+EXPORT int plugin_unregister_data_set(const char *name) {
   data_set_t *ds;
 
   if (data_sets == NULL)
@@ -1541,15 +1530,15 @@ int plugin_unregister_data_set(const char *name) {
   return 0;
 } /* int plugin_unregister_data_set */
 
-int plugin_unregister_log(const char *name) {
+EXPORT int plugin_unregister_log(const char *name) {
   return plugin_unregister(list_log, name);
 }
 
-int plugin_unregister_notification(const char *name) {
+EXPORT int plugin_unregister_notification(const char *name) {
   return plugin_unregister(list_notification, name);
 }
 
-int plugin_init_all(void) {
+EXPORT int plugin_init_all(void) {
   char const *chain_name;
   llentry_t *le;
   int status;
@@ -1559,7 +1548,7 @@ int plugin_init_all(void) {
   uc_init();
 
   if (IS_TRUE(global_option_get("CollectInternalStats"))) {
-    record_statistics = 1;
+    record_statistics = true;
     plugin_register_read("collectd", plugin_update_internal_statistics);
   }
 
@@ -1648,14 +1637,14 @@ int plugin_init_all(void) {
 } /* void plugin_init_all */
 
 /* TODO: Rename this function. */
-void plugin_read_all(void) {
+EXPORT void plugin_read_all(void) {
   uc_check_timeout();
 
   return;
 } /* void plugin_read_all */
 
 /* Read function called when the `-T' command line argument is given. */
-int plugin_read_all_once(void) {
+EXPORT int plugin_read_all_once(void) {
   int status;
   int return_status = 0;
 
@@ -1700,8 +1689,8 @@ int plugin_read_all_once(void) {
   return return_status;
 } /* int plugin_read_all_once */
 
-int plugin_write(const char *plugin, /* {{{ */
-                 const data_set_t *ds, const value_list_t *vl) {
+EXPORT int plugin_write(const char *plugin, /* {{{ */
+                        const data_set_t *ds, const value_list_t *vl) {
   llentry_t *le;
   int status;
 
@@ -1728,8 +1717,12 @@ int plugin_write(const char *plugin, /* {{{ */
       callback_func_t *cf = le->value;
       plugin_write_cb callback;
 
-      /* do not switch plugin context; rather keep the context (interval)
-       * information of the calling read plugin */
+      /* Keep the read plugin's interval and flush information but update the
+       * plugin name. */
+      plugin_ctx_t old_ctx = plugin_get_ctx();
+      plugin_ctx_t ctx = old_ctx;
+      ctx.name = cf->cf_ctx.name;
+      plugin_set_ctx(ctx);
 
       DEBUG("plugin: plugin_write: Writing values via %s.", le->key);
       callback = cf->cf_callback;
@@ -1739,6 +1732,7 @@ int plugin_write(const char *plugin, /* {{{ */
       else
         success++;
 
+      plugin_set_ctx(old_ctx);
       le = le->next;
     }
 
@@ -1775,7 +1769,8 @@ int plugin_write(const char *plugin, /* {{{ */
   return status;
 } /* }}} int plugin_write */
 
-int plugin_flush(const char *plugin, cdtime_t timeout, const char *identifier) {
+EXPORT int plugin_flush(const char *plugin, cdtime_t timeout,
+                        const char *identifier) {
   llentry_t *le;
 
   if (list_flush == NULL)
@@ -1805,7 +1800,7 @@ int plugin_flush(const char *plugin, cdtime_t timeout, const char *identifier) {
   return 0;
 } /* int plugin_flush */
 
-int plugin_shutdown_all(void) {
+EXPORT int plugin_shutdown_all(void) {
   llentry_t *le;
   int ret = 0; // Assume success.
 
@@ -1871,7 +1866,7 @@ int plugin_shutdown_all(void) {
   return ret;
 } /* void plugin_shutdown_all */
 
-int plugin_dispatch_missing(const value_list_t *vl) /* {{{ */
+EXPORT int plugin_dispatch_missing(const value_list_t *vl) /* {{{ */
 {
   if (list_missing == NULL)
     return 0;
@@ -1904,7 +1899,7 @@ static int plugin_dispatch_values_internal(value_list_t *vl) {
   int status;
   static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC;
 
-  _Bool free_meta_data = 0;
+  bool free_meta_data = false;
 
   assert(vl != NULL);
 
@@ -1924,7 +1919,7 @@ static int plugin_dispatch_values_internal(value_list_t *vl) {
    * this case matches and targets may add some and the calling function
    * may not expect (and therefore free) that data. */
   if (vl->meta == NULL)
-    free_meta_data = 1;
+    free_meta_data = true;
 
   if (list_write == NULL)
     c_complain_once(LOG_WARNING, &no_write_complaint,
@@ -1970,8 +1965,8 @@ static int plugin_dispatch_values_internal(value_list_t *vl) {
 #else
   if (ds->ds_num != vl->values_len) {
     ERROR("plugin_dispatch_values: ds->type = %s: "
-          "(ds->ds_num = %zu) != "
-          "(vl->values_len = %zu)",
+          "(ds->ds_num = %" PRIsz ") != "
+          "(vl->values_len = %" PRIsz ")",
           ds->type, ds->ds_num, vl->values_len);
     return -1;
   }
@@ -2008,7 +2003,7 @@ static int plugin_dispatch_values_internal(value_list_t *vl) {
   } else
     fc_default_action(ds, vl);
 
-  if ((free_meta_data != 0) && (vl->meta != NULL)) {
+  if ((free_meta_data == true) && (vl->meta != NULL)) {
     meta_data_destroy(vl->meta);
     vl->meta = NULL;
   }
@@ -2037,9 +2032,9 @@ static double get_drop_probability(void) /* {{{ */
   return (double)pos / (double)size;
 } /* }}} double get_drop_probability */
 
-static _Bool check_drop_value(void) /* {{{ */
+static bool check_drop_value(void) /* {{{ */
 {
-  static cdtime_t last_message_time = 0;
+  static cdtime_t last_message_time;
   static pthread_mutex_t last_message_lock = PTHREAD_MUTEX_INITIALIZER;
 
   double p;
@@ -2047,11 +2042,11 @@ static _Bool check_drop_value(void) /* {{{ */
   int status;
 
   if (write_limit_high == 0)
-    return 0;
+    return false;
 
   p = get_drop_probability();
   if (p == 0.0)
-    return 0;
+    return false;
 
   status = pthread_mutex_trylock(&last_message_lock);
   if (status == 0) {
@@ -2068,18 +2063,17 @@ static _Bool check_drop_value(void) /* {{{ */
   }
 
   if (p == 1.0)
-    return 1;
+    return true;
 
   q = cdrand_d();
   if (q > p)
-    return 1;
+    return true;
   else
-    return 0;
-} /* }}} _Bool check_drop_value */
+    return false;
+} /* }}} bool check_drop_value */
 
-int plugin_dispatch_values(value_list_t const *vl) {
+EXPORT int plugin_dispatch_values(value_list_t const *vl) {
   int status;
-  static pthread_mutex_t statistics_lock = PTHREAD_MUTEX_INITIALIZER;
 
   if (check_drop_value()) {
     if (record_statistics) {
@@ -2092,10 +2086,9 @@ int plugin_dispatch_values(value_list_t const *vl) {
 
   status = plugin_write_enqueue(vl);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("plugin_dispatch_values: plugin_write_enqueue failed "
-          "with status %i (%s).",
-          status, sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("plugin_dispatch_values: plugin_write_enqueue failed with status %i "
+          "(%s).",
+          status, STRERROR(status));
     return status;
   }
 
@@ -2104,12 +2097,21 @@ int plugin_dispatch_values(value_list_t const *vl) {
 
 __attribute__((sentinel)) int
 plugin_dispatch_multivalue(value_list_t const *template, /* {{{ */
-                           _Bool store_percentage, int store_type, ...) {
+                           bool store_percentage, int store_type, ...) {
   value_list_t *vl;
   int failed = 0;
   gauge_t sum = 0.0;
   va_list ap;
 
+  if (check_drop_value()) {
+    if (record_statistics) {
+      pthread_mutex_lock(&statistics_lock);
+      stats_values_dropped++;
+      pthread_mutex_unlock(&statistics_lock);
+    }
+    return 0;
+  }
+
   assert(template->values_len == 1);
 
   /* Calculate sum for Gauge to calculate percent if needed */
@@ -2177,7 +2179,7 @@ plugin_dispatch_multivalue(value_list_t const *template, /* {{{ */
   return failed;
 } /* }}} int plugin_dispatch_multivalue */
 
-int plugin_dispatch_notification(const notification_t *notif) {
+EXPORT int plugin_dispatch_notification(const notification_t *notif) {
   llentry_t *le;
   /* Possible TODO: Add flap detection here */
 
@@ -2214,7 +2216,7 @@ int plugin_dispatch_notification(const notification_t *notif) {
   return 0;
 } /* int plugin_dispatch_notification */
 
-void plugin_log(int level, const char *format, ...) {
+EXPORT void plugin_log(int level, const char *format, ...) {
   char msg[1024];
   va_list ap;
   llentry_t *le;
@@ -2251,6 +2253,21 @@ void plugin_log(int level, const char *format, ...) {
   }
 } /* void plugin_log */
 
+void daemon_log(int level, const char *format, ...) {
+  char msg[1024] = ""; // Size inherits from plugin_log()
+
+  char const *name = plugin_get_ctx().name;
+  if (name == NULL)
+    name = "UNKNOWN";
+
+  va_list ap;
+  va_start(ap, format);
+  vsnprintf(msg, sizeof(msg), format, ap);
+  va_end(ap);
+
+  plugin_log(level, "%s plugin: %s", name, msg);
+} /* void daemon_log */
+
 int parse_log_severity(const char *severity) {
   int log_level = -1;
 
@@ -2272,7 +2289,7 @@ int parse_log_severity(const char *severity) {
   return log_level;
 } /* int parse_log_severity */
 
-int parse_notif_severity(const char *severity) {
+EXPORT int parse_notif_severity(const char *severity) {
   int notif_severity = -1;
 
   if (strcasecmp(severity, "FAILURE") == 0)
@@ -2286,11 +2303,11 @@ int parse_notif_severity(const char *severity) {
   return notif_severity;
 } /* int parse_notif_severity */
 
-const data_set_t *plugin_get_ds(const char *name) {
+EXPORT const data_set_t *plugin_get_ds(const char *name) {
   data_set_t *ds;
 
   if (data_sets == NULL) {
-    ERROR("plugin_get_ds: No data sets are defined yet.");
+    P_ERROR("plugin_get_ds: No data sets are defined yet.");
     return NULL;
   }
 
@@ -2345,7 +2362,7 @@ static int plugin_notification_meta_add(notification_t *n, const char *name,
     break;
   }
   case NM_TYPE_BOOLEAN: {
-    meta->nm_value.nm_boolean = *((_Bool *)value);
+    meta->nm_value.nm_boolean = *((bool *)value);
     break;
   }
   default: {
@@ -2390,7 +2407,7 @@ int plugin_notification_meta_add_double(notification_t *n, const char *name,
 }
 
 int plugin_notification_meta_add_boolean(notification_t *n, const char *name,
-                                         _Bool value) {
+                                         bool value) {
   return plugin_notification_meta_add(n, name, NM_TYPE_BOOLEAN, &value);
 }
 
@@ -2462,9 +2479,7 @@ static plugin_ctx_t *plugin_ctx_create(void) {
 
   ctx = malloc(sizeof(*ctx));
   if (ctx == NULL) {
-    char errbuf[1024];
-    ERROR("Failed to allocate plugin context: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("Failed to allocate plugin context: %s", STRERRNO);
     return NULL;
   }
 
@@ -2475,12 +2490,12 @@ static plugin_ctx_t *plugin_ctx_create(void) {
   return ctx;
 } /* int plugin_ctx_create */
 
-void plugin_init_ctx(void) {
+EXPORT void plugin_init_ctx(void) {
   pthread_key_create(&plugin_ctx_key, plugin_ctx_destructor);
-  plugin_ctx_key_initialized = 1;
+  plugin_ctx_key_initialized = true;
 } /* void plugin_init_ctx */
 
-plugin_ctx_t plugin_get_ctx(void) {
+EXPORT plugin_ctx_t plugin_get_ctx(void) {
   plugin_ctx_t *ctx;
 
   assert(plugin_ctx_key_initialized);
@@ -2496,7 +2511,7 @@ plugin_ctx_t plugin_get_ctx(void) {
   return *ctx;
 } /* plugin_ctx_t plugin_get_ctx */
 
-plugin_ctx_t plugin_set_ctx(plugin_ctx_t ctx) {
+EXPORT plugin_ctx_t plugin_set_ctx(plugin_ctx_t ctx) {
   plugin_ctx_t *c;
   plugin_ctx_t old;
 
@@ -2516,13 +2531,15 @@ plugin_ctx_t plugin_set_ctx(plugin_ctx_t ctx) {
   return old;
 } /* void plugin_set_ctx */
 
-cdtime_t plugin_get_interval(void) {
+EXPORT cdtime_t plugin_get_interval(void) {
   cdtime_t interval;
 
   interval = plugin_get_ctx().interval;
   if (interval > 0)
     return interval;
 
+  P_ERROR("plugin_get_interval: Unable to determine Interval from context.");
+
   return cf_get_default_interval();
 } /* cdtime_t plugin_get_interval */
 
index a9ee72d..6b3a030 100644 (file)
 #include "collectd.h"
 
 #include "configfile.h"
-#include "meta_data.h"
+#include "utils/metadata/meta_data.h"
 #include "utils_time.h"
 
+#include <inttypes.h>
 #include <pthread.h>
 
 #define DS_TYPE_COUNTER 0
@@ -147,7 +148,7 @@ typedef struct notification_meta_s {
     int64_t nm_signed_int;
     uint64_t nm_unsigned_int;
     double nm_double;
-    _Bool nm_boolean;
+    bool nm_boolean;
   } nm_value;
   struct notification_meta_s *next;
 } notification_meta_t;
@@ -171,6 +172,7 @@ struct user_data_s {
 typedef struct user_data_s user_data_t;
 
 struct plugin_ctx_s {
+  char *name;
   cdtime_t interval;
   cdtime_t flush_interval;
   cdtime_t flush_timeout;
@@ -230,7 +232,7 @@ void plugin_set_dir(const char *dir);
  *  Re-loading an already loaded module is detected and zero is returned in
  *  this case.
  */
-int plugin_load(const char *name, _Bool global);
+int plugin_load(const char *name, bool global);
 
 int plugin_init_all(void);
 void plugin_read_all(void);
@@ -243,7 +245,7 @@ int plugin_shutdown_all(void);
  *
  * DESCRIPTION
  *  Calls the write function of the given plugin with the provided data set and
- *  value list. It differs from `plugin_dispatch_value' in that it does not
+ *  value list. It differs from `plugin_dispatch_values' in that it does not
  *  update the cache, does not do threshold checking, call the chain subsystem
  *  and so on. It looks up the requested plugin and invokes the function, end
  *  of story.
@@ -346,7 +348,7 @@ int plugin_dispatch_values(value_list_t const *vl);
  *  plugin_dispatch_multivalue
  *
  * SYNOPSIS
- *  plugin_dispatch_multivalue (vl, 1, DS_TYPE_GAUGE,
+ *  plugin_dispatch_multivalue (vl, true, DS_TYPE_GAUGE,
  *                              "free", 42.0,
  *                              "used", 58.0,
  *                              NULL);
@@ -374,7 +376,7 @@ int plugin_dispatch_values(value_list_t const *vl);
  *  The number of values it failed to dispatch (zero on success).
  */
 __attribute__((sentinel)) int plugin_dispatch_multivalue(value_list_t const *vl,
-                                                         _Bool store_percentage,
+                                                         bool store_percentage,
                                                          int store_type, ...);
 
 int plugin_dispatch_missing(const value_list_t *vl);
@@ -398,6 +400,15 @@ int parse_notif_severity(const char *severity);
 #define DEBUG(...) /* noop */
 #endif             /* ! COLLECT_DEBUG */
 
+/* This will log messages, prefixed by plugin name */
+void daemon_log(int level, const char *format, ...)
+    __attribute__((format(printf, 2, 3)));
+
+#define P_ERROR(...) daemon_log(LOG_ERR, __VA_ARGS__)
+#define P_WARNING(...) daemon_log(LOG_WARNING, __VA_ARGS__)
+#define P_NOTICE(...) daemon_log(LOG_NOTICE, __VA_ARGS__)
+#define P_INFO(...) daemon_log(LOG_INFO, __VA_ARGS__)
+
 const data_set_t *plugin_get_ds(const char *name);
 
 int plugin_notification_meta_add_string(notification_t *n, const char *name,
@@ -409,7 +420,7 @@ int plugin_notification_meta_add_unsigned_int(notification_t *n,
 int plugin_notification_meta_add_double(notification_t *n, const char *name,
                                         double value);
 int plugin_notification_meta_add_boolean(notification_t *n, const char *name,
-                                         _Bool value);
+                                         bool value);
 
 int plugin_notification_meta_copy(notification_t *dst,
                                   const notification_t *src);
index 96bf382..1624f0e 100644 (file)
@@ -39,7 +39,7 @@ char *hostname_g = "example.com";
 void plugin_set_dir(const char *dir) { /* nop */
 }
 
-int plugin_load(const char *name, _Bool global) { return ENOTSUP; }
+int plugin_load(const char *name, bool global) { return ENOTSUP; }
 
 int plugin_register_config(const char *name,
                            int (*callback)(const char *key, const char *val),
@@ -56,7 +56,19 @@ int plugin_register_init(const char *name, plugin_init_cb callback) {
   return ENOTSUP;
 }
 
-int plugin_register_read(const char *name, int (*callback)(void)) {
+int plugin_register_read(__attribute__((unused)) const char *name,
+                         __attribute__((unused)) int (*callback)(void)) {
+  return ENOTSUP;
+}
+
+int plugin_register_write(__attribute__((unused)) const char *name,
+                          __attribute__((unused)) plugin_write_cb callback,
+                          __attribute__((unused)) user_data_t const *ud) {
+  return ENOTSUP;
+}
+
+int plugin_register_missing(const char *name, plugin_missing_cb callback,
+                            user_data_t const *ud) {
   return ENOTSUP;
 }
 
@@ -75,6 +87,65 @@ int plugin_register_data_set(const data_set_t *ds) { return ENOTSUP; }
 
 int plugin_dispatch_values(value_list_t const *vl) { return ENOTSUP; }
 
+int plugin_dispatch_notification(__attribute__((unused))
+                                 const notification_t *notif) {
+  return ENOTSUP;
+}
+
+int plugin_notification_meta_add_string(__attribute__((unused))
+                                        notification_t *n,
+                                        __attribute__((unused))
+                                        const char *name,
+                                        __attribute__((unused))
+                                        const char *value) {
+  return ENOTSUP;
+}
+
+int plugin_notification_meta_add_signed_int(__attribute__((unused))
+                                            notification_t *n,
+                                            __attribute__((unused))
+                                            const char *name,
+                                            __attribute__((unused))
+                                            int64_t value) {
+  return ENOTSUP;
+}
+
+int plugin_notification_meta_add_unsigned_int(__attribute__((unused))
+                                              notification_t *n,
+                                              __attribute__((unused))
+                                              const char *name,
+                                              __attribute__((unused))
+                                              uint64_t value) {
+  return ENOTSUP;
+}
+
+int plugin_notification_meta_add_double(__attribute__((unused))
+                                        notification_t *n,
+                                        __attribute__((unused))
+                                        const char *name,
+                                        __attribute__((unused)) double value) {
+  return ENOTSUP;
+}
+
+int plugin_notification_meta_add_boolean(__attribute__((unused))
+                                         notification_t *n,
+                                         __attribute__((unused))
+                                         const char *name,
+                                         __attribute__((unused)) _Bool value) {
+  return ENOTSUP;
+}
+
+int plugin_notification_meta_copy(__attribute__((unused)) notification_t *dst,
+                                  __attribute__((unused))
+                                  const notification_t *src) {
+  return ENOTSUP;
+}
+
+int plugin_notification_meta_free(__attribute__((unused))
+                                  notification_meta_t *n) {
+  return ENOTSUP;
+}
+
 int plugin_flush(const char *plugin, cdtime_t timeout, const char *identifier) {
   return ENOTSUP;
 }
@@ -99,6 +170,17 @@ void plugin_log(int level, char const *format, ...) {
   printf("plugin_log (%i, \"%s\");\n", level, buffer);
 }
 
+void daemon_log(int level, char const *format, ...) {
+  char buffer[1024];
+  va_list ap;
+
+  va_start(ap, format);
+  vsnprintf(buffer, sizeof(buffer), format, ap);
+  va_end(ap);
+
+  printf("daemon_log (%i, \"%s\");\n", level, buffer);
+}
+
 void plugin_init_ctx(void) { /* nop */
 }
 
index 89a08dd..7eb3cb2 100644 (file)
@@ -26,7 +26,7 @@
 
 #include "collectd.h"
 
-#include "common.h"
+#include "utils/common/common.h"
 
 #include "configfile.h"
 #include "plugin.h"
@@ -39,7 +39,7 @@ static int parse_ds(data_source_t *dsrc, char *buf, size_t buf_len) {
   int fields_num;
 
   if (buf_len < 11) {
-    ERROR("parse_ds: (buf_len = %zu) < 11", buf_len);
+    ERROR("parse_ds: (buf_len = %" PRIsz ") < 11", buf_len);
     return -1;
   }
 
@@ -96,8 +96,6 @@ static int parse_ds(data_source_t *dsrc, char *buf, size_t buf_len) {
 static void parse_line(char *buf) {
   char *fields[64];
   size_t fields_num;
-  data_set_t *ds;
-
   fields_num = strsplit(buf, fields, 64);
   if (fields_num < 2)
     return;
@@ -106,33 +104,27 @@ static void parse_line(char *buf) {
   if (fields[0][0] == '#')
     return;
 
-  ds = calloc(1, sizeof(*ds));
-  if (ds == NULL)
-    return;
+  data_set_t ds = {{0}};
 
-  sstrncpy(ds->type, fields[0], sizeof(ds->type));
+  sstrncpy(ds.type, fields[0], sizeof(ds.type));
 
-  ds->ds_num = fields_num - 1;
-  ds->ds = (data_source_t *)calloc(ds->ds_num, sizeof(data_source_t));
-  if (ds->ds == NULL) {
-    sfree(ds);
+  ds.ds_num = fields_num - 1;
+  ds.ds = calloc(ds.ds_num, sizeof(*ds.ds));
+  if (ds.ds == NULL)
     return;
-  }
 
-  for (size_t i = 0; i < ds->ds_num; i++)
-    if (parse_ds(ds->ds + i, fields[i + 1], strlen(fields[i + 1])) != 0) {
-      ERROR("types_list: parse_line: Cannot parse data source #%zu "
-            "of data set %s",
-            i, ds->type);
-      sfree(ds->ds);
-      sfree(ds);
+  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);
       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) {
@@ -174,11 +166,9 @@ int read_types_list(const char *file) {
 
   fh = fopen(file, "r");
   if (fh == NULL) {
-    char errbuf[1024];
     fprintf(stderr, "Failed to open types database `%s': %s.\n", file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-    ERROR("Failed to open types database `%s': %s", file,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
+    ERROR("Failed to open types database `%s': %s", file, STRERRNO);
     return -1;
   }
 
diff --git a/src/daemon/utils_avltree.c b/src/daemon/utils_avltree.c
deleted file mode 100644 (file)
index 87568fb..0000000
+++ /dev/null
@@ -1,648 +0,0 @@
-/**
- * collectd - src/utils_avltree.c
- * Copyright (C) 2006,2007  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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->left)
-      if (n->right == NULL)
-        break;
-    iter->node = n;
-  } else {
-    n = c_avl_node_prev(iter->node);
-  }
-
-  if (n == NULL)
-    return -1;
-
-  iter->node = n;
-  *key = n->key;
-  *value = n->value;
-
-  return 0;
-} /* int c_avl_iterator_prev */
-
-void c_avl_iterator_destroy(c_avl_iterator_t *iter) { free(iter); }
-
-int c_avl_size(c_avl_tree_t *t) {
-  if (t == NULL)
-    return 0;
-  return t->size;
-}
diff --git a/src/daemon/utils_avltree.h b/src/daemon/utils_avltree.h
deleted file mode 100644 (file)
index 3f52b93..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/**
- * collectd - src/utils_avltree.h
- * Copyright (C) 2006,2007  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/daemon/utils_avltree_test.c b/src/daemon/utils_avltree_test.c
deleted file mode 100644 (file)
index bb30f9d..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/**
- * collectd - src/tests/test_utils_avltree.c
- * Copyright (C) 2013       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- */
-
-#include "common.h" /* STATIC_ARRAY_SIZE */
-#include "collectd.h"
-
-#include "testing.h"
-#include "utils_avltree.h"
-
-static int compare_total_count = 0;
-#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);
-}
-
-DEF_TEST(success) {
-  struct {
-    char *key;
-    char *value;
-  } 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"},
-  };
-
-  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);
-  }
-
-  /* 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;
-}
index ea7c3e3..c53e5d1 100644 (file)
 
 #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>
@@ -76,7 +76,7 @@ struct uc_iter_s {
   cache_entry_t *entry;
 };
 
-static c_avl_tree_t *cache_tree = NULL;
+static c_avl_tree_t *cache_tree;
 static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static int cache_compare(const cache_entry_t *a, const cache_entry_t *b) {
@@ -154,7 +154,7 @@ static int uc_insert(const data_set_t *ds, const value_list_t *vl,
   ce = cache_alloc(ds->ds_num);
   if (ce == NULL) {
     sfree(key_copy);
-    ERROR("uc_insert: cache_alloc (%zu) failed.", ds->ds_num);
+    ERROR("uc_insert: cache_alloc (%" PRIsz ") failed.", ds->ds_num);
     return -1;
   }
 
@@ -201,7 +201,7 @@ static int uc_insert(const data_set_t *ds, const value_list_t *vl,
   ce->last_time = vl->time;
   ce->last_update = cdtime();
   ce->interval = vl->interval;
-  ce->state = STATE_OKAY;
+  ce->state = STATE_UNKNOWN;
 
   if (c_avl_insert(cache_tree, key_copy, ce) != 0) {
     sfree(key_copy);
@@ -381,7 +381,7 @@ int uc_update(const data_set_t *ds, const value_list_t *vl) {
       return -1;
     } /* switch (ds->ds[i].type) */
 
-    DEBUG("uc_update: %s: ds[%zu] = %lf", name, i, ce->values_gauge[i]);
+    DEBUG("uc_update: %s: ds[%" PRIsz "] = %lf", name, i, ce->values_gauge[i]);
   } /* for (i) */
 
   /* Update the history if it exists. */
@@ -469,8 +469,8 @@ gauge_t *uc_get_rate(const data_set_t *ds, const value_list_t *vl) {
   /* This is important - the caller has no other way of knowing how many
    * values are returned. */
   if (ret_num != ds->ds_num) {
-    ERROR("utils_cache: uc_get_rate: ds[%s] has %zu values, "
-          "but uc_get_rate_by_name returned %zu.",
+    ERROR("utils_cache: uc_get_rate: ds[%s] has %" PRIsz " values, "
+          "but uc_get_rate_by_name returned %" PRIsz ".",
           ds->type, ds->ds_num, ret_num);
     sfree(ret);
     return NULL;
@@ -488,7 +488,7 @@ int uc_get_value_by_name(const char *name, value_t **ret_values,
 
   pthread_mutex_lock(&cache_lock);
 
-  if (c_avl_get(cache_tree, name, (void *) &ce) == 0) {
+  if (c_avl_get(cache_tree, name, (void *)&ce) == 0) {
     assert(ce != NULL);
 
     /* remove missing values from getval */
@@ -504,8 +504,7 @@ int uc_get_value_by_name(const char *name, value_t **ret_values,
         memcpy(ret, ce->values_raw, ret_num * sizeof(value_t));
       }
     }
-  }
-  else {
+  } else {
     DEBUG("utils_cache: uc_get_value_by_name: No such value: %s", name);
     status = -1;
   }
@@ -537,10 +536,10 @@ value_t *uc_get_value(const data_set_t *ds, const value_list_t *vl) {
 
   /* This is important - the caller has no other way of knowing how many
    * values are returned. */
-  if (ret_num != (size_t) ds->ds_num) {
-    ERROR("utils_cache: uc_get_value: ds[%s] has %zu values, "
-          "but uc_get_value_by_name returned %zu.", ds->type, ds->ds_num,
-          ret_num);
+  if (ret_num != (size_t)ds->ds_num) {
+    ERROR("utils_cache: uc_get_value: ds[%s] has %" PRIsz " values, "
+          "but uc_get_value_by_name returned %" PRIsz ".",
+          ds->type, ds->ds_num, ret_num);
     sfree(ret);
     return (NULL);
   }
@@ -827,9 +826,7 @@ int uc_inc_hits(const data_set_t *ds, const value_list_t *vl, int step) {
  * Iterator interface
  */
 uc_iter_t *uc_get_iterator(void) {
-  uc_iter_t *iter;
-
-  iter = (uc_iter_t *)calloc(1, sizeof(*iter));
+  uc_iter_t *iter = calloc(1, sizeof(*iter));
   if (iter == NULL)
     return NULL;
 
@@ -999,7 +996,7 @@ int uc_meta_data_exists(const value_list_t *vl,
                         const value_list_t *vl, const char *key, double value)
                         UC_WRAP(meta_data_add_double) int uc_meta_data_add_boolean(
                             const value_list_t *vl, const char *key,
-                            _Bool value) UC_WRAP(meta_data_add_boolean)
+                            bool value) UC_WRAP(meta_data_add_boolean)
 
                             int uc_meta_data_get_string(const value_list_t *vl,
                                                         const char *key,
@@ -1015,6 +1012,6 @@ int uc_meta_data_exists(const value_list_t *vl,
                                             const char *key, double *value)
                                             UC_WRAP(meta_data_get_double) int uc_meta_data_get_boolean(
                                                 const value_list_t *vl,
-                                                const char *key, _Bool *value)
+                                                const char *key, bool *value)
                                                 UC_WRAP(meta_data_get_boolean)
 #undef UC_WRAP
index 08c2f10..d3ea936 100644 (file)
 
 #include "plugin.h"
 
-#define STATE_OKAY 0
-#define STATE_WARNING 1
-#define STATE_ERROR 2
+#define STATE_UNKNOWN 0
+#define STATE_OKAY 1
+#define STATE_WARNING 2
+#define STATE_ERROR 3
 #define STATE_MISSING 15
 
 int uc_init(void);
@@ -42,7 +43,8 @@ int uc_update(const data_set_t *ds, const value_list_t *vl);
 int uc_get_rate_by_name(const char *name, gauge_t **ret_values,
                         size_t *ret_values_num);
 gauge_t *uc_get_rate(const data_set_t *ds, const value_list_t *vl);
-int uc_get_value_by_name(const char *name, value_t **ret_values, size_t *ret_values_num);
+int uc_get_value_by_name(const char *name, value_t **ret_values,
+                         size_t *ret_values_num);
 value_t *uc_get_value(const data_set_t *ds, const value_list_t *vl);
 
 size_t uc_get_size(void);
@@ -124,7 +126,7 @@ int uc_meta_data_add_unsigned_int(const value_list_t *vl, const char *key,
 int uc_meta_data_add_double(const value_list_t *vl, const char *key,
                             double value);
 int uc_meta_data_add_boolean(const value_list_t *vl, const char *key,
-                             _Bool value);
+                             bool value);
 
 int uc_meta_data_get_string(const value_list_t *vl, const char *key,
                             char **value);
@@ -135,6 +137,6 @@ int uc_meta_data_get_unsigned_int(const value_list_t *vl, const char *key,
 int uc_meta_data_get_double(const value_list_t *vl, const char *key,
                             double *value);
 int uc_meta_data_get_boolean(const value_list_t *vl, const char *key,
-                             _Bool *value);
+                             bool *value);
 
 #endif /* !UTILS_CACHE_H */
index 5389d12..7d7e436 100644 (file)
  *   Florian octo Forster <octo at collectd.org>
  */
 
-#include <errno.h>
 #include "utils_cache.h"
+#include <errno.h>
+
+#include <errno.h>
 
 gauge_t *uc_get_rate(__attribute__((unused)) data_set_t const *ds,
                      __attribute__((unused)) value_list_t const *vl) {
@@ -41,3 +43,28 @@ int uc_get_rate_by_name(const char *name, gauge_t **ret_values,
 int uc_get_names(char ***ret_names, cdtime_t **ret_times, size_t *ret_number) {
   return ENOTSUP;
 }
+
+int uc_get_value_by_name(const char *name, value_t **ret_values,
+                         size_t *ret_values_num) {
+  return ENOTSUP;
+}
+
+int uc_meta_data_get_signed_int(const value_list_t *vl, const char *key,
+                                int64_t *value) {
+  return -ENOENT;
+}
+
+int uc_meta_data_get_unsigned_int(const value_list_t *vl, const char *key,
+                                  uint64_t *value) {
+  return -ENOENT;
+}
+
+int uc_meta_data_add_signed_int(const value_list_t *vl, const char *key,
+                                int64_t value) {
+  return 0;
+}
+
+int uc_meta_data_add_unsigned_int(const value_list_t *vl, const char *key,
+                                  uint64_t value) {
+  return 0;
+}
index d2162ba..e34cf6f 100644 (file)
@@ -64,7 +64,7 @@ void c_complain(int level, c_complain_t *c, const char *format, ...) {
 
   va_start(ap, format);
   if (vcomplain(level, c, format, ap))
-    c->complained_once = 1;
+    c->complained_once = true;
   va_end(ap);
 } /* c_complain */
 
@@ -76,7 +76,7 @@ void c_complain_once(int level, c_complain_t *c, const char *format, ...) {
 
   va_start(ap, format);
   if (vcomplain(level, c, format, ap))
-    c->complained_once = 1;
+    c->complained_once = true;
   va_end(ap);
 } /* c_complain_once */
 
@@ -88,7 +88,7 @@ void c_do_release(int level, c_complain_t *c, const char *format, ...) {
     return;
 
   c->interval = 0;
-  c->complained_once = 0;
+  c->complained_once = false;
 
   va_start(ap, format);
   vsnprintf(message, sizeof(message), format, ap);
index 46d3a19..88387be 100644 (file)
@@ -39,7 +39,7 @@ typedef struct {
    * 0 indicates that the complaint is no longer valid. */
   cdtime_t interval;
 
-  _Bool complained_once;
+  bool complained_once;
 } c_complain_t;
 
 #define C_COMPLAIN_INIT_STATIC                                                 \
@@ -48,7 +48,7 @@ typedef struct {
   do {                                                                         \
     (c)->last = 0;                                                             \
     (c)->interval = 0;                                                         \
-    (c)->complained_once = 0;                                                  \
+    (c)->complained_once = false;                                              \
   } while (0)
 
 /*
diff --git a/src/daemon/utils_heap.c b/src/daemon/utils_heap.c
deleted file mode 100644 (file)
index d36d410..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-/**
- * collectd - src/utils_heap.c
- * Copyright (C) 2009       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- **/
-
-#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 */
diff --git a/src/daemon/utils_heap.h b/src/daemon/utils_heap.h
deleted file mode 100644 (file)
index d2d70cd..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * collectd - src/utils_heap.h
- * Copyright (C) 2009       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/daemon/utils_heap_test.c b/src/daemon/utils_heap_test.c
deleted file mode 100644 (file)
index 827c090..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * collectd - src/tests/test_utils_heap.c
- * Copyright (C) 2013       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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;
-}
index cf926f9..5500aaa 100644 (file)
 
 #include <pthread.h>
 
+#ifdef WIN32
+double erand48(unsigned short unused[3]) {
+  return (double)rand() / (double)RAND_MAX;
+}
+
+long int jrand48(unsigned short unused[3]) { return rand(); }
+#endif
+
 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-static _Bool have_seed = 0;
+static bool have_seed;
 static unsigned short seed[3];
 
 static void cdrand_seed(void) {
@@ -47,7 +55,11 @@ static void cdrand_seed(void) {
   seed[1] = (unsigned short)(t >> 16);
   seed[2] = (unsigned short)(t >> 32);
 
-  have_seed = 1;
+#ifdef WIN32
+  srand((unsigned)t);
+#endif
+
+  have_seed = true;
 }
 
 double cdrand_d(void) {
index a016342..0b5f00d 100644 (file)
@@ -30,7 +30,7 @@
 
 #include "collectd.h"
 
-#include "common.h"
+#include "utils/common/common.h"
 #include "utils_subst.h"
 
 char *subst(char *buf, size_t buflen, const char *string, size_t off1,
@@ -90,28 +90,6 @@ char *subst(char *buf, size_t buflen, const char *string, size_t off1,
   return buf;
 } /* subst */
 
-char *asubst(const char *string, int off1, int off2, const char *replacement) {
-  char *buf;
-  int len;
-
-  char *ret;
-
-  if ((NULL == string) || (0 > off1) || (0 > off2) || (off1 > off2) ||
-      (NULL == replacement))
-    return NULL;
-
-  len = off1 + strlen(replacement) + strlen(string) - off2 + 1;
-
-  buf = malloc(len);
-  if (NULL == buf)
-    return NULL;
-
-  ret = subst(buf, len, string, off1, off2, replacement);
-  if (NULL == ret)
-    free(buf);
-  return ret;
-} /* asubst */
-
 char *subst_string(char *buf, size_t buflen, const char *string,
                    const char *needle, const char *replacement) {
   size_t needle_len;
@@ -152,7 +130,7 @@ char *subst_string(char *buf, size_t buflen, const char *string,
   }
 
   if (i >= buflen) {
-    WARNING("subst_string: Loop exited after %zu iterations: "
+    WARNING("subst_string: Loop exited after %" PRIsz " iterations: "
             "string = %s; needle = %s; replacement = %s;",
             i, string, needle, replacement);
   }
index a10b258..7807555 100644 (file)
@@ -70,17 +70,6 @@ char *subst(char *buf, size_t buflen, const char *string, size_t off1,
             size_t off2, const char *replacement);
 
 /*
- * asubst:
- *
- * This function is very similar to subst(). It differs in that it
- * automatically allocates the memory required for the return value which the
- * user then has to free himself.
- *
- * Returns the newly allocated result string on success, NULL else.
- */
-char *asubst(const char *string, int off1, int off2, const char *replacement);
-
-/*
  * subst_string:
  *
  * Works like `subst', but instead of specifying start and end offsets you
index 2056096..0e58230 100644 (file)
@@ -25,7 +25,7 @@
  */
 
 #include "collectd.h"
-#include "common.h" /* for STATIC_ARRAY_SIZE */
+#include "utils/common/common.h" /* for STATIC_ARRAY_SIZE */
 
 #include "testing.h"
 #include "utils_subst.h"
index 8c03341..52af648 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
 #include "utils_threshold.h"
 
 #include <pthread.h>
index ab440ed..5dd7d0e 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_time.h"
 
 #ifndef DEFAULT_MOCK_TIME
@@ -47,9 +47,7 @@ cdtime_t cdtime(void) /* {{{ */
 
   status = clock_gettime(CLOCK_REALTIME, &ts);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("cdtime: clock_gettime failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("cdtime: clock_gettime failed: %s", STRERRNO);
     return 0;
   }
 
@@ -64,9 +62,7 @@ cdtime_t cdtime(void) /* {{{ */
 
   status = gettimeofday(&tv, /* struct timezone = */ NULL);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("cdtime: gettimeofday failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("cdtime: gettimeofday failed: %s", STRERRNO);
     return 0;
   }
 
@@ -85,10 +81,8 @@ static int get_utc_time(cdtime_t t, struct tm *t_tm, long *nsec) /* {{{ */
   NORMALIZE_TIMESPEC(t_spec);
 
   if (gmtime_r(&t_spec.tv_sec, t_tm) == NULL) {
-    char errbuf[1024];
     int status = errno;
-    ERROR("get_utc_time: gmtime_r failed: %s",
-          sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("get_utc_time: gmtime_r failed: %s", STRERRNO);
     return status;
   }
 
@@ -102,10 +96,8 @@ static int get_local_time(cdtime_t t, struct tm *t_tm, long *nsec) /* {{{ */
   NORMALIZE_TIMESPEC(t_spec);
 
   if (localtime_r(&t_spec.tv_sec, t_tm) == NULL) {
-    char errbuf[1024];
     int status = errno;
-    ERROR("get_local_time: localtime_r failed: %s",
-          sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("get_local_time: localtime_r failed: %s", STRERRNO);
     return status;
   }
 
@@ -152,9 +144,9 @@ static int format_zone(char *buffer, size_t buffer_size,
 } /* }}} int format_zone */
 
 int format_rfc3339(char *buffer, size_t buffer_size, struct tm const *t_tm,
-                   long nsec, _Bool print_nano, char const *zone) /* {{{ */
+                   long nsec, bool print_nano, char const *zone) /* {{{ */
 {
-  int len;
+  size_t len;
   char *pos = buffer;
   size_t size_left = buffer_size;
 
@@ -175,7 +167,7 @@ int format_rfc3339(char *buffer, size_t buffer_size, struct tm const *t_tm,
 } /* }}} int format_rfc3339 */
 
 int format_rfc3339_utc(char *buffer, size_t buffer_size, cdtime_t t,
-                       _Bool print_nano) /* {{{ */
+                       bool print_nano) /* {{{ */
 {
   struct tm t_tm;
   long nsec = 0;
@@ -189,7 +181,7 @@ int format_rfc3339_utc(char *buffer, size_t buffer_size, cdtime_t t,
 } /* }}} int format_rfc3339_utc */
 
 int format_rfc3339_local(char *buffer, size_t buffer_size, cdtime_t t,
-                         _Bool print_nano) /* {{{ */
+                         bool print_nano) /* {{{ */
 {
   struct tm t_tm;
   long nsec = 0;
index 62ef1dc..8466bd1 100644 (file)
--- a/src/dbi.c
+++ b/src/dbi.c
@@ -26,9 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_db_query.h"
+#include "utils/common/common.h"
+#include "utils/db_query/db_query.h"
 
 #include <dbi/dbi.h>
 
@@ -54,7 +54,7 @@ struct cdbi_driver_option_s /* {{{ */
     char *string;
     int numeric;
   } value;
-  _Bool is_numeric;
+  bool is_numeric;
 };
 typedef struct cdbi_driver_option_s cdbi_driver_option_t; /* }}} */
 
@@ -64,8 +64,6 @@ struct cdbi_database_s /* {{{ */
   char *select_db;
   char *plugin_name;
 
-  cdtime_t interval;
-
   char *driver;
   char *host;
   cdbi_driver_option_t *driver_options;
@@ -83,12 +81,12 @@ typedef struct cdbi_database_s cdbi_database_t; /* }}} */
  * Global variables
  */
 #if !defined(HAVE_LEGACY_LIBDBI) || !HAVE_LEGACY_LIBDBI
-static dbi_inst dbi_instance = 0;
+static dbi_inst dbi_instance;
 #endif
-static udb_query_t **queries = NULL;
-static size_t queries_num = 0;
-static cdbi_database_t **databases = NULL;
-static size_t databases_num = 0;
+static udb_query_t **queries;
+static size_t queries_num;
+static cdbi_database_t **databases;
+static size_t databases_num;
 
 static int cdbi_read_database(user_data_t *ud);
 
@@ -258,7 +256,7 @@ static int cdbi_config_add_database_driver_option(cdbi_database_t *db, /* {{{ */
   } else {
     assert(ci->values[1].type == OCONFIG_TYPE_NUMBER);
     option->value.numeric = (int)(ci->values[1].value.number + .5);
-    option->is_numeric = 1;
+    option->is_numeric = true;
   }
 
   db->driver_options_num++;
@@ -267,6 +265,7 @@ static int cdbi_config_add_database_driver_option(cdbi_database_t *db, /* {{{ */
 
 static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */
 {
+  cdtime_t interval = 0;
   cdbi_database_t *db;
   int status;
 
@@ -304,7 +303,7 @@ static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */
     else if (strcasecmp("Host", child->key) == 0)
       status = cf_util_get_string(child, &db->host);
     else if (strcasecmp("Interval", child->key) == 0)
-      status = cf_util_get_cdtime(child, &db->interval);
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Plugin", child->key) == 0)
       status = cf_util_get_string(child, &db->plugin_name);
     else {
@@ -370,7 +369,7 @@ static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */
           /* group = */ NULL,
           /* name = */ name ? name : db->name,
           /* callback = */ cdbi_read_database,
-          /* interval = */ (db->interval > 0) ? db->interval : 0,
+          /* interval = */ interval,
           &(user_data_t){
               .data = db,
           });
@@ -407,7 +406,7 @@ static int cdbi_config(oconfig_item_t *ci) /* {{{ */
 
 static int cdbi_init(void) /* {{{ */
 {
-  static int did_init = 0;
+  static int did_init;
   int status;
 
   if (did_init != 0)
@@ -497,8 +496,8 @@ static int cdbi_read_database_query(cdbi_database_t *db, /* {{{ */
     }
 
     column_num = (size_t)db_status;
-    DEBUG("cdbi_read_database_query (%s, %s): There are %zu columns.", db->name,
-          udb_query_get_name(q), column_num);
+    DEBUG("cdbi_read_database_query (%s, %s): There are %" PRIsz " columns.",
+          db->name, udb_query_get_name(q), column_num);
   }
 
   /* Allocate `column_names' and `column_values'. {{{ */
@@ -539,7 +538,7 @@ static int cdbi_read_database_query(cdbi_database_t *db, /* {{{ */
     column_name = dbi_result_get_field_name(res, (unsigned int)(i + 1));
     if (column_name == NULL) {
       ERROR("dbi plugin: cdbi_read_database_query (%s, %s): "
-            "Cannot retrieve name of field %zu.",
+            "Cannot retrieve name of field %" PRIsz ".",
             db->name, udb_query_get_name(q), i + 1);
       BAIL_OUT(-1);
     }
@@ -547,11 +546,16 @@ static int cdbi_read_database_query(cdbi_database_t *db, /* {{{ */
     sstrncpy(column_names[i], column_name, DATA_MAX_NAME_LEN);
   } /* }}} for (i = 0; i < column_num; i++) */
 
-  udb_query_prepare_result(
+  status = udb_query_prepare_result(
       q, prep_area, (db->host ? db->host : hostname_g),
       /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "dbi",
-      db->name, column_names, column_num,
-      /* interval = */ (db->interval > 0) ? db->interval : 0);
+      db->name, column_names, column_num);
+
+  if (status != 0) {
+    ERROR("dbi plugin: udb_query_prepare_result failed with status %i.",
+          status);
+    BAIL_OUT(-1);
+  }
 
   /* 0 = error; 1 = success; */
   status = dbi_result_first_row(res); /* {{{ */
@@ -579,7 +583,7 @@ static int cdbi_read_database_query(cdbi_database_t *db, /* {{{ */
 
       if (status != 0) {
         ERROR("dbi plugin: cdbi_read_database_query (%s, %s): "
-              "cdbi_result_get_field (%zu) failed.",
+              "cdbi_result_get_field (%" PRIsz ") failed.",
               db->name, udb_query_get_name(q), i + 1);
         status = -1;
         break;
@@ -599,15 +603,16 @@ static int cdbi_read_database_query(cdbi_database_t *db, /* {{{ */
     } /* }}} */
 
     /* Get the next row from the database. */
+    if (!dbi_result_has_next_row(res))
+      break;
+
     status = dbi_result_next_row(res); /* {{{ */
     if (status != 1) {
-      if (dbi_conn_error(db->connection, NULL) != 0) {
-        char errbuf[1024];
-        WARNING("dbi plugin: cdbi_read_database_query (%s, %s): "
-                "dbi_result_next_row failed: %s.",
-                db->name, udb_query_get_name(q),
-                cdbi_strerror(db->connection, errbuf, sizeof(errbuf)));
-      }
+      char errbuf[1024];
+      WARNING("dbi plugin: cdbi_read_database_query (%s, %s): "
+              "dbi_result_next_row failed: %s.",
+              db->name, udb_query_get_name(q),
+              cdbi_strerror(db->connection, errbuf, sizeof(errbuf)));
       break;
     } /* }}} */
   }   /* }}} while (42) */
index 688c322..5b3fbd2 100644 (file)
--- a/src/df.c
+++ b/src/df.c
 
 #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
@@ -51,14 +51,14 @@ static const char *config_keys[] = {
     "ReportByDevice", "ReportInodes", "ValuesAbsolute", "ValuesPercentage"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static ignorelist_t *il_device = NULL;
-static ignorelist_t *il_mountpoint = NULL;
-static ignorelist_t *il_fstype = NULL;
+static ignorelist_t *il_device;
+static ignorelist_t *il_mountpoint;
+static ignorelist_t *il_fstype;
 
-static _Bool by_device = 0;
-static _Bool report_inodes = 0;
-static _Bool values_absolute = 1;
-static _Bool values_percentage = 0;
+static bool by_device;
+static bool report_inodes;
+static bool values_absolute = true;
+static bool values_percentage;
 
 static int df_init(void) {
   if (il_device == NULL)
@@ -99,28 +99,28 @@ static int df_config(const char *key, const char *value) {
     return 0;
   } else if (strcasecmp(key, "ReportByDevice") == 0) {
     if (IS_TRUE(value))
-      by_device = 1;
+      by_device = true;
 
     return 0;
   } else if (strcasecmp(key, "ReportInodes") == 0) {
     if (IS_TRUE(value))
-      report_inodes = 1;
+      report_inodes = true;
     else
-      report_inodes = 0;
+      report_inodes = false;
 
     return 0;
   } else if (strcasecmp(key, "ValuesAbsolute") == 0) {
     if (IS_TRUE(value))
-      values_absolute = 1;
+      values_absolute = true;
     else
-      values_absolute = 0;
+      values_absolute = false;
 
     return 0;
   } else if (strcasecmp(key, "ValuesPercentage") == 0) {
     if (IS_TRUE(value))
-      values_percentage = 1;
+      values_percentage = true;
     else
-      values_percentage = 0;
+      values_percentage = false;
 
     return 0;
   }
@@ -203,9 +203,7 @@ static int df_read(void) {
       continue;
 
     if (STATANYFS(mnt_ptr->dir, &statbuf) < 0) {
-      char errbuf[1024];
-      ERROR(STATANYFS_STR "(%s) failed: %s", mnt_ptr->dir,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR(STATANYFS_STR "(%s) failed: %s", mnt_ptr->dir, STRERRNO);
       continue;
     }
 
@@ -227,12 +225,10 @@ static int df_read(void) {
       if (strcmp(mnt_ptr->dir, "/") == 0)
         sstrncpy(disk_name, "root", sizeof(disk_name));
       else {
-        int len;
-
         sstrncpy(disk_name, mnt_ptr->dir + 1, sizeof(disk_name));
-        len = strlen(disk_name);
+        size_t len = strlen(disk_name);
 
-        for (int i = 0; i < len; i++)
+        for (size_t i = 0; i < len; i++)
           if (disk_name[i] == '/')
             disk_name[i] = '-';
       }
index 2234c02..e73a5c0 100644 (file)
@@ -23,9 +23,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #if HAVE_MACH_MACH_TYPES_H
 #include <mach/mach_types.h>
@@ -82,7 +82,7 @@
 static mach_port_t io_master_port = MACH_PORT_NULL;
 /* This defaults to false for backwards compatibility. Please fix in the next
  * major version. */
-static _Bool use_bsd_name = 0;
+static bool use_bsd_name;
 /* #endif HAVE_IOKIT_IOKITLIB_H */
 
 #elif KERNEL_LINUX
@@ -106,9 +106,9 @@ typedef struct diskstats {
   derive_t avg_read_time;
   derive_t avg_write_time;
 
-  _Bool has_merged;
-  _Bool has_in_progress;
-  _Bool has_io_time;
+  bool has_merged;
+  bool has_in_progress;
+  bool has_io_time;
 
   struct diskstats *next;
 } diskstats_t;
@@ -126,7 +126,7 @@ static struct gmesh geom_tree;
 #define MAX_NUMDISK 1024
 extern kstat_ctl_t *kc;
 static kstat_t *ksp[MAX_NUMDISK];
-static int numdisk = 0;
+static int numdisk;
 /* #endif HAVE_LIBKSTAT */
 
 #elif defined(HAVE_LIBSTATGRAB)
@@ -145,7 +145,7 @@ static int pnumdisk;
 #if HAVE_LIBUDEV_H
 #include <libudev.h>
 
-static char *conf_udev_name_attr = NULL;
+static char *conf_udev_name_attr;
 static struct udev *handle_udev;
 #endif
 
@@ -153,7 +153,7 @@ static const char *config_keys[] = {"Disk", "UseBSDName", "IgnoreSelected",
                                     "UdevNameAttr"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static ignorelist_t *ignorelist = NULL;
+static ignorelist_t *ignorelist;
 
 static int disk_config(const char *key, const char *value) {
   if (ignorelist == NULL)
@@ -170,7 +170,7 @@ static int disk_config(const char *key, const char *value) {
     ignorelist_set_invert(ignorelist, invert);
   } else if (strcasecmp("UseBSDName", key) == 0) {
 #if HAVE_IOKIT_IOKITLIB_H
-    use_bsd_name = IS_TRUE(value) ? 1 : 0;
+    use_bsd_name = IS_TRUE(value);
 #else
     WARNING("disk plugin: The \"UseBSDName\" option is only supported "
             "on Mach / Mac OS X and will be ignored.");
@@ -303,9 +303,7 @@ static void submit_io_time(char const *plugin_instance, derive_t io_time,
 
   plugin_dispatch_values(&vl);
 } /* void submit_io_time */
-#endif /* KERNEL_FREEBSD || KERNEL_LINUX */
 
-#if KERNEL_LINUX
 static void submit_in_progress(char const *disk_name, gauge_t in_progress) {
   value_list_t vl = VALUE_LIST_INIT;
 
@@ -317,7 +315,9 @@ static void submit_in_progress(char const *disk_name, gauge_t in_progress) {
 
   plugin_dispatch_values(&vl);
 }
+#endif /* KERNEL_FREEBSD || KERNEL_LINUX */
 
+#if KERNEL_LINUX
 static counter_t disk_calc_time_incr(counter_t delta_time,
                                      counter_t delta_ops) {
   double interval = CDTIME_T_TO_DOUBLE(plugin_get_interval());
@@ -544,6 +544,7 @@ static int disk_read(void) {
 
   const char *disk_name;
   long double read_time, write_time, busy_time, total_duration;
+  uint64_t queue_length;
 
   for (retry = 0, dirty = 1; retry < 5 && dirty == 1; retry++) {
     if (snap != NULL)
@@ -646,10 +647,12 @@ static int disk_read(void) {
     }
     if (devstat_compute_statistics(snap_iter, NULL, 1.0, DSM_TOTAL_BUSY_TIME,
                                    &busy_time, DSM_TOTAL_DURATION,
-                                   &total_duration, DSM_NONE) != 0) {
+                                   &total_duration, DSM_QUEUE_LENGTH,
+                                   &queue_length, DSM_NONE) != 0) {
       WARNING("%s", devstat_errbuf);
     } else {
       submit_io_time(disk_name, busy_time, total_duration);
+      submit_in_progress(disk_name, (gauge_t)queue_length);
     }
   }
   geom_stats_snapshot_free(snap);
@@ -659,10 +662,7 @@ static int disk_read(void) {
   char buffer[1024];
 
   char *fields[32];
-  int numfields;
-  int fieldshift = 0;
-
-  int minor = 0;
+  static unsigned int poll_count = 0;
 
   derive_t read_sectors = 0;
   derive_t write_sectors = 0;
@@ -681,30 +681,19 @@ static int disk_read(void) {
   diskstats_t *ds, *pre_ds;
 
   if ((fh = fopen("/proc/diskstats", "r")) == NULL) {
-    fh = fopen("/proc/partitions", "r");
-    if (fh == NULL) {
-      ERROR("disk plugin: fopen (/proc/{diskstats,partitions}) failed.");
-      return -1;
-    }
-
-    /* Kernel is 2.4.* */
-    fieldshift = 1;
+    ERROR("disk plugin: fopen(\"/proc/diskstats\"): %s", STRERRNO);
+    return -1;
   }
 
+  poll_count++;
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
-    char *disk_name;
-    char *output_name;
-
-    numfields = strsplit(buffer, fields, 32);
+    int numfields = strsplit(buffer, fields, 32);
 
-    /* need either 7 fields (partition)
-     * or at least 14 fields (15 on Linux 2.4) */
-    if ((numfields != 7) && (numfields < (14 + fieldshift)))
+    /* need either 7 fields (partition) or at least 14 fields */
+    if ((numfields != 7) && (numfields < 14))
       continue;
 
-    minor = atoll(fields[1]);
-
-    disk_name = fields[2 + fieldshift];
+    char *disk_name = fields[2];
 
     for (ds = disklist, pre_ds = disklist; ds != NULL;
          pre_ds = ds, ds = ds->next)
@@ -712,7 +701,7 @@ static int disk_read(void) {
         break;
 
     if (ds == NULL) {
-      if ((ds = (diskstats_t *)calloc(1, sizeof(diskstats_t))) == NULL)
+      if ((ds = calloc(1, sizeof(*ds))) == NULL)
         continue;
 
       if ((ds->name = strdup(disk_name)) == NULL) {
@@ -734,25 +723,23 @@ static int disk_read(void) {
       write_ops = atoll(fields[5]);
       write_sectors = atoll(fields[6]);
     } else {
-      assert(numfields >= (14 + fieldshift));
-      read_ops = atoll(fields[3 + fieldshift]);
-      write_ops = atoll(fields[7 + fieldshift]);
+      assert(numfields >= 14);
+      read_ops = atoll(fields[3]);
+      write_ops = atoll(fields[7]);
 
-      read_sectors = atoll(fields[5 + fieldshift]);
-      write_sectors = atoll(fields[9 + fieldshift]);
+      read_sectors = atoll(fields[5]);
+      write_sectors = atoll(fields[9]);
 
-      if ((fieldshift == 0) || (minor == 0)) {
-        is_disk = 1;
-        read_merged = atoll(fields[4 + fieldshift]);
-        read_time = atoll(fields[6 + fieldshift]);
-        write_merged = atoll(fields[8 + fieldshift]);
-        write_time = atoll(fields[10 + fieldshift]);
+      is_disk = 1;
+      read_merged = atoll(fields[4]);
+      read_time = atoll(fields[6]);
+      write_merged = atoll(fields[8]);
+      write_time = atoll(fields[10]);
 
-        in_progress = atof(fields[11 + fieldshift]);
+      in_progress = atof(fields[11]);
 
-        io_time = atof(fields[12 + fieldshift]);
-        weighted_time = atof(fields[13 + fieldshift]);
-      }
+      io_time = atof(fields[12]);
+      weighted_time = atof(fields[13]);
     }
 
     {
@@ -817,24 +804,23 @@ static int disk_read(void) {
       ds->write_time = write_time;
 
       if (read_merged || write_merged)
-        ds->has_merged = 1;
+        ds->has_merged = true;
 
       if (in_progress)
-        ds->has_in_progress = 1;
+        ds->has_in_progress = true;
 
       if (io_time)
-        ds->has_io_time = 1;
+        ds->has_io_time = true;
 
     } /* if (is_disk) */
 
-    /* Don't write to the RRDs if we've just started.. */
-    ds->poll_count++;
-    if (ds->poll_count <= 2) {
-      DEBUG("disk plugin: (ds->poll_count = %i) <= "
-            "(min_poll_count = 2); => Not writing.",
-            ds->poll_count);
+    /* Skip first cycle for newly-added disk */
+    if (ds->poll_count == 0) {
+      DEBUG("disk plugin: (ds->poll_count = 0) => Skipping.");
+      ds->poll_count = poll_count;
       continue;
     }
+    ds->poll_count = poll_count;
 
     if ((read_ops == 0) && (write_ops == 0)) {
       DEBUG("disk plugin: ((read_ops == 0) && "
@@ -842,7 +828,7 @@ static int disk_read(void) {
       continue;
     }
 
-    output_name = disk_name;
+    char *output_name = disk_name;
 
 #if HAVE_LIBUDEV_H
     char *alt_name = NULL;
@@ -887,6 +873,28 @@ static int disk_read(void) {
 #endif
   } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
 
+  /* Remove disks that have disappeared from diskstats */
+  for (ds = disklist, pre_ds = disklist; ds != NULL;) {
+    /* Disk exists */
+    if (ds->poll_count == poll_count) {
+      pre_ds = ds;
+      ds = ds->next;
+      continue;
+    }
+
+    /* Disk is missing, remove it */
+    diskstats_t *missing_ds = ds;
+    if (ds == disklist) {
+      pre_ds = disklist = ds->next;
+    } else {
+      pre_ds->next = ds->next;
+    }
+    ds = ds->next;
+
+    DEBUG("disk plugin: Disk %s disappeared.", missing_ds->name);
+    free(missing_ds->name);
+    free(missing_ds);
+  }
   fclose(fh);
 /* #endif defined(KERNEL_LINUX) */
 
@@ -976,25 +984,20 @@ static int disk_read(void) {
   int rnumdisk;
 
   if ((numdisk = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0)) < 0) {
-    char errbuf[1024];
-    WARNING("disk plugin: perfstat_disk: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("disk plugin: perfstat_disk: %s", STRERRNO);
     return -1;
   }
 
   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;
 
   firstpath.name[0] = '\0';
   if ((rnumdisk = perfstat_disk(&firstpath, stat_disk, sizeof(perfstat_disk_t),
                                 numdisk)) < 0) {
-    char errbuf[1024];
-    WARNING("disk plugin: perfstat_disk : %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("disk plugin: perfstat_disk : %s", STRERRNO);
     return -1;
   }
 
index e64af0d..7fa297b 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
+#include "utils/dns/dns.h"
 #include <poll.h>
-#include "utils_dns.h"
 
 #include <pcap.h>
 
@@ -57,7 +57,7 @@ static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 static int select_numeric_qtype = 1;
 
 #define PCAP_SNAPLEN 1460
-static char *pcap_device = NULL;
+static char *pcap_device;
 
 static derive_t tr_queries;
 static derive_t tr_responses;
@@ -66,7 +66,7 @@ static counter_list_t *opcode_list;
 static counter_list_t *rcode_list;
 
 static pthread_t listen_thread;
-static int listen_thread_init = 0;
+static int listen_thread_init;
 /* The `traffic' mutex if for `tr_queries' and `tr_responses' */
 static pthread_mutex_t traffic_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_mutex_t qtype_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -287,9 +287,7 @@ static int dns_init(void) {
   status = plugin_thread_create(&listen_thread, NULL, dns_child_loop, (void *)0,
                                 "dns listen");
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("dns plugin: pthread_create failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("dns plugin: pthread_create failed: %s", STRERRNO);
     return -1;
   }
 
index 32d3d6a..4cdf01d 100644 (file)
 
 #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>
@@ -66,19 +66,19 @@ typedef struct dpdk_ka_monitor_s {
 
 typedef struct dpdk_link_status_config_s {
   int enabled;
-  _Bool send_updated;
+  bool send_updated;
   uint32_t enabled_port_mask;
   char port_name[RTE_MAX_ETHPORTS][DATA_MAX_NAME_LEN];
-  _Bool notify;
+  bool notify;
 } dpdk_link_status_config_t;
 
 typedef struct dpdk_keep_alive_config_s {
   int enabled;
-  _Bool send_updated;
+  bool send_updated;
   uint128_t lcore_mask;
   dpdk_keepalive_shm_t *shm;
   char shm_name[DATA_MAX_NAME_LEN];
-  _Bool notify;
+  bool notify;
   int fd;
 } dpdk_keep_alive_config_t;
 
@@ -129,12 +129,11 @@ static int dpdk_event_keep_alive_shm_open(void) {
             shm_name);
   }
 
-  char errbuf[ERR_BUF_SIZE];
   int fd = shm_open(shm_name, O_RDONLY, 0);
   if (fd < 0) {
     ERROR(DPDK_EVENTS_PLUGIN ": Failed to open %s as SHM:%s. Is DPDK KA "
                              "primary application running?",
-          shm_name, sstrerror(errno, errbuf, sizeof(errbuf)));
+          shm_name, STRERRNO);
     return errno;
   }
 
@@ -166,8 +165,7 @@ static int dpdk_event_keep_alive_shm_open(void) {
   ec->config.keep_alive.shm = (dpdk_keepalive_shm_t *)mmap(
       0, sizeof(*(ec->config.keep_alive.shm)), PROT_READ, MAP_SHARED, fd, 0);
   if (ec->config.keep_alive.shm == MAP_FAILED) {
-    ERROR(DPDK_EVENTS_PLUGIN ": Failed to mmap KA SHM:%s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR(DPDK_EVENTS_PLUGIN ": Failed to mmap KA SHM:%s", STRERRNO);
     close(fd);
     return errno;
   }
@@ -187,8 +185,8 @@ static void dpdk_events_default_config(void) {
   /* Link Status */
   ec->config.link_status.enabled = 1;
   ec->config.link_status.enabled_port_mask = ~0;
-  ec->config.link_status.send_updated = 1;
-  ec->config.link_status.notify = 0;
+  ec->config.link_status.send_updated = true;
+  ec->config.link_status.notify = false;
 
   for (int i = 0; i < RTE_MAX_ETHPORTS; i++) {
     ec->config.link_status.port_name[i][0] = 0;
@@ -196,8 +194,8 @@ static void dpdk_events_default_config(void) {
 
   /* Keep Alive */
   ec->config.keep_alive.enabled = 1;
-  ec->config.keep_alive.send_updated = 1;
-  ec->config.keep_alive.notify = 0;
+  ec->config.keep_alive.send_updated = true;
+  ec->config.keep_alive.notify = false;
   /* by default enable 128 cores */
   memset(&ec->config.keep_alive.lcore_mask, 1,
          sizeof(ec->config.keep_alive.lcore_mask));
@@ -421,8 +419,12 @@ static int dpdk_events_config(oconfig_item_t *ci) {
 static int dpdk_helper_link_status_get(dpdk_helper_ctx_t *phc) {
   dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(phc);
 
-  /* get Link Status values from DPDK */
+/* get Link Status values from DPDK */
+#if RTE_VERSION < RTE_VERSION_NUM(18, 05, 0, 0)
   uint8_t nb_ports = rte_eth_dev_count();
+#else
+  uint8_t nb_ports = rte_eth_dev_count_avail();
+#endif
   if (nb_ports == 0) {
     DPDK_CHILD_LOG("dpdkevent-helper: No DPDK ports available. "
                    "Check bound devices to DPDK driver.\n");
@@ -430,7 +432,7 @@ static int dpdk_helper_link_status_get(dpdk_helper_ctx_t *phc) {
   }
   ec->nb_ports = nb_ports > RTE_MAX_ETHPORTS ? RTE_MAX_ETHPORTS : nb_ports;
 
-  for (int i = 0; i < ec->nb_ports; i++) {
+  for (unsigned int i = 0; i < ec->nb_ports; i++) {
     if (ec->config.link_status.enabled_port_mask & (1 << i)) {
       struct rte_eth_link link;
       ec->link_info[i].read_time = cdtime();
@@ -499,7 +501,7 @@ static int dpdk_events_link_status_dispatch(dpdk_helper_ctx_t *phc) {
         ec->nb_ports);
 
   /* dispatch Link Status values to collectd */
-  for (int i = 0; i < ec->nb_ports; i++) {
+  for (unsigned int i = 0; i < ec->nb_ports; i++) {
     if (ec->config.link_status.enabled_port_mask & (1 << i)) {
       if (!ec->config.link_status.send_updated ||
           ec->link_info[i].status_updated) {
index c95ba0c..0005d09 100644 (file)
@@ -32,8 +32,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
-#include "utils_dpdk.h"
+#include "utils/common/common.h"
+#include "utils/dpdk/dpdk.h"
 
 #include <rte_config.h>
 #include <rte_ethdev.h>
@@ -125,9 +125,8 @@ static int dpdk_stats_preinit(void) {
 
   int ret = dpdk_helper_init(g_shm_name, sizeof(dpdk_stats_ctx_t), &g_hc);
   if (ret != 0) {
-    char errbuf[ERR_BUF_SIZE];
     ERROR("%s: failed to initialize %s helper(error: %s)", DPDK_STATS_PLUGIN,
-          g_shm_name, sstrerror(errno, errbuf, sizeof(errbuf)));
+          g_shm_name, STRERRNO);
     return ret;
   }
 
@@ -430,7 +429,7 @@ static int dpdk_stats_reinit_helper() {
   size_t data_size = sizeof(dpdk_stats_ctx_t) +
                      (ctx->stats_count * DPDK_STATS_CTX_GET_XSTAT_SIZE);
 
-  DEBUG("%s:%d helper reinit (new_size=%zu)", __FUNCTION__, __LINE__,
+  DEBUG("%s:%d helper reinit (new_size=%" PRIsz ")", __FUNCTION__, __LINE__,
         data_size);
 
   dpdk_stats_ctx_t tmp_ctx;
@@ -446,9 +445,8 @@ static int dpdk_stats_reinit_helper() {
   int ret;
   ret = dpdk_helper_init(g_shm_name, data_size, &g_hc);
   if (ret != 0) {
-    char errbuf[ERR_BUF_SIZE];
     ERROR("%s: failed to initialize %s helper(error: %s)", DPDK_STATS_PLUGIN,
-          g_shm_name, sstrerror(errno, errbuf, sizeof(errbuf)));
+          g_shm_name, STRERRNO);
     return ret;
   }
 
index 5a0eac3..0f54dd5 100644 (file)
@@ -36,8 +36,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 static const char *drbd_stats = "/proc/drbd";
 static const char *drbd_names[] = {
@@ -72,7 +72,7 @@ static int drbd_submit_fields(long int resource, char **fields,
 
   if (fields_num != drbd_names_num) {
     WARNING("drbd plugin: Wrong number of fields for "
-            "r%ld statistics. Expected %zu, got %zu.",
+            "r%ld statistics. Expected %" PRIsz ", got %" PRIsz ".",
             resource, drbd_names_num, fields_num);
     return EINVAL;
   }
index e1ce218..deb6600 100644 (file)
@@ -40,8 +40,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <stddef.h>
 
@@ -111,13 +111,13 @@ static const char *config_keys[] = {"SocketFile", "SocketGroup", "SocketPerms",
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
 /* socket configuration */
-static char *sock_file = NULL;
-static char *sock_group = NULL;
+static char *sock_file;
+static char *sock_group;
 static int sock_perms = S_IRWXU | S_IRWXG;
 static int max_conns = MAX_CONNS;
 
 /* state of the plugin */
-static int disabled = 0;
+static int disabled;
 
 /* thread managing "client" connections */
 static pthread_t connector = (pthread_t)0;
@@ -134,7 +134,7 @@ static conn_list_t conns;
 static pthread_cond_t collector_available = PTHREAD_COND_INITIALIZER;
 
 /* collector threads */
-static collector_t **collectors = NULL;
+static collector_t **collectors;
 
 static pthread_mutex_t available_mutex = PTHREAD_MUTEX_INITIALIZER;
 static int available_collectors;
@@ -260,23 +260,20 @@ static void *collect(void *arg) {
     while (42) {
       /* 256 bytes ought to be enough for anybody ;-) */
       char line[256 + 1]; /* line + '\0' */
-      int len = 0;
 
       errno = 0;
       if (fgets(line, sizeof(line), this->socket) == NULL) {
         if (errno != 0) {
-          char errbuf[1024];
           log_err("collect: reading from socket (fd #%i) "
                   "failed: %s",
-                  fileno(this->socket),
-                  sstrerror(errno, errbuf, sizeof(errbuf)));
+                  fileno(this->socket), STRERRNO);
         }
         break;
       }
 
-      len = strlen(line);
+      size_t len = strlen(line);
       if ((line[len - 1] != '\n') && (line[len - 1] != '\r')) {
-        log_warn("collect: line too long (> %zu characters): "
+        log_warn("collect: line too long (> %" PRIsz " characters): "
                  "'%s' (truncated)",
                  sizeof(line) - 1, line);
 
@@ -289,7 +286,7 @@ static void *collect(void *arg) {
         continue;
       }
 
-      line[len - 1] = 0;
+      line[len - 1] = '\0';
 
       log_debug("collect: line = '%s'", line);
 
@@ -366,9 +363,8 @@ static void *open_connection(void __attribute__((unused)) * arg) {
   /* create UNIX socket */
   errno = 0;
   if ((connector_socket = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
-    char errbuf[1024];
     disabled = 1;
-    log_err("socket() failed: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_err("socket() failed: %s", STRERRNO);
     pthread_exit((void *)1);
   }
 
@@ -381,21 +377,19 @@ static void *open_connection(void __attribute__((unused)) * arg) {
   if (bind(connector_socket, (struct sockaddr *)&addr,
            offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path)) ==
       -1) {
-    char errbuf[1024];
     disabled = 1;
     close(connector_socket);
     connector_socket = -1;
-    log_err("bind() failed: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_err("bind() failed: %s", STRERRNO);
     pthread_exit((void *)1);
   }
 
   errno = 0;
   if (listen(connector_socket, 5) == -1) {
-    char errbuf[1024];
     disabled = 1;
     close(connector_socket);
     connector_socket = -1;
-    log_err("listen() failed: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_err("listen() failed: %s", STRERRNO);
     pthread_exit((void *)1);
   }
 
@@ -414,25 +408,21 @@ static void *open_connection(void __attribute__((unused)) * arg) {
     grp = NULL;
     status = getgrnam_r(group, &sg, grbuf, sizeof(grbuf), &grp);
     if (status != 0) {
-      char errbuf[1024];
-      log_warn("getgrnam_r (%s) failed: %s", group,
-               sstrerror(status, errbuf, sizeof(errbuf)));
+      log_warn("getgrnam_r (%s) failed: %s", group, STRERROR(status));
     } else if (grp == NULL) {
       log_warn("No such group: `%s'", group);
     } else {
       status = chown(path, (uid_t)-1, grp->gr_gid);
       if (status != 0) {
-        char errbuf[1024];
         log_warn("chown (%s, -1, %i) failed: %s", path, (int)grp->gr_gid,
-                 sstrerror(errno, errbuf, sizeof(errbuf)));
+                 STRERRNO);
       }
     }
   }
 
   errno = 0;
   if (chmod(path, sock_perms) != 0) {
-    char errbuf[1024];
-    log_warn("chmod() failed: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_warn("chmod() failed: %s", STRERRNO);
   }
 
   { /* initialize collector threads */
@@ -454,9 +444,7 @@ static void *open_connection(void __attribute__((unused)) * arg) {
 
       if (plugin_thread_create(&collectors[i]->thread, &ptattr, collect,
                                collectors[i], "email collector") != 0) {
-        char errbuf[1024];
-        log_err("plugin_thread_create() failed: %s",
-                sstrerror(errno, errbuf, sizeof(errbuf)));
+        log_err("plugin_thread_create() failed: %s", STRERRNO);
         collectors[i]->thread = (pthread_t)0;
       }
     }
@@ -484,16 +472,13 @@ static void *open_connection(void __attribute__((unused)) * arg) {
 
       remote = accept(connector_socket, NULL, NULL);
       if (remote == -1) {
-        char errbuf[1024];
-
         if (errno == EINTR)
           continue;
 
         disabled = 1;
         close(connector_socket);
         connector_socket = -1;
-        log_err("accept() failed: %s",
-                sstrerror(errno, errbuf, sizeof(errbuf)));
+        log_err("accept() failed: %s", STRERRNO);
         pthread_exit((void *)1);
       }
 
@@ -538,10 +523,8 @@ static void *open_connection(void __attribute__((unused)) * arg) {
 static int email_init(void) {
   if (plugin_thread_create(&connector, NULL, open_connection, NULL,
                            "email listener") != 0) {
-    char errbuf[1024];
     disabled = 1;
-    log_err("plugin_thread_create() failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_err("plugin_thread_create() failed: %s", STRERRNO);
     return -1;
   }
 
index c7b5b3f..94a291e 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if !KERNEL_LINUX
 #error "No applicable input method."
index 3c03e36..f8bc5b5 100644 (file)
@@ -24,9 +24,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
 #include "utils_complain.h"
 
 #if HAVE_SYS_IOCTL_H
@@ -48,12 +48,12 @@ struct value_map_s {
 };
 typedef struct value_map_s value_map_t;
 
-static char **interfaces = NULL;
-static size_t interfaces_num = 0;
+static char **interfaces;
+static size_t interfaces_num;
 
-static c_avl_tree_t *value_map = NULL;
+static c_avl_tree_t *value_map;
 
-static _Bool collect_mapped_only = 0;
+static bool collect_mapped_only;
 
 static int ethstat_add_interface(const oconfig_item_t *ci) /* {{{ */
 {
@@ -204,9 +204,7 @@ static int ethstat_read_interface(char *device) {
 
   fd = socket(AF_INET, SOCK_DGRAM, /* protocol = */ 0);
   if (fd < 0) {
-    char errbuf[1024];
-    ERROR("ethstat plugin: Failed to open control socket: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("ethstat plugin: Failed to open control socket: %s", STRERRNO);
     return 1;
   }
 
@@ -218,11 +216,10 @@ static int ethstat_read_interface(char *device) {
 
   status = ioctl(fd, SIOCETHTOOL, &req);
   if (status < 0) {
-    char errbuf[1024];
     close(fd);
     ERROR("ethstat plugin: Failed to get driver information "
           "from %s: %s",
-          device, sstrerror(errno, errbuf, sizeof(errbuf)));
+          device, STRERRNO);
     return -1;
   }
 
@@ -252,12 +249,10 @@ static int ethstat_read_interface(char *device) {
   req.ifr_data = (void *)strings;
   status = ioctl(fd, SIOCETHTOOL, &req);
   if (status < 0) {
-    char errbuf[1024];
     close(fd);
     free(strings);
     free(stats);
-    ERROR("ethstat plugin: Cannot get strings from %s: %s", device,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("ethstat plugin: Cannot get strings from %s: %s", device, STRERRNO);
     return -1;
   }
 
@@ -266,12 +261,11 @@ static int ethstat_read_interface(char *device) {
   req.ifr_data = (void *)stats;
   status = ioctl(fd, SIOCETHTOOL, &req);
   if (status < 0) {
-    char errbuf[1024];
     close(fd);
     free(strings);
     free(stats);
     ERROR("ethstat plugin: Reading statistics from %s failed: %s", device,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return -1;
   }
 
index a38026e..7e16167 100644 (file)
 
 #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>
@@ -85,7 +85,7 @@ const long int MAX_GRBUF_SIZE = 65536;
 /*
  * Private variables
  */
-static program_list_t *pl_head = NULL;
+static program_list_t *pl_head;
 static pthread_mutex_t pl_lock = PTHREAD_MUTEX_INITIALIZER;
 
 /*
@@ -290,7 +290,6 @@ __attribute__((noreturn)) static void exec_child(program_list_t *pl, int uid,
                                                  int gid, int egid) /* {{{ */
 {
   int status;
-  char errbuf[1024];
 
 #if HAVE_SETGROUPS
   if (getuid() == 0) {
@@ -311,31 +310,27 @@ __attribute__((noreturn)) static void exec_child(program_list_t *pl, int uid,
 
   status = setgid(gid);
   if (status != 0) {
-    ERROR("exec plugin: setgid (%i) failed: %s", gid,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("exec plugin: setgid (%i) failed: %s", gid, STRERRNO);
     exit(-1);
   }
 
   if (egid != -1) {
     status = setegid(egid);
     if (status != 0) {
-      ERROR("exec plugin: setegid (%i) failed: %s", egid,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("exec plugin: setegid (%i) failed: %s", egid, STRERRNO);
       exit(-1);
     }
   }
 
   status = setuid(uid);
   if (status != 0) {
-    ERROR("exec plugin: setuid (%i) failed: %s", uid,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("exec plugin: setuid (%i) failed: %s", uid, STRERRNO);
     exit(-1);
   }
 
   execvp(pl->exec, pl->argv);
 
-  ERROR("exec plugin: Failed to execute ``%s'': %s", pl->exec,
-        sstrerror(errno, errbuf, sizeof(errbuf)));
+  ERROR("exec plugin: Failed to execute ``%s'': %s", pl->exec, STRERRNO);
   exit(-1);
 } /* void exec_child }}} */
 
@@ -349,13 +344,11 @@ static void reset_signal_mask(void) /* {{{ */
 
 static int create_pipe(int fd_pipe[2]) /* {{{ */
 {
-  char errbuf[1024];
   int status;
 
   status = pipe(fd_pipe);
   if (status != 0) {
-    ERROR("exec plugin: pipe failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("exec plugin: pipe failed: %s", STRERRNO);
     return -1;
   }
 
@@ -420,9 +413,7 @@ static int getegr_id(program_list_t *pl, int gid) /* {{{ */
     } else if (errno == ERANGE) {
       grbuf_size += grbuf_size; // increment buffer size and try again
     } else {
-      char errbuf[1024];
-      ERROR("exec plugin: getegr_id failed %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("exec plugin: getegr_id failed %s", STRERRNO);
       sfree(grbuf);
       return -2;
     }
@@ -430,7 +421,7 @@ static int getegr_id(program_list_t *pl, int gid) /* {{{ */
   ERROR("exec plugin: getegr_id Max grbuf size reached  for %s", pl->group);
   sfree(grbuf);
   return -2;
-} /* }}} */
+}
 
 /*
  * Creates three pipes (one for reading, one for writing and one for errors),
@@ -444,7 +435,6 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
   int fd_pipe_in[2] = {-1, -1};
   int fd_pipe_out[2] = {-1, -1};
   int fd_pipe_err[2] = {-1, -1};
-  char errbuf[1024];
   int status;
   int pid;
 
@@ -473,7 +463,7 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
   status = getpwnam_r(pl->user, &sp, nambuf, sizeof(nambuf), &sp_ptr);
   if (status != 0) {
     ERROR("exec plugin: Failed to get user information for user ``%s'': %s",
-          pl->user, sstrerror(status, errbuf, sizeof(errbuf)));
+          pl->user, STRERROR(status));
     goto failed;
   }
 
@@ -489,6 +479,8 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
     goto failed;
   }
 
+  /* The group configured in the configfile is set as effective group, because
+   * this way the forked process can (re-)gain the user's primary group. */
   egid = getegr_id(pl, gid);
   if (egid == -2) {
     goto failed;
@@ -498,8 +490,7 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
 
   pid = fork();
   if (pid < 0) {
-    ERROR("exec plugin: fork failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("exec plugin: fork failed: %s", STRERRNO);
     goto failed;
   } else if (pid == 0) {
     int fd_num;
@@ -750,9 +741,7 @@ static void *exec_notification_one(void *arg) /* {{{ */
 
   fh = fdopen(fd, "w");
   if (fh == NULL) {
-    char errbuf[1024];
-    ERROR("exec plugin: fdopen (%i) failed: %s", fd,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("exec plugin: fdopen (%i) failed: %s", fd, STRERRNO);
     kill(pid, SIGTERM);
     close(fd);
     sfree(arg);
index 3611530..93ad903 100644 (file)
 
 #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);
 
-static _Bool values_absolute = 1;
-static _Bool values_percentage = 0;
+static bool values_absolute = true;
+static bool values_percentage;
 
 static int fhcount_config(const char *key, const char *value) {
   int ret = -1;
 
   if (strcasecmp(key, "ValuesAbsolute") == 0) {
     if (IS_TRUE(value)) {
-      values_absolute = 1;
+      values_absolute = true;
     } else {
-      values_absolute = 0;
+      values_absolute = false;
     }
 
     ret = 0;
   } else if (strcasecmp(key, "ValuesPercentage") == 0) {
     if (IS_TRUE(value)) {
-      values_percentage = 1;
+      values_percentage = true;
     } else {
-      values_percentage = 0;
+      values_percentage = false;
     }
 
     ret = 0;
@@ -75,17 +75,16 @@ static int fhcount_read(void) {
   int prc_used, prc_unused;
   char *fields[3];
   char buffer[buffer_len];
-  char errbuf[1024];
   FILE *fp;
 
   // Open file
   fp = fopen("/proc/sys/fs/file-nr", "r");
   if (fp == NULL) {
-    ERROR("fhcount: fopen: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("fhcount: fopen: %s", STRERRNO);
     return EXIT_FAILURE;
   }
   if (fgets(buffer, buffer_len, fp) == NULL) {
-    ERROR("fhcount: fgets: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("fhcount: fgets: %s", STRERRNO);
     fclose(fp);
     return EXIT_FAILURE;
   }
index 7842aa6..5acd47b 100644 (file)
@@ -23,8 +23,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <dirent.h>
 #include <fcntl.h>
@@ -60,10 +60,10 @@ struct fc_directory_conf_s {
 };
 typedef struct fc_directory_conf_s fc_directory_conf_t;
 
-static fc_directory_conf_t **directories = NULL;
-static size_t directories_num = 0;
+static fc_directory_conf_t **directories;
+static size_t directories_num;
 
-void fc_free_dir(fc_directory_conf_t *dir) {
+static void fc_free_dir(fc_directory_conf_t *dir) {
   sfree(dir->path);
   sfree(dir->plugin_name);
   sfree(dir->instance);
@@ -154,26 +154,6 @@ static int fc_config_add_dir_instance(fc_directory_conf_t *dir,
   return fc_config_set_instance(dir, ci->values[0].value.string);
 } /* int fc_config_add_dir_instance */
 
-static int fc_config_add_dir_name(fc_directory_conf_t *dir,
-                                  oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("filecount plugin: The `Name' config option needs exactly one "
-            "string argument.");
-    return -1;
-  }
-
-  char *temp = strdup(ci->values[0].value.string);
-  if (temp == NULL) {
-    ERROR("filecount plugin: strdup failed.");
-    return -1;
-  }
-
-  sfree(dir->name);
-  dir->name = temp;
-
-  return 0;
-} /* int fc_config_add_dir_name */
-
 static int fc_config_add_dir_mtime(fc_directory_conf_t *dir,
                                    oconfig_item_t *ci) {
   if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_STRING) &&
@@ -369,7 +349,7 @@ static int fc_config_add_dir(oconfig_item_t *ci) {
     else if (strcasecmp("Instance", option->key) == 0)
       status = fc_config_add_dir_instance(dir, option);
     else if (strcasecmp("Name", option->key) == 0)
-      status = fc_config_add_dir_name(dir, option);
+      status = cf_util_get_string(option, &dir->name);
     else if (strcasecmp("MTime", option->key) == 0)
       status = fc_config_add_dir_mtime(dir, option);
     else if (strcasecmp("Size", option->key) == 0)
index dd36b8b..e875da8 100644 (file)
 
 #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"
index 50bd832..b14dee3 100644 (file)
@@ -26,9 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
 
 #if HAVE_NETDB_H
 #include <netdb.h>
@@ -84,19 +84,19 @@ struct metric_map_s {
 typedef struct metric_map_s metric_map_t;
 
 #define MC_RECEIVE_GROUP_DEFAULT "239.2.11.71"
-static char *mc_receive_group = NULL;
+static char *mc_receive_group;
 #define MC_RECEIVE_PORT_DEFAULT "8649"
-static char *mc_receive_port = NULL;
+static char *mc_receive_port;
 
-static struct pollfd *mc_receive_sockets = NULL;
-static size_t mc_receive_sockets_num = 0;
+static struct pollfd *mc_receive_sockets;
+static size_t mc_receive_sockets_num;
 
-static socket_entry_t *mc_send_sockets = NULL;
-static size_t mc_send_sockets_num = 0;
+static socket_entry_t *mc_send_sockets;
+static size_t mc_send_sockets_num;
 static pthread_mutex_t mc_send_sockets_lock = PTHREAD_MUTEX_INITIALIZER;
 
-static int mc_receive_thread_loop = 0;
-static int mc_receive_thread_running = 0;
+static int mc_receive_thread_loop;
+static int mc_receive_thread_running;
 static pthread_t mc_receive_thread_id;
 
 static metric_map_t metric_map_default[] =
@@ -122,8 +122,8 @@ static metric_map_t metric_map_default[] =
      {"pkts_out", "if_packets", "", "tx", -1, -1}};
 static size_t metric_map_len_default = STATIC_ARRAY_SIZE(metric_map_default);
 
-static metric_map_t *metric_map = NULL;
-static size_t metric_map_len = 0;
+static metric_map_t *metric_map;
+static size_t metric_map_len;
 
 static c_avl_tree_t *staging_tree;
 static pthread_mutex_t staging_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -217,12 +217,10 @@ static int create_sockets(socket_entry_t **ret_sockets, /* {{{ */
 
   ai_return = getaddrinfo(node, service, &ai_hints, &ai_list);
   if (ai_return != 0) {
-    char errbuf[1024];
     ERROR("gmond plugin: getaddrinfo (%s, %s) failed: %s",
           (node == NULL) ? "(null)" : node,
           (service == NULL) ? "(null)" : service,
-          (ai_return == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                    : gai_strerror(ai_return));
+          (ai_return == EAI_SYSTEM) ? STRERRNO : gai_strerror(ai_return));
     return -1;
   }
 
@@ -241,9 +239,7 @@ static int create_sockets(socket_entry_t **ret_sockets, /* {{{ */
     sockets[sockets_num].fd =
         socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (sockets[sockets_num].fd < 0) {
-      char errbuf[1024];
-      ERROR("gmond plugin: socket failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("gmond plugin: socket failed: %s", STRERRNO);
       continue;
     }
 
@@ -256,22 +252,16 @@ static int create_sockets(socket_entry_t **ret_sockets, /* {{{ */
       sockets_num++;
       break;
     } else {
-      int yes = 1;
-
       status = setsockopt(sockets[sockets_num].fd, SOL_SOCKET, SO_REUSEADDR,
-                          (void *)&yes, sizeof(yes));
+                          &(int){1}, sizeof(int));
       if (status != 0) {
-        char errbuf[1024];
-        WARNING("gmond plugin: setsockopt(2) failed: %s",
-                sstrerror(errno, errbuf, sizeof(errbuf)));
+        WARNING("gmond plugin: setsockopt(2) failed: %s", STRERRNO);
       }
     }
 
     status = bind(sockets[sockets_num].fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
     if (status != 0) {
-      char errbuf[1024];
-      ERROR("gmond plugin: bind failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("gmond plugin: bind failed: %s", STRERRNO);
       close(sockets[sockets_num].fd);
       continue;
     }
@@ -291,9 +281,7 @@ static int create_sockets(socket_entry_t **ret_sockets, /* {{{ */
       status = setsockopt(sockets[sockets_num].fd, IPPROTO_IP,
                           IP_MULTICAST_LOOP, (void *)&loop, sizeof(loop));
       if (status != 0) {
-        char errbuf[1024];
-        WARNING("gmond plugin: setsockopt(2) failed: %s",
-                sstrerror(errno, errbuf, sizeof(errbuf)));
+        WARNING("gmond plugin: setsockopt(2) failed: %s", STRERRNO);
       }
 
       struct ip_mreq mreq = {.imr_multiaddr.s_addr = addr->sin_addr.s_addr,
@@ -302,9 +290,7 @@ static int create_sockets(socket_entry_t **ret_sockets, /* {{{ */
       status = setsockopt(sockets[sockets_num].fd, IPPROTO_IP,
                           IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq));
       if (status != 0) {
-        char errbuf[1024];
-        WARNING("gmond plugin: setsockopt(2) failed: %s",
-                sstrerror(errno, errbuf, sizeof(errbuf)));
+        WARNING("gmond plugin: setsockopt(2) failed: %s", STRERRNO);
       }
     } /* if (ai_ptr->ai_family == AF_INET) */
     else if (ai_ptr->ai_family == AF_INET6) {
@@ -322,9 +308,7 @@ static int create_sockets(socket_entry_t **ret_sockets, /* {{{ */
       status = setsockopt(sockets[sockets_num].fd, IPPROTO_IPV6,
                           IPV6_MULTICAST_LOOP, (void *)&loop, sizeof(loop));
       if (status != 0) {
-        char errbuf[1024];
-        WARNING("gmond plugin: setsockopt(2) failed: %s",
-                sstrerror(errno, errbuf, sizeof(errbuf)));
+        WARNING("gmond plugin: setsockopt(2) failed: %s", STRERRNO);
       }
 
       struct ipv6_mreq mreq = {
@@ -335,9 +319,7 @@ static int create_sockets(socket_entry_t **ret_sockets, /* {{{ */
       status = setsockopt(sockets[sockets_num].fd, IPPROTO_IPV6,
                           IPV6_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq));
       if (status != 0) {
-        char errbuf[1024];
-        WARNING("gmond plugin: setsockopt(2) failed: %s",
-                sstrerror(errno, errbuf, sizeof(errbuf)));
+        WARNING("gmond plugin: setsockopt(2) failed: %s", STRERRNO);
       }
     } /* if (ai_ptr->ai_family == AF_INET6) */
 
@@ -393,9 +375,7 @@ static int request_meta_data(const char *host, const char *name) /* {{{ */
                /* flags = */ 0, (struct sockaddr *)&mc_send_sockets[i].addr,
                mc_send_sockets[i].addrlen);
     if (status == -1) {
-      char errbuf[1024];
-      ERROR("gmond plugin: sendto(2) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("gmond plugin: sendto(2) failed: %s", STRERRNO);
       continue;
     }
   }
@@ -433,7 +413,7 @@ static staging_entry_t *staging_entry_get(const char *host, /* {{{ */
   sstrncpy(se->key, key, sizeof(se->key));
   se->flags = 0;
 
-  se->vl.values = (value_t *)calloc(values_len, sizeof(*se->vl.values));
+  se->vl.values = calloc(values_len, sizeof(*se->vl.values));
   if (se->vl.values == NULL) {
     sfree(se);
     return NULL;
@@ -472,7 +452,8 @@ static int staging_entry_update(const char *host, const char *name, /* {{{ */
   }
 
   if (ds->ds_num <= ds_index) {
-    ERROR("gmond plugin: Invalid index %zu: %s has only %zu data source(s).",
+    ERROR("gmond plugin: Invalid index %" PRIsz ": %s has only %" PRIsz
+          " data source(s).",
           ds_index, ds->type, ds->ds_num);
     return -1;
   }
@@ -737,9 +718,7 @@ static int mc_handle_socket(struct pollfd *p) /* {{{ */
 
   buffer_size = recv(p->fd, buffer, sizeof(buffer), /* flags = */ 0);
   if (buffer_size <= 0) {
-    char errbuf[1024];
-    ERROR("gmond plugin: recv failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("gmond plugin: recv failed: %s", STRERRNO);
     p->revents = 0;
     return -1;
   }
@@ -764,8 +743,8 @@ static void *mc_receive_thread(void *arg) /* {{{ */
     return (void *)-1;
   }
 
-  mc_receive_sockets = (struct pollfd *)calloc(mc_receive_sockets_num,
-                                               sizeof(*mc_receive_sockets));
+  mc_receive_sockets =
+      calloc(mc_receive_sockets_num, sizeof(*mc_receive_sockets));
   if (mc_receive_sockets == NULL) {
     ERROR("gmond plugin: calloc failed.");
     for (size_t i = 0; i < mc_receive_sockets_num; i++)
@@ -785,11 +764,9 @@ static void *mc_receive_thread(void *arg) /* {{{ */
   while (mc_receive_thread_loop != 0) {
     status = poll(mc_receive_sockets, mc_receive_sockets_num, -1);
     if (status <= 0) {
-      char errbuf[1024];
       if (errno == EINTR)
         continue;
-      ERROR("gmond plugin: poll failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("gmond plugin: poll failed: %s", STRERRNO);
       break;
     }
 
@@ -854,28 +831,6 @@ static int mc_receive_thread_stop(void) /* {{{ */
  *   </Metric>
  * </Plugin>
  */
-static int gmond_config_set_string(oconfig_item_t *ci, char **str) /* {{{ */
-{
-  char *tmp;
-
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("gmond plugin: The `%s' option needs "
-            "exactly one string argument.",
-            ci->key);
-    return -1;
-  }
-
-  tmp = strdup(ci->values[0].value.string);
-  if (tmp == NULL) {
-    ERROR("gmond plugin: strdup failed.");
-    return -1;
-  }
-
-  sfree(*str);
-  *str = tmp;
-  return 0;
-} /* }}} int gmond_config_set_string */
-
 static int gmond_config_add_metric(oconfig_item_t *ci) /* {{{ */
 {
   metric_map_t *map;
@@ -910,11 +865,11 @@ static int gmond_config_add_metric(oconfig_item_t *ci) /* {{{ */
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
     if (strcasecmp("Type", child->key) == 0)
-      gmond_config_set_string(child, &map->type);
+      cf_util_get_string(child, &map->type);
     else if (strcasecmp("TypeInstance", child->key) == 0)
-      gmond_config_set_string(child, &map->type_instance);
+      cf_util_get_string(child, &map->type_instance);
     else if (strcasecmp("DataSource", child->key) == 0)
-      gmond_config_set_string(child, &map->ds_name);
+      cf_util_get_string(child, &map->ds_name);
     else {
       WARNING("gmond plugin: Unknown configuration option `%s' ignored.",
               child->key);
index b22c3a2..4d65176 100644 (file)
--- a/src/gps.c
+++ b/src/gps.c
@@ -27,8 +27,8 @@
  **/
 
 #include "collectd.h"
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_time.h"
 
 #define CGPS_TRUE 1
diff --git a/src/gpu_nvidia.c b/src/gpu_nvidia.c
new file mode 100644 (file)
index 0000000..d76e503
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+Copyright 2018 Evgeny Naumov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "daemon/collectd.h"
+#include "daemon/plugin.h"
+#include "utils/common/common.h"
+
+#include <nvml.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#define MAX_DEVNAME_LEN 256
+#define PLUGIN_NAME "gpu_nvidia"
+
+static nvmlReturn_t nv_status = NVML_SUCCESS;
+static char *nv_errline = "";
+
+#define TRY_CATCH(f, catch)                                                    \
+  if ((nv_status = f) != NVML_SUCCESS) {                                       \
+    nv_errline = #f;                                                           \
+    goto catch;                                                                \
+  }
+
+#define TRY_CATCH_OPTIONAL(f, catch)                                           \
+  if ((nv_status = f) != NVML_SUCCESS &&                                       \
+      nv_status != NVML_ERROR_NOT_SUPPORTED) {                                 \
+    nv_errline = #f;                                                           \
+    goto catch;                                                                \
+  }
+
+#define TRY(f) TRY_CATCH(f, catch)
+#define TRYOPT(f) TRY_CATCH_OPTIONAL(f, catch)
+
+#define KEY_GPUINDEX "GPUIndex"
+#define KEY_IGNORESELECTED "IgnoreSelected"
+
+static const char *config_keys[] = {
+    KEY_GPUINDEX, KEY_IGNORESELECTED,
+};
+static const unsigned int n_config_keys = STATIC_ARRAY_SIZE(config_keys);
+
+// This is a bitflag, necessitating the (extremely conservative) assumption
+// that there are no more than 64 GPUs on this system.
+static uint64_t conf_match_mask = 0;
+static bool conf_mask_is_exclude = 0;
+
+static int nvml_config(const char *key, const char *value) {
+
+  if (strcasecmp(key, KEY_GPUINDEX) == 0) {
+    char *eptr;
+    unsigned long device_ix = strtoul(value, &eptr, 10);
+    if (eptr == value) {
+      ERROR(PLUGIN_NAME ": Failed to parse GPUIndex value \"%s\"", value);
+      return -1;
+    }
+    if (device_ix >= 64) {
+      ERROR(PLUGIN_NAME
+            ": At most 64 GPUs (0 <= GPUIndex < 64) are supported!");
+      return -2;
+    }
+    conf_match_mask |= (1 << device_ix);
+  } else if (strcasecmp(key, KEY_IGNORESELECTED)) {
+    conf_mask_is_exclude = IS_TRUE(value);
+  } else {
+    ERROR(PLUGIN_NAME ": Unrecognized config option %s", key);
+    return -10;
+  }
+
+  return 0;
+}
+
+static int nvml_init(void) {
+  TRY(nvmlInit());
+  return 0;
+
+  catch : ERROR(PLUGIN_NAME ": NVML init failed with %d", nv_status);
+  return -1;
+}
+
+static int nvml_shutdown(void) {
+  TRY(nvmlShutdown())
+  return 0;
+
+  catch : ERROR(PLUGIN_NAME ": NVML shutdown failed with %d", nv_status);
+  return -1;
+}
+
+static void nvml_submit_gauge(const char *plugin_instance, const char *type,
+                              const char *type_instance, gauge_t nvml) {
+
+  value_list_t vl = VALUE_LIST_INIT;
+
+  vl.values = &(value_t){.gauge = nvml};
+  vl.values_len = 1;
+
+  sstrncpy(vl.plugin, PLUGIN_NAME, sizeof(vl.plugin));
+  sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
+
+  sstrncpy(vl.type, type, sizeof(vl.type));
+
+  if (type_instance != NULL) {
+    sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+  }
+
+  plugin_dispatch_values(&vl);
+}
+
+static int nvml_read(void) {
+
+  unsigned int device_count;
+  TRY_CATCH(nvmlDeviceGetCount(&device_count), catch_nocount);
+
+  if (device_count > 64) {
+    device_count = 64;
+  }
+
+  for (unsigned int ix = 0; ix < device_count; ix++) {
+
+    unsigned int is_match =
+        ((1 << ix) & conf_match_mask) || (conf_match_mask == 0);
+    if (conf_mask_is_exclude == !!is_match) {
+      continue;
+    }
+
+    nvmlDevice_t dev;
+    TRY(nvmlDeviceGetHandleByIndex(ix, &dev));
+
+    char dev_name[MAX_DEVNAME_LEN + 1] = {0};
+    TRY(nvmlDeviceGetName(dev, dev_name, sizeof(dev_name) - 1));
+
+    // Try to be as lenient as possible with the variety of devices that are
+    // out there, ignoring any NOT_SUPPORTED errors gently.
+    nvmlMemory_t meminfo;
+    TRYOPT(nvmlDeviceGetMemoryInfo(dev, &meminfo))
+    if (nv_status == NVML_SUCCESS) {
+      nvml_submit_gauge(dev_name, "memory", "used", meminfo.used);
+      nvml_submit_gauge(dev_name, "memory", "free", meminfo.free);
+    }
+
+    nvmlUtilization_t utilization;
+    TRYOPT(nvmlDeviceGetUtilizationRates(dev, &utilization))
+    if (nv_status == NVML_SUCCESS)
+      nvml_submit_gauge(dev_name, "percent", "gpu_used", utilization.gpu);
+
+    unsigned int fan_speed;
+    TRYOPT(nvmlDeviceGetFanSpeed(dev, &fan_speed))
+    if (nv_status == NVML_SUCCESS)
+      nvml_submit_gauge(dev_name, "fanspeed", NULL, fan_speed);
+
+    unsigned int core_temp;
+    TRYOPT(nvmlDeviceGetTemperature(dev, NVML_TEMPERATURE_GPU, &core_temp))
+    if (nv_status == NVML_SUCCESS)
+      nvml_submit_gauge(dev_name, "temperature", "core", core_temp);
+
+    unsigned int sm_clk_mhz;
+    TRYOPT(nvmlDeviceGetClockInfo(dev, NVML_CLOCK_SM, &sm_clk_mhz))
+    if (nv_status == NVML_SUCCESS)
+      nvml_submit_gauge(dev_name, "frequency", "multiprocessor",
+                        1e6 * sm_clk_mhz);
+
+    unsigned int mem_clk_mhz;
+    TRYOPT(nvmlDeviceGetClockInfo(dev, NVML_CLOCK_MEM, &mem_clk_mhz))
+    if (nv_status == NVML_SUCCESS)
+      nvml_submit_gauge(dev_name, "frequency", "memory", 1e6 * mem_clk_mhz);
+
+    unsigned int power_mW;
+    TRYOPT(nvmlDeviceGetPowerUsage(dev, &power_mW))
+    if (nv_status == NVML_SUCCESS)
+      nvml_submit_gauge(dev_name, "power", NULL, 1e-3 * power_mW);
+
+    continue;
+
+    // Failures here indicate transient errors or removal of GPU. In either
+    // case it will either be resolved or the GPU will no longer be enumerated
+    // the next time round.
+    catch : WARNING(PLUGIN_NAME
+                    ": NVML call \"%s\" failed (%d) on dev at index %d!",
+                    nv_errline, nv_status, ix);
+    continue;
+  }
+
+  return 0;
+
+// Failures here indicate serious misconfiguration; we bail out totally.
+catch_nocount:
+  ERROR(PLUGIN_NAME ": Failed to enumerate NVIDIA GPUs (\"%s\" returned %d)",
+        nv_errline, nv_status);
+  return -1;
+}
+
+void module_register(void) {
+  plugin_register_init(PLUGIN_NAME, nvml_init);
+  plugin_register_config(PLUGIN_NAME, nvml_config, config_keys, n_config_keys);
+  plugin_register_read(PLUGIN_NAME, nvml_read);
+  plugin_register_shutdown(PLUGIN_NAME, nvml_shutdown);
+}
index 0f5cfec..1e9cb20 100644 (file)
@@ -41,8 +41,8 @@ extern "C" {
 #include <stdbool.h>
 
 #include "collectd.h"
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include "daemon/utils_cache.h"
 }
@@ -56,7 +56,8 @@ using collectd::QueryValuesResponse;
 
 using google::protobuf::util::TimeUtil;
 
-typedef google::protobuf::Map<grpc::string, collectd::types::MetadataValue> grpcMetadata;
+typedef google::protobuf::Map<grpc::string, collectd::types::MetadataValue>
+    grpcMetadata;
 
 /*
  * private types
@@ -175,10 +176,11 @@ static grpc::Status marshal_meta_data(meta_data_t *meta,
     switch (md_type) {
     case MD_TYPE_STRING:
       char *md_string;
-      if (meta_data_get_string(meta, key, &md_string) != 0 || md_string == nullptr) {
+      if (meta_data_get_string(meta, key, &md_string) != 0 ||
+          md_string == nullptr) {
         strarray_free(meta_data_keys, meta_data_keys_len);
         return grpc::Status(grpc::StatusCode::INTERNAL,
-                          grpc::string("missing metadata"));
+                            grpc::string("missing metadata"));
       }
       md_value.set_string_value(md_string);
       free(md_string);
@@ -188,7 +190,7 @@ static grpc::Status marshal_meta_data(meta_data_t *meta,
       if (meta_data_get_signed_int(meta, key, &int64_value) != 0) {
         strarray_free(meta_data_keys, meta_data_keys_len);
         return grpc::Status(grpc::StatusCode::INTERNAL,
-                          grpc::string("missing metadata"));
+                            grpc::string("missing metadata"));
       }
       md_value.set_int64_value(int64_value);
       break;
@@ -197,7 +199,7 @@ static grpc::Status marshal_meta_data(meta_data_t *meta,
       if (meta_data_get_unsigned_int(meta, key, &uint64_value) != 0) {
         strarray_free(meta_data_keys, meta_data_keys_len);
         return grpc::Status(grpc::StatusCode::INTERNAL,
-                          grpc::string("missing metadata"));
+                            grpc::string("missing metadata"));
       }
       md_value.set_uint64_value(uint64_value);
       break;
@@ -206,7 +208,7 @@ static grpc::Status marshal_meta_data(meta_data_t *meta,
       if (meta_data_get_double(meta, key, &double_value) != 0) {
         strarray_free(meta_data_keys, meta_data_keys_len);
         return grpc::Status(grpc::StatusCode::INTERNAL,
-                          grpc::string("missing metadata"));
+                            grpc::string("missing metadata"));
       }
       md_value.set_double_value(double_value);
       break;
@@ -215,7 +217,7 @@ static grpc::Status marshal_meta_data(meta_data_t *meta,
       if (meta_data_get_boolean(meta, key, &bool_value) != 0) {
         strarray_free(meta_data_keys, meta_data_keys_len);
         return grpc::Status(grpc::StatusCode::INTERNAL,
-                          grpc::string("missing metadata"));
+                            grpc::string("missing metadata"));
       }
       md_value.set_bool_value(bool_value);
       break;
@@ -239,9 +241,9 @@ static grpc::Status unmarshal_meta_data(const grpcMetadata &rpc_metadata,
   *md_out = meta_data_create();
   if (*md_out == nullptr) {
     return grpc::Status(grpc::StatusCode::RESOURCE_EXHAUSTED,
-                        grpc::string("failed to metadata list"));
+                        grpc::string("failed to create metadata list"));
   }
-  for (auto kv: rpc_metadata) {
+  for (auto kv : rpc_metadata) {
     auto k = kv.first.c_str();
     auto v = kv.second;
 
@@ -267,8 +269,8 @@ static grpc::Status unmarshal_meta_data(const grpcMetadata &rpc_metadata,
       break;
     default:
       meta_data_destroy(*md_out);
-      return  grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
-                           grpc::string("Metadata of unknown type"));
+      return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+                          grpc::string("Metadata of unknown type"));
     }
   }
   return grpc::Status::OK;
@@ -482,8 +484,9 @@ private:
         break;
       }
       if (uc_iterator_get_meta(iter, &vl.meta) < 0) {
-        status = grpc::Status(grpc::StatusCode::INTERNAL,
-                              grpc::string("failed to retrieve value metadata"));
+        status =
+            grpc::Status(grpc::StatusCode::INTERNAL,
+                         grpc::string("failed to retrieve value metadata"));
       }
 
       value_lists->push(vl);
@@ -626,7 +629,8 @@ static int c_grpc_config_listen(oconfig_item_t *ci) {
   listener.port = grpc::string(ci->values[1].value.string);
   listener.ssl = nullptr;
 
-  auto ssl_opts = new (grpc::SslServerCredentialsOptions);
+  auto ssl_opts = new grpc::SslServerCredentialsOptions(
+      GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
   grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp = {};
   bool use_ssl = false;
 
@@ -659,6 +663,14 @@ static int c_grpc_config_listen(oconfig_item_t *ci) {
         return -1;
       }
       pkcp.cert_chain = read_file(cert);
+    } else if (!strcasecmp("VerifyPeer", child->key)) {
+      bool verify = false;
+      if (cf_util_get_boolean(child, &verify)) {
+        return -1;
+      }
+      ssl_opts->client_certificate_request =
+          verify ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+                 : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE;
     } else {
       WARNING("grpc: Option `%s` not allowed in <%s> block.", child->key,
               ci->key);
index a7eaf36..96b4f0c 100644 (file)
@@ -33,8 +33,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <assert.h>
 #include <libgen.h> /* for basename */
@@ -53,7 +53,7 @@
 static const char *config_keys[] = {"Host", "Port"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static char *hddtemp_host = NULL;
+static char *hddtemp_host;
 static char hddtemp_port[16];
 
 /*
@@ -108,10 +108,8 @@ static char *hddtemp_query_daemon(void) {
                               .ai_socktype = SOCK_STREAM};
 
   if ((ai_return = getaddrinfo(host, port, &ai_hints, &ai_list)) != 0) {
-    char errbuf[1024];
     ERROR("hddtemp plugin: getaddrinfo (%s, %s): %s", host, port,
-          (ai_return == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                    : gai_strerror(ai_return));
+          (ai_return == EAI_SYSTEM) ? STRERRNO : gai_strerror(ai_return));
     return NULL;
   }
 
@@ -121,17 +119,13 @@ static char *hddtemp_query_daemon(void) {
     /* create our socket descriptor */
     fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (fd < 0) {
-      char errbuf[1024];
-      ERROR("hddtemp plugin: socket: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("hddtemp plugin: socket: %s", STRERRNO);
       continue;
     }
 
     /* connect to the hddtemp daemon */
     if (connect(fd, (struct sockaddr *)ai_ptr->ai_addr, ai_ptr->ai_addrlen)) {
-      char errbuf[1024];
-      INFO("hddtemp plugin: connect (%s, %s) failed: %s", host, port,
-           sstrerror(errno, errbuf, sizeof(errbuf)));
+      INFO("hddtemp plugin: connect (%s, %s) failed: %s", host, port, STRERRNO);
       close(fd);
       fd = -1;
       continue;
@@ -177,13 +171,11 @@ static char *hddtemp_query_daemon(void) {
     if (status == 0) {
       break;
     } else if (status == -1) {
-      char errbuf[1024];
 
       if ((errno == EAGAIN) || (errno == EINTR))
         continue;
 
-      ERROR("hddtemp plugin: Error reading from socket: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("hddtemp plugin: Error reading from socket: %s", STRERRNO);
       close(fd);
       free(buffer);
       return NULL;
index c5b0ecb..e066300 100644 (file)
 
 #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";
 
-static _Bool g_flag_rpt_numa = 1;
-static _Bool g_flag_rpt_mm = 1;
+static bool g_flag_rpt_numa = true;
+static bool g_flag_rpt_mm = true;
 
-static _Bool g_values_pages = 1;
-static _Bool g_values_bytes = 0;
-static _Bool g_values_percent = 0;
+static bool g_values_pages = true;
+static bool g_values_bytes;
+static bool g_values_percent;
 
 #define HP_HAVE_NR 0x01
 #define HP_HAVE_SURPLUS 0x02
@@ -102,20 +102,20 @@ static void submit_hp(const struct entry_info *info) {
 
   if (g_values_pages) {
     sstrncpy(vl.type, "vmpage_number", sizeof(vl.type));
-    plugin_dispatch_multivalue(&vl, /* store_percentage = */ 0, DS_TYPE_GAUGE,
-                               "free", free, "used", used, NULL);
+    plugin_dispatch_multivalue(&vl, /* store_percentage = */ false,
+                               DS_TYPE_GAUGE, "free", free, "used", used, NULL);
   }
   if (g_values_bytes) {
     gauge_t page_size = (gauge_t)(1024 * info->page_size_kb);
     sstrncpy(vl.type, "memory", sizeof(vl.type));
-    plugin_dispatch_multivalue(&vl, /* store_percentage = */ 0, DS_TYPE_GAUGE,
-                               "free", free * page_size, "used",
+    plugin_dispatch_multivalue(&vl, /* store_percentage = */ false,
+                               DS_TYPE_GAUGE, "free", free * page_size, "used",
                                used * page_size, NULL);
   }
   if (g_values_percent) {
     sstrncpy(vl.type, "percent", sizeof(vl.type));
-    plugin_dispatch_multivalue(&vl, /* store_percentage = */ 1, DS_TYPE_GAUGE,
-                               "free", free, "used", used, NULL);
+    plugin_dispatch_multivalue(&vl, /* store_percentage = */ true,
+                               DS_TYPE_GAUGE, "free", free, "used", used, NULL);
   }
 }
 
@@ -185,10 +185,8 @@ static int read_syshugepages(const char *path, const char *node) {
     long page_size = strtol(result->d_name + strlen(hugepages_dir),
                             /* endptr = */ NULL, /* base = */ 10);
     if (errno != 0) {
-      char errbuf[1024];
       ERROR("%s: failed to determine page size from directory name \"%s\": %s",
-            g_plugin_name, result->d_name,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            g_plugin_name, result->d_name, STRERRNO);
       continue;
     }
 
index fc7d680..f04f887 100644 (file)
@@ -27,9 +27,9 @@
  **/
 
 #include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
 
-#include "utils_config_cores.h"
+#include "utils/config_cores/config_cores.h"
 
 #include <jevents.h>
 #include <jsession.h>
@@ -67,9 +67,9 @@ struct event_info {
 typedef struct event_info event_info_t;
 
 struct intel_pmu_ctx_s {
-  _Bool hw_cache_events;
-  _Bool kernel_pmu_events;
-  _Bool sw_events;
+  bool hw_cache_events;
+  bool kernel_pmu_events;
+  bool sw_events;
   char event_list_fn[PATH_MAX];
   char **hw_events;
   size_t hw_events_count;
@@ -196,7 +196,8 @@ static void pmu_dump_config(void) {
   DEBUG(PMU_PLUGIN ":   software_events   : %d", g_ctx.sw_events);
 
   for (size_t i = 0; i < g_ctx.hw_events_count; i++) {
-    DEBUG(PMU_PLUGIN ":   hardware_events[%zu]: %s", i, g_ctx.hw_events[i]);
+    DEBUG(PMU_PLUGIN ":   hardware_events[%" PRIsz "]: %s", i,
+          g_ctx.hw_events[i]);
   }
 }
 
@@ -266,7 +267,7 @@ static int pmu_config_hw_events(oconfig_item_t *ci) {
     return -EINVAL;
   }
 
-  g_ctx.hw_events = calloc(ci->values_num, sizeof(char *));
+  g_ctx.hw_events = calloc(ci->values_num, sizeof(*g_ctx.hw_events));
   if (g_ctx.hw_events == NULL) {
     ERROR(PMU_PLUGIN ": Failed to allocate hw events.");
     return -ENOMEM;
@@ -445,7 +446,7 @@ static int pmu_add_events(struct eventlist *el, uint32_t type,
     /* Allocate memory for event struct that contains array of efd structs
        for all cores */
     struct event *e =
-        calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1);
+        calloc(1, sizeof(struct event) + sizeof(struct efd) * el->num_cpus);
     if (e == NULL) {
       ERROR(PMU_PLUGIN ": Failed to allocate event structure");
       return -ENOMEM;
@@ -481,7 +482,7 @@ static int pmu_add_hw_events(struct eventlist *el, char **e, size_t count) {
       /* Allocate memory for event struct that contains array of efd structs
          for all cores */
       struct event *e =
-          calloc(sizeof(struct event) + sizeof(struct efd) * el->num_cpus, 1);
+          calloc(1, sizeof(struct event) + sizeof(struct efd) * el->num_cpus);
       if (e == NULL) {
         free(events);
         return -ENOMEM;
index 38a6e93..d949114 100644 (file)
@@ -26,9 +26,8 @@
  **/
 
 #include "collectd.h"
-#include "common.h"
-
-#include "utils_config_cores.h"
+#include "utils/common/common.h"
+#include "utils/config_cores/config_cores.h"
 
 #include <pqos.h>
 
@@ -54,7 +53,7 @@ struct rdt_ctx_s {
 };
 typedef struct rdt_ctx_s rdt_ctx_t;
 
-static rdt_ctx_t *g_rdt = NULL;
+static rdt_ctx_t *g_rdt;
 
 static rdt_config_status g_state = UNKNOWN;
 
@@ -66,9 +65,9 @@ static void rdt_dump_cgroups(void) {
     return;
 
   DEBUG(RDT_PLUGIN ": Core Groups Dump");
-  DEBUG(RDT_PLUGIN ":  groups count: %zu", g_rdt->num_groups);
+  DEBUG(RDT_PLUGIN ":  groups count: %" PRIsz, g_rdt->num_groups);
 
-  for (int i = 0; i < g_rdt->num_groups; i++) {
+  for (size_t i = 0; i < g_rdt->num_groups; i++) {
     core_group_t *cgroup = g_rdt->cores.cgroups + i;
 
     memset(cores, 0, sizeof(cores));
@@ -77,7 +76,7 @@ static void rdt_dump_cgroups(void) {
                cgroup->cores[j]);
     }
 
-    DEBUG(RDT_PLUGIN ":  group[%d]:", i);
+    DEBUG(RDT_PLUGIN ":  group[%zu]:", i);
     DEBUG(RDT_PLUGIN ":    description: %s", cgroup->desc);
     DEBUG(RDT_PLUGIN ":    cores: %s", cores);
     DEBUG(RDT_PLUGIN ":    events: 0x%X", g_rdt->events[i]);
@@ -125,7 +124,7 @@ static void rdt_free_cgroups(void) {
 static int rdt_default_cgroups(void) {
   unsigned num_cores = g_rdt->pqos_cpu->num_cores;
 
-  g_rdt->cores.cgroups = calloc(num_cores, sizeof(*(g_rdt->cores.cgroups)));
+  g_rdt->cores.cgroups = calloc(num_cores, sizeof(*g_rdt->cores.cgroups));
   if (g_rdt->cores.cgroups == NULL) {
     ERROR(RDT_PLUGIN ": Error allocating core groups array");
     return -ENOMEM;
@@ -138,7 +137,7 @@ static int rdt_default_cgroups(void) {
     char desc[DATA_MAX_NAME_LEN];
 
     /* set core group info */
-    cgroup->cores = calloc(1, sizeof(*(cgroup->cores)));
+    cgroup->cores = calloc(1, sizeof(*cgroup->cores));
     if (cgroup->cores == NULL) {
       ERROR(RDT_PLUGIN ": Error allocating cores array");
       rdt_free_cgroups();
@@ -159,9 +158,9 @@ static int rdt_default_cgroups(void) {
   return num_cores;
 }
 
-static int rdt_is_core_id_valid(int core_id) {
+static int rdt_is_core_id_valid(unsigned int core_id) {
 
-  for (int i = 0; i < g_rdt->pqos_cpu->num_cores; i++)
+  for (unsigned int i = 0; i < g_rdt->pqos_cpu->num_cores; i++)
     if (core_id == g_rdt->pqos_cpu->cores[i].lcore)
       return 1;
 
@@ -183,9 +182,9 @@ static int rdt_config_cgroups(oconfig_item_t *item) {
   for (size_t group_idx = 0; group_idx < n; group_idx++) {
     core_group_t *cgroup = g_rdt->cores.cgroups + group_idx;
     for (size_t core_idx = 0; core_idx < cgroup->num_cores; core_idx++) {
-      if (!rdt_is_core_id_valid((int)cgroup->cores[core_idx])) {
-        ERROR(RDT_PLUGIN ": Core group '%s' contains invalid core id '%d'",
-              cgroup->desc, (int)cgroup->cores[core_idx]);
+      if (!rdt_is_core_id_valid(cgroup->cores[core_idx])) {
+        ERROR(RDT_PLUGIN ": Core group '%s' contains invalid core id '%u'",
+              cgroup->desc, cgroup->cores[core_idx]);
         rdt_free_cgroups();
         return -EINVAL;
       }
@@ -206,7 +205,7 @@ static int rdt_config_cgroups(oconfig_item_t *item) {
   }
 
   /* Get all available events on this platform */
-  for (int i = 0; i < g_rdt->cap_mon->u.mon->num_events; i++)
+  for (unsigned int i = 0; i < g_rdt->cap_mon->u.mon->num_events; i++)
     events |= g_rdt->cap_mon->u.mon->events[i].type;
 
   events &= ~(PQOS_PERF_EVENT_LLC_MISS);
@@ -337,8 +336,8 @@ static int rdt_config(oconfig_item_t *ci) {
   return 0;
 }
 
-static void rdt_submit_derive(char *cgroup, char *type, char *type_instance,
-                              derive_t value) {
+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};
@@ -353,8 +352,8 @@ static void rdt_submit_derive(char *cgroup, char *type, char *type_instance,
   plugin_dispatch_values(&vl);
 }
 
-static void rdt_submit_gauge(char *cgroup, char *type, char *type_instance,
-                             gauge_t value) {
+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};
@@ -387,7 +386,7 @@ static int rdt_read(__attribute__((unused)) user_data_t *ud) {
   rdt_dump_data();
 #endif /* COLLECT_DEBUG */
 
-  for (int i = 0; i < g_rdt->num_groups; i++) {
+  for (size_t i = 0; i < g_rdt->num_groups; i++) {
     core_group_t *cgroup = g_rdt->cores.cgroups + i;
 
     enum pqos_mon_event mbm_events =
@@ -426,7 +425,7 @@ static int rdt_init(void) {
     return ret;
 
   /* Start monitoring */
-  for (int i = 0; i < g_rdt->num_groups; i++) {
+  for (size_t i = 0; i < g_rdt->num_groups; i++) {
     core_group_t *cg = g_rdt->cores.cgroups + i;
 
     ret = pqos_mon_start(cg->num_cores, cg->cores, g_rdt->events[i],
@@ -449,7 +448,7 @@ static int rdt_shutdown(void) {
     return 0;
 
   /* Stop monitoring */
-  for (int i = 0; i < g_rdt->num_groups; i++) {
+  for (size_t i = 0; i < g_rdt->num_groups; i++) {
     pqos_mon_stop(g_rdt->pgroups[i]);
   }
 
index 3a3e9f6..0e13970 100644 (file)
@@ -24,9 +24,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -86,9 +86,9 @@ static const char *config_keys[] = {
 };
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static ignorelist_t *ignorelist = NULL;
+static ignorelist_t *ignorelist;
 
-static _Bool report_inactive = 1;
+static bool report_inactive = true;
 
 #ifdef HAVE_LIBKSTAT
 #if HAVE_KSTAT_H
@@ -97,8 +97,8 @@ static _Bool report_inactive = 1;
 #define MAX_NUMIF 256
 extern kstat_ctl_t *kc;
 static kstat_t *ksp[MAX_NUMIF];
-static int numif = 0;
-static _Bool unique_name = 0;
+static int numif;
+static bool unique_name;
 #endif /* HAVE_LIBKSTAT */
 
 static int interface_config(const char *key, const char *value) {
@@ -117,7 +117,7 @@ static int interface_config(const char *key, const char *value) {
   else if (strcasecmp(key, "UniqueName") == 0) {
 #ifdef HAVE_LIBKSTAT
     if (IS_TRUE(value))
-      unique_name = 1;
+      unique_name = true;
 #else
     WARNING("interface plugin: the \"UniqueName\" option is only valid on "
             "Solaris.");
@@ -239,9 +239,7 @@ static int interface_read(void) {
   int numfields;
 
   if ((fh = fopen("/proc/net/dev", "r")) == NULL) {
-    char errbuf[1024];
-    WARNING("interface plugin: fopen: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("interface plugin: fopen: %s", STRERRNO);
     return -1;
   }
 
@@ -355,9 +353,7 @@ static int interface_read(void) {
 
   if ((nif = perfstat_netinterface(NULL, NULL, sizeof(perfstat_netinterface_t),
                                    0)) < 0) {
-    char errbuf[1024];
-    WARNING("interface plugin: perfstat_netinterface: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("interface plugin: perfstat_netinterface: %s", STRERRNO);
     return -1;
   }
 
@@ -370,9 +366,8 @@ static int interface_read(void) {
   id.name[0] = '\0';
   if ((ifs = perfstat_netinterface(&id, ifstat, sizeof(perfstat_netinterface_t),
                                    nif)) < 0) {
-    char errbuf[1024];
     WARNING("interface plugin: perfstat_netinterface (interfaces=%d): %s", nif,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
     return -1;
   }
 
index ed8a0c0..ab4b214 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if KERNEL_LINUX
 /* _GNU_SOURCE is needed for struct shm_info.used_ids on musl libc */
@@ -119,10 +119,9 @@ static int ipc_read_sem(void) /* {{{ */
 
   status = semctl(/* id = */ 0, /* num = */ 0, SEM_INFO, arg);
   if (status == -1) {
-    char errbuf[1024];
     ERROR("ipc plugin: semctl(2) failed: %s. "
           "Maybe the kernel is not configured for semaphores?",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return -1;
   }
 
@@ -139,10 +138,9 @@ static int ipc_read_shm(void) /* {{{ */
 
   status = shmctl(/* id = */ 0, SHM_INFO, (void *)&shm_info);
   if (status == -1) {
-    char errbuf[1024];
     ERROR("ipc plugin: shmctl(2) failed: %s. "
           "Maybe the kernel is not configured for shared memory?",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return -1;
   }
 
@@ -187,9 +185,7 @@ static caddr_t ipc_get_info(cid_t cid, int cmd, int version, int stsize,
 
   if (get_ipc_info(cid, cmd, version, buff, &size) < 0) {
     if (errno != ENOSPC) {
-      char errbuf[1024];
-      WARNING("ipc plugin: get_ipc_info: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("ipc plugin: get_ipc_info: %s", STRERRNO);
       return NULL;
     }
   }
@@ -211,9 +207,7 @@ static caddr_t ipc_get_info(cid_t cid, int cmd, int version, int stsize,
   }
 
   if (get_ipc_info(cid, cmd, version, buff, &size) < 0) {
-    char errbuf[1024];
-    WARNING("ipc plugin: get_ipc_info: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("ipc plugin: get_ipc_info: %s", STRERRNO);
     free(buff);
     return NULL;
   }
index 98cc3b0..d78ffa9 100644 (file)
@@ -26,9 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #include <OpenIPMI/ipmi_auth.h>
 #include <OpenIPMI/ipmi_conn.h>
@@ -49,12 +49,13 @@ typedef struct c_ipmi_sensor_list_s c_ipmi_sensor_list_t;
 struct c_ipmi_instance_s {
   char *name;
   ignorelist_t *ignorelist;
-  _Bool notify_add;
-  _Bool notify_remove;
-  _Bool notify_notpresent;
-  _Bool notify_conn;
-  _Bool sel_enabled;
-  _Bool sel_clear_event;
+  ignorelist_t *sel_ignorelist;
+  bool notify_add;
+  bool notify_remove;
+  bool notify_notpresent;
+  bool notify_conn;
+  bool sel_enabled;
+  bool sel_clear_event;
 
   char *host;
   char *connaddr;
@@ -62,12 +63,12 @@ struct c_ipmi_instance_s {
   char *password;
   unsigned int authtype;
 
-  _Bool connected;
+  bool connected;
   ipmi_con_t *connection;
   pthread_mutex_t sensor_list_lock;
   c_ipmi_sensor_list_t *sensor_list;
 
-  _Bool active;
+  bool active;
   pthread_t thread_id;
   int init_in_progress;
 
@@ -95,8 +96,8 @@ typedef struct c_ipmi_db_type_map_s c_ipmi_db_type_map_t;
 /*
  * Module global variables
  */
-static os_handler_t *os_handler = NULL;
-static c_ipmi_instance_t *instances = NULL;
+static os_handler_t *os_handler;
+static c_ipmi_instance_t *instances;
 
 /*
  * Misc private functions
@@ -112,7 +113,7 @@ static void c_ipmi_error(c_ipmi_instance_t *st, const char *func, int status) {
   if (errbuf[0] == 0) {
     snprintf(errbuf, sizeof(errbuf), "Unknown error %#x", status);
   }
-  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 */
@@ -309,7 +310,7 @@ static void sensor_get_name(ipmi_sensor_t *sensor, char *buffer, int buf_len) {
     return;
 
   ipmi_sensor_get_name(sensor, temp, sizeof(temp));
-  temp[sizeof(temp) - 1] = 0;
+  temp[sizeof(temp) - 1] = '\0';
 
   if (entity_id_string != NULL && strlen(temp))
     snprintf(sensor_name, sizeof(sensor_name), "%s %s", temp, entity_id_string);
@@ -357,7 +358,7 @@ static const char *sensor_unit_to_type(ipmi_sensor_t *sensor) {
 
   /* find the db type by using sensor base unit type */
   enum ipmi_unit_type_e ipmi_type = ipmi_sensor_get_base_unit(sensor);
-  for (int i = 0; i < STATIC_ARRAY_SIZE(ipmi_db_type_map); i++)
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(ipmi_db_type_map); i++)
     if (ipmi_db_type_map[i].type == ipmi_type)
       return ipmi_db_type_map[i].type_name;
 
@@ -475,7 +476,7 @@ static int sensor_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
     return 0;
   }
 
-  list_item = (c_ipmi_sensor_list_t *)calloc(1, sizeof(c_ipmi_sensor_list_t));
+  list_item = calloc(1, sizeof(*list_item));
   if (list_item == NULL) {
     pthread_mutex_unlock(&st->sensor_list_lock);
     return -1;
@@ -770,6 +771,40 @@ static int sensor_discrete_event_handler(ipmi_sensor_t *sensor,
   return IPMI_EVENT_NOT_HANDLED;
 } /* int sensor_discrete_event_handler */
 
+static int sel_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
+  char sensor_name[DATA_MAX_NAME_LEN] = {0};
+  int status = 0;
+
+  /* Check if sensor on sel_ignorelist */
+  sensor_get_name(sensor, sensor_name, sizeof(sensor_name));
+  if (ignorelist_match(st->sel_ignorelist, sensor_name) != 0)
+    return 0;
+
+  /* register threshold event if threshold sensor support events */
+  if (ipmi_sensor_get_event_reading_type(sensor) ==
+      IPMI_EVENT_READING_TYPE_THRESHOLD)
+    status = ipmi_sensor_add_threshold_event_handler(
+        sensor, sensor_threshold_event_handler, st);
+  /* register discrete handler if discrete/specific sensor support events */
+  else if (ipmi_sensor_get_event_support(sensor) != IPMI_EVENT_SUPPORT_NONE)
+    status = ipmi_sensor_add_discrete_event_handler(
+        sensor, sensor_discrete_event_handler, st);
+
+  if (status)
+    ERROR("Unable to add sensor %s event handler, status: %d", sensor_name,
+          status);
+  return status;
+}
+
+static void sel_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
+  if (ipmi_sensor_get_event_reading_type(sensor) ==
+      IPMI_EVENT_READING_TYPE_THRESHOLD)
+    ipmi_sensor_remove_threshold_event_handler(
+        sensor, sensor_threshold_event_handler, st);
+  else
+    ipmi_sensor_remove_discrete_event_handler(
+        sensor, sensor_discrete_event_handler, st);
+}
 /*
  * Entity handlers
  */
@@ -782,37 +817,12 @@ entity_sensor_update_handler(enum ipmi_update_e op,
   if ((op == IPMI_ADDED) || (op == IPMI_CHANGED)) {
     /* Will check for duplicate entries.. */
     sensor_list_add(st, sensor);
-
-    if (st->sel_enabled) {
-      int status = 0;
-      /* register threshold event handler */
-      if (ipmi_sensor_get_event_reading_type(sensor) ==
-          IPMI_EVENT_READING_TYPE_THRESHOLD)
-        status = ipmi_sensor_add_threshold_event_handler(
-            sensor, sensor_threshold_event_handler, st);
-      /* register discrete handler if discrete/specific sensor support events */
-      else if (ipmi_sensor_get_event_support(sensor) != IPMI_EVENT_SUPPORT_NONE)
-        status = ipmi_sensor_add_discrete_event_handler(
-            sensor, sensor_discrete_event_handler, st);
-
-      if (status) {
-        char buf[DATA_MAX_NAME_LEN] = {0};
-        sensor_get_name(sensor, buf, sizeof(buf));
-        ERROR("Unable to add sensor %s event handler, status: %d", buf, status);
-      }
-    }
+    if (st->sel_enabled)
+      sel_list_add(st, sensor);
   } else if (op == IPMI_DELETED) {
     sensor_list_remove(st, sensor);
-
-    if (st->sel_enabled) {
-      if (ipmi_sensor_get_event_reading_type(sensor) ==
-          IPMI_EVENT_READING_TYPE_THRESHOLD)
-        ipmi_sensor_remove_threshold_event_handler(
-            sensor, sensor_threshold_event_handler, st);
-      else
-        ipmi_sensor_remove_discrete_event_handler(
-            sensor, sensor_discrete_event_handler, st);
-    }
+    if (st->sel_enabled)
+      sel_list_remove(st, sensor);
   }
 } /* void entity_sensor_update_handler */
 
@@ -884,7 +894,7 @@ static void domain_connection_change_handler(ipmi_domain_t *domain, int err,
       plugin_dispatch_notification(&n);
     }
 
-    st->connected = 0;
+    st->connected = false;
     return;
   }
 
@@ -896,7 +906,7 @@ static void domain_connection_change_handler(ipmi_domain_t *domain, int err,
     plugin_dispatch_notification(&n);
   }
 
-  st->connected = 1;
+  st->connected = true;
 
   int status = ipmi_domain_add_entity_update_handler(
       domain, domain_entity_update_handler, /* user data = */ st);
@@ -965,11 +975,11 @@ static void *c_ipmi_thread_main(void *user_data) {
   int status = c_ipmi_thread_init(st);
   if (status != 0) {
     ERROR("ipmi plugin: c_ipmi_thread_init failed.");
-    st->active = 0;
+    st->active = false;
     return (void *)-1;
   }
 
-  while (st->active != 0) {
+  while (st->active) {
     struct timeval tv = {1, 0};
     os_handler->perform_one_op(os_handler, &tv);
   }
@@ -1000,6 +1010,15 @@ static c_ipmi_instance_t *c_ipmi_init_instance() {
     return NULL;
   }
 
+  st->sel_ignorelist = ignorelist_create(/* invert = */ 1);
+  if (st->sel_ignorelist == NULL) {
+    ignorelist_free(st->ignorelist);
+    sfree(st->name);
+    sfree(st);
+    ERROR("ipmi plugin: SEL ignorelist_create() failed.");
+    return NULL;
+  }
+
   st->sensor_list = NULL;
   pthread_mutex_init(&st->sensor_list_lock, /* attr = */ NULL);
 
@@ -1026,6 +1045,7 @@ static void c_ipmi_free_instance(c_ipmi_instance_t *st) {
   sfree(st->username);
   sfree(st->password);
 
+  ignorelist_free(st->sel_ignorelist);
   ignorelist_free(st->ignorelist);
   pthread_mutex_destroy(&st->sensor_list_lock);
   sfree(st);
@@ -1070,7 +1090,7 @@ static int c_ipmi_config_add_instance(oconfig_item_t *ci) {
       ignorelist_add(st->ignorelist, value);
       sfree(value);
     } else if (strcasecmp("IgnoreSelected", child->key) == 0) {
-      _Bool t;
+      bool t;
       status = cf_util_get_boolean(child, &t);
       if (status != 0)
         break;
@@ -1083,6 +1103,19 @@ static int c_ipmi_config_add_instance(oconfig_item_t *ci) {
       status = cf_util_get_boolean(child, &st->notify_remove);
     } else if (strcasecmp("NotifySensorNotPresent", child->key) == 0) {
       status = cf_util_get_boolean(child, &st->notify_notpresent);
+    } else if (strcasecmp("SELSensor", child->key) == 0) {
+      char *value = NULL;
+      status = cf_util_get_string(child, &value);
+      if (status != 0)
+        break;
+      ignorelist_add(st->sel_ignorelist, value);
+      sfree(value);
+    } else if (strcasecmp("SELIgnoreSelected", child->key) == 0) {
+      bool t;
+      status = cf_util_get_boolean(child, &t);
+      if (status != 0)
+        break;
+      ignorelist_set_invert(st->sel_ignorelist, /* invert = */ !t);
     } else if (strcasecmp("SELEnabled", child->key) == 0) {
       status = cf_util_get_boolean(child, &st->sel_enabled);
     } else if (strcasecmp("SELClearEvent", child->key) == 0) {
@@ -1129,7 +1162,7 @@ static int c_ipmi_config_add_instance(oconfig_item_t *ci) {
 } /* int c_ipmi_config_add_instance */
 
 static int c_ipmi_config(oconfig_item_t *ci) {
-  _Bool have_instance_block = 0;
+  bool have_instance_block = 0;
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
@@ -1163,12 +1196,12 @@ static int c_ipmi_config(oconfig_item_t *ci) {
 static int c_ipmi_read(user_data_t *user_data) {
   c_ipmi_instance_t *st = user_data->data;
 
-  if (st->active == 0) {
+  if (st->active == false) {
     INFO("ipmi plugin: c_ipmi_read: I'm not active, returning false.");
     return -1;
   }
 
-  if (st->connected == 0)
+  if (st->connected == false)
     return 0;
 
   sensor_list_read_all(st);
@@ -1239,14 +1272,14 @@ static int c_ipmi_init(void) {
     }
 
     st->init_in_progress = cycles;
-    st->active = 1;
+    st->active = true;
 
     status = plugin_thread_create(&st->thread_id, /* attr = */ NULL,
                                   c_ipmi_thread_main,
                                   /* user data = */ (void *)st, "ipmi");
 
     if (status != 0) {
-      st->active = 0;
+      st->active = false;
       st->thread_id = (pthread_t){0};
 
       plugin_unregister_read(callback_name);
@@ -1268,7 +1301,7 @@ static int c_ipmi_shutdown(void) {
     c_ipmi_instance_t *next = st->next;
 
     st->next = NULL;
-    st->active = 0;
+    st->active = false;
 
     if (!pthread_equal(st->thread_id, (pthread_t){0})) {
       pthread_join(st->thread_id, NULL);
index 286c6e9..e1d83df 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <libiptc/libip6tc.h>
 #include <libiptc/libiptc.h>
@@ -86,8 +86,8 @@ typedef struct {
   char name[64];
 } ip_chain_t;
 
-static ip_chain_t **chain_list = NULL;
-static int chain_num = 0;
+static ip_chain_t **chain_list;
+static int chain_num;
 
 static int iptables_config(const char *key, const char *value) {
   /* int ip_value; */
@@ -103,9 +103,7 @@ static int iptables_config(const char *key, const char *value) {
   ip_chain_t temp = {0};
   ip_chain_t *final, **list;
   char *table;
-  int table_len;
   char *chain;
-  int chain_len;
 
   char *value_copy;
   char *fields[4];
@@ -113,8 +111,7 @@ static int iptables_config(const char *key, const char *value) {
 
   value_copy = strdup(value);
   if (value_copy == NULL) {
-    char errbuf[1024];
-    ERROR("strdup failed: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("strdup failed: %s", STRERRNO);
     return 1;
   }
 
@@ -137,16 +134,16 @@ static int iptables_config(const char *key, const char *value) {
   table = fields[0];
   chain = fields[1];
 
-  table_len = strlen(table) + 1;
-  if ((unsigned int)table_len > sizeof(temp.table)) {
+  size_t table_len = strlen(table) + 1;
+  if (table_len > sizeof(temp.table)) {
     ERROR("Table `%s' too long.", table);
     free(value_copy);
     return 1;
   }
   sstrncpy(temp.table, table, table_len);
 
-  chain_len = strlen(chain) + 1;
-  if ((unsigned int)chain_len > sizeof(temp.chain)) {
+  size_t chain_len = strlen(chain) + 1;
+  if (chain_len > sizeof(temp.chain)) {
     ERROR("Chain `%s' too long.", chain);
     free(value_copy);
     return 1;
@@ -182,8 +179,7 @@ static int iptables_config(const char *key, const char *value) {
 
   list = realloc(chain_list, (chain_num + 1) * sizeof(ip_chain_t *));
   if (list == NULL) {
-    char errbuf[1024];
-    ERROR("realloc failed: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("realloc failed: %s", STRERRNO);
     sfree(temp.rule.comment);
     return 1;
   }
@@ -191,8 +187,7 @@ static int iptables_config(const char *key, const char *value) {
   chain_list = list;
   final = malloc(sizeof(*final));
   if (final == NULL) {
-    char errbuf[1024];
-    ERROR("malloc failed: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("malloc failed: %s", STRERRNO);
     sfree(temp.rule.comment);
     return 1;
   }
index 3d399bd..0259df8 100644 (file)
@@ -32,8 +32,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_ARPA_INET_H
 #include <arpa/inet.h>
@@ -63,9 +63,7 @@ static struct ip_vs_get_services *ipvs_get_services(void) {
 
   if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_INFO, &ipvs_info, &len) ==
       -1) {
-    char errbuf[1024];
-    log_err("ip_vs_get_services: getsockopt() failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_err("ip_vs_get_services: getsockopt() failed: %s", STRERRNO);
     return NULL;
   }
 
@@ -82,9 +80,7 @@ static struct ip_vs_get_services *ipvs_get_services(void) {
 
   if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_SERVICES, services, &len) ==
       -1) {
-    char errbuf[1024];
-    log_err("ipvs_get_services: getsockopt failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_err("ipvs_get_services: getsockopt failed: %s", STRERRNO);
 
     free(services);
     return NULL;
@@ -111,9 +107,7 @@ static struct ip_vs_get_dests *ipvs_get_dests(struct ip_vs_service_entry *se) {
   dests->num_dests = se->num_dests;
 
   if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_DESTS, dests, &len) == -1) {
-    char errbuf[1024];
-    log_err("ipvs_get_dests: getsockopt() failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_err("ipvs_get_dests: getsockopt() failed: %s", STRERRNO);
     free(dests);
     return NULL;
   }
@@ -127,9 +121,7 @@ static int cipvs_init(void) {
   struct ip_vs_getinfo ipvs_info;
 
   if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
-    char errbuf[1024];
-    log_err("cipvs_init: socket() failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_err("cipvs_init: socket() failed: %s", STRERRNO);
     return -1;
   }
 
@@ -137,9 +129,7 @@ static int cipvs_init(void) {
 
   if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_INFO, &ipvs_info, &len) ==
       -1) {
-    char errbuf[1024];
-    log_err("cipvs_init: getsockopt() failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_err("cipvs_init: getsockopt() failed: %s", STRERRNO);
     close(sockfd);
     sockfd = -1;
     return -1;
index d540415..9156356 100644 (file)
--- a/src/irq.c
+++ b/src/irq.c
@@ -23,9 +23,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #if !KERNEL_LINUX
 #error "No applicable input method."
@@ -37,7 +37,7 @@
 static const char *config_keys[] = {"Irq", "IgnoreSelected"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static ignorelist_t *ignorelist = NULL;
+static ignorelist_t *ignorelist;
 
 /*
  * Private functions
@@ -90,9 +90,7 @@ static int irq_read(void) {
    */
   fh = fopen("/proc/interrupts", "r");
   if (fh == NULL) {
-    char errbuf[1024];
-    ERROR("irq plugin: fopen (/proc/interrupts): %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("irq plugin: fopen (/proc/interrupts): %s", STRERRNO);
     return -1;
   }
 
@@ -140,7 +138,7 @@ static int irq_read(void) {
     if (irq_name_len == 4 && (strncmp(irq_name, "FIQ:", 4) == 0))
       continue;
 
-    irq_name[irq_name_len - 1] = 0;
+    irq_name[irq_name_len - 1] = '\0';
     irq_name_len--;
 
     irq_value = 0;
index c7b0be3..cf301c6 100644 (file)
@@ -23,9 +23,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <jni.h>
 
@@ -77,23 +77,23 @@ typedef struct cjni_callback_info_s cjni_callback_info_t;
 /*
  * Global variables
  */
-static JavaVM *jvm = NULL;
+static JavaVM *jvm;
 static pthread_key_t jvm_env_key;
 
 /* Configuration options for the JVM. */
-static char **jvm_argv = NULL;
-static size_t jvm_argc = 0;
+static char **jvm_argv;
+static size_t jvm_argc;
 
 /* List of class names to load */
-static java_plugin_class_t *java_classes_list = NULL;
+static java_plugin_class_t *java_classes_list;
 static size_t java_classes_list_len;
 
 /* List of config, init, and shutdown callbacks. */
-static cjni_callback_info_t *java_callbacks = NULL;
-static size_t java_callbacks_num = 0;
+static cjni_callback_info_t *java_callbacks;
+static size_t java_callbacks_num;
 static pthread_mutex_t java_callbacks_lock = PTHREAD_MUTEX_INITIALIZER;
 
-static oconfig_item_t *config_block = NULL;
+static oconfig_item_t *config_block;
 
 /*
  * Prototypes
@@ -1009,9 +1009,8 @@ static int jtoc_values_array(JNIEnv *jvm_env, /* {{{ */
   jobjectArray o_number_array;
 
   value_t *values;
-  int values_num;
 
-  values_num = ds->ds_num;
+  size_t values_num = ds->ds_num;
 
   values = NULL;
   o_number_array = NULL;
@@ -1058,13 +1057,13 @@ static int jtoc_values_array(JNIEnv *jvm_env, /* {{{ */
     BAIL_OUT(-1);
   }
 
-  values = (value_t *)calloc(values_num, sizeof(value_t));
+  values = calloc(values_num, sizeof(*values));
   if (values == NULL) {
     ERROR("java plugin: jtoc_values_array: calloc failed.");
     BAIL_OUT(-1);
   }
 
-  for (int i = 0; i < values_num; i++) {
+  for (size_t i = 0; i < values_num; i++) {
     jobject o_number;
     int status;
 
@@ -1072,7 +1071,7 @@ static int jtoc_values_array(JNIEnv *jvm_env, /* {{{ */
         (*jvm_env)->GetObjectArrayElement(jvm_env, o_number_array, (jsize)i);
     if (o_number == NULL) {
       ERROR("java plugin: jtoc_values_array: "
-            "GetObjectArrayElement (%i) failed.",
+            "GetObjectArrayElement (%zu) failed.",
             i);
       BAIL_OUT(-1);
     }
@@ -1080,7 +1079,7 @@ static int jtoc_values_array(JNIEnv *jvm_env, /* {{{ */
     status = jtoc_value(jvm_env, values + i, ds->ds[i].type, o_number);
     if (status != 0) {
       ERROR("java plugin: jtoc_values_array: "
-            "jtoc_value (%i) failed.",
+            "jtoc_value (%zu) failed.",
             i);
       BAIL_OUT(-1);
     }
@@ -1840,7 +1839,8 @@ static int cjni_create_jvm(void) /* {{{ */
   vm_args.nOptions = (jint)jvm_argc;
 
   for (size_t i = 0; i < jvm_argc; i++) {
-    DEBUG("java plugin: cjni_create_jvm: jvm_argv[%zu] = %s", i, jvm_argv[i]);
+    DEBUG("java plugin: cjni_create_jvm: jvm_argv[%" PRIsz "] = %s", i,
+          jvm_argv[i]);
     vm_args.options[i].optionString = jvm_argv[i];
   }
 
@@ -2183,8 +2183,9 @@ static int cjni_config_perform(oconfig_item_t *ci) /* {{{ */
     }
   }
 
-  DEBUG("java plugin: jvm_argc = %zu;", jvm_argc);
-  DEBUG("java plugin: java_classes_list_len = %zu;", java_classes_list_len);
+  DEBUG("java plugin: jvm_argc = %" PRIsz ";", jvm_argc);
+  DEBUG("java plugin: java_classes_list_len = %" PRIsz ";",
+        java_classes_list_len);
 
   if ((success == 0) && (errors > 0)) {
     ERROR("java plugin: All statements failed.");
index d470324..69f9764 100644 (file)
  *   Florian octo Forster <octo at collectd.org>
  **/
 
+#ifdef WIN32
+#include "gnulib_config.h"
+#include <winsock2.h>
+#endif
+
 #include "config.h"
 
 #if !defined(__GNUC__) || !__GNUC__
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/socket.h>
 #include <sys/types.h>
-#include <sys/un.h>
 #include <unistd.h>
 
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
 #include "collectd/client.h"
 
 /* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11
 #endif
 #endif
 
+#ifdef WIN32
+#define AI_ADDRCONFIG 0
+#endif
+
 /* Secure/static macros. They work like `strcpy' and `strcat', but assure null
  * termination. They work for static buffers only, because they use `sizeof'.
  * The `SSTRCATF' combines the functionality of `snprintf' and `strcat' which
  * 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)
 
@@ -160,7 +172,7 @@ static char *sstrerror(int errnum, char *buf, size_t buflen) {
   }
 #endif /* STRERROR_R_CHAR_P */
 
-  buf[buflen - 1] = 0;
+  buf[buflen - 1] = '\0';
 
   return buf;
 } /* char *sstrerror */
@@ -171,7 +183,7 @@ static int lcc_set_errno(lcc_connection_t *c, int err) /* {{{ */
     return -1;
 
   sstrerror(err, c->errbuf, sizeof(c->errbuf));
-  c->errbuf[sizeof(c->errbuf) - 1] = 0;
+  c->errbuf[sizeof(c->errbuf) - 1] = '\0';
 
   return 0;
 } /* }}} int lcc_set_errno */
@@ -232,7 +244,7 @@ static void lcc_chomp(char *str) /* {{{ */
   while (str_len > 0) {
     if (str[str_len - 1] >= 32)
       break;
-    str[str_len - 1] = 0;
+    str[str_len - 1] = '\0';
     str_len--;
   }
 } /* }}} void lcc_chomp */
@@ -296,7 +308,7 @@ static int lcc_receive(lcc_connection_t *c, /* {{{ */
 
   /* Now copy the message. */
   strncpy(res.message, ptr, sizeof(res.message));
-  res.message[sizeof(res.message) - 1] = 0;
+  res.message[sizeof(res.message) - 1] = '\0';
 
   /* Error or no lines follow: We're done. */
   if (res.status <= 0) {
@@ -367,6 +379,10 @@ static int lcc_sendreceive(lcc_connection_t *c, /* {{{ */
 
 static int lcc_open_unixsocket(lcc_connection_t *c, const char *path) /* {{{ */
 {
+#ifdef WIN32
+  lcc_set_errno(c, ENOTSUP);
+  return -1;
+#else
   struct sockaddr_un sa = {0};
   int fd;
   int status;
@@ -401,6 +417,7 @@ static int lcc_open_unixsocket(lcc_connection_t *c, const char *path) /* {{{ */
   }
 
   return 0;
+#endif /* WIN32 */
 } /* }}} int lcc_open_unixsocket */
 
 static int lcc_open_netsocket(lcc_connection_t *c, /* {{{ */
@@ -607,7 +624,7 @@ int lcc_getval(lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */
 
   snprintf(command, sizeof(command), "GETVAL %s",
            lcc_strescape(ident_esc, ident_str, sizeof(ident_esc)));
-  command[sizeof(command) - 1] = 0;
+  command[sizeof(command) - 1] = '\0';
 
   /* Send talk to the daemon.. */
   status = lcc_sendreceive(c, command, &res);
@@ -913,7 +930,7 @@ int lcc_identifier_to_string(lcc_connection_t *c, /* {{{ */
                ident->type_instance);
   }
 
-  string[string_size - 1] = 0;
+  string[string_size - 1] = '\0';
   return 0;
 } /* }}} int lcc_identifier_to_string */
 
index c8a5da5..0b97362 100644 (file)
 #include <inttypes.h>
 #include <stdint.h>
 
+#ifdef WIN32
+extern unsigned int if_nametoindex(const char *interface_name);
+#endif
+
 #define NET_DEFAULT_V4_ADDR "239.192.74.66"
 #define NET_DEFAULT_V6_ADDR "ff18::efc0:4a42"
 #define NET_DEFAULT_PORT "25826"
@@ -60,7 +64,7 @@ int lcc_server_destroy(lcc_network_t *net, lcc_server_t *srv);
 
 /* Configure servers */
 int lcc_server_set_ttl(lcc_server_t *srv, uint8_t ttl);
-int lcc_server_set_interface(lcc_server_t *srv, char const *interface);
+int lcc_server_set_interface(lcc_server_t *srv, char const *iface);
 int lcc_server_set_security_level(lcc_server_t *srv, lcc_security_level_t level,
                                   const char *username, const char *password);
 
index ef6b792..e50df17 100644 (file)
@@ -74,7 +74,7 @@ typedef struct {
 
   /* interface is the name of the interface to use when subscribing to a
    * multicast group. Has no effect when using unicast. */
-  char *interface;
+  char *iface;
 } lcc_listener_t;
 
 /* lcc_listen_and_write listens on the provided UDP socket (or opens one using
index 49257d4..2d6b3ed 100644 (file)
 #include <net/if.h>
 #endif
 
+#ifdef WIN32
+#define AI_ADDRCONFIG 0
+#endif
+
 #include "collectd/network.h"
 #include "collectd/network_buffer.h"
 
@@ -364,15 +368,15 @@ int lcc_server_set_ttl(lcc_server_t *srv, uint8_t ttl) /* {{{ */
   return 0;
 } /* }}} int lcc_server_set_ttl */
 
-int lcc_server_set_interface(lcc_server_t *srv, char const *interface) /* {{{ */
+int lcc_server_set_interface(lcc_server_t *srv, char const *iface) /* {{{ */
 {
   unsigned int if_index;
   int status;
 
-  if ((srv == NULL) || (interface == NULL))
+  if ((srv == NULL) || (iface == NULL))
     return EINVAL;
 
-  if_index = if_nametoindex(interface);
+  if_index = if_nametoindex(iface);
   if (if_index == 0)
     return ENOENT;
 
@@ -420,8 +424,8 @@ int lcc_server_set_interface(lcc_server_t *srv, char const *interface) /* {{{ */
 
 /* else: Not a multicast interface. */
 #if defined(SO_BINDTODEVICE)
-  status = setsockopt(srv->fd, SOL_SOCKET, SO_BINDTODEVICE, interface,
-                      (socklen_t)(strlen(interface) + 1));
+  status = setsockopt(srv->fd, SOL_SOCKET, SO_BINDTODEVICE, iface,
+                      (socklen_t)(strlen(iface) + 1));
   if (status != 0)
     return -1;
 #endif
index a0f8cfd..44d93e0 100644 (file)
  *   Florian octo Forster <octo at collectd.org>
  **/
 
+#ifdef WIN32
+#include "gnulib_config.h"
+#endif
+
 #include "config.h"
 
 #include <arpa/inet.h> /* htons */
 #include <assert.h>
 #include <errno.h>
 #include <math.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -122,40 +127,40 @@ struct lcc_network_buffer_s {
 /*
  * Private functions
  */
-static _Bool have_gcrypt(void) /* {{{ */
+static bool have_gcrypt(void) /* {{{ */
 {
-  static _Bool result = 0;
-  static _Bool need_init = 1;
+  static bool result;
+  static bool need_init = true;
 
   if (!need_init)
     return result;
-  need_init = 0;
+  need_init = false;
 
 #if HAVE_GCRYPT_H
 #if GCRYPT_VERSION_NUMBER < 0x010600
   if (gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread))
-    return 0;
+    return false;
 #endif
 
   if (!gcry_check_version(GCRYPT_VERSION))
-    return 0;
+    return false;
 
   if (!gcry_control(GCRYCTL_INIT_SECMEM, 32768, 0))
-    return 0;
+    return false;
 
   gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
 
-  result = 1;
-  return 1;
+  result = true;
+  return true;
 #else
-  return 0;
+  return false;
 #endif
-} /* }}} _Bool have_gcrypt */
+} /* }}} bool have_gcrypt */
 
 #ifndef HAVE_HTONLL
 static uint64_t htonll(uint64_t val) /* {{{ */
 {
-  static int config = 0;
+  static int config;
 
   uint32_t hi;
   uint32_t lo;
@@ -185,7 +190,7 @@ static uint64_t htonll(uint64_t val) /* {{{ */
 
 static double htond(double val) /* {{{ */
 {
-  static int config = 0;
+  static int config;
 
   union {
     uint8_t byte[8];
index aa753ce..4c610bb 100644 (file)
 
 #include "collectd/lcc_features.h"
 #include "collectd/network_parse.h"
+#include "globals.h"
 
 #include <errno.h>
 #include <math.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -67,7 +69,7 @@ static int network_parse(void *data, size_t data_size, lcc_security_level_t sl,
                          lcc_network_parse_options_t const *opts);
 
 #if HAVE_GCRYPT_H
-static int init_gcrypt() {
+static int init_gcrypt(void) {
   /* http://lists.gnupg.org/pipermail/gcrypt-devel/2003-August/000458.html
    * Because you can't know in a library whether another library has
    * already initialized the library */
@@ -152,11 +154,12 @@ static int parse_string(void *payload, size_t payload_size, char *out,
                         size_t out_size) {
   char *in = payload;
 
-  if ((payload_size < 1) || (in[payload_size - 1] != 0) ||
+  if ((payload_size < 1) || (in[payload_size - 1] != '\0') ||
       (payload_size > out_size))
     return EINVAL;
 
-  strncpy(out, in, out_size);
+  strncpy(out, in, out_size - 1);
+  out[out_size - 1] = '\0';
   return 0;
 }
 
@@ -219,7 +222,7 @@ static int parse_time(uint16_t type, void *payload, size_t payload_size,
 
 static double ntohd(double val) /* {{{ */
 {
-  static int config = 0;
+  static int config;
 
   union {
     uint8_t byte[8];
@@ -301,8 +304,8 @@ static int parse_values(void *payload, size_t payload_size,
     return EINVAL;
 
   state->values_len = (size_t)n;
-  state->values = calloc(sizeof(*state->values), state->values_len);
-  state->values_types = calloc(sizeof(*state->values_types), state->values_len);
+  state->values = calloc(state->values_len, sizeof(*state->values));
+  state->values_types = calloc(state->values_len, sizeof(*state->values_types));
   if ((state->values == NULL) || (state->values_types == NULL)) {
     return ENOMEM;
   }
@@ -434,7 +437,7 @@ static int decrypt_aes256(buffer_t *b, void *iv, size_t iv_size,
   uint8_t pwhash[32] = {0};
   gcry_md_hash_buffer(GCRY_MD_SHA256, pwhash, password, strlen(password));
 
-  fprintf(stderr, "sizeof(iv) = %zu\n", sizeof(iv));
+  fprintf(stderr, "sizeof(iv) = %" PRIsz "\n", sizeof(iv));
   if (gcry_cipher_setkey(cipher, pwhash, sizeof(pwhash)) ||
       gcry_cipher_setiv(cipher, iv, iv_size) ||
       gcry_cipher_decrypt(cipher, b->data, b->len, /* in = */ NULL,
@@ -519,7 +522,7 @@ static int network_parse(void *data, size_t data_size, lcc_security_level_t sl,
 
     if ((sz < 5) || (((size_t)sz - 4) > b->len)) {
       DEBUG("lcc_network_parse(): invalid 'sz' field: sz = %" PRIu16
-            ", b->len = %zu\n",
+            ", b->len = %" PRIsz "\n",
             sz, b->len);
       return EINVAL;
     }
index 07010fb..a638642 100644 (file)
@@ -245,10 +245,9 @@ static int test_network_parse() {
     uint8_t buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
     size_t buffer_size = sizeof(buffer);
     if (decode_string(raw_packet_data[i], buffer, &buffer_size)) {
-      fprintf(
-          stderr,
-          "lcc_network_parse(raw_packet_data[%zu]): decoding string failed\n",
-          i);
+      fprintf(stderr, "lcc_network_parse(raw_packet_data[%" PRIsz "]):"
+                      " decoding string failed\n",
+              i);
       return -1;
     }
 
@@ -257,12 +256,13 @@ static int test_network_parse() {
                                                    .writer = nop_writer,
                                                });
     if (status != 0) {
-      fprintf(stderr, "lcc_network_parse(raw_packet_data[%zu]) = %d, want 0\n",
+      fprintf(stderr,
+              "lcc_network_parse(raw_packet_data[%" PRIsz "]) = %d, want 0\n",
               i, status);
       ret = status;
     }
 
-    printf("ok - lcc_network_parse(raw_packet_data[%zu])\n", i);
+    printf("ok - lcc_network_parse(raw_packet_data[%" PRIsz "])\n", i);
   }
 
   return ret;
@@ -376,7 +376,7 @@ static int test_parse_values() {
   }
 
   if (vl.values_len != 3) {
-    fprintf(stderr, "parse_values(): vl.values_len = %zu, want 3\n",
+    fprintf(stderr, "parse_values(): vl.values_len = %" PRIsz ", want 3\n",
             vl.values_len);
     return -1;
   }
@@ -384,7 +384,8 @@ static int test_parse_values() {
   int want_types[] = {LCC_TYPE_GAUGE, LCC_TYPE_DERIVE, LCC_TYPE_GAUGE};
   for (size_t i = 0; i < sizeof(want_types) / sizeof(want_types[0]); i++) {
     if (vl.values_types[i] != want_types[i]) {
-      fprintf(stderr, "parse_values(): vl.values_types[%zu] = %d, want %d\n", i,
+      fprintf(stderr,
+              "parse_values(): vl.values_types[%" PRIsz "] = %d, want %d\n", i,
               vl.values_types[i], want_types[i]);
       ret = -1;
     }
index a81afda..629c367 100644 (file)
  *   Florian octo Forster <octo at collectd.org>
  **/
 
+#ifdef WIN32
+#include "gnulib_config.h"
+#endif
+
 #include "config.h"
 
 #if !defined(__GNUC__) || !__GNUC__
 #include "collectd/network_parse.h" /* for lcc_network_parse_options_t */
 #include "collectd/server.h"
 
+// clang-format off
 #include <errno.h>
-#include <net/if.h>
-#include <netdb.h>
-#include <netinet/in.h>
+#include <stdbool.h>
 #include <string.h>
+#include <unistd.h>
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <unistd.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+// clang-format on
 
 #include <stdio.h>
 #define DEBUG(...) printf(__VA_ARGS__)
 
-static _Bool is_multicast(struct addrinfo const *ai) {
+#ifdef WIN32
+#include <ws2tcpip.h>
+#define AI_ADDRCONFIG 0
+#endif
+
+static bool is_multicast(struct addrinfo const *ai) {
   if (ai->ai_family == AF_INET) {
     struct sockaddr_in *addr = (struct sockaddr_in *)ai->ai_addr;
     return IN_MULTICAST(ntohl(addr->sin_addr.s_addr));
@@ -78,13 +90,20 @@ static int server_multicast_join(lcc_listener_t *srv,
     struct ip_mreqn mreq = {
         .imr_address.s_addr = INADDR_ANY,
         .imr_multiaddr.s_addr = sa->sin_addr.s_addr,
-        .imr_ifindex = if_nametoindex(srv->interface),
+        .imr_ifindex = if_nametoindex(srv->iface),
     };
 #else
+#ifdef WIN32
     struct ip_mreq mreq = {
+        .imr_interface.s_addr = INADDR_ANY,
         .imr_multiaddr.s_addr = sa->sin_addr.s_addr,
     };
-#endif
+#else
+    struct ip_mreq mreq = {
+        .imr_multiaddr.s_addr = sa->sin_addr.s_addr,
+    };
+#endif /* WIN32 */
+#endif /* HAVE_STRUCT_IP_MREQN_IMR_IFINDEX */
     status = setsockopt(srv->conn, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
                         sizeof(mreq));
     if (status == -1)
@@ -103,7 +122,7 @@ static int server_multicast_join(lcc_listener_t *srv,
       return errno;
 
     struct ipv6_mreq mreq6 = {
-        .ipv6mr_interface = if_nametoindex(srv->interface),
+        .ipv6mr_interface = if_nametoindex(srv->iface),
     };
     memmove(&mreq6.ipv6mr_multiaddr, &sa->sin6_addr, sizeof(struct in6_addr));
 
@@ -175,7 +194,7 @@ static int server_open(lcc_listener_t *srv) {
 }
 
 int lcc_listen_and_write(lcc_listener_t srv) {
-  _Bool close_socket = 0;
+  bool close_socket = 0;
 
   if (srv.conn < 0) {
     int status = server_open(&srv);
index 0ffda3a..e05ced8 100644 (file)
@@ -115,8 +115,7 @@ oconfig_item_t *oconfig_clone(const oconfig_item_t *ci_orig) {
 
   if (ci_orig->values_num > 0) /* {{{ */
   {
-    ci_copy->values = (oconfig_value_t *)calloc((size_t)ci_orig->values_num,
-                                                sizeof(*ci_copy->values));
+    ci_copy->values = calloc(ci_orig->values_num, sizeof(*ci_copy->values));
     if (ci_copy->values == NULL) {
       fprintf(stderr, "calloc failed.\n");
       free(ci_copy->key);
@@ -144,8 +143,8 @@ oconfig_item_t *oconfig_clone(const oconfig_item_t *ci_orig) {
 
   if (ci_orig->children_num > 0) /* {{{ */
   {
-    ci_copy->children = (oconfig_item_t *)calloc((size_t)ci_orig->children_num,
-                                                 sizeof(*ci_copy->children));
+    ci_copy->children =
+        calloc(ci_orig->children_num, sizeof(*ci_copy->children));
     if (ci_copy->children == NULL) {
       fprintf(stderr, "calloc failed.\n");
       oconfig_free(ci_copy);
index 90f51de..1b2d6cc 100644 (file)
@@ -265,21 +265,19 @@ static void yyerror(const char *s)
 static char *unquote (const char *orig)
 {
        char *ret = strdup (orig);
-       int len;
-
        if (ret == NULL)
-               return (NULL);
+               return NULL;
 
-       len = strlen (ret);
+       size_t len = strlen (ret);
 
        if ((len < 2) || (ret[0] != '"') || (ret[len - 1] != '"'))
-               return (ret);
+               return ret;
 
        len -= 2;
        memmove (ret, ret + 1, len);
        ret[len] = 0;
 
-       for (int i = 0; i < len; i++)
+       for (size_t i = 0; i < len; i++)
        {
                if (ret[i] == '\\')
                {
@@ -288,5 +286,5 @@ static char *unquote (const char *orig)
                }
        }
 
-       return (ret);
+       return ret;
 } /* char *unquote */
index 4858003..7efa78a 100644 (file)
  */
 
 %{
+#ifdef WIN32
+#include "gnulib_config.h"
+#include "config.h"
+#endif
+
 #include <stdlib.h>
 #include <string.h>
 #include "oconfig.h"
@@ -40,9 +45,9 @@
 
 
 /* multiline string buffer */
-static char *ml_buffer = NULL;
-static int   ml_pos    = 0;
-static int   ml_len    = 0;
+static char *ml_buffer;
+static size_t ml_pos;
+static size_t ml_len;
 
 #define ml_free (ml_len - ml_pos)
 
@@ -110,12 +115,12 @@ IPV6_ADDR ({IPV6_BASE})|(\[{IPV6_BASE}\](:{PORT})?)
 {UNQUOTED_STRING}      {yylval.string = yytext; return (UNQUOTED_STRING);}
 
 \"{QUOTED_STRING}\\{EOL} {
-       int len = strlen (yytext);
+       size_t len = strlen (yytext);
 
        ml_pos = 0;
 
        /* remove "\\<EOL>" */
-       if ('\r' == yytext[len - 2])
+       if (yytext[len - 2] == '\r')
                len -= 3;
        else
                len -= 2;
@@ -126,10 +131,10 @@ IPV6_ADDR ({IPV6_BASE})|(\[{IPV6_BASE}\](:{PORT})?)
 }
 <ML>^{WHITE_SPACE}+ {/* remove leading white-space */}
 <ML>{NON_WHITE_SPACE}{QUOTED_STRING}\\{EOL} {
-       int len = strlen (yytext);
+       size_t len = strlen (yytext);
 
        /* remove "\\<EOL>" */
-       if ('\r' == yytext[len - 2])
+       if (yytext[len - 2] == '\r')
                len -= 3;
        else
                len -= 2;
@@ -147,18 +152,17 @@ IPV6_ADDR ({IPV6_BASE})|(\[{IPV6_BASE}\](:{PORT})?)
 %%
 static void ml_append (char *string)
 {
-       int len = strlen (string);
-       int s;
+       size_t len = strlen (string);
 
        if (ml_free <= len) {
                ml_len += len - ml_free + 1;
                ml_buffer = realloc (ml_buffer, ml_len);
-               if (NULL == ml_buffer)
+               if (ml_buffer == NULL)
                        YY_FATAL_ERROR ("out of dynamic memory in ml_append");
        }
 
-       s = snprintf (ml_buffer + ml_pos, ml_free, "%s", string);
-       if ((0 > s) || (ml_free <= s))
+       int s = snprintf(ml_buffer + ml_pos, ml_free, "%s", string);
+       if (s < 0 || (size_t)s >= ml_free)
                YY_FATAL_ERROR ("failed to write to multiline buffer");
 
        ml_pos += s;
index d6584d5..da7fe58 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <unistd.h>
 
@@ -55,7 +55,7 @@
 #include <sys/protosw.h>
 #endif /* HAVE_PERFSTAT */
 
-static _Bool report_relative_load = 0;
+static bool report_relative_load;
 
 static const char *config_keys[] = {"ReportRelative"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
@@ -63,7 +63,7 @@ static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 static int load_config(const char *key, const char *value) {
   if (strcasecmp(key, "ReportRelative") == 0)
 #ifdef _SC_NPROCESSORS_ONLN
-    report_relative_load = IS_TRUE(value) ? 1 : 0;
+    report_relative_load = IS_TRUE(value);
 #else
     WARNING("load plugin: The \"ReportRelative\" configuration "
             "is not available, because I can't determine the "
@@ -73,13 +73,11 @@ static int load_config(const char *key, const char *value) {
 }
 static void load_submit(gauge_t snum, gauge_t mnum, gauge_t lnum) {
   int cores = 0;
-  char errbuf[1024];
 
 #ifdef _SC_NPROCESSORS_ONLN
   if (report_relative_load) {
     if ((cores = sysconf(_SC_NPROCESSORS_ONLN)) < 1) {
-      WARNING("load: sysconf failed : %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("load: sysconf failed : %s", STRERRNO);
     }
   }
 #endif
@@ -114,9 +112,7 @@ static int load_read(void) {
   if (getloadavg(load, 3) == 3)
     load_submit(load[LOADAVG_1MIN], load[LOADAVG_5MIN], load[LOADAVG_15MIN]);
   else {
-    char errbuf[1024];
-    WARNING("load: getloadavg failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("load: getloadavg failed: %s", STRERRNO);
   }
 /* #endif HAVE_GETLOADAVG */
 
@@ -129,21 +125,18 @@ static int load_read(void) {
   int numfields;
 
   if ((loadavg = fopen("/proc/loadavg", "r")) == NULL) {
-    char errbuf[1024];
-    WARNING("load: fopen: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("load: fopen: %s", STRERRNO);
     return -1;
   }
 
   if (fgets(buffer, 16, loadavg) == NULL) {
-    char errbuf[1024];
-    WARNING("load: fgets: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("load: fgets: %s", STRERRNO);
     fclose(loadavg);
     return -1;
   }
 
   if (fclose(loadavg)) {
-    char errbuf[1024];
-    WARNING("load: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("load: fclose: %s", STRERRNO);
   }
 
   numfields = strsplit(buffer, fields, 8);
@@ -177,9 +170,7 @@ static int load_read(void) {
 
   if (perfstat_cpu_total(NULL, &cputotal, sizeof(perfstat_cpu_total_t), 1) <
       0) {
-    char errbuf[1024];
-    WARNING("load: perfstat_cpu : %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("load: perfstat_cpu : %s", STRERRNO);
     return -1;
   }
 
index 8ff9045..b04aadd 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <sys/types.h>
 #include <yajl/yajl_common.h>
@@ -49,7 +49,7 @@ static int log_level = LOG_INFO;
 
 static pthread_mutex_t file_lock = PTHREAD_MUTEX_INITIALIZER;
 
-static char *log_file = NULL;
+static char *log_file;
 
 static const char *config_keys[] = {"LogLevel", "File"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
@@ -75,7 +75,7 @@ static int log_logstash_config(const char *key, const char *value) {
 static void log_logstash_print(yajl_gen g, int severity,
                                cdtime_t timestamp_time) {
   FILE *fh;
-  _Bool do_close = 0;
+  bool do_close = false;
   struct tm timestamp_tm;
   char timestamp_str[64];
   const unsigned char *buf;
@@ -150,19 +150,18 @@ static void log_logstash_print(yajl_gen g, int severity,
     fh = stderr;
   } else if (strcasecmp(log_file, "stdout") == 0) {
     fh = stdout;
-    do_close = 0;
+    do_close = false;
   } else if (strcasecmp(log_file, "stderr") == 0) {
     fh = stderr;
-    do_close = 0;
+    do_close = false;
   } else {
     fh = fopen(log_file, "a");
-    do_close = 1;
+    do_close = true;
   }
 
   if (fh == NULL) {
-    char errbuf[1024];
     fprintf(stderr, "log_logstash plugin: fopen (%s) failed: %s\n", log_file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
   } else {
     fprintf(fh, "%s\n", buf);
     if (do_close) {
index de9b1f7..3a25319 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if COLLECT_DEBUG
 static int log_level = LOG_DEBUG;
@@ -39,9 +39,9 @@ static int log_level = LOG_INFO;
 
 static pthread_mutex_t file_lock = PTHREAD_MUTEX_INITIALIZER;
 
-static char *log_file = NULL;
+static char *log_file;
 static int print_timestamp = 1;
-static int print_severity = 0;
+static int print_severity;
 
 static const char *config_keys[] = {"LogLevel", "File", "Timestamp",
                                     "PrintSeverity"};
@@ -77,7 +77,7 @@ static int logfile_config(const char *key, const char *value) {
 static void logfile_print(const char *msg, int severity,
                           cdtime_t timestamp_time) {
   FILE *fh;
-  _Bool do_close = 0;
+  bool do_close = false;
   char timestamp_str[64];
   char level_str[16] = "";
 
@@ -122,13 +122,12 @@ static void logfile_print(const char *msg, int severity,
     fh = stdout;
   else {
     fh = fopen(log_file, "a");
-    do_close = 1;
+    do_close = true;
   }
 
   if (fh == NULL) {
-    char errbuf[1024];
     fprintf(stderr, "logfile plugin: fopen (%s) failed: %s\n", log_file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
   } else {
     if (print_timestamp)
       fprintf(fh, "[%s] %s%s\n", timestamp_str, level_str, msg);
index 3ecf049..dc3739b 100644 (file)
@@ -21,8 +21,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <libperfstat.h>
 #include <sys/protosw.h>
 static const char *config_keys[] = {"CpuPoolStats", "ReportBySerial"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static _Bool pool_stats = 0;
-static _Bool report_by_serial = 0;
+static bool pool_stats;
+static bool report_by_serial;
 #if PERFSTAT_SUPPORTS_DONATION
-static _Bool donate_flag = 0;
+static bool donate_flag;
 #endif
 static char serial[SYS_NMLN];
 
@@ -52,14 +52,14 @@ static perfstat_partition_total_t lparstats_old;
 static int lpar_config(const char *key, const char *value) {
   if (strcasecmp("CpuPoolStats", key) == 0) {
     if (IS_TRUE(value))
-      pool_stats = 1;
+      pool_stats = true;
     else
-      pool_stats = 0;
+      pool_stats = false;
   } else if (strcasecmp("ReportBySerial", key) == 0) {
     if (IS_TRUE(value))
-      report_by_serial = 1;
+      report_by_serial = true;
     else
-      report_by_serial = 0;
+      report_by_serial = false;
   } else {
     return -1;
   }
@@ -76,23 +76,22 @@ static int lpar_init(void) {
                                     sizeof(perfstat_partition_total_t),
                                     /* number = */ 1 /* (must be 1) */);
   if (status != 1) {
-    char errbuf[1024];
-    ERROR("lpar plugin: perfstat_partition_total failed: %s (%i)",
-          sstrerror(errno, errbuf, sizeof(errbuf)), status);
+    ERROR("lpar plugin: perfstat_partition_total failed: %s (%i)", STRERRNO,
+          status);
     return -1;
   }
 
 #if PERFSTAT_SUPPORTS_DONATION
   if (!lparstats_old.type.b.shared_enabled &&
       lparstats_old.type.b.donate_enabled) {
-    donate_flag = 1;
+    donate_flag = true;
   }
 #endif
 
   if (pool_stats && !lparstats_old.type.b.pool_util_authority) {
     WARNING("lpar plugin: This partition does not have pool authority. "
             "Disabling CPU pool statistics collection.");
-    pool_stats = 0;
+    pool_stats = false;
   }
 
   return 0;
@@ -138,9 +137,8 @@ static int lpar_read(void) {
                                &lparstats, sizeof(perfstat_partition_total_t),
                                /* number = */ 1 /* (must be 1) */);
   if (status != 1) {
-    char errbuf[1024];
-    ERROR("lpar plugin: perfstat_partition_total failed: %s (%i)",
-          sstrerror(errno, errbuf, sizeof(errbuf)), status);
+    ERROR("lpar plugin: perfstat_partition_total failed: %s (%i)", STRERRNO,
+          status);
     return -1;
   }
 
index 8cfb704..45b5b18 100644 (file)
--- a/src/lua.c
+++ b/src/lua.c
  *   Ruben Kerkhof <ruben at rubenkerkhof.com>
  **/
 
-/* <lua5.1/luaconf.h> defines a macro using "sprintf". Although not used here,
- * GCC will complain about the macro definition. */
-#define DONT_POISON_SPRINTF_YET
-
-#include "common.h"
-#include "plugin.h"
 #include "collectd.h"
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils_lua.h"
 
 /* Include the Lua API header files. */
 #include <lauxlib.h>
 #include <lua.h>
 #include <lualib.h>
-#include "utils_lua.h"
 
 #include <pthread.h>
 
-#if COLLECT_DEBUG && __GNUC__
-#undef sprintf
-#pragma GCC poison sprintf
-#endif
-
 typedef struct lua_script_s {
   char *script_path;
   lua_State *lua_state;
@@ -57,7 +48,7 @@ typedef struct lua_script_s {
 
 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;
@@ -272,6 +263,12 @@ static int lua_cb_dispatch_values(lua_State *L) /* {{{ */
   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);
@@ -303,13 +300,14 @@ static int lua_cb_register_read(lua_State *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");
@@ -350,7 +348,7 @@ static int lua_cb_register_write(lua_State *L) /* {{{ */
   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)
index 3ec79de..c30489c 100644 (file)
--- a/src/lvm.c
+++ b/src/lvm.c
@@ -23,8 +23,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <lvm2app.h>
 
index 1a387b7..85454c3 100644 (file)
@@ -88,9 +88,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #include <dirent.h>
 #include <sys/ioctl.h>
@@ -99,8 +99,8 @@
 #error "No applicable input method."
 #endif
 
-#include <linux/wireless.h>
 #include "madwifi.h"
+#include <linux/wireless.h>
 
 struct stat_spec {
   uint16_t flags;
@@ -347,10 +347,10 @@ static const char *config_keys[] = {"Interface", "IgnoreSelected", "Source",
                                     "MiscAdd",   "MiscRemove",     "MiscSet"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static ignorelist_t *ignorelist = NULL;
+static ignorelist_t *ignorelist;
 
 static int use_sysfs = 1;
-static int init_state = 0;
+static int init_state;
 
 static inline int item_watched(int i) {
   assert(i >= 0);
index 27817fe..fd87b38 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
+#include "utils/common/common.h"
 
 /*
  * internal helper functions
index c0554b2..4911ee2 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
+#include "utils/common/common.h"
 
 /*
  * private data types
index dd4018b..4052ad5 100644 (file)
@@ -33,9 +33,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
-#include "meta_data.h"
+#include "utils/common/common.h"
+#include "utils/metadata/meta_data.h"
 #include "utils_llist.h"
 
 #include <regex.h>
@@ -66,7 +66,7 @@ struct mr_match_s {
   mr_regex_t *type;
   mr_regex_t *type_instance;
   llist_t *meta; /* Maps each meta key into mr_regex_t* */
-  _Bool invert;
+  bool invert;
 };
 
 /*
@@ -151,7 +151,7 @@ static int mr_add_regex(mr_regex_t **re_head, const char *re_str, /* {{{ */
   if (status != 0) {
     char errmsg[1024];
     regerror(status, &re->re, errmsg, sizeof(errmsg));
-    errmsg[sizeof(errmsg) - 1] = 0;
+    errmsg[sizeof(errmsg) - 1] = '\0';
     log_err("Compiling regex `%s' for `%s' failed: %s.", re->re_str, option,
             errmsg);
     sfree(re->re_str);
@@ -245,7 +245,7 @@ static int mr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
     return -ENOMEM;
   }
 
-  m->invert = 0;
+  m->invert = false;
 
   status = 0;
   for (int i = 0; i < ci->children_num; i++) {
index c80694d..172b312 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
+#include "utils/common/common.h"
 
 #define SATISFY_ALL 0
 #define SATISFY_ANY 1
index 44cea26..7b9da69 100644 (file)
@@ -31,8 +31,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 
 #define SATISFY_ALL 0
index b5352bf..50955f3 100644 (file)
@@ -25,8 +25,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <netdb.h>
 #include <netinet/in.h>
@@ -38,8 +38,8 @@
 static const char *config_keys[] = {"Host", "Port", NULL};
 static int config_keys_num = 2;
 
-static char *mbmon_host = NULL;
-static char *mbmon_port = NULL;
+static char *mbmon_host;
+static char *mbmon_port;
 
 /*
  * NAME
@@ -95,10 +95,8 @@ static int mbmon_query_daemon(char *buffer, int buffer_size) {
                               .ai_socktype = SOCK_STREAM};
 
   if ((ai_return = getaddrinfo(host, port, &ai_hints, &ai_list)) != 0) {
-    char errbuf[1024];
     ERROR("mbmon: getaddrinfo (%s, %s): %s", host, port,
-          (ai_return == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                    : gai_strerror(ai_return));
+          (ai_return == EAI_SYSTEM) ? STRERRNO : gai_strerror(ai_return));
     return -1;
   }
 
@@ -108,16 +106,13 @@ static int mbmon_query_daemon(char *buffer, int buffer_size) {
     /* create our socket descriptor */
     if ((fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype,
                      ai_ptr->ai_protocol)) < 0) {
-      char errbuf[1024];
-      ERROR("mbmon: socket: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("mbmon: socket: %s", STRERRNO);
       continue;
     }
 
     /* connect to the mbmon daemon */
     if (connect(fd, (struct sockaddr *)ai_ptr->ai_addr, ai_ptr->ai_addrlen)) {
-      char errbuf[1024];
-      INFO("mbmon: connect (%s, %s): %s", host, port,
-           sstrerror(errno, errbuf, sizeof(errbuf)));
+      INFO("mbmon: connect (%s, %s): %s", host, port, STRERRNO);
       close(fd);
       fd = -1;
       continue;
@@ -142,13 +137,11 @@ static int mbmon_query_daemon(char *buffer, int buffer_size) {
   while ((status = read(fd, buffer + buffer_fill, buffer_size - buffer_fill)) !=
          0) {
     if (status == -1) {
-      char errbuf[1024];
 
       if ((errno == EAGAIN) || (errno == EINTR))
         continue;
 
-      ERROR("mbmon: Error reading from socket: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("mbmon: Error reading from socket: %s", STRERRNO);
       close(fd);
       return -1;
     }
index 6fd4623..0cce0c6 100644 (file)
@@ -31,7 +31,7 @@
 
 #include "collectd.h"
 
-#include "common.h"
+#include "utils/common/common.h"
 #include "utils_llist.h"
 
 #include <poll.h>
@@ -56,7 +56,7 @@ typedef struct mcelog_config_s {
   pthread_t tid;              /* poll thread id */
   llist_t *dimms_list;        /* DIMMs list */
   pthread_mutex_t dimms_lock; /* lock for dimms cache */
-  _Bool persist;
+  bool persist;
 } mcelog_config_t;
 
 typedef struct socket_adapter_s socket_adapter_t;
@@ -90,7 +90,7 @@ static int socket_reinit(socket_adapter_t *self);
 static int socket_receive(socket_adapter_t *self, FILE **p_file);
 
 static mcelog_config_t g_mcelog_config = {
-    .logfile = "/var/log/mcelog", .persist = 0,
+    .logfile = "/var/log/mcelog", .persist = false,
 };
 
 static socket_adapter_t socket_adapter = {
@@ -106,8 +106,8 @@ static socket_adapter_t socket_adapter = {
     .receive = socket_receive,
 };
 
-static _Bool mcelog_thread_running;
-static _Bool mcelog_apply_defaults;
+static bool mcelog_thread_running;
+static bool mcelog_apply_defaults;
 
 static void mcelog_free_dimms_list_records(llist_t *dimms_list) {
 
@@ -237,15 +237,12 @@ static int socket_close(socket_adapter_t *self) {
   int ret = 0;
   pthread_rwlock_rdlock(&self->lock);
   if (fcntl(self->sock_fd, F_GETFL) != -1) {
-    char errbuf[MCELOG_BUFF_SIZE];
     if (shutdown(self->sock_fd, SHUT_RDWR) != 0) {
-      ERROR(MCELOG_PLUGIN ": Socket shutdown failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR(MCELOG_PLUGIN ": Socket shutdown failed: %s", STRERRNO);
       ret = -1;
     }
     if (close(self->sock_fd) != 0) {
-      ERROR(MCELOG_PLUGIN ": Socket close failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR(MCELOG_PLUGIN ": Socket close failed: %s", STRERRNO);
       ret = -1;
     }
   }
@@ -277,7 +274,6 @@ static void mcelog_dispatch_notification(notification_t *n) {
 }
 
 static int socket_reinit(socket_adapter_t *self) {
-  char errbuff[MCELOG_BUFF_SIZE];
   int ret = -1;
   cdtime_t interval = plugin_get_interval();
   struct timeval socket_timeout = CDTIME_T_TO_TIMEVAL(interval);
@@ -287,8 +283,7 @@ static int socket_reinit(socket_adapter_t *self) {
   self->sock_fd =
       socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
   if (self->sock_fd < 0) {
-    ERROR(MCELOG_PLUGIN ": Could not create a socket. %s",
-          sstrerror(errno, errbuff, sizeof(errbuff)));
+    ERROR(MCELOG_PLUGIN ": Could not create a socket. %s", STRERRNO);
     pthread_rwlock_unlock(&self->lock);
     return ret;
   }
@@ -304,8 +299,7 @@ static int socket_reinit(socket_adapter_t *self) {
   pthread_rwlock_rdlock(&self->lock);
   if (connect(self->sock_fd, (struct sockaddr *)&(self->unix_sock),
               sizeof(self->unix_sock)) < 0) {
-    ERROR(MCELOG_PLUGIN ": Failed to connect to mcelog server. %s",
-          sstrerror(errno, errbuff, sizeof(errbuff)));
+    ERROR(MCELOG_PLUGIN ": Failed to connect to mcelog server. %s", STRERRNO);
     self->close(self);
     ret = -1;
   } else {
@@ -534,9 +528,7 @@ static int socket_receive(socket_adapter_t *self, FILE **pp_file) {
 
   if ((res = poll(&poll_fd, 1, MCELOG_POLL_TIMEOUT)) <= 0) {
     if (res != 0 && errno != EINTR) {
-      char errbuf[MCELOG_BUFF_SIZE];
-      ERROR("mcelog: poll failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("mcelog: poll failed: %s", STRERRNO);
     }
     pthread_rwlock_unlock(&self->lock);
     return res;
@@ -571,12 +563,10 @@ static int socket_receive(socket_adapter_t *self, FILE **pp_file) {
 }
 
 static void *poll_worker(__attribute__((unused)) void *arg) {
-  char errbuf[MCELOG_BUFF_SIZE];
   mcelog_thread_running = 1;
   FILE **pp_file = calloc(1, sizeof(*pp_file));
   if (pp_file == NULL) {
-    ERROR("mcelog: memory allocation failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("mcelog: memory allocation failed: %s", STRERRNO);
     pthread_exit((void *)1);
   }
 
index 016e6b0..643dabd 100644 (file)
--- a/src/md.c
+++ b/src/md.c
@@ -21,9 +21,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #include <sys/ioctl.h>
 
@@ -40,7 +40,7 @@
 static const char *config_keys[] = {"Device", "IgnoreSelected"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static ignorelist_t *ignorelist = NULL;
+static ignorelist_t *ignorelist;
 
 static int md_config(const char *key, const char *value) {
   if (ignorelist == NULL)
@@ -74,7 +74,6 @@ static void md_submit(const int minor, const char *type_instance,
 } /* void md_submit */
 
 static void md_process(const int minor, const char *path) {
-  char errbuf[1024];
   int fd;
   struct stat st;
   mdu_array_info_t array;
@@ -82,13 +81,12 @@ static void md_process(const int minor, const char *path) {
 
   fd = open(path, O_RDONLY);
   if (fd < 0) {
-    WARNING("md: open(%s): %s", path, sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("md: open(%s): %s", path, STRERRNO);
     return;
   }
 
   if (fstat(fd, &st) < 0) {
-    WARNING("md: Unable to fstat file descriptor for %s: %s", path,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("md: Unable to fstat file descriptor for %s: %s", path, STRERRNO);
     close(fd);
     return;
   }
@@ -109,8 +107,7 @@ static void md_process(const int minor, const char *path) {
 
   /* Retrieve md information */
   if (ioctl(fd, GET_ARRAY_INFO, &array) < 0) {
-    WARNING("md: Unable to retrieve array info from %s: %s", path,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("md: Unable to retrieve array info from %s: %s", path, STRERRNO);
     close(fd);
     return;
   }
@@ -146,9 +143,7 @@ static int md_read(void) {
 
   fh = fopen(PROC_DISKSTATS, "r");
   if (fh == NULL) {
-    char errbuf[1024];
-    WARNING("md: Unable to open %s: %s", PROC_DISKSTATS,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("md: Unable to open %s: %s", PROC_DISKSTATS, STRERRNO);
     return -1;
   }
 
index bd088ec..eefcfb7 100644 (file)
@@ -23,9 +23,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_match.h"
+#include "utils/common/common.h"
+#include "utils/match/match.h"
 
 #include <libmemcached/memcached.h>
 
@@ -68,7 +68,7 @@ struct web_page_s /* {{{ */
 /*
  * Global variables;
  */
-static web_page_t *pages_g = NULL;
+static web_page_t *pages_g;
 
 /*
  * Private functions
@@ -123,21 +123,6 @@ static int cmc_page_init_memc(web_page_t *wp) /* {{{ */
   return 0;
 } /* }}} int cmc_page_init_memc */
 
-static int cmc_config_add_string(const char *name, char **dest, /* {{{ */
-                                 oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("memcachec plugin: `%s' needs exactly one string argument.", name);
-    return -1;
-  }
-
-  sfree(*dest);
-  *dest = strdup(ci->values[0].value.string);
-  if (*dest == NULL)
-    return -1;
-
-  return 0;
-} /* }}} int cmc_config_add_string */
-
 static int cmc_config_add_match_dstype(int *dstype_ret, /* {{{ */
                                        oconfig_item_t *ci) {
   int dstype;
@@ -204,16 +189,15 @@ static int cmc_config_add_match(web_page_t *page, /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("Regex", child->key) == 0)
-      status = cmc_config_add_string("Regex", &match->regex, child);
+      status = cf_util_get_string(child, &match->regex);
     else if (strcasecmp("ExcludeRegex", child->key) == 0)
-      status =
-          cmc_config_add_string("ExcludeRegex", &match->exclude_regex, child);
+      status = cf_util_get_string(child, &match->exclude_regex);
     else if (strcasecmp("DSType", child->key) == 0)
       status = cmc_config_add_match_dstype(&match->dstype, child);
     else if (strcasecmp("Type", child->key) == 0)
-      status = cmc_config_add_string("Type", &match->type, child);
+      status = cf_util_get_string(child, &match->type);
     else if (strcasecmp("Instance", child->key) == 0)
-      status = cmc_config_add_string("Instance", &match->instance, child);
+      status = cf_util_get_string(child, &match->instance);
     else {
       WARNING("memcachec plugin: Option `%s' not allowed here.", child->key);
       status = -1;
@@ -301,11 +285,11 @@ static int cmc_config_add_page(oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("Server", child->key) == 0)
-      status = cmc_config_add_string("Server", &page->server, child);
+      status = cf_util_get_string(child, &page->server);
     else if (strcasecmp("Key", child->key) == 0)
-      status = cmc_config_add_string("Key", &page->key, child);
+      status = cf_util_get_string(child, &page->key);
     else if (strcasecmp("Plugin", child->key) == 0)
-      status = cmc_config_add_string("Plugin", &page->plugin_name, child);
+      status = cf_util_get_string(child, &page->plugin_name);
     else if (strcasecmp("Match", child->key) == 0)
       /* Be liberal with failing matches => don't set `status'. */
       cmc_config_add_match(page, child);
@@ -412,7 +396,7 @@ static void cmc_submit(const web_page_t *wp, const web_match_t *wm, /* {{{ */
   vl.values = &value;
   vl.values_len = 1;
   sstrncpy(vl.plugin, (wp->plugin_name != NULL) ? wp->plugin_name : "memcachec",
-           sizeof (vl.plugin));
+           sizeof(vl.plugin));
   sstrncpy(vl.plugin_instance, wp->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, wm->type, sizeof(vl.type));
   sstrncpy(vl.type_instance, wm->instance, sizeof(vl.type_instance));
index d2827a4..0baf6c2 100644 (file)
@@ -32,8 +32,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <netdb.h>
 #include <netinet/in.h>
@@ -69,7 +69,7 @@ struct memcached_s {
 };
 typedef struct memcached_s memcached_t;
 
-static _Bool memcached_have_instances = 0;
+static bool memcached_have_instances;
 
 static void memcached_free(void *arg) {
   memcached_t *st = arg;
@@ -99,9 +99,8 @@ static int memcached_connect_unix(memcached_t *st) {
   /* create our socket descriptor */
   int fd = socket(AF_UNIX, SOCK_STREAM, 0);
   if (fd < 0) {
-    char errbuf[1024];
     ERROR("memcached plugin: memcached_connect_unix: socket(2) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return -1;
   }
 
@@ -134,12 +133,10 @@ static int memcached_connect_inet(memcached_t *st) {
 
   int status = getaddrinfo(st->connhost, st->connport, &ai_hints, &ai_list);
   if (status != 0) {
-    char errbuf[1024];
     ERROR("memcached plugin: memcached_connect_inet: "
           "getaddrinfo(%s,%s) failed: %s",
           st->connhost, st->connport,
-          (status == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                 : gai_strerror(status));
+          (status == EAI_SYSTEM) ? STRERRNO : gai_strerror(status));
     return -1;
   }
 
@@ -148,10 +145,9 @@ static int memcached_connect_inet(memcached_t *st) {
     /* create our socket descriptor */
     fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (fd < 0) {
-      char errbuf[1024];
       WARNING("memcached plugin: memcached_connect_inet: "
               "socket(2) failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+              STRERRNO);
       continue;
     }
 
@@ -246,9 +242,8 @@ static int memcached_query_daemon(char *buffer, size_t buffer_size,
 
   status = (int)swrite(st->fd, "stats\r\n", strlen("stats\r\n"));
   if (status != 0) {
-    char errbuf[1024];
     ERROR("memcached plugin: Instance \"%s\": write(2) failed: %s", st->name,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     shutdown(st->fd, SHUT_RDWR);
     close(st->fd);
     st->fd = -1;
@@ -280,13 +275,12 @@ static int memcached_query_daemon(char *buffer, size_t buffer_size,
 
     char const end_token[5] = {'E', 'N', 'D', '\r', '\n'};
     if (status < 0) {
-      char errbuf[1024];
 
       if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
         continue;
 
       ERROR("memcached plugin: Instance \"%s\": Error reading from socket: %s",
-            st->name, sstrerror(errno, errbuf, sizeof(errbuf)));
+            st->name, STRERRNO);
       shutdown(st->fd, SHUT_RDWR);
       close(st->fd);
       st->fd = -1;
@@ -481,7 +475,7 @@ static int memcached_read(user_data_t *user_data) {
     if (strsplit(line, fields, 3) != 3)
       continue;
 
-    int name_len = strlen(fields[1]);
+    size_t name_len = strlen(fields[1]);
     if (name_len == 0)
       continue;
 
@@ -708,7 +702,7 @@ static int config_add_instance(oconfig_item_t *ci) {
   int status = 0;
 
   /* Disable automatic generation of default instance in the init callback. */
-  memcached_have_instances = 1;
+  memcached_have_instances = true;
 
   memcached_t *st = calloc(1, sizeof(*st));
   if (st == NULL) {
@@ -761,7 +755,7 @@ static int config_add_instance(oconfig_item_t *ci) {
 } /* int config_add_instance */
 
 static int memcached_config(oconfig_item_t *ci) {
-  _Bool have_instance_block = 0;
+  bool have_instance_block = 0;
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
@@ -803,7 +797,7 @@ static int memcached_init(void) {
 
   int status = memcached_add_read_callback(st);
   if (status == 0)
-    memcached_have_instances = 1;
+    memcached_have_instances = true;
 
   return status;
 } /* int memcached_init */
index e49fe84..4a3a772 100644 (file)
@@ -25,8 +25,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #ifdef HAVE_SYS_SYSCTL_H
 #include <sys/sysctl.h>
@@ -94,8 +94,8 @@ static int pagesize;
 #error "No applicable input method."
 #endif
 
-static _Bool values_absolute = 1;
-static _Bool values_percentage = 0;
+static bool values_absolute = true;
+static bool values_percentage;
 
 static int memory_config(oconfig_item_t *ci) /* {{{ */
 {
@@ -163,9 +163,9 @@ static int memory_init(void) {
 #define MEMORY_SUBMIT(...)                                                     \
   do {                                                                         \
     if (values_absolute)                                                       \
-      plugin_dispatch_multivalue(vl, 0, DS_TYPE_GAUGE, __VA_ARGS__, NULL);     \
+      plugin_dispatch_multivalue(vl, false, DS_TYPE_GAUGE, __VA_ARGS__, NULL); \
     if (values_percentage)                                                     \
-      plugin_dispatch_multivalue(vl, 1, DS_TYPE_GAUGE, __VA_ARGS__, NULL);     \
+      plugin_dispatch_multivalue(vl, true, DS_TYPE_GAUGE, __VA_ARGS__, NULL);  \
   } while (0)
 
 static int memory_read_internal(value_list_t *vl) {
@@ -268,7 +268,7 @@ static int memory_read_internal(value_list_t *vl) {
   char *fields[8];
   int numfields;
 
-  _Bool detailed_slab_info = 0;
+  bool detailed_slab_info = false;
 
   gauge_t mem_total = 0;
   gauge_t mem_used = 0;
@@ -280,8 +280,7 @@ static int memory_read_internal(value_list_t *vl) {
   gauge_t mem_slab_unreclaimable = 0;
 
   if ((fh = fopen("/proc/meminfo", "r")) == NULL) {
-    char errbuf[1024];
-    WARNING("memory: fopen: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("memory: fopen: %s", STRERRNO);
     return -1;
   }
 
@@ -300,10 +299,10 @@ static int memory_read_internal(value_list_t *vl) {
       val = &mem_slab_total;
     else if (strncasecmp(buffer, "SReclaimable:", 13) == 0) {
       val = &mem_slab_reclaimable;
-      detailed_slab_info = 1;
+      detailed_slab_info = true;
     } else if (strncasecmp(buffer, "SUnreclaim:", 11) == 0) {
       val = &mem_slab_unreclaimable;
-      detailed_slab_info = 1;
+      detailed_slab_info = true;
     } else
       continue;
 
@@ -315,8 +314,7 @@ static int memory_read_internal(value_list_t *vl) {
   }
 
   if (fclose(fh)) {
-    char errbuf[1024];
-    WARNING("memory: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("memory: fclose: %s", STRERRNO);
   }
 
   if (mem_total < (mem_free + mem_buffered + mem_cached + mem_slab_total))
@@ -421,9 +419,7 @@ static int memory_read_internal(value_list_t *vl) {
   size = sizeof(vmtotal);
 
   if (sysctl(mib, 2, &vmtotal, &size, NULL, 0) < 0) {
-    char errbuf[1024];
-    WARNING("memory plugin: sysctl failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("memory plugin: sysctl failed: %s", STRERRNO);
     return -1;
   }
 
@@ -451,9 +447,7 @@ static int memory_read_internal(value_list_t *vl) {
   perfstat_memory_total_t pmemory = {0};
 
   if (perfstat_memory_total(NULL, &pmemory, sizeof(pmemory), 1) < 0) {
-    char errbuf[1024];
-    WARNING("memory plugin: perfstat_memory_total failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("memory plugin: perfstat_memory_total failed: %s", STRERRNO);
     return -1;
   }
 
index 3f9521d..6924eaf 100644 (file)
--- a/src/mic.c
+++ b/src/mic.c
@@ -21,9 +21,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #include <MicAccessApi.h>
 #include <MicAccessErrorTypes.h>
@@ -35,8 +35,8 @@
 #define MAX_CORES 256
 
 static MicDeviceOnSystem mics[MAX_MICS];
-static U32 num_mics = 0;
-static HANDLE mic_handle = NULL;
+static U32 num_mics;
+static HANDLE mic_handle;
 
 static int const therm_ids[] = {
     eMicThermalDie,  eMicThermalDevMem, eMicThermalFin, eMicThermalFout,
@@ -50,13 +50,13 @@ static const char *config_keys[] = {
     "ShowPower",        "Power",        "IgnoreSelectedPower"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static _Bool show_cpu = 1;
-static _Bool show_cpu_cores = 1;
-static _Bool show_memory = 1;
-static _Bool show_temps = 1;
-static ignorelist_t *temp_ignore = NULL;
-static _Bool show_power = 1;
-static ignorelist_t *power_ignore = NULL;
+static bool show_cpu = true;
+static bool show_cpu_cores = true;
+static bool show_memory = true;
+static bool show_temps = true;
+static ignorelist_t *temp_ignore;
+static bool show_power = true;
+static ignorelist_t *power_ignore;
 
 static int mic_init(void) {
   U32 ret;
index 715724d..ed53319 100644 (file)
@@ -22,9 +22,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "configfile.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <modbus.h>
 #include <netdb.h>
 enum mb_register_type_e /* {{{ */
 { REG_TYPE_INT16,
   REG_TYPE_INT32,
+  REG_TYPE_INT32_CDAB,
   REG_TYPE_UINT16,
   REG_TYPE_UINT32,
-  REG_TYPE_FLOAT }; /* }}} */
+  REG_TYPE_UINT32_CDAB,
+  REG_TYPE_INT64,
+  REG_TYPE_UINT64,
+  REG_TYPE_FLOAT,
+  REG_TYPE_FLOAT_CDAB }; /* }}} */
+
 enum mb_mreg_type_e /* {{{ */
 { MREG_HOLDING,
   MREG_INPUT }; /* }}} */
@@ -101,6 +107,8 @@ struct mb_data_s /* {{{ */
   mb_mreg_type_t modbus_register_type;
   char type[DATA_MAX_NAME_LEN];
   char instance[DATA_MAX_NAME_LEN];
+  double scale;
+  double shift;
 
   mb_data_t *next;
 }; /* }}} */
@@ -121,7 +129,6 @@ struct mb_host_s /* {{{ */
   int port;     /* for Modbus/TCP */
   int baudrate; /* for Modbus/RTU */
   mb_conntype_t conntype;
-  cdtime_t interval;
 
   mb_slave_t *slaves;
   size_t slaves_num;
@@ -131,7 +138,7 @@ struct mb_host_s /* {{{ */
 #else
   modbus_t *connection;
 #endif
-  _Bool is_connected;
+  bool is_connected;
 }; /* }}} */
 typedef struct mb_host_s mb_host_t;
 
@@ -148,7 +155,7 @@ struct mb_data_group_s /* {{{ */
 /*
  * Global variables
  */
-static mb_data_t *data_definitions = NULL;
+static mb_data_t *data_definitions;
 
 /*
  * Functions
@@ -244,15 +251,11 @@ static int mb_submit(mb_host_t *host, mb_slave_t *slave, /* {{{ */
   if ((host == NULL) || (slave == NULL) || (data == NULL))
     return EINVAL;
 
-  if (host->interval == 0)
-    host->interval = plugin_get_interval();
-
   if (slave->instance[0] == 0)
     snprintf(slave->instance, sizeof(slave->instance), "slave_%i", slave->id);
 
   vl.values = &value;
   vl.values_len = 1;
-  vl.interval = host->interval;
   sstrncpy(vl.host, host->host, sizeof(vl.host));
   sstrncpy(vl.plugin, "modbus", sizeof(vl.plugin));
   sstrncpy(vl.plugin_instance, slave->instance, sizeof(vl.plugin_instance));
@@ -328,7 +331,7 @@ static int mb_init_connection(mb_host_t *host) /* {{{ */
     return status;
   }
 
-  host->is_connected = 1;
+  host->is_connected = true;
   return 0;
 } /* }}} int mb_init_connection */
 /* #endif LEGACY_LIBMODBUS */
@@ -388,21 +391,21 @@ static int mb_init_connection(mb_host_t *host) /* {{{ */
 } /* }}} int mb_init_connection */
 #endif /* !LEGACY_LIBMODBUS */
 
-#define CAST_TO_VALUE_T(ds, vt, raw)                                           \
+#define CAST_TO_VALUE_T(ds, vt, raw, scale, shift)                             \
   do {                                                                         \
     if ((ds)->ds[0].type == DS_TYPE_COUNTER)                                   \
-      (vt).counter = (counter_t)(raw);                                         \
+      (vt).counter = (((counter_t)(raw)*scale) + shift);                       \
     else if ((ds)->ds[0].type == DS_TYPE_GAUGE)                                \
-      (vt).gauge = (gauge_t)(raw);                                             \
+      (vt).gauge = (((gauge_t)(raw)*scale) + shift);                           \
     else if ((ds)->ds[0].type == DS_TYPE_DERIVE)                               \
-      (vt).derive = (derive_t)(raw);                                           \
+      (vt).derive = (((derive_t)(raw)*scale) + shift);                         \
     else /* if (ds->ds[0].type == DS_TYPE_ABSOLUTE) */                         \
-      (vt).absolute = (absolute_t)(raw);                                       \
+      (vt).absolute = (((absolute_t)(raw)*scale) + shift);                     \
   } while (0)
 
 static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
                         mb_data_t *data) {
-  uint16_t values[2] = {0};
+  uint16_t values[4] = {0};
   int values_num;
   const data_set_t *ds;
   int status = 0;
@@ -417,7 +420,7 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 
   if (ds->ds_num != 1) {
-    ERROR("Modbus plugin: The type \"%s\" has %zu data sources. "
+    ERROR("Modbus plugin: The type \"%s\" has %" PRIsz " data sources. "
           "I can only handle data sets with only one data source.",
           data->type, ds->ds_num);
     return -1;
@@ -425,18 +428,28 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
 
   if ((ds->ds[0].type != DS_TYPE_GAUGE) &&
       (data->register_type != REG_TYPE_INT32) &&
-      (data->register_type != REG_TYPE_UINT32)) {
+      (data->register_type != REG_TYPE_INT32_CDAB) &&
+      (data->register_type != REG_TYPE_UINT32) &&
+      (data->register_type != REG_TYPE_UINT32_CDAB) &&
+      (data->register_type != REG_TYPE_INT64) &&
+      (data->register_type != REG_TYPE_UINT64)) {
     NOTICE(
         "Modbus plugin: The data source of type \"%s\" is %s, not gauge. "
         "This will most likely result in problems, because the register type "
-        "is not UINT32.",
+        "is not UINT32 or UINT64.",
         data->type, DS_TYPE_TO_STRING(ds->ds[0].type));
   }
 
   if ((data->register_type == REG_TYPE_INT32) ||
+      (data->register_type == REG_TYPE_INT32_CDAB) ||
       (data->register_type == REG_TYPE_UINT32) ||
-      (data->register_type == REG_TYPE_FLOAT))
+      (data->register_type == REG_TYPE_UINT32_CDAB) ||
+      (data->register_type == REG_TYPE_FLOAT) ||
+      (data->register_type == REG_TYPE_FLOAT_CDAB))
     values_num = 2;
+  else if ((data->register_type == REG_TYPE_INT64) ||
+           (data->register_type == REG_TYPE_UINT64))
+    values_num = 4;
   else
     values_num = 1;
 
@@ -456,7 +469,7 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
     if (status != 0) {
       ERROR("Modbus plugin: mb_init_connection (%s/%s) failed. ", host->host,
             host->node);
-      host->is_connected = 0;
+      host->is_connected = false;
       host->connection = NULL;
       return -1;
     }
@@ -496,8 +509,8 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
   if (status != values_num) {
     ERROR("Modbus plugin: modbus read function (%s/%s) failed. "
-          " status = %i, values_num = %i. Giving up.",
-          host->host, host->node, status, values_num);
+          " status = %i, start_addr = %i, values_num = %i. Giving up.",
+          host->host, host->node, status, data->register_base, values_num);
 #if LEGACY_LIBMODBUS
     modbus_close(&host->connection);
 #else
@@ -521,7 +534,18 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
           "Returned float value is %g",
           (double)float_value);
 
-    CAST_TO_VALUE_T(ds, vt, float_value);
+    CAST_TO_VALUE_T(ds, vt, float_value, data->scale, data->shift);
+    mb_submit(host, slave, data, vt);
+  } else if (data->register_type == REG_TYPE_FLOAT_CDAB) {
+    float float_value;
+    value_t vt;
+
+    float_value = mb_register_to_float(values[1], values[0]);
+    DEBUG("Modbus plugin: mb_read_data: "
+          "Returned float value is %g",
+          (double)float_value);
+
+    CAST_TO_VALUE_T(ds, vt, float_value, data->scale, data->shift);
     mb_submit(host, slave, data, vt);
   } else if (data->register_type == REG_TYPE_INT32) {
     union {
@@ -535,7 +559,21 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
           "Returned int32 value is %" PRIi32,
           v.i32);
 
-    CAST_TO_VALUE_T(ds, vt, v.i32);
+    CAST_TO_VALUE_T(ds, vt, v.i32, data->scale, data->shift);
+    mb_submit(host, slave, data, vt);
+  } else if (data->register_type == REG_TYPE_INT32_CDAB) {
+    union {
+      uint32_t u32;
+      int32_t i32;
+    } v;
+    value_t vt;
+
+    v.u32 = (((uint32_t)values[1]) << 16) | ((uint32_t)values[0]);
+    DEBUG("Modbus plugin: mb_read_data: "
+          "Returned int32 value is %" PRIi32,
+          v.i32);
+
+    CAST_TO_VALUE_T(ds, vt, v.i32, data->scale, data->shift);
     mb_submit(host, slave, data, vt);
   } else if (data->register_type == REG_TYPE_INT16) {
     union {
@@ -550,7 +588,7 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
           "Returned int16 value is %" PRIi16,
           v.i16);
 
-    CAST_TO_VALUE_T(ds, vt, v.i16);
+    CAST_TO_VALUE_T(ds, vt, v.i16, data->scale, data->shift);
     mb_submit(host, slave, data, vt);
   } else if (data->register_type == REG_TYPE_UINT32) {
     uint32_t v32;
@@ -561,7 +599,45 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
           "Returned uint32 value is %" PRIu32,
           v32);
 
-    CAST_TO_VALUE_T(ds, vt, v32);
+    CAST_TO_VALUE_T(ds, vt, v32, data->scale, data->shift);
+    mb_submit(host, slave, data, vt);
+  } else if (data->register_type == REG_TYPE_UINT32_CDAB) {
+    uint32_t v32;
+    value_t vt;
+
+    v32 = (((uint32_t)values[1]) << 16) | ((uint32_t)values[0]);
+    DEBUG("Modbus plugin: mb_read_data: "
+          "Returned uint32 value is %" PRIu32,
+          v32);
+
+    CAST_TO_VALUE_T(ds, vt, v32, data->scale, data->shift);
+    mb_submit(host, slave, data, vt);
+  } else if (data->register_type == REG_TYPE_UINT64) {
+    uint64_t v64;
+    value_t vt;
+
+    v64 = (((uint64_t)values[0]) << 48) | (((uint64_t)values[1]) << 32) |
+          (((uint64_t)values[2]) << 16) | (((uint64_t)values[3]));
+    DEBUG("Modbus plugin: mb_read_data: "
+          "Returned uint64 value is %" PRIu64,
+          v64);
+
+    CAST_TO_VALUE_T(ds, vt, v64, data->scale, data->shift);
+    mb_submit(host, slave, data, vt);
+  } else if (data->register_type == REG_TYPE_INT64) {
+    union {
+      uint64_t u64;
+      int64_t i64;
+    } v;
+    value_t vt;
+
+    v.u64 = (((uint64_t)values[0]) << 48) | (((uint64_t)values[1]) << 32) |
+            (((uint64_t)values[2]) << 16) | ((uint64_t)values[3]);
+    DEBUG("Modbus plugin: mb_read_data: "
+          "Returned uint64 value is %" PRIi64,
+          v.i64);
+
+    CAST_TO_VALUE_T(ds, vt, v.i64, data->scale, data->shift);
     mb_submit(host, slave, data, vt);
   } else /* if (data->register_type == REG_TYPE_UINT16) */
   {
@@ -571,7 +647,7 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
           "Returned uint16 value is %" PRIu16,
           values[0]);
 
-    CAST_TO_VALUE_T(ds, vt, values[0]);
+    CAST_TO_VALUE_T(ds, vt, values[0], data->scale, data->shift);
     mb_submit(host, slave, data, vt);
   }
 
@@ -678,6 +754,8 @@ static int mb_config_add_data(oconfig_item_t *ci) /* {{{ */
   data.name = NULL;
   data.register_type = REG_TYPE_UINT16;
   data.next = NULL;
+  data.scale = 1;
+  data.shift = 0;
 
   status = cf_util_get_string(ci, &data.name);
   if (status != 0)
@@ -691,6 +769,10 @@ static int mb_config_add_data(oconfig_item_t *ci) /* {{{ */
     else if (strcasecmp("Instance", child->key) == 0)
       status = cf_util_get_string_buffer(child, data.instance,
                                          sizeof(data.instance));
+    else if (strcasecmp("Scale", child->key) == 0)
+      status = cf_util_get_double(child, &data.scale);
+    else if (strcasecmp("Shift", child->key) == 0)
+      status = cf_util_get_double(child, &data.shift);
     else if (strcasecmp("RegisterBase", child->key) == 0)
       status = cf_util_get_int(child, &data.register_base);
     else if (strcasecmp("RegisterType", child->key) == 0) {
@@ -702,12 +784,22 @@ static int mb_config_add_data(oconfig_item_t *ci) /* {{{ */
         data.register_type = REG_TYPE_INT16;
       else if (strcasecmp("Int32", tmp) == 0)
         data.register_type = REG_TYPE_INT32;
+      else if (strcasecmp("Int32LE", tmp) == 0)
+        data.register_type = REG_TYPE_INT32_CDAB;
       else if (strcasecmp("Uint16", tmp) == 0)
         data.register_type = REG_TYPE_UINT16;
       else if (strcasecmp("Uint32", tmp) == 0)
         data.register_type = REG_TYPE_UINT32;
+      else if (strcasecmp("Uint32LE", tmp) == 0)
+        data.register_type = REG_TYPE_UINT32_CDAB;
       else if (strcasecmp("Float", tmp) == 0)
         data.register_type = REG_TYPE_FLOAT;
+      else if (strcasecmp("FloatLE", tmp) == 0)
+        data.register_type = REG_TYPE_FLOAT_CDAB;
+      else if (strcasecmp("Uint64", tmp) == 0)
+        data.register_type = REG_TYPE_UINT64;
+      else if (strcasecmp("Int64", tmp) == 0)
+        data.register_type = REG_TYPE_INT64;
       else {
         ERROR("Modbus plugin: The register type \"%s\" is unknown.", tmp);
         status = -1;
@@ -770,10 +862,8 @@ static int mb_config_set_host_address(mb_host_t *host, /* {{{ */
 
   status = getaddrinfo(address, /* service = */ NULL, &ai_hints, &ai_list);
   if (status != 0) {
-    char errbuf[1024];
     ERROR("Modbus plugin: getaddrinfo failed: %s",
-          (status == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                 : gai_strerror(status));
+          (status == EAI_SYSTEM) ? STRERRNO : gai_strerror(status));
     return status;
   }
 
@@ -857,6 +947,7 @@ static int mb_config_add_slave(mb_host_t *host, oconfig_item_t *ci) /* {{{ */
 
 static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */
 {
+  cdtime_t interval = 0;
   mb_host_t *host;
   int status;
 
@@ -897,7 +988,7 @@ static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */
     } else if (strcasecmp("Baudrate", child->key) == 0)
       status = cf_util_get_int(child, &host->baudrate);
     else if (strcasecmp("Interval", child->key) == 0)
-      status = cf_util_get_cdtime(child, &host->interval);
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Slave", child->key) == 0)
       /* Don't set status: Gracefully continue if a slave fails. */
       mb_config_add_slave(host, child);
@@ -938,7 +1029,7 @@ static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */
 
     plugin_register_complex_read(/* group = */ NULL, name,
                                  /* callback = */ mb_read,
-                                 /* interval = */ host->interval,
+                                 /* interval = */ interval,
                                  &(user_data_t){
                                      .data = host, .free_func = host_free,
                                  });
index 5164485..630114e 100644 (file)
@@ -31,8 +31,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_complain.h"
 
 #include <mosquitto.h>
  * Data types
  */
 struct mqtt_client_conf {
-  _Bool publish;
+  bool publish;
   char *name;
 
   struct mosquitto *mosq;
-  _Bool connected;
+  bool connected;
 
   char *host;
   int port;
@@ -74,22 +74,22 @@ struct mqtt_client_conf {
 
   /* For publishing */
   char *topic_prefix;
-  _Bool store_rates;
-  _Bool retain;
+  bool store_rates;
+  bool retain;
 
   /* For subscribing */
   pthread_t thread;
-  _Bool loop;
+  bool loop;
   char *topic;
-  _Bool clean_session;
+  bool clean_session;
 
   c_complain_t complaint_cantpublish;
   pthread_mutex_t lock;
 };
 typedef struct mqtt_client_conf mqtt_client_conf_t;
 
-static mqtt_client_conf_t **subscribers = NULL;
-static size_t subscribers_num = 0;
+static mqtt_client_conf_t **subscribers;
+static size_t subscribers_num;
 
 /*
  * Functions
@@ -141,7 +141,7 @@ static void mqtt_free(mqtt_client_conf_t *conf) {
 
   if (conf->connected)
     (void)mosquitto_disconnect(conf->mosq);
-  conf->connected = 0;
+  conf->connected = false;
   (void)mosquitto_destroy(conf->mosq);
 
   sfree(conf->host);
@@ -247,14 +247,12 @@ static int mqtt_reconnect(mqtt_client_conf_t *conf) {
 
   status = mosquitto_reconnect(conf->mosq);
   if (status != MOSQ_ERR_SUCCESS) {
-    char errbuf[1024];
     ERROR("mqtt_connect_broker: mosquitto_connect failed: %s",
-          (status == MOSQ_ERR_ERRNO) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                     : mosquitto_strerror(status));
+          (status == MOSQ_ERR_ERRNO) ? STRERRNO : mosquitto_strerror(status));
     return -1;
   }
 
-  conf->connected = 1;
+  conf->connected = true;
 
   c_release(LOG_INFO, &conf->complaint_cantpublish,
             "mqtt plugin: successfully reconnected to broker \"%s:%d\"",
@@ -325,11 +323,8 @@ static int mqtt_connect(mqtt_client_conf_t *conf) {
     status =
         mosquitto_username_pw_set(conf->mosq, conf->username, conf->password);
     if (status != MOSQ_ERR_SUCCESS) {
-      char errbuf[1024];
       ERROR("mqtt plugin: mosquitto_username_pw_set failed: %s",
-            (status == MOSQ_ERR_ERRNO)
-                ? sstrerror(errno, errbuf, sizeof(errbuf))
-                : mosquitto_strerror(status));
+            (status == MOSQ_ERR_ERRNO) ? STRERRNO : mosquitto_strerror(status));
 
       mosquitto_destroy(conf->mosq);
       conf->mosq = NULL;
@@ -346,10 +341,8 @@ static int mqtt_connect(mqtt_client_conf_t *conf) {
       mosquitto_connect(conf->mosq, conf->host, conf->port, MQTT_KEEPALIVE);
 #endif
   if (status != MOSQ_ERR_SUCCESS) {
-    char errbuf[1024];
     ERROR("mqtt plugin: mosquitto_connect failed: %s",
-          (status == MOSQ_ERR_ERRNO) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                     : mosquitto_strerror(status));
+          (status == MOSQ_ERR_ERRNO) ? STRERRNO : mosquitto_strerror(status));
 
     mosquitto_destroy(conf->mosq);
     conf->mosq = NULL;
@@ -373,7 +366,7 @@ static int mqtt_connect(mqtt_client_conf_t *conf) {
     }
   }
 
-  conf->connected = 1;
+  conf->connected = true;
   return 0;
 } /* mqtt_connect */
 
@@ -400,14 +393,14 @@ static void *subscribers_thread(void *arg) {
                             /* max_packets = */ 100);
 #endif
     if (status == MOSQ_ERR_CONN_LOST) {
-      conf->connected = 0;
+      conf->connected = false;
       continue;
     } else if (status != MOSQ_ERR_SUCCESS) {
       ERROR("mqtt plugin: mosquitto_loop failed: %s",
             mosquitto_strerror(status));
       mosquitto_destroy(conf->mosq);
       conf->mosq = NULL;
-      conf->connected = 0;
+      conf->connected = false;
       continue;
     }
 
@@ -438,16 +431,14 @@ static int publish(mqtt_client_conf_t *conf, char const *topic,
 #endif
                              conf->qos, conf->retain);
   if (status != MOSQ_ERR_SUCCESS) {
-    char errbuf[1024];
     c_complain(LOG_ERR, &conf->complaint_cantpublish,
                "mqtt plugin: mosquitto_publish failed: %s",
-               (status == MOSQ_ERR_ERRNO)
-                   ? sstrerror(errno, errbuf, sizeof(errbuf))
-                   : mosquitto_strerror(status));
+               (status == MOSQ_ERR_ERRNO) ? STRERRNO
+                                          : mosquitto_strerror(status));
     /* Mark our connection "down" regardless of the error as a safety
      * measure; we will try to reconnect the next time we have to publish a
      * message */
-    conf->connected = 0;
+    conf->connected = false;
     mosquitto_disconnect(conf->mosq);
 
     pthread_mutex_unlock(&conf->lock);
@@ -541,7 +532,7 @@ static int mqtt_config_publisher(oconfig_item_t *ci) {
     ERROR("mqtt plugin: calloc failed.");
     return -1;
   }
-  conf->publish = 1;
+  conf->publish = true;
 
   conf->name = NULL;
   status = cf_util_get_string(ci, &conf->name);
@@ -555,7 +546,7 @@ static int mqtt_config_publisher(oconfig_item_t *ci) {
   conf->client_id = NULL;
   conf->qos = 0;
   conf->topic_prefix = strdup(MQTT_DEFAULT_TOPIC_PREFIX);
-  conf->store_rates = 1;
+  conf->store_rates = true;
 
   status = pthread_mutex_init(&conf->lock, NULL);
   if (status != 0) {
@@ -640,7 +631,7 @@ static int mqtt_config_subscriber(oconfig_item_t *ci) {
     ERROR("mqtt plugin: calloc failed.");
     return -1;
   }
-  conf->publish = 0;
+  conf->publish = false;
 
   conf->name = NULL;
   status = cf_util_get_string(ci, &conf->name);
@@ -654,7 +645,7 @@ static int mqtt_config_subscriber(oconfig_item_t *ci) {
   conf->client_id = NULL;
   conf->qos = 2;
   conf->topic = strdup(MQTT_DEFAULT_TOPIC);
-  conf->clean_session = 1;
+  conf->clean_session = true;
 
   status = pthread_mutex_init(&conf->lock, NULL);
   if (status != 0) {
@@ -758,9 +749,7 @@ static int mqtt_init(void) {
                                   /* args  = */ subscribers[i],
                                   /* name  = */ "mqtt");
     if (status != 0) {
-      char errbuf[1024];
-      ERROR("mqtt plugin: pthread_create failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("mqtt plugin: pthread_create failed: %s", STRERRNO);
       continue;
     }
   }
index 2adca57..d56a3b3 100644 (file)
 /* Intel MSRs. Some also available on other CPUs */
 
 /* C-state Residency Counters */
-#define MSR_PKG_C3_RESIDENCY           0x000003f8
-#define MSR_PKG_C6_RESIDENCY           0x000003f9
-#define MSR_ATOM_PKG_C6_RESIDENCY      0x000003fa
-#define MSR_PKG_C7_RESIDENCY           0x000003fa
-#define MSR_CORE_C3_RESIDENCY          0x000003fc
-#define MSR_CORE_C6_RESIDENCY          0x000003fd
-#define MSR_CORE_C7_RESIDENCY          0x000003fe
-#define MSR_KNL_CORE_C6_RESIDENCY      0x000003ff
-#define MSR_PKG_C2_RESIDENCY           0x0000060d
-#define MSR_PKG_C8_RESIDENCY           0x00000630
-#define MSR_PKG_C9_RESIDENCY           0x00000631
-#define MSR_PKG_C10_RESIDENCY          0x00000632
+#define MSR_PKG_C3_RESIDENCY 0x000003f8
+#define MSR_PKG_C6_RESIDENCY 0x000003f9
+#define MSR_ATOM_PKG_C6_RESIDENCY 0x000003fa
+#define MSR_PKG_C7_RESIDENCY 0x000003fa
+#define MSR_CORE_C3_RESIDENCY 0x000003fc
+#define MSR_CORE_C6_RESIDENCY 0x000003fd
+#define MSR_CORE_C7_RESIDENCY 0x000003fe
+#define MSR_KNL_CORE_C6_RESIDENCY 0x000003ff
+#define MSR_PKG_C2_RESIDENCY 0x0000060d
+#define MSR_PKG_C8_RESIDENCY 0x00000630
+#define MSR_PKG_C9_RESIDENCY 0x00000631
+#define MSR_PKG_C10_RESIDENCY 0x00000632
 
 /* Run Time Average Power Limiting (RAPL) Interface */
 
-#define MSR_RAPL_POWER_UNIT            0x00000606
+#define MSR_RAPL_POWER_UNIT 0x00000606
 
-#define MSR_PKG_POWER_LIMIT            0x00000610
-#define MSR_PKG_ENERGY_STATUS          0x00000611
-#define MSR_PKG_PERF_STATUS            0x00000613
-#define MSR_PKG_POWER_INFO             0x00000614
+#define MSR_PKG_POWER_LIMIT 0x00000610
+#define MSR_PKG_ENERGY_STATUS 0x00000611
+#define MSR_PKG_PERF_STATUS 0x00000613
+#define MSR_PKG_POWER_INFO 0x00000614
 
-#define MSR_DRAM_POWER_LIMIT           0x00000618
-#define MSR_DRAM_ENERGY_STATUS         0x00000619
-#define MSR_DRAM_PERF_STATUS           0x0000061b
-#define MSR_DRAM_POWER_INFO            0x0000061c
-
-#define MSR_PP0_POWER_LIMIT            0x00000638
-#define MSR_PP0_ENERGY_STATUS          0x00000639
-#define MSR_PP0_POLICY                 0x0000063a
-#define MSR_PP0_PERF_STATUS            0x0000063b
-
-#define MSR_PP1_POWER_LIMIT            0x00000640
-#define MSR_PP1_ENERGY_STATUS          0x00000641
-#define MSR_PP1_POLICY                 0x00000642
+#define MSR_DRAM_POWER_LIMIT 0x00000618
+#define MSR_DRAM_ENERGY_STATUS 0x00000619
+#define MSR_UNCORE_FREQ_SCALING 0x00000621
+#define MSR_DRAM_PERF_STATUS 0x0000061b
+#define MSR_DRAM_POWER_INFO 0x0000061c
 
+#define MSR_PP0_POWER_LIMIT 0x00000638
+#define MSR_PP0_ENERGY_STATUS 0x00000639
+#define MSR_PP0_POLICY 0x0000063a
+#define MSR_PP0_PERF_STATUS 0x0000063b
 
+#define MSR_PP1_POWER_LIMIT 0x00000640
+#define MSR_PP1_ENERGY_STATUS 0x00000641
+#define MSR_PP1_POLICY 0x00000642
 
 /* Intel defined MSRs. */
-#define MSR_IA32_TSC                   0x00000010
-#define MSR_SMI_COUNT                  0x00000034
-
-#define MSR_IA32_MPERF                 0x000000e7
-#define MSR_IA32_APERF                 0x000000e8
+#define MSR_IA32_TSC 0x00000010
+#define MSR_SMI_COUNT 0x00000034
 
-#define MSR_IA32_THERM_STATUS          0x0000019c
+#define MSR_IA32_MPERF 0x000000e7
+#define MSR_IA32_APERF 0x000000e8
 
-#define MSR_IA32_TEMPERATURE_TARGET    0x000001a2
+#define MSR_IA32_THERM_STATUS 0x0000019c
 
-#define MSR_IA32_PACKAGE_THERM_STATUS          0x000001b1
+#define MSR_IA32_MISC_ENABLE 0x000001a0
+#define MSR_IA32_TEMPERATURE_TARGET 0x000001a2
 
+#define MSR_IA32_PACKAGE_THERM_STATUS 0x000001b1
 
 #endif /* _ASM_X86_MSR_INDEX_H */
index fc69e02..5a7d5a2 100644 (file)
@@ -24,8 +24,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
@@ -46,9 +46,7 @@ static int multimeter_read_value(double *value) {
     tcflush(fd, TCIFLUSH);
 
     if (gettimeofday(&time_end, NULL) < 0) {
-      char errbuf[1024];
-      ERROR("multimeter plugin: gettimeofday failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("multimeter plugin: gettimeofday failed: %s", STRERRNO);
       return -1;
     }
     time_end.tv_sec++;
@@ -71,10 +69,9 @@ static int multimeter_read_value(double *value) {
       FD_SET(fd, &rfds);
 
       if (gettimeofday(&time_now, NULL) < 0) {
-        char errbuf[1024];
         ERROR("multimeter plugin: "
               "gettimeofday failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+              STRERRNO);
         return -1;
       }
       if (timeval_cmp(time_end, time_now, &timeout) < 0)
@@ -132,10 +129,9 @@ static int multimeter_read_value(double *value) {
         continue;
       } else /* status == -1 */
       {
-        char errbuf[1024];
         ERROR("multimeter plugin: "
               "select failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+              STRERRNO);
         break;
       }
     }
index 7fe6d76..7399fe2 100644 (file)
@@ -29,8 +29,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #ifdef HAVE_MYSQL_H
 #include <mysql.h>
@@ -58,17 +58,17 @@ struct mysql_database_s /* {{{ */
   int port;
   int timeout;
 
-  _Bool master_stats;
-  _Bool slave_stats;
-  _Bool innodb_stats;
-  _Bool wsrep_stats;
+  bool master_stats;
+  bool slave_stats;
+  bool innodb_stats;
+  bool wsrep_stats;
 
-  _Bool slave_notif;
-  _Bool slave_io_running;
-  _Bool slave_sql_running;
+  bool slave_notif;
+  bool slave_io_running;
+  bool slave_sql_running;
 
   MYSQL *con;
-  _Bool is_connected;
+  bool is_connected;
 };
 typedef struct mysql_database_s mysql_database_t; /* }}} */
 
@@ -147,8 +147,8 @@ static int mysql_config_database(oconfig_item_t *ci) /* {{{ */
   db->timeout = 0;
 
   /* trigger a notification, if it's not running */
-  db->slave_io_running = 1;
-  db->slave_sql_running = 1;
+  db->slave_io_running = true;
+  db->slave_sql_running = true;
 
   status = cf_util_get_string(ci, &db->instance);
   if (status != 0) {
@@ -268,14 +268,18 @@ static MYSQL *getconnection(mysql_database_t *db) {
     WARNING("mysql plugin: Lost connection to instance \"%s\": %s",
             db->instance, mysql_error(db->con));
   }
-  db->is_connected = 0;
+  db->is_connected = false;
 
+  /* Close the old connection before initializing a new one. */
+  if (db->con != NULL) {
+    mysql_close(db->con);
+    db->con = NULL;
+  }
+
+  db->con = mysql_init(NULL);
   if (db->con == NULL) {
-    db->con = mysql_init(NULL);
-    if (db->con == NULL) {
-      ERROR("mysql plugin: mysql_init failed: %s", mysql_error(db->con));
-      return NULL;
-    }
+    ERROR("mysql plugin: mysql_init failed: %s", mysql_error(db->con));
+    return NULL;
   }
 
   /* Configure TCP connect timeout (default: 0) */
@@ -301,7 +305,7 @@ static MYSQL *getconnection(mysql_database_t *db) {
        mysql_get_host_info(db->con), (cipher != NULL) ? cipher : "<none>",
        mysql_get_server_info(db->con), mysql_get_proto_info(db->con));
 
-  db->is_connected = 1;
+  db->is_connected = true;
   return db->con;
 } /* static MYSQL *getconnection (mysql_database_t *db) */
 
@@ -359,7 +363,7 @@ static void traffic_submit(derive_t rx, derive_t tx, mysql_database_t *db) {
 static MYSQL_RES *exec_query(MYSQL *con, const char *query) {
   MYSQL_RES *res;
 
-  int query_len = strlen(query);
+  size_t query_len = strlen(query);
 
   if (mysql_real_query(con, query, query_len)) {
     ERROR("mysql plugin: Failed to execute query: %s", mysql_error(con));
@@ -499,14 +503,14 @@ static int mysql_read_slave_stats(mysql_database_t *db, MYSQL *con) {
       snprintf(n.message, sizeof(n.message),
                "slave I/O thread not started or not connected to master");
       plugin_dispatch_notification(&n);
-      db->slave_io_running = 0;
+      db->slave_io_running = false;
     } else if (((io != NULL) && (strcasecmp(io, "yes") == 0)) &&
                (!db->slave_io_running)) {
       n.severity = NOTIF_OKAY;
       snprintf(n.message, sizeof(n.message),
                "slave I/O thread started and connected to master");
       plugin_dispatch_notification(&n);
-      db->slave_io_running = 1;
+      db->slave_io_running = true;
     }
 
     if (((sql == NULL) || (strcasecmp(sql, "yes") != 0)) &&
@@ -514,13 +518,13 @@ static int mysql_read_slave_stats(mysql_database_t *db, MYSQL *con) {
       n.severity = NOTIF_WARNING;
       snprintf(n.message, sizeof(n.message), "slave SQL thread not started");
       plugin_dispatch_notification(&n);
-      db->slave_sql_running = 0;
+      db->slave_sql_running = false;
     } else if (((sql != NULL) && (strcasecmp(sql, "yes") == 0)) &&
                (!db->slave_sql_running)) {
       n.severity = NOTIF_OKAY;
       snprintf(n.message, sizeof(n.message), "slave SQL thread started");
       plugin_dispatch_notification(&n);
-      db->slave_sql_running = 1;
+      db->slave_sql_running = true;
     }
   }
 
index 44fb976..1b510d2 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #include <netapp_api.h>
 #include <netapp_errno.h>
@@ -2229,42 +2229,6 @@ static int cna_query_system(host_config_t *host) /* {{{ */
 /*
  * Configuration handling
  */
-/* Sets a given flag if the boolean argument is true and unsets the flag if it
- * is false. On error, the flag-field is not changed. */
-static int cna_config_bool_to_flag(const oconfig_item_t *ci, /* {{{ */
-                                   uint32_t *flags, uint32_t flag) {
-  if ((ci == NULL) || (flags == NULL))
-    return EINVAL;
-
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) {
-    WARNING("netapp plugin: The %s option needs exactly one boolean argument.",
-            ci->key);
-    return -1;
-  }
-
-  if (ci->values[0].value.boolean)
-    *flags |= flag;
-  else
-    *flags &= ~flag;
-
-  return 0;
-} /* }}} int cna_config_bool_to_flag */
-
-/* Handling of the "Interval" option which is allowed in every block. */
-static int cna_config_get_interval(const oconfig_item_t *ci, /* {{{ */
-                                   cna_interval_t *out_interval) {
-  cdtime_t tmp = 0;
-  int status;
-
-  status = cf_util_get_cdtime(ci, &tmp);
-  if (status != 0)
-    return status;
-
-  out_interval->interval = tmp;
-  out_interval->last_read = 0;
-
-  return 0;
-} /* }}} int cna_config_get_interval */
 
 /* Handling of the "GetIO", "GetOps" and "GetLatency" options within a
  * <VolumePerf /> block. */
@@ -2385,7 +2349,7 @@ static int cna_config_volume_performance(host_config_t *host, /* {{{ */
 
     /* if (!item || !item->key || !*item->key) continue; */
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_volume_perf->interval);
+      cf_util_get_cdtime(item, &cfg_volume_perf->interval.interval);
     else if (!strcasecmp(item->key, "GetIO"))
       cna_config_volume_perf_option(cfg_volume_perf, item);
     else if (!strcasecmp(item->key, "GetOps"))
@@ -2481,7 +2445,7 @@ static int cna_config_quota(host_config_t *host, oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *item = ci->children + i;
 
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_quota->interval);
+      cf_util_get_cdtime(item, &cfg_quota->interval.interval);
     else
       WARNING("netapp plugin: The option %s is not allowed within "
               "`Quota' blocks.",
@@ -2517,9 +2481,9 @@ static int cna_config_disk(host_config_t *host, oconfig_item_t *ci) { /* {{{ */
 
     /* if (!item || !item->key || !*item->key) continue; */
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_disk->interval);
+      cf_util_get_cdtime(item, &cfg_disk->interval.interval);
     else if (strcasecmp(item->key, "GetBusy") == 0)
-      cna_config_bool_to_flag(item, &cfg_disk->flags, CFG_DISK_BUSIEST);
+      cf_util_get_flag(item, &cfg_disk->flags, CFG_DISK_BUSIEST);
   }
 
   if ((cfg_disk->flags & CFG_DISK_ALL) == 0) {
@@ -2556,15 +2520,15 @@ static int cna_config_wafl(host_config_t *host, oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *item = ci->children + i;
 
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_wafl->interval);
+      cf_util_get_cdtime(item, &cfg_wafl->interval.interval);
     else if (!strcasecmp(item->key, "GetNameCache"))
-      cna_config_bool_to_flag(item, &cfg_wafl->flags, CFG_WAFL_NAME_CACHE);
+      cf_util_get_flag(item, &cfg_wafl->flags, CFG_WAFL_NAME_CACHE);
     else if (!strcasecmp(item->key, "GetDirCache"))
-      cna_config_bool_to_flag(item, &cfg_wafl->flags, CFG_WAFL_DIR_CACHE);
+      cf_util_get_flag(item, &cfg_wafl->flags, CFG_WAFL_DIR_CACHE);
     else if (!strcasecmp(item->key, "GetBufferCache"))
-      cna_config_bool_to_flag(item, &cfg_wafl->flags, CFG_WAFL_BUF_CACHE);
+      cf_util_get_flag(item, &cfg_wafl->flags, CFG_WAFL_BUF_CACHE);
     else if (!strcasecmp(item->key, "GetInodeCache"))
-      cna_config_bool_to_flag(item, &cfg_wafl->flags, CFG_WAFL_INODE_CACHE);
+      cf_util_get_flag(item, &cfg_wafl->flags, CFG_WAFL_INODE_CACHE);
     else
       WARNING("netapp plugin: The %s config option is not allowed within "
               "`WAFL' blocks.",
@@ -2636,7 +2600,7 @@ static int cna_config_volume_usage(host_config_t *host, /* {{{ */
 
     /* if (!item || !item->key || !*item->key) continue; */
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_volume_usage->interval);
+      cf_util_get_cdtime(item, &cfg_volume_usage->interval.interval);
     else if (!strcasecmp(item->key, "GetCapacity"))
       cna_config_volume_usage_option(cfg_volume_usage, item);
     else if (!strcasecmp(item->key, "GetSnapshot"))
@@ -2677,7 +2641,7 @@ static int cna_config_snapvault(host_config_t *host, /* {{{ */
     oconfig_item_t *item = ci->children + i;
 
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_snapvault->interval);
+      cf_util_get_cdtime(item, &cfg_snapvault->interval.interval);
     else
       WARNING("netapp plugin: The option %s is not allowed within "
               "`SnapVault' blocks.",
@@ -2712,15 +2676,15 @@ static int cna_config_system(host_config_t *host, /* {{{ */
     oconfig_item_t *item = ci->children + i;
 
     if (strcasecmp(item->key, "Interval") == 0) {
-      cna_config_get_interval(item, &cfg_system->interval);
+      cf_util_get_cdtime(item, &cfg_system->interval.interval);
     } else if (!strcasecmp(item->key, "GetCPULoad")) {
-      cna_config_bool_to_flag(item, &cfg_system->flags, CFG_SYSTEM_CPU);
+      cf_util_get_flag(item, &cfg_system->flags, CFG_SYSTEM_CPU);
     } else if (!strcasecmp(item->key, "GetInterfaces")) {
-      cna_config_bool_to_flag(item, &cfg_system->flags, CFG_SYSTEM_NET);
+      cf_util_get_flag(item, &cfg_system->flags, CFG_SYSTEM_NET);
     } else if (!strcasecmp(item->key, "GetDiskOps")) {
-      cna_config_bool_to_flag(item, &cfg_system->flags, CFG_SYSTEM_OPS);
+      cf_util_get_flag(item, &cfg_system->flags, CFG_SYSTEM_OPS);
     } else if (!strcasecmp(item->key, "GetDiskIO")) {
-      cna_config_bool_to_flag(item, &cfg_system->flags, CFG_SYSTEM_DISK);
+      cf_util_get_flag(item, &cfg_system->flags, CFG_SYSTEM_DISK);
     } else {
       WARNING("netapp plugin: The %s config option is not allowed within "
               "`System' blocks.",
@@ -2842,11 +2806,11 @@ static int cna_register_host(host_config_t *host) /* {{{ */
 static int cna_config_host(host_config_t *host, /* {{{ */
                            const oconfig_item_t *ci) {
   oconfig_item_t *item;
-  _Bool is_vfiler = 0;
+  bool is_vfiler = false;
   int status;
 
   if (!strcasecmp(ci->key, "VFiler"))
-    is_vfiler = 1;
+    is_vfiler = true;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
     WARNING("netapp plugin: \"%s\" needs exactly one string argument. Ignoring "
@@ -3010,7 +2974,7 @@ static int cna_init(void) /* {{{ */
   char err[256] = {0};
 
   if (!na_startup(err, sizeof(err))) {
-    err[sizeof(err) - 1] = 0;
+    err[sizeof(err) - 1] = '\0';
     ERROR("netapp plugin: Error initializing netapp API: %s", err);
     return 1;
   }
index 29cd383..37c2e29 100644 (file)
@@ -27,8 +27,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <asm/types.h>
 
@@ -56,6 +56,7 @@ struct ir_link_stats_storage_s {
   uint64_t tx_dropped;
   uint64_t multicast;
   uint64_t collisions;
+  uint64_t rx_nohandler;
 
   uint64_t rx_length_errors;
   uint64_t rx_over_errors;
@@ -91,12 +92,12 @@ struct qos_stats {
 };
 
 static int ir_ignorelist_invert = 1;
-static ir_ignorelist_t *ir_ignorelist_head = NULL;
+static ir_ignorelist_t *ir_ignorelist_head;
 
 static struct mnl_socket *nl;
 
-static char **iflist = NULL;
-static size_t iflist_len = 0;
+static char **iflist;
+static size_t iflist_len;
 
 static const char *config_keys[] = {"Interface", "VerboseInterface",
                                     "QDisc",     "Class",
@@ -253,6 +254,10 @@ static void check_ignorelist_and_submit(const char *dev,
     submit_two(dev, "if_dropped", NULL, stats->rx_dropped, stats->tx_dropped);
     submit_one(dev, "if_multicast", NULL, stats->multicast);
     submit_one(dev, "if_collisions", NULL, stats->collisions);
+#if defined(HAVE_STRUCT_RTNL_LINK_STATS_RX_NOHANDLER) ||                       \
+    defined(HAVE_STRUCT_RTNL_LINK_STATS64_RX_NOHANDLER)
+    submit_one(dev, "if_rx_nohandler", NULL, stats->rx_nohandler);
+#endif
 
     submit_one(dev, "if_rx_errors", "length", stats->rx_length_errors);
     submit_one(dev, "if_rx_errors", "over", stats->rx_over_errors);
@@ -304,6 +309,9 @@ static void check_ignorelist_and_submit64(const char *dev,
   struct ir_link_stats_storage_s s;
 
   COPY_RTNL_LINK_STATS(&s, stats);
+#ifdef HAVE_STRUCT_RTNL_LINK_STATS64_RX_NOHANDLER
+  COPY_RTNL_LINK_VALUE(&s, stats, rx_nohandler);
+#endif
 
   check_ignorelist_and_submit(dev, &s);
 }
@@ -314,6 +322,9 @@ static void check_ignorelist_and_submit32(const char *dev,
   struct ir_link_stats_storage_s s;
 
   COPY_RTNL_LINK_STATS(&s, stats);
+#ifdef HAVE_STRUCT_RTNL_LINK_STATS_RX_NOHANDLER
+  COPY_RTNL_LINK_VALUE(&s, stats, rx_nohandler);
+#endif
 
   check_ignorelist_and_submit(dev, &s);
 }
@@ -402,10 +413,9 @@ static int qos_attr_cb(const struct nlattr *attr, void *data) {
 
   if (mnl_attr_get_type(attr) == TCA_STATS_BASIC) {
     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*q_stats->bs)) < 0) {
-      char errbuf[1024];
       ERROR("netlink plugin: qos_attr_cb: TCA_STATS_BASIC mnl_attr_validate2 "
             "failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
       return MNL_CB_ERROR;
     }
     q_stats->bs = mnl_attr_get_payload(attr);
@@ -439,7 +449,7 @@ static int qos_filter_cb(const struct nlmsghdr *nlh, void *args) {
   const char *tc_type;
   char tc_inst[DATA_MAX_NAME_LEN];
 
-  _Bool stats_submitted = 0;
+  bool stats_submitted = false;
 
   if (nlh->nlmsg_type == RTM_NEWQDISC)
     tc_type = "qdisc";
@@ -462,7 +472,7 @@ static int qos_filter_cb(const struct nlmsghdr *nlh, void *args) {
 
   if ((tm->tcm_ifindex >= 0) && ((size_t)tm->tcm_ifindex >= iflist_len)) {
     ERROR("netlink plugin: qos_filter_cb: tm->tcm_ifindex = %i "
-          ">= iflist_len = %zu",
+          ">= iflist_len = %" PRIsz,
           tm->tcm_ifindex, iflist_len);
     return MNL_CB_ERROR;
   }
@@ -529,9 +539,15 @@ static int qos_filter_cb(const struct nlmsghdr *nlh, void *args) {
     if (q_stats.bs != NULL || q_stats.qs != NULL) {
       char type_instance[DATA_MAX_NAME_LEN];
 
-      stats_submitted = 1;
+      stats_submitted = true;
 
-      snprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type, tc_inst);
+      int r = snprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type,
+                       tc_inst);
+      if ((size_t)r >= sizeof(type_instance)) {
+        ERROR("netlink plugin: type_instance truncated to %zu bytes, need %d",
+              sizeof(type_instance), r);
+        return MNL_CB_ERROR;
+      }
 
       if (q_stats.bs != NULL) {
         submit_one(dev, "ipt_bytes", type_instance, q_stats.bs->bytes);
@@ -554,10 +570,9 @@ static int qos_filter_cb(const struct nlmsghdr *nlh, void *args) {
       continue;
 
     if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*ts)) < 0) {
-      char errbuf[1024];
       ERROR("netlink plugin: qos_filter_cb: TCA_STATS mnl_attr_validate2 "
             "failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
       return MNL_CB_ERROR;
     }
     ts = mnl_attr_get_payload(attr);
@@ -565,7 +580,13 @@ static int qos_filter_cb(const struct nlmsghdr *nlh, void *args) {
     if (!stats_submitted && ts != NULL) {
       char type_instance[DATA_MAX_NAME_LEN];
 
-      snprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type, tc_inst);
+      int r = snprintf(type_instance, sizeof(type_instance), "%s-%s", tc_type,
+                       tc_inst);
+      if ((size_t)r >= sizeof(type_instance)) {
+        ERROR("netlink plugin: type_instance truncated to %zu bytes, need %d",
+              sizeof(type_instance), r);
+        return MNL_CB_ERROR;
+      }
 
       submit_one(dev, "ipt_bytes", type_instance, ts->bytes);
       submit_one(dev, "ipt_packets", type_instance, ts->packets);
@@ -692,9 +713,7 @@ static int ir_read(void) {
     ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
   }
   if (ret < 0) {
-    char errbuf[1024];
-    ERROR("netlink plugin: ir_read: mnl_socket_recvfrom failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("netlink plugin: ir_read: mnl_socket_recvfrom failed: %s", STRERRNO);
     return (-1);
   }
 
@@ -715,7 +734,7 @@ static int ir_read(void) {
         continue;
       }
 
-      DEBUG("netlink plugin: ir_read: querying %s from %s (%zu).",
+      DEBUG("netlink plugin: ir_read: querying %s from %s (%" PRIsz ").",
             type_name[type_index], iflist[ifindex], ifindex);
 
       nlh = mnl_nlmsg_put_header(buf);
@@ -739,9 +758,8 @@ static int ir_read(void) {
         ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
       }
       if (ret < 0) {
-        char errbuf[1024];
         ERROR("netlink plugin: ir_read: mnl_socket_recvfrom failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+              STRERRNO);
         continue;
       }
     } /* for (type_index) */
index 4e68421..f6f0ac1 100644 (file)
@@ -27,8 +27,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 #include "utils_complain.h"
 #include "utils_fbhash.h"
@@ -113,6 +113,7 @@ struct sockent_client {
 #endif
   cdtime_t next_resolve_reconnect;
   cdtime_t resolve_interval;
+  struct sockaddr_storage *bind_addr;
 };
 
 struct sockent_server {
@@ -261,30 +262,30 @@ typedef struct receive_list_entry_s receive_list_entry_t;
 /*
  * Private variables
  */
-static int network_config_ttl = 0;
+static int network_config_ttl;
 /* Ethernet - (IPv6 + UDP) = 1500 - (40 + 8) = 1452 */
 static size_t network_config_packet_size = 1452;
-static _Bool network_config_forward = 0;
-static _Bool network_config_stats = 0;
+static bool network_config_forward;
+static bool network_config_stats;
 
-static sockent_t *sending_sockets = NULL;
+static sockent_t *sending_sockets;
 
-static receive_list_entry_t *receive_list_head = NULL;
-static receive_list_entry_t *receive_list_tail = NULL;
+static receive_list_entry_t *receive_list_head;
+static receive_list_entry_t *receive_list_tail;
 static pthread_mutex_t receive_list_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t receive_list_cond = PTHREAD_COND_INITIALIZER;
-static uint64_t receive_list_length = 0;
+static uint64_t receive_list_length;
 
-static sockent_t *listen_sockets = NULL;
-static struct pollfd *listen_sockets_pollfd = NULL;
-static size_t listen_sockets_num = 0;
+static sockent_t *listen_sockets;
+static struct pollfd *listen_sockets_pollfd;
+static size_t listen_sockets_num;
 
 /* The receive and dispatch threads will run as long as `listen_loop' is set to
  * zero. */
-static int listen_loop = 0;
-static int receive_thread_running = 0;
+static int listen_loop;
+static int receive_thread_running;
 static pthread_t receive_thread_id;
-static int dispatch_thread_running = 0;
+static int dispatch_thread_running;
 static pthread_t dispatch_thread_id;
 
 /* Buffer in which to-be-sent network packets are constructed. */
@@ -301,20 +302,20 @@ static pthread_mutex_t send_buffer_lock = PTHREAD_MUTEX_INITIALIZER;
  * example). Only if neither is true, the stats_lock is acquired. The counters
  * are always read without holding a lock in the hope that writing 8 bytes to
  * memory is an atomic operation. */
-static derive_t stats_octets_rx = 0;
-static derive_t stats_octets_tx = 0;
-static derive_t stats_packets_rx = 0;
-static derive_t stats_packets_tx = 0;
-static derive_t stats_values_dispatched = 0;
-static derive_t stats_values_not_dispatched = 0;
-static derive_t stats_values_sent = 0;
-static derive_t stats_values_not_sent = 0;
+static derive_t stats_octets_rx;
+static derive_t stats_octets_tx;
+static derive_t stats_packets_rx;
+static derive_t stats_packets_tx;
+static derive_t stats_values_dispatched;
+static derive_t stats_values_not_dispatched;
+static derive_t stats_values_sent;
+static derive_t stats_values_not_sent;
 static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
 
 /*
  * Private functions
  */
-static _Bool check_receive_okay(const value_list_t *vl) /* {{{ */
+static bool check_receive_okay(const value_list_t *vl) /* {{{ */
 {
   uint64_t time_sent = 0;
   int status;
@@ -327,11 +328,11 @@ static _Bool check_receive_okay(const value_list_t *vl) /* {{{ */
     return 0;
 
   return 1;
-} /* }}} _Bool check_receive_okay */
+} /* }}} bool check_receive_okay */
 
-static _Bool check_send_okay(const value_list_t *vl) /* {{{ */
+static bool check_send_okay(const value_list_t *vl) /* {{{ */
 {
-  _Bool received = 0;
+  bool received = 0;
   int status;
 
   if (network_config_forward)
@@ -353,22 +354,22 @@ static _Bool check_send_okay(const value_list_t *vl) /* {{{ */
   /* By default, only *send* value lists that were not *received* by the
    * network plugin. */
   return !received;
-} /* }}} _Bool check_send_okay */
+} /* }}} bool check_send_okay */
 
-static _Bool check_notify_received(const notification_t *n) /* {{{ */
+static bool check_notify_received(const notification_t *n) /* {{{ */
 {
   for (notification_meta_t *ptr = n->meta; ptr != NULL; ptr = ptr->next)
     if ((strcmp("network:received", ptr->name) == 0) &&
         (ptr->type == NM_TYPE_BOOLEAN))
-      return (_Bool)ptr->nm_value.nm_boolean;
+      return (bool)ptr->nm_value.nm_boolean;
 
   return 0;
-} /* }}} _Bool check_notify_received */
+} /* }}} bool check_notify_received */
 
-static _Bool check_send_notify_okay(const notification_t *n) /* {{{ */
+static bool check_send_notify_okay(const notification_t *n) /* {{{ */
 {
   static c_complain_t complain_forwarding = C_COMPLAIN_INIT_STATIC;
-  _Bool received = 0;
+  bool received = 0;
 
   if (n->meta == NULL)
     return 1;
@@ -388,7 +389,7 @@ static _Bool check_send_notify_okay(const notification_t *n) /* {{{ */
   /* By default, only *send* value lists that were not *received* by the
    * network plugin. */
   return !received;
-} /* }}} _Bool check_send_notify_okay */
+} /* }}} bool check_send_notify_okay */
 
 static int network_dispatch_values(value_list_t *vl, /* {{{ */
                                    const char *username) {
@@ -402,7 +403,7 @@ static int network_dispatch_values(value_list_t *vl, /* {{{ */
 #if COLLECT_DEBUG
     char name[6 * DATA_MAX_NAME_LEN];
     FORMAT_VL(name, sizeof(name), vl);
-    name[sizeof(name) - 1] = 0;
+    name[sizeof(name) - 1] = '\0';
     DEBUG("network plugin: network_dispatch_values: "
           "NOT dispatching %s.",
           name);
@@ -753,7 +754,7 @@ static int parse_part_values(void **ret_buffer, size_t *ret_buffer_len,
 
   if (buffer_len < 15) {
     NOTICE("network plugin: packet is too short: "
-           "buffer_len = %zu",
+           "buffer_len = %" PRIsz,
            buffer_len);
     return -1;
   }
@@ -777,8 +778,8 @@ static int parse_part_values(void **ret_buffer, size_t *ret_buffer_len,
   if (buffer_len < exp_size) {
     WARNING("network plugin: parse_part_values: "
             "Packet too short: "
-            "Chunk of size %zu expected, "
-            "but buffer has only %zu bytes left.",
+            "Chunk of size %" PRIsz " expected, "
+            "but buffer has only %" PRIsz " bytes left.",
             exp_size, buffer_len);
     return -1;
   }
@@ -857,8 +858,8 @@ static int parse_part_number(void **ret_buffer, size_t *ret_buffer_len,
   if (buffer_len < exp_size) {
     WARNING("network plugin: parse_part_number: "
             "Packet too short: "
-            "Chunk of size %zu expected, "
-            "but buffer has only %zu bytes left.",
+            "Chunk of size %" PRIsz " expected, "
+            "but buffer has only %" PRIsz " bytes left.",
             exp_size, buffer_len);
     return -1;
   }
@@ -898,8 +899,8 @@ static int parse_part_string(void **ret_buffer, size_t *ret_buffer_len,
   if (buffer_len < header_size) {
     WARNING("network plugin: parse_part_string: "
             "Packet too short: "
-            "Chunk of at least size %zu expected, "
-            "but buffer has only %zu bytes left.",
+            "Chunk of at least size %" PRIsz " expected, "
+            "but buffer has only %" PRIsz " bytes left.",
             header_size, buffer_len);
     return -1;
   }
@@ -918,7 +919,7 @@ static int parse_part_string(void **ret_buffer, size_t *ret_buffer_len,
     WARNING("network plugin: parse_part_string: "
             "Packet too big: "
             "Chunk of size %" PRIu16 " received, "
-            "but buffer has only %zu bytes left.",
+            "but buffer has only %" PRIsz " bytes left.",
             pkg_length, buffer_len);
     return -1;
   }
@@ -939,9 +940,9 @@ static int parse_part_string(void **ret_buffer, size_t *ret_buffer_len,
   if (output_len < payload_size) {
     WARNING("network plugin: parse_part_string: "
             "Buffer too small: "
-            "Output buffer holds %zu bytes, "
+            "Output buffer holds %" PRIsz " bytes, "
             "which is too small to hold the received "
-            "%zu byte string.",
+            "%" PRIsz " byte string.",
             output_len, payload_size);
     return -1;
   }
@@ -1113,7 +1114,7 @@ static int parse_part_sign_sha256(sockent_t *se, /* {{{ */
 static int parse_part_sign_sha256(sockent_t *se, /* {{{ */
                                   void **ret_buffer, size_t *ret_buffer_size,
                                   int flags) {
-  static int warning_has_been_printed = 0;
+  static int warning_has_been_printed;
 
   char *buffer;
   size_t buffer_size;
@@ -1268,7 +1269,7 @@ static int parse_part_encr_aes256(sockent_t *se, /* {{{ */
 static int parse_part_encr_aes256(sockent_t *se, /* {{{ */
                                   void **ret_buffer, size_t *ret_buffer_size,
                                   int flags) {
-  static int warning_has_been_printed = 0;
+  static int warning_has_been_printed;
 
   char *buffer;
   size_t buffer_size;
@@ -1501,6 +1502,7 @@ static void free_sockent_client(struct sockent_client *sec) /* {{{ */
     sec->fd = -1;
   }
   sfree(sec->addr);
+  sfree(sec->bind_addr);
 #if HAVE_GCRYPT_H
   sfree(sec->username);
   sfree(sec->password);
@@ -1578,9 +1580,7 @@ static int network_set_ttl(const sockent_t *se, const struct addrinfo *ai) {
 
     if (setsockopt(se->data.client.fd, IPPROTO_IP, optname, &network_config_ttl,
                    sizeof(network_config_ttl)) != 0) {
-      char errbuf[1024];
-      ERROR("network plugin: setsockopt (ipv4-ttl): %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("network plugin: setsockopt (ipv4-ttl): %s", STRERRNO);
       return -1;
     }
   } else if (ai->ai_family == AF_INET6) {
@@ -1596,9 +1596,7 @@ static int network_set_ttl(const sockent_t *se, const struct addrinfo *ai) {
 
     if (setsockopt(se->data.client.fd, IPPROTO_IPV6, optname,
                    &network_config_ttl, sizeof(network_config_ttl)) != 0) {
-      char errbuf[1024];
-      ERROR("network plugin: setsockopt(ipv6-ttl): %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("network plugin: setsockopt(ipv6-ttl): %s", STRERRNO);
       return -1;
     }
   }
@@ -1634,9 +1632,7 @@ static int network_set_interface(const sockent_t *se,
 
       if (setsockopt(se->data.client.fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq,
                      sizeof(mreq)) != 0) {
-        char errbuf[1024];
-        ERROR("network plugin: setsockopt (ipv4-multicast-if): %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("network plugin: setsockopt (ipv4-multicast-if): %s", STRERRNO);
         return -1;
       }
 
@@ -1648,9 +1644,7 @@ static int network_set_interface(const sockent_t *se,
     if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr)) {
       if (setsockopt(se->data.client.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
                      &se->interface, sizeof(se->interface)) != 0) {
-        char errbuf[1024];
-        ERROR("network plugin: setsockopt (ipv6-multicast-if): %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("network plugin: setsockopt (ipv6-multicast-if): %s", STRERRNO);
         return -1;
       }
 
@@ -1671,9 +1665,7 @@ static int network_set_interface(const sockent_t *se,
 
     if (setsockopt(se->data.client.fd, SOL_SOCKET, SO_BINDTODEVICE,
                    interface_name, sizeof(interface_name)) == -1) {
-      char errbuf[1024];
-      ERROR("network plugin: setsockopt (bind-if): %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("network plugin: setsockopt (bind-if): %s", STRERRNO);
       return -1;
     }
 /* #endif HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
@@ -1693,6 +1685,42 @@ static int network_set_interface(const sockent_t *se,
   return 0;
 } /* }}} network_set_interface */
 
+static int network_bind_socket_to_addr(sockent_t *se,
+                                       const struct addrinfo *ai) {
+
+  if (se->data.client.bind_addr == NULL)
+    return 0;
+
+  DEBUG("network_plugin: fd %i: bind socket to address", se->data.client.fd);
+  char pbuffer[64];
+
+  if (ai->ai_family == AF_INET) {
+    struct sockaddr_in *addr =
+        (struct sockaddr_in *)(se->data.client.bind_addr);
+    inet_ntop(AF_INET, &(addr->sin_addr), pbuffer, 64);
+    DEBUG("network_plugin: binding client socket to ipv4 address: %s", pbuffer);
+    if (bind(se->data.client.fd, (struct sockaddr *)addr, sizeof(*addr)) ==
+        -1) {
+      ERROR("network plugin: failed to bind client socket (ipv4) to %s: %s",
+            pbuffer, STRERRNO);
+      return -1;
+    }
+  } else if (ai->ai_family == AF_INET6) {
+    struct sockaddr_in6 *addr =
+        (struct sockaddr_in6 *)(se->data.client.bind_addr);
+    inet_ntop(AF_INET6, &(addr->sin6_addr), pbuffer, 64);
+    DEBUG("network_plugin: binding client socket to ipv6 address: %s", pbuffer);
+    if (bind(se->data.client.fd, (struct sockaddr *)addr, sizeof(*addr)) ==
+        -1) {
+      ERROR("network plugin: failed to bind client socket (ipv6) to %s: %s",
+            pbuffer, STRERRNO);
+      return -1;
+    }
+  }
+
+  return 0;
+} /* int network_bind_socket_to_addr */
+
 static int network_bind_socket(int fd, const struct addrinfo *ai,
                                const int interface_idx) {
 #if KERNEL_SOLARIS
@@ -1700,21 +1728,17 @@ static int network_bind_socket(int fd, const struct addrinfo *ai,
 #else
   int loop = 0;
 #endif
-  int yes = 1;
 
   /* allow multiple sockets to use the same PORT number */
-  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
-    char errbuf[1024];
-    ERROR("network plugin: setsockopt (reuseaddr): %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) == -1) {
+    ERROR("network plugin: setsockopt (reuseaddr): %s", STRERRNO);
     return -1;
   }
 
   DEBUG("fd = %i; calling `bind'", fd);
 
   if (bind(fd, ai->ai_addr, ai->ai_addrlen) == -1) {
-    char errbuf[1024];
-    ERROR("bind: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("bind: %s", STRERRNO);
     return -1;
   }
 
@@ -1742,17 +1766,13 @@ static int network_bind_socket(int fd, const struct addrinfo *ai,
 
       if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) ==
           -1) {
-        char errbuf[1024];
-        ERROR("network plugin: setsockopt (multicast-loop): %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("network plugin: setsockopt (multicast-loop): %s", STRERRNO);
         return -1;
       }
 
       if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) ==
           -1) {
-        char errbuf[1024];
-        ERROR("network plugin: setsockopt (add-membership): %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("network plugin: setsockopt (add-membership): %s", STRERRNO);
         return -1;
       }
 
@@ -1782,17 +1802,13 @@ static int network_bind_socket(int fd, const struct addrinfo *ai,
 
       if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop,
                      sizeof(loop)) == -1) {
-        char errbuf[1024];
-        ERROR("network plugin: setsockopt (ipv6-multicast-loop): %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("network plugin: setsockopt (ipv6-multicast-loop): %s", STRERRNO);
         return -1;
       }
 
       if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
                      sizeof(mreq)) == -1) {
-        char errbuf[1024];
-        ERROR("network plugin: setsockopt (ipv6-add-membership): %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("network plugin: setsockopt (ipv6-add-membership): %s", STRERRNO);
         return -1;
       }
 
@@ -1815,9 +1831,7 @@ static int network_bind_socket(int fd, const struct addrinfo *ai,
 
     if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, interface_name,
                    sizeof(interface_name)) == -1) {
-      char errbuf[1024];
-      ERROR("network plugin: setsockopt (bind-if): %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("network plugin: setsockopt (bind-if): %s", STRERRNO);
       return -1;
     }
   }
@@ -1857,6 +1871,7 @@ static sockent_t *sockent_create(int type) /* {{{ */
   } else {
     se->data.client.fd = -1;
     se->data.client.addr = NULL;
+    se->data.client.bind_addr = NULL;
     se->data.client.resolve_interval = 0;
     se->data.client.next_resolve_reconnect = 0;
 #if HAVE_GCRYPT_H
@@ -1947,7 +1962,7 @@ static int sockent_client_connect(sockent_t *se) /* {{{ */
   struct sockent_client *client;
   struct addrinfo *ai_list;
   int status;
-  _Bool reconnect = 0;
+  bool reconnect = false;
   cdtime_t now;
 
   if ((se == NULL) || (se->type != SOCKENT_TYPE_CLIENT))
@@ -1961,7 +1976,7 @@ static int sockent_client_connect(sockent_t *se) /* {{{ */
           "next_resolve_reconnect = %lf",
           CDTIME_T_TO_DOUBLE(client->resolve_interval),
           CDTIME_T_TO_DOUBLE(client->next_resolve_reconnect));
-    reconnect = 1;
+    reconnect = true;
   }
 
   if (client->fd >= 0 && !reconnect) /* already connected and not stale*/
@@ -1994,9 +2009,7 @@ static int sockent_client_connect(sockent_t *se) /* {{{ */
     client->fd =
         socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (client->fd < 0) {
-      char errbuf[1024];
-      ERROR("network plugin: socket(2) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("network plugin: socket(2) failed: %s", STRERRNO);
       continue;
     }
 
@@ -2014,6 +2027,7 @@ static int sockent_client_connect(sockent_t *se) /* {{{ */
 
     network_set_ttl(se, ai_ptr);
     network_set_interface(se, ai_ptr);
+    network_bind_socket_to_addr(se, ai_ptr);
 
     /* We don't open more than one write-socket per
      * node/service pair.. */
@@ -2081,9 +2095,7 @@ static int sockent_server_listen(sockent_t *se) /* {{{ */
 
     *tmp = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (*tmp < 0) {
-      char errbuf[1024];
-      ERROR("network plugin: socket(2) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("network plugin: socket(2) failed: %s", STRERRNO);
       continue;
     }
 
@@ -2231,11 +2243,9 @@ static int network_receive(void) /* {{{ */
   while (listen_loop == 0) {
     status = poll(listen_sockets_pollfd, listen_sockets_num, -1);
     if (status <= 0) {
-      char errbuf[1024];
       if (errno == EINTR)
         continue;
-      ERROR("network plugin: poll(2) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("network plugin: poll(2) failed: %s", STRERRNO);
       break;
     }
 
@@ -2249,10 +2259,8 @@ static int network_receive(void) /* {{{ */
       buffer_len = recv(listen_sockets_pollfd[i].fd, buffer, sizeof(buffer),
                         0 /* no flags */);
       if (buffer_len < 0) {
-        char errbuf[1024];
         status = (errno != 0) ? errno : -1;
-        ERROR("network plugin: recv(2) failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("network plugin: recv(2) failed: %s", STRERRNO);
         break;
       }
 
@@ -2362,13 +2370,11 @@ static void network_send_buffer_plain(sockent_t *se, /* {{{ */
                     /* flags = */ 0, (struct sockaddr *)se->data.client.addr,
                     se->data.client.addrlen);
     if (status < 0) {
-      char errbuf[1024];
-
       if ((errno == EINTR) || (errno == EAGAIN))
         continue;
 
       ERROR("network plugin: sendto failed: %s. Closing sending socket.",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
       sockent_client_disconnect(se);
       return;
     }
@@ -2480,7 +2486,7 @@ static void network_send_buffer_encrypted(sockent_t *se, /* {{{ */
 
   assert(buffer_size <= sizeof(buffer));
   DEBUG("network plugin: network_send_buffer_encrypted: "
-        "buffer_size = %zu;",
+        "buffer_size = %" PRIsz ";",
         buffer_size);
 
   pea.head.length = htons(
@@ -2531,7 +2537,8 @@ static void network_send_buffer_encrypted(sockent_t *se, /* {{{ */
 
 static void network_send_buffer(char *buffer, size_t buffer_len) /* {{{ */
 {
-  DEBUG("network plugin: network_send_buffer: buffer_len = %zu", buffer_len);
+  DEBUG("network plugin: network_send_buffer: buffer_len = %" PRIsz,
+        buffer_len);
 
   for (sockent_t *se = sending_sockets; se != NULL; se = se->next) {
 #if HAVE_GCRYPT_H
@@ -2633,7 +2640,7 @@ static int network_write(const data_set_t *ds, const value_list_t *vl,
 #if COLLECT_DEBUG
     char name[6 * DATA_MAX_NAME_LEN];
     FORMAT_VL(name, sizeof(name), vl);
-    name[sizeof(name) - 1] = 0;
+    name[sizeof(name) - 1] = '\0';
     DEBUG("network plugin: network_write: "
           "NOT sending %s.",
           name);
@@ -2650,10 +2657,10 @@ static int network_write(const data_set_t *ds, const value_list_t *vl,
 
   pthread_mutex_lock(&send_buffer_lock);
 
-  status =
-      add_to_buffer(send_buffer_ptr, network_config_packet_size -
-                                         (send_buffer_fill + BUFF_SIG_SIZE),
-                    &send_buffer_vl, ds, vl);
+  status = add_to_buffer(send_buffer_ptr,
+                         network_config_packet_size -
+                             (send_buffer_fill + BUFF_SIG_SIZE),
+                         &send_buffer_vl, ds, vl);
   if (status >= 0) {
     /* status == bytes added to the buffer */
     send_buffer_fill += status;
@@ -2664,10 +2671,10 @@ static int network_write(const data_set_t *ds, const value_list_t *vl,
   } else {
     flush_buffer();
 
-    status =
-        add_to_buffer(send_buffer_ptr, network_config_packet_size -
-                                           (send_buffer_fill + BUFF_SIG_SIZE),
-                      &send_buffer_vl, ds, vl);
+    status = add_to_buffer(send_buffer_ptr,
+                           network_config_packet_size -
+                               (send_buffer_fill + BUFF_SIG_SIZE),
+                           &send_buffer_vl, ds, vl);
 
     if (status >= 0) {
       send_buffer_fill += status;
@@ -2716,6 +2723,57 @@ static int network_config_set_interface(const oconfig_item_t *ci, /* {{{ */
   return 0;
 } /* }}} int network_config_set_interface */
 
+static int
+network_config_set_bind_address(const oconfig_item_t *ci,
+                                struct sockaddr_storage **bind_address) {
+  if ((*bind_address) != NULL) {
+    ERROR("network_plugin: only a single bind address is allowed");
+    return -1;
+  }
+
+  char addr_text[256];
+
+  if (cf_util_get_string_buffer(ci, addr_text, sizeof(addr_text)) != 0)
+    return -1;
+
+  int ret;
+  struct addrinfo *res = NULL;
+  struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
+                              .ai_flags = AI_NUMERICHOST,
+                              .ai_protocol = IPPROTO_UDP,
+                              .ai_socktype = SOCK_DGRAM};
+
+  ret = getaddrinfo(addr_text, NULL, &ai_hints, &res);
+  if (ret) {
+    ERROR("network plugin: Bind address option has invalid address set: %s",
+          gai_strerror(ret));
+    return -1;
+  }
+
+  *bind_address = malloc(sizeof(**bind_address));
+  if (*bind_address == NULL) {
+    ERROR("network plugin: network_config_set_bind_address: malloc failed.");
+    return -1;
+  }
+  (*bind_address)->ss_family = res->ai_family;
+  if (res->ai_family == AF_INET) {
+    struct sockaddr_in *addr = (struct sockaddr_in *)(*bind_address);
+    inet_pton(AF_INET, addr_text, &(addr->sin_addr));
+  } else if (res->ai_family == AF_INET6) {
+    struct sockaddr_in6 *addr = (struct sockaddr_in6 *)(*bind_address);
+    inet_pton(AF_INET6, addr_text, &(addr->sin6_addr));
+  } else {
+    ERROR("network plugin: %s is an unknown address format %d\n", addr_text,
+          res->ai_family);
+    sfree(*bind_address);
+    freeaddrinfo(res);
+    return -1;
+  }
+
+  freeaddrinfo(res);
+  return 0;
+} /* int network_config_set_bind_address */
+
 static int network_config_set_buffer_size(const oconfig_item_t *ci) /* {{{ */
 {
   int tmp = 0;
@@ -2875,6 +2933,8 @@ static int network_config_add_server(const oconfig_item_t *ci) /* {{{ */
 #endif /* HAVE_GCRYPT_H */
         if (strcasecmp("Interface", child->key) == 0)
       network_config_set_interface(child, &se->interface);
+    else if (strcasecmp("BindAddress", child->key) == 0)
+      network_config_set_bind_address(child, &se->data.client.bind_addr);
     else if (strcasecmp("ResolveInterval", child->key) == 0)
       cf_util_get_cdtime(child, &se->data.client.resolve_interval);
     else {
@@ -3128,13 +3188,13 @@ static int network_stats_read(void) /* {{{ */
 } /* }}} int network_stats_read */
 
 static int network_init(void) {
-  static _Bool have_init = 0;
+  static bool have_init;
 
   /* Check if we were already initialized. If so, just return - there's
    * nothing more to do (for now, that is). */
   if (have_init)
     return 0;
-  have_init = 1;
+  have_init = true;
 
   if (network_config_stats)
     plugin_register_read("network", network_stats_read);
@@ -3167,9 +3227,7 @@ static int network_init(void) {
                                   dispatch_thread, NULL /* no argument */,
                                   "network disp");
     if (status != 0) {
-      char errbuf[1024];
-      ERROR("network: pthread_create failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("network: pthread_create failed: %s", STRERRNO);
     } else {
       dispatch_thread_running = 1;
     }
@@ -3181,9 +3239,7 @@ static int network_init(void) {
                                   receive_thread, NULL /* no argument */,
                                   "network recv");
     if (status != 0) {
-      char errbuf[1024];
-      ERROR("network: pthread_create failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("network: pthread_create failed: %s", STRERRNO);
     } else {
       receive_thread_running = 1;
     }
index 0fc9f3a..320caa4 100644 (file)
--- a/src/nfs.c
+++ b/src/nfs.c
@@ -24,8 +24,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_KSTAT_H
 #include <kstat.h>
@@ -33,9 +33,9 @@
 
 static const char *config_keys[] = {"ReportV2", "ReportV3", "ReportV4"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
-static _Bool report_v2 = 1;
-static _Bool report_v3 = 1;
-static _Bool report_v4 = 1;
+static bool report_v2 = true;
+static bool report_v3 = true;
+static bool report_v4 = true;
 
 /*
 see /proc/net/rpc/nfs
@@ -397,7 +397,7 @@ static int nfs_submit_fields_safe(int nfs_version, const char *instance,
                                   size_t proc_names_num) {
   if (fields_num != proc_names_num) {
     WARNING("nfs plugin: Wrong number of fields for "
-            "NFSv%i %s statistics. Expected %zu, got %zu.",
+            "NFSv%i %s statistics. Expected %" PRIsz ", got %" PRIsz ".",
             nfs_version, instance, proc_names_num, fields_num);
     return EINVAL;
   }
@@ -409,7 +409,7 @@ static int nfs_submit_fields_safe(int nfs_version, const char *instance,
 
 static int nfs_submit_nfs4_server(const char *instance, char **fields,
                                   size_t fields_num) {
-  static int suppress_warning = 0;
+  static int suppress_warning;
   size_t proc4x_names_num;
 
   switch (fields_num) {
@@ -421,7 +421,7 @@ static int nfs_submit_nfs4_server(const char *instance, char **fields,
   default:
     if (!suppress_warning) {
       WARNING("nfs plugin: Unexpected number of fields for "
-              "NFSv4 %s statistics: %zu. ",
+              "NFSv4 %s statistics: %" PRIsz ". ",
               instance, fields_num);
     }
 
@@ -451,7 +451,7 @@ static int nfs_submit_nfs4_client(const char *instance, char **fields,
                                   size_t fields_num) {
   size_t proc40_names_num, proc4x_names_num;
 
-  static int suppress_warning = 0;
+  static int suppress_warning;
 
   switch (fields_num) {
   case 34:
@@ -486,9 +486,8 @@ static int nfs_submit_nfs4_client(const char *instance, char **fields,
     break;
   default:
     if (!suppress_warning) {
-      WARNING("nfs plugin: Unexpected number of "
-              "fields for NFSv4 %s "
-              "statistics: %zu. ",
+      WARNING("nfs plugin: Unexpected number of fields for NFSv4 %s "
+              "statistics: %" PRIsz ". ",
               instance, fields_num);
     }
 
index 88118b9..7bb307a 100644 (file)
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <curl/curl.h>
 
-static char *url = NULL;
-static char *user = NULL;
-static char *pass = NULL;
-static char *verify_peer = NULL;
-static char *verify_host = NULL;
-static char *cacert = NULL;
-static char *timeout = NULL;
+static char *url;
+static char *user;
+static char *pass;
+static char *verify_peer;
+static char *verify_host;
+static char *cacert;
+static char *timeout;
 
-static CURL *curl = NULL;
+static CURL *curl;
 
 static char nginx_buffer[16384];
-static size_t nginx_buffer_len = 0;
+static size_t nginx_buffer_len;
 static char nginx_curl_error[CURL_ERROR_SIZE];
 
 static const char *config_keys[] = {
index e391cf2..849b1d4 100644 (file)
@@ -30,8 +30,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <glib.h>
 #include <libnotify/notify.h>
index 52cc838..dddb8b2 100644 (file)
@@ -24,8 +24,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <auth-client.h>
 #include <libesmtp.h>
@@ -38,19 +38,19 @@ static const char *config_keys[] = {"SMTPServer",   "SMTPPort", "SMTPUser",
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
 static char **recipients;
-static int recipients_len = 0;
+static int recipients_len;
 
 static smtp_session_t session;
 static pthread_mutex_t session_lock = PTHREAD_MUTEX_INITIALIZER;
 static smtp_message_t message;
-static auth_context_t authctx = NULL;
+static auth_context_t authctx;
 
 static int smtp_port = 25;
-static char *smtp_host = NULL;
-static char *smtp_user = NULL;
-static char *smtp_password = NULL;
-static char *email_from = NULL;
-static char *email_subject = NULL;
+static char *smtp_host;
+static char *smtp_user;
+static char *smtp_password;
+static char *email_from;
+static char *email_subject;
 
 #define DEFAULT_SMTP_HOST "localhost"
 #define DEFAULT_SMTP_FROM "root@localhost"
@@ -211,8 +211,8 @@ static int notify_email_notification(const notification_t *n,
   char subject[MAXSTRING];
 
   char buf[4096] = "";
+  char *buf_ptr = buf;
   int buf_len = sizeof(buf);
-  int i;
 
   snprintf(severity, sizeof(severity), "%s",
            (n->severity == NOTIF_FAILURE)
@@ -231,15 +231,36 @@ static int notify_email_notification(const notification_t *n,
   timestamp_str[sizeof(timestamp_str) - 1] = '\0';
 
   /* Let's make RFC822 message text with \r\n EOLs */
-  snprintf(buf, buf_len, "MIME-Version: 1.0\r\n"
-                         "Content-Type: text/plain; charset=\"US-ASCII\"\r\n"
-                         "Content-Transfer-Encoding: 8bit\r\n"
-                         "Subject: %s\r\n"
-                         "\r\n"
-                         "%s - %s@%s\r\n"
-                         "\r\n"
-                         "Message: %s",
-           subject, timestamp_str, severity, n->host, n->message);
+  int status = snprintf(buf, buf_len,
+                        "MIME-Version: 1.0\r\n"
+                        "Content-Type: text/plain; charset=\"US-ASCII\"\r\n"
+                        "Content-Transfer-Encoding: 8bit\r\n"
+                        "Subject: %s\r\n"
+                        "\r\n"
+                        "%s - %s@%s\r\n"
+                        "\r\n",
+                        subject, timestamp_str, severity, n->host);
+
+  if (status > 0) {
+    buf_ptr += status;
+    buf_len -= status;
+  }
+
+#define APPEND(format, value)                                                  \
+  if ((buf_len > 0) && (strlen(value) > 0)) {                                  \
+    status = snprintf(buf_ptr, buf_len, format "\r\n", value);                 \
+    if (status > 0) {                                                          \
+      buf_ptr += status;                                                       \
+      buf_len -= status;                                                       \
+    }                                                                          \
+  }
+
+  APPEND("Host: %s", n->host);
+  APPEND("Plugin: %s", n->plugin);
+  APPEND("Plugin instance: %s", n->plugin_instance);
+  APPEND("Type: %s", n->type);
+  APPEND("Type instance: %s", n->type_instance);
+  APPEND("\r\nMessage: %s", n->message);
 
   pthread_mutex_lock(&session_lock);
 
@@ -258,7 +279,7 @@ static int notify_email_notification(const notification_t *n,
   smtp_set_header(message, "To", NULL, NULL);
   smtp_set_message_str(message, buf);
 
-  for (i = 0; i < recipients_len; i++)
+  for (int i = 0; i < recipients_len; i++)
     smtp_add_recipient(message, recipients[i]);
 
   /* Initiate a connection to the SMTP server and transfer the message. */
index f744d48..79926b5 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #define NAGIOS_OK 0
 #define NAGIOS_WARNING 1
@@ -67,10 +67,8 @@ static int nagios_print(char const *buffer) /* {{{ */
 
   fd = open(file, O_WRONLY | O_APPEND);
   if (fd < 0) {
-    char errbuf[1024];
     status = errno;
-    ERROR("notify_nagios plugin: Opening \"%s\" failed: %s", file,
-          sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("notify_nagios plugin: Opening \"%s\" failed: %s", file, STRERRNO);
     return status;
   }
 
@@ -79,30 +77,26 @@ static int nagios_print(char const *buffer) /* {{{ */
 
   status = fcntl(fd, F_GETLK, &lock);
   if (status != 0) {
-    char errbuf[1024];
     status = errno;
     ERROR("notify_nagios plugin: Failed to acquire write lock on \"%s\": %s",
-          file, sstrerror(status, errbuf, sizeof(errbuf)));
+          file, STRERRNO);
     close(fd);
     return status;
   }
 
   status = (int)lseek(fd, 0, SEEK_END);
   if (status == -1) {
-    char errbuf[1024];
     status = errno;
     ERROR("notify_nagios plugin: Seeking to end of \"%s\" failed: %s", file,
-          sstrerror(status, errbuf, sizeof(errbuf)));
+          STRERRNO);
     close(fd);
     return status;
   }
 
   status = (int)swrite(fd, buffer, strlen(buffer));
   if (status != 0) {
-    char errbuf[1024];
     status = errno;
-    ERROR("notify_nagios plugin: Writing to \"%s\" failed: %s", file,
-          sstrerror(status, errbuf, sizeof(errbuf)));
+    ERROR("notify_nagios plugin: Writing to \"%s\" failed: %s", file, STRERRNO);
     close(fd);
     return status;
   }
index 205403d..33acc69 100644 (file)
@@ -29,8 +29,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_NETDB_H
 #include <netdb.h>
@@ -56,17 +56,17 @@ static const char *config_keys[] = {"Host", "Port", "ReverseLookups",
                                     "IncludeUnitID"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static _Bool do_reverse_lookups = 1;
+static bool do_reverse_lookups = true;
 
 /* This option only exists for backward compatibility. If it is false and two
  * ntpd peers use the same refclock driver, the plugin will try to write
  * simultaneous measurements from both to the same type instance. */
-static _Bool include_unit_id = 0;
+static bool include_unit_id;
 
 #define NTPD_DEFAULT_HOST "localhost"
 #define NTPD_DEFAULT_PORT "123"
 static int sock_descr = -1;
-static char *ntpd_host = NULL;
+static char *ntpd_host;
 static char ntpd_port[16];
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
@@ -251,7 +251,7 @@ static const char *refclock_names[] = {
     "CHRONOLOG",  "DUMBCLOCK",    "ULINK_M320", "PCF",         /* 32-35 */
     "WWV_AUDIO",  "GPS_FG",       "HOPF_S",     "HOPF_P",      /* 36-39 */
     "JJY",        "TT_IRIG",      "GPS_ZYFER",  "GPS_RIPENCC", /* 40-43 */
-    "NEOCLK4X"                                                 /* 44    */
+    "NEOCLK4X",   "PCI_TSYNC",    "GPSD_JSON"                  /* 44-46 */
 };
 static size_t refclock_names_num = STATIC_ARRAY_SIZE(refclock_names);
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
@@ -272,14 +272,14 @@ static int ntpd_config(const char *key, const char *value) {
       sstrncpy(ntpd_port, value, sizeof(ntpd_port));
   } else if (strcasecmp(key, "ReverseLookups") == 0) {
     if (IS_TRUE(value))
-      do_reverse_lookups = 1;
+      do_reverse_lookups = true;
     else
-      do_reverse_lookups = 0;
+      do_reverse_lookups = false;
   } else if (strcasecmp(key, "IncludeUnitID") == 0) {
     if (IS_TRUE(value))
-      include_unit_id = 1;
+      include_unit_id = true;
     else
-      include_unit_id = 0;
+      include_unit_id = false;
   } else {
     return -1;
   }
@@ -337,10 +337,8 @@ static int ntpd_connect(void) {
                               .ai_socktype = SOCK_DGRAM};
 
   if ((status = getaddrinfo(host, port, &ai_hints, &ai_list)) != 0) {
-    char errbuf[1024];
     ERROR("ntpd plugin: getaddrinfo (%s, %s): %s", host, port,
-          (status == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                 : gai_strerror(status));
+          (status == EAI_SYSTEM) ? STRERRNO : gai_strerror(status));
     return -1;
   }
 
@@ -409,9 +407,7 @@ static int ntpd_receive_response(int *res_items, int *res_size, char **res_data,
   *res_data = NULL;
 
   if (gettimeofday(&time_end, NULL) < 0) {
-    char errbuf[1024];
-    ERROR("ntpd plugin: gettimeofday failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("ntpd plugin: gettimeofday failed: %s", STRERRNO);
     return -1;
   }
   time_end.tv_sec++; /* wait for a most one second */
@@ -421,9 +417,7 @@ static int ntpd_receive_response(int *res_items, int *res_size, char **res_data,
     struct timeval time_left;
 
     if (gettimeofday(&time_now, NULL) < 0) {
-      char errbuf[1024];
-      ERROR("ntpd plugin: gettimeofday failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("ntpd plugin: gettimeofday failed: %s", STRERRNO);
       return -1;
     }
 
@@ -447,9 +441,7 @@ static int ntpd_receive_response(int *res_items, int *res_size, char **res_data,
       continue;
 
     if (status < 0) {
-      char errbuf[1024];
-      ERROR("ntpd plugin: poll failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("ntpd plugin: poll failed: %s", STRERRNO);
       return -1;
     }
 
@@ -466,8 +458,7 @@ static int ntpd_receive_response(int *res_items, int *res_size, char **res_data,
       continue;
 
     if (status < 0) {
-      char errbuf[1024];
-      INFO("recv(2) failed: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+      INFO("recv(2) failed: %s", STRERRNO);
       DEBUG("Closing socket #%i", sd);
       close(sd);
       sock_descr = sd = -1;
@@ -593,7 +584,7 @@ static int ntpd_receive_response(int *res_items, int *res_size, char **res_data,
      * Enough with the checks. Copy the data now.
      * We start by allocating some more memory.
      */
-    DEBUG("realloc (%p, %zu)", (void *)*res_data,
+    DEBUG("realloc (%p, %" PRIsz ")", (void *)*res_data,
           (items_num + pkt_item_num) * res_item_size);
     items = realloc(*res_data, (items_num + pkt_item_num) * res_item_size);
     if (items == NULL) {
@@ -726,7 +717,7 @@ ntpd_get_refclock_id(struct info_peer_summary const *peer_info) {
 
 static int ntpd_get_name_from_address(char *buffer, size_t buffer_size,
                                       struct info_peer_summary const *peer_info,
-                                      _Bool do_reverse_lookup) {
+                                      bool do_reverse_lookup) {
   struct sockaddr_storage sa = {0};
   socklen_t sa_len;
   int flags = 0;
@@ -763,10 +754,8 @@ static int ntpd_get_name_from_address(char *buffer, size_t buffer_size,
                        buffer_size, NULL, 0, /* No port name */
                        flags);
   if (status != 0) {
-    char errbuf[1024];
     ERROR("ntpd plugin: getnameinfo failed: %s",
-          (status == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                 : gai_strerror(status));
+          (status == EAI_SYSTEM) ? STRERRNO : gai_strerror(status));
     return -1;
   }
 
@@ -790,17 +779,6 @@ static int ntpd_get_name_refclock(char *buffer, size_t buffer_size,
   return 0;
 } /* int ntpd_get_name_refclock */
 
-static int ntpd_get_name(char *buffer, size_t buffer_size,
-                         struct info_peer_summary const *peer_info) {
-  uint32_t addr = ntohl(peer_info->srcadr);
-
-  if (!peer_info->v6_flag && ((addr & REFCLOCK_MASK) == REFCLOCK_ADDR))
-    return ntpd_get_name_refclock(buffer, buffer_size, peer_info);
-  else
-    return ntpd_get_name_from_address(buffer, buffer_size, peer_info,
-                                      do_reverse_lookups);
-} /* int ntpd_addr_to_name */
-
 static int ntpd_read(void) {
   struct info_kernel *ik;
   int ik_num;
@@ -892,18 +870,34 @@ static int ntpd_read(void) {
 
     ptr = ps + i;
 
-    status = ntpd_get_name(peername, sizeof(peername), ptr);
+    int is_refclock = !ptr->v6_flag &&
+                      ((ntohl(ptr->srcadr) & REFCLOCK_MASK) == REFCLOCK_ADDR);
+
+    if (is_refclock)
+      status = ntpd_get_name_refclock(peername, sizeof(peername), ptr);
+    else
+      status = ntpd_get_name_from_address(peername, sizeof(peername), ptr,
+                                          do_reverse_lookups);
+
     if (status != 0) {
       ERROR("ntpd plugin: Determining name of peer failed.");
       continue;
     }
 
+    // `0.0.0.0` hosts are caused by POOL servers
+    // see https://github.com/collectd/collectd/issues/2358
+    if (strcmp(peername, "0.0.0.0") == 0) {
+      continue;
+    }
+
     refclock_id = ntpd_get_refclock_id(ptr);
 
     /* Convert the `long floating point' offset value to double */
     M_LFPTOD(ntohl(ptr->offset_int), ntohl(ptr->offset_frc), offset);
 
     DEBUG("peer %i:\n"
+          "  is_refclock= %d\n"
+          "  refclock_id= %d\n"
           "  peername   = %s\n"
           "  srcadr     = 0x%08x\n"
           "  reach      = 0%03o\n"
@@ -912,16 +906,19 @@ static int ntpd_read(void) {
           "  offset_frc = %i\n"
           "  offset     = %f\n"
           "  dispersion = %f\n",
-          i, peername, ntohl(ptr->srcadr), ptr->reach, ntpd_read_fp(ptr->delay),
+          i, is_refclock, (is_refclock > 0) ? refclock_id : 0, peername,
+          ntohl(ptr->srcadr), ptr->reach, ntpd_read_fp(ptr->delay),
           ntohl(ptr->offset_int), ntohl(ptr->offset_frc), offset,
           ntpd_read_fp(ptr->dispersion));
 
-    if (refclock_id !=
-        1) /* not the system clock (offset will always be zero.. */
-      ntpd_submit_reach("time_offset", peername, ptr->reach, offset);
     ntpd_submit_reach("time_dispersion", peername, ptr->reach,
                       ntpd_read_fp(ptr->dispersion));
-    if (refclock_id == 0) /* not a reference clock */
+
+    /* not the system clock (offset will always be zero) */
+    if (!(is_refclock && refclock_id == 1))
+      ntpd_submit_reach("time_offset", peername, ptr->reach, offset);
+
+    if (!is_refclock) /* not a reference clock */
       ntpd_submit_reach("delay", peername, ptr->reach,
                         ntpd_read_fp(ptr->delay));
   }
index 56ea707..dfe7a6d 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if !KERNEL_LINUX
 #error "No applicable input method."
@@ -66,9 +66,8 @@ static int numa_read_node(int node) /* {{{ */
 
   fh = fopen(path, "r");
   if (fh == NULL) {
-    char errbuf[1024];
     ERROR("numa plugin: Reading node %i failed: open(%s): %s", node, path,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return -1;
   }
 
@@ -137,9 +136,7 @@ static int numa_init(void) /* {{{ */
       break;
     } else /* ((status != 0) && (errno != ENOENT)) */
     {
-      char errbuf[1024];
-      ERROR("numa plugin: stat(%s) failed: %s", path,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("numa plugin: stat(%s) failed: %s", path, STRERRNO);
       return -1;
     }
   }
index 58c7d79..ae48692 100644 (file)
--- a/src/nut.c
+++ b/src/nut.c
@@ -27,8 +27,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <upsclient.h>
 
@@ -53,11 +53,11 @@ struct nut_ups_s {
 static const char *config_keys[] = {"UPS", "FORCESSL", "VERIFYPEER", "CAPATH",
                                     "CONNECTTIMEOUT"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
-static int force_ssl = 0;   // Initialized to default of 0 (false)
-static int verify_peer = 0; // Initialized to default of 0 (false)
+static int force_ssl;   // Initialized to default of 0 (false)
+static int verify_peer; // Initialized to default of 0 (false)
 static int ssl_flags = UPSCLI_CONN_TRYSSL;
 static int connect_timeout = -1;
-static char *ca_path = NULL;
+static char *ca_path;
 
 static int nut_read(user_data_t *user_data);
 
@@ -144,8 +144,7 @@ static int nut_verify_peer(const char *value) {
 
 static int nut_ca_path(const char *value) {
   if (value != NULL && strcmp(value, "") != 0) {
-    ca_path = malloc(strlen(value) + 1);
-    strncpy(ca_path, value, (strlen(value) + 1));
+    ca_path = strdup(value);
   } else {
     ca_path = NULL; // Should alread be set to NULL from initialization
   }
index 1ac1d42..c8b8b7a 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <netdb.h>
 #include <netinet/in.h>
@@ -41,8 +41,8 @@ static const char *config_keys[] = {"Host", "Port", "CollectLinks",
                                     "CollectRoutes", "CollectTopology"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static char *config_node = NULL;
-static char *config_service = NULL;
+static char *config_node;
+static char *config_service;
 
 #define OLSRD_WANT_NOT 0
 #define OLSRD_WANT_SUMMARY 1
@@ -162,19 +162,16 @@ static FILE *olsrd_connect(void) /* {{{ */
        ai_ptr = ai_ptr->ai_next) {
     int fd;
     int status;
-    char errbuf[1024];
 
     fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (fd < 0) {
-      ERROR("olsrd plugin: socket failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("olsrd plugin: socket failed: %s", STRERRNO);
       continue;
     }
 
     status = connect(fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
     if (status != 0) {
-      ERROR("olsrd plugin: connect failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("olsrd plugin: connect failed: %s", STRERRNO);
       close(fd);
       continue;
     }
index 5c5152d..a0a546b 100644 (file)
@@ -21,9 +21,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #include <owcapi.h>
 #include <regex.h>
@@ -86,9 +86,9 @@ static ow_family_features_t ow_family_features[] = {
      /* features_num = */ 1}};
 static int ow_family_features_num = STATIC_ARRAY_SIZE(ow_family_features);
 
-static char *device_g = NULL;
-static cdtime_t ow_interval = 0;
-static _Bool direct_access = 0;
+static char *device_g;
+static cdtime_t ow_interval;
+static bool direct_access;
 
 static const char *config_keys[] = {"Device", "IgnoreSelected", "Sensor",
                                     "Interval"};
@@ -96,7 +96,7 @@ static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
 static ignorelist_t *sensor_list;
 
-static _Bool regex_direct_initialized = 0;
+static bool regex_direct_initialized;
 static regex_t regex_direct;
 
 /**
@@ -109,7 +109,7 @@ typedef struct direct_access_element_s {
   struct direct_access_element_s *next; /**< Next in the list */
 } direct_access_element_t;
 
-static direct_access_element_t *direct_list = NULL;
+static direct_access_element_t *direct_list;
 
 /* ===================================================================================
  */
@@ -171,7 +171,7 @@ static int direct_list_insert(const char *config) {
       direct_list_element_free(element);
       return 1;
     }
-    regex_direct_initialized = 1;
+    regex_direct_initialized = true;
     DEBUG("onewire plugin: Compiled regex!!");
   }
 
@@ -246,7 +246,7 @@ static int cow_load_config(const char *key, const char *value) {
       }
     } else {
       DEBUG("onewire plugin: %s is a direct access", value);
-      direct_access = 1;
+      direct_access = true;
     }
   } else if (strcasecmp(key, "IgnoreSelected") == 0) {
     ignorelist_set_invert(sensor_list, 1);
@@ -293,14 +293,13 @@ static int cow_read_values(const char *path, const char *name,
     char *buffer;
     size_t buffer_size;
     int status;
-    char errbuf[1024];
 
     char file[4096];
     char *endptr;
 
     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;
@@ -308,8 +307,7 @@ static int cow_read_values(const char *path, const char *name,
     status = OW_get(file, &buffer, &buffer_size);
     if (status < 0) {
       ERROR("onewire plugin: OW_get (%s/%s) failed. error = %s;", path,
-            family_info->features[i].filename,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            family_info->features[i].filename, STRERRNO);
       return -1;
     }
     DEBUG("Read onewire device %s as %s", file, buffer);
@@ -365,7 +363,6 @@ static int cow_read_bus(const char *path) {
   char *buffer;
   size_t buffer_size;
   int status;
-  char errbuf[1024];
 
   char *buffer_ptr;
   char *dummy;
@@ -374,8 +371,7 @@ static int cow_read_bus(const char *path) {
 
   status = OW_get(path, &buffer, &buffer_size);
   if (status < 0) {
-    ERROR("onewire plugin: OW_get (%s) failed. error = %s;", path,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("onewire plugin: OW_get (%s) failed. error = %s;", path, STRERRNO);
     return -1;
   }
   DEBUG("onewire plugin: OW_get (%s) returned: %s", path, buffer);
@@ -426,7 +422,6 @@ static int cow_simple_read(void) {
   char *buffer;
   size_t buffer_size;
   int status;
-  char errbuf[1024];
   char *endptr;
   direct_access_element_t *traverse;
 
@@ -438,7 +433,7 @@ static int cow_simple_read(void) {
     status = OW_get(traverse->path, &buffer, &buffer_size);
     if (status < 0) {
       ERROR("onewire plugin: OW_get (%s) failed. status = %s;", traverse->path,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
       return -1;
     }
     DEBUG("onewire plugin: Read onewire device %s as %s", traverse->path,
@@ -507,7 +502,6 @@ static int cow_shutdown(void) {
 
 static int cow_init(void) {
   int status;
-  char errbuf[1024];
 
   if (device_g == NULL) {
     ERROR("onewire plugin: cow_init: No device configured.");
@@ -517,8 +511,7 @@ static int cow_init(void) {
   DEBUG("onewire plugin: about to init device <%s>.", device_g);
   status = (int)OW_init(device_g);
   if (status != 0) {
-    ERROR("onewire plugin: OW_init(%s) failed: %s.", device_g,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("onewire plugin: OW_init(%s) failed: %s.", device_g, STRERRNO);
     return 1;
   }
 
index afe2479..5659c69 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if defined(__APPLE__)
 #pragma clang diagnostic push
@@ -47,10 +47,10 @@ struct cldap_s /* {{{ */
   char *password;
   char *cacert;
   char *host;
-  _Bool starttls;
+  bool starttls;
   int timeout;
   char *url;
-  _Bool verifyhost;
+  bool verifyhost;
   int version;
 
   LDAP *ld;
@@ -105,12 +105,12 @@ static int cldap_init_host(cldap_t *st) /* {{{ */
   if (st->cacert != NULL)
     ldap_set_option(st->ld, LDAP_OPT_X_TLS_CACERTFILE, st->cacert);
 
-  if (st->verifyhost == 0) {
+  if (st->verifyhost == false) {
     int never = LDAP_OPT_X_TLS_NEVER;
     ldap_set_option(st->ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &never);
   }
 
-  if (st->starttls != 0) {
+  if (st->starttls) {
     rc = ldap_start_tls_s(st->ld, NULL, NULL);
     if (rc != LDAP_SUCCESS) {
       ERROR("openldap plugin: Failed to start tls on %s: %s", st->url,
@@ -397,9 +397,9 @@ static int cldap_config_add(oconfig_item_t *ci) /* {{{ */
     return status;
   }
 
-  st->starttls = 0;
+  st->starttls = false;
   st->timeout = (long)CDTIME_T_TO_TIME_T(plugin_get_interval());
-  st->verifyhost = 1;
+  st->verifyhost = true;
   st->version = LDAP_VERSION3;
 
   for (int i = 0; i < ci->children_num; i++) {
index a98649b..4d4a878 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 /**
  * There is two main kinds of OpenVPN status file:
@@ -80,10 +80,10 @@ struct vpn_status_s {
 };
 typedef struct vpn_status_s vpn_status_t;
 
-static _Bool new_naming_schema = 0;
-static _Bool collect_compression = 1;
-static _Bool collect_user_count = 0;
-static _Bool collect_individual_users = 1;
+static bool new_naming_schema;
+static bool collect_compression = true;
+static bool collect_user_count;
+static bool collect_individual_users = true;
 
 static const char *config_keys[] = {
     "StatusFile",           "Compression", /* old, deprecated name */
@@ -247,7 +247,7 @@ static int multi1_read(const char *name, FILE *fh) {
   char *fields[10];
   const int max_fields = STATIC_ARRAY_SIZE(fields);
   long long sum_users = 0;
-  _Bool found_header = 0;
+  bool found_header = false;
 
   /* read the file until the "ROUTING TABLE" line is found (no more info after)
    */
@@ -256,12 +256,12 @@ static int multi1_read(const char *name, FILE *fh) {
       break;
 
     if (strcmp(buffer, V1HEADER) == 0) {
-      found_header = 1;
+      found_header = true;
       continue;
     }
 
     /* skip the first lines until the client list section is found */
-    if (found_header == 0)
+    if (found_header == false)
       /* we can't start reading data until this string is found */
       continue;
 
@@ -292,7 +292,7 @@ static int multi1_read(const char *name, FILE *fh) {
   if (ferror(fh))
     return -1;
 
-  if (found_header == 0) {
+  if (found_header == false) {
     NOTICE("openvpn plugin: Unknown file format in instance %s, please "
            "report this as bug. Make sure to include "
            "your status file, so the plugin can "
@@ -320,7 +320,7 @@ static int multi2_read(const char *name, FILE *fh) {
   const int max_fields = STATIC_ARRAY_SIZE(fields);
   long long sum_users = 0;
 
-  _Bool found_header = 0;
+  bool found_header = false;
   int idx_cname = 0;
   int idx_bytes_recv = 0;
   int idx_bytes_sent = 0;
@@ -330,7 +330,7 @@ static int multi2_read(const char *name, FILE *fh) {
     int fields_num = openvpn_strsplit(buffer, fields, max_fields);
 
     /* Try to find section header */
-    if (found_header == 0) {
+    if (found_header == false) {
       if (fields_num < 2)
         continue;
       if (strcmp(fields[0], "HEADER") != 0)
@@ -358,7 +358,7 @@ static int multi2_read(const char *name, FILE *fh) {
       /* Data row has 1 field ("HEADER") less than header row */
       columns = fields_num - 1;
 
-      found_header = 1;
+      found_header = true;
       continue;
     }
 
@@ -404,7 +404,7 @@ static int multi2_read(const char *name, FILE *fh) {
   if (ferror(fh))
     return -1;
 
-  if (found_header == 0) {
+  if (found_header == false) {
     NOTICE("openvpn plugin: Unknown file format in instance %s, please "
            "report this as bug. Make sure to include "
            "your status file, so the plugin can "
@@ -429,9 +429,7 @@ static int openvpn_read(user_data_t *user_data) {
 
   FILE *fh = fopen(st->file, "r");
   if (fh == NULL) {
-    char errbuf[1024];
-    WARNING("openvpn plugin: fopen(%s) failed: %s", st->file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("openvpn plugin: fopen(%s) failed: %s", st->file, STRERRNO);
 
     return -1;
   }
@@ -471,9 +469,7 @@ static int openvpn_config(const char *key, const char *value) {
 
     char *status_file = strdup(value);
     if (status_file == NULL) {
-      char errbuf[1024];
-      ERROR("openvpn plugin: strdup failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("openvpn plugin: strdup failed: %s", STRERRNO);
       return 1;
     }
 
@@ -491,9 +487,7 @@ static int openvpn_config(const char *key, const char *value) {
     /* create a new vpn element */
     vpn_status_t *instance = calloc(1, sizeof(*instance));
     if (instance == NULL) {
-      char errbuf[1024];
-      ERROR("openvpn plugin: malloc failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("openvpn plugin: malloc failed: %s", STRERRNO);
       sfree(status_file);
       return 1;
     }
@@ -525,29 +519,29 @@ static int openvpn_config(const char *key, const char *value) {
            (strcasecmp("Compression", key) == 0)) /* old, deprecated name */
   {
     if (IS_FALSE(value))
-      collect_compression = 0;
+      collect_compression = false;
     else
-      collect_compression = 1;
+      collect_compression = true;
   } /* if (strcasecmp ("CollectCompression", key) == 0) */
   else if (strcasecmp("ImprovedNamingSchema", key) == 0) {
     if (IS_TRUE(value)) {
       DEBUG("openvpn plugin: using the new naming schema");
-      new_naming_schema = 1;
+      new_naming_schema = true;
     } else {
-      new_naming_schema = 0;
+      new_naming_schema = false;
     }
   } /* if (strcasecmp ("ImprovedNamingSchema", key) == 0) */
   else if (strcasecmp("CollectUserCount", key) == 0) {
     if (IS_TRUE(value))
-      collect_user_count = 1;
+      collect_user_count = true;
     else
-      collect_user_count = 0;
+      collect_user_count = false;
   } /* if (strcasecmp("CollectUserCount", key) == 0) */
   else if (strcasecmp("CollectIndividualUsers", key) == 0) {
     if (IS_FALSE(value))
-      collect_individual_users = 0;
+      collect_individual_users = false;
     else
-      collect_individual_users = 1;
+      collect_individual_users = true;
   } /* if (strcasecmp("CollectIndividualUsers", key) == 0) */
   else {
     return -1;
index 099013e..3f28110 100644 (file)
@@ -47,9 +47,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_db_query.h"
+#include "utils/common/common.h"
+#include "utils/db_query/db_query.h"
 
 #include <oci.h>
 
@@ -75,10 +75,10 @@ typedef struct o_database_s o_database_t;
 /*
  * Global variables
  */
-static udb_query_t **queries = NULL;
-static size_t queries_num = 0;
-static o_database_t **databases = NULL;
-static size_t databases_num = 0;
+static udb_query_t **queries;
+static size_t queries_num;
+static o_database_t **databases;
+static size_t databases_num;
 
 OCIEnv *oci_env = NULL;
 OCIError *oci_error = NULL;
@@ -107,7 +107,7 @@ static void o_report_error(const char *where, /* {{{ */
     status = OCIErrorGet(eh, (ub4)record_number,
                          /* sqlstate = */ NULL, &error_code, (text *)&buffer[0],
                          (ub4)sizeof(buffer), OCI_HTYPE_ERROR);
-    buffer[sizeof(buffer) - 1] = 0;
+    buffer[sizeof(buffer) - 1] = '\0';
 
     if (status == OCI_NO_DATA)
       return;
@@ -247,9 +247,7 @@ static int o_config_add_database(oconfig_item_t *ci) /* {{{ */
   } /* while (status == 0) */
 
   while ((status == 0) && (db->queries_num > 0)) {
-    db->q_prep_areas = (udb_query_preparation_area_t **)calloc(
-        db->queries_num, sizeof(*db->q_prep_areas));
-
+    db->q_prep_areas = calloc(db->queries_num, sizeof(*db->q_prep_areas));
     if (db->q_prep_areas == NULL) {
       WARNING("oracle plugin: calloc failed");
       status = -1;
@@ -308,10 +306,10 @@ static int o_config(oconfig_item_t *ci) /* {{{ */
     }
 
     if (queries_num > 0) {
-      DEBUG("oracle plugin: o_config: queries_num = %zu; queries[0] = %p; "
-            "udb_query_get_user_data (queries[0]) = %p;",
-            queries_num, (void *)queries[0],
-            udb_query_get_user_data(queries[0]));
+      DEBUG(
+          "oracle plugin: o_config: queries_num = %" PRIsz "; queries[0] = %p; "
+          "udb_query_get_user_data (queries[0]) = %p;",
+          queries_num, (void *)queries[0], udb_query_get_user_data(queries[0]));
     }
   } /* for (ci->children) */
 
@@ -532,8 +530,8 @@ static int o_read_database_query(o_database_t *db, /* {{{ */
     memcpy(column_names[i], column_name, column_name_length);
     column_names[i][column_name_length] = 0;
 
-    DEBUG("oracle plugin: o_read_database_query: column_names[%zu] = %s; "
-          "column_name_length = %" PRIu32 ";",
+    DEBUG("oracle plugin: o_read_database_query: column_names[%" PRIsz "] = %s;"
+          " column_name_length = %" PRIu32 ";",
           i, column_names[i], (uint32_t)column_name_length);
 
     status = OCIDefineByPos(oci_statement, &oci_defines[i], oci_error,
@@ -550,8 +548,7 @@ static int o_read_database_query(o_database_t *db, /* {{{ */
   status = udb_query_prepare_result(
       q, prep_area, (db->host != NULL) ? db->host : hostname_g,
       /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "oracle",
-      db->name, column_names, column_num,
-      /* interval = */ 0);
+      db->name, column_names, column_num);
   if (status != 0) {
     ERROR("oracle plugin: o_read_database_query (%s, %s): "
           "udb_query_prepare_result failed.",
@@ -583,6 +580,8 @@ static int o_read_database_query(o_database_t *db, /* {{{ */
     }
   } /* }}} while (42) */
 
+  udb_query_finish_result(q, prep_area);
+
   /* DEBUG ("oracle plugin: o_read_database_query: This statement succeeded:
    * %s", q->statement); */
   FREE_ALL;
index bf457fd..0f9a57c 100644 (file)
@@ -30,9 +30,9 @@
 
 #include "collectd.h"
 
-#include "common.h" /* auxiliary functions */
+#include "utils/common/common.h" /* auxiliary functions */
 
-#include "utils_ovs.h" /* OVS helpers */
+#include "utils/ovs/ovs.h" /* OVS helpers */
 
 #define OVS_EVENTS_IFACE_NAME_SIZE 128
 #define OVS_EVENTS_IFACE_UUID_SIZE 64
@@ -66,7 +66,7 @@ typedef struct ovs_events_iface_list_s ovs_events_iface_list_t;
 
 /* OVS events configuration data */
 struct ovs_events_config_s {
-  _Bool send_notification;                 /* sent notification to collectd? */
+  bool send_notification;                  /* sent notification to collectd? */
   char ovs_db_node[OVS_DB_ADDR_NODE_SIZE]; /* OVS DB node */
   char ovs_db_serv[OVS_DB_ADDR_SERVICE_SIZE]; /* OVS DB service */
   char ovs_db_unix[OVS_DB_ADDR_UNIX_SIZE];    /* OVS DB unix socket path */
@@ -80,7 +80,7 @@ struct ovs_events_ctx_s {
   ovs_db_t *ovs_db;           /* pointer to OVS DB instance */
   ovs_events_config_t config; /* plugin config */
   char *ovs_db_select_params; /* OVS DB select parameter request */
-  _Bool is_db_available;      /* specify whether OVS DB is available */
+  bool is_db_available;       /* specify whether OVS DB is available */
 };
 typedef struct ovs_events_ctx_s ovs_events_ctx_t;
 
@@ -89,7 +89,7 @@ typedef struct ovs_events_ctx_s ovs_events_ctx_t;
  */
 static ovs_events_ctx_t ovs_events_ctx = {
     .mutex = PTHREAD_MUTEX_INITIALIZER,
-    .config = {.send_notification = 1,     /* send notification by default */
+    .config = {.send_notification = true,  /* send notification by default */
                .ovs_db_node = "localhost", /* use default OVS DB node */
                .ovs_db_serv = "6640"}      /* use default OVS DB service */
 };
@@ -231,7 +231,7 @@ static int ovs_events_config_get_interfaces(const oconfig_item_t *ci) {
  * in allocated memory. Returns negative value in case of error.
  */
 static int ovs_events_plugin_config(oconfig_item_t *ci) {
-  _Bool dispatch_values = 0;
+  bool dispatch_values = false;
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
     if (strcasecmp("SendNotification", child->key) == 0) {
@@ -404,14 +404,14 @@ static int ovs_events_get_iface_info(yajl_val jobject,
   if (!YAJL_IS_OBJECT(jobject))
     return -1;
 
-  /* zero the interface info structure */
-  memset(ifinfo, 0, sizeof(*ifinfo));
-
   /* try to find external_ids, name and link_state fields */
   jexternal_ids = ovs_utils_get_value_by_key(jobject, "external_ids");
   if (jexternal_ids == NULL || ifinfo == NULL)
     return -1;
 
+  /* zero the interface info structure */
+  memset(ifinfo, 0, sizeof(*ifinfo));
+
   /* get iface-id from external_ids field */
   jvalue = ovs_utils_get_map_value(jexternal_ids, "iface-id");
   if (jvalue != NULL && YAJL_IS_STRING(jvalue))
@@ -577,7 +577,7 @@ static void ovs_events_conn_initialize(ovs_db_t *pdb) {
       return;
     }
   }
-  OVS_EVENTS_CTX_LOCK { ovs_events_ctx.is_db_available = 1; }
+  OVS_EVENTS_CTX_LOCK { ovs_events_ctx.is_db_available = true; }
   DEBUG(OVS_EVENTS_PLUGIN ": OVS DB connection has been initialized");
 }
 
@@ -587,12 +587,12 @@ static void ovs_events_conn_terminate() {
   if (ovs_events_ctx.config.send_notification)
     ovs_events_dispatch_terminate_notification(msg);
   WARNING(OVS_EVENTS_PLUGIN ": %s", msg);
-  OVS_EVENTS_CTX_LOCK { ovs_events_ctx.is_db_available = 0; }
+  OVS_EVENTS_CTX_LOCK { ovs_events_ctx.is_db_available = false; }
 }
 
 /* Read OVS DB interface link status callback */
 static int ovs_events_plugin_read(__attribute__((unused)) user_data_t *u) {
-  _Bool is_connected = 0;
+  bool is_connected = false;
   OVS_EVENTS_CTX_LOCK { is_connected = ovs_events_ctx.is_db_available; }
   if (is_connected)
     if (ovs_db_send_request(ovs_events_ctx.ovs_db, "transact",
index e027845..fe1953f 100644 (file)
@@ -28,9 +28,9 @@
  *   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";
@@ -63,6 +63,7 @@ typedef enum iface_counter {
   tx_512_to_1023_packets,
   tx_1024_to_1522_packets,
   tx_1523_to_max_packets,
+  rx_multicast_packets,
   tx_multicast_packets,
   rx_broadcast_packets,
   tx_broadcast_packets,
@@ -70,6 +71,21 @@ typedef enum iface_counter {
   rx_oversize_errors,
   rx_fragmented_errors,
   rx_jabber_errors,
+  rx_error_bytes,
+  rx_l3_l4_xsum_error,
+  rx_management_dropped,
+  rx_mbuf_allocation_errors,
+  rx_total_bytes,
+  rx_total_missed_packets,
+  rx_undersize_errors,
+  rx_management_packets,
+  tx_management_packets,
+  rx_good_bytes,
+  tx_good_bytes,
+  rx_good_packets,
+  tx_good_packets,
+  rx_total_packets,
+  tx_total_packets,
   __iface_counter_max
 } iface_counter;
 
@@ -78,15 +94,21 @@ typedef enum iface_counter {
 #define PORT_NAME_SIZE_MAX 255
 #define UUID_SIZE 64
 
-typedef struct port_s {
-  char name[PORT_NAME_SIZE_MAX];      /* Port name */
-  char port_uuid[UUID_SIZE];          /* Port table _uuid */
+typedef struct interface_s {
+  char name[PORT_NAME_SIZE_MAX];      /* Interface name */
   char iface_uuid[UUID_SIZE];         /* Interface table uuid */
   char ex_iface_id[UUID_SIZE];        /* External iface id */
   char ex_vm_id[UUID_SIZE];           /* External vm id */
-  int64_t stats[IFACE_COUNTER_COUNT]; /* Port statistics */
-  struct bridge_list_s *br;           /* Pointer to bridge */
-  struct port_s *next;                /* Next port */
+  int64_t stats[IFACE_COUNTER_COUNT]; /* Statistics for interface */
+  struct interface_s *next;           /* Next interface for associated port */
+} interface_list_t;
+
+typedef struct port_s {
+  char name[PORT_NAME_SIZE_MAX]; /* Port name */
+  char port_uuid[UUID_SIZE];     /* Port table _uuid */
+  struct bridge_list_s *br;      /* Pointer to bridge */
+  struct interface_s *iface;     /* Pointer to first interface */
+  struct port_s *next;           /* Next port */
 } port_list_t;
 
 typedef struct bridge_list_s {
@@ -94,42 +116,62 @@ typedef struct bridge_list_s {
   struct bridge_list_s *next; /* Next bridge*/
 } bridge_list_t;
 
+#define cnt_str(x) [x] = #x
+
 static const char *const iface_counter_table[IFACE_COUNTER_COUNT] = {
-        [collisions] = "collisions",
-        [rx_bytes] = "rx_bytes",
-        [rx_crc_err] = "rx_crc_err",
-        [rx_dropped] = "rx_dropped",
-        [rx_errors] = "rx_errors",
-        [rx_frame_err] = "rx_frame_err",
-        [rx_over_err] = "rx_over_err",
-        [rx_packets] = "rx_packets",
-        [tx_bytes] = "tx_bytes",
-        [tx_dropped] = "tx_dropped",
-        [tx_errors] = "tx_errors",
-        [tx_packets] = "tx_packets",
-        [rx_1_to_64_packets] = "rx_1_to_64_packets",
-        [rx_65_to_127_packets] = "rx_65_to_127_packets",
-        [rx_128_to_255_packets] = "rx_128_to_255_packets",
-        [rx_256_to_511_packets] = "rx_256_to_511_packets",
-        [rx_512_to_1023_packets] = "rx_512_to_1023_packets",
-        [rx_1024_to_1522_packets] = "rx_1024_to_1518_packets",
-        [rx_1523_to_max_packets] = "rx_1523_to_max_packets",
-        [tx_1_to_64_packets] = "tx_1_to_64_packets",
-        [tx_65_to_127_packets] = "tx_65_to_127_packets",
-        [tx_128_to_255_packets] = "tx_128_to_255_packets",
-        [tx_256_to_511_packets] = "tx_256_to_511_packets",
-        [tx_512_to_1023_packets] = "tx_512_to_1023_packets",
-        [tx_1024_to_1522_packets] = "tx_1024_to_1518_packets",
-        [tx_1523_to_max_packets] = "tx_1523_to_max_packets",
-        [tx_multicast_packets] = "tx_multicast_packets",
-        [rx_broadcast_packets] = "rx_broadcast_packets",
-        [tx_broadcast_packets] = "tx_broadcast_packets",
-        [rx_undersized_errors] = "rx_undersized_errors",
-        [rx_oversize_errors] = "rx_oversize_errors",
-        [rx_fragmented_errors] = "rx_fragmented_errors",
-        [rx_jabber_errors] = "rx_jabber_errors",
+    cnt_str(collisions),
+    cnt_str(rx_bytes),
+    cnt_str(rx_crc_err),
+    cnt_str(rx_dropped),
+    cnt_str(rx_errors),
+    cnt_str(rx_frame_err),
+    cnt_str(rx_over_err),
+    cnt_str(rx_packets),
+    cnt_str(tx_bytes),
+    cnt_str(tx_dropped),
+    cnt_str(tx_errors),
+    cnt_str(tx_packets),
+    cnt_str(rx_1_to_64_packets),
+    cnt_str(rx_65_to_127_packets),
+    cnt_str(rx_128_to_255_packets),
+    cnt_str(rx_256_to_511_packets),
+    cnt_str(rx_512_to_1023_packets),
+    cnt_str(rx_1024_to_1522_packets),
+    cnt_str(rx_1523_to_max_packets),
+    cnt_str(tx_1_to_64_packets),
+    cnt_str(tx_65_to_127_packets),
+    cnt_str(tx_128_to_255_packets),
+    cnt_str(tx_256_to_511_packets),
+    cnt_str(tx_512_to_1023_packets),
+    cnt_str(tx_1024_to_1522_packets),
+    cnt_str(tx_1523_to_max_packets),
+    cnt_str(rx_multicast_packets),
+    cnt_str(tx_multicast_packets),
+    cnt_str(rx_broadcast_packets),
+    cnt_str(tx_broadcast_packets),
+    cnt_str(rx_undersized_errors),
+    cnt_str(rx_oversize_errors),
+    cnt_str(rx_fragmented_errors),
+    cnt_str(rx_jabber_errors),
+    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
+
 /* Entry into the list of network bridges */
 static bridge_list_t *g_bridge_list_head;
 
@@ -158,6 +200,9 @@ static ovs_stats_config_t ovs_stats_cfg = {
     .ovs_db_serv = "6640",      /* use default OVS DB service */
 };
 
+/* flag indicating whether or not to publish individual interface statistics */
+static bool interface_stats = false;
+
 static iface_counter ovs_stats_counter_name_to_type(const char *counter) {
   iface_counter index = not_supported;
 
@@ -219,6 +264,264 @@ static void ovs_stats_submit_two(const char *dev, const char *type,
   plugin_dispatch_values(&vl);
 }
 
+static void ovs_stats_submit_interfaces(port_list_t *port) {
+  char devname[PORT_NAME_SIZE_MAX * 2];
+
+  bridge_list_t *bridge = port->br;
+  for (interface_list_t *iface = port->iface; iface != NULL;
+       iface = iface->next) {
+    meta_data_t *meta = meta_data_create();
+    if (meta != NULL) {
+      meta_data_add_string(meta, "uuid", iface->iface_uuid);
+
+      if (strlen(iface->ex_vm_id))
+        meta_data_add_string(meta, "vm-uuid", iface->ex_vm_id);
+
+      if (strlen(iface->ex_iface_id))
+        meta_data_add_string(meta, "iface-id", iface->ex_iface_id);
+    }
+    strjoin(devname, sizeof(devname),
+            (char *[]){
+                bridge->name, port->name, iface->name,
+            },
+            3, ".");
+    ovs_stats_submit_one(devname, "if_collisions", NULL,
+                         iface->stats[collisions], meta);
+    ovs_stats_submit_two(devname, "if_dropped", NULL, iface->stats[rx_dropped],
+                         iface->stats[tx_dropped], meta);
+    ovs_stats_submit_two(devname, "if_errors", NULL, iface->stats[rx_errors],
+                         iface->stats[tx_errors], meta);
+    ovs_stats_submit_two(devname, "if_packets", NULL, iface->stats[rx_packets],
+                         iface->stats[tx_packets], meta);
+    ovs_stats_submit_one(devname, "if_rx_errors", "crc",
+                         iface->stats[rx_crc_err], meta);
+    ovs_stats_submit_one(devname, "if_rx_errors", "frame",
+                         iface->stats[rx_frame_err], meta);
+    ovs_stats_submit_one(devname, "if_rx_errors", "over",
+                         iface->stats[rx_over_err], meta);
+    ovs_stats_submit_one(devname, "if_rx_octets", NULL, iface->stats[rx_bytes],
+                         meta);
+    ovs_stats_submit_one(devname, "if_tx_octets", NULL, iface->stats[tx_bytes],
+                         meta);
+    ovs_stats_submit_two(devname, "if_packets", "1_to_64_packets",
+                         iface->stats[rx_1_to_64_packets],
+                         iface->stats[tx_1_to_64_packets], meta);
+    ovs_stats_submit_two(devname, "if_packets", "65_to_127_packets",
+                         iface->stats[rx_65_to_127_packets],
+                         iface->stats[tx_65_to_127_packets], meta);
+    ovs_stats_submit_two(devname, "if_packets", "128_to_255_packets",
+                         iface->stats[rx_128_to_255_packets],
+                         iface->stats[tx_128_to_255_packets], meta);
+    ovs_stats_submit_two(devname, "if_packets", "256_to_511_packets",
+                         iface->stats[rx_256_to_511_packets],
+                         iface->stats[tx_256_to_511_packets], meta);
+    ovs_stats_submit_two(devname, "if_packets", "512_to_1023_packets",
+                         iface->stats[rx_512_to_1023_packets],
+                         iface->stats[tx_512_to_1023_packets], meta);
+    ovs_stats_submit_two(devname, "if_packets", "1024_to_1522_packets",
+                         iface->stats[rx_1024_to_1522_packets],
+                         iface->stats[tx_1024_to_1522_packets], meta);
+    ovs_stats_submit_two(devname, "if_packets", "1523_to_max_packets",
+                         iface->stats[rx_1523_to_max_packets],
+                         iface->stats[tx_1523_to_max_packets], meta);
+    ovs_stats_submit_two(devname, "if_packets", "broadcast_packets",
+                         iface->stats[rx_broadcast_packets],
+                         iface->stats[tx_broadcast_packets], meta);
+    ovs_stats_submit_one(devname, "if_rx_errors", "rx_undersized_errors",
+                         iface->stats[rx_undersized_errors], meta);
+    ovs_stats_submit_one(devname, "if_rx_errors", "rx_oversize_errors",
+                         iface->stats[rx_oversize_errors], meta);
+    ovs_stats_submit_one(devname, "if_rx_errors", "rx_fragmented_errors",
+                         iface->stats[rx_fragmented_errors], meta);
+    ovs_stats_submit_one(devname, "if_rx_errors", "rx_jabber_errors",
+                         iface->stats[rx_jabber_errors], meta);
+    ovs_stats_submit_one(devname, "if_rx_octets", "rx_error_bytes",
+                         iface->stats[rx_error_bytes], meta);
+    ovs_stats_submit_one(devname, "if_errors", "rx_l3_l4_xsum_error",
+                         iface->stats[rx_l3_l4_xsum_error], meta);
+    ovs_stats_submit_one(devname, "if_dropped", "rx_management_dropped",
+                         iface->stats[rx_management_dropped], meta);
+    ovs_stats_submit_one(devname, "if_errors", "rx_mbuf_allocation_errors",
+                         iface->stats[rx_mbuf_allocation_errors], meta);
+    ovs_stats_submit_one(devname, "if_octets", "rx_total_bytes",
+                         iface->stats[rx_total_bytes], meta);
+    ovs_stats_submit_one(devname, "if_packets", "rx_total_missed_packets",
+                         iface->stats[rx_total_missed_packets], meta);
+    ovs_stats_submit_one(devname, "if_rx_errors", "rx_undersize_errors",
+                         iface->stats[rx_undersize_errors], meta);
+    ovs_stats_submit_two(devname, "if_packets", "management_packets",
+                         iface->stats[rx_management_packets],
+                         iface->stats[tx_management_packets], meta);
+    ovs_stats_submit_two(devname, "if_packets", "multicast_packets",
+                         iface->stats[rx_multicast_packets],
+                         iface->stats[tx_multicast_packets], meta);
+    ovs_stats_submit_two(devname, "if_octets", "good_bytes",
+                         iface->stats[rx_good_bytes],
+                         iface->stats[tx_good_bytes], meta);
+    ovs_stats_submit_two(devname, "if_packets", "good_packets",
+                         iface->stats[rx_good_packets],
+                         iface->stats[tx_good_packets], meta);
+    ovs_stats_submit_two(devname, "if_packets", "total_packets",
+                         iface->stats[rx_total_packets],
+                         iface->stats[tx_total_packets], meta);
+
+    meta_data_destroy(meta);
+  }
+}
+
+static int ovs_stats_get_port_stat_value(port_list_t *port,
+                                         iface_counter index) {
+  if (port == NULL)
+    return 0;
+
+  int value = 0;
+
+  for (interface_list_t *iface = port->iface; iface != NULL;
+       iface = iface->next) {
+    value = value + iface->stats[index];
+  }
+
+  return value;
+}
+
+static void ovs_stats_submit_port(port_list_t *port) {
+  char devname[PORT_NAME_SIZE_MAX * 2];
+
+  meta_data_t *meta = meta_data_create();
+  if (meta != NULL) {
+    char key_str[DATA_MAX_NAME_LEN];
+    int i = 0;
+
+    for (interface_list_t *iface = port->iface; iface != NULL;
+         iface = iface->next) {
+      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;
@@ -230,39 +533,89 @@ static port_list_t *ovs_stats_get_port(const char *uuid) {
   return NULL;
 }
 
-static port_list_t *ovs_stats_get_port_by_name(const char *name) {
-  if (name == NULL)
+static port_list_t *ovs_stats_get_port_by_interface_uuid(const char *uuid) {
+  if (uuid == NULL)
     return NULL;
 
-  for (port_list_t *port = g_port_list_head; port != NULL; port = port->next)
-    if ((strncmp(port->name, name, strlen(port->name)) == 0) &&
-        strlen(name) == strlen(port->name))
-      return port;
+  for (port_list_t *port = g_port_list_head; port != NULL; port = port->next) {
+    for (interface_list_t *iface = port->iface; iface != NULL;
+         iface = iface->next) {
+      if (strncmp(iface->iface_uuid, uuid, strlen(uuid)) == 0)
+        return port;
+    }
+  }
+  return NULL;
+}
+
+static interface_list_t *ovs_stats_get_port_interface(port_list_t *port,
+                                                      const char *uuid) {
+  if (port == NULL || uuid == NULL)
+    return NULL;
+
+  for (interface_list_t *iface = port->iface; iface != NULL;
+       iface = iface->next) {
+    if (strncmp(iface->iface_uuid, uuid, strlen(uuid)) == 0)
+      return iface;
+  }
+  return NULL;
+}
+
+static interface_list_t *ovs_stats_get_interface(const char *uuid) {
+  if (uuid == NULL)
+    return NULL;
+
+  for (port_list_t *port = g_port_list_head; port != NULL; port = port->next) {
+    for (interface_list_t *iface = port->iface; iface != NULL;
+         iface = iface->next) {
+      if (strncmp(iface->iface_uuid, uuid, strlen(uuid)) == 0)
+        return iface;
+    }
+  }
   return NULL;
 }
 
+static interface_list_t *ovs_stats_new_port_interface(port_list_t *port,
+                                                      const char *uuid) {
+  if (uuid == NULL)
+    return NULL;
+
+  interface_list_t *iface = ovs_stats_get_port_interface(port, uuid);
+
+  if (iface == NULL) {
+    iface = calloc(1, sizeof(*iface));
+    if (iface == NULL) {
+      ERROR("%s: Error allocating interface", plugin_name);
+      return NULL;
+    }
+    memset(iface->stats, -1, sizeof(int64_t[IFACE_COUNTER_COUNT]));
+    sstrncpy(iface->iface_uuid, uuid, sizeof(iface->iface_uuid));
+    interface_list_t *iface_head = port->iface;
+    iface->next = iface_head;
+    port->iface = iface;
+  }
+  return iface;
+}
+
 /* Create or get port by port uuid */
 static port_list_t *ovs_stats_new_port(bridge_list_t *bridge,
                                        const char *uuid) {
+  if (uuid == NULL)
+    return NULL;
+
   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;
 }
@@ -281,36 +634,51 @@ static bridge_list_t *ovs_stats_get_bridge(bridge_list_t *head,
   return NULL;
 }
 
+/* Check if bridge is configured to be monitored in config file */
+static int ovs_stats_is_monitored_bridge(const char *br_name) {
+  /* if no bridges are configured, return true */
+  if (g_monitored_bridge_list_head == NULL)
+    return 1;
+
+  /* check if given bridge exists */
+  if (ovs_stats_get_bridge(g_monitored_bridge_list_head, br_name) != NULL)
+    return 1;
+
+  return 0;
+}
+
 /* Delete bridge */
 static int ovs_stats_del_bridge(yajl_val bridge) {
   const char *old[] = {"old", NULL};
   const char *name[] = {"name", NULL};
 
-  yajl_val row;
-
-  if (bridge && YAJL_IS_OBJECT(bridge)) {
-    row = yajl_tree_get(bridge, old, yajl_t_object);
-    if (row && YAJL_IS_OBJECT(row)) {
-      yajl_val br_name = yajl_tree_get(row, name, yajl_t_string);
-      if (br_name && YAJL_IS_STRING(br_name)) {
-        bridge_list_t *prev_br = g_bridge_list_head;
-        for (bridge_list_t *br = g_bridge_list_head; br != NULL;
-             prev_br = br, br = br->next) {
-          if ((strncmp(br->name, br_name->u.string, strlen(br->name)) == 0) &&
-              strlen(br->name) == strlen(br_name->u.string)) {
-            if (br == g_bridge_list_head)
-              g_bridge_list_head = br->next;
-            else
-              prev_br->next = br->next;
-            sfree(br->name);
-            sfree(br);
-            break;
-          }
-        }
-      }
-    }
-  } else
+  if (!bridge || !YAJL_IS_OBJECT(bridge)) {
     WARNING("%s: Incorrect data for deleting bridge", plugin_name);
+    return 0;
+  }
+
+  yajl_val row = yajl_tree_get(bridge, old, yajl_t_object);
+  if (!row || !YAJL_IS_OBJECT(row))
+    return 0;
+
+  yajl_val br_name = yajl_tree_get(row, name, yajl_t_string);
+  if (!br_name || !YAJL_IS_STRING(br_name))
+    return 0;
+
+  bridge_list_t *prev_br = g_bridge_list_head;
+  for (bridge_list_t *br = g_bridge_list_head; br != NULL;
+       prev_br = br, br = br->next) {
+    if ((strncmp(br->name, br_name->u.string, strlen(br->name)) == 0) &&
+        strlen(br->name) == strlen(br_name->u.string)) {
+      if (br == g_bridge_list_head)
+        g_bridge_list_head = br->next;
+      else
+        prev_br->next = br->next;
+      sfree(br->name);
+      sfree(br);
+      break;
+    }
+  }
   return 0;
 }
 
@@ -319,59 +687,75 @@ static int ovs_stats_update_bridge(yajl_val bridge) {
   const char *new[] = {"new", NULL};
   const char *name[] = {"name", NULL};
   const char *ports[] = {"ports", NULL};
-  bridge_list_t *br = NULL;
-
-  if (bridge && YAJL_IS_OBJECT(bridge)) {
-    yajl_val row = yajl_tree_get(bridge, new, yajl_t_object);
-    if (row && YAJL_IS_OBJECT(row)) {
-      yajl_val br_name = yajl_tree_get(row, name, yajl_t_string);
-      yajl_val br_ports = yajl_tree_get(row, ports, yajl_t_array);
-      if (br_name && YAJL_IS_STRING(br_name)) {
-        br = ovs_stats_get_bridge(g_bridge_list_head, YAJL_GET_STRING(br_name));
-        pthread_mutex_lock(&g_stats_lock);
-        if (br == NULL) {
-          br = calloc(1, sizeof(*br));
-          if (!br) {
-            pthread_mutex_unlock(&g_stats_lock);
-            ERROR("%s: calloc(%zu) failed.", plugin_name, sizeof(*br));
-            return -1;
-          }
-          char *tmp = YAJL_GET_STRING(br_name);
 
-          if (tmp != NULL)
-            br->name = strdup(tmp);
-          if (br->name == NULL) {
-            sfree(br);
-            pthread_mutex_unlock(&g_stats_lock);
-            ERROR("%s: strdup failed.", plugin_name);
-            return -1;
-          }
-          br->next = g_bridge_list_head;
-          g_bridge_list_head = br;
-        }
-        pthread_mutex_unlock(&g_stats_lock);
-      }
-      if (br_ports && YAJL_IS_ARRAY(br_ports)) {
-        char *tmp = YAJL_GET_STRING(br_ports->u.array.values[0]);
-        if (tmp != NULL && strcmp("set", tmp) == 0) {
-          yajl_val *array = YAJL_GET_ARRAY(br_ports)->values;
-          size_t array_len = YAJL_GET_ARRAY(br_ports)->len;
-          if (array != NULL && array_len > 0 && YAJL_IS_ARRAY(array[1])) {
-            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++)
-              ovs_stats_new_port(
-                  br, YAJL_GET_STRING(ports_arr[i]->u.array.values[1]));
-          }
-        } 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 {
-    ERROR("Incorrect JSON Bridge data");
-    return -1;
+    ovs_stats_new_port(br, YAJL_GET_STRING(br_ports->u.array.values[1]));
   }
+
   return 0;
+
+failure:
+  ERROR("Incorrect JSON Bridge data");
+  return -1;
 }
 
 /* Handle JSON with Bridge Table change event */
@@ -401,31 +785,32 @@ static void ovs_stats_bridge_table_change_cb(yajl_val jupdates) {
     }
    */
   const char *path[] = {"Bridge", NULL};
-
   yajl_val bridges = yajl_tree_get(jupdates, path, yajl_t_object);
 
-  if (bridges && YAJL_IS_OBJECT(bridges)) {
-    for (size_t i = 0; i < YAJL_GET_OBJECT(bridges)->len; i++) {
-      yajl_val bridge = YAJL_GET_OBJECT(bridges)->values[i];
-      ovs_stats_update_bridge(bridge);
-    }
+  if (!bridges || !YAJL_IS_OBJECT(bridges))
+    return;
+
+  pthread_mutex_lock(&g_stats_lock);
+  for (size_t i = 0; i < YAJL_GET_OBJECT(bridges)->len; i++) {
+    yajl_val bridge = YAJL_GET_OBJECT(bridges)->values[i];
+    ovs_stats_update_bridge(bridge);
   }
+  pthread_mutex_unlock(&g_stats_lock);
 }
 
 /* Handle Bridge Table delete event */
 static void ovs_stats_bridge_table_delete_cb(yajl_val jupdates) {
   const char *path[] = {"Bridge", NULL};
   yajl_val bridges = yajl_tree_get(jupdates, path, yajl_t_object);
-  yajl_val bridge;
-  if (bridges && YAJL_IS_OBJECT(bridges)) {
-    pthread_mutex_lock(&g_stats_lock);
-    for (size_t i = 0; i < YAJL_GET_OBJECT(bridges)->len; i++) {
-      bridge = YAJL_GET_OBJECT(bridges)->values[i];
-      ovs_stats_del_bridge(bridge);
-    }
-    pthread_mutex_unlock(&g_stats_lock);
+  if (!bridges || !YAJL_IS_OBJECT(bridges))
+    return;
+
+  pthread_mutex_lock(&g_stats_lock);
+  for (size_t i = 0; i < YAJL_GET_OBJECT(bridges)->len; i++) {
+    yajl_val bridge = YAJL_GET_OBJECT(bridges)->values[i];
+    ovs_stats_del_bridge(bridge);
   }
-  return;
+  pthread_mutex_unlock(&g_stats_lock);
 }
 
 /* Handle JSON with Bridge table initial values */
@@ -438,32 +823,62 @@ static void ovs_stats_bridge_table_result_cb(yajl_val jresult,
   return;
 }
 
-/* Update port name */
+/* Update port name and interface UUID(s)*/
 static int ovs_stats_update_port(const char *uuid, yajl_val port) {
   const char *new[] = {"new", NULL};
   const char *name[] = {"name", NULL};
-  yajl_val row;
-  port_list_t *portentry = NULL;
-  if (port && YAJL_IS_OBJECT(port)) {
-    row = yajl_tree_get(port, new, yajl_t_object);
-    if (row && YAJL_IS_OBJECT(row)) {
-      yajl_val port_name = yajl_tree_get(row, name, yajl_t_string);
-      if (port_name && YAJL_IS_STRING(port_name)) {
-        portentry = ovs_stats_get_port(uuid);
-        if (portentry == NULL)
-          portentry = ovs_stats_new_port(NULL, uuid);
-        if (portentry) {
-          pthread_mutex_lock(&g_stats_lock);
-          sstrncpy(portentry->name, YAJL_GET_STRING(port_name),
-                   sizeof(portentry->name));
-          pthread_mutex_unlock(&g_stats_lock);
-        }
-      }
-    }
-  } else {
+
+  if (!port || !YAJL_IS_OBJECT(port)) {
     ERROR("Incorrect JSON Port data");
     return -1;
   }
+
+  yajl_val row = yajl_tree_get(port, new, yajl_t_object);
+  if (!row || !YAJL_IS_OBJECT(row))
+    return 0;
+
+  yajl_val port_name = yajl_tree_get(row, name, yajl_t_string);
+  if (!port_name || !YAJL_IS_STRING(port_name))
+    return 0;
+
+  /* Create or get port by port uuid */
+  port_list_t *portentry = ovs_stats_new_port(NULL, uuid);
+  if (!portentry)
+    return 0;
+
+  sstrncpy(portentry->name, YAJL_GET_STRING(port_name),
+           sizeof(portentry->name));
+
+  yajl_val ifaces_root = ovs_utils_get_value_by_key(row, "interfaces");
+  char *ifaces_root_key =
+      YAJL_GET_STRING(YAJL_GET_ARRAY(ifaces_root)->values[0]);
+
+  if (strcmp("set", ifaces_root_key) == 0) {
+    // ifaces_root is ["set", [[ "uuid", "<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 (int 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;
 }
 
@@ -477,6 +892,14 @@ static int ovs_stats_del_port(const char *uuid) {
         g_port_list_head = port->next;
       else
         prev_port->next = port->next;
+
+      for (interface_list_t *iface = port->iface; iface != NULL;
+           iface = port->iface) {
+        interface_list_t *del = iface;
+        port->iface = iface->next;
+        sfree(del);
+      }
+
       sfree(port);
       break;
     }
@@ -503,13 +926,15 @@ static void ovs_stats_port_table_change_cb(yajl_val jupdates) {
    */
   const char *path[] = {"Port", NULL};
   yajl_val ports = yajl_tree_get(jupdates, path, yajl_t_object);
-  yajl_val port;
-  if (ports && YAJL_IS_OBJECT(ports)) {
-    for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) {
-      port = YAJL_GET_OBJECT(ports)->values[i];
-      ovs_stats_update_port(YAJL_GET_OBJECT(ports)->keys[i], port);
-    }
+  if (!ports || !YAJL_IS_OBJECT(ports))
+    return;
+
+  pthread_mutex_lock(&g_stats_lock);
+  for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) {
+    yajl_val port = YAJL_GET_OBJECT(ports)->values[i];
+    ovs_stats_update_port(YAJL_GET_OBJECT(ports)->keys[i], port);
   }
+  pthread_mutex_unlock(&g_stats_lock);
   return;
 }
 
@@ -526,69 +951,75 @@ static void ovs_stats_port_table_result_cb(yajl_val jresult, yajl_val jerror) {
 static void ovs_stats_port_table_delete_cb(yajl_val jupdates) {
   const char *path[] = {"Port", NULL};
   yajl_val ports = yajl_tree_get(jupdates, path, yajl_t_object);
+  if (!ports || !YAJL_IS_OBJECT(ports))
+    return;
+
   pthread_mutex_lock(&g_stats_lock);
-  if (ports && YAJL_IS_OBJECT(ports))
-    for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) {
-      ovs_stats_del_port(YAJL_GET_OBJECT(ports)->keys[i]);
-    }
+  for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++) {
+    ovs_stats_del_port(YAJL_GET_OBJECT(ports)->keys[i]);
+  }
   pthread_mutex_unlock(&g_stats_lock);
   return;
 }
 
 /* Update interface statistics */
-static int ovs_stats_update_iface_stats(port_list_t *port, yajl_val stats) {
-  yajl_val stat;
-  iface_counter counter_index = 0;
-  char *counter_name = NULL;
-  int64_t counter_value = 0;
-  if (stats && YAJL_IS_ARRAY(stats))
-    for (size_t i = 0; i < YAJL_GET_ARRAY(stats)->len; i++) {
-      stat = YAJL_GET_ARRAY(stats)->values[i];
-      if (!YAJL_IS_ARRAY(stat))
-        return -1;
-      counter_name = YAJL_GET_STRING(YAJL_GET_ARRAY(stat)->values[0]);
-      counter_index = ovs_stats_counter_name_to_type(counter_name);
-      counter_value = YAJL_GET_INTEGER(YAJL_GET_ARRAY(stat)->values[1]);
-      if (counter_index == not_supported)
-        continue;
-      port->stats[counter_index] = counter_value;
-    }
+static int ovs_stats_update_iface_stats(interface_list_t *iface,
+                                        yajl_val stats) {
+
+  if (!stats || !YAJL_IS_ARRAY(stats))
+    return 0;
+
+  for (size_t i = 0; i < YAJL_GET_ARRAY(stats)->len; i++) {
+    yajl_val stat = YAJL_GET_ARRAY(stats)->values[i];
+    if (!YAJL_IS_ARRAY(stat))
+      return -1;
+
+    char *counter_name = YAJL_GET_STRING(YAJL_GET_ARRAY(stat)->values[0]);
+    iface_counter counter_index = ovs_stats_counter_name_to_type(counter_name);
+    int64_t counter_value = YAJL_GET_INTEGER(YAJL_GET_ARRAY(stat)->values[1]);
+    if (counter_index == not_supported)
+      continue;
+
+    iface->stats[counter_index] = counter_value;
+  }
 
   return 0;
 }
 
 /* Update interface external_ids */
-static int ovs_stats_update_iface_ext_ids(port_list_t *port, yajl_val ext_ids) {
-  yajl_val ext_id;
-  char *key;
-  char *value;
-
-  if (ext_ids && YAJL_IS_ARRAY(ext_ids))
-    for (size_t i = 0; i < YAJL_GET_ARRAY(ext_ids)->len; i++) {
-      ext_id = YAJL_GET_ARRAY(ext_ids)->values[i];
-      if (!YAJL_IS_ARRAY(ext_id))
-        return -1;
-      key = YAJL_GET_STRING(YAJL_GET_ARRAY(ext_id)->values[0]);
-      value = YAJL_GET_STRING(YAJL_GET_ARRAY(ext_id)->values[1]);
-      if (key && value) {
-        if (strncmp(key, "iface-id", strlen(key)) == 0)
-          sstrncpy(port->ex_iface_id, value, sizeof(port->ex_iface_id));
-        else if (strncmp(key, "vm-uuid", strlen(key)) == 0)
-          sstrncpy(port->ex_vm_id, value, sizeof(port->ex_vm_id));
+static int ovs_stats_update_iface_ext_ids(interface_list_t *iface,
+                                          yajl_val ext_ids) {
+
+  if (!ext_ids || !YAJL_IS_ARRAY(ext_ids))
+    return 0;
+
+  for (size_t i = 0; i < YAJL_GET_ARRAY(ext_ids)->len; i++) {
+    yajl_val ext_id = YAJL_GET_ARRAY(ext_ids)->values[i];
+    if (!YAJL_IS_ARRAY(ext_id))
+      return -1;
+
+    char *key = YAJL_GET_STRING(YAJL_GET_ARRAY(ext_id)->values[0]);
+    char *value = YAJL_GET_STRING(YAJL_GET_ARRAY(ext_id)->values[1]);
+    if (key && value) {
+      if (strncmp(key, "iface-id", strlen(key)) == 0) {
+        sstrncpy(iface->ex_iface_id, value, sizeof(iface->ex_iface_id));
+      } else if (strncmp(key, "vm-uuid", strlen(key)) == 0) {
+        sstrncpy(iface->ex_vm_id, value, sizeof(iface->ex_vm_id));
       }
     }
+  }
 
   return 0;
 }
 
 /* Get interface statistic and external_ids */
-static int ovs_stats_update_iface(yajl_val iface) {
-  if (!iface || !YAJL_IS_OBJECT(iface)) {
-    ERROR("ovs_stats plugin: incorrect JSON port data");
+static int ovs_stats_update_iface(yajl_val iface_obj) {
+  if (!iface_obj || !YAJL_IS_OBJECT(iface_obj)) {
+    ERROR("ovs_stats plugin: incorrect JSON interface data");
     return -1;
   }
 
-  yajl_val row = ovs_utils_get_value_by_key(iface, "new");
+  yajl_val row = ovs_utils_get_value_by_key(iface_obj, "new");
   if (!row || !YAJL_IS_OBJECT(row))
     return 0;
 
@@ -596,13 +1027,26 @@ static int ovs_stats_update_iface(yajl_val iface) {
   if (!iface_name || !YAJL_IS_STRING(iface_name))
     return 0;
 
-  port_list_t *port = ovs_stats_get_port_by_name(YAJL_GET_STRING(iface_name));
-  if (port == NULL)
+  yajl_val iface_uuid = ovs_utils_get_value_by_key(row, "_uuid");
+  if (!iface_uuid || !YAJL_IS_ARRAY(iface_uuid) ||
+      YAJL_GET_ARRAY(iface_uuid)->len != 2)
+    return 0;
+
+  char *iface_uuid_str = YAJL_GET_STRING(YAJL_GET_ARRAY(iface_uuid)->values[1]);
+  if (iface_uuid_str == NULL) {
+    ERROR("ovs_stats plugin: incorrect JSON interface data");
+    return -1;
+  }
+
+  interface_list_t *iface = ovs_stats_get_interface(iface_uuid_str);
+  if (iface == NULL)
     return 0;
 
+  sstrncpy(iface->name, YAJL_GET_STRING(iface_name), sizeof(iface->name));
+
   yajl_val iface_stats = ovs_utils_get_value_by_key(row, "statistics");
   yajl_val iface_ext_ids = ovs_utils_get_value_by_key(row, "external_ids");
-  yajl_val iface_uuid = ovs_utils_get_value_by_key(row, "_uuid");
+
   /*
    * {
         "statistics": [
@@ -622,17 +1066,44 @@ static int ovs_stats_update_iface(yajl_val iface) {
       }
    Check that statistics is an array with 2 elements
    */
+
   if (iface_stats && YAJL_IS_ARRAY(iface_stats) &&
       YAJL_GET_ARRAY(iface_stats)->len == 2)
-    ovs_stats_update_iface_stats(port, YAJL_GET_ARRAY(iface_stats)->values[1]);
+    ovs_stats_update_iface_stats(iface, YAJL_GET_ARRAY(iface_stats)->values[1]);
+
   if (iface_ext_ids && YAJL_IS_ARRAY(iface_ext_ids))
-    ovs_stats_update_iface_ext_ids(port,
+    ovs_stats_update_iface_ext_ids(iface,
                                    YAJL_GET_ARRAY(iface_ext_ids)->values[1]);
-  if (iface_uuid && YAJL_IS_ARRAY(iface_uuid) &&
-      YAJL_GET_ARRAY(iface_uuid)->len == 2)
-    sstrncpy(port->iface_uuid,
-             YAJL_GET_STRING(YAJL_GET_ARRAY(iface_uuid)->values[1]),
-             sizeof(port->iface_uuid));
+
+  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;
 }
@@ -690,12 +1161,16 @@ static void ovs_stats_interface_table_change_cb(yajl_val jupdates) {
     }
    */
   const char *path[] = {"Interface", NULL};
-  yajl_val ports = yajl_tree_get(jupdates, path, yajl_t_object);
+  yajl_val interfaces = yajl_tree_get(jupdates, path, yajl_t_object);
+  if (!interfaces || !YAJL_IS_OBJECT(interfaces))
+    return;
+
   pthread_mutex_lock(&g_stats_lock);
-  if (ports && YAJL_IS_OBJECT(ports))
-    for (size_t i = 0; i < YAJL_GET_OBJECT(ports)->len; i++)
-      ovs_stats_update_iface(YAJL_GET_OBJECT(ports)->values[i]);
+  for (size_t i = 0; i < YAJL_GET_OBJECT(interfaces)->len; i++) {
+    ovs_stats_update_iface(YAJL_GET_OBJECT(interfaces)->values[i]);
+  }
   pthread_mutex_unlock(&g_stats_lock);
+
   return;
 }
 
@@ -709,6 +1184,22 @@ static void ovs_stats_interface_table_result_cb(yajl_val jresult,
   return;
 }
 
+/* Handle Interface Table delete event */
+static void ovs_stats_interface_table_delete_cb(yajl_val jupdates) {
+  const char *path[] = {"Interface", NULL};
+  yajl_val interfaces = yajl_tree_get(jupdates, path, yajl_t_object);
+  if (!interfaces || !YAJL_IS_OBJECT(interfaces))
+    return;
+
+  pthread_mutex_lock(&g_stats_lock);
+  for (size_t i = 0; i < YAJL_GET_OBJECT(interfaces)->len; i++) {
+    ovs_stats_del_interface(YAJL_GET_OBJECT(interfaces)->keys[i]);
+  }
+  pthread_mutex_unlock(&g_stats_lock);
+
+  return;
+}
+
 /* Setup OVS DB table callbacks  */
 static void ovs_stats_initialize(ovs_db_t *pdb) {
   const char *bridge_columns[] = {"name", "ports", NULL};
@@ -742,25 +1233,23 @@ static void ovs_stats_initialize(ovs_db_t *pdb) {
       ovs_stats_interface_table_result_cb,
       OVS_DB_TABLE_CB_FLAG_INITIAL | OVS_DB_TABLE_CB_FLAG_INSERT |
           OVS_DB_TABLE_CB_FLAG_MODIFY);
-}
-
-/* Check if bridge is configured to be monitored in config file */
-static int ovs_stats_is_monitored_bridge(const char *br_name) {
-  /* if no bridges are configured, return true */
-  if (g_monitored_bridge_list_head == NULL)
-    return 1;
-
-  /* check if given bridge exists */
-  if (ovs_stats_get_bridge(g_monitored_bridge_list_head, br_name) != NULL)
-    return 1;
 
-  return 0;
+  ovs_db_table_cb_register(pdb, "Interface", interface_columns,
+                           ovs_stats_interface_table_delete_cb, NULL,
+                           OVS_DB_TABLE_CB_FLAG_DELETE);
 }
 
 /* Delete all ports from port list */
 static void ovs_stats_free_port_list(port_list_t *head) {
   for (port_list_t *i = head; i != NULL;) {
     port_list_t *del = i;
+
+    for (interface_list_t *iface = i->iface; iface != NULL; iface = i->iface) {
+      interface_list_t *del2 = iface;
+      i->iface = iface->next;
+      sfree(del2);
+    }
+
     i = i->next;
     sfree(del);
   }
@@ -826,13 +1315,14 @@ static int ovs_stats_plugin_config(oconfig_item_t *ci) {
         char const *br_name = child->values[j].value.string;
         if ((bridge = ovs_stats_get_bridge(g_monitored_bridge_list_head,
                                            br_name)) == NULL) {
-          if ((bridge = calloc(1, sizeof(bridge_list_t))) == NULL) {
+          if ((bridge = calloc(1, sizeof(*bridge))) == NULL) {
             ERROR("%s: Error allocating memory for bridge", plugin_name);
             goto cleanup_fail;
           } else {
             char *br_name_dup = strdup(br_name);
             if (br_name_dup == NULL) {
               ERROR("%s: strdup() copy bridge name fail", plugin_name);
+              sfree(bridge);
               goto cleanup_fail;
             }
 
@@ -846,6 +1336,11 @@ static int ovs_stats_plugin_config(oconfig_item_t *ci) {
           }
         }
       }
+    } else if (strcasecmp("InterfaceStats", child->key) == 0) {
+      if (cf_util_get_boolean(child, &interface_stats) != 0) {
+        ERROR("%s: parse '%s' option failed", plugin_name, child->key);
+        return -1;
+      }
     } else {
       WARNING("%s: option '%s' not allowed here", plugin_name, child->key);
       goto cleanup_fail;
@@ -884,89 +1379,22 @@ static int ovs_stats_plugin_init(void) {
 
 /* OvS stats read callback. Read bridge/port information and submit it*/
 static int ovs_stats_plugin_read(__attribute__((unused)) user_data_t *ud) {
-  bridge_list_t *bridge;
-  port_list_t *port;
-  char devname[PORT_NAME_SIZE_MAX * 2];
-
   pthread_mutex_lock(&g_stats_lock);
-  for (bridge = g_bridge_list_head; bridge != NULL; bridge = bridge->next) {
-    if (ovs_stats_is_monitored_bridge(bridge->name)) {
-      for (port = g_port_list_head; port != NULL; port = port->next)
-        if (port->br == bridge) {
-          if (strlen(port->name) == 0)
-            /* Skip port w/o name. This is possible when read callback
-             * is called after Interface Table update callback but before
-             * Port table Update callback. Will add this port on next read */
-            continue;
-          meta_data_t *meta = meta_data_create();
-          if (meta != NULL) {
-            meta_data_add_string(meta, "uuid", port->iface_uuid);
-            if (strlen(port->ex_vm_id))
-              meta_data_add_string(meta, "vm-uuid", port->ex_vm_id);
-            if (strlen(port->ex_iface_id))
-              meta_data_add_string(meta, "iface-id", port->ex_iface_id);
-          }
-          snprintf(devname, sizeof(devname), "%s.%s", bridge->name, port->name);
-          ovs_stats_submit_one(devname, "if_collisions", NULL,
-                               port->stats[collisions], meta);
-          ovs_stats_submit_two(devname, "if_dropped", NULL,
-                               port->stats[rx_dropped], port->stats[tx_dropped],
-                               meta);
-          ovs_stats_submit_two(devname, "if_errors", NULL,
-                               port->stats[rx_errors], port->stats[tx_errors],
-                               meta);
-          ovs_stats_submit_two(devname, "if_packets", NULL,
-                               port->stats[rx_packets], port->stats[tx_packets],
-                               meta);
-          ovs_stats_submit_one(devname, "if_rx_errors", "crc",
-                               port->stats[rx_crc_err], meta);
-          ovs_stats_submit_one(devname, "if_rx_errors", "frame",
-                               port->stats[rx_frame_err], meta);
-          ovs_stats_submit_one(devname, "if_rx_errors", "over",
-                               port->stats[rx_over_err], meta);
-          ovs_stats_submit_one(devname, "if_rx_octets", NULL,
-                               port->stats[rx_bytes], meta);
-          ovs_stats_submit_one(devname, "if_tx_octets", NULL,
-                               port->stats[tx_bytes], meta);
-          ovs_stats_submit_two(devname, "if_packets", "1_to_64_packets",
-                               port->stats[rx_1_to_64_packets],
-                               port->stats[tx_1_to_64_packets], meta);
-          ovs_stats_submit_two(devname, "if_packets", "65_to_127_packets",
-                               port->stats[rx_65_to_127_packets],
-                               port->stats[tx_65_to_127_packets], meta);
-          ovs_stats_submit_two(devname, "if_packets", "128_to_255_packets",
-                               port->stats[rx_128_to_255_packets],
-                               port->stats[tx_128_to_255_packets], meta);
-          ovs_stats_submit_two(devname, "if_packets", "256_to_511_packets",
-                               port->stats[rx_256_to_511_packets],
-                               port->stats[tx_256_to_511_packets], meta);
-          ovs_stats_submit_two(devname, "if_packets", "512_to_1023_packets",
-                               port->stats[rx_512_to_1023_packets],
-                               port->stats[tx_512_to_1023_packets], meta);
-          ovs_stats_submit_two(devname, "if_packets", "1024_to_1518_packets",
-                               port->stats[rx_1024_to_1522_packets],
-                               port->stats[tx_1024_to_1522_packets], meta);
-          ovs_stats_submit_two(devname, "if_packets", "1523_to_max_packets",
-                               port->stats[rx_1523_to_max_packets],
-                               port->stats[tx_1523_to_max_packets], meta);
-          ovs_stats_submit_two(devname, "if_packets", "broadcast_packets",
-                               port->stats[rx_broadcast_packets],
-                               port->stats[tx_broadcast_packets], meta);
-          ovs_stats_submit_one(devname, "if_multicast", "tx_multicast_packets",
-                               port->stats[tx_multicast_packets], meta);
-          ovs_stats_submit_one(devname, "if_rx_errors", "rx_undersized_errors",
-                               port->stats[rx_undersized_errors], meta);
-          ovs_stats_submit_one(devname, "if_rx_errors", "rx_oversize_errors",
-                               port->stats[rx_oversize_errors], meta);
-          ovs_stats_submit_one(devname, "if_rx_errors", "rx_fragmented_errors",
-                               port->stats[rx_fragmented_errors], meta);
-          ovs_stats_submit_one(devname, "if_rx_errors", "rx_jabber_errors",
-                               port->stats[rx_jabber_errors], meta);
-
-          meta_data_destroy(meta);
-        }
-    } else
+  for (port_list_t *port = g_port_list_head; port != NULL; port = port->next) {
+    if (strlen(port->name) == 0)
+      /* Skip port w/o name. This is possible when read callback
+       * is called after Interface Table update callback but before
+       * Port table Update callback. Will add this port on next read */
       continue;
+
+    /* Skip port if it has no bridge */
+    if (!port->br)
+      continue;
+
+    ovs_stats_submit_port(port);
+
+    if (interface_stats)
+      ovs_stats_submit_interfaces(port);
   }
   pthread_mutex_unlock(&g_stats_lock);
   return 0;
diff --git a/src/pcie_errors.c b/src/pcie_errors.c
new file mode 100644 (file)
index 0000000..6399605
--- /dev/null
@@ -0,0 +1,795 @@
+/**
+ * collectd - src/pcie_errors.c
+ *
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
+ **/
+
+#include "collectd.h"
+
+#include "utils/common/common.h"
+#include "utils_llist.h"
+
+#include <linux/pci_regs.h>
+
+#define PCIE_ERRORS_PLUGIN "pcie_errors"
+#define PCIE_DEFAULT_PROCDIR "/proc/bus/pci"
+#define PCIE_DEFAULT_SYSFSDIR "/sys/bus/pci"
+#define PCIE_NAME_LEN 512
+#define PCIE_BUFF_SIZE 1024
+
+#define PCIE_ERROR "pcie_error"
+#define PCIE_SEV_CE "correctable"
+#define PCIE_SEV_FATAL "fatal"
+#define PCIE_SEV_NOFATAL "non_fatal"
+
+#define PCIE_DEV(x) (((x) >> 3) & 0x1f)
+#define PCIE_FN(x) ((x)&0x07)
+
+#define PCIE_ECAP_OFFSET 0x100 /* ECAP always begin at offset 0x100 */
+
+typedef struct pcie_config_s {
+  bool use_sysfs;
+  bool notif_masked;
+  bool persistent;
+  char access_dir[PATH_MAX];
+} pcie_config_t;
+
+typedef struct pcie_device_s {
+  int fd;
+  int domain;
+  uint8_t bus;
+  uint8_t device;
+  uint8_t function;
+  int cap_exp;
+  int ecap_aer;
+  uint16_t device_status;
+  uint32_t correctable_errors;
+  uint32_t uncorrectable_errors;
+} pcie_device_t;
+
+typedef struct pcie_fops_s {
+  int (*list_devices)(llist_t *dev_list);
+  int (*open)(pcie_device_t *dev);
+  void (*close)(pcie_device_t *dev);
+  int (*read)(pcie_device_t *dev, void *buff, int size, int pos);
+} pcie_fops_t;
+
+typedef struct pcie_error_s {
+  int mask;
+  const char *desc;
+} pcie_error_t;
+
+static llist_t *pcie_dev_list;
+static pcie_config_t pcie_config = {.access_dir = "", .use_sysfs = true};
+static pcie_fops_t pcie_fops;
+
+/* Device Error Status */
+static const pcie_error_t pcie_base_errors[] = {
+    {PCI_EXP_DEVSTA_CED, "Correctable Error"},
+    {PCI_EXP_DEVSTA_NFED, "Non-Fatal Error"},
+    {PCI_EXP_DEVSTA_FED, "Fatal Error"},
+    {PCI_EXP_DEVSTA_URD, "Unsupported Request"}};
+static const int pcie_base_errors_num = STATIC_ARRAY_SIZE(pcie_base_errors);
+
+/* Uncorrectable Error Status */
+static const pcie_error_t pcie_aer_ues[] = {
+#ifdef PCI_ERR_UNC_DLP
+    {PCI_ERR_UNC_DLP, "Data Link Protocol"},
+#endif
+#ifdef PCI_ERR_UNC_SURPDN
+    {PCI_ERR_UNC_SURPDN, "Surprise Down"},
+#endif
+#ifdef PCI_ERR_UNC_POISON_TLP
+    {PCI_ERR_UNC_POISON_TLP, "Poisoned TLP"},
+#endif
+#ifdef PCI_ERR_UNC_FCP
+    {PCI_ERR_UNC_FCP, "Flow Control Protocol"},
+#endif
+#ifdef PCI_ERR_UNC_COMP_TIME
+    {PCI_ERR_UNC_COMP_TIME, "Completion Timeout"},
+#endif
+#ifdef PCI_ERR_UNC_COMP_ABORT
+    {PCI_ERR_UNC_COMP_ABORT, "Completer Abort"},
+#endif
+#ifdef PCI_ERR_UNC_UNX_COMP
+    {PCI_ERR_UNC_UNX_COMP, "Unexpected Completion"},
+#endif
+#ifdef PCI_ERR_UNC_RX_OVER
+    {PCI_ERR_UNC_RX_OVER, "Receiver Overflow"},
+#endif
+#ifdef PCI_ERR_UNC_MALF_TLP
+    {PCI_ERR_UNC_MALF_TLP, "Malformed TLP"},
+#endif
+#ifdef PCI_ERR_UNC_ECRC
+    {PCI_ERR_UNC_ECRC, "ECRC Error Status"},
+#endif
+#ifdef PCI_ERR_UNC_UNSUP
+    {PCI_ERR_UNC_UNSUP, "Unsupported Request"},
+#endif
+#ifdef PCI_ERR_UNC_ACSV
+    {PCI_ERR_UNC_ACSV, "ACS Violation"},
+#endif
+#ifdef PCI_ERR_UNC_INTN
+    {PCI_ERR_UNC_INTN, "Internal"},
+#endif
+#ifdef PCI_ERR_UNC_MCBTLP
+    {PCI_ERR_UNC_MCBTLP, "MC blocked TLP"},
+#endif
+#ifdef PCI_ERR_UNC_ATOMEG
+    {PCI_ERR_UNC_ATOMEG, "Atomic egress blocked"},
+#endif
+#ifdef PCI_ERR_UNC_TLPPRE
+    {PCI_ERR_UNC_TLPPRE, "TLP prefix blocked"},
+#endif
+};
+static const int pcie_aer_ues_num = STATIC_ARRAY_SIZE(pcie_aer_ues);
+
+/* Correctable Error Status */
+static const pcie_error_t pcie_aer_ces[] = {
+#ifdef PCI_ERR_COR_RCVR
+    {PCI_ERR_COR_RCVR, "Receiver Error Status"},
+#endif
+#ifdef PCI_ERR_COR_BAD_TLP
+    {PCI_ERR_COR_BAD_TLP, "Bad TLP Status"},
+#endif
+#ifdef PCI_ERR_COR_BAD_DLLP
+    {PCI_ERR_COR_BAD_DLLP, "Bad DLLP Status"},
+#endif
+#ifdef PCI_ERR_COR_REP_ROLL
+    {PCI_ERR_COR_REP_ROLL, "REPLAY_NUM Rollover"},
+#endif
+#ifdef PCI_ERR_COR_REP_TIMER
+    {PCI_ERR_COR_REP_TIMER, "Replay Timer Timeout"},
+#endif
+#ifdef PCI_ERR_COR_ADV_NFAT
+    {PCI_ERR_COR_ADV_NFAT, "Advisory Non-Fatal"},
+#endif
+#ifdef PCI_ERR_COR_INTERNAL
+    {PCI_ERR_COR_INTERNAL, "Corrected Internal"},
+#endif
+#ifdef PCI_ERR_COR_LOG_OVER
+    {PCI_ERR_COR_LOG_OVER, "Header Log Overflow"},
+#endif
+};
+static const int pcie_aer_ces_num = STATIC_ARRAY_SIZE(pcie_aer_ces);
+
+static int pcie_add_device(llist_t *list, int domain, uint8_t bus,
+                           uint8_t device, uint8_t fn) {
+  llentry_t *entry;
+  pcie_device_t *dev = calloc(1, sizeof(*dev));
+  if (dev == NULL) {
+    ERROR(PCIE_ERRORS_PLUGIN ": Failed to allocate device");
+    return -ENOMEM;
+  }
+
+  dev->domain = domain;
+  dev->bus = bus;
+  dev->device = device;
+  dev->function = fn;
+  dev->cap_exp = -1;
+  dev->ecap_aer = -1;
+  entry = llentry_create(NULL, dev);
+  if (entry == NULL) {
+    ERROR(PCIE_ERRORS_PLUGIN ": Failed to create llentry");
+    sfree(dev);
+    return -ENOMEM;
+  }
+  llist_append(list, entry);
+
+  DEBUG(PCIE_ERRORS_PLUGIN ": pci device added to list: %04x:%02x:%02x.%d",
+        domain, bus, device, fn);
+  return 0;
+}
+
+static void pcie_clear_list(llist_t *list) {
+  if (list == NULL)
+    return;
+
+  for (llentry_t *e = llist_head(list); e != NULL; e = e->next)
+    sfree(e->value);
+
+  llist_destroy(list);
+}
+
+static int pcie_list_devices_proc(llist_t *dev_list) {
+  FILE *fd;
+  char file_name[PCIE_NAME_LEN];
+  char buf[PCIE_BUFF_SIZE];
+  unsigned int i = 0;
+  int ret = 0;
+
+  if (dev_list == NULL)
+    return -EINVAL;
+
+  ret = snprintf(file_name, sizeof(file_name), "%s/devices",
+                 pcie_config.access_dir);
+  if (ret < 1 || (size_t)ret >= sizeof(file_name)) {
+    ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
+          pcie_config.access_dir, ret);
+    return -EINVAL;
+  }
+  fd = fopen(file_name, "r");
+  if (!fd) {
+    char errbuf[PCIE_BUFF_SIZE];
+    ERROR(PCIE_ERRORS_PLUGIN ": Cannot open file %s to get devices list: %s",
+          file_name, sstrerror(errno, errbuf, sizeof(errbuf)));
+    return -ENOENT;
+  }
+
+  while (fgets(buf, sizeof(buf), fd)) {
+    unsigned int slot;
+
+    if (sscanf(buf, "%x", &slot) != 1) {
+      ERROR(PCIE_ERRORS_PLUGIN ": Failed to read line %u from %s", i + 1,
+            file_name);
+      continue;
+    }
+
+    uint8_t bus = slot >> 8U;
+    uint8_t dev = PCIE_DEV(slot);
+    uint8_t fn = PCIE_FN(slot);
+    ret = pcie_add_device(dev_list, 0, bus, dev, fn);
+    if (ret)
+      break;
+
+    ++i;
+  }
+
+  fclose(fd);
+  return ret;
+}
+
+static int pcie_list_devices_sysfs(llist_t *dev_list) {
+  DIR *dir;
+  struct dirent *item;
+  char dir_name[PCIE_NAME_LEN];
+  int ret = 0;
+
+  if (dev_list == NULL)
+    return -EINVAL;
+
+  ret = snprintf(dir_name, sizeof(dir_name), "%s/devices",
+                 pcie_config.access_dir);
+  if (ret < 1 || (size_t)ret >= sizeof(dir_name)) {
+    ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
+          pcie_config.access_dir, ret);
+    return -EINVAL;
+  }
+  dir = opendir(dir_name);
+  if (!dir) {
+    char errbuf[PCIE_BUFF_SIZE];
+    ERROR(PCIE_ERRORS_PLUGIN ": Cannot open dir %s to get devices list: %s",
+          dir_name, sstrerror(errno, errbuf, sizeof(errbuf)));
+    return -ENOENT;
+  }
+
+  while ((item = readdir(dir))) {
+    unsigned int dom, bus, dev;
+    int fn;
+
+    /* Omit special non-device entries */
+    if (item->d_name[0] == '.')
+      continue;
+
+    if (sscanf(item->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &fn) != 4) {
+      ERROR(PCIE_ERRORS_PLUGIN ": Failed to parse entry %s", item->d_name);
+      continue;
+    }
+
+    ret = pcie_add_device(dev_list, dom, bus, dev, fn);
+    if (ret)
+      break;
+  }
+
+  closedir(dir);
+  return ret;
+}
+
+static void pcie_close(pcie_device_t *dev) {
+  if (close(dev->fd) == -1) {
+    char errbuf[PCIE_BUFF_SIZE];
+    ERROR(PCIE_ERRORS_PLUGIN ": Failed to close %04x:%02x:%02x.%d, fd=%d: %s",
+          dev->domain, dev->bus, dev->device, dev->function, dev->fd,
+          sstrerror(errno, errbuf, sizeof(errbuf)));
+  }
+
+  dev->fd = -1;
+}
+
+static int pcie_open(pcie_device_t *dev, const char *name) {
+  dev->fd = open(name, O_RDONLY);
+  if (dev->fd == -1) {
+    char errbuf[PCIE_BUFF_SIZE];
+    ERROR(PCIE_ERRORS_PLUGIN ": Failed to open file %s: %s", name,
+          sstrerror(errno, errbuf, sizeof(errbuf)));
+    return -ENOENT;
+  }
+
+  return 0;
+}
+
+static int pcie_open_proc(pcie_device_t *dev) {
+  char file_name[PCIE_NAME_LEN];
+
+  int ret =
+      snprintf(file_name, sizeof(file_name), "%s/%02x/%02x.%d",
+               pcie_config.access_dir, dev->bus, dev->device, dev->function);
+  if (ret < 1 || (size_t)ret >= sizeof(file_name)) {
+    ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
+          pcie_config.access_dir, ret);
+    return -EINVAL;
+  }
+
+  return pcie_open(dev, file_name);
+}
+
+static int pcie_open_sysfs(pcie_device_t *dev) {
+  char file_name[PCIE_NAME_LEN];
+
+  int ret =
+      snprintf(file_name, sizeof(file_name),
+               "%s/devices/%04x:%02x:%02x.%d/config", pcie_config.access_dir,
+               dev->domain, dev->bus, dev->device, dev->function);
+  if (ret < 1 || (size_t)ret >= sizeof(file_name)) {
+    ERROR(PCIE_ERRORS_PLUGIN ": Access dir `%s' is too long (%d)",
+          pcie_config.access_dir, ret);
+    return -EINVAL;
+  }
+
+  return pcie_open(dev, file_name);
+}
+
+static int pcie_read(pcie_device_t *dev, void *buff, int size, int pos) {
+  int len = pread(dev->fd, buff, size, pos);
+  if (len == size)
+    return 0;
+
+  if (len == -1) {
+    char errbuf[PCIE_BUFF_SIZE];
+    ERROR(PCIE_ERRORS_PLUGIN ": Failed to read %04x:%02x:%02x.%d at pos %d: %s",
+          dev->domain, dev->bus, dev->device, dev->function, pos,
+          sstrerror(errno, errbuf, sizeof(errbuf)));
+  } else {
+    ERROR(PCIE_ERRORS_PLUGIN
+          ": %04x:%02x:%02x.%d Read only %d bytes, should be %d",
+          dev->domain, dev->bus, dev->device, dev->function, len, size);
+  }
+  return -1;
+}
+
+static uint8_t pcie_read8(pcie_device_t *dev, int pos) {
+  uint8_t value;
+  if (pcie_fops.read(dev, &value, 1, pos))
+    return 0;
+  return value;
+}
+
+static uint16_t pcie_read16(pcie_device_t *dev, int pos) {
+  uint16_t value;
+  if (pcie_fops.read(dev, &value, 2, pos))
+    return 0;
+  return value;
+}
+
+static uint32_t pcie_read32(pcie_device_t *dev, int pos) {
+  uint32_t value;
+  if (pcie_fops.read(dev, &value, 4, pos))
+    return 0;
+  return value;
+}
+
+static void pcie_dispatch_notification(pcie_device_t *dev, notification_t *n,
+                                       const char *type,
+                                       const char *type_instance) {
+  sstrncpy(n->host, hostname_g, sizeof(n->host));
+  snprintf(n->plugin_instance, sizeof(n->plugin_instance), "%04x:%02x:%02x.%d",
+           dev->domain, dev->bus, dev->device, dev->function);
+  sstrncpy(n->type, type, sizeof(n->type));
+  sstrncpy(n->type_instance, type_instance, sizeof(n->type_instance));
+
+  plugin_dispatch_notification(n);
+}
+
+/* Report errors found in AER Correctable Error Status register */
+static void pcie_dispatch_correctable_errors(pcie_device_t *dev,
+                                             uint32_t errors, uint32_t masked) {
+  for (int i = 0; i < pcie_aer_ces_num; i++) {
+    const pcie_error_t *err = pcie_aer_ces + i;
+    notification_t n = {.severity = NOTIF_WARNING,
+                        .time = cdtime(),
+                        .plugin = PCIE_ERRORS_PLUGIN,
+                        .meta = NULL};
+
+    /* If not specifically set by config option omit masked errors */
+    if (!pcie_config.notif_masked && (err->mask & masked))
+      continue;
+
+    if (err->mask & errors) {
+      /* Error already reported, notify only if persistent is set */
+      if (!pcie_config.persistent && (err->mask & dev->correctable_errors))
+        continue;
+
+      DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s set", dev->domain,
+            dev->bus, dev->device, dev->function, err->desc);
+      snprintf(n.message, sizeof(n.message), "Correctable Error set: %s",
+               err->desc);
+      pcie_dispatch_notification(dev, &n, PCIE_ERROR, PCIE_SEV_CE);
+
+    } else if (err->mask & dev->correctable_errors) {
+      DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s cleared", dev->domain,
+            dev->bus, dev->device, dev->function, err->desc);
+
+      n.severity = NOTIF_OKAY;
+      snprintf(n.message, sizeof(n.message), "Correctable Error cleared: %s",
+               err->desc);
+      pcie_dispatch_notification(dev, &n, PCIE_ERROR, PCIE_SEV_CE);
+    }
+  }
+}
+
+/* Report errors found in AER Uncorrectable Error Status register */
+static void pcie_dispatch_uncorrectable_errors(pcie_device_t *dev,
+                                               uint32_t errors, uint32_t masked,
+                                               uint32_t severity) {
+  for (int i = 0; i < pcie_aer_ues_num; i++) {
+    const pcie_error_t *err = pcie_aer_ues + i;
+    const char *type_instance =
+        (severity & err->mask) ? PCIE_SEV_FATAL : PCIE_SEV_NOFATAL;
+    notification_t n = {
+        .time = cdtime(), .plugin = PCIE_ERRORS_PLUGIN, .meta = NULL};
+
+    /* If not specifically set by config option omit masked errors */
+    if (!pcie_config.notif_masked && (err->mask & masked))
+      continue;
+
+    if (err->mask & errors) {
+      /* Error already reported, notify only if persistent is set */
+      if (!pcie_config.persistent && (err->mask & dev->uncorrectable_errors))
+        continue;
+
+      DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s(%s) set", dev->domain,
+            dev->bus, dev->device, dev->function, err->desc, type_instance);
+
+      n.severity = (severity & err->mask) ? NOTIF_FAILURE : NOTIF_WARNING;
+      snprintf(n.message, sizeof(n.message), "Uncorrectable(%s) Error set: %s",
+               type_instance, err->desc);
+      pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
+
+    } else if (err->mask & dev->uncorrectable_errors) {
+      DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s(%s) cleared",
+            dev->domain, dev->bus, dev->device, dev->function, err->desc,
+            type_instance);
+
+      n.severity = NOTIF_OKAY;
+      snprintf(n.message, sizeof(n.message),
+               "Uncorrectable(%s) Error cleared: %s", type_instance, err->desc);
+      pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
+    }
+  }
+}
+
+/* Find offset of PCI Express Capability Structure
+ * in PCI configuration space.
+ * Returns offset, -1 if not found.
+**/
+static int pcie_find_cap_exp(pcie_device_t *dev) {
+  int pos = pcie_read8(dev, PCI_CAPABILITY_LIST) & ~3;
+
+  while (pos) {
+    uint8_t id = pcie_read8(dev, pos + PCI_CAP_LIST_ID);
+
+    if (id == 0xff)
+      break;
+    if (id == PCI_CAP_ID_EXP)
+      return pos;
+
+    pos = pcie_read8(dev, pos + PCI_CAP_LIST_NEXT) & ~3;
+  }
+
+  DEBUG(PCIE_ERRORS_PLUGIN ": Cannot find CAP EXP for %04x:%02x:%02x.%d",
+        dev->domain, dev->bus, dev->device, dev->function);
+
+  return -1;
+}
+
+/* Find offset of Advanced Error Reporting Capability.
+ * Returns AER offset, -1 if not found.
+**/
+static int pcie_find_ecap_aer(pcie_device_t *dev) {
+  int pos = PCIE_ECAP_OFFSET;
+  uint32_t header = pcie_read32(dev, pos);
+  int id = PCI_EXT_CAP_ID(header);
+  int next = PCI_EXT_CAP_NEXT(header);
+
+  if (!id && !next)
+    return -1;
+
+  if (id == PCI_EXT_CAP_ID_ERR)
+    return pos;
+
+  while (next) {
+    if (next <= PCIE_ECAP_OFFSET)
+      break;
+
+    header = pcie_read32(dev, next);
+    id = PCI_EXT_CAP_ID(header);
+
+    if (id == PCI_EXT_CAP_ID_ERR)
+      return next;
+
+    next = PCI_EXT_CAP_NEXT(header);
+  }
+
+  return -1;
+}
+
+static void pcie_check_dev_status(pcie_device_t *dev, int pos) {
+  /* Read Device Status register with mask for errors only */
+  uint16_t new_status = pcie_read16(dev, pos + PCI_EXP_DEVSTA) & 0xf;
+
+  /* Check if anything new should be reported */
+  if (!(pcie_config.persistent && new_status) &&
+      (new_status == dev->device_status))
+    return;
+
+  /* Report errors found in Device Status register */
+  for (int i = 0; i < pcie_base_errors_num; i++) {
+    const pcie_error_t *err = pcie_base_errors + i;
+    const char *type_instance = (err->mask == PCI_EXP_DEVSTA_FED)
+                                    ? PCIE_SEV_FATAL
+                                    : (err->mask == PCI_EXP_DEVSTA_CED)
+                                          ? PCIE_SEV_CE
+                                          : PCIE_SEV_NOFATAL;
+    int severity =
+        (err->mask == PCI_EXP_DEVSTA_FED) ? NOTIF_FAILURE : NOTIF_WARNING;
+    notification_t n = {.severity = severity,
+                        .time = cdtime(),
+                        .plugin = PCIE_ERRORS_PLUGIN,
+                        .meta = NULL};
+
+    if (err->mask & new_status) {
+      /* Error already reported, notify only if persistent is set */
+      if (!pcie_config.persistent && (err->mask & dev->device_status))
+        continue;
+
+      DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s set", dev->domain,
+            dev->bus, dev->device, dev->function, err->desc);
+      snprintf(n.message, sizeof(n.message), "Device Status Error set: %s",
+               err->desc);
+      pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
+
+    } else if (err->mask & dev->device_status) {
+      DEBUG(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: %s cleared", dev->domain,
+            dev->bus, dev->device, dev->function, err->desc);
+      n.severity = NOTIF_OKAY;
+      snprintf(n.message, sizeof(n.message), "Device Status Error cleared: %s",
+               err->desc);
+      pcie_dispatch_notification(dev, &n, PCIE_ERROR, type_instance);
+    }
+  }
+
+  dev->device_status = new_status;
+}
+
+static void pcie_check_aer(pcie_device_t *dev, int pos) {
+  /* Check for AER uncorrectable errors */
+  uint32_t errors = pcie_read32(dev, pos + PCI_ERR_UNCOR_STATUS);
+
+  if ((pcie_config.persistent && errors) ||
+      (errors != dev->uncorrectable_errors)) {
+    uint32_t masked = pcie_read32(dev, pos + PCI_ERR_UNCOR_MASK);
+    uint32_t severity = pcie_read32(dev, pos + PCI_ERR_UNCOR_SEVER);
+    pcie_dispatch_uncorrectable_errors(dev, errors, masked, severity);
+  }
+  dev->uncorrectable_errors = errors;
+
+  /* Check for AER correctable errors */
+  errors = pcie_read32(dev, pos + PCI_ERR_COR_STATUS);
+  if ((pcie_config.persistent && errors) ||
+      (errors != dev->correctable_errors)) {
+    uint32_t masked = pcie_read32(dev, pos + PCI_ERR_COR_MASK);
+    pcie_dispatch_correctable_errors(dev, errors, masked);
+  }
+  dev->correctable_errors = errors;
+}
+
+static int pcie_process_devices(llist_t *devs) {
+  int ret = 0;
+  if (devs == NULL)
+    return -1;
+
+  for (llentry_t *e = llist_head(devs); e != NULL; e = e->next) {
+    pcie_device_t *dev = e->value;
+
+    if (pcie_fops.open(dev) == 0) {
+      pcie_check_dev_status(dev, dev->cap_exp);
+      if (dev->ecap_aer != -1)
+        pcie_check_aer(dev, dev->ecap_aer);
+
+      pcie_fops.close(dev);
+    } else {
+      notification_t n = {.severity = NOTIF_FAILURE,
+                          .time = cdtime(),
+                          .message = "Failed to read device status",
+                          .plugin = PCIE_ERRORS_PLUGIN,
+                          .meta = NULL};
+      pcie_dispatch_notification(dev, &n, "", "");
+      ret = -1;
+    }
+  }
+
+  return ret;
+}
+
+/* This function is to be called during init to filter out no pcie devices */
+static void pcie_preprocess_devices(llist_t *devs) {
+  llentry_t *e_next;
+
+  if (devs == NULL)
+    return;
+
+  for (llentry_t *e = llist_head(devs); e != NULL; e = e_next) {
+    pcie_device_t *dev = e->value;
+    bool del = false;
+
+    if (pcie_fops.open(dev) == 0) {
+      uint16_t status = pcie_read16(dev, PCI_STATUS);
+      if (status & PCI_STATUS_CAP_LIST)
+        dev->cap_exp = pcie_find_cap_exp(dev);
+
+      /* Every PCIe device must have Capability Structure */
+      if (dev->cap_exp == -1) {
+        DEBUG(PCIE_ERRORS_PLUGIN ": Not PCI Express device: %04x:%02x:%02x.%d",
+              dev->domain, dev->bus, dev->device, dev->function);
+        del = true;
+      } else {
+        dev->ecap_aer = pcie_find_ecap_aer(dev);
+        if (dev->ecap_aer == -1)
+          INFO(PCIE_ERRORS_PLUGIN
+               ": Device is not AER capable: %04x:%02x:%02x.%d",
+               dev->domain, dev->bus, dev->device, dev->function);
+      }
+
+      pcie_fops.close(dev);
+    } else {
+      ERROR(PCIE_ERRORS_PLUGIN ": %04x:%02x:%02x.%d: failed to open",
+            dev->domain, dev->bus, dev->device, dev->function);
+      del = true;
+    }
+
+    e_next = e->next;
+    if (del) {
+      sfree(dev);
+      llist_remove(devs, e);
+      llentry_destroy(e);
+    }
+  }
+}
+
+static int pcie_plugin_read(__attribute__((unused)) user_data_t *ud) {
+
+  if (pcie_process_devices(pcie_dev_list) < 0) {
+    ERROR(PCIE_ERRORS_PLUGIN ": Failed to read devices state");
+    return -1;
+  }
+  return 0;
+}
+
+static void pcie_access_config(void) {
+  /* Set functions for register access to
+   * use proc or sysfs depending on config. */
+  if (pcie_config.use_sysfs) {
+    pcie_fops.list_devices = pcie_list_devices_sysfs;
+    pcie_fops.open = pcie_open_sysfs;
+    if (pcie_config.access_dir[0] == '\0')
+      sstrncpy(pcie_config.access_dir, PCIE_DEFAULT_SYSFSDIR,
+               sizeof(pcie_config.access_dir));
+  } else {
+    /* use proc */
+    pcie_fops.list_devices = pcie_list_devices_proc;
+    pcie_fops.open = pcie_open_proc;
+    if (pcie_config.access_dir[0] == '\0')
+      sstrncpy(pcie_config.access_dir, PCIE_DEFAULT_PROCDIR,
+               sizeof(pcie_config.access_dir));
+  }
+  /* Common functions */
+  pcie_fops.close = pcie_close;
+  pcie_fops.read = pcie_read;
+}
+
+static int pcie_plugin_config(oconfig_item_t *ci) {
+  int status = 0;
+
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp("Source", child->key) == 0) {
+      if ((child->values_num != 1) ||
+          (child->values[0].type != OCONFIG_TYPE_STRING)) {
+        status = -1;
+      } else if (strcasecmp("proc", child->values[0].value.string) == 0) {
+        pcie_config.use_sysfs = false;
+      } else if (strcasecmp("sysfs", child->values[0].value.string) != 0) {
+        ERROR(PCIE_ERRORS_PLUGIN ": Allowed sources are 'proc' or 'sysfs'.");
+        status = -1;
+      }
+    } else if (strcasecmp("AccessDir", child->key) == 0) {
+      status = cf_util_get_string_buffer(child, pcie_config.access_dir,
+                                         sizeof(pcie_config.access_dir));
+    } else if (strcasecmp("ReportMasked", child->key) == 0) {
+      status = cf_util_get_boolean(child, &pcie_config.notif_masked);
+    } else if (strcasecmp("PersistentNotifications", child->key) == 0) {
+      status = cf_util_get_boolean(child, &pcie_config.persistent);
+    } else {
+      ERROR(PCIE_ERRORS_PLUGIN ": Invalid configuration option \"%s\".",
+            child->key);
+      status = -1;
+      break;
+    }
+
+    if (status) {
+      ERROR(PCIE_ERRORS_PLUGIN ": Invalid configuration parameter \"%s\".",
+            child->key);
+      break;
+    }
+  }
+
+  return status;
+}
+
+static int pcie_shutdown(void) {
+  pcie_clear_list(pcie_dev_list);
+  pcie_dev_list = NULL;
+
+  return 0;
+}
+
+static int pcie_init(void) {
+
+  pcie_access_config();
+  pcie_dev_list = llist_create();
+  if (pcie_fops.list_devices(pcie_dev_list) != 0) {
+    ERROR(PCIE_ERRORS_PLUGIN ": Failed to find devices.");
+    pcie_shutdown();
+    return -1;
+  }
+  pcie_preprocess_devices(pcie_dev_list);
+  if (llist_size(pcie_dev_list) == 0) {
+    /* No any PCI Express devices were found on the system */
+    ERROR(PCIE_ERRORS_PLUGIN ": No PCIe devices found in %s",
+          pcie_config.access_dir);
+    pcie_shutdown();
+    return -1;
+  }
+
+  return 0;
+}
+
+void module_register(void) {
+  plugin_register_init(PCIE_ERRORS_PLUGIN, pcie_init);
+  plugin_register_complex_config(PCIE_ERRORS_PLUGIN, pcie_plugin_config);
+  plugin_register_complex_read(NULL, PCIE_ERRORS_PLUGIN, pcie_plugin_read, 0,
+                               NULL);
+  plugin_register_shutdown(PCIE_ERRORS_PLUGIN, pcie_shutdown);
+}
diff --git a/src/pcie_errors_test.c b/src/pcie_errors_test.c
new file mode 100644 (file)
index 0000000..5cb95fa
--- /dev/null
@@ -0,0 +1,570 @@
+/**
+ * collectd - src/pcie_errors.c
+ *
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
+ **/
+
+#define plugin_dispatch_notification plugin_dispatch_notification_pcie_test
+
+#include "pcie_errors.c" /* sic */
+#include "testing.h"
+
+#define TEST_DOMAIN 1
+#define TEST_BUS 5
+#define TEST_DEVICE 0xc
+#define TEST_FUNCTION 2
+#define TEST_DEVICE_STR "0001:05:0c.2"
+
+#define G_BUFF_LEN 4
+
+static notification_t last_notif;
+static char g_buff[G_BUFF_LEN];
+
+/* mock functions */
+int plugin_dispatch_notification_pcie_test(const notification_t *notif) {
+  last_notif = *notif;
+  return ENOTSUP;
+}
+
+ssize_t pread(__attribute__((unused)) int fd, void *buf, size_t count,
+              __attribute__((unused)) off_t offset) {
+  if (count == 0 || count > G_BUFF_LEN)
+    return -1;
+
+  memcpy(buf, g_buff, count);
+  return count;
+}
+/* end mock functions */
+
+DEF_TEST(clear_dev_list) {
+  pcie_clear_list(NULL);
+
+  llist_t *test_list = llist_create();
+  CHECK_NOT_NULL(test_list);
+
+  pcie_device_t *dev = calloc(1, sizeof(*dev));
+  CHECK_NOT_NULL(dev);
+
+  llentry_t *entry = llentry_create(NULL, dev);
+  CHECK_NOT_NULL(entry);
+
+  llist_append(test_list, entry);
+
+  for (llentry_t *e = llist_head(test_list); e != NULL; e = e->next) {
+    EXPECT_EQ_PTR(dev, e->value);
+  }
+
+  pcie_clear_list(test_list);
+
+  return 0;
+}
+
+DEF_TEST(add_to_list) {
+  llist_t *test_list = llist_create();
+  CHECK_NOT_NULL(test_list);
+
+  int ret = pcie_add_device(test_list, TEST_DOMAIN, TEST_BUS, TEST_DEVICE,
+                            TEST_FUNCTION);
+  EXPECT_EQ_INT(0, ret);
+
+  llentry_t *e = llist_head(test_list);
+  CHECK_NOT_NULL(e);
+  OK(NULL == e->next);
+
+  pcie_device_t *dev = e->value;
+  CHECK_NOT_NULL(dev);
+  EXPECT_EQ_INT(TEST_DOMAIN, dev->domain);
+  EXPECT_EQ_INT(TEST_BUS, dev->bus);
+  EXPECT_EQ_INT(TEST_DEVICE, dev->device);
+  EXPECT_EQ_INT(TEST_FUNCTION, dev->function);
+  EXPECT_EQ_INT(-1, dev->cap_exp);
+  EXPECT_EQ_INT(-1, dev->ecap_aer);
+
+  pcie_clear_list(test_list);
+
+  return 0;
+}
+
+DEF_TEST(pcie_read) {
+  int ret;
+  pcie_device_t dev = {0};
+  uint32_t val = 0;
+  g_buff[0] = 4;
+  g_buff[1] = 3;
+  g_buff[2] = 2;
+  g_buff[3] = 1;
+
+  ret = pcie_read(&dev, &val, 1, 0);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(4, val);
+
+  ret = pcie_read(&dev, &val, 2, 0);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(0x304, val);
+
+  ret = pcie_read(&dev, &val, 3, 0);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(0x20304, val);
+
+  ret = pcie_read(&dev, &val, 4, 0);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(0x1020304, val);
+
+  ret = pcie_read(&dev, &val, G_BUFF_LEN + 1, 0);
+  EXPECT_EQ_INT(-1, ret);
+
+  pcie_fops.read = pcie_read;
+
+  uint8_t val8 = pcie_read8(&dev, 0);
+  EXPECT_EQ_INT(4, val8);
+
+  uint16_t val16 = pcie_read16(&dev, 0);
+  EXPECT_EQ_INT(0x304, val16);
+
+  uint32_t val32 = pcie_read32(&dev, 0);
+  EXPECT_EQ_INT(0x1020304, val32);
+
+  return 0;
+}
+
+DEF_TEST(dispatch_notification) {
+  pcie_device_t dev = {0, TEST_DOMAIN, TEST_BUS, TEST_DEVICE, TEST_FUNCTION,
+                       0, 0,           0,        0,           0};
+  cdtime_t t = cdtime();
+  notification_t n = {
+      .severity = 1, .time = t, .plugin = "pcie_errors_test", .meta = NULL};
+
+  pcie_dispatch_notification(&dev, &n, "test_type", "test_type_instance");
+  EXPECT_EQ_INT(1, last_notif.severity);
+  EXPECT_EQ_UINT64(t, last_notif.time);
+  EXPECT_EQ_STR("pcie_errors_test", last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(hostname_g, last_notif.host);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR("test_type", last_notif.type);
+  EXPECT_EQ_STR("test_type_instance", last_notif.type_instance);
+
+  return 0;
+}
+
+DEF_TEST(access_config) {
+  pcie_config.use_sysfs = 0;
+  pcie_access_config();
+  EXPECT_EQ_PTR(pcie_list_devices_proc, pcie_fops.list_devices);
+  EXPECT_EQ_PTR(pcie_open_proc, pcie_fops.open);
+  EXPECT_EQ_PTR(pcie_close, pcie_fops.close);
+  EXPECT_EQ_PTR(pcie_read, pcie_fops.read);
+  EXPECT_EQ_STR(PCIE_DEFAULT_PROCDIR, pcie_config.access_dir);
+
+  sstrncpy(pcie_config.access_dir, "Test", sizeof(pcie_config.access_dir));
+  pcie_access_config();
+  EXPECT_EQ_STR("Test", pcie_config.access_dir);
+
+  pcie_config.use_sysfs = 1;
+  pcie_access_config();
+  EXPECT_EQ_PTR(pcie_list_devices_sysfs, pcie_fops.list_devices);
+  EXPECT_EQ_PTR(pcie_open_sysfs, pcie_fops.open);
+  EXPECT_EQ_PTR(pcie_close, pcie_fops.close);
+  EXPECT_EQ_PTR(pcie_read, pcie_fops.read);
+  EXPECT_EQ_STR("Test", pcie_config.access_dir);
+
+  pcie_config.access_dir[0] = '\0';
+  pcie_access_config();
+  EXPECT_EQ_STR(PCIE_DEFAULT_SYSFSDIR, pcie_config.access_dir);
+
+  return 0;
+}
+
+DEF_TEST(plugin_config_fail) {
+  oconfig_item_t test_cfg_parent = {"pcie_errors", NULL, 0, NULL, NULL, 0};
+  char value_buff[256] = "procs";
+  char key_buff[256] = "Sources";
+  oconfig_value_t test_cfg_value = {{value_buff}, OCONFIG_TYPE_STRING};
+  oconfig_item_t test_cfg = {
+      key_buff, &test_cfg_value, 1, &test_cfg_parent, NULL, 0};
+
+  test_cfg_parent.children = &test_cfg;
+  test_cfg_parent.children_num = 1;
+
+  int ret = pcie_plugin_config(&test_cfg_parent);
+  EXPECT_EQ_INT(-1, ret);
+
+  sstrncpy(key_buff, "Source", sizeof(key_buff));
+  ret = pcie_plugin_config(&test_cfg_parent);
+  EXPECT_EQ_INT(-1, ret);
+
+  sstrncpy(value_buff, "proc", sizeof(value_buff));
+  test_cfg_value.type = OCONFIG_TYPE_NUMBER;
+  ret = pcie_plugin_config(&test_cfg_parent);
+  EXPECT_EQ_INT(-1, ret);
+
+  sstrncpy(key_buff, "AccessDir", sizeof(key_buff));
+  ret = pcie_plugin_config(&test_cfg_parent);
+  EXPECT_EQ_INT(-1, ret);
+
+  return 0;
+}
+
+DEF_TEST(plugin_config) {
+  oconfig_item_t test_cfg_parent = {"pcie_errors", NULL, 0, NULL, NULL, 0};
+  char value_buff[256] = "proc";
+  char key_buff[256] = "source";
+  oconfig_value_t test_cfg_value = {{value_buff}, OCONFIG_TYPE_STRING};
+  oconfig_item_t test_cfg = {
+      key_buff, &test_cfg_value, 1, &test_cfg_parent, NULL, 0};
+
+  test_cfg_parent.children = &test_cfg;
+  test_cfg_parent.children_num = 1;
+
+  pcie_config.use_sysfs = 1;
+  int ret = pcie_plugin_config(&test_cfg_parent);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(0, pcie_config.use_sysfs);
+
+  pcie_config.use_sysfs = 1;
+  sstrncpy(value_buff, "sysfs", sizeof(value_buff));
+  ret = pcie_plugin_config(&test_cfg_parent);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, pcie_config.use_sysfs);
+
+  sstrncpy(key_buff, "AccessDir", sizeof(key_buff));
+  sstrncpy(value_buff, "some/test/value", sizeof(value_buff));
+  ret = pcie_plugin_config(&test_cfg_parent);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("some/test/value", pcie_config.access_dir);
+
+  memset(&test_cfg_value.value, 0, sizeof(test_cfg_value.value));
+  test_cfg_value.value.boolean = 1;
+  test_cfg_value.type = OCONFIG_TYPE_BOOLEAN;
+  sstrncpy(key_buff, "ReportMasked", sizeof(key_buff));
+  ret = pcie_plugin_config(&test_cfg_parent);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, pcie_config.notif_masked);
+
+  sstrncpy(key_buff, "PersistentNotifications", sizeof(key_buff));
+  ret = pcie_plugin_config(&test_cfg_parent);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, pcie_config.persistent);
+
+  return 0;
+}
+
+#define BAD_TLP_SET_MSG "Correctable Error set: Bad TLP Status"
+#define BAD_TLP_CLEAR_MSG "Correctable Error cleared: Bad TLP Status"
+
+DEF_TEST(dispatch_correctable_errors) {
+  pcie_device_t dev = {0, TEST_DOMAIN, TEST_BUS, TEST_DEVICE, TEST_FUNCTION,
+                       0, 0,           0,        0,           0};
+  pcie_config.notif_masked = 0;
+  pcie_config.persistent = 0;
+
+  pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+                                   ~(PCI_ERR_COR_BAD_TLP));
+  EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+  EXPECT_EQ_STR(BAD_TLP_SET_MSG, last_notif.message);
+
+  memset(&last_notif, 0, sizeof(last_notif));
+  dev.correctable_errors = PCI_ERR_COR_BAD_TLP;
+  pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+                                   ~(PCI_ERR_COR_BAD_TLP));
+  EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+  pcie_config.persistent = 1;
+  pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+                                   ~(PCI_ERR_COR_BAD_TLP));
+  EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+  EXPECT_EQ_STR(BAD_TLP_SET_MSG, last_notif.message);
+
+  memset(&last_notif, 0, sizeof(last_notif));
+  pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+                                   PCI_ERR_COR_BAD_TLP);
+  EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+  pcie_config.notif_masked = 1;
+  pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+                                   PCI_ERR_COR_BAD_TLP);
+  EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+  EXPECT_EQ_STR(BAD_TLP_SET_MSG, last_notif.message);
+
+  pcie_config.persistent = 0;
+  memset(&last_notif, 0, sizeof(last_notif));
+  pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+                                   PCI_ERR_COR_BAD_TLP);
+  EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+  dev.correctable_errors = 0;
+  pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+                                   PCI_ERR_COR_BAD_TLP);
+  EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+  EXPECT_EQ_STR(BAD_TLP_SET_MSG, last_notif.message);
+
+  pcie_dispatch_correctable_errors(&dev, PCI_ERR_COR_BAD_TLP,
+                                   ~(PCI_ERR_COR_BAD_TLP));
+  EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+  EXPECT_EQ_STR(BAD_TLP_SET_MSG, last_notif.message);
+
+  pcie_config.notif_masked = 0;
+  dev.correctable_errors = PCI_ERR_COR_BAD_TLP;
+  pcie_dispatch_correctable_errors(&dev, 0, ~(PCI_ERR_COR_BAD_TLP));
+  EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_CE, last_notif.type_instance);
+  EXPECT_EQ_STR(BAD_TLP_CLEAR_MSG, last_notif.message);
+
+  return 0;
+}
+
+#define FCP_NF_SET_MSG                                                         \
+  "Uncorrectable(non_fatal) Error set: Flow Control Protocol"
+#define FCP_F_SET_MSG "Uncorrectable(fatal) Error set: Flow Control Protocol"
+#define FCP_NF_CLEAR_MSG                                                       \
+  "Uncorrectable(non_fatal) Error cleared: Flow Control Protocol"
+#define FCP_F_CLEAR_MSG                                                        \
+  "Uncorrectable(fatal) Error cleared: Flow Control Protocol"
+
+DEF_TEST(dispatch_uncorrectable_errors) {
+  pcie_device_t dev = {0, TEST_DOMAIN, TEST_BUS, TEST_DEVICE, TEST_FUNCTION,
+                       0, 0,           0,        0,           0};
+  pcie_config.notif_masked = 0;
+  pcie_config.persistent = 0;
+
+  pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, ~(PCI_ERR_UNC_FCP),
+                                     ~(PCI_ERR_UNC_FCP));
+  EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(FCP_NF_SET_MSG, last_notif.message);
+
+  pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, ~(PCI_ERR_UNC_FCP),
+                                     PCI_ERR_UNC_FCP);
+  EXPECT_EQ_INT(NOTIF_FAILURE, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(FCP_F_SET_MSG, last_notif.message);
+
+  memset(&last_notif, 0, sizeof(last_notif));
+  dev.uncorrectable_errors = PCI_ERR_UNC_FCP;
+  pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, ~(PCI_ERR_UNC_FCP),
+                                     PCI_ERR_UNC_FCP);
+  EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+  pcie_config.persistent = 1;
+  pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, ~(PCI_ERR_UNC_FCP),
+                                     PCI_ERR_UNC_FCP);
+  EXPECT_EQ_INT(NOTIF_FAILURE, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(FCP_F_SET_MSG, last_notif.message);
+
+  memset(&last_notif, 0, sizeof(last_notif));
+  pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, PCI_ERR_UNC_FCP,
+                                     PCI_ERR_UNC_FCP);
+  EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+  pcie_config.notif_masked = 1;
+  pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, PCI_ERR_UNC_FCP,
+                                     PCI_ERR_UNC_FCP);
+  EXPECT_EQ_INT(NOTIF_FAILURE, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(FCP_F_SET_MSG, last_notif.message);
+
+  pcie_config.persistent = 0;
+  dev.uncorrectable_errors = 0;
+  memset(&last_notif, 0, sizeof(last_notif));
+  pcie_dispatch_uncorrectable_errors(&dev, PCI_ERR_UNC_FCP, ~(PCI_ERR_UNC_FCP),
+                                     PCI_ERR_UNC_FCP);
+  EXPECT_EQ_INT(NOTIF_FAILURE, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(FCP_F_SET_MSG, last_notif.message);
+
+  pcie_config.notif_masked = 0;
+  dev.uncorrectable_errors = PCI_ERR_UNC_FCP;
+  pcie_dispatch_uncorrectable_errors(&dev, 0, ~(PCI_ERR_UNC_FCP),
+                                     ~(PCI_ERR_UNC_FCP));
+  EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(FCP_NF_CLEAR_MSG, last_notif.message);
+
+  memset(&last_notif, 0, sizeof(last_notif));
+  pcie_dispatch_uncorrectable_errors(&dev, 0, ~(PCI_ERR_UNC_FCP),
+                                     PCI_ERR_UNC_FCP);
+  EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(FCP_F_CLEAR_MSG, last_notif.message);
+
+  return 0;
+}
+
+#define UR_SET_MSG "Device Status Error set: Unsupported Request"
+#define UR_CLEAR_MSG "Device Status Error cleared: Unsupported Request"
+#define FE_SET_MSG "Device Status Error set: Fatal Error"
+#define FE_CLEAR_MSG "Device Status Error cleared: Fatal Error"
+
+DEF_TEST(device_status_errors) {
+  pcie_device_t dev = {0, TEST_DOMAIN, TEST_BUS, TEST_DEVICE, TEST_FUNCTION,
+                       0, 0,           0,        0,           0};
+  pcie_config.persistent = 0;
+  g_buff[0] = (PCI_EXP_DEVSTA_URD & 0xff);
+
+  memset(&last_notif, 0, sizeof(last_notif));
+  pcie_check_dev_status(&dev, 0);
+  EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(UR_SET_MSG, last_notif.message);
+
+  memset(&last_notif, 0, sizeof(last_notif));
+  pcie_check_dev_status(&dev, 0);
+  EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+  pcie_config.persistent = 1;
+  pcie_check_dev_status(&dev, 0);
+  EXPECT_EQ_INT(NOTIF_WARNING, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(UR_SET_MSG, last_notif.message);
+
+  g_buff[0] = 0;
+  pcie_check_dev_status(&dev, 0);
+  EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(UR_CLEAR_MSG, last_notif.message);
+
+  pcie_config.persistent = 0;
+  dev.device_status = PCI_EXP_DEVSTA_URD;
+  pcie_check_dev_status(&dev, 0);
+  EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_NOFATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(UR_CLEAR_MSG, last_notif.message);
+
+  memset(&last_notif, 0, sizeof(last_notif));
+  pcie_check_dev_status(&dev, 0);
+  EXPECT_EQ_STR("", last_notif.plugin_instance);
+
+  g_buff[0] = (PCI_EXP_DEVSTA_FED & 0xff);
+  pcie_check_dev_status(&dev, 0);
+  EXPECT_EQ_INT(NOTIF_FAILURE, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(FE_SET_MSG, last_notif.message);
+
+  g_buff[0] = 0;
+  pcie_check_dev_status(&dev, 0);
+  EXPECT_EQ_INT(NOTIF_OKAY, last_notif.severity);
+  EXPECT_EQ_STR(PCIE_ERRORS_PLUGIN, last_notif.plugin);
+  OK(NULL == last_notif.meta);
+  EXPECT_EQ_STR(TEST_DEVICE_STR, last_notif.plugin_instance);
+  EXPECT_EQ_STR(PCIE_ERROR, last_notif.type);
+  EXPECT_EQ_STR(PCIE_SEV_FATAL, last_notif.type_instance);
+  EXPECT_EQ_STR(FE_CLEAR_MSG, last_notif.message);
+
+  return 0;
+}
+
+int main(void) {
+  RUN_TEST(clear_dev_list);
+  RUN_TEST(add_to_list);
+  RUN_TEST(pcie_read);
+  RUN_TEST(dispatch_notification);
+
+  RUN_TEST(access_config);
+  RUN_TEST(plugin_config_fail);
+  RUN_TEST(plugin_config);
+
+  RUN_TEST(dispatch_correctable_errors);
+  RUN_TEST(dispatch_uncorrectable_errors);
+  RUN_TEST(device_status_errors);
+
+  END_TEST;
+}
index 8df8fd4..09e6e5a 100644 (file)
 /* do not automatically get the thread specific Perl interpreter */
 #define PERL_NO_GET_CONTEXT
 
-#define DONT_POISON_SPRINTF_YET 1
 #include "collectd.h"
-
-#undef DONT_POISON_SPRINTF_YET
-
 #include <stdbool.h>
 
 #include <EXTERN.h>
 #include <perl.h>
 
-#if defined(COLLECT_DEBUG) && COLLECT_DEBUG && defined(__GNUC__) && __GNUC__
-#undef sprintf
-#pragma GCC poison sprintf
-#endif
-
 #include <XSUB.h>
 
 /* Some versions of Perl define their own version of DEBUG... :-/ */
@@ -56,8 +47,8 @@
 #endif /* DEBUG */
 
 /* ... while we want the definition found in plugin.h. */
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include "filter_chain.h"
 
@@ -138,8 +129,8 @@ static int perl_flush(cdtime_t timeout, const char *identifier,
 typedef struct c_ithread_s {
   /* the thread's Perl interpreter */
   PerlInterpreter *interp;
-  _Bool running; /* thread is inside Perl interpreter */
-  _Bool shutdown;
+  bool running; /* thread is inside Perl interpreter */
+  bool shutdown;
   pthread_t pthread;
 
   /* double linked list of threads */
@@ -183,17 +174,17 @@ extern char **environ;
  * private variables
  */
 
-static _Bool register_legacy_flush = 1;
+static bool register_legacy_flush = true;
 
 /* if perl_threads != NULL perl_threads->head must
  * point to the "base" thread */
-static c_ithread_list_t *perl_threads = NULL;
+static c_ithread_list_t *perl_threads;
 
 /* the key used to store each pthread's ithread */
 static pthread_key_t perl_thr_key;
 
-static int perl_argc = 0;
-static char **perl_argv = NULL;
+static int perl_argc;
+static char **perl_argv;
 
 static char base_name[DATA_MAX_NAME_LEN] = "";
 
@@ -331,12 +322,12 @@ static size_t av2value(pTHX_ char *name, AV *array, value_t *value,
 
   if (array_len < ds->ds_num) {
     log_warn("av2value: array does not contain enough elements for type "
-             "\"%s\": got %zu, want %zu",
+             "\"%s\": got %" PRIsz ", want %" PRIsz,
              name, array_len, ds->ds_num);
     return 0;
   } else if (array_len > ds->ds_num) {
     log_warn("av2value: array contains excess elements for type \"%s\": got "
-             "%zu, want %zu",
+             "%" PRIsz ", want %" PRIsz,
              name, array_len, ds->ds_num);
   }
 
@@ -981,7 +972,7 @@ static int pplugin_dispatch_notification(pTHX_ HV *notif) {
  * Call perl sub with thread locking flags handled.
  */
 static int call_pv_locked(pTHX_ const char *sub_name) {
-  _Bool old_running;
+  bool old_running;
   int ret;
 
   c_ithread_t *t = (c_ithread_t *)pthread_getspecific(perl_thr_key);
@@ -989,7 +980,7 @@ static int call_pv_locked(pTHX_ const char *sub_name) {
     return 0;
 
   old_running = t->running;
-  t->running = 1;
+  t->running = true;
 
   if (t->shutdown) {
     t->running = old_running;
@@ -1189,7 +1180,7 @@ static void c_ithread_destroy(c_ithread_t *ithread) {
   /* Mark as running to avoid deadlock:
      c_ithread_destroy -> log_debug -> perl_log()
   */
-  ithread->running = 1;
+  ithread->running = true;
   log_debug("Shutting down Perl interpreter %p...", aTHX);
 
 #if COLLECT_DEBUG
@@ -1275,8 +1266,8 @@ static c_ithread_t *c_ithread_create(PerlInterpreter *base) {
   }
 
   t->pthread = pthread_self();
-  t->running = 0;
-  t->shutdown = 0;
+  t->running = false;
+  t->shutdown = false;
   perl_threads->tail = t;
 
   pthread_setspecific(perl_thr_key, (const void *)t);
@@ -2274,7 +2265,7 @@ static int perl_shutdown(void) {
      * the thread as this will free the memory */
     t = t->prev;
 
-    thr->shutdown = 1;
+    thr->shutdown = true;
     if (thr->running) {
       /* Give some time to thread to exit from Perl interpreter */
       WARNING("perl shutdown: Thread is running inside Perl. Waiting.");
index 82adc53..9681d36 100644 (file)
--- a/src/pf.c
+++ b/src/pf.c
@@ -21,8 +21,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
@@ -58,7 +58,7 @@ static char const *pf_scounters[SCNT_MAX + 1] = SCNT_NAMES;
 static char const *pf_device = "/dev/pf";
 
 static void pf_submit(char const *type, char const *type_instance, uint64_t val,
-                      _Bool is_gauge) {
+                      bool is_gauge) {
   value_t values[1];
   value_list_t vl = VALUE_LIST_INIT;
 
@@ -83,17 +83,13 @@ static int pf_read(void) {
 
   fd = open(pf_device, O_RDONLY);
   if (fd < 0) {
-    char errbuf[1024];
-    ERROR("pf plugin: Unable to open %s: %s", pf_device,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("pf plugin: Unable to open %s: %s", pf_device, STRERRNO);
     return -1;
   }
 
   status = ioctl(fd, DIOCGETSTATUS, &state);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("pf plugin: ioctl(DIOCGETSTATUS) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("pf plugin: ioctl(DIOCGETSTATUS) failed: %s", STRERRNO);
     close(fd);
     return -1;
   }
@@ -107,19 +103,19 @@ static int pf_read(void) {
 
   for (int i = 0; i < PFRES_MAX; i++)
     pf_submit("pf_counters", pf_reasons[i], state.counters[i],
-              /* is gauge = */ 0);
+              /* is gauge = */ false);
   for (int i = 0; i < LCNT_MAX; i++)
     pf_submit("pf_limits", pf_lcounters[i], state.lcounters[i],
-              /* is gauge = */ 0);
+              /* is gauge = */ false);
   for (int i = 0; i < FCNT_MAX; i++)
     pf_submit("pf_state", pf_fcounters[i], state.fcounters[i],
-              /* is gauge = */ 0);
+              /* is gauge = */ false);
   for (int i = 0; i < SCNT_MAX; i++)
     pf_submit("pf_source", pf_scounters[i], state.scounters[i],
-              /* is gauge = */ 0);
+              /* is gauge = */ false);
 
   pf_submit("pf_states", "current", (uint32_t)state.states,
-            /* is gauge = */ 1);
+            /* is gauge = */ true);
 
   return 0;
 } /* int pf_read */
index 57ccc7a..61d226c 100644 (file)
@@ -25,8 +25,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <netdb.h>
 #include <poll.h>
@@ -99,15 +99,15 @@ typedef struct pinba_statnode_s pinba_statnode_t;
  * Module global variables
  */
 /* {{{ */
-static pinba_statnode_t *stat_nodes = NULL;
-static unsigned int stat_nodes_num = 0;
+static pinba_statnode_t *stat_nodes;
+static unsigned int stat_nodes_num;
 static pthread_mutex_t stat_nodes_lock;
 
-static char *conf_node = NULL;
-static char *conf_service = NULL;
+static char *conf_node;
+static char *conf_service;
 
-static _Bool collector_thread_running = 0;
-static _Bool collector_thread_do_shutdown = 0;
+static bool collector_thread_running;
+static bool collector_thread_do_shutdown;
 static pthread_t collector_thread_id;
 /* }}} */
 
@@ -280,9 +280,6 @@ static int pb_del_socket(pinba_socket_t *s, /* {{{ */
 
 static int pb_add_socket(pinba_socket_t *s, /* {{{ */
                          const struct addrinfo *ai) {
-  int fd;
-  int tmp;
-  int status;
 
   if (s->fd_num == PINBA_MAX_SOCKETS) {
     WARNING("pinba plugin: Sorry, you have hit the built-in limit of "
@@ -292,27 +289,20 @@ static int pb_add_socket(pinba_socket_t *s, /* {{{ */
     return -1;
   }
 
-  fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+  int fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
   if (fd < 0) {
-    char errbuf[1024];
-    ERROR("pinba plugin: socket(2) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("pinba plugin: socket(2) failed: %s", STRERRNO);
     return 0;
   }
 
-  tmp = 1;
-  status = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
+  int status = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
   if (status != 0) {
-    char errbuf[1024];
-    WARNING("pinba plugin: setsockopt(SO_REUSEADDR) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("pinba plugin: setsockopt(SO_REUSEADDR) failed: %s", STRERRNO);
   }
 
   status = bind(fd, ai->ai_addr, ai->ai_addrlen);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("pinba plugin: bind(2) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("pinba plugin: bind(2) failed: %s", STRERRNO);
     close(fd);
     return 0;
   }
@@ -414,7 +404,6 @@ static int pinba_udp_read_callback_fn(int sock) /* {{{ */
     status = recvfrom(sock, buffer, buffer_size - 1, MSG_DONTWAIT,
                       /* from = */ NULL, /* from len = */ 0);
     if (status < 0) {
-      char errbuf[1024];
 
       if ((errno == EINTR)
 #ifdef EWOULDBLOCK
@@ -424,8 +413,7 @@ static int pinba_udp_read_callback_fn(int sock) /* {{{ */
         continue;
       }
 
-      WARNING("pinba plugin: recvfrom(2) failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("pinba plugin: recvfrom(2) failed: %s", STRERRNO);
       return -1;
     } else if (status == 0) {
       DEBUG("pinba plugin: recvfrom(2) returned unexpected status zero.");
@@ -469,13 +457,10 @@ static int receive_loop(void) /* {{{ */
     {
       continue;
     } else if (status < 0) {
-      char errbuf[1024];
-
       if ((errno == EINTR) || (errno == EAGAIN))
         continue;
 
-      ERROR("pinba plugin: poll(2) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("pinba plugin: poll(2) failed: %s", STRERRNO);
       pinba_socket_free(s);
       return -1;
     }
@@ -501,7 +486,7 @@ static void *collector_thread(void *arg) /* {{{ */
   receive_loop();
 
   memset(&collector_thread_id, 0, sizeof(collector_thread_id));
-  collector_thread_running = 0;
+  collector_thread_running = false;
   pthread_exit(NULL);
   return NULL;
 } /* }}} void *collector_thread */
@@ -593,12 +578,10 @@ static int plugin_init(void) /* {{{ */
                                 /* attrs = */ NULL, collector_thread,
                                 /* args = */ NULL, "pinba collector");
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("pinba plugin: pthread_create(3) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("pinba plugin: pthread_create(3) failed: %s", STRERRNO);
     return -1;
   }
-  collector_thread_running = 1;
+  collector_thread_running = true;
 
   return 0;
 } /* }}} */
@@ -609,17 +592,15 @@ static int plugin_shutdown(void) /* {{{ */
     int status;
 
     DEBUG("pinba plugin: Shutting down collector thread.");
-    collector_thread_do_shutdown = 1;
+    collector_thread_do_shutdown = true;
 
     status = pthread_join(collector_thread_id, /* retval = */ NULL);
     if (status != 0) {
-      char errbuf[1024];
-      ERROR("pinba plugin: pthread_join(3) failed: %s",
-            sstrerror(status, errbuf, sizeof(errbuf)));
+      ERROR("pinba plugin: pthread_join(3) failed: %s", STRERROR(status));
     }
 
-    collector_thread_running = 0;
-    collector_thread_do_shutdown = 0;
+    collector_thread_running = false;
+    collector_thread_do_shutdown = false;
   } /* if (collector_thread_running) */
 
   return 0;
index 8cefeed..203d223 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_complain.h"
 
 #include <netinet/in.h>
@@ -69,13 +69,14 @@ typedef struct hostlist_s hostlist_t;
 /*
  * Private variables
  */
-static hostlist_t *hostlist_head = NULL;
+static hostlist_t *hostlist_head;
 
-static char *ping_source = NULL;
+static int ping_af = PING_DEF_AF;
+static char *ping_source;
 #ifdef HAVE_OPING_1_3
-static char *ping_device = NULL;
+static char *ping_device;
 #endif
-static char *ping_data = NULL;
+static char *ping_data;
 static int ping_ttl = PING_DEF_TTL;
 static double ping_interval = 1.0;
 static double ping_timeout = 0.9;
@@ -83,11 +84,11 @@ static int ping_max_missed = -1;
 
 static pthread_mutex_t ping_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t ping_cond = PTHREAD_COND_INITIALIZER;
-static int ping_thread_loop = 0;
-static int ping_thread_error = 0;
+static int ping_thread_loop;
+static int ping_thread_error;
 static pthread_t ping_thread_id;
 
-static const char *config_keys[] = {"Host",    "SourceAddress",
+static const char *config_keys[] = {"Host",    "SourceAddress", "AddressFamily",
 #ifdef HAVE_OPING_1_3
                                     "Device",
 #endif
@@ -242,6 +243,12 @@ static void *ping_thread(void *arg) /* {{{ */
     return (void *)-1;
   }
 
+  if (ping_af != PING_DEF_AF) {
+    if (ping_setopt(pingobj, PING_OPT_AF, &ping_af) != 0)
+      ERROR("ping plugin: Failed to set address family: %s",
+            ping_get_error(pingobj));
+  }
+
   if (ping_source != NULL)
     if (ping_setopt(pingobj, PING_OPT_SOURCE, (void *)ping_source) != 0)
       ERROR("ping plugin: Failed to set source address: %s",
@@ -291,12 +298,10 @@ static void *ping_thread(void *arg) /* {{{ */
 
   pthread_mutex_lock(&ping_lock);
   while (ping_thread_loop > 0) {
-    _Bool send_successful = 0;
+    bool send_successful = false;
 
     if (gettimeofday(&tv_begin, NULL) < 0) {
-      char errbuf[1024];
-      ERROR("ping plugin: gettimeofday failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("ping plugin: gettimeofday failed: %s", STRERRNO);
       ping_thread_error = 1;
       break;
     }
@@ -309,7 +314,7 @@ static void *ping_thread(void *arg) /* {{{ */
                  ping_get_error(pingobj));
     } else {
       c_release(LOG_NOTICE, &complaint, "ping plugin: ping_send succeeded.");
-      send_successful = 1;
+      send_successful = true;
     }
 
     pthread_mutex_lock(&ping_lock);
@@ -321,9 +326,7 @@ static void *ping_thread(void *arg) /* {{{ */
       (void)ping_dispatch_all(pingobj);
 
     if (gettimeofday(&tv_end, NULL) < 0) {
-      char errbuf[1024];
-      ERROR("ping plugin: gettimeofday failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("ping plugin: gettimeofday failed: %s", STRERRNO);
       ping_thread_error = 1;
       break;
     }
@@ -434,9 +437,8 @@ static int config_set_string(const char *name, /* {{{ */
 
   tmp = strdup(value);
   if (tmp == NULL) {
-    char errbuf[1024];
     ERROR("ping plugin: Setting `%s' to `%s' failed: strdup failed: %s", name,
-          value, sstrerror(errno, errbuf, sizeof(errbuf)));
+          value, STRERRNO);
     return 1;
   }
 
@@ -454,18 +456,14 @@ static int ping_config(const char *key, const char *value) /* {{{ */
 
     hl = malloc(sizeof(*hl));
     if (hl == NULL) {
-      char errbuf[1024];
-      ERROR("ping plugin: malloc failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("ping plugin: malloc failed: %s", STRERRNO);
       return 1;
     }
 
     host = strdup(value);
     if (host == NULL) {
-      char errbuf[1024];
       sfree(hl);
-      ERROR("ping plugin: strdup failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("ping plugin: strdup failed: %s", STRERRNO);
       return 1;
     }
 
@@ -477,6 +475,23 @@ static int ping_config(const char *key, const char *value) /* {{{ */
     hl->latency_squared = 0.0;
     hl->next = hostlist_head;
     hostlist_head = hl;
+  } else if (strcasecmp(key, "AddressFamily") == 0) {
+    char *af = NULL;
+    int status = config_set_string(key, &af, value);
+    if (status != 0)
+      return status;
+
+    if (strncmp(af, "any", 3) == 0) {
+      ping_af = AF_UNSPEC;
+    } else if (strncmp(af, "ipv4", 4) == 0) {
+      ping_af = AF_INET;
+    } else if (strncmp(af, "ipv6", 4) == 0) {
+      ping_af = AF_INET6;
+    } else {
+      WARNING("ping plugin: Ignoring invalid AddressFamily value %s", af);
+    }
+    free(af);
+
   } else if (strcasecmp(key, "SourceAddress") == 0) {
     int status = config_set_string(key, &ping_source, value);
     if (status != 0)
@@ -530,7 +545,7 @@ static int ping_config(const char *key, const char *value) /* {{{ */
       } /* }}} for (i = 0; i < size; i++) */
       ping_data[size] = 0;
     } else
-      WARNING("ping plugin: Ignoring invalid Size %zu.", size);
+      WARNING("ping plugin: Ignoring invalid Size %" PRIsz ".", size);
   } else if (strcasecmp(key, "Timeout") == 0) {
     double tmp;
 
index 25bedf8..8e4328f 100644 (file)
 
 #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>
@@ -102,7 +102,7 @@ typedef struct {
 typedef struct {
   char *name;
   char *statement;
-  _Bool store_rates;
+  bool store_rates;
 } c_psql_writer_t;
 
 typedef struct {
@@ -125,8 +125,6 @@ typedef struct {
   /* make sure we don't access the database object in parallel */
   pthread_mutex_t db_lock;
 
-  cdtime_t interval;
-
   /* writer "caching" settings */
   cdtime_t commit_interval;
   cdtime_t next_commit;
@@ -155,14 +153,14 @@ static const char *const def_queries[] = {
     "table_states", "disk_io",      "disk_usage"};
 static int def_queries_num = STATIC_ARRAY_SIZE(def_queries);
 
-static c_psql_database_t **databases = NULL;
-static size_t databases_num = 0;
+static c_psql_database_t **databases;
+static size_t databases_num;
 
-static udb_query_t **queries = NULL;
-static size_t queries_num = 0;
+static udb_query_t **queries;
+static size_t queries_num;
 
-static c_psql_writer_t *writers = NULL;
-static size_t writers_num = 0;
+static c_psql_writer_t *writers;
+static size_t writers_num;
 
 static int c_psql_begin(c_psql_database_t *db) {
   PGresult *r = PQexec(db->conn, "BEGIN");
@@ -237,8 +235,6 @@ static c_psql_database_t *c_psql_database_new(const char *name) {
 
   pthread_mutex_init(&db->db_lock, /* attrs = */ NULL);
 
-  db->interval = 0;
-
   db->commit_interval = 0;
   db->next_commit = 0;
   db->expire_delay = 0;
@@ -348,10 +344,10 @@ static int c_psql_connect(c_psql_database_t *db) {
 } /* c_psql_connect */
 
 static int c_psql_check_connection(c_psql_database_t *db) {
-  _Bool init = 0;
+  bool init = false;
 
   if (!db->conn) {
-    init = 1;
+    init = true;
 
     /* trigger c_release() */
     if (0 == db->conn_complaint.interval)
@@ -431,8 +427,7 @@ static PGresult *c_psql_exec_query_params(c_psql_database_t *db, udb_query_t *q,
       break;
     case C_PSQL_PARAM_INTERVAL:
       snprintf(interval, sizeof(interval), "%.3f",
-               (db->interval > 0) ? CDTIME_T_TO_DOUBLE(db->interval)
-                                  : plugin_get_interval());
+               CDTIME_T_TO_DOUBLE(plugin_get_interval()));
       params[i] = interval;
       break;
     case C_PSQL_PARAM_INSTANCE:
@@ -516,14 +511,14 @@ static int c_psql_exec_query(c_psql_database_t *db, udb_query_t *q,
   }
 
   column_num = PQnfields(res);
-  column_names = (char **)calloc(column_num, sizeof(char *));
-  if (NULL == column_names) {
+  column_names = calloc(column_num, sizeof(*column_names));
+  if (column_names == NULL) {
     log_err("calloc failed.");
     BAIL_OUT(-1);
   }
 
-  column_values = (char **)calloc(column_num, sizeof(char *));
-  if (NULL == column_values) {
+  column_values = calloc(column_num, sizeof(*column_values));
+  if (column_values == NULL) {
     log_err("calloc failed.");
     BAIL_OUT(-1);
   }
@@ -548,7 +543,7 @@ static int c_psql_exec_query(c_psql_database_t *db, udb_query_t *q,
   status = udb_query_prepare_result(
       q, prep_area, host,
       (db->plugin_name != NULL) ? db->plugin_name : "postgresql", db->instance,
-      column_names, (size_t)column_num, db->interval);
+      column_names, (size_t)column_num);
 
   if (0 != status) {
     log_err("udb_query_prepare_result failed with status %i.", status);
@@ -664,7 +659,7 @@ static char *values_name_to_sqlarray(const data_set_t *ds, char *string,
 } /* values_name_to_sqlarray */
 
 static char *values_type_to_sqlarray(const data_set_t *ds, char *string,
-                                     size_t string_len, _Bool store_rates) {
+                                     size_t string_len, bool store_rates) {
   char *str_ptr;
   size_t str_len;
 
@@ -707,7 +702,7 @@ static char *values_type_to_sqlarray(const data_set_t *ds, char *string,
 
 static char *values_to_sqlarray(const data_set_t *ds, const value_list_t *vl,
                                 char *string, size_t string_len,
-                                _Bool store_rates) {
+                                bool store_rates) {
   char *str_ptr;
   size_t str_len;
 
@@ -742,7 +737,8 @@ static char *values_to_sqlarray(const data_set_t *ds, const value_list_t *vl,
 
       status = snprintf(str_ptr, str_len, ",%lf", rates[i]);
     } else if (ds->ds[i].type == DS_TYPE_COUNTER)
-      status = snprintf(str_ptr, str_len, ",%llu", vl->values[i].counter);
+      status = snprintf(str_ptr, str_len, ",%" PRIu64,
+                        (uint64_t)vl->values[i].counter);
     else if (ds->ds[i].type == DS_TYPE_DERIVE)
       status = snprintf(str_ptr, str_len, ",%" PRIi64, vl->values[i].derive);
     else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
@@ -935,7 +931,7 @@ static int c_psql_flush(cdtime_t timeout,
 } /* c_psql_flush */
 
 static int c_psql_shutdown(void) {
-  _Bool had_flush = 0;
+  bool had_flush = false;
 
   plugin_unregister_read_group("postgresql");
 
@@ -948,7 +944,7 @@ static int c_psql_shutdown(void) {
 
       if (!had_flush) {
         plugin_unregister_flush("postgresql");
-        had_flush = 1;
+        had_flush = true;
       }
 
       plugin_unregister_flush(cb_name);
@@ -980,9 +976,9 @@ static int config_query_param_add(udb_query_t *q, oconfig_item_t *ci) {
   c_psql_param_t *tmp;
 
   data = udb_query_get_user_data(q);
-  if (NULL == data) {
+  if (data == NULL) {
     data = calloc(1, sizeof(*data));
-    if (NULL == data) {
+    if (data == NULL) {
       log_err("Out of memory.");
       return -1;
     }
@@ -993,7 +989,7 @@ static int config_query_param_add(udb_query_t *q, oconfig_item_t *ci) {
   }
 
   tmp = realloc(data->params, (data->params_num + 1) * sizeof(*data->params));
-  if (NULL == tmp) {
+  if (tmp == NULL) {
     log_err("Out of memory.");
     return -1;
   }
@@ -1096,7 +1092,7 @@ static int c_psql_config_writer(oconfig_item_t *ci) {
 
   writer->name = sstrdup(ci->values[0].value.string);
   writer->statement = NULL;
-  writer->store_rates = 1;
+  writer->store_rates = true;
 
   for (int i = 0; i < ci->children_num; ++i) {
     oconfig_item_t *c = ci->children + i;
@@ -1122,8 +1118,9 @@ static int c_psql_config_writer(oconfig_item_t *ci) {
 static int c_psql_config_database(oconfig_item_t *ci) {
   c_psql_database_t *db;
 
+  cdtime_t interval = 0;
   char cb_name[DATA_MAX_NAME_LEN];
-  static _Bool have_flush = 0;
+  static bool have_flush;
 
   if ((1 != ci->values_num) || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
     log_err("<Database> expects a single string argument.");
@@ -1162,7 +1159,7 @@ static int c_psql_config_database(oconfig_item_t *ci) {
       config_add_writer(c, writers, writers_num, &db->writers,
                         &db->writers_num);
     else if (0 == strcasecmp(c->key, "Interval"))
-      cf_util_get_cdtime(c, &db->interval);
+      cf_util_get_cdtime(c, &interval);
     else if (strcasecmp("CommitInterval", c->key) == 0)
       cf_util_get_cdtime(c, &db->commit_interval);
     else if (strcasecmp("ExpireDelay", c->key) == 0)
@@ -1179,9 +1176,7 @@ static int c_psql_config_database(oconfig_item_t *ci) {
   }
 
   if (db->queries_num > 0) {
-    db->q_prep_areas = (udb_query_preparation_area_t **)calloc(
-        db->queries_num, sizeof(*db->q_prep_areas));
-
+    db->q_prep_areas = calloc(db->queries_num, sizeof(*db->q_prep_areas));
     if (db->q_prep_areas == NULL) {
       log_err("Out of memory.");
       c_psql_database_delete(db);
@@ -1210,8 +1205,8 @@ static int c_psql_config_database(oconfig_item_t *ci) {
 
   if (db->queries_num > 0) {
     ++db->ref_cnt;
-    plugin_register_complex_read("postgresql", cb_name, c_psql_read,
-                                 /* interval = */ db->interval, &ud);
+    plugin_register_complex_read("postgresql", cb_name, c_psql_read, interval,
+                                 &ud);
   }
   if (db->writers_num > 0) {
     ++db->ref_cnt;
@@ -1220,7 +1215,7 @@ static int c_psql_config_database(oconfig_item_t *ci) {
     if (!have_flush) {
       /* flush all */
       plugin_register_flush("postgresql", c_psql_flush, /* user data = */ NULL);
-      have_flush = 1;
+      have_flush = true;
     }
 
     /* flush this connection only */
@@ -1236,7 +1231,7 @@ static int c_psql_config_database(oconfig_item_t *ci) {
 } /* c_psql_config_database */
 
 static int c_psql_config(oconfig_item_t *ci) {
-  static int have_def_config = 0;
+  static int have_def_config;
 
   if (0 == have_def_config) {
     oconfig_item_t *c;
index 74e671b..a5b45a1 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_llist.h"
 
 #include <errno.h>
 #endif
 #define FUNC_ERROR(func)                                                       \
   do {                                                                         \
-    char errbuf[1024];                                                         \
-    ERROR("powerdns plugin: %s failed: %s", func,                              \
-          sstrerror(errno, errbuf, sizeof(errbuf)));                           \
+    ERROR("powerdns plugin: %s failed: %s", func, STRERRNO);                   \
   } while (0)
 #define SOCK_ERROR(func, sockpath)                                             \
   do {                                                                         \
-    char errbuf[1024];                                                         \
     ERROR("powerdns plugin: Socket `%s` %s failed: %s", sockpath, func,        \
-          sstrerror(errno, errbuf, sizeof(errbuf)));                           \
+          STRERRNO);                                                           \
   } while (0)
 
 #define SERVER_SOCKET LOCALSTATEDIR "/run/pdns.controlsocket"
@@ -307,10 +304,10 @@ static statname_lookup_t lookup_table[] = /* {{{ */
         {"uptime", "uptime", NULL}}; /* }}} */
 static int lookup_table_length = STATIC_ARRAY_SIZE(lookup_table);
 
-static llist_t *list = NULL;
+static llist_t *list;
 
 #define PDNS_LOCAL_SOCKPATH LOCALSTATEDIR "/run/" PACKAGE_NAME "-powerdns"
-static char *local_sockpath = NULL;
+static char *local_sockpath;
 
 /* TODO: Do this before 4.4:
  * - Update the collectd.conf(5) manpage.
@@ -355,16 +352,13 @@ static void submit(const char *plugin_instance, /* {{{ */
   }
 
   if (ds->ds_num != 1) {
-    ERROR("powerdns plugin: type `%s' has %zu data sources, "
+    ERROR("powerdns plugin: type `%s' has %" PRIsz " data sources, "
           "but I can only handle one.",
           type, ds->ds_num);
     return;
   }
 
   if (0 != parse_value(value_str, &value, ds->ds[0].type)) {
-    ERROR("powerdns plugin: Cannot convert `%s' "
-          "to a number.",
-          value_str);
     return;
   }
 
@@ -475,7 +469,7 @@ static int powerdns_get_data_dgram(list_item_t *item, char **ret_buffer) {
   }
 
   memcpy(buffer, temp, buffer_size - 1);
-  buffer[buffer_size - 1] = 0;
+  buffer[buffer_size - 1] = '\0';
 
   *ret_buffer = buffer;
   return 0;
index f556912..ac5ec60 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/processes.c
  * Copyright (C) 2005       Lyonel Vincent
- * Copyright (C) 2006-2010  Florian octo Forster
+ * Copyright (C) 2006-2017  Florian octo Forster
  * Copyright (C) 2008       Oleg King
  * Copyright (C) 2009       Sebastian Harl
  * Copyright (C) 2009       Andrés J. Díaz
 
 #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"
+#endif
 
 /* Include header files for the mach system, if they exist.. */
 #if HAVE_THREAD_INFO
 #include <kstat.h>
 #endif
 
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
 #ifndef CMDLINE_BUFFER_SIZE
 #if defined(ARG_MAX) && (ARG_MAX < 4096)
 #define CMDLINE_BUFFER_SIZE ARG_MAX
@@ -189,20 +198,25 @@ typedef struct process_entry_s {
   derive_t io_syscw;
   derive_t io_diskr;
   derive_t io_diskw;
-  _Bool has_io;
+  bool has_io;
 
   derive_t cswitch_vol;
   derive_t cswitch_invol;
-  _Bool has_cswitch;
+  bool has_cswitch;
 
-  _Bool has_fd;
+#if HAVE_LIBTASKSTATS
+  ts_delay_t delay;
+#endif
+  bool has_delay;
+
+  bool has_fd;
 
-  _Bool has_maps;
+  bool has_maps;
 } process_entry_t;
 
 typedef struct procstat_entry_s {
   unsigned long id;
-  unsigned long age;
+  unsigned char age;
 
   derive_t vmem_minflt_counter;
   derive_t vmem_majflt_counter;
@@ -221,6 +235,13 @@ typedef struct procstat_entry_s {
   derive_t cswitch_vol;
   derive_t cswitch_invol;
 
+#if HAVE_LIBTASKSTATS
+  value_to_rate_state_t delay_cpu;
+  value_to_rate_state_t delay_blkio;
+  value_to_rate_state_t delay_swapin;
+  value_to_rate_state_t delay_freepages;
+#endif
+
   struct procstat_entry_s *next;
 } procstat_entry_t;
 
@@ -257,20 +278,28 @@ typedef struct procstat {
   derive_t cswitch_vol;
   derive_t cswitch_invol;
 
-  _Bool report_fd_num;
-  _Bool report_maps_num;
-  _Bool report_ctx_switch;
+  /* Linux Delay Accounting. Unit is ns/s. */
+  gauge_t delay_cpu;
+  gauge_t delay_blkio;
+  gauge_t delay_swapin;
+  gauge_t delay_freepages;
+
+  bool report_fd_num;
+  bool report_maps_num;
+  bool report_ctx_switch;
+  bool report_delay;
 
   struct procstat *next;
   struct procstat_entry_s *instances;
 } procstat_t;
 
-static procstat_t *list_head_g = NULL;
+static procstat_t *list_head_g;
 
-static _Bool want_init = 1;
-static _Bool report_ctx_switch = 0;
-static _Bool report_fd_num = 0;
-static _Bool report_maps_num = 0;
+static bool want_init = true;
+static bool report_ctx_switch;
+static bool report_fd_num;
+static bool report_maps_num;
+static bool report_delay;
 
 #if HAVE_THREAD_INFO
 static mach_port_t port_host_self;
@@ -304,6 +333,10 @@ int getthrds64(pid_t, void *, int, tid64_t *, int);
 int getargs(void *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
 #endif /* HAVE_PROCINFO_H */
 
+#if HAVE_LIBTASKSTATS
+static ts_t *taskstats_handle;
+#endif
+
 /* put name of process from config to list_head_g tree
  * list_head_g is a list of 'procstat_t' structs with
  * processes names we want to watch */
@@ -331,6 +364,7 @@ static procstat_t *ps_list_register(const char *name, const char *regexp) {
   new->report_fd_num = report_fd_num;
   new->report_maps_num = report_maps_num;
   new->report_ctx_switch = report_ctx_switch;
+  new->report_delay = report_delay;
 
 #if HAVE_REGEX_H
   if (regexp != NULL) {
@@ -439,6 +473,39 @@ static void ps_update_counter(derive_t *group_counter, derive_t *curr_counter,
   *group_counter += curr_value;
 }
 
+#if HAVE_LIBTASKSTATS
+static void ps_update_delay_one(gauge_t *out_rate_sum,
+                                value_to_rate_state_t *state, uint64_t cnt,
+                                cdtime_t t) {
+  gauge_t rate = NAN;
+  int status = value_to_rate(&rate, (value_t){.counter = (counter_t)cnt},
+                             DS_TYPE_COUNTER, t, state);
+  if ((status != 0) || isnan(rate)) {
+    return;
+  }
+
+  if (isnan(*out_rate_sum)) {
+    *out_rate_sum = rate;
+  } else {
+    *out_rate_sum += rate;
+  }
+}
+
+static void ps_update_delay(procstat_t *out, procstat_entry_t *prev,
+                            process_entry_t *curr) {
+  cdtime_t now = cdtime();
+
+  ps_update_delay_one(&out->delay_cpu, &prev->delay_cpu, curr->delay.cpu_ns,
+                      now);
+  ps_update_delay_one(&out->delay_blkio, &prev->delay_blkio,
+                      curr->delay.blkio_ns, now);
+  ps_update_delay_one(&out->delay_swapin, &prev->delay_swapin,
+                      curr->delay.swapin_ns, now);
+  ps_update_delay_one(&out->delay_freepages, &prev->delay_freepages,
+                      curr->delay.freepages_ns, now);
+}
+#endif
+
 /* add process entry to 'instances' of process 'name' (or refresh it) */
 static void ps_list_add(const char *name, const char *cmdline,
                         process_entry_t *entry) {
@@ -518,6 +585,11 @@ static void ps_list_add(const char *name, const char *cmdline,
                       entry->cpu_user_counter);
     ps_update_counter(&ps->cpu_system_counter, &pse->cpu_system_counter,
                       entry->cpu_system_counter);
+
+#if HAVE_LIBTASKSTATS
+    if (entry->has_delay)
+      ps_update_delay(ps, pse, entry);
+#endif
   }
 }
 
@@ -537,10 +609,15 @@ static void ps_list_reset(void) {
     ps->vmem_code = 0;
     ps->stack_size = 0;
 
+    ps->delay_cpu = NAN;
+    ps->delay_blkio = NAN;
+    ps->delay_swapin = NAN;
+    ps->delay_freepages = NAN;
+
     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);
@@ -555,7 +632,7 @@ static void ps_list_reset(void) {
           pse = pse_prev->next;
         }
       } else {
-        pse->age++;
+        pse->age = 1;
         pse_prev = pse;
         pse = pse->next;
       }
@@ -573,8 +650,15 @@ static void ps_tune_instance(oconfig_item_t *ci, procstat_t *ps) {
       cf_util_get_boolean(c, &ps->report_fd_num);
     else if (strcasecmp(c->key, "CollectMemoryMaps") == 0)
       cf_util_get_boolean(c, &ps->report_maps_num);
-    else {
-      ERROR("processes plugin: Option `%s' not allowed here.", c->key);
+    else if (strcasecmp(c->key, "CollectDelayAccounting") == 0) {
+#if HAVE_LIBTASKSTATS
+      cf_util_get_boolean(c, &ps->report_delay);
+#else
+      WARNING("processes plugin: The plugin has been compiled without support "
+              "for the \"CollectDelayAccounting\" option.");
+#endif
+    } else {
+      ERROR("processes plugin: Option \"%s\" not allowed here.", c->key);
     }
   } /* for (ci->children) */
 } /* void ps_tune_instance */
@@ -602,7 +686,8 @@ static int ps_config(oconfig_item_t *ci) {
 
 #if KERNEL_LINUX || KERNEL_SOLARIS || KERNEL_FREEBSD
       if (strlen(c->values[0].value.string) > max_procname_len) {
-        WARNING("processes plugin: this platform has a %zu character limit "
+        WARNING("processes plugin: this platform has a %" PRIsz
+                " character limit "
                 "to process names. The `Process \"%s\"' option will "
                 "not work as expected.",
                 max_procname_len, c->values[0].value.string);
@@ -633,6 +718,13 @@ static int ps_config(oconfig_item_t *ci) {
       cf_util_get_boolean(c, &report_fd_num);
     } else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) {
       cf_util_get_boolean(c, &report_maps_num);
+    } else if (strcasecmp(c->key, "CollectDelayAccounting") == 0) {
+#if HAVE_LIBTASKSTATS
+      cf_util_get_boolean(c, &report_delay);
+#else
+      WARNING("processes plugin: The plugin has been compiled without support "
+              "for the \"CollectDelayAccounting\" option.");
+#endif
     } else {
       ERROR("processes plugin: The `%s' configuration option is not "
             "understood and will be ignored.",
@@ -670,6 +762,15 @@ static int ps_init(void) {
 #elif KERNEL_LINUX
   pagesize_g = sysconf(_SC_PAGESIZE);
   DEBUG("pagesize_g = %li; CONFIG_HZ = %i;", pagesize_g, CONFIG_HZ);
+
+#if HAVE_LIBTASKSTATS
+  if (taskstats_handle == NULL) {
+    taskstats_handle = ts_create();
+    if (taskstats_handle == NULL) {
+      WARNING("processes plugin: Creating taskstats handle failed.");
+    }
+  }
+#endif
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS &&                                                  \
@@ -804,6 +905,31 @@ static void ps_submit_proc_list(procstat_t *ps) {
     plugin_dispatch_values(&vl);
   }
 
+  /* The ps->delay_* metrics are in nanoseconds per second. Convert to seconds
+   * per second. */
+  gauge_t const delay_factor = 1000000000.0;
+
+  struct {
+    const char *type_instance;
+    gauge_t rate_ns;
+  } delay_metrics[] = {
+      {"delay-cpu", ps->delay_cpu},
+      {"delay-blkio", ps->delay_blkio},
+      {"delay-swapin", ps->delay_swapin},
+      {"delay-freepages", ps->delay_freepages},
+  };
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(delay_metrics); i++) {
+    if (isnan(delay_metrics[i].rate_ns)) {
+      continue;
+    }
+    sstrncpy(vl.type, "delay_rate", sizeof(vl.type));
+    sstrncpy(vl.type_instance, delay_metrics[i].type_instance,
+             sizeof(vl.type_instance));
+    vl.values[0].gauge = delay_metrics[i].rate_ns / delay_factor;
+    vl.values_len = 1;
+    plugin_dispatch_values(&vl);
+  }
+
   DEBUG(
       "name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; num_maps = %lu; "
       "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
@@ -813,13 +939,16 @@ static void ps_submit_proc_list(procstat_t *ps) {
       "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
       "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
       "io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; "
-      "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";",
+      "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 "; "
+      "delay_cpu = %g; delay_blkio = %g; "
+      "delay_swapin = %g; delay_freepages = %g;",
       ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->num_maps,
       ps->vmem_size, ps->vmem_rss, ps->vmem_data, ps->vmem_code,
       ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter,
       ps->cpu_system_counter, ps->io_rchar, ps->io_wchar, ps->io_syscr,
       ps->io_syscw, ps->io_diskr, ps->io_diskw, ps->cswitch_vol,
-      ps->cswitch_invol);
+      ps->cswitch_invol, ps->delay_cpu, ps->delay_blkio, ps->delay_swapin,
+      ps->delay_freepages);
 
 } /* void ps_submit_proc_list */
 
@@ -867,8 +996,9 @@ static int ps_read_tasks_status(process_entry_t *ps) {
 
     tpid = ent->d_name;
 
-    if (snprintf(filename, sizeof(filename), "/proc/%li/task/%s/status", ps->id,
-                 tpid) >= sizeof(filename)) {
+    int r = snprintf(filename, sizeof(filename), "/proc/%li/task/%s/status",
+                     ps->id, tpid);
+    if ((size_t)r >= sizeof(filename)) {
       DEBUG("Filename too long: `%s'", filename);
       continue;
     }
@@ -904,9 +1034,7 @@ static int ps_read_tasks_status(process_entry_t *ps) {
     } /* while (fgets) */
 
     if (fclose(fh)) {
-      char errbuf[1024];
-      WARNING("processes: fclose: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("processes: fclose: %s", STRERRNO);
     }
   }
   closedir(dh);
@@ -962,8 +1090,7 @@ static int ps_read_status(long pid, process_entry_t *ps) {
   } /* while (fgets) */
 
   if (fclose(fh)) {
-    char errbuf[1024];
-    WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("processes: fclose: %s", STRERRNO);
   }
 
   ps->vmem_data = data * 1024;
@@ -1023,8 +1150,7 @@ static int ps_read_io(process_entry_t *ps) {
   } /* while (fgets) */
 
   if (fclose(fh)) {
-    char errbuf[1024];
-    WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("processes: fclose: %s", STRERRNO);
   }
   return 0;
 } /* int ps_read_io (...) */
@@ -1048,8 +1174,7 @@ static int ps_count_maps(pid_t pid) {
   } /* while (fgets) */
 
   if (fclose(fh)) {
-    char errbuf[1024];
-    WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("processes: fclose: %s", STRERRNO);
   }
   return count;
 } /* int ps_count_maps (...) */
@@ -1077,36 +1202,96 @@ static int ps_count_fd(int pid) {
   return (count >= 1) ? count : 1;
 } /* int ps_count_fd (pid) */
 
+#if HAVE_LIBTASKSTATS
+static int ps_delay(process_entry_t *ps) {
+  if (taskstats_handle == NULL) {
+    return ENOTCONN;
+  }
+
+  int status = ts_delay_by_tgid(taskstats_handle, (uint32_t)ps->id, &ps->delay);
+  if (status == EPERM) {
+    static c_complain_t c;
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_NET_ADMIN)
+    if (check_capability(CAP_NET_ADMIN) != 0) {
+      if (getuid() == 0) {
+        c_complain(
+            LOG_ERR, &c,
+            "processes plugin: Reading Delay Accounting metric failed: %s. "
+            "collectd is running as root, but missing the CAP_NET_ADMIN "
+            "capability. The most common cause for this is that the init "
+            "system is dropping capabilities.",
+            STRERROR(status));
+      } else {
+        c_complain(
+            LOG_ERR, &c,
+            "processes plugin: Reading Delay Accounting metric failed: %s. "
+            "collectd is not running as root and missing the CAP_NET_ADMIN "
+            "capability. Either run collectd as root or grant it the "
+            "CAP_NET_ADMIN capability using \"setcap cap_net_admin=ep " PREFIX
+            "/sbin/collectd\".",
+            STRERROR(status));
+      }
+    } else {
+      ERROR("processes plugin: ts_delay_by_tgid failed: %s. The CAP_NET_ADMIN "
+            "capability is available (I checked), so this error is utterly "
+            "unexpected.",
+            STRERROR(status));
+    }
+#else
+    c_complain(LOG_ERR, &c,
+               "processes plugin: Reading Delay Accounting metric failed: %s. "
+               "Reading Delay Accounting metrics requires root privileges.",
+               STRERROR(status));
+#endif
+    return status;
+  } else if (status != 0) {
+    ERROR("processes plugin: ts_delay_by_tgid failed: %s", STRERROR(status));
+    return status;
+  }
+
+  return 0;
+}
+#endif
+
 static void ps_fill_details(const procstat_t *ps, process_entry_t *entry) {
-  if (entry->has_io == 0) {
+  if (entry->has_io == false) {
     ps_read_io(entry);
-    entry->has_io = 1;
+    entry->has_io = true;
   }
 
   if (ps->report_ctx_switch) {
-    if (entry->has_cswitch == 0) {
+    if (entry->has_cswitch == false) {
       ps_read_tasks_status(entry);
-      entry->has_cswitch = 1;
+      entry->has_cswitch = true;
     }
   }
 
   if (ps->report_maps_num) {
     int num_maps;
-    if (entry->has_maps == 0 && (num_maps = ps_count_maps(entry->id)) > 0) {
+    if (entry->has_maps == false && (num_maps = ps_count_maps(entry->id)) > 0) {
       entry->num_maps = num_maps;
     }
-    entry->has_maps = 1;
+    entry->has_maps = true;
   }
 
   if (ps->report_fd_num) {
     int num_fd;
-    if (entry->has_fd == 0 && (num_fd = ps_count_fd(entry->id)) > 0) {
+    if (entry->has_fd == false && (num_fd = ps_count_fd(entry->id)) > 0) {
       entry->num_fd = num_fd;
     }
-    entry->has_fd = 1;
+    entry->has_fd = true;
   }
+
+#if HAVE_LIBTASKSTATS
+  if (ps->report_delay && !entry->has_delay) {
+    if (ps_delay(entry) == 0) {
+      entry->has_delay = true;
+    }
+  }
+#endif
 } /* void ps_fill_details (...) */
 
+/* ps_read_process reads process counters on Linux. */
 static int ps_read_process(long pid, process_entry_t *ps, char *state) {
   char filename[64];
   char buffer[1024];
@@ -1153,7 +1338,8 @@ static int ps_read_process(long pid, process_entry_t *ps, char *state) {
   /* Either '(' or ')' is not found or they are in the wrong order.
    * Anyway, something weird that shouldn't happen ever. */
   if (name_start_pos >= name_end_pos) {
-    ERROR("processes plugin: name_start_pos = %zu >= name_end_pos = %zu",
+    ERROR("processes plugin: name_start_pos = %" PRIsz
+          " >= name_end_pos = %" PRIsz,
           name_start_pos, name_end_pos);
     return -1;
   }
@@ -1260,12 +1446,10 @@ static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
   errno = 0;
   fd = open(file, O_RDONLY);
   if (fd < 0) {
-    char errbuf[4096];
     /* ENOENT means the process exited while we were handling it.
      * Don't complain about this, it only fills the logs. */
     if (errno != ENOENT)
-      WARNING("processes plugin: Failed to open `%s': %s.", file,
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("processes plugin: Failed to open `%s': %s.", file, STRERRNO);
     return NULL;
   }
 
@@ -1280,13 +1464,12 @@ static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
     status = read(fd, (void *)buf_ptr, len);
 
     if (status < 0) {
-      char errbuf[1024];
 
       if ((EAGAIN == errno) || (EINTR == errno))
         continue;
 
       WARNING("processes plugin: Failed to read from `%s': %s.", file,
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+              STRERRNO);
       close(fd);
       return NULL;
     }
@@ -1340,13 +1523,11 @@ static int read_fork_rate(void) {
   FILE *proc_stat;
   char buffer[1024];
   value_t value;
-  _Bool value_valid = 0;
+  bool value_valid = 0;
 
   proc_stat = fopen("/proc/stat", "r");
   if (proc_stat == NULL) {
-    char errbuf[1024];
-    ERROR("processes plugin: fopen (/proc/stat) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("processes plugin: fopen (/proc/stat) failed: %s", STRERRNO);
     return -1;
   }
 
@@ -1392,12 +1573,11 @@ static char *ps_get_cmdline(long pid,
   if ((status < 0) || (((size_t)status) != sizeof(info))) {
     ERROR("processes plugin: Unexpected return value "
           "while reading \"%s\": "
-          "Returned %zd but expected %zu.",
+          "Returned %zd but expected %" PRIsz ".",
           path, status, buffer_size);
     return NULL;
   }
 
-  info.pr_psargs[sizeof(info.pr_psargs) - 1] = 0;
   sstrncpy(buffer, info.pr_psargs, buffer_size);
 
   return buffer;
@@ -1597,8 +1777,7 @@ static int mach_get_task_name(task_t t, int *pid, char *name,
   return 0;
 }
 #endif /* HAVE_THREAD_INFO */
-/* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO -------
- */
+/* end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO */
 
 /* do actual readings from kernel */
 static int ps_read(void) {
@@ -1858,8 +2037,7 @@ static int ps_read(void) {
   ps_list_reset();
 
   if ((proc = opendir("/proc")) == NULL) {
-    char errbuf[1024];
-    ERROR("Cannot open `/proc': %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("Cannot open `/proc': %s", STRERRNO);
     return -1;
   }
 
@@ -1960,7 +2138,7 @@ static int ps_read(void) {
      * filter out threads (duplicate PID entries). */
     if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) {
       char cmdline[CMDLINE_BUFFER_SIZE] = "";
-      _Bool have_cmdline = 0;
+      bool have_cmdline = 0;
 
       proc_ptr = &(procs[i]);
       /* Don't probe system processes and processes without arguments */
@@ -2115,7 +2293,7 @@ static int ps_read(void) {
      * filter out threads (duplicate PID entries). */
     if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) {
       char cmdline[CMDLINE_BUFFER_SIZE] = "";
-      _Bool have_cmdline = 0;
+      bool have_cmdline = 0;
 
       proc_ptr = &(procs[i]);
       /* Don't probe zombie processes  */
@@ -2455,7 +2633,7 @@ static int ps_read(void) {
   read_fork_rate();
 #endif /* KERNEL_SOLARIS */
 
-  want_init = 0;
+  want_init = false;
 
   return 0;
 } /* int ps_read */
index b63dee9..65b450c 100644 (file)
@@ -26,9 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #if !KERNEL_LINUX
 #error "No applicable input method."
@@ -45,7 +45,7 @@ static const char *config_keys[] = {
 };
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static ignorelist_t *values_list = NULL;
+static ignorelist_t *values_list;
 
 /*
  * Functions
@@ -58,7 +58,6 @@ static void submit(const char *protocol_name, const char *str_key,
 
   status = parse_value(str_value, &value, DS_TYPE_DERIVE);
   if (status != 0) {
-    ERROR("protocols plugin: Parsing string as integer failed: %s", str_value);
     return;
   }
 
@@ -87,8 +86,7 @@ static int read_file(const char *path) {
 
   fh = fopen(path, "r");
   if (fh == NULL) {
-    ERROR("protocols plugin: fopen (%s) failed: %s.", path,
-          sstrerror(errno, key_buffer, sizeof(key_buffer)));
+    ERROR("protocols plugin: fopen (%s) failed: %s.", path, STRERRNO);
     return -1;
   }
 
index c6e8930..5f51b93 100644 (file)
@@ -29,7 +29,7 @@
 
 #include "collectd.h"
 
-#include "common.h"
+#include "utils/common/common.h"
 
 #include "cpython.h"
 
@@ -129,7 +129,7 @@ static int Config_init(PyObject *s, PyObject *args, PyObject *kwds) {
 static PyObject *Config_repr(PyObject *s) {
   Config *self = (Config *)s;
   PyObject *ret = NULL;
-  static PyObject *node_prefix = NULL, *root_prefix = NULL, *ending = NULL;
+  static PyObject *node_prefix, *root_prefix, *ending;
 
   /* This is ok because we have the GIL, so this is thread-save by default. */
   if (node_prefix == NULL)
index e60ba45..9d47d70 100644 (file)
@@ -31,7 +31,7 @@
 
 #include "collectd.h"
 
-#include "common.h"
+#include "utils/common/common.h"
 
 #include "cpython.h"
 
@@ -241,7 +241,7 @@ static char CollectdError_doc[] =
 
 static pthread_t main_thread;
 static PyOS_sighandler_t python_sigint_handler;
-static _Bool do_interactive = 0;
+static bool do_interactive;
 
 /* This is our global thread state. Python saves some stuff in thread-local
  * storage. So if we allow the interpreter to run in the background
@@ -257,8 +257,8 @@ static cpy_callback_t *cpy_init_callbacks;
 static cpy_callback_t *cpy_shutdown_callbacks;
 
 /* Make sure to hold the GIL while modifying these. */
-static int cpy_shutdown_triggered = 0;
-static int cpy_num_callbacks = 0;
+static int cpy_shutdown_triggered;
+static int cpy_num_callbacks;
 
 static void cpy_destroy_user_data(void *data) {
   cpy_callback_t *c = data;
@@ -367,7 +367,7 @@ void cpy_log_exception(const char *context) {
       continue;
 
     if (cpy[strlen(cpy) - 1] == '\n')
-      cpy[strlen(cpy) - 1] = 0;
+      cpy[strlen(cpy) - 1] = '\0';
 
     Py_BEGIN_ALLOW_THREADS;
     ERROR("%s", cpy);
@@ -448,7 +448,7 @@ static int cpy_write_callback(const data_set_t *ds,
       int64_t si;
       uint64_t ui;
       double d;
-      _Bool b;
+      bool b;
 
       type = meta_data_type(meta, table[i]);
       if (type == MD_TYPE_STRING) {
@@ -703,9 +703,8 @@ static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
   for (size_t i = 0; i < ds->ds_num; ++i) {
     tuple = PyTuple_New(4);
     PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
-    PyTuple_SET_ITEM(
-        tuple, 1,
-        cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
+    PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(
+                                   DS_TYPE_TO_STRING(ds->ds[i].type)));
     PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
     PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
     PyList_SET_ITEM(list, i, tuple);
@@ -775,8 +774,7 @@ static PyObject *cpy_register_generic_userdata(void *reg, void *handler,
 
   register_function(buf, handler,
                     &(user_data_t){
-                        .data = c,
-                        .free_func = cpy_destroy_user_data,
+                        .data = c, .free_func = cpy_destroy_user_data,
                     });
 
   ++cpy_num_callbacks;
@@ -819,8 +817,7 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args,
       /* group = */ "python", buf, cpy_read_callback,
       DOUBLE_TO_CDTIME_T(interval),
       &(user_data_t){
-          .data = c,
-          .free_func = cpy_destroy_user_data,
+          .data = c, .free_func = cpy_destroy_user_data,
       });
   ++cpy_num_callbacks;
   return cpy_string_to_unicode_or_bytes(buf);
@@ -1136,7 +1133,11 @@ static void *cpy_interactive(void *pipefd) {
     cpy_log_exception("interactive session init");
   }
   cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
+#if PY_VERSION_HEX < 0x03070000
   PyOS_AfterFork();
+#else
+  PyOS_AfterFork_Child();
+#endif
   PyEval_InitThreads();
   close(*(int *)pipefd);
   PyRun_InteractiveLoop(stdin, "<stdin>");
@@ -1200,9 +1201,8 @@ static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
   values = PyTuple_New(ci->values_num); /* New reference. */
   for (int i = 0; i < ci->values_num; ++i) {
     if (ci->values[i].type == OCONFIG_TYPE_STRING) {
-      PyTuple_SET_ITEM(
-          values, i,
-          cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
+      PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(
+                                      ci->values[i].value.string));
     } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
       PyTuple_SET_ITEM(values, i,
                        PyFloat_FromDouble(ci->values[i].value.number));
@@ -1366,7 +1366,7 @@ static int cpy_config(oconfig_item_t *ci) {
 #endif
       sfree(encoding);
     } else if (strcasecmp(item->key, "LogTraces") == 0) {
-      _Bool log_traces;
+      bool log_traces;
       if (cf_util_get_boolean(item, &log_traces) != 0) {
         status = 1;
         continue;
index 15c1848..967fecf 100644 (file)
@@ -29,7 +29,7 @@
 
 #include "collectd.h"
 
-#include "common.h"
+#include "utils/common/common.h"
 
 #include "cpython.h"
 
@@ -38,7 +38,7 @@ typedef struct {
   int (*add_signed_int)(void *, const char *, int64_t);
   int (*add_unsigned_int)(void *, const char *, uint64_t);
   int (*add_double)(void *, const char *, double);
-  int (*add_boolean)(void *, const char *, _Bool);
+  int (*add_boolean)(void *, const char *, bool);
 } cpy_build_meta_handler_t;
 
 #define FreeAll()                                                              \
@@ -52,9 +52,8 @@ typedef struct {
 
 static PyObject *cpy_common_repr(PyObject *s) {
   PyObject *ret, *tmp;
-  static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL,
-                  *l_plugin_instance = NULL;
-  static PyObject *l_host = NULL, *l_time = NULL;
+  static PyObject *l_type, *l_type_instance, *l_plugin, *l_plugin_instance;
+  static PyObject *l_host, *l_time;
   PluginData *self = (PluginData *)s;
 
   if (l_type == NULL)
@@ -203,7 +202,7 @@ static int PluginData_init(PyObject *s, PyObject *args, PyObject *kwds) {
 
 static PyObject *PluginData_repr(PyObject *s) {
   PyObject *ret;
-  static PyObject *l_closing = NULL;
+  static PyObject *l_closing;
 
   if (l_closing == NULL)
     l_closing = cpy_string_to_unicode_or_bytes(")");
@@ -562,7 +561,7 @@ static int cpy_build_meta_generic(PyObject *meta,
   CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_signed_int, int64_t)        \
   CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_unsigned_int, uint64_t)     \
   CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_double, double)             \
-  CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_boolean, _Bool)             \
+  CPY_BUILD_META_FUNC(meta_type, func_prefix##_add_boolean, bool)              \
                                                                                \
   static cpy_build_meta_handler_t cpy_##func_prefix = {                        \
       .add_string = cpy_##func_prefix##_add_string,                            \
@@ -641,7 +640,8 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
   }
   size = (size_t)PySequence_Length(values);
   if (size != ds->ds_num) {
-    PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu",
+    PyErr_Format(PyExc_RuntimeError,
+                 "type %s needs %" PRIsz " values, got %" PRIsz,
                  value_list.type, ds->ds_num, size);
     return NULL;
   }
@@ -765,7 +765,8 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
   }
   size = (size_t)PySequence_Length(values);
   if (size != ds->ds_num) {
-    PyErr_Format(PyExc_RuntimeError, "type %s needs %zu values, got %zu",
+    PyErr_Format(PyExc_RuntimeError,
+                 "type %s needs %" PRIsz " values, got %" PRIsz,
                  value_list.type, ds->ds_num, size);
     return NULL;
   }
@@ -841,8 +842,7 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) {
 
 static PyObject *Values_repr(PyObject *s) {
   PyObject *ret, *tmp;
-  static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL,
-                  *l_closing = NULL;
+  static PyObject *l_interval, *l_values, *l_meta, *l_closing;
   Values *self = (Values *)s;
 
   if (l_interval == NULL)
@@ -1144,8 +1144,7 @@ static int Notification_setstring(PyObject *self, PyObject *value, void *data) {
 
 static PyObject *Notification_repr(PyObject *s) {
   PyObject *ret, *tmp;
-  static PyObject *l_severity = NULL, *l_message = NULL, *l_meta = NULL,
-                  *l_closing = NULL;
+  static PyObject *l_severity, *l_message, *l_meta, *l_closing;
   Notification *self = (Notification *)s;
 
   if (l_severity == NULL)
index 7c704ab..37dc8d6 100644 (file)
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <hiredis/hiredis.h>
 #include <sys/time.h>
 
-#ifndef HOST_NAME_MAX
-#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
-#endif
-
 #define REDIS_DEF_HOST "localhost"
 #define REDIS_DEF_PASSWD ""
 #define REDIS_DEF_PORT 6379
-#define REDIS_DEF_TIMEOUT 2000
-#define REDIS_DEF_DB_COUNT 16
-#define MAX_REDIS_NODE_NAME 64
-#define MAX_REDIS_PASSWD_LENGTH 512
+#define REDIS_DEF_TIMEOUT_SEC 2
+#define REDIS_DEF_DB_COUNT 256
 #define MAX_REDIS_VAL_SIZE 256
 #define MAX_REDIS_QUERY 2048
 
@@ -60,60 +54,77 @@ struct redis_query_s {
   char query[MAX_REDIS_QUERY];
   char type[DATA_MAX_NAME_LEN];
   char instance[DATA_MAX_NAME_LEN];
+  int database;
+
   redis_query_t *next;
 };
 
+struct prev_s {
+  derive_t keyspace_hits;
+  derive_t keyspace_misses;
+};
+typedef struct prev_s prev_t;
+
 struct redis_node_s;
 typedef struct redis_node_s redis_node_t;
 struct redis_node_s {
-  char name[MAX_REDIS_NODE_NAME];
-  char host[HOST_NAME_MAX];
-  char passwd[MAX_REDIS_PASSWD_LENGTH];
+  char *name;
+  char *host;
+  char *socket;
+  char *passwd;
   int port;
   struct timeval timeout;
+  bool report_command_stats;
+  bool report_cpu_usage;
+  redisContext *redisContext;
   redis_query_t *queries;
+  prev_t prev;
 
   redis_node_t *next;
 };
 
-static redis_node_t *nodes_head = NULL;
+static bool redis_have_instances;
+static int redis_read(user_data_t *user_data);
 
-static int redis_node_add(const redis_node_t *rn) /* {{{ */
-{
-  redis_node_t *rn_copy;
-  redis_node_t *rn_ptr;
+static void redis_node_free(void *arg) {
+  redis_node_t *rn = arg;
+  if (rn == NULL)
+    return;
 
-  /* Check for duplicates first */
-  for (rn_ptr = nodes_head; rn_ptr != NULL; rn_ptr = rn_ptr->next)
-    if (strcmp(rn->name, rn_ptr->name) == 0)
-      break;
-
-  if (rn_ptr != NULL) {
-    ERROR("redis plugin: A node with the name `%s' already exists.", rn->name);
-    return -1;
+  redis_query_t *rq = rn->queries;
+  while (rq != NULL) {
+    redis_query_t *next = rq->next;
+    sfree(rq);
+    rq = next;
   }
 
-  rn_copy = malloc(sizeof(*rn_copy));
-  if (rn_copy == NULL) {
-    ERROR("redis plugin: malloc failed adding redis_node to the tree.");
-    return -1;
-  }
-
-  memcpy(rn_copy, rn, sizeof(*rn_copy));
-  rn_copy->next = NULL;
+  if (rn->redisContext)
+    redisFree(rn->redisContext);
+  sfree(rn->name);
+  sfree(rn->host);
+  sfree(rn->socket);
+  sfree(rn->passwd);
+  sfree(rn);
+} /* void redis_node_free */
 
+static int redis_node_add(redis_node_t *rn) /* {{{ */
+{
   DEBUG("redis plugin: Adding node \"%s\".", rn->name);
 
-  if (nodes_head == NULL)
-    nodes_head = rn_copy;
-  else {
-    rn_ptr = nodes_head;
-    while (rn_ptr->next != NULL)
-      rn_ptr = rn_ptr->next;
-    rn_ptr->next = rn_copy;
-  }
+  /* Disable automatic generation of default instance in the init callback. */
+  redis_have_instances = true;
 
-  return 0;
+  char cb_name[sizeof("redis/") + DATA_MAX_NAME_LEN];
+  snprintf(cb_name, sizeof(cb_name), "redis/%s", rn->name);
+
+  return plugin_register_complex_read(
+      /* group = */ "redis",
+      /* name      = */ cb_name,
+      /* callback  = */ redis_read,
+      /* interval  = */ 0,
+      &(user_data_t){
+          .data = rn, .free_func = redis_node_free,
+      });
 } /* }}} */
 
 static redis_query_t *redis_config_query(oconfig_item_t *ci) /* {{{ */
@@ -137,6 +148,8 @@ static redis_query_t *redis_config_query(oconfig_item_t *ci) /* {{{ */
   (void)sstrncpy(rq->instance, rq->query, sizeof(rq->instance));
   replace_special(rq->instance, sizeof(rq->instance));
 
+  rq->database = 0;
+
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
 
@@ -145,6 +158,13 @@ static redis_query_t *redis_config_query(oconfig_item_t *ci) /* {{{ */
     } else if (strcasecmp("Instance", option->key) == 0) {
       status =
           cf_util_get_string_buffer(option, rq->instance, sizeof(rq->instance));
+    } else if (strcasecmp("Database", option->key) == 0) {
+      status = cf_util_get_int(option, &rq->database);
+      if (rq->database < 0) {
+        WARNING("redis plugin: The \"Database\" option must be positive "
+                "integer or zero");
+        status = -1;
+      }
     } else {
       WARNING("redis plugin: unknown configuration option: %s", option->key);
       status = -1;
@@ -160,44 +180,65 @@ err:
 
 static int redis_config_node(oconfig_item_t *ci) /* {{{ */
 {
-  redis_query_t *rq;
-  int status;
-  int timeout;
+  redis_node_t *rn = calloc(1, sizeof(*rn));
+  if (rn == NULL) {
+    ERROR("redis plugin: calloc failed adding node.");
+    return ENOMEM;
+  }
 
-  redis_node_t rn = {.port = REDIS_DEF_PORT,
-                     .timeout.tv_usec = REDIS_DEF_TIMEOUT};
+  rn->port = REDIS_DEF_PORT;
+  rn->timeout.tv_sec = REDIS_DEF_TIMEOUT_SEC;
+  rn->report_cpu_usage = true;
 
-  sstrncpy(rn.host, REDIS_DEF_HOST, sizeof(rn.host));
+  rn->host = strdup(REDIS_DEF_HOST);
+  if (rn->host == NULL) {
+    ERROR("redis plugin: strdup failed adding node.");
+    sfree(rn);
+    return ENOMEM;
+  }
 
-  status = cf_util_get_string_buffer(ci, rn.name, sizeof(rn.name));
-  if (status != 0)
+  int status = cf_util_get_string(ci, &rn->name);
+  if (status != 0) {
+    sfree(rn->host);
+    sfree(rn);
     return status;
+  }
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
 
     if (strcasecmp("Host", option->key) == 0)
-      status = cf_util_get_string_buffer(option, rn.host, sizeof(rn.host));
+      status = cf_util_get_string(option, &rn->host);
     else if (strcasecmp("Port", option->key) == 0) {
       status = cf_util_get_port_number(option);
       if (status > 0) {
-        rn.port = status;
+        rn->port = status;
         status = 0;
       }
+    } else if (strcasecmp("Socket", option->key) == 0) {
+      status = cf_util_get_string(option, &rn->socket);
     } else if (strcasecmp("Query", option->key) == 0) {
-      rq = redis_config_query(option);
+      redis_query_t *rq = redis_config_query(option);
       if (rq == NULL) {
         status = 1;
       } else {
-        rq->next = rn.queries;
-        rn.queries = rq;
+        rq->next = rn->queries;
+        rn->queries = rq;
       }
     } else if (strcasecmp("Timeout", option->key) == 0) {
+      int timeout;
       status = cf_util_get_int(option, &timeout);
-      if (status == 0)
-        rn.timeout.tv_usec = timeout;
+      if (status == 0) {
+        rn->timeout.tv_usec = timeout * 1000;
+        rn->timeout.tv_sec = rn->timeout.tv_usec / 1000000L;
+        rn->timeout.tv_usec %= 1000000L;
+      }
     } else if (strcasecmp("Password", option->key) == 0)
-      status = cf_util_get_string_buffer(option, rn.passwd, sizeof(rn.passwd));
+      status = cf_util_get_string(option, &rn->passwd);
+    else if (strcasecmp("ReportCommandStats", option->key) == 0)
+      status = cf_util_get_boolean(option, &rn->report_command_stats);
+    else if (strcasecmp("ReportCpuUsage", option->key) == 0)
+      status = cf_util_get_boolean(option, &rn->report_cpu_usage);
     else
       WARNING("redis plugin: Option `%s' not allowed inside a `Node' "
               "block. I'll ignore this option.",
@@ -207,10 +248,12 @@ static int redis_config_node(oconfig_item_t *ci) /* {{{ */
       break;
   }
 
-  if (status != 0)
+  if (status != 0) {
+    redis_node_free(rn);
     return status;
+  }
 
-  return redis_node_add(&rn);
+  return redis_node_add(rn);
 } /* }}} int redis_config_node */
 
 static int redis_config(oconfig_item_t *ci) /* {{{ */
@@ -226,17 +269,12 @@ static int redis_config(oconfig_item_t *ci) /* {{{ */
               option->key);
   }
 
-  if (nodes_head == NULL) {
-    ERROR("redis plugin: No valid node configuration could be found.");
-    return ENOENT;
-  }
-
   return 0;
 } /* }}} */
 
 __attribute__((nonnull(2))) static void
-redis_submit(char *plugin_instance, const char *type, const char *type_instance,
-             value_t value) /* {{{ */
+redis_submit(const char *plugin_instance, const char *type,
+             const char *type_instance, value_t value) /* {{{ */
 {
   value_list_t vl = VALUE_LIST_INIT;
 
@@ -252,28 +290,78 @@ redis_submit(char *plugin_instance, const char *type, const char *type_instance,
   plugin_dispatch_values(&vl);
 } /* }}} */
 
+__attribute__((nonnull(2))) static void
+redis_submit2(const char *plugin_instance, const char *type,
+              const char *type_instance, value_t value0,
+              value_t value1) /* {{{ */
+{
+  value_list_t vl = VALUE_LIST_INIT;
+  value_t values[] = {value0, value1};
+
+  vl.values = values;
+  vl.values_len = STATIC_ARRAY_SIZE(values);
+
+  sstrncpy(vl.plugin, "redis", 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));
+
+  plugin_dispatch_values(&vl);
+} /* }}} */
+
 static int redis_init(void) /* {{{ */
 {
-  redis_node_t rn = {.name = "default",
-                     .host = REDIS_DEF_HOST,
-                     .port = REDIS_DEF_PORT,
-                     .timeout.tv_sec = 0,
-                     .timeout.tv_usec = REDIS_DEF_TIMEOUT,
-                     .next = NULL};
+  if (redis_have_instances)
+    return 0;
 
-  if (nodes_head == NULL)
-    redis_node_add(&rn);
+  redis_node_t *rn = calloc(1, sizeof(*rn));
+  if (rn == NULL)
+    return ENOMEM;
 
-  return 0;
+  rn->port = REDIS_DEF_PORT;
+  rn->timeout.tv_sec = REDIS_DEF_TIMEOUT_SEC;
+
+  rn->name = strdup("default");
+  rn->host = strdup(REDIS_DEF_HOST);
+
+  if (rn->name == NULL || rn->host == NULL) {
+    sfree(rn->name);
+    sfree(rn->host);
+    sfree(rn);
+    return ENOMEM;
+  }
+
+  return redis_node_add(rn);
 } /* }}} int redis_init */
 
-static int redis_handle_info(char *node, char const *info_line,
-                             char const *type, char const *type_instance,
-                             char const *field_name, int ds_type) /* {{{ */
-{
+static void *c_redisCommand(redis_node_t *rn, const char *format, ...) {
+  redisContext *c = rn->redisContext;
+
+  if (c == NULL)
+    return NULL;
+
+  va_list ap;
+  va_start(ap, format);
+  void *reply = redisvCommand(c, format, ap);
+  va_end(ap);
+
+  if (reply == NULL) {
+    ERROR("redis plugin: Connection error: %s", c->errstr);
+    redisFree(rn->redisContext);
+    rn->redisContext = NULL;
+  }
+
+  return reply;
+} /* void c_redisCommand */
+
+static int redis_get_info_value(char const *info_line, char const *field_name,
+                                int ds_type, value_t *val) {
   char *str = strstr(info_line, field_name);
   static char buf[MAX_REDIS_VAL_SIZE];
-  value_t val;
   if (str) {
     int i;
 
@@ -283,20 +371,29 @@ static int redis_handle_info(char *node, char const *info_line,
       buf[i] = *str;
     buf[i] = '\0';
 
-    if (parse_value(buf, &val, ds_type) == -1) {
+    if (parse_value(buf, val, ds_type) == -1) {
       WARNING("redis plugin: Unable to parse field `%s'.", field_name);
       return -1;
     }
 
-    redis_submit(node, type, type_instance, val);
     return 0;
   }
   return -1;
+} /* int redis_get_info_value */
 
+static int redis_handle_info(char *node, char const *info_line,
+                             char const *type, char const *type_instance,
+                             char const *field_name, int ds_type) /* {{{ */
+{
+  value_t val;
+  if (redis_get_info_value(info_line, field_name, ds_type, &val) != 0)
+    return -1;
+
+  redis_submit(node, type, type_instance, val);
+  return 0;
 } /* }}} int redis_handle_info */
 
-static int redis_handle_query(redisContext *rh, redis_node_t *rn,
-                              redis_query_t *rq) /* {{{ */
+static int redis_handle_query(redis_node_t *rn, redis_query_t *rq) /* {{{ */
 {
   redisReply *rr;
   const data_set_t *ds;
@@ -304,16 +401,24 @@ static int redis_handle_query(redisContext *rh, redis_node_t *rn,
 
   ds = plugin_get_ds(rq->type);
   if (!ds) {
-    ERROR("redis plugin: DataSet `%s' not defined.", rq->type);
+    ERROR("redis plugin: DS type `%s' not defined.", rq->type);
     return -1;
   }
 
   if (ds->ds_num != 1) {
-    ERROR("redis plugin: DS `%s' has too many types.", rq->type);
+    ERROR("redis plugin: DS type `%s' has too many datasources. This is not "
+          "supported currently.",
+          rq->type);
     return -1;
   }
 
-  if ((rr = redisCommand(rh, rq->query)) == NULL) {
+  if ((rr = c_redisCommand(rn, "SELECT %d", rq->database)) == NULL) {
+    WARNING("redis plugin: unable to switch to database `%d' on node `%s'.",
+            rq->database, rn->name);
+    return -1;
+  }
+
+  if ((rr = c_redisCommand(rn, rq->query)) == NULL) {
     WARNING("redis plugin: unable to carry out query `%s'.", rq->query);
     return -1;
   }
@@ -337,13 +442,24 @@ static int redis_handle_query(redisContext *rh, redis_node_t *rn,
     break;
   case REDIS_REPLY_STRING:
     if (parse_value(rr->str, &val, ds->ds[0].type) == -1) {
-      WARNING("redis plugin: Unable to parse field `%s'.", rq->type);
+      WARNING("redis plugin: Query `%s': Unable to parse value.", rq->query);
       freeReplyObject(rr);
       return -1;
     }
     break;
+  case REDIS_REPLY_ERROR:
+    WARNING("redis plugin: Query `%s' failed: %s.", rq->query, rr->str);
+    freeReplyObject(rr);
+    return -1;
+  case REDIS_REPLY_ARRAY:
+    WARNING("redis plugin: Query `%s' should return string or integer. Arrays "
+            "are not supported.",
+            rq->query);
+    freeReplyObject(rr);
+    return -1;
   default:
-    WARNING("redis plugin: Cannot coerce redis type.");
+    WARNING("redis plugin: Query `%s': Cannot coerce redis type (%i).",
+            rq->query, rr->type);
     freeReplyObject(rr);
     return -1;
   }
@@ -354,7 +470,7 @@ static int redis_handle_query(redisContext *rh, redis_node_t *rn,
   return 0;
 } /* }}} int redis_handle_query */
 
-static int redis_db_stats(char *node, char const *info_line) /* {{{ */
+static int redis_db_stats(const char *node, char const *info_line) /* {{{ */
 {
   /* redis_db_stats parses and dispatches Redis database statistics,
    * currently the number of keys for each database.
@@ -364,8 +480,8 @@ static int redis_db_stats(char *node, char const *info_line) /* {{{ */
 
   for (int db = 0; db < REDIS_DEF_DB_COUNT; db++) {
     static char buf[MAX_REDIS_VAL_SIZE];
-    static char field_name[11];
-    static char db_id[3];
+    static char field_name[12];
+    static char db_id[4];
     value_t val;
     char *str;
     int i;
@@ -393,91 +509,299 @@ static int redis_db_stats(char *node, char const *info_line) /* {{{ */
 
 } /* }}} int redis_db_stats */
 
-static int redis_read(void) /* {{{ */
-{
-  for (redis_node_t *rn = nodes_head; rn != NULL; rn = rn->next) {
-    redisContext *rh;
+static void redis_cpu_usage(const char *node, char const *info_line) {
+  while (42) {
+    value_t rusage_user;
+    value_t rusage_syst;
+
+    if (redis_get_info_value(info_line, "used_cpu_user", DS_TYPE_GAUGE,
+                             &rusage_user) != 0)
+      break;
+
+    if (redis_get_info_value(info_line, "used_cpu_sys", DS_TYPE_GAUGE,
+                             &rusage_syst) != 0)
+      break;
+
+    redis_submit2(node, "ps_cputime", "daemon",
+                  (value_t){.derive = rusage_user.gauge * 1000000},
+                  (value_t){.derive = rusage_syst.gauge * 1000000});
+    break;
+  }
+
+  while (42) {
+    value_t rusage_user;
+    value_t rusage_syst;
+
+    if (redis_get_info_value(info_line, "used_cpu_user_children", DS_TYPE_GAUGE,
+                             &rusage_user) != 0)
+      break;
+
+    if (redis_get_info_value(info_line, "used_cpu_sys_children", DS_TYPE_GAUGE,
+                             &rusage_syst) != 0)
+      break;
+
+    redis_submit2(node, "ps_cputime", "children",
+                  (value_t){.derive = rusage_user.gauge * 1000000},
+                  (value_t){.derive = rusage_syst.gauge * 1000000});
+    break;
+  }
+} /* void redis_cpu_usage */
+
+static gauge_t calculate_ratio_percent(derive_t part1, derive_t part2,
+                                       derive_t *prev1, derive_t *prev2) {
+  if ((*prev1 == 0) || (*prev2 == 0) || (part1 < *prev1) || (part2 < *prev2)) {
+    *prev1 = part1;
+    *prev2 = part2;
+    return NAN;
+  }
+
+  derive_t num = part1 - *prev1;
+  derive_t denom = part2 - *prev2 + num;
+
+  *prev1 = part1;
+  *prev2 = part2;
+
+  if (denom == 0)
+    return NAN;
+
+  if (num == 0)
+    return 0;
+
+  return 100.0 * (gauge_t)num / (gauge_t)denom;
+} /* gauge_t calculate_ratio_percent */
+
+static void redis_keyspace_usage(redis_node_t *rn, char const *info_line) {
+  value_t hits, misses;
+
+  if (redis_get_info_value(info_line, "keyspace_hits", DS_TYPE_DERIVE, &hits) !=
+      0)
+    return;
+
+  if (redis_get_info_value(info_line, "keyspace_misses", DS_TYPE_DERIVE,
+                           &misses) != 0)
+    return;
+
+  redis_submit(rn->name, "cache_result", "hits", hits);
+  redis_submit(rn->name, "cache_result", "misses", misses);
+
+  prev_t *prev = &rn->prev;
+  gauge_t ratio = calculate_ratio_percent(
+      hits.derive, misses.derive, &prev->keyspace_hits, &prev->keyspace_misses);
+  redis_submit(rn->name, "percent", "hitratio", (value_t){.gauge = ratio});
+
+} /* void redis_keyspace_usage */
+
+static void redis_check_connection(redis_node_t *rn) {
+  if (rn->redisContext)
+    return;
+
+  redisContext *rh;
+  if (rn->socket != NULL)
+    rh = redisConnectUnixWithTimeout(rn->socket, rn->timeout);
+  else
+    rh = redisConnectWithTimeout(rn->host, rn->port, rn->timeout);
+
+  if (rh == NULL) {
+    ERROR("redis plugin: can't allocate redis context");
+    return;
+  }
+  if (rh->err) {
+    if (rn->socket)
+      ERROR("redis plugin: unable to connect to node `%s' (%s): %s.", rn->name,
+            rn->socket, rh->errstr);
+    else
+      ERROR("redis plugin: unable to connect to node `%s' (%s:%d): %s.",
+            rn->name, rn->host, rn->port, rh->errstr);
+    redisFree(rh);
+    return;
+  }
+
+  rn->redisContext = rh;
+
+  if (rn->passwd) {
     redisReply *rr;
 
-    DEBUG("redis plugin: querying info from node `%s' (%s:%d).", rn->name,
-          rn->host, rn->port);
+    DEBUG("redis plugin: authenticating node `%s' passwd(%s).", rn->name,
+          rn->passwd);
 
-    rh = redisConnectWithTimeout((char *)rn->host, rn->port, rn->timeout);
-    if (rh == NULL) {
-      ERROR("redis plugin: unable to connect to node `%s' (%s:%d).", rn->name,
-            rn->host, rn->port);
-      continue;
+    if ((rr = c_redisCommand(rn, "AUTH %s", rn->passwd)) == NULL) {
+      WARNING("redis plugin: unable to authenticate on node `%s'.", rn->name);
+      return;
     }
 
-    if (strlen(rn->passwd) > 0) {
-      DEBUG("redis plugin: authenticating node `%s' passwd(%s).", rn->name,
-            rn->passwd);
+    if (rr->type != REDIS_REPLY_STATUS) {
+      WARNING("redis plugin: invalid authentication on node `%s'.", rn->name);
+      freeReplyObject(rr);
+      redisFree(rn->redisContext);
+      rn->redisContext = NULL;
+      return;
+    }
 
-      if ((rr = redisCommand(rh, "AUTH %s", rn->passwd)) == NULL) {
-        WARNING("redis plugin: unable to authenticate on node `%s'.", rn->name);
-        goto redis_fail;
-      }
+    freeReplyObject(rr);
+  }
+  return;
+} /* void redis_check_connection */
 
-      if (rr->type != REDIS_REPLY_STATUS) {
-        WARNING("redis plugin: invalid authentication on node `%s'.", rn->name);
-        goto redis_fail;
-      }
+static void redis_read_server_info(redis_node_t *rn) {
+  redisReply *rr;
 
-      freeReplyObject(rr);
+  if ((rr = c_redisCommand(rn, "INFO")) == NULL) {
+    WARNING("redis plugin: unable to get INFO from node `%s'.", rn->name);
+    return;
+  }
+
+  redis_handle_info(rn->name, rr->str, "uptime", NULL, "uptime_in_seconds",
+                    DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "current_connections", "clients",
+                    "connected_clients", DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "blocked_clients", NULL,
+                    "blocked_clients", DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "memory", NULL, "used_memory",
+                    DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "memory_lua", NULL, "used_memory_lua",
+                    DS_TYPE_GAUGE);
+  /* changes_since_last_save: Deprecated in redis version 2.6 and above */
+  redis_handle_info(rn->name, rr->str, "volatile_changes", NULL,
+                    "changes_since_last_save", DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "total_connections", NULL,
+                    "total_connections_received", DS_TYPE_DERIVE);
+  redis_handle_info(rn->name, rr->str, "total_operations", NULL,
+                    "total_commands_processed", DS_TYPE_DERIVE);
+  redis_handle_info(rn->name, rr->str, "expired_keys", NULL, "expired_keys",
+                    DS_TYPE_DERIVE);
+  redis_handle_info(rn->name, rr->str, "evicted_keys", NULL, "evicted_keys",
+                    DS_TYPE_DERIVE);
+  redis_handle_info(rn->name, rr->str, "pubsub", "channels", "pubsub_channels",
+                    DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "pubsub", "patterns", "pubsub_patterns",
+                    DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "current_connections", "slaves",
+                    "connected_slaves", DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "total_bytes", "input",
+                    "total_net_input_bytes", DS_TYPE_DERIVE);
+  redis_handle_info(rn->name, rr->str, "total_bytes", "output",
+                    "total_net_output_bytes", DS_TYPE_DERIVE);
+
+  redis_keyspace_usage(rn, rr->str);
+
+  redis_db_stats(rn->name, rr->str);
+
+  if (rn->report_cpu_usage)
+    redis_cpu_usage(rn->name, rr->str);
+
+  freeReplyObject(rr);
+} /* void redis_read_server_info */
+
+static void redis_read_command_stats(redis_node_t *rn) {
+  redisReply *rr;
+
+  if ((rr = c_redisCommand(rn, "INFO commandstats")) == NULL) {
+    WARNING("redis plugin: node `%s': unable to get `INFO commandstats'.",
+            rn->name);
+    return;
+  }
+
+  if (rr->type != REDIS_REPLY_STRING) {
+    WARNING("redis plugin: node `%s' `INFO commandstats' returned unsupported "
+            "redis type %i.",
+            rn->name, rr->type);
+    freeReplyObject(rr);
+    return;
+  }
+
+  char *command;
+  char *line;
+  char *ptr = rr->str;
+  char *saveptr = NULL;
+  while ((line = strtok_r(ptr, "\n\r", &saveptr)) != NULL) {
+    ptr = NULL;
+
+    if (line[0] == '#')
+      continue;
+
+    /* command name */
+    if (strstr(line, "cmdstat_") != line) {
+      ERROR("redis plugin: not found 'cmdstat_' prefix in line '%s'", line);
+      continue;
     }
 
-    if ((rr = redisCommand(rh, "INFO")) == NULL) {
-      WARNING("redis plugin: unable to get info from node `%s'.", rn->name);
-      goto redis_fail;
+    char *values = strstr(line, ":");
+    if (values == NULL) {
+      ERROR("redis plugin: not found ':' separator in line '%s'", line);
+      continue;
     }
 
-    redis_handle_info(rn->name, rr->str, "uptime", NULL, "uptime_in_seconds",
-                      DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "current_connections", "clients",
-                      "connected_clients", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "blocked_clients", NULL,
-                      "blocked_clients", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "memory", NULL, "used_memory",
-                      DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "memory_lua", NULL, "used_memory_lua",
-                      DS_TYPE_GAUGE);
-    /* changes_since_last_save: Deprecated in redis version 2.6 and above */
-    redis_handle_info(rn->name, rr->str, "volatile_changes", NULL,
-                      "changes_since_last_save", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "total_connections", NULL,
-                      "total_connections_received", DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "total_operations", NULL,
-                      "total_commands_processed", DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "operations_per_second", NULL,
-                      "instantaneous_ops_per_sec", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "expired_keys", NULL, "expired_keys",
-                      DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "evicted_keys", NULL, "evicted_keys",
-                      DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "pubsub", "channels",
-                      "pubsub_channels", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "pubsub", "patterns",
-                      "pubsub_patterns", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "current_connections", "slaves",
-                      "connected_slaves", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "cache_result", "hits",
-                      "keyspace_hits", DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "cache_result", "misses",
-                      "keyspace_misses", DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "total_bytes", "input",
-                      "total_net_input_bytes", DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "total_bytes", "output",
-                      "total_net_output_bytes", DS_TYPE_DERIVE);
-
-    redis_db_stats(rn->name, rr->str);
-
-    for (redis_query_t *rq = rn->queries; rq != NULL; rq = rq->next)
-      redis_handle_query(rh, rn, rq);
-
-  redis_fail:
-    if (rr != NULL)
-      freeReplyObject(rr);
-    redisFree(rh);
+    /* Null-terminate command token */
+    values[0] = '\0';
+    command = line + strlen("cmdstat_");
+    values++;
+
+    /* parse values */
+    /* cmdstat_publish:calls=20795774,usec=111039258,usec_per_call=5.34 */
+    char *field;
+    char *saveptr_field = NULL;
+    while ((field = strtok_r(values, "=", &saveptr_field)) != NULL) {
+      values = NULL;
+
+      const char *type;
+      /* only these are supported */
+      if (strcmp(field, "calls") == 0)
+        type = "commands";
+      else if (strcmp(field, "usec") == 0)
+        type = "redis_command_cputime";
+      else
+        continue;
+
+      if ((field = strtok_r(NULL, ",", &saveptr_field)) == NULL)
+        continue;
+
+      char *endptr = NULL;
+      errno = 0;
+      derive_t value = strtoll(field, &endptr, 0);
+
+      if ((endptr == field) || (errno != 0))
+        continue;
+
+      redis_submit(rn->name, type, command, (value_t){.derive = value});
+    }
+  }
+  freeReplyObject(rr);
+} /* void redis_read_command_stats */
+
+static int redis_read(user_data_t *user_data) /* {{{ */
+{
+  redis_node_t *rn = user_data->data;
+
+#if COLLECT_DEBUG
+  if (rn->socket)
+    DEBUG("redis plugin: querying info from node `%s' (%s).", rn->name,
+          rn->socket);
+  else
+    DEBUG("redis plugin: querying info from node `%s' (%s:%d).", rn->name,
+          rn->host, rn->port);
+#endif
+
+  redis_check_connection(rn);
+
+  if (!rn->redisContext) /* no connection */
+    return -1;
+
+  redis_read_server_info(rn);
+
+  if (!rn->redisContext) /* connection lost */
+    return -1;
+
+  if (rn->report_command_stats) {
+    redis_read_command_stats(rn);
+
+    if (!rn->redisContext) /* connection lost */
+      return -1;
+  }
+
+  for (redis_query_t *rq = rn->queries; rq != NULL; rq = rq->next) {
+    redis_handle_query(rn, rq);
+    if (!rn->redisContext) /* connection lost */
+      return -1;
   }
 
   return 0;
@@ -488,8 +812,5 @@ void module_register(void) /* {{{ */
 {
   plugin_register_complex_config("redis", redis_config);
   plugin_register_init("redis", redis_init);
-  plugin_register_read("redis", redis_read);
-  /* TODO: plugin_register_write: one redis list per value id with
-   * X elements */
 }
 /* }}} */
index 9ea8297..70dd75e 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <routeros_api.h>
 
@@ -39,12 +39,13 @@ struct cr_data_s {
   char *username;
   char *password;
 
-  _Bool collect_interface;
-  _Bool collect_regtable;
-  _Bool collect_cpu_load;
-  _Bool collect_memory;
-  _Bool collect_df;
-  _Bool collect_disk;
+  bool collect_interface;
+  bool collect_regtable;
+  bool collect_cpu_load;
+  bool collect_memory;
+  bool collect_df;
+  bool collect_disk;
+  bool collect_health;
 };
 typedef struct cr_data_s cr_data_t;
 
@@ -140,9 +141,17 @@ static void submit_regtable(cr_data_t *rd, /* {{{ */
   if (r == NULL)
     return;
 
+  const char *name = r->radio_name;
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 3)
+  if (name == NULL)
+    name = r->mac_address;
+#endif
+  if (name == NULL)
+    name = "default";
+
   /*** RX ***/
   snprintf(type_instance, sizeof(type_instance), "%s-%s-rx", r->interface,
-           r->radio_name);
+           name);
   cr_submit_gauge(rd, "bitrate", type_instance,
                   (gauge_t)(1000000.0 * r->rx_rate));
   cr_submit_gauge(rd, "signal_power", type_instance,
@@ -151,7 +160,7 @@ static void submit_regtable(cr_data_t *rd, /* {{{ */
 
   /*** TX ***/
   snprintf(type_instance, sizeof(type_instance), "%s-%s-tx", r->interface,
-           r->radio_name);
+           name);
   cr_submit_gauge(rd, "bitrate", type_instance,
                   (gauge_t)(1000000.0 * r->tx_rate));
   cr_submit_gauge(rd, "signal_power", type_instance,
@@ -159,8 +168,7 @@ static void submit_regtable(cr_data_t *rd, /* {{{ */
   cr_submit_gauge(rd, "signal_quality", type_instance, (gauge_t)r->tx_ccq);
 
   /*** RX / TX ***/
-  snprintf(type_instance, sizeof(type_instance), "%s-%s", r->interface,
-           r->radio_name);
+  snprintf(type_instance, sizeof(type_instance), "%s-%s", r->interface, name);
   cr_submit_io(rd, "if_octets", type_instance, (derive_t)r->rx_bytes,
                (derive_t)r->tx_bytes);
   cr_submit_gauge(rd, "snr", type_instance, (gauge_t)r->signal_to_noise);
@@ -205,13 +213,31 @@ static int handle_system_resource(__attribute__((unused))
   }
 
   if (rd->collect_disk) {
-    cr_submit_counter(rd, "counter", "secors_written",
+    cr_submit_counter(rd, "counter", "sectors_written",
                       (derive_t)r->write_sect_total);
     cr_submit_gauge(rd, "gauge", "bad_blocks", (gauge_t)r->bad_blocks);
   }
 
   return 0;
 } /* }}} int handle_system_resource */
+
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 3)
+static int handle_system_health(__attribute__((unused))
+                                ros_connection_t *c, /* {{{ */
+                                const ros_system_health_t *r,
+                                __attribute__((unused)) void *user_data) {
+
+  if ((r == NULL) || (user_data == NULL))
+    return EINVAL;
+
+  cr_data_t *rd = user_data;
+
+  cr_submit_gauge(rd, "voltage", "system", (gauge_t)r->voltage);
+  cr_submit_gauge(rd, "temperature", "system", (gauge_t)r->temperature);
+
+  return 0;
+} /* }}} int handle_system_health */
+#endif
 #endif
 
 static int cr_read(user_data_t *user_data) /* {{{ */
@@ -230,9 +256,7 @@ static int cr_read(user_data_t *user_data) /* {{{ */
     rd->connection =
         ros_connect(rd->node, rd->service, rd->username, rd->password);
     if (rd->connection == NULL) {
-      char errbuf[128];
-      ERROR("routeros plugin: ros_connect failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("routeros plugin: ros_connect failed: %s", STRERRNO);
       return -1;
     }
   }
@@ -242,9 +266,7 @@ static int cr_read(user_data_t *user_data) /* {{{ */
     status = ros_interface(rd->connection, handle_interface,
                            /* user data = */ rd);
     if (status != 0) {
-      char errbuf[128];
-      ERROR("routeros plugin: ros_interface failed: %s",
-            sstrerror(status, errbuf, sizeof(errbuf)));
+      ERROR("routeros plugin: ros_interface failed: %s", STRERROR(status));
       ros_disconnect(rd->connection);
       rd->connection = NULL;
       return -1;
@@ -255,9 +277,8 @@ static int cr_read(user_data_t *user_data) /* {{{ */
     status = ros_registration_table(rd->connection, handle_regtable,
                                     /* user data = */ rd);
     if (status != 0) {
-      char errbuf[128];
       ERROR("routeros plugin: ros_registration_table failed: %s",
-            sstrerror(status, errbuf, sizeof(errbuf)));
+            STRERROR(status));
       ros_disconnect(rd->connection);
       rd->connection = NULL;
       return -1;
@@ -270,15 +291,27 @@ static int cr_read(user_data_t *user_data) /* {{{ */
     status = ros_system_resource(rd->connection, handle_system_resource,
                                  /* user data = */ rd);
     if (status != 0) {
-      char errbuf[128];
       ERROR("routeros plugin: ros_system_resource failed: %s",
-            sstrerror(status, errbuf, sizeof(errbuf)));
+            STRERROR(status));
+      ros_disconnect(rd->connection);
+      rd->connection = NULL;
+      return -1;
+    }
+  }
+
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 3)
+  if (rd->collect_health) {
+    status = ros_system_health(rd->connection, handle_system_health,
+                               /* user data = */ rd);
+    if (status != 0) {
+      ERROR("routeros plugin: ros_system_health failed: %s", STRERROR(status));
       ros_disconnect(rd->connection);
       rd->connection = NULL;
       return -1;
     }
   }
 #endif
+#endif
 
   return 0;
 } /* }}} int cr_read */
@@ -339,6 +372,10 @@ static int cr_config_router(oconfig_item_t *ci) /* {{{ */
       cf_util_get_boolean(child, &router_data->collect_df);
     else if (strcasecmp("CollectDisk", child->key) == 0)
       cf_util_get_boolean(child, &router_data->collect_disk);
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 3)
+    else if (strcasecmp("CollectHealth", child->key) == 0)
+      cf_util_get_boolean(child, &router_data->collect_health);
+#endif
 #endif
     else {
       WARNING("routeros plugin: Unknown config option `%s'.", child->key);
@@ -361,7 +398,27 @@ static int cr_config_router(oconfig_item_t *ci) /* {{{ */
       status = -1;
     }
 
-    if (!router_data->collect_interface && !router_data->collect_regtable) {
+    int report = 0;
+    if (router_data->collect_interface)
+      report++;
+    if (router_data->collect_regtable)
+      report++;
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0)
+    if (router_data->collect_cpu_load)
+      report++;
+    if (router_data->collect_memory)
+      report++;
+    if (router_data->collect_df)
+      report++;
+    if (router_data->collect_disk)
+      report++;
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 3)
+    if (router_data->collect_health)
+      report++;
+#endif
+#endif
+
+    if (!report) {
       ERROR("routeros plugin: No `Collect*' option within a `Router' block. "
             "What statistics should I collect?");
       status = -1;
index 8287013..9aeb71a 100644 (file)
@@ -26,9 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_rrdcreate.h"
+#include "utils/common/common.h"
+#include "utils/rrdcreate/rrdcreate.h"
 
 #undef HAVE_CONFIG_H
 #include <rrd.h>
 /*
  * Private variables
  */
-static char *datadir = NULL;
-static char *daemon_address = NULL;
-static _Bool config_create_files = 1;
-static _Bool config_collect_stats = 1;
-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 char *datadir;
+static char *daemon_address;
+static bool config_create_files = true;
+static bool config_collect_stats = true;
+static rrdcreate_config_t rrdcreate_config = {.stepsize = 0,
+                                              .heartbeat = 0,
+                                              .rrarows = 1200,
+                                              .xff = 0.1,
+                                              .timespans = NULL,
+                                              .timespans_num = 0,
+                                              .consolidation_functions = NULL,
+                                              .consolidation_functions_num = 0,
+                                              .async = 0};
 
 /*
  * 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) &&
@@ -88,8 +80,8 @@ static int value_list_to_string(char *buffer, int buffer_len,
       return -1;
 
     if (ds->ds[i].type == DS_TYPE_COUNTER) {
-      status = snprintf(buffer + offset, buffer_len - offset, ":%llu",
-                        vl->values[i].counter);
+      status = snprintf(buffer + offset, buffer_len - offset, ":%" PRIu64,
+                        (uint64_t)vl->values[i].counter);
     } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
       status = snprintf(buffer + offset, buffer_len - offset, ":%f",
                         vl->values[i].gauge);
@@ -113,8 +105,6 @@ static int value_list_to_string(char *buffer, int buffer_len,
 static int value_list_to_filename(char *buffer, size_t buffer_size,
                                   value_list_t const *vl) {
   char const suffix[] = ".rrd";
-  int status;
-  size_t len;
 
   if (datadir != NULL) {
     size_t datadir_len = strlen(datadir) + 1;
@@ -124,17 +114,17 @@ static int value_list_to_filename(char *buffer, size_t buffer_size,
 
     sstrncpy(buffer, datadir, buffer_size);
     buffer[datadir_len - 1] = '/';
-    buffer[datadir_len] = 0;
+    buffer[datadir_len] = '\0';
 
     buffer += datadir_len;
     buffer_size -= datadir_len;
   }
 
-  status = FORMAT_VL(buffer, buffer_size, vl);
+  int status = FORMAT_VL(buffer, buffer_size, vl);
   if (status != 0)
     return status;
 
-  len = strlen(buffer);
+  size_t len = strlen(buffer);
   assert(len < buffer_size);
   buffer += len;
   buffer_size -= len;
@@ -147,10 +137,9 @@ static int value_list_to_filename(char *buffer, size_t buffer_size,
 } /* int value_list_to_filename */
 
 static int rc_config_get_int_positive(oconfig_item_t const *ci, int *ret) {
-  int status;
   int tmp = 0;
 
-  status = cf_util_get_int(ci, &tmp);
+  int status = cf_util_get_int(ci, &tmp);
   if (status != 0)
     return status;
   if (tmp < 0)
@@ -161,8 +150,6 @@ static int rc_config_get_int_positive(oconfig_item_t const *ci, int *ret) {
 } /* int rc_config_get_int_positive */
 
 static int rc_config_get_xff(oconfig_item_t const *ci, double *ret) {
-  double value;
-
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
     ERROR("rrdcached plugin: The \"%s\" needs exactly one numeric argument "
           "in the range [0.0, 1.0)",
@@ -170,7 +157,7 @@ static int rc_config_get_xff(oconfig_item_t const *ci, double *ret) {
     return EINVAL;
   }
 
-  value = ci->values[0].value.number;
+  double value = ci->values[0].value.number;
   if ((value >= 0.0) && (value < 1.0)) {
     *ret = value;
     return 0;
@@ -183,14 +170,12 @@ static int rc_config_get_xff(oconfig_item_t const *ci, double *ret) {
 } /* int rc_config_get_xff */
 
 static int rc_config_add_timespan(int timespan) {
-  int *tmp;
-
   if (timespan <= 0)
     return EINVAL;
 
-  tmp = realloc(rrdcreate_config.timespans,
-                sizeof(*rrdcreate_config.timespans) *
-                    (rrdcreate_config.timespans_num + 1));
+  int *tmp = realloc(rrdcreate_config.timespans,
+                     sizeof(*rrdcreate_config.timespans) *
+                         (rrdcreate_config.timespans_num + 1));
   if (tmp == NULL)
     return ENOMEM;
   rrdcreate_config.timespans = tmp;
@@ -262,12 +247,10 @@ static int rc_config(oconfig_item_t *ci) {
 } /* int rc_config */
 
 static int try_reconnect(void) {
-  int status;
-
   rrdc_disconnect();
 
   rrd_clear_error();
-  status = rrdc_connect(daemon_address);
+  int status = rrdc_connect(daemon_address);
   if (status != 0) {
     ERROR("rrdcached plugin: Failed to reconnect to RRDCacheD "
           "at %s: %s (status=%d)",
@@ -282,9 +265,6 @@ static int try_reconnect(void) {
 } /* int try_reconnect */
 
 static int rc_read(void) {
-  int status;
-  rrdc_stats_t *head;
-  _Bool retried = 0;
 
   value_list_t vl = VALUE_LIST_INIT;
   vl.values = &(value_t){.gauge = NAN};
@@ -302,7 +282,7 @@ static int rc_read(void) {
   sstrncpy(vl.plugin, "rrdcached", sizeof(vl.plugin));
 
   rrd_clear_error();
-  status = rrdc_connect(daemon_address);
+  int status = rrdc_connect(daemon_address);
   if (status != 0) {
     ERROR("rrdcached plugin: Failed to connect to RRDCacheD "
           "at %s: %s (status=%d)",
@@ -310,6 +290,9 @@ static int rc_read(void) {
     return -1;
   }
 
+  rrdc_stats_t *head;
+  bool retried = false;
+
   while (42) {
     /* The RRD client lib does not provide any means for checking a
      * connection, hence we'll have to retry upon failed operations. */
@@ -320,7 +303,7 @@ static int rc_read(void) {
       break;
 
     if (!retried) {
-      retried = 1;
+      retried = true;
       if (try_reconnect() == 0)
         continue;
       /* else: report the error and fail */
@@ -390,9 +373,8 @@ static int rc_write(const data_set_t *ds, const value_list_t *vl,
                     user_data_t __attribute__((unused)) * user_data) {
   char filename[PATH_MAX];
   char values[512];
-  char *values_array[2];
   int status;
-  _Bool retried = 0;
+  bool retried = false;
 
   if (daemon_address == NULL) {
     ERROR("rrdcached plugin: daemon_address == NULL.");
@@ -415,18 +397,13 @@ static int rc_write(const data_set_t *ds, const value_list_t *vl,
     return -1;
   }
 
-  values_array[0] = values;
-  values_array[1] = NULL;
-
   if (config_create_files) {
     struct stat statbuf;
 
     status = stat(filename, &statbuf);
     if (status != 0) {
       if (errno != ENOENT) {
-        char errbuf[1024];
-        ERROR("rrdcached plugin: stat (%s) failed: %s", filename,
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("rrdcached plugin: stat (%s) failed: %s", filename, STRERRNO);
         return -1;
       }
 
@@ -448,6 +425,10 @@ static int rc_write(const data_set_t *ds, const value_list_t *vl,
     return -1;
   }
 
+  char *values_array[2] = {
+          [0] = values, [1] = NULL,
+  };
+
   while (42) {
     /* The RRD client lib does not provide any means for checking a
      * connection, hence we'll have to retry upon failed operations. */
@@ -457,7 +438,7 @@ static int rc_write(const data_set_t *ds, const value_list_t *vl,
       break;
 
     if (!retried) {
-      retried = 1;
+      retried = true;
       if (try_reconnect() == 0)
         continue;
       /* else: report the error and fail */
@@ -474,20 +455,18 @@ static int rc_write(const data_set_t *ds, const value_list_t *vl,
 static int rc_flush(__attribute__((unused)) cdtime_t timeout, /* {{{ */
                     const char *identifier,
                     __attribute__((unused)) user_data_t *ud) {
-  char filename[PATH_MAX + 1];
-  int status;
-  _Bool retried = 0;
-
   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)",
@@ -495,6 +474,8 @@ static int rc_flush(__attribute__((unused)) cdtime_t timeout, /* {{{ */
     return -1;
   }
 
+  bool retried = false;
+
   while (42) {
     /* The RRD client lib does not provide any means for checking a
      * connection, hence we'll have to retry upon failed operations. */
@@ -504,7 +485,7 @@ static int rc_flush(__attribute__((unused)) cdtime_t timeout, /* {{{ */
       break;
 
     if (!retried) {
-      retried = 1;
+      retried = true;
       if (try_reconnect() == 0)
         continue;
       /* else: report the error and fail */
index cd275da..d0849d1 100644 (file)
 
 #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>
 
@@ -66,8 +66,8 @@ static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 /* If datadir is zero, the daemon's basedir is used. If stepsize or heartbeat
  * is zero a default, depending on the `interval' member of the value list is
  * being used. */
-static char *datadir = NULL;
-static double write_rate = 0.0;
+static char *datadir;
+static double write_rate;
 static rrdcreate_config_t rrdcreate_config = {
     /* stepsize = */ 0,
     /* heartbeat = */ 0,
@@ -84,17 +84,17 @@ static rrdcreate_config_t rrdcreate_config = {
 
 /* XXX: If you need to lock both, cache_lock and queue_lock, at the same time,
  * ALWAYS lock `cache_lock' first! */
-static cdtime_t cache_timeout = 0;
-static cdtime_t cache_flush_timeout = 0;
-static cdtime_t random_timeout = 0;
+static cdtime_t cache_timeout;
+static cdtime_t cache_flush_timeout;
+static cdtime_t random_timeout;
 static cdtime_t cache_flush_last;
-static c_avl_tree_t *cache = NULL;
+static c_avl_tree_t *cache;
 static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
 
-static rrd_queue_t *queue_head = NULL;
-static rrd_queue_t *queue_tail = NULL;
-static rrd_queue_t *flushq_head = NULL;
-static rrd_queue_t *flushq_tail = NULL;
+static rrd_queue_t *queue_head;
+static rrd_queue_t *queue_tail;
+static rrd_queue_t *flushq_head;
+static rrd_queue_t *flushq_tail;
 static pthread_t queue_thread;
 static int queue_thread_running = 1;
 static pthread_mutex_t queue_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -104,7 +104,7 @@ static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
 static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
-static int do_shutdown = 0;
+static int do_shutdown;
 
 #if HAVE_THREADSAFE_LIBRRD
 static int srrd_update(char *filename, char *template, int argc,
@@ -186,8 +186,8 @@ static int value_list_to_string_multiple(char *buffer, int buffer_len,
       return -1;
 
     if (ds->ds[i].type == DS_TYPE_COUNTER)
-      status = snprintf(buffer + offset, buffer_len - offset, ":%llu",
-                        vl->values[i].counter);
+      status = snprintf(buffer + offset, buffer_len - offset, ":%" PRIu64,
+                        (uint64_t)vl->values[i].counter);
     else if (ds->ds[i].type == DS_TYPE_GAUGE)
       status = snprintf(buffer + offset, buffer_len - offset, ":" GAUGE_FORMAT,
                         vl->values[i].gauge);
@@ -226,8 +226,8 @@ static int value_list_to_string(char *buffer, int buffer_len,
                       vl->values[0].gauge);
     break;
   case DS_TYPE_COUNTER:
-    status = snprintf(buffer, buffer_len, "%u:%llu", (unsigned)tt,
-                      vl->values[0].counter);
+    status = snprintf(buffer, buffer_len, "%u:%" PRIu64, (unsigned)tt,
+                      (uint64_t)vl->values[0].counter);
     break;
   case DS_TYPE_ABSOLUTE:
     status = snprintf(buffer, buffer_len, "%u:%" PRIu64, (unsigned)tt,
@@ -520,10 +520,7 @@ static void rrd_cache_flush(cdtime_t timeout) {
     {
       char **tmp = realloc(keys, (keys_num + 1) * sizeof(char *));
       if (tmp == NULL) {
-        char errbuf[1024];
-        ERROR("rrdtool plugin: "
-              "realloc failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+        ERROR("rrdtool plugin: realloc failed: %s", STRERRNO);
         c_avl_iterator_destroy(iter);
         sfree(keys);
         return;
@@ -572,7 +569,7 @@ static int rrd_cache_flush_identifier(cdtime_t timeout,
     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) {
@@ -627,6 +624,7 @@ static int rrd_cache_insert(const char *filename, const char *value,
   if ((status != 0) || (rc == NULL)) {
     rc = malloc(sizeof(*rc));
     if (rc == NULL) {
+      ERROR("rrdtool plugin: malloc failed: %s", STRERRNO);
       pthread_mutex_unlock(&cache_lock);
       return -1;
     }
@@ -651,15 +649,12 @@ static int rrd_cache_insert(const char *filename, const char *value,
   values_new =
       realloc((void *)rc->values, (rc->values_num + 1) * sizeof(char *));
   if (values_new == NULL) {
-    char errbuf[1024];
     void *cache_key = NULL;
 
-    sstrerror(errno, errbuf, sizeof(errbuf));
-
     c_avl_remove(cache, filename, &cache_key, NULL);
     pthread_mutex_unlock(&cache_lock);
 
-    ERROR("rrdtool plugin: realloc failed: %s", errbuf);
+    ERROR("rrdtool plugin: realloc failed: %s", STRERRNO);
 
     sfree(cache_key);
     sfree(rc->values);
@@ -681,12 +676,9 @@ static int rrd_cache_insert(const char *filename, const char *value,
     void *cache_key = strdup(filename);
 
     if (cache_key == NULL) {
-      char errbuf[1024];
-      sstrerror(errno, errbuf, sizeof(errbuf));
-
       pthread_mutex_unlock(&cache_lock);
 
-      ERROR("rrdtool plugin: strdup failed: %s", errbuf);
+      ERROR("rrdtool plugin: strdup failed: %s", STRERRNO);
 
       sfree(rc->values[0]);
       sfree(rc->values);
@@ -799,25 +791,28 @@ static int rrd_write(const data_set_t *ds, const value_list_t *vl,
   }
 
   char filename[PATH_MAX];
-  if (value_list_to_filename(filename, sizeof(filename), vl) != 0)
+  if (value_list_to_filename(filename, sizeof(filename), vl) != 0) {
+    ERROR("rrdtool plugin: failed to build filename");
     return -1;
+  }
 
   char values[32 * (ds->ds_num + 1)];
-  if (value_list_to_string(values, sizeof(values), ds, vl) != 0)
+  if (value_list_to_string(values, sizeof(values), ds, vl) != 0) {
+    ERROR("rrdtool plugin: failed to build values string");
     return -1;
+  }
 
   struct stat statbuf = {0};
   if (stat(filename, &statbuf) == -1) {
     if (errno == ENOENT) {
       if (cu_rrd_create_file(filename, ds, vl, &rrdcreate_config) != 0) {
+        ERROR("rrdtool plugin: cu_rrd_create_file (%s) failed.", filename);
         return -1;
       } else if (rrdcreate_config.async) {
         return 0;
       }
     } else {
-      char errbuf[1024];
-      ERROR("rrdtool plugin: stat(%s) failed: %s", filename,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("rrdtool plugin: stat(%s) failed: %s", filename, STRERRNO);
       return -1;
     }
   } else if (!S_ISREG(statbuf.st_mode)) {
@@ -1021,7 +1016,7 @@ static int rrd_shutdown(void) {
 } /* int rrd_shutdown */
 
 static int rrd_init(void) {
-  static int init_once = 0;
+  static int init_once;
 
   if (init_once != 0)
     return 0;
index 33982e0..8d1ece9 100644 (file)
@@ -35,9 +35,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #if defined(HAVE_SENSORS_SENSORS_H)
 #include <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);
@@ -157,43 +73,13 @@ typedef struct featurelist {
   struct featurelist *next;
 } featurelist_t;
 
-static char *conffile = NULL;
-static _Bool use_labels = 0;
+static char *conffile;
+static bool use_labels;
 #endif
 
-static featurelist_t *first_feature = NULL;
+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);
@@ -220,7 +106,7 @@ static int sensors_config(const char *key, const char *value) {
   }
 #if (SENSORS_API_VERSION >= 0x400)
   else if (strcasecmp(key, "UseLabels") == 0) {
-    use_labels = IS_TRUE(value) ? 1 : 0;
+    use_labels = IS_TRUE(value);
   }
 #endif
   else {
@@ -246,7 +132,7 @@ static void sensors_free_features(void) {
 }
 
 static int sensors_load_conf(void) {
-  static int call_once = 0;
+  static int call_once;
 
   FILE *fh = NULL;
   featurelist_t *last_feature = NULL;
@@ -264,9 +150,7 @@ static int sensors_load_conf(void) {
   if (conffile != NULL) {
     fh = fopen(conffile, "r");
     if (fh == NULL) {
-      char errbuf[1024];
-      ERROR("sensors plugin: fopen(%s) failed: %s", conffile,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("sensors plugin: fopen(%s) failed: %s", conffile, STRERRNO);
       return -1;
     }
   }
@@ -365,6 +249,9 @@ static int sensors_load_conf(void) {
 #if SENSORS_API_VERSION >= 0x402
           (feature->type != SENSORS_FEATURE_CURR) &&
 #endif
+#if SENSORS_API_VERSION >= 0x431
+          (feature->type != SENSORS_FEATURE_HUMIDITY) &&
+#endif
           (feature->type != SENSORS_FEATURE_POWER)) {
         DEBUG("sensors plugin: sensors_load_conf: "
               "Ignoring feature `%s', "
@@ -384,6 +271,9 @@ static int sensors_load_conf(void) {
 #if SENSORS_API_VERSION >= 0x402
             (subfeature->type != SENSORS_SUBFEATURE_CURR_INPUT) &&
 #endif
+#if SENSORS_API_VERSION >= 0x431
+            (subfeature->type != SENSORS_SUBFEATURE_HUMIDITY_INPUT) &&
+#endif
             (subfeature->type != SENSORS_SUBFEATURE_POWER_INPUT))
           continue;
 
@@ -518,6 +408,10 @@ static int sensors_read(void) {
     else if (fl->feature->type == SENSORS_FEATURE_CURR)
       type = "current";
 #endif
+#if SENSORS_API_VERSION >= 0x431
+    else if (fl->feature->type == SENSORS_FEATURE_HUMIDITY)
+      type = "humidity";
+#endif
     else
       continue;
 
index fda8787..aae9978 100644 (file)
@@ -23,8 +23,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if !KERNEL_LINUX
 #error "No applicable input method."
@@ -52,15 +52,15 @@ static int serial_read(void) {
   /* there are a variety of names for the serial device */
   if ((fh = fopen("/proc/tty/driver/serial", "r")) == NULL &&
       (fh = fopen("/proc/tty/driver/ttyS", "r")) == NULL) {
-    char errbuf[1024];
-    WARNING("serial: fopen: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("serial: fopen: %s", STRERRNO);
     return -1;
   }
 
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
     derive_t rx = 0;
     derive_t tx = 0;
-    _Bool have_rx = 0, have_tx = 0;
+    bool have_rx = false;
+    bool have_tx = false;
     size_t len;
 
     char *fields[16];
@@ -79,7 +79,7 @@ static int serial_read(void) {
       continue;
     if (fields[0][len - 1] != ':')
       continue;
-    fields[0][len - 1] = 0;
+    fields[0][len - 1] = '\0';
 
     for (int i = 1; i < numfields; i++) {
       len = strlen(fields[i]);
@@ -88,10 +88,10 @@ static int serial_read(void) {
 
       if (strncmp(fields[i], "tx:", 3) == 0) {
         if (strtoderive(fields[i] + 3, &tx) == 0)
-          have_tx = 1;
+          have_tx = true;
       } else if (strncmp(fields[i], "rx:", 3) == 0) {
         if (strtoderive(fields[i] + 3, &rx) == 0)
-          have_rx = 1;
+          have_rx = true;
       }
     }
 
index 8a325fe..a8b67af 100644 (file)
@@ -21,8 +21,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -340,9 +340,7 @@ static int sigrok_init(void) {
   status = plugin_thread_create(&sr_thread, NULL, sigrok_read_thread, NULL,
                                 "sigrok read");
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("sigrok plugin: Failed to create thread: %s.",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("sigrok plugin: Failed to create thread: %s.", STRERRNO);
     return -1;
   }
   sr_thread_running = TRUE;
index 30680be..2dfb924 100644 (file)
@@ -26,9 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #include <atasmart.h>
 #include <libudev.h>
@@ -42,9 +42,9 @@ static const char *config_keys[] = {"Disk", "IgnoreSelected", "IgnoreSleepMode",
 
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static ignorelist_t *ignorelist = NULL;
-static int ignore_sleep_mode = 0;
-static int use_serial = 0;
+static ignorelist_t *ignorelist;
+static int ignore_sleep_mode;
+static int use_serial;
 
 static int smart_config(const char *key, const char *value) {
   if (ignorelist == NULL)
index 8cb866d..c921e02 100644 (file)
@@ -26,8 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 #include "utils_complain.h"
 
 #include <net-snmp/net-snmp-config.h>
@@ -44,18 +45,24 @@ struct oid_s {
 };
 typedef struct oid_s oid_t;
 
-union instance_u {
-  char string[DATA_MAX_NAME_LEN];
+struct instance_s {
+  bool configured;
   oid_t oid;
+  char *prefix;
+  char *value;
 };
-typedef union instance_u instance_t;
+typedef struct instance_s instance_t;
 
 struct data_definition_s {
   char *name; /* used to reference this from the `Collect' option */
   char *type; /* used to find the data_set */
-  _Bool is_table;
-  instance_t instance;
-  char *instance_prefix;
+  bool is_table;
+  instance_t type_instance;
+  instance_t plugin_instance;
+  instance_t host;
+  oid_t filter_oid;
+  ignorelist_t *ignorelist;
+  char *plugin_name;
   oid_t *values;
   size_t values_len;
   double scale;
@@ -63,7 +70,7 @@ struct data_definition_s {
   struct data_definition_s *next;
   char **ignores;
   size_t ignores_len;
-  _Bool invert_match;
+  bool invert_match;
 };
 typedef struct data_definition_s data_definition_t;
 
@@ -90,7 +97,6 @@ struct host_definition_s {
 
   void *sess_handle;
   c_complain_t complaint;
-  cdtime_t interval;
   data_definition_t **data_list;
   int data_list_len;
 };
@@ -98,24 +104,33 @@ typedef struct host_definition_s host_definition_t;
 
 /* These two types are used to cache values in `csnmp_read_table' to handle
  * gaps in tables. */
-struct csnmp_list_instances_s {
+struct csnmp_cell_char_s {
   oid_t suffix;
-  char instance[DATA_MAX_NAME_LEN];
-  struct csnmp_list_instances_s *next;
+  char value[DATA_MAX_NAME_LEN];
+  struct csnmp_cell_char_s *next;
 };
-typedef struct csnmp_list_instances_s csnmp_list_instances_t;
+typedef struct csnmp_cell_char_s csnmp_cell_char_t;
 
-struct csnmp_table_values_s {
+struct csnmp_cell_value_s {
   oid_t suffix;
   value_t value;
-  struct csnmp_table_values_s *next;
+  struct csnmp_cell_value_s *next;
 };
-typedef struct csnmp_table_values_s csnmp_table_values_t;
+typedef struct csnmp_cell_value_s csnmp_cell_value_t;
+
+typedef enum {
+  OID_TYPE_SKIP = 0,
+  OID_TYPE_VARIABLE,
+  OID_TYPE_TYPEINSTANCE,
+  OID_TYPE_PLUGININSTANCE,
+  OID_TYPE_HOST,
+  OID_TYPE_FILTER,
+} csnmp_oid_type_t;
 
 /*
  * Private variables
  */
-static data_definition_t *data_head = NULL;
+static data_definition_t *data_head;
 
 /*
  * Prototypes
@@ -199,6 +214,22 @@ static void csnmp_host_definition_destroy(void *arg) /* {{{ */
   sfree(hd);
 } /* }}} void csnmp_host_definition_destroy */
 
+static void csnmp_data_definition_destroy(data_definition_t *dd) {
+  sfree(dd->name);
+  sfree(dd->type);
+  sfree(dd->plugin_name);
+  sfree(dd->plugin_instance.prefix);
+  sfree(dd->plugin_instance.value);
+  sfree(dd->type_instance.prefix);
+  sfree(dd->type_instance.value);
+  sfree(dd->host.prefix);
+  sfree(dd->host.value);
+  sfree(dd->values);
+  sfree(dd->ignores);
+  ignorelist_free(dd->ignorelist);
+  sfree(dd);
+} /* void csnmp_data_definition_destroy */
+
 /* Many functions to handle the configuration. {{{ */
 /* First there are many functions which do configuration stuff. It's a big
  * bloated and messy, I'm afraid. */
@@ -208,8 +239,7 @@ static void csnmp_host_definition_destroy(void *arg) /* {{{ */
  *  csnmp_config
  *  +-> call_snmp_init_once
  *  +-> csnmp_config_add_data
- *  !   +-> csnmp_config_add_data_instance
- *  !   +-> csnmp_config_add_data_instance_prefix
+ *  !   +-> csnmp_config_configure_data_instance
  *  !   +-> csnmp_config_add_data_values
  *  +-> csnmp_config_add_host
  *      +-> csnmp_config_add_host_version
@@ -219,52 +249,36 @@ static void csnmp_host_definition_destroy(void *arg) /* {{{ */
  *      +-> csnmp_config_add_host_security_level
  */
 static void call_snmp_init_once(void) {
-  static int have_init = 0;
+  static int have_init;
 
   if (have_init == 0)
     init_snmp(PACKAGE_NAME);
   have_init = 1;
 } /* void call_snmp_init_once */
 
-static int csnmp_config_add_data_instance(data_definition_t *dd,
-                                          oconfig_item_t *ci) {
+static int csnmp_config_configure_data_instance(instance_t *instance,
+                                                oconfig_item_t *ci) {
   char buffer[DATA_MAX_NAME_LEN];
-  int status;
 
-  status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
+  int status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
   if (status != 0)
     return status;
 
-  if (dd->is_table) {
-    /* Instance is an OID */
-    dd->instance.oid.oid_len = MAX_OID_LEN;
+  instance->configured = true;
 
-    if (!read_objid(buffer, dd->instance.oid.oid, &dd->instance.oid.oid_len)) {
-      ERROR("snmp plugin: read_objid (%s) failed.", buffer);
-      return -1;
-    }
-  } else {
-    /* Instance is a simple string */
-    sstrncpy(dd->instance.string, buffer, sizeof(dd->instance.string));
+  if (strlen(buffer) == 0) {
+    return 0;
   }
 
-  return 0;
-} /* int csnmp_config_add_data_instance */
+  instance->oid.oid_len = MAX_OID_LEN;
 
-static int csnmp_config_add_data_instance_prefix(data_definition_t *dd,
-                                                 oconfig_item_t *ci) {
-  int status;
-
-  if (!dd->is_table) {
-    WARNING("snmp plugin: data %s: InstancePrefix is ignored when `Table' "
-            "is set to `false'.",
-            dd->name);
+  if (!read_objid(buffer, instance->oid.oid, &instance->oid.oid_len)) {
+    ERROR("snmp plugin: read_objid (%s) failed.", buffer);
     return -1;
   }
 
-  status = cf_util_get_string(ci, &dd->instance_prefix);
-  return status;
-} /* int csnmp_config_add_data_instance_prefix */
+  return 0;
+} /* int csnmp_config_configure_data_instance */
 
 static int csnmp_config_add_data_values(data_definition_t *dd,
                                         oconfig_item_t *ci) {
@@ -301,7 +315,7 @@ static int csnmp_config_add_data_values(data_definition_t *dd,
   }
 
   return 0;
-} /* int csnmp_config_add_data_instance */
+} /* int csnmp_config_configure_data_instance */
 
 static int csnmp_config_add_data_blacklist(data_definition_t *dd,
                                            oconfig_item_t *ci) {
@@ -315,9 +329,6 @@ static int csnmp_config_add_data_blacklist(data_definition_t *dd,
     }
   }
 
-  dd->ignores_len = 0;
-  dd->ignores = NULL;
-
   for (int i = 0; i < ci->values_num; ++i) {
     if (strarray_add(&(dd->ignores), &(dd->ignores_len),
                      ci->values[i].value.string) != 0) {
@@ -329,6 +340,41 @@ static int csnmp_config_add_data_blacklist(data_definition_t *dd,
   return 0;
 } /* int csnmp_config_add_data_blacklist */
 
+static int csnmp_config_add_data_filter_values(data_definition_t *data,
+                                               oconfig_item_t *ci) {
+  if (ci->values_num < 1) {
+    WARNING("snmp plugin: `FilterValues' needs at least one argument.");
+    return -1;
+  }
+
+  for (int i = 0; i < ci->values_num; i++) {
+    if (ci->values[i].type != OCONFIG_TYPE_STRING) {
+      WARNING("snmp plugin: All arguments to `FilterValues' must be strings.");
+      return -1;
+    }
+    ignorelist_add(data->ignorelist, ci->values[i].value.string);
+  }
+
+  return 0;
+} /* int csnmp_config_add_data_filter_values */
+
+static int csnmp_config_add_data_filter_oid(data_definition_t *data,
+                                            oconfig_item_t *ci) {
+
+  char buffer[DATA_MAX_NAME_LEN];
+  int status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
+  if (status != 0)
+    return status;
+
+  data->filter_oid.oid_len = MAX_OID_LEN;
+
+  if (!read_objid(buffer, data->filter_oid.oid, &data->filter_oid.oid_len)) {
+    ERROR("snmp plugin: read_objid (%s) failed.", buffer);
+    return -1;
+  }
+  return 0;
+} /* int csnmp_config_add_data_filter_oid */
+
 static int csnmp_config_add_data(oconfig_item_t *ci) {
   data_definition_t *dd = calloc(1, sizeof(*dd));
   if (dd == NULL)
@@ -342,6 +388,22 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
 
   dd->scale = 1.0;
   dd->shift = 0.0;
+  dd->ignores_len = 0;
+  dd->ignores = NULL;
+
+  dd->ignorelist = ignorelist_create(/* invert = */ 1);
+  if (dd->ignorelist == NULL) {
+    sfree(dd->name);
+    sfree(dd);
+    ERROR("snmp plugin: ignorelist_create() failed.");
+    return ENOMEM;
+  }
+
+  dd->plugin_name = strdup("snmp");
+  if (dd->plugin_name == NULL) {
+    ERROR("snmp plugin: Can't allocate memory");
+    return ENOMEM;
+  }
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
@@ -350,10 +412,47 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
       status = cf_util_get_string(option, &dd->type);
     else if (strcasecmp("Table", option->key) == 0)
       status = cf_util_get_boolean(option, &dd->is_table);
-    else if (strcasecmp("Instance", option->key) == 0)
-      status = csnmp_config_add_data_instance(dd, option);
-    else if (strcasecmp("InstancePrefix", option->key) == 0)
-      status = csnmp_config_add_data_instance_prefix(dd, option);
+    else if (strcasecmp("Plugin", option->key) == 0)
+      status = cf_util_get_string(option, &dd->plugin_name);
+    else if (strcasecmp("Instance", option->key) == 0) {
+      if (dd->is_table) {
+        /* Instance is OID */
+        WARNING(
+            "snmp plugin: data %s: Option `Instance' is deprecated, please use "
+            "option `TypeInstanceOID'.",
+            dd->name);
+        status =
+            csnmp_config_configure_data_instance(&dd->type_instance, option);
+      } else {
+        /* Instance is a simple string */
+        WARNING(
+            "snmp plugin: data %s: Option `Instance' is deprecated, please use "
+            "option `TypeInstance'.",
+            dd->name);
+        status = cf_util_get_string(option, &dd->type_instance.value);
+      }
+    } else if (strcasecmp("InstancePrefix", option->key) == 0) {
+      WARNING("snmp plugin: data %s: Option `InstancePrefix' is deprecated, "
+              "please use option `TypeInstancePrefix'.",
+              dd->name);
+      status = cf_util_get_string(option, &dd->type_instance.prefix);
+    } else if (strcasecmp("PluginInstance", option->key) == 0)
+      status = cf_util_get_string(option, &dd->plugin_instance.value);
+    else if (strcasecmp("TypeInstance", option->key) == 0)
+      status = cf_util_get_string(option, &dd->type_instance.value);
+    else if (strcasecmp("PluginInstanceOID", option->key) == 0)
+      status =
+          csnmp_config_configure_data_instance(&dd->plugin_instance, option);
+    else if (strcasecmp("PluginInstancePrefix", option->key) == 0)
+      status = cf_util_get_string(option, &dd->plugin_instance.prefix);
+    else if (strcasecmp("TypeInstanceOID", option->key) == 0)
+      status = csnmp_config_configure_data_instance(&dd->type_instance, option);
+    else if (strcasecmp("TypeInstancePrefix", option->key) == 0)
+      status = cf_util_get_string(option, &dd->type_instance.prefix);
+    else if (strcasecmp("HostOID", option->key) == 0)
+      status = csnmp_config_configure_data_instance(&dd->host, option);
+    else if (strcasecmp("HostPrefix", option->key) == 0)
+      status = cf_util_get_string(option, &dd->host.prefix);
     else if (strcasecmp("Values", option->key) == 0)
       status = csnmp_config_add_data_values(dd, option);
     else if (strcasecmp("Shift", option->key) == 0)
@@ -364,8 +463,18 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
       status = csnmp_config_add_data_blacklist(dd, option);
     else if (strcasecmp("InvertMatch", option->key) == 0)
       status = cf_util_get_boolean(option, &dd->invert_match);
-    else {
-      WARNING("snmp plugin: Option `%s' not allowed here.", option->key);
+    else if (strcasecmp("FilterOID", option->key) == 0) {
+      status = csnmp_config_add_data_filter_oid(dd, option);
+    } else if (strcasecmp("FilterValues", option->key) == 0) {
+      status = csnmp_config_add_data_filter_values(dd, option);
+    } else if (strcasecmp("FilterIgnoreSelected", option->key) == 0) {
+      bool t;
+      status = cf_util_get_boolean(option, &t);
+      if (status == 0)
+        ignorelist_set_invert(dd->ignorelist, /* invert = */ !t);
+    } else {
+      WARNING("snmp plugin: data %s: Option `%s' not allowed here.", dd->name,
+              option->key);
       status = -1;
     }
 
@@ -374,6 +483,65 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
   } /* for (ci->children) */
 
   while (status == 0) {
+    if (dd->is_table) {
+      /* Set type_instance to SUBID by default */
+      if (!dd->plugin_instance.configured && !dd->host.configured)
+        dd->type_instance.configured = true;
+
+      if (dd->plugin_instance.value && dd->plugin_instance.configured) {
+        WARNING(
+            "snmp plugin: data %s: Option `PluginInstance' will be ignored.",
+            dd->name);
+      }
+      if (dd->type_instance.value && dd->type_instance.configured) {
+        WARNING("snmp plugin: data %s: Option `TypeInstance' will be ignored.",
+                dd->name);
+      }
+      if (dd->type_instance.prefix && !dd->type_instance.configured) {
+        WARNING("snmp plugin: data %s: Option `TypeInstancePrefix' will be "
+                "ignored.",
+                dd->name);
+      }
+      if (dd->plugin_instance.prefix && !dd->plugin_instance.configured) {
+        WARNING("snmp plugin: data %s: Option `PluginInstancePrefix' will be "
+                "ignored.",
+                dd->name);
+      }
+      if (dd->host.prefix && !dd->host.configured) {
+        WARNING("snmp plugin: data %s: Option `HostPrefix' will be ignored.",
+                dd->name);
+      }
+    } else {
+      if (dd->plugin_instance.oid.oid_len > 0) {
+        WARNING("snmp plugin: data %s: Option `PluginInstanceOID' will be "
+                "ignored.",
+                dd->name);
+      }
+      if (dd->type_instance.oid.oid_len > 0) {
+        WARNING(
+            "snmp plugin: data %s: Option `TypeInstanceOID' will be ignored.",
+            dd->name);
+      }
+      if (dd->type_instance.prefix) {
+        WARNING("snmp plugin: data %s: Option `TypeInstancePrefix' is ignored "
+                "when `Table' "
+                "set to `false'.",
+                dd->name);
+      }
+      if (dd->plugin_instance.prefix) {
+        WARNING("snmp plugin: data %s: Option `PluginInstancePrefix' is "
+                "ignored when "
+                "`Table' set to `false'.",
+                dd->name);
+      }
+      if (dd->host.prefix) {
+        WARNING(
+            "snmp plugin: data %s: Option `HostPrefix' is ignored when `Table' "
+            "set to `false'.",
+            dd->name);
+      }
+    }
+
     if (dd->type == NULL) {
       WARNING("snmp plugin: `Type' not given for data `%s'", dd->name);
       status = -1;
@@ -389,18 +557,25 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
   } /* while (status == 0) */
 
   if (status != 0) {
-    sfree(dd->name);
-    sfree(dd->instance_prefix);
-    sfree(dd->values);
-    sfree(dd->ignores);
-    sfree(dd);
+    csnmp_data_definition_destroy(dd);
     return -1;
   }
 
   DEBUG("snmp plugin: dd = { name = %s, type = %s, is_table = %s, values_len = "
-        "%zu }",
-        dd->name, dd->type, (dd->is_table != 0) ? "true" : "false",
-        dd->values_len);
+        "%" PRIsz ",",
+        dd->name, dd->type, (dd->is_table) ? "true" : "false", dd->values_len);
+
+  DEBUG("snmp plugin:        plugin_instance = %s, type_instance = %s,",
+        dd->plugin_instance.value, dd->type_instance.value);
+
+  DEBUG("snmp plugin:        type_instance_by_oid = %s, plugin_instance_by_oid "
+        "= %s }",
+        (dd->type_instance.oid.oid_len > 0)
+            ? "true"
+            : ((dd->type_instance.configured) ? "SUBID" : "false"),
+        (dd->plugin_instance.oid.oid_len > 0)
+            ? "true"
+            : ((dd->plugin_instance.configured) ? "SUBID" : "false"));
 
   if (data_head == NULL)
     data_head = dd;
@@ -434,7 +609,7 @@ static int csnmp_config_add_host_version(host_definition_t *hd,
   hd->version = version;
 
   return 0;
-} /* int csnmp_config_add_host_address */
+} /* int csnmp_config_add_host_version */
 
 static int csnmp_config_add_host_collect(host_definition_t *host,
                                          oconfig_item_t *ci) {
@@ -567,6 +742,7 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
   int status = 0;
 
   /* Registration stuff. */
+  cdtime_t interval = 0;
   char cb_name[DATA_MAX_NAME_LEN];
 
   hd = calloc(1, sizeof(*hd));
@@ -582,7 +758,6 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
   }
 
   hd->sess_handle = NULL;
-  hd->interval = 0;
 
   /* These mean that we have not set a timeout or retry value */
   hd->timeout = 0;
@@ -590,7 +765,6 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
-    status = 0;
 
     if (strcasecmp("Address", option->key) == 0)
       status = cf_util_get_string(option, &hd->address);
@@ -599,13 +773,13 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
     else if (strcasecmp("Version", option->key) == 0)
       status = csnmp_config_add_host_version(hd, option);
     else if (strcasecmp("Timeout", option->key) == 0)
-      cf_util_get_cdtime(option, &hd->timeout);
+      status = cf_util_get_cdtime(option, &hd->timeout);
     else if (strcasecmp("Retries", option->key) == 0)
-      cf_util_get_int(option, &hd->retries);
+      status = cf_util_get_int(option, &hd->retries);
     else if (strcasecmp("Collect", option->key) == 0)
-      csnmp_config_add_host_collect(hd, option);
+      status = csnmp_config_add_host_collect(hd, option);
     else if (strcasecmp("Interval", option->key) == 0)
-      cf_util_get_cdtime(option, &hd->interval);
+      status = cf_util_get_cdtime(option, &interval);
     else if (strcasecmp("Username", option->key) == 0)
       status = cf_util_get_string(option, &hd->username);
     else if (strcasecmp("AuthProtocol", option->key) == 0)
@@ -700,7 +874,7 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
   snprintf(cb_name, sizeof(cb_name), "snmp-%s", hd->name);
 
   status = plugin_register_complex_read(
-      /* group = */ NULL, cb_name, csnmp_read_host, hd->interval,
+      /* group = */ NULL, cb_name, csnmp_read_host, interval,
       &(user_data_t){
           .data = hd, .free_func = csnmp_host_definition_destroy,
       });
@@ -823,16 +997,16 @@ static void csnmp_host_open_session(host_definition_t *host) {
 
 /* TODO: Check if negative values wrap around. Problem: negative temperatures.
  */
-static value_t csnmp_value_list_to_value(struct variable_list *vl, int type,
-                                         double scale, double shift,
+static value_t csnmp_value_list_to_value(const struct variable_list *vl,
+                                         int type, double scale, double shift,
                                          const char *host_name,
                                          const char *data_name) {
   value_t ret;
   uint64_t tmp_unsigned = 0;
   int64_t tmp_signed = 0;
-  _Bool defined = 1;
+  bool defined = 1;
   /* Set to true when the original SNMP type appears to have been signed. */
-  _Bool prefer_signed = 0;
+  bool prefer_signed = 0;
 
   if ((vl->type == ASN_INTEGER) || (vl->type == ASN_UINTEGER) ||
       (vl->type == ASN_COUNTER)
@@ -972,7 +1146,7 @@ static int csnmp_strvbcopy_hexstring(char *dst, /* {{{ */
 
     if (((size_t)status) >= buffer_free) /* truncated */
     {
-      dst[dst_size - 1] = 0;
+      dst[dst_size - 1] = '\0';
       return ENOMEM;
     } else /* if (status < buffer_free) */
     {
@@ -1019,7 +1193,7 @@ static int csnmp_strvbcopy(char *dst, /* {{{ */
     dst[i] = src[i];
   }
   dst[num_chars] = 0;
-  dst[dst_size - 1] = 0;
+  dst[dst_size - 1] = '\0';
 
   if (dst_size <= vb->val_len)
     return ENOMEM;
@@ -1027,98 +1201,102 @@ static int csnmp_strvbcopy(char *dst, /* {{{ */
   return 0;
 } /* }}} int csnmp_strvbcopy */
 
-static int csnmp_instance_list_add(csnmp_list_instances_t **head,
-                                   csnmp_list_instances_t **tail,
-                                   const struct snmp_pdu *res,
-                                   const host_definition_t *hd,
-                                   const data_definition_t *dd) {
-  csnmp_list_instances_t *il;
-  struct variable_list *vb;
-  oid_t vb_name;
-  int status;
+static csnmp_cell_char_t *csnmp_get_char_cell(const struct variable_list *vb,
+                                              const oid_t *root_oid,
+                                              const host_definition_t *hd,
+                                              const data_definition_t *dd) {
 
-  /* Set vb on the last variable */
-  for (vb = res->variables; (vb != NULL) && (vb->next_variable != NULL);
-       vb = vb->next_variable)
-    /* do nothing */;
   if (vb == NULL)
-    return -1;
-
-  csnmp_oid_init(&vb_name, vb->name, vb->name_length);
+    return NULL;
 
-  il = calloc(1, sizeof(*il));
+  csnmp_cell_char_t *il = calloc(1, sizeof(*il));
   if (il == NULL) {
     ERROR("snmp plugin: calloc failed.");
-    return -1;
+    return NULL;
   }
   il->next = NULL;
 
-  status = csnmp_oid_suffix(&il->suffix, &vb_name, &dd->instance.oid);
-  if (status != 0) {
+  oid_t vb_name;
+  csnmp_oid_init(&vb_name, vb->name, vb->name_length);
+
+  if (csnmp_oid_suffix(&il->suffix, &vb_name, root_oid) != 0) {
     sfree(il);
-    return status;
+    return NULL;
   }
 
-  /* Get instance name */
+  /* Get value */
   if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR) ||
       (vb->type == ASN_IPADDRESS)) {
-    char *ptr;
-
-    csnmp_strvbcopy(il->instance, vb, sizeof(il->instance));
-    _Bool is_matched = 0;
-    for (uint32_t i = 0; i < dd->ignores_len; i++) {
-      status = fnmatch(dd->ignores[i], il->instance, 0);
-      if (status == 0) {
-        if (!dd->invert_match) {
-          sfree(il);
-          return 0;
-        } else {
-          is_matched = 1;
-          break;
-        }
-      }
-    }
-    if (dd->invert_match && !is_matched) {
-      sfree(il);
-      return 0;
-    }
-    for (ptr = il->instance; *ptr != '\0'; ptr++) {
-      if ((*ptr > 0) && (*ptr < 32))
-        *ptr = ' ';
-      else if (*ptr == '/')
-        *ptr = '_';
-    }
-    DEBUG("snmp plugin: il->instance = `%s';", il->instance);
+
+    csnmp_strvbcopy(il->value, vb, sizeof(il->value));
+
   } else {
     value_t val = csnmp_value_list_to_value(
         vb, DS_TYPE_COUNTER,
         /* scale = */ 1.0, /* shift = */ 0.0, hd->name, dd->name);
-    snprintf(il->instance, sizeof(il->instance), "%llu", val.counter);
+    snprintf(il->value, sizeof(il->value), "%" PRIu64, (uint64_t)val.counter);
   }
 
-  /* TODO: Debugging output */
+  return il;
+} /* csnmp_cell_char_t csnmp_get_char_cell */
 
+static void csnmp_cells_append(csnmp_cell_char_t **head,
+                               csnmp_cell_char_t **tail,
+                               csnmp_cell_char_t *il) {
   if (*head == NULL)
     *head = il;
   else
     (*tail)->next = il;
   *tail = il;
-
+} /* void csnmp_cells_append */
+
+static bool csnmp_ignore_instance(csnmp_cell_char_t *cell,
+                                  const data_definition_t *dd) {
+  bool is_matched = 0;
+  for (uint32_t i = 0; i < dd->ignores_len; i++) {
+    int status = fnmatch(dd->ignores[i], cell->value, 0);
+    if (status == 0) {
+      if (!dd->invert_match) {
+        return 1;
+      } else {
+        is_matched = 1;
+        break;
+      }
+    }
+  }
+  if (dd->invert_match && !is_matched) {
+    return 1;
+  }
   return 0;
-} /* int csnmp_instance_list_add */
+} /* bool csnmp_ignore_instance */
+
+static void csnmp_cell_replace_reserved_chars(csnmp_cell_char_t *cell) {
+  for (char *ptr = cell->value; *ptr != '\0'; ptr++) {
+    if ((*ptr > 0) && (*ptr < 32))
+      *ptr = ' ';
+    else if (*ptr == '/')
+      *ptr = '_';
+  }
+} /* void csnmp_cell_replace_reserved_chars */
 
 static int csnmp_dispatch_table(host_definition_t *host,
                                 data_definition_t *data,
-                                csnmp_list_instances_t *instance_list,
-                                csnmp_table_values_t **value_table) {
+                                csnmp_cell_char_t *type_instance_cells,
+                                csnmp_cell_char_t *plugin_instance_cells,
+                                csnmp_cell_char_t *hostname_cells,
+                                csnmp_cell_char_t *filter_cells,
+                                csnmp_cell_value_t **value_cells) {
   const data_set_t *ds;
   value_list_t vl = VALUE_LIST_INIT;
 
-  csnmp_list_instances_t *instance_list_ptr;
-  csnmp_table_values_t *value_table_ptr[data->values_len];
+  csnmp_cell_char_t *type_instance_cell_ptr = type_instance_cells;
+  csnmp_cell_char_t *plugin_instance_cell_ptr = plugin_instance_cells;
+  csnmp_cell_char_t *hostname_cell_ptr = hostname_cells;
+  csnmp_cell_char_t *filter_cell_ptr = filter_cells;
+  csnmp_cell_value_t *value_cell_ptr[data->values_len];
 
   size_t i;
-  _Bool have_more;
+  bool have_more;
   oid_t current_suffix;
 
   ds = plugin_get_ds(data->type);
@@ -1129,32 +1307,28 @@ static int csnmp_dispatch_table(host_definition_t *host,
   assert(ds->ds_num == data->values_len);
   assert(data->values_len > 0);
 
-  instance_list_ptr = instance_list;
-
   for (i = 0; i < data->values_len; i++)
-    value_table_ptr[i] = value_table[i];
-
-  sstrncpy(vl.host, host->name, sizeof(vl.host));
-  sstrncpy(vl.plugin, "snmp", sizeof(vl.plugin));
+    value_cell_ptr[i] = value_cells[i];
 
-  vl.interval = host->interval;
+  sstrncpy(vl.plugin, data->plugin_name, sizeof(vl.plugin));
+  sstrncpy(vl.type, data->type, sizeof(vl.type));
 
   have_more = 1;
   while (have_more) {
-    _Bool suffix_skipped = 0;
+    bool suffix_skipped = 0;
 
     /* Determine next suffix to handle. */
-    if (instance_list != NULL) {
-      if (instance_list_ptr == NULL) {
+    if (type_instance_cells != NULL) {
+      if (type_instance_cell_ptr == NULL) {
         have_more = 0;
         continue;
       }
 
-      memcpy(&current_suffix, &instance_list_ptr->suffix,
+      memcpy(&current_suffix, &type_instance_cell_ptr->suffix,
              sizeof(current_suffix));
     } else {
       /* no instance configured */
-      csnmp_table_values_t *ptr = value_table_ptr[0];
+      csnmp_cell_value_t *ptr = value_cell_ptr[0];
       if (ptr == NULL) {
         have_more = 0;
         continue;
@@ -1163,18 +1337,82 @@ static int csnmp_dispatch_table(host_definition_t *host,
       memcpy(&current_suffix, &ptr->suffix, sizeof(current_suffix));
     }
 
-    /* Update all the value_table_ptr to point at the entry with the same
+    /*
+    char oid_buffer[1024] = {0};
+    snprint_objid(oid_buffer, sizeof(oid_buffer) - 1, current_suffix.oid,
+                          current_suffix.oid_len);
+    DEBUG("SNMP PLUGIN: SUFFIX %s", oid_buffer);
+    */
+
+    /* Update plugin_instance_cell_ptr to point expected suffix */
+    if (plugin_instance_cells != NULL) {
+      while ((plugin_instance_cell_ptr != NULL) &&
+             (csnmp_oid_compare(&plugin_instance_cell_ptr->suffix,
+                                &current_suffix) < 0))
+        plugin_instance_cell_ptr = plugin_instance_cell_ptr->next;
+
+      if (plugin_instance_cell_ptr == NULL) {
+        have_more = 0;
+        continue;
+      }
+
+      if (csnmp_oid_compare(&plugin_instance_cell_ptr->suffix,
+                            &current_suffix) > 0) {
+        /* This suffix is missing in the subtree. Indicate this with the
+         * "suffix_skipped" flag and try the next instance / suffix. */
+        suffix_skipped = 1;
+      }
+    }
+
+    /* Update hostname_cell_ptr to point expected suffix */
+    if (hostname_cells != NULL) {
+      while (
+          (hostname_cell_ptr != NULL) &&
+          (csnmp_oid_compare(&hostname_cell_ptr->suffix, &current_suffix) < 0))
+        hostname_cell_ptr = hostname_cell_ptr->next;
+
+      if (hostname_cell_ptr == NULL) {
+        have_more = 0;
+        continue;
+      }
+
+      if (csnmp_oid_compare(&hostname_cell_ptr->suffix, &current_suffix) > 0) {
+        /* This suffix is missing in the subtree. Indicate this with the
+         * "suffix_skipped" flag and try the next instance / suffix. */
+        suffix_skipped = 1;
+      }
+    }
+
+    /* Update filter_cell_ptr to point expected suffix */
+    if (filter_cells != NULL) {
+      while ((filter_cell_ptr != NULL) &&
+             (csnmp_oid_compare(&filter_cell_ptr->suffix, &current_suffix) < 0))
+        filter_cell_ptr = filter_cell_ptr->next;
+
+      if (filter_cell_ptr == NULL) {
+        have_more = 0;
+        continue;
+      }
+
+      if (csnmp_oid_compare(&filter_cell_ptr->suffix, &current_suffix) > 0) {
+        /* This suffix is missing in the subtree. Indicate this with the
+         * "suffix_skipped" flag and try the next instance / suffix. */
+        suffix_skipped = 1;
+      }
+    }
+
+    /* Update all the value_cell_ptr to point at the entry with the same
      * trailing partial OID */
     for (i = 0; i < data->values_len; i++) {
       while (
-          (value_table_ptr[i] != NULL) &&
-          (csnmp_oid_compare(&value_table_ptr[i]->suffix, &current_suffix) < 0))
-        value_table_ptr[i] = value_table_ptr[i]->next;
+          (value_cell_ptr[i] != NULL) &&
+          (csnmp_oid_compare(&value_cell_ptr[i]->suffix, &current_suffix) < 0))
+        value_cell_ptr[i] = value_cell_ptr[i]->next;
 
-      if (value_table_ptr[i] == NULL) {
+      if (value_cell_ptr[i] == NULL) {
         have_more = 0;
         break;
-      } else if (csnmp_oid_compare(&value_table_ptr[i]->suffix,
+      } else if (csnmp_oid_compare(&value_cell_ptr[i]->suffix,
                                    &current_suffix) > 0) {
         /* This suffix is missing in the subtree. Indicate this with the
          * "suffix_skipped" flag and try the next instance / suffix. */
@@ -1188,43 +1426,98 @@ static int csnmp_dispatch_table(host_definition_t *host,
 
     /* Matching the values failed. Start from the beginning again. */
     if (suffix_skipped) {
-      if (instance_list != NULL)
-        instance_list_ptr = instance_list_ptr->next;
+      if (type_instance_cells != NULL)
+        type_instance_cell_ptr = type_instance_cell_ptr->next;
       else
-        value_table_ptr[0] = value_table_ptr[0]->next;
+        value_cell_ptr[0] = value_cell_ptr[0]->next;
 
       continue;
     }
 
-/* if we reach this line, all value_table_ptr[i] are non-NULL and are set
- * to the same subid. instance_list_ptr is either NULL or points to the
+/* if we reach this line, all value_cell_ptr[i] are non-NULL and are set
+ * to the same subid. type_instance_cell_ptr is either NULL or points to the
  * same subid, too. */
 #if COLLECT_DEBUG
     for (i = 1; i < data->values_len; i++) {
-      assert(value_table_ptr[i] != NULL);
-      assert(csnmp_oid_compare(&value_table_ptr[i - 1]->suffix,
-                               &value_table_ptr[i]->suffix) == 0);
+      assert(value_cell_ptr[i] != NULL);
+      assert(csnmp_oid_compare(&value_cell_ptr[i - 1]->suffix,
+                               &value_cell_ptr[i]->suffix) == 0);
     }
-    assert((instance_list_ptr == NULL) ||
-           (csnmp_oid_compare(&instance_list_ptr->suffix,
-                              &value_table_ptr[0]->suffix) == 0));
+    assert((type_instance_cell_ptr == NULL) ||
+           (csnmp_oid_compare(&type_instance_cell_ptr->suffix,
+                              &value_cell_ptr[0]->suffix) == 0));
+    assert((plugin_instance_cell_ptr == NULL) ||
+           (csnmp_oid_compare(&plugin_instance_cell_ptr->suffix,
+                              &value_cell_ptr[0]->suffix) == 0));
+    assert((hostname_cell_ptr == NULL) ||
+           (csnmp_oid_compare(&hostname_cell_ptr->suffix,
+                              &value_cell_ptr[0]->suffix) == 0));
+    assert((filter_cell_ptr == NULL) ||
+           (csnmp_oid_compare(&filter_cell_ptr->suffix,
+                              &value_cell_ptr[0]->suffix) == 0));
 #endif
 
-    sstrncpy(vl.type, data->type, sizeof(vl.type));
+    /* Check the value in filter column */
+    if (filter_cell_ptr &&
+        ignorelist_match(data->ignorelist, filter_cell_ptr->value) != 0) {
+      if (type_instance_cells != NULL)
+        type_instance_cell_ptr = type_instance_cell_ptr->next;
+      else
+        value_cell_ptr[0] = value_cell_ptr[0]->next;
+
+      continue;
+    }
 
-    {
+    /* set vl.host */
+    if (data->host.configured) {
       char temp[DATA_MAX_NAME_LEN];
+      if (hostname_cell_ptr == NULL)
+        csnmp_oid_to_string(temp, sizeof(temp), &current_suffix);
+      else
+        sstrncpy(temp, hostname_cell_ptr->value, sizeof(temp));
 
-      if (instance_list_ptr == NULL)
+      if (data->host.prefix == NULL)
+        sstrncpy(vl.host, temp, sizeof(vl.host));
+      else
+        snprintf(vl.host, sizeof(vl.host), "%s%s", data->host.prefix, temp);
+    } else {
+      sstrncpy(vl.host, host->name, sizeof(vl.host));
+    }
+
+    /* set vl.type_instance */
+    if (data->type_instance.configured) {
+      char temp[DATA_MAX_NAME_LEN];
+      if (type_instance_cell_ptr == NULL)
         csnmp_oid_to_string(temp, sizeof(temp), &current_suffix);
       else
-        sstrncpy(temp, instance_list_ptr->instance, sizeof(temp));
+        sstrncpy(temp, type_instance_cell_ptr->value, sizeof(temp));
 
-      if (data->instance_prefix == NULL)
+      if (data->type_instance.prefix == NULL)
         sstrncpy(vl.type_instance, temp, sizeof(vl.type_instance));
       else
         snprintf(vl.type_instance, sizeof(vl.type_instance), "%s%s",
-                 data->instance_prefix, temp);
+                 data->type_instance.prefix, temp);
+    } else if (data->type_instance.value) {
+      sstrncpy(vl.type_instance, data->type_instance.value,
+               sizeof(vl.type_instance));
+    }
+
+    /* set vl.plugin_instance */
+    if (data->plugin_instance.configured) {
+      char temp[DATA_MAX_NAME_LEN];
+      if (plugin_instance_cell_ptr == NULL)
+        csnmp_oid_to_string(temp, sizeof(temp), &current_suffix);
+      else
+        sstrncpy(temp, plugin_instance_cell_ptr->value, sizeof(temp));
+
+      if (data->plugin_instance.prefix == NULL)
+        sstrncpy(vl.plugin_instance, temp, sizeof(vl.plugin_instance));
+      else
+        snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s%s",
+                 data->plugin_instance.prefix, temp);
+    } else if (data->plugin_instance.value) {
+      sstrncpy(vl.plugin_instance, data->plugin_instance.value,
+               sizeof(vl.plugin_instance));
     }
 
     vl.values_len = data->values_len;
@@ -1232,26 +1525,21 @@ static int csnmp_dispatch_table(host_definition_t *host,
     vl.values = values;
 
     for (i = 0; i < data->values_len; i++)
-      vl.values[i] = value_table_ptr[i]->value;
+      vl.values[i] = value_cell_ptr[i]->value;
 
-    /* If we get here `vl.type_instance' and all `vl.values' have been set
-     * vl.type_instance can be empty, i.e. a blank port description on a
-     * switch if you're using IF-MIB::ifDescr as Instance.
-     */
-    if (vl.type_instance[0] != '\0')
-      plugin_dispatch_values(&vl);
+    plugin_dispatch_values(&vl);
 
     /* prevent leakage of pointer to local variable. */
     vl.values_len = 0;
     vl.values = NULL;
 
-    if (instance_list != NULL)
-      instance_list_ptr = instance_list_ptr->next;
+    if (type_instance_cells != NULL)
+      type_instance_cell_ptr = type_instance_cell_ptr->next;
     else
-      value_table_ptr[0] = value_table_ptr[0]->next;
+      value_cell_ptr[0] = value_cell_ptr[0]->next;
   } /* while (have_more) */
 
-  return (0);
+  return 0;
 } /* int csnmp_dispatch_table */
 
 static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
@@ -1261,25 +1549,43 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
 
   const data_set_t *ds;
 
-  size_t oid_list_len = data->values_len + 1;
+  size_t oid_list_len = data->values_len;
+
+  if (data->type_instance.oid.oid_len > 0)
+    oid_list_len++;
+
+  if (data->plugin_instance.oid.oid_len > 0)
+    oid_list_len++;
+
+  if (data->host.oid.oid_len > 0)
+    oid_list_len++;
+
+  if (data->filter_oid.oid_len > 0)
+    oid_list_len++;
+
   /* Holds the last OID returned by the device. We use this in the GETNEXT
    * request to proceed. */
   oid_t oid_list[oid_list_len];
   /* Set to false when an OID has left its subtree so we don't re-request it
    * again. */
-  _Bool oid_list_todo[oid_list_len];
+  csnmp_oid_type_t oid_list_todo[oid_list_len];
 
   int status;
   size_t i;
 
-  /* `value_list_head' and `value_list_tail' implement a linked list for each
-   * value. `instance_list_head' and `instance_list_tail' implement a linked
-   * list of
-   * instance names. This is used to jump gaps in the table. */
-  csnmp_list_instances_t *instance_list_head;
-  csnmp_list_instances_t *instance_list_tail;
-  csnmp_table_values_t **value_list_head;
-  csnmp_table_values_t **value_list_tail;
+  /* `value_list_head' and `value_cells_tail' implement a linked list for each
+   * value. `instance_cells_head' and `instance_cells_tail' implement a linked
+   * list of instance names. This is used to jump gaps in the table. */
+  csnmp_cell_char_t *type_instance_cells_head = NULL;
+  csnmp_cell_char_t *type_instance_cells_tail = NULL;
+  csnmp_cell_char_t *plugin_instance_cells_head = NULL;
+  csnmp_cell_char_t *plugin_instance_cells_tail = NULL;
+  csnmp_cell_char_t *hostname_cells_head = NULL;
+  csnmp_cell_char_t *hostname_cells_tail = NULL;
+  csnmp_cell_char_t *filter_cells_head = NULL;
+  csnmp_cell_char_t *filter_cells_tail = NULL;
+  csnmp_cell_value_t **value_cells_head;
+  csnmp_cell_value_t **value_cells_tail;
 
   DEBUG("snmp plugin: csnmp_read_table (host = %s, data = %s)", host->name,
         data->name);
@@ -1296,38 +1602,56 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
   }
 
   if (ds->ds_num != data->values_len) {
-    ERROR("snmp plugin: DataSet `%s' requires %zu values, but config talks "
-          "about %zu",
+    ERROR("snmp plugin: DataSet `%s' requires %" PRIsz
+          " values, but config talks "
+          "about %" PRIsz,
           data->type, ds->ds_num, data->values_len);
     return -1;
   }
   assert(data->values_len > 0);
 
+  for (i = 0; i < data->values_len; i++)
+    oid_list_todo[i] = OID_TYPE_VARIABLE;
+
   /* We need a copy of all the OIDs, because GETNEXT will destroy them. */
   memcpy(oid_list, data->values, data->values_len * sizeof(oid_t));
-  if (data->instance.oid.oid_len > 0)
-    memcpy(oid_list + data->values_len, &data->instance.oid, sizeof(oid_t));
-  else /* no InstanceFrom option specified. */
-    oid_list_len--;
 
-  for (i = 0; i < oid_list_len; i++)
-    oid_list_todo[i] = 1;
+  if (data->type_instance.oid.oid_len > 0) {
+    memcpy(oid_list + i, &data->type_instance.oid, sizeof(oid_t));
+    oid_list_todo[i] = OID_TYPE_TYPEINSTANCE;
+    i++;
+  }
+
+  if (data->plugin_instance.oid.oid_len > 0) {
+    memcpy(oid_list + i, &data->plugin_instance.oid, sizeof(oid_t));
+    oid_list_todo[i] = OID_TYPE_PLUGININSTANCE;
+    i++;
+  }
+
+  if (data->host.oid.oid_len > 0) {
+    memcpy(oid_list + i, &data->host.oid, sizeof(oid_t));
+    oid_list_todo[i] = OID_TYPE_HOST;
+    i++;
+  }
+
+  if (data->filter_oid.oid_len > 0) {
+    memcpy(oid_list + i, &data->filter_oid, sizeof(oid_t));
+    oid_list_todo[i] = OID_TYPE_FILTER;
+    i++;
+  }
 
   /* We're going to construct n linked lists, one for each "value".
-   * value_list_head will contain pointers to the heads of these linked lists,
-   * value_list_tail will contain pointers to the tail of the lists. */
-  value_list_head = calloc(data->values_len, sizeof(*value_list_head));
-  value_list_tail = calloc(data->values_len, sizeof(*value_list_tail));
-  if ((value_list_head == NULL) || (value_list_tail == NULL)) {
+   * value_cells_head will contain pointers to the heads of these linked lists,
+   * value_cells_tail will contain pointers to the tail of the lists. */
+  value_cells_head = calloc(data->values_len, sizeof(*value_cells_head));
+  value_cells_tail = calloc(data->values_len, sizeof(*value_cells_tail));
+  if ((value_cells_head == NULL) || (value_cells_tail == NULL)) {
     ERROR("snmp plugin: csnmp_read_table: calloc failed.");
-    sfree(value_list_head);
-    sfree(value_list_tail);
+    sfree(value_cells_head);
+    sfree(value_cells_tail);
     return -1;
   }
 
-  instance_list_head = NULL;
-  instance_list_tail = NULL;
-
   status = 0;
   while (status == 0) {
     req = snmp_pdu_create(SNMP_MSG_GETNEXT);
@@ -1440,30 +1764,126 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
       }
 
       /* An instance is configured and the res variable we process is the
-       * instance value (last index) */
-      if ((data->instance.oid.oid_len > 0) && (i == data->values_len)) {
+       * instance value */
+      if (oid_list_todo[i] == OID_TYPE_TYPEINSTANCE) {
         if ((vb->type == SNMP_ENDOFMIBVIEW) ||
-            (snmp_oid_ncompare(
-                 data->instance.oid.oid, data->instance.oid.oid_len, vb->name,
-                 vb->name_length, data->instance.oid.oid_len) != 0)) {
-          DEBUG("snmp plugin: host = %s; data = %s; Instance left its subtree.",
+            (snmp_oid_ncompare(data->type_instance.oid.oid,
+                               data->type_instance.oid.oid_len, vb->name,
+                               vb->name_length,
+                               data->type_instance.oid.oid_len) != 0)) {
+          DEBUG("snmp plugin: host = %s; data = %s; TypeInstance left its "
+                "subtree.",
                 host->name, data->name);
           oid_list_todo[i] = 0;
           continue;
         }
 
-        /* Allocate a new `csnmp_list_instances_t', insert the instance name and
+        /* Allocate a new `csnmp_cell_char_t', insert the instance name and
          * add it to the list */
-        if (csnmp_instance_list_add(&instance_list_head, &instance_list_tail,
-                                    res, host, data) != 0) {
-          ERROR("snmp plugin: host %s: csnmp_instance_list_add failed.",
+        csnmp_cell_char_t *cell =
+            csnmp_get_char_cell(vb, &data->type_instance.oid, host, data);
+        if (cell == NULL) {
+          ERROR("snmp plugin: host %s: csnmp_get_char_cell() failed.",
                 host->name);
           status = -1;
           break;
         }
+
+        if (csnmp_ignore_instance(cell, data)) {
+          sfree(cell);
+        } else {
+          csnmp_cell_replace_reserved_chars(cell);
+
+          DEBUG("snmp plugin: il->type_instance = `%s';", cell->value);
+          csnmp_cells_append(&type_instance_cells_head,
+                             &type_instance_cells_tail, cell);
+        }
+      } else if (oid_list_todo[i] == OID_TYPE_PLUGININSTANCE) {
+        if ((vb->type == SNMP_ENDOFMIBVIEW) ||
+            (snmp_oid_ncompare(data->plugin_instance.oid.oid,
+                               data->plugin_instance.oid.oid_len, vb->name,
+                               vb->name_length,
+                               data->plugin_instance.oid.oid_len) != 0)) {
+          DEBUG("snmp plugin: host = %s; data = %s; TypeInstance left its "
+                "subtree.",
+                host->name, data->name);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        /* Allocate a new `csnmp_cell_char_t', insert the instance name and
+         * add it to the list */
+        csnmp_cell_char_t *cell =
+            csnmp_get_char_cell(vb, &data->plugin_instance.oid, host, data);
+        if (cell == NULL) {
+          ERROR("snmp plugin: host %s: csnmp_get_char_cell() failed.",
+                host->name);
+          status = -1;
+          break;
+        }
+
+        csnmp_cell_replace_reserved_chars(cell);
+
+        DEBUG("snmp plugin: il->plugin_instance = `%s';", cell->value);
+        csnmp_cells_append(&plugin_instance_cells_head,
+                           &plugin_instance_cells_tail, cell);
+      } else if (oid_list_todo[i] == OID_TYPE_HOST) {
+        if ((vb->type == SNMP_ENDOFMIBVIEW) ||
+            (snmp_oid_ncompare(data->host.oid.oid, data->host.oid.oid_len,
+                               vb->name, vb->name_length,
+                               data->host.oid.oid_len) != 0)) {
+          DEBUG("snmp plugin: host = %s; data = %s; Host left its subtree.",
+                host->name, data->name);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        /* Allocate a new `csnmp_cell_char_t', insert the instance name and
+         * add it to the list */
+        csnmp_cell_char_t *cell =
+            csnmp_get_char_cell(vb, &data->host.oid, host, data);
+        if (cell == NULL) {
+          ERROR("snmp plugin: host %s: csnmp_get_char_cell() failed.",
+                host->name);
+          status = -1;
+          break;
+        }
+
+        csnmp_cell_replace_reserved_chars(cell);
+
+        DEBUG("snmp plugin: il->hostname = `%s';", cell->value);
+        csnmp_cells_append(&hostname_cells_head, &hostname_cells_tail, cell);
+      } else if (oid_list_todo[i] == OID_TYPE_FILTER) {
+        if ((vb->type == SNMP_ENDOFMIBVIEW) ||
+            (snmp_oid_ncompare(data->filter_oid.oid, data->filter_oid.oid_len,
+                               vb->name, vb->name_length,
+                               data->filter_oid.oid_len) != 0)) {
+          DEBUG("snmp plugin: host = %s; data = %s; Host left its subtree.",
+                host->name, data->name);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        /* Allocate a new `csnmp_cell_char_t', insert the instance name and
+         * add it to the list */
+        csnmp_cell_char_t *cell =
+            csnmp_get_char_cell(vb, &data->filter_oid, host, data);
+        if (cell == NULL) {
+          ERROR("snmp plugin: host %s: csnmp_get_char_cell() failed.",
+                host->name);
+          status = -1;
+          break;
+        }
+
+        csnmp_cell_replace_reserved_chars(cell);
+
+        DEBUG("snmp plugin: il->filter = `%s';", cell->value);
+        csnmp_cells_append(&filter_cells_head, &filter_cells_tail, cell);
       } else /* The variable we are processing is a normal value */
       {
-        csnmp_table_values_t *vt;
+        assert(oid_list_todo[i] == OID_TYPE_VARIABLE);
+
+        csnmp_cell_value_t *vt;
         oid_t vb_name;
         oid_t suffix;
         int ret;
@@ -1474,7 +1894,7 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
          * suffix is increasing. This also checks if we left the subtree */
         ret = csnmp_oid_suffix(&suffix, &vb_name, data->values + i);
         if (ret != 0) {
-          DEBUG("snmp plugin: host = %s; data = %s; i = %zu; "
+          DEBUG("snmp plugin: host = %s; data = %s; i = %" PRIsz "; "
                 "Value probably left its subtree.",
                 host->name, data->name, i);
           oid_list_todo[i] = 0;
@@ -1482,11 +1902,10 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
         }
 
         /* Make sure the OIDs returned by the agent are increasing. Otherwise
-         * our
-         * table matching algorithm will get confused. */
-        if ((value_list_tail[i] != NULL) &&
-            (csnmp_oid_compare(&suffix, &value_list_tail[i]->suffix) <= 0)) {
-          DEBUG("snmp plugin: host = %s; data = %s; i = %zu; "
+         * our table matching algorithm will get confused. */
+        if ((value_cells_tail[i] != NULL) &&
+            (csnmp_oid_compare(&suffix, &value_cells_tail[i]->suffix) <= 0)) {
+          DEBUG("snmp plugin: host = %s; data = %s; i = %" PRIsz "; "
                 "Suffix is not increasing.",
                 host->name, data->name, i);
           oid_list_todo[i] = 0;
@@ -1506,11 +1925,11 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
         memcpy(&vt->suffix, &suffix, sizeof(vt->suffix));
         vt->next = NULL;
 
-        if (value_list_tail[i] == NULL)
-          value_list_head[i] = vt;
+        if (value_cells_tail[i] == NULL)
+          value_cells_head[i] = vt;
         else
-          value_list_tail[i]->next = vt;
-        value_list_tail[i] = vt;
+          value_cells_tail[i]->next = vt;
+        value_cells_tail[i] = vt;
       }
 
       /* Copy OID to oid_list[i] */
@@ -1529,25 +1948,45 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
   res = NULL;
 
   if (status == 0)
-    csnmp_dispatch_table(host, data, instance_list_head, value_list_head);
+    csnmp_dispatch_table(host, data, type_instance_cells_head,
+                         plugin_instance_cells_head, hostname_cells_head,
+                         filter_cells_head, value_cells_head);
 
   /* Free all allocated variables here */
-  while (instance_list_head != NULL) {
-    csnmp_list_instances_t *next = instance_list_head->next;
-    sfree(instance_list_head);
-    instance_list_head = next;
+  while (type_instance_cells_head != NULL) {
+    csnmp_cell_char_t *next = type_instance_cells_head->next;
+    sfree(type_instance_cells_head);
+    type_instance_cells_head = next;
+  }
+
+  while (plugin_instance_cells_head != NULL) {
+    csnmp_cell_char_t *next = plugin_instance_cells_head->next;
+    sfree(plugin_instance_cells_head);
+    plugin_instance_cells_head = next;
+  }
+
+  while (hostname_cells_head != NULL) {
+    csnmp_cell_char_t *next = hostname_cells_head->next;
+    sfree(hostname_cells_head);
+    hostname_cells_head = next;
+  }
+
+  while (filter_cells_head != NULL) {
+    csnmp_cell_char_t *next = filter_cells_head->next;
+    sfree(filter_cells_head);
+    filter_cells_head = next;
   }
 
   for (i = 0; i < data->values_len; i++) {
-    while (value_list_head[i] != NULL) {
-      csnmp_table_values_t *next = value_list_head[i]->next;
-      sfree(value_list_head[i]);
-      value_list_head[i] = next;
+    while (value_cells_head[i] != NULL) {
+      csnmp_cell_value_t *next = value_cells_head[i]->next;
+      sfree(value_cells_head[i]);
+      value_cells_head[i] = next;
     }
   }
 
-  sfree(value_list_head);
-  sfree(value_list_tail);
+  sfree(value_cells_head);
+  sfree(value_cells_tail);
 
   return 0;
 } /* int csnmp_read_table */
@@ -1578,8 +2017,9 @@ static int csnmp_read_value(host_definition_t *host, data_definition_t *data) {
   }
 
   if (ds->ds_num != data->values_len) {
-    ERROR("snmp plugin: DataSet `%s' requires %zu values, but config talks "
-          "about %zu",
+    ERROR("snmp plugin: DataSet `%s' requires %" PRIsz
+          " values, but config talks "
+          "about %" PRIsz,
           data->type, ds->ds_num, data->values_len);
     return -1;
   }
@@ -1596,11 +2036,14 @@ static int csnmp_read_value(host_definition_t *host, data_definition_t *data) {
   }
 
   sstrncpy(vl.host, host->name, sizeof(vl.host));
-  sstrncpy(vl.plugin, "snmp", sizeof(vl.plugin));
+  sstrncpy(vl.plugin, data->plugin_name, sizeof(vl.plugin));
   sstrncpy(vl.type, data->type, sizeof(vl.type));
-  sstrncpy(vl.type_instance, data->instance.string, sizeof(vl.type_instance));
-
-  vl.interval = host->interval;
+  if (data->type_instance.value)
+    sstrncpy(vl.type_instance, data->type_instance.value,
+             sizeof(vl.type_instance));
+  if (data->plugin_instance.value)
+    sstrncpy(vl.plugin_instance, data->plugin_instance.value,
+             sizeof(vl.plugin_instance));
 
   req = snmp_pdu_create(SNMP_MSG_GET);
   if (req == NULL) {
@@ -1663,9 +2106,6 @@ static int csnmp_read_host(user_data_t *ud) {
 
   host = ud->data;
 
-  if (host->interval == 0)
-    host->interval = plugin_get_interval();
-
   if (host->sess_handle == NULL)
     csnmp_host_open_session(host);
 
@@ -1710,11 +2150,7 @@ static int csnmp_shutdown(void) {
   while (data_this != NULL) {
     data_next = data_this->next;
 
-    sfree(data_this->name);
-    sfree(data_this->type);
-    sfree(data_this->values);
-    sfree(data_this->ignores);
-    sfree(data_this);
+    csnmp_data_definition_destroy(data_this);
 
     data_this = data_next;
   }
index 948107b..cbd3366 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/snmp_agent.c
  *
- * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ * Copyright(c) 2017-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
  * Authors:
  *   Roman Korynkevych <romanx.korynkevych@intel.com>
  *   Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ *   Marcin Mozejko <marcinx.mozejko@intel.com>
  **/
 
 #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>
 
 #include <net-snmp/net-snmp-config.h>
 
 #include <net-snmp/agent/net-snmp-agent-includes.h>
 
 #define PLUGIN_NAME "snmp_agent"
-#define ERR_BUF_SIZE 1024
 #define TYPE_STRING -1
+#define GROUP_UNUSED -1
+#define OID_EXISTS 1
+#define MAX_KEY_SOURCES 5
+#define MAX_INDEX_KEYS 5
+#define MAX_MATCHES 5
+
+/* Identifies index key source */
+enum index_key_src_e {
+  INDEX_HOST = 0,
+  INDEX_PLUGIN,
+  INDEX_PLUGIN_INSTANCE,
+  INDEX_TYPE,
+  INDEX_TYPE_INSTANCE
+};
+typedef enum index_key_src_e index_key_src_t;
 
-#ifndef MIN
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-#endif
+struct index_key_s {
+  index_key_src_t source;
+  u_char type;
+  char *regex; /* Pattern used to parse index key source string */
+  int group;   /* If pattern gives more than one group we can specify which one
+                  we want to take */
+  regex_t regex_info;
+};
+typedef struct index_key_s index_key_t;
 
 struct oid_s {
   oid oid[MAX_OID_LEN];
@@ -54,6 +76,12 @@ struct oid_s {
 };
 typedef struct oid_s oid_t;
 
+struct token_s {
+  char *str;
+  netsnmp_variable_list *key; /* Points to succeeding key */
+};
+typedef struct token_s token_t;
+
 struct table_definition_s {
   char *name;
   oid_t index_oid;
@@ -61,6 +89,19 @@ struct table_definition_s {
   llist_t *columns;
   c_avl_tree_t *instance_index;
   c_avl_tree_t *index_instance;
+  c_avl_tree_t *instance_oids; /* Tells us how many OIDs registered for every
+                                  instance; */
+  index_key_t index_keys[MAX_INDEX_KEYS]; /* Stores information about what each
+                                             index key represents */
+  int index_keys_len;
+  netsnmp_variable_list *index_list_cont; /* Index key container used for
+                                             generating as well as parsing
+                                             OIDs, not thread-safe */
+  c_avl_tree_t *tokens[MAX_KEY_SOURCES];  /* Input string after regex execution
+                                             will be split into sepearate
+                                             tokens */
+
+  bool tokens_done; /* Set to true when all tokens are generated */
 };
 typedef struct table_definition_s table_definition_t;
 
@@ -71,7 +112,8 @@ struct data_definition_s {
   char *type;
   char *type_instance;
   const table_definition_t *table;
-  _Bool is_instance;
+  bool is_index_key; /* indicates if table column is an index key */
+  int index_key_pos; /* position in indexes list */
   oid_t *oids;
   size_t oids_len;
   double scale;
@@ -87,10 +129,13 @@ struct snmp_agent_ctx_s {
 
   llist_t *tables;
   llist_t *scalars;
+  c_avl_tree_t *registered_oids; /* AVL tree containing all registered OIDs */
 };
 typedef struct snmp_agent_ctx_s snmp_agent_ctx_t;
 
-static snmp_agent_ctx_t *g_agent = NULL;
+static snmp_agent_ctx_t *g_agent;
+static const char *index_opts[MAX_KEY_SOURCES] = {
+    "Hostname", "Plugin", "PluginInstance", "Type", "TypeInstance"};
 
 #define CHECK_DD_TYPE(_dd, _p, _pi, _t, _ti)                                   \
   (_dd->plugin ? !strcmp(_dd->plugin, _p) : 0) &&                              \
@@ -105,6 +150,9 @@ static int snmp_agent_set_vardata(void *dst_buf, size_t *dst_buf_len,
                                   u_char asn_type, double scale, double shift,
                                   const void *value, size_t len, int type);
 static int snmp_agent_unregister_oid_index(oid_t *oid, int index);
+static int snmp_agent_update_instance_oids(c_avl_tree_t *tree, oid_t *index_oid,
+                                           int value);
+static int num_compare(const int *a, const int *b);
 
 static u_char snmp_agent_get_asn_type(oid *oid, size_t oid_len) {
   struct tree *node = get_tree(oid, oid_len, g_agent->tp);
@@ -131,10 +179,55 @@ static int snmp_agent_oid_to_string(char *buf, size_t buf_size,
   return strjoin(buf, buf_size, oid_str_ptr, o->oid_len, ".");
 }
 
-static void snmp_agent_dump_data(void) {
+/* Prints a configuration storing list. It handles both table columns list
+   and scalars list */
 #if COLLECT_DEBUG
+static void snmp_agent_dump_data(llist_t *list) {
   char oid_str[DATA_MAX_NAME_LEN];
+  for (llentry_t *de = llist_head(list); de != NULL; de = de->next) {
+    data_definition_t *dd = de->value;
+    table_definition_t const *td = dd->table;
 
+    if (dd->table != NULL)
+      DEBUG(PLUGIN_NAME ":   Column:");
+    else
+      DEBUG(PLUGIN_NAME ": Scalar:");
+
+    DEBUG(PLUGIN_NAME ":     Name: %s", dd->name);
+    if (dd->plugin)
+      DEBUG(PLUGIN_NAME ":     Plugin: %s", dd->plugin);
+    if (dd->plugin_instance)
+      DEBUG(PLUGIN_NAME ":     PluginInstance: %s", dd->plugin_instance);
+    if (dd->is_index_key) {
+      index_key_t const *index_key = &td->index_keys[dd->index_key_pos];
+
+      DEBUG(PLUGIN_NAME ":     IndexKey:");
+      DEBUG(PLUGIN_NAME ":       Source: %s", index_opts[index_key->source]);
+      DEBUG(PLUGIN_NAME ":       Type: %s",
+            (index_key->type == ASN_INTEGER) ? "Integer" : "String");
+      if (index_key->regex)
+        DEBUG(PLUGIN_NAME ":       Regex: %s", index_key->regex);
+      if (index_key->group != GROUP_UNUSED)
+        DEBUG(PLUGIN_NAME ":       Group: %d", index_key->group);
+    }
+    if (dd->type)
+      DEBUG(PLUGIN_NAME ":     Type: %s", dd->type);
+    if (dd->type_instance)
+      DEBUG(PLUGIN_NAME ":     TypeInstance: %s", dd->type_instance);
+    for (size_t i = 0; i < dd->oids_len; i++) {
+      snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &dd->oids[i]);
+      DEBUG(PLUGIN_NAME ":     OID[%" PRIsz "]: %s", i, oid_str);
+    }
+    DEBUG(PLUGIN_NAME ":   Scale: %g", dd->scale);
+    DEBUG(PLUGIN_NAME ":   Shift: %g", dd->shift);
+  }
+}
+
+/* Prints parsed configuration */
+static void snmp_agent_dump_config(void) {
+  char oid_str[DATA_MAX_NAME_LEN];
+
+  /* Printing tables */
   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
     table_definition_t *td = te->value;
 
@@ -149,62 +242,28 @@ static void snmp_agent_dump_data(void) {
       DEBUG(PLUGIN_NAME ":   SizeOID: %s", oid_str);
     }
 
-    for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
-      data_definition_t *dd = de->value;
-
-      DEBUG(PLUGIN_NAME ":   Column:");
-      DEBUG(PLUGIN_NAME ":     Name: %s", dd->name);
-      if (dd->plugin)
-        DEBUG(PLUGIN_NAME ":     Plugin: %s", dd->plugin);
-      if (dd->plugin_instance)
-        DEBUG(PLUGIN_NAME ":     PluginInstance: %s", dd->plugin_instance);
-      if (dd->is_instance)
-        DEBUG(PLUGIN_NAME ":     Instance: true");
-      if (dd->type)
-        DEBUG(PLUGIN_NAME ":     Type: %s", dd->type);
-      if (dd->type_instance)
-        DEBUG(PLUGIN_NAME ":     TypeInstance: %s", dd->type_instance);
-      for (size_t i = 0; i < dd->oids_len; i++) {
-        snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &dd->oids[i]);
-        DEBUG(PLUGIN_NAME ":     OID[%zu]: %s", i, oid_str);
-      }
-      DEBUG(PLUGIN_NAME ":   Scale: %g", dd->scale);
-      DEBUG(PLUGIN_NAME ":   Shift: %g", dd->shift);
-    }
+    snmp_agent_dump_data(td->columns);
   }
 
-  for (llentry_t *e = llist_head(g_agent->scalars); e != NULL; e = e->next) {
-    data_definition_t *dd = e->value;
-
-    DEBUG(PLUGIN_NAME ": Scalar:");
-    DEBUG(PLUGIN_NAME ":   Name: %s", dd->name);
-    if (dd->plugin)
-      DEBUG(PLUGIN_NAME ":   Plugin: %s", dd->plugin);
-    if (dd->plugin_instance)
-      DEBUG(PLUGIN_NAME ":   PluginInstance: %s", dd->plugin_instance);
-    if (dd->is_instance)
-      DEBUG(PLUGIN_NAME ":   Instance: true");
-    if (dd->type)
-      DEBUG(PLUGIN_NAME ":   Type: %s", dd->type);
-    if (dd->type_instance)
-      DEBUG(PLUGIN_NAME ":   TypeInstance: %s", dd->type_instance);
-    for (size_t i = 0; i < dd->oids_len; i++) {
-      snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &dd->oids[i]);
-      DEBUG(PLUGIN_NAME ":   OID[%zu]: %s", i, oid_str);
-    }
-    DEBUG(PLUGIN_NAME ":   Scale: %g", dd->scale);
-    DEBUG(PLUGIN_NAME ":   Shift: %g", dd->shift);
-  }
-#endif /* COLLECT_DEBUG */
+  /* Printing scalars */
+  snmp_agent_dump_data(g_agent->scalars);
 }
+#endif /* COLLECT_DEBUG */
 
-static int snmp_agent_validate_data(void) {
+static int snmp_agent_validate_config(void) {
 
-  snmp_agent_dump_data();
+#if COLLECT_DEBUG
+  snmp_agent_dump_config();
+#endif
 
   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
     table_definition_t *td = te->value;
 
+    if (!td->index_keys_len) {
+      ERROR(PLUGIN_NAME ": Index keys not defined for '%s'", td->name);
+      return -EINVAL;
+    }
+
     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
       data_definition_t *dd = de->value;
 
@@ -227,11 +286,10 @@ static int snmp_agent_validate_data(void) {
         return -EINVAL;
       }
 
-      if (dd->is_instance) {
-
+      if (dd->is_index_key) {
         if (dd->type || dd->type_instance) {
           ERROR(PLUGIN_NAME ": Type and TypeInstance are not valid for "
-                            "instance data '%s'.'%s'",
+                            "index data '%s'.'%s'",
                 td->name, dd->name);
           return -EINVAL;
         }
@@ -267,9 +325,8 @@ static int snmp_agent_validate_data(void) {
       return -EINVAL;
     }
 
-    if (dd->is_instance) {
-      ERROR(PLUGIN_NAME
-            ": Instance flag can't be specified for scalar data '%s'",
+    if (dd->is_index_key) {
+      ERROR(PLUGIN_NAME ": Index field can't be specified for scalar data '%s'",
             dd->name);
       return -EINVAL;
     }
@@ -283,109 +340,429 @@ static int snmp_agent_validate_data(void) {
   return 0;
 }
 
-static void snmp_agent_generate_oid2string(oid_t *oid, size_t offset,
-                                           char *key) {
-  int key_len = oid->oid[offset];
-  int i;
+static int snmp_agent_parse_index_key(const char *input, regex_t *regex_info,
+                                      int gi, regmatch_t *m) {
+  regmatch_t matches[MAX_MATCHES];
 
-  for (i = 0; i < key_len && offset < oid->oid_len; i++)
-    key[i] = oid->oid[++offset];
+  int ret = regexec(regex_info, input, MAX_MATCHES, matches, 0);
+  if (!ret) {
+    if (gi > regex_info->re_nsub) {
+      ERROR(PLUGIN_NAME ": Group index %d not found. Check regex config", gi);
+      return -1;
+    }
+    *m = matches[gi];
+  } else if (ret == REG_NOMATCH) {
+    ERROR(PLUGIN_NAME ": No match found");
+    return -1;
+  } else {
+    char msgbuf[100];
 
-  key[i] = '\0';
+    regerror(ret, regex_info, msgbuf, sizeof(msgbuf));
+    ERROR(PLUGIN_NAME ": Regex match failed: %s", msgbuf);
+    return -1;
+  }
+
+  return 0;
 }
 
-static int snmp_agent_generate_string2oid(oid_t *oid, const char *key) {
-  int key_len = strlen(key);
+static int snmp_agent_create_token(char const *input, int t_off, int n,
+                                   c_avl_tree_t *tree,
+                                   netsnmp_variable_list *index_key) {
+  assert(tree != NULL);
+
+  token_t *token = malloc(sizeof(*token));
+
+  if (token == NULL)
+    goto error;
+
+  int *offset = malloc(sizeof(*offset));
+
+  if (offset == NULL)
+    goto free_token_error;
+
+  int ret = 0;
+
+  token->key = index_key;
+  input += t_off;
+  size_t len = strlen(input);
+
+  if (n < len)
+    len = n;
+
+  token->str = malloc(len + 1);
+
+  if (token->str == NULL)
+    goto free_offset_error;
+
+  /* copy at most n bytes from input with offset t_off into token->str */
+  sstrncpy(token->str, input, len + 1);
+  *offset = t_off;
+  ret = c_avl_insert(tree, (void *)offset, (void *)token);
+
+  if (ret == 0)
+    return 0;
+
+  sfree(token->str);
+
+free_offset_error:
+  sfree(offset);
+
+free_token_error:
+  sfree(token);
+
+error:
+  ERROR(PLUGIN_NAME ": Could not allocate memory to create token");
+
+  return -1;
+}
+
+static int snmp_agent_delete_token(int t_off, c_avl_tree_t *tree) {
+  token_t *token = NULL;
+  int *offset = NULL;
+
+  int ret = c_avl_remove(tree, &t_off, (void **)&offset, (void **)&token);
+
+  if (ret != 0) {
+    ERROR(PLUGIN_NAME ": Could not delete token");
+    return -1;
+  }
+
+  sfree(token->str);
+  sfree(token);
+  sfree(offset);
+  return 0;
+}
+
+static int snmp_agent_get_token(c_avl_tree_t *tree, int mpos) {
+
+  int *pos;
+  char *token;
+  int prev_pos = 0;
+
+  c_avl_iterator_t *it = c_avl_get_iterator(tree);
+  while (c_avl_iterator_next(it, (void **)&pos, (void **)&token) == 0) {
+    if (*pos >= mpos)
+      break;
+    else
+      prev_pos = *pos;
+  }
+
+  c_avl_iterator_destroy(it);
+  return prev_pos;
+}
+
+static int snmp_agent_tokenize(const char *input, c_avl_tree_t *tokens,
+                               const regmatch_t *m,
+                               netsnmp_variable_list *key) {
+  assert(tokens != NULL);
+
+  int ret = 0;
+  int len = strlen(input);
+
+  /* Creating first token that is going to be split later */
+  if (c_avl_size(tokens) == 0) {
+    ret = snmp_agent_create_token(input, 0, len, tokens, NULL);
+    if (ret != 0)
+      return ret;
+  }
+
+  /* Divide token that contains current match into two */
+  int t_pos = snmp_agent_get_token(tokens, m->rm_so);
+  ret = snmp_agent_delete_token(t_pos, tokens);
+
+  if (ret != 0)
+    return -1;
+
+  ret = snmp_agent_create_token(input, t_pos, m->rm_so - t_pos, tokens, key);
+
+  if (ret != 0)
+    return -1;
+
+  if (len - m->rm_eo > 1) {
+    ret = snmp_agent_create_token(input, m->rm_eo, len - m->rm_eo + 1, tokens,
+                                  NULL);
+    if (ret != 0) {
+      snmp_agent_delete_token(t_pos, tokens);
+      return -1;
+    }
+  }
 
-  oid->oid[oid->oid_len++] = key_len;
-  for (int i = 0; i < key_len; i++) {
-    oid->oid[oid->oid_len++] = key[i];
-    if (oid->oid_len >= MAX_OID_LEN) {
-      ERROR(PLUGIN_NAME ": Conversion key string %s to OID failed", key);
+  return 0;
+}
+
+static int snmp_agent_fill_index_list(table_definition_t *td,
+                                      value_list_t const *vl) {
+  int ret;
+  int i;
+  netsnmp_variable_list *key = td->index_list_cont;
+  char const *ptr;
+
+  for (i = 0; i < td->index_keys_len; i++) {
+    /* var should never be NULL */
+    assert(key != NULL);
+    ptr = NULL;
+    const index_key_src_t source = td->index_keys[i].source;
+    c_avl_tree_t *const tokens = td->tokens[source];
+    /* Generating list filled with all data necessary to generate an OID */
+    switch (source) {
+    case INDEX_HOST:
+      ptr = vl->host;
+      break;
+    case INDEX_PLUGIN:
+      ptr = vl->plugin;
+      break;
+    case INDEX_PLUGIN_INSTANCE:
+      ptr = vl->plugin_instance;
+      break;
+    case INDEX_TYPE:
+      ptr = vl->type;
+      break;
+    case INDEX_TYPE_INSTANCE:
+      ptr = vl->type_instance;
+      break;
+    default:
+      ERROR(PLUGIN_NAME ": Unknown index key source provided");
       return -EINVAL;
     }
+
+    /* Parsing input string if necessary */
+    if (td->index_keys[i].regex) {
+      regmatch_t m;
+
+      /* Parsing input string */
+      ret = snmp_agent_parse_index_key(ptr, &td->index_keys[i].regex_info,
+                                       td->index_keys[i].group, &m);
+      if (ret != 0) {
+        ERROR(PLUGIN_NAME ": Error executing regex");
+        return ret;
+      }
+
+      /* Tokenizing input string if not done yet */
+      if (td->tokens_done == false)
+        ret = snmp_agent_tokenize(ptr, tokens, &m, key);
+
+      if (ret != 0)
+        return -1;
+
+      if (td->index_keys[i].type == ASN_INTEGER) {
+        int val = strtol(ptr + m.rm_so, NULL, 0);
+
+#ifdef HAVE_NETSNMP_OLD_API
+        ret = snmp_set_var_value(key, (const u_char *)&val, sizeof(val));
+#else
+        ret = snmp_set_var_value(key, &val, sizeof(val));
+#endif
+      } else
+#ifdef HAVE_NETSNMP_OLD_API
+        ret = snmp_set_var_value(key, (const u_char *)(ptr + m.rm_so),
+                                 m.rm_eo - m.rm_so);
+#else
+        ret = snmp_set_var_value(key, ptr + m.rm_so, m.rm_eo - m.rm_so);
+#endif
+    } else
+#ifdef HAVE_NETSNMP_OLD_API
+      ret = snmp_set_var_value(key, (const u_char *)ptr, strlen(ptr));
+#else
+      ret = snmp_set_var_value(key, ptr, strlen(ptr));
+#endif
+
+    if (ret != 0)
+      return -1;
+
+    key = key->next_variable;
   }
 
+  /* Tokens for all source strings are generated */
+  for (i = 0; i < MAX_KEY_SOURCES; i++)
+    td->tokens_done = true;
+
+  return 0;
+}
+
+static int snmp_agent_prep_index_list(table_definition_t const *td,
+                                      netsnmp_variable_list **index_list) {
+  /* Generating list having only the structure (with no values) letting us
+   * know how to parse an OID*/
+  for (int i = 0; i < td->index_keys_len; i++) {
+    switch (td->index_keys[i].source) {
+    case INDEX_HOST:
+    case INDEX_PLUGIN:
+    case INDEX_PLUGIN_INSTANCE:
+    case INDEX_TYPE:
+    case INDEX_TYPE_INSTANCE:
+      snmp_varlist_add_variable(index_list, NULL, 0, td->index_keys[i].type,
+                                NULL, 0);
+      break;
+    default:
+      ERROR(PLUGIN_NAME ": Unknown index key source provided");
+      return -EINVAL;
+    }
+  }
   return 0;
 }
 
-static int snmp_agent_register_oid_string(oid_t *oid, const char *key,
+static int snmp_agent_generate_index(table_definition_t *td,
+                                     value_list_t const *vl, oid_t *index_oid) {
+
+  /* According to given information by index_keys list
+   * index OID is going to be built
+   */
+  int ret = snmp_agent_fill_index_list(td, vl);
+  if (ret != 0)
+    return -EINVAL;
+
+  /* Building only index part OID (without table prefix OID) */
+  ret = build_oid_noalloc(index_oid->oid, sizeof(index_oid->oid),
+                          &index_oid->oid_len, NULL, 0, td->index_list_cont);
+  if (ret != SNMPERR_SUCCESS) {
+    ERROR(PLUGIN_NAME ": Error building index OID");
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+/* It appends one OID to the end of another */
+static int snmp_agent_append_oid(oid_t *out, const oid_t *in) {
+
+  if (out->oid_len + in->oid_len > MAX_OID_LEN) {
+    ERROR(PLUGIN_NAME ": Cannot create OID. Output length is too long!");
+    return -EINVAL;
+  }
+  memcpy(&out->oid[out->oid_len], in->oid, in->oid_len * sizeof(oid));
+  out->oid_len += in->oid_len;
+
+  return 0;
+}
+
+static int snmp_agent_register_oid_string(const oid_t *oid,
+                                          const oid_t *index_oid,
                                           Netsnmp_Node_Handler *handler) {
   oid_t new_oid;
 
   memcpy(&new_oid, oid, sizeof(*oid));
-  int ret = snmp_agent_generate_string2oid(&new_oid, key);
+  /* Concatenating two string oids */
+  int ret = snmp_agent_append_oid(&new_oid, index_oid);
   if (ret != 0)
     return ret;
 
   return snmp_agent_register_oid(&new_oid, handler);
 }
 
-static int snmp_agent_unregister_oid_string(oid_t *oid, const char *key) {
+static int snmp_agent_unregister_oid(oid_t *oid) {
+  int ret = c_avl_remove(g_agent->registered_oids, (void *)oid, NULL, NULL);
+
+  if (ret != 0)
+    ERROR(PLUGIN_NAME ": Could not delete registration info");
+
+  return unregister_mib(oid->oid, oid->oid_len);
+}
+
+static int snmp_agent_unregister_oid_string(oid_t *oid,
+                                            const oid_t *index_oid) {
   oid_t new_oid;
+  char oid_str[DATA_MAX_NAME_LEN];
 
   memcpy(&new_oid, oid, sizeof(*oid));
-  int ret = snmp_agent_generate_string2oid(&new_oid, key);
+  /* Concatenating two string oids */
+  int ret = snmp_agent_append_oid(&new_oid, index_oid);
   if (ret != 0)
     return ret;
 
-  return unregister_mib(new_oid.oid, new_oid.oid_len);
+  snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &new_oid);
+  DEBUG(PLUGIN_NAME ": Unregistered handler for OID (%s)", oid_str);
+
+  return snmp_agent_unregister_oid(&new_oid);
 }
 
-static int snmp_agent_table_row_remove(table_definition_t *td,
-                                       const char *instance) {
+static void snmp_agent_table_data_remove(data_definition_t *dd,
+                                         table_definition_t *td,
+                                         oid_t *index_oid) {
   int *index = NULL;
-  char *ins = NULL;
+  oid_t *ind_oid = NULL;
 
   if (td->index_oid.oid_len) {
-    if ((c_avl_get(td->instance_index, instance, (void **)&index) != 0) ||
-        (c_avl_get(td->index_instance, index, (void **)&ins) != 0))
-      return 0;
+    if ((c_avl_get(td->instance_index, index_oid, (void **)&index) != 0) ||
+        (c_avl_get(td->index_instance, index, NULL) != 0))
+      return;
   } else {
-    if (c_avl_get(td->instance_index, instance, (void **)&ins) != 0)
-      return 0;
+    if (c_avl_get(td->instance_index, index_oid, NULL) != 0)
+      return;
   }
 
   pthread_mutex_lock(&g_agent->agentx_lock);
 
-  if (td->index_oid.oid_len)
-    snmp_agent_unregister_oid_index(&td->index_oid, *index);
+  int reg_oids = -1; /* Number of registered oids for given instance */
+
+  for (size_t i = 0; i < dd->oids_len; i++) {
+    if (td->index_oid.oid_len)
+      snmp_agent_unregister_oid_index(&dd->oids[i], *index);
+    else
+      snmp_agent_unregister_oid_string(&dd->oids[i], index_oid);
 
+    reg_oids =
+        snmp_agent_update_instance_oids(td->instance_oids, index_oid, -1);
+  }
+
+  /* Checking if any metrics are left registered */
+  if (reg_oids != 0) {
+    pthread_mutex_unlock(&g_agent->agentx_lock);
+    return;
+  }
+
+  /* All metrics have been unregistered. Unregistering index key OIDs */
+  int keys_processed = 0;
   for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
-    data_definition_t *dd = de->value;
+    data_definition_t *idd = de->value;
+
+    if (!idd->is_index_key)
+      continue;
 
-    for (size_t i = 0; i < dd->oids_len; i++)
+    for (size_t i = 0; i < idd->oids_len; i++)
       if (td->index_oid.oid_len)
-        snmp_agent_unregister_oid_index(&dd->oids[i], *index);
+        snmp_agent_unregister_oid_index(&idd->oids[i], *index);
       else
-        snmp_agent_unregister_oid_string(&dd->oids[i], ins);
-  }
+        snmp_agent_unregister_oid_string(&idd->oids[i], index_oid);
 
+    if (++keys_processed >= td->index_keys_len)
+      break;
+  }
   pthread_mutex_unlock(&g_agent->agentx_lock);
 
-  DEBUG(PLUGIN_NAME ": Removed row for '%s' table [%d, %s]", td->name,
-        (index != NULL) ? *index : -1, ins);
+  /* All OIDs have been unregistered so we dont need this instance registered
+   * as well */
+  char index_str[DATA_MAX_NAME_LEN];
+
+  if (index == NULL)
+    snmp_agent_oid_to_string(index_str, sizeof(index_str), index_oid);
+  else
+    snprintf(index_str, sizeof(index_str), "%d", *index);
 
   notification_t n = {
       .severity = NOTIF_WARNING, .time = cdtime(), .plugin = PLUGIN_NAME};
   sstrncpy(n.host, hostname_g, sizeof(n.host));
-  sstrncpy(n.plugin_instance, ins, sizeof(n.plugin_instance));
   snprintf(n.message, sizeof(n.message),
-           "Removed data row from table %s instance %s index %d", td->name, ins,
-           (index != NULL) ? *index : -1);
+           "Removed data row from table %s with index %s", td->name, index_str);
+  DEBUG(PLUGIN_NAME ": %s", n.message);
   plugin_dispatch_notification(&n);
 
-  if (td->index_oid.oid_len) {
-    c_avl_remove(td->index_instance, index, NULL, (void **)&ins);
-    c_avl_remove(td->instance_index, instance, NULL, (void **)&index);
+  int *val = NULL;
+
+  c_avl_remove(td->instance_oids, index_oid, NULL, (void **)&val);
+  sfree(val);
+
+  if (index != NULL) {
+    pthread_mutex_lock(&g_agent->agentx_lock);
+    snmp_agent_unregister_oid_index(&td->index_oid, *index);
+    pthread_mutex_unlock(&g_agent->agentx_lock);
+
+    c_avl_remove(td->index_instance, index, NULL, (void **)&ind_oid);
+    c_avl_remove(td->instance_index, index_oid, NULL, (void **)&index);
     sfree(index);
-    sfree(ins);
+    sfree(ind_oid);
   } else {
-    c_avl_remove(td->instance_index, instance, NULL, (void **)&ins);
-    sfree(ins);
+    c_avl_remove(td->instance_index, index_oid, NULL, NULL);
   }
-
-  return 0;
 }
 
 static int snmp_agent_clear_missing(const value_list_t *vl,
@@ -399,11 +776,23 @@ static int snmp_agent_clear_missing(const value_list_t *vl,
     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
       data_definition_t *dd = de->value;
 
-      if (!dd->is_instance) {
+      if (!dd->is_index_key) {
         if (CHECK_DD_TYPE(dd, vl->plugin, vl->plugin_instance, vl->type,
                           vl->type_instance)) {
-          snmp_agent_table_row_remove(td, vl->plugin_instance);
-          return 0;
+          oid_t *index_oid = calloc(1, sizeof(*index_oid));
+
+          if (index_oid == NULL) {
+            ERROR(PLUGIN_NAME ": Could not allocate memory for index_oid");
+            return -ENOMEM;
+          }
+
+          int ret = snmp_agent_generate_index(td, vl, index_oid);
+
+          if (ret == 0)
+            snmp_agent_table_data_remove(dd, td, index_oid);
+          sfree(index_oid);
+
+          return ret;
         }
       }
     }
@@ -444,23 +833,22 @@ static void snmp_agent_free_table_columns(table_definition_t *td) {
 
     if (td->index_oid.oid_len) {
       int *index;
-      char *instance;
+      oid_t *index_oid;
 
       c_avl_iterator_t *iter = c_avl_get_iterator(td->index_instance);
-      while (c_avl_iterator_next(iter, (void *)&index, (void *)&instance) ==
+      while (c_avl_iterator_next(iter, (void *)&index, (void *)&index_oid) ==
              0) {
         for (size_t i = 0; i < dd->oids_len; i++)
           snmp_agent_unregister_oid_index(&dd->oids[i], *index);
       }
       c_avl_iterator_destroy(iter);
     } else {
-      char *instance;
+      oid_t *index_oid;
 
       c_avl_iterator_t *iter = c_avl_get_iterator(dd->table->instance_index);
-      while (c_avl_iterator_next(iter, (void *)&instance, (void *)&instance) ==
-             0) {
+      while (c_avl_iterator_next(iter, (void *)&index_oid, NULL) == 0) {
         for (size_t i = 0; i < dd->oids_len; i++)
-          snmp_agent_unregister_oid_string(&dd->oids[i], instance);
+          snmp_agent_unregister_oid_string(&dd->oids[i], index_oid);
       }
       c_avl_iterator_destroy(iter);
     }
@@ -480,13 +868,14 @@ static void snmp_agent_free_table(table_definition_t **td) {
   if ((*td)->size_oid.oid_len)
     unregister_mib((*td)->size_oid.oid, (*td)->size_oid.oid_len);
 
+  oid_t *index_oid;
+
   /* Unregister Index OIDs */
   if ((*td)->index_oid.oid_len) {
     int *index;
-    char *instance;
 
     c_avl_iterator_t *iter = c_avl_get_iterator((*td)->index_instance);
-    while (c_avl_iterator_next(iter, (void *)&index, (void *)&instance) == 0)
+    while (c_avl_iterator_next(iter, (void **)&index, (void **)&index_oid) == 0)
       snmp_agent_unregister_oid_index(&(*td)->index_oid, *index);
 
     c_avl_iterator_destroy(iter);
@@ -497,6 +886,15 @@ static void snmp_agent_free_table(table_definition_t **td) {
 
   void *key = NULL;
   void *value = NULL;
+  int *num = NULL;
+
+  /* Removing data from instance_oids, leaving key pointers since they are still
+   * used in other AVL trees */
+  c_avl_iterator_t *iter = c_avl_get_iterator((*td)->instance_oids);
+  while (c_avl_iterator_next(iter, (void **)&index_oid, (void **)&num) == 0)
+    sfree(num);
+  c_avl_iterator_destroy(iter);
+  c_avl_destroy((*td)->instance_oids);
 
   /* index_instance and instance_index contain the same pointers */
   c_avl_destroy((*td)->index_instance);
@@ -511,20 +909,189 @@ static void snmp_agent_free_table(table_definition_t **td) {
     c_avl_destroy((*td)->instance_index);
     (*td)->instance_index = NULL;
   }
+  snmp_free_varbind((*td)->index_list_cont);
 
+  int i;
+  token_t *tok = NULL;
+
+  for (i = 0; i < (*td)->index_keys_len; i++) {
+    sfree((*td)->index_keys[i].regex);
+    regfree(&(*td)->index_keys[i].regex_info);
+  }
+  for (i = 0; i < MAX_KEY_SOURCES; i++)
+    if ((*td)->tokens[i] != NULL) {
+      while (c_avl_pick((*td)->tokens[i], &key, (void **)&tok) == 0) {
+        sfree(key);
+        sfree(tok->str);
+        sfree(tok);
+      }
+      c_avl_destroy((*td)->tokens[i]);
+      (*td)->tokens[i] = NULL;
+    }
   sfree((*td)->name);
   sfree(*td);
 
   return;
 }
 
+static int snmp_agent_parse_oid_index_keys(const table_definition_t *td,
+                                           oid_t *index_oid) {
+  assert(index_oid != NULL);
+  int ret = parse_oid_indexes(index_oid->oid, index_oid->oid_len,
+                              td->index_list_cont);
+  if (ret != SNMPERR_SUCCESS)
+    ERROR(PLUGIN_NAME ": index OID parse error!");
+  return ret;
+}
+
+static int snmp_agent_build_name(char **name, c_avl_tree_t *tokens) {
+  int *pos;
+  token_t *tok;
+  char str[DATA_MAX_NAME_LEN];
+  char out[DATA_MAX_NAME_LEN] = {0};
+  c_avl_iterator_t *it = c_avl_get_iterator(tokens);
+
+  if (it == NULL) {
+    ERROR(PLUGIN_NAME ": Error getting tokens list iterator");
+    return -1;
+  }
+
+  while (c_avl_iterator_next(it, (void **)&pos, (void **)&tok) == 0) {
+    strncat(out, tok->str, DATA_MAX_NAME_LEN - strlen(out) - 1);
+    if (tok->key != NULL) {
+      if (tok->key->type == ASN_INTEGER) {
+        snprintf(str, sizeof(str), "%ld", *tok->key->val.integer);
+        strncat(out, str, DATA_MAX_NAME_LEN - strlen(out) - 1);
+      } else /* OCTET_STR */
+        strncat(out, (char *)tok->key->val.string,
+                DATA_MAX_NAME_LEN - strlen(out) - 1);
+    }
+  }
+
+  c_avl_iterator_destroy(it);
+  *name = strdup(out);
+
+  if (*name == NULL) {
+    ERROR(PLUGIN_NAME ": Could not allocate memory");
+    return -ENOMEM;
+  }
+
+  return 0;
+}
+
+static int snmp_agent_format_name(char *name, int name_len,
+                                  data_definition_t *dd, oid_t *index_oid) {
+
+  int ret = 0;
+
+  if (index_oid == NULL) {
+    /* It's a scalar */
+    format_name(name, name_len, hostname_g, dd->plugin, dd->plugin_instance,
+                dd->type, dd->type_instance);
+  } else {
+    /* Need to parse string index OID */
+    const table_definition_t *td = dd->table;
+    ret = snmp_agent_parse_oid_index_keys(td, index_oid);
+    if (ret != 0)
+      return ret;
+
+    int i = 0;
+    netsnmp_variable_list *key = td->index_list_cont;
+    char str[DATA_MAX_NAME_LEN];
+    char *fields[MAX_KEY_SOURCES] = {hostname_g, dd->plugin,
+                                     dd->plugin_instance, dd->type,
+                                     dd->type_instance};
+
+    /* Looking for simple keys only */
+    while (key != NULL) {
+      if (!td->index_keys[i].regex) {
+        index_key_src_t source = td->index_keys[i].source;
+
+        if (source < INDEX_HOST || source > INDEX_TYPE_INSTANCE) {
+          ERROR(PLUGIN_NAME ": Unkown index key source!");
+          return -EINVAL;
+        }
+
+        if (td->index_keys[i].type == ASN_INTEGER) {
+          snprintf(str, sizeof(str), "%ld", *key->val.integer);
+          fields[source] = str;
+        } else /* OCTET_STR */
+          fields[source] = (char *)key->val.string;
+      }
+      key = key->next_variable;
+      i++;
+    }
+
+    /* Keys with regexes */
+    for (i = 0; i < MAX_KEY_SOURCES; i++) {
+      if (td->tokens[i] == NULL)
+        continue;
+      ret = snmp_agent_build_name(&fields[i], td->tokens[i]);
+      if (ret != 0)
+        return ret;
+    }
+    format_name(name, name_len, fields[INDEX_HOST], fields[INDEX_PLUGIN],
+                fields[INDEX_PLUGIN_INSTANCE], fields[INDEX_TYPE],
+                fields[INDEX_TYPE_INSTANCE]);
+    for (i = 0; i < MAX_KEY_SOURCES; i++) {
+      if (td->tokens[i])
+        sfree(fields[i]);
+    }
+  }
+
+  return 0;
+}
+
 static int snmp_agent_form_reply(struct netsnmp_request_info_s *requests,
-                                 data_definition_t *dd, char *instance,
+                                 data_definition_t *dd, oid_t *index_oid,
                                  int oid_index) {
+  int ret;
+
+  if (dd->is_index_key) {
+    const table_definition_t *td = dd->table;
+    int ret = snmp_agent_parse_oid_index_keys(td, index_oid);
+
+    if (ret != 0)
+      return ret;
+
+    netsnmp_variable_list *key = td->index_list_cont;
+    /* Searching index key */
+    for (int pos = 0; pos < dd->index_key_pos; pos++)
+      key = key->next_variable;
+
+    requests->requestvb->type = td->index_keys[dd->index_key_pos].type;
+
+    if (requests->requestvb->type == ASN_INTEGER)
+#ifdef HAVE_NETSNMP_OLD_API
+      snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
+                               (const u_char *)key->val.integer,
+                               sizeof(*key->val.integer));
+#else
+      snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
+                               key->val.integer, sizeof(*key->val.integer));
+#endif
+    else /* OCTET_STR */
+#ifdef HAVE_NETSNMP_OLD_API
+      snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
+                               (const u_char *)key->val.string,
+                               strlen((const char *)key->val.string));
+#else
+      snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
+                               key->val.string,
+                               strlen((const char *)key->val.string));
+#endif
+
+    pthread_mutex_unlock(&g_agent->lock);
+
+    return SNMP_ERR_NOERROR;
+  }
+
   char name[DATA_MAX_NAME_LEN];
-  format_name(name, sizeof(name), hostname_g, dd->plugin,
-              instance ? instance : dd->plugin_instance, dd->type,
-              dd->type_instance);
+
+  ret = snmp_agent_format_name(name, sizeof(name), dd, index_oid);
+  if (ret != 0)
+    return ret;
+
   DEBUG(PLUGIN_NAME ": Identifier '%s'", name);
 
   value_t *values;
@@ -535,7 +1102,7 @@ static int snmp_agent_form_reply(struct netsnmp_request_info_s *requests,
     return SNMP_NOSUCHINSTANCE;
   }
 
-  int ret = uc_get_value_by_name(name, &values, &values_num);
+  ret = uc_get_value_by_name(name, &values, &values_num);
 
   if (ret != 0) {
     ERROR(PLUGIN_NAME ": Failed to get value for '%s'", name);
@@ -571,14 +1138,14 @@ snmp_agent_table_oid_handler(struct netsnmp_mib_handler_s *handler,
                              struct netsnmp_agent_request_info_s *reqinfo,
                              struct netsnmp_request_info_s *requests) {
 
-  if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
+  if (reqinfo->mode != MODE_GET) {
     DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
     return SNMP_ERR_NOERROR;
   }
 
   pthread_mutex_lock(&g_agent->lock);
 
-  oid_t oid;
+  oid_t oid; /* Requested OID */
   memcpy(oid.oid, requests->requestvb->name,
          sizeof(oid.oid[0]) * requests->requestvb->name_length);
   oid.oid_len = requests->requestvb->name_length;
@@ -588,6 +1155,7 @@ snmp_agent_table_oid_handler(struct netsnmp_mib_handler_s *handler,
   snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &oid);
   DEBUG(PLUGIN_NAME ": Get request received for table OID '%s'", oid_str);
 #endif
+  oid_t index_oid; /* Index part of requested OID */
 
   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
     table_definition_t *td = te->value;
@@ -598,49 +1166,37 @@ snmp_agent_table_oid_handler(struct netsnmp_mib_handler_s *handler,
       for (size_t i = 0; i < dd->oids_len; i++) {
         int ret = snmp_oid_ncompare(oid.oid, oid.oid_len, dd->oids[i].oid,
                                     dd->oids[i].oid_len,
-                                    MIN(oid.oid_len, dd->oids[i].oid_len));
+                                    SNMP_MIN(oid.oid_len, dd->oids[i].oid_len));
         if (ret != 0)
           continue;
 
-        char *instance;
-
-        if (!td->index_oid.oid_len) {
-          char key[MAX_OID_LEN];
+        /* Calculating OID length for index part */
+        index_oid.oid_len = oid.oid_len - dd->oids[i].oid_len;
+        /* Fetching index part of the OID */
+        memcpy(index_oid.oid, &oid.oid[dd->oids[i].oid_len],
+               index_oid.oid_len * sizeof(*oid.oid));
 
-          memset(key, 0, sizeof(key));
-          snmp_agent_generate_oid2string(
-              &oid, MIN(oid.oid_len, dd->oids[i].oid_len), key);
+        char index_str[DATA_MAX_NAME_LEN];
+        snmp_agent_oid_to_string(index_str, sizeof(index_str), &index_oid);
 
-          ret = c_avl_get(td->instance_index, key, (void **)&instance);
-          if (ret != 0) {
-            DEBUG(PLUGIN_NAME ": Nonexisting index string '%s' requested", key);
-            pthread_mutex_unlock(&g_agent->lock);
-            return SNMP_NOSUCHINSTANCE;
-          }
+        if (!td->index_oid.oid_len) {
+          ret = c_avl_get(td->instance_index, &index_oid, NULL);
         } else {
-          int index = oid.oid[oid.oid_len - 1];
+          oid_t *temp_oid;
 
-          ret = c_avl_get(td->index_instance, &index, (void **)&instance);
-          if (ret != 0) {
-            DEBUG(PLUGIN_NAME ": Nonexisting index '%d' requested", index);
-            pthread_mutex_unlock(&g_agent->lock);
-            return SNMP_NOSUCHINSTANCE;
-          }
+          assert(index_oid.oid_len == 1);
+          ret = c_avl_get(td->index_instance, (int *)&index_oid.oid[0],
+                          (void **)&temp_oid);
+          memcpy(&index_oid, temp_oid, sizeof(index_oid));
         }
 
-        if (dd->is_instance) {
-          requests->requestvb->type = ASN_OCTET_STR;
-          snmp_set_var_typed_value(
-              requests->requestvb, requests->requestvb->type,
-              (const u_char *)instance, strlen((instance)));
-
+        if (ret != 0) {
+          INFO(PLUGIN_NAME ": Non-existing index (%s) requested", index_str);
           pthread_mutex_unlock(&g_agent->lock);
-
-          return SNMP_ERR_NOERROR;
+          return SNMP_NOSUCHINSTANCE;
         }
 
-        ret = snmp_agent_form_reply(requests, dd, instance, i);
-
+        ret = snmp_agent_form_reply(requests, dd, &index_oid, i);
         pthread_mutex_unlock(&g_agent->lock);
 
         return ret;
@@ -659,7 +1215,7 @@ static int snmp_agent_table_index_oid_handler(
     struct netsnmp_agent_request_info_s *reqinfo,
     struct netsnmp_request_info_s *requests) {
 
-  if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
+  if (reqinfo->mode != MODE_GET) {
     DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
     return SNMP_ERR_NOERROR;
   }
@@ -675,15 +1231,15 @@ static int snmp_agent_table_index_oid_handler(
     table_definition_t *td = te->value;
 
     if (td->index_oid.oid_len &&
-        (snmp_oid_ncompare(oid.oid, oid.oid_len, td->index_oid.oid,
-                           td->index_oid.oid_len,
-                           MIN(oid.oid_len, td->index_oid.oid_len)) == 0)) {
+        (snmp_oid_ncompare(
+             oid.oid, oid.oid_len, td->index_oid.oid, td->index_oid.oid_len,
+             SNMP_MIN(oid.oid_len, td->index_oid.oid_len)) == 0)) {
 
       DEBUG(PLUGIN_NAME ": Handle '%s' table index OID", td->name);
 
       int index = oid.oid[oid.oid_len - 1];
 
-      int ret = c_avl_get(td->index_instance, &index, &(void *){NULL});
+      int ret = c_avl_get(td->index_instance, &index, NULL);
       if (ret != 0) {
         /* nonexisting index requested */
         pthread_mutex_unlock(&g_agent->lock);
@@ -711,7 +1267,7 @@ static int snmp_agent_table_size_oid_handler(
     struct netsnmp_agent_request_info_s *reqinfo,
     struct netsnmp_request_info_s *requests) {
 
-  if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
+  if (reqinfo->mode != MODE_GET) {
     DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
     return SNMP_ERR_NOERROR;
   }
@@ -731,12 +1287,16 @@ static int snmp_agent_table_size_oid_handler(
     if (td->size_oid.oid_len &&
         (snmp_oid_ncompare(oid.oid, oid.oid_len, td->size_oid.oid,
                            td->size_oid.oid_len,
-                           MIN(oid.oid_len, td->size_oid.oid_len)) == 0)) {
+                           SNMP_MIN(oid.oid_len, td->size_oid.oid_len)) == 0)) {
       DEBUG(PLUGIN_NAME ": Handle '%s' table size OID", td->name);
 
-      long size = c_avl_size(td->index_instance);
+      long size;
+      if (td->index_oid.oid_len)
+        size = c_avl_size(td->index_instance);
+      else
+        size = c_avl_size(td->instance_index);
 
-      requests->requestvb->type = td->size_oid.type;
+      requests->requestvb->type = ASN_INTEGER;
       snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
                                (const u_char *)&size, sizeof(size));
 
@@ -757,7 +1317,7 @@ snmp_agent_scalar_oid_handler(struct netsnmp_mib_handler_s *handler,
                               struct netsnmp_agent_request_info_s *reqinfo,
                               struct netsnmp_request_info_s *requests) {
 
-  if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
+  if (reqinfo->mode != MODE_GET) {
     DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
     return SNMP_ERR_NOERROR;
   }
@@ -860,10 +1420,14 @@ static int snmp_agent_config_data_oids(data_definition_t *dd,
       return -EINVAL;
     }
 
-  if (dd->oids != NULL)
-    sfree(dd->oids);
+  if (dd->oids != NULL) {
+    WARNING(PLUGIN_NAME ": OIDs can be configured only once for each data");
+    return -EINVAL;
+  }
+
   dd->oids_len = 0;
   dd->oids = calloc(ci->values_num, sizeof(*dd->oids));
+
   if (dd->oids == NULL)
     return -ENOMEM;
   dd->oids_len = (size_t)ci->values_num;
@@ -935,98 +1499,125 @@ static int snmp_agent_config_table_index_oid(table_definition_t *td,
   return 0;
 }
 
-static int snmp_agent_config_table_data(table_definition_t *td,
-                                        oconfig_item_t *ci) {
-  data_definition_t *dd;
-  int ret = 0;
+/* Getting index key source that will represent table row */
+static int snmp_agent_config_index_key_source(table_definition_t *td,
+                                              data_definition_t *dd,
+                                              oconfig_item_t *ci) {
+  char *val = NULL;
 
-  assert(ci != NULL);
+  int ret = cf_util_get_string(ci, &val);
+  if (ret != 0)
+    return -1;
 
-  dd = calloc(1, sizeof(*dd));
-  if (dd == NULL) {
-    ERROR(PLUGIN_NAME ": Failed to allocate memory for table data definition");
-    return -ENOMEM;
+  bool match = false;
+
+  for (int i = 0; i < MAX_KEY_SOURCES; i++) {
+    if (strcasecmp(index_opts[i], (const char *)val) == 0) {
+      td->index_keys[td->index_keys_len].source = i;
+      td->index_keys[td->index_keys_len].group = GROUP_UNUSED;
+      td->index_keys[td->index_keys_len].regex = NULL;
+      match = 1;
+      break;
+    }
   }
 
-  ret = cf_util_get_string(ci, &dd->name);
-  if (ret != 0) {
-    sfree(dd);
-    return -1;
+  if (!match) {
+    ERROR(PLUGIN_NAME ": Failed to parse index key source: '%s'", val);
+    sfree(val);
+    return -EINVAL;
   }
 
-  dd->scale = 1.0;
-  dd->shift = 0.0;
+  sfree(val);
+  dd->index_key_pos = td->index_keys_len++;
+  dd->is_index_key = true;
 
-  dd->table = td;
+  return 0;
+}
 
-  for (int i = 0; i < ci->children_num; i++) {
-    oconfig_item_t *option = ci->children + i;
+/* Getting format string used to parse values from index key source */
+static int snmp_agent_config_index_key_regex(table_definition_t *td,
+                                             data_definition_t *dd,
+                                             oconfig_item_t *ci) {
+  index_key_t *index_key = &td->index_keys[dd->index_key_pos];
 
-    if (strcasecmp("Instance", option->key) == 0)
-      ret = cf_util_get_boolean(option, &dd->is_instance);
-    else if (strcasecmp("Plugin", option->key) == 0)
-      ret = cf_util_get_string(option, &dd->plugin);
-    else if (strcasecmp("PluginInstance", option->key) == 0)
-      ret = cf_util_get_string(option, &dd->plugin_instance);
-    else if (strcasecmp("Type", option->key) == 0)
-      ret = cf_util_get_string(option, &dd->type);
-    else if (strcasecmp("TypeInstance", option->key) == 0)
-      ret = cf_util_get_string(option, &dd->type_instance);
-    else if (strcasecmp("Shift", option->key) == 0)
-      ret = cf_util_get_double(option, &dd->shift);
-    else if (strcasecmp("Scale", option->key) == 0)
-      ret = cf_util_get_double(option, &dd->scale);
-    else if (strcasecmp("OIDs", option->key) == 0)
-      ret = snmp_agent_config_data_oids(dd, option);
-    else {
-      WARNING(PLUGIN_NAME ": Option `%s' not allowed here", option->key);
-      ret = -1;
-    }
+  int ret = cf_util_get_string(ci, &index_key->regex);
+  if (ret != 0)
+    return -1;
 
-    if (ret != 0) {
-      snmp_agent_free_data(&dd);
-      return -1;
-    }
+  ret = regcomp(&index_key->regex_info, index_key->regex, REG_EXTENDED);
+  if (ret) {
+    ERROR(PLUGIN_NAME ": Could not compile regex for %s", dd->name);
+    return -1;
   }
 
-  llentry_t *entry = llentry_create(dd->name, dd);
-  if (entry == NULL) {
-    snmp_agent_free_data(&dd);
-    return -ENOMEM;
+  index_key_src_t source = index_key->source;
+  if (td->tokens[source] == NULL) {
+    td->tokens[source] =
+        c_avl_create((int (*)(const void *, const void *))num_compare);
+    if (td->tokens[source] == NULL) {
+      ERROR(PLUGIN_NAME ": Could not allocate memory for AVL tree");
+      return -ENOMEM;
+    }
   }
 
-  llist_append(td->columns, entry);
-
   return 0;
 }
 
-static int snmp_agent_config_data(oconfig_item_t *ci) {
+static int snmp_agent_config_index_key(table_definition_t *td,
+                                       data_definition_t *dd,
+                                       oconfig_item_t *ci) {
+  int ret = 0;
+
+  for (int i = 0; (i < ci->children_num && ret == 0); i++) {
+    oconfig_item_t *option = ci->children + i;
+
+    if (strcasecmp("Source", option->key) == 0)
+      ret = snmp_agent_config_index_key_source(td, dd, option);
+    else if (strcasecmp("Regex", option->key) == 0)
+      ret = snmp_agent_config_index_key_regex(td, dd, option);
+    else if (strcasecmp("Group", option->key) == 0)
+      ret = cf_util_get_int(option, &td->index_keys[dd->index_key_pos].group);
+  }
+
+  return ret;
+}
+
+/* This function parses configuration of both scalar and table column
+ * because they have nearly the same structure */
+static int snmp_agent_config_table_column(table_definition_t *td,
+                                          oconfig_item_t *ci) {
   data_definition_t *dd;
   int ret = 0;
+  oconfig_item_t *option_tmp = NULL;
 
   assert(ci != NULL);
 
   dd = calloc(1, sizeof(*dd));
   if (dd == NULL) {
-    ERROR(PLUGIN_NAME ": Failed to allocate memory for data definition");
+    ERROR(PLUGIN_NAME ": Failed to allocate memory for table data definition");
     return -ENOMEM;
   }
 
   ret = cf_util_get_string(ci, &dd->name);
   if (ret != 0) {
-    free(dd);
+    sfree(dd);
     return -1;
   }
 
   dd->scale = 1.0;
   dd->shift = 0.0;
+  /* NULL if it's a scalar */
+  dd->table = td;
+  dd->is_index_key = false;
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
 
-    if (strcasecmp("Instance", option->key) == 0)
-      ret = cf_util_get_boolean(option, &dd->is_instance);
-    else if (strcasecmp("Plugin", option->key) == 0)
+    /* First 3 options are reserved for table entry only */
+    if (td != NULL && strcasecmp("IndexKey", option->key) == 0) {
+      dd->is_index_key = true;
+      option_tmp = option;
+    } else if (strcasecmp("Plugin", option->key) == 0)
       ret = cf_util_get_string(option, &dd->plugin);
     else if (strcasecmp("PluginInstance", option->key) == 0)
       ret = cf_util_get_string(option, &dd->plugin_instance);
@@ -1051,17 +1642,37 @@ static int snmp_agent_config_data(oconfig_item_t *ci) {
     }
   }
 
+  if (dd->is_index_key) {
+    ret = snmp_agent_config_index_key(td, dd, option_tmp);
+    td->index_keys[dd->index_key_pos].type =
+        snmp_agent_get_asn_type(dd->oids[0].oid, dd->oids[0].oid_len);
+
+    if (ret != 0) {
+      snmp_agent_free_data(&dd);
+      return -1;
+    }
+  }
+
   llentry_t *entry = llentry_create(dd->name, dd);
   if (entry == NULL) {
     snmp_agent_free_data(&dd);
     return -ENOMEM;
   }
 
-  llist_append(g_agent->scalars, entry);
+  /* Append to column list in parent table */
+  if (td != NULL)
+    llist_append(td->columns, entry);
+  else
+    llentry_destroy(entry);
 
   return 0;
 }
 
+/* Parses scalar configuration entry */
+static int snmp_agent_config_scalar(oconfig_item_t *ci) {
+  return snmp_agent_config_table_column(NULL, ci);
+}
+
 static int num_compare(const int *a, const int *b) {
   assert((a != NULL) && (b != NULL));
   if (*a < *b)
@@ -1072,6 +1683,10 @@ static int num_compare(const int *a, const int *b) {
     return 0;
 }
 
+static int oid_compare(const oid_t *a, const oid_t *b) {
+  return snmp_oid_compare(a->oid, a->oid_len, b->oid, b->oid_len);
+}
+
 static int snmp_agent_config_table(oconfig_item_t *ci) {
   table_definition_t *td;
   int ret = 0;
@@ -1097,6 +1712,10 @@ static int snmp_agent_config_table(oconfig_item_t *ci) {
     return -ENOMEM;
   }
 
+  for (int i = 0; i < MAX_KEY_SOURCES; i++)
+    td->tokens[i] = NULL;
+  td->tokens_done = false;
+
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
 
@@ -1105,7 +1724,7 @@ static int snmp_agent_config_table(oconfig_item_t *ci) {
     else if (strcasecmp("SizeOID", option->key) == 0)
       ret = snmp_agent_config_table_size_oid(td, option);
     else if (strcasecmp("Data", option->key) == 0)
-      ret = snmp_agent_config_table_data(td, option);
+      ret = snmp_agent_config_table_column(td, option);
     else {
       WARNING(PLUGIN_NAME ": Option `%s' not allowed here", option->key);
       ret = -1;
@@ -1117,8 +1736,13 @@ static int snmp_agent_config_table(oconfig_item_t *ci) {
     }
   }
 
+  /* Preparing index list container */
+  ret = snmp_agent_prep_index_list(td, &td->index_list_cont);
+  if (ret != 0)
+    return -EINVAL;
+
   td->instance_index =
-      c_avl_create((int (*)(const void *, const void *))strcmp);
+      c_avl_create((int (*)(const void *, const void *))oid_compare);
   if (td->instance_index == NULL) {
     snmp_agent_free_table(&td);
     return -ENOMEM;
@@ -1131,11 +1755,19 @@ static int snmp_agent_config_table(oconfig_item_t *ci) {
     return -ENOMEM;
   }
 
+  td->instance_oids =
+      c_avl_create((int (*)(const void *, const void *))oid_compare);
+  if (td->instance_oids == NULL) {
+    snmp_agent_free_table(&td);
+    return -ENOMEM;
+  }
+
   llentry_t *entry = llentry_create(td->name, td);
   if (entry == NULL) {
     snmp_agent_free_table(&td);
     return -ENOMEM;
   }
+
   llist_append(g_agent->tables, entry);
 
   return 0;
@@ -1236,98 +1868,167 @@ static int snmp_agent_unregister_oid_index(oid_t *oid, int index) {
   oid_t new_oid;
   memcpy(&new_oid, oid, sizeof(*oid));
   new_oid.oid[new_oid.oid_len++] = index;
-  return unregister_mib(new_oid.oid, new_oid.oid_len);
+  return snmp_agent_unregister_oid(&new_oid);
 }
 
-static int snmp_agent_update_index(table_definition_t *td,
-                                   const char *instance) {
+static int snmp_agent_update_instance_oids(c_avl_tree_t *tree, oid_t *index_oid,
+                                           int value) {
+  int *oids_num; /* number of oids registered for instance */
 
-  if (c_avl_get(td->instance_index, instance, NULL) == 0)
-    return 0;
+  if (c_avl_get(tree, index_oid, (void **)&oids_num) == 0) {
+    *oids_num += value;
+    return *oids_num;
+  } else {
+    ERROR(PLUGIN_NAME ": Error updating index data");
+    return -1;
+  }
+}
 
+static int snmp_agent_update_index(data_definition_t *dd,
+                                   table_definition_t *td, oid_t *index_oid,
+                                   bool *free_index_oid) {
   int ret;
   int *index = NULL;
-  char *ins;
+  int *value = NULL;
 
-  ins = strdup(instance);
-  if (ins == NULL)
-    return -ENOMEM;
+  if (c_avl_get(td->instance_index, (void *)index_oid, (void **)&index) != 0) {
+    /* We'll keep index_oid stored in AVL tree */
+    *free_index_oid = false;
 
-  /* need to generate index for the table */
-  if (td->index_oid.oid_len) {
-    index = calloc(1, sizeof(*index));
-    if (index == NULL) {
-      sfree(ins);
-      return -ENOMEM;
+    /* need to generate index for the table */
+    if (td->index_oid.oid_len) {
+      index = calloc(1, sizeof(*index));
+      if (index == NULL) {
+        ret = -ENOMEM;
+        goto error;
+      }
+
+      *index = c_avl_size(td->instance_index) + 1;
+
+      ret = c_avl_insert(td->instance_index, index_oid, index);
+      if (ret != 0)
+        goto free_index;
+
+      ret = c_avl_insert(td->index_instance, index, index_oid);
+      if (ret < 0) {
+        DEBUG(PLUGIN_NAME ": Failed to update index_instance for '%s' table",
+              td->name);
+        goto remove_avl_index_oid;
+      }
+
+      ret = snmp_agent_register_oid_index(&td->index_oid, *index,
+                                          snmp_agent_table_index_oid_handler);
+      if (ret != 0)
+        goto remove_avl_index;
+    } else {
+      /* instance as a key is required for any table */
+      ret = c_avl_insert(td->instance_index, index_oid, NULL);
+      if (ret != 0)
+        goto error;
     }
 
-    *index = c_avl_size(td->instance_index) + 1;
+    value = calloc(1, sizeof(*value));
 
-    ret = c_avl_insert(td->instance_index, ins, index);
-    if (ret != 0) {
-      sfree(ins);
-      sfree(index);
-      return ret;
+    if (value == NULL) {
+      ERROR(PLUGIN_NAME ": Failed to allocate memory");
+      ret = -ENOMEM;
+      goto unregister_index;
     }
 
-    ret = c_avl_insert(td->index_instance, index, ins);
+    ret = c_avl_insert(td->instance_oids, index_oid, value);
+
     if (ret < 0) {
-      DEBUG(PLUGIN_NAME ": Failed to update index_instance for '%s' table",
+      DEBUG(PLUGIN_NAME ": Failed to update instance_oids for '%s' table",
             td->name);
-      c_avl_remove(td->instance_index, ins, NULL, (void **)&index);
-      sfree(ins);
-      sfree(index);
-      return ret;
+      goto free_value;
     }
 
-    ret = snmp_agent_register_oid_index(&td->index_oid, *index,
-                                        snmp_agent_table_index_oid_handler);
-    if (ret != 0)
-      return ret;
-  } else {
-    /* instance as a key is required for any table */
-    ret = c_avl_insert(td->instance_index, ins, ins);
-    if (ret != 0) {
-      sfree(ins);
-      return ret;
-    }
-  }
+    int keys_processed = 0;
 
-  /* register new oids for all columns */
-  for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
-    data_definition_t *dd = de->value;
+    /* Registering index keys OIDs */
+    for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
+      data_definition_t *idd = de->value;
+      if (!idd->is_index_key)
+        continue;
 
-    for (size_t i = 0; i < dd->oids_len; i++) {
-      if (td->index_oid.oid_len) {
-        ret = snmp_agent_register_oid_index(&dd->oids[i], *index,
-                                            snmp_agent_table_oid_handler);
-      } else {
-        ret = snmp_agent_register_oid_string(&dd->oids[i], ins,
-                                             snmp_agent_table_oid_handler);
+      for (size_t i = 0; i < idd->oids_len; i++) {
+        if (td->index_oid.oid_len)
+          ret = snmp_agent_register_oid_index(&idd->oids[i], *index,
+                                              snmp_agent_table_oid_handler);
+        else
+          ret = snmp_agent_register_oid_string(&idd->oids[i], index_oid,
+                                               snmp_agent_table_oid_handler);
+
+        if (ret != 0) {
+          ERROR(PLUGIN_NAME ": Could not register OID");
+          goto free_index;
+        }
       }
 
-      if (ret != 0)
-        return ret;
+      if (++keys_processed >= td->index_keys_len)
+        break;
     }
   }
 
-  DEBUG(PLUGIN_NAME ": Updated index for '%s' table [%d, %s]", td->name,
-        (index != NULL) ? *index : -1, ins);
+  ret = 0;
 
-  notification_t n = {
-      .severity = NOTIF_OKAY, .time = cdtime(), .plugin = PLUGIN_NAME};
-  sstrncpy(n.host, hostname_g, sizeof(n.host));
-  sstrncpy(n.plugin_instance, ins, sizeof(n.plugin_instance));
-  snprintf(n.message, sizeof(n.message),
-           "Data row added to table %s instance %s index %d", td->name, ins,
-           (index != NULL) ? *index : -1);
-  plugin_dispatch_notification(&n);
+  for (size_t i = 0; i < dd->oids_len; i++) {
+    if (td->index_oid.oid_len)
+      ret = snmp_agent_register_oid_index(&dd->oids[i], *index,
+                                          snmp_agent_table_oid_handler);
+    else
+      ret = snmp_agent_register_oid_string(&dd->oids[i], index_oid,
+                                           snmp_agent_table_oid_handler);
+
+    if (ret < 0)
+      goto free_index;
+    else if (ret == OID_EXISTS)
+      break;
+    else if (snmp_agent_update_instance_oids(td->instance_oids, index_oid, 1) <
+             0)
+      goto free_index;
+  }
+
+  if (ret != OID_EXISTS) {
+    char index_str[DATA_MAX_NAME_LEN];
+
+    if (index == NULL)
+      snmp_agent_oid_to_string(index_str, sizeof(index_str), index_oid);
+    else
+      snprintf(index_str, sizeof(index_str), "%d", *index);
+
+    notification_t n = {
+        .severity = NOTIF_OKAY, .time = cdtime(), .plugin = PLUGIN_NAME};
+    sstrncpy(n.host, hostname_g, sizeof(n.host));
+    snprintf(n.message, sizeof(n.message),
+             "Data added to table %s with index %s", td->name, index_str);
+    DEBUG(PLUGIN_NAME ": %s", n.message);
+
+    plugin_dispatch_notification(&n);
+  }
 
   return 0;
+
+free_value:
+  sfree(value);
+unregister_index:
+  if (td->index_oid.oid_len)
+    snmp_agent_unregister_oid_index(index_oid, *index);
+remove_avl_index:
+  if (td->index_oid.oid_len)
+    c_avl_remove(td->index_instance, index, NULL, NULL);
+remove_avl_index_oid:
+  c_avl_remove(td->instance_index, index_oid, NULL, NULL);
+free_index:
+  if (index != NULL)
+    sfree(index);
+error:
+  *free_index_oid = true;
+
+  return ret;
 }
 
 static int snmp_agent_write(value_list_t const *vl) {
-
   if (vl == NULL)
     return -EINVAL;
 
@@ -1337,11 +2038,27 @@ static int snmp_agent_write(value_list_t const *vl) {
     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
       data_definition_t *dd = de->value;
 
-      if (!dd->is_instance) {
+      if (!dd->is_index_key) {
         if (CHECK_DD_TYPE(dd, vl->plugin, vl->plugin_instance, vl->type,
                           vl->type_instance)) {
-          snmp_agent_update_index(td, vl->plugin_instance);
-          return 0;
+          oid_t *index_oid = calloc(1, sizeof(*index_oid));
+          bool free_index_oid = true;
+
+          if (index_oid == NULL) {
+            ERROR(PLUGIN_NAME ": Could not allocate memory for index_oid");
+            return -ENOMEM;
+          }
+
+          int ret = snmp_agent_generate_index(td, vl, index_oid);
+
+          if (ret == 0)
+            ret = snmp_agent_update_index(dd, td, index_oid, &free_index_oid);
+
+          /* Index exists or update failed */
+          if (free_index_oid)
+            sfree(index_oid);
+
+          return ret;
         }
       }
     }
@@ -1363,10 +2080,6 @@ static int snmp_agent_collect(const data_set_t *ds, const value_list_t *vl,
 }
 
 static int snmp_agent_preinit(void) {
-  if (g_agent != NULL) {
-    /* already initialized if config callback was called before init callback */
-    return 0;
-  }
 
   g_agent = calloc(1, sizeof(*g_agent));
   if (g_agent == NULL) {
@@ -1376,22 +2089,26 @@ static int snmp_agent_preinit(void) {
 
   g_agent->tables = llist_create();
   g_agent->scalars = llist_create();
+  g_agent->registered_oids =
+      c_avl_create((int (*)(const void *, const void *))oid_compare);
 
   if (g_agent->tables == NULL || g_agent->scalars == NULL) {
     ERROR(PLUGIN_NAME ": llist_create() failed");
     llist_destroy(g_agent->scalars);
     llist_destroy(g_agent->tables);
+    c_avl_destroy(g_agent->registered_oids);
     return -ENOMEM;
   }
 
   int err;
-  /* make us a agentx client. */
+  /* make us an agentx client. */
   err = netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE,
                                1);
   if (err != 0) {
     ERROR(PLUGIN_NAME ": Failed to set agent role (%d)", err);
     llist_destroy(g_agent->scalars);
     llist_destroy(g_agent->tables);
+    c_avl_destroy(g_agent->registered_oids);
     return -1;
   }
 
@@ -1405,6 +2122,7 @@ static int snmp_agent_preinit(void) {
     ERROR(PLUGIN_NAME ": Failed to initialize the agent library (%d)", err);
     llist_destroy(g_agent->scalars);
     llist_destroy(g_agent->tables);
+    c_avl_destroy(g_agent->registered_oids);
     return -1;
   }
 
@@ -1480,6 +2198,26 @@ static void *snmp_agent_thread_run(void __attribute__((unused)) * arg) {
 
 static int snmp_agent_register_oid(oid_t *oid, Netsnmp_Node_Handler *handler) {
   netsnmp_handler_registration *reg;
+
+  if (c_avl_get(g_agent->registered_oids, (void *)oid, NULL) == 0)
+    return OID_EXISTS;
+  else {
+    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;
+    }
+
+    memcpy(new_oid, oid, sizeof(*oid));
+
+    int ret = c_avl_insert(g_agent->registered_oids, (void *)new_oid, NULL);
+    if (ret != 0) {
+      ERROR(PLUGIN_NAME ": Could not allocate memory to register new OID");
+      sfree(new_oid);
+      return -ENOMEM;
+    }
+  }
+
   char *oid_name = snmp_agent_get_oid_name(oid->oid, oid->oid_len - 1);
   char oid_str[DATA_MAX_NAME_LEN];
 
@@ -1554,13 +2292,22 @@ static int snmp_agent_shutdown(void) {
   pthread_mutex_destroy(&g_agent->lock);
   pthread_mutex_destroy(&g_agent->agentx_lock);
 
+  /* Freeing registered OIDs list */
+  void *oid;
+
+  if (g_agent->registered_oids != NULL) {
+    while (c_avl_pick(g_agent->registered_oids, &oid, NULL) == 0) {
+      sfree(oid);
+    }
+    c_avl_destroy(g_agent->registered_oids);
+  }
+
   sfree(g_agent);
 
   return ret;
 }
 
 static int snmp_agent_config(oconfig_item_t *ci) {
-
   int ret = snmp_agent_preinit();
 
   if (ret != 0) {
@@ -1571,7 +2318,7 @@ static int snmp_agent_config(oconfig_item_t *ci) {
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
     if (strcasecmp("Data", child->key) == 0) {
-      ret = snmp_agent_config_data(child);
+      ret = snmp_agent_config_scalar(child);
     } else if (strcasecmp("Table", child->key) == 0) {
       ret = snmp_agent_config_table(child);
     } else {
@@ -1588,7 +2335,7 @@ static int snmp_agent_config(oconfig_item_t *ci) {
     }
   }
 
-  ret = snmp_agent_validate_data();
+  ret = snmp_agent_validate_config();
   if (ret != 0) {
     ERROR(PLUGIN_NAME ": Invalid configuration provided");
     snmp_agent_free_config();
diff --git a/src/snmp_agent_test.c b/src/snmp_agent_test.c
new file mode 100644 (file)
index 0000000..581f33d
--- /dev/null
@@ -0,0 +1,831 @@
+/**
+ * collectd - src/snmp_agent_test.c
+ *
+ * Copyright(c) 2017 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:
+ *   Marcin Mozejko <marcinx.mozejko@intel.com>
+ **/
+
+#include "snmp_agent.c"
+#include "testing.h"
+
+#define TEST_HOSTNAME "test_hostname"
+#define TEST_PLUGIN "test_plugin"
+#define TEST_PLUGIN_INST "test_plugin_inst"
+#define TEST_TYPE "test_type"
+#define TEST_TYPE_INST "test_type_inst"
+
+DEF_TEST(oid_to_string) {
+  oid_t o = {.oid = {1, 2, 3, 4, 5, 6, 7, 8, 9}, .oid_len = 9};
+  char oid_str[DATA_MAX_NAME_LEN];
+
+  int ret = snmp_agent_oid_to_string(oid_str, DATA_MAX_NAME_LEN, &o);
+  EXPECT_EQ_INT(o.oid_len * 2 - 1, ret);
+  EXPECT_EQ_STR("1.2.3.4.5.6.7.8.9", oid_str);
+
+  return 0;
+}
+
+/* Testing formatting metric name for simple scalar */
+DEF_TEST(format_name_scalar) {
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+
+  dd->plugin = TEST_PLUGIN;
+  dd->plugin_instance = TEST_PLUGIN_INST;
+  dd->type = TEST_TYPE;
+  dd->type_instance = TEST_TYPE_INST;
+
+  char name[DATA_MAX_NAME_LEN];
+  int ret = snmp_agent_format_name(name, sizeof(name), dd, NULL);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR(
+      "example.com/test_plugin-test_plugin_inst/test_type-test_type_inst",
+      name);
+
+  sfree(dd);
+
+  return 0;
+}
+
+DEF_TEST(format_name_simple_index) {
+  netsnmp_variable_list *index_list_tmp = NULL;
+  oid_t index_oid;
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+  table_definition_t *td = calloc(1, sizeof(*td));
+
+  td->index_list_cont = NULL;
+  td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[0].type = ASN_OCTET_STR;
+  td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[1].type = ASN_OCTET_STR;
+  dd->table = td;
+  dd->plugin = TEST_PLUGIN;
+  dd->type = TEST_TYPE;
+
+  const char plugin_inst[] = TEST_PLUGIN_INST;
+  const char type_inst[] = TEST_TYPE_INST;
+
+  snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+                            (const u_char *)plugin_inst, strlen(plugin_inst));
+  snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+                            (const u_char *)type_inst, strlen(type_inst));
+
+  build_oid_noalloc(index_oid.oid, sizeof(index_oid.oid), &index_oid.oid_len,
+                    NULL, 0, index_list_tmp);
+
+  snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+                            0);
+  snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+                            0);
+
+  char name[DATA_MAX_NAME_LEN];
+
+  int ret = snmp_agent_format_name(name, DATA_MAX_NAME_LEN, dd, &index_oid);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR(
+      "example.com/test_plugin-test_plugin_inst/test_type-test_type_inst",
+      name);
+
+  snmp_free_varbind(index_list_tmp);
+  snmp_free_varbind(td->index_list_cont);
+  sfree(dd);
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(format_name_regex_index) {
+  netsnmp_variable_list *index_list_tmp = NULL;
+  oid_t index_oid;
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+  table_definition_t *td = calloc(1, sizeof(*td));
+
+  td->index_keys_len = 3;
+  td->index_list_cont = NULL;
+  td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[0].type = ASN_OCTET_STR;
+  td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[1].type = ASN_INTEGER;
+  td->index_keys[1].regex = "^vcpu_([0-9]{1,3})-cpu_[0-9]{1,3}$";
+  td->index_keys[1].group = 1;
+  td->index_keys[2].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[2].type = ASN_INTEGER;
+  td->index_keys[2].regex = "^vcpu_[0-9]{1,3}-cpu_([0-9]{1,3})$";
+  td->index_keys[2].group = 1;
+
+  dd->table = td;
+  dd->plugin = TEST_PLUGIN;
+  dd->type = TEST_TYPE;
+
+  const char plugin_inst[] = TEST_PLUGIN_INST;
+  int vcpu = 1;
+  int cpu = 10;
+
+  snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+                            (const u_char *)plugin_inst, strlen(plugin_inst));
+  snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_INTEGER,
+                            (const u_char *)&vcpu, sizeof(vcpu));
+  snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_INTEGER,
+                            (const u_char *)&cpu, sizeof(cpu));
+
+  build_oid_noalloc(index_oid.oid, sizeof(index_oid.oid), &index_oid.oid_len,
+                    NULL, 0, index_list_tmp);
+
+  token_t *token;
+  int *offset;
+
+  td->tokens[INDEX_TYPE_INSTANCE] =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+                            0);
+
+  token = malloc(sizeof(*token));
+  offset = malloc(sizeof(*offset));
+  token->key = snmp_varlist_add_variable(&td->index_list_cont, NULL, 0,
+                                         ASN_INTEGER, NULL, 0);
+  token->str = strdup("vcpu_");
+  *offset = 0;
+  int ret = c_avl_insert(td->tokens[INDEX_TYPE_INSTANCE], (void *)offset,
+                         (void *)token);
+
+  token = malloc(sizeof(*token));
+  offset = malloc(sizeof(*offset));
+  token->key = snmp_varlist_add_variable(&td->index_list_cont, NULL, 0,
+                                         ASN_INTEGER, NULL, 0);
+  token->str = strdup("-cpu_");
+  *offset = 6;
+  ret += c_avl_insert(td->tokens[INDEX_TYPE_INSTANCE], (void *)offset,
+                      (void *)token);
+  char name[DATA_MAX_NAME_LEN];
+
+  ret += snmp_agent_format_name(name, DATA_MAX_NAME_LEN, dd, &index_oid);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR(
+      "example.com/test_plugin-test_plugin_inst/test_type-vcpu_1-cpu_10", name);
+  while (c_avl_pick(td->tokens[INDEX_TYPE_INSTANCE], (void **)&offset,
+                    (void **)&token) == 0) {
+    sfree(offset);
+    sfree(token->str);
+    sfree(token);
+  }
+  c_avl_destroy(td->tokens[INDEX_TYPE_INSTANCE]);
+  snmp_free_varbind(index_list_tmp);
+  snmp_free_varbind(td->index_list_cont);
+  sfree(dd);
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(prep_index_list) {
+  table_definition_t *td = calloc(1, sizeof(*td));
+
+  assert(td != NULL);
+  td->index_keys_len = 5;
+  td->index_keys[0].source = INDEX_HOST;
+  td->index_keys[0].type = ASN_OCTET_STR;
+  td->index_keys[1].source = INDEX_PLUGIN;
+  td->index_keys[1].type = ASN_OCTET_STR;
+  td->index_keys[2].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[2].type = ASN_INTEGER;
+  td->index_keys[3].source = INDEX_TYPE;
+  td->index_keys[3].type = ASN_INTEGER;
+  td->index_keys[4].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[4].type = ASN_OCTET_STR;
+  td->index_list_cont = NULL;
+
+  int ret = snmp_agent_prep_index_list(td, &td->index_list_cont);
+  EXPECT_EQ_INT(0, ret);
+
+  netsnmp_variable_list *key = td->index_list_cont;
+
+  OK(key != NULL);
+  EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(ASN_INTEGER, key->type);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(ASN_INTEGER, key->type);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+  key = key->next_variable;
+  OK(key == NULL);
+
+  snmp_free_varbind(td->index_list_cont);
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(fill_index_list_simple) {
+  table_definition_t *td = calloc(1, sizeof(*td));
+  assert(td != NULL);
+
+  /* Preparing value list */
+  value_list_t *vl = calloc(1, sizeof(*vl));
+  assert(vl != NULL);
+  strncpy(vl->host, TEST_HOSTNAME, DATA_MAX_NAME_LEN);
+  strncpy(vl->plugin, TEST_PLUGIN, DATA_MAX_NAME_LEN);
+  strncpy(vl->plugin_instance, TEST_PLUGIN_INST, DATA_MAX_NAME_LEN);
+  strncpy(vl->type, TEST_TYPE, DATA_MAX_NAME_LEN);
+  strncpy(vl->type_instance, TEST_TYPE_INST, DATA_MAX_NAME_LEN);
+
+  td->index_keys_len = 5;
+  td->index_keys[0].source = INDEX_HOST;
+  td->index_keys[0].type = ASN_OCTET_STR;
+  td->index_keys[1].source = INDEX_PLUGIN;
+  td->index_keys[1].type = ASN_OCTET_STR;
+  td->index_keys[2].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[2].type = ASN_OCTET_STR;
+  td->index_keys[3].source = INDEX_TYPE;
+  td->index_keys[3].type = ASN_OCTET_STR;
+  td->index_keys[4].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[4].type = ASN_OCTET_STR;
+
+  td->index_list_cont = NULL;
+  for (int i = 0; i < td->index_keys_len; i++)
+    snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR,
+                              NULL, 0);
+
+  int ret = snmp_agent_fill_index_list(td, vl);
+  EXPECT_EQ_INT(0, ret);
+
+  netsnmp_variable_list *key = td->index_list_cont;
+
+  ret = 0;
+
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->host, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->plugin, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->plugin_instance, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->type, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->type_instance, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key == NULL);
+
+  snmp_free_varbind(td->index_list_cont);
+  sfree(vl);
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(fill_index_list_regex) {
+  table_definition_t *td = calloc(1, sizeof(*td));
+  int ret = 0;
+
+  assert(td != NULL);
+
+  /* Preparing value list */
+  value_list_t *vl = calloc(1, sizeof(*vl));
+  strncpy(vl->plugin_instance, TEST_PLUGIN_INST, DATA_MAX_NAME_LEN);
+  strncpy(vl->type_instance, "1test2test3", DATA_MAX_NAME_LEN);
+
+  td->index_keys_len = 4;
+  td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[0].type = ASN_OCTET_STR;
+  td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[1].type = ASN_INTEGER;
+  td->index_keys[1].regex = "^([0-9])test[0-9]test[0-9]$";
+  td->index_keys[1].group = 1;
+  td->index_keys[2].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[2].type = ASN_INTEGER;
+  td->index_keys[2].regex = "^[0-9]test([0-9])test[0-9]$";
+  td->index_keys[2].group = 1;
+  td->index_keys[3].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[3].type = ASN_INTEGER;
+  td->index_keys[3].regex = "^[0-9]test[0-9]test([0-9])$";
+  td->index_keys[3].group = 1;
+
+  td->index_list_cont = NULL;
+  snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+                            0);
+  for (int i = 1; i < td->index_keys_len; i++) {
+    snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_INTEGER, NULL,
+                              0);
+    ret = regcomp(&td->index_keys[i].regex_info, td->index_keys[i].regex,
+                  REG_EXTENDED);
+    EXPECT_EQ_INT(0, ret);
+  }
+  td->tokens[INDEX_TYPE_INSTANCE] =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  assert(td->tokens[INDEX_TYPE_INSTANCE] != NULL);
+
+  ret = snmp_agent_fill_index_list(td, vl);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, td->tokens_done);
+
+  netsnmp_variable_list *key = td->index_list_cont;
+
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->plugin_instance, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(1, *key->val.integer);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(2, *key->val.integer);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(3, *key->val.integer);
+  key = key->next_variable;
+  OK(key == NULL);
+
+  token_t *token;
+  int *offset;
+
+  while (c_avl_pick(td->tokens[INDEX_TYPE_INSTANCE], (void **)&offset,
+                    (void **)&token) == 0) {
+    sfree(offset);
+    sfree(token->str);
+    sfree(token);
+  }
+
+  c_avl_destroy(td->tokens[INDEX_TYPE_INSTANCE]);
+  snmp_free_varbind(td->index_list_cont);
+  sfree(vl);
+
+  for (int i = 0; i < td->index_keys_len; i++) {
+    regfree(&td->index_keys[i].regex_info);
+  }
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(config_index_key_source) {
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+  table_definition_t *td = calloc(1, sizeof(*td));
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+
+  assert(ci != NULL);
+  assert(td != NULL);
+  assert(dd != NULL);
+
+  ci->values = calloc(1, sizeof(*ci->values));
+  assert(ci->values != NULL);
+  ci->values_num = 1;
+  ci->values->value.string = "PluginInstance";
+  ci->values->type = OCONFIG_TYPE_STRING;
+
+  int ret = snmp_agent_config_index_key_source(td, dd, ci);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, td->index_keys_len);
+  EXPECT_EQ_INT(0, dd->index_key_pos);
+  EXPECT_EQ_INT(INDEX_PLUGIN_INSTANCE, td->index_keys[0].source);
+  EXPECT_EQ_INT(GROUP_UNUSED, td->index_keys[0].group);
+  OK(td->index_keys[0].regex == NULL);
+
+  sfree(ci->values);
+  sfree(ci);
+  sfree(td);
+  sfree(dd);
+
+  return 0;
+}
+
+DEF_TEST(config_index_key_regex) {
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+  table_definition_t *td = calloc(1, sizeof(*td));
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+
+  assert(ci != NULL);
+  assert(td != NULL);
+  assert(dd != NULL);
+
+  dd->index_key_pos = 0;
+  td->index_keys_len = 1;
+  td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[0].group = 1;
+  ci->values = calloc(1, sizeof(*ci->values));
+  assert(ci->values != NULL);
+  ci->values_num = 1;
+  ci->values->value.string = "^([0-9])test[0-9]test[0-9]$";
+  ci->values->type = OCONFIG_TYPE_STRING;
+
+  int ret = snmp_agent_config_index_key_regex(td, dd, ci);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR(td->index_keys[0].regex, "^([0-9])test[0-9]test[0-9]$");
+  OK(td->tokens[INDEX_PLUGIN_INSTANCE] != NULL);
+
+  c_avl_destroy(td->tokens[INDEX_PLUGIN_INSTANCE]);
+  sfree(ci->values);
+  sfree(ci);
+  sfree(td->index_keys[0].regex);
+  regfree(&td->index_keys[0].regex_info);
+  sfree(td);
+  sfree(dd);
+
+  return 0;
+}
+
+DEF_TEST(config_index_key) {
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+  table_definition_t *td = calloc(1, sizeof(*td));
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+
+  assert(ci != NULL);
+  assert(td != NULL);
+  assert(dd != NULL);
+
+  ci->children_num = 3;
+  ci->children = calloc(1, sizeof(*ci->children) * ci->children_num);
+
+  ci->children[0].key = "Source";
+  ci->children[0].parent = ci;
+  ci->children[0].values_num = 1;
+  ci->children[0].values = calloc(1, sizeof(*ci->children[0].values));
+  assert(ci->children[0].values != NULL);
+  ci->children[0].values->value.string = "PluginInstance";
+  ci->children[0].values->type = OCONFIG_TYPE_STRING;
+
+  ci->children[1].key = "Regex";
+  ci->children[1].parent = ci;
+  ci->children[1].values_num = 1;
+  ci->children[1].values = calloc(1, sizeof(*ci->children[0].values));
+  assert(ci->children[1].values != NULL);
+  ci->children[1].values->value.string = "^([0-9])test[0-9]test[0-9]$";
+  ci->children[1].values->type = OCONFIG_TYPE_STRING;
+
+  ci->children[2].key = "Group";
+  ci->children[2].parent = ci;
+  ci->children[2].values_num = 1;
+  ci->children[2].values = calloc(1, sizeof(*ci->children[0].values));
+  assert(ci->children[2].values != NULL);
+  ci->children[2].values->value.number = 1;
+  ci->children[2].values->type = OCONFIG_TYPE_NUMBER;
+
+  int ret = snmp_agent_config_index_key(td, dd, ci);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, td->index_keys_len);
+  EXPECT_EQ_INT(0, dd->index_key_pos);
+  EXPECT_EQ_INT(INDEX_PLUGIN_INSTANCE, td->index_keys[0].source);
+  EXPECT_EQ_INT(1, td->index_keys[0].group);
+  EXPECT_EQ_STR("^([0-9])test[0-9]test[0-9]$", td->index_keys[0].regex);
+  OK(td->tokens[INDEX_PLUGIN_INSTANCE] != NULL);
+
+  sfree(ci->children[0].values);
+  sfree(ci->children[1].values);
+  sfree(ci->children[2].values);
+
+  sfree(ci->children);
+  sfree(ci);
+
+  c_avl_destroy(td->tokens[INDEX_PLUGIN_INSTANCE]);
+  sfree(dd);
+  sfree(td->index_keys[0].regex);
+  regfree(&td->index_keys[0].regex_info);
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(parse_index_key) {
+  const char regex[] = "test-([0-9])-([0-9])";
+  const char input[] = "snmp-test-5-6";
+  regex_t regex_info;
+  regmatch_t match;
+
+  int ret = regcomp(&regex_info, regex, REG_EXTENDED);
+  EXPECT_EQ_INT(0, ret);
+
+  ret = snmp_agent_parse_index_key(input, &regex_info, 0, &match);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(5, match.rm_so);
+  EXPECT_EQ_INT(13, match.rm_eo);
+
+  ret = snmp_agent_parse_index_key(input, &regex_info, 1, &match);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(10, match.rm_so);
+  EXPECT_EQ_INT(11, match.rm_eo);
+
+  ret = snmp_agent_parse_index_key(input, &regex_info, 2, &match);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(12, match.rm_so);
+  EXPECT_EQ_INT(13, match.rm_eo);
+
+  regfree(&regex_info);
+
+  return 0;
+}
+
+DEF_TEST(create_token) {
+  c_avl_tree_t *tokens =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  const char input[] = "testA1-testB2";
+
+  assert(tokens != NULL);
+
+  int ret = snmp_agent_create_token(input, 0, 5, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = snmp_agent_create_token(input, 6, 6, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(2, c_avl_size(tokens));
+
+  token_t *token;
+  int *offset;
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(6, *offset);
+  EXPECT_EQ_STR("-testB", token->str);
+  sfree(offset);
+  sfree(token->str);
+  sfree(token);
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(0, *offset);
+  EXPECT_EQ_STR("testA", token->str);
+  sfree(offset);
+  sfree(token->str);
+  sfree(token);
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  OK(ret != 0);
+
+  c_avl_destroy(tokens);
+
+  return 0;
+}
+
+DEF_TEST(delete_token) {
+  c_avl_tree_t *tokens =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  const char input[] = "testA1-testB2-testC3";
+
+  assert(tokens != NULL);
+
+  int ret = snmp_agent_create_token(input, 0, 5, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = snmp_agent_create_token(input, 6, 6, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = snmp_agent_create_token(input, 13, 6, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(3, c_avl_size(tokens));
+  ret = snmp_agent_delete_token(6, tokens);
+  EXPECT_EQ_INT(0, ret);
+
+  token_t *token;
+  int *offset;
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(0, *offset);
+  EXPECT_EQ_STR("testA", token->str);
+  sfree(offset);
+  sfree(token->str);
+  sfree(token);
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(13, *offset);
+  EXPECT_EQ_STR("-testC", token->str);
+  sfree(offset);
+  sfree(token->str);
+  sfree(token);
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  OK(ret != 0);
+
+  c_avl_destroy(tokens);
+
+  return 0;
+}
+
+DEF_TEST(get_token) {
+  c_avl_tree_t *tokens =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  const char input[] = "testA1-testB2-testC3";
+
+  assert(tokens != NULL);
+
+  int ret = snmp_agent_create_token(input, 0, 5, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = snmp_agent_create_token(input, 6, 6, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = snmp_agent_create_token(input, 13, 6, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(3, c_avl_size(tokens));
+  ret = snmp_agent_get_token(tokens, 12);
+  EXPECT_EQ_INT(6, ret);
+
+  token_t *token;
+  int *offset;
+
+  while (c_avl_pick(tokens, (void **)&offset, (void **)&token) == 0) {
+    sfree(offset);
+    sfree(token->str);
+    sfree(token);
+  }
+
+  c_avl_destroy(tokens);
+
+  return 0;
+}
+
+DEF_TEST(tokenize) {
+  regmatch_t m[3];
+
+  m[0].rm_so = 5;
+  m[0].rm_eo = 6;
+  m[1].rm_so = 12;
+  m[1].rm_eo = 13;
+  m[2].rm_so = 19;
+  m[2].rm_eo = 20;
+
+  c_avl_tree_t *tokens =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  const char input[] = "testA1-testB2-testC3";
+  token_t *token;
+  int *offset;
+  c_avl_iterator_t *it;
+  int ret;
+
+  assert(tokens != NULL);
+
+  /* First pass */
+  ret = snmp_agent_tokenize(input, tokens, &m[0], NULL);
+  EXPECT_EQ_INT(0, ret);
+  it = c_avl_get_iterator(tokens);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("testA", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("-testB2-testC3", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  OK(ret != 0);
+  c_avl_iterator_destroy(it);
+
+  /* Second pass */
+  ret = snmp_agent_tokenize(input, tokens, &m[1], NULL);
+  EXPECT_EQ_INT(0, ret);
+  it = c_avl_get_iterator(tokens);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("testA", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("-testB", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("-testC3", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  OK(ret != 0);
+  c_avl_iterator_destroy(it);
+
+  /* Third pass */
+  ret = snmp_agent_tokenize(input, tokens, &m[2], NULL);
+  EXPECT_EQ_INT(0, ret);
+  it = c_avl_get_iterator(tokens);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("testA", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("-testB", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("-testC", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  OK(ret != 0);
+  c_avl_iterator_destroy(it);
+
+  while (c_avl_pick(tokens, (void **)&offset, (void **)&token) == 0) {
+    sfree(offset);
+    sfree(token->str);
+    sfree(token);
+  }
+
+  c_avl_destroy(tokens);
+
+  return 0;
+}
+
+DEF_TEST(build_name) {
+  table_definition_t *td = calloc(1, sizeof(*td));
+  c_avl_tree_t *tokens =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+
+  assert(tokens != NULL);
+  assert(td != NULL);
+
+  int n[3] = {1, 2, 3};
+  char *t[3] = {"testA", "-testB", "-testC"};
+  int off[3] = {0, 6, 13};
+  token_t *token;
+  int *offset;
+  int ret = 0;
+  char *name = NULL;
+
+  td->index_list_cont = NULL;
+  for (int i = 0; i < 3; i++) {
+    token = malloc(sizeof(*token));
+    token->str = t[i];
+    token->key =
+        snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_INTEGER,
+                                  (const u_char *)&n[i], sizeof(n[i]));
+    assert(token->key != NULL);
+    offset = &off[i];
+    ret = c_avl_insert(tokens, (void *)offset, (void *)token);
+    assert(ret == 0);
+  }
+
+  ret = snmp_agent_build_name(&name, tokens);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("testA1-testB2-testC3", name);
+
+  while (c_avl_pick(tokens, (void **)&offset, (void **)&token) == 0)
+    sfree(token);
+
+  c_avl_destroy(tokens);
+  snmp_free_varbind(td->index_list_cont);
+  sfree(td);
+  sfree(name);
+  return 0;
+}
+
+int main(void) {
+  /* snmp_agent_oid_to_string */
+  RUN_TEST(oid_to_string);
+
+  /* snmp_agent_prep_index_list */
+  RUN_TEST(prep_index_list);
+
+  /* snmp_agent_fill_index_list */
+  RUN_TEST(fill_index_list_simple);
+  RUN_TEST(fill_index_list_regex);
+
+  /* snmp_agent_format_name */
+  RUN_TEST(format_name_scalar);
+  RUN_TEST(format_name_simple_index);
+  RUN_TEST(format_name_regex_index);
+
+  /* snmp_agent_config_index_key_source */
+  RUN_TEST(config_index_key_source);
+
+  /* snmp_agent_config_index_key_regex */
+  RUN_TEST(config_index_key_regex);
+
+  /* snmp_agent_config_index_key */
+  RUN_TEST(config_index_key);
+
+  /*snmp_agent_parse_index_key */
+  RUN_TEST(parse_index_key);
+
+  /* snmp_agent_create_token */
+  RUN_TEST(create_token);
+
+  /* snmp_agent_delete_token */
+  RUN_TEST(delete_token);
+
+  /* snmp_agent_get_token */
+  RUN_TEST(get_token);
+
+  /* snmp_agent_tokenize */
+  RUN_TEST(tokenize);
+
+  /* snmp_agent_build_name */
+  RUN_TEST(build_name);
+
+  END_TEST;
+}
index 5142613..6c7820a 100644 (file)
 
 #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>
@@ -61,29 +61,29 @@ struct statsd_metric_s {
 };
 typedef struct statsd_metric_s statsd_metric_t;
 
-static c_avl_tree_t *metrics_tree = NULL;
+static c_avl_tree_t *metrics_tree;
 static pthread_mutex_t metrics_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static pthread_t network_thread;
-static _Bool network_thread_running = 0;
-static _Bool network_thread_shutdown = 0;
+static bool network_thread_running;
+static bool network_thread_shutdown;
 
-static char *conf_node = NULL;
-static char *conf_service = NULL;
+static char *conf_node;
+static char *conf_service;
 
-static _Bool conf_delete_counters = 0;
-static _Bool conf_delete_timers = 0;
-static _Bool conf_delete_gauges = 0;
-static _Bool conf_delete_sets = 0;
+static bool conf_delete_counters;
+static bool conf_delete_timers;
+static bool conf_delete_gauges;
+static bool conf_delete_sets;
 
-static double *conf_timer_percentile = NULL;
-static size_t conf_timer_percentile_num = 0;
+static double *conf_timer_percentile;
+static size_t conf_timer_percentile_num;
 
-static _Bool conf_counter_sum = 0;
-static _Bool conf_timer_lower = 0;
-static _Bool conf_timer_upper = 0;
-static _Bool conf_timer_sum = 0;
-static _Bool conf_timer_count = 0;
+static bool conf_counter_sum;
+static bool conf_timer_lower;
+static bool conf_timer_upper;
+static bool conf_timer_sum;
+static bool conf_timer_count;
 
 /* Must hold metrics_lock when calling this function. */
 static statsd_metric_t *statsd_metric_lookup_unsafe(char const *name, /* {{{ */
@@ -352,9 +352,8 @@ static int statsd_handle_set(char const *name, /* {{{ */
   status = c_avl_insert(metric->set, set_key, /* value = */ NULL);
   if (status < 0) {
     pthread_mutex_unlock(&metrics_lock);
-    if (status < 0)
-      ERROR("statsd plugin: c_avl_insert (\"%s\") failed with status %i.",
-            set_key, status);
+    ERROR("statsd plugin: c_avl_insert (\"%s\") failed with status %i.",
+          set_key, status);
     sfree(set_key);
     return -1;
   } else if (status > 0) /* key already exists */
@@ -446,13 +445,11 @@ static void statsd_network_read(int fd) /* {{{ */
 
   status = recv(fd, buffer, sizeof(buffer), /* flags = */ MSG_DONTWAIT);
   if (status < 0) {
-    char errbuf[1024];
 
     if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
       return;
 
-    ERROR("statsd plugin: recv(2) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("statsd plugin: recv(2) failed: %s", STRERRNO);
     return;
   }
 
@@ -492,28 +489,33 @@ static int statsd_network_init(struct pollfd **ret_fds, /* {{{ */
     int fd;
     struct pollfd *tmp;
 
-    char dbg_node[NI_MAXHOST];
-    char dbg_service[NI_MAXSERV];
+    char str_node[NI_MAXHOST];
+    char str_service[NI_MAXSERV];
 
     fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (fd < 0) {
-      char errbuf[1024];
-      ERROR("statsd plugin: socket(2) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("statsd plugin: socket(2) failed: %s", STRERRNO);
       continue;
     }
 
-    getnameinfo(ai_ptr->ai_addr, ai_ptr->ai_addrlen, dbg_node, sizeof(dbg_node),
-                dbg_service, sizeof(dbg_service),
+    /* allow multiple sockets to use the same PORT number */
+    int yes = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
+      ERROR("statsd plugin: setsockopt (reuseaddr): %s", STRERRNO);
+      close(fd);
+      continue;
+    }
+
+    getnameinfo(ai_ptr->ai_addr, ai_ptr->ai_addrlen, str_node, sizeof(str_node),
+                str_service, sizeof(str_service),
                 NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV);
-    DEBUG("statsd plugin: Trying to bind to [%s]:%s ...", dbg_node,
-          dbg_service);
+    DEBUG("statsd plugin: Trying to bind to [%s]:%s ...", str_node,
+          str_service);
 
     status = bind(fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
     if (status != 0) {
-      char errbuf[1024];
-      ERROR("statsd plugin: bind(2) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("statsd plugin: bind(2) to [%s]:%s failed: %s", str_node,
+            str_service, STRERRNO);
       close(fd);
       continue;
     }
@@ -531,6 +533,7 @@ static int statsd_network_init(struct pollfd **ret_fds, /* {{{ */
     memset(tmp, 0, sizeof(*tmp));
     tmp->fd = fd;
     tmp->events = POLLIN | POLLPRI;
+    INFO("statsd plugin: Listening on [%s]:%s.", str_node, str_service);
   }
 
   freeaddrinfo(ai_list);
@@ -561,13 +564,11 @@ static void *statsd_network_thread(void *args) /* {{{ */
   while (!network_thread_shutdown) {
     status = poll(fds, (nfds_t)fds_num, /* timeout = */ -1);
     if (status < 0) {
-      char errbuf[1024];
 
       if ((errno == EINTR) || (errno == EAGAIN))
         continue;
 
-      ERROR("statsd plugin: poll(2) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("statsd plugin: poll(2) failed: %s", STRERRNO);
       break;
     }
 
@@ -669,14 +670,12 @@ static int statsd_init(void) /* {{{ */
                             /* attr = */ NULL, statsd_network_thread,
                             /* args = */ NULL);
     if (status != 0) {
-      char errbuf[1024];
       pthread_mutex_unlock(&metrics_lock);
-      ERROR("statsd plugin: pthread_create failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("statsd plugin: pthread_create failed: %s", STRERRNO);
       return status;
     }
   }
-  network_thread_running = 1;
+  network_thread_running = true;
 
   pthread_mutex_unlock(&metrics_lock);
 
@@ -727,7 +726,7 @@ static int statsd_metric_submit_unsafe(char const *name,
   if (metric->type == STATSD_GAUGE)
     vl.values[0].gauge = (gauge_t)metric->value;
   else if (metric->type == STATSD_TIMER) {
-    _Bool have_events = (metric->updates_num > 0);
+    bool have_events = (metric->updates_num > 0);
 
     /* Make sure all timer metrics share the *same* timestamp. */
     vl.time = cdtime();
@@ -886,11 +885,11 @@ static int statsd_shutdown(void) /* {{{ */
   void *value;
 
   if (network_thread_running) {
-    network_thread_shutdown = 1;
+    network_thread_shutdown = true;
     pthread_kill(network_thread, SIGTERM);
     pthread_join(network_thread, /* retval = */ NULL);
   }
-  network_thread_running = 0;
+  network_thread_running = false;
 
   pthread_mutex_lock(&metrics_lock);
 
index 78f05c5..9e58919 100644 (file)
@@ -37,8 +37,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_SYS_SWAP_H
 #include <sys/swap.h>
 #if KERNEL_LINUX
 #define SWAP_HAVE_REPORT_BY_DEVICE 1
 static derive_t pagesize;
-static _Bool report_bytes = 0;
-static _Bool report_by_device = 0;
+static bool report_bytes;
+static bool report_by_device;
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
 #define SWAP_HAVE_REPORT_BY_DEVICE 1
 static derive_t pagesize;
-static _Bool report_by_device = 0;
+static bool report_by_device;
 /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
 
 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS
@@ -93,7 +93,7 @@ static _Bool report_by_device = 0;
 /* #endif defined(VM_SWAPUSAGE) */
 
 #elif HAVE_LIBKVM_GETSWAPINFO
-static kvm_t *kvm_obj = NULL;
+static kvm_t *kvm_obj;
 int kvm_pagesize;
 /* #endif HAVE_LIBKVM_GETSWAPINFO */
 
@@ -109,9 +109,9 @@ static int pagesize;
 #error "No applicable input method."
 #endif /* HAVE_LIBSTATGRAB */
 
-static _Bool values_absolute = 1;
-static _Bool values_percentage = 0;
-static _Bool report_io = 1;
+static bool values_absolute = true;
+static bool values_percentage;
+static bool report_io = true;
 
 static int swap_config(oconfig_item_t *ci) /* {{{ */
 {
@@ -203,10 +203,10 @@ static void swap_submit_usage(char const *plugin_instance, /* {{{ */
   sstrncpy(vl.type, "swap", sizeof(vl.type));
 
   if (values_absolute)
-    plugin_dispatch_multivalue(&vl, 0, DS_TYPE_GAUGE, "used", used, "free",
+    plugin_dispatch_multivalue(&vl, false, DS_TYPE_GAUGE, "used", used, "free",
                                free, other_name, other_value, NULL);
   if (values_percentage)
-    plugin_dispatch_multivalue(&vl, 1, DS_TYPE_GAUGE, "used", used, "free",
+    plugin_dispatch_multivalue(&vl, true, DS_TYPE_GAUGE, "used", used, "free",
                                free, other_name, other_value, NULL);
 } /* }}} void swap_submit_usage */
 
@@ -234,9 +234,7 @@ static int swap_read_separate(void) /* {{{ */
 
   fh = fopen("/proc/swaps", "r");
   if (fh == NULL) {
-    char errbuf[1024];
-    WARNING("swap plugin: fopen (/proc/swaps) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("swap plugin: fopen (/proc/swaps) failed: %s", STRERRNO);
     return -1;
   }
 
@@ -291,9 +289,7 @@ static int swap_read_combined(void) /* {{{ */
 
   fh = fopen("/proc/meminfo", "r");
   if (fh == NULL) {
-    char errbuf[1024];
-    WARNING("swap plugin: fopen (/proc/meminfo) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("swap plugin: fopen (/proc/meminfo) failed: %s", STRERRNO);
     return -1;
   }
 
@@ -336,53 +332,31 @@ static int swap_read_combined(void) /* {{{ */
 
 static int swap_read_io(void) /* {{{ */
 {
-  FILE *fh;
   char buffer[1024];
 
-  _Bool old_kernel = 0;
-
   uint8_t have_data = 0;
   derive_t swap_in = 0;
   derive_t swap_out = 0;
 
-  fh = fopen("/proc/vmstat", "r");
+  FILE *fh = fopen("/proc/vmstat", "r");
   if (fh == NULL) {
-    /* /proc/vmstat does not exist in kernels <2.6 */
-    fh = fopen("/proc/stat", "r");
-    if (fh == NULL) {
-      char errbuf[1024];
-      WARNING("swap: fopen: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
-      return -1;
-    } else
-      old_kernel = 1;
+    WARNING("swap: fopen(/proc/vmstat): %s", STRERRNO);
+    return -1;
   }
 
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
     char *fields[8];
-    int numfields;
+    int numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
 
-    numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
+    if (numfields != 2)
+      continue;
 
-    if (!old_kernel) {
-      if (numfields != 2)
-        continue;
-
-      if (strcasecmp("pswpin", fields[0]) == 0) {
-        strtoderive(fields[1], &swap_in);
-        have_data |= 0x01;
-      } else if (strcasecmp("pswpout", fields[0]) == 0) {
-        strtoderive(fields[1], &swap_out);
-        have_data |= 0x02;
-      }
-    } else /* if (old_kernel) */
-    {
-      if (numfields != 3)
-        continue;
-
-      if (strcasecmp("page", fields[0]) == 0) {
-        strtoderive(fields[1], &swap_in);
-        strtoderive(fields[2], &swap_out);
-      }
+    if (strcasecmp("pswpin", fields[0]) == 0) {
+      strtoderive(fields[1], &swap_in);
+      have_data |= 0x01;
+    } else if (strcasecmp("pswpout", fields[0]) == 0) {
+      strtoderive(fields[1], &swap_out);
+      have_data |= 0x02;
     }
   } /* while (fgets) */
 
@@ -436,9 +410,7 @@ static int swap_read_kstat(void) /* {{{ */
   struct anoninfo ai;
 
   if (swapctl(SC_AINFO, &ai) == -1) {
-    char errbuf[1024];
-    ERROR("swap plugin: swapctl failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("swap plugin: swapctl failed: %s", STRERRNO);
     return -1;
   }
 
@@ -513,9 +485,7 @@ static int swap_read(void) /* {{{ */
 
   status = swapctl(SC_LIST, s);
   if (status < 0) {
-    char errbuf[1024];
-    ERROR("swap plugin: swapctl (SC_LIST) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("swap plugin: swapctl (SC_LIST) failed: %s", STRERRNO);
     sfree(s_paths);
     sfree(s);
     return -1;
@@ -565,7 +535,7 @@ static int swap_read(void) /* {{{ */
     return -1;
   }
 
-  /* If the "separate" option was specified (report_by_device == 1), all
+  /* If the "separate" option was specified (report_by_device == true) all
    * values have already been dispatched from within the loop. */
   if (!report_by_device)
     swap_submit_usage(NULL, total - avail, avail, NULL, NAN);
@@ -719,9 +689,7 @@ static int swap_read(void) /* {{{ */
   status =
       perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1);
   if (status < 0) {
-    char errbuf[1024];
-    WARNING("swap plugin: perfstat_memory_total failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    WARNING("swap plugin: perfstat_memory_total failed: %s", STRERRNO);
     return -1;
   }
 
index 6c6b997..d51077f 100644 (file)
@@ -21,8 +21,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if !KERNEL_LINUX
 #error "No applicable input method."
index 90a97fb..a600f30 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_SYSLOG_H
 #include <syslog.h>
@@ -38,7 +38,7 @@ static int log_level = LOG_DEBUG;
 #else
 static int log_level = LOG_INFO;
 #endif /* COLLECT_DEBUG */
-static int notif_severity = 0;
+static int notif_severity;
 
 static const char *config_keys[] = {
     "LogLevel", "NotifyLevel",
index d798820..f181de9 100644 (file)
@@ -30,7 +30,7 @@
 
 #include "collectd.h"
 
-#include "common.h"
+#include "utils/common/common.h"
 
 #include "plugin.h"
 
@@ -132,29 +132,14 @@ static size_t tables_num;
 /*
  * configuration handling
  */
-
-static int tbl_config_set_s(char *name, char **var, oconfig_item_t *ci) {
-  if ((1 != ci->values_num) || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
-    log_err("\"%s\" expects a single string argument.", name);
-    return 1;
-  }
-
-  sfree(*var);
-  *var = sstrdup(ci->values[0].value.string);
-  return 0;
-} /* tbl_config_set_separator */
-
 static int tbl_config_append_array_i(char *name, size_t **var, size_t *len,
                                      oconfig_item_t *ci) {
-  size_t *tmp;
-  size_t num;
-
-  if (1 > ci->values_num) {
+  if (ci->values_num < 1) {
     log_err("\"%s\" expects at least one argument.", name);
     return 1;
   }
 
-  num = (size_t)ci->values_num;
+  size_t num = ci->values_num;
   for (size_t i = 0; i < num; ++i) {
     if (OCONFIG_TYPE_NUMBER != ci->values[i].type) {
       log_err("\"%s\" expects numerical arguments only.", name);
@@ -162,10 +147,9 @@ static int tbl_config_append_array_i(char *name, size_t **var, size_t *len,
     }
   }
 
-  tmp = realloc(*var, ((*len) + num) * sizeof(**var));
-  if (NULL == tmp) {
-    char errbuf[1024];
-    log_err("realloc failed: %s.", sstrerror(errno, errbuf, sizeof(errbuf)));
+  size_t *tmp = realloc(*var, ((*len) + num) * sizeof(**var));
+  if (tmp == NULL) {
+    log_err("realloc failed: %s.", STRERRNO);
     return -1;
   }
   *var = tmp;
@@ -179,7 +163,7 @@ static int tbl_config_append_array_i(char *name, size_t **var, size_t *len,
 } /* tbl_config_append_array_s */
 
 static int tbl_config_result(tbl_t *tbl, oconfig_item_t *ci) {
-  if (0 != ci->values_num) {
+  if (ci->values_num != 0) {
     log_err("<Result> does not expect any arguments.");
     return 1;
   }
@@ -187,8 +171,7 @@ static int tbl_config_result(tbl_t *tbl, oconfig_item_t *ci) {
   tbl_result_t *res =
       realloc(tbl->results, (tbl->results_num + 1) * sizeof(*tbl->results));
   if (res == NULL) {
-    char errbuf[1024];
-    log_err("realloc failed: %s.", sstrerror(errno, errbuf, sizeof(errbuf)));
+    log_err("realloc failed: %s.", STRERRNO);
     return -1;
   }
 
@@ -200,14 +183,14 @@ static int tbl_config_result(tbl_t *tbl, oconfig_item_t *ci) {
   for (int i = 0; i < ci->children_num; ++i) {
     oconfig_item_t *c = ci->children + i;
 
-    if (0 == strcasecmp(c->key, "Type"))
-      tbl_config_set_s(c->key, &res->type, c);
-    else if (0 == strcasecmp(c->key, "InstancePrefix"))
-      tbl_config_set_s(c->key, &res->instance_prefix, c);
-    else if (0 == strcasecmp(c->key, "InstancesFrom"))
+    if (strcasecmp(c->key, "Type") == 0)
+      cf_util_get_string(c, &res->type);
+    else if (strcasecmp(c->key, "InstancePrefix") == 0)
+      cf_util_get_string(c, &res->instance_prefix);
+    else if (strcasecmp(c->key, "InstancesFrom") == 0)
       tbl_config_append_array_i(c->key, &res->instances, &res->instances_num,
                                 c);
-    else if (0 == strcasecmp(c->key, "ValuesFrom"))
+    else if (strcasecmp(c->key, "ValuesFrom") == 0)
       tbl_config_append_array_i(c->key, &res->values, &res->values_num, c);
     else
       log_warn("Ignoring unknown config key \"%s\" "
@@ -216,19 +199,19 @@ static int tbl_config_result(tbl_t *tbl, oconfig_item_t *ci) {
   }
 
   int status = 0;
-  if (NULL == res->type) {
+  if (res->type == NULL) {
     log_err("No \"Type\" option specified for <Result> in table \"%s\".",
             tbl->file);
     status = 1;
   }
 
-  if (NULL == res->values) {
+  if (res->values == NULL) {
     log_err("No \"ValuesFrom\" option specified for <Result> in table \"%s\".",
             tbl->file);
     status = 1;
   }
 
-  if (0 != status) {
+  if (status != 0) {
     tbl_result_clear(res);
     return status;
   }
@@ -238,15 +221,14 @@ static int tbl_config_result(tbl_t *tbl, oconfig_item_t *ci) {
 } /* tbl_config_result */
 
 static int tbl_config_table(oconfig_item_t *ci) {
-  if ((1 != ci->values_num) || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+  if (ci->values_num != 1 || ci->values[0].type != OCONFIG_TYPE_STRING) {
     log_err("<Table> expects a single string argument.");
     return 1;
   }
 
   tbl_t *tbl = realloc(tables, (tables_num + 1) * sizeof(*tables));
-  if (NULL == tbl) {
-    char errbuf[1024];
-    log_err("realloc failed: %s.", sstrerror(errno, errbuf, sizeof(errbuf)));
+  if (tbl == NULL) {
+    log_err("realloc failed: %s.", STRERRNO);
     return -1;
   }
 
@@ -255,16 +237,16 @@ static int tbl_config_table(oconfig_item_t *ci) {
   tbl = tables + tables_num;
   tbl_setup(tbl, ci->values[0].value.string);
 
-  for (size_t i = 0; i < ((size_t)ci->children_num); ++i) {
+  for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *c = ci->children + i;
 
-    if (0 == strcasecmp(c->key, "Separator"))
-      tbl_config_set_s(c->key, &tbl->sep, c);
-    else if (0 == strcasecmp(c->key, "Plugin"))
-      tbl_config_set_s(c->key, &tbl->plugin_name, c);
-    else if (0 == strcasecmp(c->key, "Instance"))
-      tbl_config_set_s(c->key, &tbl->instance, c);
-    else if (0 == strcasecmp(c->key, "Result"))
+    if (strcasecmp(c->key, "Separator") == 0)
+      cf_util_get_string(c, &tbl->sep);
+    else if (strcasecmp(c->key, "Plugin") == 0)
+      cf_util_get_string(c, &tbl->plugin_name);
+    else if (strcasecmp(c->key, "Instance") == 0)
+      cf_util_get_string(c, &tbl->instance);
+    else if (strcasecmp(c->key, "Result") == 0)
       tbl_config_result(tbl, c);
     else
       log_warn("Ignoring unknown config key \"%s\" "
@@ -273,25 +255,25 @@ static int tbl_config_table(oconfig_item_t *ci) {
   }
 
   int status = 0;
-  if (NULL == tbl->sep) {
+  if (tbl->sep == NULL) {
     log_err("Table \"%s\" does not specify any separator.", tbl->file);
     status = 1;
   } else {
     strunescape(tbl->sep, strlen(tbl->sep) + 1);
   }
 
-  if (NULL == tbl->instance) {
+  if (tbl->instance == NULL) {
     tbl->instance = sstrdup(tbl->file);
     replace_special(tbl->instance, strlen(tbl->instance));
   }
 
-  if (NULL == tbl->results) {
+  if (tbl->results == NULL) {
     assert(tbl->results_num == 0);
     log_err("Table \"%s\" does not specify any (valid) results.", tbl->file);
     status = 1;
   }
 
-  if (0 != status) {
+  if (status != 0) {
     tbl_clear(tbl);
     return status;
   }
@@ -316,7 +298,7 @@ static int tbl_config(oconfig_item_t *ci) {
   for (int i = 0; i < ci->children_num; ++i) {
     oconfig_item_t *c = ci->children + i;
 
-    if (0 == strcasecmp(c->key, "Table"))
+    if (strcasecmp(c->key, "Table") == 0)
       tbl_config_table(c);
     else
       log_warn("Ignoring unknown config key \"%s\".", c->key);
@@ -333,14 +315,14 @@ static int tbl_prepare(tbl_t *tbl) {
     tbl_result_t *res = tbl->results + i;
 
     res->ds = plugin_get_ds(res->type);
-    if (NULL == res->ds) {
+    if (res->ds == NULL) {
       log_err("Unknown type \"%s\". See types.db(5) for details.", res->type);
       return -1;
     }
 
     if (res->values_num != res->ds->ds_num) {
-      log_err("Invalid type \"%s\". Expected %zu data source%s, "
-              "got %zu.",
+      log_err("Invalid type \"%s\". Expected %" PRIsz " data source%s, "
+              "got %" PRIsz ".",
               res->type, res->values_num, (1 == res->values_num) ? "" : "s",
               res->ds->ds_num);
       return -1;
@@ -360,16 +342,13 @@ static int tbl_result_dispatch(tbl_t *tbl, tbl_result_t *res, char **fields,
   value_list_t vl = VALUE_LIST_INIT;
   value_t values[res->values_num];
 
-  assert(NULL != res->ds);
+  assert(res->ds);
   assert(res->values_num == res->ds->ds_num);
 
   for (size_t i = 0; i < res->values_num; ++i) {
-    char *value;
-
     assert(res->values[i] < fields_num);
-    value = fields[res->values[i]];
-
-    if (0 != parse_value(value, &values[i], res->ds->ds[i].type))
+    char *value = fields[res->values[i]];
+    if (parse_value(value, &values[i], res->ds->ds[i].type) != 0)
       return -1;
   }
 
@@ -381,8 +360,8 @@ static int tbl_result_dispatch(tbl_t *tbl, tbl_result_t *res, char **fields,
   sstrncpy(vl.plugin_instance, tbl->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, res->type, sizeof(vl.type));
 
-  if (0 == res->instances_num) {
-    if (NULL != res->instance_prefix)
+  if (res->instances_num == 0) {
+    if (res->instance_prefix)
       sstrncpy(vl.type_instance, res->instance_prefix,
                sizeof(vl.type_instance));
   } else {
@@ -398,17 +377,15 @@ static int tbl_result_dispatch(tbl_t *tbl, tbl_result_t *res, char **fields,
             STATIC_ARRAY_SIZE(instances), "-");
     instances_str[sizeof(instances_str) - 1] = '\0';
 
-    vl.type_instance[sizeof(vl.type_instance) - 1] = '\0';
-    if (NULL == res->instance_prefix)
-      strncpy(vl.type_instance, instances_str, sizeof(vl.type_instance));
+    int r;
+    if (res->instance_prefix == NULL)
+      r = snprintf(vl.type_instance, sizeof(vl.type_instance), "%s",
+                   instances_str);
     else
-      snprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s",
-               res->instance_prefix, instances_str);
-
-    if ('\0' != vl.type_instance[sizeof(vl.type_instance) - 1]) {
-      vl.type_instance[sizeof(vl.type_instance) - 1] = '\0';
+      r = snprintf(vl.type_instance, sizeof(vl.type_instance), "%s-%s",
+                   res->instance_prefix, instances_str);
+    if ((size_t)r >= sizeof(vl.type_instance))
       log_warn("Truncated type instance: %s.", vl.type_instance);
-    }
   }
 
   plugin_dispatch_values(&vl);
@@ -417,15 +394,13 @@ static int tbl_result_dispatch(tbl_t *tbl, tbl_result_t *res, char **fields,
 
 static int tbl_parse_line(tbl_t *tbl, char *line, size_t len) {
   char *fields[tbl->max_colnum + 1];
-  char *ptr, *saveptr;
-
   size_t i = 0;
 
-  ptr = line;
-  saveptr = NULL;
-  while (NULL != (fields[i] = strtok_r(ptr, tbl->sep, &saveptr))) {
+  char *ptr = line;
+  char *saveptr = NULL;
+  while ((fields[i] = strtok_r(ptr, tbl->sep, &saveptr)) != NULL) {
     ptr = NULL;
-    ++i;
+    i++;
 
     if (i > tbl->max_colnum)
       break;
@@ -433,14 +408,14 @@ static int tbl_parse_line(tbl_t *tbl, char *line, size_t len) {
 
   if (i <= tbl->max_colnum) {
     log_warn("Not enough columns in line "
-             "(expected at least %zu, got %zu).",
+             "(expected at least %" PRIsz ", got %" PRIsz ").",
              tbl->max_colnum + 1, i);
     return -1;
   }
 
   for (i = 0; i < tbl->results_num; ++i)
-    if (0 != tbl_result_dispatch(tbl, tbl->results + i, fields,
-                                 STATIC_ARRAY_SIZE(fields))) {
+    if (tbl_result_dispatch(tbl, tbl->results + i, fields,
+                            STATIC_ARRAY_SIZE(fields)) != 0) {
       log_err("Failed to dispatch result.");
       continue;
     }
@@ -448,34 +423,29 @@ static int tbl_parse_line(tbl_t *tbl, char *line, size_t len) {
 } /* tbl_parse_line */
 
 static int tbl_read_table(tbl_t *tbl) {
-  FILE *fh;
   char buf[4096];
 
-  fh = fopen(tbl->file, "r");
-  if (NULL == fh) {
-    char errbuf[1024];
-    log_err("Failed to open file \"%s\": %s.", tbl->file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+  FILE *fh = fopen(tbl->file, "r");
+  if (fh == NULL) {
+    log_err("Failed to open file \"%s\": %s.", tbl->file, STRERRNO);
     return -1;
   }
 
   buf[sizeof(buf) - 1] = '\0';
-  while (NULL != fgets(buf, sizeof(buf), fh)) {
-    if ('\0' != buf[sizeof(buf) - 1]) {
+  while (fgets(buf, sizeof(buf), fh) != NULL) {
+    if (buf[sizeof(buf) - 1] != '\0') {
       buf[sizeof(buf) - 1] = '\0';
       log_warn("Table %s: Truncated line: %s", tbl->file, buf);
     }
 
-    if (0 != tbl_parse_line(tbl, buf, sizeof(buf))) {
+    if (tbl_parse_line(tbl, buf, sizeof(buf)) != 0) {
       log_warn("Table %s: Failed to parse line: %s", tbl->file, buf);
       continue;
     }
   }
 
-  if (0 != ferror(fh)) {
-    char errbuf[1024];
-    log_err("Failed to read from file \"%s\": %s.", tbl->file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+  if (ferror(fh) != 0) {
+    log_err("Failed to read from file \"%s\": %s.", tbl->file, STRERRNO);
     fclose(fh);
     return -1;
   }
@@ -491,18 +461,18 @@ static int tbl_read_table(tbl_t *tbl) {
 static int tbl_read(void) {
   int status = -1;
 
-  if (0 == tables_num)
+  if (tables_num == 0)
     return 0;
 
   for (size_t i = 0; i < tables_num; ++i) {
     tbl_t *tbl = tables + i;
 
-    if (0 != tbl_prepare(tbl)) {
+    if (tbl_prepare(tbl) != 0) {
       log_err("Failed to prepare and parse table \"%s\".", tbl->file);
       continue;
     }
 
-    if (0 == tbl_read_table(tbl))
+    if (tbl_read_table(tbl) == 0)
       status = 0;
 
     tbl_finish(tbl);
@@ -518,7 +488,7 @@ static int tbl_shutdown(void) {
 } /* tbl_shutdown */
 
 static int tbl_init(void) {
-  if (0 == tables_num)
+  if (tables_num == 0)
     return 0;
 
   plugin_register_read("table", tbl_read);
index fbba478..8c9dfbe 100644 (file)
@@ -26,9 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_latency_config.h"
+#include "utils/common/common.h"
+#include "utils/latency/latency_config.h"
 #include "utils_tail_match.h"
 
 /*
@@ -54,14 +54,17 @@ struct ctail_config_match_s {
   int flags;
   char *type;
   char *type_instance;
-  cdtime_t interval;
   latency_config_t latency;
 };
 typedef struct ctail_config_match_s ctail_config_match_t;
 
-static cu_tail_match_t **tail_match_list = NULL;
-static size_t tail_match_list_num = 0;
-static cdtime_t tail_match_list_intervals[255];
+static size_t tail_file_num;
+
+static int ctail_read(user_data_t *ud);
+
+static void ctail_match_free(void *arg) {
+  tail_match_destroy((cu_tail_match_t *)arg);
+} /* void ctail_match_free */
 
 static int ctail_config_add_match_dstype(ctail_config_match_t *cm,
                                          oconfig_item_t *ci) {
@@ -92,7 +95,7 @@ static int ctail_config_add_match_dstype(ctail_config_match_t *cm,
   } else if (strcasecmp("Distribution", ds_type) == 0) {
     cm->flags = UTILS_MATCH_DS_TYPE_GAUGE | UTILS_MATCH_CF_GAUGE_DIST;
 
-    int status = latency_config(&cm->latency, ci, "tail");
+    int status = latency_config(&cm->latency, ci);
     if (status != 0)
       return status;
   } else if (strncasecmp("Counter", ds_type, strlen("Counter")) == 0) {
@@ -134,10 +137,9 @@ static int ctail_config_add_match_dstype(ctail_config_match_t *cm,
   return 0;
 } /* int ctail_config_add_match_dstype */
 
-static int ctail_config_add_match(cu_tail_match_t *tm,
-                                  const char *plugin_name,
+static int ctail_config_add_match(cu_tail_match_t *tm, const char *plugin_name,
                                   const char *plugin_instance,
-                                  oconfig_item_t *ci, cdtime_t interval) {
+                                  oconfig_item_t *ci) {
   ctail_config_match_t cm = {0};
   int status;
 
@@ -194,8 +196,8 @@ static int ctail_config_add_match(cu_tail_match_t *tm,
     // TODO(octo): there's nothing "simple" about the latency stuff …
     status = tail_match_add_match_simple(
         tm, cm.regex, cm.excluderegex, cm.flags,
-        (plugin_name != NULL) ? plugin_name : "tail", plugin_instance,
-        cm.type, cm.type_instance, cm.latency, interval);
+        (plugin_name != NULL) ? plugin_name : "tail", plugin_instance, cm.type,
+        cm.type_instance, cm.latency);
 
     if (status != 0)
       ERROR("tail plugin: tail_match_add_match_simple failed.");
@@ -234,14 +236,13 @@ static int ctail_config_add_file(oconfig_item_t *ci) {
     int status = 0;
 
     if (strcasecmp("Plugin", option->key) == 0)
-      status = cf_util_get_string (option, &plugin_name);
+      status = cf_util_get_string(option, &plugin_name);
     else if (strcasecmp("Instance", option->key) == 0)
       status = cf_util_get_string(option, &plugin_instance);
     else if (strcasecmp("Interval", option->key) == 0)
       cf_util_get_cdtime(option, &interval);
     else if (strcasecmp("Match", option->key) == 0) {
-      status = ctail_config_add_match(tm, plugin_name, plugin_instance, option,
-                                      interval);
+      status = ctail_config_add_match(tm, plugin_name, plugin_instance, option);
       if (status == 0)
         num_matches++;
       /* Be mild with failed matches.. */
@@ -262,23 +263,15 @@ static int ctail_config_add_file(oconfig_item_t *ci) {
           ci->values[0].value.string);
     tail_match_destroy(tm);
     return -1;
-  } else {
-    cu_tail_match_t **temp;
-
-    temp = realloc(tail_match_list,
-                   sizeof(cu_tail_match_t *) * (tail_match_list_num + 1));
-    if (temp == NULL) {
-      ERROR("tail plugin: realloc failed.");
-      tail_match_destroy(tm);
-      return -1;
-    }
-
-    tail_match_list = temp;
-    tail_match_list[tail_match_list_num] = tm;
-    tail_match_list_intervals[tail_match_list_num] = interval;
-    tail_match_list_num++;
   }
 
+  char str[255];
+  snprintf(str, sizeof(str), "tail-%zu", tail_file_num++);
+
+  plugin_register_complex_read(
+      NULL, str, ctail_read, interval,
+      &(user_data_t){.data = tm, .free_func = ctail_match_free});
+
   return 0;
 } /* int ctail_config_add_file */
 
@@ -308,40 +301,6 @@ static int ctail_read(user_data_t *ud) {
   return 0;
 } /* int ctail_read */
 
-static int ctail_init(void) {
-  char str[255];
-
-  if (tail_match_list_num == 0) {
-    WARNING("tail plugin: File list is empty. Returning an error.");
-    return -1;
-  }
-
-  for (size_t i = 0; i < tail_match_list_num; i++) {
-    snprintf(str, sizeof(str), "tail-%zu", i);
-
-    plugin_register_complex_read(NULL, str, ctail_read,
-                                 tail_match_list_intervals[i],
-                                 &(user_data_t){
-                                     .data = tail_match_list[i],
-                                 });
-  }
-
-  return 0;
-} /* int ctail_init */
-
-static int ctail_shutdown(void) {
-  for (size_t i = 0; i < tail_match_list_num; i++) {
-    tail_match_destroy(tail_match_list[i]);
-    tail_match_list[i] = NULL;
-  }
-  sfree(tail_match_list);
-  tail_match_list_num = 0;
-
-  return 0;
-} /* int ctail_shutdown */
-
 void module_register(void) {
   plugin_register_complex_config("tail", ctail_config);
-  plugin_register_init("tail", ctail_init);
-  plugin_register_shutdown("tail", ctail_shutdown);
 } /* void module_register */
index 2e3ac5f..e1d473e 100644 (file)
@@ -23,9 +23,9 @@
 
 #include "collectd.h"
 
-#include "common.h" /* auxiliary functions */
-#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
-#include "utils_tail.h"
+#include "plugin.h"              /* plugin_register_*, plugin_dispatch_values */
+#include "utils/common/common.h" /* auxiliary functions */
+#include "utils/tail/tail.h"
 
 #include <fcntl.h>
 #include <stdlib.h>
@@ -50,14 +50,13 @@ struct instance_definition_s {
   cu_tail_t *tail;
   metric_definition_t **metric_list;
   size_t metric_list_len;
-  cdtime_t interval;
   ssize_t time_from;
   struct instance_definition_s *next;
 };
 typedef struct instance_definition_s instance_definition_t;
 
 /* Private */
-static metric_definition_t *metric_head = NULL;
+static metric_definition_t *metric_head;
 
 static int tcsv_submit(instance_definition_t *id, metric_definition_t *md,
                        value_t v, cdtime_t t) {
@@ -77,7 +76,6 @@ static int tcsv_submit(instance_definition_t *id, metric_definition_t *md,
     sstrncpy(vl.type_instance, md->instance, sizeof(vl.type_instance));
 
   vl.time = t;
-  vl.interval = id->interval;
 
   return plugin_dispatch_values(&vl);
 }
@@ -120,17 +118,17 @@ static int tcsv_read_metric(instance_definition_t *id, metric_definition_t *md,
   return tcsv_submit(id, md, v, t);
 }
 
-static _Bool tcsv_check_index(ssize_t index, size_t fields_num,
-                              char const *name) {
+static bool tcsv_check_index(ssize_t index, size_t fields_num,
+                             char const *name) {
   if (index < 0)
-    return 1;
+    return true;
   else if (((size_t)index) < fields_num)
-    return 1;
+    return true;
 
   ERROR("tail_csv plugin: Metric \"%s\": Request for index %zd when "
-        "only %zu fields are available.",
+        "only %" PRIsz " fields are available.",
         name, index, fields_num);
-  return 0;
+  return false;
 }
 
 static int tcsv_read_buffer(instance_definition_t *id, char *buffer,
@@ -145,7 +143,7 @@ static int tcsv_read_buffer(instance_definition_t *id, char *buffer,
   while (buffer_size > 0) {
     if ((buffer[buffer_size - 1] == '\n') ||
         (buffer[buffer_size - 1] == '\r')) {
-      buffer[buffer_size - 1] = 0;
+      buffer[buffer_size - 1] = '\0';
       buffer_size--;
     } else {
       break;
@@ -418,6 +416,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) {
   int status = 0;
 
   /* Registration variables */
+  cdtime_t interval = 0;
   char cb_name[DATA_MAX_NAME_LEN];
 
   id = calloc(1, sizeof(*id));
@@ -436,9 +435,6 @@ static int tcsv_config_add_file(oconfig_item_t *ci) {
     return status;
   }
 
-  /* Use default interval. */
-  id->interval = plugin_get_interval();
-
   for (int i = 0; i < ci->children_num; ++i) {
     oconfig_item_t *option = ci->children + i;
     status = 0;
@@ -448,7 +444,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) {
     else if (strcasecmp("Collect", option->key) == 0)
       status = tcsv_config_add_instance_collect(id, option);
     else if (strcasecmp("Interval", option->key) == 0)
-      cf_util_get_cdtime(option, &id->interval);
+      cf_util_get_cdtime(option, &interval);
     else if (strcasecmp("TimeFrom", option->key) == 0)
       status = tcsv_config_get_index(option, &id->time_from);
     else if (strcasecmp("Plugin", option->key) == 0)
@@ -484,7 +480,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) {
   snprintf(cb_name, sizeof(cb_name), "tail_csv/%s", id->path);
 
   status = plugin_register_complex_read(
-      NULL, cb_name, tcsv_read, id->interval,
+      NULL, cb_name, tcsv_read, interval,
       &(user_data_t){
           .data = id, .free_func = tcsv_instance_definition_destroy,
       });
@@ -513,7 +509,7 @@ static int tcsv_config(oconfig_item_t *ci) {
 } /* int tcsv_config */
 
 static int tcsv_init(void) { /* {{{ */
-  static _Bool have_init = 0;
+  static bool have_init;
   metric_definition_t *md;
 
   if (have_init)
@@ -532,7 +528,7 @@ static int tcsv_init(void) { /* {{{ */
             md->type, md->name);
       continue;
     } else if (ds->ds_num != 1) {
-      ERROR("tail_csv plugin: The type \"%s\" has %zu data sources. "
+      ERROR("tail_csv plugin: The type \"%s\" has %" PRIsz " data sources. "
             "Only types with a single data source are supported.",
             ds->type, ds->ds_num);
       continue;
index f59b7ea..ccf8825 100644 (file)
@@ -22,8 +22,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if !HAVE_LIBKSTAT
 #error "No applicable input method."
@@ -36,7 +36,7 @@
 #define MAX_NUMTAPE 256
 extern kstat_ctl_t *kc;
 static kstat_t *ksp[MAX_NUMTAPE];
-static int numtape = 0;
+static int numtape;
 
 static int tape_init(void) {
   kstat_t *ksp_chain;
index f83a904..6d8059e 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 #include "utils_subst.h"
 
index 66fc98d..62928c0 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
+#include "utils/common/common.h"
 #include "utils_subst.h"
 
 #include <regex.h>
@@ -37,7 +37,7 @@ typedef struct tr_action_s tr_action_t;
 struct tr_action_s {
   regex_t re;
   char *replacement;
-  _Bool may_be_empty;
+  bool may_be_empty;
 
   tr_action_t *next;
 };
@@ -110,7 +110,7 @@ static void tr_meta_data_action_destroy(tr_meta_data_action_t *act) /* {{{ */
 } /* }}} void tr_meta_data_action_destroy */
 
 static int tr_config_add_action(tr_action_t **dest, /* {{{ */
-                                const oconfig_item_t *ci, _Bool may_be_empty) {
+                                const oconfig_item_t *ci, bool may_be_empty) {
   tr_action_t *act;
   int status;
 
@@ -172,7 +172,7 @@ static int tr_config_add_action(tr_action_t **dest, /* {{{ */
 
 static int tr_config_add_meta_action(tr_meta_data_action_t **dest, /* {{{ */
                                      const oconfig_item_t *ci,
-                                     _Bool should_delete) {
+                                     bool should_delete) {
   tr_meta_data_action_t *act;
   int status;
 
@@ -262,7 +262,7 @@ static int tr_config_add_meta_action(tr_meta_data_action_t **dest, /* {{{ */
 
 static int tr_action_invoke(tr_action_t *act_head, /* {{{ */
                             char *buffer_in, size_t buffer_in_size,
-                            _Bool may_be_empty) {
+                            bool may_be_empty) {
   int status;
   char buffer[DATA_MAX_NAME_LEN];
   regmatch_t matches[8] = {[0] = {0}};
@@ -294,7 +294,8 @@ static int tr_action_invoke(tr_action_t *act_head, /* {{{ */
     subst_status = subst(temp, sizeof(temp), buffer, (size_t)matches[0].rm_so,
                          (size_t)matches[0].rm_eo, act->replacement);
     if (subst_status == NULL) {
-      ERROR("Target `replace': subst (buffer = %s, start = %zu, end = %zu, "
+      ERROR("Target `replace': subst (buffer = %s, start = %" PRIsz
+            ", end = %" PRIsz ", "
             "replacement = %s) failed.",
             buffer, (size_t)matches[0].rm_so, (size_t)matches[0].rm_eo,
             act->replacement);
@@ -305,7 +306,7 @@ static int tr_action_invoke(tr_action_t *act_head, /* {{{ */
     DEBUG("target_replace plugin: tr_action_invoke: -- buffer = %s;", buffer);
   } /* for (act = act_head; act != NULL; act = act->next) */
 
-  if ((may_be_empty == 0) && (buffer[0] == 0)) {
+  if ((may_be_empty == false) && (buffer[0] == 0)) {
     WARNING("Target `replace': Replacement resulted in an empty string, "
             "which is not allowed for this buffer (`host' or `plugin').");
     return 0;
@@ -386,7 +387,8 @@ static int tr_meta_data_action_invoke(/* {{{ */
     subst_status = subst(temp, sizeof(temp), value, (size_t)matches[0].rm_so,
                          (size_t)matches[0].rm_eo, act->replacement);
     if (subst_status == NULL) {
-      ERROR("Target `replace': subst (value = %s, start = %zu, end = %zu, "
+      ERROR("Target `replace': subst (value = %s, start = %" PRIsz
+            ", end = %" PRIsz ", "
             "replacement = %s) failed.",
             value, (size_t)matches[0].rm_so, (size_t)matches[0].rm_eo,
             act->replacement);
@@ -468,13 +470,13 @@ static int tr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
     if ((strcasecmp("Host", child->key) == 0) ||
         (strcasecmp("Hostname", child->key) == 0))
       status = tr_config_add_action(&data->host, child,
-                                    /* may be empty = */ 0);
+                                    /* may be empty = */ false);
     else if (strcasecmp("Plugin", child->key) == 0)
       status = tr_config_add_action(&data->plugin, child,
-                                    /* may be empty = */ 0);
+                                    /* may be empty = */ false);
     else if (strcasecmp("PluginInstance", child->key) == 0)
       status = tr_config_add_action(&data->plugin_instance, child,
-                                    /* may be empty = */ 1);
+                                    /* may be empty = */ true);
 #if 0
     else if (strcasecmp ("Type", child->key) == 0)
       status = tr_config_add_action (&data->type, child,
@@ -482,13 +484,13 @@ static int tr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
 #endif
     else if (strcasecmp("TypeInstance", child->key) == 0)
       status = tr_config_add_action(&data->type_instance, child,
-                                    /* may be empty = */ 1);
+                                    /* may be empty = */ true);
     else if (strcasecmp("MetaData", child->key) == 0)
       status = tr_config_add_meta_action(&data->meta, child,
-                                         /* should delete = */ 0);
+                                         /* should delete = */ false);
     else if (strcasecmp("DeleteMetaData", child->key) == 0)
       status = tr_config_add_meta_action(&data->meta, child,
-                                         /* should delete = */ 1);
+                                         /* should delete = */ true);
     else {
       ERROR("Target `replace': The `%s' configuration option is not understood "
             "and will be ignored.",
@@ -544,11 +546,11 @@ static int tr_invoke(const data_set_t *ds, value_list_t *vl, /* {{{ */
 #define HANDLE_FIELD(f, e)                                                     \
   if (data->f != NULL)                                                         \
   tr_action_invoke(data->f, vl->f, sizeof(vl->f), e)
-  HANDLE_FIELD(host, 0);
-  HANDLE_FIELD(plugin, 0);
-  HANDLE_FIELD(plugin_instance, 1);
-  /* HANDLE_FIELD (type, 0); */
-  HANDLE_FIELD(type_instance, 1);
+  HANDLE_FIELD(host, false);
+  HANDLE_FIELD(plugin, false);
+  HANDLE_FIELD(plugin_instance, true);
+  /* HANDLE_FIELD (type, false); */
+  HANDLE_FIELD(type_instance, true);
 
   return FC_TARGET_CONTINUE;
 } /* }}} int tr_invoke */
index 1cc5d79..4a9e9df 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
+#include "utils/common/common.h"
 
 #include "utils_cache.h"
 
index 9629e0e..4bbd2c0 100644 (file)
@@ -26,9 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
-#include "meta_data.h"
+#include "utils/common/common.h"
+#include "utils/metadata/meta_data.h"
 #include "utils_subst.h"
 
 struct ts_key_list_s {
index 49f09f0..c2fe192 100644 (file)
@@ -26,9 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "filter_chain.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 static void v5_swap_instances(value_list_t *vl) /* {{{ */
 {
@@ -213,15 +213,15 @@ static int v5_mysql_threads(const data_set_t *ds, value_list_t *vl) /* {{{ */
 static int v5_zfs_arc_counts(const data_set_t *ds, value_list_t *vl) /* {{{ */
 {
   value_list_t new_vl;
-  _Bool is_hits;
+  bool is_hits;
 
   if (vl->values_len != 4)
     return FC_TARGET_STOP;
 
   if (strcmp("hits", vl->type_instance) == 0)
-    is_hits = 1;
+    is_hits = true;
   else if (strcmp("misses", vl->type_instance) == 0)
-    is_hits = 0;
+    is_hits = false;
   else
     return FC_TARGET_STOP;
 
index 9fdd16e..d01bcd2 100644 (file)
@@ -59,8 +59,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if defined(__OpenBSD__)
 #define HAVE_KVM_GETFILES 1
@@ -76,7 +76,6 @@
 #endif
 
 #if KERNEL_LINUX
-#include <asm/types.h>
 #include <linux/netlink.h>
 #if HAVE_LINUX_INET_DIAG_H
 #include <linux/inet_diag.h>
@@ -206,7 +205,7 @@ static const char *tcp_state[] = {"CLOSED",    "LISTEN",      "SYN_SENT",
                                   "FIN_WAIT2", "TIME_WAIT"};
 
 static kvm_t *kvmd;
-static u_long inpcbtable_off = 0;
+static u_long inpcbtable_off;
 struct inpcbtable *inpcbtable_ptr = NULL;
 
 #define TCP_STATE_LISTEN 1
@@ -262,9 +261,9 @@ static const char *config_keys[] = {"ListeningPorts", "LocalPort", "RemotePort",
                                     "AllPortsSummary"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static int port_collect_listening = 0;
-static int port_collect_total = 0;
-static port_entry_t *port_list_head = NULL;
+static int port_collect_listening;
+static int port_collect_total;
+static port_entry_t *port_list_head;
 static uint32_t count_total[TCP_STATE_MAX + 1];
 
 #if KERNEL_LINUX
@@ -272,7 +271,7 @@ static uint32_t count_total[TCP_STATE_MAX + 1];
 /* This depends on linux inet_diag_req because if this structure is missing,
  * sequence_number is useless and we get a compilation warning.
  */
-static uint32_t sequence_number = 0;
+static uint32_t sequence_number;
 #endif
 
 static enum { SRC_DUNNO, SRC_NETLINK, SRC_PROC } linux_source = SRC_DUNNO;
@@ -459,7 +458,7 @@ static int conn_read_netlink(void) {
   if (fd < 0) {
     ERROR("tcpconns plugin: conn_read_netlink: socket(AF_NETLINK, SOCK_RAW, "
           "NETLINK_INET_DIAG) failed: %s",
-          sstrerror(errno, buf, sizeof(buf)));
+          STRERRNO);
     return -1;
   }
 
@@ -490,7 +489,7 @@ static int conn_read_netlink(void) {
 
   if (sendmsg(fd, &msg, 0) < 0) {
     ERROR("tcpconns plugin: conn_read_netlink: sendmsg(2) failed: %s",
-          sstrerror(errno, buf, sizeof(buf)));
+          STRERRNO);
     close(fd);
     return -1;
   }
@@ -499,7 +498,6 @@ static int conn_read_netlink(void) {
   iov.iov_len = sizeof(buf);
 
   while (1) {
-    int status;
     struct nlmsghdr *h;
 
     memset(&msg, 0, sizeof(msg));
@@ -508,13 +506,13 @@ static int conn_read_netlink(void) {
     msg.msg_iov = &iov;
     msg.msg_iovlen = 1;
 
-    status = recvmsg(fd, (void *)&msg, /* flags = */ 0);
+    ssize_t status = recvmsg(fd, (void *)&msg, /* flags = */ 0);
     if (status < 0) {
       if ((errno == EINTR) || (errno == EAGAIN))
         continue;
 
       ERROR("tcpconns plugin: conn_read_netlink: recvmsg(2) failed: %s",
-            sstrerror(errno, buf, sizeof(buf)));
+            STRERRNO);
       close(fd);
       return -1;
     } else if (status == 0) {
@@ -575,11 +573,11 @@ static int conn_handle_line(char *buffer) {
 
   uint8_t state;
 
-  int buffer_len = strlen(buffer);
+  size_t buffer_len = strlen(buffer);
 
   while ((buffer_len > 0) && (buffer[buffer_len - 1] < 32))
     buffer[--buffer_len] = '\0';
-  if (buffer_len <= 0)
+  if (buffer_len == 0)
     return -1;
 
   fields_len = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
index 4d68f61..1ab6e4b 100644 (file)
@@ -23,8 +23,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <arpa/inet.h>
 #include <netdb.h>
@@ -46,14 +46,14 @@ typedef struct vserver_list_s {
   int port;
   struct vserver_list_s *next;
 } vserver_list_t;
-static vserver_list_t *server_list = NULL;
+static vserver_list_t *server_list;
 
 /* Host data */
-static char *config_host = NULL;
-static char *config_port = NULL;
+static char *config_host;
+static char *config_port;
 
-static FILE *global_read_fh = NULL;
-static FILE *global_write_fh = NULL;
+static FILE *global_read_fh;
+static FILE *global_write_fh;
 
 /* Config data */
 static const char *config_keys[] = {"Host", "Port", "Server"};
@@ -204,18 +204,14 @@ static int tss2_get_socket(FILE **ret_read_fh, FILE **ret_write_fh) {
     /* Create socket */
     sd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (sd < 0) {
-      char errbuf[1024];
-      WARNING("teamspeak2 plugin: socket failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("teamspeak2 plugin: socket failed: %s", STRERRNO);
       continue;
     }
 
     /* Try to connect */
     status = connect(sd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
     if (status != 0) {
-      char errbuf[1024];
-      WARNING("teamspeak2 plugin: connect failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("teamspeak2 plugin: connect failed: %s", STRERRNO);
       close(sd);
       sd = -1;
       continue;
@@ -236,18 +232,14 @@ static int tss2_get_socket(FILE **ret_read_fh, FILE **ret_write_fh) {
   /* Create file objects from sockets */
   global_read_fh = fdopen(sd, "r");
   if (global_read_fh == NULL) {
-    char errbuf[1024];
-    ERROR("teamspeak2 plugin: fdopen failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("teamspeak2 plugin: fdopen failed: %s", STRERRNO);
     close(sd);
     return -1;
   }
 
   global_write_fh = fdopen(sd, "w");
   if (global_write_fh == NULL) {
-    char errbuf[1024];
-    ERROR("teamspeak2 plugin: fdopen failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("teamspeak2 plugin: fdopen failed: %s", STRERRNO);
     tss2_close_socket();
     return -1;
   }
@@ -263,7 +255,7 @@ static int tss2_get_socket(FILE **ret_read_fh, FILE **ret_write_fh) {
               config_host ? config_host : DEFAULT_HOST,
               config_port ? config_port : DEFAULT_PORT);
     }
-    buffer[sizeof(buffer) - 1] = 0;
+    buffer[sizeof(buffer) - 1] = '\0';
 
     if (memcmp("[TS]\r\n", buffer, 6) != 0) {
       ERROR("teamspeak2 plugin: Unexpected response when connecting "
@@ -312,14 +304,12 @@ static int tss2_receive_line(FILE *fh, char *buffer, int buffer_size) {
    */
   temp = fgets(buffer, buffer_size, fh);
   if (temp == NULL) {
-    char errbuf[1024];
-    ERROR("teamspeak2 plugin: fgets failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("teamspeak2 plugin: fgets failed: %s", STRERRNO);
     tss2_close_socket();
     return -1;
   }
 
-  buffer[buffer_size - 1] = 0;
+  buffer[buffer_size - 1] = '\0';
   return 0;
 } /* int tss2_receive_line */
 
@@ -347,7 +337,7 @@ static int tss2_select_vserver(FILE *read_fh, FILE *write_fh,
     ERROR("teamspeak2 plugin: tss2_receive_line failed.");
     return -1;
   }
-  response[sizeof(response) - 1] = 0;
+  response[sizeof(response) - 1] = '\0';
 
   /* Check answer */
   if ((strncasecmp("OK", response, 2) == 0) &&
@@ -389,7 +379,7 @@ static int tss2_vserver_gapl(FILE *read_fh, FILE *write_fh,
       ERROR("teamspeak2 plugin: tss2_receive_line failed.");
       return -1;
     }
-    buffer[sizeof(buffer) - 1] = 0;
+    buffer[sizeof(buffer) - 1] = '\0';
 
     if (strncmp("average_packet_loss=", buffer,
                 strlen("average_packet_loss=")) == 0) {
index 3b64b75..e1b48fd 100644 (file)
--- a/src/ted.c
+++ b/src/ted.c
@@ -36,8 +36,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
@@ -53,8 +53,8 @@
 
 #define DEFAULT_DEVICE "/dev/ttyUSB0"
 
-static char *conf_device = NULL;
-static int conf_retries = 0;
+static char *conf_device;
+static int conf_retries;
 
 static int fd = -1;
 
@@ -109,19 +109,15 @@ static int ted_read_value(double *ret_power, double *ret_voltage) {
       /* Some signal or something. Start over.. */
       continue;
     } else if (status < 0) {
-      char errbuf[1024];
-      ERROR("ted plugin: select failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("ted plugin: select failed: %s", STRERRNO);
       return -1;
     }
 
     receive_buffer_length = read(fd, receive_buffer, sizeof(receive_buffer));
     if (receive_buffer_length < 0) {
-      char errbuf[1024];
       if ((errno == EAGAIN) || (errno == EINTR))
         continue;
-      ERROR("ted plugin: read(2) failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("ted plugin: read(2) failed: %s", STRERRNO);
       return -1;
     } else if (receive_buffer_length == 0) {
       /* Should we close the FD in this case? */
index d3da9db..fd7e6c6 100644 (file)
@@ -29,8 +29,8 @@
 
 #include <inttypes.h>
 
-static int fail_count__ = 0;
-static int check_count__ = 0;
+static int fail_count__;
+static int check_count__;
 
 #ifndef DBL_PRECISION
 #define DBL_PRECISION 1e-12
@@ -56,7 +56,7 @@ static int check_count__ = 0;
 
 #define OK1(cond, text)                                                        \
   do {                                                                         \
-    _Bool result = (cond);                                                     \
+    bool result = (cond);                                                      \
     LOG(result, text);                                                         \
     if (!result) {                                                             \
       return -1;                                                               \
@@ -100,6 +100,18 @@ static int check_count__ = 0;
     printf("ok %i - %s = %" PRIu64 "\n", ++check_count__, #actual, got__);     \
   } while (0)
 
+#define EXPECT_EQ_PTR(expect, actual)                                          \
+  do {                                                                         \
+    void *want__ = expect;                                                     \
+    void *got__ = actual;                                                      \
+    if (got__ != want__) {                                                     \
+      printf("not ok %i - %s = %p, want %p\n", ++check_count__, #actual,       \
+             got__, want__);                                                   \
+      return -1;                                                               \
+    }                                                                          \
+    printf("ok %i - %s = %p\n", ++check_count__, #actual, got__);              \
+  } while (0)
+
 #define EXPECT_EQ_DOUBLE(expect, actual)                                       \
   do {                                                                         \
     double want__ = (double)expect;                                            \
index 9da8fa5..5f44836 100644 (file)
@@ -21,9 +21,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_ignorelist.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
 
 #if !KERNEL_LINUX
 #error "This module is for Linux only."
@@ -35,7 +35,7 @@ static const char *config_keys[] = {"Device", "IgnoreSelected",
 static const char *const dirname_sysfs = "/sys/class/thermal";
 static const char *const dirname_procfs = "/proc/acpi/thermal_zone";
 
-static _Bool force_procfs = 0;
+static bool force_procfs;
 static ignorelist_t *device_list;
 
 enum dev_type { TEMP = 0, COOLING_DEV };
@@ -59,7 +59,7 @@ static int thermal_sysfs_device_read(const char __attribute__((unused)) * dir,
                                      const char *name,
                                      void __attribute__((unused)) * user_data) {
   char filename[PATH_MAX];
-  _Bool success = 0;
+  bool success = false;
   value_t value;
 
   if (device_list && ignorelist_match(device_list, name))
@@ -69,13 +69,13 @@ static int thermal_sysfs_device_read(const char __attribute__((unused)) * dir,
   if (parse_value_file(filename, &value, DS_TYPE_GAUGE) == 0) {
     value.gauge /= 1000.0;
     thermal_submit(name, TEMP, value);
-    success = 1;
+    success = true;
   }
 
   snprintf(filename, sizeof(filename), "%s/%s/cur_state", dirname_sysfs, name);
   if (parse_value_file(filename, &value, DS_TYPE_GAUGE) == 0) {
     thermal_submit(name, COOLING_DEV, value);
-    success = 1;
+    success = true;
   }
 
   return success ? 0 : -1;
@@ -157,9 +157,9 @@ static int thermal_config(const char *key, const char *value) {
     if (IS_TRUE(value))
       ignorelist_set_invert(device_list, 0);
   } else if (strcasecmp(key, "ForceUseProcfs") == 0) {
-    force_procfs = 0;
+    force_procfs = false;
     if (IS_TRUE(value))
-      force_procfs = 1;
+      force_procfs = true;
   } else {
     return -1;
   }
index 7900133..a3d865b 100644 (file)
@@ -25,9 +25,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 #include "utils_threshold.h"
 
@@ -109,90 +109,6 @@ static int ut_threshold_add(const threshold_t *th) { /* {{{ */
  * The following approximately two hundred functions are used to handle the
  * configuration and fill the threshold list.
  * {{{ */
-static int ut_config_type_datasource(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("threshold values: The `DataSource' option needs exactly one "
-            "string argument.");
-    return -1;
-  }
-
-  sstrncpy(th->data_source, ci->values[0].value.string,
-           sizeof(th->data_source));
-
-  return 0;
-} /* int ut_config_type_datasource */
-
-static int ut_config_type_instance(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("threshold values: The `Instance' option needs exactly one "
-            "string argument.");
-    return -1;
-  }
-
-  sstrncpy(th->type_instance, ci->values[0].value.string,
-           sizeof(th->type_instance));
-
-  return 0;
-} /* int ut_config_type_instance */
-
-static int ut_config_type_max(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    WARNING("threshold values: The `%s' option needs exactly one "
-            "number argument.",
-            ci->key);
-    return -1;
-  }
-
-  if (strcasecmp(ci->key, "WarningMax") == 0)
-    th->warning_max = ci->values[0].value.number;
-  else
-    th->failure_max = ci->values[0].value.number;
-
-  return 0;
-} /* int ut_config_type_max */
-
-static int ut_config_type_min(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    WARNING("threshold values: The `%s' option needs exactly one "
-            "number argument.",
-            ci->key);
-    return -1;
-  }
-
-  if (strcasecmp(ci->key, "WarningMin") == 0)
-    th->warning_min = ci->values[0].value.number;
-  else
-    th->failure_min = ci->values[0].value.number;
-
-  return 0;
-} /* int ut_config_type_min */
-
-static int ut_config_type_hits(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    WARNING("threshold values: The `%s' option needs exactly one "
-            "number argument.",
-            ci->key);
-    return -1;
-  }
-
-  th->hits = ci->values[0].value.number;
-
-  return 0;
-} /* int ut_config_type_hits */
-
-static int ut_config_type_hysteresis(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    WARNING("threshold values: The `%s' option needs exactly one "
-            "number argument.",
-            ci->key);
-    return -1;
-  }
-
-  th->hysteresis = ci->values[0].value.number;
-
-  return 0;
-} /* int ut_config_type_hysteresis */
-
 static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) {
   threshold_t th;
   int status = 0;
@@ -223,15 +139,19 @@ static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) {
     oconfig_item_t *option = ci->children + i;
 
     if (strcasecmp("Instance", option->key) == 0)
-      status = ut_config_type_instance(&th, option);
+      status = cf_util_get_string_buffer(option, th.type_instance,
+                                         sizeof(th.type_instance));
     else if (strcasecmp("DataSource", option->key) == 0)
-      status = ut_config_type_datasource(&th, option);
-    else if ((strcasecmp("WarningMax", option->key) == 0) ||
-             (strcasecmp("FailureMax", option->key) == 0))
-      status = ut_config_type_max(&th, option);
-    else if ((strcasecmp("WarningMin", option->key) == 0) ||
-             (strcasecmp("FailureMin", option->key) == 0))
-      status = ut_config_type_min(&th, option);
+      status = cf_util_get_string_buffer(option, th.data_source,
+                                         sizeof(th.data_source));
+    else if (strcasecmp("WarningMax", option->key) == 0)
+      status = cf_util_get_double(option, &th.warning_max);
+    else if (strcasecmp("FailureMax", option->key) == 0)
+      status = cf_util_get_double(option, &th.failure_max);
+    else if (strcasecmp("WarningMin", option->key) == 0)
+      status = cf_util_get_double(option, &th.warning_min);
+    else if (strcasecmp("FailureMin", option->key) == 0)
+      status = cf_util_get_double(option, &th.failure_min);
     else if (strcasecmp("Interesting", option->key) == 0)
       status = cf_util_get_flag(option, &th.flags, UT_FLAG_INTERESTING);
     else if (strcasecmp("Invert", option->key) == 0)
@@ -243,9 +163,9 @@ static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) {
     else if (strcasecmp("Percentage", option->key) == 0)
       status = cf_util_get_flag(option, &th.flags, UT_FLAG_PERCENTAGE);
     else if (strcasecmp("Hits", option->key) == 0)
-      status = ut_config_type_hits(&th, option);
+      status = cf_util_get_int(option, &th.hits);
     else if (strcasecmp("Hysteresis", option->key) == 0)
-      status = ut_config_type_hysteresis(&th, option);
+      status = cf_util_get_double(option, &th.hysteresis);
     else {
       WARNING("threshold values: Option `%s' not allowed inside a `Type' "
               "block.",
@@ -264,19 +184,6 @@ static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) {
   return status;
 } /* int ut_config_type */
 
-static int ut_config_plugin_instance(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("threshold values: The `Instance' option needs exactly one "
-            "string argument.");
-    return -1;
-  }
-
-  sstrncpy(th->plugin_instance, ci->values[0].value.string,
-           sizeof(th->plugin_instance));
-
-  return 0;
-} /* int ut_config_plugin_instance */
-
 static int ut_config_plugin(const threshold_t *th_orig, oconfig_item_t *ci) {
   threshold_t th;
   int status = 0;
@@ -302,7 +209,8 @@ static int ut_config_plugin(const threshold_t *th_orig, oconfig_item_t *ci) {
     if (strcasecmp("Type", option->key) == 0)
       status = ut_config_type(&th, option);
     else if (strcasecmp("Instance", option->key) == 0)
-      status = ut_config_plugin_instance(&th, option);
+      status = cf_util_get_string_buffer(option, th.plugin_instance,
+                                         sizeof(th.plugin_instance));
     else {
       WARNING("threshold values: Option `%s' not allowed inside a `Plugin' "
               "block.",
@@ -401,7 +309,10 @@ static int ut_report_state(const data_set_t *ds, const value_list_t *vl,
   /* If the state didn't change, report if `persistent' is specified. If the
    * state is `okay', then only report if `persist_ok` flag is set. */
   if (state == state_old) {
-    if ((th->flags & UT_FLAG_PERSIST) == 0)
+    if (state == STATE_UNKNOWN) {
+      /* From UNKNOWN to UNKNOWN. Persist doesn't apply here. */
+      return 0;
+    } else if ((th->flags & UT_FLAG_PERSIST) == 0)
       return 0;
     else if ((state == STATE_OKAY) && ((th->flags & UT_FLAG_PERSIST_OK) == 0))
       return 0;
@@ -459,6 +370,10 @@ static int ut_report_state(const data_set_t *ds, const value_list_t *vl,
       snprintf(buf, bufsize, ": All data sources are within range again. "
                              "Current value of \"%s\" is %f.",
                ds->ds[ds_index].name, values[ds_index]);
+  } else if (state == STATE_UNKNOWN) {
+    ERROR("ut_report_state: metric transition to UNKNOWN from a different "
+          "state. This shouldn't happen.");
+    return 0;
   } else {
     double min;
     double max;
@@ -547,7 +462,7 @@ static int ut_check_one_data_source(
   if (ds != NULL) {
     ds_name = ds->ds[ds_index].name;
     if ((th->data_source[0] != 0) && (strcmp(ds_name, th->data_source) != 0))
-      return STATE_OKAY;
+      return STATE_UNKNOWN;
   }
 
   if ((th->flags & UT_FLAG_INVERT) != 0) {
@@ -576,6 +491,7 @@ static int ut_check_one_data_source(
     case STATE_WARNING:
       hysteresis_for_warning = th->hysteresis;
       break;
+    case STATE_UNKNOWN:
     case STATE_OKAY:
       /* do nothing -- the hysteresis only applies to the non-normal states */
       break;
@@ -617,7 +533,8 @@ static int ut_check_one_data_source(
  *
  * Checks all data sources of a value list against the given threshold, using
  * the ut_check_one_data_source function above. Returns the worst status,
- * which is `okay' if nothing has failed.
+ * which is `okay' if nothing has failed or `unknown' if no valid datasource was
+ * defined.
  * Returns less than zero if the data set doesn't have any data sources.
  */
 static int ut_check_one_threshold(const data_set_t *ds, const value_list_t *vl,
index 1534f51..59b7a34 100644 (file)
@@ -21,8 +21,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 
 #include <tcrdb.h>
 static const char *config_keys[] = {"Host", "Port"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static char *config_host = NULL;
-static char *config_port = NULL;
+static char *config_host;
+static char *config_port;
 
-static TCRDB *rdb = NULL;
+static TCRDB *rdb;
 
 static int tt_config(const char *key, const char *value) {
   if (strcasecmp("Host", key) == 0) {
index f388515..deb16e0 100644 (file)
@@ -37,8 +37,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_time.h"
 
 #include "msr-index.h"
 
 #define PLUGIN_NAME "turbostat"
 
+typedef enum affinity_policy_enum {
+  policy_restore_affinity, /* restore cpu affinity to whatever it was before */
+  policy_allcpus_affinity  /* do not restore affinity, set to all cpus */
+} affinity_policy_t;
+
+/* the default is to set cpu affinity to all cpus */
+static affinity_policy_t affinity_policy = policy_allcpus_affinity;
+
 /*
  * This tool uses the Model-Specific Registers (MSRs) present on Intel
  * processors.
  *
  * This value is automatically set if mperf or aperf go backward
  */
-static _Bool aperf_mperf_unstable;
+static bool aperf_mperf_unstable;
 
 /*
  * If set, use kernel logical core numbering for all "per core" metrics.
  */
-static _Bool config_lcn;
+static bool config_lcn;
 
 /*
  * Bitmask of the list of core C states supported by the processor.
@@ -78,7 +86,7 @@ static _Bool config_lcn;
  */
 static unsigned int do_core_cstate;
 static unsigned int config_core_cstate;
-static _Bool apply_config_core_cstate;
+static bool apply_config_core_cstate;
 
 /*
  * Bitmask of the list of pacages C states supported by the processor.
@@ -86,15 +94,15 @@ static _Bool apply_config_core_cstate;
  */
 static unsigned int do_pkg_cstate;
 static unsigned int config_pkg_cstate;
-static _Bool apply_config_pkg_cstate;
+static bool apply_config_pkg_cstate;
 
 /*
  * Boolean indicating if the processor supports 'I/O System-Management Interrupt
  * counter'
  */
-static _Bool do_smi;
-static _Bool config_smi;
-static _Bool apply_config_smi;
+static bool do_smi;
+static bool config_smi;
+static bool apply_config_smi;
 
 /*
  * Boolean indicating if the processor supports 'Digital temperature sensor'
@@ -105,9 +113,9 @@ static _Bool apply_config_smi;
  * might be wrong
  *  - Temperatures above the tcc_activation_temp are not recorded
  */
-static _Bool do_dts;
-static _Bool config_dts;
-static _Bool apply_config_dts;
+static bool do_dts;
+static bool config_dts;
+static bool apply_config_dts;
 
 /*
  * Boolean indicating if the processor supports 'Package thermal management'
@@ -118,9 +126,9 @@ static _Bool apply_config_dts;
  * might be wrong
  *  - Temperatures above the tcc_activation_temp are not recorded
  */
-static _Bool do_ptm;
-static _Bool config_ptm;
-static _Bool apply_config_ptm;
+static bool do_ptm;
+static bool config_ptm;
+static bool apply_config_ptm;
 
 /*
  * Thermal Control Circuit Activation Temperature as configured by the user.
@@ -129,14 +137,21 @@ static _Bool apply_config_ptm;
  */
 static unsigned int tcc_activation_temp;
 
+static unsigned int do_power_fields;
+#define UFS_PLATFORM (1 << 0)
+#define TURBO_PLATFORM (1 << 1)
+#define PSTATES_PLATFORM (1 << 2)
+
 static unsigned int do_rapl;
 static unsigned int config_rapl;
-static _Bool apply_config_rapl;
+static bool apply_config_rapl;
 static double rapl_energy_units;
+static double rapl_power_units;
 
 #define RAPL_PKG (1 << 0)
 /* 0x610 MSR_PKG_POWER_LIMIT */
 /* 0x611 MSR_PKG_ENERGY_STATUS */
+/* 0x614 MSR_PKG_POWER_INFO */
 #define RAPL_DRAM (1 << 1)
 /* 0x618 MSR_DRAM_POWER_LIMIT */
 /* 0x619 MSR_DRAM_ENERGY_STATUS */
@@ -188,6 +203,10 @@ static struct pkg_data {
   uint32_t energy_dram;  /* MSR_DRAM_ENERGY_STATUS */
   uint32_t energy_cores; /* MSR_PP0_ENERGY_STATUS */
   uint32_t energy_gfx;   /* MSR_PP1_ENERGY_STATUS */
+  uint32_t tdp;
+  uint8_t turbo_enabled;
+  uint8_t pstates_enabled;
+  uint32_t uncore;
   unsigned int tcc_activation_temp;
   unsigned int pkg_temp_c;
 } * package_delta, *package_even, *package_odd;
@@ -195,10 +214,10 @@ static struct pkg_data {
 #define DELTA_COUNTERS thread_delta, core_delta, package_delta
 #define ODD_COUNTERS thread_odd, core_odd, package_odd
 #define EVEN_COUNTERS thread_even, core_even, package_even
-static _Bool is_even = 1;
+static bool is_even = true;
 
-static _Bool allocated = 0;
-static _Bool initialized = 0;
+static bool allocated;
+static bool initialized;
 
 #define GET_THREAD(thread_base, thread_no, core_no, pkg_no)                    \
   (thread_base + (pkg_no)*topology.num_cores * topology.num_threads +          \
@@ -210,8 +229,8 @@ static _Bool initialized = 0;
 struct cpu_topology {
   unsigned int package_id;
   unsigned int core_id;
-  _Bool first_core_in_package;
-  _Bool first_thread_in_core;
+  bool first_core_in_package;
+  bool first_thread_in_core;
 };
 
 static struct topology {
@@ -233,6 +252,7 @@ static const char *config_keys[] = {
     "TCCActivationTemp",
     "RunningAveragePowerLimit",
     "LogicalCoreNames",
+    "RestoreAffinityPolicy",
 };
 static const int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
@@ -243,10 +263,10 @@ static const int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 /*
  * Open a MSR device for reading
  * Can change the scheduling affinity of the current process if multiple_read is
- * 1
+ * true
  */
 static int __attribute__((warn_unused_result))
-open_msr(unsigned int cpu, _Bool multiple_read) {
+open_msr(unsigned int cpu, bool multiple_read) {
   char pathname[32];
   int fd;
 
@@ -395,6 +415,8 @@ get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) {
   if (do_rapl & RAPL_PKG) {
     READ_MSR(MSR_PKG_ENERGY_STATUS, &msr);
     p->energy_pkg = msr & 0xFFFFFFFF;
+    READ_MSR(MSR_PKG_POWER_INFO, &msr);
+    p->tdp = msr & 0x7FFF;
   }
   if (do_rapl & RAPL_CORES) {
     READ_MSR(MSR_PP0_ENERGY_STATUS, &msr);
@@ -412,6 +434,18 @@ get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) {
     READ_MSR(MSR_IA32_PACKAGE_THERM_STATUS, &msr);
     p->pkg_temp_c = p->tcc_activation_temp - ((msr >> 16) & 0x7F);
   }
+  if (do_power_fields & TURBO_PLATFORM) {
+    READ_MSR(MSR_IA32_MISC_ENABLE, &msr);
+    p->turbo_enabled = !((msr >> 38) & 0x1);
+  }
+  if (do_power_fields & PSTATES_PLATFORM) {
+    READ_MSR(MSR_IA32_MISC_ENABLE, &msr);
+    p->pstates_enabled = (msr >> 16) & 0x1;
+  }
+  if (do_power_fields & UFS_PLATFORM) {
+    READ_MSR(MSR_UNCORE_FREQ_SCALING, &msr);
+    p->uncore = msr & 0x1F;
+  }
 
 out:
   close(msr_fd);
@@ -442,6 +476,11 @@ static inline void delta_package(struct pkg_data *delta,
   delta->energy_cores = new->energy_cores - old->energy_cores;
   delta->energy_gfx = new->energy_gfx - old->energy_gfx;
   delta->energy_dram = new->energy_dram - old->energy_dram;
+  delta->tdp = new->tdp;
+  delta->turbo_enabled = new->turbo_enabled;
+  delta->pstates_enabled = new->pstates_enabled;
+  delta->tcc_activation_temp = new->tcc_activation_temp;
+  delta->uncore = new->uncore;
 }
 
 /*
@@ -487,7 +526,7 @@ delta_thread(struct thread_data *delta, const struct thread_data *new,
               "the entire interval. Fix this by running "
               "Linux-2.6.30 or later.");
 
-      aperf_mperf_unstable = 1;
+      aperf_mperf_unstable = true;
     }
   }
 
@@ -627,9 +666,11 @@ static int submit_counters(struct thread_data *t, struct core_data *c,
     turbostat_submit(name, "percent", "pc10", 100.0 * p->pc10 / t->tsc);
 
   if (do_rapl) {
-    if (do_rapl & RAPL_PKG)
+    if (do_rapl & RAPL_PKG) {
       turbostat_submit(name, "power", "pkg",
                        p->energy_pkg * rapl_energy_units / interval_float);
+      turbostat_submit(name, "tdp", "pkg", p->tdp * rapl_power_units);
+    }
     if (do_rapl & RAPL_CORES)
       turbostat_submit(name, "power", "cores",
                        p->energy_cores * rapl_energy_units / interval_float);
@@ -640,6 +681,18 @@ static int submit_counters(struct thread_data *t, struct core_data *c,
       turbostat_submit(name, "power", "DRAM",
                        p->energy_dram * rapl_energy_units / interval_float);
   }
+
+  if (do_power_fields & TURBO_PLATFORM) {
+    turbostat_submit(name, "turbo_enabled", NULL, p->turbo_enabled);
+  }
+  if (do_power_fields & PSTATES_PLATFORM) {
+    turbostat_submit(name, "pstates_enabled", NULL, p->pstates_enabled);
+  }
+  if (do_power_fields & UFS_PLATFORM) {
+    turbostat_submit(name, "uncore_ratio", NULL, p->uncore);
+  }
+  turbostat_submit(name, "temperature", "tcc_activation",
+                   p->tcc_activation_temp);
 done:
   return 0;
 }
@@ -895,14 +948,14 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) {
     switch (model) {
     /* Atom (partial) */
     case 0x27:
-      do_smi = 0;
+      do_smi = false;
       do_core_cstate = 0;
       do_pkg_cstate = (1 << 2) | (1 << 4) | (1 << 6);
       break;
     /* Silvermont */
     case 0x37: /* BYT */
     case 0x4D: /* AVN */
-      do_smi = 1;
+      do_smi = true;
       do_core_cstate = (1 << 1) | (1 << 6);
       do_pkg_cstate = (1 << 6);
       break;
@@ -912,7 +965,7 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) {
                   Forest */
     case 0x1F: /* Core i7 and i5 Processor - Nehalem */
     case 0x2E: /* Nehalem-EX Xeon - Beckton */
-      do_smi = 1;
+      do_smi = true;
       do_core_cstate = (1 << 3) | (1 << 6);
       do_pkg_cstate = (1 << 3) | (1 << 6) | (1 << 7);
       break;
@@ -920,21 +973,21 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) {
     case 0x25: /* Westmere Client - Clarkdale, Arrandale */
     case 0x2C: /* Westmere EP - Gulftown */
     case 0x2F: /* Westmere-EX Xeon - Eagleton */
-      do_smi = 1;
+      do_smi = true;
       do_core_cstate = (1 << 3) | (1 << 6);
       do_pkg_cstate = (1 << 3) | (1 << 6) | (1 << 7);
       break;
     /* Sandy Bridge */
     case 0x2A: /* SNB */
     case 0x2D: /* SNB Xeon */
-      do_smi = 1;
+      do_smi = true;
       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7);
       break;
     /* Ivy Bridge */
     case 0x3A: /* IVB */
     case 0x3E: /* IVB Xeon */
-      do_smi = 1;
+      do_smi = true;
       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7);
       break;
@@ -942,31 +995,31 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) {
     case 0x3C: /* HSW */
     case 0x3F: /* HSW */
     case 0x46: /* HSW */
-      do_smi = 1;
+      do_smi = true;
       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7);
       break;
     case 0x45: /* HSW */
-      do_smi = 1;
+      do_smi = true;
       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7) | (1 << 8) |
                       (1 << 9) | (1 << 10);
       break;
-    /* Broadwel */
+    /* Broadwell */
     case 0x4F: /* BDW */
     case 0x56: /* BDX-DE */
-      do_smi = 1;
+      do_smi = true;
       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7);
       break;
     case 0x3D: /* BDW */
-      do_smi = 1;
+      do_smi = true;
       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7) | (1 << 8) |
                       (1 << 9) | (1 << 10);
       break;
     default:
-      do_smi = 0;
+      do_smi = false;
       do_core_cstate = 0;
       do_pkg_cstate = 0;
       break;
@@ -978,16 +1031,19 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) {
     case 0x45: /* HSW */
     case 0x46: /* HSW */
     case 0x3D: /* BDW */
+    case 0x5E: /* SKL */
       do_rapl = RAPL_PKG | RAPL_CORES | RAPL_GFX;
       break;
     case 0x3F: /* HSX */
     case 0x4F: /* BDX */
     case 0x56: /* BDX-DE */
       do_rapl = RAPL_PKG | RAPL_DRAM;
+      do_power_fields = TURBO_PLATFORM | UFS_PLATFORM | PSTATES_PLATFORM;
       break;
     case 0x2D: /* SNB Xeon */
     case 0x3E: /* IVB Xeon */
       do_rapl = RAPL_PKG | RAPL_CORES | RAPL_DRAM;
+      do_power_fields = TURBO_PLATFORM | PSTATES_PLATFORM;
       break;
     case 0x37: /* BYT */
     case 0x4D: /* AVN */
@@ -1022,6 +1078,7 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) {
     if (get_msr(0, MSR_RAPL_POWER_UNIT, &msr))
       return 0;
 
+    rapl_power_units = 1.0 / (1 << (msr & 0xF));
     if (model == 0x37)
       rapl_energy_units = 1.0 * (1 << (msr >> 8 & 0x1F)) / 1000000;
     else
@@ -1226,7 +1283,7 @@ static int __attribute__((warn_unused_result)) topology_probe(void) {
     if (ret < 0)
       goto err;
     else if ((unsigned int)ret == i)
-      cpu->first_core_in_package = 1;
+      cpu->first_core_in_package = true;
 
     ret = get_threads_on_core(i);
     if (ret < 0)
@@ -1240,7 +1297,7 @@ static int __attribute__((warn_unused_result)) topology_probe(void) {
     if (ret < 0)
       goto err;
     else if ((unsigned int)ret == i)
-      cpu->first_thread_in_core = 1;
+      cpu->first_thread_in_core = true;
 
     DEBUG("turbostat plugin: cpu %d pkg %d core %d\n", i, cpu->package_id,
           cpu->core_id);
@@ -1337,8 +1394,8 @@ static void initialize_counters(void) {
 }
 
 static void free_all_buffers(void) {
-  allocated = 0;
-  initialized = 0;
+  allocated = false;
+  initialized = false;
 
   CPU_FREE(cpu_present_set);
   cpu_present_set = NULL;
@@ -1399,13 +1456,37 @@ static int setup_all_buffers(void) {
   DO_OR_GOTO_ERR(for_all_cpus(set_temperature_target, EVEN_COUNTERS));
   DO_OR_GOTO_ERR(for_all_cpus(set_temperature_target, ODD_COUNTERS));
 
-  allocated = 1;
+  allocated = true;
   return 0;
 err:
   free_all_buffers();
   return ret;
 }
 
+int save_affinity(void) {
+  if (affinity_policy == policy_restore_affinity) {
+    /* Try to save the scheduling affinity, as it will be modified by
+     * get_counters().
+     */
+    if (sched_getaffinity(0, cpu_saved_affinity_setsize,
+                          cpu_saved_affinity_set) != 0)
+      return -1;
+  }
+
+  return 0;
+}
+
+void restore_affinity(void) {
+  /* Let's restore the affinity to the value saved in save_affinity */
+  if (affinity_policy == policy_restore_affinity)
+    (void)sched_setaffinity(0, cpu_saved_affinity_setsize,
+                            cpu_saved_affinity_set);
+  else {
+    /* reset the affinity to all present cpus */
+    (void)sched_setaffinity(0, cpu_present_setsize, cpu_present_set);
+  }
+}
+
 static int turbostat_read(void) {
   int ret;
 
@@ -1425,10 +1506,9 @@ static int turbostat_read(void) {
     }
   }
 
-  /* Saving the scheduling affinity, as it will be modified by get_counters */
-  if (sched_getaffinity(0, cpu_saved_affinity_setsize,
-                        cpu_saved_affinity_set) != 0) {
-    ERROR("turbostat plugin: Unable to save the CPU affinity");
+  if (save_affinity() != 0) {
+    ERROR("turbostat plugin: Unable to save the CPU affinity. Please read the "
+          "docs about RestoreAffinityPolicy option.");
     return -1;
   }
 
@@ -1436,8 +1516,8 @@ static int turbostat_read(void) {
     if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0)
       goto out;
     time_even = cdtime();
-    is_even = 1;
-    initialized = 1;
+    is_even = true;
+    initialized = true;
     ret = 0;
     goto out;
   }
@@ -1446,7 +1526,7 @@ static int turbostat_read(void) {
     if ((ret = for_all_cpus(get_counters, ODD_COUNTERS)) < 0)
       goto out;
     time_odd = cdtime();
-    is_even = 0;
+    is_even = false;
     time_delta = time_odd - time_even;
     if ((ret = for_all_cpus_delta(ODD_COUNTERS, EVEN_COUNTERS)) < 0)
       goto out;
@@ -1456,7 +1536,7 @@ static int turbostat_read(void) {
     if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0)
       goto out;
     time_even = cdtime();
-    is_even = 1;
+    is_even = true;
     time_delta = time_even - time_odd;
     if ((ret = for_all_cpus_delta(EVEN_COUNTERS, ODD_COUNTERS)) < 0)
       goto out;
@@ -1465,13 +1545,8 @@ static int turbostat_read(void) {
   }
   ret = 0;
 out:
-  /*
-   * Let's restore the affinity
-   * This might fail if the number of CPU changed, but we can't do anything in
-   * that case..
-   */
-  (void)sched_setaffinity(0, cpu_saved_affinity_setsize,
-                          cpu_saved_affinity_set);
+  restore_affinity();
+
   return ret;
 }
 
@@ -1553,7 +1628,7 @@ static int turbostat_config(const char *key, const char *value) {
       return -1;
     }
     config_core_cstate = (unsigned int)tmp_val;
-    apply_config_core_cstate = 1;
+    apply_config_core_cstate = true;
   } else if (strcasecmp("PackageCstates", key) == 0) {
     tmp_val = strtoul(value, &end, 0);
     if (*end != '\0' || tmp_val > UINT_MAX) {
@@ -1561,16 +1636,16 @@ static int turbostat_config(const char *key, const char *value) {
       return -1;
     }
     config_pkg_cstate = (unsigned int)tmp_val;
-    apply_config_pkg_cstate = 1;
+    apply_config_pkg_cstate = true;
   } else if (strcasecmp("SystemManagementInterrupt", key) == 0) {
     config_smi = IS_TRUE(value);
-    apply_config_smi = 1;
+    apply_config_smi = true;
   } else if (strcasecmp("DigitalTemperatureSensor", key) == 0) {
     config_dts = IS_TRUE(value);
-    apply_config_dts = 1;
+    apply_config_dts = true;
   } else if (strcasecmp("PackageThermalManagement", key) == 0) {
     config_ptm = IS_TRUE(value);
-    apply_config_ptm = 1;
+    apply_config_ptm = true;
   } else if (strcasecmp("LogicalCoreNames", key) == 0) {
     config_lcn = IS_TRUE(value);
   } else if (strcasecmp("RunningAveragePowerLimit", key) == 0) {
@@ -1580,7 +1655,7 @@ static int turbostat_config(const char *key, const char *value) {
       return -1;
     }
     config_rapl = (unsigned int)tmp_val;
-    apply_config_rapl = 1;
+    apply_config_rapl = true;
   } else if (strcasecmp("TCCActivationTemp", key) == 0) {
     tmp_val = strtoul(value, &end, 0);
     if (*end != '\0' || tmp_val > UINT_MAX) {
@@ -1588,6 +1663,15 @@ static int turbostat_config(const char *key, const char *value) {
       return -1;
     }
     tcc_activation_temp = (unsigned int)tmp_val;
+  } else if (strcasecmp("RestoreAffinityPolicy", key) == 0) {
+    if (strcasecmp("Restore", value) == 0)
+      affinity_policy = policy_restore_affinity;
+    else if (strcasecmp("AllCPUs", value) == 0)
+      affinity_policy = policy_allcpus_affinity;
+    else {
+      ERROR("turbostat plugin: Invalid RestoreAffinityPolicy '%s'", value);
+      return -1;
+    }
   } else {
     ERROR("turbostat plugin: Invalid configuration option '%s'", key);
     return -1;
index 4ca57aa..e9de64f 100644 (file)
@@ -31,6 +31,7 @@ clock_state             value:GAUGE:0:U
 clock_stratum           value:GAUGE:0:U
 compression             uncompressed:DERIVE:0:U, compressed:DERIVE:0:U
 compression_ratio       value:GAUGE:0:2
+commands                value:DERIVE:0:U
 connections             value:DERIVE:0:U
 conntrack               value:GAUGE:0:4294967295
 contextswitch           value:DERIVE:0:U
@@ -44,6 +45,7 @@ current                 value:GAUGE:U:U
 current_connections     value:GAUGE:0:U
 current_sessions        value:GAUGE:0:U
 delay                   value:GAUGE:-1000000:1000000
+delay_rate              value:GAUGE:0:U
 derive                  value:DERIVE:0:U
 df                      used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623
 df_complex              value:GAUGE:0:U
@@ -110,6 +112,7 @@ if_octets               rx:DERIVE:0:U, tx:DERIVE:0:U
 if_packets              rx:DERIVE:0:U, tx:DERIVE:0:U
 if_rx_dropped           value:DERIVE:0:U
 if_rx_errors            value:DERIVE:0:U
+if_rx_nohandler         value:DERIVE:0:U
 if_rx_octets            value:DERIVE:0:U
 if_rx_packets           value:DERIVE:0:U
 if_tx_dropped           value:DERIVE:0:U
@@ -206,9 +209,11 @@ ps_rss                  value:GAUGE:0:9223372036854775807
 ps_stacksize            value:GAUGE:0:9223372036854775807
 ps_state                value:GAUGE:0:65535
 ps_vm                   value:GAUGE:0:9223372036854775807
+pstates_enabled         value:GAUGE:0:1
 pubsub                  value:GAUGE:0:U
 queue_length            value:GAUGE:0:U
 records                 value:GAUGE:0:U
+redis_command_cputime   value:DERIVE:0:U
 requests                value:GAUGE:0:U
 response_code           value:GAUGE:0:U
 response_time           value:GAUGE:0:U
@@ -235,6 +240,7 @@ spl                     value:GAUGE:U:U
 swap                    value:GAUGE:0:1099511627776
 swap_io                 value:DERIVE:0:U
 tcp_connections         value:GAUGE:0:4294967295
+tdp                     value:GAUGE:U:U
 temperature             value:GAUGE:U:U
 threads                 value:GAUGE:0:U
 time_dispersion         value:GAUGE:-1000000:1000000
@@ -253,7 +259,10 @@ total_sessions          value:DERIVE:0:U
 total_threads           value:DERIVE:0:U
 total_time_in_ms        value:DERIVE:0:U
 total_values            value:DERIVE:0:U
+turbo_enabled           value:GAUGE:0:1
+transitions             value:DERIVE:0:U
 uptime                  value:GAUGE:0:4294967295
+uncore_ratio            value:GAUGE:0:U
 users                   value:GAUGE:0:65535
 vcl                     value:GAUGE:0:65535
 vcpu                    value:GAUGE:0:U
index 99e39ee..6ff5499 100644 (file)
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.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/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>
@@ -55,14 +55,14 @@ static const char *config_keys[] = {"SocketFile", "SocketGroup", "SocketPerms",
                                     "DeleteSocket"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static int loop = 0;
+static int loop;
 
 /* socket configuration */
 static int sock_fd = -1;
-static char *sock_file = NULL;
-static char *sock_group = NULL;
+static char *sock_file;
+static char *sock_group;
 static int sock_perms = S_IRWXU | S_IRWXG;
-static _Bool delete_socket = 0;
+static bool delete_socket;
 
 static pthread_t listen_thread = (pthread_t)0;
 
@@ -75,9 +75,7 @@ static int us_open_socket(void) {
 
   sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);
   if (sock_fd < 0) {
-    char errbuf[1024];
-    ERROR("unixsock plugin: socket failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("unixsock plugin: socket failed: %s", STRERRNO);
     return -1;
   }
 
@@ -91,9 +89,8 @@ static int us_open_socket(void) {
     errno = 0;
     status = unlink(sa.sun_path);
     if ((status != 0) && (errno != ENOENT)) {
-      char errbuf[1024];
       WARNING("unixsock plugin: Deleting socket file \"%s\" failed: %s",
-              sa.sun_path, sstrerror(errno, errbuf, sizeof(errbuf)));
+              sa.sun_path, STRERRNO);
     } else if (status == 0) {
       INFO("unixsock plugin: Successfully deleted socket file \"%s\".",
            sa.sun_path);
@@ -102,9 +99,7 @@ static int us_open_socket(void) {
 
   status = bind(sock_fd, (struct sockaddr *)&sa, sizeof(sa));
   if (status != 0) {
-    char errbuf[1024];
-    sstrerror(errno, errbuf, sizeof(errbuf));
-    ERROR("unixsock plugin: bind failed: %s", errbuf);
+    ERROR("unixsock plugin: bind failed: %s", STRERRNO);
     close(sock_fd);
     sock_fd = -1;
     return -1;
@@ -112,9 +107,7 @@ static int us_open_socket(void) {
 
   status = chmod(sa.sun_path, sock_perms);
   if (status == -1) {
-    char errbuf[1024];
-    ERROR("unixsock plugin: chmod failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("unixsock plugin: chmod failed: %s", STRERRNO);
     close(sock_fd);
     sock_fd = -1;
     return -1;
@@ -122,9 +115,7 @@ static int us_open_socket(void) {
 
   status = listen(sock_fd, 8);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("unixsock plugin: listen failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("unixsock plugin: listen failed: %s", STRERRNO);
     close(sock_fd);
     sock_fd = -1;
     return -1;
@@ -147,9 +138,8 @@ static int us_open_socket(void) {
 
     status = getgrnam_r(grpname, &sg, grbuf, sizeof(grbuf), &g);
     if (status != 0) {
-      char errbuf[1024];
       WARNING("unixsock plugin: getgrnam_r (%s) failed: %s", grpname,
-              sstrerror(status, errbuf, sizeof(errbuf)));
+              STRERROR(status));
       break;
     }
     if (g == NULL) {
@@ -159,10 +149,9 @@ static int us_open_socket(void) {
 
     if (chown((sock_file != NULL) ? sock_file : US_DEFAULT_PATH, (uid_t)-1,
               g->gr_gid) != 0) {
-      char errbuf[1024];
       WARNING("unixsock plugin: chown (%s, -1, %i) failed: %s",
               (sock_file != NULL) ? sock_file : US_DEFAULT_PATH, (int)g->gr_gid,
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+              STRERRNO);
     }
   } while (0);
 
@@ -182,18 +171,14 @@ static void *us_handle_client(void *arg) {
 
   fdout = dup(fdin);
   if (fdout < 0) {
-    char errbuf[1024];
-    ERROR("unixsock plugin: dup failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("unixsock plugin: dup failed: %s", STRERRNO);
     close(fdin);
     pthread_exit((void *)1);
   }
 
   fhin = fdopen(fdin, "r");
   if (fhin == NULL) {
-    char errbuf[1024];
-    ERROR("unixsock plugin: fdopen failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("unixsock plugin: fdopen failed: %s", STRERRNO);
     close(fdin);
     close(fdout);
     pthread_exit((void *)1);
@@ -202,9 +187,7 @@ static void *us_handle_client(void *arg) {
 
   fhout = fdopen(fdout, "w");
   if (fhout == NULL) {
-    char errbuf[1024];
-    ERROR("unixsock plugin: fdopen failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("unixsock plugin: fdopen failed: %s", STRERRNO);
     fclose(fhin); /* this closes fdin as well */
     close(fdout);
     pthread_exit((void *)1);
@@ -213,9 +196,7 @@ static void *us_handle_client(void *arg) {
 
   /* change output buffer to line buffered mode */
   if (setvbuf(fhout, NULL, _IOLBF, 0) != 0) {
-    char errbuf[1024];
-    ERROR("unixsock plugin: setvbuf failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("unixsock plugin: setvbuf failed: %s", STRERRNO);
     fclose(fhin);
     fclose(fhout);
     pthread_exit((void *)1);
@@ -227,7 +208,6 @@ static void *us_handle_client(void *arg) {
     char buffer_copy[1024];
     char *fields[128];
     int fields_num;
-    int len;
 
     errno = 0;
     if (fgets(buffer, sizeof(buffer), fhin) == NULL) {
@@ -235,14 +215,13 @@ static void *us_handle_client(void *arg) {
         continue;
 
       if (errno != 0) {
-        char errbuf[1024];
         WARNING("unixsock plugin: failed to read from socket #%i: %s",
-                fileno(fhin), sstrerror(errno, errbuf, sizeof(errbuf)));
+                fileno(fhin), STRERRNO);
       }
       break;
     }
 
-    len = strlen(buffer);
+    size_t len = strlen(buffer);
     while ((len > 0) &&
            ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
       buffer[--len] = '\0';
@@ -276,9 +255,8 @@ static void *us_handle_client(void *arg) {
       cmd_handle_flush(fhout, buffer);
     } else {
       if (fprintf(fhout, "-1 Unknown command: %s\n", fields[0]) < 0) {
-        char errbuf[1024];
         WARNING("unixsock plugin: failed to write to socket #%i: %s",
-                fileno(fhout), sstrerror(errno, errbuf, sizeof(errbuf)));
+                fileno(fhout), STRERRNO);
         break;
       }
     }
@@ -308,13 +286,11 @@ static void *us_server_thread(void __attribute__((unused)) * arg) {
     DEBUG("unixsock plugin: Calling accept..");
     status = accept(sock_fd, NULL, NULL);
     if (status < 0) {
-      char errbuf[1024];
 
       if (errno == EINTR)
         continue;
 
-      ERROR("unixsock plugin: accept failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("unixsock plugin: accept failed: %s", STRERRNO);
       close(sock_fd);
       sock_fd = -1;
       pthread_attr_destroy(&th_attr);
@@ -323,9 +299,7 @@ static void *us_server_thread(void __attribute__((unused)) * arg) {
 
     remote_fd = malloc(sizeof(*remote_fd));
     if (remote_fd == NULL) {
-      char errbuf[1024];
-      WARNING("unixsock plugin: malloc failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("unixsock plugin: malloc failed: %s", STRERRNO);
       close(status);
       continue;
     }
@@ -336,9 +310,7 @@ static void *us_server_thread(void __attribute__((unused)) * arg) {
     status = plugin_thread_create(&th, &th_attr, us_handle_client,
                                   (void *)remote_fd, "unixsock conn");
     if (status != 0) {
-      char errbuf[1024];
-      WARNING("unixsock plugin: pthread_create failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("unixsock plugin: pthread_create failed: %s", STRERRNO);
       close(*remote_fd);
       free(remote_fd);
       continue;
@@ -351,10 +323,8 @@ static void *us_server_thread(void __attribute__((unused)) * arg) {
 
   status = unlink((sock_file != NULL) ? sock_file : US_DEFAULT_PATH);
   if (status != 0) {
-    char errbuf[1024];
     NOTICE("unixsock plugin: unlink (%s) failed: %s",
-           (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
-           sstrerror(errno, errbuf, sizeof(errbuf)));
+           (sock_file != NULL) ? sock_file : US_DEFAULT_PATH, STRERRNO);
   }
 
   return (void *)0;
@@ -379,9 +349,9 @@ static int us_config(const char *key, const char *val) {
     sock_perms = (int)strtol(val, NULL, 8);
   } else if (strcasecmp(key, "DeleteSocket") == 0) {
     if (IS_TRUE(val))
-      delete_socket = 1;
+      delete_socket = true;
     else
-      delete_socket = 0;
+      delete_socket = false;
   } else {
     return -1;
   }
@@ -390,7 +360,7 @@ static int us_config(const char *key, const char *val) {
 } /* int us_config */
 
 static int us_init(void) {
-  static int have_init = 0;
+  static int have_init;
 
   int status;
 
@@ -404,9 +374,7 @@ static int us_init(void) {
   status = plugin_thread_create(&listen_thread, NULL, us_server_thread, NULL,
                                 "unixsock listen");
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("unixsock plugin: pthread_create failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("unixsock plugin: pthread_create failed: %s", STRERRNO);
     return -1;
   }
 
index 76eb0d5..dd33ab3 100644 (file)
@@ -21,8 +21,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if KERNEL_LINUX
 #include <sys/sysinfo.h>
@@ -52,6 +52,7 @@
 /*
  * Global variables
  */
+
 #if HAVE_KSTAT_H
 #include <kstat.h>
 #endif
@@ -90,9 +91,7 @@ static time_t uptime_get_sys(void) { /* {{{ */
 
   status = sysinfo(&info);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("uptime plugin: Error calling sysinfo: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("uptime plugin: Error calling sysinfo: %s", STRERRNO);
     return -1;
   }
 
@@ -151,9 +150,7 @@ static time_t uptime_get_sys(void) { /* {{{ */
   status = sysctl(mib, STATIC_ARRAY_SIZE(mib), &boottv, &boottv_len,
                   /* new_value = */ NULL, /* new_length = */ 0);
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("uptime plugin: No value read from sysctl interface: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("uptime plugin: No value read from sysctl interface: %s", STRERRNO);
     return -1;
   }
 
@@ -173,9 +170,7 @@ static time_t uptime_get_sys(void) { /* {{{ */
 
   status = perfstat_cpu_total(NULL, &cputotal, sizeof(perfstat_cpu_total_t), 1);
   if (status < 0) {
-    char errbuf[1024];
-    ERROR("uptime plugin: perfstat_cpu_total: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("uptime plugin: perfstat_cpu_total: %s", STRERRNO);
     return -1;
   }
 
index 6bc7cc3..fc03ba2 100644 (file)
@@ -27,8 +27,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_STATGRAB_H
 #include <statgrab.h>
diff --git a/src/utils/avltree/avltree.c b/src/utils/avltree/avltree.c
new file mode 100644 (file)
index 0000000..af6efed
--- /dev/null
@@ -0,0 +1,648 @@
+/**
+ * collectd - src/utils_avltree.c
+ * Copyright (C) 2006,2007  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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;
+}
diff --git a/src/utils/avltree/avltree.h b/src/utils/avltree/avltree.h
new file mode 100644 (file)
index 0000000..3f52b93
--- /dev/null
@@ -0,0 +1,169 @@
+/**
+ * collectd - src/utils_avltree.h
+ * Copyright (C) 2006,2007  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/avltree/avltree_test.c b/src/utils/avltree/avltree_test.c
new file mode 100644 (file)
index 0000000..8cbcb13
--- /dev/null
@@ -0,0 +1,180 @@
+/**
+ * collectd - src/tests/test_utils_avltree.c
+ * Copyright (C) 2013       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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;
+}
diff --git a/src/utils/cmds/cmds.c b/src/utils/cmds/cmds.c
new file mode 100644 (file)
index 0000000..036cefa
--- /dev/null
@@ -0,0 +1,308 @@
+/**
+ * collectd - src/utils_cmds.c
+ * Copyright (C) 2008       Florian Forster
+ * Copyright (C) 2016       Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/cmds.h b/src/utils/cmds/cmds.h
new file mode 100644 (file)
index 0000000..f3882f5
--- /dev/null
@@ -0,0 +1,209 @@
+/**
+ * collectd - src/utils_cmds.h
+ * Copyright (C) 2016 Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sebastian 'tokkee' Harl <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 */
diff --git a/src/utils/cmds/cmds_test.c b/src/utils/cmds/cmds_test.c
new file mode 100644 (file)
index 0000000..713f032
--- /dev/null
@@ -0,0 +1,224 @@
+/**
+ * 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;
+}
diff --git a/src/utils/cmds/flush.c b/src/utils/cmds/flush.c
new file mode 100644 (file)
index 0000000..d080d48
--- /dev/null
@@ -0,0 +1,177 @@
+/**
+ * collectd - src/utils_cmd_flush.c
+ * Copyright (C) 2008, 2016 Sebastian Harl
+ * Copyright (C) 2008       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sebastian "tokkee" Harl <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 */
diff --git a/src/utils/cmds/flush.h b/src/utils/cmds/flush.h
new file mode 100644 (file)
index 0000000..794ac1c
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * collectd - src/utils_cmd_flush.h
+ * Copyright (C) 2008, 2016 Sebastian Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sebastian "tokkee" Harl <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 */
diff --git a/src/utils/cmds/getthreshold.c b/src/utils/cmds/getthreshold.c
new file mode 100644 (file)
index 0000000..0d4e2f1
--- /dev/null
@@ -0,0 +1,182 @@
+/**
+ * collectd - src/utils_cmd_getthreshold.c
+ * Copyright (C) 2008,2009  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/getthreshold.h b/src/utils/cmds/getthreshold.h
new file mode 100644 (file)
index 0000000..78700ee
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * collectd - src/utils_cmd_getthreshold.h
+ * Copyright (C) 2009       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/getval.c b/src/utils/cmds/getval.c
new file mode 100644 (file)
index 0000000..f2586e1
--- /dev/null
@@ -0,0 +1,165 @@
+/**
+ * collectd - src/utils_cmd_getval.c
+ * Copyright (C) 2008       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/getval.h b/src/utils/cmds/getval.h
new file mode 100644 (file)
index 0000000..f2f1c2e
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * collectd - src/utils_cmd_getval.h
+ * Copyright (C) 2008       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/listval.c b/src/utils/cmds/listval.c
new file mode 100644 (file)
index 0000000..287786b
--- /dev/null
@@ -0,0 +1,103 @@
+/**
+ * collectd - src/utils_cmd_listval.c
+ * Copyright (C) 2008       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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, &times, &number);
+  if (status != 0) {
+    DEBUG("command listval: uc_get_names failed with status %i", status);
+    cmd_error(CMD_ERROR, &err, "uc_get_names failed.");
+    free_everything_and_return(CMD_ERROR);
+  }
+
+  print_to_socket(fh, "%i Value%s found\n", (int)number,
+                  (number == 1) ? "" : "s");
+  for (size_t i = 0; i < number; i++)
+    print_to_socket(fh, "%.3f %s\n", CDTIME_T_TO_DOUBLE(times[i]), names[i]);
+
+  free_everything_and_return(CMD_OK);
+} /* cmd_status_t cmd_handle_listval */
diff --git a/src/utils/cmds/listval.h b/src/utils/cmds/listval.h
new file mode 100644 (file)
index 0000000..6fff019
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * collectd - src/utils_cmd_listval.h
+ * Copyright (C) 2008       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/parse_option.c b/src/utils/cmds/parse_option.c
new file mode 100644 (file)
index 0000000..e6b516d
--- /dev/null
@@ -0,0 +1,148 @@
+/**
+ * collectd - src/utils_parse_option.c
+ * Copyright (C) 2008       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/parse_option.h b/src/utils/cmds/parse_option.h
new file mode 100644 (file)
index 0000000..3dd0a79
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+ * collectd - src/utils_parse_option.h
+ * Copyright (C) 2008       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/putnotif.c b/src/utils/cmds/putnotif.c
new file mode 100644 (file)
index 0000000..e1fecf8
--- /dev/null
@@ -0,0 +1,181 @@
+/**
+ * collectd - src/utils_cmd_putnotif.c
+ * Copyright (C) 2008       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/putnotif.h b/src/utils/cmds/putnotif.h
new file mode 100644 (file)
index 0000000..7ad0f1a
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * collectd - src/utils_cmd_putnotif.h
+ * Copyright (C) 2008       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/putval.c b/src/utils/cmds/putval.c
new file mode 100644 (file)
index 0000000..b6d5ccc
--- /dev/null
@@ -0,0 +1,285 @@
+/**
+ * collectd - src/utils_cmd_putval.c
+ * Copyright (C) 2007-2009  Florian octo Forster
+ * Copyright (C) 2016       Sebastian tokkee Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/cmds/putval.h b/src/utils/cmds/putval.h
new file mode 100644 (file)
index 0000000..f6c3e3b
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * collectd - src/utils_cmd_putval.h
+ * Copyright (C) 2007       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/common/common.c b/src/utils/common/common.c
new file mode 100644 (file)
index 0000000..d15f9b7
--- /dev/null
@@ -0,0 +1,1589 @@
+/**
+ * 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 */
diff --git a/src/utils/common/common.h b/src/utils/common/common.h
new file mode 100644 (file)
index 0000000..addf06d
--- /dev/null
@@ -0,0 +1,396 @@
+/**
+ * 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 */
diff --git a/src/utils/common/common_test.c b/src/utils/common/common_test.c
new file mode 100644 (file)
index 0000000..426082f
--- /dev/null
@@ -0,0 +1,378 @@
+/**
+ * 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;
+}
diff --git a/src/utils/config_cores/config_cores.c b/src/utils/config_cores/config_cores.c
new file mode 100644 (file)
index 0000000..13ea687
--- /dev/null
@@ -0,0 +1,372 @@
+/**
+ * collectd - src/utils_config_cores.c
+ *
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Kamil Wiatrowski <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;
+}
diff --git a/src/utils/config_cores/config_cores.h b/src/utils/config_cores/config_cores.h
new file mode 100644 (file)
index 0000000..d45f848
--- /dev/null
@@ -0,0 +1,136 @@
+/**
+ * collectd - src/utils_config_cores.h
+ *
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Kamil Wiatrowski <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 */
diff --git a/src/utils/config_cores/config_cores_test.c b/src/utils/config_cores/config_cores_test.c
new file mode 100644 (file)
index 0000000..8b4f4c4
--- /dev/null
@@ -0,0 +1,249 @@
+/**
+ * collectd - src/utils_config_cores_test.c
+ *
+ * Copyright(c) 2018 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Kamil Wiatrowski <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;
+}
diff --git a/src/utils/crc32/crc32.c b/src/utils/crc32/crc32.c
new file mode 100644 (file)
index 0000000..afc566a
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ *
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ */
+
+#include <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;
+}
diff --git a/src/utils/crc32/crc32.h b/src/utils/crc32/crc32.h
new file mode 100644 (file)
index 0000000..8e2c212
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * collectd - src/utils_crc32.h
+ * Copyright (C) 2014       Pierre-Yves Ritschard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ */
+
+#ifndef UTILS_CRC32_H
+#define UTILS_CRC32_H 1
+
+uint32_t crc32_buffer(const unsigned char *, size_t);
+
+#endif
diff --git a/src/utils/curl_stats/curl_stats.c b/src/utils/curl_stats/curl_stats.c
new file mode 100644 (file)
index 0000000..a897171
--- /dev/null
@@ -0,0 +1,252 @@
+/**
+ * collectd - src/utils_curl_stats.c
+ * Copyright (C) 2015       Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sebastian Harl <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 */
diff --git a/src/utils/curl_stats/curl_stats.h b/src/utils/curl_stats/curl_stats.h
new file mode 100644 (file)
index 0000000..3f83aab
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * collectd - src/utils_curl_stats.h
+ * Copyright (C) 2015       Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sebastian Harl <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 */
diff --git a/src/utils/db_query/db_query.c b/src/utils/db_query/db_query.c
new file mode 100644 (file)
index 0000000..392bd56
--- /dev/null
@@ -0,0 +1,1036 @@
+/**
+ * collectd - src/utils_db_query.c
+ * Copyright (C) 2008,2009  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/db_query/db_query.h b/src/utils/db_query/db_query.h
new file mode 100644 (file)
index 0000000..f173204
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * collectd - src/utils_db_query.h
+ * Copyright (C) 2008,2009  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/deq/deq.h b/src/utils/deq/deq.h
new file mode 100644 (file)
index 0000000..3182baa
--- /dev/null
@@ -0,0 +1,214 @@
+/**
+ * collectd - src/utils_deq.h
+ * Copyright(c) 2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Andy Smith <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
diff --git a/src/utils/dns/dns.c b/src/utils/dns/dns.c
new file mode 100644 (file)
index 0000000..2ea919b
--- /dev/null
@@ -0,0 +1,1171 @@
+/*
+ * 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
diff --git a/src/utils/dns/dns.h b/src/utils/dns/dns.h
new file mode 100644 (file)
index 0000000..9d9b75f
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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 */
diff --git a/src/utils/dpdk/dpdk.c b/src/utils/dpdk/dpdk.c
new file mode 100644 (file)
index 0000000..5e38ab3
--- /dev/null
@@ -0,0 +1,885 @@
+/*
+ * 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;
+}
diff --git a/src/utils/dpdk/dpdk.h b/src/utils/dpdk/dpdk.h
new file mode 100644 (file)
index 0000000..d4551d8
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * collectd - src/utils_dpdk.h
+ * MIT License
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Maryam Tahhan <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 */
diff --git a/src/utils/format_graphite/format_graphite.c b/src/utils/format_graphite/format_graphite.c
new file mode 100644 (file)
index 0000000..d0e047f
--- /dev/null
@@ -0,0 +1,335 @@
+/**
+ * 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 */
+
+static int gr_format_values(char *ret, size_t ret_len, int ds_num,
+                            const data_set_t *ds, const value_list_t *vl,
+                            gauge_t const *rates) {
+  size_t offset = 0;
+  int status;
+
+  assert(0 == strcmp(ds->type, vl->type));
+
+  memset(ret, 0, ret_len);
+
+#define BUFFER_ADD(...)                                                        \
+  do {                                                                         \
+    status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__);            \
+    if (status < 1) {                                                          \
+      return -1;                                                               \
+    } else if (((size_t)status) >= (ret_len - offset)) {                       \
+      return -1;                                                               \
+    } else                                                                     \
+      offset += ((size_t)status);                                              \
+  } while (0)
+
+  if (ds->ds[ds_num].type == DS_TYPE_GAUGE)
+    BUFFER_ADD(GAUGE_FORMAT, vl->values[ds_num].gauge);
+  else if (rates != NULL)
+    BUFFER_ADD("%f", rates[ds_num]);
+  else if (ds->ds[ds_num].type == DS_TYPE_COUNTER)
+    BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_num].counter);
+  else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
+    BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive);
+  else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
+    BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute);
+  else {
+    P_ERROR("gr_format_values: Unknown data source type: %i",
+            ds->ds[ds_num].type);
+    return -1;
+  }
+
+#undef BUFFER_ADD
+
+  return 0;
+}
+
+static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len,
+                                char escape_char, bool preserve_separator) {
+  memset(dst, 0, dst_len);
+
+  if (src == NULL)
+    return;
+
+  for (size_t i = 0; i < dst_len; i++) {
+    if (src[i] == 0) {
+      dst[i] = 0;
+      break;
+    }
+
+    if ((!preserve_separator && (src[i] == '.')) || isspace((int)src[i]) ||
+        iscntrl((int)src[i]))
+      dst[i] = escape_char;
+    else
+      dst[i] = src[i];
+  }
+}
+
+static int gr_format_name_tagged(char *ret, int ret_len, value_list_t const *vl,
+                                 char const *ds_name, char const *prefix,
+                                 char const *postfix, char const escape_char,
+                                 unsigned int flags) {
+  char n_host[DATA_MAX_NAME_LEN];
+  char n_plugin[DATA_MAX_NAME_LEN];
+  char n_plugin_instance[DATA_MAX_NAME_LEN];
+  char n_type[DATA_MAX_NAME_LEN];
+  char n_type_instance[DATA_MAX_NAME_LEN];
+
+  char tmp_plugin[DATA_MAX_NAME_LEN + 8];
+  char tmp_plugin_instance[DATA_MAX_NAME_LEN + 17];
+  char tmp_type[DATA_MAX_NAME_LEN + 6];
+  char tmp_type_instance[DATA_MAX_NAME_LEN + 15];
+  char tmp_metric[3 * DATA_MAX_NAME_LEN + 2];
+  char tmp_ds_name[DATA_MAX_NAME_LEN + 9];
+
+  if (prefix == NULL)
+    prefix = "";
+
+  if (postfix == NULL)
+    postfix = "";
+
+  gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, 1);
+  gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char, 1);
+  gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
+                      sizeof(n_plugin_instance), escape_char, 1);
+  gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char, 1);
+  gr_copy_escape_part(n_type_instance, vl->type_instance,
+                      sizeof(n_type_instance), escape_char, 1);
+
+  snprintf(tmp_plugin, sizeof(tmp_plugin), ";plugin=%s", n_plugin);
+
+  if (n_plugin_instance[0] != '\0')
+    snprintf(tmp_plugin_instance, sizeof(tmp_plugin_instance),
+             ";plugin_instance=%s", n_plugin_instance);
+  else
+    tmp_plugin_instance[0] = '\0';
+
+  if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) || strcmp(n_plugin, n_type) != 0)
+    snprintf(tmp_type, sizeof(tmp_type), ";type=%s", n_type);
+  else
+    tmp_type[0] = '\0';
+
+  if (n_type_instance[0] != '\0') {
+    if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) ||
+        strcmp(n_plugin_instance, n_type_instance) != 0)
+      snprintf(tmp_type_instance, sizeof(tmp_type_instance),
+               ";type_instance=%s", n_type_instance);
+    else
+      tmp_type_instance[0] = '\0';
+  } else
+    tmp_type_instance[0] = '\0';
+
+  /* Assert always_append_ds -> ds_name */
+  assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
+  if (ds_name != NULL) {
+    snprintf(tmp_ds_name, sizeof(tmp_ds_name), ";ds_name=%s", ds_name);
+
+    if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, ds_name);
+    else
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s.%s", n_plugin, n_type,
+               ds_name);
+  } else {
+    tmp_ds_name[0] = '\0';
+
+    if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s", n_plugin);
+    else
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, n_type);
+  }
+
+  snprintf(ret, ret_len, "%s%s%s;host=%s%s%s%s%s%s", prefix, tmp_metric,
+           postfix, n_host, tmp_plugin, tmp_plugin_instance, tmp_type,
+           tmp_type_instance, tmp_ds_name);
+
+  return 0;
+}
+
+static int gr_format_name(char *ret, int ret_len, value_list_t const *vl,
+                          char const *ds_name, char const *prefix,
+                          char const *postfix, char const escape_char,
+                          unsigned int flags) {
+  char n_host[DATA_MAX_NAME_LEN];
+  char n_plugin[DATA_MAX_NAME_LEN];
+  char n_plugin_instance[DATA_MAX_NAME_LEN];
+  char n_type[DATA_MAX_NAME_LEN];
+  char n_type_instance[DATA_MAX_NAME_LEN];
+
+  char tmp_plugin[2 * DATA_MAX_NAME_LEN + 1];
+  char tmp_type[2 * DATA_MAX_NAME_LEN + 1];
+
+  if (prefix == NULL)
+    prefix = "";
+
+  if (postfix == NULL)
+    postfix = "";
+
+  bool preserve_separator = (flags & GRAPHITE_PRESERVE_SEPARATOR);
+
+  gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char,
+                      preserve_separator);
+  gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char,
+                      preserve_separator);
+  gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
+                      sizeof(n_plugin_instance), escape_char,
+                      preserve_separator);
+  gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char,
+                      preserve_separator);
+  gr_copy_escape_part(n_type_instance, vl->type_instance,
+                      sizeof(n_type_instance), escape_char, preserve_separator);
+
+  if (n_plugin_instance[0] != '\0')
+    snprintf(tmp_plugin, sizeof(tmp_plugin), "%s%c%s", n_plugin,
+             (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+             n_plugin_instance);
+  else
+    sstrncpy(tmp_plugin, n_plugin, sizeof(tmp_plugin));
+
+  if (n_type_instance[0] != '\0') {
+    if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+      sstrncpy(tmp_type, n_type_instance, sizeof(tmp_type));
+    else
+      snprintf(tmp_type, sizeof(tmp_type), "%s%c%s", n_type,
+               (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+               n_type_instance);
+  } else
+    sstrncpy(tmp_type, n_type, sizeof(tmp_type));
+
+  /* Assert always_append_ds -> ds_name */
+  assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
+  if (ds_name != NULL) {
+    if ((flags & GRAPHITE_DROP_DUPE_FIELDS) &&
+        strcmp(tmp_plugin, tmp_type) == 0)
+      snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix,
+               tmp_plugin, ds_name);
+    else
+      snprintf(ret, ret_len, "%s%s%s.%s.%s.%s", prefix, n_host, postfix,
+               tmp_plugin, tmp_type, ds_name);
+  } else
+    snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix, tmp_plugin,
+             tmp_type);
+
+  return 0;
+}
+
+static void escape_graphite_string(char *buffer, char escape_char) {
+  assert(strchr(GRAPHITE_FORBIDDEN, escape_char) == NULL);
+
+  for (char *head = buffer + strcspn(buffer, GRAPHITE_FORBIDDEN); *head != '\0';
+       head += strcspn(head, GRAPHITE_FORBIDDEN))
+    *head = escape_char;
+}
+
+int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds,
+                    value_list_t const *vl, char const *prefix,
+                    char const *postfix, char const escape_char,
+                    unsigned int flags) {
+  int status = 0;
+  int buffer_pos = 0;
+
+  gauge_t *rates = NULL;
+  if (flags & GRAPHITE_STORE_RATES) {
+    rates = uc_get_rate(ds, vl);
+    if (rates == NULL) {
+      P_ERROR("format_graphite: error with uc_get_rate");
+      return -1;
+    }
+  }
+
+  for (size_t i = 0; i < ds->ds_num; i++) {
+    char const *ds_name = NULL;
+    char key[10 * DATA_MAX_NAME_LEN];
+    char values[512];
+    size_t message_len;
+    char message[1024];
+
+    if ((flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds->ds_num > 1))
+      ds_name = ds->ds[i].name;
+
+    /* Copy the identifier to `key' and escape it. */
+    if (flags & GRAPHITE_USE_TAGS) {
+      status = gr_format_name_tagged(key, sizeof(key), vl, ds_name, prefix,
+                                     postfix, escape_char, flags);
+      if (status != 0) {
+        P_ERROR("format_graphite: error with gr_format_name_tagged");
+        sfree(rates);
+        return status;
+      }
+    } else {
+      status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix,
+                              escape_char, flags);
+      if (status != 0) {
+        P_ERROR("format_graphite: error with gr_format_name");
+        sfree(rates);
+        return status;
+      }
+    }
+
+    escape_graphite_string(key, escape_char);
+
+    /* Convert the values to an ASCII representation and put that into
+     * `values'. */
+    status = gr_format_values(values, sizeof(values), i, ds, vl, rates);
+    if (status != 0) {
+      P_ERROR("format_graphite: error with gr_format_values");
+      sfree(rates);
+      return status;
+    }
+
+    /* Compute the graphite command */
+    message_len =
+        (size_t)snprintf(message, sizeof(message), "%s %s %u\r\n", key, values,
+                         (unsigned int)CDTIME_T_TO_TIME_T(vl->time));
+    if (message_len >= sizeof(message)) {
+      P_ERROR("format_graphite: message buffer too small: "
+              "Need %" PRIsz " bytes.",
+              message_len + 1);
+      sfree(rates);
+      return -ENOMEM;
+    }
+
+    /* Append it in case we got multiple data set */
+    if ((buffer_pos + message_len) >= buffer_size) {
+      P_ERROR("format_graphite: target buffer too small");
+      sfree(rates);
+      return -ENOMEM;
+    }
+    memcpy((void *)(buffer + buffer_pos), message, message_len);
+    buffer_pos += message_len;
+    buffer[buffer_pos] = '\0';
+  }
+  sfree(rates);
+  return status;
+} /* int format_graphite */
diff --git a/src/utils/format_graphite/format_graphite.h b/src/utils/format_graphite/format_graphite.h
new file mode 100644 (file)
index 0000000..60b89ae
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * 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
+
+int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds,
+                    const value_list_t *vl, const char *prefix,
+                    const char *postfix, const char escape_char,
+                    unsigned int flags);
+
+#endif /* UTILS_FORMAT_GRAPHITE_H */
diff --git a/src/utils/format_graphite/format_graphite_test.c b/src/utils/format_graphite/format_graphite_test.c
new file mode 100644 (file)
index 0000000..2c14d01
--- /dev/null
@@ -0,0 +1,207 @@
+/**
+ * collectd - src/utils_format_graphite_test.c
+ * Copyright (C) 2016       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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;
+}
diff --git a/src/utils/format_json/format_json.c b/src/utils/format_json/format_json.c
new file mode 100644 (file)
index 0000000..b82f21d
--- /dev/null
@@ -0,0 +1,696 @@
+/**
+ * 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
diff --git a/src/utils/format_json/format_json.h b/src/utils/format_json/format_json.h
new file mode 100644 (file)
index 0000000..d3d0216
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * collectd - src/utils_format_json.h
+ * Copyright (C) 2009       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/format_json/format_json_test.c b/src/utils/format_json/format_json_test.c
new file mode 100644 (file)
index 0000000..d041604
--- /dev/null
@@ -0,0 +1,183 @@
+/**
+ * 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;
+}
diff --git a/src/utils/format_kairosdb/format_kairosdb.c b/src/utils/format_kairosdb/format_kairosdb.c
new file mode 100644 (file)
index 0000000..3f29fcd
--- /dev/null
@@ -0,0 +1,358 @@
+/**
+ * collectd - src/utils_format_kairosdb.c
+ * Copyright (C) 2016       Aurelien beorn Rougemont
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Aurelien beorn Rougemont <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 */
diff --git a/src/utils/format_kairosdb/format_kairosdb.h b/src/utils/format_kairosdb/format_kairosdb.h
new file mode 100644 (file)
index 0000000..7b9e0e7
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * collectd - src/utils_format_kairosdb.h
+ * Copyright (C) 2016       Aurelien Rougemont
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Aurelien beorn Rougemont <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 */
diff --git a/src/utils/format_stackdriver/format_stackdriver.c b/src/utils/format_stackdriver/format_stackdriver.c
new file mode 100644 (file)
index 0000000..80b85ae
--- /dev/null
@@ -0,0 +1,766 @@
+/**
+ * collectd - src/utils_format_stackdriver.c
+ * ISC license
+ *
+ * Copyright (C) 2017  Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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 */
diff --git a/src/utils/format_stackdriver/format_stackdriver.h b/src/utils/format_stackdriver/format_stackdriver.h
new file mode 100644 (file)
index 0000000..fee260e
--- /dev/null
@@ -0,0 +1,79 @@
+/**
+ * collectd - src/utils_format_stackdriver.h
+ * ISC license
+ *
+ * Copyright (C) 2017  Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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 */
diff --git a/src/utils/format_stackdriver/format_stackdriver_test.c b/src/utils/format_stackdriver/format_stackdriver_test.c
new file mode 100644 (file)
index 0000000..d4935a3
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * collectd - src/utils_format_stackdriver_test.c
+ * ISC license
+ *
+ * Copyright (C) 2017  Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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;
+}
diff --git a/src/utils/gce/gce.c b/src/utils/gce/gce.c
new file mode 100644 (file)
index 0000000..8092765
--- /dev/null
@@ -0,0 +1,284 @@
+/**
+ * collectd - src/utils_gce.c
+ * ISC license
+ *
+ * Copyright (C) 2017  Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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 */
diff --git a/src/utils/gce/gce.h b/src/utils/gce/gce.h
new file mode 100644 (file)
index 0000000..2ee3f6e
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * collectd - src/utils_gce.h
+ * ISC license
+ *
+ * Copyright (C) 2017  Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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
diff --git a/src/utils/heap/heap.c b/src/utils/heap/heap.c
new file mode 100644 (file)
index 0000000..2fe2bcb
--- /dev/null
@@ -0,0 +1,207 @@
+/**
+ * collectd - src/utils_heap.c
+ * Copyright (C) 2009       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/heap/heap.h b/src/utils/heap/heap.h
new file mode 100644 (file)
index 0000000..d2d70cd
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ * collectd - src/utils_heap.h
+ * Copyright (C) 2009       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/heap/heap_test.c b/src/utils/heap/heap_test.c
new file mode 100644 (file)
index 0000000..169824e
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * collectd - src/tests/test_utils_heap.c
+ * Copyright (C) 2013       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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;
+}
diff --git a/src/utils/ignorelist/ignorelist.c b/src/utils/ignorelist/ignorelist.c
new file mode 100644 (file)
index 0000000..9e1b9e3
--- /dev/null
@@ -0,0 +1,309 @@
+/**
+ * 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) */
diff --git a/src/utils/ignorelist/ignorelist.h b/src/utils/ignorelist/ignorelist.h
new file mode 100644 (file)
index 0000000..a7fa86d
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * 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 */
diff --git a/src/utils/latency/latency.c b/src/utils/latency/latency.c
new file mode 100644 (file)
index 0000000..12ff2ca
--- /dev/null
@@ -0,0 +1,344 @@
+/**
+ * collectd - src/utils_latency.c
+ * Copyright (C) 2013       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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 */
diff --git a/src/utils/latency/latency.h b/src/utils/latency/latency.h
new file mode 100644 (file)
index 0000000..9d878da
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * 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);
diff --git a/src/utils/latency/latency_config.c b/src/utils/latency/latency_config.c
new file mode 100644 (file)
index 0000000..a5ae471
--- /dev/null
@@ -0,0 +1,157 @@
+/**
+ * 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 */
diff --git a/src/utils/latency/latency_config.h b/src/utils/latency/latency_config.h
new file mode 100644 (file)
index 0000000..3d2691a
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * collectd - src/utils_latency_config.c
+ * Copyright (C) 2013-2016   Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/latency/latency_test.c b/src/utils/latency/latency_test.c
new file mode 100644 (file)
index 0000000..1325017
--- /dev/null
@@ -0,0 +1,234 @@
+/**
+ * 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;
+}
diff --git a/src/utils/lookup/vl_lookup.c b/src/utils/lookup/vl_lookup.c
new file mode 100644 (file)
index 0000000..38d81ca
--- /dev/null
@@ -0,0 +1,630 @@
+/**
+ * collectd - src/utils_vl_lookup.c
+ * Copyright (C) 2012       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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 */
diff --git a/src/utils/lookup/vl_lookup.h b/src/utils/lookup/vl_lookup.h
new file mode 100644 (file)
index 0000000..90a4ee5
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * collectd - src/utils_vl_lookup.h
+ * Copyright (C) 2012       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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 */
diff --git a/src/utils/lookup/vl_lookup_test.c b/src/utils/lookup/vl_lookup_test.c
new file mode 100644 (file)
index 0000000..2dd53ce
--- /dev/null
@@ -0,0 +1,242 @@
+/**
+ * collectd - src/tests/test_utils_vl_lookup.c
+ * Copyright (C) 2012       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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 */
diff --git a/src/utils/match/match.c b/src/utils/match/match.c
new file mode 100644 (file)
index 0000000..ca6f1aa
--- /dev/null
@@ -0,0 +1,376 @@
+/**
+ * collectd - src/utils_match.c
+ * Copyright (C) 2008-2014  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/match/match.h b/src/utils/match/match.h
new file mode 100644 (file)
index 0000000..c4aee0a
--- /dev/null
@@ -0,0 +1,179 @@
+/**
+ * collectd - src/utils_match.h
+ * Copyright (C) 2008-2014  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/metadata/meta_data.c b/src/utils/metadata/meta_data.c
new file mode 100644 (file)
index 0000000..963aebb
--- /dev/null
@@ -0,0 +1,747 @@
+/**
+ * collectd - src/meta_data.c
+ * Copyright (C) 2008-2011  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/metadata/meta_data.h b/src/utils/metadata/meta_data.h
new file mode 100644 (file)
index 0000000..203b146
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * collectd - src/meta_data.h
+ * Copyright (C) 2008-2011  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/metadata/meta_data_test.c b/src/utils/metadata/meta_data_test.c
new file mode 100644 (file)
index 0000000..b9ff86d
--- /dev/null
@@ -0,0 +1,117 @@
+/**
+ * collectd - src/daemon/meta_data_test.c
+ * Copyright (C) 2015       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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;
+}
diff --git a/src/utils/mount/mount.c b/src/utils/mount/mount.c
new file mode 100644 (file)
index 0000000..319c624
--- /dev/null
@@ -0,0 +1,765 @@
+/**
+ * collectd - src/utils_mount.c
+ * Copyright (C) 2005,2006  Niki W. Waibel
+ *
+ * This program is free software; you can redistribute it and/
+ * or modify it under the terms of the GNU General Public Li-
+ * cence as published by the Free Software Foundation; either
+ * version 2 of the Licence, or any later version.
+ *
+ * This program is distributed in the hope that it will be use-
+ * ful, but WITHOUT ANY WARRANTY; without even the implied war-
+ * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Niki W. Waibel <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) */
diff --git a/src/utils/mount/mount.h b/src/utils/mount/mount.h
new file mode 100644 (file)
index 0000000..0ad7d02
--- /dev/null
@@ -0,0 +1,186 @@
+/**
+ * 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 */
diff --git a/src/utils/mount/mount_test.c b/src/utils/mount/mount_test.c
new file mode 100644 (file)
index 0000000..e34e677
--- /dev/null
@@ -0,0 +1,115 @@
+/**
+ * collectd - src/tests/test_utils_mount.c
+ * Copyright (C) 2013       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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;
+}
diff --git a/src/utils/oauth/oauth.c b/src/utils/oauth/oauth.c
new file mode 100644 (file)
index 0000000..671de84
--- /dev/null
@@ -0,0 +1,637 @@
+/**
+ * collectd - src/utils_oauth.c
+ * ISC license
+ *
+ * Copyright (C) 2017  Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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 (status >= buffer_size)
+    return ENOMEM;
+
+  return 0;
+} /* }}} int get_assertion */
+
+int oauth_parse_json_token(char const *json, /* {{{ */
+                           char *out_access_token, size_t access_token_size,
+                           cdtime_t *expires_in) {
+  time_t expire_in_seconds = 0;
+  yajl_val root;
+  yajl_val token_val;
+  yajl_val expire_val;
+  char errbuf[1024];
+  const char *token_path[] = {"access_token", NULL};
+  const char *expire_path[] = {"expires_in", NULL};
+
+  root = yajl_tree_parse(json, errbuf, sizeof(errbuf));
+  if (root == NULL) {
+    ERROR("utils_oauth: oauth_parse_json_token: parse error %s", errbuf);
+    return -1;
+  }
+
+  token_val = yajl_tree_get(root, token_path, yajl_t_string);
+  if (token_val == NULL) {
+    ERROR("utils_oauth: oauth_parse_json_token: access token field not found");
+    yajl_tree_free(root);
+    return -1;
+  }
+  sstrncpy(out_access_token, YAJL_GET_STRING(token_val), access_token_size);
+
+  expire_val = yajl_tree_get(root, expire_path, yajl_t_number);
+  if (expire_val == NULL) {
+    ERROR("utils_oauth: oauth_parse_json_token: expire field found");
+    yajl_tree_free(root);
+    return -1;
+  }
+  expire_in_seconds = (time_t)YAJL_GET_INTEGER(expire_val);
+  DEBUG("oauth_parse_json_token: expires_in %lu",
+        (unsigned long)expire_in_seconds);
+
+  *expires_in = TIME_T_TO_CDTIME_T(expire_in_seconds);
+  yajl_tree_free(root);
+  return 0;
+} /* }}} int oauth_parse_json_token */
+
+static int new_token(oauth_t *auth) /* {{{ */
+{
+  CURL *curl;
+  char assertion[1024];
+  char post_data[1024];
+  memory_t data;
+  char access_token[256];
+  cdtime_t expires_in;
+  cdtime_t now;
+  char curl_errbuf[CURL_ERROR_SIZE];
+  int status = 0;
+
+  data.size = 0;
+  data.memory = NULL;
+
+  now = cdtime();
+
+  status = get_assertion(auth, assertion, sizeof(assertion));
+  if (status != 0) {
+    ERROR("utils_oauth: Failed to get token using service account %s.",
+          auth->iss);
+    return -1;
+  }
+
+  snprintf(post_data, sizeof(post_data), "grant_type=%s&assertion=%s",
+           OAUTH_GRANT_TYPE, assertion);
+
+  curl = curl_easy_init();
+  if (curl == NULL) {
+    ERROR("utils_oauth: curl_easy_init failed.");
+    return -1;
+  }
+
+  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory);
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
+  curl_easy_setopt(curl, CURLOPT_POST, 1L);
+  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
+  curl_easy_setopt(curl, CURLOPT_URL, auth->url);
+
+  status = curl_easy_perform(curl);
+  if (status != CURLE_OK) {
+    ERROR("utils_oauth: curl_easy_perform failed with status %i: %s", status,
+          curl_errbuf);
+
+    sfree(data.memory);
+    curl_easy_cleanup(curl);
+
+    return -1;
+  } else {
+    long http_code = 0;
+
+    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+    if ((http_code < 200) || (http_code >= 300)) {
+      ERROR("utils_oauth: POST request to %s failed: HTTP error %ld", auth->url,
+            http_code);
+      if (data.memory != NULL)
+        INFO("utils_oauth: Server replied: %s", data.memory);
+
+      sfree(data.memory);
+      curl_easy_cleanup(curl);
+
+      return -1;
+    }
+  }
+
+  status = oauth_parse_json_token(data.memory, access_token,
+                                  sizeof(access_token), &expires_in);
+  if (status != 0) {
+    sfree(data.memory);
+    curl_easy_cleanup(curl);
+
+    return -1;
+  }
+
+  sfree(auth->token);
+  auth->token = strdup(access_token);
+  if (auth->token == NULL) {
+    ERROR("utils_oauth: strdup failed");
+    auth->valid_until = 0;
+
+    sfree(data.memory);
+    curl_easy_cleanup(curl);
+    return -1;
+  }
+
+  INFO("utils_oauth: OAuth2 access token is valid for %.3fs",
+       CDTIME_T_TO_DOUBLE(expires_in));
+  auth->valid_until = now + expires_in;
+
+  sfree(data.memory);
+  curl_easy_cleanup(curl);
+
+  return 0;
+} /* }}} int new_token */
+
+static int renew_token(oauth_t *auth) /* {{{ */
+{
+  /* Renew OAuth token 30 seconds *before* it expires. */
+  cdtime_t const slack = TIME_T_TO_CDTIME_T(30);
+
+  if (auth->valid_until > (cdtime() + slack))
+    return 0;
+
+  return new_token(auth);
+} /* }}} int renew_token */
+
+static oauth_t *oauth_create(char const *url, char const *iss,
+                             char const *scope, char const *aud,
+                             EVP_PKEY *key) /* {{{ */
+{
+  oauth_t *auth;
+
+  if ((url == NULL) || (iss == NULL) || (scope == NULL) || (aud == NULL) ||
+      (key == NULL))
+    return NULL;
+
+  auth = malloc(sizeof(*auth));
+  if (auth == NULL)
+    return NULL;
+  memset(auth, 0, sizeof(*auth));
+
+  auth->url = strdup(url);
+  auth->iss = strdup(iss);
+  auth->scope = strdup(scope);
+  auth->aud = strdup(aud);
+
+  if ((auth->url == NULL) || (auth->iss == NULL) || (auth->scope == NULL) ||
+      (auth->aud == NULL)) {
+    oauth_destroy(auth);
+    return NULL;
+  }
+
+  auth->key = key;
+
+  return auth;
+} /* }}} oauth_t *oauth_create */
+
+/*
+ * Public
+ */
+oauth_google_t oauth_create_google_json(char const *buffer, char const *scope) {
+  char errbuf[1024];
+  yajl_val root = yajl_tree_parse(buffer, errbuf, sizeof(errbuf));
+  if (root == NULL) {
+    ERROR("utils_oauth: oauth_create_google_json: parse error %s", errbuf);
+    return (oauth_google_t){NULL};
+  }
+
+  yajl_val field_project =
+      yajl_tree_get(root, (char const *[]){"project_id", NULL}, yajl_t_string);
+  if (field_project == NULL) {
+    ERROR("utils_oauth: oauth_create_google_json: project_id field not found");
+    yajl_tree_free(root);
+    return (oauth_google_t){NULL};
+  }
+  char const *project_id = YAJL_GET_STRING(field_project);
+
+  yajl_val field_iss = yajl_tree_get(
+      root, (char const *[]){"client_email", NULL}, yajl_t_string);
+  if (field_iss == NULL) {
+    ERROR(
+        "utils_oauth: oauth_create_google_json: client_email field not found");
+    yajl_tree_free(root);
+    return (oauth_google_t){NULL};
+  }
+
+  yajl_val field_token_uri =
+      yajl_tree_get(root, (char const *[]){"token_uri", NULL}, yajl_t_string);
+  char const *token_uri = (field_token_uri != NULL)
+                              ? YAJL_GET_STRING(field_token_uri)
+                              : GOOGLE_TOKEN_URL;
+
+  yajl_val field_priv_key =
+      yajl_tree_get(root, (char const *[]){"private_key", NULL}, yajl_t_string);
+  if (field_priv_key == NULL) {
+    ERROR("utils_oauth: oauth_create_google_json: private_key field not found");
+    yajl_tree_free(root);
+    return (oauth_google_t){NULL};
+  }
+
+  BIO *bp = BIO_new_mem_buf(YAJL_GET_STRING(field_priv_key), -1);
+  EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
+  if (pkey == NULL) {
+    char errbuf[1024];
+    ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+    ERROR(
+        "utils_oauth: oauth_create_google_json: parsing private key failed: %s",
+        errbuf);
+    BIO_free(bp);
+    yajl_tree_free(root);
+    return (oauth_google_t){NULL};
+  }
+
+  BIO_free(bp);
+
+  oauth_t *oauth = oauth_create(token_uri, YAJL_GET_STRING(field_iss), scope,
+                                token_uri, pkey);
+  if (oauth == NULL) {
+    yajl_tree_free(root);
+    return (oauth_google_t){NULL};
+  }
+
+  oauth_google_t ret = {
+      .project_id = strdup(project_id), .oauth = oauth,
+  };
+
+  yajl_tree_free(root);
+  return ret;
+} /* oauth_google_t oauth_create_google_json */
+
+oauth_google_t oauth_create_google_file(char const *path,
+                                        char const *scope) { /* {{{ */
+  int fd = open(path, O_RDONLY);
+  if (fd == -1)
+    return (oauth_google_t){NULL};
+
+  struct stat st = {0};
+  if (fstat(fd, &st) != 0) {
+    close(fd);
+    return (oauth_google_t){NULL};
+  }
+
+  size_t buf_size = (size_t)st.st_size;
+  char *buf = calloc(1, buf_size + 1);
+  if (buf == NULL) {
+    close(fd);
+    return (oauth_google_t){NULL};
+  }
+
+  if (sread(fd, buf, buf_size) != 0) {
+    free(buf);
+    close(fd);
+    return (oauth_google_t){NULL};
+  }
+  close(fd);
+  buf[buf_size] = 0;
+
+  oauth_google_t ret = oauth_create_google_json(buf, scope);
+
+  free(buf);
+  return ret;
+} /* }}} oauth_google_t oauth_create_google_file */
+
+/* oauth_create_google_default checks for JSON credentials in well-known
+ * positions, similar to gcloud and other tools. */
+oauth_google_t oauth_create_google_default(char const *scope) {
+  char const *app_creds;
+  if ((app_creds = getenv("GOOGLE_APPLICATION_CREDENTIALS")) != NULL) {
+    oauth_google_t ret = oauth_create_google_file(app_creds, scope);
+    if (ret.oauth == NULL) {
+      ERROR("The environment variable GOOGLE_APPLICATION_CREDENTIALS is set to "
+            "\"%s\" but that file could not be read.",
+            app_creds);
+    } else {
+      return ret;
+    }
+  }
+
+  char const *home;
+  if ((home = getenv("HOME")) != NULL) {
+    char path[PATH_MAX];
+    snprintf(path, sizeof(path),
+             "%s/.config/gcloud/application_default_credentials.json", home);
+
+    oauth_google_t ret = oauth_create_google_file(path, scope);
+    if (ret.oauth != NULL) {
+      return ret;
+    }
+  }
+
+  return (oauth_google_t){NULL};
+} /* }}} oauth_google_t oauth_create_google_default */
+
+void oauth_destroy(oauth_t *auth) /* {{{ */
+{
+  if (auth == NULL)
+    return;
+
+  sfree(auth->url);
+  sfree(auth->iss);
+  sfree(auth->scope);
+  sfree(auth->aud);
+
+  if (auth->key != NULL) {
+    EVP_PKEY_free(auth->key);
+    auth->key = NULL;
+  }
+
+  sfree(auth);
+} /* }}} void oauth_destroy */
+
+int oauth_access_token(oauth_t *auth, char *buffer,
+                       size_t buffer_size) /* {{{ */
+{
+  int status;
+
+  if (auth == NULL)
+    return EINVAL;
+
+  status = renew_token(auth);
+  if (status != 0)
+    return status;
+  assert(auth->token != NULL);
+
+  sstrncpy(buffer, auth->token, buffer_size);
+  return 0;
+} /* }}} int oauth_access_token */
diff --git a/src/utils/oauth/oauth.h b/src/utils/oauth/oauth.h
new file mode 100644 (file)
index 0000000..b93c87b
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * collectd - src/utils_oauth.h
+ * ISC license
+ *
+ * Copyright (C) 2017  Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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
diff --git a/src/utils/oauth/oauth_test.c b/src/utils/oauth/oauth_test.c
new file mode 100644 (file)
index 0000000..aa6e99a
--- /dev/null
@@ -0,0 +1,149 @@
+/**
+ * collectd - src/tests/utils_oauth_test.c
+ * Copyright (C) 2015  Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <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 */
diff --git a/src/utils/ovs/ovs.c b/src/utils/ovs/ovs.c
new file mode 100644 (file)
index 0000000..46c2c26
--- /dev/null
@@ -0,0 +1,1412 @@
+/**
+ * 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 **)&params,
+                &params_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;
+}
diff --git a/src/utils/ovs/ovs.h b/src/utils/ovs/ovs.h
new file mode 100644 (file)
index 0000000..c93322e
--- /dev/null
@@ -0,0 +1,236 @@
+/**
+ * collectd - src/utils_ovs.h
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Volodymyr Mytnyk <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
diff --git a/src/utils/rrdcreate/rrdcreate.c b/src/utils/rrdcreate/rrdcreate.c
new file mode 100644 (file)
index 0000000..ef01234
--- /dev/null
@@ -0,0 +1,660 @@
+/**
+ * collectd - src/utils_rrdcreate.c
+ * Copyright (C) 2006-2013  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/rrdcreate/rrdcreate.h b/src/utils/rrdcreate/rrdcreate.h
new file mode 100644 (file)
index 0000000..b2277e7
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * collectd - src/utils_rrdcreate.h
+ * Copyright (C) 2008-2013  Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils/tail/tail.c b/src/utils/tail/tail.c
new file mode 100644 (file)
index 0000000..db34a72
--- /dev/null
@@ -0,0 +1,224 @@
+/**
+ * collectd - src/utils_tail.c
+ * Copyright (C) 2007-2008  C-Ware, Inc.
+ * Copyright (C) 2008       Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author:
+ *   Luke Heberling <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 */
diff --git a/src/utils/tail/tail.h b/src/utils/tail/tail.h
new file mode 100644 (file)
index 0000000..73a6de2
--- /dev/null
@@ -0,0 +1,88 @@
+/**
+ * collectd - src/utils_tail.h
+ * Copyright (C) 2007-2008  C-Ware, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author:
+ *   Luke Heberling <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 */
diff --git a/src/utils/taskstats/taskstats.c b/src/utils/taskstats/taskstats.c
new file mode 100644 (file)
index 0000000..b020c06
--- /dev/null
@@ -0,0 +1,306 @@
+/**
+ * collectd - src/utils_taskstats.c
+ * Copyright (C) 2017       Florian octo Forster
+ *
+ * ISC License (ISC)
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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;
+}
diff --git a/src/utils/taskstats/taskstats.h b/src/utils/taskstats/taskstats.h
new file mode 100644 (file)
index 0000000..de07427
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * collectd - src/utils_taskstats.h
+ * Copyright (C) 2017       Florian octo Forster
+ *
+ * ISC License (ISC)
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <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 */
diff --git a/src/utils_cmd_flush.c b/src/utils_cmd_flush.c
deleted file mode 100644 (file)
index b180a50..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-/**
- * collectd - src/utils_cmd_flush.c
- * Copyright (C) 2008, 2016 Sebastian Harl
- * Copyright (C) 2008       Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Sebastian "tokkee" Harl <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 */
diff --git a/src/utils_cmd_flush.h b/src/utils_cmd_flush.h
deleted file mode 100644 (file)
index 129aa85..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * collectd - src/utils_cmd_flush.h
- * Copyright (C) 2008, 2016 Sebastian Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Sebastian "tokkee" Harl <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 */
diff --git a/src/utils_cmd_getthreshold.c b/src/utils_cmd_getthreshold.c
deleted file mode 100644 (file)
index 78f9a75..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
- * collectd - src/utils_cmd_getthreshold.c
- * Copyright (C) 2008,2009  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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) {                                          \
-    char errbuf[1024];                                                         \
-    WARNING("handle_getthreshold: failed to write to socket #%i: %s",          \
-            fileno(fh), sstrerror(errno, errbuf, sizeof(errbuf)));             \
-    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, "%zu Threshold found\n", i);
-
-  if (threshold.host[0] != 0)
-    print_to_socket(fh, "Host: %s\n", threshold.host);
-  if (threshold.plugin[0] != 0)
-    print_to_socket(fh, "Plugin: %s\n", threshold.plugin);
-  if (threshold.plugin_instance[0] != 0)
-    print_to_socket(fh, "Plugin Instance: %s\n", threshold.plugin_instance);
-  if (threshold.type[0] != 0)
-    print_to_socket(fh, "Type: %s\n", threshold.type);
-  if (threshold.type_instance[0] != 0)
-    print_to_socket(fh, "Type Instance: %s\n", threshold.type_instance);
-  if (threshold.data_source[0] != 0)
-    print_to_socket(fh, "Data Source: %s\n", threshold.data_source);
-  if (!isnan(threshold.warning_min))
-    print_to_socket(fh, "Warning Min: %g\n", threshold.warning_min);
-  if (!isnan(threshold.warning_max))
-    print_to_socket(fh, "Warning Max: %g\n", threshold.warning_max);
-  if (!isnan(threshold.failure_min))
-    print_to_socket(fh, "Failure Min: %g\n", threshold.failure_min);
-  if (!isnan(threshold.failure_max))
-    print_to_socket(fh, "Failure Max: %g\n", threshold.failure_max);
-  if (threshold.hysteresis > 0.0)
-    print_to_socket(fh, "Hysteresis: %g\n", threshold.hysteresis);
-  if (threshold.hits > 1)
-    print_to_socket(fh, "Hits: %i\n", threshold.hits);
-
-  return 0;
-} /* int handle_getthreshold */
diff --git a/src/utils_cmd_getthreshold.h b/src/utils_cmd_getthreshold.h
deleted file mode 100644 (file)
index 78700ee..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * collectd - src/utils_cmd_getthreshold.h
- * Copyright (C) 2009       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/utils_cmd_getval.c b/src/utils_cmd_getval.c
deleted file mode 100644 (file)
index 59046f6..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/**
- * collectd - src/utils_cmd_getval.c
- * Copyright (C) 2008       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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) {                                        \
-      char errbuf[1024];                                                       \
-      WARNING("cmd_handle_getval: failed to write to socket #%i: %s",          \
-              fileno(fh), sstrerror(errno, errbuf, sizeof(errbuf)));           \
-      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 = %zu, "
-          "but uc_get_rate_by_name returned %zu 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, "%zu Value%s found\n", values_num,
-                  (values_num == 1) ? "" : "s");
-  for (size_t i = 0; i < values_num; i++) {
-    print_to_socket(fh, "%s=", ds->ds[i].name);
-    if (isnan(values[i])) {
-      print_to_socket(fh, "NaN\n");
-    } else {
-      print_to_socket(fh, "%12e\n", values[i]);
-    }
-  }
-
-  sfree(values);
-  cmd_destroy(&cmd);
-
-  return CMD_OK;
-} /* cmd_status_t cmd_handle_getval */
-
-void cmd_destroy_getval(cmd_getval_t *getval) {
-  if (getval == NULL)
-    return;
-
-  sfree(getval->raw_identifier);
-} /* void cmd_destroy_getval */
diff --git a/src/utils_cmd_getval.h b/src/utils_cmd_getval.h
deleted file mode 100644 (file)
index 5c03fa4..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * collectd - src/utils_cmd_getval.h
- * Copyright (C) 2008       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/utils_cmd_listval.c b/src/utils_cmd_listval.c
deleted file mode 100644 (file)
index 3640630..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * collectd - src/utils_cmd_listval.c
- * Copyright (C) 2008       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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,
-                               cmd_listval_t *ret_listval
-                               __attribute__((unused)),
-                               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) {                                        \
-      char errbuf[1024];                                                       \
-      WARNING("handle_listval: failed to write to socket #%i: %s", fileno(fh), \
-              sstrerror(errno, errbuf, sizeof(errbuf)));                       \
-      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, &times, &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 */
-
-void cmd_destroy_listval(cmd_listval_t *listval __attribute__((unused))) {
-  /* nothing to do */
-} /* void cmd_destroy_listval */
diff --git a/src/utils_cmd_listval.h b/src/utils_cmd_listval.h
deleted file mode 100644 (file)
index 6abdeee..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * collectd - src/utils_cmd_listval.h
- * Copyright (C) 2008       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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,
-                               cmd_listval_t *ret_listval,
-                               const cmd_options_t *opts,
-                               cmd_error_handler_t *err);
-
-cmd_status_t cmd_handle_listval(FILE *fh, char *buffer);
-
-void cmd_destroy_listval(cmd_listval_t *listval);
-
-#endif /* UTILS_CMD_LISTVAL_H */
diff --git a/src/utils_cmd_putnotif.c b/src/utils_cmd_putnotif.c
deleted file mode 100644 (file)
index 0085c62..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/**
- * collectd - src/utils_cmd_putnotif.c
- * Copyright (C) 2008       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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) {                                        \
-      char errbuf[1024];                                                       \
-      WARNING("handle_putnotif: failed to write to socket #%i: %s",            \
-              fileno(fh), sstrerror(errno, errbuf, sizeof(errbuf)));           \
-      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 */
diff --git a/src/utils_cmd_putnotif.h b/src/utils_cmd_putnotif.h
deleted file mode 100644 (file)
index 7ad0f1a..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * collectd - src/utils_cmd_putnotif.h
- * Copyright (C) 2008       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/utils_cmd_putval.c b/src/utils_cmd_putval.c
deleted file mode 100644 (file)
index 6f1bc39..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-/**
- * collectd - src/utils_cmd_putval.c
- * Copyright (C) 2007-2009  Florian octo Forster
- * Copyright (C) 2016       Sebastian tokkee Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 = */ 0);
-  if (status != 0)
-    return status;
-  escape_string(buffer_values, sizeof(buffer_values));
-
-  snprintf(ret, ret_len, "PUTVAL %s interval=%.3f %s", buffer_ident,
-           (vl->interval > 0) ? CDTIME_T_TO_DOUBLE(vl->interval)
-                              : CDTIME_T_TO_DOUBLE(plugin_get_interval()),
-           buffer_values);
-
-  return 0;
-} /* }}} int cmd_create_putval */
diff --git a/src/utils_cmd_putval.h b/src/utils_cmd_putval.h
deleted file mode 100644 (file)
index bc6e193..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * collectd - src/utils_cmd_putval.h
- * Copyright (C) 2007       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/utils_cmds.c b/src/utils_cmds.c
deleted file mode 100644 (file)
index 055c987..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-/**
- * collectd - src/utils_cmds.c
- * Copyright (C) 2008       Florian Forster
- * Copyright (C) 2016       Sebastian 'tokkee' Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- *   Sebastian 'tokkee' Harl <sh at tokkee.org>
- **/
-
-#include "utils_cmds.h"
-#include "daemon/common.h"
-#include "utils_cmd_flush.h"
-#include "utils_cmd_getval.h"
-#include "utils_cmd_listval.h"
-#include "utils_cmd_putval.h"
-#include "utils_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, &ret_cmd->cmd.listval, 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:
-    cmd_destroy_listval(&cmd->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) {
-    char errbuf[1024];
-    WARNING("utils_cmds: failed to write to file-handle #%i: %s", fileno(fh),
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-    return;
-  }
-
-  fflush(fh);
-} /* void cmd_error_fh */
diff --git a/src/utils_cmds.h b/src/utils_cmds.h
deleted file mode 100644 (file)
index 26d5338..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-/**
- * collectd - src/utils_cmds.h
- * Copyright (C) 2016 Sebastian 'tokkee' Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Sebastian 'tokkee' Harl <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 {
-} cmd_listval_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_listval_t listval;
-    cmd_putval_t putval;
-  } cmd;
-} cmd_t;
-
-/*
- * NAME
- *   cmd_options_t
- *
- * DESCRIPTIONS
- *   Optional settings for tuning the parser behavior.
- */
-typedef struct {
-  /* identifier_default_host: If non-NULL, the hostname is optional and will
-   * default to the specified value. */
-  char *identifier_default_host;
-} cmd_options_t;
-
-/*
- * NAME
- *   cmd_status_t
- *
- * DESCRIPTION
- *   Status codes describing the parse result.
- */
-typedef enum {
-  CMD_OK = 0,
-  CMD_ERROR = -1,
-  CMD_PARSE_ERROR = -2,
-  CMD_UNKNOWN_COMMAND = -3,
-
-  /* Not necessarily fatal errors. */
-  CMD_NO_OPTION = 1,
-} cmd_status_t;
-
-/*
- * NAME
- *   cmd_error_handler_t
- *
- * DESCRIPTION
- *   An error handler describes a callback to be invoked when the parser
- *   encounters an error. The user data pointer will be passed to the callback
- *   as the first argument.
- */
-typedef struct {
-  void (*cb)(void *, cmd_status_t, const char *, va_list);
-  void *ud;
-} cmd_error_handler_t;
-
-/*
- * NAME:
- *   cmd_error
- *
- * DESCRIPTION
- *   Reports an error via the specified error handler (if set).
- */
-void cmd_error(cmd_status_t status, cmd_error_handler_t *err,
-               const char *format, ...);
-
-/*
- * NAME
- *   cmd_parse
- *
- * DESCRIPTION
- *   Parse a command string and populate a command object.
- *
- * PARAMETERS
- *   `buffer'  The command string to be parsed.
- *   `ret_cmd' The parse result will be stored at this location.
- *   `opts'    Parser options. If NULL, defaults will be used.
- *   `err'     An optional error handler to invoke on error.
- *
- * RETURN VALUE
- *   CMD_OK on success or the respective error code otherwise.
- */
-cmd_status_t cmd_parse(char *buffer, cmd_t *ret_cmd, const cmd_options_t *opts,
-                       cmd_error_handler_t *err);
-
-cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd,
-                        const cmd_options_t *opts, cmd_error_handler_t *err);
-
-void cmd_destroy(cmd_t *cmd);
-
-/*
- * NAME
- *   cmd_parse_option
- *
- * DESCRIPTION
- *   Parses a command option which must be of the form:
- *     name=value with \ and spaces
- *
- * PARAMETERS
- *   `field'     The parsed input field with any quotes removed and special
- *               characters unescaped.
- *   `ret_key'   The parsed key will be stored at this location.
- *   `ret_value' The parsed value will be stored at this location.
- *
- * RETURN VALUE
- *   CMD_OK on success or an error code otherwise.
- *   CMD_NO_OPTION if `field' does not represent an option at all (missing
- *   equal sign).
- */
-cmd_status_t cmd_parse_option(char *field, char **ret_key, char **ret_value,
-                              cmd_error_handler_t *err);
-
-/*
- * NAME
- *   cmd_error_fh
- *
- * DESCRIPTION
- *   An error callback writing the message to an open file handle using the
- *   format expected by the unixsock or exec plugins.
- *
- * PARAMETERS
- *   `ud'     Error handler user-data pointer. This must be an open
- *            file-handle (FILE *).
- *   `status' The error status code.
- *   `format' Printf-style format string.
- *   `ap'     Variable argument list providing the arguments for the format
- *            string.
- */
-void cmd_error_fh(void *ud, cmd_status_t status, const char *format,
-                  va_list ap);
-
-#endif /* UTILS_CMDS_H */
diff --git a/src/utils_cmds_test.c b/src/utils_cmds_test.c
deleted file mode 100644 (file)
index bb35ce8..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/**
- * collectd - src/tests/utils_cmds_test.c
- * Copyright (C) 2016       Sebastian 'tokkee' Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Sebastian 'tokkee' Harl <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;
-}
diff --git a/src/utils_config_cores.c b/src/utils_config_cores.c
deleted file mode 100644 (file)
index 085e8ab..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-/**
- * collectd - src/utils_config_cores.c
- *
- * Copyright(c) 2018 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- *   Kamil Wiatrowski <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 ((end - start) >= out_size) {
-      ERROR(UTIL_NAME ": Out buffer is too small.");
-      return -EINVAL;
-    }
-    sstrncpy(out, start, end - start + 1);
-    DEBUG(UTIL_NAME ": Mask for individual (not aggregated) cores: %s", out);
-  } else {
-    *grouped = 1;
-    sstrncpy(out, start, out_size);
-  }
-  return 0;
-}
-
-int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl) {
-  if (ci == NULL || cgl == NULL)
-    return -EINVAL;
-  if (ci->values_num == 0 || ci->values_num > MAX_CORES)
-    return -EINVAL;
-  core_group_t cgroups[MAX_CORES] = {{0}};
-  size_t cg_idx = 0; /* index for cgroups array */
-  int ret = 0;
-
-  for (int i = 0; i < ci->values_num; i++) {
-    if (ci->values[i].type != OCONFIG_TYPE_STRING) {
-      WARNING(UTIL_NAME ": The %s option requires string arguments.", ci->key);
-      return -EINVAL;
-    }
-  }
-
-  if (ci->values_num == 1 && ci->values[0].value.string &&
-      strlen(ci->values[0].value.string) == 0)
-    return 0;
-
-  for (int i = 0; i < ci->values_num; i++) {
-    size_t n;
-    _Bool grouped = 1;
-    char str[DATA_MAX_NAME_LEN];
-    unsigned cores[MAX_CORES] = {0};
-
-    if (cg_idx >= STATIC_ARRAY_SIZE(cgroups)) {
-      ERROR(UTIL_NAME
-            ": Configuration exceeds maximum number of cores: %" PRIsz,
-            STATIC_ARRAY_SIZE(cgroups));
-      ret = -EINVAL;
-      goto parse_error;
-    }
-    if ((ci->values[i].value.string == NULL) ||
-        (strlen(ci->values[i].value.string) == 0)) {
-      ERROR(UTIL_NAME ": Failed to parse parameters for %s option.", ci->key);
-      ret = -EINVAL;
-      goto parse_error;
-    }
-
-    ret = check_core_grouping(str, ci->values[i].value.string, sizeof(str),
-                              &grouped);
-    if (ret != 0) {
-      ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i,
-            ci->values[i].value.string);
-      goto parse_error;
-    }
-    n = str_list_to_nums(str, cores, STATIC_ARRAY_SIZE(cores));
-    if (n == 0) {
-      ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i,
-            ci->values[i].value.string);
-      ret = -EINVAL;
-      goto parse_error;
-    }
-
-    if (grouped) {
-      cgroups[cg_idx].desc = strdup(ci->values[i].value.string);
-      if (cgroups[cg_idx].desc == NULL) {
-        ERROR(UTIL_NAME ": Failed to allocate description.");
-        ret = -ENOMEM;
-        goto parse_error;
-      }
-
-      cgroups[cg_idx].cores = calloc(n, sizeof(*cgroups[cg_idx].cores));
-      if (cgroups[cg_idx].cores == NULL) {
-        ERROR(UTIL_NAME ": Failed to allocate cores for cgroup.");
-        ret = -ENOMEM;
-        goto parse_error;
-      }
-
-      for (size_t j = 0; j < n; j++)
-        cgroups[cg_idx].cores[j] = cores[j];
-
-      cgroups[cg_idx].num_cores = n;
-      cg_idx++;
-    } else {
-      for (size_t j = 0; j < n && cg_idx < STATIC_ARRAY_SIZE(cgroups); j++) {
-        char desc[DATA_MAX_NAME_LEN];
-        snprintf(desc, sizeof(desc), "%u", cores[j]);
-
-        cgroups[cg_idx].desc = strdup(desc);
-        if (cgroups[cg_idx].desc == NULL) {
-          ERROR(UTIL_NAME ": Failed to allocate desc for core %u.", cores[j]);
-          ret = -ENOMEM;
-          goto parse_error;
-        }
-
-        cgroups[cg_idx].cores = calloc(1, sizeof(*(cgroups[cg_idx].cores)));
-        if (cgroups[cg_idx].cores == NULL) {
-          ERROR(UTIL_NAME ": Failed to allocate cgroup for core %u.", cores[j]);
-          ret = -ENOMEM;
-          goto parse_error;
-        }
-        cgroups[cg_idx].num_cores = 1;
-        cgroups[cg_idx].cores[0] = cores[j];
-        cg_idx++;
-      }
-    }
-  }
-
-  cgl->cgroups = calloc(cg_idx, sizeof(*cgl->cgroups));
-  if (cgl->cgroups == NULL) {
-    ERROR(UTIL_NAME ": Failed to allocate core groups.");
-    ret = -ENOMEM;
-    goto parse_error;
-  }
-
-  cgl->num_cgroups = cg_idx;
-  for (size_t i = 0; i < cg_idx; i++)
-    cgl->cgroups[i] = cgroups[i];
-
-  return 0;
-
-parse_error:
-
-  cg_idx = 0;
-  while (cg_idx < STATIC_ARRAY_SIZE(cgroups) && cgroups[cg_idx].desc != NULL) {
-    sfree(cgroups[cg_idx].desc);
-    sfree(cgroups[cg_idx].cores);
-    cg_idx++;
-  }
-  return ret;
-}
-
-int config_cores_default(int num_cores, core_groups_list_t *cgl) {
-  if (cgl == NULL || num_cores < 0 || num_cores > MAX_CORES)
-    return -EINVAL;
-
-  cgl->cgroups = calloc(num_cores, sizeof(*(cgl->cgroups)));
-  if (cgl->cgroups == NULL) {
-    ERROR(UTIL_NAME ": Failed to allocate memory for core groups.");
-    return -ENOMEM;
-  }
-  cgl->num_cgroups = num_cores;
-
-  for (int i = 0; i < num_cores; i++) {
-    char desc[DATA_MAX_NAME_LEN];
-    snprintf(desc, sizeof(desc), "%d", i);
-
-    cgl->cgroups[i].cores = calloc(1, sizeof(*(cgl->cgroups[i].cores)));
-    if (cgl->cgroups[i].cores == NULL) {
-      ERROR(UTIL_NAME ": Failed to allocate default cores for cgroup %d.", i);
-      config_cores_cleanup(cgl);
-      return -ENOMEM;
-    }
-    cgl->cgroups[i].num_cores = 1;
-    cgl->cgroups[i].cores[0] = i;
-
-    cgl->cgroups[i].desc = strdup(desc);
-    if (cgl->cgroups[i].desc == NULL) {
-      ERROR(UTIL_NAME ": Failed to allocate description for cgroup %d.", i);
-      config_cores_cleanup(cgl);
-      return -ENOMEM;
-    }
-  }
-  return 0;
-}
-
-void config_cores_cleanup(core_groups_list_t *cgl) {
-  if (cgl == NULL)
-    return;
-  for (size_t i = 0; i < cgl->num_cgroups; i++) {
-    sfree(cgl->cgroups[i].desc);
-    sfree(cgl->cgroups[i].cores);
-  }
-  sfree(cgl->cgroups);
-  cgl->num_cgroups = 0;
-}
-
-int config_cores_cmp_cgroups(const core_group_t *cg_a,
-                             const core_group_t *cg_b) {
-  size_t found = 0;
-
-  assert(cg_a != NULL);
-  assert(cg_b != NULL);
-
-  const size_t sz_a = cg_a->num_cores;
-  const size_t sz_b = cg_b->num_cores;
-  const unsigned *tab_a = cg_a->cores;
-  const unsigned *tab_b = cg_b->cores;
-
-  for (size_t i = 0; i < sz_a; i++)
-    if (is_in_list(tab_a[i], tab_b, sz_b))
-      found++;
-
-  /* if no cores are the same */
-  if (!found)
-    return 0;
-  /* if group contains same cores */
-  if (sz_a == sz_b && sz_b == found)
-    return 1;
-  /* if not all cores are the same */
-  return -1;
-}
diff --git a/src/utils_config_cores.h b/src/utils_config_cores.h
deleted file mode 100644 (file)
index e22cbcf..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/**
- * collectd - src/utils_config_cores.h
- *
- * Copyright(c) 2018 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- *   Kamil Wiatrowski <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 *cores;
-  size_t num_cores;
-};
-typedef struct core_group_s core_group_t;
-
-struct core_groups_list_s {
-  core_group_t *cgroups;
-  size_t num_cgroups;
-};
-typedef struct core_groups_list_s core_groups_list_t;
-
-/*
- * NAME
- *   config_cores_parse
- *
- * DESCRIPTION
- *   Convert strings from config item into list of core groups.
- *
- * PARAMETERS
- *   `ci'      Pointer to config item.
- *   `cgl'     Pointer to core groups list to be filled.
- *
- * RETURN VALUE
- *    Zero upon success or non-zero if an error occurred.
- *
- * NOTES
- *    In case of an error, *cgl is not modified.
- *    Numbers can be in decimal or hexadecimal format.
- *    The memory allocated for *cgroups in list needs to be freed
- *    with config_cores_cleanup.
- *
- * EXAMPLES
- *    If config is "0-3" "[4-15]" it means that cores 0-3 are aggregated
- *    into one group and cores 4 to 15 are stored individualily in
- *    separate groups. Examples of allowed formats:
- *    "0,3,4" "10-15" - cores collected into two groups
- *    "0" "0x3" "7" - 3 cores, each in individual group
- *    "[32-63]" - 32 cores, each in individual group
- *
- *    For empty string "" *cgl is not modified and zero is returned.
- */
-int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl);
-
-/*
- * NAME
- *   config_cores_default
- *
- * DESCRIPTION
- *   Set number of cores starting from zero into individual
- *   core groups in *cgl list.
- *
- * PARAMETERS
- *   `num_cores'  Number of cores to be configured.
- *   `cgl'        Pointer to core groups list.
- *
- * RETURN VALUE
- *    Zero upon success or non-zero if an error occurred.
- *
- * NOTES
- *    The memory allocated for *cgroups in list needs to be freed
- *    with config_cores_cleanup. In case of error the memory is
- *    freed by the function itself.
- */
-int config_cores_default(int num_cores, core_groups_list_t *cgl);
-
-/*
- * NAME
- *   config_cores_cleanup
- *
- * DESCRIPTION
- *   Free the memory allocated for cgroups and set
- *   num_cgroups to zero.
- *
- * PARAMETERS
- *   `cgl'     Pointer to core groups list.
- */
-void config_cores_cleanup(core_groups_list_t *cgl);
-
-/*
- * NAME
- *   config_cores_cmp_cgroups
- *
- * DESCRIPTION
- *   Function to compare cores in 2 core groups.
- *
- * PARAMETERS
- *   `cg_a'      Pointer to core group a.
- *   `cg_b'      Pointer to core group b.
- *
- * RETURN VALUE
- *    1 if both groups contain the same cores
- *    0 if none of their cores match
- *    -1 if some but not all cores match
- */
-int config_cores_cmp_cgroups(const core_group_t *cg_a,
-                             const core_group_t *cg_b);
-
-#endif /* UTILS_CONFIG_CORES_H */
diff --git a/src/utils_config_cores_test.c b/src/utils_config_cores_test.c
deleted file mode 100644 (file)
index 2c6f5b6..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-/**
- * collectd - src/utils_config_cores_test.c
- *
- * Copyright(c) 2018 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- *   Kamil Wiatrowski <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;
-}
diff --git a/src/utils_crc32.c b/src/utils_crc32.c
deleted file mode 100644 (file)
index afc566a..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
- *  code or tables extracted from it, as desired without restriction.
- *
- *  First, the polynomial itself and its table of feedback terms.  The
- *  polynomial is
- *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
- *
- *  Note that we take it "backwards" and put the highest-order term in
- *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
- *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
- *  the MSB being 1
- *
- *  Note that the usual hardware shift register implementation, which
- *  is what we're using (we're merely optimizing it by doing eight-bit
- *  chunks at a time) shifts bits into the lowest-order term.  In our
- *  implementation, that means shifting towards the right.  Why do we
- *  do it this way?  Because the calculated CRC must be transmitted in
- *  order from highest-order term to lowest-order term.  UARTs transmit
- *  characters in order from LSB to MSB.  By storing the CRC this way
- *  we hand it to the UART in the order low-byte to high-byte; the UART
- *  sends each low-bit to hight-bit; and the result is transmission bit
- *  by bit from highest- to lowest-order term without requiring any bit
- *  shuffling on our part.  Reception works similarly
- *
- *  The feedback terms table consists of 256, 32-bit entries.  Notes
- *
- *      The table can be generated at runtime if desired; code to do so
- *      is shown later.  It might not be obvious, but the feedback
- *      terms simply represent the results of eight shift/xor opera
- *      tions for all combinations of data and CRC register values
- *
- *      The values must be right-shifted by eight bits by the "updcrc
- *      logic; the shift must be unsigned (bring in zeroes).  On some
- *      hardware you could probably optimize the shift in assembler by
- *      using byte-swap instructions
- *      polynomial $edb88320
- */
-
-#include <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;
-}
diff --git a/src/utils_crc32.h b/src/utils_crc32.h
deleted file mode 100644 (file)
index 8e2c212..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * collectd - src/utils_crc32.h
- * Copyright (C) 2014       Pierre-Yves Ritschard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Pierre-Yves Ritschard <pyr at spootnik.org>
- */
-
-#ifndef UTILS_CRC32_H
-#define UTILS_CRC32_H 1
-
-uint32_t crc32_buffer(const unsigned char *, size_t);
-
-#endif
diff --git a/src/utils_curl_stats.c b/src/utils_curl_stats.c
deleted file mode 100644 (file)
index 2a1d9de..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/**
- * collectd - src/utils_curl_stats.c
- * Copyright (C) 2015       Sebastian 'tokkee' Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Sebastian Harl <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 */
diff --git a/src/utils_curl_stats.h b/src/utils_curl_stats.h
deleted file mode 100644 (file)
index 3f83aab..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * collectd - src/utils_curl_stats.h
- * Copyright (C) 2015       Sebastian 'tokkee' Harl
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Sebastian Harl <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 */
diff --git a/src/utils_db_query.c b/src/utils_db_query.c
deleted file mode 100644 (file)
index 41f40d9..0000000
+++ /dev/null
@@ -1,1061 +0,0 @@
-/**
- * collectd - src/utils_db_query.c
- * Copyright (C) 2008,2009  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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;
-
-  cdtime_t interval;
-
-  udb_result_preparation_area_t *result_prep_areas;
-}; /* }}} */
-
-/*
- * Config Private functions
- */
-static int udb_config_set_string(char **ret_string, /* {{{ */
-                                 oconfig_item_t *ci) {
-  char *string;
-
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("db query utils: The `%s' config option "
-            "needs exactly one string argument.",
-            ci->key);
-    return -1;
-  }
-
-  string = strdup(ci->values[0].value.string);
-  if (string == NULL) {
-    ERROR("db query utils: strdup failed.");
-    return -1;
-  }
-
-  if (*ret_string != NULL)
-    free(*ret_string);
-  *ret_string = string;
-
-  return 0;
-} /* }}} int udb_config_set_string */
-
-static int udb_config_add_string(char ***ret_array, /* {{{ */
-                                 size_t *ret_array_len, oconfig_item_t *ci) {
-  char **array;
-  size_t array_len;
-
-  if (ci->values_num < 1) {
-    WARNING("db query utils: The `%s' config option "
-            "needs at least one argument.",
-            ci->key);
-    return -1;
-  }
-
-  for (int i = 0; i < ci->values_num; i++) {
-    if (ci->values[i].type != OCONFIG_TYPE_STRING) {
-      WARNING("db query utils: Argument %i to the `%s' option "
-              "is not a string.",
-              i + 1, ci->key);
-      return -1;
-    }
-  }
-
-  array_len = *ret_array_len;
-  array = realloc(*ret_array, sizeof(char *) * (array_len + ci->values_num));
-  if (array == NULL) {
-    ERROR("db query utils: realloc failed.");
-    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) {
-      ERROR("db query utils: 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) {
-  double tmp;
-
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    WARNING("db query utils: The `%s' config option "
-            "needs exactly one numeric argument.",
-            ci->key);
-    return -1;
-  }
-
-  tmp = ci->values[0].value.number;
-  if ((tmp < 0.0) || (tmp > ((double)UINT_MAX)))
-    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) {
-    ERROR("db query utils: 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)) {
-      ERROR("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
-            value_str, DS_TYPE_TO_STRING(r_area->ds->ds[i].type));
-      errno = EINVAL;
-      free(vl.values);
-      return -1;
-    }
-  }
-
-  if (q_area->interval > 0)
-    vl.interval = q_area->interval;
-
-  sstrncpy(vl.host, q_area->host, sizeof(vl.host));
-  sstrncpy(vl.plugin, q_area->plugin, sizeof(vl.plugin));
-  sstrncpy(vl.type, r->type, sizeof(vl.type));
-
-  /* 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) {
-        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) {
-        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) {
-      ERROR("db query utils:: meta_data_create failed.");
-      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) {
-        ERROR("db query utils:: meta_data_add_string failed.");
-        meta_data_destroy(vl.meta);
-        vl.meta = NULL;
-        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;
-
-#define BAIL_OUT(status)                                                       \
-  prep_area->ds = NULL;                                                        \
-  sfree(prep_area->instances_pos);                                             \
-  sfree(prep_area->values_pos);                                                \
-  sfree(prep_area->metadata_pos);                                              \
-  sfree(prep_area->instances_buffer);                                          \
-  sfree(prep_area->values_buffer);                                             \
-  sfree(prep_area->metadata_buffer);                                           \
-  return (status)
-
-  /* Make sure previous preparations are cleaned up. */
-  udb_result_finish_result(r, prep_area);
-  prep_area->instances_pos = NULL;
-  prep_area->values_pos = NULL;
-  prep_area->metadata_pos = NULL;
-
-  /* Read `ds' and check number of values {{{ */
-  prep_area->ds = plugin_get_ds(r->type);
-  if (prep_area->ds == NULL) {
-    ERROR("db query utils: udb_result_prepare_result: Type `%s' is not "
-          "known by the daemon. See types.db(5) for details.",
-          r->type);
-    BAIL_OUT(-1);
-  }
-
-  if (prep_area->ds->ds_num != r->values_num) {
-    ERROR("db query utils: udb_result_prepare_result: The type `%s' "
-          "requires exactly %zu value%s, but the configuration specifies %zu.",
-          r->type, prep_area->ds->ds_num,
-          (prep_area->ds->ds_num == 1) ? "" : "s", r->values_num);
-    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) {
-      ERROR("db query utils: 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) {
-      ERROR("db query utils: 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) {
-    ERROR("db query utils: 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) {
-    ERROR("db query utils: 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) {
-    ERROR("db query utils: 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) {
-    ERROR("db query utils: 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) {
-      ERROR("db query utils: 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) {
-      ERROR("db query utils: 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) {
-      ERROR("db query utils: 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) {
-    WARNING("db query utils: The `Result' block doesn't accept "
-            "any arguments. Ignoring %i argument%s.",
-            ci->values_num, (ci->values_num == 1) ? "" : "s");
-  }
-
-  r = calloc(1, sizeof(*r));
-  if (r == NULL) {
-    ERROR("db query utils: calloc failed.");
-    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 = udb_config_set_string(&r->type, child);
-    else if (strcasecmp("InstancePrefix", child->key) == 0)
-      status = udb_config_set_string(&r->instance_prefix, child);
-    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 {
-      WARNING("db query utils: 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) {
-      WARNING("db query utils: `Type' not given for "
-              "result in query `%s'",
-              query_name);
-      status = -1;
-    }
-    if (r->values == NULL) {
-      WARNING("db query utils: `ValuesFrom' not given for "
-              "result in query `%s'",
-              query_name);
-      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)) {
-    WARNING("db query utils: The `Query' block "
-            "needs exactly one string argument.");
-    return -1;
-  }
-
-  q = calloc(1, sizeof(*q));
-  if (q == NULL) {
-    ERROR("db query utils: 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 = udb_config_set_string(&q->name, ci);
-  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 = udb_config_set_string(&q->statement, child);
-    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 = udb_config_set_string(&q->plugin_instance_from, child);
-
-    /* Call custom callbacks */
-    else if (cb != NULL) {
-      status = (*cb)(q, child);
-      if (status != 0) {
-        WARNING("db query utils: The configuration callback failed "
-                "to handle `%s'.",
-                child->key);
-      }
-    } else {
-      WARNING("db query utils: 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) {
-      WARNING("db query utils: Query `%s': No `Statement' given.", q->name);
-      status = -1;
-    }
-    if (q->results == NULL) {
-      WARNING("db query utils: Query `%s': No (valid) `Result' block given.",
-              q->name);
-      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) {
-      ERROR("db query utils: 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)) {
-    ERROR("db query utils: 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) {
-      ERROR("db query utils: 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) {
-    ERROR("db query utils: Cannot find query `%s'. Make sure the <Query> "
-          "block is above the database definition!",
-          name);
-    return -ENOENT;
-  } else {
-    DEBUG("db query utils: 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)) {
-    ERROR("db query utils: udb_query_pick_from_list: "
-          "Invalid argument.");
-    return -EINVAL;
-  }
-
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    ERROR("db query utils: The `%s' config option "
-          "needs exactly one string argument.",
-          ci->key);
-    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);
-
-  prep_area->interval = 0;
-
-  for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
-       r = r->next, r_area = r_area->next) {
-    /* this may happen during error conditions of the caller */
-    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)) {
-    ERROR("db query utils: Query `%s': Query is not prepared; "
-          "can't handle result.",
-          q->name);
-    return -EINVAL;
-  }
-
-#if defined(COLLECT_DEBUG) && COLLECT_DEBUG /* {{{ */
-  do {
-    for (size_t i = 0; i < prep_area->column_num; i++) {
-      DEBUG("db query utils: udb_query_handle_result (%s, %s): "
-            "column[%zu] = %s;",
-            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) {
-    ERROR("db query utils: 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, cdtime_t interval) {
-  udb_result_preparation_area_t *r_area;
-  udb_result_t *r;
-  int status;
-
-  if ((q == NULL) || (prep_area == NULL))
-    return -EINVAL;
-
-  udb_query_finish_result(q, prep_area);
-
-  prep_area->column_num = column_num;
-  prep_area->host = strdup(host);
-  prep_area->plugin = strdup(plugin);
-  prep_area->db_name = strdup(db_name);
-
-  prep_area->interval = interval;
-
-  if ((prep_area->host == NULL) || (prep_area->plugin == NULL) ||
-      (prep_area->db_name == NULL)) {
-    ERROR("db query utils: Query `%s': Prepare failed: Out of memory.",
-          q->name);
-    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("db query utils: udb_query_prepare_result: "
-            "query = %s; column[%zu] = %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) {
-      ERROR("db query utils: 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) {
-      ERROR("db query utils: Query `%s': Invalid number of result "
-            "preparation areas.",
-            q->name);
-      udb_query_finish_result(q, prep_area);
-      return -EINVAL;
-    }
-
-    status = udb_result_prepare_result(r, r_area, column_names, column_num);
-    if (status != 0) {
-      udb_query_finish_result(q, prep_area);
-      return status;
-    }
-  }
-
-  return 0;
-} /* }}} int udb_query_prepare_result */
-
-udb_query_preparation_area_t *
-udb_query_allocate_preparation_area(udb_query_t *q) /* {{{ */
-{
-  udb_query_preparation_area_t *q_area;
-  udb_result_preparation_area_t **next_r_area;
-  udb_result_t *r;
-
-  q_area = calloc(1, sizeof(*q_area));
-  if (q_area == NULL)
-    return NULL;
-
-  next_r_area = &q_area->result_prep_areas;
-  for (r = q->results; r != NULL; r = r->next) {
-    udb_result_preparation_area_t *r_area;
-
-    r_area = calloc(1, sizeof(*r_area));
-    if (r_area == NULL) {
-      udb_result_preparation_area_t *a = q_area->result_prep_areas;
-
-      while (a != NULL) {
-        udb_result_preparation_area_t *next = a->next;
-        sfree(a);
-        a = next;
-      }
-
-      free(q_area);
-      return NULL;
-    }
-
-    *next_r_area = r_area;
-    next_r_area = &r_area->next;
-  }
-
-  return q_area;
-} /* }}} udb_query_preparation_area_t *udb_query_allocate_preparation_area */
-
-void udb_query_delete_preparation_area(
-    udb_query_preparation_area_t *q_area) /* {{{ */
-{
-  udb_result_preparation_area_t *r_area;
-
-  if (q_area == NULL)
-    return;
-
-  r_area = q_area->result_prep_areas;
-  while (r_area != NULL) {
-    udb_result_preparation_area_t *area = r_area;
-
-    r_area = r_area->next;
-
-    sfree(area->instances_pos);
-    sfree(area->values_pos);
-    sfree(area->instances_buffer);
-    sfree(area->values_buffer);
-    free(area);
-  }
-
-  sfree(q_area->host);
-  sfree(q_area->plugin);
-  sfree(q_area->db_name);
-
-  free(q_area);
-} /* }}} void udb_query_delete_preparation_area */
diff --git a/src/utils_db_query.h b/src/utils_db_query.h
deleted file mode 100644 (file)
index 4d6129a..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * collectd - src/utils_db_query.h
- * Copyright (C) 2008,2009  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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, cdtime_t interval);
-int udb_query_handle_result(udb_query_t const *q,
-                            udb_query_preparation_area_t *prep_area,
-                            char **column_values);
-void udb_query_finish_result(udb_query_t const *q,
-                             udb_query_preparation_area_t *prep_area);
-
-udb_query_preparation_area_t *
-udb_query_allocate_preparation_area(udb_query_t *q);
-void udb_query_delete_preparation_area(udb_query_preparation_area_t *q_area);
-
-#endif /* UTILS_DB_QUERY_H */
diff --git a/src/utils_dns.c b/src/utils_dns.c
deleted file mode 100644 (file)
index e7e04f7..0000000
+++ /dev/null
@@ -1,1171 +0,0 @@
-/*
- * collectd - src/utils_dns.c
- * Copyright (C) 2006       Florian octo Forster
- * Copyright (C) 2002       The Measurement Factory, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. Neither the name of The Measurement Factory nor the names of its
- *    contributors may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Authors:
- *   The Measurement Factory, Inc. <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 = NULL;
-#endif
-
-static ip_list_t *IgnoreList = NULL;
-
-#if HAVE_PCAP_H
-static void (*Callback)(const rfc1035_header_t *) = NULL;
-
-static int query_count_intvl = 0;
-static int query_count_total = 0;
-#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 = 0;
-  if (loop_detect > 2)
-    return 4; /* compression loop */
-  if (ns == 0)
-    return 4; /* probably compression loop */
-  do {
-    if ((*off) >= ((off_t)sz))
-      break;
-    c = *(buf + (*off));
-    if (c > 191) {
-      /* blasted compression */
-      int rc;
-      unsigned short s;
-      off_t ptr;
-      memcpy(&s, buf + (*off), sizeof(s));
-      s = ntohs(s);
-      (*off) += sizeof(s);
-      /* Sanity check */
-      if ((*off) >= ((off_t)sz))
-        return 1; /* message too short */
-      ptr = s & 0x3FFF;
-      /* Make sure the pointer is inside this message */
-      if (ptr >= ((off_t)sz))
-        return 2; /* bad compression ptr */
-      if (ptr < DNS_MSG_HDR_SZ)
-        return 2; /* bad compression ptr */
-      loop_detect++;
-      rc = rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
-      loop_detect--;
-      return rc;
-    } else if (c > RFC1035_MAXLABELSZ) {
-      /*
-       * "(The 10 and 01 combinations are reserved for future use.)"
-       */
-      return 3; /* reserved label/compression flags */
-    } else {
-      (*off)++;
-      len = (size_t)c;
-      if (len == 0)
-        break;
-      if (len > (ns - 1))
-        len = ns - 1;
-      if ((*off) + len > sz)
-        return 4; /* message is too short */
-      if (no + len + 1 > ns)
-        return 5; /* qname would overflow name buffer */
-      memcpy(name + no, buf + (*off), len);
-      (*off) += len;
-      no += len;
-      *(name + (no++)) = '.';
-    }
-  } while (c > 0);
-  if (no > 0)
-    *(name + no - 1) = '\0';
-  /* make sure we didn't allow someone to overflow the name buffer */
-  assert(no <= ((off_t)ns));
-  return 0;
-}
-
-static int handle_dns(const char *buf, int len) {
-  rfc1035_header_t qh;
-  uint16_t us;
-  off_t offset;
-  char *t;
-  int status;
-
-  /* The DNS header is 12 bytes long */
-  if (len < DNS_MSG_HDR_SZ)
-    return 0;
-
-  memcpy(&us, buf + 0, 2);
-  qh.id = ntohs(us);
-
-  memcpy(&us, buf + 2, 2);
-  us = ntohs(us);
-  qh.qr = (us >> 15) & 0x01;
-  qh.opcode = (us >> 11) & 0x0F;
-  qh.aa = (us >> 10) & 0x01;
-  qh.tc = (us >> 9) & 0x01;
-  qh.rd = (us >> 8) & 0x01;
-  qh.ra = (us >> 7) & 0x01;
-  qh.z = (us >> 6) & 0x01;
-  qh.ad = (us >> 5) & 0x01;
-  qh.cd = (us >> 4) & 0x01;
-  qh.rcode = us & 0x0F;
-
-  memcpy(&us, buf + 4, 2);
-  qh.qdcount = ntohs(us);
-
-  memcpy(&us, buf + 6, 2);
-  qh.ancount = ntohs(us);
-
-  memcpy(&us, buf + 8, 2);
-  qh.nscount = ntohs(us);
-
-  memcpy(&us, buf + 10, 2);
-  qh.arcount = ntohs(us);
-
-  offset = DNS_MSG_HDR_SZ;
-  memset(qh.qname, '\0', MAX_QNAME_SZ);
-  status = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ);
-  if (status != 0) {
-    INFO("utils_dns: handle_dns: rfc1035NameUnpack failed "
-         "with status %i.",
-         status);
-    return 0;
-  }
-  if ('\0' == qh.qname[0])
-    sstrncpy(qh.qname, ".", sizeof(qh.qname));
-  while ((t = strchr(qh.qname, '\n')))
-    *t = ' ';
-  while ((t = strchr(qh.qname, '\r')))
-    *t = ' ';
-  for (t = qh.qname; *t; t++)
-    *t = tolower((int)*t);
-
-  memcpy(&us, buf + offset, 2);
-  qh.qtype = ntohs(us);
-  memcpy(&us, buf + offset + 2, 2);
-  qh.qclass = ntohs(us);
-
-  qh.length = (uint16_t)len;
-
-  if (Callback != NULL)
-    Callback(&qh);
-
-  return 1;
-}
-
-static int handle_udp(const struct udphdr *udp, int len) {
-  char buf[PCAP_SNAPLEN];
-  if ((ntohs(udp->UDP_DEST) != 53) && (ntohs(udp->UDP_SRC) != 53))
-    return 0;
-  memcpy(buf, udp + 1, len - sizeof(*udp));
-  if (0 == handle_dns(buf, len - sizeof(*udp)))
-    return 0;
-  return 1;
-}
-
-#if HAVE_IPV6
-static int handle_ipv6(struct ip6_hdr *ipv6, int len) {
-  char buf[PCAP_SNAPLEN];
-  unsigned int offset;
-  int nexthdr;
-
-  struct in6_addr c_src_addr;
-  uint16_t payload_len;
-
-  if (0 > len)
-    return 0;
-
-  offset = sizeof(struct ip6_hdr);
-  nexthdr = ipv6->ip6_nxt;
-  c_src_addr = ipv6->ip6_src;
-  payload_len = ntohs(ipv6->ip6_plen);
-
-  if (ignore_list_match(&c_src_addr))
-    return 0;
-
-  /* Parse extension headers. This only handles the standard headers, as
-   * defined in RFC 2460, correctly. Fragments are discarded. */
-  while ((IPPROTO_ROUTING == nexthdr)     /* routing header */
-         || (IPPROTO_HOPOPTS == nexthdr)  /* Hop-by-Hop options. */
-         || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */
-         || (IPPROTO_DSTOPTS == nexthdr)  /* destination options. */
-         || (IPPROTO_AH == nexthdr)       /* destination options. */
-         || (IPPROTO_ESP == nexthdr))     /* encapsulating security payload. */
-  {
-    struct ip6_ext ext_hdr;
-    uint16_t ext_hdr_len;
-
-    /* Catch broken packets */
-    if ((offset + sizeof(struct ip6_ext)) > (unsigned int)len)
-      return 0;
-
-    /* Cannot handle fragments. */
-    if (IPPROTO_FRAGMENT == nexthdr)
-      return 0;
-
-    memcpy(&ext_hdr, (char *)ipv6 + offset, sizeof(struct ip6_ext));
-    nexthdr = ext_hdr.ip6e_nxt;
-    ext_hdr_len = (8 * (ntohs(ext_hdr.ip6e_len) + 1));
-
-    /* This header is longer than the packets payload.. WTF? */
-    if (ext_hdr_len > payload_len)
-      return 0;
-
-    offset += ext_hdr_len;
-    payload_len -= ext_hdr_len;
-  } /* while */
-
-  /* Catch broken and empty packets */
-  if (((offset + payload_len) > (unsigned int)len) || (payload_len == 0) ||
-      (payload_len > PCAP_SNAPLEN))
-    return 0;
-
-  if (IPPROTO_UDP != nexthdr)
-    return 0;
-
-  memcpy(buf, (char *)ipv6 + offset, payload_len);
-  if (handle_udp((struct udphdr *)buf, payload_len) == 0)
-    return 0;
-
-  return 1; /* Success */
-} /* int handle_ipv6 */
-/* #endif HAVE_IPV6 */
-
-#else  /* if !HAVE_IPV6 */
-static int handle_ipv6(__attribute__((unused)) void *pkg,
-                       __attribute__((unused)) int len) {
-  return 0;
-}
-#endif /* !HAVE_IPV6 */
-
-static int handle_ip(const struct ip *ip, int len) {
-  char buf[PCAP_SNAPLEN];
-  int offset = ip->ip_hl << 2;
-  struct in6_addr c_src_addr;
-  struct in6_addr c_dst_addr;
-
-  if (ip->ip_v == 6)
-    return handle_ipv6((void *)ip, len);
-
-  in6_addr_from_buffer(&c_src_addr, &ip->ip_src.s_addr,
-                       sizeof(ip->ip_src.s_addr), AF_INET);
-  in6_addr_from_buffer(&c_dst_addr, &ip->ip_dst.s_addr,
-                       sizeof(ip->ip_dst.s_addr), AF_INET);
-  if (ignore_list_match(&c_src_addr))
-    return 0;
-  if (IPPROTO_UDP != ip->ip_p)
-    return 0;
-  memcpy(buf, ((char *)ip) + offset, len - offset);
-  if (0 == handle_udp((struct udphdr *)buf, len - offset))
-    return 0;
-  return 1;
-}
-
-#if HAVE_NET_IF_PPP_H
-static int handle_ppp(const u_char *pkt, int len) {
-  char buf[PCAP_SNAPLEN];
-  unsigned short us;
-  unsigned short proto;
-  if (len < 2)
-    return 0;
-  if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) {
-    pkt += 2; /* ACFC not used */
-    len -= 2;
-  }
-  if (len < 2)
-    return 0;
-  if (*pkt % 2) {
-    proto = *pkt; /* PFC is used */
-    pkt++;
-    len--;
-  } else {
-    memcpy(&us, pkt, sizeof(us));
-    proto = ntohs(us);
-    pkt += 2;
-    len -= 2;
-  }
-  if (ETHERTYPE_IP != proto && PPP_IP != proto)
-    return 0;
-  memcpy(buf, pkt, len);
-  return handle_ip((struct ip *)buf, len);
-}
-#endif /* HAVE_NET_IF_PPP_H */
-
-static int handle_null(const u_char *pkt, int len) {
-  unsigned int family;
-  memcpy(&family, pkt, sizeof(family));
-  if (AF_INET != family)
-    return 0;
-  return handle_ip((struct ip *)(pkt + 4), len - 4);
-}
-
-#ifdef DLT_LOOP
-static int handle_loop(const u_char *pkt, int len) {
-  unsigned int family;
-  memcpy(&family, pkt, sizeof(family));
-  if (AF_INET != ntohl(family))
-    return 0;
-  return handle_ip((struct ip *)(pkt + 4), len - 4);
-}
-
-#endif
-
-#ifdef DLT_RAW
-static int handle_raw(const u_char *pkt, int len) {
-  return handle_ip((struct ip *)pkt, len);
-}
-
-#endif
-
-static int handle_ether(const u_char *pkt, int len) {
-  char buf[PCAP_SNAPLEN];
-  struct ether_header *e = (void *)pkt;
-  unsigned short etype = ntohs(e->ether_type);
-  if (len < ETHER_HDR_LEN)
-    return 0;
-  pkt += ETHER_HDR_LEN;
-  len -= ETHER_HDR_LEN;
-  if (ETHERTYPE_8021Q == etype) {
-    etype = ntohs(*(unsigned short *)(pkt + 2));
-    pkt += 4;
-    len -= 4;
-  }
-  if ((ETHERTYPE_IP != etype) && (ETHERTYPE_IPV6 != etype))
-    return 0;
-  memcpy(buf, pkt, len);
-  if (ETHERTYPE_IPV6 == etype)
-    return handle_ipv6((void *)buf, len);
-  else
-    return handle_ip((struct ip *)buf, len);
-}
-
-#ifdef DLT_LINUX_SLL
-static int handle_linux_sll(const u_char *pkt, int len) {
-  struct sll_header {
-    uint16_t pkt_type;
-    uint16_t dev_type;
-    uint16_t addr_len;
-    uint8_t addr[8];
-    uint16_t proto_type;
-  } * hdr;
-  uint16_t etype;
-
-  if ((0 > len) || ((unsigned int)len < sizeof(struct sll_header)))
-    return 0;
-
-  hdr = (struct sll_header *)pkt;
-  pkt = (u_char *)(hdr + 1);
-  len -= sizeof(struct sll_header);
-
-  etype = ntohs(hdr->proto_type);
-
-  if ((ETHERTYPE_IP != etype) && (ETHERTYPE_IPV6 != etype))
-    return 0;
-
-  if (ETHERTYPE_IPV6 == etype)
-    return handle_ipv6((void *)pkt, len);
-  else
-    return handle_ip((struct ip *)pkt, len);
-}
-#endif /* DLT_LINUX_SLL */
-
-/* public function */
-void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr,
-                 const u_char *pkt) {
-  int status;
-
-  if (hdr->caplen < ETHER_HDR_LEN)
-    return;
-
-  switch (pcap_datalink(pcap_obj)) {
-  case DLT_EN10MB:
-    status = handle_ether(pkt, hdr->caplen);
-    break;
-#if HAVE_NET_IF_PPP_H
-  case DLT_PPP:
-    status = handle_ppp(pkt, hdr->caplen);
-    break;
-#endif
-#ifdef DLT_LOOP
-  case DLT_LOOP:
-    status = handle_loop(pkt, hdr->caplen);
-    break;
-#endif
-#ifdef DLT_RAW
-  case DLT_RAW:
-    status = handle_raw(pkt, hdr->caplen);
-    break;
-#endif
-#ifdef DLT_LINUX_SLL
-  case DLT_LINUX_SLL:
-    status = handle_linux_sll(pkt, hdr->caplen);
-    break;
-#endif
-  case DLT_NULL:
-    status = handle_null(pkt, hdr->caplen);
-    break;
-
-  default:
-    ERROR("handle_pcap: unsupported data link type %d",
-          pcap_datalink(pcap_obj));
-    status = 0;
-    break;
-  } /* switch (pcap_datalink(pcap_obj)) */
-
-  if (0 == status)
-    return;
-
-  query_count_intvl++;
-  query_count_total++;
-  last_ts = hdr->ts;
-}
-#endif /* HAVE_PCAP_H */
-
-const char *qtype_str(int t) {
-  static char buf[32];
-  switch (t) {
-#if (defined(__NAMESER)) && (__NAMESER >= 19991001)
-  case ns_t_a:
-    return "A";
-  case ns_t_ns:
-    return "NS";
-  case ns_t_md:
-    return "MD";
-  case ns_t_mf:
-    return "MF";
-  case ns_t_cname:
-    return "CNAME";
-  case ns_t_soa:
-    return "SOA";
-  case ns_t_mb:
-    return "MB";
-  case ns_t_mg:
-    return "MG";
-  case ns_t_mr:
-    return "MR";
-  case ns_t_null:
-    return "NULL";
-  case ns_t_wks:
-    return "WKS";
-  case ns_t_ptr:
-    return "PTR";
-  case ns_t_hinfo:
-    return "HINFO";
-  case ns_t_minfo:
-    return "MINFO";
-  case ns_t_mx:
-    return "MX";
-  case ns_t_txt:
-    return "TXT";
-  case ns_t_rp:
-    return "RP";
-  case ns_t_afsdb:
-    return "AFSDB";
-  case ns_t_x25:
-    return "X25";
-  case ns_t_isdn:
-    return "ISDN";
-  case ns_t_rt:
-    return "RT";
-  case ns_t_nsap:
-    return "NSAP";
-  case ns_t_nsap_ptr:
-    return "NSAP-PTR";
-  case ns_t_sig:
-    return "SIG";
-  case ns_t_key:
-    return "KEY";
-  case ns_t_px:
-    return "PX";
-  case ns_t_gpos:
-    return "GPOS";
-  case ns_t_aaaa:
-    return "AAAA";
-  case ns_t_loc:
-    return "LOC";
-  case ns_t_nxt:
-    return "NXT";
-  case ns_t_eid:
-    return "EID";
-  case ns_t_nimloc:
-    return "NIMLOC";
-  case ns_t_srv:
-    return "SRV";
-  case ns_t_atma:
-    return "ATMA";
-  case ns_t_naptr:
-    return "NAPTR";
-  case ns_t_opt:
-    return "OPT";
-#if __NAMESER >= 19991006
-  case ns_t_kx:
-    return "KX";
-  case ns_t_cert:
-    return "CERT";
-  case ns_t_a6:
-    return "A6";
-  case ns_t_dname:
-    return "DNAME";
-  case ns_t_sink:
-    return "SINK";
-  case ns_t_tsig:
-    return "TSIG";
-#endif
-#if __NAMESER >= 20090302
-  case ns_t_apl:
-    return "APL";
-  case ns_t_ds:
-    return "DS";
-  case ns_t_sshfp:
-    return "SSHFP";
-  case ns_t_ipseckey:
-    return "IPSECKEY";
-  case ns_t_rrsig:
-    return "RRSIG";
-  case ns_t_nsec:
-    return "NSEC";
-  case ns_t_dnskey:
-    return "DNSKEY";
-  case ns_t_dhcid:
-    return "DHCID";
-  case ns_t_nsec3:
-    return "NSEC3";
-  case ns_t_nsec3param:
-    return "NSEC3PARAM";
-  case ns_t_hip:
-    return "HIP";
-  case ns_t_spf:
-    return "SPF";
-  case ns_t_ixfr:
-    return "IXFR";
-#endif
-  case ns_t_axfr:
-    return "AXFR";
-  case ns_t_mailb:
-    return "MAILB";
-  case ns_t_maila:
-    return "MAILA";
-  case ns_t_any:
-    return "ANY";
-#if __NAMESER >= 19991006
-  case ns_t_zxfr:
-    return "ZXFR";
-#endif
-#if __NAMESER >= 20090302
-  case ns_t_dlv:
-    return "DLV";
-#endif
-/* #endif __NAMESER >= 19991001 */
-#elif (defined(__BIND)) && (__BIND >= 19950621)
-  case T_A:
-    return "A"; /* 1 ... */
-  case T_NS:
-    return "NS";
-  case T_MD:
-    return "MD";
-  case T_MF:
-    return "MF";
-  case T_CNAME:
-    return "CNAME";
-  case T_SOA:
-    return "SOA";
-  case T_MB:
-    return "MB";
-  case T_MG:
-    return "MG";
-  case T_MR:
-    return "MR";
-  case T_NULL:
-    return "NULL";
-  case T_WKS:
-    return "WKS";
-  case T_PTR:
-    return "PTR";
-  case T_HINFO:
-    return "HINFO";
-  case T_MINFO:
-    return "MINFO";
-  case T_MX:
-    return "MX";
-  case T_TXT:
-    return "TXT";
-  case T_RP:
-    return "RP";
-  case T_AFSDB:
-    return "AFSDB";
-  case T_X25:
-    return "X25";
-  case T_ISDN:
-    return "ISDN";
-  case T_RT:
-    return "RT";
-  case T_NSAP:
-    return "NSAP";
-  case T_NSAP_PTR:
-    return "NSAP_PTR";
-  case T_SIG:
-    return "SIG";
-  case T_KEY:
-    return "KEY";
-  case T_PX:
-    return "PX";
-  case T_GPOS:
-    return "GPOS";
-  case T_AAAA:
-    return "AAAA";
-  case T_LOC:
-    return "LOC";
-  case T_NXT:
-    return "NXT";
-  case T_EID:
-    return "EID";
-  case T_NIMLOC:
-    return "NIMLOC";
-  case T_SRV:
-    return "SRV";
-  case T_ATMA:
-    return "ATMA";
-  case T_NAPTR:
-    return "NAPTR"; /* ... 35 */
-#if (__BIND >= 19960801)
-  case T_KX:
-    return "KX"; /* 36 ... */
-  case T_CERT:
-    return "CERT";
-  case T_A6:
-    return "A6";
-  case T_DNAME:
-    return "DNAME";
-  case T_SINK:
-    return "SINK";
-  case T_OPT:
-    return "OPT";
-  case T_APL:
-    return "APL";
-  case T_DS:
-    return "DS";
-  case T_SSHFP:
-    return "SSHFP";
-  case T_RRSIG:
-    return "RRSIG";
-  case T_NSEC:
-    return "NSEC";
-  case T_DNSKEY:
-    return "DNSKEY"; /* ... 48 */
-  case T_TKEY:
-    return "TKEY"; /* 249 */
-#endif /* __BIND >= 19960801 */
-  case T_TSIG:
-    return "TSIG"; /* 250 ... */
-  case T_IXFR:
-    return "IXFR";
-  case T_AXFR:
-    return "AXFR";
-  case T_MAILB:
-    return "MAILB";
-  case T_MAILA:
-    return "MAILA";
-  case T_ANY:
-    return "ANY"; /* ... 255 */
-#endif /* __BIND >= 19950621 */
-  default:
-    snprintf(buf, sizeof(buf), "#%i", t);
-    return buf;
-  } /* switch (t) */
-}
-
-const char *opcode_str(int o) {
-  static char buf[30];
-  switch (o) {
-  case 0:
-    return "Query";
-  case 1:
-    return "Iquery";
-  case 2:
-    return "Status";
-  case 4:
-    return "Notify";
-  case 5:
-    return "Update";
-  default:
-    snprintf(buf, sizeof(buf), "Opcode%d", o);
-    return buf;
-  }
-}
-
-const char *rcode_str(int rcode) {
-  static char buf[32];
-  switch (rcode) {
-#if (defined(__NAMESER)) && (__NAMESER >= 19991006)
-  case ns_r_noerror:
-    return "NOERROR";
-  case ns_r_formerr:
-    return "FORMERR";
-  case ns_r_servfail:
-    return "SERVFAIL";
-  case ns_r_nxdomain:
-    return "NXDOMAIN";
-  case ns_r_notimpl:
-    return "NOTIMPL";
-  case ns_r_refused:
-    return "REFUSED";
-  case ns_r_yxdomain:
-    return "YXDOMAIN";
-  case ns_r_yxrrset:
-    return "YXRRSET";
-  case ns_r_nxrrset:
-    return "NXRRSET";
-  case ns_r_notauth:
-    return "NOTAUTH";
-  case ns_r_notzone:
-    return "NOTZONE";
-  case ns_r_max:
-    return "MAX";
-  case ns_r_badsig:
-    return "BADSIG";
-  case ns_r_badkey:
-    return "BADKEY";
-  case ns_r_badtime:
-    return "BADTIME";
-/* #endif __NAMESER >= 19991006 */
-#elif (defined(__BIND)) && (__BIND >= 19950621)
-  case NOERROR:
-    return "NOERROR";
-  case FORMERR:
-    return "FORMERR";
-  case SERVFAIL:
-    return "SERVFAIL";
-  case NXDOMAIN:
-    return "NXDOMAIN";
-  case NOTIMP:
-    return "NOTIMP";
-  case REFUSED:
-    return "REFUSED";
-#if defined(YXDOMAIN) && defined(NXRRSET)
-  case YXDOMAIN:
-    return "YXDOMAIN";
-  case YXRRSET:
-    return "YXRRSET";
-  case NXRRSET:
-    return "NXRRSET";
-  case NOTAUTH:
-    return "NOTAUTH";
-  case NOTZONE:
-    return "NOTZONE";
-#endif /* RFC2136 rcodes */
-#endif /* __BIND >= 19950621 */
-  default:
-    snprintf(buf, sizeof(buf), "RCode%i", rcode);
-    return buf;
-  }
-} /* const char *rcode_str (int rcode) */
-
-#if 0
-static int
-main(int argc, char *argv[])
-{
-    char errbuf[PCAP_ERRBUF_SIZE];
-    int x;
-    struct stat sb;
-    int readfile_state = 0;
-    struct bpf_program fp;
-
-    port53 = htons(53);
-    SubReport = Sources_report;
-    ignore_addr.s_addr = 0;
-    progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]);
-    srandom(time(NULL));
-    ResetCounters();
-
-    while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) {
-       switch (x) {
-       case 'a':
-           anon_flag = 1;
-           break;
-       case 's':
-           sld_flag = 1;
-           break;
-       case 't':
-           nld_flag = 1;
-           break;
-       case 'p':
-           promisc_flag = 0;
-           break;
-       case 'b':
-           bpf_program_str = strdup(optarg);
-           break;
-       case 'i':
-           ignore_addr.s_addr = inet_addr(optarg);
-           break;
-       case 'f':
-           set_filter(optarg);
-           break;
-       default:
-           usage();
-           break;
-       }
-    }
-    argc -= optind;
-    argv += optind;
-
-    if (argc < 1)
-       usage();
-    device = strdup(argv[0]);
-
-    if (0 == stat(device, &sb))
-       readfile_state = 1;
-    if (readfile_state) {
-       pcap_obj = pcap_open_offline(device, errbuf);
-    } else {
-       pcap_obj = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
-    }
-    if (NULL == pcap_obj) {
-       fprintf(stderr, "pcap_open_*: %s\n", errbuf);
-       exit(1);
-    }
-
-    if (0 == isatty(1)) {
-       if (0 == readfile_state) {
-           fprintf(stderr, "Non-interactive mode requires savefile argument\n");
-           exit(1);
-       }
-       interactive = 0;
-       print_func = printf;
-    }
-
-    memset(&fp, '\0', sizeof(fp));
-    x = pcap_compile(pcap_obj, &fp, bpf_program_str, 1, 0);
-    if (x < 0) {
-       fprintf(stderr, "pcap_compile failed\n");
-       exit(1);
-    }
-    x = pcap_setfilter(pcap_obj, &fp);
-    if (x < 0) {
-       fprintf(stderr, "pcap_setfilter failed\n");
-       exit(1);
-    }
-
-    /*
-     * non-blocking call added for Mac OS X bugfix.  Sent by Max Horn.
-     * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html
-     */
-    x = pcap_setnonblock(pcap_obj, 1, errbuf);
-    if (x < 0) {
-       fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf);
-       exit(1);
-    }
-
-    switch (pcap_datalink(pcap_obj)) {
-    case DLT_EN10MB:
-       handle_datalink = handle_ether;
-       break;
-#if HAVE_NET_IF_PPP_H
-    case DLT_PPP:
-       handle_datalink = handle_ppp;
-       break;
-#endif
-#ifdef DLT_LOOP
-    case DLT_LOOP:
-       handle_datalink = handle_loop;
-       break;
-#endif
-#ifdef DLT_RAW
-    case DLT_RAW:
-       handle_datalink = handle_raw;
-       break;
-#endif
-    case DLT_NULL:
-       handle_datalink = handle_null;
-       break;
-    default:
-       fprintf(stderr, "unsupported data link type %d\n",
-           pcap_datalink(pcap_obj));
-       return 1;
-       break;
-    }
-    if (interactive) {
-       init_curses();
-       while (0 == Quit) {
-           if (readfile_state < 2) {
-               /*
-                * On some OSes select() might return 0 even when
-                * there are packets to process.  Thus, we always
-                * ignore its return value and just call pcap_dispatch()
-                * anyway.
-                */
-               if (0 == readfile_state)        /* interactive */
-                   pcap_select(pcap_obj, 1, 0);
-               x = pcap_dispatch(pcap_obj, 50, handle_pcap, NULL);
-           }
-           if (0 == x && 1 == readfile_state) {
-               /* block on keyboard until user quits */
-               readfile_state++;
-               nodelay(w, 0);
-           }
-           keyboard();
-           cron_pre();
-           report();
-           cron_post();
-       }
-       endwin();               /* klin, Thu Nov 28 08:56:51 2002 */
-    } else {
-       while (pcap_dispatch(pcap_obj, 50, handle_pcap, NULL))
-               (void) 0;
-       cron_pre();
-       Sources_report(); print_func("\n");
-       Destinatioreport(); print_func("\n");
-       Qtypes_report(); print_func("\n");
-       Opcodes_report(); print_func("\n");
-       Tld_report(); print_func("\n");
-       Sld_report(); print_func("\n");
-       Nld_report(); print_func("\n");
-       SldBySource_report();
-    }
-
-    pcap_close(pcap_obj);
-    return 0;
-} /* static int main(int argc, char *argv[]) */
-#endif
diff --git a/src/utils_dns.h b/src/utils_dns.h
deleted file mode 100644 (file)
index 9d9b75f..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * collectd - src/utils_dns.h
- * Copyright (C) 2006       Florian octo Forster
- * Copyright (C) 2002       The Measurement Factory, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- * 3. Neither the name of The Measurement Factory nor the names of its
- *    contributors may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Authors:
- *   The Measurement Factory, Inc. <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 */
diff --git a/src/utils_dpdk.c b/src/utils_dpdk.c
deleted file mode 100644 (file)
index fbdcaf8..0000000
+++ /dev/null
@@ -1,896 +0,0 @@
-/*
- * collectd - src/utils_dpdk.c
- * MIT License
- *
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- *   Maryam Tahhan <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"
-
-#define DPDK_DEFAULT_RTE_CONFIG "/var/run/.rte_config"
-#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) {
-        snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN,
-                 "/var/run/.%s_config", prefix);
-        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);
-
-  char errbuf[ERR_BUF_SIZE];
-
-  int fd = shm_open(name, O_CREAT | O_TRUNC | O_RDWR, 0666);
-  if (fd < 0) {
-    WARNING("dpdk_shm_init: Failed to open %s as SHM:%s", name,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-    *map = NULL;
-    return -1;
-  }
-
-  int ret = ftruncate(fd, size);
-  if (ret != 0) {
-    WARNING("dpdk_shm_init: Failed to resize SHM:%s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-    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",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-    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);
-  char errbuf[ERR_BUF_SIZE];
-
-  /*
-   * Call shm_unlink first, as 'name' might be no longer accessible after munmap
-   */
-  if (shm_unlink(name))
-    ERROR("shm_unlink failure %s", sstrerror(errno, errbuf, sizeof(errbuf)));
-
-  if (map != NULL) {
-    if (munmap(map, size))
-      ERROR("munmap failure %s", sstrerror(errno, errbuf, sizeof(errbuf)));
-  }
-}
-
-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;
-  char errbuf[ERR_BUF_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",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    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",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    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) {
-  char errbuf[ERR_BUF_SIZE];
-  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",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    return -1;
-  }
-
-  int pipe0_flags = fcntl(phc->pipes[0], F_GETFL, 0);
-  int pipe1_flags = fcntl(phc->pipes[1], F_GETFL, 0);
-  if (pipe0_flags == -1 || pipe1_flags == -1) {
-    WARNING("dpdk_helper_spawn: error setting up pipe flags: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-  }
-  int pipe0_err = fcntl(phc->pipes[0], F_SETFL, pipe1_flags | O_NONBLOCK);
-  int pipe1_err = fcntl(phc->pipes[1], F_SETFL, pipe0_flags | O_NONBLOCK);
-  if (pipe0_err == -1 || pipe1_err == -1) {
-    WARNING("dpdk_helper_spawn: error setting up pipes: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-  }
-
-  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",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    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) {
-  char errbuf[ERR_BUF_SIZE];
-  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__,
-              sstrerror(errno, errbuf, sizeof(errbuf)));
-      }
-    }
-  } 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__,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-    }
-  }
-
-  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) {
-      char errbuf[ERR_BUF_SIZE];
-      DPDK_CHILD_LOG("dpdk_helper_worker: error posting sema_cmd_complete "
-                     "semaphore (%s)\n",
-                     sstrerror(errno, errbuf, sizeof(errbuf)));
-    }
-
-#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));
-  char errbuf[ERR_BUF_SIZE];
-
-  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",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-    }
-    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",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-    }
-    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) {
-      char errbuf[ERR_BUF_SIZE];
-      ERROR("%s: poll(2) failed: %s", phc->shm_name,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-    }
-  }
-  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) {
-    char errbuf[ERR_BUF_SIZE];
-    ERROR("dpdk_helper_worker: error posting sema_cmd_start semaphore (%s)",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-  }
-
-#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];
-
-    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() {
-  uint8_t ports = rte_eth_dev_count();
-  if (ports == 0) {
-    ERROR(
-        "%s:%d: No DPDK ports available. Check bound devices to DPDK driver.\n",
-        __FUNCTION__, __LINE__);
-    return ports;
-  }
-
-  if (ports > RTE_MAX_ETHPORTS) {
-    ERROR("%s:%d: Number of DPDK ports (%u) is greater than "
-          "RTE_MAX_ETHPORTS=%d. Ignoring extra ports\n",
-          __FUNCTION__, __LINE__, ports, RTE_MAX_ETHPORTS);
-    ports = RTE_MAX_ETHPORTS;
-  }
-
-  return ports;
-}
diff --git a/src/utils_dpdk.h b/src/utils_dpdk.h
deleted file mode 100644 (file)
index f3b7e7f..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * collectd - src/utils_dpdk.h
- * MIT License
- *
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- *   Maryam Tahhan <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();
-
-/* 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 */
index 366b44b..7c342e5 100644 (file)
@@ -28,7 +28,7 @@
 
 #include "plugin.h"
 
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
 #include "utils_fbhash.h"
 
 struct fbhash_s {
@@ -102,7 +102,7 @@ static int fbh_read_file(fbhash_t *h) /* {{{ */
     char *key_copy;
     char *value_copy;
 
-    buffer[sizeof(buffer) - 1] = 0;
+    buffer[sizeof(buffer) - 1] = '\0';
     len = strlen(buffer);
 
     /* Remove trailing newline characters. */
diff --git a/src/utils_format_graphite.c b/src/utils_format_graphite.c
deleted file mode 100644 (file)
index 87cead1..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-/**
- * collectd - src/utils_format_graphite.c
- * Copyright (C) 2012  Thomas Meson
- * Copyright (C) 2012  Florian octo Forster
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- *
- * Authors:
- *   Thomas Meson <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 */
-
-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("%llu", vl->values[ds_num].counter);
-  else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
-    BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive);
-  else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
-    BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute);
-  else {
-    ERROR("gr_format_values plugin: Unknown data source type: %i",
-          ds->ds[ds_num].type);
-    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(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) ? 1 : 0;
-
-  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) {
-      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. */
-    status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix,
-                            escape_char, flags);
-    if (status != 0) {
-      ERROR("format_graphite: error with gr_format_name");
-      sfree(rates);
-      return status;
-    }
-
-    escape_graphite_string(key, escape_char);
-    /* Convert the values to an ASCII representation and put that into
-     * `values'. */
-    status = gr_format_values(values, sizeof(values), i, ds, vl, rates);
-    if (status != 0) {
-      ERROR("format_graphite: error with gr_format_values");
-      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)) {
-      ERROR("format_graphite: message buffer too small: "
-            "Need %zu bytes.",
-            message_len + 1);
-      sfree(rates);
-      return -ENOMEM;
-    }
-
-    /* Append it in case we got multiple data set */
-    if ((buffer_pos + message_len) >= buffer_size) {
-      ERROR("format_graphite: target buffer too small");
-      sfree(rates);
-      return -ENOMEM;
-    }
-    memcpy((void *)(buffer + buffer_pos), message, message_len);
-    buffer_pos += message_len;
-    buffer[buffer_pos] = '\0';
-  }
-  sfree(rates);
-  return status;
-} /* int format_graphite */
diff --git a/src/utils_format_graphite.h b/src/utils_format_graphite.h
deleted file mode 100644 (file)
index de90c44..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * collectd - src/utils_format_graphite.h
- * Copyright (C) 2012  Thomas Meson
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- *
- * Author:
- *   Thomas Meson <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
-
-int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds,
-                    const value_list_t *vl, const char *prefix,
-                    const char *postfix, const char escape_char,
-                    unsigned int flags);
-
-#endif /* UTILS_FORMAT_GRAPHITE_H */
diff --git a/src/utils_format_graphite_test.c b/src/utils_format_graphite_test.c
deleted file mode 100644 (file)
index a82142f..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-/**
- * collectd - src/utils_format_graphite_test.c
- * Copyright (C) 2016       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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",
-      },
-  };
-
-  for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
-    value_list_t vl = {
-        .values = &(value_t){.gauge = 42},
-        .values_len = 1,
-        .time = TIME_T_TO_CDTIME_T_STATIC(1480063672),
-        .interval = TIME_T_TO_CDTIME_T_STATIC(10),
-        .host = "example.com",
-        .plugin = "test",
-        .type = "single",
-    };
-
-    char want[1024];
-    snprintf(want, sizeof(want), "%s 42 1480063672\r\n", cases[i].want_name);
-
-    if (cases[i].plugin_instance != NULL)
-      sstrncpy(vl.plugin_instance, cases[i].plugin_instance,
-               sizeof(vl.plugin_instance));
-    if (cases[i].type_instance != NULL)
-      sstrncpy(vl.type_instance, cases[i].type_instance,
-               sizeof(vl.type_instance));
-
-    char got[1024];
-    EXPECT_EQ_INT(0, format_graphite(got, sizeof(got), &ds_single, &vl,
-                                     cases[i].prefix, cases[i].suffix, '@',
-                                     cases[i].flags));
-    EXPECT_EQ_STR(want, got);
-  }
-
-  return 0;
-}
-
-DEF_TEST(null_termination) {
-  value_list_t vl = {
-      .values = &(value_t){.gauge = 1337},
-      .values_len = 1,
-      .time = TIME_T_TO_CDTIME_T_STATIC(1480063672),
-      .interval = TIME_T_TO_CDTIME_T_STATIC(10),
-      .host = "example.com",
-      .plugin = "test",
-      .type = "single",
-  };
-  char const *want = "example_com.test.single 1337 1480063672\r\n";
-
-  char buffer[128];
-  for (size_t i = 0; i < sizeof(buffer); i++)
-    buffer[i] = (char)i;
-
-  EXPECT_EQ_INT(0, format_graphite(buffer, sizeof(buffer), &ds_single, &vl,
-                                   NULL, NULL, '_', 0));
-  EXPECT_EQ_STR(want, buffer);
-  EXPECT_EQ_INT(0, buffer[strlen(want)]);
-  for (size_t i = strlen(want) + 1; i < sizeof(buffer); i++)
-    EXPECT_EQ_INT((int)i, (int)buffer[i]);
-
-  return 0;
-}
-
-int main(void) {
-  RUN_TEST(metric_name);
-  RUN_TEST(null_termination);
-
-  END_TEST;
-}
diff --git a/src/utils_format_json.c b/src/utils_format_json.c
deleted file mode 100644 (file)
index 4ecbfbe..0000000
+++ /dev/null
@@ -1,703 +0,0 @@
-/**
- * collectd - src/utils_format_json.c
- * Copyright (C) 2009-2015  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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("%llu", 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
-
-  DEBUG("format_json: values_to_json: buffer = %s;", buffer);
-  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
-
-  DEBUG("format_json: dstypes_to_json: buffer = %s;", buffer);
-
-  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
-
-  DEBUG("format_json: dsnames_to_json: buffer = %s;", buffer);
-
-  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 = 0;
-      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
-
-  DEBUG("format_json: value_list_to_json: buffer = %s;", buffer);
-
-  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
diff --git a/src/utils_format_json.h b/src/utils_format_json.h
deleted file mode 100644 (file)
index d3d0216..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * collectd - src/utils_format_json.h
- * Copyright (C) 2009       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/utils_format_json_test.c b/src/utils_format_json_test.c
deleted file mode 100644 (file)
index 389004d..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
- * collectd - src/utils_format_json_test.c
- * Copyright (C) 2015       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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;
-}
diff --git a/src/utils_format_kairosdb.c b/src/utils_format_kairosdb.c
deleted file mode 100644 (file)
index 460f807..0000000
+++ /dev/null
@@ -1,360 +0,0 @@
-/**
- * collectd - src/utils_format_kairosdb.c
- * Copyright (C) 2016       Aurelien beorn Rougemont
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Aurelien beorn Rougemont <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("%llu", 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 */
-
-/* vim: set sw=2 sts=2 et fdm=marker : */
diff --git a/src/utils_format_kairosdb.h b/src/utils_format_kairosdb.h
deleted file mode 100644 (file)
index 7b9e0e7..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * collectd - src/utils_format_kairosdb.h
- * Copyright (C) 2016       Aurelien Rougemont
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Aurelien beorn Rougemont <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 */
diff --git a/src/utils_ignorelist.c b/src/utils_ignorelist.c
deleted file mode 100644 (file)
index 29b2af2..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-/**
- * 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) */
diff --git a/src/utils_ignorelist.h b/src/utils_ignorelist.h
deleted file mode 100644 (file)
index a7fa86d..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * 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 */
diff --git a/src/utils_latency.c b/src/utils_latency.c
deleted file mode 100644 (file)
index 625fc42..0000000
+++ /dev/null
@@ -1,344 +0,0 @@
-/**
- * collectd - src/utils_latency.c
- * Copyright (C) 2013       Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian Forster <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 it's 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) {
-      ERROR("utils_latency: latency_counter_add: Invalid bin: %" PRIu64, bin);
-      return;
-    }
-  }
-  lc->histogram[bin]++;
-} /* }}} void latency_counter_add */
-
-void latency_counter_reset(latency_counter_t *lc) /* {{{ */
-{
-  if (lc == NULL)
-    return;
-
-  cdtime_t bin_width = lc->bin_width;
-  cdtime_t max_bin = (lc->max - 1) / lc->bin_width;
-
-/*
-  If max latency is REDUCE_THRESHOLD times less than histogram's range,
-  then cut it in half. REDUCE_THRESHOLD must be >= 2.
-  Value of 4 is selected to reduce frequent changes of bin width.
-*/
-#define REDUCE_THRESHOLD 4
-  if ((lc->num > 0) && (lc->bin_width >= HISTOGRAM_DEFAULT_BIN_WIDTH * 2) &&
-      (max_bin < HISTOGRAM_NUM_BINS / REDUCE_THRESHOLD)) {
-    /* new bin width will be the previous power of 2 */
-    bin_width = bin_width / 2;
-
-    DEBUG("utils_latency: latency_counter_reset: max_latency = %.3f; "
-          "max_bin = %" PRIu64 "; old_bin_width = %.3f; new_bin_width = %.3f;",
-          CDTIME_T_TO_DOUBLE(lc->max), max_bin,
-          CDTIME_T_TO_DOUBLE(lc->bin_width), CDTIME_T_TO_DOUBLE(bin_width));
-  }
-
-  memset(lc, 0, sizeof(*lc));
-
-  /* preserve bin width */
-  lc->bin_width = bin_width;
-  lc->start_time = cdtime();
-} /* }}} void latency_counter_reset */
-
-cdtime_t latency_counter_get_min(latency_counter_t *lc) /* {{{ */
-{
-  if (lc == NULL)
-    return 0;
-  return lc->min;
-} /* }}} cdtime_t latency_counter_get_min */
-
-cdtime_t latency_counter_get_max(latency_counter_t *lc) /* {{{ */
-{
-  if (lc == NULL)
-    return 0;
-  return lc->max;
-} /* }}} cdtime_t latency_counter_get_max */
-
-cdtime_t latency_counter_get_sum(latency_counter_t *lc) /* {{{ */
-{
-  if (lc == NULL)
-    return 0;
-  return lc->sum;
-} /* }}} cdtime_t latency_counter_get_sum */
-
-size_t latency_counter_get_num(latency_counter_t *lc) /* {{{ */
-{
-  if (lc == NULL)
-    return 0;
-  return lc->num;
-} /* }}} size_t latency_counter_get_num */
-
-cdtime_t latency_counter_get_average(latency_counter_t *lc) /* {{{ */
-{
-  double average;
-
-  if ((lc == NULL) || (lc->num == 0))
-    return 0;
-
-  average = CDTIME_T_TO_DOUBLE(lc->sum) / ((double)lc->num);
-  return DOUBLE_TO_CDTIME_T(average);
-} /* }}} cdtime_t latency_counter_get_average */
-
-cdtime_t latency_counter_get_percentile(latency_counter_t *lc, /* {{{ */
-                                        double percent) {
-  double percent_upper;
-  double percent_lower;
-  double p;
-  cdtime_t latency_lower;
-  cdtime_t latency_interpolated;
-  int sum;
-  size_t i;
-
-  if ((lc == NULL) || (lc->num == 0) || !((percent > 0.0) && (percent < 100.0)))
-    return 0;
-
-  /* Find index i so that at least "percent" events are within i+1 ms. */
-  percent_upper = 0.0;
-  percent_lower = 0.0;
-  sum = 0;
-  for (i = 0; i < HISTOGRAM_NUM_BINS; i++) {
-    percent_lower = percent_upper;
-    sum += lc->histogram[i];
-    if (sum == 0)
-      percent_upper = 0.0;
-    else
-      percent_upper = 100.0 * ((double)sum) / ((double)lc->num);
-
-    if (percent_upper >= percent)
-      break;
-  }
-
-  if (i >= HISTOGRAM_NUM_BINS)
-    return 0;
-
-  assert(percent_upper >= percent);
-  assert(percent_lower < percent);
-
-  if (i == 0)
-    return lc->bin_width;
-
-  latency_lower = ((cdtime_t)i) * lc->bin_width;
-  p = (percent - percent_lower) / (percent_upper - percent_lower);
-
-  latency_interpolated =
-      latency_lower + DOUBLE_TO_CDTIME_T(p * CDTIME_T_TO_DOUBLE(lc->bin_width));
-
-  DEBUG("latency_counter_get_percentile: latency_interpolated = %.3f",
-        CDTIME_T_TO_DOUBLE(latency_interpolated));
-  return latency_interpolated;
-} /* }}} cdtime_t latency_counter_get_percentile */
-
-double latency_counter_get_rate(const latency_counter_t *lc, /* {{{ */
-                                cdtime_t lower, cdtime_t upper,
-                                const cdtime_t now) {
-  if ((lc == NULL) || (lc->num == 0))
-    return NAN;
-
-  if (upper && (upper < lower))
-    return NAN;
-  if (lower == upper)
-    return 0;
-
-  /* Buckets have an exclusive lower bound and an inclusive upper bound. That
-   * means that the first bucket, index 0, represents (0-bin_width]. That means
-   * that latency==bin_width needs to result in bin=0, that's why we need to
-   * subtract one before dividing by bin_width. */
-  cdtime_t lower_bin = 0;
-  if (lower)
-    /* lower is *exclusive* => determine bucket for lower+1 */
-    lower_bin = ((lower + 1) - 1) / lc->bin_width;
-
-  /* lower is greater than the longest latency observed => rate is zero. */
-  if (lower_bin >= HISTOGRAM_NUM_BINS)
-    return 0;
-
-  cdtime_t upper_bin = HISTOGRAM_NUM_BINS - 1;
-  if (upper)
-    upper_bin = (upper - 1) / lc->bin_width;
-
-  if (upper_bin >= HISTOGRAM_NUM_BINS) {
-    upper_bin = HISTOGRAM_NUM_BINS - 1;
-    upper = 0;
-  }
-
-  double sum = 0;
-  for (size_t i = lower_bin; i <= upper_bin; i++)
-    sum += lc->histogram[i];
-
-  if (lower) {
-    /* Approximate ratio of requests in lower_bin, that fall between
-     * lower_bin_boundary and lower. This ratio is then subtracted from sum to
-     * increase accuracy. */
-    cdtime_t lower_bin_boundary = lower_bin * lc->bin_width;
-    assert(lower >= lower_bin_boundary);
-    double lower_ratio =
-        (double)(lower - lower_bin_boundary) / ((double)lc->bin_width);
-    sum -= lower_ratio * lc->histogram[lower_bin];
-  }
-
-  if (upper) {
-    /* As above: approximate ratio of requests in upper_bin, that fall between
-     * upper and upper_bin_boundary. */
-    cdtime_t upper_bin_boundary = (upper_bin + 1) * lc->bin_width;
-    assert(upper <= upper_bin_boundary);
-    double ratio = (double)(upper_bin_boundary - upper) / (double)lc->bin_width;
-    sum -= ratio * lc->histogram[upper_bin];
-  }
-
-  return sum / (CDTIME_T_TO_DOUBLE(now - lc->start_time));
-} /* }}} double latency_counter_get_rate */
diff --git a/src/utils_latency.h b/src/utils_latency.h
deleted file mode 100644 (file)
index 9d878da..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * collectd - src/utils_latency.h
- * Copyright (C) 2013       Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian Forster <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);
diff --git a/src/utils_latency_config.c b/src/utils_latency_config.c
deleted file mode 100644 (file)
index 5eb5b6d..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/**
- * collectd - src/utils_latency_config.c
- * Copyright (C) 2013-2016   Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- *   Pavel Rochnyack <pavel2000 at ngs.ru>
- */
-
-#include "utils_latency_config.h"
-#include "common.h"
-#include "collectd.h"
-
-static int latency_config_add_percentile(latency_config_t *conf,
-                                         oconfig_item_t *ci,
-                                         const char *plugin) {
-  double percent;
-  int status = cf_util_get_double(ci, &percent);
-  if (status != 0)
-    return status;
-
-  if ((percent <= 0.0) || (percent >= 100)) {
-    ERROR("%s plugin: The value for \"%s\" must be between 0 and 100, "
-          "exclusively.",
-          plugin, ci->key);
-    return ERANGE;
-  }
-
-  double *tmp = realloc(conf->percentile,
-                        sizeof(*conf->percentile) * (conf->percentile_num + 1));
-  if (tmp == NULL) {
-    ERROR("%s plugin: realloc failed.", plugin);
-    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,
-                                     const char *plugin) {
-  if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_NUMBER) ||
-      (ci->values[1].type != OCONFIG_TYPE_NUMBER)) {
-    ERROR("%s plugin: \"%s\" requires exactly two numeric arguments.", plugin,
-          ci->key);
-    return EINVAL;
-  }
-
-  if (ci->values[1].value.number &&
-      ci->values[1].value.number <= ci->values[0].value.number) {
-    ERROR("%s plugin: MIN must be less than MAX in \"%s\".", plugin, ci->key);
-    return ERANGE;
-  }
-
-  if (ci->values[0].value.number < 0) {
-    ERROR("%s plugin: MIN must be greater then or equal to zero in \"%s\".",
-          plugin, ci->key);
-    return ERANGE;
-  }
-
-  latency_bucket_t *tmp =
-      realloc(conf->buckets, sizeof(*conf->buckets) * (conf->buckets_num + 1));
-  if (tmp == NULL) {
-    ERROR("%s plugin: realloc failed.", plugin);
-    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,
-                   char const *plugin) {
-  int status = 0;
-
-  for (int i = 0; i < ci->children_num; i++) {
-    oconfig_item_t *child = ci->children + i;
-
-    if (strcasecmp("Percentile", child->key) == 0)
-      status = latency_config_add_percentile(conf, child, plugin);
-    else if (strcasecmp("Bucket", child->key) == 0)
-      status = latency_config_add_bucket(conf, child, plugin);
-    else if (strcasecmp("BucketType", child->key) == 0)
-      status = cf_util_get_string(child, &conf->bucket_type);
-    else
-      WARNING("%s plugin: \"%s\" is not a valid option within a \"%s\" block.",
-              plugin, child->key, ci->key);
-
-    if (status != 0)
-      return status;
-  }
-
-  if ((status == 0) && (conf->percentile_num == 0) &&
-      (conf->buckets_num == 0)) {
-    ERROR("%s plugin: The \"%s\" block must contain at least one "
-          "\"Percentile\" or \"Bucket\" option.",
-          plugin, ci->key);
-    return EINVAL;
-  }
-
-  return 0;
-}
-
-int latency_config_copy(latency_config_t *dst, const latency_config_t src) {
-  *dst = (latency_config_t){
-      .percentile_num = src.percentile_num, .buckets_num = src.buckets_num,
-  };
-
-  dst->percentile = calloc(dst->percentile_num, sizeof(*dst->percentile));
-  dst->buckets = calloc(dst->buckets_num, sizeof(*dst->buckets));
-
-  if ((dst->percentile == NULL) || (dst->buckets == NULL)) {
-    latency_config_free(*dst);
-    return ENOMEM;
-  }
-
-  if (src.bucket_type != NULL) {
-    dst->bucket_type = strdup(src.bucket_type);
-    if (dst->bucket_type == NULL) {
-      latency_config_free(*dst);
-      return ENOMEM;
-    }
-  }
-
-  memmove(dst->percentile, src.percentile,
-          dst->percentile_num * sizeof(*dst->percentile));
-  memmove(dst->buckets, src.buckets, dst->buckets_num * sizeof(*dst->buckets));
-
-  return 0;
-} /* int latency_config_copy */
-
-void latency_config_free(latency_config_t conf) {
-  sfree(conf.percentile);
-  sfree(conf.buckets);
-  sfree(conf.bucket_type);
-} /* void latency_config_free */
diff --git a/src/utils_latency_config.h b/src/utils_latency_config.h
deleted file mode 100644 (file)
index 7008fd0..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * collectd - src/utils_latency_config.c
- * Copyright (C) 2013-2016   Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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,
-                   char const *plugin);
-
-int latency_config_copy(latency_config_t *dst, const latency_config_t src);
-
-void latency_config_free(latency_config_t conf);
-
-#endif /* UTILS_LATENCY_CONFIG_H */
diff --git a/src/utils_latency_test.c b/src/utils_latency_test.c
deleted file mode 100644 (file)
index 427a159..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/**
- * collectd - src/utils_latency_test.c
- * Copyright (C) 2015       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- */
-
-#define DBL_PRECISION 1e-6
-
-#include "common.h" /* for STATIC_ARRAY_SIZE */
-#include "collectd.h"
-
-#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 %zu: 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;
-}
index 0990472..d0de908 100644 (file)
  *   Florian Forster <octo at collectd.org>
  **/
 
-/* <lua5.1/luaconf.h> defines a macro using "sprintf". Although not used here,
- * GCC will complain about the macro definition. */
-#define DONT_POISON_SPRINTF_YET
-
+#include "utils/common/common.h"
 #include "utils_lua.h"
-#include "common.h"
 
 static int ltoc_values(lua_State *L, /* {{{ */
                        const data_set_t *ds, value_t *ret_values) {
@@ -57,8 +53,8 @@ static int ltoc_values(lua_State *L, /* {{{ */
   } /* while (lua_next) */
 
   if (i != ds->ds_num) {
-    WARNING("ltoc_values: invalid size for datasource \"%s\": expected %zu, "
-            "got %zu",
+    WARNING("ltoc_values: invalid size for datasource \"%s\": expected %" PRIsz
+            ", got %" PRIsz,
             ds->type, ds->ds_num, i);
     return -1;
   }
index 61d9070..e5a3d74 100644 (file)
 #ifndef UTILS_LUA_H
 #define UTILS_LUA_H 1
 
-#include "plugin.h"
 #include "collectd.h"
+#include "plugin.h"
 
-#ifndef DONT_POISON_SPRINTF_YET
-#error "Files including utils_lua.h need to define DONT_POISON_SPRINTF_YET."
-#endif
 #include <lua.h>
 
 /*
diff --git a/src/utils_match.c b/src/utils_match.c
deleted file mode 100644 (file)
index d3edb57..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-/**
- * collectd - src/utils_match.c
- * Copyright (C) 2008-2014  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/utils_match.h b/src/utils_match.h
deleted file mode 100644 (file)
index 1cff1eb..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/**
- * collectd - src/utils_match.h
- * Copyright (C) 2008-2014  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/utils_mount.c b/src/utils_mount.c
deleted file mode 100644 (file)
index 49040aa..0000000
+++ /dev/null
@@ -1,778 +0,0 @@
-/**
- * collectd - src/utils_mount.c
- * Copyright (C) 2005,2006  Niki W. Waibel
- *
- * This program is free software; you can redistribute it and/
- * or modify it under the terms of the GNU General Public Li-
- * cence as published by the Free Software Foundation; either
- * version 2 of the Licence, or any later version.
- *
- * This program is distributed in the hope that it will be use-
- * ful, but WITHOUT ANY WARRANTY; without even the implied war-
- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public Licence for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- *
- * Author:
- *   Niki W. Waibel <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
-    char errbuf[1024];
-    DEBUG("utils_mount: calling listmntent() failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-#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
-    char errbuf[1024];
-    DEBUG("utils_mount: getv?fsstat failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-#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
-    char errbuf[1024];
-    DEBUG("utils_mount: getv?fsstat failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-#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) {
-    char errbuf[1024];
-    ERROR("fopen (%s): %s", COLLECTD_MNTTAB,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    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) */
-  /* #endif HAVE_TWO_GETMNTENT || HAVE_GEN_GETMNTENT || HAVE_SUN_GETMNTENT */
-
-#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) {
-    char errbuf[1024];
-    ERROR("setmntent (%s): %s", COLLECTD_MNTTAB,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    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) {
-    char errbuf[1024];
-    ERROR("setmntent (%s): %s", COLLECTD_MNTTAB,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    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;
-  int l;
-
-  if (line == NULL || keyword == NULL) {
-    return NULL;
-  }
-
-  if (full != 0) {
-    full = 1;
-  }
-
-  line2 = sstrdup(line);
-  l2 = line2;
-  while (*l2 != '\0') {
-    if (*l2 == ',') {
-      *l2 = '\0';
-    }
-    l2++;
-  }
-
-  l = strlen(keyword);
-  p1 = line - 1;
-  p2 = strchr(line, ',');
-  do {
-    if (strncmp(line2 + (p1 - line) + 1, keyword, l + full) == 0) {
-      free(line2);
-      return p1 + 1;
-    }
-    p1 = p2;
-    if (p1 != NULL) {
-      p2 = strchr(p1 + 1, ',');
-    }
-  } while (p1 != NULL);
-
-  free(line2);
-  return NULL;
-} /* char *cu_mount_checkoption(char *line, char *keyword, int full) */
-
-char *cu_mount_getoptionvalue(char *line, const char *keyword) {
-  char *r;
-
-  r = cu_mount_checkoption(line, keyword, 0);
-  if (r != NULL) {
-    char *p;
-    r += strlen(keyword);
-    p = strchr(r, ',');
-    if (p == NULL) {
-      return sstrdup(r);
-    } else {
-      char *m;
-      if ((p - r) == 1) {
-        return NULL;
-      }
-      m = smalloc(p - r + 1);
-      sstrncpy(m, r, p - r + 1);
-      return m;
-    }
-  }
-  return r;
-} /* char *cu_mount_getoptionvalue(char *line, const char *keyword) */
-
-int cu_mount_type(const char *type) {
-  if (strcmp(type, "ext3") == 0)
-    return CUMT_EXT3;
-  if (strcmp(type, "ext2") == 0)
-    return CUMT_EXT2;
-  if (strcmp(type, "ufs") == 0)
-    return CUMT_UFS;
-  if (strcmp(type, "vxfs") == 0)
-    return CUMT_VXFS;
-  if (strcmp(type, "zfs") == 0)
-    return CUMT_ZFS;
-  return CUMT_UNKNOWN;
-} /* int cu_mount_type(const char *type) */
diff --git a/src/utils_mount.h b/src/utils_mount.h
deleted file mode 100644 (file)
index 0ad7d02..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/**
- * collectd - src/utils_mount.h
- * Copyright (C) 2005,2006  Niki W. Waibel
- *
- * This program is free software; you can redistribute it and/
- * or modify it under the terms of the GNU General Public Li-
- * cence as published by the Free Software Foundation; either
- * version 2 of the Licence, or any later version.
- *
- * This program is distributed in the hope that it will be use-
- * ful, but WITHOUT ANY WARRANTY; without even the implied war-
- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public Licence for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
- *
- * Author:
- *   Niki W. Waibel <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 */
diff --git a/src/utils_mount_test.c b/src/utils_mount_test.c
deleted file mode 100644 (file)
index e8f3009..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * collectd - src/tests/test_utils_mount.c
- * Copyright (C) 2013       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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;
-}
diff --git a/src/utils_ovs.c b/src/utils_ovs.c
deleted file mode 100644 (file)
index 4f48755..0000000
+++ /dev/null
@@ -1,1397 +0,0 @@
-/**
- * collectd - src/utils_ovs.c
- *
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- *of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to
- *do
- * so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- *all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- *   Volodymyr Mytnyk <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 = 0;
-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=%zu] %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) {
-    char errbuff[OVS_ERROR_BUFF_SIZE];
-    int sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
-    if (sock < 0) {
-      sstrerror(errno, errbuff, sizeof(errbuff));
-      OVS_DEBUG("socket(): %s", errbuff);
-      continue;
-    }
-    if (connect(sock, rp->ai_addr, rp->ai_addrlen) < 0) {
-      close(sock);
-      sstrerror(errno, errbuff, sizeof(errbuff));
-      OVS_DEBUG("connect(): %s [family=%d]", errbuff, rp->ai_family);
-    } 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)) {
-    char errbuff[OVS_ERROR_BUFF_SIZE];
-    poll_fd.fd = pdb->sock;
-    int poll_ret = poll(&poll_fd, 1, /* ms */ OVS_DB_POLL_TIMEOUT * 1000);
-    if (poll_ret < 0) {
-      sstrerror(errno, errbuff, sizeof(errbuff));
-      OVS_ERROR("poll(): %s", errbuff);
-      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) {
-        sstrerror(errno, errbuff, sizeof(errbuff));
-        OVS_ERROR("recv(): %s", errbuff);
-        /* 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) {
-  /* 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) {
-    ovs_db_destroy(pdb);
-    return NULL;
-  }
-
-  /* init polling thread */
-  if (ovs_db_poll_thread_init(pdb) < 0) {
-    ovs_db_destroy(pdb);
-    return NULL;
-  }
-  return pdb;
-}
-
-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 **)&params,
-                &params_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 -1;
-  }
-
-  /* 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 (strcmp("map", str_val) != 0)
-    return NULL;
-
-  /* try to find map value by map key */
-  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]))
-      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 (strcmp(key, str_val) == 0)
-      return array_values[1];
-  }
-  return NULL;
-}
diff --git a/src/utils_ovs.h b/src/utils_ovs.h
deleted file mode 100644 (file)
index 52c2f91..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-/**
- * collectd - src/utils_ovs.h
- *
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * Authors:
- *   Volodymyr Mytnyk <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
diff --git a/src/utils_parse_option.c b/src/utils_parse_option.c
deleted file mode 100644 (file)
index 005715c..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/**
- * collectd - src/utils_parse_option.c
- * Copyright (C) 2008       Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/utils_parse_option.h b/src/utils_parse_option.h
deleted file mode 100644 (file)
index 3dd0a79..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * collectd - src/utils_parse_option.h
- * Copyright (C) 2008       Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/utils_rrdcreate.c b/src/utils_rrdcreate.c
deleted file mode 100644 (file)
index 6cb5446..0000000
+++ /dev/null
@@ -1,666 +0,0 @@
-/**
- * collectd - src/utils_rrdcreate.c
- * Copyright (C) 2006-2013  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 = NULL;
-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) {
-    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) {
-    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) {
-    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) {
-      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))) {
-        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) {
-    char errbuf[1024];
-    ERROR("rrdtool plugin: calloc failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    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 {
-      ERROR("rrdtool plugin: 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) {
-    WARNING("rrdtool plugin: 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) {
-    ERROR("rrdtool plugin: 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) {
-    WARNING("rrdtool plugin: 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)
-      NOTICE("srrd_create_thread: File \"%s\" is already being created.",
-             args->filename);
-    else
-      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) {
-    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) {
-    char errbuf[1024];
-    ERROR("srrd_create_thread: rename (\"%s\", \"%s\") failed: %s", tmpfile,
-          args->filename, sstrerror(errno, errbuf, sizeof(errbuf)));
-    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) {
-    char errbuf[1024];
-    ERROR("srrd_create_async: pthread_create failed: %s",
-          sstrerror(status, errbuf, sizeof(errbuf)));
-    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) {
-    ERROR("cu_rrd_create_file failed: Could not calculate RRAs");
-    return -1;
-  }
-
-  if ((ds_num = ds_get(&ds_def, ds, vl, cfg)) < 1) {
-    ERROR("cu_rrd_create_file failed: Could not calculate DSes");
-    rra_free(rra_num, rra_def);
-    return -1;
-  }
-
-  argc = ds_num + rra_num;
-
-  if ((argv = malloc(sizeof(*argv) * (argc + 1))) == NULL) {
-    char errbuf[1024];
-    ERROR("cu_rrd_create_file failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    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)
-      WARNING("cu_rrd_create_file: srrd_create_async (%s) "
-              "returned status %i.",
-              filename, status);
-  } else /* synchronous */
-  {
-    status = lock_file(filename);
-    if (status != 0) {
-      if (status == EEXIST)
-        NOTICE("cu_rrd_create_file: File \"%s\" is already being created.",
-               filename);
-      else
-        ERROR("cu_rrd_create_file: Unable to lock file \"%s\".", filename);
-    } else {
-      status =
-          srrd_create(filename, stepsize, last_up, argc, (const char **)argv);
-
-      if (status != 0) {
-        WARNING("cu_rrd_create_file: srrd_create (%s) returned status %i.",
-                filename, status);
-      } else {
-        DEBUG("cu_rrd_create_file: Successfully created RRD file \"%s\".",
-              filename);
-      }
-      unlock_file(filename);
-    }
-  }
-
-  free(argv);
-  ds_free(ds_num, ds_def);
-  rra_free(rra_num, rra_def);
-
-  return status;
-} /* }}} int cu_rrd_create_file */
diff --git a/src/utils_rrdcreate.h b/src/utils_rrdcreate.h
deleted file mode 100644 (file)
index d5f9a12..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * collectd - src/utils_rrdcreate.h
- * Copyright (C) 2008-2013  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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 */
diff --git a/src/utils_tail.c b/src/utils_tail.c
deleted file mode 100644 (file)
index cdab6a7..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/**
- * collectd - src/utils_tail.c
- * Copyright (C) 2007-2008  C-Ware, Inc.
- * Copyright (C) 2008       Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Author:
- *   Luke Heberling <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;
-  FILE *fh;
-  struct stat stat_buf = {0};
-  int status;
-
-  status = stat(obj->file, &stat_buf);
-  if (status != 0) {
-    char errbuf[1024];
-    ERROR("utils_tail: stat (%s) failed: %s", obj->file,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    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) {
-      INFO("utils_tail: File `%s' was truncated.", obj->file);
-      status = fseek(obj->fh, 0, SEEK_SET);
-      if (status != 0) {
-        char errbuf[1024];
-        ERROR("utils_tail: fseek (%s) failed: %s", obj->file,
-              sstrerror(errno, errbuf, sizeof(errbuf)));
-        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;
-
-  fh = fopen(obj->file, "r");
-  if (fh == NULL) {
-    char errbuf[1024];
-    ERROR("utils_tail: fopen (%s) failed: %s", obj->file,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
-    return -1;
-  }
-
-  if (seek_end != 0) {
-    status = fseek(fh, 0, SEEK_END);
-    if (status != 0) {
-      char errbuf[1024];
-      ERROR("utils_tail: fseek (%s) failed: %s", obj->file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-      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) {
-    char errbuf[1024];
-    WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-    fclose(obj->fh);
-    obj->fh = NULL;
-    return -1;
-  }
-
-  /* EOf, well, apparently the new file is empty.. */
-  buf[0] = 0;
-  return 0;
-} /* int cu_tail_readline */
-
-int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
-                 void *data) {
-  int status;
-
-  while (42) {
-    size_t len;
-
-    status = cu_tail_readline(obj, buf, buflen);
-    if (status != 0) {
-      ERROR("utils_tail: cu_tail_read: cu_tail_readline "
-            "failed.");
-      break;
-    }
-
-    /* check for EOF */
-    if (buf[0] == 0)
-      break;
-
-    len = strlen(buf);
-    while (len > 0) {
-      if (buf[len - 1] != '\n')
-        break;
-      buf[len - 1] = '\0';
-      len--;
-    }
-
-    status = callback(data, buf, buflen);
-    if (status != 0) {
-      ERROR("utils_tail: cu_tail_read: callback returned "
-            "status %i.",
-            status);
-      break;
-    }
-  }
-
-  return status;
-} /* int cu_tail_read */
diff --git a/src/utils_tail.h b/src/utils_tail.h
deleted file mode 100644 (file)
index 73a6de2..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * collectd - src/utils_tail.h
- * Copyright (C) 2007-2008  C-Ware, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Author:
- *   Luke Heberling <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 */
index 5134a6e..0e5a861 100644 (file)
 
 #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 {
@@ -43,7 +43,6 @@ struct cu_tail_match_simple_s {
   char plugin_instance[DATA_MAX_NAME_LEN];
   char type[DATA_MAX_NAME_LEN];
   char type_instance[DATA_MAX_NAME_LEN];
-  cdtime_t interval;
   latency_config_t latency_config;
 };
 typedef struct cu_tail_match_simple_s cu_tail_match_simple_t;
@@ -57,10 +56,7 @@ struct cu_tail_match_match_s {
 typedef struct cu_tail_match_match_s cu_tail_match_match_t;
 
 struct cu_tail_match_s {
-  int flags;
   cu_tail_t *tail;
-
-  cdtime_t interval;
   cu_tail_match_match_t *matches;
   size_t matches_num;
 };
@@ -92,7 +88,6 @@ static int simple_submit_match(cu_match_t *match, void *user_data) {
   sstrncpy(vl.type, data->type, sizeof(vl.type));
   sstrncpy(vl.type_instance, data->type_instance, sizeof(vl.type_instance));
 
-  vl.interval = data->interval;
   plugin_dispatch_values(&vl);
 
   match_value_reset(match_value);
@@ -111,7 +106,6 @@ static int latency_submit_match(cu_match_t *match, void *user_data) {
   sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin));
   sstrncpy(vl.plugin_instance, data->plugin_instance,
            sizeof(vl.plugin_instance));
-  vl.interval = data->interval;
   vl.time = cdtime();
 
   /* Submit percentiles */
@@ -248,8 +242,6 @@ int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match,
   obj->matches = temp;
   obj->matches_num++;
 
-  DEBUG("tail_match_add_match interval %lf",
-        CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval));
   temp = obj->matches + (obj->matches_num - 1);
 
   temp->match = match;
@@ -264,8 +256,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
                                 const char *excluderegex, int ds_type,
                                 const char *plugin, const char *plugin_instance,
                                 const char *type, const char *type_instance,
-                                const latency_config_t latency_cfg,
-                                const cdtime_t interval) {
+                                const latency_config_t latency_cfg) {
   cu_match_t *match;
   cu_tail_match_simple_t *user_data;
   int status;
@@ -290,8 +281,6 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
     sstrncpy(user_data->type_instance, type_instance,
              sizeof(user_data->type_instance));
 
-  user_data->interval = interval;
-
   if ((ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
       (ds_type & UTILS_MATCH_CF_GAUGE_DIST)) {
     status = latency_config_copy(&user_data->latency_config, latency_cfg);
index 03b70e9..0217a7e 100644 (file)
@@ -33,8 +33,8 @@
  *   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;
@@ -80,9 +80,8 @@ void tail_match_destroy(cu_tail_match_t *obj);
  *   When `tail_match_destroy' is called the `user_data' pointer is freed using
  *   the `free_user_data' callback - if it is not NULL.
  *   When using this interface the `tail_match' module doesn't dispatch any
- * values
- *   itself - all that has to happen in either the match-callbacks or the
- *   submit_match callback.
+ *   values itself - all that has to happen in either the match-callbacks or
+ *   the submit_match callback.
  *
  * RETURN VALUE
  *   Zero upon success, non-zero otherwise.
@@ -98,14 +97,13 @@ int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match,
  *  tail_match_add_match_simple
  *
  * DESCRIPTION
- *  A simplified version of `tail_match_add_match'. The regular expressen
- * `regex'
- *  must match a number, which is then dispatched according to `ds_type'. See
- *  the `match_create_simple' function in utils_match.h for a description how
- *  this flag effects calculation of a new value.
+ *  A simplified version of `tail_match_add_match'. The regular expression
+ *  `regex' must match a number, which is then dispatched according to
+ * `ds_type'.
+ *  See the `match_create_simple' function in utils_match.h for a description
+ *  how this flag effects calculation of a new value.
  *  The values gathered are dispatched by the tail_match module in this case.
- * The
- *  passed `plugin', `plugin_instance', `type', and `type_instance' are
+ *  The passed `plugin', `plugin_instance', `type', and `type_instance' are
  *  directly used when submitting these values.
  *  With excluderegex it is possible to exlude lines from the match.
  *  The `latency_cfg' specifies configuration for submitting latency.
@@ -117,8 +115,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
                                 const char *excluderegex, int ds_type,
                                 const char *plugin, const char *plugin_instance,
                                 const char *type, const char *type_instance,
-                                const latency_config_t latency_cfg,
-                                const cdtime_t interval);
+                                const latency_config_t latency_cfg);
 
 /*
  * NAME
@@ -130,8 +127,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
  *   added `utils_match' objects.
  *   After all lines have been read and processed, the submit_match callback is
  *   called or, in case of tail_match_add_match_simple, the data is dispatched
- * to
- *   the daemon directly.
+ *   to the daemon directly.
  *
  * RETURN VALUE
  *   Zero on success, nonzero on failure.
diff --git a/src/utils_vl_lookup.c b/src/utils_vl_lookup.c
deleted file mode 100644 (file)
index 052c4c0..0000000
+++ /dev/null
@@ -1,630 +0,0 @@
-/**
- * collectd - src/utils_vl_lookup.c
- * Copyright (C) 2012       Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian Forster <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 1;
-
-    int status = regexec(&match->regex, str,
-                         /* nmatch = */ 0, /* pmatch = */ NULL,
-                         /* flags = */ 0);
-    if (status == 0)
-      return 1;
-    else
-      return 0;
-  } else if (strcmp(match->str, str) == 0)
-    return 1;
-  else
-    return 0;
-} /* }}} _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 = 0;
-    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 = 1;
-
-  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 = 0;                         \
-    }                                                                          \
-  } 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 = */ 1);
-  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 = */ 0);
-  if (by_type == NULL)
-    return 0;
-
-  status =
-      c_avl_get(by_type->by_plugin_tree, vl->plugin, (void *)&user_class_list);
-  if (status == 0) {
-    status = lu_handle_user_class_list(obj, ds, vl, user_class_list);
-    if (status < 0)
-      return status;
-    retval += status;
-  }
-
-  if (by_type->wildcard_plugin_list != NULL) {
-    status =
-        lu_handle_user_class_list(obj, ds, vl, by_type->wildcard_plugin_list);
-    if (status < 0)
-      return status;
-    retval += status;
-  }
-
-  return retval;
-} /* }}} lookup_search */
diff --git a/src/utils_vl_lookup.h b/src/utils_vl_lookup.h
deleted file mode 100644 (file)
index 90a4ee5..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * collectd - src/utils_vl_lookup.h
- * Copyright (C) 2012       Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian Forster <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 */
diff --git a/src/utils_vl_lookup_test.c b/src/utils_vl_lookup_test.c
deleted file mode 100644 (file)
index 058015e..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-/**
- * collectd - src/tests/test_utils_vl_lookup.c
- * Copyright (C) 2012       Florian Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "testing.h"
-#include "utils_vl_lookup.h"
-
-static _Bool expect_new_obj = 0;
-static _Bool have_new_obj = 0;
-
-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 = 1;
-
-  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 = 0;
-
-  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 */
index 1cb9027..60d09b5 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_SYS_SYSCTL_H
 #include <sys/sysctl.h>
 #define UUID_PRINTABLE_COMPACT_LENGTH (UUID_RAW_LENGTH * 2)
 #define UUID_PRINTABLE_NORMAL_LENGTH (UUID_PRINTABLE_COMPACT_LENGTH + 4)
 
-static char *uuidfile = NULL;
+static char *uuidfile;
 
 static const char *config_keys[] = {"UUIDFile"};
 
 static int looks_like_a_uuid(const char *uuid) {
-  int len;
-
   if (!uuid)
     return 0;
 
-  len = strlen(uuid);
-
+  size_t len = strlen(uuid);
   if (len < UUID_PRINTABLE_COMPACT_LENGTH)
     return 0;
 
diff --git a/src/valgrind.suppress b/src/valgrind.suppress
new file mode 100644 (file)
index 0000000..f4c3f34
--- /dev/null
@@ -0,0 +1,7 @@
+{
+   libnl1_virt_initialization_unpreventable_leak
+   Memcheck:Leak
+   ...
+   obj:*libnl.so.1.*
+   ...
+}
\ No newline at end of file
index 08260dc..b4ae438 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_VARNISH_V4 || HAVE_VARNISH_V5
 #include <vapi/vsc.h>
@@ -50,50 +50,50 @@ typedef struct varnish_stats c_varnish_stats_t;
 struct user_config_s {
   char *instance;
 
-  _Bool collect_cache;
-  _Bool collect_connections;
-  _Bool collect_esi;
-  _Bool collect_backend;
+  bool collect_cache;
+  bool collect_connections;
+  bool collect_esi;
+  bool collect_backend;
 #ifdef HAVE_VARNISH_V3
-  _Bool collect_dirdns;
+  bool collect_dirdns;
 #endif
-  _Bool collect_fetch;
-  _Bool collect_hcb;
-  _Bool collect_objects;
+  bool collect_fetch;
+  bool collect_hcb;
+  bool collect_objects;
 #if HAVE_VARNISH_V2
-  _Bool collect_purge;
+  bool collect_purge;
 #else
-  _Bool collect_ban;
+  bool collect_ban;
 #endif
-  _Bool collect_session;
-  _Bool collect_shm;
-  _Bool collect_sms;
+  bool collect_session;
+  bool collect_shm;
+  bool collect_sms;
 #if HAVE_VARNISH_V2
-  _Bool collect_sm;
+  bool collect_sm;
 #endif
 #if HAVE_VARNISH_V2 || HAVE_VARNISH_V4 || HAVE_VARNISH_V5
-  _Bool collect_sma;
+  bool collect_sma;
 #endif
-  _Bool collect_struct;
-  _Bool collect_totals;
+  bool collect_struct;
+  bool collect_totals;
 #if HAVE_VARNISH_V3 || HAVE_VARNISH_V4 || HAVE_VARNISH_V5
-  _Bool collect_uptime;
+  bool collect_uptime;
 #endif
-  _Bool collect_vcl;
-  _Bool collect_workers;
+  bool collect_vcl;
+  bool collect_workers;
 #if HAVE_VARNISH_V4 || HAVE_VARNISH_V5
-  _Bool collect_vsm;
-  _Bool collect_lck;
-  _Bool collect_mempool;
-  _Bool collect_mgt;
-  _Bool collect_smf;
-  _Bool collect_vbe;
-  _Bool collect_mse;
+  bool collect_vsm;
+  bool collect_lck;
+  bool collect_mempool;
+  bool collect_mgt;
+  bool collect_smf;
+  bool collect_vbe;
+  bool collect_mse;
 #endif
 };
 typedef struct user_config_s user_config_t; /* }}} */
 
-static _Bool have_instance = 0;
+static bool have_instance;
 
 static int varnish_submit(const char *plugin_instance, /* {{{ */
                           const char *category, const char *type,
@@ -1331,7 +1331,7 @@ static int varnish_read(user_data_t *ud) /* {{{ */
 {
 #if HAVE_VARNISH_V3 || HAVE_VARNISH_V4
   struct VSM_data *vd;
-  _Bool ok;
+  bool ok;
   const c_varnish_stats_t *stats;
 #elif HAVE_VARNISH_V5
   struct vsm *vd;
@@ -1480,45 +1480,45 @@ static int varnish_config_apply_default(user_config_t *conf) /* {{{ */
   if (conf == NULL)
     return EINVAL;
 
-  conf->collect_backend = 1;
-  conf->collect_cache = 1;
-  conf->collect_connections = 1;
+  conf->collect_backend = true;
+  conf->collect_cache = true;
+  conf->collect_connections = true;
 #ifdef HAVE_VARNISH_V3
-  conf->collect_dirdns = 0;
+  conf->collect_dirdns = false;
 #endif
-  conf->collect_esi = 0;
-  conf->collect_fetch = 0;
-  conf->collect_hcb = 0;
-  conf->collect_objects = 0;
+  conf->collect_esi = false;
+  conf->collect_fetch = false;
+  conf->collect_hcb = false;
+  conf->collect_objects = false;
 #if HAVE_VARNISH_V2
-  conf->collect_purge = 0;
+  conf->collect_purge = false;
 #else
-  conf->collect_ban = 0;
+  conf->collect_ban = false;
 #endif
-  conf->collect_session = 0;
-  conf->collect_shm = 1;
+  conf->collect_session = false;
+  conf->collect_shm = true;
 #if HAVE_VARNISH_V2
-  conf->collect_sm = 0;
+  conf->collect_sm = false;
 #endif
 #if HAVE_VARNISH_V2 || HAVE_VARNISH_V4 || HAVE_VARNISH_V5
-  conf->collect_sma = 0;
+  conf->collect_sma = false;
 #endif
-  conf->collect_sms = 0;
-  conf->collect_struct = 0;
-  conf->collect_totals = 0;
+  conf->collect_sms = false;
+  conf->collect_struct = false;
+  conf->collect_totals = false;
 #if HAVE_VARNISH_V3 || HAVE_VARNISH_V4 || HAVE_VARNISH_V5
-  conf->collect_uptime = 0;
+  conf->collect_uptime = false;
 #endif
-  conf->collect_vcl = 0;
-  conf->collect_workers = 0;
+  conf->collect_vcl = false;
+  conf->collect_workers = false;
 #if HAVE_VARNISH_V4 || HAVE_VARNISH_V5
-  conf->collect_vsm = 0;
-  conf->collect_lck = 0;
-  conf->collect_mempool = 0;
-  conf->collect_mgt = 0;
-  conf->collect_smf = 0;
-  conf->collect_vbe = 0;
-  conf->collect_mse = 0;
+  conf->collect_vsm = false;
+  conf->collect_lck = false;
+  conf->collect_mempool = false;
+  conf->collect_mgt = false;
+  conf->collect_smf = false;
+  conf->collect_vbe = false;
+  conf->collect_mse = false;
 #endif
 
   return 0;
@@ -1771,7 +1771,7 @@ static int varnish_config_instance(const oconfig_item_t *ci) /* {{{ */
           .data = conf, .free_func = varnish_config_free,
       });
 
-  have_instance = 1;
+  have_instance = true;
 
   return 0;
 } /* }}} int varnish_config_instance */
index 8a44c26..fd20c77 100644 (file)
 
 #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>
 #include <libxml/tree.h>
 #include <libxml/xpath.h>
 #include <libxml/xpathInternals.h>
+#include <stdbool.h>
 
 /* Plugin name */
 #define PLUGIN_NAME "virt"
 
+/* Secure strcat macro assuring null termination. Parameter (n) is the size of
+   buffer (d), allowing this macro to be safe for static and dynamic buffers */
+#define SSTRNCAT(d, s, n)                                                      \
+  do {                                                                         \
+    size_t _l = strlen(d);                                                     \
+    sstrncpy((d) + _l, (s), (n)-_l);                                           \
+  } while (0)
+
 #ifdef LIBVIR_CHECK_VERSION
 
 #if LIBVIR_CHECK_VERSION(0, 9, 2)
 #define HAVE_DOM_REASON_RUNNING_WAKEUP 1
 #endif
 
+/*
+  virConnectListAllDomains() appeared in 0.10.2
+  Note that LIBVIR_CHECK_VERSION appeared a year later, so
+  in some systems which actually have virConnectListAllDomains()
+  we can't detect this.
+ */
+#if LIBVIR_CHECK_VERSION(0, 10, 2)
+#define HAVE_LIST_ALL_DOMAINS 1
+#endif
+
 #if LIBVIR_CHECK_VERSION(1, 0, 1)
 #define HAVE_DOM_REASON_PAUSED_SNAPSHOT 1
 #endif
 
 #endif /* LIBVIR_CHECK_VERSION */
 
+/* structure used for aggregating notification-thread data*/
+typedef struct virt_notif_thread_s {
+  pthread_t event_loop_tid;
+  int domain_event_cb_id;
+  pthread_mutex_t active_mutex; /* protects 'is_active' member access*/
+  bool is_active;
+} virt_notif_thread_t;
+
 static const char *config_keys[] = {"Connection",
 
                                     "RefreshInterval",
@@ -102,14 +129,29 @@ static const char *config_keys[] = {"Connection",
                                     "IgnoreSelected",
 
                                     "HostnameFormat",
+                                    "HostnameMetadataNS",
+                                    "HostnameMetadataXPath",
                                     "InterfaceFormat",
 
                                     "PluginInstanceFormat",
 
                                     "Instances",
                                     "ExtraStats",
+                                    "PersistentNotification",
+
+                                    "ReportBlockDevices",
+                                    "ReportNetworkInterfaces",
                                     NULL};
 
+/* PersistentNotification is false by default */
+static bool persistent_notification = false;
+
+static bool report_block_devices = true;
+static bool report_network_interfaces = true;
+
+/* Thread used for handling libvirt notifications events */
+static virt_notif_thread_t notif_thread;
+
 const char *domain_states[] = {
         [VIR_DOMAIN_NOSTATE] = "no state",
         [VIR_DOMAIN_RUNNING] = "the domain is running",
@@ -124,7 +166,202 @@ const char *domain_states[] = {
 #endif
 };
 
+static int map_domain_event_to_state(int event) {
+  int ret;
+  switch (event) {
+  case VIR_DOMAIN_EVENT_STARTED:
+    ret = VIR_DOMAIN_RUNNING;
+    break;
+  case VIR_DOMAIN_EVENT_SUSPENDED:
+    ret = VIR_DOMAIN_PAUSED;
+    break;
+  case VIR_DOMAIN_EVENT_RESUMED:
+    ret = VIR_DOMAIN_RUNNING;
+    break;
+  case VIR_DOMAIN_EVENT_STOPPED:
+    ret = VIR_DOMAIN_SHUTOFF;
+    break;
+  case VIR_DOMAIN_EVENT_SHUTDOWN:
+    ret = VIR_DOMAIN_SHUTDOWN;
+    break;
+#ifdef HAVE_DOM_STATE_PMSUSPENDED
+  case VIR_DOMAIN_EVENT_PMSUSPENDED:
+    ret = VIR_DOMAIN_PMSUSPENDED;
+    break;
+#endif
+#ifdef HAVE_DOM_REASON_CRASHED
+  case VIR_DOMAIN_EVENT_CRASHED:
+    ret = VIR_DOMAIN_CRASHED;
+    break;
+#endif
+  default:
+    ret = VIR_DOMAIN_NOSTATE;
+  }
+  return ret;
+}
+
 #ifdef HAVE_DOM_REASON
+static int map_domain_event_detail_to_reason(int event, int detail) {
+  int ret;
+  switch (event) {
+  case VIR_DOMAIN_EVENT_STARTED:
+    switch (detail) {
+    case VIR_DOMAIN_EVENT_STARTED_BOOTED: /* Normal startup from boot */
+      ret = VIR_DOMAIN_RUNNING_BOOTED;
+      break;
+    case VIR_DOMAIN_EVENT_STARTED_MIGRATED: /* Incoming migration from another
+                                               host */
+      ret = VIR_DOMAIN_RUNNING_MIGRATED;
+      break;
+    case VIR_DOMAIN_EVENT_STARTED_RESTORED: /* Restored from a state file */
+      ret = VIR_DOMAIN_RUNNING_RESTORED;
+      break;
+    case VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT: /* Restored from snapshot */
+      ret = VIR_DOMAIN_RUNNING_FROM_SNAPSHOT;
+      break;
+#ifdef HAVE_DOM_REASON_RUNNING_WAKEUP
+    case VIR_DOMAIN_EVENT_STARTED_WAKEUP: /* Started due to wakeup event */
+      ret = VIR_DOMAIN_RUNNING_WAKEUP;
+      break;
+#endif
+    default:
+      ret = VIR_DOMAIN_RUNNING_UNKNOWN;
+    }
+    break;
+  case VIR_DOMAIN_EVENT_SUSPENDED:
+    switch (detail) {
+    case VIR_DOMAIN_EVENT_SUSPENDED_PAUSED: /* Normal suspend due to admin
+                                               pause */
+      ret = VIR_DOMAIN_PAUSED_USER;
+      break;
+    case VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED: /* Suspended for offline
+                                                 migration */
+      ret = VIR_DOMAIN_PAUSED_MIGRATION;
+      break;
+    case VIR_DOMAIN_EVENT_SUSPENDED_IOERROR: /* Suspended due to a disk I/O
+                                                error */
+      ret = VIR_DOMAIN_PAUSED_IOERROR;
+      break;
+    case VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG: /* Suspended due to a watchdog
+                                                 firing */
+      ret = VIR_DOMAIN_PAUSED_WATCHDOG;
+      break;
+    case VIR_DOMAIN_EVENT_SUSPENDED_RESTORED: /* Restored from paused state
+                                                 file */
+      ret = VIR_DOMAIN_PAUSED_UNKNOWN;
+      break;
+    case VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT: /* Restored from paused
+                                                      snapshot */
+      ret = VIR_DOMAIN_PAUSED_FROM_SNAPSHOT;
+      break;
+    case VIR_DOMAIN_EVENT_SUSPENDED_API_ERROR: /* Suspended after failure during
+                                                  libvirt API call */
+      ret = VIR_DOMAIN_PAUSED_UNKNOWN;
+      break;
+#ifdef HAVE_DOM_REASON_POSTCOPY
+    case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY: /* Suspended for post-copy
+                                                 migration */
+      ret = VIR_DOMAIN_PAUSED_POSTCOPY;
+      break;
+    case VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY_FAILED: /* Suspended after failed
+                                                        post-copy */
+      ret = VIR_DOMAIN_PAUSED_POSTCOPY_FAILED;
+      break;
+#endif
+    default:
+      ret = VIR_DOMAIN_PAUSED_UNKNOWN;
+    }
+    break;
+  case VIR_DOMAIN_EVENT_RESUMED:
+    switch (detail) {
+    case VIR_DOMAIN_EVENT_RESUMED_UNPAUSED: /* Normal resume due to admin
+                                               unpause */
+      ret = VIR_DOMAIN_RUNNING_UNPAUSED;
+      break;
+    case VIR_DOMAIN_EVENT_RESUMED_MIGRATED: /* Resumed for completion of
+                                               migration */
+      ret = VIR_DOMAIN_RUNNING_MIGRATED;
+      break;
+    case VIR_DOMAIN_EVENT_RESUMED_FROM_SNAPSHOT: /* Resumed from snapshot */
+      ret = VIR_DOMAIN_RUNNING_FROM_SNAPSHOT;
+      break;
+#ifdef HAVE_DOM_REASON_POSTCOPY
+    case VIR_DOMAIN_EVENT_RESUMED_POSTCOPY: /* Resumed, but migration is still
+                                               running in post-copy mode */
+      ret = VIR_DOMAIN_RUNNING_POSTCOPY;
+      break;
+#endif
+    default:
+      ret = VIR_DOMAIN_RUNNING_UNKNOWN;
+    }
+    break;
+  case VIR_DOMAIN_EVENT_STOPPED:
+    switch (detail) {
+    case VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN: /* Normal shutdown */
+      ret = VIR_DOMAIN_SHUTOFF_SHUTDOWN;
+      break;
+    case VIR_DOMAIN_EVENT_STOPPED_DESTROYED: /* Forced poweroff from host */
+      ret = VIR_DOMAIN_SHUTOFF_DESTROYED;
+      break;
+    case VIR_DOMAIN_EVENT_STOPPED_CRASHED: /* Guest crashed */
+      ret = VIR_DOMAIN_SHUTOFF_CRASHED;
+      break;
+    case VIR_DOMAIN_EVENT_STOPPED_MIGRATED: /* Migrated off to another host */
+      ret = VIR_DOMAIN_SHUTOFF_MIGRATED;
+      break;
+    case VIR_DOMAIN_EVENT_STOPPED_SAVED: /* Saved to a state file */
+      ret = VIR_DOMAIN_SHUTOFF_SAVED;
+      break;
+    case VIR_DOMAIN_EVENT_STOPPED_FAILED: /* Host emulator/mgmt failed */
+      ret = VIR_DOMAIN_SHUTOFF_FAILED;
+      break;
+    case VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT: /* Offline snapshot loaded */
+      ret = VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT;
+      break;
+    default:
+      ret = VIR_DOMAIN_SHUTOFF_UNKNOWN;
+    }
+    break;
+  case VIR_DOMAIN_EVENT_SHUTDOWN:
+    switch (detail) {
+    case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED: /* Guest finished shutdown
+                                                sequence */
+      ret = VIR_DOMAIN_SHUTDOWN_USER;
+      break;
+    default:
+      ret = VIR_DOMAIN_SHUTDOWN_UNKNOWN;
+    }
+    break;
+#ifdef HAVE_DOM_STATE_PMSUSPENDED
+  case VIR_DOMAIN_EVENT_PMSUSPENDED:
+    switch (detail) {
+    case VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY: /* Guest was PM suspended to
+                                                 memory */
+      ret = VIR_DOMAIN_PMSUSPENDED_UNKNOWN;
+      break;
+    case VIR_DOMAIN_EVENT_PMSUSPENDED_DISK: /* Guest was PM suspended to disk */
+      ret = VIR_DOMAIN_PMSUSPENDED_DISK_UNKNOWN;
+      break;
+    default:
+      ret = VIR_DOMAIN_PMSUSPENDED_UNKNOWN;
+    }
+    break;
+#endif
+  case VIR_DOMAIN_EVENT_CRASHED:
+    switch (detail) {
+    case VIR_DOMAIN_EVENT_CRASHED_PANICKED: /* Guest was panicked */
+      ret = VIR_DOMAIN_CRASHED_PANICKED;
+      break;
+    default:
+      ret = VIR_DOMAIN_CRASHED_UNKNOWN;
+    }
+    break;
+  default:
+    ret = VIR_DOMAIN_NOSTATE_UNKNOWN;
+  }
+  return ret;
+}
+
 #define DOMAIN_STATE_REASON_MAX_SIZE 20
 const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
         [VIR_DOMAIN_NOSTATE][VIR_DOMAIN_NOSTATE_UNKNOWN] =
@@ -158,7 +395,6 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
         [VIR_DOMAIN_RUNNING][VIR_DOMAIN_RUNNING_POSTCOPY] =
             "running in post-copy migration mode",
 #endif
-
         [VIR_DOMAIN_BLOCKED][VIR_DOMAIN_BLOCKED_UNKNOWN] =
             "the reason is unknown",
 
@@ -198,7 +434,6 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
         [VIR_DOMAIN_PAUSED][VIR_DOMAIN_PAUSED_POSTCOPY_FAILED] =
             "paused after failed post-copy",
 #endif
-
         [VIR_DOMAIN_SHUTDOWN][VIR_DOMAIN_SHUTDOWN_UNKNOWN] =
             "the reason is unknown",
         [VIR_DOMAIN_SHUTDOWN][VIR_DOMAIN_SHUTDOWN_USER] =
@@ -237,12 +472,12 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
   do {                                                                         \
     status = _f(__VA_ARGS__);                                                  \
     if (status != 0)                                                           \
-      ERROR(PLUGIN_NAME ": Failed to get " _name);                             \
+      ERROR(PLUGIN_NAME " plugin: Failed to get " _name);                      \
   } while (0)
 
 /* Connection. */
-static virConnectPtr conn = 0;
-static char *conn_string = NULL;
+static virConnectPtr conn;
+static char *conn_string;
 static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
 
 /* Node information required for %CPU */
@@ -252,11 +487,11 @@ static virNodeInfo nodeinfo;
 static int interval = 60;
 
 /* List of domains, if specified. */
-static ignorelist_t *il_domains = NULL;
+static ignorelist_t *il_domains;
 /* List of block devices, if specified. */
-static ignorelist_t *il_block_devices = NULL;
+static ignorelist_t *il_block_devices;
 /* List of network interface devices, if specified. */
-static ignorelist_t *il_interface_devices = NULL;
+static ignorelist_t *il_interface_devices;
 
 static int ignore_device_match(ignorelist_t *, const char *domname,
                                const char *devpath);
@@ -278,6 +513,7 @@ struct interface_device {
 typedef struct domain_s {
   virDomainPtr ptr;
   virDomainInfo info;
+  bool active;
 } domain_t;
 
 struct lv_read_state {
@@ -293,7 +529,8 @@ struct lv_read_state {
 };
 
 static void free_domains(struct lv_read_state *state);
-static int add_domain(struct lv_read_state *state, virDomainPtr dom);
+static int add_domain(struct lv_read_state *state, virDomainPtr dom,
+                      bool active);
 
 static void free_block_devices(struct lv_read_state *state);
 static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
@@ -328,20 +565,29 @@ static int nr_instances = NR_INSTANCES_DEFAULT;
 static struct lv_user_data lv_read_user_data[NR_INSTANCES_MAX];
 
 /* HostnameFormat. */
-#define HF_MAX_FIELDS 3
+#define HF_MAX_FIELDS 4
 
-enum hf_field { hf_none = 0, hf_hostname, hf_name, hf_uuid };
+enum hf_field { hf_none = 0, hf_hostname, hf_name, hf_uuid, hf_metadata };
 
 static enum hf_field hostname_format[HF_MAX_FIELDS] = {hf_name};
 
 /* PluginInstanceFormat */
-#define PLGINST_MAX_FIELDS 2
+#define PLGINST_MAX_FIELDS 3
 
-enum plginst_field { plginst_none = 0, plginst_name, plginst_uuid };
+enum plginst_field {
+  plginst_none = 0,
+  plginst_name,
+  plginst_uuid,
+  plginst_metadata
+};
 
 static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] = {
     plginst_none};
 
+/* HostnameMetadataNS && HostnameMetadataXPath */
+static char *hm_xpath;
+static char *hm_ns;
+
 /* BlockDeviceFormat */
 enum bd_field { target, source };
 
@@ -401,7 +647,7 @@ static const struct ex_stats_item ex_stats_table[] = {
 };
 
 /* BlockDeviceFormatBasename */
-_Bool blockdevice_format_basename = 0;
+static bool blockdevice_format_basename;
 static enum bd_field blockdevice_format = target;
 static enum if_field interface_format = if_name;
 
@@ -476,8 +722,96 @@ static int get_block_info(struct lv_block_info *binfo,
       ERROR(PLUGIN_NAME " plugin: %s failed: %s", (s), err->message);          \
   } while (0)
 
+char *metadata_get_hostname(virDomainPtr dom) {
+  const char *xpath_str = NULL;
+  if (hm_xpath == NULL)
+    xpath_str = "/instance/name/text()";
+  else
+    xpath_str = hm_xpath;
+
+  const char *namespace = NULL;
+  if (hm_ns == NULL) {
+    namespace = "http://openstack.org/xmlns/libvirt/nova/1.0";
+  } else {
+    namespace = hm_ns;
+  }
+
+  char *metadata_str = virDomainGetMetadata(
+      dom, VIR_DOMAIN_METADATA_ELEMENT, namespace, VIR_DOMAIN_AFFECT_CURRENT);
+  if (metadata_str == NULL) {
+    return NULL;
+  }
+
+  char *hostname = NULL;
+  xmlXPathContextPtr xpath_ctx = NULL;
+  xmlXPathObjectPtr xpath_obj = NULL;
+  xmlNodePtr xml_node = NULL;
+
+  xmlDocPtr xml_doc =
+      xmlReadDoc((xmlChar *)metadata_str, NULL, NULL, XML_PARSE_NONET);
+  if (xml_doc == NULL) {
+    ERROR(PLUGIN_NAME " plugin: xmlReadDoc failed to read metadata");
+    goto metadata_end;
+  }
+
+  xpath_ctx = xmlXPathNewContext(xml_doc);
+  if (xpath_ctx == NULL) {
+    ERROR(PLUGIN_NAME " plugin: xmlXPathNewContext(%s) failed for metadata",
+          metadata_str);
+    goto metadata_end;
+  }
+  xpath_obj = xmlXPathEval((xmlChar *)xpath_str, xpath_ctx);
+  if (xpath_obj == NULL) {
+    ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) failed for metadata",
+          xpath_str);
+    goto metadata_end;
+  }
+
+  if (xpath_obj->type != XPATH_NODESET) {
+    ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unexpected return type %d "
+                      "(wanted %d) for metadata",
+          xpath_str, xpath_obj->type, XPATH_NODESET);
+    goto metadata_end;
+  }
+
+  // TODO(sileht): We can support || operator by looping on nodes here
+  if (xpath_obj->nodesetval == NULL || xpath_obj->nodesetval->nodeNr != 1) {
+    WARNING(PLUGIN_NAME " plugin: xmlXPathEval(%s) return nodeset size=%i "
+                        "expected=1 for metadata",
+            xpath_str,
+            (xpath_obj->nodesetval == NULL) ? 0
+                                            : xpath_obj->nodesetval->nodeNr);
+    goto metadata_end;
+  }
+
+  xml_node = xpath_obj->nodesetval->nodeTab[0];
+  if (xml_node->type == XML_TEXT_NODE) {
+    hostname = strdup((const char *)xml_node->content);
+  } else if (xml_node->type == XML_ATTRIBUTE_NODE) {
+    hostname = strdup((const char *)xml_node->children->content);
+  } else {
+    ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unsupported node type %d",
+          xpath_str, xml_node->type);
+    goto metadata_end;
+  }
+
+  if (hostname == NULL) {
+    ERROR(PLUGIN_NAME " plugin: strdup(%s) hostname failed", xpath_str);
+    goto metadata_end;
+  }
+
+metadata_end:
+  if (xpath_obj)
+    xmlXPathFreeObject(xpath_obj);
+  if (xpath_ctx)
+    xmlXPathFreeContext(xpath_ctx);
+  if (xml_doc)
+    xmlFreeDoc(xml_doc);
+  sfree(metadata_str);
+  return hostname;
+}
+
 static void init_value_list(value_list_t *vl, virDomainPtr dom) {
-  int n;
   const char *name;
   char uuid[VIR_UUID_STRING_BUFLEN];
 
@@ -490,44 +824,39 @@ static void init_value_list(value_list_t *vl, virDomainPtr dom) {
     if (hostname_format[i] == hf_none)
       continue;
 
-    n = DATA_MAX_NAME_LEN - strlen(vl->host) - 2;
-
-    if (i > 0 && n >= 1) {
-      strncat(vl->host, ":", 1);
-      n--;
-    }
+    if (i > 0)
+      SSTRNCAT(vl->host, ":", sizeof(vl->host));
 
     switch (hostname_format[i]) {
     case hf_none:
       break;
     case hf_hostname:
-      strncat(vl->host, hostname_g, n);
+      SSTRNCAT(vl->host, hostname_g, sizeof(vl->host));
       break;
     case hf_name:
       name = virDomainGetName(dom);
       if (name)
-        strncat(vl->host, name, n);
+        SSTRNCAT(vl->host, name, sizeof(vl->host));
       break;
     case hf_uuid:
       if (virDomainGetUUIDString(dom, uuid) == 0)
-        strncat(vl->host, uuid, n);
+        SSTRNCAT(vl->host, uuid, sizeof(vl->host));
+      break;
+    case hf_metadata:
+      name = metadata_get_hostname(dom);
+      if (name)
+        SSTRNCAT(vl->host, name, sizeof(vl->host));
       break;
     }
   }
 
-  vl->host[sizeof(vl->host) - 1] = '\0';
-
   /* Construct the plugin instance field according to PluginInstanceFormat. */
   for (int i = 0; i < PLGINST_MAX_FIELDS; ++i) {
     if (plugin_instance_format[i] == plginst_none)
       continue;
 
-    n = sizeof(vl->plugin_instance) - strlen(vl->plugin_instance) - 2;
-
-    if (i > 0 && n >= 1) {
-      strncat(vl->plugin_instance, ":", 1);
-      n--;
-    }
+    if (i > 0)
+      SSTRNCAT(vl->plugin_instance, ":", sizeof(vl->plugin_instance));
 
     switch (plugin_instance_format[i]) {
     case plginst_none:
@@ -535,17 +864,20 @@ static void init_value_list(value_list_t *vl, virDomainPtr dom) {
     case plginst_name:
       name = virDomainGetName(dom);
       if (name)
-        strncat(vl->plugin_instance, name, n);
+        SSTRNCAT(vl->plugin_instance, name, sizeof(vl->plugin_instance));
       break;
     case plginst_uuid:
       if (virDomainGetUUIDString(dom, uuid) == 0)
-        strncat(vl->plugin_instance, uuid, n);
+        SSTRNCAT(vl->plugin_instance, uuid, sizeof(vl->plugin_instance));
+      break;
+    case plginst_metadata:
+      name = metadata_get_hostname(dom);
+      if (name)
+        SSTRNCAT(vl->plugin_instance, name, sizeof(vl->plugin_instance));
       break;
     }
   }
 
-  vl->plugin_instance[sizeof(vl->plugin_instance) - 1] = '\0';
-
 } /* void init_value_list */
 
 static int init_notif(notification_t *notif, const virDomainPtr domain,
@@ -554,7 +886,7 @@ static int init_notif(notification_t *notif, const virDomainPtr domain,
   value_list_t vl = VALUE_LIST_INIT;
 
   if (!notif) {
-    ERROR(PLUGIN_NAME ": init_notif: NULL pointer");
+    ERROR(PLUGIN_NAME " plugin: init_notif: NULL pointer");
     return -1;
   }
 
@@ -633,10 +965,11 @@ static double cpu_ns_to_percent(unsigned int node_cpus,
               (time_diff_sec * node_cpus * NANOSEC_IN_SEC);
   }
 
-  DEBUG(PLUGIN_NAME ": node_cpus=%u cpu_time_old=%llu cpu_time_new=%llu"
-                    "cpu_time_diff=%llu time_diff_sec=%f percent=%f",
-        node_cpus, cpu_time_old, cpu_time_new, cpu_time_diff, time_diff_sec,
-        percent);
+  DEBUG(PLUGIN_NAME " plugin: node_cpus=%u cpu_time_old=%" PRIu64
+                    " cpu_time_new=%" PRIu64 "cpu_time_diff=%" PRIu64
+                    " time_diff_sec=%f percent=%f",
+        node_cpus, (uint64_t)cpu_time_old, (uint64_t)cpu_time_new,
+        (uint64_t)cpu_time_diff, time_diff_sec, percent);
 
   return percent;
 }
@@ -727,25 +1060,26 @@ static unsigned int parse_ex_stats_flags(char **exstats, int numexstats) {
       }
 
       if (ex_stats_table[j + 1].name == NULL) {
-        ERROR(PLUGIN_NAME ": Unmatched ExtraStats option: %s", exstats[i]);
+        ERROR(PLUGIN_NAME " plugin: Unmatched ExtraStats option: %s",
+              exstats[i]);
       }
     }
   }
   return ex_stats_flags;
 }
 
-static void domain_state_submit(virDomainPtr dom, int state, int reason) {
-
-  if ((state < 0) || (state >= STATIC_ARRAY_SIZE(domain_states))) {
-    ERROR(PLUGIN_NAME ": Array index out of bounds: state=%d", state);
+static void domain_state_submit_notif(virDomainPtr dom, int state, int reason) {
+  if ((state < 0) || ((size_t)state >= STATIC_ARRAY_SIZE(domain_states))) {
+    ERROR(PLUGIN_NAME " plugin: Array index out of bounds: state=%d", state);
     return;
   }
 
   char msg[DATA_MAX_NAME_LEN];
   const char *state_str = domain_states[state];
 #ifdef HAVE_DOM_REASON
-  if ((reason < 0) || (reason >= STATIC_ARRAY_SIZE(domain_reasons[0]))) {
-    ERROR(PLUGIN_NAME ": Array index out of bounds: reason=%d", reason);
+  if ((reason < 0) ||
+      ((size_t)reason >= STATIC_ARRAY_SIZE(domain_reasons[0]))) {
+    ERROR(PLUGIN_NAME " plugin: Array index out of bounds: reason=%d", reason);
     return;
   }
 
@@ -754,8 +1088,8 @@ static void domain_state_submit(virDomainPtr dom, int state, int reason) {
    * have different number of reasons. We need to check if reason was
    * successfully parsed */
   if (!reason_str) {
-    ERROR(PLUGIN_NAME ": Invalid reason (%d) for domain state: %s", reason,
-          state_str);
+    ERROR(PLUGIN_NAME " plugin: Invalid reason (%d) for domain state: %s",
+          reason, state_str);
     return;
   }
 #else
@@ -784,16 +1118,13 @@ static void domain_state_submit(virDomainPtr dom, int state, int reason) {
     severity = NOTIF_FAILURE;
     break;
   default:
-    ERROR(PLUGIN_NAME ": Unrecognized domain state (%d)", state);
+    ERROR(PLUGIN_NAME " plugin: Unrecognized domain state (%d)", state);
     return;
   }
   submit_notif(dom, severity, msg, "domain_state", NULL);
 }
 
-static int lv_config(const char *key, const char *value) {
-  if (virInitialize() != 0)
-    return 1;
-
+static int lv_init_ignorelists() {
   if (il_domains == NULL)
     il_domains = ignorelist_create(1);
   if (il_block_devices == NULL)
@@ -801,6 +1132,19 @@ static int lv_config(const char *key, const char *value) {
   if (il_interface_devices == NULL)
     il_interface_devices = ignorelist_create(1);
 
+  if (!il_domains || !il_block_devices || !il_interface_devices)
+    return 1;
+
+  return 0;
+}
+
+static int lv_config(const char *key, const char *value) {
+  if (virInitialize() != 0)
+    return 1;
+
+  if (lv_init_ignorelists() != 0)
+    return 1;
+
   if (strcasecmp(key, "Connection") == 0) {
     char *tmp = strdup(value);
     if (tmp == NULL) {
@@ -843,7 +1187,7 @@ static int lv_config(const char *key, const char *value) {
     return 0;
   }
   if (strcasecmp(key, "BlockDeviceFormatBasename") == 0) {
-    blockdevice_format_basename = IS_TRUE(value);
+    blockdevice_format_basename = IS_TRUE(value) ? true : false;
     return 0;
   }
   if (strcasecmp(key, "InterfaceDevice") == 0) {
@@ -865,18 +1209,37 @@ static int lv_config(const char *key, const char *value) {
     return 0;
   }
 
-  if (strcasecmp(key, "HostnameFormat") == 0) {
-    char *value_copy;
-    char *fields[HF_MAX_FIELDS];
-    int n;
+  if (strcasecmp(key, "HostnameMetadataNS") == 0) {
+    char *tmp = strdup(value);
+    if (tmp == NULL) {
+      ERROR(PLUGIN_NAME " plugin: HostnameMetadataNS strdup failed.");
+      return 1;
+    }
+    sfree(hm_ns);
+    hm_ns = tmp;
+    return 0;
+  }
+
+  if (strcasecmp(key, "HostnameMetadataXPath") == 0) {
+    char *tmp = strdup(value);
+    if (tmp == NULL) {
+      ERROR(PLUGIN_NAME " plugin: HostnameMetadataXPath strdup failed.");
+      return 1;
+    }
+    sfree(hm_xpath);
+    hm_xpath = tmp;
+    return 0;
+  }
 
-    value_copy = strdup(value);
+  if (strcasecmp(key, "HostnameFormat") == 0) {
+    char *value_copy = strdup(value);
     if (value_copy == NULL) {
       ERROR(PLUGIN_NAME " plugin: strdup failed.");
       return -1;
     }
 
-    n = strsplit(value_copy, fields, HF_MAX_FIELDS);
+    char *fields[HF_MAX_FIELDS];
+    int n = strsplit(value_copy, fields, HF_MAX_FIELDS);
     if (n < 1) {
       sfree(value_copy);
       ERROR(PLUGIN_NAME " plugin: HostnameFormat: no fields");
@@ -890,6 +1253,8 @@ static int lv_config(const char *key, const char *value) {
         hostname_format[i] = hf_name;
       else if (strcasecmp(fields[i], "uuid") == 0)
         hostname_format[i] = hf_uuid;
+      else if (strcasecmp(fields[i], "metadata") == 0)
+        hostname_format[i] = hf_metadata;
       else {
         ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s",
               fields[i]);
@@ -906,17 +1271,14 @@ static int lv_config(const char *key, const char *value) {
   }
 
   if (strcasecmp(key, "PluginInstanceFormat") == 0) {
-    char *value_copy;
-    char *fields[PLGINST_MAX_FIELDS];
-    int n;
-
-    value_copy = strdup(value);
+    char *value_copy = strdup(value);
     if (value_copy == NULL) {
       ERROR(PLUGIN_NAME " plugin: strdup failed.");
       return -1;
     }
 
-    n = strsplit(value_copy, fields, PLGINST_MAX_FIELDS);
+    char *fields[PLGINST_MAX_FIELDS];
+    int n = strsplit(value_copy, fields, PLGINST_MAX_FIELDS);
     if (n < 1) {
       sfree(value_copy);
       ERROR(PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
@@ -931,6 +1293,8 @@ static int lv_config(const char *key, const char *value) {
         plugin_instance_format[i] = plginst_name;
       else if (strcasecmp(fields[i], "uuid") == 0)
         plugin_instance_format[i] = plginst_uuid;
+      else if (strcasecmp(fields[i], "metadata") == 0)
+        plugin_instance_format[i] = plginst_metadata;
       else {
         ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s",
               fields[i]);
@@ -1005,6 +1369,21 @@ static int lv_config(const char *key, const char *value) {
     }
   }
 
+  if (strcasecmp(key, "PersistentNotification") == 0) {
+    persistent_notification = IS_TRUE(value);
+    return 0;
+  }
+
+  if (strcasecmp(key, "ReportBlockDevices") == 0) {
+    report_block_devices = IS_TRUE(value);
+    return 0;
+  }
+
+  if (strcasecmp(key, "ReportNetworkInterfaces") == 0) {
+    report_network_interfaces = IS_TRUE(value);
+    return 0;
+  }
+
   /* Unrecognised option. */
   return -1;
 }
@@ -1027,7 +1406,7 @@ static int lv_connect(void) {
     }
     int status = virNodeGetInfo(conn, &nodeinfo);
     if (status != 0) {
-      ERROR(PLUGIN_NAME ": virNodeGetInfo failed");
+      ERROR(PLUGIN_NAME " plugin: virNodeGetInfo failed");
       return -1;
     }
   }
@@ -1053,7 +1432,7 @@ static int lv_domain_block_info(virDomainPtr dom, const char *path,
     return -1;
   }
 
-  virTypedParameterPtr params = calloc((size_t)nparams, sizeof(*params));
+  virTypedParameterPtr params = calloc(nparams, sizeof(*params));
   if (params == NULL) {
     ERROR("virt plugin: alloc(%i) for block=%s parameters failed.", nparams,
           path);
@@ -1112,7 +1491,7 @@ static void vcpu_pin_submit(virDomainPtr dom, int max_cpus, int vcpu,
                             unsigned char *cpu_maps, int cpu_map_len) {
   for (int cpu = 0; cpu < max_cpus; ++cpu) {
     char type_instance[DATA_MAX_NAME_LEN];
-    _Bool is_set = VIR_CPU_USABLE(cpu_maps, cpu_map_len, vcpu, cpu) ? 1 : 0;
+    bool is_set = VIR_CPU_USABLE(cpu_maps, cpu_map_len, vcpu, cpu);
 
     snprintf(type_instance, sizeof(type_instance), "vcpu_%d-cpu_%d", vcpu, cpu);
     submit(dom, "cpu_affinity", type_instance, &(value_t){.gauge = is_set}, 1);
@@ -1123,7 +1502,7 @@ static int get_vcpu_stats(virDomainPtr domain, unsigned short nr_virt_cpu) {
   int max_cpus = VIR_NODEINFO_MAXCPUS(nodeinfo);
   int cpu_map_len = VIR_CPU_MAPLEN(max_cpus);
 
-  virVcpuInfoPtr vinfo = calloc(nr_virt_cpu, sizeof(vinfo[0]));
+  virVcpuInfoPtr vinfo = calloc(nr_virt_cpu, sizeof(*vinfo));
   if (vinfo == NULL) {
     ERROR(PLUGIN_NAME " plugin: calloc failed.");
     return -1;
@@ -1165,7 +1544,7 @@ static int get_pcpu_stats(virDomainPtr dom) {
     return -1;
   }
 
-  virTypedParameterPtr param = calloc(nparams, sizeof(virTypedParameter));
+  virTypedParameterPtr param = calloc(nparams, sizeof(*param));
   if (param == NULL) {
     ERROR(PLUGIN_NAME " plugin: alloc(%i) for cpu parameters failed.", nparams);
     return -1;
@@ -1201,6 +1580,15 @@ static int get_pcpu_stats(virDomainPtr dom) {
 #endif /* HAVE_CPU_STATS */
 
 #ifdef HAVE_DOM_REASON
+
+static void domain_state_submit(virDomainPtr dom, int state, int reason) {
+  value_t values[] = {
+      {.gauge = (gauge_t)state}, {.gauge = (gauge_t)reason},
+  };
+
+  submit(dom, "domain_state", NULL, values, STATIC_ARRAY_SIZE(values));
+}
+
 static int get_domain_state(virDomainPtr domain) {
   int domain_state = 0;
   int domain_reason = 0;
@@ -1213,15 +1601,35 @@ static int get_domain_state(virDomainPtr domain) {
   }
 
   domain_state_submit(domain, domain_state, domain_reason);
+
+  return status;
+}
+
+#ifdef HAVE_LIST_ALL_DOMAINS
+static int get_domain_state_notify(virDomainPtr domain) {
+  int domain_state = 0;
+  int domain_reason = 0;
+
+  int status = virDomainGetState(domain, &domain_state, &domain_reason, 0);
+  if (status != 0) {
+    ERROR(PLUGIN_NAME " plugin: virDomainGetState failed with status %i.",
+          status);
+    return status;
+  }
+
+  if (persistent_notification)
+    domain_state_submit_notif(domain, domain_state, domain_reason);
+
   return status;
 }
+#endif /* HAVE_LIST_ALL_DOMAINS */
 #endif /* HAVE_DOM_REASON */
 
 static int get_memory_stats(virDomainPtr domain) {
   virDomainMemoryStatPtr minfo =
-      calloc(VIR_DOMAIN_MEMORY_STAT_NR, sizeof(virDomainMemoryStatStruct));
+      calloc(VIR_DOMAIN_MEMORY_STAT_NR, sizeof(*minfo));
   if (minfo == NULL) {
-    ERROR("virt plugin: malloc failed.");
+    ERROR("virt plugin: calloc failed.");
     return -1;
   }
 
@@ -1313,7 +1721,7 @@ static int get_block_stats(struct block_device *block_dev) {
 
 #define NM_ADD_STR_ITEMS(_items, _size)                                        \
   do {                                                                         \
-    for (int _i = 0; _i < _size; ++_i) {                                       \
+    for (size_t _i = 0; _i < _size; ++_i) {                                    \
       DEBUG(PLUGIN_NAME                                                        \
             " plugin: Adding notification metadata name=%s value=%s",          \
             _items[_i].name, _items[_i].value);                                \
@@ -1338,7 +1746,7 @@ static int fs_info_notify(virDomainPtr domain, virDomainFSInfoPtr fs_info) {
       {.name = "name", .value = fs_info->name},
       {.name = "fstype", .value = fs_info->fstype}};
 
-  for (int i = 0; i < fs_info->ndevAlias; ++i) {
+  for (size_t i = 0; i < fs_info->ndevAlias; ++i) {
     fs_dev_alias[i].name = "devAlias";
     fs_dev_alias[i].value = fs_info->devAlias[i];
   }
@@ -1445,7 +1853,7 @@ static int get_job_stats(virDomainPtr domain) {
 
 static int get_domain_metrics(domain_t *domain) {
   if (!domain || !domain->ptr) {
-    ERROR(PLUGIN_NAME "plugin: get_domain_metrics: NULL pointer");
+    ERROR(PLUGIN_NAME " plugin: get_domain_metrics: NULL pointer");
     return -1;
   }
 
@@ -1464,10 +1872,6 @@ static int get_domain_metrics(domain_t *domain) {
      * We need to get it from virDomainGetState.
      */
     GET_STATS(get_domain_state, "domain reason", domain->ptr);
-#else
-    /* virDomainGetState is not available. Submit 0, which corresponds to
-     * unknown reason. */
-    domain_state_submit(domain->ptr, info.state, 0);
 #endif
   }
 
@@ -1510,6 +1914,7 @@ static int get_domain_metrics(domain_t *domain) {
 
   /* Update cached virDomainInfo. It has to be done after cpu_submit */
   memcpy(&domain->info, &info, sizeof(domain->info));
+
   return 0;
 }
 
@@ -1558,6 +1963,192 @@ static int get_if_dev_stats(struct interface_device *if_dev) {
   return 0;
 }
 
+static int domain_lifecycle_event_cb(__attribute__((unused)) virConnectPtr con_,
+                                     virDomainPtr dom, int event, int detail,
+                                     __attribute__((unused)) void *opaque) {
+  int domain_state = map_domain_event_to_state(event);
+  int domain_reason = 0; /* 0 means UNKNOWN reason for any state */
+#ifdef HAVE_DOM_REASON
+  domain_reason = map_domain_event_detail_to_reason(event, detail);
+#endif
+  domain_state_submit_notif(dom, domain_state, domain_reason);
+
+  return 0;
+}
+
+static int register_event_impl(void) {
+  if (virEventRegisterDefaultImpl() < 0) {
+    virErrorPtr err = virGetLastError();
+    ERROR(PLUGIN_NAME
+          " plugin: error while event implementation registering: %s",
+          err && err->message ? err->message : "Unknown error");
+    return -1;
+  }
+
+  return 0;
+}
+
+static void virt_notif_thread_set_active(virt_notif_thread_t *thread_data,
+                                         const bool active) {
+  assert(thread_data != NULL);
+  pthread_mutex_lock(&thread_data->active_mutex);
+  thread_data->is_active = active;
+  pthread_mutex_unlock(&thread_data->active_mutex);
+}
+
+static bool virt_notif_thread_is_active(virt_notif_thread_t *thread_data) {
+  bool active = false;
+
+  assert(thread_data != NULL);
+  pthread_mutex_lock(&thread_data->active_mutex);
+  active = thread_data->is_active;
+  pthread_mutex_unlock(&thread_data->active_mutex);
+
+  return active;
+}
+
+/* worker function running default event implementation */
+static void *event_loop_worker(void *arg) {
+  virt_notif_thread_t *thread_data = (virt_notif_thread_t *)arg;
+
+  while (virt_notif_thread_is_active(thread_data)) {
+    if (virEventRunDefaultImpl() < 0) {
+      virErrorPtr err = virGetLastError();
+      ERROR(PLUGIN_NAME " plugin: failed to run event loop: %s\n",
+            err && err->message ? err->message : "Unknown error");
+    }
+  }
+
+  return NULL;
+}
+
+static int virt_notif_thread_init(virt_notif_thread_t *thread_data) {
+  int ret;
+
+  assert(thread_data != NULL);
+  ret = pthread_mutex_init(&thread_data->active_mutex, NULL);
+  if (ret != 0) {
+    ERROR(PLUGIN_NAME " plugin: Failed to initialize mutex, err %u", ret);
+    return ret;
+  }
+
+  /**
+   * '0' and positive integers are meaningful ID's, therefore setting
+   * domain_event_cb_id to '-1'
+   */
+  thread_data->domain_event_cb_id = -1;
+  pthread_mutex_lock(&thread_data->active_mutex);
+  thread_data->is_active = false;
+  pthread_mutex_unlock(&thread_data->active_mutex);
+
+  return 0;
+}
+
+/* register domain event callback and start event loop thread */
+static int start_event_loop(virt_notif_thread_t *thread_data) {
+  assert(thread_data != NULL);
+  thread_data->domain_event_cb_id = virConnectDomainEventRegisterAny(
+      conn, NULL, VIR_DOMAIN_EVENT_ID_LIFECYCLE,
+      VIR_DOMAIN_EVENT_CALLBACK(domain_lifecycle_event_cb), NULL, NULL);
+  if (thread_data->domain_event_cb_id == -1) {
+    ERROR(PLUGIN_NAME " plugin: error while callback registering");
+    return -1;
+  }
+
+  virt_notif_thread_set_active(thread_data, 1);
+  if (pthread_create(&thread_data->event_loop_tid, NULL, event_loop_worker,
+                     thread_data)) {
+    ERROR(PLUGIN_NAME " plugin: failed event loop thread creation");
+    virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id);
+    return -1;
+  }
+
+  return 0;
+}
+
+/* stop event loop thread and deregister callback */
+static void stop_event_loop(virt_notif_thread_t *thread_data) {
+  /* stopping loop and de-registering event handler*/
+  virt_notif_thread_set_active(thread_data, 0);
+  if (conn != NULL && thread_data->domain_event_cb_id != -1)
+    virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id);
+
+  if (pthread_join(notif_thread.event_loop_tid, NULL) != 0)
+    ERROR(PLUGIN_NAME " plugin: stopping notification thread failed");
+}
+
+static int persistent_domains_state_notification(void) {
+  int status = 0;
+  int n;
+#ifdef HAVE_LIST_ALL_DOMAINS
+  virDomainPtr *domains = NULL;
+  n = virConnectListAllDomains(conn, &domains,
+                               VIR_CONNECT_LIST_DOMAINS_PERSISTENT);
+  if (n < 0) {
+    VIRT_ERROR(conn, "reading list of persistent domains");
+    status = -1;
+  } else {
+    DEBUG(PLUGIN_NAME " plugin: getting state of %i persistent domains", n);
+    /* Fetch each persistent domain's state and notify it */
+    int n_notified = n;
+    for (int i = 0; i < n; ++i) {
+      status = get_domain_state_notify(domains[i]);
+      if (status != 0) {
+        n_notified--;
+        ERROR(PLUGIN_NAME " plugin: could not notify state of domain %s",
+              virDomainGetName(domains[i]));
+      }
+      virDomainFree(domains[i]);
+    }
+
+    sfree(domains);
+    DEBUG(PLUGIN_NAME " plugin: notified state of %i persistent domains",
+          n_notified);
+  }
+#else
+  n = virConnectNumOfDomains(conn);
+  if (n > 0) {
+    int *domids;
+    /* Get list of domains. */
+    domids = calloc(n, sizeof(*domids));
+    if (domids == NULL) {
+      ERROR(PLUGIN_NAME " plugin: calloc failed.");
+      return -1;
+    }
+    n = virConnectListDomains(conn, domids, n);
+    if (n < 0) {
+      VIRT_ERROR(conn, "reading list of domains");
+      sfree(domids);
+      return -1;
+    }
+    /* Fetch info of each active domain and notify it */
+    for (int i = 0; i < n; ++i) {
+      virDomainInfo info;
+      virDomainPtr dom = NULL;
+      dom = virDomainLookupByID(conn, domids[i]);
+      if (dom == NULL) {
+        VIRT_ERROR(conn, "virDomainLookupByID");
+        /* Could be that the domain went away -- ignore it anyway. */
+        continue;
+      }
+      status = virDomainGetInfo(dom, &info);
+      if (status == 0)
+        /* virDomainGetState is not available. Submit 0, which corresponds to
+         * unknown reason. */
+        domain_state_submit_notif(dom, info.state, 0);
+      else
+        ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
+              status);
+
+      virDomainFree(dom);
+    }
+    sfree(domids);
+  }
+#endif
+
+  return status;
+}
+
 static int lv_read(user_data_t *ud) {
   time_t t;
   struct lv_read_instance *inst = NULL;
@@ -1571,9 +2162,19 @@ static int lv_read(user_data_t *ud) {
   inst = ud->data;
   state = &inst->read_state;
 
+  bool reconnect = conn == NULL ? true : false;
+  /* event implementation must be registered before connection is opened */
   if (inst->id == 0) {
+    if (!persistent_notification && reconnect)
+      if (register_event_impl() != 0)
+        return -1;
+
     if (lv_connect() < 0)
       return -1;
+
+    if (!persistent_notification && reconnect && conn != NULL)
+      if (start_event_loop(&notif_thread) != 0)
+        return -1;
   }
 
   time(&t);
@@ -1582,32 +2183,53 @@ static int lv_read(user_data_t *ud) {
   if ((last_refresh == (time_t)0) ||
       ((interval > 0) && ((last_refresh + interval) <= t))) {
     if (refresh_lists(inst) != 0) {
-      if (inst->id == 0)
+      if (inst->id == 0) {
+        if (!persistent_notification)
+          stop_event_loop(&notif_thread);
         lv_disconnect();
+      }
       return -1;
     }
     last_refresh = t;
   }
 
-#if 0
-    for (int i = 0; i < nr_domains; ++i)
-        fprintf (stderr, "domain %s\n", virDomainGetName (state->domains[i].ptr));
-    for (int i = 0; i < nr_block_devices; ++i)
-        fprintf  (stderr, "block device %d %s:%s\n",
-                  i, virDomainGetName (block_devices[i].dom),
-                  block_devices[i].path);
-    for (int i = 0; i < nr_interface_devices; ++i)
-        fprintf (stderr, "interface device %d %s:%s\n",
-                 i, virDomainGetName (interface_devices[i].dom),
-                 interface_devices[i].path);
+  /* persistent domains state notifications are handled by instance 0 */
+  if (inst->id == 0 && persistent_notification) {
+    int status = persistent_domains_state_notification();
+    if (status != 0)
+      DEBUG(PLUGIN_NAME " plugin: persistent_domains_state_notifications "
+                        "returned with status %i",
+            status);
+  }
+
+#if COLLECT_DEBUG
+  for (int i = 0; i < state->nr_domains; ++i)
+    DEBUG(PLUGIN_NAME " plugin: domain %s",
+          virDomainGetName(state->domains[i].ptr));
+  for (int i = 0; i < state->nr_block_devices; ++i)
+    DEBUG(PLUGIN_NAME " plugin: block device %d %s:%s", i,
+          virDomainGetName(state->block_devices[i].dom),
+          state->block_devices[i].path);
+  for (int i = 0; i < state->nr_interface_devices; ++i)
+    DEBUG(PLUGIN_NAME " plugin: interface device %d %s:%s", i,
+          virDomainGetName(state->interface_devices[i].dom),
+          state->interface_devices[i].path);
 #endif
 
   /* Get domains' metrics */
   for (int i = 0; i < state->nr_domains; ++i) {
-    int status = get_domain_metrics(&state->domains[i]);
+    domain_t *dom = &state->domains[i];
+    int status = 0;
+    if (dom->active)
+      status = get_domain_metrics(dom);
+#ifdef HAVE_DOM_REASON
+    else
+      status = get_domain_state(dom->ptr);
+#endif
+
     if (status != 0)
-      ERROR(PLUGIN_NAME " failed to get metrics for domain=%s",
-            virDomainGetName(state->domains[i].ptr));
+      ERROR(PLUGIN_NAME " plugin: failed to get metrics for domain=%s",
+            virDomainGetName(dom->ptr));
   }
 
   /* Get block device stats for each domain. */
@@ -1615,7 +2237,7 @@ static int lv_read(user_data_t *ud) {
     int status = get_block_stats(&state->block_devices[i]);
     if (status != 0)
       ERROR(PLUGIN_NAME
-            " failed to get stats for block device (%s) in domain %s",
+            " plugin: failed to get stats for block device (%s) in domain %s",
             state->block_devices[i].path,
             virDomainGetName(state->block_devices[i].dom));
   }
@@ -1624,10 +2246,11 @@ static int lv_read(user_data_t *ud) {
   for (int i = 0; i < state->nr_interface_devices; ++i) {
     int status = get_if_dev_stats(&state->interface_devices[i]);
     if (status != 0)
-      ERROR(PLUGIN_NAME
-            " failed to get interface stats for device (%s) in domain %s",
-            state->interface_devices[i].path,
-            virDomainGetName(state->interface_devices[i].dom));
+      ERROR(
+          PLUGIN_NAME
+          " plugin: failed to get interface stats for device (%s) in domain %s",
+          state->interface_devices[i].path,
+          virDomainGetName(state->interface_devices[i].dom));
   }
 
   return 0;
@@ -1639,7 +2262,7 @@ static int lv_init_instance(size_t i, plugin_read_cb callback) {
 
   memset(lv_ud, 0, sizeof(*lv_ud));
 
-  snprintf(inst->tag, sizeof(inst->tag), "%s-%zu", PLUGIN_NAME, i);
+  snprintf(inst->tag, sizeof(inst->tag), "%s-%" PRIsz, PLUGIN_NAME, i);
   inst->id = i;
 
   user_data_t *ud = &(lv_ud->ud);
@@ -1647,6 +2270,7 @@ static int lv_init_instance(size_t i, plugin_read_cb callback) {
   ud->free_func = NULL;
 
   INFO(PLUGIN_NAME " plugin: reader %s initialized", inst->tag);
+
   return plugin_register_complex_read(NULL, inst->tag, callback, 0, ud);
 }
 
@@ -1661,6 +2285,7 @@ static void lv_fini_instance(size_t i) {
   struct lv_read_state *state = &(inst->read_state);
 
   lv_clean_read_state(state);
+
   INFO(PLUGIN_NAME " plugin: reader %s finalized", inst->tag);
 }
 
@@ -1668,13 +2293,31 @@ static int lv_init(void) {
   if (virInitialize() != 0)
     return -1;
 
+  /* Init ignorelists if there was no explicit configuration */
+  if (lv_init_ignorelists() != 0)
+    return -1;
+
+  /* event implementation must be registered before connection is opened */
+  if (!persistent_notification)
+    if (register_event_impl() != 0)
+      return -1;
+
   if (lv_connect() != 0)
     return -1;
 
+  DEBUG(PLUGIN_NAME " plugin: starting event loop");
+
+  if (!persistent_notification) {
+    virt_notif_thread_init(&notif_thread);
+    if (start_event_loop(&notif_thread) != 0)
+      return -1;
+  }
+
   DEBUG(PLUGIN_NAME " plugin: starting %i instances", nr_instances);
 
   for (int i = 0; i < nr_instances; ++i)
-    lv_init_instance(i, lv_read);
+    if (lv_init_instance(i, lv_read) != 0)
+      return -1;
 
   return 0;
 }
@@ -1773,224 +2416,256 @@ static int lv_instance_include_domain(struct lv_read_instance *inst,
   return 0;
 }
 
-/*
-  virConnectListAllDomains() appeared in 0.10.2
-  Note that LIBVIR_CHECK_VERSION appeared a year later, so
-  in some systems which actually have virConnectListAllDomains()
-  we can't detect this.
- */
-#ifdef LIBVIR_CHECK_VERSION
-#if LIBVIR_CHECK_VERSION(0, 10, 2)
-#define HAVE_LIST_ALL_DOMAINS 1
-#endif
-#endif
+static void lv_add_block_devices(struct lv_read_state *state, virDomainPtr dom,
+                                 const char *domname,
+                                 xmlXPathContextPtr xpath_ctx) {
+  const char *bd_xmlpath = "/domain/devices/disk/target[@dev]";
+  if (blockdevice_format == source)
+    bd_xmlpath = "/domain/devices/disk/source[@dev]";
+
+  xmlXPathObjectPtr xpath_obj =
+      xmlXPathEval((const xmlChar *)bd_xmlpath, xpath_ctx);
+
+  if (xpath_obj == NULL)
+    return;
+
+  if (xpath_obj->type != XPATH_NODESET || xpath_obj->nodesetval == NULL) {
+    xmlXPathFreeObject(xpath_obj);
+    return;
+  }
+
+  for (int j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
+    xmlNodePtr node = xpath_obj->nodesetval->nodeTab[j];
+    if (!node)
+      continue;
+
+    char *path = (char *)xmlGetProp(node, (xmlChar *)"dev");
+    if (!path)
+      continue;
+
+    if (ignore_device_match(il_block_devices, domname, path) == 0)
+      add_block_device(state, dom, path);
+
+    xmlFree(path);
+  }
+  xmlXPathFreeObject(xpath_obj);
+}
+
+static void lv_add_network_interfaces(struct lv_read_state *state,
+                                      virDomainPtr dom, const char *domname,
+                                      xmlXPathContextPtr xpath_ctx) {
+  xmlXPathObjectPtr xpath_obj = xmlXPathEval(
+      (xmlChar *)"/domain/devices/interface[target[@dev]]", xpath_ctx);
+
+  if (xpath_obj == NULL)
+    return;
+
+  if (xpath_obj->type != XPATH_NODESET || xpath_obj->nodesetval == NULL) {
+    xmlXPathFreeObject(xpath_obj);
+    return;
+  }
+
+  xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
+
+  for (int j = 0; j < xml_interfaces->nodeNr; ++j) {
+    char *path = NULL;
+    char *address = NULL;
+
+    xmlNodePtr xml_interface = xml_interfaces->nodeTab[j];
+    if (!xml_interface)
+      continue;
+
+    for (xmlNodePtr child = xml_interface->children; child;
+         child = child->next) {
+      if (child->type != XML_ELEMENT_NODE)
+        continue;
+
+      if (xmlStrEqual(child->name, (const xmlChar *)"target")) {
+        path = (char *)xmlGetProp(child, (const xmlChar *)"dev");
+        if (!path)
+          continue;
+      } else if (xmlStrEqual(child->name, (const xmlChar *)"mac")) {
+        address = (char *)xmlGetProp(child, (const xmlChar *)"address");
+        if (!address)
+          continue;
+      }
+    }
+
+    if ((ignore_device_match(il_interface_devices, domname, path) == 0 &&
+         ignore_device_match(il_interface_devices, domname, address) == 0)) {
+      add_interface_device(state, dom, path, address, j + 1);
+    }
+
+    if (path)
+      xmlFree(path);
+    if (address)
+      xmlFree(address);
+  }
+  xmlXPathFreeObject(xpath_obj);
+}
 
 static int refresh_lists(struct lv_read_instance *inst) {
   struct lv_read_state *state = &inst->read_state;
   int n;
 
+#ifndef HAVE_LIST_ALL_DOMAINS
   n = virConnectNumOfDomains(conn);
   if (n < 0) {
     VIRT_ERROR(conn, "reading number of domains");
     return -1;
   }
+#endif
 
   lv_clean_read_state(state);
 
-  if (n > 0) {
+#ifndef HAVE_LIST_ALL_DOMAINS
+  if (n == 0)
+    goto end;
+#endif
+
 #ifdef HAVE_LIST_ALL_DOMAINS
-    virDomainPtr *domains;
-    n = virConnectListAllDomains(conn, &domains,
-                                 VIR_CONNECT_LIST_DOMAINS_ACTIVE);
+  virDomainPtr *domains, *domains_inactive;
+  int m = virConnectListAllDomains(conn, &domains_inactive,
+                                   VIR_CONNECT_LIST_DOMAINS_INACTIVE);
+  n = virConnectListAllDomains(conn, &domains, VIR_CONNECT_LIST_DOMAINS_ACTIVE);
 #else
-    int *domids;
-
-    /* Get list of domains. */
-    domids = malloc(sizeof(*domids) * n);
-    if (domids == NULL) {
-      ERROR(PLUGIN_NAME " plugin: malloc failed.");
-      return -1;
-    }
+  /* Get list of domains. */
+  int *domids = calloc(n, sizeof(*domids));
+  if (domids == NULL) {
+    ERROR(PLUGIN_NAME " plugin: calloc failed.");
+    return -1;
+  }
 
-    n = virConnectListDomains(conn, domids, n);
+  n = virConnectListDomains(conn, domids, n);
 #endif
 
-    if (n < 0) {
-      VIRT_ERROR(conn, "reading list of domains");
+  if (n < 0) {
+    VIRT_ERROR(conn, "reading list of domains");
 #ifndef HAVE_LIST_ALL_DOMAINS
-      sfree(domids);
+    sfree(domids);
+#else
+    for (int i = 0; i < m; ++i)
+      virDomainFree(domains_inactive[i]);
+    sfree(domains_inactive);
 #endif
-      return -1;
+    return -1;
+  }
+
+#ifdef HAVE_LIST_ALL_DOMAINS
+  for (int i = 0; i < m; ++i)
+    if (add_domain(state, domains_inactive[i], 0) < 0) {
+      ERROR(PLUGIN_NAME " plugin: malloc failed.");
+      virDomainFree(domains_inactive[i]);
+      domains_inactive[i] = NULL;
+      continue;
     }
+#endif
 
-    /* Fetch each domain and add it to the list, unless ignore. */
-    for (int i = 0; i < n; ++i) {
-      const char *name;
-      char *xml = NULL;
-      xmlDocPtr xml_doc = NULL;
-      xmlXPathContextPtr xpath_ctx = NULL;
-      xmlXPathObjectPtr xpath_obj = NULL;
-      char tag[PARTITION_TAG_MAX_LEN] = {'\0'};
-      virDomainInfo info;
-      int status;
+  /* Fetch each domain and add it to the list, unless ignore. */
+  for (int i = 0; i < n; ++i) {
 
 #ifdef HAVE_LIST_ALL_DOMAINS
-      virDomainPtr dom = domains[i];
+    virDomainPtr dom = domains[i];
 #else
-      virDomainPtr dom = NULL;
-      dom = virDomainLookupByID(conn, domids[i]);
-      if (dom == NULL) {
-        VIRT_ERROR(conn, "virDomainLookupByID");
-        /* Could be that the domain went away -- ignore it anyway. */
-        continue;
-      }
+    virDomainPtr dom = virDomainLookupByID(conn, domids[i]);
+    if (dom == NULL) {
+      VIRT_ERROR(conn, "virDomainLookupByID");
+      /* Could be that the domain went away -- ignore it anyway. */
+      continue;
+    }
 #endif
 
-      name = virDomainGetName(dom);
-      if (name == NULL) {
-        VIRT_ERROR(conn, "virDomainGetName");
-        goto cont;
-      }
-
-      status = virDomainGetInfo(dom, &info);
-      if (status != 0) {
-        ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
-              status);
-        continue;
-      }
-
-      if (info.state != VIR_DOMAIN_RUNNING) {
-        DEBUG(PLUGIN_NAME " plugin: skipping inactive domain %s", name);
-        continue;
-      }
-
-      if (il_domains && ignorelist_match(il_domains, name) != 0)
-        goto cont;
-
-      /* Get a list of devices for this domain. */
-      xml = virDomainGetXMLDesc(dom, 0);
-      if (!xml) {
-        VIRT_ERROR(conn, "virDomainGetXMLDesc");
-        goto cont;
-      }
-
-      /* Yuck, XML.  Parse out the devices. */
-      xml_doc = xmlReadDoc((xmlChar *)xml, NULL, NULL, XML_PARSE_NONET);
-      if (xml_doc == NULL) {
-        VIRT_ERROR(conn, "xmlReadDoc");
-        goto cont;
-      }
-
-      xpath_ctx = xmlXPathNewContext(xml_doc);
-
-      if (lv_domain_get_tag(xpath_ctx, name, tag) < 0) {
-        ERROR(PLUGIN_NAME " plugin: lv_domain_get_tag failed.");
-        goto cont;
-      }
-
-      if (!lv_instance_include_domain(inst, name, tag))
-        goto cont;
+    if (add_domain(state, dom, 1) < 0) {
+      /*
+       * When domain is already tracked, then there is
+       * no problem with memory handling (will be freed
+       * with the rest of domains cached data)
+       * But in case of error like this (error occurred
+       * before adding domain to track) we have to take
+       * care it ourselves and call virDomainFree
+       */
+      ERROR(PLUGIN_NAME " plugin: malloc failed.");
+      virDomainFree(dom);
+      continue;
+    }
 
-      if (add_domain(state, dom) < 0) {
-        ERROR(PLUGIN_NAME " plugin: malloc failed.");
-        goto cont;
-      }
+    const char *domname = virDomainGetName(dom);
+    if (domname == NULL) {
+      VIRT_ERROR(conn, "virDomainGetName");
+      continue;
+    }
 
-      /* Block devices. */
-      const char *bd_xmlpath = "/domain/devices/disk/target[@dev]";
-      if (blockdevice_format == source)
-        bd_xmlpath = "/domain/devices/disk/source[@dev]";
-      xpath_obj = xmlXPathEval((const xmlChar *)bd_xmlpath, xpath_ctx);
+    virDomainInfo info;
+    int status = virDomainGetInfo(dom, &info);
+    if (status != 0) {
+      ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
+            status);
+      continue;
+    }
 
-      if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
-          xpath_obj->nodesetval == NULL)
-        goto cont;
+    if (info.state != VIR_DOMAIN_RUNNING) {
+      DEBUG(PLUGIN_NAME " plugin: skipping inactive domain %s", domname);
+      continue;
+    }
 
-      for (int j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
-        xmlNodePtr node;
-        char *path = NULL;
+    if (ignorelist_match(il_domains, domname) != 0)
+      continue;
 
-        node = xpath_obj->nodesetval->nodeTab[j];
-        if (!node)
-          continue;
-        path = (char *)xmlGetProp(node, (xmlChar *)"dev");
-        if (!path)
-          continue;
+    /* Get a list of devices for this domain. */
+    xmlDocPtr xml_doc = NULL;
+    xmlXPathContextPtr xpath_ctx = NULL;
 
-        if (il_block_devices &&
-            ignore_device_match(il_block_devices, name, path) != 0)
-          goto cont2;
+    char *xml = virDomainGetXMLDesc(dom, 0);
+    if (!xml) {
+      VIRT_ERROR(conn, "virDomainGetXMLDesc");
+      goto cont;
+    }
 
-        add_block_device(state, dom, path);
-      cont2:
-        if (path)
-          xmlFree(path);
-      }
-      xmlXPathFreeObject(xpath_obj);
+    /* Yuck, XML.  Parse out the devices. */
+    xml_doc = xmlReadDoc((xmlChar *)xml, NULL, NULL, XML_PARSE_NONET);
+    if (xml_doc == NULL) {
+      VIRT_ERROR(conn, "xmlReadDoc");
+      goto cont;
+    }
 
-      /* Network interfaces. */
-      xpath_obj = xmlXPathEval(
-          (xmlChar *)"/domain/devices/interface[target[@dev]]", xpath_ctx);
-      if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
-          xpath_obj->nodesetval == NULL)
-        goto cont;
+    xpath_ctx = xmlXPathNewContext(xml_doc);
 
-      xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
+    char tag[PARTITION_TAG_MAX_LEN] = {'\0'};
+    if (lv_domain_get_tag(xpath_ctx, domname, tag) < 0) {
+      ERROR(PLUGIN_NAME " plugin: lv_domain_get_tag failed.");
+      goto cont;
+    }
 
-      for (int j = 0; j < xml_interfaces->nodeNr; ++j) {
-        char *path = NULL;
-        char *address = NULL;
-        xmlNodePtr xml_interface;
+    if (!lv_instance_include_domain(inst, domname, tag))
+      goto cont;
 
-        xml_interface = xml_interfaces->nodeTab[j];
-        if (!xml_interface)
-          continue;
+    /* Block devices. */
+    if (report_block_devices)
+      lv_add_block_devices(state, dom, domname, xpath_ctx);
 
-        for (xmlNodePtr child = xml_interface->children; child;
-             child = child->next) {
-          if (child->type != XML_ELEMENT_NODE)
-            continue;
-
-          if (xmlStrEqual(child->name, (const xmlChar *)"target")) {
-            path = (char *)xmlGetProp(child, (const xmlChar *)"dev");
-            if (!path)
-              continue;
-          } else if (xmlStrEqual(child->name, (const xmlChar *)"mac")) {
-            address = (char *)xmlGetProp(child, (const xmlChar *)"address");
-            if (!address)
-              continue;
-          }
-        }
-
-        if (il_interface_devices &&
-            (ignore_device_match(il_interface_devices, name, path) != 0 ||
-             ignore_device_match(il_interface_devices, name, address) != 0))
-          goto cont3;
-
-        add_interface_device(state, dom, path, address, j + 1);
-      cont3:
-        if (path)
-          xmlFree(path);
-        if (address)
-          xmlFree(address);
-      }
+    /* Network interfaces. */
+    if (report_network_interfaces)
+      lv_add_network_interfaces(state, dom, domname, xpath_ctx);
 
-    cont:
-      if (xpath_obj)
-        xmlXPathFreeObject(xpath_obj);
-      if (xpath_ctx)
-        xmlXPathFreeContext(xpath_ctx);
-      if (xml_doc)
-        xmlFreeDoc(xml_doc);
-      sfree(xml);
-    }
+  cont:
+    if (xpath_ctx)
+      xmlXPathFreeContext(xpath_ctx);
+    if (xml_doc)
+      xmlFreeDoc(xml_doc);
+    sfree(xml);
+  }
 
 #ifdef HAVE_LIST_ALL_DOMAINS
-    sfree(domains);
+  /* NOTE: domains_active and domains_inactive data will be cleared during
+     refresh of all domains (inside lv_clean_read_state function) so we need
+     to free here only allocated arrays */
+  sfree(domains);
+  sfree(domains_inactive);
 #else
-    sfree(domids);
+  sfree(domids);
+
+end:
 #endif
-  }
 
   DEBUG(PLUGIN_NAME " plugin#%s: refreshing"
                     " domains=%i block_devices=%i iface_devices=%i",
@@ -2010,20 +2685,18 @@ static void free_domains(struct lv_read_state *state) {
   state->nr_domains = 0;
 }
 
-static int add_domain(struct lv_read_state *state, virDomainPtr dom) {
-  domain_t *new_ptr;
-  int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1);
+static int add_domain(struct lv_read_state *state, virDomainPtr dom,
+                      bool active) {
 
-  if (state->domains)
-    new_ptr = realloc(state->domains, new_size);
-  else
-    new_ptr = malloc(new_size);
+  int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1);
 
+  domain_t *new_ptr = realloc(state->domains, new_size);
   if (new_ptr == NULL)
     return -1;
 
   state->domains = new_ptr;
   state->domains[state->nr_domains].ptr = dom;
+  state->domains[state->nr_domains].active = active;
   memset(&state->domains[state->nr_domains].info, 0,
          sizeof(state->domains[state->nr_domains].info));
 
@@ -2042,20 +2715,15 @@ static void free_block_devices(struct lv_read_state *state) {
 
 static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
                             const char *path) {
-  struct block_device *new_ptr;
-  int new_size =
-      sizeof(state->block_devices[0]) * (state->nr_block_devices + 1);
-  char *path_copy;
 
-  path_copy = strdup(path);
+  char *path_copy = strdup(path);
   if (!path_copy)
     return -1;
 
-  if (state->block_devices)
-    new_ptr = realloc(state->block_devices, new_size);
-  else
-    new_ptr = malloc(new_size);
+  int new_size =
+      sizeof(state->block_devices[0]) * (state->nr_block_devices + 1);
 
+  struct block_device *new_ptr = realloc(state->block_devices, new_size);
   if (new_ptr == NULL) {
     sfree(path_copy);
     return -1;
@@ -2082,61 +2750,62 @@ static void free_interface_devices(struct lv_read_state *state) {
 static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
                                 const char *path, const char *address,
                                 unsigned int number) {
-  struct interface_device *new_ptr;
-  int new_size =
-      sizeof(state->interface_devices[0]) * (state->nr_interface_devices + 1);
-  char *path_copy, *address_copy, number_string[15];
 
   if ((path == NULL) || (address == NULL))
     return EINVAL;
 
-  path_copy = strdup(path);
+  char *path_copy = strdup(path);
   if (!path_copy)
     return -1;
 
-  address_copy = strdup(address);
+  char *address_copy = strdup(address);
   if (!address_copy) {
     sfree(path_copy);
     return -1;
   }
 
+  char number_string[21];
   snprintf(number_string, sizeof(number_string), "interface-%u", number);
+  char *number_copy = strdup(number_string);
+  if (!number_copy) {
+    sfree(path_copy);
+    sfree(address_copy);
+    return -1;
+  }
 
-  if (state->interface_devices)
-    new_ptr = realloc(state->interface_devices, new_size);
-  else
-    new_ptr = malloc(new_size);
+  int new_size =
+      sizeof(state->interface_devices[0]) * (state->nr_interface_devices + 1);
 
+  struct interface_device *new_ptr =
+      realloc(state->interface_devices, new_size);
   if (new_ptr == NULL) {
     sfree(path_copy);
     sfree(address_copy);
+    sfree(number_copy);
     return -1;
   }
+
   state->interface_devices = new_ptr;
   state->interface_devices[state->nr_interface_devices].dom = dom;
   state->interface_devices[state->nr_interface_devices].path = path_copy;
   state->interface_devices[state->nr_interface_devices].address = address_copy;
-  state->interface_devices[state->nr_interface_devices].number =
-      strdup(number_string);
+  state->interface_devices[state->nr_interface_devices].number = number_copy;
   return state->nr_interface_devices++;
 }
 
 static int ignore_device_match(ignorelist_t *il, const char *domname,
                                const char *devpath) {
-  char *name;
-  int n, r;
-
   if ((domname == NULL) || (devpath == NULL))
     return 0;
 
-  n = strlen(domname) + strlen(devpath) + 2;
-  name = malloc(n);
+  size_t n = strlen(domname) + strlen(devpath) + 2;
+  char *name = malloc(n);
   if (name == NULL) {
     ERROR(PLUGIN_NAME " plugin: malloc failed.");
     return 0;
   }
   snprintf(name, n, "%s:%s", domname, devpath);
-  r = ignorelist_match(il, name);
+  int r = ignorelist_match(il, name);
   sfree(name);
   return r;
 }
@@ -2146,6 +2815,11 @@ static int lv_shutdown(void) {
     lv_fini_instance(i);
   }
 
+  DEBUG(PLUGIN_NAME " plugin: stopping event loop");
+
+  if (!persistent_notification)
+    stop_event_loop(&notif_thread);
+
   lv_disconnect();
 
   ignorelist_free(il_domains);
index 489a367..458faca 100644 (file)
 #include "testing.h"
 #include "virt.c" /* sic */
 
-#include <unistd.h>
+static virDomainPtr *domains;
+static int nr_domains;
 
-static const char minimal_xml[] =
-    ""
-    "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
-    "<domain type=\"kvm\" xmlns:ovirt=\"http://ovirt.org/vm/tune/1.0\">"
-    "  <metadata/>"
-    "</domain>";
-
-static const char minimal_metadata_xml[] =
-    ""
-    "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
-    "<domain type=\"kvm\" xmlns:ovirt=\"http://ovirt.org/vm/tune/1.0\">"
-    "  <metadata>"
-    "    <ovirtmap:tag "
-    "xmlns:ovirtmap=\"http://ovirt.org/ovirtmap/tag/1.0\">virt-0</ovirtmap:tag>"
-    "  </metadata>"
-    "</domain>";
-
-struct xml_state {
-  xmlDocPtr xml_doc;
-  xmlXPathContextPtr xpath_ctx;
-  xmlXPathObjectPtr xpath_obj;
-  char tag[PARTITION_TAG_MAX_LEN];
-};
-
-static int init_state(struct xml_state *st, const char *xml) {
-  memset(st, 0, sizeof(*st));
-
-  st->xml_doc = xmlReadDoc((const xmlChar *)xml, NULL, NULL, XML_PARSE_NONET);
-  if (st->xml_doc == NULL) {
-    return -1;
-  }
-  st->xpath_ctx = xmlXPathNewContext(st->xml_doc);
-  if (st->xpath_ctx == NULL) {
+static int setup(void) {
+  if (virInitialize() != 0) {
+    printf("ERROR: virInitialize() != 0\n");
     return -1;
   }
-  return 0;
-}
 
-static void fini_state(struct xml_state *st) {
-  if (st->xpath_ctx) {
-    xmlXPathFreeContext(st->xpath_ctx);
-    st->xpath_ctx = NULL;
-  }
-  if (st->xml_doc) {
-    xmlFreeDoc(st->xml_doc);
-    st->xml_doc = NULL;
+  conn = virConnectOpen("test:///default");
+  if (conn == NULL) {
+    printf("ERROR: virConnectOpen == NULL\n");
+    return -1;
   }
-}
-
-#define TAG "virt-0"
-
-DEF_TEST(lv_domain_get_tag_no_metadata_xml) {
-  int err;
-  struct xml_state st;
-  err = init_state(&st, minimal_xml);
-  EXPECT_EQ_INT(0, err);
-
-  err = lv_domain_get_tag(st.xpath_ctx, "test", st.tag);
-
-  EXPECT_EQ_INT(0, err);
-  EXPECT_EQ_STR("", st.tag);
-
-  fini_state(&st);
-  return 0;
-}
-
-DEF_TEST(lv_domain_get_tag_valid_xml) {
-  int err;
-  struct xml_state st;
-  err = init_state(&st, minimal_metadata_xml);
-  EXPECT_EQ_INT(0, err);
-
-  err = lv_domain_get_tag(st.xpath_ctx, "test", st.tag);
-
-  EXPECT_EQ_INT(0, err);
-  EXPECT_EQ_STR(TAG, st.tag);
-
-  return 0;
-}
-
-DEF_TEST(lv_default_instance_include_domain_without_tag) {
-  struct lv_read_instance *inst = NULL;
-  int ret;
-
-  ret = lv_init_instance(0, lv_read);
-  inst = &(lv_read_user_data[0].inst);
-  EXPECT_EQ_STR("virt-0", inst->tag);
-
-  ret = lv_instance_include_domain(inst, "testing", "");
-  EXPECT_EQ_INT(1, ret);
-
-  lv_fini_instance(0);
-  return 0;
-}
-
-DEF_TEST(lv_regular_instance_skip_domain_without_tag) {
-  struct lv_read_instance *inst = NULL;
-  int ret;
-
-  ret = lv_init_instance(1, lv_read);
-  inst = &(lv_read_user_data[1].inst);
-  EXPECT_EQ_STR("virt-1", inst->tag);
 
-  ret = lv_instance_include_domain(inst, "testing", "");
-  EXPECT_EQ_INT(0, ret);
-
-  lv_fini_instance(0);
   return 0;
 }
 
-DEF_TEST(lv_include_domain_matching_tags) {
-  struct lv_read_instance *inst = NULL;
-  int ret;
-
-  ret = lv_init_instance(0, lv_read);
-  inst = &(lv_read_user_data[0].inst);
-  EXPECT_EQ_STR("virt-0", inst->tag);
-
-  ret = lv_instance_include_domain(inst, "testing", "virt-0");
-  EXPECT_EQ_INT(1, ret);
-
-  ret = lv_init_instance(1, lv_read);
-  inst = &(lv_read_user_data[1].inst);
-  EXPECT_EQ_STR("virt-1", inst->tag);
-
-  ret = lv_instance_include_domain(inst, "testing", "virt-1");
-  EXPECT_EQ_INT(1, ret);
+static int teardown(void) {
+  if (domains) {
+    for (int i = 0; i < nr_domains; ++i)
+      virDomainFree(domains[i]);
+    sfree(domains);
+  }
+  nr_domains = 0;
+  if (conn != NULL)
+    virConnectClose(conn);
 
-  lv_fini_instance(0);
-  lv_fini_instance(1);
   return 0;
 }
 
-DEF_TEST(lv_default_instance_include_domain_with_unknown_tag) {
-  struct lv_read_instance *inst = NULL;
-  int ret;
-
-  ret = lv_init_instance(0, lv_read);
-  inst = &(lv_read_user_data[0].inst);
-  EXPECT_EQ_STR("virt-0", inst->tag);
-
-  ret = lv_instance_include_domain(inst, "testing", "unknownFormat-tag");
-  EXPECT_EQ_INT(1, ret);
+#ifdef HAVE_LIST_ALL_DOMAINS
+DEF_TEST(get_domain_state_notify) {
+  if (setup() == 0) {
+    nr_domains = virConnectListAllDomains(conn, &domains,
+                                          VIR_CONNECT_LIST_DOMAINS_PERSISTENT);
+    if (nr_domains <= 0) {
+      printf("ERROR: virConnectListAllDomains: nr_domains <= 0\n");
+      return -1;
+    }
+
+    int ret = get_domain_state_notify(domains[0]);
+    EXPECT_EQ_INT(0, ret);
+  }
+  teardown();
 
-  lv_fini_instance(0);
   return 0;
 }
+#endif
 
-DEF_TEST(lv_regular_instance_skip_domain_with_unknown_tag) {
-  struct lv_read_instance *inst = NULL;
-  int ret;
-
-  ret = lv_init_instance(1, lv_read);
-  inst = &(lv_read_user_data[1].inst);
-  EXPECT_EQ_STR("virt-1", inst->tag);
-
-  ret = lv_instance_include_domain(inst, "testing", "unknownFormat-tag");
-  EXPECT_EQ_INT(0, ret);
+DEF_TEST(persistent_domains_state_notification) {
+  if (setup() == 0) {
+    int ret = persistent_domains_state_notification();
+    EXPECT_EQ_INT(0, ret);
+  }
+  teardown();
 
-  lv_fini_instance(0);
   return 0;
 }
-#undef TAG
 
 int main(void) {
-  RUN_TEST(lv_domain_get_tag_no_metadata_xml);
-  RUN_TEST(lv_domain_get_tag_valid_xml);
-
-  RUN_TEST(lv_default_instance_include_domain_without_tag);
-  RUN_TEST(lv_regular_instance_skip_domain_without_tag);
-  RUN_TEST(lv_include_domain_matching_tags);
-  RUN_TEST(lv_default_instance_include_domain_with_unknown_tag);
-  RUN_TEST(lv_regular_instance_skip_domain_with_unknown_tag);
+#ifdef HAVE_LIST_ALL_DOMAINS
+  RUN_TEST(get_domain_state_notify);
+#endif
+  RUN_TEST(persistent_domains_state_notification);
 
   END_TEST;
 }
-
-/* vim: set sw=2 sts=2 et : */
index ffe1826..2ab7dda 100644 (file)
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if KERNEL_LINUX
 static const char *config_keys[] = {"Verbose"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
-static int verbose_output = 0;
+static int verbose_output;
 /* #endif KERNEL_LINUX */
 
 #else
@@ -104,9 +104,7 @@ static int vmem_read(void) {
 
   fh = fopen("/proc/vmstat", "r");
   if (fh == NULL) {
-    char errbuf[1024];
-    ERROR("vmem plugin: fopen (/proc/vmstat) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("vmem plugin: fopen (/proc/vmstat) failed: %s", STRERRNO);
     return -1;
   }
 
index 6f9d46b..e1d1b35 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <dirent.h>
 #include <sys/types.h>
@@ -42,7 +42,7 @@
 #error "No applicable input method."
 #endif
 
-static int pagesize = 0;
+static int pagesize;
 
 static int vserver_init(void) {
   /* XXX Should we check for getpagesize () in configure?
@@ -124,9 +124,7 @@ static int vserver_read(void) {
   errno = 0;
   proc = opendir(PROCDIR);
   if (proc == NULL) {
-    char errbuf[1024];
-    ERROR("vserver plugin: fopen (%s): %s", PROCDIR,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("vserver plugin: fopen (%s): %s", PROCDIR, STRERRNO);
     return -1;
   }
 
@@ -146,13 +144,11 @@ static int vserver_read(void) {
     errno = 0;
     dent = readdir(proc);
     if (dent == NULL) {
-      char errbuf[4096];
-
       if (errno == 0) /* end of directory */
         break;
 
       ERROR("vserver plugin: failed to read directory %s: %s", PROCDIR,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            STRERRNO);
       closedir(proc);
       return -1;
     }
@@ -166,9 +162,7 @@ static int vserver_read(void) {
 
     status = stat(file, &statbuf);
     if (status != 0) {
-      char errbuf[4096];
-      WARNING("vserver plugin: stat (%s) failed: %s", file,
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("vserver plugin: stat (%s) failed: %s", file, STRERRNO);
       continue;
     }
 
@@ -181,9 +175,7 @@ static int vserver_read(void) {
       continue;
 
     if (NULL == (fh = fopen(file, "r"))) {
-      char errbuf[1024];
-      ERROR("Cannot open '%s': %s", file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("Cannot open '%s': %s", file, STRERRNO);
     }
 
     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
@@ -225,9 +217,7 @@ static int vserver_read(void) {
       continue;
 
     if (NULL == (fh = fopen(file, "r"))) {
-      char errbuf[1024];
-      ERROR("Cannot open '%s': %s", file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("Cannot open '%s': %s", file, STRERRNO);
     }
 
     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
@@ -271,9 +261,7 @@ static int vserver_read(void) {
       continue;
 
     if (NULL == (fh = fopen(file, "r"))) {
-      char errbuf[1024];
-      ERROR("Cannot open '%s': %s", file,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("Cannot open '%s': %s", file, STRERRNO);
     }
 
     while ((fh != NULL) && (NULL != fgets(buffer, BUFSIZE, fh))) {
index 5552be6..d49f1d3 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/wireless.c
- * Copyright (C) 2006,2007  Florian octo Forster
+ * Copyright (C) 2006-2018  Florian octo Forster
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
-#if !KERNEL_LINUX
+#if KERNEL_LINUX
+#include <linux/if.h>
+#include <linux/wireless.h>
+#include <sys/ioctl.h>
+#else
 #error "No applicable input method."
 #endif
 
@@ -86,12 +90,18 @@ static int wireless_read(void) {
   int numfields;
 
   int devices_found;
-  int len;
+  size_t len;
 
   /* there are a variety of names for the wireless device */
   if ((fh = fopen(WIRELESS_PROC_FILE, "r")) == NULL) {
-    char errbuf[1024];
-    WARNING("wireless: fopen: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("wireless plugin: fopen: %s", STRERRNO);
+    return -1;
+  }
+
+  int sock = socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock == -1) {
+    ERROR("wireless plugin: socket: %s", STRERRNO);
+    fclose(fh);
     return -1;
   }
 
@@ -143,9 +153,20 @@ static int wireless_read(void) {
     wireless_submit(device, "signal_power", power);
     wireless_submit(device, "signal_noise", noise);
 
+    struct iwreq req = {
+        .ifr_ifrn.ifrn_name = {0},
+    };
+    sstrncpy(req.ifr_ifrn.ifrn_name, device, sizeof(req.ifr_ifrn.ifrn_name));
+    if (ioctl(sock, SIOCGIWRATE, &req) == -1) {
+      WARNING("wireless plugin: ioctl(SIOCGIWRATE): %s", STRERRNO);
+    } else {
+      wireless_submit(device, "bitrate", (double)req.u.bitrate.value);
+    }
+
     devices_found++;
   }
 
+  close(sock);
   fclose(fh);
 
   /* If no wireless devices are present return an error, so the plugin
index 7d7c0e8..000b62e 100644 (file)
  *     Protocol "udp"
  *     LogSendErrors true
  *     Prefix "collectd"
+ *     UseTags true
  *   </Carbon>
  * </Plugin>
  */
 
 #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>
 
@@ -65,7 +66,7 @@
 #endif
 
 #ifndef WG_DEFAULT_LOG_SEND_ERRORS
-#define WG_DEFAULT_LOG_SEND_ERRORS 1
+#define WG_DEFAULT_LOG_SEND_ERRORS true
 #endif
 
 #ifndef WG_DEFAULT_ESCAPE
@@ -92,7 +93,7 @@ struct wg_callback {
   char *node;
   char *service;
   char *protocol;
-  _Bool log_send_errors;
+  bool log_send_errors;
   char *prefix;
   char *postfix;
   char escape_char;
@@ -111,7 +112,7 @@ struct wg_callback {
   /* Force reconnect useful for load balanced environments */
   cdtime_t last_reconnect_time;
   cdtime_t reconnect_interval;
-  _Bool reconnect_interval_reached;
+  bool reconnect_interval_reached;
 };
 
 /* wg_force_reconnect_check closes cb->sock_fd when it was open for longer
@@ -131,7 +132,7 @@ static void wg_force_reconnect_check(struct wg_callback *cb) {
   close(cb->sock_fd);
   cb->sock_fd = -1;
   cb->last_reconnect_time = now;
-  cb->reconnect_interval_reached = 1;
+  cb->reconnect_interval_reached = true;
 
   INFO("write_graphite plugin: Connection closed after %.3f seconds.",
        CDTIME_T_TO_DOUBLE(now - cb->last_reconnect_time));
@@ -156,11 +157,9 @@ static int wg_send_buffer(struct wg_callback *cb) {
   status = swrite(cb->sock_fd, cb->send_buf, strlen(cb->send_buf));
   if (status != 0) {
     if (cb->log_send_errors) {
-      char errbuf[1024];
       ERROR("write_graphite plugin: send to %s:%s (%s) failed with status %zi "
             "(%s)",
-            cb->node, cb->service, cb->protocol, status,
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+            cb->node, cb->service, cb->protocol, status, STRERRNO);
     }
 
     close(cb->sock_fd);
@@ -177,7 +176,7 @@ static int wg_flush_nolock(cdtime_t timeout, struct wg_callback *cb) {
   int status;
 
   DEBUG("write_graphite plugin: wg_flush_nolock: timeout = %.3f; "
-        "send_buf_fill = %zu;",
+        "send_buf_fill = %" PRIsz ";",
         (double)timeout, cb->send_buf_fill);
 
   /* timeout == 0  => flush unconditionally */
@@ -238,9 +237,7 @@ static int wg_callback_init(struct wg_callback *cb) {
     cb->sock_fd =
         socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (cb->sock_fd < 0) {
-      char errbuf[1024];
-      snprintf(connerr, sizeof(connerr), "failed to open socket: %s",
-               sstrerror(errno, errbuf, sizeof(errbuf)));
+      snprintf(connerr, sizeof(connerr), "failed to open socket: %s", STRERRNO);
       continue;
     }
 
@@ -248,10 +245,8 @@ static int wg_callback_init(struct wg_callback *cb) {
 
     status = connect(cb->sock_fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
     if (status != 0) {
-      char errbuf[1024];
-      snprintf(connerr, sizeof(connerr), "failed to connect to remote "
-                                         "host: %s",
-               sstrerror(errno, errbuf, sizeof(errbuf)));
+      snprintf(connerr, sizeof(connerr), "failed to connect to remote host: %s",
+               STRERRNO);
       close(cb->sock_fd);
       cb->sock_fd = -1;
       continue;
@@ -263,9 +258,6 @@ static int wg_callback_init(struct wg_callback *cb) {
   freeaddrinfo(ai_list);
 
   if (cb->sock_fd < 0) {
-    if (connerr[0] == '\0')
-      /* this should not happen but try to get a message anyway */
-      sstrerror(errno, connerr, sizeof(connerr));
     c_complain(LOG_ERR, &cb->init_complaint,
                "write_graphite plugin: Connecting to %s:%s via %s failed. "
                "The last error was: %s",
@@ -283,7 +275,7 @@ static int wg_callback_init(struct wg_callback *cb) {
   if (!cb->reconnect_interval_reached || (cb->send_buf_free == 0))
     wg_reset_buffer(cb);
   else
-    cb->reconnect_interval_reached = 0;
+    cb->reconnect_interval_reached = false;
 
   return 0;
 }
@@ -382,7 +374,8 @@ static int wg_send_message(char const *message, struct wg_callback *cb) {
   cb->send_buf_fill += message_len;
   cb->send_buf_free -= message_len;
 
-  DEBUG("write_graphite plugin: [%s]:%s (%s) buf %zu/%zu (%.1f %%) \"%s\"",
+  DEBUG("write_graphite plugin: [%s]:%s (%s) buf %" PRIsz "/%" PRIsz
+        " (%.1f %%) \"%s\"",
         cb->node, cb->service, cb->protocol, cb->send_buf_fill,
         sizeof(cb->send_buf),
         100.0 * ((double)cb->send_buf_fill) / ((double)sizeof(cb->send_buf)),
@@ -474,7 +467,7 @@ static int wg_config_node(oconfig_item_t *ci) {
   cb->protocol = strdup(WG_DEFAULT_PROTOCOL);
   cb->last_reconnect_time = cdtime();
   cb->reconnect_interval = 0;
-  cb->reconnect_interval_reached = 0;
+  cb->reconnect_interval_reached = false;
   cb->log_send_errors = WG_DEFAULT_LOG_SEND_ERRORS;
   cb->prefix = NULL;
   cb->postfix = NULL;
@@ -526,6 +519,8 @@ static int wg_config_node(oconfig_item_t *ci) {
       cf_util_get_flag(child, &cb->format_flags, GRAPHITE_PRESERVE_SEPARATOR);
     else if (strcasecmp("DropDuplicateFields", child->key) == 0)
       cf_util_get_flag(child, &cb->format_flags, GRAPHITE_DROP_DUPE_FIELDS);
+    else if (strcasecmp("UseTags", child->key) == 0)
+      cf_util_get_flag(child, &cb->format_flags, GRAPHITE_USE_TAGS);
     else if (strcasecmp("EscapeCharacter", child->key) == 0)
       config_set_char(&cb->escape_char, child);
     else {
index 87e518b..74fdaca 100644 (file)
 
 #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>
 
@@ -50,16 +50,16 @@ struct wh_callback_s {
   char *user;
   char *pass;
   char *credentials;
-  _Bool verify_peer;
-  _Bool verify_host;
+  bool verify_peer;
+  bool verify_host;
   char *cacert;
   char *capath;
   char *clientkey;
   char *clientcert;
   char *clientkeypass;
   long sslversion;
-  _Bool store_rates;
-  _Bool log_http_error;
+  bool store_rates;
+  bool log_http_error;
   int low_speed_limit;
   time_t low_speed_time;
   int timeout;
@@ -68,8 +68,8 @@ struct wh_callback_s {
 #define WH_FORMAT_JSON 1
 #define WH_FORMAT_KAIROSDB 2
   int format;
-  _Bool send_metrics;
-  _Bool send_notifications;
+  bool send_metrics;
+  bool send_notifications;
 
   CURL *curl;
   struct curl_slist *headers;
@@ -228,7 +228,7 @@ static int wh_flush_nolock(cdtime_t timeout, wh_callback_t *cb) /* {{{ */
   int status;
 
   DEBUG("write_http plugin: wh_flush_nolock: timeout = %.3f; "
-        "send_buffer_fill = %zu;",
+        "send_buffer_fill = %" PRIsz ";",
         CDTIME_T_TO_DOUBLE(timeout), cb->send_buffer_fill);
 
   /* timeout == 0  => flush unconditionally */
@@ -380,7 +380,7 @@ static int wh_write_command(const data_set_t *ds,
                                  CDTIME_T_TO_DOUBLE(vl->interval), values);
   if (command_len >= sizeof(command)) {
     ERROR("write_http plugin: Command buffer too small: "
-          "Need %zu bytes.",
+          "Need %" PRIsz " bytes.",
           command_len + 1);
     return -1;
   }
@@ -410,8 +410,8 @@ static int wh_write_command(const data_set_t *ds,
   cb->send_buffer_fill += command_len;
   cb->send_buffer_free -= command_len;
 
-  DEBUG("write_http plugin: <%s> buffer %zu/%zu (%g%%) \"%s\"", cb->location,
-        cb->send_buffer_fill, cb->send_buffer_size,
+  DEBUG("write_http plugin: <%s> buffer %" PRIsz "/%" PRIsz " (%g%%) \"%s\"",
+        cb->location, cb->send_buffer_fill, cb->send_buffer_size,
         100.0 * ((double)cb->send_buffer_fill) / ((double)cb->send_buffer_size),
         command);
 
@@ -452,8 +452,8 @@ static int wh_write_json(const data_set_t *ds, const value_list_t *vl, /* {{{ */
     return status;
   }
 
-  DEBUG("write_http plugin: <%s> buffer %zu/%zu (%g%%)", cb->location,
-        cb->send_buffer_fill, cb->send_buffer_size,
+  DEBUG("write_http plugin: <%s> buffer %" PRIsz "/%" PRIsz " (%g%%)",
+        cb->location, cb->send_buffer_fill, cb->send_buffer_size,
         100.0 * ((double)cb->send_buffer_fill) /
             ((double)cb->send_buffer_size));
 
@@ -501,8 +501,8 @@ static int wh_write_kairosdb(const data_set_t *ds,
     return status;
   }
 
-  DEBUG("write_http plugin: <%s> buffer %zu/%zu (%g%%)", cb->location,
-        cb->send_buffer_fill, cb->send_buffer_size,
+  DEBUG("write_http plugin: <%s> buffer %" PRIsz "/%" PRIsz " (%g%%)",
+        cb->location, cb->send_buffer_fill, cb->send_buffer_size,
         100.0 * ((double)cb->send_buffer_fill) /
             ((double)cb->send_buffer_size));
 
@@ -624,16 +624,16 @@ static int wh_config_node(oconfig_item_t *ci) /* {{{ */
     ERROR("write_http plugin: calloc failed.");
     return -1;
   }
-  cb->verify_peer = 1;
-  cb->verify_host = 1;
+  cb->verify_peer = true;
+  cb->verify_host = true;
   cb->format = WH_FORMAT_COMMAND;
   cb->sslversion = CURL_SSLVERSION_DEFAULT;
   cb->low_speed_limit = 0;
   cb->timeout = 0;
-  cb->log_http_error = 0;
+  cb->log_http_error = false;
   cb->headers = NULL;
-  cb->send_metrics = 1;
-  cb->send_notifications = 0;
+  cb->send_metrics = true;
+  cb->send_notifications = false;
   cb->data_ttl = 0;
   cb->metrics_prefix = strdup(WRITE_HTTP_DEFAULT_PREFIX);
 
@@ -802,7 +802,8 @@ static int wh_config_node(oconfig_item_t *ci) /* {{{ */
   /* Allocate the buffer. */
   cb->send_buffer = malloc(cb->send_buffer_size);
   if (cb->send_buffer == NULL) {
-    ERROR("write_http plugin: malloc(%zu) failed.", cb->send_buffer_size);
+    ERROR("write_http plugin: malloc(%" PRIsz ") failed.",
+          cb->send_buffer_size);
     wh_callback_free(cb);
     return -1;
   }
index 2baaf0e..4c7a471 100644 (file)
 
 #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>
@@ -43,7 +43,7 @@ struct kafka_topic_context {
 #define KAFKA_FORMAT_GRAPHITE 2
   uint8_t format;
   unsigned int graphite_flags;
-  _Bool store_rates;
+  bool store_rates;
   rd_kafka_topic_conf_t *conf;
   rd_kafka_topic_t *topic;
   rd_kafka_conf_t *kafka_conf;
@@ -273,7 +273,7 @@ static void kafka_config_topic(rd_kafka_conf_t *conf,
   }
 
   tctx->escape_char = '.';
-  tctx->store_rates = 1;
+  tctx->store_rates = true;
   tctx->format = KAFKA_FORMAT_JSON;
   tctx->key = NULL;
 
@@ -383,6 +383,10 @@ static void kafka_config_topic(rd_kafka_conf_t *conf,
       status = cf_util_get_flag(child, &tctx->graphite_flags,
                                 GRAPHITE_PRESERVE_SEPARATOR);
 
+    } else if (strcasecmp("GraphiteUseTags", child->key) == 0) {
+      status =
+          cf_util_get_flag(child, &tctx->graphite_flags, GRAPHITE_USE_TAGS);
+
     } else if (strcasecmp("GraphitePrefix", child->key) == 0) {
       status = cf_util_get_string(child, &tctx->prefix);
     } else if (strcasecmp("GraphitePostfix", child->key) == 0) {
index fdc99ef..3e14316 100644 (file)
 
 #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>
 
@@ -96,7 +96,7 @@ static int wl_write(const data_set_t *ds, const value_list_t *vl,
 
 static int wl_config(oconfig_item_t *ci) /* {{{ */
 {
-  _Bool format_seen = 0;
+  bool format_seen = false;
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
@@ -110,7 +110,7 @@ static int wl_config(oconfig_item_t *ci) /* {{{ */
       if (format_seen) {
         WARNING("write_log plugin: Redefining option `%s'.", child->key);
       }
-      format_seen = 1;
+      format_seen = true;
 
       if (strcasecmp("Graphite", str) == 0)
         wl_format = WL_FORMAT_GRAPHITE;
index 46b6d86..0cb1e02 100644 (file)
@@ -32,8 +32,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 
 #include <mongoc.h>
@@ -50,8 +50,8 @@ struct wm_node_s {
   char *user;
   char *passwd;
 
-  _Bool store_rates;
-  _Bool connected;
+  bool store_rates;
+  bool connected;
 
   mongoc_client_t *client;
   mongoc_database_t *database;
@@ -63,7 +63,7 @@ typedef struct wm_node_s wm_node_t;
  * Functions
  */
 static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */
-                              const value_list_t *vl, _Bool store_rates) {
+                              const value_list_t *vl, bool store_rates) {
   bson_t *ret;
   bson_t subarray;
   gauge_t *rates;
@@ -96,7 +96,7 @@ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */
   for (size_t i = 0; i < ds->ds_num; i++) {
     char key[16];
 
-    snprintf(key, sizeof(key), "%zu", i);
+    snprintf(key, sizeof(key), "%" PRIsz, i);
 
     if (ds->ds[i].type == DS_TYPE_GAUGE)
       BSON_APPEND_DOUBLE(&subarray, key, vl->values[i].gauge);
@@ -109,7 +109,7 @@ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */
     else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
       BSON_APPEND_INT64(&subarray, key, vl->values[i].absolute);
     else {
-      ERROR("write_mongodb plugin: Unknown ds_type %d for index %zu",
+      ERROR("write_mongodb plugin: Unknown ds_type %d for index %" PRIsz,
             ds->ds[i].type, i);
       bson_destroy(ret);
       return NULL;
@@ -121,7 +121,7 @@ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */
   for (size_t i = 0; i < ds->ds_num; i++) {
     char key[16];
 
-    snprintf(key, sizeof(key), "%zu", i);
+    snprintf(key, sizeof(key), "%" PRIsz, i);
 
     if (store_rates)
       BSON_APPEND_UTF8(&subarray, key, "gauge");
@@ -134,7 +134,7 @@ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */
   for (size_t i = 0; i < ds->ds_num; i++) {
     char key[16];
 
-    snprintf(key, sizeof(key), "%zu", i);
+    snprintf(key, sizeof(key), "%" PRIsz, i);
     BSON_APPEND_UTF8(&subarray, key, ds->ds[i].name);
   }
   bson_append_array_end(ret, &subarray); /* }}} dsnames */
@@ -144,7 +144,7 @@ static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */
   size_t error_location;
   if (!bson_validate(ret, BSON_VALIDATE_UTF8, &error_location)) {
     ERROR("write_mongodb plugin: Error in generated BSON document "
-          "at byte %zu",
+          "at byte %" PRIsz,
           error_location);
     bson_destroy(ret);
     return NULL;
@@ -170,7 +170,7 @@ static int wm_initialize(wm_node_t *node) /* {{{ */
             "authentication string.");
       mongoc_client_destroy(node->client);
       node->client = NULL;
-      node->connected = 0;
+      node->connected = false;
       return -1;
     }
 
@@ -179,7 +179,7 @@ static int wm_initialize(wm_node_t *node) /* {{{ */
       ERROR("write_mongodb plugin: Authenticating to [%s]:%d for database "
             "\"%s\" as user \"%s\" failed.",
             node->host, node->port, node->db, node->user);
-      node->connected = 0;
+      node->connected = false;
       sfree(uri);
       return -1;
     }
@@ -190,7 +190,7 @@ static int wm_initialize(wm_node_t *node) /* {{{ */
             "authentication string.");
       mongoc_client_destroy(node->client);
       node->client = NULL;
-      node->connected = 0;
+      node->connected = false;
       return -1;
     }
 
@@ -198,7 +198,7 @@ static int wm_initialize(wm_node_t *node) /* {{{ */
     if (!node->client) {
       ERROR("write_mongodb plugin: Connecting to [%s]:%d failed.", node->host,
             node->port);
-      node->connected = 0;
+      node->connected = false;
       sfree(uri);
       return -1;
     }
@@ -210,11 +210,11 @@ static int wm_initialize(wm_node_t *node) /* {{{ */
     ERROR("write_mongodb plugin: error creating/getting database");
     mongoc_client_destroy(node->client);
     node->client = NULL;
-    node->connected = 0;
+    node->connected = false;
     return -1;
   }
 
-  node->connected = 1;
+  node->connected = true;
   return 0;
 } /* }}} int wm_initialize */
 
@@ -248,7 +248,7 @@ static int wm_write(const data_set_t *ds, /* {{{ */
     mongoc_client_destroy(node->client);
     node->database = NULL;
     node->client = NULL;
-    node->connected = 0;
+    node->connected = false;
     pthread_mutex_unlock(&node->lock);
     bson_destroy(bson_record);
     return -1;
@@ -263,7 +263,7 @@ static int wm_write(const data_set_t *ds, /* {{{ */
     mongoc_client_destroy(node->client);
     node->database = NULL;
     node->client = NULL;
-    node->connected = 0;
+    node->connected = false;
     pthread_mutex_unlock(&node->lock);
     bson_destroy(bson_record);
     mongoc_collection_destroy(collection);
@@ -291,7 +291,7 @@ static void wm_config_free(void *ptr) /* {{{ */
   mongoc_client_destroy(node->client);
   node->database = NULL;
   node->client = NULL;
-  node->connected = 0;
+  node->connected = false;
 
   sfree(node->host);
   sfree(node);
@@ -312,7 +312,7 @@ static int wm_config_node(oconfig_item_t *ci) /* {{{ */
     return ENOMEM;
   }
   node->port = MONGOC_DEFAULT_PORT;
-  node->store_rates = 1;
+  node->store_rates = true;
   pthread_mutex_init(&node->lock, /* attr = */ NULL);
 
   status = cf_util_get_string_buffer(ci, node->name, sizeof(node->name));
index 8554580..b109d42 100644 (file)
@@ -26,9 +26,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
 #include "utils_complain.h"
 #include "utils_time.h"
 
 static c_avl_tree_t *metrics;
 static pthread_mutex_t metrics_lock = PTHREAD_MUTEX_INITIALIZER;
 
+static char *httpd_host = NULL;
 static unsigned short httpd_port = 9103;
 static struct MHD_Daemon *httpd;
 
 static cdtime_t staleness_delta = PROMETHEUS_DEFAULT_STALENESS_DELTA;
 
-/* Unfortunately, protoc-c doesn't export it's implementation of varint, so we
+/* Unfortunately, protoc-c doesn't export its implementation of varint, so we
  * need to implement our own. */
 static size_t varint(uint8_t buffer[static VARINT_UINT32_BYTES],
                      uint32_t value) {
@@ -244,9 +245,8 @@ static int http_handler(void *cls, struct MHD_Connection *connection,
 
   char const *accept = MHD_lookup_connection_value(connection, MHD_HEADER_KIND,
                                                    MHD_HTTP_HEADER_ACCEPT);
-  _Bool want_proto =
-      (accept != NULL) &&
-      (strstr(accept, "application/vnd.google.protobuf") != NULL);
+  bool want_proto = (accept != NULL) &&
+                    (strstr(accept, "application/vnd.google.protobuf") != NULL);
 
   uint8_t scratch[4096] = {0};
   ProtobufCBufferSimple simple = PROTOBUF_C_BUFFER_SIMPLE_INIT(scratch);
@@ -689,7 +689,7 @@ static char *metric_family_name(data_set_t const *ds, value_list_t const *vl,
  * necessary. */
 static Io__Prometheus__Client__MetricFamily *
 metric_family_get(data_set_t const *ds, value_list_t const *vl, size_t ds_index,
-                  _Bool allocate) {
+                  bool allocate) {
   char *name = metric_family_name(ds, vl, ds_index);
   if (name == NULL) {
     ERROR("write_prometheus plugin: Allocating metric family name failed.");
@@ -747,7 +747,7 @@ static int prom_open_socket(int addrfamily) {
   snprintf(service, sizeof(service), "%hu", httpd_port);
 
   struct addrinfo *res;
-  int status = getaddrinfo(NULL, service,
+  int status = getaddrinfo(httpd_host, service,
                            &(struct addrinfo){
                                .ai_flags = AI_PASSIVE | AI_ADDRCONFIG,
                                .ai_family = addrfamily,
@@ -769,11 +769,9 @@ static int prom_open_socket(int addrfamily) {
     if (fd == -1)
       continue;
 
-    int tmp = 1;
-    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)) != 0) {
-      char errbuf[1024];
-      WARNING("write_prometheus: setsockopt(SO_REUSEADDR) failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) != 0) {
+      WARNING("write_prometheus plugin: setsockopt(SO_REUSEADDR) failed: %s",
+              STRERRNO);
       close(fd);
       fd = -1;
       continue;
@@ -791,6 +789,15 @@ static int prom_open_socket(int addrfamily) {
       continue;
     }
 
+    char str_node[NI_MAXHOST];
+    char str_service[NI_MAXSERV];
+
+    getnameinfo(ai->ai_addr, ai->ai_addrlen, str_node, sizeof(str_node),
+                str_service, sizeof(str_service),
+                NI_NUMERICHOST | NI_NUMERICSERV);
+
+    INFO("write_prometheus plugin: Listening on [%s]:%s.", str_node,
+         str_service);
     break;
   }
 
@@ -805,7 +812,9 @@ static struct MHD_Daemon *prom_start_daemon() {
   if (fd == -1)
     fd = prom_open_socket(PF_INET);
   if (fd == -1) {
-    ERROR("write_prometheus plugin: Opening a listening socket failed.");
+    ERROR("write_prometheus plugin: Opening a listening socket for [%s]:%hu "
+          "failed.",
+          (httpd_host != NULL) ? httpd_host : "::", httpd_port);
     return NULL;
   }
 
@@ -852,7 +861,15 @@ static int prom_config(oconfig_item_t *ci) {
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
 
-    if (strcasecmp("Port", child->key) == 0) {
+    if (strcasecmp("Host", child->key) == 0) {
+#if MHD_VERSION >= 0x00090000
+      cf_util_get_string(child, &httpd_host);
+#else
+      ERROR("write_prometheus plugin: Option `Host' not supported. Please "
+            "upgrade libmicrohttpd to at least 0.9.0");
+      return -1;
+#endif
+    } else if (strcasecmp("Port", child->key) == 0) {
       int status = cf_util_get_port_number(child);
       if (status > 0)
         httpd_port = (unsigned short)status;
@@ -880,7 +897,6 @@ static int prom_init() {
   if (httpd == NULL) {
     httpd = prom_start_daemon();
     if (httpd == NULL) {
-      ERROR("write_prometheus plugin: MHD_start_daemon() failed.");
       return -1;
     }
     DEBUG("write_prometheus plugin: Successfully started microhttpd %s",
@@ -896,7 +912,7 @@ static int prom_write(data_set_t const *ds, value_list_t const *vl,
 
   for (size_t i = 0; i < ds->ds_num; i++) {
     Io__Prometheus__Client__MetricFamily *fam =
-        metric_family_get(ds, vl, i, /* allocate = */ 1);
+        metric_family_get(ds, vl, i, /* allocate = */ true);
     if (fam == NULL)
       continue;
 
@@ -923,7 +939,7 @@ static int prom_missing(value_list_t const *vl,
 
   for (size_t i = 0; i < ds->ds_num; i++) {
     Io__Prometheus__Client__MetricFamily *fam =
-        metric_family_get(ds, vl, i, /* allocate = */ 0);
+        metric_family_get(ds, vl, i, /* allocate = */ false);
     if (fam == NULL)
       continue;
 
@@ -973,6 +989,8 @@ static int prom_shutdown() {
   }
   pthread_mutex_unlock(&metrics_lock);
 
+  sfree(httpd_host);
+
   return 0;
 }
 
index 7dd5029..324999c 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <hiredis/hiredis.h>
 #include <sys/time.h>
@@ -46,7 +46,7 @@ struct wr_node_s {
   int database;
   int max_set_size;
   int max_set_duration;
-  _Bool store_rates;
+  bool store_rates;
 
   redisContext *conn;
   pthread_mutex_t lock;
@@ -129,10 +129,10 @@ static int wr_write(const data_set_t *ds, /* {{{ */
   if (node->max_set_duration > 0) {
     /*
      * remove element, scored less than 'current-max_set_duration'
-     * '(%d' indicates 'less than' in redis CLI.
+     * '(...' indicates 'less than' in redis CLI.
      */
-    rr = redisCommand(node->conn, "ZREMRANGEBYSCORE %s -1 (%d", key,
-                      (time - node->max_set_duration) + 1);
+    rr = redisCommand(node->conn, "ZREMRANGEBYSCORE %s -1 (%.9f", key,
+                      (CDTIME_T_TO_DOUBLE(vl->time) - node->max_set_duration));
     if (rr == NULL)
       WARNING("ZREMRANGEBYSCORE command error. key:%s message:%s", key,
               node->conn->errstr);
@@ -184,14 +184,14 @@ static int wr_config_node(oconfig_item_t *ci) /* {{{ */
     return ENOMEM;
   node->host = NULL;
   node->port = 0;
-  node->timeout.tv_sec = 0;
-  node->timeout.tv_usec = 1000;
+  node->timeout.tv_sec = 1;
+  node->timeout.tv_usec = 0;
   node->conn = NULL;
   node->prefix = NULL;
   node->database = 0;
   node->max_set_size = -1;
   node->max_set_duration = -1;
-  node->store_rates = 1;
+  node->store_rates = true;
   pthread_mutex_init(&node->lock, /* attr = */ NULL);
 
   status = cf_util_get_string_buffer(ci, node->name, sizeof(node->name));
@@ -213,8 +213,11 @@ static int wr_config_node(oconfig_item_t *ci) /* {{{ */
       }
     } else if (strcasecmp("Timeout", child->key) == 0) {
       status = cf_util_get_int(child, &timeout);
-      if (status == 0)
-        node->timeout.tv_usec = timeout;
+      if (status == 0) {
+        node->timeout.tv_usec = timeout * 1000;
+        node->timeout.tv_sec = node->timeout.tv_usec / 1000000L;
+        node->timeout.tv_usec %= 1000000L;
+      }
     } else if (strcasecmp("Prefix", child->key) == 0) {
       status = cf_util_get_string(child, &node->prefix);
     } else if (strcasecmp("Database", child->key) == 0) {
index 86f0c1f..62ddc67 100644 (file)
@@ -30,8 +30,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 #include "utils_complain.h"
 #include "write_riemann_threshold.h"
@@ -48,11 +48,11 @@ struct riemann_host {
   char *name;
   char *event_service_prefix;
   pthread_mutex_t lock;
-  _Bool batch_mode;
-  _Bool notifications;
-  _Bool check_thresholds;
-  _Bool store_rates;
-  _Bool always_append_ds;
+  bool batch_mode;
+  bool notifications;
+  bool check_thresholds;
+  bool store_rates;
+  bool always_append_ds;
   char *node;
   int port;
   riemann_client_type_t client_type;
@@ -181,9 +181,7 @@ static int wrr_send(struct riemann_host *host, riemann_message_t *msg) {
   return status;
 }
 
-static riemann_message_t *
-wrr_notification_to_message(struct riemann_host *host, /* {{{ */
-                            notification_t const *n) {
+static riemann_message_t *wrr_notification_to_message(notification_t const *n) {
   riemann_message_t *msg;
   riemann_event_t *event;
   char service_buffer[6 * DATA_MAX_NAME_LEN];
@@ -271,7 +269,7 @@ wrr_notification_to_message(struct riemann_host *host, /* {{{ */
         "host = \"%s\", service = \"%s\", state = \"%s\"",
         event->host, event->service, event->state);
   return msg;
-} /* }}} riemann_message_t *wrr_notification_to_message */
+}
 
 static riemann_event_t *
 wrr_value_to_event(struct riemann_host const *host, /* {{{ */
@@ -362,7 +360,7 @@ wrr_value_to_event(struct riemann_host const *host, /* {{{ */
   {
     char ds_index[DATA_MAX_NAME_LEN];
 
-    snprintf(ds_index, sizeof(ds_index), "%zu", index);
+    snprintf(ds_index, sizeof(ds_index), "%" PRIsz, index);
     riemann_event_string_attribute_add(event, "ds_index", ds_index);
   }
 
@@ -546,7 +544,7 @@ static int wrr_notification(const notification_t *n, user_data_t *ud) /* {{{ */
   /*
    * Never batch for notifications, send them ASAP
    */
-  msg = wrr_notification_to_message(host, n);
+  msg = wrr_notification_to_message(n);
   if (msg == NULL)
     return -1;
 
@@ -632,11 +630,11 @@ static int wrr_config_node(oconfig_item_t *ci) /* {{{ */
   host->reference_count = 1;
   host->node = NULL;
   host->port = 0;
-  host->notifications = 1;
-  host->check_thresholds = 0;
-  host->store_rates = 1;
-  host->always_append_ds = 0;
-  host->batch_mode = 1;
+  host->notifications = true;
+  host->check_thresholds = false;
+  host->store_rates = true;
+  host->always_append_ds = false;
+  host->batch_mode = true;
   host->batch_max = RIEMANN_BATCH_MAX; /* typical MSS */
   host->batch_init = cdtime();
   host->batch_timeout = 0;
@@ -701,21 +699,13 @@ static int wrr_config_node(oconfig_item_t *ci) /* {{{ */
     } else if (strcasecmp("Port", child->key) == 0) {
       host->port = cf_util_get_port_number(child);
       if (host->port == -1) {
-        ERROR("write_riemann plugin: Invalid argument "
-              "configured for the \"Port\" "
-              "option.");
         break;
       }
     } else if (strcasecmp("Protocol", child->key) == 0) {
       char tmp[16];
       status = cf_util_get_string_buffer(child, tmp, sizeof(tmp));
-      if (status != 0) {
-        ERROR("write_riemann plugin: cf_util_get_"
-              "string_buffer failed with "
-              "status %i.",
-              status);
+      if (status != 0)
         break;
-      }
 
       if (strcasecmp("UDP", tmp) == 0)
         host->client_type = RIEMANN_CLIENT_UDP;
@@ -731,31 +721,16 @@ static int wrr_config_node(oconfig_item_t *ci) /* {{{ */
                 tmp);
     } else if (strcasecmp("TLSCAFile", child->key) == 0) {
       status = cf_util_get_string(child, &host->tls_ca_file);
-      if (status != 0) {
-        ERROR("write_riemann plugin: cf_util_get_"
-              "string_buffer failed with "
-              "status %i.",
-              status);
+      if (status != 0)
         break;
-      }
     } else if (strcasecmp("TLSCertFile", child->key) == 0) {
       status = cf_util_get_string(child, &host->tls_cert_file);
-      if (status != 0) {
-        ERROR("write_riemann plugin: cf_util_get_"
-              "string_buffer failed with "
-              "status %i.",
-              status);
+      if (status != 0)
         break;
-      }
     } else if (strcasecmp("TLSKeyFile", child->key) == 0) {
       status = cf_util_get_string(child, &host->tls_key_file);
-      if (status != 0) {
-        ERROR("write_riemann plugin: cf_util_get_"
-              "string_buffer failed with "
-              "status %i.",
-              status);
+      if (status != 0)
         break;
-      }
     } else if (strcasecmp("StoreRates", child->key) == 0) {
       status = cf_util_get_boolean(child, &host->store_rates);
       if (status != 0)
index 35f3814..041ed7d 100644 (file)
@@ -27,9 +27,9 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
-#include "utils_avltree.h"
+#include "utils/avltree/avltree.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 #include "utils_threshold.h"
 #include "write_riemann_threshold.h"
@@ -63,7 +63,7 @@ static int ut_check_one_data_source(
   if (ds != NULL) {
     ds_name = ds->ds[ds_index].name;
     if ((th->data_source[0] != 0) && (strcmp(ds_name, th->data_source) != 0))
-      return STATE_OKAY;
+      return STATE_UNKNOWN;
   }
 
   if ((th->flags & UT_FLAG_INVERT) != 0) {
@@ -73,8 +73,9 @@ static int ut_check_one_data_source(
 
   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
    * and probably, do not work as you expect. Enjoy! :D */
-  if ((th->hysteresis > 0) &&
-      ((prev_state = uc_get_state(ds, vl)) != STATE_OKAY)) {
+  prev_state = uc_get_state(ds, vl);
+  if ((th->hysteresis > 0) && (prev_state != STATE_OKAY) &&
+      (prev_state != STATE_UNKNOWN)) {
     switch (prev_state) {
     case STATE_ERROR:
       if ((!isnan(th->failure_min) &&
index bd7a56d..7d08fb5 100644 (file)
 
 #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>
 #include <inttypes.h>
@@ -108,10 +107,10 @@ struct sensu_host {
 #define F_READY 0x01
   uint8_t flags;
   pthread_mutex_t lock;
-  _Bool notifications;
-  _Bool metrics;
-  _Bool store_rates;
-  _Bool always_append_ds;
+  bool notifications;
+  bool metrics;
+  bool store_rates;
+  bool always_append_ds;
   char *separator;
   char *node;
   char *service;
@@ -120,8 +119,8 @@ struct sensu_host {
   int reference_count;
 };
 
-static char *sensu_tags = NULL;
-static char **sensu_attrs = NULL;
+static char *sensu_tags;
+static char **sensu_attrs;
 static size_t sensu_attrs_num;
 
 static int add_str_to_list(struct str_list *strs,
@@ -310,8 +309,8 @@ static int sensu_format_name2(char *ret, int ret_len, const char *hostname,
 
 static void in_place_replace_sensu_name_reserved(char *orig_name) /* {{{ */
 {
-  int len = strlen(orig_name);
-  for (int i = 0; i < len; i++) {
+  size_t len = strlen(orig_name);
+  for (size_t i = 0; i < len; i++) {
     // some plugins like ipmi generate special characters in metric name
     switch (orig_name[i]) {
     case '(':
@@ -338,8 +337,7 @@ static void in_place_replace_sensu_name_reserved(char *orig_name) /* {{{ */
 
 static char *sensu_value_to_json(struct sensu_host const *host, /* {{{ */
                                  data_set_t const *ds, value_list_t const *vl,
-                                 size_t index, gauge_t const *rates,
-                                 int status) {
+                                 size_t index, gauge_t const *rates) {
   char name_buffer[5 * DATA_MAX_NAME_LEN];
   char service_buffer[6 * DATA_MAX_NAME_LEN];
   char *ret_str;
@@ -455,7 +453,7 @@ static char *sensu_value_to_json(struct sensu_host const *host, /* {{{ */
   // incorporate the data source index
   {
     char ds_index[DATA_MAX_NAME_LEN];
-    snprintf(ds_index, sizeof(ds_index), "%zu", index);
+    snprintf(ds_index, sizeof(ds_index), "%" PRIsz, index);
     res = my_asprintf(&temp_str, "%s, \"collectd_data_source_index\": %s",
                       ret_str, ds_index);
     free(ret_str);
@@ -520,7 +518,8 @@ static char *sensu_value_to_json(struct sensu_host const *host, /* {{{ */
         return NULL;
       }
     } else {
-      res = my_asprintf(&value_str, "%llu", vl->values[index].counter);
+      res = my_asprintf(&value_str, "%" PRIu64,
+                        (uint64_t)vl->values[index].counter);
       if (res == -1) {
         free(ret_str);
         ERROR("write_sensu plugin: Unable to alloc memory");
@@ -627,7 +626,7 @@ static char *replace_str(const char *str, const char *old, /* {{{ */
     r += newlen;
     p = q + oldlen;
   }
-  strncpy(r, p, strlen(p));
+  sstrncpy(r, p, retlen + 1);
 
   return ret;
 } /* }}} char *replace_str */
@@ -880,11 +879,9 @@ static int sensu_send_msg(struct sensu_host *host, const char *msg) /* {{{ */
   sensu_close_socket(host);
 
   if (status != 0) {
-    char errbuf[1024];
     ERROR("write_sensu plugin: Sending to Sensu at %s:%s failed: %s",
           (host->node != NULL) ? host->node : SENSU_HOST,
-          (host->service != NULL) ? host->service : SENSU_PORT,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          (host->service != NULL) ? host->service : SENSU_PORT, STRERRNO);
     return -1;
   }
 
@@ -928,7 +925,7 @@ static int sensu_write(const data_set_t *ds, /* {{{ */
     }
   }
   for (size_t i = 0; i < vl->values_len; i++) {
-    msg = sensu_value_to_json(host, ds, vl, (int)i, rates, statuses[i]);
+    msg = sensu_value_to_json(host, ds, vl, (int)i, rates);
     if (msg == NULL) {
       sfree(rates);
       pthread_mutex_unlock(&host->lock);
@@ -1021,10 +1018,10 @@ static int sensu_config_node(oconfig_item_t *ci) /* {{{ */
   host->reference_count = 1;
   host->node = NULL;
   host->service = NULL;
-  host->notifications = 0;
-  host->metrics = 0;
-  host->store_rates = 1;
-  host->always_append_ds = 0;
+  host->notifications = false;
+  host->metrics = false;
+  host->store_rates = true;
+  host->always_append_ds = false;
   host->metric_handlers.nb_strs = 0;
   host->metric_handlers.strs = NULL;
   host->notification_handlers.nb_strs = 0;
@@ -1087,12 +1084,8 @@ static int sensu_config_node(oconfig_item_t *ci) /* {{{ */
         break;
     } else if (strcasecmp("Port", child->key) == 0) {
       status = cf_util_get_service(child, &host->service);
-      if (status != 0) {
-        ERROR("write_sensu plugin: Invalid argument "
-              "configured for the \"Port\" "
-              "option.");
+      if (status != 0)
         break;
-      }
     } else if (strcasecmp("StoreRates", child->key) == 0) {
       status = cf_util_get_boolean(child, &host->store_rates);
       if (status != 0)
@@ -1126,16 +1119,17 @@ static int sensu_config_node(oconfig_item_t *ci) /* {{{ */
     return -1;
   }
 
-  if ((host->notification_handlers.nb_strs > 0) && (host->notifications == 0)) {
+  if ((host->notification_handlers.nb_strs > 0) &&
+      (host->notifications == false)) {
     WARNING("write_sensu plugin: NotificationHandler given so forcing "
             "notifications to be enabled");
     host->notifications = 1;
   }
 
-  if ((host->metric_handlers.nb_strs > 0) && (host->metrics == 0)) {
+  if ((host->metric_handlers.nb_strs > 0) && (host->metrics == false)) {
     WARNING("write_sensu plugin: MetricHandler given so forcing metrics to be "
             "enabled");
-    host->metrics = 1;
+    host->metrics = true;
   }
 
   if (!(host->notifications || host->metrics)) {
@@ -1227,10 +1221,10 @@ static int sensu_config(oconfig_item_t *ci) /* {{{ */
         continue;
 
       status = add_str_to_list(&sensu_tags_arr, tmp);
+      DEBUG("write_sensu plugin: Got tag: %s", tmp);
       sfree(tmp);
       if (status != 0)
         continue;
-      DEBUG("write_sensu plugin: Got tag: %s", tmp);
     } else {
       WARNING("write_sensu plugin: Ignoring unknown "
               "configuration option \"%s\" at top level.",
diff --git a/src/write_stackdriver.c b/src/write_stackdriver.c
new file mode 100644 (file)
index 0000000..fd0192a
--- /dev/null
@@ -0,0 +1,689 @@
+/**
+ * collectd - src/write_stackdriver.c
+ * ISC license
+ *
+ * Copyright (C) 2017  Florian Forster
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors:
+ *   Florian Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "configfile.h"
+#include "plugin.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 <yajl/yajl_tree.h>
+
+/*
+ * Private variables
+ */
+#ifndef GCM_API_URL
+#define GCM_API_URL "https://monitoring.googleapis.com/v3"
+#endif
+
+#ifndef MONITORING_SCOPE
+#define MONITORING_SCOPE "https://www.googleapis.com/auth/monitoring"
+#endif
+
+struct wg_callback_s {
+  /* config */
+  char *email;
+  char *project;
+  char *url;
+  sd_resource_t *resource;
+
+  /* runtime */
+  oauth_t *auth;
+  sd_output_t *formatter;
+  CURL *curl;
+  char curl_errbuf[CURL_ERROR_SIZE];
+  /* used by flush */
+  size_t timeseries_count;
+  cdtime_t send_buffer_init_time;
+
+  pthread_mutex_t lock;
+};
+typedef struct wg_callback_s wg_callback_t;
+
+struct wg_memory_s {
+  char *memory;
+  size_t size;
+};
+typedef struct wg_memory_s wg_memory_t;
+
+static size_t wg_write_memory_cb(void *contents, size_t size,
+                                 size_t nmemb, /* {{{ */
+                                 void *userp) {
+  size_t realsize = size * nmemb;
+  wg_memory_t *mem = (wg_memory_t *)userp;
+
+  if (0x7FFFFFF0 < mem->size || 0x7FFFFFF0 - mem->size < realsize) {
+    ERROR("integer overflow");
+    return 0;
+  }
+
+  mem->memory = (char *)realloc((void *)mem->memory, mem->size + realsize + 1);
+  if (mem->memory == NULL) {
+    /* out of memory! */
+    ERROR("wg_write_memory_cb: not enough memory (realloc returned NULL)");
+    return 0;
+  }
+
+  memcpy(&(mem->memory[mem->size]), contents, realsize);
+  mem->size += realsize;
+  mem->memory[mem->size] = 0;
+  return realsize;
+} /* }}} size_t wg_write_memory_cb */
+
+static char *wg_get_authorization_header(wg_callback_t *cb) { /* {{{ */
+  int status = 0;
+  char access_token[256];
+  char authorization_header[256];
+
+  assert((cb->auth != NULL) || gce_check());
+  if (cb->auth != NULL)
+    status = oauth_access_token(cb->auth, access_token, sizeof(access_token));
+  else
+    status = gce_access_token(cb->email, access_token, sizeof(access_token));
+  if (status != 0) {
+    ERROR("write_stackdriver plugin: Failed to get access token");
+    return NULL;
+  }
+
+  status = snprintf(authorization_header, sizeof(authorization_header),
+                    "Authorization: Bearer %s", access_token);
+  if ((status < 1) || ((size_t)status >= sizeof(authorization_header)))
+    return NULL;
+
+  return strdup(authorization_header);
+} /* }}} char *wg_get_authorization_header */
+
+typedef struct {
+  int code;
+  char *message;
+} api_error_t;
+
+static api_error_t *parse_api_error(char const *body) {
+  char errbuf[1024];
+  yajl_val root = yajl_tree_parse(body, errbuf, sizeof(errbuf));
+  if (root == NULL) {
+    ERROR("write_stackdriver plugin: yajl_tree_parse failed: %s", errbuf);
+    return NULL;
+  }
+
+  api_error_t *err = calloc(1, sizeof(*err));
+  if (err == NULL) {
+    ERROR("write_stackdriver plugin: calloc failed");
+    yajl_tree_free(root);
+    return NULL;
+  }
+
+  yajl_val code = yajl_tree_get(root, (char const *[]){"error", "code", NULL},
+                                yajl_t_number);
+  if (YAJL_IS_INTEGER(code)) {
+    err->code = YAJL_GET_INTEGER(code);
+  }
+
+  yajl_val message = yajl_tree_get(
+      root, (char const *[]){"error", "message", NULL}, yajl_t_string);
+  if (YAJL_IS_STRING(message)) {
+    char const *m = YAJL_GET_STRING(message);
+    if (m != NULL) {
+      err->message = strdup(m);
+    }
+  }
+
+  return err;
+}
+
+static char *api_error_string(api_error_t *err, char *buffer,
+                              size_t buffer_size) {
+  if (err == NULL) {
+    strncpy(buffer, "Unknown error (API error is NULL)", buffer_size);
+  } else if (err->message == NULL) {
+    snprintf(buffer, buffer_size, "API error %d", err->code);
+  } else {
+    snprintf(buffer, buffer_size, "API error %d: %s", err->code, err->message);
+  }
+
+  return buffer;
+}
+#define API_ERROR_STRING(err) api_error_string(err, (char[1024]){""}, 1024)
+
+// do_post does a HTTP POST request, assuming a JSON payload and using OAuth
+// authentication. Returns -1 on error and the HTTP status code otherwise.
+// ret_content, if not NULL, will contain the server's response.
+// If ret_content is provided and the server responds with a 4xx or 5xx error,
+// an appropriate message will be logged.
+static int do_post(wg_callback_t *cb, char const *url, void const *payload,
+                   wg_memory_t *ret_content) {
+  if (cb->curl == NULL) {
+    cb->curl = curl_easy_init();
+    if (cb->curl == NULL) {
+      ERROR("write_stackdriver plugin: curl_easy_init() failed");
+      return -1;
+    }
+
+    curl_easy_setopt(cb->curl, CURLOPT_ERRORBUFFER, cb->curl_errbuf);
+    curl_easy_setopt(cb->curl, CURLOPT_NOSIGNAL, 1L);
+  }
+
+  curl_easy_setopt(cb->curl, CURLOPT_POST, 1L);
+  curl_easy_setopt(cb->curl, CURLOPT_URL, url);
+
+  long timeout_ms = 2 * CDTIME_T_TO_MS(plugin_get_interval());
+  if (timeout_ms < 10000) {
+    timeout_ms = 10000;
+  }
+  curl_easy_setopt(cb->curl, CURLOPT_TIMEOUT_MS, timeout_ms);
+
+  /* header */
+  char *auth_header = wg_get_authorization_header(cb);
+  if (auth_header == NULL) {
+    ERROR("write_stackdriver plugin: getting access token failed with");
+    return -1;
+  }
+
+  struct curl_slist *headers =
+      curl_slist_append(NULL, "Content-Type: application/json");
+  headers = curl_slist_append(headers, auth_header);
+  curl_easy_setopt(cb->curl, CURLOPT_HTTPHEADER, headers);
+
+  curl_easy_setopt(cb->curl, CURLOPT_POSTFIELDS, payload);
+
+  curl_easy_setopt(cb->curl, CURLOPT_WRITEFUNCTION,
+                   ret_content ? wg_write_memory_cb : NULL);
+  curl_easy_setopt(cb->curl, CURLOPT_WRITEDATA, ret_content);
+
+  int status = curl_easy_perform(cb->curl);
+
+  /* clean up that has to happen in any case */
+  curl_slist_free_all(headers);
+  sfree(auth_header);
+  curl_easy_setopt(cb->curl, CURLOPT_HTTPHEADER, NULL);
+  curl_easy_setopt(cb->curl, CURLOPT_WRITEFUNCTION, NULL);
+  curl_easy_setopt(cb->curl, CURLOPT_WRITEDATA, NULL);
+
+  if (status != CURLE_OK) {
+    ERROR("write_stackdriver plugin: POST %s failed: %s", url, cb->curl_errbuf);
+    if (ret_content != NULL) {
+      sfree(ret_content->memory);
+      ret_content->size = 0;
+    }
+    return -1;
+  }
+
+  long http_code = 0;
+  curl_easy_getinfo(cb->curl, CURLINFO_RESPONSE_CODE, &http_code);
+
+  if (ret_content != NULL) {
+    if ((http_code >= 400) && (http_code < 500)) {
+      ERROR("write_stackdriver plugin: POST %s: %s", url,
+            API_ERROR_STRING(parse_api_error(ret_content->memory)));
+    } else if (http_code >= 500) {
+      WARNING("write_stackdriver plugin: POST %s: %s", url,
+              ret_content->memory);
+    }
+  }
+
+  return (int)http_code;
+} /* int do_post */
+
+static int wg_call_metricdescriptor_create(wg_callback_t *cb,
+                                           char const *payload) {
+  char url[1024];
+  snprintf(url, sizeof(url), "%s/projects/%s/metricDescriptors", cb->url,
+           cb->project);
+  wg_memory_t response = {0};
+
+  int status = do_post(cb, url, payload, &response);
+  if (status == -1) {
+    ERROR("write_stackdriver plugin: POST %s failed", url);
+    return -1;
+  }
+  sfree(response.memory);
+
+  if (status != 200) {
+    ERROR("write_stackdriver plugin: POST %s: unexpected response code: got "
+          "%d, want 200",
+          url, status);
+    return -1;
+  }
+  return 0;
+} /* int wg_call_metricdescriptor_create */
+
+static int wg_call_timeseries_write(wg_callback_t *cb, char const *payload) {
+  char url[1024];
+  snprintf(url, sizeof(url), "%s/projects/%s/timeSeries", cb->url, cb->project);
+  wg_memory_t response = {0};
+
+  int status = do_post(cb, url, payload, &response);
+  if (status == -1) {
+    ERROR("write_stackdriver plugin: POST %s failed", url);
+    return -1;
+  }
+  sfree(response.memory);
+
+  if (status != 200) {
+    ERROR("write_stackdriver plugin: POST %s: unexpected response code: got "
+          "%d, want 200",
+          url, status);
+    return -1;
+  }
+  return 0;
+} /* int wg_call_timeseries_write */
+
+static void wg_reset_buffer(wg_callback_t *cb) /* {{{ */
+{
+  cb->timeseries_count = 0;
+  cb->send_buffer_init_time = cdtime();
+} /* }}} wg_reset_buffer */
+
+static int wg_callback_init(wg_callback_t *cb) /* {{{ */
+{
+  if (cb->curl != NULL)
+    return 0;
+
+  cb->formatter = sd_output_create(cb->resource);
+  if (cb->formatter == NULL) {
+    ERROR("write_stackdriver plugin: sd_output_create failed.");
+    return -1;
+  }
+
+  cb->curl = curl_easy_init();
+  if (cb->curl == NULL) {
+    ERROR("write_stackdriver plugin: curl_easy_init failed.");
+    return -1;
+  }
+
+  curl_easy_setopt(cb->curl, CURLOPT_NOSIGNAL, 1L);
+  curl_easy_setopt(cb->curl, CURLOPT_USERAGENT,
+                   PACKAGE_NAME "/" PACKAGE_VERSION);
+  curl_easy_setopt(cb->curl, CURLOPT_ERRORBUFFER, cb->curl_errbuf);
+  wg_reset_buffer(cb);
+
+  return 0;
+} /* }}} int wg_callback_init */
+
+static int wg_flush_nolock(cdtime_t timeout, wg_callback_t *cb) /* {{{ */
+{
+  if (cb->timeseries_count == 0) {
+    cb->send_buffer_init_time = cdtime();
+    return 0;
+  }
+
+  /* timeout == 0  => flush unconditionally */
+  if (timeout > 0) {
+    cdtime_t now = cdtime();
+
+    if ((cb->send_buffer_init_time + timeout) > now)
+      return 0;
+  }
+
+  char *payload = sd_output_reset(cb->formatter);
+  int status = wg_call_timeseries_write(cb, payload);
+  wg_reset_buffer(cb);
+  return status;
+} /* }}} wg_flush_nolock */
+
+static int wg_flush(cdtime_t timeout, /* {{{ */
+                    const char *identifier __attribute__((unused)),
+                    user_data_t *user_data) {
+  wg_callback_t *cb;
+  int status;
+
+  if (user_data == NULL)
+    return -EINVAL;
+
+  cb = user_data->data;
+
+  pthread_mutex_lock(&cb->lock);
+
+  if (cb->curl == NULL) {
+    status = wg_callback_init(cb);
+    if (status != 0) {
+      ERROR("write_stackdriver plugin: wg_callback_init failed.");
+      pthread_mutex_unlock(&cb->lock);
+      return -1;
+    }
+  }
+
+  status = wg_flush_nolock(timeout, cb);
+  pthread_mutex_unlock(&cb->lock);
+
+  return status;
+} /* }}} int wg_flush */
+
+static void wg_callback_free(void *data) /* {{{ */
+{
+  wg_callback_t *cb = data;
+  if (cb == NULL)
+    return;
+
+  sd_output_destroy(cb->formatter);
+  cb->formatter = NULL;
+
+  sfree(cb->email);
+  sfree(cb->project);
+  sfree(cb->url);
+
+  oauth_destroy(cb->auth);
+  if (cb->curl) {
+    curl_easy_cleanup(cb->curl);
+  }
+
+  sfree(cb);
+} /* }}} void wg_callback_free */
+
+static int wg_metric_descriptors_create(wg_callback_t *cb, const data_set_t *ds,
+                                        const value_list_t *vl) {
+  /* {{{ */
+  for (size_t i = 0; i < ds->ds_num; i++) {
+    char buffer[4096];
+
+    int status = sd_format_metric_descriptor(buffer, sizeof(buffer), ds, vl, i);
+    if (status != 0) {
+      ERROR("write_stackdriver plugin: sd_format_metric_descriptor failed "
+            "with status "
+            "%d",
+            status);
+      return status;
+    }
+
+    status = wg_call_metricdescriptor_create(cb, buffer);
+    if (status != 0) {
+      ERROR("write_stackdriver plugin: wg_call_metricdescriptor_create failed "
+            "with "
+            "status %d",
+            status);
+      return status;
+    }
+  }
+
+  return sd_output_register_metric(cb->formatter, ds, vl);
+} /* }}} int wg_metric_descriptors_create */
+
+static int wg_write(const data_set_t *ds, const value_list_t *vl, /* {{{ */
+                    user_data_t *user_data) {
+  wg_callback_t *cb = user_data->data;
+  if (cb == NULL)
+    return EINVAL;
+
+  pthread_mutex_lock(&cb->lock);
+
+  if (cb->curl == NULL) {
+    int status = wg_callback_init(cb);
+    if (status != 0) {
+      ERROR("write_stackdriver plugin: wg_callback_init failed.");
+      pthread_mutex_unlock(&cb->lock);
+      return status;
+    }
+  }
+
+  int status;
+  while (42) {
+    status = sd_output_add(cb->formatter, ds, vl);
+    if (status == 0) { /* success */
+      break;
+    } else if (status == ENOBUFS) { /* success, flush */
+      wg_flush_nolock(0, cb);
+      status = 0;
+      break;
+    } else if (status == EEXIST) {
+      /* metric already in the buffer; flush and retry */
+      wg_flush_nolock(0, cb);
+      continue;
+    } else if (status == ENOENT) {
+      /* new metric, create metric descriptor first */
+      status = wg_metric_descriptors_create(cb, ds, vl);
+      if (status != 0) {
+        break;
+      }
+      continue;
+    } else {
+      break;
+    }
+  }
+
+  if (status == 0) {
+    cb->timeseries_count++;
+  }
+
+  pthread_mutex_unlock(&cb->lock);
+  return status;
+} /* }}} int wg_write */
+
+static void wg_check_scope(char const *email) /* {{{ */
+{
+  char *scope = gce_scope(email);
+  if (scope == NULL) {
+    WARNING("write_stackdriver plugin: Unable to determine scope of this "
+            "instance.");
+    return;
+  }
+
+  if (strstr(scope, MONITORING_SCOPE) == NULL) {
+    size_t scope_len;
+
+    /* Strip trailing newline characers for printing. */
+    scope_len = strlen(scope);
+    while ((scope_len > 0) && (iscntrl((int)scope[scope_len - 1])))
+      scope[--scope_len] = 0;
+
+    WARNING("write_stackdriver plugin: The determined scope of this instance "
+            "(\"%s\") does not contain the monitoring scope (\"%s\"). You need "
+            "to add this scope to the list of scopes passed to gcutil with "
+            "--service_account_scopes when creating the instance. "
+            "Alternatively, to use this plugin on an instance which does not "
+            "have this scope, use a Service Account.",
+            scope, MONITORING_SCOPE);
+  }
+
+  sfree(scope);
+} /* }}} void wg_check_scope */
+
+static int wg_config_resource(oconfig_item_t *ci, wg_callback_t *cb) /* {{{ */
+{
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+    ERROR("write_stackdriver plugin: The \"%s\" option requires exactly one "
+          "string "
+          "argument.",
+          ci->key);
+    return EINVAL;
+  }
+  char *resource_type = ci->values[0].value.string;
+
+  if (cb->resource != NULL) {
+    sd_resource_destroy(cb->resource);
+  }
+
+  cb->resource = sd_resource_create(resource_type);
+  if (cb->resource == NULL) {
+    ERROR("write_stackdriver plugin: sd_resource_create(\"%s\") failed.",
+          resource_type);
+    return ENOMEM;
+  }
+
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp("Label", child->key) == 0) {
+      if ((child->values_num != 2) ||
+          (child->values[0].type != OCONFIG_TYPE_STRING) ||
+          (child->values[1].type != OCONFIG_TYPE_STRING)) {
+        ERROR("write_stackdriver plugin: The \"Label\" option needs exactly "
+              "two string arguments.");
+        continue;
+      }
+
+      sd_resource_add_label(cb->resource, child->values[0].value.string,
+                            child->values[1].value.string);
+    }
+  }
+
+  return 0;
+} /* }}} int wg_config_resource */
+
+static int wg_config(oconfig_item_t *ci) /* {{{ */
+{
+  if (ci == NULL) {
+    return EINVAL;
+  }
+
+  wg_callback_t *cb = calloc(1, sizeof(*cb));
+  if (cb == NULL) {
+    ERROR("write_stackdriver plugin: calloc failed.");
+    return ENOMEM;
+  }
+  cb->url = strdup(GCM_API_URL);
+  pthread_mutex_init(&cb->lock, /* attr = */ NULL);
+
+  char *credential_file = NULL;
+
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+    if (strcasecmp("Project", child->key) == 0)
+      cf_util_get_string(child, &cb->project);
+    else if (strcasecmp("Email", child->key) == 0)
+      cf_util_get_string(child, &cb->email);
+    else if (strcasecmp("Url", child->key) == 0)
+      cf_util_get_string(child, &cb->url);
+    else if (strcasecmp("CredentialFile", child->key) == 0)
+      cf_util_get_string(child, &credential_file);
+    else if (strcasecmp("Resource", child->key) == 0)
+      wg_config_resource(child, cb);
+    else {
+      ERROR("write_stackdriver plugin: Invalid configuration option: %s.",
+            child->key);
+      wg_callback_free(cb);
+      return EINVAL;
+    }
+  }
+
+  /* Set up authentication */
+  /* Option 1: Credentials file given => use service account */
+  if (credential_file != NULL) {
+    oauth_google_t cfg =
+        oauth_create_google_file(credential_file, MONITORING_SCOPE);
+    if (cfg.oauth == NULL) {
+      ERROR("write_stackdriver plugin: oauth_create_google_file failed");
+      wg_callback_free(cb);
+      return EINVAL;
+    }
+    cb->auth = cfg.oauth;
+
+    if (cb->project == NULL) {
+      cb->project = cfg.project_id;
+      INFO("write_stackdriver plugin: Automatically detected project ID: "
+           "\"%s\"",
+           cb->project);
+    } else {
+      sfree(cfg.project_id);
+    }
+  }
+  /* Option 2: Look for credentials in well-known places */
+  if (cb->auth == NULL) {
+    oauth_google_t cfg = oauth_create_google_default(MONITORING_SCOPE);
+    cb->auth = cfg.oauth;
+
+    if (cb->project == NULL) {
+      cb->project = cfg.project_id;
+      INFO("write_stackdriver plugin: Automatically detected project ID: "
+           "\"%s\"",
+           cb->project);
+    } else {
+      sfree(cfg.project_id);
+    }
+  }
+
+  if ((cb->auth != NULL) && (cb->email != NULL)) {
+    NOTICE("write_stackdriver plugin: A service account email was configured "
+           "but is "
+           "not used for authentication because %s used instead.",
+           (credential_file != NULL) ? "a credential file was"
+                                     : "application default credentials were");
+  }
+
+  /* Option 3: Running on GCE => use metadata service */
+  if ((cb->auth == NULL) && gce_check()) {
+    wg_check_scope(cb->email);
+  } else if (cb->auth == NULL) {
+    ERROR("write_stackdriver plugin: Unable to determine credentials. Please "
+          "either "
+          "specify the \"Credentials\" option or set up Application Default "
+          "Credentials.");
+    wg_callback_free(cb);
+    return EINVAL;
+  }
+
+  if ((cb->project == NULL) && gce_check()) {
+    cb->project = gce_project_id();
+  }
+  if (cb->project == NULL) {
+    ERROR("write_stackdriver plugin: Unable to determine the project number. "
+          "Please specify the \"Project\" option manually.");
+    wg_callback_free(cb);
+    return EINVAL;
+  }
+
+  if ((cb->resource == NULL) && gce_check()) {
+    /* TODO(octo): add error handling */
+    cb->resource = sd_resource_create("gce_instance");
+    sd_resource_add_label(cb->resource, "project_id", gce_project_id());
+    sd_resource_add_label(cb->resource, "instance_id", gce_instance_id());
+    sd_resource_add_label(cb->resource, "zone", gce_zone());
+  }
+  if (cb->resource == NULL) {
+    /* TODO(octo): add error handling */
+    cb->resource = sd_resource_create("global");
+    sd_resource_add_label(cb->resource, "project_id", cb->project);
+  }
+
+  DEBUG("write_stackdriver plugin: Registering write callback with URL %s",
+        cb->url);
+  assert((cb->auth != NULL) || gce_check());
+
+  user_data_t user_data = {
+      .data = cb,
+  };
+  plugin_register_flush("write_stackdriver", wg_flush, &user_data);
+
+  user_data.free_func = wg_callback_free;
+  plugin_register_write("write_stackdriver", wg_write, &user_data);
+
+  return 0;
+} /* }}} int wg_config */
+
+static int wg_init(void) {
+  /* {{{ */
+  /* Call this while collectd is still single-threaded to avoid
+   * initialization issues in libgcrypt. */
+  curl_global_init(CURL_GLOBAL_SSL);
+
+  return 0;
+} /* }}} int wg_init */
+
+void module_register(void) /* {{{ */
+{
+  plugin_register_complex_config("write_stackdriver", wg_config);
+  plugin_register_init("write_stackdriver", wg_init);
+} /* }}} void module_register */
index 5c43eda..f8f4cb9 100644 (file)
@@ -43,8 +43,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 #include "utils_cache.h"
 #include "utils_random.h"
 
@@ -79,8 +79,8 @@ struct wt_callback {
   char *service;
   char *host_tags;
 
-  _Bool store_rates;
-  _Bool always_append_ds;
+  bool store_rates;
+  bool always_append_ds;
 
   char send_buf[WT_SEND_BUF_SIZE];
   size_t send_buf_free;
@@ -89,13 +89,13 @@ struct wt_callback {
 
   pthread_mutex_t send_lock;
 
-  _Bool connect_failed_log_enabled;
+  bool connect_failed_log_enabled;
   int connect_dns_failed_attempts_remaining;
   cdtime_t next_random_ttl;
 };
 
-static cdtime_t resolve_interval = 0;
-static cdtime_t resolve_jitter = 0;
+static cdtime_t resolve_interval;
+static cdtime_t resolve_jitter;
 
 /*
  * Functions
@@ -112,9 +112,8 @@ static int wt_send_buffer(struct wt_callback *cb) {
 
   status = swrite(cb->sock_fd, cb->send_buf, strlen(cb->send_buf));
   if (status != 0) {
-    char errbuf[1024];
     ERROR("write_tsdb plugin: send failed with status %zi (%s)", status,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
 
     close(cb->sock_fd);
     cb->sock_fd = -1;
@@ -130,7 +129,7 @@ static int wt_flush_nolock(cdtime_t timeout, struct wt_callback *cb) {
   int status;
 
   DEBUG("write_tsdb plugin: wt_flush_nolock: timeout = %.3f; "
-        "send_buf_fill = %zu;",
+        "send_buf_fill = %" PRIsz ";",
         (double)timeout, cb->send_buf_fill);
 
   /* timeout == 0  => flush unconditionally */
@@ -153,7 +152,7 @@ static int wt_flush_nolock(cdtime_t timeout, struct wt_callback *cb) {
   return status;
 }
 
-static cdtime_t new_random_ttl() {
+static cdtime_t new_random_ttl(void) {
   if (resolve_jitter == 0)
     return 0;
 
@@ -242,10 +241,9 @@ static int wt_callback_init(struct wt_callback *cb) {
   }
 
   if (cb->sock_fd < 0) {
-    char errbuf[1024];
     ERROR("write_tsdb plugin: Connecting to %s:%s failed. "
           "The last error was: %s",
-          node, service, sstrerror(errno, errbuf, sizeof(errbuf)));
+          node, service, STRERRNO);
     return -1;
   }
 
@@ -315,7 +313,7 @@ static int wt_flush(cdtime_t timeout,
 
 static int wt_format_values(char *ret, size_t ret_len, int ds_num,
                             const data_set_t *ds, const value_list_t *vl,
-                            _Bool store_rates) {
+                            bool store_rates) {
   size_t offset = 0;
   int status;
   gauge_t *rates = NULL;
@@ -349,7 +347,7 @@ static int wt_format_values(char *ret, size_t ret_len, int ds_num,
     }
     BUFFER_ADD(GAUGE_FORMAT, rates[ds_num]);
   } else if (ds->ds[ds_num].type == DS_TYPE_COUNTER)
-    BUFFER_ADD("%llu", vl->values[ds_num].counter);
+    BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_num].counter);
   else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
     BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive);
   else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
@@ -466,7 +464,7 @@ static int wt_send_message(const char *key, const char *value, cdtime_t time,
 
   if (message_len >= sizeof(message)) {
     ERROR("write_tsdb plugin: message buffer too small: "
-          "Need %zu bytes.",
+          "Need %" PRIsz " bytes.",
           message_len + 1);
     return -1;
   }
@@ -499,8 +497,8 @@ static int wt_send_message(const char *key, const char *value, cdtime_t time,
   cb->send_buf_fill += message_len;
   cb->send_buf_free -= message_len;
 
-  DEBUG("write_tsdb plugin: [%s]:%s buf %zu/%zu (%.1f %%) \"%s\"", cb->node,
-        cb->service, cb->send_buf_fill, sizeof(cb->send_buf),
+  DEBUG("write_tsdb plugin: [%s]:%s buf %" PRIsz "/%" PRIsz " (%.1f %%) \"%s\"",
+        cb->node, cb->service, cb->send_buf_fill, sizeof(cb->send_buf),
         100.0 * ((double)cb->send_buf_fill) / ((double)sizeof(cb->send_buf)),
         message);
 
index 8cba476..e63a766 100644 (file)
@@ -21,8 +21,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <xenctrl.h>
 
@@ -56,7 +56,7 @@ static int xencpu_init(void) {
 
   xc_physinfo_t *physinfo;
 
-  physinfo = calloc(1, sizeof(xc_physinfo_t));
+  physinfo = calloc(1, sizeof(*physinfo));
   if (physinfo == NULL) {
     ERROR("xencpu plugin: calloc() for physinfo failed.");
     xc_interface_close(xc_handle);
@@ -75,14 +75,14 @@ static int xencpu_init(void) {
 
   INFO("xencpu plugin: Found %" PRIu32 " processors.", num_cpus);
 
-  cpu_info = calloc(num_cpus, sizeof(xc_cpuinfo_t));
+  cpu_info = calloc(num_cpus, sizeof(*cpu_info));
   if (cpu_info == NULL) {
     ERROR("xencpu plugin: calloc() for num_cpus failed.");
     xc_interface_close(xc_handle);
     return ENOMEM;
   }
 
-  cpu_states = calloc(num_cpus, sizeof(value_to_rate_state_t));
+  cpu_states = calloc(num_cpus, sizeof(*cpu_states));
   if (cpu_states == NULL) {
     ERROR("xencpu plugin: calloc() for cpu_states failed.");
     xc_interface_close(xc_handle);
index 3e3a3c3..2d550b4 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <xmms/xmmsctrl.h>
 
index 6c66fb1..d18a93f 100644 (file)
@@ -29,8 +29,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 /*
  * Global variables
@@ -207,9 +207,8 @@ static int za_read(void) {
 
   fh = fopen(ZOL_ARCSTATS_FILE, "r");
   if (fh == NULL) {
-    char errbuf[1024];
     ERROR("zfs_arc plugin: Opening \"%s\" failed: %s", ZOL_ARCSTATS_FILE,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+          STRERRNO);
     return -1;
   }
 
index 3cd0534..cd804f7 100644 (file)
 
 #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)
@@ -68,9 +68,7 @@ static int zone_read_procfile(char const *pidstr, char const *name, void *buf,
   }
 
   if (sread(fd, buf, bufsize) != 0) {
-    char errbuf[1024];
-    ERROR("zone plugin: Reading \"%s\" failed: %s", procfile,
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("zone plugin: Reading \"%s\" failed: %s", procfile, STRERRNO);
     close(fd);
     return 1;
   }
index 0b2507d..9c70ea5 100644 (file)
@@ -26,8 +26,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <netdb.h>
 #include <netinet/in.h>
@@ -37,8 +37,8 @@
 #define ZOOKEEPER_DEF_HOST "127.0.0.1"
 #define ZOOKEEPER_DEF_PORT "2181"
 
-static char *zk_host = NULL;
-static char *zk_port = NULL;
+static char *zk_host;
+static char *zk_port;
 
 static const char *config_keys[] = {"Host", "Port"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
@@ -99,28 +99,22 @@ static int zookeeper_connect(void) {
 
   status = getaddrinfo(host, port, &ai_hints, &ai_list);
   if (status != 0) {
-    char errbuf[1024];
     INFO("getaddrinfo failed: %s",
-         (status == EAI_SYSTEM) ? sstrerror(errno, errbuf, sizeof(errbuf))
-                                : gai_strerror(status));
+         (status == EAI_SYSTEM) ? STRERRNO : gai_strerror(status));
     return -1;
   }
 
   for (struct addrinfo *ai = ai_list; ai != NULL; ai = ai->ai_next) {
     sk = socket(ai->ai_family, SOCK_STREAM, 0);
     if (sk < 0) {
-      char errbuf[1024];
-      WARNING("zookeeper: socket(2) failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("zookeeper: socket(2) failed: %s", STRERRNO);
       continue;
     }
     status = (int)connect(sk, ai->ai_addr, ai->ai_addrlen);
     if (status != 0) {
-      char errbuf[1024];
       close(sk);
       sk = -1;
-      WARNING("zookeeper: connect(2) failed: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
+      WARNING("zookeeper: connect(2) failed: %s", STRERRNO);
       continue;
     }
 
@@ -144,9 +138,7 @@ static int zookeeper_query(char *buffer, size_t buffer_size) {
 
   status = (int)swrite(sk, "mntr\r\n", strlen("mntr\r\n"));
   if (status != 0) {
-    char errbuf[1024];
-    ERROR("zookeeper: write(2) failed: %s",
-          sstrerror(errno, errbuf, sizeof(errbuf)));
+    ERROR("zookeeper: write(2) failed: %s", STRERRNO);
     close(sk);
     return -1;
   }
@@ -158,11 +150,9 @@ static int zookeeper_query(char *buffer, size_t buffer_size) {
                              buffer_size - buffer_fill, /* flags = */ 0)) !=
          0) {
     if (status < 0) {
-      char errbuf[1024];
       if ((errno == EAGAIN) || (errno == EINTR))
         continue;
-      ERROR("zookeeper: Error reading from socket: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+      ERROR("zookeeper: Error reading from socket: %s", STRERRNO);
       close(sk);
       return -1;
     }