26677979b26257e4a9ef89aeed363e9d032e3418
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 #include "sysdb.h"
29 #include "core/plugin.h"
30 #include "core/error.h"
31 #include "core/time.h"
32 #include "utils/llist.h"
33 #include "utils/strbuf.h"
35 #include <assert.h>
37 #include <errno.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <unistd.h>
45 #include <ltdl.h>
47 #include <pthread.h>
49 /*
50 * private data types
51 */
53 struct sdb_plugin_info {
54 char *name;
56 char *description;
57 char *copyright;
58 char *license;
60 int version;
61 int plugin_version;
62 };
63 #define SDB_PLUGIN_INFO_INIT { /* name */ NULL, /* desc */ NULL, \
64 /* copyright */ NULL, /* license */ NULL, \
65 /* version */ -1, /* plugin_version */ -1 }
66 #define INFO_GET(i, attr) \
67 ((i)->attr ? (i)->attr : #attr" not set")
69 typedef struct {
70 sdb_object_t super;
71 void *cb_callback;
72 sdb_object_t *cb_user_data;
73 sdb_plugin_ctx_t cb_ctx;
74 } sdb_plugin_cb_t;
75 #define SDB_PLUGIN_CB_INIT { SDB_OBJECT_INIT, \
76 /* callback = */ NULL, /* user_data = */ NULL, \
77 SDB_PLUGIN_CTX_INIT }
79 typedef struct {
80 sdb_plugin_cb_t super;
81 #define ccb_callback super.cb_callback
82 #define ccb_user_data super.cb_user_data
83 #define ccb_ctx super.cb_ctx
84 sdb_time_t ccb_interval;
85 sdb_time_t ccb_next_update;
86 } sdb_plugin_collector_cb_t;
88 #define SDB_PLUGIN_CB(obj) ((sdb_plugin_cb_t *)(obj))
89 #define SDB_PLUGIN_CCB(obj) ((sdb_plugin_collector_cb_t *)(obj))
91 /*
92 * private variables
93 */
95 static sdb_plugin_ctx_t plugin_default_ctx = SDB_PLUGIN_CTX_INIT;
97 static pthread_key_t plugin_ctx_key;
98 static _Bool plugin_ctx_key_initialized = 0;
100 static sdb_llist_t *config_list = NULL;
101 static sdb_llist_t *init_list = NULL;
102 static sdb_llist_t *collector_list = NULL;
103 static sdb_llist_t *cname_list = NULL;
104 static sdb_llist_t *shutdown_list = NULL;
105 static sdb_llist_t *log_list = NULL;
107 /*
108 * private helper functions
109 */
111 static void
112 sdb_plugin_info_clear(sdb_plugin_info_t *info)
113 {
114 sdb_plugin_info_t empty_info = SDB_PLUGIN_INFO_INIT;
115 if (! info)
116 return;
118 if (info->name)
119 free(info->name);
120 if (info->description)
121 free(info->description);
122 if (info->copyright)
123 free(info->copyright);
124 if (info->license)
125 free(info->license);
127 *info = empty_info;
128 } /* sdb_plugin_info_clear */
130 static void
131 sdb_plugin_ctx_destructor(void *ctx)
132 {
133 if (! ctx)
134 return;
135 free(ctx);
136 } /* sdb_plugin_ctx_destructor */
138 static void
139 sdb_plugin_ctx_init(void)
140 {
141 if (plugin_ctx_key_initialized)
142 return;
144 pthread_key_create(&plugin_ctx_key, sdb_plugin_ctx_destructor);
145 plugin_ctx_key_initialized = 1;
146 } /* sdb_plugin_ctx_init */
148 static sdb_plugin_ctx_t *
149 sdb_plugin_ctx_create(void)
150 {
151 sdb_plugin_ctx_t *ctx;
153 ctx = malloc(sizeof(*ctx));
154 if (! ctx)
155 return NULL;
157 *ctx = plugin_default_ctx;
159 if (! plugin_ctx_key_initialized)
160 sdb_plugin_ctx_init();
161 pthread_setspecific(plugin_ctx_key, ctx);
162 return ctx;
163 } /* sdb_plugin_ctx_create */
165 static int
166 sdb_plugin_cmp_next_update(const sdb_object_t *a, const sdb_object_t *b)
167 {
168 const sdb_plugin_collector_cb_t *ccb1
169 = (const sdb_plugin_collector_cb_t *)a;
170 const sdb_plugin_collector_cb_t *ccb2
171 = (const sdb_plugin_collector_cb_t *)b;
173 assert(ccb1 && ccb2);
175 return (ccb1->ccb_next_update > ccb2->ccb_next_update)
176 ? 1 : (ccb1->ccb_next_update < ccb2->ccb_next_update)
177 ? -1 : 0;
178 } /* sdb_plugin_cmp_next_update */
180 /*
181 * private types
182 */
184 static int
185 sdb_plugin_cb_init(sdb_object_t *obj, va_list ap)
186 {
187 sdb_llist_t **list = va_arg(ap, sdb_llist_t **);
188 const char *type = va_arg(ap, const char *);
189 void *callback = va_arg(ap, void *);
190 sdb_object_t *ud = va_arg(ap, sdb_object_t *);
192 assert(list);
193 assert(type);
194 assert(obj);
196 if (sdb_llist_search_by_name(*list, obj->name)) {
197 sdb_log(SDB_LOG_WARNING, "plugin: %s callback '%s' "
198 "has already been registered. Ignoring newly "
199 "registered version.", type, obj->name);
200 return -1;
201 }
203 SDB_PLUGIN_CB(obj)->cb_callback = callback;
204 SDB_PLUGIN_CB(obj)->cb_ctx = sdb_plugin_get_ctx();
206 sdb_object_ref(ud);
207 SDB_PLUGIN_CB(obj)->cb_user_data = ud;
208 return 0;
209 } /* sdb_plugin_cb_init */
211 static void
212 sdb_plugin_cb_destroy(sdb_object_t *obj)
213 {
214 assert(obj);
215 sdb_object_deref(SDB_PLUGIN_CB(obj)->cb_user_data);
216 } /* sdb_plugin_cb_destroy */
218 static sdb_type_t sdb_plugin_cb_type = {
219 sizeof(sdb_plugin_cb_t),
221 sdb_plugin_cb_init,
222 sdb_plugin_cb_destroy
223 };
225 static sdb_type_t sdb_plugin_collector_cb_type = {
226 sizeof(sdb_plugin_collector_cb_t),
228 sdb_plugin_cb_init,
229 sdb_plugin_cb_destroy
230 };
232 static int
233 sdb_plugin_add_callback(sdb_llist_t **list, const char *type,
234 const char *name, void *callback, sdb_object_t *user_data)
235 {
236 sdb_object_t *obj;
238 if ((! name) || (! callback))
239 return -1;
241 assert(list);
243 if (! *list)
244 *list = sdb_llist_create();
245 if (! *list)
246 return -1;
248 obj = sdb_object_create(name, sdb_plugin_cb_type,
249 list, type, callback, user_data);
250 if (! obj)
251 return -1;
253 if (sdb_llist_append(*list, obj)) {
254 sdb_object_deref(obj);
255 return -1;
256 }
258 /* pass control to the list */
259 sdb_object_deref(obj);
261 sdb_log(SDB_LOG_INFO, "plugin: Registered %s callback '%s'.",
262 type, name);
263 return 0;
264 } /* sdb_plugin_add_callback */
266 /*
267 * public API
268 */
270 int
271 sdb_plugin_load(const char *name)
272 {
273 char real_name[strlen(name) > 0 ? strlen(name) : 1];
274 const char *name_ptr;
275 char *tmp;
277 char filename[1024];
279 lt_dlhandle lh;
281 int (*mod_init)(sdb_plugin_info_t *);
282 sdb_plugin_info_t info = SDB_PLUGIN_INFO_INIT;
284 int status;
286 if ((! name) || (! *name))
287 return -1;
289 real_name[0] = '\0';
290 name_ptr = name;
292 while ((tmp = strstr(name_ptr, "::"))) {
293 strncat(real_name, name_ptr, (size_t)(tmp - name_ptr));
294 strcat(real_name, "/");
295 name_ptr = tmp + strlen("::");
296 }
297 strcat(real_name, name_ptr);
299 snprintf(filename, sizeof(filename), "%s/%s.so",
300 PKGLIBDIR, real_name);
301 filename[sizeof(filename) - 1] = '\0';
303 if (access(filename, R_OK)) {
304 char errbuf[1024];
305 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s' (%s): %s",
306 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
307 return -1;
308 }
310 lt_dlinit();
311 lt_dlerror();
313 lh = lt_dlopen(filename);
314 if (! lh) {
315 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s': %s"
316 "The most common cause for this problem are missing "
317 "dependencies.\n", name, lt_dlerror());
318 return -1;
319 }
321 mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
322 if (! mod_init) {
323 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s': "
324 "could not find symbol 'sdb_module_init'", name);
325 return -1;
326 }
328 status = mod_init(&info);
329 if (status) {
330 sdb_log(SDB_LOG_ERR, "plugin: Failed to initialize "
331 "plugin '%s'", name);
332 sdb_plugin_info_clear(&info);
333 return -1;
334 }
336 /* compare minor version */
337 if ((info.version < 0)
338 || ((int)(info.version / 100) != (int)(SDB_VERSION / 100)))
339 sdb_log(SDB_LOG_WARNING, "plugin: WARNING: version of "
340 "plugin '%s' (%i.%i.%i) does not match our version "
341 "(%i.%i.%i); this might cause problems",
342 name, SDB_VERSION_DECODE(info.version),
343 SDB_VERSION_DECODE(SDB_VERSION));
345 sdb_log(SDB_LOG_INFO, "plugin: Successfully loaded "
346 "plugin '%s' v%i (%s)\n\t%s\n\tLicense: %s",
347 INFO_GET(&info, name), info.plugin_version,
348 INFO_GET(&info, description),
349 INFO_GET(&info, copyright),
350 INFO_GET(&info, license));
351 sdb_plugin_info_clear(&info);
352 return 0;
353 } /* sdb_plugin_load */
355 int
356 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
357 {
358 va_list ap;
360 if (! info)
361 return -1;
363 va_start(ap, type);
365 switch (type) {
366 case SDB_PLUGIN_INFO_NAME:
367 {
368 char *name = va_arg(ap, char *);
369 if (name) {
370 if (info->name)
371 free(info->name);
372 info->name = strdup(name);
373 }
374 }
375 break;
376 case SDB_PLUGIN_INFO_DESC:
377 {
378 char *desc = va_arg(ap, char *);
379 if (desc) {
380 if (info->description)
381 free(info->description);
382 info->description = strdup(desc);
383 }
384 }
385 break;
386 case SDB_PLUGIN_INFO_COPYRIGHT:
387 {
388 char *copyright = va_arg(ap, char *);
389 if (copyright)
390 info->copyright = strdup(copyright);
391 }
392 break;
393 case SDB_PLUGIN_INFO_LICENSE:
394 {
395 char *license = va_arg(ap, char *);
396 if (license) {
397 if (info->license)
398 free(info->license);
399 info->license = strdup(license);
400 }
401 }
402 break;
403 case SDB_PLUGIN_INFO_VERSION:
404 {
405 int version = va_arg(ap, int);
406 info->version = version;
407 }
408 break;
409 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
410 {
411 int version = va_arg(ap, int);
412 info->plugin_version = version;
413 }
414 break;
415 default:
416 va_end(ap);
417 return -1;
418 }
420 va_end(ap);
421 return 0;
422 } /* sdb_plugin_set_info */
424 int
425 sdb_plugin_register_config(const char *name, sdb_plugin_config_cb callback)
426 {
427 return sdb_plugin_add_callback(&config_list, "init", name,
428 callback, NULL);
429 } /* sdb_plugin_register_config */
431 int
432 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
433 sdb_object_t *user_data)
434 {
435 return sdb_plugin_add_callback(&init_list, "init", name,
436 callback, user_data);
437 } /* sdb_plugin_register_init */
439 int
440 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
441 sdb_object_t *user_data)
442 {
443 return sdb_plugin_add_callback(&shutdown_list, "shutdown", name,
444 callback, user_data);
445 } /* sdb_plugin_register_shutdown */
447 int
448 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
449 sdb_object_t *user_data)
450 {
451 return sdb_plugin_add_callback(&log_list, "log", name, callback,
452 user_data);
453 } /* sdb_plugin_register_log */
455 int
456 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
457 sdb_object_t *user_data)
458 {
459 return sdb_plugin_add_callback(&cname_list, "cname", name, callback,
460 user_data);
461 } /* sdb_plugin_register_cname */
463 int
464 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
465 const sdb_time_t *interval, sdb_object_t *user_data)
466 {
467 sdb_object_t *obj;
469 if ((! name) || (! callback))
470 return -1;
472 if (! collector_list)
473 collector_list = sdb_llist_create();
474 if (! collector_list)
475 return -1;
477 obj = sdb_object_create(name, sdb_plugin_collector_cb_type,
478 &collector_list, "collector", callback, user_data);
479 if (! obj)
480 return -1;
482 if (interval)
483 SDB_PLUGIN_CCB(obj)->ccb_interval = *interval;
484 else {
485 sdb_time_t tmp = sdb_plugin_get_ctx().interval;
487 if (tmp > 0)
488 SDB_PLUGIN_CCB(obj)->ccb_interval = tmp;
489 else
490 SDB_PLUGIN_CCB(obj)->ccb_interval = 0;
491 }
493 if (! (SDB_PLUGIN_CCB(obj)->ccb_next_update = sdb_gettime())) {
494 char errbuf[1024];
495 sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
496 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
497 sdb_object_deref(obj);
498 return -1;
499 }
501 if (sdb_llist_insert_sorted(collector_list, obj,
502 sdb_plugin_cmp_next_update)) {
503 sdb_object_deref(obj);
504 return -1;
505 }
507 /* pass control to the list */
508 sdb_object_deref(obj);
510 sdb_log(SDB_LOG_INFO, "plugin: Registered collector callback '%s' "
511 "(interval = %.3fs).", name,
512 SDB_TIME_TO_DOUBLE(SDB_PLUGIN_CCB(obj)->ccb_interval));
513 return 0;
514 } /* sdb_plugin_register_collector */
516 sdb_plugin_ctx_t
517 sdb_plugin_get_ctx(void)
518 {
519 sdb_plugin_ctx_t *ctx;
521 if (! plugin_ctx_key_initialized)
522 sdb_plugin_ctx_init();
523 ctx = pthread_getspecific(plugin_ctx_key);
525 if (! ctx)
526 ctx = sdb_plugin_ctx_create();
527 if (! ctx)
528 return plugin_default_ctx;
529 return *ctx;
530 } /* sdb_plugin_get_ctx */
532 sdb_plugin_ctx_t
533 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx)
534 {
535 sdb_plugin_ctx_t *tmp;
536 sdb_plugin_ctx_t old;
538 if (! plugin_ctx_key_initialized)
539 sdb_plugin_ctx_init();
540 tmp = pthread_getspecific(plugin_ctx_key);
542 if (! tmp)
543 tmp = sdb_plugin_ctx_create();
544 if (! tmp)
545 return plugin_default_ctx;
547 old = *tmp;
548 *tmp = ctx;
549 return old;
550 } /* sdb_plugin_set_ctx */
552 int
553 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
554 {
555 sdb_plugin_cb_t *plugin;
556 sdb_plugin_config_cb callback;
558 sdb_plugin_ctx_t old_ctx;
560 int status;
562 if ((! name) || (! ci))
563 return -1;
565 plugin = SDB_PLUGIN_CB(sdb_llist_search_by_name(config_list, name));
566 if (! plugin) {
567 /* XXX: check if any such plugin has been loaded */
568 sdb_log(SDB_LOG_ERR, "plugin: Plugin '%s' did not register "
569 "a config callback.", name);
570 errno = ENOENT;
571 return -1;
572 }
574 old_ctx = sdb_plugin_set_ctx(plugin->cb_ctx);
575 callback = plugin->cb_callback;
576 status = callback(ci);
577 sdb_plugin_set_ctx(old_ctx);
578 return status;
579 } /* sdb_plugin_configure */
581 int
582 sdb_plugin_init_all(void)
583 {
584 sdb_llist_iter_t *iter;
586 iter = sdb_llist_get_iter(init_list);
587 while (sdb_llist_iter_has_next(iter)) {
588 sdb_plugin_init_cb callback;
589 sdb_plugin_ctx_t old_ctx;
591 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
592 assert(obj);
594 callback = SDB_PLUGIN_CB(obj)->cb_callback;
596 old_ctx = sdb_plugin_set_ctx(SDB_PLUGIN_CB(obj)->cb_ctx);
597 if (callback(SDB_PLUGIN_CB(obj)->cb_user_data)) {
598 /* XXX: unload plugin */
599 }
600 sdb_plugin_set_ctx(old_ctx);
601 }
602 sdb_llist_iter_destroy(iter);
603 return 0;
604 } /* sdb_plugin_init_all */
606 int
607 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
608 {
609 if (! collector_list) {
610 sdb_log(SDB_LOG_WARNING, "plugin: No collectors registered. "
611 "Quiting main loop.");
612 return -1;
613 }
615 if (! loop)
616 return -1;
618 while (loop->do_loop) {
619 sdb_plugin_collector_cb callback;
620 sdb_plugin_ctx_t old_ctx;
622 sdb_time_t interval, now;
624 sdb_object_t *obj = sdb_llist_shift(collector_list);
625 if (! obj)
626 return -1;
628 callback = SDB_PLUGIN_CCB(obj)->ccb_callback;
630 if (! (now = sdb_gettime())) {
631 char errbuf[1024];
632 sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
633 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
634 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
635 }
637 if (now < SDB_PLUGIN_CCB(obj)->ccb_next_update) {
638 interval = SDB_PLUGIN_CCB(obj)->ccb_next_update - now;
640 errno = 0;
641 while (loop->do_loop && sdb_sleep(interval, &interval)) {
642 if (errno != EINTR) {
643 char errbuf[1024];
644 sdb_log(SDB_LOG_ERR, "plugin: Failed to sleep: %s",
645 sdb_strerror(errno, errbuf, sizeof(errbuf)));
646 return -1;
647 }
648 errno = 0;
649 }
651 if (! loop->do_loop)
652 return 0;
653 }
655 old_ctx = sdb_plugin_set_ctx(SDB_PLUGIN_CCB(obj)->ccb_ctx);
656 if (callback(SDB_PLUGIN_CCB(obj)->ccb_user_data)) {
657 /* XXX */
658 }
659 sdb_plugin_set_ctx(old_ctx);
661 interval = SDB_PLUGIN_CCB(obj)->ccb_interval;
662 if (! interval)
663 interval = loop->default_interval;
664 if (! interval) {
665 sdb_log(SDB_LOG_WARNING, "plugin: No interval configured "
666 "for plugin '%s'; skipping any further "
667 "iterations.", obj->name);
668 sdb_object_deref(obj);
669 continue;
670 }
672 SDB_PLUGIN_CCB(obj)->ccb_next_update += interval;
674 if (! (now = sdb_gettime())) {
675 char errbuf[1024];
676 sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
677 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
678 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
679 }
681 if (now > SDB_PLUGIN_CCB(obj)->ccb_next_update) {
682 sdb_log(SDB_LOG_WARNING, "plugin: Plugin '%s' took too "
683 "long; skipping iterations to keep up.",
684 obj->name);
685 SDB_PLUGIN_CCB(obj)->ccb_next_update = now;
686 }
688 if (sdb_llist_insert_sorted(collector_list, obj,
689 sdb_plugin_cmp_next_update)) {
690 sdb_log(SDB_LOG_ERR, "plugin: Failed to re-insert "
691 "plugin '%s' into collector list. Unable to further "
692 "use the plugin.",
693 obj->name);
694 sdb_object_deref(obj);
695 return -1;
696 }
698 /* pass control back to the list */
699 sdb_object_deref(obj);
700 }
701 return 0;
702 } /* sdb_plugin_read_loop */
704 char *
705 sdb_plugin_cname(char *hostname)
706 {
707 sdb_llist_iter_t *iter;
709 if (! hostname)
710 return NULL;
712 if (! cname_list)
713 return hostname;
715 iter = sdb_llist_get_iter(cname_list);
716 while (sdb_llist_iter_has_next(iter)) {
717 sdb_plugin_cname_cb callback;
718 char *cname;
720 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
721 assert(obj);
723 callback = SDB_PLUGIN_CB(obj)->cb_callback;
724 cname = callback(hostname, SDB_PLUGIN_CB(obj)->cb_user_data);
725 if (cname) {
726 free(hostname);
727 hostname = cname;
728 }
729 /* else: don't change hostname */
730 }
731 sdb_llist_iter_destroy(iter);
732 return hostname;
733 } /* sdb_plugin_cname */
735 int
736 sdb_plugin_log(int prio, const char *msg)
737 {
738 sdb_llist_iter_t *iter;
739 int ret = -1;
741 if (! msg)
742 return 0;
744 if (! log_list)
745 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
747 iter = sdb_llist_get_iter(log_list);
748 while (sdb_llist_iter_has_next(iter)) {
749 sdb_plugin_log_cb callback;
750 int tmp;
752 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
753 assert(obj);
755 callback = SDB_PLUGIN_CB(obj)->cb_callback;
756 tmp = callback(prio, msg, SDB_PLUGIN_CB(obj)->cb_user_data);
757 if (tmp > ret)
758 ret = tmp;
759 }
760 sdb_llist_iter_destroy(iter);
761 return ret;
762 } /* sdb_plugin_log */
764 int
765 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
766 {
767 sdb_strbuf_t *buf;
768 int ret;
770 if (! fmt)
771 return 0;
773 buf = sdb_strbuf_create(64);
774 if (! buf) {
775 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
776 ret += vfprintf(stderr, fmt, ap);
777 return ret;
778 }
780 if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
781 sdb_strbuf_destroy(buf);
782 return -1;
783 }
785 ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
786 sdb_strbuf_destroy(buf);
787 return ret;
788 } /* sdb_plugin_vlogf */
790 int
791 sdb_plugin_logf(int prio, const char *fmt, ...)
792 {
793 va_list ap;
794 int ret;
796 if (! fmt)
797 return 0;
799 va_start(ap, fmt);
800 ret = sdb_plugin_vlogf(prio, fmt, ap);
801 va_end(ap);
802 return ret;
803 } /* sdb_plugin_logf */
805 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */