Code

Merged branch 'master' of git://git.tokkee.org/sysdb.
[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 /*
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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, ...)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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, ...)
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 : */