Merge branch 'collectd-5.5' into collectd-5.6
[collectd.git] / src / daemon / plugin.c
index a8e6ca1..c6efc3f 100644 (file)
@@ -26,6 +26,7 @@
  **/
 
 #include "collectd.h"
+
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
@@ -80,6 +81,12 @@ struct write_queue_s
        write_queue_t *next;
 };
 
+struct flush_callback_s {
+       char *name;
+       cdtime_t timeout;
+};
+typedef struct flush_callback_s flush_callback_t;
+
 /*
  * Private variables
  */
@@ -282,7 +289,7 @@ static int register_callback (llist_t **list, /* {{{ */
                {
                        ERROR ("plugin: register_callback: "
                                        "llentry_create failed.");
-                       free (key);
+                       sfree (key);
                        destroy_callback (cf);
                        return (-1);
                }
@@ -312,8 +319,8 @@ static void log_list_callbacks (llist_t **list, /* {{{ */
 {
        char *str;
        int len;
-       llentry_t *le;
        int i;
+       llentry_t *le;
        int n;
        char **keys;
 
@@ -352,9 +359,9 @@ static void log_list_callbacks (llist_t **list, /* {{{ */
                *str = '\0';
                strjoin(str, len, keys, n, "', '");
                INFO("%s ['%s']", comment, str);
-               free(str);
+               sfree (str);
        }
-       free(keys);
+       sfree (keys);
 } /* }}} void log_list_callbacks */
 
 static int create_register_callback (llist_t **list, /* {{{ */
@@ -362,13 +369,12 @@ static int create_register_callback (llist_t **list, /* {{{ */
 {
        callback_func_t *cf;
 
-       cf = (callback_func_t *) malloc (sizeof (*cf));
+       cf = calloc (1, sizeof (*cf));
        if (cf == NULL)
        {
-               ERROR ("plugin: create_register_callback: malloc failed.");
+               ERROR ("plugin: create_register_callback: calloc failed.");
                return (-1);
        }
-       memset (cf, 0, sizeof (*cf));
 
        cf->cf_callback = callback;
        if (ud == NULL)
@@ -477,7 +483,9 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
        {
                read_func_t *rf;
                plugin_ctx_t old_ctx;
+               cdtime_t start;
                cdtime_t now;
+               cdtime_t elapsed;
                int status;
                int rf_type;
                int rc;
@@ -556,6 +564,8 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
 
                DEBUG ("plugin_read_thread: Handling `%s'.", rf->rf_name);
 
+               start = cdtime ();
+
                old_ctx = plugin_set_ctx (rf->rf_ctx);
 
                if (rf_type == RF_SIMPLE)
@@ -599,8 +609,22 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                /* update the ``next read due'' field */
                now = cdtime ();
 
+               /* calculate the time spent in the read function */
+               elapsed = (now - start);
+
+               if (elapsed > rf->rf_effective_interval)
+                       WARNING ("plugin_read_thread: read-function of the `%s' plugin took %.3f "
+                               "seconds, which is above its read interval (%.3f seconds). You might "
+                               "want to adjust the `Interval' or `ReadThreads' settings.",
+                               rf->rf_name, CDTIME_T_TO_DOUBLE(elapsed),
+                               CDTIME_T_TO_DOUBLE(rf->rf_effective_interval));
+
+               DEBUG ("plugin_read_thread: read-function of the `%s' plugin took "
+                               "%.6f seconds.",
+                               rf->rf_name, CDTIME_T_TO_DOUBLE(elapsed));
+
                DEBUG ("plugin_read_thread: Effective interval of the "
-                               "%s plugin is %.3f seconds.",
+                               "`%s' plugin is %.3f seconds.",
                                rf->rf_name,
                                CDTIME_T_TO_DOUBLE (rf->rf_effective_interval));
 
@@ -617,7 +641,7 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
                        rf->rf_next_read = now;
                }
 
-               DEBUG ("plugin_read_thread: Next read of the %s plugin at %.3f.",
+               DEBUG ("plugin_read_thread: Next read of the `%s' plugin at %.3f.",
                                rf->rf_name,
                                CDTIME_T_TO_DOUBLE (rf->rf_next_read));
 
@@ -631,8 +655,6 @@ static void *plugin_read_thread (void __attribute__((unused)) *args)
 
 static void start_read_threads (int num)
 {
-       int i;
-
        if (read_threads != NULL)
                return;
 
@@ -644,7 +666,7 @@ static void start_read_threads (int num)
        }
 
        read_threads_num = 0;
-       for (i = 0; i < num; i++)
+       for (int i = 0; i < num; i++)
        {
                if (pthread_create (read_threads + read_threads_num, NULL,
                                        plugin_read_thread, NULL) == 0)
@@ -661,8 +683,6 @@ static void start_read_threads (int num)
 
 static void stop_read_threads (void)
 {
-       int i;
-
        if (read_threads == NULL)
                return;
 
@@ -674,7 +694,7 @@ static void stop_read_threads (void)
        pthread_cond_broadcast (&read_cond);
        pthread_mutex_unlock (&read_lock);
 
-       for (i = 0; i < read_threads_num; i++)
+       for (int i = 0; i < read_threads_num; i++)
        {
                if (pthread_join (read_threads[i], NULL) != 0)
                {
@@ -846,8 +866,6 @@ static void *plugin_write_thread (void __attribute__((unused)) *args) /* {{{ */
 
 static void start_write_threads (size_t num) /* {{{ */
 {
-       size_t i;
-
        if (write_threads != NULL)
                return;
 
@@ -859,7 +877,7 @@ static void start_write_threads (size_t num) /* {{{ */
        }
 
        write_threads_num = 0;
-       for (i = 0; i < num; i++)
+       for (size_t i = 0; i < num; i++)
        {
                int status;
 
@@ -883,7 +901,7 @@ static void start_write_threads (size_t num) /* {{{ */
 static void stop_write_threads (void) /* {{{ */
 {
        write_queue_t *q;
-       int i;
+       size_t i;
 
        if (write_threads == NULL)
                return;
@@ -924,7 +942,7 @@ static void stop_write_threads (void) /* {{{ */
 
        if (i > 0)
        {
-               WARNING ("plugin: %i value list%s left after shutting down "
+               WARNING ("plugin: %zu value list%s left after shutting down "
                                "the write threads.",
                                i, (i == 1) ? " was" : "s were");
        }
@@ -935,17 +953,17 @@ static void stop_write_threads (void) /* {{{ */
  */
 void plugin_set_dir (const char *dir)
 {
-       if (plugindir != NULL)
-               free (plugindir);
+       sfree (plugindir);
 
        if (dir == NULL)
-               plugindir = NULL;
-       else if ((plugindir = strdup (dir)) == NULL)
        {
-               char errbuf[1024];
-               ERROR ("strdup failed: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               plugindir = NULL;
+               return;
        }
+
+       plugindir = strdup (dir);
+       if (plugindir == NULL)
+               ERROR ("plugin_set_dir: strdup(\"%s\") failed", dir);
 }
 
 static _Bool plugin_is_loaded (char const *name)
@@ -953,7 +971,7 @@ static _Bool plugin_is_loaded (char const *name)
        int status;
 
        if (plugins_loaded == NULL)
-               plugins_loaded = c_avl_create ((void *) strcasecmp);
+               plugins_loaded = c_avl_create ((int (*) (const void *, const void *)) strcasecmp);
        assert (plugins_loaded != NULL);
 
        status = c_avl_get (plugins_loaded, name, /* ret_value = */ NULL);
@@ -1083,6 +1101,7 @@ int plugin_load (char const *plugin_name, uint32_t flags)
                        /* success */
                        plugin_mark_loaded (plugin_name);
                        ret = 0;
+                       INFO ("plugin_load: plugin \"%s\" successfully loaded.", plugin_name);
                        break;
                }
                else
@@ -1219,14 +1238,13 @@ int plugin_register_read (const char *name,
        read_func_t *rf;
        int status;
 
-       rf = malloc (sizeof (*rf));
+       rf = calloc (1, sizeof (*rf));
        if (rf == NULL)
        {
-               ERROR ("plugin_register_read: malloc failed.");
+               ERROR ("plugin_register_read: calloc failed.");
                return (ENOMEM);
        }
 
-       memset (rf, 0, sizeof (read_func_t));
        rf->rf_callback = (void *) callback;
        rf->rf_udata.data = NULL;
        rf->rf_udata.free_func = NULL;
@@ -1247,20 +1265,19 @@ int plugin_register_read (const char *name,
 
 int plugin_register_complex_read (const char *group, const char *name,
                plugin_read_cb callback,
-               const struct timespec *interval,
+               cdtime_t interval,
                user_data_t *user_data)
 {
        read_func_t *rf;
        int status;
 
-       rf = malloc (sizeof (*rf));
+       rf = calloc (1,sizeof (*rf));
        if (rf == NULL)
        {
-               ERROR ("plugin_register_complex_read: malloc failed.");
+               ERROR ("plugin_register_complex_read: calloc failed.");
                return (ENOMEM);
        }
 
-       memset (rf, 0, sizeof (read_func_t));
        rf->rf_callback = (void *) callback;
        if (group != NULL)
                sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
@@ -1268,10 +1285,7 @@ int plugin_register_complex_read (const char *group, const char *name,
                rf->rf_group[0] = '\0';
        rf->rf_name = strdup (name);
        rf->rf_type = RF_COMPLEX;
-       if (interval != NULL)
-               rf->rf_interval = TIMESPEC_TO_CDTIME_T (interval);
-       else
-               rf->rf_interval = plugin_get_interval ();
+       rf->rf_interval = (interval != 0) ? interval : plugin_get_interval ();
 
        /* Set user data */
        if (user_data == NULL)
@@ -1302,11 +1316,104 @@ int plugin_register_write (const char *name,
                                (void *) callback, ud));
 } /* int plugin_register_write */
 
+static int plugin_flush_timeout_callback (user_data_t *ud)
+{
+       flush_callback_t *cb = ud->data;
+
+       return plugin_flush (cb->name, cb->timeout, /* identifier = */ NULL);
+} /* static int plugin_flush_callback */
+
+static void plugin_flush_timeout_callback_free (void *data)
+{
+       flush_callback_t *cb = data;
+
+       if (cb == NULL) return;
+
+       sfree (cb->name);
+       sfree (cb);
+} /* static void plugin_flush_callback_free */
+
+static char *plugin_flush_callback_name (const char *name)
+{
+       const char *flush_prefix = "flush/";
+       size_t prefix_size;
+       char *flush_name;
+       size_t name_size;
+
+       prefix_size = strlen(flush_prefix);
+       name_size = strlen(name);
+
+       flush_name = malloc (name_size + prefix_size + 1);
+       if (flush_name == NULL)
+       {
+               ERROR ("plugin_flush_callback_name: malloc failed.");
+               return (NULL);
+       }
+
+       sstrncpy (flush_name, flush_prefix, prefix_size + 1);
+       sstrncpy (flush_name + prefix_size, name, name_size + 1);
+
+       return flush_name;
+} /* static char *plugin_flush_callback_name */
+
 int plugin_register_flush (const char *name,
                plugin_flush_cb callback, user_data_t *ud)
 {
-       return (create_register_callback (&list_flush, name,
-                               (void *) callback, ud));
+       int status;
+       plugin_ctx_t ctx = plugin_get_ctx ();
+
+       status = create_register_callback (&list_flush, name,
+               (void *) callback, ud);
+       if (status != 0)
+               return status;
+
+       if (ctx.flush_interval != 0)
+       {
+               char *flush_name;
+               flush_callback_t *cb;
+
+               flush_name = plugin_flush_callback_name (name);
+               if (flush_name == NULL)
+                       return (-1);
+
+               cb = malloc(sizeof (*cb));
+               if (cb == NULL)
+               {
+                       ERROR ("plugin_register_flush: malloc failed.");
+                       sfree (flush_name);
+                       return (-1);
+               }
+
+               cb->name = strdup (name);
+               if (cb->name == NULL)
+               {
+                       ERROR ("plugin_register_flush: strdup failed.");
+                       sfree (cb);
+                       sfree (flush_name);
+                       return (-1);
+               }
+               cb->timeout = ctx.flush_timeout;
+
+               ud->data = cb;
+               ud->free_func = plugin_flush_timeout_callback_free;
+
+               status = plugin_register_complex_read (
+                       /* group     = */ "flush",
+                       /* name      = */ flush_name,
+                       /* callback  = */ plugin_flush_timeout_callback,
+                       /* interval  = */ ctx.flush_interval,
+                       /* user data = */ ud);
+
+               sfree (flush_name);
+               if (status != 0)
+               {
+                       sfree (cb->name);
+                       sfree (cb);
+                       return status;
+               }
+       }
+
+       return 0;
 } /* int plugin_register_flush */
 
 int plugin_register_missing (const char *name,
@@ -1347,7 +1454,6 @@ static void plugin_free_data_sets (void)
 int plugin_register_data_set (const data_set_t *ds)
 {
        data_set_t *ds_copy;
-       int i;
 
        if ((data_sets != NULL)
                        && (c_avl_get (data_sets, ds->type, NULL) == 0))
@@ -1362,20 +1468,20 @@ int plugin_register_data_set (const data_set_t *ds)
                        return (-1);
        }
 
-       ds_copy = (data_set_t *) malloc (sizeof (data_set_t));
+       ds_copy = malloc (sizeof (*ds_copy));
        if (ds_copy == NULL)
                return (-1);
        memcpy(ds_copy, ds, sizeof (data_set_t));
 
-       ds_copy->ds = (data_source_t *) malloc (sizeof (data_source_t)
+       ds_copy->ds = malloc (sizeof (*ds_copy->ds)
                        * ds->ds_num);
        if (ds_copy->ds == NULL)
        {
-               free (ds_copy);
+               sfree (ds_copy);
                return (-1);
        }
 
-       for (i = 0; i < ds->ds_num; i++)
+       for (size_t i = 0; i < ds->ds_num; i++)
                memcpy (ds_copy->ds + i, ds->ds + i, sizeof (data_source_t));
 
        return (c_avl_insert (data_sets, (void *) ds_copy->type, (void *) ds_copy));
@@ -1525,7 +1631,21 @@ int plugin_unregister_write (const char *name)
 
 int plugin_unregister_flush (const char *name)
 {
-       return (plugin_unregister (list_flush, name));
+       plugin_ctx_t ctx = plugin_get_ctx ();
+
+       if (ctx.flush_interval != 0)
+       {
+               char *flush_name;
+
+               flush_name = plugin_flush_callback_name (name);
+               if (flush_name != NULL)
+               {
+                       plugin_unregister_read(flush_name);
+                       sfree (flush_name);
+               }
+       }
+
+       return plugin_unregister (list_flush, name);
 }
 
 int plugin_unregister_missing (const char *name)
@@ -1564,11 +1684,12 @@ int plugin_unregister_notification (const char *name)
        return (plugin_unregister (list_notification, name));
 }
 
-void plugin_init_all (void)
+int plugin_init_all (void)
 {
        char const *chain_name;
        llentry_t *le;
        int status;
+       int ret = 0;
 
        /* Init the value cache */
        uc_init ();
@@ -1613,7 +1734,7 @@ void plugin_init_all (void)
        }
 
        if ((list_init == NULL) && (read_heap == NULL))
-               return;
+               return ret;
 
        /* Calling all init callbacks before checking if read callbacks
         * are available allows the init callbacks to register the read
@@ -1642,6 +1763,7 @@ void plugin_init_all (void)
                         * handling themselves. */
                        /* FIXME: Unload _all_ functions */
                        plugin_unregister_read (le->key);
+                       ret = -1;
                }
 
                le = le->next;
@@ -1663,6 +1785,7 @@ void plugin_init_all (void)
                if (num != -1)
                        start_read_threads ((num > 0) ? num : 5);
        }
+       return ret;
 } /* void plugin_init_all */
 
 /* TODO: Rename this function. */
@@ -1846,9 +1969,10 @@ int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier)
   return (0);
 } /* int plugin_flush */
 
-void plugin_shutdown_all (void)
+int plugin_shutdown_all (void)
 {
        llentry_t *le;
+       int ret = 0;  // Assume success.
 
        destroy_all_callbacks (&list_init);
 
@@ -1889,7 +2013,8 @@ void plugin_shutdown_all (void)
                 * after callback returns. */
                le = le->next;
 
-               (*callback) ();
+               if ((*callback) () != 0)
+                       ret = -1;
 
                plugin_set_ctx (old_ctx);
        }
@@ -1909,6 +2034,7 @@ void plugin_shutdown_all (void)
 
        plugin_free_loaded ();
        plugin_free_data_sets ();
+       return (ret);
 } /* void plugin_shutdown_all */
 
 int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */
@@ -1964,8 +2090,10 @@ static int plugin_dispatch_values_internal (value_list_t *vl)
 
        int free_meta_data = 0;
 
-       if ((vl == NULL) || (vl->type[0] == 0)
-                       || (vl->values == NULL) || (vl->values_len < 1))
+       assert(vl);
+       assert(vl->plugin);
+
+       if (vl->type[0] == 0 || vl->values == NULL || vl->values_len < 1)
        {
                ERROR ("plugin_dispatch_values: Invalid value list "
                                "from plugin %s.", vl->plugin);
@@ -2032,8 +2160,8 @@ static int plugin_dispatch_values_internal (value_list_t *vl)
        if (ds->ds_num != vl->values_len)
        {
                ERROR ("plugin_dispatch_values: ds->type = %s: "
-                               "(ds->ds_num = %i) != "
-                               "(vl->values_len = %i)",
+                               "(ds->ds_num = %zu) != "
+                               "(vl->values_len = %zu)",
                                ds->type, ds->ds_num, vl->values_len);
                return (-1);
        }
@@ -2086,7 +2214,7 @@ static int plugin_dispatch_values_internal (value_list_t *vl)
                         * don't get confused.. */
                        if (saved_values != NULL)
                        {
-                               free (vl->values);
+                               sfree (vl->values);
                                vl->values     = saved_values;
                                vl->values_len = saved_values_len;
                        }
@@ -2115,7 +2243,7 @@ static int plugin_dispatch_values_internal (value_list_t *vl)
         * confused.. */
        if (saved_values != NULL)
        {
-               free (vl->values);
+               sfree (vl->values);
                vl->values     = saved_values;
                vl->values_len = saved_values_len;
        }
@@ -2230,7 +2358,7 @@ int plugin_dispatch_multivalue (value_list_t const *template, /* {{{ */
 
        assert (template->values_len == 1);
 
-  /* Calculate sum for Gauge to calculate percent if needed */
+       /* Calculate sum for Gauge to calculate percent if needed */
        if (DS_TYPE_GAUGE == store_type)        {
                va_start (ap, store_type);
                while (42)
@@ -2451,13 +2579,12 @@ static int plugin_notification_meta_add (notification_t *n,
     return (-1);
   }
 
-  meta = (notification_meta_t *) malloc (sizeof (notification_meta_t));
+  meta = calloc (1, sizeof (*meta));
   if (meta == NULL)
   {
-    ERROR ("plugin_notification_meta_add: malloc failed.");
+    ERROR ("plugin_notification_meta_add: calloc failed.");
     return (-1);
   }
-  memset (meta, 0, sizeof (notification_meta_t));
 
   sstrncpy (meta->name, name, sizeof (meta->name));
   meta->type = type;
@@ -2554,14 +2681,12 @@ int plugin_notification_meta_add_boolean (notification_t *n,
 int plugin_notification_meta_copy (notification_t *dst,
     const notification_t *src)
 {
-  notification_meta_t *meta;
-
   assert (dst != NULL);
   assert (src != NULL);
   assert (dst != src);
   assert ((src->meta == NULL) || (src->meta != dst->meta));
 
-  for (meta = src->meta; meta != NULL; meta = meta->next)
+  for (notification_meta_t *meta = src->meta; meta != NULL; meta = meta->next)
   {
     if (meta->type == NM_TYPE_STRING)
       plugin_notification_meta_add_string (dst, meta->name,
@@ -2601,7 +2726,11 @@ int plugin_notification_meta_free (notification_meta_t *n)
 
     if (this->type == NM_TYPE_STRING)
     {
-      free ((char *)this->nm_value.nm_string);
+      /* Assign to a temporary variable to work around nm_string's const
+       * modifier. */
+      void *tmp = (void *) this->nm_value.nm_string;
+
+      sfree (tmp);
       this->nm_value.nm_string = NULL;
     }
     sfree (this);
@@ -2708,7 +2837,7 @@ static void *plugin_thread_start (void *arg)
 
        plugin_set_ctx (plugin_thread->ctx);
 
-       free (plugin_thread);
+       sfree (plugin_thread);
 
        return start_routine (plugin_arg);
 } /* void *plugin_thread_start */