[svn-commits] twilson: branch twilson/config_work r361950 - in /team/twilson/config_work: a...
SVN commits to the Digium repositories
svn-commits at lists.digium.com
Wed Apr 11 18:28:56 CDT 2012
Author: twilson
Date: Wed Apr 11 18:28:52 2012
New Revision: 361950
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=361950
Log:
Handle simple configs with very little code
This definitely needs some cleanup, but I just wanted to save it before
tearing into it again.
Basically, this allows one to set a bunch of alloc callbacks and some
other info, then load a config that has no relations between global
(e.g. "general") and specific (e.g. "peers") and handle config loading
and reloading with a single function call.
I want to find a way to clean up having the void * return types in
some of the callbacks since that means sillyness on the module side
of things. I could just make things void * and do casting on the other
side, but I hate doing that. I could cast at the module side, but that
is ugly too. We'll see.
Right now I have hardcoded "general" as the category with global options,
it will be a regex when I'm done.
I may decide I hate this.
Modified:
team/twilson/config_work/apps/app_skel.c
team/twilson/config_work/include/asterisk/config_options.h
team/twilson/config_work/main/config_options.c
Modified: team/twilson/config_work/apps/app_skel.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_work/apps/app_skel.c?view=diff&rev=361950&r1=361949&r2=361950
==============================================================================
--- team/twilson/config_work/apps/app_skel.c (original)
+++ team/twilson/config_work/apps/app_skel.c Wed Apr 11 18:28:52 2012
@@ -140,9 +140,30 @@
};
static AO2_GLOBAL_OBJ_STATIC(global_config, NUM_GLOBAL_OBJECTS);
-AST_MUTEX_DEFINE_STATIC(global_config_transaction);
+AST_MUTEX_DEFINE_STATIC(reload_lock);
static struct ao2_container *config_opts;
+
+static void *skel_global_alloc(const char *cat);
+static void *skel_pvt_alloc(const char *cat);
+static void *skel_pvt_cfg_alloc(const char *cat);
+static struct ao2_container *skel_pvt_container_alloc(void);
+static struct ao2_container *skel_pvt_cfg_container_alloc(void);
+static void *skel_find_pvt_by_category(struct ao2_container *tmp_container, const char *category);
+static int skel_apply_config(void *new_global, struct ao2_container *new_pvt_container, struct ao2_container *new_cfg_container);
+
+static struct ast_config_option_info config_info = {
+ .module = AST_MODULE,
+ .filename = "app_skel.conf",
+ .global_contexts = "general",
+ .global_alloc = skel_global_alloc,
+ .pvt_alloc = skel_pvt_alloc,
+ .pvt_cfg_alloc = skel_pvt_cfg_alloc,
+ .pvt_container_alloc = skel_pvt_container_alloc,
+ .pvt_cfg_container_alloc = skel_pvt_cfg_container_alloc,
+ .find_pvt = skel_find_pvt_by_category,
+ .apply_config = skel_apply_config,
+};
static void skel_global_config_destructor(void *obj)
{
@@ -189,6 +210,25 @@
struct skel_pvt_config *one = obj, *two = arg;
const char *match = (flags & OBJ_KEY) ? arg : two->name;
return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
+}
+
+#define PVT_BUCKETS 17
+static struct ao2_container *skel_pvt_container_alloc(void)
+{
+ return ao2_container_alloc(PVT_BUCKETS, skel_pvt_hash, skel_pvt_cmp);
+}
+static struct ao2_container *skel_pvt_cfg_container_alloc(void)
+{
+ return ao2_container_alloc(PVT_BUCKETS, skel_pvt_config_hash, skel_pvt_config_cmp);
+}
+void *skel_find_pvt_by_category(struct ao2_container *tmp_container, const char *category)
+{
+ if (tmp_container) {
+ return ao2_find(tmp_container, category, OBJ_KEY);
+ } else {
+ RAII_VAR(struct ao2_container *, pvt_container, ao2_global_obj_ref(global_config, PVT_CONTAINER), ao2_cleanup);
+ return pvt_container ? ao2_find(pvt_container, category, OBJ_KEY) : NULL;
+ }
}
/*! \brief A custom bitfield handler
@@ -210,7 +250,7 @@
return 0;
}
-static void apply_config(struct skel_global_config *new_global, struct ao2_container *new_pvt_container, struct ao2_container *new_cfg_container)
+static int skel_apply_config(void *new_global, struct ao2_container *new_pvt_container, struct ao2_container *new_cfg_container)
{
void *old;
void *array[NUM_GLOBAL_OBJECTS];
@@ -220,10 +260,6 @@
array[PVT_CONTAINER] = new_pvt_container;
array[PVT_CFG_CONTAINER] = new_cfg_container;
- /* Instead of having a global lock here, I would prefer an ao2 global obj API
- * function that allows the entire array to be swapped out with its intenral
- * lock held. */
- ast_mutex_lock(&global_config_transaction);
for (i = 0; i < NUM_GLOBAL_OBJECTS; i++) {
if ((old = ao2_global_obj_replace(global_config, i, array[i]))) {
/* There was an existing config */
@@ -231,9 +267,9 @@
}
ao2_ref(array[i], -1);
}
- ast_mutex_unlock(&global_config_transaction);
ast_log(LOG_NOTICE, "Succesfully applied config!\n");
+ return 0;
}
static int app_exec(struct ast_channel *chan, const char *data)
@@ -281,7 +317,7 @@
return res;
}
-static struct skel_pvt *new_pvt(const char *name)
+static void *skel_pvt_alloc(const char *name)
{
struct skel_pvt *pvt;
@@ -297,7 +333,7 @@
return pvt;
}
-static struct skel_pvt_config *new_pvt_cfg(struct ast_config *cfg, const char *cat)
+static void *skel_pvt_cfg_alloc(const char *cat)
{
struct skel_pvt_config *pvtcfg;
@@ -322,55 +358,16 @@
return NULL;
}
- if (ast_config_parse_category_options(config_opts, cfg, cat, pvtcfg)) {
- ao2_ref(pvtcfg, -1);
- return NULL;
- }
-
return pvtcfg;
}
-static int process_pvt_config_options(struct ast_config *cfg, struct ao2_container *newpvts, struct ao2_container *newcfgs) {
- RAII_VAR(struct ao2_container *, pvt_container, ao2_global_obj_ref(global_config, PVT_CONTAINER), ao2_cleanup);
- const char *cat = NULL;
-
- /* It is ok if pvt_container is NULL, it just means this is our first load */
-
- while ((cat = ast_category_browse(cfg, cat))) {
- RAII_VAR(struct skel_pvt *, tmppvt, NULL, ao2_cleanup);
- RAII_VAR(struct skel_pvt_config *, tmpcfg, NULL, ao2_cleanup);
-
- if (!strcasecmp(cat, "general")) {
- continue;
- }
-
- /* If we've already linked a private for cat in newpvts, don't add a second one with the same name */
- if ((tmppvt = ao2_find(newpvts, cat, OBJ_KEY))) {
- return -1;
- }
-
- /* Get a ref to the existing private, or create a new one */
- if (!(pvt_container && (tmppvt = ao2_find(pvt_container, cat, OBJ_KEY))) && !(tmppvt = new_pvt(cat))) {
- /* Since we will be replacing the whole private container, bail out on errors instead of just
- * skipping privates with config errors */
- ast_log(LOG_WARNING, "Could not create pvt '%s,' ignoring all private config changes.\n", cat);
- return -1;
- }
-
- if (!(tmpcfg = new_pvt_cfg(cfg, cat))) {
- return -1;
- }
- /* We have a valid pvt/cfg, link 'em */
- ao2_link(newpvts, tmppvt);
- ao2_link(newcfgs, tmpcfg);
- }
-
- return 0;
-}
-
-static struct skel_global_config *new_global_config(void)
+static void *skel_global_alloc(const char *cat)
{
struct skel_global_config *cfg;
+
+ if (cat) {
+ return NULL;
+ }
/* Allocate/initialize memory */
if (!(cfg = ao2_alloc(sizeof(*cfg), skel_global_config_destructor))) {
@@ -389,88 +386,6 @@
}
return cfg;
-}
-
-static struct skel_global_config *process_global_config_options(struct ast_config *cfg)
-{
- struct skel_global_config *skelcfg;
-
- if (!(skelcfg = new_global_config())) {
- return NULL;
- }
-
- if (ast_config_parse_category_options(config_opts, cfg, "general", skelcfg)) {
- ao2_ref(skelcfg, -1);
- return NULL;
- }
-
- return skelcfg;
-}
-
-static int process_config(int reload)
-{
- struct ast_config *cfg;
- struct ast_flags cfg_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0, };
- static const char *cfg_file = "app_skel.conf";
- struct skel_global_config *new_global_config = NULL;
- struct ao2_container *new_pvt_container = NULL;
- struct ao2_container *new_cfg_container = NULL;
-
- if (!(cfg = ast_config_load(cfg_file, cfg_flags))) {
- ast_log(LOG_ERROR, "Unable to load config file '%s'\n", cfg_file);
- return -1;
- } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
- ast_debug(1, "%s was unchanged\n", cfg_file);
- return 0;
- } else if (cfg == CONFIG_STATUS_FILEINVALID) {
- ast_log(LOG_ERROR, "Contents of %s are invalid and cannot be parsed\n", cfg_file);
- return -1;
- } else if (cfg == CONFIG_STATUS_FILEMISSING) {
- ast_log(LOG_ERROR, "%s is missing! Cannot load %s\n", cfg_file, AST_MODULE);
- return -1;
- }
-
- if (!(new_pvt_container = ao2_container_alloc(17, skel_pvt_hash, skel_pvt_cmp))) {
- goto error;
- }
- if (!(new_cfg_container = ao2_container_alloc(17, skel_pvt_config_hash, skel_pvt_config_cmp))) {
- goto error;
- }
-
- /* If either [general] or individual parsing fails, refuse to load or make any changes.
- * If no settings from [general] filter down into settings for individual private
- * structures, then treating the two settings as separate cases would be fine. Note
- * that since we replace the entire private container, it is important that we verify
- * that there are no errors in *any* of the private configurations before replacing
- * the existing container */
- if (!(new_global_config = process_global_config_options(cfg))) {
- goto error;
- }
-
- if (process_pvt_config_options(cfg, new_pvt_container, new_cfg_container)) {
- goto error;
- }
-
- /* apply_config releases the temporary objects */
- apply_config(new_global_config, new_pvt_container, new_cfg_container);
-
- ast_config_destroy(cfg);
-
- return 0;
-
-error:
- ast_log(LOG_WARNING, "Ignoring config changes\n");
- ast_config_destroy(cfg);
- if (new_pvt_container) {
- ao2_ref(new_pvt_container, -1);
- }
- if (new_cfg_container) {
- ao2_ref(new_cfg_container, -1);
- }
- if (new_global_config) {
- ao2_ref(new_global_config, -1);
- }
- return -1;
}
static char *handle_skel_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
@@ -558,9 +473,12 @@
static int reload_module(void)
{
- if (process_config(1)) {
+ ast_mutex_lock(&reload_lock);
+ if (ast_config_option_parse_config(&config_info, 1)) {
+ ast_mutex_unlock(&reload_lock);
return AST_MODULE_LOAD_DECLINE;
}
+ ast_mutex_unlock(&reload_lock);
return 0;
}
@@ -577,6 +495,7 @@
if (!(config_opts = ast_config_option_container_new())) {
return AST_MODULE_LOAD_DECLINE;
}
+ config_info.opts_container = config_opts;
/* General Options */
ast_config_option_register(config_opts, "foo", CONTEXT_ALLOW, "general", "booya", OPT_STRINGFIELD_T, struct skel_global_config, 0, foo);
@@ -592,9 +511,12 @@
ast_config_option_register_custom(config_opts, "bit1", CONTEXT_DENY, "general", "yes", custom_bitfield_handler, 0);
ast_config_option_register_custom(config_opts, "bit2", CONTEXT_DENY, "general", "no", custom_bitfield_handler, 0);
- if (process_config(0)) {
+ ast_mutex_lock(&reload_lock);
+ if (ast_config_option_parse_config(&config_info, 0)) {
+ ast_mutex_unlock(&reload_lock);
return AST_MODULE_LOAD_DECLINE;
}
+ ast_mutex_unlock(&reload_lock);
ast_cli_register_multiple(skel_cli, ARRAY_LEN(skel_cli));
return ast_register_application_xml(app, app_exec) ?
Modified: team/twilson/config_work/include/asterisk/config_options.h
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_work/include/asterisk/config_options.h?view=diff&rev=361950&r1=361949&r2=361950
==============================================================================
--- team/twilson/config_work/include/asterisk/config_options.h (original)
+++ team/twilson/config_work/include/asterisk/config_options.h Wed Apr 11 18:28:52 2012
@@ -32,6 +32,25 @@
#include "asterisk/astobj2.h"
struct ast_config_option;
+
+typedef struct ao2_container *(*ast_config_option_user_container_alloc)(void);
+typedef void *(*ast_config_option_user_item_alloc)(const char *category);
+typedef void *(*ast_config_option_find_pvt_by_category)(struct ao2_container *tmp_container, const char *category);
+typedef int (*ast_config_option_apply_config)(void *global_obj, struct ao2_container *pvt_container, struct ao2_container *pvt_cfg_container);
+
+struct ast_config_option_info {
+ const char *module;
+ const char *filename;
+ struct ao2_container *opts_container;
+ ast_config_option_user_item_alloc global_alloc;
+ ast_config_option_user_item_alloc pvt_alloc;
+ ast_config_option_user_item_alloc pvt_cfg_alloc;
+ ast_config_option_user_container_alloc pvt_container_alloc;
+ ast_config_option_user_container_alloc pvt_cfg_container_alloc;
+ ast_config_option_find_pvt_by_category find_pvt;
+ ast_config_option_apply_config apply_config;
+ const char *global_contexts;
+};
/*! \brief The option types with default handlers
*
@@ -83,6 +102,22 @@
*/
struct ast_config_option *ast_config_option_find(struct ao2_container *container, const char *name, const char *context);
+/*! \brief Completely handle simple configs
+ *
+ * \note This is for configs that have no relationship between global options and
+ * those for any private objects (peers, etc.) If a config is well designed, this
+ * should be the case since config templates should be used for setting default
+ * values for individual objects.
+ *
+ * \param info The config_options_info to be used for handling the config
+ * \param filename The filename of the config
+ * \param reload Whether or not this is a reload
+ *
+ * \retval 0 Success
+ * \retval 1 Failure
+ */
+int ast_config_option_parse_config(struct ast_config_option_info *info, int reload);
+
/*! \brief Parse each option defined in a config context
* \param container The container of options from which to configure \a obj
* \param cfg The ast_config being parsed
Modified: team/twilson/config_work/main/config_options.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_work/main/config_options.c?view=diff&rev=361950&r1=361949&r2=361950
==============================================================================
--- team/twilson/config_work/main/config_options.c (original)
+++ team/twilson/config_work/main/config_options.c Wed Apr 11 18:28:52 2012
@@ -185,6 +185,110 @@
return ao2_container_alloc(CONFIG_OPT_BUCKETS, config_opt_hash, config_opt_cmp);
}
+static int is_global_context(struct ast_config_option_info *info, const char *cat)
+{
+ /* XXX Do regex comparison so we can do things like "general|authentication", etc. */
+ return !strcasecmp(cat, "general");
+}
+
+int ast_config_option_parse_config(struct ast_config_option_info *info, int reload)
+{
+ struct ast_config *cfg;
+ struct ast_flags cfg_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0, };
+ const char *cat = NULL;
+ void *new_global_obj = NULL;
+ struct ao2_container *new_pvt_container = NULL, *new_pvt_cfg_container = NULL;
+
+ if (ast_strlen_zero(info->filename)) {
+ ast_log(LOG_ERROR, "No filename given, cannot proceed!\n");
+ return -1;
+ }
+
+ if (!(cfg = ast_config_load(info->filename, cfg_flags))) {
+ ast_log(LOG_ERROR, "Unable to load config file '%s'\n", info->filename);
+ return -1;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
+ ast_debug(1, "%s was unchanged\n", info->filename);
+ return 0;
+ } else if (cfg == CONFIG_STATUS_FILEINVALID) {
+ ast_log(LOG_ERROR, "Contents of %s are invalid and cannot be parsed\n", info->filename);
+ return -1;
+ } else if (cfg == CONFIG_STATUS_FILEMISSING) {
+ ast_log(LOG_ERROR, "%s is missing! Cannot load %s\n", info->filename, info->module);
+ return -1;
+ }
+
+ if (info->global_alloc && !(new_global_obj = info->global_alloc(NULL))) {
+ ast_log(LOG_ERROR, "Unable to allocate storage for global options for %s\n", info->filename);
+ goto error;
+ }
+
+ if (info->pvt_container_alloc && !(new_pvt_container = info->pvt_container_alloc())) {
+ ast_log(LOG_ERROR, "Unable to allocate storage for private container for %s\n", info->filename);
+ goto error;
+ }
+
+ if (info->pvt_cfg_container_alloc && !(new_pvt_cfg_container = info->pvt_cfg_container_alloc())) {
+ ast_log(LOG_ERROR, "Unable to allocate storage for private config container for %s\n", info->filename);
+ goto error;
+ }
+
+ while ((cat = ast_category_browse(cfg, cat))) {
+ RAII_VAR(void *, tmppvt, NULL, ao2_cleanup);
+ RAII_VAR(void *, tmpcfg, NULL, ao2_cleanup);
+
+ if (new_global_obj && is_global_context(info, cat)) {
+ if (ast_config_parse_category_options(info->opts_container, cfg, cat, new_global_obj)) {
+ goto error;
+ }
+ } else if (new_pvt_container && new_pvt_cfg_container) {
+ /* If we've already linked a private for cat in newpvts, don't add a second one with the same name */
+ if ((tmppvt = info->find_pvt(new_pvt_container, cat))) {
+ goto error;
+ }
+
+ /* Get a ref to the existing private, or create a new one */
+ if (!(tmppvt = info->find_pvt(NULL, cat)) && !(tmppvt = info->pvt_alloc(cat))) {
+ /* Since we will be replacing the whole private container, bail out on errors instead of just
+ * skipping privates with config errors */
+ ast_log(LOG_WARNING, "Could not create pvt '%s,' ignoring all private config changes.\n", cat);
+ goto error;
+ }
+
+ if (!(tmpcfg = info->pvt_cfg_alloc(cat))) {
+ goto error;
+ }
+
+ if (ast_config_parse_category_options(info->opts_container, cfg, cat, tmpcfg)) {
+ goto error;
+ }
+
+ /* We have a valid pvt/cfg, link 'em */
+ if (!ao2_link(new_pvt_container, tmppvt)) {
+ goto error;
+ }
+ if (!ao2_link(new_pvt_cfg_container, tmpcfg)) {
+ goto error;
+ }
+ }
+ }
+ ast_config_destroy(cfg);
+ return info->apply_config(new_global_obj, new_pvt_container, new_pvt_cfg_container);
+
+error:
+ ast_config_destroy(cfg);
+ if (new_global_obj) {
+ ao2_ref(new_global_obj, -1);
+ }
+ if (new_pvt_container) {
+ ao2_ref(new_pvt_container, -1);
+ }
+ if (new_pvt_cfg_container) {
+ ao2_ref(new_pvt_cfg_container, -1);
+ }
+ return -1;
+}
+
int ast_config_parse_category_options(struct ao2_container *container, struct ast_config *cfg, const char *cat, void *obj)
{
struct ast_variable *var;
More information about the svn-commits
mailing list