Fix conflict from remove of ssnprintf2 and align utils/common/common.h
authorZebity Spring <zebity@yahoo.com>
Thu, 24 Oct 2019 11:08:22 +0000 (22:08 +1100)
committerZebity Spring <zebity@yahoo.com>
Thu, 24 Oct 2019 11:08:22 +0000 (22:08 +1100)
20 files changed:
.travis.yml
AUTHORS
ChangeLog
Makefile.am
README
configure.ac
contrib/redhat/collectd.spec
src/collectd.conf.in
src/collectd.conf.pod
src/connectivity.c [new file with mode: 0644]
src/contextswitch.c
src/exec.c
src/java.c
src/lvm.c [deleted file]
src/sysevent.c
src/utils/common/common.c
src/utils/common/common.h
src/utils/common/common_test.c
src/zfs_arc.c
version-gen.sh

index 4ba4e2a..f715343 100644 (file)
@@ -12,17 +12,21 @@ env:
 matrix:
   include:
     - os: osx
-      osx_image: xcode10.1
+      osx_image: xcode11.2
       compiler: clang
+      jdk: openjdk10
       env:
         - CXX=clang++
         - PATH="/usr/local/opt/mysql-client/bin:$PATH"
+        - JAVA_HOME="/Library/Java/JavaVirtualMachines/openjdk-13.jdk/Contents/Home"
     - os: linux
       dist: xenial
       compiler: clang
+      jdk: openjdk10
     - os: linux
       dist: xenial
       compiler: gcc
+      jdk: openjdk10
 
 before_install:
   # When building the coverity_scan branch, allow only the first job to continue to avoid travis-ci/travis-ci#1975.
@@ -32,7 +36,10 @@ before_script: autoreconf -vif
 
 script:
   - if [[ "${TRAVIS_BRANCH}" == "coverity_scan" ]]; then exit 0; fi
+  - type pkg-config
+  - pkg-config --list-all | sort -u
   - ./configure
+  - cat config.log
   - make distcheck DISTCHECK_CONFIGURE_FLAGS="--disable-dependency-tracking --enable-debug"
 
 addons:
@@ -56,7 +63,6 @@ addons:
     - liblua50-dev
     - liblua5.1-0-dev
     - liblua5.2-dev
-    - liblvm2-dev
     - libmemcached-dev
     - libmicrohttpd-dev
     - libmnl-dev
@@ -78,7 +84,6 @@ addons:
     - libsensors4-dev
     - libsigrok-dev
     - libsnmp-dev
-    - libstatgrab-dev
     - libtokyocabinet-dev
     - libtokyotyrant-dev
     - libudev-dev
@@ -106,6 +111,7 @@ addons:
     packages:
     - curl
     - glib
+    - grpc
     - hiredis
     - libdbi
     - libmemcached
@@ -115,9 +121,10 @@ addons:
     - liboping
     - libpcap
     - librdkafka
-    - libstatgrab
     - libvirt
+    - libxml2
     - lua
+    - mongo-c-driver
     - mosquitto
     - mysql-client
     - net-snmp
diff --git a/AUTHORS b/AUTHORS
index db58066..d8548d1 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -57,14 +57,22 @@ Andreas Henriksson <andreas at fatal.se>
  - libmnl support in the netlink plugin.
 
 Andrew Bays <abays at redhat.com>
+ - connectivity plugin.
  - procevent plugin.
  - sysevent plugin.
+ - connectivity plugin.
 
 Andy Parkins <andyp at fussylogic.co.uk>
  - battery plugin: sysfs code.
 
+Aneesh Puttur <aputtur at redhat.com>
+ - connectivity plugin.
+
 Andy Smith <ansmith at redhat.com>
  - AMQP 1.0 plugin.
+Aneesh Puttur <aputtur at redhat.com>
+ - connectivity plugin.
 
 Anthony Dewhurst <dewhurst at gmail.com>
  - zfs_arc plugin.
index 7ea5a07..e558a26 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,300 @@
+2019-10-01, Version 5.9.2
+       * syslog plugin: Don't fail if syslog loglevel doesn't match. Thanks to
+         Fabien Wernli. #3236 #3238
+       * collectd: Fix ssnprintf wrapper. Thanks to Fabien Wernli. #3237
+       * rdt plugin: Fix compile time issues. Thanks to Matthias Runge. #3245
+
+2019-07-24, Version 5.9.1
+       * collectd: redhat spec: fix build due to new upstream plugins. Thanks
+         to Fabien Wernli. #3175
+       * collectd: regex match: Fix unexpected match with empty meta data .
+         Thanks to Takuro Ashie. #3178
+       * collectd: Fix return value or loglevel for several plugins. Thanks to
+         Fabien Wernli. #3182
+       * collectd: Add standard include early or _FILE_OFFSET_BITS will have
+         definition … . Thanks to Dagobert Michelsen. #3193
+       * collectd: Use GCC-specific flags only when compiling with GCC. Thanks
+         to Dagobert Michelsen. #3195
+       * Use test_utils_proc_pids only when compiling the plugin that uses it.
+         Thanks to Dagobert Michelsen. #3197
+       * DNS plugin: Do not use headers from glibc. Thanks to Pavel Rochnyak.
+         #3156, #3145
+       * collectd: Add missing definitions for libnetsnmpagent. Thanks to
+         Dagobert Michelsen. #3203
+       * collectd: Move Makefile rules for pid_test inside conditional for
+         code. Thanks to Dagobert Michelsen. #3206
+       * collectd: Recover setlocale() call in src/daemon/collectd.c do_init().
+         Thanks to Pavel Rochnyak. #3214, #3181
+       * collectd: Add snprintf wrapper for GCC 8.2/3. Thanks to zebity. #3153,
+         #2895, #3038
+       * collectd: Fix bug that leads to CPPFLAGS gets overridden with CFLAGS
+         when libxmms is enabled. Thanks to Dagobert Michelsen. #3207
+       * Write_Riemann plugin: Copy MetaData to Riemann events in
+         write_riemann. Thanks to Romain Tartière. #3158
+       * virt plugin: Fix memory leak with libvirt MetadataXPath enabled.
+         Thanks to Pavel Rochnyak. #3225, #3230
+
+2019-06-13, Version 5.9.0
+       * Build System: configure.ac: option "--with-libxml2" has been added.
+         Thanks to Dimitrios Apostolou, Pavel Rochnyak. #2864
+       * Build System: configure.ac: run dpdk build tests only if pkgconfig
+         fails. Thanks to Luca Boccassi, Pavel Rochnyak. #3015
+       * Build System: The "df" plugin is now built when "getmntent_r()" is
+         available. Thanks to Florian Forster. #3095
+       * Build System: The ability to turn on collectd "debug" feature in RPMs
+         has been added. Thanks to dehotot. #2755
+       * collectd: A new "UNKNOWN" state as the initial state of metrics has
+         been added. Thanks to Luis Fernández Álvarez, Florian Forster. #2976
+       * collectd: Base port to Windows. Thanks to Sean Campbell. #2810
+       * collectd: Code ownership of five plugins has been handed out to folks
+         from Intel. Thanks to Florian Forster. #3053
+       * collectd: config parser: Improved error reporting on global options.
+         Thanks to Pavel Rochnyak. #2813
+       * collectd: daemon: make plugin_dispatch_multivalue() obey write queue
+         limits. Thanks to Adam Romanek. #2898
+       * collectd: Macros "STRERROR" and "STRERRNO" have been added. Thanks to
+         Florian Forster. #2519
+       * collectd: Plugin name field has been added to plugin context to
+         improve error reporting. Thanks to Pavel Rochnyak. #2821
+       * collectd-tg: Use "CLOCK_REALTIME" for collectd-tg times. Thanks to
+         Andrew Bays. #2837
+       * tree-wide: Don't initialize static pointers to NULL, use "bool" from
+         "stdbool.h" (instead of "_Bool"). Thanks to Ruben Kerkhof. #2771,
+         #2772
+       * tree-wide: Replace zu with "PRIsz" and llu with "PRIu64". Thanks to
+         Sean Campbell. #2512
+       * tree-wide: Use interval value from plugin context, do not set
+         "vl->interval" in plugins more. Thanks to Pavel Rochnyak. #2847
+       * tree-wide: Utilities and libraries have been moved to "src/utils/".
+         Thanks to Florian Forster. #2961
+       * AMPQ1 plugin: A new plugin to write to amqp1 protocol. Thanks to Andy
+         Smith. #2618
+       * Chrony plugin: Ignoring late responses has been added. Thanks to
+         Miroslav Lichvar, Pavel Rochnyak. #2896
+       * CPUFreq plugin: Read number of p-state transitions and time spent in
+         each p-state. Thanks to Sexton Rory. #2803
+       * cURL, cURL-XML plugins: Option "Interval" has been added. Thanks to
+         Pavel Rochnyak. #2847
+       * Disk plugin: Report number of in progress disk IO requests on FreeBSD.
+         Thanks to Nathan Huff. #2878
+       * Exec plugin: Dynamic allocation of grname buffer has been added.
+         Thanks to sreedi, Florian Forster. #2937
+       * GPU NVML plugin: New plugin to collect NVIDIA GPU stats. Thanks to
+         Evgeny Naumov. #2923
+       * gRPC plugin: The "VerifyPeer" option for servers has been added.
+         Thanks to Florian Forster. #2593
+       * Intel RDT plugin: Support for groups of PIDs has been added. Thanks to
+         Wojciech Andralojc, Mateusz Starzyk, Michal Aleksinski. #2891
+       * IPMI plugin: Config options "SELSensor" and "SELIgnoreSelected" have
+         been added. Thanks to Mariusz Szafranski. #2796
+       * Modbus plugin: Support for 64 bit vals has been added, support for
+         CDAB endian 32-bit modbus polls has been added. Thanks to Anthony
+         Vickers, PJ Bostley. #2670, #2660
+       * Modbus plugin: The "Scale" and "Shift" metrics have been added. Thanks
+         to cekstam. #2729
+       * Netlink plugin: Handle new counter from Linux kernel version 4.6+.
+         Thanks to Pavel Rochnyak. #2767
+       * Network plugin: Option "BindAddress" has been added. Thanks to Ofir
+         Hermesh. #2831
+       * Ping plugin: An "AddressFamily" configuration option has been added.
+         Thanks to 依云 lilydjwg. #2961
+       * OVS Stats plugin: Extended metrics "ovs-dpdk" have been added. Thanks
+         to Matteo Croce, Ryan McCabe. #3000
+       * OVS Stats plugin: Support of bond interface and a "InterfaceStats"
+         config option have been added. Thanks to Andrew Bays. #2880
+       * PCIe Errors plugin: New plugin to read "PCIe" errors. Thanks to Kamil
+         Wiatrowski. #2733
+       * Processes plugin: Support for Linux Delay Accounting has been added.
+         Thanks to Florian Forster. #2598
+       * Redis plugin: Keyspace "hitratio" metric has been added, metric
+         "operations_per_second" has been removed, an option for connecting via
+         UNIX socket has been added. Thanks to Pavel Rochnyak. #2838, #2845,
+         #2904
+       * RouterOS plugin: Support for temperature and voltage data has been
+         added, use MAC-address when Radio-name is missing. Thanks to Pavel
+         Rochnyak. #2851, #2854
+       * RRDCacheD plugin: Time resolution has been improved to microseconds.
+         Thanks to Brian T. O'Neill. #3065
+       * Sensors plugin: Checks for upper limit of "SENSORS_API_VERSION" have
+         been removed, support for libsensors older than 3.0.0 has been
+         dropped. Thanks to Pavel Rochnyak. #3013, #3014
+       * SNMP plugin: New options "PluginInstance", "TypeInstance",
+         "TypeInstanceOID", "PluginInstanceOID", "FilterOID", "FilterValues"
+         and "FilterIgnoreSelected" have been added. Thanks to Pavel Rochnyak.
+         #2817, #2819
+       * SNMP Agent plugin: Multiple key indexes to snmp table and other new
+         features have been added, refactoring, coverity scan issues have been
+         fixed. Thanks to Marcin Mozejko. #2702, #2844
+       * Swap plugin: Support for Linux 2.4 has been dropped. Thanks to Pavel
+         Rochnyak. #2979
+       * Turbostat plugin: Configuration option "RestoreAffinityPolicy" has
+         been added. Thanks to Pablo Llopis. #2627
+       * Turbostat plugin: New metrics "P-states", "Turboboost", "Platform
+         TDP", "Uncore bus ratio" have been added. Thanks to Sexton Rory. #2806
+       * Turbostat plugin: Support of reporting GPU power on SKL has been
+         added. Thanks to Gordon Kelly. #2605
+       * virt plugin: Allow read "Hostname" from libvirt metadata. Thanks to
+         Mehdi ABAAKOUK. #2807
+       * virt plugin: Block info statistics for disk devices have been added.
+         Thanks to Radoslaw Jablonski. #2874
+       * Wireless plugin: A "bitrate" metric has been added. Thanks to Florian
+         Forster. #2950
+       * Write Graphite, Write Kafka plugins: Support for Graphite 1.1+ tag has
+         been added. Thanks to Dan Cech. #2631
+       * Write Prometheus plugin: Option "Host" has been added. Thanks to Pavel
+         Rochnyak. #2969
+       * Write Stackdriver plugin: New plugin to write to Google Stackdriver
+         Monitoring. Thanks to Florian Forster. #2472
+       * Write Syslog plugin: "write_syslog" plugin writes values lists as
+         syslog messages. Thanks to Shirly Radco. #3019
+       * Build System: A warning that pkgdatadir and pkglibdir were previously
+         defined has been fixed, additional plugins have been enabled,
+         GNULIB_DIR has been added to LDFLAGS in configure.ac on Windows.
+         Thanks to Sean Campbell. #2907, #2885, #2882
+       * Build System: Including "utils/mount/mount.h" has been fixed. Thanks
+         to Florian Forster. #3097
+       * Build System: The amount of output from ./configure has been reduced,
+         rendering of collectd-lua(5) manpage has been fixed, don't hide errors
+         when creating manpage. Thanks to Ruben Kerkhof. #3086, #3088, #3092
+       * collectd: A bug in "c_avl_iterator_prev" has been fixed. Thanks to
+         volth. #2917
+       * collectd: A stringop compiler warning has been fixed. Thanks to Ruben
+         Kerkhof, Juan Osorio Robles. #3021
+       * collectd: An invalid memory access in the "strjoin()" function has
+         been fixed. Thanks to Florian Forster. #3063
+       * collectd: collectd binary has been refactored. Thanks to Sean
+         Campbell, Sebastian Harl. #2745
+       * collectd: collectdmon cannot exit command line options parse loop has
+         been fixed. Thanks to takahashi-tsc. #2774
+       * collectd: Endianness checks for AIX have been added, gcc issue on Mac
+         byteorder has been fixed, fallback for endianness conversion has been
+         added. Thanks to Dagobert Michelsen. #2761, #2741, #2717
+       * collectd: Handle failure of simple config callbacks. Thanks to Ruben
+         Kerkhof. #3085
+       * collectd: Include "kstat.h" if available to provide "kstat_ctl_t",
+         include "kstat.h" when available. Thanks to Dagobert Michelsen. #2716,
+         #2711
+       * collectd: Parsing option for avoiding making BaseDir has been fixed.
+         Thanks to Mariusz Białończyk. #2856
+       * collectd: Remove empty "cmd_listval_t" data structure and related
+         no-op code. Thanks to Pavel Rochnyak. #2779
+       * collectd: src/daemon/plugin.c: Refactor plugin_load_file(),
+         src/utils_format_json.c: Remove chatty debug messages. Thanks to
+         Florian Forster. #2558, #2938
+       * collectd: Stop poisoning function in debug mode. Thanks to Ruben
+         Kerkhof. #2804
+       * collectd: The number of allocations when parsing types.db has been
+         reduced. Thanks to Ruben Kerkhof. #3091
+       * collectd: The organization of the source repository has been improved.
+         Thanks to Florian Forster. #2961
+       * collectd: Typos have been fixed. Thanks to Florian Forster, Jakub
+         Jankowski, William Pursell. #2944, #2692, #2643
+       * tree-wide: cleanup: cf_util_get* instead of local copy in plugins,
+         prefixed error reporting. Thanks to Pavel Rochnyak. #2833
+       * tree-wide: Some style issues have been fixed. Thanks to Ruben Kerkhof.
+         #3022
+       * tree-wide: "sstrerror()" has been replaced with "STRERRNO". Thanks to
+         Pavel Rochnyak. #2735
+       * AMQP1 plugin: Potential memory leaks found via scan-build have been
+         fixed, a typo in error log message has been fixed, cleanups. Thanks to
+         Andy Smith, Andrew Bays, Ruben Kerkhof. #2802, #2876, #2797
+       * Barometer plugin: Support to "libi2c-4.0" has been added. Thanks to
+         Pavel Rochnyak. #2783
+       * DBI, Oracle, PostgreSQL plugins: Fixes and improvements. Thanks to
+         Pavel Rochnyak. #1705
+       * Disk plugin: "HAVE_UDEV_H" has been changed to "HAVE_LIBUDEV_H".
+         Thanks to Dylan Stephano-Shachter. #2668
+       * Disk plugin: In linux, reset the disk when it disappears from
+         "/proc/diskstats". Thanks to Nikita Kozlov, Pavel Rochnyak. #2551
+       * DPDK Events, DPDK Stats plugins: Buffer size for parsing lcores has
+         been increased, a deprecation warning has been fixed, runtime config
+         file path has been fixed. Thanks to Kevin Laatz. #2722, #2840, #2924
+       * DPDK Stats plugin: A compilation issue has been fixed. Thanks to
+         Volodymyr Mytnyk. #2524
+       * GPS plugin: Build with gpsd version 3.18 has been fixed. Thanks to
+         Baruch Siach. #2947
+       * Intel RDT plugin: Compiler warnings have been fixed. Thanks to Ruben
+         Kerkhof. #3104
+       * Log Logstash plugin: Non-portable struct initialization with "{}" has
+         been fixed. Thanks to Florian Forster. #2988
+       * LUA plugin: A memory leak has been fixed. Thanks to Ruben Kerkhof.
+         #3090
+       * MySQL plugin: Properly cleanup dropped MySQL connections. Thanks to
+         Dhrupad Bhardwaj. #2704
+       * Netlink plugin: Truncation warnings have been fixed. Thanks to Ruben
+         Kerkhof. #2777
+       * NFS plugin: Message "Unexpected number of fields for NFSv4 server
+         statistics: 62" has been fixed. Thanks to Yedidyah Bar David. #2076
+       * NFS plugin: Number of fields for "NFSv4" has been fixed. Thanks to
+         Jan-Philipp Litza. #2915
+       * Notify Email plugin: All notification parameters have been included
+         into email. Thanks to Pavel Rochnyak. #2834
+       * NTPd plugin: Don't treat normal peers as refclocks, skip "0.0.0.0"
+         hosts in ntpd plugin. Thanks to Pavel Rochnyak, Ivan Kurnosov. #2822,
+         #2376
+       * OAuth plugin: src/utils_oauth.c: Renew OAuth tokens 30 seconds before
+         they expire. Thanks to Florian Forster. #2970
+       * OVS Stats plugin: A macro to populate counters list has been added,
+         value of "OpenFlow" has been corrected. Thanks to Matteo Croce. #2966,
+         #2963
+       * OVS Stats plugin: Code style, cleanup and improvements. Thanks to
+         Pavel Rochnyak. #3011, #3012
+       * OVS Stats, OVS Events plugins: utils_ovs: Avoid potential access of
+         freed memory, fixes. Thanks to Ciara Loftus, Mark Kavanagh. #2801,
+         #2731
+       * Processes plugin: Compilation has been fixed when ps_delay() is not
+         used. Thanks to Pavel Rochnyak. #2610
+       * Python plugin: A compilation warning with Python 3.7 has been fixed.
+         Thanks to Manoj Srivastava. #3042
+       * Redis plugin: Bugfixes, extended error reporting, persistent
+         connections and parallel polling, ability to select db for queries has
+         been fixed. Thanks to Pavel Rochnyak, skob. #2826, #2789
+       * Router OS plugin: Unset radio-name showing up as "(null)" has been
+         fixed. Thanks to melak. #2740
+       * RRDCacheD plugin: Cleanup rrdcached plugin a bit. Thanks to Pavel
+         Rochnyak. #3080
+       * RRDTool plugin: Error reporting has been extended. Thanks to Pavel
+         Rochnyak. #2825
+       * Sensors plugin: Support for humidity sensors has been added. Thanks to
+         Sarah Fischmann. #2913
+       * Sensu, OVS Stat, Turbostat, virt, OAuth, Write Prometheus, Intel RDT
+         plugins: Compiler warnings have been fixed. Thanks to Ruben Kerkhof.
+         #3093, #3098, #3099, #3100, #3102, #3103, #3104
+       * virt plugin: Code "do {} while(0)" around macro has been removed.
+         Thanks to Florian Forster. #2579
+       * virt plugin: Compiler warnings, a segfault in libvirt, typo in error
+         messages have been fixed. Thanks to Antoine Naud, Ruben Kerkhof, sarah
+         niuxu18. #2808, #2919, #2957
+       * virt plugin: Optional "virDomainGetCPUStats()" has been removed from
+         main flow, cleanup. Thanks to Pavel Rochnyak. #2972, #2978
+       * virt plugin: Tracking of VM state changes has been fixed. Thanks to
+         Radoslaw Jablonski. #2701
+       * Write MongoDB plugin: Plugin dependencies have been fixed. Thanks to
+         Pavel Rochnyak. #3010
+       * Write Prometheus plugin: A compilation issue on Mac OS X has been
+         fixed. Thanks to Florian Forster. #3059
+       * Write Redis plugin: Bug ""max_set_duration" deletes unexpected data"
+         has been fixed. Thanks to takahashi-tsc. #2773
+       * Write Stackdriver plugin: Potential NULL dereference and error
+         reporting have been fixed. Thanks to Florian Forster. #2960
+       * collectd.conf(5): a typo has been fixed, the tail plugin's
+         documentation has been improved. Thanks to Ruben Kerkhof, Florian
+         Forster. #3087, #2994
+       * collectd.conf.pod: virt: "Instances" option has been documented, a
+         clarifying example has been added. Thanks to Pavel Rochnyak, Fabien
+         Wernli. #2990, #2903
+       * collectd-python: "Import" configuration option has been documented.
+         Thanks to Tyler Harper. #2985
+       * collectd-snmp.pod: Document thread usage correctly. Thanks to Nathan
+         Ward. #3078
+       * CONTRIBUTING.md: Improve wording around ChangeLog; fix example,
+         document the new change log requirement / behavior. Thanks to Florian
+         Forster. #3061, #3054
+       * docs/review_comments.md: document with frequent review comments has
+         been started. Thanks to Florian Forster. #2964
+       * README: Include compiler defenses suggestion, do not point users to
+         non-existing file. Thanks to Kevin Laatz, Ruben Kerkhof. #2721, #2045
+
 2018-10-23, Version 5.8.1
        * collectd: Fix "BaseDir" option. Thanks to Mariusz Białończyk and
          Pavel Rochnyak. #2857
index 5470b9b..7795fd3 100644 (file)
@@ -774,6 +774,15 @@ check_uptime_la_SOURCES = src/check_uptime.c
 check_uptime_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 endif
 
+if BUILD_PLUGIN_CONNECTIVITY
+pkglib_LTLIBRARIES += connectivity.la
+connectivity_la_SOURCES = src/connectivity.c
+connectivity_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
+connectivity_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBYAJL_CPPFLAGS)
+connectivity_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBYAJL_LDFLAGS)
+connectivity_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS) $(BUILD_WITH_LIBMNL_LIBS) libignorelist.la
+endif
+
 if BUILD_PLUGIN_CONNTRACK
 pkglib_LTLIBRARIES += conntrack.la
 conntrack_la_SOURCES = src/conntrack.c
@@ -1213,14 +1222,6 @@ lua_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 lua_la_LIBADD = $(BUILD_WITH_LIBLUA_LIBS)
 endif
 
-if BUILD_PLUGIN_LVM
-pkglib_LTLIBRARIES += lvm.la
-lvm_la_SOURCES = src/lvm.c
-lvm_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBLVM2APP_CPPFLAGS)
-lvm_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBLVM2APP_LDFLAGS)
-lvm_la_LIBADD = $(BUILD_WITH_LIBLVM2APP_LIBS)
-endif
-
 if BUILD_PLUGIN_MADWIFI
 pkglib_LTLIBRARIES += madwifi.la
 madwifi_la_SOURCES = \
@@ -1411,6 +1412,12 @@ test_plugin_network_LDADD = \
        libplugin_mock.la \
        libmetadata.la \
        $(GCRYPT_LIBS)
+if BUILD_WITH_LIBSOCKET
+test_plugin_network_LDADD += -lsocket
+endif
+if BUILD_WITH_LIBNSL
+test_plugin_network_LDADD += -lnsl
+endif
 check_PROGRAMS += test_plugin_network
 endif
 
diff --git a/README b/README
index dd7a437..c8be7d3 100644 (file)
--- a/README
+++ b/README
@@ -54,6 +54,9 @@ Features
     - chrony
       Chrony daemon statistics: Local clock drift, offset to peers, etc.
 
+    - connectivity
+      Event-based interface status.
+
     - conntrack
       Number of nf_conntrack entries.
 
@@ -204,10 +207,6 @@ Features
       collectd without the need to start a heavy interpreter every interval.
       See collectd-lua(5) for details.
 
-    - lvm
-      Size of “Logical Volumes” (LV) and “Volume Groups” (VG) of Linux'
-      “Logical Volume Manager” (LVM).
-
     - madwifi
       Queries very detailed usage statistics from wireless LAN adapters and
       interfaces that use the Atheros chipset and the MadWifi driver.
@@ -842,10 +841,6 @@ Prerequisites
     Used by the `lua' plugin. Currently, Lua 5.1 and later are supported.
     <https://www.lua.org/>
 
-  * liblvm2 (optional)
-    Used by the `lvm' plugin.
-    <ftp://sources.redhat.com/pub/lvm2/>
-
   * libmemcached (optional)
     Used by the `memcachec' plugin to connect to a memcache daemon.
     <http://tangent.org/552/libmemcached.html>
index 9ff6408..34386d0 100644 (file)
@@ -743,10 +743,10 @@ test_cxx_flags() {
 #
 AC_CHECK_FUNCS_ONCE([ \
     asprintf \
+    execvpe \
     getpwnam \
     getpwnam_r \
     if_indextoname \
-    setenv \
     setgroups \
     setlocale
   ]
@@ -858,6 +858,17 @@ AC_CHECK_FUNCS([socket],
 AM_CONDITIONAL([BUILD_WITH_LIBSOCKET], [test "x$socket_needs_socket" = "xyes"])
 AM_CONDITIONAL([BUILD_WITH_GNULIB], [test "x$socket_needs_gnulib" = "xyes"])
 
+AC_CHECK_FUNCS([inet_ntop],
+  [],
+  [
+    AC_CHECK_LIB([nsl], [inet_ntop],
+      [inet_ntop_needs_nsl="yes"],
+      [AC_MSG_ERROR([cannot find inet_ntop() in libnsl])]
+    )
+  ]
+)
+AM_CONDITIONAL([BUILD_WITH_LIBNSL], [test "x$inet_ntop_needs_nsl" = "xyes"])
+
 clock_gettime_needs_posix4="no"
 AC_CHECK_FUNCS([clock_gettime],
   [have_clock_gettime="yes"],
@@ -3158,67 +3169,6 @@ AC_SUBST(BUILD_WITH_LIBLUA_CFLAGS)
 AC_SUBST(BUILD_WITH_LIBLUA_LIBS)
 # }}}
 
-# --with-liblvm2app {{{
-AC_ARG_WITH([liblvm2app],
-  [AS_HELP_STRING([--with-liblvm2app@<:@=PREFIX@:>@], [Path to liblvm2app.])],
-  [
-    if test "x$withval" = "xno"; then
-      with_liblvm2app="no"
-    else
-      with_liblvm2app="yes"
-      if test "x$withval" != "xyes"; then
-        with_liblvm2app_cppflags="-I$withval/include"
-        with_liblvm2app_ldflags="-L$withval/lib"
-      fi
-    fi
-  ],
-  [
-    if test "x$ac_system" = "xLinux"; then
-      with_liblvm2app="yes"
-    else
-      with_liblvm2app="no (Linux only library)"
-    fi
-  ]
-)
-
-if test "x$with_liblvm2app" = "xyes"; then
-  SAVE_CPPFLAGS="$CPPFLAGS"
-  CPPFLAGS="$CPPFLAGS $with_liblvm2app_cppflags"
-
-  AC_CHECK_HEADERS([lvm2app.h],
-    [with_liblvm2app="yes"],
-    [with_liblvm2app="no (lvm2app.h not found)"]
-  )
-
-  CPPFLAGS="$SAVE_CPPFLAGS"
-fi
-
-if test "x$with_liblvm2app" = "xyes"; then
-  SAVE_CPPFLAGS="$CPPFLAGS"
-  SAVE_LDFLAGS="$LDFLAGS"
-  CPPFLAGS="$CPPFLAGS $with_liblvm2app_cppflags"
-  LDFLAGS="$LDFLAGS $with_liblvm2app_ldflags"
-
-  AC_CHECK_LIB([lvm2app], [lvm_lv_get_property],
-    [with_liblvm2app="yes"],
-    [with_liblvm2app="no (Symbol 'lvm_lv_get_property' not found)"]
-  )
-
-  CPPFLAGS="$SAVE_CPPFLAGS"
-  LDFLAGS="$SAVE_LDFLAGS"
-fi
-
-if test "x$with_liblvm2app" = "xyes"; then
-  BUILD_WITH_LIBLVM2APP_CPPFLAGS="$with_liblvm2app_cppflags"
-  BUILD_WITH_LIBLVM2APP_LDFLAGS="$with_liblvm2app_ldflags"
-  BUILD_WITH_LIBLVM2APP_LIBS="-llvm2app"
-fi
-
-AC_SUBST([BUILD_WITH_LIBLVM2APP_CPPFLAGS])
-AC_SUBST([BUILD_WITH_LIBLVM2APP_LDFLAGS])
-AC_SUBST([BUILD_WITH_LIBLVM2APP_LIBS])
-# }}}
-
 # --with-libmemcached {{{
 AC_ARG_WITH([libmemcached],
   [AS_HELP_STRING([--with-libmemcached@<:@=PREFIX@:>@], [Path to libmemcached.])],
@@ -6362,6 +6312,7 @@ plugin_battery="no"
 plugin_bind="no"
 plugin_ceph="no"
 plugin_cgroups="no"
+plugin_connectivity="no"
 plugin_conntrack="no"
 plugin_contextswitch="no"
 plugin_cpu="no"
@@ -6446,7 +6397,6 @@ if test "x$ac_system" = "xLinux"; then
   plugin_ipc="yes"
   plugin_irq="yes"
   plugin_load="yes"
-  plugin_lvm="yes"
   plugin_mcelog="yes"
   plugin_memory="yes"
   plugin_nfs="yes"
@@ -6480,6 +6430,10 @@ if test "x$ac_system" = "xLinux"; then
     plugin_ovs_events="yes"
     plugin_ovs_stats="yes"
     plugin_procevent="yes"
+
+    if test "x$with_libmnl" = "xyes"; then
+      plugin_connectivity="yes"
+    fi
   fi
 
   if test "x$have_pci_regs_h" = "xyes"; then
@@ -6791,6 +6745,7 @@ AC_PLUGIN([ceph],                [$plugin_ceph],              [Ceph daemon stati
 AC_PLUGIN([cgroups],             [$plugin_cgroups],           [CGroups CPU usage accounting])
 AC_PLUGIN([chrony],              [yes],                       [Chrony statistics])
 AC_PLUGIN([check_uptime],        [yes],                       [Notify about uptime reset])
+AC_PLUGIN([connectivity],        [$plugin_connectivity],      [Network interface up/down events])
 AC_PLUGIN([conntrack],           [$plugin_conntrack],         [nf_conntrack statistics])
 AC_PLUGIN([contextswitch],       [$plugin_contextswitch],     [context switch statistics])
 AC_PLUGIN([cpu],                 [$plugin_cpu],               [CPU usage statistics])
@@ -6834,7 +6789,6 @@ AC_PLUGIN([log_logstash],        [$plugin_log_logstash],      [Logstash json_eve
 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])
@@ -7153,7 +7107,6 @@ AC_MSG_RESULT([    libkstat  . . . . . . $with_kstat])
 AC_MSG_RESULT([    libkvm  . . . . . . . $with_libkvm])
 AC_MSG_RESULT([    libldap . . . . . . . $with_libldap])
 AC_MSG_RESULT([    liblua  . . . . . . . $with_liblua])
-AC_MSG_RESULT([    liblvm2app  . . . . . $with_liblvm2app])
 AC_MSG_RESULT([    libmemcached  . . . . $with_libmemcached])
 AC_MSG_RESULT([    libmicrohttpd . . . . $with_libmicrohttpd])
 AC_MSG_RESULT([    libmnl  . . . . . . . $with_libmnl])
@@ -7223,6 +7176,7 @@ AC_MSG_RESULT([    ceph  . . . . . . . . $enable_ceph])
 AC_MSG_RESULT([    cgroups . . . . . . . $enable_cgroups])
 AC_MSG_RESULT([    chrony. . . . . . . . $enable_chrony])
 AC_MSG_RESULT([    check_uptime. . . . . $enable_check_uptime])
+AC_MSG_RESULT([    connectivity. . . . . $enable_connectivity])
 AC_MSG_RESULT([    conntrack . . . . . . $enable_conntrack])
 AC_MSG_RESULT([    contextswitch . . . . $enable_contextswitch])
 AC_MSG_RESULT([    cpu . . . . . . . . . $enable_cpu])
@@ -7266,7 +7220,6 @@ AC_MSG_RESULT([    logfile . . . . . . . $enable_logfile])
 AC_MSG_RESULT([    log_logstash  . . . . $enable_log_logstash])
 AC_MSG_RESULT([    lpar  . . . . . . . . $enable_lpar])
 AC_MSG_RESULT([    lua . . . . . . . . . $enable_lua])
-AC_MSG_RESULT([    lvm . . . . . . . . . $enable_lvm])
 AC_MSG_RESULT([    madwifi . . . . . . . $enable_madwifi])
 AC_MSG_RESULT([    match_empty_counter . $enable_match_empty_counter])
 AC_MSG_RESULT([    match_hashed  . . . . $enable_match_hashed])
index 8bde54c..864ef1b 100644 (file)
@@ -53,6 +53,7 @@
 %define with_ceph 0%{!?_without_ceph:1}
 %define with_cgroups 0%{!?_without_cgroups:1}
 %define with_chrony 0%{!?_without_chrony:1}
+%define with_connectivity 0%{!?_without_connectivity:1}
 %define with_conntrack 0%{!?_without_conntrack:1}
 %define with_contextswitch 0%{!?_without_contextswitch:1}
 %define with_cpu 0%{!?_without_cpu:1}
@@ -89,7 +90,6 @@
 %define with_log_logstash 0%{!?_without_log_logstash:1}
 %define with_logfile 0%{!?_without_logfile:1}
 %define with_lua 0%{!?_without_lua:1}
-%define with_lvm 0%{!?_without_lvm:1}
 %define with_madwifi 0%{!?_without_madwifi:1}
 %define with_mbmon 0%{!?_without_mbmon:1}
 %define with_mcelog 0%{!?_without_mcelog:1}
 %define with_gmond 0
 %define with_iptables 0
 %define with_ipvs 0
-%define with_lvm 0
 %define with_modbus 0
 %define with_netlink 0
 %define with_redis 0
 
 # Plugins not buildable on RHEL < 7
 %if 0%{?rhel} && 0%{?rhel} < 7
+%define with_connectivity 0
 %define with_cpusleep 0
 %define with_gps 0
 %define with_mqtt 0
 
 Summary:       Statistics collection and monitoring daemon
 Name:          collectd
-Version:       5.9.0
-Release:       1%{?dist}
+Version:       5.9.2
+Release:       2%{?dist}
 URL:           https://collectd.org
 Source:                https://collectd.org/files/%{name}-%{version}.tar.bz2
 License:       GPLv2
@@ -384,6 +384,16 @@ Requires:      %{name}%{?_isa} = %{version}-%{release}
 Chrony plugin for collectd
 %endif
 
+%if %{with_connectivity}
+%package connectivity
+Summary:       Connectivity plugin for collectd
+Group:         System Environment/Daemons
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: libmnl-devel, yajl-devel
+%description connectivity
+Monitors network interface up/down status via netlink library.
+%endif
+
 %if %{with_curl}
 %package curl
 Summary:       Curl plugin for collectd
@@ -578,17 +588,6 @@ The Lua plugin embeds a Lua interpreter into collectd and exposes the
 application programming interface (API) to Lua scripts.
 %endif
 
-%if %{with_lvm}
-%package lvm
-Summary:       LVM plugin for collectd
-Group:         System Environment/Daemons
-Requires:      %{name}%{?_isa} = %{version}-%{release}
-BuildRequires: lvm2-devel
-%description lvm
-This plugin collects size of “Logical Volumes” (LV) and “Volume Groups” (VG)
-of Linux' “Logical Volume Manager” (LVM).
-%endif
-
 %if %{with_mcelog}
 %package mcelog
 Summary:       Mcelog plugin for collectd
@@ -1160,6 +1159,12 @@ Collectd utilities
 %define _with_chrony --disable-chrony
 %endif
 
+%if %{with_connectivity}
+%define _with_connectivity --enable-connectivity
+%else
+%define _with_connectivity --disable-connectivity
+%endif
+
 %if %{with_conntrack}
 %define _with_conntrack --enable-conntrack
 %else
@@ -1424,12 +1429,6 @@ Collectd utilities
 %define _with_lua --disable-lua
 %endif
 
-%if %{with_lvm}
-%define _with_lvm --enable-lvm
-%else
-%define _with_lvm --disable-lvm
-%endif
-
 %if %{with_madwifi}
 %define _with_madwifi --enable-madwifi
 %else
@@ -2028,6 +2027,7 @@ Collectd utilities
        %{?_with_ceph} \
        %{?_with_cgroups} \
        %{?_with_chrony} \
+       %{?_with_connectivity} \
        %{?_with_conntrack} \
        %{?_with_contextswitch} \
        %{?_with_cpufreq} \
@@ -2070,7 +2070,6 @@ Collectd utilities
        %{?_with_logfile} \
        %{?_with_lpar} \
        %{?_with_lua} \
-       %{?_with_lvm} \
        %{?_with_madwifi} \
        %{?_with_mbmon} \
        %{?_with_mcelog} \
@@ -2577,6 +2576,11 @@ fi
 %{_libdir}/%{name}/chrony.so
 %endif
 
+%if %{with_connectivity}
+%files connectivity
+%{_libdir}/%{name}/connectivity.so
+%endif
+
 %if %{with_curl}
 %files curl
 %{_libdir}/%{name}/curl.so
@@ -2686,11 +2690,6 @@ fi
 %{_libdir}/%{name}/lua.so
 %endif
 
-%if %{with_lvm}
-%files lvm
-%{_libdir}/%{name}/lvm.so
-%endif
-
 %if %{with_memcachec}
 %files memcachec
 %{_libdir}/%{name}/memcachec.so
@@ -2905,6 +2904,9 @@ fi
 %doc contrib/
 
 %changelog
+* Mon Oct 14 2019 Ruben Kerkhof <ruben@rubenkerkhof.com> - 5.9.2-2
+- Remove lvm plugin, liblvmapp has been deprecated upstream
+
 * 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)
index f8c4808..dcad150 100644 (file)
 #@BUILD_PLUGIN_CGROUPS_TRUE@LoadPlugin cgroups
 #@BUILD_PLUGIN_CHRONY_TRUE@LoadPlugin chrony
 #@BUILD_PLUGIN_CHECK_UPTIME_TRUE@LoadPlugin check_uptime
+#@BUILD_PLUGIN_CONNECTIVITY_TRUE@LoadPlugin connectivity
 #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack
 #@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 @BUILD_PLUGIN_LOAD_TRUE@@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load
 #@BUILD_PLUGIN_LPAR_TRUE@LoadPlugin lpar
 #@BUILD_PLUGIN_LUA_TRUE@LoadPlugin lua
-#@BUILD_PLUGIN_LVM_TRUE@LoadPlugin lvm
 #@BUILD_PLUGIN_MADWIFI_TRUE@LoadPlugin madwifi
 #@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon
 #@BUILD_PLUGIN_MCELOG_TRUE@LoadPlugin mcelog
 #      Timeout "2"
 #</Plugin>
 
+#<Plugin connectivity>
+#  Interface eth0
+#</Plugin>
+
 #<Plugin cgroups>
 #  CGroup "libvirt"
 #  IgnoreSelected false
index 8dff63c..cda1002 100644 (file)
@@ -1603,6 +1603,47 @@ Connection timeout in seconds. Defaults to B<2>.
 
 =back
 
+=head2 Plugin Connectivity
+
+connectivity - Documentation of collectd's C<connectivity plugin>
+
+
+  LoadPlugin connectivity
+  # ...
+  <Plugin connectivity>
+    Interface eth0
+  </Plugin>
+
+The C<connectivity plugin> queries interface status using netlink (man 7 netlink) which provides information about network interfaces via the NETLINK_ROUTE family (man 7 rtnetlink). The plugin translates the value it receives to collectd's internal format and, depending on the write plugins you have loaded, it may be written to disk or submitted to another instance.
+The plugin listens to interfaces enumerated within the plugin configuration (see below).  If no interfaces are listed, then the default is for all interfaces to be monitored.
+
+This example shows C<connectivity plugin> monitoring all interfaces.
+LoadPlugin connectivity
+<Plugin connectivity>
+</Plugin>
+
+This example shows C<connectivity plugin> monitoring 2 interfaces, "eth0" and "eth1".
+LoadPlugin connectivity
+<Plugin connectivity>
+  Interface eth0
+  Interface eth1
+</Plugin>
+
+This example shows C<connectivity plugin> monitoring all interfaces except "eth1".
+LoadPlugin connectivity
+<Plugin connectivity>
+  Interface eth1
+  IgnoreSelected true
+</Plugin>
+=over 4
+
+=item B<Interface> I<interface_name>
+
+interface(s) to monitor connect to. 
+
+=back
+
 =head2 Plugin C<conntrack>
 
 This plugin collects IP conntrack statistics.
@@ -4787,7 +4828,7 @@ Hostname of the database server. Defaults to B<localhost>.
 
 Username to use when connecting to the database. The user does not have to be
 granted any privileges (which is synonym to granting the C<USAGE> privilege),
-unless you want to collectd replication statistics (see B<MasterStats> and
+unless you want to collect replication statistics (see B<MasterStats> and
 B<SlaveStats> below). In this case, the user needs the C<REPLICATION CLIENT>
 (or C<SUPER>) privileges. Else, any existing MySQL user will do.
 
@@ -4837,9 +4878,10 @@ or SQL threads are not running. Defaults to B<false>.
 
 =item B<WsrepStats> I<true|false>
 
- Enable the collection of wsrep plugin statistics, used in Master-Master
- replication setups like in MySQL Galera/Percona XtraDB Cluster.
- User needs only privileges to execute 'SHOW GLOBAL STATUS'
+Enable the collection of wsrep plugin statistics, used in Master-Master
+replication setups like in MySQL Galera/Percona XtraDB Cluster.
+User needs only privileges to execute 'SHOW GLOBAL STATUS'.
+Defaults to B<false>.
 
 =item B<ConnectTimeout> I<Seconds>
 
diff --git a/src/connectivity.c b/src/connectivity.c
new file mode 100644 (file)
index 0000000..45b65aa
--- /dev/null
@@ -0,0 +1,1032 @@
+/**
+ * collectd - src/connectivity.c
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Red Hat NFVPE
+ *     Andrew Bays <abays at redhat.com>
+ *     Aneesh Puttur <aputtur at redhat.com>
+ **/
+
+#include "collectd.h"
+
+#include "plugin.h"
+#include "utils/common/common.h"
+#include "utils/ignorelist/ignorelist.h"
+#include "utils_complain.h"
+
+#include <asm/types.h>
+#include <errno.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#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
+
+#define MYPROTO NETLINK_ROUTE
+
+#define LINK_STATE_DOWN 0
+#define LINK_STATE_UP 1
+#define LINK_STATE_UNKNOWN 2
+
+#define CONNECTIVITY_DOMAIN_FIELD "domain"
+#define CONNECTIVITY_DOMAIN_VALUE "stateChange"
+#define CONNECTIVITY_EVENT_ID_FIELD "eventId"
+#define CONNECTIVITY_EVENT_NAME_FIELD "eventName"
+#define CONNECTIVITY_EVENT_NAME_DOWN_VALUE "down"
+#define CONNECTIVITY_EVENT_NAME_UP_VALUE "up"
+#define CONNECTIVITY_LAST_EPOCH_MICROSEC_FIELD "lastEpochMicrosec"
+#define CONNECTIVITY_PRIORITY_FIELD "priority"
+#define CONNECTIVITY_PRIORITY_VALUE "high"
+#define CONNECTIVITY_REPORTING_ENTITY_NAME_FIELD "reportingEntityName"
+#define CONNECTIVITY_REPORTING_ENTITY_NAME_VALUE "collectd connectivity plugin"
+#define CONNECTIVITY_SEQUENCE_FIELD "sequence"
+#define CONNECTIVITY_SEQUENCE_VALUE "0"
+#define CONNECTIVITY_SOURCE_NAME_FIELD "sourceName"
+#define CONNECTIVITY_START_EPOCH_MICROSEC_FIELD "startEpochMicrosec"
+#define CONNECTIVITY_VERSION_FIELD "version"
+#define CONNECTIVITY_VERSION_VALUE "1.0"
+
+#define CONNECTIVITY_NEW_STATE_FIELD "newState"
+#define CONNECTIVITY_NEW_STATE_FIELD_DOWN_VALUE "outOfService"
+#define CONNECTIVITY_NEW_STATE_FIELD_UP_VALUE "inService"
+#define CONNECTIVITY_OLD_STATE_FIELD "oldState"
+#define CONNECTIVITY_OLD_STATE_FIELD_DOWN_VALUE "outOfService"
+#define CONNECTIVITY_OLD_STATE_FIELD_UP_VALUE "inService"
+#define CONNECTIVITY_STATE_CHANGE_FIELDS_FIELD "stateChangeFields"
+#define CONNECTIVITY_STATE_CHANGE_FIELDS_VERSION_FIELD                         \
+  "stateChangeFieldsVersion"
+#define CONNECTIVITY_STATE_CHANGE_FIELDS_VERSION_VALUE "1.0"
+#define CONNECTIVITY_STATE_INTERFACE_FIELD "stateInterface"
+
+/*
+ * Private data types
+ */
+
+struct interface_list_s {
+  char *interface;
+
+  uint32_t status;
+  uint32_t prev_status;
+  uint32_t sent;
+  cdtime_t timestamp;
+
+  struct interface_list_s *next;
+};
+typedef struct interface_list_s interface_list_t;
+
+/*
+ * Private variables
+ */
+
+static ignorelist_t *ignorelist = NULL;
+
+static interface_list_t *interface_list_head = NULL;
+static int monitor_all_interfaces = 1;
+
+static int connectivity_netlink_thread_loop = 0;
+static int connectivity_netlink_thread_error = 0;
+static pthread_t connectivity_netlink_thread_id;
+static int connectivity_dequeue_thread_loop = 0;
+static pthread_t connectivity_dequeue_thread_id;
+static pthread_mutex_t connectivity_threads_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t connectivity_data_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t connectivity_cond = PTHREAD_COND_INITIALIZER;
+static int nl_sock = -1;
+static int event_id = 0;
+static int statuses_to_send = 0;
+
+static const char *config_keys[] = {"Interface", "IgnoreSelected"};
+static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
+
+/*
+ * Private functions
+ */
+
+static int gen_message_payload(int state, int old_state, const char *interface,
+                               cdtime_t timestamp, char **buf) {
+  const unsigned char *buf2;
+  yajl_gen g;
+  char json_str[DATA_MAX_NAME_LEN];
+
+#if !defined(HAVE_YAJL_V2)
+  yajl_gen_config conf = {0};
+#endif
+
+#if HAVE_YAJL_V2
+  size_t len;
+  g = yajl_gen_alloc(NULL);
+  yajl_gen_config(g, yajl_gen_beautify, 0);
+#else
+  unsigned int len;
+  g = yajl_gen_alloc(&conf, NULL);
+#endif
+
+  yajl_gen_clear(g);
+
+  // *** BEGIN common event header ***
+
+  if (yajl_gen_map_open(g) != yajl_gen_status_ok)
+    goto err;
+
+  // domain
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_DOMAIN_FIELD,
+                      strlen(CONNECTIVITY_DOMAIN_FIELD)) != yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_DOMAIN_VALUE,
+                      strlen(CONNECTIVITY_DOMAIN_VALUE)) != yajl_gen_status_ok)
+    goto err;
+
+  // eventId
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_EVENT_ID_FIELD,
+                      strlen(CONNECTIVITY_EVENT_ID_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  event_id = event_id + 1;
+  if (snprintf(json_str, sizeof(json_str), "%d", event_id) < 0) {
+    goto err;
+  }
+
+  if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
+    goto err;
+  }
+
+  // eventName
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_EVENT_NAME_FIELD,
+                      strlen(CONNECTIVITY_EVENT_NAME_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (snprintf(json_str, sizeof(json_str), "interface %s %s", interface,
+               (state == 0 ? CONNECTIVITY_EVENT_NAME_DOWN_VALUE
+                           : CONNECTIVITY_EVENT_NAME_UP_VALUE)) < 0) {
+    goto err;
+  }
+
+  if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
+      yajl_gen_status_ok) {
+    goto err;
+  }
+
+  // lastEpochMicrosec
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_LAST_EPOCH_MICROSEC_FIELD,
+                      strlen(CONNECTIVITY_LAST_EPOCH_MICROSEC_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (snprintf(json_str, sizeof(json_str), "%" PRIu64,
+               CDTIME_T_TO_US(cdtime())) < 0) {
+    goto err;
+  }
+
+  if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
+    goto err;
+  }
+
+  // priority
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_PRIORITY_FIELD,
+                      strlen(CONNECTIVITY_PRIORITY_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_PRIORITY_VALUE,
+                      strlen(CONNECTIVITY_PRIORITY_VALUE)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // reportingEntityName
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_REPORTING_ENTITY_NAME_FIELD,
+                      strlen(CONNECTIVITY_REPORTING_ENTITY_NAME_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_REPORTING_ENTITY_NAME_VALUE,
+                      strlen(CONNECTIVITY_REPORTING_ENTITY_NAME_VALUE)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // sequence
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_SEQUENCE_FIELD,
+                      strlen(CONNECTIVITY_SEQUENCE_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_number(g, CONNECTIVITY_SEQUENCE_VALUE,
+                      strlen(CONNECTIVITY_SEQUENCE_VALUE)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // sourceName
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_SOURCE_NAME_FIELD,
+                      strlen(CONNECTIVITY_SOURCE_NAME_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)interface, strlen(interface)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // startEpochMicrosec
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_START_EPOCH_MICROSEC_FIELD,
+                      strlen(CONNECTIVITY_START_EPOCH_MICROSEC_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (snprintf(json_str, sizeof(json_str), "%" PRIu64,
+               CDTIME_T_TO_US(timestamp)) < 0) {
+    goto err;
+  }
+
+  if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
+    goto err;
+  }
+
+  // version
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_VERSION_FIELD,
+                      strlen(CONNECTIVITY_VERSION_FIELD)) != yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_number(g, CONNECTIVITY_VERSION_VALUE,
+                      strlen(CONNECTIVITY_VERSION_VALUE)) != yajl_gen_status_ok)
+    goto err;
+
+  // *** END common event header ***
+
+  // *** BEGIN state change fields ***
+
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_STATE_CHANGE_FIELDS_FIELD,
+                      strlen(CONNECTIVITY_STATE_CHANGE_FIELDS_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_map_open(g) != yajl_gen_status_ok)
+    goto err;
+
+  // newState
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_NEW_STATE_FIELD,
+                      strlen(CONNECTIVITY_NEW_STATE_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  int new_state_len =
+      (state == 0 ? strlen(CONNECTIVITY_NEW_STATE_FIELD_DOWN_VALUE)
+                  : strlen(CONNECTIVITY_NEW_STATE_FIELD_UP_VALUE));
+
+  if (yajl_gen_string(g,
+                      (u_char *)(state == 0
+                                     ? CONNECTIVITY_NEW_STATE_FIELD_DOWN_VALUE
+                                     : CONNECTIVITY_NEW_STATE_FIELD_UP_VALUE),
+                      new_state_len) != yajl_gen_status_ok)
+    goto err;
+
+  // oldState
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_OLD_STATE_FIELD,
+                      strlen(CONNECTIVITY_OLD_STATE_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  int old_state_len =
+      (old_state == 0 ? strlen(CONNECTIVITY_OLD_STATE_FIELD_DOWN_VALUE)
+                      : strlen(CONNECTIVITY_OLD_STATE_FIELD_UP_VALUE));
+
+  if (yajl_gen_string(g,
+                      (u_char *)(old_state == 0
+                                     ? CONNECTIVITY_OLD_STATE_FIELD_DOWN_VALUE
+                                     : CONNECTIVITY_OLD_STATE_FIELD_UP_VALUE),
+                      old_state_len) != yajl_gen_status_ok)
+    goto err;
+
+  // stateChangeFieldsVersion
+  if (yajl_gen_string(g,
+                      (u_char *)CONNECTIVITY_STATE_CHANGE_FIELDS_VERSION_FIELD,
+                      strlen(CONNECTIVITY_STATE_CHANGE_FIELDS_VERSION_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_number(g, CONNECTIVITY_STATE_CHANGE_FIELDS_VERSION_VALUE,
+                      strlen(CONNECTIVITY_STATE_CHANGE_FIELDS_VERSION_VALUE)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // stateInterface
+  if (yajl_gen_string(g, (u_char *)CONNECTIVITY_STATE_INTERFACE_FIELD,
+                      strlen(CONNECTIVITY_STATE_INTERFACE_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)interface, strlen(interface)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // close state change and header fields
+  if (yajl_gen_map_close(g) != yajl_gen_status_ok ||
+      yajl_gen_map_close(g) != yajl_gen_status_ok)
+    goto err;
+
+  // *** END state change fields ***
+
+  if (yajl_gen_get_buf(g, &buf2, &len) != yajl_gen_status_ok)
+    goto err;
+
+  *buf = strdup((char *)buf2);
+
+  if (*buf == NULL) {
+    ERROR("connectivity plugin: strdup failed during gen_message_payload: %s",
+          STRERRNO);
+    goto err;
+  }
+
+  yajl_gen_free(g);
+
+  return 0;
+
+err:
+  yajl_gen_free(g);
+  ERROR("connectivity plugin: gen_message_payload failed to generate JSON");
+  return -1;
+}
+
+static interface_list_t *add_interface(const char *interface, int status,
+                                       int prev_status) {
+  interface_list_t *il = calloc(1, sizeof(*il));
+
+  if (il == NULL) {
+    ERROR("connectivity plugin: calloc failed during add_interface: %s",
+          STRERRNO);
+    return NULL;
+  }
+
+  char *interface2 = strdup(interface);
+  if (interface2 == NULL) {
+    sfree(il);
+    ERROR("connectivity plugin: strdup failed during add_interface: %s",
+          STRERRNO);
+    return NULL;
+  }
+
+  il->interface = interface2;
+  il->status = status;
+  il->prev_status = prev_status;
+  il->timestamp = cdtime();
+  il->sent = 0;
+  il->next = interface_list_head;
+  interface_list_head = il;
+
+  DEBUG("connectivity plugin: added interface %s", interface2);
+
+  return il;
+}
+
+static int connectivity_link_state(struct nlmsghdr *msg) {
+  pthread_mutex_lock(&connectivity_data_lock);
+
+  struct nlattr *attr;
+  struct ifinfomsg *ifi = mnl_nlmsg_get_payload(msg);
+
+  /* Scan attribute list for device name. */
+  mnl_attr_for_each(attr, msg, sizeof(*ifi)) {
+    if (mnl_attr_get_type(attr) != IFLA_IFNAME)
+      continue;
+
+    if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+      ERROR("connectivity plugin: connectivity_link_state: IFLA_IFNAME "
+            "mnl_attr_validate "
+            "failed.");
+      pthread_mutex_unlock(&connectivity_data_lock);
+      return MNL_CB_ERROR;
+    }
+
+    const char *dev = mnl_attr_get_str(attr);
+
+    // Check the list of interfaces we should monitor, if we've chosen
+    // a subset.  If we don't care about this one, abort.
+    if (ignorelist_match(ignorelist, dev) != 0) {
+      DEBUG("connectivity plugin: Ignoring link state change for unmonitored "
+            "interface: %s",
+            dev);
+      break;
+    }
+
+    interface_list_t *il = NULL;
+
+    for (il = interface_list_head; il != NULL; il = il->next)
+      if (strcmp(dev, il->interface) == 0)
+        break;
+
+    if (il == NULL) {
+      // We haven't encountered this interface yet, so add it to the linked list
+      il = add_interface(dev, LINK_STATE_UNKNOWN, LINK_STATE_UNKNOWN);
+
+      if (il == NULL) {
+        ERROR("connectivity plugin: unable to add interface %s during "
+              "connectivity_link_state",
+              dev);
+        return MNL_CB_ERROR;
+      }
+    }
+
+    uint32_t prev_status = il->status;
+    il->status =
+        ((ifi->ifi_flags & IFF_RUNNING) ? LINK_STATE_UP : LINK_STATE_DOWN);
+    il->timestamp = cdtime();
+
+    // If the new status is different than the previous status,
+    // store the previous status and set sent to zero, and set the
+    // global flag to indicate there are statuses to dispatch
+    if (il->status != prev_status) {
+      il->prev_status = prev_status;
+      il->sent = 0;
+      statuses_to_send = 1;
+    }
+
+    DEBUG("connectivity plugin (%llu): Interface %s status is now %s",
+          (unsigned long long)il->timestamp, dev,
+          ((ifi->ifi_flags & IFF_RUNNING) ? "UP" : "DOWN"));
+
+    // no need to loop again, we found the interface name attr
+    // (otherwise the first if-statement in the loop would
+    // have moved us on with 'continue')
+    break;
+  }
+
+  pthread_mutex_unlock(&connectivity_data_lock);
+
+  return 0;
+}
+
+static int msg_handler(struct nlmsghdr *msg) {
+  // We are only interested in RTM_NEWLINK messages
+  if (msg->nlmsg_type != RTM_NEWLINK) {
+    return 0;
+  }
+  return connectivity_link_state(msg);
+}
+
+static int read_event(int (*msg_handler)(struct nlmsghdr *)) {
+  int ret = 0;
+  int recv_flags = MSG_DONTWAIT;
+
+  if (nl_sock == -1 || msg_handler == NULL)
+    return EINVAL;
+
+  while (42) {
+    pthread_mutex_lock(&connectivity_threads_lock);
+
+    if (connectivity_netlink_thread_loop <= 0) {
+      pthread_mutex_unlock(&connectivity_threads_lock);
+      return ret;
+    }
+
+    pthread_mutex_unlock(&connectivity_threads_lock);
+
+    char buf[4096];
+    int status = recv(nl_sock, buf, sizeof(buf), recv_flags);
+
+    if (status < 0) {
+
+      // If there were no more messages to drain from the socket,
+      // then signal the dequeue thread and allow it to dispatch
+      // any saved interface status changes.  Then continue, but
+      // block and wait for new messages
+      if (errno == EWOULDBLOCK || errno == EAGAIN) {
+        pthread_cond_signal(&connectivity_cond);
+
+        recv_flags = 0;
+        continue;
+      }
+
+      if (errno == EINTR) {
+        // Interrupt, so just continue and try again
+        continue;
+      }
+
+      /* Anything else is an error */
+      ERROR("connectivity plugin: read_event: Error recv: %d", status);
+      return status;
+    }
+
+    // Message received successfully, so we'll stop blocking on the
+    // receive call for now (until we get a "would block" error, which
+    // will be handled above)
+    recv_flags = MSG_DONTWAIT;
+
+    if (status == 0) {
+      DEBUG("connectivity plugin: read_event: EOF");
+    }
+
+    /* We need to handle more than one message per 'recvmsg' */
+    for (struct nlmsghdr *h = (struct nlmsghdr *)buf;
+         NLMSG_OK(h, (unsigned int)status); h = NLMSG_NEXT(h, status)) {
+      /* Finish reading */
+      if (h->nlmsg_type == NLMSG_DONE)
+        return ret;
+
+      /* Message is some kind of error */
+      if (h->nlmsg_type == NLMSG_ERROR) {
+        struct nlmsgerr *l_err = (struct nlmsgerr *)NLMSG_DATA(h);
+        ERROR("connectivity plugin: read_event: Message is an error: %d",
+              l_err->error);
+        return -1; // Error
+      }
+
+      /* Call message handler */
+      if (msg_handler) {
+        ret = (*msg_handler)(h);
+        if (ret < 0) {
+          ERROR("connectivity plugin: read_event: Message handler error %d",
+                ret);
+          return ret;
+        }
+      } else {
+        ERROR("connectivity plugin: read_event: Error NULL message handler");
+        return -1;
+      }
+    }
+  }
+
+  return ret;
+}
+
+static void connectivity_dispatch_notification(const char *interface,
+                                               gauge_t value, gauge_t old_value,
+                                               cdtime_t timestamp) {
+
+  notification_t n = {
+      .severity = (value == LINK_STATE_UP ? NOTIF_OKAY : NOTIF_FAILURE),
+      .time = cdtime(),
+      .plugin = "connectivity",
+      .type = "gauge",
+      .type_instance = "interface_status",
+  };
+
+  sstrncpy(n.host, hostname_g, sizeof(n.host));
+  sstrncpy(n.plugin_instance, interface, sizeof(n.plugin_instance));
+
+  char *buf = NULL;
+
+  gen_message_payload(value, old_value, interface, timestamp, &buf);
+
+  int status = plugin_notification_meta_add_string(&n, "ves", buf);
+
+  if (status < 0) {
+    sfree(buf);
+    ERROR("connectivity plugin: unable to set notification VES metadata: %s",
+          STRERRNO);
+    return;
+  }
+
+  DEBUG("connectivity plugin: notification VES metadata: %s",
+        n.meta->nm_value.nm_string);
+
+  DEBUG("connectivity plugin: dispatching state %d for interface %s",
+        (int)value, interface);
+
+  plugin_dispatch_notification(&n);
+  plugin_notification_meta_free(n.meta);
+
+  // strdup'd in gen_message_payload
+  if (buf != NULL)
+    sfree(buf);
+}
+
+// NOTE: Caller MUST hold connectivity_data_lock when calling this function
+static void send_interface_status() {
+  for (interface_list_t *il = interface_list_head; il != NULL;
+       il = il->next) /* {{{ */
+  {
+    uint32_t status = il->status;
+    uint32_t prev_status = il->prev_status;
+    uint32_t sent = il->sent;
+
+    if (status != prev_status && sent == 0) {
+      connectivity_dispatch_notification(il->interface, status, prev_status,
+                                         il->timestamp);
+      il->sent = 1;
+    }
+  } /* }}} for (il = interface_list_head; il != NULL; il = il->next) */
+
+  statuses_to_send = 0;
+}
+
+static void read_interface_status() /* {{{ */
+{
+  pthread_mutex_lock(&connectivity_data_lock);
+
+  // If we don't have any interface statuses to dispatch,
+  // then we wait until signalled
+  if (!statuses_to_send)
+    pthread_cond_wait(&connectivity_cond, &connectivity_data_lock);
+
+  send_interface_status();
+
+  pthread_mutex_unlock(&connectivity_data_lock);
+} /* }}} int *read_interface_status */
+
+static void *connectivity_netlink_thread(void *arg) /* {{{ */
+{
+  pthread_mutex_lock(&connectivity_threads_lock);
+
+  while (connectivity_netlink_thread_loop > 0) {
+    pthread_mutex_unlock(&connectivity_threads_lock);
+
+    int status = read_event(msg_handler);
+
+    pthread_mutex_lock(&connectivity_threads_lock);
+
+    if (status < 0) {
+      connectivity_netlink_thread_error = 1;
+      break;
+    }
+  } /* while (connectivity_netlink_thread_loop > 0) */
+
+  pthread_mutex_unlock(&connectivity_threads_lock);
+
+  return (void *)0;
+} /* }}} void *connectivity_netlink_thread */
+
+static void *connectivity_dequeue_thread(void *arg) /* {{{ */
+{
+  pthread_mutex_lock(&connectivity_threads_lock);
+
+  while (connectivity_dequeue_thread_loop > 0) {
+    pthread_mutex_unlock(&connectivity_threads_lock);
+
+    read_interface_status();
+
+    pthread_mutex_lock(&connectivity_threads_lock);
+  } /* while (connectivity_dequeue_thread_loop > 0) */
+
+  pthread_mutex_unlock(&connectivity_threads_lock);
+
+  return ((void *)0);
+} /* }}} void *connectivity_dequeue_thread */
+
+static int nl_connect() {
+  struct sockaddr_nl sa_nl = {
+      .nl_family = AF_NETLINK,
+      .nl_groups = RTMGRP_LINK,
+      .nl_pid = getpid(),
+  };
+
+  nl_sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+  if (nl_sock == -1) {
+    ERROR("connectivity plugin: socket open failed: %s", STRERRNO);
+    return -1;
+  }
+
+  int rc = bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl));
+  if (rc == -1) {
+    ERROR("connectivity plugin: socket bind failed: %s", STRERRNO);
+    close(nl_sock);
+    nl_sock = -1;
+    return -1;
+  }
+
+  return 0;
+}
+
+static int start_netlink_thread(void) /* {{{ */
+{
+  pthread_mutex_lock(&connectivity_threads_lock);
+
+  if (connectivity_netlink_thread_loop != 0) {
+    pthread_mutex_unlock(&connectivity_threads_lock);
+    return 0;
+  }
+
+  connectivity_netlink_thread_loop = 1;
+  connectivity_netlink_thread_error = 0;
+
+  int status;
+
+  if (nl_sock == -1) {
+    status = nl_connect();
+
+    if (status != 0) {
+      pthread_mutex_unlock(&connectivity_threads_lock);
+      return status;
+    }
+  }
+
+  status = plugin_thread_create(&connectivity_netlink_thread_id,
+                                /* attr = */ NULL, connectivity_netlink_thread,
+                                /* arg = */ (void *)0, "connectivity");
+  if (status != 0) {
+    connectivity_netlink_thread_loop = 0;
+    ERROR("connectivity plugin: Starting thread failed.");
+    pthread_mutex_unlock(&connectivity_threads_lock);
+
+    int status2 = close(nl_sock);
+
+    if (status2 != 0) {
+      ERROR("connectivity plugin: failed to close socket %d: %d (%s)", nl_sock,
+            status2, STRERRNO);
+    }
+
+    nl_sock = -1;
+
+    return -1;
+  }
+
+  pthread_mutex_unlock(&connectivity_threads_lock);
+
+  return status;
+}
+
+static int start_dequeue_thread(void) /* {{{ */
+{
+  pthread_mutex_lock(&connectivity_threads_lock);
+
+  if (connectivity_dequeue_thread_loop != 0) {
+    pthread_mutex_unlock(&connectivity_threads_lock);
+    return 0;
+  }
+
+  connectivity_dequeue_thread_loop = 1;
+
+  int status =
+      plugin_thread_create(&connectivity_dequeue_thread_id,
+                           /* attr = */ NULL, connectivity_dequeue_thread,
+                           /* arg = */ (void *)0, "connectivity");
+  if (status != 0) {
+    connectivity_dequeue_thread_loop = 0;
+    ERROR("connectivity plugin: Starting dequeue thread failed.");
+    pthread_mutex_unlock(&connectivity_threads_lock);
+    return -1;
+  }
+
+  pthread_mutex_unlock(&connectivity_threads_lock);
+
+  return status;
+} /* }}} int start_dequeue_thread */
+
+static int start_threads(void) /* {{{ */
+{
+  int status = start_netlink_thread();
+  int status2 = start_dequeue_thread();
+
+  if (status != 0)
+    return status;
+  else
+    return status2;
+} /* }}} int start_threads */
+
+static int stop_netlink_thread(int shutdown) /* {{{ */
+{
+  int socket_status;
+
+  if (nl_sock != -1) {
+    socket_status = close(nl_sock);
+    if (socket_status != 0) {
+      ERROR("connectivity plugin: failed to close socket %d: %d (%s)", nl_sock,
+            socket_status, STRERRNO);
+    }
+
+    nl_sock = -1;
+  } else
+    socket_status = 0;
+
+  pthread_mutex_lock(&connectivity_threads_lock);
+
+  if (connectivity_netlink_thread_loop == 0) {
+    pthread_mutex_unlock(&connectivity_threads_lock);
+    // Thread has already been terminated, nothing more to attempt
+    return socket_status;
+  }
+
+  // Set thread termination status
+  connectivity_netlink_thread_loop = 0;
+  pthread_mutex_unlock(&connectivity_threads_lock);
+
+  // Let threads waiting on access to the interface list know to move
+  // on such that they'll see the thread's termination status
+  pthread_cond_broadcast(&connectivity_cond);
+
+  int thread_status;
+
+  if (shutdown == 1) {
+    // Since the thread is blocking, calling pthread_join
+    // doesn't actually succeed in stopping it.  It will stick around
+    // until a NETLINK message is received on the socket (at which
+    // it will realize that "connectivity_netlink_thread_loop" is 0 and will
+    // break out of the read loop and be allowed to die).  This is
+    // fine when the process isn't supposed to be exiting, but in
+    // the case of a process shutdown, we don't want to have an
+    // idle thread hanging around.  Calling pthread_cancel here in
+    // the case of a shutdown is just assures that the thread is
+    // gone and that the process has been fully terminated.
+
+    DEBUG("connectivity plugin: Canceling netlink thread for process shutdown");
+
+    thread_status = pthread_cancel(connectivity_netlink_thread_id);
+
+    if (thread_status != 0 && thread_status != ESRCH) {
+      ERROR("connectivity plugin: Unable to cancel netlink thread: %d",
+            thread_status);
+      thread_status = -1;
+    } else
+      thread_status = 0;
+  } else {
+    thread_status =
+        pthread_join(connectivity_netlink_thread_id, /* return = */ NULL);
+    if (thread_status != 0 && thread_status != ESRCH) {
+      ERROR("connectivity plugin: Stopping netlink thread failed: %d",
+            thread_status);
+      thread_status = -1;
+    } else
+      thread_status = 0;
+  }
+
+  pthread_mutex_lock(&connectivity_threads_lock);
+  memset(&connectivity_netlink_thread_id, 0,
+         sizeof(connectivity_netlink_thread_id));
+  connectivity_netlink_thread_error = 0;
+  pthread_mutex_unlock(&connectivity_threads_lock);
+
+  DEBUG("connectivity plugin: Finished requesting stop of netlink thread");
+
+  if (socket_status != 0)
+    return socket_status;
+  else
+    return thread_status;
+}
+
+static int stop_dequeue_thread() /* {{{ */
+{
+  pthread_mutex_lock(&connectivity_threads_lock);
+
+  if (connectivity_dequeue_thread_loop == 0) {
+    pthread_mutex_unlock(&connectivity_threads_lock);
+    return -1;
+  }
+
+  // Set thread termination status
+  connectivity_dequeue_thread_loop = 0;
+  pthread_mutex_unlock(&connectivity_threads_lock);
+
+  // Let threads waiting on access to the interface list know to move
+  // on such that they'll see the threads termination status
+  pthread_cond_broadcast(&connectivity_cond);
+
+  // Calling pthread_cancel here just assures that the thread is
+  // gone and that the process has been fully terminated.
+
+  DEBUG("connectivity plugin: Canceling dequeue thread for process shutdown");
+
+  int status = pthread_cancel(connectivity_dequeue_thread_id);
+
+  if (status != 0 && status != ESRCH) {
+    ERROR("connectivity plugin: Unable to cancel dequeue thread: %d", status);
+    status = -1;
+  } else
+    status = 0;
+
+  pthread_mutex_lock(&connectivity_threads_lock);
+  memset(&connectivity_dequeue_thread_id, 0,
+         sizeof(connectivity_dequeue_thread_id));
+  pthread_mutex_unlock(&connectivity_threads_lock);
+
+  DEBUG("connectivity plugin: Finished requesting stop of dequeue thread");
+
+  return status;
+} /* }}} int stop_dequeue_thread */
+
+static int stop_threads() /* {{{ */
+{
+  int status = stop_netlink_thread(1);
+  int status2 = stop_dequeue_thread();
+
+  if (status != 0)
+    return status;
+  else
+    return status2;
+} /* }}} int stop_threads */
+
+static int connectivity_init(void) /* {{{ */
+{
+  if (monitor_all_interfaces) {
+    NOTICE("connectivity plugin: No interfaces have been selected, so all will "
+           "be monitored");
+  }
+
+  return start_threads();
+} /* }}} int connectivity_init */
+
+static int connectivity_config(const char *key, const char *value) /* {{{ */
+{
+  if (ignorelist == NULL) {
+    ignorelist = ignorelist_create(/* invert = */ 1);
+
+    if (ignorelist == NULL)
+      return -1;
+  }
+
+  if (strcasecmp(key, "Interface") == 0) {
+    ignorelist_add(ignorelist, value);
+    monitor_all_interfaces = 0;
+  } else if (strcasecmp(key, "IgnoreSelected") == 0) {
+    int invert = 1;
+    if (IS_TRUE(value))
+      invert = 0;
+    ignorelist_set_invert(ignorelist, invert);
+  } else {
+    return -1;
+  }
+
+  return 0;
+} /* }}} int connectivity_config */
+
+static int connectivity_read(void) /* {{{ */
+{
+  pthread_mutex_lock(&connectivity_threads_lock);
+
+  if (connectivity_netlink_thread_error != 0) {
+
+    pthread_mutex_unlock(&connectivity_threads_lock);
+
+    ERROR("connectivity plugin: The netlink thread had a problem. Restarting "
+          "it.");
+
+    stop_netlink_thread(0);
+
+    for (interface_list_t *il = interface_list_head; il != NULL;
+         il = il->next) {
+      il->status = LINK_STATE_UNKNOWN;
+      il->prev_status = LINK_STATE_UNKNOWN;
+      il->sent = 0;
+    }
+
+    start_netlink_thread();
+
+    return -1;
+  } /* if (connectivity_netlink_thread_error != 0) */
+
+  pthread_mutex_unlock(&connectivity_threads_lock);
+
+  return 0;
+} /* }}} int connectivity_read */
+
+static int connectivity_shutdown(void) /* {{{ */
+{
+  DEBUG("connectivity plugin: Shutting down thread.");
+
+  int status = stop_threads();
+
+  interface_list_t *il = interface_list_head;
+  while (il != NULL) {
+    interface_list_t *il_next;
+
+    il_next = il->next;
+
+    sfree(il->interface);
+    sfree(il);
+
+    il = il_next;
+  }
+
+  ignorelist_free(ignorelist);
+
+  return status;
+} /* }}} int connectivity_shutdown */
+
+void module_register(void) {
+  plugin_register_config("connectivity", connectivity_config, config_keys,
+                         config_keys_num);
+  plugin_register_init("connectivity", connectivity_init);
+  plugin_register_read("connectivity", connectivity_read);
+  plugin_register_shutdown("connectivity", connectivity_shutdown);
+} /* void module_register */
index cf3d3da..d23d071 100644 (file)
 #include "plugin.h"
 #include "utils/common/common.h"
 
-#ifdef HAVE_SYS_SYSCTL_H
+#if defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYS_SYSCTL_H)
 #include <sys/sysctl.h>
-#endif
-
-#if HAVE_SYSCTLBYNAME
 /* no global variables */
 /* #endif HAVE_SYSCTLBYNAME */
 
index 9574f2c..499d675 100644 (file)
@@ -26,6 +26,9 @@
 #define _DEFAULT_SOURCE
 #define _BSD_SOURCE /* For setgroups */
 
+/* _GNU_SOURCE is needed in Linux to use execvpe */
+#define _GNU_SOURCE
+
 #include "collectd.h"
 
 #include "plugin.h"
@@ -43,6 +46,8 @@
 #include <sys/capability.h>
 #endif
 
+extern char **environ;
+
 #define PL_NORMAL 0x01
 #define PL_NOTIF_ACTION 0x02
 
@@ -245,49 +250,9 @@ 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];
-
-  snprintf(buffer, sizeof(buffer), "%.3f",
-           CDTIME_T_TO_DOUBLE(plugin_get_interval()));
-  setenv("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1);
-
-  sstrncpy(buffer, hostname_g, sizeof(buffer));
-  setenv("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
-#else
-  snprintf(env_interval, sizeof(env_interval), "COLLECTD_INTERVAL=%.3f",
-           CDTIME_T_TO_DOUBLE(plugin_get_interval()));
-  putenv(env_interval);
-
-  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) /* {{{ */
+__attribute__((noreturn)) static void exec_child(program_list_t *pl,
+                                                 char **envp, int uid, int gid,
+                                                 int egid) /* {{{ */
 {
   int status;
 
@@ -328,7 +293,12 @@ __attribute__((noreturn)) static void exec_child(program_list_t *pl, int uid,
     exit(-1);
   }
 
+#ifdef HAVE_EXECVPE
+  execvpe(pl->exec, pl->argv, envp);
+#else
+  environ = envp;
   execvp(pl->exec, pl->argv);
+#endif
 
   ERROR("exec plugin: Failed to execute ``%s'': %s", pl->exec, STRERRNO);
   exit(-1);
@@ -486,17 +456,42 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
     goto failed;
   }
 
-  set_environment();
+  double interval = CDTIME_T_TO_DOUBLE(plugin_get_interval());
 
   pid = fork();
   if (pid < 0) {
     ERROR("exec plugin: fork failed: %s", STRERRNO);
     goto failed;
   } else if (pid == 0) {
-    int fd_num;
+    char interval_buf[128];
+    snprintf(interval_buf, sizeof(interval_buf), "COLLECTD_INTERVAL=%.3f",
+             interval);
+
+    /* max hostname len is 255, so this should be enough */
+    char hostname_buf[300];
+    snprintf(hostname_buf, sizeof(hostname_buf), "COLLECTD_HOSTNAME=%s",
+             hostname_g);
+
+    size_t env_size = 0;
+    while (environ[env_size] != NULL) {
+      ++env_size;
+    }
+
+    /* Copy the environment variables */
+    char *envp[env_size + 3];
+    size_t envp_idx;
+    for (envp_idx = 0; environ[envp_idx] != NULL && envp_idx < env_size;
+         ++envp_idx) {
+      envp[envp_idx] = environ[envp_idx];
+    }
+
+    /* Add the collectd environment variables */
+    envp[envp_idx++] = interval_buf;
+    envp[envp_idx++] = hostname_buf;
+    envp[envp_idx++] = NULL;
 
     /* Close all file descriptors but the pipe end we need. */
-    fd_num = getdtablesize();
+    int fd_num = getdtablesize();
     for (int fd = 0; fd < fd_num; fd++) {
       if ((fd == fd_pipe_in[0]) || (fd == fd_pipe_out[1]) ||
           (fd == fd_pipe_err[1]))
@@ -525,12 +520,10 @@ static int fork_child(program_list_t *pl, int *fd_in, int *fd_out,
     /* Unblock all signals */
     reset_signal_mask();
 
-    exec_child(pl, uid, gid, egid);
+    exec_child(pl, envp, uid, gid, egid);
     /* does not return */
   }
 
-  unset_environment();
-
   close(fd_pipe_in[0]);
   close(fd_pipe_out[1]);
   close(fd_pipe_err[1]);
@@ -553,8 +546,6 @@ 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 41f4909..4da73bb 100644 (file)
@@ -2076,7 +2076,7 @@ static int cjni_config_load_plugin(oconfig_item_t *ci) /* {{{ */
     class->object = NULL;
   if (class->object == NULL) {
     ERROR("java plugin: cjni_config_load_plugin: "
-          "Could create a new `%s' object.",
+          "Could not create a new `%s' object.",
           class->name);
     cjni_thread_detach();
     free(class->name);
diff --git a/src/lvm.c b/src/lvm.c
deleted file mode 100644 (file)
index 3077c93..0000000
--- a/src/lvm.c
+++ /dev/null
@@ -1,211 +0,0 @@
-/**
- * collectd - src/lvm.c
- * Copyright (C) 2013       Chad Malfait
- * Copyright (C) 2014       Carnegie Mellon University
- *
- * 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:
- *   Chad Malfait <malfaitc at yahoo.com>
- *   Benjamin Gilbert <bgilbert at backtick.net>
- **/
-
-#include "collectd.h"
-
-#include "plugin.h"
-#include "utils/common/common.h"
-
-#include <lvm2app.h>
-
-#ifdef HAVE_SYS_CAPABILITY_H
-#include <sys/capability.h>
-#endif /* HAVE_SYS_CAPABILITY_H */
-
-#define NO_VALUE UINT64_MAX
-#define PERCENT_SCALE_FACTOR 1e-8
-
-static uint64_t get_lv_property_int(lv_t lv, char const *property) {
-  lvm_property_value_t v;
-
-  v = lvm_lv_get_property(lv, property);
-  if (!v.is_valid || !v.is_integer)
-    return NO_VALUE;
-  /* May be NO_VALUE if @property does not apply to this LV */
-  return v.value.integer;
-}
-
-static char const *get_lv_property_string(lv_t lv, char const *property) {
-  lvm_property_value_t v;
-
-  v = lvm_lv_get_property(lv, property);
-  if (!v.is_valid || !v.is_string)
-    return NULL;
-  return v.value.string;
-}
-
-static void lvm_submit(char const *plugin_instance, char const *type_instance,
-                       uint64_t ivalue) {
-  value_list_t vl = VALUE_LIST_INIT;
-
-  vl.values = &(value_t){.gauge = (gauge_t)ivalue};
-  vl.values_len = 1;
-
-  sstrncpy(vl.plugin, "lvm", sizeof(vl.plugin));
-  sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
-  sstrncpy(vl.type, "df_complex", sizeof(vl.type));
-  sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
-
-  plugin_dispatch_values(&vl);
-}
-
-static void report_lv_utilization(lv_t lv, char const *vg_name,
-                                  char const *lv_name, uint64_t lv_size,
-                                  char const *used_percent_property) {
-  uint64_t used_percent_unscaled;
-  uint64_t used_bytes;
-  char plugin_instance[DATA_MAX_NAME_LEN];
-
-  used_percent_unscaled = get_lv_property_int(lv, used_percent_property);
-  if (used_percent_unscaled == NO_VALUE)
-    return;
-  used_bytes = lv_size * (used_percent_unscaled * PERCENT_SCALE_FACTOR);
-
-  ssnprintf(plugin_instance, sizeof(plugin_instance), "%s-%s", vg_name,
-            lv_name);
-  lvm_submit(plugin_instance, "used", used_bytes);
-  lvm_submit(plugin_instance, "free", lv_size - used_bytes);
-}
-
-static void report_thin_pool_utilization(lv_t lv, char const *vg_name,
-                                         uint64_t lv_size) {
-  char const *data_lv;
-  char const *metadata_lv;
-  uint64_t metadata_size;
-
-  data_lv = get_lv_property_string(lv, "data_lv");
-  metadata_lv = get_lv_property_string(lv, "metadata_lv");
-  metadata_size = get_lv_property_int(lv, "lv_metadata_size");
-  if (data_lv == NULL || metadata_lv == NULL || metadata_size == NO_VALUE)
-    return;
-
-  report_lv_utilization(lv, vg_name, data_lv, lv_size, "data_percent");
-  report_lv_utilization(lv, vg_name, metadata_lv, metadata_size,
-                        "metadata_percent");
-}
-
-static void vg_read(vg_t vg, char const *vg_name) {
-  struct dm_list *lvs;
-  struct lvm_lv_list *lvl;
-  char const *name;
-  char const *attrs;
-  uint64_t size;
-
-  lvm_submit(vg_name, "free", lvm_vg_get_free_size(vg));
-
-  lvs = lvm_vg_list_lvs(vg);
-  if (!lvs) {
-    /* no VGs are defined, which is not an error per se */
-    return;
-  }
-
-  dm_list_iterate_items(lvl, lvs) {
-    name = lvm_lv_get_name(lvl->lv);
-    attrs = get_lv_property_string(lvl->lv, "lv_attr");
-    size = lvm_lv_get_size(lvl->lv);
-    if (name == NULL || attrs == NULL || size == NO_VALUE)
-      continue;
-
-    /* Condition on volume type.  We want the reported sizes in the
-       volume group to sum to the size of the volume group, so we ignore
-       virtual volumes.  */
-    switch (attrs[0]) {
-    case 's':
-    case 'S':
-      /* Snapshot.  Also report used/free space. */
-      report_lv_utilization(lvl->lv, vg_name, name, size, "data_percent");
-      break;
-    case 't':
-      /* Thin pool virtual volume.  We report the underlying data
-         and metadata volumes, not this one.  Report used/free
-         space, then ignore. */
-      report_thin_pool_utilization(lvl->lv, vg_name, size);
-      continue;
-    case 'v':
-      /* Virtual volume.  Ignore. */
-      continue;
-    case 'V':
-      /* Thin volume or thin snapshot.  Ignore. */
-      continue;
-    }
-    lvm_submit(vg_name, name, size);
-  }
-}
-
-static int lvm_read(void) {
-  lvm_t lvm;
-  struct dm_list *vg_names;
-  struct lvm_str_list *name_list;
-
-  lvm = lvm_init(NULL);
-  if (!lvm) {
-    ERROR("lvm plugin: lvm_init failed.");
-    return -1;
-  }
-
-  vg_names = lvm_list_vg_names(lvm);
-  if (!vg_names) {
-    ERROR("lvm plugin lvm_list_vg_name failed %s", lvm_errmsg(lvm));
-    lvm_quit(lvm);
-    return -1;
-  }
-
-  dm_list_iterate_items(name_list, vg_names) {
-    vg_t vg;
-
-    vg = lvm_vg_open(lvm, name_list->str, "r", 0);
-    if (!vg) {
-      ERROR("lvm plugin: lvm_vg_open (%s) failed: %s", name_list->str,
-            lvm_errmsg(lvm));
-      continue;
-    }
-
-    vg_read(vg, name_list->str);
-    lvm_vg_close(vg);
-  }
-
-  lvm_quit(lvm);
-  return 0;
-} /*lvm_read */
-
-static int c_lvm_init(void) {
-#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_ADMIN)
-  if (check_capability(CAP_SYS_ADMIN) != 0) {
-    if (getuid() == 0)
-      WARNING("lvm plugin: Running collectd as root, but the "
-              "CAP_SYS_ADMIN capability is missing. The plugin's read "
-              "function will probably fail. Is your init system dropping "
-              "capabilities?");
-    else
-      WARNING("lvm plugin: collectd doesn't have the CAP_SYS_ADMIN "
-              "capability. If you don't want to run collectd as root, try "
-              "running \"setcap cap_sys_admin=ep\" on the collectd binary.");
-  }
-#endif
-  return 0;
-}
-
-void module_register(void) {
-  plugin_register_init("lvm", c_lvm_init);
-  plugin_register_read("lvm", lvm_read);
-} /* void module_register */
index aab5141..7f9aa9f 100644 (file)
@@ -924,16 +924,26 @@ static int sysevent_init(void) /* {{{ */
   ring.buffer = (char **)calloc(buffer_length, sizeof(char *));
 
   if (ring.buffer == NULL) {
-    ERROR("sysevent plugin: sysevent_init calloc failed");
+    ERROR("sysevent plugin: sysevent_init ring buffer calloc failed");
     return -1;
   }
 
   for (int i = 0; i < buffer_length; i++) {
     ring.buffer[i] = calloc(1, listen_buffer_size);
+
+    if (ring.buffer[i] == NULL) {
+      ERROR("sysevent plugin: sysevent_init ring buffer entry calloc failed");
+      return -1;
+    }
   }
 
   ring.timestamp = (cdtime_t *)calloc(buffer_length, sizeof(cdtime_t));
 
+  if (ring.timestamp == NULL) {
+    ERROR("sysevent plugin: sysevent_init ring buffer timestamp calloc failed");
+    return -1;
+  }
+
   if (sock == -1) {
     struct addrinfo hints = {
         .ai_family = AF_UNSPEC,
index 2711c9d..aad767e 100644 (file)
@@ -101,22 +101,6 @@ int ssnprintf(char *str, size_t sz, const char *format, ...) {
   return ret;
 } /* int ssnprintf */
 
-/* ssnprintf2 returns zero on success, one if truncation occurred
-   and a negative integer on error. */
-int ssnprintf2(char *str, size_t sz, const char *format, ...) {
-  va_list ap;
-  va_start(ap, format);
-
-  int ret = vsnprintf(str, sz, format, ap);
-
-  va_end(ap);
-
-  if (ret < 0) {
-    return ret;
-  }
-  return (size_t)ret >= sz;
-} /* int ssnprintf2 */
-
 char *ssnprintf_alloc(char const *format, ...) /* {{{ */
 {
   char static_buffer[1024] = "";
index 595ae3e..1ca6505 100644 (file)
@@ -69,9 +69,6 @@ char *sstrncpy(char *dest, const char *src, size_t n);
 __attribute__((format(printf, 3, 4))) int ssnprintf(char *str, size_t size,
                                                     char const *format, ...);
 
-__attribute__((format(printf, 3, 4))) int ssnprintf2(char *str, size_t size,
-                                                     char const *format, ...);
-
 __attribute__((format(printf, 1, 2))) char *ssnprintf_alloc(char const *format,
                                                             ...);
 
index 4f15c16..b8b3b4f 100644 (file)
@@ -196,9 +196,9 @@ DEF_TEST(escape_slashes) {
   };
 
   for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
-    char buffer[32];
+    char buffer[32] = {0};
 
-    strncpy(buffer, cases[i].str, sizeof(buffer));
+    strncpy(buffer, cases[i].str, sizeof(buffer) - 1);
     OK(escape_slashes(buffer, sizeof(buffer)) == 0);
     EXPECT_EQ_STR(cases[i].want, buffer);
   }
@@ -221,9 +221,9 @@ DEF_TEST(escape_string) {
   };
 
   for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
-    char buffer[16];
+    char buffer[16] = {0};
 
-    strncpy(buffer, cases[i].str, sizeof(buffer));
+    strncpy(buffer, cases[i].str, sizeof(buffer) - 1);
     OK(escape_string(buffer, sizeof(buffer)) == 0);
     EXPECT_EQ_STR(cases[i].want, buffer);
   }
@@ -232,33 +232,33 @@ DEF_TEST(escape_string) {
 }
 
 DEF_TEST(strunescape) {
-  char buffer[16];
+  char buffer[32] = {0};
   int status;
 
-  strncpy(buffer, "foo\\tbar", sizeof(buffer));
+  strncpy(buffer, "foo\\tbar", sizeof(buffer) - 1);
   status = strunescape(buffer, sizeof(buffer));
   OK(status == 0);
   EXPECT_EQ_STR("foo\tbar", buffer);
 
-  strncpy(buffer, "\\tfoo\\r\\n", sizeof(buffer));
+  strncpy(buffer, "\\tfoo\\r\\n", sizeof(buffer) - 1);
   status = strunescape(buffer, sizeof(buffer));
   OK(status == 0);
   EXPECT_EQ_STR("\tfoo\r\n", buffer);
 
-  strncpy(buffer, "With \\\"quotes\\\"", sizeof(buffer));
+  strncpy(buffer, "With \\\"quotes\\\"", sizeof(buffer) - 1);
   status = strunescape(buffer, sizeof(buffer));
   OK(status == 0);
   EXPECT_EQ_STR("With \"quotes\"", buffer);
 
   /* Backslash before null byte */
-  strncpy(buffer, "\\tbackslash end\\", sizeof(buffer));
+  strncpy(buffer, "\\tbackslash end\\", sizeof(buffer) - 1);
   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));
+  strncpy(buffer, "\\t3\\56", sizeof(buffer) - 1);
   status = strunescape(buffer, 4);
   OK(status != 0);
   OK(buffer[0] == '\t');
index be93b9d..3364697 100644 (file)
@@ -268,10 +268,17 @@ static int za_read(void) {
   za_read_gauge(ksp, "mfu_size", "cache_size", "mfu_size");
   za_read_gauge(ksp, "mru_ghost_size", "cache_size", "mru_ghost_size");
   za_read_gauge(ksp, "mru_size", "cache_size", "mru_size");
-  za_read_gauge(ksp, "other_size", "cache_size", "other_size");
   za_read_gauge(ksp, "p", "cache_size", "p");
   za_read_gauge(ksp, "size", "cache_size", "arc");
 
+  /* The "other_size" value was replaced by more specific values in ZFS on Linux
+   * version 0.7.0 (commit 25458cb)
+   */
+  if (za_read_gauge(ksp, "dbuf_size", "cache_size", "dbuf_size") != 0 ||
+      za_read_gauge(ksp, "dnode_size", "cache_size", "dnode_size") != 0 ||
+      za_read_gauge(ksp, "bonus_size", "cache_size", "bonus_size") != 0)
+    za_read_gauge(ksp, "other_size", "cache_size", "other_size");
+
   /* The "l2_size" value has disappeared from Solaris some time in
    * early 2013, and has only reappeared recently in Solaris 11.2.
    * Stop trying if we ever fail to read it, so we don't spam the log.
index 048b5a2..76bd065 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-DEFAULT_VERSION="5.8.1.git"
+DEFAULT_VERSION="5.9.2.git"
 
 if [ -d .git ]; then
        VERSION="`git describe --dirty=+ --abbrev=7 2> /dev/null | grep collectd | sed -e 's/^collectd-//' -e 's/-/./g'`"