X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fplugin.c;h=0f360c0f3eb5a0f952440483da19ec9ead4fd3fd;hb=c4e824e456a30e1dc8f750a425472a0955f6b646;hp=74565c35821d11cdcd2c026e88ad5dc3a65d5ac5;hpb=52f41fed6e32f66f5817a82ae175fd443084f8a3;p=collectd.git diff --git a/src/plugin.c b/src/plugin.c index 74565c35..0f360c0f 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -1,6 +1,6 @@ /** * collectd - src/plugin.c - * Copyright (C) 2005-2009 Florian octo Forster + * Copyright (C) 2005-2010 Florian octo Forster * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster * Sebastian Harl **/ @@ -34,19 +34,36 @@ #include "configfile.h" #include "utils_avltree.h" #include "utils_llist.h" +#include "utils_heap.h" #include "utils_cache.h" -#include "utils_threshold.h" #include "filter_chain.h" /* * Private structures */ +struct callback_func_s +{ + void *cf_callback; + user_data_t cf_udata; +}; +typedef struct callback_func_s callback_func_t; + +#define RF_SIMPLE 0 +#define RF_COMPLEX 1 +#define RF_REMOVE 65535 struct read_func_s { - int wait_time; - int wait_left; - int (*callback) (void); - enum { DONE = 0, TODO = 1, ACTIVE = 2 } needs_read; + /* `read_func_t' "inherits" from `callback_func_t'. + * The `rf_super' member MUST be the first one in this structure! */ +#define rf_callback rf_super.cf_callback +#define rf_udata rf_super.cf_udata + callback_func_t rf_super; + char rf_group[DATA_MAX_NAME_LEN]; + char rf_name[DATA_MAX_NAME_LEN]; + int rf_type; + struct timespec rf_interval; + struct timespec rf_effective_interval; + struct timespec rf_next_read; }; typedef struct read_func_s read_func_t; @@ -54,9 +71,9 @@ typedef struct read_func_s read_func_t; * Private variables */ static llist_t *list_init; -static llist_t *list_read; static llist_t *list_write; static llist_t *list_flush; +static llist_t *list_missing; static llist_t *list_shutdown; static llist_t *list_log; static llist_t *list_notification; @@ -68,6 +85,8 @@ static c_avl_tree_t *data_sets; static char *plugindir = NULL; +static c_heap_t *read_heap = NULL; +static llist_t *read_list; static int read_loop = 1; static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t read_cond = PTHREAD_COND_INITIALIZER; @@ -85,26 +104,101 @@ static const char *plugin_get_dir (void) return (plugindir); } -static int register_callback (llist_t **list, const char *name, void *callback) +static void destroy_callback (callback_func_t *cf) /* {{{ */ +{ + if (cf == NULL) + return; + + if ((cf->cf_udata.data != NULL) && (cf->cf_udata.free_func != NULL)) + { + cf->cf_udata.free_func (cf->cf_udata.data); + cf->cf_udata.data = NULL; + cf->cf_udata.free_func = NULL; + } + sfree (cf); +} /* }}} void destroy_callback */ + +static void destroy_all_callbacks (llist_t **list) /* {{{ */ +{ + llentry_t *le; + + if (*list == NULL) + return; + + le = llist_head (*list); + while (le != NULL) + { + llentry_t *le_next; + + le_next = le->next; + + sfree (le->key); + destroy_callback (le->value); + le->value = NULL; + + le = le_next; + } + + llist_destroy (*list); + *list = NULL; +} /* }}} void destroy_all_callbacks */ + +static void destroy_read_heap (void) /* {{{ */ +{ + if (read_heap == NULL) + return; + + while (42) + { + callback_func_t *cf; + + cf = c_heap_get_root (read_heap); + if (cf == NULL) + break; + + destroy_callback (cf); + } + + c_heap_destroy (read_heap); + read_heap = NULL; +} /* }}} void destroy_read_heap */ + +static int register_callback (llist_t **list, /* {{{ */ + const char *name, callback_func_t *cf) { llentry_t *le; char *key; - if ((*list == NULL) - && ((*list = llist_create ()) == NULL)) + if (*list == NULL) + { + *list = llist_create (); + if (*list == NULL) + { + ERROR ("plugin: register_callback: " + "llist_create failed."); + destroy_callback (cf); + return (-1); + } + } + + key = strdup (name); + if (key == NULL) + { + ERROR ("plugin: register_callback: strdup failed."); + destroy_callback (cf); return (-1); + } le = llist_search (*list, name); if (le == NULL) { - key = strdup (name); - if (key == NULL) - return (-1); - - le = llentry_create (key, callback); + le = llentry_create (key, cf); if (le == NULL) { + ERROR ("plugin: register_callback: " + "llentry_create failed."); free (key); + destroy_callback (cf); return (-1); } @@ -112,34 +206,76 @@ static int register_callback (llist_t **list, const char *name, void *callback) } else { - le->value = callback; + callback_func_t *old_cf; + + old_cf = le->value; + le->value = cf; + + WARNING ("plugin: register_callback: " + "a callback named `%s' already exists - " + "overwriting the old entry!", name); + + destroy_callback (old_cf); + sfree (key); } return (0); -} /* int register_callback */ +} /* }}} int register_callback */ -static int plugin_unregister (llist_t *list, const char *name) +static int create_register_callback (llist_t **list, /* {{{ */ + const char *name, void *callback, user_data_t *ud) +{ + callback_func_t *cf; + + cf = (callback_func_t *) malloc (sizeof (*cf)); + if (cf == NULL) + { + ERROR ("plugin: create_register_callback: malloc failed."); + return (-1); + } + memset (cf, 0, sizeof (*cf)); + + cf->cf_callback = callback; + if (ud == NULL) + { + cf->cf_udata.data = NULL; + cf->cf_udata.free_func = NULL; + } + else + { + cf->cf_udata = *ud; + } + + return (register_callback (list, name, cf)); +} /* }}} int create_register_callback */ + +static int plugin_unregister (llist_t *list, const char *name) /* {{{ */ { llentry_t *e; - e = llist_search (list, name); + if (list == NULL) + return (-1); + e = llist_search (list, name); if (e == NULL) return (-1); llist_remove (list, e); - free (e->key); + + sfree (e->key); + destroy_callback (e->value); + llentry_destroy (e); return (0); -} /* int plugin_unregister */ +} /* }}} int plugin_unregister */ /* * (Try to) load the shared object `file'. Won't complain if it isn't a shared * object, but it will bitch about a shared object not having a * ``module_register'' symbol.. */ -static int plugin_load_file (char *file) +static int plugin_load_file (char *file, uint32_t flags) { lt_dlhandle dlh; void (*reg_handle) (void); @@ -149,7 +285,24 @@ static int plugin_load_file (char *file) lt_dlinit (); lt_dlerror (); /* clear errors */ - if ((dlh = lt_dlopen (file)) == NULL) +#if LIBTOOL_VERSION == 2 + if (flags & PLUGIN_FLAGS_GLOBAL) { + lt_dladvise advise; + lt_dladvise_init(&advise); + lt_dladvise_global(&advise); + dlh = lt_dlopenadvise(file, advise); + lt_dladvise_destroy(&advise); + } else { + dlh = lt_dlopen (file); + } +#else /* if LIBTOOL_VERSION == 1 */ + if (flags & PLUGIN_FLAGS_GLOBAL) + ERROR ("plugin_load_file: The global flag is not supported, " + "libtool 2 is required for this."); + dlh = lt_dlopen (file); +#endif + + if (dlh == NULL) { const char *error = lt_dlerror (); @@ -171,80 +324,177 @@ static int plugin_load_file (char *file) return (0); } -static void *plugin_read_thread (void __attribute__((unused)) *args) +static _Bool timeout_reached(struct timespec timeout) { - llentry_t *le; - read_func_t *rf; - int status; - int done; - - pthread_mutex_lock (&read_lock); + struct timeval now; + gettimeofday(&now, NULL); + return (now.tv_sec >= timeout.tv_sec && now.tv_usec >= (timeout.tv_nsec / 1000)); +} +static void *plugin_read_thread (void __attribute__((unused)) *args) +{ while (read_loop != 0) { - le = llist_head (list_read); - done = 0; - - while ((read_loop != 0) && (le != NULL)) + read_func_t *rf; + cdtime_t now; + int status; + int rf_type; + int rc; + + /* Get the read function that needs to be read next. */ + rf = c_heap_get_root (read_heap); + if (rf == NULL) { - rf = (read_func_t *) le->value; + struct timespec abstime; - if (rf->needs_read != TODO) - { - le = le->next; - continue; - } + now = cdtime (); - /* We will do this read function */ - rf->needs_read = ACTIVE; + CDTIME_T_TO_TIMESPEC (now + interval_g, &abstime); - DEBUG ("[thread #%5lu] plugin: plugin_read_thread: Handling %s", - (unsigned long int) pthread_self (), le->key); + pthread_mutex_lock (&read_lock); + pthread_cond_timedwait (&read_cond, &read_lock, + &abstime); pthread_mutex_unlock (&read_lock); + continue; + } + + if ((rf->rf_interval.tv_sec == 0) && (rf->rf_interval.tv_nsec == 0)) + { + now = cdtime (); - status = rf->callback (); - done++; + CDTIME_T_TO_TIMESPEC (interval_g, &rf->rf_interval); - if (status != 0) - { - if (rf->wait_time < interval_g) - rf->wait_time = interval_g; - rf->wait_left = rf->wait_time; - rf->wait_time = rf->wait_time * 2; - if (rf->wait_time > 86400) - rf->wait_time = 86400; - - NOTICE ("read-function of plugin `%s' " - "failed. Will suspend it for %i " - "seconds.", le->key, rf->wait_left); - } - else + rf->rf_effective_interval = rf->rf_interval; + + CDTIME_T_TO_TIMESPEC (now, &rf->rf_next_read); + } + + /* sleep until this entry is due, + * using pthread_cond_timedwait */ + pthread_mutex_lock (&read_lock); + /* In pthread_cond_timedwait, spurious wakeups are possible + * (and really happen, at least on NetBSD with > 1 CPU), thus + * we need to re-evaluate the condition every time + * pthread_cond_timedwait returns. */ + rc = 0; + while ((read_loop != 0) + && !timeout_reached(rf->rf_next_read) + && rc == 0) + { + rc = pthread_cond_timedwait (&read_cond, &read_lock, + &rf->rf_next_read); + } + + /* Must hold `read_lock' when accessing `rf->rf_type'. */ + rf_type = rf->rf_type; + pthread_mutex_unlock (&read_lock); + + /* Check if we're supposed to stop.. This may have interrupted + * the sleep, too. */ + if (read_loop == 0) + { + /* Insert `rf' again, so it can be free'd correctly */ + c_heap_insert (read_heap, rf); + break; + } + + /* The entry has been marked for deletion. The linked list + * entry has already been removed by `plugin_unregister_read'. + * All we have to do here is free the `read_func_t' and + * continue. */ + if (rf_type == RF_REMOVE) + { + DEBUG ("plugin_read_thread: Destroying the `%s' " + "callback.", rf->rf_name); + destroy_callback ((callback_func_t *) rf); + rf = NULL; + continue; + } + + DEBUG ("plugin_read_thread: Handling `%s'.", rf->rf_name); + + if (rf_type == RF_SIMPLE) + { + int (*callback) (void); + + callback = rf->rf_callback; + status = (*callback) (); + } + else + { + plugin_read_cb callback; + + assert (rf_type == RF_COMPLEX); + + callback = rf->rf_callback; + status = (*callback) (&rf->rf_udata); + } + + /* If the function signals failure, we will increase the + * intervals in which it will be called. */ + if (status != 0) + { + rf->rf_effective_interval.tv_sec *= 2; + rf->rf_effective_interval.tv_nsec *= 2; + NORMALIZE_TIMESPEC (rf->rf_effective_interval); + + if (rf->rf_effective_interval.tv_sec >= 86400) { - rf->wait_left = 0; - rf->wait_time = interval_g; + rf->rf_effective_interval.tv_sec = 86400; + rf->rf_effective_interval.tv_nsec = 0; } - pthread_mutex_lock (&read_lock); - - rf->needs_read = DONE; - le = le->next; - } /* while (le != NULL) */ + NOTICE ("read-function of plugin `%s' failed. " + "Will suspend it for %i seconds.", + rf->rf_name, + (int) rf->rf_effective_interval.tv_sec); + } + else + { + /* Success: Restore the interval, if it was changed. */ + rf->rf_effective_interval = rf->rf_interval; + } - if ((read_loop != 0) && (done == 0)) + /* update the ``next read due'' field */ + now = cdtime (); + + DEBUG ("plugin_read_thread: Effective interval of the " + "%s plugin is %i.%09i.", + rf->rf_name, + (int) rf->rf_effective_interval.tv_sec, + (int) rf->rf_effective_interval.tv_nsec); + + /* Calculate the next (absolute) time at which this function + * should be called. */ + rf->rf_next_read.tv_sec = rf->rf_next_read.tv_sec + + rf->rf_effective_interval.tv_sec; + rf->rf_next_read.tv_nsec = rf->rf_next_read.tv_nsec + + rf->rf_effective_interval.tv_nsec; + NORMALIZE_TIMESPEC (rf->rf_next_read); + + /* Check, if `rf_next_read' is in the past. */ + if (TIMESPEC_TO_CDTIME_T (&rf->rf_next_read) < now) { - DEBUG ("[thread #%5lu] plugin: plugin_read_thread: Waiting on read_cond.", - (unsigned long int) pthread_self ()); - pthread_cond_wait (&read_cond, &read_lock); + /* `rf_next_read' is in the past. Insert `now' + * so this value doesn't trail off into the + * past too much. */ + CDTIME_T_TO_TIMESPEC (now, &rf->rf_next_read); } - } /* while (read_loop) */ - pthread_mutex_unlock (&read_lock); + DEBUG ("plugin_read_thread: Next read of the %s plugin at %i.%09i.", + rf->rf_name, + (int) rf->rf_next_read.tv_sec, + (int) rf->rf_next_read.tv_nsec); + + /* Re-insert this read function into the heap again. */ + c_heap_insert (read_heap, rf); + } /* while (read_loop) */ pthread_exit (NULL); return ((void *) 0); } /* void *plugin_read_thread */ -static void start_threads (int num) +static void start_read_threads (int num) { int i; @@ -254,7 +504,7 @@ static void start_threads (int num) read_threads = (pthread_t *) calloc (num, sizeof (pthread_t)); if (read_threads == NULL) { - ERROR ("plugin: start_threads: calloc failed."); + ERROR ("plugin: start_read_threads: calloc failed."); return; } @@ -268,22 +518,24 @@ static void start_threads (int num) } else { - ERROR ("plugin: start_threads: pthread_create failed."); + ERROR ("plugin: start_read_threads: pthread_create failed."); return; } } /* for (i) */ -} /* void start_threads */ +} /* void start_read_threads */ -static void stop_threads (void) +static void stop_read_threads (void) { int i; if (read_threads == NULL) return; + INFO ("collectd: Stopping %i read threads.", read_threads_num); + pthread_mutex_lock (&read_lock); read_loop = 0; - DEBUG ("plugin: stop_threads: Signalling `read_cond'"); + DEBUG ("plugin: stop_read_threads: Signalling `read_cond'"); pthread_cond_broadcast (&read_cond); pthread_mutex_unlock (&read_lock); @@ -291,13 +543,13 @@ static void stop_threads (void) { if (pthread_join (read_threads[i], NULL) != 0) { - ERROR ("plugin: stop_threads: pthread_join failed."); + ERROR ("plugin: stop_read_threads: pthread_join failed."); } read_threads[i] = (pthread_t) 0; } sfree (read_threads); read_threads_num = 0; -} /* void stop_threads */ +} /* void stop_read_threads */ /* * Public functions @@ -318,7 +570,7 @@ void plugin_set_dir (const char *dir) } #define BUFSIZE 512 -int plugin_load (const char *type) +int plugin_load (const char *type, uint32_t flags) { DIR *dh; const char *dir; @@ -380,7 +632,7 @@ int plugin_load (const char *type) continue; } - if (plugin_load_file (filename) == 0) + if (plugin_load_file (filename, flags) == 0) { /* success */ ret = 0; @@ -420,9 +672,86 @@ int plugin_register_complex_config (const char *type, int plugin_register_init (const char *name, int (*callback) (void)) { - return (register_callback (&list_init, name, (void *) callback)); + return (create_register_callback (&list_init, name, (void *) callback, + /* user_data = */ NULL)); } /* plugin_register_init */ +static int plugin_compare_read_func (const void *arg0, const void *arg1) +{ + const read_func_t *rf0; + const read_func_t *rf1; + + rf0 = arg0; + rf1 = arg1; + + if (rf0->rf_next_read.tv_sec < rf1->rf_next_read.tv_sec) + return (-1); + else if (rf0->rf_next_read.tv_sec > rf1->rf_next_read.tv_sec) + return (1); + else if (rf0->rf_next_read.tv_nsec < rf1->rf_next_read.tv_nsec) + return (-1); + else if (rf0->rf_next_read.tv_nsec > rf1->rf_next_read.tv_nsec) + return (1); + else + return (0); +} /* int plugin_compare_read_func */ + +/* Add a read function to both, the heap and a linked list. The linked list if + * used to look-up read functions, especially for the remove function. The heap + * is used to determine which plugin to read next. */ +static int plugin_insert_read (read_func_t *rf) +{ + int status; + llentry_t *le; + + pthread_mutex_lock (&read_lock); + + if (read_list == NULL) + { + read_list = llist_create (); + if (read_list == NULL) + { + pthread_mutex_unlock (&read_lock); + ERROR ("plugin_insert_read: read_list failed."); + return (-1); + } + } + + if (read_heap == NULL) + { + read_heap = c_heap_create (plugin_compare_read_func); + if (read_heap == NULL) + { + pthread_mutex_unlock (&read_lock); + ERROR ("plugin_insert_read: c_heap_create failed."); + return (-1); + } + } + + le = llentry_create (rf->rf_name, rf); + if (le == NULL) + { + pthread_mutex_unlock (&read_lock); + ERROR ("plugin_insert_read: llentry_create failed."); + return (-1); + } + + status = c_heap_insert (read_heap, rf); + if (status != 0) + { + pthread_mutex_unlock (&read_lock); + ERROR ("plugin_insert_read: c_heap_insert failed."); + llentry_destroy (le); + return (-1); + } + + /* This does not fail. */ + llist_append (read_list, le); + + pthread_mutex_unlock (&read_lock); + return (0); +} /* int plugin_insert_read */ + int plugin_register_read (const char *name, int (*callback) (void)) { @@ -437,31 +766,88 @@ int plugin_register_read (const char *name, return (-1); } - memset (rf, '\0', sizeof (read_func_t)); - rf->wait_time = interval_g; - rf->wait_left = 0; - rf->callback = callback; - rf->needs_read = DONE; - - return (register_callback (&list_read, name, (void *) rf)); + memset (rf, 0, sizeof (read_func_t)); + rf->rf_callback = (void *) callback; + rf->rf_udata.data = NULL; + rf->rf_udata.free_func = NULL; + rf->rf_group[0] = '\0'; + sstrncpy (rf->rf_name, name, sizeof (rf->rf_name)); + rf->rf_type = RF_SIMPLE; + rf->rf_interval.tv_sec = 0; + rf->rf_interval.tv_nsec = 0; + rf->rf_effective_interval = rf->rf_interval; + + return (plugin_insert_read (rf)); } /* int plugin_register_read */ +int plugin_register_complex_read (const char *group, const char *name, + plugin_read_cb callback, + const struct timespec *interval, + user_data_t *user_data) +{ + read_func_t *rf; + + rf = (read_func_t *) malloc (sizeof (read_func_t)); + if (rf == NULL) + { + ERROR ("plugin_register_complex_read: malloc failed."); + return (-1); + } + + memset (rf, 0, sizeof (read_func_t)); + rf->rf_callback = (void *) callback; + if (group != NULL) + sstrncpy (rf->rf_group, group, sizeof (rf->rf_group)); + else + rf->rf_group[0] = '\0'; + sstrncpy (rf->rf_name, name, sizeof (rf->rf_name)); + rf->rf_type = RF_COMPLEX; + if (interval != NULL) + { + rf->rf_interval = *interval; + } + rf->rf_effective_interval = rf->rf_interval; + + /* Set user data */ + if (user_data == NULL) + { + rf->rf_udata.data = NULL; + rf->rf_udata.free_func = NULL; + } + else + { + rf->rf_udata = *user_data; + } + + return (plugin_insert_read (rf)); +} /* int plugin_register_complex_read */ + int plugin_register_write (const char *name, - int (*callback) (const data_set_t *ds, const value_list_t *vl)) + plugin_write_cb callback, user_data_t *ud) { - return (register_callback (&list_write, name, (void *) callback)); + return (create_register_callback (&list_write, name, + (void *) callback, ud)); } /* int plugin_register_write */ int plugin_register_flush (const char *name, - int (*callback) (const int timeout, const char *identifier)) + plugin_flush_cb callback, user_data_t *ud) { - return (register_callback (&list_flush, name, (void *) callback)); + return (create_register_callback (&list_flush, name, + (void *) callback, ud)); } /* int plugin_register_flush */ +int plugin_register_missing (const char *name, + plugin_missing_cb callback, user_data_t *ud) +{ + return (create_register_callback (&list_missing, name, + (void *) callback, ud)); +} /* int plugin_register_missing */ + int plugin_register_shutdown (char *name, int (*callback) (void)) { - return (register_callback (&list_shutdown, name, (void *) callback)); + return (create_register_callback (&list_shutdown, name, + (void *) callback, /* user_data = */ NULL)); } /* int plugin_register_shutdown */ int plugin_register_data_set (const data_set_t *ds) @@ -501,16 +887,18 @@ int plugin_register_data_set (const data_set_t *ds) return (c_avl_insert (data_sets, (void *) ds_copy->type, (void *) ds_copy)); } /* int plugin_register_data_set */ -int plugin_register_log (char *name, - void (*callback) (int priority, const char *msg)) +int plugin_register_log (const char *name, + plugin_log_cb callback, user_data_t *ud) { - return (register_callback (&list_log, name, (void *) callback)); + return (create_register_callback (&list_log, name, + (void *) callback, ud)); } /* int plugin_register_log */ int plugin_register_notification (const char *name, - int (*callback) (const notification_t *notif)) + plugin_notification_cb callback, user_data_t *ud) { - return (register_callback (&list_notification, name, (void *) callback)); + return (create_register_callback (&list_notification, name, + (void *) callback, ud)); } /* int plugin_register_log */ int plugin_unregister_config (const char *name) @@ -530,22 +918,106 @@ int plugin_unregister_init (const char *name) return (plugin_unregister (list_init, name)); } -int plugin_unregister_read (const char *name) +int plugin_unregister_read (const char *name) /* {{{ */ { - llentry_t *e; + llentry_t *le; + read_func_t *rf; - e = llist_search (list_read, name); + if (name == NULL) + return (-ENOENT); - if (e == NULL) - return (-1); + pthread_mutex_lock (&read_lock); - llist_remove (list_read, e); - free (e->value); - free (e->key); - llentry_destroy (e); + if (read_list == NULL) + { + pthread_mutex_unlock (&read_lock); + return (-ENOENT); + } + + le = llist_search (read_list, name); + if (le == NULL) + { + pthread_mutex_unlock (&read_lock); + WARNING ("plugin_unregister_read: No such read function: %s", + name); + return (-ENOENT); + } + + llist_remove (read_list, le); + + rf = le->value; + assert (rf != NULL); + rf->rf_type = RF_REMOVE; + + pthread_mutex_unlock (&read_lock); + + llentry_destroy (le); + + DEBUG ("plugin_unregister_read: Marked `%s' for removal.", name); return (0); -} +} /* }}} int plugin_unregister_read */ + +static int compare_read_func_group (llentry_t *e, void *ud) /* {{{ */ +{ + read_func_t *rf = e->value; + char *group = ud; + + return strcmp (rf->rf_group, (const char *)group); +} /* }}} int compare_read_func_group */ + +int plugin_unregister_read_group (const char *group) /* {{{ */ +{ + llentry_t *le; + read_func_t *rf; + + int found = 0; + + if (group == NULL) + return (-ENOENT); + + pthread_mutex_lock (&read_lock); + + if (read_list == NULL) + { + pthread_mutex_unlock (&read_lock); + return (-ENOENT); + } + + while (42) + { + le = llist_search_custom (read_list, + compare_read_func_group, (void *)group); + + if (le == NULL) + break; + + ++found; + + llist_remove (read_list, le); + + rf = le->value; + assert (rf != NULL); + rf->rf_type = RF_REMOVE; + + llentry_destroy (le); + + DEBUG ("plugin_unregister_read_group: " + "Marked `%s' (group `%s') for removal.", + rf->rf_name, group); + } + + pthread_mutex_unlock (&read_lock); + + if (found == 0) + { + WARNING ("plugin_unregister_read_group: No such " + "group of read function: %s", group); + return (-ENOENT); + } + + return (0); +} /* }}} int plugin_unregister_read_group */ int plugin_unregister_write (const char *name) { @@ -557,6 +1029,11 @@ int plugin_unregister_flush (const char *name) return (plugin_unregister (list_flush, name)); } +int plugin_unregister_missing (const char *name) +{ + return (plugin_unregister (list_missing, name)); +} + int plugin_unregister_shutdown (const char *name) { return (plugin_unregister (list_shutdown, name)); @@ -591,7 +1068,6 @@ int plugin_unregister_notification (const char *name) void plugin_init_all (void) { const char *chain_name; - int (*callback) (void); llentry_t *le; int status; @@ -605,7 +1081,7 @@ void plugin_init_all (void) post_cache_chain = fc_chain_get_by_name (chain_name); - if ((list_init == NULL) && (list_read == NULL)) + if ((list_init == NULL) && (read_heap == NULL)) return; /* Calling all init callbacks before checking if read callbacks @@ -614,7 +1090,11 @@ void plugin_init_all (void) le = llist_head (list_init); while (le != NULL) { - callback = (int (*) (void)) le->value; + callback_func_t *cf; + plugin_init_cb callback; + + cf = le->value; + callback = cf->cf_callback; status = (*callback) (); if (status != 0) @@ -634,82 +1114,68 @@ void plugin_init_all (void) } /* Start read-threads */ - if (list_read != NULL) + if (read_heap != NULL) { const char *rt; int num; rt = global_option_get ("ReadThreads"); num = atoi (rt); if (num != -1) - start_threads ((num > 0) ? num : 5); + start_read_threads ((num > 0) ? num : 5); } } /* void plugin_init_all */ +/* TODO: Rename this function. */ void plugin_read_all (void) { - llentry_t *le; - read_func_t *rf; - uc_check_timeout (); - if (list_read == NULL) - return; - - pthread_mutex_lock (&read_lock); - - le = llist_head (list_read); - while (le != NULL) - { - rf = (read_func_t *) le->value; - - if (rf->needs_read != DONE) - { - le = le->next; - continue; - } - - if (rf->wait_left > 0) - rf->wait_left -= interval_g; - - if (rf->wait_left <= 0) - { - rf->needs_read = TODO; - } - - le = le->next; - } - - DEBUG ("plugin: plugin_read_all: Signalling `read_cond'"); - pthread_cond_broadcast (&read_cond); - pthread_mutex_unlock (&read_lock); + return; } /* void plugin_read_all */ /* Read function called when the `-T' command line argument is given. */ int plugin_read_all_once (void) { - llentry_t *le; - read_func_t *rf; int status; int return_status = 0; - if (list_read == NULL) + if (read_heap == NULL) { NOTICE ("No read-functions are registered."); return (0); } - for (le = llist_head (list_read); - le != NULL; - le = le->next) + while (42) { - rf = (read_func_t *) le->value; - status = rf->callback (); + read_func_t *rf; + + rf = c_heap_get_root (read_heap); + if (rf == NULL) + break; + + if (rf->rf_type == RF_SIMPLE) + { + int (*callback) (void); + + callback = rf->rf_callback; + status = (*callback) (); + } + else + { + plugin_read_cb callback; + + callback = rf->rf_callback; + status = (*callback) (&rf->rf_udata); + } + if (status != 0) { NOTICE ("read-function of plugin `%s' failed.", - le->key); + rf->rf_name); return_status = -1; } + + destroy_callback ((void *) rf); } return (return_status); @@ -718,7 +1184,6 @@ int plugin_read_all_once (void) int plugin_write (const char *plugin, /* {{{ */ const data_set_t *ds, const value_list_t *vl) { - int (*callback) (const data_set_t *ds, const value_list_t *vl); llentry_t *le; int status; @@ -746,8 +1211,12 @@ int plugin_write (const char *plugin, /* {{{ */ le = llist_head (list_write); while (le != NULL) { - callback = le->value; - status = (*callback) (ds, vl); + callback_func_t *cf = le->value; + plugin_write_cb callback; + + DEBUG ("plugin: plugin_write: Writing values via %s.", le->key); + callback = cf->cf_callback; + status = (*callback) (ds, vl, &cf->cf_udata); if (status != 0) failure++; else @@ -763,6 +1232,9 @@ int plugin_write (const char *plugin, /* {{{ */ } else /* plugin != NULL */ { + callback_func_t *cf; + plugin_write_cb callback; + le = llist_head (list_write); while (le != NULL) { @@ -775,16 +1247,18 @@ int plugin_write (const char *plugin, /* {{{ */ if (le == NULL) return (ENOENT); - callback = le->value; - status = (*callback) (ds, vl); + cf = le->value; + + DEBUG ("plugin: plugin_write: Writing values via %s.", le->key); + callback = cf->cf_callback; + status = (*callback) (ds, vl, &cf->cf_udata); } return (status); } /* }}} int plugin_write */ -int plugin_flush (const char *plugin, int timeout, const char *identifier) +int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier) { - int (*callback) (int timeout, const char *identifier); llentry_t *le; if (list_flush == NULL) @@ -793,6 +1267,9 @@ int plugin_flush (const char *plugin, int timeout, const char *identifier) le = llist_head (list_flush); while (le != NULL) { + callback_func_t *cf; + plugin_flush_cb callback; + if ((plugin != NULL) && (strcmp (plugin, le->key) != 0)) { @@ -800,8 +1277,10 @@ int plugin_flush (const char *plugin, int timeout, const char *identifier) continue; } - callback = (int (*) (int, const char *)) le->value; - (*callback) (timeout, identifier); + cf = le->value; + callback = cf->cf_callback; + + (*callback) (timeout, identifier, &cf->cf_udata); le = le->next; } @@ -810,18 +1289,34 @@ int plugin_flush (const char *plugin, int timeout, const char *identifier) void plugin_shutdown_all (void) { - int (*callback) (void); llentry_t *le; - stop_threads (); + stop_read_threads (); - if (list_shutdown == NULL) - return; + destroy_all_callbacks (&list_init); + + pthread_mutex_lock (&read_lock); + llist_destroy (read_list); + read_list = NULL; + pthread_mutex_unlock (&read_lock); + + destroy_read_heap (); + + plugin_flush (/* plugin = */ NULL, + /* timeout = */ 0, + /* identifier = */ NULL); + + le = NULL; + if (list_shutdown != NULL) + le = llist_head (list_shutdown); - le = llist_head (list_shutdown); while (le != NULL) { - callback = (int (*) (void)) le->value; + callback_func_t *cf; + plugin_shutdown_cb callback; + + cf = le->value; + callback = cf->cf_callback; /* Advance the pointer before calling the callback allows * shutdown functions to unregister themselves. If done the @@ -831,8 +1326,59 @@ void plugin_shutdown_all (void) (*callback) (); } + + /* Write plugins which use the `user_data' pointer usually need the + * same data available to the flush callback. If this is the case, set + * the free_function to NULL when registering the flush callback and to + * the real free function when registering the write callback. This way + * the data isn't freed twice. */ + destroy_all_callbacks (&list_flush); + destroy_all_callbacks (&list_missing); + destroy_all_callbacks (&list_write); + + destroy_all_callbacks (&list_notification); + destroy_all_callbacks (&list_shutdown); + destroy_all_callbacks (&list_log); } /* void plugin_shutdown_all */ +int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */ +{ + llentry_t *le; + + if (list_missing == NULL) + return (0); + + le = llist_head (list_missing); + while (le != NULL) + { + callback_func_t *cf; + plugin_missing_cb callback; + int status; + + cf = le->value; + callback = cf->cf_callback; + + status = (*callback) (vl, &cf->cf_udata); + if (status != 0) + { + if (status < 0) + { + ERROR ("plugin_dispatch_missing: Callback function \"%s\" " + "failed with status %i.", + le->key, status); + return (status); + } + else + { + return (0); + } + } + + le = le->next; + } + return (0); +} /* int }}} plugin_dispatch_missing */ + int plugin_dispatch_values (value_list_t *vl) { int status; @@ -843,6 +1389,8 @@ int plugin_dispatch_values (value_list_t *vl) data_set_t *ds; + int free_meta_data = 0; + if ((vl == NULL) || (vl->type[0] == 0) || (vl->values == NULL) || (vl->values_len < 1)) { @@ -850,6 +1398,12 @@ int plugin_dispatch_values (value_list_t *vl) return (-1); } + /* Free meta data only if the calling function didn't specify any. In + * this case matches and targets may add some and the calling function + * may not expect (and therefore free) that data. */ + if (vl->meta == NULL) + free_meta_data = 1; + if (list_write == NULL) c_complain_once (LOG_WARNING, &no_write_complaint, "plugin_dispatch_values: No write callback has been " @@ -866,18 +1420,27 @@ int plugin_dispatch_values (value_list_t *vl) if (c_avl_get (data_sets, vl->type, (void *) &ds) != 0) { - INFO ("plugin_dispatch_values: Dataset not found: %s", vl->type); + char ident[6 * DATA_MAX_NAME_LEN]; + + FORMAT_VL (ident, sizeof (ident), vl); + INFO ("plugin_dispatch_values: Dataset not found: %s " + "(from \"%s\"), check your types.db!", + vl->type, ident); return (-1); } if (vl->time == 0) - vl->time = time (NULL); + vl->time = cdtime (); - DEBUG ("plugin_dispatch_values: time = %u; interval = %i; " + if (vl->interval <= 0) + vl->interval = interval_g; + + DEBUG ("plugin_dispatch_values: time = %.3f; interval = %.3f; " "host = %s; " "plugin = %s; plugin_instance = %s; " "type = %s; type_instance = %s;", - (unsigned int) vl->time, vl->interval, + CDTIME_T_TO_DOUBLE (vl->time), + CDTIME_T_TO_DOUBLE (vl->interval), vl->host, vl->plugin, vl->plugin_instance, vl->type, vl->type_instance); @@ -984,19 +1547,24 @@ int plugin_dispatch_values (value_list_t *vl) vl->values_len = saved_values_len; } + if ((free_meta_data != 0) && (vl->meta != NULL)) + { + meta_data_destroy (vl->meta); + vl->meta = NULL; + } + return (0); } /* int plugin_dispatch_values */ int plugin_dispatch_notification (const notification_t *notif) { - int (*callback) (const notification_t *); llentry_t *le; /* Possible TODO: Add flap detection here */ DEBUG ("plugin_dispatch_notification: severity = %i; message = %s; " - "time = %u; host = %s;", + "time = %.3f; host = %s;", notif->severity, notif->message, - (unsigned int) notif->time, notif->host); + CDTIME_T_TO_DOUBLE (notif->time), notif->host); /* Nobody cares for notifications */ if (list_notification == NULL) @@ -1005,8 +1573,19 @@ int plugin_dispatch_notification (const notification_t *notif) le = llist_head (list_notification); while (le != NULL) { - callback = (int (*) (const notification_t *)) le->value; - (*callback) (notif); + callback_func_t *cf; + plugin_notification_cb callback; + int status; + + cf = le->value; + callback = cf->cf_callback; + status = (*callback) (notif, &cf->cf_udata); + if (status != 0) + { + WARNING ("plugin_dispatch_notification: Notification " + "callback %s returned %i.", + le->key, status); + } le = le->next; } @@ -1018,13 +1597,8 @@ void plugin_log (int level, const char *format, ...) { char msg[1024]; va_list ap; - - void (*callback) (int, const char *); llentry_t *le; - if (list_log == NULL) - return; - #if !COLLECT_DEBUG if (level >= LOG_DEBUG) return; @@ -1035,11 +1609,22 @@ void plugin_log (int level, const char *format, ...) msg[sizeof (msg) - 1] = '\0'; va_end (ap); + if (list_log == NULL) + { + fprintf (stderr, "%s\n", msg); + return; + } + le = llist_head (list_log); while (le != NULL) { - callback = (void (*) (int, const char *)) le->value; - (*callback) (level, msg); + callback_func_t *cf; + plugin_log_cb callback; + + cf = le->value; + callback = cf->cf_callback; + + (*callback) (level, msg, &cf->cf_udata); le = le->next; } @@ -1113,7 +1698,7 @@ static int plugin_notification_meta_add (notification_t *n, } case NM_TYPE_BOOLEAN: { - meta->nm_value.nm_boolean = *((bool *) value); + meta->nm_value.nm_boolean = *((_Bool *) value); break; } default: @@ -1167,7 +1752,7 @@ int plugin_notification_meta_add_double (notification_t *n, int plugin_notification_meta_add_boolean (notification_t *n, const char *name, - bool value) + _Bool value) { return (plugin_notification_meta_add (n, name, NM_TYPE_BOOLEAN, &value)); } @@ -1232,3 +1817,5 @@ int plugin_notification_meta_free (notification_meta_t *n) return (0); } /* int plugin_notification_meta_free */ + +/* vim: set sw=8 ts=8 noet fdm=marker : */