Merge pull request #2168 from maryamtahhan/feat_libvirt_upstream
authorRuben Kerkhof <ruben@rubenkerkhof.com>
Mon, 10 Apr 2017 08:27:31 +0000 (10:27 +0200)
committerGitHub <noreply@github.com>
Mon, 10 Apr 2017 08:27:31 +0000 (10:27 +0200)
virt: add support for new metrics

21 files changed:
Makefile.am
build.sh
configure.ac
docs/BUILD.dpdkstat.md
src/collectd.conf.in
src/collectd.conf.pod
src/daemon/utils_ignorelist.c [deleted file]
src/daemon/utils_ignorelist.h [deleted file]
src/dpdkstat.c
src/intel_rdt.c
src/ovs_events.c
src/ovs_stats.c
src/processes.c
src/utils_format_kairosdb.c
src/utils_format_kairosdb.h
src/utils_ignorelist.c [new file with mode: 0644]
src/utils_ignorelist.h [new file with mode: 0644]
src/utils_ovs.c
src/virt.c
src/write_http.c
src/write_mongodb.c

index c033472..1f18d23 100644 (file)
@@ -114,6 +114,7 @@ noinst_LTLIBRARIES = \
        libformat_graphite.la \
        libformat_json.la \
        libheap.la \
+       libignorelist.la \
        liblatency.la \
        liblookup.la \
        libmetadata.la \
@@ -199,8 +200,6 @@ collectd_SOURCES = \
        src/daemon/utils_cache.h \
        src/daemon/utils_complain.c \
        src/daemon/utils_complain.h \
-       src/daemon/utils_ignorelist.c \
-       src/daemon/utils_ignorelist.h \
        src/daemon/utils_llist.c \
        src/daemon/utils_llist.h \
        src/daemon/utils_random.c \
@@ -331,6 +330,10 @@ libheap_la_SOURCES = \
        src/daemon/utils_heap.c \
        src/daemon/utils_heap.h
 
+libignorelist_la_SOURCES = \
+       src/utils_ignorelist.c \
+       src/utils_ignorelist.h
+
 libmetadata_la_SOURCES = \
        src/daemon/meta_data.c \
        src/daemon/meta_data.h
@@ -340,13 +343,11 @@ libplugin_mock_la_SOURCES = \
        src/daemon/utils_cache_mock.c \
        src/daemon/utils_complain.c \
        src/daemon/utils_complain.h \
-       src/daemon/utils_ignorelist.c \
-       src/daemon/utils_ignorelist.h \
        src/daemon/utils_time.c \
        src/daemon/utils_time.h
 
 libplugin_mock_la_CPPFLAGS = $(AM_CPPFLAGS) -DMOCK_TIME
-libplugin_mock_la_LIBADD = libcommon.la $(COMMON_LIBS)
+libplugin_mock_la_LIBADD = libcommon.la libignorelist.la $(COMMON_LIBS)
 
 libformat_graphite_la_SOURCES = \
        src/utils_format_graphite.c \
@@ -602,7 +603,7 @@ if BUILD_PLUGIN_CGROUPS
 pkglib_LTLIBRARIES += cgroups.la
 cgroups_la_SOURCES = src/cgroups.c
 cgroups_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-cgroups_la_LIBADD = libmount.la
+cgroups_la_LIBADD = libignorelist.la libmount.la
 endif
 
 if BUILD_PLUGIN_CHRONY
@@ -719,7 +720,7 @@ if BUILD_PLUGIN_DF
 pkglib_LTLIBRARIES += df.la
 df_la_SOURCES = src/df.c
 df_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-df_la_LIBADD = libmount.la
+df_la_LIBADD = libignorelist.la libmount.la
 endif
 
 if BUILD_PLUGIN_DISK
@@ -728,7 +729,7 @@ disk_la_SOURCES = src/disk.c
 disk_la_CFLAGS = $(AM_CFLAGS)
 disk_la_CPPFLAGS = $(AM_CPPFLAGS)
 disk_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-disk_la_LIBADD =
+disk_la_LIBADD = libignorelist.la
 if BUILD_WITH_LIBKSTAT
 disk_la_LIBADD += -lkstat
 endif
@@ -888,7 +889,7 @@ pkglib_LTLIBRARIES += interface.la
 interface_la_SOURCES = src/interface.c
 interface_la_CFLAGS = $(AM_CFLAGS)
 interface_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-interface_la_LIBADD =
+interface_la_LIBADD = libignorelist.la
 if BUILD_WITH_LIBSTATGRAB
 interface_la_CFLAGS += $(BUILD_WITH_LIBSTATGRAB_CFLAGS)
 interface_la_LIBADD += $(BUILD_WITH_LIBSTATGRAB_LDFLAGS)
@@ -924,7 +925,7 @@ pkglib_LTLIBRARIES += ipmi.la
 ipmi_la_SOURCES = src/ipmi.c
 ipmi_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_OPENIPMI_CFLAGS)
 ipmi_la_LDFLAGS = $(PLUGIN_LDFLAGS)
-ipmi_la_LIBADD = $(BUILD_WITH_OPENIPMI_LIBS)
+ipmi_la_LIBADD = libignorelist.la $(BUILD_WITH_OPENIPMI_LIBS)
 endif
 
 if BUILD_PLUGIN_IPVS
@@ -941,6 +942,7 @@ if BUILD_PLUGIN_IRQ
 pkglib_LTLIBRARIES += irq.la
 irq_la_SOURCES = src/irq.c
 irq_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+irq_la_LIBADD = libignorelist.la
 endif
 
 if BUILD_PLUGIN_JAVA
@@ -1013,6 +1015,7 @@ madwifi_la_SOURCES = \
        src/madwifi.c \
        src/madwifi.h
 madwifi_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+madwifi_la_LIBADD = libignorelist.la
 endif
 
 if BUILD_PLUGIN_MATCH_EMPTY_COUNTER
@@ -1065,6 +1068,7 @@ if BUILD_PLUGIN_MD
 pkglib_LTLIBRARIES += md.la
 md_la_SOURCES = src/md.c
 md_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+md_la_LIBADD = libignorelist.la
 endif
 
 if BUILD_PLUGIN_MEMCACHEC
@@ -1114,7 +1118,7 @@ pkglib_LTLIBRARIES += mic.la
 mic_la_SOURCES = src/mic.c
 mic_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_MIC_CPPFLAGS)
 mic_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_MIC_LDFLAGS)
-mic_la_LIBADD = $(BUILD_WITH_MIC_LIBS)
+mic_la_LIBADD = libignorelist.la $(BUILD_WITH_MIC_LIBS)
 endif
 
 if BUILD_PLUGIN_MODBUS
@@ -1152,7 +1156,7 @@ pkglib_LTLIBRARIES += netapp.la
 netapp_la_SOURCES = src/netapp.c
 netapp_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBNETAPP_CPPFLAGS)
 netapp_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBNETAPP_LDFLAGS)
-netapp_la_LIBADD = $(LIBNETAPP_LIBS)
+netapp_la_LIBADD = libignorelist.la $(LIBNETAPP_LIBS)
 endif
 
 if BUILD_PLUGIN_NETLINK
@@ -1257,8 +1261,8 @@ if BUILD_PLUGIN_ONEWIRE
 pkglib_LTLIBRARIES += onewire.la
 onewire_la_SOURCES = src/onewire.c
 onewire_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBOWCAPI_CPPFLAGS)
-onewire_la_LIBADD = $(BUILD_WITH_LIBOWCAPI_LIBS)
 onewire_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBOWCAPI_LDFLAGS)
+onewire_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBOWCAPI_LIBS)
 endif
 
 if BUILD_PLUGIN_OPENLDAP
@@ -1396,6 +1400,7 @@ if BUILD_PLUGIN_PROTOCOLS
 pkglib_LTLIBRARIES += protocols.la
 protocols_la_SOURCES = src/protocols.c
 protocols_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+protocols_la_LIBADD = libignorelist.la
 endif
 
 if BUILD_PLUGIN_REDIS
@@ -1441,7 +1446,7 @@ pkglib_LTLIBRARIES += sensors.la
 sensors_la_SOURCES = src/sensors.c
 sensors_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBSENSORS_CPPFLAGS)
 sensors_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBSENSORS_LDFLAGS)
-sensors_la_LIBADD = $(BUILD_WITH_LIBSENSORS_LIBS)
+sensors_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBSENSORS_LIBS)
 endif
 
 if BUILD_PLUGIN_SERIAL
@@ -1464,7 +1469,7 @@ pkglib_LTLIBRARIES += smart.la
 smart_la_SOURCES = src/smart.c
 smart_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBATASMART_CPPFLAGS) $(BUILD_WITH_LIBUDEV_CPPFLAGS)
 smart_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBATASMART_LDFLAGS) $(BUILD_WITH_LIBUDEV_LDFLAGS)
-smart_la_LIBADD = $(BUILD_WITH_LIBATASMART_LIBS) $(BUILD_WITH_LIBUDEV_LIBS)
+smart_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBATASMART_LIBS) $(BUILD_WITH_LIBUDEV_LIBS)
 endif
 endif
 
@@ -1606,6 +1611,7 @@ if BUILD_PLUGIN_THERMAL
 pkglib_LTLIBRARIES += thermal.la
 thermal_la_SOURCES = src/thermal.c
 thermal_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+thermal_la_LIBADD = libignorelist.la
 endif
 
 if BUILD_PLUGIN_THRESHOLD
@@ -1683,8 +1689,8 @@ pkglib_LTLIBRARIES += virt.la
 virt_la_SOURCES = src/virt.c
 virt_la_CFLAGS = $(AM_CFLAGS) \
        $(BUILD_WITH_LIBVIRT_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
-virt_la_LIBADD = $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
 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,
@@ -1758,9 +1764,8 @@ endif
 if BUILD_PLUGIN_WRITE_MONGODB
 pkglib_LTLIBRARIES += write_mongodb.la
 write_mongodb_la_SOURCES = src/write_mongodb.c
-write_mongodb_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBMONGOC_CPPFLAGS)
+write_mongodb_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMONGOC_CFLAGS)
 write_mongodb_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBMONGOC_LDFLAGS)
-write_mongodb_la_LIBADD = -lmongoc
 endif
 
 if BUILD_PLUGIN_WRITE_PROMETHEUS
index 465eff9..40f5361 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -18,12 +18,7 @@ EOF
        done
 }
 
-check_for_application lex bison autoheader aclocal automake autoconf
-
-# Actually we don't need the pkg-config executable, but we need the M4 macros.
-# We check for `pkg-config' here and hope that M4 macros will then be
-# available, too.
-check_for_application pkg-config
+check_for_application lex bison autoheader aclocal automake autoconf pkg-config
 
 libtoolize=""
 libtoolize --version >/dev/null 2>/dev/null
index 3b932ca..320572d 100644 (file)
@@ -3310,52 +3310,62 @@ AC_ARG_WITH([libmongoc],
     else if test "x$withval" = "xno"; then
       with_libmongoc="no"
     else
-      with_libmongoc="yes"
-      LIBMONGOC_CPPFLAGS="$LIBMONGOC_CPPFLAGS -I$withval/include"
-      LIBMONGOC_LDFLAGS="$LIBMONGOC_LDFLAGS -L$withval/lib"
+      with_libmongoc="no"
     fi; fi
   ],
   [with_libmongoc="yes"]
 )
 
-SAVE_CPPFLAGS="$CPPFLAGS"
-SAVE_LDFLAGS="$LDFLAGS"
-
-CPPFLAGS="$CPPFLAGS $LIBMONGOC_CPPFLAGS"
-LDFLAGS="$LDFLAGS $LIBMONGOC_LDFLAGS"
+if test "x$with_libmongoc" = "xyes"; then
+  PKG_CHECK_MODULES([LIBMONGOC], [libmongoc-1.0],
+    [with_libmongoc="yes"],
+    [with_libmongoc="no (pkg-config could not find libmongoc)"]
+  )
+fi
 
 if test "x$with_libmongoc" = "xyes"; then
-  if test "x$LIBMONGOC_CPPFLAGS" != "x"; then
-    AC_MSG_NOTICE([libmongoc CPPFLAGS: $LIBMONGOC_CPPFLAGS])
+  SAVE_CPPFLAGS="$CPPFLAGS"
+
+  CPPFLAGS="$CPPFLAGS $LIBMONGOC_CFLAGS"
+
+  if test "x$CPPFLAGS" != "x"; then
+    AC_MSG_NOTICE([libmongoc CPPFLAGS: $LIBMONGOC_CFLAGS])
   fi
 
-  AC_CHECK_HEADERS([mongo.h],
+  AC_CHECK_HEADERS([mongoc.h],
     [with_libmongoc="yes"],
-    [with_libmongoc="no ('mongo.h' not found)"],
-    [[#define MONGO_HAVE_STDINT 1]]
+    [with_libmongoc="no ('mongoc.h' not found)"]
   )
+
+  CPPFLAGS="$SAVE_CPPFLAGS"
 fi
 
 if test "x$with_libmongoc" = "xyes"; then
+  SAVE_CPPFLAGS="$CPPFLAGS"
+  SAVE_LDFLAGS="$LDFLAGS"
+
+  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], [mongo_run_command],
+  AC_CHECK_LIB([mongoc-1.0], [mongoc_init],
     [with_libmongoc="yes"],
-    [with_libmongoc="no (symbol 'mongo_run_command' not found)"]
+    [with_libmongoc="no (symbol 'mongoc_init' not found)"]
   )
-fi
 
-CPPFLAGS="$SAVE_CPPFLAGS"
-LDFLAGS="$SAVE_LDFLAGS"
+  CPPFLAGS="$SAVE_CPPFLAGS"
+  LDFLAGS="$SAVE_LDFLAGS"
+fi
 
 if test "x$with_libmongoc" = "xyes"; then
-  BUILD_WITH_LIBMONGOC_CPPFLAGS="$LIBMONGOC_CPPFLAGS"
+  BUILD_WITH_LIBMONGOC_CFLAGS="$LIBMONGOC_CFLAGS"
   BUILD_WITH_LIBMONGOC_LDFLAGS="$LIBMONGOC_LDFLAGS"
 fi
 
-AC_SUBST([BUILD_WITH_LIBMONGOC_CPPFLAGS])
+AC_SUBST([BUILD_WITH_LIBMONGOC_CFLAGS])
 AC_SUBST([BUILD_WITH_LIBMONGOC_LDFLAGS])
 # }}}
 
@@ -6005,6 +6015,11 @@ if test "x$ac_system" = "xLinux"; then
   if test "x$c_cv_have_clock_boottime_monotonic" = "xyes"; then
     plugin_cpusleep="yes"
   fi
+
+  if test "x$with_libyajl" = "xyes" && test "x$with_libyajl2" = "xyes"; then
+    plugin_ovs_events="yes"
+    plugin_ovs_stats="yes"
+  fi
 fi
 
 if test "x$ac_system" = "xOpenBSD"; then
@@ -6181,11 +6196,6 @@ if test "x$with_libyajl" = "xyes"; then
   plugin_log_logstash="yes"
 fi
 
-if test "x$with_libyajl" = "xyes" && test "x$with_libyajl2" = "xyes"; then
-  plugin_ovs_events="yes"
-  plugin_ovs_stats="yes"
-fi
-
 if test "x$with_libperl" = "xyes" && test "x$c_cv_have_perl_ithreads" = "xyes"; then
   plugin_perl="yes"
 fi
index fe9f0e6..96f1eb9 100644 (file)
@@ -208,6 +208,12 @@ See also: http://dpdk.org/doc/guides/prog_guide/multi_proc_support.html
  *  The same PCI device configuration should be passed to the primary process as
     the secondary process uses the same port indexes as the primary.
  *  A blacklist / whitelist of NICs isn't supported yet.
+ *  Plugin initialization time depends on read interval. It requires 5 read
+    cycles to set up internal buffers and states. During that time no statistics
+    are submitted.
+ *  If number of DPDK ports is increased while plugin is running, internal
+    buffers are resized. That requires 3 read cycles and no port statistics
+    are submitted in that time.
 
 ## License
 
index 6a5307e..e2d6aaf 100644 (file)
 #  Address "127.0.0.1"
 #  Socket "/var/run/openvswitch/db.sock"
 #  Interfaces "br0" "veth0"
-#  SendNotification false
-#  DispatchValues true
+#  SendNotification true
+#  DispatchValues false
 #</Plugin>
 
 #<Plugin ovs_stats>
 #</Plugin>
 
 #<Plugin processes>
+#      CollectFileDescriptor true
+#      CollectContextSwitch true
 #      Process "name"
+#      ProcessMatch "name" "regex"
+#      <Process "collectd">
+#              CollectFileDescriptor false
+#              CollectContextSwitch false
+#      </Process>
+#      <ProcessMatch "name" "regex">
+#              CollectFileDescriptor false
+#              CollectContextSwitch true
+#      </Process>
 #</Plugin>
 
 #<Plugin protocols>
 #              Header "X-Custom-Header: custom_value"
 #              SSLVersion "TLSv1"
 #              Format "Command"
+#              Attribute "key" "value"     # only available for KAIROSDB format
 #              Metrics true
 #              Notifications false
 #              StoreRates false
index af7961e..6a7a917 100644 (file)
@@ -5637,8 +5637,8 @@ B<Synopsis:>
    Address "127.0.0.1"
    Socket "/var/run/openvswitch/db.sock"
    Interfaces "br0" "veth0"
-   SendNotification false
-   DispatchValues true
+   SendNotification true
+   DispatchValues false
  </Plugin>
 
 The plugin provides the following configuration options:
@@ -5676,12 +5676,12 @@ Default: empty (all interfaces on all bridges are monitored)
 =item B<SendNotification> I<true|false>
 
 If set to true, OVS link notifications (interface status and OVS DB connection
-terminate) are sent to collectd. Default value is false.
+terminate) are sent to collectd. Default value is true.
 
 =item B<DispatchValues> I<true|false>
 
 Dispatch the OVS DB interface link status value with configured plugin interval.
-Defaults to true. Please note, if B<SendNotification> and B<DispatchValues>
+Defaults to false. Please note, if B<SendNotification> and B<DispatchValues>
 options are false, no OVS information will be provided by the plugin.
 
 =back
@@ -6528,9 +6528,15 @@ C<I<prefix>/var/run/collectd-powerdns>.
 =item B<Process> I<Name>
 
 Select more detailed statistics of processes matching this name. The statistics
-collected for these selected processes are size of the resident segment size
-(RSS), user- and system-time used, number of processes and number of threads,
-io data (where available) and minor and major pagefaults.
+collected for these selected processes are:
+ - size of the resident segment size (RSS)
+ - user- and system-time used
+ - number of processes
+ - number of threads
+ - number of open files (under Linux)
+ - io data (where available)
+ - context switches (under Linux)
+ - minor and major pagefaults.
 
 Some platforms have a limit on the length of process names. I<Name> must stay
 below this limit.
@@ -8763,6 +8769,15 @@ create output in the I<JavaScript Object Notation> (JSON). When set to KAIROSDB
 
 Defaults to B<Command>.
 
+=item B<Attribute> I<String> I<String>
+
+Only available for KAIROSDB output format.
+
+Consider the two given strings to be the key and value of an additional tag for
+each metric being sent out.
+
+You can add multiple B<Attribute>.
+
 =item B<Metrics> B<true>|B<false>
 
 Controls whether I<metrics> are POSTed to this location. Defaults to B<true>.
diff --git a/src/daemon/utils_ignorelist.c b/src/daemon/utils_ignorelist.c
deleted file mode 100644 (file)
index 9cf6aa1..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/daemon/utils_ignorelist.h b/src/daemon/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 */
index 6b057f2..cfe993c 100644 (file)
@@ -103,6 +103,8 @@ static void dpdk_stats_default_config(void) {
   for (int i = 0; i < RTE_MAX_ETHPORTS; i++) {
     ec->config.port_name[i][0] = 0;
   }
+  /* Enable all ports by default */
+  ec->config.enabled_port_mask = ~0;
 }
 
 static int dpdk_stats_preinit(void) {
index e2e2e39..fc2a5f1 100644 (file)
@@ -252,14 +252,13 @@ static int cgroup_set(rdt_core_group_t *cg, char *desc, uint64_t *cores,
  *   `item'        Config option containing core groups.
  *   `groups'      Table of core groups to set values in.
  *   `max_groups'  Maximum number of core groups allowed.
- *   `max_core'    Maximum allowed core value.
  *
  * RETURN VALUE
  *   On success, the number of core 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, uint64_t max_core) {
+                              size_t max_groups) {
   int index = 0;
 
   assert(groups != NULL);
@@ -285,14 +284,6 @@ static int oconfig_to_cgroups(oconfig_item_t *item, rdt_core_group_t *groups,
       return (-EINVAL);
     }
 
-    for (int i = 0; i < n; i++) {
-      if (cores[i] > max_core) {
-        ERROR(RDT_PLUGIN ": Core group (%s) contains invalid core id (%d)",
-              item->values[j].value.string, (int)cores[i]);
-        return (-EINVAL);
-      }
-    }
-
     /* set core group info */
     ret = cgroup_set(&groups[index], item->values[j].value.string, cores, n);
     if (ret < 0)
@@ -395,6 +386,15 @@ static int rdt_default_cgroups(void) {
   return g_rdt->pqos_cpu->num_cores;
 }
 
+static int rdt_is_core_id_valid(int core_id) {
+
+  for (int i = 0; i < g_rdt->pqos_cpu->num_cores; i++)
+    if (core_id == g_rdt->pqos_cpu->cores[i].lcore)
+      return 1;
+
+  return 0;
+}
+
 static int rdt_config_cgroups(oconfig_item_t *item) {
   int n = 0;
   enum pqos_mon_event events = 0;
@@ -413,14 +413,27 @@ static int rdt_config_cgroups(oconfig_item_t *item) {
     DEBUG(RDT_PLUGIN ":  [%d]: %s", j, item->values[j].value.string);
   }
 
-  n = oconfig_to_cgroups(item, g_rdt->cgroups, RDT_MAX_CORES,
-                         g_rdt->pqos_cpu->num_cores - 1);
+  n = oconfig_to_cgroups(item, g_rdt->cgroups, g_rdt->pqos_cpu->num_cores);
   if (n < 0) {
     rdt_free_cgroups();
     ERROR(RDT_PLUGIN ": Error parsing core groups configuration.");
     return (-EINVAL);
   }
 
+  /* 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]);
+        rdt_free_cgroups();
+        return (-EINVAL);
+      }
+    }
+  }
+
   if (n == 0) {
     /* create default core groups if "Cores" config option is empty */
     n = rdt_default_cgroups();
index 8c2cd12..d372b87 100644 (file)
@@ -86,7 +86,8 @@ typedef struct ovs_events_ctx_s ovs_events_ctx_t;
  */
 static ovs_events_ctx_t ovs_events_ctx = {
     .mutex = PTHREAD_MUTEX_INITIALIZER,
-    .config = {.ovs_db_node = "localhost", /* use default OVS DB node */
+    .config = {.send_notification = 1,     /* send notification by default */
+               .ovs_db_node = "localhost", /* use default OVS DB node */
                .ovs_db_serv = "6640"}      /* use default OVS DB service */
 };
 
@@ -227,7 +228,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 = 1;
+  _Bool dispatch_values = 0;
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
     if (strcasecmp("SendNotification", child->key) == 0) {
index 1641e87..23b7533 100644 (file)
@@ -349,13 +349,15 @@ static int ovs_stats_update_bridge(yajl_val bridge) {
       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 *ports_arr =
-              YAJL_GET_ARRAY(br_ports->u.array.values[1])->values;
-          size_t ports_num = YAJL_GET_ARRAY(br_ports->u.array.values[1])->len;
-
-          for (size_t i = 0; i < ports_num; i++)
-            ovs_stats_new_port(
-                br, YAJL_GET_STRING(ports_arr[i]->u.array.values[1]));
+          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]));
       }
@@ -537,6 +539,8 @@ static int ovs_stats_update_iface_stats(port_list_t *port, yajl_val stats) {
   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]);
@@ -557,6 +561,8 @@ static int ovs_stats_update_iface_ext_ids(port_list_t *port, yajl_val ext_ids) {
   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) {
index 4f61351..6e472e6 100644 (file)
@@ -32,6 +32,7 @@
  *   Manuel Sanmartin
  *   Clément Stenac <clement.stenac at diwi.org>
  *   Cosmin Ioiart <cioiart at gmail.com>
+ *   Pavel Rochnyack <pavel2000 at ngs.ru>
  **/
 
 #include "collectd.h"
 #endif
 #endif
 
-typedef struct procstat_entry_s {
+#define PROCSTAT_NAME_LEN 256
+typedef struct process_entry_s {
   unsigned long id;
-  unsigned long age;
+  char name[PROCSTAT_NAME_LEN];
 
   unsigned long num_proc;
   unsigned long num_lwp;
+  unsigned long num_fd;
   unsigned long vmem_size;
   unsigned long vmem_rss;
   unsigned long vmem_data;
@@ -188,10 +191,31 @@ typedef struct procstat_entry_s {
   derive_t cswitch_invol;
   _Bool has_cswitch;
 
+  _Bool has_fd;
+} process_entry_t;
+
+typedef struct procstat_entry_s {
+  unsigned long id;
+  unsigned long age;
+
+  derive_t vmem_minflt_counter;
+  derive_t vmem_majflt_counter;
+
+  derive_t cpu_user_counter;
+  derive_t cpu_system_counter;
+
+  /* io data */
+  derive_t io_rchar;
+  derive_t io_wchar;
+  derive_t io_syscr;
+  derive_t io_syscw;
+
+  derive_t cswitch_vol;
+  derive_t cswitch_invol;
+
   struct procstat_entry_s *next;
 } procstat_entry_t;
 
-#define PROCSTAT_NAME_LEN 256
 typedef struct procstat {
   char name[PROCSTAT_NAME_LEN];
 #if HAVE_REGEX_H
@@ -200,6 +224,7 @@ typedef struct procstat {
 
   unsigned long num_proc;
   unsigned long num_lwp;
+  unsigned long num_fd;
   unsigned long vmem_size;
   unsigned long vmem_rss;
   unsigned long vmem_data;
@@ -221,6 +246,9 @@ typedef struct procstat {
   derive_t cswitch_vol;
   derive_t cswitch_invol;
 
+  _Bool report_fd_num;
+  _Bool report_ctx_switch;
+
   struct procstat *next;
   struct procstat_entry_s *instances;
 } procstat_t;
@@ -229,6 +257,7 @@ static procstat_t *list_head_g = NULL;
 
 static _Bool want_init = 1;
 static _Bool report_ctx_switch = 0;
+static _Bool report_fd_num = 0;
 
 #if HAVE_THREAD_INFO
 static mach_port_t port_host_self;
@@ -240,7 +269,7 @@ static mach_msg_type_number_t pset_list_len;
 
 #elif KERNEL_LINUX
 static long pagesize_g;
-static void ps_fill_details(const procstat_t *ps, procstat_entry_t *entry);
+static void ps_fill_details(const procstat_t *ps, process_entry_t *entry);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS &&                                                  \
@@ -265,7 +294,7 @@ int getargs(void *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
 /* put name of process from config to list_head_g tree
  * list_head_g is a list of 'procstat_t' structs with
  * processes names we want to watch */
-static void ps_list_register(const char *name, const char *regexp) {
+static procstat_t *ps_list_register(const char *name, const char *regexp) {
   procstat_t *new;
   procstat_t *ptr;
   int status;
@@ -273,10 +302,20 @@ static void ps_list_register(const char *name, const char *regexp) {
   new = calloc(1, sizeof(*new));
   if (new == NULL) {
     ERROR("processes plugin: ps_list_register: calloc failed.");
-    return;
+    return (NULL);
   }
   sstrncpy(new->name, name, sizeof(new->name));
 
+  new->io_rchar = -1;
+  new->io_wchar = -1;
+  new->io_syscr = -1;
+  new->io_syscw = -1;
+  new->cswitch_vol = -1;
+  new->cswitch_invol = -1;
+
+  new->report_fd_num = report_fd_num;
+  new->report_ctx_switch = report_ctx_switch;
+
 #if HAVE_REGEX_H
   if (regexp != NULL) {
     DEBUG("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp,
@@ -285,7 +324,7 @@ static void ps_list_register(const char *name, const char *regexp) {
     if (new->re == NULL) {
       ERROR("processes plugin: ps_list_register: malloc failed.");
       sfree(new);
-      return;
+      return (NULL);
     }
 
     status = regcomp(new->re, regexp, REG_EXTENDED | REG_NOSUB);
@@ -294,7 +333,7 @@ static void ps_list_register(const char *name, const char *regexp) {
             regexp);
       sfree(new->re);
       sfree(new);
-      return;
+      return (NULL);
     }
   }
 #else
@@ -305,7 +344,7 @@ static void ps_list_register(const char *name, const char *regexp) {
           "has been disabled at compile time.",
           regexp);
     sfree(new);
-    return;
+    return (NULL);
   }
 #endif
 
@@ -320,7 +359,7 @@ static void ps_list_register(const char *name, const char *regexp) {
       sfree(new->re);
 #endif
       sfree(new);
-      return;
+      return (NULL);
     }
 
     if (ptr->next == NULL)
@@ -331,6 +370,8 @@ static void ps_list_register(const char *name, const char *regexp) {
     list_head_g = new;
   else
     ptr->next = new;
+
+  return (new);
 } /* void ps_list_register */
 
 /* try to match name against entry, returns 1 if success */
@@ -375,13 +416,16 @@ static void ps_update_counter(derive_t *group_counter, derive_t *curr_counter,
   else
     curr_value = new_counter - *curr_counter;
 
+  if (*group_counter == -1)
+    *group_counter = 0;
+
   *curr_counter = new_counter;
   *group_counter += curr_value;
 }
 
 /* add process entry to 'instances' of process 'name' (or refresh it) */
 static void ps_list_add(const char *name, const char *cmdline,
-                        procstat_entry_t *entry) {
+                        process_entry_t *entry) {
   procstat_entry_t *pse;
 
   if (entry->id == 0)
@@ -416,35 +460,32 @@ static void ps_list_add(const char *name, const char *cmdline,
     }
 
     pse->age = 0;
-    pse->num_proc = entry->num_proc;
-    pse->num_lwp = entry->num_lwp;
-    pse->vmem_size = entry->vmem_size;
-    pse->vmem_rss = entry->vmem_rss;
-    pse->vmem_data = entry->vmem_data;
-    pse->vmem_code = entry->vmem_code;
-    pse->stack_size = entry->stack_size;
-    pse->io_rchar = entry->io_rchar;
-    pse->io_wchar = entry->io_wchar;
-    pse->io_syscr = entry->io_syscr;
-    pse->io_syscw = entry->io_syscw;
-    pse->cswitch_vol = entry->cswitch_vol;
-    pse->cswitch_invol = entry->cswitch_invol;
-
-    ps->num_proc += pse->num_proc;
-    ps->num_lwp += pse->num_lwp;
-    ps->vmem_size += pse->vmem_size;
-    ps->vmem_rss += pse->vmem_rss;
-    ps->vmem_data += pse->vmem_data;
-    ps->vmem_code += pse->vmem_code;
-    ps->stack_size += pse->stack_size;
-
-    ps->io_rchar += ((pse->io_rchar == -1) ? 0 : pse->io_rchar);
-    ps->io_wchar += ((pse->io_wchar == -1) ? 0 : pse->io_wchar);
-    ps->io_syscr += ((pse->io_syscr == -1) ? 0 : pse->io_syscr);
-    ps->io_syscw += ((pse->io_syscw == -1) ? 0 : pse->io_syscw);
-
-    ps->cswitch_vol += ((pse->cswitch_vol == -1) ? 0 : pse->cswitch_vol);
-    ps->cswitch_invol += ((pse->cswitch_invol == -1) ? 0 : pse->cswitch_invol);
+
+    ps->num_proc += entry->num_proc;
+    ps->num_lwp += entry->num_lwp;
+    ps->num_fd += entry->num_fd;
+    ps->vmem_size += entry->vmem_size;
+    ps->vmem_rss += entry->vmem_rss;
+    ps->vmem_data += entry->vmem_data;
+    ps->vmem_code += entry->vmem_code;
+    ps->stack_size += entry->stack_size;
+
+    if ((entry->io_rchar != -1) && (entry->io_wchar != -1)) {
+      ps_update_counter(&ps->io_rchar, &pse->io_rchar, entry->io_rchar);
+      ps_update_counter(&ps->io_wchar, &pse->io_wchar, entry->io_wchar);
+    }
+
+    if ((entry->io_syscr != -1) && (entry->io_syscw != -1)) {
+      ps_update_counter(&ps->io_syscr, &pse->io_syscr, entry->io_syscr);
+      ps_update_counter(&ps->io_syscw, &pse->io_syscw, entry->io_syscw);
+    }
+
+    if ((entry->cswitch_vol != -1) && (entry->cswitch_vol != -1)) {
+      ps_update_counter(&ps->cswitch_vol, &pse->cswitch_vol,
+                        entry->cswitch_vol);
+      ps_update_counter(&ps->cswitch_invol, &pse->cswitch_invol,
+                        entry->cswitch_invol);
+    }
 
     ps_update_counter(&ps->vmem_minflt_counter, &pse->vmem_minflt_counter,
                       entry->vmem_minflt_counter);
@@ -466,17 +507,12 @@ static void ps_list_reset(void) {
   for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
     ps->num_proc = 0;
     ps->num_lwp = 0;
+    ps->num_fd = 0;
     ps->vmem_size = 0;
     ps->vmem_rss = 0;
     ps->vmem_data = 0;
     ps->vmem_code = 0;
     ps->stack_size = 0;
-    ps->io_rchar = -1;
-    ps->io_wchar = -1;
-    ps->io_syscr = -1;
-    ps->io_syscw = -1;
-    ps->cswitch_vol = -1;
-    ps->cswitch_invol = -1;
 
     pse_prev = NULL;
     pse = ps->instances;
@@ -504,6 +540,20 @@ static void ps_list_reset(void) {
   }   /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
 }
 
+static void ps_tune_instance(oconfig_item_t *ci, procstat_t *ps) {
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *c = ci->children + i;
+
+    if (strcasecmp(c->key, "CollectContextSwitch") == 0)
+      cf_util_get_boolean(c, &ps->report_ctx_switch);
+    else if (strcasecmp(c->key, "CollectFileDescriptor") == 0)
+      cf_util_get_boolean(c, &ps->report_fd_num);
+    else {
+      ERROR("processes plugin: Option `%s' not allowed here.", c->key);
+    }
+  } /* for (ci->children) */
+} /* void ps_tune_instance */
+
 /* put all pre-defined 'Process' names from config to list_head_g tree */
 static int ps_config(oconfig_item_t *ci) {
 #if KERNEL_LINUX
@@ -512,6 +562,8 @@ static int ps_config(oconfig_item_t *ci) {
   const size_t max_procname_len = MAXCOMLEN - 1;
 #endif
 
+  procstat_t *ps;
+
   for (int i = 0; i < ci->children_num; ++i) {
     oconfig_item_t *c = ci->children + i;
 
@@ -523,13 +575,6 @@ static int ps_config(oconfig_item_t *ci) {
         continue;
       }
 
-      if (c->children_num != 0) {
-        WARNING("processes plugin: the `Process' config option "
-                "does not expect any child elements -- ignoring "
-                "content (%i elements) of the <Process '%s'> block.",
-                c->children_num, c->values[0].value.string);
-      }
-
 #if KERNEL_LINUX || KERNEL_SOLARIS || KERNEL_FREEBSD
       if (strlen(c->values[0].value.string) > max_procname_len) {
         WARNING("processes plugin: this platform has a %zu character limit "
@@ -539,7 +584,10 @@ static int ps_config(oconfig_item_t *ci) {
       }
 #endif
 
-      ps_list_register(c->values[0].value.string, NULL);
+      ps = ps_list_register(c->values[0].value.string, NULL);
+
+      if (c->children_num != 0 && ps != NULL)
+        ps_tune_instance(c, ps);
     } else if (strcasecmp(c->key, "ProcessMatch") == 0) {
       if ((c->values_num != 2) || (OCONFIG_TYPE_STRING != c->values[0].type) ||
           (OCONFIG_TYPE_STRING != c->values[1].type)) {
@@ -549,18 +597,15 @@ static int ps_config(oconfig_item_t *ci) {
         continue;
       }
 
-      if (c->children_num != 0) {
-        WARNING("processes plugin: the `ProcessMatch' config option "
-                "does not expect any child elements -- ignoring "
-                "content (%i elements) of the <ProcessMatch '%s' '%s'> "
-                "block.",
-                c->children_num, c->values[0].value.string,
-                c->values[1].value.string);
-      }
+      ps = ps_list_register(c->values[0].value.string,
+                            c->values[1].value.string);
 
-      ps_list_register(c->values[0].value.string, c->values[1].value.string);
+      if (c->children_num != 0 && ps != NULL)
+        ps_tune_instance(c, ps);
     } else if (strcasecmp(c->key, "CollectContextSwitch") == 0) {
       cf_util_get_boolean(c, &report_ctx_switch);
+    } else if (strcasecmp(c->key, "CollectFileDescriptor") == 0) {
+      cf_util_get_boolean(c, &report_fd_num);
     } else {
       ERROR("processes plugin: The `%s' configuration option is not "
             "understood and will be ignored.",
@@ -695,7 +740,14 @@ static void ps_submit_proc_list(procstat_t *ps) {
     plugin_dispatch_values(&vl);
   }
 
-  if (report_ctx_switch) {
+  if (ps->num_fd > 0) {
+    sstrncpy(vl.type, "file_handles", sizeof(vl.type));
+    vl.values[0].gauge = ps->num_fd;
+    vl.values_len = 1;
+    plugin_dispatch_values(&vl);
+  }
+
+  if ((ps->cswitch_vol != -1) && (ps->cswitch_invol != -1)) {
     sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
     sstrncpy(vl.type_instance, "voluntary", sizeof(vl.type_instance));
     vl.values[0].derive = ps->cswitch_vol;
@@ -709,7 +761,7 @@ static void ps_submit_proc_list(procstat_t *ps) {
     plugin_dispatch_values(&vl);
   }
 
-  DEBUG("name = %s; num_proc = %lu; num_lwp = %lu; "
+  DEBUG("name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; "
         "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
         "vmem_code = %lu; "
         "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
@@ -717,8 +769,8 @@ static void ps_submit_proc_list(procstat_t *ps) {
         "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
         "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
         "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";",
-        ps->name, ps->num_proc, ps->num_lwp, ps->vmem_size, ps->vmem_rss,
-        ps->vmem_data, ps->vmem_code, ps->vmem_minflt_counter,
+        ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->vmem_size,
+        ps->vmem_rss, ps->vmem_data, ps->vmem_code, ps->vmem_minflt_counter,
         ps->vmem_majflt_counter, ps->cpu_user_counter, ps->cpu_system_counter,
         ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw, ps->cswitch_vol,
         ps->cswitch_invol);
@@ -741,7 +793,7 @@ static void ps_submit_fork_rate(derive_t value) {
 
 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
 #if KERNEL_LINUX
-static int ps_read_tasks_status(procstat_entry_t *ps) {
+static int ps_read_tasks_status(process_entry_t *ps) {
   char dirname[64];
   DIR *dh;
   char filename[64];
@@ -815,7 +867,7 @@ static int ps_read_tasks_status(procstat_entry_t *ps) {
 } /* int *ps_read_tasks_status */
 
 /* Read data from /proc/pid/status */
-static procstat_t *ps_read_status(long pid, procstat_t *ps) {
+static int ps_read_status(long pid, process_entry_t *ps) {
   FILE *fh;
   char buffer[1024];
   char filename[64];
@@ -828,7 +880,7 @@ static procstat_t *ps_read_status(long pid, procstat_t *ps) {
 
   ssnprintf(filename, sizeof(filename), "/proc/%li/status", pid);
   if ((fh = fopen(filename, "r")) == NULL)
-    return (NULL);
+    return (-1);
 
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
     unsigned long tmp;
@@ -868,10 +920,10 @@ static procstat_t *ps_read_status(long pid, procstat_t *ps) {
   if (threads != 0)
     ps->num_lwp = threads;
 
-  return (ps);
-} /* procstat_t *ps_read_vmem */
+  return (0);
+} /* int *ps_read_status */
 
-static int ps_read_io(procstat_entry_t *ps) {
+static int ps_read_io(process_entry_t *ps) {
   FILE *fh;
   char buffer[1024];
   char filename[64];
@@ -880,8 +932,10 @@ static int ps_read_io(procstat_entry_t *ps) {
   int numfields;
 
   ssnprintf(filename, sizeof(filename), "/proc/%li/io", ps->id);
-  if ((fh = fopen(filename, "r")) == NULL)
+  if ((fh = fopen(filename, "r")) == NULL) {
+    DEBUG("ps_read_io: Failed to open file `%s'", filename);
     return (-1);
+  }
 
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
     derive_t *val = NULL;
@@ -920,32 +974,52 @@ static int ps_read_io(procstat_entry_t *ps) {
   return (0);
 } /* int ps_read_io (...) */
 
-static void ps_fill_details(const procstat_t *ps, procstat_entry_t *entry) {
-  if (entry->has_io == 0 && ps_read_io(entry) != 0) {
-    /* no io data */
-    entry->io_rchar = -1;
-    entry->io_wchar = -1;
-    entry->io_syscr = -1;
-    entry->io_syscw = -1;
+static int ps_count_fd(int pid) {
+  char dirname[64];
+  DIR *dh;
+  struct dirent *ent;
+  int count = 0;
+
+  ssnprintf(dirname, sizeof(dirname), "/proc/%i/fd", pid);
 
-    DEBUG("ps_read_process: not get io data for pid %li", entry->id);
+  if ((dh = opendir(dirname)) == NULL) {
+    DEBUG("Failed to open directory `%s'", dirname);
+    return (-1);
   }
-  entry->has_io = 1;
+  while ((ent = readdir(dh)) != NULL) {
+    if (!isdigit((int)ent->d_name[0]))
+      continue;
+    else
+      count++;
+  }
+  closedir(dh);
 
-  if (report_ctx_switch) {
-    if (entry->has_cswitch == 0 && ps_read_tasks_status(entry) != 0) {
-      entry->cswitch_vol = -1;
-      entry->cswitch_invol = -1;
+  return ((count >= 1) ? count : 1);
+} /* int ps_count_fd (pid) */
 
-      DEBUG("ps_read_tasks_status: not get context "
-            "switch data for pid %li",
-            entry->id);
+static void ps_fill_details(const procstat_t *ps, process_entry_t *entry) {
+  if (entry->has_io == 0) {
+    ps_read_io(entry);
+    entry->has_io = 1;
+  }
+
+  if (ps->report_ctx_switch) {
+    if (entry->has_cswitch == 0) {
+      ps_read_tasks_status(entry);
+      entry->has_cswitch = 1;
+    }
+  }
+
+  if (ps->report_fd_num) {
+    int num_fd;
+    if (entry->has_fd == 0 && (num_fd = ps_count_fd(entry->id)) > 0) {
+      entry->num_fd = num_fd;
     }
-    entry->has_cswitch = 1;
+    entry->has_fd = 1;
   }
 } /* void ps_fill_details (...) */
 
-static int ps_read_process(long pid, procstat_t *ps, char *state) {
+static int ps_read_process(long pid, process_entry_t *ps, char *state) {
   char filename[64];
   char buffer[1024];
 
@@ -967,8 +1041,6 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
 
   ssize_t status;
 
-  memset(ps, 0, sizeof(procstat_t));
-
   ssnprintf(filename, sizeof(filename), "/proc/%li/stat", pid);
 
   status = read_file_contents(filename, buffer, sizeof(buffer) - 1);
@@ -1023,7 +1095,7 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
     ps->num_proc = 0;
   } else {
     ps->num_lwp = strtoul(fields[17], /* endptr = */ NULL, /* base = */ 10);
-    if ((ps_read_status(pid, ps)) == NULL) {
+    if ((ps_read_status(pid, ps)) != 0) {
       /* No VMem data */
       ps->vmem_data = -1;
       ps->vmem_code = -1;
@@ -1068,6 +1140,15 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
   ps->vmem_rss = (unsigned long)vmem_rss;
   ps->stack_size = (unsigned long)stack_size;
 
+  /* no data by default. May be filled by ps_fill_details () */
+  ps->io_rchar = -1;
+  ps->io_wchar = -1;
+  ps->io_syscr = -1;
+  ps->io_syscw = -1;
+
+  ps->cswitch_vol = -1;
+  ps->cswitch_invol = -1;
+
   /* success */
   return (0);
 } /* int ps_read_process (...) */
@@ -1239,7 +1320,7 @@ static char *ps_get_cmdline(long pid,
  * The values for input and ouput chars are calculated "by hand"
  * Added a few "solaris" specific process states as well
  */
-static int ps_read_process(long pid, procstat_t *ps, char *state) {
+static int ps_read_process(long pid, process_entry_t *ps, char *state) {
   char filename[64];
   char f_psinfo[64], f_usage[64];
   char *buffer;
@@ -1304,6 +1385,11 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
   ps->stack_size = myStatus->pr_stksize;
 
   /*
+   * TODO: File descriptor count for Solaris
+   */
+  ps->num_fd = 0;
+
+  /*
    * Calculating input/ouput chars
    * Formula used is total chars / total blocks => chars/block
    * then convert input/output blocks to chars
@@ -1444,7 +1530,7 @@ static int ps_read(void) {
   int blocked = 0;
 
   procstat_t *ps;
-  procstat_entry_t pse;
+  process_entry_t pse;
 
   ps_list_reset();
 
@@ -1526,6 +1612,14 @@ static int ps_read(void) {
         pse.vmem_data = 0;
         pse.vmem_code = 0;
 
+        pse.io_rchar = -1;
+        pse.io_wchar = -1;
+        pse.io_syscr = -1;
+        pse.io_syscw = -1;
+
+        /* File descriptor count not implemented */
+        pse.num_fd = 0;
+
         pse.vmem_minflt_counter = task_events_info.cow_faults;
         pse.vmem_majflt_counter = task_events_info.faults;
 
@@ -1657,8 +1751,7 @@ static int ps_read(void) {
   char cmdline[CMDLINE_BUFFER_SIZE];
 
   int status;
-  procstat_t ps;
-  procstat_entry_t pse;
+  process_entry_t pse;
   char state;
 
   running = sleeping = zombies = stopped = paging = blocked = 0;
@@ -1677,38 +1770,15 @@ static int ps_read(void) {
     if ((pid = atol(ent->d_name)) < 1)
       continue;
 
-    status = ps_read_process(pid, &ps, &state);
+    memset(&pse, 0, sizeof(pse));
+    pse.id = pid;
+
+    status = ps_read_process(pid, &pse, &state);
     if (status != 0) {
       DEBUG("ps_read_process failed: %i", status);
       continue;
     }
 
-    memset(&pse, 0, sizeof(pse));
-    pse.id = pid;
-    pse.age = 0;
-
-    pse.num_proc = ps.num_proc;
-    pse.num_lwp = ps.num_lwp;
-    pse.vmem_size = ps.vmem_size;
-    pse.vmem_rss = ps.vmem_rss;
-    pse.vmem_data = ps.vmem_data;
-    pse.vmem_code = ps.vmem_code;
-    pse.stack_size = ps.stack_size;
-
-    pse.vmem_minflt_counter = ps.vmem_minflt_counter;
-    pse.vmem_majflt_counter = ps.vmem_majflt_counter;
-
-    pse.cpu_user_counter = ps.cpu_user_counter;
-    pse.cpu_system_counter = ps.cpu_system_counter;
-
-    pse.io_rchar = ps.io_rchar;
-    pse.io_wchar = ps.io_wchar;
-    pse.io_syscr = ps.io_syscr;
-    pse.io_syscw = ps.io_syscw;
-
-    pse.cswitch_vol = ps.cswitch_vol;
-    pse.cswitch_invol = ps.cswitch_invol;
-
     switch (state) {
     case 'R':
       running++;
@@ -1730,8 +1800,8 @@ static int ps_read(void) {
       break;
     }
 
-    ps_list_add(ps.name, ps_get_cmdline(pid, ps.name, cmdline, sizeof(cmdline)),
-                &pse);
+    ps_list_add(pse.name,
+                ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
   }
 
   closedir(proc);
@@ -1764,7 +1834,7 @@ static int ps_read(void) {
   struct kinfo_proc *proc_ptr = NULL;
   int count; /* returns number of processes */
 
-  procstat_entry_t pse;
+  process_entry_t pse;
 
   ps_list_reset();
 
@@ -1814,8 +1884,8 @@ static int ps_read(void) {
         }
       } /* if (process has argument list) */
 
+      memset(&pse, 0, sizeof(pse));
       pse.id = procs[i].ki_pid;
-      pse.age = 0;
 
       pse.num_proc = 1;
       pse.num_lwp = procs[i].ki_numthreads;
@@ -1850,6 +1920,9 @@ static int ps_read(void) {
       pse.io_syscr = -1;
       pse.io_syscw = -1;
 
+      /* file descriptor count not implemented */
+      pse.num_fd = 0;
+
       /* context switch counters not implemented */
       pse.cswitch_vol = -1;
       pse.cswitch_invol = -1;
@@ -1911,7 +1984,7 @@ static int ps_read(void) {
   struct kinfo_proc *proc_ptr = NULL;
   int count; /* returns number of processes */
 
-  procstat_entry_t pse;
+  process_entry_t pse;
 
   ps_list_reset();
 
@@ -1963,7 +2036,6 @@ static int ps_read(void) {
 
       memset(&pse, 0, sizeof(pse));
       pse.id = procs[i].p_pid;
-      pse.age = 0;
 
       pse.num_proc = 1;
       pse.num_lwp = 1; /* XXX: accumulate p_tid values for a single p_pid ? */
@@ -1987,6 +2059,9 @@ static int ps_read(void) {
       pse.io_syscr = -1;
       pse.io_syscw = -1;
 
+      /* file descriptor count not implemented */
+      pse.num_fd = 0;
+
       /* context switch counters not implemented */
       pse.cswitch_vol = -1;
       pse.cswitch_invol = -1;
@@ -2045,7 +2120,7 @@ static int ps_read(void) {
   pid_t pindex = 0;
   int nprocs;
 
-  procstat_entry_t pse;
+  process_entry_t pse;
 
   ps_list_reset();
   while ((nprocs = getprocs64(procentry, sizeof(struct procentry64),
@@ -2085,8 +2160,9 @@ static int ps_read(void) {
         }
       }
 
+      memset(&pse, 0, sizeof(pse));
+
       pse.id = procentry[i].pi_pid;
-      pse.age = 0;
       pse.num_lwp = procentry[i].pi_thcount;
       pse.num_proc = 1;
 
@@ -2127,7 +2203,6 @@ static int ps_read(void) {
       pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
                              procentry[i].pi_ru.ru_utime.tv_usec / 1000;
 
-      pse.cpu_system = 0;
       /* tv_usec is nanosec ??? */
       pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
                                procentry[i].pi_ru.ru_stime.tv_usec / 1000;
@@ -2137,7 +2212,7 @@ static int ps_read(void) {
 
       pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
       pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
-      /* Not supported */
+      /* Not supported/implemented */
       pse.vmem_data = 0;
       pse.vmem_code = 0;
       pse.stack_size = 0;
@@ -2147,6 +2222,8 @@ static int ps_read(void) {
       pse.io_syscr = -1;
       pse.io_syscw = -1;
 
+      pse.num_fd = 0;
+
       pse.cswitch_vol = -1;
       pse.cswitch_invol = -1;
 
@@ -2199,8 +2276,7 @@ static int ps_read(void) {
 
   while ((ent = readdir(proc)) != NULL) {
     long pid;
-    struct procstat ps;
-    procstat_entry_t pse;
+    process_entry_t pse;
     char *endptr;
 
     if (!isdigit((int)ent->d_name[0]))
@@ -2210,38 +2286,15 @@ static int ps_read(void) {
     if (*endptr != 0) /* value didn't completely parse as a number */
       continue;
 
-    status = ps_read_process(pid, &ps, &state);
+    memset(&pse, 0, sizeof(pse));
+    pse.id = pid;
+
+    status = ps_read_process(pid, &pse, &state);
     if (status != 0) {
       DEBUG("ps_read_process failed: %i", status);
       continue;
     }
 
-    memset(&pse, 0, sizeof(pse));
-    pse.id = pid;
-    pse.age = 0;
-
-    pse.num_proc = ps.num_proc;
-    pse.num_lwp = ps.num_lwp;
-    pse.vmem_size = ps.vmem_size;
-    pse.vmem_rss = ps.vmem_rss;
-    pse.vmem_data = ps.vmem_data;
-    pse.vmem_code = ps.vmem_code;
-    pse.stack_size = ps.stack_size;
-
-    pse.vmem_minflt_counter = ps.vmem_minflt_counter;
-    pse.vmem_majflt_counter = ps.vmem_majflt_counter;
-
-    pse.cpu_user_counter = ps.cpu_user_counter;
-    pse.cpu_system_counter = ps.cpu_system_counter;
-
-    pse.io_rchar = ps.io_rchar;
-    pse.io_wchar = ps.io_wchar;
-    pse.io_syscr = ps.io_syscr;
-    pse.io_syscw = ps.io_syscw;
-
-    pse.cswitch_vol = -1;
-    pse.cswitch_invol = -1;
-
     switch (state) {
     case 'R':
       running++;
@@ -2269,8 +2322,8 @@ static int ps_read(void) {
       break;
     }
 
-    ps_list_add(ps.name, ps_get_cmdline(pid, ps.name, cmdline, sizeof(cmdline)),
-                &pse);
+    ps_list_add(pse.name,
+                ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
   } /* while(readdir) */
   closedir(proc);
 
index 1e54b97..2a14f0e 100644 (file)
@@ -181,7 +181,9 @@ static int values_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */
 
 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) {
+                                  int store_rates,
+                                  char const *const *http_attrs,
+                                  size_t http_attrs_num) {
   char temp[512];
   size_t offset = 0;
   int status;
@@ -231,6 +233,11 @@ static int value_list_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */
     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);
@@ -252,11 +259,13 @@ static int value_list_to_kairosdb(char *buffer, size_t buffer_size, /* {{{ */
 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) {
+    const value_list_t *vl, int store_rates, size_t temp_size,
+    char const *const *http_attrs, size_t http_attrs_num) {
   char temp[temp_size];
   int status;
 
-  status = value_list_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates);
+  status = value_list_to_kairosdb(temp, sizeof(temp), ds, vl, store_rates,
+                                  http_attrs, http_attrs_num);
   if (status != 0)
     return (status);
   temp_size = strlen(temp);
@@ -324,7 +333,8 @@ int format_kairosdb_finalize(char *buffer, /* {{{ */
 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) {
+                               int store_rates, char const *const *http_attrs,
+                               size_t http_attrs_num) {
   if ((buffer == NULL) || (ret_buffer_fill == NULL) ||
       (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL))
     return (-EINVAL);
@@ -334,5 +344,7 @@ int format_kairosdb_value_list(char *buffer, /* {{{ */
 
   return (format_kairosdb_value_list_nocheck(
       buffer, ret_buffer_fill, ret_buffer_free, ds, vl, store_rates,
-      (*ret_buffer_free) - 2));
+      (*ret_buffer_free) - 2, http_attrs, http_attrs_num));
 } /* }}} int format_kairosdb_value_list */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
index 9174318..b6aa39d 100644 (file)
@@ -39,7 +39,9 @@ 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);
+                               const value_list_t *vl, int store_rates,
+                               char const *const *http_attrs,
+                               size_t http_attrs_num);
 int format_kairosdb_finalize(char *buffer, size_t *ret_buffer_fill,
                              size_t *ret_buffer_free);
 
diff --git a/src/utils_ignorelist.c b/src/utils_ignorelist.c
new file mode 100644 (file)
index 0000000..9cf6aa1
--- /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 "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
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 */
index 57da628..65e6679 100644 (file)
@@ -330,6 +330,9 @@ static yajl_gen_status ovs_yajl_gen_val(yajl_gen jgen, yajl_val jval) {
   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))
@@ -566,7 +569,11 @@ static int ovs_db_json_data_process(ovs_db_t *pdb, const char *data,
 
   /* get method name */
   if ((jval = yajl_tree_get(jnode, method_path, yajl_t_string)) != NULL) {
-    method = YAJL_GET_STRING(jval);
+    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)
index 1113113..140b488 100644 (file)
@@ -1795,6 +1795,18 @@ 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 int refresh_lists(struct lv_read_instance *inst) {
   struct lv_read_state *state = &inst->read_state;
   int n;
@@ -1808,6 +1820,10 @@ static int refresh_lists(struct lv_read_instance *inst) {
   lv_clean_read_state(state);
 
   if (n > 0) {
+#ifdef HAVE_LIST_ALL_DOMAINS
+    virDomainPtr *domains;
+    n = virConnectListAllDomains (conn, &domains, VIR_CONNECT_LIST_DOMAINS_ACTIVE);
+#else
     int *domids;
 
     /* Get list of domains. */
@@ -1818,15 +1834,18 @@ static int refresh_lists(struct lv_read_instance *inst) {
     }
 
     n = virConnectListDomains(conn, domids, n);
+#endif
+
     if (n < 0) {
       VIRT_ERROR(conn, "reading list of domains");
+#ifndef HAVE_LIST_ALL_DOMAINS
       sfree(domids);
+#endif
       return -1;
     }
 
     /* Fetch each domain and add it to the list, unless ignore. */
     for (int i = 0; i < n; ++i) {
-      virDomainPtr dom = NULL;
       const char *name;
       char *xml = NULL;
       xmlDocPtr xml_doc = NULL;
@@ -1836,12 +1855,17 @@ static int refresh_lists(struct lv_read_instance *inst) {
       virDomainInfo info;
       int status;
 
+#ifdef HAVE_LIST_ALL_DOMAINS
+      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;
       }
+#endif
 
       name = virDomainGetName(dom);
       if (name == NULL) {
@@ -1982,7 +2006,11 @@ static int refresh_lists(struct lv_read_instance *inst) {
       sfree(xml);
     }
 
+#ifdef HAVE_LIST_ALL_DOMAINS
+    sfree (domains);
+#else
     sfree(domids);
+#endif
   }
 
   DEBUG(PLUGIN_NAME " plugin#%s: refreshing"
index c33ab54..c6efe07 100644 (file)
@@ -81,6 +81,9 @@ struct wh_callback_s {
 };
 typedef struct wh_callback_s wh_callback_t;
 
+static char **http_attrs;
+static size_t http_attrs_num;
+
 static void wh_log_http_error(wh_callback_t *cb) {
   if (!cb->log_http_error)
     return;
@@ -468,9 +471,9 @@ static int wh_write_kairosdb(const data_set_t *ds,
     }
   }
 
-  status = format_kairosdb_value_list(cb->send_buffer, &cb->send_buffer_fill,
-                                      &cb->send_buffer_free, ds, vl,
-                                      cb->store_rates);
+  status = format_kairosdb_value_list(
+      cb->send_buffer, &cb->send_buffer_fill, &cb->send_buffer_free, ds, vl,
+      cb->store_rates, (char const *const *)http_attrs, http_attrs_num);
   if (status == -ENOMEM) {
     status = wh_flush_nolock(/* timeout = */ 0, cb);
     if (status != 0) {
@@ -479,9 +482,9 @@ static int wh_write_kairosdb(const data_set_t *ds,
       return (status);
     }
 
-    status = format_kairosdb_value_list(cb->send_buffer, &cb->send_buffer_fill,
-                                        &cb->send_buffer_free, ds, vl,
-                                        cb->store_rates);
+    status = format_kairosdb_value_list(
+        cb->send_buffer, &cb->send_buffer_fill, &cb->send_buffer_free, ds, vl,
+        cb->store_rates, (char const *const *)http_attrs, http_attrs_num);
   }
   if (status != 0) {
     pthread_mutex_unlock(&cb->send_lock);
@@ -703,7 +706,34 @@ static int wh_config_node(oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_boolean(child, &cb->log_http_error);
     else if (strcasecmp("Header", child->key) == 0)
       status = wh_config_append_string("Header", &cb->headers, child);
-    else {
+    else if (strcasecmp("Attribute", child->key) == 0) {
+      char *key = NULL;
+      char *val = NULL;
+
+      if (child->values_num != 2) {
+        WARNING("write_http plugin: Attribute need both a key and a value.");
+        break;
+      }
+      if (child->values[0].type != OCONFIG_TYPE_STRING ||
+          child->values[1].type != OCONFIG_TYPE_STRING) {
+        WARNING("write_http plugin: Attribute needs string arguments.");
+        break;
+      }
+      if ((key = strdup(child->values[0].value.string)) == NULL) {
+        WARNING("cannot allocate memory for attribute key.");
+        break;
+      }
+      if ((val = strdup(child->values[1].value.string)) == NULL) {
+        WARNING("cannot allocate memory for attribute value.");
+        sfree(key);
+        break;
+      }
+      strarray_add(&http_attrs, &http_attrs_num, key);
+      strarray_add(&http_attrs, &http_attrs_num, val);
+      DEBUG("write_http plugin: got attribute: %s => %s", key, val);
+      sfree(key);
+      sfree(val);
+    } else {
       ERROR("write_http plugin: Invalid configuration "
             "option: %s.",
             child->key);
index 10f7832..66dc8e0 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (C) 2010-2013  Florian Forster
  * Copyright (C) 2010       Akkarit Sangpetch
  * Copyright (C) 2012       Chris Lundquist
+ * Copyright (C) 2017       Saikrishna Arcot
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -26,6 +27,7 @@
  *   Florian Forster <octo at collectd.org>
  *   Akkarit Sangpetch <asangpet at andrew.cmu.edu>
  *   Chris Lundquist <clundquist at bluebox.net>
+ *   Saikrishna Arcot <saiarcot895 at gmail.com>
  **/
 
 #include "collectd.h"
 #include "plugin.h"
 #include "utils_cache.h"
 
-#define MONGO_HAVE_STDINT 1
-#include <mongo.h>
-
-#if (MONGO_MAJOR == 0) && (MONGO_MINOR < 8)
-#define bson_alloc() bson_create()
-#define bson_dealloc(b) bson_dispose(b)
-#endif
+#include <mongoc.h>
 
 struct wm_node_s {
   char name[DATA_MAX_NAME_LEN];
@@ -55,8 +51,10 @@ struct wm_node_s {
   char *passwd;
 
   _Bool store_rates;
+  _Bool connected;
 
-  mongo conn[1];
+  mongoc_client_t *client;
+  mongoc_database_t *database;
   pthread_mutex_t lock;
 };
 typedef struct wm_node_s wm_node_t;
@@ -64,170 +62,235 @@ typedef struct wm_node_s wm_node_t;
 /*
  * Functions
  */
-static bson *wm_create_bson(const data_set_t *ds, /* {{{ */
-                            const value_list_t *vl, _Bool store_rates) {
-  bson *ret;
+static bson_t *wm_create_bson(const data_set_t *ds, /* {{{ */
+                              const value_list_t *vl, _Bool store_rates) {
+  bson_t *ret;
+  bson_t subarray;
   gauge_t *rates;
 
-  ret = bson_alloc(); /* matched by bson_dealloc() */
-  if (ret == NULL) {
-    ERROR("write_mongodb plugin: bson_create failed.");
-    return (NULL);
+  ret = bson_new();
+  if (!ret) {
+    ERROR("write_mongodb plugin: bson_new failed.");
+    return NULL;
   }
 
   if (store_rates) {
     rates = uc_get_rate(ds, vl);
     if (rates == NULL) {
       ERROR("write_mongodb plugin: uc_get_rate() failed.");
-      return (NULL);
+      bson_free(ret);
+      return NULL;
     }
   } else {
     rates = NULL;
   }
 
-  bson_init(ret); /* matched by bson_destroy() */
-  bson_append_date(ret, "time", (bson_date_t)CDTIME_T_TO_MS(vl->time));
-  bson_append_string(ret, "host", vl->host);
-  bson_append_string(ret, "plugin", vl->plugin);
-  bson_append_string(ret, "plugin_instance", vl->plugin_instance);
-  bson_append_string(ret, "type", vl->type);
-  bson_append_string(ret, "type_instance", vl->type_instance);
+  BSON_APPEND_DATE_TIME(ret, "timestamp", CDTIME_T_TO_MS(vl->time));
+  BSON_APPEND_UTF8(ret, "host", vl->host);
+  BSON_APPEND_UTF8(ret, "plugin", vl->plugin);
+  BSON_APPEND_UTF8(ret, "plugin_instance", vl->plugin_instance);
+  BSON_APPEND_UTF8(ret, "type", vl->type);
+  BSON_APPEND_UTF8(ret, "type_instance", vl->type_instance);
 
-  bson_append_start_array(ret, "values"); /* {{{ */
+  BSON_APPEND_ARRAY_BEGIN(ret, "values", &subarray); /* {{{ */
   for (int i = 0; i < ds->ds_num; i++) {
     char key[16];
 
     ssnprintf(key, sizeof(key), "%i", i);
 
     if (ds->ds[i].type == DS_TYPE_GAUGE)
-      bson_append_double(ret, key, vl->values[i].gauge);
+      BSON_APPEND_DOUBLE(&subarray, key, vl->values[i].gauge);
     else if (store_rates)
-      bson_append_double(ret, key, (double)rates[i]);
+      BSON_APPEND_DOUBLE(&subarray, key, (double)rates[i]);
     else if (ds->ds[i].type == DS_TYPE_COUNTER)
-      bson_append_long(ret, key, vl->values[i].counter);
+      BSON_APPEND_INT64(&subarray, key, vl->values[i].counter);
     else if (ds->ds[i].type == DS_TYPE_DERIVE)
-      bson_append_long(ret, key, vl->values[i].derive);
+      BSON_APPEND_INT64(&subarray, key, vl->values[i].derive);
     else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
-      bson_append_long(ret, key, vl->values[i].absolute);
-    else
-      assert(23 == 42);
+      BSON_APPEND_INT64(&subarray, key, vl->values[i].absolute);
+    else {
+      ERROR("write_mongodb plugin: Unknown ds_type %d for index %d",
+            ds->ds[i].type, i);
+      bson_free(ret);
+      return NULL;
+    }
   }
-  bson_append_finish_array(ret); /* }}} values */
+  bson_append_array_end(ret, &subarray); /* }}} values */
 
-  bson_append_start_array(ret, "dstypes"); /* {{{ */
+  BSON_APPEND_ARRAY_BEGIN(ret, "dstypes", &subarray); /* {{{ */
   for (int i = 0; i < ds->ds_num; i++) {
     char key[16];
 
     ssnprintf(key, sizeof(key), "%i", i);
 
     if (store_rates)
-      bson_append_string(ret, key, "gauge");
+      BSON_APPEND_UTF8(&subarray, key, "gauge");
     else
-      bson_append_string(ret, key, DS_TYPE_TO_STRING(ds->ds[i].type));
+      BSON_APPEND_UTF8(&subarray, key, DS_TYPE_TO_STRING(ds->ds[i].type));
   }
-  bson_append_finish_array(ret); /* }}} dstypes */
+  bson_append_array_end(ret, &subarray); /* }}} dstypes */
 
-  bson_append_start_array(ret, "dsnames"); /* {{{ */
+  BSON_APPEND_ARRAY_BEGIN(ret, "dsnames", &subarray); /* {{{ */
   for (int i = 0; i < ds->ds_num; i++) {
     char key[16];
 
     ssnprintf(key, sizeof(key), "%i", i);
-    bson_append_string(ret, key, ds->ds[i].name);
+    BSON_APPEND_UTF8(&subarray, key, ds->ds[i].name);
   }
-  bson_append_finish_array(ret); /* }}} dsnames */
-
-  bson_finish(ret);
+  bson_append_array_end(ret, &subarray); /* }}} dsnames */
 
   sfree(rates);
-  return (ret);
+
+  size_t error_location;
+  if (!bson_validate(ret, BSON_VALIDATE_UTF8, &error_location)) {
+    ERROR("write_mongodb plugin: Error in generated BSON document "
+        "at byte %zu", error_location);
+    bson_free(ret);
+    return NULL;
+  }
+
+  return ret;
 } /* }}} bson *wm_create_bson */
 
-static int wm_write(const data_set_t *ds, /* {{{ */
-                    const value_list_t *vl, user_data_t *ud) {
-  wm_node_t *node = ud->data;
-  char collection_name[512];
-  bson *bson_record;
-  int status;
+static int wm_initialize(wm_node_t *node) /* {{{ */
+{
+  char *uri;
+  size_t uri_length;
+  char const *format_string;
 
-  ssnprintf(collection_name, sizeof(collection_name), "collectd.%s",
-            vl->plugin);
+  if (node->connected) {
+    return 0;
+  }
 
-  bson_record = wm_create_bson(ds, vl, node->store_rates);
-  if (bson_record == NULL)
-    return (ENOMEM);
+  INFO("write_mongodb plugin: Connecting to [%s]:%i",
+       (node->host != NULL) ? node->host : "localhost",
+       (node->port != 0) ? node->port : MONGOC_DEFAULT_PORT);
+
+  if ((node->db != NULL) && (node->user != NULL) && (node->passwd != NULL)) {
+    format_string = "mongodb://%s:%s@%s:%d/?authSource=%s";
+    uri_length = strlen(format_string) + strlen(node->user) +
+                 strlen(node->passwd) + strlen(node->host) + 5 +
+                 strlen(node->db) + 1;
+    if ((uri = calloc(sizeof(char), uri_length)) == NULL) {
+      ERROR("write_mongodb plugin: Not enough memory to assemble "
+            "authentication string.");
+      mongoc_client_destroy(node->client);
+      node->client = NULL;
+      node->connected = 0;
+      return -1;
+    }
+    ssnprintf(uri, uri_length, format_string, node->user, node->passwd,
+              node->host, node->port, node->db);
 
-  pthread_mutex_lock(&node->lock);
+    node->client = mongoc_client_new(uri);
+    if (!node->client) {
+      ERROR("write_mongodb plugin: Authenticating to [%s]%i for database "
+            "\"%s\" as user \"%s\" failed.",
+            (node->host != NULL) ? node->host : "localhost",
+            (node->port != 0) ? node->port : MONGOC_DEFAULT_PORT, node->db,
+            node->user);
+      node->connected = 0;
+      sfree(uri);
+      return -1;
+    }
+  } else {
+    format_string = "mongodb://%s:%d";
+    uri_length = strlen(format_string) + strlen(node->host) + 5 + 1;
+    if ((uri = calloc(sizeof(char), uri_length)) == NULL) {
+      ERROR("write_mongodb plugin: Not enough memory to assemble "
+            "authentication string.");
+      mongoc_client_destroy(node->client);
+      node->client = NULL;
+      node->connected = 0;
+      return -1;
+    }
+    snprintf(uri, uri_length, format_string, node->host, node->port);
 
-  if (!mongo_is_connected(node->conn)) {
-    INFO("write_mongodb plugin: Connecting to [%s]:%i",
-         (node->host != NULL) ? node->host : "localhost",
-         (node->port != 0) ? node->port : MONGO_DEFAULT_PORT);
-    status = mongo_connect(node->conn, node->host, node->port);
-    if (status != MONGO_OK) {
+    node->client = mongoc_client_new(uri);
+    if (!node->client) {
       ERROR("write_mongodb plugin: Connecting to [%s]:%i failed.",
             (node->host != NULL) ? node->host : "localhost",
-            (node->port != 0) ? node->port : MONGO_DEFAULT_PORT);
-      mongo_destroy(node->conn);
-      pthread_mutex_unlock(&node->lock);
-      return (-1);
+            (node->port != 0) ? node->port : MONGOC_DEFAULT_PORT);
+      node->connected = 0;
+      sfree(uri);
+      return -1;
     }
+  }
+  sfree(uri);
+
+  node->database = mongoc_client_get_database(node->client, "collectd");
+  if (!node->database) {
+    ERROR("write_mongodb plugin: error creating/getting database");
+    mongoc_client_destroy(node->client);
+    node->client = NULL;
+    node->connected = 0;
+    return -1;
+  }
 
-    if ((node->db != NULL) && (node->user != NULL) && (node->passwd != NULL)) {
-      status = mongo_cmd_authenticate(node->conn, node->db, node->user,
-                                      node->passwd);
-      if (status != MONGO_OK) {
-        ERROR("write_mongodb plugin: Authenticating to [%s]%i for database "
-              "\"%s\" as user \"%s\" failed.",
-              (node->host != NULL) ? node->host : "localhost",
-              (node->port != 0) ? node->port : MONGO_DEFAULT_PORT, node->db,
-              node->user);
-        mongo_destroy(node->conn);
-        pthread_mutex_unlock(&node->lock);
-        return (-1);
-      }
-    }
+  node->connected = 1;
+  return 0;
+} /* }}} int wm_initialize */
 
-    if (node->timeout > 0) {
-      status = mongo_set_op_timeout(node->conn, node->timeout);
-      if (status != MONGO_OK) {
-        WARNING("write_mongodb plugin: mongo_set_op_timeout(%i) failed: %s",
-                node->timeout, node->conn->errstr);
-      }
-    }
+static int wm_write(const data_set_t *ds, /* {{{ */
+                    const value_list_t *vl, user_data_t *ud) {
+  wm_node_t *node = ud->data;
+  mongoc_collection_t *collection = NULL;
+  bson_t *bson_record;
+  bson_error_t error;
+  int status;
+
+  bson_record = wm_create_bson(ds, vl, node->store_rates);
+  if (!bson_record) {
+    ERROR("write_mongodb plugin: error making insert bson");
+    return -1;
   }
 
-  /* Assert if the connection has been established */
-  assert(mongo_is_connected(node->conn));
-
-#if MONGO_MINOR >= 6
-  /* There was an API change in 0.6.0 as linked below */
-  /* https://github.com/mongodb/mongo-c-driver/blob/master/HISTORY.md */
-  status = mongo_insert(node->conn, collection_name, bson_record, NULL);
-#else
-  status = mongo_insert(node->conn, collection_name, bson_record);
-#endif
-
-  if (status != MONGO_OK) {
-    ERROR("write_mongodb plugin: error inserting record: %d", node->conn->err);
-    if (node->conn->err != MONGO_BSON_INVALID)
-      ERROR("write_mongodb plugin: %s", node->conn->errstr);
-    else
-      ERROR("write_mongodb plugin: Invalid BSON structure, error = %#x",
-            (unsigned int)bson_record->err);
+  pthread_mutex_lock(&node->lock);
+  if (wm_initialize(node) < 0) {
+    ERROR("write_mongodb plugin: error making connection to server");
+    pthread_mutex_unlock(&node->lock);
+    bson_free(bson_record);
+    return -1;
+  }
 
-    /* Disconnect except on data errors. */
-    if ((node->conn->err != MONGO_BSON_INVALID) &&
-        (node->conn->err != MONGO_BSON_NOT_FINISHED))
-      mongo_destroy(node->conn);
+  collection =
+      mongoc_client_get_collection(node->client, "collectd", vl->plugin);
+  if (!collection) {
+    ERROR("write_mongodb plugin: error creating/getting collection");
+    mongoc_database_destroy(node->database);
+    mongoc_client_destroy(node->client);
+    node->database = NULL;
+    node->client = NULL;
+    node->connected = 0;
+    pthread_mutex_unlock(&node->lock);
+    bson_free(bson_record);
+    return -1;
   }
 
-  pthread_mutex_unlock(&node->lock);
+  status = mongoc_collection_insert(collection, MONGOC_INSERT_NONE, bson_record,
+                                    NULL, &error);
+
+  if (!status) {
+    ERROR("write_mongodb plugin: error inserting record: %s", error.message);
+    mongoc_database_destroy(node->database);
+    mongoc_client_destroy(node->client);
+    node->database = NULL;
+    node->client = NULL;
+    node->connected = 0;
+    pthread_mutex_unlock(&node->lock);
+    bson_free(bson_record);
+    mongoc_collection_destroy(collection);
+    return -1;
+  }
 
   /* free our resource as not to leak memory */
-  bson_destroy(bson_record); /* matches bson_init() */
-  bson_dealloc(bson_record); /* matches bson_alloc() */
+  mongoc_collection_destroy(collection);
 
-  return (0);
+  pthread_mutex_unlock(&node->lock);
+
+  bson_free(bson_record);
+
+  return 0;
 } /* }}} int wm_write */
 
 static void wm_config_free(void *ptr) /* {{{ */
@@ -237,8 +300,11 @@ static void wm_config_free(void *ptr) /* {{{ */
   if (node == NULL)
     return;
 
-  if (mongo_is_connected(node->conn))
-    mongo_destroy(node->conn);
+  mongoc_database_destroy(node->database);
+  mongoc_client_destroy(node->client);
+  node->database = NULL;
+  node->client = NULL;
+  node->connected = 0;
 
   sfree(node->host);
   sfree(node);
@@ -252,7 +318,7 @@ static int wm_config_node(oconfig_item_t *ci) /* {{{ */
   node = calloc(1, sizeof(*node));
   if (node == NULL)
     return (ENOMEM);
-  mongo_init(node->conn);
+  mongoc_init();
   node->host = NULL;
   node->store_rates = 1;
   pthread_mutex_init(&node->lock, /* attr = */ NULL);