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 **/
22 /*
23 * This module allows to filter value lists based on Perl-compatible regular
24 * expressions.
25 */
27 #include "collectd.h"
28 #include "configfile.h"
29 #include "plugin.h"
30 #include "common.h"
32 #include <pcre.h>
34 #define log_err(...) ERROR ("filter_pcre: " __VA_ARGS__)
35 #define log_warn(...) WARNING ("filter_pcre: " __VA_ARGS__)
37 /*
38 * private data types
39 */
41 typedef struct {
42 pcre *re;
43 pcre_extra *extra;
44 } c_pcre_t;
46 #define C_PCRE_INIT(regex) do { \
47 (regex).re = NULL; \
48 (regex).extra = NULL; \
49 } while (0)
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)
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;
64 int action;
65 } regex_t;
67 /*
68 * private variables
69 */
71 static regex_t *regexes = NULL;
72 static int regexes_num = 0;
74 /*
75 * internal helper functions
76 */
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];
84 if (NULL == re)
85 return 1;
87 if (NULL == string)
88 string = "";
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));
99 if (0 <= status)
100 return 1;
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 */
108 static regex_t *regex_new (void)
109 {
110 regex_t *re;
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 }
119 re = regexes + (regexes_num - 1);
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);
127 re->action = 0;
128 return re;
129 } /* regex_new */
131 static void regex_delete (regex_t *re)
132 {
133 if (NULL == re)
134 return;
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);
142 re->action = 0;
143 } /* regex_delete */
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;
150 if (NULL == re)
151 return 1;
153 if ((NULL == re->host.re) || c_pcre_match (&re->host, vl->host))
154 ++matches;
156 if ((NULL == re->plugin.re) || c_pcre_match (&re->plugin, vl->plugin))
157 ++matches;
159 if ((NULL == re->plugin_instance.re)
160 || c_pcre_match (&re->plugin_instance, vl->plugin_instance))
161 ++matches;
163 if ((NULL == re->type.re) || c_pcre_match (&re->type, vl->type))
164 ++matches;
166 if ((NULL == re->type_instance.re)
167 || c_pcre_match (&re->type_instance, vl->type_instance))
168 ++matches;
170 if (5 == matches)
171 return 1;
172 return 0;
173 } /* regex_match */
175 /*
176 * interface to collectd
177 */
179 static int c_pcre_filter (const data_set_t *ds, value_list_t *vl)
180 {
181 int i;
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 */
189 static int c_pcre_shutdown (void)
190 {
191 int i;
193 plugin_unregister_filter ("filter_pcre");
194 plugin_unregister_shutdown ("filter_pcre");
196 for (i = 0; i < regexes_num; ++i)
197 regex_delete (regexes + i);
199 sfree (regexes);
200 regexes_num = 0;
201 return 0;
202 } /* c_pcre_shutdown */
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;
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 }
216 pattern = ci->values[0].value.string;
218 re->re = pcre_compile (pattern,
219 /* options = */ 0,
220 /* errptr = */ &errptr,
221 /* erroffset = */ &erroffset,
222 /* tableptr = */ NULL);
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 }
230 re->extra = pcre_study (re->re,
231 /* options = */ 0,
232 /* errptr = */ &errptr);
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 */
242 static int config_set_action (int *action, oconfig_item_t *ci)
243 {
244 const char *action_str;
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 }
252 action_str = ci->values[0].value.string;
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 */
265 static int c_pcre_config_regex (oconfig_item_t *ci)
266 {
267 regex_t *re;
268 int i;
270 if (0 != ci->values_num) {
271 log_err ("<RegEx> expects no arguments.");
272 return 1;
273 }
275 re = regex_new ();
277 for (i = 0; i < ci->children_num; ++i) {
278 oconfig_item_t *c = ci->children + i;
279 int status = 0;
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);
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 */
305 static int c_pcre_config (oconfig_item_t *ci)
306 {
307 int i;
309 for (i = 0; i < ci->children_num; ++i) {
310 oconfig_item_t *c = ci->children + i;
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 }
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 */
323 void module_register (void)
324 {
325 plugin_register_complex_config ("filter_pcre", c_pcre_config);
326 } /* module_register */
328 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */