2012-06-01 16:33:25 +00:00
/*
* 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>
*/
2012-06-15 16:20:16 +00:00
/*** MODULEINFO
<support_level>core</support_level>
***/
2012-06-01 16:33:25 +00:00
#include "asterisk.h"
#include <regex.h>
2013-02-15 13:38:12 +00:00
#include "asterisk/_private.h"
2012-06-01 16:33:25 +00:00
#include "asterisk/config.h"
#include "asterisk/config_options.h"
#include "asterisk/stringfields.h"
#include "asterisk/acl.h"
2017-07-10 15:04:58 -04:00
#include "asterisk/app.h"
2012-06-01 16:33:25 +00:00
#include "asterisk/frame.h"
2013-02-15 13:38:12 +00:00
#include "asterisk/xmldoc.h"
#include "asterisk/cli.h"
#include "asterisk/term.h"
2014-07-20 22:06:33 +00:00
#include "asterisk/format_cap.h"
2012-06-01 16:33:25 +00:00
#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 ;
2012-07-12 21:43:09 +00:00
const char * aliased_to ;
const char * default_val ;
2012-06-01 16:33:25 +00:00
enum aco_matchtype match_type ;
regex_t * name_regex ;
struct aco_type ** obj ;
enum aco_option_type type ;
aco_option_handler handler ;
unsigned int flags ;
2013-08-23 15:21:40 +00:00
unsigned int no_doc : 1 ;
2016-01-25 11:35:21 -04:00
#ifdef AST_DEVMODE
unsigned int doc_unavailable : 1 ;
#endif
2012-07-12 21:43:09 +00:00
unsigned char deprecated : 1 ;
2012-06-01 16:33:25 +00:00
size_t argc ;
intptr_t args [ 0 ];
};
2013-02-15 13:38:12 +00:00
#ifdef AST_XML_DOCS
static struct ao2_container * xmldocs ;
/*! \brief Value of the aco_option_type enum as strings */
static char * aco_option_type_string [] = {
"ACL" , /* OPT_ACL_T, */
"Boolean" , /* OPT_BOOL_T, */
"Boolean" , /* OPT_BOOLFLAG_T, */
"String" , /* OPT_CHAR_ARRAY_T, */
"Codec" , /* OPT_CODEC_T, */
"Custom" , /* OPT_CUSTOM_T, */
"Double" , /* OPT_DOUBLE_T, */
"Integer" , /* OPT_INT_T, */
"None" , /* OPT_NOOP_T, */
"IP Address" , /* OPT_SOCKADDR_T, */
"String" , /* OPT_STRINGFIELD_T, */
"Unsigned Integer" , /* OPT_UINT_T, */
2016-06-20 13:21:52 -05:00
"Boolean" , /* OPT_YESNO_T, */
2018-01-02 01:26:10 -05:00
"Time Length" , /* OPT_TIMELEN_T, */
2013-02-15 13:38:12 +00:00
};
2017-03-30 10:51:14 -04:00
#endif /* AST_XML_DOCS */
2013-02-15 13:38:12 +00:00
2012-06-01 16:33:25 +00:00
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 );
2017-07-10 15:04:58 -04:00
static int timelen_handler_fn ( const struct aco_option * opt , struct ast_variable * var , void * obj );
2012-06-01 16:33:25 +00:00
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 );
2012-06-28 01:12:06 +00:00
static int boolflag_handler_fn ( const struct aco_option * opt , struct ast_variable * var , void * obj );
2012-06-01 16:33:25 +00:00
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 );
2012-07-19 23:21:40 +00:00
static int noop_handler_fn ( const struct aco_option * opt , struct ast_variable * var , void * obj );
static int chararray_handler_fn ( const struct aco_option * opt , struct ast_variable * var , void * obj );
2012-06-01 16:33:25 +00:00
2013-02-15 13:38:12 +00:00
#ifdef AST_XML_DOCS
2017-12-12 13:36:39 -05:00
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 );
2013-02-15 13:38:12 +00:00
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 );
#endif
2012-06-01 16:33:25 +00:00
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 ;
2016-06-20 13:21:52 -05:00
/* Reading from config files, BOOL and YESNO are handled exactly the
* same. Their difference is in how they are rendered to users
*/
case OPT_YESNO_T : return bool_handler_fn ;
2012-06-28 01:12:06 +00:00
case OPT_BOOLFLAG_T : return boolflag_handler_fn ;
2012-07-19 23:21:40 +00:00
case OPT_CHAR_ARRAY_T : return chararray_handler_fn ;
2012-06-01 16:33:25 +00:00
case OPT_CODEC_T : return codec_handler_fn ;
case OPT_DOUBLE_T : return double_handler_fn ;
case OPT_INT_T : return int_handler_fn ;
2012-07-19 23:21:40 +00:00
case OPT_NOOP_T : return noop_handler_fn ;
2012-06-01 16:33:25 +00:00
case OPT_SOCKADDR_T : return sockaddr_handler_fn ;
case OPT_STRINGFIELD_T : return stringfield_handler_fn ;
case OPT_UINT_T : return uint_handler_fn ;
2017-07-10 15:04:58 -04:00
case OPT_TIMELEN_T : return timelen_handler_fn ;
2012-06-01 16:33:25 +00:00
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 ;
}
2013-02-15 13:38:12 +00:00
static int link_option_to_types ( struct aco_info * info , struct aco_type ** types , struct aco_option * opt )
2012-07-12 21:43:09 +00:00
{
size_t idx = 0 ;
struct aco_type * type ;
while (( type = types [ idx ++ ])) {
2012-09-04 15:35:02 +00:00
if ( ! type -> internal ) {
ast_log ( LOG_ERROR , "Attempting to register option using uninitialized type \n " );
return - 1 ;
}
2016-01-25 11:35:21 -04:00
if ( ! ao2_link ( type -> internal -> opts , opt )) {
2013-06-25 01:12:58 +00:00
do {
2013-08-23 18:02:36 +00:00
ao2_unlink ( types [ idx - 1 ] -> internal -> opts , opt );
2013-06-25 01:12:58 +00:00
} while ( -- idx );
2012-07-12 21:43:09 +00:00
return - 1 ;
}
2016-01-25 11:35:21 -04:00
#ifdef AST_XML_DOCS
if ( ! info -> hidden && ! opt -> no_doc &&
xmldoc_update_config_option ( types , info -> module , opt -> name , type -> name , opt -> default_val , opt -> match_type == ACO_REGEX , opt -> type )) {
#ifdef AST_DEVMODE
opt -> doc_unavailable = 1 ;
#endif
}
2016-05-04 09:17:26 +01:00
#endif
2012-07-12 21:43:09 +00:00
}
2013-06-25 01:12:58 +00:00
/* The container(s) should hold the only ref to opt */
ao2_ref ( opt , - 1 );
2012-07-12 21:43:09 +00:00
return 0 ;
}
int aco_option_register_deprecated ( struct aco_info * info , const char * name , struct aco_type ** types , const char * aliased_to )
{
struct aco_option * opt ;
if ( ! info || ast_strlen_zero ( name ) || ast_strlen_zero ( aliased_to )) {
return - 1 ;
}
2018-10-01 23:12:14 -04:00
opt = ao2_alloc_options ( sizeof ( * opt ), config_option_destroy , AO2_ALLOC_OPT_LOCK_NOLOCK );
if ( ! opt ) {
2012-07-12 21:43:09 +00:00
return - 1 ;
}
opt -> name = name ;
opt -> aliased_to = aliased_to ;
opt -> deprecated = 1 ;
opt -> match_type = ACO_EXACT ;
2013-02-15 13:38:12 +00:00
if ( link_option_to_types ( info , types , opt )) {
2012-07-12 21:43:09 +00:00
ao2_ref ( opt , - 1 );
return - 1 ;
}
return 0 ;
}
2013-05-21 18:00:22 +00:00
unsigned int aco_option_get_flags ( const struct aco_option * option )
{
return option -> flags ;
}
2013-08-23 21:49:47 +00:00
intptr_t aco_option_get_argument ( const struct aco_option * option , unsigned int position )
{
return option -> args [ position ];
}
2013-02-15 13:38:12 +00:00
#ifdef AST_XML_DOCS
/*! \internal
* \brief Find a particular ast_xml_doc_item from it's parent config_info, types, and name
*/
static struct ast_xml_doc_item * find_xmldoc_option ( struct ast_xml_doc_item * config_info , struct aco_type ** types , const char * name )
{
struct ast_xml_doc_item * iter = config_info ;
if ( ! iter ) {
return NULL ;
}
/* First is just the configInfo, we can skip it */
2014-07-24 13:00:59 +00:00
while (( iter = AST_LIST_NEXT ( iter , next ))) {
2013-02-15 13:38:12 +00:00
size_t x ;
if ( strcasecmp ( iter -> name , name )) {
continue ;
}
for ( x = 0 ; types [ x ]; x ++ ) {
/* All we care about is that at least one type has the option */
if ( ! strcasecmp ( types [ x ] -> name , iter -> ref )) {
return iter ;
}
}
}
return NULL ;
}
/*! \internal
* \brief Find a particular ast_xml_doc_item from it's parent config_info and name
*/
static struct ast_xml_doc_item * find_xmldoc_type ( struct ast_xml_doc_item * config_info , const char * name )
{
struct ast_xml_doc_item * iter = config_info ;
if ( ! iter ) {
return NULL ;
}
/* First is just the config Info, skip it */
2014-07-24 13:00:59 +00:00
while (( iter = AST_LIST_NEXT ( iter , next ))) {
2013-02-15 13:38:12 +00:00
if ( ! strcasecmp ( iter -> type , "configObject" ) && ! strcasecmp ( iter -> name , name )) {
break ;
}
}
return iter ;
}
#endif /* AST_XML_DOCS */
2012-06-01 16:33:25 +00:00
int __aco_option_register ( struct aco_info * info , const char * name , enum aco_matchtype matchtype , struct aco_type ** types ,
2013-08-23 15:21:40 +00:00
const char * default_val , enum aco_option_type kind , aco_option_handler handler , unsigned int flags ,
unsigned int no_doc , size_t argc , ...)
2012-06-01 16:33:25 +00:00
{
struct aco_option * opt ;
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 ;
}
2018-10-01 23:12:14 -04:00
opt = ao2_alloc_options ( sizeof ( * opt ) + argc * sizeof ( opt -> args [ 0 ]),
config_option_destroy , AO2_ALLOC_OPT_LOCK_NOLOCK );
if ( ! opt ) {
2012-06-01 16:33:25 +00:00
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 ;
2013-08-23 15:21:40 +00:00
opt -> no_doc = no_doc ;
2012-06-01 16:33:25 +00:00
if ( ! opt -> handler && ! ( opt -> handler = ast_config_option_default_handler ( opt -> type ))) {
/* This should never happen */
2014-05-09 22:49:26 +00:00
ast_log ( LOG_ERROR , "No handler provided, and no default handler exists for type %u \n " , opt -> type );
2012-06-01 16:33:25 +00:00
ao2_ref ( opt , - 1 );
return - 1 ;
};
2013-02-15 13:38:12 +00:00
if ( link_option_to_types ( info , types , opt )) {
2012-07-12 21:43:09 +00:00
ao2_ref ( opt , - 1 );
return - 1 ;
2012-06-01 16:33:25 +00:00
}
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 ;
2017-12-12 13:36:39 -05:00
case ACO_PREFIX :
return strncasecmp ( name , match -> name , strlen ( match -> name )) ? 0 : CMP_MATCH | CMP_STOP ;
2012-06-01 16:33:25 +00:00
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 ;
2012-09-04 15:35:02 +00:00
if ( ! type || ! type -> internal || ! type -> internal -> opts ) {
ast_log ( LOG_NOTICE , "Attempting to use NULL or unitialized config type \n " );
return NULL ;
}
2012-06-01 16:33:25 +00:00
/* 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 )
{
2018-11-19 15:10:02 -05:00
return ao2_container_alloc_hash ( AO2_ALLOC_OPT_LOCK_MUTEX , 0 , CONFIG_OPT_BUCKETS ,
config_opt_hash , NULL , config_opt_cmp );
2012-06-01 16:33:25 +00:00
}
2017-12-12 13:36:39 -05:00
static int internal_aco_type_category_check ( struct aco_type * match , const char * category )
{
const char ** categories = ( const char ** ) match -> category ;
switch ( match -> category_match ) {
case ACO_WHITELIST :
return regexec ( match -> internal -> regex , category , 0 , NULL , 0 );
case ACO_BLACKLIST :
return ! regexec ( match -> internal -> regex , category , 0 , NULL , 0 );
case ACO_WHITELIST_EXACT :
return strcasecmp ( match -> category , category );
case ACO_BLACKLIST_EXACT :
return ! strcasecmp ( match -> category , category );
case ACO_WHITELIST_ARRAY :
while ( * categories ) {
if ( ! strcasecmp ( * categories , category )) {
return 0 ;
}
categories ++ ;
}
return - 1 ;
case ACO_BLACKLIST_ARRAY :
while ( * categories ) {
if ( ! strcasecmp ( * categories , category )) {
return - 1 ;
}
categories ++ ;
}
return 0 ;
}
return - 1 ;
}
2012-06-01 16:33:25 +00:00
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 */
2017-12-12 13:36:39 -05:00
if ( internal_aco_type_category_check ( match , category )) {
2012-06-01 16:33:25 +00:00
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 ;
2013-06-13 13:15:56 +00:00
regex_t * regex_skip ;
2012-06-01 16:33:25 +00:00
/* Skip preloaded categories if we aren't preloading */
if ( ! preload && is_preload ( file , cat )) {
return 0 ;
}
2013-06-13 13:15:56 +00:00
/* Skip the category if we've been told to ignore it */
if ( ! ast_strlen_zero ( file -> skip_category )) {
regex_skip = build_regex ( file -> skip_category );
if ( ! regexec ( regex_skip , cat , 0 , NULL , 0 )) {
2013-08-29 21:37:29 +00:00
regfree ( regex_skip );
2013-06-13 13:15:56 +00:00
ast_free ( regex_skip );
return 0 ;
}
2013-08-29 21:37:29 +00:00
regfree ( regex_skip );
2013-06-13 13:15:56 +00:00
ast_free ( regex_skip );
}
2012-06-01 16:33:25 +00:00
/* 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 ;
}
2017-12-12 13:36:39 -05:00
if ( type -> type == ACO_IGNORE ) {
return 0 ;
}
2012-06-01 16:33:25 +00:00
field = info -> internal -> pending + type -> item_offset ;
if ( !* field ) {
2013-08-30 18:30:01 +00:00
ast_log ( LOG_ERROR , "In %s: %s - No object to update! \n " , file -> filename , cat );
2012-06-01 16:33:25 +00:00
return - 1 ;
}
if ( type -> type == ACO_GLOBAL && * field ) {
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 ;
}
2012-06-07 20:32:07 +00:00
static enum aco_process_status internal_process_ast_config ( struct aco_info * info , struct aco_file * file , struct ast_config * cfg )
2012-06-01 16:33:25 +00:00
{
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 )) {
2012-06-07 20:32:07 +00:00
return ACO_PROCESS_ERROR ;
2012-06-01 16:33:25 +00:00
}
}
}
while (( cat = ast_category_browse ( cfg , cat ))) {
if ( process_category ( cfg , info , file , cat , 0 )) {
2012-06-07 20:32:07 +00:00
return ACO_PROCESS_ERROR ;
2012-06-01 16:33:25 +00:00
}
}
2012-06-07 20:32:07 +00:00
return ACO_PROCESS_OK ;
2012-06-01 16:33:25 +00:00
}
2012-06-07 20:32:07 +00:00
enum aco_process_status aco_process_ast_config ( struct aco_info * info , struct aco_file * file , struct ast_config * cfg )
2012-06-01 16:33:25 +00:00
{
2012-09-04 15:35:02 +00:00
if ( ! info -> internal ) {
ast_log ( LOG_ERROR , "Attempt to process %s with uninitialized aco_info \n " , file -> filename );
2018-01-04 10:27:19 -05:00
return ACO_PROCESS_ERROR ;
2012-09-04 15:35:02 +00:00
}
2012-06-01 16:33:25 +00:00
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 );
2017-05-04 15:04:46 -06:00
info -> internal -> pending = NULL ;
2012-06-07 20:32:07 +00:00
return ACO_PROCESS_OK ;
2012-06-01 16:33:25 +00:00
error :
ao2_cleanup ( info -> internal -> pending );
2017-05-04 15:04:46 -06:00
info -> internal -> pending = NULL ;
2012-06-07 20:32:07 +00:00
return ACO_PROCESS_ERROR ;
2012-06-01 16:33:25 +00:00
}
2012-06-07 20:32:07 +00:00
enum aco_process_status aco_process_config ( struct aco_info * info , int reload )
2012-06-01 16:33:25 +00:00
{
struct ast_config * cfg ;
struct ast_flags cfg_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 , };
2013-09-18 17:15:53 +00:00
int res = ACO_PROCESS_OK ;
int file_count = 0 ;
2012-06-01 16:33:25 +00:00
struct aco_file * file ;
2013-09-18 17:15:53 +00:00
if ( ! info -> internal ) {
ast_log ( LOG_ERROR , "Attempting to process uninitialized aco_info \n " );
2012-06-07 20:32:07 +00:00
return ACO_PROCESS_ERROR ;
2012-06-01 16:33:25 +00:00
}
2013-09-18 17:15:53 +00:00
if ( ! ( info -> files [ 0 ])) {
ast_log ( LOG_ERROR , "No filename given, cannot proceed! \n " );
2012-09-04 15:35:02 +00:00
return ACO_PROCESS_ERROR ;
}
2012-06-01 16:33:25 +00:00
if ( ! ( info -> internal -> pending = info -> snapshot_alloc ())) {
ast_log ( LOG_ERROR , "In %s: Could not allocate temporary objects \n " , info -> module );
2012-06-07 20:32:07 +00:00
return ACO_PROCESS_ERROR ;
2012-06-01 16:33:25 +00:00
}
2013-09-18 17:15:53 +00:00
while ( res != ACO_PROCESS_ERROR && ( file = info -> files [ file_count ++ ])) {
2012-06-14 13:35:07 +00:00
const char * filename = file -> filename ;
2013-09-20 22:44:11 +00:00
struct aco_type * match ;
int i ;
/* set defaults for global objects */
for ( i = 0 , match = file -> types [ i ]; match ; match = file -> types [ ++ i ]) {
void ** field = info -> internal -> pending + match -> item_offset ;
2017-12-12 13:36:39 -05:00
if ( match -> type == ACO_IGNORE ) {
continue ;
}
2013-09-20 22:44:11 +00:00
if ( match -> type != ACO_GLOBAL || !* field ) {
continue ;
}
if ( aco_set_defaults ( match , match -> category , * field )) {
ast_log ( LOG_ERROR , "In %s: Setting defaults for %s failed \n " , file -> filename , match -> category );
res = ACO_PROCESS_ERROR ;
break ;
}
}
if ( res == ACO_PROCESS_ERROR ) {
break ;
}
2013-09-18 17:15:53 +00:00
2012-06-14 13:35:07 +00:00
try_alias :
2013-09-18 17:15:53 +00:00
cfg = ast_config_load ( filename , cfg_flags );
if ( ! cfg || cfg == CONFIG_STATUS_FILEMISSING ) {
2012-06-14 13:35:07 +00:00
if ( file -> alias && strcmp ( file -> alias , filename )) {
filename = file -> alias ;
goto try_alias ;
}
2012-06-01 16:33:25 +00:00
ast_log ( LOG_ERROR , "Unable to load config file '%s' \n " , file -> filename );
2012-06-07 20:32:07 +00:00
res = ACO_PROCESS_ERROR ;
2012-06-01 16:33:25 +00:00
break ;
} else if ( cfg == CONFIG_STATUS_FILEUNCHANGED ) {
ast_debug ( 1 , "%s was unchanged \n " , file -> filename );
2012-06-07 20:32:07 +00:00
res = ACO_PROCESS_UNCHANGED ;
2012-06-01 16:33:25 +00:00
continue ;
} else if ( cfg == CONFIG_STATUS_FILEINVALID ) {
2013-09-18 17:15:53 +00:00
ast_log ( LOG_ERROR , "Contents of %s are invalid and cannot be parsed \n " ,
file -> filename );
2012-06-07 20:32:07 +00:00
res = ACO_PROCESS_ERROR ;
2012-06-01 16:33:25 +00:00
break ;
}
2013-09-18 17:15:53 +00:00
/* A file got loaded. */
if ( reload ) {
/* Must do any subsequent file loads unconditionally. */
reload = 0 ;
ast_clear_flag ( & cfg_flags , CONFIG_FLAG_FILEUNCHANGED );
if ( file_count != 1 ) {
/*
* Must restart loading to load all config files since a file
* after the first one changed.
*/
file_count = 0 ;
} else {
res = internal_process_ast_config ( info , file , cfg );
}
} else {
res = internal_process_ast_config ( info , file , cfg );
}
2012-06-01 16:33:25 +00:00
ast_config_destroy ( cfg );
}
2012-06-07 20:32:07 +00:00
if ( res != ACO_PROCESS_OK ) {
2013-02-15 13:38:12 +00:00
goto end ;
2012-06-07 20:32:07 +00:00
}
2013-09-18 17:15:53 +00:00
if ( info -> pre_apply_config && info -> pre_apply_config ()) {
2012-06-07 20:32:07 +00:00
res = ACO_PROCESS_ERROR ;
goto end ;
}
if ( apply_config ( info )) {
res = ACO_PROCESS_ERROR ;
goto end ;
}
2012-06-01 16:33:25 +00:00
2012-06-14 13:41:47 +00:00
if ( info -> post_apply_config ) {
info -> post_apply_config ();
}
2012-06-07 20:32:07 +00:00
end :
2012-06-01 16:33:25 +00:00
ao2_cleanup ( info -> internal -> pending );
2017-05-04 15:04:46 -06:00
info -> internal -> pending = NULL ;
2012-06-01 16:33:25 +00:00
return res ;
}
2012-07-19 23:21:40 +00:00
int aco_process_var ( struct aco_type * type , const char * cat , struct ast_variable * var , void * obj )
{
RAII_VAR ( struct aco_option * , opt , aco_option_find ( type , var -> name ), ao2_cleanup );
if ( opt && opt -> deprecated && ! ast_strlen_zero ( opt -> aliased_to )) {
const char * alias = ast_strdupa ( opt -> aliased_to );
ast_log ( LOG_WARNING , "At line %d of %s option '%s' is deprecated. Use '%s' instead \n " , var -> lineno , var -> file , var -> name , alias );
ao2_ref ( opt , - 1 );
opt = aco_option_find ( type , alias );
}
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 ;
}
2016-01-25 11:35:21 -04:00
#ifdef AST_DEVMODE
if ( opt -> doc_unavailable ) {
ast_log ( LOG_ERROR , "Config option '%s' of type '%s' is not completely documented and can not be set \n " , var -> name , type -> name );
return - 1 ;
}
#endif
2012-07-19 23:21:40 +00:00
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 ;
}
2012-06-01 16:33:25 +00:00
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 ) {
2012-07-19 23:21:40 +00:00
if ( aco_process_var ( type , cat , var , obj )) {
2012-06-01 16:33:25 +00:00
return - 1 ;
}
}
return 0 ;
}
static void internal_type_destroy ( struct aco_type * type )
{
2012-09-04 15:35:02 +00:00
/* If we've already had all our internal data cleared out,
* then there's no need to proceed further
*/
if ( ! type -> internal ) {
return ;
}
2012-06-01 16:33:25 +00:00
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 ;
}
2017-12-12 13:36:39 -05:00
switch ( type -> category_match ) {
case ACO_BLACKLIST :
case ACO_WHITELIST :
if ( ! ( type -> internal -> regex = build_regex ( type -> category ))) {
internal_type_destroy ( type );
return - 1 ;
}
break ;
case ACO_BLACKLIST_EXACT :
case ACO_WHITELIST_EXACT :
case ACO_BLACKLIST_ARRAY :
case ACO_WHITELIST_ARRAY :
break ;
2012-06-01 16:33:25 +00:00
}
if ( ! ( type -> internal -> opts = aco_option_container_alloc ())) {
internal_type_destroy ( type );
2012-07-21 13:25:26 +00:00
return - 1 ;
2012-06-01 16:33:25 +00:00
}
return 0 ;
}
int aco_info_init ( struct aco_info * info )
{
2013-02-15 13:38:12 +00:00
size_t x = 0 , y = 0 ;
struct aco_file * file ;
struct aco_type * type ;
2012-06-01 16:33:25 +00:00
if ( ! ( info -> internal = ast_calloc ( 1 , sizeof ( * info -> internal )))) {
return - 1 ;
}
2013-02-15 13:38:12 +00:00
while (( file = info -> files [ x ++ ])) {
while (( type = file -> types [ y ++ ])) {
if ( internal_type_init ( type )) {
goto error ;
}
#ifdef AST_XML_DOCS
2013-08-25 18:00:46 +00:00
if ( ! info -> hidden &&
! type -> hidden &&
2017-12-12 13:36:39 -05:00
type -> type != ACO_IGNORE &&
xmldoc_update_config_type ( info -> module , type -> name , type -> category , type -> matchfield , type -> matchvalue , type -> category_match )) {
2012-06-01 16:33:25 +00:00
goto error ;
}
2013-02-15 13:38:12 +00:00
#endif /* AST_XML_DOCS */
2012-06-01 16:33:25 +00:00
}
2013-02-15 13:38:12 +00:00
y = 0 ;
2012-06-01 16:33:25 +00:00
}
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 ;
2015-09-03 21:15:13 -05:00
if ( ! type -> internal ) {
return - 1 ;
}
2012-06-01 16:33:25 +00:00
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 ;
}
2013-02-15 13:38:12 +00:00
#ifdef AST_XML_DOCS
/*! \internal
* \brief Complete the name of the module the user is looking for
*/
2018-03-17 01:51:47 -04:00
static char * complete_config_module ( const char * word )
2013-02-15 13:38:12 +00:00
{
size_t wordlen = strlen ( word );
struct ao2_iterator i ;
struct ast_xml_doc_item * cur ;
i = ao2_iterator_init ( xmldocs , 0 );
while (( cur = ao2_iterator_next ( & i ))) {
2018-03-17 01:51:47 -04:00
if ( ! strncasecmp ( word , cur -> name , wordlen )) {
if ( ast_cli_completion_add ( ast_strdup ( cur -> name ))) {
ao2_ref ( cur , - 1 );
break ;
}
2013-02-15 13:38:12 +00:00
}
ao2_ref ( cur , - 1 );
}
ao2_iterator_destroy ( & i );
2018-03-17 01:51:47 -04:00
return NULL ;
2013-02-15 13:38:12 +00:00
}
/*! \internal
* \brief Complete the name of the configuration type the user is looking for
*/
2018-03-17 01:51:47 -04:00
static char * complete_config_type ( const char * module , const char * word )
2013-02-15 13:38:12 +00:00
{
size_t wordlen = strlen ( word );
2018-03-17 01:51:47 -04:00
struct ast_xml_doc_item * info ;
2013-02-15 13:38:12 +00:00
struct ast_xml_doc_item * cur ;
2018-03-17 01:51:47 -04:00
info = ao2_find ( xmldocs , module , OBJ_KEY );
if ( ! info ) {
2013-02-15 13:38:12 +00:00
return NULL ;
}
cur = info ;
2014-07-24 13:00:59 +00:00
while (( cur = AST_LIST_NEXT ( cur , next ))) {
2018-03-17 01:51:47 -04:00
if ( ! strcasecmp ( cur -> type , "configObject" ) && ! strncasecmp ( word , cur -> name , wordlen )) {
if ( ast_cli_completion_add ( ast_strdup ( cur -> name ))) {
break ;
}
2013-02-15 13:38:12 +00:00
}
}
2018-03-17 01:51:47 -04:00
ao2_ref ( info , - 1 );
return NULL ;
2013-02-15 13:38:12 +00:00
}
/*! \internal
* \brief Complete the name of the configuration option the user is looking for
*/
2018-03-17 01:51:47 -04:00
static char * complete_config_option ( const char * module , const char * option , const char * word )
2013-02-15 13:38:12 +00:00
{
size_t wordlen = strlen ( word );
2018-03-17 01:51:47 -04:00
struct ast_xml_doc_item * info ;
2013-02-15 13:38:12 +00:00
struct ast_xml_doc_item * cur ;
2018-03-17 01:51:47 -04:00
info = ao2_find ( xmldocs , module , OBJ_KEY );
if ( ! info ) {
2013-02-15 13:38:12 +00:00
return NULL ;
}
cur = info ;
2014-07-24 13:00:59 +00:00
while (( cur = AST_LIST_NEXT ( cur , next ))) {
2018-03-17 01:51:47 -04:00
if ( ! strcasecmp ( cur -> type , "configOption" ) && ! strcasecmp ( cur -> ref , option ) && ! strncasecmp ( word , cur -> name , wordlen )) {
if ( ast_cli_completion_add ( ast_strdup ( cur -> name ))) {
break ;
}
2013-02-15 13:38:12 +00:00
}
}
2018-03-17 01:51:47 -04:00
ao2_ref ( info , - 1 );
return NULL ;
2013-02-15 13:38:12 +00:00
}
/* Define as 0 if we want to allow configurations to be registered without
* documentation
*/
2013-08-23 15:21:40 +00:00
#define XMLDOC_STRICT 1
2013-02-15 13:38:12 +00:00
/*! \internal
* \brief Update the XML documentation for a config type based on its registration
*/
2017-12-12 13:36:39 -05:00
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 )
2013-02-15 13:38:12 +00:00
{
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. */
2017-12-01 09:29:43 -05:00
if (( results = ast_xmldoc_query ( "/docs/configInfo[@name='%s']/configFile/configObject[@name='%s']/syntax" , module , name ))) {
2013-02-15 13:38:12 +00:00
return 0 ;
}
2017-12-01 09:29:43 -05:00
if ( ! ( results = ast_xmldoc_query ( "/docs/configInfo[@name='%s']/configFile/configObject[@name='%s']" , module , name ))) {
2013-02-15 13:38:12 +00:00
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 );
2017-12-12 13:36:39 -05:00
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 ;
}
2013-02-15 13:38:12 +00:00
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 ;
}
2017-12-01 09:29:43 -05:00
if ( ! ( results = ast_xmldoc_query ( "/docs/configInfo[@name='%s']/configFile/configObject[@name='%s']/configOption[@name='%s']" , module , object_name , name ))) {
2013-02-15 13:38:12 +00:00
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 ))) {
2016-01-28 12:44:43 -06:00
ast_log ( LOG_WARNING , "Could not obtain results for option '%s' with type '%s' in module '%s' \n " , name , object_name , module );
2013-02-15 13:38:12 +00:00
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 );
2014-07-24 13:00:59 +00:00
while (( tmp = AST_LIST_NEXT ( tmp , next ))) {
2013-02-15 13:38:12 +00:00
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 ;
2014-07-24 13:00:59 +00:00
while (( tmp = AST_LIST_NEXT ( tmp , next ))) {
2013-02-15 13:38:12 +00:00
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 ;
2014-07-24 13:00:59 +00:00
while (( tmp = AST_LIST_NEXT ( tmp , next ))) {
2013-02-15 13:38:12 +00:00
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 ;
2014-07-24 13:00:59 +00:00
while (( tmp = AST_LIST_NEXT ( tmp , next ))) {
2013-02-15 13:38:12 +00:00
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 ));
2014-05-28 22:54:12 +00:00
ast_cli ( a -> fd , "[%s%s] \n " , option_name , ast_term_reset ());
2013-02-15 13:38:12 +00:00
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 ));
}
2014-03-07 21:54:01 +00:00
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 ));
}
2013-02-15 13:38:12 +00:00
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 ) {
2018-03-17 01:51:47 -04:00
case 3 :
return complete_config_module ( a -> word );
case 4 :
return complete_config_type ( a -> argv [ 3 ], a -> word );
case 5 :
return complete_config_option ( a -> argv [ 3 ], a -> argv [ 4 ], a -> word );
default :
return NULL ;
2013-02-15 13:38:12 +00:00
}
}
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
2015-03-26 22:24:26 +00:00
ast_register_cleanup ( aco_deinit );
2013-02-15 13:38:12 +00:00
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 ;
}
2012-06-07 15:43:37 +00:00
/* 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
*/
2012-06-01 16:33:25 +00:00
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 ) {
2013-02-15 13:38:12 +00:00
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 ]);
2012-06-01 16:33:25 +00:00
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 ;
}
2012-06-07 15:43:37 +00:00
/*! \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
*/
2012-06-01 16:33:25 +00:00
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 ]);
2016-03-19 13:34:26 +01:00
unsigned int flags = PARSE_UINT32 | opt -> flags ;
2012-06-01 16:33:25 +00:00
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 ) {
2014-05-09 22:49:26 +00:00
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 ]);
2012-06-01 16:33:25 +00:00
res = 0 ;
} else if ( opt -> flags & PARSE_DEFAULT ) {
2014-05-09 22:49:26 +00:00
ast_log ( LOG_WARNING , "Failed to set %s=%s, Set to default value %u instead. \n " , var -> name , var -> value , * field );
2012-06-01 16:33:25 +00:00
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 ;
}
2017-07-10 15:04:58 -04:00
/*! \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 ;
}
2012-06-07 15:43:37 +00:00
/*! \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
*/
2012-06-01 16:33:25 +00:00
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 );
}
2012-06-07 15:43:37 +00:00
/*! \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
*/
2012-06-01 16:33:25 +00:00
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 ;
2012-06-07 15:43:37 +00:00
* ha = ast_append_ha ( opt -> flags ? "permit" : "deny" , var -> value , * ha , & error );
2012-06-01 16:33:25 +00:00
return error ;
}
2012-06-07 15:43:37 +00:00
/*! \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
*/
2012-06-01 16:33:25 +00:00
static int codec_handler_fn ( const struct aco_option * opt , struct ast_variable * var , void * obj ) {
2014-07-20 22:06:33 +00:00
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 );
2012-06-01 16:33:25 +00:00
}
2012-06-07 15:43:37 +00:00
/*! \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
*/
2012-06-01 16:33:25 +00:00
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 ]);
2013-06-27 02:55:16 +00:00
if ( opt -> flags && ast_strlen_zero ( var -> value )) {
return - 1 ;
}
2012-06-01 16:33:25 +00:00
ast_string_field_ptr_set_by_fields ( * pool , * mgr , field , var -> value );
return 0 ;
}
2012-06-07 15:43:37 +00:00
/*! \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
*/
2012-06-01 16:33:25 +00:00
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 ;
}
2012-06-28 01:12:06 +00:00
/*! \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 ;
}
2012-06-07 15:43:37 +00:00
/*! \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
*/
2012-06-01 16:33:25 +00:00
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 );
}
2012-07-19 23:21:40 +00:00
2013-06-27 02:55:16 +00:00
/*! \brief Default handler for doing nothing
2012-07-19 23:21:40 +00:00
*/
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 ];
2013-06-27 02:55:16 +00:00
if ( opt -> flags && ast_strlen_zero ( var -> value )) {
return - 1 ;
}
2012-07-19 23:21:40 +00:00
ast_copy_string ( field , var -> value , len );
return 0 ;
}