Merge branch 'collectd-5.5' into collectd-5.6
[collectd.git] / src / match_regex.c
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  **/
28
29 /*
30  * This module allows to filter and rewrite value lists based on
31  * Perl-compatible regular expressions.
32  */
33
34 #include "collectd.h"
35
36 #include "filter_chain.h"
37
38 #include <sys/types.h>
39 #include <regex.h>
40
41 #define log_err(...) ERROR ("`regex' match: " __VA_ARGS__)
42 #define log_warn(...) WARNING ("`regex' match: " __VA_ARGS__)
43
44 /*
45  * private data types
46  */
47
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;
54
55         mr_regex_t *next;
56 };
57
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 };
69
70 /*
71  * internal helper functions
72  */
73 static void mr_free_regex (mr_regex_t *r) /* {{{ */
74 {
75         if (r == NULL)
76                 return;
77
78         regfree (&r->re);
79         memset (&r->re, 0, sizeof (r->re));
80         free (r->re_str);
81
82         if (r->next != NULL)
83                 mr_free_regex (r->next);
84 } /* }}} void mr_free_regex */
85
86 static void mr_free_match (mr_match_t *m) /* {{{ */
87 {
88         if (m == NULL)
89                 return;
90
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);
96
97         free (m);
98 } /* }}} void mr_free_match */
99
100 static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
101                 const char *string)
102 {
103         if (re_head == NULL)
104                 return (FC_MATCH_MATCHES);
105
106         for (mr_regex_t *re = re_head; re != NULL; re = re->next)
107         {
108                 int status;
109
110                 status = regexec (&re->re, string,
111                                 /* nmatch = */ 0, /* pmatch = */ NULL,
112                                 /* eflags = */ 0);
113                 if (status == 0)
114                 {
115                         DEBUG ("regex match: Regular expression `%s' matches `%s'.",
116                                         re->re_str, string);
117                 }
118                 else
119                 {
120                         DEBUG ("regex match: Regular expression `%s' does not match `%s'.",
121                                         re->re_str, string);
122                         return (FC_MATCH_NO_MATCH);
123                 }
124
125         }
126
127         return (FC_MATCH_MATCHES);
128 } /* }}} int mr_match_regexen */
129
130 static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
131                 oconfig_item_t *ci)
132 {
133         mr_regex_t *re;
134         int status;
135
136         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
137         {
138                 log_warn ("`%s' needs exactly one string argument.", ci->key);
139                 return (-1);
140         }
141
142         re = calloc (1, sizeof (*re));
143         if (re == NULL)
144         {
145                 log_err ("mr_config_add_regex: calloc failed.");
146                 return (-1);
147         }
148         re->next = NULL;
149
150         re->re_str = strdup (ci->values[0].value.string);
151         if (re->re_str == NULL)
152         {
153                 free (re);
154                 log_err ("mr_config_add_regex: strdup failed.");
155                 return (-1);
156         }
157
158         status = regcomp (&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
159         if (status != 0)
160         {
161                 char errmsg[1024];
162                 regerror (status, &re->re, errmsg, sizeof (errmsg));
163                 errmsg[sizeof (errmsg) - 1] = 0;
164                 log_err ("Compiling regex `%s' for `%s' failed: %s.",
165                                 re->re_str, ci->key, errmsg);
166                 free (re->re_str);
167                 free (re);
168                 return (-1);
169         }
170
171         if (*re_head == NULL)
172         {
173                 *re_head = re;
174         }
175         else
176         {
177                 mr_regex_t *ptr;
178
179                 ptr = *re_head;
180                 while (ptr->next != NULL)
181                         ptr = ptr->next;
182
183                 ptr->next = re;
184         }
185
186         return (0);
187 } /* }}} int mr_config_add_regex */
188
189 static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
190 {
191         mr_match_t *m;
192         int status;
193
194         m = calloc (1, sizeof (*m));
195         if (m == NULL)
196         {
197                 log_err ("mr_create: calloc failed.");
198                 return (-ENOMEM);
199         }
200
201         m->invert = 0;
202
203         status = 0;
204         for (int i = 0; i < ci->children_num; i++)
205         {
206                 oconfig_item_t *child = ci->children + i;
207
208                 if ((strcasecmp ("Host", child->key) == 0)
209                                 || (strcasecmp ("Hostname", child->key) == 0))
210                         status = mr_config_add_regex (&m->host, child);
211                 else if (strcasecmp ("Plugin", child->key) == 0)
212                         status = mr_config_add_regex (&m->plugin, child);
213                 else if (strcasecmp ("PluginInstance", child->key) == 0)
214                         status = mr_config_add_regex (&m->plugin_instance, child);
215                 else if (strcasecmp ("Type", child->key) == 0)
216                         status = mr_config_add_regex (&m->type, child);
217                 else if (strcasecmp ("TypeInstance", child->key) == 0)
218                         status = mr_config_add_regex (&m->type_instance, child);
219                 else if (strcasecmp ("Invert", child->key) == 0)
220                         status = cf_util_get_boolean(child, &m->invert);
221                 else
222                 {
223                         log_err ("The `%s' configuration option is not understood and "
224                                         "will be ignored.", child->key);
225                         status = 0;
226                 }
227
228                 if (status != 0)
229                         break;
230         }
231
232         /* Additional sanity-checking */
233         while (status == 0)
234         {
235                 if ((m->host == NULL)
236                                 && (m->plugin == NULL)
237                                 && (m->plugin_instance == NULL)
238                                 && (m->type == NULL)
239                                 && (m->type_instance == NULL))
240                 {
241                         log_err ("No (valid) regular expressions have been configured. "
242                                         "This match will be ignored.");
243                         status = -1;
244                 }
245
246                 break;
247         }
248
249         if (status != 0)
250         {
251                 mr_free_match (m);
252                 return (status);
253         }
254
255         *user_data = m;
256         return (0);
257 } /* }}} int mr_create */
258
259 static int mr_destroy (void **user_data) /* {{{ */
260 {
261         if ((user_data != NULL) && (*user_data != NULL))
262                 mr_free_match (*user_data);
263         return (0);
264 } /* }}} int mr_destroy */
265
266 static int mr_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
267                 const value_list_t *vl,
268                 notification_meta_t __attribute__((unused)) **meta,
269                 void **user_data)
270 {
271         mr_match_t *m;
272         int match_value = FC_MATCH_MATCHES;
273         int nomatch_value = FC_MATCH_NO_MATCH;
274
275         if ((user_data == NULL) || (*user_data == NULL))
276                 return (-1);
277
278         m = *user_data;
279
280         if (m->invert)
281         {
282                 match_value = FC_MATCH_NO_MATCH;
283                 nomatch_value = FC_MATCH_MATCHES;
284         }
285
286         if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH)
287                 return (nomatch_value);
288         if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
289                 return (nomatch_value);
290         if (mr_match_regexen (m->plugin_instance,
291                                 vl->plugin_instance) == FC_MATCH_NO_MATCH)
292                 return (nomatch_value);
293         if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
294                 return (nomatch_value);
295         if (mr_match_regexen (m->type_instance,
296                                 vl->type_instance) == FC_MATCH_NO_MATCH)
297                 return (nomatch_value);
298
299         return (match_value);
300 } /* }}} int mr_match */
301
302 void module_register (void)
303 {
304         match_proc_t mproc = { 0 };
305
306         mproc.create  = mr_create;
307         mproc.destroy = mr_destroy;
308         mproc.match   = mr_match;
309         fc_register_match ("regex", mproc);
310 } /* module_register */
311
312 /* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */
313