/**
* collectd - src/configfile.c
- * Copyright (C) 2005-2008 Florian octo Forster
+ * Copyright (C) 2005-2010 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
*
* Authors:
* Florian octo Forster <octo at verplant.org>
+ * Sebastian tokkee Harl <sh at tokkee.org>
**/
#include "collectd.h"
#include "configfile.h"
#include "types_list.h"
#include "utils_threshold.h"
+#include "filter_chain.h"
#if HAVE_WORDEXP_H
# include <wordexp.h>
*/
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);
+static int dispatch_loadplugin (const oconfig_item_t *ci);
/*
* Private variables
{
{"TypesDB", dispatch_value_typesdb},
{"PluginDir", dispatch_value_plugindir},
- {"LoadPlugin", dispatch_value_loadplugin}
+ {"LoadPlugin", dispatch_loadplugin}
};
static int cf_value_map_num = STATIC_ARRAY_LEN (cf_value_map);
{"BaseDir", NULL, PKGLOCALSTATEDIR},
{"PIDFile", NULL, PIDFILE},
{"Hostname", NULL, NULL},
- {"FQDNLookup", NULL, "false"},
+ {"FQDNLookup", NULL, "true"},
{"Interval", NULL, "10"},
- {"ReadThreads", NULL, "5"}
+ {"ReadThreads", NULL, "5"},
+ {"Timeout", NULL, "2"},
+ {"PreCacheChain", NULL, "PreCache"},
+ {"PostCacheChain", NULL, "PostCache"}
};
static int cf_global_options_num = STATIC_ARRAY_LEN (cf_global_options);
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;
free (key);
free (value);
- DEBUG ("return (%i)", ret);
+ DEBUG ("cf_dispatch: return (%i)", ret);
return (ret);
} /* int cf_dispatch */
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)
cf_default_typesdb = 0;
- if (ci->values_num < 1)
+ 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)
+ 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);
}
-static int dispatch_value_loadplugin (const oconfig_item_t *ci)
+static int dispatch_loadplugin (const oconfig_item_t *ci)
{
+ int i;
+ uint32_t flags = 0;
assert (strcasecmp (ci->key, "LoadPlugin") == 0);
if (ci->values_num != 1)
if (ci->values[0].type != OCONFIG_TYPE_STRING)
return (-1);
- return (plugin_load (ci->values[0].value.string));
+ for (i = 0; i < ci->children_num; ++i) {
+ if (ci->children[i].values_num != 1 ||
+ ci->children[i].values[0].type != OCONFIG_TYPE_BOOLEAN) {
+ WARNING("Ignoring unknown LoadPlugin option %s for plugin %s", ci->children[i].key, ci->values[0].value.string);
+ continue;
+ }
+ if (strcasecmp(ci->children[i].key, "globals") == 0) {
+ flags |= PLUGIN_FLAGS_GLOBAL;
+ } else {
+ WARNING("Ignoring unknown LoadPlugin option %s for plugin %s", ci->children[i].key, ci->values[0].value.string);
+ }
+ }
+ return (plugin_load (ci->values[0].value.string, flags));
} /* int dispatch_value_loadplugin */
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");
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)
{
if (ci->children[i].children == NULL)
dispatch_value_plugin (name, ci->children + i);
else
- {DEBUG ("No nested config blocks allow for this plugin.");}
+ {
+ WARNING ("There is a `%s' block within the "
+ "configuration for the %s plugin. "
+ "The plugin either only expects "
+ "\"simple\" configuration statements "
+ "or wasn't loaded using `LoadPlugin'."
+ " Please check your configuration.",
+ ci->children[i].key, name);
+ }
}
return (0);
static int dispatch_block (oconfig_item_t *ci)
{
- if (strcasecmp (ci->key, "Plugin") == 0)
+ if (strcasecmp (ci->key, "LoadPlugin") == 0)
+ return (dispatch_loadplugin (ci));
+ else 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);
}
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
+ * is _not_ the last element, (offset < (dst->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)))
+ if ((src->children_num == 0) && (offset < (dst->children_num - 1)))
{
- int nmemb = src->children_num - (offset + 1);
- memmove (src->children + offset, src->children + offset + 1,
+ int nmemb = dst->children_num - (offset + 1);
+ memmove (dst->children + offset, dst->children + offset + 1,
sizeof (oconfig_item_t) * nmemb);
}
sizeof (oconfig_item_t) * nmemb);
}
- /* Last but not least: If there are new childrem, copy them to the
+ /* Last but not least: If there are new children, copy them to the
* memory reserved for them. */
if (src->children_num > 0)
{
/* Now replace the i'th child in `root' with `new'. */
cf_ci_replace_child (root, new, i);
+ /* ... and go back to the new i'th child. */
+ --i;
+
sfree (new->values);
sfree (new);
} /* for (i = 0; i < root->children_num; i++) */
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);
ERROR ("configfile: malloc failed.");
return (NULL);
}
- memset (root, '\0', sizeof (oconfig_item_t));
+ memset (root, 0, sizeof (oconfig_item_t));
while ((de = readdir (dh)) != NULL)
{
- oconfig_item_t *temp;
+ char name[1024];
+ char **tmp;
- if ((de->d_name[0] == '.') || (de->d_name[0] == '\0'))
+ 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))
+ if ((status < 0) || ((size_t) 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)
+ {
+ /* An error should already have been reported. */
+ sfree (name);
continue;
+ }
cf_ci_append_children (root, temp);
sfree (temp->children);
sfree (temp);
+
+ free (name);
}
+ free(filenames);
return (root);
} /* oconfig_item_t *cf_read_dir */
int status;
const char *path_ptr;
wordexp_t we;
- int i;
+ size_t i;
if (depth >= CF_MAX_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;
if (status != 0)
{
char errbuf[1024];
- ERROR ("configfile: stat (%s) failed: %s",
+ WARNING ("configfile: stat (%s) failed: %s",
path_ptr,
sstrerror (errno, errbuf, sizeof (errbuf)));
- return (NULL);
+ continue;
}
if (S_ISREG (statbuf.st_mode))
temp = cf_read_dir (path_ptr, depth);
else
{
- ERROR ("configfile: %s is neither a file nor a "
+ WARNING ("configfile: %s is neither a file nor a "
"directory.", path);
continue;
}
wordfree (&we);
+ if (root->children == NULL)
+ {
+ oconfig_free (root);
+ return (NULL);
+ }
+
return (root);
} /* oconfig_item_t *cf_read_generic */
/* #endif HAVE_WORDEXP_H */
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 */
+
+/* Assures the config option is a string, duplicates it and returns the copy in
+ * "ret_string". If necessary "*ret_string" is freed first. Returns zero upon
+ * success. */
+int cf_util_get_string (const oconfig_item_t *ci, char **ret_string) /* {{{ */
+{
+ char *string;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ ERROR ("cf_util_get_string: The %s option requires "
+ "exactly one string argument.", ci->key);
+ return (-1);
+ }
+
+ string = strdup (ci->values[0].value.string);
+ if (string == NULL)
+ return (-1);
+
+ if (*ret_string != NULL)
+ sfree (*ret_string);
+ *ret_string = string;
+
+ return (0);
+} /* }}} int cf_util_get_string */
+
+/* Assures the config option is a string and copies it to the provided buffer.
+ * Assures null-termination. */
+int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer, /* {{{ */
+ size_t buffer_size)
+{
+ if ((ci == NULL) || (buffer == NULL) || (buffer_size < 1))
+ return (EINVAL);
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ ERROR ("cf_util_get_string_buffer: The %s option requires "
+ "exactly one string argument.", ci->key);
+ return (-1);
+ }
+
+ strncpy (buffer, ci->values[0].value.string, buffer_size);
+ buffer[buffer_size - 1] = 0;
+
+ return (0);
+} /* }}} int cf_util_get_string_buffer */
+
+/* Assures the config option is a number and returns it as an int. */
+int cf_util_get_int (const oconfig_item_t *ci, int *ret_value) /* {{{ */
+{
+ if ((ci == NULL) || (ret_value == NULL))
+ return (EINVAL);
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+ {
+ ERROR ("cf_util_get_int: The %s option requires "
+ "exactly one numeric argument.", ci->key);
+ return (-1);
+ }
+
+ *ret_value = (int) ci->values[0].value.number;
+
+ return (0);
+} /* }}} int cf_util_get_int */
+
+int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
+{
+ if ((ci == NULL) || (ret_bool == NULL))
+ return (EINVAL);
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+ {
+ ERROR ("cf_util_get_boolean: The %s option requires "
+ "exactly one boolean argument.", ci->key);
+ return (-1);
+ }
+
+ *ret_bool = ci->values[0].value.boolean ? 1 : 0;
+
+ return (0);
+} /* }}} int cf_util_get_boolean */
+
+int cf_util_get_flag (const oconfig_item_t *ci, /* {{{ */
+ unsigned int *ret_value, unsigned int flag)
+{
+ int status;
+ _Bool b;
+
+ if (ret_value == NULL)
+ return (EINVAL);
+
+ b = 0;
+ status = cf_util_get_boolean (ci, &b);
+ if (status != 0)
+ return (status);
+
+ if (b)
+ {
+ *ret_value |= flag;
+ }
+ else
+ {
+ *ret_value &= ~flag;
+ }
+
+ return (0);
+} /* }}} int cf_util_get_flag */
+
+/* Assures that the config option is a string. The string is then converted to
+ * a port number using `service_name_to_port_number' and returned. Returns the
+ * port number in the range [1-65535] or less than zero upon failure. */
+int cf_util_get_port_number (const oconfig_item_t *ci) /* {{{ */
+{
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ ERROR ("cf_util_get_port_number: The %s option requires "
+ "exactly one string argument.", ci->key);
+ return (-1);
+ }
+
+ return (service_name_to_port_number (ci->values[0].value.string));
+} /* }}} int cf_util_get_port_number */