@ -127,6 +127,21 @@ struct ast_module {
int usecount ;
/*! List of users holding the module. */
struct module_user_list users ;
/*! List of required module names. */
struct ast_vector_string requires ;
/*! List of optional api modules. */
struct ast_vector_string optional_modules ;
/*! List of modules this enhances. */
struct ast_vector_string enhances ;
/*!
* \ brief Vector holding pointers to modules we have a reference to .
*
* When one module requires another , the required module gets added
* to this list with a reference .
*/
struct module_vector reffed_deps ;
struct {
/*! The module running and ready to accept requests. */
unsigned int running : 1 ;
@ -163,6 +178,225 @@ static int module_vector_cmp(struct ast_module *a, struct ast_module *b)
return a_pri - b_pri ;
}
static struct ast_module * find_resource ( const char * resource , int do_lock ) ;
/*!
* \ internal
* \ brief Add a reference from mod to dep .
*
* \ param mod Owner of the new reference .
* \ param dep Module to reference
* \ param missing Vector to store name of \ a dep if it is not running .
*
* This function returns failure if \ a dep is not running and \ a missing
* is NULL . If \ a missing is not NULL errors will only be returned for
* allocation failures .
*
* \ retval 0 Success
* \ retval - 1 Failure
*
* \ note Adding a second reference to the same dep will return success
* without doing anything .
*/
static int module_reffed_deps_add ( struct ast_module * mod , struct ast_module * dep ,
struct ast_vector_const_string * missing )
{
if ( ! dep - > flags . running ) {
return ! missing ? - 1 : AST_VECTOR_APPEND ( missing , dep - > info - > name ) ;
}
if ( AST_VECTOR_GET_CMP ( & mod - > reffed_deps , dep , AST_VECTOR_ELEM_DEFAULT_CMP ) ) {
/* Skip duplicate. */
return 0 ;
}
if ( AST_VECTOR_APPEND ( & mod - > reffed_deps , dep ) ) {
return - 1 ;
}
ast_module_ref ( dep ) ;
return 0 ;
}
/*!
* \ internal
* \ brief Add references for modules that enhance a dependency .
*
* \ param mod Owner of the new references .
* \ param dep Module to check for enhancers .
* \ param missing Vector to store name of any enhancer that is not running or declined .
*
* \ retval 0 Success
* \ retval - 1 Failure
*/
static int module_reffed_deps_add_dep_enhancers ( struct ast_module * mod ,
struct ast_module * dep , struct ast_vector_const_string * missing )
{
struct ast_module * cur ;
AST_DLLIST_TRAVERSE ( & module_list , cur , entry ) {
if ( cur - > flags . declined ) {
continue ;
}
if ( ! AST_VECTOR_GET_CMP ( & cur - > enhances , dep - > info - > name , ! strcasecmp ) ) {
/* dep is not enhanced by cur. */
continue ;
}
/* dep is enhanced by cur, therefore mod requires cur. */
if ( module_reffed_deps_add ( mod , cur , missing ) ) {
return - 1 ;
}
}
return 0 ;
}
/*!
* \ internal
* \ brief Add references to a list of dependencies .
*
* \ param mod Owner of the new references .
* \ param vec List of required modules to process
* \ param missing Vector to store names of modules that are not running .
* \ param ref_enhancers Reference all enhancers of each required module .
* \ param isoptional Modules that are not loaded can be ignored .
*
* \ retval 0 Success
* \ retval - 1 Failure
*/
static int module_deps_process_reqlist ( struct ast_module * mod ,
struct ast_vector_string * vec , struct ast_vector_const_string * missing ,
int ref_enhancers , int isoptional )
{
int idx ;
for ( idx = 0 ; idx < AST_VECTOR_SIZE ( vec ) ; idx + + ) {
const char * depname = AST_VECTOR_GET ( vec , idx ) ;
struct ast_module * dep = find_resource ( depname , 0 ) ;
if ( ! dep | | ! dep - > flags . running ) {
if ( isoptional & & ! dep ) {
continue ;
}
if ( missing & & ! AST_VECTOR_APPEND ( missing , depname ) ) {
continue ;
}
return - 1 ;
}
if ( module_reffed_deps_add ( mod , dep , missing ) ) {
return - 1 ;
}
if ( ref_enhancers & & module_reffed_deps_add_dep_enhancers ( mod , dep , missing ) ) {
return - 1 ;
}
}
return 0 ;
}
/*!
* \ internal
* \ brief Grab all references required to start the module .
*
* \ param mod The module we ' re trying to start .
* \ param missing Vector to store a list of missing dependencies .
*
* \ retval 0 Success
* \ retval - 1 Failure
*
* \ note module_list must be locked .
*
* \ note Caller is responsible for initializing and freeing the vector .
* Elements are safely read only while module_list remains locked .
*/
static int module_deps_reference ( struct ast_module * mod , struct ast_vector_const_string * missing )
{
int res = 0 ;
/* Grab references to modules we enhance but not other enhancements. */
res | = module_deps_process_reqlist ( mod , & mod - > enhances , missing , 0 , 0 ) ;
/* Grab references to modules we require plus enhancements. */
res | = module_deps_process_reqlist ( mod , & mod - > requires , missing , 1 , 0 ) ;
/* Grab references to optional modules including enhancements. */
res | = module_deps_process_reqlist ( mod , & mod - > optional_modules , missing , 1 , 1 ) ;
return res ;
}
/*!
* \ brief Recursively find required dependencies that are not running .
*
* \ param mod Module to scan for dependencies .
* \ param missingdeps Vector listing modules that must be started first .
*
* \ retval 0 All dependencies resolved .
* \ retval - 1 Failed to resolve some dependencies .
*
* An error from this function usually means a required module is not even
* loaded . This function is safe from infinite recursion , but dependency
* loops are not reported as an error from here . On success missingdeps
* will contain a list of every module that needs to be running before this
* module can start . missingdeps is sorted by load priority so any missing
* dependencies can be started if needed .
*/
static int module_deps_missing_recursive ( struct ast_module * mod , struct module_vector * missingdeps )
{
int i = 0 ;
int res = - 1 ;
struct ast_vector_const_string localdeps ;
struct ast_module * dep ;
/*
* localdeps stores a copy of all dependencies that mod could not reference .
* First we discard modules that we ' ve already found . We add all newly found
* modules to the missingdeps vector then scan them recursively . This will
* ensure we quickly run out of stuff to do .
*/
AST_VECTOR_INIT ( & localdeps , 0 ) ;
if ( module_deps_reference ( mod , & localdeps ) ) {
goto clean_return ;
}
while ( i < AST_VECTOR_SIZE ( & localdeps ) ) {
dep = find_resource ( AST_VECTOR_GET ( & localdeps , i ) , 0 ) ;
if ( ! dep ) {
goto clean_return ;
}
if ( AST_VECTOR_GET_CMP ( missingdeps , dep , AST_VECTOR_ELEM_DEFAULT_CMP ) ) {
/* Skip common dependency. We have already searched it. */
AST_VECTOR_REMOVE ( & localdeps , i , 0 ) ;
} else {
/* missingdeps is the real list so keep it sorted. */
if ( AST_VECTOR_ADD_SORTED ( missingdeps , dep , module_vector_cmp ) ) {
goto clean_return ;
}
i + + ;
}
}
res = 0 ;
for ( i = 0 ; ! res & & i < AST_VECTOR_SIZE ( & localdeps ) ; i + + ) {
dep = find_resource ( AST_VECTOR_GET ( & localdeps , i ) , 0 ) ;
/* We've already confirmed dep is loaded in the first loop. */
res = module_deps_missing_recursive ( dep , missingdeps ) ;
}
clean_return :
AST_VECTOR_FREE ( & localdeps ) ;
return res ;
}
const char * ast_module_name ( const struct ast_module * mod )
{
if ( ! mod | | ! mod - > info ) {
@ -234,6 +468,10 @@ void ast_module_register(const struct ast_module_info *info)
mod - > ref_debug = ao2_t_alloc ( 0 , NULL , info - > name ) ;
}
AST_LIST_HEAD_INIT ( & mod - > users ) ;
AST_VECTOR_INIT ( & mod - > requires , 0 ) ;
AST_VECTOR_INIT ( & mod - > optional_modules , 0 ) ;
AST_VECTOR_INIT ( & mod - > enhances , 0 ) ;
AST_VECTOR_INIT ( & mod - > reffed_deps , 0 ) ;
AST_DLLIST_INSERT_TAIL ( & module_list , mod , entry ) ;
AST_DLLIST_UNLOCK ( & module_list ) ;
@ -244,6 +482,19 @@ void ast_module_register(const struct ast_module_info *info)
static void module_destroy ( struct ast_module * mod )
{
AST_VECTOR_CALLBACK_VOID ( & mod - > requires , ast_free ) ;
AST_VECTOR_FREE ( & mod - > requires ) ;
AST_VECTOR_CALLBACK_VOID ( & mod - > optional_modules , ast_free ) ;
AST_VECTOR_FREE ( & mod - > optional_modules ) ;
AST_VECTOR_CALLBACK_VOID ( & mod - > enhances , ast_free ) ;
AST_VECTOR_FREE ( & mod - > enhances ) ;
/* Release references to all dependencies. */
AST_VECTOR_CALLBACK_VOID ( & mod - > reffed_deps , ast_module_unref ) ;
AST_VECTOR_FREE ( & mod - > reffed_deps ) ;
AST_LIST_HEAD_DESTROY ( & mod - > users ) ;
ao2_cleanup ( mod - > ref_debug ) ;
ast_free ( mod ) ;
@ -267,7 +518,15 @@ void ast_module_unregister(const struct ast_module_info *info)
AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END ;
AST_DLLIST_UNLOCK ( & module_list ) ;
if ( mod ) {
if ( mod & & ! mod - > usecount ) {
/*
* We are intentionally leaking mod if usecount is not zero .
* This is necessary if the module is being forcefully unloaded .
* In addition module_destroy is not safe to run after exit ( )
* is called . ast_module_unregister is run during cleanup of
* the process when libc releases each module ' s shared object
* library .
*/
ast_debug ( 5 , " Unregistering module %s \n " , info - > name ) ;
module_destroy ( mod ) ;
}
@ -1117,6 +1376,23 @@ static enum ast_module_load_result start_resource(struct ast_module *mod)
return AST_MODULE_LOAD_FAILURE ;
}
if ( module_deps_reference ( mod , NULL ) ) {
struct module_vector missing ;
int i ;
AST_VECTOR_INIT ( & missing , 0 ) ;
if ( module_deps_missing_recursive ( mod , & missing ) ) {
ast_log ( LOG_ERROR , " %s has one or more unknown dependencies. \n " , mod - > info - > name ) ;
}
for ( i = 0 ; i < AST_VECTOR_SIZE ( & missing ) ; i + + ) {
ast_log ( LOG_ERROR , " %s loaded before dependency %s! \n " , mod - > info - > name ,
AST_VECTOR_GET ( & missing , i ) - > info - > name ) ;
}
AST_VECTOR_FREE ( & missing ) ;
return AST_MODULE_LOAD_FAILURE ;
}
if ( ! ast_fully_booted ) {
ast_verb ( 1 , " Loading %s. \n " , mod - > resource ) ;
}
@ -1184,6 +1460,17 @@ static enum ast_module_load_result load_resource(const char *resource_name, unsi
}
return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE ;
}
/* Split lists from mod->info. */
res = ast_vector_string_split ( & mod - > requires , mod - > info - > requires , " , " , 0 , strcasecmp ) ;
res | = ast_vector_string_split ( & mod - > optional_modules , mod - > info - > optional_modules , " , " , 0 , strcasecmp ) ;
res | = ast_vector_string_split ( & mod - > enhances , mod - > info - > enhances , " , " , 0 , strcasecmp ) ;
if ( res ) {
ast_log ( LOG_WARNING , " Failed to initialize dependency structures for module '%s'. \n " , resource_name ) ;
unload_dynamic_module ( mod ) ;
return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE ;
}
}
if ( inspect_module ( mod ) ) {
@ -1256,6 +1543,117 @@ static struct load_order_entry *add_to_load_order(const char *resource, struct l
AST_LIST_HEAD_NOLOCK ( load_retries , load_order_entry ) ;
static enum ast_module_load_result start_resource_attempt ( struct ast_module * mod , int * count )
{
enum ast_module_load_result lres ;
/* Try to grab required references. */
if ( module_deps_reference ( mod , NULL ) ) {
/* We're likely to retry so not an error. */
ast_debug ( 1 , " Module %s is missing dependencies \n " , mod - > resource ) ;
return AST_MODULE_LOAD_SKIP ;
}
lres = start_resource ( mod ) ;
ast_debug ( 3 , " START: %-46s[%d] %d \n " ,
mod - > resource ,
ast_test_flag ( mod - > info , AST_MODFLAG_LOAD_ORDER ) ? mod - > info - > load_pri : AST_MODPRI_DEFAULT ,
lres ) ;
if ( lres = = AST_MODULE_LOAD_SUCCESS ) {
( * count ) + + ;
} else if ( lres = = AST_MODULE_LOAD_FAILURE ) {
ast_log ( LOG_ERROR , " *** Failed to load module %s \n " , mod - > resource ) ;
}
return lres ;
}
static int start_resource_list ( struct module_vector * resources , int * mod_count )
{
struct module_vector missingdeps ;
int res = 0 ;
AST_VECTOR_INIT ( & missingdeps , 0 ) ;
while ( AST_VECTOR_SIZE ( resources ) ) {
struct ast_module * mod = AST_VECTOR_REMOVE ( resources , 0 , 1 ) ;
enum ast_module_load_result lres ;
lres = start_resource_attempt ( mod , mod_count ) ;
if ( lres = = AST_MODULE_LOAD_SUCCESS ) {
/* No missing dependencies, successful. */
continue ;
}
if ( lres = = AST_MODULE_LOAD_FAILURE ) {
ast_log ( LOG_ERROR , " Failed to load %s. \n " , ast_module_name ( mod ) ) ;
res = - 1 ;
break ;
}
if ( lres = = AST_MODULE_LOAD_DECLINE ) {
continue ;
}
res = module_deps_missing_recursive ( mod , & missingdeps ) ;
if ( res ) {
break ;
}
if ( ! AST_VECTOR_SIZE ( & missingdeps ) ) {
ast_log ( LOG_WARNING , " %s isn't missing any dependencies but still didn't start \n " ,
ast_module_name ( mod ) ) ;
/* Dependencies were met but the module failed to start. */
res = - 1 ;
break ;
}
ast_debug ( 1 , " %s has %d dependencies \n " ,
ast_module_name ( mod ) , ( int ) AST_VECTOR_SIZE ( & missingdeps ) ) ;
while ( AST_VECTOR_SIZE ( & missingdeps ) ) {
int didwork = 0 ;
int i = 0 ;
while ( i < AST_VECTOR_SIZE ( & missingdeps ) ) {
struct ast_module * dep = AST_VECTOR_GET ( & missingdeps , i ) ;
ast_debug ( 1 , " %s trying to start %s \n " , ast_module_name ( mod ) , ast_module_name ( dep ) ) ;
if ( ! start_resource_attempt ( dep , mod_count ) ) {
ast_debug ( 1 , " %s started %s \n " , ast_module_name ( mod ) , ast_module_name ( dep ) ) ;
AST_VECTOR_REMOVE ( & missingdeps , i , 1 ) ;
AST_VECTOR_REMOVE_CMP_ORDERED ( resources , dep ,
AST_VECTOR_ELEM_DEFAULT_CMP , AST_VECTOR_ELEM_CLEANUP_NOOP ) ;
didwork + + ;
continue ;
}
ast_debug ( 1 , " %s failed to start %s \n " , ast_module_name ( mod ) , ast_module_name ( dep ) ) ;
i + + ;
}
if ( ! didwork ) {
break ;
}
}
if ( AST_VECTOR_SIZE ( & missingdeps ) ) {
ast_log ( LOG_ERROR , " Failed to load %s due to unfilled dependencies. \n " ,
ast_module_name ( mod ) ) ;
res = - 1 ;
break ;
}
res = start_resource_attempt ( mod , mod_count ) ;
if ( res ) {
ast_log ( LOG_ERROR , " Failed to load %s: %d \n " , ast_module_name ( mod ) , res ) ;
break ;
}
}
AST_VECTOR_FREE ( & missingdeps ) ;
return res ;
}
/*! loads modules in order by load_pri, updates mod_count
\ return - 1 on failure to load module , - 2 on failure to load required module , otherwise 0
*/
@ -1358,30 +1756,9 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
AST_LIST_TRAVERSE_SAFE_END ;
}
/* second remove modules from heap sorted by priority */
for ( i = 0 ; i < AST_VECTOR_SIZE ( & resource_heap ) ; i + + ) {
struct ast_module * mod = AST_VECTOR_GET ( & resource_heap , i ) ;
enum ast_module_load_result lres ;
lres = start_resource ( mod ) ;
ast_debug ( 3 , " START: %-46s %d %d \n " , mod - > resource , lres , global_symbols ) ;
switch ( lres ) {
case AST_MODULE_LOAD_SUCCESS :
count + + ;
case AST_MODULE_LOAD_DECLINE :
break ;
case AST_MODULE_LOAD_FAILURE :
ast_log ( LOG_ERROR , " *** Failed to load module %s \n " , mod - > resource ) ;
res = - 1 ;
goto done ;
case AST_MODULE_LOAD_SKIP :
case AST_MODULE_LOAD_PRIORITY :
break ;
}
}
res = start_resource_list ( & resource_heap , & count ) ;
done :
while ( ( order = AST_LIST_REMOVE_HEAD ( & load_retries , entry ) ) ) {
ast_free ( order - > resource ) ;
ast_free ( order ) ;