Merge branch 'collectd-4.7' into collectd-4.8
[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
33 struct cu_match_s
34 {
35   regex_t regex;
36   int flags;
37
38   int (*callback) (const char *str, char * const *matches, size_t matches_num,
39       void *user_data);
40   void *user_data;
41 };
42
43 /*
44  * Private functions
45  */
46 static char *match_substr (const char *str, int begin, int end)
47 {
48   char *ret;
49   size_t ret_len;
50
51   if ((begin < 0) || (end < 0) || (begin >= end))
52     return (NULL);
53   if ((size_t) end > (strlen (str) + 1))
54   {
55     ERROR ("utils_match: match_substr: `end' points after end of string.");
56     return (NULL);
57   }
58
59   ret_len = end - begin;
60   ret = (char *) malloc (sizeof (char) * (ret_len + 1));
61   if (ret == NULL)
62   {
63     ERROR ("utils_match: match_substr: malloc failed.");
64     return (NULL);
65   }
66
67   sstrncpy (ret, str + begin, ret_len + 1);
68   return (ret);
69 } /* char *match_substr */
70
71 static int default_callback (const char __attribute__((unused)) *str,
72     char * const *matches, size_t matches_num, void *user_data)
73 {
74   cu_match_value_t *data = (cu_match_value_t *) user_data;
75
76   if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
77   {
78     gauge_t value;
79     char *endptr = NULL;
80
81     if (matches_num < 2)
82       return (-1);
83
84     value = strtod (matches[1], &endptr);
85     if (matches[1] == endptr)
86       return (-1);
87
88     if ((data->values_num == 0)
89         || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST))
90     {
91       data->value.gauge = value;
92     }
93     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE)
94     {
95       double f = ((double) data->values_num)
96         / ((double) (data->values_num + 1));
97       data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
98     }
99     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN)
100     {
101       if (data->value.gauge > value)
102         data->value.gauge = value;
103     }
104     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX)
105     {
106       if (data->value.gauge < value)
107         data->value.gauge = value;
108     }
109     else
110     {
111       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
112       return (-1);
113     }
114
115     data->values_num++;
116   }
117   else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER)
118   {
119     counter_t value;
120     char *endptr = NULL;
121
122     if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
123     {
124       data->value.counter++;
125       data->values_num++;
126       return (0);
127     }
128
129     if (matches_num < 2)
130       return (-1);
131
132     value = strtoll (matches[1], &endptr, 0);
133     if (matches[1] == endptr)
134       return (-1);
135
136     if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
137       data->value.counter = value;
138     else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
139       data->value.counter += value;
140     else
141     {
142       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
143       return (-1);
144     }
145
146     data->values_num++;
147   }
148   else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE)
149   {
150     derive_t value;
151     char *endptr = NULL;
152
153     if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
154     {
155       data->value.counter++;
156       data->values_num++;
157       return (0);
158     }
159
160     if (matches_num < 2)
161       return (-1);
162
163     value = strtoll (matches[1], &endptr, 0);
164     if (matches[1] == endptr)
165       return (-1);
166
167     if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
168       data->value.derive = value;
169     else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
170       data->value.derive += value;
171     else
172     {
173       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
174       return (-1);
175     }
176
177     data->values_num++;
178   }
179   else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE)
180   {
181     absolute_t value;
182     char *endptr = NULL;
183
184     if (matches_num < 2)
185       return (-1);
186
187     value = strtoll (matches[1], &endptr, 0);
188     if (matches[1] == endptr)
189       return (-1);
190
191     if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
192       data->value.absolute = value;
193     else
194     {
195       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
196       return (-1);
197     }
198
199     data->values_num++;
200   }
201   else
202   {
203     ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
204     return (-1);
205   }
206
207   return (0);
208 } /* int default_callback */
209
210 /*
211  * Public functions
212  */
213 cu_match_t *match_create_callback (const char *regex,
214                 int (*callback) (const char *str,
215                   char * const *matches, size_t matches_num, void *user_data),
216                 void *user_data)
217 {
218   cu_match_t *obj;
219   int status;
220
221   DEBUG ("utils_match: match_create_callback: regex = %s", regex);
222
223   obj = (cu_match_t *) malloc (sizeof (cu_match_t));
224   if (obj == NULL)
225     return (NULL);
226   memset (obj, '\0', sizeof (cu_match_t));
227
228   status = regcomp (&obj->regex, regex, REG_EXTENDED);
229   if (status != 0)
230   {
231     ERROR ("Compiling the regular expression \"%s\" failed.", regex);
232     sfree (obj);
233     return (NULL);
234   }
235
236   obj->callback = callback;
237   obj->user_data = user_data;
238
239   return (obj);
240 } /* cu_match_t *match_create_callback */
241
242 cu_match_t *match_create_simple (const char *regex, int match_ds_type)
243 {
244   cu_match_value_t *user_data;
245   cu_match_t *obj;
246
247   user_data = (cu_match_value_t *) malloc (sizeof (cu_match_value_t));
248   if (user_data == NULL)
249     return (NULL);
250   memset (user_data, '\0', sizeof (cu_match_value_t));
251   user_data->ds_type = match_ds_type;
252
253   obj = match_create_callback (regex, default_callback, user_data);
254   if (obj == NULL)
255   {
256     sfree (user_data);
257     return (NULL);
258   }
259
260   obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
261
262   return (obj);
263 } /* cu_match_t *match_create_simple */
264
265 void match_destroy (cu_match_t *obj)
266 {
267   if (obj == NULL)
268     return;
269
270   if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
271   {
272     sfree (obj->user_data);
273   }
274
275   sfree (obj);
276 } /* void match_destroy */
277
278 int match_apply (cu_match_t *obj, const char *str)
279 {
280   int status;
281   regmatch_t re_match[32];
282   char *matches[32];
283   size_t matches_num;
284   size_t i;
285
286   if ((obj == NULL) || (str == NULL))
287     return (-1);
288
289   status = regexec (&obj->regex, str,
290       STATIC_ARRAY_SIZE (re_match), re_match,
291       /* eflags = */ 0);
292
293   /* Regex did not match */
294   if (status != 0)
295     return (0);
296
297   memset (matches, '\0', sizeof (matches));
298   for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
299   {
300     if ((re_match[matches_num].rm_so < 0)
301         || (re_match[matches_num].rm_eo < 0))
302       break;
303
304     matches[matches_num] = match_substr (str,
305         re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
306     if (matches[matches_num] == NULL)
307     {
308       status = -1;
309       break;
310     }
311   }
312
313   if (status != 0)
314   {
315     ERROR ("utils_match: match_apply: match_substr failed.");
316   }
317   else
318   {
319     status = obj->callback (str, matches, matches_num, obj->user_data);
320     if (status != 0)
321     {
322       ERROR ("utils_match: match_apply: callback failed.");
323     }
324   }
325
326   for (i = 0; i < matches_num; i++)
327   {
328     sfree (matches[i]);
329   }
330
331   return (status);
332 } /* int match_apply */
333
334 void *match_get_user_data (cu_match_t *obj)
335 {
336   if (obj == NULL)
337     return (NULL);
338   return (obj->user_data);
339 } /* void *match_get_user_data */
340
341 /* vim: set sw=2 sts=2 ts=8 : */