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
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
SAVE_CFLAGS="$CFLAGS"
CFLAGS="-Wall -Werror"
+SAVE_LDFAGS="$LDFLAGS"
+LDFLAGS=""
AC_CACHE_CHECK([for strtok_r],
[c_cv_have_strtok_r_default],
fi
CFLAGS="$SAVE_CFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
if test "x$c_cv_have_strtok_r_reentrant" = "xyes"; then
CFLAGS="$CFLAGS -D_REENTRANT=1"
fi
LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags"
AC_CHECK_LIB([netsnmp], [init_snmp],
- [with_libnetsmp="yes"],
+ [with_libnetsnmp="yes"],
[with_libnetsnmp="no (libnetsnmp not found)"]
)
fi
if test "x$with_libnetsnmp" = "xyes"; then
+ SAVE_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags"
+
+ AC_CHECK_LIB([netsnmp], [netsnmp_get_version],
+ [with_libnetsnmp="yes"],
+ [with_libnetsnmp="no (couldn't get libnetsnmp version)"]
+ )
+
+ LDFLAGS="$SAVE_LDFLAGS"
+fi
+
+if test "x$with_libnetsnmp" = "xyes"; then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ SAVE_LDFLAGS="$LDFLAGS"
+ SAVE_LIBS="$LIBS"
+ CPPFLAGS="$CPPFLAGS $with_libnetsnmp_cppflags -Wall -Werror"
+ LDFLAGS="$LDFLAGS $with_libnetsnmp_ldflags"
+ LIBS="$LIBS -lnetsnmp"
+
+ AC_CACHE_CHECK([whether netsnmp library has old API],
+ [c_cv_have_netsnmp_old_api],
+ [
+ AC_LINK_IFELSE(
+ [
+ AC_LANG_PROGRAM(
+ [[
+ #include <net-snmp/net-snmp-config.h>
+ #include <net-snmp/net-snmp-includes.h>
+ ]],
+ [[
+ netsnmp_variable_list *key = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);;
+ int val;
+ u_char type = ASN_INTEGER;
+ snmp_set_var_value(key, &val, sizeof(val));
+ snmp_set_var_typed_value(key, type, &val, sizeof(val));
+ return 0;
+ ]]
+ )
+ ],
+ [c_cv_have_netsnmp_old_api="no"],
+ [c_cv_have_netsnmp_old_api="yes"]
+ )
+ ]
+ )
+
+ if test "x$c_cv_have_netsnmp_old_api" = "xyes"; then
+ AC_DEFINE([HAVE_NETSNMP_OLD_API], [1],
+ ["Define 1 if you have old netsnmp API]")
+ fi
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+ LIBS="$SAVE_LIBS"
+fi
+
+if test "x$with_libnetsnmp" = "xyes"; then
BUILD_WITH_LIBNETSNMP_CPPFLAGS="$with_libnetsnmp_cppflags"
BUILD_WITH_LIBNETSNMP_LDFLAGS="$with_libnetsnmp_ldflags"
BUILD_WITH_LIBNETSNMP_LIBS="-lnetsnmp"
AC_SUBST([BUILD_WITH_LIBNETSNMP_LIBS])
# }}}
-# --with-libnetsmpagent {{{
+# --with-libnetsnmpagent {{{
AC_ARG_WITH([libnetsnmpagent],
[AS_HELP_STRING([--with-libnetsnmpagent@<:@=PREFIX@:>@], [Path to libnetsnmpagent.])],
[
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;
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;
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;
}
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)) {
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 {
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)
# ...
<Plugin snmp>
<Data "powerplus_voltge_input">
- Type "voltage"
Table false
- Instance "input_line1"
+ Type "voltage"
+ TypeInstance "input_line1"
Scale 0.1
Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1"
</Data>
<Data "hr_users">
- Type "users"
Table false
- Instance ""
+ Type "users"
Shift -1
Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0"
</Data>
<Data "std_traffic">
- Type "if_octets"
Table true
- Instance "IF-MIB::ifDescr"
+ Type "if_octets"
+ TypeInstanceOID "IF-MIB::ifDescr"
+ #FilterOID "IF-MIB::ifOperStatus"
+ #FilterValues "1", "2"
Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
</Data>
collectd. B<One> value list is dispatched and, eventually, one file will be
written.
-When B<Table> is set to B<true>, the OIDs given to B<Values> (see below) are
-queried using the C<GETNEXT> SNMP command until the subtree is left. After all
+When B<Table> is set to B<true>, the OIDs given to B<Values>, B<TypeInstanceOID>,
+B<PluginInstanceOID>, B<HostOID> and B<FilterOID> (see below) are queried using
+the C<GETNEXT> SNMP command until the subtree is left. After all
the lists (think: all columns of the table) have been read B<several> values
sets will be dispatches and, eventually, several files will be written. If you
configure a B<Type> (see above) which needs more than one data source (for
the B<Type> setting, not the B<Table> setting.
Since the semantic of B<Instance> and B<Values> depends on this setting you
-need to set it before setting them. Doing vice verse will result in undefined
+need to set it before setting them. Doing vice versa will result in undefined
behavior.
-=item B<Instance> I<Instance>
+=item B<Plugin> I<Plugin>
-Sets the type-instance of the values that are dispatched. The meaning of this
-setting depends on whether B<Table> is set to I<true> or I<false>:
+Use I<Plugin> as the plugin name of the values that are dispatched.
+Defaults to C<snmp>.
-If B<Table> is set to I<true>, I<Instance> is interpreted as an SNMP-prefix
-that will return a list of values. Those values are then used as the actual
-type-instance. An example would be the C<IF-MIB::ifDescr> subtree.
-L<variables(5)> from the SNMP distribution describes the format of OIDs.
+=item B<PluginInstance> I<Instance>
-If B<Table> is set to I<true> and B<Instance> is omitted, then "SUBID" will be
-used as the instance.
+Sets the plugin-instance of the values that are dispatched to I<Instance> value.
-If B<Table> is set to I<false> the actual string configured for I<Instance> is
-copied into the value-list. In this case I<Instance> may be empty, i.E<nbsp>e.
-"".
+When B<Table> is set to I<true> and B<PluginInstanceOID> is set then this option
+has no effect.
-=item B<InstancePrefix> I<String>
+Defaults to an empty string.
+
+=item B<TypeInstance> I<Instance>
+
+Sets the type-instance of the values that are dispatched to I<Instance> value.
-If B<Table> is set to I<true>, you may feel the need to add something to the
-instance of the files. If set, I<String> is prepended to the instance as
-determined by querying the agent. When B<Table> is set to I<false> this option
+When B<Table> is set to I<true> and B<TypeInstanceOID> is set then this option
has no effect.
+Defaults to an empty string.
+
+=item B<TypeInstanceOID> I<OID>
+
+=item B<PluginInstanceOID> I<OID>
+
+=item B<HostOID> I<OID>
+
+If B<Table> is set to I<true>, I<OID> is interpreted as an SNMP-prefix that will
+return a list of values. Those values are then used as the actual type-instance,
+plugin-instance or host of dispatched metrics. An example would be the
+C<IF-MIB::ifDescr> subtree. L<variables(5)> from the SNMP distribution describes
+the format of OIDs. When option is set to empty string, then "SUBID" will be used
+as the value.
+
+Prefix may be set for values with use of appropriate B<TypeInstancePrefix>,
+B<PluginInstancePrefix> and B<HostPrefix> options.
+
+When B<Table> is set to I<false> these options has no effect.
+
+Defaults: When no one of these options is configured explicitly,
+B<TypeInstanceOID> defaults to an empty string.
+
+=item B<TypeInstancePrefix>
+
+=item B<PluginInstancePrefix>
+
+=item B<HostPrefix>
+
+These options are intented to be used together with B<TypeInstanceOID>,
+B<PluginInstanceOID> and B<HostOID> respectively.
+
+If set, I<String> is preprended to values received by querying the agent.
+
+When B<Table> is set to I<false> these options has no effect.
+
The C<UPS-MIB> is an example where you need this setting: It has voltages of
the inlets, outlets and the battery of an UPS. However, it doesn't provide a
descriptive column for these voltages. In this case having 1, 2,E<nbsp>... as
both have the subids 1, 2,E<nbsp>... You can use this setting to distinguish
between the different voltages.
+=item B<Instance> I<Instance>
+
+Attention: this option exists for backwards compatibility only and will be
+removed in next major release. Please use B<TypeInstance> / B<TypeInstanceOID>
+instead.
+
+The meaning of this setting depends on whether B<Table> is set to I<true> or
+I<false>.
+
+If B<Table> is set to I<true>, option behaves as B<TypeInstanceOID>.
+If B<Table> is set to I<false>, option behaves as B<TypeInstance>.
+
+Note what B<Table> option must be set before setting B<Instance>.
+
+=item B<InstancePrefix> I<String>
+
+Attention: this option exists for backwards compatibility only and will be
+removed in next major release. Please use B<TypeInstancePrefix> instead.
+
=item B<Values> I<OID> [I<OID> ...]
Configures the values to be queried from the SNMP host. The meaning slightly
=item B<Ignore> I<Value> [, I<Value> ...]
-The ignore values allows one to ignore Instances based on their name and the
-patterns specified by the various values you've entered. The match is a
+The ignore values allows one to ignore TypeInstances based on their name and
+the patterns specified by the various values you've entered. The match is a
glob-type shell matching.
+When B<Table> is set to I<false> then this option has no effect.
+
=item B<InvertMatch> I<true|false(default)>
The invertmatch value should be use in combination of the Ignore option.
It changes the behaviour of the Ignore option, from a blacklist behaviour
when InvertMatch is set to false, to a whitelist when specified to true.
+=item B<FilterOID> I<OID>
+
+=item B<FilterValues> I<Value> [, I<Value> ...]
+
+=item B<FilterIgnoreSelected> I<true|false(default)>
+
+When B<Table> is set to I<true>, these options allow to configure filtering
+based on MIB values.
+
+The B<FilterOID> declares I<OID> to fill table column with values.
+The B<FilterValues> declares values list to do match. Whether table row will be
+collected or ignored depends on the B<FilterIgnoreSelected> setting.
+As with other plugins that use the daemon's ignorelist functionality, a string
+that starts and ends with a slash is interpreted as a regular expression.
+
+If no selection is configured at all, B<all> table rows are selected.
+
+When B<Table> is set to I<false> then these options has no effect.
+
+See B<Table> and F</"IGNORELISTS"> for details.
+
=back
=head2 The Host block
{
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;
#<Plugin snmp>
# <Data "powerplus_voltge_input">
-# Type "voltage"
# Table false
-# Instance "input_line1"
+# Type "voltage"
+# TypeInstance "input_line1"
# Values "SNMPv2-SMI::enterprises.6050.5.4.1.1.2.1"
# </Data>
# <Data "hr_users">
-# Type "users"
# Table false
-# Instance ""
+# Type "users"
+# TypeInstance ""
# Values "HOST-RESOURCES-MIB::hrSystemNumUsers.0"
# </Data>
# <Data "std_traffic">
+# Table true
# Type "if_octets"
+# TypeInstanceOID "IF-MIB::ifDescr"
+# #TypeInstancePrefix "port"
+# Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
+# #FilterOID "IF-MIB::ifOperStatus"
+# #FilterValues "1", "2"
+# </Data>
+# <Data "interface_traffic">
# Table true
-# Instance "IF-MIB::ifDescr"
+# Type "if_octets"
+# Plugin "interface"
+# PluginInstanceOID "IF-MIB::ifDescr"
# Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
# </Data>
#
# IndexOID "IF-MIB::ifIndex"
# SizeOID "IF-MIB::ifNumber"
# <Data "ifDescr">
-# Instance true
+# <IndexKey>
+# Source "PluginInstance"
+# </IndexKey>
# Plugin "interface"
# OIDs "IF-MIB::ifDescr"
# </Data>
B<MeasureResponseCode> options are set to B<true>, B<Match> blocks are
optional.
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+URL. By default the global B<Interval> setting will be used.
+
=item B<Timeout> I<Milliseconds>
The B<Timeout> option sets the overall timeout for HTTP requests to B<URL>, in
May be overridden by B<PluginInstanceFrom> option inside B<XPath> blocks.
Defaults to an empty string (no plugin instance).
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+URL. By default the global B<Interval> setting will be used.
+
=item B<Namespace> I<Prefix> I<URL>
If an XPath expression references namespaces, they must be specified
that the manual selection of an interface for unicast traffic is only
necessary in rare cases.
+=item B<BindAddress> I<IP Address>
+
+Set the outgoing IP address for IP packets. This option can be used instead of
+the I<Interface> option to explicitly define the IP address which will be used
+to send Packets to the remote server.
+
=item B<ResolveInterval> I<Seconds>
Sets the interval at which to re-resolve the DNS for the I<Host>. This is
=head2 Plugin C<redis>
-The I<Redis plugin> connects to one or more Redis servers and gathers
-information about each server's state. For each server there is a I<Node> block
-which configures the connection parameters for this node.
+The I<Redis plugin> connects to one or more Redis servers, gathers
+information about each server's state and executes user-defined queries.
+For each server there is a I<Node> block which configures the connection
+parameters and set of user-defined queries for this node.
<Plugin redis>
<Node "example">
Host "localhost"
Port "6379"
Timeout 2000
+ ReportCommandStats false
+ ReportCpuUsage true
<Query "LLEN myqueue">
#Database 0
Type "queue_length"
Instance "myqueue"
- <Query>
+ </Query>
</Node>
</Plugin>
-The information shown in the synopsis above is the I<default configuration>
-which is used by the plugin if no configuration is present.
-
=over 4
=item B<Node> I<Nodename>
The B<Node> block identifies a new Redis node, that is a new Redis instance
running in an specified host and port. The name for node is a canonical
identifier which is used as I<plugin instance>. It is limited to
-64E<nbsp>characters in length.
+128E<nbsp>characters in length.
+
+When no B<Node> is configured explicitly, plugin connects to "localhost:6379".
=item B<Host> I<Hostname>
=item B<Timeout> I<Milliseconds>
The B<Timeout> option set the socket timeout for node response. Since the Redis
-read function is blocking, you should keep this value as low as possible. Keep
-in mind that the sum of all B<Timeout> values for all B<Nodes> should be lower
-than B<Interval> defined globally.
+read function is blocking, you should keep this value as low as possible.
+It is expected what B<Timeout> values should be lower than B<Interval> defined
+globally.
-=item B<Query> I<Querystring>
+Defaults to 2000 (2 seconds).
-The B<Query> block identifies a query to execute against the redis server.
-There may be an arbitrary number of queries to execute.
+=item B<ReportCommandStats> B<false>|B<true>
-=item B<Database> I<Index>
+Enables or disables reporting of statistics based on the command type, including
+rate of command calls and average CPU time consumed by command processing.
+Defaults to B<false>.
-This index selects the Redis logical database to use for query. Defaults
-to C<0>.
+=item B<ReportCpuUsage> B<true>|B<false>
+
+Enables or disables reporting of CPU consumption statistics.
+Defaults to B<true>.
+
+=item B<Query> I<Querystring>
+
+The B<Query> block identifies a query to execute against the redis server.
+There may be an arbitrary number of queries to execute. Each query should
+return single string or integer.
=item B<Type> I<Collectd type>
-Within a query definition, a valid collectd type to use as when submitting
+Within a query definition, a valid I<collectd type> to use as when submitting
the result of the query. When not supplied, will default to B<gauge>.
+Currently only types with one datasource are supported.
+See L<types.db(5)> for more details on types and their configuration.
+
=item B<Instance> I<Type instance>
Within a query definition, an optional type instance to use when submitting
the result of the query. When not supplied will default to the escaped
-command, up to 64 chars.
+command, up to 128 chars.
+
+=item B<Database> I<Index>
+
+This index selects the Redis logical database to use for query. Defaults
+to C<0>.
=back
IndexOID "IF-MIB::ifIndex"
SizeOID "IF-MIB::ifNumber"
<Data "ifDescr">
- Instance true
+ <IndexKey>
+ Source "PluginInstance"
+ </IndexKey>
Plugin "interface"
OIDs "IF-MIB::ifDescr"
</Data>
OIDs "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
</Data>
</Table>
+ <Table "CPUAffinityTable">
+ <Data "DomainName">
+ <IndexKey>
+ Source "PluginInstance"
+ </IndexKey>
+ Plugin "virt"
+ OIDs "LIBVIRT-HYPERVISOR-MIB::lvhAffinityDomainName"
+ </Data>
+ <Data "VCPU">
+ Plugin "virt"
+ <IndexKey>
+ Source "TypeInstance"
+ Regex "^vcpu_([0-9]{1,3})-cpu_[0-9]{1,3}$"
+ Group 1
+ </IndexKey>
+ OIDs "LIBVIRT-HYPERVISOR-MIB::lvhVCPUIndex"
+ </Data>
+ <Data "CPU">
+ Plugin "virt"
+ <IndexKey>
+ Source "TypeInstance"
+ Regex "^vcpu_[0-9]{1,3}-cpu_([0-9]{1,3})$"
+ Group 1
+ </IndexKey>
+ OIDs "LIBVIRT-HYPERVISOR-MIB::lvhCPUIndex"
+ </Data>
+ <Data "CPUAffinity">
+ Plugin "virt"
+ Type "cpu_affinity"
+ OIDs "LIBVIRT-HYPERVISOR-MIB::lvhCPUAffinity"
+ </Data>
+ </Table>
</Plugin>
There are two types of blocks that can be contained in the
C<E<lt>PluginE<nbsp> snmp_agentE<gt>> block: B<Data> and B<Table>:
-=head3 The B<Data> block
+=head3 B<Data> block
The B<Data> block defines a list OIDs that are to be handled. This block can
define scalar or table OIDs. If B<Data> block is defined inside of B<Table>
=over 4
-=item B<Instance> I<true|false>
+=item B<IndexKey> block
+
+B<IndexKey> block contains all data needed for proper index build of snmp table.
+In case more than
+one table B<Data> block has B<IndexKey> block present then multiple key index is
+built. If B<Data> block defines scalar data type B<IndexKey> has no effect and can
+be omitted.
+
+=over 8
+
+=item B<Source> I<String>
+
+B<Source> can be set to one of the following values: "Hostname", "Plugin",
+"PluginInstance", "Type", "TypeInstance". This value indicates which field of
+corresponding collectd metric is taken as a SNMP table index.
-When B<Instance> is set to B<true>, the value for requested OID is copied from
-plugin instance field of corresponding collectd value. If B<Data> block defines
-scalar data type B<Instance> has no effect and can be omitted.
+=item B<Regex> I<String>
+
+B<Regex> option can also be used to parse strings or numbers out of
+specific field. For example: type-instance field which is "vcpu1-cpu2" can be
+parsed into two numeric fields CPU = 2 and VCPU = 1 and can be later used
+as a table index.
+
+=item B<Group> I<Number>
+
+B<Group> number can be specified in case groups are used in regex.
+
+=back
=item B<Plugin> I<String>
Protocol "tcp"
LogSendErrors true
Prefix "collectd"
+ UseTags false
</Node>
</Plugin>
=item B<Prefix> I<String>
-When set, I<String> is added in front of the host name. Dots and whitespace are
-I<not> escaped in this string (see B<EscapeCharacter> below).
+When B<UseTags> is I<false>, B<Prefix> value is added in front of the host name.
+When B<UseTags> is I<true>, B<Prefix> value is added in front of series name.
+
+Dots and whitespace are I<not> escaped in this string (see B<EscapeCharacter>
+below).
=item B<Postfix> I<String>
-When set, I<String> is appended to the host name. Dots and whitespace are
-I<not> escaped in this string (see B<EscapeCharacter> below).
+When B<UseTags> is I<false>, B<Postfix> value appended to the host name.
+When B<UseTags> is I<true>, B<Postgix> value appended to the end of series name
+(before the first ; that separates the name from the tags).
+
+Dots and whitespace are I<not> escaped in this string (see B<EscapeCharacter>
+below).
=item B<EscapeCharacter> I<Char>
default), the plugin and plugin instance (and likewise the type and type
instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
+Option value is not used when B<UseTags> is I<true>.
+
=item B<AlwaysAppendDS> B<false>|B<true>
If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
I<EscapeCharacter>. Otherwise, if set to B<true>, the C<.> (dot) character
is preserved, i.e. passed through.
+Option value is not used when B<UseTags> is I<true>.
+
=item B<DropDuplicateFields> B<false>|B<true>
If set to B<true>, detect and remove duplicate components in Graphite metric
names. For example, the metric name C<host.load.load.shortterm> will
be shortened to C<host.load.shortterm>.
+=item B<UseTags> B<false>|B<true>
+
+If set to B<true>, Graphite metric names will be generated as tagged series.
+This allows for much more flexibility than the traditional hierarchical layout.
+
+Example:
+C<test.single;host=example.com;plugin=test;plugin_instance=foo;type=single;type_instance=bar>
+
+You can use B<Postfix> option to add more tags by specifying it like
+C<;tag1=value1;tag2=value2>. Note what tagging support was added since Graphite
+version 1.1.x.
+
+If set to B<true>, the B<SeparateInstances> and B<PreserveSeparator> settings
+are not used.
+
+Default value: B<false>.
+
=back
=head2 Plugin C<write_log>
=item B<GraphitePrefix> (B<Format>=I<Graphite> only)
A prefix can be added in the metric name when outputting in the I<Graphite>
-format. It's added before the I<Host> name.
+format.
+
+When B<GraphiteUseTags> is I<false>, prefix is added before the I<Host> name.
Metric name will be
C<E<lt>prefixE<gt>E<lt>hostE<gt>E<lt>postfixE<gt>E<lt>pluginE<gt>E<lt>typeE<gt>E<lt>nameE<gt>>
+When B<GraphiteUseTags> is I<true>, prefix is added in front of series name.
+
=item B<GraphitePostfix> (B<Format>=I<Graphite> only)
A postfix can be added in the metric name when outputting in the I<Graphite>
-format. It's added after the I<Host> name.
+format.
+
+When B<GraphiteUseTags> is I<false>, postfix is added after the I<Host> name.
Metric name will be
C<E<lt>prefixE<gt>E<lt>hostE<gt>E<lt>postfixE<gt>E<lt>pluginE<gt>E<lt>typeE<gt>E<lt>nameE<gt>>
+When B<GraphiteUseTags> is I<true>, prefix value appended to the end of series
+name (before the first ; that separates the name from the tags).
+
=item B<GraphiteEscapeChar> (B<Format>=I<Graphite> only)
Specify a character to replace dots (.) in the host part of the metric name.
default), the plugin and plugin instance (and likewise the type and type
instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
+Option value is not used when B<GraphiteUseTags> is I<true>.
+
=item B<GraphiteAlwaysAppendDS> B<true>|B<false>
If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
is preserved, i.e. passed through.
+Option value is not used when B<GraphiteUseTags> is I<true>.
+
+=item B<GraphiteUseTags> B<false>|B<true>
+
+If set to B<true> Graphite metric names will be generated as tagged series.
+
+Default value: B<false>.
+
=item B<StoreRates> B<true>|B<false>
If set to B<true> (the default), convert counter values to rates. If set to
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;
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;
sfree(wp->buffer);
cc_web_match_free(wp->matches);
- cc_web_page_free(wp->next);
sfree(wp);
} /* }}} void cc_web_page_free */
static int cc_config_add_page(oconfig_item_t *ci) /* {{{ */
{
+ cdtime_t interval = 0;
web_page_t *page;
int status;
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) {
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 */
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 */
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;
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 */
char *cacert;
struct curl_slist *headers;
char *post_body;
- cdtime_t interval;
int timeout;
curl_stats_t *stats;
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;
}
#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()));
{
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 "
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) {
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,
});
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 */
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;
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) {
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,
});
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;
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;
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,
* 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;
}
* 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;
}
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;
*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;
}
#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;
}
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;
}
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;
}
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;
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;
}
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;
}
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;
}
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;
}
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
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 */
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;
}
#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 */
} /* 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) {
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 */
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;
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);
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);
} /* 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 "
/* 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);
* 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;
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;
}
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;
}
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;
}
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;
}
*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;
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;
}
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;
}
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;
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);
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;
}
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);
n = llist_size(*list);
if (n == 0) {
- INFO("%s [none]", comment);
+ INFO("%s: [none]", comment);
return;
}
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;
}
vl->time = cdtime();
/* Fill in the interval from the thread context, if it is zero. */
- if (vl->interval == 0) {
- plugin_ctx_t ctx = plugin_get_ctx();
-
- if (ctx.interval != 0)
- vl->interval = ctx.interval;
- else {
- char name[6 * DATA_MAX_NAME_LEN];
- FORMAT_VL(name, sizeof(name), vl);
- ERROR("plugin_value_list_clone: Unable to determine "
- "interval from context for "
- "value list \"%s\". "
- "This indicates a broken plugin. "
- "Please report this problem to the "
- "collectd mailing list or at "
- "<http://collectd.org/bugs/>.",
- name);
- vl->interval = cf_get_default_interval();
- }
- }
+ if (vl->interval == 0)
+ vl->interval = plugin_get_interval();
return vl;
} /* }}} value_list_t *plugin_value_list_clone */
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;
}
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) {
}
rf->rf_ctx = plugin_get_ctx();
+ rf->rf_ctx.interval = rf->rf_interval;
status = plugin_insert_read(rf);
if (status != 0) {
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;
else
success++;
+ plugin_set_ctx(old_ctx);
le = le->next;
}
}
} /* 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;
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;
}
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 */
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;
#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,
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;
}
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 */
}
* Florian octo Forster <octo at collectd.org>
*/
-#include <errno.h>
#include "utils_cache.h"
+#include <errno.h>
gauge_t *uc_get_rate(__attribute__((unused)) data_set_t const *ds,
__attribute__((unused)) value_list_t const *vl) {
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;
+}
char *select_db;
char *plugin_name;
- cdtime_t interval;
-
char *driver;
char *host;
cdbi_driver_option_t *driver_options;
static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */
{
+ cdtime_t interval = 0;
cdbi_database_t *db;
int status;
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 {
/* 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,
});
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.",
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");
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) &&
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)
* </Metric>
* </Plugin>
*/
-static int gmond_config_set_string(oconfig_item_t *ci, char **str) /* {{{ */
-{
- char *tmp;
-
- if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
- WARNING("gmond plugin: The `%s' option needs "
- "exactly one string argument.",
- ci->key);
- return -1;
- }
-
- tmp = strdup(ci->values[0].value.string);
- if (tmp == NULL) {
- ERROR("gmond plugin: strdup failed.");
- return -1;
- }
-
- sfree(*str);
- *str = tmp;
- return 0;
-} /* }}} int gmond_config_set_string */
-
static int gmond_config_add_metric(oconfig_item_t *ci) /* {{{ */
{
metric_map_t *map;
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);
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;
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;
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);
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;
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));
static int mb_config_add_host(oconfig_item_t *ci) /* {{{ */
{
+ cdtime_t interval = 0;
mb_host_t *host;
int status;
} 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);
plugin_register_complex_read(/* group = */ NULL, name,
/* callback = */ mb_read,
- /* interval = */ host->interval,
+ /* interval = */ interval,
&(user_data_t){
.data = host, .free_func = host_free,
});
/*
* Configuration handling
*/
-/* Sets a given flag if the boolean argument is true and unsets the flag if it
- * is false. On error, the flag-field is not changed. */
-static int cna_config_bool_to_flag(const oconfig_item_t *ci, /* {{{ */
- uint32_t *flags, uint32_t flag) {
- if ((ci == NULL) || (flags == NULL))
- return EINVAL;
-
- if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) {
- WARNING("netapp plugin: The %s option needs exactly one boolean argument.",
- ci->key);
- return -1;
- }
-
- if (ci->values[0].value.boolean)
- *flags |= flag;
- else
- *flags &= ~flag;
-
- return 0;
-} /* }}} int cna_config_bool_to_flag */
-
-/* Handling of the "Interval" option which is allowed in every block. */
-static int cna_config_get_interval(const oconfig_item_t *ci, /* {{{ */
- cna_interval_t *out_interval) {
- cdtime_t tmp = 0;
- int status;
-
- status = cf_util_get_cdtime(ci, &tmp);
- if (status != 0)
- return status;
-
- out_interval->interval = tmp;
- out_interval->last_read = 0;
-
- return 0;
-} /* }}} int cna_config_get_interval */
/* Handling of the "GetIO", "GetOps" and "GetLatency" options within a
* <VolumePerf /> block. */
/* 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"))
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.",
/* 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) {
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.",
/* 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"))
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.",
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.",
#endif
cdtime_t next_resolve_reconnect;
cdtime_t resolve_interval;
+ struct sockaddr_storage *bind_addr;
};
struct sockent_server {
sec->fd = -1;
}
sfree(sec->addr);
+ sfree(sec->bind_addr);
#if HAVE_GCRYPT_H
sfree(sec->username);
sfree(sec->password);
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
} 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
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.. */
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;
#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 {
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)
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);
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. */
"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);
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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;
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;
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"
" 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));
}
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.",
/* do not automatically get the thread specific Perl interpreter */
#define PERL_NO_GET_CONTEXT
+#include "collectd.h"
#include <stdbool.h>
#include <EXTERN.h>
/* 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;
pthread_mutex_init(&db->db_lock, /* attrs = */ NULL);
- db->interval = 0;
-
db->commit_interval = 0;
db->next_commit = 0;
db->expire_delay = 0;
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:
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);
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;
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)
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;
}
if (0 != parse_value(value_str, &value, ds->ds[0].type)) {
- ERROR("powerdns plugin: Cannot convert `%s' "
- "to a number.",
- value_str);
return;
}
status = parse_value(str_value, &value, DS_TYPE_DERIVE);
if (status != 0) {
- ERROR("protocols plugin: Parsing string as integer failed: %s", str_value);
return;
}
#include <hiredis/hiredis.h>
#include <sys/time.h>
-#ifndef HOST_NAME_MAX
-#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
-#endif
-
#define REDIS_DEF_HOST "localhost"
#define REDIS_DEF_PASSWD ""
#define REDIS_DEF_PORT 6379
-#define REDIS_DEF_TIMEOUT 2000
+#define REDIS_DEF_TIMEOUT_SEC 2
#define REDIS_DEF_DB_COUNT 256
-#define MAX_REDIS_NODE_NAME 64
-#define MAX_REDIS_PASSWD_LENGTH 512
#define MAX_REDIS_VAL_SIZE 256
#define MAX_REDIS_QUERY 2048
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) /* {{{ */
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.",
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) /* {{{ */
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;
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;
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;
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;
}
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;
}
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.
} /* }}} 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;
{
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 */
}
/* }}} */
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;
}
}
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;
#include "common.h"
#include "plugin.h"
#include "utils_complain.h"
+#include "utils_ignorelist.h"
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
};
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;
void *sess_handle;
c_complain_t complaint;
- cdtime_t interval;
data_definition_t **data_list;
int data_list_len;
};
/* 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
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. */
* 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
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) {
}
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) {
}
}
- 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) {
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)
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;
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)
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;
}
} /* 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;
} /* 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 {
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) {
int status = 0;
/* Registration stuff. */
+ cdtime_t interval = 0;
char cb_name[DATA_MAX_NAME_LEN];
hd = calloc(1, sizeof(*hd));
}
hd->sess_handle = NULL;
- hd->interval = 0;
/* These mean that we have not set a timeout or retry value */
hd->timeout = 0;
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)
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,
});
/* 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;
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;
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;
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. */
/* 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;
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);
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);
}
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);
}
/* 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;
}
/* 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);
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] */
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 */
}
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) {
host = ud->data;
- if (host->interval == 0)
- host->interval = plugin_get_interval();
-
if (host->sess_handle == NULL)
csnmp_host_open_session(host);
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;
}
/**
* collectd - src/snmp_agent.c
*
- * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ * Copyright(c) 2017-2018 Intel Corporation. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* Authors:
* Roman Korynkevych <romanx.korynkevych@intel.com>
* Serhiy Pshyk <serhiyx.pshyk@intel.com>
+ * Marcin Mozejko <marcinx.mozejko@intel.com>
**/
#include "collectd.h"
#include "utils_avltree.h"
#include "utils_cache.h"
#include "utils_llist.h"
+#include <regex.h>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#define PLUGIN_NAME "snmp_agent"
-#define ERR_BUF_SIZE 1024
#define TYPE_STRING -1
+#define GROUP_UNUSED -1
+#define OID_EXISTS 1
+#define MAX_KEY_SOURCES 5
+#define MAX_INDEX_KEYS 5
+#define MAX_MATCHES 5
+
+/* Identifies index key source */
+enum index_key_src_e {
+ INDEX_HOST = 0,
+ INDEX_PLUGIN,
+ INDEX_PLUGIN_INSTANCE,
+ INDEX_TYPE,
+ INDEX_TYPE_INSTANCE
+};
+typedef enum index_key_src_e index_key_src_t;
-#ifndef MIN
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-#endif
+struct index_key_s {
+ index_key_src_t source;
+ u_char type;
+ char *regex; /* Pattern used to parse index key source string */
+ int group; /* If pattern gives more than one group we can specify which one
+ we want to take */
+ regex_t regex_info;
+};
+typedef struct index_key_s index_key_t;
struct oid_s {
oid oid[MAX_OID_LEN];
};
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;
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;
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;
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) && \
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);
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;
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;
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;
}
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;
}
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,
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;
}
}
}
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);
}
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);
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);
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;
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);
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;
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;
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;
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;
}
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);
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;
}
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));
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;
}
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;
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);
}
}
+ 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)
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;
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;
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;
}
}
+ /* 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;
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;
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;
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;
}
}
}
}
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) {
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;
}
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;
}
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];
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) {
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 {
}
}
- ret = snmp_agent_validate_data();
+ ret = snmp_agent_validate_config();
if (ret != 0) {
ERROR(PLUGIN_NAME ": Invalid configuration provided");
snmp_agent_free_config();
--- /dev/null
+/**
+ * collectd - src/snmp_agent_test.c
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Marcin Mozejko <marcinx.mozejko@intel.com>
+ **/
+
+#include "snmp_agent.c"
+#include "testing.h"
+
+#define TEST_HOSTNAME "test_hostname"
+#define TEST_PLUGIN "test_plugin"
+#define TEST_PLUGIN_INST "test_plugin_inst"
+#define TEST_TYPE "test_type"
+#define TEST_TYPE_INST "test_type_inst"
+
+DEF_TEST(oid_to_string) {
+ oid_t o = {.oid = {1, 2, 3, 4, 5, 6, 7, 8, 9}, .oid_len = 9};
+ char oid_str[DATA_MAX_NAME_LEN];
+
+ int ret = snmp_agent_oid_to_string(oid_str, DATA_MAX_NAME_LEN, &o);
+ EXPECT_EQ_INT(o.oid_len * 2 - 1, ret);
+ EXPECT_EQ_STR("1.2.3.4.5.6.7.8.9", oid_str);
+
+ return 0;
+}
+
+/* Testing formatting metric name for simple scalar */
+DEF_TEST(format_name_scalar) {
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+
+ dd->plugin = TEST_PLUGIN;
+ dd->plugin_instance = TEST_PLUGIN_INST;
+ dd->type = TEST_TYPE;
+ dd->type_instance = TEST_TYPE_INST;
+
+ char name[DATA_MAX_NAME_LEN];
+ int ret = snmp_agent_format_name(name, sizeof(name), dd, NULL);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR(
+ "example.com/test_plugin-test_plugin_inst/test_type-test_type_inst",
+ name);
+
+ sfree(dd);
+
+ return 0;
+}
+
+DEF_TEST(format_name_simple_index) {
+ netsnmp_variable_list *index_list_tmp = NULL;
+ oid_t index_oid;
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+ table_definition_t *td = calloc(1, sizeof(*td));
+
+ td->index_list_cont = NULL;
+ td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[0].type = ASN_OCTET_STR;
+ td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[1].type = ASN_OCTET_STR;
+ dd->table = td;
+ dd->plugin = TEST_PLUGIN;
+ dd->type = TEST_TYPE;
+
+ const char plugin_inst[] = TEST_PLUGIN_INST;
+ const char type_inst[] = TEST_TYPE_INST;
+
+ snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+ (const u_char *)plugin_inst, strlen(plugin_inst));
+ snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+ (const u_char *)type_inst, strlen(type_inst));
+
+ build_oid_noalloc(index_oid.oid, sizeof(index_oid.oid), &index_oid.oid_len,
+ NULL, 0, index_list_tmp);
+
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+ 0);
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+ 0);
+
+ char name[DATA_MAX_NAME_LEN];
+
+ int ret = snmp_agent_format_name(name, DATA_MAX_NAME_LEN, dd, &index_oid);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR(
+ "example.com/test_plugin-test_plugin_inst/test_type-test_type_inst",
+ name);
+
+ snmp_free_varbind(index_list_tmp);
+ snmp_free_varbind(td->index_list_cont);
+ sfree(dd);
+ sfree(td);
+
+ return 0;
+}
+
+DEF_TEST(format_name_regex_index) {
+ netsnmp_variable_list *index_list_tmp = NULL;
+ oid_t index_oid;
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+ table_definition_t *td = calloc(1, sizeof(*td));
+
+ td->index_keys_len = 3;
+ td->index_list_cont = NULL;
+ td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[0].type = ASN_OCTET_STR;
+ td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[1].type = ASN_INTEGER;
+ td->index_keys[1].regex = "^vcpu_([0-9]{1,3})-cpu_[0-9]{1,3}$";
+ td->index_keys[1].group = 1;
+ td->index_keys[2].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[2].type = ASN_INTEGER;
+ td->index_keys[2].regex = "^vcpu_[0-9]{1,3}-cpu_([0-9]{1,3})$";
+ td->index_keys[2].group = 1;
+
+ dd->table = td;
+ dd->plugin = TEST_PLUGIN;
+ dd->type = TEST_TYPE;
+
+ const char plugin_inst[] = TEST_PLUGIN_INST;
+ int vcpu = 1;
+ int cpu = 10;
+
+ snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_OCTET_STR,
+ (const u_char *)plugin_inst, strlen(plugin_inst));
+ snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_INTEGER,
+ (const u_char *)&vcpu, sizeof(vcpu));
+ snmp_varlist_add_variable(&index_list_tmp, NULL, 0, ASN_INTEGER,
+ (const u_char *)&cpu, sizeof(cpu));
+
+ build_oid_noalloc(index_oid.oid, sizeof(index_oid.oid), &index_oid.oid_len,
+ NULL, 0, index_list_tmp);
+
+ token_t *token;
+ int *offset;
+
+ td->tokens[INDEX_TYPE_INSTANCE] =
+ c_avl_create((int (*)(const void *, const void *))num_compare);
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+ 0);
+
+ token = malloc(sizeof(*token));
+ offset = malloc(sizeof(*offset));
+ token->key = snmp_varlist_add_variable(&td->index_list_cont, NULL, 0,
+ ASN_INTEGER, NULL, 0);
+ token->str = strdup("vcpu_");
+ *offset = 0;
+ int ret = c_avl_insert(td->tokens[INDEX_TYPE_INSTANCE], (void *)offset,
+ (void *)token);
+
+ token = malloc(sizeof(*token));
+ offset = malloc(sizeof(*offset));
+ token->key = snmp_varlist_add_variable(&td->index_list_cont, NULL, 0,
+ ASN_INTEGER, NULL, 0);
+ token->str = strdup("-cpu_");
+ *offset = 6;
+ ret += c_avl_insert(td->tokens[INDEX_TYPE_INSTANCE], (void *)offset,
+ (void *)token);
+ char name[DATA_MAX_NAME_LEN];
+
+ ret += snmp_agent_format_name(name, DATA_MAX_NAME_LEN, dd, &index_oid);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR(
+ "example.com/test_plugin-test_plugin_inst/test_type-vcpu_1-cpu_10", name);
+ while (c_avl_pick(td->tokens[INDEX_TYPE_INSTANCE], (void **)&offset,
+ (void **)&token) == 0) {
+ sfree(offset);
+ sfree(token->str);
+ sfree(token);
+ }
+ c_avl_destroy(td->tokens[INDEX_TYPE_INSTANCE]);
+ snmp_free_varbind(index_list_tmp);
+ snmp_free_varbind(td->index_list_cont);
+ sfree(dd);
+ sfree(td);
+
+ return 0;
+}
+
+DEF_TEST(prep_index_list) {
+ table_definition_t *td = calloc(1, sizeof(*td));
+
+ assert(td != NULL);
+ td->index_keys_len = 5;
+ td->index_keys[0].source = INDEX_HOST;
+ td->index_keys[0].type = ASN_OCTET_STR;
+ td->index_keys[1].source = INDEX_PLUGIN;
+ td->index_keys[1].type = ASN_OCTET_STR;
+ td->index_keys[2].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[2].type = ASN_INTEGER;
+ td->index_keys[3].source = INDEX_TYPE;
+ td->index_keys[3].type = ASN_INTEGER;
+ td->index_keys[4].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[4].type = ASN_OCTET_STR;
+ td->index_list_cont = NULL;
+
+ int ret = snmp_agent_prep_index_list(td, &td->index_list_cont);
+ EXPECT_EQ_INT(0, ret);
+
+ netsnmp_variable_list *key = td->index_list_cont;
+
+ OK(key != NULL);
+ EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(ASN_INTEGER, key->type);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(ASN_INTEGER, key->type);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(ASN_OCTET_STR, key->type);
+ key = key->next_variable;
+ OK(key == NULL);
+
+ snmp_free_varbind(td->index_list_cont);
+ sfree(td);
+
+ return 0;
+}
+
+DEF_TEST(fill_index_list_simple) {
+ table_definition_t *td = calloc(1, sizeof(*td));
+ assert(td != NULL);
+
+ /* Preparing value list */
+ value_list_t *vl = calloc(1, sizeof(*vl));
+ assert(vl != NULL);
+ strncpy(vl->host, TEST_HOSTNAME, DATA_MAX_NAME_LEN);
+ strncpy(vl->plugin, TEST_PLUGIN, DATA_MAX_NAME_LEN);
+ strncpy(vl->plugin_instance, TEST_PLUGIN_INST, DATA_MAX_NAME_LEN);
+ strncpy(vl->type, TEST_TYPE, DATA_MAX_NAME_LEN);
+ strncpy(vl->type_instance, TEST_TYPE_INST, DATA_MAX_NAME_LEN);
+
+ td->index_keys_len = 5;
+ td->index_keys[0].source = INDEX_HOST;
+ td->index_keys[0].type = ASN_OCTET_STR;
+ td->index_keys[1].source = INDEX_PLUGIN;
+ td->index_keys[1].type = ASN_OCTET_STR;
+ td->index_keys[2].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[2].type = ASN_OCTET_STR;
+ td->index_keys[3].source = INDEX_TYPE;
+ td->index_keys[3].type = ASN_OCTET_STR;
+ td->index_keys[4].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[4].type = ASN_OCTET_STR;
+
+ td->index_list_cont = NULL;
+ for (int i = 0; i < td->index_keys_len; i++)
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR,
+ NULL, 0);
+
+ int ret = snmp_agent_fill_index_list(td, vl);
+ EXPECT_EQ_INT(0, ret);
+
+ netsnmp_variable_list *key = td->index_list_cont;
+
+ ret = 0;
+
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->host, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->plugin, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->plugin_instance, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->type, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->type_instance, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key == NULL);
+
+ snmp_free_varbind(td->index_list_cont);
+ sfree(vl);
+ sfree(td);
+
+ return 0;
+}
+
+DEF_TEST(fill_index_list_regex) {
+ table_definition_t *td = calloc(1, sizeof(*td));
+ int ret = 0;
+
+ assert(td != NULL);
+
+ /* Preparing value list */
+ value_list_t *vl = calloc(1, sizeof(*vl));
+ strncpy(vl->plugin_instance, TEST_PLUGIN_INST, DATA_MAX_NAME_LEN);
+ strncpy(vl->type_instance, "1test2test3", DATA_MAX_NAME_LEN);
+
+ td->index_keys_len = 4;
+ td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[0].type = ASN_OCTET_STR;
+ td->index_keys[1].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[1].type = ASN_INTEGER;
+ td->index_keys[1].regex = "^([0-9])test[0-9]test[0-9]$";
+ td->index_keys[1].group = 1;
+ td->index_keys[2].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[2].type = ASN_INTEGER;
+ td->index_keys[2].regex = "^[0-9]test([0-9])test[0-9]$";
+ td->index_keys[2].group = 1;
+ td->index_keys[3].source = INDEX_TYPE_INSTANCE;
+ td->index_keys[3].type = ASN_INTEGER;
+ td->index_keys[3].regex = "^[0-9]test[0-9]test([0-9])$";
+ td->index_keys[3].group = 1;
+
+ td->index_list_cont = NULL;
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_OCTET_STR, NULL,
+ 0);
+ for (int i = 1; i < td->index_keys_len; i++) {
+ snmp_varlist_add_variable(&td->index_list_cont, NULL, 0, ASN_INTEGER, NULL,
+ 0);
+ ret = regcomp(&td->index_keys[i].regex_info, td->index_keys[i].regex,
+ REG_EXTENDED);
+ EXPECT_EQ_INT(0, ret);
+ }
+ td->tokens[INDEX_TYPE_INSTANCE] =
+ c_avl_create((int (*)(const void *, const void *))num_compare);
+ assert(td->tokens[INDEX_TYPE_INSTANCE] != NULL);
+
+ ret = snmp_agent_fill_index_list(td, vl);
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(1, td->tokens_done);
+
+ netsnmp_variable_list *key = td->index_list_cont;
+
+ OK(key != NULL);
+ EXPECT_EQ_STR(vl->plugin_instance, (char *)key->val.string);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(1, *key->val.integer);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(2, *key->val.integer);
+ key = key->next_variable;
+ OK(key != NULL);
+ EXPECT_EQ_INT(3, *key->val.integer);
+ key = key->next_variable;
+ OK(key == NULL);
+
+ token_t *token;
+ int *offset;
+
+ while (c_avl_pick(td->tokens[INDEX_TYPE_INSTANCE], (void **)&offset,
+ (void **)&token) == 0) {
+ sfree(offset);
+ sfree(token->str);
+ sfree(token);
+ }
+
+ c_avl_destroy(td->tokens[INDEX_TYPE_INSTANCE]);
+ snmp_free_varbind(td->index_list_cont);
+ sfree(vl);
+
+ for (int i = 0; i < td->index_keys_len; i++) {
+ regfree(&td->index_keys[i].regex_info);
+ }
+ sfree(td);
+
+ return 0;
+}
+
+DEF_TEST(config_index_key_source) {
+ oconfig_item_t *ci = calloc(1, sizeof(*ci));
+ table_definition_t *td = calloc(1, sizeof(*td));
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+
+ assert(ci != NULL);
+ assert(td != NULL);
+ assert(dd != NULL);
+
+ ci->values = calloc(1, sizeof(*ci->values));
+ assert(ci->values != NULL);
+ ci->values_num = 1;
+ ci->values->value.string = "PluginInstance";
+ ci->values->type = OCONFIG_TYPE_STRING;
+
+ int ret = snmp_agent_config_index_key_source(td, dd, ci);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(1, td->index_keys_len);
+ EXPECT_EQ_INT(0, dd->index_key_pos);
+ EXPECT_EQ_INT(INDEX_PLUGIN_INSTANCE, td->index_keys[0].source);
+ EXPECT_EQ_INT(GROUP_UNUSED, td->index_keys[0].group);
+ OK(td->index_keys[0].regex == NULL);
+
+ sfree(ci->values);
+ sfree(ci);
+ sfree(td);
+ sfree(dd);
+
+ return 0;
+}
+
+DEF_TEST(config_index_key_regex) {
+ oconfig_item_t *ci = calloc(1, sizeof(*ci));
+ table_definition_t *td = calloc(1, sizeof(*td));
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+
+ assert(ci != NULL);
+ assert(td != NULL);
+ assert(dd != NULL);
+
+ dd->index_key_pos = 0;
+ td->index_keys_len = 1;
+ td->index_keys[0].source = INDEX_PLUGIN_INSTANCE;
+ td->index_keys[0].group = 1;
+ ci->values = calloc(1, sizeof(*ci->values));
+ assert(ci->values != NULL);
+ ci->values_num = 1;
+ ci->values->value.string = "^([0-9])test[0-9]test[0-9]$";
+ ci->values->type = OCONFIG_TYPE_STRING;
+
+ int ret = snmp_agent_config_index_key_regex(td, dd, ci);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_STR(td->index_keys[0].regex, "^([0-9])test[0-9]test[0-9]$");
+ OK(td->tokens[INDEX_PLUGIN_INSTANCE] != NULL);
+
+ c_avl_destroy(td->tokens[INDEX_PLUGIN_INSTANCE]);
+ sfree(ci->values);
+ sfree(ci);
+ sfree(td->index_keys[0].regex);
+ regfree(&td->index_keys[0].regex_info);
+ sfree(td);
+ sfree(dd);
+
+ return 0;
+}
+
+DEF_TEST(config_index_key) {
+ oconfig_item_t *ci = calloc(1, sizeof(*ci));
+ table_definition_t *td = calloc(1, sizeof(*td));
+ data_definition_t *dd = calloc(1, sizeof(*dd));
+
+ assert(ci != NULL);
+ assert(td != NULL);
+ assert(dd != NULL);
+
+ ci->children_num = 3;
+ ci->children = calloc(1, sizeof(*ci->children) * ci->children_num);
+
+ ci->children[0].key = "Source";
+ ci->children[0].parent = ci;
+ ci->children[0].values_num = 1;
+ ci->children[0].values = calloc(1, sizeof(*ci->children[0].values));
+ assert(ci->children[0].values != NULL);
+ ci->children[0].values->value.string = "PluginInstance";
+ ci->children[0].values->type = OCONFIG_TYPE_STRING;
+
+ ci->children[1].key = "Regex";
+ ci->children[1].parent = ci;
+ ci->children[1].values_num = 1;
+ ci->children[1].values = calloc(1, sizeof(*ci->children[0].values));
+ assert(ci->children[1].values != NULL);
+ ci->children[1].values->value.string = "^([0-9])test[0-9]test[0-9]$";
+ ci->children[1].values->type = OCONFIG_TYPE_STRING;
+
+ ci->children[2].key = "Group";
+ ci->children[2].parent = ci;
+ ci->children[2].values_num = 1;
+ ci->children[2].values = calloc(1, sizeof(*ci->children[0].values));
+ assert(ci->children[2].values != NULL);
+ ci->children[2].values->value.number = 1;
+ ci->children[2].values->type = OCONFIG_TYPE_NUMBER;
+
+ int ret = snmp_agent_config_index_key(td, dd, ci);
+
+ EXPECT_EQ_INT(0, ret);
+ EXPECT_EQ_INT(1, td->index_keys_len);
+ EXPECT_EQ_INT(0, dd->index_key_pos);
+ EXPECT_EQ_INT(INDEX_PLUGIN_INSTANCE, td->index_keys[0].source);
+ EXPECT_EQ_INT(1, td->index_keys[0].group);
+ EXPECT_EQ_STR("^([0-9])test[0-9]test[0-9]$", td->index_keys[0].regex);
+ OK(td->tokens[INDEX_PLUGIN_INSTANCE] != NULL);
+
+ sfree(ci->children[0].values);
+ sfree(ci->children[1].values);
+ sfree(ci->children[2].values);
+
+ sfree(ci->children);
+ sfree(ci);
+
+ c_avl_destroy(td->tokens[INDEX_PLUGIN_INSTANCE]);
+ sfree(dd);
+ sfree(td->index_keys[0].regex);
+ regfree(&td->index_keys[0].regex_info);
+ sfree(td);
+
+ return 0;
+}
+
+DEF_TEST(parse_index_key) {
+ const char regex[] = "test-([0-9])-([0-9])";
+ const char input[] = "snmp-test-5-6";
+ regex_t regex_info;
+ regmatch_t match;
+
+ int ret = regcomp(®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;
+}
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) {
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;
}
/*
* 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) {
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);
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
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) {
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;
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.");
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.. */
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 */
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 */
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;
};
sstrncpy(vl.type_instance, md->instance, sizeof(vl.type_instance));
vl.time = t;
- vl.interval = id->interval;
return plugin_dispatch_values(&vl);
}
int status = 0;
/* Registration variables */
+ cdtime_t interval = 0;
char cb_name[DATA_MAX_NAME_LEN];
id = calloc(1, sizeof(*id));
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;
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)
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,
});
* 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;
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)
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.",
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;
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.",
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
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
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;
}
}
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;
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;
}
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;
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;
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));
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;
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;
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;
}
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);
/* 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);
}
/* }}} */
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);
}
}
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++) */
}
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++) */
}
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++) */
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;
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)
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;
}
/* 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;
}
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;
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;
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)
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;
}
/* 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) */
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;
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;
}
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;
}
} /* for (i = 0; i < src_list_len; i++) */
if (num_added <= 0) {
- ERROR("db query utils: Cannot find query `%s'. Make sure the <Query> "
- "block is above the database definition!",
- name);
+ P_ERROR("Cannot find query `%s'. Make sure the <Query> "
+ "block is above the database definition!",
+ name);
return -ENOENT;
} else {
- DEBUG("db query utils: Added %i versions of query `%s'.", num_added, name);
+ DEBUG("Added %i versions of query `%s'.", num_added, name);
}
return 0;
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;
sfree(prep_area->plugin);
sfree(prep_area->db_name);
- prep_area->interval = 0;
-
for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL;
r = r->next, r_area = r_area->next) {
/* this may happen during error conditions of the caller */
if ((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]);
}
}
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;
}
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;
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;
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;
}
#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]);
}
}
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;
}
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;
}
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);
}
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",
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;
}
}
}
+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,
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;
}
}
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;
}
(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;
}
#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,
.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++) {
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;
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;
}
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;
}
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;
}
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;
}
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;
}
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);
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;
}
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);
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;
}
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);
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);
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;
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;
}
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;
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);
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;
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;
};
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);
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 */
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;
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;
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);
* 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.
* 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.
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
* 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.
* Protocol "udp"
* LogSendErrors true
* Prefix "collectd"
+ * UseTags true
* </Carbon>
* </Plugin>
*/
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 {
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) {
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;
}
} 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) {
} 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;
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)
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)