fix dstype gauge values and expands the number of possible subtypes
[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 (data->ds_type & UTILS_MATCH_CF_GAUGE_INC)
84     {
85       data->value.gauge = isnan (data->value.gauge) ? 1 : data->value.gauge + 1;
86       data->values_num++;
87       return(0);
88     }
89
90     if (matches_num < 2)
91       return (-1);
92
93     value = (gauge_t) strtod (matches[1], &endptr);
94     if (matches[1] == endptr)
95       return (-1);
96
97     if ((data->values_num == 0)
98         || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST))
99     {
100       data->value.gauge = value;
101     }
102     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE)
103     {
104       double f = ((double) data->values_num)
105         / ((double) (data->values_num + 1));
106       data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
107     }
108     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN)
109     {
110       if (data->value.gauge > value)
111         data->value.gauge = value;
112     }
113     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX)
114     {
115       if (data->value.gauge < value)
116         data->value.gauge = value;
117     }
118     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD)
119     {
120       data->value.gauge += value;
121     }
122     else
123     {
124       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
125       return (-1);
126     }
127
128     data->values_num++;
129   }
130   else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER)
131   {
132     counter_t value;
133     char *endptr = NULL;
134
135     if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
136     {
137       data->value.counter++;
138       data->values_num++;
139       return (0);
140     }
141
142     if (matches_num < 2)
143       return (-1);
144
145     value = (counter_t) strtoull (matches[1], &endptr, 0);
146     if (matches[1] == endptr)
147       return (-1);
148
149     if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
150       data->value.counter = value;
151     else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
152       data->value.counter += value;
153     else
154     {
155       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
156       return (-1);
157     }
158
159     data->values_num++;
160   }
161   else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE)
162   {
163     derive_t value;
164     char *endptr = NULL;
165
166     if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
167     {
168       data->value.counter++;
169       data->values_num++;
170       return (0);
171     }
172
173     if (matches_num < 2)
174       return (-1);
175
176     value = (derive_t) strtoll (matches[1], &endptr, 0);
177     if (matches[1] == endptr)
178       return (-1);
179
180     if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
181       data->value.derive = value;
182     else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
183       data->value.derive += value;
184     else
185     {
186       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
187       return (-1);
188     }
189
190     data->values_num++;
191   }
192   else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE)
193   {
194     absolute_t value;
195     char *endptr = NULL;
196
197     if (matches_num < 2)
198       return (-1);
199
200     value = (absolute_t) strtoull (matches[1], &endptr, 0);
201     if (matches[1] == endptr)
202       return (-1);
203
204     if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
205       data->value.absolute = value;
206     else
207     {
208       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
209       return (-1);
210     }
211
212     data->values_num++;
213   }
214   else
215   {
216     ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
217     return (-1);
218   }
219
220   return (0);
221 } /* int default_callback */
222
223 /*
224  * Public functions
225  */
226 cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
227                 int (*callback) (const char *str,
228                   char * const *matches, size_t matches_num, void *user_data),
229                 void *user_data)
230 {
231   cu_match_t *obj;
232   int status;
233
234   DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
235          regex, excluderegex);
236
237   obj = (cu_match_t *) malloc (sizeof (cu_match_t));
238   if (obj == NULL)
239     return (NULL);
240   memset (obj, '\0', sizeof (cu_match_t));
241
242   status = regcomp (&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
243   if (status != 0)
244   {
245     ERROR ("Compiling the regular expression \"%s\" failed.", regex);
246     sfree (obj);
247     return (NULL);
248   }
249
250   if (excluderegex && strcmp(excluderegex, "") != 0) {
251     status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED);
252     if (status != 0)
253     {
254         ERROR ("Compiling the excluding regular expression \"%s\" failed.",
255                excluderegex);
256         sfree (obj);
257         return (NULL);
258     }
259     obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
260   }
261
262   obj->callback = callback;
263   obj->user_data = user_data;
264
265   return (obj);
266 } /* cu_match_t *match_create_callback */
267
268 cu_match_t *match_create_simple (const char *regex,
269                                  const char *excluderegex, int match_ds_type)
270 {
271   cu_match_value_t *user_data;
272   cu_match_t *obj;
273
274   user_data = (cu_match_value_t *) malloc (sizeof (cu_match_value_t));
275   if (user_data == NULL)
276     return (NULL);
277   memset (user_data, '\0', sizeof (cu_match_value_t));
278   user_data->ds_type = match_ds_type;
279
280   obj = match_create_callback (regex, excluderegex,
281                                default_callback, user_data);
282   if (obj == NULL)
283   {
284     sfree (user_data);
285     return (NULL);
286   }
287
288   obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
289
290   return (obj);
291 } /* cu_match_t *match_create_simple */
292
293 void match_destroy (cu_match_t *obj)
294 {
295   if (obj == NULL)
296     return;
297
298   if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
299   {
300     sfree (obj->user_data);
301   }
302
303   sfree (obj);
304 } /* void match_destroy */
305
306 int match_apply (cu_match_t *obj, const char *str)
307 {
308   int status;
309   regmatch_t re_match[32];
310   char *matches[32];
311   size_t matches_num;
312   size_t i;
313
314   if ((obj == NULL) || (str == NULL))
315     return (-1);
316
317   if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
318     status = regexec (&obj->excluderegex, str,
319                       STATIC_ARRAY_SIZE (re_match), re_match,
320                       /* eflags = */ 0);
321     /* Regex did match, so exclude this line */
322     if (status == 0) {
323       DEBUG("ExludeRegex matched, don't count that line\n");
324       return (0);
325     }
326   }
327
328   status = regexec (&obj->regex, str,
329       STATIC_ARRAY_SIZE (re_match), re_match,
330       /* eflags = */ 0);
331
332   /* Regex did not match */
333   if (status != 0)
334     return (0);
335
336   memset (matches, '\0', sizeof (matches));
337   for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
338   {
339     if ((re_match[matches_num].rm_so < 0)
340         || (re_match[matches_num].rm_eo < 0))
341       break;
342
343     matches[matches_num] = match_substr (str,
344         re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
345     if (matches[matches_num] == NULL)
346     {
347       status = -1;
348       break;
349     }
350   }
351
352   if (status != 0)
353   {
354     ERROR ("utils_match: match_apply: match_substr failed.");
355   }
356   else
357   {
358     status = obj->callback (str, matches, matches_num, obj->user_data);
359     if (status != 0)
360     {
361       ERROR ("utils_match: match_apply: callback failed.");
362     }
363   }
364
365   for (i = 0; i < matches_num; i++)
366   {
367     sfree (matches[i]);
368   }
369
370   return (status);
371 } /* int match_apply */
372
373 void *match_get_user_data (cu_match_t *obj)
374 {
375   if (obj == NULL)
376     return (NULL);
377   return (obj->user_data);
378 } /* void *match_get_user_data */
379
380 /* vim: set sw=2 sts=2 ts=8 : */