1 /**
2 * collectd - src/match_regex.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 * Authors:
19 * Sebastian Harl <sh at tokkee.org>
20 * Florian Forster <octo at verplant.org>
21 **/
23 /*
24 * This module allows to filter and rewrite value lists based on
25 * Perl-compatible regular expressions.
26 */
28 #include "collectd.h"
29 #include "filter_chain.h"
31 #include <sys/types.h>
32 #include <regex.h>
34 #define log_err(...) ERROR ("`regex' match: " __VA_ARGS__)
35 #define log_warn(...) WARNING ("`regex' match: " __VA_ARGS__)
37 /*
38 * private data types
39 */
41 struct mr_regex_s;
42 typedef struct mr_regex_s mr_regex_t;
43 struct mr_regex_s
44 {
45 regex_t re;
46 char *re_str;
48 mr_regex_t *next;
49 };
51 struct mr_match_s;
52 typedef struct mr_match_s mr_match_t;
53 struct mr_match_s
54 {
55 mr_regex_t *host;
56 mr_regex_t *plugin;
57 mr_regex_t *plugin_instance;
58 mr_regex_t *type;
59 mr_regex_t *type_instance;
60 };
62 /*
63 * internal helper functions
64 */
65 static void mr_free_regex (mr_regex_t *r) /* {{{ */
66 {
67 if (r == NULL)
68 return;
70 regfree (&r->re);
71 memset (&r->re, 0, sizeof (r->re));
72 free (r->re_str);
74 if (r->next != NULL)
75 mr_free_regex (r->next);
76 } /* }}} void mr_free_regex */
78 static void mr_free_match (mr_match_t *m) /* {{{ */
79 {
80 if (m == NULL)
81 return;
83 mr_free_regex (m->host);
84 mr_free_regex (m->plugin);
85 mr_free_regex (m->plugin_instance);
86 mr_free_regex (m->type);
87 mr_free_regex (m->type_instance);
89 free (m);
90 } /* }}} void mr_free_match */
92 static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
93 const char *string)
94 {
95 mr_regex_t *re;
97 if (re_head == NULL)
98 return (FC_MATCH_MATCHES);
100 for (re = re_head; re != NULL; re = re->next)
101 {
102 int status;
104 status = regexec (&re->re, string,
105 /* nmatch = */ 0, /* pmatch = */ NULL,
106 /* eflags = */ 0);
107 if (status == 0)
108 {
109 DEBUG ("regex match: Regular expression `%s' matches `%s'.",
110 re->re_str, string);
111 return (FC_MATCH_MATCHES);
112 }
113 else
114 {
115 DEBUG ("regex match: Regular expression `%s' does not match `%s'.",
116 re->re_str, string);
117 }
119 }
121 return (FC_MATCH_NO_MATCH);
122 } /* }}} int mr_match_regexen */
124 static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
125 oconfig_item_t *ci)
126 {
127 mr_regex_t *re;
128 int status;
130 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
131 {
132 log_warn ("`%s' needs exactly one string argument.", ci->key);
133 return (-1);
134 }
136 re = (mr_regex_t *) malloc (sizeof (*re));
137 if (re == NULL)
138 {
139 log_err ("mr_config_add_regex: malloc failed.");
140 return (-1);
141 }
142 memset (re, 0, sizeof (*re));
143 re->next = NULL;
145 re->re_str = strdup (ci->values[0].value.string);
146 if (re->re_str == NULL)
147 {
148 free (re);
149 log_err ("mr_config_add_regex: strdup failed.");
150 return (-1);
151 }
153 status = regcomp (&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
154 if (status != 0)
155 {
156 char errmsg[1024];
157 regerror (status, &re->re, errmsg, sizeof (errmsg));
158 errmsg[sizeof (errmsg) - 1] = 0;
159 log_err ("Compiling regex `%s' for `%s' failed: %s.",
160 re->re_str, ci->key, errmsg);
161 free (re->re_str);
162 free (re);
163 return (-1);
164 }
166 if (*re_head == NULL)
167 {
168 *re_head = re;
169 }
170 else
171 {
172 mr_regex_t *ptr;
174 ptr = *re_head;
175 while (ptr->next != NULL)
176 ptr = ptr->next;
178 ptr->next = re;
179 }
181 return (0);
182 } /* }}} int mr_config_add_regex */
184 static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
185 {
186 mr_match_t *m;
187 int status;
188 int i;
190 m = (mr_match_t *) malloc (sizeof (*m));
191 if (m == NULL)
192 {
193 log_err ("mr_create: malloc failed.");
194 return (-ENOMEM);
195 }
196 memset (m, 0, sizeof (*m));
198 status = 0;
199 for (i = 0; i < ci->children_num; i++)
200 {
201 oconfig_item_t *child = ci->children + i;
203 if ((strcasecmp ("Host", child->key) == 0)
204 || (strcasecmp ("Hostname", child->key) == 0))
205 status = mr_config_add_regex (&m->host, child);
206 else if (strcasecmp ("Plugin", child->key) == 0)
207 status = mr_config_add_regex (&m->plugin, child);
208 else if (strcasecmp ("PluginInstance", child->key) == 0)
209 status = mr_config_add_regex (&m->plugin_instance, child);
210 else if (strcasecmp ("Type", child->key) == 0)
211 status = mr_config_add_regex (&m->type, child);
212 else if (strcasecmp ("TypeInstance", child->key) == 0)
213 status = mr_config_add_regex (&m->type_instance, child);
214 else
215 {
216 log_err ("The `%s' configuration option is not understood and "
217 "will be ignored.", child->key);
218 status = 0;
219 }
221 if (status != 0)
222 break;
223 }
225 /* Additional sanity-checking */
226 while (status == 0)
227 {
228 if ((m->host == NULL)
229 && (m->plugin == NULL)
230 && (m->plugin_instance == NULL)
231 && (m->type == NULL)
232 && (m->type_instance == NULL))
233 {
234 log_err ("No (valid) regular expressions have been configured. "
235 "This match will be ignored.");
236 status = -1;
237 }
239 break;
240 }
242 if (status != 0)
243 {
244 mr_free_match (m);
245 return (status);
246 }
248 *user_data = m;
249 return (0);
250 } /* }}} int mr_create */
252 static int mr_destroy (void **user_data) /* {{{ */
253 {
254 if ((user_data != NULL) && (*user_data != NULL))
255 mr_free_match (*user_data);
256 return (0);
257 } /* }}} int mr_destroy */
259 static int mr_match (const data_set_t *ds, const value_list_t *vl, /* {{{ */
260 notification_meta_t **meta, void **user_data)
261 {
262 mr_match_t *m;
264 if ((user_data == NULL) || (*user_data == NULL))
265 return (-1);
267 m = *user_data;
269 if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH)
270 return (FC_MATCH_NO_MATCH);
271 if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
272 return (FC_MATCH_NO_MATCH);
273 if (mr_match_regexen (m->plugin_instance,
274 vl->plugin_instance) == FC_MATCH_NO_MATCH)
275 return (FC_MATCH_NO_MATCH);
276 if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
277 return (FC_MATCH_NO_MATCH);
278 if (mr_match_regexen (m->type_instance,
279 vl->type_instance) == FC_MATCH_NO_MATCH)
280 return (FC_MATCH_NO_MATCH);
282 return (FC_MATCH_MATCHES);
283 } /* }}} int mr_match */
285 void module_register (void)
286 {
287 match_proc_t mproc;
289 memset (&mproc, 0, sizeof (mproc));
290 mproc.create = mr_create;
291 mproc.destroy = mr_destroy;
292 mproc.match = mr_match;
293 fc_register_match ("regex", mproc);
294 } /* module_register */
296 /* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */