3b6db4337cdbd0b982cad7cbaa944fcedce3648d
1 /**
2 * collectd - src/match_regex.c
3 * Copyright (C) 2008 Sebastian Harl
4 * Copyright (C) 2008 Florian Forster
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; only version 2 of the License is applicable.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors:
20 * Sebastian Harl <sh at tokkee.org>
21 * Florian Forster <octo at verplant.org>
22 **/
24 /*
25 * This module allows to filter and rewrite value lists based on
26 * Perl-compatible regular expressions.
27 */
29 #include "collectd.h"
30 #include "filter_chain.h"
32 #include <sys/types.h>
33 #include <regex.h>
35 #define log_err(...) ERROR ("`regex' match: " __VA_ARGS__)
36 #define log_warn(...) WARNING ("`regex' match: " __VA_ARGS__)
38 /*
39 * private data types
40 */
42 struct mr_regex_s;
43 typedef struct mr_regex_s mr_regex_t;
44 struct mr_regex_s
45 {
46 regex_t re;
47 char *re_str;
49 mr_regex_t *next;
50 };
52 struct mr_match_s;
53 typedef struct mr_match_s mr_match_t;
54 struct mr_match_s
55 {
56 mr_regex_t *host;
57 mr_regex_t *plugin;
58 mr_regex_t *plugin_instance;
59 mr_regex_t *type;
60 mr_regex_t *type_instance;
61 int invert;
62 };
64 /*
65 * internal helper functions
66 */
67 static void mr_free_regex (mr_regex_t *r) /* {{{ */
68 {
69 if (r == NULL)
70 return;
72 regfree (&r->re);
73 memset (&r->re, 0, sizeof (r->re));
74 free (r->re_str);
76 if (r->next != NULL)
77 mr_free_regex (r->next);
78 } /* }}} void mr_free_regex */
80 static void mr_free_match (mr_match_t *m) /* {{{ */
81 {
82 if (m == NULL)
83 return;
85 mr_free_regex (m->host);
86 mr_free_regex (m->plugin);
87 mr_free_regex (m->plugin_instance);
88 mr_free_regex (m->type);
89 mr_free_regex (m->type_instance);
91 free (m);
92 } /* }}} void mr_free_match */
94 static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
95 const char *string)
96 {
97 mr_regex_t *re;
99 if (re_head == NULL)
100 return (FC_MATCH_MATCHES);
102 for (re = re_head; re != NULL; re = re->next)
103 {
104 int status;
106 status = regexec (&re->re, string,
107 /* nmatch = */ 0, /* pmatch = */ NULL,
108 /* eflags = */ 0);
109 if (status == 0)
110 {
111 DEBUG ("regex match: Regular expression `%s' matches `%s'.",
112 re->re_str, string);
113 }
114 else
115 {
116 DEBUG ("regex match: Regular expression `%s' does not match `%s'.",
117 re->re_str, string);
118 return (FC_MATCH_NO_MATCH);
119 }
121 }
123 return (FC_MATCH_MATCHES);
124 } /* }}} int mr_match_regexen */
126 static int mr_config_add_boolean (int *ret_value, /* {{{ */
127 oconfig_item_t *ci)
128 {
130 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
131 {
132 ERROR ("`regex' match: `%s' needs exactly one boolean argument.",
133 ci->key);
134 return (-1);
135 }
137 if (ci->values[0].value.boolean)
138 *ret_value = 1;
139 else
140 *ret_value = 0;
142 return (0);
143 } /* }}} int mv_config_add_boolean */
145 static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
146 oconfig_item_t *ci)
147 {
148 mr_regex_t *re;
149 int status;
151 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
152 {
153 log_warn ("`%s' needs exactly one string argument.", ci->key);
154 return (-1);
155 }
157 re = (mr_regex_t *) malloc (sizeof (*re));
158 if (re == NULL)
159 {
160 log_err ("mr_config_add_regex: malloc failed.");
161 return (-1);
162 }
163 memset (re, 0, sizeof (*re));
164 re->next = NULL;
166 re->re_str = strdup (ci->values[0].value.string);
167 if (re->re_str == NULL)
168 {
169 free (re);
170 log_err ("mr_config_add_regex: strdup failed.");
171 return (-1);
172 }
174 status = regcomp (&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
175 if (status != 0)
176 {
177 char errmsg[1024];
178 regerror (status, &re->re, errmsg, sizeof (errmsg));
179 errmsg[sizeof (errmsg) - 1] = 0;
180 log_err ("Compiling regex `%s' for `%s' failed: %s.",
181 re->re_str, ci->key, errmsg);
182 free (re->re_str);
183 free (re);
184 return (-1);
185 }
187 if (*re_head == NULL)
188 {
189 *re_head = re;
190 }
191 else
192 {
193 mr_regex_t *ptr;
195 ptr = *re_head;
196 while (ptr->next != NULL)
197 ptr = ptr->next;
199 ptr->next = re;
200 }
202 return (0);
203 } /* }}} int mr_config_add_regex */
205 static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
206 {
207 mr_match_t *m;
208 int status;
209 int i;
211 m = (mr_match_t *) malloc (sizeof (*m));
212 if (m == NULL)
213 {
214 log_err ("mr_create: malloc failed.");
215 return (-ENOMEM);
216 }
217 memset (m, 0, sizeof (*m));
219 m->invert = 0;
221 status = 0;
222 for (i = 0; i < ci->children_num; i++)
223 {
224 oconfig_item_t *child = ci->children + i;
226 if ((strcasecmp ("Host", child->key) == 0)
227 || (strcasecmp ("Hostname", child->key) == 0))
228 status = mr_config_add_regex (&m->host, child);
229 else if (strcasecmp ("Plugin", child->key) == 0)
230 status = mr_config_add_regex (&m->plugin, child);
231 else if (strcasecmp ("PluginInstance", child->key) == 0)
232 status = mr_config_add_regex (&m->plugin_instance, child);
233 else if (strcasecmp ("Type", child->key) == 0)
234 status = mr_config_add_regex (&m->type, child);
235 else if (strcasecmp ("TypeInstance", child->key) == 0)
236 status = mr_config_add_regex (&m->type_instance, child);
237 else if (strcasecmp ("Invert", child->key) == 0)
238 status = mr_config_add_boolean(&m->invert, child);
239 else
240 {
241 log_err ("The `%s' configuration option is not understood and "
242 "will be ignored.", child->key);
243 status = 0;
244 }
246 if (status != 0)
247 break;
248 }
250 /* Additional sanity-checking */
251 while (status == 0)
252 {
253 if ((m->host == NULL)
254 && (m->plugin == NULL)
255 && (m->plugin_instance == NULL)
256 && (m->type == NULL)
257 && (m->type_instance == NULL))
258 {
259 log_err ("No (valid) regular expressions have been configured. "
260 "This match will be ignored.");
261 status = -1;
262 }
264 break;
265 }
267 if (status != 0)
268 {
269 mr_free_match (m);
270 return (status);
271 }
273 *user_data = m;
274 return (0);
275 } /* }}} int mr_create */
277 static int mr_destroy (void **user_data) /* {{{ */
278 {
279 if ((user_data != NULL) && (*user_data != NULL))
280 mr_free_match (*user_data);
281 return (0);
282 } /* }}} int mr_destroy */
284 static int mr_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
285 const value_list_t *vl,
286 notification_meta_t __attribute__((unused)) **meta,
287 void **user_data)
288 {
289 mr_match_t *m;
291 if ((user_data == NULL) || (*user_data == NULL))
292 return (-1);
294 m = *user_data;
296 int match_value = FC_MATCH_MATCHES;
297 int nomatch_value = FC_MATCH_NO_MATCH;
299 if (m->invert)
300 {
301 match_value = FC_MATCH_NO_MATCH;
302 nomatch_value = FC_MATCH_MATCHES;
303 }
305 if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH)
306 return (nomatch_value);
307 if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
308 return (nomatch_value);
309 if (mr_match_regexen (m->plugin_instance,
310 vl->plugin_instance) == FC_MATCH_NO_MATCH)
311 return (nomatch_value);
312 if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
313 return (nomatch_value);
314 if (mr_match_regexen (m->type_instance,
315 vl->type_instance) == FC_MATCH_NO_MATCH)
316 return (nomatch_value);
318 return (match_value);
319 } /* }}} int mr_match */
321 void module_register (void)
322 {
323 match_proc_t mproc;
325 memset (&mproc, 0, sizeof (mproc));
326 mproc.create = mr_create;
327 mproc.destroy = mr_destroy;
328 mproc.match = mr_match;
329 fc_register_match ("regex", mproc);
330 } /* module_register */
332 /* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */