Merge pull request #673 from deferraz/master
[collectd.git] / src / utils_match.c
1 /**
2  * collectd - src/utils_match.c
3  * Copyright (C) 2008       Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28 #include "common.h"
29 #include "plugin.h"
30
31 #include "utils_match.h"
32
33 #include <regex.h>
34
35 #define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
36 #define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
37
38 struct cu_match_s
39 {
40   regex_t regex;
41   regex_t excluderegex;
42   int flags;
43
44   int (*callback) (const char *str, char * const *matches, size_t matches_num,
45       void *user_data);
46   void *user_data;
47 };
48
49 /*
50  * Private functions
51  */
52 static char *match_substr (const char *str, int begin, int end)
53 {
54   char *ret;
55   size_t ret_len;
56
57   if ((begin < 0) || (end < 0) || (begin >= end))
58     return (NULL);
59   if ((size_t) end > (strlen (str) + 1))
60   {
61     ERROR ("utils_match: match_substr: `end' points after end of string.");
62     return (NULL);
63   }
64
65   ret_len = end - begin;
66   ret = (char *) malloc (sizeof (char) * (ret_len + 1));
67   if (ret == NULL)
68   {
69     ERROR ("utils_match: match_substr: malloc failed.");
70     return (NULL);
71   }
72
73   sstrncpy (ret, str + begin, ret_len + 1);
74   return (ret);
75 } /* char *match_substr */
76
77 static int default_callback (const char __attribute__((unused)) *str,
78     char * const *matches, size_t matches_num, void *user_data)
79 {
80   cu_match_value_t *data = (cu_match_value_t *) user_data;
81
82   if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
83   {
84     gauge_t value;
85     char *endptr = NULL;
86
87     if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC)
88     {
89       data->value.gauge = isnan (data->value.gauge) ? 1 : data->value.gauge + 1;
90       data->values_num++;
91       return(0);
92     }
93
94     if (matches_num < 2)
95       return (-1);
96
97     value = (gauge_t) strtod (matches[1], &endptr);
98     if (matches[1] == endptr)
99       return (-1);
100
101     if ((data->values_num == 0)
102         || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST))
103     {
104       data->value.gauge = value;
105     }
106     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE)
107     {
108       double f = ((double) data->values_num)
109         / ((double) (data->values_num + 1));
110       data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
111     }
112     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN)
113     {
114       if (data->value.gauge > value)
115         data->value.gauge = value;
116     }
117     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX)
118     {
119       if (data->value.gauge < value)
120         data->value.gauge = value;
121     }
122     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD)
123     {
124       data->value.gauge += value;
125     }
126     else
127     {
128       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
129       return (-1);
130     }
131
132     data->values_num++;
133   }
134   else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER)
135   {
136     counter_t value;
137     char *endptr = NULL;
138
139     if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
140     {
141       data->value.counter++;
142       data->values_num++;
143       return (0);
144     }
145
146     if (matches_num < 2)
147       return (-1);
148
149     value = (counter_t) strtoull (matches[1], &endptr, 0);
150     if (matches[1] == endptr)
151       return (-1);
152
153     if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
154       data->value.counter = value;
155     else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
156       data->value.counter += value;
157     else
158     {
159       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
160       return (-1);
161     }
162
163     data->values_num++;
164   }
165   else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE)
166   {
167     derive_t value;
168     char *endptr = NULL;
169
170     if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
171     {
172       data->value.counter++;
173       data->values_num++;
174       return (0);
175     }
176
177     if (matches_num < 2)
178       return (-1);
179
180     value = (derive_t) strtoll (matches[1], &endptr, 0);
181     if (matches[1] == endptr)
182       return (-1);
183
184     if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
185       data->value.derive = value;
186     else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
187       data->value.derive += value;
188     else
189     {
190       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
191       return (-1);
192     }
193
194     data->values_num++;
195   }
196   else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE)
197   {
198     absolute_t value;
199     char *endptr = NULL;
200
201     if (matches_num < 2)
202       return (-1);
203
204     value = (absolute_t) strtoull (matches[1], &endptr, 0);
205     if (matches[1] == endptr)
206       return (-1);
207
208     if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
209       data->value.absolute = value;
210     else
211     {
212       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
213       return (-1);
214     }
215
216     data->values_num++;
217   }
218   else
219   {
220     ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
221     return (-1);
222   }
223
224   return (0);
225 } /* int default_callback */
226
227 /*
228  * Public functions
229  */
230 cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
231                 int (*callback) (const char *str,
232                   char * const *matches, size_t matches_num, void *user_data),
233                 void *user_data)
234 {
235   cu_match_t *obj;
236   int status;
237
238   DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
239          regex, excluderegex);
240
241   obj = (cu_match_t *) malloc (sizeof (cu_match_t));
242   if (obj == NULL)
243     return (NULL);
244   memset (obj, '\0', sizeof (cu_match_t));
245
246   status = regcomp (&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
247   if (status != 0)
248   {
249     ERROR ("Compiling the regular expression \"%s\" failed.", regex);
250     sfree (obj);
251     return (NULL);
252   }
253
254   if (excluderegex && strcmp(excluderegex, "") != 0) {
255     status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED);
256     if (status != 0)
257     {
258         ERROR ("Compiling the excluding regular expression \"%s\" failed.",
259                excluderegex);
260         sfree (obj);
261         return (NULL);
262     }
263     obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
264   }
265
266   obj->callback = callback;
267   obj->user_data = user_data;
268
269   return (obj);
270 } /* cu_match_t *match_create_callback */
271
272 cu_match_t *match_create_simple (const char *regex,
273                                  const char *excluderegex, int match_ds_type)
274 {
275   cu_match_value_t *user_data;
276   cu_match_t *obj;
277
278   user_data = (cu_match_value_t *) malloc (sizeof (cu_match_value_t));
279   if (user_data == NULL)
280     return (NULL);
281   memset (user_data, '\0', sizeof (cu_match_value_t));
282   user_data->ds_type = match_ds_type;
283
284   obj = match_create_callback (regex, excluderegex,
285                                default_callback, user_data);
286   if (obj == NULL)
287   {
288     sfree (user_data);
289     return (NULL);
290   }
291
292   obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
293
294   return (obj);
295 } /* cu_match_t *match_create_simple */
296
297 void match_destroy (cu_match_t *obj)
298 {
299   if (obj == NULL)
300     return;
301
302   if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
303   {
304     sfree (obj->user_data);
305   }
306
307   sfree (obj);
308 } /* void match_destroy */
309
310 int match_apply (cu_match_t *obj, const char *str)
311 {
312   int status;
313   regmatch_t re_match[32];
314   char *matches[32];
315   size_t matches_num;
316   size_t i;
317
318   if ((obj == NULL) || (str == NULL))
319     return (-1);
320
321   if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
322     status = regexec (&obj->excluderegex, str,
323                       STATIC_ARRAY_SIZE (re_match), re_match,
324                       /* eflags = */ 0);
325     /* Regex did match, so exclude this line */
326     if (status == 0) {
327       DEBUG("ExludeRegex matched, don't count that line\n");
328       return (0);
329     }
330   }
331
332   status = regexec (&obj->regex, str,
333       STATIC_ARRAY_SIZE (re_match), re_match,
334       /* eflags = */ 0);
335
336   /* Regex did not match */
337   if (status != 0)
338     return (0);
339
340   memset (matches, '\0', sizeof (matches));
341   for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
342   {
343     if ((re_match[matches_num].rm_so < 0)
344         || (re_match[matches_num].rm_eo < 0))
345       break;
346
347     matches[matches_num] = match_substr (str,
348         re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
349     if (matches[matches_num] == NULL)
350     {
351       status = -1;
352       break;
353     }
354   }
355
356   if (status != 0)
357   {
358     ERROR ("utils_match: match_apply: match_substr failed.");
359   }
360   else
361   {
362     status = obj->callback (str, matches, matches_num, obj->user_data);
363     if (status != 0)
364     {
365       ERROR ("utils_match: match_apply: callback failed.");
366     }
367   }
368
369   for (i = 0; i < matches_num; i++)
370   {
371     sfree (matches[i]);
372   }
373
374   return (status);
375 } /* int match_apply */
376
377 void *match_get_user_data (cu_match_t *obj)
378 {
379   if (obj == NULL)
380     return (NULL);
381   return (obj->user_data);
382 } /* void *match_get_user_data */
383
384 /* vim: set sw=2 sts=2 ts=8 : */