Merge pull request #2725 from RafaelMarinheiro/fix_grpc_query_values
authorMatthias Runge <mrunge@redhat.com>
Mon, 1 Jul 2019 07:25:33 +0000 (09:25 +0200)
committerGitHub <noreply@github.com>
Mon, 1 Jul 2019 07:25:33 +0000 (09:25 +0200)
utils_cache: fix pointer reference and meta_data ingestion

404 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]
ChangeLog
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/redhat/collectd.spec
contrib/systemd.collectd.service
docs/CONTRIBUTING.md [new file with mode: 0644]
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-lua.pod
src/collectd-nagios.c
src/collectd-python.pod
src/collectd-snmp.pod
src/collectd-tg.c
src/collectd.conf.in
src/collectd.conf.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/intel_rdt_test.c [new file with mode: 0644]
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/network_buffer.h
src/libcollectdclient/collectd/server.h
src/libcollectdclient/collectd/stdendian.h [new file with mode: 0644]
src/libcollectdclient/network.c
src/libcollectdclient/network_buffer.c
src/libcollectdclient/network_parse.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/proc_pids/proc_pids.c [new file with mode: 0644]
src/utils/proc_pids/proc_pids.h [new file with mode: 0644]
src/utils/proc_pids/proc_pids_test.c [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_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_taskstats.c [deleted file]
src/utils_taskstats.h [deleted file]
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_syslog.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
version-gen.sh

diff --git a/.cirrus.yml b/.cirrus.yml
new file mode 100644 (file)
index 0000000..ab88daf
--- /dev/null
@@ -0,0 +1,225 @@
+env:
+  LANG: C
+  CIRRUS_CLONE_DEPTH: 1
+  DEFAULT_CONFIG_OPTS: --enable-debug --without-libstatgrab --disable-dependency-tracking
+
+
+###
+# make distcheck and other sanity checks
+#
+release_ready_task:
+  container:
+    image: collectd/ci:stretch_amd64
+  lint_script:
+    - /checks/check-bashisms.sh
+    - /checks/check-pod.sh
+  configure_script:
+    - ./build.sh
+    - ./configure $DEFAULT_CONFIG_OPTS
+  checks_script:
+    - make -j2 -s distcheck DISTCHECK_CONFIGURE_FLAGS="${DEFAULT_CONFIG_OPTS}"
+
+###
+# Default toolchain and build flags used in deb packages, on a range of Debian
+# and Ubuntu releases (+ Debian/unstable)
+# Most should succeed, and PRs shouldn't break them.
+#
+debian_default_toolchain_task:
+  matrix:
+    - allow_failures: false
+      container:
+        image: collectd/ci:jessie_amd64
+    - allow_failures: false
+      container:
+        image: collectd/ci:stretch_amd64
+    - allow_failures: false
+      container:
+        image: collectd/ci:stretch_i386
+    - allow_failures: false
+      container:
+        image: collectd/ci:trusty_amd64
+    - allow_failures: false
+      container:
+        image: collectd/ci:xenial_amd64
+    # debian/unstable is expected to fail
+    - allow_failures: true
+      skip_notifications: true
+      only_if: $CIRRUS_BRANCH == 'master'
+      container:
+        image: collectd/ci:sid_amd64
+  configure_script:
+    - ./build.sh
+    - gcc --version
+    - >
+      ./configure CC=gcc $DEFAULT_CONFIG_OPTS
+      CFLAGS="$(dpkg-buildflags --get CFLAGS)"
+      CPPLAGS="$(dpkg-buildflags --get CPPFLAGS)"
+      LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"
+  build_script:
+    - make -j2 -sk
+  tests_script:
+    - make -j2 -sk check
+    - /checks/check-built-plugins.sh
+
+###
+# Default toolchain and build flags used in RPM packages, on a range of RedHat
+# and Fedora releases (+ Fedora/rawhide)
+# Most should succeed, and PRs shouldn't break them.
+#
+redhat_default_toolchain_task:
+  matrix:
+    - allow_failures: false
+      skip_notifications: false
+      container:
+        image: collectd/ci:el6_x86_64
+    - allow_failures: true
+      skip_notifications: true
+      container:
+        image: collectd/ci:el7_x86_64 # TODO: fix this platform
+    - allow_failures: true
+      skip_notifications: true
+      container:
+        image: collectd/ci:fedora28_x86_64
+    # fedora/rawhide is expected to fail
+    - allow_failures: true
+      skip_notifications: true
+      only_if: $CIRRUS_BRANCH == 'master'
+      container:
+        image: collectd/ci:fedora_rawhide_x86_64
+  configure_script:
+    - ./build.sh
+    - gcc --version
+    - ./configure CC=gcc $DEFAULT_CONFIG_OPTS CFLAGS="$(rpm --eval '%optflags')"
+  build_script:
+    - make -j2 -sk
+  tests_script:
+    - make -j2 -sk check
+    - /checks/check-built-plugins.sh
+
+
+###
+# Misc non-standard build environment & options on most recent released debian
+# version.
+# Some are expected to fail, others should always pass
+non_standard_toolchains_task:
+  container:
+    image: collectd/ci:stretch_amd64
+  only_if: $CIRRUS_PR == ''
+
+  matrix:
+
+    # build using clang with default build flags, should always pass
+    - env:
+        LABEL: clang
+      allow_failures: true # TODO: fix this platform
+      skip_notifications: true
+      configure_script:
+        - ./build.sh
+        - clang --version
+        - >
+          ./configure CC=clang CXX=clang++
+          $DEFAULT_CONFIG_OPTS
+          CFLAGS="$(dpkg-buildflags --get CFLAGS)"
+          CPPLAGS="$(dpkg-buildflags --get CPPFLAGS)"
+          LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"
+      build_script:
+        - make -j2 -sk
+      tests_script:
+        - make -j2 -sk check
+
+    # build against libstatgrab, should always pass
+    - env:
+        LABEL: statgrab
+      allow_failures: false
+      skip_notifications: false
+      configure_script:
+        - ./build.sh
+        - gcc --version
+        - >
+          ./configure --with-libstatgrab --enable-debug
+          CFLAGS="$(dpkg-buildflags --get CFLAGS)"
+          CPPLAGS="$(dpkg-buildflags --get CPPFLAGS)"
+          LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"
+      build_script:
+        - make -j2 -sk
+      tests_script:
+        - >
+          for i in cpu disk interface load memory swap users; do
+            if ! $(ldd ".libs/${i}.so" 2>/dev/null | grep -q 'libstatgrab.so'); then
+              echo "plugin $i NOT linked against libstatgrab"
+              exit 1
+            fi
+          done
+
+    # build using clang with a collection of strict build flags, will most
+    # probably always fail
+    - env:
+        LABEL: clang strict
+      allow_failures: true
+      skip_notifications: true
+      configure_script:
+        - ./build.sh
+        - clang --version
+        - >
+          ./configure CC=clang CXX=clang++
+          $DEFAULT_CONFIG_OPTS
+          CFLAGS='-Wall
+          -Wno-error
+          -Wextra
+          -Wformat=2
+          -Wformat-security
+          -Wformat-nonliteral
+          -Wmissing-include-dirs
+          -Wold-style-definition
+          -Wpointer-arith
+          -Winit-self
+          -Wmissing-prototypes
+          -Wimplicit-function-declaration
+          -Wmissing-declarations
+          -Wstrict-prototypes
+          -Wmissing-noreturn
+          -Wshadow
+          -Wendif-labels
+          -Wwrite-strings
+          -Wno-unused-parameter
+          -Wno-missing-field-initializers
+          -Wdate-time
+          -Wnested-externs
+          -Wno-typedef-redefinition
+          -Wno-gnu-variable-sized-type-not-at-end'
+      build_script:
+        - make -j2 -sk
+      tests_script:
+        - make -j2 -sk check
+
+###
+# Build using a range of compilers, available in debian/unstable. NB: might
+# fail because of changes to the distro, not the compiler used.
+#
+bleeding_edge_compilers_task:
+  container:
+    image: collectd/ci:sid_amd64
+  only_if: $CIRRUS_BRANCH == 'master'
+  allow_failures: true
+  skip_notifications: true
+  env:
+    matrix:
+      CC: gcc-7
+      CC: gcc-8
+      CC: clang-6.0
+      CC: clang-7
+      CC: clang-8
+      CC: clang-9
+  configure_script:
+    - ./build.sh
+    - $CC --version
+    - >
+      ./configure CC=$CC
+      $DEFAULT_CONFIG_OPTS
+      CFLAGS="$(dpkg-buildflags --get CFLAGS)"
+      CPPLAGS="$(dpkg-buildflags --get CPPFLAGS)"
+      LDFLAGS="$(dpkg-buildflags --get LDFLAGS)"
+  build_script:
+    - make -j2 -sk
+  tests_script:
+    - make -j2 -sk check
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 1bd6142..4ba4e2a 100644 (file)
+# Travis CI configuration file
+# https://travis-ci.org/collectd/collectd
+language: c
+
 env:
   global:
-   # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
-   # via the "travis encrypt" command using the project repo's public key
-   - secure: "ZdWWp0XX3C4sLIp4lqeQTWC7vt+GsWjmyRiD17T9833NBAW4dddz310I6iyeXe6oX09ZFFiVIN4ogx9ANcNBx9jriGXI2/82nBhpxOJBebet8JCNS5VeTr4rDSfQOKP+Oc+ko5KbbghTuAtO2CFYiH3jZUcn4TdsYbVanf+TwUs="
+    - MAKEFLAGS="-j 2"
+    # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
+    # via the "travis encrypt" command using the project repo's public key
+    - secure: "ZdWWp0XX3C4sLIp4lqeQTWC7vt+GsWjmyRiD17T9833NBAW4dddz310I6iyeXe6oX09ZFFiVIN4ogx9ANcNBx9jriGXI2/82nBhpxOJBebet8JCNS5VeTr4rDSfQOKP+Oc+ko5KbbghTuAtO2CFYiH3jZUcn4TdsYbVanf+TwUs="
+
+matrix:
+  include:
+    - os: osx
+      osx_image: xcode10.1
+      compiler: clang
+      env:
+        - CXX=clang++
+        - PATH="/usr/local/opt/mysql-client/bin:$PATH"
+    - os: linux
+      dist: xenial
+      compiler: clang
+    - os: linux
+      dist: xenial
+      compiler: gcc
 
-sudo: required
-dist: trusty
-compiler:
-  - gcc
-  - clang
-language: c
 before_install:
   # When building the coverity_scan branch, allow only the first job to continue to avoid travis-ci/travis-ci#1975.
   - if [[ "${TRAVIS_BRANCH}" == "coverity_scan" && ! "${TRAVIS_JOB_NUMBER}" =~ \.1$ ]]; then exit 0; fi
-  - sudo apt-get update -qq
-  - sudo apt-get install -qq --no-install-recommends
-      autotools-dev
-      iptables-dev
-      libatasmart-dev
-      libcap-dev
-      libcurl4-gnutls-dev
-      libdbi0-dev
-      libesmtp-dev
-      libganglia1-dev
-      libgcrypt11-dev
-      libglib2.0-dev
-      libgps-dev
-      libhiredis-dev
-      libi2c-dev
-      libldap2-dev
-      libltdl-dev
-      liblua50-dev
-      liblua5.1-0-dev
-      liblua5.2-dev
-      liblvm2-dev
-      libmemcached-dev
-      libmicrohttpd-dev
-      libmnl-dev
-      libmodbus-dev
-      libmosquitto0-dev
-      libmysqlclient-dev
-      libnotify-dev
-      libopenipmi-dev
-      liboping-dev
-      libow-dev
-      libpcap-dev
-      libperl-dev
-      libpq-dev
-      libprotobuf-c0-dev
-      librabbitmq-dev
-      librdkafka-dev
-      libriemann-client-dev
-      librrd-dev
-      libsensors4-dev
-      libsigrok-dev
-      libsnmp-dev
-      libstatgrab-dev
-      libtokyocabinet-dev
-      libtokyotyrant-dev
-      libudev-dev
-      libupsclient-dev
-      libvarnish-dev
-      libvirt-dev
-      libxen-dev
-      libxml2-dev
-      libyajl-dev
-      linux-libc-dev
-      perl
-      protobuf-c-compiler
-      python3-dev
-      python-dev
-      xfslibs-dev
-before_script: autoreconf -fi
+
+before_script: autoreconf -vif
+
 script:
   - if [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]]; then exit 0; fi
   - ./configure
-  - make -j 4
-  - make check
+  - make distcheck DISTCHECK_CONFIGURE_FLAGS="--disable-dependency-tracking --enable-debug"
 
 addons:
+  apt:
+    packages:
+    - autotools-dev
+    - iptables-dev
+    - libatasmart-dev
+    - libcap-dev
+    - libcurl4-gnutls-dev
+    - libdbi0-dev
+    - libesmtp-dev
+    - libganglia1-dev
+    - libgcrypt11-dev
+    - libglib2.0-dev
+    - libgps-dev
+    - libhiredis-dev
+    - libi2c-dev
+    - libldap2-dev
+    - libltdl-dev
+    - liblua50-dev
+    - liblua5.1-0-dev
+    - liblua5.2-dev
+    - liblvm2-dev
+    - libmemcached-dev
+    - libmicrohttpd-dev
+    - libmnl-dev
+    - libmodbus-dev
+    - libmosquitto-dev
+    - libmysqlclient-dev
+    - libnotify-dev
+    - libopenipmi-dev
+    - liboping-dev
+    - libow-dev
+    - libpcap-dev
+    - libperl-dev
+    - libpq-dev
+    - libprotobuf-c0-dev
+    - librabbitmq-dev
+    - librdkafka-dev
+    - libriemann-client-dev
+    - librrd-dev
+    - libsensors4-dev
+    - libsigrok-dev
+    - libsnmp-dev
+    - libstatgrab-dev
+    - libtokyocabinet-dev
+    - libtokyotyrant-dev
+    - libudev-dev
+    - libupsclient-dev
+    - libvarnish-dev
+    - libvirt-dev
+    - libxen-dev
+    - libxml2-dev
+    - libyajl-dev
+    - linux-libc-dev
+    - perl
+    - protobuf-c-compiler
+    - python3-dev
+    - python-dev
+    - xfslibs-dev
   coverity_scan:
     project:
       name: "collectd/collectd"
       description: "Build submitted via Travis CI"
     notification_email: collectd-changes@verplant.org
     build_command_prepend: "./configure; make clean"
-    build_command: "make -j 4"
+    build_command: "make -j $(nproc)"
     branch_pattern: coverity_scan
+  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..d05b86f 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.
 
@@ -169,6 +172,9 @@ Jiri Tyr <jiri.tyr at gmail.com>
 Julien Ammous <j.ammous at gmail.com>
  - Lua plugin.
 
+Shirly Radco <sradco at redhat.com>
+ - write_syslog plugin.
+
 Kevin Bowling <kbowling at llnw.com>
  - write_tsdb plugin for http://opentsdb.net/
 
@@ -286,7 +292,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 5c7c42f..7ea5a07 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,80 @@
+2018-10-23, Version 5.8.1
+       * collectd: Fix "BaseDir" option. Thanks to Mariusz Białończyk and
+         Pavel Rochnyak. #2857
+       * collectd: improve error handling, check return values. Thanks to
+         Florian Forster.
+       * Build System: use "kstat.h", when available. Thanks to Dagobert
+         Michelsen and Pavel Rochnyak. #2784
+       * Build System: Fix distcheck on MacOS. Thanks to Ruben Kerkhof.
+       * Build System: add missing include of ""collectd.h"" to fix builds on
+         Solaris. Thanks to Pavel Rochnyak.
+       * Build System: add endianess checks for AIX, fix GCC issue on Mac
+         byteorder, fix byteorder on Solaris, add fallback for endianess
+         conversion. Thanks to Dagobert Michelsen (multiple cherry picks from
+         master).
+       * Build System: Out-of-tree builds have been fixed. Thanks to Florian
+         Forster. #2602
+       * Configuration: Error handling in the config parsing code has been
+         improved. Thanks to Florian Forster.
+       * Documentation: Fix typo in collectd.conf(5). Thanks to Pavel Rochnyak.
+         #2760
+       * Documentation: update note on dpdkstat. Thanks to Maryam Tahhan. #2613
+       * Various plugins: Errors found by the static code analysis tool
+         Coverity were fixed. Thanks to Florian Forster. #2559, #2560, #2561,
+         #2562, #2563, #2565, #2568, #2575, #2579, #2580, #2588, #2589
+       * Ceph plugin: A segfault has been fixed. Thanks to Aleksei Zakharov and
+         Matthias Runge. #2572
+       * DF plugin: fix memory leak in error case. Thanks to Takahashi tsc.
+       * Exec plugin: check return value of "plugin_thread_create()". Thanks to
+         Florian Forster.
+       * Exec plugin: Handling of large groups has been fixed. Thanks to
+         Sridhar Mallem. #2696
+       * Exec plugin: Incorrect use of *putenv(3)* has been fixed. Thanks to
+         Daniel Vrátil.
+       * Exec plugin: A deadlock related to setting environment variables after
+         *fork()* has been fixed. Thanks to Daniel Vrátil.
+       * Intel PMU plugin: add core groups feature. Thanks to Kamil Wiatrowski.
+         #2681
+       * Intel PMU plugin: fix compatibility issue with collectd 5.8. Thanks to
+         Kamil Wiatrowski.
+       * Intel PMU plugin: fix possible "NULL" pointer dereference. Thanks to
+         Kamil Wiatrowski. #2676
+       * IPMI plugin: A segfault caused by a wrong data type has been fixed.
+         Thanks to Mariusz Szafrański. #2742
+       * IPMI plugin: The sensor configuration option has been fixed. Thanks to
+         Pavel Rochnyak. #2629
+       * memcached plugin: A deadlock situation has been fixed. Thanks to Pavel
+         Rochnyak. #2612
+       * NFS plugin: Support for NFSv4 has been fixed. Thanks to Jan-Philipp
+         Litza. #2076
+       * NTPd plugin: A memory leak in the error handling path has been fixed.
+         Thanks to Ruben Kerkhof. #2942
+       * OVS Stats plugin: A deadlock situation has been fixed. Thanks to
+         Volodymyr Mytnyk. #2590
+       * OVS Stats plugin: Fix reconnect after thread terminated. Thanks to
+         Volodymyr Mytnyk and Maram Tahhan. #2574
+       * Perl plugin: A compilation failure has been fixed. Thanks to Pavel
+         Rochnyak. #2732
+       * Perl plugin: Fix exporting notification meta data. Thanks to Florian
+         Forster.
+       * RRDtool plugin: Handling of very large "GAUGE" metrics has been fixed.
+         Thanks to Miroslav Lichvar. #2566
+       * Tail plugin: Several regressions have been fixed. Thanks to Pavel
+         Rochnyak. #2535, #2587, #2611
+       * turbostat plugin: A potential segfault due to an incorrect *free()*
+         has been fixed. Thanks to Ruben Kerkhof. #2948
+       * UUID plugin: Fix hostname setting. Thanks to Pavel Rochnyak. #2723
+       * virt plugin: A segfault during error handling has been fixed. Thanks
+         to Ruben Kerkhof. {{Issue|2919]}
+       * Write Kafka plugin: A build failure due to a deprecated API call has
+         been fixed. Thanks to Pavel Rochnyak. #2607, #2628, #2640
+       * Write Prometheus plugin: Fix "MHD_USE_INTERNAL_POLLING_THREAD" flag in
+         newer libmicrohttpd. Thanks to Pavel Rochnyak. #2849
+       * Write Prometheus plugin: set "SO_REUSEADDRESS" on listening socket.
+         Thanks to Pavel Rochnyak. #2570, #2673
+       * Write Syslog plugin: The new "write_syslog" plugin writes value
+         lists as syslog messages. Thanks to Shirly Radco. #3019
+
 2017-11-17, Version 5.8.0
        * collectd: The core daemon is now completely licensed under the MIT
          license.
index e37716f..0821283 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 = \
@@ -141,7 +158,9 @@ check_PROGRAMS = \
        test_utils_subst \
        test_utils_time \
        test_utils_vl_lookup \
-       test_libcollectd_network_parse
+       test_libcollectd_network_parse \
+       test_utils_config_cores \
+       test_utils_proc_pids
 
 
 TESTS = $(check_PROGRAMS)
@@ -149,7 +168,7 @@ TESTS = $(check_PROGRAMS)
 LOG_COMPILER = env VALGRIND="@VALGRIND@" $(abs_srcdir)/testwrapper.sh
 
 
-jardir = $(pkgdatadir)/java
+jardir = $(cpkgdatadir)/java
 
 pkglib_LTLIBRARIES =
 
@@ -158,6 +177,9 @@ PLUGIN_LDFLAGS = \
        -module \
        -avoid-version \
        -export-symbols-regex '\<module_register\>'
+if BUILD_WIN32
+PLUGIN_LDFLAGS += -shared -no-undefined -lcollectd -L.
+endif
 
 
 AM_CPPFLAGS = \
@@ -165,13 +187,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
@@ -193,6 +228,7 @@ endif
 
 
 collectd_SOURCES = \
+       src/daemon/cmd.h \
        src/daemon/collectd.c \
        src/daemon/collectd.h \
        src/daemon/configfile.c \
@@ -201,8 +237,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 \
@@ -235,6 +271,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
@@ -246,6 +289,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
 
@@ -296,22 +342,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)
 
@@ -326,26 +372,36 @@ test_utils_subst_SOURCES = \
        src/daemon/utils_subst.h
 test_utils_subst_LDADD = libplugin_mock.la
 
+test_utils_config_cores_SOURCES = \
+       src/utils/config_cores/config_cores_test.c \
+       src/testing.h
+test_utils_config_cores_LDADD = libplugin_mock.la
+
+test_utils_proc_pids_SOURCES = \
+       src/utils/proc_pids/proc_pids_test.c \
+       src/testing.h
+test_utils_proc_pids_LDADD = libplugin_mock.la
+
 libavltree_la_SOURCES = \
-       src/daemon/utils_avltree.c \
-       src/daemon/utils_avltree.h
+       src/utils/avltree/avltree.c \
+       src/utils/avltree/avltree.h
 
 libcommon_la_SOURCES = \
-       src/daemon/common.c \
-       src/daemon/common.h
+       src/utils/common/common.c \
+       src/utils/common/common.h
 libcommon_la_LIBADD = $(COMMON_LIBS)
 
 libheap_la_SOURCES = \
-       src/daemon/utils_heap.c \
-       src/daemon/utils_heap.h
+       src/utils/heap/heap.c \
+       src/utils/heap/heap.h
 
 libignorelist_la_SOURCES = \
-       src/utils_ignorelist.c \
-       src/utils_ignorelist.h
+       src/utils/ignorelist/ignorelist.c \
+       src/utils/ignorelist/ignorelist.h
 
 libmetadata_la_SOURCES = \
-       src/daemon/meta_data.c \
-       src/daemon/meta_data.h
+       src/utils/metadata/meta_data.c \
+       src/utils/metadata/meta_data.h
 
 libplugin_mock_la_SOURCES = \
        src/daemon/plugin_mock.c \
@@ -359,11 +415,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 \
@@ -372,8 +428,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    =
@@ -385,7 +441,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 \
@@ -403,16 +459,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 \
@@ -420,41 +476,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 \
@@ -464,11 +520,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 \
@@ -483,7 +539,8 @@ libcollectdclient_la_SOURCES = \
        src/libcollectdclient/network.c \
        src/libcollectdclient/network_buffer.c \
        src/libcollectdclient/network_parse.c \
-       src/libcollectdclient/server.c
+       src/libcollectdclient/server.c \
+       src/libcollectdclient/collectd/stdendian.h
 libcollectdclient_la_CPPFLAGS = \
        $(AM_CPPFLAGS) \
        -I$(srcdir)/src/libcollectdclient \
@@ -491,6 +548,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)
@@ -519,13 +580,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
@@ -542,6 +665,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
@@ -592,7 +729,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
@@ -696,10 +833,10 @@ if BUILD_PLUGIN_CURL
 pkglib_LTLIBRARIES += curl.la
 curl_la_SOURCES = \
        src/curl.c \
-       src/utils_curl_stats.c \
-       src/utils_curl_stats.h \
-       src/utils_match.c \
-       src/utils_match.h
+       src/utils/curl_stats/curl_stats.c \
+       src/utils/curl_stats/curl_stats.h \
+       src/utils/match/match.c \
+       src/utils/match/match.h
 curl_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
 curl_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 curl_la_LIBADD = liblatency.la $(BUILD_WITH_LIBCURL_LIBS)
@@ -709,15 +846,15 @@ if BUILD_PLUGIN_CURL_JSON
 pkglib_LTLIBRARIES += curl_json.la
 curl_json_la_SOURCES = \
        src/curl_json.c \
-       src/utils_curl_stats.c \
-       src/utils_curl_stats.h
+       src/utils/curl_stats/curl_stats.c \
+       src/utils/curl_stats/curl_stats.h
 curl_json_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCURL_CFLAGS)
 curl_json_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
 curl_json_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
 curl_json_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBYAJL_LIBS)
 
 test_plugin_curl_json_SOURCES = src/curl_json_test.c \
-                               src/utils_curl_stats.c \
+                               src/utils/curl_stats/curl_stats.c \
                                src/daemon/configfile.c \
                                src/daemon/types_list.c
 test_plugin_curl_json_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
@@ -730,8 +867,8 @@ if BUILD_PLUGIN_CURL_XML
 pkglib_LTLIBRARIES += curl_xml.la
 curl_xml_la_SOURCES = \
        src/curl_xml.c \
-       src/utils_curl_stats.c \
-       src/utils_curl_stats.h
+       src/utils/curl_stats/curl_stats.c \
+       src/utils/curl_stats/curl_stats.h
 curl_xml_la_CFLAGS = $(AM_CFLAGS) \
                $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
 curl_xml_la_LDFLAGS = $(PLUGIN_LDFLAGS)
@@ -742,8 +879,8 @@ if BUILD_PLUGIN_DBI
 pkglib_LTLIBRARIES += dbi.la
 dbi_la_SOURCES = \
        src/dbi.c \
-       src/utils_db_query.c \
-       src/utils_db_query.h
+       src/utils/db_query/db_query.c \
+       src/utils/db_query/db_query.h
 dbi_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBDBI_CPPFLAGS)
 dbi_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBDBI_LDFLAGS)
 dbi_la_LIBADD = $(BUILD_WITH_LIBDBI_LIBS)
@@ -793,8 +930,8 @@ if BUILD_PLUGIN_DNS
 pkglib_LTLIBRARIES += dns.la
 dns_la_SOURCES = \
        src/dns.c \
-       src/utils_dns.c \
-       src/utils_dns.h
+       src/utils/dns/dns.c \
+       src/utils/dns/dns.h
 dns_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBPCAP_CPPFLAGS)
 dns_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPCAP_LDFLAGS)
 dns_la_LIBADD = $(BUILD_WITH_LIBPCAP_LIBS)
@@ -802,7 +939,7 @@ endif
 
 if BUILD_PLUGIN_DPDKEVENTS
 pkglib_LTLIBRARIES += dpdkevents.la
-dpdkevents_la_SOURCES = src/dpdkevents.c src/utils_dpdk.c src/utils_dpdk.h
+dpdkevents_la_SOURCES = src/dpdkevents.c src/utils/dpdk/dpdk.c src/utils/dpdk/dpdk.h
 dpdkevents_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS)
 dpdkevents_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS)
 dpdkevents_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS)
@@ -811,7 +948,7 @@ endif
 
 if BUILD_PLUGIN_DPDKSTAT
 pkglib_LTLIBRARIES += dpdkstat.la
-dpdkstat_la_SOURCES = src/dpdkstat.c src/utils_dpdk.c src/utils_dpdk.h
+dpdkstat_la_SOURCES = src/dpdkstat.c src/utils/dpdk/dpdk.c src/utils/dpdk/dpdk.h
 dpdkstat_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS)
 dpdkstat_la_CFLAGS = $(AM_CFLAGS) $(LIBDPDK_CFLAGS)
 dpdkstat_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS)
@@ -883,6 +1020,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
@@ -913,7 +1058,10 @@ endif
 
 if BUILD_PLUGIN_INTEL_PMU
 pkglib_LTLIBRARIES += intel_pmu.la
-intel_pmu_la_SOURCES = src/intel_pmu.c
+intel_pmu_la_SOURCES = \
+       src/intel_pmu.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)
@@ -921,10 +1069,27 @@ endif
 
 if BUILD_PLUGIN_INTEL_RDT
 pkglib_LTLIBRARIES += intel_rdt.la
-intel_rdt_la_SOURCES = src/intel_rdt.c
+intel_rdt_la_SOURCES = \
+       src/intel_rdt.c \
+       src/utils/proc_pids/proc_pids.c \
+       src/utils/proc_pids/proc_pids.h \
+       src/utils/config_cores/config_cores.h \
+       src/utils/config_cores/config_cores.c
 intel_rdt_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBPQOS_CPPFLAGS)
 intel_rdt_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPQOS_LDFLAGS)
 intel_rdt_la_LIBADD = $(BUILD_WITH_LIBPQOS_LIBS)
+
+test_plugin_intel_rdt_SOURCES = \
+       src/intel_rdt_test.c \
+       src/utils/config_cores/config_cores.c \
+       src/utils/proc_pids/proc_pids.c \
+       src/daemon/configfile.c \
+       src/daemon/types_list.c
+test_plugin_intel_rdt_CPPFLAGS = $(AM_CPPFLAGS)
+test_plugin_intel_rdt_LDFLAGS = $(PLUGIN_LDFLAGS)
+test_plugin_intel_rdt_LDADD = liboconfig.la libplugin_mock.la
+check_PROGRAMS += test_plugin_intel_rdt
+TESTS += test_plugin_intel_rdt
 endif
 
 if BUILD_PLUGIN_INTERFACE
@@ -1012,6 +1177,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
@@ -1114,8 +1280,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)
@@ -1322,8 +1488,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)
@@ -1333,8 +1499,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)
@@ -1344,13 +1510,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
@@ -1397,8 +1581,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)
@@ -1425,8 +1609,8 @@ endif
 if HAVE_LIBMNL
 noinst_LTLIBRARIES += libtaskstats.la
 libtaskstats_la_SOURCES = \
-       src/utils_taskstats.c \
-       src/utils_taskstats.h
+       src/utils/taskstats/taskstats.c \
+       src/utils/taskstats/taskstats.h
 libtaskstats_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
 libtaskstats_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS)
 endif
@@ -1473,8 +1657,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)
@@ -1484,8 +1668,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)
@@ -1528,7 +1712,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
@@ -1537,6 +1721,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
@@ -1593,10 +1794,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)
@@ -1607,8 +1808,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
 
@@ -1758,18 +1959,16 @@ 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 src/daemon/configfile.c \
+       src/daemon/types_list.c
+test_plugin_virt_CPPFLAGS = $(AM_CPPFLAGS) \
+       $(BUILD_WITH_LIBVIRT_CPPFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
+test_plugin_virt_LDFLAGS = $(PLUGIN_LDFLAGS) \
+       $(BUILD_WITH_LIBVIRT_LDFLAGS) $(BUILD_WITH_LIBXML2_LDFLAGS)
+test_plugin_virt_LDADD = liboconfig.la 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
@@ -1801,8 +2000,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)
@@ -1832,6 +2031,7 @@ pkglib_LTLIBRARIES += write_mongodb.la
 write_mongodb_la_SOURCES = src/write_mongodb.c
 write_mongodb_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMONGOC_CFLAGS)
 write_mongodb_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMONGOC_LDFLAGS)
+write_mongodb_la_LIBADD = $(BUILD_WITH_LIBMONGOC_LIBS)
 endif
 
 if BUILD_PLUGIN_WRITE_PROMETHEUS
@@ -1869,6 +2069,21 @@ 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_SYSLOG
+pkglib_LTLIBRARIES += write_syslog.la
+write_syslog_la_SOURCES = src/write_syslog.c
+write_syslog_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+endif
+
 if BUILD_PLUGIN_WRITE_TSDB
 pkglib_LTLIBRARIES += write_tsdb.la
 write_tsdb_la_SOURCES = src/write_tsdb.c
@@ -1921,20 +2136,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 --release=$(VERSION) --center=$(PACKAGE) $< $@
 
 .pod.5:
-       $(AM_V_POD2MAN_C)pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< \
-               >.pod2man.tmp.$$$$ 2>/dev/null && mv -f .pod2man.tmp.$$$$ $@ || true
-       @if grep '\<POD ERRORS\>' $@ >/dev/null 2>&1; \
-       then \
-               echo "$@ has some POD errors!"; false; \
-       fi
+       $(AM_V_POD2MAN_C)pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< $@
 
 V_PROTOC = $(v_protoc_@AM_V@)
 v_protoc_ = $(v_protoc_@AM_DEFAULT_V@)
@@ -1984,19 +2189,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 +2281,4 @@ generic-jmx.jar: $(JAVA_TIMESTAMP_FILE)
 
 jar_DATA = collectd-api.jar generic-jmx.jar
 endif
+
diff --git a/README b/README
index ca86c84..f28d499 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
@@ -546,6 +563,10 @@ Features
       Sends data to Sensu, a stream processing and monitoring system, via the
       Sensu client local TCP socket.
 
+    - write_syslog
+      Sends data in syslog format, using TCP, where the message
+      contains the metric in human or JSON format.
+
     - write_tsdb
       Sends data OpenTSDB, a scalable no master, no shared state time series
       database.
@@ -735,6 +756,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 +927,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 +1012,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 +1055,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 bd4c1a3..c0ccce3 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -18,34 +18,156 @@ EOF
     done
 }
 
-check_for_application lex bison autoheader aclocal automake autoconf pkg-config
-
-libtoolize=""
-libtoolize --version >/dev/null 2>/dev/null
-if test $? -eq 0; then
-    libtoolize=libtoolize
-else
-    glibtoolize --version >/dev/null 2>/dev/null
+setup_libtool()
+{
+    libtoolize=""
+    libtoolize --version >/dev/null 2>/dev/null
     if test $? -eq 0; then
-        libtoolize=glibtoolize
+        libtoolize=libtoolize
     else
-        cat >&2 <<EOF
+        glibtoolize --version >/dev/null 2>/dev/null
+        if test $? -eq 0; then
+            libtoolize=glibtoolize
+        else
+            cat >&2 <<EOF
 WARNING: Neither \`libtoolize' nor \`glibtoolize' have been found!
     Please make sure that one of them is installed and is in one of the
     directories listed in the PATH environment variable.
 EOF
-        GLOBAL_ERROR_INDICATOR=1
+            GLOBAL_ERROR_INDICATOR=1
+        fi
     fi
- fi
 
-if test "$GLOBAL_ERROR_INDICATOR" != "0"; then
-    exit 1
-fi
+    if test "$GLOBAL_ERROR_INDICATOR" != "0"; then
+        exit 1
+    fi
+}
+
+build()
+{
+    echo "Building..."
+    check_for_application lex bison autoheader aclocal automake autoconf pkg-config
+    setup_libtool
+
+    set -x
+    autoheader \
+    && aclocal -I m4 \
+    && $libtoolize --copy --force \
+    && automake --add-missing --copy \
+    && autoconf
+}
+
+build_cygwin()
+{
+    echo "Building for Cygwin..."
+    check_for_application aclocal autoconf autoheader automake bison flex git make pkg-config x86_64-w64-mingw32-gcc
+    setup_libtool
+
+    set -e
+
+    : ${INSTALL_DIR:="C:/PROGRA~1/collectd"}
+    : ${LIBDIR:="${INSTALL_DIR}"}
+    : ${BINDIR:="${INSTALL_DIR}"}
+    : ${SBINDIR:="${INSTALL_DIR}"}
+    : ${SYSCONFDIR:="${INSTALL_DIR}"}
+    : ${LOCALSTATEDIR:="${INSTALL_DIR}"}
+    : ${DATAROOTDIR:="${INSTALL_DIR}"}
+    : ${DATADIR:="${INSTALL_DIR}"}
 
-set -x
+    echo "Installing collectd to ${INSTALL_DIR}."
+    TOP_SRCDIR="$(pwd)"
+    MINGW_ROOT="$(x86_64-w64-mingw32-gcc -print-sysroot)/mingw"
+    export GNULIB_DIR="${TOP_SRCDIR}/gnulib/build/gllib"
+
+    export CC="x86_64-w64-mingw32-gcc"
+
+    if [ -d "${TOP_SRCDIR}/gnulib/build" ]; then
+        echo "Assuming that gnulib is already built, because gnulib/build exists."
+    else
+        git submodule init
+        git submodule update
+        cd gnulib
+        ./gnulib-tool \
+          --create-testdir \
+          --source-base=lib \
+          --dir=${TOP_SRCDIR}/gnulib/build \
+          canonicalize-lgpl \
+          fcntl-h \
+          fnmatch \
+          getsockopt \
+          gettimeofday \
+          nanosleep \
+          netdb \
+          net_if \
+          poll \
+          recv \
+          regex \
+          sendto \
+          setlocale \
+          strtok_r \
+          sys_resource \
+          sys_socket \
+          sys_stat \
+          sys_wait \
+          time_r
+
+        cd ${TOP_SRCDIR}/gnulib/build
+        ./configure --host="mingw32" LIBS="-lws2_32 -lpthread"
+        make 
+        cd gllib
+
+        # We have to rebuild libgnu.a to get the list of *.o files to build a dll later
+        rm libgnu.a
+        OBJECT_LIST=`make V=1 | grep "ar" | cut -d' ' -f4-`
+        $CC -shared -o libgnu.dll $OBJECT_LIST -lws2_32 -lpthread
+        rm libgnu.a # get rid of it, to use libgnu.dll
+       fi
+    cd "${TOP_SRCDIR}"
+
+    set -x
+    autoreconf --install
+
+    export LDFLAGS="-L${GNULIB_DIR}"
+    export LIBS="-lgnu"
+    export CFLAGS="-Drestrict=__restrict -I${GNULIB_DIR}"
+
+    ./configure \
+      --prefix="${INSTALL_DIR}" \
+      --libdir="${LIBDIR}" \
+      --bindir="${BINDIR}" \
+      --sbindir="${SBINDIR}" \
+      --sysconfdir="${SYSCONFDIR}" \
+      --localstatedir="${LOCALSTATEDIR}" \
+      --datarootdir="${DATAROOTDIR}" \
+      --datarootdir="${DATADIR}" \
+      --disable-all-plugins \
+      --host="mingw32" \
+      --enable-logfile \
+      --enable-match_regex \
+      --enable-target_replace \
+      --enable-target_set
+
+    cp ${GNULIB_DIR}/../config.h src/gnulib_config.h
+    echo "#include <config.h.in>" >> src/gnulib_config.h
+
+    cp libtool libtool_bak
+    sed -i "s%\$LTCC \$LTCFLAGS\(.*cwrapper.*\)%\$LTCC \1%" libtool
+
+    make
+    make install
+
+    cp "${GNULIB_DIR}/libgnu.dll" "${INSTALL_DIR}"
+    cp "${MINGW_ROOT}/bin/zlib1.dll" "${INSTALL_DIR}"
+    cp "${MINGW_ROOT}/bin/libwinpthread-1.dll" "${INSTALL_DIR}"
+    cp "${MINGW_ROOT}/bin/libdl.dll" "${INSTALL_DIR}"
+
+    echo "Done."
+}
+
+os_name="$(uname)"
+if test "${os_name#CYGWIN}" != "$os_name"; then
+    build_cygwin
+else
+    build
+fi
 
-autoheader \
-&& aclocal -I m4 \
-&& $libtoolize --copy --force \
-&& automake --add-missing --copy \
-&& autoconf
index 352a7b6..cf40722 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"
@@ -624,16 +635,6 @@ fi
 
 # }}}
 
-# For the dns plugin
-AC_CHECK_HEADERS([arpa/nameser.h])
-AC_CHECK_HEADERS([arpa/nameser_compat.h], [], [],
-  [[
-    #if HAVE_ARPA_NAMESER_H
-    # include <arpa/nameser.h>
-    #endif
-  ]]
-)
-
 AC_CHECK_HEADERS([net/if_arp.h], [], [],
   [[
     #if HAVE_SYS_SOCKET_H
@@ -745,6 +746,7 @@ AC_CHECK_FUNCS_ONCE([ \
     getaddrinfo \
     getgrnam_r \
     getnameinfo \
+    getpwnam \
     getpwnam_r \
     gettimeofday \
     if_indextoname \
@@ -765,11 +767,16 @@ AC_CHECK_FUNCS_ONCE([ \
 
 AC_FUNC_STRERROR_R
 
-SAVE_CFLAGS="$CFLAGS"
-# Emulate behavior of src/Makefile.am
 if test "x$GCC" = "xyes"; then
+  SAVE_CFLAGS="$CFLAGS"
   CFLAGS="$CFLAGS -Wall -Werror"
 fi
+SAVE_LDFLAGS="$LDFLAGS"
+LDFLAGS=""
+if test "x$ac_system" = "xWindows"; then
+  # This is exported from build.sh
+  LDFLAGS="$LDFLAGS -L${GNULIB_DIR}"
+fi
 
 AC_CACHE_CHECK([for strtok_r],
   [c_cv_have_strtok_r_default],
@@ -842,6 +849,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 +859,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],
@@ -1561,13 +1575,13 @@ if test "x$have_getmntent" = "xlibc"; then
       AC_COMPILE_IFELSE(
         [
           AC_LANG_PROGRAM(
-            [[#include "$srcdir/src/utils_mount.h"]],
+            [[#include "$srcdir/src/utils/mount/mount.h"]],
             [[
               FILE *fh;
               struct mntent *me;
               fh = setmntent ("/etc/mtab", "r");
               me = getmntent (fh);
-              return(me->mnt_passno);
+              return me->mnt_passno;
             ]]
           )
         ],
@@ -1583,14 +1597,14 @@ if test "x$have_getmntent" = "xlibc"; then
       AC_COMPILE_IFELSE(
         [
           AC_LANG_PROGRAM(
-            [[#include "$srcdir/src/utils_mount.h"]],
+            [[#include "$srcdir/src/utils/mount/mount.h"]],
             [[
               FILE *fh;
               struct mnttab mt;
               int status;
               fh = fopen ("/etc/mnttab", "r");
               status = getmntent (fh, &mt);
-              return(status);
+              return status;
             ]]
           )
         ],
@@ -1878,14 +1892,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 +2066,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 +2128,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 +2135,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 +2175,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 +2182,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 +2323,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 {{{
@@ -2380,13 +2432,30 @@ if test "x$with_libdpdk" != "xno"; then
 fi
 
 if test "x$with_libdpdk" = "xyes"; then
+  SAVE_LIBS="$LIBS"
+  LIBS="$LIBDPDK_LIBS $LIBS"
   SAVE_LDFLAGS="$LDFLAGS"
   LDFLAGS="$LIBDPDK_LDFLAGS $LDFLAGS"
-  AC_CHECK_LIB([dpdk], [rte_eal_init],
+  SAVE_CPPFLAGS="$CPPFLAGS"
+  CPPFLAGS="$LIBDPDK_CPPFLAGS $CPPFLAGS"
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$LIBDPDK_CFLAGS $CFLAGS"
+  AC_LINK_IFELSE(
+    [
+      AC_LANG_PROGRAM(
+        [[
+          #include <rte_eal.h>
+        ]],
+        [[return rte_eal_init(0, NULL);]]
+      )
+    ],
     [with_libdpdk="yes"],
     [with_libdpdk="no (symbol 'rte_eal_init' not found)"]
   )
+  LIBS="$SAVE_LIBS"
   LDFLAGS="$SAVE_LDFLAGS"
+  CPPFLAGS="$SAVE_CPPFLAGS"
+  CFLAGS="$SAVE_CFLAGS"
 fi
 
 # }}}
@@ -2550,9 +2619,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)"]
@@ -2889,18 +2955,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
@@ -2958,7 +3012,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"
@@ -2998,10 +3051,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)"]
@@ -3009,10 +3058,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)"]
@@ -3049,18 +3094,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)"]
+                                      )
+                                    ]
+                                  )
+                                ]
+                              )
+                            ]
                           )
                         ]
                       )
@@ -3305,7 +3365,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)"
@@ -3388,10 +3447,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)"]
@@ -3407,10 +3462,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)"]
@@ -3423,10 +3474,12 @@ fi
 if test "x$with_libmongoc" = "xyes"; then
   BUILD_WITH_LIBMONGOC_CFLAGS="$LIBMONGOC_CFLAGS"
   BUILD_WITH_LIBMONGOC_LDFLAGS="$LIBMONGOC_LDFLAGS"
+  BUILD_WITH_LIBMONGOC_LIBS="$LIBMONGOC_LIBS"
 fi
 
 AC_SUBST([BUILD_WITH_LIBMONGOC_CFLAGS])
 AC_SUBST([BUILD_WITH_LIBMONGOC_LDFLAGS])
+AC_SUBST([BUILD_WITH_LIBMONGOC_LIBS])
 # }}}
 
 # --with-libmosquitto {{{
@@ -3660,6 +3713,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)"],
@@ -3700,9 +3765,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)"]
@@ -3710,16 +3772,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)"],
@@ -3781,7 +3837,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)"]
   )
 
@@ -3789,6 +3845,65 @@ 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"
+  if test "x$GCC" = "xyes"; then
+    CPPFLAGS="$CPPFLAGS -Wall -Werror"
+  fi
+  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"
@@ -3799,7 +3914,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.])],
   [
@@ -4081,7 +4196,7 @@ if test "x$with_libpcap" = "xyes"; then
             [[#include <pcap.h>]],
             [[
               int val = PCAP_ERROR_IFACE_NOT_UP;
-              return(val);
+              return val;
             ]]
           )
         ],
@@ -4211,7 +4326,10 @@ if test "x$with_libperl" = "xyes"; then
   # (see issues #41 and #42)
   SAVE_CFLAGS="$CFLAGS"
   SAVE_LIBS="$LIBS"
-  CFLAGS="$CFLAGS $PERL_CFLAGS -Wall -Werror"
+  CFLAGS="$CFLAGS $PERL_CFLAGS"
+  if test "x$GCC" = "xyes"; then
+    CFLAGS="$CFLAGS -Wall -Werror"
+  fi
   LIBS="$LIBS $PERL_LIBS"
 
   AC_CACHE_CHECK([for broken Perl_load_module()],
@@ -4651,7 +4769,7 @@ if test "$PYTHON_CONFIG" != ""; then
   if test $? -ne 0; then
     with_libpython="no"
   fi
-  LIBPYTHON_LIBS="`${PYTHON_CONFIG} --libs`"
+  LIBPYTHON_LIBS="`${PYTHON_CONFIG} --libs --embed`" || LIBPYTHON_LIBS="`${PYTHON_CONFIG} --libs`"
   if test $? -ne 0; then
     with_libpython="no"
   fi
@@ -4686,6 +4804,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.])],
@@ -5050,6 +5218,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"
@@ -5070,6 +5259,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.])],
@@ -5369,7 +5607,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)"
@@ -5530,15 +5767,15 @@ if test "x$with_libxmms" = "xyes"; then
 fi
 
 if test "x$with_libxmms" = "xyes"; then
-  SAVE_CFLAGS="$CFLAGS"
-  CFLAGS="$with_xmms_cflags"
+  SAVE_CPPFLAGS="$CFLAGS"
+  CPPFLAGS="$with_xmms_cflags"
 
   AC_CHECK_HEADER([xmmsctrl.h],
     [with_libxmms="yes"],
     [with_libxmms="no"],
   )
 
-  CFLAGS="$SAVE_CFLAGS"
+  CPPFLAGS="$SAVE_CPPFLAGS"
 fi
 
 if test "x$with_libxmms" = "xyes"; then
@@ -5625,6 +5862,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 {{{
@@ -5756,32 +5994,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"
@@ -5814,6 +6061,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`"
@@ -6128,6 +6384,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"
@@ -6146,6 +6403,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"
@@ -6167,6 +6425,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"
@@ -6224,6 +6483,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
@@ -6253,6 +6516,7 @@ fi
 # FreeBSD
 
 if test "x$ac_system" = "xFreeBSD"; then
+  plugin_cpufreq="yes"
   plugin_disk="yes"
   plugin_zfs_arc="yes"
 fi
@@ -6321,6 +6585,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
@@ -6361,7 +6629,7 @@ if test "x$c_cv_have_one_getmntent" = "xyes"; then
   plugin_df="yes"
 fi
 
-if test "x$c_cv_have_getmntent_r" = "xyes"; then
+if test "x$have_getmntent_r" = "xyes"; then
   plugin_df="yes"
 fi
 
@@ -6509,161 +6777,166 @@ 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_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_stackdriver],   [$plugin_write_stackdriver], [Google Stackdriver Monitoring output plugin])
+AC_PLUGIN([write_syslog],        [yes],                       [Syslog output plugin])
+AC_PLUGIN([write_tsdb],          [yes],                       [TSDB output plugin])
+AC_PLUGIN([xencpu],              [$plugin_xencpu],            [Xen Host CPU usage])
+AC_PLUGIN([xmms],                [$with_libxmms],             [XMMS statistics])
+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
@@ -6801,11 +7074,13 @@ AC_SUBST([LCC_VERSION_STRING])
 
 AC_CONFIG_FILES([src/libcollectdclient/collectd/lcc_features.h])
 
-AM_CFLAGS="-Wall"
-AM_CXXFLAGS="-Wall"
-if test "x$enable_werror" != "xno"; then
-  AM_CFLAGS="$AM_CFLAGS -Werror"
-  AM_CXXFLAGS="$AM_CXXFLAGS -Werror"
+if test "x$GCC" = "xyes"; then
+  AM_CFLAGS="-Wall"
+  AM_CXXFLAGS="-Wall"
+  if test "x$enable_werror" != "xno"; then
+    AM_CFLAGS="$AM_CFLAGS -Werror"
+    AM_CXXFLAGS="$AM_CXXFLAGS -Werror"
+  fi
 fi
 
 AC_SUBST([AM_CFLAGS])
@@ -6888,6 +7163,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])
@@ -6899,6 +7175,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])
@@ -6906,6 +7183,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])
@@ -6930,6 +7208,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])
@@ -6966,6 +7245,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])
@@ -7019,6 +7299,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])
@@ -7076,6 +7357,8 @@ 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_syslog . .  . . $enable_write_syslog])
 AC_MSG_RESULT([    write_tsdb  . . . . . $enable_write_tsdb])
 AC_MSG_RESULT([    xencpu  . . . . . . . $enable_xencpu])
 AC_MSG_RESULT([    xmms  . . . . . . . . $enable_xmms])
@@ -7093,3 +7376,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 d84b457..246fcb5 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}
 %define with_openvpn 0%{!?_without_openvpn:1}
 %define with_ovs_events 0%{!?_without_ovs_events:1}
 %define with_ovs_stats 0%{!?_without_ovs_stats:1}
+%define with_pcie_errors 0%{!?_without_pcie_errors:1}
 %define with_perl 0%{!?_without_perl:1}
 %define with_pinba 0%{!?_without_pinba:1}
 %define with_ping 0%{!?_without_ping:1}
 %define with_write_prometheus 0%{!?_without_write_prometheus:1}
 %define with_write_redis 0%{!?_without_write_redis:1}
 %define with_write_riemann 0%{!?_without_write_riemann:1}
+%define with_write_stackdriver 0%{!?_without_write_stackdriver:1}
 %define with_write_sensu 0%{!?_without_write_sensu:1}
+%define with_write_syslog 0%{!?_without_write_syslog:1}
 %define with_write_tsdb 0%{!?_without_write_tsdb:1}
 %define with_xmms 0%{!?_without_xmms:0%{?_has_xmms}}
 %define with_zfs_arc 0%{!?_without_zfs_arc:1}
 %define with_xencpu 0%{!?_without_xencpu:0}
 # plugin zone disabled, requires Solaris
 %define with_zone 0%{!?_without_zone:0}
+# plugin gpu_nvidia requires cuda-nvml-dev
+# get it from https://developer.nvidia.com/cuda-downloads
+# then install cuda-nvml-dev-10-1 or other version
+%define with_gpu_nvidia 0%{!?_without_gpu_nvidia:0}
+# not sure why this one's failing
+%define with_write_stackdriver 0%{!?_without_write_stackdriver:0}
 
 # Plugins not buildable on RHEL < 6
 %if 0%{?rhel} && 0%{?rhel} < 6
 
 Summary:       Statistics collection and monitoring daemon
 Name:          collectd
-Version:       5.7.1
-Release:       8%{?dist}
+Version:       5.9.0
+Release:       1%{?dist}
 URL:           https://collectd.org
 Source:                https://collectd.org/files/%{name}-%{version}.tar.bz2
 License:       GPLv2
@@ -277,13 +290,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}
@@ -725,6 +749,16 @@ The Perl plugin embeds a Perl interpreter into collectd and exposes the
 application programming interface (API) to Perl-scripts.
 %endif
 
+%if %{with_pcie_errors}
+%package pcie_errors
+Summary:       PCI Express errors plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+%description pcie_errors
+The pcie_errors plugin collects PCI Express errors from Device Status in Capability
+structure and from Advanced Error Reporting Extended Capability.
+%endif
+
 %if %{with_pinba}
 %package pinba
 Summary:       Pinba plugin for collectd
@@ -929,6 +963,27 @@ BuildRequires:     riemann-c-client-devel >= 1.6
 The riemann plugin submits values to Riemann, an event stream processor.
 %endif
 
+%if %{with_write_stackdriver}
+%package write_stackdriver
+Summary:       stackdriver plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: curl-devel, yajl-devel, openssl-devel
+%description write_stackdriver
+The write_stackdriver collectd plugin writes metrics to the
+Google Stackdriver Monitoring service.
+%endif
+
+%if %{with_gpu_nvidia}
+%package gpu_nvidia
+Summary:       stackdriver plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: cuda-nvml-dev-10-1
+%description gpu_nvidia
+The gpu_nvidia collectd plugin collects NVidia GPU metrics.
+%endif
+
 %if %{with_xencpu}
 %package xencpu
 Summary:       xencpu plugin for collectd
@@ -1015,6 +1070,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 +1421,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}
@@ -1531,6 +1592,12 @@ Collectd utilities
 %define _with_perl --disable-perl
 %endif
 
+%if %{with_pcie_errors}
+%define _with_pcie_errors --enable-pcie_errors
+%else
+%define _with_pcie_errors --disable-pcie_errors
+%endif
+
 %if %{with_pf}
 %define _with_pf --enable-pf
 %else
@@ -1830,12 +1897,30 @@ Collectd utilities
 %define _with_write_riemann --disable-write_riemann
 %endif
 
+%if %{with_write_stackdriver}
+%define _with_write_stackdriver --enable-write_stackdriver
+%else
+%define _with_write_stackdriver --disable-write_stackdriver
+%endif
+
+%if %{with_gpu_nvidia}
+%define _with_gpu_nvidia --enable-gpu_nvidia
+%else
+%define _with_gpu_nvidia --disable-gpu_nvidia
+%endif
+
 %if %{with_write_sensu}
 %define _with_write_sensu --enable-write_sensu
 %else
 %define _with_write_sensu --disable-write_sensu
 %endif
 
+%if %{with_write_syslog}
+%define _with_write_syslog --enable-write_syslog
+%else
+%define _with_write_syslog --disable-write_syslog
+%endif
+
 %if %{with_write_tsdb}
 %define _with_write_tsdb --enable-write_tsdb
 %else
@@ -1872,8 +1957,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 +1980,7 @@ Collectd utilities
        --enable-target_v5upgrade \
        %{?_with_aggregation} \
        %{?_with_amqp} \
+       %{?_with_amqp1} \
        %{?_with_apache} \
        %{?_with_apcups} \
        %{?_with_apple_sensors} \
@@ -1973,6 +2066,7 @@ Collectd utilities
        %{?_with_ovs_events} \
        %{?_with_ovs_stats} \
        %{?_with_perl} \
+       %{?_with_pcie_errors} \
        %{?_with_pf} \
        %{?_with_pinba} \
        %{?_with_ping} \
@@ -2024,7 +2118,10 @@ Collectd utilities
        %{?_with_write_prometheus} \
        %{?_with_write_redis} \
        %{?_with_write_riemann} \
+       %{?_with_write_stackdriver} \
+       %{?_with_gpu_nvidia} \
        %{?_with_write_sensu} \
+       %{?_with_write_syslog} \
        %{?_with_write_tsdb} \
        %{?_with_xencpu} \
        %{?_with_xmms} \
@@ -2359,6 +2456,9 @@ fi
 %if %{with_write_log}
 %{_libdir}/%{name}/write_log.so
 %endif
+%if %{with_write_syslog}
+%{_libdir}/%{name}/write_syslog.so
+%endif
 %if %{with_write_sensu}
 %{_libdir}/%{name}/write_sensu.so
 %endif
@@ -2399,6 +2499,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
@@ -2622,6 +2727,11 @@ fi
 %{_libdir}/%{name}/perl.so
 %endif
 
+%if %{with_pcie_errors}
+%files pcie_errors
+%{_libdir}/%{name}/pcie_errors.so
+%endif
+
 %if %{with_pinba}
 %files pinba
 %{_libdir}/%{name}/pinba.so
@@ -2715,6 +2825,16 @@ fi
 %{_libdir}/%{name}/write_riemann.so
 %endif
 
+%if %{with_write_stackdriver}
+%files write_stackdriver
+%{_libdir}/%{name}/write_stackdriver.so
+%endif
+
+%if %{with_gpu_nvidia}
+%files write_gpu_nvidia
+%{_libdir}/%{name}/write_gpu_nvidia.so
+%endif
+
 %if %{with_xencpu}
 %files xencpu
 %{_libdir}/%{name}/xencpu.so
@@ -2737,6 +2857,14 @@ fi
 %doc contrib/
 
 %changelog
+* Fri Jun 14 2019 Fabien Wernli <rpmbuild@faxmodem.org> - 5.9.0-1
+- add code for write_stackdriver (disabled for now)
+- add code for gpu_nvidia (disabled for now)
+- add pcie_errors
+
+* Thu Sep 28 2017 Jakub Jankowski <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 c5b1142..fe535bf 100644 (file)
@@ -8,8 +8,6 @@ Requires=local-fs.target network-online.target
 ExecStart=/usr/sbin/collectd
 EnvironmentFile=-/etc/sysconfig/collectd
 EnvironmentFile=-/etc/default/collectd
-ProtectSystem=full
-ProtectHome=true
 
 # A few plugins won't work without some privileges, which you'll have to
 # specify using the CapabilityBoundingSet directive below.
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
new file mode 100644 (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)
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 6c18448..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;
@@ -825,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;
 
@@ -850,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;
@@ -859,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);
@@ -902,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;
@@ -998,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 262fa42..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;
@@ -285,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) {
@@ -347,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;
@@ -360,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);
   }
@@ -372,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;
     }
   }
 
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 beffc1a..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++) {
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 6c6347c..fd733b4 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
@@ -1306,8 +1309,8 @@ static int collectd_barometer_config(const char *key, const char *value) {
   } else if (strcasecmp(key, "Normalization") == 0) {
     int normalize_tmp = atoi(value);
     if (normalize_tmp < 0 || normalize_tmp > 2) {
-      WARNING("barometer: collectd_barometer_config: invalid normalization: %d",
-              normalize_tmp);
+      ERROR("barometer: collectd_barometer_config: invalid normalization: %d",
+            normalize_tmp);
       return 1;
     }
     config_normalize = normalize_tmp;
@@ -1392,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);
@@ -1635,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 b77a641..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.");
@@ -474,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);
 
@@ -555,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
@@ -617,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++) {
@@ -640,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
@@ -677,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");
@@ -691,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;
@@ -712,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;
@@ -763,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);
@@ -817,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;
@@ -927,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);
@@ -946,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;
@@ -963,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:
@@ -1082,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, ...
@@ -1252,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;
@@ -1267,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
@@ -1295,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);
@@ -1325,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");
@@ -1439,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;
@@ -1484,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;
@@ -1517,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 {
@@ -1541,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)
@@ -1606,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;
@@ -1622,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 df4a720..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,
@@ -281,7 +281,9 @@ static int ceph_cb_number(void *ctx, const char *number_val,
    * "rate", use the "sum" in the pair and assign that to the derive
    * value. */
   if (convert_special_metrics && (state->depth > 2) &&
+      state->stack[state->depth - 2] &&
       (strcmp("filestore", state->stack[state->depth - 2]) == 0) &&
+      state->stack[state->depth - 1] &&
       (strcmp("journal_wr_bytes", state->stack[state->depth - 1]) == 0) &&
       (strcmp("avgcount", state->key) == 0)) {
     DEBUG("ceph plugin: Skipping avgcount for filestore.JournalWrBytes");
@@ -348,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;
 }
@@ -404,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;
@@ -415,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;
   }
@@ -470,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,
@@ -1030,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;
   }
@@ -1140,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 "
@@ -1156,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;
@@ -1178,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;
@@ -1204,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;
@@ -1225,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;
@@ -1294,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);
@@ -1303,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];
 
@@ -1341,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=%" PRIsz ",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;
@@ -1365,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) {
@@ -1386,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;
@@ -1407,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) {
@@ -1434,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 4f34b3a..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,
@@ -112,7 +112,7 @@ static int read_cpuacct_procs(const char *dirname, char const *cgroup_name,
 
     /* Strip colon off the first column, if found */
     if (key[key_len - 1] == ':')
-      key[key_len - 1] = 0;
+      key[key_len - 1] = '\0';
 
     status = parse_value(fields[1], &value, DS_TYPE_DERIVE);
     if (status != 0)
@@ -181,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.");
@@ -199,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 7a25655..eccd71f 100644 (file)
@@ -72,8 +72,8 @@ These are used to collect the actual data. It is called once
 per interval (see the B<Interval> configuration option of collectd). Usually
 it will call B<collectd.dispatch_values> to dispatch the values to collectd
 which will pass them on to all registered B<write functions>. If this function
-does not return 0 the plugin will be skipped for an increasing
-amount of time until it returns normally again.
+does not return 0, interval between its calls will grow until function returns
+0 again. See the B<MaxReadInterval> configuration option of collectd.
 
 =item write functions
 
@@ -90,12 +90,14 @@ The following functions are provided to Lua modules:
 
 =item register_read(callback)
 
+Function to register read callbacks.
 The callback will be called without arguments.
 If this callback function does not return 0 the next call will be delayed by
 an increasing interval.
 
-=item register_write
+=item register_write(callback)
 
+Function to register write callbacks.
 The callback function will be called with one argument passed, which will be a
 table of values.
 If this callback function does not return 0 next call will be delayed by
@@ -136,8 +138,8 @@ A very simple write function might look like:
 
 To register those functions with collectd:
 
-  collectd.register_read(read)
-  collectd.register_write(write)
+  collectd.register_read(read)     -- pass function as variable
+  collectd.register_write("write") -- pass by global-scope function name
 
 =back
 
@@ -150,12 +152,12 @@ L<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 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 6ec61f3..f09f373 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_SYSLOG_TRUE@LoadPlugin write_syslog
 #@BUILD_PLUGIN_WRITE_TSDB_TRUE@LoadPlugin write_tsdb
 #@BUILD_PLUGIN_XENCPU_TRUE@LoadPlugin xencpu
 #@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms
 #  </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"
 #<Plugin curl>
 #  <Page "stock_quotes">
 #    URL "http://finance.google.com/finance?q=NYSE%3AAMD"
+#    AddressFamily "any"
 #    User "foo"
 #    Password "bar"
 #    Digest false
 
 #<Plugin curl_json>
 #  <URL "http://localhost:80/test.json">
+#    AddressFamily "any"
 #    Instance "test_http_json"
 #    <Key "testArray/0">
 #      Type "gauge"
 # }
 ## See: http://wiki.apache.org/couchdb/Runtime_Statistics
 #  <URL "http://localhost:5984/_stats">
+#    AddressFamily "ipv4"
 #    Instance "httpd"
 #    <Key "httpd/requests/count">
 #      Type "http_requests"
 #  </URL>
 ## Database status metrics:
 #  <URL "http://localhost:5984/_all_dbs">
+#    AddressFamily "ipv6"
 #    Instance "dbs"
 #    <Key "*/doc_count">
 #      Type "gauge"
 
 #<Plugin curl_xml>
 #  <URL "http://localhost/stats.xml">
+#    AddressFamily "any"
 #    Host "my_host"
 #    #Plugin "stats"
 #    Instance "some_instance"
 #  PauseConnect 5
 #</Plugin>
 
+#<Plugin gpu_nvidia>
+#   GPUIndex 0
+#   GPUIndex 2
+#   IgnoreSelected false
+#</Plugin>
+
 #<Plugin grpc>
 #      <Server "example.com" "50051">
 #              EnableSSL true
 #    ReportSoftwareEvents true
 #    EventList "/var/cache/pmu/GenuineIntel-6-2D-core.json"
 #    HardwareEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS" "L2_RQSTS.ALL_CODE_RD"
+#    Cores "[0-3]"
 #</Plugin>
 
 #<Plugin "intel_rdt">
 #  Cores "0-2"
+#  Processes "sshd"
 #</Plugin>
 
 #<Plugin interface>
 #              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>
 #   <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"
+#      ExtraStats "cpu_util disk disk_err domain_state fs_info job_stats_background pcpu perf vcpu vcpupin disk_physical disk_allocation disk_capacity memory"
+#      PersistentNotification false
 #</Plugin>
 
 #<Plugin vmem>
 #    SeparateInstances false
 #    PreserveSeparator false
 #    DropDuplicateFields false
+#    ReverseHost false
 #  </Node>
 #</Plugin>
 
 #      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_syslog>
+#      <Node>
+#              Host "localhost"
+#              Port "44514"
+#              Prefix "collectd"
+#              MessageFormat "human"
+#              HostTags ""
+#              StoreRates false
+#              AlwaysAppendDS false
+#      </Node>
+#</Plugin>
+
 #<Plugin write_tsdb>
 #      <Node>
 #              Host "localhost"
index e971512..ed49195 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
@@ -1491,12 +1656,25 @@ Defaults to B<true>.
 
 =head2 Plugin C<cpufreq>
 
-This plugin doesn't have any options. It reads
+This plugin is available on Linux and FreeBSD only.  It doesn't have any
+options.  On Linux it reads
 F</sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq> (for the first CPU
 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.
 
+On Linux, 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.
+
+On FreeBSD it does a sysctl dev.cpu.0.freq and submits this as instance 0.
+At this time FreeBSD only has one frequency setting for all cores.
+See the BUGS section in the FreeBSD man page for cpufreq(4) for more details.
+
+On FreeBSD the plugin checks the success of sysctl dev.cpu.0.freq and
+unregisters the plugin when this fails.  A message will be logged to indicate
+this.
+
 =head2 Plugin C<cpusleep>
 
 This plugin doesn't have any options. It reads CLOCK_BOOTTIME and
@@ -1626,6 +1804,7 @@ finance page and dispatch the value to collectd.
     <Page "stock_quotes">
       Plugin "quotes"
       URL "http://finance.google.com/finance?q=NYSE%3AAMD"
+      AddressFamily "any"
       User "foo"
       Password "bar"
       Digest false
@@ -1666,6 +1845,18 @@ Defaults to C<curl>.
 URL of the web site to retrieve. Since a regular expression will be used to
 extract information from this data, non-binary data is a big plus here ;)
 
+=item B<AddressFamily> I<Type>
+
+IP version to resolve URL to. Useful in cases when hostname in URL resolves
+to both IPv4 and IPv6 addresses, and you are interested in using one of them
+specifically.
+Use C<ipv4> to enforce IPv4, C<ipv6> to enforce IPv6, or C<any> to keep the
+default behavior of resolving addresses to all IP versions your system allows.
+If C<libcurl> is compiled without IPv6 support, using C<ipv6> will result in
+a warning and fallback to C<any>.
+If C<Type> cannot be parsed, a warning will be printed and the whole B<Page>
+block will be ignored.
+
 =item B<User> I<Name>
 
 Username to use if authorization is required to read the page.
@@ -1743,6 +1934,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
@@ -1772,6 +1968,7 @@ C<_stats> runtime statistics module of I<CouchDB>
 
   <Plugin curl_json>
     <URL "http://localhost:5984/_stats">
+      AddressFamily "any"
       Instance "httpd"
       <Key "httpd/requests/count">
         Type "http_requests"
@@ -1816,6 +2013,18 @@ The following options are valid within B<URL> blocks:
 
 =over 4
 
+=item B<AddressFamily> I<Type>
+
+IP version to resolve URL to. Useful in cases when hostname in URL resolves
+to both IPv4 and IPv6 addresses, and you are interested in using one of them
+specifically.
+Use C<ipv4> to enforce IPv4, C<ipv6> to enforce IPv6, or C<any> to keep the
+default behavior of resolving addresses to all IP versions your system allows.
+If C<libcurl> is compiled without IPv6 support, using C<ipv6> will result in
+a warning and fallback to C<any>.
+If C<Type> cannot be parsed, a warning will be printed and the whole B<URL>
+block will be ignored.
+
 =item B<Host> I<Name>
 
 Use I<Name> as the host name when submitting values. Defaults to the global
@@ -1887,6 +2096,7 @@ The B<curl_xml plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and B<libxml2>
 
  <Plugin "curl_xml">
    <URL "http://localhost/stats.xml">
+     AddressFamily "any"
      Host "my_host"
      #Plugin "curl_xml"
      Instance "some_instance"
@@ -1923,6 +2133,18 @@ Within the B<URL> block the following options are accepted:
 
 =over 4
 
+=item B<AddressFamily> I<Type>
+
+IP version to resolve URL to. Useful in cases when hostname in URL resolves
+to both IPv4 and IPv6 addresses, and you are interested in using one of them
+specifically.
+Use C<ipv4> to enforce IPv4, C<ipv6> to enforce IPv6, or C<any> to keep the
+default behavior of resolving addresses to all IP versions your system allows.
+If C<libcurl> is compiled without IPv6 support, using C<ipv6> will result in
+a warning and fallback to C<any>.
+If C<Type> cannot be parsed, a warning will be printed and the whole B<URL>
+block will be ignored.
+
 =item B<Host> I<Name>
 
 Use I<Name> as the host name when submitting values. Defaults to the global
@@ -1939,6 +2161,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 +3258,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
@@ -3182,6 +3433,7 @@ B<Synopsis:>
     ReportSoftwareEvents true
     EventList "/var/cache/pmu/GenuineIntel-6-2D-core.json"
     HardwareEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS" "L2_RQSTS.ALL_CODE_RD"
+    Cores "0-3" "4,6" "[12-15]"
   </Plugin>
 
 B<Options:>
@@ -3253,6 +3505,23 @@ event_download.py script to download event list for current CPU.
 This field is a list of event names or groups of comma separated event names.
 This option requires B<EventList> option to be configured.
 
+=item B<Cores> I<cores groups>
+
+All events are reported on a per core basis. Monitoring of the events can be
+configured for a group of cores (aggregated statistics). This field defines
+groups of cores on which to monitor supported events. The field is represented
+as list of strings with core group values. Each string represents a list of
+cores in a group. If a group is enclosed in square brackets each core is added
+individually to a separate group (that is statistics are not aggregated).
+Allowed formats are:
+    0,1,2,3
+    0-10,20-18
+    1,3,5-8,10,0x10-12
+    [4-15,32-63]
+
+If an empty string is provided as value for this field default cores
+configuration is applied - that is separate group is created for each core.
+
 =back
 
 =head2 Plugin C<intel_rdt>
@@ -3277,6 +3546,7 @@ B<Synopsis:>
 
   <Plugin "intel_rdt">
     Cores "0-2" "3,4,6" "8-10,15"
+    Processes "sshd,qemu-system-x86" "bash"
   </Plugin>
 
 B<Options:>
@@ -3292,11 +3562,10 @@ recommended to set interval higher than 1 sec.
 
 =item B<Cores> I<cores groups>
 
-All events are reported on a per core basis. Monitoring of the events can be
-configured for group of cores (aggregated statistics). This field defines groups
-of cores on which to monitor supported events. The field is represented as list
-of strings with core group values. Each string represents a list of cores in a
-group. Allowed formats are:
+Monitoring of the events can be configured for group of cores
+(aggregated statistics). This field defines groups of cores on which to monitor
+supported events. The field is represented as list of strings with core group
+values. Each string represents a list of cores in a group. Allowed formats are:
     0,1,2,3
     0-10,20-18
     1,3,5-8,10,0x10-12
@@ -3304,6 +3573,15 @@ group. Allowed formats are:
 If an empty string is provided as value for this field default cores
 configuration is applied - a separate group is created for each core.
 
+=item B<Processes> I<process names groups>
+
+Monitoring of the events can be configured for group of processes
+(aggregated statistics). This field defines groups of processes on which to
+monitor supported events. The field is represented as list of strings with
+process names group values. Each string represents a list of processes in a
+group. Allowed format is:
+    sshd,bash,qemu
+
 =back
 
 B<Note:> By default global interval is used to retrieve statistics on monitored
@@ -3453,8 +3731,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
@@ -4059,8 +4352,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:>
 
@@ -4070,6 +4364,8 @@ B<Synopsis:>
    RegisterCmd ReadHolding
    Type voltage
    Instance "input-1"
+   #Scale 1.0
+   #Shift 0.0
  </Data>
 
  <Data "voltage-input-2">
@@ -4128,7 +4424,7 @@ 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>|B<Int32LE>|B<Uint32LE>|B<FloatLE>
+=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>,
@@ -4140,7 +4436,10 @@ 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>.
+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>
 
@@ -4155,9 +4454,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
@@ -4192,6 +4501,12 @@ For Modbus/RTU, specifies the path to the serial device being used.
 For Modbus/RTU, specifies the baud rate of the serial device.
 Note, connections currently support only 8/N/1.
 
+=item B<UARTType> I<UARTType>
+
+For Modbus/RTU, specifies the type of the serial device.
+RS232, RS422 and RS485 are supported. Defaults to RS232.
+Available only on Linux systems with libmodbus>=2.9.4.
+
 =item B<Interval> I<Interval>
 
 Sets the interval (in seconds) in which the values will be collected from this
@@ -5081,8 +5396,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:
@@ -5224,6 +5540,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
@@ -6016,6 +6338,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:
@@ -6049,6 +6372,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>
@@ -6185,6 +6561,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
@@ -6875,8 +7256,8 @@ B<Synopsis:>
    </Process>
    <ProcessMatch "name" "regex">
      CollectFileDescriptor false
-     CollectContextSwitch  true
-   </Process>
+     CollectContextSwitch true
+   </ProcessMatch>
  </Plugin>
 
 =over 4
@@ -6998,6 +7379,7 @@ multiple routers:
       CollectRegistrationTable true
       CollectDF true
       CollectDisk true
+      CollectHealth true
     </Router>
   </Plugin>
 
@@ -7058,29 +7440,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>
@@ -7088,7 +7478,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>
 
@@ -7101,6 +7493,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>.
@@ -7108,25 +7505,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
 
@@ -7566,7 +7985,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>
@@ -7577,12 +7998,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>
@@ -7591,11 +8044,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>
+
+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>
 
-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<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>
 
@@ -7984,26 +8460,26 @@ Sets how the values are cumulated. I<Type> is one of:
 
 =item B<GaugeAverage>
 
-Calculate the average.
+Calculate the average of all values matched during the interval.
 
 =item B<GaugeMin>
 
-Use the smallest number only.
+Report the smallest value matched during the interval.
 
 =item B<GaugeMax>
 
-Use the greatest number only.
+Report the greatest value matched during the interval.
 
 =item B<GaugeLast>
 
-Use the last number found.
+Report the last value matched during the interval.
 
 =item B<GaugePersist>
 
-Use the last number found. The number is not reset at the end of an interval.
-It is continously reported until another number is matched. This is intended
-for cases in which only state changes are reported, for example a thermometer
-that only reports the temperature when it changes.
+Report the last matching value. The metric is I<not> reset to C<NaN> at the end
+of an interval. It is continuously reported until another value is matched.
+This is intended for cases in which only state changes are reported, for
+example a thermometer that only reports the temperature when it changes.
 
 =item B<CounterSet>
 
@@ -8034,6 +8510,9 @@ Increase the internal counter by one. These B<DSType> are the only ones that do
 not use the matched subexpression, but simply count the number of matched
 lines. Thus, you may use a regular expression without submatch in this case.
 
+B<GaugeInc> is reset to I<zero> after every read, unlike other B<Gauge*>
+metrics which are reset to C<NaN>.
+
 =item B<Distribution>
 
 Type to do calculations based on the distribution of values, primarily
@@ -8107,8 +8586,12 @@ The B<Gauge*> and B<Distribution> types interpret the submatch as a floating
 point number, using L<strtod(3)>. The B<Counter*> and B<AbsoluteSet> types
 interpret the submatch as an unsigned integer using L<strtoull(3)>. The
 B<Derive*> types interpret the submatch as a signed integer using
-L<strtoll(3)>. B<CounterInc> and B<DeriveInc> do not use the submatch at all
-and it may be omitted in this case.
+L<strtoll(3)>. B<CounterInc>, B<DeriveInc> and B<GaugeInc> do not use the
+submatch at all and it may be omitted in this case.
+
+The B<Gauge*> types, unless noted otherwise, are reset to C<NaN> after being
+reported. In other words, B<GaugeAverage> reports the average of all values
+matched since the last metric was reported (or C<NaN> if there was no match).
 
 =item B<Type> I<Type>
 
@@ -8132,13 +8615,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>
 
@@ -8460,6 +8944,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>
@@ -8729,6 +9240,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>
@@ -8770,13 +9315,51 @@ surrounded by I</.../> and collectd was compiled with support for regexps.
 
 The default is to collect statistics for all domains and all their devices.
 
-Example:
+B<Note:> B<BlockDevice> and B<InterfaceDevice> options are related to
+corresponding B<*Format> options. Specifically, B<BlockDevice> filtering depends
+on B<BlockDeviceFormat> setting - if user wants to filter block devices by
+'target' name then B<BlockDeviceFormat> option has to be set to 'target' and
+B<BlockDevice> option must be set to a valid block device target
+name("/:hdb/"). Mixing formats and filter values from different worlds (i.e.,
+using 'target' name as B<BlockDevice> value with B<BlockDeviceFormat> set to
+'source') may lead to unexpected results (all devices filtered out or all
+visible, depending on the value of B<IgnoreSelected> option).
+Similarly, option B<InterfaceDevice> is related to B<InterfaceFormat> setting
+(i.e., when user wants to use MAC address as a filter then B<InterfaceFormat>
+has to be set to 'address' - using wrong type here may filter out all of the
+interfaces).
+
+B<Example 1:>
+
+Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
+will be collected:
 
  BlockDevice "/:hdb/"
  IgnoreSelected "true"
+ BlockDeviceFormat "target"
 
-Ignore all I<hdb> devices on any domain, but other block devices (eg. I<hda>)
-will be collected.
+B<Example 2:>
+
+Collect metrics only for block device on 'baremetal0' domain when its
+'source' matches given path:
+
+ BlockDevice "baremetal0:/var/lib/libvirt/images/baremetal0.qcow2"
+ BlockDeviceFormat source
+
+As you can see it is possible to filter devices/interfaces using
+various formats - for block devices 'target' or 'source' name can be
+used.  Interfaces can be filtered using 'name', 'address' or 'number'.
+
+B<Example 3:>
+
+Collect metrics only for domains 'baremetal0' and 'baremetal1' and
+ignore any other domain:
+
+ Domain "baremetal0"
+ Domain "baremetal1"
+
+It is possible to filter multiple block devices/domains/interfaces by
+adding multiple filtering entries in separate lines.
 
 =item B<BlockDeviceFormat> B<target>|B<source>
 
@@ -8807,6 +9390,11 @@ to C<sda>.
 Setting C<BlockDeviceFormat source> will cause the I<type instance> to be set
 to C<var_lib_libvirt_images_image1.qcow2>.
 
+B<Note:> this option determines also what field will be used for
+filtering over block devices (filter value in B<BlockDevice>
+will be applied to target or source). More info about filtering
+block devices can be found in the description of B<BlockDevice>.
+
 =item B<BlockDeviceFormatBasename> B<false>|B<true>
 
 The B<BlockDeviceFormatBasename> controls whether the full path or the
@@ -8821,7 +9409,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
@@ -8831,7 +9419,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
@@ -8841,7 +9433,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
@@ -8851,23 +9443,47 @@ 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.
+
+B<Note:> this option determines also what field will be used for
+filtering over interface device (filter value in B<InterfaceDevice>
+will be applied to name, address or number).  More info about filtering
+interfaces can be found in the description of B<InterfaceDevice>.
+
+=item B<PluginInstanceFormat> B<name|uuid|metadata|none>
 
 When the virt plugin logs data, it sets the plugin_instance of the collected
 data according to this setting. The default is to not set the plugin_instance.
 
 B<name> means use the guest's name as provided by the hypervisor.
 B<uuid> means use the guest's UUID.
+B<metadata> means use information from guest's metadata.
 
 You can also specify combinations of the B<name> and B<uuid> fields.
 For example B<name uuid> means to concatenate the guest name and UUID
 (with a literal colon character between, thus I<"foo:1234-1234-1234-1234">).
 
-=item B<Instances> B<integer>
+=item B<HostnameMetadataNS> B<string>
 
-How many read instances you want to use for this plugin. The default is one,
-and the sensible setting is a multiple of the B<ReadThreads> value.
-If you are not sure, just use the default setting.
+When B<metadata> is used in B<HostnameFormat> or B<PluginInstanceFormat>, this
+selects in which metadata namespace we will pick the hostname. The default is
+I<http://openstack.org/xmlns/libvirt/nova/1.0>.
+
+=item B<HostnameMetadataXPath> B<string>
+
+When B<metadata> is used in B<HostnameFormat> or B<PluginInstanceFormat>, this
+describes where the hostname is located in the libvirt metadata. The default is
+I</instance/name/text()>.
+
+=item B<ReportBlockDevices> B<true>|B<false>
+
+Enabled by default. Allows to disable stats reporting of block devices for
+whole plugin.
+
+=item B<ReportNetworkInterfaces> B<true>|B<false>
+
+Enabled by default. Allows to disable stats reporting of network interfaces for
+whole plugin.
 
 =item B<ExtraStats> B<string>
 
@@ -8889,9 +9505,7 @@ I<0.9.5> or later.
 =item B<disk_err>: report disk errors if any occured. Requires libvirt API version
 I<0.9.10> or later.
 
-=item B<domain_state>: report domain state and reason in human-readable format as
-a notification. If libvirt API version I<0.9.2> or later is available, domain
-reason will be included in notification.
+=item B<domain_state>: report domain state and reason as 'domain_state' metric.
 
 =item B<fs_info>: report file system information as a notification. Requires
 libvirt API version I<1.2.11> or later. Can be collected only if I<Guest Agent>
@@ -8906,6 +9520,9 @@ Requires libvirt API version I<1.2.9> or later.
 a domain. Only one type of job statistics can be collected at the same time.
 Requires libvirt API version I<1.2.9> or later.
 
+=item B<memory>: report statistics about memory usage details, provided
+by libvirt virDomainMemoryStats() function.
+
 =item B<pcpu>: report the physical user/system cpu time consumed by the hypervisor, per-vm.
 Requires libvirt API version I<0.9.11> or later.
 
@@ -8914,10 +9531,57 @@ metrics they must be enabled for domain and supported by the platform. Requires
 libvirt API version I<1.3.3> or later.
 B<Note>: I<perf> metrics can't be collected if I<intel_rdt> plugin is enabled.
 
+=item B<vcpu>: report domain virtual CPUs utilisation.
+
 =item B<vcpupin>: report pinning of domain VCPUs to host physical CPUs.
 
+=item B<disk_physical>: report 'disk_physical' statistic for disk device.
+B<Note>: This statistic is only reported for disk devices with 'source'
+property available.
+
+=item B<disk_allocation>: report 'disk_allocation' statistic for disk device.
+B<Note>: This statistic is only reported for disk devices with 'source'
+property available.
+
+=item B<disk_capacity>: report 'disk_capacity' statistic for disk device.
+B<Note>: This statistic is only reported for disk devices with 'source'
+property available.
+
 =back
 
+=item B<PersistentNotification> B<true>|B<false>
+
+Override default configuration to only send notifications when there is a change
+in the lifecycle state of a domain. When set to true notifications will be sent
+for every read cycle. Default is false. Does not affect the stats being
+dispatched.
+
+=item B<Instances> B<integer>
+
+How many read instances you want to use for this plugin. The default is one,
+and the sensible setting is a multiple of the B<ReadThreads> value.
+
+This option is only useful when domains are specially tagged.
+If you are not sure, just use the default setting.
+
+The reader instance will only query the domains with attached matching tag.
+Tags should have the form of 'virt-X' where X is the reader instance number,
+starting from 0.
+
+The special-purpose reader instance #0, guaranteed to be always present,
+will query all the domains with missing or unrecognized tag, so no domain will
+ever be left out.
+
+Domain tagging is done with a custom attribute in the libvirt domain metadata
+section. Value is selected by an XPath I</domain/metadata/ovirtmap/tag/text()>
+expression in the I<http://ovirt.org/ovirtmap/tag/1.0> namespace.
+(XPath and namespace values are not configurable yet).
+
+Tagging could be used by management applications to evenly spread the
+load among the reader threads, or to pin on the same threads all
+the libvirt domains which use the same shared storage, to minimize
+the disruption in presence of storage outages.
+
 =back
 
 =head2 Plugin C<vmem>
@@ -8971,6 +9635,8 @@ Synopsis:
      Protocol "tcp"
      LogSendErrors true
      Prefix "collectd"
+     UseTags false
+     ReverseHost false
    </Node>
  </Plugin>
 
@@ -9008,13 +9674,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>
 
@@ -9036,6 +9709,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"
@@ -9048,12 +9723,55 @@ 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>.
+
+=item B<ReverseHost> B<false>|B<true>
+
+If set to B<true>, the (dot separated) parts of the B<host> field of the
+I<value list> will be rewritten in reverse order. The rewrite happens I<before>
+special characters are replaced with the B<EscapeCharacter>.
+
+This option might be convenient if the metrics are presented with Graphite in a
+DNS like tree structure (probably without replacing dots in hostnames).
+
+Example:
+ Hostname "node3.cluster1.example.com"
+ LoadPlugin "cpu"
+ LoadPlugin "write_graphite"
+ <Plugin "write_graphite">
+  <Node "graphite.example.com">
+   EscapeCharacter "."
+   ReverseHost true
+  </Node>
+ </Plugin>
+
+ result on the wire: com.example.cluster1.node3.cpu-0.cpu-idle 99.900993 1543010932
+
+Default value: B<false>.
+
 =back
 
 =head2 Plugin C<write_log>
@@ -9222,6 +9940,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>.
@@ -9479,17 +10204,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.
@@ -9504,6 +10238,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"
@@ -9516,6 +10252,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
@@ -9876,6 +10620,268 @@ 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<write_syslog>
+
+The C<write_syslog> plugin writes data in I<syslog> format log messages.
+It implements the basic syslog protocol, RFC 5424, extends it with
+content-based filtering, rich filtering capabilities,
+flexible configuration options and adds features such as using TCP for transport.
+The plugin can connect to a I<Syslog> daemon, like syslog-ng and rsyslog, that will
+ingest metrics, transform and ship them to the specified output.
+The plugin uses I<TCP> over the "line based" protocol with a default port 44514.
+The data will be sent in blocks of at most 1428 bytes to minimize the number of
+network packets.
+
+Synopsis:
+
+ <Plugin write_syslog>
+   ResolveInterval 60
+   ResolveJitter 60
+   <Node "example">
+     Host "syslog-1.my.domain"
+     Port "44514"
+     Prefix "collectd"
+     MessageFormat "human"
+     HostTags ""
+   </Node>
+ </Plugin>
+
+The configuration consists of one or more E<lt>B<Node>E<nbsp>I<Name>E<gt>
+blocks and global directives.
+
+Global directives are:
+
+=over 4
+
+=item B<ResolveInterval> I<seconds>
+
+=item B<ResolveJitter> I<seconds>
+
+When I<collectd> connects to a syslog node, it will request the hostname from
+DNS. This can become a problem if the syslog node is unavailable or badly
+configured because collectd will request DNS in order to reconnect for every
+metric, which can flood your DNS. So you can cache the last value for
+I<ResolveInterval> seconds.
+Defaults to the I<Interval> of the I<write_syslog plugin>, e.g. 10E<nbsp>seconds.
+
+You can also define a jitter, a random interval to wait in addition to
+I<ResolveInterval>. This prevents all your collectd servers to resolve the
+hostname at the same time when the connection fails.
+Defaults to the I<Interval> of the I<write_syslog plugin>, e.g. 10E<nbsp>seconds.
+
+B<Note:> If the DNS resolution has already been successful when the socket
+closes, the plugin will try to reconnect immediately with the cached
+information. DNS is queried only when the socket is closed for a longer than
+I<ResolveInterval> + I<ResolveJitter> seconds.
+
+=back
+
+Inside the B<Node> blocks, the following options are recognized:
+
+=over 4
+
+=item B<Host> I<Address>
+
+Hostname or address to connect to. Defaults to C<localhost>.
+
+=item B<Port> I<Service>
+
+Service name or port number to connect to. Defaults to C<44514>.
+
+
+=item B<HostTags> I<String>
+
+When set, I<HostTags> is added to the end of the metric.
+It is intended to be used for adding additional metadata to tag the metric with.
+Dots and whitespace are I<not> escaped in this string.
+
+Examples:
+
+When MessageFormat is set to "human".
+
+  ["prefix1" "example1"="example1_v"]["prefix2" "example2"="example2_v"]"
+
+When MessageFormat is set to "JSON", text should be in JSON format.
+Escaping the quotation marks is required.
+
+  HostTags "\"prefix1\": {\"example1\":\"example1_v\",\"example2\":\"example2_v\"}"
+
+=item B<MessageFormat> I<String>
+
+I<MessageFormat> selects the format in which messages are sent to the
+syslog deamon, human or JSON. Defaults to human.
+
+Syslog message format:
+
+<priority>VERSION ISOTIMESTAMP HOSTNAME APPLICATION PID MESSAGEID STRUCTURED-DATA MSG
+
+The difference between the message formats are in the STRUCTURED-DATA and MSG parts.
+
+Human format:
+
+  <166>1 ISOTIMESTAMP HOSTNAME collectd PID MESSAGEID
+  ["collectd" "value": "v1" "plugin"="plugin_v" "plugin_instance"="plugin_instance_v"
+  "type_instance"="type_instance_v" "type"="type_v" "ds_name"="ds_name_v" "interval"="interval_v" ]
+  "host_tag_example"="host_tag_example_v" plugin_v.type_v.ds_name_v="v1"
+
+JSON format:
+
+  <166>1 ISOTIMESTAMP HOSTNAME collectd PID MESSAGEID STRUCTURED-DATA
+  {
+    "collectd": {
+    "time": time_as_epoch, "interval": interval_v, "plugin": "plugin_v",
+    "plugin_instance": "plugin_instance_v", "type":"type_v",
+    "type_instance": "type_instance_v", "plugin_v": {"type_v": v1}
+    } , "host":"host_v", "host_tag_example": "host_tag_example_v"
+  }
+
+=item B<StoreRates> B<false>|B<true>
+
+If set to B<true>, convert counter values to rates. If set to B<false>
+(the default) counter values are stored as is, as an increasing
+integer number.
+
+=item B<AlwaysAppendDS> B<false>|B<true>
+
+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<Prefix> I<String>
+
+When set, I<Prefix> is added to all metrics names as a prefix. It is intended in
+case you want to be able to define the source of the specific metric. Dots and
+whitespace are I<not> escaped in this string.
+
+=back
+
 =head2 Plugin C<xencpu>
 
 This plugin collects metrics of hardware CPU load for machine running Xen
@@ -10864,7 +11870,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 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 35ac5a3..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>
index 8480f11..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;
 
@@ -431,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);
@@ -505,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 */
@@ -588,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 */
 
index 0139947..f95b282 100644 (file)
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
-static int num_cpu = 0;
+#if KERNEL_FREEBSD
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#endif
+
+#if KERNEL_LINUX
+#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;
+}
+#endif /* KERNEL_LINUX */
 
 static int cpufreq_init(void) {
-  int status;
-  char filename[256];
+#if KERNEL_LINUX
+  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,30 +100,118 @@ 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");
+#elif KERNEL_FREEBSD
+  char mib[] = "dev.cpu.0.freq";
+  int cpufreq;
+  size_t cf_len = sizeof(cpufreq);
+
+  if (sysctlbyname(mib, &cpufreq, &cf_len, NULL, 0) != 0) {
+    WARNING("cpufreq plugin: sysctl \"%s\" failed.", mib);
+    plugin_unregister_read("cpufreq");
+  }
+#endif
 
   return 0;
 } /* int cpufreq_init */
 
-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);
 }
 
+#if KERNEL_LINUX
+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);
+}
+#endif /* KERNEL_LINUX */
+
 static int cpufreq_read(void) {
-  for (int i = 0; i < num_cpu; i++) {
+#if KERNEL_LINUX
+  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 +222,28 @@ 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);
   }
+#elif KERNEL_FREEBSD
+  /* FreeBSD currently only has 1 freq setting.  See BUGS in cpufreq(4) */
+  char mib[] = "dev.cpu.0.freq";
+  int cpufreq;
+  size_t cf_len = sizeof(cpufreq);
+
+  if (sysctlbyname(mib, &cpufreq, &cf_len, NULL, 0) != 0) {
+    WARNING("cpufreq plugin: sysctl \"%s\" failed.", mib);
+    return 0;
+  }
+
+  value_t v;
+  /* convert Mhz to Hz */
+  v.gauge = cpufreq * 1000000.0;
 
+  cpufreq_submit(0, "cpufreq", NULL, &v);
+#endif
   return 0;
 } /* int cpufreq_read */
 
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 f5126d2..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) {
@@ -190,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;
       }
index 35ec1f8..e3e3c17 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>
@@ -57,17 +57,18 @@ struct web_page_s /* {{{ */
   char *instance;
 
   char *url;
+  int address_family;
   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 +79,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 +133,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 +158,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 */
 
@@ -351,6 +346,7 @@ static int cc_page_init_curl(web_page_t *wp) /* {{{ */
   curl_easy_setopt(wp->curl, CURLOPT_ERRORBUFFER, wp->curl_errbuf);
   curl_easy_setopt(wp->curl, CURLOPT_FOLLOWLOCATION, 1L);
   curl_easy_setopt(wp->curl, CURLOPT_MAXREDIRS, 50L);
+  curl_easy_setopt(wp->curl, CURLOPT_IPRESOLVE, wp->address_family);
 
   if (wp->user != NULL) {
 #ifdef HAVE_CURLOPT_USERNAME
@@ -401,6 +397,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;
 
@@ -416,13 +413,14 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
   }
   page->plugin_name = NULL;
   page->url = NULL;
+  page->address_family = CURL_IPRESOLVE_WHATEVER;
   page->user = NULL;
   page->pass = NULL;
-  page->digest = 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;
 
@@ -442,7 +440,34 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string(child, &page->plugin_name);
     else if (strcasecmp("URL", child->key) == 0)
       status = cf_util_get_string(child, &page->url);
-    else if (strcasecmp("User", child->key) == 0)
+    else if (strcasecmp("AddressFamily", child->key) == 0) {
+      char *af = NULL;
+      status = cf_util_get_string(child, &af);
+      if (status != 0 || af == NULL) {
+        WARNING("curl plugin: Cannot parse value of `%s' "
+                "for instance `%s'.",
+                child->key, page->instance);
+      } else if (strcasecmp("any", af) == 0) {
+        page->address_family = CURL_IPRESOLVE_WHATEVER;
+      } else if (strcasecmp("ipv4", af) == 0) {
+        page->address_family = CURL_IPRESOLVE_V4;
+      } else if (strcasecmp("ipv6", af) == 0) {
+        /* If curl supports ipv6, use it. If not, log a warning and
+         * fall back to default - don't set status to non-zero.
+         */
+        curl_version_info_data *curl_info = curl_version_info(CURLVERSION_NOW);
+        if (curl_info->features & CURL_VERSION_IPV6)
+          page->address_family = CURL_IPRESOLVE_V6;
+        else
+          WARNING("curl plugin: IPv6 not supported by this libCURL. "
+                  "Using fallback `any'.");
+      } else {
+        WARNING("curl plugin: Unsupported value of `%s' "
+                "for instance `%s'.",
+                child->key, page->instance);
+        status = -1;
+      }
+    } else if (strcasecmp("User", child->key) == 0)
       status = cf_util_get_string(child, &page->user);
     else if (strcasecmp("Password", child->key) == 0)
       status = cf_util_get_string(child, &page->pass);
@@ -465,6 +490,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 +535,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 +582,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 +630,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 +696,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 09a606a..9d01e6e 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;
@@ -88,16 +88,16 @@ struct cj_s /* {{{ */
   char *sock;
 
   char *url;
+  int address_family;
   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 +237,7 @@ static int cj_cb_number(void *ctx, const char *number, yajl_len_t number_len) {
   /* Create a null-terminated version of the string. */
   char buffer[number_len + 1];
   memcpy(buffer, number, number_len);
-  buffer[sizeof(buffer) - 1] = 0;
+  buffer[sizeof(buffer) - 1] = '\0';
 
   if (db->state[db->depth].entry == NULL ||
       db->state[db->depth].entry->type != KEY) {
@@ -256,7 +256,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 +273,7 @@ static int cj_cb_map_key(void *ctx, unsigned char const *in_name,
   char name[in_name_len + 1];
 
   memmove(name, in_name, in_name_len);
-  name[sizeof(name) - 1] = 0;
+  name[sizeof(name) - 1] = '\0';
 
   if (cj_load_key(ctx, name) != 0)
     return CJ_CB_ABORT;
@@ -325,7 +324,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 +334,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);
 }
 
@@ -584,6 +583,7 @@ static int cj_init_curl(cj_t *db) /* {{{ */
   curl_easy_setopt(db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
   curl_easy_setopt(db->curl, CURLOPT_FOLLOWLOCATION, 1L);
   curl_easy_setopt(db->curl, CURLOPT_MAXREDIRS, 50L);
+  curl_easy_setopt(db->curl, CURLOPT_IPRESOLVE, db->address_family);
 
   if (db->user != NULL) {
 #ifdef HAVE_CURLOPT_USERNAME
@@ -624,9 +624,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 +636,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 "
@@ -653,6 +651,7 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */
   }
 
   db->timeout = -1;
+  db->address_family = CURL_IPRESOLVE_WHATEVER;
 
   if (strcasecmp("URL", ci->key) == 0)
     status = cf_util_get_string(ci, &db->url);
@@ -699,13 +698,38 @@ 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) {
       db->stats = curl_stats_from_config(child);
       if (db->stats == NULL)
         status = -1;
+    } else if (db->url && strcasecmp("AddressFamily", child->key) == 0) {
+      char *af = NULL;
+      status = cf_util_get_string(child, &af);
+      if (status != 0 || af == NULL) {
+        WARNING("curl_json plugin: Cannot parse value of `%s' for URL `%s'.",
+                child->key, db->url);
+      } else if (strcasecmp("any", af) == 0) {
+        db->address_family = CURL_IPRESOLVE_WHATEVER;
+      } else if (strcasecmp("ipv4", af) == 0) {
+        db->address_family = CURL_IPRESOLVE_V4;
+      } else if (strcasecmp("ipv6", af) == 0) {
+        /* If curl supports ipv6, use it. If not, log a warning and
+         * fall back to default - don't set status to non-zero.
+         */
+        curl_version_info_data *curl_info = curl_version_info(CURLVERSION_NOW);
+        if (curl_info->features & CURL_VERSION_IPV6)
+          db->address_family = CURL_IPRESOLVE_V6;
+        else
+          WARNING("curl_json plugin: IPv6 not supported by this libCURL. "
+                  "Using fallback `any'.");
+      } else {
+        WARNING("curl_json plugin: Unsupported value of `%s' for URL `%s'.",
+                child->key, db->url);
+        status = -1;
+      }
     } else {
       WARNING("curl_json plugin: Option `%s' not allowed here.", child->key);
       status = -1;
@@ -737,8 +761,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,9 +839,6 @@ 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 */
 
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 19ae5f4..ce6bd46 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>
@@ -76,12 +76,13 @@ struct cx_s /* {{{ */
   char *host;
 
   char *url;
+  int address_family;
   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;
@@ -736,6 +737,7 @@ static int cx_init_curl(cx_t *db) /* {{{ */
   curl_easy_setopt(db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
   curl_easy_setopt(db->curl, CURLOPT_FOLLOWLOCATION, 1L);
   curl_easy_setopt(db->curl, CURLOPT_MAXREDIRS, 50L);
+  curl_easy_setopt(db->curl, CURLOPT_IPRESOLVE, db->address_family);
 
   if (db->user != NULL) {
 #ifdef HAVE_CURLOPT_USERNAME
@@ -814,6 +816,7 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
   }
 
   db->timeout = -1;
+  db->address_family = CURL_IPRESOLVE_WHATEVER;
 
   int status = cf_util_get_string(ci, &db->url);
   if (status != 0) {
@@ -823,6 +826,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,12 +858,39 @@ 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) {
       db->stats = curl_stats_from_config(child);
       if (db->stats == NULL)
         status = -1;
+    } else if (strcasecmp("AddressFamily", child->key) == 0) {
+      char *af = NULL;
+      status = cf_util_get_string(child, &af);
+      if (status != 0 || af == NULL) {
+        WARNING("curl_xml plugin: Cannot parse value of `%s' for URL `%s'.",
+                child->key, db->url);
+      } else if (strcasecmp("any", af) == 0) {
+        db->address_family = CURL_IPRESOLVE_WHATEVER;
+      } else if (strcasecmp("ipv4", af) == 0) {
+        db->address_family = CURL_IPRESOLVE_V4;
+      } else if (strcasecmp("ipv6", af) == 0) {
+        /* If curl supports ipv6, use it. If not, log a warning and
+         * fall back to default - don't set status to non-zero.
+         */
+        curl_version_info_data *curl_info = curl_version_info(CURLVERSION_NOW);
+        if (curl_info->features & CURL_VERSION_IPV6)
+          db->address_family = CURL_IPRESOLVE_V6;
+        else
+          WARNING("curl_xml plugin: IPv6 not supported by this libCURL. "
+                  "Using fallback `any'.");
+      } else {
+        WARNING("curl_xml plugin: Unsupported value of `%s' for URL `%s'.",
+                child->key, db->url);
+        status = -1;
+      }
     } else {
       WARNING("curl_xml plugin: Option `%s' not allowed here.", child->key);
       status = -1;
@@ -891,7 +923,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 727876b..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,18 +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) {
     ERROR("strdup failed: %s", STRERRNO);
     return -1;
   }
 
-  dirlen = strlen(dir);
+  size_t dirlen = strlen(dir);
   while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
     dir[--dirlen] = '\0';
 
@@ -176,7 +155,7 @@ 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;
@@ -211,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();
@@ -225,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"
 
@@ -292,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
@@ -306,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!",
@@ -333,150 +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) {
-    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) {
-  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 readiness.");
-
-  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) {
-    ERROR("creating UNIX socket failed: %s", STRERRNO);
-    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) {
-    ERROR("sendto(\"%s\") failed: %s", notifysocket, STRERRNO);
-    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, "htTC:"
+    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
@@ -484,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
@@ -505,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;
   }
 
@@ -514,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.");
@@ -535,134 +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 */
-      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);
-
-    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)) {
-    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 (0 != sigaction(SIGTERM, &sig_term_action, NULL)) {
-    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};
+  return config;
+}
 
-  if (0 != sigaction(SIGUSR1, &sig_usr1_action, NULL)) {
-    ERROR("Error: Failed to install a signal handler for signal USR1: %s",
-          STRERRNO);
-    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;
@@ -680,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 fbbb608..0000000
+++ /dev/null
@@ -1,1579 +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 ((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;
-
-          ERROR("check_create_dir: mkdir (%s): %s", dir, STRERRNO);
-          return -1;
-        } else {
-          ERROR("check_create_dir: stat (%s): %s", dir, STRERRNO);
-          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(":%" 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);
-    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) {
-    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)) {
-    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 cf4c5a7..0000000
+++ /dev/null
@@ -1,394 +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);
-
-#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
-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 af2840e..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-/**
- * collectd - src/tests/test_common.c
- * Copyright (C) 2013       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- */
-
-#include "common.h"
-#include "testing.h"
-
-#if HAVE_KSTAT_H
-#include <kstat.h>
-#endif
-
-#if HAVE_LIBKSTAT
-kstat_ctl_t *kc;
-#endif /* HAVE_LIBKSTAT */
-
-DEF_TEST(sstrncpy) {
-  char buffer[16] = "";
-  char *ptr = &buffer[4];
-  char *ret;
-
-  buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff;
-  buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff;
-
-  ret = sstrncpy(ptr, "foobar", 8);
-  OK(ret == ptr);
-  EXPECT_EQ_STR("foobar", ptr);
-  OK(buffer[3] == buffer[12]);
-
-  ret = sstrncpy(ptr, "abc", 8);
-  OK(ret == ptr);
-  EXPECT_EQ_STR("abc", ptr);
-  OK(buffer[3] == buffer[12]);
-
-  ret = sstrncpy(ptr, "collectd", 8);
-  OK(ret == ptr);
-  OK(ptr[7] == 0);
-  EXPECT_EQ_STR("collect", ptr);
-  OK(buffer[3] == buffer[12]);
-
-  return 0;
-}
-
-DEF_TEST(sstrdup) {
-  char *ptr;
-
-  ptr = sstrdup("collectd");
-  OK(ptr != NULL);
-  EXPECT_EQ_STR("collectd", ptr);
-
-  sfree(ptr);
-
-  ptr = sstrdup(NULL);
-  OK(ptr == NULL);
-
-  return 0;
-}
-
-DEF_TEST(strsplit) {
-  char buffer[32];
-  char *fields[8];
-  int status;
-
-  strncpy(buffer, "foo bar", sizeof(buffer));
-  status = strsplit(buffer, fields, 8);
-  OK(status == 2);
-  EXPECT_EQ_STR("foo", fields[0]);
-  EXPECT_EQ_STR("bar", fields[1]);
-
-  strncpy(buffer, "foo \t bar", sizeof(buffer));
-  status = strsplit(buffer, fields, 8);
-  OK(status == 2);
-  EXPECT_EQ_STR("foo", fields[0]);
-  EXPECT_EQ_STR("bar", fields[1]);
-
-  strncpy(buffer, "one two\tthree\rfour\nfive", sizeof(buffer));
-  status = strsplit(buffer, fields, 8);
-  OK(status == 5);
-  EXPECT_EQ_STR("one", fields[0]);
-  EXPECT_EQ_STR("two", fields[1]);
-  EXPECT_EQ_STR("three", fields[2]);
-  EXPECT_EQ_STR("four", fields[3]);
-  EXPECT_EQ_STR("five", fields[4]);
-
-  strncpy(buffer, "\twith trailing\n", sizeof(buffer));
-  status = strsplit(buffer, fields, 8);
-  OK(status == 2);
-  EXPECT_EQ_STR("with", fields[0]);
-  EXPECT_EQ_STR("trailing", fields[1]);
-
-  strncpy(buffer, "1 2 3 4 5 6 7 8 9 10 11 12 13", sizeof(buffer));
-  status = strsplit(buffer, fields, 8);
-  OK(status == 8);
-  EXPECT_EQ_STR("7", fields[6]);
-  EXPECT_EQ_STR("8", fields[7]);
-
-  strncpy(buffer, "single", sizeof(buffer));
-  status = strsplit(buffer, fields, 8);
-  OK(status == 1);
-  EXPECT_EQ_STR("single", fields[0]);
-
-  strncpy(buffer, "", sizeof(buffer));
-  status = strsplit(buffer, fields, 8);
-  OK(status == 0);
-
-  return 0;
-}
-
-DEF_TEST(strjoin) {
-  struct {
-    char **fields;
-    size_t fields_num;
-    char *separator;
-
-    int want_return;
-    char *want_buffer;
-  } cases[] = {
-      /* Normal case. */
-      {(char *[]){"foo", "bar"}, 2, "!", 7, "foo!bar"},
-      /* One field only. */
-      {(char *[]){"foo"}, 1, "!", 3, "foo"},
-      /* No fields at all. */
-      {NULL, 0, "!", 0, ""},
-      /* Longer separator. */
-      {(char *[]){"foo", "bar"}, 2, "rcht", 10, "foorchtbar"},
-      /* Empty separator. */
-      {(char *[]){"foo", "bar"}, 2, "", 6, "foobar"},
-      /* NULL separator. */
-      {(char *[]){"foo", "bar"}, 2, NULL, 6, "foobar"},
-      /* buffer not large enough -> string is truncated. */
-      {(char *[]){"aaaaaa", "bbbbbb", "c!"}, 3, "-", 16, "aaaaaa-bbbbbb-c"},
-      /* buffer not large enough -> last field fills buffer completely. */
-      {(char *[]){"aaaaaaa", "bbbbbbb", "!"}, 3, "-", 17, "aaaaaaa-bbbbbbb"},
-      /* buffer not large enough -> string does *not* end in separator. */
-      {(char *[]){"aaaa", "bbbb", "cccc", "!"}, 4, "-", 16, "aaaa-bbbb-cccc"},
-      /* buffer not large enough -> string does not end with partial
-         separator. */
-      {(char *[]){"aaaaaa", "bbbbbb", "!"}, 3, "+-", 17, "aaaaaa+-bbbbbb"},
-  };
-
-  for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
-    char buffer[16];
-    int status;
-
-    memset(buffer, 0xFF, sizeof(buffer));
-    status = strjoin(buffer, sizeof(buffer), cases[i].fields,
-                     cases[i].fields_num, cases[i].separator);
-    EXPECT_EQ_INT(cases[i].want_return, status);
-    EXPECT_EQ_STR(cases[i].want_buffer, buffer);
-  }
-
-  /* use (NULL, 0) to determine required buffer size. */
-  EXPECT_EQ_INT(3, strjoin(NULL, 0, (char *[]){"a", "b"}, 2, "-"));
-
-  return 0;
-}
-
-DEF_TEST(escape_slashes) {
-  struct {
-    char *str;
-    char *want;
-  } cases[] = {
-      {"foo/bar/baz", "foo_bar_baz"},
-      {"/like/a/path", "like_a_path"},
-      {"trailing/slash/", "trailing_slash_"},
-      {"foo//bar", "foo__bar"},
-  };
-
-  for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
-    char buffer[32];
-
-    strncpy(buffer, cases[i].str, sizeof(buffer));
-    OK(escape_slashes(buffer, sizeof(buffer)) == 0);
-    EXPECT_EQ_STR(cases[i].want, buffer);
-  }
-
-  return 0;
-}
-
-DEF_TEST(escape_string) {
-  struct {
-    char *str;
-    char *want;
-  } cases[] = {
-      {"foobar", "foobar"},
-      {"f00bar", "f00bar"},
-      {"foo bar", "\"foo bar\""},
-      {"foo \"bar\"", "\"foo \\\"bar\\\"\""},
-      {"012345678901234", "012345678901234"},
-      {"012345 78901234", "\"012345 789012\""},
-      {"012345 78901\"34", "\"012345 78901\""},
-  };
-
-  for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
-    char buffer[16];
-
-    strncpy(buffer, cases[i].str, sizeof(buffer));
-    OK(escape_string(buffer, sizeof(buffer)) == 0);
-    EXPECT_EQ_STR(cases[i].want, buffer);
-  }
-
-  return 0;
-}
-
-DEF_TEST(strunescape) {
-  char buffer[16];
-  int status;
-
-  strncpy(buffer, "foo\\tbar", sizeof(buffer));
-  status = strunescape(buffer, sizeof(buffer));
-  OK(status == 0);
-  EXPECT_EQ_STR("foo\tbar", buffer);
-
-  strncpy(buffer, "\\tfoo\\r\\n", sizeof(buffer));
-  status = strunescape(buffer, sizeof(buffer));
-  OK(status == 0);
-  EXPECT_EQ_STR("\tfoo\r\n", buffer);
-
-  strncpy(buffer, "With \\\"quotes\\\"", sizeof(buffer));
-  status = strunescape(buffer, sizeof(buffer));
-  OK(status == 0);
-  EXPECT_EQ_STR("With \"quotes\"", buffer);
-
-  /* Backslash before null byte */
-  strncpy(buffer, "\\tbackslash end\\", sizeof(buffer));
-  status = strunescape(buffer, sizeof(buffer));
-  OK(status != 0);
-  EXPECT_EQ_STR("\tbackslash end", buffer);
-  return 0;
-
-  /* Backslash at buffer end */
-  strncpy(buffer, "\\t3\\56", sizeof(buffer));
-  status = strunescape(buffer, 4);
-  OK(status != 0);
-  OK(buffer[0] == '\t');
-  OK(buffer[1] == '3');
-  OK(buffer[2] == 0);
-  OK(buffer[3] == 0);
-  OK(buffer[4] == '5');
-  OK(buffer[5] == '6');
-  OK(buffer[6] == '7');
-
-  return 0;
-}
-
-DEF_TEST(parse_values) {
-  struct {
-    char buffer[64];
-    int status;
-    gauge_t value;
-  } cases[] = {
-      {"1435044576:42", 0, 42.0}, {"1435044576:42:23", -1, NAN},
-      {"1435044576:U", 0, NAN},   {"N:12.3", 0, 12.3},
-      {"N:42.0:23", -1, NAN},     {"N:U", 0, NAN},
-      {"T:42.0", -1, NAN},
-  };
-
-  for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
-    data_source_t dsrc = {
-        .name = "value", .type = DS_TYPE_GAUGE, .min = 0.0, .max = NAN,
-    };
-    data_set_t ds = {
-        .type = "example", .ds_num = 1, .ds = &dsrc,
-    };
-
-    value_t v = {
-        .gauge = NAN,
-    };
-    value_list_t vl = {
-        .values = &v,
-        .values_len = 1,
-        .time = 0,
-        .interval = 0,
-        .host = "example.com",
-        .plugin = "common_test",
-        .type = "example",
-        .meta = NULL,
-    };
-
-    int status = parse_values(cases[i].buffer, &vl, &ds);
-    EXPECT_EQ_INT(cases[i].status, status);
-    if (status != 0)
-      continue;
-
-    EXPECT_EQ_DOUBLE(cases[i].value, vl.values[0].gauge);
-  }
-
-  return 0;
-}
-
-DEF_TEST(value_to_rate) {
-  struct {
-    time_t t0;
-    time_t t1;
-    int ds_type;
-    value_t v0;
-    value_t v1;
-    gauge_t want;
-  } cases[] = {
-      {0, 10, DS_TYPE_DERIVE, {.derive = 0}, {.derive = 1000}, NAN},
-      {10, 20, DS_TYPE_DERIVE, {.derive = 1000}, {.derive = 2000}, 100.0},
-      {20, 30, DS_TYPE_DERIVE, {.derive = 2000}, {.derive = 1800}, -20.0},
-      {0, 10, DS_TYPE_COUNTER, {.counter = 0}, {.counter = 1000}, NAN},
-      {10, 20, DS_TYPE_COUNTER, {.counter = 1000}, {.counter = 5000}, 400.0},
-      /* 32bit wrap-around. */
-      {20,
-       30,
-       DS_TYPE_COUNTER,
-       {.counter = 4294967238ULL},
-       {.counter = 42},
-       10.0},
-      /* 64bit wrap-around. */
-      {30,
-       40,
-       DS_TYPE_COUNTER,
-       {.counter = 18446744073709551558ULL},
-       {.counter = 42},
-       10.0},
-  };
-
-  for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
-    cdtime_t t0 = TIME_T_TO_CDTIME_T(cases[i].t0);
-    value_to_rate_state_t state = {
-        .last_value = cases[i].v0, .last_time = t0,
-    };
-    gauge_t got;
-
-    if (cases[i].t0 == 0) {
-      EXPECT_EQ_INT(EAGAIN,
-                    value_to_rate(&got, cases[i].v1, cases[i].ds_type,
-                                  TIME_T_TO_CDTIME_T(cases[i].t1), &state));
-      continue;
-    }
-
-    EXPECT_EQ_INT(0, value_to_rate(&got, cases[i].v1, cases[i].ds_type,
-                                   TIME_T_TO_CDTIME_T(cases[i].t1), &state));
-    EXPECT_EQ_DOUBLE(cases[i].want, got);
-  }
-
-  return 0;
-}
-
-int main(void) {
-  RUN_TEST(sstrncpy);
-  RUN_TEST(sstrdup);
-  RUN_TEST(strsplit);
-  RUN_TEST(strjoin);
-  RUN_TEST(escape_slashes);
-  RUN_TEST(escape_string);
-  RUN_TEST(strunescape);
-  RUN_TEST(parse_values);
-  RUN_TEST(value_to_rate);
-
-  END_TEST;
-}
index e61128e..5cf8ac1 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,38 @@ 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 +287,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 +340,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 +353,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 +382,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 +412,16 @@ 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) {
+      oconfig_item_t *child = ci->children + i;
+      ret = dispatch_value_plugin(name, child);
+      if (ret != 0) {
+        ERROR("Plugin %s failed to handle option %s, return code: %i", name,
+              child->key, ret);
+        return ret;
+      }
+    } else {
       WARNING("There is a `%s' block within the "
               "configuration for the %s plugin. "
               "The plugin either only expects "
@@ -461,9 +480,9 @@ static int cf_ci_replace_child(oconfig_item_t *dst, oconfig_item_t *src,
     return 0;
   }
 
-  temp = realloc(dst->children,
-                 sizeof(oconfig_item_t) *
-                     (dst->children_num + src->children_num - 1));
+  temp =
+      realloc(dst->children, sizeof(oconfig_item_t) *
+                                 (dst->children_num + src->children_num - 1));
   if (temp == NULL) {
     ERROR("configfile: realloc failed.");
     return -1;
@@ -502,9 +521,8 @@ static int cf_ci_append_children(oconfig_item_t *dst, oconfig_item_t *src) {
   if ((src == NULL) || (src->children_num == 0))
     return 0;
 
-  temp =
-      realloc(dst->children,
-              sizeof(oconfig_item_t) * (dst->children_num + src->children_num));
+  temp = realloc(dst->children, sizeof(oconfig_item_t) *
+                                    (dst->children_num + src->children_num));
   if (temp == NULL) {
     ERROR("configfile: realloc failed.");
     return -1;
@@ -792,7 +810,7 @@ static oconfig_item_t *cf_read_generic(const char *path, const char *pattern,
 
   return root;
 } /* oconfig_item_t *cf_read_generic */
-/* #endif HAVE_WORDEXP_H */
+  /* #endif HAVE_WORDEXP_H */
 
 #else  /* if !HAVE_WORDEXP_H */
 static oconfig_item_t *cf_read_generic(const char *path, const char *pattern,
@@ -826,7 +844,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);
 
@@ -1035,16 +1053,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;
 
@@ -1056,21 +1070,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 */
@@ -1082,9 +1094,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;
   }
 
@@ -1099,9 +1109,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;
   }
 
@@ -1110,37 +1118,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;
@@ -1152,12 +1158,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;
@@ -1182,9 +1187,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;
   }
 
@@ -1194,11 +1197,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;
   }
 
@@ -1212,18 +1213,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;
@@ -1231,16 +1229,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);
@@ -1257,16 +1253,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..85f1b11 100644 (file)
  * DEALINGS IN THE SOFTWARE.
  **/
 
-#include "common.h"
+// clang-format off
+/*
+ * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris
+ * See Github Issue #3193 for details
+ */
+#include "utils/common/common.h"
 #include "globals.h"
+// clang-format on
 
 #if HAVE_KSTAT_H
 #include <kstat.h>
index 5a277c0..9ec72f0 100644 (file)
 #endif
 
 #ifndef PRIsz
+#ifdef WIN32
+#define PRIsz "Iu"
+#else
 #define PRIsz "zu"
-#endif /* PRIsz */
+#endif /* WIN32 */
+#endif /* !PRIsz */
 
 /* Type for time as used by "utils_time.h" */
 typedef uint64_t cdtime_t;
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 438366c..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;
   }
@@ -391,7 +400,7 @@ static int plugin_unregister(llist_t *list, const char *name) /* {{{ */
 
 /* 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) {
+static int plugin_load_file(char const *file, bool global) {
   int flags = RTLD_NOW;
   if (global)
     flags |= RTLD_GLOBAL;
@@ -618,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;
@@ -637,7 +646,7 @@ static void start_read_threads(size_t num) /* {{{ */
     }
 
     char name[THREAD_NAME_MAX];
-    snprintf(name, sizeof(name), "reader#%" PRIsz, read_threads_num);
+    snprintf(name, sizeof(name), "reader#%" PRIu64, (uint64_t)read_threads_num);
     set_thread_name(read_threads[read_threads_num], name);
 
     read_threads_num++;
@@ -710,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 */
@@ -824,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;
@@ -843,7 +835,8 @@ static void start_write_threads(size_t num) /* {{{ */
     }
 
     char name[THREAD_NAME_MAX];
-    snprintf(name, sizeof(name), "writer#%" PRIsz, write_threads_num);
+    snprintf(name, sizeof(name), "writer#%" PRIu64,
+             (uint64_t)write_threads_num);
     set_thread_name(write_threads[write_threads_num], name);
 
     write_threads_num++;
@@ -861,7 +854,7 @@ static void stop_write_threads(void) /* {{{ */
   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);
@@ -912,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)
@@ -954,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] = "";
@@ -988,13 +986,14 @@ 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;
   }
 
@@ -1047,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 */
 
@@ -1111,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;
   }
 
@@ -1141,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;
 
@@ -1159,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) {
@@ -1169,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;
 
@@ -1200,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) {
@@ -1211,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 */
 
@@ -1253,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();
 
@@ -1302,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 */
 
@@ -1330,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)) {
@@ -1359,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;
@@ -1422,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:");
 }
 
@@ -1434,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;
@@ -1484,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) {
@@ -1504,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)
@@ -1527,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;
@@ -1545,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);
   }
 
@@ -1634,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;
 
@@ -1686,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;
 
@@ -1714,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;
@@ -1725,6 +1732,7 @@ int plugin_write(const char *plugin, /* {{{ */
       else
         success++;
 
+      plugin_set_ctx(old_ctx);
       le = le->next;
     }
 
@@ -1761,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)
@@ -1791,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.
 
@@ -1857,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;
@@ -1890,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);
 
@@ -1910,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,
@@ -1994,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;
   }
@@ -2023,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;
@@ -2033,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) {
@@ -2054,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) {
@@ -2089,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 */
@@ -2162,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 */
 
@@ -2199,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;
@@ -2236,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;
 
@@ -2257,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)
@@ -2271,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;
   }
 
@@ -2330,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: {
@@ -2375,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);
 }
 
@@ -2458,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);
@@ -2479,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;
 
@@ -2499,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 1ccf10b..7eb3cb2 100644 (file)
@@ -26,7 +26,7 @@
 
 #include "collectd.h"
 
-#include "common.h"
+#include "utils/common/common.h"
 
 #include "configfile.h"
 #include "plugin.h"
@@ -96,8 +96,6 @@ static int parse_ds(data_source_t *dsrc, char *buf, size_t buf_len) {
 static void parse_line(char *buf) {
   char *fields[64];
   size_t fields_num;
-  data_set_t *ds;
-
   fields_num = strsplit(buf, fields, 64);
   if (fields_num < 2)
     return;
@@ -106,33 +104,27 @@ static void parse_line(char *buf) {
   if (fields[0][0] == '#')
     return;
 
-  ds = calloc(1, sizeof(*ds));
-  if (ds == NULL)
-    return;
+  data_set_t ds = {{0}};
 
-  sstrncpy(ds->type, fields[0], sizeof(ds->type));
+  sstrncpy(ds.type, fields[0], sizeof(ds.type));
 
-  ds->ds_num = fields_num - 1;
-  ds->ds = (data_source_t *)calloc(ds->ds_num, sizeof(data_source_t));
-  if (ds->ds == NULL) {
-    sfree(ds);
+  ds.ds_num = fields_num - 1;
+  ds.ds = calloc(ds.ds_num, sizeof(*ds.ds));
+  if (ds.ds == NULL)
     return;
-  }
 
-  for (size_t i = 0; i < ds->ds_num; i++)
-    if (parse_ds(ds->ds + i, fields[i + 1], strlen(fields[i + 1])) != 0) {
+  for (size_t i = 0; i < ds.ds_num; i++)
+    if (parse_ds(ds.ds + i, fields[i + 1], strlen(fields[i + 1])) != 0) {
       ERROR("types_list: parse_line: Cannot parse data source #%" PRIsz
             " of data set %s",
-            i, ds->type);
-      sfree(ds->ds);
-      sfree(ds);
+            i, ds.type);
+      sfree(ds.ds);
       return;
     }
 
-  plugin_register_data_set(ds);
+  plugin_register_data_set(&ds);
 
-  sfree(ds->ds);
-  sfree(ds);
+  sfree(ds.ds);
 } /* void parse_line */
 
 static void parse_file(FILE *fh) {
diff --git a/src/daemon/utils_avltree.c b/src/daemon/utils_avltree.c
deleted file mode 100644 (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 44b8840..e63a20e 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) {
@@ -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 (vl->meta != NULL) {
     ce->meta = meta_data_clone(vl->meta);
@@ -830,9 +830,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;
 
@@ -1001,7 +999,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,
@@ -1017,6 +1015,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 546ca18..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;
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 a807c7f..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
@@ -144,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;
 
@@ -167,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;
@@ -181,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 60324e8..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)
@@ -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); /* {{{ */
@@ -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 e1d5b51..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;
   }
@@ -225,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 45706bc..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)
@@ -142,10 +142,10 @@ static int pnumdisk;
 #error "No applicable input method."
 #endif
 
-#if HAVE_UDEV_H
+#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,13 +170,13 @@ 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.");
 #endif
   } else if (strcasecmp("UdevNameAttr", key) == 0) {
-#if HAVE_UDEV_H
+#if HAVE_LIBUDEV_H
     if (conf_udev_name_attr != NULL) {
       free(conf_udev_name_attr);
       conf_udev_name_attr = NULL;
@@ -212,7 +212,7 @@ static int disk_init(void) {
 /* #endif HAVE_IOKIT_IOKITLIB_H */
 
 #elif KERNEL_LINUX
-#if HAVE_UDEV_H
+#if HAVE_LIBUDEV_H
   if (conf_udev_name_attr != NULL) {
     handle_udev = udev_new();
     if (handle_udev == NULL) {
@@ -220,7 +220,7 @@ static int disk_init(void) {
       return -1;
     }
   }
-#endif /* HAVE_UDEV_H */
+#endif /* HAVE_LIBUDEV_H */
 /* #endif KERNEL_LINUX */
 
 #elif KERNEL_FREEBSD
@@ -263,10 +263,10 @@ static int disk_init(void) {
 
 static int disk_shutdown(void) {
 #if KERNEL_LINUX
-#if HAVE_UDEV_H
+#if HAVE_LIBUDEV_H
   if (handle_udev != NULL)
     udev_unref(handle_udev);
-#endif /* HAVE_UDEV_H */
+#endif /* HAVE_LIBUDEV_H */
 #endif /* KERNEL_LINUX */
   return 0;
 } /* int disk_shutdown */
@@ -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());
@@ -328,7 +328,7 @@ static counter_t disk_calc_time_incr(counter_t delta_time,
 }
 #endif
 
-#if HAVE_UDEV_H
+#if HAVE_LIBUDEV_H
 /**
  * Attempt to provide an rename disk instance from an assigned udev attribute.
  *
@@ -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,28 +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);
 
-    if ((numfields != (14 + fieldshift)) && (numfields != 7))
+    /* need either 7 fields (partition) or at least 14 fields */
+    if ((numfields != 7) && (numfields < 14))
       continue;
 
-    minor = atoll(fields[1]);
-
-    disk_name = fields[2 + fieldshift];
+    char *disk_name = fields[2];
 
     for (ds = disklist, pre_ds = disklist; ds != NULL;
          pre_ds = ds, ds = ds->next)
@@ -710,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) {
@@ -731,28 +722,24 @@ static int disk_read(void) {
       read_sectors = atoll(fields[4]);
       write_ops = atoll(fields[5]);
       write_sectors = atoll(fields[6]);
-    } else if (numfields == (14 + fieldshift)) {
-      read_ops = atoll(fields[3 + fieldshift]);
-      write_ops = atoll(fields[7 + fieldshift]);
+    } else {
+      assert(numfields >= 14);
+      read_ops = atoll(fields[3]);
+      write_ops = atoll(fields[7]);
 
-      read_sectors = atoll(fields[5 + fieldshift]);
-      write_sectors = atoll(fields[9 + fieldshift]);
+      read_sectors = atoll(fields[5]);
+      write_sectors = atoll(fields[9]);
 
-      if ((fieldshift == 0) || (minor == 0)) {
-        is_disk = 1;
-        read_merged = atoll(fields[4 + fieldshift]);
-        read_time = atoll(fields[6 + fieldshift]);
-        write_merged = atoll(fields[8 + fieldshift]);
-        write_time = atoll(fields[10 + fieldshift]);
+      is_disk = 1;
+      read_merged = atoll(fields[4]);
+      read_time = atoll(fields[6]);
+      write_merged = atoll(fields[8]);
+      write_time = atoll(fields[10]);
 
-        in_progress = atof(fields[11 + fieldshift]);
+      in_progress = atof(fields[11]);
 
-        io_time = atof(fields[12 + fieldshift]);
-        weighted_time = atof(fields[13 + fieldshift]);
-      }
-    } else {
-      DEBUG("numfields = %i; => unknown file format.", numfields);
-      continue;
+      io_time = atof(fields[12]);
+      weighted_time = atof(fields[13]);
     }
 
     {
@@ -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,9 +828,9 @@ static int disk_read(void) {
       continue;
     }
 
-    output_name = disk_name;
+    char *output_name = disk_name;
 
-#if HAVE_UDEV_H
+#if HAVE_LIBUDEV_H
     char *alt_name = NULL;
     if (conf_udev_name_attr != NULL) {
       alt_name =
@@ -855,7 +841,7 @@ static int disk_read(void) {
 #endif
 
     if (ignorelist_match(ignorelist, output_name) != 0) {
-#if HAVE_UDEV_H
+#if HAVE_LIBUDEV_H
       /* release udev-based alternate name, if allocated */
       sfree(alt_name);
 #endif
@@ -881,12 +867,34 @@ static int disk_read(void) {
         submit_io_time(output_name, io_time, weighted_time);
     } /* if (is_disk) */
 
-#if HAVE_UDEV_H
+#if HAVE_LIBUDEV_H
     /* release udev-based alternate name, if allocated */
     sfree(alt_name);
 #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) */
 
@@ -981,9 +989,8 @@ static int disk_read(void) {
   }
 
   if (numdisk != pnumdisk || stat_disk == NULL) {
-    if (stat_disk != NULL)
-      free(stat_disk);
-    stat_disk = (perfstat_disk_t *)calloc(numdisk, sizeof(perfstat_disk_t));
+    free(stat_disk);
+    stat_disk = calloc(numdisk, sizeof(*stat_disk));
   }
   pnumdisk = numdisk;
 
index 3ab456b..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.h"
+#include "utils/dns/dns.h"
 #include <poll.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;
index 064dce1..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;
 
@@ -185,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;
@@ -194,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));
@@ -419,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");
@@ -428,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();
@@ -497,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 59ab976..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>
index 69dc4ef..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[] = {
index d94c542..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,7 +260,6 @@ 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) {
@@ -272,7 +271,7 @@ static void *collect(void *arg) {
         break;
       }
 
-      len = strlen(line);
+      size_t len = strlen(line);
       if ((line[len - 1] != '\n') && (line[len - 1] != '\r')) {
         log_warn("collect: line too long (> %" PRIsz " characters): "
                  "'%s' (truncated)",
@@ -287,7 +286,7 @@ static void *collect(void *arg) {
         continue;
       }
 
-      line[len - 1] = 0;
+      line[len - 1] = '\0';
 
       log_debug("collect: line = '%s'", line);
 
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 c5b02d3..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) /* {{{ */
 {
index 36f2d4d..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>
@@ -78,9 +78,14 @@ typedef struct program_list_and_notification_s {
 } program_list_and_notification_t;
 
 /*
+ * constants
+ */
+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;
 
 /*
@@ -240,11 +245,17 @@ static int exec_config(oconfig_item_t *ci) /* {{{ */
   return 0;
 } /* int exec_config }}} */
 
+#if !defined(HAVE_SETENV)
+static char env_interval[64];
+// max hostname len is 255, so this should be enough
+static char env_hostname[300];
+#endif
+
 static void set_environment(void) /* {{{ */
 {
+#ifdef HAVE_SETENV
   char buffer[1024];
 
-#ifdef HAVE_SETENV
   snprintf(buffer, sizeof(buffer), "%.3f",
            CDTIME_T_TO_DOUBLE(plugin_get_interval()));
   setenv("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
@@ -252,15 +263,29 @@ static void set_environment(void) /* {{{ */
   sstrncpy(buffer, hostname_g, sizeof(buffer));
   setenv("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
 #else
-  snprintf(buffer, sizeof(buffer), "COLLECTD_INTERVAL=%.3f",
+  snprintf(env_interval, sizeof(env_interval), "COLLECTD_INTERVAL=%.3f",
            CDTIME_T_TO_DOUBLE(plugin_get_interval()));
-  putenv(buffer);
+  putenv(env_interval);
 
-  snprintf(buffer, sizeof(buffer), "COLLECTD_HOSTNAME=%s", hostname_g);
-  putenv(buffer);
+  snprintf(env_hostname, sizeof(env_hostname), "COLLECTD_HOSTNAME=%s",
+           hostname_g);
+  putenv(env_hostname);
 #endif
 } /* }}} void set_environment */
 
+static void unset_environment(void) /* {{{ */
+{
+#ifdef HAVE_SETENV
+  unsetenv("COLLECTD_INTERVAL");
+  unsetenv("COLLECTD_HOSTNAME");
+#else
+  snprintf(env_interval, sizeof(env_interval), "COLLECTD_INTERVAL");
+  putenv(env_interval);
+  snprintf(env_hostname, sizeof(env_hostname), "COLLECTD_HOSTNAME");
+  putenv(env_hostname);
+#endif
+} /* }}} void unset_environment */
+
 __attribute__((noreturn)) static void exec_child(program_list_t *pl, int uid,
                                                  int gid, int egid) /* {{{ */
 {
@@ -340,6 +365,65 @@ static void close_pipe(int fd_pipe[2]) /* {{{ */
 } /* }}} void close_pipe */
 
 /*
+ * Get effective group ID from group name.
+ * Input arguments:
+ *       pl  :program list struct with group name
+ *       gid :group id to fallback in case egid cannot be determined.
+ * Returns:
+ *       egid effective group id if successfull,
+ *            -1 if group is not defined/not found.
+ *            -2 for any buffer allocation error.
+ */
+static int getegr_id(program_list_t *pl, int gid) /* {{{ */
+{
+  if (pl->group == NULL) {
+    return -1;
+  }
+  if (strcmp(pl->group, "") == 0) {
+    return gid;
+  }
+  struct group *gr_ptr = NULL;
+  struct group gr;
+
+  long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
+  if (grbuf_size <= 0)
+    grbuf_size = sysconf(_SC_PAGESIZE);
+  if (grbuf_size <= 0)
+    grbuf_size = 4096;
+
+  char *temp = NULL;
+  char *grbuf = NULL;
+
+  do {
+    temp = realloc(grbuf, grbuf_size);
+    if (temp == NULL) {
+      ERROR("exec plugin: getegr_id for %s: realloc buffer[%ld] failed ",
+            pl->group, grbuf_size);
+      sfree(grbuf);
+      return -2;
+    }
+    grbuf = temp;
+    if (getgrnam_r(pl->group, &gr, grbuf, grbuf_size, &gr_ptr) == 0) {
+      sfree(grbuf);
+      if (gr_ptr == NULL) {
+        ERROR("exec plugin: No such group: `%s'", pl->group);
+        return -1;
+      }
+      return gr.gr_gid;
+    } else if (errno == ERANGE) {
+      grbuf_size += grbuf_size; // increment buffer size and try again
+    } else {
+      ERROR("exec plugin: getegr_id failed %s", STRERRNO);
+      sfree(grbuf);
+      return -2;
+    }
+  } while (grbuf_size <= MAX_GRBUF_SIZE);
+  ERROR("exec plugin: getegr_id Max grbuf size reached  for %s", pl->group);
+  sfree(grbuf);
+  return -2;
+}
+
+/*
  * Creates three pipes (one for reading, one for writing and one for errors),
  * forks a child, sets up the pipes so that fd_in is connected to STDIN of
  * the child and fd_out is connected to STDOUT and fd_err is connected to STDERR
@@ -397,36 +481,12 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
 
   /* The group configured in the configfile is set as effective group, because
    * this way the forked process can (re-)gain the user's primary group. */
-  egid = -1;
-  if (pl->group != NULL) {
-    if (*pl->group != '\0') {
-      struct group *gr_ptr = NULL;
-      struct group gr;
-
-      long int grbuf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
-      if (grbuf_size <= 0)
-        grbuf_size = sysconf(_SC_PAGESIZE);
-      if (grbuf_size <= 0)
-        grbuf_size = 4096;
-      char grbuf[grbuf_size];
-
-      status = getgrnam_r(pl->group, &gr, grbuf, sizeof(grbuf), &gr_ptr);
-      if (status != 0) {
-        ERROR("exec plugin: Failed to get group information "
-              "for group ``%s'': %s",
-              pl->group, STRERROR(status));
-        goto failed;
-      }
-      if (gr_ptr == NULL) {
-        ERROR("exec plugin: No such group: `%s'", pl->group);
-        goto failed;
-      }
+  egid = getegr_id(pl, gid);
+  if (egid == -2) {
+    goto failed;
+  }
 
-      egid = gr.gr_gid;
-    } else {
-      egid = gid;
-    }
-  } /* if (pl->group == NULL) */
+  set_environment();
 
   pid = fork();
   if (pid < 0) {
@@ -462,8 +522,6 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
       close(fd_pipe_err[1]);
     }
 
-    set_environment();
-
     /* Unblock all signals */
     reset_signal_mask();
 
@@ -471,6 +529,8 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
     /* does not return */
   }
 
+  unset_environment();
+
   close(fd_pipe_in[0]);
   close(fd_pipe_out[1]);
   close(fd_pipe_err[1]);
@@ -493,6 +553,8 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
   return pid;
 
 failed:
+  unset_environment();
+
   close_pipe(fd_pipe_in);
   close_pipe(fd_pipe_out);
   close_pipe(fd_pipe_err);
index 97f0438..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;
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 cb005fd..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;
@@ -252,10 +252,8 @@ 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) {
         WARNING("gmond plugin: setsockopt(2) failed: %s", STRERRNO);
       }
@@ -415,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;
@@ -745,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++)
@@ -833,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;
@@ -889,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 1d32d04..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
@@ -141,7 +141,12 @@ static void *cgps_thread(void *pData) {
         continue;
       }
 
-      if (gps_read(&gpsd_conn) == -1) {
+#if GPSD_API_MAJOR_VERSION > 6
+      if (gps_read(&gpsd_conn, NULL, 0) == -1)
+#else
+      if (gps_read(&gpsd_conn) == -1)
+#endif
+      {
         WARNING("gps plugin: incorrect data! (err_count: %d)", err_count);
         err_count++;
 
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 977c5b2..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"
 }
@@ -664,7 +664,7 @@ static int c_grpc_config_listen(oconfig_item_t *ci) {
       }
       pkcp.cert_chain = read_file(cert);
     } else if (!strcasecmp("VerifyPeer", child->key)) {
-      _Bool verify = 0;
+      bool verify = false;
       if (cf_util_get_boolean(child, &verify)) {
         return -1;
       }
index 36c4128..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];
 
 /*
index 29a7f9e..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);
   }
 }
 
index d868c89..f04f887 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/intel_pmu.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:
  *   Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ *   Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
  **/
 
 #include "collectd.h"
-#include "common.h"
+#include "utils/common/common.h"
+
+#include "utils/config_cores/config_cores.h"
 
 #include <jevents.h>
 #include <jsession.h>
@@ -64,12 +67,13 @@ 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;
+  core_groups_list_t cores;
   struct eventlist *event_list;
 };
 typedef struct intel_pmu_ctx_s intel_pmu_ctx_t;
@@ -197,8 +201,61 @@ static void pmu_dump_config(void) {
   }
 }
 
+static void pmu_dump_cgroups(void) {
+
+  DEBUG(PMU_PLUGIN ": Core groups:");
+
+  for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
+    core_group_t *cgroup = g_ctx.cores.cgroups + i;
+    const size_t cores_size = cgroup->num_cores * 4 + 1;
+    char *cores = calloc(cores_size, sizeof(*cores));
+    if (cores == NULL) {
+      DEBUG(PMU_PLUGIN ": Failed to allocate string to list cores.");
+      return;
+    }
+    for (size_t j = 0; j < cgroup->num_cores; j++)
+      if (snprintf(cores + strlen(cores), cores_size - strlen(cores), " %d",
+                   cgroup->cores[j]) < 0) {
+        DEBUG(PMU_PLUGIN ": Failed to write list of cores to string.");
+        sfree(cores);
+        return;
+      }
+
+    DEBUG(PMU_PLUGIN ":   group[%" PRIsz "]", i);
+    DEBUG(PMU_PLUGIN ":     description: %s", cgroup->desc);
+    DEBUG(PMU_PLUGIN ":     cores count: %" PRIsz, cgroup->num_cores);
+    DEBUG(PMU_PLUGIN ":     cores      :%s", cores);
+    sfree(cores);
+  }
+}
+
 #endif /* COLLECT_DEBUG */
 
+static int pmu_validate_cgroups(core_group_t *cgroups, size_t len,
+                                int max_cores) {
+  /* i - group index, j - core index */
+  for (size_t i = 0; i < len; i++) {
+    for (size_t j = 0; j < cgroups[i].num_cores; j++) {
+      int core = (int)cgroups[i].cores[j];
+
+      /* Core index cannot exceed number of cores in system,
+         note that max_cores include both online and offline CPUs. */
+      if (core >= max_cores) {
+        ERROR(PMU_PLUGIN ": Core %d is not valid, max core index: %d.", core,
+              max_cores - 1);
+        return -1;
+      }
+    }
+    /* Check if cores are set in remaining groups */
+    for (size_t k = i + 1; k < len; k++)
+      if (config_cores_cmp_cgroups(&cgroups[i], &cgroups[k]) != 0) {
+        ERROR(PMU_PLUGIN ": Same cores cannot be set in different groups.");
+        return -1;
+      }
+  }
+  return 0;
+}
+
 static int pmu_config_hw_events(oconfig_item_t *ci) {
 
   if (strcasecmp("HardwareEvents", ci->key) != 0) {
@@ -210,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;
@@ -253,6 +310,8 @@ static int pmu_config(oconfig_item_t *ci) {
       ret = pmu_config_hw_events(child);
     } else if (strcasecmp("ReportSoftwareEvents", child->key) == 0) {
       ret = cf_util_get_boolean(child, &g_ctx.sw_events);
+    } else if (strcasecmp("Cores", child->key) == 0) {
+      ret = config_cores_parse(child, &g_ctx.cores);
     } else {
       ERROR(PMU_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
       ret = -1;
@@ -271,20 +330,17 @@ static int pmu_config(oconfig_item_t *ci) {
   return 0;
 }
 
-static void pmu_submit_counter(int cpu, char *event, counter_t value,
-                               meta_data_t *meta) {
+static void pmu_submit_counter(const char *cgroup, const char *event,
+                               counter_t value, meta_data_t *meta) {
   value_list_t vl = VALUE_LIST_INIT;
 
   vl.values = &(value_t){.counter = value};
   vl.values_len = 1;
 
   sstrncpy(vl.plugin, PMU_PLUGIN, sizeof(vl.plugin));
-  if (cpu == -1) {
-    snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "all");
-  } else {
+  sstrncpy(vl.plugin_instance, cgroup, sizeof(vl.plugin_instance));
+  if (meta)
     vl.meta = meta;
-    snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%d", cpu);
-  }
   sstrncpy(vl.type, "counter", sizeof(vl.type));
   sstrncpy(vl.type_instance, event, sizeof(vl.type_instance));
 
@@ -317,49 +373,65 @@ static void pmu_dispatch_data(void) {
   struct event *e;
 
   for (e = g_ctx.event_list->eventlist; e; e = e->next) {
-    uint64_t all_value = 0;
-    int event_enabled = 0;
-    for (int i = 0; i < g_ctx.event_list->num_cpus; i++) {
-
-      if (e->efd[i].fd < 0)
-        continue;
-
-      event_enabled++;
-
-      /* If there are more events than counters, the kernel uses time
-       * multiplexing. With multiplexing, at the end of the run,
-       * the counter is scaled basing on total time enabled vs time running.
-       * final_count = raw_count * time_enabled/time_running
-       */
-      uint64_t value = event_scaled_value(e, i);
-      all_value += value;
-
-      /* get meta data with information about scaling */
-      meta_data_t *meta = pmu_meta_data_create(&e->efd[i]);
-
-      /* dispatch per CPU value */
-      pmu_submit_counter(i, e->event, value, meta);
-
-      meta_data_destroy(meta);
-    }
+    for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
+      core_group_t *cgroup = g_ctx.cores.cgroups + i;
+      uint64_t cgroup_value = 0;
+      int event_enabled_cgroup = 0;
+      meta_data_t *meta = NULL;
+
+      for (size_t j = 0; j < cgroup->num_cores; j++) {
+        int core = (int)cgroup->cores[j];
+        if (e->efd[core].fd < 0)
+          continue;
+
+        event_enabled_cgroup++;
+
+        /* If there are more events than counters, the kernel uses time
+         * multiplexing. With multiplexing, at the end of the run,
+         * the counter is scaled basing on total time enabled vs time running.
+         * final_count = raw_count * time_enabled/time_running
+         */
+        uint64_t value = event_scaled_value(e, core);
+        cgroup_value += value;
+
+        /* get meta data with information about scaling */
+        if (cgroup->num_cores == 1)
+          meta = pmu_meta_data_create(&e->efd[core]);
+      }
 
-    if (event_enabled > 0) {
-      DEBUG(PMU_PLUGIN ": %-20s %'10lu", e->event, all_value);
-      /* dispatch all CPU value */
-      pmu_submit_counter(-1, e->event, all_value, NULL);
+      if (event_enabled_cgroup > 0) {
+        DEBUG(PMU_PLUGIN ": %s/%s = %lu", e->event, cgroup->desc, cgroup_value);
+        /* dispatch per core group value */
+        pmu_submit_counter(cgroup->desc, e->event, cgroup_value, meta);
+        meta_data_destroy(meta);
+      }
     }
   }
 }
 
 static int pmu_read(__attribute__((unused)) user_data_t *ud) {
   int ret;
+  struct event *e;
 
   DEBUG(PMU_PLUGIN ": %s:%d", __FUNCTION__, __LINE__);
 
-  ret = read_all_events(g_ctx.event_list);
-  if (ret != 0) {
-    ERROR(PMU_PLUGIN ": Failed to read values of all events.");
-    return ret;
+  /* read all events only for configured cores */
+  for (e = g_ctx.event_list->eventlist; e; e = e->next) {
+    for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
+      core_group_t *cgroup = g_ctx.cores.cgroups + i;
+      for (size_t j = 0; j < cgroup->num_cores; j++) {
+        int core = (int)cgroup->cores[j];
+        if (e->efd[core].fd < 0)
+          continue;
+
+        ret = read_event(e, core);
+        if (ret != 0) {
+          ERROR(PMU_PLUGIN ": Failed to read value of %s/%d event.", e->event,
+                core);
+          return ret;
+        }
+      }
+    }
   }
 
   pmu_dispatch_data();
@@ -374,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;
@@ -404,13 +476,13 @@ static int pmu_add_hw_events(struct eventlist *el, char **e, size_t count) {
     if (!events)
       return -1;
 
-    char *s, *tmp;
+    char *s, *tmp = NULL;
     for (s = strtok_r(events, ",", &tmp); s; s = strtok_r(NULL, ",", &tmp)) {
 
       /* 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;
@@ -460,6 +532,7 @@ static void pmu_free_events(struct eventlist *el) {
 
   while (e) {
     struct event *next = e->next;
+    sfree(e->event);
     sfree(e);
     e = next;
   }
@@ -474,13 +547,18 @@ static int pmu_setup_events(struct eventlist *el, bool measure_all,
 
   for (e = el->eventlist; e; e = e->next) {
 
-    for (int i = 0; i < el->num_cpus; i++) {
-      if (setup_event(e, i, leader, measure_all, measure_pid) < 0) {
-        WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).",
-                e->event, i);
-      } else {
-        /* success if at least one event was set */
-        ret = 0;
+    for (size_t i = 0; i < g_ctx.cores.num_cgroups; i++) {
+      core_group_t *cgroup = g_ctx.cores.cgroups + i;
+      for (size_t j = 0; j < cgroup->num_cores; j++) {
+        int core = (int)cgroup->cores[j];
+
+        if (setup_event(e, core, leader, measure_all, measure_pid) < 0) {
+          WARNING(PMU_PLUGIN ": perf event '%s' is not available (cpu=%d).",
+                  e->event, core);
+        } else {
+          /* success if at least one event was set */
+          ret = 0;
+        }
       }
     }
 
@@ -504,6 +582,24 @@ static int pmu_init(void) {
     return -ENOMEM;
   }
 
+  if (g_ctx.cores.num_cgroups == 0) {
+    ret = config_cores_default(g_ctx.event_list->num_cpus, &g_ctx.cores);
+    if (ret != 0) {
+      ERROR(PMU_PLUGIN ": Failed to set default core groups.");
+      goto init_error;
+    }
+  } else {
+    ret = pmu_validate_cgroups(g_ctx.cores.cgroups, g_ctx.cores.num_cgroups,
+                               g_ctx.event_list->num_cpus);
+    if (ret != 0) {
+      ERROR(PMU_PLUGIN ": Invalid core groups configuration.");
+      goto init_error;
+    }
+  }
+#if COLLECT_DEBUG
+  pmu_dump_cgroups();
+#endif
+
   if (g_ctx.hw_cache_events) {
     ret =
         pmu_add_events(g_ctx.event_list, PERF_TYPE_HW_CACHE, g_hw_cache_events,
@@ -579,6 +675,8 @@ init_error:
   sfree(g_ctx.hw_events);
   g_ctx.hw_events_count = 0;
 
+  config_cores_cleanup(&g_ctx.cores);
+
   return ret;
 }
 
@@ -594,6 +692,8 @@ static int pmu_shutdown(void) {
   sfree(g_ctx.hw_events);
   g_ctx.hw_events_count = 0;
 
+  config_cores_cleanup(&g_ctx.cores);
+
   return 0;
 }
 
index 29c02fe..a68620e 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/intel_rdt.c
  *
- * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ * Copyright(c) 2016-2019 Intel Corporation. All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  *
  * Authors:
  *   Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ *   Starzyk, Mateusz <mateuszx.starzyk@intel.com>
+ *   Wojciech Andralojc <wojciechx.andralojc@intel.com>
+ *   Michał Aleksiński <michalx.aleksinski@intel.com>
  **/
 
 #include "collectd.h"
-#include "common.h"
-
+#include "utils/common/common.h"
+#include "utils/config_cores/config_cores.h"
+#include "utils/proc_pids/proc_pids.h"
 #include <pqos.h>
 
 #define RDT_PLUGIN "intel_rdt"
 
+/* libpqos v2.0 or newer is required for process monitoring*/
+#undef LIBPQOS2
+#if defined(PQOS_VERSION) && PQOS_VERSION >= 20000
+#define LIBPQOS2
+#endif
+
+#define RDT_PLUGIN "intel_rdt"
+
 #define RDT_MAX_SOCKETS 8
 #define RDT_MAX_SOCKET_CORES 64
 #define RDT_MAX_CORES (RDT_MAX_SOCKET_CORES * RDT_MAX_SOCKETS)
 
+#ifdef LIBPQOS2
+/*
+ * Process name inside comm file is limited to 16 chars.
+ * More info here: http://man7.org/linux/man-pages/man5/proc.5.html
+ */
+#define RDT_MAX_NAMES_GROUPS 64
+#define RDT_PROC_PATH "/proc"
+#endif /* LIBPQOS2 */
+
 typedef enum {
   UNKNOWN = 0,
   CONFIGURATION_ERROR,
 } rdt_config_status;
 
-struct rdt_core_group_s {
+#ifdef LIBPQOS2
+struct rdt_name_group_s {
   char *desc;
-  size_t num_cores;
-  unsigned *cores;
+  size_t num_names;
+  char **names;
+  proc_pids_t **proc_pids;
+  size_t monitored_pids_count;
   enum pqos_mon_event events;
 };
-typedef struct rdt_core_group_s rdt_core_group_t;
+typedef struct rdt_name_group_s rdt_name_group_t;
+#endif /* LIBPQOS2 */
 
 struct rdt_ctx_s {
-  rdt_core_group_t cgroups[RDT_MAX_CORES];
-  struct pqos_mon_data *pgroups[RDT_MAX_CORES];
-  size_t num_groups;
+  core_groups_list_t cores;
+  enum pqos_mon_event events[RDT_MAX_CORES];
+  struct pqos_mon_data *pcgroups[RDT_MAX_CORES];
+#ifdef LIBPQOS2
+  rdt_name_group_t ngroups[RDT_MAX_NAMES_GROUPS];
+  struct pqos_mon_data *pngroups[RDT_MAX_NAMES_GROUPS];
+  size_t num_ngroups;
+  proc_pids_t **proc_pids;
+  size_t num_proc_pids;
+#endif /* LIBPQOS2 */
   const struct pqos_cpuinfo *pqos_cpu;
   const struct pqos_cap *pqos_cap;
   const struct pqos_capability *cap_mon;
 };
 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;
 
-static int isdup(const uint64_t *nums, size_t size, uint64_t val) {
-  for (size_t i = 0; i < size; i++)
-    if (nums[i] == val)
-      return 1;
-  return 0;
+static int g_interface = -1;
+
+static void rdt_submit_derive(const char *cgroup, const char *type,
+                              const char *type_instance, derive_t value) {
+  value_list_t vl = VALUE_LIST_INIT;
+
+  vl.values = &(value_t){.derive = value};
+  vl.values_len = 1;
+
+  sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin));
+  snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup);
+  sstrncpy(vl.type, type, sizeof(vl.type));
+  if (type_instance)
+    sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+
+  plugin_dispatch_values(&vl);
 }
 
-static int strtouint64(const char *s, uint64_t *n) {
-  char *endptr = NULL;
+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;
 
-  assert(s != NULL);
-  assert(n != NULL);
+  vl.values = &(value_t){.gauge = value};
+  vl.values_len = 1;
 
-  *n = strtoull(s, &endptr, 0);
+  sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin));
+  snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup);
+  sstrncpy(vl.type, type, sizeof(vl.type));
+  if (type_instance)
+    sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
 
-  if (!(*s != '\0' && *endptr == '\0')) {
-    DEBUG(RDT_PLUGIN ": Error converting '%s' to unsigned number.", s);
-    return -EINVAL;
+  plugin_dispatch_values(&vl);
+}
+
+#if COLLECT_DEBUG
+static void rdt_dump_cgroups(void) {
+  char cores[RDT_MAX_CORES * 4];
+
+  if (g_rdt == NULL)
+    return;
+
+  DEBUG(RDT_PLUGIN ": Core Groups Dump");
+  DEBUG(RDT_PLUGIN ":  groups count: %" PRIsz, g_rdt->cores.num_cgroups);
+
+  for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) {
+    core_group_t *cgroup = g_rdt->cores.cgroups + i;
+
+    memset(cores, 0, sizeof(cores));
+    for (size_t j = 0; j < cgroup->num_cores; j++) {
+      snprintf(cores + strlen(cores), sizeof(cores) - strlen(cores) - 1, " %d",
+               cgroup->cores[j]);
+    }
+
+    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]);
   }
 
+  return;
+}
+
+#ifdef LIBPQOS2
+static void rdt_dump_ngroups(void) {
+
+  char names[DATA_MAX_NAME_LEN];
+
+  if (g_rdt == NULL)
+    return;
+
+  DEBUG(RDT_PLUGIN ": Process Names Groups Dump");
+  DEBUG(RDT_PLUGIN ":  groups count: %" PRIsz, g_rdt->num_ngroups);
+
+  for (size_t i = 0; i < g_rdt->num_ngroups; i++) {
+    memset(names, 0, sizeof(names));
+    for (size_t j = 0; j < g_rdt->ngroups[i].num_names; j++)
+      snprintf(names + strlen(names), sizeof(names) - strlen(names) - 1, " %s",
+               g_rdt->ngroups[i].names[j]);
+
+    DEBUG(RDT_PLUGIN ":  group[%d]:", (int)i);
+    DEBUG(RDT_PLUGIN ":    description: %s", g_rdt->ngroups[i].desc);
+    DEBUG(RDT_PLUGIN ":    process names:%s", names);
+    DEBUG(RDT_PLUGIN ":    events: 0x%X", g_rdt->ngroups[i].events);
+  }
+
+  return;
+}
+#endif /* LIBPQOS2 */
+
+static inline double bytes_to_kb(const double bytes) { return bytes / 1024.0; }
+
+static inline double bytes_to_mb(const double bytes) {
+  return bytes / (1024.0 * 1024.0);
+}
+
+static void rdt_dump_cores_data(void) {
+/*
+ * CORE - monitored group of cores
+ * RMID - Resource Monitoring ID associated with the monitored group
+ *        This is not available for monitoring with resource control
+ * LLC - last level cache occupancy
+ * MBL - local memory bandwidth
+ * MBR - remote memory bandwidth
+ */
+#ifdef LIBPQOS2
+  if (g_interface == PQOS_INTER_OS_RESCTRL_MON) {
+    DEBUG(RDT_PLUGIN ":  CORE     LLC[KB]   MBL[MB]    MBR[MB]");
+  } else {
+    DEBUG(RDT_PLUGIN ":  CORE     RMID    LLC[KB]   MBL[MB]    MBR[MB]");
+  }
+#else
+  DEBUG(RDT_PLUGIN ":  CORE     RMID    LLC[KB]   MBL[MB]    MBR[MB]");
+#endif /* LIBPQOS2 */
+
+  for (int i = 0; i < g_rdt->cores.num_cgroups; i++) {
+    const struct pqos_event_values *pv = &g_rdt->pcgroups[i]->values;
+
+    double llc = bytes_to_kb(pv->llc);
+    double mbr = bytes_to_mb(pv->mbm_remote_delta);
+    double mbl = bytes_to_mb(pv->mbm_local_delta);
+#ifdef LIBPQOS2
+    if (g_interface == PQOS_INTER_OS_RESCTRL_MON) {
+      DEBUG(RDT_PLUGIN ": [%s] %10.1f %10.1f %10.1f",
+            g_rdt->cores.cgroups[i].desc, llc, mbl, mbr);
+    } else {
+      DEBUG(RDT_PLUGIN ": [%s] %8u %10.1f %10.1f %10.1f",
+            g_rdt->cores.cgroups[i].desc, g_rdt->pcgroups[i]->poll_ctx[0].rmid,
+            llc, mbl, mbr);
+    }
+#else
+    DEBUG(RDT_PLUGIN ": [%s] %8u %10.1f %10.1f %10.1f",
+          g_rdt->cores.cgroups[i].desc, g_rdt->pcgroups[i]->poll_ctx[0].rmid,
+          llc, mbl, mbr);
+#endif /* LIBPQOS2 */
+  }
+}
+
+#ifdef LIBPQOS2
+static void rdt_dump_pids_data(void) {
+  /*
+   * NAME - monitored group of processes
+   * PIDs - list of PID numbers in the NAME group
+   * LLC - last level cache occupancy
+   * MBL - local memory bandwidth
+   * MBR - remote memory bandwidth
+   */
+
+  DEBUG(RDT_PLUGIN ":  NAME     PIDs");
+  char pids[DATA_MAX_NAME_LEN];
+  for (size_t i = 0; i < g_rdt->num_ngroups; ++i) {
+    memset(pids, 0, sizeof(pids));
+    for (size_t j = 0; j < g_rdt->ngroups[i].num_names; ++j) {
+      pids_list_t *list = g_rdt->ngroups[i].proc_pids[j]->curr;
+      for (size_t k = 0; k < list->size; k++)
+        snprintf(pids + strlen(pids), sizeof(pids) - strlen(pids) - 1, " %u",
+                 list->pids[k]);
+    }
+    DEBUG(RDT_PLUGIN ":  [%s] %s", g_rdt->ngroups[i].desc, pids);
+  }
+
+  DEBUG(RDT_PLUGIN ":  NAME    LLC[KB]   MBL[MB]    MBR[MB]");
+  for (size_t i = 0; i < g_rdt->num_ngroups; i++) {
+
+    const struct pqos_event_values *pv = &g_rdt->pngroups[i]->values;
+
+    double llc = bytes_to_kb(pv->llc);
+    double mbr = bytes_to_mb(pv->mbm_remote_delta);
+    double mbl = bytes_to_mb(pv->mbm_local_delta);
+
+    DEBUG(RDT_PLUGIN ":  [%s] %10.1f %10.1f %10.1f", g_rdt->ngroups[i].desc,
+          llc, mbl, mbr);
+  }
+}
+#endif /* LIBPQOS2 */
+#endif /* COLLECT_DEBUG */
+
+#ifdef LIBPQOS2
+static int isdupstr(const char *names[], const size_t size, const char *name) {
+  for (size_t i = 0; i < size; i++)
+    if (strncmp(names[i], name, (size_t)MAX_PROC_NAME_LEN) == 0)
+      return 1;
+
   return 0;
 }
 
 /*
  * NAME
- *   strlisttonums
+ *   strlisttoarray
  *
  * 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.
+ *   Converts string representing list of strings into array of strings.
+ *   Allowed format is:
+ *     name,name1,name2,name3
  *
  * PARAMETERS
- *   `s'         String representing list of unsigned numbers.
- *   `nums'      Array to put converted numeric values into.
- *   `max'       Maximum number of elements that nums can accommodate.
+ *   `str_list'  String representing list of strings.
+ *   `names'     Array to put extracted strings into.
+ *   `names_num' Variable to put number of extracted strings.
  *
  * RETURN VALUE
- *    Number of elements placed into nums.
+ *    Number of elements placed into names.
  */
-static size_t strlisttonums(char *s, uint64_t *nums, size_t max) {
-  int ret;
-  size_t index = 0;
+static int strlisttoarray(char *str_list, char ***names, size_t *names_num) {
   char *saveptr = NULL;
 
-  if (s == NULL || nums == NULL || max == 0)
-    return index;
+  if (str_list == NULL || names == NULL)
+    return -EINVAL;
 
-  for (;;) {
-    char *p = NULL;
-    char *token = NULL;
+  if (strstr(str_list, ",,")) {
+    /* strtok ignores empty words between separators.
+     * This condition handles that by rejecting strings
+     * with consecutive seprators */
+    ERROR(RDT_PLUGIN ": Empty process name");
+    return -EINVAL;
+  }
 
-    token = strtok_r(s, ",", &saveptr);
+  for (;;) {
+    char *token = strtok_r(str_list, ",", &saveptr);
     if (token == NULL)
       break;
 
-    s = NULL;
+    str_list = NULL;
 
     while (isspace(*token))
       token++;
+
     if (*token == '\0')
       continue;
 
-    p = strchr(token, '-');
-    if (p != NULL) {
-      uint64_t n, start, end;
-      *p = '\0';
-      ret = strtouint64(token, &start);
-      if (ret < 0)
-        return 0;
-      ret = strtouint64(p + 1, &end);
-      if (ret < 0)
-        return 0;
-      if (start > end) {
-        return 0;
-      }
-      for (n = start; n <= end; n++) {
-        if (!(isdup(nums, index, n))) {
-          nums[index] = n;
-          index++;
-        }
-        if (index >= max)
-          return index;
-      }
+    if ((isdupstr((const char **)*names, *names_num, token))) {
+      ERROR(RDT_PLUGIN ": Duplicated process name \'%s\' in group \'%s\'",
+            token, str_list);
+      return -EINVAL;
     } else {
-      uint64_t val;
-
-      ret = strtouint64(token, &val);
-      if (ret < 0)
-        return 0;
-
-      if (!(isdup(nums, index, val))) {
-        nums[index] = val;
-        index++;
+      if (0 != strarray_add(names, names_num, token)) {
+        ERROR(RDT_PLUGIN ": Error allocating process name string");
+        return -ENOMEM;
       }
-      if (index >= max)
-        return index;
     }
   }
 
-  return index;
+  return 0;
 }
 
 /*
  * NAME
- *   cgroup_cmp
+ *   ngroup_cmp
  *
  * DESCRIPTION
- *   Function to compare cores in 2 core groups.
+ *   Function to compare names in two name groups.
  *
  * PARAMETERS
- *   `cg_a'      Pointer to core group a.
- *   `cg_b'      Pointer to core group b.
+ *   `ng_a'      Pointer to name group a.
+ *   `ng_b'      Pointer to name 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
+ *    1 if both groups contain the same names
+ *    0 if none of their names match
+ *    -1 if some but not all names match
  */
-static int cgroup_cmp(const rdt_core_group_t *cg_a,
-                      const rdt_core_group_t *cg_b) {
-  int found = 0;
+static int ngroup_cmp(const rdt_name_group_t *ng_a,
+                      const rdt_name_group_t *ng_b) {
+  unsigned found = 0;
 
-  assert(cg_a != NULL);
-  assert(cg_b != NULL);
+  assert(ng_a != NULL);
+  assert(ng_b != NULL);
 
-  const int sz_a = cg_a->num_cores;
-  const int sz_b = cg_b->num_cores;
-  const unsigned *tab_a = cg_a->cores;
-  const unsigned *tab_b = cg_b->cores;
+  const size_t sz_a = (unsigned)ng_a->num_names;
+  const size_t sz_b = (unsigned)ng_b->num_names;
+  const char **tab_a = (const char **)ng_a->names;
+  const char **tab_b = (const char **)ng_b->names;
 
-  for (int i = 0; i < sz_a; i++) {
-    for (int j = 0; j < sz_b; j++)
-      if (tab_a[i] == tab_b[j])
+  for (size_t i = 0; i < sz_a; i++) {
+    for (size_t j = 0; j < sz_b; j++)
+      if (strncmp(tab_a[i], tab_b[j], (size_t)MAX_PROC_NAME_LEN) == 0)
         found++;
   }
-  /* if no cores are the same */
+  /* if no names are the same */
   if (!found)
     return 0;
-  /* if group contains same cores */
-  if (sz_a == sz_b && sz_b == found)
+  /* if group contains same names */
+  if (sz_a == sz_b && sz_b == (size_t)found)
     return 1;
-  /* if not all cores are the same */
+  /* if not all names are the same */
   return -1;
 }
 
-static int cgroup_set(rdt_core_group_t *cg, char *desc, uint64_t *cores,
-                      size_t num_cores) {
-  assert(cg != NULL);
-  assert(desc != NULL);
-  assert(cores != NULL);
-  assert(num_cores > 0);
-
-  cg->cores = calloc(num_cores, sizeof(unsigned));
-  if (cg->cores == NULL) {
-    ERROR(RDT_PLUGIN ": Error allocating core group table");
-    return -ENOMEM;
-  }
-  cg->num_cores = num_cores;
-  cg->desc = strdup(desc);
-  if (cg->desc == NULL) {
-    ERROR(RDT_PLUGIN ": Error allocating core group description");
-    sfree(cg->cores);
-    return -ENOMEM;
-  }
-
-  for (size_t i = 0; i < num_cores; i++)
-    cg->cores[i] = (unsigned)cores[i];
-
-  return 0;
-}
-
 /*
  * NAME
- *   oconfig_to_cgroups
+ *   oconfig_to_ngroups
  *
  * DESCRIPTION
- *   Function to set the descriptions and cores for each core group.
+ *   Function to set the descriptions and names for each process names group.
  *   Takes a config option containing list of strings that are used to set
- *   core group values.
+ *   process group values.
  *
  * PARAMETERS
- *   `item'        Config option containing core groups.
- *   `groups'      Table of core groups to set values in.
- *   `max_groups'  Maximum number of core groups allowed.
+ *   `item'        Config option containing process names groups.
+ *   `groups'      Table of process name groups to set values in.
+ *   `max_groups'  Maximum number of process name groups allowed.
  *
  * RETURN VALUE
- *   On success, the number of core groups set up. On error, appropriate
+ *   On success, the number of name groups set up. On error, appropriate
  *   negative error value.
  */
-static int oconfig_to_cgroups(oconfig_item_t *item, rdt_core_group_t *groups,
-                              size_t max_groups) {
+static int oconfig_to_ngroups(const oconfig_item_t *item,
+                              rdt_name_group_t *groups,
+                              const size_t max_groups) {
   int index = 0;
 
   assert(groups != NULL);
@@ -267,32 +411,37 @@ static int oconfig_to_cgroups(oconfig_item_t *item, rdt_core_group_t *groups,
 
   for (int j = 0; j < item->values_num; j++) {
     int ret;
-    size_t n;
-    uint64_t cores[RDT_MAX_CORES] = {0};
     char value[DATA_MAX_NAME_LEN];
 
     if ((item->values[j].value.string == NULL) ||
-        (strlen(item->values[j].value.string) == 0))
-      continue;
+        (strlen(item->values[j].value.string) == 0)) {
+      ERROR(RDT_PLUGIN ": Error - empty group");
+      return -EINVAL;
+    }
 
     sstrncpy(value, item->values[j].value.string, sizeof(value));
 
-    n = strlisttonums(value, cores, STATIC_ARRAY_SIZE(cores));
-    if (n == 0) {
-      ERROR(RDT_PLUGIN ": Error parsing core group (%s)",
+    ret = strlisttoarray(value, &groups[index].names, &groups[index].num_names);
+    if (ret != 0 || groups[index].num_names == 0) {
+      ERROR(RDT_PLUGIN ": Error parsing process names group (%s)",
             item->values[j].value.string);
       return -EINVAL;
     }
 
-    /* set core group info */
-    ret = cgroup_set(&groups[index], item->values[j].value.string, cores, n);
-    if (ret < 0)
-      return ret;
+    /* set group description info */
+    groups[index].desc = sstrdup(item->values[j].value.string);
+    if (groups[index].desc == NULL) {
+      ERROR(RDT_PLUGIN ": Error allocating name group description");
+      return -ENOMEM;
+    }
+
+    groups[index].proc_pids = NULL;
+    groups[index].monitored_pids_count = 0;
 
     index++;
 
-    if (index >= max_groups) {
-      WARNING(RDT_PLUGIN ": Too many core groups configured");
+    if (index >= (const int)max_groups) {
+      WARNING(RDT_PLUGIN ": Too many process names groups configured");
       return index;
     }
   }
@@ -300,95 +449,494 @@ static int oconfig_to_cgroups(oconfig_item_t *item, rdt_core_group_t *groups,
   return index;
 }
 
-#if COLLECT_DEBUG
-static void rdt_dump_cgroups(void) {
-  char cores[RDT_MAX_CORES * 4];
+/*
+ * NAME
+ *   rdt_free_ngroups
+ *
+ * DESCRIPTION
+ *   Function to deallocate memory allocated for name groups.
+ *
+ * PARAMETERS
+ *   `rdt'       Pointer to rdt context
+ */
+static void rdt_free_ngroups(rdt_ctx_t *rdt) {
+  for (int i = 0; i < RDT_MAX_NAMES_GROUPS; i++) {
+    if (rdt->ngroups[i].desc)
+      DEBUG(RDT_PLUGIN ": Freeing pids \'%s\' group\'s data...",
+            rdt->ngroups[i].desc);
+    sfree(rdt->ngroups[i].desc);
+    strarray_free(rdt->ngroups[i].names, rdt->ngroups[i].num_names);
+
+    if (rdt->ngroups[i].proc_pids)
+      proc_pids_free(rdt->ngroups[i].proc_pids, rdt->ngroups[i].num_names);
+
+    rdt->ngroups[i].num_names = 0;
+    sfree(rdt->pngroups[i]);
+  }
+  if (rdt->proc_pids)
+    sfree(rdt->proc_pids);
 
-  if (g_rdt == NULL)
-    return;
+  rdt->num_ngroups = 0;
+}
 
-  DEBUG(RDT_PLUGIN ": Core Groups Dump");
-  DEBUG(RDT_PLUGIN ":  groups count: %" PRIsz, g_rdt->num_groups);
+/*
+ * NAME
+ *   rdt_config_ngroups
+ *
+ * DESCRIPTION
+ *   Reads name groups configuration.
+ *
+ * PARAMETERS
+ *   `rdt`       Pointer to rdt context
+ *   `item'      Config option containing process names groups.
+ *
+ * RETURN VALUE
+ *  0 on success. Negative number on error.
+ */
+static int rdt_config_ngroups(rdt_ctx_t *rdt, const oconfig_item_t *item) {
+  int n = 0;
+  enum pqos_mon_event events = 0;
 
-  for (int i = 0; i < g_rdt->num_groups; i++) {
+  if (item == NULL) {
+    DEBUG(RDT_PLUGIN ": ngroups_config: Invalid argument.");
+    return -EINVAL;
+  }
 
-    memset(cores, 0, sizeof(cores));
-    for (int j = 0; j < g_rdt->cgroups[i].num_cores; j++) {
-      snprintf(cores + strlen(cores), sizeof(cores) - strlen(cores) - 1, " %d",
-               g_rdt->cgroups[i].cores[j]);
+  DEBUG(RDT_PLUGIN ": Process names groups [%d]:", item->values_num);
+  for (int j = 0; j < item->values_num; j++) {
+    if (item->values[j].type != OCONFIG_TYPE_STRING) {
+      ERROR(RDT_PLUGIN
+            ": given process names group value is not a string [idx=%d]",
+            j);
+      return -EINVAL;
     }
+    DEBUG(RDT_PLUGIN ":  [%d]: %s", j, item->values[j].value.string);
+  }
 
-    DEBUG(RDT_PLUGIN ":  group[%d]:", i);
-    DEBUG(RDT_PLUGIN ":    description: %s", g_rdt->cgroups[i].desc);
-    DEBUG(RDT_PLUGIN ":    cores: %s", cores);
-    DEBUG(RDT_PLUGIN ":    events: 0x%X", g_rdt->cgroups[i].events);
+  n = oconfig_to_ngroups(item, rdt->ngroups, RDT_MAX_NAMES_GROUPS);
+  if (n < 0) {
+    rdt_free_ngroups(rdt);
+    ERROR(RDT_PLUGIN ": Error parsing process name groups configuration.");
+    return -EINVAL;
   }
 
-  return;
-}
+  /* validate configured process name values */
+  for (int group_idx = 0; group_idx < n; group_idx++) {
+    DEBUG(RDT_PLUGIN ":  checking group [%d]: %s", group_idx,
+          rdt->ngroups[group_idx].desc);
+    for (size_t name_idx = 0; name_idx < rdt->ngroups[group_idx].num_names;
+         name_idx++) {
+      DEBUG(RDT_PLUGIN ":    checking process name [%zu]: %s", name_idx,
+            rdt->ngroups[group_idx].names[name_idx]);
+      if (!proc_pids_is_name_valid(rdt->ngroups[group_idx].names[name_idx])) {
+        ERROR(RDT_PLUGIN ": Process name group '%s' contains invalid name '%s'",
+              rdt->ngroups[group_idx].desc,
+              rdt->ngroups[group_idx].names[name_idx]);
+        rdt_free_ngroups(rdt);
+        return -EINVAL;
+      }
+    }
+  }
 
-static inline double bytes_to_kb(const double bytes) { return bytes / 1024.0; }
+  if (n == 0) {
+    ERROR(RDT_PLUGIN ": Empty process name groups configured.");
+    return -EINVAL;
+  }
 
-static inline double bytes_to_mb(const double bytes) {
-  return bytes / (1024.0 * 1024.0);
+  /* Get all available events on this platform */
+  for (unsigned i = 0; i < rdt->cap_mon->u.mon->num_events; i++)
+    events |= rdt->cap_mon->u.mon->events[i].type;
+
+  events &= ~(PQOS_PERF_EVENT_LLC_MISS);
+
+  DEBUG(RDT_PLUGIN ": Available events to monitor: %#x", events);
+
+  rdt->num_ngroups = n;
+  for (int i = 0; i < n; i++) {
+    for (int j = 0; j < i; j++) {
+      int found = ngroup_cmp(&rdt->ngroups[j], &rdt->ngroups[i]);
+      if (found != 0) {
+        rdt_free_ngroups(rdt);
+        ERROR(RDT_PLUGIN
+              ": Cannot monitor same process name in different groups.");
+        return -EINVAL;
+      }
+    }
+
+    rdt->ngroups[i].events = events;
+    rdt->pngroups[i] = calloc(1, sizeof(*rdt->pngroups[i]));
+    if (rdt->pngroups[i] == NULL) {
+      rdt_free_ngroups(rdt);
+      ERROR(RDT_PLUGIN
+            ": Failed to allocate memory for process name monitoring data.");
+      return -ENOMEM;
+    }
+  }
+
+  return 0;
 }
 
-static void rdt_dump_data(void) {
-  /*
-   * CORE - monitored group of cores
-   * RMID - Resource Monitoring ID associated with the monitored group
-   * LLC - last level cache occupancy
-   * MBL - local memory bandwidth
-   * MBR - remote memory bandwidth
-   */
-  DEBUG("  CORE     RMID    LLC[KB]   MBL[MB]    MBR[MB]");
-  for (int i = 0; i < g_rdt->num_groups; i++) {
+/*
+ * NAME
+ *   rdt_refresh_ngroup
+ *
+ * DESCRIPTION
+ *   Refresh pids monitored by name group.
+ *
+ * PARAMETERS
+ *   `ngroup`         Pointer to name group.
+ *   `group_mon_data' PQoS monitoring context.
+ *
+ * RETURN VALUE
+ *  0 on success. Negative number on error.
+ */
+static int rdt_refresh_ngroup(rdt_name_group_t *ngroup,
+                              struct pqos_mon_data *group_mon_data) {
 
-    const struct pqos_event_values *pv = &g_rdt->pgroups[i]->values;
+  int result = 0;
 
-    double llc = bytes_to_kb(pv->llc);
-    double mbr = bytes_to_mb(pv->mbm_remote_delta);
-    double mbl = bytes_to_mb(pv->mbm_local_delta);
+  if (NULL == ngroup)
+    return -1;
 
-    DEBUG(" [%s] %8u %10.1f %10.1f %10.1f", g_rdt->cgroups[i].desc,
-          g_rdt->pgroups[i]->poll_ctx[0].rmid, llc, mbl, mbr);
+  if (NULL == ngroup->proc_pids) {
+    ERROR(RDT_PLUGIN
+          ": rdt_refresh_ngroup: \'%s\' uninitialized process pids array.",
+          ngroup->desc);
+
+    return -1;
   }
+
+  DEBUG(RDT_PLUGIN ": rdt_refresh_ngroup: \'%s\' process names group.",
+        ngroup->desc);
+
+  proc_pids_t **proc_pids = ngroup->proc_pids;
+  pids_list_t added_pids;
+  pids_list_t removed_pids;
+
+  memset(&added_pids, 0, sizeof(added_pids));
+  memset(&removed_pids, 0, sizeof(removed_pids));
+
+  for (size_t i = 0; i < ngroup->num_names; ++i) {
+    int diff_result = pids_list_diff(proc_pids[i], &added_pids, &removed_pids);
+    if (0 != diff_result) {
+      ERROR(RDT_PLUGIN
+            ": rdt_refresh_ngroup: \'%s\'. Error [%d] during PID diff.",
+            ngroup->desc, diff_result);
+      result = -1;
+      goto cleanup;
+    }
+  }
+
+  DEBUG(RDT_PLUGIN ": rdt_refresh_ngroup: \'%s\' process names group, added: "
+                   "%u, removed: %u.",
+        ngroup->desc, (unsigned)added_pids.size, (unsigned)removed_pids.size);
+
+  if (added_pids.size > 0) {
+
+    /* no pids are monitored for this group yet: start monitoring */
+    if (0 == ngroup->monitored_pids_count) {
+
+      int start_result =
+          pqos_mon_start_pids(added_pids.size, added_pids.pids, ngroup->events,
+                              (void *)ngroup->desc, group_mon_data);
+      if (PQOS_RETVAL_OK == start_result) {
+        ngroup->monitored_pids_count = added_pids.size;
+      } else {
+        ERROR(RDT_PLUGIN ": rdt_refresh_ngroup: \'%s\'. Error [%d] while "
+                         "STARTING pids monitoring",
+              ngroup->desc, start_result);
+        result = -1;
+        goto pqos_error_recovery;
+      }
+
+    } else {
+
+      int add_result =
+          pqos_mon_add_pids(added_pids.size, added_pids.pids, group_mon_data);
+      if (PQOS_RETVAL_OK == add_result)
+        ngroup->monitored_pids_count += added_pids.size;
+      else {
+        ERROR(RDT_PLUGIN
+              ": rdt_refresh_ngroup: \'%s\'. Error [%d] while ADDING pids.",
+              ngroup->desc, add_result);
+        result = -1;
+        goto pqos_error_recovery;
+      }
+    }
+  }
+
+  if (removed_pids.size > 0) {
+
+    /* all pids are removed: stop monitoring */
+    if (removed_pids.size == ngroup->monitored_pids_count) {
+      /* all pids for this group are lost: stop monitoring */
+      int stop_result = pqos_mon_stop(group_mon_data);
+      if (PQOS_RETVAL_OK != stop_result) {
+        ERROR(RDT_PLUGIN ": rdt_refresh_ngroup: \'%s\'. Error [%d] while "
+                         "STOPPING monitoring",
+              ngroup->desc, stop_result);
+        result = -1;
+        goto pqos_error_recovery;
+      }
+      ngroup->monitored_pids_count = 0;
+    } else {
+      int remove_result = pqos_mon_remove_pids(
+          removed_pids.size, removed_pids.pids, group_mon_data);
+      if (PQOS_RETVAL_OK == remove_result) {
+        ngroup->monitored_pids_count -= removed_pids.size;
+      } else {
+        ERROR(RDT_PLUGIN
+              ": rdt_refresh_ngroup: \'%s\'. Error [%d] while REMOVING pids.",
+              ngroup->desc, remove_result);
+        result = -1;
+        goto pqos_error_recovery;
+      }
+    }
+  }
+
+  goto cleanup;
+
+pqos_error_recovery:
+  /* Why?
+   * Resources might be temporary unavailable.
+   *
+   * How?
+   * Collectd will halt the reading thread for this
+   * plugin if it returns an error.
+   * Consecutive errors will be increasing the read period
+   * up to 1 day interval.
+   * On pqos error stop monitoring current group
+   * and reset the proc_pids array
+   * monitoring will be restarted on next collectd read cycle
+   */
+  DEBUG(RDT_PLUGIN ": rdt_refresh_ngroup: \'%s\' group RESET after error.",
+        ngroup->desc);
+  pqos_mon_stop(group_mon_data);
+  for (size_t i = 0; i < ngroup->num_names; ++i)
+    if (ngroup->proc_pids[i]->curr)
+      ngroup->proc_pids[i]->curr->size = 0;
+
+  ngroup->monitored_pids_count = 0;
+
+cleanup:
+  pids_list_clear(&added_pids);
+  pids_list_clear(&removed_pids);
+
+  return result;
 }
+
+/*
+ * NAME
+ *   read_pids_data
+ *
+ * DESCRIPTION
+ *   Poll monitoring statistics for name groups
+ *
+ * RETURN VALUE
+ *  0 on success. Negative number on error.
+ */
+static int read_pids_data() {
+
+  if (0 == g_rdt->num_ngroups) {
+    DEBUG(RDT_PLUGIN ": read_pids_data: not configured - PIDs read skipped");
+    return 0;
+  }
+
+  DEBUG(RDT_PLUGIN ": read_pids_data: Scanning active groups");
+  struct pqos_mon_data *active_groups[RDT_MAX_NAMES_GROUPS] = {0};
+  size_t active_group_idx = 0;
+  for (size_t pngroups_idx = 0;
+       pngroups_idx < STATIC_ARRAY_SIZE(g_rdt->pngroups); ++pngroups_idx)
+    if (0 != g_rdt->ngroups[pngroups_idx].monitored_pids_count)
+      active_groups[active_group_idx++] = g_rdt->pngroups[pngroups_idx];
+
+  int ret = 0;
+
+  if (0 == active_group_idx) {
+    DEBUG(RDT_PLUGIN ": read_pids_data: no active groups - PIDs read skipped");
+    goto groups_refresh;
+  }
+
+  DEBUG(RDT_PLUGIN ": read_pids_data: PIDs data polling");
+
+  int poll_result = pqos_mon_poll(active_groups, active_group_idx);
+  if (poll_result != PQOS_RETVAL_OK) {
+    ERROR(RDT_PLUGIN ": read_pids_data: Failed to poll monitoring data for "
+                     "pids. Error [%d].",
+          poll_result);
+    ret = -poll_result;
+    goto groups_refresh;
+  }
+
+  for (size_t i = 0; i < g_rdt->num_ngroups; i++) {
+    enum pqos_mon_event mbm_events =
+        (PQOS_MON_EVENT_LMEM_BW | PQOS_MON_EVENT_TMEM_BW |
+         PQOS_MON_EVENT_RMEM_BW);
+
+    if (g_rdt->pngroups[i] == NULL ||
+        g_rdt->ngroups[i].monitored_pids_count == 0)
+      continue;
+
+    const struct pqos_event_values *pv = &g_rdt->pngroups[i]->values;
+
+    /* Submit only monitored events data */
+
+    if (g_rdt->ngroups[i].events & PQOS_MON_EVENT_L3_OCCUP)
+      rdt_submit_gauge(g_rdt->ngroups[i].desc, "bytes", "llc", pv->llc);
+
+    if (g_rdt->ngroups[i].events & PQOS_PERF_EVENT_IPC)
+      rdt_submit_gauge(g_rdt->ngroups[i].desc, "ipc", NULL, pv->ipc);
+
+    if (g_rdt->ngroups[i].events & mbm_events) {
+      rdt_submit_derive(g_rdt->ngroups[i].desc, "memory_bandwidth", "local",
+                        pv->mbm_local_delta);
+      rdt_submit_derive(g_rdt->ngroups[i].desc, "memory_bandwidth", "remote",
+                        pv->mbm_remote_delta);
+    }
+  }
+
+#if COLLECT_DEBUG
+  rdt_dump_pids_data();
 #endif /* COLLECT_DEBUG */
 
-static void rdt_free_cgroups(void) {
-  for (int i = 0; i < RDT_MAX_CORES; i++) {
-    sfree(g_rdt->cgroups[i].desc);
+groups_refresh:
+  ret = proc_pids_update(RDT_PROC_PATH, g_rdt->proc_pids, g_rdt->num_proc_pids);
+  if (0 != ret) {
+    ERROR(RDT_PLUGIN ": Initial update of proc pids failed");
+    return ret;
+  }
+
+  for (size_t i = 0; i < g_rdt->num_ngroups; i++) {
+    int refresh_result =
+        rdt_refresh_ngroup(&(g_rdt->ngroups[i]), g_rdt->pngroups[i]);
+
+    if (0 != refresh_result) {
+      ERROR(RDT_PLUGIN ": read_pids_data: NGroup %zu refresh failed. Error: %d",
+            i, refresh_result);
+      if (0 == ret) {
+        /* refresh error will be escalated only if there were no
+         * errors before.
+         */
+        ret = refresh_result;
+      }
+    }
+  }
+
+  assert(ret <= 0);
+  return ret;
+}
+
+/*
+ * NAME
+ *   rdt_init_pids_monitoring
+ *
+ * DESCRIPTION
+ *   Initialize pids monitoring for all name groups
+ */
+static void rdt_init_pids_monitoring() {
+  for (size_t group_idx = 0; group_idx < g_rdt->num_ngroups; group_idx++) {
+    /*
+     * Each group must have not-null proc_pids array.
+     * Initial refresh is not mandatory for proper
+     * PIDs statistics detection.
+     */
+    rdt_name_group_t *ng = &g_rdt->ngroups[group_idx];
+    int init_result =
+        proc_pids_init((const char **)ng->names, ng->num_names, &ng->proc_pids);
+    if (0 != init_result) {
+      ERROR(RDT_PLUGIN
+            ": Initialization of proc_pids for group %zu failed. Error: %d",
+            group_idx, init_result);
+      continue;
+    }
+
+    /* update global proc_pids table */
+    proc_pids_t **proc_pids = realloc(g_rdt->proc_pids,
+                                      (g_rdt->num_proc_pids + ng->num_names) *
+                                          sizeof(*g_rdt->proc_pids));
+    if (NULL == proc_pids) {
+      ERROR(RDT_PLUGIN ": Alloc error\n");
+      continue;
+    }
 
-    sfree(g_rdt->cgroups[i].cores);
-    g_rdt->cgroups[i].num_cores = 0;
+    for (size_t i = 0; i < ng->num_names; i++)
+      proc_pids[g_rdt->num_proc_pids + i] = ng->proc_pids[i];
 
-    sfree(g_rdt->pgroups[i]);
+    g_rdt->proc_pids = proc_pids;
+    g_rdt->num_proc_pids += ng->num_names;
+  }
+
+  if (g_rdt->num_ngroups > 0) {
+    int update_result =
+        proc_pids_update(RDT_PROC_PATH, g_rdt->proc_pids, g_rdt->num_proc_pids);
+    if (0 != update_result)
+      ERROR(RDT_PLUGIN ": Initial update of proc pids failed");
+  }
+
+  for (size_t group_idx = 0; group_idx < g_rdt->num_ngroups; group_idx++) {
+    int refresh_result = rdt_refresh_ngroup(&(g_rdt->ngroups[group_idx]),
+                                            g_rdt->pngroups[group_idx]);
+    if (0 != refresh_result)
+      ERROR(RDT_PLUGIN ": Initial refresh of group %zu failed. Error: %d",
+            group_idx, refresh_result);
+  }
+}
+#endif /* LIBPQOS2 */
+/*
+ * NAME
+ *   rdt_free_cgroups
+ *
+ * DESCRIPTION
+ *   Function to deallocate memory allocated for core groups.
+ */
+static void rdt_free_cgroups(void) {
+  config_cores_cleanup(&g_rdt->cores);
+  for (int i = 0; i < RDT_MAX_CORES; i++) {
+    sfree(g_rdt->pcgroups[i]);
   }
+  g_rdt->cores.num_cgroups = 0;
 }
 
 static int rdt_default_cgroups(void) {
-  int ret;
+  unsigned num_cores = g_rdt->pqos_cpu->num_cores;
+
+  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;
+  }
+  g_rdt->cores.num_cgroups = num_cores;
 
   /* configure each core in separate group */
-  for (unsigned i = 0; i < g_rdt->pqos_cpu->num_cores; i++) {
+  for (unsigned i = 0; i < num_cores; i++) {
+    core_group_t *cgroup = g_rdt->cores.cgroups + i;
     char desc[DATA_MAX_NAME_LEN];
-    uint64_t core = i;
-
-    snprintf(desc, sizeof(desc), "%d", g_rdt->pqos_cpu->cores[i].lcore);
 
     /* set core group info */
-    ret = cgroup_set(&g_rdt->cgroups[i], desc, &core, 1);
-    if (ret < 0)
-      return ret;
+    cgroup->cores = calloc(1, sizeof(*cgroup->cores));
+    if (cgroup->cores == NULL) {
+      ERROR(RDT_PLUGIN ": Error allocating cores array");
+      rdt_free_cgroups();
+      return -ENOMEM;
+    }
+    cgroup->num_cores = 1;
+    cgroup->cores[0] = i;
+
+    snprintf(desc, sizeof(desc), "%d", g_rdt->pqos_cpu->cores[i].lcore);
+    cgroup->desc = strdup(desc);
+    if (cgroup->desc == NULL) {
+      ERROR(RDT_PLUGIN ": Error allocating core group description");
+      rdt_free_cgroups();
+      return -ENOMEM;
+    }
   }
 
-  return g_rdt->pqos_cpu->num_cores;
+  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;
 
@@ -396,38 +944,23 @@ static int rdt_is_core_id_valid(int core_id) {
 }
 
 static int rdt_config_cgroups(oconfig_item_t *item) {
-  int n = 0;
+  size_t n = 0;
   enum pqos_mon_event events = 0;
 
-  if (item == NULL) {
-    DEBUG(RDT_PLUGIN ": cgroups_config: Invalid argument.");
-    return -EINVAL;
-  }
-
-  DEBUG(RDT_PLUGIN ": Core groups [%d]:", item->values_num);
-  for (int j = 0; j < item->values_num; j++) {
-    if (item->values[j].type != OCONFIG_TYPE_STRING) {
-      ERROR(RDT_PLUGIN ": given core group value is not a string [idx=%d]", j);
-      return -EINVAL;
-    }
-    DEBUG(RDT_PLUGIN ":  [%d]: %s", j, item->values[j].value.string);
-  }
-
-  n = oconfig_to_cgroups(item, g_rdt->cgroups, g_rdt->pqos_cpu->num_cores);
-  if (n < 0) {
+  if (config_cores_parse(item, &g_rdt->cores) < 0) {
     rdt_free_cgroups();
     ERROR(RDT_PLUGIN ": Error parsing core groups configuration.");
     return -EINVAL;
   }
+  n = g_rdt->cores.num_cgroups;
 
   /* validate configured core id values */
-  for (int group_idx = 0; group_idx < n; group_idx++) {
-    for (int core_idx = 0; core_idx < g_rdt->cgroups[group_idx].num_cores;
-         core_idx++) {
-      if (!rdt_is_core_id_valid(g_rdt->cgroups[group_idx].cores[core_idx])) {
-        ERROR(RDT_PLUGIN ": Core group '%s' contains invalid core id '%d'",
-              g_rdt->cgroups[group_idx].desc,
-              (int)g_rdt->cgroups[group_idx].cores[core_idx]);
+  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(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;
       }
@@ -436,18 +969,19 @@ static int rdt_config_cgroups(oconfig_item_t *item) {
 
   if (n == 0) {
     /* create default core groups if "Cores" config option is empty */
-    n = rdt_default_cgroups();
-    if (n < 0) {
+    int ret = rdt_default_cgroups();
+    if (ret < 0) {
       rdt_free_cgroups();
       ERROR(RDT_PLUGIN ": Error creating default core groups configuration.");
-      return n;
+      return ret;
     }
+    n = (size_t)ret;
     INFO(RDT_PLUGIN
          ": No core groups configured. Default core groups created.");
   }
 
   /* 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);
@@ -456,11 +990,12 @@ static int rdt_config_cgroups(oconfig_item_t *item) {
         g_rdt->pqos_cpu->num_cores);
   DEBUG(RDT_PLUGIN ": Available events to monitor: %#x", events);
 
-  g_rdt->num_groups = n;
+  g_rdt->cores.num_cgroups = n;
   for (int i = 0; i < n; i++) {
     for (int j = 0; j < i; j++) {
       int found = 0;
-      found = cgroup_cmp(&g_rdt->cgroups[j], &g_rdt->cgroups[i]);
+      found = config_cores_cmp_cgroups(&g_rdt->cores.cgroups[j],
+                                       &g_rdt->cores.cgroups[i]);
       if (found != 0) {
         rdt_free_cgroups();
         ERROR(RDT_PLUGIN ": Cannot monitor same cores in different groups.");
@@ -468,9 +1003,9 @@ static int rdt_config_cgroups(oconfig_item_t *item) {
       }
     }
 
-    g_rdt->cgroups[i].events = events;
-    g_rdt->pgroups[i] = calloc(1, sizeof(*g_rdt->pgroups[i]));
-    if (g_rdt->pgroups[i] == NULL) {
+    g_rdt->events[i] = events;
+    g_rdt->pcgroups[i] = calloc(1, sizeof(*g_rdt->pcgroups[i]));
+    if (g_rdt->pcgroups[i] == NULL) {
       rdt_free_cgroups();
       ERROR(RDT_PLUGIN ": Failed to allocate memory for monitoring data.");
       return -ENOMEM;
@@ -501,14 +1036,34 @@ static int rdt_preinit(void) {
   struct pqos_config pqos = {.fd_log = -1,
                              .callback_log = rdt_pqos_log,
                              .context_log = NULL,
-                             .verbose = 0};
+                             .verbose = 0,
+#ifdef LIBPQOS2
+                             .interface = PQOS_INTER_OS_RESCTRL_MON};
+  DEBUG(RDT_PLUGIN ": Initializing PQoS with RESCTRL interface");
+#else
+                             .interface = PQOS_INTER_MSR};
+  DEBUG(RDT_PLUGIN ": Initializing PQoS with MSR interface");
+#endif
 
   ret = pqos_init(&pqos);
+  DEBUG(RDT_PLUGIN ": PQoS initialization result: [%d]", ret);
+
+#ifdef LIBPQOS2
+  if (ret == PQOS_RETVAL_INTER) {
+    pqos.interface = PQOS_INTER_MSR;
+    DEBUG(RDT_PLUGIN ": Initializing PQoS with MSR interface");
+    ret = pqos_init(&pqos);
+    DEBUG(RDT_PLUGIN ": PQoS initialization result: [%d]", ret);
+  }
+#endif
+
   if (ret != PQOS_RETVAL_OK) {
     ERROR(RDT_PLUGIN ": Error initializing PQoS library!");
     goto rdt_preinit_error1;
   }
 
+  g_interface = pqos.interface;
+
   ret = pqos_cap_get(&g_rdt->pqos_cap, &g_rdt->pqos_cpu);
   if (ret != PQOS_RETVAL_OK) {
     ERROR(RDT_PLUGIN ": Error retrieving PQoS capabilities.");
@@ -537,7 +1092,6 @@ rdt_preinit_error2:
   pqos_fini();
 
 rdt_preinit_error1:
-
   sfree(g_rdt);
 
   return -1;
@@ -550,25 +1104,66 @@ static int rdt_config(oconfig_item_t *ci) {
       reports a failure in configuration and
       aborts
     */
-    return (0);
+    return 0;
   }
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
 
-    if (strcasecmp("Cores", child->key) == 0) {
-      if (rdt_config_cgroups(child) != 0) {
+    if (strncasecmp("Cores", child->key, (size_t)strlen("Cores")) == 0) {
+      if (g_rdt->cores.num_cgroups > 0) {
+        ERROR(RDT_PLUGIN
+              ": Configuration parameter \"%s\" can be used only once.",
+              child->key);
+        g_state = CONFIGURATION_ERROR;
+      } else if (rdt_config_cgroups(child) != 0)
         g_state = CONFIGURATION_ERROR;
+
+      if (g_state == CONFIGURATION_ERROR)
         /* if we return -1 at this point collectd
            reports a failure in configuration and
            aborts
          */
-        return (0);
-      }
+        return 0;
 
 #if COLLECT_DEBUG
       rdt_dump_cgroups();
 #endif /* COLLECT_DEBUG */
+    } else if (strncasecmp("Processes", child->key,
+                           (size_t)strlen("Processes")) == 0) {
+#ifdef LIBPQOS2
+      if (g_interface != PQOS_INTER_OS_RESCTRL_MON) {
+        ERROR(RDT_PLUGIN ": Configuration parameter \"%s\" not supported. "
+                         "Resctrl monitoring is needed for PIDs monitoring.",
+              child->key);
+        g_state = CONFIGURATION_ERROR;
+      }
+
+      else if (g_rdt->num_ngroups > 0) {
+        ERROR(RDT_PLUGIN
+              ": Configuration parameter \"%s\" can be used only once.",
+              child->key);
+        g_state = CONFIGURATION_ERROR;
+      }
+
+      else if (rdt_config_ngroups(g_rdt, child) != 0)
+        g_state = CONFIGURATION_ERROR;
+
+      if (g_state == CONFIGURATION_ERROR)
+        /* if we return -1 at this point collectd
+           reports a failure in configuration and
+           aborts
+         */
+        return 0;
+
+#if COLLECT_DEBUG
+      rdt_dump_ngroups();
+#endif /* COLLECT_DEBUG */
+#else  /* !LIBPQOS2 */
+      ERROR(RDT_PLUGIN ": Configuration parameter \"%s\" not supported, please "
+                       "recompile collectd with libpqos version 2.0 or newer.",
+            child->key);
+#endif /* LIBPQOS2 */
     } else {
       ERROR(RDT_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
     }
@@ -577,103 +1172,115 @@ 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) {
-  value_list_t vl = VALUE_LIST_INIT;
+static int read_cores_data() {
 
-  vl.values = &(value_t){.derive = value};
-  vl.values_len = 1;
+  if (0 == g_rdt->cores.num_cgroups) {
+    DEBUG(RDT_PLUGIN ": read_cores_data: not configured - Cores read skipped");
+    return 0;
+  }
+  DEBUG(RDT_PLUGIN ": read_cores_data: Cores data poll");
 
-  sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin));
-  snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup);
-  sstrncpy(vl.type, type, sizeof(vl.type));
-  if (type_instance)
-    sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+  int ret =
+      pqos_mon_poll(&g_rdt->pcgroups[0], (unsigned)g_rdt->cores.num_cgroups);
+  if (ret != PQOS_RETVAL_OK) {
+    ERROR(RDT_PLUGIN ": read_cores_data: Failed to poll monitoring data for "
+                     "cores. Error [%d].",
+          ret);
+    return -1;
+  }
 
-  plugin_dispatch_values(&vl);
-}
+  for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) {
+    core_group_t *cgroup = g_rdt->cores.cgroups + i;
+    enum pqos_mon_event mbm_events =
+        (PQOS_MON_EVENT_LMEM_BW | PQOS_MON_EVENT_TMEM_BW |
+         PQOS_MON_EVENT_RMEM_BW);
 
-static void rdt_submit_gauge(char *cgroup, char *type, char *type_instance,
-                             gauge_t value) {
-  value_list_t vl = VALUE_LIST_INIT;
+    const struct pqos_event_values *pv = &g_rdt->pcgroups[i]->values;
 
-  vl.values = &(value_t){.gauge = value};
-  vl.values_len = 1;
+    /* Submit only monitored events data */
 
-  sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin));
-  snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup);
-  sstrncpy(vl.type, type, sizeof(vl.type));
-  if (type_instance)
-    sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+    if (g_rdt->events[i] & PQOS_MON_EVENT_L3_OCCUP)
+      rdt_submit_gauge(cgroup->desc, "bytes", "llc", pv->llc);
 
-  plugin_dispatch_values(&vl);
+    if (g_rdt->events[i] & PQOS_PERF_EVENT_IPC)
+      rdt_submit_gauge(cgroup->desc, "ipc", NULL, pv->ipc);
+
+    if (g_rdt->events[i] & mbm_events) {
+      rdt_submit_derive(cgroup->desc, "memory_bandwidth", "local",
+                        pv->mbm_local_delta);
+      rdt_submit_derive(cgroup->desc, "memory_bandwidth", "remote",
+                        pv->mbm_remote_delta);
+    }
+  }
+
+#if COLLECT_DEBUG
+  rdt_dump_cores_data();
+#endif /* COLLECT_DEBUG */
+
+  return 0;
 }
 
 static int rdt_read(__attribute__((unused)) user_data_t *ud) {
-  int ret;
 
   if (g_rdt == NULL) {
     ERROR(RDT_PLUGIN ": rdt_read: plugin not initialized.");
     return -EINVAL;
   }
 
-  ret = pqos_mon_poll(&g_rdt->pgroups[0], (unsigned)g_rdt->num_groups);
-  if (ret != PQOS_RETVAL_OK) {
-    ERROR(RDT_PLUGIN ": Failed to poll monitoring data.");
-    return -1;
-  }
+  int cores_read_result = read_cores_data();
 
-#if COLLECT_DEBUG
-  rdt_dump_data();
-#endif /* COLLECT_DEBUG */
+#ifdef LIBPQOS2
+  int pids_read_result = read_pids_data();
+#endif /* LIBPQOS2 */
 
-  for (int i = 0; i < g_rdt->num_groups; i++) {
-    enum pqos_mon_event mbm_events =
-        (PQOS_MON_EVENT_LMEM_BW | PQOS_MON_EVENT_TMEM_BW |
-         PQOS_MON_EVENT_RMEM_BW);
+  if (0 != cores_read_result)
+    return cores_read_result;
 
-    const struct pqos_event_values *pv = &g_rdt->pgroups[i]->values;
+#ifdef LIBPQOS2
+  if (0 != pids_read_result)
+    return pids_read_result;
+#endif /* LIBPQOS2 */
 
-    /* Submit only monitored events data */
+  return 0;
+}
 
-    if (g_rdt->cgroups[i].events & PQOS_MON_EVENT_L3_OCCUP)
-      rdt_submit_gauge(g_rdt->cgroups[i].desc, "bytes", "llc", pv->llc);
+static void rdt_init_cores_monitoring() {
+  for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) {
+    core_group_t *cg = g_rdt->cores.cgroups + i;
 
-    if (g_rdt->cgroups[i].events & PQOS_PERF_EVENT_IPC)
-      rdt_submit_gauge(g_rdt->cgroups[i].desc, "ipc", NULL, pv->ipc);
+    int mon_start_result =
+        pqos_mon_start(cg->num_cores, cg->cores, g_rdt->events[i],
+                       (void *)cg->desc, g_rdt->pcgroups[i]);
 
-    if (g_rdt->cgroups[i].events & mbm_events) {
-      rdt_submit_derive(g_rdt->cgroups[i].desc, "memory_bandwidth", "local",
-                        pv->mbm_local_delta);
-      rdt_submit_derive(g_rdt->cgroups[i].desc, "memory_bandwidth", "remote",
-                        pv->mbm_remote_delta);
-    }
+    if (mon_start_result != PQOS_RETVAL_OK)
+      ERROR(RDT_PLUGIN
+            ": Error starting cores monitoring group %s (pqos status=%d)",
+            cg->desc, mon_start_result);
   }
-
-  return 0;
 }
 
 static int rdt_init(void) {
-  int ret;
 
-  if (g_state == CONFIGURATION_ERROR)
+  if (g_state == CONFIGURATION_ERROR) {
+    if (g_rdt != NULL) {
+      if (g_rdt->cores.num_cgroups > 0)
+        rdt_free_cgroups();
+#ifdef LIBPQOS2
+      if (g_rdt->num_ngroups > 0)
+        rdt_free_ngroups(g_rdt);
+#endif
+    }
     return -1;
+  }
 
-  ret = rdt_preinit();
-  if (ret != 0)
-    return ret;
-
-  /* Start monitoring */
-  for (int i = 0; i < g_rdt->num_groups; i++) {
-    rdt_core_group_t *cg = &g_rdt->cgroups[i];
+  int rdt_preinint_result = rdt_preinit();
+  if (rdt_preinint_result != 0)
+    return rdt_preinint_result;
 
-    ret = pqos_mon_start(cg->num_cores, cg->cores, cg->events, (void *)cg->desc,
-                         g_rdt->pgroups[i]);
-
-    if (ret != PQOS_RETVAL_OK)
-      ERROR(RDT_PLUGIN ": Error starting monitoring group %s (pqos status=%d)",
-            cg->desc, ret);
-  }
+  rdt_init_cores_monitoring();
+#ifdef LIBPQOS2
+  rdt_init_pids_monitoring();
+#endif /* LIBPQOS2 */
 
   return 0;
 }
@@ -686,16 +1293,24 @@ static int rdt_shutdown(void) {
   if (g_rdt == NULL)
     return 0;
 
-  /* Stop monitoring */
-  for (int i = 0; i < g_rdt->num_groups; i++) {
-    pqos_mon_stop(g_rdt->pgroups[i]);
+  /* Stop monitoring cores */
+  for (size_t i = 0; i < g_rdt->cores.num_cgroups; i++) {
+    pqos_mon_stop(g_rdt->pcgroups[i]);
   }
 
+/* Stop pids monitoring */
+#ifdef LIBPQOS2
+  for (size_t i = 0; i < g_rdt->num_ngroups; i++)
+    pqos_mon_stop(g_rdt->pngroups[i]);
+#endif
+
   ret = pqos_fini();
   if (ret != PQOS_RETVAL_OK)
     ERROR(RDT_PLUGIN ": Error shutting down PQoS library.");
-
   rdt_free_cgroups();
+#ifdef LIBPQOS2
+  rdt_free_ngroups(g_rdt);
+#endif /* LIBPQOS2 */
   sfree(g_rdt);
 
   return 0;
diff --git a/src/intel_rdt_test.c b/src/intel_rdt_test.c
new file mode 100644 (file)
index 0000000..af5672b
--- /dev/null
@@ -0,0 +1,302 @@
+#include "intel_rdt.c" /* sic */
+#include "testing.h"
+
+/***************************************************************************
+ * PQOS mocks
+ */
+int pqos_mon_reset(void) { return 0; }
+int pqos_mon_assoc_get(const unsigned lcore, pqos_rmid_t *rmid) { return 0; }
+int pqos_mon_start(const unsigned num_cores, const unsigned *cores,
+                   const enum pqos_mon_event event, void *context,
+                   struct pqos_mon_data *group) {
+  return 0;
+}
+#if PQOS_VERSION >= 30000
+int pqos_mon_start_pids(const unsigned num_pids, const pid_t *pids,
+                        const enum pqos_mon_event event, void *context,
+                        struct pqos_mon_data *group) {
+  return 0;
+}
+int pqos_mon_add_pids(const unsigned num_pids, const pid_t *pids,
+                      struct pqos_mon_data *group) {
+  return 0;
+}
+int pqos_mon_remove_pids(const unsigned num_pids, const pid_t *pids,
+                         struct pqos_mon_data *group) {
+  return 0;
+}
+
+#else
+int pqos_mon_start_pid(const pid_t pids, const enum pqos_mon_event event,
+                       void *context, struct pqos_mon_data *group) {
+  return 0;
+}
+#endif
+int pqos_mon_stop(struct pqos_mon_data *group) { return 0; }
+int pqos_mon_poll(struct pqos_mon_data **groups, const unsigned num_groups) {
+  return 0;
+}
+
+#if PQOS_VERSION >= 30000
+int pqos_alloc_reset(const enum pqos_cdp_config l3_cdp_cfg,
+                     const enum pqos_cdp_config l2_cdp_cfg,
+                     const enum pqos_mba_config mba_cfg) {
+  return 0;
+}
+#elif PQOS_VERSION >= 20000
+int pqos_alloc_reset(const enum pqos_cdp_config l3_cdp_cfg,
+                     const enum pqos_cdp_config l2_cdp_cfg) {
+  return 0;
+}
+#else
+int pqos_alloc_reset(const enum pqos_cdp_config l3_cdp_cfg) { return 0; }
+#endif
+int pqos_alloc_assoc_set(const unsigned lcore, const unsigned class_id) {
+  return 0;
+}
+int pqos_alloc_assoc_get(const unsigned lcore, unsigned *class_id) { return 0; }
+int pqos_alloc_assoc_set_pid(const pid_t task, const unsigned class_id) {
+  return 0;
+}
+int pqos_alloc_assoc_get_pid(const pid_t task, unsigned *class_id) { return 0; }
+int pqos_alloc_assign(const unsigned technology, const unsigned *core_array,
+                      const unsigned core_num, unsigned *class_id) {
+  return 0;
+}
+int pqos_alloc_release(const unsigned *core_array, const unsigned core_num) {
+  return 0;
+}
+int pqos_alloc_assign_pid(const unsigned technology, const pid_t *task_array,
+                          const unsigned task_num, unsigned *class_id) {
+  return 0;
+}
+int pqos_alloc_release_pid(const pid_t *task_array, const unsigned task_num) {
+  return 0;
+}
+int pqos_init(const struct pqos_config *config) { return 0; }
+int pqos_fini(void) { return 0; }
+int pqos_cap_get_type(const struct pqos_cap *cap, const enum pqos_cap_type type,
+                      const struct pqos_capability **cap_item) {
+  return 0;
+}
+int pqos_cap_get(const struct pqos_cap **cap, const struct pqos_cpuinfo **cpu) {
+  return 0;
+}
+
+#ifdef LIBPQOS2
+/***************************************************************************
+ * helper functions
+ */
+rdt_ctx_t *stub_rdt_setup() {
+
+  rdt_ctx_t *rdt = calloc(1, sizeof(*rdt));
+  struct pqos_cpuinfo *pqos_cpu = calloc(1, sizeof(*pqos_cpu));
+  struct pqos_cap *pqos_cap = calloc(1, sizeof(*pqos_cap));
+  struct pqos_cap_mon *mon = calloc(1, sizeof(*mon));
+  struct pqos_capability *cap_mon = calloc(1, sizeof(*cap_mon));
+
+  cap_mon->u.mon = mon;
+  rdt->pqos_cap = pqos_cap;
+  rdt->pqos_cpu = pqos_cpu;
+  rdt->cap_mon = cap_mon;
+
+  return rdt;
+}
+
+void stub_rdt_teardown(rdt_ctx_t *rdt) {
+  free(rdt->cap_mon->u.mon);
+  free((void *)rdt->cap_mon);
+  free((void *)rdt->pqos_cpu);
+  free((void *)rdt->pqos_cap);
+  free(rdt);
+}
+
+/***************************************************************************
+ * tests
+ */
+DEF_TEST(rdt_config_ngroups__one_process) {
+  /* setup */
+  rdt_ctx_t *rdt = stub_rdt_setup();
+
+  oconfig_value_t values[] = {
+      {.value.string = "proc1", .type = OCONFIG_TYPE_STRING},
+  };
+  oconfig_item_t config_item = {
+      .values = values, .values_num = STATIC_ARRAY_SIZE(values),
+  };
+
+  /* check */
+  int result = rdt_config_ngroups(rdt, &config_item);
+  EXPECT_EQ_INT(0, result);
+  EXPECT_EQ_STR(values[0].value.string, rdt->ngroups[0].desc);
+  EXPECT_EQ_INT(1, rdt->num_ngroups);
+
+  /* cleanup */
+  rdt_free_ngroups(rdt);
+  stub_rdt_teardown(rdt);
+
+  return 0;
+}
+
+DEF_TEST(rdt_config_ngroups__two_groups) {
+  /* setup */
+  rdt_ctx_t *rdt = stub_rdt_setup();
+
+  oconfig_value_t values[] = {
+      {.value.string = "proc11,proc12,proc13", .type = OCONFIG_TYPE_STRING},
+      {.value.string = "proc21,proc22,proc23", .type = OCONFIG_TYPE_STRING},
+  };
+  oconfig_item_t config_item = {
+      .values = values, .values_num = STATIC_ARRAY_SIZE(values),
+  };
+
+  /* check */
+  int result = rdt_config_ngroups(rdt, &config_item);
+  EXPECT_EQ_INT(0, result);
+  EXPECT_EQ_INT(2, rdt->num_ngroups);
+  EXPECT_EQ_STR("proc11,proc12,proc13", rdt->ngroups[0].desc);
+  EXPECT_EQ_STR("proc21,proc22,proc23", rdt->ngroups[1].desc);
+  EXPECT_EQ_STR("proc11", rdt->ngroups[0].names[0]);
+  EXPECT_EQ_STR("proc12", rdt->ngroups[0].names[1]);
+  EXPECT_EQ_STR("proc13", rdt->ngroups[0].names[2]);
+  EXPECT_EQ_STR("proc21", rdt->ngroups[1].names[0]);
+  EXPECT_EQ_STR("proc22", rdt->ngroups[1].names[1]);
+  EXPECT_EQ_STR("proc23", rdt->ngroups[1].names[2]);
+
+  /* cleanup */
+  rdt_free_ngroups(rdt);
+  stub_rdt_teardown(rdt);
+
+  return 0;
+}
+
+DEF_TEST(rdt_config_ngroups__too_long_proc_name) {
+  /* setup */
+  rdt_ctx_t *rdt = stub_rdt_setup();
+
+  oconfig_value_t values[] = {
+      {.value.string = "_seventeen_chars_", .type = OCONFIG_TYPE_STRING},
+  };
+  oconfig_item_t config_item = {
+      .values = values, .values_num = STATIC_ARRAY_SIZE(values),
+  };
+
+  /* check */
+  int result = rdt_config_ngroups(rdt, &config_item);
+  EXPECT_EQ_INT(-EINVAL, result);
+
+  /* cleanup */
+  stub_rdt_teardown(rdt);
+
+  return 0;
+}
+
+DEF_TEST(rdt_config_ngroups__duplicate_proc_name_between_groups) {
+  /* setup */
+  rdt_ctx_t *rdt = stub_rdt_setup();
+
+  oconfig_value_t values[] = {
+      {.value.string = "proc11,proc12,proc", .type = OCONFIG_TYPE_STRING},
+      {.value.string = "proc21,proc,proc23", .type = OCONFIG_TYPE_STRING},
+  };
+  oconfig_item_t config_item = {
+      .values = values, .values_num = STATIC_ARRAY_SIZE(values),
+  };
+
+  /* check */
+  int result = rdt_config_ngroups(rdt, &config_item);
+  EXPECT_EQ_INT(-EINVAL, result);
+
+  /* cleanup */
+  stub_rdt_teardown(rdt);
+
+  return 0;
+}
+
+DEF_TEST(rdt_config_ngroups__duplicate_proc_name_in_group) {
+  /* setup */
+  rdt_ctx_t *rdt = stub_rdt_setup();
+
+  oconfig_value_t values[] = {
+      {.value.string = "proc11,proc,proc,proc14", .type = OCONFIG_TYPE_STRING},
+  };
+  oconfig_item_t config_item = {
+      .values = values, .values_num = STATIC_ARRAY_SIZE(values),
+  };
+
+  /* check */
+  int result = rdt_config_ngroups(rdt, &config_item);
+  EXPECT_EQ_INT(-EINVAL, result);
+
+  /* cleanup */
+  stub_rdt_teardown(rdt);
+
+  return 0;
+}
+
+DEF_TEST(rdt_config_ngroups__empty_group) {
+  /* setup */
+  rdt_ctx_t *rdt = stub_rdt_setup();
+
+  oconfig_value_t values[] = {
+      {.value.string = "proc11,proc12,proc13", .type = OCONFIG_TYPE_STRING},
+      {.value.string = "", .type = OCONFIG_TYPE_STRING},
+
+  };
+  oconfig_item_t config_item = {
+      .values = values, .values_num = STATIC_ARRAY_SIZE(values),
+  };
+
+  /* check */
+  int result = rdt_config_ngroups(rdt, &config_item);
+  EXPECT_EQ_INT(-EINVAL, result);
+
+  /* cleanup */
+  stub_rdt_teardown(rdt);
+
+  return 0;
+}
+
+DEF_TEST(rdt_config_ngroups__empty_proc_name) {
+  /* setup */
+  rdt_ctx_t *rdt = stub_rdt_setup();
+
+  oconfig_value_t values[] = {
+      {.value.string = "proc11,,proc13", .type = OCONFIG_TYPE_STRING},
+  };
+  oconfig_item_t config_item = {
+      .values = values, .values_num = STATIC_ARRAY_SIZE(values),
+  };
+
+  /* check */
+  int result = rdt_config_ngroups(rdt, &config_item);
+  EXPECT_EQ_INT(-EINVAL, result);
+
+  /* cleanup */
+  stub_rdt_teardown(rdt);
+
+  return 0;
+}
+
+int main(void) {
+  RUN_TEST(rdt_config_ngroups__one_process);
+  RUN_TEST(rdt_config_ngroups__two_groups);
+  RUN_TEST(rdt_config_ngroups__too_long_proc_name);
+  RUN_TEST(rdt_config_ngroups__duplicate_proc_name_between_groups);
+  RUN_TEST(rdt_config_ngroups__duplicate_proc_name_in_group);
+  RUN_TEST(rdt_config_ngroups__empty_group);
+  RUN_TEST(rdt_config_ngroups__empty_proc_name);
+  END_TEST;
+}
+
+#else
+DEF_TEST(pqos12_test_stub) {
+  EXPECT_EQ_INT(0, 0);
+  return 0;
+}
+
+int main(void) {
+  RUN_TEST(pqos12_test_stub);
+  END_TEST;
+}
+#endif
index c45ef66..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.");
index 6e888c4..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 */
index 2db344e..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,39 +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 if threshold sensor support events */
-      if ((ipmi_sensor_get_event_reading_type(sensor) ==
-           IPMI_EVENT_READING_TYPE_THRESHOLD) &&
-          (ipmi_sensor_get_threshold_access(sensor) !=
-           IPMI_THRESHOLD_ACCESS_SUPPORT_NONE))
-        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 */
 
@@ -886,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;
   }
 
@@ -898,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);
@@ -967,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);
   }
@@ -1002,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);
 
@@ -1028,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);
@@ -1064,10 +1082,15 @@ static int c_ipmi_config_add_instance(oconfig_item_t *ci) {
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
 
-    if (strcasecmp("Sensor", child->key) == 0)
-      ignorelist_add(st->ignorelist, child->values[0].value.string);
-    else if (strcasecmp("IgnoreSelected", child->key) == 0) {
-      _Bool t;
+    if (strcasecmp("Sensor", child->key) == 0) {
+      char *value = NULL;
+      status = cf_util_get_string(child, &value);
+      if (status != 0)
+        break;
+      ignorelist_add(st->ignorelist, value);
+      sfree(value);
+    } else if (strcasecmp("IgnoreSelected", child->key) == 0) {
+      bool t;
       status = cf_util_get_boolean(child, &t);
       if (status != 0)
         break;
@@ -1080,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) {
@@ -1126,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;
@@ -1160,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);
@@ -1210,7 +1246,7 @@ static int c_ipmi_init(void) {
   }
 
   /* Don't send `ADD' notifications during startup (~ 1 minute) */
-  int cycles = 1 + (60 / CDTIME_T_TO_TIME_T(plugin_get_interval()));
+  int cycles = 1 + (int)(TIME_T_TO_CDTIME_T(60) / plugin_get_interval());
 
   st = instances;
   while (NULL != st) {
@@ -1236,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);
@@ -1265,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 bc75d7b..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];
@@ -136,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;
index 0afc749..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>
index f8cf37c..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
@@ -138,7 +138,7 @@ static int irq_read(void) {
     if (irq_name_len == 4 && (strncmp(irq_name, "FIQ:", 4) == 0))
       continue;
 
-    irq_name[irq_name_len - 1] = 0;
+    irq_name[irq_name_len - 1] = '\0';
     irq_name_len--;
 
     irq_value = 0;
index a35ca46..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);
     }
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 d66b815..ed8ab51 100644 (file)
@@ -27,6 +27,8 @@
 #ifndef LIBCOLLECTDCLIENT_NETWORK_BUFFER_H
 #define LIBCOLLECTDCLIENT_NETWORK_BUFFER_H 1
 
+#include "config.h"
+
 #include "collectd/network.h" /* for lcc_security_level_t */
 #include "collectd/types.h"
 
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
diff --git a/src/libcollectdclient/collectd/stdendian.h b/src/libcollectdclient/collectd/stdendian.h
new file mode 100644 (file)
index 0000000..9ab0fb5
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ *   stdendian.h
+ *
+ *   This header defines the following endian macros as defined here:
+ *   http://austingroupbugs.net/view.php?id=162
+ *
+ *     BYTE_ORDER         this macro shall have a value equal to one
+ *                        of the *_ENDIAN macros in this header.
+ *     LITTLE_ENDIAN      if BYTE_ORDER == LITTLE_ENDIAN, the host
+ *                        byte order is from least significant to
+ *                        most significant.
+ *     BIG_ENDIAN         if BYTE_ORDER == BIG_ENDIAN, the host byte
+ *                        order is from most significant to least
+ *                        significant.
+ *
+ *   The following are defined as macros:
+ *
+ *     uint16_t bswap16(uint16_t x);
+ *     uint32_t bswap32(uint32_t x);
+ *     uint64_t bswap64(uint64_t x);
+
+ *     uint16_t htobe16(uint16_t x);
+ *     uint16_t htole16(uint16_t x);
+ *     uint16_t be16toh(uint16_t x);
+ *     uint16_t le16toh(uint16_t x);
+ *
+ *     uint32_t htobe32(uint32_t x);
+ *     uint32_t htole32(uint32_t x);
+ *     uint32_t be32toh(uint32_t x);
+ *     uint32_t le32toh(uint32_t x);
+ *
+ *     uint64_t htobe64(uint64_t x);
+ *     uint64_t htole64(uint64_t x);
+ *     uint64_t be64toh(uint64_t x);
+ *     uint64_t le64toh(uint64_t x);
+ *
+ *   The header defines the following macro for OpenCL compatibility
+ *
+ https://www.khronos.org/registry/cl/sdk/2.0/docs/man/xhtml/preprocessorDirectives.html
+ *
+ *     __ENDIAN_LITTLE__  if BYTE_ORDER == LITTLE_ENDIAN then this
+ *                        macro is present for OpenCL compatibility
+ *
+ *   The implementation provides a uniform interface to endian macros using only
+ *   system headers on recent Linux, Darwin, FreeBSD, Solaris and Windows
+ systems.
+ *
+ *   This approach is intended to avoid the need for preflight configure
+ scripts.
+ *   An alternative approach would be to test compiler CPU architecture marcros.
+ *
+ *   This header has had *limited* testing on recent C11/C++11 compilers and is
+ *   based on the austin bug tracker interface, manpages, and headers present in
+ *   Linux, FreeBSD, Windows, Solaris and Darwin.
+ *
+ *   The header uses __builtin_bswapXX intrinsic with GCC/Clang (__GNUC__) on
+ *   platforms that do not provide bswap16, bswap32, bswap64 (Darwin)
+ *
+ *   Public Domain.
+ */
+
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+/* Linux / GLIBC */
+#if defined(__linux__) || defined(__GLIBC__)
+#include <byteswap.h>
+#include <endian.h>
+#define __ENDIAN_DEFINED 1
+#define __BSWAP_DEFINED 1
+#define __HOSTSWAP_DEFINED 1
+#define _BYTE_ORDER __BYTE_ORDER
+#define _LITTLE_ENDIAN __LITTLE_ENDIAN
+#define _BIG_ENDIAN __BIG_ENDIAN
+#define bswap16(x) bswap_16(x)
+#define bswap32(x) bswap_32(x)
+#define bswap64(x) bswap_64(x)
+#endif /* __linux__ || __GLIBC__ */
+
+/* BSD */
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) ||   \
+    defined(__OpenBSD__)
+#include <sys/endian.h>
+#define __ENDIAN_DEFINED 1
+#define __BSWAP_DEFINED 1
+#define __HOSTSWAP_DEFINED 1
+#endif /* BSD */
+
+/* Solaris */
+#if defined(sun)
+#include <sys/byteorder.h>
+#include <sys/isa_defs.h>
+#define bswap16(x) BSWAP_16(x)
+#define bswap32(x) BSWAP_32(x)
+#define bswap64(x) BSWAP_64(x)
+/* sun headers don't set a value for _LITTLE_ENDIAN or _BIG_ENDIAN */
+#if defined(_LITTLE_ENDIAN)
+#undef _LITTLE_ENDIAN
+#define _LITTLE_ENDIAN 1234
+#define _BIG_ENDIAN 4321
+#define _BYTE_ORDER _LITTLE_ENDIAN
+#elif defined(_BIG_ENDIAN)
+#undef _BIG_ENDIAN
+#define _LITTLE_ENDIAN 1234
+#define _BIG_ENDIAN 4321
+#define _BYTE_ORDER _BIG_ENDIAN
+#endif
+#define __ENDIAN_DEFINED 1
+#endif /* sun */
+
+/* AIX */
+#if defined(_AIX)
+#include <sys/machine.h>
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define _LITTLE_ENDIAN 1234
+#define _BIG_ENDIAN 4321
+#define _BYTE_ORDER _LITTLE_ENDIAN
+#elif BYTE_ORDER == BIG_ENDIAN
+#define _LITTLE_ENDIAN 1234
+#define _BIG_ENDIAN 4321
+#define _BYTE_ORDER _BIG_ENDIAN
+#else
+#error Could not determine CPU byte order for AIX
+#endif
+#define __ENDIAN_DEFINED 1
+#endif /* AIX */
+
+/* Windows */
+#if defined(_WIN32) || defined(_MSC_VER)
+/* assumes all Microsoft targets are little endian */
+#define _LITTLE_ENDIAN 1234
+#define _BIG_ENDIAN 4321
+#define _BYTE_ORDER _LITTLE_ENDIAN
+#define __ENDIAN_DEFINED 1
+#endif /* _MSC_VER */
+
+/* OS X */
+#if defined(__APPLE__)
+#include <machine/endian.h>
+#define _BYTE_ORDER BYTE_ORDER
+#define _LITTLE_ENDIAN LITTLE_ENDIAN
+#define _BIG_ENDIAN BIG_ENDIAN
+#define __ENDIAN_DEFINED 1
+#endif /* __APPLE__ */
+
+/* OpenCL */
+#if defined(__OPENCL_VERSION__)
+#define _LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#if defined(__ENDIAN_LITTLE__)
+#define _BYTE_ORDER _LITTLE_ENDIAN
+#else
+#define _BYTE_ORDER _BIG_ENDIAN
+#endif
+#define bswap16(x) as_ushort(as_uchar2(ushort(x)).s1s0)
+#define bswap32(x) as_uint(as_uchar4(uint(x)).s3s2s1s0)
+#define bswap64(x) as_ulong(as_uchar8(ulong(x)).s7s6s5s4s3s2s1s0)
+#define __ENDIAN_DEFINED 1
+#define __BSWAP_DEFINED 1
+#endif
+
+/* Unknown */
+#if !__ENDIAN_DEFINED
+#error Could not determine CPU byte order
+#endif
+
+/* POSIX - http://austingroupbugs.net/view.php?id=162 */
+#ifndef BYTE_ORDER
+#define BYTE_ORDER _BYTE_ORDER
+#endif
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN _LITTLE_ENDIAN
+#endif
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN _BIG_ENDIAN
+#endif
+
+/* OpenCL compatibility - define __ENDIAN_LITTLE__ on little endian systems */
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+#if !defined(__ENDIAN_LITTLE__)
+#define __ENDIAN_LITTLE__ 1
+#endif
+#endif
+
+/* Byte swap macros */
+#if !__BSWAP_DEFINED
+
+#ifndef bswap16
+/* handle missing __builtin_bswap16
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52624 */
+#if defined __GNUC__
+#define bswap16(x) __builtin_bswap16(x)
+#else
+#define bswap16(x)                                                             \
+  ((uint16_t)((((uint16_t)(x)&0xff00) >> 8) | (((uint16_t)(x)&0x00ff) << 8)))
+#endif
+#endif
+
+#ifndef bswap32
+#if defined __GNUC__
+#define bswap32(x) __builtin_bswap32(x)
+#else
+#define bswap32(x)                                                             \
+  ((uint32_t)(                                                                 \
+      (((uint32_t)(x)&0xff000000) >> 24) | (((uint32_t)(x)&0x00ff0000) >> 8) | \
+      (((uint32_t)(x)&0x0000ff00) << 8) | (((uint32_t)(x)&0x000000ff) << 24)))
+#endif
+#endif
+
+#ifndef bswap64
+#if defined __GNUC__
+#define bswap64(x) __builtin_bswap64(x)
+#else
+#define bswap64(x)                                                             \
+  ((uint64_t)((((uint64_t)(x)&0xff00000000000000ull) >> 56) |                  \
+              (((uint64_t)(x)&0x00ff000000000000ull) >> 40) |                  \
+              (((uint64_t)(x)&0x0000ff0000000000ull) >> 24) |                  \
+              (((uint64_t)(x)&0x000000ff00000000ull) >> 8) |                   \
+              (((uint64_t)(x)&0x00000000ff000000ull) << 8) |                   \
+              (((uint64_t)(x)&0x0000000000ff0000ull) << 24) |                  \
+              (((uint64_t)(x)&0x000000000000ff00ull) << 40) |                  \
+              (((uint64_t)(x)&0x00000000000000ffull) << 56)))
+#endif
+#endif
+
+#endif
+
+/* Host swap macros */
+#ifndef __HOSTSWAP_DEFINED
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+#define htobe16(x) bswap16((x))
+#define htole16(x) ((uint16_t)(x))
+#define be16toh(x) bswap16((x))
+#define le16toh(x) ((uint16_t)(x))
+
+#define htobe32(x) bswap32((x))
+#define htole32(x) ((uint32_t)(x))
+#define be32toh(x) bswap32((x))
+#define le32toh(x) ((uint32_t)(x))
+
+#define htobe64(x) bswap64((x))
+#define htole64(x) ((uint64_t)(x))
+#define be64toh(x) bswap64((x))
+#define le64toh(x) ((uint64_t)(x))
+#elif _BYTE_ORDER == _BIG_ENDIAN
+#define htobe16(x) ((uint16_t)(x))
+#define htole16(x) bswap16((x))
+#define be16toh(x) ((uint16_t)(x))
+#define le16toh(x) bswap16((x))
+
+#define htobe32(x) ((uint32_t)(x))
+#define htole32(x) bswap32((x))
+#define be32toh(x) ((uint32_t)(x))
+#define le64toh(x) bswap64((x))
+
+#define htobe64(x) ((uint64_t)(x))
+#define htole64(x) bswap64((x))
+#define be64toh(x) ((uint64_t)(x))
+#define le32toh(x) bswap32((x))
+#endif
+#endif
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 49b1a00..4c610bb 100644 (file)
@@ -36,6 +36,7 @@
 #include <errno.h>
 #include <math.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 
 #elif HAVE_SYS_ENDIAN_H
 #include <sys/endian.h>
 #else /* fallback */
-__attribute__((const)) static uint16_t be16toh(uint16_t n) {
-  uint8_t tmp[2];
-  memmove(tmp, &n, sizeof(tmp));
-
-  return ((uint16_t)tmp[0] << 8) | ((uint16_t)tmp[1] << 0);
-}
-
-__attribute__((const)) static uint64_t be64toh(uint64_t n) {
-  uint8_t tmp[8];
-  memmove(tmp, &n, sizeof(tmp));
-
-  return ((uint64_t)tmp[0] << 56) | ((uint64_t)tmp[1] << 48) |
-         ((uint64_t)tmp[2] << 40) | ((uint64_t)tmp[3] << 32) |
-         ((uint64_t)tmp[4] << 24) | ((uint64_t)tmp[5] << 16) |
-         ((uint64_t)tmp[6] << 8) | ((uint64_t)tmp[7] << 0);
-}
+#include "collectd/stdendian.h"
 #endif
 
 #if HAVE_GCRYPT_H
@@ -83,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 */
@@ -168,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;
 }
 
@@ -235,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];
@@ -317,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;
   }
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 83aca5e..a94ee96 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #include <unistd.h>
 
 #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);
 
 static int load_config(const char *key, const char *value) {
-  if (strcasecmp(key, "ReportRelative") == 0)
+  if (strcasecmp(key, "ReportRelative") == 0) {
 #ifdef _SC_NPROCESSORS_ONLN
-    report_relative_load = IS_TRUE(value) ? 1 : 0;
+    report_relative_load = IS_TRUE(value);
 #else
     WARNING("load plugin: The \"ReportRelative\" configuration "
             "is not available, because I can't determine the "
             "number of CPUS on this system. Sorry.");
 #endif
+    return 0;
+  }
   return -1;
 }
 static void load_submit(gauge_t snum, gauge_t mnum, gauge_t lnum) {
@@ -89,7 +91,9 @@ static void load_submit(gauge_t snum, gauge_t mnum, gauge_t lnum) {
 
   value_list_t vl = VALUE_LIST_INIT;
   value_t values[] = {
-      {.gauge = snum}, {.gauge = mnum}, {.gauge = lnum},
+      {.gauge = snum},
+      {.gauge = mnum},
+      {.gauge = lnum},
   };
 
   vl.values = values;
@@ -114,7 +118,7 @@ static int load_read(void) {
   else {
     WARNING("load: getloadavg failed: %s", STRERRNO);
   }
-/* #endif HAVE_GETLOADAVG */
+    /* #endif HAVE_GETLOADAVG */
 
 #elif defined(KERNEL_LINUX)
   gauge_t snum, mnum, lnum;
@@ -149,7 +153,7 @@ static int load_read(void) {
   lnum = atof(fields[2]);
 
   load_submit(snum, mnum, lnum);
-/* #endif KERNEL_LINUX */
+  /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBSTATGRAB
   gauge_t snum, mnum, lnum;
@@ -162,7 +166,7 @@ static int load_read(void) {
   mnum = ls->min5;
   lnum = ls->min15;
   load_submit(snum, mnum, lnum);
-/* #endif HAVE_LIBSTATGRAB */
+  /* #endif HAVE_LIBSTATGRAB */
 
 #elif HAVE_PERFSTAT
   gauge_t snum, mnum, lnum;
@@ -178,7 +182,7 @@ static int load_read(void) {
   mnum = (float)cputotal.loadavg[1] / (float)(1 << SBITS);
   lnum = (float)cputotal.loadavg[2] / (float)(1 << SBITS);
   load_submit(snum, mnum, lnum);
-/* #endif HAVE_PERFSTAT */
+  /* #endif HAVE_PERFSTAT */
 
 #else
 #error "No applicable input method."
index de34b0e..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,13 +150,13 @@ 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) {
@@ -182,22 +182,14 @@ err:
 
 static void log_logstash_log(int severity, const char *msg,
                              user_data_t __attribute__((unused)) * user_data) {
-  yajl_gen g;
-#if !defined(HAVE_YAJL_V2)
-  yajl_gen_config conf = {};
-
-  conf.beautify = 0;
-#endif
-
   if (severity > log_level)
     return;
 
 #if HAVE_YAJL_V2
-  g = yajl_gen_alloc(NULL);
+  yajl_gen g = yajl_gen_alloc(NULL);
 #else
-  g = yajl_gen_alloc(&conf, NULL);
+  yajl_gen g = yajl_gen_alloc(&(yajl_gen_config){0}, NULL);
 #endif
-
   if (g == NULL) {
     fprintf(stderr, "Could not allocate JSON generator.\n");
     return;
index 6692287..ef75052 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"};
@@ -52,8 +52,8 @@ static int logfile_config(const char *key, const char *value) {
     log_level = parse_log_severity(value);
     if (log_level < 0) {
       log_level = LOG_INFO;
-      ERROR("logfile: invalid loglevel [%s] defaulting to 'info'", value);
-      return 1;
+      WARNING("logfile: invalid loglevel [%s] defaulting to 'info'", value);
+      return 0;
     }
   } else if (0 == strcasecmp(key, "File")) {
     sfree(log_file);
@@ -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,7 +122,7 @@ 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) {
index c4189b7..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;
   }
@@ -84,14 +84,14 @@ static int lpar_init(void) {
 #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;
index 8cfb704..9a1ceed 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
+#define PLUGIN_READ 1
+#define PLUGIN_WRITE 2
 
 typedef struct lua_script_s {
-  char *script_path;
   lua_State *lua_state;
   struct lua_script_s *next;
 } lua_script_t;
 
 typedef struct {
   lua_State *lua_state;
-  const char *lua_function_name;
+  char *lua_function_name;
   pthread_mutex_t lock;
   int callback_id;
 } clua_callback_data_t;
@@ -89,18 +82,14 @@ static int clua_load_callback(lua_State *L, int callback_ref) /* {{{ */
  * garbage collector. */
 static int clua_store_thread(lua_State *L, int idx) /* {{{ */
 {
-  if (idx < 0)
-    idx += lua_gettop(L) + 1;
-
-  /* Copy the thread pointer */
-  lua_pushvalue(L, idx); /* +1 = 3 */
-  if (!lua_isthread(L, -1)) {
-    lua_pop(L, 3); /* -3 = 0 */
+  if (!lua_isthread(L, idx)) {
     return -1;
   }
 
+  /* Copy the thread pointer */
+  lua_pushvalue(L, idx);
+
   luaL_ref(L, LUA_REGISTRYINDEX);
-  lua_pop(L, 1); /* -1 = 0 */
   return 0;
 } /* }}} int clua_store_thread */
 
@@ -272,61 +261,46 @@ static int lua_cb_dispatch_values(lua_State *L) /* {{{ */
   return 0;
 } /* }}} lua_cb_dispatch_values */
 
-static int lua_cb_register_read(lua_State *L) /* {{{ */
+static void lua_cb_free(void *data) {
+  clua_callback_data_t *cb = data;
+  free(cb->lua_function_name);
+  pthread_mutex_destroy(&cb->lock);
+  free(cb);
+}
+
+static int lua_cb_register_generic(lua_State *L, int type) /* {{{ */
 {
   int nargs = lua_gettop(L);
 
   if (nargs != 1)
     return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
 
-  luaL_checktype(L, 1, LUA_TFUNCTION);
-
-  char function_name[DATA_MAX_NAME_LEN];
-  snprintf(function_name, sizeof(function_name), "lua/%s", lua_tostring(L, 1));
+  char subname[DATA_MAX_NAME_LEN];
+  if (!lua_isfunction(L, 1) && lua_isstring(L, 1)) {
+    const char *fname = lua_tostring(L, 1);
+    snprintf(subname, sizeof(subname), "%s()", fname);
 
-  int callback_id = clua_store_callback(L, 1);
-  if (callback_id < 0)
-    return luaL_error(L, "%s", "Storing callback function failed");
-
-  lua_State *thread = lua_newthread(L);
-  if (thread == NULL)
-    return luaL_error(L, "%s", "lua_newthread failed");
-  clua_store_thread(L, -1);
-  lua_pop(L, 1);
-
-  clua_callback_data_t *cb = calloc(1, sizeof(*cb));
-  if (cb == NULL)
-    return luaL_error(L, "%s", "calloc failed");
-
-  cb->lua_state = thread;
-  cb->callback_id = callback_id;
-  cb->lua_function_name = strdup(function_name);
-  pthread_mutex_init(&cb->lock, NULL);
-
-  int status = plugin_register_complex_read(/* group = */ "lua",
-                                            /* name      = */ function_name,
-                                            /* callback  = */ clua_read,
-                                            /* interval  = */ 0,
-                                            &(user_data_t){
-                                                .data = cb,
-                                            });
-
-  if (status != 0)
-    return luaL_error(L, "%s", "plugin_register_complex_read failed");
-  return 0;
-} /* }}} int lua_cb_register_read */
-
-static int lua_cb_register_write(lua_State *L) /* {{{ */
-{
-  int nargs = lua_gettop(L);
-
-  if (nargs != 1)
-    return luaL_error(L, "Invalid number of arguments (%d != 1)", nargs);
+    lua_getglobal(L, fname); // Push function into stack
+    lua_remove(L, 1);        // Remove string from stack
+    if (!lua_isfunction(L, -1)) {
+      return luaL_error(L, "Unable to find function '%s'", fname);
+    }
+  } else {
+    lua_getfield(L, LUA_REGISTRYINDEX, "collectd:callback_num");
+    int tmp = lua_tointeger(L, -1);
+    snprintf(subname, sizeof(subname), "callback_%d", tmp);
+    lua_pop(L, 1); // Remove old value from stack
+    lua_pushinteger(L, tmp + 1);
+    lua_setfield(L, LUA_REGISTRYINDEX, "collectd:callback_num"); // pops value
+  }
 
   luaL_checktype(L, 1, LUA_TFUNCTION);
 
-  char function_name[DATA_MAX_NAME_LEN] = "";
-  snprintf(function_name, sizeof(function_name), "lua/%s", lua_tostring(L, 1));
+  lua_getfield(L, LUA_REGISTRYINDEX, "collectd:script_path");
+  char function_name[DATA_MAX_NAME_LEN];
+  snprintf(function_name, sizeof(function_name), "lua/%s/%s",
+           lua_tostring(L, -1), subname);
+  lua_pop(L, 1);
 
   int callback_id = clua_store_callback(L, 1);
   if (callback_id < 0)
@@ -347,16 +321,41 @@ static int lua_cb_register_write(lua_State *L) /* {{{ */
   cb->lua_function_name = strdup(function_name);
   pthread_mutex_init(&cb->lock, NULL);
 
-  int status = plugin_register_write(/* name = */ function_name,
-                                     /* callback  = */ clua_write,
+  if (PLUGIN_READ == type) {
+    int status =
+        plugin_register_complex_read(/* group = */ "lua",
+                                     /* name      = */ function_name,
+                                     /* callback  = */ clua_read,
+                                     /* interval  = */ 0,
                                      &(user_data_t){
-                                         .data = cb,
+                                         .data = cb, .free_func = lua_cb_free,
                                      });
 
-  if (status != 0)
-    return luaL_error(L, "%s", "plugin_register_write failed");
-  return 0;
-} /* }}} int lua_cb_register_write */
+    if (status != 0)
+      return luaL_error(L, "%s", "plugin_register_complex_read failed");
+    return 0;
+  } else if (PLUGIN_WRITE == type) {
+    int status = plugin_register_write(/* name = */ function_name,
+                                       /* callback  = */ clua_write,
+                                       &(user_data_t){
+                                           .data = cb, .free_func = lua_cb_free,
+                                       });
+
+    if (status != 0)
+      return luaL_error(L, "%s", "plugin_register_write failed");
+    return 0;
+  } else {
+    return luaL_error(L, "%s", "lua_cb_register_generic unsupported type");
+  }
+} /* }}} int lua_cb_register_generic */
+
+static int lua_cb_register_read(lua_State *L) {
+  return lua_cb_register_generic(L, PLUGIN_READ);
+}
+
+static int lua_cb_register_write(lua_State *L) {
+  return lua_cb_register_generic(L, PLUGIN_WRITE);
+}
 
 static const luaL_Reg collectdlib[] = {
     {"log_debug", lua_cb_log_debug},
@@ -391,7 +390,6 @@ static void lua_script_free(lua_script_t *script) /* {{{ */
     script->lua_state = NULL;
   }
 
-  sfree(script->script_path);
   sfree(script);
 
   lua_script_free(next);
@@ -455,14 +453,7 @@ static int lua_script_load(const char *script_path) /* {{{ */
     return status;
   }
 
-  script->script_path = strdup(script_path);
-  if (script->script_path == NULL) {
-    ERROR("Lua plugin: strdup failed.");
-    lua_script_free(script);
-    return -1;
-  }
-
-  status = luaL_loadfile(script->lua_state, script->script_path);
+  status = luaL_loadfile(script->lua_state, script_path);
   if (status != 0) {
     ERROR("Lua plugin: luaL_loadfile failed: %s",
           lua_tostring(script->lua_state, -1));
@@ -471,6 +462,11 @@ static int lua_script_load(const char *script_path) /* {{{ */
     return -1;
   }
 
+  lua_pushstring(script->lua_state, script_path);
+  lua_setfield(script->lua_state, LUA_REGISTRYINDEX, "collectd:script_path");
+  lua_pushinteger(script->lua_state, 0);
+  lua_setfield(script->lua_state, LUA_REGISTRYINDEX, "collectd:callback_num");
+
   status = lua_pcall(script->lua_state,
                      /* nargs = */ 0,
                      /* nresults = */ LUA_MULTRET,
@@ -483,11 +479,8 @@ static int lua_script_load(const char *script_path) /* {{{ */
             "In addition, no error message could be retrieved from the stack.",
             status);
     else
-      ERROR("Lua plugin: Executing script \"%s\" failed:\n%s",
-            script->script_path, errmsg);
-
-    lua_script_free(script);
-    return -1;
+      ERROR("Lua plugin: Executing script \"%s\" failed: %s", script_path,
+            errmsg);
   }
 
   /* Append this script to the global list of scripts. */
@@ -501,6 +494,9 @@ static int lua_script_load(const char *script_path) /* {{{ */
     scripts = script;
   }
 
+  if (status != 0)
+    return -1;
+
   return 0;
 } /* }}} int lua_script_load */
 
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..99f0709 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++) {
@@ -336,21 +336,22 @@ static int mr_match(const data_set_t __attribute__((unused)) * ds, /* {{{ */
   if (mr_match_regexen(m->type_instance, vl->type_instance) ==
       FC_MATCH_NO_MATCH)
     return nomatch_value;
-  if (vl->meta != NULL) {
-    for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next) {
-      mr_regex_t *meta_re = (mr_regex_t *)e->value;
-      char *value;
-      int status = meta_data_get_string(vl->meta, e->key, &value);
-      if (status == (-ENOENT)) /* key is not present */
-        return nomatch_value;
-      if (status != 0) /* some other problem */
-        continue;      /* error will have already been printed. */
-      if (mr_match_regexen(meta_re, value) == FC_MATCH_NO_MATCH) {
-        sfree(value);
-        return nomatch_value;
-      }
+  for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next) {
+    mr_regex_t *meta_re = (mr_regex_t *)e->value;
+    char *value;
+    int status;
+    if (vl->meta == NULL)
+      return nomatch_value;
+    status = meta_data_get_string(vl->meta, e->key, &value);
+    if (status == (-ENOENT)) /* key is not present */
+      return nomatch_value;
+    if (status != 0) /* some other problem */
+      continue;      /* error will have already been printed. */
+    if (mr_match_regexen(meta_re, value) == FC_MATCH_NO_MATCH) {
       sfree(value);
+      return nomatch_value;
     }
+    sfree(value);
   }
 
   return match_value;
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 f58d01e..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
index 1a92a06..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) {
 
index b8df328..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)
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 635088e..eb804d1 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;
@@ -475,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;
 
@@ -509,6 +509,13 @@ static int memcached_read(user_data_t *user_data) {
     }
 
     /*
+     * Number of secs since the server started
+     */
+    else if (FIELD_IS("uptime")) {
+      submit_gauge("uptime", NULL, atof(fields[2]), st);
+    }
+
+    /*
      * Number of bytes used and available (total - used)
      */
     else if (FIELD_IS("bytes")) {
@@ -702,7 +709,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) {
@@ -755,7 +762,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;
@@ -797,7 +804,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 80b1104..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;
@@ -299,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;
 
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 daa3c02..04232c3 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>
@@ -80,6 +80,8 @@ enum mb_register_type_e /* {{{ */
   REG_TYPE_UINT16,
   REG_TYPE_UINT32,
   REG_TYPE_UINT32_CDAB,
+  REG_TYPE_INT64,
+  REG_TYPE_UINT64,
   REG_TYPE_FLOAT,
   REG_TYPE_FLOAT_CDAB }; /* }}} */
 
@@ -95,6 +97,12 @@ enum mb_conntype_e /* {{{ */
   MBCONN_RTU }; /* }}} */
 typedef enum mb_conntype_e mb_conntype_t;
 
+enum mb_uarttype_e /* {{{ */
+{ UARTTYPE_RS232,
+  UARTTYPE_RS422,
+  UARTTYPE_RS485 }; /* }}} */
+typedef enum mb_uarttype_e mb_uarttype_t;
+
 struct mb_data_s;
 typedef struct mb_data_s mb_data_t;
 struct mb_data_s /* {{{ */
@@ -105,6 +113,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;
 }; /* }}} */
@@ -122,10 +132,10 @@ struct mb_host_s /* {{{ */
   char host[DATA_MAX_NAME_LEN];
   char node[NI_MAXHOST]; /* TCP hostname or RTU serial device */
   /* char service[NI_MAXSERV]; */
-  int port;     /* for Modbus/TCP */
-  int baudrate; /* for Modbus/RTU */
+  int port;               /* for Modbus/TCP */
+  int baudrate;           /* for Modbus/RTU */
+  mb_uarttype_t uarttype; /* UART type for Modbus/RTU */
   mb_conntype_t conntype;
-  cdtime_t interval;
 
   mb_slave_t *slaves;
   size_t slaves_num;
@@ -135,7 +145,7 @@ struct mb_host_s /* {{{ */
 #else
   modbus_t *connection;
 #endif
-  _Bool is_connected;
+  bool is_connected;
 }; /* }}} */
 typedef struct mb_host_s mb_host_t;
 
@@ -152,7 +162,7 @@ struct mb_data_group_s /* {{{ */
 /*
  * Global variables
  */
-static mb_data_t *data_definitions = NULL;
+static mb_data_t *data_definitions;
 
 /*
  * Functions
@@ -248,15 +258,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));
@@ -332,7 +338,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,25 +394,41 @@ static int mb_init_connection(mb_host_t *host) /* {{{ */
     return status;
   }
 
+#if defined(linux) && LIBMODBUS_VERSION_CHECK(2, 9, 4)
+  switch (host->uarttype) {
+  case UARTTYPE_RS485:
+    if (modbus_rtu_set_serial_mode(host->connection, MODBUS_RTU_RS485))
+      DEBUG("Modbus plugin: Setting RS485 mode failed.");
+    break;
+  case UARTTYPE_RS422:
+    /* libmodbus doesn't say anything about full-duplex symmetric RS422 UART */
+    break;
+  case UARTTYPE_RS232:
+    break;
+  default:
+    DEBUG("Modbus plugin: Invalid UART type!.");
+  }
+#endif /* defined(linux) && LIBMODBUS_VERSION_CHECK(2, 9, 4) */
+
   return 0;
 } /* }}} int mb_init_connection */
 #endif /* !LEGACY_LIBMODBUS */
 
-#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;
@@ -431,11 +453,13 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
       (data->register_type != REG_TYPE_INT32) &&
       (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_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));
   }
 
@@ -446,6 +470,9 @@ static int mb_read_data(mb_host_t *host, mb_slave_t *slave, /* {{{ */
       (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;
 
@@ -465,7 +492,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;
     }
@@ -530,7 +557,7 @@ 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;
@@ -541,7 +568,7 @@ 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_INT32) {
     union {
@@ -555,7 +582,7 @@ 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 {
@@ -569,7 +596,7 @@ 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_INT16) {
     union {
@@ -584,7 +611,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;
@@ -595,7 +622,7 @@ 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;
@@ -606,7 +633,34 @@ 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_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) */
   {
@@ -616,7 +670,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);
   }
 
@@ -723,6 +777,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)
@@ -736,6 +792,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) {
@@ -759,6 +819,10 @@ static int mb_config_add_data(oconfig_item_t *ci) /* {{{ */
         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;
@@ -906,6 +970,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;
 
@@ -941,12 +1006,36 @@ static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */
         status = -1;
     } else if (strcasecmp("Device", child->key) == 0) {
       status = cf_util_get_string_buffer(child, host->node, sizeof(host->node));
-      if (status == 0)
+      if (status == 0) {
         host->conntype = MBCONN_RTU;
+        host->uarttype = UARTTYPE_RS232;
+      }
     } else if (strcasecmp("Baudrate", child->key) == 0)
       status = cf_util_get_int(child, &host->baudrate);
-    else if (strcasecmp("Interval", child->key) == 0)
-      status = cf_util_get_cdtime(child, &host->interval);
+    else if (strcasecmp("UARTType", child->key) == 0) {
+#if defined(linux) && !LEGACY_LIBMODBUS && LIBMODBUS_VERSION_CHECK(2, 9, 4)
+      char buffer[NI_MAXHOST];
+      status = cf_util_get_string_buffer(child, buffer, sizeof(buffer));
+      if (status != 0)
+        break;
+      if (strncmp(buffer, "RS485", 6) == 0)
+        host->uarttype = UARTTYPE_RS485;
+      else if (strncmp(buffer, "RS422", 6) == 0)
+        host->uarttype = UARTTYPE_RS422;
+      else if (strncmp(buffer, "RS232", 6) == 0)
+        host->uarttype = UARTTYPE_RS232;
+      else {
+        ERROR("Modbus plugin: The UARTType \"%s\" is unknown.", buffer);
+        status = -1;
+        break;
+      }
+#else
+      ERROR("Modbus plugin: Option `UARTType' not supported. Please "
+            "upgrade libmodbus to at least 2.9.4");
+      return -1;
+#endif
+    } else if (strcasecmp("Interval", child->key) == 0)
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Slave", child->key) == 0)
       /* Don't set status: Gracefully continue if a slave fails. */
       mb_config_add_slave(host, child);
@@ -987,7 +1076,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 d134c38..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);
@@ -252,7 +252,7 @@ static int mqtt_reconnect(mqtt_client_conf_t *conf) {
     return -1;
   }
 
-  conf->connected = 1;
+  conf->connected = true;
 
   c_release(LOG_INFO, &conf->complaint_cantpublish,
             "mqtt plugin: successfully reconnected to broker \"%s:%d\"",
@@ -366,7 +366,7 @@ static int mqtt_connect(mqtt_client_conf_t *conf) {
     }
   }
 
-  conf->connected = 1;
+  conf->connected = true;
   return 0;
 } /* mqtt_connect */
 
@@ -393,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,7 +438,7 @@ static int publish(mqtt_client_conf_t *conf, char const *topic,
     /* 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);
@@ -532,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);
@@ -546,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) {
@@ -631,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);
@@ -645,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) {
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 ca9b15d..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>
index 7fe6d76..0eba01f 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;
     }
   }
 
@@ -901,6 +905,8 @@ static int mysql_read(user_data_t *ud) {
 
     } else if (strncmp(key, "Slow_queries", strlen("Slow_queries")) == 0) {
       derive_submit("mysql_slow_queries", NULL, val, db);
+    } else if (strcmp(key, "Uptime") == 0) {
+      gauge_submit("uptime", NULL, val, db);
     }
   }
   mysql_free_result(res);
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 0f71ce7..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);
 }
@@ -357,11 +368,10 @@ static int link_filter_cb(const struct nlmsghdr *nlh,
     if (mnl_attr_get_type(attr) != IFLA_STATS64)
       continue;
 
-    if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*stats.stats64)) < 0) {
-      char errbuf[1024];
-      ERROR("netlink plugin: link_filter_cb: IFLA_STATS64 mnl_attr_validate2 "
-            "failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    uint16_t attr_len = mnl_attr_get_payload_len(attr);
+    if (attr_len < sizeof(*stats.stats64)) {
+      ERROR("netlink plugin: link_filter_cb: IFLA_STATS64 attribute has "
+            "insufficient data.");
       return MNL_CB_ERROR;
     }
     stats.stats64 = mnl_attr_get_payload(attr);
@@ -375,11 +385,10 @@ static int link_filter_cb(const struct nlmsghdr *nlh,
     if (mnl_attr_get_type(attr) != IFLA_STATS)
       continue;
 
-    if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC, sizeof(*stats.stats32)) < 0) {
-      char errbuf[1024];
-      ERROR("netlink plugin: link_filter_cb: IFLA_STATS mnl_attr_validate2 "
-            "failed: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    uint16_t attr_len = mnl_attr_get_payload_len(attr);
+    if (attr_len < sizeof(*stats.stats32)) {
+      ERROR("netlink plugin: link_filter_cb: IFLA_STATS attribute has "
+            "insufficient data.");
       return MNL_CB_ERROR;
     }
     stats.stats32 = mnl_attr_get_payload(attr);
@@ -404,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);
@@ -441,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";
@@ -531,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);
@@ -556,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);
@@ -567,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);
@@ -694,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);
   }
 
@@ -741,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 fcacd38..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);
@@ -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);
@@ -1683,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
@@ -1690,10 +1728,9 @@ 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) {
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) == -1) {
     ERROR("network plugin: setsockopt (reuseaddr): %s", STRERRNO);
     return -1;
   }
@@ -1834,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
@@ -1924,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))
@@ -1938,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*/
@@ -1989,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.. */
@@ -2601,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);
@@ -2684,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;
@@ -2843,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 {
@@ -3096,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);
index f50a352..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
@@ -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) {
@@ -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:
@@ -519,7 +519,8 @@ static int nfs_submit_nfs4_client(const char *instance, char **fields,
 static void nfs_read_linux(FILE *fh, const char *inst) {
   char buffer[1024];
 
-  char *fields[64];
+  // The stats line is prefixed with type and number of fields, thus plus 2
+  char *fields[MAX(NFS4_SERVER_MAX_PROC, NFS4_CLIENT_MAX_PROC) + 2];
   int fields_num = 0;
 
   if (fh == NULL)
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 68f6e2a..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
index 84512d2..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;
   }
@@ -717,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;
@@ -779,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;
@@ -821,11 +810,13 @@ static int ntpd_read(void) {
   if (status != 0) {
     ERROR("ntpd plugin: ntpd_do_query (REQ_GET_KERNEL) failed with status %i",
           status);
+    free(ik);
     return status;
   } else if ((ik == NULL) || (ik_num == 0) || (ik_size == 0)) {
     ERROR("ntpd plugin: ntpd_do_query returned unexpected data. "
           "(ik = %p; ik_num = %i; ik_size = %i)",
           (void *)ik, ik_num, ik_size);
+    free(ik);
     return -1;
   }
 
@@ -860,11 +851,13 @@ static int ntpd_read(void) {
     ERROR(
         "ntpd plugin: ntpd_do_query (REQ_PEER_LIST_SUM) failed with status %i",
         status);
+    free(ps);
     return status;
   } else if ((ps == NULL) || (ps_num == 0) || (ps_size == 0)) {
     ERROR("ntpd plugin: ntpd_do_query returned unexpected data. "
           "(ps = %p; ps_num = %i; ps_size = %i)",
           (void *)ps, ps_num, ps_size);
+    free(ps);
     return -1;
   }
 
@@ -877,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"
@@ -897,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 c68fb86..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."
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 eb64077..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
index c203751..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);
@@ -299,7 +299,7 @@ static int cow_read_values(const char *path, const char *name,
 
     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;
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 608bef6..b0e4bf4 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 */
@@ -139,7 +139,8 @@ static void iostats_submit(const char *pinst, const char *tinst, derive_t rx,
                            derive_t tx) {
   value_list_t vl = VALUE_LIST_INIT;
   value_t values[] = {
-      {.derive = rx}, {.derive = tx},
+      {.derive = rx},
+      {.derive = tx},
   };
 
   /* NOTE ON THE NEW NAMING SCHEMA:
@@ -165,7 +166,8 @@ static void compression_submit(const char *pinst, const char *tinst,
                                derive_t uncompressed, derive_t compressed) {
   value_list_t vl = VALUE_LIST_INIT;
   value_t values[] = {
-      {.derive = uncompressed}, {.derive = compressed},
+      {.derive = uncompressed},
+      {.derive = compressed},
   };
 
   vl.values = values;
@@ -247,7 +249,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 +258,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 +294,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 +322,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 +332,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 +360,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 +406,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 "
@@ -502,14 +504,15 @@ static int openvpn_config(const char *key, const char *value) {
         /* callback  = */ openvpn_read,
         /* interval  = */ 0,
         &(user_data_t){
-            .data = instance, .free_func = openvpn_free,
+            .data = instance,
+            .free_func = openvpn_free,
         });
 
     if (status == EINVAL) {
-      WARNING("openvpn plugin: status filename \"%s\" "
-              "already used, please choose a "
-              "different one.",
-              status_name);
+      ERROR("openvpn plugin: status filename \"%s\" "
+            "already used, please choose a "
+            "different one.",
+            status_name);
       return -1;
     }
 
@@ -519,29 +522,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 bf6aef5..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;
@@ -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..da218b6 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 (size_t i = 0; i < YAJL_GET_ARRAY(ifaces_list)->len; i++) {
+      yajl_val iface_tuple = YAJL_GET_ARRAY(ifaces_list)->values[i];
+
+      // iface_tuple is [ "uuid", "<some_uuid>" ]
+      char *iface_uuid_str =
+          YAJL_GET_STRING(YAJL_GET_ARRAY(iface_tuple)->values[1]);
+
+      // Also checks if interface already registered
+      ovs_stats_new_port_interface(portentry, iface_uuid_str);
+    }
+  } else {
+    // ifaces_root is [ "uuid", "<some_uuid>" ]
+    char *iface_uuid_str =
+        YAJL_GET_STRING(YAJL_GET_ARRAY(ifaces_root)->values[1]);
+
+    // Also checks if interface already registered
+    ovs_stats_new_port_interface(portentry, iface_uuid_str);
+  }
+
   return 0;
 }
 
@@ -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 5ad99ee..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] = "";
 
@@ -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);
@@ -1642,23 +1633,23 @@ static void _plugin_register_generic_userdata(pTHX, int type,
  */
 
 static XS(Collectd_plugin_register_read) {
-  return _plugin_register_generic_userdata(aTHX, PLUGIN_READ, "read");
+  _plugin_register_generic_userdata(aTHX, PLUGIN_READ, "read");
 }
 
 static XS(Collectd_plugin_register_write) {
-  return _plugin_register_generic_userdata(aTHX, PLUGIN_WRITE, "write");
+  _plugin_register_generic_userdata(aTHX, PLUGIN_WRITE, "write");
 }
 
 static XS(Collectd_plugin_register_log) {
-  return _plugin_register_generic_userdata(aTHX, PLUGIN_LOG, "log");
+  _plugin_register_generic_userdata(aTHX, PLUGIN_LOG, "log");
 }
 
 static XS(Collectd_plugin_register_notification) {
-  return _plugin_register_generic_userdata(aTHX, PLUGIN_NOTIF, "notification");
+  _plugin_register_generic_userdata(aTHX, PLUGIN_NOTIF, "notification");
 }
 
 static XS(Collectd_plugin_register_flush) {
-  return _plugin_register_generic_userdata(aTHX, PLUGIN_FLUSH, "flush");
+  _plugin_register_generic_userdata(aTHX, PLUGIN_FLUSH, "flush");
 }
 
 typedef int perl_unregister_function_t(const char *name);
@@ -1685,8 +1676,6 @@ static void _plugin_unregister_generic(pTHX, perl_unregister_function_t *unreg,
   unreg(SvPV_nolen(ST(0)));
 
   XSRETURN_EMPTY;
-
-  return;
 } /* static void _plugin_unregister_generic ( ... ) */
 
 /*
@@ -1700,24 +1689,24 @@ static void _plugin_unregister_generic(pTHX, perl_unregister_function_t *unreg,
  */
 
 static XS(Collectd_plugin_unregister_read) {
-  return _plugin_unregister_generic(aTHX, plugin_unregister_read, "read");
+  _plugin_unregister_generic(aTHX, plugin_unregister_read, "read");
 }
 
 static XS(Collectd_plugin_unregister_write) {
-  return _plugin_unregister_generic(aTHX, plugin_unregister_write, "write");
+  _plugin_unregister_generic(aTHX, plugin_unregister_write, "write");
 }
 
 static XS(Collectd_plugin_unregister_log) {
-  return _plugin_unregister_generic(aTHX, plugin_unregister_log, "log");
+  _plugin_unregister_generic(aTHX, plugin_unregister_log, "log");
 }
 
 static XS(Collectd_plugin_unregister_notification) {
-  return _plugin_unregister_generic(aTHX, plugin_unregister_notification,
-                                    "notification");
+  _plugin_unregister_generic(aTHX, plugin_unregister_notification,
+                             "notification");
 }
 
 static XS(Collectd_plugin_unregister_flush) {
-  return _plugin_unregister_generic(aTHX, plugin_unregister_flush, "flush");
+  _plugin_unregister_generic(aTHX, plugin_unregister_flush, "flush");
 }
 
 /*
@@ -2276,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 1e4c465..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;
 
@@ -103,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 339988d..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,14 +289,13 @@ 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) {
     ERROR("pinba plugin: socket(2) failed: %s", STRERRNO);
     return 0;
   }
 
-  tmp = 1;
-  status = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
+  int status = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
   if (status != 0) {
     WARNING("pinba plugin: setsockopt(SO_REUSEADDR) failed: %s", STRERRNO);
   }
@@ -490,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 */
@@ -585,7 +581,7 @@ static int plugin_init(void) /* {{{ */
     ERROR("pinba plugin: pthread_create(3) failed: %s", STRERRNO);
     return -1;
   }
-  collector_thread_running = 1;
+  collector_thread_running = true;
 
   return 0;
 } /* }}} */
@@ -596,15 +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) {
       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 1ffd72b..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,7 +298,7 @@ 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) {
       ERROR("ping plugin: gettimeofday failed: %s", STRERRNO);
@@ -307,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);
@@ -468,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)
index 62b3d55..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;
 
@@ -936,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");
 
@@ -949,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);
@@ -981,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;
     }
@@ -994,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;
   }
@@ -1097,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;
@@ -1123,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.");
@@ -1163,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)
@@ -1180,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);
@@ -1211,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;
@@ -1221,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 */
@@ -1237,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 729a15b..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>
@@ -279,7 +279,7 @@ static statname_lookup_t lookup_table[] = /* {{{ */
         {"ipv6-questions", "dns_question", "incoming-ipv6"},
         {"malloc-bytes", "gauge", "malloc_bytes"},
         {"max-mthread-stack", "gauge", "max_mthread_stack"},
-        {"no-packet-error", "gauge", "no_packet_error"},
+        {"no-packet-error", "errors", "no_packet_error"},
         {"noedns-outqueries", "dns_question", "outgoing-noedns"},
         {"noping-outqueries", "dns_question", "outgoing-noping"},
         {"over-capacity-drops", "dns_question", "incoming-over_capacity"},
@@ -304,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.
@@ -359,9 +359,6 @@ static void submit(const char *plugin_instance, /* {{{ */
   }
 
   if (0 != parse_value(value_str, &value, ds->ds[0].type)) {
-    ERROR("powerdns plugin: Cannot convert `%s' "
-          "to a number.",
-          value_str);
     return;
   }
 
@@ -472,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 d73d24a..ac5ec60 100644 (file)
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 #if HAVE_LIBTASKSTATS
+#include "utils/taskstats/taskstats.h"
 #include "utils_complain.h"
-#include "utils_taskstats.h"
 #endif
 
 /* Include header files for the mach system, if they exist.. */
@@ -198,25 +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;
 
 #if HAVE_LIBTASKSTATS
   ts_delay_t delay;
 #endif
-  _Bool has_delay;
+  bool has_delay;
 
-  _Bool has_fd;
+  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;
@@ -284,22 +284,22 @@ typedef struct procstat {
   gauge_t delay_swapin;
   gauge_t delay_freepages;
 
-  _Bool report_fd_num;
-  _Bool report_maps_num;
-  _Bool report_ctx_switch;
-  _Bool report_delay;
+  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 report_delay = 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;
@@ -334,7 +334,7 @@ int getargs(void *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
 #endif /* HAVE_PROCINFO_H */
 
 #if HAVE_LIBTASKSTATS
-static ts_t *taskstats_handle = NULL;
+static ts_t *taskstats_handle;
 #endif
 
 /* put name of process from config to list_head_g tree
@@ -587,7 +587,8 @@ static void ps_list_add(const char *name, const char *cmdline,
                       entry->cpu_system_counter);
 
 #if HAVE_LIBTASKSTATS
-    ps_update_delay(ps, pse, entry);
+    if (entry->has_delay)
+      ps_update_delay(ps, pse, entry);
 #endif
   }
 }
@@ -616,7 +617,7 @@ static void ps_list_reset(void) {
     pse_prev = NULL;
     pse = ps->instances;
     while (pse != NULL) {
-      if (pse->age > 10) {
+      if (pse->age > 0) {
         DEBUG("Removing this procstat entry cause it's too old: "
               "id = %lu; name = %s;",
               pse->id, ps->name);
@@ -631,7 +632,7 @@ static void ps_list_reset(void) {
           pse = pse_prev->next;
         }
       } else {
-        pse->age++;
+        pse->age = 1;
         pse_prev = pse;
         pse = pse->next;
       }
@@ -909,7 +910,7 @@ static void ps_submit_proc_list(procstat_t *ps) {
   gauge_t const delay_factor = 1000000000.0;
 
   struct {
-    char *type_instance;
+    const char *type_instance;
     gauge_t rate_ns;
   } delay_metrics[] = {
       {"delay-cpu", ps->delay_cpu},
@@ -924,7 +925,7 @@ static void ps_submit_proc_list(procstat_t *ps) {
     sstrncpy(vl.type, "delay_rate", sizeof(vl.type));
     sstrncpy(vl.type_instance, delay_metrics[i].type_instance,
              sizeof(vl.type_instance));
-    vl.values[0].gauge = delay_metrics[i].rate_ns * delay_factor;
+    vl.values[0].gauge = delay_metrics[i].rate_ns / delay_factor;
     vl.values_len = 1;
     plugin_dispatch_values(&vl);
   }
@@ -995,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;
     }
@@ -1252,38 +1254,38 @@ static int ps_delay(process_entry_t *ps) {
 #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 = 1;
+      entry->has_delay = true;
     }
   }
 #endif
@@ -1521,7 +1523,7 @@ 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) {
@@ -1576,7 +1578,6 @@ static char *ps_get_cmdline(long pid,
     return NULL;
   }
 
-  info.pr_psargs[sizeof(info.pr_psargs) - 1] = 0;
   sstrncpy(buffer, info.pr_psargs, buffer_size);
 
   return buffer;
@@ -2137,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 */
@@ -2292,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  */
@@ -2632,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 a50539e..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;
   }
 
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 7fe0a31..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,                            \
@@ -843,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)
@@ -1146,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 ec8956c..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) /* {{{ */
@@ -272,6 +298,19 @@ static int cr_read(user_data_t *user_data) /* {{{ */
       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;
@@ -333,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);
@@ -355,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 529d29c..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) &&
@@ -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,9 +397,6 @@ static int rc_write(const data_set_t *ds, const value_list_t *vl,
     return -1;
   }
 
-  values_array[0] = values;
-  values_array[1] = NULL;
-
   if (config_create_files) {
     struct stat statbuf;
 
@@ -446,6 +425,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. */
@@ -455,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 */
@@ -472,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)",
@@ -493,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. */
@@ -502,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 e3ad07e..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,
@@ -569,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) {
@@ -624,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;
     }
@@ -790,17 +791,22 @@ 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;
@@ -1010,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 572d41f..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);
@@ -149,7 +65,7 @@ typedef struct featurelist {
 static char *conffile = SENSORS_CONF_PATH;
 /* #endif SENSORS_API_VERSION < 0x400 */
 
-#elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500)
+#elif (SENSORS_API_VERSION >= 0x400)
 typedef struct featurelist {
   const sensors_chip_name *chip;
   const sensors_feature *feature;
@@ -157,48 +73,13 @@ typedef struct featurelist {
   struct featurelist *next;
 } featurelist_t;
 
-static char *conffile = NULL;
-static _Bool use_labels = 0;
-/* #endif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */
-
-#else /* if SENSORS_API_VERSION >= 0x500 */
-#error "This version of libsensors is not supported yet. Please report this " \
-       "as bug."
+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);
@@ -223,9 +104,9 @@ static int sensors_config(const char *key, const char *value) {
     if (IS_TRUE(value))
       ignorelist_set_invert(sensor_list, 0);
   }
-#if (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500)
+#if (SENSORS_API_VERSION >= 0x400)
   else if (strcasecmp(key, "UseLabels") == 0) {
-    use_labels = IS_TRUE(value) ? 1 : 0;
+    use_labels = IS_TRUE(value);
   }
 #endif
   else {
@@ -251,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;
@@ -351,7 +232,7 @@ static int sensors_load_conf(void) {
   }   /* while sensors_get_detected_chips */
 /* #endif SENSORS_API_VERSION < 0x400 */
 
-#elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500)
+#elif (SENSORS_API_VERSION >= 0x400)
   chip_num = 0;
   while ((chip = sensors_get_detected_chips(NULL, &chip_num)) != NULL) {
     const sensors_feature *feature;
@@ -368,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', "
@@ -387,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;
 
@@ -408,7 +295,7 @@ static int sensors_load_conf(void) {
       } /* while (subfeature) */
     }   /* while (feature) */
   }     /* while (chip) */
-#endif /* (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */
+#endif /* (SENSORS_API_VERSION >= 0x400) */
 
   if (first_feature == NULL) {
     sensors_cleanup();
@@ -483,7 +370,7 @@ static int sensors_read(void) {
   } /* for fl = first_feature .. NULL */
 /* #endif SENSORS_API_VERSION < 0x400 */
 
-#elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500)
+#elif (SENSORS_API_VERSION >= 0x400)
   for (featurelist_t *fl = first_feature; fl != NULL; fl = fl->next) {
     double value;
     int status;
@@ -521,12 +408,16 @@ 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;
 
     sensors_submit(plugin_instance, type, type_instance, value);
   } /* for fl = first_feature .. NULL */
-#endif /* (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */
+#endif /* (SENSORS_API_VERSION >= 0x400) */
 
   return 0;
 } /* int sensors_read */
index 2b77db6..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."
@@ -59,7 +59,8 @@ static int serial_read(void) {
   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];
@@ -78,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]);
@@ -87,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 eeab8c9..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>
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 d0f9e84..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 = "
-        "%" PRIsz " }",
-        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,99 +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), "%" PRIu64,
-             (uint64_t)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);
@@ -1130,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;
@@ -1164,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. */
@@ -1189,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;
@@ -1233,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) {
@@ -1262,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);
@@ -1305,31 +1610,48 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
   }
   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);
@@ -1442,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;
@@ -1484,10 +1902,9 @@ 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)) {
+         * 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);
@@ -1508,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] */
@@ -1531,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 */
@@ -1599,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) {
@@ -1666,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);
 
@@ -1713,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 b213adb..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[%" PRIsz "]: %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[%" PRIsz "]: %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 ccd15eb..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 */
@@ -490,8 +489,8 @@ 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) {
@@ -499,15 +498,24 @@ static int statsd_network_init(struct pollfd **ret_fds, /* {{{ */
       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) {
-      ERROR("statsd plugin: bind(2) failed: %s", STRERRNO);
+      ERROR("statsd plugin: bind(2) to [%s]:%s failed: %s", str_node,
+            str_service, STRERRNO);
       close(fd);
       continue;
     }
@@ -525,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);
@@ -666,7 +675,7 @@ static int statsd_init(void) /* {{{ */
       return status;
     }
   }
-  network_thread_running = 1;
+  network_thread_running = true;
 
   pthread_mutex_unlock(&metrics_lock);
 
@@ -717,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();
@@ -876,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 dfca67b..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 */
 
@@ -332,52 +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) {
-      WARNING("swap: fopen: %s", STRERRNO);
-      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) */
 
@@ -556,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);
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..d3be5c9 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,10 +38,11 @@ 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",
+    "LogLevel",
+    "NotifyLevel",
 };
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
@@ -56,7 +57,8 @@ static int sl_config(const char *key, const char *value) {
   } else if (strcasecmp(key, "NotifyLevel") == 0) {
     notif_severity = parse_notif_severity(value);
     if (notif_severity < 0)
-      return 1;
+      ERROR("syslog: invalid notification severity [%s]", value);
+    return 1;
   }
 
   return 0;
index 20f0275..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,8 +147,8 @@ 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) {
+  size_t *tmp = realloc(*var, ((*len) + num) * sizeof(**var));
+  if (tmp == NULL) {
     log_err("realloc failed: %s.", STRERRNO);
     return -1;
   }
@@ -178,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;
   }
@@ -198,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\" "
@@ -214,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;
   }
@@ -236,13 +221,13 @@ 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) {
+  if (tbl == NULL) {
     log_err("realloc failed: %s.", STRERRNO);
     return -1;
   }
@@ -252,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\" "
@@ -270,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;
   }
@@ -313,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);
@@ -330,7 +315,7 @@ 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;
     }
@@ -357,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;
   }
 
@@ -378,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 {
@@ -395,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);
@@ -414,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;
@@ -436,8 +414,8 @@ static int tbl_parse_line(tbl_t *tbl, char *line, size_t len) {
   }
 
   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;
     }
@@ -445,29 +423,28 @@ 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) {
+  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)) {
+  if (ferror(fh) != 0) {
     log_err("Failed to read from file \"%s\": %s.", tbl->file, STRERRNO);
     fclose(fh);
     return -1;
@@ -484,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);
@@ -511,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 f963528..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 %" 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)
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 54d4164..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}};
@@ -306,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;
@@ -470,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,
@@ -484,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.",
@@ -546,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 3889d0f..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;
@@ -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,7 +506,7 @@ 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;
@@ -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 ae9200c..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"};
@@ -255,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 "
@@ -309,7 +309,7 @@ static int tss2_receive_line(FILE *fh, char *buffer, int buffer_size) {
     return -1;
   }
 
-  buffer[buffer_size - 1] = 0;
+  buffer[buffer_size - 1] = '\0';
   return 0;
 } /* int tss2_receive_line */
 
@@ -337,7 +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) &&
@@ -379,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 b803681..ff650a8 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;
 
@@ -243,7 +243,7 @@ static int ted_config(const char *key, const char *value) {
 
     tmp = atoi(value);
     if (tmp < 0) {
-      WARNING("ted plugin: Invalid retry count: %i", tmp);
+      ERROR("ted plugin: Invalid retry count: %i", tmp);
       return 1;
     }
     conf_retries = tmp;
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 85f7d87..ce57942 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;
@@ -985,10 +1038,12 @@ static int __attribute__((warn_unused_result)) probe_cpu(void) {
     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 */
@@ -1023,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
@@ -1227,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)
@@ -1241,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);
@@ -1289,15 +1345,15 @@ static int allocate_counters(struct thread_data **threads,
   *cores = calloc(total_cores, sizeof(struct core_data));
   if (*cores == NULL) {
     ERROR("turbostat plugin: calloc failed");
-    sfree(threads);
+    sfree(*threads);
     return -1;
   }
 
   *packages = calloc(topology.num_packages, sizeof(struct pkg_data));
   if (*packages == NULL) {
     ERROR("turbostat plugin: calloc failed");
-    sfree(cores);
-    sfree(threads);
+    sfree(*cores);
+    sfree(*threads);
     return -1;
   }
 
@@ -1338,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;
@@ -1400,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;
 }
 
+static int save_affinity(void) {
+  if (affinity_policy == policy_restore_affinity) {
+    /* Try to save the scheduling affinity, as it will be modified by
+     * get_counters().
+     */
+    if (sched_getaffinity(0, cpu_saved_affinity_setsize,
+                          cpu_saved_affinity_set) != 0)
+      return -1;
+  }
+
+  return 0;
+}
+
+static void restore_affinity(void) {
+  /* Let's restore the affinity to the value saved in save_affinity */
+  if (affinity_policy == policy_restore_affinity)
+    (void)sched_setaffinity(0, cpu_saved_affinity_setsize,
+                            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;
 
@@ -1426,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;
   }
 
@@ -1437,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;
   }
@@ -1447,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;
@@ -1457,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;
@@ -1466,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;
 }
 
@@ -1554,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) {
@@ -1562,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) {
@@ -1581,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) {
@@ -1589,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 f27ba46..69f59b0 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
@@ -50,6 +51,8 @@ df                      used:GAUGE:0:1125899906842623, free:GAUGE:0:112589990684
 df_complex              value:GAUGE:0:U
 df_inodes               value:GAUGE:0:U
 dilution_of_precision   value:GAUGE:0:U
+disk_allocation         value:GAUGE:0:U
+disk_capacity           value:GAUGE:0:U
 disk_error              value:GAUGE:0:U
 disk_io_time            io_time:DERIVE:0:U, weighted_io_time:DERIVE:0:U
 disk_latency            read:GAUGE:0:U, write:GAUGE:0:U
@@ -57,6 +60,7 @@ disk_merged             read:DERIVE:0:U, write:DERIVE:0:U
 disk_octets             read:DERIVE:0:U, write:DERIVE:0:U
 disk_ops                read:DERIVE:0:U, write:DERIVE:0:U
 disk_ops_complex        value:DERIVE:0:U
+disk_physical           value:GAUGE:0:U
 disk_time               read:DERIVE:0:U, write:DERIVE:0:U
 dns_answer              value:DERIVE:0:U
 dns_notify              value:DERIVE:0:U
@@ -111,6 +115,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
@@ -207,9 +212,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
@@ -236,6 +243,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
@@ -254,7 +262,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 bceafe6..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;
 
@@ -208,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) {
@@ -222,7 +221,7 @@ static void *us_handle_client(void *arg) {
       break;
     }
 
-    len = strlen(buffer);
+    size_t len = strlen(buffer);
     while ((len > 0) &&
            ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
       buffer[--len] = '\0';
@@ -350,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;
   }
@@ -361,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;
 
index 43d72e5..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>
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..4f449a3
--- /dev/null
@@ -0,0 +1,324 @@
+/**
+ * collectd - src/tests/utils_cmds_test.c
+ * Copyright (C) 2016       Sebastian 'tokkee' Harl
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+// clang-format off
+/*
+ * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris
+ * See Github Issue #3193 for details
+ */
+#include "utils/common/common.h"
+#include "testing.h"
+#include "utils/cmds/cmds.h"
+// clang-format on
+
+static void error_cb(void *ud, cmd_status_t status, const char *format,
+                     va_list ap) {
+  if (status == CMD_OK)
+    return;
+
+  printf("ERROR[%d]: ", status);
+  vprintf(format, ap);
+  printf("\n");
+  fflush(stdout);
+} /* void error_cb */
+
+static cmd_options_t default_host_opts = {
+    /* identifier_default_host = */ "dummy-host",
+};
+
+static struct {
+  char *input;
+  cmd_options_t *opts;
+  cmd_status_t expected_status;
+  cmd_type_t expected_type;
+} parse_data[] = {
+    /* Valid FLUSH commands. */
+    {
+        "FLUSH",
+        NULL,
+        CMD_OK,
+        CMD_FLUSH,
+    },
+    {
+        "FLUSH identifier=myhost/magic/MAGIC",
+        NULL,
+        CMD_OK,
+        CMD_FLUSH,
+    },
+    {
+        "FLUSH identifier=magic/MAGIC",
+        &default_host_opts,
+        CMD_OK,
+        CMD_FLUSH,
+    },
+    {
+        "FLUSH timeout=123 plugin=\"A\"",
+        NULL,
+        CMD_OK,
+        CMD_FLUSH,
+    },
+    /* Invalid FLUSH commands. */
+    {
+        /* Missing hostname; no default. */
+        "FLUSH identifier=magic/MAGIC",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        /* Missing 'identifier' key. */
+        "FLUSH myhost/magic/MAGIC",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        /* Invalid timeout. */
+        "FLUSH timeout=A",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        /* Invalid identifier. */
+        "FLUSH identifier=invalid",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        /* Invalid option. */
+        "FLUSH invalid=option",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+
+    /* Valid GETVAL commands. */
+    {
+        "GETVAL myhost/magic/MAGIC",
+        NULL,
+        CMD_OK,
+        CMD_GETVAL,
+    },
+    {
+        "GETVAL magic/MAGIC",
+        &default_host_opts,
+        CMD_OK,
+        CMD_GETVAL,
+    },
+
+    /* Invalid GETVAL commands. */
+    {
+        "GETVAL magic/MAGIC",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        "GETVAL",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        "GETVAL invalid",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+
+    /* Valid LISTVAL commands. */
+    {
+        "LISTVAL",
+        NULL,
+        CMD_OK,
+        CMD_LISTVAL,
+    },
+
+    /* Invalid LISTVAL commands. */
+    {
+        "LISTVAL invalid",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+
+    /* Valid PUTVAL commands. */
+    {
+        "PUTVAL magic/MAGIC N:42",
+        &default_host_opts,
+        CMD_OK,
+        CMD_PUTVAL,
+    },
+    {
+        "PUTVAL myhost/magic/MAGIC N:42",
+        NULL,
+        CMD_OK,
+        CMD_PUTVAL,
+    },
+    {
+        "PUTVAL myhost/magic/MAGIC 1234:42",
+        NULL,
+        CMD_OK,
+        CMD_PUTVAL,
+    },
+    {
+        "PUTVAL myhost/magic/MAGIC 1234:42 2345:23",
+        NULL,
+        CMD_OK,
+        CMD_PUTVAL,
+    },
+    {
+        "PUTVAL myhost/magic/MAGIC interval=2 1234:42",
+        NULL,
+        CMD_OK,
+        CMD_PUTVAL,
+    },
+    {
+        "PUTVAL myhost/magic/MAGIC interval=2 1234:42 interval=5 2345:23",
+        NULL,
+        CMD_OK,
+        CMD_PUTVAL,
+    },
+
+    /* Invalid PUTVAL commands. */
+    {
+        "PUTVAL magic/MAGIC N:42",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        "PUTVAL",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        "PUTVAL invalid N:42",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        "PUTVAL myhost/magic/MAGIC A:42",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        "PUTVAL myhost/magic/MAGIC 1234:A",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        "PUTVAL myhost/magic/MAGIC",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        "PUTVAL 1234:A",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    {
+        "PUTVAL myhost/magic/UNKNOWN 1234:42",
+        NULL,
+        CMD_PARSE_ERROR,
+        CMD_UNKNOWN,
+    },
+    /*
+     * As of collectd 5.x, PUTVAL accepts invalid options.
+    {
+            "PUTVAL myhost/magic/MAGIC invalid=2 1234:42",
+            NULL,
+            CMD_PARSE_ERROR,
+            CMD_UNKNOWN,
+    },
+    */
+
+    /* Invalid commands. */
+    {
+        "INVALID",
+        NULL,
+        CMD_UNKNOWN_COMMAND,
+        CMD_UNKNOWN,
+    },
+    {
+        "INVALID interval=2",
+        NULL,
+        CMD_UNKNOWN_COMMAND,
+        CMD_UNKNOWN,
+    },
+};
+
+DEF_TEST(parse) {
+  cmd_error_handler_t err = {error_cb, NULL};
+  int test_result = 0;
+
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(parse_data); i++) {
+    char *input = strdup(parse_data[i].input);
+
+    char description[1024];
+    cmd_status_t status;
+    cmd_t cmd;
+
+    bool result;
+
+    memset(&cmd, 0, sizeof(cmd));
+
+    status = cmd_parse(input, &cmd, parse_data[i].opts, &err);
+    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..4f15c16
--- /dev/null
@@ -0,0 +1,390 @@
+/**
+ * collectd - src/tests/test_common.c
+ * Copyright (C) 2013       Florian octo Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ */
+
+// clang-format off
+/*
+ * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris
+ * See Github Issue #3193 for details
+ */
+#include "utils/common/common.h"
+#include "testing.h"
+// clang-format on
+
+#if HAVE_KSTAT_H
+#include <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..fb5cb4f
--- /dev/null
@@ -0,0 +1,1077 @@
+/*
+ * collectd - src/utils_dns.c
+ * Copyright (C) 2006       Florian octo Forster
+ * Copyright (C) 2002       The Measurement Factory, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of The Measurement Factory nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors:
+ *   The Measurement Factory, Inc. <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_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];
+  // clang-format off
+  /*
+  Built (with minor cleanup) from glibc-2.29 by
+    cat resolv/arpa/nameser.h | grep "ns_t_" | \
+    perl -ne '/ns_t_(\S+)\ =\ (\d+)/; print "  case $2:\n    return \"".uc($1)."\";\n";'
+  */
+  // clang-format on
+  switch (t) {
+  case 1:
+    return "A";
+  case 2:
+    return "NS";
+  case 3:
+    return "MD";
+  case 4:
+    return "MF";
+  case 5:
+    return "CNAME";
+  case 6:
+    return "SOA";
+  case 7:
+    return "MB";
+  case 8:
+    return "MG";
+  case 9:
+    return "MR";
+  case 10:
+    return "NULL";
+  case 11:
+    return "WKS";
+  case 12:
+    return "PTR";
+  case 13:
+    return "HINFO";
+  case 14:
+    return "MINFO";
+  case 15:
+    return "MX";
+  case 16:
+    return "TXT";
+  case 17:
+    return "RP";
+  case 18:
+    return "AFSDB";
+  case 19:
+    return "X25";
+  case 20:
+    return "ISDN";
+  case 21:
+    return "RT";
+  case 22:
+    return "NSAP";
+  case 23:
+    return "NSAP-PTR";
+  case 24:
+    return "SIG";
+  case 25:
+    return "KEY";
+  case 26:
+    return "PX";
+  case 27:
+    return "GPOS";
+  case 28:
+    return "AAAA";
+  case 29:
+    return "LOC";
+  case 30:
+    return "NXT";
+  case 31:
+    return "EID";
+  case 32:
+    return "NIMLOC";
+  case 33:
+    return "SRV";
+  case 34:
+    return "ATMA";
+  case 35:
+    return "NAPTR";
+  case 36:
+    return "KX";
+  case 37:
+    return "CERT";
+  case 38:
+    return "A6";
+  case 39:
+    return "DNAME";
+  case 40:
+    return "SINK";
+  case 41:
+    return "OPT";
+  case 42:
+    return "APL";
+  case 43:
+    return "DS";
+  case 44:
+    return "SSHFP";
+  case 45:
+    return "IPSECKEY";
+  case 46:
+    return "RRSIG";
+  case 47:
+    return "NSEC";
+  case 48:
+    return "DNSKEY";
+  case 49:
+    return "DHCID";
+  case 50:
+    return "NSEC3";
+  case 51:
+    return "NSEC3PARAM";
+  case 52:
+    return "TLSA";
+  case 53:
+    return "SMIMEA";
+  case 55:
+    return "HIP";
+  case 56:
+    return "NINFO";
+  case 57:
+    return "RKEY";
+  case 58:
+    return "TALINK";
+  case 59:
+    return "CDS";
+  case 60:
+    return "CDNSKEY";
+  case 61:
+    return "OPENPGPKEY";
+  case 62:
+    return "CSYNC";
+  case 99:
+    return "SPF";
+  case 100:
+    return "UINFO";
+  case 101:
+    return "UID";
+  case 102:
+    return "GID";
+  case 103:
+    return "UNSPEC";
+  case 104:
+    return "NID";
+  case 105:
+    return "L32";
+  case 106:
+    return "L64";
+  case 107:
+    return "LP";
+  case 108:
+    return "EUI48";
+  case 109:
+    return "EUI64";
+  case 249:
+    return "TKEY";
+  case 250:
+    return "TSIG";
+  case 251:
+    return "IXFR";
+  case 252:
+    return "AXFR";
+  case 253:
+    return "MAILB";
+  case 254:
+    return "MAILA";
+  case 255:
+    return "ANY";
+  case 256:
+    return "URI";
+  case 257:
+    return "CAA";
+  case 258:
+    return "AVC";
+  case 32768:
+    return "TA";
+  case 32769:
+    return "DLV";
+  default:
+    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];
+  /* RFC2136 rcodes */
+  // clang-format off
+  /*
+  Built (with minor cleanup) from glibc-2.29 by
+    cat resolv/arpa/nameser.h | grep "ns_r_" | \
+    perl -ne '/ns_r_(\S+)\ =\ (\d+)/; print "  case $2:\n    return \"".uc($1)."\";\n";'
+
+  https://tools.ietf.org/html/rfc2671 assigns EDNS Extended RCODE "16" to "BADVERS"
+  https://tools.ietf.org/html/rfc2845 declares 0..15 as DNS RCODE and 16 is BADSIG.
+  */
+  // clang-format on
+  switch (rcode) {
+  case 1:
+    return "FORMERR";
+  case 2:
+    return "SERVFAIL";
+  case 3:
+    return "NXDOMAIN";
+  case 4:
+    return "NOTIMPL";
+  case 5:
+    return "REFUSED";
+  case 6:
+    return "YXDOMAIN";
+  case 7:
+    return "YXRRSET";
+  case 8:
+    return "NXRRSET";
+  case 9:
+    return "NOTAUTH";
+  case 10:
+    return "NOTZONE";
+  case 11:
+    return "MAX";
+  case 16:
+    return "BADSIG";
+  case 17:
+    return "BADKEY";
+  case 18:
+    return "BADTIME";
+  default:
+    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..9cc49b6
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * collectd - src/utils_dns.h
+ * Copyright (C) 2006       Florian octo Forster
+ * Copyright (C) 2002       The Measurement Factory, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of The Measurement Factory nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors:
+ *   The Measurement Factory, Inc. <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 <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..ffef3e2
--- /dev/null
@@ -0,0 +1,377 @@
+/**
+ * collectd - src/utils_format_graphite.c
+ * Copyright (C) 2012  Thomas Meson
+ * Copyright (C) 2012  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Thomas Meson <zllak at hycik.org>
+ *   Florian octo Forster <octo at collectd.org>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+
+#include "utils/format_graphite/format_graphite.h"
+#include "utils_cache.h"
+
+#define GRAPHITE_FORBIDDEN " \t\"\\:!/()\n\r"
+
+/* Utils functions to format data sets in graphite format.
+ * Largely taken from write_graphite.c as it remains the same formatting */
+
+/* helper function for reverse_hostname */
+void reverse_string(char *r_host, int len) {
+  for (int i = 0, j = len - 1; i < j; i++, j--) {
+    char t = r_host[i];
+    r_host[i] = r_host[j];
+    r_host[j] = t;
+  }
+}
+
+void reverse_hostname(char *r_host, char const *orig_host) {
+  int len_host = strlen(orig_host);
+
+  /* put reversed hostname into working copy */
+  for (int i = 0; i < len_host; i++)
+    r_host[i] = orig_host[len_host - 1 - i];
+  r_host[len_host] = '\0';
+
+  /* reverse labels (except last) */
+  int p = 0;
+  for (int i = 0; i < len_host; i++)
+    if (r_host[i] == '.') {
+      reverse_string(&r_host[p], i - p);
+      p = i + 1;
+    }
+
+  /* reverse last label */
+  reverse_string(&r_host[p], len_host - p);
+}
+
+static int gr_format_values(char *ret, size_t ret_len, int ds_num,
+                            const data_set_t *ds, const value_list_t *vl,
+                            gauge_t const *rates) {
+  size_t offset = 0;
+  int status;
+
+  assert(0 == strcmp(ds->type, vl->type));
+
+  memset(ret, 0, ret_len);
+
+#define BUFFER_ADD(...)                                                        \
+  do {                                                                         \
+    status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__);            \
+    if (status < 1) {                                                          \
+      return -1;                                                               \
+    } else if (((size_t)status) >= (ret_len - offset)) {                       \
+      return -1;                                                               \
+    } else                                                                     \
+      offset += ((size_t)status);                                              \
+  } while (0)
+
+  if (ds->ds[ds_num].type == DS_TYPE_GAUGE)
+    BUFFER_ADD(GAUGE_FORMAT, vl->values[ds_num].gauge);
+  else if (rates != NULL)
+    BUFFER_ADD("%f", rates[ds_num]);
+  else if (ds->ds[ds_num].type == DS_TYPE_COUNTER)
+    BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_num].counter);
+  else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
+    BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive);
+  else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
+    BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute);
+  else {
+    P_ERROR("gr_format_values: Unknown data source type: %i",
+            ds->ds[ds_num].type);
+    return -1;
+  }
+
+#undef BUFFER_ADD
+
+  return 0;
+}
+
+static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len,
+                                char escape_char, bool preserve_separator) {
+  memset(dst, 0, dst_len);
+
+  if (src == NULL)
+    return;
+
+  for (size_t i = 0; i < dst_len; i++) {
+    if (src[i] == 0) {
+      dst[i] = 0;
+      break;
+    }
+
+    if ((!preserve_separator && (src[i] == '.')) || isspace((int)src[i]) ||
+        iscntrl((int)src[i]))
+      dst[i] = escape_char;
+    else
+      dst[i] = src[i];
+  }
+}
+
+static int gr_format_name_tagged(char *ret, int ret_len, value_list_t const *vl,
+                                 char const *ds_name, char const *prefix,
+                                 char const *postfix, char const escape_char,
+                                 unsigned int flags) {
+  char n_host[DATA_MAX_NAME_LEN];
+  char n_plugin[DATA_MAX_NAME_LEN];
+  char n_plugin_instance[DATA_MAX_NAME_LEN];
+  char n_type[DATA_MAX_NAME_LEN];
+  char n_type_instance[DATA_MAX_NAME_LEN];
+
+  char tmp_plugin[DATA_MAX_NAME_LEN + 8];
+  char tmp_plugin_instance[DATA_MAX_NAME_LEN + 17];
+  char tmp_type[DATA_MAX_NAME_LEN + 6];
+  char tmp_type_instance[DATA_MAX_NAME_LEN + 15];
+  char tmp_metric[3 * DATA_MAX_NAME_LEN + 2];
+  char tmp_ds_name[DATA_MAX_NAME_LEN + 9];
+
+  if (prefix == NULL)
+    prefix = "";
+
+  if (postfix == NULL)
+    postfix = "";
+
+  if (flags & GRAPHITE_REVERSE_HOST) {
+    char r_host[DATA_MAX_NAME_LEN];
+    reverse_hostname(r_host, vl->host);
+    gr_copy_escape_part(n_host, r_host, sizeof(n_host), escape_char, 1);
+  } else {
+    gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, 1);
+  }
+  gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char, 1);
+  gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
+                      sizeof(n_plugin_instance), escape_char, 1);
+  gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char, 1);
+  gr_copy_escape_part(n_type_instance, vl->type_instance,
+                      sizeof(n_type_instance), escape_char, 1);
+
+  snprintf(tmp_plugin, sizeof(tmp_plugin), ";plugin=%s", n_plugin);
+
+  if (n_plugin_instance[0] != '\0')
+    snprintf(tmp_plugin_instance, sizeof(tmp_plugin_instance),
+             ";plugin_instance=%s", n_plugin_instance);
+  else
+    tmp_plugin_instance[0] = '\0';
+
+  if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) || strcmp(n_plugin, n_type) != 0)
+    snprintf(tmp_type, sizeof(tmp_type), ";type=%s", n_type);
+  else
+    tmp_type[0] = '\0';
+
+  if (n_type_instance[0] != '\0') {
+    if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) ||
+        strcmp(n_plugin_instance, n_type_instance) != 0)
+      snprintf(tmp_type_instance, sizeof(tmp_type_instance),
+               ";type_instance=%s", n_type_instance);
+    else
+      tmp_type_instance[0] = '\0';
+  } else
+    tmp_type_instance[0] = '\0';
+
+  /* Assert always_append_ds -> ds_name */
+  assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
+  if (ds_name != NULL) {
+    snprintf(tmp_ds_name, sizeof(tmp_ds_name), ";ds_name=%s", ds_name);
+
+    if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, ds_name);
+    else
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s.%s", n_plugin, n_type,
+               ds_name);
+  } else {
+    tmp_ds_name[0] = '\0';
+
+    if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s", n_plugin);
+    else
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, n_type);
+  }
+
+  snprintf(ret, ret_len, "%s%s%s;host=%s%s%s%s%s%s", prefix, tmp_metric,
+           postfix, n_host, tmp_plugin, tmp_plugin_instance, tmp_type,
+           tmp_type_instance, tmp_ds_name);
+
+  return 0;
+}
+
+static int gr_format_name(char *ret, int ret_len, value_list_t const *vl,
+                          char const *ds_name, char const *prefix,
+                          char const *postfix, char const escape_char,
+                          unsigned int flags) {
+  char n_host[DATA_MAX_NAME_LEN];
+  char n_plugin[DATA_MAX_NAME_LEN];
+  char n_plugin_instance[DATA_MAX_NAME_LEN];
+  char n_type[DATA_MAX_NAME_LEN];
+  char n_type_instance[DATA_MAX_NAME_LEN];
+
+  char tmp_plugin[2 * DATA_MAX_NAME_LEN + 1];
+  char tmp_type[2 * DATA_MAX_NAME_LEN + 1];
+
+  if (prefix == NULL)
+    prefix = "";
+
+  if (postfix == NULL)
+    postfix = "";
+
+  bool preserve_separator = (flags & GRAPHITE_PRESERVE_SEPARATOR);
+
+  if (flags & GRAPHITE_REVERSE_HOST) {
+    char r_host[DATA_MAX_NAME_LEN];
+    reverse_hostname(r_host, vl->host);
+    gr_copy_escape_part(n_host, r_host, sizeof(n_host), escape_char,
+                        preserve_separator);
+  } else {
+    gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char,
+                        preserve_separator);
+  }
+  gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char,
+                      preserve_separator);
+  gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
+                      sizeof(n_plugin_instance), escape_char,
+                      preserve_separator);
+  gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char,
+                      preserve_separator);
+  gr_copy_escape_part(n_type_instance, vl->type_instance,
+                      sizeof(n_type_instance), escape_char, preserve_separator);
+
+  if (n_plugin_instance[0] != '\0')
+    snprintf(tmp_plugin, sizeof(tmp_plugin), "%s%c%s", n_plugin,
+             (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+             n_plugin_instance);
+  else
+    sstrncpy(tmp_plugin, n_plugin, sizeof(tmp_plugin));
+
+  if (n_type_instance[0] != '\0') {
+    if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+      sstrncpy(tmp_type, n_type_instance, sizeof(tmp_type));
+    else
+      snprintf(tmp_type, sizeof(tmp_type), "%s%c%s", n_type,
+               (flags & GRAPHITE_SEPARATE_INSTANCES) ? '.' : '-',
+               n_type_instance);
+  } else
+    sstrncpy(tmp_type, n_type, sizeof(tmp_type));
+
+  /* Assert always_append_ds -> ds_name */
+  assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
+  if (ds_name != NULL) {
+    if ((flags & GRAPHITE_DROP_DUPE_FIELDS) &&
+        strcmp(tmp_plugin, tmp_type) == 0)
+      snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix,
+               tmp_plugin, ds_name);
+    else
+      snprintf(ret, ret_len, "%s%s%s.%s.%s.%s", prefix, n_host, postfix,
+               tmp_plugin, tmp_type, ds_name);
+  } else
+    snprintf(ret, ret_len, "%s%s%s.%s.%s", prefix, n_host, postfix, tmp_plugin,
+             tmp_type);
+
+  return 0;
+}
+
+static void escape_graphite_string(char *buffer, char escape_char) {
+  assert(strchr(GRAPHITE_FORBIDDEN, escape_char) == NULL);
+
+  for (char *head = buffer + strcspn(buffer, GRAPHITE_FORBIDDEN); *head != '\0';
+       head += strcspn(head, GRAPHITE_FORBIDDEN))
+    *head = escape_char;
+}
+
+int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds,
+                    value_list_t const *vl, char const *prefix,
+                    char const *postfix, char const escape_char,
+                    unsigned int flags) {
+  int status = 0;
+  int buffer_pos = 0;
+
+  gauge_t *rates = NULL;
+  if (flags & GRAPHITE_STORE_RATES) {
+    rates = uc_get_rate(ds, vl);
+    if (rates == NULL) {
+      P_ERROR("format_graphite: error with uc_get_rate");
+      return -1;
+    }
+  }
+
+  for (size_t i = 0; i < ds->ds_num; i++) {
+    char const *ds_name = NULL;
+    char key[10 * DATA_MAX_NAME_LEN];
+    char values[512];
+    size_t message_len;
+    char message[1024];
+
+    if ((flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds->ds_num > 1))
+      ds_name = ds->ds[i].name;
+
+    /* Copy the identifier to `key' and escape it. */
+    if (flags & GRAPHITE_USE_TAGS) {
+      status = gr_format_name_tagged(key, sizeof(key), vl, ds_name, prefix,
+                                     postfix, escape_char, flags);
+      if (status != 0) {
+        P_ERROR("format_graphite: error with gr_format_name_tagged");
+        sfree(rates);
+        return status;
+      }
+    } else {
+      status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix,
+                              escape_char, flags);
+      if (status != 0) {
+        P_ERROR("format_graphite: error with gr_format_name");
+        sfree(rates);
+        return status;
+      }
+    }
+
+    escape_graphite_string(key, escape_char);
+
+    /* Convert the values to an ASCII representation and put that into
+     * `values'. */
+    status = gr_format_values(values, sizeof(values), i, ds, vl, rates);
+    if (status != 0) {
+      P_ERROR("format_graphite: error with gr_format_values");
+      sfree(rates);
+      return status;
+    }
+
+    /* Compute the graphite command */
+    message_len =
+        (size_t)snprintf(message, sizeof(message), "%s %s %u\r\n", key, values,
+                         (unsigned int)CDTIME_T_TO_TIME_T(vl->time));
+    if (message_len >= sizeof(message)) {
+      P_ERROR("format_graphite: message buffer too small: "
+              "Need %" PRIsz " bytes.",
+              message_len + 1);
+      sfree(rates);
+      return -ENOMEM;
+    }
+
+    /* Append it in case we got multiple data set */
+    if ((buffer_pos + message_len) >= buffer_size) {
+      P_ERROR("format_graphite: target buffer too small");
+      sfree(rates);
+      return -ENOMEM;
+    }
+    memcpy((void *)(buffer + buffer_pos), message, message_len);
+    buffer_pos += message_len;
+    buffer[buffer_pos] = '\0';
+  }
+  sfree(rates);
+  return status;
+} /* int format_graphite */
diff --git a/src/utils/format_graphite/format_graphite.h b/src/utils/format_graphite/format_graphite.h
new file mode 100644 (file)
index 0000000..4df7db3
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * collectd - src/utils_format_graphite.h
+ * Copyright (C) 2012  Thomas Meson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Thomas Meson <zllak at hycik.org>
+ **/
+
+#ifndef UTILS_FORMAT_GRAPHITE_H
+#define UTILS_FORMAT_GRAPHITE_H 1
+
+#include "collectd.h"
+
+#include "plugin.h"
+
+#define GRAPHITE_STORE_RATES 0x01
+#define GRAPHITE_SEPARATE_INSTANCES 0x02
+#define GRAPHITE_ALWAYS_APPEND_DS 0x04
+#define GRAPHITE_DROP_DUPE_FIELDS 0x08
+#define GRAPHITE_PRESERVE_SEPARATOR 0x10
+#define GRAPHITE_USE_TAGS 0x20
+#define GRAPHITE_REVERSE_HOST 0x40
+
+int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds,
+                    const value_list_t *vl, const char *prefix,
+                    const char *postfix, const char escape_char,
+                    unsigned int flags);
+
+#endif /* UTILS_FORMAT_GRAPHITE_H */
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..5bae546
--- /dev/null
@@ -0,0 +1,186 @@
+/**
+ * collectd - src/utils/mount/mount.h
+ * Copyright (C) 2005,2006  Niki W. Waibel
+ *
+ * This program is free software; you can redistribute it and/
+ * or modify it under the terms of the GNU General Public Li-
+ * cence as published by the Free Software Foundation; either
+ * version 2 of the Licence, or any later version.
+ *
+ * This program is distributed in the hope that it will be use-
+ * ful, but WITHOUT ANY WARRANTY; without even the implied war-
+ * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Niki W. Waibel <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..4b31056
--- /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 ((size_t)status >= buffer_size)
+    return ENOMEM;
+
+  return 0;
+} /* }}} int get_assertion */
+
+int oauth_parse_json_token(char const *json, /* {{{ */
+                           char *out_access_token, size_t access_token_size,
+                           cdtime_t *expires_in) {
+  time_t expire_in_seconds = 0;
+  yajl_val root;
+  yajl_val token_val;
+  yajl_val expire_val;
+  char errbuf[1024];
+  const char *token_path[] = {"access_token", NULL};
+  const char *expire_path[] = {"expires_in", NULL};
+
+  root = yajl_tree_parse(json, errbuf, sizeof(errbuf));
+  if (root == NULL) {
+    ERROR("utils_oauth: oauth_parse_json_token: parse error %s", errbuf);
+    return -1;
+  }
+
+  token_val = yajl_tree_get(root, token_path, yajl_t_string);
+  if (token_val == NULL) {
+    ERROR("utils_oauth: oauth_parse_json_token: access token field not found");
+    yajl_tree_free(root);
+    return -1;
+  }
+  sstrncpy(out_access_token, YAJL_GET_STRING(token_val), access_token_size);
+
+  expire_val = yajl_tree_get(root, expire_path, yajl_t_number);
+  if (expire_val == NULL) {
+    ERROR("utils_oauth: oauth_parse_json_token: expire field found");
+    yajl_tree_free(root);
+    return -1;
+  }
+  expire_in_seconds = (time_t)YAJL_GET_INTEGER(expire_val);
+  DEBUG("oauth_parse_json_token: expires_in %lu",
+        (unsigned long)expire_in_seconds);
+
+  *expires_in = TIME_T_TO_CDTIME_T(expire_in_seconds);
+  yajl_tree_free(root);
+  return 0;
+} /* }}} int oauth_parse_json_token */
+
+static int new_token(oauth_t *auth) /* {{{ */
+{
+  CURL *curl;
+  char assertion[1024];
+  char post_data[1024];
+  memory_t data;
+  char access_token[256];
+  cdtime_t expires_in;
+  cdtime_t now;
+  char curl_errbuf[CURL_ERROR_SIZE];
+  int status = 0;
+
+  data.size = 0;
+  data.memory = NULL;
+
+  now = cdtime();
+
+  status = get_assertion(auth, assertion, sizeof(assertion));
+  if (status != 0) {
+    ERROR("utils_oauth: Failed to get token using service account %s.",
+          auth->iss);
+    return -1;
+  }
+
+  snprintf(post_data, sizeof(post_data), "grant_type=%s&assertion=%s",
+           OAUTH_GRANT_TYPE, assertion);
+
+  curl = curl_easy_init();
+  if (curl == NULL) {
+    ERROR("utils_oauth: curl_easy_init failed.");
+    return -1;
+  }
+
+  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
+  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory);
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
+  curl_easy_setopt(curl, CURLOPT_POST, 1L);
+  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
+  curl_easy_setopt(curl, CURLOPT_URL, auth->url);
+
+  status = curl_easy_perform(curl);
+  if (status != CURLE_OK) {
+    ERROR("utils_oauth: curl_easy_perform failed with status %i: %s", status,
+          curl_errbuf);
+
+    sfree(data.memory);
+    curl_easy_cleanup(curl);
+
+    return -1;
+  } else {
+    long http_code = 0;
+
+    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+    if ((http_code < 200) || (http_code >= 300)) {
+      ERROR("utils_oauth: POST request to %s failed: HTTP error %ld", auth->url,
+            http_code);
+      if (data.memory != NULL)
+        INFO("utils_oauth: Server replied: %s", data.memory);
+
+      sfree(data.memory);
+      curl_easy_cleanup(curl);
+
+      return -1;
+    }
+  }
+
+  status = oauth_parse_json_token(data.memory, access_token,
+                                  sizeof(access_token), &expires_in);
+  if (status != 0) {
+    sfree(data.memory);
+    curl_easy_cleanup(curl);
+
+    return -1;
+  }
+
+  sfree(auth->token);
+  auth->token = strdup(access_token);
+  if (auth->token == NULL) {
+    ERROR("utils_oauth: strdup failed");
+    auth->valid_until = 0;
+
+    sfree(data.memory);
+    curl_easy_cleanup(curl);
+    return -1;
+  }
+
+  INFO("utils_oauth: OAuth2 access token is valid for %.3fs",
+       CDTIME_T_TO_DOUBLE(expires_in));
+  auth->valid_until = now + expires_in;
+
+  sfree(data.memory);
+  curl_easy_cleanup(curl);
+
+  return 0;
+} /* }}} int new_token */
+
+static int renew_token(oauth_t *auth) /* {{{ */
+{
+  /* Renew OAuth token 30 seconds *before* it expires. */
+  cdtime_t const slack = TIME_T_TO_CDTIME_T(30);
+
+  if (auth->valid_until > (cdtime() + slack))
+    return 0;
+
+  return new_token(auth);
+} /* }}} int renew_token */
+
+static oauth_t *oauth_create(char const *url, char const *iss,
+                             char const *scope, char const *aud,
+                             EVP_PKEY *key) /* {{{ */
+{
+  oauth_t *auth;
+
+  if ((url == NULL) || (iss == NULL) || (scope == NULL) || (aud == NULL) ||
+      (key == NULL))
+    return NULL;
+
+  auth = malloc(sizeof(*auth));
+  if (auth == NULL)
+    return NULL;
+  memset(auth, 0, sizeof(*auth));
+
+  auth->url = strdup(url);
+  auth->iss = strdup(iss);
+  auth->scope = strdup(scope);
+  auth->aud = strdup(aud);
+
+  if ((auth->url == NULL) || (auth->iss == NULL) || (auth->scope == NULL) ||
+      (auth->aud == NULL)) {
+    oauth_destroy(auth);
+    return NULL;
+  }
+
+  auth->key = key;
+
+  return auth;
+} /* }}} oauth_t *oauth_create */
+
+/*
+ * Public
+ */
+oauth_google_t oauth_create_google_json(char const *buffer, char const *scope) {
+  char errbuf[1024];
+  yajl_val root = yajl_tree_parse(buffer, errbuf, sizeof(errbuf));
+  if (root == NULL) {
+    ERROR("utils_oauth: oauth_create_google_json: parse error %s", errbuf);
+    return (oauth_google_t){NULL};
+  }
+
+  yajl_val field_project =
+      yajl_tree_get(root, (char const *[]){"project_id", NULL}, yajl_t_string);
+  if (field_project == NULL) {
+    ERROR("utils_oauth: oauth_create_google_json: project_id field not found");
+    yajl_tree_free(root);
+    return (oauth_google_t){NULL};
+  }
+  char const *project_id = YAJL_GET_STRING(field_project);
+
+  yajl_val field_iss = yajl_tree_get(
+      root, (char const *[]){"client_email", NULL}, yajl_t_string);
+  if (field_iss == NULL) {
+    ERROR(
+        "utils_oauth: oauth_create_google_json: client_email field not found");
+    yajl_tree_free(root);
+    return (oauth_google_t){NULL};
+  }
+
+  yajl_val field_token_uri =
+      yajl_tree_get(root, (char const *[]){"token_uri", NULL}, yajl_t_string);
+  char const *token_uri = (field_token_uri != NULL)
+                              ? YAJL_GET_STRING(field_token_uri)
+                              : GOOGLE_TOKEN_URL;
+
+  yajl_val field_priv_key =
+      yajl_tree_get(root, (char const *[]){"private_key", NULL}, yajl_t_string);
+  if (field_priv_key == NULL) {
+    ERROR("utils_oauth: oauth_create_google_json: private_key field not found");
+    yajl_tree_free(root);
+    return (oauth_google_t){NULL};
+  }
+
+  BIO *bp = BIO_new_mem_buf(YAJL_GET_STRING(field_priv_key), -1);
+  EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
+  if (pkey == NULL) {
+    char errbuf[1024];
+    ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
+    ERROR(
+        "utils_oauth: oauth_create_google_json: parsing private key failed: %s",
+        errbuf);
+    BIO_free(bp);
+    yajl_tree_free(root);
+    return (oauth_google_t){NULL};
+  }
+
+  BIO_free(bp);
+
+  oauth_t *oauth = oauth_create(token_uri, YAJL_GET_STRING(field_iss), scope,
+                                token_uri, pkey);
+  if (oauth == NULL) {
+    yajl_tree_free(root);
+    return (oauth_google_t){NULL};
+  }
+
+  oauth_google_t ret = {
+      .project_id = strdup(project_id), .oauth = oauth,
+  };
+
+  yajl_tree_free(root);
+  return ret;
+} /* oauth_google_t oauth_create_google_json */
+
+oauth_google_t oauth_create_google_file(char const *path,
+                                        char const *scope) { /* {{{ */
+  int fd = open(path, O_RDONLY);
+  if (fd == -1)
+    return (oauth_google_t){NULL};
+
+  struct stat st = {0};
+  if (fstat(fd, &st) != 0) {
+    close(fd);
+    return (oauth_google_t){NULL};
+  }
+
+  size_t buf_size = (size_t)st.st_size;
+  char *buf = calloc(1, buf_size + 1);
+  if (buf == NULL) {
+    close(fd);
+    return (oauth_google_t){NULL};
+  }
+
+  if (sread(fd, buf, buf_size) != 0) {
+    free(buf);
+    close(fd);
+    return (oauth_google_t){NULL};
+  }
+  close(fd);
+  buf[buf_size] = 0;
+
+  oauth_google_t ret = oauth_create_google_json(buf, scope);
+
+  free(buf);
+  return ret;
+} /* }}} oauth_google_t oauth_create_google_file */
+
+/* oauth_create_google_default checks for JSON credentials in well-known
+ * positions, similar to gcloud and other tools. */
+oauth_google_t oauth_create_google_default(char const *scope) {
+  char const *app_creds;
+  if ((app_creds = getenv("GOOGLE_APPLICATION_CREDENTIALS")) != NULL) {
+    oauth_google_t ret = oauth_create_google_file(app_creds, scope);
+    if (ret.oauth == NULL) {
+      ERROR("The environment variable GOOGLE_APPLICATION_CREDENTIALS is set to "
+            "\"%s\" but that file could not be read.",
+            app_creds);
+    } else {
+      return ret;
+    }
+  }
+
+  char const *home;
+  if ((home = getenv("HOME")) != NULL) {
+    char path[PATH_MAX];
+    snprintf(path, sizeof(path),
+             "%s/.config/gcloud/application_default_credentials.json", home);
+
+    oauth_google_t ret = oauth_create_google_file(path, scope);
+    if (ret.oauth != NULL) {
+      return ret;
+    }
+  }
+
+  return (oauth_google_t){NULL};
+} /* }}} oauth_google_t oauth_create_google_default */
+
+void oauth_destroy(oauth_t *auth) /* {{{ */
+{
+  if (auth == NULL)
+    return;
+
+  sfree(auth->url);
+  sfree(auth->iss);
+  sfree(auth->scope);
+  sfree(auth->aud);
+
+  if (auth->key != NULL) {
+    EVP_PKEY_free(auth->key);
+    auth->key = NULL;
+  }
+
+  sfree(auth);
+} /* }}} void oauth_destroy */
+
+int oauth_access_token(oauth_t *auth, char *buffer,
+                       size_t buffer_size) /* {{{ */
+{
+  int status;
+
+  if (auth == NULL)
+    return EINVAL;
+
+  status = renew_token(auth);
+  if (status != 0)
+    return status;
+  assert(auth->token != NULL);
+
+  sstrncpy(buffer, auth->token, buffer_size);
+  return 0;
+} /* }}} int oauth_access_token */
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/proc_pids/proc_pids.c b/src/utils/proc_pids/proc_pids.c
new file mode 100644 (file)
index 0000000..336a996
--- /dev/null
@@ -0,0 +1,361 @@
+/**
+ * collectd - src/utils/proc_pids/proc_pids.c
+ *
+ * Copyright(c) 2018-2019 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Starzyk, Mateusz <mateuszx.starzyk@intel.com>
+ *   Wojciech Andralojc <wojciechx.andralojc@intel.com>
+ *   Michał Aleksiński <michalx.aleksinski@intel.com>
+ **/
+
+#include "collectd.h"
+#include "utils/common/common.h"
+#include "utils/proc_pids/proc_pids.h"
+
+#define UTIL_NAME "utils_proc_pids"
+
+void pids_list_free(pids_list_t *list) {
+  assert(list);
+
+  sfree(list->pids);
+  sfree(list);
+}
+
+int proc_pids_is_name_valid(const char *name) {
+
+  if (name != NULL) {
+    unsigned len = strlen(name);
+    if (len > 0 && len <= MAX_PROC_NAME_LEN)
+      return 1;
+    else {
+      DEBUG(UTIL_NAME
+            ": Process name \'%s\' is too long. Max supported len is %d chars.",
+            name, MAX_PROC_NAME_LEN);
+    }
+  }
+
+  return 0;
+}
+
+int pids_list_add_pid(pids_list_t *list, const pid_t pid) {
+  assert(list);
+
+  if (list->allocated == list->size) {
+    size_t new_allocated = list->allocated + 1 + list->allocated / 10;
+    pid_t *new_pids = realloc(list->pids, sizeof(pid_t) * new_allocated);
+
+    if (NULL == new_pids) {
+      ERROR(UTIL_NAME ": Alloc error\n");
+      return -1;
+    }
+
+    list->pids = new_pids;
+    list->allocated = new_allocated;
+  }
+
+  list->pids[list->size] = pid;
+  list->size++;
+
+  return 0;
+}
+
+int pids_list_add_list(pids_list_t *dst, pids_list_t *src) {
+  assert(dst);
+  assert(src);
+
+  if (dst->allocated < dst->size + src->size) {
+    pid_t *new_pids =
+        realloc(dst->pids, sizeof(pid_t) * (dst->size + src->size));
+
+    if (NULL == new_pids) {
+      ERROR(UTIL_NAME ": Alloc error\n");
+      return -1;
+    }
+
+    dst->allocated = dst->size + src->size;
+    dst->pids = new_pids;
+  }
+
+  memcpy(dst->pids + dst->size, src->pids, src->size * sizeof(*(src->pids)));
+  dst->size += src->size;
+
+  return 0;
+}
+
+int pids_list_clear(pids_list_t *list) {
+  assert(list);
+
+  if (list->pids != NULL)
+    sfree(list->pids);
+
+  list->size = 0;
+  list->allocated = 0;
+
+  return 0;
+}
+
+int pids_list_contains_pid(pids_list_t *list, const pid_t pid) {
+  assert(list);
+
+  for (int i = 0; i < list->size; i++)
+    if (list->pids[i] == pid)
+      return 1;
+
+  return 0;
+}
+
+/*
+ * NAME
+ *   read_proc_name
+ *
+ * DESCRIPTION
+ *   Reads process name from given pid directory.
+ *   Strips new-line character (\n).
+ *
+ * PARAMETERS
+ *   `procfs_path' Path to systems proc directory (e.g. /proc)
+ *   `pid_entry'   Dirent for PID directory
+ *   `name'        Output buffer for process name, recommended proc_comm.
+ *   `out_size'    Output buffer size, recommended sizeof(proc_comm)
+ *
+ * RETURN VALUE
+ *   On success, the number of read bytes (includes stripped \n).
+ *   -1 on file open error
+*/
+static int read_proc_name(const char *procfs_path,
+                          const struct dirent *pid_entry, char *name,
+                          const size_t out_size) {
+  assert(pid_entry);
+  assert(name);
+  assert(out_size);
+  memset(name, 0, out_size);
+
+  const char *comm_file_name = "comm";
+
+  char *path = ssnprintf_alloc("%s/%s/%s", procfs_path, pid_entry->d_name,
+                               comm_file_name);
+  if (path == NULL)
+    return -1;
+  FILE *f = fopen(path, "r");
+  if (f == NULL) {
+    ERROR(UTIL_NAME ": Failed to open comm file, error: %d\n", errno);
+    sfree(path);
+    return -1;
+  }
+  size_t read_length = fread(name, sizeof(char), out_size, f);
+  name[out_size - 1] = '\0';
+  fclose(f);
+  sfree(path);
+  /* strip new line ending */
+  char *newline = strchr(name, '\n');
+  if (newline) {
+    *newline = '\0';
+  }
+
+  return read_length;
+}
+
+/*
+ * NAME
+ *   get_pid_number
+ *
+ * DESCRIPTION
+ *   Gets pid number for given /proc/pid directory entry or
+ *   returns error if input directory does not hold PID information.
+ *
+ * PARAMETERS
+ *   `entry'    Dirent for PID directory
+ *   `pid'      PID number to be filled
+ *
+ * RETURN VALUE
+ *   0 on success. -1 on error.
+ */
+static int get_pid_number(struct dirent *entry, pid_t *pid) {
+  char *tmp_end; /* used for strtoul error check*/
+
+  if (pid == NULL || entry == NULL)
+    return -1;
+
+  if (entry->d_type != DT_DIR)
+    return -1;
+
+  /* trying to get pid number from directory name*/
+  *pid = strtoul(entry->d_name, &tmp_end, 10);
+  if (*tmp_end != '\0') {
+    return -1; /* conversion failed, not proc-pid */
+  }
+  /* all checks passed, marking as success */
+  return 0;
+}
+
+int proc_pids_init(const char **procs_names_array,
+                   const size_t procs_names_array_size,
+                   proc_pids_t **proc_pids[]) {
+
+  proc_pids_t **proc_pids_array;
+  assert(proc_pids);
+  assert(NULL == *proc_pids);
+
+  /* Copy procs names to output array. Initialize pids list with NULL value. */
+  proc_pids_array = calloc(procs_names_array_size, sizeof(*proc_pids_array));
+
+  if (NULL == proc_pids_array)
+    return -1;
+
+  for (size_t i = 0; i < procs_names_array_size; ++i) {
+    proc_pids_array[i] = calloc(1, sizeof(**proc_pids_array));
+    if (NULL == proc_pids_array[i])
+      goto proc_pids_init_error;
+
+    sstrncpy(proc_pids_array[i]->process_name, procs_names_array[i],
+             STATIC_ARRAY_SIZE(proc_pids_array[i]->process_name));
+    proc_pids_array[i]->prev = NULL;
+    proc_pids_array[i]->curr = NULL;
+  }
+
+  *proc_pids = proc_pids_array;
+
+  return 0;
+proc_pids_init_error:
+  if (NULL != proc_pids_array) {
+    for (size_t i = 0; i < procs_names_array_size; ++i) {
+      free(proc_pids_array[i]);
+    }
+    free(proc_pids_array);
+  }
+  return -1;
+}
+
+static void swap_proc_pids(proc_pids_t **proc_pids, size_t proc_pids_num) {
+  for (size_t i = 0; i < proc_pids_num; i++) {
+    pids_list_t *swap = proc_pids[i]->prev;
+    proc_pids[i]->prev = proc_pids[i]->curr;
+    proc_pids[i]->curr = swap;
+  }
+}
+
+int proc_pids_update(const char *procfs_path, proc_pids_t **proc_pids,
+                     size_t proc_pids_num) {
+  assert(procfs_path);
+  assert(proc_pids);
+
+  DIR *proc_dir = opendir(procfs_path);
+  if (proc_dir == NULL) {
+    ERROR(UTIL_NAME ": Could not open %s directory, error: %d", procfs_path,
+          errno);
+    return -1;
+  }
+
+  swap_proc_pids(proc_pids, proc_pids_num);
+
+  for (size_t i = 0; i < proc_pids_num; i++) {
+    if (NULL == proc_pids[i]->curr)
+      proc_pids[i]->curr = calloc(1, sizeof(*(proc_pids[i]->curr)));
+
+    if (NULL == proc_pids[i]->curr) {
+      ERROR(UTIL_NAME ": Alloc error\n");
+      goto update_error;
+    }
+
+    proc_pids[i]->curr->size = 0;
+  }
+
+  /* Go through procfs and find PIDS and their comms */
+  struct dirent *entry;
+  while ((entry = readdir(proc_dir)) != NULL) {
+    pid_t pid;
+    int pid_conversion = get_pid_number(entry, &pid);
+    if (pid_conversion < 0)
+      continue;
+
+    proc_comm_t comm;
+    int read_result =
+        read_proc_name(procfs_path, entry, comm, sizeof(proc_comm_t));
+    if (read_result <= 0)
+      continue;
+
+    /* Try to find comm in input procs array */
+    for (size_t i = 0; i < proc_pids_num; ++i) {
+      if (0 ==
+          strncmp(comm, proc_pids[i]->process_name, STATIC_ARRAY_SIZE(comm)))
+        pids_list_add_pid(proc_pids[i]->curr, pid);
+    }
+  }
+
+  int close_result = closedir(proc_dir);
+  if (0 != close_result) {
+    ERROR(UTIL_NAME ": failed to close /proc directory, error: %d", errno);
+    goto update_error;
+  }
+  return 0;
+
+update_error:
+  swap_proc_pids(proc_pids, proc_pids_num);
+  return -1;
+}
+
+int pids_list_diff(proc_pids_t *proc, pids_list_t *added,
+                   pids_list_t *removed) {
+  assert(proc);
+  assert(added);
+  assert(removed);
+
+  added->size = 0;
+  removed->size = 0;
+
+  if (NULL == proc->prev || 0 == proc->prev->size) {
+    /* append all PIDs from curr to added*/
+    return pids_list_add_list(added, proc->curr);
+  } else if (NULL == proc->curr || 0 == proc->curr->size) {
+    /* append all PIDs from prev to removed*/
+    return pids_list_add_list(removed, proc->prev);
+  }
+
+  for (int i = 0; i < proc->prev->size; i++)
+    if (0 == pids_list_contains_pid(proc->curr, proc->prev->pids[i])) {
+      int add_result = pids_list_add_pid(removed, proc->prev->pids[i]);
+      if (add_result < 0)
+        return add_result;
+    }
+
+  for (int i = 0; i < proc->curr->size; i++)
+    if (0 == pids_list_contains_pid(proc->prev, proc->curr->pids[i])) {
+      int add_result = pids_list_add_pid(added, proc->curr->pids[i]);
+      if (add_result < 0)
+        return add_result;
+    }
+
+  return 0;
+}
+
+int proc_pids_free(proc_pids_t *proc_pids[], size_t proc_pids_num) {
+  for (size_t i = 0; i < proc_pids_num; i++) {
+    if (NULL != proc_pids[i]->curr)
+      pids_list_free(proc_pids[i]->curr);
+    if (NULL != proc_pids[i]->prev)
+      pids_list_free(proc_pids[i]->prev);
+    sfree(proc_pids[i]);
+  }
+  sfree(proc_pids);
+
+  return 0;
+}
diff --git a/src/utils/proc_pids/proc_pids.h b/src/utils/proc_pids/proc_pids.h
new file mode 100644 (file)
index 0000000..8b19497
--- /dev/null
@@ -0,0 +1,226 @@
+/**
+ * collectd - src/utils/proc_pids/proc_pids.h
+ *
+ * Copyright(c) 2018-2019 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Starzyk, Mateusz <mateuszx.starzyk@intel.com>
+ *   Wojciech Andralojc <wojciechx.andralojc@intel.com>
+ *   Michał Aleksiński <michalx.aleksinski@intel.com>
+ **/
+
+#include <dirent.h>
+#include <sys/types.h>
+
+/*
+ * Process name inside comm file is limited to 16 chars.
+ * More info here: http://man7.org/linux/man-pages/man5/proc.5.html
+ */
+#define MAX_PROC_NAME_LEN 16
+
+/* Helper typedef for process name array
+ * Extra 1 char is added for string null termination.
+ */
+typedef char proc_comm_t[MAX_PROC_NAME_LEN + 1];
+
+/* List of pids. */
+typedef struct pids_list_s {
+  pid_t *pids;
+  size_t size;
+  size_t allocated;
+} pids_list_t;
+
+/* Holds process name and list of pids assigned to that name */
+typedef struct proc_pids_s {
+  proc_comm_t process_name;
+  pids_list_t *prev;
+  pids_list_t *curr;
+} proc_pids_t;
+
+/*
+ * NAME
+ *   pids_list_free
+ *
+ * DESCRIPTION
+ *   Free all elements of given pids list
+ *
+ * PARAMETERS
+ *   `list'     Head of target pids_list.
+ */
+void pids_list_free(pids_list_t *list);
+
+/*
+ * NAME
+ *   pids_list_add_pid
+ *
+ * DESCRIPTION
+ *   Adds pid at the end of the pids array.
+ *   Reallocates memory for new pid element, it is up to user to free it.
+ *
+ * PARAMETERS
+ *   `list'     Target pids_list.
+ *   `pid'      Pid to be added.
+ *
+ * RETURN VALUE
+ *   On success, returns 0.
+ *   -1 on memory allocation error.
+ */
+int pids_list_add_pid(pids_list_t *list, const pid_t pid);
+
+/*
+ * NAME
+ *   pids_list_clear
+ *
+ * DESCRIPTION
+ *   Remove all pids from the list
+ *
+ * PARAMETERS
+ *   `list'     Target pids_list.
+ *
+ * RETURN VALUE
+ *   On success, return 0
+ */
+int pids_list_clear(pids_list_t *list);
+
+/*
+ * NAME
+ *   pids_list_add_list
+ *
+ * DESCRIPTION
+ *   Adds pids list at the end of the pids list.
+ *   Allocates memory for new pid elements, it is up to user to free it.
+ *
+ * PARAMETERS
+ *   `dst'      Target PIDs list.
+ *   `src'      Source PIDs list.
+ *
+ * RETURN VALUE
+ *   On success, returns 0.
+ *   -1 on memory allocation error.
+ */
+int pids_list_add_list(pids_list_t *dst, pids_list_t *src);
+
+/*
+ * NAME
+ *   pids_list_contains_pid
+ *
+ * DESCRIPTION
+ *   Tests if pids list contains specific pid.
+ *
+ * PARAMETERS
+ *   `list'     pids_list to check.
+ *   `pid'      Pid to be searched for.
+ *
+ * RETURN VALUE
+ *   If PID found in list, returns 1,
+ *   Otherwise returns 0.
+ */
+int pids_list_contains_pid(pids_list_t *list, const pid_t pid);
+
+/*
+ * NAME
+ *   pids_list_diff
+ *
+ * DESCRIPTION
+ *   Searches for differences in two given lists
+ *
+ * PARAMETERS
+ *   `proc'            List of pids
+ *   `added'           New pids which appeared
+ *   `removed'         Result array storing pids which disappeared
+ * RETURN VALUE
+ *   0 on success. Negative number on error.
+ */
+int pids_list_diff(proc_pids_t *proc, pids_list_t *added, pids_list_t *removed);
+
+/*
+ * NAME
+ *   proc_pids_is_name_valid
+ *
+ * DESCRIPTION
+ *   Checks if given string is valid process name.
+ *
+ * PARAMETERS
+ *   `name'     null-terminated char array
+ *
+ * RETURN VALUE
+ *   If given name is a valid process name, returns 1,
+ *   Otherwise returns 0.
+ */
+int proc_pids_is_name_valid(const char *name);
+
+/*
+ * NAME
+ *   proc_pids_init
+ *
+ * DESCRIPTION
+ *   Helper function to properly initialize array of proc_pids.
+ *   Allocates memory for proc_pids structs.
+ *
+ * PARAMETERS
+ *   `procs_names_array'      Array of null-terminated strings with
+ *                            process' names to be copied to new array
+ *   `procs_names_array_size' procs_names_array element count
+ *   `proc_pids'              Address of pointer, under which new
+ *                            array of proc_pids will be allocated.
+ *                            Must be NULL.
+ * RETURN VALUE
+ *   0 on success. Negative number on error:
+ *   -1: allocation error
+ */
+int proc_pids_init(const char **procs_names_array,
+                   const size_t procs_names_array_size,
+                   proc_pids_t **proc_pids[]);
+
+/*
+ * NAME
+ *   proc_pids_update
+ *
+ * DESCRIPTION
+ *   Updates PIDs matching processes's names.
+ *   Searches all PID directories in /proc fs and updates current pids_list.
+ *
+ * PARAMETERS
+ *   `procfs_path'     Path to systems proc directory (e.g. /proc)
+ *   `proc_pids'       Array of proc_pids pointers to be updated.
+ *   `proc_pids_num'   proc_pids element count
+ *
+ * RETURN VALUE
+ *   0 on success. -1 on error.
+ */
+int proc_pids_update(const char *procfs_path, proc_pids_t *proc_pids[],
+                     size_t proc_pids_num);
+
+/*
+ * NAME
+ *   proc_pids_free
+ *
+ * DESCRIPTION
+ *   Releses memory allocatd for proc_pids
+ *
+ * PARAMETERS
+ *   `proc_pids'       Array of proc_pids
+ *   `proc_pids_num'   proc_pids element count
+ *
+ * RETURN VALUE
+ *   0 on success. -1 on error.
+ */
+int proc_pids_free(proc_pids_t *proc_pids[], size_t proc_pids_num);
diff --git a/src/utils/proc_pids/proc_pids_test.c b/src/utils/proc_pids/proc_pids_test.c
new file mode 100644 (file)
index 0000000..42d17f4
--- /dev/null
@@ -0,0 +1,511 @@
+// clang-format off
+/*
+ * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris
+ * See Github Issue #3193 for details
+ */
+#include "utils/proc_pids/proc_pids.c" /* sic */
+#include "testing.h"
+// clang-format on
+#include <sys/stat.h>
+
+/***************************************************************************
+ * helper functions
+ */
+
+typedef struct stub_proc_pid {
+  proc_comm_t comm;
+  pid_t pid;
+} stub_proc_pid_t;
+
+static const char *proc_fs = "/tmp/procfs_stub";
+
+/*
+ * NAME
+ *   stub_procfs_setup
+ *
+ * DESCRIPTION
+ *   Prepares testing environment by creating temporary
+ *   PID/comm file structure.
+ *
+ * PARAMETERS
+ *   `proc_pids_array'          Array of stub_proc_pid_t structs. Represents
+ *                              which PIDs should hold given process name.
+ *   `proc_pids_array_length'   Element count of input array.
+ *
+ * RETURN VALUE
+ *   0 on success.
+ *   -1 on base dir creation error.
+ *   -2 on comm file creation error.
+ *   -3 on comm file write error.
+ */
+int stub_procfs_setup(const stub_proc_pid_t *proc_pids_array,
+                      const size_t proc_pids_array_length) {
+  if (mkdir(proc_fs, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
+    return -1;
+  char path[256];
+
+  for (size_t i = 0; i < proc_pids_array_length; ++i) {
+    memset(path, 0, sizeof(path));
+    snprintf(path, STATIC_ARRAY_SIZE(path), "%s/%d", proc_fs,
+             proc_pids_array[i].pid);
+    mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+    strncat(path, "/comm", STATIC_ARRAY_SIZE(path) - strlen(path) - 1);
+
+    FILE *fp = fopen(path, "w");
+    if (!fp)
+      return -2;
+
+    size_t slen = strlen(proc_pids_array[i].comm);
+    size_t wlen = fwrite(proc_pids_array[i].comm, sizeof(char), slen, fp);
+    fclose(fp);
+
+    if (slen != wlen)
+      return -3;
+  }
+  return 0;
+}
+
+/*
+ * NAME
+ *   stub_procfs_teardown
+ *
+ * DESCRIPTION
+ *   Clears testing environment: removes stub proc files.
+ *   NOTE - This function could be implemented by usage of nftw, but this
+ *   would require #define _XOPEN_SOURCE 500, which
+ *   messes up intel_rdt includes.
+ *
+ * RETURN VALUE
+ *   system command result
+ */
+int stub_procfs_teardown() {
+  char cmd[256];
+  sstrncpy(cmd, "rm -rf ", STATIC_ARRAY_SIZE(cmd));
+  strncat(cmd, proc_fs, STATIC_ARRAY_SIZE(cmd) - strlen(cmd) - 1);
+  return system(cmd);
+}
+
+/* Max PID value. More info:
+ * http://web.archive.org/web/20111209081734/http://research.cs.wisc.edu/condor/condorg/linux_scalability.html
+ */
+#define MAX_PID 4194304
+#define MAX_PID_STR "4194304"
+
+/***************************************************************************
+ * tests
+ */
+DEF_TEST(proc_pids_init__on_nullptr) {
+  /* setup */
+  const char *procs_names_array[] = {"proc1", "proc2", "proc3"};
+  const size_t procs_names_array_size = STATIC_ARRAY_SIZE(procs_names_array);
+  proc_pids_t **proc_pids_array = NULL;
+
+  /* check */
+  int result = proc_pids_init(procs_names_array, procs_names_array_size,
+                              &proc_pids_array);
+  EXPECT_EQ_INT(0, result);
+  for (size_t i = 0; i < procs_names_array_size; ++i)
+    EXPECT_EQ_STR(procs_names_array[i], proc_pids_array[i]->process_name);
+
+  /* cleanup */
+  proc_pids_free(proc_pids_array, procs_names_array_size);
+  return 0;
+}
+
+DEF_TEST(pid_list_add_pid__empty_list) {
+  /* setup */
+  pids_list_t *proc_pids_instance = calloc(1, sizeof(*proc_pids_instance));
+  pid_t pid = 1234;
+
+  /* check */
+  pids_list_add_pid(proc_pids_instance, pid);
+  EXPECT_EQ_INT(pid, proc_pids_instance->pids[0]);
+
+  /* cleanup */
+  pids_list_free(proc_pids_instance);
+  return 0;
+}
+
+DEF_TEST(pid_list_add_pid__non_empty_list) {
+  /* setup */
+  pids_list_t *proc_pids_instance = calloc(1, sizeof(*proc_pids_instance));
+  pid_t pids[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
+
+  /* check */
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids); ++i)
+    pids_list_add_pid(proc_pids_instance, pids[i]);
+
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids); ++i) {
+    EXPECT_EQ_INT(pids[i], proc_pids_instance->pids[i]);
+  }
+
+  /* cleanup */
+  pids_list_free(proc_pids_instance);
+  return 0;
+}
+
+DEF_TEST(pids_list_add_pids_list__non_empty_lists) {
+  /* setup */
+  pid_t pids_array_1[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
+  pid_t pids_array_2[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007};
+  pids_list_t *pids_list_1 = calloc(1, sizeof(*pids_list_1));
+  pids_list_t *pids_list_2 = calloc(1, sizeof(*pids_list_2));
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_1); ++i) {
+    pids_list_add_pid(pids_list_1, pids_array_1[i]);
+    pids_list_add_pid(pids_list_2, pids_array_2[i]);
+  }
+
+  /* check */
+  int result = pids_list_add_list(pids_list_1, pids_list_2);
+  EXPECT_EQ_INT(0, result);
+  EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_2) +
+                    STATIC_ARRAY_SIZE(pids_array_1),
+                pids_list_1->size);
+
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_1); ++i) {
+    EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array_1[i]));
+    EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array_2[i]));
+  }
+
+  /* setup */
+  pids_list_free(pids_list_1);
+  pids_list_free(pids_list_2);
+  return 0;
+}
+
+DEF_TEST(pids_list_add_pids_list__add_to_empty) {
+  /* setup */
+  pid_t pids_array[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007};
+  pids_list_t *pids_list_1 = calloc(1, sizeof(*pids_list_1));
+  pids_list_t *pids_list_2 = calloc(1, sizeof(*pids_list_2));
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array); ++i)
+    pids_list_add_pid(pids_list_2, pids_array[i]);
+
+  /* check */
+  int result = pids_list_add_list(pids_list_1, pids_list_2);
+  EXPECT_EQ_INT(0, result);
+  EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array), pids_list_1->size);
+
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array); ++i)
+    EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array[i]));
+
+  /* setup */
+  pids_list_free(pids_list_1);
+  pids_list_free(pids_list_2);
+  return 0;
+}
+
+DEF_TEST(get_pid_number__valid_dir) {
+  /* setup */
+  struct dirent d;
+  sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name));
+  d.d_type = DT_DIR;
+  pid_t pid = 0;
+
+  /* check */
+  int pid_conversion = get_pid_number(&d, &pid);
+
+  EXPECT_EQ_INT(0, pid_conversion);
+  EXPECT_EQ_INT(MAX_PID, pid);
+
+  /* cleanup */
+  return 0;
+}
+
+DEF_TEST(get_pid_number__invalid_dir_name) {
+  /* setup */
+  struct dirent d;
+  sstrncpy(d.d_name, "invalid", STATIC_ARRAY_SIZE(d.d_name));
+  d.d_type = DT_DIR;
+  pid_t pid = 0;
+
+  /* check */
+  int pid_conversion = get_pid_number(&d, &pid);
+
+  EXPECT_EQ_INT(-1, pid_conversion);
+  EXPECT_EQ_INT(0, pid);
+
+  /* cleanup */
+  return 0;
+}
+
+DEF_TEST(read_proc_name__valid_name) {
+  /* setup */
+  stub_proc_pid_t pp_stubs[] = {{"proc1", MAX_PID}};
+  stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs));
+  struct dirent d;
+  sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name));
+  d.d_type = DT_DIR;
+
+  /* check */
+  proc_comm_t comm;
+  int read_result = read_proc_name(proc_fs, &d, comm, STATIC_ARRAY_SIZE(comm));
+
+  EXPECT_EQ_INT(strlen(pp_stubs[0].comm), read_result);
+  EXPECT_EQ_STR(pp_stubs[0].comm, comm);
+
+  /* cleanup */
+  stub_procfs_teardown();
+  return 0;
+}
+
+DEF_TEST(read_proc_name__invalid_name) {
+  /* setup */
+  struct dirent d;
+  sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name));
+  d.d_type = DT_DIR;
+
+  /* check */
+  proc_comm_t comm;
+  int read_result = read_proc_name(proc_fs, &d, comm, STATIC_ARRAY_SIZE(comm));
+
+  EXPECT_EQ_INT(-1, read_result);
+
+  /* cleanup */
+  return 0;
+}
+
+DEF_TEST(proc_pids_update__one_proc_many_pid) {
+  /* setup */
+  const char *proc_names[] = {"proc1"};
+  stub_proc_pid_t pp_stubs[] = {{"proc1", 1007},
+                                {"proc1", 1008},
+                                {"proc1", 1009},
+                                {"proc2", 1010},
+                                {"proc3", 1011}};
+  proc_pids_t **proc_pids = NULL;
+  int result;
+  stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs));
+
+  result =
+      proc_pids_init(proc_names, STATIC_ARRAY_SIZE(proc_names), &proc_pids);
+  EXPECT_EQ_INT(0, result);
+
+  /* check */
+  result = proc_pids_update(proc_fs, proc_pids, STATIC_ARRAY_SIZE(proc_names));
+  EXPECT_EQ_INT(0, result);
+
+  /* proc name check */
+  EXPECT_EQ_STR(proc_names[0], proc_pids[0]->process_name);
+
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(pp_stubs); ++i) {
+    if (0 == strcmp(pp_stubs[i].comm, proc_names[0]))
+      /* check if proc struct has correct pids */
+      EXPECT_EQ_INT(pids_list_contains_pid(proc_pids[0]->curr, pp_stubs[i].pid),
+                    1);
+    else
+      /* check if proc struct has no incorrect pids */
+      EXPECT_EQ_INT(pids_list_contains_pid(proc_pids[0]->curr, pp_stubs[i].pid),
+                    0);
+  }
+
+  /* cleanup */
+  proc_pids_free(proc_pids, STATIC_ARRAY_SIZE(proc_names));
+  stub_procfs_teardown();
+  return 0;
+}
+
+DEF_TEST(proc_pids_update__many_proc_many_pid) {
+  /* setup */
+  const char *proc_names[] = {"proc1", "proc2", "proc3"};
+  stub_proc_pid_t pp_stubs[] = {
+      {"proc1", 1007}, {"proc1", 1008}, {"proc1", 1009}, {"proc2", 2007},
+      {"proc2", 2008}, {"proc2", 2009}, {"proc3", 3007}, {"proc3", 3008},
+      {"proc3", 3009}, {"proc4", 4007}, {"proc4", 4008}, {"proc4", 4009},
+      {"proc5", 5007}, {"proc5", 5008}, {"proc5", 5009}};
+  proc_pids_t **proc_pids = NULL;
+  int result;
+  stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs));
+
+  result =
+      proc_pids_init(proc_names, STATIC_ARRAY_SIZE(proc_names), &proc_pids);
+  EXPECT_EQ_INT(0, result);
+
+  /* check */
+  result = proc_pids_update(proc_fs, proc_pids, STATIC_ARRAY_SIZE(proc_names));
+  EXPECT_EQ_INT(0, result);
+
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(proc_names); ++i) {
+
+    /* proc name check */
+    EXPECT_EQ_STR(proc_names[i], proc_pids[i]->process_name);
+
+    for (size_t j = 0; j < STATIC_ARRAY_SIZE(pp_stubs); ++j) {
+      if (0 == strcmp(pp_stubs[j].comm, proc_names[i]))
+        /* check if proc struct has correct pids */
+        EXPECT_EQ_INT(
+            pids_list_contains_pid(proc_pids[i]->curr, pp_stubs[j].pid), 1);
+      else
+        /* check if proc struct has no incorrect pids */
+        EXPECT_EQ_INT(
+            pids_list_contains_pid(proc_pids[i]->curr, pp_stubs[j].pid), 0);
+    }
+  }
+
+  /* cleanup */
+  proc_pids_free(proc_pids, STATIC_ARRAY_SIZE(proc_names));
+  stub_procfs_teardown();
+  return 0;
+}
+
+DEF_TEST(pids_list_diff__all_changed) {
+  /* setup */
+  pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
+  pid_t pids_array_after[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007};
+  proc_pids_t proc_pids;
+  pids_list_t curr;
+  pids_list_t prev;
+
+  prev.pids = pids_array_before;
+  prev.size = STATIC_ARRAY_SIZE(pids_array_before);
+  prev.allocated = prev.size;
+  curr.pids = pids_array_after;
+  curr.size = STATIC_ARRAY_SIZE(pids_array_after);
+  curr.allocated = curr.size;
+  proc_pids.curr = &curr;
+  proc_pids.prev = &prev;
+
+  pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
+  pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
+
+  /* check */
+  int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
+  EXPECT_EQ_INT(0, result);
+  EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_before), lost_pids->size);
+  EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_after), new_pids->size);
+
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_before); ++i) {
+    EXPECT_EQ_INT(1, pids_list_contains_pid(new_pids, pids_array_after[i]));
+    EXPECT_EQ_INT(1, pids_list_contains_pid(lost_pids, pids_array_before[i]));
+  }
+
+  /* cleanup */
+  pids_list_free(new_pids);
+  pids_list_free(lost_pids);
+
+  return 0;
+}
+
+DEF_TEST(pids_list_diff__nothing_changed) {
+  /* setup */
+  pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
+  proc_pids_t proc_pids;
+  pids_list_t curr;
+  pids_list_t prev;
+
+  prev.pids = pids_array_before;
+  prev.size = STATIC_ARRAY_SIZE(pids_array_before);
+  prev.allocated = prev.size;
+  curr.pids = pids_array_before;
+  curr.size = STATIC_ARRAY_SIZE(pids_array_before);
+  curr.allocated = curr.size;
+  proc_pids.curr = &curr;
+  proc_pids.prev = &prev;
+
+  pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
+  pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
+
+  /* check */
+  int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
+  EXPECT_EQ_INT(0, result);
+  EXPECT_EQ_INT(0, lost_pids->size);
+  EXPECT_EQ_INT(0, new_pids->size);
+
+  /* cleanup */
+  pids_list_free(lost_pids);
+  pids_list_free(new_pids);
+
+  return 0;
+}
+
+DEF_TEST(pids_list_diff__one_added) {
+  /* setup */
+  pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
+  pid_t pids_array_after[] = {1000, 1001, 1002, 1003, 1004,
+                              1005, 1006, 1007, 1008};
+  proc_pids_t proc_pids;
+  pids_list_t curr;
+  pids_list_t prev;
+
+  prev.pids = pids_array_before;
+  prev.size = STATIC_ARRAY_SIZE(pids_array_before);
+  prev.allocated = prev.size;
+  curr.pids = pids_array_after;
+  curr.size = STATIC_ARRAY_SIZE(pids_array_after);
+  curr.allocated = curr.size;
+  proc_pids.curr = &curr;
+  proc_pids.prev = &prev;
+
+  pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
+  pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
+
+  /* check */
+  int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
+  EXPECT_EQ_INT(0, result);
+  EXPECT_EQ_INT(0, lost_pids->size);
+  EXPECT_EQ_INT(1, new_pids->size);
+  EXPECT_EQ_INT(1008, new_pids->pids[0]);
+
+  /* cleanup */
+  pids_list_free(lost_pids);
+  pids_list_free(new_pids);
+
+  return 0;
+}
+
+DEF_TEST(pids_list_diff__one_removed) {
+  /* setup */
+  pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004,
+                               1005, 1006, 1007, 1008};
+  pid_t pids_array_after[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
+
+  proc_pids_t proc_pids;
+  pids_list_t curr;
+  pids_list_t prev;
+
+  prev.pids = pids_array_before;
+  prev.size = STATIC_ARRAY_SIZE(pids_array_before);
+  prev.allocated = prev.size;
+  curr.pids = pids_array_after;
+  curr.size = STATIC_ARRAY_SIZE(pids_array_after);
+  curr.allocated = curr.size;
+  proc_pids.curr = &curr;
+  proc_pids.prev = &prev;
+
+  pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
+  pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
+
+  /* check */
+  int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
+  EXPECT_EQ_INT(0, result);
+  EXPECT_EQ_INT(0, new_pids->size);
+  EXPECT_EQ_INT(1, lost_pids->size);
+  EXPECT_EQ_INT(1008, lost_pids->pids[0]);
+
+  /* cleanup */
+  pids_list_free(lost_pids);
+  pids_list_free(new_pids);
+
+  return 0;
+}
+
+int main(void) {
+  stub_procfs_teardown();
+  RUN_TEST(proc_pids_init__on_nullptr);
+  RUN_TEST(pid_list_add_pid__empty_list);
+  RUN_TEST(pid_list_add_pid__non_empty_list);
+  RUN_TEST(pids_list_add_pids_list__non_empty_lists);
+  RUN_TEST(pids_list_add_pids_list__add_to_empty);
+  RUN_TEST(get_pid_number__valid_dir);
+  RUN_TEST(get_pid_number__invalid_dir_name);
+  RUN_TEST(read_proc_name__valid_name);
+  RUN_TEST(read_proc_name__invalid_name);
+  RUN_TEST(proc_pids_update__one_proc_many_pid);
+  RUN_TEST(proc_pids_update__many_proc_many_pid);
+  RUN_TEST(pids_list_diff__all_changed);
+  RUN_TEST(pids_list_diff__nothing_changed);
+  RUN_TEST(pids_list_diff__one_added);
+  RUN_TEST(pids_list_diff__one_removed);
+  stub_procfs_teardown();
+  END_TEST;
+}
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 c1f3f56..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/**
- * collectd - src/utils_cmd_getthreshold.c
- * Copyright (C) 2008,2009  Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_avltree.h"
-#include "utils_cmd_getthreshold.h"
-#include "utils_parse_option.h" /* for `parse_string' */
-#include "utils_threshold.h"
-
-#define print_to_socket(fh, ...)                                               \
-  if (fprintf(fh, __VA_ARGS__) < 0) {                                          \
-    WARNING("handle_getthreshold: failed to write to socket #%i: %s",          \
-            fileno(fh), STRERRNO);                                             \
-    return -1;                                                                 \
-  }
-
-int handle_getthreshold(FILE *fh, char *buffer) {
-  char *command;
-  char *identifier;
-  char *identifier_copy;
-
-  char *host;
-  char *plugin;
-  char *plugin_instance;
-  char *type;
-  char *type_instance;
-
-  threshold_t threshold;
-
-  int status;
-  size_t i;
-
-  if ((fh == NULL) || (buffer == NULL))
-    return -1;
-
-  DEBUG("utils_cmd_getthreshold: handle_getthreshold (fh = %p, buffer = %s);",
-        (void *)fh, buffer);
-
-  command = NULL;
-  status = parse_string(&buffer, &command);
-  if (status != 0) {
-    print_to_socket(fh, "-1 Cannot parse command.\n");
-    return -1;
-  }
-  assert(command != NULL);
-
-  if (strcasecmp("GETTHRESHOLD", command) != 0) {
-    print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command);
-    return -1;
-  }
-
-  identifier = NULL;
-  status = parse_string(&buffer, &identifier);
-  if (status != 0) {
-    print_to_socket(fh, "-1 Cannot parse identifier.\n");
-    return -1;
-  }
-  assert(identifier != NULL);
-
-  if (*buffer != 0) {
-    print_to_socket(fh, "-1 Garbage after end of command: %s\n", buffer);
-    return -1;
-  }
-
-  /* parse_identifier() modifies its first argument,
-   * returning pointers into it */
-  identifier_copy = sstrdup(identifier);
-
-  status = parse_identifier(identifier_copy, &host, &plugin, &plugin_instance,
-                            &type, &type_instance,
-                            /* default_host = */ NULL);
-  if (status != 0) {
-    DEBUG("handle_getthreshold: Cannot parse identifier `%s'.", identifier);
-    print_to_socket(fh, "-1 Cannot parse identifier `%s'.\n", identifier);
-    sfree(identifier_copy);
-    return -1;
-  }
-
-  value_list_t vl = {.values = NULL};
-  sstrncpy(vl.host, host, sizeof(vl.host));
-  sstrncpy(vl.plugin, plugin, sizeof(vl.plugin));
-  if (plugin_instance != NULL)
-    sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
-  sstrncpy(vl.type, type, sizeof(vl.type));
-  if (type_instance != NULL)
-    sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
-  sfree(identifier_copy);
-
-  status = ut_search_threshold(&vl, &threshold);
-  if (status == ENOENT) {
-    print_to_socket(fh, "-1 No threshold found for identifier %s\n",
-                    identifier);
-    return 0;
-  } else if (status != 0) {
-    print_to_socket(fh, "-1 Error while looking up threshold: %i\n", status);
-    return -1;
-  }
-
-  /* Lets count the number of lines we'll return. */
-  i = 0;
-  if (threshold.host[0] != 0)
-    i++;
-  if (threshold.plugin[0] != 0)
-    i++;
-  if (threshold.plugin_instance[0] != 0)
-    i++;
-  if (threshold.type[0] != 0)
-    i++;
-  if (threshold.type_instance[0] != 0)
-    i++;
-  if (threshold.data_source[0] != 0)
-    i++;
-  if (!isnan(threshold.warning_min))
-    i++;
-  if (!isnan(threshold.warning_max))
-    i++;
-  if (!isnan(threshold.failure_min))
-    i++;
-  if (!isnan(threshold.failure_max))
-    i++;
-  if (threshold.hysteresis > 0.0)
-    i++;
-  if (threshold.hits > 1)
-    i++;
-
-  /* Print the response */
-  print_to_socket(fh, "%" PRIsz " Threshold found\n", i);
-
-  if (threshold.host[0] != 0)
-    print_to_socket(fh, "Host: %s\n", threshold.host);
-  if (threshold.plugin[0] != 0)
-    print_to_socket(fh, "Plugin: %s\n", threshold.plugin);
-  if (threshold.plugin_instance[0] != 0)
-    print_to_socket(fh, "Plugin Instance: %s\n", threshold.plugin_instance);
-  if (threshold.type[0] != 0)
-    print_to_socket(fh, "Type: %s\n", threshold.type);
-  if (threshold.type_instance[0] != 0)
-    print_to_socket(fh, "Type Instance: %s\n", threshold.type_instance);
-  if (threshold.data_source[0] != 0)
-    print_to_socket(fh, "Data Source: %s\n", threshold.data_source);
-  if (!isnan(threshold.warning_min))
-    print_to_socket(fh, "Warning Min: %g\n", threshold.warning_min);
-  if (!isnan(threshold.warning_max))
-    print_to_socket(fh, "Warning Max: %g\n", threshold.warning_max);
-  if (!isnan(threshold.failure_min))
-    print_to_socket(fh, "Failure Min: %g\n", threshold.failure_min);
-  if (!isnan(threshold.failure_max))
-    print_to_socket(fh, "Failure Max: %g\n", threshold.failure_max);
-  if (threshold.hysteresis > 0.0)
-    print_to_socket(fh, "Hysteresis: %g\n", threshold.hysteresis);
-  if (threshold.hits > 1)
-    print_to_socket(fh, "Hits: %i\n", threshold.hits);
-
-  return 0;
-} /* int handle_getthreshold */
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 f747d5b..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- * collectd - src/utils_cmd_getval.c
- * Copyright (C) 2008       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_cache.h"
-#include "utils_cmd_getval.h"
-#include "utils_parse_option.h"
-
-cmd_status_t cmd_parse_getval(size_t argc, char **argv,
-                              cmd_getval_t *ret_getval,
-                              const cmd_options_t *opts,
-                              cmd_error_handler_t *err) {
-  char *identifier_copy;
-  int status;
-
-  if ((ret_getval == NULL) || (opts == NULL)) {
-    errno = EINVAL;
-    cmd_error(CMD_ERROR, err, "Invalid arguments to cmd_parse_getval.");
-    return CMD_ERROR;
-  }
-
-  if (argc != 1) {
-    if (argc == 0)
-      cmd_error(CMD_PARSE_ERROR, err, "Missing identifier.");
-    else
-      cmd_error(CMD_PARSE_ERROR, err, "Garbage after identifier: `%s'.",
-                argv[1]);
-    return CMD_PARSE_ERROR;
-  }
-
-  /* parse_identifier() modifies its first argument,
-   * returning pointers into it */
-  identifier_copy = sstrdup(argv[0]);
-
-  status = parse_identifier(
-      argv[0], &ret_getval->identifier.host, &ret_getval->identifier.plugin,
-      &ret_getval->identifier.plugin_instance, &ret_getval->identifier.type,
-      &ret_getval->identifier.type_instance, opts->identifier_default_host);
-  if (status != 0) {
-    DEBUG("cmd_parse_getval: Cannot parse identifier `%s'.", identifier_copy);
-    cmd_error(CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.",
-              identifier_copy);
-    sfree(identifier_copy);
-    return CMD_PARSE_ERROR;
-  }
-
-  ret_getval->raw_identifier = identifier_copy;
-  return CMD_OK;
-} /* cmd_status_t cmd_parse_getval */
-
-#define print_to_socket(fh, ...)                                               \
-  do {                                                                         \
-    if (fprintf(fh, __VA_ARGS__) < 0) {                                        \
-      WARNING("cmd_handle_getval: failed to write to socket #%i: %s",          \
-              fileno(fh), STRERRNO);                                           \
-      return -1;                                                               \
-    }                                                                          \
-    fflush(fh);                                                                \
-  } while (0)
-
-cmd_status_t cmd_handle_getval(FILE *fh, char *buffer) {
-  cmd_error_handler_t err = {cmd_error_fh, fh};
-  cmd_status_t status;
-  cmd_t cmd;
-
-  gauge_t *values;
-  size_t values_num;
-
-  const data_set_t *ds;
-
-  if ((fh == NULL) || (buffer == NULL))
-    return -1;
-
-  DEBUG("utils_cmd_getval: cmd_handle_getval (fh = %p, buffer = %s);",
-        (void *)fh, buffer);
-
-  if ((status = cmd_parse(buffer, &cmd, NULL, &err)) != CMD_OK)
-    return status;
-  if (cmd.type != CMD_GETVAL) {
-    cmd_error(CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
-              CMD_TO_STRING(cmd.type));
-    cmd_destroy(&cmd);
-    return CMD_UNKNOWN_COMMAND;
-  }
-
-  ds = plugin_get_ds(cmd.cmd.getval.identifier.type);
-  if (ds == NULL) {
-    DEBUG("cmd_handle_getval: plugin_get_ds (%s) == NULL;",
-          cmd.cmd.getval.identifier.type);
-    cmd_error(CMD_ERROR, &err, "Type `%s' is unknown.\n",
-              cmd.cmd.getval.identifier.type);
-    cmd_destroy(&cmd);
-    return -1;
-  }
-
-  values = NULL;
-  values_num = 0;
-  status =
-      uc_get_rate_by_name(cmd.cmd.getval.raw_identifier, &values, &values_num);
-  if (status != 0) {
-    cmd_error(CMD_ERROR, &err, "No such value.");
-    cmd_destroy(&cmd);
-    return CMD_ERROR;
-  }
-
-  if (ds->ds_num != values_num) {
-    ERROR("ds[%s]->ds_num = %" PRIsz ", "
-          "but uc_get_rate_by_name returned %" PRIsz " values.",
-          ds->type, ds->ds_num, values_num);
-    cmd_error(CMD_ERROR, &err, "Error reading value from cache.");
-    sfree(values);
-    cmd_destroy(&cmd);
-    return CMD_ERROR;
-  }
-
-  print_to_socket(fh, "%" PRIsz " Value%s found\n", values_num,
-                  (values_num == 1) ? "" : "s");
-  for (size_t i = 0; i < values_num; i++) {
-    print_to_socket(fh, "%s=", ds->ds[i].name);
-    if (isnan(values[i])) {
-      print_to_socket(fh, "NaN\n");
-    } else {
-      print_to_socket(fh, "%12e\n", values[i]);
-    }
-  }
-
-  sfree(values);
-  cmd_destroy(&cmd);
-
-  return CMD_OK;
-} /* cmd_status_t cmd_handle_getval */
-
-void cmd_destroy_getval(cmd_getval_t *getval) {
-  if (getval == NULL)
-    return;
-
-  sfree(getval->raw_identifier);
-} /* void cmd_destroy_getval */
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 df23a95..0000000
+++ /dev/null
@@ -1,109 +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) {                                        \
-      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 */
-
-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 75a8fae..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-/**
- * collectd - src/utils_cmd_putnotif.c
- * Copyright (C) 2008       Florian octo Forster
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- **/
-
-#include "collectd.h"
-
-#include "common.h"
-#include "plugin.h"
-
-#include "utils_cmd_putnotif.h"
-#include "utils_parse_option.h"
-
-#define print_to_socket(fh, ...)                                               \
-  do {                                                                         \
-    if (fprintf(fh, __VA_ARGS__) < 0) {                                        \
-      WARNING("handle_putnotif: failed to write to socket #%i: %s",            \
-              fileno(fh), STRERRNO);                                           \
-      return -1;                                                               \
-    }                                                                          \
-    fflush(fh);                                                                \
-  } while (0)
-
-static int set_option_severity(notification_t *n, const char *value) {
-  if (strcasecmp(value, "Failure") == 0)
-    n->severity = NOTIF_FAILURE;
-  else if (strcasecmp(value, "Warning") == 0)
-    n->severity = NOTIF_WARNING;
-  else if (strcasecmp(value, "Okay") == 0)
-    n->severity = NOTIF_OKAY;
-  else
-    return -1;
-
-  return 0;
-} /* int set_option_severity */
-
-static int set_option_time(notification_t *n, const char *value) {
-  char *endptr = NULL;
-  double tmp;
-
-  errno = 0;
-  tmp = strtod(value, &endptr);
-  if ((errno != 0)         /* Overflow */
-      || (endptr == value) /* Invalid string */
-      || (endptr == NULL)  /* This should not happen */
-      || (*endptr != 0))   /* Trailing chars */
-    return -1;
-
-  n->time = DOUBLE_TO_CDTIME_T(tmp);
-
-  return 0;
-} /* int set_option_time */
-
-static int set_option(notification_t *n, const char *option,
-                      const char *value) {
-  if ((n == NULL) || (option == NULL) || (value == NULL))
-    return -1;
-
-  DEBUG("utils_cmd_putnotif: set_option (option = %s, value = %s);", option,
-        value);
-
-  /* Add a meta option in the form: <type>:<key> */
-  if (option[0] != '\0' && option[1] == ':') {
-    /* Refuse empty key */
-    if (option[2] == '\0')
-      return 1;
-
-    if (option[0] == 's')
-      return plugin_notification_meta_add_string(n, option + 2, value);
-    else
-      return 1;
-  }
-
-  if (strcasecmp("severity", option) == 0)
-    return set_option_severity(n, value);
-  else if (strcasecmp("time", option) == 0)
-    return set_option_time(n, value);
-  else if (strcasecmp("message", option) == 0)
-    sstrncpy(n->message, value, sizeof(n->message));
-  else if (strcasecmp("host", option) == 0)
-    sstrncpy(n->host, value, sizeof(n->host));
-  else if (strcasecmp("plugin", option) == 0)
-    sstrncpy(n->plugin, value, sizeof(n->plugin));
-  else if (strcasecmp("plugin_instance", option) == 0)
-    sstrncpy(n->plugin_instance, value, sizeof(n->plugin_instance));
-  else if (strcasecmp("type", option) == 0)
-    sstrncpy(n->type, value, sizeof(n->type));
-  else if (strcasecmp("type_instance", option) == 0)
-    sstrncpy(n->type_instance, value, sizeof(n->type_instance));
-  else
-    return 1;
-
-  return 0;
-} /* int set_option */
-
-int handle_putnotif(FILE *fh, char *buffer) {
-  char *command;
-  notification_t n = {0};
-  int status;
-
-  if ((fh == NULL) || (buffer == NULL))
-    return -1;
-
-  DEBUG("utils_cmd_putnotif: handle_putnotif (fh = %p, buffer = %s);",
-        (void *)fh, buffer);
-
-  command = NULL;
-  status = parse_string(&buffer, &command);
-  if (status != 0) {
-    print_to_socket(fh, "-1 Cannot parse command.\n");
-    return -1;
-  }
-  assert(command != NULL);
-
-  if (strcasecmp("PUTNOTIF", command) != 0) {
-    print_to_socket(fh, "-1 Unexpected command: `%s'.\n", command);
-    return -1;
-  }
-
-  status = 0;
-  while (*buffer != 0) {
-    char *key;
-    char *value;
-
-    status = parse_option(&buffer, &key, &value);
-    if (status != 0) {
-      print_to_socket(fh, "-1 Malformed option.\n");
-      break;
-    }
-
-    status = set_option(&n, key, value);
-    if (status != 0) {
-      print_to_socket(fh, "-1 Error parsing option `%s'\n", key);
-      break;
-    }
-  } /* for (i) */
-
-  /* Check for required fields and complain if anything is missing. */
-  if ((status == 0) && (n.severity == 0)) {
-    print_to_socket(fh, "-1 Option `severity' missing.\n");
-    status = -1;
-  }
-  if ((status == 0) && (n.time == 0)) {
-    print_to_socket(fh, "-1 Option `time' missing.\n");
-    status = -1;
-  }
-  if ((status == 0) && (strlen(n.message) == 0)) {
-    print_to_socket(fh, "-1 No message or message of length 0 given.\n");
-    status = -1;
-  }
-
-  /* If status is still zero the notification is fine and we can finally
-   * dispatch it. */
-  if (status == 0) {
-    plugin_dispatch_notification(&n);
-    print_to_socket(fh, "0 Success\n");
-  }
-
-  return 0;
-} /* int handle_putnotif */
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 fe57d5a..0000000
+++ /dev/null
@@ -1,310 +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 "daemon/common.h"
-#include "utils_cmd_flush.h"
-#include "utils_cmd_getval.h"
-#include "utils_cmd_listval.h"
-#include "utils_cmd_putval.h"
-#include "utils_cmds.h"
-#include "utils_parse_option.h"
-
-#include <stdbool.h>
-#include <string.h>
-
-static cmd_options_t default_options = {
-    /* identifier_default_host = */ NULL,
-};
-
-/*
- * private helper functions
- */
-
-static cmd_status_t cmd_split(char *buffer, size_t *ret_len, char ***ret_fields,
-                              cmd_error_handler_t *err) {
-  char *field;
-  bool in_field, in_quotes;
-
-  size_t estimate, len;
-  char **fields;
-
-  estimate = 0;
-  in_field = false;
-  for (char *string = buffer; *string != '\0'; ++string) {
-    /* Make a quick worst-case estimate of the number of fields by
-     * counting spaces and ignoring quotation marks. */
-    if (!isspace((int)*string)) {
-      if (!in_field) {
-        estimate++;
-        in_field = true;
-      }
-    } else {
-      in_field = false;
-    }
-  }
-
-  /* fields will be NULL-terminated */
-  fields = malloc((estimate + 1) * sizeof(*fields));
-  if (fields == NULL) {
-    cmd_error(CMD_ERROR, err, "malloc failed.");
-    return CMD_ERROR;
-  }
-
-#define END_FIELD()                                                            \
-  do {                                                                         \
-    *field = '\0';                                                             \
-    field = NULL;                                                              \
-    in_field = false;                                                          \
-  } while (0)
-#define NEW_FIELD()                                                            \
-  do {                                                                         \
-    field = string;                                                            \
-    in_field = true;                                                           \
-    assert(len < estimate);                                                    \
-    fields[len] = field;                                                       \
-    field++;                                                                   \
-    len++;                                                                     \
-  } while (0)
-
-  len = 0;
-  field = NULL;
-  in_field = false;
-  in_quotes = false;
-  for (char *string = buffer; *string != '\0'; string++) {
-    if (isspace((int)string[0])) {
-      if (!in_quotes) {
-        if (in_field)
-          END_FIELD();
-
-        /* skip space */
-        continue;
-      }
-    } else if (string[0] == '"') {
-      /* Note: Two consecutive quoted fields not separated by space are
-       * treated as different fields. This is the collectd 5.x behavior
-       * around splitting fields. */
-
-      if (in_quotes) {
-        /* end of quoted field */
-        if (!in_field) /* empty quoted string */
-          NEW_FIELD();
-        END_FIELD();
-        in_quotes = false;
-        continue;
-      }
-
-      in_quotes = true;
-      /* if (! in_field): add new field on next iteration
-       * else: quoted string following an unquoted string (one field)
-       * in either case: skip quotation mark */
-      continue;
-    } else if ((string[0] == '\\') && in_quotes) {
-      /* Outside of quotes, a backslash is a regular character (mostly
-       * for backward compatibility). */
-
-      if (string[1] == '\0') {
-        free(fields);
-        cmd_error(CMD_PARSE_ERROR, err, "Backslash at end of string.");
-        return CMD_PARSE_ERROR;
-      }
-
-      /* un-escape the next character; skip backslash */
-      string++;
-    }
-
-    if (!in_field)
-      NEW_FIELD();
-    else {
-      *field = string[0];
-      field++;
-    }
-  }
-
-  if (in_quotes) {
-    free(fields);
-    cmd_error(CMD_PARSE_ERROR, err, "Unterminated quoted string.");
-    return CMD_PARSE_ERROR;
-  }
-
-#undef NEW_FIELD
-#undef END_FIELD
-
-  fields[len] = NULL;
-  if (ret_len != NULL)
-    *ret_len = len;
-  if (ret_fields != NULL)
-    *ret_fields = fields;
-  else
-    free(fields);
-  return CMD_OK;
-} /* int cmd_split */
-
-/*
- * public API
- */
-
-void cmd_error(cmd_status_t status, cmd_error_handler_t *err,
-               const char *format, ...) {
-  va_list ap;
-
-  if ((err == NULL) || (err->cb == NULL))
-    return;
-
-  va_start(ap, format);
-  err->cb(err->ud, status, format, ap);
-  va_end(ap);
-} /* void cmd_error */
-
-cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd,
-                        const cmd_options_t *opts, cmd_error_handler_t *err) {
-  char *command = NULL;
-  cmd_status_t status;
-
-  if ((argc < 1) || (argv == NULL) || (ret_cmd == NULL)) {
-    errno = EINVAL;
-    cmd_error(CMD_ERROR, err, "Missing command.");
-    return CMD_ERROR;
-  }
-
-  if (opts == NULL)
-    opts = &default_options;
-
-  memset(ret_cmd, 0, sizeof(*ret_cmd));
-  command = argv[0];
-  if (strcasecmp("FLUSH", command) == 0) {
-    ret_cmd->type = CMD_FLUSH;
-    status =
-        cmd_parse_flush(argc - 1, argv + 1, &ret_cmd->cmd.flush, opts, err);
-  } else if (strcasecmp("GETVAL", command) == 0) {
-    ret_cmd->type = CMD_GETVAL;
-    status =
-        cmd_parse_getval(argc - 1, argv + 1, &ret_cmd->cmd.getval, opts, err);
-  } else if (strcasecmp("LISTVAL", command) == 0) {
-    ret_cmd->type = CMD_LISTVAL;
-    status =
-        cmd_parse_listval(argc - 1, argv + 1, &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) {
-    WARNING("utils_cmds: failed to write to file-handle #%i: %s", fileno(fh),
-            STRERRNO);
-    return;
-  }
-
-  fflush(fh);
-} /* void cmd_error_fh */
diff --git a/src/utils_cmds.h b/src/utils_cmds.h
deleted file mode 100644 (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_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 a58bc06..0000000
+++ /dev/null
@@ -1,1062 +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 %" PRIsz
-          " value%s, but the configuration specifies %" PRIsz ".",
-          r->type, prep_area->ds->ds_num,
-          (prep_area->ds->ds_num == 1) ? "" : "s", r->values_num);
-    BAIL_OUT(-1);
-  }
-  /* }}} */
-
-  /* Allocate r->instances_pos, r->values_pos, r->metadata_post,
-   * r->instances_buffer, r->values_buffer, and r->metadata_buffer {{{ */
-  if (r->instances_num > 0) {
-    prep_area->instances_pos =
-        (size_t *)calloc(r->instances_num, sizeof(size_t));
-    if (prep_area->instances_pos == NULL) {
-      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[%" 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) {
-    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[%" 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) {
-      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 30a1057..0000000
+++ /dev/null
@@ -1,872 +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);
-
-  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];
-
-    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 fbeff4f..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("%" PRIu64, (uint64_t)vl->values[ds_num].counter);
-  else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
-    BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive);
-  else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
-    BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute);
-  else {
-    ERROR("gr_format_values plugin: Unknown data source type: %i",
-          ds->ds[ds_num].type);
-    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 %" PRIsz " bytes.",
-            message_len + 1);
-      sfree(rates);
-      return -ENOMEM;
-    }
-
-    /* Append it in case we got multiple data set */
-    if ((buffer_pos + message_len) >= buffer_size) {
-      ERROR("format_graphite: target buffer too small");
-      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 6a0c664..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("%" 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
-
-  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 4003243..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("%" 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 */
-
-/* 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 1d3bf2e..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 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) {
-      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 42a6e87..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 "collectd.h"
-#include "common.h" /* for STATIC_ARRAY_SIZE */
-
-#include "testing.h"
-#include "utils_latency.h"
-#include "utils_time.h"
-
-DEF_TEST(simple) {
-  struct {
-    double val;
-    double min;
-    double max;
-    double sum;
-    double avg;
-  } cases[] = {
-      /* val  min  max  sum   avg */
-      {0.5, 0.5, 0.5, 0.5, 0.5}, {0.3, 0.3, 0.5, 0.8, 0.4},
-      {0.7, 0.3, 0.7, 1.5, 0.5}, {2.5, 0.3, 2.5, 4.0, 1.0},
-      {99, 0.3, 99, 103, 20.6},
-      /* { -1, 0.3,  99, 103, 20.6}, see issue #1139 */
-  };
-  latency_counter_t *l;
-
-  CHECK_NOT_NULL(l = latency_counter_create());
-
-  for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
-    printf("# case %" PRIsz ": DOUBLE_TO_CDTIME_T(%g) = %" PRIu64 "\n", i,
-           cases[i].val, DOUBLE_TO_CDTIME_T(cases[i].val));
-    latency_counter_add(l, DOUBLE_TO_CDTIME_T(cases[i].val));
-
-    EXPECT_EQ_DOUBLE(cases[i].min,
-                     CDTIME_T_TO_DOUBLE(latency_counter_get_min(l)));
-    EXPECT_EQ_DOUBLE(cases[i].max,
-                     CDTIME_T_TO_DOUBLE(latency_counter_get_max(l)));
-    EXPECT_EQ_DOUBLE(cases[i].sum,
-                     CDTIME_T_TO_DOUBLE(latency_counter_get_sum(l)));
-    EXPECT_EQ_DOUBLE(cases[i].avg,
-                     CDTIME_T_TO_DOUBLE(latency_counter_get_average(l)));
-  }
-
-  latency_counter_destroy(l);
-  return 0;
-}
-
-DEF_TEST(percentile) {
-  latency_counter_t *l;
-
-  CHECK_NOT_NULL(l = latency_counter_create());
-
-  for (size_t i = 0; i < 100; i++) {
-    latency_counter_add(l, TIME_T_TO_CDTIME_T(((time_t)i) + 1));
-  }
-
-  EXPECT_EQ_DOUBLE(1.0, CDTIME_T_TO_DOUBLE(latency_counter_get_min(l)));
-  EXPECT_EQ_DOUBLE(100.0, CDTIME_T_TO_DOUBLE(latency_counter_get_max(l)));
-  EXPECT_EQ_DOUBLE(100.0 * 101.0 / 2.0,
-                   CDTIME_T_TO_DOUBLE(latency_counter_get_sum(l)));
-  EXPECT_EQ_DOUBLE(50.5, CDTIME_T_TO_DOUBLE(latency_counter_get_average(l)));
-
-  EXPECT_EQ_DOUBLE(50.0,
-                   CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 50.0)));
-  EXPECT_EQ_DOUBLE(80.0,
-                   CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 80.0)));
-  EXPECT_EQ_DOUBLE(95.0,
-                   CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 95.0)));
-  EXPECT_EQ_DOUBLE(99.0,
-                   CDTIME_T_TO_DOUBLE(latency_counter_get_percentile(l, 99.0)));
-
-  CHECK_ZERO(latency_counter_get_percentile(l, -1.0));
-  CHECK_ZERO(latency_counter_get_percentile(l, 101.0));
-
-  latency_counter_destroy(l);
-  return 0;
-}
-
-DEF_TEST(get_rate) {
-  /* We re-declare the struct here so we can inspect its content. */
-  struct {
-    cdtime_t start_time;
-    cdtime_t sum;
-    size_t num;
-    cdtime_t min;
-    cdtime_t max;
-    cdtime_t bin_width;
-    int histogram[HISTOGRAM_NUM_BINS];
-  } * peek;
-  latency_counter_t *l;
-
-  CHECK_NOT_NULL(l = latency_counter_create());
-  peek = (void *)l;
-
-  for (time_t i = 1; i <= 125; i++) {
-    latency_counter_add(l, TIME_T_TO_CDTIME_T(i));
-  }
-
-  /* We expect a bucket width of 125ms. */
-  EXPECT_EQ_UINT64(DOUBLE_TO_CDTIME_T(0.125), peek->bin_width);
-
-  struct {
-    size_t index;
-    int want;
-  } bucket_cases[] = {
-      {0, 0},  /* (0.000-0.125] */
-      {1, 0},  /* (0.125-0.250] */
-      {2, 0},  /* (0.250-0.375] */
-      {3, 0},  /* (0.375-0.500] */
-      {4, 0},  /* (0.500-0.625] */
-      {5, 0},  /* (0.625-0.750] */
-      {6, 0},  /* (0.750-0.875] */
-      {7, 1},  /* (0.875-1.000] */
-      {8, 0},  /* (1.000-1.125] */
-      {9, 0},  /* (1.125-1.250] */
-      {10, 0}, /* (1.250-1.375] */
-      {11, 0}, /* (1.375-1.500] */
-      {12, 0}, /* (1.500-1.625] */
-      {13, 0}, /* (1.625-1.750] */
-      {14, 0}, /* (1.750-1.875] */
-      {15, 1}, /* (1.875-2.000] */
-      {16, 0}, /* (2.000-2.125] */
-  };
-
-  for (size_t i = 0; i < STATIC_ARRAY_SIZE(bucket_cases); i++) {
-    size_t index = bucket_cases[i].index;
-    EXPECT_EQ_INT(bucket_cases[i].want, peek->histogram[index]);
-  }
-
-  struct {
-    cdtime_t lower_bound;
-    cdtime_t upper_bound;
-    double want;
-  } cases[] = {
-      {
-          // bucket 6 is zero
-          DOUBLE_TO_CDTIME_T_STATIC(0.750), DOUBLE_TO_CDTIME_T_STATIC(0.875),
-          0.00,
-      },
-      {
-          // bucket 7 contains the t=1 update
-          DOUBLE_TO_CDTIME_T_STATIC(0.875), DOUBLE_TO_CDTIME_T_STATIC(1.000),
-          1.00,
-      },
-      {
-          // range: bucket 7 - bucket 15; contains the t=1 and t=2 updates
-          DOUBLE_TO_CDTIME_T_STATIC(0.875), DOUBLE_TO_CDTIME_T_STATIC(2.000),
-          2.00,
-      },
-      {
-          // lower bucket is only partially applied
-          DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)),
-          DOUBLE_TO_CDTIME_T_STATIC(2.000), 1.75,
-      },
-      {
-          // upper bucket is only partially applied
-          DOUBLE_TO_CDTIME_T_STATIC(0.875),
-          DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), 1.75,
-      },
-      {
-          // both buckets are only partially applied
-          DOUBLE_TO_CDTIME_T_STATIC(0.875 + (0.125 / 4)),
-          DOUBLE_TO_CDTIME_T_STATIC(2.000 - (0.125 / 4)), 1.50,
-      },
-      {
-          // lower bound is unspecified
-          0, DOUBLE_TO_CDTIME_T_STATIC(2.000), 2.00,
-      },
-      {
-          // upper bound is unspecified
-          DOUBLE_TO_CDTIME_T_STATIC(125.000 - 0.125), 0, 1.00,
-      },
-      {
-          // overflow test: upper >> longest latency
-          DOUBLE_TO_CDTIME_T_STATIC(1.000), DOUBLE_TO_CDTIME_T_STATIC(999999),
-          124.00,
-      },
-      {
-          // overflow test: lower > longest latency
-          DOUBLE_TO_CDTIME_T_STATIC(130), 0, 0.00,
-      },
-      {
-          // lower > upper => error
-          DOUBLE_TO_CDTIME_T_STATIC(10), DOUBLE_TO_CDTIME_T_STATIC(9), NAN,
-      },
-      {
-          // lower == upper => zero
-          DOUBLE_TO_CDTIME_T_STATIC(9), DOUBLE_TO_CDTIME_T_STATIC(9), 0.00,
-      },
-  };
-
-  for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
-    cdtime_t now = peek->start_time + TIME_T_TO_CDTIME_T(1);
-    EXPECT_EQ_DOUBLE(cases[i].want,
-                     latency_counter_get_rate(l, cases[i].lower_bound,
-                                              cases[i].upper_bound, now));
-  }
-
-  latency_counter_destroy(l);
-  return 0;
-}
-
-int main(void) {
-  RUN_TEST(simple);
-  RUN_TEST(percentile);
-  RUN_TEST(get_rate);
-
-  END_TEST;
-}
index 1f060f8..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 "common.h"
+#include "utils/common/common.h"
 #include "utils_lua.h"
 
 static int ltoc_values(lua_State *L, /* {{{ */
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 b8af367..0000000
+++ /dev/null
@@ -1,763 +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
-    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_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 0ee05e0..0000000
+++ /dev/null
@@ -1,1391 +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=%" PRIsz "] %s", len, sjson);
-
-  /* parse json data */
-  jnode = yajl_tree_parse(sjson, yajl_errbuf, sizeof(yajl_errbuf));
-  if (jnode == NULL) {
-    OVS_ERROR("yajl_tree_parse() %s", yajl_errbuf);
-    sfree(sjson);
-    return -1;
-  }
-
-  /* get method name */
-  if ((jval = yajl_tree_get(jnode, method_path, yajl_t_string)) != NULL) {
-    if ((method = YAJL_GET_STRING(jval)) == NULL) {
-      yajl_tree_free(jnode);
-      sfree(sjson);
-      return -1;
-    }
-    if (strcmp("echo", method) == 0) {
-      /* echo request from the server */
-      if (ovs_db_table_echo_cb(pdb, jnode) < 0)
-        OVS_ERROR("handle echo request failed");
-    } else if (strcmp("update", method) == 0) {
-      /* update notification */
-      if (ovs_db_table_update_cb(pdb, jnode) < 0)
-        OVS_ERROR("handle update notification failed");
-    }
-  } else if ((jval = yajl_tree_get(jnode, result_path, yajl_t_any)) != NULL) {
-    /* result notification */
-    if (ovs_db_result_cb(pdb, jnode) < 0)
-      OVS_ERROR("handle result reply failed");
-  } else
-    OVS_ERROR("connot find method or result failed");
-
-  /* release memory */
-  yajl_tree_free(jnode);
-  sfree(sjson);
-  return 0;
-}
-
-/*
- * JSON reader implementation.
- *
- * This module process raw JSON data (byte stream) and
- * returns fully-fledged JSON data which can be processed
- * (parsed) by YAJL later.
- */
-
-/* Allocate JSON reader instance */
-static ovs_json_reader_t *ovs_json_reader_alloc() {
-  ovs_json_reader_t *jreader = NULL;
-
-  if ((jreader = calloc(sizeof(ovs_json_reader_t), 1)) == NULL)
-    return NULL;
-
-  return jreader;
-}
-
-/* Push raw data into into the JSON reader for processing */
-static int ovs_json_reader_push_data(ovs_json_reader_t *jreader,
-                                     const char *data, size_t data_len) {
-  char *new_buff = NULL;
-  size_t available = jreader->buff_size - jreader->buff_offset;
-
-  /* check/update required memory space */
-  if (available < data_len) {
-    OVS_DEBUG("Reallocate buffer [size=%d, available=%d required=%d]",
-              (int)jreader->buff_size, (int)available, (int)data_len);
-
-    /* allocate new chunk of memory */
-    new_buff = realloc(jreader->buff_ptr, (jreader->buff_size + data_len));
-    if (new_buff == NULL)
-      return -1;
-
-    /* point to new allocated memory */
-    jreader->buff_ptr = new_buff;
-    jreader->buff_size += data_len;
-  }
-
-  /* store input data */
-  memcpy(jreader->buff_ptr + jreader->buff_offset, data, data_len);
-  jreader->buff_offset += data_len;
-  return 0;
-}
-
-/* Pop one fully-fledged JSON if already exists. Returns 0 if
- * completed JSON already exists otherwise negative value is
- * returned */
-static int ovs_json_reader_pop(ovs_json_reader_t *jreader,
-                               const char **json_ptr, size_t *json_len_ptr) {
-  size_t nbraces = 0;
-  size_t json_len = 0;
-  char *json = NULL;
-
-  /* search open/close brace */
-  for (size_t i = jreader->json_offset; i < jreader->buff_offset; i++) {
-    if (jreader->buff_ptr[i] == '{') {
-      nbraces++;
-    } else if (jreader->buff_ptr[i] == '}')
-      if (nbraces)
-        if (!(--nbraces)) {
-          /* JSON data */
-          *json_ptr = jreader->buff_ptr + jreader->json_offset;
-          *json_len_ptr = json_len + 1;
-          jreader->json_offset = i + 1;
-          return 0;
-        }
-
-    /* increase JSON data length */
-    if (nbraces)
-      json_len++;
-  }
-
-  if (jreader->json_offset) {
-    if (jreader->json_offset < jreader->buff_offset) {
-      /* shift data to the beginning of the buffer
-       * and zero rest of the buffer data */
-      json = &jreader->buff_ptr[jreader->json_offset];
-      json_len = jreader->buff_offset - jreader->json_offset;
-      for (size_t i = 0; i < jreader->buff_size; i++)
-        jreader->buff_ptr[i] = ((i < json_len) ? (json[i]) : (0));
-      jreader->buff_offset = json_len;
-    } else
-      /* reset the buffer */
-      jreader->buff_offset = 0;
-
-    /* data is at the beginning of the buffer */
-    jreader->json_offset = 0;
-  }
-
-  return -1;
-}
-
-/* Reset JSON reader. It is useful when start processing
- * new raw data. E.g.: in case of lost stream connection.
- */
-static void ovs_json_reader_reset(ovs_json_reader_t *jreader) {
-  if (jreader) {
-    jreader->buff_offset = 0;
-    jreader->json_offset = 0;
-  }
-}
-
-/* Release internal data allocated for JSON reader */
-static void ovs_json_reader_free(ovs_json_reader_t *jreader) {
-  if (jreader) {
-    free(jreader->buff_ptr);
-    free(jreader);
-  }
-}
-
-/* Reconnect to OVS DB and call the OVS DB post connection init callback
- * if connection has been established.
- */
-static void ovs_db_reconnect(ovs_db_t *pdb) {
-  const char *node_info = pdb->node;
-  struct addrinfo *result;
-
-  if (pdb->unix_path[0] != '\0') {
-    /* use UNIX socket instead of INET address */
-    node_info = pdb->unix_path;
-    result = calloc(1, sizeof(struct addrinfo));
-    struct sockaddr_un *sa_unix = calloc(1, sizeof(struct sockaddr_un));
-    if (result == NULL || sa_unix == NULL) {
-      sfree(result);
-      sfree(sa_unix);
-      return;
-    }
-    result->ai_family = AF_UNIX;
-    result->ai_socktype = SOCK_STREAM;
-    result->ai_addrlen = sizeof(*sa_unix);
-    result->ai_addr = (struct sockaddr *)sa_unix;
-    sa_unix->sun_family = result->ai_family;
-    sstrncpy(sa_unix->sun_path, pdb->unix_path, sizeof(sa_unix->sun_path));
-  } else {
-    /* inet socket address */
-    struct addrinfo hints;
-
-    /* setup criteria for selecting the socket address */
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family = AF_UNSPEC;
-    hints.ai_socktype = SOCK_STREAM;
-
-    /* get socket addresses */
-    int ret = getaddrinfo(pdb->node, pdb->service, &hints, &result);
-    if (ret != 0) {
-      OVS_ERROR("getaddrinfo(): %s", gai_strerror(ret));
-      return;
-    }
-  }
-  /* try to connect to the server */
-  for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) {
-    int sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
-    if (sock < 0) {
-      OVS_DEBUG("socket(): %s", STRERRNO);
-      continue;
-    }
-    if (connect(sock, rp->ai_addr, rp->ai_addrlen) < 0) {
-      close(sock);
-      OVS_DEBUG("connect(): %s [family=%d]", STRERRNO, rp->ai_family);
-    } else {
-      /* send notification to event thread */
-      pdb->sock = sock;
-      ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_ESTABLISHED);
-      break;
-    }
-  }
-
-  if (pdb->sock < 0)
-    OVS_ERROR("connect to \"%s\" failed", node_info);
-
-  freeaddrinfo(result);
-}
-
-/* POLL worker thread.
- * It listens on OVS DB connection for incoming
- * requests/reply/events etc. Also, it reconnects to OVS DB
- * if connection has been lost.
- */
-static void *ovs_poll_worker(void *arg) {
-  ovs_db_t *pdb = (ovs_db_t *)arg; /* pointer to OVS DB */
-  ovs_json_reader_t *jreader = NULL;
-  struct pollfd poll_fd = {
-      .fd = pdb->sock, .events = POLLIN | POLLPRI, .revents = 0,
-  };
-
-  /* create JSON reader instance */
-  if ((jreader = ovs_json_reader_alloc()) == NULL) {
-    OVS_ERROR("initialize json reader failed");
-    return NULL;
-  }
-
-  /* poll data */
-  while (ovs_db_poll_is_running(pdb)) {
-    poll_fd.fd = pdb->sock;
-    int poll_ret = poll(&poll_fd, 1, /* ms */ OVS_DB_POLL_TIMEOUT * 1000);
-    if (poll_ret < 0) {
-      OVS_ERROR("poll(): %s", STRERRNO);
-      break;
-    } else if (poll_ret == 0) {
-      OVS_DEBUG("poll(): timeout");
-      if (pdb->sock < 0)
-        /* invalid fd, so try to reconnect */
-        ovs_db_reconnect(pdb);
-      continue;
-    }
-    if (poll_fd.revents & POLLNVAL) {
-      /* invalid file descriptor, clean-up */
-      ovs_db_callback_remove_all(pdb);
-      ovs_json_reader_reset(jreader);
-      /* setting poll FD to -1 tells poll() call to ignore this FD.
-       * In that case poll() call will return timeout all the time */
-      pdb->sock = (-1);
-    } else if ((poll_fd.revents & POLLERR) || (poll_fd.revents & POLLHUP)) {
-      /* connection is broken */
-      close(poll_fd.fd);
-      ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED);
-      OVS_ERROR("poll() peer closed its end of the channel");
-    } else if ((poll_fd.revents & POLLIN) || (poll_fd.revents & POLLPRI)) {
-      /* read incoming data */
-      char buff[OVS_DB_POLL_READ_BLOCK_SIZE];
-      ssize_t nbytes = recv(poll_fd.fd, buff, sizeof(buff), 0);
-      if (nbytes < 0) {
-        OVS_ERROR("recv(): %s", STRERRNO);
-        /* read error? Try to reconnect */
-        close(poll_fd.fd);
-        continue;
-      } else if (nbytes == 0) {
-        close(poll_fd.fd);
-        ovs_db_event_post(pdb, OVS_DB_EVENT_CONN_TERMINATED);
-        OVS_ERROR("recv() peer has performed an orderly shutdown");
-        continue;
-      }
-      /* read incoming data */
-      size_t json_len = 0;
-      const char *json = NULL;
-      OVS_DEBUG("recv(): received %zd bytes of data", nbytes);
-      ovs_json_reader_push_data(jreader, buff, nbytes);
-      while (!ovs_json_reader_pop(jreader, &json, &json_len))
-        /* process JSON data */
-        ovs_db_json_data_process(pdb, json, json_len);
-    }
-  }
-
-  OVS_DEBUG("poll thread has been completed");
-  ovs_json_reader_free(jreader);
-  return NULL;
-}
-
-/* EVENT worker thread.
- * Perform task based on incoming events. This
- * task can be done asynchronously which allows to
- * handle OVS DB callback like 'init_cb'.
- */
-static void *ovs_event_worker(void *arg) {
-  ovs_db_t *pdb = (ovs_db_t *)arg;
-
-  while (pdb->event_thread.value != OVS_DB_EVENT_TERMINATE) {
-    /* wait for an event */
-    struct timespec ts;
-    clock_gettime(CLOCK_REALTIME, &ts);
-    ts.tv_sec += (OVS_DB_EVENT_TIMEOUT);
-    int ret = pthread_cond_timedwait(&pdb->event_thread.cond,
-                                     &pdb->event_thread.mutex, &ts);
-    if (!ret || ret == ETIMEDOUT) {
-      /* handle the event */
-      OVS_DEBUG("handle event %d", pdb->event_thread.value);
-      switch (pdb->event_thread.value) {
-      case OVS_DB_EVENT_CONN_ESTABLISHED:
-        if (pdb->cb.post_conn_init)
-          pdb->cb.post_conn_init(pdb);
-        /* reset event */
-        pdb->event_thread.value = OVS_DB_EVENT_NONE;
-        break;
-      case OVS_DB_EVENT_CONN_TERMINATED:
-        if (pdb->cb.post_conn_terminate)
-          pdb->cb.post_conn_terminate();
-        /* reset event */
-        pdb->event_thread.value = OVS_DB_EVENT_NONE;
-        break;
-      case OVS_DB_EVENT_NONE:
-        /* wait timeout */
-        OVS_DEBUG("no event received (timeout)");
-        break;
-      default:
-        OVS_DEBUG("unknown event received");
-        break;
-      }
-    } else {
-      /* unexpected error */
-      OVS_ERROR("pthread_cond_timedwait() failed");
-      break;
-    }
-  }
-
-  OVS_DEBUG("event thread has been completed");
-  return NULL;
-}
-
-/* Initialize EVENT thread */
-static int ovs_db_event_thread_init(ovs_db_t *pdb) {
-  pdb->event_thread.tid = (pthread_t){0};
-  /* init event thread condition variable */
-  if (pthread_cond_init(&pdb->event_thread.cond, NULL)) {
-    return -1;
-  }
-  /* init event thread mutex */
-  if (pthread_mutex_init(&pdb->event_thread.mutex, NULL)) {
-    pthread_cond_destroy(&pdb->event_thread.cond);
-    return -1;
-  }
-  /* Hold the event thread mutex. It ensures that no events
-   * will be lost while thread is still starting. Once event
-   * thread is started and ready to accept events, it will release
-   * the mutex */
-  if (pthread_mutex_lock(&pdb->event_thread.mutex)) {
-    pthread_mutex_destroy(&pdb->event_thread.mutex);
-    pthread_cond_destroy(&pdb->event_thread.cond);
-    return -1;
-  }
-  /* start event thread */
-  pthread_t tid;
-  if (plugin_thread_create(&tid, NULL, ovs_event_worker, pdb,
-                           "utils_ovs:event") != 0) {
-    pthread_mutex_unlock(&pdb->event_thread.mutex);
-    pthread_mutex_destroy(&pdb->event_thread.mutex);
-    pthread_cond_destroy(&pdb->event_thread.cond);
-    return -1;
-  }
-  pdb->event_thread.tid = tid;
-  return 0;
-}
-
-/* Terminate EVENT thread */
-static int ovs_db_event_thread_terminate(ovs_db_t *pdb) {
-  if (pthread_equal(pdb->event_thread.tid, (pthread_t){0})) {
-    /* already terminated */
-    return 0;
-  }
-  ovs_db_event_post(pdb, OVS_DB_EVENT_TERMINATE);
-  if (pthread_join(pdb->event_thread.tid, NULL) != 0)
-    return -1;
-  /* Event thread always holds the thread mutex when
-   * performs some task (handles event) and releases it when
-   * while sleeping. Thus, if event thread exits, the mutex
-   * remains locked */
-  pdb->event_thread.tid = (pthread_t){0};
-  pthread_mutex_unlock(&pdb->event_thread.mutex);
-  return 0;
-}
-
-/* Destroy EVENT thread private data */
-static void ovs_db_event_thread_data_destroy(ovs_db_t *pdb) {
-  /* destroy mutex */
-  pthread_mutex_destroy(&pdb->event_thread.mutex);
-  pthread_cond_destroy(&pdb->event_thread.cond);
-}
-
-/* Initialize POLL thread */
-static int ovs_db_poll_thread_init(ovs_db_t *pdb) {
-  pdb->poll_thread.tid = (pthread_t){0};
-  /* init event thread mutex */
-  if (pthread_mutex_init(&pdb->poll_thread.mutex, NULL)) {
-    return -1;
-  }
-  /* start poll thread */
-  pthread_t tid;
-  pdb->poll_thread.state = OVS_DB_POLL_STATE_RUNNING;
-  if (plugin_thread_create(&tid, NULL, ovs_poll_worker, pdb,
-                           "utils_ovs:poll") != 0) {
-    pthread_mutex_destroy(&pdb->poll_thread.mutex);
-    return -1;
-  }
-  pdb->poll_thread.tid = tid;
-  return 0;
-}
-
-/* Destroy POLL thread */
-/* XXX: Must hold pdb->mutex when calling! */
-static int ovs_db_poll_thread_destroy(ovs_db_t *pdb) {
-  if (pthread_equal(pdb->poll_thread.tid, (pthread_t){0})) {
-    /* already destroyed */
-    return 0;
-  }
-  /* change thread state */
-  pthread_mutex_lock(&pdb->poll_thread.mutex);
-  pdb->poll_thread.state = OVS_DB_POLL_STATE_EXITING;
-  pthread_mutex_unlock(&pdb->poll_thread.mutex);
-  /* join the thread */
-  if (pthread_join(pdb->poll_thread.tid, NULL) != 0)
-    return -1;
-  pthread_mutex_destroy(&pdb->poll_thread.mutex);
-  pdb->poll_thread.tid = (pthread_t){0};
-  return 0;
-}
-
-/*
- * Public OVS DB API implementation
- */
-
-ovs_db_t *ovs_db_init(const char *node, const char *service,
-                      const char *unix_path, ovs_db_callback_t *cb) {
-  /* 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 ef12601..0000000
+++ /dev/null
@@ -1,659 +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) {
-    ERROR("rrdtool plugin: 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 {
-      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) {
-    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) {
-    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) {
-    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) {
-    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)
-      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 b5dc5af..0000000
+++ /dev/null
@@ -1,226 +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) {
-    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) {
-      INFO("utils_tail: File `%s' was truncated.", obj->file);
-      status = fseek(obj->fh, 0, SEEK_SET);
-      if (status != 0) {
-        ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
-        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) {
-    ERROR("utils_tail: fopen (%s) failed: %s", obj->file, STRERRNO);
-    return -1;
-  }
-
-  if (seek_end != 0) {
-    status = fseek(fh, 0, SEEK_END);
-    if (status != 0) {
-      ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
-      fclose(fh);
-      return -1;
-    }
-  }
-
-  if (obj->fh != NULL)
-    fclose(obj->fh);
-  obj->fh = fh;
-  memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
-
-  return 0;
-} /* int cu_tail_reopen */
-
-cu_tail_t *cu_tail_create(const char *file) {
-  cu_tail_t *obj;
-
-  obj = calloc(1, sizeof(*obj));
-  if (obj == NULL)
-    return NULL;
-
-  obj->file = strdup(file);
-  if (obj->file == NULL) {
-    free(obj);
-    return NULL;
-  }
-
-  obj->fh = NULL;
-
-  return obj;
-} /* cu_tail_t *cu_tail_create */
-
-int cu_tail_destroy(cu_tail_t *obj) {
-  if (obj->fh != NULL)
-    fclose(obj->fh);
-  free(obj->file);
-  free(obj);
-
-  return 0;
-} /* int cu_tail_destroy */
-
-int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) {
-  int status;
-
-  if (buflen < 1) {
-    ERROR("utils_tail: cu_tail_readline: buflen too small: %i bytes.", buflen);
-    return -1;
-  }
-
-  if (obj->fh == NULL) {
-    status = cu_tail_reopen(obj);
-    if (status < 0)
-      return status;
-  }
-  assert(obj->fh != NULL);
-
-  /* Try to read from the filehandle. If that succeeds, everything appears to
-   * be fine and we can return. */
-  clearerr(obj->fh);
-  if (fgets(buf, buflen, obj->fh) != NULL) {
-    buf[buflen - 1] = 0;
-    return 0;
-  }
-
-  /* Check if we encountered an error */
-  if (ferror(obj->fh) != 0) {
-    /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */
-    fclose(obj->fh);
-    obj->fh = NULL;
-  }
-  /* else: eof -> check if the file was moved away and reopen the new file if
-   * so.. */
-
-  status = cu_tail_reopen(obj);
-  /* error -> return with error */
-  if (status < 0)
-    return status;
-  /* file end reached and file not reopened -> nothing more to read */
-  else if (status > 0) {
-    buf[0] = 0;
-    return 0;
-  }
-
-  /* If we get here: file was re-opened and there may be more to read.. Let's
-   * try again. */
-  if (fgets(buf, buflen, obj->fh) != NULL) {
-    buf[buflen - 1] = 0;
-    return 0;
-  }
-
-  if (ferror(obj->fh) != 0) {
-    WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file,
-            STRERRNO);
-    fclose(obj->fh);
-    obj->fh = NULL;
-    return -1;
-  }
-
-  /* EOf, well, apparently the new file is empty.. */
-  buf[0] = 0;
-  return 0;
-} /* int cu_tail_readline */
-
-int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
-                 void *data) {
-  int status;
-
-  while (42) {
-    size_t len;
-
-    status = cu_tail_readline(obj, buf, buflen);
-    if (status != 0) {
-      ERROR("utils_tail: cu_tail_read: cu_tail_readline "
-            "failed.");
-      break;
-    }
-
-    /* check for EOF */
-    if (buf[0] == 0)
-      break;
-
-    len = strlen(buf);
-    while (len > 0) {
-      if (buf[len - 1] != '\n')
-        break;
-      buf[len - 1] = '\0';
-      len--;
-    }
-
-    status = callback(data, buf, buflen);
-    if (status != 0) {
-      ERROR("utils_tail: cu_tail_read: callback returned "
-            "status %i.",
-            status);
-      break;
-    }
-  }
-
-  return status;
-} /* int cu_tail_read */
diff --git a/src/utils_tail.h b/src/utils_tail.h
deleted file mode 100644 (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_taskstats.c b/src/utils_taskstats.c
deleted file mode 100644 (file)
index f0d7333..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-/**
- * collectd - src/utils_taskstats.c
- * Copyright (C) 2017       Florian octo Forster
- *
- * ISC License (ISC)
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <octo at collectd.org>
- */
-
-#include "collectd.h"
-#include "utils_taskstats.h"
-
-#include "common.h"
-#include "plugin.h"
-#include "utils_time.h"
-
-#include <libmnl/libmnl.h>
-#include <linux/genetlink.h>
-#include <linux/taskstats.h>
-
-struct ts_s {
-  struct mnl_socket *nl;
-  pid_t pid;
-  uint32_t seq;
-  uint16_t genl_id_taskstats;
-  unsigned int port_id;
-};
-
-/* nlmsg_errno returns the errno encoded in nlh or zero if not an error. */
-static int nlmsg_errno(struct nlmsghdr *nlh, size_t sz) {
-  if (!mnl_nlmsg_ok(nlh, (int)sz)) {
-    ERROR("utils_taskstats: mnl_nlmsg_ok failed.");
-    return EPROTO;
-  }
-
-  if (nlh->nlmsg_type != NLMSG_ERROR) {
-    return 0;
-  }
-
-  struct nlmsgerr *nlerr = mnl_nlmsg_get_payload(nlh);
-  /* (struct nlmsgerr).error holds a negative errno. */
-  return nlerr->error * (-1);
-}
-
-static int get_taskstats_attr_cb(const struct nlattr *attr, void *data) {
-  struct taskstats *ret_taskstats = data;
-
-  uint16_t type = mnl_attr_get_type(attr);
-  switch (type) {
-  case TASKSTATS_TYPE_STATS:
-    if (mnl_attr_get_payload_len(attr) != sizeof(*ret_taskstats)) {
-      ERROR("utils_taskstats: mnl_attr_get_payload_len(attr) = %" PRIu32
-            ", want %zu",
-            mnl_attr_get_payload_len(attr), sizeof(*ret_taskstats));
-      return MNL_CB_ERROR;
-    }
-    struct taskstats *ts = mnl_attr_get_payload(attr);
-    memmove(ret_taskstats, ts, sizeof(*ret_taskstats));
-    return MNL_CB_OK;
-
-  case TASKSTATS_TYPE_AGGR_PID: /* fall through */
-  case TASKSTATS_TYPE_AGGR_TGID:
-    return mnl_attr_parse_nested(attr, get_taskstats_attr_cb, ret_taskstats);
-
-  case TASKSTATS_TYPE_PID: /* fall through */
-  case TASKSTATS_TYPE_TGID:
-    /* ignore */
-    return MNL_CB_OK;
-
-  default:
-    DEBUG("utils_taskstats: unknown attribute %" PRIu16
-          ", want one of TASKSTATS_TYPE_AGGR_PID/TGID, TASKSTATS_TYPE_STATS",
-          type);
-  }
-  return MNL_CB_OK;
-}
-
-static int get_taskstats_msg_cb(const struct nlmsghdr *nlh, void *data) {
-  return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_taskstats_attr_cb,
-                        data);
-}
-
-static int get_taskstats(ts_t *ts, uint32_t tgid,
-                         struct taskstats *ret_taskstats) {
-  char buffer[MNL_SOCKET_BUFFER_SIZE];
-  uint32_t seq = ts->seq++;
-
-  struct nlmsghdr *nlh = mnl_nlmsg_put_header(buffer);
-  *nlh = (struct nlmsghdr){
-      .nlmsg_len = nlh->nlmsg_len,
-      .nlmsg_type = ts->genl_id_taskstats,
-      .nlmsg_flags = NLM_F_REQUEST,
-      .nlmsg_seq = seq,
-      .nlmsg_pid = ts->pid,
-  };
-
-  struct genlmsghdr *genh = mnl_nlmsg_put_extra_header(nlh, sizeof(*genh));
-  *genh = (struct genlmsghdr){
-      .cmd = TASKSTATS_CMD_GET,
-      .version = TASKSTATS_GENL_VERSION, // or TASKSTATS_VERSION?
-  };
-
-  // mnl_attr_put_u32(nlh, TASKSTATS_CMD_ATTR_PID, tgid);
-  mnl_attr_put_u32(nlh, TASKSTATS_CMD_ATTR_TGID, tgid);
-
-  if (mnl_socket_sendto(ts->nl, nlh, nlh->nlmsg_len) < 0) {
-    int status = errno;
-    ERROR("utils_taskstats: mnl_socket_sendto() = %s", STRERROR(status));
-    return status;
-  }
-
-  int status = mnl_socket_recvfrom(ts->nl, buffer, sizeof(buffer));
-  if (status < 0) {
-    status = errno;
-    ERROR("utils_taskstats: mnl_socket_recvfrom() = %s", STRERROR(status));
-    return status;
-  } else if (status == 0) {
-    ERROR("utils_taskstats: mnl_socket_recvfrom() = 0");
-    return ECONNABORTED;
-  }
-  size_t buffer_size = (size_t)status;
-
-  if ((status = nlmsg_errno((void *)buffer, buffer_size)) != 0) {
-    ERROR("utils_taskstats: TASKSTATS_CMD_GET(TASKSTATS_CMD_ATTR_TGID = "
-          "%" PRIu32 ") = %s",
-          (uint32_t)tgid, STRERROR(status));
-    return status;
-  }
-
-  status = mnl_cb_run(buffer, buffer_size, seq, ts->port_id,
-                      get_taskstats_msg_cb, ret_taskstats);
-  if (status < MNL_CB_STOP) {
-    ERROR("utils_taskstats: Parsing message failed.");
-    return EPROTO;
-  }
-
-  return 0;
-}
-
-static int get_family_id_attr_cb(const struct nlattr *attr, void *data) {
-  uint16_t type = mnl_attr_get_type(attr);
-  if (type != CTRL_ATTR_FAMILY_ID) {
-    return MNL_CB_OK;
-  }
-
-  if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
-    ERROR("mnl_attr_validate() = %s", STRERRNO);
-    return MNL_CB_ERROR;
-  }
-
-  uint16_t *ret_family_id = data;
-  *ret_family_id = mnl_attr_get_u16(attr);
-  return MNL_CB_STOP;
-}
-
-static int get_family_id_msg_cb(const struct nlmsghdr *nlh, void *data) {
-  return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb,
-                        data);
-}
-
-/* get_family_id initializes ts->genl_id_taskstats. Returns 0 on success and
- * an error code otherwise. */
-static int get_family_id(ts_t *ts) {
-  char buffer[MNL_SOCKET_BUFFER_SIZE];
-  uint32_t seq = ts->seq++;
-
-  struct nlmsghdr *nlh = mnl_nlmsg_put_header(buffer);
-  *nlh = (struct nlmsghdr){
-      .nlmsg_len = nlh->nlmsg_len,
-      .nlmsg_type = GENL_ID_CTRL,
-      .nlmsg_flags = NLM_F_REQUEST,
-      .nlmsg_seq = seq,
-      .nlmsg_pid = ts->pid,
-  };
-
-  struct genlmsghdr *genh = mnl_nlmsg_put_extra_header(nlh, sizeof(*genh));
-  *genh = (struct genlmsghdr){
-      .cmd = CTRL_CMD_GETFAMILY, .version = 0x01,
-  };
-
-  mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, TASKSTATS_GENL_NAME);
-
-  assert(genh->cmd == CTRL_CMD_GETFAMILY);
-  assert(genh->version == TASKSTATS_GENL_VERSION);
-
-  if (mnl_socket_sendto(ts->nl, nlh, nlh->nlmsg_len) < 0) {
-    int status = errno;
-    ERROR("utils_taskstats: mnl_socket_sendto() = %s", STRERROR(status));
-    return status;
-  }
-
-  ts->genl_id_taskstats = 0;
-  while (42) {
-    int status = mnl_socket_recvfrom(ts->nl, buffer, sizeof(buffer));
-    if (status < 0) {
-      status = errno;
-      ERROR("utils_taskstats: mnl_socket_recvfrom() = %s", STRERROR(status));
-      return status;
-    } else if (status == 0) {
-      break;
-    }
-    size_t buffer_size = (size_t)status;
-
-    if ((status = nlmsg_errno((void *)buffer, buffer_size)) != 0) {
-      ERROR("utils_taskstats: CTRL_CMD_GETFAMILY(\"%s\"): %s",
-            TASKSTATS_GENL_NAME, STRERROR(status));
-      return status;
-    }
-
-    status = mnl_cb_run(buffer, buffer_size, seq, ts->port_id,
-                        get_family_id_msg_cb, &ts->genl_id_taskstats);
-    if (status < MNL_CB_STOP) {
-      ERROR("utils_taskstats: Parsing message failed.");
-      return EPROTO;
-    } else if (status == MNL_CB_STOP) {
-      break;
-    }
-  }
-
-  if (ts->genl_id_taskstats == 0) {
-    ERROR("utils_taskstats: Netlink communication succeeded, but "
-          "genl_id_taskstats is still zero.");
-    return ENOENT;
-  }
-
-  return 0;
-}
-
-void ts_destroy(ts_t *ts) {
-  if (ts == NULL) {
-    return;
-  }
-
-  if (ts->nl != NULL) {
-    mnl_socket_close(ts->nl);
-    ts->nl = NULL;
-  }
-
-  sfree(ts);
-}
-
-ts_t *ts_create(void) {
-  ts_t *ts = calloc(1, sizeof(*ts));
-  if (ts == NULL) {
-    ERROR("utils_taskstats: calloc failed: %s", STRERRNO);
-    return NULL;
-  }
-
-  if ((ts->nl = mnl_socket_open(NETLINK_GENERIC)) == NULL) {
-    ERROR("utils_taskstats: mnl_socket_open(NETLINK_GENERIC) = %s", STRERRNO);
-    ts_destroy(ts);
-    return NULL;
-  }
-
-  if (mnl_socket_bind(ts->nl, 0, MNL_SOCKET_AUTOPID) != 0) {
-    ERROR("utils_taskstats: mnl_socket_bind() = %s", STRERRNO);
-    ts_destroy(ts);
-    return NULL;
-  }
-
-  ts->pid = getpid();
-  ts->port_id = mnl_socket_get_portid(ts->nl);
-
-  int status = get_family_id(ts);
-  if (status != 0) {
-    ERROR("utils_taskstats: get_family_id() = %s", STRERROR(status));
-    ts_destroy(ts);
-    return NULL;
-  }
-
-  return ts;
-}
-
-int ts_delay_by_tgid(ts_t *ts, uint32_t tgid, ts_delay_t *out) {
-  if ((ts == NULL) || (out == NULL)) {
-    return EINVAL;
-  }
-
-  struct taskstats raw = {0};
-
-  int status = get_taskstats(ts, tgid, &raw);
-  if (status != 0) {
-    return status;
-  }
-
-  *out = (ts_delay_t){
-      .cpu_ns = raw.cpu_delay_total,
-      .blkio_ns = raw.blkio_delay_total,
-      .swapin_ns = raw.swapin_delay_total,
-      .freepages_ns = raw.freepages_delay_total,
-  };
-  return 0;
-}
diff --git a/src/utils_taskstats.h b/src/utils_taskstats.h
deleted file mode 100644 (file)
index de07427..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * collectd - src/utils_taskstats.h
- * Copyright (C) 2017       Florian octo Forster
- *
- * ISC License (ISC)
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors:
- *   Florian octo Forster <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_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 4846841..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;
 
@@ -193,7 +190,7 @@ static int uuid_init(void) {
   char *uuid = uuid_get_local();
 
   if (uuid) {
-    sstrncpy(hostname_g, uuid, DATA_MAX_NAME_LEN);
+    hostname_set(uuid);
     sfree(uuid);
     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 06e2408..b9b224e 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 (Sep 2012)
+  Note that LIBVIR_CHECK_VERSION appeared a year later (Dec 2013,
+  libvirt-1.2.0),
+  so in some systems which actually have virConnectListAllDomains()
+  we can't detect this.
+ */
+#if LIBVIR_CHECK_VERSION(0, 10, 2)
+#define HAVE_LIST_ALL_DOMAINS 1
+#endif
+
 #if LIBVIR_CHECK_VERSION(1, 0, 1)
 #define HAVE_DOM_REASON_PAUSED_SNAPSHOT 1
 #endif
 #define HAVE_DOM_REASON_POSTCOPY 1
 #endif
 
+#if LIBVIR_CHECK_VERSION(4, 10, 0)
+#define HAVE_DOM_REASON_SHUTOFF_DAEMON 1
+#endif
 #endif /* LIBVIR_CHECK_VERSION */
 
-static const char *config_keys[] = {"Connection",
-
-                                    "RefreshInterval",
+/* 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;
 
-                                    "Domain",
-                                    "BlockDevice",
-                                    "BlockDeviceFormat",
-                                    "BlockDeviceFormatBasename",
-                                    "InterfaceDevice",
-                                    "IgnoreSelected",
+/* PersistentNotification is false by default */
+static bool persistent_notification = false;
 
-                                    "HostnameFormat",
-                                    "InterfaceFormat",
+static bool report_block_devices = true;
+static bool report_network_interfaces = true;
 
-                                    "PluginInstanceFormat",
-
-                                    "Instances",
-                                    "ExtraStats",
-                                    NULL};
+/* Thread used for handling libvirt notifications events */
+static virt_notif_thread_t notif_thread;
 
 const char *domain_states[] = {
         [VIR_DOMAIN_NOSTATE] = "no state",
@@ -124,7 +144,212 @@ 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 */
+#ifdef LIBVIR_CHECK_VERSION
+#if LIBVIR_CHECK_VERSION(3, 4, 0)
+    case VIR_DOMAIN_EVENT_SHUTDOWN_GUEST: /* Domain finished shutting down after
+                                             request from the guest itself (e.g.
+                                             hardware-specific action) */
+    case VIR_DOMAIN_EVENT_SHUTDOWN_HOST:  /* Domain finished shutting down after
+                                             request from the host (e.g. killed
+                                             by a signal) */
+#endif
+#endif
+      ret = VIR_DOMAIN_SHUTDOWN_USER;
+      break;
+    default:
+      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 +383,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 +422,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] =
@@ -216,6 +439,10 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
             "domain failed to start",
         [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT] =
             "restored from a snapshot which was taken while domain was shutoff",
+#ifdef HAVE_DOM_REASON_SHUTOFF_DAEMON
+        [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_DAEMON] =
+            "daemon decides to kill domain during reconnection processing",
+#endif
 
         [VIR_DOMAIN_CRASHED][VIR_DOMAIN_CRASHED_UNKNOWN] =
             "the reason is unknown",
@@ -230,19 +457,18 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = {
 };
 #endif /* HAVE_DOM_REASON */
 
-#define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
 #define NANOSEC_IN_SEC 1e9
 
 #define GET_STATS(_f, _name, ...)                                              \
   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 +478,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);
@@ -265,6 +491,7 @@ static int ignore_device_match(ignorelist_t *, const char *domname,
 struct block_device {
   virDomainPtr dom; /* domain */
   char *path;       /* name of block device */
+  bool has_source;  /* information whether source is defined or not */
 };
 
 /* Actual list of network interfaces found on last refresh. */
@@ -278,6 +505,7 @@ struct interface_device {
 typedef struct domain_s {
   virDomainPtr ptr;
   virDomainInfo info;
+  bool active;
 } domain_t;
 
 struct lv_read_state {
@@ -293,11 +521,12 @@ 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,
-                            const char *path);
+                            const char *path, bool has_source);
 
 static void free_interface_devices(struct lv_read_state *state);
 static int add_interface_device(struct lv_read_state *state, virDomainPtr dom,
@@ -328,20 +557,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 };
 
@@ -370,6 +608,11 @@ enum ex_stats {
   ex_stats_job_stats_completed = 1 << 8,
   ex_stats_job_stats_background = 1 << 9,
 #endif
+  ex_stats_disk_allocation = 1 << 10,
+  ex_stats_disk_capacity = 1 << 11,
+  ex_stats_disk_physical = 1 << 12,
+  ex_stats_memory = 1 << 13,
+  ex_stats_vcpu = 1 << 14
 };
 
 static unsigned int extra_stats = ex_stats_none;
@@ -397,11 +640,16 @@ static const struct ex_stats_item ex_stats_table[] = {
     {"job_stats_completed", ex_stats_job_stats_completed},
     {"job_stats_background", ex_stats_job_stats_background},
 #endif
+    {"disk_allocation", ex_stats_disk_allocation},
+    {"disk_capacity", ex_stats_disk_capacity},
+    {"disk_physical", ex_stats_disk_physical},
+    {"memory", ex_stats_memory},
+    {"vcpu", ex_stats_vcpu},
     {NULL, ex_stats_none},
 };
 
 /* 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;
 
@@ -409,14 +657,10 @@ static enum if_field interface_format = if_name;
 static time_t last_refresh = (time_t)0;
 
 static int refresh_lists(struct lv_read_instance *inst);
+static int register_event_impl(void);
+static int start_event_loop(virt_notif_thread_t *thread_data);
 
-struct lv_info {
-  virDomainInfo di;
-  unsigned long long total_user_cpu_time;
-  unsigned long long total_syst_cpu_time;
-};
-
-struct lv_block_info {
+struct lv_block_stats {
   virDomainBlockStatsStruct bi;
 
   long long rd_total_times;
@@ -426,50 +670,56 @@ struct lv_block_info {
   long long fl_total_times;
 };
 
-static void init_block_info(struct lv_block_info *binfo) {
-  if (binfo == NULL)
+static void init_block_stats(struct lv_block_stats *bstats) {
+  if (bstats == NULL)
     return;
 
-  binfo->bi.rd_req = -1;
-  binfo->bi.wr_req = -1;
-  binfo->bi.rd_bytes = -1;
-  binfo->bi.wr_bytes = -1;
+  bstats->bi.rd_req = -1;
+  bstats->bi.wr_req = -1;
+  bstats->bi.rd_bytes = -1;
+  bstats->bi.wr_bytes = -1;
+
+  bstats->rd_total_times = -1;
+  bstats->wr_total_times = -1;
+  bstats->fl_req = -1;
+  bstats->fl_total_times = -1;
+}
 
-  binfo->rd_total_times = -1;
-  binfo->wr_total_times = -1;
-  binfo->fl_req = -1;
-  binfo->fl_total_times = -1;
+static void init_block_info(virDomainBlockInfoPtr binfo) {
+  binfo->allocation = -1;
+  binfo->capacity = -1;
+  binfo->physical = -1;
 }
 
 #ifdef HAVE_BLOCK_STATS_FLAGS
 
-#define GET_BLOCK_INFO_VALUE(NAME, FIELD)                                      \
+#define GET_BLOCK_STATS_VALUE(NAME, FIELD)                                     \
   if (!strcmp(param[i].field, NAME)) {                                         \
-    binfo->FIELD = param[i].value.l;                                           \
+    bstats->FIELD = param[i].value.l;                                          \
     continue;                                                                  \
   }
 
-static int get_block_info(struct lv_block_info *binfo,
-                          virTypedParameterPtr param, int nparams) {
-  if (binfo == NULL || param == NULL)
+static int get_block_stats(struct lv_block_stats *bstats,
+                           virTypedParameterPtr param, int nparams) {
+  if (bstats == NULL || param == NULL)
     return -1;
 
   for (int i = 0; i < nparams; ++i) {
     /* ignore type. Everything must be LLONG anyway. */
-    GET_BLOCK_INFO_VALUE("rd_operations", bi.rd_req);
-    GET_BLOCK_INFO_VALUE("wr_operations", bi.wr_req);
-    GET_BLOCK_INFO_VALUE("rd_bytes", bi.rd_bytes);
-    GET_BLOCK_INFO_VALUE("wr_bytes", bi.wr_bytes);
-    GET_BLOCK_INFO_VALUE("rd_total_times", rd_total_times);
-    GET_BLOCK_INFO_VALUE("wr_total_times", wr_total_times);
-    GET_BLOCK_INFO_VALUE("flush_operations", fl_req);
-    GET_BLOCK_INFO_VALUE("flush_total_times", fl_total_times);
+    GET_BLOCK_STATS_VALUE("rd_operations", bi.rd_req);
+    GET_BLOCK_STATS_VALUE("wr_operations", bi.wr_req);
+    GET_BLOCK_STATS_VALUE("rd_bytes", bi.rd_bytes);
+    GET_BLOCK_STATS_VALUE("wr_bytes", bi.wr_bytes);
+    GET_BLOCK_STATS_VALUE("rd_total_times", rd_total_times);
+    GET_BLOCK_STATS_VALUE("wr_total_times", wr_total_times);
+    GET_BLOCK_STATS_VALUE("flush_operations", fl_req);
+    GET_BLOCK_STATS_VALUE("flush_total_times", fl_total_times);
   }
 
   return 0;
 }
 
-#undef GET_BLOCK_INFO_VALUE
+#undef GET_BLOCK_STATS_VALUE
 
 #endif /* HAVE_BLOCK_STATS_FLAGS */
 
@@ -479,61 +729,99 @@ static int get_block_info(struct lv_block_info *binfo,
     virErrorPtr err;                                                           \
     err = (conn) ? virConnGetLastError((conn)) : virGetLastError();            \
     if (err)                                                                   \
-      ERROR("%s: %s", (s), err->message);                                      \
+      ERROR(PLUGIN_NAME " plugin: %s failed: %s", (s), err->message);          \
   } while (0)
 
-static void init_lv_info(struct lv_info *info) {
-  if (info != NULL)
-    memset(info, 0, sizeof(*info));
-}
+static 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;
 
-static int lv_domain_info(virDomainPtr dom, struct lv_info *info) {
-#ifdef HAVE_CPU_STATS
-  virTypedParameterPtr param = NULL;
-  int nparams = 0;
-#endif /* HAVE_CPU_STATS */
-  int ret = virDomainGetInfo(dom, &(info->di));
-  if (ret != 0) {
-    return ret;
+  const char *namespace = NULL;
+  if (hm_ns == NULL) {
+    namespace = "http://openstack.org/xmlns/libvirt/nova/1.0";
+  } else {
+    namespace = hm_ns;
   }
 
-#ifdef HAVE_CPU_STATS
-  nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0);
-  if (nparams < 0) {
-    VIRT_ERROR(conn, "getting the CPU params count");
-    return -1;
+  char *metadata_str = virDomainGetMetadata(
+      dom, VIR_DOMAIN_METADATA_ELEMENT, namespace, VIR_DOMAIN_AFFECT_CURRENT);
+  if (metadata_str == NULL) {
+    return NULL;
   }
 
-  param = calloc(nparams, sizeof(virTypedParameter));
-  if (param == NULL) {
-    ERROR("virt plugin: alloc(%i) for cpu parameters failed.", nparams);
-    return -1;
+  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;
   }
 
-  ret = virDomainGetCPUStats(dom, param, nparams, -1, 1, 0); // total stats.
-  if (ret < 0) {
-    virTypedParamsClear(param, nparams);
-    sfree(param);
-    VIRT_ERROR(conn, "getting the disk params values");
-    return -1;
+  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;
   }
 
-  for (int i = 0; i < nparams; ++i) {
-    if (!strcmp(param[i].field, "user_time"))
-      info->total_user_cpu_time = param[i].value.ul;
-    else if (!strcmp(param[i].field, "system_time"))
-      info->total_syst_cpu_time = param[i].value.ul;
+  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;
   }
 
-  virTypedParamsClear(param, nparams);
-  sfree(param);
-#endif /* HAVE_CPU_STATS */
+  // 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;
+  }
 
-  return 0;
+  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];
 
@@ -546,44 +834,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:
@@ -591,17 +874,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,
@@ -610,7 +896,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;
   }
 
@@ -654,13 +940,14 @@ static void memory_submit(virDomainPtr dom, gauge_t value) {
 
 static void memory_stats_submit(gauge_t value, virDomainPtr dom,
                                 int tag_index) {
-  static const char *tags[] = {"swap_in",        "swap_out", "major_fault",
-                               "minor_fault",    "unused",   "available",
-                               "actual_balloon", "rss",      "usable",
-                               "last_update"};
+  static const char *tags[] = {"swap_in",        "swap_out",   "major_fault",
+                               "minor_fault",    "unused",     "available",
+                               "actual_balloon", "rss",        "usable",
+                               "last_update",    "disk_caches"};
 
   if ((tag_index < 0) || (tag_index >= (int)STATIC_ARRAY_SIZE(tags))) {
-    ERROR("virt plugin: Array index out of bounds: tag_index = %d", tag_index);
+    ERROR(PLUGIN_NAME " plugin: Array index out of bounds: tag_index = %d",
+          tag_index);
     return;
   }
 
@@ -676,14 +963,6 @@ static void submit_derive2(const char *type, derive_t v0, derive_t v1,
   submit(dom, type, devname, values, STATIC_ARRAY_SIZE(values));
 } /* void submit_derive2 */
 
-static void pcpu_submit(virDomainPtr dom, struct lv_info *info) {
-#ifdef HAVE_CPU_STATS
-  if (extra_stats & ex_stats_pcpu)
-    submit_derive2("ps_cputime", info->total_user_cpu_time,
-                   info->total_syst_cpu_time, dom, NULL);
-#endif /* HAVE_CPU_STATS */
-}
-
 static double cpu_ns_to_percent(unsigned int node_cpus,
                                 unsigned long long cpu_time_old,
                                 unsigned long long cpu_time_new) {
@@ -697,8 +976,8 @@ 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=%" PRIu64
-                    " cpu_time_new=%" PRIu64 "cpu_time_diff=%" PRIu64
+  DEBUG(PLUGIN_NAME " plugin: node_cpus=%u cpu_time_old=%" PRIu64
+                    " cpu_time_new=%" PRIu64 " cpu_time_diff=%" PRIu64
                     " time_diff_sec=%f percent=%f",
         node_cpus, (uint64_t)cpu_time_old, (uint64_t)cpu_time_new,
         (uint64_t)cpu_time_diff, time_diff_sec, percent);
@@ -734,8 +1013,9 @@ static void vcpu_submit(derive_t value, virDomainPtr dom, int vcpu_nr,
   submit(dom, type, type_instance, &(value_t){.derive = value}, 1);
 }
 
-static void disk_submit(struct lv_block_info *binfo, virDomainPtr dom,
-                        const char *dev) {
+static void disk_block_stats_submit(struct lv_block_stats *bstats,
+                                    virDomainPtr dom, const char *dev,
+                                    virDomainBlockInfoPtr binfo) {
   char *dev_copy = strdup(dev);
   const char *type_instance = dev_copy;
 
@@ -754,34 +1034,58 @@ static void disk_submit(struct lv_block_info *binfo, virDomainPtr dom,
   snprintf(flush_type_instance, sizeof(flush_type_instance), "flush-%s",
            type_instance);
 
-  if ((binfo->bi.rd_req != -1) && (binfo->bi.wr_req != -1))
-    submit_derive2("disk_ops", (derive_t)binfo->bi.rd_req,
-                   (derive_t)binfo->bi.wr_req, dom, type_instance);
+  if ((bstats->bi.rd_req != -1) && (bstats->bi.wr_req != -1))
+    submit_derive2("disk_ops", (derive_t)bstats->bi.rd_req,
+                   (derive_t)bstats->bi.wr_req, dom, type_instance);
 
-  if ((binfo->bi.rd_bytes != -1) && (binfo->bi.wr_bytes != -1))
-    submit_derive2("disk_octets", (derive_t)binfo->bi.rd_bytes,
-                   (derive_t)binfo->bi.wr_bytes, dom, type_instance);
+  if ((bstats->bi.rd_bytes != -1) && (bstats->bi.wr_bytes != -1))
+    submit_derive2("disk_octets", (derive_t)bstats->bi.rd_bytes,
+                   (derive_t)bstats->bi.wr_bytes, dom, type_instance);
 
   if (extra_stats & ex_stats_disk) {
-    if ((binfo->rd_total_times != -1) && (binfo->wr_total_times != -1))
-      submit_derive2("disk_time", (derive_t)binfo->rd_total_times,
-                     (derive_t)binfo->wr_total_times, dom, type_instance);
+    if ((bstats->rd_total_times != -1) && (bstats->wr_total_times != -1))
+      submit_derive2("disk_time", (derive_t)bstats->rd_total_times,
+                     (derive_t)bstats->wr_total_times, dom, type_instance);
 
-    if (binfo->fl_req != -1)
+    if (bstats->fl_req != -1)
       submit(dom, "total_requests", flush_type_instance,
-             &(value_t){.derive = (derive_t)binfo->fl_req}, 1);
-    if (binfo->fl_total_times != -1) {
-      derive_t value = binfo->fl_total_times / 1000; // ns -> ms
+             &(value_t){.derive = (derive_t)bstats->fl_req}, 1);
+    if (bstats->fl_total_times != -1) {
+      derive_t value = bstats->fl_total_times / 1000; // ns -> ms
       submit(dom, "total_time_in_ms", flush_type_instance,
              &(value_t){.derive = value}, 1);
     }
   }
 
+  /* disk_allocation, disk_capacity and disk_physical are stored only
+   * if corresponding extrastats are set in collectd configuration file */
+  if ((extra_stats & ex_stats_disk_allocation) && binfo->allocation != -1)
+    submit(dom, "disk_allocation", type_instance,
+           &(value_t){.gauge = (gauge_t)binfo->allocation}, 1);
+
+  if ((extra_stats & ex_stats_disk_capacity) && binfo->capacity != -1)
+    submit(dom, "disk_capacity", type_instance,
+           &(value_t){.gauge = (gauge_t)binfo->capacity}, 1);
+
+  if ((extra_stats & ex_stats_disk_physical) && binfo->physical != -1)
+    submit(dom, "disk_physical", type_instance,
+           &(value_t){.gauge = (gauge_t)binfo->physical}, 1);
+
   sfree(dev_copy);
 }
 
-static unsigned int parse_ex_stats_flags(char **exstats, int numexstats) {
+/**
+ * Function for parsing ExtraStats configuration options.
+ * Result of parsing is stored under 'out_parsed_flags' pointer.
+ *
+ * Returns 0 in case of success and 1 in case of parsing error
+ */
+static int parse_ex_stats_flags(unsigned int *out_parsed_flags, char **exstats,
+                                int numexstats) {
   unsigned int ex_stats_flags = ex_stats_none;
+
+  assert(out_parsed_flags != NULL);
+
   for (int i = 0; i < numexstats; i++) {
     for (int j = 0; ex_stats_table[j].name != NULL; j++) {
       if (strcasecmp(exstats[i], ex_stats_table[j].name) == 0) {
@@ -792,25 +1096,29 @@ 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 1;
       }
     }
   }
-  return ex_stats_flags;
-}
 
-static void domain_state_submit(virDomainPtr dom, int state, int reason) {
+  *out_parsed_flags = ex_stats_flags;
+  return 0;
+}
 
-  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;
   }
 
@@ -819,8 +1127,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
@@ -849,16 +1157,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)
@@ -866,197 +1171,261 @@ static int lv_config(const char *key, const char *value) {
   if (il_interface_devices == NULL)
     il_interface_devices = ignorelist_create(1);
 
-  if (strcasecmp(key, "Connection") == 0) {
-    char *tmp = strdup(value);
-    if (tmp == NULL) {
-      ERROR(PLUGIN_NAME " plugin: Connection strdup failed.");
-      return 1;
-    }
-    sfree(conn_string);
-    conn_string = tmp;
-    return 0;
-  }
+  if (!il_domains || !il_block_devices || !il_interface_devices)
+    return 1;
 
-  if (strcasecmp(key, "RefreshInterval") == 0) {
-    char *eptr = NULL;
-    interval = strtol(value, &eptr, 10);
-    if (eptr == NULL || *eptr != '\0')
-      return 1;
-    return 0;
-  }
+  return 0;
+}
 
-  if (strcasecmp(key, "Domain") == 0) {
-    if (ignorelist_add(il_domains, value))
-      return 1;
-    return 0;
+/* Validates config option that may take multiple strings arguments.
+ * Returns 0 on success, -1 otherwise */
+static int check_config_multiple_string_entry(const oconfig_item_t *ci) {
+  if (ci == NULL) {
+    ERROR(PLUGIN_NAME " plugin: ci oconfig_item can't be NULL");
+    return -1;
   }
-  if (strcasecmp(key, "BlockDevice") == 0) {
-    if (ignorelist_add(il_block_devices, value))
-      return 1;
-    return 0;
+
+  if (ci->values_num < 1) {
+    ERROR(PLUGIN_NAME
+          " plugin: the '%s' option requires at least one string argument",
+          ci->key);
+    return -1;
   }
 
-  if (strcasecmp(key, "BlockDeviceFormat") == 0) {
-    if (strcasecmp(value, "target") == 0)
-      blockdevice_format = target;
-    else if (strcasecmp(value, "source") == 0)
-      blockdevice_format = source;
-    else {
-      ERROR(PLUGIN_NAME " plugin: unknown BlockDeviceFormat: %s", value);
+  for (int i = 0; i < ci->values_num; ++i) {
+    if (ci->values[i].type != OCONFIG_TYPE_STRING) {
+      ERROR(PLUGIN_NAME
+            " plugin: one of the '%s' options is not a valid string",
+            ci->key);
       return -1;
     }
-    return 0;
-  }
-  if (strcasecmp(key, "BlockDeviceFormatBasename") == 0) {
-    blockdevice_format_basename = IS_TRUE(value);
-    return 0;
-  }
-  if (strcasecmp(key, "InterfaceDevice") == 0) {
-    if (ignorelist_add(il_interface_devices, value))
-      return 1;
-    return 0;
   }
 
-  if (strcasecmp(key, "IgnoreSelected") == 0) {
-    if (IS_TRUE(value)) {
-      ignorelist_set_invert(il_domains, 0);
-      ignorelist_set_invert(il_block_devices, 0);
-      ignorelist_set_invert(il_interface_devices, 0);
-    } else {
-      ignorelist_set_invert(il_domains, 1);
-      ignorelist_set_invert(il_block_devices, 1);
-      ignorelist_set_invert(il_interface_devices, 1);
-    }
-    return 0;
-  }
+  return 0;
+}
 
-  if (strcasecmp(key, "HostnameFormat") == 0) {
-    char *value_copy;
-    char *fields[HF_MAX_FIELDS];
-    int n;
+static int lv_config(oconfig_item_t *ci) {
+  if (lv_init_ignorelists() != 0) {
+    ERROR(PLUGIN_NAME " plugin: lv_init_ignorelist failed.");
+    return -1;
+  }
 
-    value_copy = strdup(value);
-    if (value_copy == NULL) {
-      ERROR(PLUGIN_NAME " plugin: strdup failed.");
-      return -1;
-    }
+  for (int i = 0; i < ci->children_num; ++i) {
+    oconfig_item_t *c = ci->children + i;
 
-    n = strsplit(value_copy, fields, HF_MAX_FIELDS);
-    if (n < 1) {
-      sfree(value_copy);
-      ERROR(PLUGIN_NAME " plugin: HostnameFormat: no fields");
-      return -1;
-    }
+    if (strcasecmp(c->key, "Connection") == 0) {
+      if (cf_util_get_string(c, &conn_string) != 0 || conn_string == NULL)
+        return -1;
 
-    for (int i = 0; i < n; ++i) {
-      if (strcasecmp(fields[i], "hostname") == 0)
-        hostname_format[i] = hf_hostname;
-      else if (strcasecmp(fields[i], "name") == 0)
-        hostname_format[i] = hf_name;
-      else if (strcasecmp(fields[i], "uuid") == 0)
-        hostname_format[i] = hf_uuid;
-      else {
-        ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s",
-              fields[i]);
-        sfree(value_copy);
+      continue;
+    } else if (strcasecmp(c->key, "RefreshInterval") == 0) {
+      if (cf_util_get_int(c, &interval) != 0)
         return -1;
-      }
-    }
-    sfree(value_copy);
 
-    for (int i = n; i < HF_MAX_FIELDS; ++i)
-      hostname_format[i] = hf_none;
+      continue;
+    } else if (strcasecmp(c->key, "Domain") == 0) {
+      char *domain_name = NULL;
+      if (cf_util_get_string(c, &domain_name) != 0)
+        return -1;
 
-    return 0;
-  }
+      if (ignorelist_add(il_domains, domain_name)) {
+        ERROR(PLUGIN_NAME " plugin: Adding '%s' to domain-ignorelist failed",
+              domain_name);
+        sfree(domain_name);
+        return -1;
+      }
 
-  if (strcasecmp(key, "PluginInstanceFormat") == 0) {
-    char *value_copy;
-    char *fields[PLGINST_MAX_FIELDS];
-    int n;
+      sfree(domain_name);
+      continue;
+    } else if (strcasecmp(c->key, "BlockDevice") == 0) {
+      char *device_name = NULL;
+      if (cf_util_get_string(c, &device_name) != 0)
+        return -1;
 
-    value_copy = strdup(value);
-    if (value_copy == NULL) {
-      ERROR(PLUGIN_NAME " plugin: strdup failed.");
-      return -1;
-    }
+      if (ignorelist_add(il_block_devices, device_name) != 0) {
+        ERROR(PLUGIN_NAME
+              " plugin: Adding '%s' to block-device-ignorelist failed",
+              device_name);
+        sfree(device_name);
+        return -1;
+      }
 
-    n = strsplit(value_copy, fields, PLGINST_MAX_FIELDS);
-    if (n < 1) {
-      sfree(value_copy);
-      ERROR(PLUGIN_NAME " plugin: PluginInstanceFormat: no fields");
-      return -1;
-    }
+      sfree(device_name);
+      continue;
+    } else if (strcasecmp(c->key, "BlockDeviceFormat") == 0) {
+      char *device_format = NULL;
+      if (cf_util_get_string(c, &device_format) != 0)
+        return -1;
 
-    for (int i = 0; i < n; ++i) {
-      if (strcasecmp(fields[i], "none") == 0) {
-        plugin_instance_format[i] = plginst_none;
-        break;
-      } else if (strcasecmp(fields[i], "name") == 0)
-        plugin_instance_format[i] = plginst_name;
-      else if (strcasecmp(fields[i], "uuid") == 0)
-        plugin_instance_format[i] = plginst_uuid;
+      if (strcasecmp(device_format, "target") == 0)
+        blockdevice_format = target;
+      else if (strcasecmp(device_format, "source") == 0)
+        blockdevice_format = source;
       else {
-        ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s",
-              fields[i]);
-        sfree(value_copy);
+        ERROR(PLUGIN_NAME " plugin: unknown BlockDeviceFormat: %s",
+              device_format);
+        sfree(device_format);
         return -1;
       }
-    }
-    sfree(value_copy);
 
-    for (int i = n; i < PLGINST_MAX_FIELDS; ++i)
-      plugin_instance_format[i] = plginst_none;
+      sfree(device_format);
+      continue;
+    } else if (strcasecmp(c->key, "BlockDeviceFormatBasename") == 0) {
+      if (cf_util_get_boolean(c, &blockdevice_format_basename) != 0)
+        return -1;
 
-    return 0;
-  }
+      continue;
+    } else if (strcasecmp(c->key, "InterfaceDevice") == 0) {
+      char *interface_name = NULL;
+      if (cf_util_get_string(c, &interface_name) != 0)
+        return -1;
 
-  if (strcasecmp(key, "InterfaceFormat") == 0) {
-    if (strcasecmp(value, "name") == 0)
-      interface_format = if_name;
-    else if (strcasecmp(value, "address") == 0)
-      interface_format = if_address;
-    else if (strcasecmp(value, "number") == 0)
-      interface_format = if_number;
-    else {
-      ERROR(PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", value);
-      return -1;
-    }
-    return 0;
-  }
+      if (ignorelist_add(il_interface_devices, interface_name)) {
+        ERROR(PLUGIN_NAME " plugin: Adding '%s' to interface-ignorelist failed",
+              interface_name);
+        sfree(interface_name);
+        return -1;
+      }
 
-  if (strcasecmp(key, "Instances") == 0) {
-    char *eptr = NULL;
-    double val = strtod(value, &eptr);
+      sfree(interface_name);
+      continue;
+    } else if (strcasecmp(c->key, "IgnoreSelected") == 0) {
+      bool ignore_selected = false;
+      if (cf_util_get_boolean(c, &ignore_selected) != 0)
+        return -1;
 
-    if (*eptr != '\0') {
-      ERROR(PLUGIN_NAME " plugin: Invalid value for Instances = '%s'", value);
-      return 1;
-    }
-    if (val <= 0) {
-      ERROR(PLUGIN_NAME " plugin: Instances <= 0 makes no sense.");
-      return 1;
-    }
-    if (val > NR_INSTANCES_MAX) {
-      ERROR(PLUGIN_NAME " plugin: Instances=%f > NR_INSTANCES_MAX=%i"
-                        " use a lower setting or recompile the plugin.",
-            val, NR_INSTANCES_MAX);
-      return 1;
-    }
+      if (ignore_selected) {
+        ignorelist_set_invert(il_domains, 0);
+        ignorelist_set_invert(il_block_devices, 0);
+        ignorelist_set_invert(il_interface_devices, 0);
+      } else {
+        ignorelist_set_invert(il_domains, 1);
+        ignorelist_set_invert(il_block_devices, 1);
+        ignorelist_set_invert(il_interface_devices, 1);
+      }
 
-    nr_instances = (int)val;
-    DEBUG(PLUGIN_NAME " plugin: configured %i instances", nr_instances);
-    return 0;
-  }
+      continue;
+    } else if (strcasecmp(c->key, "HostnameMetadataNS") == 0) {
+      if (cf_util_get_string(c, &hm_ns) != 0)
+        return -1;
+
+      continue;
+    } else if (strcasecmp(c->key, "HostnameMetadataXPath") == 0) {
+      if (cf_util_get_string(c, &hm_xpath) != 0)
+        return -1;
+
+      continue;
+    } else if (strcasecmp(c->key, "HostnameFormat") == 0) {
+      /* this option can take multiple strings arguments in one config line*/
+      if (check_config_multiple_string_entry(c) != 0) {
+        ERROR(PLUGIN_NAME " plugin: Could not get 'HostnameFormat' parameter");
+        return -1;
+      }
+
+      const int params_num = c->values_num;
+      for (int i = 0; i < params_num; ++i) {
+        const char *param_name = c->values[i].value.string;
+        if (strcasecmp(param_name, "hostname") == 0)
+          hostname_format[i] = hf_hostname;
+        else if (strcasecmp(param_name, "name") == 0)
+          hostname_format[i] = hf_name;
+        else if (strcasecmp(param_name, "uuid") == 0)
+          hostname_format[i] = hf_uuid;
+        else if (strcasecmp(param_name, "metadata") == 0)
+          hostname_format[i] = hf_metadata;
+        else {
+          ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s",
+                param_name);
+          return -1;
+        }
+      }
+
+      for (int i = params_num; i < HF_MAX_FIELDS; ++i)
+        hostname_format[i] = hf_none;
+
+      continue;
+    } else if (strcasecmp(c->key, "PluginInstanceFormat") == 0) {
+      /* this option can handle list of string parameters in one line*/
+      if (check_config_multiple_string_entry(c) != 0) {
+        ERROR(PLUGIN_NAME
+              " plugin: Could not get 'PluginInstanceFormat' parameter");
+        return -1;
+      }
+
+      const int params_num = c->values_num;
+      for (int i = 0; i < params_num; ++i) {
+        const char *param_name = c->values[i].value.string;
+        if (strcasecmp(param_name, "none") == 0) {
+          plugin_instance_format[i] = plginst_none;
+          break;
+        } else if (strcasecmp(param_name, "name") == 0)
+          plugin_instance_format[i] = plginst_name;
+        else if (strcasecmp(param_name, "uuid") == 0)
+          plugin_instance_format[i] = plginst_uuid;
+        else if (strcasecmp(param_name, "metadata") == 0)
+          plugin_instance_format[i] = plginst_metadata;
+        else {
+          ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s",
+                param_name);
+
+          return -1;
+        }
+      }
+
+      for (int i = params_num; i < PLGINST_MAX_FIELDS; ++i)
+        plugin_instance_format[i] = plginst_none;
+
+      continue;
+    } else if (strcasecmp(c->key, "InterfaceFormat") == 0) {
+      char *format = NULL;
+      if (cf_util_get_string(c, &format) != 0)
+        return -1;
+
+      if (strcasecmp(format, "name") == 0)
+        interface_format = if_name;
+      else if (strcasecmp(format, "address") == 0)
+        interface_format = if_address;
+      else if (strcasecmp(format, "number") == 0)
+        interface_format = if_number;
+      else {
+        ERROR(PLUGIN_NAME " plugin: unknown InterfaceFormat: %s", format);
+        sfree(format);
+        return -1;
+      }
+
+      sfree(format);
+      continue;
+    } else if (strcasecmp(c->key, "Instances") == 0) {
+      if (cf_util_get_int(c, &nr_instances) != 0)
+        return -1;
+
+      if (nr_instances <= 0) {
+        ERROR(PLUGIN_NAME " plugin: Instances <= 0 makes no sense.");
+        return -1;
+      }
+      if (nr_instances > NR_INSTANCES_MAX) {
+        ERROR(PLUGIN_NAME " plugin: Instances=%i > NR_INSTANCES_MAX=%i"
+                          " use a lower setting or recompile the plugin.",
+              nr_instances, NR_INSTANCES_MAX);
+        return -1;
+      }
+
+      DEBUG(PLUGIN_NAME " plugin: configured %i instances", nr_instances);
+      continue;
+    } else if (strcasecmp(c->key, "ExtraStats") == 0) {
+      char *ex_str = NULL;
+
+      if (cf_util_get_string(c, &ex_str) != 0)
+        return -1;
 
-  if (strcasecmp(key, "ExtraStats") == 0) {
-    char *localvalue = strdup(value);
-    if (localvalue != NULL) {
       char *exstats[EX_STATS_MAX_FIELDS];
-      int numexstats =
-          strsplit(localvalue, exstats, STATIC_ARRAY_SIZE(exstats));
-      extra_stats = parse_ex_stats_flags(exstats, numexstats);
-      sfree(localvalue);
+      int numexstats = strsplit(ex_str, exstats, STATIC_ARRAY_SIZE(exstats));
+      int status = parse_ex_stats_flags(&extra_stats, exstats, numexstats);
+      sfree(ex_str);
+      if (status != 0) {
+        ERROR(PLUGIN_NAME " plugin: parsing 'ExtraStats' option failed");
+        return status;
+      }
 
 #ifdef HAVE_JOB_STATS
       if ((extra_stats & ex_stats_job_stats_completed) &&
@@ -1064,18 +1433,44 @@ static int lv_config(const char *key, const char *value) {
         ERROR(PLUGIN_NAME " plugin: Invalid job stats configuration. Only one "
                           "type of job statistics can be collected at the same "
                           "time");
-        return 1;
+        return -1;
       }
 #endif
+
+      /* ExtraStats parsed successfully */
+      continue;
+    } else if (strcasecmp(c->key, "PersistentNotification") == 0) {
+      if (cf_util_get_boolean(c, &persistent_notification) != 0)
+        return -1;
+
+      continue;
+    } else if (strcasecmp(c->key, "ReportBlockDevices") == 0) {
+      if (cf_util_get_boolean(c, &report_block_devices) != 0)
+        return -1;
+
+      continue;
+    } else if (strcasecmp(c->key, "ReportNetworkInterfaces") == 0) {
+      if (cf_util_get_boolean(c, &report_network_interfaces) != 0)
+        return -1;
+
+      continue;
+    } else {
+      /* Unrecognised option. */
+      ERROR(PLUGIN_NAME " plugin: Unrecognized option: '%s'", c->key);
+      return -1;
     }
   }
 
-  /* Unrecognised option. */
-  return -1;
+  return 0;
 }
 
 static int lv_connect(void) {
   if (conn == NULL) {
+    /* event implementation must be registered before connection is opened */
+    if (!persistent_notification)
+      if (register_event_impl() != 0)
+        return -1;
+
 /* `conn_string == NULL' is acceptable */
 #ifdef HAVE_FS_INFO
     /* virDomainGetFSInfo requires full read-write access connection */
@@ -1092,9 +1487,18 @@ static int lv_connect(void) {
     }
     int status = virNodeGetInfo(conn, &nodeinfo);
     if (status != 0) {
-      ERROR(PLUGIN_NAME ": virNodeGetInfo failed");
+      ERROR(PLUGIN_NAME " plugin: virNodeGetInfo failed");
+      virConnectClose(conn);
+      conn = NULL;
       return -1;
     }
+
+    if (!persistent_notification)
+      if (start_event_loop(&notif_thread) != 0) {
+        virConnectClose(conn);
+        conn = NULL;
+        return -1;
+      }
   }
   c_release(LOG_NOTICE, &conn_complain,
             PLUGIN_NAME " plugin: Connection established.");
@@ -1108,8 +1512,8 @@ static void lv_disconnect(void) {
   WARNING(PLUGIN_NAME " plugin: closed connection to libvirt");
 }
 
-static int lv_domain_block_info(virDomainPtr dom, const char *path,
-                                struct lv_block_info *binfo) {
+static int lv_domain_block_stats(virDomainPtr dom, const char *path,
+                                 struct lv_block_stats *bstats) {
 #ifdef HAVE_BLOCK_STATS_FLAGS
   int nparams = 0;
   if (virDomainBlockStatsFlags(dom, path, NULL, &nparams, 0) < 0 ||
@@ -1118,10 +1522,10 @@ static int lv_domain_block_info(virDomainPtr dom, const char *path,
     return -1;
   }
 
-  virTypedParameterPtr params = calloc((size_t)nparams, sizeof(*params));
+  virTypedParameterPtr params = calloc(nparams, sizeof(*params));
   if (params == NULL) {
-    ERROR("virt plugin: alloc(%i) for block=%s parameters failed.", nparams,
-          path);
+    ERROR(PLUGIN_NAME " plugin: alloc(%i) for block=%s parameters failed.",
+          nparams, path);
     return -1;
   }
 
@@ -1129,14 +1533,14 @@ static int lv_domain_block_info(virDomainPtr dom, const char *path,
   if (virDomainBlockStatsFlags(dom, path, params, &nparams, 0) < 0) {
     VIRT_ERROR(conn, "getting the disk params values");
   } else {
-    rc = get_block_info(binfo, params, nparams);
+    rc = get_block_stats(bstats, params, nparams);
   }
 
   virTypedParamsClear(params, nparams);
   sfree(params);
   return rc;
 #else
-  return virDomainBlockStats(dom, path, &(binfo->bi), sizeof(binfo->bi));
+  return virDomainBlockStats(dom, path, &(bstats->bi), sizeof(bstats->bi));
 #endif /* HAVE_BLOCK_STATS_FLAGS */
 }
 
@@ -1161,8 +1565,17 @@ static int get_perf_events(virDomainPtr domain) {
   int status =
       virDomainListGetStats(domain_array, VIR_DOMAIN_STATS_PERF, &stats, 0);
   if (status == -1) {
-    ERROR("virt plugin: virDomainListGetStats failed with status %i.", status);
-    return status;
+    ERROR(PLUGIN_NAME " plugin: virDomainListGetStats failed with status %i.",
+          status);
+
+    virErrorPtr err = virGetLastError();
+    if (err->code == VIR_ERR_NO_SUPPORT) {
+      ERROR(PLUGIN_NAME
+            " plugin: Disabled unsupported ExtraStats selector: perf");
+      extra_stats &= ~(ex_stats_perf);
+    }
+
+    return -1;
   }
 
   for (int i = 0; i < status; ++i)
@@ -1177,7 +1590,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);
@@ -1186,19 +1599,24 @@ static void vcpu_pin_submit(virDomainPtr dom, int max_cpus, int vcpu,
 
 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: malloc failed.");
+    ERROR(PLUGIN_NAME " plugin: calloc failed.");
     return -1;
   }
 
-  unsigned char *cpumaps = calloc(nr_virt_cpu, cpu_map_len);
-  if (cpumaps == NULL) {
-    ERROR(PLUGIN_NAME " plugin: malloc failed.");
-    sfree(vinfo);
-    return -1;
+  int cpu_map_len = 0;
+  unsigned char *cpumaps = NULL;
+  if (extra_stats & ex_stats_vcpupin) {
+    cpu_map_len = VIR_CPU_MAPLEN(max_cpus);
+    cpumaps = calloc(nr_virt_cpu, cpu_map_len);
+
+    if (cpumaps == NULL) {
+      ERROR(PLUGIN_NAME " plugin: calloc failed.");
+      sfree(vinfo);
+      return -1;
+    }
   }
 
   int status =
@@ -1206,13 +1624,26 @@ static int get_vcpu_stats(virDomainPtr domain, unsigned short nr_virt_cpu) {
   if (status < 0) {
     ERROR(PLUGIN_NAME " plugin: virDomainGetVcpus failed with status %i.",
           status);
+
+    virErrorPtr err = virGetLastError();
+    if (err->code == VIR_ERR_NO_SUPPORT) {
+      if (extra_stats & ex_stats_vcpu)
+        ERROR(PLUGIN_NAME
+              " plugin: Disabled unsupported ExtraStats selector: vcpu");
+      if (extra_stats & ex_stats_vcpupin)
+        ERROR(PLUGIN_NAME
+              " plugin: Disabled unsupported ExtraStats selector: vcpupin");
+      extra_stats &= ~(ex_stats_vcpu | ex_stats_vcpupin);
+    }
+
     sfree(cpumaps);
     sfree(vinfo);
-    return status;
+    return -1;
   }
 
   for (int i = 0; i < nr_virt_cpu; ++i) {
-    vcpu_submit(vinfo[i].cpuTime, domain, vinfo[i].number, "virt_vcpu");
+    if (extra_stats & ex_stats_vcpu)
+      vcpu_submit(vinfo[i].cpuTime, domain, vinfo[i].number, "virt_vcpu");
     if (extra_stats & ex_stats_vcpupin)
       vcpu_pin_submit(domain, max_cpus, i, cpumaps, cpu_map_len);
   }
@@ -1222,8 +1653,80 @@ static int get_vcpu_stats(virDomainPtr domain, unsigned short nr_virt_cpu) {
   return 0;
 }
 
+#ifdef HAVE_CPU_STATS
+static int get_pcpu_stats(virDomainPtr dom) {
+  int nparams = virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0);
+  if (nparams < 0) {
+    VIRT_ERROR(conn, "getting the CPU params count");
+
+    virErrorPtr err = virGetLastError();
+    if (err->code == VIR_ERR_NO_SUPPORT) {
+      ERROR(PLUGIN_NAME
+            " plugin: Disabled unsupported ExtraStats selector: pcpu");
+      extra_stats &= ~(ex_stats_pcpu);
+    }
+
+    return -1;
+  }
+
+  virTypedParameterPtr param = calloc(nparams, sizeof(*param));
+  if (param == NULL) {
+    ERROR(PLUGIN_NAME " plugin: alloc(%i) for cpu parameters failed.", nparams);
+    return -1;
+  }
+
+  int ret = virDomainGetCPUStats(dom, param, nparams, -1, 1, 0); // total stats.
+  if (ret < 0) {
+    virTypedParamsClear(param, nparams);
+    sfree(param);
+    VIRT_ERROR(conn, "getting the CPU params values");
+    return -1;
+  }
+
+  unsigned long long total_user_cpu_time = 0;
+  unsigned long long total_syst_cpu_time = 0;
+
+  for (int i = 0; i < nparams; ++i) {
+    if (!strcmp(param[i].field, "user_time"))
+      total_user_cpu_time = param[i].value.ul;
+    else if (!strcmp(param[i].field, "system_time"))
+      total_syst_cpu_time = param[i].value.ul;
+  }
+
+  if (total_user_cpu_time > 0 || total_syst_cpu_time > 0)
+    submit_derive2("ps_cputime", total_user_cpu_time, total_syst_cpu_time, dom,
+                   NULL);
+
+  virTypedParamsClear(param, nparams);
+  sfree(param);
+
+  return 0;
+}
+#endif /* HAVE_CPU_STATS */
+
 #ifdef HAVE_DOM_REASON
-static int get_domain_state(virDomainPtr domain) {
+static int submit_domain_state(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;
+  }
+
+  value_t values[] = {
+      {.gauge = (gauge_t)domain_state}, {.gauge = (gauge_t)domain_reason},
+  };
+
+  submit(domain, "domain_state", NULL, values, STATIC_ARRAY_SIZE(values));
+
+  return 0;
+}
+
+#ifdef HAVE_LIST_ALL_DOMAINS
+static int get_domain_state_notify(virDomainPtr domain) {
   int domain_state = 0;
   int domain_reason = 0;
 
@@ -1234,30 +1737,74 @@ static int get_domain_state(virDomainPtr domain) {
     return status;
   }
 
-  domain_state_submit(domain, domain_state, domain_reason);
+  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(PLUGIN_NAME " plugin: calloc failed.");
     return -1;
   }
 
   int mem_stats =
       virDomainMemoryStats(domain, minfo, VIR_DOMAIN_MEMORY_STAT_NR, 0);
   if (mem_stats < 0) {
-    ERROR("virt plugin: virDomainMemoryStats failed with mem_stats %i.",
+    ERROR(PLUGIN_NAME " plugin: virDomainMemoryStats failed with mem_stats %i.",
           mem_stats);
     sfree(minfo);
-    return mem_stats;
+
+    virErrorPtr err = virGetLastError();
+    if (err->code == VIR_ERR_NO_SUPPORT) {
+      ERROR(PLUGIN_NAME
+            " plugin: Disabled unsupported ExtraStats selector: memory");
+      extra_stats &= ~(ex_stats_memory);
+    }
+
+    return -1;
   }
 
-  for (int i = 0; i < mem_stats; i++)
-    memory_stats_submit((gauge_t)minfo[i].val * 1024, domain, minfo[i].tag);
+  derive_t swap_in = -1;
+  derive_t swap_out = -1;
+  derive_t min_flt = -1;
+  derive_t maj_flt = -1;
+
+  for (int i = 0; i < mem_stats; i++) {
+    if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_IN)
+      swap_in = minfo[i].val;
+    else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_OUT)
+      swap_out = minfo[i].val;
+    else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT)
+      min_flt = minfo[i].val;
+    else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT)
+      maj_flt = minfo[i].val;
+#ifdef LIBVIR_CHECK_VERSION
+#if LIBVIR_CHECK_VERSION(2, 1, 0)
+    else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_LAST_UPDATE)
+      /* Skip 'last_update' reporting as that is not memory but timestamp */
+      continue;
+#endif
+#endif
+    else
+      memory_stats_submit((gauge_t)minfo[i].val * 1024, domain, minfo[i].tag);
+  }
+
+  if (swap_in > 0 || swap_out > 0) {
+    submit(domain, "swap_io", "in", &(value_t){.gauge = swap_in}, 1);
+    submit(domain, "swap_io", "out", &(value_t){.gauge = swap_out}, 1);
+  }
+
+  if (min_flt > 0 || maj_flt > 0) {
+    value_t values[] = {
+        {.gauge = (gauge_t)min_flt}, {.gauge = (gauge_t)maj_flt},
+    };
+    submit(domain, "ps_pagefaults", NULL, values, STATIC_ARRAY_SIZE(values));
+  }
 
   sfree(minfo);
   return 0;
@@ -1276,6 +1823,15 @@ static int get_disk_err(virDomainPtr domain) {
   if (disk_err_count == -1) {
     ERROR(PLUGIN_NAME
           " plugin: failed to get preferred size of disk errors array");
+
+    virErrorPtr err = virGetLastError();
+
+    if (err->code == VIR_ERR_NO_SUPPORT) {
+      ERROR(PLUGIN_NAME
+            " plugin: Disabled unsupported ExtraStats selector: disk_err");
+      extra_stats &= ~(ex_stats_disk_err);
+    }
+
     return -1;
   }
 
@@ -1303,22 +1859,57 @@ static int get_disk_err(virDomainPtr domain) {
 }
 #endif /* HAVE_DISK_ERR */
 
-static int get_block_stats(struct block_device *block_dev) {
-
+static int get_block_device_stats(struct block_device *block_dev) {
   if (!block_dev) {
     ERROR(PLUGIN_NAME " plugin: get_block_stats NULL pointer");
     return -1;
   }
 
-  struct lv_block_info binfo;
+  virDomainBlockInfo binfo;
   init_block_info(&binfo);
 
-  if (lv_domain_block_info(block_dev->dom, block_dev->path, &binfo) < 0) {
-    ERROR(PLUGIN_NAME " plugin: lv_domain_block_info failed");
+  /* Fetching block info stats only if needed*/
+  if (extra_stats & (ex_stats_disk_allocation | ex_stats_disk_capacity |
+                     ex_stats_disk_physical)) {
+    /* Block info statistics can be only fetched from devices with 'source'
+     * defined */
+    if (block_dev->has_source) {
+      if (virDomainGetBlockInfo(block_dev->dom, block_dev->path, &binfo, 0) <
+          0) {
+        ERROR(PLUGIN_NAME " plugin: virDomainGetBlockInfo failed for path: %s",
+              block_dev->path);
+
+        virErrorPtr err = virGetLastError();
+        if (err->code == VIR_ERR_NO_SUPPORT) {
+
+          if (extra_stats & ex_stats_disk_allocation)
+            ERROR(PLUGIN_NAME " plugin: Disabled unsupported ExtraStats "
+                              "selector: disk_allocation");
+          if (extra_stats & ex_stats_disk_capacity)
+            ERROR(PLUGIN_NAME " plugin: Disabled unsupported ExtraStats "
+                              "selector: disk_capacity");
+          if (extra_stats & ex_stats_disk_physical)
+            ERROR(PLUGIN_NAME " plugin: Disabled unsupported ExtraStats "
+                              "selector: disk_physical");
+
+          extra_stats &= ~(ex_stats_disk_allocation | ex_stats_disk_capacity |
+                           ex_stats_disk_physical);
+        }
+
+        return -1;
+      }
+    }
+  }
+
+  struct lv_block_stats bstats;
+  init_block_stats(&bstats);
+
+  if (lv_domain_block_stats(block_dev->dom, block_dev->path, &bstats) < 0) {
+    ERROR(PLUGIN_NAME " plugin: lv_domain_block_stats failed");
     return -1;
   }
 
-  disk_submit(&binfo, block_dev->dom, block_dev->path);
+  disk_block_stats_submit(&bstats, block_dev->dom, block_dev->path, &binfo);
   return 0;
 }
 
@@ -1335,7 +1926,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);                                \
@@ -1360,7 +1951,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];
   }
@@ -1391,7 +1982,15 @@ static int get_fs_info(virDomainPtr domain) {
   if (mount_points_cnt == -1) {
     ERROR(PLUGIN_NAME " plugin: virDomainGetFSInfo failed: %d",
           mount_points_cnt);
-    return mount_points_cnt;
+
+    virErrorPtr err = virGetLastError();
+    if (err->code == VIR_ERR_NO_SUPPORT) {
+      ERROR(PLUGIN_NAME
+            " plugin: Disabled unsupported ExtraStats selector: fs_info");
+      extra_stats &= ~(ex_stats_fs_info);
+    }
+
+    return -1;
   }
 
   for (int i = 0; i < mount_points_cnt; ++i) {
@@ -1438,7 +2037,6 @@ static void job_stats_submit(virDomainPtr domain, virTypedParameterPtr param) {
 }
 
 static int get_job_stats(virDomainPtr domain) {
-  int ret = 0;
   int job_type = 0;
   int nparams = 0;
   virTypedParameterPtr params = NULL;
@@ -1446,10 +2044,24 @@ static int get_job_stats(virDomainPtr domain) {
                   ? VIR_DOMAIN_JOB_STATS_COMPLETED
                   : 0;
 
-  ret = virDomainGetJobStats(domain, &job_type, &params, &nparams, flags);
+  int ret = virDomainGetJobStats(domain, &job_type, &params, &nparams, flags);
   if (ret != 0) {
     ERROR(PLUGIN_NAME " plugin: virDomainGetJobStats failed: %d", ret);
-    return ret;
+
+    virErrorPtr err = virGetLastError();
+    // VIR_ERR_INVALID_ARG returned when VIR_DOMAIN_JOB_STATS_COMPLETED flag is
+    // not supported by driver
+    if (err->code == VIR_ERR_NO_SUPPORT || err->code == VIR_ERR_INVALID_ARG) {
+      if (extra_stats & ex_stats_job_stats_completed)
+        ERROR(PLUGIN_NAME " plugin: Disabled unsupported ExtraStats selector: "
+                          "job_stats_completed");
+      if (extra_stats & ex_stats_job_stats_background)
+        ERROR(PLUGIN_NAME " plugin: Disabled unsupported ExtraStats selector: "
+                          "job_stats_background");
+      extra_stats &=
+          ~(ex_stats_job_stats_completed | ex_stats_job_stats_background);
+    }
+    return -1;
   }
 
   DEBUG(PLUGIN_NAME " plugin: job_type=%d nparams=%d", job_type, nparams);
@@ -1461,20 +2073,18 @@ static int get_job_stats(virDomainPtr domain) {
   }
 
   virTypedParamsFree(params, nparams);
-  return ret;
+  return 0;
 }
 #endif /* HAVE_JOB_STATS */
 
 static int get_domain_metrics(domain_t *domain) {
-  struct lv_info info;
-
   if (!domain || !domain->ptr) {
-    ERROR(PLUGIN_NAME ": get_domain_metrics: NULL pointer");
+    ERROR(PLUGIN_NAME " plugin: get_domain_metrics: NULL pointer");
     return -1;
   }
 
-  init_lv_info(&info);
-  int status = lv_domain_info(domain->ptr, &info);
+  virDomainInfo info;
+  int status = virDomainGetInfo(domain->ptr, &info);
   if (status != 0) {
     ERROR(PLUGIN_NAME " plugin: virDomainGetInfo failed with status %i.",
           status);
@@ -1487,25 +2097,27 @@ static int get_domain_metrics(domain_t *domain) {
      * however it doesn't provide a reason for entering particular state.
      * We need to get it from virDomainGetState.
      */
-    GET_STATS(get_domain_state, "domain reason", domain->ptr);
-#else
-    /* virDomainGetState is not available. Submit 0, which corresponds to
-     * unknown reason. */
-    domain_state_submit(domain->ptr, info.di.state, 0);
+    GET_STATS(submit_domain_state, "domain reason", domain->ptr);
 #endif
   }
 
   /* Gather remaining stats only for running domains */
-  if (info.di.state != VIR_DOMAIN_RUNNING)
+  if (info.state != VIR_DOMAIN_RUNNING)
     return 0;
 
-  pcpu_submit(domain->ptr, &info);
-  cpu_submit(domain, info.di.cpuTime);
+#ifdef HAVE_CPU_STATS
+  if (extra_stats & ex_stats_pcpu)
+    get_pcpu_stats(domain->ptr);
+#endif
+
+  cpu_submit(domain, info.cpuTime);
 
-  memory_submit(domain->ptr, (gauge_t)info.di.memory * 1024);
+  memory_submit(domain->ptr, (gauge_t)info.memory * 1024);
 
-  GET_STATS(get_vcpu_stats, "vcpu stats", domain->ptr, info.di.nrVirtCpu);
-  GET_STATS(get_memory_stats, "memory stats", domain->ptr);
+  if (extra_stats & (ex_stats_vcpu | ex_stats_vcpupin))
+    GET_STATS(get_vcpu_stats, "vcpu stats", domain->ptr, info.nrVirtCpu);
+  if (extra_stats & ex_stats_memory)
+    GET_STATS(get_memory_stats, "memory stats", domain->ptr);
 
 #ifdef HAVE_PERF_STATS
   if (extra_stats & ex_stats_perf)
@@ -1529,7 +2141,8 @@ static int get_domain_metrics(domain_t *domain) {
 #endif
 
   /* Update cached virDomainInfo. It has to be done after cpu_submit */
-  memcpy(&domain->info, &info.di, sizeof(domain->info));
+  memcpy(&domain->info, &info, sizeof(domain->info));
+
   return 0;
 }
 
@@ -1578,76 +2191,324 @@ static int get_if_dev_stats(struct interface_device *if_dev) {
   return 0;
 }
 
-static int lv_read(user_data_t *ud) {
-  time_t t;
-  struct lv_read_instance *inst = NULL;
-  struct lv_read_state *state = NULL;
+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 void virt_eventloop_timeout_cb(int timer ATTRIBUTE_UNUSED,
+                                      void *timer_info) {}
+
+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;
+  }
+
+  if (virEventAddTimeout(CDTIME_T_TO_MS(plugin_get_interval()),
+                         virt_eventloop_timeout_cb, NULL, NULL) < 0) {
+    virErrorPtr err = virGetLastError();
+    ERROR(PLUGIN_NAME " plugin: virEventAddTimeout failed: %s",
+          err && err->message ? err->message : "Unknown error");
+    return -1;
+  }
+
+  return 0;
+}
+
+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) {
+  assert(thread_data != NULL);
+
+  int ret = pthread_mutex_init(&thread_data->active_mutex, NULL);
+  if (ret != 0) {
+    ERROR(PLUGIN_NAME " plugin: Failed to initialize mutex, err %u", ret);
+    return ret;
+  }
+
+  /**
+   * '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;
+  }
+
+  DEBUG(PLUGIN_NAME " plugin: starting event loop");
+
+  virt_notif_thread_set_active(thread_data, 1);
+  if (pthread_create(&thread_data->event_loop_tid, NULL, event_loop_worker,
+                     thread_data)) {
+    ERROR(PLUGIN_NAME " plugin: failed event loop thread creation");
+    virt_notif_thread_set_active(thread_data, 0);
+    virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id);
+    thread_data->domain_event_cb_id = -1;
+    return -1;
+  }
+
+  return 0;
+}
+
+/* stop event loop thread and deregister callback */
+static void stop_event_loop(virt_notif_thread_t *thread_data) {
+
+  DEBUG(PLUGIN_NAME " plugin: stopping event loop");
+
+  /* Stopping loop */
+  if (virt_notif_thread_is_active(thread_data)) {
+    virt_notif_thread_set_active(thread_data, 0);
+    if (pthread_join(notif_thread.event_loop_tid, NULL) != 0)
+      ERROR(PLUGIN_NAME " plugin: stopping notification thread failed");
+  }
+
+  /* ... and de-registering event handler */
+  if (conn != NULL && thread_data->domain_event_cb_id != -1) {
+    virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id);
+    thread_data->domain_event_cb_id = -1;
+  }
+}
+
+static int persistent_domains_state_notification(void) {
+  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) {
   if (ud->data == NULL) {
     ERROR(PLUGIN_NAME " plugin: NULL userdata");
     return -1;
   }
 
-  inst = ud->data;
-  state = &inst->read_state;
+  struct lv_read_instance *inst = ud->data;
+  struct lv_read_state *state = &inst->read_state;
 
-  if (inst->id == 0) {
+  if (inst->id == 0)
     if (lv_connect() < 0)
       return -1;
+
+  /* Wait until inst#0 establish connection */
+  if (conn == NULL) {
+    DEBUG(PLUGIN_NAME " plugin#%s: Wait until inst#0 establish connection",
+          inst->tag);
+    return 0;
   }
 
+  int ret = virConnectIsAlive(conn);
+  if (ret == 0) { /* Connection lost */
+    if (inst->id == 0) {
+      c_complain(LOG_ERR, &conn_complain,
+                 PLUGIN_NAME " plugin: Lost connection.");
+
+      if (!persistent_notification)
+        stop_event_loop(&notif_thread);
+
+      lv_disconnect();
+      last_refresh = 0;
+    }
+    return -1;
+  }
+
+  time_t t;
   time(&t);
 
   /* Need to refresh domain or device lists? */
   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 if (extra_stats & ex_stats_domain_state)
+      status = submit_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. */
   for (int i = 0; i < state->nr_block_devices; ++i) {
-    int status = get_block_stats(&state->block_devices[i]);
+    int status = get_block_device_stats(&state->block_devices[i]);
     if (status != 0)
       ERROR(PLUGIN_NAME
-            " failed to get stats for block device (%s) in domain %s",
+            " plugin: failed to get stats for block device (%s) in domain %s",
             state->block_devices[i].path,
-            virDomainGetName(state->domains[i].ptr));
+            virDomainGetName(state->block_devices[i].dom));
   }
 
   /* Get interface stats for each domain. */
   for (int i = 0; i < state->nr_interface_devices; ++i) {
     int status = get_if_dev_stats(&state->interface_devices[i]);
     if (status != 0)
-      ERROR(PLUGIN_NAME
-            " failed to get interface stats for device (%s) in domain %s",
-            state->interface_devices[i].path,
-            virDomainGetName(state->interface_devices[i].dom));
+      ERROR(
+          PLUGIN_NAME
+          " plugin: failed to get interface stats for device (%s) in domain %s",
+          state->interface_devices[i].path,
+          virDomainGetName(state->interface_devices[i].dom));
   }
 
   return 0;
@@ -1667,6 +2528,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);
 }
 
@@ -1681,6 +2543,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);
 }
 
@@ -1688,13 +2551,21 @@ static int lv_init(void) {
   if (virInitialize() != 0)
     return -1;
 
-  if (lv_connect() != 0)
+  /* Init ignorelists if there was no explicit configuration */
+  if (lv_init_ignorelists() != 0)
     return -1;
 
+  if (!persistent_notification)
+    if (virt_notif_thread_init(&notif_thread) != 0)
+      return -1;
+
+  lv_connect();
+
   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;
 }
@@ -1793,224 +2664,335 @@ 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) {
+  xmlXPathObjectPtr xpath_obj =
+      xmlXPathEval((const xmlChar *)"/domain/devices/disk", xpath_ctx);
+
+  if (xpath_obj == NULL) {
+    DEBUG(PLUGIN_NAME " plugin: no disk xpath-object found for domain %s",
+          domname);
+    return;
+  }
+
+  if (xpath_obj->type != XPATH_NODESET || xpath_obj->nodesetval == NULL) {
+    DEBUG(PLUGIN_NAME " plugin: no disk node found for domain %s", domname);
+    goto cleanup;
+  }
+
+  xmlNodeSetPtr xml_block_devices = xpath_obj->nodesetval;
+  for (int i = 0; i < xml_block_devices->nodeNr; ++i) {
+    xmlNodePtr xml_device = xpath_obj->nodesetval->nodeTab[i];
+    char *path_str = NULL;
+    char *source_str = NULL;
+
+    if (!xml_device)
+      continue;
+
+    /* Fetching path and source for block device */
+    for (xmlNodePtr child = xml_device->children; child; child = child->next) {
+      if (child->type != XML_ELEMENT_NODE)
+        continue;
+
+      /* we are interested only in either "target" or "source" elements */
+      if (xmlStrEqual(child->name, (const xmlChar *)"target"))
+        path_str = (char *)xmlGetProp(child, (const xmlChar *)"dev");
+      else if (xmlStrEqual(child->name, (const xmlChar *)"source")) {
+        /* name of the source is located in "dev" or "file" element (it depends
+         * on type of source). Trying "dev" at first*/
+        source_str = (char *)xmlGetProp(child, (const xmlChar *)"dev");
+        if (!source_str)
+          source_str = (char *)xmlGetProp(child, (const xmlChar *)"file");
+      }
+      /* ignoring any other element*/
+    }
+
+    /* source_str will be interpreted as a device path if blockdevice_format
+     *  param is set to 'source'. */
+    const char *device_path =
+        (blockdevice_format == source) ? source_str : path_str;
+
+    if (!device_path) {
+      /* no path found and we can't add block_device without it */
+      WARNING(PLUGIN_NAME " plugin: could not generate device path for disk in "
+                          "domain %s - disk device will be ignored in reports",
+              domname);
+      goto cont;
+    }
+
+    if (ignore_device_match(il_block_devices, domname, device_path) == 0) {
+      /* we only have to store information whether 'source' exists or not */
+      bool has_source = (source_str != NULL) ? true : false;
+
+      add_block_device(state, dom, device_path, has_source);
+    }
+
+  cont:
+    if (path_str)
+      xmlFree(path_str);
+
+    if (source_str)
+      xmlFree(source_str);
+  }
+
+cleanup:
+  xmlXPathFreeObject(xpath_obj);
+}
+
+static void lv_add_network_interfaces(struct lv_read_state *state,
+                                      virDomainPtr dom, const char *domname,
+                                      xmlXPathContextPtr xpath_ctx) {
+  xmlXPathObjectPtr xpath_obj = xmlXPathEval(
+      (xmlChar *)"/domain/devices/interface[target[@dev]]", xpath_ctx);
+
+  if (xpath_obj == NULL)
+    return;
+
+  if (xpath_obj->type != XPATH_NODESET || xpath_obj->nodesetval == NULL) {
+    xmlXPathFreeObject(xpath_obj);
+    return;
+  }
+
+  xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
+
+  for (int j = 0; j < xml_interfaces->nodeNr; ++j) {
+    char *path = NULL;
+    char *address = NULL;
+    const int itf_number = j + 1;
+
+    xmlNodePtr xml_interface = xml_interfaces->nodeTab[j];
+    if (!xml_interface)
+      continue;
+
+    for (xmlNodePtr child = xml_interface->children; child;
+         child = child->next) {
+      if (child->type != XML_ELEMENT_NODE)
+        continue;
+
+      if (xmlStrEqual(child->name, (const xmlChar *)"target")) {
+        path = (char *)xmlGetProp(child, (const xmlChar *)"dev");
+        if (!path)
+          continue;
+      } else if (xmlStrEqual(child->name, (const xmlChar *)"mac")) {
+        address = (char *)xmlGetProp(child, (const xmlChar *)"address");
+        if (!address)
+          continue;
+      }
+    }
+
+    bool device_ignored = false;
+    switch (interface_format) {
+    case if_name:
+      if (ignore_device_match(il_interface_devices, domname, path) != 0)
+        device_ignored = true;
+      break;
+    case if_address:
+      if (ignore_device_match(il_interface_devices, domname, address) != 0)
+        device_ignored = true;
+      break;
+    case if_number: {
+      char number_string[4];
+      snprintf(number_string, sizeof(number_string), "%d", itf_number);
+      if (ignore_device_match(il_interface_devices, domname, number_string) !=
+          0)
+        device_ignored = true;
+    } break;
+    default:
+      ERROR(PLUGIN_NAME " plugin: Unknown interface_format option: %d",
+            interface_format);
+    }
+
+    if (!device_ignored)
+      add_interface_device(state, dom, path, address, itf_number);
+
+    if (path)
+      xmlFree(path);
+    if (address)
+      xmlFree(address);
+  }
+  xmlXPathFreeObject(xpath_obj);
+}
+
+static bool is_domain_ignored(virDomainPtr dom) {
+  const char *domname = virDomainGetName(dom);
+
+  if (domname == NULL) {
+    VIRT_ERROR(conn, "virDomainGetName failed, ignoring domain");
+    return true;
+  }
+
+  if (ignorelist_match(il_domains, domname) != 0) {
+    DEBUG(PLUGIN_NAME
+          " plugin: ignoring domain '%s' because of ignorelist option",
+          domname);
+    return true;
+  }
+
+  return false;
+}
 
 static int refresh_lists(struct lv_read_instance *inst) {
   struct lv_read_state *state = &inst->read_state;
   int n;
 
+#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 (is_domain_ignored(domains_inactive[i]) ||
+        add_domain(state, domains_inactive[i], 0) < 0) {
+      /* domain ignored or failed during adding to domains list*/
+      virDomainFree(domains_inactive[i]);
+      domains_inactive[i] = NULL;
+      continue;
     }
+#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 (is_domain_ignored(dom) || add_domain(state, dom, 1) < 0) {
+      /*
+       * domain ignored or failed during adding to domains list
+       *
+       * When domain is already tracked, then there is
+       * no problem with memory handling (will be freed
+       * with the rest of domains cached data)
+       * But in case of error like this (error occurred
+       * before adding domain to track) we have to take
+       * care it ourselves and call virDomainFree
+       */
+      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;
+    /* Get a list of devices for this domain. */
+    xmlDocPtr xml_doc = NULL;
+    xmlXPathContextPtr xpath_ctx = NULL;
 
-        node = xpath_obj->nodesetval->nodeTab[j];
-        if (!node)
-          continue;
-        path = (char *)xmlGetProp(node, (xmlChar *)"dev");
-        if (!path)
-          continue;
+    char *xml = virDomainGetXMLDesc(dom, 0);
+    if (!xml) {
+      VIRT_ERROR(conn, "virDomainGetXMLDesc");
+      goto cont;
+    }
 
-        if (il_block_devices &&
-            ignore_device_match(il_block_devices, name, path) != 0)
-          goto cont2;
+    /* 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;
+    }
 
-        add_block_device(state, dom, path);
-      cont2:
-        if (path)
-          xmlFree(path);
-      }
-      xmlXPathFreeObject(xpath_obj);
+    xpath_ctx = xmlXPathNewContext(xml_doc);
 
-      /* 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;
+    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;
+    }
 
-      xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
+    if (!lv_instance_include_domain(inst, domname, tag))
+      goto cont;
 
-      for (int j = 0; j < xml_interfaces->nodeNr; ++j) {
-        char *path = NULL;
-        char *address = NULL;
-        xmlNodePtr xml_interface;
+    /* Block devices. */
+    if (report_block_devices)
+      lv_add_block_devices(state, dom, domname, xpath_ctx);
 
-        xml_interface = xml_interfaces->nodeTab[j];
-        if (!xml_interface)
-          continue;
+    /* Network interfaces. */
+    if (report_network_interfaces)
+      lv_add_network_interfaces(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);
-      }
-
-    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",
@@ -2030,20 +3012,19 @@ 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;
+static int add_domain(struct lv_read_state *state, virDomainPtr dom,
+                      bool active) {
   int new_size = sizeof(state->domains[0]) * (state->nr_domains + 1);
 
-  if (state->domains)
-    new_ptr = realloc(state->domains, new_size);
-  else
-    new_ptr = malloc(new_size);
-
-  if (new_ptr == NULL)
+  domain_t *new_ptr = realloc(state->domains, new_size);
+  if (new_ptr == NULL) {
+    ERROR(PLUGIN_NAME " plugin: realloc failed in add_domain()");
     return -1;
+  }
 
   state->domains = new_ptr;
   state->domains[state->nr_domains].ptr = dom;
+  state->domains[state->nr_domains].active = active;
   memset(&state->domains[state->nr_domains].info, 0,
          sizeof(state->domains[state->nr_domains].info));
 
@@ -2061,21 +3042,16 @@ 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;
+                            const char *path, bool has_source) {
 
-  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;
@@ -2083,6 +3059,7 @@ static int add_block_device(struct lv_read_state *state, virDomainPtr dom,
   state->block_devices = new_ptr;
   state->block_devices[state->nr_block_devices].dom = dom;
   state->block_devices[state->nr_block_devices].path = path_copy;
+  state->block_devices[state->nr_block_devices].has_source = has_source;
   return state->nr_block_devices++;
 }
 
@@ -2102,61 +3079,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;
 }
@@ -2166,6 +3144,9 @@ static int lv_shutdown(void) {
     lv_fini_instance(i);
   }
 
+  if (!persistent_notification)
+    stop_event_loop(&notif_thread);
+
   lv_disconnect();
 
   ignorelist_free(il_domains);
@@ -2179,7 +3160,7 @@ static int lv_shutdown(void) {
 }
 
 void module_register(void) {
-  plugin_register_config(PLUGIN_NAME, lv_config, config_keys, NR_CONFIG_KEYS);
+  plugin_register_complex_config("virt", lv_config);
   plugin_register_init(PLUGIN_NAME, lv_init);
   plugin_register_shutdown(PLUGIN_NAME, lv_shutdown);
 }
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 95cfbaf..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
index f15bb3b..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?
index 31aba0a..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,11 +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) {
-    WARNING("wireless: fopen: %s", STRERRNO);
+    ERROR("wireless plugin: fopen: %s", STRERRNO);
+    return -1;
+  }
+
+  int sock = socket(AF_INET, SOCK_DGRAM, 0);
+  if (sock == -1) {
+    ERROR("wireless plugin: socket: %s", STRERRNO);
+    fclose(fh);
     return -1;
   }
 
@@ -142,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 b39448f..cb6793d 100644 (file)
@@ -29,7 +29,7 @@
  * Based on the write_http plugin.
  **/
 
-/* write_graphite plugin configuation example
+/* write_graphite plugin configuration example
  *
  * <Plugin write_graphite>
  *   <Carbon>
  *     Protocol "udp"
  *     LogSendErrors true
  *     Prefix "collectd"
+ *     UseTags true
+ *     ReverseHost false
  *   </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 +67,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 +94,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 +113,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 +133,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));
@@ -274,7 +276,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;
 }
@@ -466,7 +468,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;
@@ -518,6 +520,10 @@ 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("ReverseHost", child->key) == 0)
+      cf_util_get_flag(child, &cb->format_flags, GRAPHITE_REVERSE_HOST);
     else if (strcasecmp("EscapeCharacter", child->key) == 0)
       config_set_char(&cb->escape_char, child);
     else {
index 024dccc..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;
@@ -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);
 
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 a6b39d3..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;
@@ -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 4c363b2..96e71ca 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"
 
@@ -54,6 +54,7 @@
 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;
 
@@ -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.");
@@ -722,7 +722,7 @@ metric_family_get(data_set_t const *ds, value_list_t const *vl, size_t ds_index,
 
   int status = c_avl_insert(metrics, fam->name, fam);
   if (status != 0) {
-    ERROR("write_prometheus plugin: Adding \"%s\" failed.", name);
+    ERROR("write_prometheus plugin: Adding \"%s\" failed.", fam->name);
     metric_family_destroy(fam);
     return NULL;
   }
@@ -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,
@@ -760,15 +760,18 @@ static int prom_open_socket(int addrfamily) {
 
   int fd = -1;
   for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) {
-    fd = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC, 0);
+    int flags = ai->ai_socktype;
+#ifdef SOCK_CLOEXEC
+    flags |= SOCK_CLOEXEC;
+#endif
+
+    fd = socket(ai->ai_family, flags, 0);
     if (fd == -1)
       continue;
 
-    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;
@@ -786,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;
   }
 
@@ -800,12 +812,19 @@ 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;
   }
 
+  unsigned int flags = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG;
+#if MHD_VERSION >= 0x00095300
+  flags |= MHD_USE_INTERNAL_POLLING_THREAD;
+#endif
+
   struct MHD_Daemon *d = MHD_start_daemon(
-      MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, httpd_port,
+      flags, httpd_port,
       /* MHD_AcceptPolicyCallback = */ NULL,
       /* MHD_AcceptPolicyCallback arg = */ NULL, http_handler, NULL,
       MHD_OPTION_LISTEN_SOCKET, fd, MHD_OPTION_EXTERNAL_LOGGER, prom_logger,
@@ -842,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;
@@ -870,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",
@@ -886,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;
 
@@ -913,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;
 
@@ -963,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 55699d5..8056993 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, /* {{{ */
@@ -394,6 +392,23 @@ wrr_value_to_event(struct riemann_host const *host, /* {{{ */
                       RIEMANN_EVENT_FIELD_NONE);
   }
 
+  if (vl->meta) {
+    char **toc;
+    int n = meta_data_toc(vl->meta, &toc);
+
+    for (int i = 0; i < n; i++) {
+      char *key = toc[i];
+      char *value;
+
+      if (0 == meta_data_as_string(vl->meta, key, &value)) {
+        riemann_event_string_attribute_add(event, key, value);
+        free(value);
+      }
+    }
+
+    free(toc);
+  }
+
   DEBUG("write_riemann plugin: Successfully created message for metric: "
         "host = \"%s\", service = \"%s\"",
         event->host, event->service);
@@ -546,7 +561,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 +647,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 +716,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 +738,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 71dd585..7d08fb5 100644 (file)
@@ -28,8 +28,8 @@
 
 #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>
@@ -107,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;
@@ -119,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,
@@ -309,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 '(':
@@ -337,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;
@@ -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 */
@@ -926,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);
@@ -1019,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;
@@ -1085,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)
@@ -1124,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)) {
@@ -1225,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 */
diff --git a/src/write_syslog.c b/src/write_syslog.c
new file mode 100644 (file)
index 0000000..92c5dde
--- /dev/null
@@ -0,0 +1,639 @@
+/**
+ * collectd - src/write_syslog.c
+ * Copyright (C) 2012       Pierre-Yves Ritschard
+ * Copyright (C) 2011       Scott Sanders
+ * Copyright (C) 2009       Paul Sadauskas
+ * Copyright (C) 2009       Doug MacEachern
+ * Copyright (C) 2007-2012  Florian octo Forster
+ * Copyright (C) 2013-2014  Limelight Networks, Inc.
+ * Copyright (C) 2019       Shirly Radco
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Based on the write_graphite plugin. Authors:
+ *   Florian octo Forster <octo at collectd.org>
+ *   Doug MacEachern <dougm at hyperic.com>
+ *   Paul Sadauskas <psadauskas at gmail.com>
+ *   Scott Sanders <scott at jssjr.com>
+ *   Pierre-Yves Ritschard <pyr at spootnik.org>
+ * Based on the write_tsdb plugin. Authors:
+ *   Brett Hawn <bhawn at llnw.com>
+ *   Kevin Bowling <kbowling@llnw.com>
+ * write_syslog. Authors:
+ *   Shirly Radco <sradco@redhat.com>
+ **/
+
+/* write_syslog plugin configuration example
+ *
+ * <Plugin write_syslog>
+ *   <Node>
+ *     Host "localhost"
+ *     Port "44514"
+ *     Prefix "collectd"
+ *     MessageFormat "human"
+ *     HostTags "["prefix1" "example1"="example1_v"]
+ *   </Node>
+ * </Plugin>
+ *
+ */
+
+#include "collectd.h"
+#include "utils/common/common.h"
+
+#include "plugin.h"
+#include "utils_cache.h"
+#include "utils_random.h"
+
+#include <netdb.h>
+
+#define WS_DEFAULT_NODE "localhost"
+
+#define WS_DEFAULT_SERVICE "44514"
+
+#define WS_DEFAULT_FORMAT "human"
+
+#define WS_DEFAULT_PREFIX "collectd"
+
+#define WS_DEFAULT_ESCAPE '.'
+
+/* Ethernet - (IPv6 + TCP) = 1500 - (40 + 32) = 1428 */
+#define WS_SEND_BUF_SIZE 1428
+
+/*
+ * Private variables
+ */
+struct ws_callback {
+  struct addrinfo *ai;
+  cdtime_t ai_last_update;
+  int sock_fd;
+
+  char *node;
+  char *service;
+  char *host_tags;
+  char *msg_format;
+  char *metrics_prefix;
+  bool store_rates;
+  bool always_append_ds;
+
+  char send_buf[WS_SEND_BUF_SIZE];
+  size_t send_buf_free;
+  size_t send_buf_fill;
+  cdtime_t send_buf_init_time;
+
+  pthread_mutex_t send_lock;
+
+  bool connect_failed_log_enabled;
+  int connect_dns_failed_attempts_remaining;
+  cdtime_t next_random_ttl;
+};
+
+static cdtime_t resolve_interval;
+static cdtime_t resolve_jitter;
+
+/*
+ * Functions
+ */
+static void ws_reset_buffer(struct ws_callback *cb) {
+  memset(cb->send_buf, 0, sizeof(cb->send_buf));
+  cb->send_buf_free = sizeof(cb->send_buf);
+  cb->send_buf_fill = 0;
+  cb->send_buf_init_time = cdtime();
+}
+
+static int ws_send_buffer(struct ws_callback *cb) {
+  ssize_t status = 0;
+
+  status = swrite(cb->sock_fd, cb->send_buf, strlen(cb->send_buf));
+  if (status != 0) {
+    ERROR("write_syslog plugin: send failed with status %zi (%s)", status,
+          STRERRNO);
+
+    if (cb->sock_fd > 0) {
+      close(cb->sock_fd);
+      cb->sock_fd = -1;
+    }
+
+    return -1;
+  }
+
+  return 0;
+}
+
+/* NOTE: You must hold cb->send_lock when calling this function! */
+static int ws_flush_nolock(cdtime_t timeout, struct ws_callback *cb) {
+  int status;
+
+  DEBUG("write_syslog plugin: ws_flush_nolock: timeout = %.3f; "
+        "send_buf_fill = %" PRIsz ";",
+        (double)timeout, cb->send_buf_fill);
+
+  /* timeout == 0  => flush unconditionally */
+  if (timeout > 0) {
+    cdtime_t now;
+
+    now = cdtime();
+    if ((cb->send_buf_init_time + timeout) > now)
+      return 0;
+  }
+
+  if (cb->send_buf_fill == 0) {
+    cb->send_buf_init_time = cdtime();
+    return 0;
+  }
+
+  status = ws_send_buffer(cb);
+  ws_reset_buffer(cb);
+
+  return status;
+}
+
+static cdtime_t new_random_ttl(void) {
+  if (resolve_jitter == 0)
+    return 0;
+
+  return (cdtime_t)cdrand_range(0, (long)resolve_jitter);
+}
+
+static int ws_callback_init(struct ws_callback *cb) {
+  int status;
+  cdtime_t now;
+
+  const char *node = cb->node ? cb->node : WS_DEFAULT_NODE;
+  const char *service = cb->service ? cb->service : WS_DEFAULT_SERVICE;
+
+  if (cb->sock_fd > 0)
+    return 0;
+
+  now = cdtime();
+  if (cb->ai) {
+    /* When we are here, we still have the IP in cache.
+     * If we have remaining attempts without calling the DNS, we update the
+     * last_update date so we keep the info until next time.
+     * If there is no more attempts, we need to flush the cache.
+     */
+
+    if ((cb->ai_last_update + resolve_interval + cb->next_random_ttl) < now) {
+      cb->next_random_ttl = new_random_ttl();
+      if (cb->connect_dns_failed_attempts_remaining > 0) {
+        /* Warning : this is run under send_lock mutex.
+         * This is why we do not use another mutex here.
+         * */
+        cb->ai_last_update = now;
+        cb->connect_dns_failed_attempts_remaining--;
+      } else {
+        freeaddrinfo(cb->ai);
+        cb->ai = NULL;
+      }
+    }
+  }
+
+  if (cb->ai == NULL) {
+    if ((cb->ai_last_update + resolve_interval + cb->next_random_ttl) >= now) {
+      DEBUG("write_syslog plugin: too many getaddrinfo(%s, %s) failures", node,
+            service);
+      return -1;
+    }
+    cb->ai_last_update = now;
+    cb->next_random_ttl = new_random_ttl();
+
+    struct addrinfo ai_hints = {
+        .ai_family = AF_UNSPEC,
+        .ai_flags = AI_ADDRCONFIG,
+        .ai_socktype = SOCK_STREAM,
+    };
+
+    status = getaddrinfo(node, service, &ai_hints, &cb->ai);
+    if (status != 0) {
+      if (cb->ai) {
+        freeaddrinfo(cb->ai);
+        cb->ai = NULL;
+      }
+      if (cb->connect_failed_log_enabled) {
+        ERROR("write_syslog plugin: getaddrinfo(%s, %s) failed: %s", node,
+              service, gai_strerror(status));
+        cb->connect_failed_log_enabled = 0;
+      }
+      return -1;
+    }
+  }
+
+  assert(cb->ai != NULL);
+  for (struct addrinfo *ai = cb->ai; ai != NULL; ai = ai->ai_next) {
+    cb->sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+    if (cb->sock_fd < 0)
+      continue;
+
+    set_sock_opts(cb->sock_fd);
+
+    status = connect(cb->sock_fd, ai->ai_addr, ai->ai_addrlen);
+    if (status != 0) {
+      close(cb->sock_fd);
+      cb->sock_fd = -1;
+      continue;
+    }
+
+    break;
+  }
+
+  if (cb->sock_fd < 0) {
+    ERROR("write_syslog plugin: Connecting to %s:%s failed. "
+          "The last error was: %s",
+          node, service, STRERRNO);
+    return -1;
+  }
+
+  if (cb->connect_failed_log_enabled == 0) {
+    INFO("write_syslog plugin: Connecting to %s:%s succeeded.", node, service);
+    cb->connect_failed_log_enabled = 1;
+  }
+  cb->connect_dns_failed_attempts_remaining = 1;
+
+  ws_reset_buffer(cb);
+
+  return 0;
+}
+
+static void ws_callback_free(void *data) {
+  struct ws_callback *cb;
+
+  if (data == NULL)
+    return;
+
+  cb = data;
+
+  pthread_mutex_lock(&cb->send_lock);
+
+  ws_flush_nolock(0, cb);
+
+  close(cb->sock_fd);
+  cb->sock_fd = -1;
+
+  sfree(cb->node);
+  sfree(cb->service);
+  sfree(cb->host_tags);
+  sfree(cb->msg_format);
+  sfree(cb->metrics_prefix);
+
+  pthread_mutex_unlock(&cb->send_lock);
+  pthread_mutex_destroy(&cb->send_lock);
+
+  sfree(cb);
+}
+
+static int ws_flush(cdtime_t timeout,
+                    const char *identifier __attribute__((unused)),
+                    user_data_t *user_data) {
+  struct ws_callback *cb;
+  int status;
+
+  if (user_data == NULL)
+    return -EINVAL;
+
+  cb = user_data->data;
+
+  pthread_mutex_lock(&cb->send_lock);
+
+  if (cb->sock_fd < 0) {
+    status = ws_callback_init(cb);
+    if (status != 0) {
+      ERROR("write_syslog plugin: ws_callback_init failed.");
+      pthread_mutex_unlock(&cb->send_lock);
+      return -1;
+    }
+  }
+
+  status = ws_flush_nolock(timeout, cb);
+  pthread_mutex_unlock(&cb->send_lock);
+
+  return status;
+}
+
+static int ws_format_values(char *ret, size_t ret_len, int ds_num,
+                            const data_set_t *ds, const value_list_t *vl,
+                            bool store_rates) {
+  size_t offset = 0;
+  int status;
+  gauge_t *rates = NULL;
+
+  assert(strcmp(ds->type, vl->type) == 0);
+
+  memset(ret, 0, ret_len);
+
+#define BUFFER_ADD(...)                                                        \
+  do {                                                                         \
+    status = snprintf(ret + offset, ret_len - offset, __VA_ARGS__);            \
+    if (status < 1) {                                                          \
+      sfree(rates);                                                            \
+      return -1;                                                               \
+    } else if (((size_t)status) >= (ret_len - offset)) {                       \
+      sfree(rates);                                                            \
+      return -1;                                                               \
+    } else                                                                     \
+      offset += ((size_t)status);                                              \
+  } while (0)
+
+  if (ds->ds[ds_num].type == DS_TYPE_GAUGE)
+    BUFFER_ADD(GAUGE_FORMAT, vl->values[ds_num].gauge);
+  else if (store_rates) {
+    if (rates == NULL)
+      rates = uc_get_rate(ds, vl);
+    if (rates == NULL) {
+      WARNING("format_values: "
+              "uc_get_rate failed.");
+      return -1;
+    }
+    BUFFER_ADD(GAUGE_FORMAT, rates[ds_num]);
+  } else if (ds->ds[ds_num].type == DS_TYPE_COUNTER)
+    BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[ds_num].counter);
+  else if (ds->ds[ds_num].type == DS_TYPE_DERIVE)
+    BUFFER_ADD("%" PRIi64, vl->values[ds_num].derive);
+  else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
+    BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute);
+  else {
+    ERROR("format_values plugin: Unknown data source type: %i",
+          ds->ds[ds_num].type);
+    sfree(rates);
+    return -1;
+  }
+
+#undef BUFFER_ADD
+
+  sfree(rates);
+  return 0;
+}
+
+static int ws_format_name(char *ret, int ret_len, const value_list_t *vl,
+                          const struct ws_callback *cb, const char *ds_name) {
+
+  if (ds_name != NULL) {
+    snprintf(ret, ret_len, "%s.%s", vl->type, ds_name);
+  } else { /* ds_name == NULL */
+    snprintf(ret, ret_len, "%s", vl->type);
+  }
+
+  return 0;
+}
+
+static int ws_send_message(const char *key, const char *value, cdtime_t time,
+                           struct ws_callback *cb, const char *plugin,
+                           const char *plugin_instance,
+                           const char *type_instance, const char *type,
+                           const char *ds_name, cdtime_t interval,
+                           const char *host) {
+  int status;
+  size_t message_len;
+  char message[1024];
+  char rfc3339_timestamp[64];
+  const char *host_tags = cb->host_tags ? cb->host_tags : "";
+  const char *host_tags_json_prefix = "";
+  const char *metrics_prefix =
+      cb->metrics_prefix ? cb->metrics_prefix : WS_DEFAULT_PREFIX;
+  const char *msg_format = cb->msg_format ? cb->msg_format : WS_DEFAULT_FORMAT;
+  int pid;
+
+  pid = getpid();
+
+  rfc3339_local(rfc3339_timestamp, sizeof(rfc3339_timestamp), time);
+
+  /* skip if value is NaN */
+  if (value[0] == 'n')
+    return 0;
+
+  if (strcasecmp("JSON", msg_format) == 0) {
+    if (cb->host_tags) {
+      host_tags_json_prefix = ",";
+    }
+    status = snprintf(
+        /* The metric key-values are are part of the syslog msg, in json
+           format */
+        message, sizeof(message),
+        "<166>1 %s %s collectd %d - - {\"time\":%.0f, \"%s\":{ \"%s\":{ "
+        "\"%s\":%s }, "
+        "\"plugin\":\"%s\", \"plugin_instance\":\"%s\", "
+        "\"type_instance\":\"%s\","
+        " \"type\":\"%s\", \"interval\":%.0f }, \"hostname\":\"%s\" %s "
+        "%s}\n",
+        rfc3339_timestamp, host, pid, CDTIME_T_TO_DOUBLE(time), metrics_prefix,
+        plugin, key, value, plugin, plugin_instance, type_instance, type,
+        CDTIME_T_TO_DOUBLE(interval), host, host_tags_json_prefix, host_tags);
+  } else {
+    status = snprintf(
+        /* The metric key-values are part of the syslog structrude data,
+         * MessageFormat = "human" */
+        message, sizeof(message),
+        "<166>1 %s %s collectd %d - [%s value=\"%s\""
+        " plugin=\"%s\" plugin_instance=\"%s\""
+        " type_instance=\"%s\" type=\"%s\""
+        " ds_name=\"%s\" interval=\"%.0f\"] %s %s.%s=\"%s\"\n",
+        rfc3339_timestamp, host, pid, metrics_prefix, value, plugin,
+        plugin_instance, type_instance, type, ds_name,
+        CDTIME_T_TO_DOUBLE(interval), host_tags, plugin, key, value);
+  }
+  if (status < 0)
+    return -1;
+  message_len = (size_t)status;
+
+  if (message_len >= sizeof(message)) {
+    ERROR("write_syslog plugin: message buffer too small: "
+          "Need %" PRIsz " bytes.",
+          message_len + 1);
+    return -1;
+  }
+
+  pthread_mutex_lock(&cb->send_lock);
+
+  if (cb->sock_fd < 0) {
+    status = ws_callback_init(cb);
+    if (status != 0) {
+      ERROR("write_syslog plugin: ws_callback_init failed.");
+      pthread_mutex_unlock(&cb->send_lock);
+      return -1;
+    }
+  }
+
+  if (message_len >= cb->send_buf_free) {
+    status = ws_flush_nolock(0, cb);
+    if (status != 0) {
+      pthread_mutex_unlock(&cb->send_lock);
+      return status;
+    }
+  }
+
+  /* Assert that we have enough space for this message. */
+  assert(message_len < cb->send_buf_free);
+
+  /* `message_len + 1' because `message_len' does not include the
+   * trailing null byte. Neither does `send_buffer_fill'. */
+  memcpy(cb->send_buf + cb->send_buf_fill, message, message_len + 1);
+  cb->send_buf_fill += message_len;
+  cb->send_buf_free -= message_len;
+
+  DEBUG("write_syslog plugin: [%s]:%s buf %" PRIsz "/%" PRIsz
+        " (%.1f %%) \"%s\"",
+        cb->node, cb->service, cb->send_buf_fill, sizeof(cb->send_buf),
+        100.0 * ((double)cb->send_buf_fill) / ((double)sizeof(cb->send_buf)),
+        message);
+
+  pthread_mutex_unlock(&cb->send_lock);
+
+  return 0;
+}
+
+static int ws_write_messages(const data_set_t *ds, const value_list_t *vl,
+                             struct ws_callback *cb) {
+  char key[10 * DATA_MAX_NAME_LEN];
+  char values[512];
+
+  int status;
+
+  if (0 != strcmp(ds->type, vl->type)) {
+    ERROR("write_syslog plugin: DS type does not match "
+          "value list type");
+    return -1;
+  }
+
+  for (size_t i = 0; i < ds->ds_num; i++) {
+    const char *ds_name = NULL;
+
+    if (cb->always_append_ds || (ds->ds_num > 1))
+      ds_name = ds->ds[i].name;
+
+    /* Copy the identifier to 'key' and escape it. */
+    status = ws_format_name(key, sizeof(key), vl, cb, ds_name);
+    if (status != 0) {
+      ERROR("write_syslog plugin: error with format_name");
+      return status;
+    }
+
+    escape_string(key, sizeof(key));
+    /* Convert the values to an ASCII representation and put that into
+     * 'values'. */
+    status =
+        ws_format_values(values, sizeof(values), i, ds, vl, cb->store_rates);
+    if (status != 0) {
+      ERROR("write_syslog plugin: error with "
+            "ws_format_values");
+      return status;
+    }
+
+    /* Send the message to tcp */
+    status = ws_send_message(key, values, vl->time, cb, vl->plugin,
+                             vl->plugin_instance, vl->type_instance, vl->type,
+                             ds_name, vl->interval, vl->host);
+    if (status != 0) {
+      ERROR("write_syslog plugin: error with "
+            "ws_send_message");
+      return status;
+    }
+  }
+
+  return 0;
+}
+
+static int ws_write(const data_set_t *ds, const value_list_t *vl,
+                    user_data_t *user_data) {
+  struct ws_callback *cb;
+  int status;
+
+  if (user_data == NULL)
+    return EINVAL;
+
+  cb = user_data->data;
+
+  status = ws_write_messages(ds, vl, cb);
+
+  return status;
+}
+
+static int ws_config_tsd(oconfig_item_t *ci) {
+  struct ws_callback *cb;
+  char callback_name[DATA_MAX_NAME_LEN];
+
+  cb = calloc(1, sizeof(*cb));
+  if (cb == NULL) {
+    ERROR("write_syslog plugin: calloc failed.");
+    return -1;
+  }
+  cb->sock_fd = -1;
+  cb->connect_failed_log_enabled = 1;
+  cb->next_random_ttl = new_random_ttl();
+
+  pthread_mutex_init(&cb->send_lock, NULL);
+
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp("Host", child->key) == 0)
+      cf_util_get_string(child, &cb->node);
+    else if (strcasecmp("Port", child->key) == 0)
+      cf_util_get_service(child, &cb->service);
+    else if (strcasecmp("MessageFormat", child->key) == 0)
+      cf_util_get_string(child, &cb->msg_format);
+    else if (strcasecmp("HostTags", child->key) == 0)
+      cf_util_get_string(child, &cb->host_tags);
+    else if (strcasecmp("StoreRates", child->key) == 0)
+      cf_util_get_boolean(child, &cb->store_rates);
+    else if (strcasecmp("AlwaysAppendDS", child->key) == 0)
+      cf_util_get_boolean(child, &cb->always_append_ds);
+    else if (strcasecmp("Prefix", child->key) == 0)
+      cf_util_get_string(child, &cb->metrics_prefix);
+    else {
+      ERROR("write_syslog plugin: Invalid configuration "
+            "option: %s.",
+            child->key);
+      return -1;
+    }
+  }
+
+  snprintf(callback_name, sizeof(callback_name), "write_syslog/%s/%s",
+           cb->node != NULL ? cb->node : WS_DEFAULT_NODE,
+           cb->service != NULL ? cb->service : WS_DEFAULT_SERVICE);
+
+  user_data_t user_data = {.data = cb, .free_func = ws_callback_free};
+
+  plugin_register_write(callback_name, ws_write, &user_data);
+
+  user_data.free_func = NULL;
+  plugin_register_flush(callback_name, ws_flush, &user_data);
+
+  return 0;
+}
+
+static int ws_config(oconfig_item_t *ci) {
+  if ((resolve_interval == 0) && (resolve_jitter == 0))
+    resolve_interval = resolve_jitter = plugin_get_interval();
+
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp("Node", child->key) == 0) {
+      if (ws_config_tsd(child) < 0)
+        return -1;
+    } else if (strcasecmp("ResolveInterval", child->key) == 0)
+      cf_util_get_cdtime(child, &resolve_interval);
+    else if (strcasecmp("ResolveJitter", child->key) == 0)
+      cf_util_get_cdtime(child, &resolve_jitter);
+    else {
+      ERROR("write_syslog plugin: Invalid configuration "
+            "option: %s.",
+            child->key);
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+void module_register(void) {
+  plugin_register_complex_config("write_syslog", ws_config);
+}
index 349b0d0..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
@@ -152,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;
 
@@ -313,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;
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 c9abdd5..d18a93f 100644 (file)
@@ -29,8 +29,8 @@
 
 #include "collectd.h"
 
-#include "common.h"
 #include "plugin.h"
+#include "utils/common/common.h"
 
 /*
  * Global variables
@@ -231,6 +231,23 @@ static int za_read(void) {
     return -1;
   }
 
+  // Ignore the first two lines because they contain information about
+  // the rest of the file.
+  // See kstat_seq_show_headers module/spl/spl-kstat.c of the spl kernel
+  // module.
+  if (fgets(buffer, sizeof(buffer), fh) == NULL) {
+    ERROR("zfs_arc plugin: \"%s\" does not contain a single line.",
+          ZOL_ARCSTATS_FILE);
+    fclose(fh);
+    return (-1);
+  }
+  if (fgets(buffer, sizeof(buffer), fh) == NULL) {
+    ERROR("zfs_arc plugin: \"%s\" does not contain at least two lines.",
+          ZOL_ARCSTATS_FILE);
+    fclose(fh);
+    return (-1);
+  }
+
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
     char *fields[3];
     value_t v;
index 16df404..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)
index 1de01d0..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);
index f16e661..048b5a2 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-DEFAULT_VERSION="5.8.0.git"
+DEFAULT_VERSION="5.8.1.git"
 
 if [ -d .git ]; then
        VERSION="`git describe --dirty=+ --abbrev=7 2> /dev/null | grep collectd | sed -e 's/^collectd-//' -e 's/-/./g'`"