Merge pull request #681 from pyr/feature/use-collectd-hostname
[collectd.git] / src / utils_match.c
1 /**
2  * collectd - src/utils_match.c
3  * Copyright (C) 2008  Florian octo Forster
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; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26
27 #include "utils_match.h"
28
29 #include <regex.h>
30
31 #define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
32 #define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
33
34 struct cu_match_s
35 {
36   regex_t regex;
37   regex_t excluderegex;
38   int flags;
39
40   int (*callback) (const char *str, char * const *matches, size_t matches_num,
41       void *user_data);
42   void *user_data;
43 };
44
45 /*
46  * Private functions
47  */
48 static char *match_substr (const char *str, int begin, int end)
49 {
50   char *ret;
51   size_t ret_len;
52
53   if ((begin < 0) || (end < 0) || (begin >= end))
54     return (NULL);
55   if ((size_t) end > (strlen (str) + 1))
56   {
57     ERROR ("utils_match: match_substr: `end' points after end of string.");
58     return (NULL);
59   }
60
61   ret_len = end - begin;
62   ret = (char *) malloc (sizeof (char) * (ret_len + 1));
63   if (ret == NULL)
64   {
65     ERROR ("utils_match: match_substr: malloc failed.");
66     return (NULL);
67   }
68
69   sstrncpy (ret, str + begin, ret_len + 1);
70   return (ret);
71 } /* char *match_substr */
72
73 static int default_callback (const char __attribute__((unused)) *str,
74     char * const *matches, size_t matches_num, void *user_data)
75 {
76   cu_match_value_t *data = (cu_match_value_t *) user_data;
77
78   if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
79   {
80     gauge_t value;
81     char *endptr = NULL;
82
83     if (matches_num < 2)
84       return (-1);
85
86     value = (gauge_t) strtod (matches[1], &endptr);
87     if (matches[1] == endptr)
88       return (-1);
89
90     if ((data->values_num == 0)
91         || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST))
92     {
93       data->value.gauge = value;
94     }
95     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE)
96     {
97       double f = ((double) data->values_num)
98         / ((double) (data->values_num + 1));
99       data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
100     }
101     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN)
102     {
103       if (data->value.gauge > value)
104         data->value.gauge = value;
105     }
106     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX)
107     {
108       if (data->value.gauge < value)
109         data->value.gauge = value;
110     }
111     else
112     {
113       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
114       return (-1);
115     }
116
117     data->values_num++;
118   }
119   else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER)
120   {
121     counter_t value;
122     char *endptr = NULL;
123
124     if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
125     {
126       data->value.counter++;
127       data->values_num++;
128       return (0);
129     }
130
131     if (matches_num < 2)
132       return (-1);
133
134     value = (counter_t) strtoull (matches[1], &endptr, 0);
135     if (matches[1] == endptr)
136       return (-1);
137
138     if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
139       data->value.counter = value;
140     else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
141       data->value.counter += value;
142     else
143     {
144       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
145       return (-1);
146     }
147
148     data->values_num++;
149   }
150   else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE)
151   {
152     derive_t value;
153     char *endptr = NULL;
154
155     if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
156     {
157       data->value.counter++;
158       data->values_num++;
159       return (0);
160     }
161
162     if (matches_num < 2)
163       return (-1);
164
165     value = (derive_t) strtoll (matches[1], &endptr, 0);
166     if (matches[1] == endptr)
167       return (-1);
168
169     if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
170       data->value.derive = value;
171     else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
172       data->value.derive += value;
173     else
174     {
175       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
176       return (-1);
177     }
178
179     data->values_num++;
180   }
181   else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE)
182   {
183     absolute_t value;
184     char *endptr = NULL;
185
186     if (matches_num < 2)
187       return (-1);
188
189     value = (absolute_t) strtoull (matches[1], &endptr, 0);
190     if (matches[1] == endptr)
191       return (-1);
192
193     if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
194       data->value.absolute = value;
195     else
196     {
197       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
198       return (-1);
199     }
200
201     data->values_num++;
202   }
203   else
204   {
205     ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
206     return (-1);
207   }
208
209   return (0);
210 } /* int default_callback */
211
212 /*
213  * Public functions
214  */
215 cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
216                 int (*callback) (const char *str,
217                   char * const *matches, size_t matches_num, void *user_data),
218                 void *user_data)
219 {
220   cu_match_t *obj;
221   int status;
222
223   DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
224          regex, excluderegex);
225
226   obj = (cu_match_t *) malloc (sizeof (cu_match_t));
227   if (obj == NULL)
228     return (NULL);
229   memset (obj, '\0', sizeof (cu_match_t));
230
231   status = regcomp (&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
232   if (status != 0)
233   {
234     ERROR ("Compiling the regular expression \"%s\" failed.", regex);
235     sfree (obj);
236     return (NULL);
237   }
238
239   if (excluderegex && strcmp(excluderegex, "") != 0) {
240     status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED);
241     if (status != 0)
242     {
243         ERROR ("Compiling the excluding regular expression \"%s\" failed.",
244                excluderegex);
245         sfree (obj);
246         return (NULL);
247     }
248     obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
249   }
250
251   obj->callback = callback;
252   obj->user_data = user_data;
253
254   return (obj);
255 } /* cu_match_t *match_create_callback */
256
257 cu_match_t *match_create_simple (const char *regex,
258                                  const char *excluderegex, int match_ds_type)
259 {
260   cu_match_value_t *user_data;
261   cu_match_t *obj;
262
263   user_data = (cu_match_value_t *) malloc (sizeof (cu_match_value_t));
264   if (user_data == NULL)
265     return (NULL);
266   memset (user_data, '\0', sizeof (cu_match_value_t));
267   user_data->ds_type = match_ds_type;
268
269   obj = match_create_callback (regex, excluderegex,
270                                default_callback, user_data);
271   if (obj == NULL)
272   {
273     sfree (user_data);
274     return (NULL);
275   }
276
277   obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
278
279   return (obj);
280 } /* cu_match_t *match_create_simple */
281
282 void match_destroy (cu_match_t *obj)
283 {
284   if (obj == NULL)
285     return;
286
287   if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
288   {
289     sfree (obj->user_data);
290   }
291
292   sfree (obj);
293 } /* void match_destroy */
294
295 int match_apply (cu_match_t *obj, const char *str)
296 {
297   int status;
298   regmatch_t re_match[32];
299   char *matches[32];
300   size_t matches_num;
301   size_t i;
302
303   if ((obj == NULL) || (str == NULL))
304     return (-1);
305
306   if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
307     status = regexec (&obj->excluderegex, str,
308                       STATIC_ARRAY_SIZE (re_match), re_match,
309                       /* eflags = */ 0);
310     /* Regex did match, so exclude this line */
311     if (status == 0) {
312       DEBUG("ExludeRegex matched, don't count that line\n");
313       return (0);
314     }
315   }
316
317   status = regexec (&obj->regex, str,
318       STATIC_ARRAY_SIZE (re_match), re_match,
319       /* eflags = */ 0);
320
321   /* Regex did not match */
322   if (status != 0)
323     return (0);
324
325   memset (matches, '\0', sizeof (matches));
326   for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
327   {
328     if ((re_match[matches_num].rm_so < 0)
329         || (re_match[matches_num].rm_eo < 0))
330       break;
331
332     matches[matches_num] = match_substr (str,
333         re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
334     if (matches[matches_num] == NULL)
335     {
336       status = -1;
337       break;
338     }
339   }
340
341   if (status != 0)
342   {
343     ERROR ("utils_match: match_apply: match_substr failed.");
344   }
345   else
346   {
347     status = obj->callback (str, matches, matches_num, obj->user_data);
348     if (status != 0)
349     {
350       ERROR ("utils_match: match_apply: callback failed.");
351     }
352   }
353
354   for (i = 0; i < matches_num; i++)
355   {
356     sfree (matches[i]);
357   }
358
359   return (status);
360 } /* int match_apply */
361
362 void *match_get_user_data (cu_match_t *obj)
363 {
364   if (obj == NULL)
365     return (NULL);
366   return (obj->user_data);
367 } /* void *match_get_user_data */
368
369 /* vim: set sw=2 sts=2 ts=8 : */