Code

3fb0a77348bb098b414f9724fb9281598ae41463
[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      *timeseries_fetcher_list = NULL;
145 static sdb_llist_t      *writer_list = NULL;
146 static sdb_llist_t      *reader_list = NULL;
148 static struct {
149         const char   *type;
150         sdb_llist_t **list;
151 } all_lists[] = {
152         { "config",             &config_list },
153         { "init",               &init_list },
154         { "collector",          &collector_list },
155         { "cname",              &cname_list },
156         { "shutdown",           &shutdown_list },
157         { "log",                &log_list },
158         { "timeseries fetcher", &timeseries_fetcher_list },
159         { "store writer",       &writer_list },
160         { "store reader",       &reader_list },
161 };
163 /*
164  * private helper functions
165  */
167 static void
168 plugin_info_clear(sdb_plugin_info_t *info)
170         sdb_plugin_info_t empty_info = SDB_PLUGIN_INFO_INIT;
171         if (! info)
172                 return;
174         if (info->plugin_name)
175                 free(info->plugin_name);
176         if (info->filename)
177                 free(info->filename);
179         if (info->description)
180                 free(info->description);
181         if (info->copyright)
182                 free(info->copyright);
183         if (info->license)
184                 free(info->license);
186         *info = empty_info;
187 } /* plugin_info_clear */
189 static void
190 ctx_key_init(void)
192         if (plugin_ctx_key_initialized)
193                 return;
195         pthread_key_create(&plugin_ctx_key, /* destructor */ NULL);
196         plugin_ctx_key_initialized = 1;
197 } /* ctx_key_init */
199 static int
200 plugin_cmp_next_update(const sdb_object_t *a, const sdb_object_t *b)
202         const collector_t *ccb1 = (const collector_t *)a;
203         const collector_t *ccb2 = (const collector_t *)b;
205         assert(ccb1 && ccb2);
207         return (ccb1->ccb_next_update > ccb2->ccb_next_update)
208                 ? 1 : (ccb1->ccb_next_update < ccb2->ccb_next_update)
209                 ? -1 : 0;
210 } /* plugin_cmp_next_update */
212 static int
213 plugin_lookup_by_name(const sdb_object_t *obj, const void *id)
215         const callback_t *cb = CONST_CB(obj);
216         const char *name = id;
218         assert(cb && id);
220         /* when a plugin was registered from outside a plugin (e.g. the core),
221          * we don't have a plugin context */
222         if (! cb->cb_ctx)
223                 return 1;
225         if (!strcasecmp(cb->cb_ctx->info.plugin_name, name))
226                 return 0;
227         return 1;
228 } /* plugin_lookup_by_name */
230 /* since this function is called from sdb_plugin_reconfigure_finish()
231  * when iterating through all_plugins, we may not do any additional
232  * modifications to all_plugins except for the optional removal */
233 static void
234 plugin_unregister_by_name(const char *plugin_name)
236         sdb_object_t *obj;
237         size_t i;
239         for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
240                 const char  *type =  all_lists[i].type;
241                 sdb_llist_t *list = *all_lists[i].list;
243                 while (1) {
244                         callback_t *cb;
246                         cb = CB(sdb_llist_remove(list,
247                                                 plugin_lookup_by_name, plugin_name));
248                         if (! cb)
249                                 break;
251                         assert(cb->cb_ctx);
253                         sdb_log(SDB_LOG_INFO, "core: Unregistering "
254                                         "%s callback '%s' (module %s)", type, cb->super.name,
255                                         cb->cb_ctx->info.plugin_name);
256                         sdb_object_deref(SDB_OBJ(cb));
257                 }
258         }
260         obj = sdb_llist_search_by_name(all_plugins, plugin_name);
261         /* when called from sdb_plugin_reconfigure_finish, the object has already
262          * been removed from the list */
263         if (obj && (obj->ref_cnt <= 1)) {
264                 sdb_llist_remove_by_name(all_plugins, plugin_name);
265                 sdb_object_deref(obj);
266         }
267         /* else: other callbacks still reference it */
268 } /* plugin_unregister_by_name */
270 /*
271  * private types
272  */
274 static int
275 ctx_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
277         ctx_t *ctx = CTX(obj);
279         assert(ctx);
281         ctx->public = plugin_default_ctx;
282         ctx->info = plugin_default_info;
283         ctx->handle = NULL;
284         ctx->use_cnt = 1;
285         return 0;
286 } /* ctx_init */
288 static void
289 ctx_destroy(sdb_object_t *obj)
291         ctx_t *ctx = CTX(obj);
293         if (ctx->handle) {
294                 const char *err;
296                 sdb_log(SDB_LOG_INFO, "core: Unloading module %s",
297                                 ctx->info.plugin_name);
299                 lt_dlerror();
300                 lt_dlclose(ctx->handle);
301                 if ((err = lt_dlerror()))
302                         sdb_log(SDB_LOG_WARNING, "core: Failed to unload module %s: %s",
303                                         ctx->info.plugin_name, err);
304         }
306         plugin_info_clear(&ctx->info);
307 } /* ctx_destroy */
309 static sdb_type_t ctx_type = {
310         sizeof(ctx_t),
312         ctx_init,
313         ctx_destroy
314 };
316 static ctx_t *
317 ctx_get(void)
319         if (! plugin_ctx_key_initialized)
320                 ctx_key_init();
321         return pthread_getspecific(plugin_ctx_key);
322 } /* ctx_get */
324 static ctx_t *
325 ctx_set(ctx_t *new)
327         ctx_t *old;
329         if (! plugin_ctx_key_initialized)
330                 ctx_key_init();
332         old = pthread_getspecific(plugin_ctx_key);
333         if (old)
334                 sdb_object_deref(SDB_OBJ(old));
335         if (new)
336                 sdb_object_ref(SDB_OBJ(new));
337         pthread_setspecific(plugin_ctx_key, new);
338         return old;
339 } /* ctx_set */
341 static ctx_t *
342 ctx_create(const char *name)
344         ctx_t *ctx;
346         ctx = CTX(sdb_object_create(name, ctx_type));
347         if (! ctx)
348                 return NULL;
350         if (! plugin_ctx_key_initialized)
351                 ctx_key_init();
352         ctx_set(ctx);
353         return ctx;
354 } /* ctx_create */
356 /*
357  * plugin_init_ok:
358  * Checks whether the registration of a new plugin identified by 'obj' is
359  * okay. It consumes the first two arguments of 'ap'.
360  */
361 static bool
362 plugin_init_ok(sdb_object_t *obj, va_list ap)
364         sdb_llist_t **list = va_arg(ap, sdb_llist_t **);
365         const char *type = va_arg(ap, const char *);
367         assert(list); assert(type);
369         if (sdb_llist_search_by_name(*list, obj->name)) {
370                 sdb_log(SDB_LOG_WARNING, "core: %s callback '%s' "
371                                 "has already been registered. Ignoring newly "
372                                 "registered version.", type, obj->name);
373                 return 0;
374         }
375         return 1;
376 } /* plugin_init_ok */
378 static int
379 plugin_cb_init(sdb_object_t *obj, va_list ap)
381         void *callback;
382         sdb_object_t *ud;
384         if (! plugin_init_ok(obj, ap))
385                 return -1;
387         callback = va_arg(ap, void *);
388         ud = va_arg(ap, sdb_object_t *);
390         /* cb_ctx may be NULL if the plugin was not registered by a plugin */
392         CB(obj)->cb_callback = callback;
393         CB(obj)->cb_ctx      = ctx_get();
394         sdb_object_ref(SDB_OBJ(CB(obj)->cb_ctx));
396         sdb_object_ref(ud);
397         CB(obj)->cb_user_data = ud;
398         return 0;
399 } /* plugin_cb_init */
401 static void
402 plugin_cb_destroy(sdb_object_t *obj)
404         assert(obj);
405         sdb_object_deref(CB(obj)->cb_user_data);
406         sdb_object_deref(SDB_OBJ(CB(obj)->cb_ctx));
407 } /* plugin_cb_destroy */
409 static sdb_type_t callback_type = {
410         sizeof(callback_t),
412         plugin_cb_init,
413         plugin_cb_destroy
414 };
416 static sdb_type_t collector_type = {
417         sizeof(collector_t),
419         plugin_cb_init,
420         plugin_cb_destroy
421 };
423 static int
424 plugin_writer_init(sdb_object_t *obj, va_list ap)
426         sdb_store_writer_t *impl;
427         sdb_object_t *ud;
429         if (! plugin_init_ok(obj, ap))
430                 return -1;
432         impl = va_arg(ap, sdb_store_writer_t *);
433         ud = va_arg(ap, sdb_object_t *);
434         assert(impl);
436         if ((! impl->store_host) || (! impl->store_service)
437                         || (! impl->store_metric) || (! impl->store_attribute)) {
438                 sdb_log(SDB_LOG_ERR, "core: store writer callback '%s' "
439                                 "does not fully implement the writer interface.",
440                                 obj->name);
441                 return -1;
442         }
444         /* ctx may be NULL if the callback was not registered by a plugin */
446         WRITER(obj)->impl = *impl;
447         WRITER(obj)->w_ctx  = ctx_get();
448         sdb_object_ref(SDB_OBJ(WRITER(obj)->w_ctx));
450         sdb_object_ref(ud);
451         WRITER(obj)->w_user_data = ud;
452         return 0;
453 } /* plugin_writer_init */
455 static void
456 plugin_writer_destroy(sdb_object_t *obj)
458         assert(obj);
459         sdb_object_deref(WRITER(obj)->w_user_data);
460         sdb_object_deref(SDB_OBJ(WRITER(obj)->w_ctx));
461 } /* plugin_writer_destroy */
463 static sdb_type_t writer_type = {
464         sizeof(writer_t),
466         plugin_writer_init,
467         plugin_writer_destroy
468 };
470 static int
471 plugin_reader_init(sdb_object_t *obj, va_list ap)
473         sdb_store_reader_t *impl;
474         sdb_object_t *ud;
476         if (! plugin_init_ok(obj, ap))
477                 return -1;
479         impl = va_arg(ap, sdb_store_reader_t *);
480         ud = va_arg(ap, sdb_object_t *);
481         assert(impl);
483         if ((! impl->prepare_query) || (! impl->execute_query)) {
484                 sdb_log(SDB_LOG_ERR, "core: store reader callback '%s' "
485                                 "does not fully implement the reader interface.",
486                                 obj->name);
487                 return -1;
488         }
490         /* ctx may be NULL if the callback was not registered by a plugin */
492         READER(obj)->impl = *impl;
493         READER(obj)->r_ctx  = ctx_get();
494         sdb_object_ref(SDB_OBJ(READER(obj)->r_ctx));
496         sdb_object_ref(ud);
497         READER(obj)->r_user_data = ud;
498         return 0;
499 } /* plugin_reader_init */
501 static void
502 plugin_reader_destroy(sdb_object_t *obj)
504         assert(obj);
505         sdb_object_deref(READER(obj)->r_user_data);
506         sdb_object_deref(SDB_OBJ(READER(obj)->r_ctx));
507 } /* plugin_reader_destroy */
509 static sdb_type_t reader_type = {
510         sizeof(reader_t),
512         plugin_reader_init,
513         plugin_reader_destroy
514 };
516 static int
517 plugin_ts_fetcher_init(sdb_object_t *obj, va_list ap)
519         sdb_timeseries_fetcher_t *impl;
520         sdb_object_t *ud;
522         if (! plugin_init_ok(obj, ap))
523                 return -1;
525         impl = va_arg(ap, sdb_timeseries_fetcher_t *);
526         ud = va_arg(ap, sdb_object_t *);
527         assert(impl);
529         if ((! impl->describe) || (! impl->fetch)) {
530                 sdb_log(SDB_LOG_ERR, "core: timeseries fetcher callback '%s' "
531                                 "does not fully implement the interface.",
532                                 obj->name);
533                 return -1;
534         }
536         /* ctx may be NULL if the callback was not registered by a plugin */
538         TS_FETCHER(obj)->impl = *impl;
539         TS_FETCHER(obj)->ts_ctx  = ctx_get();
540         sdb_object_ref(SDB_OBJ(TS_FETCHER(obj)->ts_ctx));
542         sdb_object_ref(ud);
543         TS_FETCHER(obj)->ts_user_data = ud;
544         return 0;
545 } /* plugin_ts_fetcher_init */
547 static void
548 plugin_ts_fetcher_destroy(sdb_object_t *obj)
550         assert(obj);
551         sdb_object_deref(TS_FETCHER(obj)->ts_user_data);
552         sdb_object_deref(SDB_OBJ(TS_FETCHER(obj)->ts_ctx));
553 } /* plugin_ts_fetcher_destroy */
555 static sdb_type_t ts_fetcher_type = {
556         sizeof(ts_fetcher_t),
558         plugin_ts_fetcher_init,
559         plugin_ts_fetcher_destroy
560 };
562 static int
563 module_init(const char *name, lt_dlhandle lh, sdb_plugin_info_t *info)
565         int (*mod_init)(sdb_plugin_info_t *);
566         int status;
568         mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
569         if (! mod_init) {
570                 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
571                                 "could not find symbol 'sdb_module_init'", name);
572                 return -1;
573         }
575         status = mod_init(info);
576         if (status) {
577                 sdb_log(SDB_LOG_ERR, "core: Failed to initialize "
578                                 "module '%s'", name);
579                 plugin_unregister_by_name(name);
580                 return -1;
581         }
582         return 0;
583 } /* module_init */
585 static int
586 module_load(const char *basedir, const char *name,
587                 const sdb_plugin_ctx_t *plugin_ctx)
589         char  base_name[name ? strlen(name) + 1 : 1];
590         const char *name_ptr;
591         char *tmp;
593         char filename[1024];
594         lt_dlhandle lh;
596         ctx_t *ctx;
598         int status;
600         assert(name);
602         base_name[0] = '\0';
603         name_ptr = name;
605         while ((tmp = strstr(name_ptr, "::"))) {
606                 strncat(base_name, name_ptr, (size_t)(tmp - name_ptr));
607                 strcat(base_name, "/");
608                 name_ptr = tmp + strlen("::");
609         }
610         strcat(base_name, name_ptr);
612         if (! basedir)
613                 basedir = PKGLIBDIR;
615         snprintf(filename, sizeof(filename), "%s/%s.so", basedir, base_name);
616         filename[sizeof(filename) - 1] = '\0';
618         if (access(filename, R_OK)) {
619                 char errbuf[1024];
620                 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s' (%s): %s",
621                                 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
622                 return -1;
623         }
625         lt_dlinit();
626         lt_dlerror();
628         lh = lt_dlopen(filename);
629         if (! lh) {
630                 sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': %s"
631                                 "The most common cause for this problem are missing "
632                                 "dependencies.\n", name, lt_dlerror());
633                 return -1;
634         }
636         if (ctx_get())
637                 sdb_log(SDB_LOG_WARNING, "core: Discarding old plugin context");
639         ctx = ctx_create(name);
640         if (! ctx) {
641                 sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin context");
642                 return -1;
643         }
645         ctx->info.plugin_name = strdup(name);
646         ctx->info.filename = strdup(filename);
647         ctx->handle = lh;
649         if (plugin_ctx)
650                 ctx->public = *plugin_ctx;
652         if ((status = module_init(name, lh, &ctx->info))) {
653                 sdb_object_deref(SDB_OBJ(ctx));
654                 return status;
655         }
657         /* compare minor version */
658         if ((ctx->info.version < 0)
659                         || ((int)(ctx->info.version / 100) != (int)(SDB_VERSION / 100)))
660                 sdb_log(SDB_LOG_WARNING, "core: WARNING: version of "
661                                 "plugin '%s' (%i.%i.%i) does not match our version "
662                                 "(%i.%i.%i); this might cause problems",
663                                 name, SDB_VERSION_DECODE(ctx->info.version),
664                                 SDB_VERSION_DECODE(SDB_VERSION));
666         sdb_llist_append(all_plugins, SDB_OBJ(ctx));
668         sdb_log(SDB_LOG_INFO, "core: Successfully loaded "
669                         "plugin %s v%i (%s)", ctx->info.plugin_name,
670                         ctx->info.plugin_version,
671                         INFO_GET(&ctx->info, description));
672         sdb_log(SDB_LOG_INFO, "core: Plugin %s: %s, License: %s",
673                         ctx->info.plugin_name,
674                         INFO_GET(&ctx->info, copyright),
675                         INFO_GET(&ctx->info, license));
677         /* any registered callbacks took ownership of the context */
678         sdb_object_deref(SDB_OBJ(ctx));
680         /* reset */
681         ctx_set(NULL);
682         return 0;
683 } /* module_load */
685 static char *
686 plugin_get_name(const char *name, char *buf, size_t bufsize)
688         ctx_t *ctx = ctx_get();
690         if (ctx)
691                 snprintf(buf, bufsize, "%s::%s", ctx->info.plugin_name, name);
692         else
693                 snprintf(buf, bufsize, "core::%s", name);
694         return buf;
695 } /* plugin_get_name */
697 static int
698 plugin_add_impl(sdb_llist_t **list, sdb_type_t T, const char *type,
699                 const char *name, void *impl, sdb_object_t *user_data)
701         sdb_object_t *obj;
703         if ((! name) || (! impl))
704                 return -1;
706         assert(list);
708         if (! *list)
709                 *list = sdb_llist_create();
710         if (! *list)
711                 return -1;
713         obj = sdb_object_create(name, T, list, type, impl, user_data);
714         if (! obj)
715                 return -1;
717         if (sdb_llist_append(*list, obj)) {
718                 sdb_object_deref(obj);
719                 return -1;
720         }
722         /* pass control to the list */
723         sdb_object_deref(obj);
725         sdb_log(SDB_LOG_INFO, "core: Registered %s callback '%s'.",
726                         type, name);
727         return 0;
728 } /* plugin_add_impl */
730 /*
731  * object meta-data
732  */
734 typedef struct {
735         int obj_type;
736         sdb_time_t last_update;
737         sdb_time_t interval;
738 } interval_fetcher_t;
740 static int
741 interval_fetcher_host(sdb_store_host_t *host, sdb_object_t *user_data)
743         interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
744         lu->obj_type = SDB_HOST;
745         lu->last_update = host->last_update;
746         return 0;
747 } /* interval_fetcher_host */
749 static int
750 interval_fetcher_service(sdb_store_service_t *svc, sdb_object_t *user_data)
752         interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
753         lu->obj_type = SDB_SERVICE;
754         lu->last_update = svc->last_update;
755         return 0;
756 } /* interval_fetcher_service */
758 static int
759 interval_fetcher_metric(sdb_store_metric_t *metric, sdb_object_t *user_data)
761         interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
762         lu->obj_type = SDB_METRIC;
763         lu->last_update = metric->last_update;
764         return 0;
765 } /* interval_fetcher_metric */
767 static int
768 interval_fetcher_attr(sdb_store_attribute_t *attr, sdb_object_t *user_data)
770         interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
771         lu->obj_type = SDB_ATTRIBUTE;
772         lu->last_update = attr->last_update;
773         return 0;
774 } /* interval_fetcher_attr */
776 static sdb_store_writer_t interval_fetcher = {
777         interval_fetcher_host, interval_fetcher_service,
778         interval_fetcher_metric, interval_fetcher_attr,
779 };
781 static int
782 get_interval(int obj_type, const char *hostname,
783                 int parent_type, const char *parent, const char *name,
784                 sdb_time_t last_update, sdb_time_t *interval_out)
786         sdb_ast_fetch_t fetch = SDB_AST_FETCH_INIT;
787         char hn[hostname ? strlen(hostname) + 1 : 1];
788         char pn[parent ? strlen(parent) + 1 : 1];
789         char n[strlen(name) + 1];
790         int status;
792         interval_fetcher_t lu = { 0, 0, 0 };
793         sdb_object_wrapper_t obj = SDB_OBJECT_WRAPPER_STATIC(&lu);
794         sdb_time_t interval;
796         assert(name);
798         if (hostname)
799                 strncpy(hn, hostname, sizeof(hn));
800         if (parent)
801                 strncpy(pn, parent, sizeof(pn));
802         strncpy(n, name, sizeof(n));
804         fetch.obj_type = obj_type;
805         fetch.hostname = hostname ? hn : NULL;
806         fetch.parent_type = parent_type;
807         fetch.parent = parent ? pn : NULL;
808         fetch.name = n;
810         status = sdb_plugin_query(SDB_AST_NODE(&fetch),
811                         &interval_fetcher, SDB_OBJ(&obj), NULL);
812         if ((status < 0) || (lu.obj_type != obj_type) || (lu.last_update == 0)) {
813                 *interval_out = 0;
814                 return 0;
815         }
817         if (lu.last_update >= last_update) {
818                 if (lu.last_update > last_update)
819                         sdb_log(SDB_LOG_DEBUG, "memstore: Cannot update %s '%s' - "
820                                         "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
821                                         SDB_STORE_TYPE_TO_NAME(obj_type), name,
822                                         lu.last_update, last_update);
823                 *interval_out = lu.interval;
824                 return 1;
825         }
827         interval = last_update - lu.last_update;
828         if (lu.interval && interval)
829                 interval = (sdb_time_t)((0.9 * (double)lu.interval)
830                                 + (0.1 * (double)interval));
831         *interval_out = interval;
832         return 0;
833 } /* get_interval */
835 static void
836 get_backend(char **backends, size_t *backends_num)
838         const sdb_plugin_info_t *info;
840         info = sdb_plugin_current();
841         if ((! info) || (! info->plugin_name) || (! *info->plugin_name)) {
842                 *backends_num = 0;
843                 return;
844         }
846         backends[0] = info->plugin_name;
847         *backends_num = 1;
848 } /* get_backend */
850 /*
851  * public API
852  */
854 int
855 sdb_plugin_load(const char *basedir, const char *name,
856                 const sdb_plugin_ctx_t *plugin_ctx)
858         ctx_t *ctx;
860         int status;
862         if ((! name) || (! *name))
863                 return -1;
865         if (! all_plugins) {
866                 if (! (all_plugins = sdb_llist_create())) {
867                         sdb_log(SDB_LOG_ERR, "core: Failed to load plugin '%s': "
868                                         "internal error while creating linked list", name);
869                         return -1;
870                 }
871         }
873         ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
874         if (ctx) {
875                 /* plugin already loaded */
876                 if (! ctx->use_cnt) {
877                         /* reloading plugin */
878                         ctx_t *old_ctx = ctx_set(ctx);
880                         status = module_init(ctx->info.plugin_name, ctx->handle, NULL);
881                         if (status)
882                                 return status;
884                         sdb_log(SDB_LOG_INFO, "core: Successfully reloaded plugin "
885                                         "'%s' (%s)", ctx->info.plugin_name,
886                                         INFO_GET(&ctx->info, description));
887                         ctx_set(old_ctx);
888                 }
889                 ++ctx->use_cnt;
890                 return 0;
891         }
893         return module_load(basedir, name, plugin_ctx);
894 } /* sdb_plugin_load */
896 int
897 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
899         va_list ap;
901         if (! info)
902                 return -1;
904         va_start(ap, type);
906         switch (type) {
907                 case SDB_PLUGIN_INFO_DESC:
908                         {
909                                 char *desc = va_arg(ap, char *);
910                                 if (desc) {
911                                         if (info->description)
912                                                 free(info->description);
913                                         info->description = strdup(desc);
914                                 }
915                         }
916                         break;
917                 case SDB_PLUGIN_INFO_COPYRIGHT:
918                         {
919                                 char *copyright = va_arg(ap, char *);
920                                 if (copyright)
921                                         info->copyright = strdup(copyright);
922                         }
923                         break;
924                 case SDB_PLUGIN_INFO_LICENSE:
925                         {
926                                 char *license = va_arg(ap, char *);
927                                 if (license) {
928                                         if (info->license)
929                                                 free(info->license);
930                                         info->license = strdup(license);
931                                 }
932                         }
933                         break;
934                 case SDB_PLUGIN_INFO_VERSION:
935                         {
936                                 int version = va_arg(ap, int);
937                                 info->version = version;
938                         }
939                         break;
940                 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
941                         {
942                                 int version = va_arg(ap, int);
943                                 info->plugin_version = version;
944                         }
945                         break;
946                 default:
947                         va_end(ap);
948                         return -1;
949         }
951         va_end(ap);
952         return 0;
953 } /* sdb_plugin_set_info */
955 int
956 sdb_plugin_register_config(sdb_plugin_config_cb callback)
958         ctx_t *ctx = ctx_get();
960         if (! ctx) {
961                 sdb_log(SDB_LOG_ERR, "core: Invalid attempt to register a "
962                                 "config callback from outside a plugin");
963                 return -1;
964         }
965         return plugin_add_impl(&config_list, callback_type, "config",
966                         ctx->info.plugin_name, (void *)callback, NULL);
967 } /* sdb_plugin_register_config */
969 int
970 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
971                 sdb_object_t *user_data)
973         char cb_name[1024];
974         return plugin_add_impl(&init_list, callback_type, "init",
975                         plugin_get_name(name, cb_name, sizeof(cb_name)),
976                         (void *)callback, user_data);
977 } /* sdb_plugin_register_init */
979 int
980 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
981                 sdb_object_t *user_data)
983         char cb_name[1024];
984         return plugin_add_impl(&shutdown_list, callback_type, "shutdown",
985                         plugin_get_name(name, cb_name, sizeof(cb_name)),
986                         (void *)callback, user_data);
987 } /* sdb_plugin_register_shutdown */
989 int
990 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
991                 sdb_object_t *user_data)
993         char cb_name[1024];
994         return plugin_add_impl(&log_list, callback_type, "log",
995                         plugin_get_name(name, cb_name, sizeof(cb_name)),
996                         callback, user_data);
997 } /* sdb_plugin_register_log */
999 int
1000 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
1001                 sdb_object_t *user_data)
1003         char cb_name[1024];
1004         return plugin_add_impl(&cname_list, callback_type, "cname",
1005                         plugin_get_name(name, cb_name, sizeof(cb_name)),
1006                         callback, user_data);
1007 } /* sdb_plugin_register_cname */
1009 int
1010 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
1011                 const sdb_time_t *interval, sdb_object_t *user_data)
1013         char cb_name[1024];
1014         sdb_object_t *obj;
1016         if ((! name) || (! callback))
1017                 return -1;
1019         if (! collector_list)
1020                 collector_list = sdb_llist_create();
1021         if (! collector_list)
1022                 return -1;
1024         plugin_get_name(name, cb_name, sizeof(cb_name));
1026         obj = sdb_object_create(cb_name, collector_type,
1027                         &collector_list, "collector", callback, user_data);
1028         if (! obj)
1029                 return -1;
1031         if (interval)
1032                 CCB(obj)->ccb_interval = *interval;
1033         else {
1034                 ctx_t *ctx = ctx_get();
1036                 if (! ctx) {
1037                         sdb_log(SDB_LOG_ERR, "core: Cannot determine interval "
1038                                         "for collector %s; none specified and no plugin "
1039                                         "context found", cb_name);
1040                         return -1;
1041                 }
1043                 CCB(obj)->ccb_interval = ctx->public.interval;
1044         }
1046         if (! (CCB(obj)->ccb_next_update = sdb_gettime())) {
1047                 char errbuf[1024];
1048                 sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1049                                 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
1050                 sdb_object_deref(obj);
1051                 return -1;
1052         }
1054         if (sdb_llist_insert_sorted(collector_list, obj,
1055                                 plugin_cmp_next_update)) {
1056                 sdb_object_deref(obj);
1057                 return -1;
1058         }
1060         /* pass control to the list */
1061         sdb_object_deref(obj);
1063         sdb_log(SDB_LOG_INFO, "core: Registered collector callback '%s' "
1064                         "(interval = %.3fs).", cb_name,
1065                         SDB_TIME_TO_DOUBLE(CCB(obj)->ccb_interval));
1066         return 0;
1067 } /* sdb_plugin_register_collector */
1069 int
1070 sdb_plugin_register_timeseries_fetcher(const char *name,
1071                 sdb_timeseries_fetcher_t *fetcher, sdb_object_t *user_data)
1073         return plugin_add_impl(&timeseries_fetcher_list, ts_fetcher_type, "time-series fetcher",
1074                         name, fetcher, user_data);
1075 } /* sdb_plugin_register_timeseries_fetcher */
1077 int
1078 sdb_plugin_register_writer(const char *name,
1079                 sdb_store_writer_t *writer, sdb_object_t *user_data)
1081         char cb_name[1024];
1082         return plugin_add_impl(&writer_list, writer_type, "store writer",
1083                         plugin_get_name(name, cb_name, sizeof(cb_name)),
1084                         writer, user_data);
1085 } /* sdb_store_register_writer */
1087 int
1088 sdb_plugin_register_reader(const char *name,
1089                 sdb_store_reader_t *reader, sdb_object_t *user_data)
1091         char cb_name[1024];
1092         return plugin_add_impl(&reader_list, reader_type, "store reader",
1093                         plugin_get_name(name, cb_name, sizeof(cb_name)),
1094                         reader, user_data);
1095 } /* sdb_plugin_register_reader */
1097 void
1098 sdb_plugin_unregister_all(void)
1100         size_t i;
1102         for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
1103                 const char  *type =  all_lists[i].type;
1104                 sdb_llist_t *list = *all_lists[i].list;
1106                 size_t len = sdb_llist_len(list);
1108                 if (! len)
1109                         continue;
1111                 sdb_llist_clear(list);
1112                 sdb_log(SDB_LOG_INFO, "core: Unregistered %zu %s callback%s",
1113                                 len, type, len == 1 ? "" : "s");
1114         }
1115 } /* sdb_plugin_unregister_all */
1117 sdb_plugin_ctx_t
1118 sdb_plugin_get_ctx(void)
1120         ctx_t *c;
1122         c = ctx_get();
1123         if (! c) {
1124                 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid read access to plugin "
1125                                 "context outside a plugin");
1126                 return plugin_default_ctx;
1127         }
1128         return c->public;
1129 } /* sdb_plugin_get_ctx */
1131 int
1132 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx, sdb_plugin_ctx_t *old)
1134         ctx_t *c;
1136         c = ctx_get();
1137         if (! c) {
1138                 sdb_plugin_log(SDB_LOG_ERR, "core: Invalid write access to plugin "
1139                                 "context outside a plugin");
1140                 return -1;
1141         }
1143         if (old)
1144                 *old = c->public;
1145         c->public = ctx;
1146         return 0;
1147 } /* sdb_plugin_set_ctx */
1149 const sdb_plugin_info_t *
1150 sdb_plugin_current(void)
1152         ctx_t *ctx = ctx_get();
1154         if (! ctx)
1155                 return NULL;
1156         return &ctx->info;
1157 } /* sdb_plugin_current */
1159 int
1160 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
1162         callback_t *plugin;
1163         sdb_plugin_config_cb callback;
1165         ctx_t *old_ctx;
1167         int status;
1169         if ((! name) || (! ci))
1170                 return -1;
1172         plugin = CB(sdb_llist_search_by_name(config_list, name));
1173         if (! plugin) {
1174                 ctx_t *ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
1175                 if (! ctx)
1176                         sdb_log(SDB_LOG_ERR, "core: Cannot configure unknown "
1177                                         "plugin '%s'. Missing 'LoadPlugin \"%s\"'?",
1178                                         name, name);
1179                 else
1180                         sdb_log(SDB_LOG_ERR, "core: Plugin '%s' did not register "
1181                                         "a config callback.", name);
1182                 errno = ENOENT;
1183                 return -1;
1184         }
1186         old_ctx = ctx_set(plugin->cb_ctx);
1187         callback = (sdb_plugin_config_cb)plugin->cb_callback;
1188         status = callback(ci);
1189         ctx_set(old_ctx);
1190         return status;
1191 } /* sdb_plugin_configure */
1193 int
1194 sdb_plugin_reconfigure_init(void)
1196         sdb_llist_iter_t *iter;
1198         iter = sdb_llist_get_iter(config_list);
1199         if (config_list && (! iter))
1200                 return -1;
1202         /* deconfigure all plugins */
1203         while (sdb_llist_iter_has_next(iter)) {
1204                 callback_t *plugin;
1205                 sdb_plugin_config_cb callback;
1206                 ctx_t *old_ctx;
1208                 plugin = CB(sdb_llist_iter_get_next(iter));
1209                 old_ctx = ctx_set(plugin->cb_ctx);
1210                 callback = (sdb_plugin_config_cb)plugin->cb_callback;
1211                 callback(NULL);
1212                 ctx_set(old_ctx);
1213         }
1214         sdb_llist_iter_destroy(iter);
1216         iter = sdb_llist_get_iter(all_plugins);
1217         if (all_plugins && (! iter))
1218                 return -1;
1220         /* record all plugins as being unused */
1221         while (sdb_llist_iter_has_next(iter))
1222                 CTX(sdb_llist_iter_get_next(iter))->use_cnt = 0;
1223         sdb_llist_iter_destroy(iter);
1225         sdb_plugin_unregister_all();
1226         return 0;
1227 } /* sdb_plugin_reconfigure_init */
1229 int
1230 sdb_plugin_reconfigure_finish(void)
1232         sdb_llist_iter_t *iter;
1234         iter = sdb_llist_get_iter(all_plugins);
1235         if (all_plugins && (! iter))
1236                 return -1;
1238         while (sdb_llist_iter_has_next(iter)) {
1239                 ctx_t *ctx = CTX(sdb_llist_iter_get_next(iter));
1240                 if (ctx->use_cnt)
1241                         continue;
1243                 sdb_log(SDB_LOG_INFO, "core: Module %s no longer in use",
1244                                 ctx->info.plugin_name);
1245                 sdb_llist_iter_remove_current(iter);
1246                 plugin_unregister_by_name(ctx->info.plugin_name);
1247                 sdb_object_deref(SDB_OBJ(ctx));
1248         }
1249         sdb_llist_iter_destroy(iter);
1250         return 0;
1251 } /* sdb_plugin_reconfigure_finish */
1253 int
1254 sdb_plugin_init_all(void)
1256         sdb_llist_iter_t *iter;
1257         int ret = 0;
1259         iter = sdb_llist_get_iter(init_list);
1260         while (sdb_llist_iter_has_next(iter)) {
1261                 callback_t *cb;
1262                 sdb_plugin_init_cb callback;
1263                 ctx_t *old_ctx;
1265                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1266                 assert(obj);
1267                 cb = CB(obj);
1269                 callback = (sdb_plugin_init_cb)cb->cb_callback;
1271                 old_ctx = ctx_set(cb->cb_ctx);
1272                 if (callback(cb->cb_user_data)) {
1273                         sdb_log(SDB_LOG_ERR, "core: Failed to initialize plugin "
1274                                         "'%s'. Unregistering all callbacks.", obj->name);
1275                         ctx_set(old_ctx);
1276                         plugin_unregister_by_name(cb->cb_ctx->info.plugin_name);
1277                         ++ret;
1278                 }
1279                 else
1280                         ctx_set(old_ctx);
1281         }
1282         sdb_llist_iter_destroy(iter);
1283         return ret;
1284 } /* sdb_plugin_init_all */
1286 int
1287 sdb_plugin_shutdown_all(void)
1289         sdb_llist_iter_t *iter;
1290         int ret = 0;
1292         iter = sdb_llist_get_iter(shutdown_list);
1293         while (sdb_llist_iter_has_next(iter)) {
1294                 callback_t *cb;
1295                 sdb_plugin_shutdown_cb callback;
1296                 ctx_t *old_ctx;
1298                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1299                 assert(obj);
1300                 cb = CB(obj);
1302                 callback = (sdb_plugin_shutdown_cb)cb->cb_callback;
1304                 old_ctx = ctx_set(cb->cb_ctx);
1305                 if (callback(cb->cb_user_data)) {
1306                         sdb_log(SDB_LOG_ERR, "core: Failed to shutdown plugin '%s'.",
1307                                         obj->name);
1308                         ++ret;
1309                 }
1310                 ctx_set(old_ctx);
1311         }
1312         sdb_llist_iter_destroy(iter);
1313         return ret;
1314 } /* sdb_plugin_shutdown_all */
1316 int
1317 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
1319         if (! collector_list) {
1320                 sdb_log(SDB_LOG_WARNING, "core: No collectors registered. "
1321                                 "Quiting main loop.");
1322                 return -1;
1323         }
1325         if (! loop)
1326                 return -1;
1328         while (loop->do_loop) {
1329                 sdb_plugin_collector_cb callback;
1330                 ctx_t *old_ctx;
1332                 sdb_time_t interval, now;
1334                 sdb_object_t *obj = sdb_llist_shift(collector_list);
1335                 if (! obj)
1336                         return -1;
1338                 callback = (sdb_plugin_collector_cb)CCB(obj)->ccb_callback;
1340                 if (! (now = sdb_gettime())) {
1341                         char errbuf[1024];
1342                         sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1343                                         "time in collector main loop: %s",
1344                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
1345                         now = CCB(obj)->ccb_next_update;
1346                 }
1348                 if (now < CCB(obj)->ccb_next_update) {
1349                         interval = CCB(obj)->ccb_next_update - now;
1351                         errno = 0;
1352                         while (loop->do_loop && sdb_sleep(interval, &interval)) {
1353                                 if (errno != EINTR) {
1354                                         char errbuf[1024];
1355                                         sdb_log(SDB_LOG_ERR, "core: Failed to sleep "
1356                                                         "in collector main loop: %s",
1357                                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
1358                                         sdb_llist_insert_sorted(collector_list, obj,
1359                                                         plugin_cmp_next_update);
1360                                         sdb_object_deref(obj);
1361                                         return -1;
1362                                 }
1363                                 errno = 0;
1364                         }
1366                         if (! loop->do_loop) {
1367                                 /* put back; don't worry about errors */
1368                                 sdb_llist_insert_sorted(collector_list, obj,
1369                                                 plugin_cmp_next_update);
1370                                 sdb_object_deref(obj);
1371                                 return 0;
1372                         }
1373                 }
1375                 old_ctx = ctx_set(CCB(obj)->ccb_ctx);
1376                 if (callback(CCB(obj)->ccb_user_data)) {
1377                         /* XXX */
1378                 }
1379                 ctx_set(old_ctx);
1381                 interval = CCB(obj)->ccb_interval;
1382                 if (! interval)
1383                         interval = loop->default_interval;
1384                 if (! interval) {
1385                         sdb_log(SDB_LOG_WARNING, "core: No interval configured "
1386                                         "for plugin '%s'; skipping any further "
1387                                         "iterations.", obj->name);
1388                         sdb_object_deref(obj);
1389                         continue;
1390                 }
1392                 CCB(obj)->ccb_next_update += interval;
1394                 if (! (now = sdb_gettime())) {
1395                         char errbuf[1024];
1396                         sdb_log(SDB_LOG_ERR, "core: Failed to determine current "
1397                                         "time in collector main loop: %s",
1398                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
1399                         now = CCB(obj)->ccb_next_update;
1400                 }
1402                 if (now > CCB(obj)->ccb_next_update) {
1403                         sdb_log(SDB_LOG_WARNING, "core: Plugin '%s' took too "
1404                                         "long; skipping iterations to keep up.",
1405                                         obj->name);
1406                         CCB(obj)->ccb_next_update = now;
1407                 }
1409                 if (sdb_llist_insert_sorted(collector_list, obj,
1410                                         plugin_cmp_next_update)) {
1411                         sdb_log(SDB_LOG_ERR, "core: Failed to re-insert "
1412                                         "plugin '%s' into collector list. Unable to further "
1413                                         "use the plugin.",
1414                                         obj->name);
1415                         sdb_object_deref(obj);
1416                         return -1;
1417                 }
1419                 /* pass control back to the list */
1420                 sdb_object_deref(obj);
1421         }
1422         return 0;
1423 } /* sdb_plugin_read_loop */
1425 char *
1426 sdb_plugin_cname(char *hostname)
1428         sdb_llist_iter_t *iter;
1430         if (! hostname)
1431                 return NULL;
1433         if (! cname_list)
1434                 return hostname;
1436         iter = sdb_llist_get_iter(cname_list);
1437         while (sdb_llist_iter_has_next(iter)) {
1438                 sdb_plugin_cname_cb callback;
1439                 char *cname;
1441                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1442                 assert(obj);
1444                 callback = (sdb_plugin_cname_cb)CB(obj)->cb_callback;
1445                 cname = callback(hostname, CB(obj)->cb_user_data);
1446                 if (cname) {
1447                         free(hostname);
1448                         hostname = cname;
1449                 }
1450                 /* else: don't change hostname */
1451         }
1452         sdb_llist_iter_destroy(iter);
1453         return hostname;
1454 } /* sdb_plugin_cname */
1456 int
1457 sdb_plugin_log(int prio, const char *msg)
1459         sdb_llist_iter_t *iter;
1460         int ret = -1;
1462         bool logged = 0;
1464         if (! msg)
1465                 return 0;
1467         iter = sdb_llist_get_iter(log_list);
1468         while (sdb_llist_iter_has_next(iter)) {
1469                 sdb_plugin_log_cb callback;
1470                 int tmp;
1472                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1473                 assert(obj);
1475                 callback = (sdb_plugin_log_cb)CB(obj)->cb_callback;
1476                 tmp = callback(prio, msg, CB(obj)->cb_user_data);
1477                 if (tmp > ret)
1478                         ret = tmp;
1480                 if (CB(obj)->cb_ctx)
1481                         logged = 1;
1482                 /* else: this is an internally registered callback */
1483         }
1484         sdb_llist_iter_destroy(iter);
1486         if (! logged)
1487                 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
1488         return ret;
1489 } /* sdb_plugin_log */
1491 int
1492 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
1494         sdb_strbuf_t *buf;
1495         int ret;
1497         if (! fmt)
1498                 return 0;
1500         buf = sdb_strbuf_create(64);
1501         if (! buf) {
1502                 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
1503                 ret += vfprintf(stderr, fmt, ap);
1504                 return ret;
1505         }
1507         if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
1508                 sdb_strbuf_destroy(buf);
1509                 return -1;
1510         }
1512         ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
1513         sdb_strbuf_destroy(buf);
1514         return ret;
1515 } /* sdb_plugin_vlogf */
1517 int
1518 sdb_plugin_logf(int prio, const char *fmt, ...)
1520         va_list ap;
1521         int ret;
1523         if (! fmt)
1524                 return 0;
1526         va_start(ap, fmt);
1527         ret = sdb_plugin_vlogf(prio, fmt, ap);
1528         va_end(ap);
1529         return ret;
1530 } /* sdb_plugin_logf */
1532 sdb_timeseries_t *
1533 sdb_plugin_fetch_timeseries(const char *type, const char *id,
1534                 sdb_timeseries_opts_t *opts)
1536         ts_fetcher_t *fetcher;
1537         sdb_timeseries_t *ts;
1539         ctx_t *old_ctx;
1541         if ((! type) || (! id) || (! opts))
1542                 return NULL;
1544         fetcher = TS_FETCHER(sdb_llist_search_by_name(timeseries_fetcher_list, type));
1545         if (! fetcher) {
1546                 sdb_log(SDB_LOG_ERR, "core: Cannot fetch time-series of type %s: "
1547                                 "no such plugin loaded", type);
1548                 errno = ENOENT;
1549                 return NULL;
1550         }
1552         old_ctx = ctx_set(fetcher->ts_ctx);
1553         ts = fetcher->impl.fetch(id, opts, fetcher->ts_user_data);
1554         ctx_set(old_ctx);
1555         return ts;
1556 } /* sdb_plugin_fetch_timeseries */
1558 int
1559 sdb_plugin_query(sdb_ast_node_t *ast,
1560                 sdb_store_writer_t *w, sdb_object_t *wd, sdb_strbuf_t *errbuf)
1562         size_t n = sdb_llist_len(reader_list);
1563         reader_t *reader;
1564         sdb_object_t *q;
1565         int status = 0;
1567         if (! ast)
1568                 return 0;
1570         if ((ast->type != SDB_AST_TYPE_FETCH)
1571                         && (ast->type != SDB_AST_TYPE_LIST)
1572                         && (ast->type != SDB_AST_TYPE_LOOKUP)) {
1573                 sdb_log(SDB_LOG_ERR, "core: Cannot execute query of type %s",
1574                                 SDB_AST_TYPE_TO_STRING(ast));
1575                 sdb_strbuf_sprintf(errbuf, "Cannot execute query of type %s",
1576                                 SDB_AST_TYPE_TO_STRING(ast));
1577                 return -1;
1578         }
1580         if (n != 1) {
1581                 char *msg = (n > 0)
1582                         ? "Cannot execute query: multiple readers not supported"
1583                         : "Cannot execute query: no readers registered";
1584                 sdb_strbuf_sprintf(errbuf, "%s", msg);
1585                 sdb_log(SDB_LOG_ERR, "core: %s", msg);
1586                 return -1;
1587         }
1589         reader = READER(sdb_llist_get(reader_list, 0));
1590         assert(reader);
1592         q = reader->impl.prepare_query(ast, errbuf, reader->r_user_data);
1593         if (q)
1594                 status = reader->impl.execute_query(q, w, SDB_OBJ(wd),
1595                                 errbuf, reader->r_user_data);
1596         else
1597                 status = -1;
1599         sdb_object_deref(SDB_OBJ(q));
1600         sdb_object_deref(SDB_OBJ(reader));
1601         return status;
1602 } /* sdb_plugin_query */
1604 int
1605 sdb_plugin_store_host(const char *name, sdb_time_t last_update)
1607         sdb_store_host_t host = SDB_STORE_HOST_INIT;
1608         char *backends[1];
1609         char *cname;
1611         sdb_llist_iter_t *iter;
1612         int status = 0;
1614         if (! name)
1615                 return -1;
1617         if (! sdb_llist_len(writer_list)) {
1618                 sdb_log(SDB_LOG_ERR, "core: Cannot store host: "
1619                                 "no writers registered");
1620                 return -1;
1621         }
1623         cname = sdb_plugin_cname(strdup(name));
1624         if (! cname) {
1625                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1626                 return -1;
1627         }
1629         host.name = cname;
1630         host.last_update = last_update ? last_update : sdb_gettime();
1631         if (get_interval(SDB_HOST, NULL, -1, NULL, cname,
1632                                 host.last_update, &host.interval)) {
1633                 free(cname);
1634                 return 1;
1635         }
1636         host.backends = (const char * const *)backends;
1637         get_backend(backends, &host.backends_num);
1639         iter = sdb_llist_get_iter(writer_list);
1640         while (sdb_llist_iter_has_next(iter)) {
1641                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1642                 int s;
1643                 assert(writer);
1644                 s = writer->impl.store_host(&host, writer->w_user_data);
1645                 if (((s > 0) && (status >= 0)) || (s < 0))
1646                         status = s;
1647         }
1648         sdb_llist_iter_destroy(iter);
1649         free(cname);
1650         return status;
1651 } /* sdb_plugin_store_host */
1653 int
1654 sdb_plugin_store_service(const char *hostname, const char *name,
1655                 sdb_time_t last_update)
1657         sdb_store_service_t service = SDB_STORE_SERVICE_INIT;
1658         char *backends[1];
1659         char *cname;
1661         sdb_llist_iter_t *iter;
1662         sdb_data_t d;
1664         int status = 0;
1666         if ((! hostname) || (! name))
1667                 return -1;
1669         if (! sdb_llist_len(writer_list)) {
1670                 sdb_log(SDB_LOG_ERR, "core: Cannot store service: "
1671                                 "no writers registered");
1672                 return -1;
1673         }
1675         cname = sdb_plugin_cname(strdup(hostname));
1676         if (! cname) {
1677                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1678                 return -1;
1679         }
1681         service.hostname = cname;
1682         service.name = name;
1683         service.last_update = last_update ? last_update : sdb_gettime();
1684         if (get_interval(SDB_SERVICE, cname, -1, NULL, name,
1685                                 service.last_update, &service.interval)) {
1686                 free(cname);
1687                 return 1;
1688         }
1689         service.backends = (const char * const *)backends;
1690         get_backend(backends, &service.backends_num);
1692         iter = sdb_llist_get_iter(writer_list);
1693         while (sdb_llist_iter_has_next(iter)) {
1694                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1695                 int s;
1696                 assert(writer);
1697                 s = writer->impl.store_service(&service, writer->w_user_data);
1698                 if (((s > 0) && (status >= 0)) || (s < 0))
1699                         status = s;
1700         }
1701         sdb_llist_iter_destroy(iter);
1703         if (! status) {
1704                 /* record the hostname as an attribute */
1705                 d.type = SDB_TYPE_STRING;
1706                 d.data.string = cname;
1707                 if (sdb_plugin_store_service_attribute(cname, name,
1708                                         "hostname", &d, service.last_update))
1709                         status = -1;
1710         }
1712         free(cname);
1713         return status;
1714 } /* sdb_plugin_store_service */
1716 int
1717 sdb_plugin_store_metric(const char *hostname, const char *name,
1718                 sdb_metric_store_t *store, sdb_time_t last_update)
1720         sdb_store_metric_t metric = SDB_STORE_METRIC_INIT;
1721         char *backends[1];
1722         char *cname;
1724         sdb_llist_iter_t *iter;
1725         sdb_data_t d;
1727         int status = 0;
1729         if ((! hostname) || (! name))
1730                 return -1;
1732         if (! sdb_llist_len(writer_list)) {
1733                 sdb_log(SDB_LOG_ERR, "core: Cannot store metric: "
1734                                 "no writers registered");
1735                 return -1;
1736         }
1738         cname = sdb_plugin_cname(strdup(hostname));
1739         if (! cname) {
1740                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1741                 return -1;
1742         }
1744         if (store && ((! store->type) || (! store->id)))
1745                 store = NULL;
1747         metric.hostname = cname;
1748         metric.name = name;
1749         if (store) {
1750                 if (store->last_update < last_update)
1751                         store->last_update = last_update;
1752                 metric.stores = store;
1753                 metric.stores_num = 1;
1754         }
1755         metric.last_update = last_update ? last_update : sdb_gettime();
1756         if (get_interval(SDB_METRIC, cname, -1, NULL, name,
1757                                 metric.last_update, &metric.interval)) {
1758                 free(cname);
1759                 return 1;
1760         }
1761         metric.backends = (const char * const *)backends;
1762         get_backend(backends, &metric.backends_num);
1764         iter = sdb_llist_get_iter(writer_list);
1765         while (sdb_llist_iter_has_next(iter)) {
1766                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1767                 int s;
1768                 assert(writer);
1769                 s = writer->impl.store_metric(&metric, writer->w_user_data);
1770                 if (((s > 0) && (status >= 0)) || (s < 0))
1771                         status = s;
1772         }
1773         sdb_llist_iter_destroy(iter);
1775         if (! status) {
1776                 /* record the hostname as an attribute */
1777                 d.type = SDB_TYPE_STRING;
1778                 d.data.string = cname;
1779                 if (sdb_plugin_store_metric_attribute(cname, name,
1780                                         "hostname", &d, metric.last_update))
1781                         status = -1;
1782         }
1784         free(cname);
1785         return status;
1786 } /* sdb_plugin_store_metric */
1788 int
1789 sdb_plugin_store_attribute(const char *hostname, const char *key,
1790                 const sdb_data_t *value, sdb_time_t last_update)
1792         sdb_store_attribute_t attr = SDB_STORE_ATTRIBUTE_INIT;
1793         char *backends[1];
1794         char *cname;
1796         sdb_llist_iter_t *iter;
1797         int status = 0;
1799         if ((! hostname) || (! key) || (! value))
1800                 return -1;
1802         if (! sdb_llist_len(writer_list)) {
1803                 sdb_log(SDB_LOG_ERR, "core: Cannot store attribute: "
1804                                 "no writers registered");
1805                 return -1;
1806         }
1808         cname = sdb_plugin_cname(strdup(hostname));
1809         if (! cname) {
1810                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1811                 return -1;
1812         }
1814         attr.parent_type = SDB_HOST;
1815         attr.parent = cname;
1816         attr.key = key;
1817         attr.value = *value;
1818         attr.last_update = last_update ? last_update : sdb_gettime();
1819         if (get_interval(SDB_ATTRIBUTE, cname, -1, NULL, key,
1820                                 attr.last_update, &attr.interval)) {
1821                 free(cname);
1822                 return 1;
1823         }
1824         attr.backends = (const char * const *)backends;
1825         get_backend(backends, &attr.backends_num);
1827         iter = sdb_llist_get_iter(writer_list);
1828         while (sdb_llist_iter_has_next(iter)) {
1829                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1830                 int s;
1831                 assert(writer);
1832                 s = writer->impl.store_attribute(&attr, writer->w_user_data);
1833                 if (((s > 0) && (status >= 0)) || (s < 0))
1834                         status = s;
1835         }
1836         sdb_llist_iter_destroy(iter);
1837         free(cname);
1838         return status;
1839 } /* sdb_plugin_store_attribute */
1841 int
1842 sdb_plugin_store_service_attribute(const char *hostname, const char *service,
1843                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
1845         sdb_store_attribute_t attr = SDB_STORE_ATTRIBUTE_INIT;
1846         char *backends[1];
1847         char *cname;
1849         sdb_llist_iter_t *iter;
1850         int status = 0;
1852         if ((! hostname) || (! service) || (! key) || (! value))
1853                 return -1;
1855         if (! sdb_llist_len(writer_list)) {
1856                 sdb_log(SDB_LOG_ERR, "core: Cannot store service attribute: "
1857                                 "no writers registered");
1858                 return -1;
1859         }
1861         cname = sdb_plugin_cname(strdup(hostname));
1862         if (! cname) {
1863                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1864                 return -1;
1865         }
1867         attr.hostname = cname;
1868         attr.parent_type = SDB_SERVICE;
1869         attr.parent = service;
1870         attr.key = key;
1871         attr.value = *value;
1872         attr.last_update = last_update ? last_update : sdb_gettime();
1873         if (get_interval(SDB_ATTRIBUTE, cname, SDB_SERVICE, service, key,
1874                                 attr.last_update, &attr.interval)) {
1875                 free(cname);
1876                 return 1;
1877         }
1878         attr.backends = (const char * const *)backends;
1879         get_backend(backends, &attr.backends_num);
1881         iter = sdb_llist_get_iter(writer_list);
1882         while (sdb_llist_iter_has_next(iter)) {
1883                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1884                 int s;
1885                 assert(writer);
1886                 s = writer->impl.store_attribute(&attr, writer->w_user_data);
1887                 if (((s > 0) && (status >= 0)) || (s < 0))
1888                         status = s;
1889         }
1890         sdb_llist_iter_destroy(iter);
1891         free(cname);
1892         return status;
1893 } /* sdb_plugin_store_service_attribute */
1895 int
1896 sdb_plugin_store_metric_attribute(const char *hostname, const char *metric,
1897                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
1899         sdb_store_attribute_t attr = SDB_STORE_ATTRIBUTE_INIT;
1900         char *backends[1];
1901         char *cname;
1903         sdb_llist_iter_t *iter;
1904         int status = 0;
1906         if ((! hostname) || (! metric) || (! key) || (! value))
1907                 return -1;
1909         if (! sdb_llist_len(writer_list)) {
1910                 sdb_log(SDB_LOG_ERR, "core: Cannot store metric attribute: "
1911                                 "no writers registered");
1912                 return -1;
1913         }
1915         cname = sdb_plugin_cname(strdup(hostname));
1916         if (! cname) {
1917                 sdb_log(SDB_LOG_ERR, "core: strdup failed");
1918                 return -1;
1919         }
1921         attr.hostname = cname;
1922         attr.parent_type = SDB_METRIC;
1923         attr.parent = metric;
1924         attr.key = key;
1925         attr.value = *value;
1926         attr.last_update = last_update ? last_update : sdb_gettime();
1927         if (get_interval(SDB_ATTRIBUTE, cname, SDB_METRIC, metric, key,
1928                                 attr.last_update, &attr.interval)) {
1929                 free(cname);
1930                 return 1;
1931         }
1932         attr.backends = (const char * const *)backends;
1933         get_backend(backends, &attr.backends_num);
1935         iter = sdb_llist_get_iter(writer_list);
1936         while (sdb_llist_iter_has_next(iter)) {
1937                 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1938                 int s;
1939                 assert(writer);
1940                 s = writer->impl.store_attribute(&attr, writer->w_user_data);
1941                 if (((s > 0) && (status >= 0)) || (s < 0))
1942                         status = s;
1943         }
1944         sdb_llist_iter_destroy(iter);
1945         free(cname);
1946         return status;
1947 } /* sdb_plugin_store_metric_attribute */
1949 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */