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