From d54717c39e62f4cc8b290ac4836c4d4469d87c24 Mon Sep 17 00:00:00 2001 From: Terry Wilson Date: Fri, 1 Jun 2012 16:33:25 +0000 Subject: Add new config-parsing framework 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-fbb531ad65f3 --- include/asterisk/astobj2.h | 6 + include/asterisk/config.h | 43 ++-- include/asterisk/config_options.h | 482 ++++++++++++++++++++++++++++++++++++++ include/asterisk/stringfields.h | 36 +-- include/asterisk/utils.h | 47 ++++ 5 files changed, 576 insertions(+), 38 deletions(-) create mode 100644 include/asterisk/config_options.h (limited to 'include') diff --git a/include/asterisk/astobj2.h b/include/asterisk/astobj2.h index 15ed4892e..a5068ebe6 100644 --- a/include/asterisk/astobj2.h +++ b/include/asterisk/astobj2.h @@ -1461,4 +1461,10 @@ void *__ao2_iterator_next(struct ao2_iterator *iter); /* extra functions */ void ao2_bt(void); /* backtrace */ +/*! gcc __attribute__(cleanup()) functions + * \note they must be able to handle NULL parameters because most of the + * allocation/find functions can fail and we don't want to try to tear + * down a NULL */ +void ao2_cleanup(void *obj); +void ao2_iterator_cleanup(struct ao2_iterator *iter); #endif /* _ASTERISK_ASTOBJ2_H */ diff --git a/include/asterisk/config.h b/include/asterisk/config.h index 86c2bb5dd..5e71d80b1 100644 --- a/include/asterisk/config.h +++ b/include/asterisk/config.h @@ -644,8 +644,9 @@ enum ast_parse_flags { * the range (inclusive). An error is returned if the value * is outside or inside the range, respectively. */ - PARSE_IN_RANGE = 0x0020, /* accept values inside a range */ - PARSE_OUT_RANGE = 0x0040, /* accept values outside a range */ + PARSE_IN_RANGE = 0x0020, /* accept values inside a range */ + PARSE_OUT_RANGE = 0x0040, /* accept values outside a range */ + PARSE_RANGE_DEFAULTS = 0x0080, /* default to range min/max on range error */ /* Port handling, for ast_sockaddr. accept/ignore/require/forbid * port number after the hostname or address. @@ -661,32 +662,32 @@ enum ast_parse_flags { * * \param arg the string to parse. It is not modified. * \param flags combination of ast_parse_flags to specify the - * return type and additional checks. + * return type and additional checks. * \param result pointer to the result. NULL is valid here, and can - * be used to perform only the validity checks. + * be used to perform only the validity checks. * \param ... extra arguments are required according to flags. * * \retval 0 in case of success, != 0 otherwise. * \retval result returns the parsed value in case of success, - * the default value in case of error, or it is left unchanged - * in case of error and no default specified. Note that in certain - * cases (e.g. sockaddr_in, with multi-field return values) some - * of the fields in result may be changed even if an error occurs. + * the default value in case of error, or it is left unchanged + * in case of error and no default specified. Note that in certain + * cases (e.g. sockaddr_in, with multi-field return values) some + * of the fields in result may be changed even if an error occurs. * * \details * Examples of use: - * ast_parse_arg("223", PARSE_INT32|PARSE_IN_RANGE, - * &a, -1000, 1000); - * returns 0, a = 223 - * ast_parse_arg("22345", PARSE_INT32|PARSE_IN_RANGE|PARSE_DEFAULT, - * &a, 9999, 10, 100); - * returns 1, a = 9999 - * ast_parse_arg("22345ssf", PARSE_UINT32|PARSE_IN_RANGE, &b, 10, 100); - * returns 1, b unchanged - * ast_parse_arg("www.foo.biz:44", PARSE_INADDR, &sa); - * returns 0, sa contains address and port - * ast_parse_arg("www.foo.biz", PARSE_INADDR|PARSE_PORT_REQUIRE, &sa); - * returns 1 because port is missing, sa contains address + * ast_parse_arg("223", PARSE_INT32|PARSE_IN_RANGE, &a, -1000, 1000); + * returns 0, a = 223 + * ast_parse_arg("22345", PARSE_INT32|PARSE_IN_RANGE|PARSE_DEFAULT, &a, 9999, 10, 100); + * returns 1, a = 9999 + * ast_parse_arg("22345ssf", PARSE_UINT32|PARSE_IN_RANGE, &b, 10, 100); + * returns 1, b unchanged + * ast_parse_arg("12", PARSE_UINT32|PARSE_IN_RANGE|PARSE_RANGE_DEFAULTS, &a, 1, 10); + * returns 1, a = 10 + * ast_parse_arg("www.foo.biz:44", PARSE_INADDR, &sa); + * returns 0, sa contains address and port + * ast_parse_arg("www.foo.biz", PARSE_INADDR|PARSE_PORT_REQUIRE, &sa); + * returns 1 because port is missing, sa contains address */ int ast_parse_arg(const char *arg, enum ast_parse_flags flags, void *result, ...); @@ -696,7 +697,7 @@ int ast_parse_arg(const char *arg, enum ast_parse_flags flags, * string in a switch() statement, yet we need a similar behaviour, with many * branches and a break on a matching one. * The following somehow simplifies the job: we create a block using - * the CV_START and CV_END macros, and then within the block we can run + * the CV_START and CV_END macros, and then within the block we can run * actions such as "if (condition) { body; break; }" * Additional macros are present to run simple functions (e.g. ast_copy_string) * or to pass arguments to ast_parse_arg() diff --git a/include/asterisk/config_options.h b/include/asterisk/config_options.h new file mode 100644 index 000000000..a1b7f3714 --- /dev/null +++ b/include/asterisk/config_options.h @@ -0,0 +1,482 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2012, Digium, Inc. + * + * Mark Spencer + * + * 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 + */ + +#ifndef _ASTERISK_CONFIG_OPTIONS_H +#define _ASTERISK_CONFIG_OPTIONS_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#include + +#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 */ diff --git a/include/asterisk/stringfields.h b/include/asterisk/stringfields.h index 86e265beb..b5fdf9c8e 100644 --- a/include/asterisk/stringfields.h +++ b/include/asterisk/stringfields.h @@ -310,25 +310,27 @@ void __ast_string_field_release_active(struct ast_string_field_pool *pool_head, \brief Set a field to a simple string value \param x Pointer to a structure containing fields \param ptr Pointer to a field within the structure - \param data String value to be copied into the field + \param data String value to be copied into the field \return nothing */ -#define ast_string_field_ptr_set(x, ptr, data) do { \ - const char *__d__ = (data); \ - size_t __dlen__ = (__d__) ? strlen(__d__) + 1 : 1; \ - ast_string_field *__p__ = (ast_string_field *) (ptr); \ - if (__dlen__ == 1) { \ - __ast_string_field_release_active((x)->__field_mgr_pool, *__p__); \ - *__p__ = __ast_string_field_empty; \ - } else if ((__dlen__ <= AST_STRING_FIELD_ALLOCATION(*__p__)) || \ - (!__ast_string_field_ptr_grow(&(x)->__field_mgr, &(x)->__field_mgr_pool, __dlen__, __p__)) || \ - (*__p__ = __ast_string_field_alloc_space(&(x)->__field_mgr, &(x)->__field_mgr_pool, __dlen__))) { \ - if (*__p__ != (*ptr)) { \ - __ast_string_field_release_active((x)->__field_mgr_pool, (*ptr)); \ - } \ - memcpy(* (void **) __p__, __d__, __dlen__); \ - } \ - } while (0) +#define ast_string_field_ptr_set(x, ptr, data) ast_string_field_ptr_set_by_fields((x)->__field_mgr_pool, (x)->__field_mgr, ptr, data) + +#define ast_string_field_ptr_set_by_fields(field_mgr_pool, field_mgr, ptr, data) do { \ + const char *__d__ = (data); \ + size_t __dlen__ = (__d__) ? strlen(__d__) + 1 : 1; \ + ast_string_field *__p__ = (ast_string_field *) (ptr); \ + if (__dlen__ == 1) { \ + __ast_string_field_release_active(field_mgr_pool, *__p__); \ + *__p__ = __ast_string_field_empty; \ + } else if ((__dlen__ <= AST_STRING_FIELD_ALLOCATION(*__p__)) || \ + (!__ast_string_field_ptr_grow(&field_mgr, &field_mgr_pool, __dlen__, __p__)) || \ + (*__p__ = __ast_string_field_alloc_space(&field_mgr, &field_mgr_pool, __dlen__))) { \ + if (*__p__ != (*ptr)) { \ + __ast_string_field_release_active(field_mgr_pool, (*ptr)); \ + } \ + memcpy(* (void **) __p__, __d__, __dlen__); \ + } \ + } while (0) /*! \brief Set a field to a simple string value diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h index c33f42322..4ebd3ead8 100644 --- a/include/asterisk/utils.h +++ b/include/asterisk/utils.h @@ -863,4 +863,51 @@ int ast_get_tid(void); */ char *ast_utils_which(const char *binary, char *fullpath, size_t fullpath_size); +/*! \brief Declare a variable that will call a destructor function when it goes out of scope + * \param vartype The type of the variable + * \param varname The name of the variable + * \param initval The initial value of the variable + * \param dtor The destructor function of type' void func(vartype *)' + * + * \code + * void mything_cleanup(struct mything *t) + * { + * if (t) { + * ast_free(t->stuff); + * } + * } + * + * void do_stuff(const char *name) + * { + * RAII_VAR(struct mything *, thing, mything_alloc(name), mything_cleanup); + * ... + * } + * + * \note This macro is especially useful for working with ao2 objects. A common idiom + * would be a function that needed to look up an ao2 object and might have several error + * conditions after the allocation that would normally need to unref the ao2 object. + * With RAII_VAR, it is possible to just return and leave the cleanup to the destructor + * function. For example: + * \code + * void do_stuff(const char *name) + * { + * RAII_VAR(struct mything *, thing, find_mything(name), ao2_cleanup); + * if (!thing) { + * return; + * } + * if (error) { + * return; + * } + * do_stuff_with_thing(thing); + * return; + * } + * } + * \encode + * + */ +#define RAII_VAR(vartype, varname, initval, dtor) \ + auto void _dtor_ ## varname (vartype * v); \ + auto void _dtor_ ## varname (vartype * v) { dtor(*v); } \ + vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval) + #endif /* _ASTERISK_UTILS_H */ -- cgit v1.2.3