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 /* helper to access info attributes */
54 #define INFO_GET(i, attr) \
55 ((i)->attr ? (i)->attr : #attr" not set")
57 /*
58 * private data types
59 */
61 typedef struct {
62 sdb_object_t super;
63 sdb_plugin_ctx_t public;
65 sdb_plugin_info_t info;
66 lt_dlhandle handle;
68 /* The usage count differs from the object's ref count
69 * in that it provides higher level information about how
70 * the plugin is in use. */
71 size_t use_cnt;
72 } ctx_t;
73 #define CTX_INIT { SDB_OBJECT_INIT, \
74 SDB_PLUGIN_CTX_INIT, SDB_PLUGIN_INFO_INIT, NULL, 0 }
76 #define CTX(obj) ((ctx_t *)(obj))
78 typedef struct {
79 sdb_object_t super;
80 void *cb_callback;
81 sdb_object_t *cb_user_data;
82 ctx_t *cb_ctx;
83 } sdb_plugin_cb_t;
84 #define SDB_PLUGIN_CB_INIT { SDB_OBJECT_INIT, \
85 /* callback = */ NULL, /* user_data = */ NULL, \
86 SDB_PLUGIN_CTX_INIT }
88 typedef struct {
89 sdb_plugin_cb_t super;
90 #define ccb_callback super.cb_callback
91 #define ccb_user_data super.cb_user_data
92 #define ccb_ctx super.cb_ctx
93 sdb_time_t ccb_interval;
94 sdb_time_t ccb_next_update;
95 } sdb_plugin_collector_cb_t;
97 #define SDB_PLUGIN_CB(obj) ((sdb_plugin_cb_t *)(obj))
98 #define SDB_CONST_PLUGIN_CB(obj) ((const sdb_plugin_cb_t *)(obj))
99 #define SDB_PLUGIN_CCB(obj) ((sdb_plugin_collector_cb_t *)(obj))
100 #define SDB_CONST_PLUGIN_CCB(obj) ((const sdb_plugin_collector_cb_t *)(obj))
102 /*
103 * private variables
104 */
106 static sdb_plugin_ctx_t plugin_default_ctx = SDB_PLUGIN_CTX_INIT;
107 static sdb_plugin_info_t plugin_default_info = SDB_PLUGIN_INFO_INIT;
109 static pthread_key_t plugin_ctx_key;
110 static _Bool plugin_ctx_key_initialized = 0;
112 /* a list of the plugin contexts of all registered plugins */
113 static sdb_llist_t *all_plugins = NULL;
115 static sdb_llist_t *config_list = NULL;
116 static sdb_llist_t *init_list = NULL;
117 static sdb_llist_t *collector_list = NULL;
118 static sdb_llist_t *cname_list = NULL;
119 static sdb_llist_t *shutdown_list = NULL;
120 static sdb_llist_t *log_list = NULL;
121 static sdb_llist_t *ts_fetcher_list = NULL;
123 static struct {
124 const char *type;
125 sdb_llist_t **list;
126 } all_lists[] = {
127 { "config", &config_list },
128 { "init", &init_list },
129 { "collector", &collector_list },
130 { "cname", &cname_list },
131 { "shutdown", &shutdown_list },
132 { "log", &log_list },
133 };
135 /*
136 * private helper functions
137 */
139 static void
140 plugin_info_clear(sdb_plugin_info_t *info)
141 {
142 sdb_plugin_info_t empty_info = SDB_PLUGIN_INFO_INIT;
143 if (! info)
144 return;
146 if (info->plugin_name)
147 free(info->plugin_name);
148 if (info->filename)
149 free(info->filename);
151 if (info->description)
152 free(info->description);
153 if (info->copyright)
154 free(info->copyright);
155 if (info->license)
156 free(info->license);
158 *info = empty_info;
159 } /* plugin_info_clear */
161 static void
162 ctx_key_init(void)
163 {
164 if (plugin_ctx_key_initialized)
165 return;
167 pthread_key_create(&plugin_ctx_key, /* destructor */ NULL);
168 plugin_ctx_key_initialized = 1;
169 } /* ctx_key_init */
171 static int
172 plugin_cmp_next_update(const sdb_object_t *a, const sdb_object_t *b)
173 {
174 const sdb_plugin_collector_cb_t *ccb1
175 = (const sdb_plugin_collector_cb_t *)a;
176 const sdb_plugin_collector_cb_t *ccb2
177 = (const sdb_plugin_collector_cb_t *)b;
179 assert(ccb1 && ccb2);
181 return (ccb1->ccb_next_update > ccb2->ccb_next_update)
182 ? 1 : (ccb1->ccb_next_update < ccb2->ccb_next_update)
183 ? -1 : 0;
184 } /* plugin_cmp_next_update */
186 static int
187 plugin_lookup_by_name(const sdb_object_t *obj, const void *id)
188 {
189 const sdb_plugin_cb_t *cb = SDB_CONST_PLUGIN_CB(obj);
190 const char *name = id;
192 assert(cb && id);
194 /* when a plugin was registered from outside a plugin (e.g. the core),
195 * we don't have a plugin context */
196 if (! cb->cb_ctx)
197 return 1;
199 if (!strcasecmp(cb->cb_ctx->info.plugin_name, name))
200 return 0;
201 return 1;
202 } /* plugin_lookup_by_name */
204 /* since this function is called from sdb_plugin_reconfigure_finish()
205 * when iterating through all_plugins, we may not do any additional
206 * modifications to all_plugins except for the optional removal */
207 static void
208 plugin_unregister_by_name(const char *plugin_name)
209 {
210 sdb_object_t *obj;
211 size_t i;
213 for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
214 const char *type = all_lists[i].type;
215 sdb_llist_t *list = *all_lists[i].list;
217 while (1) {
218 sdb_plugin_cb_t *cb;
220 cb = SDB_PLUGIN_CB(sdb_llist_remove(list,
221 plugin_lookup_by_name, plugin_name));
222 if (! cb)
223 break;
225 assert(cb->cb_ctx);
227 sdb_log(SDB_LOG_INFO, "core: Unregistering "
228 "%s callback '%s' (module %s)", type, cb->super.name,
229 cb->cb_ctx->info.plugin_name);
230 sdb_object_deref(SDB_OBJ(cb));
231 }
232 }
234 obj = sdb_llist_search_by_name(all_plugins, plugin_name);
235 /* when called from sdb_plugin_reconfigure_finish, the object has already
236 * been removed from the list */
237 if (obj && (obj->ref_cnt <= 1)) {
238 sdb_llist_remove_by_name(all_plugins, plugin_name);
239 sdb_object_deref(obj);
240 }
241 /* else: other callbacks still reference it */
242 } /* plugin_unregister_by_name */
244 static void
245 plugin_unregister_all(void)
246 {
247 size_t i;
249 for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
250 const char *type = all_lists[i].type;
251 sdb_llist_t *list = *all_lists[i].list;
253 size_t len = sdb_llist_len(list);
255 if (! len)
256 continue;
258 sdb_llist_clear(list);
259 sdb_log(SDB_LOG_INFO, "core: Unregistered %zu %s callback%s",
260 len, type, len == 1 ? "" : "s");
261 }
262 } /* plugin_unregister_all */
264 /*
265 * private types
266 */
268 static int
269 ctx_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
270 {
271 ctx_t *ctx = CTX(obj);
273 assert(ctx);
275 ctx->public = plugin_default_ctx;
276 ctx->info = plugin_default_info;
277 ctx->handle = NULL;
278 ctx->use_cnt = 1;
279 return 0;
280 } /* ctx_init */
282 static void
283 ctx_destroy(sdb_object_t *obj)
284 {
285 ctx_t *ctx = CTX(obj);
287 if (ctx->handle) {
288 const char *err;
290 sdb_log(SDB_LOG_INFO, "core: Unloading module %s",
291 ctx->info.plugin_name);
293 lt_dlerror();
294 lt_dlclose(ctx->handle);
295 if ((err = lt_dlerror()))
296 sdb_log(SDB_LOG_WARNING, "core: Failed to unload module %s: %s",
297 ctx->info.plugin_name, err);
298 }
300 plugin_info_clear(&ctx->info);
301 } /* ctx_destroy */
303 static sdb_type_t ctx_type = {
304 sizeof(ctx_t),
306 ctx_init,
307 ctx_destroy
308 };
310 static ctx_t *
311 ctx_get(void)
312 {
313 if (! plugin_ctx_key_initialized)
314 ctx_key_init();
315 return pthread_getspecific(plugin_ctx_key);
316 } /* ctx_get */
318 static ctx_t *
319 ctx_set(ctx_t *new)
320 {
321 ctx_t *old;
323 if (! plugin_ctx_key_initialized)
324 ctx_key_init();
326 old = pthread_getspecific(plugin_ctx_key);
327 if (old)
328 sdb_object_deref(SDB_OBJ(old));
329 if (new)
330 sdb_object_ref(SDB_OBJ(new));
331 pthread_setspecific(plugin_ctx_key, new);
332 return old;
333 } /* ctx_set */
335 static ctx_t *
336 ctx_create(const char *name)
337 {
338 ctx_t *ctx;
340 ctx = CTX(sdb_object_create(name, ctx_type));
341 if (! ctx)
342 return NULL;
344 if (! plugin_ctx_key_initialized)
345 ctx_key_init();
346 ctx_set(ctx);
347 return ctx;
348 } /* ctx_create */
350 static int
351 plugin_cb_init(sdb_object_t *obj, va_list ap)
352 {
353 sdb_llist_t **list = va_arg(ap, sdb_llist_t **);
354 const char *type = va_arg(ap, const char *);
355 void *callback = va_arg(ap, void *);
356 sdb_object_t *ud = va_arg(ap, sdb_object_t *);
358 assert(list);
359 assert(type);
360 assert(obj);
362 if (sdb_llist_search_by_name(*list, obj->name)) {
363 sdb_log(SDB_LOG_WARNING, "core: %s callback '%s' "
364 "has already been registered. Ignoring newly "
365 "registered version.", type, obj->name);
366 return -1;
367 }
369 /* cb_ctx may be NULL if the plugin was not registered by a plugin */
371 SDB_PLUGIN_CB(obj)->cb_callback = callback;
372 SDB_PLUGIN_CB(obj)->cb_ctx = ctx_get();
373 sdb_object_ref(SDB_OBJ(SDB_PLUGIN_CB(obj)->cb_ctx));
375 sdb_object_ref(ud);
376 SDB_PLUGIN_CB(obj)->cb_user_data = ud;
377 return 0;
378 } /* plugin_cb_init */
380 static void
381 plugin_cb_destroy(sdb_object_t *obj)
382 {
383 assert(obj);
384 sdb_object_deref(SDB_PLUGIN_CB(obj)->cb_user_data);
385 sdb_object_deref(SDB_OBJ(SDB_PLUGIN_CB(obj)->cb_ctx));
386 } /* plugin_cb_destroy */
388 static sdb_type_t sdb_plugin_cb_type = {
389 sizeof(sdb_plugin_cb_t),
391 plugin_cb_init,
392 plugin_cb_destroy
393 };
395 static sdb_type_t sdb_plugin_collector_cb_type = {
396 sizeof(sdb_plugin_collector_cb_t),
398 plugin_cb_init,
399 plugin_cb_destroy
400 };
402 static int
403 module_init(const char *name, lt_dlhandle lh, sdb_plugin_info_t *info)
404 {
405 int (*mod_init)(sdb_plugin_info_t *);
406 int status;
408 mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
409 if (! mod_init) {
410 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
411 "could not find symbol 'sdb_module_init'", name);
412 return -1;
413 }
415 status = mod_init(info);
416 if (status) {
417 sdb_log(SDB_LOG_ERR, "core: Failed to initialize "
418 "module '%s'", name);
419 plugin_unregister_by_name(name);
420 return -1;
421 }
422 return 0;
423 } /* module_init */
425 static int
426 module_load(const char *basedir, const char *name,
427 const sdb_plugin_ctx_t *plugin_ctx)
428 {
429 char base_name[name ? strlen(name) + 1 : 1];
430 const char *name_ptr;
431 char *tmp;
433 char filename[1024];
434 lt_dlhandle lh;
436 ctx_t *ctx;
438 int status;
440 assert(name);
442 base_name[0] = '\0';
443 name_ptr = name;
445 while ((tmp = strstr(name_ptr, "::"))) {
446 strncat(base_name, name_ptr, (size_t)(tmp - name_ptr));
447 strcat(base_name, "/");
448 name_ptr = tmp + strlen("::");
449 }
450 strcat(base_name, name_ptr);
452 if (! basedir)
453 basedir = PKGLIBDIR;
455 snprintf(filename, sizeof(filename), "%s/%s.so", basedir, base_name);
456 filename[sizeof(filename) - 1] = '\0';
458 if (access(filename, R_OK)) {
459 char errbuf[1024];
460 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s' (%s): %s",
461 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
462 return -1;
463 }
465 lt_dlinit();
466 lt_dlerror();
468 lh = lt_dlopen(filename);
469 if (! lh) {
470 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': %s"
471 "The most common cause for this problem are missing "
472 "dependencies.\n", name, lt_dlerror());
473 return -1;
474 }
476 if (ctx_get())
477 sdb_log(SDB_LOG_WARNING, "core: Discarding old plugin context");
479 ctx = ctx_create(name);
480 if (! ctx) {
481 sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin context");
482 return -1;
483 }
485 ctx->info.plugin_name = strdup(name);
486 ctx->info.filename = strdup(filename);
487 ctx->handle = lh;
489 if (plugin_ctx)
490 ctx->public = *plugin_ctx;
492 if ((status = module_init(name, lh, &ctx->info))) {
493 sdb_object_deref(SDB_OBJ(ctx));
494 return status;
495 }
497 /* compare minor version */
498 if ((ctx->info.version < 0)
499 || ((int)(ctx->info.version / 100) != (int)(SDB_VERSION / 100)))
500 sdb_log(SDB_LOG_WARNING, "core: WARNING: version of "
501 "plugin '%s' (%i.%i.%i) does not match our version "
502 "(%i.%i.%i); this might cause problems",
503 name, SDB_VERSION_DECODE(ctx->info.version),
504 SDB_VERSION_DECODE(SDB_VERSION));
506 sdb_llist_append(all_plugins, SDB_OBJ(ctx));
508 sdb_log(SDB_LOG_INFO, "core: Successfully loaded "
509 "plugin %s v%i (%s)", ctx->info.plugin_name,
510 ctx->info.plugin_version,
511 INFO_GET(&ctx->info, description));
512 sdb_log(SDB_LOG_INFO, "core: Plugin %s: %s, License: %s",
513 ctx->info.plugin_name,
514 INFO_GET(&ctx->info, copyright),
515 INFO_GET(&ctx->info, license));
517 /* any registered callbacks took ownership of the context */
518 sdb_object_deref(SDB_OBJ(ctx));
520 /* reset */
521 ctx_set(NULL);
522 return 0;
523 } /* module_load */
525 static char *
526 plugin_get_name(const char *name, char *buf, size_t bufsize)
527 {
528 ctx_t *ctx = ctx_get();
530 if (ctx)
531 snprintf(buf, bufsize, "%s::%s", ctx->info.plugin_name, name);
532 else
533 snprintf(buf, bufsize, "core::%s", name);
534 return buf;
535 } /* plugin_get_name */
537 static int
538 plugin_add_callback(sdb_llist_t **list, const char *type,
539 const char *name, void *callback, sdb_object_t *user_data)
540 {
541 sdb_object_t *obj;
543 if ((! name) || (! callback))
544 return -1;
546 assert(list);
548 if (! *list)
549 *list = sdb_llist_create();
550 if (! *list)
551 return -1;
553 obj = sdb_object_create(name, sdb_plugin_cb_type,
554 list, type, callback, user_data);
555 if (! obj)
556 return -1;
558 if (sdb_llist_append(*list, obj)) {
559 sdb_object_deref(obj);
560 return -1;
561 }
563 /* pass control to the list */
564 sdb_object_deref(obj);
566 sdb_log(SDB_LOG_INFO, "core: Registered %s callback '%s'.",
567 type, name);
568 return 0;
569 } /* plugin_add_callback */
571 /*
572 * public API
573 */
575 int
576 sdb_plugin_load(const char *basedir, const char *name,
577 const sdb_plugin_ctx_t *plugin_ctx)
578 {
579 ctx_t *ctx;
581 int status;
583 if ((! name) || (! *name))
584 return -1;
586 if (! all_plugins) {
587 if (! (all_plugins = sdb_llist_create())) {
588 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
589 "internal error while creating linked list", name);
590 return -1;
591 }
592 }
594 ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
595 if (ctx) {
596 /* plugin already loaded */
597 if (! ctx->use_cnt) {
598 /* reloading plugin */
599 ctx_t *old_ctx = ctx_set(ctx);
601 status = module_init(ctx->info.plugin_name, ctx->handle, NULL);
602 if (status)
603 return status;
605 sdb_log(SDB_LOG_INFO, "core: Successfully reloaded plugin "
606 "'%s' (%s)", ctx->info.plugin_name,
607 INFO_GET(&ctx->info, description));
608 ctx_set(old_ctx);
609 }
610 ++ctx->use_cnt;
611 return 0;
612 }
614 return module_load(basedir, name, plugin_ctx);
615 } /* sdb_plugin_load */
617 int
618 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
619 {
620 va_list ap;
622 if (! info)
623 return -1;
625 va_start(ap, type);
627 switch (type) {
628 case SDB_PLUGIN_INFO_DESC:
629 {
630 char *desc = va_arg(ap, char *);
631 if (desc) {
632 if (info->description)
633 free(info->description);
634 info->description = strdup(desc);
635 }
636 }
637 break;
638 case SDB_PLUGIN_INFO_COPYRIGHT:
639 {
640 char *copyright = va_arg(ap, char *);
641 if (copyright)
642 info->copyright = strdup(copyright);
643 }
644 break;
645 case SDB_PLUGIN_INFO_LICENSE:
646 {
647 char *license = va_arg(ap, char *);
648 if (license) {
649 if (info->license)
650 free(info->license);
651 info->license = strdup(license);
652 }
653 }
654 break;
655 case SDB_PLUGIN_INFO_VERSION:
656 {
657 int version = va_arg(ap, int);
658 info->version = version;
659 }
660 break;
661 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
662 {
663 int version = va_arg(ap, int);
664 info->plugin_version = version;
665 }
666 break;
667 default:
668 va_end(ap);
669 return -1;
670 }
672 va_end(ap);
673 return 0;
674 } /* sdb_plugin_set_info */
676 int
677 sdb_plugin_register_config(sdb_plugin_config_cb callback)
678 {
679 ctx_t *ctx = ctx_get();
681 if (! ctx) {
682 sdb_log(SDB_LOG_ERR, "core: Invalid attempt to register a "
683 "config callback from outside a plugin");
684 return -1;
685 }
686 return plugin_add_callback(&config_list, "init", ctx->info.plugin_name,
687 (void *)callback, NULL);
688 } /* sdb_plugin_register_config */
690 int
691 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
692 sdb_object_t *user_data)
693 {
694 char cb_name[1024];
695 return plugin_add_callback(&init_list, "init",
696 plugin_get_name(name, cb_name, sizeof(cb_name)),
697 (void *)callback, user_data);
698 } /* sdb_plugin_register_init */
700 int
701 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
702 sdb_object_t *user_data)
703 {
704 char cb_name[1024];
705 return plugin_add_callback(&shutdown_list, "shutdown",
706 plugin_get_name(name, cb_name, sizeof(cb_name)),
707 (void *)callback, user_data);
708 } /* sdb_plugin_register_shutdown */
710 int
711 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
712 sdb_object_t *user_data)
713 {
714 char cb_name[1024];
715 return plugin_add_callback(&log_list, "log",
716 plugin_get_name(name, cb_name, sizeof(cb_name)),
717 callback, user_data);
718 } /* sdb_plugin_register_log */
720 int
721 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
722 sdb_object_t *user_data)
723 {
724 char cb_name[1024];
725 return plugin_add_callback(&cname_list, "cname",
726 plugin_get_name(name, cb_name, sizeof(cb_name)),
727 callback, user_data);
728 } /* sdb_plugin_register_cname */
730 int
731 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
732 const sdb_time_t *interval, sdb_object_t *user_data)
733 {
734 char cb_name[1024];
735 sdb_object_t *obj;
737 if ((! name) || (! callback))
738 return -1;
740 if (! collector_list)
741 collector_list = sdb_llist_create();
742 if (! collector_list)
743 return -1;
745 plugin_get_name(name, cb_name, sizeof(cb_name));
747 obj = sdb_object_create(cb_name, sdb_plugin_collector_cb_type,
748 &collector_list, "collector", callback, user_data);
749 if (! obj)
750 return -1;
752 if (interval)
753 SDB_PLUGIN_CCB(obj)->ccb_interval = *interval;
754 else {
755 ctx_t *ctx = ctx_get();
757 if (! ctx) {
758 sdb_log(SDB_LOG_ERR, "core: Cannot determine interval "
759 "for collector %s; none specified and no plugin "
760 "context found", cb_name);
761 return -1;
762 }
764 SDB_PLUGIN_CCB(obj)->ccb_interval = ctx->public.interval;
765 }
767 if (! (SDB_PLUGIN_CCB(obj)->ccb_next_update = sdb_gettime())) {
768 char errbuf[1024];
769 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
770 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
771 sdb_object_deref(obj);
772 return -1;
773 }
775 if (sdb_llist_insert_sorted(collector_list, obj,
776 plugin_cmp_next_update)) {
777 sdb_object_deref(obj);
778 return -1;
779 }
781 /* pass control to the list */
782 sdb_object_deref(obj);
784 sdb_log(SDB_LOG_INFO, "core: Registered collector callback '%s' "
785 "(interval = %.3fs).", cb_name,
786 SDB_TIME_TO_DOUBLE(SDB_PLUGIN_CCB(obj)->ccb_interval));
787 return 0;
788 } /* sdb_plugin_register_collector */
790 int
791 sdb_plugin_register_ts_fetcher(const char *name,
792 sdb_plugin_fetch_ts_cb callback, sdb_object_t *user_data)
793 {
794 return plugin_add_callback(&ts_fetcher_list, "time-series fetcher",
795 name, callback, user_data);
796 } /* sdb_plugin_register_ts_fetcher */
798 sdb_plugin_ctx_t
799 sdb_plugin_get_ctx(void)
800 {
801 ctx_t *c;
803 c = ctx_get();
804 if (! c) {
805 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid read access to plugin "
806 "context outside a plugin");
807 return plugin_default_ctx;
808 }
809 return c->public;
810 } /* sdb_plugin_get_ctx */
812 int
813 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx, sdb_plugin_ctx_t *old)
814 {
815 ctx_t *c;
817 c = ctx_get();
818 if (! c) {
819 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid write access to plugin "
820 "context outside a plugin");
821 return -1;
822 }
824 if (old)
825 *old = c->public;
826 c->public = ctx;
827 return 0;
828 } /* sdb_plugin_set_ctx */
830 const sdb_plugin_info_t *
831 sdb_plugin_current(void)
832 {
833 ctx_t *ctx = ctx_get();
835 if (! ctx)
836 return NULL;
837 return &ctx->info;
838 } /* sdb_plugin_current */
840 int
841 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
842 {
843 sdb_plugin_cb_t *plugin;
844 sdb_plugin_config_cb callback;
846 ctx_t *old_ctx;
848 int status;
850 if ((! name) || (! ci))
851 return -1;
853 plugin = SDB_PLUGIN_CB(sdb_llist_search_by_name(config_list, name));
854 if (! plugin) {
855 ctx_t *ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
856 if (! ctx)
857 sdb_log(SDB_LOG_ERR, "core: Cannot configure unknown "
858 "plugin '%s'. Missing 'LoadPlugin \"%s\"'?",
859 name, name);
860 else
861 sdb_log(SDB_LOG_ERR, "core: Plugin '%s' did not register "
862 "a config callback.", name);
863 errno = ENOENT;
864 return -1;
865 }
867 old_ctx = ctx_set(plugin->cb_ctx);
868 callback = (sdb_plugin_config_cb)plugin->cb_callback;
869 status = callback(ci);
870 ctx_set(old_ctx);
871 return status;
872 } /* sdb_plugin_configure */
874 int
875 sdb_plugin_reconfigure_init(void)
876 {
877 sdb_llist_iter_t *iter;
879 iter = sdb_llist_get_iter(config_list);
880 if (config_list && (! iter))
881 return -1;
883 /* deconfigure all plugins */
884 while (sdb_llist_iter_has_next(iter)) {
885 sdb_plugin_cb_t *plugin;
886 sdb_plugin_config_cb callback;
887 ctx_t *old_ctx;
889 plugin = SDB_PLUGIN_CB(sdb_llist_iter_get_next(iter));
890 old_ctx = ctx_set(plugin->cb_ctx);
891 callback = (sdb_plugin_config_cb)plugin->cb_callback;
892 callback(NULL);
893 ctx_set(old_ctx);
894 }
895 sdb_llist_iter_destroy(iter);
897 iter = sdb_llist_get_iter(all_plugins);
898 if (all_plugins && (! iter))
899 return -1;
901 /* record all plugins as being unused */
902 while (sdb_llist_iter_has_next(iter))
903 CTX(sdb_llist_iter_get_next(iter))->use_cnt = 0;
904 sdb_llist_iter_destroy(iter);
906 plugin_unregister_all();
907 return 0;
908 } /* sdb_plugin_reconfigure_init */
910 int
911 sdb_plugin_reconfigure_finish(void)
912 {
913 sdb_llist_iter_t *iter;
915 iter = sdb_llist_get_iter(all_plugins);
916 if (all_plugins && (! iter))
917 return -1;
919 while (sdb_llist_iter_has_next(iter)) {
920 ctx_t *ctx = CTX(sdb_llist_iter_get_next(iter));
921 if (ctx->use_cnt)
922 continue;
924 sdb_log(SDB_LOG_INFO, "core: Module %s no longer in use",
925 ctx->info.plugin_name);
926 sdb_llist_iter_remove_current(iter);
927 plugin_unregister_by_name(ctx->info.plugin_name);
928 sdb_object_deref(SDB_OBJ(ctx));
929 }
930 sdb_llist_iter_destroy(iter);
931 return 0;
932 } /* sdb_plugin_reconfigure_finish */
934 int
935 sdb_plugin_init_all(void)
936 {
937 sdb_llist_iter_t *iter;
938 int ret = 0;
940 iter = sdb_llist_get_iter(init_list);
941 while (sdb_llist_iter_has_next(iter)) {
942 sdb_plugin_cb_t *cb;
943 sdb_plugin_init_cb callback;
944 ctx_t *old_ctx;
946 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
947 assert(obj);
948 cb = SDB_PLUGIN_CB(obj);
950 callback = (sdb_plugin_init_cb)cb->cb_callback;
952 old_ctx = ctx_set(cb->cb_ctx);
953 if (callback(cb->cb_user_data)) {
954 sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin "
955 "'%s'. Unregistering all callbacks.", obj->name);
956 ctx_set(old_ctx);
957 plugin_unregister_by_name(cb->cb_ctx->info.plugin_name);
958 ++ret;
959 }
960 else
961 ctx_set(old_ctx);
962 }
963 sdb_llist_iter_destroy(iter);
964 return ret;
965 } /* sdb_plugin_init_all */
967 int
968 sdb_plugin_shutdown_all(void)
969 {
970 sdb_llist_iter_t *iter;
971 int ret = 0;
973 iter = sdb_llist_get_iter(shutdown_list);
974 while (sdb_llist_iter_has_next(iter)) {
975 sdb_plugin_cb_t *cb;
976 sdb_plugin_shutdown_cb callback;
977 ctx_t *old_ctx;
979 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
980 assert(obj);
981 cb = SDB_PLUGIN_CB(obj);
983 callback = (sdb_plugin_shutdown_cb)cb->cb_callback;
985 old_ctx = ctx_set(cb->cb_ctx);
986 if (callback(cb->cb_user_data)) {
987 sdb_log(SDB_LOG_ERR, "core: Failed to shutdown plugin '%s'.",
988 obj->name);
989 ++ret;
990 }
991 ctx_set(old_ctx);
992 }
993 sdb_llist_iter_destroy(iter);
994 return ret;
995 } /* sdb_plugin_shutdown_all */
997 int
998 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
999 {
1000 if (! collector_list) {
1001 sdb_log(SDB_LOG_WARNING, "core: No collectors registered. "
1002 "Quiting main loop.");
1003 return -1;
1004 }
1006 if (! loop)
1007 return -1;
1009 while (loop->do_loop) {
1010 sdb_plugin_collector_cb callback;
1011 ctx_t *old_ctx;
1013 sdb_time_t interval, now;
1015 sdb_object_t *obj = sdb_llist_shift(collector_list);
1016 if (! obj)
1017 return -1;
1019 callback = (sdb_plugin_collector_cb)SDB_PLUGIN_CCB(obj)->ccb_callback;
1021 if (! (now = sdb_gettime())) {
1022 char errbuf[1024];
1023 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1024 "time in collector main loop: %s",
1025 sdb_strerror(errno, errbuf, sizeof(errbuf)));
1026 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
1027 }
1029 if (now < SDB_PLUGIN_CCB(obj)->ccb_next_update) {
1030 interval = SDB_PLUGIN_CCB(obj)->ccb_next_update - now;
1032 errno = 0;
1033 while (loop->do_loop && sdb_sleep(interval, &interval)) {
1034 if (errno != EINTR) {
1035 char errbuf[1024];
1036 sdb_log(SDB_LOG_ERR, "core: Failed to sleep "
1037 "in collector main loop: %s",
1038 sdb_strerror(errno, errbuf, sizeof(errbuf)));
1039 sdb_llist_insert_sorted(collector_list, obj,
1040 plugin_cmp_next_update);
1041 sdb_object_deref(obj);
1042 return -1;
1043 }
1044 errno = 0;
1045 }
1047 if (! loop->do_loop) {
1048 /* put back; don't worry about errors */
1049 sdb_llist_insert_sorted(collector_list, obj,
1050 plugin_cmp_next_update);
1051 sdb_object_deref(obj);
1052 return 0;
1053 }
1054 }
1056 old_ctx = ctx_set(SDB_PLUGIN_CCB(obj)->ccb_ctx);
1057 if (callback(SDB_PLUGIN_CCB(obj)->ccb_user_data)) {
1058 /* XXX */
1059 }
1060 ctx_set(old_ctx);
1062 interval = SDB_PLUGIN_CCB(obj)->ccb_interval;
1063 if (! interval)
1064 interval = loop->default_interval;
1065 if (! interval) {
1066 sdb_log(SDB_LOG_WARNING, "core: No interval configured "
1067 "for plugin '%s'; skipping any further "
1068 "iterations.", obj->name);
1069 sdb_object_deref(obj);
1070 continue;
1071 }
1073 SDB_PLUGIN_CCB(obj)->ccb_next_update += interval;
1075 if (! (now = sdb_gettime())) {
1076 char errbuf[1024];
1077 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1078 "time in collector main loop: %s",
1079 sdb_strerror(errno, errbuf, sizeof(errbuf)));
1080 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
1081 }
1083 if (now > SDB_PLUGIN_CCB(obj)->ccb_next_update) {
1084 sdb_log(SDB_LOG_WARNING, "core: Plugin '%s' took too "
1085 "long; skipping iterations to keep up.",
1086 obj->name);
1087 SDB_PLUGIN_CCB(obj)->ccb_next_update = now;
1088 }
1090 if (sdb_llist_insert_sorted(collector_list, obj,
1091 plugin_cmp_next_update)) {
1092 sdb_log(SDB_LOG_ERR, "core: Failed to re-insert "
1093 "plugin '%s' into collector list. Unable to further "
1094 "use the plugin.",
1095 obj->name);
1096 sdb_object_deref(obj);
1097 return -1;
1098 }
1100 /* pass control back to the list */
1101 sdb_object_deref(obj);
1102 }
1103 return 0;
1104 } /* sdb_plugin_read_loop */
1106 char *
1107 sdb_plugin_cname(char *hostname)
1108 {
1109 sdb_llist_iter_t *iter;
1111 if (! hostname)
1112 return NULL;
1114 if (! cname_list)
1115 return hostname;
1117 iter = sdb_llist_get_iter(cname_list);
1118 while (sdb_llist_iter_has_next(iter)) {
1119 sdb_plugin_cname_cb callback;
1120 char *cname;
1122 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1123 assert(obj);
1125 callback = (sdb_plugin_cname_cb)SDB_PLUGIN_CB(obj)->cb_callback;
1126 cname = callback(hostname, SDB_PLUGIN_CB(obj)->cb_user_data);
1127 if (cname) {
1128 free(hostname);
1129 hostname = cname;
1130 }
1131 /* else: don't change hostname */
1132 }
1133 sdb_llist_iter_destroy(iter);
1134 return hostname;
1135 } /* sdb_plugin_cname */
1137 int
1138 sdb_plugin_log(int prio, const char *msg)
1139 {
1140 sdb_llist_iter_t *iter;
1141 int ret = -1;
1143 _Bool logged = 0;
1145 if (! msg)
1146 return 0;
1148 iter = sdb_llist_get_iter(log_list);
1149 while (sdb_llist_iter_has_next(iter)) {
1150 sdb_plugin_log_cb callback;
1151 int tmp;
1153 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1154 assert(obj);
1156 callback = (sdb_plugin_log_cb)SDB_PLUGIN_CB(obj)->cb_callback;
1157 tmp = callback(prio, msg, SDB_PLUGIN_CB(obj)->cb_user_data);
1158 if (tmp > ret)
1159 ret = tmp;
1161 if (SDB_PLUGIN_CB(obj)->cb_ctx)
1162 logged = 1;
1163 /* else: this is an internally registered callback */
1164 }
1165 sdb_llist_iter_destroy(iter);
1167 if (! logged)
1168 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
1169 return ret;
1170 } /* sdb_plugin_log */
1172 int
1173 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
1174 {
1175 sdb_strbuf_t *buf;
1176 int ret;
1178 if (! fmt)
1179 return 0;
1181 buf = sdb_strbuf_create(64);
1182 if (! buf) {
1183 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
1184 ret += vfprintf(stderr, fmt, ap);
1185 return ret;
1186 }
1188 if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
1189 sdb_strbuf_destroy(buf);
1190 return -1;
1191 }
1193 ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
1194 sdb_strbuf_destroy(buf);
1195 return ret;
1196 } /* sdb_plugin_vlogf */
1198 int
1199 sdb_plugin_logf(int prio, const char *fmt, ...)
1200 {
1201 va_list ap;
1202 int ret;
1204 if (! fmt)
1205 return 0;
1207 va_start(ap, fmt);
1208 ret = sdb_plugin_vlogf(prio, fmt, ap);
1209 va_end(ap);
1210 return ret;
1211 } /* sdb_plugin_logf */
1213 sdb_timeseries_t *
1214 sdb_plugin_fetch_timeseries(const char *type, const char *id,
1215 sdb_timeseries_opts_t *opts)
1216 {
1217 sdb_plugin_cb_t *plugin;
1218 sdb_plugin_fetch_ts_cb callback;
1219 sdb_timeseries_t *ts;
1221 ctx_t *old_ctx;
1223 if ((! type) || (! id) || (! opts))
1224 return NULL;
1226 plugin = SDB_PLUGIN_CB(sdb_llist_search_by_name(ts_fetcher_list, type));
1227 if (! plugin) {
1228 sdb_log(SDB_LOG_ERR, "core: Cannot fetch time-series of type %s: "
1229 "no such plugin loaded", type);
1230 errno = ENOENT;
1231 return NULL;
1232 }
1234 old_ctx = ctx_set(plugin->cb_ctx);
1235 callback = (sdb_plugin_fetch_ts_cb)plugin->cb_callback;
1236 ts = callback(id, opts, plugin->cb_user_data);
1237 ctx_set(old_ctx);
1238 return ts;
1239 } /* sdb_plugin_fetch_timeseries */
1241 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */