Code

plugin: Added support for log callbacks.
[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"
34 #include <assert.h>
36 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <strings.h>
42 #include <unistd.h>
44 #include <ltdl.h>
46 #include <pthread.h>
48 /*
49  * private data types
50  */
52 struct sdb_plugin_info {
53         char *name;
55         char *description;
56         char *copyright;
57         char *license;
59         int   version;
60         int   plugin_version;
61 };
62 #define SDB_PLUGIN_INFO_INIT { "no name set", "no description set", \
63         /* copyright */ "", /* license */ "", \
64         /* version */ -1, /* plugin_version */ -1 }
66 typedef struct {
67         sdb_object_t super;
68         char cb_name[64];
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, /* name = */ "", \
74         /* callback = */ NULL, /* user_data = */ NULL, \
75         SDB_PLUGIN_CTX_INIT }
77 typedef struct {
78         sdb_plugin_cb_t super;
79 #define ccb_name super.cb_name
80 #define ccb_callback super.cb_callback
81 #define ccb_user_data super.cb_user_data
82 #define ccb_ctx super.cb_ctx
83         sdb_time_t ccb_interval;
84         sdb_time_t ccb_next_update;
85 } sdb_plugin_collector_cb_t;
87 #define SDB_PLUGIN_CB(obj) ((sdb_plugin_cb_t *)(obj))
88 #define SDB_PLUGIN_CCB(obj) ((sdb_plugin_collector_cb_t *)(obj))
90 /*
91  * private variables
92  */
94 static sdb_plugin_ctx_t  plugin_default_ctx = SDB_PLUGIN_CTX_INIT;
96 static pthread_key_t    plugin_ctx_key;
97 static _Bool            plugin_ctx_key_initialized = 0;
99 static sdb_llist_t      *config_list = NULL;
100 static sdb_llist_t      *init_list = NULL;
101 static sdb_llist_t      *collector_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_name(const sdb_object_t *a, const sdb_object_t *b)
147         const sdb_plugin_cb_t *cb1 = (const sdb_plugin_cb_t *)a;
148         const sdb_plugin_cb_t *cb2 = (const sdb_plugin_cb_t *)b;
150         assert(cb1 && cb2);
151         return strcasecmp(cb1->cb_name, cb2->cb_name);
152 } /* sdb_plugin_cmp_name */
154 static int
155 sdb_plugin_cmp_next_update(const sdb_object_t *a, const sdb_object_t *b)
157         const sdb_plugin_collector_cb_t *ccb1
158                 = (const sdb_plugin_collector_cb_t *)a;
159         const sdb_plugin_collector_cb_t *ccb2
160                 = (const sdb_plugin_collector_cb_t *)b;
162         assert(ccb1 && ccb2);
164         return (ccb1->ccb_next_update > ccb2->ccb_next_update)
165                 ? 1 : (ccb1->ccb_next_update < ccb2->ccb_next_update)
166                 ? -1 : 0;
167 } /* sdb_plugin_cmp_next_update */
169 static sdb_plugin_cb_t *
170 sdb_plugin_find_by_name(sdb_llist_t *list, const char *name)
172         sdb_plugin_cb_t tmp = SDB_PLUGIN_CB_INIT;
174         sdb_object_t *obj;
175         assert(name);
177         if (! list)
178                 return NULL;
180         snprintf(tmp.cb_name, sizeof(tmp.cb_name), "%s", name);
181         tmp.cb_name[sizeof(tmp.cb_name) - 1] = '\0';
182         obj = sdb_llist_search(list, SDB_OBJ(&tmp), sdb_plugin_cmp_name);
183         if (! obj)
184                 return NULL;
185         return SDB_PLUGIN_CB(obj);
186 } /* sdb_plugin_find_by_name */
188 /*
189  * private types
190  */
192 static int
193 sdb_plugin_cb_init(sdb_object_t *obj, va_list ap)
195         sdb_llist_t **list = va_arg(ap, sdb_llist_t **);
196         const char  *type = va_arg(ap, const char *);
197         const char  *name = va_arg(ap, const char *);
198         void    *callback = va_arg(ap, void *);
199         sdb_object_t   *ud = va_arg(ap, sdb_object_t *);
201         assert(list);
202         assert(type);
203         assert(obj);
205         if (sdb_plugin_find_by_name(*list, name)) {
206                 sdb_log(SDB_LOG_WARNING, "plugin: %s callback '%s' "
207                                 "has already been registered. Ignoring newly "
208                                 "registered version.", type, name);
209                 return -1;
210         }
212         snprintf(SDB_PLUGIN_CB(obj)->cb_name,
213                         sizeof(SDB_PLUGIN_CB(obj)->cb_name),
214                         "%s", name);
215         SDB_PLUGIN_CB(obj)->cb_name[sizeof(SDB_PLUGIN_CB(obj)->cb_name) - 1] = '\0';
216         SDB_PLUGIN_CB(obj)->cb_callback = callback;
217         SDB_PLUGIN_CB(obj)->cb_ctx      = sdb_plugin_get_ctx();
219         sdb_object_ref(ud);
220         SDB_PLUGIN_CB(obj)->cb_user_data = ud;
221         return 0;
222 } /* sdb_plugin_cb_init */
224 static void
225 sdb_plugin_cb_destroy(sdb_object_t *obj)
227         assert(obj);
228         sdb_object_deref(SDB_PLUGIN_CB(obj)->cb_user_data);
229 } /* sdb_plugin_cb_destroy */
231 static sdb_type_t sdb_plugin_cb_type = {
232         sizeof(sdb_plugin_cb_t),
234         sdb_plugin_cb_init,
235         sdb_plugin_cb_destroy,
236         /* clone = */ NULL
237 };
239 static sdb_type_t sdb_plugin_collector_cb_type = {
240         sizeof(sdb_plugin_collector_cb_t),
242         sdb_plugin_cb_init,
243         sdb_plugin_cb_destroy,
244         /* clone = */ NULL
245 };
247 static int
248 sdb_plugin_add_callback(sdb_llist_t **list, const char *type,
249                 const char *name, void *callback, sdb_object_t *user_data)
251         sdb_object_t *obj;
253         if ((! name) || (! callback))
254                 return -1;
256         assert(list);
258         if (! *list)
259                 *list = sdb_llist_create();
260         if (! *list)
261                 return -1;
263         obj = sdb_object_create(sdb_plugin_cb_type,
264                         list, type, name, callback, user_data);
265         if (! obj)
266                 return -1;
268         if (sdb_llist_append(*list, obj)) {
269                 sdb_object_deref(obj);
270                 return -1;
271         }
273         /* pass control to the list */
274         sdb_object_deref(obj);
276         sdb_log(SDB_LOG_INFO, "plugin: Registered %s callback '%s'.",
277                         type, name);
278         return 0;
279 } /* sdb_plugin_add_callback */
281 /*
282  * public API
283  */
285 int
286 sdb_plugin_load(const char *name)
288         char  real_name[strlen(name) > 0 ? strlen(name) : 1];
289         const char *name_ptr;
290         char *tmp;
292         char filename[1024];
294         lt_dlhandle lh;
296         int (*mod_init)(sdb_plugin_info_t *);
297         sdb_plugin_info_t plugin_info = SDB_PLUGIN_INFO_INIT;
299         int status;
301         if ((! name) || (! *name))
302                 return -1;
304         real_name[0] = '\0';
305         name_ptr = name;
307         while ((tmp = strstr(name_ptr, "::"))) {
308                 strncat(real_name, name_ptr, (size_t)(tmp - name_ptr));
309                 strcat(real_name, "/");
310                 name_ptr = tmp + strlen("::");
311         }
312         strcat(real_name, name_ptr);
314         snprintf(filename, sizeof(filename), "%s/%s.so",
315                         PKGLIBDIR, real_name);
316         filename[sizeof(filename) - 1] = '\0';
318         if (access(filename, R_OK)) {
319                 char errbuf[1024];
320                 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s' (%s): %s",
321                                 name, filename, sdb_strerror(errno, errbuf, sizeof(errbuf)));
322                 return -1;
323         }
325         lt_dlinit();
326         lt_dlerror();
328         lh = lt_dlopen(filename);
329         if (! lh) {
330                 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s': %s"
331                                 "The most common cause for this problem are missing "
332                                 "dependencies.\n", name, lt_dlerror());
333                 return -1;
334         }
336         mod_init = (int (*)(sdb_plugin_info_t *))lt_dlsym(lh, "sdb_module_init");
337         if (! mod_init) {
338                 sdb_log(SDB_LOG_ERR, "plugin: Failed to load plugin '%s': "
339                                 "could not find symbol 'sdb_module_init'", name);
340                 return -1;
341         }
343         status = mod_init(&plugin_info);
344         if (status) {
345                 sdb_log(SDB_LOG_ERR, "plugin: Failed to initialize "
346                                 "plugin '%s'", name);
347                 return -1;
348         }
350         /* compare minor version */
351         if ((plugin_info.version < 0)
352                         || ((int)(plugin_info.version / 100) != (int)(SDB_VERSION / 100)))
353                 sdb_log(SDB_LOG_WARNING, "plugin: WARNING: version of "
354                                 "plugin '%s' (%i.%i.%i) does not match our version "
355                                 "(%i.%i.%i); this might cause problems",
356                                 name, SDB_VERSION_DECODE(plugin_info.version),
357                                 SDB_VERSION_DECODE(SDB_VERSION));
359         sdb_log(SDB_LOG_INFO, "plugin: Successfully loaded "
360                         "plugin '%s' v%i (%s)\n\t%s",
361                         plugin_info.name, plugin_info.plugin_version,
362                         plugin_info.description, plugin_info.copyright);
363         return 0;
364 } /* sdb_plugin_load */
366 int
367 sdb_plugin_set_info(sdb_plugin_info_t *info, int type, ...)
369         va_list ap;
371         if (! info)
372                 return -1;
374         va_start(ap, type);
376         switch (type) {
377                 case SDB_PLUGIN_INFO_NAME:
378                         {
379                                 char *name = va_arg(ap, char *);
380                                 info->name = name;
381                         }
382                         break;
383                 case SDB_PLUGIN_INFO_DESC:
384                         {
385                                 char *desc = va_arg(ap, char *);
386                                 info->description = desc;
387                         }
388                         break;
389                 case SDB_PLUGIN_INFO_COPYRIGHT:
390                         {
391                                 char *copyright = va_arg(ap, char *);
392                                 info->copyright = copyright;
393                         }
394                         break;
395                 case SDB_PLUGIN_INFO_LICENSE:
396                         {
397                                 char *license = va_arg(ap, char *);
398                                 info->license = license;
399                         }
400                         break;
401                 case SDB_PLUGIN_INFO_VERSION:
402                         {
403                                 int version = va_arg(ap, int);
404                                 info->version = version;
405                         }
406                         break;
407                 case SDB_PLUGIN_INFO_PLUGIN_VERSION:
408                         {
409                                 int version = va_arg(ap, int);
410                                 info->plugin_version = version;
411                         }
412                         break;
413                 default:
414                         va_end(ap);
415                         return -1;
416         }
418         va_end(ap);
419         return 0;
420 } /* sdb_plugin_set_info */
422 int
423 sdb_plugin_register_config(const char *name, sdb_plugin_config_cb callback)
425         return sdb_plugin_add_callback(&config_list, "init", name,
426                         callback, NULL);
427 } /* sdb_plugin_register_config */
429 int
430 sdb_plugin_register_init(const char *name, sdb_plugin_init_cb callback,
431                 sdb_object_t *user_data)
433         return sdb_plugin_add_callback(&init_list, "init", name,
434                         callback, user_data);
435 } /* sdb_plugin_register_init */
437 int
438 sdb_plugin_register_shutdown(const char *name, sdb_plugin_shutdown_cb callback,
439                 sdb_object_t *user_data)
441         return sdb_plugin_add_callback(&shutdown_list, "shutdown", name,
442                         callback, user_data);
443 } /* sdb_plugin_register_shutdown */
445 int
446 sdb_plugin_register_log(const char *name, sdb_plugin_log_cb callback,
447                 sdb_object_t *user_data)
449         return sdb_plugin_add_callback(&log_list, "log", name, callback,
450                         user_data);
451 } /* sdb_plugin_register_log */
453 int
454 sdb_plugin_register_collector(const char *name, sdb_plugin_collector_cb callback,
455                 const sdb_time_t *interval, sdb_object_t *user_data)
457         sdb_object_t *obj;
459         if ((! name) || (! callback))
460                 return -1;
462         if (! collector_list)
463                 collector_list = sdb_llist_create();
464         if (! collector_list)
465                 return -1;
467         obj = sdb_object_create(sdb_plugin_collector_cb_type,
468                         &collector_list, "collector", name, callback, user_data);
469         if (! obj)
470                 return -1;
472         if (interval)
473                 SDB_PLUGIN_CCB(obj)->ccb_interval = *interval;
474         else {
475                 sdb_time_t tmp = sdb_plugin_get_ctx().interval;
477                 if (tmp > 0)
478                         SDB_PLUGIN_CCB(obj)->ccb_interval = tmp;
479                 else
480                         SDB_PLUGIN_CCB(obj)->ccb_interval = 0;
481         }
483         if (! (SDB_PLUGIN_CCB(obj)->ccb_next_update = sdb_gettime())) {
484                 char errbuf[1024];
485                 sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
486                                 "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
487                 sdb_object_deref(obj);
488                 return -1;
489         }
491         if (sdb_llist_insert_sorted(collector_list, obj,
492                                 sdb_plugin_cmp_next_update)) {
493                 sdb_object_deref(obj);
494                 return -1;
495         }
497         /* pass control to the list */
498         sdb_object_deref(obj);
500         sdb_log(SDB_LOG_INFO, "plugin: Registered collector callback '%s' "
501                         "(interval = %.3fs).", name,
502                         SDB_TIME_TO_DOUBLE(SDB_PLUGIN_CCB(obj)->ccb_interval));
503         return 0;
504 } /* sdb_plugin_register_collector */
506 sdb_plugin_ctx_t
507 sdb_plugin_get_ctx(void)
509         sdb_plugin_ctx_t *ctx;
511         if (! plugin_ctx_key_initialized)
512                 sdb_plugin_ctx_init();
513         ctx = pthread_getspecific(plugin_ctx_key);
515         if (! ctx)
516                 ctx = sdb_plugin_ctx_create();
517         if (! ctx)
518                 return plugin_default_ctx;
519         return *ctx;
520 } /* sdb_plugin_get_ctx */
522 sdb_plugin_ctx_t
523 sdb_plugin_set_ctx(sdb_plugin_ctx_t ctx)
525         sdb_plugin_ctx_t *tmp;
526         sdb_plugin_ctx_t old;
528         if (! plugin_ctx_key_initialized)
529                 sdb_plugin_ctx_init();
530         tmp = pthread_getspecific(plugin_ctx_key);
532         if (! tmp)
533                 tmp = sdb_plugin_ctx_create();
534         if (! tmp)
535                 return plugin_default_ctx;
537         old = *tmp;
538         *tmp = ctx;
539         return old;
540 } /* sdb_plugin_set_ctx */
542 int
543 sdb_plugin_configure(const char *name, oconfig_item_t *ci)
545         sdb_plugin_cb_t *plugin;
546         sdb_plugin_config_cb callback;
548         sdb_plugin_ctx_t old_ctx;
550         int status;
552         if ((! name) || (! ci))
553                 return -1;
555         plugin = sdb_plugin_find_by_name(config_list, name);
556         if (! plugin) {
557                 /* XXX: check if any such plugin has been loaded */
558                 sdb_log(SDB_LOG_ERR, "plugin: Plugin '%s' did not register "
559                                 "a config callback.", name);
560                 errno = ENOENT;
561                 return -1;
562         }
564         old_ctx = sdb_plugin_set_ctx(plugin->cb_ctx);
565         callback = plugin->cb_callback;
566         status = callback(ci);
567         sdb_plugin_set_ctx(old_ctx);
568         return status;
569 } /* sdb_plugin_configure */
571 int
572 sdb_plugin_init_all(void)
574         sdb_llist_iter_t *iter;
576         iter = sdb_llist_get_iter(init_list);
577         while (sdb_llist_iter_has_next(iter)) {
578                 sdb_plugin_init_cb callback;
579                 sdb_plugin_ctx_t old_ctx;
581                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
582                 assert(obj);
584                 callback = SDB_PLUGIN_CB(obj)->cb_callback;
586                 old_ctx = sdb_plugin_set_ctx(SDB_PLUGIN_CB(obj)->cb_ctx);
587                 if (callback(SDB_PLUGIN_CB(obj)->cb_user_data)) {
588                         /* XXX: unload plugin */
589                 }
590                 sdb_plugin_set_ctx(old_ctx);
591         }
592         sdb_llist_iter_destroy(iter);
593         return 0;
594 } /* sdb_plugin_init_all */
596 int
597 sdb_plugin_collector_loop(sdb_plugin_loop_t *loop)
599         if (! collector_list) {
600                 sdb_log(SDB_LOG_WARNING, "plugin: No collectors registered. "
601                                 "Quiting main loop.");
602                 return -1;
603         }
605         if (! loop)
606                 return -1;
608         while (loop->do_loop) {
609                 sdb_plugin_collector_cb callback;
610                 sdb_plugin_ctx_t old_ctx;
612                 sdb_time_t interval, now;
614                 sdb_object_t *obj = sdb_llist_shift(collector_list);
615                 if (! obj)
616                         return -1;
618                 callback = SDB_PLUGIN_CCB(obj)->ccb_callback;
620                 if (! (now = sdb_gettime())) {
621                         char errbuf[1024];
622                         sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
623                                         "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
624                         now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
625                 }
627                 if (now < SDB_PLUGIN_CCB(obj)->ccb_next_update) {
628                         interval = SDB_PLUGIN_CCB(obj)->ccb_next_update - now;
630                         errno = 0;
631                         while (loop->do_loop && sdb_sleep(interval, &interval)) {
632                                 if (errno != EINTR) {
633                                         char errbuf[1024];
634                                         sdb_log(SDB_LOG_ERR, "plugin: Failed to sleep: %s",
635                                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
636                                         return -1;
637                                 }
638                                 errno = 0;
639                         }
641                         if (! loop->do_loop)
642                                 return 0;
643                 }
645                 old_ctx = sdb_plugin_set_ctx(SDB_PLUGIN_CCB(obj)->ccb_ctx);
646                 if (callback(SDB_PLUGIN_CCB(obj)->ccb_user_data)) {
647                         /* XXX */
648                 }
649                 sdb_plugin_set_ctx(old_ctx);
651                 interval = SDB_PLUGIN_CCB(obj)->ccb_interval;
652                 if (! interval)
653                         interval = loop->default_interval;
654                 if (! interval) {
655                         sdb_log(SDB_LOG_WARNING, "plugin: No interval configured "
656                                         "for plugin '%s'; skipping any further "
657                                         "iterations.", SDB_PLUGIN_CCB(obj)->ccb_name);
658                         sdb_object_deref(obj);
659                         continue;
660                 }
662                 SDB_PLUGIN_CCB(obj)->ccb_next_update += interval;
664                 if (! (now = sdb_gettime())) {
665                         char errbuf[1024];
666                         sdb_log(SDB_LOG_ERR, "plugin: Failed to determine current "
667                                         "time: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
668                         now = SDB_PLUGIN_CCB(obj)->ccb_next_update;
669                 }
671                 if (now > SDB_PLUGIN_CCB(obj)->ccb_next_update) {
672                         sdb_log(SDB_LOG_WARNING, "plugin: Plugin '%s' took too "
673                                         "long; skipping iterations to keep up.",
674                                         SDB_PLUGIN_CCB(obj)->ccb_name);
675                         SDB_PLUGIN_CCB(obj)->ccb_next_update = now;
676                 }
678                 if (sdb_llist_insert_sorted(collector_list, obj,
679                                         sdb_plugin_cmp_next_update)) {
680                         sdb_log(SDB_LOG_ERR, "plugin: Failed to re-insert "
681                                         "plugin '%s' into collector list. Unable to further "
682                                         "use the plugin.",
683                                         SDB_PLUGIN_CCB(obj)->ccb_name);
684                         sdb_object_deref(obj);
685                         return -1;
686                 }
688                 /* pass control back to the list */
689                 sdb_object_deref(obj);
690         }
691         return 0;
692 } /* sdb_plugin_read_loop */
694 int
695 sdb_plugin_log(int prio, const char *msg)
697         sdb_llist_iter_t *iter;
698         int ret = -1;
700         if (! log_list)
701                 return fprintf(stderr, "[%s] %s\n", SDB_LOG_PRIO_TO_STRING(prio), msg);
703         iter = sdb_llist_get_iter(log_list);
704         while (sdb_llist_iter_has_next(iter)) {
705                 sdb_plugin_log_cb callback;
706                 int tmp;
708                 sdb_object_t *obj = sdb_llist_iter_get_next(iter);
709                 assert(obj);
711                 callback = SDB_PLUGIN_CB(obj)->cb_callback;
712                 tmp = callback(prio, msg, SDB_PLUGIN_CB(obj)->cb_user_data);
713                 if (tmp > ret)
714                         ret = tmp;
715         }
716         sdb_llist_iter_destroy(iter);
717         return ret;
718 } /* sdb_plugin_log */
720 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */