[asterisk-commits] twilson: branch twilson/config_work r367832 - in /team/twilson/config_work: a...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon May 28 16:41:03 CDT 2012


Author: twilson
Date: Mon May 28 16:40:55 2012
New Revision: 367832

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=367832
Log:
Add support for multiple files

I think this should give enough leeway to handle any users.conf stuff and
also crazy things like one context resulting in multiple object types being
created (i.e. chan_sip callbackextension in sip.conf or registersip+hassip in
users.conf means you create a peer and a sip_registry, type=friend in chan_iax2
results in an iax2_peer and an iax2_user, etc.)

I still need to add the ability to set a match function instead of just a
matchvalue so you can match things like hassip= with ast_true(), etc. Also need
to be able to do regex matching on option names.

Yay for days off work! :-)

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
    team/twilson/config_work/main/udptl.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=367832&r1=367831&r2=367832
==============================================================================
--- team/twilson/config_work/apps/app_skel.c (original)
+++ team/twilson/config_work/apps/app_skel.c Mon May 28 16:40:55 2012
@@ -173,42 +173,53 @@
  */ 
 static void *skel_level_alloc(const char *cat);
 
-/*! \brief Check if a skel level exists in the specified container
+/*! \brief Find a skel level in the specified container
  * \note This function *does not* look for a skel_level in the active container. It is used
  * internally by the Config Options code to check if an level has already been added to the
  * container that will be swapped for the live container on a successul reload.
  *
  * \param container A non-active container to search for a level
  * \param category The category associated with the level to check for
- * \retval 1 The level exists in the container
- * \retval 0 The level does not exist in the container
+ * \retval non-NULL The level from the container
+ * \retval NULL The level does not exist in the container
  */
-static int skel_level_exists(struct ao2_container *tmp_container, const char *category);
+static void *skel_level_find(struct ao2_container *tmp_container, const char *category);
 
 /*! \brief An aco_type structure to link the "general" category to the skel_global_config type */
-static struct aco_type global_options = {
+static struct aco_type global_option = {
 	.type = ACO_GLOBAL,
 	.item_offset = offsetof(struct skel_config, global),
 	.category_match = ACO_WHITELIST,
 	.category = "^general$",
 };
 
+struct aco_type *global_options[] = ACO_TYPES(&global_option);
+
 /*! \brief An aco_type structure to link the "sounds" category to the skel_global_config type */
-static struct aco_type sound_options = {
+static struct aco_type sound_option = {
 	.type = ACO_GLOBAL,
 	.item_offset = offsetof(struct skel_config, global),
 	.category_match = ACO_WHITELIST,
 	.category = "^sounds$",
 };
 
+struct aco_type *sound_options[] = ACO_TYPES(&sound_option);
+
 /*! \brief An aco_type structure to link the everything but the "general" and "sounds" categories to the skel_level type */
-static struct aco_type level_options = {
+static struct aco_type level_option = {
 	.type = ACO_ITEM,
 	.category_match = ACO_BLACKLIST,
 	.category = "^(general|sounds)$",
 	.item_alloc = skel_level_alloc,
-	.item_exists = skel_level_exists,
+	.item_find = skel_level_find,
 	.item_offset = offsetof(struct skel_config, levels),
+};
+
+struct aco_type *level_options[] = ACO_TYPES(&level_option);
+
+struct aco_file app_skel_conf = {
+	.filename = "app_skel.conf",
+	.types = ACO_TYPES(&global_option, &sound_option, &level_option),
 };
 
 /*! \brief A global object container that will contain the skel_config that gets swapped out on reloads */
@@ -218,8 +229,8 @@
 static struct ao2_container *games;
 
 /*! \brief Register information about the configs being processed by this module */
-CONFIG_INFO_STANDARD(cfg_info, "app_skel.conf", globals, skel_config_alloc,
-	.types = ACO_TYPES(&global_options, &sound_options, &level_options),
+CONFIG_INFO_STANDARD(cfg_info, globals, skel_config_alloc,
+	.files = ACO_FILES(&app_skel_conf),
 );
 
 static void skel_global_config_destructor(void *obj)
@@ -433,10 +444,9 @@
 	return level;
 }
 
-static int skel_level_exists(struct ao2_container *tmp_container, const char *category)
-{
-	RAII_VAR(struct skel_level *, level, ao2_find(tmp_container, category, OBJ_KEY), ao2_cleanup);
-	return level ? 1 : 0;
+static void *skel_level_find(struct ao2_container *tmp_container, const char *category)
+{
+	return ao2_find(tmp_container, category, OBJ_KEY);
 }
 
 /*! \brief Look up an existing state object, or create a new one
@@ -648,20 +658,20 @@
 	}
 
 	/* Global options */
-	aco_option_register(&cfg_info, "games", &global_options, "3", OPT_UINT_T, 0, FLDSET(struct skel_global_config, num_games));
-	aco_option_register_custom(&cfg_info, "cheat", &global_options, "no", custom_bitfield_handler, 0);
+	aco_option_register(&cfg_info, "games", global_options, "3", OPT_UINT_T, 0, FLDSET(struct skel_global_config, num_games));
+	aco_option_register_custom(&cfg_info, "cheat", global_options, "no", custom_bitfield_handler, 0);
 
 	/* Sound options */
-	aco_option_register(&cfg_info, "prompt", &sound_options, "please-enter-your&number&queue-less-than", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, prompt));
-	aco_option_register(&cfg_info, "wrong_guess", &sound_options, "vm-pls-try-again", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, wrong));
-	aco_option_register(&cfg_info, "right_guess", &sound_options, "auth-thankyou", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, right));
-	aco_option_register(&cfg_info, "too_high", &sound_options, "high", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, high));
-	aco_option_register(&cfg_info, "too_low", &sound_options, "low", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, low));
-	aco_option_register(&cfg_info, "lose", &sound_options, "vm-goodbye", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, lose));
+	aco_option_register(&cfg_info, "prompt", sound_options, "please-enter-your&number&queue-less-than", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, prompt));
+	aco_option_register(&cfg_info, "wrong_guess", sound_options, "vm-pls-try-again", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, wrong));
+	aco_option_register(&cfg_info, "right_guess", sound_options, "auth-thankyou", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, right));
+	aco_option_register(&cfg_info, "too_high", sound_options, "high", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, high));
+	aco_option_register(&cfg_info, "too_low", sound_options, "low", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, low));
+	aco_option_register(&cfg_info, "lose", sound_options, "vm-goodbye", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, lose));
 
 	/* Level options */
-	aco_option_register(&cfg_info, "max_number", &level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_num));
-	aco_option_register(&cfg_info, "max_guesses", &level_options, NULL, OPT_UINT_T, 1, FLDSET(struct skel_level, max_guesses));
+	aco_option_register(&cfg_info, "max_number", level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_num));
+	aco_option_register(&cfg_info, "max_guesses", level_options, NULL, OPT_UINT_T, 1, FLDSET(struct skel_level, max_guesses));
 
 	if (aco_process_config(&cfg_info, 0)) {
 		goto error;

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=367832&r1=367831&r2=367832
==============================================================================
--- team/twilson/config_work/include/asterisk/config_options.h (original)
+++ team/twilson/config_work/include/asterisk/config_options.h Mon May 28 16:40:55 2012
@@ -35,6 +35,7 @@
 
 struct aco_option;
 struct aco_info_internal;
+struct aco_type_internal;
 
 enum aco_type_t {
 	ACO_GLOBAL,
@@ -59,10 +60,10 @@
 /*! \brief Find a item given a category and container of items
  * \param container The container to search for the item
  * \param category The category associated with the item
- * \retval 1 config exists in container
- * \retval 0 config does not exist in container
- */
-typedef int (*aco_type_item_exists)(struct ao2_container *newcontainer, const char *category);
+ * \retval non-NULL item from the container
+ * \retval NULL item does not exist in container
+ */
+typedef void *(*aco_type_item_find)(struct ao2_container *newcontainer, const char *category);
 
 /*! \brief Callback function that is called after a config object is initialized with defaults
  *
@@ -98,15 +99,15 @@
 	const char *category;   /*!< A regular expression for matching categories to be allowed or denied */
 	const char *matchfield; /*!< An option name to match for this type (i.e. a 'type'-like column) */
 	const char *matchvalue; /*!< The value of the option to require for matching (i.e. 'peer' for type= in sip.conf) */
-	regex_t *regex;
 	enum aco_category_op category_match; /*!< Whether the following category regex is a whitelist or blacklist */
 	size_t item_offset;                  /*!< The offset in the config snapshot for the global config or item config container */
 
 	/* non-global callbacks */
 	aco_type_item_alloc item_alloc;         /*!< An allocation function for item associated with this type */
-	aco_type_item_exists item_exists;       /*!< A callback function to find an existing item in a particular container */
+	aco_type_item_find item_find;           /*!< A callback function to find an existing item in a particular container */
 	aco_type_post_item_init item_post_init; /*!< An optional callback function that is called after defaults are applied, but before config processing */
 	aco_type_prelink item_prelink;          /*!< An optional callback function that is called after config processing, but before applying changes */
+	struct aco_type_internal *internal;
 };
 
 /*! \brief A callback function for applying the config changes
@@ -121,19 +122,24 @@
  */
 typedef void *(*aco_snapshot_alloc)(void);
 
+struct aco_file {
+	const char *filename;
+	const char **preload;
+	struct aco_type *types[]; /*!< The list of types for this config. Required. Use a sentinel! */
+};
+
 struct aco_info {
 	const char *module;         /*!< The name of the module whose config is being processed */
-	const char *filename;       /*!< The config filename */
 	aco_pre_apply_config pre_apply_config; /*!< A callback called after processing, but before changes are applied */
 	aco_snapshot_alloc snapshot_alloc;     /*!< Allocate an object to hold all global configs and item containers */
 	struct ao2_global_obj *global_obj;     /*!< The global object array that holds the user-defined config object */
-	const char **preload;     /*!< Categories to parse first. Do something like char *arr[] = {"general", NULL}; and do .preload = arr */
 	struct aco_info_internal *internal;
-	struct aco_type *types[]; /*!< The list of types for this config. Required. Use a sentinel! */
+	struct aco_file *files[];    /*!< The config filename */
 };
 
 /*! \brief A helper macro to ensure that aco_info types always have a sentinel */
 #define ACO_TYPES(...) { __VA_ARGS__, NULL, }
+#define ACO_FILES(...) { __VA_ARGS__, NULL, }
 
 /*! \brief Get pending config changes
  * \note This will most likely be called from the pre_apply_config callback function
@@ -153,8 +159,9 @@
  * Example:
  * \code
  * static AO2_GLOBAL_OBJ_STATIC(globals, 1);
- * CONFIG_INFO_STANDARD(cfg_info, "app_skel.conf", globals, skel_config_alloc,
+ * CONFIG_INFO_STANDARD(cfg_info, globals, skel_config_alloc,
  *     .pre_apply_config = skel_pre_apply_config,
+ *     .files = { &app_skel_conf, NULL },
  * );
  * ...
  * if (aco_info_init(&cfg_info)) {
@@ -164,10 +171,9 @@
  * aco_info_destroy(&cfg_info);
  * \endcode
  */
-#define CONFIG_INFO_STANDARD(name, fn, arr, alloc, ...) \
+#define CONFIG_INFO_STANDARD(name, arr, alloc, ...) \
 static struct aco_info name = { \
 	.module = AST_MODULE, \
-	.filename = fn, \
 	.global_obj = &arr, \
 	.snapshot_alloc = alloc, \
 	__VA_ARGS__ \
@@ -234,16 +240,17 @@
 /*! \brief Process config info from an ast_config via options registered with an aco_info
  *
  * \param info The aco_info to be used for handling the config
+ * \param file The file attached to aco_info that the config represents
  * \param cfg A pointer to a loaded ast_config to parse
  * \param reload Whether or not this is a reload
  *
  * \retval 0 Success
  * \retval -1 Failure
  */
-int aco_process_ast_config(struct aco_info *info, struct ast_config *cfg, int reload);
+int aco_process_ast_config(struct aco_info *info, struct aco_file *file, struct ast_config *cfg);
 
 /*! \brief Parse each option defined in a config category
- * \param info The aco_info with the options for parsing
+ * \param type The aco_type with the options for parsing
  * \param cfg The ast_config being parsed
  * \param cat The config category being parsed
  * \param obj The user-defined config object that will store the parsed config items
@@ -251,24 +258,25 @@
  * \retval 0 Success
  * \retval -1 Failure
  */
-int aco_process_category_options(struct aco_info *info, struct ast_config *cfg, const char *cat, void *obj);
+int aco_process_category_options(struct aco_type *type, struct ast_config *cfg, const char *cat, void *obj);
 
 /*! \brief Set all default options of \a obj
- * \param info The aco_info with the options
+ * \param info The aco_type with the options
  * \param category The configuration category from which \a obj is being configured
  * \param obj The object being configured
  *
  * \retval 0 Success
  * \retval -1 Failure
  */
-int aco_set_defaults(struct aco_info *info, const char *category, void *obj);
+int aco_set_defaults(struct aco_type *type, const char *category, void *obj);
 
 /*! \brief register a config option
  *
  * \note this should probably only be called by one of the aco_option_register* macros
  *
+ * \param info The aco_info holding this module's config information
  * \param name The name of the option
- * \param obj An option object which holds type info about what is being configured
+ * \param types An array of valid option types for matching categories to the correct struct type
  * \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)
@@ -279,34 +287,34 @@
  * \retval 0 success
  * \retval -1 failure
  */
-int __aco_option_register(struct aco_info *info, const char *name, struct aco_type *obj,
+int __aco_option_register(struct aco_info *info, const char *name, struct aco_type **types,
 	const char *default_val, enum aco_option_type type, aco_option_handler handler, unsigned int flags, size_t argc, ...);
 
 /* \brief Register a config option
  * \param info A pointer to the aco_info struct
  * \param name The name of the option
- * \param obj An option object which holds type info about what is being configured
+ * \param types An array of valid option types for matching categories to the correct struct type
  * \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 opt_type The option type for default option type handling
  * \param flags \a type specific flags, stored in the option and available to the handler
  *
  * \returns An option on success, NULL on failure
  */
-#define aco_option_register(info, name, obj, default_val, opt_type, flags, ...) \
-	__aco_option_register(info, name, obj, default_val, opt_type, NULL, flags, VA_NARGS(__VA_ARGS__), __VA_ARGS__);
+#define aco_option_register(info, name, types, default_val, opt_type, flags, ...) \
+	__aco_option_register(info, name, types, default_val, opt_type, NULL, flags, VA_NARGS(__VA_ARGS__), __VA_ARGS__);
 
 /* \brief Register a config option
  * \param info A pointer to the aco_info struct
  * \param name The name of the option
- * \param obj An option object which holds type info about what is being configured
+ * \param types An array of valid option types for matching categories to the correct struct type
  * \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 aco_option_register_custom(info, name, obj, default_val, handler, flags) \
-	__aco_option_register(info, name, obj, default_val, OPT_CUSTOM_T, handler, flags, 0);
+#define aco_option_register_custom(info, name, type, default_val, handler, flags) \
+	__aco_option_register(info, name, type, default_val, OPT_CUSTOM_T, handler, flags, 0);
 
 /*! \note  Everything below this point is to handle converting varargs
  * containing field names, to varargs containing a count of args, followed

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=367832&r1=367831&r2=367832
==============================================================================
--- team/twilson/config_work/main/config_options.c (original)
+++ team/twilson/config_work/main/config_options.c Mon May 28 16:40:55 2012
@@ -43,16 +43,19 @@
  * \internal
  */
 struct aco_info_internal {
+	void *pending;              /*!< The user-defined config object awaiting application */
+};
+
+struct aco_type_internal {
+	regex_t *regex;
 	struct ao2_container *opts; /*!< The container of options registered to the aco_info */
-	void *pending;              /*!< The user-defined config object awaiting application */
 };
 
 struct aco_option {
 	const char *name;
 	const char *default_val;
-	struct aco_type *obj;
+	struct aco_type **obj;
 	enum aco_option_type type;
-	enum aco_category_op category_match;
 	aco_option_handler handler;
 	unsigned int flags;
 	size_t argc;
@@ -121,19 +124,20 @@
 	return regex;
 }
 
-int __aco_option_register(struct aco_info *info, const char *name, struct aco_type *obj,
-	const char *default_val, enum aco_option_type type, aco_option_handler handler, unsigned int flags, size_t argc, ...)
+int __aco_option_register(struct aco_info *info, const char *name, struct aco_type **types,
+	const char *default_val, enum aco_option_type kind, aco_option_handler handler, unsigned int flags, size_t argc, ...)
 {
 	struct aco_option *opt;
+	struct aco_type *type;
 	va_list ap;
 	int tmp;
 
 	/* Custom option types require a handler */
-	if (!handler && type == OPT_CUSTOM_T) {
-		return -1;
-	}
-
-	if (!obj) {
+	if (!handler && kind == OPT_CUSTOM_T) {
+		return -1;
+	}
+
+	if (!(types && types[0])) {
 		return -1;
 	}
 
@@ -148,9 +152,8 @@
 	va_end(ap);
 
 	opt->name = name;
-	opt->obj = obj;
 	opt->default_val = default_val;
-	opt->type = type;
+	opt->type = kind;
 	opt->handler = handler;
 	opt->flags = flags;
 	opt->argc = argc;
@@ -162,9 +165,15 @@
 		return -1;
 	};
 
-	if (!ao2_link(info->internal->opts, opt)) {
-		ao2_ref(opt, -1);
-		return -1;
+	tmp = 0;
+	while ((type = types[tmp++])) {
+		if (!ao2_link(type->internal->opts, opt)) {
+			while (--tmp) {
+				ao2_unlink(types[tmp]->internal->opts, opt);
+			}
+			ao2_ref(opt, -1);
+			return -1;
+		}
 	}
 
 	return 0;
@@ -184,17 +193,16 @@
 	return strcasecmp(opt1->name, name) ? 0 : CMP_MATCH | CMP_STOP;
 }
 
-static int find_option_cb(void *obj, void *arg, void *data, int flags)
-{
-	const char *name = arg, *category = data;
+static int find_option_cb(void *obj, void *arg, int flags)
+{
 	struct aco_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->obj->regex, category, 0, NULL, 0) == !match->obj->category_match ? 0 : CMP_MATCH | CMP_STOP;
-}
-
-static struct aco_option *aco_option_find(struct aco_info *info, const char *name, const char *cat)
-{
-	return ao2_callback_data(info->internal->opts, OBJ_KEY, find_option_cb, (void *) name, (void *) cat);
+	const char *name = arg;
+	return strcasecmp(name, match->name) ? 0 : CMP_MATCH | CMP_STOP;
+}
+
+static struct aco_option *aco_option_find(struct aco_type *type, const char *name)
+{
+	return ao2_callback(type->internal->opts, OBJ_KEY, find_option_cb, (void *) name);
 }
 
 struct ao2_container *aco_option_container_alloc(void)
@@ -202,15 +210,15 @@
 	return ao2_container_alloc(CONFIG_OPT_BUCKETS, config_opt_hash, config_opt_cmp);
 }
 
-static struct aco_type *internal_aco_type_find(struct aco_info *info, struct ast_config *cfg, const char *category)
+static struct aco_type *internal_aco_type_find(struct aco_file *file, struct ast_config *cfg, const char *category)
 {
 	size_t x;
 	struct aco_type *match;
 	const char *val;
 
-	for (x = 0, match = info->types[x]; match; match = info->types[++x]) {
+	for (x = 0, match = file->types[x]; match; match = file->types[++x]) {
 		/* First make sure we are an object that can service this category */
-		if (!regexec(match->regex, category, 0, NULL, 0) == !match->category_match) {
+		if (!regexec(match->internal->regex, category, 0, NULL, 0) == !match->category_match) {
 			continue;
 		}
 
@@ -231,137 +239,149 @@
 	return match;
 }
 
-static int is_preload(struct aco_info *info, const char *cat)
+static int is_preload(struct aco_file *file, const char *cat)
 {
 	int i;
 
-	if (!info->preload) {
+	if (!file->preload) {
 		return 0;
 	}
 
-	for (i = 0; !ast_strlen_zero(info->preload[i]); i++) {
-		if (!strcasecmp(cat, info->preload[i])) {
+	for (i = 0; !ast_strlen_zero(file->preload[i]); i++) {
+		if (!strcasecmp(cat, file->preload[i])) {
 			return 1;
 		}
 	}
 	return 0;
 }
 
-static int process_category(struct ast_config *cfg, struct aco_info *info, const char *cat, int preload) {
+static int process_category(struct ast_config *cfg, struct aco_info *info, struct aco_file *file, const char *cat, int preload) {
 	RAII_VAR(void *, new_item, NULL, ao2_cleanup);
-	struct aco_type *obj;
+	struct aco_type *type;
 	/* For global types, field is the global option struct. For non-global, it is the container for items.
 	 * We do not grab a reference to these objects, as the info already holds references to them. This
 	 * pointer is just a convenience. Do not actually store it somewhere. */
 	void **field;
 
 	/* Skip preloaded categories if we aren't preloading */
-	if (!preload && is_preload(info, cat)) {
+	if (!preload && is_preload(file, cat)) {
 		return 0;
 	}
 
 	/* Find aco_type by category, if not found it is an error */
-	if (!(obj = internal_aco_type_find(info, cfg, cat))) {
-		ast_log(LOG_ERROR, "Could not find config type for category '%s' in '%s'\n", cat, info->filename);
-		return -1;
-	}
-
-	field = info->internal->pending + obj->item_offset;
+	if (!(type = internal_aco_type_find(file, cfg, cat))) {
+		ast_log(LOG_ERROR, "Could not find config type for category '%s' in '%s'\n", cat, file->filename);
+		return -1;
+	}
+
+	field = info->internal->pending + type->item_offset;
 	if (!*field) {
 		ast_log(LOG_ERROR, "No object to update!\n");
 		return -1;
 	}
 
-	if (obj->type == ACO_GLOBAL && *field) {
-		if (aco_set_defaults(info, cat, *field)) {
-			ast_log(LOG_ERROR, "In %s: Setting defaults for %s failed\n", info->filename, cat);
-			return -1;
-		}
-		if (aco_process_category_options(info, cfg, cat, *field)) {
-			ast_log(LOG_ERROR, "In %s: Processing options for %s failed\n", info->filename, cat);
-			return -1;
-		}
-	} else if (obj->type == ACO_ITEM) {
-		/* If we've already linked an item for this category, don't add a second one with the same name */
+	if (type->type == ACO_GLOBAL && *field) {
+		if (aco_set_defaults(type, cat, *field)) {
+			ast_log(LOG_ERROR, "In %s: Setting defaults for %s failed\n", file->filename, cat);
+			return -1;
+		}
+		if (aco_process_category_options(type, cfg, cat, *field)) {
+			ast_log(LOG_ERROR, "In %s: Processing options for %s failed\n", file->filename, cat);
+			return -1;
+		}
+	} else if (type->type == ACO_ITEM) {
+		int new = 0;
+		/* If we have multiple definitions of a category in a file, or can set the values from multiple
+		 * files, look up the entry if we've already added it so we can merge the values together.
+		 * Otherwise, alloc a new item. */
 		if (*field) {
-			if ((obj->item_exists(*field, cat))) {
-				ast_log(LOG_ERROR, "In %s: Multiple definitions of %s!\n", info->filename, cat);
+			if (!(new_item = type->item_find(*field, cat))) {
+				if (!(new_item = type->item_alloc(cat))) {
+					ast_log(LOG_ERROR, "In %s: Could not create item for %s\n", file->filename, cat);
+					return -1;
+				}
+				if (aco_set_defaults(type, cat, new_item)) {
+					ast_log(LOG_ERROR, "In %s: Setting defaults for %s failed\n", file->filename, cat);
+					return -1;
+				}
+				new = 1;
+			}
+		}
+
+		/* XXX Should we only call the post_init after the initial default setting, or allow each
+		 * type to do a pre-process step? Right now, going with allowing each type. */
+		if (type->item_post_init && type->item_post_init(new_item)) {
+			ast_log(LOG_ERROR, "In %s: Post-init callback for %s failed\n", file->filename, cat);
+			return -1;
+		}
+
+		if (aco_process_category_options(type, cfg, cat, new_item)) {
+			ast_log(LOG_ERROR, "In %s: Processing options for %s failed\n", file->filename, cat);
+			return -1;
+		}
+
+		if (type->item_prelink && type->item_prelink(new_item)) {
+			ast_log(LOG_ERROR, "In %s: Pre-link callback for %s failed\n", file->filename, cat);
+			return -1;
+		}
+
+		if (new && !ao2_link(*field, new_item)) {
+			ast_log(LOG_ERROR, "In %s: Linking config for %s failed\n", file->filename, cat);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int apply_config(struct aco_info *info)
+{
+	ao2_global_obj_replace_unref(*info->global_obj, info->internal->pending);
+
+	return 0;
+}
+
+static int internal_process_ast_config(struct aco_info *info, struct aco_file *file, struct ast_config *cfg)
+{
+	const char *cat = NULL;
+
+	if (file->preload) {
+		int i;
+		for (i = 0; !ast_strlen_zero(file->preload[i]); i++) {
+			if (process_category(cfg, info, file, file->preload[i], 1)) {
 				return -1;
 			}
 		}
-
-		if (!(new_item = obj->item_alloc(cat))) {
-			ast_log(LOG_ERROR, "In %s: Could not create item for %s\n", info->filename, cat);
-			return -1;
-		}
-
-		if (aco_set_defaults(info, cat, new_item)) {
-			ast_log(LOG_ERROR, "In %s: Setting defaults for %s failed\n", info->filename, cat);
-			return -1;
-		}
-
-		if (obj->item_post_init && obj->item_post_init(new_item)) {
-			ast_log(LOG_ERROR, "In %s: Post-init callback for %s failed\n", info->filename, cat);
-			return -1;
-		}
-
-		if (aco_process_category_options(info, cfg, cat, new_item)) {
-			ast_log(LOG_ERROR, "In %s: Processing options for %s failed\n", info->filename, cat);
-			return -1;
-		}
-
-		if (obj->item_prelink && obj->item_prelink(new_item)) {
-			ast_log(LOG_ERROR, "In %s: Pre-link callback for %s failed\n", info->filename, cat);
-			return -1;
-		}
-
-		if (!ao2_link(*field, new_item)) {
-			ast_log(LOG_ERROR, "In %s: Linking config for %s failed\n", info->filename, cat);
-			return -1;
-		}
-	}
-	return 0;
-}
-
-static int apply_config(struct aco_info *info)
-{
-	ao2_global_obj_replace_unref(*info->global_obj, info->internal->pending);
-
-	return 0;
-}
-
-int aco_process_ast_config(struct aco_info *info, struct ast_config *cfg, int reload)
-{
-	const char *cat = NULL;
-	int res;
-
+	}
+
+	while ((cat = ast_category_browse(cfg, cat))) {
+		if (process_category(cfg, info, file, cat, 0)) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int aco_process_ast_config(struct aco_info *info, struct aco_file *file, struct ast_config *cfg)
+{
 	if (!(info->internal->pending = info->snapshot_alloc())) {
-		ast_log(LOG_ERROR, "In %s: Could not allocate temporary objects\n", info->filename);
+		ast_log(LOG_ERROR, "In %s: Could not allocate temporary objects\n", file->filename);
 		goto error;
 	}
 
-	if (info->preload) {
-		int i;
-		for (i = 0; !ast_strlen_zero(info->preload[i]); i++) {
-			if (process_category(cfg, info, info->preload[i], 1)) {
-				goto error;
-			}
-		}
-	}
-
-	while ((cat = ast_category_browse(cfg, cat))) {
-		if (process_category(cfg, info, cat, 0)) {
-			goto error;
-		}
+	if (internal_process_ast_config(info, file, cfg)) {
+		goto error;
 	}
 
 	if (info->pre_apply_config && info->pre_apply_config()) {
 		goto error;
 	}
 
-	res = apply_config(info); /* The module already knows where the objects are */
-	ao2_ref(info->internal->pending, -1);
-	return res;
+	if (apply_config(info)) {
+		goto error;
+	};
+
+	ao2_cleanup(info->internal->pending);
+	return 0;
 
 error:
 	ao2_cleanup(info->internal->pending);
@@ -372,39 +392,56 @@
 {
 	struct ast_config *cfg;
 	struct ast_flags cfg_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0, };
-	int res;
-
-	if (ast_strlen_zero(info->filename)) {
+	int res = 0, x = 0;
+	struct aco_file *file;
+
+	if (!(info->files[0])) {
 		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;
-	}
-
-	res = aco_process_ast_config(info, cfg, reload);
-	ast_config_destroy(cfg);
-
+	if (!(info->internal->pending = info->snapshot_alloc())) {
+		ast_log(LOG_ERROR, "In %s: Could not allocate temporary objects\n", info->module);
+		return -1;
+	}
+
+	while (!res && (file = info->files[x++])) {
+		if (!(cfg = ast_config_load(file->filename, cfg_flags))) {
+			ast_log(LOG_ERROR, "Unable to load config file '%s'\n", file->filename);
+			res = -1;
+			break;
+		} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
+			ast_debug(1, "%s was unchanged\n", file->filename);
+			res = 0;
+			continue;
+		} else if (cfg == CONFIG_STATUS_FILEINVALID) {
+			ast_log(LOG_ERROR, "Contents of %s are invalid and cannot be parsed\n", file->filename);
+			res = -1;
+			break;
+		} else if (cfg == CONFIG_STATUS_FILEMISSING) {
+			ast_log(LOG_ERROR, "%s is missing! Cannot load %s\n", file->filename, info->module);
+			res = -1;
+			break;
+		}
+
+		res = internal_process_ast_config(info, file, cfg);
+		ast_config_destroy(cfg);
+	}
+
+	if (res || (res = ((info->pre_apply_config && info->pre_apply_config()) || apply_config(info)))) {
+		;
+	};
+
+	ao2_cleanup(info->internal->pending);
 	return res;
 }
 
-int aco_process_category_options(struct aco_info *info, struct ast_config *cfg, const char *cat, void *obj)
+int aco_process_category_options(struct aco_type *type, struct ast_config *cfg, const char *cat, void *obj)
 {
 	struct ast_variable *var;
 
 	for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
-		RAII_VAR(struct aco_option *, opt, aco_option_find(info, var->name, cat), ao2_cleanup);
+		RAII_VAR(struct aco_option *, opt, aco_option_find(type, var->name), ao2_cleanup);
 		if (!opt) {
 			ast_log(LOG_ERROR, "Could not find option suitable for category '%s' named '%s' at line %d of %s\n", cat, var->name, var->lineno, var->file);
 			return -1;
@@ -425,18 +462,22 @@
 
 static void internal_type_destroy(struct aco_type *type)
 {
-	if (type->regex) {
-		regfree(type->regex);
-		ast_free(type->regex);
-	}
-}
-
-static void internal_info_types_destroy(struct aco_info *info)
+	if (type->internal->regex) {
+		regfree(type->internal->regex);
+		ast_free(type->internal->regex);
+	}
+	ao2_cleanup(type->internal->opts);
+	type->internal->opts = NULL;
+	ast_free(type->internal);
+	type->internal = NULL;
+}
+
+static void internal_file_types_destroy(struct aco_file *file)
 {
 	size_t x;
 	struct aco_type *t;
 
-	for (x = 0, t = info->types[x]; t; t = info->types[++x]) {
+	for (x = 0, t = file->types[x]; t; t = file->types[++x]) {
 		internal_type_destroy(t);
 		t = NULL;
 	}
@@ -444,28 +485,34 @@
 
 static int internal_type_init(struct aco_type *type)
 {
-	if (!(type->regex = build_category_regex(type->category))) {
-		return -1;
-	}
+	if (!(type->internal = ast_calloc(1, sizeof(*type->internal)))) {
+		return -1;
+	}
+
+	if (!(type->internal->regex = build_category_regex(type->category))) {
+		internal_type_destroy(type);
+	}
+
+	if (!(type->internal->opts = aco_option_container_alloc())) {
+		internal_type_destroy(type);
+	}
+
 	return 0;
 }
 
 int aco_info_init(struct aco_info *info)
 {
-	size_t x;
-	struct aco_type *t;
+	size_t x, y;
 
 	if (!(info->internal = ast_calloc(1, sizeof(*info->internal)))) {
 		return -1;
 	}
 
-	if (!(info->internal->opts = aco_option_container_alloc())) {
-		goto error;
-	}
-
-	for (x = 0, t = info->types[x]; t; t = info->types[++x]) {
-		if (internal_type_init(t)) {
-			goto error;
+	for (x = 0; info->files[x]; x++) {
+		for (y = 0; info->files[x]->types[y]; y++) {
+			if (internal_type_init(info->files[x]->types[y])) {
+				goto error;
+			}
 		}
 	}
 
@@ -477,35 +524,26 @@
 
 void aco_info_destroy(struct aco_info *info)
 {
-	if (info->internal) {
-		ao2_cleanup(info->internal->opts);
-		ast_free(info->internal);
-		info->internal = NULL;
-	}
-	internal_info_types_destroy(info);
-}
-
-/*! \brief match for anything where the category passes (or fails if !category_match) the category regex
- * \internal
- */
-static int match_option_by_category(void *obj, void *arg, int flags)
-{
-	struct aco_option *match = obj;
-	const char *category = arg;
-
-	return !regexec(match->obj->regex, category, 0, NULL, 0) == !match->obj->category_match ? 0 : CMP_MATCH;
-}
-
-int aco_set_defaults(struct aco_info *info, const char *category, void *obj)
-{
-	RAII_VAR(struct ao2_iterator *, iter, NULL, ao2_iterator_cleanup);
+	int x;
+	/* It shouldn't be possible for internal->pending to be in use when this is called because
+	 * of the locks in loader.c around reloads and unloads and the fact that internal->pending
+	 * only exists while those locks are held */
+	ast_free(info->internal);
+	info->internal = NULL;
+
+	for (x = 0; info->files[x]; x++) {
+		internal_file_types_destroy(info->files[x]);
+	}
+}
+
+int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
+{
 	struct aco_option *opt;
-
-	if (!(iter = ao2_callback(info->internal->opts, OBJ_MULTIPLE, match_option_by_category, (void *) category))) {
-		return -1;
-	}
-
-	while ((opt = ao2_iterator_next(iter))) {
+	struct ao2_iterator iter;
+
+	iter = ao2_iterator_init(type->internal->opts, 0);
+
+	while ((opt = ao2_iterator_next(&iter))) {
 		RAII_VAR(struct ast_variable *, var, NULL, ast_variables_destroy);
 
 		if (ast_strlen_zero(opt->default_val)) {
@@ -514,15 +552,18 @@
 		}
 		if (!(var = ast_variable_new(opt->name, opt->default_val, ""))) {
 			ao2_ref(opt, -1);
+			ao2_iterator_destroy(&iter);
 			return -1;
 		}
 		if (opt->handler(opt, var, obj)) {
+			ast_log(LOG_ERROR, "Unable to set default for %s, %s=%s\n", category, var->name, var->value);
 			ao2_ref(opt, -1);
-			ast_log(LOG_ERROR, "Unable to set default for %s, %s=%s\n", category, var->name, var->value);
+			ao2_iterator_destroy(&iter);
 			return -1;
 		}
 		ao2_ref(opt, -1);
 	}
+	ao2_iterator_destroy(&iter);
 
 	return 0;
 }

Modified: team/twilson/config_work/main/udptl.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_work/main/udptl.c?view=diff&rev=367832&r1=367831&r2=367832
==============================================================================
--- team/twilson/config_work/main/udptl.c (original)
+++ team/twilson/config_work/main/udptl.c Mon May 28 16:40:55 2012
@@ -192,14 +192,22 @@
 static void *udptl_snapshot_alloc(void);
 static int udptl_pre_apply_config(void);
 
-static struct aco_type general_options = {
+static struct aco_type general_option = {
 	.type = ACO_GLOBAL,
 	.category_match = ACO_WHITELIST,
+	.item_offset = offsetof(struct udptl_config, general),
 	.category = "^general$",
 };
 
-CONFIG_INFO_STANDARD(cfg_info, "udptl.conf", globals, udptl_snapshot_alloc,
-	.types = ACO_TYPES(&general_options),
+static struct aco_type *general_options[] = ACO_TYPES(&general_option);
+
+static struct aco_file udptl_conf = {
+	.filename = "udptl.conf",
+	.types = ACO_TYPES(&general_option),
+};
+
+CONFIG_INFO_STANDARD(cfg_info, globals, udptl_snapshot_alloc,
+	.files = ACO_FILES(&udptl_conf),
 	.pre_apply_config = udptl_pre_apply_config,
 );
 
@@ -1452,30 +1460,30 @@
 		return;
 	}
 
-	aco_option_register(&cfg_info, "udptlstart", &general_options, __stringify(DEFAULT_UDPTLSTART),
+	aco_option_register(&cfg_info, "udptlstart", general_options, __stringify(DEFAULT_UDPTLSTART),
 		OPT_UINT_T, PARSE_IN_RANGE | PARSE_DEFAULT,
 		FLDSET(struct udptl_global_options, start), DEFAULT_UDPTLSTART, 1024, 65535);
 
-	aco_option_register(&cfg_info, "udptlend", &general_options, __stringify(DEFAULT_UDPTLEND),
+	aco_option_register(&cfg_info, "udptlend", general_options, __stringify(DEFAULT_UDPTLEND),
 		OPT_UINT_T, PARSE_IN_RANGE | PARSE_DEFAULT,
 		FLDSET(struct udptl_global_options, end), DEFAULT_UDPTLEND, 1024, 65535);
 
-	aco_option_register(&cfg_info, "udptlfecentries", &general_options, NULL,
+	aco_option_register(&cfg_info, "udptlfecentries", general_options, NULL,
 		OPT_UINT_T, PARSE_IN_RANGE | PARSE_RANGE_DEFAULTS,
 		FLDSET(struct udptl_global_options, fecentries), 1, MAX_FEC_ENTRIES);
 
-	aco_option_register(&cfg_info, "udptlfecspan", &general_options, NULL,
+	aco_option_register(&cfg_info, "udptlfecspan", general_options, NULL,
 		OPT_UINT_T, PARSE_IN_RANGE | PARSE_RANGE_DEFAULTS,
 		FLDSET(struct udptl_global_options, fecspan), 1, MAX_FEC_SPAN);
 
-	aco_option_register(&cfg_info, "udptlchecksums", &general_options, "yes",
+	aco_option_register(&cfg_info, "udptlchecksums", general_options, "yes",
 		OPT_BOOL_T, 0, FLDSET(struct udptl_global_options, nochecksums));
 
-	aco_option_register(&cfg_info, "use_even_ports", &general_options, "no",
+	aco_option_register(&cfg_info, "use_even_ports", general_options, "no",
 		OPT_BOOL_T, 1, FLDSET(struct udptl_global_options, use_even_ports));
 
-	aco_option_register_custom(&cfg_info, "t38faxudpec", &general_options, NULL, removed_options_handler, 0);
-	aco_option_register_custom(&cfg_info, "t38faxmaxdatagram", &general_options, NULL, removed_options_handler, 0);
+	aco_option_register_custom(&cfg_info, "t38faxudpec", general_options, NULL, removed_options_handler, 0);
+	aco_option_register_custom(&cfg_info, "t38faxmaxdatagram", general_options, NULL, removed_options_handler, 0);
 
 	ast_cli_register_multiple(cli_udptl, ARRAY_LEN(cli_udptl));
 




More information about the asterisk-commits mailing list