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 { "no name set", "no description set", \
64 /* copyright */ "", /* license */ "", \
65 /* version */ -1, /* plugin_version */ -1 }
67 typedef struct {
68 sdb_object_t super;
69 void *cb_callback;
70 sdb_object_t *cb_user_data;
71 sdb_plugin_ctx_t cb_ctx;
72 } sdb_plugin_cb_t;
73 #define SDB_PLUGIN_CB_INIT { SDB_OBJECT_INIT, \
74 /* callback = */ NULL, /* user_data = */ NULL, \
75 SDB_PLUGIN_CTX_INIT }
77 typedef struct {
78 sdb_plugin_cb_t super;
79 #define ccb_callback super.cb_callback
80 #define ccb_user_data super.cb_user_data
81 #define ccb_ctx super.cb_ctx
82 sdb_time_t ccb_interval;
83 sdb_time_t ccb_next_update;
84 } sdb_plugin_collector_cb_t;
86 #define SDB_PLUGIN_CB(obj) ((sdb_plugin_cb_t *)(obj))
87 #define SDB_PLUGIN_CCB(obj) ((sdb_plugin_collector_cb_t *)(obj))
89 /*
90 * private variables
91 */
93 static sdb_plugin_ctx_t plugin_default_ctx = SDB_PLUGIN_CTX_INIT;
95 static pthread_key_t plugin_ctx_key;
96 static _Bool plugin_ctx_key_initialized = 0;
98 static sdb_llist_t *config_list = NULL;
99 static sdb_llist_t *init_list = NULL;
100 static sdb_llist_t *collector_list = NULL;
101 static sdb_llist_t *cname_list = NULL;
102 static sdb_llist_t *shutdown_list = NULL;
103 static sdb_llist_t *log_list = NULL;
105 /*
106 * private helper functions
107 */
109 static void
110 sdb_plugin_ctx_destructor(void *ctx)
111 {
112 if (! ctx)
113 return;
114 free(ctx);
115 } /* sdb_plugin_ctx_destructor */
117 static void
118 sdb_plugin_ctx_init(void)
119 {
120 if (plugin_ctx_key_initialized)
121 return;
123 pthread_key_create(&plugin_ctx_key, sdb_plugin_ctx_destructor);
124 plugin_ctx_key_initialized = 1;
125 } /* sdb_plugin_ctx_init */
127 static sdb_plugin_ctx_t *
128 sdb_plugin_ctx_create(void)
129 {
130 sdb_plugin_ctx_t *ctx;
132 ctx = malloc(sizeof(*ctx));
133 if (! ctx)
134 return NULL;
136 *ctx = plugin_default_ctx;
138 if (! plugin_ctx_key_initialized)
139 sdb_plugin_ctx_init();
140 pthread_setspecific(plugin_ctx_key, ctx);
141 return ctx;
142 } /* sdb_plugin_ctx_create */
144 static int
145 sdb_plugin_cmp_next_update(const sdb_object_t *a, const sdb_object_t *b)
146 {
147 const sdb_plugin_collector_cb_t *ccb1
148 = (const sdb_plugin_collector_cb_t *)a;
149 const sdb_plugin_collector_cb_t *ccb2
150 = (const sdb_plugin_collector_cb_t *)b;
152 assert(ccb1 && ccb2);
154 return (ccb1->ccb_next_update > ccb2->ccb_next_update)
155 ? 1 : (ccb1->ccb_next_update < ccb2->ccb_next_update)
156 ? -1 : 0;
157 } /* sdb_plugin_cmp_next_update */
159 /*
160 * private types
161 */
163 static int
164 sdb_plugin_cb_init(sdb_object_t *obj, va_list ap)
165 {
166 sdb_llist_t **list = va_arg(ap, sdb_llist_t **);
167 const char *type = va_arg(ap, const char *);
168 void *callback = va_arg(ap, void *);
169 sdb_object_t *ud = va_arg(ap, sdb_object_t *);
171 assert(list);
172 assert(type);
173 assert(obj);
175 if (sdb_llist_search_by_name(*list, obj->name)) {
176 sdb_log(SDB_LOG_WARNING, "plugin: %s callback '%s' "
177 "has already been registered. Ignoring newly "
178 "registered version.", type, obj->name);
179 return -1;
180 }
182 SDB_PLUGIN_CB(obj)->cb_callback = callback;
183 SDB_PLUGIN_CB(obj)->cb_ctx = sdb_plugin_get_ctx();
185 sdb_object_ref(ud);
186 SDB_PLUGIN_CB(obj)->cb_user_data = ud;
187 return 0;
188 } /* sdb_plugin_cb_init */
190 static void
191 sdb_plugin_cb_destroy(sdb_object_t *obj)
192 {
193 assert(obj);
194 sdb_object_deref(SDB_PLUGIN_CB(obj)->cb_user_data);
195 } /* sdb_plugin_cb_destroy */
197 static sdb_type_t sdb_plugin_cb_type = {
198 sizeof(sdb_plugin_cb_t),
200 sdb_plugin_cb_init,
201 sdb_plugin_cb_destroy
202 };
204 static sdb_type_t sdb_plugin_collector_cb_type = {
205 sizeof(sdb_plugin_collector_cb_t),
207 sdb_plugin_cb_init,
208 sdb_plugin_cb_destroy
209 };
211 static int
212 sdb_plugin_add_callback(sdb_llist_t **list, const char *type,
213 const char *name, void *callback, sdb_object_t *user_data)
214 {
215 sdb_object_t *obj;
217 if ((! name) || (! callback))
218 return -1;
220 assert(list);
222 if (! *list)
223 *list = sdb_llist_create();
224 if (! *list)
225 return -1;
227 obj = sdb_object_create(name, sdb_plugin_cb_type,
228 list, type, callback, user_data);
229 if (! obj)
230 return -1;
232 if (sdb_llist_append(*list, obj)) {
233 sdb_object_deref(obj);
234 return -1;
235 }
237 /* pass control to the list */
238 sdb_object_deref(obj);
240 sdb_log(SDB_LOG_INFO, "plugin: Registered %s callback '%s'.",
241 type, name);
242 return 0;
243 } /* sdb_plugin_add_callback */
245 /*
246 * public API
247 */
249 int
250 sdb_plugin_load(const char *name)
251 {
252 char real_name[strlen(name) > 0 ? strlen(name) : 1];
253 const char *name_ptr;
254 char *tmp;
256 char filename[1024];
258 lt_dlhandle lh;
260 int (*mod_init)(sdb_plugin_info_t *);
261 sdb_plugin_info_t plugin_info = SDB_PLUGIN_INFO_INIT;
263 int status;
265 if ((! name) || (! *name))
266 return -1;
268 real_name[0] = '\0';
269 name_ptr = name;
271 while ((tmp = strstr(name_ptr, "::"))) {
272 strncat(real_name, name_ptr, (size_t)(tmp - name_ptr));
273 strcat(real_name, "/");
274 name_ptr = tmp + strlen("::");
275 }
276 strcat(real_name, name_ptr);
278 snprintf(filename, sizeof(filename), "%s/%s.so",
279 PKGLIBDIR, real_name);
280 filename[sizeof(filename) - 1] = '\0';
282 if (access(filename, R_OK)) {
283 char errbuf[1024];
284 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s' (%s): %s",
285 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
286 return -1;
287 }
289 lt_dlinit();
290 lt_dlerror();
292 lh = lt_dlopen(filename);
293 if (! lh) {
294 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s': %s"
295 "The most common cause for this problem are missing "
296 "dependencies.\n", name, lt_dlerror());
297 return -1;
298 }
300 mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
301 if (! mod_init) {
302 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s': "
303 "could not find symbol 'sdb_module_init'", name);
304 return -1;
305 }
307 status = mod_init(&plugin_info);
308 if (status) {
309 sdb_log(SDB_LOG_ERR, "plugin: Failed to initialize "
310 "plugin '%s'", name);
311 return -1;
312 }
314 /* compare minor version */
315 if ((plugin_info.version < 0)
316 || ((int)(plugin_info.version / 100) != (int)(SDB_VERSION / 100)))
317 sdb_log(SDB_LOG_WARNING, "plugin: WARNING: version of "
318 "plugin '%s' (%i.%i.%i) does not match our version "
319 "(%i.%i.%i); this might cause problems",
320 name, SDB_VERSION_DECODE(plugin_info.version),
321 SDB_VERSION_DECODE(SDB_VERSION));
323 sdb_log(SDB_LOG_INFO, "plugin: Successfully loaded "
324 "plugin '%s' v%i (%s)\n\t%s",
325 plugin_info.name, plugin_info.plugin_version,
326 plugin_info.description, plugin_info.copyright);
327 return 0;
328 } /* sdb_plugin_load */
330 int
331 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
332 {
333 va_list ap;
335 if (! info)
336 return -1;
338 va_start(ap, type);
340 switch (type) {
341 case SDB_PLUGIN_INFO_NAME:
342 {
343 char *name = va_arg(ap, char *);
344 info->name = name;
345 }
346 break;
347 case SDB_PLUGIN_INFO_DESC:
348 {
349 char *desc = va_arg(ap, char *);
350 info->description = desc;
351 }
352 break;
353 case SDB_PLUGIN_INFO_COPYRIGHT:
354 {
355 char *copyright = va_arg(ap, char *);
356 info->copyright = copyright;
357 }
358 break;
359 case SDB_PLUGIN_INFO_LICENSE:
360 {
361 char *license = va_arg(ap, char *);
362 info->license = license;
363 }
364 break;
365 case SDB_PLUGIN_INFO_VERSION:
366 {
367 int version = va_arg(ap, int);
368 info->version = version;
369 }
370 break;
371 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
372 {
373 int version = va_arg(ap, int);
374 info->plugin_version = version;
375 }
376 break;
377 default:
378 va_end(ap);
379 return -1;
380 }
382 va_end(ap);
383 return 0;
384 } /* sdb_plugin_set_info */
386 int
387 sdb_plugin_register_config(const char *name, sdb_plugin_config_cb callback)
388 {
389 return sdb_plugin_add_callback(&config_list, "init", name,
390 callback, NULL);
391 } /* sdb_plugin_register_config */
393 int
394 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
395 sdb_object_t *user_data)
396 {
397 return sdb_plugin_add_callback(&init_list, "init", name,
398 callback, user_data);
399 } /* sdb_plugin_register_init */
401 int
402 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
403 sdb_object_t *user_data)
404 {
405 return sdb_plugin_add_callback(&shutdown_list, "shutdown", name,
406 callback, user_data);
407 } /* sdb_plugin_register_shutdown */
409 int
410 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
411 sdb_object_t *user_data)
412 {
413 return sdb_plugin_add_callback(&log_list, "log", name, callback,
414 user_data);
415 } /* sdb_plugin_register_log */
417 int
418 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
419 sdb_object_t *user_data)
420 {
421 return sdb_plugin_add_callback(&cname_list, "cname", name, callback,
422 user_data);
423 } /* sdb_plugin_register_cname */
425 int
426 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
427 const sdb_time_t *interval, sdb_object_t *user_data)
428 {
429 sdb_object_t *obj;
431 if ((! name) || (! callback))
432 return -1;
434 if (! collector_list)
435 collector_list = sdb_llist_create();
436 if (! collector_list)
437 return -1;
439 obj = sdb_object_create(name, sdb_plugin_collector_cb_type,
440 &collector_list, "collector", callback, user_data);
441 if (! obj)
442 return -1;
444 if (interval)
445 SDB_PLUGIN_CCB(obj)->ccb_interval = *interval;
446 else {
447 sdb_time_t tmp = sdb_plugin_get_ctx().interval;
449 if (tmp > 0)
450 SDB_PLUGIN_CCB(obj)->ccb_interval = tmp;
451 else
452 SDB_PLUGIN_CCB(obj)->ccb_interval = 0;
453 }
455 if (! (SDB_PLUGIN_CCB(obj)->ccb_next_update = sdb_gettime())) {
456 char errbuf[1024];
457 sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
458 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
459 sdb_object_deref(obj);
460 return -1;
461 }
463 if (sdb_llist_insert_sorted(collector_list, obj,
464 sdb_plugin_cmp_next_update)) {
465 sdb_object_deref(obj);
466 return -1;
467 }
469 /* pass control to the list */
470 sdb_object_deref(obj);
472 sdb_log(SDB_LOG_INFO, "plugin: Registered collector callback '%s' "
473 "(interval = %.3fs).", name,
474 SDB_TIME_TO_DOUBLE(SDB_PLUGIN_CCB(obj)->ccb_interval));
475 return 0;
476 } /* sdb_plugin_register_collector */
478 sdb_plugin_ctx_t
479 sdb_plugin_get_ctx(void)
480 {
481 sdb_plugin_ctx_t *ctx;
483 if (! plugin_ctx_key_initialized)
484 sdb_plugin_ctx_init();
485 ctx = pthread_getspecific(plugin_ctx_key);
487 if (! ctx)
488 ctx = sdb_plugin_ctx_create();
489 if (! ctx)
490 return plugin_default_ctx;
491 return *ctx;
492 } /* sdb_plugin_get_ctx */
494 sdb_plugin_ctx_t
495 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx)
496 {
497 sdb_plugin_ctx_t *tmp;
498 sdb_plugin_ctx_t old;
500 if (! plugin_ctx_key_initialized)
501 sdb_plugin_ctx_init();
502 tmp = pthread_getspecific(plugin_ctx_key);
504 if (! tmp)
505 tmp = sdb_plugin_ctx_create();
506 if (! tmp)
507 return plugin_default_ctx;
509 old = *tmp;
510 *tmp = ctx;
511 return old;
512 } /* sdb_plugin_set_ctx */
514 int
515 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
516 {
517 sdb_plugin_cb_t *plugin;
518 sdb_plugin_config_cb callback;
520 sdb_plugin_ctx_t old_ctx;
522 int status;
524 if ((! name) || (! ci))
525 return -1;
527 plugin = SDB_PLUGIN_CB(sdb_llist_search_by_name(config_list, name));
528 if (! plugin) {
529 /* XXX: check if any such plugin has been loaded */
530 sdb_log(SDB_LOG_ERR, "plugin: Plugin '%s' did not register "
531 "a config callback.", name);
532 errno = ENOENT;
533 return -1;
534 }
536 old_ctx = sdb_plugin_set_ctx(plugin->cb_ctx);
537 callback = plugin->cb_callback;
538 status = callback(ci);
539 sdb_plugin_set_ctx(old_ctx);
540 return status;
541 } /* sdb_plugin_configure */
543 int
544 sdb_plugin_init_all(void)
545 {
546 sdb_llist_iter_t *iter;
548 iter = sdb_llist_get_iter(init_list);
549 while (sdb_llist_iter_has_next(iter)) {
550 sdb_plugin_init_cb callback;
551 sdb_plugin_ctx_t old_ctx;
553 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
554 assert(obj);
556 callback = SDB_PLUGIN_CB(obj)->cb_callback;
558 old_ctx = sdb_plugin_set_ctx(SDB_PLUGIN_CB(obj)->cb_ctx);
559 if (callback(SDB_PLUGIN_CB(obj)->cb_user_data)) {
560 /* XXX: unload plugin */
561 }
562 sdb_plugin_set_ctx(old_ctx);
563 }
564 sdb_llist_iter_destroy(iter);
565 return 0;
566 } /* sdb_plugin_init_all */
568 int
569 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
570 {
571 if (! collector_list) {
572 sdb_log(SDB_LOG_WARNING, "plugin: No collectors registered. "
573 "Quiting main loop.");
574 return -1;
575 }
577 if (! loop)
578 return -1;
580 while (loop->do_loop) {
581 sdb_plugin_collector_cb callback;
582 sdb_plugin_ctx_t old_ctx;
584 sdb_time_t interval, now;
586 sdb_object_t *obj = sdb_llist_shift(collector_list);
587 if (! obj)
588 return -1;
590 callback = SDB_PLUGIN_CCB(obj)->ccb_callback;
592 if (! (now = sdb_gettime())) {
593 char errbuf[1024];
594 sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
595 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
596 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
597 }
599 if (now < SDB_PLUGIN_CCB(obj)->ccb_next_update) {
600 interval = SDB_PLUGIN_CCB(obj)->ccb_next_update - now;
602 errno = 0;
603 while (loop->do_loop && sdb_sleep(interval, &interval)) {
604 if (errno != EINTR) {
605 char errbuf[1024];
606 sdb_log(SDB_LOG_ERR, "plugin: Failed to sleep: %s",
607 sdb_strerror(errno, errbuf, sizeof(errbuf)));
608 return -1;
609 }
610 errno = 0;
611 }
613 if (! loop->do_loop)
614 return 0;
615 }
617 old_ctx = sdb_plugin_set_ctx(SDB_PLUGIN_CCB(obj)->ccb_ctx);
618 if (callback(SDB_PLUGIN_CCB(obj)->ccb_user_data)) {
619 /* XXX */
620 }
621 sdb_plugin_set_ctx(old_ctx);
623 interval = SDB_PLUGIN_CCB(obj)->ccb_interval;
624 if (! interval)
625 interval = loop->default_interval;
626 if (! interval) {
627 sdb_log(SDB_LOG_WARNING, "plugin: No interval configured "
628 "for plugin '%s'; skipping any further "
629 "iterations.", obj->name);
630 sdb_object_deref(obj);
631 continue;
632 }
634 SDB_PLUGIN_CCB(obj)->ccb_next_update += interval;
636 if (! (now = sdb_gettime())) {
637 char errbuf[1024];
638 sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
639 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
640 now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
641 }
643 if (now > SDB_PLUGIN_CCB(obj)->ccb_next_update) {
644 sdb_log(SDB_LOG_WARNING, "plugin: Plugin '%s' took too "
645 "long; skipping iterations to keep up.",
646 obj->name);
647 SDB_PLUGIN_CCB(obj)->ccb_next_update = now;
648 }
650 if (sdb_llist_insert_sorted(collector_list, obj,
651 sdb_plugin_cmp_next_update)) {
652 sdb_log(SDB_LOG_ERR, "plugin: Failed to re-insert "
653 "plugin '%s' into collector list. Unable to further "
654 "use the plugin.",
655 obj->name);
656 sdb_object_deref(obj);
657 return -1;
658 }
660 /* pass control back to the list */
661 sdb_object_deref(obj);
662 }
663 return 0;
664 } /* sdb_plugin_read_loop */
666 char *
667 sdb_plugin_cname(char *hostname)
668 {
669 sdb_llist_iter_t *iter;
671 if (! hostname)
672 return NULL;
674 if (! cname_list)
675 return hostname;
677 iter = sdb_llist_get_iter(cname_list);
678 while (sdb_llist_iter_has_next(iter)) {
679 sdb_plugin_cname_cb callback;
680 char *cname;
682 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
683 assert(obj);
685 callback = SDB_PLUGIN_CB(obj)->cb_callback;
686 cname = callback(hostname, SDB_PLUGIN_CB(obj)->cb_user_data);
687 if (cname) {
688 free(hostname);
689 hostname = cname;
690 }
691 /* else: don't change hostname */
692 }
693 sdb_llist_iter_destroy(iter);
694 return hostname;
695 } /* sdb_plugin_cname */
697 int
698 sdb_plugin_log(int prio, const char *msg)
699 {
700 sdb_llist_iter_t *iter;
701 int ret = -1;
703 if (! msg)
704 return 0;
706 if (! log_list)
707 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
709 iter = sdb_llist_get_iter(log_list);
710 while (sdb_llist_iter_has_next(iter)) {
711 sdb_plugin_log_cb callback;
712 int tmp;
714 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
715 assert(obj);
717 callback = SDB_PLUGIN_CB(obj)->cb_callback;
718 tmp = callback(prio, msg, SDB_PLUGIN_CB(obj)->cb_user_data);
719 if (tmp > ret)
720 ret = tmp;
721 }
722 sdb_llist_iter_destroy(iter);
723 return ret;
724 } /* sdb_plugin_log */
726 int
727 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
728 {
729 sdb_strbuf_t *buf;
730 int ret;
732 if (! fmt)
733 return 0;
735 buf = sdb_strbuf_create(64);
736 if (! buf) {
737 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
738 ret += vfprintf(stderr, fmt, ap);
739 return ret;
740 }
742 if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
743 sdb_strbuf_destroy(buf);
744 return -1;
745 }
747 ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
748 sdb_strbuf_destroy(buf);
749 return ret;
750 } /* sdb_plugin_vlogf */
752 int
753 sdb_plugin_logf(int prio, const char *fmt, ...)
754 {
755 va_list ap;
756 int ret;
758 if (! fmt)
759 return 0;
761 va_start(ap, fmt);
762 ret = sdb_plugin_vlogf(prio, fmt, ap);
763 va_end(ap);
764 return ret;
765 } /* sdb_plugin_logf */
767 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */