X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fplugin.c;h=6139baf0de05a2145ac5020d73d2bf64ef2814ae;hb=5a7145dd5626503f411a4aed87f30fc0f9689c90;hp=856ec1a01f89fbddadf744b81b7b31d5d202345b;hpb=c41ead0b33b4f3210c49591499ce8010172ae2cb;p=collectd.git diff --git a/src/plugin.c b/src/plugin.c index 856ec1a0..6139baf0 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -34,6 +34,7 @@ #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" @@ -57,10 +58,11 @@ struct read_func_s #define rf_callback rf_super.cf_callback #define rf_udata rf_super.cf_udata callback_func_t rf_super; + char rf_name[DATA_MAX_NAME_LEN]; int rf_type; - int rf_wait_time; - int rf_wait_left; - enum { DONE = 0, TODO = 1, ACTIVE = 2 } rf_needs_read; + struct timespec rf_interval; + struct timespec rf_effective_interval; + struct timespec rf_next_read; }; typedef struct read_func_s read_func_t; @@ -68,7 +70,6 @@ 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_shutdown; @@ -82,6 +83,7 @@ static c_avl_tree_t *data_sets; static char *plugindir = NULL; +static c_heap_t *read_heap = NULL; static int read_loop = 1; static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t read_cond = PTHREAD_COND_INITIALIZER; @@ -138,6 +140,26 @@ static void destroy_all_callbacks (llist_t **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_head_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) { @@ -196,14 +218,12 @@ static int register_callback (llist_t **list, /* {{{ */ static int create_register_callback (llist_t **list, /* {{{ */ const char *name, void *callback, user_data_t *ud) { - char *key; callback_func_t *cf; cf = (callback_func_t *) malloc (sizeof (*cf)); if (cf == NULL) { ERROR ("plugin: create_register_callback: malloc failed."); - sfree (key); return (-1); } memset (cf, 0, sizeof (*cf)); @@ -282,86 +302,138 @@ static int plugin_load_file (char *file) static void *plugin_read_thread (void __attribute__((unused)) *args) { - llentry_t *le; - read_func_t *rf; - int status; - int done; - - pthread_mutex_lock (&read_lock); - while (read_loop != 0) { - le = llist_head (list_read); - done = 0; + read_func_t *rf; + struct timeval now; + int status; - while ((read_loop != 0) && (le != NULL)) + /* Get the read function that needs to be read next. */ + rf = c_head_get_root (read_heap); + if (rf == NULL) { - rf = (read_func_t *) le->value; + struct timespec abstime; - if (rf->rf_needs_read != TODO) - { - le = le->next; - continue; - } + gettimeofday (&now, /* timezone = */ NULL); - /* We will do this read function */ - rf->rf_needs_read = ACTIVE; + abstime.tv_sec = now.tv_sec + interval_g; + abstime.tv_nsec = 1000 * now.tv_usec; - 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_type == RF_SIMPLE) - { - int (*callback) (void); + if ((rf->rf_interval.tv_sec == 0) && (rf->rf_interval.tv_nsec == 0)) + { + gettimeofday (&now, /* timezone = */ NULL); - callback = rf->rf_callback; - status = (*callback) (); - } - else - { - plugin_read_cb callback; + rf->rf_interval.tv_sec = interval_g; + rf->rf_interval.tv_nsec = 0; - callback = rf->rf_callback; - status = (*callback) (&rf->rf_udata); - } + rf->rf_effective_interval = rf->rf_interval; - done++; + rf->rf_next_read.tv_sec = now.tv_sec; + rf->rf_next_read.tv_nsec = 1000 * now.tv_usec; + } - if (status != 0) - { - if (rf->rf_wait_time < interval_g) - rf->rf_wait_time = interval_g; - rf->rf_wait_left = rf->rf_wait_time; - rf->rf_wait_time = rf->rf_wait_time * 2; - if (rf->rf_wait_time > 86400) - rf->rf_wait_time = 86400; - - NOTICE ("read-function of plugin `%s' " - "failed. Will suspend it for %i " - "seconds.", le->key, rf->rf_wait_left); - } - else + /* sleep until this entry is due, + * using pthread_cond_timedwait */ + pthread_mutex_lock (&read_lock); + pthread_cond_timedwait (&read_cond, &read_lock, + &rf->rf_next_read); + 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; + } + + DEBUG ("plugin_read_thread: Handling `%s'.", rf->rf_name); + + 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 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->rf_wait_left = 0; - rf->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->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 */ + gettimeofday (&now, /* timezone = */ NULL); + + 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 ((rf->rf_next_read.tv_sec < now.tv_sec) + || ((rf->rf_next_read.tv_sec == now.tv_sec) + && (rf->rf_next_read.tv_nsec < (1000 * now.tv_usec)))) { - 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. */ + rf->rf_next_read.tv_sec = now.tv_sec; + rf->rf_next_read.tv_nsec = 1000 * now.tv_usec; } - } /* 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); @@ -404,6 +476,8 @@ static void stop_read_threads (void) 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_read_threads: Signalling `read_cond'"); @@ -499,6 +573,7 @@ int plugin_load (const char *type) else if (!S_ISREG (statbuf.st_mode)) { /* don't follow symlinks */ + WARNING ("stat %s: not a regular file", filename); continue; } @@ -546,11 +621,42 @@ int plugin_register_init (const char *name, /* 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 */ + int plugin_register_read (const char *name, int (*callback) (void)) { read_func_t *rf; + if (read_heap == NULL) + { + read_heap = c_heap_create (plugin_compare_read_func); + if (read_heap == NULL) + { + ERROR ("plugin_register_read: " + "c_heap_create failed."); + return (-1); + } + } + rf = (read_func_t *) malloc (sizeof (read_func_t)); if (rf == NULL) { @@ -564,19 +670,32 @@ int plugin_register_read (const char *name, rf->rf_callback = (void *) callback; rf->rf_udata.data = NULL; rf->rf_udata.free_func = NULL; - rf->rf_wait_time = interval_g; - rf->rf_wait_left = 0; + sstrncpy (rf->rf_name, name, sizeof (rf->rf_name)); rf->rf_type = RF_SIMPLE; - rf->rf_needs_read = DONE; + rf->rf_interval.tv_sec = 0; + rf->rf_interval.tv_nsec = 0; + rf->rf_effective_interval = rf->rf_interval; - return (register_callback (&list_read, name, (callback_func_t *) rf)); + return (c_heap_insert (read_heap, rf)); } /* int plugin_register_read */ int plugin_register_complex_read (const char *name, - plugin_read_cb callback, user_data_t *user_data) + plugin_read_cb callback, + const struct timespec *interval, + user_data_t *user_data) { read_func_t *rf; + if (read_heap == NULL) + { + read_heap = c_heap_create (plugin_compare_read_func); + if (read_heap == NULL) + { + ERROR ("plugin_register_read: c_heap_create failed."); + return (-1); + } + } + rf = (read_func_t *) malloc (sizeof (read_func_t)); if (rf == NULL) { @@ -586,10 +705,13 @@ int plugin_register_complex_read (const char *name, memset (rf, 0, sizeof (read_func_t)); rf->rf_callback = (void *) callback; - rf->rf_wait_time = interval_g; - rf->rf_wait_left = 0; + sstrncpy (rf->rf_name, name, sizeof (rf->rf_name)); rf->rf_type = RF_COMPLEX; - rf->rf_needs_read = DONE; + if (interval != NULL) + { + rf->rf_interval = *interval; + } + rf->rf_effective_interval = rf->rf_interval; /* Set user data */ if (user_data == NULL) @@ -602,7 +724,7 @@ int plugin_register_complex_read (const char *name, rf->rf_udata = *user_data; } - return (register_callback (&list_read, name, (callback_func_t *) rf)); + return (c_heap_insert (read_heap, rf)); } /* int plugin_register_complex_read */ int plugin_register_write (const char *name, @@ -696,7 +818,9 @@ int plugin_unregister_init (const char *name) int plugin_unregister_read (const char *name) { - return (plugin_unregister (list_read, name)); + /* TODO: Implement removal of a specific key from the heap. */ + assert (0); + return (-1); } int plugin_unregister_write (const char *name) @@ -756,7 +880,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 @@ -789,7 +913,7 @@ void plugin_init_all (void) } /* Start read-threads */ - if (list_read != NULL) + if (read_heap != NULL) { const char *rt; int num; @@ -800,64 +924,33 @@ void plugin_init_all (void) } } /* 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->rf_needs_read != DONE) - { - le = le->next; - continue; - } - - if (rf->rf_wait_left > 0) - rf->rf_wait_left -= interval_g; - - if (rf->rf_wait_left <= 0) - { - rf->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; + read_func_t *rf; + + rf = c_head_get_root (read_heap); + if (rf == NULL) + break; if (rf->rf_type == RF_SIMPLE) { @@ -877,9 +970,11 @@ int plugin_read_all_once (void) 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); @@ -998,7 +1093,7 @@ void plugin_shutdown_all (void) stop_read_threads (); destroy_all_callbacks (&list_init); - destroy_all_callbacks (&list_read); + destroy_read_heap (); plugin_flush (/* plugin = */ NULL, /* timeout = */ -1, /* identifier = */ NULL); @@ -1036,9 +1131,14 @@ int plugin_dispatch_values (value_list_t *vl) int status; static c_complain_t no_write_complaint = C_COMPLAIN_INIT_STATIC; + value_t *saved_values; + int saved_values_len; + data_set_t *ds; - if ((vl == NULL) || (*vl->type == '\0')) { + if ((vl == NULL) || (vl->type[0] == 0) + || (vl->values == NULL) || (vl->values_len < 1)) + { ERROR ("plugin_dispatch_values: Invalid value list."); return (-1); } @@ -1066,6 +1166,9 @@ int plugin_dispatch_values (value_list_t *vl) if (vl->time == 0) vl->time = time (NULL); + if (vl->interval <= 0) + vl->interval = interval_g; + DEBUG ("plugin_dispatch_values: time = %u; interval = %i; " "host = %s; " "plugin = %s; plugin_instance = %s; " @@ -1102,6 +1205,31 @@ int plugin_dispatch_values (value_list_t *vl) escape_slashes (vl->type, sizeof (vl->type)); escape_slashes (vl->type_instance, sizeof (vl->type_instance)); + /* Copy the values. This way, we can assure `targets' that they get + * dynamically allocated values, which they can free and replace if + * they like. */ + if ((pre_cache_chain != NULL) || (post_cache_chain != NULL)) + { + saved_values = vl->values; + saved_values_len = vl->values_len; + + vl->values = (value_t *) calloc (vl->values_len, + sizeof (*vl->values)); + if (vl->values == NULL) + { + ERROR ("plugin_dispatch_values: calloc failed."); + vl->values = saved_values; + return (-1); + } + memcpy (vl->values, saved_values, + vl->values_len * sizeof (*vl->values)); + } + else /* if ((pre == NULL) && (post == NULL)) */ + { + saved_values = NULL; + saved_values_len = 0; + } + if (pre_cache_chain != NULL) { status = fc_process_chain (ds, vl, pre_cache_chain); @@ -1113,12 +1241,25 @@ int plugin_dispatch_values (value_list_t *vl) status, status); } else if (status == FC_TARGET_STOP) + { + /* Restore the state of the value_list so that plugins + * don't get confused.. */ + if (saved_values != NULL) + { + free (vl->values); + vl->values = saved_values; + vl->values_len = saved_values_len; + } return (0); + } } /* Update the value cache */ uc_update (ds, vl); + /* Initiate threshold checking */ + ut_check_threshold (ds, vl); + if (post_cache_chain != NULL) { status = fc_process_chain (ds, vl, post_cache_chain); @@ -1133,6 +1274,15 @@ int plugin_dispatch_values (value_list_t *vl) else fc_default_action (ds, vl); + /* Restore the state of the value_list so that plugins don't get + * confused.. */ + if (saved_values != NULL) + { + free (vl->values); + vl->values = saved_values; + vl->values_len = saved_values_len; + } + return (0); } /* int plugin_dispatch_values */