X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fconfigfile.c;h=8b526f2e958902a5dcbb22a674b0c0caa267f44f;hb=3c316743f0dccb70e49a10ed44951dd4b289d9f0;hp=f4e9c604fe26cafd75ee04620158b11f5aed6726;hpb=785feb9d2544aff7f561fbda30aa762eec9e07bc;p=collectd.git diff --git a/src/configfile.c b/src/configfile.c index f4e9c604..8b526f2e 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -1,6 +1,6 @@ /** * collectd - src/configfile.c - * Copyright (C) 2005,2006 Florian octo Forster + * Copyright (C) 2005-2008 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 @@ -18,6 +18,7 @@ * * Authors: * Florian octo Forster + * Sebastian tokkee Harl **/ #include "collectd.h" @@ -27,6 +28,12 @@ #include "common.h" #include "plugin.h" #include "configfile.h" +#include "types_list.h" +#include "utils_threshold.h" + +#if HAVE_WORDEXP_H +# include +#endif /* HAVE_WORDEXP_H */ #define ESCAPE_NULL(str) ((str) == NULL ? "(null)" : (str)) @@ -65,6 +72,7 @@ typedef struct cf_global_option_s /* * Prototypes of callback functions */ +static int dispatch_value_typesdb (const oconfig_item_t *ci); static int dispatch_value_plugindir (const oconfig_item_t *ci); static int dispatch_value_loadplugin (const oconfig_item_t *ci); @@ -76,6 +84,7 @@ static cf_complex_callback_t *complex_callback_head = NULL; static cf_value_map_t cf_value_map[] = { + {"TypesDB", dispatch_value_typesdb}, {"PluginDir", dispatch_value_plugindir}, {"LoadPlugin", dispatch_value_loadplugin} }; @@ -83,15 +92,17 @@ static int cf_value_map_num = STATIC_ARRAY_LEN (cf_value_map); static cf_global_option_t cf_global_options[] = { - {"BaseDir", NULL, PKGLOCALSTATEDIR}, - {"PIDFile", NULL, PIDFILE}, - {"Hostname", NULL, NULL}, - {"Interval", NULL, "10"}, - {"ReadThreads", NULL, "5"}, - {"TypesDB", NULL, PLUGINDIR"/types.db"} /* FIXME: Configure path */ + {"BaseDir", NULL, PKGLOCALSTATEDIR}, + {"PIDFile", NULL, PIDFILE}, + {"Hostname", NULL, NULL}, + {"FQDNLookup", NULL, "false"}, + {"Interval", NULL, "10"}, + {"ReadThreads", NULL, "5"} }; static int cf_global_options_num = STATIC_ARRAY_LEN (cf_global_options); +static int cf_default_typesdb = 1; + /* * Functions to handle register/unregister, search, and other plugin related * stuff @@ -126,7 +137,9 @@ static int cf_dispatch (const char *type, const char *orig_key, if ((cf_cb = cf_search (type)) == NULL) { - WARNING ("Plugin `%s' did not register a callback.", type); + WARNING ("Found a configuration for the `%s' plugin, but " + "the plugin isn't loaded or didn't register " + "a configuration callback.", type); return (-1); } @@ -173,10 +186,38 @@ static int dispatch_global_option (const oconfig_item_t *ci) tmp[127] = '\0'; return (global_option_set (ci->key, tmp)); } + else if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN) + { + if (ci->values[0].value.boolean) + return (global_option_set (ci->key, "true")); + else + return (global_option_set (ci->key, "false")); + } return (-1); } /* int dispatch_global_option */ +static int dispatch_value_typesdb (const oconfig_item_t *ci) +{ + int i = 0; + + assert (strcasecmp (ci->key, "TypesDB") == 0); + + cf_default_typesdb = 0; + + if (ci->values_num < 1) + return (-1); + + for (i = 0; i < ci->values_num; ++i) + { + if (OCONFIG_TYPE_STRING != ci->values[i].type) + continue; + + read_types_list (ci->values[i].value.string); + } + return (0); +} /* int dispatch_value_typesdb */ + static int dispatch_value_plugindir (const oconfig_item_t *ci) { assert (strcasecmp (ci->key, "PluginDir") == 0); @@ -287,7 +328,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); @@ -298,10 +339,399 @@ static int dispatch_block (oconfig_item_t *ci) { if (strcasecmp (ci->key, "Plugin") == 0) return (dispatch_block_plugin (ci)); + else if (strcasecmp (ci->key, "Threshold") == 0) + return (ut_config (ci)); return (0); } +static int cf_ci_replace_child (oconfig_item_t *dst, oconfig_item_t *src, + int offset) +{ + oconfig_item_t *temp; + int i; + + assert (offset >= 0); + assert (dst->children_num > offset); + + /* Free the memory used by the replaced child. Usually that's the + * `Include "blah"' statement. */ + temp = dst->children + offset; + for (i = 0; i < temp->values_num; i++) + { + if (temp->values[i].type == OCONFIG_TYPE_STRING) + { + sfree (temp->values[i].value.string); + } + } + sfree (temp->values); + temp = NULL; + + /* 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; + int new_offset = offset + src->children_num; + + memmove (dst->children + new_offset, + dst->children + old_offset, + sizeof (oconfig_item_t) * nmemb); + } + + /* Last but not least: If there are new childrem, copy them to the + * memory reserved for them. */ + if (src->children_num > 0) + { + memcpy (dst->children + offset, + src->children, + sizeof (oconfig_item_t) * src->children_num); + } + + /* Update the number of children. */ + dst->children_num += (src->children_num - 1); + + return (0); +} /* int cf_ci_replace_child */ + +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)); + if (temp == NULL) + { + ERROR ("configfile: realloc failed."); + return (-1); + } + dst->children = temp; + + memcpy (dst->children + dst->children_num, + src->children, + sizeof (oconfig_item_t) + * src->children_num); + dst->children_num += src->children_num; + + return (0); +} /* int cf_ci_append_children */ + +#define CF_MAX_DEPTH 8 +static oconfig_item_t *cf_read_generic (const char *path, int depth); + +static int cf_include_all (oconfig_item_t *root, int depth) +{ + int i; + + for (i = 0; i < root->children_num; i++) + { + oconfig_item_t *new; + oconfig_item_t *old; + + /* Ignore all blocks, including `Include' blocks. */ + if (root->children[i].children_num != 0) + continue; + + if (strcasecmp (root->children[i].key, "Include") != 0) + continue; + + old = root->children + i; + + if ((old->values_num != 1) + || (old->values[0].type != OCONFIG_TYPE_STRING)) + { + ERROR ("configfile: `Include' needs exactly one string argument."); + continue; + } + + new = cf_read_generic (old->values[0].value.string, depth + 1); + if (new == NULL) + continue; + + /* Now replace the i'th child in `root' with `new'. */ + cf_ci_replace_child (root, new, i); + + sfree (new->values); + sfree (new); + } /* for (i = 0; i < root->children_num; i++) */ + + return (0); +} /* int cf_include_all */ + +static oconfig_item_t *cf_read_file (const char *file, int depth) +{ + oconfig_item_t *root; + + assert (depth < CF_MAX_DEPTH); + + root = oconfig_parse_file (file); + if (root == NULL) + { + ERROR ("configfile: Cannot read file `%s'.", file); + return (NULL); + } + + cf_include_all (root, 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 **filenames = NULL; + int filenames_num = 0; + int status; + int i; + + assert (depth < CF_MAX_DEPTH); + + dh = opendir (dir); + if (dh == NULL) + { + char errbuf[1024]; + ERROR ("configfile: opendir failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (NULL); + } + + root = (oconfig_item_t *) malloc (sizeof (oconfig_item_t)); + if (root == NULL) + { + ERROR ("configfile: malloc failed."); + return (NULL); + } + memset (root, '\0', sizeof (oconfig_item_t)); + + while ((de = readdir (dh)) != NULL) + { + char name[1024]; + char **tmp; + + if ((de->d_name[0] == '.') || (de->d_name[0] == '\0')) + continue; + + status = snprintf (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); + 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) { + 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 */ + +/* + * cf_read_generic + * + * Path is stat'ed and either cf_read_file or cf_read_dir is called + * accordingly. + * + * There are two versions of this function: If `wordexp' exists shell wildcards + * will be expanded and the function will include all matches found. If + * `wordexp' (or, more precisely, it's header file) is not available the + * simpler function is used which does not do any such expansion. + */ +#if HAVE_WORDEXP_H +static oconfig_item_t *cf_read_generic (const char *path, int depth) +{ + oconfig_item_t *root = NULL; + int status; + const char *path_ptr; + wordexp_t we; + int i; + + if (depth >= CF_MAX_DEPTH) + { + ERROR ("configfile: Not including `%s' because the maximum " + "nesting depth has been reached.", path); + return (NULL); + } + + status = wordexp (path, &we, WRDE_NOCMD); + if (status != 0) + { + ERROR ("configfile: wordexp (%s) failed.", path); + return (NULL); + } + + root = (oconfig_item_t *) malloc (sizeof (oconfig_item_t)); + if (root == NULL) + { + ERROR ("configfile: malloc failed."); + return (NULL); + } + 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; + struct stat statbuf; + + path_ptr = we.we_wordv[i]; + + status = stat (path_ptr, &statbuf); + if (status != 0) + { + char errbuf[1024]; + ERROR ("configfile: stat (%s) failed: %s", + path_ptr, + sstrerror (errno, errbuf, sizeof (errbuf))); + oconfig_free (root); + return (NULL); + } + + if (S_ISREG (statbuf.st_mode)) + temp = cf_read_file (path_ptr, depth); + else if (S_ISDIR (statbuf.st_mode)) + temp = cf_read_dir (path_ptr, depth); + else + { + ERROR ("configfile: %s is neither a file nor a " + "directory.", path); + continue; + } + + if (temp == NULL) { + oconfig_free (root); + return (NULL); + } + + cf_ci_append_children (root, temp); + sfree (temp->children); + sfree (temp); + } + + wordfree (&we); + + return (root); +} /* oconfig_item_t *cf_read_generic */ +/* #endif HAVE_WORDEXP_H */ + +#else /* if !HAVE_WORDEXP_H */ +static oconfig_item_t *cf_read_generic (const char *path, int depth) +{ + struct stat statbuf; + int status; + + if (depth >= CF_MAX_DEPTH) + { + ERROR ("configfile: Not including `%s' because the maximum " + "nesting depth has been reached.", path); + return (NULL); + } + + status = stat (path, &statbuf); + if (status != 0) + { + char errbuf[1024]; + ERROR ("configfile: stat (%s) failed: %s", + path, + sstrerror (errno, errbuf, sizeof (errbuf))); + return (NULL); + } + + if (S_ISREG (statbuf.st_mode)) + return (cf_read_file (path, depth)); + else if (S_ISDIR (statbuf.st_mode)) + return (cf_read_dir (path, depth)); + + ERROR ("configfile: %s is neither a file nor a directory.", path); + return (NULL); +} /* oconfig_item_t *cf_read_generic */ +#endif /* !HAVE_WORDEXP_H */ + /* * Public functions */ @@ -443,7 +873,7 @@ int cf_read (char *filename) oconfig_item_t *conf; int i; - conf = oconfig_parse_file (filename); + conf = cf_read_generic (filename, 0 /* depth */); if (conf == NULL) { ERROR ("Unable to read config file %s.", filename); @@ -458,5 +888,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"); + return (0); } /* int cf_read */