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;
84 lt_dlhandle handle;
86 /* The usage count differs from the object's ref count
87 * in that it provides higher level information about how
88 * the plugin is in use. */
89 size_t use_cnt;
90 } ctx_t;
91 #define CTX_INIT { SDB_OBJECT_INIT, \
92 SDB_PLUGIN_CTX_INIT, SDB_PLUGIN_INFO_INIT, NULL, 0 }
94 #define CTX(obj) ((ctx_t *)(obj))
96 typedef struct {
97 sdb_object_t super;
98 void *cb_callback;
99 sdb_object_t *cb_user_data;
100 ctx_t *cb_ctx;
101 } sdb_plugin_cb_t;
102 #define SDB_PLUGIN_CB_INIT { SDB_OBJECT_INIT, \
103 /* callback = */ NULL, /* user_data = */ NULL, \
104 SDB_PLUGIN_CTX_INIT }
106 typedef struct {
107 sdb_plugin_cb_t super;
108 #define ccb_callback super.cb_callback
109 #define ccb_user_data super.cb_user_data
110 #define ccb_ctx super.cb_ctx
111 sdb_time_t ccb_interval;
112 sdb_time_t ccb_next_update;
113 } sdb_plugin_collector_cb_t;
115 #define SDB_PLUGIN_CB(obj) ((sdb_plugin_cb_t *)(obj))
116 #define SDB_CONST_PLUGIN_CB(obj) ((const sdb_plugin_cb_t *)(obj))
117 #define SDB_PLUGIN_CCB(obj) ((sdb_plugin_collector_cb_t *)(obj))
118 #define SDB_CONST_PLUGIN_CCB(obj) ((const sdb_plugin_collector_cb_t *)(obj))
120 /*
121 * private variables
122 */
124 static sdb_plugin_ctx_t plugin_default_ctx = SDB_PLUGIN_CTX_INIT;
125 static sdb_plugin_info_t plugin_default_info = SDB_PLUGIN_INFO_INIT;
127 static pthread_key_t plugin_ctx_key;
128 static _Bool plugin_ctx_key_initialized = 0;
130 /* a list of the plugin contexts of all registered plugins */
131 static sdb_llist_t *all_plugins = NULL;
133 static sdb_llist_t *config_list = NULL;
134 static sdb_llist_t *init_list = NULL;
135 static sdb_llist_t *collector_list = NULL;
136 static sdb_llist_t *cname_list = NULL;
137 static sdb_llist_t *shutdown_list = NULL;
138 static sdb_llist_t *log_list = NULL;
140 /*
141 * private helper functions
142 */
144 static void
145 plugin_info_clear(sdb_plugin_info_t *info)
146 {
147 sdb_plugin_info_t empty_info = SDB_PLUGIN_INFO_INIT;
148 if (! info)
149 return;
151 if (info->plugin_name)
152 free(info->plugin_name);
153 if (info->filename)
154 free(info->filename);
156 if (info->name)
157 free(info->name);
158 if (info->description)
159 free(info->description);
160 if (info->copyright)
161 free(info->copyright);
162 if (info->license)
163 free(info->license);
165 *info = empty_info;
166 } /* plugin_info_clear */
168 static void
169 ctx_key_init(void)
170 {
171 if (plugin_ctx_key_initialized)
172 return;
174 pthread_key_create(&plugin_ctx_key, /* destructor */ NULL);
175 plugin_ctx_key_initialized = 1;
176 } /* ctx_key_init */
178 static int
179 plugin_cmp_next_update(const sdb_object_t *a, const sdb_object_t *b)
180 {
181 const sdb_plugin_collector_cb_t *ccb1
182 = (const sdb_plugin_collector_cb_t *)a;
183 const sdb_plugin_collector_cb_t *ccb2
184 = (const sdb_plugin_collector_cb_t *)b;
186 assert(ccb1 && ccb2);
188 return (ccb1->ccb_next_update > ccb2->ccb_next_update)
189 ? 1 : (ccb1->ccb_next_update < ccb2->ccb_next_update)
190 ? -1 : 0;
191 } /* plugin_cmp_next_update */
193 static int
194 plugin_lookup_by_name(const sdb_object_t *obj, const void *id)
195 {
196 const sdb_plugin_cb_t *cb = SDB_CONST_PLUGIN_CB(obj);
197 const char *name = id;
199 assert(cb && id);
201 /* when a plugin was registered from outside a plugin (e.g. the core),
202 * we don't have a plugin context */
203 if (! cb->cb_ctx)
204 return 1;
206 if (!strcasecmp(cb->cb_ctx->info.plugin_name, name))
207 return 0;
208 return 1;
209 } /* plugin_lookup_by_name */
211 static void
212 plugin_unregister_by_name(const char *plugin_name)
213 {
214 sdb_object_t *obj;
215 size_t i;
217 struct {
218 const char *type;
219 sdb_llist_t *list;
220 } all_lists[] = {
221 { "config", config_list },
222 { "init", init_list },
223 { "collector", collector_list },
224 { "cname", cname_list },
225 { "shutdown", shutdown_list },
226 { "log", log_list },
227 };
229 for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
230 const char *type = all_lists[i].type;
231 sdb_llist_t *list = all_lists[i].list;
233 while (1) {
234 sdb_plugin_cb_t *cb;
236 cb = SDB_PLUGIN_CB(sdb_llist_remove(list,
237 plugin_lookup_by_name, plugin_name));
238 if (! cb)
239 break;
241 assert(cb->cb_ctx);
243 sdb_log(SDB_LOG_INFO, "core: Unregistering "
244 "%s callback '%s' (module %s)", type, cb->super.name,
245 cb->cb_ctx->info.plugin_name);
246 sdb_object_deref(SDB_OBJ(cb));
247 }
248 }
250 obj = sdb_llist_search_by_name(all_plugins, plugin_name);
251 if (obj->ref_cnt <= 1)
252 sdb_llist_remove_by_name(all_plugins, plugin_name);
253 /* else: other callbacks still reference it */
254 sdb_object_deref(obj);
255 } /* plugin_unregister_by_name */
257 /*
258 * private types
259 */
261 static int
262 ctx_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
263 {
264 ctx_t *ctx = CTX(obj);
266 assert(ctx);
268 ctx->public = plugin_default_ctx;
269 ctx->info = plugin_default_info;
270 ctx->handle = NULL;
271 ctx->use_cnt = 1;
272 return 0;
273 } /* ctx_init */
275 static void
276 ctx_destroy(sdb_object_t *obj)
277 {
278 ctx_t *ctx = CTX(obj);
280 if (ctx->handle) {
281 const char *err;
283 sdb_log(SDB_LOG_INFO, "core: Unloading module %s",
284 ctx->info.plugin_name);
286 lt_dlerror();
287 lt_dlclose(ctx->handle);
288 if ((err = lt_dlerror()))
289 sdb_log(SDB_LOG_WARNING, "core: Failed to unload module %s: %s",
290 ctx->info.plugin_name, err);
291 }
293 plugin_info_clear(&ctx->info);
294 } /* ctx_destroy */
296 static sdb_type_t ctx_type = {
297 sizeof(ctx_t),
299 ctx_init,
300 ctx_destroy
301 };
303 static ctx_t *
304 ctx_get(void)
305 {
306 if (! plugin_ctx_key_initialized)
307 ctx_key_init();
308 return pthread_getspecific(plugin_ctx_key);
309 } /* ctx_get */
311 static ctx_t *
312 ctx_set(ctx_t *new)
313 {
314 ctx_t *old;
316 if (! plugin_ctx_key_initialized)
317 ctx_key_init();
319 old = pthread_getspecific(plugin_ctx_key);
320 if (old)
321 sdb_object_deref(SDB_OBJ(old));
322 if (new)
323 sdb_object_ref(SDB_OBJ(new));
324 pthread_setspecific(plugin_ctx_key, new);
325 return old;
326 } /* ctx_set */
328 static ctx_t *
329 ctx_create(const char *name)
330 {
331 ctx_t *ctx;
333 ctx = CTX(sdb_object_create(name, ctx_type));
334 if (! ctx)
335 return NULL;
337 if (! plugin_ctx_key_initialized)
338 ctx_key_init();
339 ctx_set(ctx);
340 return ctx;
341 } /* ctx_create */
343 static int
344 plugin_cb_init(sdb_object_t *obj, va_list ap)
345 {
346 sdb_llist_t **list = va_arg(ap, sdb_llist_t **);
347 const char *type = va_arg(ap, const char *);
348 void *callback = va_arg(ap, void *);
349 sdb_object_t *ud = va_arg(ap, sdb_object_t *);
351 assert(list);
352 assert(type);
353 assert(obj);
355 if (sdb_llist_search_by_name(*list, obj->name)) {
356 sdb_log(SDB_LOG_WARNING, "core: %s callback '%s' "
357 "has already been registered. Ignoring newly "
358 "registered version.", type, obj->name);
359 return -1;
360 }
362 /* cb_ctx may be NULL if the plugin was not registered by a plugin */
364 SDB_PLUGIN_CB(obj)->cb_callback = callback;
365 SDB_PLUGIN_CB(obj)->cb_ctx = ctx_get();
366 sdb_object_ref(SDB_OBJ(SDB_PLUGIN_CB(obj)->cb_ctx));
368 sdb_object_ref(ud);
369 SDB_PLUGIN_CB(obj)->cb_user_data = ud;
370 return 0;
371 } /* plugin_cb_init */
373 static void
374 plugin_cb_destroy(sdb_object_t *obj)
375 {
376 assert(obj);
377 sdb_object_deref(SDB_PLUGIN_CB(obj)->cb_user_data);
378 sdb_object_deref(SDB_OBJ(SDB_PLUGIN_CB(obj)->cb_ctx));
379 } /* plugin_cb_destroy */
381 static sdb_type_t sdb_plugin_cb_type = {
382 sizeof(sdb_plugin_cb_t),
384 plugin_cb_init,
385 plugin_cb_destroy
386 };
388 static sdb_type_t sdb_plugin_collector_cb_type = {
389 sizeof(sdb_plugin_collector_cb_t),
391 plugin_cb_init,
392 plugin_cb_destroy
393 };
395 static int
396 module_init(const char *name, lt_dlhandle lh, sdb_plugin_info_t *info)
397 {
398 int (*mod_init)(sdb_plugin_info_t *);
399 int status;
401 mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
402 if (! mod_init) {
403 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
404 "could not find symbol 'sdb_module_init'", name);
405 return -1;
406 }
408 status = mod_init(info);
409 if (status) {
410 sdb_log(SDB_LOG_ERR, "core: Failed to initialize "
411 "module '%s'", name);
412 plugin_unregister_by_name(name);
413 return -1;
414 }
415 return 0;
416 } /* module_init */
418 static int
419 module_load(const char *name, const sdb_plugin_ctx_t *plugin_ctx)
420 {
421 char base_name[name ? strlen(name) + 1 : 1];
422 const char *name_ptr;
423 char *tmp;
425 char filename[1024];
426 lt_dlhandle lh;
428 ctx_t *ctx;
430 int status;
432 base_name[0] = '\0';
433 name_ptr = name;
435 while ((tmp = strstr(name_ptr, "::"))) {
436 strncat(base_name, name_ptr, (size_t)(tmp - name_ptr));
437 strcat(base_name, "/");
438 name_ptr = tmp + strlen("::");
439 }
440 strcat(base_name, name_ptr);
442 snprintf(filename, sizeof(filename), "%s/%s.so",
443 PKGLIBDIR, base_name);
444 filename[sizeof(filename) - 1] = '\0';
446 if (access(filename, R_OK)) {
447 char errbuf[1024];
448 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s' (%s): %s",
449 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
450 return -1;
451 }
453 lt_dlinit();
454 lt_dlerror();
456 lh = lt_dlopen(filename);
457 if (! lh) {
458 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': %s"
459 "The most common cause for this problem are missing "
460 "dependencies.\n", name, lt_dlerror());
461 return -1;
462 }
464 if (ctx_get())
465 sdb_log(SDB_LOG_WARNING, "core: Discarding old plugin context");
467 ctx = ctx_create(name);
468 if (! ctx) {
469 sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin context");
470 return -1;
471 }
473 ctx->info.plugin_name = strdup(name);
474 ctx->info.filename = strdup(filename);
475 ctx->handle = lh;
477 if (plugin_ctx)
478 ctx->public = *plugin_ctx;
480 if ((status = module_init(name, lh, &ctx->info))) {
481 sdb_object_deref(SDB_OBJ(ctx));
482 return status;
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 sdb_llist_append(all_plugins, SDB_OBJ(ctx));
496 sdb_log(SDB_LOG_INFO, "core: Successfully loaded "
497 "plugin '%s' v%i (%s)\n\t%s\n\tLicense: %s",
498 INFO_GET(&ctx->info, name), ctx->info.plugin_version,
499 INFO_GET(&ctx->info, description),
500 INFO_GET(&ctx->info, copyright),
501 INFO_GET(&ctx->info, license));
503 /* any registered callbacks took ownership of the context */
504 sdb_object_deref(SDB_OBJ(ctx));
506 /* reset */
507 ctx_set(NULL);
508 return 0;
509 } /* module_load */
511 static int
512 plugin_add_callback(sdb_llist_t **list, const char *type,
513 const char *name, void *callback, sdb_object_t *user_data)
514 {
515 sdb_object_t *obj;
517 if ((! name) || (! callback))
518 return -1;
520 assert(list);
522 if (! *list)
523 *list = sdb_llist_create();
524 if (! *list)
525 return -1;
527 obj = sdb_object_create(name, sdb_plugin_cb_type,
528 list, type, callback, user_data);
529 if (! obj)
530 return -1;
532 if (sdb_llist_append(*list, obj)) {
533 sdb_object_deref(obj);
534 return -1;
535 }
537 /* pass control to the list */
538 sdb_object_deref(obj);
540 sdb_log(SDB_LOG_INFO, "core: Registered %s callback '%s'.",
541 type, name);
542 return 0;
543 } /* plugin_add_callback */
545 /*
546 * public API
547 */
549 int
550 sdb_plugin_load(const char *name, const sdb_plugin_ctx_t *plugin_ctx)
551 {
552 ctx_t *ctx;
554 if ((! name) || (! *name))
555 return -1;
557 if (! all_plugins) {
558 if (! (all_plugins = sdb_llist_create())) {
559 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
560 "internal error while creating linked list", name);
561 return -1;
562 }
563 }
565 ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
566 if (ctx) {
567 /* plugin already loaded */
568 ++ctx->use_cnt;
569 return 0;
570 }
572 return module_load(name, plugin_ctx);
573 } /* sdb_plugin_load */
575 int
576 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
577 {
578 va_list ap;
580 if (! info)
581 return -1;
583 va_start(ap, type);
585 switch (type) {
586 case SDB_PLUGIN_INFO_NAME:
587 {
588 char *name = va_arg(ap, char *);
589 if (name) {
590 if (info->name)
591 free(info->name);
592 info->name = strdup(name);
593 }
594 }
595 break;
596 case SDB_PLUGIN_INFO_DESC:
597 {
598 char *desc = va_arg(ap, char *);
599 if (desc) {
600 if (info->description)
601 free(info->description);
602 info->description = strdup(desc);
603 }
604 }
605 break;
606 case SDB_PLUGIN_INFO_COPYRIGHT:
607 {
608 char *copyright = va_arg(ap, char *);
609 if (copyright)
610 info->copyright = strdup(copyright);
611 }
612 break;
613 case SDB_PLUGIN_INFO_LICENSE:
614 {
615 char *license = va_arg(ap, char *);
616 if (license) {
617 if (info->license)
618 free(info->license);
619 info->license = strdup(license);
620 }
621 }
622 break;
623 case SDB_PLUGIN_INFO_VERSION:
624 {
625 int version = va_arg(ap, int);
626 info->version = version;
627 }
628 break;
629 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
630 {
631 int version = va_arg(ap, int);
632 info->plugin_version = version;
633 }
634 break;
635 default:
636 va_end(ap);
637 return -1;
638 }
640 va_end(ap);
641 return 0;
642 } /* sdb_plugin_set_info */
644 int
645 sdb_plugin_register_config(const char *name, sdb_plugin_config_cb callback)
646 {
647 return plugin_add_callback(&config_list, "init", name,
648 (void *)callback, NULL);
649 } /* sdb_plugin_register_config */
651 int
652 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
653 sdb_object_t *user_data)
654 {
655 return plugin_add_callback(&init_list, "init", name,
656 (void *)callback, user_data);
657 } /* sdb_plugin_register_init */
659 int
660 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
661 sdb_object_t *user_data)
662 {
663 return plugin_add_callback(&shutdown_list, "shutdown", name,
664 (void *)callback, user_data);
665 } /* sdb_plugin_register_shutdown */
667 int
668 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
669 sdb_object_t *user_data)
670 {
671 return plugin_add_callback(&log_list, "log", name, (void *)callback,
672 user_data);
673 } /* sdb_plugin_register_log */
675 int
676 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
677 sdb_object_t *user_data)
678 {
679 return plugin_add_callback(&cname_list, "cname", name, (void *)callback,
680 user_data);
681 } /* sdb_plugin_register_cname */
683 int
684 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
685 const sdb_time_t *interval, sdb_object_t *user_data)
686 {
687 sdb_object_t *obj;
689 if ((! name) || (! callback))
690 return -1;
692 if (! collector_list)
693 collector_list = sdb_llist_create();
694 if (! collector_list)
695 return -1;
697 obj = sdb_object_create(name, sdb_plugin_collector_cb_type,
698 &collector_list, "collector", callback, user_data);
699 if (! obj)
700 return -1;
702 if (interval)
703 SDB_PLUGIN_CCB(obj)->ccb_interval = *interval;
704 else {
705 sdb_time_t tmp = sdb_plugin_get_ctx().interval;
707 if (tmp > 0)
708 SDB_PLUGIN_CCB(obj)->ccb_interval = tmp;
709 else
710 SDB_PLUGIN_CCB(obj)->ccb_interval = 0;
711 }
713 if (! (SDB_PLUGIN_CCB(obj)->ccb_next_update = sdb_gettime())) {
714 char errbuf[1024];
715 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
716 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
717 sdb_object_deref(obj);
718 return -1;
719 }
721 if (sdb_llist_insert_sorted(collector_list, obj,
722 plugin_cmp_next_update)) {
723 sdb_object_deref(obj);
724 return -1;
725 }
727 /* pass control to the list */
728 sdb_object_deref(obj);
730 sdb_log(SDB_LOG_INFO, "core: Registered collector callback '%s' "
731 "(interval = %.3fs).", name,
732 SDB_TIME_TO_DOUBLE(SDB_PLUGIN_CCB(obj)->ccb_interval));
733 return 0;
734 } /* sdb_plugin_register_collector */
736 sdb_plugin_ctx_t
737 sdb_plugin_get_ctx(void)
738 {
739 ctx_t *c;
741 c = ctx_get();
742 if (! c) {
743 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid read access to plugin "
744 "context outside a plugin");
745 return plugin_default_ctx;
746 }
747 return c->public;
748 } /* sdb_plugin_get_ctx */
750 int
751 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx, sdb_plugin_ctx_t *old)
752 {
753 ctx_t *c;
755 c = ctx_get();
756 if (! c) {
757 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid write access to plugin "
758 "context outside a plugin");
759 return -1;
760 }
762 if (old)
763 *old = c->public;
764 c->public = ctx;
765 return 0;
766 } /* sdb_plugin_set_ctx */
768 int
769 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
770 {
771 sdb_plugin_cb_t *plugin;
772 sdb_plugin_config_cb callback;
774 ctx_t *old_ctx;
776 int status;
778 if ((! name) || (! ci))
779 return -1;
781 plugin = SDB_PLUGIN_CB(sdb_llist_search_by_name(config_list, name));
782 if (! plugin) {
783 ctx_t *ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
784 if (! ctx)
785 sdb_log(SDB_LOG_ERR, "core: Plugin '%s' not loaded.", name);
786 else
787 sdb_log(SDB_LOG_ERR, "core: Plugin '%s' did not register "
788 "a config callback.", name);
789 errno = ENOENT;
790 return -1;
791 }
793 old_ctx = ctx_set(plugin->cb_ctx);
794 callback = (sdb_plugin_config_cb)plugin->cb_callback;
795 status = callback(ci);
796 ctx_set(old_ctx);
797 return status;
798 } /* sdb_plugin_configure */
800 int
801 sdb_plugin_init_all(void)
802 {
803 sdb_llist_iter_t *iter;
804 int ret = 0;
806 iter = sdb_llist_get_iter(init_list);
807 while (sdb_llist_iter_has_next(iter)) {
808 sdb_plugin_cb_t *cb;
809 sdb_plugin_init_cb callback;
810 ctx_t *old_ctx;
812 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
813 assert(obj);
814 cb = SDB_PLUGIN_CB(obj);
816 callback = (sdb_plugin_init_cb)cb->cb_callback;
818 old_ctx = ctx_set(cb->cb_ctx);
819 if (callback(cb->cb_user_data)) {
820 sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin "
821 "'%s'. Unregistering all callbacks.", obj->name);
822 ctx_set(old_ctx);
823 plugin_unregister_by_name(cb->cb_ctx->info.plugin_name);
824 ++ret;
825 }
826 else
827 ctx_set(old_ctx);
828 }
829 sdb_llist_iter_destroy(iter);
830 return ret;
831 } /* sdb_plugin_init_all */
833 int
834 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
835 {
836 if (! collector_list) {
837 sdb_log(SDB_LOG_WARNING, "core: No collectors registered. "
838 "Quiting main loop.");
839 return -1;
840 }
842 if (! loop)
843 return -1;
845 while (loop->do_loop) {
846 sdb_plugin_collector_cb callback;
847 ctx_t *old_ctx;
849 sdb_time_t interval, now;
851 sdb_object_t *obj = sdb_llist_shift(collector_list);
852 if (! obj)
853 return -1;
855 callback = (sdb_plugin_collector_cb)SDB_PLUGIN_CCB(obj)->ccb_callback;
857 if (! (now = sdb_gettime())) {
858 char errbuf[1024];
859 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
860 "time in collector main loop: %s",
861 sdb_strerror(errno, errbuf, sizeof(errbuf)));
862 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
863 }
865 if (now < SDB_PLUGIN_CCB(obj)->ccb_next_update) {
866 interval = SDB_PLUGIN_CCB(obj)->ccb_next_update - now;
868 errno = 0;
869 while (loop->do_loop && sdb_sleep(interval, &interval)) {
870 if (errno != EINTR) {
871 char errbuf[1024];
872 sdb_log(SDB_LOG_ERR, "core: Failed to sleep "
873 "in collector main loop: %s",
874 sdb_strerror(errno, errbuf, sizeof(errbuf)));
875 return -1;
876 }
877 errno = 0;
878 }
880 if (! loop->do_loop)
881 return 0;
882 }
884 old_ctx = ctx_set(SDB_PLUGIN_CCB(obj)->ccb_ctx);
885 if (callback(SDB_PLUGIN_CCB(obj)->ccb_user_data)) {
886 /* XXX */
887 }
888 ctx_set(old_ctx);
890 interval = SDB_PLUGIN_CCB(obj)->ccb_interval;
891 if (! interval)
892 interval = loop->default_interval;
893 if (! interval) {
894 sdb_log(SDB_LOG_WARNING, "core: No interval configured "
895 "for plugin '%s'; skipping any further "
896 "iterations.", obj->name);
897 sdb_object_deref(obj);
898 continue;
899 }
901 SDB_PLUGIN_CCB(obj)->ccb_next_update += interval;
903 if (! (now = sdb_gettime())) {
904 char errbuf[1024];
905 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
906 "time in collector main loop: %s",
907 sdb_strerror(errno, errbuf, sizeof(errbuf)));
908 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
909 }
911 if (now > SDB_PLUGIN_CCB(obj)->ccb_next_update) {
912 sdb_log(SDB_LOG_WARNING, "core: Plugin '%s' took too "
913 "long; skipping iterations to keep up.",
914 obj->name);
915 SDB_PLUGIN_CCB(obj)->ccb_next_update = now;
916 }
918 if (sdb_llist_insert_sorted(collector_list, obj,
919 plugin_cmp_next_update)) {
920 sdb_log(SDB_LOG_ERR, "core: Failed to re-insert "
921 "plugin '%s' into collector list. Unable to further "
922 "use the plugin.",
923 obj->name);
924 sdb_object_deref(obj);
925 return -1;
926 }
928 /* pass control back to the list */
929 sdb_object_deref(obj);
930 }
931 return 0;
932 } /* sdb_plugin_read_loop */
934 char *
935 sdb_plugin_cname(char *hostname)
936 {
937 sdb_llist_iter_t *iter;
939 if (! hostname)
940 return NULL;
942 if (! cname_list)
943 return hostname;
945 iter = sdb_llist_get_iter(cname_list);
946 while (sdb_llist_iter_has_next(iter)) {
947 sdb_plugin_cname_cb callback;
948 char *cname;
950 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
951 assert(obj);
953 callback = (sdb_plugin_cname_cb)SDB_PLUGIN_CB(obj)->cb_callback;
954 cname = callback(hostname, SDB_PLUGIN_CB(obj)->cb_user_data);
955 if (cname) {
956 free(hostname);
957 hostname = cname;
958 }
959 /* else: don't change hostname */
960 }
961 sdb_llist_iter_destroy(iter);
962 return hostname;
963 } /* sdb_plugin_cname */
965 int
966 sdb_plugin_log(int prio, const char *msg)
967 {
968 sdb_llist_iter_t *iter;
969 int ret = -1;
971 if (! msg)
972 return 0;
974 if (! sdb_llist_len(log_list))
975 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
977 iter = sdb_llist_get_iter(log_list);
978 while (sdb_llist_iter_has_next(iter)) {
979 sdb_plugin_log_cb callback;
980 int tmp;
982 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
983 assert(obj);
985 callback = (sdb_plugin_log_cb)SDB_PLUGIN_CB(obj)->cb_callback;
986 tmp = callback(prio, msg, SDB_PLUGIN_CB(obj)->cb_user_data);
987 if (tmp > ret)
988 ret = tmp;
989 }
990 sdb_llist_iter_destroy(iter);
991 return ret;
992 } /* sdb_plugin_log */
994 int
995 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
996 {
997 sdb_strbuf_t *buf;
998 int ret;
1000 if (! fmt)
1001 return 0;
1003 buf = sdb_strbuf_create(64);
1004 if (! buf) {
1005 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
1006 ret += vfprintf(stderr, fmt, ap);
1007 return ret;
1008 }
1010 if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
1011 sdb_strbuf_destroy(buf);
1012 return -1;
1013 }
1015 ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
1016 sdb_strbuf_destroy(buf);
1017 return ret;
1018 } /* sdb_plugin_vlogf */
1020 int
1021 sdb_plugin_logf(int prio, const char *fmt, ...)
1022 {
1023 va_list ap;
1024 int ret;
1026 if (! fmt)
1027 return 0;
1029 va_start(ap, fmt);
1030 ret = sdb_plugin_vlogf(prio, fmt, ap);
1031 va_end(ap);
1032 return ret;
1033 } /* sdb_plugin_logf */
1035 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */