[svn-commits] twilson: branch twilson/config_work r362135 - in /team/twilson/config_work: a...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Apr 13 18:34:58 CDT 2012


Author: twilson
Date: Fri Apr 13 18:34:54 2012
New Revision: 362135

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=362135
Log:
Saving work.

Things work, there dont' appear to be any ref/memory leaks.
MUCH cleanup work to do this weekend. Then on to reviewboard.

This basically adds the ability to support multiple global
or private containers for configs--which may not be useful
to the majority of configs.

I'd also like to implement
1) matching config option objects by fields (to handle things
   like type=peer and type=user being different objects
2) forcing preloading of certain contexts so that users can
   make decisions after certain contexts are loaded. This
   would allow supporting legacy configs that inherit private
   options from 

Modified:
    team/twilson/config_work/apps/app_skel.c
    team/twilson/config_work/include/asterisk/config_options.h
    team/twilson/config_work/main/asterisk.exports.in
    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=362135&r1=362134&r2=362135
==============================================================================
--- team/twilson/config_work/apps/app_skel.c (original)
+++ team/twilson/config_work/apps/app_skel.c Fri Apr 13 18:34:54 2012
@@ -144,26 +144,11 @@
 static AO2_GLOBAL_OBJ_STATIC(global_config, NUM_GLOBAL_OBJECTS);
 AST_MUTEX_DEFINE_STATIC(reload_lock);
 
-static struct ao2_container *config_opts;
-
-static struct skel_global_config *skel_global_alloc(const char *cat);
-static struct skel_pvt *skel_pvt_alloc(const char *cat);
-static struct skel_pvt_config *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 struct skel_pvt *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 int skel_apply_config(void);
 
 static struct ast_config_option_info config_info = {
 	.module = AST_MODULE,
 	.filename = "app_skel.conf",
-	.global_contexts = "general",
-	.global_alloc = (ast_config_option_user_item_alloc) skel_global_alloc,
-	.pvt_alloc = (ast_config_option_user_item_alloc) skel_pvt_alloc,
-	.pvt_cfg_alloc = (ast_config_option_user_item_alloc) skel_pvt_cfg_alloc,
-	.pvt_container_alloc = skel_pvt_container_alloc,
-	.pvt_cfg_container_alloc = skel_pvt_cfg_container_alloc,
-	.find_pvt = (ast_config_option_find_pvt_by_category) skel_find_pvt_by_category,
 	.apply_config = skel_apply_config,
 };
 
@@ -173,9 +158,31 @@
 	ast_string_field_free_memory(cfg);
 }
 
+static void skel_pvt_cfg_unlink(struct skel_pvt_config *cfg)
+{
+	RAII_VAR(struct ao2_container *, cfgs, ao2_global_obj_ref(global_config, PVT_CFG_CONTAINER), ao2_cleanup);
+	if (cfgs) {
+		ao2_unlink(cfgs, cfg);
+	}
+}
+
+static struct skel_pvt_config *skel_pvt_find_cfg(struct skel_pvt *pvt)
+{
+	RAII_VAR(struct ao2_container *, cfgs, ao2_global_obj_ref(global_config, PVT_CFG_CONTAINER), ao2_cleanup);
+	if (cfgs) {
+		return ao2_find(cfgs, pvt->name, OBJ_KEY);
+	}
+
+	return NULL;
+}
+
 static void skel_pvt_destructor(void *obj)
 {
 	struct skel_pvt *pvt = obj;
+	RAII_VAR(struct skel_pvt_config *, cfg, skel_pvt_find_cfg(pvt), ao2_cleanup);
+	if (cfg) {
+		skel_pvt_cfg_unlink(cfg);
+	}
 	ast_string_field_free_memory(pvt);
 }
 
@@ -203,7 +210,6 @@
 {
 	const struct skel_pvt_config *cfg = obj;
 	const char *name = (flags & OBJ_KEY) ? obj : cfg->name;
-	/* make this and others work with OBJ_KEY */
 	return ast_str_case_hash(name);
 }
 
@@ -224,14 +230,17 @@
 	return ao2_container_alloc(PVT_BUCKETS, skel_pvt_config_hash, skel_pvt_config_cmp);
 }
 
-static struct skel_pvt *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;
-	}
+static int skel_containers_alloc(struct ao2_container **newpvts, struct ao2_container **newcfgs)
+{
+	if (!(*newpvts = skel_pvt_container_alloc())) {
+		return -1;
+	}
+	if (!(*newcfgs = skel_pvt_cfg_container_alloc())) {
+		ao2_ref(*newpvts, -1);
+		*newpvts = NULL;
+		return -1;
+	}
+	return 0;
 }
 
 /*! \brief A custom bitfield handler
@@ -253,25 +262,33 @@
 	return 0;
 }
 
-static int skel_apply_config(void *new_global, struct ao2_container *new_pvt_container, struct ao2_container *new_cfg_container)
+static int skel_apply_config(void)
 {
 	void *old;
 	void *array[NUM_GLOBAL_OBJECTS];
 	int i;
-
-	array[GLOBAL_OPTIONS] = new_global;
-	array[PVT_CONTAINER] = new_pvt_container;
-	array[PVT_CFG_CONTAINER] = new_cfg_container;
+	RAII_VAR(struct ast_config_option_object *, glob, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_config_option_object *, priv, NULL, ao2_cleanup);
+
+	if (!(glob = ao2_find(config_info.objs_container, "global", OBJ_KEY))) {
+		return -1;
+	}
+	if (!(priv = ao2_find(config_info.objs_container, "private", OBJ_KEY))) {
+		return -1;
+	}
+
+	array[GLOBAL_OPTIONS] = glob->new_global_cfg;
+	array[PVT_CONTAINER] = priv->new_pvts;
+	array[PVT_CFG_CONTAINER] = priv->new_cfgs;
 
 	for (i = 0; i < NUM_GLOBAL_OBJECTS; i++) {
 		if ((old = ao2_global_obj_replace(global_config, i, array[i]))) {
 			/* There was an existing config */
 			ao2_ref(old, -1);
 		}
-		ao2_ref(array[i], -1);
-	}
-
-	ast_log(LOG_NOTICE, "Succesfully applied config!\n");
+		/* the "alloc" ref will be cleaned up by the caller */	
+	}
+
 	return 0;
 }
 
@@ -334,6 +351,17 @@
 	}
 	ast_string_field_set(pvt, name, name);
 	return pvt;
+}
+
+static void *skel_find_pvt(struct ao2_container *tmp_container, const char *category)
+{
+	return ao2_find(tmp_container, category, OBJ_KEY);
+}
+
+static void *skel_find_or_create_pvt(const char *category)
+{
+	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) : skel_pvt_alloc(category);
 }
 
 static struct skel_pvt_config *skel_pvt_cfg_alloc(const char *cat)
@@ -479,41 +507,74 @@
 static int unload_module(void)
 {
 	ast_cli_unregister_multiple(skel_cli, ARRAY_LEN(skel_cli));
+	ao2_ref(config_info.opts_container, -1);
+	ao2_ref(config_info.objs_container, -1);
 	ao2_global_obj_release(global_config);
 	return ast_unregister_application(app);
 }
 
 static int load_module(void)
 {
-	if (!(config_opts = ast_config_option_container_new())) {
-		return AST_MODULE_LOAD_DECLINE;
-	}
-	config_info.opts_container = config_opts;
+	RAII_VAR(struct ast_config_option_object *, global_type, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_config_option_object *, priv_type, NULL, ao2_cleanup);
+
+	if (!(config_info.opts_container = ast_config_option_container_new())) {
+		ast_log(LOG_ERROR, "1\n");
+		goto error;
+	}
+
+	if (!(config_info.objs_container = aco_obj_container_new())) {
+		ast_log(LOG_ERROR, "2\n");
+		goto error;
+	}
+
+	if (!(global_type = aco_obj_global_alloc("global", CONTEXT_ALLOW, "general", (aco_obj_alloc) skel_global_alloc))) {
+		ast_log(LOG_ERROR, "3\n");
+		goto error;
+	}
+
+	if (!(priv_type = aco_obj_private_alloc("private", CONTEXT_DENY, "general", (aco_obj_alloc) skel_pvt_cfg_alloc, skel_containers_alloc, skel_find_or_create_pvt, skel_find_pvt, NULL, NULL))) {
+		ast_log(LOG_ERROR, "4\n");
+		goto error;
+	}
+
+	aco_obj_register(config_info.objs_container, global_type);
+	aco_obj_register(config_info.objs_container, priv_type);
 
 	/* General Options */
-	ast_config_option_register(config_opts, "foo", CONTEXT_ALLOW, "general", "booya", OPT_STRINGFIELD_T, struct skel_global_config, 0, foo);
-	ast_config_option_register(config_opts, "bar", CONTEXT_ALLOW, "general", NULL, OPT_STRINGFIELD_T, struct skel_global_config, 0, bar);
-	ast_config_option_register(config_opts, "baz", CONTEXT_ALLOW, "general", NULL, OPT_STRINGFIELD_T, struct skel_global_config, 0, baz);
-	ast_config_option_register(config_opts, "blah", CONTEXT_ALLOW, "general", NULL, OPT_BOOL_T, struct skel_global_config, 1, blah);
-	ast_config_option_register(config_opts, "bindaddr", CONTEXT_ALLOW, "general", "0.0.0.0:1234", OPT_SOCKADDR_T, struct skel_global_config, PARSE_PORT_REQUIRE, bindaddr);
+	ast_config_option_register(config_info.opts_container, "foo", global_type, "booya", OPT_STRINGFIELD_T, struct skel_global_config, 0, foo);
+	ast_config_option_register(config_info.opts_container, "bar", global_type, NULL, OPT_STRINGFIELD_T, struct skel_global_config, 0, bar);
+	ast_config_option_register(config_info.opts_container, "baz", global_type, NULL, OPT_STRINGFIELD_T, struct skel_global_config, 0, baz);
+	ast_config_option_register(config_info.opts_container, "blah", global_type, NULL, OPT_BOOL_T, struct skel_global_config, 1, blah);
+	ast_config_option_register(config_info.opts_container, "bindaddr", global_type, "0.0.0.0:1234", OPT_SOCKADDR_T, struct skel_global_config, PARSE_PORT_REQUIRE, bindaddr);
 
 	/* Private Options */
-	ast_config_option_register(config_opts, "description", CONTEXT_DENY, "general", NULL, OPT_STRINGFIELD_T, struct skel_pvt_config, 0, description);
-	ast_config_option_register(config_opts, "allow", CONTEXT_DENY, "general", "ulaw,alaw", OPT_CODEC_T, struct skel_pvt_config, 1, prefs, caps);
-	ast_config_option_register(config_opts, "permit", CONTEXT_DENY, "general", NULL, OPT_ACL_T, struct skel_pvt_config, 1, permit);
-	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);
+	ast_config_option_register(config_info.opts_container, "description", priv_type, NULL, OPT_STRINGFIELD_T, struct skel_pvt_config, 0, description);
+	ast_config_option_register(config_info.opts_container, "allow", priv_type, "ulaw,alaw", OPT_CODEC_T, struct skel_pvt_config, 1, prefs, caps);
+	ast_config_option_register(config_info.opts_container, "permit", priv_type, NULL, OPT_ACL_T, struct skel_pvt_config, 1, permit);
+	ast_config_option_register_custom(config_info.opts_container, "bit1", priv_type, "yes", custom_bitfield_handler, 0);
+	ast_config_option_register_custom(config_info.opts_container, "bit2", priv_type, "no", custom_bitfield_handler, 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_log(LOG_ERROR, "5\n");
+		goto error;
 	}
 	ast_mutex_unlock(&reload_lock);
 
 	ast_cli_register_multiple(skel_cli, ARRAY_LEN(skel_cli));
 	return ast_register_application_xml(app, app_exec) ?
 		AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
+
+error:
+	if (config_info.opts_container) {
+		ao2_ref(config_info.opts_container, -1);
+	}
+	if (config_info.objs_container) {
+		ao2_ref(config_info.objs_container, -1);
+	}
+	return AST_MODULE_LOAD_DECLINE;
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Skeleton (sample) Application",

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=362135&r1=362134&r2=362135
==============================================================================
--- team/twilson/config_work/include/asterisk/config_options.h (original)
+++ team/twilson/config_work/include/asterisk/config_options.h Fri Apr 13 18:34:54 2012
@@ -28,28 +28,71 @@
 extern "C" {
 #endif
 
+#include <regex.h>
+
 #include "asterisk/config.h"
 #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);
+enum ast_config_option_object_t {
+	GLOBAL_OBJ,
+	PRIVATE_OBJ,
+};
+
+/*! \brief Whether a context regex is a blackist or a whitelist */
+enum ast_config_option_context_op {
+	CONTEXT_DENY = 0,
+	CONTEXT_ALLOW,
+};
+
+typedef void *(*aco_obj_alloc)(const char *category);
+typedef int (*aco_obj_containers_alloc)(struct ao2_container **newpvts, struct ao2_container **newcfgs);
+typedef void *(*aco_obj_find_or_create_pvt)(const char *context);
+typedef void *(*aco_obj_find_pvt)(struct ao2_container *newcontainer, const char *context);
+typedef int (*aco_obj_post_cfg_init)(void *newcfg);
+typedef int (*aco_obj_prelink)(void *newcfg);
+
+/* ao2 lookup by context, OBJ_KEY, only access with a global reload lock held */
+struct ast_config_option_object {
+	/* common stuff */
+	enum ast_config_option_object_t type;
+	const char *name;
+	const char *context;
+	regex_t *regex;
+	enum ast_config_option_context_op context_allow;
+	aco_obj_alloc cfg_alloc;
+
+	/* non-global callbacks */
+	aco_obj_containers_alloc containers_alloc; /* required. cfgs required, pvts if non-config-related state */ 
+	aco_obj_find_or_create_pvt find_or_create_pvt; /* required if there is non-config-related state */
+	aco_obj_find_pvt find_pvt; /* required if there is non-config-related state */
+	aco_obj_post_cfg_init post_cfg_init; /*!< Filter global options done if necessary */
+	aco_obj_prelink prelink; /* optional, can be used to do additional checks on a config */
+
+	/* global cached object */
+	void *new_global_cfg; /* This is a cache and the reason we need locks */
+
+	/* non-global cached objects */
+	struct ao2_container *new_pvts; /* required if there is non-config-related state */
+	struct ao2_container *new_cfgs; /* required */
+};
+
+struct ao2_container *aco_obj_container_new(void);
+struct ast_config_option_object *aco_obj_global_alloc(const char *name, enum ast_config_option_context_op op, const char *context, aco_obj_alloc alloc);
+struct ast_config_option_object *aco_obj_private_alloc(const char *name, enum ast_config_option_context_op op, const char *context,
+	aco_obj_alloc alloc, aco_obj_containers_alloc containers_alloc, aco_obj_find_or_create_pvt find_or_create_pvt,
+	aco_obj_find_pvt find_pvt, aco_obj_post_cfg_init post_cfg_init, aco_obj_prelink prelink);
+int aco_obj_register(struct ao2_container *container, struct ast_config_option_object *obj);
+
+typedef int (*aco_apply_config)(void);
 
 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;
+	struct ao2_container *objs_container;
+	aco_apply_config apply_config;
 };
 
 /*! \brief The option types with default handlers
@@ -74,12 +117,6 @@
 	OPT_UINT_T,        /*!< fields: unsigned int */
 };
 
-/*! \brief Whether a context regex is a blackist or a whitelist */
-enum ast_config_option_context_op {
-	CONTEXT_DENY,
-	CONTEXT_ALLOW,
-};
-
 /*! \brief A callback function for handling a particular option
  * \param opt The option being configured
  * \param var The config variable to use to configure \a obj
@@ -144,8 +181,7 @@
  * \note this should probably only be called by one of the ast_config_option_register* macros
  *
  * \param name The name of the option
- * \param context_allow Whether or not the \a context is a whitelist of blacklist
- * \param context A regex string defining what contexts to allow/disallow the option to apply to
+ * \param obj An option object which holds type info about what is being configured
  * \param default_val The default value of the option in the same format as defined in a config file
  * \param type The option type (only for default handlers)
  * \param handler The handler function for the option (only for non-default types)
@@ -155,14 +191,13 @@
  *
  * \returns An option on success, NULL on failure
  */
-struct ast_config_option *__ast_config_option_build(const char *name, enum ast_config_option_context_op context_allow,
-	const char *context, const char *default_val, enum ast_config_option_type type, ast_config_option_handler handler, unsigned int flags, size_t argc, ...);
+struct ast_config_option *__ast_config_option_build(const char *name, struct ast_config_option_object *obj,
+	const char *default_val, enum ast_config_option_type type, ast_config_option_handler handler, unsigned int flags, size_t argc, ...);
 
 /* \brief Register a config option
  * \param container The container to store the registered option in
  * \param name The name of the option
- * \param op Whether or not the \a context is a whitelist of blacklist
- * \param context A regex string defining what contexts to allow/disallow the option to apply to
+ * \param obj An option object which holds type info about what is being configured
  * \param default_val The default value of the option in the same format as defined in a config file
  * \param opt_type The option type
  * \param struct_type The type of the struct being configured (e.g. struct skel_pvt_config)
@@ -170,11 +205,11 @@
  *
  * \returns An option on success, NULL on failure
  */
-#define ast_config_option_register(container, name, op, context, default_val, opt_type, struct_type, flags, ...) {\
+#define ast_config_option_register(container, name, obj, default_val, opt_type, struct_type, flags, ...) {\
 	struct ast_config_option *__opt; \
 	__opt = opt_type == OPT_STRINGFIELD_T ? \
-		__ast_config_option_build(name, op, context, default_val, opt_type, NULL, flags, ARGMAP(offsetof, struct_type, __VA_ARGS__, __field_mgr_pool, __field_mgr)) : \
-		__ast_config_option_build(name, op, context, default_val, opt_type, NULL, flags, ARGMAP(offsetof, struct_type, __VA_ARGS__)); \
+		__ast_config_option_build(name, obj, default_val, opt_type, NULL, flags, ARGMAP(offsetof, struct_type, __VA_ARGS__, __field_mgr_pool, __field_mgr)) : \
+		__ast_config_option_build(name, obj, default_val, opt_type, NULL, flags, ARGMAP(offsetof, struct_type, __VA_ARGS__)); \
 	if (__opt) { \
 		ao2_link(container, __opt); \
 		ao2_ref(__opt, -1); \
@@ -184,16 +219,15 @@
 /* \brief Register a config option
  * \param container The container to store the registered option in
  * \param name The name of the option
- * \param op Whether or not the \a context is a whitelist of blacklist
- * \param context A regex string defining what contexts to allow/disallow the option to apply to
+ * \param obj An option object which holds type info about what is being configured
  * \param default_val The default value of the option in the same format as defined in a config file
  * \param handler The handler callback for the option
  * \param flags \a type specific flags, stored in the option and available to the handler
  *
  * \returns An option on success, NULL on failure
  */
-#define ast_config_option_register_custom(container, name, op, context, default_val, handler, flags) { \
-	struct ast_config_option *__opt = __ast_config_option_build(name, op, context, default_val, OPT_CUSTOM_T, handler, flags, 0); \
+#define ast_config_option_register_custom(container, name, obj, default_val, handler, flags) { \
+	struct ast_config_option *__opt = __ast_config_option_build(name, obj, default_val, OPT_CUSTOM_T, handler, flags, 0); \
 	if (__opt) { \
 		ao2_link(container, __opt); \
 		ao2_ref(__opt, -1); \
@@ -204,7 +238,8 @@
  * containing field names, to varargs containing a count of args, followed
  * by the offset of each of the field names in the struct type that is
  * passed in. It is currently limited to 8 arguments, but 8 variadic
- * arguments should be good enough for anyone. If not, it is easy to add more.
+ * arguments, like 640K, should be good enough for anyone. If not, it is
+ * easy to add more.
  * */
 
 /*! \def ARGMAP(func, func_arg, x, ...)

Modified: team/twilson/config_work/main/asterisk.exports.in
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_work/main/asterisk.exports.in?view=diff&rev=362135&r1=362134&r2=362135
==============================================================================
--- team/twilson/config_work/main/asterisk.exports.in (original)
+++ team/twilson/config_work/main/asterisk.exports.in Fri Apr 13 18:34:54 2012
@@ -5,6 +5,7 @@
 		LINKER_SYMBOL_PREFIX__ast_*;
 		LINKER_SYMBOL_PREFIXpbx_*;
 		LINKER_SYMBOL_PREFIXastman_*;
+		LINKER_SYMBOL_PREFIXaco_*;
 		LINKER_SYMBOL_PREFIXao2_*;
 		LINKER_SYMBOL_PREFIX__ao2_*;
 		LINKER_SYMBOL_PREFIXoption_debug;

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=362135&r1=362134&r2=362135
==============================================================================
--- team/twilson/config_work/main/config_options.c (original)
+++ team/twilson/config_work/main/config_options.c Fri Apr 13 18:34:54 2012
@@ -41,9 +41,8 @@
 
 struct ast_config_option {
 	const char *name;
-	const char *context;
 	const char *default_val;
-	regex_t *context_regex;
+	struct ast_config_option_object *obj;
 	enum ast_config_option_type type;
 	enum ast_config_option_context_op context_allow;
 	ast_config_option_handler handler;
@@ -55,9 +54,8 @@
 static void config_option_destroy(void *obj)
 {
 	struct ast_config_option *opt = obj;
-	if (opt->context_regex) {
-		regfree(opt->context_regex);
-		ast_free(opt->context_regex);
+	if (opt->obj) {
+		ao2_ref(opt->obj, -1);
 	}
 }
 
@@ -88,14 +86,6 @@
 	return NULL;
 }
 
-static void regex_cleanup(regex_t *regex)
-{
-	if (regex) {
-		regfree(regex);
-		ast_free(regex);
-	}
-}
-
 static regex_t *build_context_regex(const char *text)
 {
 	int res;
@@ -119,12 +109,11 @@
 /*! \brief build a config option
  * \note this should probably only be called by one of the ast_config_option_register* macros
  */
-struct ast_config_option *__ast_config_option_build(const char *name, enum ast_config_option_context_op context_allow,
-	const char *context, const char *default_val, enum ast_config_option_type type, ast_config_option_handler handler, unsigned int flags, size_t argc, ...)
+struct ast_config_option *__ast_config_option_build(const char *name, struct ast_config_option_object *obj,
+	const char *default_val, enum ast_config_option_type type, ast_config_option_handler handler, unsigned int flags, size_t argc, ...)
 {
 	struct ast_config_option *opt;
 	va_list ap;
-	regex_t *regex;
 	int tmp;
 
 	/* Custom option types require a handler */
@@ -132,13 +121,11 @@
 		return NULL;
 	}
 
-	if (!(regex = build_context_regex(context))) {
+	if (!obj) {
 		return NULL;
 	}
 
 	if (!(opt = ao2_alloc(sizeof(*opt) + argc * sizeof(opt->args[0]), config_option_destroy))) {
-		regfree(regex);
-		ast_free(regex);
 		return NULL;
 	}
 
@@ -149,9 +136,8 @@
 	va_end(ap);
 
 	opt->name = name;
-	opt->context_allow = context_allow;
-	opt->context = context;
-	opt->context_regex = regex;
+	ao2_ref(obj, +1);
+	opt->obj = obj;
 	opt->default_val = default_val;
 	opt->type = type;
 	opt->handler = handler;
@@ -160,24 +146,10 @@
 
 	if (!opt->handler && !(opt->handler = ast_config_option_default_handler(opt->type))) {
 		ao2_ref(opt, -1);
-		regfree(regex);
-		ast_free(regex);
 		return NULL;
 	};
 
 	return opt;
-}
-
-/*! \brief Test if the recorded context of each option matches the generated regex of the other
- * \param opt1 the first option
- * \param opt2 the second option
- * \retval 0 both options have regexes that match each other's recorded context setting
- * \retval -1 the options have regexes that don't match each other's recorded context setting
- */
-static int test_opt_contexts(struct ast_config_option *opt1, struct ast_config_option *opt2)
-{
-	return regexec(opt1->context_regex, opt2->context, 0, NULL, 0) ||
-	       regexec(opt2->context_regex, opt1->context, 0, NULL, 0) ? -1 : 0;
 }
 
 static int config_opt_hash(const void *obj, const int flags)
@@ -190,7 +162,8 @@
 static int config_opt_cmp(void *obj, void *arg, int flags)
 {
 	struct ast_config_option *opt1 = obj, *opt2 = arg;
-	return strcasecmp(opt1->name, opt2->name) || opt1->context_allow != opt2->context_allow || test_opt_contexts(opt1, opt2) ? 0 : CMP_MATCH | CMP_STOP;
+	const char *name = (flags & OBJ_KEY) ? arg : opt2->name;
+	return strcasecmp(opt1->name, name) ? 0 : CMP_MATCH | CMP_STOP;
 }
 
 static int find_option_cb(void *obj, void *arg, void *data, int flags)
@@ -198,7 +171,7 @@
 	const char *name = arg, *context = data;
 	struct ast_config_option *match = obj;
 	/* Continue if we don't match on name, or if NOT (regex_matches XOR regex_should_match) */
-	return strcasecmp(name, match->name) || !regexec(match->context_regex, context, 0, NULL, 0) == !match->context_allow ? 0 : CMP_MATCH | CMP_STOP; 
+	return strcasecmp(name, match->name) || !regexec(match->obj->regex, context, 0, NULL, 0) == !match->obj->context_allow ? 0 : CMP_MATCH | CMP_STOP; 
 }
 
 struct ast_config_option *ast_config_option_find(struct ao2_container *container, const char *name, const char *cat)
@@ -209,6 +182,70 @@
 struct ao2_container *ast_config_option_container_new(void)
 {
 	return ao2_container_alloc(CONFIG_OPT_BUCKETS, config_opt_hash, config_opt_cmp);
+}
+
+static int allocate_temp_objects(void *o, void *arg, int flags)
+{
+	struct ast_config_option_object *obj = o;
+	int *error = arg;
+
+	switch (obj->type) {
+	case GLOBAL_OBJ:
+		if (!(obj->new_global_cfg = obj->cfg_alloc(NULL))) {
+			*error = -1;
+			return CMP_STOP;
+		}
+		break;
+	case PRIVATE_OBJ:
+		if (obj->containers_alloc(&obj->new_pvts, &obj->new_cfgs)) {
+			*error = -1;
+			return CMP_STOP;
+		}
+		break;
+	}
+	return 0;
+}
+
+static int cleanup_temp_objects(void *obj, void *arg, int flags)
+{
+	struct ast_config_option_object *object = obj;
+	if (!object) {
+		return 0;
+	}
+
+	switch (object->type) {
+	case GLOBAL_OBJ:
+		if (object->new_global_cfg) {
+			ao2_ref(object->new_global_cfg, -1);
+			object->new_global_cfg = NULL;
+		}
+		break;
+	case PRIVATE_OBJ:
+		if (object->new_pvts) {
+			ao2_ref(object->new_pvts, -1);
+			object->new_pvts = NULL;
+		}
+		if (object->new_cfgs) {
+			ao2_ref(object->new_cfgs, -1);
+			object->new_cfgs = NULL;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int config_opt_obj_context_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_config_option_object *match = obj;
+	const char *context = arg;
+
+	return !regexec(match->regex, context, 0, NULL, 0) == !match->context_allow ? 0 : CMP_MATCH | CMP_STOP; 
+}
+
+static struct ast_config_option_object *ast_config_option_object_find(struct ao2_container *container, const char *context)
+{
+	return ao2_callback(container, 0, config_opt_obj_context_cmp, (void *) context);
 }
 
 int ast_config_option_parse_config(struct ast_config_option_info *info, int reload)
@@ -216,9 +253,7 @@
 	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;
-	RAII_VAR(regex_t *, regex, NULL, regex_cleanup);
+	int err = 0, res;
 	
 	if (ast_strlen_zero(info->filename)) {
 		ast_log(LOG_ERROR, "No filename given, cannot proceed!\n");
@@ -239,45 +274,44 @@
 		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;
-	}
-
-	if (new_global_obj && !ast_strlen_zero(info->global_contexts) && !(regex = build_context_regex(info->global_contexts))) {
+	ao2_callback(info->objs_container, OBJ_NODATA, allocate_temp_objects, &err);
+	if (err) {
+		ao2_callback(info->objs_container, OBJ_NODATA, cleanup_temp_objects, NULL);
+		ast_log(LOG_NOTICE, "1\n");
 		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 && !ast_strlen_zero(info->global_contexts) && !regexec(regex, cat, 0, NULL, 0)) {
-			if (ast_config_option_set_defaults(info->opts_container, cat, new_global_obj)) {
-				goto error;
-			}
-			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) {
+		RAII_VAR(struct ast_config_option_object *, obj, ast_config_option_object_find(info->objs_container, cat), ao2_cleanup);
+
+		/* Find config object by context, if not found it is an error */
+		if (!obj) {
+			ast_log(LOG_NOTICE, "2\n");
+			goto error;
+		}
+
+		/* if type == GLOBAL_OBJ, set defaults and configure the cached cfg object */
+		if (obj->type == GLOBAL_OBJ && obj->new_global_cfg) {
+			if (ast_config_option_set_defaults(info->opts_container, cat, obj->new_global_cfg)) {
+				ast_log(LOG_NOTICE, "3\n");
+				goto error;
+			}
+			if (ast_config_parse_category_options(info->opts_container, cfg, cat, obj->new_global_cfg)) {
+				ast_log(LOG_NOTICE, "4\n");
+				goto error;
+			}
+		} else if (obj->type == PRIVATE_OBJ) {
 			/* 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))) {
-				if (!(tmppvt = info->pvt_alloc(cat))) {
+			if (obj->new_pvts) {
+				if ((tmppvt = obj->find_pvt(obj->new_pvts, cat))) {
+					ast_log(LOG_NOTICE, "5\n");
+					goto error;
+				}
+
+				/* Get a ref to the existing private, or create a new one */
+				if (!(tmppvt = obj->find_or_create_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);
@@ -285,65 +319,80 @@
 				}
 			}
 
-			if (!(tmpcfg = info->pvt_cfg_alloc(cat))) {
+			if (!(tmpcfg = obj->cfg_alloc(cat))) {
+				ast_log(LOG_NOTICE, "6\n");
 				goto error;
 			}
 
 			if (ast_config_option_set_defaults(info->opts_container, cat, tmpcfg)) {
+				ast_log(LOG_NOTICE, "7\n");
+				goto error;
+			}
+
+			/* XXX Might rename this callback to make it obvious it is after defaults
+			 * have been set. The purpose is to allow the module to override default
+			 * values based on other info, like what is in the various global settings
+			 * stored in info->objectsthe module to override default
+			 * values based on other info, like what is in the various global settings
+			 * stored in info->objects--but I'll have to implement preload support for
+			 * that to work first! TODO */
+			if (obj->post_cfg_init && obj->post_cfg_init(tmpcfg)) {
+				ast_log(LOG_NOTICE, "8\n");
 				goto error;
 			}
 
 			if (ast_config_parse_category_options(info->opts_container, cfg, cat, tmpcfg)) {
+				ast_log(LOG_NOTICE, "9\n");
+				goto error;
+			}
+
+			if (obj->prelink && obj->prelink(tmpcfg)) {
+				ast_log(LOG_NOTICE, "10\n");
 				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)) {
+			if (tmppvt && !ao2_link(obj->new_pvts, tmppvt)) {
+				ast_log(LOG_NOTICE, "11\n");
+				goto error;
+			}
+			if (!ao2_link(obj->new_cfgs, tmpcfg)) {
+				ast_log(LOG_NOTICE, "12\n");
 				goto error;
 			}
 		}
 	}
 	ast_config_destroy(cfg);
-	return info->apply_config(new_global_obj, new_pvt_container, new_pvt_cfg_container);
+
+	res = info->apply_config(); /* The module already knows where the objects are */
+	ao2_callback(info->objs_container, OBJ_NODATA, cleanup_temp_objects, NULL);
+
+	return res;
 
 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);
-	}
+	ao2_callback(info->objs_container, OBJ_NODATA, cleanup_temp_objects, NULL);
 	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;
-	struct ast_config_option *opt;
 
 	for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
-		if (!(opt = ast_config_option_find(container, var->name, cat))) {
+		RAII_VAR(struct ast_config_option *, opt, ast_config_option_find(container, var->name, cat), ao2_cleanup);
+		if (!opt) {
 			ast_log(LOG_WARNING, "Could not find option suitable for category '%s' named '%s'\n", cat, var->name);
 			return -1;
 		}
 		if (!opt->handler) {
 			/* It should be impossible for an option to not have a handler */
-			ao2_ref(opt, -1);
 			return -1;
 		}
 		if (opt->handler(opt, var, obj)) {
 			ast_log(LOG_WARNING, "Error parsing %s=%s at line %d\n", var->name, var->value, var->lineno);
-			ao2_ref(opt, -1);
 			return -1;
 		}
-		ao2_ref(opt, -1);
 	}
 
 	return 0;
@@ -356,7 +405,7 @@
 	struct ast_config_option *match = obj;
 	const char *context = arg;
 
-	return !regexec(match->context_regex, context, 0, NULL, 0) == !match->context_allow ? 0 : CMP_MATCH;
+	return !regexec(match->obj->regex, context, 0, NULL, 0) == !match->obj->context_allow ? 0 : CMP_MATCH;
 }
 
 int ast_config_option_set_defaults(struct ao2_container *container, const char *context, void *obj)
@@ -372,6 +421,7 @@
 		RAII_VAR(struct ast_variable *, var, NULL, ast_variables_destroy);
 
 		if (ast_strlen_zero(opt->default_val)) {
+			ao2_ref(opt, -1);
 			continue;
 		}
 		if (!(var = ast_variable_new(opt->name, opt->default_val, ""))) {
@@ -382,9 +432,95 @@
 			ao2_ref(opt, -1);
 			return -1;
 		}
+		ao2_ref(opt, -1);
 	}
 
 	return 0;
+}
+
+static int config_opt_obj_hash(const void *obj, const int flags)
+{
+	const struct ast_config_option_object *object = obj;
+	const char *name = (flags & OBJ_KEY) ? obj : object->name;
+	return ast_str_case_hash(name);
+}
+
+static int config_opt_obj_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_config_option_object *obj1 = obj, *obj2 = arg;
+	const char *name = (flags & OBJ_KEY) ? arg : obj2->name;
+	return strcasecmp(obj1->name, name) ? 0 : CMP_STOP | CMP_MATCH;
+}
+
+struct ao2_container *aco_obj_container_new(void)
+{
+	return ao2_container_alloc(7, config_opt_obj_hash, config_opt_obj_cmp);
+}
+
+static void config_obj_destructor(void *o)
+{
+	struct ast_config_option_object *obj = o;
+	if (obj->regex) {
+		regfree(obj->regex);
+		ast_free(obj->regex);
+	}
+	return;
+}
+
+struct ast_config_option_object *aco_obj_global_alloc(const char *name, enum ast_config_option_context_op op, const char *context, aco_obj_alloc alloc)
+{
+	struct ast_config_option_object *obj;
+
+	if (!(obj = ao2_alloc(sizeof(*obj), config_obj_destructor))) {
+		return NULL;
+	}
+
+	if (!(obj->regex = build_context_regex(context))) {
+		ao2_ref(obj, -1);
+		return NULL;
+	}
+
+	obj->type = GLOBAL_OBJ;
+	obj->name = name;
+	obj->context_allow = op;
+	obj->cfg_alloc = alloc;
+	obj->context = context;
+
+	return obj;
+}
+
+struct ast_config_option_object *aco_obj_private_alloc(const char *name, enum ast_config_option_context_op op, const char *context,
+	aco_obj_alloc alloc, aco_obj_containers_alloc containers_alloc, aco_obj_find_or_create_pvt find_or_create_pvt,
+	aco_obj_find_pvt find_pvt, aco_obj_post_cfg_init post_cfg_init, aco_obj_prelink prelink)
+{
+	struct ast_config_option_object *obj;
+
+	if (!(obj = ao2_alloc(sizeof(*obj), config_obj_destructor))) {
+		return NULL;
+	}
+
+	if (!(obj->regex = build_context_regex(context))) {
+		ao2_ref(obj, -1);
+		return NULL;
+	}
+
+	obj->type = PRIVATE_OBJ;
+	obj->name = name;
+	obj->context_allow = op;
+	obj->cfg_alloc = alloc;
+	obj->context = context;
+	obj->containers_alloc = containers_alloc;
+	obj->find_or_create_pvt = find_or_create_pvt;
+	obj->find_pvt = find_pvt;
+	obj->post_cfg_init = post_cfg_init;
+	obj->prelink = prelink;
+
+	return obj;
+}
+
+int aco_obj_register(struct ao2_container *container, struct ast_config_option_object *obj)
+{
+	return !ao2_link(container, obj);
 }
 
 /* default config option handlers */




More information about the svn-commits mailing list