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