src/utils_match.[ch]: Improved the handling of gauge values.
[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, void *user_data);
39   void *user_data;
40 };
41
42 /*
43  * Private functions
44  */
45 static int default_callback (const char *str, void *user_data)
46 {
47   cu_match_value_t *data = (cu_match_value_t *) user_data;
48
49   if (data->ds_type & UTILS_MATCH_DS_TYPE_GAUGE)
50   {
51     gauge_t value;
52     char *endptr = NULL;
53
54     value = strtod (str, &endptr);
55     if (str == endptr)
56       return (-1);
57
58     if ((data->values_num == 0)
59         || (data->ds_type & UTILS_MATCH_CF_GAUGE_LAST))
60     {
61       data->value.gauge = value;
62     }
63     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_AVERAGE)
64     {
65       double f = ((double) data->values_num)
66         / ((double) (data->values_num + 1));
67       data->value.gauge = (data->value.gauge * f) + (value * (1.0 - f));
68     }
69     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MIN)
70     {
71       if (data->value.gauge > value)
72         data->value.gauge = value;
73     }
74     else if (data->ds_type & UTILS_MATCH_CF_GAUGE_MAX)
75     {
76       if (data->value.gauge < value)
77         data->value.gauge = value;
78     }
79     else
80     {
81       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
82       return (-1);
83     }
84
85     data->values_num++;
86   }
87   else if (data->ds_type & UTILS_MATCH_DS_TYPE_COUNTER)
88   {
89     counter_t value;
90     char *endptr = NULL;
91
92     if (data->ds_type & UTILS_MATCH_CF_COUNTER_INC)
93     {
94       data->value.counter++;
95       data->values_num++;
96       return (0);
97     }
98
99     value = strtoll (str, &endptr, 0);
100     if (str == endptr)
101       return (-1);
102
103     if (data->ds_type & UTILS_MATCH_CF_COUNTER_SET)
104       data->value.counter = value;
105     else if (data->ds_type & UTILS_MATCH_CF_COUNTER_ADD)
106       data->value.counter += value;
107     else
108     {
109       ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
110       return (-1);
111     }
112
113     data->values_num++;
114   }
115   else
116   {
117     ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
118     return (-1);
119   }
120
121   return (0);
122 } /* int default_callback */
123
124 /*
125  * Public functions
126  */
127 cu_match_t *match_create_callback (const char *regex,
128                 int (*callback) (const char *str, void *user_data),
129                 void *user_data)
130 {
131   cu_match_t *obj;
132   int status;
133
134   obj = (cu_match_t *) malloc (sizeof (cu_match_t));
135   if (obj == NULL)
136     return (NULL);
137   memset (obj, '\0', sizeof (cu_match_t));
138
139   status = regcomp (&obj->regex, regex, REG_EXTENDED);
140   if (status != 0)
141   {
142     ERROR ("Compiling the regular expression \"%s\" failed.", regex);
143     sfree (obj);
144     return (NULL);
145   }
146
147   obj->callback = callback;
148   obj->user_data = user_data;
149
150   return (obj);
151 } /* cu_match_t *match_create_callback */
152
153 cu_match_t *match_create_simple (const char *regex, int match_ds_type)
154 {
155   cu_match_value_t *user_data;
156   cu_match_t *obj;
157
158   user_data = (cu_match_value_t *) malloc (sizeof (cu_match_value_t));
159   if (user_data == NULL)
160     return (NULL);
161   memset (user_data, '\0', sizeof (cu_match_value_t));
162   user_data->ds_type = match_ds_type;
163
164   obj = match_create_callback (regex, default_callback, user_data);
165   if (obj == NULL)
166   {
167     sfree (user_data);
168     return (NULL);
169   }
170
171   obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
172
173   return (obj);
174 } /* cu_match_t *match_create_simple */
175
176 void match_destroy (cu_match_t *obj)
177 {
178   if (obj == NULL)
179     return;
180
181   if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
182   {
183     sfree (obj->user_data);
184   }
185
186   sfree (obj);
187 } /* void match_destroy */
188
189 int match_apply (cu_match_t *obj, const char *str)
190 {
191   int status;
192   regmatch_t re_match[2];
193   char *sub_match;
194   size_t sub_match_len;
195
196   if ((obj == NULL) || (str == NULL))
197     return (-1);
198
199   re_match[0].rm_so = -1;
200   re_match[0].rm_eo = -1;
201   re_match[1].rm_so = -1;
202   re_match[1].rm_eo = -1;
203   status = regexec (&obj->regex, str, /* nmatch = */ 2, re_match,
204       /* eflags = */ 0);
205
206   /* Regex did not match */
207   if (status != 0)
208     return (0);
209
210   /* re_match[0] is the location of the entire match.
211    * re_match[1] is the location of the sub-match. */
212   if (re_match[1].rm_so < 0)
213   {
214     status = obj->callback (str, obj->user_data);
215     return (status);
216   }
217
218   assert (re_match[1].rm_so < re_match[1].rm_eo);
219   sub_match_len = (size_t) (re_match[1].rm_eo - re_match[1].rm_so);
220   sub_match = (char *) malloc (sizeof (char) * (sub_match_len + 1));
221   if (sub_match == NULL)
222   {
223     ERROR ("malloc failed.");
224     return (-1);
225   }
226   sstrncpy (sub_match, str + re_match[1].rm_so, sub_match_len + 1);
227
228   DEBUG ("utils_match: match_apply: Dispatching substring \"%s\" to "
229       "callback.", sub_match);
230   status = obj->callback (sub_match, obj->user_data);
231
232   sfree (sub_match);
233
234   return (status);
235 } /* int match_apply */
236
237 void *match_get_user_data (cu_match_t *obj)
238 {
239   if (obj == NULL)
240     return (NULL);
241   return (obj->user_data);
242 } /* void *match_get_user_data */
243
244 /* vim: set sw=2 sts=2 ts=8 : */