Merge pull request #1611 from stefanth/Collection3/graph-rendering-speedup
[collectd.git] / src / daemon / utils_match.c
1 /**
2  * collectd - src/utils_match.c
3  * Copyright (C) 2008-2014  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 = malloc (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 = calloc (1, sizeof (*obj));
242   if (obj == NULL)
243     return (NULL);
244
245   status = regcomp (&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
246   if (status != 0)
247   {
248     ERROR ("Compiling the regular expression \"%s\" failed.", regex);
249     sfree (obj);
250     return (NULL);
251   }
252
253   if (excluderegex && strcmp(excluderegex, "") != 0) {
254     status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED);
255     if (status != 0)
256     {
257         ERROR ("Compiling the excluding regular expression \"%s\" failed.",
258                excluderegex);
259         sfree (obj);
260         return (NULL);
261     }
262     obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
263   }
264
265   obj->callback = callback;
266   obj->user_data = user_data;
267
268   return (obj);
269 } /* cu_match_t *match_create_callback */
270
271 cu_match_t *match_create_simple (const char *regex,
272                                  const char *excluderegex, int match_ds_type)
273 {
274   cu_match_value_t *user_data;
275   cu_match_t *obj;
276
277   user_data = calloc (1, sizeof (*user_data));
278   if (user_data == NULL)
279     return (NULL);
280   user_data->ds_type = match_ds_type;
281
282   obj = match_create_callback (regex, excluderegex,
283                                default_callback, user_data);
284   if (obj == NULL)
285   {
286     sfree (user_data);
287     return (NULL);
288   }
289
290   obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
291
292   return (obj);
293 } /* cu_match_t *match_create_simple */
294
295 void match_value_reset (cu_match_value_t *mv)
296 {
297   if (mv == NULL)
298     return;
299
300   if (mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
301   {
302     mv->value.gauge = NAN;
303     mv->values_num = 0;
304   }
305 } /* }}} void match_value_reset */
306
307 void match_destroy (cu_match_t *obj)
308 {
309   if (obj == NULL)
310     return;
311
312   if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
313   {
314     sfree (obj->user_data);
315   }
316
317   sfree (obj);
318 } /* void match_destroy */
319
320 int match_apply (cu_match_t *obj, const char *str)
321 {
322   int status;
323   regmatch_t re_match[32];
324   char *matches[32];
325   size_t matches_num;
326   size_t i;
327
328   if ((obj == NULL) || (str == NULL))
329     return (-1);
330
331   if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
332     status = regexec (&obj->excluderegex, str,
333                       STATIC_ARRAY_SIZE (re_match), re_match,
334                       /* eflags = */ 0);
335     /* Regex did match, so exclude this line */
336     if (status == 0) {
337       DEBUG("ExludeRegex matched, don't count that line\n");
338       return (0);
339     }
340   }
341
342   status = regexec (&obj->regex, str,
343       STATIC_ARRAY_SIZE (re_match), re_match,
344       /* eflags = */ 0);
345
346   /* Regex did not match */
347   if (status != 0)
348     return (0);
349
350   memset (matches, '\0', sizeof (matches));
351   for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
352   {
353     if ((re_match[matches_num].rm_so < 0)
354         || (re_match[matches_num].rm_eo < 0))
355       break;
356
357     matches[matches_num] = match_substr (str,
358         re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
359     if (matches[matches_num] == NULL)
360     {
361       status = -1;
362       break;
363     }
364   }
365
366   if (status != 0)
367   {
368     ERROR ("utils_match: match_apply: match_substr failed.");
369   }
370   else
371   {
372     status = obj->callback (str, matches, matches_num, obj->user_data);
373     if (status != 0)
374     {
375       ERROR ("utils_match: match_apply: callback failed.");
376     }
377   }
378
379   for (i = 0; i < matches_num; i++)
380   {
381     sfree (matches[i]);
382   }
383
384   return (status);
385 } /* int match_apply */
386
387 void *match_get_user_data (cu_match_t *obj)
388 {
389   if (obj == NULL)
390     return (NULL);
391   return (obj->user_data);
392 } /* void *match_get_user_data */
393
394 /* vim: set sw=2 sts=2 ts=8 : */