1 /**
2 * collectd - src/utils_match.c
3 * Copyright (C) 2008-2014 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 **/
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
27 #include "utils_match.h"
29 #include <regex.h>
31 #define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
32 #define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
34 struct cu_match_s
35 {
36 regex_t regex;
37 regex_t excluderegex;
38 int flags;
40 int (*callback) (const char *str, char * const *matches, size_t matches_num,
41 void *user_data);
42 void *user_data;
43 };
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;
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 }
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 }
69 sstrncpy (ret, str + begin, ret_len + 1);
70 return (ret);
71 } /* char *match_substr */
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;
78 if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
79 {
80 gauge_t value;
81 char *endptr = NULL;
83 if (matches_num < 2)
84 return (-1);
86 value = (gauge_t) strtod (matches[1], &endptr);
87 if (matches[1] == endptr)
88 return (-1);
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 }
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;
124 if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
125 {
126 data->value.counter++;
127 data->values_num++;
128 return (0);
129 }
131 if (matches_num < 2)
132 return (-1);
134 value = (counter_t) strtoull (matches[1], &endptr, 0);
135 if (matches[1] == endptr)
136 return (-1);
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 }
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;
155 if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
156 {
157 data->value.counter++;
158 data->values_num++;
159 return (0);
160 }
162 if (matches_num < 2)
163 return (-1);
165 value = (derive_t) strtoll (matches[1], &endptr, 0);
166 if (matches[1] == endptr)
167 return (-1);
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 }
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;
186 if (matches_num < 2)
187 return (-1);
189 value = (absolute_t) strtoull (matches[1], &endptr, 0);
190 if (matches[1] == endptr)
191 return (-1);
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 }
201 data->values_num++;
202 }
203 else
204 {
205 ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
206 return (-1);
207 }
209 return (0);
210 } /* int default_callback */
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;
223 DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
224 regex, excluderegex);
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));
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 }
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 }
251 obj->callback = callback;
252 obj->user_data = user_data;
254 return (obj);
255 } /* cu_match_t *match_create_callback */
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;
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;
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 }
277 obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
279 return (obj);
280 } /* cu_match_t *match_create_simple */
282 void match_value_reset (cu_match_value_t *mv)
283 {
284 if (mv == NULL)
285 return;
287 if (mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
288 {
289 mv->value.gauge = NAN;
290 mv->values_num = 0;
291 }
292 } /* }}} void match_value_reset */
294 void match_destroy (cu_match_t *obj)
295 {
296 if (obj == NULL)
297 return;
299 if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
300 {
301 sfree (obj->user_data);
302 }
304 sfree (obj);
305 } /* void match_destroy */
307 int match_apply (cu_match_t *obj, const char *str)
308 {
309 int status;
310 regmatch_t re_match[32];
311 char *matches[32];
312 size_t matches_num;
313 size_t i;
315 if ((obj == NULL) || (str == NULL))
316 return (-1);
318 if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
319 status = regexec (&obj->excluderegex, str,
320 STATIC_ARRAY_SIZE (re_match), re_match,
321 /* eflags = */ 0);
322 /* Regex did match, so exclude this line */
323 if (status == 0) {
324 DEBUG("ExludeRegex matched, don't count that line\n");
325 return (0);
326 }
327 }
329 status = regexec (&obj->regex, str,
330 STATIC_ARRAY_SIZE (re_match), re_match,
331 /* eflags = */ 0);
333 /* Regex did not match */
334 if (status != 0)
335 return (0);
337 memset (matches, '\0', sizeof (matches));
338 for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
339 {
340 if ((re_match[matches_num].rm_so < 0)
341 || (re_match[matches_num].rm_eo < 0))
342 break;
344 matches[matches_num] = match_substr (str,
345 re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
346 if (matches[matches_num] == NULL)
347 {
348 status = -1;
349 break;
350 }
351 }
353 if (status != 0)
354 {
355 ERROR ("utils_match: match_apply: match_substr failed.");
356 }
357 else
358 {
359 status = obj->callback (str, matches, matches_num, obj->user_data);
360 if (status != 0)
361 {
362 ERROR ("utils_match: match_apply: callback failed.");
363 }
364 }
366 for (i = 0; i < matches_num; i++)
367 {
368 sfree (matches[i]);
369 }
371 return (status);
372 } /* int match_apply */
374 void *match_get_user_data (cu_match_t *obj)
375 {
376 if (obj == NULL)
377 return (NULL);
378 return (obj->user_data);
379 } /* void *match_get_user_data */
381 /* vim: set sw=2 sts=2 ts=8 : */