# include <wordexp.h>
#endif /* HAVE_WORDEXP_H */
+#if HAVE_FNMATCH_H
+# include <fnmatch.h>
+#endif /* HAVE_FNMATCH_H */
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif /* HAVE_LIBGEN_H */
+
#define ESCAPE_NULL(str) ((str) == NULL ? "(null)" : (str))
/*
int (*callback) (const char *, const char *);
const char **keys;
int keys_num;
+ plugin_ctx_t ctx;
struct cf_callback *next;
} cf_callback_t;
{
char *type;
int (*callback) (oconfig_item_t *);
+ plugin_ctx_t ctx;
struct cf_complex_callback_s *next;
} cf_complex_callback_t;
typedef struct cf_value_map_s
{
char *key;
- int (*func) (const oconfig_item_t *);
+ int (*func) (oconfig_item_t *);
} cf_value_map_t;
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_loadplugin (const oconfig_item_t *ci);
+static int dispatch_value_typesdb (oconfig_item_t *ci);
+static int dispatch_value_plugindir (oconfig_item_t *ci);
+static int dispatch_loadplugin (oconfig_item_t *ci);
+static int dispatch_block_plugin (oconfig_item_t *ci);
/*
* Private variables
{
{"TypesDB", dispatch_value_typesdb},
{"PluginDir", dispatch_value_plugindir},
- {"LoadPlugin", dispatch_loadplugin}
+ {"LoadPlugin", dispatch_loadplugin},
+ {"Plugin", dispatch_block_plugin}
};
-static int cf_value_map_num = STATIC_ARRAY_LEN (cf_value_map);
+static int cf_value_map_num = STATIC_ARRAY_SIZE (cf_value_map);
static cf_global_option_t cf_global_options[] =
{
{"PIDFile", NULL, PIDFILE},
{"Hostname", NULL, NULL},
{"FQDNLookup", NULL, "true"},
- {"Interval", NULL, "10"},
+ {"Interval", NULL, NULL},
{"ReadThreads", NULL, "5"},
+ {"WriteThreads", NULL, "5"},
+ {"WriteQueueLimitHigh", NULL, NULL},
+ {"WriteQueueLimitLow", NULL, NULL},
{"Timeout", NULL, "2"},
+ {"AutoLoadPlugin", NULL, "false"},
{"PreCacheChain", NULL, "PreCache"},
{"PostCacheChain", NULL, "PostCache"}
};
-static int cf_global_options_num = STATIC_ARRAY_LEN (cf_global_options);
+static int cf_global_options_num = STATIC_ARRAY_SIZE (cf_global_options);
static int cf_default_typesdb = 1;
const char *orig_value)
{
cf_callback_t *cf_cb;
+ plugin_ctx_t old_ctx;
char *key;
char *value;
int ret;
int i;
+ if (orig_key == NULL)
+ return (EINVAL);
+
DEBUG ("type = %s, key = %s, value = %s",
ESCAPE_NULL(type),
- ESCAPE_NULL(orig_key),
+ orig_key,
ESCAPE_NULL(orig_value));
if ((cf_cb = cf_search (type)) == NULL)
ret = -1;
+ old_ctx = plugin_set_ctx (cf_cb->ctx);
+
for (i = 0; i < cf_cb->keys_num; i++)
{
if ((cf_cb->keys[i] != NULL)
}
}
+ plugin_set_ctx (old_ctx);
+
if (i >= cf_cb->keys_num)
WARNING ("Plugin `%s' did not register for value `%s'.", type, key);
free (key);
free (value);
- DEBUG ("cf_dispatch: return (%i)", ret);
-
return (ret);
} /* int cf_dispatch */
return (-1);
} /* int dispatch_global_option */
-static int dispatch_value_typesdb (const oconfig_item_t *ci)
+static int dispatch_value_typesdb (oconfig_item_t *ci)
{
int i = 0;
return (0);
} /* int dispatch_value_typesdb */
-static int dispatch_value_plugindir (const oconfig_item_t *ci)
+static int dispatch_value_plugindir (oconfig_item_t *ci)
{
assert (strcasecmp (ci->key, "PluginDir") == 0);
return (0);
}
-static int dispatch_loadplugin (const oconfig_item_t *ci)
+static int dispatch_loadplugin (oconfig_item_t *ci)
{
int i;
const char *name;
unsigned int flags = 0;
+ plugin_ctx_t ctx;
+ plugin_ctx_t old_ctx;
+ int ret_val;
+
assert (strcasecmp (ci->key, "LoadPlugin") == 0);
if (ci->values_num != 1)
name = ci->values[0].value.string;
- /*
- * XXX: Magic at work:
- *
- * Some of the language bindings, for example the Python and Perl
- * plugins, need to be able to export symbols to the scripts they run.
- * For this to happen, the "Globals" flag needs to be set.
- * Unfortunately, this technical detail is hard to explain to the
- * average user and she shouldn't have to worry about this, ideally.
- * So in order to save everyone's sanity use a different default for a
- * handful of special plugins. --octo
- */
- if ((strcasecmp ("Perl", name) == 0)
- || (strcasecmp ("Python", name) == 0))
- flags |= PLUGIN_FLAGS_GLOBAL;
+ /* default to the global interval set before loading this plugin */
+ memset (&ctx, 0, sizeof (ctx));
+ ctx.interval = cf_get_default_interval ();
for (i = 0; i < ci->children_num; ++i) {
if (strcasecmp("Globals", ci->children[i].key) == 0)
cf_util_get_flag (ci->children + i, &flags, PLUGIN_FLAGS_GLOBAL);
+ else if (strcasecmp ("Interval", ci->children[i].key) == 0) {
+ double interval = 0.0;
+
+ if (cf_util_get_double (ci->children + i, &interval) != 0) {
+ /* cf_util_get_double will log an error */
+ continue;
+ }
+
+ ctx.interval = DOUBLE_TO_CDTIME_T (interval);
+ }
else {
WARNING("Ignoring unknown LoadPlugin option \"%s\" "
"for plugin \"%s\"",
}
}
- return (plugin_load (name, (uint32_t) flags));
+ old_ctx = plugin_set_ctx (ctx);
+ ret_val = plugin_load (name, (uint32_t) flags);
+ /* reset to the "global" context */
+ plugin_set_ctx (old_ctx);
+
+ return (ret_val);
} /* int dispatch_value_loadplugin */
static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci)
return (cf_dispatch (plugin, ci->key, buffer_ptr));
} /* int dispatch_value_plugin */
-static int dispatch_value (const oconfig_item_t *ci)
+static int dispatch_value (oconfig_item_t *ci)
{
int ret = -2;
int i;
name = ci->values[0].value.string;
+ if (IS_TRUE (global_option_get ("AutoLoadPlugin")))
+ {
+ plugin_ctx_t ctx;
+ plugin_ctx_t old_ctx;
+ int status;
+
+ /* default to the global interval set before loading this plugin */
+ memset (&ctx, 0, sizeof (ctx));
+ ctx.interval = cf_get_default_interval ();
+
+ old_ctx = plugin_set_ctx (ctx);
+ status = plugin_load (name, /* flags = */ 0);
+ /* reset to the "global" context */
+ plugin_set_ctx (old_ctx);
+
+ if (status != 0)
+ {
+ ERROR ("Automatically loading plugin \"%s\" failed "
+ "with status %i.", name, status);
+ return (status);
+ }
+ }
+
/* 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));
+ {
+ plugin_ctx_t old_ctx;
+ int ret_val;
+
+ old_ctx = plugin_set_ctx (cb->ctx);
+ ret_val = (cb->callback (ci));
+ plugin_set_ctx (old_ctx);
+ return (ret_val);
+ }
+ }
/* Hm, no complex plugin found. Dispatch the values one by one */
for (i = 0; i < ci->children_num; i++)
/* Resize the memory containing the children to be big enough to hold
* all children. */
+ if (dst->children_num + src->children_num - 1 == 0)
+ {
+ dst->children_num = 0;
+ return (0);
+ }
+
temp = (oconfig_item_t *) realloc (dst->children,
sizeof (oconfig_item_t)
* (dst->children_num + src->children_num - 1));
} /* int cf_ci_append_children */
#define CF_MAX_DEPTH 8
-static oconfig_item_t *cf_read_generic (const char *path, int depth);
+static oconfig_item_t *cf_read_generic (const char *path,
+ const char *pattern, int depth);
static int cf_include_all (oconfig_item_t *root, int depth)
{
oconfig_item_t *new;
oconfig_item_t *old;
- /* Ignore all blocks, including `Include' blocks. */
- if (root->children[i].children_num != 0)
- continue;
+ char *pattern = NULL;
+
+ int j;
if (strcasecmp (root->children[i].key, "Include") != 0)
continue;
continue;
}
- new = cf_read_generic (old->values[0].value.string, depth + 1);
+ for (j = 0; j < old->children_num; ++j)
+ {
+ oconfig_item_t *child = old->children + j;
+
+ if (strcasecmp (child->key, "Filter") == 0)
+ cf_util_get_string (child, &pattern);
+ else
+ ERROR ("configfile: Option `%s' not allowed in <Include> block.",
+ child->key);
+ }
+
+ new = cf_read_generic (old->values[0].value.string, pattern, depth + 1);
+ sfree (pattern);
+
if (new == NULL)
- continue;
+ return (-1);
/* Now replace the i'th child in `root' with `new'. */
- cf_ci_replace_child (root, new, i);
+ if (cf_ci_replace_child (root, new, i) < 0)
+ return (-1);
/* ... and go back to the new i'th child. */
--i;
return (0);
} /* int cf_include_all */
-static oconfig_item_t *cf_read_file (const char *file, int depth)
+static oconfig_item_t *cf_read_file (const char *file,
+ const char *pattern, int depth)
{
oconfig_item_t *root;
+ int status;
assert (depth < CF_MAX_DEPTH);
+ if (pattern != NULL) {
+#if HAVE_FNMATCH_H && HAVE_LIBGEN_H
+ char *tmp = sstrdup (file);
+ char *filename = basename (tmp);
+
+ if ((filename != NULL) && (fnmatch (pattern, filename, 0) != 0)) {
+ DEBUG ("configfile: Not including `%s' because it "
+ "does not match pattern `%s'.",
+ filename, pattern);
+ free (tmp);
+ return (NULL);
+ }
+
+ free (tmp);
+#else
+ ERROR ("configfile: Cannot apply pattern filter '%s' "
+ "to file '%s': functions basename() and / or "
+ "fnmatch() not available.", pattern, file);
+#endif /* HAVE_FNMATCH_H && HAVE_LIBGEN_H */
+ }
+
root = oconfig_parse_file (file);
if (root == NULL)
{
return (NULL);
}
- cf_include_all (root, depth);
+ status = cf_include_all (root, depth);
+ if (status != 0)
+ {
+ oconfig_free (root);
+ return (NULL);
+ }
return (root);
} /* oconfig_item_t *cf_read_file */
return strcmp (*(const char **) p1, *(const char **) p2);
}
-static oconfig_item_t *cf_read_dir (const char *dir, int depth)
+static oconfig_item_t *cf_read_dir (const char *dir,
+ const char *pattern, int depth)
{
oconfig_item_t *root = NULL;
DIR *dh;
filenames[filenames_num - 1] = sstrdup (name);
}
+ if (filenames == NULL)
+ return (root);
+
qsort ((void *) filenames, filenames_num, sizeof (*filenames),
cf_compare_string);
oconfig_item_t *temp;
char *name = filenames[i];
- temp = cf_read_generic (name, depth);
+ temp = cf_read_generic (name, pattern, depth);
if (temp == NULL)
{
/* An error should already have been reported. */
* 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)
+static oconfig_item_t *cf_read_generic (const char *path,
+ const char *pattern, int depth)
{
oconfig_item_t *root = NULL;
int status;
}
if (S_ISREG (statbuf.st_mode))
- temp = cf_read_file (path_ptr, depth);
+ temp = cf_read_file (path_ptr, pattern, depth);
else if (S_ISDIR (statbuf.st_mode))
- temp = cf_read_dir (path_ptr, depth);
+ temp = cf_read_dir (path_ptr, pattern, depth);
else
{
WARNING ("configfile: %s is neither a file nor a "
wordfree (&we);
- if (root->children == NULL)
- {
- oconfig_free (root);
- return (NULL);
- }
-
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)
+static oconfig_item_t *cf_read_generic (const char *path,
+ const char *pattern, int depth)
{
struct stat statbuf;
int status;
}
if (S_ISREG (statbuf.st_mode))
- return (cf_read_file (path, depth));
+ return (cf_read_file (path, pattern, depth));
else if (S_ISDIR (statbuf.st_mode))
- return (cf_read_dir (path, depth));
+ return (cf_read_dir (path, pattern, depth));
ERROR ("configfile: %s is neither a file nor a directory.", path);
return (NULL);
: cf_global_options[i].def);
} /* char *global_option_get */
+long global_option_get_long (const char *option, long default_value)
+{
+ const char *str;
+ long value;
+
+ str = global_option_get (option);
+ if (NULL == str)
+ return (default_value);
+
+ errno = 0;
+ value = strtol (str, /* endptr = */ NULL, /* base = */ 0);
+ if (errno != 0)
+ return (default_value);
+
+ return (value);
+} /* char *global_option_get_long */
+
+cdtime_t cf_get_default_interval (void)
+{
+ char const *str = global_option_get ("Interval");
+ double interval_double = COLLECTD_DEFAULT_INTERVAL;
+
+ if (str != NULL)
+ {
+ char *endptr = NULL;
+ double tmp = strtod (str, &endptr);
+
+ if ((endptr == NULL) || (endptr == str) || (*endptr != 0))
+ ERROR ("cf_get_default_interval: Unable to parse string \"%s\" "
+ "as number.", str);
+ else if (tmp <= 0.0)
+ ERROR ("cf_get_default_interval: Interval must be a positive number. "
+ "The current number is %g.", tmp);
+ else
+ interval_double = tmp;
+ }
+
+ return (DOUBLE_TO_CDTIME_T (interval_double));
+} /* }}} cdtime_t cf_get_default_interval */
+
void cf_unregister (const char *type)
{
cf_callback_t *this, *prev;
cf_cb->callback = callback;
cf_cb->keys = keys;
cf_cb->keys_num = keys_num;
+ cf_cb->ctx = plugin_get_ctx ();
cf_cb->next = first_callback;
first_callback = cf_cb;
new->callback = callback;
new->next = NULL;
+ new->ctx = plugin_get_ctx ();
+
if (complex_callback_head == NULL)
{
complex_callback_head = new;
oconfig_item_t *conf;
int i;
- conf = cf_read_generic (filename, 0 /* depth */);
+ conf = cf_read_generic (filename, /* pattern = */ NULL, /* depth = */ 0);
if (conf == NULL)
{
ERROR ("Unable to read config file %s.", filename);
return (-1);
}
+ else if (conf->children_num == 0)
+ {
+ ERROR ("Configuration file %s is empty.", filename);
+ oconfig_free (conf);
+ return (-1);
+ }
for (i = 0; i < conf->children_num; i++)
{
return (0);
} /* }}} int cf_util_get_int */
+int cf_util_get_double (const oconfig_item_t *ci, double *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_double: The %s option requires "
+ "exactly one numeric argument.", ci->key);
+ return (-1);
+ }
+
+ *ret_value = ci->values[0].value.number;
+
+ return (0);
+} /* }}} int cf_util_get_double */
+
int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
{
if ((ci == NULL) || (ret_bool == NULL))