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 **/
28
29 /*
30 * This module allows to filter and rewrite value lists based on
31 * Perl-compatible regular expressions.
32 */
33
34 #include "collectd.h"
35
36 #include "filter_chain.h"
37 #include "utils/common/common.h"
38 #include "utils/metadata/meta_data.h"
39 #include "utils_llist.h"
40
41 #include <regex.h>
42 #include <sys/types.h>
43
44 #define log_err(...) ERROR("`regex' match: " __VA_ARGS__)
45 #define log_warn(...) WARNING("`regex' match: " __VA_ARGS__)
46
47 /*
48 * private data types
49 */
50
51 struct mr_regex_s;
52 typedef struct mr_regex_s mr_regex_t;
53 struct mr_regex_s {
54 regex_t re;
55 char *re_str;
56
57 mr_regex_t *next;
58 };
59
60 struct mr_match_s;
61 typedef struct mr_match_s mr_match_t;
62 struct mr_match_s {
63 mr_regex_t *host;
64 mr_regex_t *plugin;
65 mr_regex_t *plugin_instance;
66 mr_regex_t *type;
67 mr_regex_t *type_instance;
68 llist_t *meta; /* Maps each meta key into mr_regex_t* */
69 bool invert;
70 };
71
72 /*
73 * internal helper functions
74 */
mr_free_regex(mr_regex_t * r)75 static void mr_free_regex(mr_regex_t *r) /* {{{ */
76 {
77 if (r == NULL)
78 return;
79
80 regfree(&r->re);
81 memset(&r->re, 0, sizeof(r->re));
82 sfree(r->re_str);
83
84 if (r->next != NULL)
85 mr_free_regex(r->next);
86 } /* }}} void mr_free_regex */
87
mr_free_match(mr_match_t * m)88 static void mr_free_match(mr_match_t *m) /* {{{ */
89 {
90 if (m == NULL)
91 return;
92
93 mr_free_regex(m->host);
94 mr_free_regex(m->plugin);
95 mr_free_regex(m->plugin_instance);
96 mr_free_regex(m->type);
97 mr_free_regex(m->type_instance);
98 for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next) {
99 sfree(e->key);
100 mr_free_regex((mr_regex_t *)e->value);
101 }
102 llist_destroy(m->meta);
103
104 sfree(m);
105 } /* }}} void mr_free_match */
106
mr_match_regexen(mr_regex_t * re_head,const char * string)107 static int mr_match_regexen(mr_regex_t *re_head, /* {{{ */
108 const char *string) {
109 if (re_head == NULL)
110 return FC_MATCH_MATCHES;
111
112 for (mr_regex_t *re = re_head; re != NULL; re = re->next) {
113 int status;
114
115 status = regexec(&re->re, string,
116 /* nmatch = */ 0, /* pmatch = */ NULL,
117 /* eflags = */ 0);
118 if (status == 0) {
119 DEBUG("regex match: Regular expression `%s' matches `%s'.", re->re_str,
120 string);
121 } else {
122 DEBUG("regex match: Regular expression `%s' does not match `%s'.",
123 re->re_str, string);
124 return FC_MATCH_NO_MATCH;
125 }
126 }
127
128 return FC_MATCH_MATCHES;
129 } /* }}} int mr_match_regexen */
130
mr_add_regex(mr_regex_t ** re_head,const char * re_str,const char * option)131 static int mr_add_regex(mr_regex_t **re_head, const char *re_str, /* {{{ */
132 const char *option) {
133 mr_regex_t *re;
134 int status;
135
136 re = calloc(1, sizeof(*re));
137 if (re == NULL) {
138 log_err("mr_add_regex: calloc failed.");
139 return -1;
140 }
141 re->next = NULL;
142
143 re->re_str = strdup(re_str);
144 if (re->re_str == NULL) {
145 sfree(re);
146 log_err("mr_add_regex: strdup failed.");
147 return -1;
148 }
149
150 status = regcomp(&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
151 if (status != 0) {
152 char errmsg[1024];
153 regerror(status, &re->re, errmsg, sizeof(errmsg));
154 errmsg[sizeof(errmsg) - 1] = '\0';
155 log_err("Compiling regex `%s' for `%s' failed: %s.", re->re_str, option,
156 errmsg);
157 sfree(re->re_str);
158 sfree(re);
159 return -1;
160 }
161
162 if (*re_head == NULL) {
163 *re_head = re;
164 } else {
165 mr_regex_t *ptr;
166
167 ptr = *re_head;
168 while (ptr->next != NULL)
169 ptr = ptr->next;
170
171 ptr->next = re;
172 }
173
174 return 0;
175 } /* }}} int mr_add_regex */
176
mr_config_add_regex(mr_regex_t ** re_head,oconfig_item_t * ci)177 static int mr_config_add_regex(mr_regex_t **re_head, /* {{{ */
178 oconfig_item_t *ci) {
179 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
180 log_warn("`%s' needs exactly one string argument.", ci->key);
181 return -1;
182 }
183
184 return mr_add_regex(re_head, ci->values[0].value.string, ci->key);
185 } /* }}} int mr_config_add_regex */
186
mr_config_add_meta_regex(llist_t ** meta,oconfig_item_t * ci)187 static int mr_config_add_meta_regex(llist_t **meta, /* {{{ */
188 oconfig_item_t *ci) {
189 char *meta_key;
190 llentry_t *entry;
191 mr_regex_t *re_head;
192 int status;
193 char buffer[1024];
194
195 if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
196 (ci->values[1].type != OCONFIG_TYPE_STRING)) {
197 log_warn("`%s' needs exactly two string arguments.", ci->key);
198 return -1;
199 }
200
201 if (*meta == NULL) {
202 *meta = llist_create();
203 if (*meta == NULL) {
204 log_err("mr_config_add_meta_regex: llist_create failed.");
205 return -1;
206 }
207 }
208
209 meta_key = ci->values[0].value.string;
210 entry = llist_search(*meta, meta_key);
211 if (entry == NULL) {
212 meta_key = strdup(meta_key);
213 if (meta_key == NULL) {
214 log_err("mr_config_add_meta_regex: strdup failed.");
215 return -1;
216 }
217 entry = llentry_create(meta_key, NULL);
218 if (entry == NULL) {
219 log_err("mr_config_add_meta_regex: llentry_create failed.");
220 sfree(meta_key);
221 return -1;
222 }
223 /* meta_key and entry will now be freed by mr_free_match(). */
224 llist_append(*meta, entry);
225 }
226
227 snprintf(buffer, sizeof(buffer), "%s `%s'", ci->key, meta_key);
228 /* Can't pass &entry->value into mr_add_regex, so copy in/out. */
229 re_head = entry->value;
230 status = mr_add_regex(&re_head, ci->values[1].value.string, buffer);
231 if (status == 0) {
232 entry->value = re_head;
233 }
234 return status;
235 } /* }}} int mr_config_add_meta_regex */
236
mr_create(const oconfig_item_t * ci,void ** user_data)237 static int mr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
238 {
239 mr_match_t *m;
240 int status;
241
242 m = calloc(1, sizeof(*m));
243 if (m == NULL) {
244 log_err("mr_create: calloc failed.");
245 return -ENOMEM;
246 }
247
248 m->invert = false;
249
250 status = 0;
251 for (int i = 0; i < ci->children_num; i++) {
252 oconfig_item_t *child = ci->children + i;
253
254 if ((strcasecmp("Host", child->key) == 0) ||
255 (strcasecmp("Hostname", child->key) == 0))
256 status = mr_config_add_regex(&m->host, child);
257 else if (strcasecmp("Plugin", child->key) == 0)
258 status = mr_config_add_regex(&m->plugin, child);
259 else if (strcasecmp("PluginInstance", child->key) == 0)
260 status = mr_config_add_regex(&m->plugin_instance, child);
261 else if (strcasecmp("Type", child->key) == 0)
262 status = mr_config_add_regex(&m->type, child);
263 else if (strcasecmp("TypeInstance", child->key) == 0)
264 status = mr_config_add_regex(&m->type_instance, child);
265 else if (strcasecmp("MetaData", child->key) == 0)
266 status = mr_config_add_meta_regex(&m->meta, child);
267 else if (strcasecmp("Invert", child->key) == 0)
268 status = cf_util_get_boolean(child, &m->invert);
269 else {
270 log_err("The `%s' configuration option is not understood and "
271 "will be ignored.",
272 child->key);
273 status = 0;
274 }
275
276 if (status != 0)
277 break;
278 }
279
280 /* Additional sanity-checking */
281 while (status == 0) {
282 if ((m->host == NULL) && (m->plugin == NULL) &&
283 (m->plugin_instance == NULL) && (m->type == NULL) &&
284 (m->type_instance == NULL) && (m->meta == NULL)) {
285 log_err("No (valid) regular expressions have been configured. "
286 "This match will be ignored.");
287 status = -1;
288 }
289
290 break;
291 }
292
293 if (status != 0) {
294 mr_free_match(m);
295 return status;
296 }
297
298 *user_data = m;
299 return 0;
300 } /* }}} int mr_create */
301
mr_destroy(void ** user_data)302 static int mr_destroy(void **user_data) /* {{{ */
303 {
304 if ((user_data != NULL) && (*user_data != NULL))
305 mr_free_match(*user_data);
306 return 0;
307 } /* }}} int mr_destroy */
308
mr_match(const data_set_t * ds,const value_list_t * vl,notification_meta_t ** meta,void ** user_data)309 static int mr_match(const data_set_t __attribute__((unused)) * ds, /* {{{ */
310 const value_list_t *vl,
311 notification_meta_t __attribute__((unused)) * *meta,
312 void **user_data) {
313 mr_match_t *m;
314 int match_value = FC_MATCH_MATCHES;
315 int nomatch_value = FC_MATCH_NO_MATCH;
316
317 if ((user_data == NULL) || (*user_data == NULL))
318 return -1;
319
320 m = *user_data;
321
322 if (m->invert) {
323 match_value = FC_MATCH_NO_MATCH;
324 nomatch_value = FC_MATCH_MATCHES;
325 }
326
327 if (mr_match_regexen(m->host, vl->host) == FC_MATCH_NO_MATCH)
328 return nomatch_value;
329 if (mr_match_regexen(m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
330 return nomatch_value;
331 if (mr_match_regexen(m->plugin_instance, vl->plugin_instance) ==
332 FC_MATCH_NO_MATCH)
333 return nomatch_value;
334 if (mr_match_regexen(m->type, vl->type) == FC_MATCH_NO_MATCH)
335 return nomatch_value;
336 if (mr_match_regexen(m->type_instance, vl->type_instance) ==
337 FC_MATCH_NO_MATCH)
338 return nomatch_value;
339 for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next) {
340 mr_regex_t *meta_re = (mr_regex_t *)e->value;
341 char *value;
342 int status;
343 if (vl->meta == NULL)
344 return nomatch_value;
345 status = meta_data_as_string(vl->meta, e->key, &value);
346 if (status == (-ENOENT)) /* key is not present */
347 return nomatch_value;
348 if (status != 0) /* some other problem */
349 continue; /* error will have already been printed. */
350 if (mr_match_regexen(meta_re, value) == FC_MATCH_NO_MATCH) {
351 sfree(value);
352 return nomatch_value;
353 }
354 sfree(value);
355 }
356
357 return match_value;
358 } /* }}} int mr_match */
359
module_register(void)360 void module_register(void) {
361 match_proc_t mproc = {0};
362
363 mproc.create = mr_create;
364 mproc.destroy = mr_destroy;
365 mproc.match = mr_match;
366 fc_register_match("regex", mproc);
367 } /* module_register */
368