f7d5e3e75866bc018e95f2bce406eb206b6f094a
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 **/
29 /*
30 * This module allows to filter and rewrite value lists based on
31 * Perl-compatible regular expressions.
32 */
34 #include "collectd.h"
36 #include "filter_chain.h"
38 #include <sys/types.h>
39 #include <regex.h>
41 #define log_err(...) ERROR ("`regex' match: " __VA_ARGS__)
42 #define log_warn(...) WARNING ("`regex' match: " __VA_ARGS__)
44 /*
45 * private data types
46 */
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;
55 mr_regex_t *next;
56 };
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 };
70 /*
71 * internal helper functions
72 */
73 static void mr_free_regex (mr_regex_t *r) /* {{{ */
74 {
75 if (r == NULL)
76 return;
78 regfree (&r->re);
79 memset (&r->re, 0, sizeof (r->re));
80 free (r->re_str);
82 if (r->next != NULL)
83 mr_free_regex (r->next);
84 } /* }}} void mr_free_regex */
86 static void mr_free_match (mr_match_t *m) /* {{{ */
87 {
88 if (m == NULL)
89 return;
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);
97 free (m);
98 } /* }}} void mr_free_match */
100 static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
101 const char *string)
102 {
103 mr_regex_t *re;
105 if (re_head == NULL)
106 return (FC_MATCH_MATCHES);
108 for (re = re_head; re != NULL; re = re->next)
109 {
110 int status;
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 }
127 }
129 return (FC_MATCH_MATCHES);
130 } /* }}} int mr_match_regexen */
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;
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 }
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;
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 }
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 }
173 if (*re_head == NULL)
174 {
175 *re_head = re;
176 }
177 else
178 {
179 mr_regex_t *ptr;
181 ptr = *re_head;
182 while (ptr->next != NULL)
183 ptr = ptr->next;
185 ptr->next = re;
186 }
188 return (0);
189 } /* }}} int mr_config_add_regex */
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;
197 m = calloc (1, sizeof (*m));
198 if (m == NULL)
199 {
200 log_err ("mr_create: calloc failed.");
201 return (-ENOMEM);
202 }
204 m->invert = 0;
206 status = 0;
207 for (i = 0; i < ci->children_num; i++)
208 {
209 oconfig_item_t *child = ci->children + i;
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 }
231 if (status != 0)
232 break;
233 }
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 }
249 break;
250 }
252 if (status != 0)
253 {
254 mr_free_match (m);
255 return (status);
256 }
258 *user_data = m;
259 return (0);
260 } /* }}} int mr_create */
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 */
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;
278 if ((user_data == NULL) || (*user_data == NULL))
279 return (-1);
281 m = *user_data;
283 if (m->invert)
284 {
285 match_value = FC_MATCH_NO_MATCH;
286 nomatch_value = FC_MATCH_MATCHES;
287 }
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);
302 return (match_value);
303 } /* }}} int mr_match */
305 void module_register (void)
306 {
307 match_proc_t mproc = { 0 };
309 mproc.create = mr_create;
310 mproc.destroy = mr_destroy;
311 mproc.match = mr_match;
312 fc_register_match ("regex", mproc);
313 } /* module_register */
315 /* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */