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