mirror of https://github.com/asterisk/asterisk
This framework adds a way to register the various options in a config file with Asterisk and to handle loading and reloading of that config in a consistent and atomic manner. Review: https://reviewboard.asterisk.org/r/1873/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@368181 65c4cc65-6c06-0410-ace0-fbb531ad65f3certified/11.2
parent
463f9d729a
commit
d54717c39e
@ -0,0 +1,27 @@
|
|||||||
|
[general]
|
||||||
|
games=3
|
||||||
|
cheat=no
|
||||||
|
|
||||||
|
[sounds]
|
||||||
|
prompt=please-enter-your,number,queue-less-than
|
||||||
|
wrong_guess=vm-pls-try-again
|
||||||
|
right_guess=auth-thankyou
|
||||||
|
too_high=high
|
||||||
|
too_low=low
|
||||||
|
lose=vm-goodbye
|
||||||
|
|
||||||
|
[easy]
|
||||||
|
max_number=10
|
||||||
|
max_guesses=4
|
||||||
|
|
||||||
|
[medium]
|
||||||
|
max_number=100
|
||||||
|
max_guesses=6
|
||||||
|
|
||||||
|
[hard]
|
||||||
|
max_number=1000
|
||||||
|
max_guesses=7
|
||||||
|
|
||||||
|
[nightmare]
|
||||||
|
max_number=1000
|
||||||
|
max_guesses=1
|
@ -0,0 +1,482 @@
|
|||||||
|
/*
|
||||||
|
* Asterisk -- An open source telephony toolkit.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012, Digium, Inc.
|
||||||
|
*
|
||||||
|
* Mark Spencer <markster@digium.com>
|
||||||
|
*
|
||||||
|
* See http://www.asterisk.org for more information about
|
||||||
|
* the Asterisk project. Please do not directly contact
|
||||||
|
* any of the maintainers of this project for assistance;
|
||||||
|
* the project provides a web site, mailing lists and IRC
|
||||||
|
* channels for your use.
|
||||||
|
*
|
||||||
|
* This program is free software, distributed under the terms of
|
||||||
|
* the GNU General Public License Version 2. See the LICENSE file
|
||||||
|
* at the top of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \file
|
||||||
|
* \brief Configuration option-handling
|
||||||
|
* \author Terry Wilson <twilson@digium.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ASTERISK_CONFIG_OPTIONS_H
|
||||||
|
#define _ASTERISK_CONFIG_OPTIONS_H
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
|
#include "asterisk/config.h"
|
||||||
|
#include "asterisk/astobj2.h"
|
||||||
|
|
||||||
|
struct aco_option;
|
||||||
|
struct aco_info_internal;
|
||||||
|
struct aco_type_internal;
|
||||||
|
|
||||||
|
enum aco_type_t {
|
||||||
|
ACO_GLOBAL,
|
||||||
|
ACO_ITEM,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! \brief Whether a category regex is a blackist or a whitelist */
|
||||||
|
enum aco_category_op {
|
||||||
|
ACO_BLACKLIST = 0,
|
||||||
|
ACO_WHITELIST,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! \brief What kind of matching should be done on an option name */
|
||||||
|
enum aco_matchtype {
|
||||||
|
ACO_EXACT = 1,
|
||||||
|
ACO_REGEX,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! Callback functions for option parsing via aco_process_config() */
|
||||||
|
|
||||||
|
/*! \brief Allocate a configurable ao2 object
|
||||||
|
* \param category The config category the object is being generated for
|
||||||
|
* \retval NULL error
|
||||||
|
* \retval non-NULL a new configurable ao2 object
|
||||||
|
*/
|
||||||
|
typedef void *(*aco_type_item_alloc)(const char *category);
|
||||||
|
|
||||||
|
/*! \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 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
|
||||||
|
*
|
||||||
|
* \note This callback is called during config processing after a new config is allocated and
|
||||||
|
* and defaults applied but before values from the config are read. This callback could be used
|
||||||
|
* to merge in settings inherited from the global settings if necessary, despite that being a
|
||||||
|
* bad thing to do!
|
||||||
|
*
|
||||||
|
* \param newitem The newly allocated config object with defaults populated
|
||||||
|
* \retval 0 succes, continue processing
|
||||||
|
* \retval non-zero failure, stop processing
|
||||||
|
*/
|
||||||
|
typedef int (*aco_type_item_pre_process)(void *newitem);
|
||||||
|
|
||||||
|
/*! \brief Callback function that is called after config processing, but before linking
|
||||||
|
*
|
||||||
|
* \note This callback is called after config processing, but before linking the object
|
||||||
|
* in the config container. This callback can be used to verify that all settings make
|
||||||
|
* sense together, that required options have been set, etc.
|
||||||
|
*
|
||||||
|
* \param newitem The newly configured object
|
||||||
|
* \retval 0 success, continue processing
|
||||||
|
* \retval non-zero failure, stop processing
|
||||||
|
*/
|
||||||
|
typedef int (*aco_type_prelink)(void *newitem);
|
||||||
|
|
||||||
|
/*! \brief A function for determining whether the value for the matchfield in an aco_type is sufficient for a match
|
||||||
|
* \param text The value of the option
|
||||||
|
* \retval -1 The value is sufficient for a match
|
||||||
|
* \retval 0 The value is not sufficient for a match
|
||||||
|
*/
|
||||||
|
typedef int (*aco_matchvalue_func)(const char *text);
|
||||||
|
|
||||||
|
/*! \struct aco_type
|
||||||
|
* \brief Type information about a category-level configurable object
|
||||||
|
*/
|
||||||
|
struct aco_type {
|
||||||
|
/* common stuff */
|
||||||
|
enum aco_type_t type; /*!< Whether this is a global or item type */
|
||||||
|
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) */
|
||||||
|
aco_matchvalue_func matchfunc; /*!< A function for determing whether the option value matches (i.e. hassip= requires ast_true()) */
|
||||||
|
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_find item_find; /*!< A callback function to find an existing item in a particular container */
|
||||||
|
aco_type_item_pre_process item_pre_process; /*!< 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
|
||||||
|
* \retval 0 Success
|
||||||
|
* \retval non-zero Failure. Changes not applied
|
||||||
|
*/
|
||||||
|
typedef int (*aco_pre_apply_config)(void);
|
||||||
|
|
||||||
|
/*! \brief A callback functino for allocating an object to hold all config objects
|
||||||
|
* \retval NULL error
|
||||||
|
* \retval non-NULL a config object container
|
||||||
|
*/
|
||||||
|
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 */
|
||||||
|
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 */
|
||||||
|
struct aco_info_internal *internal;
|
||||||
|
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
|
||||||
|
* \param info An initialized aco_info
|
||||||
|
* \retval NULL error
|
||||||
|
* \retval non-NULL A pointer to the user-defined config object with un-applied changes
|
||||||
|
*/
|
||||||
|
void *aco_pending_config(struct aco_info *info);
|
||||||
|
|
||||||
|
/*! \def CONFIG_INFO_STANDARD
|
||||||
|
* \brief Declare an aco_info struct with default module and preload values
|
||||||
|
* \param name The name of the struct
|
||||||
|
* \param fn The filename of the config
|
||||||
|
* \param arr The global object array for holding the user-defined config object
|
||||||
|
* \param alloc The allocater for the user-defined config object
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* \code
|
||||||
|
* static AO2_GLOBAL_OBJ_STATIC(globals, 1);
|
||||||
|
* 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)) {
|
||||||
|
* return AST_MODULE_LOAD_DECLINE;
|
||||||
|
* }
|
||||||
|
* ...
|
||||||
|
* aco_info_destroy(&cfg_info);
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
#define CONFIG_INFO_STANDARD(name, arr, alloc, ...) \
|
||||||
|
static struct aco_info name = { \
|
||||||
|
.module = AST_MODULE, \
|
||||||
|
.global_obj = &arr, \
|
||||||
|
.snapshot_alloc = alloc, \
|
||||||
|
__VA_ARGS__ \
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! \brief Initialize an aco_info structure
|
||||||
|
* \note aco_info_destroy must be called if this succeeds
|
||||||
|
* \param info The address of an aco_info struct to initialize
|
||||||
|
* \retval 0 Success
|
||||||
|
* \retval non-zero Failure
|
||||||
|
*/
|
||||||
|
int aco_info_init(struct aco_info *info);
|
||||||
|
|
||||||
|
/*! \brief Destroy an initialized aco_info struct
|
||||||
|
* \param info The address of the aco_info struct to destroy
|
||||||
|
*/
|
||||||
|
void aco_info_destroy(struct aco_info *info);
|
||||||
|
|
||||||
|
/*! \brief The option types with default handlers
|
||||||
|
*
|
||||||
|
* \note aco_option_register takes an option type which is used
|
||||||
|
* to look up the handler for that type. Each non-custom type requires
|
||||||
|
* field names for specific types in the struct being configured. Each
|
||||||
|
* option below is commented with the field types, *in the order
|
||||||
|
* they must be passed* to aco_option_register. The fields
|
||||||
|
* are located in the args array in the ast_config_option passed to
|
||||||
|
* the default handler function.
|
||||||
|
* */
|
||||||
|
enum aco_option_type {
|
||||||
|
OPT_ACL_T, /*!< fields: struct ast_ha * */
|
||||||
|
OPT_BOOL_T, /*!< fields: unsigned int */
|
||||||
|
OPT_CODEC_T, /*!< fields: struct ast_codec pref, struct ast_format_cap * */
|
||||||
|
OPT_CUSTOM_T, /*!< fields: none */
|
||||||
|
OPT_DOUBLE_T, /*!< fields: double */
|
||||||
|
OPT_INT_T, /*!< fields: int */
|
||||||
|
OPT_SOCKADDR_T, /*!< fields: struct ast_sockaddr */
|
||||||
|
OPT_STRINGFIELD_T, /*!< fields: ast_string_field */
|
||||||
|
OPT_UINT_T, /*!< fields: unsigned int */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! \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
|
||||||
|
* \param obj The object to be configured
|
||||||
|
*
|
||||||
|
* \retval 0 Parsing and recording the config value succeeded
|
||||||
|
* \retval non-zero Failure. Parsing should stop and no reload applied
|
||||||
|
*/
|
||||||
|
typedef int (*aco_option_handler)(const struct aco_option *opt, struct ast_variable *var, void *obj);
|
||||||
|
|
||||||
|
/*! \brief Allocate a container to hold config options */
|
||||||
|
struct ao2_container *aco_option_container_alloc(void);
|
||||||
|
|
||||||
|
/*! \brief Process a config info via the options registered with an aco_info
|
||||||
|
*
|
||||||
|
* \param info The config_options_info to be used for handling the config
|
||||||
|
* \param reload Whether or not this is a reload
|
||||||
|
*
|
||||||
|
* \retval 0 Success
|
||||||
|
* \retval -1 Failure
|
||||||
|
*/
|
||||||
|
int aco_process_config(struct aco_info *info, int reload);
|
||||||
|
|
||||||
|
/*! \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 aco_file *file, struct ast_config *cfg);
|
||||||
|
|
||||||
|
/*! \brief Parse each option defined in a config category
|
||||||
|
* \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
|
||||||
|
*
|
||||||
|
* \retval 0 Success
|
||||||
|
* \retval -1 Failure
|
||||||
|
*/
|
||||||
|
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_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_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 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)
|
||||||
|
* \param flags \a type specific flags, stored in the option and available to the handler
|
||||||
|
* \param argc The number for variadic arguments
|
||||||
|
* \param ... field offsets to store for default handlers
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
|
int __aco_option_register(struct aco_info *info, const char *name, enum aco_matchtype match_type, 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 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 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, matchtype, types, default_val, opt_type, flags, ...) \
|
||||||
|
__aco_option_register(info, name, matchtype, 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 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, matchtype, type, default_val, handler, flags) \
|
||||||
|
__aco_option_register(info, name, matchtype, 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
|
||||||
|
* 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, like 640K, should be good enough for anyone. If not, it is
|
||||||
|
* easy to add more.
|
||||||
|
* */
|
||||||
|
|
||||||
|
/*! \def ARGMAP(func, func_arg, x, ...)
|
||||||
|
* \brief Map \a func(\a func_arg, field) across all fields including \a x
|
||||||
|
* \param func The function (almost certainly offsetof) to map across the fields
|
||||||
|
* \param func_arg The first argument (almost certainly a type (e.g. "struct mystruct")
|
||||||
|
* \param x The first field
|
||||||
|
* \param varargs The rest of the fields
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* \code
|
||||||
|
* struct foo {
|
||||||
|
* int a;
|
||||||
|
* char *b;
|
||||||
|
* foo *c;
|
||||||
|
* };
|
||||||
|
* ARGMAP(offsetof, struct foo, a, c)
|
||||||
|
* \endcode
|
||||||
|
* produces the string:
|
||||||
|
* \code
|
||||||
|
* 2, offsetof(struct foo, a), offsetof(struct foo, b)
|
||||||
|
* \encode
|
||||||
|
* which can be passed as the varargs to some other function
|
||||||
|
*
|
||||||
|
* The macro isn't limited to offsetof, but that is the only purpose for
|
||||||
|
* which it has been tested.
|
||||||
|
*
|
||||||
|
* As an example of how the processing works:
|
||||||
|
*
|
||||||
|
* ARGMAP(offsetof, struct foo, a, b, c) ->
|
||||||
|
* ARGMAP_(3, offsetof, struct foo, a, b, c) ->
|
||||||
|
* ARGMAP_3(offsetof, struct foo, 3, a, b, c) ->
|
||||||
|
* ARGMAP_2(offsetof, struct foo, ARGIFY(3, offsetof(struct foo, a)), b, c) ->
|
||||||
|
* ARGMAP_1(offsetof, struct foo, ARGIFY(3, offsetof(struct foo, a), offsetof(struct foo, b)), c) ->
|
||||||
|
* ARGIFY(3, offsetof(struct foo, a), offsetof(struct foo, b), offsetof(struct foo, c)) ->
|
||||||
|
* 3, offsetof(struct foo, a), offsetof(struct foo, b), offsetof(struct foo, c)
|
||||||
|
*/
|
||||||
|
#define ARGMAP(func, func_arg, x, ...) ARGMAP_(VA_NARGS(x, ##__VA_ARGS__), func, func_arg, x, __VA_ARGS__)
|
||||||
|
|
||||||
|
/*! \note This is sneaky. On the very first argument, we set "in" to N, the number of arguments, so
|
||||||
|
* that the accumulation both works properly for the first argument (since "in" can't be empty) and
|
||||||
|
* we get the number of arguments in our varargs as a bonus */
|
||||||
|
#define ARGMAP_(N, func, func_arg, x, ...) PASTE(ARGMAP_, N)(func, func_arg, N, x, __VA_ARGS__)
|
||||||
|
|
||||||
|
/*! \def PASTE(arg1, arg2)
|
||||||
|
* \brief Paste two arguments together, even if they are macros themselves
|
||||||
|
* \note Uses two levels to handle the case where arg1 and arg2 are macros themselves
|
||||||
|
*/
|
||||||
|
#define PASTE(arg1, arg2) PASTE1(arg1, arg2)
|
||||||
|
#define PASTE1(arg1, arg2) arg1##arg2
|
||||||
|
|
||||||
|
/*! \brief Take a comma-separated list and allow it to be passed as a single argument to another macro */
|
||||||
|
#define ARGIFY(...) __VA_ARGS__
|
||||||
|
|
||||||
|
/*! \brief The individual field handlers for ARGMAP
|
||||||
|
* \param func The function (most likely offsetof)
|
||||||
|
* \param func_arg The first argument to func (most likely a type e.g. "struct my_struct")
|
||||||
|
* \param in The accumulated function-mapped field names so far
|
||||||
|
* \param x The next field name
|
||||||
|
* \param varargs The rest of the field names
|
||||||
|
*/
|
||||||
|
#define ARGMAP_1(func, func_arg, in, x, ...) ARGIFY(in, func(func_arg, x))
|
||||||
|
#define ARGMAP_2(func, func_arg, in, x, ...)\
|
||||||
|
ARGMAP_1(func, func_arg, ARGIFY(in, func(func_arg, x)), __VA_ARGS__)
|
||||||
|
#define ARGMAP_3(func, func_arg, in, x, ...)\
|
||||||
|
ARGMAP_2(func, func_arg, ARGIFY(in, func(func_arg, x)), __VA_ARGS__)
|
||||||
|
#define ARGMAP_4(func, func_arg, in, x, ...)\
|
||||||
|
ARGMAP_3(func, func_arg, ARGIFY(in, func(func_arg, x)), __VA_ARGS__)
|
||||||
|
#define ARGMAP_5(func, func_arg, in, x, ...)\
|
||||||
|
ARGMAP_4(func, func_arg, ARGIFY(in, func(func_arg, x)), __VA_ARGS__)
|
||||||
|
#define ARGMAP_6(func, func_arg, in, x, ...)\
|
||||||
|
ARGMAP_5(func, func_arg, ARGIFY(in, func(func_arg, x)), __VA_ARGS__)
|
||||||
|
#define ARGMAP_7(func, func_arg, in, x, ...)\
|
||||||
|
ARGMAP_6(func, func_arg, ARGIFY(in, func(func_arg, x)), __VA_ARGS__)
|
||||||
|
#define ARGMAP_8(func, func_arg, in, x, ...)\
|
||||||
|
ARGMAP_7(func, func_arg, ARGIFY(in, func(func_arg, x)), __VA_ARGS__)
|
||||||
|
|
||||||
|
/*! \def VA_NARGS(...)
|
||||||
|
* \brief Results in the number of arguments passed to it
|
||||||
|
* \note Currently only up to 8, but expanding is easy. This macro basically counts
|
||||||
|
* commas + 1. To visualize:
|
||||||
|
*
|
||||||
|
* VA_NARGS(one, two, three) -> v
|
||||||
|
* VA_NARGS1(one, two, three, 8, 7, 6, 5, 4, 3, 2, 1, 0) ->
|
||||||
|
* VA_NARGS1( _1, _2, _3, _4, _5, _6, _7, _8, N, ... ) N -> 3
|
||||||
|
*
|
||||||
|
* Note that VA_NARGS *does not* work when there are no arguments passed. Pasting an empty
|
||||||
|
* __VA_ARGS__ with a comma like ", ##__VA_ARGS__" will delete the leading comma, but it
|
||||||
|
* does not work when __VA_ARGS__ is the first argument. Instead, 1 is returned instead of 0:
|
||||||
|
*
|
||||||
|
* VA_NARGS() -> v
|
||||||
|
* VA_NARGS1( , 8, 7, 6, 5, 4, 3, 2, 1, 0) ->
|
||||||
|
* VA_NARGS1(_1, _2, _3, _4, _5, _6, _7, _8, N) -> 1
|
||||||
|
*/
|
||||||
|
#define VA_NARGS(...) VA_NARGS1(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||||
|
#define VA_NARGS1(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
|
||||||
|
|
||||||
|
/*! \def FLDSET(type, ...)
|
||||||
|
* \brief Convert a struct and list of fields to an argument list of field offsets
|
||||||
|
* \param type The type with the fields (e.g. "struct my_struct")
|
||||||
|
* \param varags The fields in the struct whose offsets are needed as arguments
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
* \code
|
||||||
|
* struct foo {int a, char b[128], char *c};
|
||||||
|
* FLDSET(struct foo, a, c)
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* produces
|
||||||
|
* \code
|
||||||
|
* offsetof(struct foo, a), offsetof(struct foo, c)
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
#define FLDSET(type, ...) FLDSET1(type, ##__VA_ARGS__)
|
||||||
|
#define FLDSET1(type, ...) POPPED(ARGMAP(offsetof, type, ##__VA_ARGS__))
|
||||||
|
|
||||||
|
/*! \def STRFLDSET(type, ...)
|
||||||
|
* \brief Convert a struct and a list of stringfield fields to an argument list of field offsets
|
||||||
|
* \note Stringfields require the passing of the field manager pool, and field manager to the
|
||||||
|
* default stringfield option handler, so registering options that point to stringfields requires
|
||||||
|
* this macro to be called instead of the FLDSET macro.
|
||||||
|
* \param type The type with the fields (e.g. "struct my_struct")
|
||||||
|
* \param varargs The fields in the struct whose offsets are needed as arguments
|
||||||
|
*/
|
||||||
|
#define STRFLDSET(type, ...) FLDSET(type, __VA_ARGS__, __field_mgr_pool, __field_mgr)
|
||||||
|
|
||||||
|
/*! \def POPPED(...)
|
||||||
|
* \brief A list of arguments without the first argument
|
||||||
|
* \note Used internally to remove the leading "number of arguments" argument from ARGMAP for
|
||||||
|
* FLDSET. This is because a call to FLDSET may be followed by additional arguments in
|
||||||
|
* aco_register_option, so the true number of arguments will possibly be different than what
|
||||||
|
* ARGMAP returns.
|
||||||
|
* \params varags A list of arguments
|
||||||
|
*
|
||||||
|
* POPPED(a, b, c) -> b, c
|
||||||
|
*/
|
||||||
|
#define POPPED(...) POPPED1(__VA_ARGS__)
|
||||||
|
#define POPPED1(x, ...) __VA_ARGS__
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ASTERISK_CONFIG_OPTIONS_H */
|
@ -0,0 +1,693 @@
|
|||||||
|
/*
|
||||||
|
* Asterisk -- An open source telephony toolkit.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012, Digium, Inc.
|
||||||
|
*
|
||||||
|
* Mark Spencer <markster@digium.com>
|
||||||
|
*
|
||||||
|
* See http://www.asterisk.org for more information about
|
||||||
|
* the Asterisk project. Please do not directly contact
|
||||||
|
* any of the maintainers of this project for assistance;
|
||||||
|
* the project provides a web site, mailing lists and IRC
|
||||||
|
* channels for your use.
|
||||||
|
*
|
||||||
|
* This program is free software, distributed under the terms of
|
||||||
|
* the GNU General Public License Version 2. See the LICENSE file
|
||||||
|
* at the top of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \file
|
||||||
|
* \brief Configuration Option-handling
|
||||||
|
* \author Terry Wilson <twilson@digium.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "asterisk.h"
|
||||||
|
|
||||||
|
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||||
|
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
|
#include "asterisk/config.h"
|
||||||
|
#include "asterisk/config_options.h"
|
||||||
|
#include "asterisk/stringfields.h"
|
||||||
|
#include "asterisk/acl.h"
|
||||||
|
#include "asterisk/frame.h"
|
||||||
|
|
||||||
|
#ifdef LOW_MEMORY
|
||||||
|
#define CONFIG_OPT_BUCKETS 5
|
||||||
|
#else
|
||||||
|
#define CONFIG_OPT_BUCKETS 53
|
||||||
|
#endif /* LOW_MEMORY */
|
||||||
|
|
||||||
|
/*! \brief Bits of aco_info that shouldn't be assigned outside this file
|
||||||
|
* \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 */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct aco_option {
|
||||||
|
const char *name;
|
||||||
|
enum aco_matchtype match_type;
|
||||||
|
regex_t *name_regex;
|
||||||
|
const char *default_val;
|
||||||
|
struct aco_type **obj;
|
||||||
|
enum aco_option_type type;
|
||||||
|
aco_option_handler handler;
|
||||||
|
unsigned int flags;
|
||||||
|
size_t argc;
|
||||||
|
intptr_t args[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
void *aco_pending_config(struct aco_info *info)
|
||||||
|
{
|
||||||
|
if (!(info && info->internal)) {
|
||||||
|
ast_log(LOG_ERROR, "This may not be called without an initialized aco_info!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return info->internal->pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_option_destroy(void *obj)
|
||||||
|
{
|
||||||
|
struct aco_option *opt = obj;
|
||||||
|
if (opt->match_type == ACO_REGEX && opt->name_regex) {
|
||||||
|
regfree(opt->name_regex);
|
||||||
|
ast_free(opt->name_regex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int int_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
|
||||||
|
static int uint_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
|
||||||
|
static int double_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
|
||||||
|
static int sockaddr_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
|
||||||
|
static int stringfield_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
|
||||||
|
static int bool_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
|
||||||
|
static int acl_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
|
||||||
|
static int codec_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj);
|
||||||
|
|
||||||
|
static aco_option_handler ast_config_option_default_handler(enum aco_option_type type)
|
||||||
|
{
|
||||||
|
switch(type) {
|
||||||
|
case OPT_ACL_T: return acl_handler_fn;
|
||||||
|
case OPT_BOOL_T: return bool_handler_fn;
|
||||||
|
case OPT_CODEC_T: return codec_handler_fn;
|
||||||
|
case OPT_DOUBLE_T: return double_handler_fn;
|
||||||
|
case OPT_INT_T: return int_handler_fn;
|
||||||
|
case OPT_SOCKADDR_T: return sockaddr_handler_fn;
|
||||||
|
case OPT_STRINGFIELD_T: return stringfield_handler_fn;
|
||||||
|
case OPT_UINT_T: return uint_handler_fn;
|
||||||
|
|
||||||
|
case OPT_CUSTOM_T: return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static regex_t *build_regex(const char *text)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
regex_t *regex;
|
||||||
|
|
||||||
|
if (!(regex = ast_malloc(sizeof(*regex)))) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((res = regcomp(regex, text, REG_EXTENDED | REG_ICASE | REG_NOSUB))) {
|
||||||
|
size_t len = regerror(res, regex, NULL, 0);
|
||||||
|
char buf[len];
|
||||||
|
regerror(res, regex, buf, len);
|
||||||
|
ast_log(LOG_ERROR, "Could not compile regex '%s': %s\n", text, buf);
|
||||||
|
ast_free(regex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return regex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __aco_option_register(struct aco_info *info, const char *name, enum aco_matchtype matchtype, 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 && kind == OPT_CUSTOM_T) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(types && types[0])) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(opt = ao2_alloc(sizeof(*opt) + argc * sizeof(opt->args[0]), config_option_destroy))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchtype == ACO_REGEX && !(opt->name_regex = build_regex(name))) {
|
||||||
|
ao2_ref(opt, -1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start(ap, argc);
|
||||||
|
for (tmp = 0; tmp < argc; tmp++) {
|
||||||
|
opt->args[tmp] = va_arg(ap, size_t);
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
opt->name = name;
|
||||||
|
opt->match_type = matchtype;
|
||||||
|
opt->default_val = default_val;
|
||||||
|
opt->type = kind;
|
||||||
|
opt->handler = handler;
|
||||||
|
opt->flags = flags;
|
||||||
|
opt->argc = argc;
|
||||||
|
|
||||||
|
if (!opt->handler && !(opt->handler = ast_config_option_default_handler(opt->type))) {
|
||||||
|
/* This should never happen */
|
||||||
|
ast_log(LOG_ERROR, "No handler provided, and no default handler exists for type %d\n", opt->type);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_opt_hash(const void *obj, const int flags)
|
||||||
|
{
|
||||||
|
const struct aco_option *opt = obj;
|
||||||
|
const char *name = (flags & OBJ_KEY) ? obj : opt->name;
|
||||||
|
return ast_str_case_hash(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_opt_cmp(void *obj, void *arg, int flags)
|
||||||
|
{
|
||||||
|
struct aco_option *opt1 = obj, *opt2 = arg;
|
||||||
|
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, int flags)
|
||||||
|
{
|
||||||
|
struct aco_option *match = obj;
|
||||||
|
const char *name = arg;
|
||||||
|
|
||||||
|
switch (match->match_type) {
|
||||||
|
case ACO_EXACT:
|
||||||
|
return strcasecmp(name, match->name) ? 0 : CMP_MATCH | CMP_STOP;
|
||||||
|
case ACO_REGEX:
|
||||||
|
return regexec(match->name_regex, name, 0, NULL, 0) ? 0 : CMP_MATCH | CMP_STOP;
|
||||||
|
}
|
||||||
|
ast_log(LOG_ERROR, "Unknown match type. This should not be possible.\n");
|
||||||
|
return CMP_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct aco_option *aco_option_find(struct aco_type *type, const char *name)
|
||||||
|
{
|
||||||
|
struct aco_option *opt;
|
||||||
|
/* Try an exact match with OBJ_KEY for the common/fast case, then iterate through
|
||||||
|
* all options for the regex cases */
|
||||||
|
if (!(opt = ao2_callback(type->internal->opts, OBJ_KEY, find_option_cb, (void *) name))) {
|
||||||
|
opt = ao2_callback(type->internal->opts, 0, find_option_cb, (void *) name);
|
||||||
|
}
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ao2_container *aco_option_container_alloc(void)
|
||||||
|
{
|
||||||
|
return ao2_container_alloc(CONFIG_OPT_BUCKETS, config_opt_hash, config_opt_cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = file->types[x]; match; match = file->types[++x]) {
|
||||||
|
/* First make sure we are an object that can service this category */
|
||||||
|
if (!regexec(match->internal->regex, category, 0, NULL, 0) == !match->category_match) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then, see if we need to match a particular field */
|
||||||
|
if (!ast_strlen_zero(match->matchfield) && (!ast_strlen_zero(match->matchvalue) || match->matchfunc)) {
|
||||||
|
if (!(val = ast_variable_retrieve(cfg, category, match->matchfield))) {
|
||||||
|
ast_log(LOG_ERROR, "Required match field '%s' not found\n", match->matchfield);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (match->matchfunc) {
|
||||||
|
if (!match->matchfunc(val)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (strcasecmp(val, match->matchvalue)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If we get this far, we're a match */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_preload(struct aco_file *file, const char *cat)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!file->preload) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, struct aco_file *file, const char *cat, int preload) {
|
||||||
|
RAII_VAR(void *, new_item, NULL, ao2_cleanup);
|
||||||
|
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(file, cat)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find aco_type by category, if not found it is an error */
|
||||||
|
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 (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 (!(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type->item_pre_process && type->item_pre_process(new_item)) {
|
||||||
|
ast_log(LOG_ERROR, "In %s: Preprocess 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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", file->filename);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (internal_process_ast_config(info, file, cfg)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->pre_apply_config && info->pre_apply_config()) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apply_config(info)) {
|
||||||
|
goto error;
|
||||||
|
};
|
||||||
|
|
||||||
|
ao2_cleanup(info->internal->pending);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
ao2_cleanup(info->internal->pending);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int aco_process_config(struct aco_info *info, int reload)
|
||||||
|
{
|
||||||
|
struct ast_config *cfg;
|
||||||
|
struct ast_flags cfg_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0, };
|
||||||
|
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 (!(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_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(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;
|
||||||
|
}
|
||||||
|
if (!opt->handler) {
|
||||||
|
/* It should be impossible for an option to not have a handler */
|
||||||
|
ast_log(LOG_ERROR, "BUG! Somehow a config option for %s/%s was created with no handler!\n", cat, var->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (opt->handler(opt, var, obj)) {
|
||||||
|
ast_log(LOG_ERROR, "Error parsing %s=%s at line %d of %s\n", var->name, var->value, var->lineno, var->file);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void internal_type_destroy(struct aco_type *type)
|
||||||
|
{
|
||||||
|
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 = file->types[x]; t; t = file->types[++x]) {
|
||||||
|
internal_type_destroy(t);
|
||||||
|
t = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int internal_type_init(struct aco_type *type)
|
||||||
|
{
|
||||||
|
if (!(type->internal = ast_calloc(1, sizeof(*type->internal)))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(type->internal->regex = build_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, y;
|
||||||
|
|
||||||
|
if (!(info->internal = ast_calloc(1, sizeof(*info->internal)))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
aco_info_destroy(info);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void aco_info_destroy(struct aco_info *info)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
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)) {
|
||||||
|
ao2_ref(opt, -1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
ao2_iterator_destroy(&iter);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ao2_ref(opt, -1);
|
||||||
|
}
|
||||||
|
ao2_iterator_destroy(&iter);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* default config option handlers */
|
||||||
|
static int int_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj) {
|
||||||
|
int *field = (int *)(obj + opt->args[0]);
|
||||||
|
unsigned int flags = PARSE_INT32 | opt->flags;
|
||||||
|
int res = 0;
|
||||||
|
if (opt->flags & PARSE_IN_RANGE) {
|
||||||
|
res = opt->flags & PARSE_DEFAULT ?
|
||||||
|
ast_parse_arg(var->value, flags, field, (int) opt->args[1], (int) opt->args[2], opt->args[3]) :
|
||||||
|
ast_parse_arg(var->value, flags, field, (int) opt->args[1], (int) opt->args[2]);
|
||||||
|
if (res) {
|
||||||
|
if (opt->flags & PARSE_RANGE_DEFAULTS) {
|
||||||
|
ast_log(LOG_WARNING, "Failed to set %s=%s. Set to %d instead due to range limit (%d, %d)\n", var->name, var->value, *field, (int) opt->args[1], (int) opt->args[2]);
|
||||||
|
res = 0;
|
||||||
|
} else if (opt->flags & PARSE_DEFAULT) {
|
||||||
|
ast_log(LOG_WARNING, "Failed to set %s=%s, Set to default value %d instead.\n", var->name, var->value, *field);
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ((opt->flags & PARSE_DEFAULT) && ast_parse_arg(var->value, flags, field, (int) opt->args[1])) {
|
||||||
|
ast_log(LOG_WARNING, "Attempted to set %s=%s, but set it to %d instead due to default)\n", var->name, var->value, *field);
|
||||||
|
} else {
|
||||||
|
res = ast_parse_arg(var->value, flags, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uint_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj) {
|
||||||
|
unsigned int *field = (unsigned int *)(obj + opt->args[0]);
|
||||||
|
unsigned int flags = PARSE_INT32 | opt->flags;
|
||||||
|
int res = 0;
|
||||||
|
if (opt->flags & PARSE_IN_RANGE) {
|
||||||
|
res = opt->flags & PARSE_DEFAULT ?
|
||||||
|
ast_parse_arg(var->value, flags, field, (unsigned int) opt->args[1], (unsigned int) opt->args[2], opt->args[3]) :
|
||||||
|
ast_parse_arg(var->value, flags, field, (unsigned int) opt->args[1], (unsigned int) opt->args[2]);
|
||||||
|
if (res) {
|
||||||
|
if (opt->flags & PARSE_RANGE_DEFAULTS) {
|
||||||
|
ast_log(LOG_WARNING, "Failed to set %s=%s. Set to %d instead due to range limit (%d, %d)\n", var->name, var->value, *field, (int) opt->args[1], (int) opt->args[2]);
|
||||||
|
res = 0;
|
||||||
|
} else if (opt->flags & PARSE_DEFAULT) {
|
||||||
|
ast_log(LOG_WARNING, "Failed to set %s=%s, Set to default value %d instead.\n", var->name, var->value, *field);
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ((opt->flags & PARSE_DEFAULT) && ast_parse_arg(var->value, flags, field, (unsigned int) opt->args[1])) {
|
||||||
|
ast_log(LOG_WARNING, "Attempted to set %s=%s, but set it to %u instead due to default)\n", var->name, var->value, *field);
|
||||||
|
} else {
|
||||||
|
res = ast_parse_arg(var->value, flags, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int double_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj) {
|
||||||
|
double *field = (double *)(obj + opt->args[0]);
|
||||||
|
return ast_parse_arg(var->value, PARSE_DOUBLE | opt->flags, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acl_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj) {
|
||||||
|
struct ast_ha **ha = (struct ast_ha **)(obj + opt->args[0]);
|
||||||
|
const char *permit = (const char *) opt->args[1];
|
||||||
|
int error = 0;
|
||||||
|
*ha = ast_append_ha(permit, var->value, *ha, &error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* opt->args[0] = struct ast_codec_pref, opt->args[1] struct ast_format_cap * */
|
||||||
|
static int codec_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj) {
|
||||||
|
struct ast_codec_pref *pref = (struct ast_codec_pref *)(obj + opt->args[0]);
|
||||||
|
struct ast_format_cap **cap = (struct ast_format_cap **)(obj + opt->args[1]);
|
||||||
|
return ast_parse_allow_disallow(pref, *cap, var->value, opt->flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* opt->args[0] = ast_string_field, opt->args[1] = field_mgr_pool, opt->args[2] = field_mgr */
|
||||||
|
static int stringfield_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||||
|
{
|
||||||
|
ast_string_field *field = (const char **)(obj + opt->args[0]);
|
||||||
|
struct ast_string_field_pool **pool = (struct ast_string_field_pool **)(obj + opt->args[1]);
|
||||||
|
struct ast_string_field_mgr *mgr = (struct ast_string_field_mgr *)(obj + opt->args[2]);
|
||||||
|
ast_string_field_ptr_set_by_fields(*pool, *mgr, field, var->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bool_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||||
|
{
|
||||||
|
unsigned int *field = (unsigned int *)(obj + opt->args[0]);
|
||||||
|
*field = opt->flags ? ast_true(var->value) : ast_false(var->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sockaddr_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||||
|
{
|
||||||
|
struct ast_sockaddr *field = (struct ast_sockaddr *)(obj + opt->args[0]);
|
||||||
|
return ast_parse_arg(var->value, PARSE_ADDR | opt->flags, field);
|
||||||
|
}
|
Loading…
Reference in new issue