src/utils_vl_lookup.c: Fix a wrong pointer being passed to the callback.
[collectd.git] / src / utils_vl_lookup.c
1 /**
2  * collectd - src/utils_vl_lookup.c
3  * Copyright (C) 2012  Florian 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 Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28 #include "common.h"
29 #include "utils_vl_lookup.h"
30 #include "utils_avltree.h"
31
32 #if BUILD_TEST
33 # define sstrncpy strncpy
34 # define plugin_log(s, ...) do { \
35   printf ("[severity %i] ", s); \
36   printf (__VA_ARGS__); \
37   printf ("\n"); \
38 } while (0)
39 #endif
40
41 /*
42  * Types
43  */
44 struct lookup_s
45 {
46   c_avl_tree_t *by_type_tree;
47
48   lookup_class_callback_t cb_user_class;
49   lookup_obj_callback_t cb_user_obj;
50   lookup_free_class_callback_t cb_free_class;
51   lookup_free_obj_callback_t cb_free_obj;
52 };
53
54 struct user_obj_s;
55 typedef struct user_obj_s user_obj_t;
56 struct user_obj_s
57 {
58   void *user_obj;
59   identifier_t ident;
60
61   user_obj_t *next;
62 };
63
64 struct user_class_s
65 {
66   void *user_class;
67   identifier_t ident;
68   user_obj_t *user_obj_list; /* list of user_obj */
69 };
70 typedef struct user_class_s user_class_t;
71
72 struct user_class_list_s;
73 typedef struct user_class_list_s user_class_list_t;
74 struct user_class_list_s
75 {
76   user_class_t entry;
77   user_class_list_t *next;
78 };
79
80 struct by_type_entry_s
81 {
82   c_avl_tree_t *by_plugin_tree; /* plugin -> user_class_list_t */
83   user_class_list_t *wildcard_plugin_list;
84 };
85 typedef struct by_type_entry_s by_type_entry_t;
86
87 #define LU_IS_ANY(str) (strcmp (str, "/any/") == 0)
88 #define LU_IS_ALL(str) (strcmp (str, "/all/") == 0)
89 #define LU_IS_WILDCARD(str) (LU_IS_ANY(str) || LU_IS_ALL(str))
90
91 /*
92  * Private functions
93  */
94 static void *lu_create_user_obj (lookup_t *obj, /* {{{ */
95     data_set_t const *ds, value_list_t const *vl,
96     user_class_t *user_class)
97 {
98   user_obj_t *user_obj;
99
100   user_obj = malloc (sizeof (*user_obj));
101   if (user_obj == NULL)
102   {
103     ERROR ("utils_vl_lookup: malloc failed.");
104     return (NULL);
105   }
106   memset (user_obj, 0, sizeof (*user_obj));
107   user_obj->next = NULL;
108
109   user_obj->user_obj = obj->cb_user_class (ds, vl, user_class->user_class);
110   if (user_obj->user_obj == NULL)
111   {
112     sfree (user_obj);
113     WARNING("utils_vl_lookup: User-provided constructor failed.");
114     return (NULL);
115   }
116
117   sstrncpy (user_obj->ident.host,
118     LU_IS_ALL (user_class->ident.host) ?  "/all/" : vl->host,
119     sizeof (user_obj->ident.host));
120   sstrncpy (user_obj->ident.plugin,
121     LU_IS_ALL (user_class->ident.plugin) ?  "/all/" : vl->plugin,
122     sizeof (user_obj->ident.plugin));
123   sstrncpy (user_obj->ident.plugin_instance,
124     LU_IS_ALL (user_class->ident.plugin_instance) ?  "/all/" : vl->plugin_instance,
125     sizeof (user_obj->ident.plugin_instance));
126   sstrncpy (user_obj->ident.type,
127     LU_IS_ALL (user_class->ident.type) ?  "/all/" : vl->type,
128     sizeof (user_obj->ident.type));
129   sstrncpy (user_obj->ident.type_instance,
130     LU_IS_ALL (user_class->ident.type_instance) ?  "/all/" : vl->type_instance,
131     sizeof (user_obj->ident.type_instance));
132
133   if (user_class->user_obj_list == NULL)
134   {
135     user_class->user_obj_list = user_obj;
136   }
137   else
138   {
139     user_obj_t *last = user_class->user_obj_list;
140     while (last->next != NULL)
141       last = last->next;
142     last->next = user_obj;
143   }
144
145   return (user_obj);
146 } /* }}} void *lu_create_user_obj */
147
148 static user_obj_t *lu_find_user_obj (user_class_t *user_class, /* {{{ */
149     value_list_t const *vl)
150 {
151   user_obj_t *ptr;
152
153   for (ptr = user_class->user_obj_list;
154       ptr != NULL;
155       ptr = ptr->next)
156   {
157     if (!LU_IS_ALL (ptr->ident.host)
158         && (strcmp (ptr->ident.host, vl->host) != 0))
159       continue;
160     if (!LU_IS_ALL (ptr->ident.plugin_instance)
161         && (strcmp (ptr->ident.plugin_instance, vl->plugin_instance) != 0))
162       continue;
163     if (!LU_IS_ALL (ptr->ident.type_instance)
164         && (strcmp (ptr->ident.type_instance, vl->type_instance) != 0))
165       continue;
166
167     return (ptr);
168   }
169
170   return (NULL);
171 } /* }}} user_obj_t *lu_find_user_obj */
172
173 static int lu_handle_user_class (lookup_t *obj, /* {{{ */
174     data_set_t const *ds, value_list_t const *vl,
175     user_class_t *user_class)
176 {
177   user_obj_t *user_obj;
178   int status;
179
180   assert (strcmp (vl->type, user_class->ident.type) == 0);
181   assert (LU_IS_WILDCARD (user_class->ident.plugin)
182       || (strcmp (vl->plugin, user_class->ident.plugin) == 0));
183
184   /* When we get here, type and plugin already match the user class. Now check
185    * the rest of the fields. */
186   if (!LU_IS_WILDCARD (user_class->ident.type_instance)
187       && (strcmp (vl->type_instance, user_class->ident.type_instance) != 0))
188     return (1);
189   if (!LU_IS_WILDCARD (user_class->ident.plugin_instance)
190       && (strcmp (vl->plugin_instance,
191           user_class->ident.plugin_instance) != 0))
192     return (1);
193   if (!LU_IS_WILDCARD (user_class->ident.host)
194       && (strcmp (vl->host, user_class->ident.host) != 0))
195     return (1);
196
197   user_obj = lu_find_user_obj (user_class, vl);
198   if (user_obj == NULL)
199   {
200     /* call lookup_class_callback_t() and insert into the list of user objects. */
201     user_obj = lu_create_user_obj (obj, ds, vl, user_class);
202     if (user_obj == NULL)
203       return (-1);
204   }
205
206   status = obj->cb_user_obj (ds, vl,
207       user_class->user_class, user_obj->user_obj);
208   if (status != 0)
209   {
210     ERROR ("utils_vl_lookup: The user object callback failed with status %i.",
211         status);
212     /* Returning a negative value means: abort! */
213     if (status < 0)
214       return (status);
215     else
216       return (1);
217   }
218
219   return (0);
220 } /* }}} int lu_handle_user_class */
221
222 static int lu_handle_user_class_list (lookup_t *obj, /* {{{ */
223     data_set_t const *ds, value_list_t const *vl,
224     user_class_list_t *user_class_list)
225 {
226   user_class_list_t *ptr;
227   int retval = 0;
228   
229   for (ptr = user_class_list; ptr != NULL; ptr = ptr->next)
230   {
231     int status;
232
233     status = lu_handle_user_class (obj, ds, vl, &ptr->entry);
234     if (status < 0)
235       return (status);
236     else if (status == 0)
237       retval++;
238   }
239
240   return (retval);
241 } /* }}} int lu_handle_user_class_list */
242
243 static by_type_entry_t *lu_search_by_type (lookup_t *obj, /* {{{ */
244     char const *type, _Bool allocate_if_missing)
245 {
246   by_type_entry_t *by_type;
247   char *type_copy;
248   int status;
249
250   status = c_avl_get (obj->by_type_tree, type, (void *) &by_type);
251   if (status == 0)
252     return (by_type);
253
254   if (!allocate_if_missing)
255     return (NULL);
256
257   type_copy = strdup (type);
258   if (type_copy == NULL)
259   {
260     ERROR ("utils_vl_lookup: strdup failed.");
261     return (NULL);
262   }
263
264   by_type = malloc (sizeof (*by_type));
265   if (by_type == NULL)
266   {
267     ERROR ("utils_vl_lookup: malloc failed.");
268     sfree (type_copy);
269     return (NULL);
270   }
271   memset (by_type, 0, sizeof (*by_type));
272   by_type->wildcard_plugin_list = NULL;
273   
274   by_type->by_plugin_tree = c_avl_create ((void *) strcmp);
275   if (by_type->by_plugin_tree == NULL)
276   {
277     ERROR ("utils_vl_lookup: c_avl_create failed.");
278     sfree (by_type);
279     sfree (type_copy);
280     return (NULL);
281   }
282
283   status = c_avl_insert (obj->by_type_tree,
284       /* key = */ type_copy, /* value = */ by_type);
285   assert (status <= 0); /* >0 => entry exists => race condition. */
286   if (status != 0)
287   {
288     ERROR ("utils_vl_lookup: c_avl_insert failed.");
289     c_avl_destroy (by_type->by_plugin_tree);
290     sfree (by_type);
291     sfree (type_copy);
292     return (NULL);
293   }
294   
295   return (by_type);
296 } /* }}} by_type_entry_t *lu_search_by_type */
297
298 static int lu_add_by_plugin (by_type_entry_t *by_type, /* {{{ */
299     identifier_t const *ident, user_class_list_t *user_class_list)
300 {
301   user_class_list_t *ptr = NULL;
302
303   /* Lookup user_class_list from the per-plugin structure. If this is the first
304    * user_class to be added, the blocks return immediately. Otherwise they will
305    * set "ptr" to non-NULL. */
306   if (LU_IS_WILDCARD (ident->plugin))
307   {
308     if (by_type->wildcard_plugin_list == NULL)
309     {
310       by_type->wildcard_plugin_list = user_class_list;
311       return (0);
312     }
313
314     ptr = by_type->wildcard_plugin_list;
315   } /* if (plugin is wildcard) */
316   else /* (plugin is not wildcard) */
317   {
318     int status;
319
320     status = c_avl_get (by_type->by_plugin_tree,
321         ident->plugin, (void *) &ptr);
322
323     if (status != 0) /* plugin not yet in tree */
324     {
325       char *plugin_copy = strdup (ident->plugin);
326
327       if (plugin_copy == NULL)
328       {
329         ERROR ("utils_vl_lookup: strdup failed.");
330         sfree (user_class_list);
331         return (ENOMEM);
332       }
333
334       status = c_avl_insert (by_type->by_plugin_tree,
335           plugin_copy, user_class_list);
336       if (status != 0)
337       {
338         ERROR ("utils_vl_lookup: c_avl_insert(\"%s\") failed with status %i.",
339             plugin_copy, status);
340         sfree (plugin_copy);
341         sfree (user_class_list);
342         return (status);
343       }
344       else
345       {
346         return (0);
347       }
348     } /* if (plugin not yet in tree) */
349   } /* if (plugin is not wildcard) */
350
351   assert (ptr != NULL);
352
353   while (ptr->next != NULL)
354     ptr = ptr->next;
355   ptr->next = user_class_list;
356
357   return (0);
358 } /* }}} int lu_add_by_plugin */
359
360 static void lu_destroy_user_obj (lookup_t *obj, /* {{{ */
361     user_obj_t *user_obj)
362 {
363   while (user_obj != NULL)
364   {
365     user_obj_t *next = user_obj->next;
366
367     if (obj->cb_free_obj != NULL)
368       obj->cb_free_obj (user_obj->user_obj);
369     user_obj->user_obj = NULL;
370
371     sfree (user_obj);
372     user_obj = next;
373   }
374 } /* }}} void lu_destroy_user_obj */
375
376 static void lu_destroy_user_class_list (lookup_t *obj, /* {{{ */
377     user_class_list_t *user_class_list)
378 {
379   while (user_class_list != NULL)
380   {
381     user_class_list_t *next = user_class_list->next;
382
383     if (obj->cb_free_class != NULL)
384       obj->cb_free_class (user_class_list->entry.user_class);
385     user_class_list->entry.user_class = NULL;
386
387     lu_destroy_user_obj (obj, user_class_list->entry.user_obj_list);
388     user_class_list->entry.user_obj_list = NULL;
389
390     sfree (user_class_list);
391     user_class_list = next;
392   }
393 } /* }}} void lu_destroy_user_class_list */
394
395 static void lu_destroy_by_type (lookup_t *obj, /* {{{ */
396     by_type_entry_t *by_type)
397 {
398   
399   while (42)
400   {
401     char *plugin = NULL;
402     user_class_list_t *user_class_list = NULL;
403     int status;
404
405     status = c_avl_pick (by_type->by_plugin_tree,
406         (void *) &plugin, (void *) &user_class_list);
407     if (status != 0)
408       break;
409
410     DEBUG ("utils_vl_lookup: lu_destroy_by_type: Destroying plugin \"%s\".",
411         plugin);
412     sfree (plugin);
413     lu_destroy_user_class_list (obj, user_class_list);
414   }
415
416   c_avl_destroy (by_type->by_plugin_tree);
417   by_type->by_plugin_tree = NULL;
418
419   lu_destroy_user_class_list (obj, by_type->wildcard_plugin_list);
420   by_type->wildcard_plugin_list = NULL;
421
422   sfree (by_type);
423 } /* }}} int lu_destroy_by_type */
424
425 /*
426  * Public functions
427  */
428 lookup_t *lookup_create (lookup_class_callback_t cb_user_class, /* {{{ */
429     lookup_obj_callback_t cb_user_obj,
430     lookup_free_class_callback_t cb_free_class,
431     lookup_free_obj_callback_t cb_free_obj)
432 {
433   lookup_t *obj = malloc (sizeof (*obj));
434   if (obj == NULL)
435   {
436     ERROR ("utils_vl_lookup: malloc failed.");
437     return (NULL);
438   }
439   memset (obj, 0, sizeof (*obj));
440
441   obj->by_type_tree = c_avl_create ((void *) strcmp);
442   if (obj->by_type_tree == NULL)
443   {
444     ERROR ("utils_vl_lookup: c_avl_create failed.");
445     sfree (obj);
446     return (NULL);
447   }
448
449   obj->cb_user_class = cb_user_class;
450   obj->cb_user_obj = cb_user_obj;
451   obj->cb_free_class = cb_free_class;
452   obj->cb_free_obj = cb_free_obj;
453
454   return (obj);
455 } /* }}} lookup_t *lookup_create */
456
457 void lookup_destroy (lookup_t *obj) /* {{{ */
458 {
459   int status;
460
461   if (obj == NULL)
462     return;
463
464   while (42)
465   {
466     char *type = NULL;
467     by_type_entry_t *by_type = NULL;
468
469     status = c_avl_pick (obj->by_type_tree, (void *) &type, (void *) &by_type);
470     if (status != 0)
471       break;
472
473     DEBUG ("utils_vl_lookup: lookup_destroy: Destroying type \"%s\".", type);
474     sfree (type);
475     lu_destroy_by_type (obj, by_type);
476   }
477
478   c_avl_destroy (obj->by_type_tree);
479   obj->by_type_tree = NULL;
480
481   sfree (obj);
482 } /* }}} void lookup_destroy */
483
484 int lookup_add (lookup_t *obj, /* {{{ */
485     identifier_t const *ident, void *user_class)
486 {
487   by_type_entry_t *by_type = NULL;
488   user_class_list_t *user_class_obj;
489
490   by_type = lu_search_by_type (obj, ident->type, /* allocate = */ 1);
491   if (by_type == NULL)
492     return (-1);
493
494   user_class_obj = malloc (sizeof (*user_class_obj));
495   if (user_class_obj == NULL)
496   {
497     ERROR ("utils_vl_lookup: malloc failed.");
498     return (ENOMEM);
499   }
500   memset (user_class_obj, 0, sizeof (*user_class_obj));
501   user_class_obj->entry.user_class = user_class;
502   memmove (&user_class_obj->entry.ident, ident, sizeof (*ident));
503   user_class_obj->entry.user_obj_list = NULL;
504   user_class_obj->next = NULL;
505
506   return (lu_add_by_plugin (by_type, ident, user_class_obj));
507 } /* }}} int lookup_add */
508
509 /* returns the number of successful calls to the callback function */
510 int lookup_search (lookup_t *obj, /* {{{ */
511     data_set_t const *ds, value_list_t const *vl)
512 {
513   by_type_entry_t *by_type = NULL;
514   user_class_list_t *user_class_list = NULL;
515   int retval = 0;
516   int status;
517
518   if ((obj == NULL) || (ds == NULL) || (vl == NULL))
519     return (-EINVAL);
520
521   by_type = lu_search_by_type (obj, vl->type, /* allocate = */ 0);
522   if (by_type == NULL)
523     return (0);
524
525   status = c_avl_get (by_type->by_plugin_tree,
526       vl->plugin, (void *) &user_class_list);
527   if (status == 0)
528   {
529     status = lu_handle_user_class_list (obj, ds, vl, user_class_list);
530     if (status < 0)
531       return (status);
532     retval += status;
533   }
534
535   if (by_type->wildcard_plugin_list != NULL)
536   {
537     status = lu_handle_user_class_list (obj, ds, vl,
538         by_type->wildcard_plugin_list);
539     if (status < 0)
540       return (status);
541     retval += status;
542   }
543     
544   return (retval);
545 } /* }}} lookup_search */