2 * collectd - src/utils_vl_lookup.c
3 * Copyright (C) 2012 Florian Forster
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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.
24 * Florian Forster <octo at collectd.org>
33 #include "utils_avltree.h"
34 #include "utils_vl_lookup.h"
42 #endif /* HAVE_LIBKSTAT */
45 #define sstrncpy strncpy
46 #define plugin_log(s, ...) \
48 printf("[severity %i] ", s); \
49 printf(__VA_ARGS__); \
58 char str[DATA_MAX_NAME_LEN];
62 typedef struct part_match_s part_match_t;
64 struct identifier_match_s {
67 part_match_t plugin_instance;
69 part_match_t type_instance;
71 unsigned int group_by;
73 typedef struct identifier_match_s identifier_match_t;
76 c_avl_tree_t *by_type_tree;
78 lookup_class_callback_t cb_user_class;
79 lookup_obj_callback_t cb_user_obj;
80 lookup_free_class_callback_t cb_free_class;
81 lookup_free_obj_callback_t cb_free_obj;
85 typedef struct user_obj_s user_obj_t;
88 lookup_identifier_t ident;
96 identifier_match_t match;
97 user_obj_t *user_obj_list; /* list of user_obj */
99 typedef struct user_class_s user_class_t;
101 struct user_class_list_s;
102 typedef struct user_class_list_s user_class_list_t;
103 struct user_class_list_s {
105 user_class_list_t *next;
108 struct by_type_entry_s {
109 c_avl_tree_t *by_plugin_tree; /* plugin -> user_class_list_t */
110 user_class_list_t *wildcard_plugin_list;
112 typedef struct by_type_entry_s by_type_entry_t;
117 static bool lu_part_matches(part_match_t const *match, /* {{{ */
119 if (match->is_regex) {
120 /* Short cut popular catch-all regex. */
121 if (strcmp(".*", match->str) == 0)
124 int status = regexec(&match->regex, str,
125 /* nmatch = */ 0, /* pmatch = */ NULL,
131 } else if (strcmp(match->str, str) == 0)
135 } /* }}} bool lu_part_matches */
137 static int lu_copy_ident_to_match_part(part_match_t *match_part, /* {{{ */
138 char const *ident_part) {
139 size_t len = strlen(ident_part);
142 if ((len < 3) || (ident_part[0] != '/') || (ident_part[len - 1] != '/')) {
143 sstrncpy(match_part->str, ident_part, sizeof(match_part->str));
144 match_part->is_regex = false;
148 /* Copy string without the leading slash. */
149 sstrncpy(match_part->str, ident_part + 1, sizeof(match_part->str));
150 assert(sizeof(match_part->str) > len);
151 /* strip trailing slash */
152 match_part->str[len - 2] = 0;
154 status = regcomp(&match_part->regex, match_part->str,
155 /* flags = */ REG_EXTENDED);
158 regerror(status, &match_part->regex, errbuf, sizeof(errbuf));
159 ERROR("utils_vl_lookup: Compiling regular expression \"%s\" failed: %s",
160 match_part->str, errbuf);
163 match_part->is_regex = true;
166 } /* }}} int lu_copy_ident_to_match_part */
168 static int lu_copy_ident_to_match(identifier_match_t *match, /* {{{ */
169 lookup_identifier_t const *ident,
170 unsigned int group_by) {
171 memset(match, 0, sizeof(*match));
173 match->group_by = group_by;
175 #define COPY_FIELD(field) \
177 int status = lu_copy_ident_to_match_part(&match->field, ident->field); \
184 COPY_FIELD(plugin_instance);
186 COPY_FIELD(type_instance);
191 } /* }}} int lu_copy_ident_to_match */
193 /* user_class->lock must be held when calling this function */
194 static void *lu_create_user_obj(lookup_t *obj, /* {{{ */
195 data_set_t const *ds, value_list_t const *vl,
196 user_class_t *user_class) {
197 user_obj_t *user_obj;
199 user_obj = calloc(1, sizeof(*user_obj));
200 if (user_obj == NULL) {
201 ERROR("utils_vl_lookup: calloc failed.");
204 user_obj->next = NULL;
206 user_obj->user_obj = obj->cb_user_class(ds, vl, user_class->user_class);
207 if (user_obj->user_obj == NULL) {
209 WARNING("utils_vl_lookup: User-provided constructor failed.");
213 #define COPY_FIELD(field, group_mask) \
215 if (user_class->match.field.is_regex && \
216 ((user_class->match.group_by & group_mask) == 0)) \
217 sstrncpy(user_obj->ident.field, "/.*/", sizeof(user_obj->ident.field)); \
219 sstrncpy(user_obj->ident.field, vl->field, \
220 sizeof(user_obj->ident.field)); \
223 COPY_FIELD(host, LU_GROUP_BY_HOST);
224 COPY_FIELD(plugin, LU_GROUP_BY_PLUGIN);
225 COPY_FIELD(plugin_instance, LU_GROUP_BY_PLUGIN_INSTANCE);
227 COPY_FIELD(type_instance, LU_GROUP_BY_TYPE_INSTANCE);
231 if (user_class->user_obj_list == NULL) {
232 user_class->user_obj_list = user_obj;
234 user_obj_t *last = user_class->user_obj_list;
235 while (last->next != NULL)
237 last->next = user_obj;
241 } /* }}} void *lu_create_user_obj */
243 /* user_class->lock must be held when calling this function */
244 static user_obj_t *lu_find_user_obj(user_class_t *user_class, /* {{{ */
245 value_list_t const *vl) {
248 for (ptr = user_class->user_obj_list; ptr != NULL; ptr = ptr->next) {
249 if (user_class->match.host.is_regex &&
250 (user_class->match.group_by & LU_GROUP_BY_HOST) &&
251 (strcmp(vl->host, ptr->ident.host) != 0))
253 if (user_class->match.plugin.is_regex &&
254 (user_class->match.group_by & LU_GROUP_BY_PLUGIN) &&
255 (strcmp(vl->plugin, ptr->ident.plugin) != 0))
257 if (user_class->match.plugin_instance.is_regex &&
258 (user_class->match.group_by & LU_GROUP_BY_PLUGIN_INSTANCE) &&
259 (strcmp(vl->plugin_instance, ptr->ident.plugin_instance) != 0))
261 if (user_class->match.type_instance.is_regex &&
262 (user_class->match.group_by & LU_GROUP_BY_TYPE_INSTANCE) &&
263 (strcmp(vl->type_instance, ptr->ident.type_instance) != 0))
270 } /* }}} user_obj_t *lu_find_user_obj */
272 static int lu_handle_user_class(lookup_t *obj, /* {{{ */
273 data_set_t const *ds, value_list_t const *vl,
274 user_class_t *user_class) {
275 user_obj_t *user_obj;
278 assert(strcmp(vl->type, user_class->match.type.str) == 0);
279 assert(user_class->match.plugin.is_regex ||
280 (strcmp(vl->plugin, user_class->match.plugin.str)) == 0);
282 if (!lu_part_matches(&user_class->match.type_instance, vl->type_instance) ||
283 !lu_part_matches(&user_class->match.plugin_instance,
284 vl->plugin_instance) ||
285 !lu_part_matches(&user_class->match.plugin, vl->plugin) ||
286 !lu_part_matches(&user_class->match.host, vl->host))
289 pthread_mutex_lock(&user_class->lock);
290 user_obj = lu_find_user_obj(user_class, vl);
291 if (user_obj == NULL) {
292 /* call lookup_class_callback_t() and insert into the list of user objects.
294 user_obj = lu_create_user_obj(obj, ds, vl, user_class);
295 if (user_obj == NULL) {
296 pthread_mutex_unlock(&user_class->lock);
300 pthread_mutex_unlock(&user_class->lock);
302 status = obj->cb_user_obj(ds, vl, user_class->user_class, user_obj->user_obj);
304 ERROR("utils_vl_lookup: The user object callback failed with status %i.",
306 /* Returning a negative value means: abort! */
314 } /* }}} int lu_handle_user_class */
316 static int lu_handle_user_class_list(lookup_t *obj, /* {{{ */
317 data_set_t const *ds,
318 value_list_t const *vl,
319 user_class_list_t *user_class_list) {
320 user_class_list_t *ptr;
323 for (ptr = user_class_list; ptr != NULL; ptr = ptr->next) {
326 status = lu_handle_user_class(obj, ds, vl, &ptr->entry);
329 else if (status == 0)
334 } /* }}} int lu_handle_user_class_list */
336 static by_type_entry_t *lu_search_by_type(lookup_t *obj, /* {{{ */
338 bool allocate_if_missing) {
339 by_type_entry_t *by_type;
343 status = c_avl_get(obj->by_type_tree, type, (void *)&by_type);
347 if (!allocate_if_missing)
350 type_copy = strdup(type);
351 if (type_copy == NULL) {
352 ERROR("utils_vl_lookup: strdup failed.");
356 by_type = calloc(1, sizeof(*by_type));
357 if (by_type == NULL) {
358 ERROR("utils_vl_lookup: calloc failed.");
362 by_type->wildcard_plugin_list = NULL;
364 by_type->by_plugin_tree =
365 c_avl_create((int (*)(const void *, const void *))strcmp);
366 if (by_type->by_plugin_tree == NULL) {
367 ERROR("utils_vl_lookup: c_avl_create failed.");
373 status = c_avl_insert(obj->by_type_tree,
374 /* key = */ type_copy, /* value = */ by_type);
375 assert(status <= 0); /* >0 => entry exists => race condition. */
377 ERROR("utils_vl_lookup: c_avl_insert failed.");
378 c_avl_destroy(by_type->by_plugin_tree);
385 } /* }}} by_type_entry_t *lu_search_by_type */
387 static int lu_add_by_plugin(by_type_entry_t *by_type, /* {{{ */
388 user_class_list_t *user_class_list) {
389 user_class_list_t *ptr = NULL;
390 identifier_match_t const *match = &user_class_list->entry.match;
392 /* Lookup user_class_list from the per-plugin structure. If this is the first
393 * user_class to be added, the block returns immediately. Otherwise they will
394 * set "ptr" to non-NULL. */
395 if (match->plugin.is_regex) {
396 if (by_type->wildcard_plugin_list == NULL) {
397 by_type->wildcard_plugin_list = user_class_list;
401 ptr = by_type->wildcard_plugin_list;
402 } /* if (plugin is wildcard) */
403 else /* (plugin is not wildcard) */
408 c_avl_get(by_type->by_plugin_tree, match->plugin.str, (void *)&ptr);
410 if (status != 0) /* plugin not yet in tree */
412 char *plugin_copy = strdup(match->plugin.str);
414 if (plugin_copy == NULL) {
415 ERROR("utils_vl_lookup: strdup failed.");
416 sfree(user_class_list);
421 c_avl_insert(by_type->by_plugin_tree, plugin_copy, user_class_list);
423 ERROR("utils_vl_lookup: c_avl_insert(\"%s\") failed with status %i.",
424 plugin_copy, status);
426 sfree(user_class_list);
431 } /* if (plugin not yet in tree) */
432 } /* if (plugin is not wildcard) */
436 while (ptr->next != NULL)
438 ptr->next = user_class_list;
441 } /* }}} int lu_add_by_plugin */
443 static void lu_destroy_user_obj(lookup_t *obj, /* {{{ */
444 user_obj_t *user_obj) {
445 while (user_obj != NULL) {
446 user_obj_t *next = user_obj->next;
448 if (obj->cb_free_obj != NULL)
449 obj->cb_free_obj(user_obj->user_obj);
450 user_obj->user_obj = NULL;
455 } /* }}} void lu_destroy_user_obj */
457 static void lu_destroy_user_class_list(lookup_t *obj, /* {{{ */
458 user_class_list_t *user_class_list) {
459 while (user_class_list != NULL) {
460 user_class_list_t *next = user_class_list->next;
462 if (obj->cb_free_class != NULL)
463 obj->cb_free_class(user_class_list->entry.user_class);
464 user_class_list->entry.user_class = NULL;
466 #define CLEAR_FIELD(field) \
468 if (user_class_list->entry.match.field.is_regex) { \
469 regfree(&user_class_list->entry.match.field.regex); \
470 user_class_list->entry.match.field.is_regex = false; \
476 CLEAR_FIELD(plugin_instance);
478 CLEAR_FIELD(type_instance);
482 lu_destroy_user_obj(obj, user_class_list->entry.user_obj_list);
483 user_class_list->entry.user_obj_list = NULL;
484 pthread_mutex_destroy(&user_class_list->entry.lock);
486 sfree(user_class_list);
487 user_class_list = next;
489 } /* }}} void lu_destroy_user_class_list */
491 static void lu_destroy_by_type(lookup_t *obj, /* {{{ */
492 by_type_entry_t *by_type) {
496 user_class_list_t *user_class_list = NULL;
499 status = c_avl_pick(by_type->by_plugin_tree, (void *)&plugin,
500 (void *)&user_class_list);
504 DEBUG("utils_vl_lookup: lu_destroy_by_type: Destroying plugin \"%s\".",
507 lu_destroy_user_class_list(obj, user_class_list);
510 c_avl_destroy(by_type->by_plugin_tree);
511 by_type->by_plugin_tree = NULL;
513 lu_destroy_user_class_list(obj, by_type->wildcard_plugin_list);
514 by_type->wildcard_plugin_list = NULL;
517 } /* }}} int lu_destroy_by_type */
522 lookup_t *lookup_create(lookup_class_callback_t cb_user_class, /* {{{ */
523 lookup_obj_callback_t cb_user_obj,
524 lookup_free_class_callback_t cb_free_class,
525 lookup_free_obj_callback_t cb_free_obj) {
526 lookup_t *obj = calloc(1, sizeof(*obj));
528 ERROR("utils_vl_lookup: calloc failed.");
532 obj->by_type_tree = c_avl_create((int (*)(const void *, const void *))strcmp);
533 if (obj->by_type_tree == NULL) {
534 ERROR("utils_vl_lookup: c_avl_create failed.");
539 obj->cb_user_class = cb_user_class;
540 obj->cb_user_obj = cb_user_obj;
541 obj->cb_free_class = cb_free_class;
542 obj->cb_free_obj = cb_free_obj;
545 } /* }}} lookup_t *lookup_create */
547 void lookup_destroy(lookup_t *obj) /* {{{ */
556 by_type_entry_t *by_type = NULL;
558 status = c_avl_pick(obj->by_type_tree, (void *)&type, (void *)&by_type);
562 DEBUG("utils_vl_lookup: lookup_destroy: Destroying type \"%s\".", type);
564 lu_destroy_by_type(obj, by_type);
567 c_avl_destroy(obj->by_type_tree);
568 obj->by_type_tree = NULL;
571 } /* }}} void lookup_destroy */
573 int lookup_add(lookup_t *obj, /* {{{ */
574 lookup_identifier_t const *ident, unsigned int group_by,
576 by_type_entry_t *by_type = NULL;
577 user_class_list_t *user_class_obj;
579 by_type = lu_search_by_type(obj, ident->type, /* allocate = */ true);
583 user_class_obj = calloc(1, sizeof(*user_class_obj));
584 if (user_class_obj == NULL) {
585 ERROR("utils_vl_lookup: calloc failed.");
588 pthread_mutex_init(&user_class_obj->entry.lock, /* attr = */ NULL);
589 user_class_obj->entry.user_class = user_class;
590 lu_copy_ident_to_match(&user_class_obj->entry.match, ident, group_by);
591 user_class_obj->entry.user_obj_list = NULL;
592 user_class_obj->next = NULL;
594 return lu_add_by_plugin(by_type, user_class_obj);
595 } /* }}} int lookup_add */
597 /* returns the number of successful calls to the callback function */
598 int lookup_search(lookup_t *obj, /* {{{ */
599 data_set_t const *ds, value_list_t const *vl) {
600 by_type_entry_t *by_type = NULL;
601 user_class_list_t *user_class_list = NULL;
605 if ((obj == NULL) || (ds == NULL) || (vl == NULL))
608 by_type = lu_search_by_type(obj, vl->type, /* allocate = */ false);
613 c_avl_get(by_type->by_plugin_tree, vl->plugin, (void *)&user_class_list);
615 status = lu_handle_user_class_list(obj, ds, vl, user_class_list);
621 if (by_type->wildcard_plugin_list != NULL) {
623 lu_handle_user_class_list(obj, ds, vl, by_type->wildcard_plugin_list);
630 } /* }}} lookup_search */