Code

plugin: The replaced SDB_PLUGIN_INFO_NAME with the actual plugin name.
[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)\n\t%s\n\tLicense: %s",
524                         ctx->info.plugin_name, ctx->info.plugin_version,
525                         INFO_GET(&ctx->info, description),
526                         INFO_GET(&ctx->info, copyright),
527                         INFO_GET(&ctx->info, license));
529         /* any registered callbacks took ownership of the context */
530         sdb_object_deref(SDB_OBJ(ctx));
532         /* reset */
533         ctx_set(NULL);
534         return 0;
535 } /* module_load */
537 static char *
538 plugin_get_name(const char *name, char *buf, size_t bufsize)
540         ctx_t *ctx = ctx_get();
542         if (ctx)
543                 snprintf(buf, bufsize, "%s::%s", ctx->info.plugin_name, name);
544         else
545                 snprintf(buf, bufsize, "core::%s", name);
546         return buf;
547 } /* plugin_get_name */
549 static int
550 plugin_add_callback(sdb_llist_t **list, const char *type,
551                 const char *name, void *callback, sdb_object_t *user_data)
553         sdb_object_t *obj;
555         if ((! name) || (! callback))
556                 return -1;
558         assert(list);
560         if (! *list)
561                 *list = sdb_llist_create();
562         if (! *list)
563                 return -1;
565         obj = sdb_object_create(name, sdb_plugin_cb_type,
566                         list, type, callback, user_data);
567         if (! obj)
568                 return -1;
570         if (sdb_llist_append(*list, obj)) {
571                 sdb_object_deref(obj);
572                 return -1;
573         }
575         /* pass control to the list */
576         sdb_object_deref(obj);
578         sdb_log(SDB_LOG_INFO, "core: Registered %s callback '%s'.",
579                         type, name);
580         return 0;
581 } /* plugin_add_callback */
583 /*
584  * public API
585  */
587 int
588 sdb_plugin_load(const char *basedir, const char *name,
589                 const sdb_plugin_ctx_t *plugin_ctx)
591         ctx_t *ctx;
593         int status;
595         if ((! name) || (! *name))
596                 return -1;
598         if (! all_plugins) {
599                 if (! (all_plugins = sdb_llist_create())) {
600                         sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
601                                         "internal error while creating linked list", name);
602                         return -1;
603                 }
604         }
606         ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
607         if (ctx) {
608                 /* plugin already loaded */
609                 if (! ctx->use_cnt) {
610                         /* reloading plugin */
611                         ctx_t *old_ctx = ctx_set(ctx);
613                         status = module_init(ctx->info.plugin_name, ctx->handle, NULL);
614                         if (status)
615                                 return status;
617                         sdb_log(SDB_LOG_INFO, "core: Successfully reloaded plugin "
618                                         "'%s' (%s)", ctx->info.plugin_name,
619                                         INFO_GET(&ctx->info, description));
620                         ctx_set(old_ctx);
621                 }
622                 ++ctx->use_cnt;
623                 return 0;
624         }
626         return module_load(basedir, name, plugin_ctx);
627 } /* sdb_plugin_load */
629 int
630 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
632         va_list ap;
634         if (! info)
635                 return -1;
637         va_start(ap, type);
639         switch (type) {
640                 case SDB_PLUGIN_INFO_DESC:
641                         {
642                                 char *desc = va_arg(ap, char *);
643                                 if (desc) {
644                                         if (info->description)
645                                                 free(info->description);
646                                         info->description = strdup(desc);
647                                 }
648                         }
649                         break;
650                 case SDB_PLUGIN_INFO_COPYRIGHT:
651                         {
652                                 char *copyright = va_arg(ap, char *);
653                                 if (copyright)
654                                         info->copyright = strdup(copyright);
655                         }
656                         break;
657                 case SDB_PLUGIN_INFO_LICENSE:
658                         {
659                                 char *license = va_arg(ap, char *);
660                                 if (license) {
661                                         if (info->license)
662                                                 free(info->license);
663                                         info->license = strdup(license);
664                                 }
665                         }
666                         break;
667                 case SDB_PLUGIN_INFO_VERSION:
668                         {
669                                 int version = va_arg(ap, int);
670                                 info->version = version;
671                         }
672                         break;
673                 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
674                         {
675                                 int version = va_arg(ap, int);
676                                 info->plugin_version = version;
677                         }
678                         break;
679                 default:
680                         va_end(ap);
681                         return -1;
682         }
684         va_end(ap);
685         return 0;
686 } /* sdb_plugin_set_info */
688 int
689 sdb_plugin_register_config(sdb_plugin_config_cb callback)
691         ctx_t *ctx = ctx_get();
693         if (! ctx) {
694                 sdb_log(SDB_LOG_ERR, "core: Invalid attempt to register a "
695                                 "config callback from outside a plugin");
696                 return -1;
697         }
698         return plugin_add_callback(&config_list, "init", ctx->info.plugin_name,
699                         (void *)callback, NULL);
700 } /* sdb_plugin_register_config */
702 int
703 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
704                 sdb_object_t *user_data)
706         char cb_name[1024];
707         return plugin_add_callback(&init_list, "init",
708                         plugin_get_name(name, cb_name, sizeof(cb_name)),
709                         (void *)callback, user_data);
710 } /* sdb_plugin_register_init */
712 int
713 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
714                 sdb_object_t *user_data)
716         char cb_name[1024];
717         return plugin_add_callback(&shutdown_list, "shutdown",
718                         plugin_get_name(name, cb_name, sizeof(cb_name)),
719                         (void *)callback, user_data);
720 } /* sdb_plugin_register_shutdown */
722 int
723 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
724                 sdb_object_t *user_data)
726         char cb_name[1024];
727         return plugin_add_callback(&log_list, "log",
728                         plugin_get_name(name, cb_name, sizeof(cb_name)),
729                         callback, user_data);
730 } /* sdb_plugin_register_log */
732 int
733 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
734                 sdb_object_t *user_data)
736         char cb_name[1024];
737         return plugin_add_callback(&cname_list, "cname",
738                         plugin_get_name(name, cb_name, sizeof(cb_name)),
739                         callback, user_data);
740 } /* sdb_plugin_register_cname */
742 int
743 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
744                 const sdb_time_t *interval, sdb_object_t *user_data)
746         char cb_name[1024];
747         sdb_object_t *obj;
749         if ((! name) || (! callback))
750                 return -1;
752         if (! collector_list)
753                 collector_list = sdb_llist_create();
754         if (! collector_list)
755                 return -1;
757         plugin_get_name(name, cb_name, sizeof(cb_name));
759         obj = sdb_object_create(cb_name, sdb_plugin_collector_cb_type,
760                         &collector_list, "collector", callback, user_data);
761         if (! obj)
762                 return -1;
764         if (interval)
765                 SDB_PLUGIN_CCB(obj)->ccb_interval = *interval;
766         else {
767                 sdb_time_t tmp = sdb_plugin_get_ctx().interval;
769                 if (tmp > 0)
770                         SDB_PLUGIN_CCB(obj)->ccb_interval = tmp;
771                 else
772                         SDB_PLUGIN_CCB(obj)->ccb_interval = 0;
773         }
775         if (! (SDB_PLUGIN_CCB(obj)->ccb_next_update = sdb_gettime())) {
776                 char errbuf[1024];
777                 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
778                                 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
779                 sdb_object_deref(obj);
780                 return -1;
781         }
783         if (sdb_llist_insert_sorted(collector_list, obj,
784                                 plugin_cmp_next_update)) {
785                 sdb_object_deref(obj);
786                 return -1;
787         }
789         /* pass control to the list */
790         sdb_object_deref(obj);
792         sdb_log(SDB_LOG_INFO, "core: Registered collector callback '%s' "
793                         "(interval = %.3fs).", cb_name,
794                         SDB_TIME_TO_DOUBLE(SDB_PLUGIN_CCB(obj)->ccb_interval));
795         return 0;
796 } /* sdb_plugin_register_collector */
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 int
831 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
833         sdb_plugin_cb_t *plugin;
834         sdb_plugin_config_cb callback;
836         ctx_t *old_ctx;
838         int status;
840         if ((! name) || (! ci))
841                 return -1;
843         plugin = SDB_PLUGIN_CB(sdb_llist_search_by_name(config_list, name));
844         if (! plugin) {
845                 ctx_t *ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
846                 if (! ctx)
847                         sdb_log(SDB_LOG_ERR, "core: Cannot configure unknown "
848                                         "plugin '%s'. Missing 'LoadPlugin \"%s\"'?",
849                                         name, name);
850                 else
851                         sdb_log(SDB_LOG_ERR, "core: Plugin '%s' did not register "
852                                         "a config callback.", name);
853                 errno = ENOENT;
854                 return -1;
855         }
857         old_ctx = ctx_set(plugin->cb_ctx);
858         callback = (sdb_plugin_config_cb)plugin->cb_callback;
859         status = callback(ci);
860         ctx_set(old_ctx);
861         return status;
862 } /* sdb_plugin_configure */
864 int
865 sdb_plugin_reconfigure_init(void)
867         sdb_llist_iter_t *iter;
869         iter = sdb_llist_get_iter(config_list);
870         if (config_list && (! iter))
871                 return -1;
873         /* deconfigure all plugins */
874         while (sdb_llist_iter_has_next(iter)) {
875                 sdb_plugin_cb_t *plugin;
876                 sdb_plugin_config_cb callback;
877                 ctx_t *old_ctx;
879                 plugin = SDB_PLUGIN_CB(sdb_llist_iter_get_next(iter));
880                 old_ctx = ctx_set(plugin->cb_ctx);
881                 callback = (sdb_plugin_config_cb)plugin->cb_callback;
882                 callback(NULL);
883                 ctx_set(old_ctx);
884         }
885         sdb_llist_iter_destroy(iter);
887         iter = sdb_llist_get_iter(all_plugins);
888         if (all_plugins && (! iter))
889                 return -1;
891         /* record all plugins as being unused */
892         while (sdb_llist_iter_has_next(iter))
893                 CTX(sdb_llist_iter_get_next(iter))->use_cnt = 0;
894         sdb_llist_iter_destroy(iter);
896         plugin_unregister_all();
897         return 0;
898 } /* sdb_plugin_reconfigure_init */
900 int
901 sdb_plugin_reconfigure_finish(void)
903         sdb_llist_iter_t *iter;
905         iter = sdb_llist_get_iter(all_plugins);
906         if (all_plugins && (! iter))
907                 return -1;
909         while (sdb_llist_iter_has_next(iter)) {
910                 ctx_t *ctx = CTX(sdb_llist_iter_get_next(iter));
911                 if (ctx->use_cnt)
912                         continue;
914                 sdb_log(SDB_LOG_INFO, "core: Module %s no longer in use",
915                                 ctx->info.plugin_name);
916                 sdb_llist_iter_remove_current(iter);
917                 plugin_unregister_by_name(ctx->info.plugin_name);
918                 sdb_object_deref(SDB_OBJ(ctx));
919         }
920         sdb_llist_iter_destroy(iter);
921         return 0;
922 } /* sdb_plugin_reconfigure_finish */
924 int
925 sdb_plugin_init_all(void)
927         sdb_llist_iter_t *iter;
928         int ret = 0;
930         iter = sdb_llist_get_iter(init_list);
931         while (sdb_llist_iter_has_next(iter)) {
932                 sdb_plugin_cb_t *cb;
933                 sdb_plugin_init_cb callback;
934                 ctx_t *old_ctx;
936                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
937                 assert(obj);
938                 cb = SDB_PLUGIN_CB(obj);
940                 callback = (sdb_plugin_init_cb)cb->cb_callback;
942                 old_ctx = ctx_set(cb->cb_ctx);
943                 if (callback(cb->cb_user_data)) {
944                         sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin "
945                                         "'%s'. Unregistering all callbacks.", obj->name);
946                         ctx_set(old_ctx);
947                         plugin_unregister_by_name(cb->cb_ctx->info.plugin_name);
948                         ++ret;
949                 }
950                 else
951                         ctx_set(old_ctx);
952         }
953         sdb_llist_iter_destroy(iter);
954         return ret;
955 } /* sdb_plugin_init_all */
957 int
958 sdb_plugin_shutdown_all(void)
960         sdb_llist_iter_t *iter;
961         int ret = 0;
963         iter = sdb_llist_get_iter(shutdown_list);
964         while (sdb_llist_iter_has_next(iter)) {
965                 sdb_plugin_cb_t *cb;
966                 sdb_plugin_shutdown_cb callback;
967                 ctx_t *old_ctx;
969                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
970                 assert(obj);
971                 cb = SDB_PLUGIN_CB(obj);
973                 callback = (sdb_plugin_shutdown_cb)cb->cb_callback;
975                 old_ctx = ctx_set(cb->cb_ctx);
976                 if (callback(cb->cb_user_data)) {
977                         sdb_log(SDB_LOG_ERR, "core: Failed to shutdown plugin '%s'.",
978                                         obj->name);
979                         ++ret;
980                 }
981                 ctx_set(old_ctx);
982         }
983         sdb_llist_iter_destroy(iter);
984         return ret;
985 } /* sdb_plugin_shutdown_all */
987 int
988 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
990         if (! collector_list) {
991                 sdb_log(SDB_LOG_WARNING, "core: No collectors registered. "
992                                 "Quiting main loop.");
993                 return -1;
994         }
996         if (! loop)
997                 return -1;
999         while (loop->do_loop) {
1000                 sdb_plugin_collector_cb callback;
1001                 ctx_t *old_ctx;
1003                 sdb_time_t interval, now;
1005                 sdb_object_t *obj = sdb_llist_shift(collector_list);
1006                 if (! obj)
1007                         return -1;
1009                 callback = (sdb_plugin_collector_cb)SDB_PLUGIN_CCB(obj)->ccb_callback;
1011                 if (! (now = sdb_gettime())) {
1012                         char errbuf[1024];
1013                         sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1014                                         "time in collector main loop: %s",
1015                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
1016                         now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
1017                 }
1019                 if (now < SDB_PLUGIN_CCB(obj)->ccb_next_update) {
1020                         interval = SDB_PLUGIN_CCB(obj)->ccb_next_update - now;
1022                         errno = 0;
1023                         while (loop->do_loop && sdb_sleep(interval, &interval)) {
1024                                 if (errno != EINTR) {
1025                                         char errbuf[1024];
1026                                         sdb_log(SDB_LOG_ERR, "core: Failed to sleep "
1027                                                         "in collector main loop: %s",
1028                                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
1029                                         sdb_llist_insert_sorted(collector_list, obj,
1030                                                         plugin_cmp_next_update);
1031                                         sdb_object_deref(obj);
1032                                         return -1;
1033                                 }
1034                                 errno = 0;
1035                         }
1037                         if (! loop->do_loop) {
1038                                 /* put back; don't worry about errors */
1039                                 sdb_llist_insert_sorted(collector_list, obj,
1040                                                 plugin_cmp_next_update);
1041                                 sdb_object_deref(obj);
1042                                 return 0;
1043                         }
1044                 }
1046                 old_ctx = ctx_set(SDB_PLUGIN_CCB(obj)->ccb_ctx);
1047                 if (callback(SDB_PLUGIN_CCB(obj)->ccb_user_data)) {
1048                         /* XXX */
1049                 }
1050                 ctx_set(old_ctx);
1052                 interval = SDB_PLUGIN_CCB(obj)->ccb_interval;
1053                 if (! interval)
1054                         interval = loop->default_interval;
1055                 if (! interval) {
1056                         sdb_log(SDB_LOG_WARNING, "core: No interval configured "
1057                                         "for plugin '%s'; skipping any further "
1058                                         "iterations.", obj->name);
1059                         sdb_object_deref(obj);
1060                         continue;
1061                 }
1063                 SDB_PLUGIN_CCB(obj)->ccb_next_update += interval;
1065                 if (! (now = sdb_gettime())) {
1066                         char errbuf[1024];
1067                         sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1068                                         "time in collector main loop: %s",
1069                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
1070                         now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
1071                 }
1073                 if (now > SDB_PLUGIN_CCB(obj)->ccb_next_update) {
1074                         sdb_log(SDB_LOG_WARNING, "core: Plugin '%s' took too "
1075                                         "long; skipping iterations to keep up.",
1076                                         obj->name);
1077                         SDB_PLUGIN_CCB(obj)->ccb_next_update = now;
1078                 }
1080                 if (sdb_llist_insert_sorted(collector_list, obj,
1081                                         plugin_cmp_next_update)) {
1082                         sdb_log(SDB_LOG_ERR, "core: Failed to re-insert "
1083                                         "plugin '%s' into collector list. Unable to further "
1084                                         "use the plugin.",
1085                                         obj->name);
1086                         sdb_object_deref(obj);
1087                         return -1;
1088                 }
1090                 /* pass control back to the list */
1091                 sdb_object_deref(obj);
1092         }
1093         return 0;
1094 } /* sdb_plugin_read_loop */
1096 char *
1097 sdb_plugin_cname(char *hostname)
1099         sdb_llist_iter_t *iter;
1101         if (! hostname)
1102                 return NULL;
1104         if (! cname_list)
1105                 return hostname;
1107         iter = sdb_llist_get_iter(cname_list);
1108         while (sdb_llist_iter_has_next(iter)) {
1109                 sdb_plugin_cname_cb callback;
1110                 char *cname;
1112                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1113                 assert(obj);
1115                 callback = (sdb_plugin_cname_cb)SDB_PLUGIN_CB(obj)->cb_callback;
1116                 cname = callback(hostname, SDB_PLUGIN_CB(obj)->cb_user_data);
1117                 if (cname) {
1118                         free(hostname);
1119                         hostname = cname;
1120                 }
1121                 /* else: don't change hostname */
1122         }
1123         sdb_llist_iter_destroy(iter);
1124         return hostname;
1125 } /* sdb_plugin_cname */
1127 int
1128 sdb_plugin_log(int prio, const char *msg)
1130         sdb_llist_iter_t *iter;
1131         int ret = -1;
1133         _Bool logged = 0;
1135         if (! msg)
1136                 return 0;
1138         iter = sdb_llist_get_iter(log_list);
1139         while (sdb_llist_iter_has_next(iter)) {
1140                 sdb_plugin_log_cb callback;
1141                 int tmp;
1143                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1144                 assert(obj);
1146                 callback = (sdb_plugin_log_cb)SDB_PLUGIN_CB(obj)->cb_callback;
1147                 tmp = callback(prio, msg, SDB_PLUGIN_CB(obj)->cb_user_data);
1148                 if (tmp > ret)
1149                         ret = tmp;
1151                 if (SDB_PLUGIN_CB(obj)->cb_ctx)
1152                         logged = 1;
1153                 /* else: this is an internally registered callback */
1154         }
1155         sdb_llist_iter_destroy(iter);
1157         if (! logged)
1158                 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
1159         return ret;
1160 } /* sdb_plugin_log */
1162 int
1163 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
1165         sdb_strbuf_t *buf;
1166         int ret;
1168         if (! fmt)
1169                 return 0;
1171         buf = sdb_strbuf_create(64);
1172         if (! buf) {
1173                 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
1174                 ret += vfprintf(stderr, fmt, ap);
1175                 return ret;
1176         }
1178         if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
1179                 sdb_strbuf_destroy(buf);
1180                 return -1;
1181         }
1183         ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
1184         sdb_strbuf_destroy(buf);
1185         return ret;
1186 } /* sdb_plugin_vlogf */
1188 int
1189 sdb_plugin_logf(int prio, const char *fmt, ...)
1191         va_list ap;
1192         int ret;
1194         if (! fmt)
1195                 return 0;
1197         va_start(ap, fmt);
1198         ret = sdb_plugin_vlogf(prio, fmt, ap);
1199         va_end(ap);
1200         return ret;
1201 } /* sdb_plugin_logf */
1203 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */