Merge branch 'ff/tail'
[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   DEBUG ("utils_match: match_create_callback: regex = %s", regex);
135
136   obj = (cu_match_t *) malloc (sizeof (cu_match_t));
137   if (obj == NULL)
138     return (NULL);
139   memset (obj, '\0', sizeof (cu_match_t));
140
141   status = regcomp (&obj->regex, regex, REG_EXTENDED);
142   if (status != 0)
143   {
144     ERROR ("Compiling the regular expression \"%s\" failed.", regex);
145     sfree (obj);
146     return (NULL);
147   }
148
149   obj->callback = callback;
150   obj->user_data = user_data;
151
152   return (obj);
153 } /* cu_match_t *match_create_callback */
154
155 cu_match_t *match_create_simple (const char *regex, int match_ds_type)
156 {
157   cu_match_value_t *user_data;
158   cu_match_t *obj;
159
160   user_data = (cu_match_value_t *) malloc (sizeof (cu_match_value_t));
161   if (user_data == NULL)
162     return (NULL);
163   memset (user_data, '\0', sizeof (cu_match_value_t));
164   user_data->ds_type = match_ds_type;
165
166   obj = match_create_callback (regex, default_callback, user_data);
167   if (obj == NULL)
168   {
169     sfree (user_data);
170     return (NULL);
171   }
172
173   obj->flags |= UTILS_MATCH_FLAGS_FREE_USER_DATA;
174
175   return (obj);
176 } /* cu_match_t *match_create_simple */
177
178 void match_destroy (cu_match_t *obj)
179 {
180   if (obj == NULL)
181     return;
182
183   if (obj->flags & UTILS_MATCH_FLAGS_FREE_USER_DATA)
184   {
185     sfree (obj->user_data);
186   }
187
188   sfree (obj);
189 } /* void match_destroy */
190
191 int match_apply (cu_match_t *obj, const char *str)
192 {
193   int status;
194   regmatch_t re_match[2];
195   char *sub_match;
196   size_t sub_match_len;
197
198   if ((obj == NULL) || (str == NULL))
199     return (-1);
200
201   re_match[0].rm_so = -1;
202   re_match[0].rm_eo = -1;
203   re_match[1].rm_so = -1;
204   re_match[1].rm_eo = -1;
205   status = regexec (&obj->regex, str, /* nmatch = */ 2, re_match,
206       /* eflags = */ 0);
207
208   /* Regex did not match */
209   if (status != 0)
210     return (0);
211
212   /* re_match[0] is the location of the entire match.
213    * re_match[1] is the location of the sub-match. */
214   if (re_match[1].rm_so < 0)
215   {
216     status = obj->callback (str, obj->user_data);
217     return (status);
218   }
219
220   assert (re_match[1].rm_so < re_match[1].rm_eo);
221   sub_match_len = (size_t) (re_match[1].rm_eo - re_match[1].rm_so);
222   sub_match = (char *) malloc (sizeof (char) * (sub_match_len + 1));
223   if (sub_match == NULL)
224   {
225     ERROR ("malloc failed.");
226     return (-1);
227   }
228   sstrncpy (sub_match, str + re_match[1].rm_so, sub_match_len + 1);
229
230   DEBUG ("utils_match: match_apply: Dispatching substring \"%s\" to "
231       "callback.", sub_match);
232   status = obj->callback (sub_match, obj->user_data);
233
234   sfree (sub_match);
235
236   return (status);
237 } /* int match_apply */
238
239 void *match_get_user_data (cu_match_t *obj)
240 {
241   if (obj == NULL)
242     return (NULL);
243   return (obj->user_data);
244 } /* void *match_get_user_data */
245
246 /* vim: set sw=2 sts=2 ts=8 : */