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 **/
27 #include "collectd.h"
29 #include "common.h"
30 #include "plugin.h"
32 #include "utils_match.h"
34 #include <regex.h>
36 #define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
37 #define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
39 struct cu_match_s
40 {
41 regex_t regex;
42 regex_t excluderegex;
43 int flags;
45 int (*callback) (const char *str, char * const *matches, size_t matches_num,
46 void *user_data);
47 void *user_data;
48 };
50 /*
51 * Private functions
52 */
53 static char *match_substr (const char *str, int begin, int end)
54 {
55 char *ret;
56 size_t ret_len;
58 if ((begin < 0) || (end < 0) || (begin >= end))
59 return (NULL);
60 if ((size_t) end > (strlen (str) + 1))
61 {
62 ERROR ("utils_match: match_substr: `end' points after end of string.");
63 return (NULL);
64 }
66 ret_len = end - begin;
67 ret = malloc (ret_len + 1);
68 if (ret == NULL)
69 {
70 ERROR ("utils_match: match_substr: malloc failed.");
71 return (NULL);
72 }
74 sstrncpy (ret, str + begin, ret_len + 1);
75 return (ret);
76 } /* char *match_substr */
78 static int default_callback (const char __attribute__((unused)) *str,
79 char * const *matches, size_t matches_num, void *user_data)
80 {
81 cu_match_value_t *data = (cu_match_value_t *) user_data;
83 if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
84 {
85 gauge_t value;
86 char *endptr = NULL;
88 if (data->ds_type & UTILS_MATCH_CF_GAUGE_INC)
89 {
90 data->value.gauge = isnan (data->value.gauge) ? 1 : data->value.gauge + 1;
91 data->values_num++;
92 return(0);
93 }
95 if (matches_num < 2)
96 return (-1);
98 value = (gauge_t) strtod (matches[1], &endptr);
99 if (matches[1] == endptr)
100 return (-1);
102 if ((data->values_num == 0)
103 || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST))
104 {
105 data->value.gauge = value;
106 }
107 else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE)
108 {
109 double f = ((double) data->values_num)
110 / ((double) (data->values_num + 1));
111 data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
112 }
113 else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN)
114 {
115 if (data->value.gauge > value)
116 data->value.gauge = value;
117 }
118 else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX)
119 {
120 if (data->value.gauge < value)
121 data->value.gauge = value;
122 }
123 else if (data->ds_type & UTILS_MATCH_CF_GAUGE_ADD)
124 {
125 data->value.gauge += value;
126 }
127 else
128 {
129 ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
130 return (-1);
131 }
133 data->values_num++;
134 }
135 else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER)
136 {
137 counter_t value;
138 char *endptr = NULL;
140 if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
141 {
142 data->value.counter++;
143 data->values_num++;
144 return (0);
145 }
147 if (matches_num < 2)
148 return (-1);
150 value = (counter_t) strtoull (matches[1], &endptr, 0);
151 if (matches[1] == endptr)
152 return (-1);
154 if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
155 data->value.counter = value;
156 else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
157 data->value.counter += value;
158 else
159 {
160 ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
161 return (-1);
162 }
164 data->values_num++;
165 }
166 else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE)
167 {
168 derive_t value;
169 char *endptr = NULL;
171 if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
172 {
173 data->value.derive++;
174 data->values_num++;
175 return (0);
176 }
178 if (matches_num < 2)
179 return (-1);
181 value = (derive_t) strtoll (matches[1], &endptr, 0);
182 if (matches[1] == endptr)
183 return (-1);
185 if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
186 data->value.derive = value;
187 else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
188 data->value.derive += value;
189 else
190 {
191 ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
192 return (-1);
193 }
195 data->values_num++;
196 }
197 else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE)
198 {
199 absolute_t value;
200 char *endptr = NULL;
202 if (matches_num < 2)
203 return (-1);
205 value = (absolute_t) strtoull (matches[1], &endptr, 0);
206 if (matches[1] == endptr)
207 return (-1);
209 if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
210 data->value.absolute = value;
211 else
212 {
213 ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
214 return (-1);
215 }
217 data->values_num++;
218 }
219 else
220 {
221 ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
222 return (-1);
223 }
225 return (0);
226 } /* int default_callback */
228 /*
229 * Public functions
230 */
231 cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
232 int (*callback) (const char *str,
233 char * const *matches, size_t matches_num, void *user_data),
234 void *user_data)
235 {
236 cu_match_t *obj;
237 int status;
239 DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
240 regex, excluderegex);
242 obj = calloc (1, sizeof (*obj));
243 if (obj == NULL)
244 return (NULL);
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 }
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 }
266 obj->callback = callback;
267 obj->user_data = user_data;
269 return (obj);
270 } /* cu_match_t *match_create_callback */
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;
278 user_data = calloc (1, sizeof (*user_data));
279 if (user_data == NULL)
280 return (NULL);
281 user_data->ds_type = match_ds_type;
283 obj = match_create_callback (regex, excluderegex,
284 default_callback, user_data);
285 if (obj == NULL)
286 {
287 sfree (user_data);
288 return (NULL);
289 }
291 obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
293 return (obj);
294 } /* cu_match_t *match_create_simple */
296 void match_value_reset (cu_match_value_t *mv)
297 {
298 if ((mv == NULL)
299 || ((mv->ds_type & UTILS_MATCH_DS_TYPE_GAUGE) == 0)
300 || ((mv->ds_type & UTILS_MATCH_CF_GAUGE_PERSIST) != 0))
301 return;
303 mv->value.gauge = NAN;
304 mv->values_num = 0;
305 } /* }}} void match_value_reset */
307 void match_destroy (cu_match_t *obj)
308 {
309 if (obj == NULL)
310 return;
312 if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
313 {
314 sfree (obj->user_data);
315 }
317 sfree (obj);
318 } /* void match_destroy */
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] = { 0 };
325 size_t matches_num;
327 if ((obj == NULL) || (str == NULL))
328 return (-1);
330 if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
331 status = regexec (&obj->excluderegex, str,
332 STATIC_ARRAY_SIZE (re_match), re_match,
333 /* eflags = */ 0);
334 /* Regex did match, so exclude this line */
335 if (status == 0) {
336 DEBUG("ExludeRegex matched, don't count that line\n");
337 return (0);
338 }
339 }
341 status = regexec (&obj->regex, str,
342 STATIC_ARRAY_SIZE (re_match), re_match,
343 /* eflags = */ 0);
345 /* Regex did not match */
346 if (status != 0)
347 return (0);
349 for (matches_num = 0; matches_num < STATIC_ARRAY_SIZE (matches); matches_num++)
350 {
351 if ((re_match[matches_num].rm_so < 0)
352 || (re_match[matches_num].rm_eo < 0))
353 break;
355 matches[matches_num] = match_substr (str,
356 re_match[matches_num].rm_so, re_match[matches_num].rm_eo);
357 if (matches[matches_num] == NULL)
358 {
359 status = -1;
360 break;
361 }
362 }
364 if (status != 0)
365 {
366 ERROR ("utils_match: match_apply: match_substr failed.");
367 }
368 else
369 {
370 status = obj->callback (str, matches, matches_num, obj->user_data);
371 if (status != 0)
372 {
373 ERROR ("utils_match: match_apply: callback failed.");
374 }
375 }
377 for (size_t i = 0; i < matches_num; i++)
378 {
379 sfree (matches[i]);
380 }
382 return (status);
383 } /* int match_apply */
385 void *match_get_user_data (cu_match_t *obj)
386 {
387 if (obj == NULL)
388 return (NULL);
389 return (obj->user_data);
390 } /* void *match_get_user_data */
392 /* vim: set sw=2 sts=2 ts=8 : */