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