X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Fconfigfile.c;h=e6b8ce821465cc008cfc21a1c7320fabc2078c7c;hb=3f391479bfc45d0ff6e0c7b87c899e41a192f392;hp=5472de4e3e18d3a927289f9d6c5425ee88b70386;hpb=ec6fec62b15992b22774233c6a915e46673b25af;p=collectd.git diff --git a/src/configfile.c b/src/configfile.c index 5472de4e..c929d009 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-2009 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,36 +18,29 @@ * * Authors: * Florian octo Forster + * Sebastian tokkee Harl **/ #include "collectd.h" -#include "libconfig/libconfig.h" +#include "liboconfig/oconfig.h" #include "common.h" #include "plugin.h" #include "configfile.h" -#include "network.h" -#include "utils_debug.h" +#include "types_list.h" +#include "utils_threshold.h" +#include "filter_chain.h" -#define SHORTOPT_NONE 0 - -#define ERR_NOT_NESTED "Sections cannot be nested.\n" -#define ERR_SECTION_ONLY "`%s' can only be used as section.\n" -#define ERR_NEEDS_ARG "Section `%s' needs an argument.\n" -#define ERR_NEEDS_SECTION "`%s' can only be used within a section.\n" +#if HAVE_WORDEXP_H +# include +#endif /* HAVE_WORDEXP_H */ #define ESCAPE_NULL(str) ((str) == NULL ? "(null)" : (str)) -#define DEBUG_CALLBACK(shortvar, var, arguments, value) \ - DBG("shortvar = %s, var = %s, arguments = %s, value = %s, ...", \ - ESCAPE_NULL(shortvar), \ - ESCAPE_NULL(var), \ - ESCAPE_NULL(arguments), \ - ESCAPE_NULL(value)) - -extern int operating_mode; - +/* + * Private types + */ typedef struct cf_callback { const char *type; @@ -57,39 +50,67 @@ typedef struct cf_callback struct cf_callback *next; } cf_callback_t; -static cf_callback_t *first_callback = NULL; +typedef struct cf_complex_callback_s +{ + char *type; + int (*callback) (oconfig_item_t *); + struct cf_complex_callback_s *next; +} cf_complex_callback_t; + +typedef struct cf_value_map_s +{ + char *key; + int (*func) (const oconfig_item_t *); +} cf_value_map_t; -typedef struct cf_mode_item +typedef struct cf_global_option_s { char *key; char *value; - int mode; -} cf_mode_item_t; + char *def; +} cf_global_option_t; -/* TODO - * - LogFile +/* + * Prototypes of callback functions */ -static cf_mode_item_t cf_mode_list[] = +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); + +/* + * Private variables + */ +static cf_callback_t *first_callback = NULL; +static cf_complex_callback_t *complex_callback_head = NULL; + +static cf_value_map_t cf_value_map[] = { - {"TimeToLive", NULL, MODE_CLIENT }, - {"PIDFile", NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG }, - {"DataDir", NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG }, - {"LogFile", NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG } + {"TypesDB", dispatch_value_typesdb}, + {"PluginDir", dispatch_value_plugindir}, + {"LoadPlugin", dispatch_value_loadplugin} }; -static int cf_mode_num = 4; +static int cf_value_map_num = STATIC_ARRAY_LEN (cf_value_map); -static int nesting_depth = 0; -static char *current_module = NULL; +static cf_global_option_t cf_global_options[] = +{ + {"BaseDir", NULL, PKGLOCALSTATEDIR}, + {"PIDFile", NULL, PIDFILE}, + {"Hostname", NULL, NULL}, + {"FQDNLookup", NULL, "false"}, + {"Interval", NULL, "10"}, + {"ReadThreads", NULL, "5"}, + {"PreCacheChain", NULL, "PreCache"}, + {"PostCacheChain", NULL, "PostCache"} +}; +static int cf_global_options_num = STATIC_ARRAY_LEN (cf_global_options); -/* `cf_register' needs this prototype */ -static int cf_callback_plugin_dispatch (const char *, const char *, - const char *, const char *, lc_flags_t, void *); +static int cf_default_typesdb = 1; /* * Functions to handle register/unregister, search, and other plugin related * stuff */ -static cf_callback_t *cf_search (char *type) +static cf_callback_t *cf_search (const char *type) { cf_callback_t *cf_cb; @@ -103,7 +124,8 @@ static cf_callback_t *cf_search (char *type) return (cf_cb); } -static int cf_dispatch (char *type, const char *orig_key, const char *orig_value) +static int cf_dispatch (const char *type, const char *orig_key, + const char *orig_value) { cf_callback_t *cf_cb; char *key; @@ -111,14 +133,16 @@ static int cf_dispatch (char *type, const char *orig_key, const char *orig_value int ret; int i; - DBG ("type = %s, key = %s, value = %s", + DEBUG ("type = %s, key = %s, value = %s", ESCAPE_NULL(type), ESCAPE_NULL(orig_key), ESCAPE_NULL(orig_value)); if ((cf_cb = cf_search (type)) == NULL) { - syslog (LOG_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); } @@ -134,7 +158,8 @@ static int cf_dispatch (char *type, const char *orig_key, const char *orig_value 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; @@ -142,376 +167,742 @@ static int cf_dispatch (char *type, const char *orig_key, const char *orig_value } if (i >= cf_cb->keys_num) - syslog (LOG_WARNING, "Plugin `%s' did not register for value `%s'.", type, key); + WARNING ("Plugin `%s' did not register for value `%s'.", type, key); free (key); free (value); - DBG ("return (%i)", ret); + DEBUG ("return (%i)", ret); return (ret); -} +} /* int cf_dispatch */ -void cf_unregister (const char *type) +static int dispatch_global_option (const oconfig_item_t *ci) { - cf_callback_t *this, *prev; + if (ci->values_num != 1) + return (-1); + if (ci->values[0].type == OCONFIG_TYPE_STRING) + return (global_option_set (ci->key, ci->values[0].value.string)); + else if (ci->values[0].type == OCONFIG_TYPE_NUMBER) + { + char tmp[128]; + 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) + { + if (ci->values[0].value.boolean) + return (global_option_set (ci->key, "true")); + else + return (global_option_set (ci->key, "false")); + } - for (prev = NULL, this = first_callback; - this != NULL; - prev = this, this = this->next) - if (strcasecmp (this->type, type) == 0) - { - if (prev == NULL) - first_callback = this->next; - else - prev->next = this->next; + return (-1); +} /* int dispatch_global_option */ - free (this); - break; +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) { + ERROR ("configfile: `TypesDB' needs at least one argument."); + return (-1); + } + + for (i = 0; i < ci->values_num; ++i) + { + if (OCONFIG_TYPE_STRING != ci->values[i].type) { + WARNING ("configfile: TypesDB: Skipping %i. argument which " + "is not a string.", i + 1); + 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); + + if (ci->values_num != 1) + return (-1); + if (ci->values[0].type != OCONFIG_TYPE_STRING) + return (-1); + + plugin_set_dir (ci->values[0].value.string); + return (0); } -void cf_register (const char *type, - int (*callback) (const char *, const char *), - const char **keys, int keys_num) +static int dispatch_value_loadplugin (const oconfig_item_t *ci) { - cf_callback_t *cf_cb; - char buf[64]; - int i; + assert (strcasecmp (ci->key, "LoadPlugin") == 0); - /* Remove this module from the list, if it already exists */ - cf_unregister (type); + if (ci->values_num != 1) + return (-1); + if (ci->values[0].type != OCONFIG_TYPE_STRING) + return (-1); - /* This pointer will be free'd in `cf_unregister' */ - if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL) - return; + return (plugin_load (ci->values[0].value.string)); +} /* int dispatch_value_loadplugin */ - cf_cb->type = type; - cf_cb->callback = callback; - cf_cb->keys = keys; - cf_cb->keys_num = keys_num; +static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci) +{ + char buffer[4096]; + char *buffer_ptr; + int buffer_free; + int i; - cf_cb->next = first_callback; - first_callback = cf_cb; + buffer_ptr = buffer; + buffer_free = sizeof (buffer); - for (i = 0; i < keys_num; i++) + for (i = 0; i < ci->values_num; i++) { - if (snprintf (buf, 64, "Plugin.%s", keys[i]) < 64) + int status = -1; + + if (ci->values[i].type == OCONFIG_TYPE_STRING) + status = ssnprintf (buffer_ptr, buffer_free, " %s", + ci->values[i].value.string); + else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) + status = ssnprintf (buffer_ptr, buffer_free, " %lf", + ci->values[i].value.number); + else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) + status = ssnprintf (buffer_ptr, buffer_free, " %s", + ci->values[i].value.boolean + ? "true" : "false"); + + if ((status < 0) || (status >= buffer_free)) + return (-1); + buffer_free -= status; + buffer_ptr += status; + } + /* skip the initial space */ + buffer_ptr = buffer + 1; + + return (cf_dispatch (plugin, ci->key, buffer_ptr)); +} /* int dispatch_value_plugin */ + +static int dispatch_value (const oconfig_item_t *ci) +{ + int ret = -2; + int i; + + for (i = 0; i < cf_value_map_num; i++) + if (strcasecmp (cf_value_map[i].key, ci->key) == 0) { - /* This may be called multiple times for the same - * `key', but apparently `lc_register_*' can handle - * it.. */ - lc_register_callback (buf, SHORTOPT_NONE, - LC_VAR_STRING, cf_callback_plugin_dispatch, - NULL); + ret = cf_value_map[i].func (ci); + break; } - else + + for (i = 0; i < cf_global_options_num; i++) + if (strcasecmp (cf_global_options[i].key, ci->key) == 0) { - DBG ("Key was truncated: `%s'", ESCAPE_NULL(keys[i])); + ret = dispatch_global_option (ci); + break; } - } -} -/* - * Other query functions - */ -char *cf_get_option (const char *key, char *def) + return (ret); +} /* int dispatch_value */ + +static int dispatch_block_plugin (oconfig_item_t *ci) { int i; + char *name; - for (i = 0; i < cf_mode_num; i++) - { - if ((cf_mode_list[i].mode & operating_mode) == 0) - continue; + cf_complex_callback_t *cb; - if (strcasecmp (cf_mode_list[i].key, key) != 0) - continue; + if (strcasecmp (ci->key, "Plugin") != 0) + return (-1); + if (ci->values_num < 1) + return (-1); + if (ci->values[0].type != OCONFIG_TYPE_STRING) + return (-1); - if (cf_mode_list[i].value != NULL) - return (cf_mode_list[i].value); - return (def); + name = ci->values[0].value.string; + + /* Check for a complex callback first */ + for (cb = complex_callback_head; cb != NULL; cb = cb->next) + if (strcasecmp (name, cb->type) == 0) + return (cb->callback (ci)); + + /* Hm, no complex plugin found. Dispatch the values one by one */ + for (i = 0; i < ci->children_num; i++) + { + if (ci->children[i].children == NULL) + dispatch_value_plugin (name, ci->children + i); + else + {DEBUG ("No nested config blocks allowed for this plugin.");} } - return (NULL); + return (0); } -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Functions for the actual parsing * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -/* - * `cf_callback_mode' - * Chose the `operating_mode' - * - * Mode `value' - */ -static int cf_callback_mode (const char *shortvar, const char *var, - const char *arguments, const char *value, lc_flags_t flags, - void *extra) +static int dispatch_block (oconfig_item_t *ci) { - DEBUG_CALLBACK (shortvar, var, arguments, value); - - if (strcasecmp (value, "Client") == 0) - operating_mode = MODE_CLIENT; -#if HAVE_LIBRRD - else if (strcasecmp (value, "Server") == 0) - operating_mode = MODE_SERVER; - else if (strcasecmp (value, "Local") == 0) - operating_mode = MODE_LOCAL; -#else /* !HAVE_LIBRRD */ - else if (strcasecmp (value, "Server") == 0) + if (strcasecmp (ci->key, "Plugin") == 0) + 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); +} + +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++) { - fprintf (stderr, "Invalid mode `Server': " - "You need to link against librrd for this " - "mode to be available.\n"); - syslog (LOG_ERR, "Invalid mode `Server': " - "You need to link against librrd for this " - "mode to be available."); - return (LC_CBRET_ERROR); + if (temp->values[i].type == OCONFIG_TYPE_STRING) + { + sfree (temp->values[i].value.string); + } } - else if (strcasecmp (value, "Local") == 0) + 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))) { - fprintf (stderr, "Invalid mode `Local': " - "You need to link against librrd for this " - "mode to be available.\n"); - syslog (LOG_ERR, "Invalid mode `Local': " - "You need to link against librrd for this " - "mode to be available."); - return (LC_CBRET_ERROR); + int nmemb = src->children_num - (offset + 1); + memmove (src->children + offset, src->children + offset + 1, + sizeof (oconfig_item_t) * nmemb); } -#endif - else if (strcasecmp (value, "Log") == 0) - operating_mode = MODE_LOG; - else + + /* 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) { - syslog (LOG_ERR, "Invalid value for config option `Mode': `%s'", value); - return (LC_CBRET_ERROR); + 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; - return (LC_CBRET_OKAY); -} + memmove (dst->children + new_offset, + dst->children + old_offset, + sizeof (oconfig_item_t) * nmemb); + } -/* - * `cf_callback_mode_plugindir' - * Change the plugin directory - * - * - * PluginDir `value' - * - */ -static int cf_callback_mode_plugindir (const char *shortvar, const char *var, - const char *arguments, const char *value, lc_flags_t flags, - void *extra) + /* 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) { - DEBUG_CALLBACK (shortvar, var, arguments, value); + oconfig_item_t *temp; - plugin_set_dir (value); + if ((src == NULL) || (src->children_num == 0)) + return (0); - return (LC_CBRET_OKAY); -} + 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_callback_mode_option (const char *shortvar, const char *var, - const char *arguments, const char *value, lc_flags_t flags, - void *extra) +static int cf_include_all (oconfig_item_t *root, int depth) { - cf_mode_item_t *item; + 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; - DEBUG_CALLBACK (shortvar, var, arguments, value); + /* Now replace the i'th child in `root' with `new'. */ + cf_ci_replace_child (root, new, i); - if (extra == NULL) + 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) { - fprintf (stderr, "No extra..?\n"); - return (LC_CBRET_ERROR); + ERROR ("configfile: Cannot read file `%s'.", file); + return (NULL); } - item = (cf_mode_item_t *) extra; + 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); - if (strcasecmp (item->key, shortvar)) + dh = opendir (dir); + if (dh == NULL) { - fprintf (stderr, "Wrong extra..\n"); - return (LC_CBRET_ERROR); + char errbuf[1024]; + ERROR ("configfile: opendir failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (NULL); } - if ((operating_mode & item->mode) == 0) + root = (oconfig_item_t *) malloc (sizeof (oconfig_item_t)); + if (root == NULL) { - fprintf (stderr, "Option `%s' is not valid in this mode!\n", shortvar); - return (LC_CBRET_ERROR); + ERROR ("configfile: malloc failed."); + return (NULL); } + memset (root, '\0', sizeof (oconfig_item_t)); - if (item->value != NULL) + while ((de = readdir (dh)) != NULL) { - free (item->value); - item->value = NULL; + char name[1024]; + char **tmp; + + if ((de->d_name[0] == '.') || (de->d_name[0] == '\0')) + continue; + + status = ssnprintf (name, sizeof (name), "%s/%s", + dir, de->d_name); + if ((status < 0) || ((size_t) 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); } - if ((item->value = strdup (value)) == NULL) + qsort ((void *) filenames, filenames_num, sizeof (*filenames), + cf_compare_string); + + for (i = 0; i < filenames_num; ++i) { - perror ("strdup"); - return (LC_CBRET_ERROR); + 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); } - return (LC_CBRET_OKAY); -} + free(filenames); + return (root); +} /* oconfig_item_t *cf_read_dir */ -/* - * `cf_callback_mode_loadmodule': - * Load a plugin. +/* + * cf_read_generic + * + * Path is stat'ed and either cf_read_file or cf_read_dir is called + * accordingly. * - * - * LoadPlugin `value' - * + * 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. */ -static int cf_callback_mode_loadmodule (const char *shortvar, const char *var, - const char *arguments, const char *value, lc_flags_t flags, - void *extra) +#if HAVE_WORDEXP_H +static oconfig_item_t *cf_read_generic (const char *path, int depth) { - DEBUG_CALLBACK (shortvar, var, arguments, value); + oconfig_item_t *root = NULL; + int status; + const char *path_ptr; + wordexp_t we; + size_t i; - if (plugin_load (value)) - syslog (LOG_ERR, "plugin_load (%s): failed to load plugin", value); + if (depth >= CF_MAX_DEPTH) + { + ERROR ("configfile: Not including `%s' because the maximum " + "nesting depth has been reached.", path); + return (NULL); + } - /* Return `okay' even if there was an error, because it's not a syntax - * problem.. */ - return (LC_CBRET_OKAY); -} + status = wordexp (path, &we, WRDE_NOCMD); + if (status != 0) + { + ERROR ("configfile: wordexp (%s) failed.", path); + return (NULL); + } -/* - * `cf_callback_plugin' - * Start/end section `plugin' - * - * - * ... - * - */ -static int cf_callback_plugin (const char *shortvar, const char *var, - const char *arguments, const char *value, lc_flags_t flags, - void *extra) -{ - DEBUG_CALLBACK (shortvar, var, arguments, value); + 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); - if (flags == LC_FLAGS_SECTIONSTART) + for (i = 0; i < we.we_wordc; i++) { - if (nesting_depth != 0) - { - fprintf (stderr, ERR_NOT_NESTED); - return (LC_CBRET_ERROR); - } + oconfig_item_t *temp; + struct stat statbuf; - if (arguments == NULL) + path_ptr = we.we_wordv[i]; + + status = stat (path_ptr, &statbuf); + if (status != 0) { - fprintf (stderr, ERR_NEEDS_ARG, shortvar); - return (LC_CBRET_ERROR); + char errbuf[1024]; + ERROR ("configfile: stat (%s) failed: %s", + path_ptr, + sstrerror (errno, errbuf, sizeof (errbuf))); + oconfig_free (root); + return (NULL); } - if ((current_module = strdup (arguments)) == 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 { - perror ("strdup"); - return (LC_CBRET_ERROR); + ERROR ("configfile: %s is neither a file nor a " + "directory.", path); + continue; } - nesting_depth++; + if (temp == NULL) { + oconfig_free (root); + return (NULL); + } - if (cf_search (current_module) != NULL) - return (LC_CBRET_OKAY); - else - return (LC_CBRET_IGNORESECTION); + cf_ci_append_children (root, temp); + sfree (temp->children); + sfree (temp); } - else if (flags == LC_FLAGS_SECTIONEND) - { - if (current_module != NULL) - { - free (current_module); - current_module = NULL; - } - nesting_depth--; + wordfree (&we); + + return (root); +} /* oconfig_item_t *cf_read_generic */ +/* #endif HAVE_WORDEXP_H */ - return (LC_CBRET_OKAY); +#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); } - else + + status = stat (path, &statbuf); + if (status != 0) { - fprintf (stderr, ERR_SECTION_ONLY, shortvar); - return (LC_CBRET_ERROR); + char errbuf[1024]; + ERROR ("configfile: stat (%s) failed: %s", + path, + sstrerror (errno, errbuf, sizeof (errbuf))); + return (NULL); } -} -/* - * `cf_callback_plugin_dispatch' - * Send options within `plugin' sections to the plugin that requests it. - * - * - * `var' `value' - * + 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 */ -static int cf_callback_plugin_dispatch (const char *shortvar, const char *var, - const char *arguments, const char *value, lc_flags_t flags, - void *extra) +int global_option_set (const char *option, const char *value) { - DEBUG_CALLBACK (shortvar, var, arguments, value); + int i; - if ((nesting_depth == 0) || (current_module == NULL)) - { - fprintf (stderr, ERR_NEEDS_SECTION, shortvar); - return (LC_CBRET_ERROR); - } + DEBUG ("option = %s; value = %s;", option, value); + + for (i = 0; i < cf_global_options_num; i++) + if (strcasecmp (cf_global_options[i].key, option) == 0) + break; + + if (i >= cf_global_options_num) + return (-1); + + sfree (cf_global_options[i].value); - /* Send the data to the plugin */ - if (cf_dispatch (current_module, shortvar, value) < 0) - return (LC_CBRET_ERROR); + if (value != NULL) + cf_global_options[i].value = strdup (value); + else + cf_global_options[i].value = NULL; - return (LC_CBRET_OKAY); + return (0); } -static void cf_init (void) +const char *global_option_get (const char *option) { - static int run_once = 0; int i; - if (run_once != 0) + for (i = 0; i < cf_global_options_num; i++) + if (strcasecmp (cf_global_options[i].key, option) == 0) + break; + + if (i >= cf_global_options_num) + return (NULL); + + return ((cf_global_options[i].value != NULL) + ? cf_global_options[i].value + : cf_global_options[i].def); +} /* char *global_option_get */ + +void cf_unregister (const char *type) +{ + cf_callback_t *this, *prev; + + for (prev = NULL, this = first_callback; + this != NULL; + prev = this, this = this->next) + if (strcasecmp (this->type, type) == 0) + { + if (prev == NULL) + first_callback = this->next; + else + prev->next = this->next; + + free (this); + break; + } +} /* void cf_unregister */ + +void cf_unregister_complex (const char *type) +{ + cf_complex_callback_t *this, *prev; + + for (prev = NULL, this = complex_callback_head; + this != NULL; + prev = this, this = this->next) + if (strcasecmp (this->type, type) == 0) + { + if (prev == NULL) + complex_callback_head = this->next; + else + prev->next = this->next; + + sfree (this->type); + sfree (this); + break; + } +} /* void cf_unregister */ + +void cf_register (const char *type, + int (*callback) (const char *, const char *), + const char **keys, int keys_num) +{ + cf_callback_t *cf_cb; + + /* Remove this module from the list, if it already exists */ + cf_unregister (type); + + /* This pointer will be free'd in `cf_unregister' */ + if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL) return; - run_once = 1; - lc_register_callback ("Mode", SHORTOPT_NONE, LC_VAR_STRING, - cf_callback_mode, NULL); - lc_register_callback ("Plugin", SHORTOPT_NONE, LC_VAR_SECTION, - cf_callback_plugin, NULL); + cf_cb->type = type; + cf_cb->callback = callback; + cf_cb->keys = keys; + cf_cb->keys_num = keys_num; + + cf_cb->next = first_callback; + first_callback = cf_cb; +} /* void cf_register */ + +int cf_register_complex (const char *type, int (*callback) (oconfig_item_t *)) +{ + cf_complex_callback_t *new; - lc_register_callback ("PluginDir", SHORTOPT_NONE, - LC_VAR_STRING, cf_callback_mode_plugindir, NULL); - lc_register_callback ("LoadPlugin", SHORTOPT_NONE, - LC_VAR_STRING, cf_callback_mode_loadmodule, NULL); + new = (cf_complex_callback_t *) malloc (sizeof (cf_complex_callback_t)); + if (new == NULL) + return (-1); - for (i = 0; i < cf_mode_num; i++) + new->type = strdup (type); + if (new->type == NULL) { - cf_mode_item_t *item; + sfree (new); + return (-1); + } - item = &cf_mode_list[i]; + new->callback = callback; + new->next = NULL; - lc_register_callback (item->key, SHORTOPT_NONE, LC_VAR_STRING, - cf_callback_mode_option, (void *) item); + if (complex_callback_head == NULL) + { + complex_callback_head = new; + } + else + { + cf_complex_callback_t *last = complex_callback_head; + while (last->next != NULL) + last = last->next; + last->next = new; } -} + + return (0); +} /* int cf_register_complex */ int cf_read (char *filename) { - cf_init (); - - if (filename == NULL) - filename = CONFIGFILE; - - DBG ("Starting to parse file `%s'", filename); + oconfig_item_t *conf; + int i; - /* int lc_process_file(const char *appname, const char *pathname, lc_conf_type_t type); */ - if (lc_process_file ("collectd", filename, LC_CONF_APACHE)) + conf = cf_read_generic (filename, 0 /* depth */); + if (conf == NULL) { - syslog (LOG_ERR, "lc_process_file (%s): %s", filename, lc_geterrstr ()); + ERROR ("Unable to read config file %s.", filename); return (-1); } - DBG ("Done parsing file `%s'", filename); + for (i = 0; i < conf->children_num; i++) + { + if (conf->children[i].children == NULL) + dispatch_value (conf->children + i); + else + dispatch_block (conf->children + i); + } - /* free memory and stuff */ - lc_cleanup (); + oconfig_free (conf); + + /* Read the default types.db if no `TypesDB' option was given. */ + if (cf_default_typesdb) + read_types_list (PKGDATADIR"/types.db"); return (0); -} +} /* int cf_read */