Merge branch 'collectd-4.5'
[collectd.git] / src / configfile.c
index 36b6dad..f9ade29 100644 (file)
@@ -18,6 +18,7 @@
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   Sebastian tokkee Harl <sh at tokkee.org>
  **/
 
 #include "collectd.h"
@@ -29,6 +30,7 @@
 #include "configfile.h"
 #include "types_list.h"
 #include "utils_threshold.h"
+#include "filter_chain.h"
 
 #if HAVE_WORDEXP_H
 # include <wordexp.h>
@@ -154,7 +156,8 @@ static int cf_dispatch (const char *type, const char *orig_key,
 
        for (i = 0; i < cf_cb->keys_num; i++)
        {
-               if (strcasecmp (cf_cb->keys[i], key) == 0)
+               if ((cf_cb->keys[i] != NULL)
+                               && (strcasecmp (cf_cb->keys[i], key) == 0))
                {
                        ret = (*cf_cb->callback) (key, value);
                        break;
@@ -181,8 +184,7 @@ static int dispatch_global_option (const oconfig_item_t *ci)
        else if (ci->values[0].type == OCONFIG_TYPE_NUMBER)
        {
                char tmp[128];
-               snprintf (tmp, sizeof (tmp), "%lf", ci->values[0].value.number);
-               tmp[127] = '\0';
+               ssnprintf (tmp, sizeof (tmp), "%lf", ci->values[0].value.number);
                return (global_option_set (ci->key, tmp));
        }
        else if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN)
@@ -257,13 +259,13 @@ static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci)
                int status = -1;
 
                if (ci->values[i].type == OCONFIG_TYPE_STRING)
-                       status = snprintf (buffer_ptr, buffer_free, " %s",
+                       status = ssnprintf (buffer_ptr, buffer_free, " %s",
                                        ci->values[i].value.string);
                else if (ci->values[i].type == OCONFIG_TYPE_NUMBER)
-                       status = snprintf (buffer_ptr, buffer_free, " %lf",
+                       status = ssnprintf (buffer_ptr, buffer_free, " %lf",
                                        ci->values[i].value.number);
                else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
-                       status = snprintf (buffer_ptr, buffer_free, " %s",
+                       status = ssnprintf (buffer_ptr, buffer_free, " %s",
                                        ci->values[i].value.boolean
                                        ? "true" : "false");
 
@@ -276,7 +278,7 @@ static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci)
        buffer_ptr = buffer + 1;
 
        return (cf_dispatch (plugin, ci->key, buffer_ptr));
-} /* int plugin_conf_dispatch */
+} /* int dispatch_value_plugin */
 
 static int dispatch_value (const oconfig_item_t *ci)
 {
@@ -327,7 +329,7 @@ static int dispatch_block_plugin (oconfig_item_t *ci)
                if (ci->children[i].children == NULL)
                        dispatch_value_plugin (name, ci->children + i);
                else
-                       {DEBUG ("No nested config blocks allow for this plugin.");}
+                       {DEBUG ("No nested config blocks allowed for this plugin.");}
        }
 
        return (0);
@@ -340,6 +342,8 @@ static int dispatch_block (oconfig_item_t *ci)
                return (dispatch_block_plugin (ci));
        else if (strcasecmp (ci->key, "Threshold") == 0)
                return (ut_config (ci));
+       else if (strcasecmp (ci->key, "Chain") == 0)
+               return (fc_configure (ci));
 
        return (0);
 }
@@ -353,18 +357,6 @@ static int cf_ci_replace_child (oconfig_item_t *dst, oconfig_item_t *src,
        assert (offset >= 0);
        assert (dst->children_num > offset);
 
-       /* Resize the memory containing the children to be big enough to hold
-        * all children. */
-       temp = (oconfig_item_t *) realloc (dst->children,
-                       sizeof (oconfig_item_t)
-                       * (dst->children_num + src->children_num - 1));
-       if (temp == NULL)
-       {
-               ERROR ("configfile: realloc failed.");
-               return (-1);
-       }
-       dst->children = temp;
-
        /* Free the memory used by the replaced child. Usually that's the
         * `Include "blah"' statement. */
        temp = dst->children + offset;
@@ -378,9 +370,34 @@ static int cf_ci_replace_child (oconfig_item_t *dst, oconfig_item_t *src,
        sfree (temp->values);
        temp = NULL;
 
-       /* If there are children behind the include statement, move them to the
-        * end of the list, so that the new children have room before them. */
-       if ((dst->children_num - (offset + 1)) > 0)
+       /* If (src->children_num == 0) the array size is decreased. If offset
+        * is _not_ the last element, (offset < (src->children_num - 1)), then
+        * we need to move the trailing elements before resizing the array. */
+       if ((src->children_num == 0) && (offset < (src->children_num - 1)))
+       {
+               int nmemb = src->children_num - (offset + 1);
+               memmove (src->children + offset, src->children + offset + 1,
+                               sizeof (oconfig_item_t) * nmemb);
+       }
+
+       /* Resize the memory containing the children to be big enough to hold
+        * all children. */
+       temp = (oconfig_item_t *) realloc (dst->children,
+                       sizeof (oconfig_item_t)
+                       * (dst->children_num + src->children_num - 1));
+       if (temp == NULL)
+       {
+               ERROR ("configfile: realloc failed.");
+               return (-1);
+       }
+       dst->children = temp;
+
+       /* If there are children behind the include statement, and they have
+        * not yet been moved because (src->children_num == 0), then move them
+        * to the end of the list, so that the new children have room before
+        * them. */
+       if ((src->children_num > 0)
+                       && ((dst->children_num - (offset + 1)) > 0))
        {
                int nmemb = dst->children_num - (offset + 1);
                int old_offset = offset + 1;
@@ -410,6 +427,9 @@ static int cf_ci_append_children (oconfig_item_t *dst, oconfig_item_t *src)
 {
        oconfig_item_t *temp;
 
+       if ((src == NULL) || (src->children_num == 0))
+               return (0);
+
        temp = (oconfig_item_t *) realloc (dst->children,
                        sizeof (oconfig_item_t)
                        * (dst->children_num + src->children_num));
@@ -489,13 +509,20 @@ static oconfig_item_t *cf_read_file (const char *file, int depth)
        return (root);
 } /* oconfig_item_t *cf_read_file */
 
+static int cf_compare_string (const void *p1, const void *p2)
+{
+       return strcmp (*(const char **) p1, *(const char **) p2);
+}
+
 static oconfig_item_t *cf_read_dir (const char *dir, int depth)
 {
        oconfig_item_t *root = NULL;
        DIR *dh;
        struct dirent *de;
-       char name[1024];
+       char **filenames = NULL;
+       int filenames_num = 0;
        int status;
+       int i;
 
        assert (depth < CF_MAX_DEPTH);
 
@@ -518,30 +545,68 @@ static oconfig_item_t *cf_read_dir (const char *dir, int depth)
 
        while ((de = readdir (dh)) != NULL)
        {
-               oconfig_item_t *temp;
+               char   name[1024];
+               char **tmp;
 
                if ((de->d_name[0] == '.') || (de->d_name[0] == '\0'))
                        continue;
 
-               status = snprintf (name, sizeof (name), "%s/%s",
+               status = ssnprintf (name, sizeof (name), "%s/%s",
                                dir, de->d_name);
                if (status >= sizeof (name))
                {
                        ERROR ("configfile: Not including `%s/%s' because its"
                                        " name is too long.",
                                        dir, de->d_name);
-                       continue;
+                       for (i = 0; i < filenames_num; ++i)
+                               free (filenames[i]);
+                       free (filenames);
+                       free (root);
+                       return (NULL);
+               }
+
+               ++filenames_num;
+               tmp = (char **) realloc (filenames,
+                               filenames_num * sizeof (*filenames));
+               if (tmp == NULL) {
+                       ERROR ("configfile: realloc failed.");
+                       for (i = 0; i < filenames_num - 1; ++i)
+                               free (filenames[i]);
+                       free (filenames);
+                       free (root);
+                       return (NULL);
                }
+               filenames = tmp;
+
+               filenames[filenames_num - 1] = sstrdup (name);
+       }
+
+       qsort ((void *) filenames, filenames_num, sizeof (*filenames),
+                       cf_compare_string);
+
+       for (i = 0; i < filenames_num; ++i)
+       {
+               oconfig_item_t *temp;
+               char *name = filenames[i];
 
                temp = cf_read_generic (name, depth);
-               if (temp == NULL)
-                       continue;
+               if (temp == NULL) {
+                       int j;
+                       for (j = i; j < filenames_num; ++j)
+                               free (filenames[j]);
+                       free (filenames);
+                       oconfig_free (root);
+                       return (NULL);
+               }
 
                cf_ci_append_children (root, temp);
                sfree (temp->children);
                sfree (temp);
+
+               free (name);
        }
 
+       free(filenames);
        return (root);
 } /* oconfig_item_t *cf_read_dir */
 
@@ -587,6 +652,11 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth)
        }
        memset (root, '\0', sizeof (oconfig_item_t));
 
+       /* wordexp() might return a sorted list already. That's not
+        * documented though, so let's make sure we get what we want. */
+       qsort ((void *) we.we_wordv, we.we_wordc, sizeof (*we.we_wordv),
+                       cf_compare_string);
+
        for (i = 0; i < we.we_wordc; i++)
        {
                oconfig_item_t *temp;
@@ -601,6 +671,7 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth)
                        ERROR ("configfile: stat (%s) failed: %s",
                                        path_ptr,
                                        sstrerror (errno, errbuf, sizeof (errbuf)));
+                       oconfig_free (root);
                        return (NULL);
                }
 
@@ -615,6 +686,11 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth)
                        continue;
                }
 
+               if (temp == NULL) {
+                       oconfig_free (root);
+                       return (NULL);
+               }
+
                cf_ci_append_children (root, temp);
                sfree (temp->children);
                sfree (temp);
@@ -815,7 +891,11 @@ int cf_read (char *filename)
                        dispatch_block (conf->children + i);
        }
 
+       oconfig_free (conf);
+
+       /* Read the default types.db if no `TypesDB' option was given. */
        if (cf_default_typesdb)
-               read_types_list (PLUGINDIR"/types.db"); /* FIXME: Configure path */
+               read_types_list (PKGDATADIR"/types.db");
+
        return (0);
 } /* int cf_read */