2 * collectd - src/match_regex.c
3 * Copyright (C) 2008 Sebastian Harl
4 * Copyright (C) 2008 Florian Forster
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
25 * Sebastian Harl <sh at tokkee.org>
26 * Florian Forster <octo at collectd.org>
30 * This module allows to filter and rewrite value lists based on
31 * Perl-compatible regular expressions.
36 #include "filter_chain.h"
37 #include "meta_data.h"
38 #include "utils_llist.h"
40 #include <sys/types.h>
43 #define log_err(...) ERROR ("`regex' match: " __VA_ARGS__)
44 #define log_warn(...) WARNING ("`regex' match: " __VA_ARGS__)
51 typedef struct mr_regex_s mr_regex_t;
61 typedef struct mr_match_s mr_match_t;
66 mr_regex_t *plugin_instance;
68 mr_regex_t *type_instance;
69 llist_t *meta; /* Maps each meta key into mr_regex_t* */
74 * internal helper functions
76 static void mr_free_regex (mr_regex_t *r) /* {{{ */
82 memset (&r->re, 0, sizeof (r->re));
86 mr_free_regex (r->next);
87 } /* }}} void mr_free_regex */
89 static void mr_free_match (mr_match_t *m) /* {{{ */
94 mr_free_regex (m->host);
95 mr_free_regex (m->plugin);
96 mr_free_regex (m->plugin_instance);
97 mr_free_regex (m->type);
98 mr_free_regex (m->type_instance);
99 for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next)
102 mr_free_regex ((mr_regex_t *) e->value);
104 llist_destroy (m->meta);
107 } /* }}} void mr_free_match */
109 static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
113 return (FC_MATCH_MATCHES);
115 for (mr_regex_t *re = re_head; re != NULL; re = re->next)
119 status = regexec (&re->re, string,
120 /* nmatch = */ 0, /* pmatch = */ NULL,
124 DEBUG ("regex match: Regular expression `%s' matches `%s'.",
129 DEBUG ("regex match: Regular expression `%s' does not match `%s'.",
131 return (FC_MATCH_NO_MATCH);
136 return (FC_MATCH_MATCHES);
137 } /* }}} int mr_match_regexen */
139 static int mr_add_regex (mr_regex_t **re_head, const char *re_str, /* {{{ */
145 re = calloc (1, sizeof (*re));
148 log_err ("mr_add_regex: calloc failed.");
153 re->re_str = strdup (re_str);
154 if (re->re_str == NULL)
157 log_err ("mr_add_regex: strdup failed.");
161 status = regcomp (&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
165 regerror (status, &re->re, errmsg, sizeof (errmsg));
166 errmsg[sizeof (errmsg) - 1] = 0;
167 log_err ("Compiling regex `%s' for `%s' failed: %s.",
168 re->re_str, option, errmsg);
174 if (*re_head == NULL)
183 while (ptr->next != NULL)
190 } /* }}} int mr_add_regex */
192 static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
195 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
197 log_warn ("`%s' needs exactly one string argument.", ci->key);
201 return mr_add_regex (re_head, ci->values[0].value.string, ci->key);
202 } /* }}} int mr_config_add_regex */
204 static int mr_config_add_meta_regex (llist_t **meta, /* {{{ */
213 if ((ci->values_num != 2)
214 || (ci->values[0].type != OCONFIG_TYPE_STRING)
215 || (ci->values[1].type != OCONFIG_TYPE_STRING))
217 log_warn ("`%s' needs exactly two string arguments.", ci->key);
223 *meta = llist_create();
226 log_err ("mr_config_add_meta_regex: llist_create failed.");
231 key = ci->values[0].value.string;
232 entry = llist_search (*meta, key);
238 log_err ("mr_config_add_meta_regex: strdup failed.");
241 entry = llentry_create (key, NULL);
244 log_err ("mr_config_add_meta_regex: llentry_create failed.");
248 /* key and entry will now be freed by mr_free_match(). */
249 llist_append (*meta, entry);
252 snprintf (buffer, sizeof (buffer), "%s `%s'", ci->key, key);
253 /* Can't pass &entry->value into mr_add_regex, so copy in/out. */
254 re_head = entry->value;
255 status = mr_add_regex (&re_head, ci->values[1].value.string, buffer);
257 entry->value = re_head;
260 } /* }}} int mr_config_add_meta_regex */
262 static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
267 m = calloc (1, sizeof (*m));
270 log_err ("mr_create: calloc failed.");
277 for (int i = 0; i < ci->children_num; i++)
279 oconfig_item_t *child = ci->children + i;
281 if ((strcasecmp ("Host", child->key) == 0)
282 || (strcasecmp ("Hostname", child->key) == 0))
283 status = mr_config_add_regex (&m->host, child);
284 else if (strcasecmp ("Plugin", child->key) == 0)
285 status = mr_config_add_regex (&m->plugin, child);
286 else if (strcasecmp ("PluginInstance", child->key) == 0)
287 status = mr_config_add_regex (&m->plugin_instance, child);
288 else if (strcasecmp ("Type", child->key) == 0)
289 status = mr_config_add_regex (&m->type, child);
290 else if (strcasecmp ("TypeInstance", child->key) == 0)
291 status = mr_config_add_regex (&m->type_instance, child);
292 else if (strcasecmp ("MetaData", child->key) == 0)
293 status = mr_config_add_meta_regex (&m->meta, child);
294 else if (strcasecmp ("Invert", child->key) == 0)
295 status = cf_util_get_boolean(child, &m->invert);
298 log_err ("The `%s' configuration option is not understood and "
299 "will be ignored.", child->key);
307 /* Additional sanity-checking */
310 if ((m->host == NULL)
311 && (m->plugin == NULL)
312 && (m->plugin_instance == NULL)
314 && (m->type_instance == NULL)
315 && (m->meta == NULL))
317 log_err ("No (valid) regular expressions have been configured. "
318 "This match will be ignored.");
333 } /* }}} int mr_create */
335 static int mr_destroy (void **user_data) /* {{{ */
337 if ((user_data != NULL) && (*user_data != NULL))
338 mr_free_match (*user_data);
340 } /* }}} int mr_destroy */
342 static int mr_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
343 const value_list_t *vl,
344 notification_meta_t __attribute__((unused)) **meta,
348 int match_value = FC_MATCH_MATCHES;
349 int nomatch_value = FC_MATCH_NO_MATCH;
351 if ((user_data == NULL) || (*user_data == NULL))
358 match_value = FC_MATCH_NO_MATCH;
359 nomatch_value = FC_MATCH_MATCHES;
362 if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH)
363 return (nomatch_value);
364 if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
365 return (nomatch_value);
366 if (mr_match_regexen (m->plugin_instance,
367 vl->plugin_instance) == FC_MATCH_NO_MATCH)
368 return (nomatch_value);
369 if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
370 return (nomatch_value);
371 if (mr_match_regexen (m->type_instance,
372 vl->type_instance) == FC_MATCH_NO_MATCH)
373 return (nomatch_value);
374 if (vl->meta != NULL)
376 for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next)
378 mr_regex_t *meta_re = (mr_regex_t *) e->value;
380 int status = meta_data_get_string (vl->meta, e->key, &value);
381 if (status == 0) /* key is present */
383 if (mr_match_regexen (meta_re, value) == FC_MATCH_NO_MATCH)
386 return (nomatch_value);
393 return (match_value);
394 } /* }}} int mr_match */
396 void module_register (void)
398 match_proc_t mproc = { 0 };
400 mproc.create = mr_create;
401 mproc.destroy = mr_destroy;
402 mproc.match = mr_match;
403 fc_register_match ("regex", mproc);
404 } /* module_register */
406 /* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */