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 *description;
63 char *copyright;
64 char *license;
66 int version;
67 int plugin_version;
68 };
69 #define SDB_PLUGIN_INFO_INIT { \
70 /* plugin_name */ NULL, /* filename */ NULL, /* desc */ NULL, \
71 /* copyright */ NULL, /* license */ NULL, \
72 /* version */ -1, /* plugin_version */ -1 }
73 #define INFO_GET(i, attr) \
74 ((i)->attr ? (i)->attr : #attr" not set")
76 typedef struct {
77 sdb_object_t super;
78 sdb_plugin_ctx_t public;
80 sdb_plugin_info_t info;
81 lt_dlhandle handle;
83 /* The usage count differs from the object's ref count
84 * in that it provides higher level information about how
85 * the plugin is in use. */
86 size_t use_cnt;
87 } ctx_t;
88 #define CTX_INIT { SDB_OBJECT_INIT, \
89 SDB_PLUGIN_CTX_INIT, SDB_PLUGIN_INFO_INIT, NULL, 0 }
91 #define CTX(obj) ((ctx_t *)(obj))
93 typedef struct {
94 sdb_object_t super;
95 void *cb_callback;
96 sdb_object_t *cb_user_data;
97 ctx_t *cb_ctx;
98 } sdb_plugin_cb_t;
99 #define SDB_PLUGIN_CB_INIT { SDB_OBJECT_INIT, \
100 /* callback = */ NULL, /* user_data = */ NULL, \
101 SDB_PLUGIN_CTX_INIT }
103 typedef struct {
104 sdb_plugin_cb_t super;
105 #define ccb_callback super.cb_callback
106 #define ccb_user_data super.cb_user_data
107 #define ccb_ctx super.cb_ctx
108 sdb_time_t ccb_interval;
109 sdb_time_t ccb_next_update;
110 } sdb_plugin_collector_cb_t;
112 #define SDB_PLUGIN_CB(obj) ((sdb_plugin_cb_t *)(obj))
113 #define SDB_CONST_PLUGIN_CB(obj) ((const sdb_plugin_cb_t *)(obj))
114 #define SDB_PLUGIN_CCB(obj) ((sdb_plugin_collector_cb_t *)(obj))
115 #define SDB_CONST_PLUGIN_CCB(obj) ((const sdb_plugin_collector_cb_t *)(obj))
117 /*
118 * private variables
119 */
121 static sdb_plugin_ctx_t plugin_default_ctx = SDB_PLUGIN_CTX_INIT;
122 static sdb_plugin_info_t plugin_default_info = SDB_PLUGIN_INFO_INIT;
124 static pthread_key_t plugin_ctx_key;
125 static _Bool plugin_ctx_key_initialized = 0;
127 /* a list of the plugin contexts of all registered plugins */
128 static sdb_llist_t *all_plugins = NULL;
130 static sdb_llist_t *config_list = NULL;
131 static sdb_llist_t *init_list = NULL;
132 static sdb_llist_t *collector_list = NULL;
133 static sdb_llist_t *cname_list = NULL;
134 static sdb_llist_t *shutdown_list = NULL;
135 static sdb_llist_t *log_list = NULL;
137 static struct {
138 const char *type;
139 sdb_llist_t **list;
140 } all_lists[] = {
141 { "config", &config_list },
142 { "init", &init_list },
143 { "collector", &collector_list },
144 { "cname", &cname_list },
145 { "shutdown", &shutdown_list },
146 { "log", &log_list },
147 };
149 /*
150 * private helper functions
151 */
153 static void
154 plugin_info_clear(sdb_plugin_info_t *info)
155 {
156 sdb_plugin_info_t empty_info = SDB_PLUGIN_INFO_INIT;
157 if (! info)
158 return;
160 if (info->plugin_name)
161 free(info->plugin_name);
162 if (info->filename)
163 free(info->filename);
165 if (info->description)
166 free(info->description);
167 if (info->copyright)
168 free(info->copyright);
169 if (info->license)
170 free(info->license);
172 *info = empty_info;
173 } /* plugin_info_clear */
175 static void
176 ctx_key_init(void)
177 {
178 if (plugin_ctx_key_initialized)
179 return;
181 pthread_key_create(&plugin_ctx_key, /* destructor */ NULL);
182 plugin_ctx_key_initialized = 1;
183 } /* ctx_key_init */
185 static int
186 plugin_cmp_next_update(const sdb_object_t *a, const sdb_object_t *b)
187 {
188 const sdb_plugin_collector_cb_t *ccb1
189 = (const sdb_plugin_collector_cb_t *)a;
190 const sdb_plugin_collector_cb_t *ccb2
191 = (const sdb_plugin_collector_cb_t *)b;
193 assert(ccb1 && ccb2);
195 return (ccb1->ccb_next_update > ccb2->ccb_next_update)
196 ? 1 : (ccb1->ccb_next_update < ccb2->ccb_next_update)
197 ? -1 : 0;
198 } /* plugin_cmp_next_update */
200 static int
201 plugin_lookup_by_name(const sdb_object_t *obj, const void *id)
202 {
203 const sdb_plugin_cb_t *cb = SDB_CONST_PLUGIN_CB(obj);
204 const char *name = id;
206 assert(cb && id);
208 /* when a plugin was registered from outside a plugin (e.g. the core),
209 * we don't have a plugin context */
210 if (! cb->cb_ctx)
211 return 1;
213 if (!strcasecmp(cb->cb_ctx->info.plugin_name, name))
214 return 0;
215 return 1;
216 } /* plugin_lookup_by_name */
218 /* since this function is called from sdb_plugin_reconfigure_finish()
219 * when iterating through all_plugins, we may not do any additional
220 * modifications to all_plugins except for the optional removal */
221 static void
222 plugin_unregister_by_name(const char *plugin_name)
223 {
224 sdb_object_t *obj;
225 size_t i;
227 for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
228 const char *type = all_lists[i].type;
229 sdb_llist_t *list = *all_lists[i].list;
231 while (1) {
232 sdb_plugin_cb_t *cb;
234 cb = SDB_PLUGIN_CB(sdb_llist_remove(list,
235 plugin_lookup_by_name, plugin_name));
236 if (! cb)
237 break;
239 assert(cb->cb_ctx);
241 sdb_log(SDB_LOG_INFO, "core: Unregistering "
242 "%s callback '%s' (module %s)", type, cb->super.name,
243 cb->cb_ctx->info.plugin_name);
244 sdb_object_deref(SDB_OBJ(cb));
245 }
246 }
248 obj = sdb_llist_search_by_name(all_plugins, plugin_name);
249 /* when called from sdb_plugin_reconfigure_finish, the object has already
250 * been removed from the list */
251 if (obj && (obj->ref_cnt <= 1)) {
252 sdb_llist_remove_by_name(all_plugins, plugin_name);
253 sdb_object_deref(obj);
254 }
255 /* else: other callbacks still reference it */
256 } /* plugin_unregister_by_name */
258 static void
259 plugin_unregister_all(void)
260 {
261 size_t i;
263 for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
264 const char *type = all_lists[i].type;
265 sdb_llist_t *list = *all_lists[i].list;
267 size_t len = sdb_llist_len(list);
269 if (! len)
270 continue;
272 sdb_llist_clear(list);
273 sdb_log(SDB_LOG_INFO, "core: Unregistered %zu %s callback%s",
274 len, type, len == 1 ? "" : "s");
275 }
276 } /* plugin_unregister_all */
278 /*
279 * private types
280 */
282 static int
283 ctx_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
284 {
285 ctx_t *ctx = CTX(obj);
287 assert(ctx);
289 ctx->public = plugin_default_ctx;
290 ctx->info = plugin_default_info;
291 ctx->handle = NULL;
292 ctx->use_cnt = 1;
293 return 0;
294 } /* ctx_init */
296 static void
297 ctx_destroy(sdb_object_t *obj)
298 {
299 ctx_t *ctx = CTX(obj);
301 if (ctx->handle) {
302 const char *err;
304 sdb_log(SDB_LOG_INFO, "core: Unloading module %s",
305 ctx->info.plugin_name);
307 lt_dlerror();
308 lt_dlclose(ctx->handle);
309 if ((err = lt_dlerror()))
310 sdb_log(SDB_LOG_WARNING, "core: Failed to unload module %s: %s",
311 ctx->info.plugin_name, err);
312 }
314 plugin_info_clear(&ctx->info);
315 } /* ctx_destroy */
317 static sdb_type_t ctx_type = {
318 sizeof(ctx_t),
320 ctx_init,
321 ctx_destroy
322 };
324 static ctx_t *
325 ctx_get(void)
326 {
327 if (! plugin_ctx_key_initialized)
328 ctx_key_init();
329 return pthread_getspecific(plugin_ctx_key);
330 } /* ctx_get */
332 static ctx_t *
333 ctx_set(ctx_t *new)
334 {
335 ctx_t *old;
337 if (! plugin_ctx_key_initialized)
338 ctx_key_init();
340 old = pthread_getspecific(plugin_ctx_key);
341 if (old)
342 sdb_object_deref(SDB_OBJ(old));
343 if (new)
344 sdb_object_ref(SDB_OBJ(new));
345 pthread_setspecific(plugin_ctx_key, new);
346 return old;
347 } /* ctx_set */
349 static ctx_t *
350 ctx_create(const char *name)
351 {
352 ctx_t *ctx;
354 ctx = CTX(sdb_object_create(name, ctx_type));
355 if (! ctx)
356 return NULL;
358 if (! plugin_ctx_key_initialized)
359 ctx_key_init();
360 ctx_set(ctx);
361 return ctx;
362 } /* ctx_create */
364 static int
365 plugin_cb_init(sdb_object_t *obj, va_list ap)
366 {
367 sdb_llist_t **list = va_arg(ap, sdb_llist_t **);
368 const char *type = va_arg(ap, const char *);
369 void *callback = va_arg(ap, void *);
370 sdb_object_t *ud = va_arg(ap, sdb_object_t *);
372 assert(list);
373 assert(type);
374 assert(obj);
376 if (sdb_llist_search_by_name(*list, obj->name)) {
377 sdb_log(SDB_LOG_WARNING, "core: %s callback '%s' "
378 "has already been registered. Ignoring newly "
379 "registered version.", type, obj->name);
380 return -1;
381 }
383 /* cb_ctx may be NULL if the plugin was not registered by a plugin */
385 SDB_PLUGIN_CB(obj)->cb_callback = callback;
386 SDB_PLUGIN_CB(obj)->cb_ctx = ctx_get();
387 sdb_object_ref(SDB_OBJ(SDB_PLUGIN_CB(obj)->cb_ctx));
389 sdb_object_ref(ud);
390 SDB_PLUGIN_CB(obj)->cb_user_data = ud;
391 return 0;
392 } /* plugin_cb_init */
394 static void
395 plugin_cb_destroy(sdb_object_t *obj)
396 {
397 assert(obj);
398 sdb_object_deref(SDB_PLUGIN_CB(obj)->cb_user_data);
399 sdb_object_deref(SDB_OBJ(SDB_PLUGIN_CB(obj)->cb_ctx));
400 } /* plugin_cb_destroy */
402 static sdb_type_t sdb_plugin_cb_type = {
403 sizeof(sdb_plugin_cb_t),
405 plugin_cb_init,
406 plugin_cb_destroy
407 };
409 static sdb_type_t sdb_plugin_collector_cb_type = {
410 sizeof(sdb_plugin_collector_cb_t),
412 plugin_cb_init,
413 plugin_cb_destroy
414 };
416 static int
417 module_init(const char *name, lt_dlhandle lh, sdb_plugin_info_t *info)
418 {
419 int (*mod_init)(sdb_plugin_info_t *);
420 int status;
422 mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
423 if (! mod_init) {
424 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
425 "could not find symbol 'sdb_module_init'", name);
426 return -1;
427 }
429 status = mod_init(info);
430 if (status) {
431 sdb_log(SDB_LOG_ERR, "core: Failed to initialize "
432 "module '%s'", name);
433 plugin_unregister_by_name(name);
434 return -1;
435 }
436 return 0;
437 } /* module_init */
439 static int
440 module_load(const char *basedir, const char *name,
441 const sdb_plugin_ctx_t *plugin_ctx)
442 {
443 char base_name[name ? strlen(name) + 1 : 1];
444 const char *name_ptr;
445 char *tmp;
447 char filename[1024];
448 lt_dlhandle lh;
450 ctx_t *ctx;
452 int status;
454 assert(name);
456 base_name[0] = '\0';
457 name_ptr = name;
459 while ((tmp = strstr(name_ptr, "::"))) {
460 strncat(base_name, name_ptr, (size_t)(tmp - name_ptr));
461 strcat(base_name, "/");
462 name_ptr = tmp + strlen("::");
463 }
464 strcat(base_name, name_ptr);
466 if (! basedir)
467 basedir = PKGLIBDIR;
469 snprintf(filename, sizeof(filename), "%s/%s.so", basedir, base_name);
470 filename[sizeof(filename) - 1] = '\0';
472 if (access(filename, R_OK)) {
473 char errbuf[1024];
474 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s' (%s): %s",
475 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
476 return -1;
477 }
479 lt_dlinit();
480 lt_dlerror();
482 lh = lt_dlopen(filename);
483 if (! lh) {
484 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': %s"
485 "The most common cause for this problem are missing "
486 "dependencies.\n", name, lt_dlerror());
487 return -1;
488 }
490 if (ctx_get())
491 sdb_log(SDB_LOG_WARNING, "core: Discarding old plugin context");
493 ctx = ctx_create(name);
494 if (! ctx) {
495 sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin context");
496 return -1;
497 }
499 ctx->info.plugin_name = strdup(name);
500 ctx->info.filename = strdup(filename);
501 ctx->handle = lh;
503 if (plugin_ctx)
504 ctx->public = *plugin_ctx;
506 if ((status = module_init(name, lh, &ctx->info))) {
507 sdb_object_deref(SDB_OBJ(ctx));
508 return status;
509 }
511 /* compare minor version */
512 if ((ctx->info.version < 0)
513 || ((int)(ctx->info.version / 100) != (int)(SDB_VERSION / 100)))
514 sdb_log(SDB_LOG_WARNING, "core: WARNING: version of "
515 "plugin '%s' (%i.%i.%i) does not match our version "
516 "(%i.%i.%i); this might cause problems",
517 name, SDB_VERSION_DECODE(ctx->info.version),
518 SDB_VERSION_DECODE(SDB_VERSION));
520 sdb_llist_append(all_plugins, SDB_OBJ(ctx));
522 sdb_log(SDB_LOG_INFO, "core: Successfully loaded "
523 "plugin %s v%i (%s)", ctx->info.plugin_name,
524 ctx->info.plugin_version,
525 INFO_GET(&ctx->info, description));
526 sdb_log(SDB_LOG_INFO, "core: Plugin %s: %s, License: %s",
527 ctx->info.plugin_name,
528 INFO_GET(&ctx->info, copyright),
529 INFO_GET(&ctx->info, license));
531 /* any registered callbacks took ownership of the context */
532 sdb_object_deref(SDB_OBJ(ctx));
534 /* reset */
535 ctx_set(NULL);
536 return 0;
537 } /* module_load */
539 static char *
540 plugin_get_name(const char *name, char *buf, size_t bufsize)
541 {
542 ctx_t *ctx = ctx_get();
544 if (ctx)
545 snprintf(buf, bufsize, "%s::%s", ctx->info.plugin_name, name);
546 else
547 snprintf(buf, bufsize, "core::%s", name);
548 return buf;
549 } /* plugin_get_name */
551 static int
552 plugin_add_callback(sdb_llist_t **list, const char *type,
553 const char *name, void *callback, sdb_object_t *user_data)
554 {
555 sdb_object_t *obj;
557 if ((! name) || (! callback))
558 return -1;
560 assert(list);
562 if (! *list)
563 *list = sdb_llist_create();
564 if (! *list)
565 return -1;
567 obj = sdb_object_create(name, sdb_plugin_cb_type,
568 list, type, callback, user_data);
569 if (! obj)
570 return -1;
572 if (sdb_llist_append(*list, obj)) {
573 sdb_object_deref(obj);
574 return -1;
575 }
577 /* pass control to the list */
578 sdb_object_deref(obj);
580 sdb_log(SDB_LOG_INFO, "core: Registered %s callback '%s'.",
581 type, name);
582 return 0;
583 } /* plugin_add_callback */
585 /*
586 * public API
587 */
589 int
590 sdb_plugin_load(const char *basedir, const char *name,
591 const sdb_plugin_ctx_t *plugin_ctx)
592 {
593 ctx_t *ctx;
595 int status;
597 if ((! name) || (! *name))
598 return -1;
600 if (! all_plugins) {
601 if (! (all_plugins = sdb_llist_create())) {
602 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
603 "internal error while creating linked list", name);
604 return -1;
605 }
606 }
608 ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
609 if (ctx) {
610 /* plugin already loaded */
611 if (! ctx->use_cnt) {
612 /* reloading plugin */
613 ctx_t *old_ctx = ctx_set(ctx);
615 status = module_init(ctx->info.plugin_name, ctx->handle, NULL);
616 if (status)
617 return status;
619 sdb_log(SDB_LOG_INFO, "core: Successfully reloaded plugin "
620 "'%s' (%s)", ctx->info.plugin_name,
621 INFO_GET(&ctx->info, description));
622 ctx_set(old_ctx);
623 }
624 ++ctx->use_cnt;
625 return 0;
626 }
628 return module_load(basedir, name, plugin_ctx);
629 } /* sdb_plugin_load */
631 int
632 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
633 {
634 va_list ap;
636 if (! info)
637 return -1;
639 va_start(ap, type);
641 switch (type) {
642 case SDB_PLUGIN_INFO_DESC:
643 {
644 char *desc = va_arg(ap, char *);
645 if (desc) {
646 if (info->description)
647 free(info->description);
648 info->description = strdup(desc);
649 }
650 }
651 break;
652 case SDB_PLUGIN_INFO_COPYRIGHT:
653 {
654 char *copyright = va_arg(ap, char *);
655 if (copyright)
656 info->copyright = strdup(copyright);
657 }
658 break;
659 case SDB_PLUGIN_INFO_LICENSE:
660 {
661 char *license = va_arg(ap, char *);
662 if (license) {
663 if (info->license)
664 free(info->license);
665 info->license = strdup(license);
666 }
667 }
668 break;
669 case SDB_PLUGIN_INFO_VERSION:
670 {
671 int version = va_arg(ap, int);
672 info->version = version;
673 }
674 break;
675 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
676 {
677 int version = va_arg(ap, int);
678 info->plugin_version = version;
679 }
680 break;
681 default:
682 va_end(ap);
683 return -1;
684 }
686 va_end(ap);
687 return 0;
688 } /* sdb_plugin_set_info */
690 int
691 sdb_plugin_register_config(sdb_plugin_config_cb callback)
692 {
693 ctx_t *ctx = ctx_get();
695 if (! ctx) {
696 sdb_log(SDB_LOG_ERR, "core: Invalid attempt to register a "
697 "config callback from outside a plugin");
698 return -1;
699 }
700 return plugin_add_callback(&config_list, "init", ctx->info.plugin_name,
701 (void *)callback, NULL);
702 } /* sdb_plugin_register_config */
704 int
705 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
706 sdb_object_t *user_data)
707 {
708 char cb_name[1024];
709 return plugin_add_callback(&init_list, "init",
710 plugin_get_name(name, cb_name, sizeof(cb_name)),
711 (void *)callback, user_data);
712 } /* sdb_plugin_register_init */
714 int
715 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
716 sdb_object_t *user_data)
717 {
718 char cb_name[1024];
719 return plugin_add_callback(&shutdown_list, "shutdown",
720 plugin_get_name(name, cb_name, sizeof(cb_name)),
721 (void *)callback, user_data);
722 } /* sdb_plugin_register_shutdown */
724 int
725 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
726 sdb_object_t *user_data)
727 {
728 char cb_name[1024];
729 return plugin_add_callback(&log_list, "log",
730 plugin_get_name(name, cb_name, sizeof(cb_name)),
731 callback, user_data);
732 } /* sdb_plugin_register_log */
734 int
735 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
736 sdb_object_t *user_data)
737 {
738 char cb_name[1024];
739 return plugin_add_callback(&cname_list, "cname",
740 plugin_get_name(name, cb_name, sizeof(cb_name)),
741 callback, user_data);
742 } /* sdb_plugin_register_cname */
744 int
745 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
746 const sdb_time_t *interval, sdb_object_t *user_data)
747 {
748 char cb_name[1024];
749 sdb_object_t *obj;
751 if ((! name) || (! callback))
752 return -1;
754 if (! collector_list)
755 collector_list = sdb_llist_create();
756 if (! collector_list)
757 return -1;
759 plugin_get_name(name, cb_name, sizeof(cb_name));
761 obj = sdb_object_create(cb_name, sdb_plugin_collector_cb_type,
762 &collector_list, "collector", callback, user_data);
763 if (! obj)
764 return -1;
766 if (interval)
767 SDB_PLUGIN_CCB(obj)->ccb_interval = *interval;
768 else {
769 sdb_time_t tmp = sdb_plugin_get_ctx().interval;
771 if (tmp > 0)
772 SDB_PLUGIN_CCB(obj)->ccb_interval = tmp;
773 else
774 SDB_PLUGIN_CCB(obj)->ccb_interval = 0;
775 }
777 if (! (SDB_PLUGIN_CCB(obj)->ccb_next_update = sdb_gettime())) {
778 char errbuf[1024];
779 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
780 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
781 sdb_object_deref(obj);
782 return -1;
783 }
785 if (sdb_llist_insert_sorted(collector_list, obj,
786 plugin_cmp_next_update)) {
787 sdb_object_deref(obj);
788 return -1;
789 }
791 /* pass control to the list */
792 sdb_object_deref(obj);
794 sdb_log(SDB_LOG_INFO, "core: Registered collector callback '%s' "
795 "(interval = %.3fs).", cb_name,
796 SDB_TIME_TO_DOUBLE(SDB_PLUGIN_CCB(obj)->ccb_interval));
797 return 0;
798 } /* sdb_plugin_register_collector */
800 sdb_plugin_ctx_t
801 sdb_plugin_get_ctx(void)
802 {
803 ctx_t *c;
805 c = ctx_get();
806 if (! c) {
807 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid read access to plugin "
808 "context outside a plugin");
809 return plugin_default_ctx;
810 }
811 return c->public;
812 } /* sdb_plugin_get_ctx */
814 int
815 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx, sdb_plugin_ctx_t *old)
816 {
817 ctx_t *c;
819 c = ctx_get();
820 if (! c) {
821 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid write access to plugin "
822 "context outside a plugin");
823 return -1;
824 }
826 if (old)
827 *old = c->public;
828 c->public = ctx;
829 return 0;
830 } /* sdb_plugin_set_ctx */
832 int
833 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
834 {
835 sdb_plugin_cb_t *plugin;
836 sdb_plugin_config_cb callback;
838 ctx_t *old_ctx;
840 int status;
842 if ((! name) || (! ci))
843 return -1;
845 plugin = SDB_PLUGIN_CB(sdb_llist_search_by_name(config_list, name));
846 if (! plugin) {
847 ctx_t *ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
848 if (! ctx)
849 sdb_log(SDB_LOG_ERR, "core: Cannot configure unknown "
850 "plugin '%s'. Missing 'LoadPlugin \"%s\"'?",
851 name, name);
852 else
853 sdb_log(SDB_LOG_ERR, "core: Plugin '%s' did not register "
854 "a config callback.", name);
855 errno = ENOENT;
856 return -1;
857 }
859 old_ctx = ctx_set(plugin->cb_ctx);
860 callback = (sdb_plugin_config_cb)plugin->cb_callback;
861 status = callback(ci);
862 ctx_set(old_ctx);
863 return status;
864 } /* sdb_plugin_configure */
866 int
867 sdb_plugin_reconfigure_init(void)
868 {
869 sdb_llist_iter_t *iter;
871 iter = sdb_llist_get_iter(config_list);
872 if (config_list && (! iter))
873 return -1;
875 /* deconfigure all plugins */
876 while (sdb_llist_iter_has_next(iter)) {
877 sdb_plugin_cb_t *plugin;
878 sdb_plugin_config_cb callback;
879 ctx_t *old_ctx;
881 plugin = SDB_PLUGIN_CB(sdb_llist_iter_get_next(iter));
882 old_ctx = ctx_set(plugin->cb_ctx);
883 callback = (sdb_plugin_config_cb)plugin->cb_callback;
884 callback(NULL);
885 ctx_set(old_ctx);
886 }
887 sdb_llist_iter_destroy(iter);
889 iter = sdb_llist_get_iter(all_plugins);
890 if (all_plugins && (! iter))
891 return -1;
893 /* record all plugins as being unused */
894 while (sdb_llist_iter_has_next(iter))
895 CTX(sdb_llist_iter_get_next(iter))->use_cnt = 0;
896 sdb_llist_iter_destroy(iter);
898 plugin_unregister_all();
899 return 0;
900 } /* sdb_plugin_reconfigure_init */
902 int
903 sdb_plugin_reconfigure_finish(void)
904 {
905 sdb_llist_iter_t *iter;
907 iter = sdb_llist_get_iter(all_plugins);
908 if (all_plugins && (! iter))
909 return -1;
911 while (sdb_llist_iter_has_next(iter)) {
912 ctx_t *ctx = CTX(sdb_llist_iter_get_next(iter));
913 if (ctx->use_cnt)
914 continue;
916 sdb_log(SDB_LOG_INFO, "core: Module %s no longer in use",
917 ctx->info.plugin_name);
918 sdb_llist_iter_remove_current(iter);
919 plugin_unregister_by_name(ctx->info.plugin_name);
920 sdb_object_deref(SDB_OBJ(ctx));
921 }
922 sdb_llist_iter_destroy(iter);
923 return 0;
924 } /* sdb_plugin_reconfigure_finish */
926 int
927 sdb_plugin_init_all(void)
928 {
929 sdb_llist_iter_t *iter;
930 int ret = 0;
932 iter = sdb_llist_get_iter(init_list);
933 while (sdb_llist_iter_has_next(iter)) {
934 sdb_plugin_cb_t *cb;
935 sdb_plugin_init_cb callback;
936 ctx_t *old_ctx;
938 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
939 assert(obj);
940 cb = SDB_PLUGIN_CB(obj);
942 callback = (sdb_plugin_init_cb)cb->cb_callback;
944 old_ctx = ctx_set(cb->cb_ctx);
945 if (callback(cb->cb_user_data)) {
946 sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin "
947 "'%s'. Unregistering all callbacks.", obj->name);
948 ctx_set(old_ctx);
949 plugin_unregister_by_name(cb->cb_ctx->info.plugin_name);
950 ++ret;
951 }
952 else
953 ctx_set(old_ctx);
954 }
955 sdb_llist_iter_destroy(iter);
956 return ret;
957 } /* sdb_plugin_init_all */
959 int
960 sdb_plugin_shutdown_all(void)
961 {
962 sdb_llist_iter_t *iter;
963 int ret = 0;
965 iter = sdb_llist_get_iter(shutdown_list);
966 while (sdb_llist_iter_has_next(iter)) {
967 sdb_plugin_cb_t *cb;
968 sdb_plugin_shutdown_cb callback;
969 ctx_t *old_ctx;
971 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
972 assert(obj);
973 cb = SDB_PLUGIN_CB(obj);
975 callback = (sdb_plugin_shutdown_cb)cb->cb_callback;
977 old_ctx = ctx_set(cb->cb_ctx);
978 if (callback(cb->cb_user_data)) {
979 sdb_log(SDB_LOG_ERR, "core: Failed to shutdown plugin '%s'.",
980 obj->name);
981 ++ret;
982 }
983 ctx_set(old_ctx);
984 }
985 sdb_llist_iter_destroy(iter);
986 return ret;
987 } /* sdb_plugin_shutdown_all */
989 int
990 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
991 {
992 if (! collector_list) {
993 sdb_log(SDB_LOG_WARNING, "core: No collectors registered. "
994 "Quiting main loop.");
995 return -1;
996 }
998 if (! loop)
999 return -1;
1001 while (loop->do_loop) {
1002 sdb_plugin_collector_cb callback;
1003 ctx_t *old_ctx;
1005 sdb_time_t interval, now;
1007 sdb_object_t *obj = sdb_llist_shift(collector_list);
1008 if (! obj)
1009 return -1;
1011 callback = (sdb_plugin_collector_cb)SDB_PLUGIN_CCB(obj)->ccb_callback;
1013 if (! (now = sdb_gettime())) {
1014 char errbuf[1024];
1015 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1016 "time in collector main loop: %s",
1017 sdb_strerror(errno, errbuf, sizeof(errbuf)));
1018 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
1019 }
1021 if (now < SDB_PLUGIN_CCB(obj)->ccb_next_update) {
1022 interval = SDB_PLUGIN_CCB(obj)->ccb_next_update - now;
1024 errno = 0;
1025 while (loop->do_loop && sdb_sleep(interval, &interval)) {
1026 if (errno != EINTR) {
1027 char errbuf[1024];
1028 sdb_log(SDB_LOG_ERR, "core: Failed to sleep "
1029 "in collector main loop: %s",
1030 sdb_strerror(errno, errbuf, sizeof(errbuf)));
1031 sdb_llist_insert_sorted(collector_list, obj,
1032 plugin_cmp_next_update);
1033 sdb_object_deref(obj);
1034 return -1;
1035 }
1036 errno = 0;
1037 }
1039 if (! loop->do_loop) {
1040 /* put back; don't worry about errors */
1041 sdb_llist_insert_sorted(collector_list, obj,
1042 plugin_cmp_next_update);
1043 sdb_object_deref(obj);
1044 return 0;
1045 }
1046 }
1048 old_ctx = ctx_set(SDB_PLUGIN_CCB(obj)->ccb_ctx);
1049 if (callback(SDB_PLUGIN_CCB(obj)->ccb_user_data)) {
1050 /* XXX */
1051 }
1052 ctx_set(old_ctx);
1054 interval = SDB_PLUGIN_CCB(obj)->ccb_interval;
1055 if (! interval)
1056 interval = loop->default_interval;
1057 if (! interval) {
1058 sdb_log(SDB_LOG_WARNING, "core: No interval configured "
1059 "for plugin '%s'; skipping any further "
1060 "iterations.", obj->name);
1061 sdb_object_deref(obj);
1062 continue;
1063 }
1065 SDB_PLUGIN_CCB(obj)->ccb_next_update += interval;
1067 if (! (now = sdb_gettime())) {
1068 char errbuf[1024];
1069 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1070 "time in collector main loop: %s",
1071 sdb_strerror(errno, errbuf, sizeof(errbuf)));
1072 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
1073 }
1075 if (now > SDB_PLUGIN_CCB(obj)->ccb_next_update) {
1076 sdb_log(SDB_LOG_WARNING, "core: Plugin '%s' took too "
1077 "long; skipping iterations to keep up.",
1078 obj->name);
1079 SDB_PLUGIN_CCB(obj)->ccb_next_update = now;
1080 }
1082 if (sdb_llist_insert_sorted(collector_list, obj,
1083 plugin_cmp_next_update)) {
1084 sdb_log(SDB_LOG_ERR, "core: Failed to re-insert "
1085 "plugin '%s' into collector list. Unable to further "
1086 "use the plugin.",
1087 obj->name);
1088 sdb_object_deref(obj);
1089 return -1;
1090 }
1092 /* pass control back to the list */
1093 sdb_object_deref(obj);
1094 }
1095 return 0;
1096 } /* sdb_plugin_read_loop */
1098 char *
1099 sdb_plugin_cname(char *hostname)
1100 {
1101 sdb_llist_iter_t *iter;
1103 if (! hostname)
1104 return NULL;
1106 if (! cname_list)
1107 return hostname;
1109 iter = sdb_llist_get_iter(cname_list);
1110 while (sdb_llist_iter_has_next(iter)) {
1111 sdb_plugin_cname_cb callback;
1112 char *cname;
1114 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1115 assert(obj);
1117 callback = (sdb_plugin_cname_cb)SDB_PLUGIN_CB(obj)->cb_callback;
1118 cname = callback(hostname, SDB_PLUGIN_CB(obj)->cb_user_data);
1119 if (cname) {
1120 free(hostname);
1121 hostname = cname;
1122 }
1123 /* else: don't change hostname */
1124 }
1125 sdb_llist_iter_destroy(iter);
1126 return hostname;
1127 } /* sdb_plugin_cname */
1129 int
1130 sdb_plugin_log(int prio, const char *msg)
1131 {
1132 sdb_llist_iter_t *iter;
1133 int ret = -1;
1135 _Bool logged = 0;
1137 if (! msg)
1138 return 0;
1140 iter = sdb_llist_get_iter(log_list);
1141 while (sdb_llist_iter_has_next(iter)) {
1142 sdb_plugin_log_cb callback;
1143 int tmp;
1145 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1146 assert(obj);
1148 callback = (sdb_plugin_log_cb)SDB_PLUGIN_CB(obj)->cb_callback;
1149 tmp = callback(prio, msg, SDB_PLUGIN_CB(obj)->cb_user_data);
1150 if (tmp > ret)
1151 ret = tmp;
1153 if (SDB_PLUGIN_CB(obj)->cb_ctx)
1154 logged = 1;
1155 /* else: this is an internally registered callback */
1156 }
1157 sdb_llist_iter_destroy(iter);
1159 if (! logged)
1160 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
1161 return ret;
1162 } /* sdb_plugin_log */
1164 int
1165 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
1166 {
1167 sdb_strbuf_t *buf;
1168 int ret;
1170 if (! fmt)
1171 return 0;
1173 buf = sdb_strbuf_create(64);
1174 if (! buf) {
1175 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
1176 ret += vfprintf(stderr, fmt, ap);
1177 return ret;
1178 }
1180 if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
1181 sdb_strbuf_destroy(buf);
1182 return -1;
1183 }
1185 ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
1186 sdb_strbuf_destroy(buf);
1187 return ret;
1188 } /* sdb_plugin_vlogf */
1190 int
1191 sdb_plugin_logf(int prio, const char *fmt, ...)
1192 {
1193 va_list ap;
1194 int ret;
1196 if (! fmt)
1197 return 0;
1199 va_start(ap, fmt);
1200 ret = sdb_plugin_vlogf(prio, fmt, ap);
1201 va_end(ap);
1202 return ret;
1203 } /* sdb_plugin_logf */
1205 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */