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