author | Florian Forster <octo@leeloo.lan.home.verplant.org> | |
Tue, 24 Feb 2009 09:15:52 +0000 (10:15 +0100) | ||
committer | Florian Forster <octo@leeloo.lan.home.verplant.org> | |
Tue, 24 Feb 2009 09:15:52 +0000 (10:15 +0100) |
15 files changed:
configure.in | patch | blob | history | |
src/Makefile.am | patch | blob | history | |
src/bind.c | patch | blob | history | |
src/collectd-perl.pod | patch | blob | history | |
src/collectd.conf.pod | patch | blob | history | |
src/common.c | patch | blob | history | |
src/common.h | patch | blob | history | |
src/liboconfig/oconfig.c | patch | blob | history | |
src/liboconfig/oconfig.h | patch | blob | history | |
src/perl.c | patch | blob | history | |
src/powerdns.c | patch | blob | history | |
src/table.c | [new file with mode: 0644] | patch | blob |
src/types.db.pod | patch | blob | history | |
src/utils_cmd_putval.c | patch | blob | history | |
src/utils_db_query.c | patch | blob | history |
diff --git a/configure.in b/configure.in
index e2d4f8ce5bf4842db9a73f01d90fbb552219b82d..76d686f3257698823ffc69fda59e0f6a0b88afe5 100644 (file)
--- a/configure.in
+++ b/configure.in
AC_PLUGIN([snmp], [$with_libnetsnmp], [SNMP querying plugin])
AC_PLUGIN([swap], [$plugin_swap], [Swap usage statistics])
AC_PLUGIN([syslog], [$have_syslog], [Syslog logging plugin])
+AC_PLUGIN([table], [yes], [Parsing of tabular data])
AC_PLUGIN([tail], [yes], [Parsing of logfiles])
AC_PLUGIN([tape], [$plugin_tape], [Tape drive statistics])
AC_PLUGIN([target_notification], [yes], [The notification target])
snmp . . . . . . . . $enable_snmp
swap . . . . . . . . $enable_swap
syslog . . . . . . . $enable_syslog
+ table . . . . . . . . $enable_table
tail . . . . . . . . $enable_tail
tape . . . . . . . . $enable_tape
target_notification . $enable_target_notification
diff --git a/src/Makefile.am b/src/Makefile.am
index f81fbbde802d31d3cab7dae277d416ae7ac3d3bb..aabb9b75d80d8555f4dbea3b4350a42ad9c0a254 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
collectd_DEPENDENCIES += syslog.la
endif
+if BUILD_PLUGIN_TABLE
+pkglib_LTLIBRARIES += table.la
+table_la_SOURCES = table.c
+table_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" table.la
+collectd_DEPENDENCIES += table.la
+endif
+
if BUILD_PLUGIN_TAIL
pkglib_LTLIBRARIES += tail.la
tail_la_SOURCES = tail.c
diff --git a/src/bind.c b/src/bind.c
index b464f8de64841f1b389a87e36eb85958db96018f..4b3662f9819f867abd8161aeb29f4817ce5641b6 100644 (file)
--- a/src/bind.c
+++ b/src/bind.c
STATIC_ARRAY_SIZE (memsummary_translation_table);
/* }}} */
-static void remove_special (char *buffer, size_t buffer_size) /* {{{ */
-{
- size_t i;
-
- for (i = 0; i < buffer_size; i++)
- {
- if (buffer[i] == 0)
- return;
- if ((!isalnum ((int) buffer[i])) && (buffer[i] != '-'))
- buffer[i] = '_';
- }
-} /* }}} void remove_special */
-
static void submit (time_t ts, const char *plugin_instance, /* {{{ */
const char *type, const char *type_instance, value_t value)
{
if (plugin_instance) {
sstrncpy(vl.plugin_instance, plugin_instance,
sizeof(vl.plugin_instance));
- remove_special (vl.plugin_instance, sizeof (vl.plugin_instance));
+ replace_special (vl.plugin_instance, sizeof (vl.plugin_instance));
}
sstrncpy(vl.type, type, sizeof(vl.type));
if (type_instance) {
sstrncpy(vl.type_instance, type_instance,
sizeof(vl.type_instance));
- remove_special (vl.plugin_instance, sizeof (vl.plugin_instance));
+ replace_special (vl.plugin_instance, sizeof (vl.plugin_instance));
}
plugin_dispatch_values(&vl);
} /* }}} void submit */
diff --git a/src/collectd-perl.pod b/src/collectd-perl.pod
index acb8abda9a3825b23e4f7b1395577c2c3ff07982..0f48ba58527faabc67fbd6f7bd1ca981bab8071d 100644 (file)
--- a/src/collectd-perl.pod
+++ b/src/collectd-perl.pod
file which are automatically registered with collectd - see L<types.db(5)> for
a description of the format of this file.
+B<Note>: Using B<plugin_register> to register a data-set is deprecated. Add
+the new type to a custom L<types.db(5)> file instead. This functionality might
+be removed in a future version of collectd.
+
If the I<type> argument is any of the other types (B<TYPE_INIT>, B<TYPE_READ>,
...) then I<data> is expected to be a function name. If the name is not
prefixed with the plugin's package name collectd will add it automatically.
diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod
index fa8910174624bbcc69fa61b0e63cdc5b506e8e99..962fcfe7b9ae27261c8074aea6d2f42294cb8aca 100644 (file)
--- a/src/collectd.conf.pod
+++ b/src/collectd.conf.pod
Specifies the columns whose values will be used to create the "type-instance"
for each row. If you specify more than one column, the value of all columns
-will be join together with the dashes I<("-")> as separation character.
+will be joined together with dashes I<("-")> as separation characters.
The plugin itself does not check whether or not all built instances are
different. It's your responsibility to assure that each is unique. This is
=back
+=head2 Plugin C<table>
+
+The C<table plugin> provides generic means to parse tabular data and dispatch
+user specified values. Values are selected based on column numbers. For
+example, this plugin may be used to get values from the Linux L<proc(5)>
+filesystem or CSV (comma separated values) files.
+
+ <Plugin table>
+ <Table "/proc/slabinfo">
+ Instance "slabinfo"
+ Separator " "
+ <Result>
+ Type gauge
+ InstancePrefix "active_objs"
+ InstancesFrom 0
+ ValuesFrom 1
+ </Result>
+ <Result>
+ Type gauge
+ InstancePrefix "objperslab"
+ InstancesFrom 0
+ ValuesFrom 4
+ </Result>
+ </Table>
+ </Plugin>
+
+The configuration consists of one or more B<Table> blocks, each of which
+configures one file to parse. Within each B<Table> block, there are one or
+more B<Result> blocks, which configure which data to select and how to
+interpret it.
+
+The following options are available inside a B<Table> block:
+
+=over 4
+
+=item B<Instance> I<instance>
+
+If specified, I<instance> is used as the plugin instance. So, in the above
+example, the plugin name C<table-slabinfo> would be used. If omitted, the
+filename of the table is used instead, with all special characters replaced
+with an underscore (C<_>).
+
+=item B<Separator> I<string>
+
+Any character of I<string> is interpreted as a delimiter between the different
+columns of the table. A sequence of two or more contiguous delimiters in the
+table is considered to be a single delimiter, i.E<nbsp>e. there cannot be any
+empty columns. The plugin uses the L<strtok_r(3)> function to parse the lines
+of a table - see its documentation for more details. This option is mandatory.
+
+A horizontal tab, newline and carriage return may be specified by C<\\t>,
+C<\\n> and C<\\r> respectively. Please note that the double backslashes are
+required because of collectd's config parsing.
+
+=back
+
+The following options are available inside a B<Result> block:
+
+=over 4
+
+=item B<Type> I<type>
+
+Sets the type used to dispatch the values to the daemon. Detailed information
+about types and their configuration can be found in L<types.db(5)>. This
+option is mandatory.
+
+=item B<InstancePrefix> I<prefix>
+
+If specified, prepend I<prefix> to the type instance. If omitted, only the
+B<InstancesFrom> option is considered for the type instance.
+
+=item B<InstancesFrom> I<column0> [I<column1> ...]
+
+If specified, the content of the given columns (identified by the column
+number starting at zero) will be used to create the type instance for each
+row. Multiple values (and the instance prefix) will be joined together with
+dashes (I<->) as separation character. If omitted, only the B<InstancePrefix>
+option is considered for the type instance.
+
+The plugin itself does not check whether or not all built instances are
+different. It’s your responsibility to assure that each is unique. This is
+especially true, if you do not specify B<InstancesFrom>: B<You> have to make
+sure that the table only contains one row.
+
+If neither B<InstancePrefix> nor B<InstancesFrom> is given, the type instance
+will be empty.
+
+=item B<ValuesFrom> I<column0> [I<column1> ...]
+
+Specifies the columns (identified by the column numbers starting at zero)
+whose content is used as the actual data for the data sets that are dispatched
+to the daemon. How many such columns you need is determined by the B<Type>
+setting above. If you specify too many or not enough columns, the plugin will
+complain about that and no data will be submitted to the daemon. The plugin
+uses L<strtoll(3)> and L<strtod(3)> to parse counter and gauge values
+respectively, so anything supported by those functions is supported by the
+plugin as well. This option is mandatory.
+
+=back
+
=head2 Plugin C<tail>
-The C<tail plugin> plugins follows logfiles, just like L<tail(1)> does, parses
+The C<tail plugin> follows logfiles, just like L<tail(1)> does, parses
each line and dispatches found values. What is matched can be configured by the
user using (extended) regular expressions, as described in L<regex(7)>.
diff --git a/src/common.c b/src/common.c
index 9a20f78af559d5590fdff03c4a0917bb3b5a7c98..4aa0ebe26a1f2fb3140746698d596486956e6440 100644 (file)
--- a/src/common.c
+++ b/src/common.c
return (ret);
} /* int strsubstitute */
+int strunescape (char *buf, size_t buf_len)
+{
+ size_t i;
+
+ for (i = 0; (i < buf_len) && (buf[i] != '\0'); ++i)
+ {
+ if (buf[i] != '\\')
+ continue;
+
+ if ((i >= buf_len) || (buf[i + 1] == '\0')) {
+ ERROR ("string unescape: backslash found at end of string.");
+ return (-1);
+ }
+
+ switch (buf[i + 1]) {
+ case 't':
+ buf[i] = '\t';
+ break;
+ case 'n':
+ buf[i] = '\n';
+ break;
+ case 'r':
+ buf[i] = '\r';
+ break;
+ default:
+ buf[i] = buf[i + 1];
+ break;
+ }
+
+ memmove (buf + i + 1, buf + i + 2, buf_len - i - 2);
+ }
+ return (0);
+} /* int strunescape */
+
int escape_slashes (char *buf, int buf_len)
{
int i;
return (0);
} /* int escape_slashes */
+void replace_special (char *buffer, size_t buffer_size)
+{
+ size_t i;
+
+ for (i = 0; i < buffer_size; i++)
+ {
+ if (buffer[i] == 0)
+ return;
+ if ((!isalnum ((int) buffer[i])) && (buffer[i] != '-'))
+ buffer[i] = '_';
+ }
+} /* void replace_special */
+
int timeval_cmp (struct timeval tv0, struct timeval tv1, struct timeval *delta)
{
struct timeval *larger;
return (0);
} /* int parse_identifier */
+int parse_value (const char *value, value_t *ret_value, const data_source_t ds)
+{
+ char *endptr = NULL;
+
+ if (DS_TYPE_COUNTER == ds.type)
+ ret_value->counter = (counter_t)strtoll (value, &endptr, 0);
+ else if (DS_TYPE_GAUGE == ds.type)
+ ret_value->gauge = (gauge_t)strtod (value, &endptr);
+ else {
+ ERROR ("parse_value: Invalid data source \"%s\" "
+ "(type = %i).", ds.name, ds.type);
+ return -1;
+ }
+
+ if (value == endptr) {
+ ERROR ("parse_value: Failed to parse string as number: %s.", value);
+ return -1;
+ }
+ else if ((NULL != endptr) && ('\0' != *endptr))
+ WARNING ("parse_value: Ignoring trailing garbage after number: %s.",
+ endptr);
+ return 0;
+} /* int parse_value */
+
int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
{
int i;
}
else
{
- if (strcmp ("U", ptr) == 0)
+ if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
vl->values[i].gauge = NAN;
- else if (ds->ds[i].type == DS_TYPE_COUNTER)
- vl->values[i].counter = atoll (ptr);
- else if (ds->ds[i].type == DS_TYPE_GAUGE)
- vl->values[i].gauge = atof (ptr);
+ else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i]))
+ return -1;
}
i++;
diff --git a/src/common.h b/src/common.h
index 85db3adb35faf5981477b1dd90179f6aaffa9c6b..72098083e53be05744a6b78e806bc9413a4c1b8d 100644 (file)
--- a/src/common.h
+++ b/src/common.h
@@ -158,8 +158,48 @@ int strjoin (char *dst, size_t dst_len, char **fields, size_t fields_num, const
*/
int escape_slashes (char *buf, int buf_len);
+/*
+ * NAME
+ * replace_special
+ *
+ * DESCRIPTION
+ * Replaces any special characters (anything that's not alpha-numeric or a
+ * dash) with an underscore.
+ *
+ * E.g. "foo$bar&" would become "foo_bar_".
+ *
+ * PARAMETERS
+ * `buffer' String to be handled.
+ * `buffer_size' Length of the string. The function returns after
+ * encountering a null-byte or reading this many bytes.
+ */
+void replace_special (char *buffer, size_t buffer_size);
+
int strsubstitute (char *str, char c_from, char c_to);
+/*
+ * NAME
+ * strunescape
+ *
+ * DESCRIPTION
+ * Replaces any escaped characters in a string with the appropriate special
+ * characters. The following escaped characters are recognized:
+ *
+ * \t -> <tab>
+ * \n -> <newline>
+ * \r -> <carriage return>
+ *
+ * For all other escacped characters only the backslash will be removed.
+ *
+ * PARAMETERS
+ * `buf' String to be unescaped.
+ * `buf_len' Length of the string, including the terminating null-byte.
+ *
+ * RETURN VALUE
+ * Returns zero upon success, a value less than zero else.
+ */
+int strunescape (char *buf, size_t buf_len);
+
/*
* NAME
* timeval_cmp
int parse_identifier (char *str, char **ret_host,
char **ret_plugin, char **ret_plugin_instance,
char **ret_type, char **ret_type_instance);
+int parse_value (const char *value, value_t *ret_value, const data_source_t ds);
int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds);
#if !HAVE_GETPWNAM_R
index 79b53aec8825f858c87d4b83682b9b99450496be..629775ab8c44dd55de5d930d0189168546d455c3 100644 (file)
--- a/src/liboconfig/oconfig.c
+++ b/src/liboconfig/oconfig.c
return (ret);
} /* oconfig_item_t *oconfig_parse_file */
+oconfig_item_t *oconfig_clone (const oconfig_item_t *ci_orig)
+{
+ oconfig_item_t *ci_copy;
+
+ ci_copy = (oconfig_item_t *) malloc (sizeof (*ci_copy));
+ if (ci_copy == NULL)
+ {
+ fprintf (stderr, "malloc failed.\n");
+ return (NULL);
+ }
+ memset (ci_copy, 0, sizeof (*ci_copy));
+ ci_copy->values = NULL;
+ ci_copy->parent = NULL;
+ ci_copy->children = NULL;
+
+ ci_copy->key = strdup (ci_orig->key);
+ if (ci_copy->key == NULL)
+ {
+ fprintf (stderr, "strdup failed.\n");
+ free (ci_copy);
+ return (NULL);
+ }
+
+ if (ci_orig->values_num > 0) /* {{{ */
+ {
+ int i;
+
+ ci_copy->values = (oconfig_value_t *) calloc (ci_orig->values_num,
+ sizeof (*ci_copy->values));
+ if (ci_copy->values == NULL)
+ {
+ fprintf (stderr, "calloc failed.\n");
+ free (ci_copy->key);
+ free (ci_copy);
+ return (NULL);
+ }
+ ci_copy->values_num = ci_orig->values_num;
+
+ for (i = 0; i < ci_copy->values_num; i++)
+ {
+ ci_copy->values[i].type = ci_orig->values[i].type;
+ if (ci_copy->values[i].type == OCONFIG_TYPE_STRING)
+ {
+ ci_copy->values[i].value.string
+ = strdup (ci_orig->values[i].value.string);
+ if (ci_copy->values[i].value.string == NULL)
+ {
+ fprintf (stderr, "strdup failed.\n");
+ oconfig_free (ci_copy);
+ return (NULL);
+ }
+ }
+ else /* ci_copy->values[i].type != OCONFIG_TYPE_STRING) */
+ {
+ ci_copy->values[i].value = ci_orig->values[i].value;
+ }
+ }
+ } /* }}} if (ci_orig->values_num > 0) */
+
+ if (ci_orig->children_num > 0) /* {{{ */
+ {
+ int i;
+
+ ci_copy->children = (oconfig_item_t *) calloc (ci_orig->children_num,
+ sizeof (*ci_copy->children));
+ if (ci_copy->children == NULL)
+ {
+ fprintf (stderr, "calloc failed.\n");
+ oconfig_free (ci_copy);
+ return (NULL);
+ }
+ ci_copy->children_num = ci_orig->children_num;
+
+ for (i = 0; i < ci_copy->children_num; i++)
+ {
+ oconfig_item_t *child;
+
+ child = oconfig_clone (ci_orig->children + i);
+ if (child == NULL)
+ {
+ oconfig_free (ci_copy);
+ return (NULL);
+ }
+ child->parent = ci_copy;
+ ci_copy->children[i] = *child;
+ free (child);
+ } /* for (i = 0; i < ci_copy->children_num; i++) */
+ } /* }}} if (ci_orig->children_num > 0) */
+
+ return (ci_copy);
+} /* oconfig_item_t *oconfig_clone */
+
void oconfig_free (oconfig_item_t *ci)
{
int i;
}
/*
- * vim:shiftwidth=2:tabstop=8:softtabstop=2
+ * vim:shiftwidth=2:tabstop=8:softtabstop=2:fdm=marker
*/
index e6e53e27020d8e773072a232401c018c1144bc43..70fc6230fc5753916c8c03d211c78695fda3b0f7 100644 (file)
--- a/src/liboconfig/oconfig.h
+++ b/src/liboconfig/oconfig.h
/**
* oconfig - src/oconfig.h
- * Copyright (C) 2006,2007 Florian octo Forster <octo at verplant.org>
+ * Copyright (C) 2006-2009 Florian octo Forster <octo at verplant.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
oconfig_item_t *oconfig_parse_fh (FILE *fh);
oconfig_item_t *oconfig_parse_file (const char *file);
+oconfig_item_t *oconfig_clone (const oconfig_item_t *ci);
+
void oconfig_free (oconfig_item_t *ci);
/*
diff --git a/src/perl.c b/src/perl.c
index 81ef7202b9b3219fa0cf8e457ea18dad53c8bc86..17d35af589d2466d1965498504d8f4d12f19da8c 100644 (file)
--- a/src/perl.c
+++ b/src/perl.c
dXSARGS;
+ log_warn ("Using plugin_register() to register new data-sets is "
+ "deprecated - add new entries to a custom types.db instead.");
+
if (2 != items) {
log_err ("Usage: Collectd::plugin_register_data_set(type, dataset)");
XSRETURN_EMPTY;
diff --git a/src/powerdns.c b/src/powerdns.c
index 164137ba39da5f9019bf037faff07efbafea1c26..beb49fbb64a1dc7d3fb97d82f31ffe05fad98f7d 100644 (file)
--- a/src/powerdns.c
+++ b/src/powerdns.c
return;
}
- if (ds->ds[0].type == DS_TYPE_GAUGE)
+ if (0 != parse_value (value, &values[0], ds->ds[0]))
{
- char *endptr = NULL;
-
- values[0].gauge = strtod (value, &endptr);
-
- if (endptr == value)
- {
- ERROR ("powerdns plugin: Cannot convert `%s' "
- "to a floating point number.", value);
- return;
- }
- }
- else
- {
- char *endptr = NULL;
-
- values[0].counter = strtoll (value, &endptr, 0);
- if (endptr == value)
- {
- ERROR ("powerdns plugin: Cannot convert `%s' "
- "to an integer number.", value);
- return;
- }
+ ERROR ("powerdns plugin: Cannot convert `%s' "
+ "to a number.", value);
+ return;
}
vl.values = values;
diff --git a/src/table.c b/src/table.c
--- /dev/null
+++ b/src/table.c
@@ -0,0 +1,560 @@
+/**
+ * collectd - src/table.c
+ * Copyright (C) 2009 Sebastian Harl
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Sebastian Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This module provides generic means to parse and dispatch tabular data.
+ */
+
+#include "collectd.h"
+#include "common.h"
+
+#include "configfile.h"
+#include "plugin.h"
+
+#define log_err(...) ERROR ("table plugin: " __VA_ARGS__)
+#define log_warn(...) WARNING ("table plugin: " __VA_ARGS__)
+
+/*
+ * private data types
+ */
+
+typedef struct {
+ char *type;
+ char *instance_prefix;
+ int *instances;
+ size_t instances_num;
+ int *values;
+ size_t values_num;
+
+ const data_set_t *ds;
+} tbl_result_t;
+
+typedef struct {
+ char *file;
+ char *sep;
+ char *instance;
+
+ tbl_result_t *results;
+ size_t results_num;
+
+ size_t max_colnum;
+} tbl_t;
+
+static void tbl_result_setup (tbl_result_t *res)
+{
+ res->type = NULL;
+
+ res->instance_prefix = NULL;
+ res->instances = NULL;
+ res->instances_num = 0;
+
+ res->values = NULL;
+ res->values_num = 0;
+
+ res->ds = NULL;
+} /* tbl_result_setup */
+
+static void tbl_result_clear (tbl_result_t *res)
+{
+ sfree (res->type);
+
+ sfree (res->instance_prefix);
+ sfree (res->instances);
+ res->instances_num = 0;
+
+ sfree (res->values);
+ res->values_num = 0;
+
+ res->ds = NULL;
+} /* tbl_result_clear */
+
+static void tbl_setup (tbl_t *tbl, char *file)
+{
+ tbl->file = sstrdup (file);
+ tbl->sep = NULL;
+ tbl->instance = NULL;
+
+ tbl->results = NULL;
+ tbl->results_num = 0;
+
+ tbl->max_colnum = 0;
+} /* tbl_setup */
+
+static void tbl_clear (tbl_t *tbl)
+{
+ size_t i;
+
+ sfree (tbl->file);
+ sfree (tbl->sep);
+ sfree (tbl->instance);
+
+ for (i = 0; i < tbl->results_num; ++i)
+ tbl_result_clear (tbl->results + i);
+ sfree (tbl->results);
+ tbl->results_num = 0;
+
+ tbl->max_colnum = 0;
+} /* tbl_clear */
+
+static tbl_t *tables;
+static size_t tables_num;
+
+/*
+ * configuration handling
+ */
+
+static int tbl_config_set_s (char *name, char **var, oconfig_item_t *ci)
+{
+ if ((1 != ci->values_num)
+ || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+ 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, int **var, size_t *len,
+ oconfig_item_t *ci)
+{
+ int *tmp;
+
+ size_t i;
+
+ if (1 > ci->values_num) {
+ log_err ("\"%s\" expects at least one argument.", name);
+ return 1;
+ }
+
+ for (i = 0; i < ci->values_num; ++i) {
+ if (OCONFIG_TYPE_NUMBER != ci->values[i].type) {
+ log_err ("\"%s\" expects numerical arguments only.", name);
+ return 1;
+ }
+ }
+
+ *len += ci->values_num;
+ tmp = (int *)realloc (*var, *len * sizeof (**var));
+ if (NULL == tmp) {
+ char errbuf[1024];
+ log_err ("realloc failed: %s.",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return -1;
+ }
+
+ *var = tmp;
+
+ for (i = *len - ci->values_num; i < *len; ++i)
+ (*var)[i] = (int)ci->values[i].value.number;
+ return 0;
+} /* tbl_config_append_array_s */
+
+static int tbl_config_result (tbl_t *tbl, oconfig_item_t *ci)
+{
+ tbl_result_t *res;
+
+ int status = 0;
+ size_t i;
+
+ if (0 != ci->values_num) {
+ log_err ("<Result> does not expect any arguments.");
+ return 1;
+ }
+
+ res = (tbl_result_t *)realloc (tbl->results,
+ (tbl->results_num + 1) * sizeof (*tbl->results));
+ if (NULL == tbl) {
+ char errbuf[1024];
+ log_err ("realloc failed: %s.",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return -1;
+ }
+
+ tbl->results = res;
+ ++tbl->results_num;
+
+ res = tbl->results + tbl->results_num - 1;
+ tbl_result_setup (res);
+
+ for (i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *c = ci->children + i;
+
+ if (0 == strcasecmp (c->key, "Type"))
+ tbl_config_set_s (c->key, &res->type, c);
+ else if (0 == strcasecmp (c->key, "InstancePrefix"))
+ tbl_config_set_s (c->key, &res->instance_prefix, c);
+ else if (0 == strcasecmp (c->key, "InstancesFrom"))
+ tbl_config_append_array_i (c->key,
+ &res->instances, &res->instances_num, c);
+ else if (0 == strcasecmp (c->key, "ValuesFrom"))
+ tbl_config_append_array_i (c->key,
+ &res->values, &res->values_num, c);
+ else
+ log_warn ("Ignoring unknown config key \"%s\" "
+ " in <Result>.", c->key);
+ }
+
+ if (NULL == res->type) {
+ log_err ("No \"Type\" option specified for <Result> "
+ "in table \"%s\".", tbl->file);
+ status = 1;
+ }
+
+ if (NULL == res->values) {
+ log_err ("No \"ValuesFrom\" option specified for <Result> "
+ "in table \"%s\".", tbl->file);
+ status = 1;
+ }
+
+ if (0 != status) {
+ tbl_result_clear (res);
+ --tbl->results_num;
+ return status;
+ }
+ return 0;
+} /* tbl_config_result */
+
+static int tbl_config_table (oconfig_item_t *ci)
+{
+ tbl_t *tbl;
+
+ int status = 0;
+ size_t i;
+
+ if ((1 != ci->values_num)
+ || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+ log_err ("<Table> expects a single string argument.");
+ return 1;
+ }
+
+ tbl = (tbl_t *)realloc (tables, (tables_num + 1) * sizeof (*tables));
+ if (NULL == tbl) {
+ char errbuf[1024];
+ log_err ("realloc failed: %s.",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return -1;
+ }
+
+ tables = tbl;
+ ++tables_num;
+
+ tbl = tables + tables_num - 1;
+ tbl_setup (tbl, ci->values[0].value.string);
+
+ for (i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *c = ci->children + i;
+
+ if (0 == strcasecmp (c->key, "Separator"))
+ tbl_config_set_s (c->key, &tbl->sep, c);
+ else if (0 == strcasecmp (c->key, "Instance"))
+ tbl_config_set_s (c->key, &tbl->instance, c);
+ else if (0 == strcasecmp (c->key, "Result"))
+ tbl_config_result (tbl, c);
+ else
+ log_warn ("Ignoring unknown config key \"%s\" "
+ "in <Table %s>.", c->key, tbl->file);
+ }
+
+ if (NULL == tbl->sep) {
+ log_err ("Table \"%s\" does not specify any separator.", tbl->file);
+ status = 1;
+ }
+ strunescape (tbl->sep, strlen (tbl->sep) + 1);
+
+ if (NULL == tbl->instance) {
+ tbl->instance = sstrdup (tbl->file);
+ replace_special (tbl->instance, strlen (tbl->instance));
+ }
+
+ if (NULL == tbl->results) {
+ log_err ("Table \"%s\" does not specify any (valid) results.",
+ tbl->file);
+ status = 1;
+ }
+
+ if (0 != status) {
+ tbl_clear (tbl);
+ --tables_num;
+ return status;
+ }
+
+ for (i = 0; i < tbl->results_num; ++i) {
+ tbl_result_t *res = tbl->results + i;
+ size_t j;
+
+ for (j = 0; j < res->instances_num; ++j)
+ if (res->instances[j] > tbl->max_colnum)
+ tbl->max_colnum = res->instances[j];
+
+ for (j = 0; j < res->values_num; ++j)
+ if (res->values[j] > tbl->max_colnum)
+ tbl->max_colnum = res->values[j];
+ }
+ return 0;
+} /* tbl_config_table */
+
+static int tbl_config (oconfig_item_t *ci)
+{
+ size_t i;
+
+ for (i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *c = ci->children + i;
+
+ if (0 == strcasecmp (c->key, "Table"))
+ tbl_config_table (c);
+ else
+ log_warn ("Ignoring unknown config key \"%s\".", c->key);
+ }
+ return 0;
+} /* tbl_config */
+
+/*
+ * result handling
+ */
+
+static int tbl_prepare (tbl_t *tbl)
+{
+ size_t i;
+
+ for (i = 0; i < tbl->results_num; ++i) {
+ tbl_result_t *res = tbl->results + i;
+
+ res->ds = plugin_get_ds (res->type);
+ if (NULL == res->ds) {
+ log_err ("Unknown type \"%s\". See types.db(5) for details.",
+ res->type);
+ return -1;
+ }
+
+ if (res->values_num != (size_t)res->ds->ds_num) {
+ log_err ("Invalid type \"%s\". Expected %zu data source%s, "
+ "got %i.", res->type, res->values_num,
+ (1 == res->values_num) ? "" : "s",
+ res->ds->ds_num);
+ return -1;
+ }
+ }
+ return 0;
+} /* tbl_prepare */
+
+static int tbl_finish (tbl_t *tbl)
+{
+ size_t i;
+
+ for (i = 0; i < tbl->results_num; ++i)
+ tbl->results[i].ds = NULL;
+ return 0;
+} /* tbl_finish */
+
+static int tbl_result_dispatch (tbl_t *tbl, tbl_result_t *res,
+ char **fields, size_t fields_num)
+{
+ value_list_t vl = VALUE_LIST_INIT;
+ value_t values[res->values_num];
+
+ size_t i;
+
+ assert (NULL != res->ds);
+ assert (res->values_num == res->ds->ds_num);
+
+ for (i = 0; i < res->values_num; ++i) {
+ char *value;
+
+ assert (res->values[i] < fields_num);
+ value = fields[res->values[i]];
+
+ if (0 != parse_value (value, &values[i], res->ds->ds[i]))
+ return -1;
+ }
+
+ vl.values = values;
+ vl.values_len = STATIC_ARRAY_SIZE (values);
+
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "table", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, tbl->instance, sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, res->type, sizeof (vl.type));
+
+ if (0 == res->instances_num) {
+ if (NULL != res->instance_prefix)
+ sstrncpy (vl.type_instance, res->instance_prefix,
+ sizeof (vl.type_instance));
+ }
+ else {
+ char *instances[res->instances_num];
+ char instances_str[DATA_MAX_NAME_LEN];
+
+ for (i = 0; i < res->instances_num; ++i) {
+ assert (res->instances[i] < fields_num);
+ instances[i] = fields[res->instances[i]];
+ }
+
+ strjoin (instances_str, sizeof (instances_str),
+ instances, STATIC_ARRAY_SIZE (instances), "-");
+ instances_str[sizeof (instances_str) - 1] = '\0';
+
+ vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+ if (NULL == res->instance_prefix)
+ strncpy (vl.type_instance, instances_str,
+ sizeof (vl.type_instance));
+ else
+ snprintf (vl.type_instance, sizeof (vl.type_instance),
+ "%s-%s", res->instance_prefix, instances_str);
+
+ if ('\0' != vl.type_instance[sizeof (vl.type_instance) - 1]) {
+ vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+ log_warn ("Truncated type instance: %s.", vl.type_instance);
+ }
+ }
+
+ plugin_dispatch_values (&vl);
+ return 0;
+} /* tbl_result_dispatch */
+
+static int tbl_parse_line (tbl_t *tbl, char *line, size_t len)
+{
+ char *fields[tbl->max_colnum + 1];
+ char *ptr, *saveptr;
+
+ size_t i;
+
+ i = 0;
+ ptr = line;
+ saveptr = NULL;
+ while (NULL != (fields[i] = strtok_r (ptr, tbl->sep, &saveptr))) {
+ ptr = NULL;
+ ++i;
+
+ if (i > tbl->max_colnum)
+ break;
+ }
+
+ if (i <= tbl->max_colnum) {
+ log_err ("Not enough columns in line "
+ "(expected at least %zu, got %zu).",
+ tbl->max_colnum + 1, i);
+ return -1;
+ }
+
+ for (i = 0; i < tbl->results_num; ++i)
+ if (0 != tbl_result_dispatch (tbl, tbl->results + i,
+ fields, STATIC_ARRAY_SIZE (fields))) {
+ log_err ("Failed to dispatch result.");
+ continue;
+ }
+ return 0;
+} /* tbl_parse_line */
+
+static int tbl_read_table (tbl_t *tbl)
+{
+ FILE *fh;
+ char buf[4096];
+
+ fh = fopen (tbl->file, "r");
+ if (NULL == fh) {
+ char errbuf[1024];
+ log_err ("Failed to open file \"%s\": %s.", tbl->file,
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return -1;
+ }
+
+ buf[sizeof (buf) - 1] = '\0';
+ while (NULL != fgets (buf, sizeof (buf), fh)) {
+ if ('\0' != buf[sizeof (buf) - 1]) {
+ buf[sizeof (buf) - 1] = '\0';
+ log_err ("Table %s: Truncated line: %s", tbl->file, buf);
+ }
+
+ if (0 != tbl_parse_line (tbl, buf, sizeof (buf))) {
+ log_err ("Table %s: Failed to parse line: %s", tbl->file, buf);
+ continue;
+ }
+ }
+
+ if (0 != ferror (fh)) {
+ char errbuf[1024];
+ log_err ("Failed to read from file \"%s\": %s.", tbl->file,
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ fclose (fh);
+ return -1;
+ }
+
+ fclose (fh);
+ return 0;
+} /* tbl_read_table */
+
+/*
+ * collectd callbacks
+ */
+
+static int tbl_read (void)
+{
+ int status = -1;
+ size_t i;
+
+ if (0 == tables_num)
+ return 0;
+
+ for (i = 0; i < tables_num; ++i) {
+ tbl_t *tbl = tables + i;
+
+ if (0 != tbl_prepare (tbl)) {
+ log_err ("Failed to prepare and parse table \"%s\".", tbl->file);
+ continue;
+ }
+
+ if (0 == tbl_read_table (tbl))
+ status = 0;
+
+ tbl_finish (tbl);
+ }
+ return status;
+} /* tbl_read */
+
+static int tbl_shutdown (void)
+{
+ size_t i;
+
+ for (i = 0; i < tables_num; ++i)
+ tbl_clear (&tables[i]);
+ sfree (tables);
+ return 0;
+} /* tbl_shutdown */
+
+static int tbl_init (void)
+{
+ if (0 == tables_num)
+ return 0;
+
+ plugin_register_read ("table", tbl_read);
+ plugin_register_shutdown ("table", tbl_shutdown);
+ return 0;
+} /* tbl_init */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("table", tbl_config);
+ plugin_register_init ("table", tbl_init);
+} /* module_register */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
diff --git a/src/types.db.pod b/src/types.db.pod
index 4a1bfa4059d058d7726a1dfd59e10ab002ae8c3c..a46eb41c9150f89f2237d71ade30f05e8f7c1265 100644 (file)
--- a/src/types.db.pod
+++ b/src/types.db.pod
TypesDB "/opt/collectd/share/collectd/types.db"
TypesDB "/opt/collectd/etc/types.db.custom"
+B<Note>: Make sure to make this file available on all systems if you're
+sending values over the network.
+
=head1 SEE ALSO
L<collectd(1)>,
diff --git a/src/utils_cmd_putval.c b/src/utils_cmd_putval.c
index 5bd6ec738fd2e188b4b253e3e7ce51937383690a..be09185ee1c3cb6ee14fa2bde688af23a942b17b 100644 (file)
--- a/src/utils_cmd_putval.c
+++ b/src/utils_cmd_putval.c
return -1; \
}
-static int parse_value (const data_set_t *ds, value_list_t *vl,
+static int dispatch_values (const data_set_t *ds, value_list_t *vl,
FILE *fh, char *buffer)
{
char *dummy;
break;
}
- if (strcmp (ptr, "U") == 0)
+ if ((strcmp (ptr, "U") == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
vl->values[i].gauge = NAN;
- else if (ds->ds[i].type == DS_TYPE_COUNTER)
- vl->values[i].counter = atoll (ptr);
- else if (ds->ds[i].type == DS_TYPE_GAUGE)
- vl->values[i].gauge = atof (ptr);
+ else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i]))
+ {
+ print_to_socket (fh, "-1 Failed to parse value `%s'.", ptr);
+ return (-1);
+ }
i++;
} /* while (strtok_r) */
{
char identifier[128];
FORMAT_VL (identifier, sizeof (identifier), vl, ds);
- ERROR ("cmd putval: parse_value: "
+ ERROR ("cmd putval: dispatch_values: "
"Number of values incorrect: "
"Got %i, expected %i. Identifier is `%s'.",
i, vl->values_len, identifier);
plugin_dispatch_values (vl);
return (0);
-} /* int parse_value */
+} /* int dispatch_values */
static int set_option (value_list_t *vl, const char *key, const char *value)
{
}
assert (string != NULL);
- status = parse_value (ds, &vl, fh, string);
+ status = dispatch_values (ds, &vl, fh, string);
if (status != 0)
{
/* An error has already been printed. */
diff --git a/src/utils_db_query.c b/src/utils_db_query.c
index c2897c7c88c51717fde50b4d9c5b97b92b6c5727..5531b25fe7fd60eb5e34300b8f2cfb0ce3cbbf2f 100644 (file)
--- a/src/utils_db_query.c
+++ b/src/utils_db_query.c
{
value_list_t vl = VALUE_LIST_INIT;
value_t value;
- char *endptr;
+ char *value_str;
assert (r->legacy_mode == 1);
assert (r->ds != NULL);
vl.values = &value;
vl.values_len = 1;
- endptr = NULL;
- errno = 0;
- if (r->ds->ds[0].type == DS_TYPE_COUNTER)
- vl.values[0].counter = (counter_t) strtoll (column_values[r->legacy_position],
- &endptr, /* base = */ 0);
- else if (r->ds->ds[0].type == DS_TYPE_GAUGE)
- vl.values[0].gauge = (gauge_t) strtod (column_values[r->legacy_position],
- &endptr);
- else
- errno = EINVAL;
-
- if ((endptr == column_values[r->legacy_position]) || (errno != 0))
+ value_str = column_values[r->legacy_position];
+ if (0 != parse_value (value_str, &vl.values[0], r->ds->ds[0]))
{
- WARNING ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
- column_values[r->legacy_position],
+ ERROR ("db query utils: udb_legacy_result_handle_result: "
+ "Parsing `%s' as %s failed.", value_str,
(r->ds->ds[0].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
- vl.values[0].gauge = NAN;
+ errno = EINVAL;
+ return (-1);
}
sstrncpy (vl.host, q->host, sizeof (vl.host));
/*
* Result private functions
*/
-static void udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
+static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
{
value_list_t vl = VALUE_LIST_INIT;
size_t i;
if (vl.values == NULL)
{
ERROR ("db query utils: malloc failed.");
- return;
+ return (-1);
}
vl.values_len = r->ds->ds_num;
for (i = 0; i < r->values_num; i++)
{
- char *endptr;
-
- endptr = NULL;
- errno = 0;
- if (r->ds->ds[i].type == DS_TYPE_COUNTER)
- vl.values[i].counter = (counter_t) strtoll (r->values_buffer[i],
- &endptr, /* base = */ 0);
- else if (r->ds->ds[i].type == DS_TYPE_GAUGE)
- vl.values[i].gauge = (gauge_t) strtod (r->values_buffer[i], &endptr);
- else
- errno = EINVAL;
+ char *value_str = r->values_buffer[i];
- if ((endptr == r->values_buffer[i]) || (errno != 0))
+ if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i]))
{
- WARNING ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
- r->values_buffer[i],
+ ERROR ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
+ value_str,
(r->ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
- vl.values[i].gauge = NAN;
+ errno = EINVAL;
+ return (-1);
}
}
plugin_dispatch_values (&vl);
sfree (vl.values);
+ return (0);
} /* }}} void udb_result_submit */
static void udb_result_finish_result (udb_result_t *r) /* {{{ */
for (i = 0; i < r->values_num; i++)
r->values_buffer[i] = column_values[r->values_pos[i]];
- udb_result_submit (r, q);
-
- return (0);
+ return udb_result_submit (r, q);
} /* }}} int udb_result_handle_result */
static int udb_result_prepare_result (udb_result_t *r, /* {{{ */