a92dee0b0405319db077852de9f12318ab2fd17e
[collectd.git] / src / filter_pcre.c
1 /**
2  * collectd - src/filter_pcre.c
3  * Copyright (C) 2008  Sebastian Harl
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Author:
19  *   Sebastian Harl <sh at tokkee.org>
20  **/
21
22 /*
23  * This module allows to filter value lists based on Perl-compatible regular
24  * expressions.
25  */
26
27 #include "collectd.h"
28 #include "configfile.h"
29 #include "plugin.h"
30 #include "common.h"
31
32 #include <pcre.h>
33
34 #define log_err(...) ERROR ("filter_pcre: " __VA_ARGS__)
35 #define log_warn(...) WARNING ("filter_pcre: " __VA_ARGS__)
36
37 /*
38  * private data types
39  */
40
41 typedef struct {
42         pcre       *re;
43         pcre_extra *extra;
44 } c_pcre_t;
45
46 #define C_PCRE_INIT(regex) do { \
47                 (regex).re    = NULL; \
48                 (regex).extra = NULL; \
49         } while (0)
50
51 #define C_PCRE_FREE(regex) do { \
52                 pcre_free ((regex).re); \
53                 pcre_free ((regex).extra); \
54                 C_PCRE_INIT (regex); \
55         } while (0)
56
57 typedef struct {
58         c_pcre_t host;
59         c_pcre_t plugin;
60         c_pcre_t plugin_instance;
61         c_pcre_t type;
62         c_pcre_t type_instance;
63
64         int action;
65 } regex_t;
66
67 /*
68  * private variables
69  */
70
71 static regex_t *regexes     = NULL;
72 static int      regexes_num = 0;
73
74 /*
75  * internal helper functions
76  */
77
78 /* returns true if string matches the regular expression */
79 static int c_pcre_match (c_pcre_t *re, const char *string)
80 {
81         int status;
82         int ovector[30];
83
84         if (NULL == re)
85                 return 1;
86
87         if (NULL == string)
88                 string = "";
89
90         status = pcre_exec (re->re,
91                         /* extra       = */ re->extra,
92                         /* subject     = */ string,
93                         /* length      = */ strlen (string),
94                         /* startoffset = */ 0,
95                         /* options     = */ 0,
96                         /* ovector     = */ ovector,
97                         /* ovecsize    = */ STATIC_ARRAY_SIZE (ovector));
98
99         if (0 <= status)
100                 return 1;
101
102         if (PCRE_ERROR_NOMATCH != status)
103                 log_err ("PCRE matching of string \"%s\" failed with status %d",
104                                 string, status);
105         return 0;
106 } /* c_pcre_match */
107
108 static regex_t *regex_new (void)
109 {
110         regex_t *re;
111
112         ++regexes_num;
113         regexes = (regex_t *)realloc (regexes, regexes_num * sizeof (*regexes));
114         if (NULL == regexes) {
115                 log_err ("Out of memory.");
116                 exit (5);
117         }
118
119         re = regexes + (regexes_num - 1);
120
121         C_PCRE_INIT (re->host);
122         C_PCRE_INIT (re->plugin);
123         C_PCRE_INIT (re->plugin_instance);
124         C_PCRE_INIT (re->type);
125         C_PCRE_INIT (re->type_instance);
126
127         re->action = 0;
128         return re;
129 } /* regex_new */
130
131 static void regex_delete (regex_t *re)
132 {
133         if (NULL == re)
134                 return;
135
136         C_PCRE_FREE (re->host);
137         C_PCRE_FREE (re->plugin);
138         C_PCRE_FREE (re->plugin_instance);
139         C_PCRE_FREE (re->type);
140         C_PCRE_FREE (re->type_instance);
141
142         re->action = 0;
143 } /* regex_delete */
144
145 /* returns true if the value list matches the regular expression */
146 static int regex_match (regex_t *re, value_list_t *vl)
147 {
148         int matches = 0;
149
150         if (NULL == re)
151                 return 1;
152
153         if ((NULL == re->host.re) || c_pcre_match (&re->host, vl->host))
154                 ++matches;
155
156         if ((NULL == re->plugin.re) || c_pcre_match (&re->plugin, vl->plugin))
157                 ++matches;
158
159         if ((NULL == re->plugin_instance.re)
160                         || c_pcre_match (&re->plugin_instance, vl->plugin_instance))
161                 ++matches;
162
163         if ((NULL == re->type.re) || c_pcre_match (&re->type, vl->type))
164                 ++matches;
165
166         if ((NULL == re->type_instance.re)
167                         || c_pcre_match (&re->type_instance, vl->type_instance))
168                 ++matches;
169
170         if (5 == matches)
171                 return 1;
172         return 0;
173 } /* regex_match */
174
175 /*
176  * interface to collectd
177  */
178
179 static int c_pcre_filter (const data_set_t *ds, value_list_t *vl)
180 {
181         int i;
182
183         for (i = 0; i < regexes_num; ++i)
184                 if (regex_match (regexes + i, vl))
185                         return regexes[i].action;
186         return 0;
187 } /* c_pcre_filter */
188
189 static int c_pcre_shutdown (void)
190 {
191         int i;
192
193         plugin_unregister_filter ("filter_pcre");
194         plugin_unregister_shutdown ("filter_pcre");
195
196         for (i = 0; i < regexes_num; ++i)
197                 regex_delete (regexes + i);
198
199         sfree (regexes);
200         regexes_num = 0;
201         return 0;
202 } /* c_pcre_shutdown */
203
204 static int config_set_regex (c_pcre_t *re, oconfig_item_t *ci)
205 {
206         const char *pattern;
207         const char *errptr;
208         int erroffset;
209
210         if ((0 != ci->children_num) || (1 != ci->values_num)
211                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
212                 log_err ("<RegEx>: %s expects a single string argument.", ci->key);
213                 return 1;
214         }
215
216         pattern = ci->values[0].value.string;
217
218         re->re = pcre_compile (pattern,
219                         /* options   = */ 0,
220                         /* errptr    = */ &errptr,
221                         /* erroffset = */ &erroffset,
222                         /* tableptr  = */ NULL);
223
224         if (NULL == re->re) {
225                 log_err ("<RegEx>: PCRE compilation of pattern \"%s\" failed "
226                                 "at offset %d: %s", pattern, erroffset, errptr);
227                 return 1;
228         }
229
230         re->extra = pcre_study (re->re,
231                         /* options = */ 0,
232                         /* errptr  = */ &errptr);
233
234         if (NULL != errptr) {
235                 log_err ("<RegEx>: PCRE studying of pattern \"%s\" failed: %s",
236                                 pattern, errptr);
237                 return 1;
238         }
239         return 0;
240 } /* config_set_regex */
241
242 static int config_set_action (int *action, oconfig_item_t *ci)
243 {
244         const char *action_str;
245
246         if ((0 != ci->children_num) || (1 != ci->values_num)
247                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
248                 log_err ("<RegEx>: Action expects a single string argument.");
249                 return 1;
250         }
251
252         action_str = ci->values[0].value.string;
253
254         if (0 == strcasecmp (action_str, "NoWrite"))
255                 *action |= FILTER_NOWRITE;
256         else if (0 == strcasecmp (action_str, "NoThresholdCheck"))
257                 *action |= FILTER_NOTHRESHOLD_CHECK;
258         else if (0 == strcasecmp (action_str, "Ignore"))
259                 *action |= FILTER_IGNORE;
260         else
261                 log_warn ("<Regex>: Ignoring unknown action \"%s\".", action_str);
262         return 0;
263 } /* config_set_action */
264
265 static int c_pcre_config_regex (oconfig_item_t *ci)
266 {
267         regex_t *re;
268         int i;
269
270         if (0 != ci->values_num) {
271                 log_err ("<RegEx> expects no arguments.");
272                 return 1;
273         }
274
275         re = regex_new ();
276
277         for (i = 0; i < ci->children_num; ++i) {
278                 oconfig_item_t *c = ci->children + i;
279                 int status = 0;
280
281                 if (0 == strcasecmp (c->key, "Host"))
282                         status = config_set_regex (&re->host, c);
283                 else if (0 == strcasecmp (c->key, "Plugin"))
284                         status = config_set_regex (&re->plugin, c);
285                 else if (0 == strcasecmp (c->key, "PluginInstance"))
286                         status = config_set_regex (&re->plugin_instance, c);
287                 else if (0 == strcasecmp (c->key, "Type"))
288                         status = config_set_regex (&re->type, c);
289                 else if (0 == strcasecmp (c->key, "TypeInstance"))
290                         status = config_set_regex (&re->type_instance, c);
291                 else if (0 == strcasecmp (c->key, "Action"))
292                         status = config_set_action (&re->action, c);
293                 else
294                         log_warn ("<RegEx>: Ignoring unknown config key \"%s\".", c->key);
295
296                 if (0 != status) {
297                         log_err ("Ignoring regular expression definition.");
298                         regex_delete (re);
299                         --regexes_num;
300                 }
301         }
302         return 0;
303 } /* c_pcre_config_regex */
304
305 static int c_pcre_config (oconfig_item_t *ci)
306 {
307         int i;
308
309         for (i = 0; i < ci->children_num; ++i) {
310                 oconfig_item_t *c = ci->children + i;
311
312                 if (0 == strcasecmp (c->key, "RegEx"))
313                         c_pcre_config_regex (c);
314                 else
315                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
316         }
317
318         plugin_register_filter ("filter_pcre", c_pcre_filter);
319         plugin_register_shutdown ("filter_pcre", c_pcre_shutdown);
320         return 0;
321 } /* c_pcre_config */
322
323 void module_register (void)
324 {
325         plugin_register_complex_config ("filter_pcre", c_pcre_config);
326 } /* module_register */
327
328 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
329