Code

plugin/store: Added support for "cname" plugins.
[sysdb.git] / src / core / plugin.c
1 /*
2  * SysDB - src/core/plugin.c
3  * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #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)
112         if (! ctx)
113                 return;
114         free(ctx);
115 } /* sdb_plugin_ctx_destructor */
117 static void
118 sdb_plugin_ctx_init(void)
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)
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)
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)
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)
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         /* clone = */ NULL
203 };
205 static sdb_type_t sdb_plugin_collector_cb_type = {
206         sizeof(sdb_plugin_collector_cb_t),
208         sdb_plugin_cb_init,
209         sdb_plugin_cb_destroy,
210         /* clone = */ NULL
211 };
213 static int
214 sdb_plugin_add_callback(sdb_llist_t **list, const char *type,
215                 const char *name, void *callback, sdb_object_t *user_data)
217         sdb_object_t *obj;
219         if ((! name) || (! callback))
220                 return -1;
222         assert(list);
224         if (! *list)
225                 *list = sdb_llist_create();
226         if (! *list)
227                 return -1;
229         obj = sdb_object_create(name, sdb_plugin_cb_type,
230                         list, type, callback, user_data);
231         if (! obj)
232                 return -1;
234         if (sdb_llist_append(*list, obj)) {
235                 sdb_object_deref(obj);
236                 return -1;
237         }
239         /* pass control to the list */
240         sdb_object_deref(obj);
242         sdb_log(SDB_LOG_INFO, "plugin: Registered %s callback '%s'.",
243                         type, name);
244         return 0;
245 } /* sdb_plugin_add_callback */
247 /*
248  * public API
249  */
251 int
252 sdb_plugin_load(const char *name)
254         char  real_name[strlen(name) > 0 ? strlen(name) : 1];
255         const char *name_ptr;
256         char *tmp;
258         char filename[1024];
260         lt_dlhandle lh;
262         int (*mod_init)(sdb_plugin_info_t *);
263         sdb_plugin_info_t plugin_info = SDB_PLUGIN_INFO_INIT;
265         int status;
267         if ((! name) || (! *name))
268                 return -1;
270         real_name[0] = '\0';
271         name_ptr = name;
273         while ((tmp = strstr(name_ptr, "::"))) {
274                 strncat(real_name, name_ptr, (size_t)(tmp - name_ptr));
275                 strcat(real_name, "/");
276                 name_ptr = tmp + strlen("::");
277         }
278         strcat(real_name, name_ptr);
280         snprintf(filename, sizeof(filename), "%s/%s.so",
281                         PKGLIBDIR, real_name);
282         filename[sizeof(filename) - 1] = '\0';
284         if (access(filename, R_OK)) {
285                 char errbuf[1024];
286                 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s' (%s): %s",
287                                 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
288                 return -1;
289         }
291         lt_dlinit();
292         lt_dlerror();
294         lh = lt_dlopen(filename);
295         if (! lh) {
296                 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s': %s"
297                                 "The most common cause for this problem are missing "
298                                 "dependencies.\n", name, lt_dlerror());
299                 return -1;
300         }
302         mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
303         if (! mod_init) {
304                 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s': "
305                                 "could not find symbol 'sdb_module_init'", name);
306                 return -1;
307         }
309         status = mod_init(&plugin_info);
310         if (status) {
311                 sdb_log(SDB_LOG_ERR, "plugin: Failed to initialize "
312                                 "plugin '%s'", name);
313                 return -1;
314         }
316         /* compare minor version */
317         if ((plugin_info.version < 0)
318                         || ((int)(plugin_info.version / 100) != (int)(SDB_VERSION / 100)))
319                 sdb_log(SDB_LOG_WARNING, "plugin: WARNING: version of "
320                                 "plugin '%s' (%i.%i.%i) does not match our version "
321                                 "(%i.%i.%i); this might cause problems",
322                                 name, SDB_VERSION_DECODE(plugin_info.version),
323                                 SDB_VERSION_DECODE(SDB_VERSION));
325         sdb_log(SDB_LOG_INFO, "plugin: Successfully loaded "
326                         "plugin '%s' v%i (%s)\n\t%s",
327                         plugin_info.name, plugin_info.plugin_version,
328                         plugin_info.description, plugin_info.copyright);
329         return 0;
330 } /* sdb_plugin_load */
332 int
333 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
335         va_list ap;
337         if (! info)
338                 return -1;
340         va_start(ap, type);
342         switch (type) {
343                 case SDB_PLUGIN_INFO_NAME:
344                         {
345                                 char *name = va_arg(ap, char *);
346                                 info->name = name;
347                         }
348                         break;
349                 case SDB_PLUGIN_INFO_DESC:
350                         {
351                                 char *desc = va_arg(ap, char *);
352                                 info->description = desc;
353                         }
354                         break;
355                 case SDB_PLUGIN_INFO_COPYRIGHT:
356                         {
357                                 char *copyright = va_arg(ap, char *);
358                                 info->copyright = copyright;
359                         }
360                         break;
361                 case SDB_PLUGIN_INFO_LICENSE:
362                         {
363                                 char *license = va_arg(ap, char *);
364                                 info->license = license;
365                         }
366                         break;
367                 case SDB_PLUGIN_INFO_VERSION:
368                         {
369                                 int version = va_arg(ap, int);
370                                 info->version = version;
371                         }
372                         break;
373                 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
374                         {
375                                 int version = va_arg(ap, int);
376                                 info->plugin_version = version;
377                         }
378                         break;
379                 default:
380                         va_end(ap);
381                         return -1;
382         }
384         va_end(ap);
385         return 0;
386 } /* sdb_plugin_set_info */
388 int
389 sdb_plugin_register_config(const char *name, sdb_plugin_config_cb callback)
391         return sdb_plugin_add_callback(&config_list, "init", name,
392                         callback, NULL);
393 } /* sdb_plugin_register_config */
395 int
396 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
397                 sdb_object_t *user_data)
399         return sdb_plugin_add_callback(&init_list, "init", name,
400                         callback, user_data);
401 } /* sdb_plugin_register_init */
403 int
404 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
405                 sdb_object_t *user_data)
407         return sdb_plugin_add_callback(&shutdown_list, "shutdown", name,
408                         callback, user_data);
409 } /* sdb_plugin_register_shutdown */
411 int
412 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
413                 sdb_object_t *user_data)
415         return sdb_plugin_add_callback(&log_list, "log", name, callback,
416                         user_data);
417 } /* sdb_plugin_register_log */
419 int
420 sdb_plugin_register_cname(const char *name, sdb_plugin_cname_cb callback,
421                 sdb_object_t *user_data)
423         return sdb_plugin_add_callback(&cname_list, "cname", name, callback,
424                         user_data);
425 } /* sdb_plugin_register_cname */
427 int
428 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
429                 const sdb_time_t *interval, sdb_object_t *user_data)
431         sdb_object_t *obj;
433         if ((! name) || (! callback))
434                 return -1;
436         if (! collector_list)
437                 collector_list = sdb_llist_create();
438         if (! collector_list)
439                 return -1;
441         obj = sdb_object_create(name, sdb_plugin_collector_cb_type,
442                         &collector_list, "collector", callback, user_data);
443         if (! obj)
444                 return -1;
446         if (interval)
447                 SDB_PLUGIN_CCB(obj)->ccb_interval = *interval;
448         else {
449                 sdb_time_t tmp = sdb_plugin_get_ctx().interval;
451                 if (tmp > 0)
452                         SDB_PLUGIN_CCB(obj)->ccb_interval = tmp;
453                 else
454                         SDB_PLUGIN_CCB(obj)->ccb_interval = 0;
455         }
457         if (! (SDB_PLUGIN_CCB(obj)->ccb_next_update = sdb_gettime())) {
458                 char errbuf[1024];
459                 sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
460                                 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
461                 sdb_object_deref(obj);
462                 return -1;
463         }
465         if (sdb_llist_insert_sorted(collector_list, obj,
466                                 sdb_plugin_cmp_next_update)) {
467                 sdb_object_deref(obj);
468                 return -1;
469         }
471         /* pass control to the list */
472         sdb_object_deref(obj);
474         sdb_log(SDB_LOG_INFO, "plugin: Registered collector callback '%s' "
475                         "(interval = %.3fs).", name,
476                         SDB_TIME_TO_DOUBLE(SDB_PLUGIN_CCB(obj)->ccb_interval));
477         return 0;
478 } /* sdb_plugin_register_collector */
480 sdb_plugin_ctx_t
481 sdb_plugin_get_ctx(void)
483         sdb_plugin_ctx_t *ctx;
485         if (! plugin_ctx_key_initialized)
486                 sdb_plugin_ctx_init();
487         ctx = pthread_getspecific(plugin_ctx_key);
489         if (! ctx)
490                 ctx = sdb_plugin_ctx_create();
491         if (! ctx)
492                 return plugin_default_ctx;
493         return *ctx;
494 } /* sdb_plugin_get_ctx */
496 sdb_plugin_ctx_t
497 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx)
499         sdb_plugin_ctx_t *tmp;
500         sdb_plugin_ctx_t old;
502         if (! plugin_ctx_key_initialized)
503                 sdb_plugin_ctx_init();
504         tmp = pthread_getspecific(plugin_ctx_key);
506         if (! tmp)
507                 tmp = sdb_plugin_ctx_create();
508         if (! tmp)
509                 return plugin_default_ctx;
511         old = *tmp;
512         *tmp = ctx;
513         return old;
514 } /* sdb_plugin_set_ctx */
516 int
517 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
519         sdb_plugin_cb_t *plugin;
520         sdb_plugin_config_cb callback;
522         sdb_plugin_ctx_t old_ctx;
524         int status;
526         if ((! name) || (! ci))
527                 return -1;
529         plugin = SDB_PLUGIN_CB(sdb_llist_search_by_name(config_list, name));
530         if (! plugin) {
531                 /* XXX: check if any such plugin has been loaded */
532                 sdb_log(SDB_LOG_ERR, "plugin: Plugin '%s' did not register "
533                                 "a config callback.", name);
534                 errno = ENOENT;
535                 return -1;
536         }
538         old_ctx = sdb_plugin_set_ctx(plugin->cb_ctx);
539         callback = plugin->cb_callback;
540         status = callback(ci);
541         sdb_plugin_set_ctx(old_ctx);
542         return status;
543 } /* sdb_plugin_configure */
545 int
546 sdb_plugin_init_all(void)
548         sdb_llist_iter_t *iter;
550         iter = sdb_llist_get_iter(init_list);
551         while (sdb_llist_iter_has_next(iter)) {
552                 sdb_plugin_init_cb callback;
553                 sdb_plugin_ctx_t old_ctx;
555                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
556                 assert(obj);
558                 callback = SDB_PLUGIN_CB(obj)->cb_callback;
560                 old_ctx = sdb_plugin_set_ctx(SDB_PLUGIN_CB(obj)->cb_ctx);
561                 if (callback(SDB_PLUGIN_CB(obj)->cb_user_data)) {
562                         /* XXX: unload plugin */
563                 }
564                 sdb_plugin_set_ctx(old_ctx);
565         }
566         sdb_llist_iter_destroy(iter);
567         return 0;
568 } /* sdb_plugin_init_all */
570 int
571 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
573         if (! collector_list) {
574                 sdb_log(SDB_LOG_WARNING, "plugin: No collectors registered. "
575                                 "Quiting main loop.");
576                 return -1;
577         }
579         if (! loop)
580                 return -1;
582         while (loop->do_loop) {
583                 sdb_plugin_collector_cb callback;
584                 sdb_plugin_ctx_t old_ctx;
586                 sdb_time_t interval, now;
588                 sdb_object_t *obj = sdb_llist_shift(collector_list);
589                 if (! obj)
590                         return -1;
592                 callback = SDB_PLUGIN_CCB(obj)->ccb_callback;
594                 if (! (now = sdb_gettime())) {
595                         char errbuf[1024];
596                         sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
597                                         "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
598                         now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
599                 }
601                 if (now < SDB_PLUGIN_CCB(obj)->ccb_next_update) {
602                         interval = SDB_PLUGIN_CCB(obj)->ccb_next_update - now;
604                         errno = 0;
605                         while (loop->do_loop && sdb_sleep(interval, &interval)) {
606                                 if (errno != EINTR) {
607                                         char errbuf[1024];
608                                         sdb_log(SDB_LOG_ERR, "plugin: Failed to sleep: %s",
609                                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
610                                         return -1;
611                                 }
612                                 errno = 0;
613                         }
615                         if (! loop->do_loop)
616                                 return 0;
617                 }
619                 old_ctx = sdb_plugin_set_ctx(SDB_PLUGIN_CCB(obj)->ccb_ctx);
620                 if (callback(SDB_PLUGIN_CCB(obj)->ccb_user_data)) {
621                         /* XXX */
622                 }
623                 sdb_plugin_set_ctx(old_ctx);
625                 interval = SDB_PLUGIN_CCB(obj)->ccb_interval;
626                 if (! interval)
627                         interval = loop->default_interval;
628                 if (! interval) {
629                         sdb_log(SDB_LOG_WARNING, "plugin: No interval configured "
630                                         "for plugin '%s'; skipping any further "
631                                         "iterations.", obj->name);
632                         sdb_object_deref(obj);
633                         continue;
634                 }
636                 SDB_PLUGIN_CCB(obj)->ccb_next_update += interval;
638                 if (! (now = sdb_gettime())) {
639                         char errbuf[1024];
640                         sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
641                                         "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
642                         now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
643                 }
645                 if (now > SDB_PLUGIN_CCB(obj)->ccb_next_update) {
646                         sdb_log(SDB_LOG_WARNING, "plugin: Plugin '%s' took too "
647                                         "long; skipping iterations to keep up.",
648                                         obj->name);
649                         SDB_PLUGIN_CCB(obj)->ccb_next_update = now;
650                 }
652                 if (sdb_llist_insert_sorted(collector_list, obj,
653                                         sdb_plugin_cmp_next_update)) {
654                         sdb_log(SDB_LOG_ERR, "plugin: Failed to re-insert "
655                                         "plugin '%s' into collector list. Unable to further "
656                                         "use the plugin.",
657                                         obj->name);
658                         sdb_object_deref(obj);
659                         return -1;
660                 }
662                 /* pass control back to the list */
663                 sdb_object_deref(obj);
664         }
665         return 0;
666 } /* sdb_plugin_read_loop */
668 char *
669 sdb_plugin_cname(char *hostname)
671         sdb_llist_iter_t *iter;
673         if (! hostname)
674                 return NULL;
676         if (! cname_list)
677                 return hostname;
679         iter = sdb_llist_get_iter(cname_list);
680         while (sdb_llist_iter_has_next(iter)) {
681                 sdb_plugin_cname_cb callback;
682                 char *cname;
684                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
685                 assert(obj);
687                 callback = SDB_PLUGIN_CB(obj)->cb_callback;
688                 cname = callback(hostname, SDB_PLUGIN_CB(obj)->cb_user_data);
689                 if (cname) {
690                         free(hostname);
691                         hostname = cname;
692                 }
693                 /* else: don't change hostname */
694         }
695         sdb_llist_iter_destroy(iter);
696         return hostname;
697 } /* sdb_plugin_cname */
699 int
700 sdb_plugin_log(int prio, const char *msg)
702         sdb_llist_iter_t *iter;
703         int ret = -1;
705         if (! msg)
706                 return 0;
708         if (! log_list)
709                 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
711         iter = sdb_llist_get_iter(log_list);
712         while (sdb_llist_iter_has_next(iter)) {
713                 sdb_plugin_log_cb callback;
714                 int tmp;
716                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
717                 assert(obj);
719                 callback = SDB_PLUGIN_CB(obj)->cb_callback;
720                 tmp = callback(prio, msg, SDB_PLUGIN_CB(obj)->cb_user_data);
721                 if (tmp > ret)
722                         ret = tmp;
723         }
724         sdb_llist_iter_destroy(iter);
725         return ret;
726 } /* sdb_plugin_log */
728 int
729 sdb_plugin_vlogf(int prio, const char *fmt, va_list ap)
731         sdb_strbuf_t *buf;
732         int ret;
734         if (! fmt)
735                 return 0;
737         buf = sdb_strbuf_create(64);
738         if (! buf) {
739                 ret = fprintf(stderr, "[%s] ", SDB_LOG_PRIO_TO_STRING(prio));
740                 ret += vfprintf(stderr, fmt, ap);
741                 return ret;
742         }
744         if (sdb_strbuf_vsprintf(buf, fmt, ap) < 0) {
745                 sdb_strbuf_destroy(buf);
746                 return -1;
747         }
749         ret = sdb_plugin_log(prio, sdb_strbuf_string(buf));
750         sdb_strbuf_destroy(buf);
751         return ret;
752 } /* sdb_plugin_vlogf */
754 int
755 sdb_plugin_logf(int prio, const char *fmt, ...)
757         va_list ap;
758         int ret;
760         if (! fmt)
761                 return 0;
763         va_start(ap, fmt);
764         ret = sdb_plugin_vlogf(prio, fmt, ap);
765         va_end(ap);
766         return ret;
767 } /* sdb_plugin_logf */
769 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */