src/plugin.[ch]: Implement "missing" callbacks.
[collectd.git] / src / plugin.c
1 /**
2  * collectd - src/plugin.c
3  * Copyright (C) 2005-2010  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at collectd.org>
20  *   Sebastian Harl <sh at tokkee.org>
21  **/
22
23 #include "collectd.h"
24 #include "utils_complain.h"
25
26 #include <ltdl.h>
27
28 #if HAVE_PTHREAD_H
29 # include <pthread.h>
30 #endif
31
32 #include "common.h"
33 #include "plugin.h"
34 #include "configfile.h"
35 #include "utils_avltree.h"
36 #include "utils_llist.h"
37 #include "utils_heap.h"
38 #include "utils_cache.h"
39 #include "utils_threshold.h"
40 #include "filter_chain.h"
41
42 /*
43  * Private structures
44  */
45 struct callback_func_s
46 {
47         void *cf_callback;
48         user_data_t cf_udata;
49 };
50 typedef struct callback_func_s callback_func_t;
51
52 #define RF_SIMPLE  0
53 #define RF_COMPLEX 1
54 #define RF_REMOVE  65535
55 struct read_func_s
56 {
57         /* `read_func_t' "inherits" from `callback_func_t'.
58          * The `rf_super' member MUST be the first one in this structure! */
59 #define rf_callback rf_super.cf_callback
60 #define rf_udata rf_super.cf_udata
61         callback_func_t rf_super;
62         char rf_group[DATA_MAX_NAME_LEN];
63         char rf_name[DATA_MAX_NAME_LEN];
64         int rf_type;
65         struct timespec rf_interval;
66         struct timespec rf_effective_interval;
67         struct timespec rf_next_read;
68 };
69 typedef struct read_func_s read_func_t;
70
71 /*
72  * Private variables
73  */
74 static llist_t *list_init;
75 static llist_t *list_write;
76 static llist_t *list_flush;
77 static llist_t *list_missing;
78 static llist_t *list_shutdown;
79 static llist_t *list_log;
80 static llist_t *list_notification;
81
82 static fc_chain_t *pre_cache_chain = NULL;
83 static fc_chain_t *post_cache_chain = NULL;
84
85 static c_avl_tree_t *data_sets;
86
87 static char *plugindir = NULL;
88
89 static c_heap_t       *read_heap = NULL;
90 static llist_t        *read_list;
91 static int             read_loop = 1;
92 static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
93 static pthread_cond_t  read_cond = PTHREAD_COND_INITIALIZER;
94 static pthread_t      *read_threads = NULL;
95 static int             read_threads_num = 0;
96
97 /*
98  * Static functions
99  */
100 static const char *plugin_get_dir (void)
101 {
102         if (plugindir == NULL)
103                 return (PLUGINDIR);
104         else
105                 return (plugindir);
106 }
107
108 static void destroy_callback (callback_func_t *cf) /* {{{ */
109 {
110         if (cf == NULL)
111                 return;
112
113         if ((cf->cf_udata.data != NULL) && (cf->cf_udata.free_func != NULL))
114         {
115                 cf->cf_udata.free_func (cf->cf_udata.data);
116                 cf->cf_udata.data = NULL;
117                 cf->cf_udata.free_func = NULL;
118         }
119         sfree (cf);
120 } /* }}} void destroy_callback */
121
122 static void destroy_all_callbacks (llist_t **list) /* {{{ */
123 {
124         llentry_t *le;
125
126         if (*list == NULL)
127                 return;
128
129         le = llist_head (*list);
130         while (le != NULL)
131         {
132                 llentry_t *le_next;
133
134                 le_next = le->next;
135
136                 sfree (le->key);
137                 destroy_callback (le->value);
138                 le->value = NULL;
139
140                 le = le_next;
141         }
142
143         llist_destroy (*list);
144         *list = NULL;
145 } /* }}} void destroy_all_callbacks */
146
147 static void destroy_read_heap (void) /* {{{ */
148 {
149         if (read_heap == NULL)
150                 return;
151
152         while (42)
153         {
154                 callback_func_t *cf;
155
156                 cf = c_heap_get_root (read_heap);
157                 if (cf == NULL)
158                         break;
159
160                 destroy_callback (cf);
161         }
162
163         c_heap_destroy (read_heap);
164         read_heap = NULL;
165 } /* }}} void destroy_read_heap */
166
167 static int register_callback (llist_t **list, /* {{{ */
168                 const char *name, callback_func_t *cf)
169 {
170         llentry_t *le;
171         char *key;
172
173         if (*list == NULL)
174         {
175                 *list = llist_create ();
176                 if (*list == NULL)
177                 {
178                         ERROR ("plugin: register_callback: "
179                                         "llist_create failed.");
180                         destroy_callback (cf);
181                         return (-1);
182                 }
183         }
184
185         key = strdup (name);
186         if (key == NULL)
187         {
188                 ERROR ("plugin: register_callback: strdup failed.");
189                 destroy_callback (cf);
190                 return (-1);
191         }
192
193         le = llist_search (*list, name);
194         if (le == NULL)
195         {
196                 le = llentry_create (key, cf);
197                 if (le == NULL)
198                 {
199                         ERROR ("plugin: register_callback: "
200                                         "llentry_create failed.");
201                         free (key);
202                         destroy_callback (cf);
203                         return (-1);
204                 }
205
206                 llist_append (*list, le);
207         }
208         else
209         {
210                 callback_func_t *old_cf;
211
212                 old_cf = le->value;
213                 le->value = cf;
214
215                 WARNING ("plugin: register_callback: "
216                                 "a callback named `%s' already exists - "
217                                 "overwriting the old entry!", name);
218
219                 destroy_callback (old_cf);
220                 sfree (key);
221         }
222
223         return (0);
224 } /* }}} int register_callback */
225
226 static int create_register_callback (llist_t **list, /* {{{ */
227                 const char *name, void *callback, user_data_t *ud)
228 {
229         callback_func_t *cf;
230
231         cf = (callback_func_t *) malloc (sizeof (*cf));
232         if (cf == NULL)
233         {
234                 ERROR ("plugin: create_register_callback: malloc failed.");
235                 return (-1);
236         }
237         memset (cf, 0, sizeof (*cf));
238
239         cf->cf_callback = callback;
240         if (ud == NULL)
241         {
242                 cf->cf_udata.data = NULL;
243                 cf->cf_udata.free_func = NULL;
244         }
245         else
246         {
247                 cf->cf_udata = *ud;
248         }
249
250         return (register_callback (list, name, cf));
251 } /* }}} int create_register_callback */
252
253 static int plugin_unregister (llist_t *list, const char *name) /* {{{ */
254 {
255         llentry_t *e;
256
257         if (list == NULL)
258                 return (-1);
259
260         e = llist_search (list, name);
261         if (e == NULL)
262                 return (-1);
263
264         llist_remove (list, e);
265
266         sfree (e->key);
267         destroy_callback (e->value);
268
269         llentry_destroy (e);
270
271         return (0);
272 } /* }}} int plugin_unregister */
273
274 /*
275  * (Try to) load the shared object `file'. Won't complain if it isn't a shared
276  * object, but it will bitch about a shared object not having a
277  * ``module_register'' symbol..
278  */
279 static int plugin_load_file (char *file, uint32_t flags)
280 {
281         lt_dlhandle dlh;
282         void (*reg_handle) (void);
283
284         DEBUG ("file = %s", file);
285
286         lt_dlinit ();
287         lt_dlerror (); /* clear errors */
288
289 #if LIBTOOL_VERSION == 2
290         if (flags & PLUGIN_FLAGS_GLOBAL) {
291                 lt_dladvise advise;
292                 lt_dladvise_init(&advise);
293                 lt_dladvise_global(&advise);
294                 dlh = lt_dlopenadvise(file, advise);
295                 lt_dladvise_destroy(&advise);
296         } else {
297                 dlh = lt_dlopen (file);
298         }
299 #else /* if LIBTOOL_VERSION == 1 */
300         if (flags & PLUGIN_FLAGS_GLOBAL)
301                 ERROR ("plugin_load_file: The global flag is not supported, "
302                                 "libtool 2 is required for this.");
303         dlh = lt_dlopen (file);
304 #endif
305
306         if (dlh == NULL)
307         {
308                 const char *error = lt_dlerror ();
309
310                 ERROR ("lt_dlopen (%s) failed: %s", file, error);
311                 fprintf (stderr, "lt_dlopen (%s) failed: %s\n", file, error);
312                 return (1);
313         }
314
315         if ((reg_handle = (void (*) (void)) lt_dlsym (dlh, "module_register")) == NULL)
316         {
317                 WARNING ("Couldn't find symbol `module_register' in `%s': %s\n",
318                                 file, lt_dlerror ());
319                 lt_dlclose (dlh);
320                 return (-1);
321         }
322
323         (*reg_handle) ();
324
325         return (0);
326 }
327
328 static _Bool timeout_reached(struct timespec timeout)
329 {
330         struct timeval now;
331         gettimeofday(&now, NULL);
332         return (now.tv_sec >= timeout.tv_sec && now.tv_usec >= (timeout.tv_nsec / 1000));
333 }
334
335 static void *plugin_read_thread (void __attribute__((unused)) *args)
336 {
337         while (read_loop != 0)
338         {
339                 read_func_t *rf;
340                 cdtime_t now;
341                 int status;
342                 int rf_type;
343                 int rc;
344
345                 /* Get the read function that needs to be read next. */
346                 rf = c_heap_get_root (read_heap);
347                 if (rf == NULL)
348                 {
349                         struct timespec abstime;
350
351                         now = cdtime ();
352
353                         CDTIME_T_TO_TIMESPEC (now + interval_g, &abstime);
354
355                         pthread_mutex_lock (&read_lock);
356                         pthread_cond_timedwait (&read_cond, &read_lock,
357                                         &abstime);
358                         pthread_mutex_unlock (&read_lock);
359                         continue;
360                 }
361
362                 if ((rf->rf_interval.tv_sec == 0) && (rf->rf_interval.tv_nsec == 0))
363                 {
364                         now = cdtime ();
365
366                         CDTIME_T_TO_TIMESPEC (interval_g, &rf->rf_interval);
367
368                         rf->rf_effective_interval = rf->rf_interval;
369
370                         CDTIME_T_TO_TIMESPEC (now, &rf->rf_next_read);
371                 }
372
373                 /* sleep until this entry is due,
374                  * using pthread_cond_timedwait */
375                 pthread_mutex_lock (&read_lock);
376                 /* In pthread_cond_timedwait, spurious wakeups are possible
377                  * (and really happen, at least on NetBSD with > 1 CPU), thus
378                  * we need to re-evaluate the condition every time
379                  * pthread_cond_timedwait returns. */
380                 rc = 0;
381                 while ((read_loop != 0)
382                                 && !timeout_reached(rf->rf_next_read)
383                                 && rc == 0)
384                 {
385                         rc = pthread_cond_timedwait (&read_cond, &read_lock,
386                                 &rf->rf_next_read);
387                 }
388
389                 /* Must hold `read_lock' when accessing `rf->rf_type'. */
390                 rf_type = rf->rf_type;
391                 pthread_mutex_unlock (&read_lock);
392
393                 /* Check if we're supposed to stop.. This may have interrupted
394                  * the sleep, too. */
395                 if (read_loop == 0)
396                 {
397                         /* Insert `rf' again, so it can be free'd correctly */
398                         c_heap_insert (read_heap, rf);
399                         break;
400                 }
401
402                 /* The entry has been marked for deletion. The linked list
403                  * entry has already been removed by `plugin_unregister_read'.
404                  * All we have to do here is free the `read_func_t' and
405                  * continue. */
406                 if (rf_type == RF_REMOVE)
407                 {
408                         DEBUG ("plugin_read_thread: Destroying the `%s' "
409                                         "callback.", rf->rf_name);
410                         destroy_callback ((callback_func_t *) rf);
411                         rf = NULL;
412                         continue;
413                 }
414
415                 DEBUG ("plugin_read_thread: Handling `%s'.", rf->rf_name);
416
417                 if (rf_type == RF_SIMPLE)
418                 {
419                         int (*callback) (void);
420
421                         callback = rf->rf_callback;
422                         status = (*callback) ();
423                 }
424                 else
425                 {
426                         plugin_read_cb callback;
427
428                         assert (rf_type == RF_COMPLEX);
429
430                         callback = rf->rf_callback;
431                         status = (*callback) (&rf->rf_udata);
432                 }
433
434                 /* If the function signals failure, we will increase the
435                  * intervals in which it will be called. */
436                 if (status != 0)
437                 {
438                         rf->rf_effective_interval.tv_sec *= 2;
439                         rf->rf_effective_interval.tv_nsec *= 2;
440                         NORMALIZE_TIMESPEC (rf->rf_effective_interval);
441
442                         if (rf->rf_effective_interval.tv_sec >= 86400)
443                         {
444                                 rf->rf_effective_interval.tv_sec = 86400;
445                                 rf->rf_effective_interval.tv_nsec = 0;
446                         }
447
448                         NOTICE ("read-function of plugin `%s' failed. "
449                                         "Will suspend it for %i seconds.",
450                                         rf->rf_name,
451                                         (int) rf->rf_effective_interval.tv_sec);
452                 }
453                 else
454                 {
455                         /* Success: Restore the interval, if it was changed. */
456                         rf->rf_effective_interval = rf->rf_interval;
457                 }
458
459                 /* update the ``next read due'' field */
460                 now = cdtime ();
461
462                 DEBUG ("plugin_read_thread: Effective interval of the "
463                                 "%s plugin is %i.%09i.",
464                                 rf->rf_name,
465                                 (int) rf->rf_effective_interval.tv_sec,
466                                 (int) rf->rf_effective_interval.tv_nsec);
467
468                 /* Calculate the next (absolute) time at which this function
469                  * should be called. */
470                 rf->rf_next_read.tv_sec = rf->rf_next_read.tv_sec
471                         + rf->rf_effective_interval.tv_sec;
472                 rf->rf_next_read.tv_nsec = rf->rf_next_read.tv_nsec
473                         + rf->rf_effective_interval.tv_nsec;
474                 NORMALIZE_TIMESPEC (rf->rf_next_read);
475
476                 /* Check, if `rf_next_read' is in the past. */
477                 if (TIMESPEC_TO_CDTIME_T (&rf->rf_next_read) < now)
478                 {
479                         /* `rf_next_read' is in the past. Insert `now'
480                          * so this value doesn't trail off into the
481                          * past too much. */
482                         CDTIME_T_TO_TIMESPEC (now, &rf->rf_next_read);
483                 }
484
485                 DEBUG ("plugin_read_thread: Next read of the %s plugin at %i.%09i.",
486                                 rf->rf_name,
487                                 (int) rf->rf_next_read.tv_sec,
488                                 (int) rf->rf_next_read.tv_nsec);
489
490                 /* Re-insert this read function into the heap again. */
491                 c_heap_insert (read_heap, rf);
492         } /* while (read_loop) */
493
494         pthread_exit (NULL);
495         return ((void *) 0);
496 } /* void *plugin_read_thread */
497
498 static void start_read_threads (int num)
499 {
500         int i;
501
502         if (read_threads != NULL)
503                 return;
504
505         read_threads = (pthread_t *) calloc (num, sizeof (pthread_t));
506         if (read_threads == NULL)
507         {
508                 ERROR ("plugin: start_read_threads: calloc failed.");
509                 return;
510         }
511
512         read_threads_num = 0;
513         for (i = 0; i < num; i++)
514         {
515                 if (pthread_create (read_threads + read_threads_num, NULL,
516                                         plugin_read_thread, NULL) == 0)
517                 {
518                         read_threads_num++;
519                 }
520                 else
521                 {
522                         ERROR ("plugin: start_read_threads: pthread_create failed.");
523                         return;
524                 }
525         } /* for (i) */
526 } /* void start_read_threads */
527
528 static void stop_read_threads (void)
529 {
530         int i;
531
532         if (read_threads == NULL)
533                 return;
534
535         INFO ("collectd: Stopping %i read threads.", read_threads_num);
536
537         pthread_mutex_lock (&read_lock);
538         read_loop = 0;
539         DEBUG ("plugin: stop_read_threads: Signalling `read_cond'");
540         pthread_cond_broadcast (&read_cond);
541         pthread_mutex_unlock (&read_lock);
542
543         for (i = 0; i < read_threads_num; i++)
544         {
545                 if (pthread_join (read_threads[i], NULL) != 0)
546                 {
547                         ERROR ("plugin: stop_read_threads: pthread_join failed.");
548                 }
549                 read_threads[i] = (pthread_t) 0;
550         }
551         sfree (read_threads);
552         read_threads_num = 0;
553 } /* void stop_read_threads */
554
555 /*
556  * Public functions
557  */
558 void plugin_set_dir (const char *dir)
559 {
560         if (plugindir != NULL)
561                 free (plugindir);
562
563         if (dir == NULL)
564                 plugindir = NULL;
565         else if ((plugindir = strdup (dir)) == NULL)
566         {
567                 char errbuf[1024];
568                 ERROR ("strdup failed: %s",
569                                 sstrerror (errno, errbuf, sizeof (errbuf)));
570         }
571 }
572
573 #define BUFSIZE 512
574 int plugin_load (const char *type, uint32_t flags)
575 {
576         DIR  *dh;
577         const char *dir;
578         char  filename[BUFSIZE] = "";
579         char  typename[BUFSIZE];
580         int   typename_len;
581         int   ret;
582         struct stat    statbuf;
583         struct dirent *de;
584         int status;
585
586         DEBUG ("type = %s", type);
587
588         dir = plugin_get_dir ();
589         ret = 1;
590
591         /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
592          * type when matching the filename */
593         status = ssnprintf (typename, sizeof (typename), "%s.so", type);
594         if ((status < 0) || ((size_t) status >= sizeof (typename)))
595         {
596                 WARNING ("snprintf: truncated: `%s.so'", type);
597                 return (-1);
598         }
599         typename_len = strlen (typename);
600
601         if ((dh = opendir (dir)) == NULL)
602         {
603                 char errbuf[1024];
604                 ERROR ("opendir (%s): %s", dir,
605                                 sstrerror (errno, errbuf, sizeof (errbuf)));
606                 return (-1);
607         }
608
609         while ((de = readdir (dh)) != NULL)
610         {
611                 if (strncasecmp (de->d_name, typename, typename_len))
612                         continue;
613
614                 status = ssnprintf (filename, sizeof (filename),
615                                 "%s/%s", dir, de->d_name);
616                 if ((status < 0) || ((size_t) status >= sizeof (filename)))
617                 {
618                         WARNING ("snprintf: truncated: `%s/%s'", dir, de->d_name);
619                         continue;
620                 }
621
622                 if (lstat (filename, &statbuf) == -1)
623                 {
624                         char errbuf[1024];
625                         WARNING ("stat %s: %s", filename,
626                                         sstrerror (errno, errbuf, sizeof (errbuf)));
627                         continue;
628                 }
629                 else if (!S_ISREG (statbuf.st_mode))
630                 {
631                         /* don't follow symlinks */
632                         WARNING ("stat %s: not a regular file", filename);
633                         continue;
634                 }
635
636                 if (plugin_load_file (filename, flags) == 0)
637                 {
638                         /* success */
639                         ret = 0;
640                         break;
641                 }
642                 else
643                 {
644                         fprintf (stderr, "Unable to load plugin %s.\n", type);
645                 }
646         }
647
648         closedir (dh);
649
650         if (filename[0] == '\0')
651                 fprintf (stderr, "Could not find plugin %s.\n", type);
652
653         return (ret);
654 }
655
656 /*
657  * The `register_*' functions follow
658  */
659 int plugin_register_config (const char *name,
660                 int (*callback) (const char *key, const char *val),
661                 const char **keys, int keys_num)
662 {
663         cf_register (name, callback, keys, keys_num);
664         return (0);
665 } /* int plugin_register_config */
666
667 int plugin_register_complex_config (const char *type,
668                 int (*callback) (oconfig_item_t *))
669 {
670         return (cf_register_complex (type, callback));
671 } /* int plugin_register_complex_config */
672
673 int plugin_register_init (const char *name,
674                 int (*callback) (void))
675 {
676         return (create_register_callback (&list_init, name, (void *) callback,
677                                 /* user_data = */ NULL));
678 } /* plugin_register_init */
679
680 static int plugin_compare_read_func (const void *arg0, const void *arg1)
681 {
682         const read_func_t *rf0;
683         const read_func_t *rf1;
684
685         rf0 = arg0;
686         rf1 = arg1;
687
688         if (rf0->rf_next_read.tv_sec < rf1->rf_next_read.tv_sec)
689                 return (-1);
690         else if (rf0->rf_next_read.tv_sec > rf1->rf_next_read.tv_sec)
691                 return (1);
692         else if (rf0->rf_next_read.tv_nsec < rf1->rf_next_read.tv_nsec)
693                 return (-1);
694         else if (rf0->rf_next_read.tv_nsec > rf1->rf_next_read.tv_nsec)
695                 return (1);
696         else
697                 return (0);
698 } /* int plugin_compare_read_func */
699
700 /* Add a read function to both, the heap and a linked list. The linked list if
701  * used to look-up read functions, especially for the remove function. The heap
702  * is used to determine which plugin to read next. */
703 static int plugin_insert_read (read_func_t *rf)
704 {
705         int status;
706         llentry_t *le;
707
708         pthread_mutex_lock (&read_lock);
709
710         if (read_list == NULL)
711         {
712                 read_list = llist_create ();
713                 if (read_list == NULL)
714                 {
715                         pthread_mutex_unlock (&read_lock);
716                         ERROR ("plugin_insert_read: read_list failed.");
717                         return (-1);
718                 }
719         }
720
721         if (read_heap == NULL)
722         {
723                 read_heap = c_heap_create (plugin_compare_read_func);
724                 if (read_heap == NULL)
725                 {
726                         pthread_mutex_unlock (&read_lock);
727                         ERROR ("plugin_insert_read: c_heap_create failed.");
728                         return (-1);
729                 }
730         }
731
732         le = llentry_create (rf->rf_name, rf);
733         if (le == NULL)
734         {
735                 pthread_mutex_unlock (&read_lock);
736                 ERROR ("plugin_insert_read: llentry_create failed.");
737                 return (-1);
738         }
739
740         status = c_heap_insert (read_heap, rf);
741         if (status != 0)
742         {
743                 pthread_mutex_unlock (&read_lock);
744                 ERROR ("plugin_insert_read: c_heap_insert failed.");
745                 llentry_destroy (le);
746                 return (-1);
747         }
748
749         /* This does not fail. */
750         llist_append (read_list, le);
751
752         pthread_mutex_unlock (&read_lock);
753         return (0);
754 } /* int plugin_insert_read */
755
756 int plugin_register_read (const char *name,
757                 int (*callback) (void))
758 {
759         read_func_t *rf;
760
761         rf = (read_func_t *) malloc (sizeof (read_func_t));
762         if (rf == NULL)
763         {
764                 char errbuf[1024];
765                 ERROR ("plugin_register_read: malloc failed: %s",
766                                 sstrerror (errno, errbuf, sizeof (errbuf)));
767                 return (-1);
768         }
769
770         memset (rf, 0, sizeof (read_func_t));
771         rf->rf_callback = (void *) callback;
772         rf->rf_udata.data = NULL;
773         rf->rf_udata.free_func = NULL;
774         rf->rf_group[0] = '\0';
775         sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
776         rf->rf_type = RF_SIMPLE;
777         rf->rf_interval.tv_sec = 0;
778         rf->rf_interval.tv_nsec = 0;
779         rf->rf_effective_interval = rf->rf_interval;
780
781         return (plugin_insert_read (rf));
782 } /* int plugin_register_read */
783
784 int plugin_register_complex_read (const char *group, const char *name,
785                 plugin_read_cb callback,
786                 const struct timespec *interval,
787                 user_data_t *user_data)
788 {
789         read_func_t *rf;
790
791         rf = (read_func_t *) malloc (sizeof (read_func_t));
792         if (rf == NULL)
793         {
794                 ERROR ("plugin_register_complex_read: malloc failed.");
795                 return (-1);
796         }
797
798         memset (rf, 0, sizeof (read_func_t));
799         rf->rf_callback = (void *) callback;
800         if (group != NULL)
801                 sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
802         else
803                 rf->rf_group[0] = '\0';
804         sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
805         rf->rf_type = RF_COMPLEX;
806         if (interval != NULL)
807         {
808                 rf->rf_interval = *interval;
809         }
810         rf->rf_effective_interval = rf->rf_interval;
811
812         /* Set user data */
813         if (user_data == NULL)
814         {
815                 rf->rf_udata.data = NULL;
816                 rf->rf_udata.free_func = NULL;
817         }
818         else
819         {
820                 rf->rf_udata = *user_data;
821         }
822
823         return (plugin_insert_read (rf));
824 } /* int plugin_register_complex_read */
825
826 int plugin_register_write (const char *name,
827                 plugin_write_cb callback, user_data_t *ud)
828 {
829         return (create_register_callback (&list_write, name,
830                                 (void *) callback, ud));
831 } /* int plugin_register_write */
832
833 int plugin_register_flush (const char *name,
834                 plugin_flush_cb callback, user_data_t *ud)
835 {
836         return (create_register_callback (&list_flush, name,
837                                 (void *) callback, ud));
838 } /* int plugin_register_flush */
839
840 int plugin_register_missing (const char *name,
841                 plugin_missing_cb callback, user_data_t *ud)
842 {
843         return (create_register_callback (&list_missing, name,
844                                 (void *) callback, ud));
845 } /* int plugin_register_missing */
846
847 int plugin_register_shutdown (char *name,
848                 int (*callback) (void))
849 {
850         return (create_register_callback (&list_shutdown, name,
851                                 (void *) callback, /* user_data = */ NULL));
852 } /* int plugin_register_shutdown */
853
854 int plugin_register_data_set (const data_set_t *ds)
855 {
856         data_set_t *ds_copy;
857         int i;
858
859         if ((data_sets != NULL)
860                         && (c_avl_get (data_sets, ds->type, NULL) == 0))
861         {
862                 NOTICE ("Replacing DS `%s' with another version.", ds->type);
863                 plugin_unregister_data_set (ds->type);
864         }
865         else if (data_sets == NULL)
866         {
867                 data_sets = c_avl_create ((int (*) (const void *, const void *)) strcmp);
868                 if (data_sets == NULL)
869                         return (-1);
870         }
871
872         ds_copy = (data_set_t *) malloc (sizeof (data_set_t));
873         if (ds_copy == NULL)
874                 return (-1);
875         memcpy(ds_copy, ds, sizeof (data_set_t));
876
877         ds_copy->ds = (data_source_t *) malloc (sizeof (data_source_t)
878                         * ds->ds_num);
879         if (ds_copy->ds == NULL)
880         {
881                 free (ds_copy);
882                 return (-1);
883         }
884
885         for (i = 0; i < ds->ds_num; i++)
886                 memcpy (ds_copy->ds + i, ds->ds + i, sizeof (data_source_t));
887
888         return (c_avl_insert (data_sets, (void *) ds_copy->type, (void *) ds_copy));
889 } /* int plugin_register_data_set */
890
891 int plugin_register_log (const char *name,
892                 plugin_log_cb callback, user_data_t *ud)
893 {
894         return (create_register_callback (&list_log, name,
895                                 (void *) callback, ud));
896 } /* int plugin_register_log */
897
898 int plugin_register_notification (const char *name,
899                 plugin_notification_cb callback, user_data_t *ud)
900 {
901         return (create_register_callback (&list_notification, name,
902                                 (void *) callback, ud));
903 } /* int plugin_register_log */
904
905 int plugin_unregister_config (const char *name)
906 {
907         cf_unregister (name);
908         return (0);
909 } /* int plugin_unregister_config */
910
911 int plugin_unregister_complex_config (const char *name)
912 {
913         cf_unregister_complex (name);
914         return (0);
915 } /* int plugin_unregister_complex_config */
916
917 int plugin_unregister_init (const char *name)
918 {
919         return (plugin_unregister (list_init, name));
920 }
921
922 int plugin_unregister_read (const char *name) /* {{{ */
923 {
924         llentry_t *le;
925         read_func_t *rf;
926
927         if (name == NULL)
928                 return (-ENOENT);
929
930         pthread_mutex_lock (&read_lock);
931
932         if (read_list == NULL)
933         {
934                 pthread_mutex_unlock (&read_lock);
935                 return (-ENOENT);
936         }
937
938         le = llist_search (read_list, name);
939         if (le == NULL)
940         {
941                 pthread_mutex_unlock (&read_lock);
942                 WARNING ("plugin_unregister_read: No such read function: %s",
943                                 name);
944                 return (-ENOENT);
945         }
946
947         llist_remove (read_list, le);
948
949         rf = le->value;
950         assert (rf != NULL);
951         rf->rf_type = RF_REMOVE;
952
953         pthread_mutex_unlock (&read_lock);
954
955         llentry_destroy (le);
956
957         DEBUG ("plugin_unregister_read: Marked `%s' for removal.", name);
958
959         return (0);
960 } /* }}} int plugin_unregister_read */
961
962 static int compare_read_func_group (llentry_t *e, void *ud) /* {{{ */
963 {
964         read_func_t *rf    = e->value;
965         char        *group = ud;
966
967         return strcmp (rf->rf_group, (const char *)group);
968 } /* }}} int compare_read_func_group */
969
970 int plugin_unregister_read_group (const char *group) /* {{{ */
971 {
972         llentry_t *le;
973         read_func_t *rf;
974
975         int found = 0;
976
977         if (group == NULL)
978                 return (-ENOENT);
979
980         pthread_mutex_lock (&read_lock);
981
982         if (read_list == NULL)
983         {
984                 pthread_mutex_unlock (&read_lock);
985                 return (-ENOENT);
986         }
987
988         while (42)
989         {
990                 le = llist_search_custom (read_list,
991                                 compare_read_func_group, (void *)group);
992
993                 if (le == NULL)
994                         break;
995
996                 ++found;
997
998                 llist_remove (read_list, le);
999
1000                 rf = le->value;
1001                 assert (rf != NULL);
1002                 rf->rf_type = RF_REMOVE;
1003
1004                 llentry_destroy (le);
1005
1006                 DEBUG ("plugin_unregister_read_group: "
1007                                 "Marked `%s' (group `%s') for removal.",
1008                                 rf->rf_name, group);
1009         }
1010
1011         pthread_mutex_unlock (&read_lock);
1012
1013         if (found == 0)
1014         {
1015                 WARNING ("plugin_unregister_read_group: No such "
1016                                 "group of read function: %s", group);
1017                 return (-ENOENT);
1018         }
1019
1020         return (0);
1021 } /* }}} int plugin_unregister_read_group */
1022
1023 int plugin_unregister_write (const char *name)
1024 {
1025         return (plugin_unregister (list_write, name));
1026 }
1027
1028 int plugin_unregister_flush (const char *name)
1029 {
1030         return (plugin_unregister (list_flush, name));
1031 }
1032
1033 int plugin_unregister_missing (const char *name)
1034 {
1035         return (plugin_unregister (list_missing, name));
1036 }
1037
1038 int plugin_unregister_shutdown (const char *name)
1039 {
1040         return (plugin_unregister (list_shutdown, name));
1041 }
1042
1043 int plugin_unregister_data_set (const char *name)
1044 {
1045         data_set_t *ds;
1046
1047         if (data_sets == NULL)
1048                 return (-1);
1049
1050         if (c_avl_remove (data_sets, name, NULL, (void *) &ds) != 0)
1051                 return (-1);
1052
1053         sfree (ds->ds);
1054         sfree (ds);
1055
1056         return (0);
1057 } /* int plugin_unregister_data_set */
1058
1059 int plugin_unregister_log (const char *name)
1060 {
1061         return (plugin_unregister (list_log, name));
1062 }
1063
1064 int plugin_unregister_notification (const char *name)
1065 {
1066         return (plugin_unregister (list_notification, name));
1067 }
1068
1069 void plugin_init_all (void)
1070 {
1071         const char *chain_name;
1072         llentry_t *le;
1073         int status;
1074
1075         /* Init the value cache */
1076         uc_init ();
1077
1078         chain_name = global_option_get ("PreCacheChain");
1079         pre_cache_chain = fc_chain_get_by_name (chain_name);
1080
1081         chain_name = global_option_get ("PostCacheChain");
1082         post_cache_chain = fc_chain_get_by_name (chain_name);
1083
1084
1085         if ((list_init == NULL) && (read_heap == NULL))
1086                 return;
1087
1088         /* Calling all init callbacks before checking if read callbacks
1089          * are available allows the init callbacks to register the read
1090          * callback. */
1091         le = llist_head (list_init);
1092         while (le != NULL)
1093         {
1094                 callback_func_t *cf;
1095                 plugin_init_cb callback;
1096
1097                 cf = le->value;
1098                 callback = cf->cf_callback;
1099                 status = (*callback) ();
1100
1101                 if (status != 0)
1102                 {
1103                         ERROR ("Initialization of plugin `%s' "
1104                                         "failed with status %i. "
1105                                         "Plugin will be unloaded.",
1106                                         le->key, status);
1107                         /* Plugins that register read callbacks from the init
1108                          * callback should take care of appropriate error
1109                          * handling themselves. */
1110                         /* FIXME: Unload _all_ functions */
1111                         plugin_unregister_read (le->key);
1112                 }
1113
1114                 le = le->next;
1115         }
1116
1117         /* Start read-threads */
1118         if (read_heap != NULL)
1119         {
1120                 const char *rt;
1121                 int num;
1122                 rt = global_option_get ("ReadThreads");
1123                 num = atoi (rt);
1124                 if (num != -1)
1125                         start_read_threads ((num > 0) ? num : 5);
1126         }
1127 } /* void plugin_init_all */
1128
1129 /* TODO: Rename this function. */
1130 void plugin_read_all (void)
1131 {
1132         uc_check_timeout ();
1133
1134         return;
1135 } /* void plugin_read_all */
1136
1137 /* Read function called when the `-T' command line argument is given. */
1138 int plugin_read_all_once (void)
1139 {
1140         int status;
1141         int return_status = 0;
1142
1143         if (read_heap == NULL)
1144         {
1145                 NOTICE ("No read-functions are registered.");
1146                 return (0);
1147         }
1148
1149         while (42)
1150         {
1151                 read_func_t *rf;
1152
1153                 rf = c_heap_get_root (read_heap);
1154                 if (rf == NULL)
1155                         break;
1156
1157                 if (rf->rf_type == RF_SIMPLE)
1158                 {
1159                         int (*callback) (void);
1160
1161                         callback = rf->rf_callback;
1162                         status = (*callback) ();
1163                 }
1164                 else
1165                 {
1166                         plugin_read_cb callback;
1167
1168                         callback = rf->rf_callback;
1169                         status = (*callback) (&rf->rf_udata);
1170                 }
1171
1172                 if (status != 0)
1173                 {
1174                         NOTICE ("read-function of plugin `%s' failed.",
1175                                         rf->rf_name);
1176                         return_status = -1;
1177                 }
1178
1179                 destroy_callback ((void *) rf);
1180         }
1181
1182         return (return_status);
1183 } /* int plugin_read_all_once */
1184
1185 int plugin_write (const char *plugin, /* {{{ */
1186                 const data_set_t *ds, const value_list_t *vl)
1187 {
1188   llentry_t *le;
1189   int status;
1190
1191   if (vl == NULL)
1192     return (EINVAL);
1193
1194   if (list_write == NULL)
1195     return (ENOENT);
1196
1197   if (ds == NULL)
1198   {
1199     ds = plugin_get_ds (vl->type);
1200     if (ds == NULL)
1201     {
1202       ERROR ("plugin_write: Unable to lookup type `%s'.", vl->type);
1203       return (ENOENT);
1204     }
1205   }
1206
1207   if (plugin == NULL)
1208   {
1209     int success = 0;
1210     int failure = 0;
1211
1212     le = llist_head (list_write);
1213     while (le != NULL)
1214     {
1215       callback_func_t *cf = le->value;
1216       plugin_write_cb callback;
1217
1218       DEBUG ("plugin: plugin_write: Writing values via %s.", le->key);
1219       callback = cf->cf_callback;
1220       status = (*callback) (ds, vl, &cf->cf_udata);
1221       if (status != 0)
1222         failure++;
1223       else
1224         success++;
1225
1226       le = le->next;
1227     }
1228
1229     if ((success == 0) && (failure != 0))
1230       status = -1;
1231     else
1232       status = 0;
1233   }
1234   else /* plugin != NULL */
1235   {
1236     callback_func_t *cf;
1237     plugin_write_cb callback;
1238
1239     le = llist_head (list_write);
1240     while (le != NULL)
1241     {
1242       if (strcasecmp (plugin, le->key) == 0)
1243         break;
1244
1245       le = le->next;
1246     }
1247
1248     if (le == NULL)
1249       return (ENOENT);
1250
1251     cf = le->value;
1252
1253     DEBUG ("plugin: plugin_write: Writing values via %s.", le->key);
1254     callback = cf->cf_callback;
1255     status = (*callback) (ds, vl, &cf->cf_udata);
1256   }
1257
1258   return (status);
1259 } /* }}} int plugin_write */
1260
1261 int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier)
1262 {
1263   llentry_t *le;
1264
1265   if (list_flush == NULL)
1266     return (0);
1267
1268   le = llist_head (list_flush);
1269   while (le != NULL)
1270   {
1271     callback_func_t *cf;
1272     plugin_flush_cb callback;
1273
1274     if ((plugin != NULL)
1275         && (strcmp (plugin, le->key) != 0))
1276     {
1277       le = le->next;
1278       continue;
1279     }
1280
1281     cf = le->value;
1282     callback = cf->cf_callback;
1283
1284     (*callback) (timeout, identifier, &cf->cf_udata);
1285
1286     le = le->next;
1287   }
1288   return (0);
1289 } /* int plugin_flush */
1290
1291 void plugin_shutdown_all (void)
1292 {
1293         llentry_t *le;
1294
1295         stop_read_threads ();
1296
1297         destroy_all_callbacks (&list_init);
1298
1299         pthread_mutex_lock (&read_lock);
1300         llist_destroy (read_list);
1301         read_list = NULL;
1302         pthread_mutex_unlock (&read_lock);
1303
1304         destroy_read_heap ();
1305
1306         plugin_flush (/* plugin = */ NULL,
1307                         /* timeout = */ 0,
1308                         /* identifier = */ NULL);
1309
1310         le = NULL;
1311         if (list_shutdown != NULL)
1312                 le = llist_head (list_shutdown);
1313
1314         while (le != NULL)
1315         {
1316                 callback_func_t *cf;
1317                 plugin_shutdown_cb callback;
1318
1319                 cf = le->value;
1320                 callback = cf->cf_callback;
1321
1322                 /* Advance the pointer before calling the callback allows
1323                  * shutdown functions to unregister themselves. If done the
1324                  * other way around the memory `le' points to will be freed
1325                  * after callback returns. */
1326                 le = le->next;
1327
1328                 (*callback) ();
1329         }
1330
1331         /* Write plugins which use the `user_data' pointer usually need the
1332          * same data available to the flush callback. If this is the case, set
1333          * the free_function to NULL when registering the flush callback and to
1334          * the real free function when registering the write callback. This way
1335          * the data isn't freed twice. */
1336         destroy_all_callbacks (&list_flush);
1337         destroy_all_callbacks (&list_missing);
1338         destroy_all_callbacks (&list_write);
1339
1340         destroy_all_callbacks (&list_notification);
1341         destroy_all_callbacks (&list_shutdown);
1342         destroy_all_callbacks (&list_log);
1343 } /* void plugin_shutdown_all */
1344
1345 int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
1346 {
1347   llentry_t *le;
1348
1349   if (list_missing == NULL)
1350     return (0);
1351
1352   le = llist_head (list_missing);
1353   while (le != NULL)
1354   {
1355     callback_func_t *cf;
1356     plugin_missing_cb callback;
1357     int status;
1358
1359     cf = le->value;
1360     callback = cf->cf_callback;
1361
1362     status = (*callback) (vl);
1363     if (status != 0)
1364     {
1365       if (status < 0)
1366       {
1367         ERROR ("plugin_dispatch_missing: Callback function \"%s\" "
1368             "failed with status %i.",
1369             le->key, status);
1370         return (status);
1371       }
1372       else
1373       {
1374         return (0);
1375       }
1376     }
1377
1378     le = le->next;
1379   }
1380   return (0);
1381 } /* int }}} plugin_dispatch_missing */
1382
1383 int plugin_dispatch_values (value_list_t *vl)
1384 {
1385         int status;
1386         static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC;
1387
1388         value_t *saved_values;
1389         int      saved_values_len;
1390
1391         data_set_t *ds;
1392
1393         int free_meta_data = 0;
1394
1395         if ((vl == NULL) || (vl->type[0] == 0)
1396                         || (vl->values == NULL) || (vl->values_len < 1))
1397         {
1398                 ERROR ("plugin_dispatch_values: Invalid value list.");
1399                 return (-1);
1400         }
1401
1402         /* Free meta data only if the calling function didn't specify any. In
1403          * this case matches and targets may add some and the calling function
1404          * may not expect (and therefore free) that data. */
1405         if (vl->meta == NULL)
1406                 free_meta_data = 1;
1407
1408         if (list_write == NULL)
1409                 c_complain_once (LOG_WARNING, &no_write_complaint,
1410                                 "plugin_dispatch_values: No write callback has been "
1411                                 "registered. Please load at least one output plugin, "
1412                                 "if you want the collected data to be stored.");
1413
1414         if (data_sets == NULL)
1415         {
1416                 ERROR ("plugin_dispatch_values: No data sets registered. "
1417                                 "Could the types database be read? Check "
1418                                 "your `TypesDB' setting!");
1419                 return (-1);
1420         }
1421
1422         if (c_avl_get (data_sets, vl->type, (void *) &ds) != 0)
1423         {
1424                 char ident[6 * DATA_MAX_NAME_LEN];
1425
1426                 FORMAT_VL (ident, sizeof (ident), vl);
1427                 INFO ("plugin_dispatch_values: Dataset not found: %s "
1428                                 "(from \"%s\"), check your types.db!",
1429                                 vl->type, ident);
1430                 return (-1);
1431         }
1432
1433         if (vl->time == 0)
1434                 vl->time = cdtime ();
1435
1436         if (vl->interval <= 0)
1437                 vl->interval = interval_g;
1438
1439         DEBUG ("plugin_dispatch_values: time = %.3f; interval = %.3f; "
1440                         "host = %s; "
1441                         "plugin = %s; plugin_instance = %s; "
1442                         "type = %s; type_instance = %s;",
1443                         CDTIME_T_TO_DOUBLE (vl->time),
1444                         CDTIME_T_TO_DOUBLE (vl->interval),
1445                         vl->host,
1446                         vl->plugin, vl->plugin_instance,
1447                         vl->type, vl->type_instance);
1448
1449 #if COLLECT_DEBUG
1450         assert (0 == strcmp (ds->type, vl->type));
1451 #else
1452         if (0 != strcmp (ds->type, vl->type))
1453                 WARNING ("plugin_dispatch_values: (ds->type = %s) != (vl->type = %s)",
1454                                 ds->type, vl->type);
1455 #endif
1456
1457 #if COLLECT_DEBUG
1458         assert (ds->ds_num == vl->values_len);
1459 #else
1460         if (ds->ds_num != vl->values_len)
1461         {
1462                 ERROR ("plugin_dispatch_values: ds->type = %s: "
1463                                 "(ds->ds_num = %i) != "
1464                                 "(vl->values_len = %i)",
1465                                 ds->type, ds->ds_num, vl->values_len);
1466                 return (-1);
1467         }
1468 #endif
1469
1470         escape_slashes (vl->host, sizeof (vl->host));
1471         escape_slashes (vl->plugin, sizeof (vl->plugin));
1472         escape_slashes (vl->plugin_instance, sizeof (vl->plugin_instance));
1473         escape_slashes (vl->type, sizeof (vl->type));
1474         escape_slashes (vl->type_instance, sizeof (vl->type_instance));
1475
1476         /* Copy the values. This way, we can assure `targets' that they get
1477          * dynamically allocated values, which they can free and replace if
1478          * they like. */
1479         if ((pre_cache_chain != NULL) || (post_cache_chain != NULL))
1480         {
1481                 saved_values     = vl->values;
1482                 saved_values_len = vl->values_len;
1483
1484                 vl->values = (value_t *) calloc (vl->values_len,
1485                                 sizeof (*vl->values));
1486                 if (vl->values == NULL)
1487                 {
1488                         ERROR ("plugin_dispatch_values: calloc failed.");
1489                         vl->values = saved_values;
1490                         return (-1);
1491                 }
1492                 memcpy (vl->values, saved_values,
1493                                 vl->values_len * sizeof (*vl->values));
1494         }
1495         else /* if ((pre == NULL) && (post == NULL)) */
1496         {
1497                 saved_values     = NULL;
1498                 saved_values_len = 0;
1499         }
1500
1501         if (pre_cache_chain != NULL)
1502         {
1503                 status = fc_process_chain (ds, vl, pre_cache_chain);
1504                 if (status < 0)
1505                 {
1506                         WARNING ("plugin_dispatch_values: Running the "
1507                                         "pre-cache chain failed with "
1508                                         "status %i (%#x).",
1509                                         status, status);
1510                 }
1511                 else if (status == FC_TARGET_STOP)
1512                 {
1513                         /* Restore the state of the value_list so that plugins
1514                          * don't get confused.. */
1515                         if (saved_values != NULL)
1516                         {
1517                                 free (vl->values);
1518                                 vl->values     = saved_values;
1519                                 vl->values_len = saved_values_len;
1520                         }
1521                         return (0);
1522                 }
1523         }
1524
1525         /* Update the value cache */
1526         uc_update (ds, vl);
1527
1528         /* Initiate threshold checking */
1529         ut_check_threshold (ds, vl);
1530
1531         if (post_cache_chain != NULL)
1532         {
1533                 status = fc_process_chain (ds, vl, post_cache_chain);
1534                 if (status < 0)
1535                 {
1536                         WARNING ("plugin_dispatch_values: Running the "
1537                                         "post-cache chain failed with "
1538                                         "status %i (%#x).",
1539                                         status, status);
1540                 }
1541         }
1542         else
1543                 fc_default_action (ds, vl);
1544
1545         /* Restore the state of the value_list so that plugins don't get
1546          * confused.. */
1547         if (saved_values != NULL)
1548         {
1549                 free (vl->values);
1550                 vl->values     = saved_values;
1551                 vl->values_len = saved_values_len;
1552         }
1553
1554         if ((free_meta_data != 0) && (vl->meta != NULL))
1555         {
1556                 meta_data_destroy (vl->meta);
1557                 vl->meta = NULL;
1558         }
1559
1560         return (0);
1561 } /* int plugin_dispatch_values */
1562
1563 int plugin_dispatch_notification (const notification_t *notif)
1564 {
1565         llentry_t *le;
1566         /* Possible TODO: Add flap detection here */
1567
1568         DEBUG ("plugin_dispatch_notification: severity = %i; message = %s; "
1569                         "time = %.3f; host = %s;",
1570                         notif->severity, notif->message,
1571                         CDTIME_T_TO_DOUBLE (notif->time), notif->host);
1572
1573         /* Nobody cares for notifications */
1574         if (list_notification == NULL)
1575                 return (-1);
1576
1577         le = llist_head (list_notification);
1578         while (le != NULL)
1579         {
1580                 callback_func_t *cf;
1581                 plugin_notification_cb callback;
1582                 int status;
1583
1584                 cf = le->value;
1585                 callback = cf->cf_callback;
1586                 status = (*callback) (notif, &cf->cf_udata);
1587                 if (status != 0)
1588                 {
1589                         WARNING ("plugin_dispatch_notification: Notification "
1590                                         "callback %s returned %i.",
1591                                         le->key, status);
1592                 }
1593
1594                 le = le->next;
1595         }
1596
1597         return (0);
1598 } /* int plugin_dispatch_notification */
1599
1600 void plugin_log (int level, const char *format, ...)
1601 {
1602         char msg[1024];
1603         va_list ap;
1604         llentry_t *le;
1605
1606 #if !COLLECT_DEBUG
1607         if (level >= LOG_DEBUG)
1608                 return;
1609 #endif
1610
1611         va_start (ap, format);
1612         vsnprintf (msg, sizeof (msg), format, ap);
1613         msg[sizeof (msg) - 1] = '\0';
1614         va_end (ap);
1615
1616         if (list_log == NULL)
1617         {
1618                 fprintf (stderr, "%s\n", msg);
1619                 return;
1620         }
1621
1622         le = llist_head (list_log);
1623         while (le != NULL)
1624         {
1625                 callback_func_t *cf;
1626                 plugin_log_cb callback;
1627
1628                 cf = le->value;
1629                 callback = cf->cf_callback;
1630
1631                 (*callback) (level, msg, &cf->cf_udata);
1632
1633                 le = le->next;
1634         }
1635 } /* void plugin_log */
1636
1637 const data_set_t *plugin_get_ds (const char *name)
1638 {
1639         data_set_t *ds;
1640
1641         if (c_avl_get (data_sets, name, (void *) &ds) != 0)
1642         {
1643                 DEBUG ("No such dataset registered: %s", name);
1644                 return (NULL);
1645         }
1646
1647         return (ds);
1648 } /* data_set_t *plugin_get_ds */
1649
1650 static int plugin_notification_meta_add (notification_t *n,
1651     const char *name,
1652     enum notification_meta_type_e type,
1653     const void *value)
1654 {
1655   notification_meta_t *meta;
1656   notification_meta_t *tail;
1657
1658   if ((n == NULL) || (name == NULL) || (value == NULL))
1659   {
1660     ERROR ("plugin_notification_meta_add: A pointer is NULL!");
1661     return (-1);
1662   }
1663
1664   meta = (notification_meta_t *) malloc (sizeof (notification_meta_t));
1665   if (meta == NULL)
1666   {
1667     ERROR ("plugin_notification_meta_add: malloc failed.");
1668     return (-1);
1669   }
1670   memset (meta, 0, sizeof (notification_meta_t));
1671
1672   sstrncpy (meta->name, name, sizeof (meta->name));
1673   meta->type = type;
1674
1675   switch (type)
1676   {
1677     case NM_TYPE_STRING:
1678     {
1679       meta->nm_value.nm_string = strdup ((const char *) value);
1680       if (meta->nm_value.nm_string == NULL)
1681       {
1682         ERROR ("plugin_notification_meta_add: strdup failed.");
1683         sfree (meta);
1684         return (-1);
1685       }
1686       break;
1687     }
1688     case NM_TYPE_SIGNED_INT:
1689     {
1690       meta->nm_value.nm_signed_int = *((int64_t *) value);
1691       break;
1692     }
1693     case NM_TYPE_UNSIGNED_INT:
1694     {
1695       meta->nm_value.nm_unsigned_int = *((uint64_t *) value);
1696       break;
1697     }
1698     case NM_TYPE_DOUBLE:
1699     {
1700       meta->nm_value.nm_double = *((double *) value);
1701       break;
1702     }
1703     case NM_TYPE_BOOLEAN:
1704     {
1705       meta->nm_value.nm_boolean = *((_Bool *) value);
1706       break;
1707     }
1708     default:
1709     {
1710       ERROR ("plugin_notification_meta_add: Unknown type: %i", type);
1711       sfree (meta);
1712       return (-1);
1713     }
1714   } /* switch (type) */
1715
1716   meta->next = NULL;
1717   tail = n->meta;
1718   while ((tail != NULL) && (tail->next != NULL))
1719     tail = tail->next;
1720
1721   if (tail == NULL)
1722     n->meta = meta;
1723   else
1724     tail->next = meta;
1725
1726   return (0);
1727 } /* int plugin_notification_meta_add */
1728
1729 int plugin_notification_meta_add_string (notification_t *n,
1730     const char *name,
1731     const char *value)
1732 {
1733   return (plugin_notification_meta_add (n, name, NM_TYPE_STRING, value));
1734 }
1735
1736 int plugin_notification_meta_add_signed_int (notification_t *n,
1737     const char *name,
1738     int64_t value)
1739 {
1740   return (plugin_notification_meta_add (n, name, NM_TYPE_SIGNED_INT, &value));
1741 }
1742
1743 int plugin_notification_meta_add_unsigned_int (notification_t *n,
1744     const char *name,
1745     uint64_t value)
1746 {
1747   return (plugin_notification_meta_add (n, name, NM_TYPE_UNSIGNED_INT, &value));
1748 }
1749
1750 int plugin_notification_meta_add_double (notification_t *n,
1751     const char *name,
1752     double value)
1753 {
1754   return (plugin_notification_meta_add (n, name, NM_TYPE_DOUBLE, &value));
1755 }
1756
1757 int plugin_notification_meta_add_boolean (notification_t *n,
1758     const char *name,
1759     _Bool value)
1760 {
1761   return (plugin_notification_meta_add (n, name, NM_TYPE_BOOLEAN, &value));
1762 }
1763
1764 int plugin_notification_meta_copy (notification_t *dst,
1765     const notification_t *src)
1766 {
1767   notification_meta_t *meta;
1768
1769   assert (dst != NULL);
1770   assert (src != NULL);
1771   assert (dst != src);
1772   assert ((src->meta == NULL) || (src->meta != dst->meta));
1773
1774   for (meta = src->meta; meta != NULL; meta = meta->next)
1775   {
1776     if (meta->type == NM_TYPE_STRING)
1777       plugin_notification_meta_add_string (dst, meta->name,
1778           meta->nm_value.nm_string);
1779     else if (meta->type == NM_TYPE_SIGNED_INT)
1780       plugin_notification_meta_add_signed_int (dst, meta->name,
1781           meta->nm_value.nm_signed_int);
1782     else if (meta->type == NM_TYPE_UNSIGNED_INT)
1783       plugin_notification_meta_add_unsigned_int (dst, meta->name,
1784           meta->nm_value.nm_unsigned_int);
1785     else if (meta->type == NM_TYPE_DOUBLE)
1786       plugin_notification_meta_add_double (dst, meta->name,
1787           meta->nm_value.nm_double);
1788     else if (meta->type == NM_TYPE_BOOLEAN)
1789       plugin_notification_meta_add_boolean (dst, meta->name,
1790           meta->nm_value.nm_boolean);
1791   }
1792
1793   return (0);
1794 } /* int plugin_notification_meta_copy */
1795
1796 int plugin_notification_meta_free (notification_meta_t *n)
1797 {
1798   notification_meta_t *this;
1799   notification_meta_t *next;
1800
1801   if (n == NULL)
1802   {
1803     ERROR ("plugin_notification_meta_free: n == NULL!");
1804     return (-1);
1805   }
1806
1807   this = n;
1808   while (this != NULL)
1809   {
1810     next = this->next;
1811
1812     if (this->type == NM_TYPE_STRING)
1813     {
1814       free ((char *)this->nm_value.nm_string);
1815       this->nm_value.nm_string = NULL;
1816     }
1817     sfree (this);
1818
1819     this = next;
1820   }
1821
1822   return (0);
1823 } /* int plugin_notification_meta_free */
1824
1825 /* vim: set sw=8 ts=8 noet fdm=marker : */