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 **/
27 #include "collectd.h"
28 #include "common.h"
29 #include "utils_vl_lookup.h"
30 #include "utils_avltree.h"
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
41 /*
42 * Types
43 */
44 struct lookup_s
45 {
46 c_avl_tree_t *by_type_tree;
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 };
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;
61 user_obj_t *next;
62 };
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;
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 };
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;
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;
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;
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 }
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));
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 }
141 return (user_obj);
142 } /* }}} void *lu_create_user_obj */
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;
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;
163 return (ptr);
164 }
166 return (NULL);
167 } /* }}} user_obj_t *lu_find_user_obj */
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;
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));
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);
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 }
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 }
215 return (0);
216 } /* }}} int lu_handle_user_class */
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;
225 for (ptr = user_class_list; ptr != NULL; ptr = ptr->next)
226 {
227 int status;
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 }
236 return (retval);
237 } /* }}} int lu_handle_user_class_list */
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;
246 status = c_avl_get (obj->by_type_tree, type, (void *) &by_type);
247 if (status == 0)
248 return (by_type);
250 if (!allocate_if_missing)
251 return (NULL);
253 type_copy = strdup (type);
254 if (type_copy == NULL)
255 {
256 ERROR ("utils_vl_lookup: strdup failed.");
257 return (NULL);
258 }
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;
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 }
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 }
291 return (by_type);
292 } /* }}} by_type_entry_t *lu_search_by_type */
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;
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 }
310 ptr = by_type->wildcard_plugin_list;
311 } /* if (plugin is wildcard) */
312 else /* (plugin is not wildcard) */
313 {
314 int status;
316 status = c_avl_get (by_type->by_plugin_tree,
317 ident->plugin, (void *) &ptr);
319 if (status != 0) /* plugin not yet in tree */
320 {
321 char *plugin_copy = strdup (ident->plugin);
323 if (plugin_copy == NULL)
324 {
325 ERROR ("utils_vl_lookup: strdup failed.");
326 sfree (user_class_list);
327 return (ENOMEM);
328 }
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) */
347 assert (ptr != NULL);
349 while (ptr->next != NULL)
350 ptr = ptr->next;
351 ptr->next = user_class_list;
353 return (0);
354 } /* }}} int lu_add_by_plugin */
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;
363 if (obj->cb_free_obj != NULL)
364 obj->cb_free_obj (user_obj->user_obj);
365 user_obj->user_obj = NULL;
367 sfree (user_obj);
368 user_obj = next;
369 }
370 } /* }}} void lu_destroy_user_obj */
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;
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;
383 lu_destroy_user_obj (obj, user_class_list->entry.user_obj_list);
384 user_class_list->entry.user_obj_list = NULL;
386 sfree (user_class_list);
387 user_class_list = next;
388 }
389 } /* }}} void lu_destroy_user_class_list */
391 static void lu_destroy_by_type (lookup_t *obj, /* {{{ */
392 by_type_entry_t *by_type)
393 {
395 while (42)
396 {
397 char *plugin = NULL;
398 user_class_list_t *user_class_list = NULL;
399 int status;
401 status = c_avl_pick (by_type->by_plugin_tree,
402 (void *) &plugin, (void *) &user_class_list);
403 if (status != 0)
404 break;
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 }
412 c_avl_destroy (by_type->by_plugin_tree);
413 by_type->by_plugin_tree = NULL;
415 lu_destroy_user_class_list (obj, by_type->wildcard_plugin_list);
416 by_type->wildcard_plugin_list = NULL;
418 sfree (by_type);
419 } /* }}} int lu_destroy_by_type */
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));
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 }
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;
450 return (obj);
451 } /* }}} lookup_t *lookup_create */
453 void lookup_destroy (lookup_t *obj) /* {{{ */
454 {
455 int status;
457 if (obj == NULL)
458 return;
460 while (42)
461 {
462 char *type = NULL;
463 by_type_entry_t *by_type = NULL;
465 status = c_avl_pick (obj->by_type_tree, (void *) &type, (void *) &by_type);
466 if (status != 0)
467 break;
469 DEBUG ("utils_vl_lookup: lookup_destroy: Destroying type \"%s\".", type);
470 sfree (type);
471 lu_destroy_by_type (obj, by_type);
472 }
474 c_avl_destroy (obj->by_type_tree);
475 obj->by_type_tree = NULL;
477 sfree (obj);
478 } /* }}} void lookup_destroy */
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;
486 by_type = lu_search_by_type (obj, ident->type, /* allocate = */ 1);
487 if (by_type == NULL)
488 return (-1);
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;
502 return (lu_add_by_plugin (by_type, ident, user_class_obj));
503 } /* }}} int lookup_add */
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;
514 if ((obj == NULL) || (ds == NULL) || (vl == NULL))
515 return (-EINVAL);
517 by_type = lu_search_by_type (obj, vl->type, /* allocate = */ 0);
518 if (by_type == NULL)
519 return (0);
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 }
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 }
540 return (retval);
541 } /* }}} lookup_search */