Merge pull request #2733 from elfiesmelfie/feat_pcie_aer
authorPavel Rochnyak <pavel2000@ngs.ru>
Thu, 5 Jul 2018 09:03:17 +0000 (16:03 +0700)
committerGitHub <noreply@github.com>
Thu, 5 Jul 2018 09:03:17 +0000 (16:03 +0700)
New plugin to read PCIe errors

56 files changed:
Makefile.am
configure.ac
src/bind.c
src/collectd-snmp.pod
src/collectd-tg.c
src/collectd.conf.in
src/collectd.conf.pod
src/curl.c
src/curl_json.c
src/curl_xml.c
src/daemon/common.c
src/daemon/configfile.c
src/daemon/plugin.c
src/daemon/plugin.h
src/daemon/plugin_mock.c
src/daemon/utils_cache_mock.c
src/dbi.c
src/dpdkevents.c
src/filecount.c
src/gmond.c
src/memcachec.c
src/modbus.c
src/netapp.c
src/network.c
src/notify_email.c
src/ntpd.c
src/oracle.c
src/perl.c
src/postgresql.c
src/powerdns.c
src/protocols.c
src/redis.c
src/rrdtool.c
src/snmp.c
src/snmp_agent.c
src/snmp_agent_test.c [new file with mode: 0644]
src/statsd.c
src/table.c
src/tail.c
src/tail_csv.c
src/threshold.c
src/types.db
src/utils_db_query.c
src/utils_db_query.h
src/utils_dpdk.c
src/utils_format_graphite.c
src/utils_format_graphite.h
src/utils_format_graphite_test.c
src/utils_rrdcreate.c
src/utils_tail_match.c
src/utils_tail_match.h
src/write_graphite.c
src/write_kafka.c
src/write_redis.c
src/write_riemann.c
src/write_sensu.c

index effbdd4..4d32f74 100644 (file)
@@ -1576,7 +1576,7 @@ pkglib_LTLIBRARIES += snmp.la
 snmp_la_SOURCES = src/snmp.c
 snmp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBNETSNMP_CPPFLAGS)
 snmp_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBNETSNMP_LDFLAGS)
-snmp_la_LIBADD = $(BUILD_WITH_LIBNETSNMP_LIBS)
+snmp_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBNETSNMP_LIBS)
 endif
 
 if BUILD_PLUGIN_SNMP_AGENT
@@ -1585,6 +1585,23 @@ snmp_agent_la_SOURCES = src/snmp_agent.c
 snmp_agent_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBNETSNMPAGENT_CPPFLAGS)
 snmp_agent_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBNETSNMPAGENT_LDFLAGS)
 snmp_agent_la_LIBADD = $(BUILD_WITH_LIBNETSNMPAGENT_LIBS)
+
+test_plugin_snmp_agent_SOURCES = src/snmp_agent_test.c \
+                                 src/daemon/utils_avltree.c \
+                                 src/daemon/utils_llist.c \
+                                 src/daemon/configfile.c \
+                                 src/daemon/types_list.c
+test_plugin_snmp_agent_CPPFLAGS = $(AM_CPPFLAGS) \
+       $(BUILD_WITH_LIBNETSNMPAGENT_CPPFLAGS)
+test_plugin_snmp_agent_LDFLAGS = $(PLUGIN_LDFLAGS) \
+       $(BUILD_WITH_LIBNETSNMPAGENT_LDFLAGS)
+test_plugin_snmp_agent_LDADD = liboconfig.la libplugin_mock.la \
+       $(BUILD_WITH_LIBNETSNMPAGENT_LIBS) $(BUILD_WITH_LIBNETSNMP_LIBS)
+
+check_PROGRAMS += test_plugin_snmp_agent
+TESTS += test_plugin_snmp_agent
+
+
 endif
 
 if BUILD_PLUGIN_STATSD
index 7049975..a91b755 100644 (file)
@@ -773,6 +773,8 @@ AC_FUNC_STRERROR_R
 
 SAVE_CFLAGS="$CFLAGS"
 CFLAGS="-Wall -Werror"
+SAVE_LDFAGS="$LDFLAGS"
+LDFLAGS=""
 
 AC_CACHE_CHECK([for strtok_r],
   [c_cv_have_strtok_r_default],
@@ -845,6 +847,7 @@ if test "x$c_cv_have_strtok_r_default" = "xno"; then
 fi
 
 CFLAGS="$SAVE_CFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
 if test "x$c_cv_have_strtok_r_reentrant" = "xyes"; then
   CFLAGS="$CFLAGS -D_REENTRANT=1"
 fi
@@ -3820,7 +3823,7 @@ if test "x$with_libnetsnmp" = "xyes"; then
   LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags"
 
   AC_CHECK_LIB([netsnmp], [init_snmp],
-    [with_libnetsmp="yes"],
+    [with_libnetsnmp="yes"],
     [with_libnetsnmp="no (libnetsnmp not found)"]
   )
 
@@ -3828,6 +3831,62 @@ if test "x$with_libnetsnmp" = "xyes"; then
 fi
 
 if test "x$with_libnetsnmp" = "xyes"; then
+  SAVE_LDFLAGS="$LDFLAGS"
+  LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags"
+
+  AC_CHECK_LIB([netsnmp], [netsnmp_get_version],
+    [with_libnetsnmp="yes"],
+    [with_libnetsnmp="no (couldn't get libnetsnmp version)"]
+  )
+
+  LDFLAGS="$SAVE_LDFLAGS"
+fi
+
+if test "x$with_libnetsnmp" = "xyes"; then
+  SAVE_CPPFLAGS="$CPPFLAGS"
+  SAVE_LDFLAGS="$LDFLAGS"
+  SAVE_LIBS="$LIBS"
+  CPPFLAGS="$CPPFLAGS $with_libnetsnmp_cppflags -Wall -Werror"
+  LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags"
+  LIBS="$LIBS -lnetsnmp"
+
+  AC_CACHE_CHECK([whether netsnmp library has old API],
+    [c_cv_have_netsnmp_old_api],
+    [
+      AC_LINK_IFELSE(
+        [
+          AC_LANG_PROGRAM(
+            [[
+              #include <net-snmp/net-snmp-config.h>
+              #include <net-snmp/net-snmp-includes.h>
+            ]],
+            [[
+              netsnmp_variable_list *key = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);;
+              int val;
+              u_char type = ASN_INTEGER;
+              snmp_set_var_value(key, &val, sizeof(val));
+              snmp_set_var_typed_value(key, type, &val, sizeof(val));
+              return 0;
+            ]]
+          )
+        ],
+        [c_cv_have_netsnmp_old_api="no"],
+        [c_cv_have_netsnmp_old_api="yes"]
+      )
+    ]
+  )
+
+  if test "x$c_cv_have_netsnmp_old_api" = "xyes"; then
+    AC_DEFINE([HAVE_NETSNMP_OLD_API], [1],
+              ["Define 1 if you have old netsnmp API]")
+  fi
+
+  CPPFLAGS="$SAVE_CPPFLAGS"
+  LDFLAGS="$SAVE_LDFLAGS"
+  LIBS="$SAVE_LIBS"
+fi
+
+if test "x$with_libnetsnmp" = "xyes"; then
   BUILD_WITH_LIBNETSNMP_CPPFLAGS="$with_libnetsnmp_cppflags"
   BUILD_WITH_LIBNETSNMP_LDFLAGS="$with_libnetsnmp_ldflags"
   BUILD_WITH_LIBNETSNMP_LIBS="-lnetsnmp"
@@ -3838,7 +3897,7 @@ AC_SUBST([BUILD_WITH_LIBNETSNMP_LDFLAGS])
 AC_SUBST([BUILD_WITH_LIBNETSNMP_LIBS])
 # }}}
 
-# --with-libnetsmpagent {{{
+# --with-libnetsnmpagent {{{
 AC_ARG_WITH([libnetsnmpagent],
   [AS_HELP_STRING([--with-libnetsnmpagent@<:@=PREFIX@:>@], [Path to libnetsnmpagent.])],
   [
index 3a5e3c3..fe3480d 100644 (file)
@@ -73,9 +73,9 @@ typedef int (*list_callback_t)(const char *name, value_t value,
 struct cb_view_s {
   char *name;
 
-  int qtypes;
-  int resolver_stats;
-  int cacherrsets;
+  _Bool qtypes;
+  _Bool resolver_stats;
+  _Bool cacherrsets;
 
   char **zones;
   size_t zones_num;
@@ -107,12 +107,12 @@ typedef struct list_info_ptr_s list_info_ptr_t;
 static bool config_parse_time = true;
 
 static char *url;
-static int global_opcodes = 1;
-static int global_qtypes = 1;
-static int global_server_stats = 1;
-static int global_zone_maint_stats = 1;
-static int global_resolver_stats;
-static int global_memory_stats = 1;
+static _Bool global_opcodes = 1;
+static _Bool global_qtypes = 1;
+static _Bool global_server_stats = 1;
+static _Bool global_zone_maint_stats = 1;
+static _Bool global_resolver_stats;
+static _Bool global_memory_stats = 1;
 static int timeout = -1;
 
 static cb_view_t *views;
@@ -343,8 +343,6 @@ static int bind_xml_read_derive(xmlDoc *doc, xmlNode *node, /* {{{ */
 
   int status = parse_value(str_ptr, &value, DS_TYPE_DERIVE);
   if (status != 0) {
-    ERROR("bind plugin: Parsing string \"%s\" to derive value failed.",
-          str_ptr);
     xmlFree(str_ptr);
     return -1;
   }
@@ -1410,22 +1408,6 @@ static int bind_xml(const char *data) /* {{{ */
   return ret;
 } /* }}} int bind_xml */
 
-static int bind_config_set_bool(const char *name, int *var, /* {{{ */
-                                oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) {
-    WARNING("bind plugin: The `%s' option needs "
-            "exactly one boolean argument.",
-            name);
-    return -1;
-  }
-
-  if (ci->values[0].value.boolean)
-    *var = 1;
-  else
-    *var = 0;
-  return 0;
-} /* }}} int bind_config_set_bool */
-
 static int bind_config_add_view_zone(cb_view_t *view, /* {{{ */
                                      oconfig_item_t *ci) {
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
@@ -1484,11 +1466,11 @@ static int bind_config_add_view(oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("QTypes", child->key) == 0)
-      bind_config_set_bool("QTypes", &tmp->qtypes, child);
+      cf_util_get_boolean(child, &tmp->qtypes);
     else if (strcasecmp("ResolverStats", child->key) == 0)
-      bind_config_set_bool("ResolverStats", &tmp->resolver_stats, child);
+      cf_util_get_boolean(child, &tmp->resolver_stats);
     else if (strcasecmp("CacheRRSets", child->key) == 0)
-      bind_config_set_bool("CacheRRSets", &tmp->cacherrsets, child);
+      cf_util_get_boolean(child, &tmp->cacherrsets);
     else if (strcasecmp("Zone", child->key) == 0)
       bind_config_add_view_zone(tmp, child);
     else {
@@ -1508,27 +1490,19 @@ static int bind_config(oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("Url", child->key) == 0) {
-      if ((child->values_num != 1) ||
-          (child->values[0].type != OCONFIG_TYPE_STRING)) {
-        WARNING("bind plugin: The `Url' option needs "
-                "exactly one string argument.");
-        return -1;
-      }
-
-      sfree(url);
-      url = strdup(child->values[0].value.string);
+      cf_util_get_string(child, &url);
     } else if (strcasecmp("OpCodes", child->key) == 0)
-      bind_config_set_bool("OpCodes", &global_opcodes, child);
+      cf_util_get_boolean(child, &global_opcodes);
     else if (strcasecmp("QTypes", child->key) == 0)
-      bind_config_set_bool("QTypes", &global_qtypes, child);
+      cf_util_get_boolean(child, &global_qtypes);
     else if (strcasecmp("ServerStats", child->key) == 0)
-      bind_config_set_bool("ServerStats", &global_server_stats, child);
+      cf_util_get_boolean(child, &global_server_stats);
     else if (strcasecmp("ZoneMaintStats", child->key) == 0)
-      bind_config_set_bool("ZoneMaintStats", &global_zone_maint_stats, child);
+      cf_util_get_boolean(child, &global_zone_maint_stats);
     else if (strcasecmp("ResolverStats", child->key) == 0)
-      bind_config_set_bool("ResolverStats", &global_resolver_stats, child);
+      cf_util_get_boolean(child, &global_resolver_stats);
     else if (strcasecmp("MemoryStats", child->key) == 0)
-      bind_config_set_bool("MemoryStats", &global_memory_stats, child);
+      cf_util_get_boolean(child, &global_memory_stats);
     else if (strcasecmp("View", child->key) == 0)
       bind_config_add_view(child);
     else if (strcasecmp("ParseTime", child->key) == 0)
index d615088..9d508d1 100644 (file)
@@ -10,23 +10,24 @@ collectd-snmp - Documentation of collectd's C<snmp plugin>
   # ...
   <Plugin snmp>
     <Data "powerplus_voltge_input">
-      Type "voltage"
       Table false
-      Instance "input_line1"
+      Type "voltage"
+      TypeInstance "input_line1"
       Scale 0.1
       Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1"
     </Data>
     <Data "hr_users">
-      Type "users"
       Table false
-      Instance ""
+      Type "users"
       Shift -1
       Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0"
     </Data>
     <Data "std_traffic">
-      Type "if_octets"
       Table true
-      Instance "IF-MIB::ifDescr"
+      Type "if_octets"
+      TypeInstanceOID "IF-MIB::ifDescr"
+      #FilterOID "IF-MIB::ifOperStatus"
+      #FilterValues "1", "2"
       Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
     </Data>
 
@@ -114,8 +115,9 @@ queried using the C<GET> SNMP command (see L<snmpget(1)>) and transmitted to
 collectd. B<One> value list is dispatched and, eventually, one file will be
 written.
 
-When B<Table> is set to B<true>, the OIDs given to B<Values> (see below) are
-queried using the C<GETNEXT> SNMP command until the subtree is left. After all
+When B<Table> is set to B<true>, the OIDs given to B<Values>, B<TypeInstanceOID>,
+B<PluginInstanceOID>, B<HostOID> and B<FilterOID> (see below) are queried using
+the C<GETNEXT> SNMP command until the subtree is left. After all
 the lists (think: all columns of the table) have been read B<several> values
 sets will be dispatches and, eventually, several files will be written. If you
 configure a B<Type> (see above) which needs more than one data source (for
@@ -138,33 +140,66 @@ C<IF-MIB::ifHCInOctets> and C<IF-MIB::ifHCOutOctets>. But, this is because of
 the B<Type> setting, not the B<Table> setting.
 
 Since the semantic of B<Instance> and B<Values> depends on this setting you
-need to set it before setting them. Doing vice verse will result in undefined
+need to set it before setting them. Doing vice versa will result in undefined
 behavior.
 
-=item B<Instance> I<Instance>
+=item B<Plugin> I<Plugin>
 
-Sets the type-instance of the values that are dispatched. The meaning of this
-setting depends on whether B<Table> is set to I<true> or I<false>:
+Use I<Plugin> as the plugin name of the values that are dispatched.
+Defaults to C<snmp>.
 
-If B<Table> is set to I<true>, I<Instance> is interpreted as an SNMP-prefix
-that will return a list of values. Those values are then used as the actual
-type-instance. An example would be the C<IF-MIB::ifDescr> subtree.
-L<variables(5)> from the SNMP distribution describes the format of OIDs.
+=item B<PluginInstance> I<Instance>
 
-If B<Table> is set to I<true> and B<Instance> is omitted, then "SUBID" will be
-used as the instance.
+Sets the plugin-instance of the values that are dispatched to I<Instance> value.
 
-If B<Table> is set to I<false> the actual string configured for I<Instance> is
-copied into the value-list. In this case I<Instance> may be empty, i.E<nbsp>e.
-"".
+When B<Table> is set to I<true> and B<PluginInstanceOID> is set then this option
+has no effect.
 
-=item B<InstancePrefix> I<String>
+Defaults to an empty string.
+
+=item B<TypeInstance> I<Instance>
+
+Sets the type-instance of the values that are dispatched to I<Instance> value.
 
-If B<Table> is set to I<true>, you may feel the need to add something to the
-instance of the files. If set, I<String> is prepended to the instance as
-determined by querying the agent. When B<Table> is set to I<false> this option
+When B<Table> is set to I<true> and B<TypeInstanceOID> is set then this option
 has no effect.
 
+Defaults to an empty string.
+
+=item B<TypeInstanceOID> I<OID>
+
+=item B<PluginInstanceOID> I<OID>
+
+=item B<HostOID> I<OID>
+
+If B<Table> is set to I<true>, I<OID> is interpreted as an SNMP-prefix that will
+return a list of values. Those values are then used as the actual type-instance,
+plugin-instance or host of dispatched metrics. An example would be the
+C<IF-MIB::ifDescr> subtree. L<variables(5)> from the SNMP distribution describes
+the format of OIDs. When option is set to empty string, then "SUBID" will be used
+as the value.
+
+Prefix may be set for values with use of appropriate B<TypeInstancePrefix>,
+B<PluginInstancePrefix> and B<HostPrefix> options.
+
+When B<Table> is set to I<false> these options has no effect.
+
+Defaults: When no one of these options is configured explicitly,
+B<TypeInstanceOID> defaults to an empty string.
+
+=item B<TypeInstancePrefix>
+
+=item B<PluginInstancePrefix>
+
+=item B<HostPrefix>
+
+These options are intented to be used together with B<TypeInstanceOID>,
+B<PluginInstanceOID> and B<HostOID> respectively.
+
+If set, I<String> is preprended to values received by querying the agent.
+
+When B<Table> is set to I<false> these options has no effect.
+
 The C<UPS-MIB> is an example where you need this setting: It has voltages of
 the inlets, outlets and the battery of an UPS. However, it doesn't provide a
 descriptive column for these voltages. In this case having 1, 2,E<nbsp>... as
@@ -172,6 +207,25 @@ instances is not enough, because the inlet voltages and outlet voltages may
 both have the subids 1, 2,E<nbsp>... You can use this setting to distinguish
 between the different voltages.
 
+=item B<Instance> I<Instance>
+
+Attention: this option exists for backwards compatibility only and will be
+removed in next major release. Please use B<TypeInstance> / B<TypeInstanceOID>
+instead.
+
+The meaning of this setting depends on whether B<Table> is set to I<true> or
+I<false>.
+
+If B<Table> is set to I<true>, option behaves as B<TypeInstanceOID>.
+If B<Table> is set to I<false>, option behaves as B<TypeInstance>.
+
+Note what B<Table> option must be set before setting B<Instance>.
+
+=item B<InstancePrefix> I<String>
+
+Attention: this option exists for backwards compatibility only and will be
+removed in next major release. Please use B<TypeInstancePrefix> instead.
+
 =item B<Values> I<OID> [I<OID> ...]
 
 Configures the values to be queried from the SNMP host. The meaning slightly
@@ -208,16 +262,39 @@ This value is not applied to counter-values.
 
 =item B<Ignore> I<Value> [, I<Value> ...]
 
-The ignore values allows one to ignore Instances based on their name and the
-patterns specified by the various values you've entered. The match is a
+The ignore values allows one to ignore TypeInstances based on their name and
+the patterns specified by the various values you've entered. The match is a
 glob-type shell matching.
 
+When B<Table> is set to I<false> then this option has no effect.
+
 =item B<InvertMatch> I<true|false(default)>
 
 The invertmatch value should be use in combination of the Ignore option.
 It changes the behaviour of the Ignore option, from a blacklist behaviour
 when InvertMatch is set to false, to a whitelist when specified to true.
 
+=item B<FilterOID> I<OID>
+
+=item B<FilterValues> I<Value> [, I<Value> ...]
+
+=item B<FilterIgnoreSelected> I<true|false(default)>
+
+When B<Table> is set to I<true>, these options allow to configure filtering
+based on MIB values.
+
+The B<FilterOID> declares I<OID> to fill table column with values.
+The B<FilterValues> declares values list to do match. Whether table row will be
+collected or ignored depends on the B<FilterIgnoreSelected> setting.
+As with other plugins that use the daemon's ignorelist functionality, a string
+that starts and ends with a slash is interpreted as a regular expression.
+
+If no selection is configured at all, B<all> table rows are selected.
+
+When B<Table> is set to I<false> then these options has no effect.
+
+See B<Table> and F</"IGNORELISTS"> for details.
+
 =back
 
 =head2 The Host block
index 4669c65..2d83bbf 100644 (file)
@@ -105,7 +105,7 @@ static double dtime(void) /* {{{ */
 {
   struct timespec ts = {0};
 
-  if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+  if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
     perror("clock_gettime");
 
   return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
index 6a5dfbd..bb83bda 100644 (file)
 
 #<Plugin snmp>
 #   <Data "powerplus_voltge_input">
-#       Type "voltage"
 #       Table false
-#       Instance "input_line1"
+#       Type "voltage"
+#       TypeInstance "input_line1"
 #       Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1"
 #   </Data>
 #   <Data "hr_users">
-#       Type "users"
 #       Table false
-#       Instance ""
+#       Type "users"
+#       TypeInstance ""
 #       Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0"
 #   </Data>
 #   <Data "std_traffic">
+#       Table true
 #       Type "if_octets"
+#       TypeInstanceOID "IF-MIB::ifDescr"
+#       #TypeInstancePrefix "port"
+#       Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
+#       #FilterOID "IF-MIB::ifOperStatus"
+#       #FilterValues "1", "2"
+#   </Data>
+#   <Data "interface_traffic">
 #       Table true
-#       Instance "IF-MIB::ifDescr"
+#       Type "if_octets"
+#       Plugin "interface"
+#       PluginInstanceOID "IF-MIB::ifDescr"
 #       Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
 #   </Data>
 #
 #    IndexOID "IF-MIB::ifIndex"
 #    SizeOID "IF-MIB::ifNumber"
 #    <Data "ifDescr">
-#      Instance true
+#      <IndexKey>
+#        Source "PluginInstance"
+#      </IndexKey>
 #      Plugin "interface"
 #      OIDs "IF-MIB::ifDescr"
 #    </Data>
index 87b92e3..01fae1b 100644 (file)
@@ -1908,6 +1908,11 @@ plugin below on how matches are defined. If the B<MeasureResponseTime> or
 B<MeasureResponseCode> options are set to B<true>, B<Match> blocks are
 optional.
 
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+URL. By default the global B<Interval> setting will be used.
+
 =item B<Timeout> I<Milliseconds>
 
 The B<Timeout> option sets the overall timeout for HTTP requests to B<URL>, in
@@ -2104,6 +2109,11 @@ Use I<Instance> as the plugin instance when submitting values.
 May be overridden by B<PluginInstanceFrom> option inside B<XPath> blocks.
 Defaults to an empty string (no plugin instance).
 
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+URL. By default the global B<Interval> setting will be used.
+
 =item B<Namespace> I<Prefix> I<URL>
 
 If an XPath expression references namespaces, they must be specified
@@ -5438,6 +5448,12 @@ behavior is to let the kernel choose the appropriate interface. Be warned
 that the manual selection of an interface for unicast traffic is only
 necessary in rare cases.
 
+=item B<BindAddress> I<IP Address>
+
+Set the outgoing IP address for IP packets. This option can be used instead of
+the I<Interface> option to explicitly define the IP address which will be used
+to send Packets to the remote server. 
+
 =item B<ResolveInterval> I<Seconds>
 
 Sets the interval at which to re-resolve the DNS for the I<Host>. This is
@@ -7327,26 +7343,26 @@ Defaults to B<false>.
 
 =head2 Plugin C<redis>
 
-The I<Redis plugin> connects to one or more Redis servers and gathers
-information about each server's state. For each server there is a I<Node> block
-which configures the connection parameters for this node.
+The I<Redis plugin> connects to one or more Redis servers, gathers
+information about each server's state and executes user-defined queries.
+For each server there is a I<Node> block which configures the connection
+parameters and set of user-defined queries for this node.
 
   <Plugin redis>
     <Node "example">
         Host "localhost"
         Port "6379"
         Timeout 2000
+        ReportCommandStats false
+        ReportCpuUsage true
         <Query "LLEN myqueue">
           #Database 0
           Type "queue_length"
           Instance "myqueue"
-        <Query>
+        </Query>
     </Node>
   </Plugin>
 
-The information shown in the synopsis above is the I<default configuration>
-which is used by the plugin if no configuration is present.
-
 =over 4
 
 =item B<Node> I<Nodename>
@@ -7354,7 +7370,9 @@ which is used by the plugin if no configuration is present.
 The B<Node> block identifies a new Redis node, that is a new Redis instance
 running in an specified host and port. The name for node is a canonical
 identifier which is used as I<plugin instance>. It is limited to
-64E<nbsp>characters in length.
+128E<nbsp>characters in length.
+
+When no B<Node> is configured explicitly, plugin connects to "localhost:6379".
 
 =item B<Host> I<Hostname>
 
@@ -7374,30 +7392,47 @@ Use I<Password> to authenticate when connecting to I<Redis>.
 =item B<Timeout> I<Milliseconds>
 
 The B<Timeout> option set the socket timeout for node response. Since the Redis
-read function is blocking, you should keep this value as low as possible. Keep
-in mind that the sum of all B<Timeout> values for all B<Nodes> should be lower
-than B<Interval> defined globally.
+read function is blocking, you should keep this value as low as possible.
+It is expected what B<Timeout> values should be lower than B<Interval> defined
+globally.
 
-=item B<Query> I<Querystring>
+Defaults to 2000 (2 seconds).
 
-The B<Query> block identifies a query to execute against the redis server.
-There may be an arbitrary number of queries to execute.
+=item B<ReportCommandStats> B<false>|B<true>
 
-=item B<Database> I<Index>
+Enables or disables reporting of statistics based on the command type, including
+rate of command calls and average CPU time consumed by command processing.
+Defaults to B<false>.
 
-This index selects the Redis logical database to use for query. Defaults
-to C<0>.
+=item B<ReportCpuUsage> B<true>|B<false>
+
+Enables or disables reporting of CPU consumption statistics.
+Defaults to B<true>.
+
+=item B<Query> I<Querystring>
+
+The B<Query> block identifies a query to execute against the redis server.
+There may be an arbitrary number of queries to execute. Each query should
+return single string or integer.
 
 =item B<Type> I<Collectd type>
 
-Within a query definition, a valid collectd type to use as when submitting
+Within a query definition, a valid I<collectd type> to use as when submitting
 the result of the query. When not supplied, will default to B<gauge>.
 
+Currently only types with one datasource are supported.
+See L<types.db(5)> for more details on types and their configuration.
+
 =item B<Instance> I<Type instance>
 
 Within a query definition, an optional type instance to use when submitting
 the result of the query. When not supplied will default to the escaped
-command, up to 64 chars.
+command, up to 128 chars.
+
+=item B<Database> I<Index>
+
+This index selects the Redis logical database to use for query. Defaults
+to C<0>.
 
 =back
 
@@ -7837,7 +7872,9 @@ B<Synopsis:>
       IndexOID "IF-MIB::ifIndex"
       SizeOID "IF-MIB::ifNumber"
       <Data "ifDescr">
-        Instance true
+        <IndexKey>
+          Source "PluginInstance"
+        </IndexKey>
         Plugin "interface"
         OIDs "IF-MIB::ifDescr"
       </Data>
@@ -7848,12 +7885,44 @@ B<Synopsis:>
         OIDs "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
       </Data>
     </Table>
+    <Table "CPUAffinityTable">
+      <Data "DomainName">
+        <IndexKey>
+          Source "PluginInstance"
+        </IndexKey>
+        Plugin "virt"
+        OIDs "LIBVIRT-HYPERVISOR-MIB::lvhAffinityDomainName"
+      </Data>
+      <Data "VCPU">
+        Plugin "virt"
+        <IndexKey>
+          Source "TypeInstance"
+          Regex "^vcpu_([0-9]{1,3})-cpu_[0-9]{1,3}$"
+          Group 1
+        </IndexKey>
+        OIDs "LIBVIRT-HYPERVISOR-MIB::lvhVCPUIndex"
+      </Data>
+      <Data "CPU">
+        Plugin "virt"
+        <IndexKey>
+          Source "TypeInstance"
+          Regex "^vcpu_[0-9]{1,3}-cpu_([0-9]{1,3})$"
+          Group 1
+        </IndexKey>
+        OIDs "LIBVIRT-HYPERVISOR-MIB::lvhCPUIndex"
+      </Data>
+      <Data "CPUAffinity">
+        Plugin "virt"
+        Type "cpu_affinity"
+        OIDs "LIBVIRT-HYPERVISOR-MIB::lvhCPUAffinity"
+      </Data>
+    </Table>
   </Plugin>
 
 There are two types of blocks that can be contained in the
 C<E<lt>PluginE<nbsp> snmp_agentE<gt>> block: B<Data> and B<Table>:
 
-=head3 The B<Data> block
+=head3 B<Data> block
 
 The B<Data> block defines a list OIDs that are to be handled. This block can
 define scalar or table OIDs. If B<Data> block is defined inside of B<Table>
@@ -7862,11 +7931,34 @@ The following options can be set:
 
 =over 4
 
-=item B<Instance> I<true|false>
+=item B<IndexKey> block
+
+B<IndexKey> block contains all data needed for proper index build of snmp table.
+In case more than
+one table B<Data> block has B<IndexKey> block present then multiple key index is
+built. If B<Data> block defines scalar data type B<IndexKey> has no effect and can
+be omitted.
+
+=over 8
+
+=item B<Source> I<String>
+
+B<Source> can be set to one of the following values: "Hostname", "Plugin",
+"PluginInstance", "Type", "TypeInstance". This value indicates which field of
+corresponding collectd metric is taken as a SNMP table index.
 
-When B<Instance> is set to B<true>, the value for requested OID is copied from
-plugin instance field of corresponding collectd value. If B<Data> block defines
-scalar data type B<Instance> has no effect and can be omitted.
+=item B<Regex> I<String>
+
+B<Regex> option can also be used to parse strings or numbers out of
+specific field. For example: type-instance field which is "vcpu1-cpu2" can be
+parsed into two numeric fields CPU = 2 and VCPU = 1 and can be later used
+as a table index.
+
+=item B<Group> I<Number>
+
+B<Group> number can be specified in case groups are used in regex.
+
+=back
 
 =item B<Plugin> I<String>
 
@@ -9248,6 +9340,7 @@ Synopsis:
      Protocol "tcp"
      LogSendErrors true
      Prefix "collectd"
+     UseTags false
    </Node>
  </Plugin>
 
@@ -9285,13 +9378,20 @@ approach and logging errors fills syslog with unneeded messages.
 
 =item B<Prefix> I<String>
 
-When set, I<String> is added in front of the host name. Dots and whitespace are
-I<not> escaped in this string (see B<EscapeCharacter> below).
+When B<UseTags> is I<false>, B<Prefix> value is added in front of the host name.
+When B<UseTags> is I<true>, B<Prefix> value is added in front of series name.
+
+Dots and whitespace are I<not> escaped in this string (see B<EscapeCharacter>
+below).
 
 =item B<Postfix> I<String>
 
-When set, I<String> is appended to the host name. Dots and whitespace are
-I<not> escaped in this string (see B<EscapeCharacter> below).
+When B<UseTags> is I<false>, B<Postfix> value appended to the host name.
+When B<UseTags> is I<true>, B<Postgix> value appended to the end of series name
+(before the first ; that separates the name from the tags).
+
+Dots and whitespace are I<not> escaped in this string (see B<EscapeCharacter>
+below).
 
 =item B<EscapeCharacter> I<Char>
 
@@ -9313,6 +9413,8 @@ path component, for example C<host.cpu.0.cpu.idle>. If set to B<false> (the
 default), the plugin and plugin instance (and likewise the type and type
 instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
 
+Option value is not used when B<UseTags> is I<true>.
+
 =item B<AlwaysAppendDS> B<false>|B<true>
 
 If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
@@ -9325,12 +9427,31 @@ If set to B<false> (the default) the C<.> (dot) character is replaced with
 I<EscapeCharacter>. Otherwise, if set to B<true>, the C<.> (dot) character
 is preserved, i.e. passed through.
 
+Option value is not used when B<UseTags> is I<true>.
+
 =item B<DropDuplicateFields> B<false>|B<true>
 
 If set to B<true>, detect and remove duplicate components in Graphite metric
 names. For example, the metric name  C<host.load.load.shortterm> will
 be shortened to C<host.load.shortterm>.
 
+=item B<UseTags> B<false>|B<true>
+
+If set to B<true>, Graphite metric names will be generated as tagged series.
+This allows for much more flexibility than the traditional hierarchical layout.
+
+Example:
+C<test.single;host=example.com;plugin=test;plugin_instance=foo;type=single;type_instance=bar>
+
+You can use B<Postfix> option to add more tags by specifying it like
+C<;tag1=value1;tag2=value2>. Note what tagging support was added since Graphite
+version 1.1.x.
+
+If set to B<true>, the B<SeparateInstances> and B<PreserveSeparator> settings
+are not used.
+
+Default value: B<false>.
+
 =back
 
 =head2 Plugin C<write_log>
@@ -9756,17 +9877,26 @@ been set to B<JSON>.
 =item B<GraphitePrefix> (B<Format>=I<Graphite> only)
 
 A prefix can be added in the metric name when outputting in the I<Graphite>
-format. It's added before the I<Host> name.
+format.
+
+When B<GraphiteUseTags> is I<false>, prefix is added before the I<Host> name.
 Metric name will be
 C<E<lt>prefixE<gt>E<lt>hostE<gt>E<lt>postfixE<gt>E<lt>pluginE<gt>E<lt>typeE<gt>E<lt>nameE<gt>>
 
+When B<GraphiteUseTags> is I<true>, prefix is added in front of series name.
+
 =item B<GraphitePostfix> (B<Format>=I<Graphite> only)
 
 A postfix can be added in the metric name when outputting in the I<Graphite>
-format. It's added after the I<Host> name.
+format.
+
+When B<GraphiteUseTags> is I<false>, postfix is added after the I<Host> name.
 Metric name will be
 C<E<lt>prefixE<gt>E<lt>hostE<gt>E<lt>postfixE<gt>E<lt>pluginE<gt>E<lt>typeE<gt>E<lt>nameE<gt>>
 
+When B<GraphiteUseTags> is I<true>, prefix value appended to the end of series
+name (before the first ; that separates the name from the tags).
+
 =item B<GraphiteEscapeChar> (B<Format>=I<Graphite> only)
 
 Specify a character to replace dots (.) in the host part of the metric name.
@@ -9781,6 +9911,8 @@ path component, for example C<host.cpu.0.cpu.idle>. If set to B<false> (the
 default), the plugin and plugin instance (and likewise the type and type
 instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
 
+Option value is not used when B<GraphiteUseTags> is I<true>.
+
 =item B<GraphiteAlwaysAppendDS> B<true>|B<false>
 
 If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
@@ -9793,6 +9925,14 @@ If set to B<false> (the default) the C<.> (dot) character is replaced with
 I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
 is preserved, i.e. passed through.
 
+Option value is not used when B<GraphiteUseTags> is I<true>.
+
+=item B<GraphiteUseTags> B<false>|B<true>
+
+If set to B<true> Graphite metric names will be generated as tagged series.
+
+Default value: B<false>.
+
 =item B<StoreRates> B<true>|B<false>
 
 If set to B<true> (the default), convert counter values to rates. If set to
index 4bfd1e4..4925ad0 100644 (file)
@@ -78,18 +78,13 @@ struct web_page_s /* {{{ */
   size_t buffer_fill;
 
   web_match_t *matches;
-
-  web_page_t *next;
 }; /* }}} */
 
 /*
- * Global variables;
- */
-static web_page_t *pages_g;
-
-/*
  * Private functions
  */
+static int cc_read_page(user_data_t *ud);
+
 static size_t cc_curl_callback(void *buf, /* {{{ */
                                size_t size, size_t nmemb, void *user_data) {
   web_page_t *wp;
@@ -137,8 +132,9 @@ static void cc_web_match_free(web_match_t *wm) /* {{{ */
   sfree(wm);
 } /* }}} void cc_web_match_free */
 
-static void cc_web_page_free(web_page_t *wp) /* {{{ */
+static void cc_web_page_free(void *arg) /* {{{ */
 {
+  web_page_t *wp = (web_page_t *)arg;
   if (wp == NULL)
     return;
 
@@ -161,7 +157,6 @@ static void cc_web_page_free(web_page_t *wp) /* {{{ */
   sfree(wp->buffer);
 
   cc_web_match_free(wp->matches);
-  cc_web_page_free(wp->next);
   sfree(wp);
 } /* }}} void cc_web_page_free */
 
@@ -400,6 +395,7 @@ static int cc_page_init_curl(web_page_t *wp) /* {{{ */
 
 static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
 {
+  cdtime_t interval = 0;
   web_page_t *page;
   int status;
 
@@ -464,6 +460,8 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
       status = cc_config_append_string("Header", &page->headers, child);
     else if (strcasecmp("Post", child->key) == 0)
       status = cf_util_get_string(child, &page->post_body);
+    else if (strcasecmp("Interval", child->key) == 0)
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Timeout", child->key) == 0)
       status = cf_util_get_int(child, &page->timeout);
     else if (strcasecmp("Statistics", child->key) == 0) {
@@ -507,17 +505,15 @@ static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
     return status;
   }
 
-  /* Add the new page to the linked list */
-  if (pages_g == NULL)
-    pages_g = page;
-  else {
-    web_page_t *prev;
+  /* If all went well, register this page for reading */
+  char *cb_name = ssnprintf_alloc("curl-%s-%s", page->instance, page->url);
 
-    prev = pages_g;
-    while (prev->next != NULL)
-      prev = prev->next;
-    prev->next = page;
-  }
+  plugin_register_complex_read(/* group = */ NULL, cb_name, cc_read_page,
+                               interval,
+                               &(user_data_t){
+                                   .data = page, .free_func = cc_web_page_free,
+                               });
+  sfree(cb_name);
 
   return 0;
 } /* }}} int cc_config_add_page */
@@ -556,10 +552,6 @@ static int cc_config(oconfig_item_t *ci) /* {{{ */
 
 static int cc_init(void) /* {{{ */
 {
-  if (pages_g == NULL) {
-    INFO("curl plugin: No pages have been defined.");
-    return -1;
-  }
   curl_global_init(CURL_GLOBAL_SSL);
   return 0;
 } /* }}} int cc_init */
@@ -608,8 +600,16 @@ static void cc_submit_response_time(const web_page_t *wp, /* {{{ */
   plugin_dispatch_values(&vl);
 } /* }}} void cc_submit_response_time */
 
-static int cc_read_page(web_page_t *wp) /* {{{ */
+static int cc_read_page(user_data_t *ud) /* {{{ */
 {
+
+  if ((ud == NULL) || (ud->data == NULL)) {
+    ERROR("curl plugin: cc_read_page: Invalid user data.");
+    return -1;
+  }
+
+  web_page_t *wp = (web_page_t *)ud->data;
+
   int status;
   cdtime_t start = 0;
 
@@ -666,25 +666,7 @@ static int cc_read_page(web_page_t *wp) /* {{{ */
   return 0;
 } /* }}} int cc_read_page */
 
-static int cc_read(void) /* {{{ */
-{
-  for (web_page_t *wp = pages_g; wp != NULL; wp = wp->next)
-    cc_read_page(wp);
-
-  return 0;
-} /* }}} int cc_read */
-
-static int cc_shutdown(void) /* {{{ */
-{
-  cc_web_page_free(pages_g);
-  pages_g = NULL;
-
-  return 0;
-} /* }}} int cc_shutdown */
-
 void module_register(void) {
   plugin_register_complex_config("curl", cc_config);
   plugin_register_init("curl", cc_init);
-  plugin_register_read("curl", cc_read);
-  plugin_register_shutdown("curl", cc_shutdown);
 } /* void module_register */
index f0badc9..dedfed0 100644 (file)
@@ -97,7 +97,6 @@ struct cj_s /* {{{ */
   char *cacert;
   struct curl_slist *headers;
   char *post_body;
-  cdtime_t interval;
   int timeout;
   curl_stats_t *stats;
 
@@ -256,7 +255,6 @@ static int cj_cb_number(void *ctx, const char *number, yajl_len_t number_len) {
   value_t vt;
   int status = parse_value(buffer, &vt, type);
   if (status != 0) {
-    NOTICE("curl_json plugin: Unable to parse number: \"%s\"", buffer);
     cj_advance_array(ctx);
     return CJ_CB_CONTINUE;
   }
@@ -624,9 +622,6 @@ static int cj_init_curl(cj_t *db) /* {{{ */
 #ifdef HAVE_CURLOPT_TIMEOUT_MS
   if (db->timeout >= 0)
     curl_easy_setopt(db->curl, CURLOPT_TIMEOUT_MS, (long)db->timeout);
-  else if (db->interval > 0)
-    curl_easy_setopt(db->curl, CURLOPT_TIMEOUT_MS,
-                     (long)CDTIME_T_TO_MS(db->interval));
   else
     curl_easy_setopt(db->curl, CURLOPT_TIMEOUT_MS,
                      (long)CDTIME_T_TO_MS(plugin_get_interval()));
@@ -639,6 +634,7 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */
 {
   cj_t *db;
   int status = 0;
+  cdtime_t interval = 0;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
     WARNING("curl_json plugin: The `URL' block "
@@ -699,7 +695,7 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */
     else if (strcasecmp("Key", child->key) == 0)
       status = cj_config_add_key(db, child);
     else if (strcasecmp("Interval", child->key) == 0)
-      status = cf_util_get_cdtime(child, &db->interval);
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Timeout", child->key) == 0)
       status = cf_util_get_int(child, &db->timeout);
     else if (strcasecmp("Statistics", child->key) == 0) {
@@ -737,8 +733,7 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */
     cb_name = ssnprintf_alloc("curl_json-%s-%s", db->instance,
                               db->url ? db->url : db->sock);
 
-    plugin_register_complex_read(/* group = */ NULL, cb_name, cj_read,
-                                 /* interval = */ db->interval,
+    plugin_register_complex_read(/* group = */ NULL, cb_name, cj_read, interval,
                                  &(user_data_t){
                                      .data = db, .free_func = cj_free,
                                  });
@@ -816,9 +811,6 @@ static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
   sstrncpy(vl.plugin_instance, db->instance, sizeof(vl.plugin_instance));
   sstrncpy(vl.type, key->type, sizeof(vl.type));
 
-  if (db->interval > 0)
-    vl.interval = db->interval;
-
   plugin_dispatch_values(&vl);
 } /* }}} int cj_submit_impl */
 
index 654bb67..0bed05a 100644 (file)
@@ -823,6 +823,8 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
     return status;
   }
 
+  cdtime_t interval = 0;
+
   /* Fill the `cx_t' structure.. */
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
@@ -853,6 +855,8 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string(child, &db->post_body);
     else if (strcasecmp("Namespace", child->key) == 0)
       status = cx_config_add_namespace(db, child);
+    else if (strcasecmp("Interval", child->key) == 0)
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Timeout", child->key) == 0)
       status = cf_util_get_int(child, &db->timeout);
     else if (strcasecmp("Statistics", child->key) == 0) {
@@ -891,7 +895,7 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */
   char *cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url);
 
   plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read,
-                               /* interval = */ 0,
+                               /* interval = */ interval,
                                &(user_data_t){
                                    .data = db, .free_func = cx_free,
                                });
index 582d6b2..76c7036 100644 (file)
@@ -417,7 +417,7 @@ int strunescape(char *buf, size_t buf_len) {
       continue;
 
     if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) {
-      ERROR("string unescape: backslash found at end of string.");
+      P_ERROR("string unescape: backslash found at end of string.");
       /* Ensure null-byte at the end of the buffer. */
       buf[i] = 0;
       return -1;
@@ -544,9 +544,8 @@ int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta) {
 int check_create_dir(const char *file_orig) {
   struct stat statbuf;
 
-  char file_copy[512];
-  char dir[512];
-  int dir_len = 512;
+  char file_copy[PATH_MAX];
+  char dir[PATH_MAX];
   char *fields[16];
   int fields_num;
   char *ptr;
@@ -563,8 +562,10 @@ int check_create_dir(const char *file_orig) {
 
   if ((len = strlen(file_orig)) < 1)
     return -1;
-  else if (len >= sizeof(file_copy))
+  else if (len >= sizeof(file_copy)) {
+    ERROR("check_create_dir: name (%s) is too long.", file_orig);
     return -1;
+  }
 
   /*
    * If `file_orig' ends in a slash the last component is a directory,
@@ -605,9 +606,9 @@ int check_create_dir(const char *file_orig) {
      * behavior.
      */
     if (fields[i][0] == '.') {
-      ERROR("Cowardly refusing to create a directory that "
-            "begins with a `.' (dot): `%s'",
-            file_orig);
+      P_ERROR("Cowardly refusing to create a directory that "
+              "begins with a `.' (dot): `%s'",
+              file_orig);
       return -2;
     }
 
@@ -615,9 +616,10 @@ int check_create_dir(const char *file_orig) {
      * Join the components together again
      */
     dir[0] = '/';
-    if (strjoin(dir + path_is_absolute, (size_t)(dir_len - path_is_absolute),
-                fields, (size_t)(i + 1), "/") < 0) {
-      ERROR("strjoin failed: `%s', component #%i", file_orig, i);
+    if (strjoin(dir + path_is_absolute,
+                (size_t)(sizeof(dir) - path_is_absolute), fields,
+                (size_t)(i + 1), "/") < 0) {
+      P_ERROR("strjoin failed: `%s', component #%i", file_orig, i);
       return -1;
     }
 
@@ -633,16 +635,16 @@ int check_create_dir(const char *file_orig) {
           if (EEXIST == errno)
             continue;
 
-          ERROR("check_create_dir: mkdir (%s): %s", dir, STRERRNO);
+          P_ERROR("check_create_dir: mkdir (%s): %s", dir, STRERRNO);
           return -1;
         } else {
-          ERROR("check_create_dir: stat (%s): %s", dir, STRERRNO);
+          P_ERROR("check_create_dir: stat (%s): %s", dir, STRERRNO);
           return -1;
         }
       } else if (!S_ISDIR(statbuf.st_mode)) {
-        ERROR("check_create_dir: `%s' exists but is not "
-              "a directory!",
-              dir);
+        P_ERROR("check_create_dir: `%s' exists but is not "
+                "a directory!",
+                dir);
         return -1;
       }
       break;
@@ -665,12 +667,12 @@ int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name) {
 
   *ksp_ptr = kstat_lookup(kc, module, instance, name);
   if (*ksp_ptr == NULL) {
-    ERROR("get_kstat: Cound not find kstat %s", ident);
+    P_ERROR("get_kstat: Cound not find kstat %s", ident);
     return -1;
   }
 
   if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
-    ERROR("get_kstat: kstat %s has wrong type", ident);
+    P_ERROR("get_kstat: kstat %s has wrong type", ident);
     *ksp_ptr = NULL;
     return -1;
   }
@@ -681,12 +683,12 @@ int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name) {
 #endif
 
   if (kstat_read(kc, *ksp_ptr, NULL) == -1) {
-    ERROR("get_kstat: kstat %s could not be read", ident);
+    P_ERROR("get_kstat: kstat %s could not be read", ident);
     return -1;
   }
 
   if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
-    ERROR("get_kstat: kstat %s has wrong type", ident);
+    P_ERROR("get_kstat: kstat %s has wrong type", ident);
     return -1;
   }
 
@@ -698,12 +700,12 @@ long long get_kstat_value(kstat_t *ksp, char *name) {
   long long retval = -1LL;
 
   if (ksp == NULL) {
-    ERROR("get_kstat_value (\"%s\"): ksp is NULL.", name);
+    P_ERROR("get_kstat_value (\"%s\"): ksp is NULL.", name);
     return -1LL;
   } else if (ksp->ks_type != KSTAT_TYPE_NAMED) {
-    ERROR("get_kstat_value (\"%s\"): ksp->ks_type (%#x) "
-          "is not KSTAT_TYPE_NAMED (%#x).",
-          name, (unsigned int)ksp->ks_type, (unsigned int)KSTAT_TYPE_NAMED);
+    P_ERROR("get_kstat_value (\"%s\"): ksp->ks_type (%#x) "
+            "is not KSTAT_TYPE_NAMED (%#x).",
+            name, (unsigned int)ksp->ks_type, (unsigned int)KSTAT_TYPE_NAMED);
     return -1LL;
   }
 
@@ -721,7 +723,7 @@ long long get_kstat_value(kstat_t *ksp, char *name) {
   else if (kn->data_type == KSTAT_DATA_UINT64)
     retval = (long long)kn->value.ui64; /* XXX: Might overflow! */
   else
-    WARNING("get_kstat_value: Not a numeric value: %s", name);
+    P_WARNING("get_kstat_value: Not a numeric value: %s", name);
 
   return retval;
 }
@@ -1033,19 +1035,19 @@ int parse_value(const char *value_orig, value_t *ret_value, int ds_type) {
 
   default:
     sfree(value);
-    ERROR("parse_value: Invalid data source type: %i.", ds_type);
+    P_ERROR("parse_value: Invalid data source type: %i.", ds_type);
     return -1;
   }
 
   if (value == endptr) {
-    ERROR("parse_value: Failed to parse string as %s: \"%s\".",
-          DS_TYPE_TO_STRING(ds_type), value);
+    P_ERROR("parse_value: Failed to parse string as %s: \"%s\".",
+            DS_TYPE_TO_STRING(ds_type), value);
     sfree(value);
     return -1;
   } else if ((NULL != endptr) && ('\0' != *endptr))
-    INFO("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
-         "Input string was \"%s\".",
-         endptr, DS_TYPE_TO_STRING(ds_type), value_orig);
+    P_INFO("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
+           "Input string was \"%s\".",
+           endptr, DS_TYPE_TO_STRING(ds_type), value_orig);
 
   sfree(value);
   return 0;
@@ -1210,7 +1212,7 @@ int walk_directory(const char *dir, dirwalk_callback_f callback,
   failure = 0;
 
   if ((dh = opendir(dir)) == NULL) {
-    ERROR("walk_directory: Cannot open '%s': %s", dir, STRERRNO);
+    P_ERROR("walk_directory: Cannot open '%s': %s", dir, STRERRNO);
     return -1;
   }
 
@@ -1250,7 +1252,7 @@ ssize_t read_file_contents(const char *filename, char *buf, size_t bufsize) {
 
   ret = (ssize_t)fread(buf, 1, bufsize, fh);
   if ((ret == 0) && (ferror(fh) != 0)) {
-    ERROR("read_file_contents: Reading file \"%s\" failed.", filename);
+    P_ERROR("read_file_contents: Reading file \"%s\" failed.", filename);
     ret = -1;
   }
 
@@ -1409,8 +1411,8 @@ int service_name_to_port_number(const char *service_name) {
 
   status = getaddrinfo(/* node = */ NULL, service_name, &ai_hints, &ai_list);
   if (status != 0) {
-    ERROR("service_name_to_port_number: getaddrinfo failed: %s",
-          gai_strerror(status));
+    P_ERROR("service_name_to_port_number: getaddrinfo failed: %s",
+            gai_strerror(status));
     return -1;
   }
 
@@ -1448,7 +1450,7 @@ void set_sock_opts(int sockfd) /* {{{ */
   status = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &socktype,
                       &(socklen_t){sizeof(socktype)});
   if (status != 0) {
-    WARNING("set_sock_opts: failed to determine socket type");
+    P_WARNING("set_sock_opts: failed to determine socket type");
     return;
   }
 
@@ -1456,14 +1458,14 @@ void set_sock_opts(int sockfd) /* {{{ */
     status =
         setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &(int){1}, sizeof(int));
     if (status != 0)
-      WARNING("set_sock_opts: failed to set socket keepalive flag");
+      P_WARNING("set_sock_opts: failed to set socket keepalive flag");
 
 #ifdef TCP_KEEPIDLE
     int tcp_keepidle = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 100 + 1);
     status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepidle,
                         sizeof(tcp_keepidle));
     if (status != 0)
-      WARNING("set_sock_opts: failed to set socket tcp keepalive time");
+      P_WARNING("set_sock_opts: failed to set socket tcp keepalive time");
 #endif
 
 #ifdef TCP_KEEPINTVL
@@ -1472,7 +1474,7 @@ void set_sock_opts(int sockfd) /* {{{ */
     status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepintvl,
                         sizeof(tcp_keepintvl));
     if (status != 0)
-      WARNING("set_sock_opts: failed to set socket tcp keepalive interval");
+      P_WARNING("set_sock_opts: failed to set socket tcp keepalive interval");
 #endif
   }
 } /* }}} void set_sock_opts */
@@ -1556,12 +1558,12 @@ int check_capability(int arg) /* {{{ */
     return -1;
 
   if (!(cap = cap_get_proc())) {
-    ERROR("check_capability: cap_get_proc failed.");
+    P_ERROR("check_capability: cap_get_proc failed.");
     return -1;
   }
 
   if (cap_get_flag(cap, cap_value, CAP_EFFECTIVE, &cap_flag_value) < 0) {
-    ERROR("check_capability: cap_get_flag failed.");
+    P_ERROR("check_capability: cap_get_flag failed.");
     cap_free(cap);
     return -1;
   }
@@ -1572,8 +1574,8 @@ int check_capability(int arg) /* {{{ */
 #else
 int check_capability(__attribute__((unused)) int arg) /* {{{ */
 {
-  WARNING("check_capability: unsupported capability implementation. "
-          "Some plugin(s) may require elevated privileges to work properly.");
+  P_WARNING("check_capability: unsupported capability implementation. "
+            "Some plugin(s) may require elevated privileges to work properly.");
   return 0;
 } /* }}} int check_capability */
 #endif /* HAVE_CAPABILITY */
index 2830a86..8750bef 100644 (file)
@@ -190,8 +190,12 @@ static int cf_dispatch(const char *type, const char *orig_key,
 } /* int cf_dispatch */
 
 static int dispatch_global_option(const oconfig_item_t *ci) {
-  if (ci->values_num != 1)
+  if (ci->values_num != 1) {
+    ERROR("configfile: Global option `%s' needs exactly one argument.",
+          ci->key);
     return -1;
+  }
+
   if (ci->values[0].type == OCONFIG_TYPE_STRING)
     return global_option_set(ci->key, ci->values[0].value.string, 0);
   else if (ci->values[0].type == OCONFIG_TYPE_NUMBER) {
@@ -205,6 +209,8 @@ static int dispatch_global_option(const oconfig_item_t *ci) {
       return global_option_set(ci->key, "false", 0);
   }
 
+  ERROR("configfile: Global option `%s' argument has unknown type.", ci->key);
+
   return -1;
 } /* int dispatch_global_option */
 
@@ -234,37 +240,37 @@ static int dispatch_value_typesdb(oconfig_item_t *ci) {
 static int dispatch_value_plugindir(oconfig_item_t *ci) {
   assert(strcasecmp(ci->key, "PluginDir") == 0);
 
-  if (ci->values_num != 1)
-    return -1;
-  if (ci->values[0].type != OCONFIG_TYPE_STRING)
+  if (ci->values_num != 1 || ci->values[0].type != OCONFIG_TYPE_STRING) {
+    ERROR("configfile: The `PluginDir' option needs exactly one string "
+          "argument.");
     return -1;
+  }
 
   plugin_set_dir(ci->values[0].value.string);
   return 0;
 }
 
 static int dispatch_loadplugin(oconfig_item_t *ci) {
-  const char *name;
   bool global = false;
-  plugin_ctx_t ctx = {0};
-  plugin_ctx_t old_ctx;
-  int ret_val;
 
   assert(strcasecmp(ci->key, "LoadPlugin") == 0);
 
-  if (ci->values_num != 1)
-    return -1;
-  if (ci->values[0].type != OCONFIG_TYPE_STRING)
+  if (ci->values_num != 1 || ci->values[0].type != OCONFIG_TYPE_STRING) {
+    ERROR("configfile: The `LoadPlugin' block needs exactly one string "
+          "argument.");
     return -1;
+  }
 
-  name = ci->values[0].value.string;
+  const char *name = ci->values[0].value.string;
   if (strcmp("libvirt", name) == 0)
     name = "virt";
 
   /* default to the global interval set before loading this plugin */
-  ctx.interval = cf_get_default_interval();
-  ctx.flush_interval = 0;
-  ctx.flush_timeout = 0;
+  plugin_ctx_t ctx = {
+      .interval = cf_get_default_interval(), .name = strdup(name),
+  };
+  if (ctx.name == NULL)
+    return ENOMEM;
 
   for (int i = 0; i < ci->children_num; ++i) {
     oconfig_item_t *child = ci->children + i;
@@ -280,12 +286,12 @@ static int dispatch_loadplugin(oconfig_item_t *ci) {
     else {
       WARNING("Ignoring unknown LoadPlugin option \"%s\" "
               "for plugin \"%s\"",
-              child->key, ci->values[0].value.string);
+              child->key, name);
     }
   }
 
-  old_ctx = plugin_set_ctx(ctx);
-  ret_val = plugin_load(name, global);
+  plugin_ctx_t old_ctx = plugin_set_ctx(ctx);
+  int ret_val = plugin_load(name, global);
   /* reset to the "global" context */
   plugin_set_ctx(old_ctx);
 
@@ -333,6 +339,9 @@ static int dispatch_value(oconfig_item_t *ci) {
       break;
     }
 
+  if (ret != 0)
+    return ret;
+
   for (int i = 0; i < cf_global_options_num; i++)
     if (strcasecmp(cf_global_options[i].key, ci->key) == 0) {
       ret = dispatch_global_option(ci);
@@ -343,16 +352,18 @@ static int dispatch_value(oconfig_item_t *ci) {
 } /* int dispatch_value */
 
 static int dispatch_block_plugin(oconfig_item_t *ci) {
-  const char *name;
+  assert(strcasecmp(ci->key, "Plugin") == 0);
 
-  if (strcasecmp(ci->key, "Plugin") != 0)
-    return -1;
-  if (ci->values_num < 1)
+  if (ci->values_num < 1) {
+    ERROR("configfile: The `Plugin' block requires arguments.");
     return -1;
-  if (ci->values[0].type != OCONFIG_TYPE_STRING)
+  }
+  if (ci->values[0].type != OCONFIG_TYPE_STRING) {
+    ERROR("configfile: First argument of `Plugin' block should be a string.");
     return -1;
+  }
 
-  name = ci->values[0].value.string;
+  const char *name = ci->values[0].value.string;
   if (strcmp("libvirt", name) == 0) {
     /* TODO(octo): Remove this legacy. */
     WARNING("The \"libvirt\" plugin has been renamed to \"virt\" to avoid "
@@ -370,6 +381,7 @@ static int dispatch_block_plugin(oconfig_item_t *ci) {
 
     /* default to the global interval set before loading this plugin */
     ctx.interval = cf_get_default_interval();
+    ctx.name = strdup(name);
 
     old_ctx = plugin_set_ctx(ctx);
     status = plugin_load(name, /* flags = */ false);
@@ -1035,16 +1047,12 @@ int cf_read(const char *filename) {
  * success. */
 int cf_util_get_string(const oconfig_item_t *ci, char **ret_string) /* {{{ */
 {
-  char *string;
-
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    ERROR("cf_util_get_string: The %s option requires "
-          "exactly one string argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one string argument.", ci->key);
     return -1;
   }
 
-  string = strdup(ci->values[0].value.string);
+  char *string = strdup(ci->values[0].value.string);
   if (string == NULL)
     return -1;
 
@@ -1063,9 +1071,7 @@ int cf_util_get_string_buffer(const oconfig_item_t *ci, char *buffer, /* {{{ */
     return EINVAL;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    ERROR("cf_util_get_string_buffer: The %s option requires "
-          "exactly one string argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one string argument.", ci->key);
     return -1;
   }
 
@@ -1082,9 +1088,7 @@ int cf_util_get_int(const oconfig_item_t *ci, int *ret_value) /* {{{ */
     return EINVAL;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    ERROR("cf_util_get_int: The %s option requires "
-          "exactly one numeric argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one numeric argument.", ci->key);
     return -1;
   }
 
@@ -1099,9 +1103,7 @@ int cf_util_get_double(const oconfig_item_t *ci, double *ret_value) /* {{{ */
     return EINVAL;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    ERROR("cf_util_get_double: The %s option requires "
-          "exactly one numeric argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one numeric argument.", ci->key);
     return -1;
   }
 
@@ -1117,9 +1119,7 @@ int cf_util_get_boolean(const oconfig_item_t *ci, bool *ret_bool) /* {{{ */
 
   if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_BOOLEAN) &&
                                 (ci->values[0].type != OCONFIG_TYPE_STRING))) {
-    ERROR("cf_util_get_boolean: The %s option requires "
-          "exactly one boolean argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one boolean argument.", ci->key);
     return -1;
   }
 
@@ -1128,19 +1128,19 @@ int cf_util_get_boolean(const oconfig_item_t *ci, bool *ret_bool) /* {{{ */
     *ret_bool = ci->values[0].value.boolean ? true : false;
     break;
   case OCONFIG_TYPE_STRING:
-    WARNING("cf_util_get_boolean: Using string value `%s' for boolean option "
-            "`%s' is deprecated and will be removed in future releases. "
-            "Use unquoted true or false instead.",
-            ci->values[0].value.string, ci->key);
+    P_WARNING("Using string value `%s' for boolean option `%s' is deprecated "
+              "and will be removed in future releases. Use unquoted true or "
+              "false instead.",
+              ci->values[0].value.string, ci->key);
 
     if (IS_TRUE(ci->values[0].value.string))
       *ret_bool = true;
     else if (IS_FALSE(ci->values[0].value.string))
       *ret_bool = false;
     else {
-      ERROR("cf_util_get_boolean: Cannot parse string value `%s' of the `%s' "
-            "option as a boolean value.",
-            ci->values[0].value.string, ci->key);
+      P_ERROR("Cannot parse string value `%s' of the `%s' option as a boolean "
+              "value.",
+              ci->values[0].value.string, ci->key);
       return -1;
     }
     break;
@@ -1181,9 +1181,7 @@ int cf_util_get_port_number(const oconfig_item_t *ci) /* {{{ */
 
   if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_STRING) &&
                                 (ci->values[0].type != OCONFIG_TYPE_NUMBER))) {
-    ERROR("cf_util_get_port_number: The \"%s\" option requires "
-          "exactly one string argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one string argument.", ci->key);
     return -1;
   }
 
@@ -1193,11 +1191,9 @@ int cf_util_get_port_number(const oconfig_item_t *ci) /* {{{ */
   assert(ci->values[0].type == OCONFIG_TYPE_NUMBER);
   tmp = (int)(ci->values[0].value.number + 0.5);
   if ((tmp < 1) || (tmp > 65535)) {
-    ERROR("cf_util_get_port_number: The \"%s\" option requires "
-          "a service name or a port number. The number "
-          "you specified, %i, is not in the valid "
-          "range of 1-65535.",
-          ci->key, tmp);
+    P_ERROR("The `%s' option requires a service name or a port number. The "
+            "number you specified, %i, is not in the valid range of 1-65535.",
+            ci->key, tmp);
     return -1;
   }
 
@@ -1211,18 +1207,15 @@ int cf_util_get_service(const oconfig_item_t *ci, char **ret_string) /* {{{ */
   int status;
 
   if (ci->values_num != 1) {
-    ERROR("cf_util_get_service: The %s option requires exactly "
-          "one argument.",
-          ci->key);
+    P_ERROR("The `%s` option requires exactly one argument.", ci->key);
     return -1;
   }
 
   if (ci->values[0].type == OCONFIG_TYPE_STRING)
     return cf_util_get_string(ci, ret_string);
   if (ci->values[0].type != OCONFIG_TYPE_NUMBER) {
-    ERROR("cf_util_get_service: The %s option requires "
-          "exactly one string or numeric argument.",
-          ci->key);
+    P_ERROR("The `%s` option requires exactly one string or numeric argument.",
+            ci->key);
   }
 
   port = 0;
@@ -1230,16 +1223,14 @@ int cf_util_get_service(const oconfig_item_t *ci, char **ret_string) /* {{{ */
   if (status != 0)
     return status;
   else if ((port < 1) || (port > 65535)) {
-    ERROR("cf_util_get_service: The port number given "
-          "for the %s option is out of "
-          "range (%i).",
-          ci->key, port);
+    P_ERROR("The port number given for the `%s` option is out of range (%i).",
+            ci->key, port);
     return -1;
   }
 
   service = malloc(6);
   if (service == NULL) {
-    ERROR("cf_util_get_service: Out of memory.");
+    P_ERROR("cf_util_get_service: Out of memory.");
     return -1;
   }
   snprintf(service, 6, "%i", port);
@@ -1256,16 +1247,13 @@ int cf_util_get_cdtime(const oconfig_item_t *ci, cdtime_t *ret_value) /* {{{ */
     return EINVAL;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    ERROR("cf_util_get_cdtime: The %s option requires "
-          "exactly one numeric argument.",
-          ci->key);
+    P_ERROR("The `%s' option requires exactly one numeric argument.", ci->key);
     return -1;
   }
 
   if (ci->values[0].value.number < 0.0) {
-    ERROR("cf_util_get_cdtime: The numeric argument of the %s "
-          "option must not be negative.",
-          ci->key);
+    P_ERROR("The numeric argument of the `%s' option must not be negative.",
+            ci->key);
     return -1;
   }
 
index 427a813..73d7c84 100644 (file)
@@ -292,10 +292,10 @@ static int register_callback(llist_t **list, /* {{{ */
     old_cf = le->value;
     le->value = cf;
 
-    WARNING("plugin: register_callback: "
-            "a callback named `%s' already exists - "
-            "overwriting the old entry!",
-            name);
+    P_WARNING("register_callback: "
+              "a callback named `%s' already exists - "
+              "overwriting the old entry!",
+              name);
 
     destroy_callback(old_cf);
     sfree(key);
@@ -315,7 +315,7 @@ static void log_list_callbacks(llist_t **list, /* {{{ */
 
   n = llist_size(*list);
   if (n == 0) {
-    INFO("%s [none]", comment);
+    INFO("%s: [none]", comment);
     return;
   }
 
@@ -346,19 +346,22 @@ static void log_list_callbacks(llist_t **list, /* {{{ */
 static int create_register_callback(llist_t **list, /* {{{ */
                                     const char *name, void *callback,
                                     user_data_t const *ud) {
-  callback_func_t *cf;
 
-  cf = calloc(1, sizeof(*cf));
+  if (name == NULL || callback == NULL)
+    return EINVAL;
+
+  callback_func_t *cf = calloc(1, sizeof(*cf));
   if (cf == NULL) {
     free_userdata(ud);
     ERROR("plugin: create_register_callback: calloc failed.");
-    return -1;
+    return ENOMEM;
   }
 
   cf->cf_callback = callback;
   if (ud == NULL) {
-    cf->cf_udata.data = NULL;
-    cf->cf_udata.free_func = NULL;
+    cf->cf_udata = (user_data_t){
+        .data = NULL, .free_func = NULL,
+    };
   } else {
     cf->cf_udata = *ud;
   }
@@ -710,25 +713,8 @@ plugin_value_list_clone(value_list_t const *vl_orig) /* {{{ */
     vl->time = cdtime();
 
   /* Fill in the interval from the thread context, if it is zero. */
-  if (vl->interval == 0) {
-    plugin_ctx_t ctx = plugin_get_ctx();
-
-    if (ctx.interval != 0)
-      vl->interval = ctx.interval;
-    else {
-      char name[6 * DATA_MAX_NAME_LEN];
-      FORMAT_VL(name, sizeof(name), vl);
-      ERROR("plugin_value_list_clone: Unable to determine "
-            "interval from context for "
-            "value list \"%s\". "
-            "This indicates a broken plugin. "
-            "Please report this problem to the "
-            "collectd mailing list or at "
-            "<http://collectd.org/bugs/>.",
-            name);
-      vl->interval = cf_get_default_interval();
-    }
-  }
+  if (vl->interval == 0)
+    vl->interval = plugin_get_interval();
 
   return vl;
 } /* }}} value_list_t *plugin_value_list_clone */
@@ -1111,9 +1097,9 @@ static int plugin_insert_read(read_func_t *rf) {
   le = llist_search(read_list, rf->rf_name);
   if (le != NULL) {
     pthread_mutex_unlock(&read_lock);
-    WARNING("The read function \"%s\" is already registered. "
-            "Check for duplicates in your configuration!",
-            rf->rf_name);
+    P_WARNING("The read function \"%s\" is already registered. "
+              "Check for duplicates in your configuration!",
+              rf->rf_name);
     return EINVAL;
   }
 
@@ -1159,6 +1145,7 @@ int plugin_register_read(const char *name, int (*callback)(void)) {
   rf->rf_name = strdup(name);
   rf->rf_type = RF_SIMPLE;
   rf->rf_interval = plugin_get_interval();
+  rf->rf_ctx.interval = rf->rf_interval;
 
   status = plugin_insert_read(rf);
   if (status != 0) {
@@ -1200,6 +1187,7 @@ int plugin_register_complex_read(const char *group, const char *name,
   }
 
   rf->rf_ctx = plugin_get_ctx();
+  rf->rf_ctx.interval = rf->rf_interval;
 
   status = plugin_insert_read(rf);
   if (status != 0) {
@@ -1714,8 +1702,12 @@ int plugin_write(const char *plugin, /* {{{ */
       callback_func_t *cf = le->value;
       plugin_write_cb callback;
 
-      /* do not switch plugin context; rather keep the context (interval)
-       * information of the calling read plugin */
+      /* Keep the read plugin's interval and flush information but update the
+       * plugin name. */
+      plugin_ctx_t old_ctx = plugin_get_ctx();
+      plugin_ctx_t ctx = old_ctx;
+      ctx.name = cf->cf_ctx.name;
+      plugin_set_ctx(ctx);
 
       DEBUG("plugin: plugin_write: Writing values via %s.", le->key);
       callback = cf->cf_callback;
@@ -1725,6 +1717,7 @@ int plugin_write(const char *plugin, /* {{{ */
       else
         success++;
 
+      plugin_set_ctx(old_ctx);
       le = le->next;
     }
 
@@ -2236,6 +2229,21 @@ void plugin_log(int level, const char *format, ...) {
   }
 } /* void plugin_log */
 
+void daemon_log(int level, const char *format, ...) {
+  char msg[1024] = ""; // Size inherits from plugin_log()
+
+  char const *name = plugin_get_ctx().name;
+  if (name == NULL)
+    name = "UNKNOWN";
+
+  va_list ap;
+  va_start(ap, format);
+  vsnprintf(msg, sizeof(msg), format, ap);
+  va_end(ap);
+
+  plugin_log(level, "%s plugin: %s", name, msg);
+} /* void daemon_log */
+
 int parse_log_severity(const char *severity) {
   int log_level = -1;
 
@@ -2275,7 +2283,7 @@ const data_set_t *plugin_get_ds(const char *name) {
   data_set_t *ds;
 
   if (data_sets == NULL) {
-    ERROR("plugin_get_ds: No data sets are defined yet.");
+    P_ERROR("plugin_get_ds: No data sets are defined yet.");
     return NULL;
   }
 
@@ -2506,6 +2514,8 @@ cdtime_t plugin_get_interval(void) {
   if (interval > 0)
     return interval;
 
+  P_ERROR("plugin_get_interval: Unable to determine Interval from context.");
+
   return cf_get_default_interval();
 } /* cdtime_t plugin_get_interval */
 
index 0369067..871eccd 100644 (file)
@@ -171,6 +171,7 @@ struct user_data_s {
 typedef struct user_data_s user_data_t;
 
 struct plugin_ctx_s {
+  char *name;
   cdtime_t interval;
   cdtime_t flush_interval;
   cdtime_t flush_timeout;
@@ -398,6 +399,15 @@ int parse_notif_severity(const char *severity);
 #define DEBUG(...) /* noop */
 #endif             /* ! COLLECT_DEBUG */
 
+/* This will log messages, prefixed by plugin name */
+void daemon_log(int level, const char *format, ...)
+    __attribute__((format(printf, 2, 3)));
+
+#define P_ERROR(...) daemon_log(LOG_ERR, __VA_ARGS__)
+#define P_WARNING(...) daemon_log(LOG_WARNING, __VA_ARGS__)
+#define P_NOTICE(...) daemon_log(LOG_NOTICE, __VA_ARGS__)
+#define P_INFO(...) daemon_log(LOG_INFO, __VA_ARGS__)
+
 const data_set_t *plugin_get_ds(const char *name);
 
 int plugin_notification_meta_add_string(notification_t *n, const char *name,
index 8f8334e..1624f0e 100644 (file)
@@ -56,7 +56,19 @@ int plugin_register_init(const char *name, plugin_init_cb callback) {
   return ENOTSUP;
 }
 
-int plugin_register_read(const char *name, int (*callback)(void)) {
+int plugin_register_read(__attribute__((unused)) const char *name,
+                         __attribute__((unused)) int (*callback)(void)) {
+  return ENOTSUP;
+}
+
+int plugin_register_write(__attribute__((unused)) const char *name,
+                          __attribute__((unused)) plugin_write_cb callback,
+                          __attribute__((unused)) user_data_t const *ud) {
+  return ENOTSUP;
+}
+
+int plugin_register_missing(const char *name, plugin_missing_cb callback,
+                            user_data_t const *ud) {
   return ENOTSUP;
 }
 
@@ -158,6 +170,17 @@ void plugin_log(int level, char const *format, ...) {
   printf("plugin_log (%i, \"%s\");\n", level, buffer);
 }
 
+void daemon_log(int level, char const *format, ...) {
+  char buffer[1024];
+  va_list ap;
+
+  va_start(ap, format);
+  vsnprintf(buffer, sizeof(buffer), format, ap);
+  va_end(ap);
+
+  printf("daemon_log (%i, \"%s\");\n", level, buffer);
+}
+
 void plugin_init_ctx(void) { /* nop */
 }
 
index 5389d12..1495a80 100644 (file)
@@ -24,8 +24,8 @@
  *   Florian octo Forster <octo at collectd.org>
  */
 
-#include <errno.h>
 #include "utils_cache.h"
+#include <errno.h>
 
 gauge_t *uc_get_rate(__attribute__((unused)) data_set_t const *ds,
                      __attribute__((unused)) value_list_t const *vl) {
@@ -41,3 +41,8 @@ int uc_get_rate_by_name(const char *name, gauge_t **ret_values,
 int uc_get_names(char ***ret_names, cdtime_t **ret_times, size_t *ret_number) {
   return ENOTSUP;
 }
+
+int uc_get_value_by_name(const char *name, value_t **ret_values,
+                         size_t *ret_values_num) {
+  return ENOTSUP;
+}
index 1909d8c..fe9bc51 100644 (file)
--- a/src/dbi.c
+++ b/src/dbi.c
@@ -64,8 +64,6 @@ struct cdbi_database_s /* {{{ */
   char *select_db;
   char *plugin_name;
 
-  cdtime_t interval;
-
   char *driver;
   char *host;
   cdbi_driver_option_t *driver_options;
@@ -267,6 +265,7 @@ static int cdbi_config_add_database_driver_option(cdbi_database_t *db, /* {{{ */
 
 static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */
 {
+  cdtime_t interval = 0;
   cdbi_database_t *db;
   int status;
 
@@ -304,7 +303,7 @@ static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */
     else if (strcasecmp("Host", child->key) == 0)
       status = cf_util_get_string(child, &db->host);
     else if (strcasecmp("Interval", child->key) == 0)
-      status = cf_util_get_cdtime(child, &db->interval);
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Plugin", child->key) == 0)
       status = cf_util_get_string(child, &db->plugin_name);
     else {
@@ -370,7 +369,7 @@ static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */
           /* group = */ NULL,
           /* name = */ name ? name : db->name,
           /* callback = */ cdbi_read_database,
-          /* interval = */ (db->interval > 0) ? db->interval : 0,
+          /* interval = */ interval,
           &(user_data_t){
               .data = db,
           });
@@ -550,8 +549,7 @@ static int cdbi_read_database_query(cdbi_database_t *db, /* {{{ */
   status = udb_query_prepare_result(
       q, prep_area, (db->host ? db->host : hostname_g),
       /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "dbi",
-      db->name, column_names, column_num,
-      /* interval = */ (db->interval > 0) ? db->interval : 0);
+      db->name, column_names, column_num);
 
   if (status != 0) {
     ERROR("dbi plugin: udb_query_prepare_result failed with status %i.",
index 9970be0..2a44b2c 100644 (file)
@@ -419,8 +419,12 @@ static int dpdk_events_config(oconfig_item_t *ci) {
 static int dpdk_helper_link_status_get(dpdk_helper_ctx_t *phc) {
   dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(phc);
 
-  /* get Link Status values from DPDK */
+/* get Link Status values from DPDK */
+#if RTE_VERSION < RTE_VERSION_NUM(18, 05, 0, 0)
   uint8_t nb_ports = rte_eth_dev_count();
+#else
+  uint8_t nb_ports = rte_eth_dev_count_avail();
+#endif
   if (nb_ports == 0) {
     DPDK_CHILD_LOG("dpdkevent-helper: No DPDK ports available. "
                    "Check bound devices to DPDK driver.\n");
index ef1a138..9091ff5 100644 (file)
@@ -154,26 +154,6 @@ static int fc_config_add_dir_instance(fc_directory_conf_t *dir,
   return fc_config_set_instance(dir, ci->values[0].value.string);
 } /* int fc_config_add_dir_instance */
 
-static int fc_config_add_dir_name(fc_directory_conf_t *dir,
-                                  oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("filecount plugin: The `Name' config option needs exactly one "
-            "string argument.");
-    return -1;
-  }
-
-  char *temp = strdup(ci->values[0].value.string);
-  if (temp == NULL) {
-    ERROR("filecount plugin: strdup failed.");
-    return -1;
-  }
-
-  sfree(dir->name);
-  dir->name = temp;
-
-  return 0;
-} /* int fc_config_add_dir_name */
-
 static int fc_config_add_dir_mtime(fc_directory_conf_t *dir,
                                    oconfig_item_t *ci) {
   if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_STRING) &&
@@ -369,7 +349,7 @@ static int fc_config_add_dir(oconfig_item_t *ci) {
     else if (strcasecmp("Instance", option->key) == 0)
       status = fc_config_add_dir_instance(dir, option);
     else if (strcasecmp("Name", option->key) == 0)
-      status = fc_config_add_dir_name(dir, option);
+      status = cf_util_get_string(option, &dir->name);
     else if (strcasecmp("MTime", option->key) == 0)
       status = fc_config_add_dir_mtime(dir, option);
     else if (strcasecmp("Size", option->key) == 0)
index ca65419..2bca05a 100644 (file)
@@ -833,28 +833,6 @@ static int mc_receive_thread_stop(void) /* {{{ */
  *   </Metric>
  * </Plugin>
  */
-static int gmond_config_set_string(oconfig_item_t *ci, char **str) /* {{{ */
-{
-  char *tmp;
-
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("gmond plugin: The `%s' option needs "
-            "exactly one string argument.",
-            ci->key);
-    return -1;
-  }
-
-  tmp = strdup(ci->values[0].value.string);
-  if (tmp == NULL) {
-    ERROR("gmond plugin: strdup failed.");
-    return -1;
-  }
-
-  sfree(*str);
-  *str = tmp;
-  return 0;
-} /* }}} int gmond_config_set_string */
-
 static int gmond_config_add_metric(oconfig_item_t *ci) /* {{{ */
 {
   metric_map_t *map;
@@ -889,11 +867,11 @@ static int gmond_config_add_metric(oconfig_item_t *ci) /* {{{ */
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
     if (strcasecmp("Type", child->key) == 0)
-      gmond_config_set_string(child, &map->type);
+      cf_util_get_string(child, &map->type);
     else if (strcasecmp("TypeInstance", child->key) == 0)
-      gmond_config_set_string(child, &map->type_instance);
+      cf_util_get_string(child, &map->type_instance);
     else if (strcasecmp("DataSource", child->key) == 0)
-      gmond_config_set_string(child, &map->ds_name);
+      cf_util_get_string(child, &map->ds_name);
     else {
       WARNING("gmond plugin: Unknown configuration option `%s' ignored.",
               child->key);
index 13e388e..f293aa1 100644 (file)
@@ -123,21 +123,6 @@ static int cmc_page_init_memc(web_page_t *wp) /* {{{ */
   return 0;
 } /* }}} int cmc_page_init_memc */
 
-static int cmc_config_add_string(const char *name, char **dest, /* {{{ */
-                                 oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("memcachec plugin: `%s' needs exactly one string argument.", name);
-    return -1;
-  }
-
-  sfree(*dest);
-  *dest = strdup(ci->values[0].value.string);
-  if (*dest == NULL)
-    return -1;
-
-  return 0;
-} /* }}} int cmc_config_add_string */
-
 static int cmc_config_add_match_dstype(int *dstype_ret, /* {{{ */
                                        oconfig_item_t *ci) {
   int dstype;
@@ -204,16 +189,15 @@ static int cmc_config_add_match(web_page_t *page, /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("Regex", child->key) == 0)
-      status = cmc_config_add_string("Regex", &match->regex, child);
+      status = cf_util_get_string(child, &match->regex);
     else if (strcasecmp("ExcludeRegex", child->key) == 0)
-      status =
-          cmc_config_add_string("ExcludeRegex", &match->exclude_regex, child);
+      status = cf_util_get_string(child, &match->exclude_regex);
     else if (strcasecmp("DSType", child->key) == 0)
       status = cmc_config_add_match_dstype(&match->dstype, child);
     else if (strcasecmp("Type", child->key) == 0)
-      status = cmc_config_add_string("Type", &match->type, child);
+      status = cf_util_get_string(child, &match->type);
     else if (strcasecmp("Instance", child->key) == 0)
-      status = cmc_config_add_string("Instance", &match->instance, child);
+      status = cf_util_get_string(child, &match->instance);
     else {
       WARNING("memcachec plugin: Option `%s' not allowed here.", child->key);
       status = -1;
@@ -301,11 +285,11 @@ static int cmc_config_add_page(oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("Server", child->key) == 0)
-      status = cmc_config_add_string("Server", &page->server, child);
+      status = cf_util_get_string(child, &page->server);
     else if (strcasecmp("Key", child->key) == 0)
-      status = cmc_config_add_string("Key", &page->key, child);
+      status = cf_util_get_string(child, &page->key);
     else if (strcasecmp("Plugin", child->key) == 0)
-      status = cmc_config_add_string("Plugin", &page->plugin_name, child);
+      status = cf_util_get_string(child, &page->plugin_name);
     else if (strcasecmp("Match", child->key) == 0)
       /* Be liberal with failing matches => don't set `status'. */
       cmc_config_add_match(page, child);
index bb9eaa0..0a4f40c 100644 (file)
@@ -129,7 +129,6 @@ struct mb_host_s /* {{{ */
   int port;     /* for Modbus/TCP */
   int baudrate; /* for Modbus/RTU */
   mb_conntype_t conntype;
-  cdtime_t interval;
 
   mb_slave_t *slaves;
   size_t slaves_num;
@@ -252,15 +251,11 @@ static int mb_submit(mb_host_t *host, mb_slave_t *slave, /* {{{ */
   if ((host == NULL) || (slave == NULL) || (data == NULL))
     return EINVAL;
 
-  if (host->interval == 0)
-    host->interval = plugin_get_interval();
-
   if (slave->instance[0] == 0)
     snprintf(slave->instance, sizeof(slave->instance), "slave_%i", slave->id);
 
   vl.values = &value;
   vl.values_len = 1;
-  vl.interval = host->interval;
   sstrncpy(vl.host, host->host, sizeof(vl.host));
   sstrncpy(vl.plugin, "modbus", sizeof(vl.plugin));
   sstrncpy(vl.plugin_instance, slave->instance, sizeof(vl.plugin_instance));
@@ -952,6 +947,7 @@ static int mb_config_add_slave(mb_host_t *host, oconfig_item_t *ci) /* {{{ */
 
 static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */
 {
+  cdtime_t interval = 0;
   mb_host_t *host;
   int status;
 
@@ -992,7 +988,7 @@ static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */
     } else if (strcasecmp("Baudrate", child->key) == 0)
       status = cf_util_get_int(child, &host->baudrate);
     else if (strcasecmp("Interval", child->key) == 0)
-      status = cf_util_get_cdtime(child, &host->interval);
+      status = cf_util_get_cdtime(child, &interval);
     else if (strcasecmp("Slave", child->key) == 0)
       /* Don't set status: Gracefully continue if a slave fails. */
       mb_config_add_slave(host, child);
@@ -1033,7 +1029,7 @@ static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */
 
     plugin_register_complex_read(/* group = */ NULL, name,
                                  /* callback = */ mb_read,
-                                 /* interval = */ host->interval,
+                                 /* interval = */ interval,
                                  &(user_data_t){
                                      .data = host, .free_func = host_free,
                                  });
index 281764c..62600ca 100644 (file)
@@ -2229,42 +2229,6 @@ static int cna_query_system(host_config_t *host) /* {{{ */
 /*
  * Configuration handling
  */
-/* Sets a given flag if the boolean argument is true and unsets the flag if it
- * is false. On error, the flag-field is not changed. */
-static int cna_config_bool_to_flag(const oconfig_item_t *ci, /* {{{ */
-                                   uint32_t *flags, uint32_t flag) {
-  if ((ci == NULL) || (flags == NULL))
-    return EINVAL;
-
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) {
-    WARNING("netapp plugin: The %s option needs exactly one boolean argument.",
-            ci->key);
-    return -1;
-  }
-
-  if (ci->values[0].value.boolean)
-    *flags |= flag;
-  else
-    *flags &= ~flag;
-
-  return 0;
-} /* }}} int cna_config_bool_to_flag */
-
-/* Handling of the "Interval" option which is allowed in every block. */
-static int cna_config_get_interval(const oconfig_item_t *ci, /* {{{ */
-                                   cna_interval_t *out_interval) {
-  cdtime_t tmp = 0;
-  int status;
-
-  status = cf_util_get_cdtime(ci, &tmp);
-  if (status != 0)
-    return status;
-
-  out_interval->interval = tmp;
-  out_interval->last_read = 0;
-
-  return 0;
-} /* }}} int cna_config_get_interval */
 
 /* Handling of the "GetIO", "GetOps" and "GetLatency" options within a
  * <VolumePerf /> block. */
@@ -2385,7 +2349,7 @@ static int cna_config_volume_performance(host_config_t *host, /* {{{ */
 
     /* if (!item || !item->key || !*item->key) continue; */
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_volume_perf->interval);
+      cf_util_get_cdtime(item, &cfg_volume_perf->interval.interval);
     else if (!strcasecmp(item->key, "GetIO"))
       cna_config_volume_perf_option(cfg_volume_perf, item);
     else if (!strcasecmp(item->key, "GetOps"))
@@ -2481,7 +2445,7 @@ static int cna_config_quota(host_config_t *host, oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *item = ci->children + i;
 
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_quota->interval);
+      cf_util_get_cdtime(item, &cfg_quota->interval.interval);
     else
       WARNING("netapp plugin: The option %s is not allowed within "
               "`Quota' blocks.",
@@ -2517,9 +2481,9 @@ static int cna_config_disk(host_config_t *host, oconfig_item_t *ci) { /* {{{ */
 
     /* if (!item || !item->key || !*item->key) continue; */
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_disk->interval);
+      cf_util_get_cdtime(item, &cfg_disk->interval.interval);
     else if (strcasecmp(item->key, "GetBusy") == 0)
-      cna_config_bool_to_flag(item, &cfg_disk->flags, CFG_DISK_BUSIEST);
+      cf_util_get_flag(item, &cfg_disk->flags, CFG_DISK_BUSIEST);
   }
 
   if ((cfg_disk->flags & CFG_DISK_ALL) == 0) {
@@ -2556,15 +2520,15 @@ static int cna_config_wafl(host_config_t *host, oconfig_item_t *ci) /* {{{ */
     oconfig_item_t *item = ci->children + i;
 
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_wafl->interval);
+      cf_util_get_cdtime(item, &cfg_wafl->interval.interval);
     else if (!strcasecmp(item->key, "GetNameCache"))
-      cna_config_bool_to_flag(item, &cfg_wafl->flags, CFG_WAFL_NAME_CACHE);
+      cf_util_get_flag(item, &cfg_wafl->flags, CFG_WAFL_NAME_CACHE);
     else if (!strcasecmp(item->key, "GetDirCache"))
-      cna_config_bool_to_flag(item, &cfg_wafl->flags, CFG_WAFL_DIR_CACHE);
+      cf_util_get_flag(item, &cfg_wafl->flags, CFG_WAFL_DIR_CACHE);
     else if (!strcasecmp(item->key, "GetBufferCache"))
-      cna_config_bool_to_flag(item, &cfg_wafl->flags, CFG_WAFL_BUF_CACHE);
+      cf_util_get_flag(item, &cfg_wafl->flags, CFG_WAFL_BUF_CACHE);
     else if (!strcasecmp(item->key, "GetInodeCache"))
-      cna_config_bool_to_flag(item, &cfg_wafl->flags, CFG_WAFL_INODE_CACHE);
+      cf_util_get_flag(item, &cfg_wafl->flags, CFG_WAFL_INODE_CACHE);
     else
       WARNING("netapp plugin: The %s config option is not allowed within "
               "`WAFL' blocks.",
@@ -2636,7 +2600,7 @@ static int cna_config_volume_usage(host_config_t *host, /* {{{ */
 
     /* if (!item || !item->key || !*item->key) continue; */
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_volume_usage->interval);
+      cf_util_get_cdtime(item, &cfg_volume_usage->interval.interval);
     else if (!strcasecmp(item->key, "GetCapacity"))
       cna_config_volume_usage_option(cfg_volume_usage, item);
     else if (!strcasecmp(item->key, "GetSnapshot"))
@@ -2677,7 +2641,7 @@ static int cna_config_snapvault(host_config_t *host, /* {{{ */
     oconfig_item_t *item = ci->children + i;
 
     if (strcasecmp(item->key, "Interval") == 0)
-      cna_config_get_interval(item, &cfg_snapvault->interval);
+      cf_util_get_cdtime(item, &cfg_snapvault->interval.interval);
     else
       WARNING("netapp plugin: The option %s is not allowed within "
               "`SnapVault' blocks.",
@@ -2712,15 +2676,15 @@ static int cna_config_system(host_config_t *host, /* {{{ */
     oconfig_item_t *item = ci->children + i;
 
     if (strcasecmp(item->key, "Interval") == 0) {
-      cna_config_get_interval(item, &cfg_system->interval);
+      cf_util_get_cdtime(item, &cfg_system->interval.interval);
     } else if (!strcasecmp(item->key, "GetCPULoad")) {
-      cna_config_bool_to_flag(item, &cfg_system->flags, CFG_SYSTEM_CPU);
+      cf_util_get_flag(item, &cfg_system->flags, CFG_SYSTEM_CPU);
     } else if (!strcasecmp(item->key, "GetInterfaces")) {
-      cna_config_bool_to_flag(item, &cfg_system->flags, CFG_SYSTEM_NET);
+      cf_util_get_flag(item, &cfg_system->flags, CFG_SYSTEM_NET);
     } else if (!strcasecmp(item->key, "GetDiskOps")) {
-      cna_config_bool_to_flag(item, &cfg_system->flags, CFG_SYSTEM_OPS);
+      cf_util_get_flag(item, &cfg_system->flags, CFG_SYSTEM_OPS);
     } else if (!strcasecmp(item->key, "GetDiskIO")) {
-      cna_config_bool_to_flag(item, &cfg_system->flags, CFG_SYSTEM_DISK);
+      cf_util_get_flag(item, &cfg_system->flags, CFG_SYSTEM_DISK);
     } else {
       WARNING("netapp plugin: The %s config option is not allowed within "
               "`System' blocks.",
index 84ba00b..d602dfc 100644 (file)
@@ -113,6 +113,7 @@ struct sockent_client {
 #endif
   cdtime_t next_resolve_reconnect;
   cdtime_t resolve_interval;
+  struct sockaddr_storage *bind_addr;
 };
 
 struct sockent_server {
@@ -1501,6 +1502,7 @@ static void free_sockent_client(struct sockent_client *sec) /* {{{ */
     sec->fd = -1;
   }
   sfree(sec->addr);
+  sfree(sec->bind_addr);
 #if HAVE_GCRYPT_H
   sfree(sec->username);
   sfree(sec->password);
@@ -1683,6 +1685,42 @@ static int network_set_interface(const sockent_t *se,
   return 0;
 } /* }}} network_set_interface */
 
+static int network_bind_socket_to_addr(sockent_t *se,
+                                       const struct addrinfo *ai) {
+
+  if (se->data.client.bind_addr == NULL)
+    return 0;
+
+  DEBUG("network_plugin: fd %i: bind socket to address", se->data.client.fd);
+  char pbuffer[64];
+
+  if (ai->ai_family == AF_INET) {
+    struct sockaddr_in *addr =
+        (struct sockaddr_in *)(se->data.client.bind_addr);
+    inet_ntop(AF_INET, &(addr->sin_addr), pbuffer, 64);
+    DEBUG("network_plugin: binding client socket to ipv4 address: %s", pbuffer);
+    if (bind(se->data.client.fd, (struct sockaddr *)addr, sizeof(*addr)) ==
+        -1) {
+      ERROR("network plugin: failed to bind client socket (ipv4) to %s: %s",
+            pbuffer, STRERRNO);
+      return -1;
+    }
+  } else if (ai->ai_family == AF_INET6) {
+    struct sockaddr_in6 *addr =
+        (struct sockaddr_in6 *)(se->data.client.bind_addr);
+    inet_ntop(AF_INET6, &(addr->sin6_addr), pbuffer, 64);
+    DEBUG("network_plugin: binding client socket to ipv6 address: %s", pbuffer);
+    if (bind(se->data.client.fd, (struct sockaddr *)addr, sizeof(*addr)) ==
+        -1) {
+      ERROR("network plugin: failed to bind client socket (ipv6) to %s: %s",
+            pbuffer, STRERRNO);
+      return -1;
+    }
+  }
+
+  return 0;
+} /* int network_bind_socket_to_addr */
+
 static int network_bind_socket(int fd, const struct addrinfo *ai,
                                const int interface_idx) {
 #if KERNEL_SOLARIS
@@ -1834,6 +1872,7 @@ static sockent_t *sockent_create(int type) /* {{{ */
   } else {
     se->data.client.fd = -1;
     se->data.client.addr = NULL;
+    se->data.client.bind_addr = NULL;
     se->data.client.resolve_interval = 0;
     se->data.client.next_resolve_reconnect = 0;
 #if HAVE_GCRYPT_H
@@ -1989,6 +2028,7 @@ static int sockent_client_connect(sockent_t *se) /* {{{ */
 
     network_set_ttl(se, ai_ptr);
     network_set_interface(se, ai_ptr);
+    network_bind_socket_to_addr(se, ai_ptr);
 
     /* We don't open more than one write-socket per
      * node/service pair.. */
@@ -2684,6 +2724,57 @@ static int network_config_set_interface(const oconfig_item_t *ci, /* {{{ */
   return 0;
 } /* }}} int network_config_set_interface */
 
+static int
+network_config_set_bind_address(const oconfig_item_t *ci,
+                                struct sockaddr_storage **bind_address) {
+  if ((*bind_address) != NULL) {
+    ERROR("network_plugin: only a single bind address is allowed");
+    return -1;
+  }
+
+  char addr_text[256];
+
+  if (cf_util_get_string_buffer(ci, addr_text, sizeof(addr_text)) != 0)
+    return -1;
+
+  int ret;
+  struct addrinfo *res = NULL;
+  struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
+                              .ai_flags = AI_NUMERICHOST,
+                              .ai_protocol = IPPROTO_UDP,
+                              .ai_socktype = SOCK_DGRAM};
+
+  ret = getaddrinfo(addr_text, NULL, &ai_hints, &res);
+  if (ret) {
+    ERROR("network plugin: Bind address option has invalid address set: %s",
+          gai_strerror(ret));
+    return -1;
+  }
+
+  *bind_address = malloc(sizeof(**bind_address));
+  if (*bind_address == NULL) {
+    ERROR("network plugin: network_config_set_bind_address: malloc failed.");
+    return -1;
+  }
+  (*bind_address)->ss_family = res->ai_family;
+  if (res->ai_family == AF_INET) {
+    struct sockaddr_in *addr = (struct sockaddr_in *)(*bind_address);
+    inet_pton(AF_INET, addr_text, &(addr->sin_addr));
+  } else if (res->ai_family == AF_INET6) {
+    struct sockaddr_in6 *addr = (struct sockaddr_in6 *)(*bind_address);
+    inet_pton(AF_INET6, addr_text, &(addr->sin6_addr));
+  } else {
+    ERROR("network plugin: %s is an unknown address format %d\n", addr_text,
+          res->ai_family);
+    sfree(*bind_address);
+    freeaddrinfo(res);
+    return -1;
+  }
+
+  freeaddrinfo(res);
+  return 0;
+} /* int network_config_set_bind_address */
+
 static int network_config_set_buffer_size(const oconfig_item_t *ci) /* {{{ */
 {
   int tmp = 0;
@@ -2843,6 +2934,8 @@ static int network_config_add_server(const oconfig_item_t *ci) /* {{{ */
 #endif /* HAVE_GCRYPT_H */
         if (strcasecmp("Interface", child->key) == 0)
       network_config_set_interface(child, &se->interface);
+    else if (strcasecmp("BindAddress", child->key) == 0)
+      network_config_set_bind_address(child, &se->data.client.bind_addr);
     else if (strcasecmp("ResolveInterval", child->key) == 0)
       cf_util_get_cdtime(child, &se->data.client.resolve_interval);
     else {
index 6b32ad9..bb36ff2 100644 (file)
@@ -211,8 +211,8 @@ static int notify_email_notification(const notification_t *n,
   char subject[MAXSTRING];
 
   char buf[4096] = "";
+  char *buf_ptr = buf;
   int buf_len = sizeof(buf);
-  int i;
 
   snprintf(severity, sizeof(severity), "%s",
            (n->severity == NOTIF_FAILURE)
@@ -231,15 +231,36 @@ static int notify_email_notification(const notification_t *n,
   timestamp_str[sizeof(timestamp_str) - 1] = '\0';
 
   /* Let's make RFC822 message text with \r\n EOLs */
-  snprintf(buf, buf_len, "MIME-Version: 1.0\r\n"
-                         "Content-Type: text/plain; charset=\"US-ASCII\"\r\n"
-                         "Content-Transfer-Encoding: 8bit\r\n"
-                         "Subject: %s\r\n"
-                         "\r\n"
-                         "%s - %s@%s\r\n"
-                         "\r\n"
-                         "Message: %s",
-           subject, timestamp_str, severity, n->host, n->message);
+  int status = snprintf(buf, buf_len,
+                        "MIME-Version: 1.0\r\n"
+                        "Content-Type: text/plain; charset=\"US-ASCII\"\r\n"
+                        "Content-Transfer-Encoding: 8bit\r\n"
+                        "Subject: %s\r\n"
+                        "\r\n"
+                        "%s - %s@%s\r\n"
+                        "\r\n",
+                        subject, timestamp_str, severity, n->host);
+
+  if (status > 0) {
+    buf_ptr += status;
+    buf_len -= status;
+  }
+
+#define APPEND(format, value)                                                  \
+  if ((buf_len > 0) && (strlen(value) > 0)) {                                  \
+    status = snprintf(buf_ptr, buf_len, format "\r\n", value);                 \
+    if (status > 0) {                                                          \
+      buf_ptr += status;                                                       \
+      buf_len -= status;                                                       \
+    }                                                                          \
+  }
+
+  APPEND("Host: %s", n->host);
+  APPEND("Plugin: %s", n->plugin);
+  APPEND("Plugin instance: %s", n->plugin_instance);
+  APPEND("Type: %s", n->type);
+  APPEND("Type instance: %s", n->type_instance);
+  APPEND("\r\nMessage: %s", n->message);
 
   pthread_mutex_lock(&session_lock);
 
@@ -258,7 +279,7 @@ static int notify_email_notification(const notification_t *n,
   smtp_set_header(message, "To", NULL, NULL);
   smtp_set_message_str(message, buf);
 
-  for (i = 0; i < recipients_len; i++)
+  for (int i = 0; i < recipients_len; i++)
     smtp_add_recipient(message, recipients[i]);
 
   /* Initiate a connection to the SMTP server and transfer the message. */
index baa1988..ef63498 100644 (file)
@@ -251,7 +251,7 @@ static const char *refclock_names[] = {
     "CHRONOLOG",  "DUMBCLOCK",    "ULINK_M320", "PCF",         /* 32-35 */
     "WWV_AUDIO",  "GPS_FG",       "HOPF_S",     "HOPF_P",      /* 36-39 */
     "JJY",        "TT_IRIG",      "GPS_ZYFER",  "GPS_RIPENCC", /* 40-43 */
-    "NEOCLK4X"                                                 /* 44    */
+    "NEOCLK4X",   "PCI_TSYNC",    "GPSD_JSON"                  /* 44-46 */
 };
 static size_t refclock_names_num = STATIC_ARRAY_SIZE(refclock_names);
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
@@ -779,17 +779,6 @@ static int ntpd_get_name_refclock(char *buffer, size_t buffer_size,
   return 0;
 } /* int ntpd_get_name_refclock */
 
-static int ntpd_get_name(char *buffer, size_t buffer_size,
-                         struct info_peer_summary const *peer_info) {
-  uint32_t addr = ntohl(peer_info->srcadr);
-
-  if (!peer_info->v6_flag && ((addr & REFCLOCK_MASK) == REFCLOCK_ADDR))
-    return ntpd_get_name_refclock(buffer, buffer_size, peer_info);
-  else
-    return ntpd_get_name_from_address(buffer, buffer_size, peer_info,
-                                      do_reverse_lookups);
-} /* int ntpd_addr_to_name */
-
 static int ntpd_read(void) {
   struct info_kernel *ik;
   int ik_num;
@@ -877,7 +866,15 @@ static int ntpd_read(void) {
 
     ptr = ps + i;
 
-    status = ntpd_get_name(peername, sizeof(peername), ptr);
+    int is_refclock = !ptr->v6_flag &&
+                      ((ntohl(ptr->srcadr) & REFCLOCK_MASK) == REFCLOCK_ADDR);
+
+    if (is_refclock)
+      status = ntpd_get_name_refclock(peername, sizeof(peername), ptr);
+    else
+      status = ntpd_get_name_from_address(peername, sizeof(peername), ptr,
+                                          do_reverse_lookups);
+
     if (status != 0) {
       ERROR("ntpd plugin: Determining name of peer failed.");
       continue;
@@ -895,6 +892,8 @@ static int ntpd_read(void) {
     M_LFPTOD(ntohl(ptr->offset_int), ntohl(ptr->offset_frc), offset);
 
     DEBUG("peer %i:\n"
+          "  is_refclock= %d\n"
+          "  refclock_id= %d\n"
           "  peername   = %s\n"
           "  srcadr     = 0x%08x\n"
           "  reach      = 0%03o\n"
@@ -903,16 +902,19 @@ static int ntpd_read(void) {
           "  offset_frc = %i\n"
           "  offset     = %f\n"
           "  dispersion = %f\n",
-          i, peername, ntohl(ptr->srcadr), ptr->reach, ntpd_read_fp(ptr->delay),
+          i, is_refclock, (is_refclock > 0) ? refclock_id : 0, peername,
+          ntohl(ptr->srcadr), ptr->reach, ntpd_read_fp(ptr->delay),
           ntohl(ptr->offset_int), ntohl(ptr->offset_frc), offset,
           ntpd_read_fp(ptr->dispersion));
 
-    if (refclock_id !=
-        1) /* not the system clock (offset will always be zero.. */
-      ntpd_submit_reach("time_offset", peername, ptr->reach, offset);
     ntpd_submit_reach("time_dispersion", peername, ptr->reach,
                       ntpd_read_fp(ptr->dispersion));
-    if (refclock_id == 0) /* not a reference clock */
+
+    /* not the system clock (offset will always be zero) */
+    if (!(is_refclock && refclock_id == 1))
+      ntpd_submit_reach("time_offset", peername, ptr->reach, offset);
+
+    if (!is_refclock) /* not a reference clock */
       ntpd_submit_reach("delay", peername, ptr->reach,
                         ntpd_read_fp(ptr->delay));
   }
index 4bca838..1b17d9c 100644 (file)
@@ -550,8 +550,7 @@ static int o_read_database_query(o_database_t *db, /* {{{ */
   status = udb_query_prepare_result(
       q, prep_area, (db->host != NULL) ? db->host : hostname_g,
       /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "oracle",
-      db->name, column_names, column_num,
-      /* interval = */ 0);
+      db->name, column_names, column_num);
   if (status != 0) {
     ERROR("oracle plugin: o_read_database_query (%s, %s): "
           "udb_query_prepare_result failed.",
index 31c68ad..fffbc21 100644 (file)
@@ -33,6 +33,7 @@
 /* do not automatically get the thread specific Perl interpreter */
 #define PERL_NO_GET_CONTEXT
 
+#include "collectd.h"
 #include <stdbool.h>
 
 #include <EXTERN.h>
index 7c140e0..ae887a1 100644 (file)
@@ -125,8 +125,6 @@ typedef struct {
   /* make sure we don't access the database object in parallel */
   pthread_mutex_t db_lock;
 
-  cdtime_t interval;
-
   /* writer "caching" settings */
   cdtime_t commit_interval;
   cdtime_t next_commit;
@@ -237,8 +235,6 @@ static c_psql_database_t *c_psql_database_new(const char *name) {
 
   pthread_mutex_init(&db->db_lock, /* attrs = */ NULL);
 
-  db->interval = 0;
-
   db->commit_interval = 0;
   db->next_commit = 0;
   db->expire_delay = 0;
@@ -431,8 +427,7 @@ static PGresult *c_psql_exec_query_params(c_psql_database_t *db, udb_query_t *q,
       break;
     case C_PSQL_PARAM_INTERVAL:
       snprintf(interval, sizeof(interval), "%.3f",
-               (db->interval > 0) ? CDTIME_T_TO_DOUBLE(db->interval)
-                                  : plugin_get_interval());
+               CDTIME_T_TO_DOUBLE(plugin_get_interval()));
       params[i] = interval;
       break;
     case C_PSQL_PARAM_INSTANCE:
@@ -548,7 +543,7 @@ static int c_psql_exec_query(c_psql_database_t *db, udb_query_t *q,
   status = udb_query_prepare_result(
       q, prep_area, host,
       (db->plugin_name != NULL) ? db->plugin_name : "postgresql", db->instance,
-      column_names, (size_t)column_num, db->interval);
+      column_names, (size_t)column_num);
 
   if (0 != status) {
     log_err("udb_query_prepare_result failed with status %i.", status);
@@ -1123,6 +1118,7 @@ static int c_psql_config_writer(oconfig_item_t *ci) {
 static int c_psql_config_database(oconfig_item_t *ci) {
   c_psql_database_t *db;
 
+  cdtime_t interval = 0;
   char cb_name[DATA_MAX_NAME_LEN];
   static bool have_flush;
 
@@ -1163,7 +1159,7 @@ static int c_psql_config_database(oconfig_item_t *ci) {
       config_add_writer(c, writers, writers_num, &db->writers,
                         &db->writers_num);
     else if (0 == strcasecmp(c->key, "Interval"))
-      cf_util_get_cdtime(c, &db->interval);
+      cf_util_get_cdtime(c, &interval);
     else if (strcasecmp("CommitInterval", c->key) == 0)
       cf_util_get_cdtime(c, &db->commit_interval);
     else if (strcasecmp("ExpireDelay", c->key) == 0)
@@ -1211,8 +1207,8 @@ static int c_psql_config_database(oconfig_item_t *ci) {
 
   if (db->queries_num > 0) {
     ++db->ref_cnt;
-    plugin_register_complex_read("postgresql", cb_name, c_psql_read,
-                                 /* interval = */ db->interval, &ud);
+    plugin_register_complex_read("postgresql", cb_name, c_psql_read, interval,
+                                 &ud);
   }
   if (db->writers_num > 0) {
     ++db->ref_cnt;
index eb3ec53..7a2fbfd 100644 (file)
@@ -359,9 +359,6 @@ static void submit(const char *plugin_instance, /* {{{ */
   }
 
   if (0 != parse_value(value_str, &value, ds->ds[0].type)) {
-    ERROR("powerdns plugin: Cannot convert `%s' "
-          "to a number.",
-          value_str);
     return;
   }
 
index 59eb49a..36b1d83 100644 (file)
@@ -58,7 +58,6 @@ static void submit(const char *protocol_name, const char *str_key,
 
   status = parse_value(str_value, &value, DS_TYPE_DERIVE);
   if (status != 0) {
-    ERROR("protocols plugin: Parsing string as integer failed: %s", str_value);
     return;
   }
 
index 5734098..41442b6 100644 (file)
 #include <hiredis/hiredis.h>
 #include <sys/time.h>
 
-#ifndef HOST_NAME_MAX
-#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
-#endif
-
 #define REDIS_DEF_HOST "localhost"
 #define REDIS_DEF_PASSWD ""
 #define REDIS_DEF_PORT 6379
-#define REDIS_DEF_TIMEOUT 2000
+#define REDIS_DEF_TIMEOUT_SEC 2
 #define REDIS_DEF_DB_COUNT 256
-#define MAX_REDIS_NODE_NAME 64
-#define MAX_REDIS_PASSWD_LENGTH 512
 #define MAX_REDIS_VAL_SIZE 256
 #define MAX_REDIS_QUERY 2048
 
@@ -65,57 +59,69 @@ struct redis_query_s {
   redis_query_t *next;
 };
 
+struct prev_s {
+  derive_t keyspace_hits;
+  derive_t keyspace_misses;
+};
+typedef struct prev_s prev_t;
+
 struct redis_node_s;
 typedef struct redis_node_s redis_node_t;
 struct redis_node_s {
-  char name[MAX_REDIS_NODE_NAME];
-  char host[HOST_NAME_MAX];
-  char passwd[MAX_REDIS_PASSWD_LENGTH];
+  char *name;
+  char *host;
+  char *passwd;
   int port;
   struct timeval timeout;
+  bool report_command_stats;
+  bool report_cpu_usage;
+  redisContext *redisContext;
   redis_query_t *queries;
+  prev_t prev;
 
   redis_node_t *next;
 };
 
-static redis_node_t *nodes_head;
-
-static int redis_node_add(const redis_node_t *rn) /* {{{ */
-{
-  redis_node_t *rn_copy;
-  redis_node_t *rn_ptr;
-
-  /* Check for duplicates first */
-  for (rn_ptr = nodes_head; rn_ptr != NULL; rn_ptr = rn_ptr->next)
-    if (strcmp(rn->name, rn_ptr->name) == 0)
-      break;
+static bool redis_have_instances;
+static int redis_read(user_data_t *user_data);
 
-  if (rn_ptr != NULL) {
-    ERROR("redis plugin: A node with the name `%s' already exists.", rn->name);
-    return -1;
-  }
+static void redis_node_free(void *arg) {
+  redis_node_t *rn = arg;
+  if (rn == NULL)
+    return;
 
-  rn_copy = malloc(sizeof(*rn_copy));
-  if (rn_copy == NULL) {
-    ERROR("redis plugin: malloc failed adding redis_node to the tree.");
-    return -1;
+  redis_query_t *rq = rn->queries;
+  while (rq != NULL) {
+    redis_query_t *next = rq->next;
+    sfree(rq);
+    rq = next;
   }
 
-  memcpy(rn_copy, rn, sizeof(*rn_copy));
-  rn_copy->next = NULL;
+  redisFree(rn->redisContext);
+  sfree(rn->name);
+  sfree(rn->host);
+  sfree(rn->passwd);
+  sfree(rn);
+} /* void redis_node_free */
 
+static int redis_node_add(redis_node_t *rn) /* {{{ */
+{
   DEBUG("redis plugin: Adding node \"%s\".", rn->name);
 
-  if (nodes_head == NULL)
-    nodes_head = rn_copy;
-  else {
-    rn_ptr = nodes_head;
-    while (rn_ptr->next != NULL)
-      rn_ptr = rn_ptr->next;
-    rn_ptr->next = rn_copy;
-  }
+  /* Disable automatic generation of default instance in the init callback. */
+  redis_have_instances = true;
 
-  return 0;
+  char cb_name[sizeof("redis/") + DATA_MAX_NAME_LEN];
+  snprintf(cb_name, sizeof(cb_name), "redis/%s", rn->name);
+
+  return plugin_register_complex_read(
+      /* group = */ "redis",
+      /* name      = */ cb_name,
+      /* callback  = */ redis_read,
+      /* interval  = */ 0,
+      &(user_data_t){
+          .data = rn, .free_func = redis_node_free,
+      });
 } /* }}} */
 
 static redis_query_t *redis_config_query(oconfig_item_t *ci) /* {{{ */
@@ -171,44 +177,63 @@ err:
 
 static int redis_config_node(oconfig_item_t *ci) /* {{{ */
 {
-  redis_query_t *rq;
-  int status;
-  int timeout;
+  redis_node_t *rn = calloc(1, sizeof(*rn));
+  if (rn == NULL) {
+    ERROR("redis plugin: calloc failed adding node.");
+    return ENOMEM;
+  }
 
-  redis_node_t rn = {.port = REDIS_DEF_PORT,
-                     .timeout.tv_usec = REDIS_DEF_TIMEOUT};
+  rn->port = REDIS_DEF_PORT;
+  rn->timeout.tv_sec = REDIS_DEF_TIMEOUT_SEC;
+  rn->report_cpu_usage = true;
 
-  sstrncpy(rn.host, REDIS_DEF_HOST, sizeof(rn.host));
+  rn->host = strdup(REDIS_DEF_HOST);
+  if (rn->host == NULL) {
+    ERROR("redis plugin: strdup failed adding node.");
+    sfree(rn);
+    return ENOMEM;
+  }
 
-  status = cf_util_get_string_buffer(ci, rn.name, sizeof(rn.name));
-  if (status != 0)
+  int status = cf_util_get_string(ci, &rn->name);
+  if (status != 0) {
+    sfree(rn->host);
+    sfree(rn);
     return status;
+  }
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
 
     if (strcasecmp("Host", option->key) == 0)
-      status = cf_util_get_string_buffer(option, rn.host, sizeof(rn.host));
+      status = cf_util_get_string(option, &rn->host);
     else if (strcasecmp("Port", option->key) == 0) {
       status = cf_util_get_port_number(option);
       if (status > 0) {
-        rn.port = status;
+        rn->port = status;
         status = 0;
       }
     } else if (strcasecmp("Query", option->key) == 0) {
-      rq = redis_config_query(option);
+      redis_query_t *rq = redis_config_query(option);
       if (rq == NULL) {
         status = 1;
       } else {
-        rq->next = rn.queries;
-        rn.queries = rq;
+        rq->next = rn->queries;
+        rn->queries = rq;
       }
     } else if (strcasecmp("Timeout", option->key) == 0) {
+      int timeout;
       status = cf_util_get_int(option, &timeout);
-      if (status == 0)
-        rn.timeout.tv_usec = timeout;
+      if (status == 0) {
+        rn->timeout.tv_usec = timeout * 1000;
+        rn->timeout.tv_sec = rn->timeout.tv_usec / 1000000L;
+        rn->timeout.tv_usec %= 1000000L;
+      }
     } else if (strcasecmp("Password", option->key) == 0)
-      status = cf_util_get_string_buffer(option, rn.passwd, sizeof(rn.passwd));
+      status = cf_util_get_string(option, &rn->passwd);
+    else if (strcasecmp("ReportCommandStats", option->key) == 0)
+      status = cf_util_get_boolean(option, &rn->report_command_stats);
+    else if (strcasecmp("ReportCpuUsage", option->key) == 0)
+      status = cf_util_get_boolean(option, &rn->report_cpu_usage);
     else
       WARNING("redis plugin: Option `%s' not allowed inside a `Node' "
               "block. I'll ignore this option.",
@@ -218,10 +243,12 @@ static int redis_config_node(oconfig_item_t *ci) /* {{{ */
       break;
   }
 
-  if (status != 0)
+  if (status != 0) {
+    redis_node_free(rn);
     return status;
+  }
 
-  return redis_node_add(&rn);
+  return redis_node_add(rn);
 } /* }}} int redis_config_node */
 
 static int redis_config(oconfig_item_t *ci) /* {{{ */
@@ -237,17 +264,12 @@ static int redis_config(oconfig_item_t *ci) /* {{{ */
               option->key);
   }
 
-  if (nodes_head == NULL) {
-    ERROR("redis plugin: No valid node configuration could be found.");
-    return ENOENT;
-  }
-
   return 0;
 } /* }}} */
 
 __attribute__((nonnull(2))) static void
-redis_submit(char *plugin_instance, const char *type, const char *type_instance,
-             value_t value) /* {{{ */
+redis_submit(const char *plugin_instance, const char *type,
+             const char *type_instance, value_t value) /* {{{ */
 {
   value_list_t vl = VALUE_LIST_INIT;
 
@@ -263,28 +285,78 @@ redis_submit(char *plugin_instance, const char *type, const char *type_instance,
   plugin_dispatch_values(&vl);
 } /* }}} */
 
+__attribute__((nonnull(2))) static void
+redis_submit2(const char *plugin_instance, const char *type,
+              const char *type_instance, value_t value0,
+              value_t value1) /* {{{ */
+{
+  value_list_t vl = VALUE_LIST_INIT;
+  value_t values[] = {value0, value1};
+
+  vl.values = values;
+  vl.values_len = STATIC_ARRAY_SIZE(values);
+
+  sstrncpy(vl.plugin, "redis", sizeof(vl.plugin));
+  sstrncpy(vl.type, type, sizeof(vl.type));
+
+  if (plugin_instance != NULL)
+    sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
+
+  if (type_instance != NULL)
+    sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+
+  plugin_dispatch_values(&vl);
+} /* }}} */
+
 static int redis_init(void) /* {{{ */
 {
-  redis_node_t rn = {.name = "default",
-                     .host = REDIS_DEF_HOST,
-                     .port = REDIS_DEF_PORT,
-                     .timeout.tv_sec = 0,
-                     .timeout.tv_usec = REDIS_DEF_TIMEOUT,
-                     .next = NULL};
+  if (redis_have_instances)
+    return 0;
 
-  if (nodes_head == NULL)
-    redis_node_add(&rn);
+  redis_node_t *rn = calloc(1, sizeof(*rn));
+  if (rn == NULL)
+    return ENOMEM;
 
-  return 0;
+  rn->port = REDIS_DEF_PORT;
+  rn->timeout.tv_sec = REDIS_DEF_TIMEOUT_SEC;
+
+  rn->name = strdup("default");
+  rn->host = strdup(REDIS_DEF_HOST);
+
+  if (rn->name == NULL || rn->host == NULL) {
+    sfree(rn->name);
+    sfree(rn->host);
+    sfree(rn);
+    return ENOMEM;
+  }
+
+  return redis_node_add(rn);
 } /* }}} int redis_init */
 
-static int redis_handle_info(char *node, char const *info_line,
-                             char const *type, char const *type_instance,
-                             char const *field_name, int ds_type) /* {{{ */
-{
+static void *c_redisCommand(redis_node_t *rn, const char *format, ...) {
+  redisContext *c = rn->redisContext;
+
+  if (c == NULL)
+    return NULL;
+
+  va_list ap;
+  va_start(ap, format);
+  void *reply = redisvCommand(c, format, ap);
+  va_end(ap);
+
+  if (reply == NULL) {
+    ERROR("redis plugin: Connection error: %s", c->errstr);
+    redisFree(rn->redisContext);
+    rn->redisContext = NULL;
+  }
+
+  return reply;
+} /* void c_redisCommand */
+
+static int redis_get_info_value(char const *info_line, char const *field_name,
+                                int ds_type, value_t *val) {
   char *str = strstr(info_line, field_name);
   static char buf[MAX_REDIS_VAL_SIZE];
-  value_t val;
   if (str) {
     int i;
 
@@ -294,20 +366,29 @@ static int redis_handle_info(char *node, char const *info_line,
       buf[i] = *str;
     buf[i] = '\0';
 
-    if (parse_value(buf, &val, ds_type) == -1) {
+    if (parse_value(buf, val, ds_type) == -1) {
       WARNING("redis plugin: Unable to parse field `%s'.", field_name);
       return -1;
     }
 
-    redis_submit(node, type, type_instance, val);
     return 0;
   }
   return -1;
+} /* int redis_get_info_value */
+
+static int redis_handle_info(char *node, char const *info_line,
+                             char const *type, char const *type_instance,
+                             char const *field_name, int ds_type) /* {{{ */
+{
+  value_t val;
+  if (redis_get_info_value(info_line, field_name, ds_type, &val) != 0)
+    return -1;
 
+  redis_submit(node, type, type_instance, val);
+  return 0;
 } /* }}} int redis_handle_info */
 
-static int redis_handle_query(redisContext *rh, redis_node_t *rn,
-                              redis_query_t *rq) /* {{{ */
+static int redis_handle_query(redis_node_t *rn, redis_query_t *rq) /* {{{ */
 {
   redisReply *rr;
   const data_set_t *ds;
@@ -315,22 +396,24 @@ static int redis_handle_query(redisContext *rh, redis_node_t *rn,
 
   ds = plugin_get_ds(rq->type);
   if (!ds) {
-    ERROR("redis plugin: DataSet `%s' not defined.", rq->type);
+    ERROR("redis plugin: DS type `%s' not defined.", rq->type);
     return -1;
   }
 
   if (ds->ds_num != 1) {
-    ERROR("redis plugin: DS `%s' has too many types.", rq->type);
+    ERROR("redis plugin: DS type `%s' has too many datasources. This is not "
+          "supported currently.",
+          rq->type);
     return -1;
   }
 
-  if ((rr = redisCommand(rh, "SELECT %d", rq->database)) == NULL) {
+  if ((rr = c_redisCommand(rn, "SELECT %d", rq->database)) == NULL) {
     WARNING("redis plugin: unable to switch to database `%d' on node `%s'.",
             rq->database, rn->name);
     return -1;
   }
 
-  if ((rr = redisCommand(rh, rq->query)) == NULL) {
+  if ((rr = c_redisCommand(rn, rq->query)) == NULL) {
     WARNING("redis plugin: unable to carry out query `%s'.", rq->query);
     return -1;
   }
@@ -354,13 +437,24 @@ static int redis_handle_query(redisContext *rh, redis_node_t *rn,
     break;
   case REDIS_REPLY_STRING:
     if (parse_value(rr->str, &val, ds->ds[0].type) == -1) {
-      WARNING("redis plugin: Unable to parse field `%s'.", rq->type);
+      WARNING("redis plugin: Query `%s': Unable to parse value.", rq->query);
       freeReplyObject(rr);
       return -1;
     }
     break;
+  case REDIS_REPLY_ERROR:
+    WARNING("redis plugin: Query `%s' failed: %s.", rq->query, rr->str);
+    freeReplyObject(rr);
+    return -1;
+  case REDIS_REPLY_ARRAY:
+    WARNING("redis plugin: Query `%s' should return string or integer. Arrays "
+            "are not supported.",
+            rq->query);
+    freeReplyObject(rr);
+    return -1;
   default:
-    WARNING("redis plugin: Cannot coerce redis type.");
+    WARNING("redis plugin: Query `%s': Cannot coerce redis type (%i).",
+            rq->query, rr->type);
     freeReplyObject(rr);
     return -1;
   }
@@ -371,7 +465,7 @@ static int redis_handle_query(redisContext *rh, redis_node_t *rn,
   return 0;
 } /* }}} int redis_handle_query */
 
-static int redis_db_stats(char *node, char const *info_line) /* {{{ */
+static int redis_db_stats(const char *node, char const *info_line) /* {{{ */
 {
   /* redis_db_stats parses and dispatches Redis database statistics,
    * currently the number of keys for each database.
@@ -410,91 +504,285 @@ static int redis_db_stats(char *node, char const *info_line) /* {{{ */
 
 } /* }}} int redis_db_stats */
 
-static int redis_read(void) /* {{{ */
-{
-  for (redis_node_t *rn = nodes_head; rn != NULL; rn = rn->next) {
-    redisContext *rh;
+static void redis_cpu_usage(const char *node, char const *info_line) {
+  while (42) {
+    value_t rusage_user;
+    value_t rusage_syst;
+
+    if (redis_get_info_value(info_line, "used_cpu_user", DS_TYPE_GAUGE,
+                             &rusage_user) != 0)
+      break;
+
+    if (redis_get_info_value(info_line, "used_cpu_sys", DS_TYPE_GAUGE,
+                             &rusage_syst) != 0)
+      break;
+
+    redis_submit2(node, "ps_cputime", "daemon",
+                  (value_t){.derive = rusage_user.gauge * 1000000},
+                  (value_t){.derive = rusage_syst.gauge * 1000000});
+    break;
+  }
+
+  while (42) {
+    value_t rusage_user;
+    value_t rusage_syst;
+
+    if (redis_get_info_value(info_line, "used_cpu_user_children", DS_TYPE_GAUGE,
+                             &rusage_user) != 0)
+      break;
+
+    if (redis_get_info_value(info_line, "used_cpu_sys_children", DS_TYPE_GAUGE,
+                             &rusage_syst) != 0)
+      break;
+
+    redis_submit2(node, "ps_cputime", "children",
+                  (value_t){.derive = rusage_user.gauge * 1000000},
+                  (value_t){.derive = rusage_syst.gauge * 1000000});
+    break;
+  }
+} /* void redis_cpu_usage */
+
+static gauge_t calculate_ratio_percent(derive_t part1, derive_t part2,
+                                       derive_t *prev1, derive_t *prev2) {
+  if ((*prev1 == 0) || (*prev2 == 0) || (part1 < *prev1) || (part2 < *prev2)) {
+    *prev1 = part1;
+    *prev2 = part2;
+    return NAN;
+  }
+
+  derive_t num = part1 - *prev1;
+  derive_t denom = part2 - *prev2 + num;
+
+  *prev1 = part1;
+  *prev2 = part2;
+
+  if (denom == 0)
+    return NAN;
+
+  if (num == 0)
+    return 0;
+
+  return 100.0 * (gauge_t)num / (gauge_t)denom;
+} /* gauge_t calculate_ratio_percent */
+
+static void redis_keyspace_usage(redis_node_t *rn, char const *info_line) {
+  value_t hits, misses;
+
+  if (redis_get_info_value(info_line, "keyspace_hits", DS_TYPE_DERIVE, &hits) !=
+      0)
+    return;
+
+  if (redis_get_info_value(info_line, "keyspace_misses", DS_TYPE_DERIVE,
+                           &misses) != 0)
+    return;
+
+  redis_submit(rn->name, "cache_result", "hits", hits);
+  redis_submit(rn->name, "cache_result", "misses", misses);
+
+  prev_t *prev = &rn->prev;
+  gauge_t ratio = calculate_ratio_percent(
+      hits.derive, misses.derive, &prev->keyspace_hits, &prev->keyspace_misses);
+  redis_submit(rn->name, "percent", "hitratio", (value_t){.gauge = ratio});
+
+} /* void redis_keyspace_usage */
+
+static void redis_check_connection(redis_node_t *rn) {
+  if (rn->redisContext)
+    return;
+
+  redisContext *rh = redisConnectWithTimeout(rn->host, rn->port, rn->timeout);
+
+  if (rh == NULL) {
+    ERROR("redis plugin: can't allocate redis context");
+    return;
+  }
+  if (rh->err) {
+    ERROR("redis plugin: unable to connect to node `%s' (%s:%d): %s.", rn->name,
+          rn->host, rn->port, rh->errstr);
+    redisFree(rh);
+    return;
+  }
+
+  rn->redisContext = rh;
+
+  if (rn->passwd) {
     redisReply *rr;
 
-    DEBUG("redis plugin: querying info from node `%s' (%s:%d).", rn->name,
-          rn->host, rn->port);
+    DEBUG("redis plugin: authenticating node `%s' passwd(%s).", rn->name,
+          rn->passwd);
 
-    rh = redisConnectWithTimeout((char *)rn->host, rn->port, rn->timeout);
-    if (rh == NULL) {
-      ERROR("redis plugin: unable to connect to node `%s' (%s:%d).", rn->name,
-            rn->host, rn->port);
-      continue;
+    if ((rr = c_redisCommand(rn, "AUTH %s", rn->passwd)) == NULL) {
+      WARNING("redis plugin: unable to authenticate on node `%s'.", rn->name);
+      return;
     }
 
-    if (strlen(rn->passwd) > 0) {
-      DEBUG("redis plugin: authenticating node `%s' passwd(%s).", rn->name,
-            rn->passwd);
+    if (rr->type != REDIS_REPLY_STATUS) {
+      WARNING("redis plugin: invalid authentication on node `%s'.", rn->name);
+      freeReplyObject(rr);
+      redisFree(rn->redisContext);
+      rn->redisContext = NULL;
+      return;
+    }
 
-      if ((rr = redisCommand(rh, "AUTH %s", rn->passwd)) == NULL) {
-        WARNING("redis plugin: unable to authenticate on node `%s'.", rn->name);
-        goto redis_fail;
-      }
+    freeReplyObject(rr);
+  }
+  return;
+} /* void redis_check_connection */
 
-      if (rr->type != REDIS_REPLY_STATUS) {
-        WARNING("redis plugin: invalid authentication on node `%s'.", rn->name);
-        goto redis_fail;
-      }
+static void redis_read_server_info(redis_node_t *rn) {
+  redisReply *rr;
 
-      freeReplyObject(rr);
+  if ((rr = c_redisCommand(rn, "INFO")) == NULL) {
+    WARNING("redis plugin: unable to get INFO from node `%s'.", rn->name);
+    return;
+  }
+
+  redis_handle_info(rn->name, rr->str, "uptime", NULL, "uptime_in_seconds",
+                    DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "current_connections", "clients",
+                    "connected_clients", DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "blocked_clients", NULL,
+                    "blocked_clients", DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "memory", NULL, "used_memory",
+                    DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "memory_lua", NULL, "used_memory_lua",
+                    DS_TYPE_GAUGE);
+  /* changes_since_last_save: Deprecated in redis version 2.6 and above */
+  redis_handle_info(rn->name, rr->str, "volatile_changes", NULL,
+                    "changes_since_last_save", DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "total_connections", NULL,
+                    "total_connections_received", DS_TYPE_DERIVE);
+  redis_handle_info(rn->name, rr->str, "total_operations", NULL,
+                    "total_commands_processed", DS_TYPE_DERIVE);
+  redis_handle_info(rn->name, rr->str, "expired_keys", NULL, "expired_keys",
+                    DS_TYPE_DERIVE);
+  redis_handle_info(rn->name, rr->str, "evicted_keys", NULL, "evicted_keys",
+                    DS_TYPE_DERIVE);
+  redis_handle_info(rn->name, rr->str, "pubsub", "channels", "pubsub_channels",
+                    DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "pubsub", "patterns", "pubsub_patterns",
+                    DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "current_connections", "slaves",
+                    "connected_slaves", DS_TYPE_GAUGE);
+  redis_handle_info(rn->name, rr->str, "total_bytes", "input",
+                    "total_net_input_bytes", DS_TYPE_DERIVE);
+  redis_handle_info(rn->name, rr->str, "total_bytes", "output",
+                    "total_net_output_bytes", DS_TYPE_DERIVE);
+
+  redis_keyspace_usage(rn, rr->str);
+
+  redis_db_stats(rn->name, rr->str);
+
+  if (rn->report_cpu_usage)
+    redis_cpu_usage(rn->name, rr->str);
+
+  freeReplyObject(rr);
+} /* void redis_read_server_info */
+
+static void redis_read_command_stats(redis_node_t *rn) {
+  redisReply *rr;
+
+  if ((rr = c_redisCommand(rn, "INFO commandstats")) == NULL) {
+    WARNING("redis plugin: node `%s': unable to get `INFO commandstats'.",
+            rn->name);
+    return;
+  }
+
+  if (rr->type != REDIS_REPLY_STRING) {
+    WARNING("redis plugin: node `%s' `INFO commandstats' returned unsupported "
+            "redis type %i.",
+            rn->name, rr->type);
+    freeReplyObject(rr);
+    return;
+  }
+
+  char *command;
+  char *line;
+  char *ptr = rr->str;
+  char *saveptr = NULL;
+  while ((line = strtok_r(ptr, "\n\r", &saveptr)) != NULL) {
+    ptr = NULL;
+
+    if (line[0] == '#')
+      continue;
+
+    /* command name */
+    if (strstr(line, "cmdstat_") != line) {
+      ERROR("redis plugin: not found 'cmdstat_' prefix in line '%s'", line);
+      continue;
     }
 
-    if ((rr = redisCommand(rh, "INFO")) == NULL) {
-      WARNING("redis plugin: unable to get info from node `%s'.", rn->name);
-      goto redis_fail;
+    char *values = strstr(line, ":");
+    if (values == NULL) {
+      ERROR("redis plugin: not found ':' separator in line '%s'", line);
+      continue;
     }
 
-    redis_handle_info(rn->name, rr->str, "uptime", NULL, "uptime_in_seconds",
-                      DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "current_connections", "clients",
-                      "connected_clients", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "blocked_clients", NULL,
-                      "blocked_clients", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "memory", NULL, "used_memory",
-                      DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "memory_lua", NULL, "used_memory_lua",
-                      DS_TYPE_GAUGE);
-    /* changes_since_last_save: Deprecated in redis version 2.6 and above */
-    redis_handle_info(rn->name, rr->str, "volatile_changes", NULL,
-                      "changes_since_last_save", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "total_connections", NULL,
-                      "total_connections_received", DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "total_operations", NULL,
-                      "total_commands_processed", DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "operations_per_second", NULL,
-                      "instantaneous_ops_per_sec", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "expired_keys", NULL, "expired_keys",
-                      DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "evicted_keys", NULL, "evicted_keys",
-                      DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "pubsub", "channels",
-                      "pubsub_channels", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "pubsub", "patterns",
-                      "pubsub_patterns", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "current_connections", "slaves",
-                      "connected_slaves", DS_TYPE_GAUGE);
-    redis_handle_info(rn->name, rr->str, "cache_result", "hits",
-                      "keyspace_hits", DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "cache_result", "misses",
-                      "keyspace_misses", DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "total_bytes", "input",
-                      "total_net_input_bytes", DS_TYPE_DERIVE);
-    redis_handle_info(rn->name, rr->str, "total_bytes", "output",
-                      "total_net_output_bytes", DS_TYPE_DERIVE);
-
-    redis_db_stats(rn->name, rr->str);
-
-    for (redis_query_t *rq = rn->queries; rq != NULL; rq = rq->next)
-      redis_handle_query(rh, rn, rq);
-
-  redis_fail:
-    if (rr != NULL)
-      freeReplyObject(rr);
-    redisFree(rh);
+    /* Null-terminate command token */
+    values[0] = '\0';
+    command = line + strlen("cmdstat_");
+    values++;
+
+    /* parse values */
+    /* cmdstat_publish:calls=20795774,usec=111039258,usec_per_call=5.34 */
+    char *field;
+    char *saveptr_field = NULL;
+    while ((field = strtok_r(values, "=", &saveptr_field)) != NULL) {
+      values = NULL;
+
+      const char *type;
+      /* only these are supported */
+      if (strcmp(field, "calls") == 0)
+        type = "commands";
+      else if (strcmp(field, "usec") == 0)
+        type = "redis_command_cputime";
+      else
+        continue;
+
+      if ((field = strtok_r(NULL, ",", &saveptr_field)) == NULL)
+        continue;
+
+      char *endptr = NULL;
+      errno = 0;
+      derive_t value = strtoll(field, &endptr, 0);
+
+      if ((endptr == field) || (errno != 0))
+        continue;
+
+      redis_submit(rn->name, type, command, (value_t){.derive = value});
+    }
+  }
+  freeReplyObject(rr);
+} /* void redis_read_command_stats */
+
+static int redis_read(user_data_t *user_data) /* {{{ */
+{
+  redis_node_t *rn = user_data->data;
+
+  DEBUG("redis plugin: querying info from node `%s' (%s:%d).", rn->name,
+        rn->host, rn->port);
+
+  redis_check_connection(rn);
+
+  if (!rn->redisContext) /* no connection */
+    return -1;
+
+  redis_read_server_info(rn);
+
+  if (!rn->redisContext) /* connection lost */
+    return -1;
+
+  if (rn->report_command_stats) {
+    redis_read_command_stats(rn);
+
+    if (!rn->redisContext) /* connection lost */
+      return -1;
+  }
+
+  for (redis_query_t *rq = rn->queries; rq != NULL; rq = rq->next) {
+    redis_handle_query(rn, rq);
+    if (!rn->redisContext) /* connection lost */
+      return -1;
   }
 
   return 0;
@@ -505,8 +793,5 @@ void module_register(void) /* {{{ */
 {
   plugin_register_complex_config("redis", redis_config);
   plugin_register_init("redis", redis_init);
-  plugin_register_read("redis", redis_read);
-  /* TODO: plugin_register_write: one redis list per value id with
-   * X elements */
 }
 /* }}} */
index 5c87a43..f6290d7 100644 (file)
@@ -624,6 +624,7 @@ static int rrd_cache_insert(const char *filename, const char *value,
   if ((status != 0) || (rc == NULL)) {
     rc = malloc(sizeof(*rc));
     if (rc == NULL) {
+      ERROR("rrdtool plugin: malloc failed: %s", STRERRNO);
       pthread_mutex_unlock(&cache_lock);
       return -1;
     }
@@ -790,17 +791,22 @@ static int rrd_write(const data_set_t *ds, const value_list_t *vl,
   }
 
   char filename[PATH_MAX];
-  if (value_list_to_filename(filename, sizeof(filename), vl) != 0)
+  if (value_list_to_filename(filename, sizeof(filename), vl) != 0) {
+    ERROR("rrdtool plugin: failed to build filename");
     return -1;
+  }
 
   char values[32 * (ds->ds_num + 1)];
-  if (value_list_to_string(values, sizeof(values), ds, vl) != 0)
+  if (value_list_to_string(values, sizeof(values), ds, vl) != 0) {
+    ERROR("rrdtool plugin: failed to build values string");
     return -1;
+  }
 
   struct stat statbuf = {0};
   if (stat(filename, &statbuf) == -1) {
     if (errno == ENOENT) {
       if (cu_rrd_create_file(filename, ds, vl, &rrdcreate_config) != 0) {
+        ERROR("rrdtool plugin: cu_rrd_create_file (%s) failed.", filename);
         return -1;
       } else if (rrdcreate_config.async) {
         return 0;
index 7c3ebc8..a86d085 100644 (file)
@@ -29,6 +29,7 @@
 #include "common.h"
 #include "plugin.h"
 #include "utils_complain.h"
+#include "utils_ignorelist.h"
 
 #include <net-snmp/net-snmp-config.h>
 #include <net-snmp/net-snmp-includes.h>
@@ -44,18 +45,24 @@ struct oid_s {
 };
 typedef struct oid_s oid_t;
 
-union instance_u {
-  char string[DATA_MAX_NAME_LEN];
+struct instance_s {
+  bool configured;
   oid_t oid;
+  char *prefix;
+  char *value;
 };
-typedef union instance_u instance_t;
+typedef struct instance_s instance_t;
 
 struct data_definition_s {
   char *name; /* used to reference this from the `Collect' option */
   char *type; /* used to find the data_set */
   bool is_table;
-  instance_t instance;
-  char *instance_prefix;
+  instance_t type_instance;
+  instance_t plugin_instance;
+  instance_t host;
+  oid_t filter_oid;
+  ignorelist_t *ignorelist;
+  char *plugin_name;
   oid_t *values;
   size_t values_len;
   double scale;
@@ -90,7 +97,6 @@ struct host_definition_s {
 
   void *sess_handle;
   c_complain_t complaint;
-  cdtime_t interval;
   data_definition_t **data_list;
   int data_list_len;
 };
@@ -98,19 +104,28 @@ typedef struct host_definition_s host_definition_t;
 
 /* These two types are used to cache values in `csnmp_read_table' to handle
  * gaps in tables. */
-struct csnmp_list_instances_s {
+struct csnmp_cell_char_s {
   oid_t suffix;
-  char instance[DATA_MAX_NAME_LEN];
-  struct csnmp_list_instances_s *next;
+  char value[DATA_MAX_NAME_LEN];
+  struct csnmp_cell_char_s *next;
 };
-typedef struct csnmp_list_instances_s csnmp_list_instances_t;
+typedef struct csnmp_cell_char_s csnmp_cell_char_t;
 
-struct csnmp_table_values_s {
+struct csnmp_cell_value_s {
   oid_t suffix;
   value_t value;
-  struct csnmp_table_values_s *next;
+  struct csnmp_cell_value_s *next;
 };
-typedef struct csnmp_table_values_s csnmp_table_values_t;
+typedef struct csnmp_cell_value_s csnmp_cell_value_t;
+
+typedef enum {
+  OID_TYPE_SKIP = 0,
+  OID_TYPE_VARIABLE,
+  OID_TYPE_TYPEINSTANCE,
+  OID_TYPE_PLUGININSTANCE,
+  OID_TYPE_HOST,
+  OID_TYPE_FILTER,
+} csnmp_oid_type_t;
 
 /*
  * Private variables
@@ -199,6 +214,22 @@ static void csnmp_host_definition_destroy(void *arg) /* {{{ */
   sfree(hd);
 } /* }}} void csnmp_host_definition_destroy */
 
+static void csnmp_data_definition_destroy(data_definition_t *dd) {
+  sfree(dd->name);
+  sfree(dd->type);
+  sfree(dd->plugin_name);
+  sfree(dd->plugin_instance.prefix);
+  sfree(dd->plugin_instance.value);
+  sfree(dd->type_instance.prefix);
+  sfree(dd->type_instance.value);
+  sfree(dd->host.prefix);
+  sfree(dd->host.value);
+  sfree(dd->values);
+  sfree(dd->ignores);
+  ignorelist_free(dd->ignorelist);
+  sfree(dd);
+} /* void csnmp_data_definition_destroy */
+
 /* Many functions to handle the configuration. {{{ */
 /* First there are many functions which do configuration stuff. It's a big
  * bloated and messy, I'm afraid. */
@@ -208,8 +239,7 @@ static void csnmp_host_definition_destroy(void *arg) /* {{{ */
  *  csnmp_config
  *  +-> call_snmp_init_once
  *  +-> csnmp_config_add_data
- *  !   +-> csnmp_config_add_data_instance
- *  !   +-> csnmp_config_add_data_instance_prefix
+ *  !   +-> csnmp_config_configure_data_instance
  *  !   +-> csnmp_config_add_data_values
  *  +-> csnmp_config_add_host
  *      +-> csnmp_config_add_host_version
@@ -226,45 +256,29 @@ static void call_snmp_init_once(void) {
   have_init = 1;
 } /* void call_snmp_init_once */
 
-static int csnmp_config_add_data_instance(data_definition_t *dd,
-                                          oconfig_item_t *ci) {
+static int csnmp_config_configure_data_instance(instance_t *instance,
+                                                oconfig_item_t *ci) {
   char buffer[DATA_MAX_NAME_LEN];
-  int status;
 
-  status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
+  int status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
   if (status != 0)
     return status;
 
-  if (dd->is_table) {
-    /* Instance is an OID */
-    dd->instance.oid.oid_len = MAX_OID_LEN;
+  instance->configured = true;
 
-    if (!read_objid(buffer, dd->instance.oid.oid, &dd->instance.oid.oid_len)) {
-      ERROR("snmp plugin: read_objid (%s) failed.", buffer);
-      return -1;
-    }
-  } else {
-    /* Instance is a simple string */
-    sstrncpy(dd->instance.string, buffer, sizeof(dd->instance.string));
+  if (strlen(buffer) == 0) {
+    return 0;
   }
 
-  return 0;
-} /* int csnmp_config_add_data_instance */
+  instance->oid.oid_len = MAX_OID_LEN;
 
-static int csnmp_config_add_data_instance_prefix(data_definition_t *dd,
-                                                 oconfig_item_t *ci) {
-  int status;
-
-  if (!dd->is_table) {
-    WARNING("snmp plugin: data %s: InstancePrefix is ignored when `Table' "
-            "is set to `false'.",
-            dd->name);
+  if (!read_objid(buffer, instance->oid.oid, &instance->oid.oid_len)) {
+    ERROR("snmp plugin: read_objid (%s) failed.", buffer);
     return -1;
   }
 
-  status = cf_util_get_string(ci, &dd->instance_prefix);
-  return status;
-} /* int csnmp_config_add_data_instance_prefix */
+  return 0;
+} /* int csnmp_config_configure_data_instance */
 
 static int csnmp_config_add_data_values(data_definition_t *dd,
                                         oconfig_item_t *ci) {
@@ -301,7 +315,7 @@ static int csnmp_config_add_data_values(data_definition_t *dd,
   }
 
   return 0;
-} /* int csnmp_config_add_data_instance */
+} /* int csnmp_config_configure_data_instance */
 
 static int csnmp_config_add_data_blacklist(data_definition_t *dd,
                                            oconfig_item_t *ci) {
@@ -315,9 +329,6 @@ static int csnmp_config_add_data_blacklist(data_definition_t *dd,
     }
   }
 
-  dd->ignores_len = 0;
-  dd->ignores = NULL;
-
   for (int i = 0; i < ci->values_num; ++i) {
     if (strarray_add(&(dd->ignores), &(dd->ignores_len),
                      ci->values[i].value.string) != 0) {
@@ -329,6 +340,41 @@ static int csnmp_config_add_data_blacklist(data_definition_t *dd,
   return 0;
 } /* int csnmp_config_add_data_blacklist */
 
+static int csnmp_config_add_data_filter_values(data_definition_t *data,
+                                               oconfig_item_t *ci) {
+  if (ci->values_num < 1) {
+    WARNING("snmp plugin: `FilterValues' needs at least one argument.");
+    return -1;
+  }
+
+  for (int i = 0; i < ci->values_num; i++) {
+    if (ci->values[i].type != OCONFIG_TYPE_STRING) {
+      WARNING("snmp plugin: All arguments to `FilterValues' must be strings.");
+      return -1;
+    }
+    ignorelist_add(data->ignorelist, ci->values[i].value.string);
+  }
+
+  return 0;
+} /* int csnmp_config_add_data_filter_values */
+
+static int csnmp_config_add_data_filter_oid(data_definition_t *data,
+                                            oconfig_item_t *ci) {
+
+  char buffer[DATA_MAX_NAME_LEN];
+  int status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer));
+  if (status != 0)
+    return status;
+
+  data->filter_oid.oid_len = MAX_OID_LEN;
+
+  if (!read_objid(buffer, data->filter_oid.oid, &data->filter_oid.oid_len)) {
+    ERROR("snmp plugin: read_objid (%s) failed.", buffer);
+    return -1;
+  }
+  return 0;
+} /* int csnmp_config_add_data_filter_oid */
+
 static int csnmp_config_add_data(oconfig_item_t *ci) {
   data_definition_t *dd = calloc(1, sizeof(*dd));
   if (dd == NULL)
@@ -342,6 +388,22 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
 
   dd->scale = 1.0;
   dd->shift = 0.0;
+  dd->ignores_len = 0;
+  dd->ignores = NULL;
+
+  dd->ignorelist = ignorelist_create(/* invert = */ 1);
+  if (dd->ignorelist == NULL) {
+    sfree(dd->name);
+    sfree(dd);
+    ERROR("snmp plugin: ignorelist_create() failed.");
+    return ENOMEM;
+  }
+
+  dd->plugin_name = strdup("snmp");
+  if (dd->plugin_name == NULL) {
+    ERROR("snmp plugin: Can't allocate memory");
+    return ENOMEM;
+  }
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
@@ -350,10 +412,47 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
       status = cf_util_get_string(option, &dd->type);
     else if (strcasecmp("Table", option->key) == 0)
       status = cf_util_get_boolean(option, &dd->is_table);
-    else if (strcasecmp("Instance", option->key) == 0)
-      status = csnmp_config_add_data_instance(dd, option);
-    else if (strcasecmp("InstancePrefix", option->key) == 0)
-      status = csnmp_config_add_data_instance_prefix(dd, option);
+    else if (strcasecmp("Plugin", option->key) == 0)
+      status = cf_util_get_string(option, &dd->plugin_name);
+    else if (strcasecmp("Instance", option->key) == 0) {
+      if (dd->is_table) {
+        /* Instance is OID */
+        WARNING(
+            "snmp plugin: data %s: Option `Instance' is deprecated, please use "
+            "option `TypeInstanceOID'.",
+            dd->name);
+        status =
+            csnmp_config_configure_data_instance(&dd->type_instance, option);
+      } else {
+        /* Instance is a simple string */
+        WARNING(
+            "snmp plugin: data %s: Option `Instance' is deprecated, please use "
+            "option `TypeInstance'.",
+            dd->name);
+        status = cf_util_get_string(option, &dd->type_instance.value);
+      }
+    } else if (strcasecmp("InstancePrefix", option->key) == 0) {
+      WARNING("snmp plugin: data %s: Option `InstancePrefix' is deprecated, "
+              "please use option `TypeInstancePrefix'.",
+              dd->name);
+      status = cf_util_get_string(option, &dd->type_instance.prefix);
+    } else if (strcasecmp("PluginInstance", option->key) == 0)
+      status = cf_util_get_string(option, &dd->plugin_instance.value);
+    else if (strcasecmp("TypeInstance", option->key) == 0)
+      status = cf_util_get_string(option, &dd->type_instance.value);
+    else if (strcasecmp("PluginInstanceOID", option->key) == 0)
+      status =
+          csnmp_config_configure_data_instance(&dd->plugin_instance, option);
+    else if (strcasecmp("PluginInstancePrefix", option->key) == 0)
+      status = cf_util_get_string(option, &dd->plugin_instance.prefix);
+    else if (strcasecmp("TypeInstanceOID", option->key) == 0)
+      status = csnmp_config_configure_data_instance(&dd->type_instance, option);
+    else if (strcasecmp("TypeInstancePrefix", option->key) == 0)
+      status = cf_util_get_string(option, &dd->type_instance.prefix);
+    else if (strcasecmp("HostOID", option->key) == 0)
+      status = csnmp_config_configure_data_instance(&dd->host, option);
+    else if (strcasecmp("HostPrefix", option->key) == 0)
+      status = cf_util_get_string(option, &dd->host.prefix);
     else if (strcasecmp("Values", option->key) == 0)
       status = csnmp_config_add_data_values(dd, option);
     else if (strcasecmp("Shift", option->key) == 0)
@@ -364,8 +463,18 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
       status = csnmp_config_add_data_blacklist(dd, option);
     else if (strcasecmp("InvertMatch", option->key) == 0)
       status = cf_util_get_boolean(option, &dd->invert_match);
-    else {
-      WARNING("snmp plugin: Option `%s' not allowed here.", option->key);
+    else if (strcasecmp("FilterOID", option->key) == 0) {
+      status = csnmp_config_add_data_filter_oid(dd, option);
+    } else if (strcasecmp("FilterValues", option->key) == 0) {
+      status = csnmp_config_add_data_filter_values(dd, option);
+    } else if (strcasecmp("FilterIgnoreSelected", option->key) == 0) {
+      bool t;
+      status = cf_util_get_boolean(option, &t);
+      if (status == 0)
+        ignorelist_set_invert(dd->ignorelist, /* invert = */ !t);
+    } else {
+      WARNING("snmp plugin: data %s: Option `%s' not allowed here.", dd->name,
+              option->key);
       status = -1;
     }
 
@@ -374,6 +483,65 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
   } /* for (ci->children) */
 
   while (status == 0) {
+    if (dd->is_table) {
+      /* Set type_instance to SUBID by default */
+      if (!dd->plugin_instance.configured && !dd->host.configured)
+        dd->type_instance.configured = true;
+
+      if (dd->plugin_instance.value && dd->plugin_instance.configured) {
+        WARNING(
+            "snmp plugin: data %s: Option `PluginInstance' will be ignored.",
+            dd->name);
+      }
+      if (dd->type_instance.value && dd->type_instance.configured) {
+        WARNING("snmp plugin: data %s: Option `TypeInstance' will be ignored.",
+                dd->name);
+      }
+      if (dd->type_instance.prefix && !dd->type_instance.configured) {
+        WARNING("snmp plugin: data %s: Option `TypeInstancePrefix' will be "
+                "ignored.",
+                dd->name);
+      }
+      if (dd->plugin_instance.prefix && !dd->plugin_instance.configured) {
+        WARNING("snmp plugin: data %s: Option `PluginInstancePrefix' will be "
+                "ignored.",
+                dd->name);
+      }
+      if (dd->host.prefix && !dd->host.configured) {
+        WARNING("snmp plugin: data %s: Option `HostPrefix' will be ignored.",
+                dd->name);
+      }
+    } else {
+      if (dd->plugin_instance.oid.oid_len > 0) {
+        WARNING("snmp plugin: data %s: Option `PluginInstanceOID' will be "
+                "ignored.",
+                dd->name);
+      }
+      if (dd->type_instance.oid.oid_len > 0) {
+        WARNING(
+            "snmp plugin: data %s: Option `TypeInstanceOID' will be ignored.",
+            dd->name);
+      }
+      if (dd->type_instance.prefix) {
+        WARNING("snmp plugin: data %s: Option `TypeInstancePrefix' is ignored "
+                "when `Table' "
+                "set to `false'.",
+                dd->name);
+      }
+      if (dd->plugin_instance.prefix) {
+        WARNING("snmp plugin: data %s: Option `PluginInstancePrefix' is "
+                "ignored when "
+                "`Table' set to `false'.",
+                dd->name);
+      }
+      if (dd->host.prefix) {
+        WARNING(
+            "snmp plugin: data %s: Option `HostPrefix' is ignored when `Table' "
+            "set to `false'.",
+            dd->name);
+      }
+    }
+
     if (dd->type == NULL) {
       WARNING("snmp plugin: `Type' not given for data `%s'", dd->name);
       status = -1;
@@ -389,18 +557,26 @@ static int csnmp_config_add_data(oconfig_item_t *ci) {
   } /* while (status == 0) */
 
   if (status != 0) {
-    sfree(dd->name);
-    sfree(dd->instance_prefix);
-    sfree(dd->values);
-    sfree(dd->ignores);
-    sfree(dd);
+    csnmp_data_definition_destroy(dd);
     return -1;
   }
 
   DEBUG("snmp plugin: dd = { name = %s, type = %s, is_table = %s, values_len = "
-        "%" PRIsz " }",
+        "%" PRIsz ",",
         dd->name, dd->type, (dd->is_table) ? "true" : "false", dd->values_len);
 
+  DEBUG("snmp plugin:        plugin_instance = %s, type_instance = %s,",
+        dd->plugin_instance.value, dd->type_instance.value);
+
+  DEBUG("snmp plugin:        type_instance_by_oid = %s, plugin_instance_by_oid "
+        "= %s }",
+        (dd->type_instance.oid.oid_len > 0)
+            ? "true"
+            : ((dd->type_instance.configured) ? "SUBID" : "false"),
+        (dd->plugin_instance.oid.oid_len > 0)
+            ? "true"
+            : ((dd->plugin_instance.configured) ? "SUBID" : "false"));
+
   if (data_head == NULL)
     data_head = dd;
   else {
@@ -433,7 +609,7 @@ static int csnmp_config_add_host_version(host_definition_t *hd,
   hd->version = version;
 
   return 0;
-} /* int csnmp_config_add_host_address */
+} /* int csnmp_config_add_host_version */
 
 static int csnmp_config_add_host_collect(host_definition_t *host,
                                          oconfig_item_t *ci) {
@@ -566,6 +742,7 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
   int status = 0;
 
   /* Registration stuff. */
+  cdtime_t interval = 0;
   char cb_name[DATA_MAX_NAME_LEN];
 
   hd = calloc(1, sizeof(*hd));
@@ -581,7 +758,6 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
   }
 
   hd->sess_handle = NULL;
-  hd->interval = 0;
 
   /* These mean that we have not set a timeout or retry value */
   hd->timeout = 0;
@@ -603,7 +779,7 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
     else if (strcasecmp("Collect", option->key) == 0)
       status = csnmp_config_add_host_collect(hd, option);
     else if (strcasecmp("Interval", option->key) == 0)
-      status = cf_util_get_cdtime(option, &hd->interval);
+      status = cf_util_get_cdtime(option, &interval);
     else if (strcasecmp("Username", option->key) == 0)
       status = cf_util_get_string(option, &hd->username);
     else if (strcasecmp("AuthProtocol", option->key) == 0)
@@ -698,7 +874,7 @@ static int csnmp_config_add_host(oconfig_item_t *ci) {
   snprintf(cb_name, sizeof(cb_name), "snmp-%s", hd->name);
 
   status = plugin_register_complex_read(
-      /* group = */ NULL, cb_name, csnmp_read_host, hd->interval,
+      /* group = */ NULL, cb_name, csnmp_read_host, interval,
       &(user_data_t){
           .data = hd, .free_func = csnmp_host_definition_destroy,
       });
@@ -821,8 +997,8 @@ static void csnmp_host_open_session(host_definition_t *host) {
 
 /* TODO: Check if negative values wrap around. Problem: negative temperatures.
  */
-static value_t csnmp_value_list_to_value(struct variable_list *vl, int type,
-                                         double scale, double shift,
+static value_t csnmp_value_list_to_value(const struct variable_list *vl,
+                                         int type, double scale, double shift,
                                          const char *host_name,
                                          const char *data_name) {
   value_t ret;
@@ -1025,96 +1201,99 @@ static int csnmp_strvbcopy(char *dst, /* {{{ */
   return 0;
 } /* }}} int csnmp_strvbcopy */
 
-static int csnmp_instance_list_add(csnmp_list_instances_t **head,
-                                   csnmp_list_instances_t **tail,
-                                   const struct snmp_pdu *res,
-                                   const host_definition_t *hd,
-                                   const data_definition_t *dd) {
-  csnmp_list_instances_t *il;
-  struct variable_list *vb;
-  oid_t vb_name;
-  int status;
+static csnmp_cell_char_t *csnmp_get_char_cell(const struct variable_list *vb,
+                                              const oid_t *root_oid,
+                                              const host_definition_t *hd,
+                                              const data_definition_t *dd) {
 
-  /* Set vb on the last variable */
-  for (vb = res->variables; (vb != NULL) && (vb->next_variable != NULL);
-       vb = vb->next_variable)
-    /* do nothing */;
   if (vb == NULL)
-    return -1;
-
-  csnmp_oid_init(&vb_name, vb->name, vb->name_length);
+    return NULL;
 
-  il = calloc(1, sizeof(*il));
+  csnmp_cell_char_t *il = calloc(1, sizeof(*il));
   if (il == NULL) {
     ERROR("snmp plugin: calloc failed.");
-    return -1;
+    return NULL;
   }
   il->next = NULL;
 
-  status = csnmp_oid_suffix(&il->suffix, &vb_name, &dd->instance.oid);
-  if (status != 0) {
+  oid_t vb_name;
+  csnmp_oid_init(&vb_name, vb->name, vb->name_length);
+
+  if (csnmp_oid_suffix(&il->suffix, &vb_name, root_oid) != 0) {
     sfree(il);
-    return status;
+    return NULL;
   }
 
-  /* Get instance name */
+  /* Get value */
   if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR) ||
       (vb->type == ASN_IPADDRESS)) {
-    char *ptr;
-
-    csnmp_strvbcopy(il->instance, vb, sizeof(il->instance));
-    bool is_matched = 0;
-    for (uint32_t i = 0; i < dd->ignores_len; i++) {
-      status = fnmatch(dd->ignores[i], il->instance, 0);
-      if (status == 0) {
-        if (!dd->invert_match) {
-          sfree(il);
-          return 0;
-        } else {
-          is_matched = 1;
-          break;
-        }
-      }
-    }
-    if (dd->invert_match && !is_matched) {
-      sfree(il);
-      return 0;
-    }
-    for (ptr = il->instance; *ptr != '\0'; ptr++) {
-      if ((*ptr > 0) && (*ptr < 32))
-        *ptr = ' ';
-      else if (*ptr == '/')
-        *ptr = '_';
-    }
-    DEBUG("snmp plugin: il->instance = `%s';", il->instance);
+
+    csnmp_strvbcopy(il->value, vb, sizeof(il->value));
+
   } else {
     value_t val = csnmp_value_list_to_value(
         vb, DS_TYPE_COUNTER,
         /* scale = */ 1.0, /* shift = */ 0.0, hd->name, dd->name);
-    snprintf(il->instance, sizeof(il->instance), "%" PRIu64,
-             (uint64_t)val.counter);
+    snprintf(il->value, sizeof(il->value), "%" PRIu64, (uint64_t)val.counter);
   }
 
-  /* TODO: Debugging output */
+  return il;
+} /* csnmp_cell_char_t csnmp_get_char_cell */
 
+static void csnmp_cells_append(csnmp_cell_char_t **head,
+                               csnmp_cell_char_t **tail,
+                               csnmp_cell_char_t *il) {
   if (*head == NULL)
     *head = il;
   else
     (*tail)->next = il;
   *tail = il;
-
+} /* void csnmp_cells_append */
+
+static bool csnmp_ignore_instance(csnmp_cell_char_t *cell,
+                                  const data_definition_t *dd) {
+  bool is_matched = 0;
+  for (uint32_t i = 0; i < dd->ignores_len; i++) {
+    int status = fnmatch(dd->ignores[i], cell->value, 0);
+    if (status == 0) {
+      if (!dd->invert_match) {
+        return 1;
+      } else {
+        is_matched = 1;
+        break;
+      }
+    }
+  }
+  if (dd->invert_match && !is_matched) {
+    return 1;
+  }
   return 0;
-} /* int csnmp_instance_list_add */
+} /* bool csnmp_ignore_instance */
+
+static void csnmp_cell_replace_reserved_chars(csnmp_cell_char_t *cell) {
+  for (char *ptr = cell->value; *ptr != '\0'; ptr++) {
+    if ((*ptr > 0) && (*ptr < 32))
+      *ptr = ' ';
+    else if (*ptr == '/')
+      *ptr = '_';
+  }
+} /* void csnmp_cell_replace_reserved_chars */
 
 static int csnmp_dispatch_table(host_definition_t *host,
                                 data_definition_t *data,
-                                csnmp_list_instances_t *instance_list,
-                                csnmp_table_values_t **value_table) {
+                                csnmp_cell_char_t *type_instance_cells,
+                                csnmp_cell_char_t *plugin_instance_cells,
+                                csnmp_cell_char_t *hostname_cells,
+                                csnmp_cell_char_t *filter_cells,
+                                csnmp_cell_value_t **value_cells) {
   const data_set_t *ds;
   value_list_t vl = VALUE_LIST_INIT;
 
-  csnmp_list_instances_t *instance_list_ptr;
-  csnmp_table_values_t *value_table_ptr[data->values_len];
+  csnmp_cell_char_t *type_instance_cell_ptr = type_instance_cells;
+  csnmp_cell_char_t *plugin_instance_cell_ptr = plugin_instance_cells;
+  csnmp_cell_char_t *hostname_cell_ptr = hostname_cells;
+  csnmp_cell_char_t *filter_cell_ptr = filter_cells;
+  csnmp_cell_value_t *value_cell_ptr[data->values_len];
 
   size_t i;
   bool have_more;
@@ -1128,32 +1307,28 @@ static int csnmp_dispatch_table(host_definition_t *host,
   assert(ds->ds_num == data->values_len);
   assert(data->values_len > 0);
 
-  instance_list_ptr = instance_list;
-
   for (i = 0; i < data->values_len; i++)
-    value_table_ptr[i] = value_table[i];
-
-  sstrncpy(vl.host, host->name, sizeof(vl.host));
-  sstrncpy(vl.plugin, "snmp", sizeof(vl.plugin));
+    value_cell_ptr[i] = value_cells[i];
 
-  vl.interval = host->interval;
+  sstrncpy(vl.plugin, data->plugin_name, sizeof(vl.plugin));
+  sstrncpy(vl.type, data->type, sizeof(vl.type));
 
   have_more = 1;
   while (have_more) {
     bool suffix_skipped = 0;
 
     /* Determine next suffix to handle. */
-    if (instance_list != NULL) {
-      if (instance_list_ptr == NULL) {
+    if (type_instance_cells != NULL) {
+      if (type_instance_cell_ptr == NULL) {
         have_more = 0;
         continue;
       }
 
-      memcpy(&current_suffix, &instance_list_ptr->suffix,
+      memcpy(&current_suffix, &type_instance_cell_ptr->suffix,
              sizeof(current_suffix));
     } else {
       /* no instance configured */
-      csnmp_table_values_t *ptr = value_table_ptr[0];
+      csnmp_cell_value_t *ptr = value_cell_ptr[0];
       if (ptr == NULL) {
         have_more = 0;
         continue;
@@ -1162,18 +1337,82 @@ static int csnmp_dispatch_table(host_definition_t *host,
       memcpy(&current_suffix, &ptr->suffix, sizeof(current_suffix));
     }
 
-    /* Update all the value_table_ptr to point at the entry with the same
+    /*
+    char oid_buffer[1024] = {0};
+    snprint_objid(oid_buffer, sizeof(oid_buffer) - 1, current_suffix.oid,
+                          current_suffix.oid_len);
+    DEBUG("SNMP PLUGIN: SUFFIX %s", oid_buffer);
+    */
+
+    /* Update plugin_instance_cell_ptr to point expected suffix */
+    if (plugin_instance_cells != NULL) {
+      while ((plugin_instance_cell_ptr != NULL) &&
+             (csnmp_oid_compare(&plugin_instance_cell_ptr->suffix,
+                                &current_suffix) < 0))
+        plugin_instance_cell_ptr = plugin_instance_cell_ptr->next;
+
+      if (plugin_instance_cell_ptr == NULL) {
+        have_more = 0;
+        continue;
+      }
+
+      if (csnmp_oid_compare(&plugin_instance_cell_ptr->suffix,
+                            &current_suffix) > 0) {
+        /* This suffix is missing in the subtree. Indicate this with the
+         * "suffix_skipped" flag and try the next instance / suffix. */
+        suffix_skipped = 1;
+      }
+    }
+
+    /* Update hostname_cell_ptr to point expected suffix */
+    if (hostname_cells != NULL) {
+      while (
+          (hostname_cell_ptr != NULL) &&
+          (csnmp_oid_compare(&hostname_cell_ptr->suffix, &current_suffix) < 0))
+        hostname_cell_ptr = hostname_cell_ptr->next;
+
+      if (hostname_cell_ptr == NULL) {
+        have_more = 0;
+        continue;
+      }
+
+      if (csnmp_oid_compare(&hostname_cell_ptr->suffix, &current_suffix) > 0) {
+        /* This suffix is missing in the subtree. Indicate this with the
+         * "suffix_skipped" flag and try the next instance / suffix. */
+        suffix_skipped = 1;
+      }
+    }
+
+    /* Update filter_cell_ptr to point expected suffix */
+    if (filter_cells != NULL) {
+      while ((filter_cell_ptr != NULL) &&
+             (csnmp_oid_compare(&filter_cell_ptr->suffix, &current_suffix) < 0))
+        filter_cell_ptr = filter_cell_ptr->next;
+
+      if (filter_cell_ptr == NULL) {
+        have_more = 0;
+        continue;
+      }
+
+      if (csnmp_oid_compare(&filter_cell_ptr->suffix, &current_suffix) > 0) {
+        /* This suffix is missing in the subtree. Indicate this with the
+         * "suffix_skipped" flag and try the next instance / suffix. */
+        suffix_skipped = 1;
+      }
+    }
+
+    /* Update all the value_cell_ptr to point at the entry with the same
      * trailing partial OID */
     for (i = 0; i < data->values_len; i++) {
       while (
-          (value_table_ptr[i] != NULL) &&
-          (csnmp_oid_compare(&value_table_ptr[i]->suffix, &current_suffix) < 0))
-        value_table_ptr[i] = value_table_ptr[i]->next;
+          (value_cell_ptr[i] != NULL) &&
+          (csnmp_oid_compare(&value_cell_ptr[i]->suffix, &current_suffix) < 0))
+        value_cell_ptr[i] = value_cell_ptr[i]->next;
 
-      if (value_table_ptr[i] == NULL) {
+      if (value_cell_ptr[i] == NULL) {
         have_more = 0;
         break;
-      } else if (csnmp_oid_compare(&value_table_ptr[i]->suffix,
+      } else if (csnmp_oid_compare(&value_cell_ptr[i]->suffix,
                                    &current_suffix) > 0) {
         /* This suffix is missing in the subtree. Indicate this with the
          * "suffix_skipped" flag and try the next instance / suffix. */
@@ -1187,43 +1426,98 @@ static int csnmp_dispatch_table(host_definition_t *host,
 
     /* Matching the values failed. Start from the beginning again. */
     if (suffix_skipped) {
-      if (instance_list != NULL)
-        instance_list_ptr = instance_list_ptr->next;
+      if (type_instance_cells != NULL)
+        type_instance_cell_ptr = type_instance_cell_ptr->next;
       else
-        value_table_ptr[0] = value_table_ptr[0]->next;
+        value_cell_ptr[0] = value_cell_ptr[0]->next;
 
       continue;
     }
 
-/* if we reach this line, all value_table_ptr[i] are non-NULL and are set
- * to the same subid. instance_list_ptr is either NULL or points to the
+/* if we reach this line, all value_cell_ptr[i] are non-NULL and are set
+ * to the same subid. type_instance_cell_ptr is either NULL or points to the
  * same subid, too. */
 #if COLLECT_DEBUG
     for (i = 1; i < data->values_len; i++) {
-      assert(value_table_ptr[i] != NULL);
-      assert(csnmp_oid_compare(&value_table_ptr[i - 1]->suffix,
-                               &value_table_ptr[i]->suffix) == 0);
+      assert(value_cell_ptr[i] != NULL);
+      assert(csnmp_oid_compare(&value_cell_ptr[i - 1]->suffix,
+                               &value_cell_ptr[i]->suffix) == 0);
     }
-    assert((instance_list_ptr == NULL) ||
-           (csnmp_oid_compare(&instance_list_ptr->suffix,
-                              &value_table_ptr[0]->suffix) == 0));
+    assert((type_instance_cell_ptr == NULL) ||
+           (csnmp_oid_compare(&type_instance_cell_ptr->suffix,
+                              &value_cell_ptr[0]->suffix) == 0));
+    assert((plugin_instance_cell_ptr == NULL) ||
+           (csnmp_oid_compare(&plugin_instance_cell_ptr->suffix,
+                              &value_cell_ptr[0]->suffix) == 0));
+    assert((hostname_cell_ptr == NULL) ||
+           (csnmp_oid_compare(&hostname_cell_ptr->suffix,
+                              &value_cell_ptr[0]->suffix) == 0));
+    assert((filter_cell_ptr == NULL) ||
+           (csnmp_oid_compare(&filter_cell_ptr->suffix,
+                              &value_cell_ptr[0]->suffix) == 0));
 #endif
 
-    sstrncpy(vl.type, data->type, sizeof(vl.type));
+    /* Check the value in filter column */
+    if (filter_cell_ptr &&
+        ignorelist_match(data->ignorelist, filter_cell_ptr->value) != 0) {
+      if (type_instance_cells != NULL)
+        type_instance_cell_ptr = type_instance_cell_ptr->next;
+      else
+        value_cell_ptr[0] = value_cell_ptr[0]->next;
 
-    {
+      continue;
+    }
+
+    /* set vl.host */
+    if (data->host.configured) {
       char temp[DATA_MAX_NAME_LEN];
+      if (hostname_cell_ptr == NULL)
+        csnmp_oid_to_string(temp, sizeof(temp), &current_suffix);
+      else
+        sstrncpy(temp, hostname_cell_ptr->value, sizeof(temp));
+
+      if (data->host.prefix == NULL)
+        sstrncpy(vl.host, temp, sizeof(vl.host));
+      else
+        snprintf(vl.host, sizeof(vl.host), "%s%s", data->host.prefix, temp);
+    } else {
+      sstrncpy(vl.host, host->name, sizeof(vl.host));
+    }
 
-      if (instance_list_ptr == NULL)
+    /* set vl.type_instance */
+    if (data->type_instance.configured) {
+      char temp[DATA_MAX_NAME_LEN];
+      if (type_instance_cell_ptr == NULL)
         csnmp_oid_to_string(temp, sizeof(temp), &current_suffix);
       else
-        sstrncpy(temp, instance_list_ptr->instance, sizeof(temp));
+        sstrncpy(temp, type_instance_cell_ptr->value, sizeof(temp));
 
-      if (data->instance_prefix == NULL)
+      if (data->type_instance.prefix == NULL)
         sstrncpy(vl.type_instance, temp, sizeof(vl.type_instance));
       else
         snprintf(vl.type_instance, sizeof(vl.type_instance), "%s%s",
-                 data->instance_prefix, temp);
+                 data->type_instance.prefix, temp);
+    } else if (data->type_instance.value) {
+      sstrncpy(vl.type_instance, data->type_instance.value,
+               sizeof(vl.type_instance));
+    }
+
+    /* set vl.plugin_instance */
+    if (data->plugin_instance.configured) {
+      char temp[DATA_MAX_NAME_LEN];
+      if (plugin_instance_cell_ptr == NULL)
+        csnmp_oid_to_string(temp, sizeof(temp), &current_suffix);
+      else
+        sstrncpy(temp, plugin_instance_cell_ptr->value, sizeof(temp));
+
+      if (data->plugin_instance.prefix == NULL)
+        sstrncpy(vl.plugin_instance, temp, sizeof(vl.plugin_instance));
+      else
+        snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s%s",
+                 data->plugin_instance.prefix, temp);
+    } else if (data->plugin_instance.value) {
+      sstrncpy(vl.plugin_instance, data->plugin_instance.value,
+               sizeof(vl.plugin_instance));
     }
 
     vl.values_len = data->values_len;
@@ -1231,23 +1525,18 @@ static int csnmp_dispatch_table(host_definition_t *host,
     vl.values = values;
 
     for (i = 0; i < data->values_len; i++)
-      vl.values[i] = value_table_ptr[i]->value;
+      vl.values[i] = value_cell_ptr[i]->value;
 
-    /* If we get here `vl.type_instance' and all `vl.values' have been set
-     * vl.type_instance can be empty, i.e. a blank port description on a
-     * switch if you're using IF-MIB::ifDescr as Instance.
-     */
-    if (vl.type_instance[0] != '\0')
-      plugin_dispatch_values(&vl);
+    plugin_dispatch_values(&vl);
 
     /* prevent leakage of pointer to local variable. */
     vl.values_len = 0;
     vl.values = NULL;
 
-    if (instance_list != NULL)
-      instance_list_ptr = instance_list_ptr->next;
+    if (type_instance_cells != NULL)
+      type_instance_cell_ptr = type_instance_cell_ptr->next;
     else
-      value_table_ptr[0] = value_table_ptr[0]->next;
+      value_cell_ptr[0] = value_cell_ptr[0]->next;
   } /* while (have_more) */
 
   return (0);
@@ -1260,25 +1549,43 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
 
   const data_set_t *ds;
 
-  size_t oid_list_len = data->values_len + 1;
+  size_t oid_list_len = data->values_len;
+
+  if (data->type_instance.oid.oid_len > 0)
+    oid_list_len++;
+
+  if (data->plugin_instance.oid.oid_len > 0)
+    oid_list_len++;
+
+  if (data->host.oid.oid_len > 0)
+    oid_list_len++;
+
+  if (data->filter_oid.oid_len > 0)
+    oid_list_len++;
+
   /* Holds the last OID returned by the device. We use this in the GETNEXT
    * request to proceed. */
   oid_t oid_list[oid_list_len];
   /* Set to false when an OID has left its subtree so we don't re-request it
    * again. */
-  bool oid_list_todo[oid_list_len];
+  csnmp_oid_type_t oid_list_todo[oid_list_len];
 
   int status;
   size_t i;
 
-  /* `value_list_head' and `value_list_tail' implement a linked list for each
-   * value. `instance_list_head' and `instance_list_tail' implement a linked
-   * list of
-   * instance names. This is used to jump gaps in the table. */
-  csnmp_list_instances_t *instance_list_head;
-  csnmp_list_instances_t *instance_list_tail;
-  csnmp_table_values_t **value_list_head;
-  csnmp_table_values_t **value_list_tail;
+  /* `value_list_head' and `value_cells_tail' implement a linked list for each
+   * value. `instance_cells_head' and `instance_cells_tail' implement a linked
+   * list of instance names. This is used to jump gaps in the table. */
+  csnmp_cell_char_t *type_instance_cells_head = NULL;
+  csnmp_cell_char_t *type_instance_cells_tail = NULL;
+  csnmp_cell_char_t *plugin_instance_cells_head = NULL;
+  csnmp_cell_char_t *plugin_instance_cells_tail = NULL;
+  csnmp_cell_char_t *hostname_cells_head = NULL;
+  csnmp_cell_char_t *hostname_cells_tail = NULL;
+  csnmp_cell_char_t *filter_cells_head = NULL;
+  csnmp_cell_char_t *filter_cells_tail = NULL;
+  csnmp_cell_value_t **value_cells_head;
+  csnmp_cell_value_t **value_cells_tail;
 
   DEBUG("snmp plugin: csnmp_read_table (host = %s, data = %s)", host->name,
         data->name);
@@ -1303,31 +1610,48 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
   }
   assert(data->values_len > 0);
 
+  for (i = 0; i < data->values_len; i++)
+    oid_list_todo[i] = OID_TYPE_VARIABLE;
+
   /* We need a copy of all the OIDs, because GETNEXT will destroy them. */
   memcpy(oid_list, data->values, data->values_len * sizeof(oid_t));
-  if (data->instance.oid.oid_len > 0)
-    memcpy(oid_list + data->values_len, &data->instance.oid, sizeof(oid_t));
-  else /* no InstanceFrom option specified. */
-    oid_list_len--;
 
-  for (i = 0; i < oid_list_len; i++)
-    oid_list_todo[i] = 1;
+  if (data->type_instance.oid.oid_len > 0) {
+    memcpy(oid_list + i, &data->type_instance.oid, sizeof(oid_t));
+    oid_list_todo[i] = OID_TYPE_TYPEINSTANCE;
+    i++;
+  }
+
+  if (data->plugin_instance.oid.oid_len > 0) {
+    memcpy(oid_list + i, &data->plugin_instance.oid, sizeof(oid_t));
+    oid_list_todo[i] = OID_TYPE_PLUGININSTANCE;
+    i++;
+  }
+
+  if (data->host.oid.oid_len > 0) {
+    memcpy(oid_list + i, &data->host.oid, sizeof(oid_t));
+    oid_list_todo[i] = OID_TYPE_HOST;
+    i++;
+  }
+
+  if (data->filter_oid.oid_len > 0) {
+    memcpy(oid_list + i, &data->filter_oid, sizeof(oid_t));
+    oid_list_todo[i] = OID_TYPE_FILTER;
+    i++;
+  }
 
   /* We're going to construct n linked lists, one for each "value".
-   * value_list_head will contain pointers to the heads of these linked lists,
-   * value_list_tail will contain pointers to the tail of the lists. */
-  value_list_head = calloc(data->values_len, sizeof(*value_list_head));
-  value_list_tail = calloc(data->values_len, sizeof(*value_list_tail));
-  if ((value_list_head == NULL) || (value_list_tail == NULL)) {
+   * value_cells_head will contain pointers to the heads of these linked lists,
+   * value_cells_tail will contain pointers to the tail of the lists. */
+  value_cells_head = calloc(data->values_len, sizeof(*value_cells_head));
+  value_cells_tail = calloc(data->values_len, sizeof(*value_cells_tail));
+  if ((value_cells_head == NULL) || (value_cells_tail == NULL)) {
     ERROR("snmp plugin: csnmp_read_table: calloc failed.");
-    sfree(value_list_head);
-    sfree(value_list_tail);
+    sfree(value_cells_head);
+    sfree(value_cells_tail);
     return -1;
   }
 
-  instance_list_head = NULL;
-  instance_list_tail = NULL;
-
   status = 0;
   while (status == 0) {
     req = snmp_pdu_create(SNMP_MSG_GETNEXT);
@@ -1440,30 +1764,126 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
       }
 
       /* An instance is configured and the res variable we process is the
-       * instance value (last index) */
-      if ((data->instance.oid.oid_len > 0) && (i == data->values_len)) {
+       * instance value */
+      if (oid_list_todo[i] == OID_TYPE_TYPEINSTANCE) {
         if ((vb->type == SNMP_ENDOFMIBVIEW) ||
-            (snmp_oid_ncompare(
-                 data->instance.oid.oid, data->instance.oid.oid_len, vb->name,
-                 vb->name_length, data->instance.oid.oid_len) != 0)) {
-          DEBUG("snmp plugin: host = %s; data = %s; Instance left its subtree.",
+            (snmp_oid_ncompare(data->type_instance.oid.oid,
+                               data->type_instance.oid.oid_len, vb->name,
+                               vb->name_length,
+                               data->type_instance.oid.oid_len) != 0)) {
+          DEBUG("snmp plugin: host = %s; data = %s; TypeInstance left its "
+                "subtree.",
                 host->name, data->name);
           oid_list_todo[i] = 0;
           continue;
         }
 
-        /* Allocate a new `csnmp_list_instances_t', insert the instance name and
+        /* Allocate a new `csnmp_cell_char_t', insert the instance name and
          * add it to the list */
-        if (csnmp_instance_list_add(&instance_list_head, &instance_list_tail,
-                                    res, host, data) != 0) {
-          ERROR("snmp plugin: host %s: csnmp_instance_list_add failed.",
+        csnmp_cell_char_t *cell =
+            csnmp_get_char_cell(vb, &data->type_instance.oid, host, data);
+        if (cell == NULL) {
+          ERROR("snmp plugin: host %s: csnmp_get_char_cell() failed.",
                 host->name);
           status = -1;
           break;
         }
+
+        if (csnmp_ignore_instance(cell, data)) {
+          sfree(cell);
+        } else {
+          csnmp_cell_replace_reserved_chars(cell);
+
+          DEBUG("snmp plugin: il->type_instance = `%s';", cell->value);
+          csnmp_cells_append(&type_instance_cells_head,
+                             &type_instance_cells_tail, cell);
+        }
+      } else if (oid_list_todo[i] == OID_TYPE_PLUGININSTANCE) {
+        if ((vb->type == SNMP_ENDOFMIBVIEW) ||
+            (snmp_oid_ncompare(data->plugin_instance.oid.oid,
+                               data->plugin_instance.oid.oid_len, vb->name,
+                               vb->name_length,
+                               data->plugin_instance.oid.oid_len) != 0)) {
+          DEBUG("snmp plugin: host = %s; data = %s; TypeInstance left its "
+                "subtree.",
+                host->name, data->name);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        /* Allocate a new `csnmp_cell_char_t', insert the instance name and
+         * add it to the list */
+        csnmp_cell_char_t *cell =
+            csnmp_get_char_cell(vb, &data->plugin_instance.oid, host, data);
+        if (cell == NULL) {
+          ERROR("snmp plugin: host %s: csnmp_get_char_cell() failed.",
+                host->name);
+          status = -1;
+          break;
+        }
+
+        csnmp_cell_replace_reserved_chars(cell);
+
+        DEBUG("snmp plugin: il->plugin_instance = `%s';", cell->value);
+        csnmp_cells_append(&plugin_instance_cells_head,
+                           &plugin_instance_cells_tail, cell);
+      } else if (oid_list_todo[i] == OID_TYPE_HOST) {
+        if ((vb->type == SNMP_ENDOFMIBVIEW) ||
+            (snmp_oid_ncompare(data->host.oid.oid, data->host.oid.oid_len,
+                               vb->name, vb->name_length,
+                               data->host.oid.oid_len) != 0)) {
+          DEBUG("snmp plugin: host = %s; data = %s; Host left its subtree.",
+                host->name, data->name);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        /* Allocate a new `csnmp_cell_char_t', insert the instance name and
+         * add it to the list */
+        csnmp_cell_char_t *cell =
+            csnmp_get_char_cell(vb, &data->host.oid, host, data);
+        if (cell == NULL) {
+          ERROR("snmp plugin: host %s: csnmp_get_char_cell() failed.",
+                host->name);
+          status = -1;
+          break;
+        }
+
+        csnmp_cell_replace_reserved_chars(cell);
+
+        DEBUG("snmp plugin: il->hostname = `%s';", cell->value);
+        csnmp_cells_append(&hostname_cells_head, &hostname_cells_tail, cell);
+      } else if (oid_list_todo[i] == OID_TYPE_FILTER) {
+        if ((vb->type == SNMP_ENDOFMIBVIEW) ||
+            (snmp_oid_ncompare(data->filter_oid.oid, data->filter_oid.oid_len,
+                               vb->name, vb->name_length,
+                               data->filter_oid.oid_len) != 0)) {
+          DEBUG("snmp plugin: host = %s; data = %s; Host left its subtree.",
+                host->name, data->name);
+          oid_list_todo[i] = 0;
+          continue;
+        }
+
+        /* Allocate a new `csnmp_cell_char_t', insert the instance name and
+         * add it to the list */
+        csnmp_cell_char_t *cell =
+            csnmp_get_char_cell(vb, &data->filter_oid, host, data);
+        if (cell == NULL) {
+          ERROR("snmp plugin: host %s: csnmp_get_char_cell() failed.",
+                host->name);
+          status = -1;
+          break;
+        }
+
+        csnmp_cell_replace_reserved_chars(cell);
+
+        DEBUG("snmp plugin: il->filter = `%s';", cell->value);
+        csnmp_cells_append(&filter_cells_head, &filter_cells_tail, cell);
       } else /* The variable we are processing is a normal value */
       {
-        csnmp_table_values_t *vt;
+        assert(oid_list_todo[i] == OID_TYPE_VARIABLE);
+
+        csnmp_cell_value_t *vt;
         oid_t vb_name;
         oid_t suffix;
         int ret;
@@ -1482,10 +1902,9 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
         }
 
         /* Make sure the OIDs returned by the agent are increasing. Otherwise
-         * our
-         * table matching algorithm will get confused. */
-        if ((value_list_tail[i] != NULL) &&
-            (csnmp_oid_compare(&suffix, &value_list_tail[i]->suffix) <= 0)) {
+         * our table matching algorithm will get confused. */
+        if ((value_cells_tail[i] != NULL) &&
+            (csnmp_oid_compare(&suffix, &value_cells_tail[i]->suffix) <= 0)) {
           DEBUG("snmp plugin: host = %s; data = %s; i = %" PRIsz "; "
                 "Suffix is not increasing.",
                 host->name, data->name, i);
@@ -1506,11 +1925,11 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
         memcpy(&vt->suffix, &suffix, sizeof(vt->suffix));
         vt->next = NULL;
 
-        if (value_list_tail[i] == NULL)
-          value_list_head[i] = vt;
+        if (value_cells_tail[i] == NULL)
+          value_cells_head[i] = vt;
         else
-          value_list_tail[i]->next = vt;
-        value_list_tail[i] = vt;
+          value_cells_tail[i]->next = vt;
+        value_cells_tail[i] = vt;
       }
 
       /* Copy OID to oid_list[i] */
@@ -1529,25 +1948,45 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) {
   res = NULL;
 
   if (status == 0)
-    csnmp_dispatch_table(host, data, instance_list_head, value_list_head);
+    csnmp_dispatch_table(host, data, type_instance_cells_head,
+                         plugin_instance_cells_head, hostname_cells_head,
+                         filter_cells_head, value_cells_head);
 
   /* Free all allocated variables here */
-  while (instance_list_head != NULL) {
-    csnmp_list_instances_t *next = instance_list_head->next;
-    sfree(instance_list_head);
-    instance_list_head = next;
+  while (type_instance_cells_head != NULL) {
+    csnmp_cell_char_t *next = type_instance_cells_head->next;
+    sfree(type_instance_cells_head);
+    type_instance_cells_head = next;
+  }
+
+  while (plugin_instance_cells_head != NULL) {
+    csnmp_cell_char_t *next = plugin_instance_cells_head->next;
+    sfree(plugin_instance_cells_head);
+    plugin_instance_cells_head = next;
+  }
+
+  while (hostname_cells_head != NULL) {
+    csnmp_cell_char_t *next = hostname_cells_head->next;
+    sfree(hostname_cells_head);
+    hostname_cells_head = next;
+  }
+
+  while (filter_cells_head != NULL) {
+    csnmp_cell_char_t *next = filter_cells_head->next;
+    sfree(filter_cells_head);
+    filter_cells_head = next;
   }
 
   for (i = 0; i < data->values_len; i++) {
-    while (value_list_head[i] != NULL) {
-      csnmp_table_values_t *next = value_list_head[i]->next;
-      sfree(value_list_head[i]);
-      value_list_head[i] = next;
+    while (value_cells_head[i] != NULL) {
+      csnmp_cell_value_t *next = value_cells_head[i]->next;
+      sfree(value_cells_head[i]);
+      value_cells_head[i] = next;
     }
   }
 
-  sfree(value_list_head);
-  sfree(value_list_tail);
+  sfree(value_cells_head);
+  sfree(value_cells_tail);
 
   return 0;
 } /* int csnmp_read_table */
@@ -1597,11 +2036,14 @@ static int csnmp_read_value(host_definition_t *host, data_definition_t *data) {
   }
 
   sstrncpy(vl.host, host->name, sizeof(vl.host));
-  sstrncpy(vl.plugin, "snmp", sizeof(vl.plugin));
+  sstrncpy(vl.plugin, data->plugin_name, sizeof(vl.plugin));
   sstrncpy(vl.type, data->type, sizeof(vl.type));
-  sstrncpy(vl.type_instance, data->instance.string, sizeof(vl.type_instance));
-
-  vl.interval = host->interval;
+  if (data->type_instance.value)
+    sstrncpy(vl.type_instance, data->type_instance.value,
+             sizeof(vl.type_instance));
+  if (data->plugin_instance.value)
+    sstrncpy(vl.plugin_instance, data->plugin_instance.value,
+             sizeof(vl.plugin_instance));
 
   req = snmp_pdu_create(SNMP_MSG_GET);
   if (req == NULL) {
@@ -1664,9 +2106,6 @@ static int csnmp_read_host(user_data_t *ud) {
 
   host = ud->data;
 
-  if (host->interval == 0)
-    host->interval = plugin_get_interval();
-
   if (host->sess_handle == NULL)
     csnmp_host_open_session(host);
 
@@ -1711,11 +2150,7 @@ static int csnmp_shutdown(void) {
   while (data_this != NULL) {
     data_next = data_this->next;
 
-    sfree(data_this->name);
-    sfree(data_this->type);
-    sfree(data_this->values);
-    sfree(data_this->ignores);
-    sfree(data_this);
+    csnmp_data_definition_destroy(data_this);
 
     data_this = data_next;
   }
index 0d1c577..1c7191f 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/snmp_agent.c
  *
- * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ * Copyright(c) 2017-2018 Intel Corporation. All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +24,7 @@
  * Authors:
  *   Roman Korynkevych <romanx.korynkevych@intel.com>
  *   Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ *   Marcin Mozejko <marcinx.mozejko@intel.com>
  **/
 
 #include "collectd.h"
@@ -32,6 +33,7 @@
 #include "utils_avltree.h"
 #include "utils_cache.h"
 #include "utils_llist.h"
+#include <regex.h>
 
 #include <net-snmp/net-snmp-config.h>
 
 #include <net-snmp/agent/net-snmp-agent-includes.h>
 
 #define PLUGIN_NAME "snmp_agent"
-#define ERR_BUF_SIZE 1024
 #define TYPE_STRING -1
+#define GROUP_UNUSED -1
+#define OID_EXISTS 1
+#define MAX_KEY_SOURCES 5
+#define MAX_INDEX_KEYS 5
+#define MAX_MATCHES 5
+
+/* Identifies index key source */
+enum index_key_src_e {
+  INDEX_HOST = 0,
+  INDEX_PLUGIN,
+  INDEX_PLUGIN_INSTANCE,
+  INDEX_TYPE,
+  INDEX_TYPE_INSTANCE
+};
+typedef enum index_key_src_e index_key_src_t;
 
-#ifndef MIN
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-#endif
+struct index_key_s {
+  index_key_src_t source;
+  u_char type;
+  char *regex; /* Pattern used to parse index key source string */
+  int group;   /* If pattern gives more than one group we can specify which one
+                  we want to take */
+  regex_t regex_info;
+};
+typedef struct index_key_s index_key_t;
 
 struct oid_s {
   oid oid[MAX_OID_LEN];
@@ -54,6 +76,12 @@ struct oid_s {
 };
 typedef struct oid_s oid_t;
 
+struct token_s {
+  char *str;
+  netsnmp_variable_list *key; /* Points to succeeding key */
+};
+typedef struct token_s token_t;
+
 struct table_definition_s {
   char *name;
   oid_t index_oid;
@@ -61,6 +89,19 @@ struct table_definition_s {
   llist_t *columns;
   c_avl_tree_t *instance_index;
   c_avl_tree_t *index_instance;
+  c_avl_tree_t *instance_oids; /* Tells us how many OIDs registered for every
+                                  instance; */
+  index_key_t index_keys[MAX_INDEX_KEYS]; /* Stores information about what each
+                                             index key represents */
+  int index_keys_len;
+  netsnmp_variable_list *index_list_cont; /* Index key container used for
+                                             generating as well as parsing
+                                             OIDs, not thread-safe */
+  c_avl_tree_t *tokens[MAX_KEY_SOURCES];  /* Input string after regex execution
+                                             will be split into sepearate
+                                             tokens */
+
+  bool tokens_done; /* Set to true when all tokens are generated */
 };
 typedef struct table_definition_s table_definition_t;
 
@@ -71,7 +112,8 @@ struct data_definition_s {
   char *type;
   char *type_instance;
   const table_definition_t *table;
-  bool is_instance;
+  bool is_index_key; /* indicates if table column is an index key */
+  int index_key_pos; /* position in indexes list */
   oid_t *oids;
   size_t oids_len;
   double scale;
@@ -87,10 +129,13 @@ struct snmp_agent_ctx_s {
 
   llist_t *tables;
   llist_t *scalars;
+  c_avl_tree_t *registered_oids; /* AVL tree containing all registered OIDs */
 };
 typedef struct snmp_agent_ctx_s snmp_agent_ctx_t;
 
 static snmp_agent_ctx_t *g_agent;
+static const char *index_opts[MAX_KEY_SOURCES] = {
+    "Hostname", "Plugin", "PluginInstance", "Type", "TypeInstance"};
 
 #define CHECK_DD_TYPE(_dd, _p, _pi, _t, _ti)                                   \
   (_dd->plugin ? !strcmp(_dd->plugin, _p) : 0) &&                              \
@@ -105,6 +150,9 @@ static int snmp_agent_set_vardata(void *dst_buf, size_t *dst_buf_len,
                                   u_char asn_type, double scale, double shift,
                                   const void *value, size_t len, int type);
 static int snmp_agent_unregister_oid_index(oid_t *oid, int index);
+static int snmp_agent_update_instance_oids(c_avl_tree_t *tree, oid_t *index_oid,
+                                           int value);
+static int num_compare(const int *a, const int *b);
 
 static u_char snmp_agent_get_asn_type(oid *oid, size_t oid_len) {
   struct tree *node = get_tree(oid, oid_len, g_agent->tp);
@@ -131,10 +179,55 @@ static int snmp_agent_oid_to_string(char *buf, size_t buf_size,
   return strjoin(buf, buf_size, oid_str_ptr, o->oid_len, ".");
 }
 
-static void snmp_agent_dump_data(void) {
+/* Prints a configuration storing list. It handles both table columns list
+   and scalars list */
 #if COLLECT_DEBUG
+static void snmp_agent_dump_data(llist_t *list) {
   char oid_str[DATA_MAX_NAME_LEN];
+  for (llentry_t *de = llist_head(list); de != NULL; de = de->next) {
+    data_definition_t *dd = de->value;
+    table_definition_t const *td = dd->table;
 
+    if (dd->table != NULL)
+      DEBUG(PLUGIN_NAME ":   Column:");
+    else
+      DEBUG(PLUGIN_NAME ": Scalar:");
+
+    DEBUG(PLUGIN_NAME ":     Name: %s", dd->name);
+    if (dd->plugin)
+      DEBUG(PLUGIN_NAME ":     Plugin: %s", dd->plugin);
+    if (dd->plugin_instance)
+      DEBUG(PLUGIN_NAME ":     PluginInstance: %s", dd->plugin_instance);
+    if (dd->is_index_key) {
+      index_key_t const *index_key = &td->index_keys[dd->index_key_pos];
+
+      DEBUG(PLUGIN_NAME ":     IndexKey:");
+      DEBUG(PLUGIN_NAME ":       Source: %s", index_opts[index_key->source]);
+      DEBUG(PLUGIN_NAME ":       Type: %s",
+            (index_key->type == ASN_INTEGER) ? "Integer" : "String");
+      if (index_key->regex)
+        DEBUG(PLUGIN_NAME ":       Regex: %s", index_key->regex);
+      if (index_key->group != GROUP_UNUSED)
+        DEBUG(PLUGIN_NAME ":       Group: %d", index_key->group);
+    }
+    if (dd->type)
+      DEBUG(PLUGIN_NAME ":     Type: %s", dd->type);
+    if (dd->type_instance)
+      DEBUG(PLUGIN_NAME ":     TypeInstance: %s", dd->type_instance);
+    for (size_t i = 0; i < dd->oids_len; i++) {
+      snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &dd->oids[i]);
+      DEBUG(PLUGIN_NAME ":     OID[%" PRIsz "]: %s", i, oid_str);
+    }
+    DEBUG(PLUGIN_NAME ":   Scale: %g", dd->scale);
+    DEBUG(PLUGIN_NAME ":   Shift: %g", dd->shift);
+  }
+}
+
+/* Prints parsed configuration */
+static void snmp_agent_dump_config(void) {
+  char oid_str[DATA_MAX_NAME_LEN];
+
+  /* Printing tables */
   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
     table_definition_t *td = te->value;
 
@@ -149,62 +242,28 @@ static void snmp_agent_dump_data(void) {
       DEBUG(PLUGIN_NAME ":   SizeOID: %s", oid_str);
     }
 
-    for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
-      data_definition_t *dd = de->value;
-
-      DEBUG(PLUGIN_NAME ":   Column:");
-      DEBUG(PLUGIN_NAME ":     Name: %s", dd->name);
-      if (dd->plugin)
-        DEBUG(PLUGIN_NAME ":     Plugin: %s", dd->plugin);
-      if (dd->plugin_instance)
-        DEBUG(PLUGIN_NAME ":     PluginInstance: %s", dd->plugin_instance);
-      if (dd->is_instance)
-        DEBUG(PLUGIN_NAME ":     Instance: true");
-      if (dd->type)
-        DEBUG(PLUGIN_NAME ":     Type: %s", dd->type);
-      if (dd->type_instance)
-        DEBUG(PLUGIN_NAME ":     TypeInstance: %s", dd->type_instance);
-      for (size_t i = 0; i < dd->oids_len; i++) {
-        snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &dd->oids[i]);
-        DEBUG(PLUGIN_NAME ":     OID[%" PRIsz "]: %s", i, oid_str);
-      }
-      DEBUG(PLUGIN_NAME ":   Scale: %g", dd->scale);
-      DEBUG(PLUGIN_NAME ":   Shift: %g", dd->shift);
-    }
+    snmp_agent_dump_data(td->columns);
   }
 
-  for (llentry_t *e = llist_head(g_agent->scalars); e != NULL; e = e->next) {
-    data_definition_t *dd = e->value;
-
-    DEBUG(PLUGIN_NAME ": Scalar:");
-    DEBUG(PLUGIN_NAME ":   Name: %s", dd->name);
-    if (dd->plugin)
-      DEBUG(PLUGIN_NAME ":   Plugin: %s", dd->plugin);
-    if (dd->plugin_instance)
-      DEBUG(PLUGIN_NAME ":   PluginInstance: %s", dd->plugin_instance);
-    if (dd->is_instance)
-      DEBUG(PLUGIN_NAME ":   Instance: true");
-    if (dd->type)
-      DEBUG(PLUGIN_NAME ":   Type: %s", dd->type);
-    if (dd->type_instance)
-      DEBUG(PLUGIN_NAME ":   TypeInstance: %s", dd->type_instance);
-    for (size_t i = 0; i < dd->oids_len; i++) {
-      snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &dd->oids[i]);
-      DEBUG(PLUGIN_NAME ":   OID[%" PRIsz "]: %s", i, oid_str);
-    }
-    DEBUG(PLUGIN_NAME ":   Scale: %g", dd->scale);
-    DEBUG(PLUGIN_NAME ":   Shift: %g", dd->shift);
-  }
-#endif /* COLLECT_DEBUG */
+  /* Printing scalars */
+  snmp_agent_dump_data(g_agent->scalars);
 }
+#endif /* COLLECT_DEBUG */
 
-static int snmp_agent_validate_data(void) {
+static int snmp_agent_validate_config(void) {
 
-  snmp_agent_dump_data();
+#if COLLECT_DEBUG
+  snmp_agent_dump_config();
+#endif
 
   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
     table_definition_t *td = te->value;
 
+    if (!td->index_keys_len) {
+      ERROR(PLUGIN_NAME ": Index keys not defined for '%s'", td->name);
+      return -EINVAL;
+    }
+
     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
       data_definition_t *dd = de->value;
 
@@ -227,11 +286,10 @@ static int snmp_agent_validate_data(void) {
         return -EINVAL;
       }
 
-      if (dd->is_instance) {
-
+      if (dd->is_index_key) {
         if (dd->type || dd->type_instance) {
           ERROR(PLUGIN_NAME ": Type and TypeInstance are not valid for "
-                            "instance data '%s'.'%s'",
+                            "index data '%s'.'%s'",
                 td->name, dd->name);
           return -EINVAL;
         }
@@ -267,9 +325,8 @@ static int snmp_agent_validate_data(void) {
       return -EINVAL;
     }
 
-    if (dd->is_instance) {
-      ERROR(PLUGIN_NAME
-            ": Instance flag can't be specified for scalar data '%s'",
+    if (dd->is_index_key) {
+      ERROR(PLUGIN_NAME ": Index field can't be specified for scalar data '%s'",
             dd->name);
       return -EINVAL;
     }
@@ -283,109 +340,429 @@ static int snmp_agent_validate_data(void) {
   return 0;
 }
 
-static void snmp_agent_generate_oid2string(oid_t *oid, size_t offset,
-                                           char *key) {
-  size_t key_len = oid->oid[offset];
-  size_t i;
+static int snmp_agent_parse_index_key(const char *input, regex_t *regex_info,
+                                      int gi, regmatch_t *m) {
+  regmatch_t matches[MAX_MATCHES];
+
+  int ret = regexec(regex_info, input, MAX_MATCHES, matches, 0);
+  if (!ret) {
+    if (gi > regex_info->re_nsub) {
+      ERROR(PLUGIN_NAME ": Group index %d not found. Check regex config", gi);
+      return -1;
+    }
+    *m = matches[gi];
+  } else if (ret == REG_NOMATCH) {
+    ERROR(PLUGIN_NAME ": No match found");
+    return -1;
+  } else {
+    char msgbuf[100];
+
+    regerror(ret, regex_info, msgbuf, sizeof(msgbuf));
+    ERROR(PLUGIN_NAME ": Regex match failed: %s", msgbuf);
+    return -1;
+  }
+
+  return 0;
+}
+
+static int snmp_agent_create_token(char const *input, int t_off, int n,
+                                   c_avl_tree_t *tree,
+                                   netsnmp_variable_list *index_key) {
+  assert(tree != NULL);
+
+  token_t *token = malloc(sizeof(*token));
+
+  if (token == NULL)
+    goto error;
+
+  int *offset = malloc(sizeof(*offset));
+
+  if (offset == NULL)
+    goto free_token_error;
+
+  int ret = 0;
+
+  token->key = index_key;
+  input += t_off;
+  size_t len = strlen(input);
+
+  if (n < len)
+    len = n;
 
-  for (i = 0; i < key_len && offset < oid->oid_len; i++)
-    key[i] = oid->oid[++offset];
+  token->str = malloc(len + 1);
+
+  if (token->str == NULL)
+    goto free_offset_error;
+
+  /* copy at most n bytes from input with offset t_off into token->str */
+  sstrncpy(token->str, input, len + 1);
+  *offset = t_off;
+  ret = c_avl_insert(tree, (void *)offset, (void *)token);
+
+  if (ret == 0)
+    return 0;
+
+  sfree(token->str);
+
+free_offset_error:
+  sfree(offset);
+
+free_token_error:
+  sfree(token);
+
+error:
+  ERROR(PLUGIN_NAME ": Could not allocate memory to create token");
+
+  return -1;
+}
+
+static int snmp_agent_delete_token(int t_off, c_avl_tree_t *tree) {
+  token_t *token = NULL;
+  int *offset = NULL;
+
+  int ret = c_avl_remove(tree, &t_off, (void **)&offset, (void **)&token);
+
+  if (ret != 0) {
+    ERROR(PLUGIN_NAME ": Could not delete token");
+    return -1;
+  }
+
+  sfree(token->str);
+  sfree(token);
+  sfree(offset);
+  return 0;
+}
+
+static int snmp_agent_get_token(c_avl_tree_t *tree, int mpos) {
+
+  int *pos;
+  char *token;
+  int prev_pos = 0;
+
+  c_avl_iterator_t *it = c_avl_get_iterator(tree);
+  while (c_avl_iterator_next(it, (void **)&pos, (void **)&token) == 0) {
+    if (*pos >= mpos)
+      break;
+    else
+      prev_pos = *pos;
+  }
+
+  c_avl_iterator_destroy(it);
+  return prev_pos;
+}
+
+static int snmp_agent_tokenize(const char *input, c_avl_tree_t *tokens,
+                               const regmatch_t *m,
+                               netsnmp_variable_list *key) {
+  assert(tokens != NULL);
+
+  int ret = 0;
+  int len = strlen(input);
+
+  /* Creating first token that is going to be split later */
+  if (c_avl_size(tokens) == 0) {
+    ret = snmp_agent_create_token(input, 0, len, tokens, NULL);
+    if (ret != 0)
+      return ret;
+  }
+
+  /* Divide token that contains current match into two */
+  int t_pos = snmp_agent_get_token(tokens, m->rm_so);
+  ret = snmp_agent_delete_token(t_pos, tokens);
+
+  if (ret != 0)
+    return -1;
+
+  ret = snmp_agent_create_token(input, t_pos, m->rm_so - t_pos, tokens, key);
+
+  if (ret != 0)
+    return -1;
+
+  if (len - m->rm_eo > 1) {
+    ret = snmp_agent_create_token(input, m->rm_eo, len - m->rm_eo + 1, tokens,
+                                  NULL);
+    if (ret != 0) {
+      snmp_agent_delete_token(t_pos, tokens);
+      return -1;
+    }
+  }
 
-  key[i] = '\0';
+  return 0;
 }
 
-static int snmp_agent_generate_string2oid(oid_t *oid, const char *key) {
-  size_t key_len = strlen(key);
+static int snmp_agent_fill_index_list(table_definition_t *td,
+                                      value_list_t const *vl) {
+  int ret;
+  int i;
+  netsnmp_variable_list *key = td->index_list_cont;
+  char const *ptr;
+
+  for (i = 0; i < td->index_keys_len; i++) {
+    /* var should never be NULL */
+    assert(key != NULL);
+    ptr = NULL;
+    const index_key_src_t source = td->index_keys[i].source;
+    c_avl_tree_t *const tokens = td->tokens[source];
+    /* Generating list filled with all data necessary to generate an OID */
+    switch (source) {
+    case INDEX_HOST:
+      ptr = vl->host;
+      break;
+    case INDEX_PLUGIN:
+      ptr = vl->plugin;
+      break;
+    case INDEX_PLUGIN_INSTANCE:
+      ptr = vl->plugin_instance;
+      break;
+    case INDEX_TYPE:
+      ptr = vl->type;
+      break;
+    case INDEX_TYPE_INSTANCE:
+      ptr = vl->type_instance;
+      break;
+    default:
+      ERROR(PLUGIN_NAME ": Unknown index key source provided");
+      return -EINVAL;
+    }
+
+    /* Parsing input string if necessary */
+    if (td->index_keys[i].regex) {
+      regmatch_t m;
+
+      /* Parsing input string */
+      ret = snmp_agent_parse_index_key(ptr, &td->index_keys[i].regex_info,
+                                       td->index_keys[i].group, &m);
+      if (ret != 0) {
+        ERROR(PLUGIN_NAME ": Error executing regex");
+        return ret;
+      }
+
+      /* Tokenizing input string if not done yet */
+      if (td->tokens_done == false)
+        ret = snmp_agent_tokenize(ptr, tokens, &m, key);
+
+      if (ret != 0)
+        return -1;
+
+      if (td->index_keys[i].type == ASN_INTEGER) {
+        int val = strtol(ptr + m.rm_so, NULL, 0);
+
+#ifdef HAVE_NETSNMP_OLD_API
+        ret = snmp_set_var_value(key, (const u_char *)&val, sizeof(val));
+#else
+        ret = snmp_set_var_value(key, &val, sizeof(val));
+#endif
+      } else
+#ifdef HAVE_NETSNMP_OLD_API
+        ret = snmp_set_var_value(key, (const u_char *)(ptr + m.rm_so),
+                                 m.rm_eo - m.rm_so);
+#else
+        ret = snmp_set_var_value(key, ptr + m.rm_so, m.rm_eo - m.rm_so);
+#endif
+    } else
+#ifdef HAVE_NETSNMP_OLD_API
+      ret = snmp_set_var_value(key, (const u_char *)ptr, strlen(ptr));
+#else
+      ret = snmp_set_var_value(key, ptr, strlen(ptr));
+#endif
+
+    if (ret != 0)
+      return -1;
+
+    key = key->next_variable;
+  }
+
+  /* Tokens for all source strings are generated */
+  for (i = 0; i < MAX_KEY_SOURCES; i++)
+    td->tokens_done = true;
+
+  return 0;
+}
 
-  oid->oid[oid->oid_len++] = key_len;
-  for (size_t i = 0; i < key_len; i++) {
-    oid->oid[oid->oid_len++] = key[i];
-    if (oid->oid_len >= MAX_OID_LEN) {
-      ERROR(PLUGIN_NAME ": Conversion key string %s to OID failed", key);
+static int snmp_agent_prep_index_list(table_definition_t const *td,
+                                      netsnmp_variable_list **index_list) {
+  /* Generating list having only the structure (with no values) letting us
+   * know how to parse an OID*/
+  for (int i = 0; i < td->index_keys_len; i++) {
+    switch (td->index_keys[i].source) {
+    case INDEX_HOST:
+    case INDEX_PLUGIN:
+    case INDEX_PLUGIN_INSTANCE:
+    case INDEX_TYPE:
+    case INDEX_TYPE_INSTANCE:
+      snmp_varlist_add_variable(index_list, NULL, 0, td->index_keys[i].type,
+                                NULL, 0);
+      break;
+    default:
+      ERROR(PLUGIN_NAME ": Unknown index key source provided");
       return -EINVAL;
     }
   }
+  return 0;
+}
+
+static int snmp_agent_generate_index(table_definition_t *td,
+                                     value_list_t const *vl, oid_t *index_oid) {
+
+  /* According to given information by index_keys list
+   * index OID is going to be built
+   */
+  int ret = snmp_agent_fill_index_list(td, vl);
+  if (ret != 0)
+    return -EINVAL;
+
+  /* Building only index part OID (without table prefix OID) */
+  ret = build_oid_noalloc(index_oid->oid, sizeof(index_oid->oid),
+                          &index_oid->oid_len, NULL, 0, td->index_list_cont);
+  if (ret != SNMPERR_SUCCESS) {
+    ERROR(PLUGIN_NAME ": Error building index OID");
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+/* It appends one OID to the end of another */
+static int snmp_agent_append_oid(oid_t *out, const oid_t *in) {
+
+  if (out->oid_len + in->oid_len > MAX_OID_LEN) {
+    ERROR(PLUGIN_NAME ": Cannot create OID. Output length is too long!");
+    return -EINVAL;
+  }
+  memcpy(&out->oid[out->oid_len], in->oid, in->oid_len * sizeof(oid));
+  out->oid_len += in->oid_len;
 
   return 0;
 }
 
-static int snmp_agent_register_oid_string(oid_t *oid, const char *key,
+static int snmp_agent_register_oid_string(const oid_t *oid,
+                                          const oid_t *index_oid,
                                           Netsnmp_Node_Handler *handler) {
   oid_t new_oid;
 
   memcpy(&new_oid, oid, sizeof(*oid));
-  int ret = snmp_agent_generate_string2oid(&new_oid, key);
+  /* Concatenating two string oids */
+  int ret = snmp_agent_append_oid(&new_oid, index_oid);
   if (ret != 0)
     return ret;
 
   return snmp_agent_register_oid(&new_oid, handler);
 }
 
-static int snmp_agent_unregister_oid_string(oid_t *oid, const char *key) {
+static int snmp_agent_unregister_oid(oid_t *oid) {
+  int ret = c_avl_remove(g_agent->registered_oids, (void *)oid, NULL, NULL);
+
+  if (ret != 0)
+    ERROR(PLUGIN_NAME ": Could not delete registration info");
+
+  return unregister_mib(oid->oid, oid->oid_len);
+}
+
+static int snmp_agent_unregister_oid_string(oid_t *oid,
+                                            const oid_t *index_oid) {
   oid_t new_oid;
+  char oid_str[DATA_MAX_NAME_LEN];
 
   memcpy(&new_oid, oid, sizeof(*oid));
-  int ret = snmp_agent_generate_string2oid(&new_oid, key);
+  /* Concatenating two string oids */
+  int ret = snmp_agent_append_oid(&new_oid, index_oid);
   if (ret != 0)
     return ret;
 
-  return unregister_mib(new_oid.oid, new_oid.oid_len);
+  snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &new_oid);
+  DEBUG(PLUGIN_NAME ": Unregistered handler for OID (%s)", oid_str);
+
+  return snmp_agent_unregister_oid(&new_oid);
 }
 
-static int snmp_agent_table_row_remove(table_definition_t *td,
-                                       const char *instance) {
+static void snmp_agent_table_data_remove(data_definition_t *dd,
+                                         table_definition_t *td,
+                                         oid_t *index_oid) {
   int *index = NULL;
-  char *ins = NULL;
+  oid_t *ind_oid = NULL;
 
   if (td->index_oid.oid_len) {
-    if ((c_avl_get(td->instance_index, instance, (void **)&index) != 0) ||
-        (c_avl_get(td->index_instance, index, (void **)&ins) != 0))
-      return 0;
+    if ((c_avl_get(td->instance_index, index_oid, (void **)&index) != 0) ||
+        (c_avl_get(td->index_instance, index, NULL) != 0))
+      return;
   } else {
-    if (c_avl_get(td->instance_index, instance, (void **)&ins) != 0)
-      return 0;
+    if (c_avl_get(td->instance_index, index_oid, NULL) != 0)
+      return;
   }
 
   pthread_mutex_lock(&g_agent->agentx_lock);
 
-  if (td->index_oid.oid_len)
-    snmp_agent_unregister_oid_index(&td->index_oid, *index);
+  int reg_oids = -1; /* Number of registered oids for given instance */
+
+  for (size_t i = 0; i < dd->oids_len; i++) {
+    if (td->index_oid.oid_len)
+      snmp_agent_unregister_oid_index(&dd->oids[i], *index);
+    else
+      snmp_agent_unregister_oid_string(&dd->oids[i], index_oid);
+
+    reg_oids =
+        snmp_agent_update_instance_oids(td->instance_oids, index_oid, -1);
+  }
+
+  /* Checking if any metrics are left registered */
+  if (reg_oids != 0) {
+    pthread_mutex_unlock(&g_agent->agentx_lock);
+    return;
+  }
 
+  /* All metrics have been unregistered. Unregistering index key OIDs */
+  int keys_processed = 0;
   for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
-    data_definition_t *dd = de->value;
+    data_definition_t *idd = de->value;
 
-    for (size_t i = 0; i < dd->oids_len; i++)
+    if (!idd->is_index_key)
+      continue;
+
+    for (size_t i = 0; i < idd->oids_len; i++)
       if (td->index_oid.oid_len)
-        snmp_agent_unregister_oid_index(&dd->oids[i], *index);
+        snmp_agent_unregister_oid_index(&idd->oids[i], *index);
       else
-        snmp_agent_unregister_oid_string(&dd->oids[i], ins);
-  }
+        snmp_agent_unregister_oid_string(&idd->oids[i], index_oid);
 
+    if (++keys_processed >= td->index_keys_len)
+      break;
+  }
   pthread_mutex_unlock(&g_agent->agentx_lock);
 
-  DEBUG(PLUGIN_NAME ": Removed row for '%s' table [%d, %s]", td->name,
-        (index != NULL) ? *index : -1, ins);
+  /* All OIDs have been unregistered so we dont need this instance registered
+   * as well */
+  char index_str[DATA_MAX_NAME_LEN];
+
+  if (index == NULL)
+    snmp_agent_oid_to_string(index_str, sizeof(index_str), index_oid);
+  else
+    snprintf(index_str, sizeof(index_str), "%d", *index);
 
   notification_t n = {
       .severity = NOTIF_WARNING, .time = cdtime(), .plugin = PLUGIN_NAME};
   sstrncpy(n.host, hostname_g, sizeof(n.host));
-  sstrncpy(n.plugin_instance, ins, sizeof(n.plugin_instance));
   snprintf(n.message, sizeof(n.message),
-           "Removed data row from table %s instance %s index %d", td->name, ins,
-           (index != NULL) ? *index : -1);
+           "Removed data row from table %s with index %s", td->name, index_str);
+  DEBUG(PLUGIN_NAME ": %s", n.message);
   plugin_dispatch_notification(&n);
 
-  if (td->index_oid.oid_len) {
-    c_avl_remove(td->index_instance, index, NULL, (void **)&ins);
-    c_avl_remove(td->instance_index, instance, NULL, (void **)&index);
+  int *val = NULL;
+
+  c_avl_remove(td->instance_oids, index_oid, NULL, (void **)&val);
+  sfree(val);
+
+  if (index != NULL) {
+    pthread_mutex_lock(&g_agent->agentx_lock);
+    snmp_agent_unregister_oid_index(&td->index_oid, *index);
+    pthread_mutex_unlock(&g_agent->agentx_lock);
+
+    c_avl_remove(td->index_instance, index, NULL, (void **)&ind_oid);
+    c_avl_remove(td->instance_index, index_oid, NULL, (void **)&index);
     sfree(index);
-    sfree(ins);
+    sfree(ind_oid);
   } else {
-    c_avl_remove(td->instance_index, instance, NULL, (void **)&ins);
-    sfree(ins);
+    c_avl_remove(td->instance_index, index_oid, NULL, NULL);
   }
-
-  return 0;
 }
 
 static int snmp_agent_clear_missing(const value_list_t *vl,
@@ -399,11 +776,23 @@ static int snmp_agent_clear_missing(const value_list_t *vl,
     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
       data_definition_t *dd = de->value;
 
-      if (!dd->is_instance) {
+      if (!dd->is_index_key) {
         if (CHECK_DD_TYPE(dd, vl->plugin, vl->plugin_instance, vl->type,
                           vl->type_instance)) {
-          snmp_agent_table_row_remove(td, vl->plugin_instance);
-          return 0;
+          oid_t *index_oid = calloc(1, sizeof(*index_oid));
+
+          if (index_oid == NULL) {
+            ERROR(PLUGIN_NAME ": Could not allocate memory for index_oid");
+            return -ENOMEM;
+          }
+
+          int ret = snmp_agent_generate_index(td, vl, index_oid);
+
+          if (ret == 0)
+            snmp_agent_table_data_remove(dd, td, index_oid);
+          sfree(index_oid);
+
+          return ret;
         }
       }
     }
@@ -444,23 +833,22 @@ static void snmp_agent_free_table_columns(table_definition_t *td) {
 
     if (td->index_oid.oid_len) {
       int *index;
-      char *instance;
+      oid_t *index_oid;
 
       c_avl_iterator_t *iter = c_avl_get_iterator(td->index_instance);
-      while (c_avl_iterator_next(iter, (void *)&index, (void *)&instance) ==
+      while (c_avl_iterator_next(iter, (void *)&index, (void *)&index_oid) ==
              0) {
         for (size_t i = 0; i < dd->oids_len; i++)
           snmp_agent_unregister_oid_index(&dd->oids[i], *index);
       }
       c_avl_iterator_destroy(iter);
     } else {
-      char *instance;
+      oid_t *index_oid;
 
       c_avl_iterator_t *iter = c_avl_get_iterator(dd->table->instance_index);
-      while (c_avl_iterator_next(iter, (void *)&instance, (void *)&instance) ==
-             0) {
+      while (c_avl_iterator_next(iter, (void *)&index_oid, NULL) == 0) {
         for (size_t i = 0; i < dd->oids_len; i++)
-          snmp_agent_unregister_oid_string(&dd->oids[i], instance);
+          snmp_agent_unregister_oid_string(&dd->oids[i], index_oid);
       }
       c_avl_iterator_destroy(iter);
     }
@@ -480,13 +868,14 @@ static void snmp_agent_free_table(table_definition_t **td) {
   if ((*td)->size_oid.oid_len)
     unregister_mib((*td)->size_oid.oid, (*td)->size_oid.oid_len);
 
+  oid_t *index_oid;
+
   /* Unregister Index OIDs */
   if ((*td)->index_oid.oid_len) {
     int *index;
-    char *instance;
 
     c_avl_iterator_t *iter = c_avl_get_iterator((*td)->index_instance);
-    while (c_avl_iterator_next(iter, (void *)&index, (void *)&instance) == 0)
+    while (c_avl_iterator_next(iter, (void **)&index, (void **)&index_oid) == 0)
       snmp_agent_unregister_oid_index(&(*td)->index_oid, *index);
 
     c_avl_iterator_destroy(iter);
@@ -497,6 +886,15 @@ static void snmp_agent_free_table(table_definition_t **td) {
 
   void *key = NULL;
   void *value = NULL;
+  int *num = NULL;
+
+  /* Removing data from instance_oids, leaving key pointers since they are still
+   * used in other AVL trees */
+  c_avl_iterator_t *iter = c_avl_get_iterator((*td)->instance_oids);
+  while (c_avl_iterator_next(iter, (void **)&index_oid, (void **)&num) == 0)
+    sfree(num);
+  c_avl_iterator_destroy(iter);
+  c_avl_destroy((*td)->instance_oids);
 
   /* index_instance and instance_index contain the same pointers */
   c_avl_destroy((*td)->index_instance);
@@ -511,20 +909,189 @@ static void snmp_agent_free_table(table_definition_t **td) {
     c_avl_destroy((*td)->instance_index);
     (*td)->instance_index = NULL;
   }
+  snmp_free_varbind((*td)->index_list_cont);
+
+  int i;
+  token_t *tok = NULL;
 
+  for (i = 0; i < (*td)->index_keys_len; i++) {
+    sfree((*td)->index_keys[i].regex);
+    regfree(&(*td)->index_keys[i].regex_info);
+  }
+  for (i = 0; i < MAX_KEY_SOURCES; i++)
+    if ((*td)->tokens[i] != NULL) {
+      while (c_avl_pick((*td)->tokens[i], &key, (void **)&tok) == 0) {
+        sfree(key);
+        sfree(tok->str);
+        sfree(tok);
+      }
+      c_avl_destroy((*td)->tokens[i]);
+      (*td)->tokens[i] = NULL;
+    }
   sfree((*td)->name);
   sfree(*td);
 
   return;
 }
 
+static int snmp_agent_parse_oid_index_keys(const table_definition_t *td,
+                                           oid_t *index_oid) {
+  assert(index_oid != NULL);
+  int ret = parse_oid_indexes(index_oid->oid, index_oid->oid_len,
+                              td->index_list_cont);
+  if (ret != SNMPERR_SUCCESS)
+    ERROR(PLUGIN_NAME ": index OID parse error!");
+  return ret;
+}
+
+static int snmp_agent_build_name(char **name, c_avl_tree_t *tokens) {
+  int *pos;
+  token_t *tok;
+  char str[DATA_MAX_NAME_LEN];
+  char out[DATA_MAX_NAME_LEN] = {0};
+  c_avl_iterator_t *it = c_avl_get_iterator(tokens);
+
+  if (it == NULL) {
+    ERROR(PLUGIN_NAME ": Error getting tokens list iterator");
+    return -1;
+  }
+
+  while (c_avl_iterator_next(it, (void **)&pos, (void **)&tok) == 0) {
+    strncat(out, tok->str, DATA_MAX_NAME_LEN - strlen(out) - 1);
+    if (tok->key != NULL) {
+      if (tok->key->type == ASN_INTEGER) {
+        snprintf(str, sizeof(str), "%ld", *tok->key->val.integer);
+        strncat(out, str, DATA_MAX_NAME_LEN - strlen(out) - 1);
+      } else /* OCTET_STR */
+        strncat(out, (char *)tok->key->val.string,
+                DATA_MAX_NAME_LEN - strlen(out) - 1);
+    }
+  }
+
+  c_avl_iterator_destroy(it);
+  *name = strdup(out);
+
+  if (*name == NULL) {
+    ERROR(PLUGIN_NAME ": Could not allocate memory");
+    return -ENOMEM;
+  }
+
+  return 0;
+}
+
+static int snmp_agent_format_name(char *name, int name_len,
+                                  data_definition_t *dd, oid_t *index_oid) {
+
+  int ret = 0;
+
+  if (index_oid == NULL) {
+    /* It's a scalar */
+    format_name(name, name_len, hostname_g, dd->plugin, dd->plugin_instance,
+                dd->type, dd->type_instance);
+  } else {
+    /* Need to parse string index OID */
+    const table_definition_t *td = dd->table;
+    ret = snmp_agent_parse_oid_index_keys(td, index_oid);
+    if (ret != 0)
+      return ret;
+
+    int i = 0;
+    netsnmp_variable_list *key = td->index_list_cont;
+    char str[DATA_MAX_NAME_LEN];
+    char *fields[MAX_KEY_SOURCES] = {hostname_g, dd->plugin,
+                                     dd->plugin_instance, dd->type,
+                                     dd->type_instance};
+
+    /* Looking for simple keys only */
+    while (key != NULL) {
+      if (!td->index_keys[i].regex) {
+        index_key_src_t source = td->index_keys[i].source;
+
+        if (source < INDEX_HOST || source > INDEX_TYPE_INSTANCE) {
+          ERROR(PLUGIN_NAME ": Unkown index key source!");
+          return -EINVAL;
+        }
+
+        if (td->index_keys[i].type == ASN_INTEGER) {
+          snprintf(str, sizeof(str), "%ld", *key->val.integer);
+          fields[source] = str;
+        } else /* OCTET_STR */
+          fields[source] = (char *)key->val.string;
+      }
+      key = key->next_variable;
+      i++;
+    }
+
+    /* Keys with regexes */
+    for (i = 0; i < MAX_KEY_SOURCES; i++) {
+      if (td->tokens[i] == NULL)
+        continue;
+      ret = snmp_agent_build_name(&fields[i], td->tokens[i]);
+      if (ret != 0)
+        return ret;
+    }
+    format_name(name, name_len, fields[INDEX_HOST], fields[INDEX_PLUGIN],
+                fields[INDEX_PLUGIN_INSTANCE], fields[INDEX_TYPE],
+                fields[INDEX_TYPE_INSTANCE]);
+    for (i = 0; i < MAX_KEY_SOURCES; i++) {
+      if (td->tokens[i])
+        sfree(fields[i]);
+    }
+  }
+
+  return 0;
+}
+
 static int snmp_agent_form_reply(struct netsnmp_request_info_s *requests,
-                                 data_definition_t *dd, char *instance,
+                                 data_definition_t *dd, oid_t *index_oid,
                                  int oid_index) {
+  int ret;
+
+  if (dd->is_index_key) {
+    const table_definition_t *td = dd->table;
+    int ret = snmp_agent_parse_oid_index_keys(td, index_oid);
+
+    if (ret != 0)
+      return ret;
+
+    netsnmp_variable_list *key = td->index_list_cont;
+    /* Searching index key */
+    for (int pos = 0; pos < dd->index_key_pos; pos++)
+      key = key->next_variable;
+
+    requests->requestvb->type = td->index_keys[dd->index_key_pos].type;
+
+    if (requests->requestvb->type == ASN_INTEGER)
+#ifdef HAVE_NETSNMP_OLD_API
+      snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
+                               (const u_char *)key->val.integer,
+                               sizeof(*key->val.integer));
+#else
+      snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
+                               key->val.integer, sizeof(*key->val.integer));
+#endif
+    else /* OCTET_STR */
+#ifdef HAVE_NETSNMP_OLD_API
+      snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
+                               (const u_char *)key->val.string,
+                               strlen((const char *)key->val.string));
+#else
+      snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
+                               key->val.string,
+                               strlen((const char *)key->val.string));
+#endif
+
+    pthread_mutex_unlock(&g_agent->lock);
+
+    return SNMP_ERR_NOERROR;
+  }
+
   char name[DATA_MAX_NAME_LEN];
-  format_name(name, sizeof(name), hostname_g, dd->plugin,
-              instance ? instance : dd->plugin_instance, dd->type,
-              dd->type_instance);
+
+  ret = snmp_agent_format_name(name, sizeof(name), dd, index_oid);
+  if (ret != 0)
+    return ret;
+
   DEBUG(PLUGIN_NAME ": Identifier '%s'", name);
 
   value_t *values;
@@ -535,7 +1102,7 @@ static int snmp_agent_form_reply(struct netsnmp_request_info_s *requests,
     return SNMP_NOSUCHINSTANCE;
   }
 
-  int ret = uc_get_value_by_name(name, &values, &values_num);
+  ret = uc_get_value_by_name(name, &values, &values_num);
 
   if (ret != 0) {
     ERROR(PLUGIN_NAME ": Failed to get value for '%s'", name);
@@ -571,14 +1138,14 @@ snmp_agent_table_oid_handler(struct netsnmp_mib_handler_s *handler,
                              struct netsnmp_agent_request_info_s *reqinfo,
                              struct netsnmp_request_info_s *requests) {
 
-  if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
+  if (reqinfo->mode != MODE_GET) {
     DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
     return SNMP_ERR_NOERROR;
   }
 
   pthread_mutex_lock(&g_agent->lock);
 
-  oid_t oid;
+  oid_t oid; /* Requested OID */
   memcpy(oid.oid, requests->requestvb->name,
          sizeof(oid.oid[0]) * requests->requestvb->name_length);
   oid.oid_len = requests->requestvb->name_length;
@@ -588,6 +1155,7 @@ snmp_agent_table_oid_handler(struct netsnmp_mib_handler_s *handler,
   snmp_agent_oid_to_string(oid_str, sizeof(oid_str), &oid);
   DEBUG(PLUGIN_NAME ": Get request received for table OID '%s'", oid_str);
 #endif
+  oid_t index_oid; /* Index part of requested OID */
 
   for (llentry_t *te = llist_head(g_agent->tables); te != NULL; te = te->next) {
     table_definition_t *td = te->value;
@@ -598,49 +1166,37 @@ snmp_agent_table_oid_handler(struct netsnmp_mib_handler_s *handler,
       for (size_t i = 0; i < dd->oids_len; i++) {
         int ret = snmp_oid_ncompare(oid.oid, oid.oid_len, dd->oids[i].oid,
                                     dd->oids[i].oid_len,
-                                    MIN(oid.oid_len, dd->oids[i].oid_len));
+                                    SNMP_MIN(oid.oid_len, dd->oids[i].oid_len));
         if (ret != 0)
           continue;
 
-        char *instance;
-
-        if (!td->index_oid.oid_len) {
-          char key[MAX_OID_LEN];
+        /* Calculating OID length for index part */
+        index_oid.oid_len = oid.oid_len - dd->oids[i].oid_len;
+        /* Fetching index part of the OID */
+        memcpy(index_oid.oid, &oid.oid[dd->oids[i].oid_len],
+               index_oid.oid_len * sizeof(*oid.oid));
 
-          memset(key, 0, sizeof(key));
-          snmp_agent_generate_oid2string(
-              &oid, MIN(oid.oid_len, dd->oids[i].oid_len), key);
+        char index_str[DATA_MAX_NAME_LEN];
+        snmp_agent_oid_to_string(index_str, sizeof(index_str), &index_oid);
 
-          ret = c_avl_get(td->instance_index, key, (void **)&instance);
-          if (ret != 0) {
-            DEBUG(PLUGIN_NAME ": Nonexisting index string '%s' requested", key);
-            pthread_mutex_unlock(&g_agent->lock);
-            return SNMP_NOSUCHINSTANCE;
-          }
+        if (!td->index_oid.oid_len) {
+          ret = c_avl_get(td->instance_index, &index_oid, NULL);
         } else {
-          int index = oid.oid[oid.oid_len - 1];
+          oid_t *temp_oid;
 
-          ret = c_avl_get(td->index_instance, &index, (void **)&instance);
-          if (ret != 0) {
-            DEBUG(PLUGIN_NAME ": Nonexisting index '%d' requested", index);
-            pthread_mutex_unlock(&g_agent->lock);
-            return SNMP_NOSUCHINSTANCE;
-          }
+          assert(index_oid.oid_len == 1);
+          ret = c_avl_get(td->index_instance, (int *)&index_oid.oid[0],
+                          (void **)&temp_oid);
+          memcpy(&index_oid, temp_oid, sizeof(index_oid));
         }
 
-        if (dd->is_instance) {
-          requests->requestvb->type = ASN_OCTET_STR;
-          snmp_set_var_typed_value(
-              requests->requestvb, requests->requestvb->type,
-              (const u_char *)instance, strlen((instance)));
-
+        if (ret != 0) {
+          INFO(PLUGIN_NAME ": Non-existing index (%s) requested", index_str);
           pthread_mutex_unlock(&g_agent->lock);
-
-          return SNMP_ERR_NOERROR;
+          return SNMP_NOSUCHINSTANCE;
         }
 
-        ret = snmp_agent_form_reply(requests, dd, instance, i);
-
+        ret = snmp_agent_form_reply(requests, dd, &index_oid, i);
         pthread_mutex_unlock(&g_agent->lock);
 
         return ret;
@@ -659,7 +1215,7 @@ static int snmp_agent_table_index_oid_handler(
     struct netsnmp_agent_request_info_s *reqinfo,
     struct netsnmp_request_info_s *requests) {
 
-  if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
+  if (reqinfo->mode != MODE_GET) {
     DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
     return SNMP_ERR_NOERROR;
   }
@@ -675,15 +1231,15 @@ static int snmp_agent_table_index_oid_handler(
     table_definition_t *td = te->value;
 
     if (td->index_oid.oid_len &&
-        (snmp_oid_ncompare(oid.oid, oid.oid_len, td->index_oid.oid,
-                           td->index_oid.oid_len,
-                           MIN(oid.oid_len, td->index_oid.oid_len)) == 0)) {
+        (snmp_oid_ncompare(
+             oid.oid, oid.oid_len, td->index_oid.oid, td->index_oid.oid_len,
+             SNMP_MIN(oid.oid_len, td->index_oid.oid_len)) == 0)) {
 
       DEBUG(PLUGIN_NAME ": Handle '%s' table index OID", td->name);
 
       int index = oid.oid[oid.oid_len - 1];
 
-      int ret = c_avl_get(td->index_instance, &index, &(void *){NULL});
+      int ret = c_avl_get(td->index_instance, &index, NULL);
       if (ret != 0) {
         /* nonexisting index requested */
         pthread_mutex_unlock(&g_agent->lock);
@@ -711,7 +1267,7 @@ static int snmp_agent_table_size_oid_handler(
     struct netsnmp_agent_request_info_s *reqinfo,
     struct netsnmp_request_info_s *requests) {
 
-  if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
+  if (reqinfo->mode != MODE_GET) {
     DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
     return SNMP_ERR_NOERROR;
   }
@@ -731,12 +1287,16 @@ static int snmp_agent_table_size_oid_handler(
     if (td->size_oid.oid_len &&
         (snmp_oid_ncompare(oid.oid, oid.oid_len, td->size_oid.oid,
                            td->size_oid.oid_len,
-                           MIN(oid.oid_len, td->size_oid.oid_len)) == 0)) {
+                           SNMP_MIN(oid.oid_len, td->size_oid.oid_len)) == 0)) {
       DEBUG(PLUGIN_NAME ": Handle '%s' table size OID", td->name);
 
-      long size = c_avl_size(td->index_instance);
+      long size;
+      if (td->index_oid.oid_len)
+        size = c_avl_size(td->index_instance);
+      else
+        size = c_avl_size(td->instance_index);
 
-      requests->requestvb->type = td->size_oid.type;
+      requests->requestvb->type = ASN_INTEGER;
       snmp_set_var_typed_value(requests->requestvb, requests->requestvb->type,
                                (const u_char *)&size, sizeof(size));
 
@@ -757,7 +1317,7 @@ snmp_agent_scalar_oid_handler(struct netsnmp_mib_handler_s *handler,
                               struct netsnmp_agent_request_info_s *reqinfo,
                               struct netsnmp_request_info_s *requests) {
 
-  if (reqinfo->mode != MODE_GET && reqinfo->mode != MODE_GETNEXT) {
+  if (reqinfo->mode != MODE_GET) {
     DEBUG(PLUGIN_NAME ": Not supported request mode (%d)", reqinfo->mode);
     return SNMP_ERR_NOERROR;
   }
@@ -860,10 +1420,14 @@ static int snmp_agent_config_data_oids(data_definition_t *dd,
       return -EINVAL;
     }
 
-  if (dd->oids != NULL)
-    sfree(dd->oids);
+  if (dd->oids != NULL) {
+    WARNING(PLUGIN_NAME ": OIDs can be configured only once for each data");
+    return -EINVAL;
+  }
+
   dd->oids_len = 0;
   dd->oids = calloc(ci->values_num, sizeof(*dd->oids));
+
   if (dd->oids == NULL)
     return -ENOMEM;
   dd->oids_len = (size_t)ci->values_num;
@@ -935,98 +1499,125 @@ static int snmp_agent_config_table_index_oid(table_definition_t *td,
   return 0;
 }
 
-static int snmp_agent_config_table_data(table_definition_t *td,
-                                        oconfig_item_t *ci) {
-  data_definition_t *dd;
-  int ret = 0;
+/* Getting index key source that will represent table row */
+static int snmp_agent_config_index_key_source(table_definition_t *td,
+                                              data_definition_t *dd,
+                                              oconfig_item_t *ci) {
+  char *val = NULL;
 
-  assert(ci != NULL);
+  int ret = cf_util_get_string(ci, &val);
+  if (ret != 0)
+    return -1;
 
-  dd = calloc(1, sizeof(*dd));
-  if (dd == NULL) {
-    ERROR(PLUGIN_NAME ": Failed to allocate memory for table data definition");
-    return -ENOMEM;
+  bool match = false;
+
+  for (int i = 0; i < MAX_KEY_SOURCES; i++) {
+    if (strcasecmp(index_opts[i], (const char *)val) == 0) {
+      td->index_keys[td->index_keys_len].source = i;
+      td->index_keys[td->index_keys_len].group = GROUP_UNUSED;
+      td->index_keys[td->index_keys_len].regex = NULL;
+      match = 1;
+      break;
+    }
   }
 
-  ret = cf_util_get_string(ci, &dd->name);
-  if (ret != 0) {
-    sfree(dd);
-    return -1;
+  if (!match) {
+    ERROR(PLUGIN_NAME ": Failed to parse index key source: '%s'", val);
+    sfree(val);
+    return -EINVAL;
   }
 
-  dd->scale = 1.0;
-  dd->shift = 0.0;
+  sfree(val);
+  dd->index_key_pos = td->index_keys_len++;
+  dd->is_index_key = true;
 
-  dd->table = td;
+  return 0;
+}
 
-  for (int i = 0; i < ci->children_num; i++) {
-    oconfig_item_t *option = ci->children + i;
+/* Getting format string used to parse values from index key source */
+static int snmp_agent_config_index_key_regex(table_definition_t *td,
+                                             data_definition_t *dd,
+                                             oconfig_item_t *ci) {
+  index_key_t *index_key = &td->index_keys[dd->index_key_pos];
 
-    if (strcasecmp("Instance", option->key) == 0)
-      ret = cf_util_get_boolean(option, &dd->is_instance);
-    else if (strcasecmp("Plugin", option->key) == 0)
-      ret = cf_util_get_string(option, &dd->plugin);
-    else if (strcasecmp("PluginInstance", option->key) == 0)
-      ret = cf_util_get_string(option, &dd->plugin_instance);
-    else if (strcasecmp("Type", option->key) == 0)
-      ret = cf_util_get_string(option, &dd->type);
-    else if (strcasecmp("TypeInstance", option->key) == 0)
-      ret = cf_util_get_string(option, &dd->type_instance);
-    else if (strcasecmp("Shift", option->key) == 0)
-      ret = cf_util_get_double(option, &dd->shift);
-    else if (strcasecmp("Scale", option->key) == 0)
-      ret = cf_util_get_double(option, &dd->scale);
-    else if (strcasecmp("OIDs", option->key) == 0)
-      ret = snmp_agent_config_data_oids(dd, option);
-    else {
-      WARNING(PLUGIN_NAME ": Option `%s' not allowed here", option->key);
-      ret = -1;
-    }
+  int ret = cf_util_get_string(ci, &index_key->regex);
+  if (ret != 0)
+    return -1;
 
-    if (ret != 0) {
-      snmp_agent_free_data(&dd);
-      return -1;
-    }
+  ret = regcomp(&index_key->regex_info, index_key->regex, REG_EXTENDED);
+  if (ret) {
+    ERROR(PLUGIN_NAME ": Could not compile regex for %s", dd->name);
+    return -1;
   }
 
-  llentry_t *entry = llentry_create(dd->name, dd);
-  if (entry == NULL) {
-    snmp_agent_free_data(&dd);
-    return -ENOMEM;
+  index_key_src_t source = index_key->source;
+  if (td->tokens[source] == NULL) {
+    td->tokens[source] =
+        c_avl_create((int (*)(const void *, const void *))num_compare);
+    if (td->tokens[source] == NULL) {
+      ERROR(PLUGIN_NAME ": Could not allocate memory for AVL tree");
+      return -ENOMEM;
+    }
   }
 
-  llist_append(td->columns, entry);
-
   return 0;
 }
 
-static int snmp_agent_config_data(oconfig_item_t *ci) {
+static int snmp_agent_config_index_key(table_definition_t *td,
+                                       data_definition_t *dd,
+                                       oconfig_item_t *ci) {
+  int ret = 0;
+
+  for (int i = 0; (i < ci->children_num && ret == 0); i++) {
+    oconfig_item_t *option = ci->children + i;
+
+    if (strcasecmp("Source", option->key) == 0)
+      ret = snmp_agent_config_index_key_source(td, dd, option);
+    else if (strcasecmp("Regex", option->key) == 0)
+      ret = snmp_agent_config_index_key_regex(td, dd, option);
+    else if (strcasecmp("Group", option->key) == 0)
+      ret = cf_util_get_int(option, &td->index_keys[dd->index_key_pos].group);
+  }
+
+  return ret;
+}
+
+/* This function parses configuration of both scalar and table column
+ * because they have nearly the same structure */
+static int snmp_agent_config_table_column(table_definition_t *td,
+                                          oconfig_item_t *ci) {
   data_definition_t *dd;
   int ret = 0;
+  oconfig_item_t *option_tmp = NULL;
 
   assert(ci != NULL);
 
   dd = calloc(1, sizeof(*dd));
   if (dd == NULL) {
-    ERROR(PLUGIN_NAME ": Failed to allocate memory for data definition");
+    ERROR(PLUGIN_NAME ": Failed to allocate memory for table data definition");
     return -ENOMEM;
   }
 
   ret = cf_util_get_string(ci, &dd->name);
   if (ret != 0) {
-    free(dd);
+    sfree(dd);
     return -1;
   }
 
   dd->scale = 1.0;
   dd->shift = 0.0;
+  /* NULL if it's a scalar */
+  dd->table = td;
+  dd->is_index_key = false;
 
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
 
-    if (strcasecmp("Instance", option->key) == 0)
-      ret = cf_util_get_boolean(option, &dd->is_instance);
-    else if (strcasecmp("Plugin", option->key) == 0)
+    /* First 3 options are reserved for table entry only */
+    if (td != NULL && strcasecmp("IndexKey", option->key) == 0) {
+      dd->is_index_key = true;
+      option_tmp = option;
+    } else if (strcasecmp("Plugin", option->key) == 0)
       ret = cf_util_get_string(option, &dd->plugin);
     else if (strcasecmp("PluginInstance", option->key) == 0)
       ret = cf_util_get_string(option, &dd->plugin_instance);
@@ -1051,17 +1642,37 @@ static int snmp_agent_config_data(oconfig_item_t *ci) {
     }
   }
 
+  if (dd->is_index_key) {
+    ret = snmp_agent_config_index_key(td, dd, option_tmp);
+    td->index_keys[dd->index_key_pos].type =
+        snmp_agent_get_asn_type(dd->oids[0].oid, dd->oids[0].oid_len);
+
+    if (ret != 0) {
+      snmp_agent_free_data(&dd);
+      return -1;
+    }
+  }
+
   llentry_t *entry = llentry_create(dd->name, dd);
   if (entry == NULL) {
     snmp_agent_free_data(&dd);
     return -ENOMEM;
   }
 
-  llist_append(g_agent->scalars, entry);
+  /* Append to column list in parent table */
+  if (td != NULL)
+    llist_append(td->columns, entry);
+  else
+    llentry_destroy(entry);
 
   return 0;
 }
 
+/* Parses scalar configuration entry */
+static int snmp_agent_config_scalar(oconfig_item_t *ci) {
+  return snmp_agent_config_table_column(NULL, ci);
+}
+
 static int num_compare(const int *a, const int *b) {
   assert((a != NULL) && (b != NULL));
   if (*a < *b)
@@ -1072,6 +1683,10 @@ static int num_compare(const int *a, const int *b) {
     return 0;
 }
 
+static int oid_compare(const oid_t *a, const oid_t *b) {
+  return snmp_oid_compare(a->oid, a->oid_len, b->oid, b->oid_len);
+}
+
 static int snmp_agent_config_table(oconfig_item_t *ci) {
   table_definition_t *td;
   int ret = 0;
@@ -1097,6 +1712,10 @@ static int snmp_agent_config_table(oconfig_item_t *ci) {
     return -ENOMEM;
   }
 
+  for (int i = 0; i < MAX_KEY_SOURCES; i++)
+    td->tokens[i] = NULL;
+  td->tokens_done = false;
+
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *option = ci->children + i;
 
@@ -1105,7 +1724,7 @@ static int snmp_agent_config_table(oconfig_item_t *ci) {
     else if (strcasecmp("SizeOID", option->key) == 0)
       ret = snmp_agent_config_table_size_oid(td, option);
     else if (strcasecmp("Data", option->key) == 0)
-      ret = snmp_agent_config_table_data(td, option);
+      ret = snmp_agent_config_table_column(td, option);
     else {
       WARNING(PLUGIN_NAME ": Option `%s' not allowed here", option->key);
       ret = -1;
@@ -1117,8 +1736,13 @@ static int snmp_agent_config_table(oconfig_item_t *ci) {
     }
   }
 
+  /* Preparing index list container */
+  ret = snmp_agent_prep_index_list(td, &td->index_list_cont);
+  if (ret != 0)
+    return -EINVAL;
+
   td->instance_index =
-      c_avl_create((int (*)(const void *, const void *))strcmp);
+      c_avl_create((int (*)(const void *, const void *))oid_compare);
   if (td->instance_index == NULL) {
     snmp_agent_free_table(&td);
     return -ENOMEM;
@@ -1131,11 +1755,19 @@ static int snmp_agent_config_table(oconfig_item_t *ci) {
     return -ENOMEM;
   }
 
+  td->instance_oids =
+      c_avl_create((int (*)(const void *, const void *))oid_compare);
+  if (td->instance_oids == NULL) {
+    snmp_agent_free_table(&td);
+    return -ENOMEM;
+  }
+
   llentry_t *entry = llentry_create(td->name, td);
   if (entry == NULL) {
     snmp_agent_free_table(&td);
     return -ENOMEM;
   }
+
   llist_append(g_agent->tables, entry);
 
   return 0;
@@ -1236,98 +1868,167 @@ static int snmp_agent_unregister_oid_index(oid_t *oid, int index) {
   oid_t new_oid;
   memcpy(&new_oid, oid, sizeof(*oid));
   new_oid.oid[new_oid.oid_len++] = index;
-  return unregister_mib(new_oid.oid, new_oid.oid_len);
+  return snmp_agent_unregister_oid(&new_oid);
 }
 
-static int snmp_agent_update_index(table_definition_t *td,
-                                   const char *instance) {
+static int snmp_agent_update_instance_oids(c_avl_tree_t *tree, oid_t *index_oid,
+                                           int value) {
+  int *oids_num; /* number of oids registered for instance */
 
-  if (c_avl_get(td->instance_index, instance, NULL) == 0)
-    return 0;
+  if (c_avl_get(tree, index_oid, (void **)&oids_num) == 0) {
+    *oids_num += value;
+    return *oids_num;
+  } else {
+    ERROR(PLUGIN_NAME ": Error updating index data");
+    return -1;
+  }
+}
 
+static int snmp_agent_update_index(data_definition_t *dd,
+                                   table_definition_t *td, oid_t *index_oid,
+                                   bool *free_index_oid) {
   int ret;
   int *index = NULL;
-  char *ins;
+  int *value = NULL;
 
-  ins = strdup(instance);
-  if (ins == NULL)
-    return -ENOMEM;
+  if (c_avl_get(td->instance_index, (void *)index_oid, (void **)&index) != 0) {
+    /* We'll keep index_oid stored in AVL tree */
+    *free_index_oid = false;
 
-  /* need to generate index for the table */
-  if (td->index_oid.oid_len) {
-    index = calloc(1, sizeof(*index));
-    if (index == NULL) {
-      sfree(ins);
-      return -ENOMEM;
+    /* need to generate index for the table */
+    if (td->index_oid.oid_len) {
+      index = calloc(1, sizeof(*index));
+      if (index == NULL) {
+        ret = -ENOMEM;
+        goto error;
+      }
+
+      *index = c_avl_size(td->instance_index) + 1;
+
+      ret = c_avl_insert(td->instance_index, index_oid, index);
+      if (ret != 0)
+        goto free_index;
+
+      ret = c_avl_insert(td->index_instance, index, index_oid);
+      if (ret < 0) {
+        DEBUG(PLUGIN_NAME ": Failed to update index_instance for '%s' table",
+              td->name);
+        goto remove_avl_index_oid;
+      }
+
+      ret = snmp_agent_register_oid_index(&td->index_oid, *index,
+                                          snmp_agent_table_index_oid_handler);
+      if (ret != 0)
+        goto remove_avl_index;
+    } else {
+      /* instance as a key is required for any table */
+      ret = c_avl_insert(td->instance_index, index_oid, NULL);
+      if (ret != 0)
+        goto error;
     }
 
-    *index = c_avl_size(td->instance_index) + 1;
+    value = calloc(1, sizeof(*value));
 
-    ret = c_avl_insert(td->instance_index, ins, index);
-    if (ret != 0) {
-      sfree(ins);
-      sfree(index);
-      return ret;
+    if (value == NULL) {
+      ERROR(PLUGIN_NAME ": Failed to allocate memory");
+      ret = -ENOMEM;
+      goto unregister_index;
     }
 
-    ret = c_avl_insert(td->index_instance, index, ins);
+    ret = c_avl_insert(td->instance_oids, index_oid, value);
+
     if (ret < 0) {
-      DEBUG(PLUGIN_NAME ": Failed to update index_instance for '%s' table",
+      DEBUG(PLUGIN_NAME ": Failed to update instance_oids for '%s' table",
             td->name);
-      c_avl_remove(td->instance_index, ins, NULL, (void **)&index);
-      sfree(ins);
-      sfree(index);
-      return ret;
+      goto free_value;
     }
 
-    ret = snmp_agent_register_oid_index(&td->index_oid, *index,
-                                        snmp_agent_table_index_oid_handler);
-    if (ret != 0)
-      return ret;
-  } else {
-    /* instance as a key is required for any table */
-    ret = c_avl_insert(td->instance_index, ins, ins);
-    if (ret != 0) {
-      sfree(ins);
-      return ret;
-    }
-  }
+    int keys_processed = 0;
 
-  /* register new oids for all columns */
-  for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
-    data_definition_t *dd = de->value;
+    /* Registering index keys OIDs */
+    for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
+      data_definition_t *idd = de->value;
+      if (!idd->is_index_key)
+        continue;
 
-    for (size_t i = 0; i < dd->oids_len; i++) {
-      if (td->index_oid.oid_len) {
-        ret = snmp_agent_register_oid_index(&dd->oids[i], *index,
-                                            snmp_agent_table_oid_handler);
-      } else {
-        ret = snmp_agent_register_oid_string(&dd->oids[i], ins,
-                                             snmp_agent_table_oid_handler);
+      for (size_t i = 0; i < idd->oids_len; i++) {
+        if (td->index_oid.oid_len)
+          ret = snmp_agent_register_oid_index(&idd->oids[i], *index,
+                                              snmp_agent_table_oid_handler);
+        else
+          ret = snmp_agent_register_oid_string(&idd->oids[i], index_oid,
+                                               snmp_agent_table_oid_handler);
+
+        if (ret != 0) {
+          ERROR(PLUGIN_NAME ": Could not register OID");
+          goto free_index;
+        }
       }
 
-      if (ret != 0)
-        return ret;
+      if (++keys_processed >= td->index_keys_len)
+        break;
     }
   }
 
-  DEBUG(PLUGIN_NAME ": Updated index for '%s' table [%d, %s]", td->name,
-        (index != NULL) ? *index : -1, ins);
+  ret = 0;
 
-  notification_t n = {
-      .severity = NOTIF_OKAY, .time = cdtime(), .plugin = PLUGIN_NAME};
-  sstrncpy(n.host, hostname_g, sizeof(n.host));
-  sstrncpy(n.plugin_instance, ins, sizeof(n.plugin_instance));
-  snprintf(n.message, sizeof(n.message),
-           "Data row added to table %s instance %s index %d", td->name, ins,
-           (index != NULL) ? *index : -1);
-  plugin_dispatch_notification(&n);
+  for (size_t i = 0; i < dd->oids_len; i++) {
+    if (td->index_oid.oid_len)
+      ret = snmp_agent_register_oid_index(&dd->oids[i], *index,
+                                          snmp_agent_table_oid_handler);
+    else
+      ret = snmp_agent_register_oid_string(&dd->oids[i], index_oid,
+                                           snmp_agent_table_oid_handler);
+
+    if (ret < 0)
+      goto free_index;
+    else if (ret == OID_EXISTS)
+      break;
+    else if (snmp_agent_update_instance_oids(td->instance_oids, index_oid, 1) <
+             0)
+      goto free_index;
+  }
+
+  if (ret != OID_EXISTS) {
+    char index_str[DATA_MAX_NAME_LEN];
+
+    if (index == NULL)
+      snmp_agent_oid_to_string(index_str, sizeof(index_str), index_oid);
+    else
+      snprintf(index_str, sizeof(index_str), "%d", *index);
+
+    notification_t n = {
+        .severity = NOTIF_OKAY, .time = cdtime(), .plugin = PLUGIN_NAME};
+    sstrncpy(n.host, hostname_g, sizeof(n.host));
+    snprintf(n.message, sizeof(n.message),
+             "Data added to table %s with index %s", td->name, index_str);
+    DEBUG(PLUGIN_NAME ": %s", n.message);
+
+    plugin_dispatch_notification(&n);
+  }
 
   return 0;
+
+free_value:
+  sfree(value);
+unregister_index:
+  if (td->index_oid.oid_len)
+    snmp_agent_unregister_oid_index(index_oid, *index);
+remove_avl_index:
+  if (td->index_oid.oid_len)
+    c_avl_remove(td->index_instance, index, NULL, NULL);
+remove_avl_index_oid:
+  c_avl_remove(td->instance_index, index_oid, NULL, NULL);
+free_index:
+  if (index != NULL)
+    sfree(index);
+error:
+  *free_index_oid = true;
+
+  return ret;
 }
 
 static int snmp_agent_write(value_list_t const *vl) {
-
   if (vl == NULL)
     return -EINVAL;
 
@@ -1337,11 +2038,27 @@ static int snmp_agent_write(value_list_t const *vl) {
     for (llentry_t *de = llist_head(td->columns); de != NULL; de = de->next) {
       data_definition_t *dd = de->value;
 
-      if (!dd->is_instance) {
+      if (!dd->is_index_key) {
         if (CHECK_DD_TYPE(dd, vl->plugin, vl->plugin_instance, vl->type,
                           vl->type_instance)) {
-          snmp_agent_update_index(td, vl->plugin_instance);
-          return 0;
+          oid_t *index_oid = calloc(1, sizeof(*index_oid));
+          bool free_index_oid = true;
+
+          if (index_oid == NULL) {
+            ERROR(PLUGIN_NAME ": Could not allocate memory for index_oid");
+            return -ENOMEM;
+          }
+
+          int ret = snmp_agent_generate_index(td, vl, index_oid);
+
+          if (ret == 0)
+            ret = snmp_agent_update_index(dd, td, index_oid, &free_index_oid);
+
+          /* Index exists or update failed */
+          if (free_index_oid)
+            sfree(index_oid);
+
+          return ret;
         }
       }
     }
@@ -1363,10 +2080,6 @@ static int snmp_agent_collect(const data_set_t *ds, const value_list_t *vl,
 }
 
 static int snmp_agent_preinit(void) {
-  if (g_agent != NULL) {
-    /* already initialized if config callback was called before init callback */
-    return 0;
-  }
 
   g_agent = calloc(1, sizeof(*g_agent));
   if (g_agent == NULL) {
@@ -1376,22 +2089,26 @@ static int snmp_agent_preinit(void) {
 
   g_agent->tables = llist_create();
   g_agent->scalars = llist_create();
+  g_agent->registered_oids =
+      c_avl_create((int (*)(const void *, const void *))oid_compare);
 
   if (g_agent->tables == NULL || g_agent->scalars == NULL) {
     ERROR(PLUGIN_NAME ": llist_create() failed");
     llist_destroy(g_agent->scalars);
     llist_destroy(g_agent->tables);
+    c_avl_destroy(g_agent->registered_oids);
     return -ENOMEM;
   }
 
   int err;
-  /* make us a agentx client. */
+  /* make us an agentx client. */
   err = netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE,
                                1);
   if (err != 0) {
     ERROR(PLUGIN_NAME ": Failed to set agent role (%d)", err);
     llist_destroy(g_agent->scalars);
     llist_destroy(g_agent->tables);
+    c_avl_destroy(g_agent->registered_oids);
     return -1;
   }
 
@@ -1405,6 +2122,7 @@ static int snmp_agent_preinit(void) {
     ERROR(PLUGIN_NAME ": Failed to initialize the agent library (%d)", err);
     llist_destroy(g_agent->scalars);
     llist_destroy(g_agent->tables);
+    c_avl_destroy(g_agent->registered_oids);
     return -1;
   }
 
@@ -1480,6 +2198,27 @@ static void *snmp_agent_thread_run(void __attribute__((unused)) * arg) {
 
 static int snmp_agent_register_oid(oid_t *oid, Netsnmp_Node_Handler *handler) {
   netsnmp_handler_registration *reg;
+
+  if (c_avl_get(g_agent->registered_oids, (void *)oid, NULL) == 0)
+    return OID_EXISTS;
+  else {
+    oid_t *new_oid = calloc(1, sizeof(*oid));
+
+    if (new_oid == NULL) {
+      ERROR(PLUGIN_NAME ": Could not allocate memory to register new OID");
+      return -ENOMEM;
+    }
+
+    memcpy(new_oid, oid, sizeof(*oid));
+
+    int ret = c_avl_insert(g_agent->registered_oids, (void *)new_oid, NULL);
+    if (ret != 0) {
+      ERROR(PLUGIN_NAME ": Could not allocate memory to register new OID");
+      sfree(new_oid);
+      return -ENOMEM;
+    }
+  }
+
   char *oid_name = snmp_agent_get_oid_name(oid->oid, oid->oid_len - 1);
   char oid_str[DATA_MAX_NAME_LEN];
 
@@ -1554,13 +2293,22 @@ static int snmp_agent_shutdown(void) {
   pthread_mutex_destroy(&g_agent->lock);
   pthread_mutex_destroy(&g_agent->agentx_lock);
 
+  /* Freeing registered OIDs list */
+  void *oid;
+
+  if (g_agent->registered_oids != NULL) {
+    while (c_avl_pick(g_agent->registered_oids, &oid, NULL) == 0) {
+      sfree(oid);
+    }
+    c_avl_destroy(g_agent->registered_oids);
+  }
+
   sfree(g_agent);
 
   return ret;
 }
 
 static int snmp_agent_config(oconfig_item_t *ci) {
-
   int ret = snmp_agent_preinit();
 
   if (ret != 0) {
@@ -1571,7 +2319,7 @@ static int snmp_agent_config(oconfig_item_t *ci) {
   for (int i = 0; i < ci->children_num; i++) {
     oconfig_item_t *child = ci->children + i;
     if (strcasecmp("Data", child->key) == 0) {
-      ret = snmp_agent_config_data(child);
+      ret = snmp_agent_config_scalar(child);
     } else if (strcasecmp("Table", child->key) == 0) {
       ret = snmp_agent_config_table(child);
     } else {
@@ -1588,7 +2336,7 @@ static int snmp_agent_config(oconfig_item_t *ci) {
     }
   }
 
-  ret = snmp_agent_validate_data();
+  ret = snmp_agent_validate_config();
   if (ret != 0) {
     ERROR(PLUGIN_NAME ": Invalid configuration provided");
     snmp_agent_free_config();
diff --git a/src/snmp_agent_test.c b/src/snmp_agent_test.c
new file mode 100644 (file)
index 0000000..581f33d
--- /dev/null
@@ -0,0 +1,831 @@
+/**
+ * collectd - src/snmp_agent_test.c
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Marcin Mozejko <marcinx.mozejko@intel.com>
+ **/
+
+#include "snmp_agent.c"
+#include "testing.h"
+
+#define TEST_HOSTNAME "test_hostname"
+#define TEST_PLUGIN "test_plugin"
+#define TEST_PLUGIN_INST "test_plugin_inst"
+#define TEST_TYPE "test_type"
+#define TEST_TYPE_INST "test_type_inst"
+
+DEF_TEST(oid_to_string) {
+  oid_t o = {.oid = {1, 2, 3, 4, 5, 6, 7, 8, 9}, .oid_len = 9};
+  char oid_str[DATA_MAX_NAME_LEN];
+
+  int ret = snmp_agent_oid_to_string(oid_str, DATA_MAX_NAME_LEN, &o);
+  EXPECT_EQ_INT(o.oid_len * 2 - 1, ret);
+  EXPECT_EQ_STR("1.2.3.4.5.6.7.8.9", oid_str);
+
+  return 0;
+}
+
+/* Testing formatting metric name for simple scalar */
+DEF_TEST(format_name_scalar) {
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+
+  dd->plugin = TEST_PLUGIN;
+  dd->plugin_instance = TEST_PLUGIN_INST;
+  dd->type = TEST_TYPE;
+  dd->type_instance = TEST_TYPE_INST;
+
+  char name[DATA_MAX_NAME_LEN];
+  int ret = snmp_agent_format_name(name, sizeof(name), dd, NULL);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR(
+      "example.com/test_plugin-test_plugin_inst/test_type-test_type_inst",
+      name);
+
+  sfree(dd);
+
+  return 0;
+}
+
+DEF_TEST(format_name_simple_index) {
+  netsnmp_variable_list *index_list_tmp = NULL;
+  oid_t index_oid;
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+  table_definition_t *td = calloc(1, sizeof(*td));
+
+  td->index_list_cont = NULL;
+  td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[0].type = ASN_OCTET_STR;
+  td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[1].type = ASN_OCTET_STR;
+  dd->table = td;
+  dd->plugin = TEST_PLUGIN;
+  dd->type = TEST_TYPE;
+
+  const char plugin_inst[] = TEST_PLUGIN_INST;
+  const char type_inst[] = TEST_TYPE_INST;
+
+  snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+                            (const u_char *)plugin_inst, strlen(plugin_inst));
+  snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+                            (const u_char *)type_inst, strlen(type_inst));
+
+  build_oid_noalloc(index_oid.oid, sizeof(index_oid.oid), &index_oid.oid_len,
+                    NULL, 0, index_list_tmp);
+
+  snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+                            0);
+  snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+                            0);
+
+  char name[DATA_MAX_NAME_LEN];
+
+  int ret = snmp_agent_format_name(name, DATA_MAX_NAME_LEN, dd, &index_oid);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR(
+      "example.com/test_plugin-test_plugin_inst/test_type-test_type_inst",
+      name);
+
+  snmp_free_varbind(index_list_tmp);
+  snmp_free_varbind(td->index_list_cont);
+  sfree(dd);
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(format_name_regex_index) {
+  netsnmp_variable_list *index_list_tmp = NULL;
+  oid_t index_oid;
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+  table_definition_t *td = calloc(1, sizeof(*td));
+
+  td->index_keys_len = 3;
+  td->index_list_cont = NULL;
+  td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[0].type = ASN_OCTET_STR;
+  td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[1].type = ASN_INTEGER;
+  td->index_keys[1].regex = "^vcpu_([0-9]{1,3})-cpu_[0-9]{1,3}$";
+  td->index_keys[1].group = 1;
+  td->index_keys[2].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[2].type = ASN_INTEGER;
+  td->index_keys[2].regex = "^vcpu_[0-9]{1,3}-cpu_([0-9]{1,3})$";
+  td->index_keys[2].group = 1;
+
+  dd->table = td;
+  dd->plugin = TEST_PLUGIN;
+  dd->type = TEST_TYPE;
+
+  const char plugin_inst[] = TEST_PLUGIN_INST;
+  int vcpu = 1;
+  int cpu = 10;
+
+  snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+                            (const u_char *)plugin_inst, strlen(plugin_inst));
+  snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_INTEGER,
+                            (const u_char *)&vcpu, sizeof(vcpu));
+  snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_INTEGER,
+                            (const u_char *)&cpu, sizeof(cpu));
+
+  build_oid_noalloc(index_oid.oid, sizeof(index_oid.oid), &index_oid.oid_len,
+                    NULL, 0, index_list_tmp);
+
+  token_t *token;
+  int *offset;
+
+  td->tokens[INDEX_TYPE_INSTANCE] =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+                            0);
+
+  token = malloc(sizeof(*token));
+  offset = malloc(sizeof(*offset));
+  token->key = snmp_varlist_add_variable(&td->index_list_cont, NULL, 0,
+                                         ASN_INTEGER, NULL, 0);
+  token->str = strdup("vcpu_");
+  *offset = 0;
+  int ret = c_avl_insert(td->tokens[INDEX_TYPE_INSTANCE], (void *)offset,
+                         (void *)token);
+
+  token = malloc(sizeof(*token));
+  offset = malloc(sizeof(*offset));
+  token->key = snmp_varlist_add_variable(&td->index_list_cont, NULL, 0,
+                                         ASN_INTEGER, NULL, 0);
+  token->str = strdup("-cpu_");
+  *offset = 6;
+  ret += c_avl_insert(td->tokens[INDEX_TYPE_INSTANCE], (void *)offset,
+                      (void *)token);
+  char name[DATA_MAX_NAME_LEN];
+
+  ret += snmp_agent_format_name(name, DATA_MAX_NAME_LEN, dd, &index_oid);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR(
+      "example.com/test_plugin-test_plugin_inst/test_type-vcpu_1-cpu_10", name);
+  while (c_avl_pick(td->tokens[INDEX_TYPE_INSTANCE], (void **)&offset,
+                    (void **)&token) == 0) {
+    sfree(offset);
+    sfree(token->str);
+    sfree(token);
+  }
+  c_avl_destroy(td->tokens[INDEX_TYPE_INSTANCE]);
+  snmp_free_varbind(index_list_tmp);
+  snmp_free_varbind(td->index_list_cont);
+  sfree(dd);
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(prep_index_list) {
+  table_definition_t *td = calloc(1, sizeof(*td));
+
+  assert(td != NULL);
+  td->index_keys_len = 5;
+  td->index_keys[0].source = INDEX_HOST;
+  td->index_keys[0].type = ASN_OCTET_STR;
+  td->index_keys[1].source = INDEX_PLUGIN;
+  td->index_keys[1].type = ASN_OCTET_STR;
+  td->index_keys[2].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[2].type = ASN_INTEGER;
+  td->index_keys[3].source = INDEX_TYPE;
+  td->index_keys[3].type = ASN_INTEGER;
+  td->index_keys[4].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[4].type = ASN_OCTET_STR;
+  td->index_list_cont = NULL;
+
+  int ret = snmp_agent_prep_index_list(td, &td->index_list_cont);
+  EXPECT_EQ_INT(0, ret);
+
+  netsnmp_variable_list *key = td->index_list_cont;
+
+  OK(key != NULL);
+  EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(ASN_INTEGER, key->type);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(ASN_INTEGER, key->type);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+  key = key->next_variable;
+  OK(key == NULL);
+
+  snmp_free_varbind(td->index_list_cont);
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(fill_index_list_simple) {
+  table_definition_t *td = calloc(1, sizeof(*td));
+  assert(td != NULL);
+
+  /* Preparing value list */
+  value_list_t *vl = calloc(1, sizeof(*vl));
+  assert(vl != NULL);
+  strncpy(vl->host, TEST_HOSTNAME, DATA_MAX_NAME_LEN);
+  strncpy(vl->plugin, TEST_PLUGIN, DATA_MAX_NAME_LEN);
+  strncpy(vl->plugin_instance, TEST_PLUGIN_INST, DATA_MAX_NAME_LEN);
+  strncpy(vl->type, TEST_TYPE, DATA_MAX_NAME_LEN);
+  strncpy(vl->type_instance, TEST_TYPE_INST, DATA_MAX_NAME_LEN);
+
+  td->index_keys_len = 5;
+  td->index_keys[0].source = INDEX_HOST;
+  td->index_keys[0].type = ASN_OCTET_STR;
+  td->index_keys[1].source = INDEX_PLUGIN;
+  td->index_keys[1].type = ASN_OCTET_STR;
+  td->index_keys[2].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[2].type = ASN_OCTET_STR;
+  td->index_keys[3].source = INDEX_TYPE;
+  td->index_keys[3].type = ASN_OCTET_STR;
+  td->index_keys[4].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[4].type = ASN_OCTET_STR;
+
+  td->index_list_cont = NULL;
+  for (int i = 0; i < td->index_keys_len; i++)
+    snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR,
+                              NULL, 0);
+
+  int ret = snmp_agent_fill_index_list(td, vl);
+  EXPECT_EQ_INT(0, ret);
+
+  netsnmp_variable_list *key = td->index_list_cont;
+
+  ret = 0;
+
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->host, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->plugin, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->plugin_instance, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->type, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->type_instance, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key == NULL);
+
+  snmp_free_varbind(td->index_list_cont);
+  sfree(vl);
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(fill_index_list_regex) {
+  table_definition_t *td = calloc(1, sizeof(*td));
+  int ret = 0;
+
+  assert(td != NULL);
+
+  /* Preparing value list */
+  value_list_t *vl = calloc(1, sizeof(*vl));
+  strncpy(vl->plugin_instance, TEST_PLUGIN_INST, DATA_MAX_NAME_LEN);
+  strncpy(vl->type_instance, "1test2test3", DATA_MAX_NAME_LEN);
+
+  td->index_keys_len = 4;
+  td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[0].type = ASN_OCTET_STR;
+  td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[1].type = ASN_INTEGER;
+  td->index_keys[1].regex = "^([0-9])test[0-9]test[0-9]$";
+  td->index_keys[1].group = 1;
+  td->index_keys[2].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[2].type = ASN_INTEGER;
+  td->index_keys[2].regex = "^[0-9]test([0-9])test[0-9]$";
+  td->index_keys[2].group = 1;
+  td->index_keys[3].source = INDEX_TYPE_INSTANCE;
+  td->index_keys[3].type = ASN_INTEGER;
+  td->index_keys[3].regex = "^[0-9]test[0-9]test([0-9])$";
+  td->index_keys[3].group = 1;
+
+  td->index_list_cont = NULL;
+  snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+                            0);
+  for (int i = 1; i < td->index_keys_len; i++) {
+    snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_INTEGER, NULL,
+                              0);
+    ret = regcomp(&td->index_keys[i].regex_info, td->index_keys[i].regex,
+                  REG_EXTENDED);
+    EXPECT_EQ_INT(0, ret);
+  }
+  td->tokens[INDEX_TYPE_INSTANCE] =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  assert(td->tokens[INDEX_TYPE_INSTANCE] != NULL);
+
+  ret = snmp_agent_fill_index_list(td, vl);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, td->tokens_done);
+
+  netsnmp_variable_list *key = td->index_list_cont;
+
+  OK(key != NULL);
+  EXPECT_EQ_STR(vl->plugin_instance, (char *)key->val.string);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(1, *key->val.integer);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(2, *key->val.integer);
+  key = key->next_variable;
+  OK(key != NULL);
+  EXPECT_EQ_INT(3, *key->val.integer);
+  key = key->next_variable;
+  OK(key == NULL);
+
+  token_t *token;
+  int *offset;
+
+  while (c_avl_pick(td->tokens[INDEX_TYPE_INSTANCE], (void **)&offset,
+                    (void **)&token) == 0) {
+    sfree(offset);
+    sfree(token->str);
+    sfree(token);
+  }
+
+  c_avl_destroy(td->tokens[INDEX_TYPE_INSTANCE]);
+  snmp_free_varbind(td->index_list_cont);
+  sfree(vl);
+
+  for (int i = 0; i < td->index_keys_len; i++) {
+    regfree(&td->index_keys[i].regex_info);
+  }
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(config_index_key_source) {
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+  table_definition_t *td = calloc(1, sizeof(*td));
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+
+  assert(ci != NULL);
+  assert(td != NULL);
+  assert(dd != NULL);
+
+  ci->values = calloc(1, sizeof(*ci->values));
+  assert(ci->values != NULL);
+  ci->values_num = 1;
+  ci->values->value.string = "PluginInstance";
+  ci->values->type = OCONFIG_TYPE_STRING;
+
+  int ret = snmp_agent_config_index_key_source(td, dd, ci);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, td->index_keys_len);
+  EXPECT_EQ_INT(0, dd->index_key_pos);
+  EXPECT_EQ_INT(INDEX_PLUGIN_INSTANCE, td->index_keys[0].source);
+  EXPECT_EQ_INT(GROUP_UNUSED, td->index_keys[0].group);
+  OK(td->index_keys[0].regex == NULL);
+
+  sfree(ci->values);
+  sfree(ci);
+  sfree(td);
+  sfree(dd);
+
+  return 0;
+}
+
+DEF_TEST(config_index_key_regex) {
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+  table_definition_t *td = calloc(1, sizeof(*td));
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+
+  assert(ci != NULL);
+  assert(td != NULL);
+  assert(dd != NULL);
+
+  dd->index_key_pos = 0;
+  td->index_keys_len = 1;
+  td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+  td->index_keys[0].group = 1;
+  ci->values = calloc(1, sizeof(*ci->values));
+  assert(ci->values != NULL);
+  ci->values_num = 1;
+  ci->values->value.string = "^([0-9])test[0-9]test[0-9]$";
+  ci->values->type = OCONFIG_TYPE_STRING;
+
+  int ret = snmp_agent_config_index_key_regex(td, dd, ci);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR(td->index_keys[0].regex, "^([0-9])test[0-9]test[0-9]$");
+  OK(td->tokens[INDEX_PLUGIN_INSTANCE] != NULL);
+
+  c_avl_destroy(td->tokens[INDEX_PLUGIN_INSTANCE]);
+  sfree(ci->values);
+  sfree(ci);
+  sfree(td->index_keys[0].regex);
+  regfree(&td->index_keys[0].regex_info);
+  sfree(td);
+  sfree(dd);
+
+  return 0;
+}
+
+DEF_TEST(config_index_key) {
+  oconfig_item_t *ci = calloc(1, sizeof(*ci));
+  table_definition_t *td = calloc(1, sizeof(*td));
+  data_definition_t *dd = calloc(1, sizeof(*dd));
+
+  assert(ci != NULL);
+  assert(td != NULL);
+  assert(dd != NULL);
+
+  ci->children_num = 3;
+  ci->children = calloc(1, sizeof(*ci->children) * ci->children_num);
+
+  ci->children[0].key = "Source";
+  ci->children[0].parent = ci;
+  ci->children[0].values_num = 1;
+  ci->children[0].values = calloc(1, sizeof(*ci->children[0].values));
+  assert(ci->children[0].values != NULL);
+  ci->children[0].values->value.string = "PluginInstance";
+  ci->children[0].values->type = OCONFIG_TYPE_STRING;
+
+  ci->children[1].key = "Regex";
+  ci->children[1].parent = ci;
+  ci->children[1].values_num = 1;
+  ci->children[1].values = calloc(1, sizeof(*ci->children[0].values));
+  assert(ci->children[1].values != NULL);
+  ci->children[1].values->value.string = "^([0-9])test[0-9]test[0-9]$";
+  ci->children[1].values->type = OCONFIG_TYPE_STRING;
+
+  ci->children[2].key = "Group";
+  ci->children[2].parent = ci;
+  ci->children[2].values_num = 1;
+  ci->children[2].values = calloc(1, sizeof(*ci->children[0].values));
+  assert(ci->children[2].values != NULL);
+  ci->children[2].values->value.number = 1;
+  ci->children[2].values->type = OCONFIG_TYPE_NUMBER;
+
+  int ret = snmp_agent_config_index_key(td, dd, ci);
+
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, td->index_keys_len);
+  EXPECT_EQ_INT(0, dd->index_key_pos);
+  EXPECT_EQ_INT(INDEX_PLUGIN_INSTANCE, td->index_keys[0].source);
+  EXPECT_EQ_INT(1, td->index_keys[0].group);
+  EXPECT_EQ_STR("^([0-9])test[0-9]test[0-9]$", td->index_keys[0].regex);
+  OK(td->tokens[INDEX_PLUGIN_INSTANCE] != NULL);
+
+  sfree(ci->children[0].values);
+  sfree(ci->children[1].values);
+  sfree(ci->children[2].values);
+
+  sfree(ci->children);
+  sfree(ci);
+
+  c_avl_destroy(td->tokens[INDEX_PLUGIN_INSTANCE]);
+  sfree(dd);
+  sfree(td->index_keys[0].regex);
+  regfree(&td->index_keys[0].regex_info);
+  sfree(td);
+
+  return 0;
+}
+
+DEF_TEST(parse_index_key) {
+  const char regex[] = "test-([0-9])-([0-9])";
+  const char input[] = "snmp-test-5-6";
+  regex_t regex_info;
+  regmatch_t match;
+
+  int ret = regcomp(&regex_info, regex, REG_EXTENDED);
+  EXPECT_EQ_INT(0, ret);
+
+  ret = snmp_agent_parse_index_key(input, &regex_info, 0, &match);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(5, match.rm_so);
+  EXPECT_EQ_INT(13, match.rm_eo);
+
+  ret = snmp_agent_parse_index_key(input, &regex_info, 1, &match);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(10, match.rm_so);
+  EXPECT_EQ_INT(11, match.rm_eo);
+
+  ret = snmp_agent_parse_index_key(input, &regex_info, 2, &match);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(12, match.rm_so);
+  EXPECT_EQ_INT(13, match.rm_eo);
+
+  regfree(&regex_info);
+
+  return 0;
+}
+
+DEF_TEST(create_token) {
+  c_avl_tree_t *tokens =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  const char input[] = "testA1-testB2";
+
+  assert(tokens != NULL);
+
+  int ret = snmp_agent_create_token(input, 0, 5, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = snmp_agent_create_token(input, 6, 6, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(2, c_avl_size(tokens));
+
+  token_t *token;
+  int *offset;
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(6, *offset);
+  EXPECT_EQ_STR("-testB", token->str);
+  sfree(offset);
+  sfree(token->str);
+  sfree(token);
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(0, *offset);
+  EXPECT_EQ_STR("testA", token->str);
+  sfree(offset);
+  sfree(token->str);
+  sfree(token);
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  OK(ret != 0);
+
+  c_avl_destroy(tokens);
+
+  return 0;
+}
+
+DEF_TEST(delete_token) {
+  c_avl_tree_t *tokens =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  const char input[] = "testA1-testB2-testC3";
+
+  assert(tokens != NULL);
+
+  int ret = snmp_agent_create_token(input, 0, 5, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = snmp_agent_create_token(input, 6, 6, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = snmp_agent_create_token(input, 13, 6, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(3, c_avl_size(tokens));
+  ret = snmp_agent_delete_token(6, tokens);
+  EXPECT_EQ_INT(0, ret);
+
+  token_t *token;
+  int *offset;
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(0, *offset);
+  EXPECT_EQ_STR("testA", token->str);
+  sfree(offset);
+  sfree(token->str);
+  sfree(token);
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(13, *offset);
+  EXPECT_EQ_STR("-testC", token->str);
+  sfree(offset);
+  sfree(token->str);
+  sfree(token);
+
+  ret = c_avl_pick(tokens, (void **)&offset, (void **)&token);
+  OK(ret != 0);
+
+  c_avl_destroy(tokens);
+
+  return 0;
+}
+
+DEF_TEST(get_token) {
+  c_avl_tree_t *tokens =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  const char input[] = "testA1-testB2-testC3";
+
+  assert(tokens != NULL);
+
+  int ret = snmp_agent_create_token(input, 0, 5, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = snmp_agent_create_token(input, 6, 6, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = snmp_agent_create_token(input, 13, 6, tokens, NULL);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(3, c_avl_size(tokens));
+  ret = snmp_agent_get_token(tokens, 12);
+  EXPECT_EQ_INT(6, ret);
+
+  token_t *token;
+  int *offset;
+
+  while (c_avl_pick(tokens, (void **)&offset, (void **)&token) == 0) {
+    sfree(offset);
+    sfree(token->str);
+    sfree(token);
+  }
+
+  c_avl_destroy(tokens);
+
+  return 0;
+}
+
+DEF_TEST(tokenize) {
+  regmatch_t m[3];
+
+  m[0].rm_so = 5;
+  m[0].rm_eo = 6;
+  m[1].rm_so = 12;
+  m[1].rm_eo = 13;
+  m[2].rm_so = 19;
+  m[2].rm_eo = 20;
+
+  c_avl_tree_t *tokens =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+  const char input[] = "testA1-testB2-testC3";
+  token_t *token;
+  int *offset;
+  c_avl_iterator_t *it;
+  int ret;
+
+  assert(tokens != NULL);
+
+  /* First pass */
+  ret = snmp_agent_tokenize(input, tokens, &m[0], NULL);
+  EXPECT_EQ_INT(0, ret);
+  it = c_avl_get_iterator(tokens);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("testA", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("-testB2-testC3", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  OK(ret != 0);
+  c_avl_iterator_destroy(it);
+
+  /* Second pass */
+  ret = snmp_agent_tokenize(input, tokens, &m[1], NULL);
+  EXPECT_EQ_INT(0, ret);
+  it = c_avl_get_iterator(tokens);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("testA", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("-testB", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("-testC3", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  OK(ret != 0);
+  c_avl_iterator_destroy(it);
+
+  /* Third pass */
+  ret = snmp_agent_tokenize(input, tokens, &m[2], NULL);
+  EXPECT_EQ_INT(0, ret);
+  it = c_avl_get_iterator(tokens);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("testA", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("-testB", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("-testC", token->str);
+  ret = c_avl_iterator_next(it, (void **)&offset, (void **)&token);
+  OK(ret != 0);
+  c_avl_iterator_destroy(it);
+
+  while (c_avl_pick(tokens, (void **)&offset, (void **)&token) == 0) {
+    sfree(offset);
+    sfree(token->str);
+    sfree(token);
+  }
+
+  c_avl_destroy(tokens);
+
+  return 0;
+}
+
+DEF_TEST(build_name) {
+  table_definition_t *td = calloc(1, sizeof(*td));
+  c_avl_tree_t *tokens =
+      c_avl_create((int (*)(const void *, const void *))num_compare);
+
+  assert(tokens != NULL);
+  assert(td != NULL);
+
+  int n[3] = {1, 2, 3};
+  char *t[3] = {"testA", "-testB", "-testC"};
+  int off[3] = {0, 6, 13};
+  token_t *token;
+  int *offset;
+  int ret = 0;
+  char *name = NULL;
+
+  td->index_list_cont = NULL;
+  for (int i = 0; i < 3; i++) {
+    token = malloc(sizeof(*token));
+    token->str = t[i];
+    token->key =
+        snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_INTEGER,
+                                  (const u_char *)&n[i], sizeof(n[i]));
+    assert(token->key != NULL);
+    offset = &off[i];
+    ret = c_avl_insert(tokens, (void *)offset, (void *)token);
+    assert(ret == 0);
+  }
+
+  ret = snmp_agent_build_name(&name, tokens);
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_STR("testA1-testB2-testC3", name);
+
+  while (c_avl_pick(tokens, (void **)&offset, (void **)&token) == 0)
+    sfree(token);
+
+  c_avl_destroy(tokens);
+  snmp_free_varbind(td->index_list_cont);
+  sfree(td);
+  sfree(name);
+  return 0;
+}
+
+int main(void) {
+  /* snmp_agent_oid_to_string */
+  RUN_TEST(oid_to_string);
+
+  /* snmp_agent_prep_index_list */
+  RUN_TEST(prep_index_list);
+
+  /* snmp_agent_fill_index_list */
+  RUN_TEST(fill_index_list_simple);
+  RUN_TEST(fill_index_list_regex);
+
+  /* snmp_agent_format_name */
+  RUN_TEST(format_name_scalar);
+  RUN_TEST(format_name_simple_index);
+  RUN_TEST(format_name_regex_index);
+
+  /* snmp_agent_config_index_key_source */
+  RUN_TEST(config_index_key_source);
+
+  /* snmp_agent_config_index_key_regex */
+  RUN_TEST(config_index_key_regex);
+
+  /* snmp_agent_config_index_key */
+  RUN_TEST(config_index_key);
+
+  /*snmp_agent_parse_index_key */
+  RUN_TEST(parse_index_key);
+
+  /* snmp_agent_create_token */
+  RUN_TEST(create_token);
+
+  /* snmp_agent_delete_token */
+  RUN_TEST(delete_token);
+
+  /* snmp_agent_get_token */
+  RUN_TEST(get_token);
+
+  /* snmp_agent_tokenize */
+  RUN_TEST(tokenize);
+
+  /* snmp_agent_build_name */
+  RUN_TEST(build_name);
+
+  END_TEST;
+}
index 444e8ea..6fbfd18 100644 (file)
@@ -489,8 +489,8 @@ static int statsd_network_init(struct pollfd **ret_fds, /* {{{ */
     int fd;
     struct pollfd *tmp;
 
-    char dbg_node[NI_MAXHOST];
-    char dbg_service[NI_MAXSERV];
+    char str_node[NI_MAXHOST];
+    char str_service[NI_MAXSERV];
 
     fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
     if (fd < 0) {
@@ -498,15 +498,16 @@ static int statsd_network_init(struct pollfd **ret_fds, /* {{{ */
       continue;
     }
 
-    getnameinfo(ai_ptr->ai_addr, ai_ptr->ai_addrlen, dbg_node, sizeof(dbg_node),
-                dbg_service, sizeof(dbg_service),
+    getnameinfo(ai_ptr->ai_addr, ai_ptr->ai_addrlen, str_node, sizeof(str_node),
+                str_service, sizeof(str_service),
                 NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV);
-    DEBUG("statsd plugin: Trying to bind to [%s]:%s ...", dbg_node,
-          dbg_service);
+    DEBUG("statsd plugin: Trying to bind to [%s]:%s ...", str_node,
+          str_service);
 
     status = bind(fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
     if (status != 0) {
-      ERROR("statsd plugin: bind(2) failed: %s", STRERRNO);
+      ERROR("statsd plugin: bind(2) to [%s]:%s failed: %s", str_node,
+            str_service, STRERRNO);
       close(fd);
       continue;
     }
index 189d605..492bea6 100644 (file)
@@ -132,18 +132,6 @@ static size_t tables_num;
 /*
  * configuration handling
  */
-
-static int tbl_config_set_s(char *name, char **var, oconfig_item_t *ci) {
-  if (ci->values_num != 1 || ci->values[0].type != OCONFIG_TYPE_STRING) {
-    log_err("\"%s\" expects a single string argument.", name);
-    return 1;
-  }
-
-  sfree(*var);
-  *var = sstrdup(ci->values[0].value.string);
-  return 0;
-} /* tbl_config_set_separator */
-
 static int tbl_config_append_array_i(char *name, size_t **var, size_t *len,
                                      oconfig_item_t *ci) {
   if (ci->values_num < 1) {
@@ -196,9 +184,9 @@ static int tbl_config_result(tbl_t *tbl, oconfig_item_t *ci) {
     oconfig_item_t *c = ci->children + i;
 
     if (strcasecmp(c->key, "Type") == 0)
-      tbl_config_set_s(c->key, &res->type, c);
+      cf_util_get_string(c, &res->type);
     else if (strcasecmp(c->key, "InstancePrefix") == 0)
-      tbl_config_set_s(c->key, &res->instance_prefix, c);
+      cf_util_get_string(c, &res->instance_prefix);
     else if (strcasecmp(c->key, "InstancesFrom") == 0)
       tbl_config_append_array_i(c->key, &res->instances, &res->instances_num,
                                 c);
@@ -253,11 +241,11 @@ static int tbl_config_table(oconfig_item_t *ci) {
     oconfig_item_t *c = ci->children + i;
 
     if (strcasecmp(c->key, "Separator") == 0)
-      tbl_config_set_s(c->key, &tbl->sep, c);
+      cf_util_get_string(c, &tbl->sep);
     else if (strcasecmp(c->key, "Plugin") == 0)
-      tbl_config_set_s(c->key, &tbl->plugin_name, c);
+      cf_util_get_string(c, &tbl->plugin_name);
     else if (strcasecmp(c->key, "Instance") == 0)
-      tbl_config_set_s(c->key, &tbl->instance, c);
+      cf_util_get_string(c, &tbl->instance);
     else if (strcasecmp(c->key, "Result") == 0)
       tbl_config_result(tbl, c);
     else
index a6471d8..841b331 100644 (file)
@@ -54,14 +54,17 @@ struct ctail_config_match_s {
   int flags;
   char *type;
   char *type_instance;
-  cdtime_t interval;
   latency_config_t latency;
 };
 typedef struct ctail_config_match_s ctail_config_match_t;
 
-static cu_tail_match_t **tail_match_list;
-static size_t tail_match_list_num;
-static cdtime_t tail_match_list_intervals[255];
+static size_t tail_file_num;
+
+static int ctail_read(user_data_t *ud);
+
+static void ctail_match_free(void *arg) {
+  tail_match_destroy((cu_tail_match_t *)arg);
+} /* void ctail_match_free */
 
 static int ctail_config_add_match_dstype(ctail_config_match_t *cm,
                                          oconfig_item_t *ci) {
@@ -136,7 +139,7 @@ static int ctail_config_add_match_dstype(ctail_config_match_t *cm,
 
 static int ctail_config_add_match(cu_tail_match_t *tm, const char *plugin_name,
                                   const char *plugin_instance,
-                                  oconfig_item_t *ci, cdtime_t interval) {
+                                  oconfig_item_t *ci) {
   ctail_config_match_t cm = {0};
   int status;
 
@@ -194,7 +197,7 @@ static int ctail_config_add_match(cu_tail_match_t *tm, const char *plugin_name,
     status = tail_match_add_match_simple(
         tm, cm.regex, cm.excluderegex, cm.flags,
         (plugin_name != NULL) ? plugin_name : "tail", plugin_instance, cm.type,
-        cm.type_instance, cm.latency, interval);
+        cm.type_instance, cm.latency);
 
     if (status != 0)
       ERROR("tail plugin: tail_match_add_match_simple failed.");
@@ -239,8 +242,7 @@ static int ctail_config_add_file(oconfig_item_t *ci) {
     else if (strcasecmp("Interval", option->key) == 0)
       cf_util_get_cdtime(option, &interval);
     else if (strcasecmp("Match", option->key) == 0) {
-      status = ctail_config_add_match(tm, plugin_name, plugin_instance, option,
-                                      interval);
+      status = ctail_config_add_match(tm, plugin_name, plugin_instance, option);
       if (status == 0)
         num_matches++;
       /* Be mild with failed matches.. */
@@ -261,23 +263,15 @@ static int ctail_config_add_file(oconfig_item_t *ci) {
           ci->values[0].value.string);
     tail_match_destroy(tm);
     return -1;
-  } else {
-    cu_tail_match_t **temp;
-
-    temp = realloc(tail_match_list,
-                   sizeof(cu_tail_match_t *) * (tail_match_list_num + 1));
-    if (temp == NULL) {
-      ERROR("tail plugin: realloc failed.");
-      tail_match_destroy(tm);
-      return -1;
-    }
-
-    tail_match_list = temp;
-    tail_match_list[tail_match_list_num] = tm;
-    tail_match_list_intervals[tail_match_list_num] = interval;
-    tail_match_list_num++;
   }
 
+  char str[255];
+  snprintf(str, sizeof(str), "tail-%zu", tail_file_num++);
+
+  plugin_register_complex_read(
+      NULL, str, ctail_read, interval,
+      &(user_data_t){.data = tm, .free_func = ctail_match_free});
+
   return 0;
 } /* int ctail_config_add_file */
 
@@ -307,40 +301,6 @@ static int ctail_read(user_data_t *ud) {
   return 0;
 } /* int ctail_read */
 
-static int ctail_init(void) {
-  char str[255];
-
-  if (tail_match_list_num == 0) {
-    WARNING("tail plugin: File list is empty. Returning an error.");
-    return -1;
-  }
-
-  for (size_t i = 0; i < tail_match_list_num; i++) {
-    snprintf(str, sizeof(str), "tail-%zu", i);
-
-    plugin_register_complex_read(NULL, str, ctail_read,
-                                 tail_match_list_intervals[i],
-                                 &(user_data_t){
-                                     .data = tail_match_list[i],
-                                 });
-  }
-
-  return 0;
-} /* int ctail_init */
-
-static int ctail_shutdown(void) {
-  for (size_t i = 0; i < tail_match_list_num; i++) {
-    tail_match_destroy(tail_match_list[i]);
-    tail_match_list[i] = NULL;
-  }
-  sfree(tail_match_list);
-  tail_match_list_num = 0;
-
-  return 0;
-} /* int ctail_shutdown */
-
 void module_register(void) {
   plugin_register_complex_config("tail", ctail_config);
-  plugin_register_init("tail", ctail_init);
-  plugin_register_shutdown("tail", ctail_shutdown);
 } /* void module_register */
index be7cd40..48f3d74 100644 (file)
@@ -50,7 +50,6 @@ struct instance_definition_s {
   cu_tail_t *tail;
   metric_definition_t **metric_list;
   size_t metric_list_len;
-  cdtime_t interval;
   ssize_t time_from;
   struct instance_definition_s *next;
 };
@@ -77,7 +76,6 @@ static int tcsv_submit(instance_definition_t *id, metric_definition_t *md,
     sstrncpy(vl.type_instance, md->instance, sizeof(vl.type_instance));
 
   vl.time = t;
-  vl.interval = id->interval;
 
   return plugin_dispatch_values(&vl);
 }
@@ -418,6 +416,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) {
   int status = 0;
 
   /* Registration variables */
+  cdtime_t interval = 0;
   char cb_name[DATA_MAX_NAME_LEN];
 
   id = calloc(1, sizeof(*id));
@@ -436,9 +435,6 @@ static int tcsv_config_add_file(oconfig_item_t *ci) {
     return status;
   }
 
-  /* Use default interval. */
-  id->interval = plugin_get_interval();
-
   for (int i = 0; i < ci->children_num; ++i) {
     oconfig_item_t *option = ci->children + i;
     status = 0;
@@ -448,7 +444,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) {
     else if (strcasecmp("Collect", option->key) == 0)
       status = tcsv_config_add_instance_collect(id, option);
     else if (strcasecmp("Interval", option->key) == 0)
-      cf_util_get_cdtime(option, &id->interval);
+      cf_util_get_cdtime(option, &interval);
     else if (strcasecmp("TimeFrom", option->key) == 0)
       status = tcsv_config_get_index(option, &id->time_from);
     else if (strcasecmp("Plugin", option->key) == 0)
@@ -484,7 +480,7 @@ static int tcsv_config_add_file(oconfig_item_t *ci) {
   snprintf(cb_name, sizeof(cb_name), "tail_csv/%s", id->path);
 
   status = plugin_register_complex_read(
-      NULL, cb_name, tcsv_read, id->interval,
+      NULL, cb_name, tcsv_read, interval,
       &(user_data_t){
           .data = id, .free_func = tcsv_instance_definition_destroy,
       });
index 7900133..79300f1 100644 (file)
@@ -109,90 +109,6 @@ static int ut_threshold_add(const threshold_t *th) { /* {{{ */
  * The following approximately two hundred functions are used to handle the
  * configuration and fill the threshold list.
  * {{{ */
-static int ut_config_type_datasource(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("threshold values: The `DataSource' option needs exactly one "
-            "string argument.");
-    return -1;
-  }
-
-  sstrncpy(th->data_source, ci->values[0].value.string,
-           sizeof(th->data_source));
-
-  return 0;
-} /* int ut_config_type_datasource */
-
-static int ut_config_type_instance(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("threshold values: The `Instance' option needs exactly one "
-            "string argument.");
-    return -1;
-  }
-
-  sstrncpy(th->type_instance, ci->values[0].value.string,
-           sizeof(th->type_instance));
-
-  return 0;
-} /* int ut_config_type_instance */
-
-static int ut_config_type_max(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    WARNING("threshold values: The `%s' option needs exactly one "
-            "number argument.",
-            ci->key);
-    return -1;
-  }
-
-  if (strcasecmp(ci->key, "WarningMax") == 0)
-    th->warning_max = ci->values[0].value.number;
-  else
-    th->failure_max = ci->values[0].value.number;
-
-  return 0;
-} /* int ut_config_type_max */
-
-static int ut_config_type_min(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    WARNING("threshold values: The `%s' option needs exactly one "
-            "number argument.",
-            ci->key);
-    return -1;
-  }
-
-  if (strcasecmp(ci->key, "WarningMin") == 0)
-    th->warning_min = ci->values[0].value.number;
-  else
-    th->failure_min = ci->values[0].value.number;
-
-  return 0;
-} /* int ut_config_type_min */
-
-static int ut_config_type_hits(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    WARNING("threshold values: The `%s' option needs exactly one "
-            "number argument.",
-            ci->key);
-    return -1;
-  }
-
-  th->hits = ci->values[0].value.number;
-
-  return 0;
-} /* int ut_config_type_hits */
-
-static int ut_config_type_hysteresis(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    WARNING("threshold values: The `%s' option needs exactly one "
-            "number argument.",
-            ci->key);
-    return -1;
-  }
-
-  th->hysteresis = ci->values[0].value.number;
-
-  return 0;
-} /* int ut_config_type_hysteresis */
-
 static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) {
   threshold_t th;
   int status = 0;
@@ -223,15 +139,19 @@ static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) {
     oconfig_item_t *option = ci->children + i;
 
     if (strcasecmp("Instance", option->key) == 0)
-      status = ut_config_type_instance(&th, option);
+      status = cf_util_get_string_buffer(option, th.type_instance,
+                                         sizeof(th.type_instance));
     else if (strcasecmp("DataSource", option->key) == 0)
-      status = ut_config_type_datasource(&th, option);
-    else if ((strcasecmp("WarningMax", option->key) == 0) ||
-             (strcasecmp("FailureMax", option->key) == 0))
-      status = ut_config_type_max(&th, option);
-    else if ((strcasecmp("WarningMin", option->key) == 0) ||
-             (strcasecmp("FailureMin", option->key) == 0))
-      status = ut_config_type_min(&th, option);
+      status = cf_util_get_string_buffer(option, th.data_source,
+                                         sizeof(th.data_source));
+    else if (strcasecmp("WarningMax", option->key) == 0)
+      status = cf_util_get_double(option, &th.warning_max);
+    else if (strcasecmp("FailureMax", option->key) == 0)
+      status = cf_util_get_double(option, &th.failure_max);
+    else if (strcasecmp("WarningMin", option->key) == 0)
+      status = cf_util_get_double(option, &th.warning_min);
+    else if (strcasecmp("FailureMin", option->key) == 0)
+      status = cf_util_get_double(option, &th.failure_min);
     else if (strcasecmp("Interesting", option->key) == 0)
       status = cf_util_get_flag(option, &th.flags, UT_FLAG_INTERESTING);
     else if (strcasecmp("Invert", option->key) == 0)
@@ -243,9 +163,9 @@ static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) {
     else if (strcasecmp("Percentage", option->key) == 0)
       status = cf_util_get_flag(option, &th.flags, UT_FLAG_PERCENTAGE);
     else if (strcasecmp("Hits", option->key) == 0)
-      status = ut_config_type_hits(&th, option);
+      status = cf_util_get_int(option, &th.hits);
     else if (strcasecmp("Hysteresis", option->key) == 0)
-      status = ut_config_type_hysteresis(&th, option);
+      status = cf_util_get_double(option, &th.hysteresis);
     else {
       WARNING("threshold values: Option `%s' not allowed inside a `Type' "
               "block.",
@@ -264,19 +184,6 @@ static int ut_config_type(const threshold_t *th_orig, oconfig_item_t *ci) {
   return status;
 } /* int ut_config_type */
 
-static int ut_config_plugin_instance(threshold_t *th, oconfig_item_t *ci) {
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("threshold values: The `Instance' option needs exactly one "
-            "string argument.");
-    return -1;
-  }
-
-  sstrncpy(th->plugin_instance, ci->values[0].value.string,
-           sizeof(th->plugin_instance));
-
-  return 0;
-} /* int ut_config_plugin_instance */
-
 static int ut_config_plugin(const threshold_t *th_orig, oconfig_item_t *ci) {
   threshold_t th;
   int status = 0;
@@ -302,7 +209,8 @@ static int ut_config_plugin(const threshold_t *th_orig, oconfig_item_t *ci) {
     if (strcasecmp("Type", option->key) == 0)
       status = ut_config_type(&th, option);
     else if (strcasecmp("Instance", option->key) == 0)
-      status = ut_config_plugin_instance(&th, option);
+      status = cf_util_get_string_buffer(option, th.plugin_instance,
+                                         sizeof(th.plugin_instance));
     else {
       WARNING("threshold values: Option `%s' not allowed inside a `Plugin' "
               "block.",
index 1b1e6f0..f4933ee 100644 (file)
@@ -31,6 +31,7 @@ clock_state             value:GAUGE:0:U
 clock_stratum           value:GAUGE:0:U
 compression             uncompressed:DERIVE:0:U, compressed:DERIVE:0:U
 compression_ratio       value:GAUGE:0:2
+commands                value:DERIVE:0:U
 connections             value:DERIVE:0:U
 conntrack               value:GAUGE:0:4294967295
 contextswitch           value:DERIVE:0:U
@@ -211,6 +212,7 @@ ps_vm                   value:GAUGE:0:9223372036854775807
 pubsub                  value:GAUGE:0:U
 queue_length            value:GAUGE:0:U
 records                 value:GAUGE:0:U
+redis_command_cputime   value:DERIVE:0:U
 requests                value:GAUGE:0:U
 response_code           value:GAUGE:0:U
 response_time           value:GAUGE:0:U
index 61fe2e4..0279a47 100644 (file)
@@ -84,55 +84,29 @@ struct udb_query_preparation_area_s /* {{{ */
   char *plugin;
   char *db_name;
 
-  cdtime_t interval;
-
   udb_result_preparation_area_t *result_prep_areas;
 }; /* }}} */
 
 /*
  * Config Private functions
  */
-static int udb_config_set_string(char **ret_string, /* {{{ */
-                                 oconfig_item_t *ci) {
-  char *string;
-
-  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("db query utils: The `%s' config option "
-            "needs exactly one string argument.",
-            ci->key);
-    return -1;
-  }
-
-  string = strdup(ci->values[0].value.string);
-  if (string == NULL) {
-    ERROR("db query utils: strdup failed.");
-    return -1;
-  }
-
-  if (*ret_string != NULL)
-    free(*ret_string);
-  *ret_string = string;
-
-  return 0;
-} /* }}} int udb_config_set_string */
-
 static int udb_config_add_string(char ***ret_array, /* {{{ */
                                  size_t *ret_array_len, oconfig_item_t *ci) {
   char **array;
   size_t array_len;
 
   if (ci->values_num < 1) {
-    WARNING("db query utils: The `%s' config option "
-            "needs at least one argument.",
-            ci->key);
+    P_WARNING("The `%s' config option "
+              "needs at least one argument.",
+              ci->key);
     return -1;
   }
 
   for (int i = 0; i < ci->values_num; i++) {
     if (ci->values[i].type != OCONFIG_TYPE_STRING) {
-      WARNING("db query utils: Argument %i to the `%s' option "
-              "is not a string.",
-              i + 1, ci->key);
+      P_WARNING("Argument %i to the `%s' option "
+                "is not a string.",
+                i + 1, ci->key);
       return -1;
     }
   }
@@ -140,7 +114,7 @@ static int udb_config_add_string(char ***ret_array, /* {{{ */
   array_len = *ret_array_len;
   array = realloc(*ret_array, sizeof(char *) * (array_len + ci->values_num));
   if (array == NULL) {
-    ERROR("db query utils: realloc failed.");
+    P_ERROR("udb_config_add_string: realloc failed.");
     return -1;
   }
   *ret_array = array;
@@ -148,7 +122,7 @@ static int udb_config_add_string(char ***ret_array, /* {{{ */
   for (int i = 0; i < ci->values_num; i++) {
     array[array_len] = strdup(ci->values[i].value.string);
     if (array[array_len] == NULL) {
-      ERROR("db query utils: strdup failed.");
+      P_ERROR("udb_config_add_string: strdup failed.");
       *ret_array_len = array_len;
       return -1;
     }
@@ -161,18 +135,19 @@ static int udb_config_add_string(char ***ret_array, /* {{{ */
 
 static int udb_config_set_uint(unsigned int *ret_value, /* {{{ */
                                oconfig_item_t *ci) {
-  double tmp;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) {
-    WARNING("db query utils: The `%s' config option "
-            "needs exactly one numeric argument.",
-            ci->key);
+    P_WARNING("The `%s' config option "
+              "needs exactly one numeric argument.",
+              ci->key);
     return -1;
   }
 
-  tmp = ci->values[0].value.number;
-  if ((tmp < 0.0) || (tmp > ((double)UINT_MAX)))
+  double tmp = ci->values[0].value.number;
+  if ((tmp < 0.0) || (tmp > ((double)UINT_MAX))) {
+    P_WARNING("The value given for the `%s` option is out of range.", ci->key);
     return -ERANGE;
+  }
 
   *ret_value = (unsigned int)(tmp + .5);
   return 0;
@@ -194,7 +169,7 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */
 
   vl.values = calloc(r->values_num, sizeof(*vl.values));
   if (vl.values == NULL) {
-    ERROR("db query utils: calloc failed.");
+    P_ERROR("udb_result_submit: calloc failed.");
     return -1;
   }
   vl.values_len = r_area->ds->ds_num;
@@ -203,17 +178,14 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */
     char *value_str = r_area->values_buffer[i];
 
     if (0 != parse_value(value_str, &vl.values[i], r_area->ds->ds[i].type)) {
-      ERROR("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
-            value_str, DS_TYPE_TO_STRING(r_area->ds->ds[i].type));
+      P_ERROR("udb_result_submit: Parsing `%s' as %s failed.", value_str,
+              DS_TYPE_TO_STRING(r_area->ds->ds[i].type));
       errno = EINVAL;
       free(vl.values);
       return -1;
     }
   }
 
-  if (q_area->interval > 0)
-    vl.interval = q_area->interval;
-
   sstrncpy(vl.host, q_area->host, sizeof(vl.host));
   sstrncpy(vl.plugin, q_area->plugin, sizeof(vl.plugin));
   sstrncpy(vl.type, r->type, sizeof(vl.type));
@@ -238,7 +210,7 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */
       int status = strjoin(vl.type_instance, sizeof(vl.type_instance),
                            r_area->instances_buffer, r->instances_num, "-");
       if (status < 0) {
-        ERROR(
+        P_ERROR(
             "udb_result_submit: creating type_instance failed with status %d.",
             status);
         return status;
@@ -249,7 +221,7 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */
       int status = strjoin(tmp, sizeof(tmp), r_area->instances_buffer,
                            r->instances_num, "-");
       if (status < 0) {
-        ERROR(
+        P_ERROR(
             "udb_result_submit: creating type_instance failed with status %d.",
             status);
         return status;
@@ -267,7 +239,7 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */
   if (r->metadata_num > 0) {
     vl.meta = meta_data_create();
     if (vl.meta == NULL) {
-      ERROR("db query utils:: meta_data_create failed.");
+      P_ERROR("udb_result_submit: meta_data_create failed.");
       free(vl.values);
       return -ENOMEM;
     }
@@ -276,7 +248,7 @@ static int udb_result_submit(udb_result_t *r, /* {{{ */
       int status = meta_data_add_string(vl.meta, r->metadata[i],
                                         r_area->metadata_buffer[i]);
       if (status != 0) {
-        ERROR("db query utils:: meta_data_add_string failed.");
+        P_ERROR("udb_result_submit: meta_data_add_string failed.");
         meta_data_destroy(vl.meta);
         vl.meta = NULL;
         free(vl.values);
@@ -355,18 +327,18 @@ static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */
   /* Read `ds' and check number of values {{{ */
   prep_area->ds = plugin_get_ds(r->type);
   if (prep_area->ds == NULL) {
-    ERROR("db query utils: udb_result_prepare_result: Type `%s' is not "
-          "known by the daemon. See types.db(5) for details.",
-          r->type);
+    P_ERROR("udb_result_prepare_result: Type `%s' is not "
+            "known by the daemon. See types.db(5) for details.",
+            r->type);
     BAIL_OUT(-1);
   }
 
   if (prep_area->ds->ds_num != r->values_num) {
-    ERROR("db query utils: udb_result_prepare_result: The type `%s' "
-          "requires exactly %" PRIsz
-          " value%s, but the configuration specifies %" PRIsz ".",
-          r->type, prep_area->ds->ds_num,
-          (prep_area->ds->ds_num == 1) ? "" : "s", r->values_num);
+    P_ERROR("udb_result_prepare_result: The type `%s' "
+            "requires exactly %" PRIsz
+            " value%s, but the configuration specifies %" PRIsz ".",
+            r->type, prep_area->ds->ds_num,
+            (prep_area->ds->ds_num == 1) ? "" : "s", r->values_num);
     BAIL_OUT(-1);
   }
   /* }}} */
@@ -377,39 +349,39 @@ static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */
     prep_area->instances_pos =
         (size_t *)calloc(r->instances_num, sizeof(size_t));
     if (prep_area->instances_pos == NULL) {
-      ERROR("db query utils: udb_result_prepare_result: calloc failed.");
+      P_ERROR("udb_result_prepare_result: calloc failed.");
       BAIL_OUT(-ENOMEM);
     }
 
     prep_area->instances_buffer =
         (char **)calloc(r->instances_num, sizeof(char *));
     if (prep_area->instances_buffer == NULL) {
-      ERROR("db query utils: udb_result_prepare_result: calloc failed.");
+      P_ERROR("udb_result_prepare_result: calloc failed.");
       BAIL_OUT(-ENOMEM);
     }
   } /* if (r->instances_num > 0) */
 
   prep_area->values_pos = (size_t *)calloc(r->values_num, sizeof(size_t));
   if (prep_area->values_pos == NULL) {
-    ERROR("db query utils: udb_result_prepare_result: calloc failed.");
+    P_ERROR("udb_result_prepare_result: calloc failed.");
     BAIL_OUT(-ENOMEM);
   }
 
   prep_area->values_buffer = (char **)calloc(r->values_num, sizeof(char *));
   if (prep_area->values_buffer == NULL) {
-    ERROR("db query utils: udb_result_prepare_result: calloc failed.");
+    P_ERROR("udb_result_prepare_result: calloc failed.");
     BAIL_OUT(-ENOMEM);
   }
 
   prep_area->metadata_pos = (size_t *)calloc(r->metadata_num, sizeof(size_t));
   if (prep_area->metadata_pos == NULL) {
-    ERROR("db query utils: udb_result_prepare_result: calloc failed.");
+    P_ERROR("udb_result_prepare_result: calloc failed.");
     BAIL_OUT(-ENOMEM);
   }
 
   prep_area->metadata_buffer = (char **)calloc(r->metadata_num, sizeof(char *));
   if (prep_area->metadata_buffer == NULL) {
-    ERROR("db query utils: udb_result_prepare_result: calloc failed.");
+    P_ERROR("udb_result_prepare_result: calloc failed.");
     BAIL_OUT(-ENOMEM);
   }
 
@@ -427,9 +399,9 @@ static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */
     }
 
     if (j >= column_num) {
-      ERROR("db query utils: udb_result_prepare_result: "
-            "Column `%s' could not be found.",
-            r->instances[i]);
+      P_ERROR("udb_result_prepare_result: "
+              "Column `%s' could not be found.",
+              r->instances[i]);
       BAIL_OUT(-ENOENT);
     }
   } /* }}} for (i = 0; i < r->instances_num; i++) */
@@ -446,9 +418,9 @@ static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */
     }
 
     if (j >= column_num) {
-      ERROR("db query utils: udb_result_prepare_result: "
-            "Column `%s' could not be found.",
-            r->values[i]);
+      P_ERROR("udb_result_prepare_result: "
+              "Column `%s' could not be found.",
+              r->values[i]);
       BAIL_OUT(-ENOENT);
     }
   } /* }}} for (i = 0; i < r->values_num; i++) */
@@ -465,9 +437,9 @@ static int udb_result_prepare_result(udb_result_t const *r, /* {{{ */
     }
 
     if (j >= column_num) {
-      ERROR("db query utils: udb_result_prepare_result: "
-            "Metadata column `%s' could not be found.",
-            r->values[i]);
+      P_ERROR("udb_result_prepare_result: "
+              "Metadata column `%s' could not be found.",
+              r->values[i]);
       BAIL_OUT(-ENOENT);
     }
   } /* }}} for (i = 0; i < r->metadata_num; i++) */
@@ -507,14 +479,14 @@ static int udb_result_create(const char *query_name, /* {{{ */
   int status;
 
   if (ci->values_num != 0) {
-    WARNING("db query utils: The `Result' block doesn't accept "
-            "any arguments. Ignoring %i argument%s.",
-            ci->values_num, (ci->values_num == 1) ? "" : "s");
+    P_WARNING("The `Result' block doesn't accept "
+              "any arguments. Ignoring %i argument%s.",
+              ci->values_num, (ci->values_num == 1) ? "" : "s");
   }
 
   r = calloc(1, sizeof(*r));
   if (r == NULL) {
-    ERROR("db query utils: calloc failed.");
+    P_ERROR("udb_result_create: calloc failed.");
     return -1;
   }
   r->type = NULL;
@@ -530,9 +502,9 @@ static int udb_result_create(const char *query_name, /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("Type", child->key) == 0)
-      status = udb_config_set_string(&r->type, child);
+      status = cf_util_get_string(child, &r->type);
     else if (strcasecmp("InstancePrefix", child->key) == 0)
-      status = udb_config_set_string(&r->instance_prefix, child);
+      status = cf_util_get_string(child, &r->instance_prefix);
     else if (strcasecmp("InstancesFrom", child->key) == 0)
       status = udb_config_add_string(&r->instances, &r->instances_num, child);
     else if (strcasecmp("ValuesFrom", child->key) == 0)
@@ -540,8 +512,8 @@ static int udb_result_create(const char *query_name, /* {{{ */
     else if (strcasecmp("MetadataFrom", child->key) == 0)
       status = udb_config_add_string(&r->metadata, &r->metadata_num, child);
     else {
-      WARNING("db query utils: Query `%s': Option `%s' not allowed here.",
-              query_name, child->key);
+      P_WARNING("Query `%s': Option `%s' not allowed here.", query_name,
+                child->key);
       status = -1;
     }
 
@@ -552,15 +524,15 @@ static int udb_result_create(const char *query_name, /* {{{ */
   /* Check that all necessary options have been given. */
   while (status == 0) {
     if (r->type == NULL) {
-      WARNING("db query utils: `Type' not given for "
-              "result in query `%s'",
-              query_name);
+      P_WARNING("udb_result_create: `Type' not given for "
+                "result in query `%s'",
+                query_name);
       status = -1;
     }
     if (r->values == NULL) {
-      WARNING("db query utils: `ValuesFrom' not given for "
-              "result in query `%s'",
-              query_name);
+      P_WARNING("udb_result_create: `ValuesFrom' not given for "
+                "result in query `%s'",
+                query_name);
       status = -1;
     }
 
@@ -623,14 +595,14 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */
   query_list_len = *ret_query_list_len;
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    WARNING("db query utils: The `Query' block "
-            "needs exactly one string argument.");
+    P_WARNING("udb_result_create: The `Query' block "
+              "needs exactly one string argument.");
     return -1;
   }
 
   q = calloc(1, sizeof(*q));
   if (q == NULL) {
-    ERROR("db query utils: calloc failed.");
+    P_ERROR("udb_query_create: calloc failed.");
     return -1;
   }
   q->min_version = 0;
@@ -639,7 +611,7 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */
   q->results = NULL;
   q->plugin_instance_from = NULL;
 
-  status = udb_config_set_string(&q->name, ci);
+  status = cf_util_get_string(ci, &q->name);
   if (status != 0) {
     sfree(q);
     return status;
@@ -650,7 +622,7 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */
     oconfig_item_t *child = ci->children + i;
 
     if (strcasecmp("Statement", child->key) == 0)
-      status = udb_config_set_string(&q->statement, child);
+      status = cf_util_get_string(child, &q->statement);
     else if (strcasecmp("Result", child->key) == 0)
       status = udb_result_create(q->name, &q->results, child);
     else if (strcasecmp("MinVersion", child->key) == 0)
@@ -658,19 +630,19 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */
     else if (strcasecmp("MaxVersion", child->key) == 0)
       status = udb_config_set_uint(&q->max_version, child);
     else if (strcasecmp("PluginInstanceFrom", child->key) == 0)
-      status = udb_config_set_string(&q->plugin_instance_from, child);
+      status = cf_util_get_string(child, &q->plugin_instance_from);
 
     /* Call custom callbacks */
     else if (cb != NULL) {
       status = (*cb)(q, child);
       if (status != 0) {
-        WARNING("db query utils: The configuration callback failed "
-                "to handle `%s'.",
-                child->key);
+        P_WARNING("The configuration callback failed "
+                  "to handle `%s'.",
+                  child->key);
       }
     } else {
-      WARNING("db query utils: Query `%s': Option `%s' not allowed here.",
-              q->name, child->key);
+      P_WARNING("Query `%s': Option `%s' not allowed here.", q->name,
+                child->key);
       status = -1;
     }
 
@@ -681,12 +653,11 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */
   /* Check that all necessary options have been given. */
   if (status == 0) {
     if (q->statement == NULL) {
-      WARNING("db query utils: Query `%s': No `Statement' given.", q->name);
+      P_WARNING("Query `%s': No `Statement' given.", q->name);
       status = -1;
     }
     if (q->results == NULL) {
-      WARNING("db query utils: Query `%s': No (valid) `Result' block given.",
-              q->name);
+      P_WARNING("Query `%s': No (valid) `Result' block given.", q->name);
       status = -1;
     }
   } /* if (status == 0) */
@@ -698,7 +669,7 @@ int udb_query_create(udb_query_t ***ret_query_list, /* {{{ */
 
     temp = realloc(query_list, sizeof(*query_list) * (query_list_len + 1));
     if (temp == NULL) {
-      ERROR("db query utils: realloc failed");
+      P_ERROR("udb_query_create: realloc failed");
       status = -1;
     } else {
       query_list = temp;
@@ -738,8 +709,8 @@ int udb_query_pick_from_list_by_name(const char *name, /* {{{ */
 
   if ((name == NULL) || (src_list == NULL) || (dst_list == NULL) ||
       (dst_list_len == NULL)) {
-    ERROR("db query utils: udb_query_pick_from_list_by_name: "
-          "Invalid argument.");
+    P_ERROR("udb_query_pick_from_list_by_name: "
+            "Invalid argument.");
     return -EINVAL;
   }
 
@@ -754,7 +725,7 @@ int udb_query_pick_from_list_by_name(const char *name, /* {{{ */
     tmp_list_len = *dst_list_len;
     tmp_list = realloc(*dst_list, (tmp_list_len + 1) * sizeof(udb_query_t *));
     if (tmp_list == NULL) {
-      ERROR("db query utils: realloc failed.");
+      P_ERROR("udb_query_pick_from_list_by_name: realloc failed.");
       return -ENOMEM;
     }
 
@@ -768,12 +739,12 @@ int udb_query_pick_from_list_by_name(const char *name, /* {{{ */
   } /* for (i = 0; i < src_list_len; i++) */
 
   if (num_added <= 0) {
-    ERROR("db query utils: Cannot find query `%s'. Make sure the <Query> "
-          "block is above the database definition!",
-          name);
+    P_ERROR("Cannot find query `%s'. Make sure the <Query> "
+            "block is above the database definition!",
+            name);
     return -ENOENT;
   } else {
-    DEBUG("db query utils: Added %i versions of query `%s'.", num_added, name);
+    DEBUG("Added %i versions of query `%s'.", num_added, name);
   }
 
   return 0;
@@ -786,15 +757,15 @@ int udb_query_pick_from_list(oconfig_item_t *ci, /* {{{ */
 
   if ((ci == NULL) || (src_list == NULL) || (dst_list == NULL) ||
       (dst_list_len == NULL)) {
-    ERROR("db query utils: udb_query_pick_from_list: "
-          "Invalid argument.");
+    P_ERROR("udb_query_pick_from_list: "
+            "Invalid argument.");
     return -EINVAL;
   }
 
   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
-    ERROR("db query utils: The `%s' config option "
-          "needs exactly one string argument.",
-          ci->key);
+    P_ERROR("The `%s' config option "
+            "needs exactly one string argument.",
+            ci->key);
     return -1;
   }
   name = ci->values[0].value.string;
@@ -859,8 +830,6 @@ void udb_query_finish_result(udb_query_t const *q, /* {{{ */
   sfree(prep_area->plugin);
   sfree(prep_area->db_name);
 
-  prep_area->interval = 0;
-
   for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
        r = r->next, r_area = r_area->next) {
     /* this may happen during error conditions of the caller */
@@ -883,16 +852,16 @@ int udb_query_handle_result(udb_query_t const *q, /* {{{ */
 
   if ((prep_area->column_num < 1) || (prep_area->host == NULL) ||
       (prep_area->plugin == NULL) || (prep_area->db_name == NULL)) {
-    ERROR("db query utils: Query `%s': Query is not prepared; "
-          "can't handle result.",
-          q->name);
+    P_ERROR("Query `%s': Query is not prepared; "
+            "can't handle result.",
+            q->name);
     return -EINVAL;
   }
 
 #if defined(COLLECT_DEBUG) && COLLECT_DEBUG /* {{{ */
   do {
     for (size_t i = 0; i < prep_area->column_num; i++) {
-      DEBUG("db query utils: udb_query_handle_result (%s, %s): "
+      DEBUG("udb_query_handle_result (%s, %s): "
             "column[%" PRIsz "] = %s;",
             prep_area->db_name, q->name, i, column_values[i]);
     }
@@ -908,9 +877,9 @@ int udb_query_handle_result(udb_query_t const *q, /* {{{ */
   }
 
   if (success == 0) {
-    ERROR("db query utils: udb_query_handle_result (%s, %s): "
-          "All results failed.",
-          prep_area->db_name, q->name);
+    P_ERROR("udb_query_handle_result (%s, %s): "
+            "All results failed.",
+            prep_area->db_name, q->name);
     return -1;
   }
 
@@ -921,7 +890,7 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */
                              udb_query_preparation_area_t *prep_area,
                              const char *host, const char *plugin,
                              const char *db_name, char **column_names,
-                             size_t column_num, cdtime_t interval) {
+                             size_t column_num) {
   udb_result_preparation_area_t *r_area;
   udb_result_t *r;
   int status;
@@ -934,7 +903,6 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */
   assert(prep_area->host == NULL);
   assert(prep_area->plugin == NULL);
   assert(prep_area->db_name == NULL);
-  assert(prep_area->interval == 0);
 #endif
 
   prep_area->column_num = column_num;
@@ -942,12 +910,9 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */
   prep_area->plugin = strdup(plugin);
   prep_area->db_name = strdup(db_name);
 
-  prep_area->interval = interval;
-
   if ((prep_area->host == NULL) || (prep_area->plugin == NULL) ||
       (prep_area->db_name == NULL)) {
-    ERROR("db query utils: Query `%s': Prepare failed: Out of memory.",
-          q->name);
+    P_ERROR("Query `%s': Prepare failed: Out of memory.", q->name);
     udb_query_finish_result(q, prep_area);
     return -ENOMEM;
   }
@@ -955,7 +920,7 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */
 #if defined(COLLECT_DEBUG) && COLLECT_DEBUG
   do {
     for (size_t i = 0; i < column_num; i++) {
-      DEBUG("db query utils: udb_query_prepare_result: "
+      DEBUG("udb_query_prepare_result: "
             "query = %s; column[%" PRIsz "] = %s;",
             q->name, i, column_names[i]);
     }
@@ -974,9 +939,9 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */
     }
 
     if (i >= column_num) {
-      ERROR("db query utils: udb_query_prepare_result: "
-            "Column `%s' from `PluginInstanceFrom' could not be found.",
-            q->plugin_instance_from);
+      P_ERROR("udb_query_prepare_result: "
+              "Column `%s' from `PluginInstanceFrom' could not be found.",
+              q->plugin_instance_from);
       udb_query_finish_result(q, prep_area);
       return -ENOENT;
     }
@@ -986,9 +951,9 @@ int udb_query_prepare_result(udb_query_t const *q, /* {{{ */
   for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
        r = r->next, r_area = r_area->next) {
     if (!r_area) {
-      ERROR("db query utils: Query `%s': Invalid number of result "
-            "preparation areas.",
-            q->name);
+      P_ERROR("Query `%s': Invalid number of result "
+              "preparation areas.",
+              q->name);
       udb_query_finish_result(q, prep_area);
       return -EINVAL;
     }
index 4d6129a..f173204 100644 (file)
@@ -71,7 +71,7 @@ int udb_query_prepare_result(udb_query_t const *q,
                              udb_query_preparation_area_t *prep_area,
                              const char *host, const char *plugin,
                              const char *db_name, char **column_names,
-                             size_t column_num, cdtime_t interval);
+                             size_t column_num);
 int udb_query_handle_result(udb_query_t const *q,
                             udb_query_preparation_area_t *prep_area,
                             char **column_values);
index 1d4668f..aee9791 100644 (file)
@@ -853,7 +853,11 @@ uint128_t str_to_uint128(const char *str, int len) {
 }
 
 uint8_t dpdk_helper_eth_dev_count(void) {
+#if RTE_VERSION < RTE_VERSION_NUM(18, 05, 0, 0)
   uint8_t ports = rte_eth_dev_count();
+#else
+  uint8_t ports = rte_eth_dev_count_avail();
+#endif
   if (ports == 0) {
     ERROR(
         "%s:%d: No DPDK ports available. Check bound devices to DPDK driver.\n",
index 0bc802b..de3f0c2 100644 (file)
@@ -66,8 +66,8 @@ static int gr_format_values(char *ret, size_t ret_len, int ds_num,
   else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE)
     BUFFER_ADD("%" PRIu64, vl->values[ds_num].absolute);
   else {
-    ERROR("gr_format_values plugin: Unknown data source type: %i",
-          ds->ds[ds_num].type);
+    P_ERROR("gr_format_values: Unknown data source type: %i",
+            ds->ds[ds_num].type);
     return -1;
   }
 
@@ -97,6 +97,86 @@ static void gr_copy_escape_part(char *dst, const char *src, size_t dst_len,
   }
 }
 
+static int gr_format_name_tagged(char *ret, int ret_len, value_list_t const *vl,
+                                 char const *ds_name, char const *prefix,
+                                 char const *postfix, char const escape_char,
+                                 unsigned int flags) {
+  char n_host[DATA_MAX_NAME_LEN];
+  char n_plugin[DATA_MAX_NAME_LEN];
+  char n_plugin_instance[DATA_MAX_NAME_LEN];
+  char n_type[DATA_MAX_NAME_LEN];
+  char n_type_instance[DATA_MAX_NAME_LEN];
+
+  char tmp_plugin[DATA_MAX_NAME_LEN + 8];
+  char tmp_plugin_instance[DATA_MAX_NAME_LEN + 17];
+  char tmp_type[DATA_MAX_NAME_LEN + 6];
+  char tmp_type_instance[DATA_MAX_NAME_LEN + 15];
+  char tmp_metric[3 * DATA_MAX_NAME_LEN + 2];
+  char tmp_ds_name[DATA_MAX_NAME_LEN + 9];
+
+  if (prefix == NULL)
+    prefix = "";
+
+  if (postfix == NULL)
+    postfix = "";
+
+  gr_copy_escape_part(n_host, vl->host, sizeof(n_host), escape_char, 1);
+  gr_copy_escape_part(n_plugin, vl->plugin, sizeof(n_plugin), escape_char, 1);
+  gr_copy_escape_part(n_plugin_instance, vl->plugin_instance,
+                      sizeof(n_plugin_instance), escape_char, 1);
+  gr_copy_escape_part(n_type, vl->type, sizeof(n_type), escape_char, 1);
+  gr_copy_escape_part(n_type_instance, vl->type_instance,
+                      sizeof(n_type_instance), escape_char, 1);
+
+  snprintf(tmp_plugin, sizeof(tmp_plugin), ";plugin=%s", n_plugin);
+
+  if (n_plugin_instance[0] != '\0')
+    snprintf(tmp_plugin_instance, sizeof(tmp_plugin_instance),
+             ";plugin_instance=%s", n_plugin_instance);
+  else
+    tmp_plugin_instance[0] = '\0';
+
+  if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) || strcmp(n_plugin, n_type) != 0)
+    snprintf(tmp_type, sizeof(tmp_type), ";type=%s", n_type);
+  else
+    tmp_type[0] = '\0';
+
+  if (n_type_instance[0] != '\0') {
+    if (!(flags & GRAPHITE_DROP_DUPE_FIELDS) ||
+        strcmp(n_plugin_instance, n_type_instance) != 0)
+      snprintf(tmp_type_instance, sizeof(tmp_type_instance),
+               ";type_instance=%s", n_type_instance);
+    else
+      tmp_type_instance[0] = '\0';
+  } else
+    tmp_type_instance[0] = '\0';
+
+  /* Assert always_append_ds -> ds_name */
+  assert(!(flags & GRAPHITE_ALWAYS_APPEND_DS) || (ds_name != NULL));
+  if (ds_name != NULL) {
+    snprintf(tmp_ds_name, sizeof(tmp_ds_name), ";ds_name=%s", ds_name);
+
+    if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, ds_name);
+    else
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s.%s", n_plugin, n_type,
+               ds_name);
+  } else {
+    tmp_ds_name[0] = '\0';
+
+    if ((flags & GRAPHITE_DROP_DUPE_FIELDS) && strcmp(n_plugin, n_type) == 0)
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s", n_plugin);
+    else
+      snprintf(tmp_metric, sizeof(tmp_metric), "%s.%s", n_plugin, n_type);
+  }
+
+  snprintf(ret, ret_len, "%s%s%s;host=%s%s%s%s%s%s", prefix, tmp_metric,
+           postfix, n_host, tmp_plugin, tmp_plugin_instance, tmp_type,
+           tmp_type_instance, tmp_ds_name);
+
+  return 0;
+}
+
 static int gr_format_name(char *ret, int ret_len, value_list_t const *vl,
                           char const *ds_name, char const *prefix,
                           char const *postfix, char const escape_char,
@@ -183,7 +263,7 @@ int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds,
   if (flags & GRAPHITE_STORE_RATES) {
     rates = uc_get_rate(ds, vl);
     if (rates == NULL) {
-      ERROR("format_graphite: error with uc_get_rate");
+      P_ERROR("format_graphite: error with uc_get_rate");
       return -1;
     }
   }
@@ -199,20 +279,31 @@ int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds,
       ds_name = ds->ds[i].name;
 
     /* Copy the identifier to `key' and escape it. */
-    status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix,
-                            escape_char, flags);
-    if (status != 0) {
-      ERROR("format_graphite: error with gr_format_name");
-      sfree(rates);
-      return status;
+    if (flags & GRAPHITE_USE_TAGS) {
+      status = gr_format_name_tagged(key, sizeof(key), vl, ds_name, prefix,
+                                     postfix, escape_char, flags);
+      if (status != 0) {
+        P_ERROR("format_graphite: error with gr_format_name_tagged");
+        sfree(rates);
+        return status;
+      }
+    } else {
+      status = gr_format_name(key, sizeof(key), vl, ds_name, prefix, postfix,
+                              escape_char, flags);
+      if (status != 0) {
+        P_ERROR("format_graphite: error with gr_format_name");
+        sfree(rates);
+        return status;
+      }
     }
 
     escape_graphite_string(key, escape_char);
+
     /* Convert the values to an ASCII representation and put that into
      * `values'. */
     status = gr_format_values(values, sizeof(values), i, ds, vl, rates);
     if (status != 0) {
-      ERROR("format_graphite: error with gr_format_values");
+      P_ERROR("format_graphite: error with gr_format_values");
       sfree(rates);
       return status;
     }
@@ -222,16 +313,16 @@ int format_graphite(char *buffer, size_t buffer_size, data_set_t const *ds,
         (size_t)snprintf(message, sizeof(message), "%s %s %u\r\n", key, values,
                          (unsigned int)CDTIME_T_TO_TIME_T(vl->time));
     if (message_len >= sizeof(message)) {
-      ERROR("format_graphite: message buffer too small: "
-            "Need %" PRIsz " bytes.",
-            message_len + 1);
+      P_ERROR("format_graphite: message buffer too small: "
+              "Need %" PRIsz " bytes.",
+              message_len + 1);
       sfree(rates);
       return -ENOMEM;
     }
 
     /* Append it in case we got multiple data set */
     if ((buffer_pos + message_len) >= buffer_size) {
-      ERROR("format_graphite: target buffer too small");
+      P_ERROR("format_graphite: target buffer too small");
       sfree(rates);
       return -ENOMEM;
     }
index de90c44..60b89ae 100644 (file)
@@ -31,6 +31,7 @@
 #define GRAPHITE_ALWAYS_APPEND_DS 0x04
 #define GRAPHITE_DROP_DUPE_FIELDS 0x08
 #define GRAPHITE_PRESERVE_SEPARATOR 0x10
+#define GRAPHITE_USE_TAGS 0x20
 
 int format_graphite(char *buffer, size_t buffer_size, const data_set_t *ds,
                     const value_list_t *vl, const char *prefix,
index a82142f..42efa68 100644 (file)
@@ -124,6 +124,22 @@ DEF_TEST(metric_name) {
           .suffix = NULL,
           .want_name = "foo.example@com.test.single",
       },
+      /* flag GRAPHITE_USE_TAGS */
+      {.flags = GRAPHITE_USE_TAGS,
+       .want_name = "test.single;host=example.com;plugin=test;type=single"},
+      {.plugin_instance = "f.o.o",
+       .type_instance = "b.a.r",
+       .flags = GRAPHITE_USE_TAGS,
+       .want_name = "test.single;host=example.com;plugin=test;plugin_instance="
+                    "f.o.o;type=single;type_instance=b.a.r"},
+      {.flags = GRAPHITE_USE_TAGS ^ GRAPHITE_ALWAYS_APPEND_DS,
+       .want_name = "test.single.value;host=example.com;plugin=test;type="
+                    "single;ds_name=value"},
+      {.plugin_instance = "foo",
+       .type_instance = "foo",
+       .flags = GRAPHITE_USE_TAGS ^ GRAPHITE_DROP_DUPE_FIELDS,
+       .want_name = "test.single;host=example.com;plugin=test;plugin_instance="
+                    "foo;type=single"},
   };
 
   for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
index ce7838d..8f92cfd 100644 (file)
@@ -96,7 +96,7 @@ static srrd_create_args_t *srrd_create_args_create(const char *filename,
 
   args = calloc(1, sizeof(*args));
   if (args == NULL) {
-    ERROR("srrd_create_args_create: calloc failed.");
+    P_ERROR("srrd_create_args_create: calloc failed.");
     return NULL;
   }
   args->filename = NULL;
@@ -106,14 +106,14 @@ static srrd_create_args_t *srrd_create_args_create(const char *filename,
 
   args->filename = strdup(filename);
   if (args->filename == NULL) {
-    ERROR("srrd_create_args_create: strdup failed.");
+    P_ERROR("srrd_create_args_create: strdup failed.");
     srrd_create_args_destroy(args);
     return NULL;
   }
 
   args->argv = calloc((size_t)(argc + 1), sizeof(*args->argv));
   if (args->argv == NULL) {
-    ERROR("srrd_create_args_create: calloc failed.");
+    P_ERROR("srrd_create_args_create: calloc failed.");
     srrd_create_args_destroy(args);
     return NULL;
   }
@@ -121,7 +121,7 @@ static srrd_create_args_t *srrd_create_args_create(const char *filename,
   for (args->argc = 0; args->argc < argc; args->argc++) {
     args->argv[args->argc] = strdup(argv[args->argc]);
     if (args->argv[args->argc] == NULL) {
-      ERROR("srrd_create_args_create: strdup failed.");
+      P_ERROR("srrd_create_args_create: strdup failed.");
       srrd_create_args_destroy(args);
       return NULL;
     }
@@ -212,7 +212,7 @@ static int rra_get(char ***ret, const value_list_t *vl, /* {{{ */
                         rra_types[j], cfg->xff, cdp_len, cdp_num);
 
       if ((status < 0) || ((size_t)status >= sizeof(buffer))) {
-        ERROR("rra_get: Buffer would have been truncated.");
+        P_ERROR("rra_get: Buffer would have been truncated.");
         continue;
       }
 
@@ -251,7 +251,7 @@ static int ds_get(char ***ret, /* {{{ */
 
   ds_def = calloc(ds->ds_num, sizeof(*ds_def));
   if (ds_def == NULL) {
-    ERROR("rrdtool plugin: calloc failed: %s", STRERRNO);
+    P_ERROR("ds_get: calloc failed: %s", STRERRNO);
     return -1;
   }
 
@@ -271,7 +271,7 @@ static int ds_get(char ***ret, /* {{{ */
     else if (d->type == DS_TYPE_ABSOLUTE)
       type = "ABSOLUTE";
     else {
-      ERROR("rrdtool plugin: Unknown DS type: %i", d->type);
+      P_ERROR("ds_get: Unknown DS type: %i", d->type);
       break;
     }
 
@@ -335,8 +335,8 @@ static int srrd_create(const char *filename, /* {{{ */
   status = rrd_create_r(filename_copy, pdp_step, last_up, argc, (void *)argv);
 
   if (status != 0) {
-    WARNING("rrdtool plugin: rrd_create_r (%s) failed: %s", filename,
-            rrd_get_error());
+    P_WARNING("srrd_create: rrd_create_r (%s) failed: %s", filename,
+              rrd_get_error());
   }
 
   sfree(filename_copy);
@@ -360,7 +360,7 @@ static int srrd_create(const char *filename, /* {{{ */
   new_argc = 6 + argc;
   new_argv = malloc((new_argc + 1) * sizeof(*new_argv));
   if (new_argv == NULL) {
-    ERROR("rrdtool plugin: malloc failed.");
+    P_ERROR("srrd_create: malloc failed.");
     return -1;
   }
 
@@ -388,8 +388,8 @@ static int srrd_create(const char *filename, /* {{{ */
   pthread_mutex_unlock(&librrd_lock);
 
   if (status != 0) {
-    WARNING("rrdtool plugin: rrd_create (%s) failed: %s", filename,
-            rrd_get_error());
+    P_WARNING("srrd_create: rrd_create (%s) failed: %s", filename,
+              rrd_get_error());
   }
 
   sfree(new_argv);
@@ -487,10 +487,11 @@ static void *srrd_create_thread(void *targs) /* {{{ */
   status = lock_file(args->filename);
   if (status != 0) {
     if (status == EEXIST)
-      NOTICE("srrd_create_thread: File \"%s\" is already being created.",
-             args->filename);
+      P_NOTICE("srrd_create_thread: File \"%s\" is already being created.",
+               args->filename);
     else
-      ERROR("srrd_create_thread: Unable to lock file \"%s\".", args->filename);
+      P_ERROR("srrd_create_thread: Unable to lock file \"%s\".",
+              args->filename);
     srrd_create_args_destroy(args);
     return 0;
   }
@@ -500,8 +501,8 @@ static void *srrd_create_thread(void *targs) /* {{{ */
   status = srrd_create(tmpfile, args->pdp_step, args->last_up, args->argc,
                        (void *)args->argv);
   if (status != 0) {
-    WARNING("srrd_create_thread: srrd_create (%s) returned status %i.",
-            args->filename, status);
+    P_WARNING("srrd_create_thread: srrd_create (%s) returned status %i.",
+              args->filename, status);
     unlink(tmpfile);
     unlock_file(args->filename);
     srrd_create_args_destroy(args);
@@ -510,8 +511,8 @@ static void *srrd_create_thread(void *targs) /* {{{ */
 
   status = rename(tmpfile, args->filename);
   if (status != 0) {
-    ERROR("srrd_create_thread: rename (\"%s\", \"%s\") failed: %s", tmpfile,
-          args->filename, STRERRNO);
+    P_ERROR("srrd_create_thread: rename (\"%s\", \"%s\") failed: %s", tmpfile,
+            args->filename, STRERRNO);
     unlink(tmpfile);
     unlock_file(args->filename);
     srrd_create_args_destroy(args);
@@ -556,7 +557,7 @@ static int srrd_create_async(const char *filename, /* {{{ */
 
   status = pthread_create(&thread, &attr, srrd_create_thread, args);
   if (status != 0) {
-    ERROR("srrd_create_async: pthread_create failed: %s", STRERROR(status));
+    P_ERROR("srrd_create_async: pthread_create failed: %s", STRERROR(status));
     pthread_attr_destroy(&attr);
     srrd_create_args_destroy(args);
     return status;
@@ -587,12 +588,12 @@ int cu_rrd_create_file(const char *filename, /* {{{ */
     return -1;
 
   if ((rra_num = rra_get(&rra_def, vl, cfg)) < 1) {
-    ERROR("cu_rrd_create_file failed: Could not calculate RRAs");
+    P_ERROR("cu_rrd_create_file failed: Could not calculate RRAs");
     return -1;
   }
 
   if ((ds_num = ds_get(&ds_def, ds, vl, cfg)) < 1) {
-    ERROR("cu_rrd_create_file failed: Could not calculate DSes");
+    P_ERROR("cu_rrd_create_file failed: Could not calculate DSes");
     rra_free(rra_num, rra_def);
     return -1;
   }
@@ -600,7 +601,7 @@ int cu_rrd_create_file(const char *filename, /* {{{ */
   argc = ds_num + rra_num;
 
   if ((argv = malloc(sizeof(*argv) * (argc + 1))) == NULL) {
-    ERROR("cu_rrd_create_file failed: %s", STRERRNO);
+    P_ERROR("cu_rrd_create_file failed: %s", STRERRNO);
     rra_free(rra_num, rra_def);
     ds_free(ds_num, ds_def);
     return -1;
@@ -624,25 +625,25 @@ int cu_rrd_create_file(const char *filename, /* {{{ */
     status = srrd_create_async(filename, stepsize, last_up, argc,
                                (const char **)argv);
     if (status != 0)
-      WARNING("cu_rrd_create_file: srrd_create_async (%s) "
-              "returned status %i.",
-              filename, status);
+      P_WARNING("cu_rrd_create_file: srrd_create_async (%s) "
+                "returned status %i.",
+                filename, status);
   } else /* synchronous */
   {
     status = lock_file(filename);
     if (status != 0) {
       if (status == EEXIST)
-        NOTICE("cu_rrd_create_file: File \"%s\" is already being created.",
-               filename);
+        P_NOTICE("cu_rrd_create_file: File \"%s\" is already being created.",
+                 filename);
       else
-        ERROR("cu_rrd_create_file: Unable to lock file \"%s\".", filename);
+        P_ERROR("cu_rrd_create_file: Unable to lock file \"%s\".", filename);
     } else {
       status =
           srrd_create(filename, stepsize, last_up, argc, (const char **)argv);
 
       if (status != 0) {
-        WARNING("cu_rrd_create_file: srrd_create (%s) returned status %i.",
-                filename, status);
+        P_WARNING("cu_rrd_create_file: srrd_create (%s) returned status %i.",
+                  filename, status);
       } else {
         DEBUG("cu_rrd_create_file: Successfully created RRD file \"%s\".",
               filename);
index 5134a6e..ccab5ac 100644 (file)
@@ -43,7 +43,6 @@ struct cu_tail_match_simple_s {
   char plugin_instance[DATA_MAX_NAME_LEN];
   char type[DATA_MAX_NAME_LEN];
   char type_instance[DATA_MAX_NAME_LEN];
-  cdtime_t interval;
   latency_config_t latency_config;
 };
 typedef struct cu_tail_match_simple_s cu_tail_match_simple_t;
@@ -57,10 +56,7 @@ struct cu_tail_match_match_s {
 typedef struct cu_tail_match_match_s cu_tail_match_match_t;
 
 struct cu_tail_match_s {
-  int flags;
   cu_tail_t *tail;
-
-  cdtime_t interval;
   cu_tail_match_match_t *matches;
   size_t matches_num;
 };
@@ -92,7 +88,6 @@ static int simple_submit_match(cu_match_t *match, void *user_data) {
   sstrncpy(vl.type, data->type, sizeof(vl.type));
   sstrncpy(vl.type_instance, data->type_instance, sizeof(vl.type_instance));
 
-  vl.interval = data->interval;
   plugin_dispatch_values(&vl);
 
   match_value_reset(match_value);
@@ -111,7 +106,6 @@ static int latency_submit_match(cu_match_t *match, void *user_data) {
   sstrncpy(vl.plugin, data->plugin, sizeof(vl.plugin));
   sstrncpy(vl.plugin_instance, data->plugin_instance,
            sizeof(vl.plugin_instance));
-  vl.interval = data->interval;
   vl.time = cdtime();
 
   /* Submit percentiles */
@@ -248,8 +242,6 @@ int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match,
   obj->matches = temp;
   obj->matches_num++;
 
-  DEBUG("tail_match_add_match interval %lf",
-        CDTIME_T_TO_DOUBLE(((cu_tail_match_simple_t *)user_data)->interval));
   temp = obj->matches + (obj->matches_num - 1);
 
   temp->match = match;
@@ -264,8 +256,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
                                 const char *excluderegex, int ds_type,
                                 const char *plugin, const char *plugin_instance,
                                 const char *type, const char *type_instance,
-                                const latency_config_t latency_cfg,
-                                const cdtime_t interval) {
+                                const latency_config_t latency_cfg) {
   cu_match_t *match;
   cu_tail_match_simple_t *user_data;
   int status;
@@ -290,8 +281,6 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
     sstrncpy(user_data->type_instance, type_instance,
              sizeof(user_data->type_instance));
 
-  user_data->interval = interval;
-
   if ((ds_type & UTILS_MATCH_DS_TYPE_GAUGE) &&
       (ds_type & UTILS_MATCH_CF_GAUGE_DIST)) {
     status = latency_config_copy(&user_data->latency_config, latency_cfg);
index 03b70e9..2d4c253 100644 (file)
@@ -80,9 +80,8 @@ void tail_match_destroy(cu_tail_match_t *obj);
  *   When `tail_match_destroy' is called the `user_data' pointer is freed using
  *   the `free_user_data' callback - if it is not NULL.
  *   When using this interface the `tail_match' module doesn't dispatch any
- * values
- *   itself - all that has to happen in either the match-callbacks or the
- *   submit_match callback.
+ *   values itself - all that has to happen in either the match-callbacks or
+ *   the submit_match callback.
  *
  * RETURN VALUE
  *   Zero upon success, non-zero otherwise.
@@ -98,14 +97,13 @@ int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match,
  *  tail_match_add_match_simple
  *
  * DESCRIPTION
- *  A simplified version of `tail_match_add_match'. The regular expressen
- * `regex'
- *  must match a number, which is then dispatched according to `ds_type'. See
- *  the `match_create_simple' function in utils_match.h for a description how
- *  this flag effects calculation of a new value.
+ *  A simplified version of `tail_match_add_match'. The regular expression
+ *  `regex' must match a number, which is then dispatched according to
+ * `ds_type'.
+ *  See the `match_create_simple' function in utils_match.h for a description
+ *  how this flag effects calculation of a new value.
  *  The values gathered are dispatched by the tail_match module in this case.
- * The
- *  passed `plugin', `plugin_instance', `type', and `type_instance' are
+ *  The passed `plugin', `plugin_instance', `type', and `type_instance' are
  *  directly used when submitting these values.
  *  With excluderegex it is possible to exlude lines from the match.
  *  The `latency_cfg' specifies configuration for submitting latency.
@@ -117,8 +115,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
                                 const char *excluderegex, int ds_type,
                                 const char *plugin, const char *plugin_instance,
                                 const char *type, const char *type_instance,
-                                const latency_config_t latency_cfg,
-                                const cdtime_t interval);
+                                const latency_config_t latency_cfg);
 
 /*
  * NAME
@@ -130,8 +127,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex,
  *   added `utils_match' objects.
  *   After all lines have been read and processed, the submit_match callback is
  *   called or, in case of tail_match_add_match_simple, the data is dispatched
- * to
- *   the daemon directly.
+ *   to the daemon directly.
  *
  * RETURN VALUE
  *   Zero on success, nonzero on failure.
index 099c62b..7624e24 100644 (file)
@@ -38,6 +38,7 @@
  *     Protocol "udp"
  *     LogSendErrors true
  *     Prefix "collectd"
+ *     UseTags true
  *   </Carbon>
  * </Plugin>
  */
@@ -518,6 +519,8 @@ static int wg_config_node(oconfig_item_t *ci) {
       cf_util_get_flag(child, &cb->format_flags, GRAPHITE_PRESERVE_SEPARATOR);
     else if (strcasecmp("DropDuplicateFields", child->key) == 0)
       cf_util_get_flag(child, &cb->format_flags, GRAPHITE_DROP_DUPE_FIELDS);
+    else if (strcasecmp("UseTags", child->key) == 0)
+      cf_util_get_flag(child, &cb->format_flags, GRAPHITE_USE_TAGS);
     else if (strcasecmp("EscapeCharacter", child->key) == 0)
       config_set_char(&cb->escape_char, child);
     else {
index c120d15..04e67b9 100644 (file)
@@ -383,6 +383,10 @@ static void kafka_config_topic(rd_kafka_conf_t *conf,
       status = cf_util_get_flag(child, &tctx->graphite_flags,
                                 GRAPHITE_PRESERVE_SEPARATOR);
 
+    } else if (strcasecmp("GraphiteUseTags", child->key) == 0) {
+      status =
+          cf_util_get_flag(child, &tctx->graphite_flags, GRAPHITE_USE_TAGS);
+
     } else if (strcasecmp("GraphitePrefix", child->key) == 0) {
       status = cf_util_get_string(child, &tctx->prefix);
     } else if (strcasecmp("GraphitePostfix", child->key) == 0) {
index c17654b..72cb594 100644 (file)
@@ -184,8 +184,8 @@ static int wr_config_node(oconfig_item_t *ci) /* {{{ */
     return ENOMEM;
   node->host = NULL;
   node->port = 0;
-  node->timeout.tv_sec = 0;
-  node->timeout.tv_usec = 1000;
+  node->timeout.tv_sec = 1;
+  node->timeout.tv_usec = 0;
   node->conn = NULL;
   node->prefix = NULL;
   node->database = 0;
@@ -213,8 +213,11 @@ static int wr_config_node(oconfig_item_t *ci) /* {{{ */
       }
     } else if (strcasecmp("Timeout", child->key) == 0) {
       status = cf_util_get_int(child, &timeout);
-      if (status == 0)
-        node->timeout.tv_usec = timeout;
+      if (status == 0) {
+        node->timeout.tv_usec = timeout * 1000;
+        node->timeout.tv_sec = node->timeout.tv_usec / 1000000L;
+        node->timeout.tv_usec %= 1000000L;
+      }
     } else if (strcasecmp("Prefix", child->key) == 0) {
       status = cf_util_get_string(child, &node->prefix);
     } else if (strcasecmp("Database", child->key) == 0) {
index 1578e1c..b35d10e 100644 (file)
@@ -699,21 +699,13 @@ static int wrr_config_node(oconfig_item_t *ci) /* {{{ */
     } else if (strcasecmp("Port", child->key) == 0) {
       host->port = cf_util_get_port_number(child);
       if (host->port == -1) {
-        ERROR("write_riemann plugin: Invalid argument "
-              "configured for the \"Port\" "
-              "option.");
         break;
       }
     } else if (strcasecmp("Protocol", child->key) == 0) {
       char tmp[16];
       status = cf_util_get_string_buffer(child, tmp, sizeof(tmp));
-      if (status != 0) {
-        ERROR("write_riemann plugin: cf_util_get_"
-              "string_buffer failed with "
-              "status %i.",
-              status);
+      if (status != 0)
         break;
-      }
 
       if (strcasecmp("UDP", tmp) == 0)
         host->client_type = RIEMANN_CLIENT_UDP;
@@ -729,31 +721,16 @@ static int wrr_config_node(oconfig_item_t *ci) /* {{{ */
                 tmp);
     } else if (strcasecmp("TLSCAFile", child->key) == 0) {
       status = cf_util_get_string(child, &host->tls_ca_file);
-      if (status != 0) {
-        ERROR("write_riemann plugin: cf_util_get_"
-              "string_buffer failed with "
-              "status %i.",
-              status);
+      if (status != 0)
         break;
-      }
     } else if (strcasecmp("TLSCertFile", child->key) == 0) {
       status = cf_util_get_string(child, &host->tls_cert_file);
-      if (status != 0) {
-        ERROR("write_riemann plugin: cf_util_get_"
-              "string_buffer failed with "
-              "status %i.",
-              status);
+      if (status != 0)
         break;
-      }
     } else if (strcasecmp("TLSKeyFile", child->key) == 0) {
       status = cf_util_get_string(child, &host->tls_key_file);
-      if (status != 0) {
-        ERROR("write_riemann plugin: cf_util_get_"
-              "string_buffer failed with "
-              "status %i.",
-              status);
+      if (status != 0)
         break;
-      }
     } else if (strcasecmp("StoreRates", child->key) == 0) {
       status = cf_util_get_boolean(child, &host->store_rates);
       if (status != 0)
index 4c9f42b..6ea8106 100644 (file)
@@ -1084,12 +1084,8 @@ static int sensu_config_node(oconfig_item_t *ci) /* {{{ */
         break;
     } else if (strcasecmp("Port", child->key) == 0) {
       status = cf_util_get_service(child, &host->service);
-      if (status != 0) {
-        ERROR("write_sensu plugin: Invalid argument "
-              "configured for the \"Port\" "
-              "option.");
+      if (status != 0)
         break;
-      }
     } else if (strcasecmp("StoreRates", child->key) == 0) {
       status = cf_util_get_boolean(child, &host->store_rates);
       if (status != 0)