From: Pavel Rochnyak Date: Thu, 5 Jul 2018 09:03:17 +0000 (+0700) Subject: Merge pull request #2733 from elfiesmelfie/feat_pcie_aer X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=829683c47113c0f6305c9089424170ff706d047c;hp=1cc2660c708e7baed49a3e528cb60667441d5036;p=collectd.git Merge pull request #2733 from elfiesmelfie/feat_pcie_aer New plugin to read PCIe errors --- diff --git a/Makefile.am b/Makefile.am index effbdd43..4d32f74c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/configure.ac b/configure.ac index 7049975c..a91b755e 100644 --- a/configure.ac +++ b/configure.ac @@ -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 + #include + ]], + [[ + 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.])], [ diff --git a/src/bind.c b/src/bind.c index 3a5e3c31..fe3480d0 100644 --- a/src/bind.c +++ b/src/bind.c @@ -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) diff --git a/src/collectd-snmp.pod b/src/collectd-snmp.pod index d615088e..9d508d17 100644 --- a/src/collectd-snmp.pod +++ b/src/collectd-snmp.pod @@ -10,23 +10,24 @@ collectd-snmp - Documentation of collectd's C # ... - 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" - Type "users" Table false - Instance "" + Type "users" Shift -1 Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0" - 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" @@ -114,8 +115,9 @@ queried using the C SNMP command (see L) and transmitted to collectd. B value list is dispatched and, eventually, one file will be written. -When B is set to B, the OIDs given to B (see below) are -queried using the C SNMP command until the subtree is left. After all +When B
is set to B, the OIDs given to B, B, +B, B and B (see below) are queried using +the C SNMP command until the subtree is left. After all the lists (think: all columns of the table) have been read B values sets will be dispatches and, eventually, several files will be written. If you configure a B (see above) which needs more than one data source (for @@ -138,33 +140,66 @@ C and C. But, this is because of the B setting, not the B
setting. Since the semantic of B and B 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 I +=item B I -Sets the type-instance of the values that are dispatched. The meaning of this -setting depends on whether B
is set to I or I: +Use I as the plugin name of the values that are dispatched. +Defaults to C. -If B
is set to I, I 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 subtree. -L from the SNMP distribution describes the format of OIDs. +=item B I -If B
is set to I and B is omitted, then "SUBID" will be -used as the instance. +Sets the plugin-instance of the values that are dispatched to I value. -If B
is set to I the actual string configured for I is -copied into the value-list. In this case I may be empty, i.Ee. -"". +When B
is set to I and B is set then this option +has no effect. -=item B I +Defaults to an empty string. + +=item B I + +Sets the type-instance of the values that are dispatched to I value. -If B
is set to I, you may feel the need to add something to the -instance of the files. If set, I is prepended to the instance as -determined by querying the agent. When B
is set to I this option +When B
is set to I and B is set then this option has no effect. +Defaults to an empty string. + +=item B I + +=item B I + +=item B I + +If B
is set to I, I 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 subtree. L 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, +B and B options. + +When B
is set to I these options has no effect. + +Defaults: When no one of these options is configured explicitly, +B defaults to an empty string. + +=item B + +=item B + +=item B + +These options are intented to be used together with B, +B and B respectively. + +If set, I is preprended to values received by querying the agent. + +When B
is set to I these options has no effect. + The C 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... as @@ -172,6 +207,25 @@ instances is not enough, because the inlet voltages and outlet voltages may both have the subids 1, 2,E... You can use this setting to distinguish between the different voltages. +=item B I + +Attention: this option exists for backwards compatibility only and will be +removed in next major release. Please use B / B +instead. + +The meaning of this setting depends on whether B
is set to I or +I. + +If B
is set to I, option behaves as B. +If B
is set to I, option behaves as B. + +Note what B
option must be set before setting B. + +=item B I + +Attention: this option exists for backwards compatibility only and will be +removed in next major release. Please use B instead. + =item B I [I ...] 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 I [, I ...] -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
is set to I then this option has no effect. + =item B I 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 I + +=item B I [, I ...] + +=item B I + +When B
is set to I, these options allow to configure filtering +based on MIB values. + +The B declares I to fill table column with values. +The B declares values list to do match. Whether table row will be +collected or ignored depends on the B 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 table rows are selected. + +When B
is set to I then these options has no effect. + +See B
and F for details. + =back =head2 The Host block diff --git a/src/collectd-tg.c b/src/collectd-tg.c index 4669c655..2d83bbfe 100644 --- a/src/collectd-tg.c +++ b/src/collectd-tg.c @@ -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; diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 6a5dfbdf..bb83bda9 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -1344,21 +1344,31 @@ # # -# Type "voltage" # Table false -# Instance "input_line1" +# Type "voltage" +# TypeInstance "input_line1" # Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1" # # -# Type "users" # Table false -# Instance "" +# Type "users" +# TypeInstance "" # Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0" # # +# 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" +# +# # Table true -# Instance "IF-MIB::ifDescr" +# Type "if_octets" +# Plugin "interface" +# PluginInstanceOID "IF-MIB::ifDescr" # Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets" # # @@ -1399,7 +1409,9 @@ # IndexOID "IF-MIB::ifIndex" # SizeOID "IF-MIB::ifNumber" # -# Instance true +# +# Source "PluginInstance" +# # Plugin "interface" # OIDs "IF-MIB::ifDescr" # diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 87b92e3a..01fae1b0 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -1908,6 +1908,11 @@ plugin below on how matches are defined. If the B or B options are set to B, B blocks are optional. +=item B I + +Sets the interval (in seconds) in which the values will be collected from this +URL. By default the global B setting will be used. + =item B I The B option sets the overall timeout for HTTP requests to B, in @@ -2104,6 +2109,11 @@ Use I as the plugin instance when submitting values. May be overridden by B option inside B blocks. Defaults to an empty string (no plugin instance). +=item B I + +Sets the interval (in seconds) in which the values will be collected from this +URL. By default the global B setting will be used. + =item B I I 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 I + +Set the outgoing IP address for IP packets. This option can be used instead of +the I option to explicitly define the IP address which will be used +to send Packets to the remote server. + =item B I Sets the interval at which to re-resolve the DNS for the I. This is @@ -7327,26 +7343,26 @@ Defaults to B. =head2 Plugin C -The I connects to one or more Redis servers and gathers -information about each server's state. For each server there is a I block -which configures the connection parameters for this node. +The I 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 block which configures the connection +parameters and set of user-defined queries for this node. Host "localhost" Port "6379" Timeout 2000 + ReportCommandStats false + ReportCpuUsage true #Database 0 Type "queue_length" Instance "myqueue" - + -The information shown in the synopsis above is the I -which is used by the plugin if no configuration is present. - =over 4 =item B I @@ -7354,7 +7370,9 @@ which is used by the plugin if no configuration is present. The B 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. It is limited to -64Echaracters in length. +128Echaracters in length. + +When no B is configured explicitly, plugin connects to "localhost:6379". =item B I @@ -7374,30 +7392,47 @@ Use I to authenticate when connecting to I. =item B I The B 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 values for all B should be lower -than B defined globally. +read function is blocking, you should keep this value as low as possible. +It is expected what B values should be lower than B defined +globally. -=item B I +Defaults to 2000 (2 seconds). -The B block identifies a query to execute against the redis server. -There may be an arbitrary number of queries to execute. +=item B B|B -=item B I +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. -This index selects the Redis logical database to use for query. Defaults -to C<0>. +=item B B|B + +Enables or disables reporting of CPU consumption statistics. +Defaults to B. + +=item B I + +The B 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 I -Within a query definition, a valid collectd type to use as when submitting +Within a query definition, a valid I to use as when submitting the result of the query. When not supplied, will default to B. +Currently only types with one datasource are supported. +See L for more details on types and their configuration. + =item B I 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 I + +This index selects the Redis logical database to use for query. Defaults +to C<0>. =back @@ -7837,7 +7872,9 @@ B IndexOID "IF-MIB::ifIndex" SizeOID "IF-MIB::ifNumber" - Instance true + + Source "PluginInstance" + Plugin "interface" OIDs "IF-MIB::ifDescr" @@ -7848,12 +7885,44 @@ B OIDs "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
+ + + + Source "PluginInstance" + + Plugin "virt" + OIDs "LIBVIRT-HYPERVISOR-MIB::lvhAffinityDomainName" + + + Plugin "virt" + + Source "TypeInstance" + Regex "^vcpu_([0-9]{1,3})-cpu_[0-9]{1,3}$" + Group 1 + + OIDs "LIBVIRT-HYPERVISOR-MIB::lvhVCPUIndex" + + + Plugin "virt" + + Source "TypeInstance" + Regex "^vcpu_[0-9]{1,3}-cpu_([0-9]{1,3})$" + Group 1 + + OIDs "LIBVIRT-HYPERVISOR-MIB::lvhCPUIndex" + + + Plugin "virt" + Type "cpu_affinity" + OIDs "LIBVIRT-HYPERVISOR-MIB::lvhCPUAffinity" + +
There are two types of blocks that can be contained in the CPluginE snmp_agentE> block: B and B: -=head3 The B block +=head3 B block The B block defines a list OIDs that are to be handled. This block can define scalar or table OIDs. If B block is defined inside of B
@@ -7862,11 +7931,34 @@ The following options can be set: =over 4 -=item B I +=item B block + +B block contains all data needed for proper index build of snmp table. +In case more than +one table B block has B block present then multiple key index is +built. If B block defines scalar data type B has no effect and can +be omitted. + +=over 8 + +=item B I + +B 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 is set to B, the value for requested OID is copied from -plugin instance field of corresponding collectd value. If B block defines -scalar data type B has no effect and can be omitted. +=item B I + +B 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 I + +B number can be specified in case groups are used in regex. + +=back =item B I @@ -9248,6 +9340,7 @@ Synopsis: Protocol "tcp" LogSendErrors true Prefix "collectd" + UseTags false @@ -9285,13 +9378,20 @@ approach and logging errors fills syslog with unneeded messages. =item B I -When set, I is added in front of the host name. Dots and whitespace are -I escaped in this string (see B below). +When B is I, B value is added in front of the host name. +When B is I, B value is added in front of series name. + +Dots and whitespace are I escaped in this string (see B +below). =item B I -When set, I is appended to the host name. Dots and whitespace are -I escaped in this string (see B below). +When B is I, B value appended to the host name. +When B is I, B value appended to the end of series name +(before the first ; that separates the name from the tags). + +Dots and whitespace are I escaped in this string (see B +below). =item B I @@ -9313,6 +9413,8 @@ path component, for example C. If set to B (the default), the plugin and plugin instance (and likewise the type and type instance) are put into one component, for example C. +Option value is not used when B is I. + =item B B|B If set to B, append the name of the I (DS) to the "metric" @@ -9325,12 +9427,31 @@ If set to B (the default) the C<.> (dot) character is replaced with I. Otherwise, if set to B, the C<.> (dot) character is preserved, i.e. passed through. +Option value is not used when B is I. + =item B B|B If set to B, detect and remove duplicate components in Graphite metric names. For example, the metric name C will be shortened to C. +=item B B|B + +If set to B, Graphite metric names will be generated as tagged series. +This allows for much more flexibility than the traditional hierarchical layout. + +Example: +C + +You can use B 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, the B and B settings +are not used. + +Default value: B. + =back =head2 Plugin C @@ -9756,17 +9877,26 @@ been set to B. =item B (B=I only) A prefix can be added in the metric name when outputting in the I -format. It's added before the I name. +format. + +When B is I, prefix is added before the I name. Metric name will be CprefixEEhostEEpostfixEEpluginEEtypeEEnameE> +When B is I, prefix is added in front of series name. + =item B (B=I only) A postfix can be added in the metric name when outputting in the I -format. It's added after the I name. +format. + +When B is I, postfix is added after the I name. Metric name will be CprefixEEhostEEpostfixEEpluginEEtypeEEnameE> +When B is I, prefix value appended to the end of series +name (before the first ; that separates the name from the tags). + =item B (B=I only) Specify a character to replace dots (.) in the host part of the metric name. @@ -9781,6 +9911,8 @@ path component, for example C. If set to B (the default), the plugin and plugin instance (and likewise the type and type instance) are put into one component, for example C. +Option value is not used when B is I. + =item B B|B If set to B, append the name of the I (DS) to the "metric" @@ -9793,6 +9925,14 @@ If set to B (the default) the C<.> (dot) character is replaced with I. Otherwise, if set to B, the C<.> (dot) character is preserved, i.e. passed through. +Option value is not used when B is I. + +=item B B|B + +If set to B Graphite metric names will be generated as tagged series. + +Default value: B. + =item B B|B If set to B (the default), convert counter values to rates. If set to diff --git a/src/curl.c b/src/curl.c index 4bfd1e4d..4925ad09 100644 --- a/src/curl.c +++ b/src/curl.c @@ -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 */ diff --git a/src/curl_json.c b/src/curl_json.c index f0badc99..dedfed05 100644 --- a/src/curl_json.c +++ b/src/curl_json.c @@ -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 */ diff --git a/src/curl_xml.c b/src/curl_xml.c index 654bb671..0bed05a5 100644 --- a/src/curl_xml.c +++ b/src/curl_xml.c @@ -823,6 +823,8 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */ return status; } + cdtime_t interval = 0; + /* Fill the `cx_t' structure.. */ for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; @@ -853,6 +855,8 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */ status = cf_util_get_string(child, &db->post_body); else if (strcasecmp("Namespace", child->key) == 0) status = cx_config_add_namespace(db, child); + else if (strcasecmp("Interval", child->key) == 0) + status = cf_util_get_cdtime(child, &interval); else if (strcasecmp("Timeout", child->key) == 0) status = cf_util_get_int(child, &db->timeout); else if (strcasecmp("Statistics", child->key) == 0) { @@ -891,7 +895,7 @@ static int cx_config_add_url(oconfig_item_t *ci) /* {{{ */ char *cb_name = ssnprintf_alloc("curl_xml-%s-%s", db->instance, db->url); plugin_register_complex_read(/* group = */ "curl_xml", cb_name, cx_read, - /* interval = */ 0, + /* interval = */ interval, &(user_data_t){ .data = db, .free_func = cx_free, }); diff --git a/src/daemon/common.c b/src/daemon/common.c index 582d6b23..76c7036d 100644 --- a/src/daemon/common.c +++ b/src/daemon/common.c @@ -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 */ diff --git a/src/daemon/configfile.c b/src/daemon/configfile.c index 2830a86e..8750bef8 100644 --- a/src/daemon/configfile.c +++ b/src/daemon/configfile.c @@ -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; } diff --git a/src/daemon/plugin.c b/src/daemon/plugin.c index 427a8134..73d7c841 100644 --- a/src/daemon/plugin.c +++ b/src/daemon/plugin.c @@ -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 " - ".", - 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 */ diff --git a/src/daemon/plugin.h b/src/daemon/plugin.h index 03690678..871eccdc 100644 --- a/src/daemon/plugin.h +++ b/src/daemon/plugin.h @@ -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, diff --git a/src/daemon/plugin_mock.c b/src/daemon/plugin_mock.c index 8f8334ea..1624f0ea 100644 --- a/src/daemon/plugin_mock.c +++ b/src/daemon/plugin_mock.c @@ -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 */ } diff --git a/src/daemon/utils_cache_mock.c b/src/daemon/utils_cache_mock.c index 5389d126..1495a803 100644 --- a/src/daemon/utils_cache_mock.c +++ b/src/daemon/utils_cache_mock.c @@ -24,8 +24,8 @@ * Florian octo Forster */ -#include #include "utils_cache.h" +#include 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; +} diff --git a/src/dbi.c b/src/dbi.c index 1909d8ca..fe9bc516 100644 --- 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.", diff --git a/src/dpdkevents.c b/src/dpdkevents.c index 9970be0c..2a44b2c1 100644 --- a/src/dpdkevents.c +++ b/src/dpdkevents.c @@ -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"); diff --git a/src/filecount.c b/src/filecount.c index ef1a1387..9091ff55 100644 --- a/src/filecount.c +++ b/src/filecount.c @@ -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) diff --git a/src/gmond.c b/src/gmond.c index ca654194..2bca05ad 100644 --- a/src/gmond.c +++ b/src/gmond.c @@ -833,28 +833,6 @@ static int mc_receive_thread_stop(void) /* {{{ */ * * */ -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); diff --git a/src/memcachec.c b/src/memcachec.c index 13e388e8..f293aa1b 100644 --- a/src/memcachec.c +++ b/src/memcachec.c @@ -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); diff --git a/src/modbus.c b/src/modbus.c index bb9eaa0f..0a4f40ca 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -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, }); diff --git a/src/netapp.c b/src/netapp.c index 281764c7..62600ca2 100644 --- a/src/netapp.c +++ b/src/netapp.c @@ -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 * 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.", diff --git a/src/network.c b/src/network.c index 84ba00b5..d602dfc8 100644 --- a/src/network.c +++ b/src/network.c @@ -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 { diff --git a/src/notify_email.c b/src/notify_email.c index 6b32ad9a..bb36ff27 100644 --- a/src/notify_email.c +++ b/src/notify_email.c @@ -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. */ diff --git a/src/ntpd.c b/src/ntpd.c index baa1988b..ef634988 100644 --- a/src/ntpd.c +++ b/src/ntpd.c @@ -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)); } diff --git a/src/oracle.c b/src/oracle.c index 4bca8387..1b17d9c3 100644 --- a/src/oracle.c +++ b/src/oracle.c @@ -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.", diff --git a/src/perl.c b/src/perl.c index 31c68ad7..fffbc21d 100644 --- a/src/perl.c +++ b/src/perl.c @@ -33,6 +33,7 @@ /* do not automatically get the thread specific Perl interpreter */ #define PERL_NO_GET_CONTEXT +#include "collectd.h" #include #include diff --git a/src/postgresql.c b/src/postgresql.c index 7c140e0a..ae887a17 100644 --- a/src/postgresql.c +++ b/src/postgresql.c @@ -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; diff --git a/src/powerdns.c b/src/powerdns.c index eb3ec537..7a2fbfd6 100644 --- a/src/powerdns.c +++ b/src/powerdns.c @@ -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; } diff --git a/src/protocols.c b/src/protocols.c index 59eb49a7..36b1d83b 100644 --- a/src/protocols.c +++ b/src/protocols.c @@ -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; } diff --git a/src/redis.c b/src/redis.c index 57340981..41442b66 100644 --- a/src/redis.c +++ b/src/redis.c @@ -28,17 +28,11 @@ #include #include -#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 */ } /* }}} */ diff --git a/src/rrdtool.c b/src/rrdtool.c index 5c87a434..f6290d73 100644 --- a/src/rrdtool.c +++ b/src/rrdtool.c @@ -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; diff --git a/src/snmp.c b/src/snmp.c index 7c3ebc82..a86d0853 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -29,6 +29,7 @@ #include "common.h" #include "plugin.h" #include "utils_complain.h" +#include "utils_ignorelist.h" #include #include @@ -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(¤t_suffix, &instance_list_ptr->suffix, + memcpy(¤t_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(¤t_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, + ¤t_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, + ¤t_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, ¤t_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, ¤t_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, ¤t_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, ¤t_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, ¤t_suffix) < 0)) - value_table_ptr[i] = value_table_ptr[i]->next; + (value_cell_ptr[i] != NULL) && + (csnmp_oid_compare(&value_cell_ptr[i]->suffix, ¤t_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, ¤t_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), ¤t_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), ¤t_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), ¤t_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; } diff --git a/src/snmp_agent.c b/src/snmp_agent.c index 0d1c5778..1c7191ff 100644 --- a/src/snmp_agent.c +++ b/src/snmp_agent.c @@ -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 * Serhiy Pshyk + * Marcin Mozejko **/ #include "collectd.h" @@ -32,6 +33,7 @@ #include "utils_avltree.h" #include "utils_cache.h" #include "utils_llist.h" +#include #include @@ -40,12 +42,32 @@ #include #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 index 00000000..581f33d3 --- /dev/null +++ b/src/snmp_agent_test.c @@ -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 + **/ + +#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(®ex_info, regex, REG_EXTENDED); + EXPECT_EQ_INT(0, ret); + + ret = snmp_agent_parse_index_key(input, ®ex_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, ®ex_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, ®ex_info, 2, &match); + EXPECT_EQ_INT(0, ret); + EXPECT_EQ_INT(12, match.rm_so); + EXPECT_EQ_INT(13, match.rm_eo); + + regfree(®ex_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; +} diff --git a/src/statsd.c b/src/statsd.c index 444e8ea1..6fbfd181 100644 --- a/src/statsd.c +++ b/src/statsd.c @@ -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; } diff --git a/src/table.c b/src/table.c index 189d6057..492bea61 100644 --- a/src/table.c +++ b/src/table.c @@ -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 diff --git a/src/tail.c b/src/tail.c index a6471d83..841b3312 100644 --- a/src/tail.c +++ b/src/tail.c @@ -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 */ diff --git a/src/tail_csv.c b/src/tail_csv.c index be7cd40f..48f3d743 100644 --- a/src/tail_csv.c +++ b/src/tail_csv.c @@ -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, }); diff --git a/src/threshold.c b/src/threshold.c index 79001334..79300f14 100644 --- a/src/threshold.c +++ b/src/threshold.c @@ -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.", diff --git a/src/types.db b/src/types.db index 1b1e6f0c..f4933ee3 100644 --- a/src/types.db +++ b/src/types.db @@ -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 diff --git a/src/utils_db_query.c b/src/utils_db_query.c index 61fe2e41..0279a471 100644 --- a/src/utils_db_query.c +++ b/src/utils_db_query.c @@ -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 " - "block is above the database definition!", - name); + P_ERROR("Cannot find query `%s'. Make sure the " + "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; } diff --git a/src/utils_db_query.h b/src/utils_db_query.h index 4d6129a1..f173204c 100644 --- a/src/utils_db_query.h +++ b/src/utils_db_query.h @@ -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); diff --git a/src/utils_dpdk.c b/src/utils_dpdk.c index 1d4668f3..aee97917 100644 --- a/src/utils_dpdk.c +++ b/src/utils_dpdk.c @@ -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", diff --git a/src/utils_format_graphite.c b/src/utils_format_graphite.c index 0bc802b6..de3f0c2e 100644 --- a/src/utils_format_graphite.c +++ b/src/utils_format_graphite.c @@ -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; } diff --git a/src/utils_format_graphite.h b/src/utils_format_graphite.h index de90c44c..60b89ae7 100644 --- a/src/utils_format_graphite.h +++ b/src/utils_format_graphite.h @@ -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, diff --git a/src/utils_format_graphite_test.c b/src/utils_format_graphite_test.c index a82142fa..42efa681 100644 --- a/src/utils_format_graphite_test.c +++ b/src/utils_format_graphite_test.c @@ -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++) { diff --git a/src/utils_rrdcreate.c b/src/utils_rrdcreate.c index ce7838d5..8f92cfd3 100644 --- a/src/utils_rrdcreate.c +++ b/src/utils_rrdcreate.c @@ -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); diff --git a/src/utils_tail_match.c b/src/utils_tail_match.c index 5134a6e1..ccab5ace 100644 --- a/src/utils_tail_match.c +++ b/src/utils_tail_match.c @@ -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); diff --git a/src/utils_tail_match.h b/src/utils_tail_match.h index 03b70e93..2d4c2531 100644 --- a/src/utils_tail_match.h +++ b/src/utils_tail_match.h @@ -80,9 +80,8 @@ void tail_match_destroy(cu_tail_match_t *obj); * When `tail_match_destroy' is called the `user_data' pointer is freed using * the `free_user_data' callback - if it is not NULL. * When using this interface the `tail_match' module doesn't dispatch any - * values - * itself - all that has to happen in either the match-callbacks or the - * submit_match callback. + * values itself - all that has to happen in either the match-callbacks or + * the submit_match callback. * * RETURN VALUE * Zero upon success, non-zero otherwise. @@ -98,14 +97,13 @@ int tail_match_add_match(cu_tail_match_t *obj, cu_match_t *match, * tail_match_add_match_simple * * DESCRIPTION - * A simplified version of `tail_match_add_match'. The regular expressen - * `regex' - * must match a number, which is then dispatched according to `ds_type'. See - * the `match_create_simple' function in utils_match.h for a description how - * this flag effects calculation of a new value. + * A simplified version of `tail_match_add_match'. The regular expression + * `regex' must match a number, which is then dispatched according to + * `ds_type'. + * See the `match_create_simple' function in utils_match.h for a description + * how this flag effects calculation of a new value. * The values gathered are dispatched by the tail_match module in this case. - * The - * passed `plugin', `plugin_instance', `type', and `type_instance' are + * The passed `plugin', `plugin_instance', `type', and `type_instance' are * directly used when submitting these values. * With excluderegex it is possible to exlude lines from the match. * The `latency_cfg' specifies configuration for submitting latency. @@ -117,8 +115,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, const char *excluderegex, int ds_type, const char *plugin, const char *plugin_instance, const char *type, const char *type_instance, - const latency_config_t latency_cfg, - const cdtime_t interval); + const latency_config_t latency_cfg); /* * NAME @@ -130,8 +127,7 @@ int tail_match_add_match_simple(cu_tail_match_t *obj, const char *regex, * added `utils_match' objects. * After all lines have been read and processed, the submit_match callback is * called or, in case of tail_match_add_match_simple, the data is dispatched - * to - * the daemon directly. + * to the daemon directly. * * RETURN VALUE * Zero on success, nonzero on failure. diff --git a/src/write_graphite.c b/src/write_graphite.c index 099c62bb..7624e243 100644 --- a/src/write_graphite.c +++ b/src/write_graphite.c @@ -38,6 +38,7 @@ * Protocol "udp" * LogSendErrors true * Prefix "collectd" + * UseTags true * * */ @@ -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 { diff --git a/src/write_kafka.c b/src/write_kafka.c index c120d15d..04e67b92 100644 --- a/src/write_kafka.c +++ b/src/write_kafka.c @@ -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) { diff --git a/src/write_redis.c b/src/write_redis.c index c17654b4..72cb5946 100644 --- a/src/write_redis.c +++ b/src/write_redis.c @@ -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) { diff --git a/src/write_riemann.c b/src/write_riemann.c index 1578e1c1..b35d10ee 100644 --- a/src/write_riemann.c +++ b/src/write_riemann.c @@ -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) diff --git a/src/write_sensu.c b/src/write_sensu.c index 4c9f42ba..6ea8106c 100644 --- a/src/write_sensu.c +++ b/src/write_sensu.c @@ -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)