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 "filter_chain.h"
38 #include <regex.h>
39 #include <sys/types.h>
41 #define log_err(...) ERROR("`regex' match: " __VA_ARGS__)
42 #define log_warn(...) WARNING("`regex' match: " __VA_ARGS__)
44 /*
45 * private data types
46 */
48 struct mr_regex_s;
49 typedef struct mr_regex_s mr_regex_t;
50 struct mr_regex_s {
51 regex_t re;
52 char *re_str;
54 mr_regex_t *next;
55 };
57 struct mr_match_s;
58 typedef struct mr_match_s mr_match_t;
59 struct mr_match_s {
60 mr_regex_t *host;
61 mr_regex_t *plugin;
62 mr_regex_t *plugin_instance;
63 mr_regex_t *type;
64 mr_regex_t *type_instance;
65 _Bool invert;
66 };
68 /*
69 * internal helper functions
70 */
71 static void mr_free_regex(mr_regex_t *r) /* {{{ */
72 {
73 if (r == NULL)
74 return;
76 regfree(&r->re);
77 memset(&r->re, 0, sizeof(r->re));
78 free(r->re_str);
80 if (r->next != NULL)
81 mr_free_regex(r->next);
82 } /* }}} void mr_free_regex */
84 static void mr_free_match(mr_match_t *m) /* {{{ */
85 {
86 if (m == NULL)
87 return;
89 mr_free_regex(m->host);
90 mr_free_regex(m->plugin);
91 mr_free_regex(m->plugin_instance);
92 mr_free_regex(m->type);
93 mr_free_regex(m->type_instance);
95 free(m);
96 } /* }}} void mr_free_match */
98 static int mr_match_regexen(mr_regex_t *re_head, /* {{{ */
99 const char *string) {
100 if (re_head == NULL)
101 return (FC_MATCH_MATCHES);
103 for (mr_regex_t *re = re_head; re != NULL; re = re->next) {
104 int status;
106 status = regexec(&re->re, string,
107 /* nmatch = */ 0, /* pmatch = */ NULL,
108 /* eflags = */ 0);
109 if (status == 0) {
110 DEBUG("regex match: Regular expression `%s' matches `%s'.", re->re_str,
111 string);
112 } else {
113 DEBUG("regex match: Regular expression `%s' does not match `%s'.",
114 re->re_str, string);
115 return (FC_MATCH_NO_MATCH);
116 }
117 }
119 return (FC_MATCH_MATCHES);
120 } /* }}} int mr_match_regexen */
122 static int mr_config_add_regex(mr_regex_t **re_head, /* {{{ */
123 oconfig_item_t *ci) {
124 mr_regex_t *re;
125 int status;
127 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
128 log_warn("`%s' needs exactly one string argument.", ci->key);
129 return (-1);
130 }
132 re = calloc(1, sizeof(*re));
133 if (re == NULL) {
134 log_err("mr_config_add_regex: calloc failed.");
135 return (-1);
136 }
137 re->next = NULL;
139 re->re_str = strdup(ci->values[0].value.string);
140 if (re->re_str == NULL) {
141 free(re);
142 log_err("mr_config_add_regex: strdup failed.");
143 return (-1);
144 }
146 status = regcomp(&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
147 if (status != 0) {
148 char errmsg[1024];
149 regerror(status, &re->re, errmsg, sizeof(errmsg));
150 errmsg[sizeof(errmsg) - 1] = 0;
151 log_err("Compiling regex `%s' for `%s' failed: %s.", re->re_str, ci->key,
152 errmsg);
153 free(re->re_str);
154 free(re);
155 return (-1);
156 }
158 if (*re_head == NULL) {
159 *re_head = re;
160 } else {
161 mr_regex_t *ptr;
163 ptr = *re_head;
164 while (ptr->next != NULL)
165 ptr = ptr->next;
167 ptr->next = re;
168 }
170 return (0);
171 } /* }}} int mr_config_add_regex */
173 static int mr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
174 {
175 mr_match_t *m;
176 int status;
178 m = calloc(1, sizeof(*m));
179 if (m == NULL) {
180 log_err("mr_create: calloc failed.");
181 return (-ENOMEM);
182 }
184 m->invert = 0;
186 status = 0;
187 for (int i = 0; i < ci->children_num; i++) {
188 oconfig_item_t *child = ci->children + i;
190 if ((strcasecmp("Host", child->key) == 0) ||
191 (strcasecmp("Hostname", child->key) == 0))
192 status = mr_config_add_regex(&m->host, child);
193 else if (strcasecmp("Plugin", child->key) == 0)
194 status = mr_config_add_regex(&m->plugin, child);
195 else if (strcasecmp("PluginInstance", child->key) == 0)
196 status = mr_config_add_regex(&m->plugin_instance, child);
197 else if (strcasecmp("Type", child->key) == 0)
198 status = mr_config_add_regex(&m->type, child);
199 else if (strcasecmp("TypeInstance", child->key) == 0)
200 status = mr_config_add_regex(&m->type_instance, child);
201 else if (strcasecmp("Invert", child->key) == 0)
202 status = cf_util_get_boolean(child, &m->invert);
203 else {
204 log_err("The `%s' configuration option is not understood and "
205 "will be ignored.",
206 child->key);
207 status = 0;
208 }
210 if (status != 0)
211 break;
212 }
214 /* Additional sanity-checking */
215 while (status == 0) {
216 if ((m->host == NULL) && (m->plugin == NULL) &&
217 (m->plugin_instance == NULL) && (m->type == NULL) &&
218 (m->type_instance == NULL)) {
219 log_err("No (valid) regular expressions have been configured. "
220 "This match will be ignored.");
221 status = -1;
222 }
224 break;
225 }
227 if (status != 0) {
228 mr_free_match(m);
229 return (status);
230 }
232 *user_data = m;
233 return (0);
234 } /* }}} int mr_create */
236 static int mr_destroy(void **user_data) /* {{{ */
237 {
238 if ((user_data != NULL) && (*user_data != NULL))
239 mr_free_match(*user_data);
240 return (0);
241 } /* }}} int mr_destroy */
243 static int mr_match(const data_set_t __attribute__((unused)) * ds, /* {{{ */
244 const value_list_t *vl,
245 notification_meta_t __attribute__((unused)) * *meta,
246 void **user_data) {
247 mr_match_t *m;
248 int match_value = FC_MATCH_MATCHES;
249 int nomatch_value = FC_MATCH_NO_MATCH;
251 if ((user_data == NULL) || (*user_data == NULL))
252 return (-1);
254 m = *user_data;
256 if (m->invert) {
257 match_value = FC_MATCH_NO_MATCH;
258 nomatch_value = FC_MATCH_MATCHES;
259 }
261 if (mr_match_regexen(m->host, vl->host) == FC_MATCH_NO_MATCH)
262 return (nomatch_value);
263 if (mr_match_regexen(m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
264 return (nomatch_value);
265 if (mr_match_regexen(m->plugin_instance, vl->plugin_instance) ==
266 FC_MATCH_NO_MATCH)
267 return (nomatch_value);
268 if (mr_match_regexen(m->type, vl->type) == FC_MATCH_NO_MATCH)
269 return (nomatch_value);
270 if (mr_match_regexen(m->type_instance, vl->type_instance) ==
271 FC_MATCH_NO_MATCH)
272 return (nomatch_value);
274 return (match_value);
275 } /* }}} int mr_match */
277 void module_register(void) {
278 match_proc_t mproc = {0};
280 mproc.create = mr_create;
281 mproc.destroy = mr_destroy;
282 mproc.match = mr_match;
283 fc_register_match("regex", mproc);
284 } /* module_register */
286 /* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */