src/utils_logtail.c: Code cleanups.
[collectd.git] / src / utils_logtail.c
1 /*
2  * collectd - src/utils_logtail.c
3  * Copyright (C) 2007-2008  C-Ware, Inc.
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; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Author:
19  *   Luke Heberling <lukeh at c-ware.com>
20  *
21  * Description:
22  *   Encapsulates useful code to plugins which must parse a log file.
23  */
24
25 #include "collectd.h"
26 #include "common.h"
27 #include "plugin.h"
28 #include "utils_tail.h"
29 #include "utils_llist.h"
30 #include "utils_avltree.h"
31
32 struct logtail_instance_s
33 {
34   char *name;
35   cu_tail_t *tail;
36   c_avl_tree_t *tree;
37   llist_t *list;
38   uint cache_size;
39   unsigned long *counters;
40 };
41 typedef struct logtail_instance_s logtail_instance_t;
42
43 static void submit (const char *plugin, const char *plugin_instance,
44     const char *name, value_t value)
45 {
46   value_list_t vl = VALUE_LIST_INIT;
47   value_t values[1];
48
49   values[0] = value;
50
51   vl.values = values;
52   vl.values_len = 1;
53   vl.time = time (NULL);
54   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
55   sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
56   sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
57   sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
58
59   plugin_dispatch_values (name, &vl);
60 } /* static void submit */
61
62 static int destroy_instance (logtail_instance_t *inst)
63 {
64   if (inst == NULL)
65     return (-1);
66
67   sfree (inst->name);
68   if (inst->tail != NULL)
69   {
70     cu_tail_destroy (inst->tail);
71     inst->tail = NULL;
72   }
73   if (inst->tree != NULL)
74   {
75     c_avl_destroy (inst->tree);
76     inst->tree = NULL;
77   }
78   assert ((inst->list == NULL) || (llist_size (inst->list) == 0));
79   if (inst->list != NULL)
80   {
81     llist_destroy (inst->list);
82     inst->list = NULL;
83   }
84
85   sfree (inst->counters);
86   sfree (inst);
87
88   return (0);
89 } /* int destroy_instance */
90
91 int logtail_term (llist_t **instances)
92 {
93   llentry_t *entry;
94   llentry_t *prev;
95
96   llentry_t *lentry;
97   llentry_t *lprev;
98
99   logtail_instance_t *instance;
100
101   if (*instances != NULL)
102   {
103     entry = llist_head (*instances);
104     while (entry)
105     {
106       prev = entry;
107       entry = entry->next;
108
109       instance = prev->value;
110       if (instance->list != NULL)
111       {
112         lentry = llist_head (instance->list);
113         while (lentry)
114         {
115           lprev = lentry;
116           lentry = lentry->next;
117           if (lprev->key != NULL)
118             free (lprev->key);
119           if (lprev->value != NULL)
120             free (lprev->value);
121           llist_remove (instance->list, lprev);
122           llentry_destroy (lprev);
123         }
124       }
125
126       llist_remove (*instances, prev);
127       llentry_destroy (prev);
128       destroy_instance (instance);
129     }
130
131     llist_destroy (*instances);
132     *instances = NULL;
133   }
134
135   return (0);
136 } /* int logtail_term */
137
138 int logtail_init (llist_t **instances)
139 {
140   if (*instances == NULL)
141     *instances = llist_create();
142
143   return (*instances == NULL);
144 } /* int logtail_init */
145
146 int logtail_read (llist_t **instances, tailfunc *func, char *plugin,
147     char **counter_instances)
148 {
149   llentry_t *entry;
150   char buffer[2048];
151   int status;
152   int i;
153
154   for (entry = llist_head (*instances); entry != NULL; entry = entry->next )
155   {
156     logtail_instance_t *instance = (logtail_instance_t *) entry->value;
157
158     status = cu_tail_read (instance->tail, buffer, sizeof (buffer),
159         func, instance);
160     if (status != 0)
161       continue;
162
163     for (i = 0; counter_instances[i] != NULL; i++)
164     {
165       char *name = counter_instances[i];
166       value_t value;
167       
168       value.counter = (counter_t) instance->counters[i];
169
170       submit (plugin, instance->name, name, value);
171     }
172   }
173
174   return (0);
175 } /* int logtail_read */
176
177 int logtail_config (llist_t **instances, oconfig_item_t *ci, char *plugin,
178     char **names, char *default_file, int default_cache_size)
179 {
180   int counterslen = 0;
181   logtail_instance_t *instance;
182
183   llentry_t *entry;
184   char *tail_file;
185
186   oconfig_item_t *gchild;
187   int gchildren;
188
189   oconfig_item_t *child = ci->children;
190   int children = ci->children_num;
191
192   while (*(names++) != NULL)
193     counterslen += sizeof (unsigned long);
194
195   if (*instances == NULL)
196   {
197     *instances = llist_create();
198     if (*instances == NULL)
199       return 1;
200   }
201
202
203   for (; children; --children, ++child)
204   {
205     tail_file = NULL;
206
207     if (strcmp (child->key, "Instance") != 0)
208     {
209       WARNING ("%s plugin: Ignoring unknown"
210           " config option `%s'.", plugin, child->key);
211       continue;
212     }
213
214     if ((child->values_num != 1) ||
215         (child->values[0].type != OCONFIG_TYPE_STRING))
216     {
217       WARNING ("%s plugin: `Instance' needs exactly"
218           " one string argument.", plugin);
219       continue;
220     }
221
222     instance = malloc (sizeof (logtail_instance_t));
223     if (instance == NULL)
224     {
225       ERROR ("%s plugin: `malloc' failed.", plugin);
226       return 1;
227     }
228     memset (instance, '\0', sizeof (logtail_instance_t));
229
230     instance->counters = malloc (counterslen);
231     if (instance->counters == NULL)
232     {
233       ERROR ("%s plugin: `malloc' failed.", plugin);
234       destroy_instance (instance);
235       return 1;
236     }
237     memset (instance->counters, '\0', counterslen);
238
239     instance->name = strdup (child->values[0].value.string);
240     if (instance->name == NULL)
241     {
242       ERROR ("%s plugin: `strdup' failed.", plugin);
243       destroy_instance (instance);
244       return 1;
245     }
246
247     instance->list = llist_create();
248     if (instance->list == NULL)
249     {
250       ERROR ("%s plugin: `llist_create' failed.", plugin);
251       destroy_instance (instance);
252       return 1;
253     }
254
255     instance->tree = c_avl_create ((void *)strcmp);
256     if (instance->tree == NULL)
257     {
258       ERROR ("%s plugin: `c_avl_create' failed.", plugin);
259       destroy_instance (instance);
260       return 1;
261     }
262
263     entry = llentry_create (instance->name, instance);
264     if (entry == NULL)
265     {
266       ERROR ("%s plugin: `llentry_create' failed.", plugin);
267       destroy_instance (instance);
268       return 1;
269     }
270
271     gchild = child->children;
272     gchildren = child->children_num;
273
274     for (; gchildren; --gchildren, ++gchild)
275     {
276       if (strcmp (gchild->key, "LogFile") == 0)
277       {
278         if (gchild->values_num != 1 || 
279             gchild->values[0].type != OCONFIG_TYPE_STRING)
280         {
281           WARNING ("%s plugin: config option `%s'"
282               " should have exactly one string value.",
283               plugin, gchild->key);
284           continue;
285         }
286         if (tail_file != NULL)
287         {
288           WARNING ("%s plugin: ignoring extraneous"
289               " `LogFile' config option.", plugin);
290           continue;
291         }
292         tail_file = gchild->values[0].value.string;
293       }
294       else if (strcmp (gchild->key, "CacheSize") == 0)
295       {
296         if (gchild->values_num != 1 
297             || gchild->values[0].type != OCONFIG_TYPE_NUMBER)
298         {
299           WARNING ("%s plugin: config option `%s'"
300               " should have exactly one numerical value.",
301               plugin, gchild->key);
302           continue;
303         }
304         if (instance->cache_size)
305         {
306           WARNING ("%s plugin: ignoring extraneous"
307               " `CacheSize' config option.", plugin);
308           continue;
309         }
310         instance->cache_size = gchild->values[0].value.number;
311       }
312       else
313       {
314         WARNING ("%s plugin: Ignoring unknown config option"
315             " `%s'.", plugin, gchild->key);
316         continue;
317       }
318
319       if (gchild->children_num)
320       {
321         WARNING ("%s plugin: config option `%s' should not"
322             " have children.", plugin, gchild->key);
323       }
324     }
325
326     if (tail_file == NULL)
327       tail_file = default_file;
328     instance->tail = cu_tail_create (tail_file);
329     if (instance->tail == NULL)
330     {
331       ERROR ("%s plugin: `cu_tail_create' failed.", plugin);
332       destroy_instance (instance);
333
334       llentry_destroy (entry);
335       return 1;
336     }
337
338     if (instance->cache_size == 0)
339       instance->cache_size = default_cache_size;
340
341     llist_append (*instances, entry);
342   }
343
344   return 0;
345 } /* int logtail_config */
346
347 unsigned long *logtail_counters (logtail_instance_t *instance)
348 {
349   return instance->counters;
350 } /* unsigned log *logtail_counters */
351
352 int logtail_cache (logtail_instance_t *instance, char *plugin, char *key, void **data, int len)
353 {
354   llentry_t *entry = NULL;
355
356   if (c_avl_get (instance->tree, key, (void*)&entry) == 0)
357   {
358     *data = entry->value;
359     return (0);
360   }
361
362   if ((key = strdup (key)) == NULL)
363   {
364     ERROR ("%s plugin: `strdup' failed.", plugin);
365     return (0);
366   }
367
368   if (data != NULL && (*data = malloc (len)) == NULL)
369   {
370     ERROR ("%s plugin: `malloc' failed.", plugin);
371     free (key);
372     return (0);
373   }
374
375   if (data != NULL)
376     memset (*data, '\0', len);
377
378   entry = llentry_create (key, data == NULL ? NULL : *data);
379   if (entry == NULL)
380   {
381     ERROR ("%s plugin: `llentry_create' failed.", plugin);
382     free (key);
383     if (data !=NULL)
384       free (*data);
385     return (0);
386   }
387
388   if (c_avl_insert (instance->tree, key, entry) != 0)
389   {
390     ERROR ("%s plugin: `c_avl_insert' failed.", plugin);
391     llentry_destroy (entry);
392     free (key);
393     if (data != NULL)
394       free (*data);
395     return (0);
396   }
397
398   llist_prepend (instance->list, entry);
399
400   while (llist_size (instance->list) > instance->cache_size &&
401       (entry = llist_tail (instance->list)) != NULL )
402   {
403     c_avl_remove (instance->tree, entry->key, NULL, NULL);
404     llist_remove (instance->list, entry);
405     free (entry->key);
406     if (entry->value != NULL)
407       free (entry->value);
408     llentry_destroy (entry);
409   }
410
411   return (1);
412 }
413
414 void logtail_decache (logtail_instance_t *instance, char *key)
415 {
416   llentry_t *entry = NULL;
417   if (c_avl_remove (instance->tree, key, NULL, (void*)&entry))
418     return;
419
420   llist_remove (instance->list, entry);
421   free (entry->key);
422   if (entry->value != NULL)
423     free (entry->value);
424
425   llentry_destroy (entry);
426 }
427
428 /* vim: set sw=2 sts=2 ts=8 : */