1 /*
2 * SysDB - src/core/plugin.c
3 * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 #if HAVE_CONFIG_H
29 # include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "sysdb.h"
33 #include "core/plugin.h"
34 #include "core/time.h"
35 #include "utils/error.h"
36 #include "utils/llist.h"
37 #include "utils/strbuf.h"
39 #include <assert.h>
41 #include <errno.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <strings.h>
47 #include <unistd.h>
49 #include <ltdl.h>
51 #include <pthread.h>
53 /*
54 * private data types
55 */
57 struct sdb_plugin_info {
58 char *plugin_name;
59 char *filename;
61 /* public attributes */
62 char *name;
64 char *description;
65 char *copyright;
66 char *license;
68 int version;
69 int plugin_version;
70 };
71 #define SDB_PLUGIN_INFO_INIT { \
72 /* plugin_name */ NULL, /* filename */ NULL, \
73 /* name */ NULL, /* desc */ NULL, \
74 /* copyright */ NULL, /* license */ NULL, \
75 /* version */ -1, /* plugin_version */ -1 }
76 #define INFO_GET(i, attr) \
77 ((i)->attr ? (i)->attr : #attr" not set")
79 typedef struct {
80 sdb_object_t super;
81 sdb_plugin_ctx_t public;
83 sdb_plugin_info_t info;
85 /* The usage count differs from the object's ref count
86 * in that it provides higher level information about how
87 * the plugin is in use. */
88 size_t use_cnt;
89 } ctx_t;
90 #define CTX_INIT { SDB_OBJECT_INIT, \
91 SDB_PLUGIN_CTX_INIT, SDB_PLUGIN_INFO_INIT, 0 }
93 #define CTX(obj) ((ctx_t *)(obj))
95 typedef struct {
96 sdb_object_t super;
97 void *cb_callback;
98 sdb_object_t *cb_user_data;
99 ctx_t *cb_ctx;
100 } sdb_plugin_cb_t;
101 #define SDB_PLUGIN_CB_INIT { SDB_OBJECT_INIT, \
102 /* callback = */ NULL, /* user_data = */ NULL, \
103 SDB_PLUGIN_CTX_INIT }
105 typedef struct {
106 sdb_plugin_cb_t super;
107 #define ccb_callback super.cb_callback
108 #define ccb_user_data super.cb_user_data
109 #define ccb_ctx super.cb_ctx
110 sdb_time_t ccb_interval;
111 sdb_time_t ccb_next_update;
112 } sdb_plugin_collector_cb_t;
114 #define SDB_PLUGIN_CB(obj) ((sdb_plugin_cb_t *)(obj))
115 #define SDB_CONST_PLUGIN_CB(obj) ((const sdb_plugin_cb_t *)(obj))
116 #define SDB_PLUGIN_CCB(obj) ((sdb_plugin_collector_cb_t *)(obj))
117 #define SDB_CONST_PLUGIN_CCB(obj) ((const sdb_plugin_collector_cb_t *)(obj))
119 /*
120 * private variables
121 */
123 static sdb_plugin_ctx_t plugin_default_ctx = SDB_PLUGIN_CTX_INIT;
124 static sdb_plugin_info_t plugin_default_info = SDB_PLUGIN_INFO_INIT;
126 static pthread_key_t plugin_ctx_key;
127 static _Bool plugin_ctx_key_initialized = 0;
129 /* a list of the plugin contexts of all registered plugins */
130 static sdb_llist_t *all_plugins = NULL;
132 static sdb_llist_t *config_list = NULL;
133 static sdb_llist_t *init_list = NULL;
134 static sdb_llist_t *collector_list = NULL;
135 static sdb_llist_t *cname_list = NULL;
136 static sdb_llist_t *shutdown_list = NULL;
137 static sdb_llist_t *log_list = NULL;
139 /*
140 * private helper functions
141 */
143 static void
144 plugin_info_clear(sdb_plugin_info_t *info)
145 {
146 sdb_plugin_info_t empty_info = SDB_PLUGIN_INFO_INIT;
147 if (! info)
148 return;
150 if (info->plugin_name)
151 free(info->plugin_name);
152 if (info->filename)
153 free(info->filename);
155 if (info->name)
156 free(info->name);
157 if (info->description)
158 free(info->description);
159 if (info->copyright)
160 free(info->copyright);
161 if (info->license)
162 free(info->license);
164 *info = empty_info;
165 } /* plugin_info_clear */
167 static void
168 ctx_key_init(void)
169 {
170 if (plugin_ctx_key_initialized)
171 return;
173 pthread_key_create(&plugin_ctx_key, /* destructor */ NULL);
174 plugin_ctx_key_initialized = 1;
175 } /* ctx_key_init */
177 static int
178 plugin_cmp_next_update(const sdb_object_t *a, const sdb_object_t *b)
179 {
180 const sdb_plugin_collector_cb_t *ccb1
181 = (const sdb_plugin_collector_cb_t *)a;
182 const sdb_plugin_collector_cb_t *ccb2
183 = (const sdb_plugin_collector_cb_t *)b;
185 assert(ccb1 && ccb2);
187 return (ccb1->ccb_next_update > ccb2->ccb_next_update)
188 ? 1 : (ccb1->ccb_next_update < ccb2->ccb_next_update)
189 ? -1 : 0;
190 } /* plugin_cmp_next_update */
192 static int
193 plugin_lookup_by_name(const sdb_object_t *obj, const void *id)
194 {
195 const sdb_plugin_cb_t *cb = SDB_CONST_PLUGIN_CB(obj);
196 const char *name = id;
198 assert(cb && id && cb->cb_ctx);
199 if (!strcasecmp(cb->cb_ctx->info.plugin_name, name))
200 return 0;
201 return 1;
202 } /* plugin_lookup_by_name */
204 static void
205 plugin_unregister_by_name(const char *plugin_name)
206 {
207 size_t i;
209 struct {
210 const char *type;
211 sdb_llist_t *list;
212 } all_lists[] = {
213 { "config", config_list },
214 { "init", init_list },
215 { "collector", collector_list },
216 { "cname", cname_list },
217 { "shutdown", shutdown_list },
218 { "log", log_list },
219 };
221 for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
222 const char *type = all_lists[i].type;
223 sdb_llist_t *list = all_lists[i].list;
225 while (1) {
226 sdb_object_t *obj = sdb_llist_remove(list,
227 plugin_lookup_by_name, plugin_name);
228 sdb_plugin_cb_t *cb = SDB_PLUGIN_CB(obj);
230 if (! obj)
231 break;
233 sdb_log(SDB_LOG_INFO, "core: Unregistering "
234 "%s callback '%s' (module %s)", type, obj->name,
235 cb->cb_ctx->info.plugin_name);
236 sdb_object_deref(obj);
237 }
238 }
239 } /* plugin_unregister_by_name */
241 /*
242 * private types
243 */
245 static int
246 ctx_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
247 {
248 ctx_t *ctx = CTX(obj);
250 assert(ctx);
252 ctx->public = plugin_default_ctx;
253 ctx->info = plugin_default_info;
254 ctx->use_cnt = 1;
255 return 0;
256 } /* ctx_init */
258 static void
259 ctx_destroy(sdb_object_t *obj)
260 {
261 ctx_t *ctx = CTX(obj);
262 plugin_info_clear(&ctx->info);
263 } /* ctx_destroy */
265 static sdb_type_t ctx_type = {
266 sizeof(ctx_t),
268 ctx_init,
269 ctx_destroy
270 };
272 static ctx_t *
273 ctx_create(const char *name)
274 {
275 ctx_t *ctx;
277 ctx = CTX(sdb_object_create(name, ctx_type));
278 if (! ctx)
279 return NULL;
281 if (! plugin_ctx_key_initialized)
282 ctx_key_init();
283 pthread_setspecific(plugin_ctx_key, ctx);
284 return ctx;
285 } /* ctx_create */
287 static ctx_t *
288 ctx_get(void)
289 {
290 if (! plugin_ctx_key_initialized)
291 ctx_key_init();
292 return pthread_getspecific(plugin_ctx_key);
293 } /* ctx_get */
295 static ctx_t *
296 ctx_set(ctx_t *new)
297 {
298 ctx_t *old;
300 if (! plugin_ctx_key_initialized)
301 ctx_key_init();
303 old = pthread_getspecific(plugin_ctx_key);
304 pthread_setspecific(plugin_ctx_key, new);
305 return old;
306 } /* ctx_set */
308 static int
309 plugin_cb_init(sdb_object_t *obj, va_list ap)
310 {
311 sdb_llist_t **list = va_arg(ap, sdb_llist_t **);
312 const char *type = va_arg(ap, const char *);
313 void *callback = va_arg(ap, void *);
314 sdb_object_t *ud = va_arg(ap, sdb_object_t *);
316 assert(list);
317 assert(type);
318 assert(obj);
320 if (sdb_llist_search_by_name(*list, obj->name)) {
321 sdb_log(SDB_LOG_WARNING, "core: %s callback '%s' "
322 "has already been registered. Ignoring newly "
323 "registered version.", type, obj->name);
324 return -1;
325 }
327 SDB_PLUGIN_CB(obj)->cb_callback = callback;
328 SDB_PLUGIN_CB(obj)->cb_ctx = ctx_get();
329 sdb_object_ref(SDB_OBJ(SDB_PLUGIN_CB(obj)->cb_ctx));
331 sdb_object_ref(ud);
332 SDB_PLUGIN_CB(obj)->cb_user_data = ud;
333 return 0;
334 } /* plugin_cb_init */
336 static void
337 plugin_cb_destroy(sdb_object_t *obj)
338 {
339 assert(obj);
340 sdb_object_deref(SDB_PLUGIN_CB(obj)->cb_user_data);
341 sdb_object_deref(SDB_OBJ(SDB_PLUGIN_CB(obj)->cb_ctx));
342 } /* plugin_cb_destroy */
344 static sdb_type_t sdb_plugin_cb_type = {
345 sizeof(sdb_plugin_cb_t),
347 plugin_cb_init,
348 plugin_cb_destroy
349 };
351 static sdb_type_t sdb_plugin_collector_cb_type = {
352 sizeof(sdb_plugin_collector_cb_t),
354 plugin_cb_init,
355 plugin_cb_destroy
356 };
358 static int
359 plugin_add_callback(sdb_llist_t **list, const char *type,
360 const char *name, void *callback, sdb_object_t *user_data)
361 {
362 sdb_object_t *obj;
364 if ((! name) || (! callback))
365 return -1;
367 assert(list);
369 if (! *list)
370 *list = sdb_llist_create();
371 if (! *list)
372 return -1;
374 obj = sdb_object_create(name, sdb_plugin_cb_type,
375 list, type, callback, user_data);
376 if (! obj)
377 return -1;
379 if (sdb_llist_append(*list, obj)) {
380 sdb_object_deref(obj);
381 return -1;
382 }
384 /* pass control to the list */
385 sdb_object_deref(obj);
387 sdb_log(SDB_LOG_INFO, "core: Registered %s callback '%s'.",
388 type, name);
389 return 0;
390 } /* plugin_add_callback */
392 /*
393 * public API
394 */
396 int
397 sdb_plugin_load(const char *name, const sdb_plugin_ctx_t *plugin_ctx)
398 {
399 char base_name[name ? strlen(name) + 1 : 1];
400 const char *name_ptr;
401 char *tmp;
403 char filename[1024];
404 ctx_t *ctx;
406 lt_dlhandle lh;
408 int (*mod_init)(sdb_plugin_info_t *);
409 int status;
411 if ((! name) || (! *name))
412 return -1;
414 base_name[0] = '\0';
415 name_ptr = name;
417 while ((tmp = strstr(name_ptr, "::"))) {
418 strncat(base_name, name_ptr, (size_t)(tmp - name_ptr));
419 strcat(base_name, "/");
420 name_ptr = tmp + strlen("::");
421 }
422 strcat(base_name, name_ptr);
424 ctx = CTX(sdb_llist_search_by_name(all_plugins, base_name));
425 if (ctx) {
426 /* plugin already loaded */
427 ++ctx->use_cnt;
428 return 0;
429 }
431 snprintf(filename, sizeof(filename), "%s/%s.so",
432 PKGLIBDIR, base_name);
433 filename[sizeof(filename) - 1] = '\0';
435 if (access(filename, R_OK)) {
436 char errbuf[1024];
437 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s' (%s): %s",
438 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
439 return -1;
440 }
442 lt_dlinit();
443 lt_dlerror();
445 lh = lt_dlopen(filename);
446 if (! lh) {
447 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': %s"
448 "The most common cause for this problem are missing "
449 "dependencies.\n", name, lt_dlerror());
450 return -1;
451 }
453 if (ctx_get())
454 sdb_log(SDB_LOG_WARNING, "core: Discarding old plugin context");
456 ctx = ctx_create(name);
457 if (! ctx) {
458 sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin context");
459 return -1;
460 }
462 ctx->info.plugin_name = strdup(name);
463 ctx->info.filename = strdup(filename);
465 if (plugin_ctx)
466 ctx->public = *plugin_ctx;
468 mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
469 if (! mod_init) {
470 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
471 "could not find symbol 'sdb_module_init'", name);
472 sdb_object_deref(SDB_OBJ(ctx));
473 return -1;
474 }
476 status = mod_init(&ctx->info);
477 if (status) {
478 sdb_log(SDB_LOG_ERR, "core: Failed to initialize "
479 "module '%s'", name);
480 plugin_unregister_by_name(ctx->info.plugin_name);
481 sdb_object_deref(SDB_OBJ(ctx));
482 return -1;
483 }
485 /* compare minor version */
486 if ((ctx->info.version < 0)
487 || ((int)(ctx->info.version / 100) != (int)(SDB_VERSION / 100)))
488 sdb_log(SDB_LOG_WARNING, "core: WARNING: version of "
489 "plugin '%s' (%i.%i.%i) does not match our version "
490 "(%i.%i.%i); this might cause problems",
491 name, SDB_VERSION_DECODE(ctx->info.version),
492 SDB_VERSION_DECODE(SDB_VERSION));
494 if (! all_plugins) {
495 if (! (all_plugins = sdb_llist_create())) {
496 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
497 "internal error while creating linked list", name);
498 plugin_unregister_by_name(ctx->info.plugin_name);
499 sdb_object_deref(SDB_OBJ(ctx));
500 return -1;
501 }
502 }
504 sdb_llist_append(all_plugins, SDB_OBJ(ctx));
506 sdb_log(SDB_LOG_INFO, "core: Successfully loaded "
507 "plugin '%s' v%i (%s)\n\t%s\n\tLicense: %s",
508 INFO_GET(&ctx->info, name), ctx->info.plugin_version,
509 INFO_GET(&ctx->info, description),
510 INFO_GET(&ctx->info, copyright),
511 INFO_GET(&ctx->info, license));
513 /* any registered callbacks took ownership of the context */
514 sdb_object_deref(SDB_OBJ(ctx));
516 /* reset */
517 ctx_set(NULL);
518 return 0;
519 } /* sdb_plugin_load */
521 int
522 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
523 {
524 va_list ap;
526 if (! info)
527 return -1;
529 va_start(ap, type);
531 switch (type) {
532 case SDB_PLUGIN_INFO_NAME:
533 {
534 char *name = va_arg(ap, char *);
535 if (name) {
536 if (info->name)
537 free(info->name);
538 info->name = strdup(name);
539 }
540 }
541 break;
542 case SDB_PLUGIN_INFO_DESC:
543 {
544 char *desc = va_arg(ap, char *);
545 if (desc) {
546 if (info->description)
547 free(info->description);
548 info->description = strdup(desc);
549 }
550 }
551 break;
552 case SDB_PLUGIN_INFO_COPYRIGHT:
553 {
554 char *copyright = va_arg(ap, char *);
555 if (copyright)
556 info->copyright = strdup(copyright);
557 }
558 break;
559 case SDB_PLUGIN_INFO_LICENSE:
560 {
561 char *license = va_arg(ap, char *);
562 if (license) {
563 if (info->license)
564 free(info->license);
565 info->license = strdup(license);
566 }
567 }
568 break;
569 case SDB_PLUGIN_INFO_VERSION:
570 {
571 int version = va_arg(ap, int);
572 info->version = version;
573 }
574 break;
575 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
576 {
577 int version = va_arg(ap, int);
578 info->plugin_version = version;
579 }
580 break;
581 default:
582 va_end(ap);
583 return -1;
584 }
586 va_end(ap);
587 return 0;
588 } /* sdb_plugin_set_info */
590 int
591 sdb_plugin_register_config(const char *name, sdb_plugin_config_cb callback)
592 {
593 return plugin_add_callback(&config_list, "init", name,
594 (void *)callback, NULL);
595 } /* sdb_plugin_register_config */
597 int
598 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
599 sdb_object_t *user_data)
600 {
601 return plugin_add_callback(&init_list, "init", name,
602 (void *)callback, user_data);
603 } /* sdb_plugin_register_init */
605 int
606 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
607 sdb_object_t *user_data)
608 {
609 return plugin_add_callback(&shutdown_list, "shutdown", name,
610 (void *)callback, user_data);
611 } /* sdb_plugin_register_shutdown */
613 int
614 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
615 sdb_object_t *user_data)
616 {
617 return plugin_add_callback(&log_list, "log", name, (void *)callback,
618 user_data);
619 } /* sdb_plugin_register_log */
621 int
622 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
623 sdb_object_t *user_data)
624 {
625 return plugin_add_callback(&cname_list, "cname", name, (void *)callback,
626 user_data);
627 } /* sdb_plugin_register_cname */
629 int
630 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
631 const sdb_time_t *interval, sdb_object_t *user_data)
632 {
633 sdb_object_t *obj;
635 if ((! name) || (! callback))
636 return -1;
638 if (! collector_list)
639 collector_list = sdb_llist_create();
640 if (! collector_list)
641 return -1;
643 obj = sdb_object_create(name, sdb_plugin_collector_cb_type,
644 &collector_list, "collector", callback, user_data);
645 if (! obj)
646 return -1;
648 if (interval)
649 SDB_PLUGIN_CCB(obj)->ccb_interval = *interval;
650 else {
651 sdb_time_t tmp = sdb_plugin_get_ctx().interval;
653 if (tmp > 0)
654 SDB_PLUGIN_CCB(obj)->ccb_interval = tmp;
655 else
656 SDB_PLUGIN_CCB(obj)->ccb_interval = 0;
657 }
659 if (! (SDB_PLUGIN_CCB(obj)->ccb_next_update = sdb_gettime())) {
660 char errbuf[1024];
661 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
662 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
663 sdb_object_deref(obj);
664 return -1;
665 }
667 if (sdb_llist_insert_sorted(collector_list, obj,
668 plugin_cmp_next_update)) {
669 sdb_object_deref(obj);
670 return -1;
671 }
673 /* pass control to the list */
674 sdb_object_deref(obj);
676 sdb_log(SDB_LOG_INFO, "core: Registered collector callback '%s' "
677 "(interval = %.3fs).", name,
678 SDB_TIME_TO_DOUBLE(SDB_PLUGIN_CCB(obj)->ccb_interval));
679 return 0;
680 } /* sdb_plugin_register_collector */
682 sdb_plugin_ctx_t
683 sdb_plugin_get_ctx(void)
684 {
685 ctx_t *c;
687 c = ctx_get();
688 if (! c) {
689 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid read access to plugin "
690 "context outside a plugin");
691 return plugin_default_ctx;
692 }
693 return c->public;
694 } /* sdb_plugin_get_ctx */
696 int
697 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx, sdb_plugin_ctx_t *old)
698 {
699 ctx_t *c;
701 c = ctx_get();
702 if (! c) {
703 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid write access to plugin "
704 "context outside a plugin");
705 return -1;
706 }
708 if (old)
709 *old = c->public;
710 c->public = ctx;
711 return 0;
712 } /* sdb_plugin_set_ctx */
714 int
715 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
716 {
717 sdb_plugin_cb_t *plugin;
718 sdb_plugin_config_cb callback;
720 ctx_t *old_ctx;
722 int status;
724 if ((! name) || (! ci))
725 return -1;
727 plugin = SDB_PLUGIN_CB(sdb_llist_search_by_name(config_list, name));
728 if (! plugin) {
729 ctx_t *ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
730 if (! ctx)
731 sdb_log(SDB_LOG_ERR, "core: Plugin '%s' not loaded.", name);
732 else
733 sdb_log(SDB_LOG_ERR, "core: Plugin '%s' did not register "
734 "a config callback.", name);
735 errno = ENOENT;
736 return -1;
737 }
739 old_ctx = ctx_set(plugin->cb_ctx);
740 callback = (sdb_plugin_config_cb)plugin->cb_callback;
741 status = callback(ci);
742 ctx_set(old_ctx);
743 return status;
744 } /* sdb_plugin_configure */
746 int
747 sdb_plugin_init_all(void)
748 {
749 sdb_llist_iter_t *iter;
750 int ret = 0;
752 iter = sdb_llist_get_iter(init_list);
753 while (sdb_llist_iter_has_next(iter)) {
754 sdb_plugin_cb_t *cb;
755 sdb_plugin_init_cb callback;
756 ctx_t *old_ctx;
758 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
759 assert(obj);
760 cb = SDB_PLUGIN_CB(obj);
762 callback = (sdb_plugin_init_cb)cb->cb_callback;
764 old_ctx = ctx_set(cb->cb_ctx);
765 if (callback(cb->cb_user_data)) {
766 sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin "
767 "'%s'. Unregistering all callbacks.", obj->name);
768 plugin_unregister_by_name(cb->cb_ctx->info.plugin_name);
769 ++ret;
770 }
771 ctx_set(old_ctx);
772 }
773 sdb_llist_iter_destroy(iter);
774 return ret;
775 } /* sdb_plugin_init_all */
777 int
778 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
779 {
780 if (! collector_list) {
781 sdb_log(SDB_LOG_WARNING, "core: No collectors registered. "
782 "Quiting main loop.");
783 return -1;
784 }
786 if (! loop)
787 return -1;
789 while (loop->do_loop) {
790 sdb_plugin_collector_cb callback;
791 ctx_t *old_ctx;
793 sdb_time_t interval, now;
795 sdb_object_t *obj = sdb_llist_shift(collector_list);
796 if (! obj)
797 return -1;
799 callback = (sdb_plugin_collector_cb)SDB_PLUGIN_CCB(obj)->ccb_callback;
801 if (! (now = sdb_gettime())) {
802 char errbuf[1024];
803 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
804 "time in collector main loop: %s",
805 sdb_strerror(errno, errbuf, sizeof(errbuf)));
806 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
807 }
809 if (now < SDB_PLUGIN_CCB(obj)->ccb_next_update) {
810 interval = SDB_PLUGIN_CCB(obj)->ccb_next_update - now;
812 errno = 0;
813 while (loop->do_loop && sdb_sleep(interval, &interval)) {
814 if (errno != EINTR) {
815 char errbuf[1024];
816 sdb_log(SDB_LOG_ERR, "core: Failed to sleep "
817 "in collector main loop: %s",
818 sdb_strerror(errno, errbuf, sizeof(errbuf)));
819 return -1;
820 }
821 errno = 0;
822 }
824 if (! loop->do_loop)
825 return 0;
826 }
828 old_ctx = ctx_set(SDB_PLUGIN_CCB(obj)->ccb_ctx);
829 if (callback(SDB_PLUGIN_CCB(obj)->ccb_user_data)) {
830 /* XXX */
831 }
832 ctx_set(old_ctx);
834 interval = SDB_PLUGIN_CCB(obj)->ccb_interval;
835 if (! interval)
836 interval = loop->default_interval;
837 if (! interval) {
838 sdb_log(SDB_LOG_WARNING, "core: No interval configured "
839 "for plugin '%s'; skipping any further "
840 "iterations.", obj->name);
841 sdb_object_deref(obj);
842 continue;
843 }
845 SDB_PLUGIN_CCB(obj)->ccb_next_update += interval;
847 if (! (now = sdb_gettime())) {
848 char errbuf[1024];
849 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
850 "time in collector main loop: %s",
851 sdb_strerror(errno, errbuf, sizeof(errbuf)));
852 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
853 }
855 if (now > SDB_PLUGIN_CCB(obj)->ccb_next_update) {
856 sdb_log(SDB_LOG_WARNING, "core: Plugin '%s' took too "
857 "long; skipping iterations to keep up.",
858 obj->name);
859 SDB_PLUGIN_CCB(obj)->ccb_next_update = now;
860 }
862 if (sdb_llist_insert_sorted(collector_list, obj,
863 plugin_cmp_next_update)) {
864 sdb_log(SDB_LOG_ERR, "core: Failed to re-insert "
865 "plugin '%s' into collector list. Unable to further "
866 "use the plugin.",
867 obj->name);
868 sdb_object_deref(obj);
869 return -1;
870 }
872 /* pass control back to the list */
873 sdb_object_deref(obj);
874 }
875 return 0;
876 } /* sdb_plugin_read_loop */
878 char *
879 sdb_plugin_cname(char *hostname)
880 {
881 sdb_llist_iter_t *iter;
883 if (! hostname)
884 return NULL;
886 if (! cname_list)
887 return hostname;
889 iter = sdb_llist_get_iter(cname_list);
890 while (sdb_llist_iter_has_next(iter)) {
891 sdb_plugin_cname_cb callback;
892 char *cname;
894 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
895 assert(obj);
897 callback = (sdb_plugin_cname_cb)SDB_PLUGIN_CB(obj)->cb_callback;
898 cname = callback(hostname, SDB_PLUGIN_CB(obj)->cb_user_data);
899 if (cname) {
900 free(hostname);
901 hostname = cname;
902 }
903 /* else: don't change hostname */
904 }
905 sdb_llist_iter_destroy(iter);
906 return hostname;
907 } /* sdb_plugin_cname */
909 int
910 sdb_plugin_log(int prio, const char *msg)
911 {
912 sdb_llist_iter_t *iter;
913 int ret = -1;
915 if (! msg)
916 return 0;
918 if (! log_list)
919 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
921 iter = sdb_llist_get_iter(log_list);
922 while (sdb_llist_iter_has_next(iter)) {
923 sdb_plugin_log_cb callback;
924 int tmp;
926 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
927 assert(obj);
929 callback = (sdb_plugin_log_cb)SDB_PLUGIN_CB(obj)->cb_callback;
930 tmp = callback(prio, msg, SDB_PLUGIN_CB(obj)->cb_user_data);
931 if (tmp > ret)
932 ret = tmp;
933 }
934 sdb_llist_iter_destroy(iter);
935 return ret;
936 } /* sdb_plugin_log */
938 int
939 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
940 {
941 sdb_strbuf_t *buf;
942 int ret;
944 if (! fmt)
945 return 0;
947 buf = sdb_strbuf_create(64);
948 if (! buf) {
949 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
950 ret += vfprintf(stderr, fmt, ap);
951 return ret;
952 }
954 if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
955 sdb_strbuf_destroy(buf);
956 return -1;
957 }
959 ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
960 sdb_strbuf_destroy(buf);
961 return ret;
962 } /* sdb_plugin_vlogf */
964 int
965 sdb_plugin_logf(int prio, const char *fmt, ...)
966 {
967 va_list ap;
968 int ret;
970 if (! fmt)
971 return 0;
973 va_start(ap, fmt);
974 ret = sdb_plugin_vlogf(prio, fmt, ap);
975 va_end(ap);
976 return ret;
977 } /* sdb_plugin_logf */
979 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */