Code

8d836cc981de718a9f42cb270ec1b7f8ce66d783
[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 } callback_t;
84 #define CB_INIT { SDB_OBJECT_INIT, \
85         /* callback = */ NULL, /* user_data = */ NULL, \
86         SDB_PLUGIN_CTX_INIT }
87 #define CB(obj) ((callback_t *)(obj))
88 #define CONST_CB(obj) ((const callback_t *)(obj))
90 typedef struct {
91         callback_t super;
92 #define ccb_callback super.cb_callback
93 #define ccb_user_data super.cb_user_data
94 #define ccb_ctx super.cb_ctx
95         sdb_time_t ccb_interval;
96         sdb_time_t ccb_next_update;
97 } collector_t;
98 #define CCB(obj) ((collector_t *)(obj))
99 #define CONST_CCB(obj) ((const collector_t *)(obj))
101 typedef struct {
102         callback_t super; /* cb_callback will always be NULL */
103 #define w_user_data super.cb_user_data
104 #define w_ctx super.cb_ctx
105         sdb_store_writer_t impl;
106 } writer_t;
107 #define WRITER(obj) ((writer_t *)(obj))
109 typedef struct {
110         callback_t super; /* cb_callback will always be NULL */
111 #define r_user_data super.cb_user_data
112 #define r_ctx super.cb_ctx
113         sdb_store_reader_t impl;
114 } reader_t;
115 #define READER(obj) ((reader_t *)(obj))
117 typedef struct {
118         callback_t super; /* cb_callback will always be NULL */
119 #define ts_user_data super.cb_user_data
120 #define ts_ctx super.cb_ctx
121         sdb_timeseries_fetcher_t impl;
122 } ts_fetcher_t;
123 #define TS_FETCHER(obj) ((ts_fetcher_t *)(obj))
125 /*
126  * private variables
127  */
129 static sdb_plugin_ctx_t  plugin_default_ctx  = SDB_PLUGIN_CTX_INIT;
130 static sdb_plugin_info_t plugin_default_info = SDB_PLUGIN_INFO_INIT;
132 static pthread_key_t     plugin_ctx_key;
133 static bool              plugin_ctx_key_initialized = 0;
135 /* a list of the plugin contexts of all registered plugins */
136 static sdb_llist_t      *all_plugins = NULL;
138 static sdb_llist_t      *config_list = NULL;
139 static sdb_llist_t      *init_list = NULL;
140 static sdb_llist_t      *collector_list = NULL;
141 static sdb_llist_t      *cname_list = NULL;
142 static sdb_llist_t      *shutdown_list = NULL;
143 static sdb_llist_t      *log_list = NULL;
144 static sdb_llist_t      *ts_fetcher_list = NULL;
145 static sdb_llist_t      *timeseries_fetcher_list = NULL;
146 static sdb_llist_t      *writer_list = NULL;
147 static sdb_llist_t      *reader_list = NULL;
149 static struct {
150         const char   *type;
151         sdb_llist_t **list;
152 } all_lists[] = {
153         { "config",             &config_list },
154         { "init",               &init_list },
155         { "collector",          &collector_list },
156         { "cname",              &cname_list },
157         { "shutdown",           &shutdown_list },
158         { "log",                &log_list },
159         { "timeseries fetcher", &ts_fetcher_list },
160         { "timeseries fetcher", &timeseries_fetcher_list },
161         { "store writer",       &writer_list },
162         { "store reader",       &reader_list },
163 };
165 /*
166  * private helper functions
167  */
169 static void
170 plugin_info_clear(sdb_plugin_info_t *info)
172         sdb_plugin_info_t empty_info = SDB_PLUGIN_INFO_INIT;
173         if (! info)
174                 return;
176         if (info->plugin_name)
177                 free(info->plugin_name);
178         if (info->filename)
179                 free(info->filename);
181         if (info->description)
182                 free(info->description);
183         if (info->copyright)
184                 free(info->copyright);
185         if (info->license)
186                 free(info->license);
188         *info = empty_info;
189 } /* plugin_info_clear */
191 static void
192 ctx_key_init(void)
194         if (plugin_ctx_key_initialized)
195                 return;
197         pthread_key_create(&plugin_ctx_key, /* destructor */ NULL);
198         plugin_ctx_key_initialized = 1;
199 } /* ctx_key_init */
201 static int
202 plugin_cmp_next_update(const sdb_object_t *a, const sdb_object_t *b)
204         const collector_t *ccb1 = (const collector_t *)a;
205         const collector_t *ccb2 = (const collector_t *)b;
207         assert(ccb1 && ccb2);
209         return (ccb1->ccb_next_update > ccb2->ccb_next_update)
210                 ? 1 : (ccb1->ccb_next_update < ccb2->ccb_next_update)
211                 ? -1 : 0;
212 } /* plugin_cmp_next_update */
214 static int
215 plugin_lookup_by_name(const sdb_object_t *obj, const void *id)
217         const callback_t *cb = CONST_CB(obj);
218         const char *name = id;
220         assert(cb && id);
222         /* when a plugin was registered from outside a plugin (e.g. the core),
223          * we don't have a plugin context */
224         if (! cb->cb_ctx)
225                 return 1;
227         if (!strcasecmp(cb->cb_ctx->info.plugin_name, name))
228                 return 0;
229         return 1;
230 } /* plugin_lookup_by_name */
232 /* since this function is called from sdb_plugin_reconfigure_finish()
233  * when iterating through all_plugins, we may not do any additional
234  * modifications to all_plugins except for the optional removal */
235 static void
236 plugin_unregister_by_name(const char *plugin_name)
238         sdb_object_t *obj;
239         size_t i;
241         for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
242                 const char  *type =  all_lists[i].type;
243                 sdb_llist_t *list = *all_lists[i].list;
245                 while (1) {
246                         callback_t *cb;
248                         cb = CB(sdb_llist_remove(list,
249                                                 plugin_lookup_by_name, plugin_name));
250                         if (! cb)
251                                 break;
253                         assert(cb->cb_ctx);
255                         sdb_log(SDB_LOG_INFO, "core: Unregistering "
256                                         "%s callback '%s' (module %s)", type, cb->super.name,
257                                         cb->cb_ctx->info.plugin_name);
258                         sdb_object_deref(SDB_OBJ(cb));
259                 }
260         }
262         obj = sdb_llist_search_by_name(all_plugins, plugin_name);
263         /* when called from sdb_plugin_reconfigure_finish, the object has already
264          * been removed from the list */
265         if (obj && (obj->ref_cnt <= 1)) {
266                 sdb_llist_remove_by_name(all_plugins, plugin_name);
267                 sdb_object_deref(obj);
268         }
269         /* else: other callbacks still reference it */
270 } /* plugin_unregister_by_name */
272 /*
273  * private types
274  */
276 static int
277 ctx_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
279         ctx_t *ctx = CTX(obj);
281         assert(ctx);
283         ctx->public = plugin_default_ctx;
284         ctx->info = plugin_default_info;
285         ctx->handle = NULL;
286         ctx->use_cnt = 1;
287         return 0;
288 } /* ctx_init */
290 static void
291 ctx_destroy(sdb_object_t *obj)
293         ctx_t *ctx = CTX(obj);
295         if (ctx->handle) {
296                 const char *err;
298                 sdb_log(SDB_LOG_INFO, "core: Unloading module %s",
299                                 ctx->info.plugin_name);
301                 lt_dlerror();
302                 lt_dlclose(ctx->handle);
303                 if ((err = lt_dlerror()))
304                         sdb_log(SDB_LOG_WARNING, "core: Failed to unload module %s: %s",
305                                         ctx->info.plugin_name, err);
306         }
308         plugin_info_clear(&ctx->info);
309 } /* ctx_destroy */
311 static sdb_type_t ctx_type = {
312         sizeof(ctx_t),
314         ctx_init,
315         ctx_destroy
316 };
318 static ctx_t *
319 ctx_get(void)
321         if (! plugin_ctx_key_initialized)
322                 ctx_key_init();
323         return pthread_getspecific(plugin_ctx_key);
324 } /* ctx_get */
326 static ctx_t *
327 ctx_set(ctx_t *new)
329         ctx_t *old;
331         if (! plugin_ctx_key_initialized)
332                 ctx_key_init();
334         old = pthread_getspecific(plugin_ctx_key);
335         if (old)
336                 sdb_object_deref(SDB_OBJ(old));
337         if (new)
338                 sdb_object_ref(SDB_OBJ(new));
339         pthread_setspecific(plugin_ctx_key, new);
340         return old;
341 } /* ctx_set */
343 static ctx_t *
344 ctx_create(const char *name)
346         ctx_t *ctx;
348         ctx = CTX(sdb_object_create(name, ctx_type));
349         if (! ctx)
350                 return NULL;
352         if (! plugin_ctx_key_initialized)
353                 ctx_key_init();
354         ctx_set(ctx);
355         return ctx;
356 } /* ctx_create */
358 /*
359  * plugin_init_ok:
360  * Checks whether the registration of a new plugin identified by 'obj' is
361  * okay. It consumes the first two arguments of 'ap'.
362  */
363 static bool
364 plugin_init_ok(sdb_object_t *obj, va_list ap)
366         sdb_llist_t **list = va_arg(ap, sdb_llist_t **);
367         const char *type = va_arg(ap, const char *);
369         assert(list); assert(type);
371         if (sdb_llist_search_by_name(*list, obj->name)) {
372                 sdb_log(SDB_LOG_WARNING, "core: %s callback '%s' "
373                                 "has already been registered. Ignoring newly "
374                                 "registered version.", type, obj->name);
375                 return 0;
376         }
377         return 1;
378 } /* plugin_init_ok */
380 static int
381 plugin_cb_init(sdb_object_t *obj, va_list ap)
383         void *callback;
384         sdb_object_t *ud;
386         if (! plugin_init_ok(obj, ap))
387                 return -1;
389         callback = va_arg(ap, void *);
390         ud = va_arg(ap, sdb_object_t *);
392         /* cb_ctx may be NULL if the plugin was not registered by a plugin */
394         CB(obj)->cb_callback = callback;
395         CB(obj)->cb_ctx      = ctx_get();
396         sdb_object_ref(SDB_OBJ(CB(obj)->cb_ctx));
398         sdb_object_ref(ud);
399         CB(obj)->cb_user_data = ud;
400         return 0;
401 } /* plugin_cb_init */
403 static void
404 plugin_cb_destroy(sdb_object_t *obj)
406         assert(obj);
407         sdb_object_deref(CB(obj)->cb_user_data);
408         sdb_object_deref(SDB_OBJ(CB(obj)->cb_ctx));
409 } /* plugin_cb_destroy */
411 static sdb_type_t callback_type = {
412         sizeof(callback_t),
414         plugin_cb_init,
415         plugin_cb_destroy
416 };
418 static sdb_type_t collector_type = {
419         sizeof(collector_t),
421         plugin_cb_init,
422         plugin_cb_destroy
423 };
425 static int
426 plugin_writer_init(sdb_object_t *obj, va_list ap)
428         sdb_store_writer_t *impl;
429         sdb_object_t *ud;
431         if (! plugin_init_ok(obj, ap))
432                 return -1;
434         impl = va_arg(ap, sdb_store_writer_t *);
435         ud = va_arg(ap, sdb_object_t *);
436         assert(impl);
438         if ((! impl->store_host) || (! impl->store_service)
439                         || (! impl->store_metric) || (! impl->store_attribute)) {
440                 sdb_log(SDB_LOG_ERR, "core: store writer callback '%s' "
441                                 "does not fully implement the writer interface.",
442                                 obj->name);
443                 return -1;
444         }
446         /* ctx may be NULL if the callback was not registered by a plugin */
448         WRITER(obj)->impl = *impl;
449         WRITER(obj)->w_ctx  = ctx_get();
450         sdb_object_ref(SDB_OBJ(WRITER(obj)->w_ctx));
452         sdb_object_ref(ud);
453         WRITER(obj)->w_user_data = ud;
454         return 0;
455 } /* plugin_writer_init */
457 static void
458 plugin_writer_destroy(sdb_object_t *obj)
460         assert(obj);
461         sdb_object_deref(WRITER(obj)->w_user_data);
462         sdb_object_deref(SDB_OBJ(WRITER(obj)->w_ctx));
463 } /* plugin_writer_destroy */
465 static sdb_type_t writer_type = {
466         sizeof(writer_t),
468         plugin_writer_init,
469         plugin_writer_destroy
470 };
472 static int
473 plugin_reader_init(sdb_object_t *obj, va_list ap)
475         sdb_store_reader_t *impl;
476         sdb_object_t *ud;
478         if (! plugin_init_ok(obj, ap))
479                 return -1;
481         impl = va_arg(ap, sdb_store_reader_t *);
482         ud = va_arg(ap, sdb_object_t *);
483         assert(impl);
485         if ((! impl->prepare_query) || (! impl->execute_query)) {
486                 sdb_log(SDB_LOG_ERR, "core: store reader callback '%s' "
487                                 "does not fully implement the reader interface.",
488                                 obj->name);
489                 return -1;
490         }
492         /* ctx may be NULL if the callback was not registered by a plugin */
494         READER(obj)->impl = *impl;
495         READER(obj)->r_ctx  = ctx_get();
496         sdb_object_ref(SDB_OBJ(READER(obj)->r_ctx));
498         sdb_object_ref(ud);
499         READER(obj)->r_user_data = ud;
500         return 0;
501 } /* plugin_reader_init */
503 static void
504 plugin_reader_destroy(sdb_object_t *obj)
506         assert(obj);
507         sdb_object_deref(READER(obj)->r_user_data);
508         sdb_object_deref(SDB_OBJ(READER(obj)->r_ctx));
509 } /* plugin_reader_destroy */
511 static sdb_type_t reader_type = {
512         sizeof(reader_t),
514         plugin_reader_init,
515         plugin_reader_destroy
516 };
518 static int
519 plugin_ts_fetcher_init(sdb_object_t *obj, va_list ap)
521         sdb_timeseries_fetcher_t *impl;
522         sdb_object_t *ud;
524         if (! plugin_init_ok(obj, ap))
525                 return -1;
527         impl = va_arg(ap, sdb_timeseries_fetcher_t *);
528         ud = va_arg(ap, sdb_object_t *);
529         assert(impl);
531         if ((! impl->describe) || (! impl->fetch)) {
532                 sdb_log(SDB_LOG_ERR, "core: timeseries fetcher callback '%s' "
533                                 "does not fully implement the interface.",
534                                 obj->name);
535                 return -1;
536         }
538         /* ctx may be NULL if the callback was not registered by a plugin */
540         TS_FETCHER(obj)->impl = *impl;
541         TS_FETCHER(obj)->ts_ctx  = ctx_get();
542         sdb_object_ref(SDB_OBJ(TS_FETCHER(obj)->ts_ctx));
544         sdb_object_ref(ud);
545         TS_FETCHER(obj)->ts_user_data = ud;
546         return 0;
547 } /* plugin_ts_fetcher_init */
549 static void
550 plugin_ts_fetcher_destroy(sdb_object_t *obj)
552         assert(obj);
553         sdb_object_deref(TS_FETCHER(obj)->ts_user_data);
554         sdb_object_deref(SDB_OBJ(TS_FETCHER(obj)->ts_ctx));
555 } /* plugin_ts_fetcher_destroy */
557 static sdb_type_t ts_fetcher_type = {
558         sizeof(ts_fetcher_t),
560         plugin_ts_fetcher_init,
561         plugin_ts_fetcher_destroy
562 };
564 static int
565 module_init(const char *name, lt_dlhandle lh, sdb_plugin_info_t *info)
567         int (*mod_init)(sdb_plugin_info_t *);
568         int status;
570         mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
571         if (! mod_init) {
572                 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
573                                 "could not find symbol 'sdb_module_init'", name);
574                 return -1;
575         }
577         status = mod_init(info);
578         if (status) {
579                 sdb_log(SDB_LOG_ERR, "core: Failed to initialize "
580                                 "module '%s'", name);
581                 plugin_unregister_by_name(name);
582                 return -1;
583         }
584         return 0;
585 } /* module_init */
587 static int
588 module_load(const char *basedir, const char *name,
589                 const sdb_plugin_ctx_t *plugin_ctx)
591         char  base_name[name ? strlen(name) + 1 : 1];
592         const char *name_ptr;
593         char *tmp;
595         char filename[1024];
596         lt_dlhandle lh;
598         ctx_t *ctx;
600         int status;
602         assert(name);
604         base_name[0] = '\0';
605         name_ptr = name;
607         while ((tmp = strstr(name_ptr, "::"))) {
608                 strncat(base_name, name_ptr, (size_t)(tmp - name_ptr));
609                 strcat(base_name, "/");
610                 name_ptr = tmp + strlen("::");
611         }
612         strcat(base_name, name_ptr);
614         if (! basedir)
615                 basedir = PKGLIBDIR;
617         snprintf(filename, sizeof(filename), "%s/%s.so", basedir, base_name);
618         filename[sizeof(filename) - 1] = '\0';
620         if (access(filename, R_OK)) {
621                 char errbuf[1024];
622                 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s' (%s): %s",
623                                 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
624                 return -1;
625         }
627         lt_dlinit();
628         lt_dlerror();
630         lh = lt_dlopen(filename);
631         if (! lh) {
632                 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': %s"
633                                 "The most common cause for this problem are missing "
634                                 "dependencies.\n", name, lt_dlerror());
635                 return -1;
636         }
638         if (ctx_get())
639                 sdb_log(SDB_LOG_WARNING, "core: Discarding old plugin context");
641         ctx = ctx_create(name);
642         if (! ctx) {
643                 sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin context");
644                 return -1;
645         }
647         ctx->info.plugin_name = strdup(name);
648         ctx->info.filename = strdup(filename);
649         ctx->handle = lh;
651         if (plugin_ctx)
652                 ctx->public = *plugin_ctx;
654         if ((status = module_init(name, lh, &ctx->info))) {
655                 sdb_object_deref(SDB_OBJ(ctx));
656                 return status;
657         }
659         /* compare minor version */
660         if ((ctx->info.version < 0)
661                         || ((int)(ctx->info.version / 100) != (int)(SDB_VERSION / 100)))
662                 sdb_log(SDB_LOG_WARNING, "core: WARNING: version of "
663                                 "plugin '%s' (%i.%i.%i) does not match our version "
664                                 "(%i.%i.%i); this might cause problems",
665                                 name, SDB_VERSION_DECODE(ctx->info.version),
666                                 SDB_VERSION_DECODE(SDB_VERSION));
668         sdb_llist_append(all_plugins, SDB_OBJ(ctx));
670         sdb_log(SDB_LOG_INFO, "core: Successfully loaded "
671                         "plugin %s v%i (%s)", ctx->info.plugin_name,
672                         ctx->info.plugin_version,
673                         INFO_GET(&ctx->info, description));
674         sdb_log(SDB_LOG_INFO, "core: Plugin %s: %s, License: %s",
675                         ctx->info.plugin_name,
676                         INFO_GET(&ctx->info, copyright),
677                         INFO_GET(&ctx->info, license));
679         /* any registered callbacks took ownership of the context */
680         sdb_object_deref(SDB_OBJ(ctx));
682         /* reset */
683         ctx_set(NULL);
684         return 0;
685 } /* module_load */
687 static char *
688 plugin_get_name(const char *name, char *buf, size_t bufsize)
690         ctx_t *ctx = ctx_get();
692         if (ctx)
693                 snprintf(buf, bufsize, "%s::%s", ctx->info.plugin_name, name);
694         else
695                 snprintf(buf, bufsize, "core::%s", name);
696         return buf;
697 } /* plugin_get_name */
699 static int
700 plugin_add_impl(sdb_llist_t **list, sdb_type_t T, const char *type,
701                 const char *name, void *impl, sdb_object_t *user_data)
703         sdb_object_t *obj;
705         if ((! name) || (! impl))
706                 return -1;
708         assert(list);
710         if (! *list)
711                 *list = sdb_llist_create();
712         if (! *list)
713                 return -1;
715         obj = sdb_object_create(name, T, list, type, impl, user_data);
716         if (! obj)
717                 return -1;
719         if (sdb_llist_append(*list, obj)) {
720                 sdb_object_deref(obj);
721                 return -1;
722         }
724         /* pass control to the list */
725         sdb_object_deref(obj);
727         sdb_log(SDB_LOG_INFO, "core: Registered %s callback '%s'.",
728                         type, name);
729         return 0;
730 } /* plugin_add_impl */
732 /*
733  * object meta-data
734  */
736 typedef struct {
737         int obj_type;
738         sdb_time_t last_update;
739         sdb_time_t interval;
740 } interval_fetcher_t;
742 static int
743 interval_fetcher_host(sdb_store_host_t *host, sdb_object_t *user_data)
745         interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
746         lu->obj_type = SDB_HOST;
747         lu->last_update = host->last_update;
748         return 0;
749 } /* interval_fetcher_host */
751 static int
752 interval_fetcher_service(sdb_store_service_t *svc, sdb_object_t *user_data)
754         interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
755         lu->obj_type = SDB_SERVICE;
756         lu->last_update = svc->last_update;
757         return 0;
758 } /* interval_fetcher_service */
760 static int
761 interval_fetcher_metric(sdb_store_metric_t *metric, sdb_object_t *user_data)
763         interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
764         lu->obj_type = SDB_METRIC;
765         lu->last_update = metric->last_update;
766         return 0;
767 } /* interval_fetcher_metric */
769 static int
770 interval_fetcher_attr(sdb_store_attribute_t *attr, sdb_object_t *user_data)
772         interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
773         lu->obj_type = SDB_ATTRIBUTE;
774         lu->last_update = attr->last_update;
775         return 0;
776 } /* interval_fetcher_attr */
778 static sdb_store_writer_t interval_fetcher = {
779         interval_fetcher_host, interval_fetcher_service,
780         interval_fetcher_metric, interval_fetcher_attr,
781 };
783 static int
784 get_interval(int obj_type, const char *hostname,
785                 int parent_type, const char *parent, const char *name,
786                 sdb_time_t last_update, sdb_time_t *interval_out)
788         sdb_ast_fetch_t fetch = SDB_AST_FETCH_INIT;
789         char hn[hostname ? strlen(hostname) + 1 : 1];
790         char pn[parent ? strlen(parent) + 1 : 1];
791         char n[strlen(name) + 1];
792         int status;
794         interval_fetcher_t lu = { 0, 0, 0 };
795         sdb_object_wrapper_t obj = SDB_OBJECT_WRAPPER_STATIC(&lu);
796         sdb_time_t interval;
798         assert(name);
800         if (hostname)
801                 strncpy(hn, hostname, sizeof(hn));
802         if (parent)
803                 strncpy(pn, parent, sizeof(pn));
804         strncpy(n, name, sizeof(n));
806         fetch.obj_type = obj_type;
807         fetch.hostname = hostname ? hn : NULL;
808         fetch.parent_type = parent_type;
809         fetch.parent = parent ? pn : NULL;
810         fetch.name = n;
812         status = sdb_plugin_query(SDB_AST_NODE(&fetch),
813                         &interval_fetcher, SDB_OBJ(&obj), NULL);
814         if ((status < 0) || (lu.obj_type != obj_type) || (lu.last_update == 0)) {
815                 *interval_out = 0;
816                 return 0;
817         }
819         if (lu.last_update >= last_update) {
820                 if (lu.last_update > last_update)
821                         sdb_log(SDB_LOG_DEBUG, "memstore: Cannot update %s '%s' - "
822                                         "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
823                                         SDB_STORE_TYPE_TO_NAME(obj_type), name,
824                                         lu.last_update, last_update);
825                 *interval_out = lu.interval;
826                 return 1;
827         }
829         interval = last_update - lu.last_update;
830         if (lu.interval && interval)
831                 interval = (sdb_time_t)((0.9 * (double)lu.interval)
832                                 + (0.1 * (double)interval));
833         *interval_out = interval;
834         return 0;
835 } /* get_interval */
837 static void
838 get_backend(char **backends, size_t *backends_num)
840         const sdb_plugin_info_t *info;
842         info = sdb_plugin_current();
843         if ((! info) || (! info->plugin_name) || (! *info->plugin_name)) {
844                 *backends_num = 0;
845                 return;
846         }
848         backends[0] = info->plugin_name;
849         *backends_num = 1;
850 } /* get_backend */
852 /*
853  * public API
854  */
856 int
857 sdb_plugin_load(const char *basedir, const char *name,
858                 const sdb_plugin_ctx_t *plugin_ctx)
860         ctx_t *ctx;
862         int status;
864         if ((! name) || (! *name))
865                 return -1;
867         if (! all_plugins) {
868                 if (! (all_plugins = sdb_llist_create())) {
869                         sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
870                                         "internal error while creating linked list", name);
871                         return -1;
872                 }
873         }
875         ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
876         if (ctx) {
877                 /* plugin already loaded */
878                 if (! ctx->use_cnt) {
879                         /* reloading plugin */
880                         ctx_t *old_ctx = ctx_set(ctx);
882                         status = module_init(ctx->info.plugin_name, ctx->handle, NULL);
883                         if (status)
884                                 return status;
886                         sdb_log(SDB_LOG_INFO, "core: Successfully reloaded plugin "
887                                         "'%s' (%s)", ctx->info.plugin_name,
888                                         INFO_GET(&ctx->info, description));
889                         ctx_set(old_ctx);
890                 }
891                 ++ctx->use_cnt;
892                 return 0;
893         }
895         return module_load(basedir, name, plugin_ctx);
896 } /* sdb_plugin_load */
898 int
899 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
901         va_list ap;
903         if (! info)
904                 return -1;
906         va_start(ap, type);
908         switch (type) {
909                 case SDB_PLUGIN_INFO_DESC:
910                         {
911                                 char *desc = va_arg(ap, char *);
912                                 if (desc) {
913                                         if (info->description)
914                                                 free(info->description);
915                                         info->description = strdup(desc);
916                                 }
917                         }
918                         break;
919                 case SDB_PLUGIN_INFO_COPYRIGHT:
920                         {
921                                 char *copyright = va_arg(ap, char *);
922                                 if (copyright)
923                                         info->copyright = strdup(copyright);
924                         }
925                         break;
926                 case SDB_PLUGIN_INFO_LICENSE:
927                         {
928                                 char *license = va_arg(ap, char *);
929                                 if (license) {
930                                         if (info->license)
931                                                 free(info->license);
932                                         info->license = strdup(license);
933                                 }
934                         }
935                         break;
936                 case SDB_PLUGIN_INFO_VERSION:
937                         {
938                                 int version = va_arg(ap, int);
939                                 info->version = version;
940                         }
941                         break;
942                 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
943                         {
944                                 int version = va_arg(ap, int);
945                                 info->plugin_version = version;
946                         }
947                         break;
948                 default:
949                         va_end(ap);
950                         return -1;
951         }
953         va_end(ap);
954         return 0;
955 } /* sdb_plugin_set_info */
957 int
958 sdb_plugin_register_config(sdb_plugin_config_cb callback)
960         ctx_t *ctx = ctx_get();
962         if (! ctx) {
963                 sdb_log(SDB_LOG_ERR, "core: Invalid attempt to register a "
964                                 "config callback from outside a plugin");
965                 return -1;
966         }
967         return plugin_add_impl(&config_list, callback_type, "config",
968                         ctx->info.plugin_name, (void *)callback, NULL);
969 } /* sdb_plugin_register_config */
971 int
972 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
973                 sdb_object_t *user_data)
975         char cb_name[1024];
976         return plugin_add_impl(&init_list, callback_type, "init",
977                         plugin_get_name(name, cb_name, sizeof(cb_name)),
978                         (void *)callback, user_data);
979 } /* sdb_plugin_register_init */
981 int
982 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
983                 sdb_object_t *user_data)
985         char cb_name[1024];
986         return plugin_add_impl(&shutdown_list, callback_type, "shutdown",
987                         plugin_get_name(name, cb_name, sizeof(cb_name)),
988                         (void *)callback, user_data);
989 } /* sdb_plugin_register_shutdown */
991 int
992 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
993                 sdb_object_t *user_data)
995         char cb_name[1024];
996         return plugin_add_impl(&log_list, callback_type, "log",
997                         plugin_get_name(name, cb_name, sizeof(cb_name)),
998                         callback, user_data);
999 } /* sdb_plugin_register_log */
1001 int
1002 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
1003                 sdb_object_t *user_data)
1005         char cb_name[1024];
1006         return plugin_add_impl(&cname_list, callback_type, "cname",
1007                         plugin_get_name(name, cb_name, sizeof(cb_name)),
1008                         callback, user_data);
1009 } /* sdb_plugin_register_cname */
1011 int
1012 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
1013                 const sdb_time_t *interval, sdb_object_t *user_data)
1015         char cb_name[1024];
1016         sdb_object_t *obj;
1018         if ((! name) || (! callback))
1019                 return -1;
1021         if (! collector_list)
1022                 collector_list = sdb_llist_create();
1023         if (! collector_list)
1024                 return -1;
1026         plugin_get_name(name, cb_name, sizeof(cb_name));
1028         obj = sdb_object_create(cb_name, collector_type,
1029                         &collector_list, "collector", callback, user_data);
1030         if (! obj)
1031                 return -1;
1033         if (interval)
1034                 CCB(obj)->ccb_interval = *interval;
1035         else {
1036                 ctx_t *ctx = ctx_get();
1038                 if (! ctx) {
1039                         sdb_log(SDB_LOG_ERR, "core: Cannot determine interval "
1040                                         "for collector %s; none specified and no plugin "
1041                                         "context found", cb_name);
1042                         return -1;
1043                 }
1045                 CCB(obj)->ccb_interval = ctx->public.interval;
1046         }
1048         if (! (CCB(obj)->ccb_next_update = sdb_gettime())) {
1049                 char errbuf[1024];
1050                 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1051                                 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
1052                 sdb_object_deref(obj);
1053                 return -1;
1054         }
1056         if (sdb_llist_insert_sorted(collector_list, obj,
1057                                 plugin_cmp_next_update)) {
1058                 sdb_object_deref(obj);
1059                 return -1;
1060         }
1062         /* pass control to the list */
1063         sdb_object_deref(obj);
1065         sdb_log(SDB_LOG_INFO, "core: Registered collector callback '%s' "
1066                         "(interval = %.3fs).", cb_name,
1067                         SDB_TIME_TO_DOUBLE(CCB(obj)->ccb_interval));
1068         return 0;
1069 } /* sdb_plugin_register_collector */
1071 int
1072 sdb_plugin_register_ts_fetcher(const char *name,
1073                 sdb_plugin_fetch_ts_cb callback, sdb_object_t *user_data)
1075         return plugin_add_impl(&ts_fetcher_list, callback_type, "time-series fetcher",
1076                         name, callback, user_data);
1077 } /* sdb_plugin_register_ts_fetcher */
1079 int
1080 sdb_plugin_register_timeseries_fetcher(const char *name,
1081                 sdb_timeseries_fetcher_t *fetcher, sdb_object_t *user_data)
1083         return plugin_add_impl(&timeseries_fetcher_list, ts_fetcher_type, "time-series fetcher",
1084                         name, fetcher, user_data);
1085 } /* sdb_plugin_register_timeseries_fetcher */
1087 int
1088 sdb_plugin_register_writer(const char *name,
1089                 sdb_store_writer_t *writer, sdb_object_t *user_data)
1091         char cb_name[1024];
1092         return plugin_add_impl(&writer_list, writer_type, "store writer",
1093                         plugin_get_name(name, cb_name, sizeof(cb_name)),
1094                         writer, user_data);
1095 } /* sdb_store_register_writer */
1097 int
1098 sdb_plugin_register_reader(const char *name,
1099                 sdb_store_reader_t *reader, sdb_object_t *user_data)
1101         char cb_name[1024];
1102         return plugin_add_impl(&reader_list, reader_type, "store reader",
1103                         plugin_get_name(name, cb_name, sizeof(cb_name)),
1104                         reader, user_data);
1105 } /* sdb_plugin_register_reader */
1107 void
1108 sdb_plugin_unregister_all(void)
1110         size_t i;
1112         for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
1113                 const char  *type =  all_lists[i].type;
1114                 sdb_llist_t *list = *all_lists[i].list;
1116                 size_t len = sdb_llist_len(list);
1118                 if (! len)
1119                         continue;
1121                 sdb_llist_clear(list);
1122                 sdb_log(SDB_LOG_INFO, "core: Unregistered %zu %s callback%s",
1123                                 len, type, len == 1 ? "" : "s");
1124         }
1125 } /* sdb_plugin_unregister_all */
1127 sdb_plugin_ctx_t
1128 sdb_plugin_get_ctx(void)
1130         ctx_t *c;
1132         c = ctx_get();
1133         if (! c) {
1134                 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid read access to plugin "
1135                                 "context outside a plugin");
1136                 return plugin_default_ctx;
1137         }
1138         return c->public;
1139 } /* sdb_plugin_get_ctx */
1141 int
1142 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx, sdb_plugin_ctx_t *old)
1144         ctx_t *c;
1146         c = ctx_get();
1147         if (! c) {
1148                 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid write access to plugin "
1149                                 "context outside a plugin");
1150                 return -1;
1151         }
1153         if (old)
1154                 *old = c->public;
1155         c->public = ctx;
1156         return 0;
1157 } /* sdb_plugin_set_ctx */
1159 const sdb_plugin_info_t *
1160 sdb_plugin_current(void)
1162         ctx_t *ctx = ctx_get();
1164         if (! ctx)
1165                 return NULL;
1166         return &ctx->info;
1167 } /* sdb_plugin_current */
1169 int
1170 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
1172         callback_t *plugin;
1173         sdb_plugin_config_cb callback;
1175         ctx_t *old_ctx;
1177         int status;
1179         if ((! name) || (! ci))
1180                 return -1;
1182         plugin = CB(sdb_llist_search_by_name(config_list, name));
1183         if (! plugin) {
1184                 ctx_t *ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
1185                 if (! ctx)
1186                         sdb_log(SDB_LOG_ERR, "core: Cannot configure unknown "
1187                                         "plugin '%s'. Missing 'LoadPlugin \"%s\"'?",
1188                                         name, name);
1189                 else
1190                         sdb_log(SDB_LOG_ERR, "core: Plugin '%s' did not register "
1191                                         "a config callback.", name);
1192                 errno = ENOENT;
1193                 return -1;
1194         }
1196         old_ctx = ctx_set(plugin->cb_ctx);
1197         callback = (sdb_plugin_config_cb)plugin->cb_callback;
1198         status = callback(ci);
1199         ctx_set(old_ctx);
1200         return status;
1201 } /* sdb_plugin_configure */
1203 int
1204 sdb_plugin_reconfigure_init(void)
1206         sdb_llist_iter_t *iter;
1208         iter = sdb_llist_get_iter(config_list);
1209         if (config_list && (! iter))
1210                 return -1;
1212         /* deconfigure all plugins */
1213         while (sdb_llist_iter_has_next(iter)) {
1214                 callback_t *plugin;
1215                 sdb_plugin_config_cb callback;
1216                 ctx_t *old_ctx;
1218                 plugin = CB(sdb_llist_iter_get_next(iter));
1219                 old_ctx = ctx_set(plugin->cb_ctx);
1220                 callback = (sdb_plugin_config_cb)plugin->cb_callback;
1221                 callback(NULL);
1222                 ctx_set(old_ctx);
1223         }
1224         sdb_llist_iter_destroy(iter);
1226         iter = sdb_llist_get_iter(all_plugins);
1227         if (all_plugins && (! iter))
1228                 return -1;
1230         /* record all plugins as being unused */
1231         while (sdb_llist_iter_has_next(iter))
1232                 CTX(sdb_llist_iter_get_next(iter))->use_cnt = 0;
1233         sdb_llist_iter_destroy(iter);
1235         sdb_plugin_unregister_all();
1236         return 0;
1237 } /* sdb_plugin_reconfigure_init */
1239 int
1240 sdb_plugin_reconfigure_finish(void)
1242         sdb_llist_iter_t *iter;
1244         iter = sdb_llist_get_iter(all_plugins);
1245         if (all_plugins && (! iter))
1246                 return -1;
1248         while (sdb_llist_iter_has_next(iter)) {
1249                 ctx_t *ctx = CTX(sdb_llist_iter_get_next(iter));
1250                 if (ctx->use_cnt)
1251                         continue;
1253                 sdb_log(SDB_LOG_INFO, "core: Module %s no longer in use",
1254                                 ctx->info.plugin_name);
1255                 sdb_llist_iter_remove_current(iter);
1256                 plugin_unregister_by_name(ctx->info.plugin_name);
1257                 sdb_object_deref(SDB_OBJ(ctx));
1258         }
1259         sdb_llist_iter_destroy(iter);
1260         return 0;
1261 } /* sdb_plugin_reconfigure_finish */
1263 int
1264 sdb_plugin_init_all(void)
1266         sdb_llist_iter_t *iter;
1267         int ret = 0;
1269         iter = sdb_llist_get_iter(init_list);
1270         while (sdb_llist_iter_has_next(iter)) {
1271                 callback_t *cb;
1272                 sdb_plugin_init_cb callback;
1273                 ctx_t *old_ctx;
1275                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1276                 assert(obj);
1277                 cb = CB(obj);
1279                 callback = (sdb_plugin_init_cb)cb->cb_callback;
1281                 old_ctx = ctx_set(cb->cb_ctx);
1282                 if (callback(cb->cb_user_data)) {
1283                         sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin "
1284                                         "'%s'. Unregistering all callbacks.", obj->name);
1285                         ctx_set(old_ctx);
1286                         plugin_unregister_by_name(cb->cb_ctx->info.plugin_name);
1287                         ++ret;
1288                 }
1289                 else
1290                         ctx_set(old_ctx);
1291         }
1292         sdb_llist_iter_destroy(iter);
1293         return ret;
1294 } /* sdb_plugin_init_all */
1296 int
1297 sdb_plugin_shutdown_all(void)
1299         sdb_llist_iter_t *iter;
1300         int ret = 0;
1302         iter = sdb_llist_get_iter(shutdown_list);
1303         while (sdb_llist_iter_has_next(iter)) {
1304                 callback_t *cb;
1305                 sdb_plugin_shutdown_cb callback;
1306                 ctx_t *old_ctx;
1308                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1309                 assert(obj);
1310                 cb = CB(obj);
1312                 callback = (sdb_plugin_shutdown_cb)cb->cb_callback;
1314                 old_ctx = ctx_set(cb->cb_ctx);
1315                 if (callback(cb->cb_user_data)) {
1316                         sdb_log(SDB_LOG_ERR, "core: Failed to shutdown plugin '%s'.",
1317                                         obj->name);
1318                         ++ret;
1319                 }
1320                 ctx_set(old_ctx);
1321         }
1322         sdb_llist_iter_destroy(iter);
1323         return ret;
1324 } /* sdb_plugin_shutdown_all */
1326 int
1327 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
1329         if (! collector_list) {
1330                 sdb_log(SDB_LOG_WARNING, "core: No collectors registered. "
1331                                 "Quiting main loop.");
1332                 return -1;
1333         }
1335         if (! loop)
1336                 return -1;
1338         while (loop->do_loop) {
1339                 sdb_plugin_collector_cb callback;
1340                 ctx_t *old_ctx;
1342                 sdb_time_t interval, now;
1344                 sdb_object_t *obj = sdb_llist_shift(collector_list);
1345                 if (! obj)
1346                         return -1;
1348                 callback = (sdb_plugin_collector_cb)CCB(obj)->ccb_callback;
1350                 if (! (now = sdb_gettime())) {
1351                         char errbuf[1024];
1352                         sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1353                                         "time in collector main loop: %s",
1354                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
1355                         now = CCB(obj)->ccb_next_update;
1356                 }
1358                 if (now < CCB(obj)->ccb_next_update) {
1359                         interval = CCB(obj)->ccb_next_update - now;
1361                         errno = 0;
1362                         while (loop->do_loop && sdb_sleep(interval, &interval)) {
1363                                 if (errno != EINTR) {
1364                                         char errbuf[1024];
1365                                         sdb_log(SDB_LOG_ERR, "core: Failed to sleep "
1366                                                         "in collector main loop: %s",
1367                                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
1368                                         sdb_llist_insert_sorted(collector_list, obj,
1369                                                         plugin_cmp_next_update);
1370                                         sdb_object_deref(obj);
1371                                         return -1;
1372                                 }
1373                                 errno = 0;
1374                         }
1376                         if (! loop->do_loop) {
1377                                 /* put back; don't worry about errors */
1378                                 sdb_llist_insert_sorted(collector_list, obj,
1379                                                 plugin_cmp_next_update);
1380                                 sdb_object_deref(obj);
1381                                 return 0;
1382                         }
1383                 }
1385                 old_ctx = ctx_set(CCB(obj)->ccb_ctx);
1386                 if (callback(CCB(obj)->ccb_user_data)) {
1387                         /* XXX */
1388                 }
1389                 ctx_set(old_ctx);
1391                 interval = CCB(obj)->ccb_interval;
1392                 if (! interval)
1393                         interval = loop->default_interval;
1394                 if (! interval) {
1395                         sdb_log(SDB_LOG_WARNING, "core: No interval configured "
1396                                         "for plugin '%s'; skipping any further "
1397                                         "iterations.", obj->name);
1398                         sdb_object_deref(obj);
1399                         continue;
1400                 }
1402                 CCB(obj)->ccb_next_update += interval;
1404                 if (! (now = sdb_gettime())) {
1405                         char errbuf[1024];
1406                         sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1407                                         "time in collector main loop: %s",
1408                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
1409                         now = CCB(obj)->ccb_next_update;
1410                 }
1412                 if (now > CCB(obj)->ccb_next_update) {
1413                         sdb_log(SDB_LOG_WARNING, "core: Plugin '%s' took too "
1414                                         "long; skipping iterations to keep up.",
1415                                         obj->name);
1416                         CCB(obj)->ccb_next_update = now;
1417                 }
1419                 if (sdb_llist_insert_sorted(collector_list, obj,
1420                                         plugin_cmp_next_update)) {
1421                         sdb_log(SDB_LOG_ERR, "core: Failed to re-insert "
1422                                         "plugin '%s' into collector list. Unable to further "
1423                                         "use the plugin.",
1424                                         obj->name);
1425                         sdb_object_deref(obj);
1426                         return -1;
1427                 }
1429                 /* pass control back to the list */
1430                 sdb_object_deref(obj);
1431         }
1432         return 0;
1433 } /* sdb_plugin_read_loop */
1435 char *
1436 sdb_plugin_cname(char *hostname)
1438         sdb_llist_iter_t *iter;
1440         if (! hostname)
1441                 return NULL;
1443         if (! cname_list)
1444                 return hostname;
1446         iter = sdb_llist_get_iter(cname_list);
1447         while (sdb_llist_iter_has_next(iter)) {
1448                 sdb_plugin_cname_cb callback;
1449                 char *cname;
1451                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1452                 assert(obj);
1454                 callback = (sdb_plugin_cname_cb)CB(obj)->cb_callback;
1455                 cname = callback(hostname, CB(obj)->cb_user_data);
1456                 if (cname) {
1457                         free(hostname);
1458                         hostname = cname;
1459                 }
1460                 /* else: don't change hostname */
1461         }
1462         sdb_llist_iter_destroy(iter);
1463         return hostname;
1464 } /* sdb_plugin_cname */
1466 int
1467 sdb_plugin_log(int prio, const char *msg)
1469         sdb_llist_iter_t *iter;
1470         int ret = -1;
1472         bool logged = 0;
1474         if (! msg)
1475                 return 0;
1477         iter = sdb_llist_get_iter(log_list);
1478         while (sdb_llist_iter_has_next(iter)) {
1479                 sdb_plugin_log_cb callback;
1480                 int tmp;
1482                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1483                 assert(obj);
1485                 callback = (sdb_plugin_log_cb)CB(obj)->cb_callback;
1486                 tmp = callback(prio, msg, CB(obj)->cb_user_data);
1487                 if (tmp > ret)
1488                         ret = tmp;
1490                 if (CB(obj)->cb_ctx)
1491                         logged = 1;
1492                 /* else: this is an internally registered callback */
1493         }
1494         sdb_llist_iter_destroy(iter);
1496         if (! logged)
1497                 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
1498         return ret;
1499 } /* sdb_plugin_log */
1501 int
1502 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
1504         sdb_strbuf_t *buf;
1505         int ret;
1507         if (! fmt)
1508                 return 0;
1510         buf = sdb_strbuf_create(64);
1511         if (! buf) {
1512                 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
1513                 ret += vfprintf(stderr, fmt, ap);
1514                 return ret;
1515         }
1517         if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
1518                 sdb_strbuf_destroy(buf);
1519                 return -1;
1520         }
1522         ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
1523         sdb_strbuf_destroy(buf);
1524         return ret;
1525 } /* sdb_plugin_vlogf */
1527 int
1528 sdb_plugin_logf(int prio, const char *fmt, ...)
1530         va_list ap;
1531         int ret;
1533         if (! fmt)
1534                 return 0;
1536         va_start(ap, fmt);
1537         ret = sdb_plugin_vlogf(prio, fmt, ap);
1538         va_end(ap);
1539         return ret;
1540 } /* sdb_plugin_logf */
1542 sdb_timeseries_t *
1543 sdb_plugin_fetch_timeseries(const char *type, const char *id,
1544                 sdb_timeseries_opts_t *opts)
1546         callback_t *plugin;
1547         sdb_plugin_fetch_ts_cb callback;
1549         ts_fetcher_t *fetcher;
1550         sdb_timeseries_t *ts;
1552         ctx_t *old_ctx;
1554         if ((! type) || (! id) || (! opts))
1555                 return NULL;
1557         fetcher = TS_FETCHER(sdb_llist_search_by_name(timeseries_fetcher_list, type));
1558         if (fetcher) {
1559                 old_ctx = ctx_set(fetcher->ts_ctx);
1560                 ts = fetcher->impl.fetch(id, opts, fetcher->ts_user_data);
1561                 ctx_set(old_ctx);
1562                 return ts;
1563         }
1565         /* fallback code */
1566         plugin = CB(sdb_llist_search_by_name(ts_fetcher_list, type));
1567         if (! plugin) {
1568                 sdb_log(SDB_LOG_ERR, "core: Cannot fetch time-series of type %s: "
1569                                 "no such plugin loaded", type);
1570                 errno = ENOENT;
1571                 return NULL;
1572         }
1574         old_ctx = ctx_set(plugin->cb_ctx);
1575         callback = (sdb_plugin_fetch_ts_cb)plugin->cb_callback;
1576         ts = callback(id, opts, plugin->cb_user_data);
1577         ctx_set(old_ctx);
1578         return ts;
1579 } /* sdb_plugin_fetch_timeseries */
1581 int
1582 sdb_plugin_query(sdb_ast_node_t *ast,
1583                 sdb_store_writer_t *w, sdb_object_t *wd, sdb_strbuf_t *errbuf)
1585         size_t n = sdb_llist_len(reader_list);
1586         reader_t *reader;
1587         sdb_object_t *q;
1588         int status = 0;
1590         if (! ast)
1591                 return 0;
1593         if ((ast->type != SDB_AST_TYPE_FETCH)
1594                         && (ast->type != SDB_AST_TYPE_LIST)
1595                         && (ast->type != SDB_AST_TYPE_LOOKUP)) {
1596                 sdb_log(SDB_LOG_ERR, "core: Cannot execute query of type %s",
1597                                 SDB_AST_TYPE_TO_STRING(ast));
1598                 sdb_strbuf_sprintf(errbuf, "Cannot execute query of type %s",
1599                                 SDB_AST_TYPE_TO_STRING(ast));
1600                 return -1;
1601         }
1603         if (n != 1) {
1604                 char *msg = (n > 0)
1605                         ? "Cannot execute query: multiple readers not supported"
1606                         : "Cannot execute query: no readers registered";
1607                 sdb_strbuf_sprintf(errbuf, "%s", msg);
1608                 sdb_log(SDB_LOG_ERR, "core: %s", msg);
1609                 return -1;
1610         }
1612         reader = READER(sdb_llist_get(reader_list, 0));
1613         assert(reader);
1615         q = reader->impl.prepare_query(ast, errbuf, reader->r_user_data);
1616         if (q)
1617                 status = reader->impl.execute_query(q, w, SDB_OBJ(wd),
1618                                 errbuf, reader->r_user_data);
1619         else
1620                 status = -1;
1622         sdb_object_deref(SDB_OBJ(q));
1623         sdb_object_deref(SDB_OBJ(reader));
1624         return status;
1625 } /* sdb_plugin_query */
1627 int
1628 sdb_plugin_store_host(const char *name, sdb_time_t last_update)
1630         sdb_store_host_t host = SDB_STORE_HOST_INIT;
1631         char *backends[1];
1632         char *cname;
1634         sdb_llist_iter_t *iter;
1635         int status = 0;
1637         if (! name)
1638                 return -1;
1640         if (! sdb_llist_len(writer_list)) {
1641                 sdb_log(SDB_LOG_ERR, "core: Cannot store host: "
1642                                 "no writers registered");
1643                 return -1;
1644         }
1646         cname = sdb_plugin_cname(strdup(name));
1647         if (! cname) {
1648                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1649                 return -1;
1650         }
1652         host.name = cname;
1653         host.last_update = last_update ? last_update : sdb_gettime();
1654         if (get_interval(SDB_HOST, NULL, -1, NULL, cname,
1655                                 host.last_update, &host.interval)) {
1656                 free(cname);
1657                 return 1;
1658         }
1659         host.backends = (const char * const *)backends;
1660         get_backend(backends, &host.backends_num);
1662         iter = sdb_llist_get_iter(writer_list);
1663         while (sdb_llist_iter_has_next(iter)) {
1664                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1665                 int s;
1666                 assert(writer);
1667                 s = writer->impl.store_host(&host, writer->w_user_data);
1668                 if (((s > 0) && (status >= 0)) || (s < 0))
1669                         status = s;
1670         }
1671         sdb_llist_iter_destroy(iter);
1672         free(cname);
1673         return status;
1674 } /* sdb_plugin_store_host */
1676 int
1677 sdb_plugin_store_service(const char *hostname, const char *name,
1678                 sdb_time_t last_update)
1680         sdb_store_service_t service = SDB_STORE_SERVICE_INIT;
1681         char *backends[1];
1682         char *cname;
1684         sdb_llist_iter_t *iter;
1685         sdb_data_t d;
1687         int status = 0;
1689         if ((! hostname) || (! name))
1690                 return -1;
1692         if (! sdb_llist_len(writer_list)) {
1693                 sdb_log(SDB_LOG_ERR, "core: Cannot store service: "
1694                                 "no writers registered");
1695                 return -1;
1696         }
1698         cname = sdb_plugin_cname(strdup(hostname));
1699         if (! cname) {
1700                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1701                 return -1;
1702         }
1704         service.hostname = cname;
1705         service.name = name;
1706         service.last_update = last_update ? last_update : sdb_gettime();
1707         if (get_interval(SDB_SERVICE, cname, -1, NULL, name,
1708                                 service.last_update, &service.interval)) {
1709                 free(cname);
1710                 return 1;
1711         }
1712         service.backends = (const char * const *)backends;
1713         get_backend(backends, &service.backends_num);
1715         iter = sdb_llist_get_iter(writer_list);
1716         while (sdb_llist_iter_has_next(iter)) {
1717                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1718                 int s;
1719                 assert(writer);
1720                 s = writer->impl.store_service(&service, writer->w_user_data);
1721                 if (((s > 0) && (status >= 0)) || (s < 0))
1722                         status = s;
1723         }
1724         sdb_llist_iter_destroy(iter);
1726         if (! status) {
1727                 /* record the hostname as an attribute */
1728                 d.type = SDB_TYPE_STRING;
1729                 d.data.string = cname;
1730                 if (sdb_plugin_store_service_attribute(cname, name,
1731                                         "hostname", &d, service.last_update))
1732                         status = -1;
1733         }
1735         free(cname);
1736         return status;
1737 } /* sdb_plugin_store_service */
1739 int
1740 sdb_plugin_store_metric(const char *hostname, const char *name,
1741                 sdb_metric_store_t *store, sdb_time_t last_update)
1743         sdb_store_metric_t metric = SDB_STORE_METRIC_INIT;
1744         char *backends[1];
1745         char *cname;
1747         sdb_llist_iter_t *iter;
1748         sdb_data_t d;
1750         int status = 0;
1752         if ((! hostname) || (! name))
1753                 return -1;
1755         if (! sdb_llist_len(writer_list)) {
1756                 sdb_log(SDB_LOG_ERR, "core: Cannot store metric: "
1757                                 "no writers registered");
1758                 return -1;
1759         }
1761         cname = sdb_plugin_cname(strdup(hostname));
1762         if (! cname) {
1763                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1764                 return -1;
1765         }
1767         if (store && ((! store->type) || (! store->id)))
1768                 store = NULL;
1770         metric.hostname = cname;
1771         metric.name = name;
1772         if (store) {
1773                 if (store->last_update < last_update)
1774                         store->last_update = last_update;
1775                 metric.stores = store;
1776                 metric.stores_num = 1;
1777         }
1778         metric.last_update = last_update ? last_update : sdb_gettime();
1779         if (get_interval(SDB_METRIC, cname, -1, NULL, name,
1780                                 metric.last_update, &metric.interval)) {
1781                 free(cname);
1782                 return 1;
1783         }
1784         metric.backends = (const char * const *)backends;
1785         get_backend(backends, &metric.backends_num);
1787         iter = sdb_llist_get_iter(writer_list);
1788         while (sdb_llist_iter_has_next(iter)) {
1789                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1790                 int s;
1791                 assert(writer);
1792                 s = writer->impl.store_metric(&metric, writer->w_user_data);
1793                 if (((s > 0) && (status >= 0)) || (s < 0))
1794                         status = s;
1795         }
1796         sdb_llist_iter_destroy(iter);
1798         if (! status) {
1799                 /* record the hostname as an attribute */
1800                 d.type = SDB_TYPE_STRING;
1801                 d.data.string = cname;
1802                 if (sdb_plugin_store_metric_attribute(cname, name,
1803                                         "hostname", &d, metric.last_update))
1804                         status = -1;
1805         }
1807         free(cname);
1808         return status;
1809 } /* sdb_plugin_store_metric */
1811 int
1812 sdb_plugin_store_attribute(const char *hostname, const char *key,
1813                 const sdb_data_t *value, sdb_time_t last_update)
1815         sdb_store_attribute_t attr = SDB_STORE_ATTRIBUTE_INIT;
1816         char *backends[1];
1817         char *cname;
1819         sdb_llist_iter_t *iter;
1820         int status = 0;
1822         if ((! hostname) || (! key) || (! value))
1823                 return -1;
1825         if (! sdb_llist_len(writer_list)) {
1826                 sdb_log(SDB_LOG_ERR, "core: Cannot store attribute: "
1827                                 "no writers registered");
1828                 return -1;
1829         }
1831         cname = sdb_plugin_cname(strdup(hostname));
1832         if (! cname) {
1833                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1834                 return -1;
1835         }
1837         attr.parent_type = SDB_HOST;
1838         attr.parent = cname;
1839         attr.key = key;
1840         attr.value = *value;
1841         attr.last_update = last_update ? last_update : sdb_gettime();
1842         if (get_interval(SDB_ATTRIBUTE, cname, -1, NULL, key,
1843                                 attr.last_update, &attr.interval)) {
1844                 free(cname);
1845                 return 1;
1846         }
1847         attr.backends = (const char * const *)backends;
1848         get_backend(backends, &attr.backends_num);
1850         iter = sdb_llist_get_iter(writer_list);
1851         while (sdb_llist_iter_has_next(iter)) {
1852                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1853                 int s;
1854                 assert(writer);
1855                 s = writer->impl.store_attribute(&attr, writer->w_user_data);
1856                 if (((s > 0) && (status >= 0)) || (s < 0))
1857                         status = s;
1858         }
1859         sdb_llist_iter_destroy(iter);
1860         free(cname);
1861         return status;
1862 } /* sdb_plugin_store_attribute */
1864 int
1865 sdb_plugin_store_service_attribute(const char *hostname, const char *service,
1866                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
1868         sdb_store_attribute_t attr = SDB_STORE_ATTRIBUTE_INIT;
1869         char *backends[1];
1870         char *cname;
1872         sdb_llist_iter_t *iter;
1873         int status = 0;
1875         if ((! hostname) || (! service) || (! key) || (! value))
1876                 return -1;
1878         if (! sdb_llist_len(writer_list)) {
1879                 sdb_log(SDB_LOG_ERR, "core: Cannot store service attribute: "
1880                                 "no writers registered");
1881                 return -1;
1882         }
1884         cname = sdb_plugin_cname(strdup(hostname));
1885         if (! cname) {
1886                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1887                 return -1;
1888         }
1890         attr.hostname = cname;
1891         attr.parent_type = SDB_SERVICE;
1892         attr.parent = service;
1893         attr.key = key;
1894         attr.value = *value;
1895         attr.last_update = last_update ? last_update : sdb_gettime();
1896         if (get_interval(SDB_ATTRIBUTE, cname, SDB_SERVICE, service, key,
1897                                 attr.last_update, &attr.interval)) {
1898                 free(cname);
1899                 return 1;
1900         }
1901         attr.backends = (const char * const *)backends;
1902         get_backend(backends, &attr.backends_num);
1904         iter = sdb_llist_get_iter(writer_list);
1905         while (sdb_llist_iter_has_next(iter)) {
1906                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1907                 int s;
1908                 assert(writer);
1909                 s = writer->impl.store_attribute(&attr, writer->w_user_data);
1910                 if (((s > 0) && (status >= 0)) || (s < 0))
1911                         status = s;
1912         }
1913         sdb_llist_iter_destroy(iter);
1914         free(cname);
1915         return status;
1916 } /* sdb_plugin_store_service_attribute */
1918 int
1919 sdb_plugin_store_metric_attribute(const char *hostname, const char *metric,
1920                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
1922         sdb_store_attribute_t attr = SDB_STORE_ATTRIBUTE_INIT;
1923         char *backends[1];
1924         char *cname;
1926         sdb_llist_iter_t *iter;
1927         int status = 0;
1929         if ((! hostname) || (! metric) || (! key) || (! value))
1930                 return -1;
1932         if (! sdb_llist_len(writer_list)) {
1933                 sdb_log(SDB_LOG_ERR, "core: Cannot store metric attribute: "
1934                                 "no writers registered");
1935                 return -1;
1936         }
1938         cname = sdb_plugin_cname(strdup(hostname));
1939         if (! cname) {
1940                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1941                 return -1;
1942         }
1944         attr.hostname = cname;
1945         attr.parent_type = SDB_METRIC;
1946         attr.parent = metric;
1947         attr.key = key;
1948         attr.value = *value;
1949         attr.last_update = last_update ? last_update : sdb_gettime();
1950         if (get_interval(SDB_ATTRIBUTE, cname, SDB_METRIC, metric, key,
1951                                 attr.last_update, &attr.interval)) {
1952                 free(cname);
1953                 return 1;
1954         }
1955         attr.backends = (const char * const *)backends;
1956         get_backend(backends, &attr.backends_num);
1958         iter = sdb_llist_get_iter(writer_list);
1959         while (sdb_llist_iter_has_next(iter)) {
1960                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1961                 int s;
1962                 assert(writer);
1963                 s = writer->impl.store_attribute(&attr, writer->w_user_data);
1964                 if (((s > 0) && (status >= 0)) || (s < 0))
1965                         status = s;
1966         }
1967         sdb_llist_iter_destroy(iter);
1968         free(cname);
1969         return status;
1970 } /* sdb_plugin_store_metric_attribute */
1972 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */