src/filter_chain.c, src/match_regex.c: Add some debugging messages.
[collectd.git] / src / match_regex.c
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  **/
22
23 /*
24  * This module allows to filter and rewrite value lists based on
25  * Perl-compatible regular expressions.
26  */
27
28 #include "collectd.h"
29 #include "filter_chain.h"
30
31 #include <sys/types.h>
32 #include <regex.h>
33
34 #define log_err(...) ERROR ("`regex' match: " __VA_ARGS__)
35 #define log_warn(...) WARNING ("`regex' match: " __VA_ARGS__)
36
37 /*
38  * private data types
39  */
40
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;
47
48         mr_regex_t *next;
49 };
50
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 };
61
62 /*
63  * internal helper functions
64  */
65 static void mr_free_regex (mr_regex_t *r) /* {{{ */
66 {
67         if (r == NULL)
68                 return;
69
70         regfree (&r->re);
71         memset (&r->re, 0, sizeof (r->re));
72         free (r->re_str);
73
74         if (r->next != NULL)
75                 mr_free_regex (r->next);
76 } /* }}} void mr_free_regex */
77
78 static void mr_free_match (mr_match_t *m) /* {{{ */
79 {
80         if (m == NULL)
81                 return;
82
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);
88
89         free (m);
90 } /* }}} void mr_free_match */
91
92 static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
93                 const char *string)
94 {
95         mr_regex_t *re;
96
97         if (re_head == NULL)
98                 return (FC_MATCH_MATCHES);
99
100         for (re = re_head; re != NULL; re = re->next)
101         {
102                 int status;
103
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                 }
118
119         }
120
121         return (FC_MATCH_NO_MATCH);
122 } /* }}} int mr_match_regexen */
123
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;
129
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         }
135
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;
144
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         }
152
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         }
165
166         if (*re_head == NULL)
167         {
168                 *re_head = re;
169         }
170         else
171         {
172                 mr_regex_t *ptr;
173
174                 ptr = *re_head;
175                 while (ptr->next != NULL)
176                         ptr = ptr->next;
177
178                 ptr->next = re;
179         }
180
181         return (0);
182 } /* }}} int mr_config_add_regex */
183
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;
189
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));
197
198         status = 0;
199         for (i = 0; i < ci->children_num; i++)
200         {
201                 oconfig_item_t *child = ci->children + i;
202
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                 }
220
221                 if (status != 0)
222                         break;
223         }
224
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                 }
238
239                 break;
240         }
241
242         if (status != 0)
243         {
244                 mr_free_match (m);
245                 return (status);
246         }
247
248         *user_data = m;
249         return (0);
250 } /* }}} int mr_create */
251
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 */
258
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;
263
264         if ((user_data == NULL) || (*user_data == NULL))
265                 return (-1);
266
267         m = *user_data;
268
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);
281
282         return (FC_MATCH_MATCHES);
283 } /* }}} int mr_match */
284
285 void module_register (void)
286 {
287         match_proc_t mproc;
288
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 */
295
296 /* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */
297