b3f00bd655504075b97c5f158b8c45a8c0d20d05
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", ×eries_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)
169 {
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)
191 {
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)
201 {
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)
214 {
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)
235 {
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, "Unregistering %s callback '%s' (module %s)",
254 type, cb->super.name, cb->cb_ctx->info.plugin_name);
255 sdb_object_deref(SDB_OBJ(cb));
256 }
257 }
259 obj = sdb_llist_search_by_name(all_plugins, plugin_name);
260 /* when called from sdb_plugin_reconfigure_finish, the object has already
261 * been removed from the list */
262 if (obj && (obj->ref_cnt <= 1)) {
263 sdb_llist_remove_by_name(all_plugins, plugin_name);
264 sdb_object_deref(obj);
265 }
266 /* else: other callbacks still reference it */
267 } /* plugin_unregister_by_name */
269 /*
270 * store writer wrapper for performing database queries:
271 * It wraps another store writer, adding extra logic as needed.
272 */
274 typedef struct {
275 sdb_object_t super;
276 sdb_store_writer_t *w;
277 sdb_object_t *ud;
278 sdb_query_opts_t opts;
279 } query_writer_t;
280 #define QUERY_WRITER_INIT(w, ud) { \
281 SDB_OBJECT_INIT, \
282 (w), (ud), \
283 SDB_DEFAULT_QUERY_OPTS \
284 }
285 #define QUERY_WRITER(obj) ((query_writer_t *)(obj))
287 static int
288 query_store_host(sdb_store_host_t *host, sdb_object_t *user_data)
289 {
290 query_writer_t *qw = QUERY_WRITER(user_data);
291 return qw->w->store_host(host, qw->ud);
292 } /* query_store_host */
294 static int
295 query_store_service(sdb_store_service_t *service, sdb_object_t *user_data)
296 {
297 query_writer_t *qw = QUERY_WRITER(user_data);
298 return qw->w->store_service(service, qw->ud);
299 } /* query_store_service */
301 static int
302 query_store_metric(sdb_store_metric_t *metric, sdb_object_t *user_data)
303 {
304 query_writer_t *qw = QUERY_WRITER(user_data);
305 sdb_timeseries_info_t *infos[metric->stores_num];
306 sdb_metric_store_t stores[metric->stores_num];
308 const sdb_metric_store_t *orig_stores = metric->stores;
309 int status;
310 size_t i;
312 if (! qw->opts.describe_timeseries)
313 /* nothing further to do */
314 return qw->w->store_metric(metric, qw->ud);
316 for (i = 0; i < metric->stores_num; i++) {
317 sdb_metric_store_t *s = stores + i;
318 *s = metric->stores[i];
319 infos[i] = sdb_plugin_describe_timeseries(s->type, s->id);
320 s->info = infos[i];
321 }
323 metric->stores = stores;
324 status = qw->w->store_metric(metric, qw->ud);
325 metric->stores = orig_stores;
327 for (i = 0; i < metric->stores_num; i++)
328 sdb_timeseries_info_destroy(infos[i]);
329 return status;
330 } /* query_store_metric */
332 static int
333 query_store_attribute(sdb_store_attribute_t *attr, sdb_object_t *user_data)
334 {
335 query_writer_t *qw = QUERY_WRITER(user_data);
336 return qw->w->store_attribute(attr, qw->ud);
337 } /* query_store_attribute */
339 static sdb_store_writer_t query_writer = {
340 query_store_host, query_store_service,
341 query_store_metric, query_store_attribute,
342 };
344 /*
345 * private types
346 */
348 static int
349 ctx_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
350 {
351 ctx_t *ctx = CTX(obj);
353 assert(ctx);
355 ctx->public = plugin_default_ctx;
356 ctx->info = plugin_default_info;
357 ctx->handle = NULL;
358 ctx->use_cnt = 1;
359 return 0;
360 } /* ctx_init */
362 static void
363 ctx_destroy(sdb_object_t *obj)
364 {
365 ctx_t *ctx = CTX(obj);
367 if (ctx->handle) {
368 const char *err;
370 sdb_log(SDB_LOG_INFO, "Unloading module %s", ctx->info.plugin_name);
372 lt_dlerror();
373 lt_dlclose(ctx->handle);
374 if ((err = lt_dlerror()))
375 sdb_log(SDB_LOG_WARNING, "Failed to unload module %s: %s",
376 ctx->info.plugin_name, err);
377 }
379 plugin_info_clear(&ctx->info);
380 } /* ctx_destroy */
382 static sdb_type_t ctx_type = {
383 sizeof(ctx_t),
385 ctx_init,
386 ctx_destroy
387 };
389 static ctx_t *
390 ctx_get(void)
391 {
392 if (! plugin_ctx_key_initialized)
393 ctx_key_init();
394 return pthread_getspecific(plugin_ctx_key);
395 } /* ctx_get */
397 static ctx_t *
398 ctx_set(ctx_t *new)
399 {
400 ctx_t *old;
402 if (! plugin_ctx_key_initialized)
403 ctx_key_init();
405 old = pthread_getspecific(plugin_ctx_key);
406 if (old)
407 sdb_object_deref(SDB_OBJ(old));
408 if (new)
409 sdb_object_ref(SDB_OBJ(new));
410 pthread_setspecific(plugin_ctx_key, new);
411 return old;
412 } /* ctx_set */
414 static ctx_t *
415 ctx_create(const char *name)
416 {
417 ctx_t *ctx;
419 ctx = CTX(sdb_object_create(name, ctx_type));
420 if (! ctx)
421 return NULL;
423 if (! plugin_ctx_key_initialized)
424 ctx_key_init();
425 ctx_set(ctx);
426 return ctx;
427 } /* ctx_create */
429 /*
430 * plugin_init_ok:
431 * Checks whether the registration of a new plugin identified by 'obj' is
432 * okay. It consumes the first two arguments of 'ap'.
433 */
434 static bool
435 plugin_init_ok(sdb_object_t *obj, va_list ap)
436 {
437 sdb_llist_t **list = va_arg(ap, sdb_llist_t **);
438 const char *type = va_arg(ap, const char *);
440 assert(list); assert(type);
442 if (sdb_llist_search_by_name(*list, obj->name)) {
443 sdb_log(SDB_LOG_WARNING, "%s callback '%s' has already been "
444 "registered. Ignoring newly registered version.",
445 type, obj->name);
446 return 0;
447 }
448 return 1;
449 } /* plugin_init_ok */
451 static int
452 plugin_cb_init(sdb_object_t *obj, va_list ap)
453 {
454 void *callback;
455 sdb_object_t *ud;
457 if (! plugin_init_ok(obj, ap))
458 return -1;
460 callback = va_arg(ap, void *);
461 ud = va_arg(ap, sdb_object_t *);
463 /* cb_ctx may be NULL if the plugin was not registered by a plugin */
465 CB(obj)->cb_callback = callback;
466 CB(obj)->cb_ctx = ctx_get();
467 sdb_object_ref(SDB_OBJ(CB(obj)->cb_ctx));
469 sdb_object_ref(ud);
470 CB(obj)->cb_user_data = ud;
471 return 0;
472 } /* plugin_cb_init */
474 static void
475 plugin_cb_destroy(sdb_object_t *obj)
476 {
477 assert(obj);
478 sdb_object_deref(CB(obj)->cb_user_data);
479 sdb_object_deref(SDB_OBJ(CB(obj)->cb_ctx));
480 } /* plugin_cb_destroy */
482 static sdb_type_t callback_type = {
483 sizeof(callback_t),
485 plugin_cb_init,
486 plugin_cb_destroy
487 };
489 static sdb_type_t collector_type = {
490 sizeof(collector_t),
492 plugin_cb_init,
493 plugin_cb_destroy
494 };
496 static int
497 plugin_writer_init(sdb_object_t *obj, va_list ap)
498 {
499 sdb_store_writer_t *impl;
500 sdb_object_t *ud;
502 if (! plugin_init_ok(obj, ap))
503 return -1;
505 impl = va_arg(ap, sdb_store_writer_t *);
506 ud = va_arg(ap, sdb_object_t *);
507 assert(impl);
509 if ((! impl->store_host) || (! impl->store_service)
510 || (! impl->store_metric) || (! impl->store_attribute)) {
511 sdb_log(SDB_LOG_ERR, "store writer callback '%s' does not fully "
512 "implement the writer interface.", obj->name);
513 return -1;
514 }
516 /* ctx may be NULL if the callback was not registered by a plugin */
518 WRITER(obj)->impl = *impl;
519 WRITER(obj)->w_ctx = ctx_get();
520 sdb_object_ref(SDB_OBJ(WRITER(obj)->w_ctx));
522 sdb_object_ref(ud);
523 WRITER(obj)->w_user_data = ud;
524 return 0;
525 } /* plugin_writer_init */
527 static void
528 plugin_writer_destroy(sdb_object_t *obj)
529 {
530 assert(obj);
531 sdb_object_deref(WRITER(obj)->w_user_data);
532 sdb_object_deref(SDB_OBJ(WRITER(obj)->w_ctx));
533 } /* plugin_writer_destroy */
535 static sdb_type_t writer_type = {
536 sizeof(writer_t),
538 plugin_writer_init,
539 plugin_writer_destroy
540 };
542 static int
543 plugin_reader_init(sdb_object_t *obj, va_list ap)
544 {
545 sdb_store_reader_t *impl;
546 sdb_object_t *ud;
548 if (! plugin_init_ok(obj, ap))
549 return -1;
551 impl = va_arg(ap, sdb_store_reader_t *);
552 ud = va_arg(ap, sdb_object_t *);
553 assert(impl);
555 if ((! impl->prepare_query) || (! impl->execute_query)) {
556 sdb_log(SDB_LOG_ERR, "store reader callback '%s' does not fully "
557 "implement the reader interface.", obj->name);
558 return -1;
559 }
561 /* ctx may be NULL if the callback was not registered by a plugin */
563 READER(obj)->impl = *impl;
564 READER(obj)->r_ctx = ctx_get();
565 sdb_object_ref(SDB_OBJ(READER(obj)->r_ctx));
567 sdb_object_ref(ud);
568 READER(obj)->r_user_data = ud;
569 return 0;
570 } /* plugin_reader_init */
572 static void
573 plugin_reader_destroy(sdb_object_t *obj)
574 {
575 assert(obj);
576 sdb_object_deref(READER(obj)->r_user_data);
577 sdb_object_deref(SDB_OBJ(READER(obj)->r_ctx));
578 } /* plugin_reader_destroy */
580 static sdb_type_t reader_type = {
581 sizeof(reader_t),
583 plugin_reader_init,
584 plugin_reader_destroy
585 };
587 static int
588 plugin_ts_fetcher_init(sdb_object_t *obj, va_list ap)
589 {
590 sdb_timeseries_fetcher_t *impl;
591 sdb_object_t *ud;
593 if (! plugin_init_ok(obj, ap))
594 return -1;
596 impl = va_arg(ap, sdb_timeseries_fetcher_t *);
597 ud = va_arg(ap, sdb_object_t *);
598 assert(impl);
600 if ((! impl->describe) || (! impl->fetch)) {
601 sdb_log(SDB_LOG_ERR, "timeseries fetcher callback '%s' does not fully "
602 "implement the interface.", obj->name);
603 return -1;
604 }
606 /* ctx may be NULL if the callback was not registered by a plugin */
608 TS_FETCHER(obj)->impl = *impl;
609 TS_FETCHER(obj)->ts_ctx = ctx_get();
610 sdb_object_ref(SDB_OBJ(TS_FETCHER(obj)->ts_ctx));
612 sdb_object_ref(ud);
613 TS_FETCHER(obj)->ts_user_data = ud;
614 return 0;
615 } /* plugin_ts_fetcher_init */
617 static void
618 plugin_ts_fetcher_destroy(sdb_object_t *obj)
619 {
620 assert(obj);
621 sdb_object_deref(TS_FETCHER(obj)->ts_user_data);
622 sdb_object_deref(SDB_OBJ(TS_FETCHER(obj)->ts_ctx));
623 } /* plugin_ts_fetcher_destroy */
625 static sdb_type_t ts_fetcher_type = {
626 sizeof(ts_fetcher_t),
628 plugin_ts_fetcher_init,
629 plugin_ts_fetcher_destroy
630 };
632 static int
633 module_init(const char *name, lt_dlhandle lh, sdb_plugin_info_t *info)
634 {
635 int (*mod_init)(sdb_plugin_info_t *);
636 int status;
638 mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
639 if (! mod_init) {
640 sdb_log(SDB_LOG_ERR, "Failed to load plugin '%s': "
641 "could not find symbol 'sdb_module_init'", name);
642 return -1;
643 }
645 status = mod_init(info);
646 if (status) {
647 sdb_log(SDB_LOG_ERR, "Failed to initialize module '%s'", name);
648 plugin_unregister_by_name(name);
649 return -1;
650 }
651 return 0;
652 } /* module_init */
654 static int
655 module_load(const char *basedir, const char *name,
656 const sdb_plugin_ctx_t *plugin_ctx)
657 {
658 char base_name[name ? strlen(name) + 1 : 1];
659 const char *name_ptr;
660 char *tmp;
662 char filename[1024];
663 lt_dlhandle lh;
665 ctx_t *ctx;
667 int status;
669 assert(name);
671 base_name[0] = '\0';
672 name_ptr = name;
674 while ((tmp = strstr(name_ptr, "::"))) {
675 strncat(base_name, name_ptr, (size_t)(tmp - name_ptr));
676 strcat(base_name, "/");
677 name_ptr = tmp + strlen("::");
678 }
679 strcat(base_name, name_ptr);
681 if (! basedir)
682 basedir = PKGLIBDIR;
684 snprintf(filename, sizeof(filename), "%s/%s.so", basedir, base_name);
685 filename[sizeof(filename) - 1] = '\0';
687 if (access(filename, R_OK)) {
688 char errbuf[1024];
689 sdb_log(SDB_LOG_ERR, "Failed to load plugin '%s' (%s): %s",
690 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
691 return -1;
692 }
694 lt_dlinit();
695 lt_dlerror();
697 lh = lt_dlopen(filename);
698 if (! lh) {
699 sdb_log(SDB_LOG_ERR, "Failed to load plugin '%s': %s"
700 "The most common cause for this problem are missing "
701 "dependencies.\n", name, lt_dlerror());
702 return -1;
703 }
705 if (ctx_get())
706 sdb_log(SDB_LOG_WARNING, "Discarding old plugin context");
708 ctx = ctx_create(name);
709 if (! ctx) {
710 sdb_log(SDB_LOG_ERR, "Failed to initialize plugin context");
711 return -1;
712 }
714 ctx->info.plugin_name = strdup(name);
715 ctx->info.filename = strdup(filename);
716 ctx->handle = lh;
718 if (plugin_ctx)
719 ctx->public = *plugin_ctx;
721 if ((status = module_init(name, lh, &ctx->info))) {
722 sdb_object_deref(SDB_OBJ(ctx));
723 ctx_set(NULL);
724 return status;
725 }
727 /* compare minor version */
728 if ((ctx->info.version < 0)
729 || ((int)(ctx->info.version / 100) != (int)(SDB_VERSION / 100)))
730 sdb_log(SDB_LOG_WARNING, "WARNING: plugin version (%i.%i.%i) "
731 "does not match core version (%i.%i.%i); "
732 "this might cause problems",
733 SDB_VERSION_DECODE(ctx->info.version),
734 SDB_VERSION_DECODE(SDB_VERSION));
736 sdb_llist_append(all_plugins, SDB_OBJ(ctx));
738 /* log messages will be prefixed by the plugin name */
739 sdb_log(SDB_LOG_INFO, "Successfully loaded "
740 "version %i (%s)", ctx->info.plugin_version,
741 INFO_GET(&ctx->info, description));
742 sdb_log(SDB_LOG_INFO, "%s, License: %s",
743 INFO_GET(&ctx->info, copyright),
744 INFO_GET(&ctx->info, license));
746 /* any registered callbacks took ownership of the context */
747 sdb_object_deref(SDB_OBJ(ctx));
749 /* reset */
750 ctx_set(NULL);
751 return 0;
752 } /* module_load */
754 static char *
755 plugin_get_name(const char *name, char *buf, size_t bufsize)
756 {
757 ctx_t *ctx = ctx_get();
759 if (ctx)
760 snprintf(buf, bufsize, "%s::%s", ctx->info.plugin_name, name);
761 else
762 snprintf(buf, bufsize, "core::%s", name);
763 return buf;
764 } /* plugin_get_name */
766 static int
767 plugin_add_impl(sdb_llist_t **list, sdb_type_t T, const char *type,
768 const char *name, void *impl, sdb_object_t *user_data)
769 {
770 sdb_object_t *obj;
772 if ((! name) || (! impl))
773 return -1;
775 assert(list);
777 if (! *list)
778 *list = sdb_llist_create();
779 if (! *list)
780 return -1;
782 obj = sdb_object_create(name, T, list, type, impl, user_data);
783 if (! obj)
784 return -1;
786 if (sdb_llist_append(*list, obj)) {
787 sdb_object_deref(obj);
788 return -1;
789 }
791 /* pass control to the list */
792 sdb_object_deref(obj);
794 sdb_log(SDB_LOG_INFO, "Registered %s callback '%s'.", type, name);
795 return 0;
796 } /* plugin_add_impl */
798 /*
799 * object meta-data
800 */
802 typedef struct {
803 int obj_type;
804 sdb_time_t last_update;
805 sdb_time_t interval;
806 } interval_fetcher_t;
808 static int
809 interval_fetcher_host(sdb_store_host_t *host, sdb_object_t *user_data)
810 {
811 interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
812 lu->obj_type = SDB_HOST;
813 lu->last_update = host->last_update;
814 return 0;
815 } /* interval_fetcher_host */
817 static int
818 interval_fetcher_service(sdb_store_service_t *svc, sdb_object_t *user_data)
819 {
820 interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
821 lu->obj_type = SDB_SERVICE;
822 lu->last_update = svc->last_update;
823 return 0;
824 } /* interval_fetcher_service */
826 static int
827 interval_fetcher_metric(sdb_store_metric_t *metric, sdb_object_t *user_data)
828 {
829 interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
830 lu->obj_type = SDB_METRIC;
831 lu->last_update = metric->last_update;
832 return 0;
833 } /* interval_fetcher_metric */
835 static int
836 interval_fetcher_attr(sdb_store_attribute_t *attr, sdb_object_t *user_data)
837 {
838 interval_fetcher_t *lu = SDB_OBJ_WRAPPER(user_data)->data;
839 lu->obj_type = SDB_ATTRIBUTE;
840 lu->last_update = attr->last_update;
841 return 0;
842 } /* interval_fetcher_attr */
844 static sdb_store_writer_t interval_fetcher = {
845 interval_fetcher_host, interval_fetcher_service,
846 interval_fetcher_metric, interval_fetcher_attr,
847 };
849 static int
850 get_interval(int obj_type, const char *hostname,
851 int parent_type, const char *parent, const char *name,
852 sdb_time_t last_update, sdb_time_t *interval_out)
853 {
854 sdb_ast_fetch_t fetch = SDB_AST_FETCH_INIT;
855 char hn[hostname ? strlen(hostname) + 1 : 1];
856 char pn[parent ? strlen(parent) + 1 : 1];
857 char n[strlen(name) + 1];
858 int status;
860 interval_fetcher_t lu = { 0, 0, 0 };
861 sdb_object_wrapper_t obj = SDB_OBJECT_WRAPPER_STATIC(&lu);
862 sdb_time_t interval;
864 assert(name);
866 if (hostname)
867 strncpy(hn, hostname, sizeof(hn));
868 if (parent)
869 strncpy(pn, parent, sizeof(pn));
870 strncpy(n, name, sizeof(n));
872 fetch.obj_type = obj_type;
873 fetch.hostname = hostname ? hn : NULL;
874 fetch.parent_type = parent_type;
875 fetch.parent = parent ? pn : NULL;
876 fetch.name = n;
878 status = sdb_plugin_query(SDB_AST_NODE(&fetch),
879 &interval_fetcher, SDB_OBJ(&obj), NULL, NULL);
880 if ((status < 0) || (lu.obj_type != obj_type) || (lu.last_update == 0)) {
881 *interval_out = 0;
882 return 0;
883 }
885 if (lu.last_update >= last_update) {
886 if (lu.last_update > last_update)
887 sdb_log(SDB_LOG_DEBUG, "Cannot update %s '%s' - "
888 "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
889 SDB_STORE_TYPE_TO_NAME(obj_type), name,
890 lu.last_update, last_update);
891 *interval_out = lu.interval;
892 return 1;
893 }
895 interval = last_update - lu.last_update;
896 if (lu.interval && interval)
897 interval = (sdb_time_t)((0.9 * (double)lu.interval)
898 + (0.1 * (double)interval));
899 *interval_out = interval;
900 return 0;
901 } /* get_interval */
903 static void
904 get_backend(char **backends, size_t *backends_num)
905 {
906 const sdb_plugin_info_t *info;
908 info = sdb_plugin_current();
909 if ((! info) || (! info->plugin_name) || (! *info->plugin_name)) {
910 *backends_num = 0;
911 return;
912 }
914 backends[0] = info->plugin_name;
915 *backends_num = 1;
916 } /* get_backend */
918 /*
919 * public API
920 */
922 int
923 sdb_plugin_load(const char *basedir, const char *name,
924 const sdb_plugin_ctx_t *plugin_ctx)
925 {
926 ctx_t *ctx;
928 int status;
930 if ((! name) || (! *name))
931 return -1;
933 if (! all_plugins) {
934 if (! (all_plugins = sdb_llist_create())) {
935 sdb_log(SDB_LOG_ERR, "Failed to load plugin '%s': "
936 "internal error while creating linked list", name);
937 return -1;
938 }
939 }
941 ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
942 if (ctx) {
943 /* plugin already loaded */
944 if (! ctx->use_cnt) {
945 /* reloading plugin */
946 ctx_t *old_ctx = ctx_set(ctx);
948 status = module_init(ctx->info.plugin_name, ctx->handle, NULL);
949 ctx_set(old_ctx);
950 if (status)
951 return status;
953 ctx_set(old_ctx);
954 sdb_log(SDB_LOG_INFO, "Successfully reloaded plugin "
955 "'%s' (%s)", ctx->info.plugin_name,
956 INFO_GET(&ctx->info, description));
957 }
958 ++ctx->use_cnt;
959 return 0;
960 }
962 return module_load(basedir, name, plugin_ctx);
963 } /* sdb_plugin_load */
965 int
966 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
967 {
968 va_list ap;
970 if (! info)
971 return -1;
973 va_start(ap, type);
975 switch (type) {
976 case SDB_PLUGIN_INFO_DESC:
977 {
978 char *desc = va_arg(ap, char *);
979 if (desc) {
980 if (info->description)
981 free(info->description);
982 info->description = strdup(desc);
983 }
984 }
985 break;
986 case SDB_PLUGIN_INFO_COPYRIGHT:
987 {
988 char *copyright = va_arg(ap, char *);
989 if (copyright)
990 info->copyright = strdup(copyright);
991 }
992 break;
993 case SDB_PLUGIN_INFO_LICENSE:
994 {
995 char *license = va_arg(ap, char *);
996 if (license) {
997 if (info->license)
998 free(info->license);
999 info->license = strdup(license);
1000 }
1001 }
1002 break;
1003 case SDB_PLUGIN_INFO_VERSION:
1004 {
1005 int version = va_arg(ap, int);
1006 info->version = version;
1007 }
1008 break;
1009 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
1010 {
1011 int version = va_arg(ap, int);
1012 info->plugin_version = version;
1013 }
1014 break;
1015 default:
1016 va_end(ap);
1017 return -1;
1018 }
1020 va_end(ap);
1021 return 0;
1022 } /* sdb_plugin_set_info */
1024 int
1025 sdb_plugin_register_config(sdb_plugin_config_cb callback)
1026 {
1027 ctx_t *ctx = ctx_get();
1029 if (! ctx) {
1030 sdb_log(SDB_LOG_ERR, "Invalid attempt to register a "
1031 "config callback from outside a plugin");
1032 return -1;
1033 }
1034 return plugin_add_impl(&config_list, callback_type, "config",
1035 ctx->info.plugin_name, (void *)callback, NULL);
1036 } /* sdb_plugin_register_config */
1038 int
1039 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
1040 sdb_object_t *user_data)
1041 {
1042 char cb_name[1024];
1043 return plugin_add_impl(&init_list, callback_type, "init",
1044 plugin_get_name(name, cb_name, sizeof(cb_name)),
1045 (void *)callback, user_data);
1046 } /* sdb_plugin_register_init */
1048 int
1049 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
1050 sdb_object_t *user_data)
1051 {
1052 char cb_name[1024];
1053 return plugin_add_impl(&shutdown_list, callback_type, "shutdown",
1054 plugin_get_name(name, cb_name, sizeof(cb_name)),
1055 (void *)callback, user_data);
1056 } /* sdb_plugin_register_shutdown */
1058 int
1059 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
1060 sdb_object_t *user_data)
1061 {
1062 char cb_name[1024];
1063 return plugin_add_impl(&log_list, callback_type, "log",
1064 plugin_get_name(name, cb_name, sizeof(cb_name)),
1065 callback, user_data);
1066 } /* sdb_plugin_register_log */
1068 int
1069 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
1070 sdb_object_t *user_data)
1071 {
1072 char cb_name[1024];
1073 return plugin_add_impl(&cname_list, callback_type, "cname",
1074 plugin_get_name(name, cb_name, sizeof(cb_name)),
1075 callback, user_data);
1076 } /* sdb_plugin_register_cname */
1078 int
1079 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
1080 const sdb_time_t *interval, sdb_object_t *user_data)
1081 {
1082 char cb_name[1024];
1083 sdb_object_t *obj;
1085 if ((! name) || (! callback))
1086 return -1;
1088 if (! collector_list)
1089 collector_list = sdb_llist_create();
1090 if (! collector_list)
1091 return -1;
1093 plugin_get_name(name, cb_name, sizeof(cb_name));
1095 obj = sdb_object_create(cb_name, collector_type,
1096 &collector_list, "collector", callback, user_data);
1097 if (! obj)
1098 return -1;
1100 if (interval)
1101 CCB(obj)->ccb_interval = *interval;
1102 else {
1103 ctx_t *ctx = ctx_get();
1105 if (! ctx) {
1106 sdb_log(SDB_LOG_ERR, "Cannot determine interval for collector %s; "
1107 "none specified and no plugin context found", cb_name);
1108 return -1;
1109 }
1111 CCB(obj)->ccb_interval = ctx->public.interval;
1112 }
1114 if (! (CCB(obj)->ccb_next_update = sdb_gettime())) {
1115 char errbuf[1024];
1116 sdb_log(SDB_LOG_ERR, "Failed to determine current time: %s",
1117 sdb_strerror(errno, errbuf, sizeof(errbuf)));
1118 sdb_object_deref(obj);
1119 return -1;
1120 }
1122 if (sdb_llist_insert_sorted(collector_list, obj,
1123 plugin_cmp_next_update)) {
1124 sdb_object_deref(obj);
1125 return -1;
1126 }
1128 /* pass control to the list */
1129 sdb_object_deref(obj);
1131 sdb_log(SDB_LOG_INFO, "Registered collector callback '%s' "
1132 "(interval = %.3fs).", cb_name,
1133 SDB_TIME_TO_DOUBLE(CCB(obj)->ccb_interval));
1134 return 0;
1135 } /* sdb_plugin_register_collector */
1137 int
1138 sdb_plugin_register_timeseries_fetcher(const char *name,
1139 sdb_timeseries_fetcher_t *fetcher, sdb_object_t *user_data)
1140 {
1141 return plugin_add_impl(×eries_fetcher_list, ts_fetcher_type, "time-series fetcher",
1142 name, fetcher, user_data);
1143 } /* sdb_plugin_register_timeseries_fetcher */
1145 int
1146 sdb_plugin_register_writer(const char *name,
1147 sdb_store_writer_t *writer, sdb_object_t *user_data)
1148 {
1149 char cb_name[1024];
1150 return plugin_add_impl(&writer_list, writer_type, "store writer",
1151 plugin_get_name(name, cb_name, sizeof(cb_name)),
1152 writer, user_data);
1153 } /* sdb_store_register_writer */
1155 int
1156 sdb_plugin_register_reader(const char *name,
1157 sdb_store_reader_t *reader, sdb_object_t *user_data)
1158 {
1159 char cb_name[1024];
1160 return plugin_add_impl(&reader_list, reader_type, "store reader",
1161 plugin_get_name(name, cb_name, sizeof(cb_name)),
1162 reader, user_data);
1163 } /* sdb_plugin_register_reader */
1165 void
1166 sdb_plugin_unregister_all(void)
1167 {
1168 size_t i;
1170 for (i = 0; i < SDB_STATIC_ARRAY_LEN(all_lists); ++i) {
1171 const char *type = all_lists[i].type;
1172 sdb_llist_t *list = *all_lists[i].list;
1174 size_t len = sdb_llist_len(list);
1176 if (! len)
1177 continue;
1179 sdb_llist_clear(list);
1180 sdb_log(SDB_LOG_INFO, "Unregistered %zu %s callback%s",
1181 len, type, len == 1 ? "" : "s");
1182 }
1183 } /* sdb_plugin_unregister_all */
1185 sdb_plugin_ctx_t
1186 sdb_plugin_get_ctx(void)
1187 {
1188 ctx_t *c;
1190 c = ctx_get();
1191 if (! c) {
1192 sdb_log(SDB_LOG_ERR, "Invalid read access to plugin "
1193 "context outside a plugin");
1194 return plugin_default_ctx;
1195 }
1196 return c->public;
1197 } /* sdb_plugin_get_ctx */
1199 int
1200 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx, sdb_plugin_ctx_t *old)
1201 {
1202 ctx_t *c;
1204 c = ctx_get();
1205 if (! c) {
1206 sdb_log(SDB_LOG_ERR, "Invalid write access to plugin "
1207 "context outside a plugin");
1208 return -1;
1209 }
1211 if (old)
1212 *old = c->public;
1213 c->public = ctx;
1214 return 0;
1215 } /* sdb_plugin_set_ctx */
1217 const sdb_plugin_info_t *
1218 sdb_plugin_current(void)
1219 {
1220 ctx_t *ctx = ctx_get();
1222 if (! ctx)
1223 return NULL;
1224 return &ctx->info;
1225 } /* sdb_plugin_current */
1227 int
1228 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
1229 {
1230 callback_t *plugin;
1231 sdb_plugin_config_cb callback;
1233 ctx_t *old_ctx;
1235 int status;
1237 if ((! name) || (! ci))
1238 return -1;
1240 plugin = CB(sdb_llist_search_by_name(config_list, name));
1241 if (! plugin) {
1242 ctx_t *ctx = CTX(sdb_llist_search_by_name(all_plugins, name));
1243 if (! ctx)
1244 sdb_log(SDB_LOG_ERR, "Cannot configure unknown plugin '%s'. "
1245 "Missing 'LoadPlugin \"%s\"'?",
1246 name, name);
1247 else
1248 sdb_log(SDB_LOG_ERR, "Plugin '%s' did not register "
1249 "a config callback.", name);
1250 errno = ENOENT;
1251 return -1;
1252 }
1254 old_ctx = ctx_set(plugin->cb_ctx);
1255 callback = (sdb_plugin_config_cb)plugin->cb_callback;
1256 status = callback(ci);
1257 ctx_set(old_ctx);
1258 return status;
1259 } /* sdb_plugin_configure */
1261 int
1262 sdb_plugin_reconfigure_init(void)
1263 {
1264 sdb_llist_iter_t *iter;
1266 iter = sdb_llist_get_iter(config_list);
1267 if (config_list && (! iter))
1268 return -1;
1270 /* deconfigure all plugins */
1271 while (sdb_llist_iter_has_next(iter)) {
1272 callback_t *plugin;
1273 sdb_plugin_config_cb callback;
1274 ctx_t *old_ctx;
1276 plugin = CB(sdb_llist_iter_get_next(iter));
1277 old_ctx = ctx_set(plugin->cb_ctx);
1278 callback = (sdb_plugin_config_cb)plugin->cb_callback;
1279 callback(NULL);
1280 ctx_set(old_ctx);
1281 }
1282 sdb_llist_iter_destroy(iter);
1284 iter = sdb_llist_get_iter(all_plugins);
1285 if (all_plugins && (! iter))
1286 return -1;
1288 /* record all plugins as being unused */
1289 while (sdb_llist_iter_has_next(iter))
1290 CTX(sdb_llist_iter_get_next(iter))->use_cnt = 0;
1291 sdb_llist_iter_destroy(iter);
1293 sdb_plugin_unregister_all();
1294 return 0;
1295 } /* sdb_plugin_reconfigure_init */
1297 int
1298 sdb_plugin_reconfigure_finish(void)
1299 {
1300 sdb_llist_iter_t *iter;
1302 iter = sdb_llist_get_iter(all_plugins);
1303 if (all_plugins && (! iter))
1304 return -1;
1306 while (sdb_llist_iter_has_next(iter)) {
1307 ctx_t *ctx = CTX(sdb_llist_iter_get_next(iter));
1308 if (ctx->use_cnt)
1309 continue;
1311 sdb_log(SDB_LOG_INFO, "Module %s no longer in use",
1312 ctx->info.plugin_name);
1313 sdb_llist_iter_remove_current(iter);
1314 plugin_unregister_by_name(ctx->info.plugin_name);
1315 sdb_object_deref(SDB_OBJ(ctx));
1316 }
1317 sdb_llist_iter_destroy(iter);
1318 return 0;
1319 } /* sdb_plugin_reconfigure_finish */
1321 int
1322 sdb_plugin_init_all(void)
1323 {
1324 sdb_llist_iter_t *iter;
1325 int ret = 0;
1327 iter = sdb_llist_get_iter(init_list);
1328 while (sdb_llist_iter_has_next(iter)) {
1329 callback_t *cb;
1330 sdb_plugin_init_cb callback;
1331 ctx_t *old_ctx;
1333 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1334 assert(obj);
1335 cb = CB(obj);
1337 callback = (sdb_plugin_init_cb)cb->cb_callback;
1339 old_ctx = ctx_set(cb->cb_ctx);
1340 if (callback(cb->cb_user_data)) {
1341 sdb_log(SDB_LOG_ERR, "Failed to initialize plugin '%s'. "
1342 "Unregistering all callbacks.", obj->name);
1343 ctx_set(old_ctx);
1344 plugin_unregister_by_name(cb->cb_ctx->info.plugin_name);
1345 ++ret;
1346 }
1347 else
1348 ctx_set(old_ctx);
1349 }
1350 sdb_llist_iter_destroy(iter);
1351 return ret;
1352 } /* sdb_plugin_init_all */
1354 int
1355 sdb_plugin_shutdown_all(void)
1356 {
1357 sdb_llist_iter_t *iter;
1358 int ret = 0;
1360 iter = sdb_llist_get_iter(shutdown_list);
1361 while (sdb_llist_iter_has_next(iter)) {
1362 callback_t *cb;
1363 sdb_plugin_shutdown_cb callback;
1364 ctx_t *old_ctx;
1366 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1367 assert(obj);
1368 cb = CB(obj);
1370 callback = (sdb_plugin_shutdown_cb)cb->cb_callback;
1372 old_ctx = ctx_set(cb->cb_ctx);
1373 if (callback(cb->cb_user_data)) {
1374 sdb_log(SDB_LOG_ERR, "Failed to shutdown plugin '%s'.",
1375 obj->name);
1376 ++ret;
1377 }
1378 ctx_set(old_ctx);
1379 }
1380 sdb_llist_iter_destroy(iter);
1381 return ret;
1382 } /* sdb_plugin_shutdown_all */
1384 int
1385 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
1386 {
1387 if (! collector_list) {
1388 sdb_log(SDB_LOG_WARNING, "No collectors registered. "
1389 "Quiting main loop.");
1390 return -1;
1391 }
1393 if (! loop)
1394 return -1;
1396 while (loop->do_loop) {
1397 sdb_plugin_collector_cb callback;
1398 ctx_t *old_ctx;
1400 sdb_time_t interval, now;
1402 sdb_object_t *obj = sdb_llist_shift(collector_list);
1403 if (! obj)
1404 return -1;
1406 callback = (sdb_plugin_collector_cb)CCB(obj)->ccb_callback;
1408 if (! (now = sdb_gettime())) {
1409 char errbuf[1024];
1410 sdb_log(SDB_LOG_ERR, "Failed to determine current "
1411 "time in collector main loop: %s",
1412 sdb_strerror(errno, errbuf, sizeof(errbuf)));
1413 now = CCB(obj)->ccb_next_update;
1414 }
1416 if (now < CCB(obj)->ccb_next_update) {
1417 interval = CCB(obj)->ccb_next_update - now;
1419 errno = 0;
1420 while (loop->do_loop && sdb_sleep(interval, &interval)) {
1421 if (errno != EINTR) {
1422 char errbuf[1024];
1423 sdb_log(SDB_LOG_ERR, "Failed to sleep "
1424 "in collector main loop: %s",
1425 sdb_strerror(errno, errbuf, sizeof(errbuf)));
1426 sdb_llist_insert_sorted(collector_list, obj,
1427 plugin_cmp_next_update);
1428 sdb_object_deref(obj);
1429 return -1;
1430 }
1431 errno = 0;
1432 }
1434 if (! loop->do_loop) {
1435 /* put back; don't worry about errors */
1436 sdb_llist_insert_sorted(collector_list, obj,
1437 plugin_cmp_next_update);
1438 sdb_object_deref(obj);
1439 return 0;
1440 }
1441 }
1443 old_ctx = ctx_set(CCB(obj)->ccb_ctx);
1444 if (callback(CCB(obj)->ccb_user_data)) {
1445 /* XXX */
1446 }
1447 ctx_set(old_ctx);
1449 interval = CCB(obj)->ccb_interval;
1450 if (! interval)
1451 interval = loop->default_interval;
1452 if (! interval) {
1453 sdb_log(SDB_LOG_WARNING, "No interval configured "
1454 "for plugin '%s'; skipping any further "
1455 "iterations.", obj->name);
1456 sdb_object_deref(obj);
1457 continue;
1458 }
1460 CCB(obj)->ccb_next_update += interval;
1462 if (! (now = sdb_gettime())) {
1463 char errbuf[1024];
1464 sdb_log(SDB_LOG_ERR, "Failed to determine current "
1465 "time in collector main loop: %s",
1466 sdb_strerror(errno, errbuf, sizeof(errbuf)));
1467 now = CCB(obj)->ccb_next_update;
1468 }
1470 if (now > CCB(obj)->ccb_next_update) {
1471 sdb_log(SDB_LOG_WARNING, "Plugin '%s' took too "
1472 "long; skipping iterations to keep up.",
1473 obj->name);
1474 CCB(obj)->ccb_next_update = now;
1475 }
1477 if (sdb_llist_insert_sorted(collector_list, obj,
1478 plugin_cmp_next_update)) {
1479 sdb_log(SDB_LOG_ERR, "Failed to re-insert plugin '%s' into "
1480 "collector list. Unable to further use the plugin.",
1481 obj->name);
1482 sdb_object_deref(obj);
1483 return -1;
1484 }
1486 /* pass control back to the list */
1487 sdb_object_deref(obj);
1488 }
1489 return 0;
1490 } /* sdb_plugin_read_loop */
1492 char *
1493 sdb_plugin_cname(char *hostname)
1494 {
1495 sdb_llist_iter_t *iter;
1497 if (! hostname)
1498 return NULL;
1500 if (! cname_list)
1501 return hostname;
1503 iter = sdb_llist_get_iter(cname_list);
1504 while (sdb_llist_iter_has_next(iter)) {
1505 sdb_plugin_cname_cb callback;
1506 char *cname;
1508 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1509 assert(obj);
1511 callback = (sdb_plugin_cname_cb)CB(obj)->cb_callback;
1512 cname = callback(hostname, CB(obj)->cb_user_data);
1513 if (cname) {
1514 free(hostname);
1515 hostname = cname;
1516 }
1517 /* else: don't change hostname */
1518 }
1519 sdb_llist_iter_destroy(iter);
1520 return hostname;
1521 } /* sdb_plugin_cname */
1523 int
1524 sdb_plugin_log(int prio, const char *msg)
1525 {
1526 const sdb_plugin_info_t *info;
1527 int ret = -1;
1529 if (! msg)
1530 return 0;
1532 info = sdb_plugin_current();
1534 {
1535 const char *plugin_name = info ? info->plugin_name : "";
1536 char log_msg[strlen(msg) + strlen(plugin_name) + strlen(" plugin: ") + 1];
1538 sdb_llist_iter_t *iter;
1539 bool logged = 0;
1541 if (*plugin_name)
1542 snprintf(log_msg, sizeof(log_msg), "%s plugin: %s",
1543 plugin_name, msg);
1544 else
1545 strncpy(log_msg, msg, sizeof(log_msg));
1547 iter = sdb_llist_get_iter(log_list);
1548 while (sdb_llist_iter_has_next(iter)) {
1549 sdb_plugin_log_cb callback;
1550 int tmp;
1552 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
1553 assert(obj);
1555 callback = (sdb_plugin_log_cb)CB(obj)->cb_callback;
1556 tmp = callback(prio, msg, CB(obj)->cb_user_data);
1557 if (tmp > ret)
1558 ret = tmp;
1560 if (CB(obj)->cb_ctx)
1561 logged = 1;
1562 /* else: this is an internally registered callback */
1563 }
1564 sdb_llist_iter_destroy(iter);
1566 if (! logged)
1567 return fprintf(stderr, "[%s] %s\n",
1568 SDB_LOG_PRIO_TO_STRING(prio), log_msg);
1569 }
1570 return ret;
1571 } /* sdb_plugin_log */
1573 int
1574 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
1575 {
1576 sdb_strbuf_t *buf;
1577 int ret;
1579 if (! fmt)
1580 return 0;
1582 buf = sdb_strbuf_create(64);
1583 if (! buf) {
1584 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
1585 ret += vfprintf(stderr, fmt, ap);
1586 return ret;
1587 }
1589 if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
1590 sdb_strbuf_destroy(buf);
1591 return -1;
1592 }
1594 ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
1595 sdb_strbuf_destroy(buf);
1596 return ret;
1597 } /* sdb_plugin_vlogf */
1599 int
1600 sdb_plugin_logf(int prio, const char *fmt, ...)
1601 {
1602 va_list ap;
1603 int ret;
1605 if (! fmt)
1606 return 0;
1608 va_start(ap, fmt);
1609 ret = sdb_plugin_vlogf(prio, fmt, ap);
1610 va_end(ap);
1611 return ret;
1612 } /* sdb_plugin_logf */
1614 sdb_timeseries_t *
1615 sdb_plugin_fetch_timeseries(const char *type, const char *id,
1616 sdb_timeseries_opts_t *opts)
1617 {
1618 ts_fetcher_t *fetcher;
1619 sdb_timeseries_t *ts;
1621 ctx_t *old_ctx;
1623 if ((! type) || (! id) || (! opts))
1624 return NULL;
1626 fetcher = TS_FETCHER(sdb_llist_search_by_name(timeseries_fetcher_list, type));
1627 if (! fetcher) {
1628 sdb_log(SDB_LOG_ERR, "Cannot fetch time-series of type %s: "
1629 "no such plugin loaded", type);
1630 errno = ENOENT;
1631 return NULL;
1632 }
1634 old_ctx = ctx_set(fetcher->ts_ctx);
1635 ts = fetcher->impl.fetch(id, opts, fetcher->ts_user_data);
1636 ctx_set(old_ctx);
1637 return ts;
1638 } /* sdb_plugin_fetch_timeseries */
1640 sdb_timeseries_info_t *
1641 sdb_plugin_describe_timeseries(const char *type, const char *id)
1642 {
1643 ts_fetcher_t *fetcher;
1644 sdb_timeseries_info_t *ts_info;
1646 ctx_t *old_ctx;
1648 if ((! type) || (! id))
1649 return NULL;
1651 fetcher = TS_FETCHER(sdb_llist_search_by_name(timeseries_fetcher_list, type));
1652 if (! fetcher) {
1653 sdb_log(SDB_LOG_ERR, "Cannot describe time-series of type %s: "
1654 "no such plugin loaded", type);
1655 errno = ENOENT;
1656 return NULL;
1657 }
1659 old_ctx = ctx_set(fetcher->ts_ctx);
1660 ts_info = fetcher->impl.describe(id, fetcher->ts_user_data);
1661 ctx_set(old_ctx);
1662 return ts_info;
1663 } /* sdb_plugin_describe_timeseries */
1665 int
1666 sdb_plugin_query(sdb_ast_node_t *ast,
1667 sdb_store_writer_t *w, sdb_object_t *wd,
1668 sdb_query_opts_t *opts, sdb_strbuf_t *errbuf)
1669 {
1670 query_writer_t qw = QUERY_WRITER_INIT(w, wd);
1671 reader_t *reader;
1672 sdb_object_t *q;
1674 size_t n = sdb_llist_len(reader_list);
1675 int status = 0;
1677 if (! ast)
1678 return 0;
1680 if (opts)
1681 qw.opts = *opts;
1683 if ((ast->type != SDB_AST_TYPE_FETCH)
1684 && (ast->type != SDB_AST_TYPE_LIST)
1685 && (ast->type != SDB_AST_TYPE_LOOKUP)) {
1686 sdb_log(SDB_LOG_ERR, "Cannot execute query of type %s",
1687 SDB_AST_TYPE_TO_STRING(ast));
1688 sdb_strbuf_sprintf(errbuf, "Cannot execute query of type %s",
1689 SDB_AST_TYPE_TO_STRING(ast));
1690 return -1;
1691 }
1693 if (n != 1) {
1694 char *msg = (n > 0)
1695 ? "Cannot execute query: multiple readers not supported"
1696 : "Cannot execute query: no readers registered";
1697 sdb_strbuf_sprintf(errbuf, "%s", msg);
1698 sdb_log(SDB_LOG_ERR, "%s", msg);
1699 return -1;
1700 }
1702 reader = READER(sdb_llist_get(reader_list, 0));
1703 assert(reader);
1705 q = reader->impl.prepare_query(ast, errbuf, reader->r_user_data);
1706 if (q)
1707 status = reader->impl.execute_query(q, &query_writer, SDB_OBJ(&qw),
1708 errbuf, reader->r_user_data);
1709 else
1710 status = -1;
1712 sdb_object_deref(SDB_OBJ(q));
1713 sdb_object_deref(SDB_OBJ(reader));
1714 return status;
1715 } /* sdb_plugin_query */
1717 int
1718 sdb_plugin_store_host(const char *name, sdb_time_t last_update)
1719 {
1720 sdb_store_host_t host = SDB_STORE_HOST_INIT;
1721 char *backends[1];
1722 char *cname;
1724 sdb_llist_iter_t *iter;
1725 int status = 0;
1727 if (! name)
1728 return -1;
1730 if (! sdb_llist_len(writer_list)) {
1731 sdb_log(SDB_LOG_ERR, "Cannot store host: no writers registered");
1732 return -1;
1733 }
1735 cname = sdb_plugin_cname(strdup(name));
1736 if (! cname) {
1737 sdb_log(SDB_LOG_ERR, "strdup failed");
1738 return -1;
1739 }
1741 host.name = cname;
1742 host.last_update = last_update ? last_update : sdb_gettime();
1743 if (get_interval(SDB_HOST, NULL, -1, NULL, cname,
1744 host.last_update, &host.interval)) {
1745 free(cname);
1746 return 1;
1747 }
1748 host.backends = (const char * const *)backends;
1749 get_backend(backends, &host.backends_num);
1751 iter = sdb_llist_get_iter(writer_list);
1752 while (sdb_llist_iter_has_next(iter)) {
1753 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1754 int s;
1755 assert(writer);
1756 s = writer->impl.store_host(&host, writer->w_user_data);
1757 if (((s > 0) && (status >= 0)) || (s < 0))
1758 status = s;
1759 }
1760 sdb_llist_iter_destroy(iter);
1761 free(cname);
1762 return status;
1763 } /* sdb_plugin_store_host */
1765 int
1766 sdb_plugin_store_service(const char *hostname, const char *name,
1767 sdb_time_t last_update)
1768 {
1769 sdb_store_service_t service = SDB_STORE_SERVICE_INIT;
1770 char *backends[1];
1771 char *cname;
1773 sdb_llist_iter_t *iter;
1774 sdb_data_t d;
1776 int status = 0;
1778 if ((! hostname) || (! name))
1779 return -1;
1781 if (! sdb_llist_len(writer_list)) {
1782 sdb_log(SDB_LOG_ERR, "Cannot store service: "
1783 "no writers registered");
1784 return -1;
1785 }
1787 cname = sdb_plugin_cname(strdup(hostname));
1788 if (! cname) {
1789 sdb_log(SDB_LOG_ERR, "strdup failed");
1790 return -1;
1791 }
1793 service.hostname = cname;
1794 service.name = name;
1795 service.last_update = last_update ? last_update : sdb_gettime();
1796 if (get_interval(SDB_SERVICE, cname, -1, NULL, name,
1797 service.last_update, &service.interval)) {
1798 free(cname);
1799 return 1;
1800 }
1801 service.backends = (const char * const *)backends;
1802 get_backend(backends, &service.backends_num);
1804 iter = sdb_llist_get_iter(writer_list);
1805 while (sdb_llist_iter_has_next(iter)) {
1806 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1807 int s;
1808 assert(writer);
1809 s = writer->impl.store_service(&service, writer->w_user_data);
1810 if (((s > 0) && (status >= 0)) || (s < 0))
1811 status = s;
1812 }
1813 sdb_llist_iter_destroy(iter);
1815 if (! status) {
1816 /* record the hostname as an attribute */
1817 d.type = SDB_TYPE_STRING;
1818 d.data.string = cname;
1819 if (sdb_plugin_store_service_attribute(cname, name,
1820 "hostname", &d, service.last_update))
1821 status = -1;
1822 }
1824 free(cname);
1825 return status;
1826 } /* sdb_plugin_store_service */
1828 int
1829 sdb_plugin_store_metric(const char *hostname, const char *name,
1830 sdb_metric_store_t *store, sdb_time_t last_update)
1831 {
1832 sdb_store_metric_t metric = SDB_STORE_METRIC_INIT;
1833 char *backends[1];
1834 char *cname;
1836 sdb_llist_iter_t *iter;
1837 sdb_data_t d;
1839 int status = 0;
1841 if ((! hostname) || (! name))
1842 return -1;
1844 if (! sdb_llist_len(writer_list)) {
1845 sdb_log(SDB_LOG_ERR, "Cannot store metric: no writers registered");
1846 return -1;
1847 }
1849 cname = sdb_plugin_cname(strdup(hostname));
1850 if (! cname) {
1851 sdb_log(SDB_LOG_ERR, "strdup failed");
1852 return -1;
1853 }
1855 if (store && ((! store->type) || (! store->id)))
1856 store = NULL;
1858 metric.hostname = cname;
1859 metric.name = name;
1860 if (store) {
1861 if (store->last_update < last_update)
1862 store->last_update = last_update;
1863 metric.stores = store;
1864 metric.stores_num = 1;
1865 }
1866 metric.last_update = last_update ? last_update : sdb_gettime();
1867 if (get_interval(SDB_METRIC, cname, -1, NULL, name,
1868 metric.last_update, &metric.interval)) {
1869 free(cname);
1870 return 1;
1871 }
1872 metric.backends = (const char * const *)backends;
1873 get_backend(backends, &metric.backends_num);
1875 iter = sdb_llist_get_iter(writer_list);
1876 while (sdb_llist_iter_has_next(iter)) {
1877 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1878 int s;
1879 assert(writer);
1880 s = writer->impl.store_metric(&metric, writer->w_user_data);
1881 if (((s > 0) && (status >= 0)) || (s < 0))
1882 status = s;
1883 }
1884 sdb_llist_iter_destroy(iter);
1886 if (! status) {
1887 /* record the hostname as an attribute */
1888 d.type = SDB_TYPE_STRING;
1889 d.data.string = cname;
1890 if (sdb_plugin_store_metric_attribute(cname, name,
1891 "hostname", &d, metric.last_update))
1892 status = -1;
1893 }
1895 free(cname);
1896 return status;
1897 } /* sdb_plugin_store_metric */
1899 int
1900 sdb_plugin_store_attribute(const char *hostname, const char *key,
1901 const sdb_data_t *value, sdb_time_t last_update)
1902 {
1903 sdb_store_attribute_t attr = SDB_STORE_ATTRIBUTE_INIT;
1904 char *backends[1];
1905 char *cname;
1907 sdb_llist_iter_t *iter;
1908 int status = 0;
1910 if ((! hostname) || (! key) || (! value))
1911 return -1;
1913 if (! sdb_llist_len(writer_list)) {
1914 sdb_log(SDB_LOG_ERR, "Cannot store attribute: no writers registered");
1915 return -1;
1916 }
1918 cname = sdb_plugin_cname(strdup(hostname));
1919 if (! cname) {
1920 sdb_log(SDB_LOG_ERR, "strdup failed");
1921 return -1;
1922 }
1924 attr.parent_type = SDB_HOST;
1925 attr.parent = cname;
1926 attr.key = key;
1927 attr.value = *value;
1928 attr.last_update = last_update ? last_update : sdb_gettime();
1929 if (get_interval(SDB_ATTRIBUTE, cname, -1, NULL, key,
1930 attr.last_update, &attr.interval)) {
1931 free(cname);
1932 return 1;
1933 }
1934 attr.backends = (const char * const *)backends;
1935 get_backend(backends, &attr.backends_num);
1937 iter = sdb_llist_get_iter(writer_list);
1938 while (sdb_llist_iter_has_next(iter)) {
1939 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1940 int s;
1941 assert(writer);
1942 s = writer->impl.store_attribute(&attr, writer->w_user_data);
1943 if (((s > 0) && (status >= 0)) || (s < 0))
1944 status = s;
1945 }
1946 sdb_llist_iter_destroy(iter);
1947 free(cname);
1948 return status;
1949 } /* sdb_plugin_store_attribute */
1951 int
1952 sdb_plugin_store_service_attribute(const char *hostname, const char *service,
1953 const char *key, const sdb_data_t *value, sdb_time_t last_update)
1954 {
1955 sdb_store_attribute_t attr = SDB_STORE_ATTRIBUTE_INIT;
1956 char *backends[1];
1957 char *cname;
1959 sdb_llist_iter_t *iter;
1960 int status = 0;
1962 if ((! hostname) || (! service) || (! key) || (! value))
1963 return -1;
1965 if (! sdb_llist_len(writer_list)) {
1966 sdb_log(SDB_LOG_ERR, "Cannot store service attribute: "
1967 "no writers registered");
1968 return -1;
1969 }
1971 cname = sdb_plugin_cname(strdup(hostname));
1972 if (! cname) {
1973 sdb_log(SDB_LOG_ERR, "strdup failed");
1974 return -1;
1975 }
1977 attr.hostname = cname;
1978 attr.parent_type = SDB_SERVICE;
1979 attr.parent = service;
1980 attr.key = key;
1981 attr.value = *value;
1982 attr.last_update = last_update ? last_update : sdb_gettime();
1983 if (get_interval(SDB_ATTRIBUTE, cname, SDB_SERVICE, service, key,
1984 attr.last_update, &attr.interval)) {
1985 free(cname);
1986 return 1;
1987 }
1988 attr.backends = (const char * const *)backends;
1989 get_backend(backends, &attr.backends_num);
1991 iter = sdb_llist_get_iter(writer_list);
1992 while (sdb_llist_iter_has_next(iter)) {
1993 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
1994 int s;
1995 assert(writer);
1996 s = writer->impl.store_attribute(&attr, writer->w_user_data);
1997 if (((s > 0) && (status >= 0)) || (s < 0))
1998 status = s;
1999 }
2000 sdb_llist_iter_destroy(iter);
2001 free(cname);
2002 return status;
2003 } /* sdb_plugin_store_service_attribute */
2005 int
2006 sdb_plugin_store_metric_attribute(const char *hostname, const char *metric,
2007 const char *key, const sdb_data_t *value, sdb_time_t last_update)
2008 {
2009 sdb_store_attribute_t attr = SDB_STORE_ATTRIBUTE_INIT;
2010 char *backends[1];
2011 char *cname;
2013 sdb_llist_iter_t *iter;
2014 int status = 0;
2016 if ((! hostname) || (! metric) || (! key) || (! value))
2017 return -1;
2019 if (! sdb_llist_len(writer_list)) {
2020 sdb_log(SDB_LOG_ERR, "Cannot store metric attribute: "
2021 "no writers registered");
2022 return -1;
2023 }
2025 cname = sdb_plugin_cname(strdup(hostname));
2026 if (! cname) {
2027 sdb_log(SDB_LOG_ERR, "strdup failed");
2028 return -1;
2029 }
2031 attr.hostname = cname;
2032 attr.parent_type = SDB_METRIC;
2033 attr.parent = metric;
2034 attr.key = key;
2035 attr.value = *value;
2036 attr.last_update = last_update ? last_update : sdb_gettime();
2037 if (get_interval(SDB_ATTRIBUTE, cname, SDB_METRIC, metric, key,
2038 attr.last_update, &attr.interval)) {
2039 free(cname);
2040 return 1;
2041 }
2042 attr.backends = (const char * const *)backends;
2043 get_backend(backends, &attr.backends_num);
2045 iter = sdb_llist_get_iter(writer_list);
2046 while (sdb_llist_iter_has_next(iter)) {
2047 writer_t *writer = WRITER(sdb_llist_iter_get_next(iter));
2048 int s;
2049 assert(writer);
2050 s = writer->impl.store_attribute(&attr, writer->w_user_data);
2051 if (((s > 0) && (status >= 0)) || (s < 0))
2052 status = s;
2053 }
2054 sdb_llist_iter_destroy(iter);
2055 free(cname);
2056 return status;
2057 } /* sdb_plugin_store_metric_attribute */
2059 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */