/**
* collectd - src/configfile.c
- * Copyright (C) 2005 Florian octo Forster
+ * Copyright (C) 2005,2006 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
* Florian octo Forster <octo at verplant.org>
**/
-/*
- * FIXME:
- * - remove all (I mean *ALL*) calls to `fprintf': `stderr' will have been
- * closed.
- */
-
#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"
-#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"
-
-#ifdef HAVE_LIBRRD
-extern int operating_mode;
-#else
-static int operating_mode = MODE_LOCAL;
-#endif
+#define ESCAPE_NULL(str) ((str) == NULL ? "(null)" : (str))
+/*
+ * Private types
+ */
typedef struct cf_callback
{
- char *type;
- int (*callback) (char *, char *);
- char **keys;
+ const char *type;
+ int (*callback) (const char *, const char *);
+ const char **keys;
int keys_num;
struct cf_callback *next;
} cf_callback_t;
+typedef struct cf_value_map_s
+{
+ char *key;
+ int (*func) (const oconfig_item_t *);
+} cf_value_map_t;
+
+typedef struct cf_global_option_s
+{
+ char *key;
+ char *value;
+ char *def;
+} cf_global_option_t;
+
+/*
+ * Prototypes of callback functions
+ */
+static int dispatch_value_pidfile (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 int nesting_depth = 0;
-static char *current_module = NULL;
+static cf_value_map_t cf_value_map[] =
+{
+ {"PIDFile", dispatch_value_pidfile},
+ {"PluginDir", dispatch_value_plugindir},
+ {"LoadPlugin", dispatch_value_loadplugin}
+};
+static int cf_value_map_num = STATIC_ARRAY_LEN (cf_value_map);
-/* cf_register needs this prototype */
-int cf_callback_general (const char *, const char *, const char *,
- const char *, lc_flags_t, void *);
+static cf_global_option_t cf_global_options[] =
+{
+ {"BaseDir", NULL, PKGLOCALSTATEDIR},
+ {"LogFile", NULL, LOGFILE},
+ {"PIDFile", NULL, PIDFILE}
+};
+static int cf_global_options_num = STATIC_ARRAY_LEN (cf_global_options);
/*
- * Functions to handle register/unregister, search, ...
+ * Functions to handle register/unregister, search, and other plugin related
+ * stuff
*/
-cf_callback_t *cf_search (char *type)
+static cf_callback_t *cf_search (const char *type)
{
cf_callback_t *cf_cb;
return (cf_cb);
}
-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;
int ret;
int i;
+ DBG ("type = %s, key = %s, value = %s",
+ ESCAPE_NULL(type),
+ ESCAPE_NULL(orig_key),
+ ESCAPE_NULL(orig_value));
+
if ((cf_cb = cf_search (type)) == NULL)
{
- fprintf (stderr, "Plugin `%s' did not register a callback.\n", type);
+ syslog (LOG_WARNING, "Plugin `%s' did not register a callback.", type);
return (-1);
}
}
if (i >= cf_cb->keys_num)
- fprintf (stderr, "Plugin `%s' did not register for value `%s'.\n", type, key);
+ syslog (LOG_WARNING, "Plugin `%s' did not register for value `%s'.", type, key);
free (key);
free (value);
+ DBG ("return (%i)", ret);
+
return (ret);
}
-void cf_unregister (char *type)
+static int dispatch_value_pidfile (const oconfig_item_t *ci)
{
- 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;
+ assert (strcasecmp (ci->key, "PIDFile") == 0);
+
+ if (ci->values_num != 1)
+ return (-1);
+ if (ci->values[0].type != OCONFIG_TYPE_STRING)
+ return (-1);
- free (this);
- break;
- }
+ return (global_option_set ("PIDFile", ci->values[0].value.string));
}
-void cf_register (char *type,
- int (*callback) (char *, char *),
- char **keys, int keys_num)
+static int dispatch_value_plugindir (const oconfig_item_t *ci)
{
- cf_callback_t *cf_cb;
- char buf[64];
- int i;
-
- /* Remove this module from the list, if it already exists */
- cf_unregister (type);
+ assert (strcasecmp (ci->key, "PluginDir") == 0);
+
+ 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;
+ plugin_set_dir (ci->values[0].value.string);
+ return (0);
+}
- cf_cb->type = type;
- cf_cb->callback = callback;
- cf_cb->keys = keys;
- cf_cb->keys_num = keys_num;
+static int dispatch_value_loadplugin (const oconfig_item_t *ci)
+{
+ assert (strcasecmp (ci->key, "LoadPlugin") == 0);
- cf_cb->next = first_callback;
- first_callback = cf_cb;
+ if (ci->values_num != 1)
+ return (-1);
+ if (ci->values[0].type != OCONFIG_TYPE_STRING)
+ return (-1);
- for (i = 0; i < keys_num; i++)
- {
- if (snprintf (buf, 64, "Plugin.%s", keys[i]) < 64)
- {
- /* 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_general,
- NULL);
- }
- else
- {
- DBG ("Key was truncated: `%s'", keys[i]);
- }
- }
-}
+ return (plugin_load (ci->values[0].value.string));
+} /* int dispatch_value_loadplugin */
-/*
- * Functions for the actual parsing
- */
-int cf_callback_general (const char *shortvar, const char *var,
- const char *arguments, const char *value, lc_flags_t flags,
- void *extra)
+static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci)
{
- DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
- shortvar, var, arguments, value);
+ char buffer[4096];
+ char *buffer_ptr;
+ int buffer_free;
+ int i;
+
+ buffer_ptr = buffer;
+ buffer_free = sizeof (buffer);
- if ((nesting_depth == 0) || (current_module == NULL))
+ for (i = 0; i < ci->values_num; i++)
{
- fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
- return (LC_CBRET_ERROR);
+ int status = -1;
+
+ if (ci->values[i].type == OCONFIG_TYPE_STRING)
+ status = snprintf (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",
+ ci->values[i].value.number);
+ else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
+ status = snprintf (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;
- /* Send the data to the plugin */
- if (cf_dispatch (current_module, shortvar, value) < 0)
- return (LC_CBRET_ERROR);
-
- return (LC_CBRET_OKAY);
-}
+ return (cf_dispatch (plugin, ci->key, buffer_ptr));
+} /* int plugin_conf_dispatch */
-int cf_callback_section_mode (const char *shortvar, const char *var,
- const char *arguments, const char *value, lc_flags_t flags,
- void *extra)
+static int dispatch_value (const oconfig_item_t *ci)
{
- DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
- shortvar, var, arguments, value);
+ int ret = -2;
+ int i;
- if (flags == LC_FLAGS_SECTIONSTART)
- {
- if (nesting_depth != 0)
+ for (i = 0; i < cf_value_map_num; i++)
+ if (strcasecmp (cf_value_map[i].key, ci->key) == 0)
{
- fprintf (stderr, ERR_NOT_NESTED);
- return (LC_CBRET_ERROR);
+ ret = cf_value_map[i].func (ci);
+ break;
}
- if (arguments == NULL)
- {
- fprintf (stderr, ERR_NEEDS_ARG, shortvar);
- return (LC_CBRET_ERROR);
- }
+ return (ret);
+} /* int dispatch_value */
- nesting_depth++;
+static int dispatch_block_plugin (oconfig_item_t *ci)
+{
+ int i;
+ char *name;
- if (((operating_mode == MODE_CLIENT)
- && (strcasecmp (arguments, "Client") == 0))
- || ((operating_mode == MODE_SERVER)
- && (strcasecmp (arguments, "Server") == 0))
- || ((operating_mode == MODE_LOCAL)
- && (strcasecmp (arguments, "Local") == 0)))
- {
- return (LC_CBRET_OKAY);
- }
- else
- {
- return (LC_CBRET_IGNORESECTION);
- }
- }
- else if (flags == LC_FLAGS_SECTIONEND)
- {
- nesting_depth--;
+ 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);
- return (LC_CBRET_OKAY);
- }
- else
+ name = ci->values[0].value.string;
+
+ for (i = 0; i < ci->children_num; i++)
{
- fprintf (stderr, ERR_SECTION_ONLY, shortvar);
- return (LC_CBRET_ERROR);
+ if (ci->children[i].children == NULL)
+ dispatch_value_plugin (name, ci->children + i);
+ else
+ {DBG ("No nested config blocks allow for plugins. Yet.");}
}
+ return (0);
}
-int cf_callback_section_module (const char *shortvar, const char *var,
- const char *arguments, const char *value, lc_flags_t flags,
- void *extra)
-{
- DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
- shortvar, var, arguments, value);
- if (flags == LC_FLAGS_SECTIONSTART)
- {
- if (nesting_depth != 0)
- {
- fprintf (stderr, ERR_NOT_NESTED);
- return (LC_CBRET_ERROR);
- }
+static int dispatch_block (oconfig_item_t *ci)
+{
+ if (strcasecmp (ci->key, "Plugin") == 0)
+ return (dispatch_block_plugin (ci));
- if (arguments == NULL)
- {
- fprintf (stderr, ERR_NEEDS_ARG, shortvar);
- return (LC_CBRET_ERROR);
- }
+ return (0);
+}
- if ((current_module = strdup (arguments)) == NULL)
- {
- perror ("strdup");
- return (LC_CBRET_ERROR);
- }
+/*
+ * Public functions
+ */
+int global_option_set (const char *option, const char *value)
+{
+ int i;
- nesting_depth++;
+ for (i = 0; i < cf_global_options_num; i++)
+ if (strcasecmp (cf_global_options[i].key, option) == 0)
+ break;
- if (cf_search (current_module) != NULL)
- return (LC_CBRET_OKAY);
- else
- return (LC_CBRET_IGNORESECTION);
- }
- else if (flags == LC_FLAGS_SECTIONEND)
- {
- if (current_module != NULL)
- {
- free (current_module);
- current_module = NULL;
- }
+ if (i >= cf_global_options_num)
+ return (-1);
- nesting_depth--;
+ if (cf_global_options[i].value != NULL)
+ free (cf_global_options[i].value);
- return (LC_CBRET_OKAY);
- }
+ if (value != NULL)
+ cf_global_options[i].value = strdup (value);
else
- {
- fprintf (stderr, ERR_SECTION_ONLY, shortvar);
- return (LC_CBRET_ERROR);
- }
+ cf_global_options[i].value = NULL;
+
+ return (0);
}
-int cf_callback_loadmodule (const char *shortvar, const char *var,
- const char *arguments, const char *value, lc_flags_t flags,
- void *extra)
+const char *global_option_get (const char *option)
{
- DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
- shortvar, var, arguments, value);
+ int i;
- if (nesting_depth == 0)
- {
- fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
- return (LC_CBRET_ERROR);
- }
+ 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 */
- if (plugin_load (value))
- syslog (LOG_ERR, "plugin_load (%s): failed to load plugin", shortvar);
+void cf_unregister (const char *type)
+{
+ cf_callback_t *this, *prev;
- /* Return `okay' even if there was an error, because it's not a syntax
- * problem.. */
- return (LC_CBRET_OKAY);
+ 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;
+ }
}
-int cf_read (char *filename)
+void cf_register (const char *type,
+ int (*callback) (const char *, const char *),
+ const char **keys, int keys_num)
{
- if (filename == NULL)
- filename = CONFIGFILE;
+ cf_callback_t *cf_cb;
- lc_register_callback ("Mode", SHORTOPT_NONE, LC_VAR_SECTION,
- cf_callback_section_mode, NULL);
- lc_register_callback ("Plugin", SHORTOPT_NONE, LC_VAR_SECTION,
- cf_callback_section_module, NULL);
+ /* 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;
+
+ cf_cb->type = type;
+ cf_cb->callback = callback;
+ cf_cb->keys = keys;
+ cf_cb->keys_num = keys_num;
- /*
- * TODO:
- * - Add more directives, such as `DefaultMode', `DataDir', `PIDFile', ...
- */
+ cf_cb->next = first_callback;
+ first_callback = cf_cb;
+} /* void cf_register */
- lc_register_callback ("Mode.LoadPlugin", SHORTOPT_NONE,
- LC_VAR_STRING, cf_callback_loadmodule,
- NULL);
+int cf_read (char *filename)
+{
+ oconfig_item_t *conf;
+ int i;
- if (lc_process_file ("collectd", filename, LC_CONF_APACHE))
+ conf = oconfig_parse_file (filename);
+ if (conf == NULL)
{
- syslog (LOG_ERR, "lc_process_file (%s): %s", filename, lc_geterrstr ());
+ syslog (LOG_ERR, "Unable to read config file %s.", filename);
return (-1);
}
- /* free memory and stuff */
- lc_cleanup ();
+ 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);
+ }
return (0);
-}
+} /* int cf_read */