summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: b15b750)
raw | patch | inline | side by side (parent: b15b750)
author | Sebastian Harl <sh@tokkee.org> | |
Tue, 28 Oct 2008 21:02:38 +0000 (22:02 +0100) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Tue, 28 Oct 2008 21:02:38 +0000 (22:02 +0100) |
The user may specify a set of Perl-compatible regular expressions to match any
component of the (host, plugin, plugin instance, type, type instance) tuple.
Any of the filter flags may be used to handle a successful match.
Sample plugin configuration:
<Plugin filter_pcre>
<RegEx>
Host "^mail\d+$"
Plugin "^tcpconns$"
TypeInstance "^SYN_"
Action NoWrite
</Plugin>
component of the (host, plugin, plugin instance, type, type instance) tuple.
Any of the filter flags may be used to handle a successful match.
Sample plugin configuration:
<Plugin filter_pcre>
<RegEx>
Host "^mail\d+$"
Plugin "^tcpconns$"
TypeInstance "^SYN_"
Action NoWrite
</Plugin>
README | patch | blob | history | |
configure.in | patch | blob | history | |
src/Makefile.am | patch | blob | history | |
src/collectd.conf.in | patch | blob | history | |
src/collectd.conf.pod | patch | blob | history | |
src/filter_pcre.c | [new file with mode: 0644] | patch | blob |
index b6bdb4133fd731e00626f2726768c131588494e5..8ff8aeee061659cbf6a283ced6170197fe260a87 100644 (file)
--- a/README
+++ b/README
needed. Please read collectd-unixsock(5) for a description on how that's
done.
+ * Filtering and rewriting values dispatched to collectd can be done by the
+ following plugins:
+
+ - filter_pcre
+ Filter value lists based on Perl-compatible regular expressions.
+
* Logging is, as everything in collectd, provided by plugins. The following
plugins keep up informed about what's going on:
Used to capture packets by the `dns' plugin.
<http://www.tcpdump.org/>
+ * libpcre (optional)
+ Used by the `filter_pcre' plugin.
+ <http://www.pcre.org/>
+
* libperl (optional)
Obviously used by the `perl' plugin. The library has to be compiled with
ithread support (introduced in Perl 5.6.0).
diff --git a/configure.in b/configure.in
index 90ac4627fc7731338ee6e12be2ac441991e91f6c..9319d5fce11c7ac865b77dbee14434a93f294c95 100644 (file)
--- a/configure.in
+++ b/configure.in
AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes")
# }}}
+# --with-libpcre {{{
+with_pcre_config="pcre-config"
+with_pcre_cflags=""
+with_pcre_libs=""
+AC_ARG_WITH(libpcre, [AS_HELP_STRING([--with-libpcre@<:@=PREFIX@:>@],
+ [Path to libpcre.])],
+ [
+ if test "x$withval" = "xno"
+ then
+ with_libpcre="no"
+ else if test "x$withval" = "xyes"
+ then
+ with_libpcre="yes"
+ else
+ if test -f "$withval" && test -x "$withval"
+ then
+ with_pcre_config="$withval"
+ else if test -x "$withval/bin/pcre-config"
+ then
+ with_pcre_config="$withval/bin/pcre-config"
+ fi; fi
+ with_libpcre="yes"
+ fi; fi
+ ],
+ [
+ with_libpcre="yes"
+ ])
+
+if test "x$with_libpcre" = "xyes"
+then
+ with_pcre_cflags=`$with_pcre_config --cflags 2>/dev/null`
+ pcre_config_status=$?
+
+ if test $pcre_config_status -ne 0
+ then
+ with_libpcre="no ($with_pcre_config failed)"
+ else
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_pcre_cflags"
+
+ AC_CHECK_HEADERS(pcre.h, [], [with_libpcre="no (pcre.h not found)"], [])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ fi
+fi
+
+if test "x$with_libpcre" = "xyes"
+then
+ with_pcre_libs=`$with_pcre_config --libs 2>/dev/null`
+ pcre_config_status=$?
+
+ if test $pcre_config_status -ne 0
+ then
+ with_libpcre="no ($with_pcre_config failed)"
+ else
+ AC_CHECK_LIB(pcre, pcre_compile,
+ [with_libpcre="yes"],
+ [with_libpcre="no (symbol 'pcre_compile' not found)"],
+ [$with_pcre_libs])
+ fi
+fi
+
+if test "x$with_libpcre" = "xyes"
+then
+ BUILD_WITH_LIBPCRE_CFLAGS="$with_pcre_cflags"
+ BUILD_WITH_LIBPCRE_LIBS="$with_pcre_libs"
+ AC_SUBST(BUILD_WITH_LIBPCRE_CFLAGS)
+ AC_SUBST(BUILD_WITH_LIBPCRE_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBPCRE, test "x$with_libpcre" = "xyes")
+# }}}
+
# --with-libperl {{{
perl_interpreter="perl"
AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
AC_PLUGIN([exec], [yes], [Execution of external programs])
AC_PLUGIN([filecount], [yes], [Count files in directories])
AC_PLUGIN([filter_ignore], [yes], [Ignore specific values])
+AC_PLUGIN([filter_pcre], [$with_libpcre], [Filter based on PCRE])
AC_PLUGIN([hddtemp], [yes], [Query hddtempd])
AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics])
AC_PLUGIN([iptables], [$with_libiptc], [IPTables rule counters])
libopenipmi . . . . . $with_libopenipmipthread
liboping . . . . . . $with_liboping
libpcap . . . . . . . $with_libpcap
+ libpcre . . . . . . . $with_libpcre
libperl . . . . . . . $with_libperl
libpthread . . . . . $with_libpthread
libpq . . . . . . . . $with_libpq
exec . . . . . . . . $enable_exec
filecount . . . . . . $enable_filecount
filter_ignore . . . . $enable_filter_ignore
+ filter_pcre . . . . . $enable_filter_pcre
hddtemp . . . . . . . $enable_hddtemp
interface . . . . . . $enable_interface
iptables . . . . . . $enable_iptables
diff --git a/src/Makefile.am b/src/Makefile.am
index 2196d32c87614d928291a99e911a0a8f33e417a4..fb3587b62c135decd69c11ba082e870698f46b7f 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
collectd_DEPENDENCIES += filter_ignore.la
endif
+if BUILD_PLUGIN_FILTER_PCRE
+pkglib_LTLIBRARIES += filter_pcre.la
+filter_pcre_la_SOURCES = filter_pcre.c
+filter_pcre_la_CPPFLAGS = $(BUILD_WITH_LIBPCRE_CFLAGS)
+filter_pcre_la_LDFLAGS = -module -avoid-version \
+ $(BUILD_WITH_LIBPCRE_LIBS)
+collectd_LDADD += "-dlopen" filter_pcre.la
+collectd_DEPENDENCIES += filter_pcre.la
+endif
+
if BUILD_PLUGIN_HDDTEMP
pkglib_LTLIBRARIES += hddtemp.la
hddtemp_la_SOURCES = hddtemp.c
diff --git a/src/collectd.conf.in b/src/collectd.conf.in
index 0597273f43e83936891ba91b4035ca26df405f95..4963a0604750c0e342921d88caf69709d9e520be 100644 (file)
--- a/src/collectd.conf.in
+++ b/src/collectd.conf.in
@BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy
@BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec
@BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount
+@BUILD_PLUGIN_FILTER_PCRE_TRUE@LoadPlugin filter_pcre
@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp
@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface
@BUILD_PLUGIN_IPTABLES_TRUE@LoadPlugin iptables
# </Directory>
#</Plugin>
+#<Plugin filter_pcre>
+# <RegEx>
+# Host "^mail\d+$"
+# Plugin "^tcpconns$"
+# TypeInstance "^SYN_"
+#
+# Action NoWrite
+# </RegEx>
+#</Plugin>
+
@BUILD_PLUGIN_HDDTEMP_TRUE@<Plugin hddtemp>
# Host "127.0.0.1"
# Port "7634"
diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod
index 5d07ee5288f8d14e514af0782cf542670b3306c2..bc494b8fc5c42bfa4c68381d34a54d9f7f481ead 100644 (file)
--- a/src/collectd.conf.pod
+++ b/src/collectd.conf.pod
=back
+=head2 Plugin C<filter_pcre>
+
+This plugin allows you to filter value lists based on Perl-compatible regular
+expressions whose syntax and semantics are as close as possible to those of
+the Perl 5 language. See L<pcre(3)> for details.
+
+ <Plugin filter_pcre>
+ <RegEx>
+ Host "^mail\d+$"
+ Plugin "^tcpconns$"
+ TypeInstance "^SYN_"
+
+ Action NoWrite
+ </Plugin>
+
+The configuration consists of one or more C<RegEx> blocks, each of which
+specifies a regular expression identifying a set of value lists and how to
+handle successful matches. A value list keeps the values of a single data-set
+and is identified by the tuple (host, plugin, plugin instance, type, type
+instance). The plugin and type instances are optional components. If they are
+missing they are treated as empty strings. Within those blocks, the following
+options are recognized:
+
+=over 4
+
+=item B<Host> I<regex>
+
+=item B<Plugin> I<regex>
+
+=item B<PluginInstance> I<regex>
+
+=item B<Type> I<regex>
+
+=item B<TypeInstance> I<regex>
+
+Specifies the regular expression for each component of the identifier. If any
+of these options is missing it is interpreted as a pattern which matches any
+string. All five components of a value list have to match the appropriate
+regular expression to trigger the specified action.
+
+=item B<Action> I<NoWrite>|I<NoThresholdCheck>|I<Ignore>
+
+Specify how to handle successful matches:
+
+=over 4
+
+=item B<NoWrite>
+
+Do not send the value list to any output (a.k.a. write) plugins.
+
+=item B<NoThresholdCheck>
+
+Skip threshold checking for this value list.
+
+=item B<Ignore>
+
+Completely ignore this value list.
+
+=back
+
+Two or more actions may be combined by specifying multiple B<Action> options.
+
+=back
+
=head2 Plugin C<hddtemp>
To get values from B<hddtemp> collectd connects to B<localhost> (127.0.0.1),
L<hddtemp(8)>,
L<kstat(3KSTAT)>,
L<mbmon(1)>,
+L<pcre(3)>,
L<psql(1)>,
L<rrdtool(1)>,
L<sensors(1)>
diff --git a/src/filter_pcre.c b/src/filter_pcre.c
--- /dev/null
+++ b/src/filter_pcre.c
@@ -0,0 +1,329 @@
+/**
+ * collectd - src/filter_pcre.c
+ * Copyright (C) 2008 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
+ *
+ * Author:
+ * Sebastian Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This module allows to filter value lists based on Perl-compatible regular
+ * expressions.
+ */
+
+#include "collectd.h"
+#include "configfile.h"
+#include "plugin.h"
+#include "common.h"
+
+#include <pcre.h>
+
+#define log_err(...) ERROR ("filter_pcre: " __VA_ARGS__)
+#define log_warn(...) WARNING ("filter_pcre: " __VA_ARGS__)
+
+/*
+ * private data types
+ */
+
+typedef struct {
+ pcre *re;
+ pcre_extra *extra;
+} c_pcre_t;
+
+#define C_PCRE_INIT(regex) do { \
+ (regex).re = NULL; \
+ (regex).extra = NULL; \
+ } while (0)
+
+#define C_PCRE_FREE(regex) do { \
+ pcre_free ((regex).re); \
+ pcre_free ((regex).extra); \
+ C_PCRE_INIT (regex); \
+ } while (0)
+
+typedef struct {
+ c_pcre_t host;
+ c_pcre_t plugin;
+ c_pcre_t plugin_instance;
+ c_pcre_t type;
+ c_pcre_t type_instance;
+
+ int action;
+} regex_t;
+
+/*
+ * private variables
+ */
+
+static regex_t *regexes = NULL;
+static int regexes_num = 0;
+
+/*
+ * internal helper functions
+ */
+
+/* returns true if string matches the regular expression */
+static int c_pcre_match (c_pcre_t *re, const char *string)
+{
+ int status;
+ int ovector[30];
+
+ if (NULL == re)
+ return 1;
+
+ if (NULL == string)
+ string = "";
+
+ status = pcre_exec (re->re,
+ /* extra = */ re->extra,
+ /* subject = */ string,
+ /* length = */ strlen (string),
+ /* startoffset = */ 0,
+ /* options = */ 0,
+ /* ovector = */ ovector,
+ /* ovecsize = */ STATIC_ARRAY_SIZE (ovector));
+
+ if (0 <= status)
+ return 1;
+
+ if (PCRE_ERROR_NOMATCH != status)
+ log_err ("PCRE matching of string \"%s\" failed with status %d",
+ string, status);
+ return 0;
+} /* c_pcre_match */
+
+static regex_t *regex_new (void)
+{
+ regex_t *re;
+
+ ++regexes_num;
+ regexes = (regex_t *)realloc (regexes, regexes_num * sizeof (*regexes));
+ if (NULL == regexes) {
+ log_err ("Out of memory.");
+ exit (5);
+ }
+
+ re = regexes + (regexes_num - 1);
+
+ C_PCRE_INIT (re->host);
+ C_PCRE_INIT (re->plugin);
+ C_PCRE_INIT (re->plugin_instance);
+ C_PCRE_INIT (re->type);
+ C_PCRE_INIT (re->type_instance);
+
+ re->action = 0;
+ return re;
+} /* regex_new */
+
+static void regex_delete (regex_t *re)
+{
+ if (NULL == re)
+ return;
+
+ C_PCRE_FREE (re->host);
+ C_PCRE_FREE (re->plugin);
+ C_PCRE_FREE (re->plugin_instance);
+ C_PCRE_FREE (re->type);
+ C_PCRE_FREE (re->type_instance);
+
+ re->action = 0;
+} /* regex_delete */
+
+/* returns true if the value list matches the regular expression */
+static int regex_match (regex_t *re, value_list_t *vl)
+{
+ int matches = 0;
+
+ if (NULL == re)
+ return 1;
+
+ if ((NULL == re->host.re) || c_pcre_match (&re->host, vl->host))
+ ++matches;
+
+ if ((NULL == re->plugin.re) || c_pcre_match (&re->plugin, vl->plugin))
+ ++matches;
+
+ if ((NULL == re->plugin_instance.re)
+ || c_pcre_match (&re->plugin_instance, vl->plugin_instance))
+ ++matches;
+
+ if ((NULL == re->type.re) || c_pcre_match (&re->type, vl->type))
+ ++matches;
+
+ if ((NULL == re->type_instance.re)
+ || c_pcre_match (&re->type_instance, vl->type_instance))
+ ++matches;
+
+ if (5 == matches)
+ return 1;
+ return 0;
+} /* regex_match */
+
+/*
+ * interface to collectd
+ */
+
+static int c_pcre_filter (const data_set_t *ds, value_list_t *vl)
+{
+ int i;
+
+ for (i = 0; i < regexes_num; ++i)
+ if (regex_match (regexes + i, vl))
+ return regexes[i].action;
+ return 0;
+} /* c_pcre_filter */
+
+static int c_pcre_shutdown (void)
+{
+ int i;
+
+ plugin_unregister_filter ("filter_pcre");
+ plugin_unregister_shutdown ("filter_pcre");
+
+ for (i = 0; i < regexes_num; ++i)
+ regex_delete (regexes + i);
+
+ sfree (regexes);
+ regexes_num = 0;
+ return 0;
+} /* c_pcre_shutdown */
+
+static int config_set_regex (c_pcre_t *re, oconfig_item_t *ci)
+{
+ const char *pattern;
+ const char *errptr;
+ int erroffset;
+
+ if ((0 != ci->children_num) || (1 != ci->values_num)
+ || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+ log_err ("<RegEx>: %s expects a single string argument.", ci->key);
+ return 1;
+ }
+
+ pattern = ci->values[0].value.string;
+
+ re->re = pcre_compile (pattern,
+ /* options = */ 0,
+ /* errptr = */ &errptr,
+ /* erroffset = */ &erroffset,
+ /* tableptr = */ NULL);
+
+ if (NULL == re->re) {
+ log_err ("<RegEx>: PCRE compilation of pattern \"%s\" failed "
+ "at offset %d: %s", pattern, erroffset, errptr);
+ return 1;
+ }
+
+ re->extra = pcre_study (re->re,
+ /* options = */ 0,
+ /* errptr = */ &errptr);
+
+ if (NULL != errptr) {
+ log_err ("<RegEx>: PCRE studying of pattern \"%s\" failed: %s",
+ pattern, errptr);
+ return 1;
+ }
+ return 0;
+} /* config_set_regex */
+
+static int config_set_action (int *action, oconfig_item_t *ci)
+{
+ const char *action_str;
+
+ if ((0 != ci->children_num) || (1 != ci->values_num)
+ || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
+ log_err ("<RegEx>: Action expects a single string argument.");
+ return 1;
+ }
+
+ action_str = ci->values[0].value.string;
+
+ if (0 == strcasecmp (action_str, "NoWrite"))
+ *action |= FILTER_NOWRITE;
+ else if (0 == strcasecmp (action_str, "NoThresholdCheck"))
+ *action |= FILTER_NOTHRESHOLD_CHECK;
+ else if (0 == strcasecmp (action_str, "Ignore"))
+ *action |= FILTER_IGNORE;
+ else
+ log_warn ("<Regex>: Ignoring unknown action \"%s\".", action_str);
+ return 0;
+} /* config_set_action */
+
+static int c_pcre_config_regex (oconfig_item_t *ci)
+{
+ regex_t *re;
+ int i;
+
+ if (0 != ci->values_num) {
+ log_err ("<RegEx> expects no arguments.");
+ return 1;
+ }
+
+ re = regex_new ();
+
+ for (i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *c = ci->children + i;
+ int status = 0;
+
+ if (0 == strcasecmp (c->key, "Host"))
+ status = config_set_regex (&re->host, c);
+ else if (0 == strcasecmp (c->key, "Plugin"))
+ status = config_set_regex (&re->plugin, c);
+ else if (0 == strcasecmp (c->key, "PluginInstance"))
+ status = config_set_regex (&re->plugin_instance, c);
+ else if (0 == strcasecmp (c->key, "Type"))
+ status = config_set_regex (&re->type, c);
+ else if (0 == strcasecmp (c->key, "TypeInstance"))
+ status = config_set_regex (&re->type_instance, c);
+ else if (0 == strcasecmp (c->key, "Action"))
+ status = config_set_action (&re->action, c);
+ else
+ log_warn ("<RegEx>: Ignoring unknown config key \"%s\".", c->key);
+
+ if (0 != status) {
+ log_err ("Ignoring regular expression definition.");
+ regex_delete (re);
+ --regexes_num;
+ }
+ }
+ return 0;
+} /* c_pcre_config_regex */
+
+static int c_pcre_config (oconfig_item_t *ci)
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *c = ci->children + i;
+
+ if (0 == strcasecmp (c->key, "RegEx"))
+ c_pcre_config_regex (c);
+ else
+ log_warn ("Ignoring unknown config key \"%s\".", c->key);
+ }
+
+ plugin_register_filter ("filter_pcre", c_pcre_filter);
+ plugin_register_shutdown ("filter_pcre", c_pcre_shutdown);
+ return 0;
+} /* c_pcre_config */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("filter_pcre", c_pcre_config);
+} /* module_register */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+