Skip to content
Snippets Groups Projects
config_options.c 47 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	}
    
    	if (!(info = ao2_find(xmldocs, module, OBJ_KEY))) {
    		return NULL;
    	}
    
    	cur = info;
    
    	while ((cur = AST_LIST_NEXT(cur, next))) {
    
    		if (!strcasecmp(cur->type, "configObject") && !strncasecmp(word, cur->name, wordlen) && ++which > state) {
    			c = ast_strdup(cur->name);
    			break;
    		}
    	}
    	return c;
    }
    
    /*! \internal
     * \brief Complete the name of the configuration option the user is looking for
     */
    static char *complete_config_option(const char *module, const char *option, const char *word, int pos, int state)
    {
    	char *c = NULL;
    	size_t wordlen = strlen(word);
    	int which = 0;
    	RAII_VAR(struct ast_xml_doc_item *, info, NULL, ao2_cleanup);
    	struct ast_xml_doc_item *cur;
    
    	if (pos != 5) {
    		return NULL;
    	}
    
    	if (!(info = ao2_find(xmldocs, module, OBJ_KEY))) {
    		return NULL;
    	}
    
    	cur = info;
    
    	while ((cur = AST_LIST_NEXT(cur, next))) {
    
    		if (!strcasecmp(cur->type, "configOption") && !strcasecmp(cur->ref, option) && !strncasecmp(word, cur->name, wordlen) && ++which > state) {
    			c = ast_strdup(cur->name);
    			break;
    		}
    	}
    	return c;
    }
    
    /* Define as 0 if we want to allow configurations to be registered without
     * documentation
     */
    
    
    /*! \internal
     * \brief Update the XML documentation for a config type based on its registration
     */
    
    static int xmldoc_update_config_type(const char *module, const char *name, const char *category, const char *matchfield, const char *matchvalue, enum aco_category_op category_match)
    
    {
    	RAII_VAR(struct ast_xml_xpath_results *, results, NULL, ast_xml_xpath_results_free);
    	RAII_VAR(struct ast_xml_doc_item *, config_info, ao2_find(xmldocs, module, OBJ_KEY), ao2_cleanup);
    	struct ast_xml_doc_item *config_type;
    	struct ast_xml_node *type, *syntax, *matchinfo, *tmp;
    
    	/* If we already have a syntax element, bail. This isn't an error, since we may unload a module which
    	 * has updated the docs and then load it again. */
    
    	if ((results = ast_xmldoc_query("/docs/configInfo[@name='%s']/configFile/configObject[@name='%s']/syntax", module, name))) {
    
    	if (!(results = ast_xmldoc_query("/docs/configInfo[@name='%s']/configFile/configObject[@name='%s']", module, name))) {
    
    		ast_log(LOG_WARNING, "Cannot update type '%s' in module '%s' because it has no existing documentation!\n", name, module);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    	if (!(type = ast_xml_xpath_get_first_result(results))) {
    		ast_log(LOG_WARNING, "Could not retrieve documentation for type '%s' in module '%s'\n", name, module);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    	if (!(syntax = ast_xml_new_child(type, "syntax"))) {
    		ast_log(LOG_WARNING, "Could not create syntax node for type '%s' in module '%s'\n", name, module);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    	if (!(matchinfo = ast_xml_new_child(syntax, "matchInfo"))) {
    		ast_log(LOG_WARNING, "Could not create matchInfo node for type '%s' in module '%s'\n", name, module);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    	if (!(tmp = ast_xml_new_child(matchinfo, "category"))) {
    		ast_log(LOG_WARNING, "Could not create category node for type '%s' in module '%s'\n", name, module);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    	ast_xml_set_text(tmp, category);
    
    	switch (category_match) {
    	case ACO_WHITELIST:
    	case ACO_WHITELIST_EXACT:
    	case ACO_WHITELIST_ARRAY:
    		ast_xml_set_attribute(tmp, "match", "true");
    		break;
    	case ACO_BLACKLIST:
    	case ACO_BLACKLIST_EXACT:
    	case ACO_BLACKLIST_ARRAY:
    		ast_xml_set_attribute(tmp, "match", "false");
    		break;
    	}
    
    
    	if (!ast_strlen_zero(matchfield) && !(tmp = ast_xml_new_child(matchinfo, "field"))) {
    		ast_log(LOG_WARNING, "Could not add %s attribute for type '%s' in module '%s'\n", matchfield, name, module);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    	ast_xml_set_attribute(tmp, "name", matchfield);
    	ast_xml_set_text(tmp, matchvalue);
    
    	if (!config_info || !(config_type = find_xmldoc_type(config_info, name))) {
    		ast_log(LOG_WARNING, "Could not obtain XML documentation item for config type %s\n", name);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    	if (ast_xmldoc_regenerate_doc_item(config_type)) {
    		ast_log(LOG_WARNING, "Could not update type '%s' with values from config type registration\n", name);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    	return 0;
    }
    
    /*! \internal
     * \brief Update the XML documentation for a config option based on its registration
     */
    static int xmldoc_update_config_option(struct aco_type **types, const char *module, const char *name, const char *object_name, const char *default_value, unsigned int regex, enum aco_option_type type)
    {
    	RAII_VAR(struct ast_xml_xpath_results *, results, NULL, ast_xml_xpath_results_free);
    	RAII_VAR(struct ast_xml_doc_item *, config_info, ao2_find(xmldocs, module, OBJ_KEY), ao2_cleanup);
    	struct ast_xml_doc_item * config_option;
    	struct ast_xml_node *option;
    
    	ast_assert(ARRAY_LEN(aco_option_type_string) > type);
    
    	if (!config_info || !(config_option = find_xmldoc_option(config_info, types, name))) {
    		ast_log(LOG_ERROR, "XML Documentation for option '%s' in modules '%s' not found!\n", name, module);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    
    	if (!(results = ast_xmldoc_query("/docs/configInfo[@name='%s']/configFile/configObject[@name='%s']/configOption[@name='%s']", module, object_name, name))) {
    
    		ast_log(LOG_WARNING, "Could not find option '%s' with type '%s' in module '%s'\n", name, object_name, module);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    	if (!(option = ast_xml_xpath_get_first_result(results))) {
    
    		ast_log(LOG_WARNING, "Could not obtain results for option '%s' with type '%s' in module '%s'\n", name, object_name, module);
    
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    	ast_xml_set_attribute(option, "regex", regex ? "true" : "false");
    	ast_xml_set_attribute(option, "default", default_value);
    	ast_xml_set_attribute(option, "type", aco_option_type_string[type]);
    
    	if (ast_xmldoc_regenerate_doc_item(config_option)) {
    		ast_log(LOG_WARNING, "Could not update option '%s' with values from config option registration\n", name);
    		return XMLDOC_STRICT ? -1 : 0;
    	}
    
    	return 0;
    }
    
    /*! \internal
     * \brief Show the modules with configuration information
     */
    static void cli_show_modules(struct ast_cli_args *a)
    {
    	struct ast_xml_doc_item *item;
    	struct ao2_iterator it_items;
    
    	ast_assert(a->argc == 3);
    
    	if (ao2_container_count(xmldocs) == 0) {
    		ast_cli(a->fd, "No modules found.\n");
    		return;
    	}
    
    	it_items = ao2_iterator_init(xmldocs, 0);
    	ast_cli(a->fd, "The following modules have configuration information:\n");
    	while ((item = ao2_iterator_next(&it_items))) {
    		ast_cli(a->fd, "\t%s\n", item->name);
    		ao2_ref(item, -1);
    	}
    	ao2_iterator_destroy(&it_items);
    }
    
    /*! \internal
     * \brief Show the configuration types for a module
     */
    static void cli_show_module_types(struct ast_cli_args *a)
    {
    	RAII_VAR(struct ast_xml_doc_item *, item, NULL, ao2_cleanup);
    	struct ast_xml_doc_item *tmp;
    
    	ast_assert(a->argc == 4);
    
    	if (!(item = ao2_find(xmldocs, a->argv[3], OBJ_KEY))) {
    		ast_cli(a->fd, "Module %s not found.\n", a->argv[3]);
    		return;
    	}
    
    	if (ast_str_strlen(item->synopsis)) {
    		ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(item->synopsis), 1));
    	}
    	if (ast_str_strlen(item->description)) {
    		ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(item->description), 1));
    	}
    
    	tmp = item;
    	ast_cli(a->fd, "Configuration option types for %s:\n", tmp->name);
    
    	while ((tmp = AST_LIST_NEXT(tmp, next))) {
    
    		if (!strcasecmp(tmp->type, "configObject")) {
    			ast_cli(a->fd, "%-25s -- %-65.65s\n", tmp->name,
    				ast_str_buffer(tmp->synopsis));
    		}
    	}
    }
    
    /*! \internal
     * \brief Show the information for a configuration type
     */
    static void cli_show_module_type(struct ast_cli_args *a)
    {
    	RAII_VAR(struct ast_xml_doc_item *, item, NULL, ao2_cleanup);
    	struct ast_xml_doc_item *tmp;
    	char option_type[64];
    	int match = 0;
    
    	ast_assert(a->argc == 5);
    
    	if (!(item = ao2_find(xmldocs, a->argv[3], OBJ_KEY))) {
    		ast_cli(a->fd, "Unknown module %s\n", a->argv[3]);
    		return;
    	}
    
    	tmp = item;
    
    	while ((tmp = AST_LIST_NEXT(tmp, next))) {
    
    		if (!strcasecmp(tmp->type, "configObject") && !strcasecmp(tmp->name, a->argv[4])) {
    			match = 1;
    			term_color(option_type, tmp->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(option_type));
    			ast_cli(a->fd, "%s", option_type);
    			if (ast_str_strlen(tmp->syntax)) {
    				ast_cli(a->fd, ": [%s]\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->syntax), 1));
    			} else {
    				ast_cli(a->fd, "\n\n");
    			}
    			if (ast_str_strlen(tmp->synopsis)) {
    				ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->synopsis), 1));
    			}
    			if (ast_str_strlen(tmp->description)) {
    				ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->description), 1));
    			}
    		}
    	}
    
    	if (!match) {
    		ast_cli(a->fd, "Unknown configuration type %s\n", a->argv[4]);
    		return;
    	}
    
    	/* Now iterate over the options for the type */
    	tmp = item;
    
    	while ((tmp = AST_LIST_NEXT(tmp, next))) {
    
    		if (!strcasecmp(tmp->type, "configOption") && !strcasecmp(tmp->ref, a->argv[4])) {
    			ast_cli(a->fd, "%-25s -- %-65.65s\n", tmp->name,
    					ast_str_buffer(tmp->synopsis));
    		}
    	}
    }
    
    /*! \internal
     * \brief Show detailed information for an option
     */
    static void cli_show_module_options(struct ast_cli_args *a)
    {
    	RAII_VAR(struct ast_xml_doc_item *, item, NULL, ao2_cleanup);
    	struct ast_xml_doc_item *tmp;
    	char option_name[64];
    	int match = 0;
    
    	ast_assert(a->argc == 6);
    
    	if (!(item = ao2_find(xmldocs, a->argv[3], OBJ_KEY))) {
    		ast_cli(a->fd, "Unknown module %s\n", a->argv[3]);
    		return;
    	}
    	tmp = item;
    
    	while ((tmp = AST_LIST_NEXT(tmp, next))) {
    
    		if (!strcasecmp(tmp->type, "configOption") && !strcasecmp(tmp->ref, a->argv[4]) && !strcasecmp(tmp->name, a->argv[5])) {
    			if (match) {
    				ast_cli(a->fd, "\n");
    			}
    			term_color(option_name, tmp->ref, COLOR_MAGENTA, COLOR_BLACK, sizeof(option_name));
    
    			ast_cli(a->fd, "[%s%s]\n", option_name, ast_term_reset());
    
    			if (ast_str_strlen(tmp->syntax)) {
    				ast_cli(a->fd, "%s\n", ast_xmldoc_printable(ast_str_buffer(tmp->syntax), 1));
    			}
    			ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(AS_OR(tmp->synopsis, "No information available"), 1));
    			if (ast_str_strlen(tmp->description)) {
    				ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->description), 1));
    			}
    
    
    			if (ast_str_strlen(tmp->seealso)) {
    				ast_cli(a->fd, "See Also:\n");
    				ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->seealso), 1));
    			}
    
    
    			match = 1;
    		}
    	}
    
    	if (!match) {
    		ast_cli(a->fd, "No option %s found for %s:%s\n", a->argv[5], a->argv[3], a->argv[4]);
    	}
    }
    
    static char *cli_show_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "config show help";
    		e->usage =
    			"Usage: config show help [<module> [<type> [<option>]]]\n"
    			"   Display detailed information about module configuration.\n"
    			"     * If nothing is specified, the modules that have\n"
    			"       configuration information are listed.\n"
    			"     * If <module> is specified, the configuration types\n"
    			"       for that module will be listed, along with brief\n"
    			"       information about that type.\n"
    			"     * If <module> and <type> are specified, detailed\n"
    			"       information about the type is displayed, as well\n"
    			"       as the available options.\n"
    			"     * If <module>, <type>, and <option> are specified,\n"
    			"       detailed information will be displayed about that\n"
    			"       option.\n"
    			"   NOTE: the help documentation is partially generated at run\n"
    			"     time when a module is loaded. If a module is not loaded,\n"
    			"     configuration help for that module may be incomplete.\n";
    		return NULL;
    	case CLI_GENERATE:
    		switch(a->pos) {
    		case 3: return complete_config_module(a->word, a->pos, a->n);
    		case 4: return complete_config_type(a->argv[3], a->word, a->pos, a->n);
    		case 5: return complete_config_option(a->argv[3], a->argv[4], a->word, a->pos, a->n);
    		default: return NULL;
    		}
    	}
    
    	switch (a->argc) {
    	case 3:
    		cli_show_modules(a);
    		break;
    	case 4:
    		cli_show_module_types(a);
    		break;
    	case 5:
    		cli_show_module_type(a);
    		break;
    	case 6:
    		cli_show_module_options(a);
    		break;
    	default:
    		return CLI_SHOWUSAGE;
    	}
    
    	return CLI_SUCCESS;
    }
    
    static struct ast_cli_entry cli_aco[] = {
    	AST_CLI_DEFINE(cli_show_help, "Show configuration help for a module"),
    };
    
    static void aco_deinit(void)
    {
    	ast_cli_unregister(cli_aco);
    	ao2_cleanup(xmldocs);
    }
    #endif /* AST_XML_DOCS */
    
    int aco_init(void)
    {
    #ifdef AST_XML_DOCS
    
    	ast_register_cleanup(aco_deinit);
    
    	if (!(xmldocs = ast_xmldoc_build_documentation("configInfo"))) {
    		ast_log(LOG_ERROR, "Couldn't build config documentation\n");
    		return -1;
    	}
    	ast_cli_register_multiple(cli_aco, ARRAY_LEN(cli_aco));
    #endif /* AST_XML_DOCS */
    	return 0;
    }
    
    
    /* Default config option handlers */
    
    /*! \brief Default option handler for signed integers
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    
    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;
    }
    
    
    /*! \brief Default option handler for unsigned integers
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    
    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_UINT32 | 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 %u 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 %u 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;
    }
    
    
    /*! \brief Default option handler for timelen signed integers
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    static int timelen_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj)
    {
    	int *field = (int *)(obj + opt->args[0]);
    	unsigned int flags = PARSE_TIMELEN | opt->flags;
    	int res = 0;
    	if (opt->flags & PARSE_IN_RANGE) {
    		if (opt->flags & PARSE_DEFAULT) {
    			res = ast_parse_arg(var->value, flags, field, (enum ast_timelen) opt->args[1], (int) opt->args[2], (int) opt->args[3], opt->args[4]);
    		} else {
    			res = ast_parse_arg(var->value, flags, field, (enum ast_timelen) opt->args[1], (int) opt->args[2], (int) opt->args[3]);
    		}
    		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[2], (int) opt->args[3]);
    				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, (enum ast_timelen) opt->args[1], (int) opt->args[2])) {
    		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, (enum ast_timelen) opt->args[1]);
    	}
    
    	return res;
    }
    
    
    /*! \brief Default option handler for doubles
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    
    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);
    }
    
    
    /*! \brief Default handler for ACLs
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    
    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]);
    	int error = 0;
    
    	*ha = ast_append_ha(opt->flags ? "permit" : "deny", var->value, *ha, &error);
    
    /*! \brief Default option handler for codec preferences/capabilities
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    
    static int codec_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj) {
    
    	struct ast_format_cap **cap = (struct ast_format_cap **)(obj + opt->args[0]);
    	return ast_format_cap_update_by_allow_disallow(*cap, var->value, opt->flags);
    
    /*! \brief Default option handler for stringfields
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    
    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]);
    
    
    	if (opt->flags && ast_strlen_zero(var->value)) {
    		return -1;
    	}
    
    	ast_string_field_ptr_set_by_fields(*pool, *mgr, field, var->value);
    	return 0;
    }
    
    
    /*! \brief Default option handler for bools (ast_true/ast_false)
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    
    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;
    }
    
    
    /*! \brief Default option handler for bools (ast_true/ast_false) that are stored as flags
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    static int boolflag_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj)
    {
    	unsigned int *flags_field = (unsigned int *)(obj + opt->args[0]);
    	unsigned int val = opt->flags ? ast_true(var->value) : ast_false(var->value);
    	unsigned int flag = opt->args[1];
    	if (val) {
    		*flags_field |= flag;
    	} else {
    		*flags_field &= ~flag;
    	}
    	return 0;
    }
    
    
    /*! \brief Default handler for ast_sockaddrs
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    
    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);
    }
    
    /*! \brief Default handler for doing nothing
    
     */
    static int noop_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj)
    {
    	return 0;
    }
    
    /*! \brief Default handler for character arrays
     * \note For a description of the opt->flags and opt->args values, see the documentation for
     * enum aco_option_type in config_options.h
     */
    static int chararray_handler_fn(const struct aco_option *opt, struct ast_variable *var, void *obj)
    {
    	char *field = (char *)(obj + opt->args[0]);
    	size_t len = opt->args[1];
    
    
    	if (opt->flags && ast_strlen_zero(var->value)) {
    		return -1;
    	}
    
    	ast_copy_string(field, var->value, len);
    	return 0;
    }