Code

Use stdbool.h's bool type instead of _Bool.
[sysdb.git] / src / core / plugin.c
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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, ...)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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, ...)
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)
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 : */