1 /**
2 * collectd - src/match_regex.c
3 * Copyright (C) 2008 Sebastian Harl
4 * Copyright (C) 2008 Florian Forster
5 *
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:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
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.
23 *
24 * Authors:
25 * Sebastian Harl <sh at tokkee.org>
26 * Florian Forster <octo at collectd.org>
27 **/
29 /*
30 * This module allows to filter and rewrite value lists based on
31 * Perl-compatible regular expressions.
32 */
34 #include "collectd.h"
36 #include "common.h"
37 #include "filter_chain.h"
38 #include "meta_data.h"
39 #include "utils_llist.h"
41 #include <sys/types.h>
42 #include <regex.h>
44 #define log_err(...) ERROR ("`regex' match: " __VA_ARGS__)
45 #define log_warn(...) WARNING ("`regex' match: " __VA_ARGS__)
47 /*
48 * private data types
49 */
51 struct mr_regex_s;
52 typedef struct mr_regex_s mr_regex_t;
53 struct mr_regex_s
54 {
55 regex_t re;
56 char *re_str;
58 mr_regex_t *next;
59 };
61 struct mr_match_s;
62 typedef struct mr_match_s mr_match_t;
63 struct mr_match_s
64 {
65 mr_regex_t *host;
66 mr_regex_t *plugin;
67 mr_regex_t *plugin_instance;
68 mr_regex_t *type;
69 mr_regex_t *type_instance;
70 llist_t *meta; /* Maps each meta key into mr_regex_t* */
71 _Bool invert;
72 };
74 /*
75 * internal helper functions
76 */
77 static void mr_free_regex (mr_regex_t *r) /* {{{ */
78 {
79 if (r == NULL)
80 return;
82 regfree (&r->re);
83 memset (&r->re, 0, sizeof (r->re));
84 sfree (r->re_str);
86 if (r->next != NULL)
87 mr_free_regex (r->next);
88 } /* }}} void mr_free_regex */
90 static void mr_free_match (mr_match_t *m) /* {{{ */
91 {
92 if (m == NULL)
93 return;
95 mr_free_regex (m->host);
96 mr_free_regex (m->plugin);
97 mr_free_regex (m->plugin_instance);
98 mr_free_regex (m->type);
99 mr_free_regex (m->type_instance);
100 for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next)
101 {
102 sfree (e->key);
103 mr_free_regex ((mr_regex_t *) e->value);
104 }
105 llist_destroy (m->meta);
107 sfree (m);
108 } /* }}} void mr_free_match */
110 static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
111 const char *string)
112 {
113 if (re_head == NULL)
114 return (FC_MATCH_MATCHES);
116 for (mr_regex_t *re = re_head; re != NULL; re = re->next)
117 {
118 int status;
120 status = regexec (&re->re, string,
121 /* nmatch = */ 0, /* pmatch = */ NULL,
122 /* eflags = */ 0);
123 if (status == 0)
124 {
125 DEBUG ("regex match: Regular expression `%s' matches `%s'.",
126 re->re_str, string);
127 }
128 else
129 {
130 DEBUG ("regex match: Regular expression `%s' does not match `%s'.",
131 re->re_str, string);
132 return (FC_MATCH_NO_MATCH);
133 }
135 }
137 return (FC_MATCH_MATCHES);
138 } /* }}} int mr_match_regexen */
140 static int mr_add_regex (mr_regex_t **re_head, const char *re_str, /* {{{ */
141 const char *option)
142 {
143 mr_regex_t *re;
144 int status;
146 re = calloc (1, sizeof (*re));
147 if (re == NULL)
148 {
149 log_err ("mr_add_regex: calloc failed.");
150 return (-1);
151 }
152 re->next = NULL;
154 re->re_str = strdup (re_str);
155 if (re->re_str == NULL)
156 {
157 sfree (re);
158 log_err ("mr_add_regex: strdup failed.");
159 return (-1);
160 }
162 status = regcomp (&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
163 if (status != 0)
164 {
165 char errmsg[1024];
166 regerror (status, &re->re, errmsg, sizeof (errmsg));
167 errmsg[sizeof (errmsg) - 1] = 0;
168 log_err ("Compiling regex `%s' for `%s' failed: %s.",
169 re->re_str, option, errmsg);
170 sfree (re->re_str);
171 sfree (re);
172 return (-1);
173 }
175 if (*re_head == NULL)
176 {
177 *re_head = re;
178 }
179 else
180 {
181 mr_regex_t *ptr;
183 ptr = *re_head;
184 while (ptr->next != NULL)
185 ptr = ptr->next;
187 ptr->next = re;
188 }
190 return (0);
191 } /* }}} int mr_add_regex */
193 static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
194 oconfig_item_t *ci)
195 {
196 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
197 {
198 log_warn ("`%s' needs exactly one string argument.", ci->key);
199 return (-1);
200 }
202 return mr_add_regex (re_head, ci->values[0].value.string, ci->key);
203 } /* }}} int mr_config_add_regex */
205 static int mr_config_add_meta_regex (llist_t **meta, /* {{{ */
206 oconfig_item_t *ci)
207 {
208 char *meta_key;
209 llentry_t *entry;
210 mr_regex_t *re_head;
211 int status;
212 char buffer[1024];
214 if ((ci->values_num != 2)
215 || (ci->values[0].type != OCONFIG_TYPE_STRING)
216 || (ci->values[1].type != OCONFIG_TYPE_STRING))
217 {
218 log_warn ("`%s' needs exactly two string arguments.", ci->key);
219 return (-1);
220 }
222 if (*meta == NULL)
223 {
224 *meta = llist_create();
225 if (*meta == NULL)
226 {
227 log_err ("mr_config_add_meta_regex: llist_create failed.");
228 return (-1);
229 }
230 }
232 meta_key = ci->values[0].value.string;
233 entry = llist_search (*meta, meta_key);
234 if (entry == NULL)
235 {
236 meta_key = strdup (meta_key);
237 if (meta_key == NULL)
238 {
239 log_err ("mr_config_add_meta_regex: strdup failed.");
240 return (-1);
241 }
242 entry = llentry_create (meta_key, NULL);
243 if (entry == NULL)
244 {
245 log_err ("mr_config_add_meta_regex: llentry_create failed.");
246 sfree (meta_key);
247 return (-1);
248 }
249 /* meta_key and entry will now be freed by mr_free_match(). */
250 llist_append (*meta, entry);
251 }
253 ssnprintf (buffer, sizeof (buffer), "%s `%s'", ci->key, meta_key);
254 /* Can't pass &entry->value into mr_add_regex, so copy in/out. */
255 re_head = entry->value;
256 status = mr_add_regex (&re_head, ci->values[1].value.string, buffer);
257 if (status == 0) {
258 entry->value = re_head;
259 }
260 return status;
261 } /* }}} int mr_config_add_meta_regex */
263 static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
264 {
265 mr_match_t *m;
266 int status;
268 m = calloc (1, sizeof (*m));
269 if (m == NULL)
270 {
271 log_err ("mr_create: calloc failed.");
272 return (-ENOMEM);
273 }
275 m->invert = 0;
277 status = 0;
278 for (int i = 0; i < ci->children_num; i++)
279 {
280 oconfig_item_t *child = ci->children + i;
282 if ((strcasecmp ("Host", child->key) == 0)
283 || (strcasecmp ("Hostname", child->key) == 0))
284 status = mr_config_add_regex (&m->host, child);
285 else if (strcasecmp ("Plugin", child->key) == 0)
286 status = mr_config_add_regex (&m->plugin, child);
287 else if (strcasecmp ("PluginInstance", child->key) == 0)
288 status = mr_config_add_regex (&m->plugin_instance, child);
289 else if (strcasecmp ("Type", child->key) == 0)
290 status = mr_config_add_regex (&m->type, child);
291 else if (strcasecmp ("TypeInstance", child->key) == 0)
292 status = mr_config_add_regex (&m->type_instance, child);
293 else if (strcasecmp ("MetaData", child->key) == 0)
294 status = mr_config_add_meta_regex (&m->meta, child);
295 else if (strcasecmp ("Invert", child->key) == 0)
296 status = cf_util_get_boolean(child, &m->invert);
297 else
298 {
299 log_err ("The `%s' configuration option is not understood and "
300 "will be ignored.", child->key);
301 status = 0;
302 }
304 if (status != 0)
305 break;
306 }
308 /* Additional sanity-checking */
309 while (status == 0)
310 {
311 if ((m->host == NULL)
312 && (m->plugin == NULL)
313 && (m->plugin_instance == NULL)
314 && (m->type == NULL)
315 && (m->type_instance == NULL)
316 && (m->meta == NULL))
317 {
318 log_err ("No (valid) regular expressions have been configured. "
319 "This match will be ignored.");
320 status = -1;
321 }
323 break;
324 }
326 if (status != 0)
327 {
328 mr_free_match (m);
329 return (status);
330 }
332 *user_data = m;
333 return (0);
334 } /* }}} int mr_create */
336 static int mr_destroy (void **user_data) /* {{{ */
337 {
338 if ((user_data != NULL) && (*user_data != NULL))
339 mr_free_match (*user_data);
340 return (0);
341 } /* }}} int mr_destroy */
343 static int mr_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
344 const value_list_t *vl,
345 notification_meta_t __attribute__((unused)) **meta,
346 void **user_data)
347 {
348 mr_match_t *m;
349 int match_value = FC_MATCH_MATCHES;
350 int nomatch_value = FC_MATCH_NO_MATCH;
352 if ((user_data == NULL) || (*user_data == NULL))
353 return (-1);
355 m = *user_data;
357 if (m->invert)
358 {
359 match_value = FC_MATCH_NO_MATCH;
360 nomatch_value = FC_MATCH_MATCHES;
361 }
363 if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH)
364 return (nomatch_value);
365 if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
366 return (nomatch_value);
367 if (mr_match_regexen (m->plugin_instance,
368 vl->plugin_instance) == FC_MATCH_NO_MATCH)
369 return (nomatch_value);
370 if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
371 return (nomatch_value);
372 if (mr_match_regexen (m->type_instance,
373 vl->type_instance) == FC_MATCH_NO_MATCH)
374 return (nomatch_value);
375 if (vl->meta != NULL)
376 {
377 for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next)
378 {
379 mr_regex_t *meta_re = (mr_regex_t *) e->value;
380 char *value;
381 int status = meta_data_get_string (vl->meta, e->key, &value);
382 if (status == (-ENOENT)) /* key is not present */
383 return (nomatch_value);
384 if (status != 0) /* some other problem */
385 continue; /* error will have already been printed. */
386 if (mr_match_regexen (meta_re, value) == FC_MATCH_NO_MATCH)
387 {
388 sfree (value);
389 return (nomatch_value);
390 }
391 sfree (value);
392 }
393 }
395 return (match_value);
396 } /* }}} int mr_match */
398 void module_register (void)
399 {
400 match_proc_t mproc = { 0 };
402 mproc.create = mr_create;
403 mproc.destroy = mr_destroy;
404 mproc.match = mr_match;
405 fc_register_match ("regex", mproc);
406 } /* module_register */
408 /* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */