[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