@ -1216,7 +1216,73 @@ int modules_shutdown(void)
return ! res ;
return ! res ;
}
}
int ast_unload_resource ( const char * resource_name , enum ast_module_unload_mode force )
/*!
* \ brief Whether or not this module should be able to be unloaded successfully ,
* if we recursively unload any modules that are dependent on it .
* \ note module_list should be locked when calling this
* \ retval 0 if not , 1 if likely possible
*/
static int graceful_unload_possible ( struct ast_module * target , struct ast_vector_const_string * dependents )
{
struct ast_module * mod ;
int usecount = target - > usecount ;
/* Check the reffed_deps of each module to see if we're one of them. */
AST_DLLIST_TRAVERSE ( & module_list , mod , entry ) {
if ( AST_VECTOR_GET_CMP ( & mod - > reffed_deps , target , AST_VECTOR_ELEM_DEFAULT_CMP ) ) {
const char * name ;
/* This module is dependent on the target.
* If we can unload this module gracefully ,
* then that would decrement our use count .
* If any single module could not be unloaded gracefully ,
* then we don ' t proceed . */
int unloadable ;
if ( AST_VECTOR_GET_CMP ( dependents , ast_module_name ( mod ) , ! strcasecmp ) ) {
/* Already in our list, we already checked this module,
* and we gave it the green light . */
ast_debug ( 3 , " Skipping duplicate dependent %s \n " , ast_module_name ( mod ) ) ;
if ( ! - - usecount ) {
break ;
}
continue ;
}
unloadable = graceful_unload_possible ( mod , dependents ) ;
if ( ! unloadable ) {
ast_log ( LOG_NOTICE , " Can't unload %s right now because %s is dependent on it \n " , ast_module_name ( target ) , ast_module_name ( mod ) ) ;
return 0 ;
}
/* Insert at beginning, so later if we're loading modules again automatically, we can do so in the same order. */
name = ast_strdup ( ast_module_name ( mod ) ) ;
if ( ! name ) {
return 0 ;
}
ast_debug ( 3 , " Found new dependent %s \n " , ast_module_name ( mod ) ) ;
if ( AST_VECTOR_INSERT_AT ( dependents , 0 , name ) ) {
ast_log ( LOG_ERROR , " Failed to add module '%s' to vector \n " , ast_module_name ( mod ) ) ;
return 0 ;
}
if ( ! - - usecount ) {
break ;
}
}
}
if ( usecount ) {
ast_log ( LOG_NOTICE , " Module %s cannot be unloaded (would still have use count %d/%d after unloading dependents) \n " , ast_module_name ( target ) , usecount , target - > usecount ) ;
return 0 ;
}
return 1 ;
}
/*!
* \ brief Unload a resource
* \ param resource_name Module name
* \ param force
* \ param recursive Whether to attempt to recursively unload dependents of this module and load them again afterwards
* \ param dependents . Can be NULL if autounload is 0.
* \ retval 0 on success , - 1 on failure
*/
static int auto_unload_resource ( const char * resource_name , enum ast_module_unload_mode force , int recursive , struct ast_vector_const_string * dependents )
{
{
struct ast_module * mod ;
struct ast_module * mod ;
int res = - 1 ;
int res = - 1 ;
@ -1244,13 +1310,55 @@ int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode f
goto exit ; /* Skip all the intervening !error checks, only the last one is relevant. */
goto exit ; /* Skip all the intervening !error checks, only the last one is relevant. */
}
}
if ( ! error & & mod - > usecount > 0 & & recursive ) {
/* Try automatically unloading all modules dependent on the module we're trying to unload,
* and then , optionally , load them back again if we end up loading this module again .
* If any modules that have us as a dependency can ' t be unloaded , for whatever reason ,
* then the entire unload operation will fail , so to try to make this an atomic operation
* and avoid leaving modules in a partial unload state , first check if we think we ' re going
* to be able to pull this off , and if not , abort .
*
* A race condition is technically still possible , if some depending module suddenly goes in use
* between this check and trying to unload it , but this takes care of the majority of
* easy - to - avoid cases that would fail eventually anyways .
*
* Note that we can encounter false negatives ( e . g . unloading all the dependents would allow
* a module to unload , but graceful_unload_possible returns 0 ) . This is because it ' s only
* checking direct module dependencies ; other dependencies caused by a module registering
* a resource that cause its ref count to get bumped aren ' t accounted for here .
*/
if ( graceful_unload_possible ( mod , dependents ) ) {
int i , res = 0 ;
size_t num_deps = AST_VECTOR_SIZE ( dependents ) ;
ast_debug ( 1 , " %lu module%s will need to be unloaded \n " , AST_VECTOR_SIZE ( dependents ) , ESS ( AST_VECTOR_SIZE ( dependents ) ) ) ;
/* Unload from the end, since the last module was the first one added, which means it isn't a dependency of anything else. */
for ( i = AST_VECTOR_SIZE ( dependents ) - 1 ; i > = 0 ; i - - ) {
const char * depname = AST_VECTOR_GET ( dependents , i ) ;
res = ast_unload_resource ( depname , force ) ;
if ( res ) {
ast_log ( LOG_WARNING , " Failed to unload %lu module%s automatically (%s could not be unloaded) \n " , num_deps , ESS ( num_deps ) , depname ) ;
/* To be polite, load modules that we already unloaded,
* to try to leave things the way they were when we started . */
for ( i + + ; i < num_deps ; i + + ) {
const char * depname = AST_VECTOR_GET ( dependents , i ) ;
res = ast_load_resource ( depname ) ;
if ( res ) {
ast_log ( LOG_WARNING , " Could not load module '%s' again automatically \n " , depname ) ;
}
}
break ;
}
}
/* Either we failed, or we successfully unloaded everything.
* If we succeeded , we can now proceed and unload ourselves . */
}
}
if ( ! error & & ( mod - > usecount > 0 ) ) {
if ( ! error & & ( mod - > usecount > 0 ) ) {
if ( force )
if ( force ) {
ast_log ( LOG_WARNING , " Warning: Forcing removal of module '%s' with use count %d \n " ,
ast_log ( LOG_WARNING , " Warning: Forcing removal of module '%s' with use count %d \n " , resource_name , mod - > usecount ) ;
resource_name , mod - > usecount ) ;
} else {
else {
ast_log ( LOG_WARNING , " %s unload failed, '%s' has use count %d \n " , recursive ? " Recursive soft " : " Soft " , resource_name , mod - > usecount ) ;
ast_log ( LOG_WARNING , " Soft unload failed, '%s' has use count %d \n " , resource_name ,
mod - > usecount ) ;
error = 1 ;
error = 1 ;
}
}
}
}
@ -1296,6 +1404,52 @@ exit:
return res ;
return res ;
}
}
int ast_refresh_resource ( const char * resource_name , enum ast_module_unload_mode force , int recursive )
{
if ( recursive ) {
/* Recursively unload dependents of this module and then load them back again */
int res , i ;
struct ast_vector_const_string dependents ;
AST_VECTOR_INIT ( & dependents , 0 ) ;
res = auto_unload_resource ( resource_name , force , recursive , & dependents ) ;
if ( res ) {
AST_VECTOR_FREE ( & dependents ) ;
return 1 ;
}
/* Start by loading the target again. */
if ( ast_load_resource ( resource_name ) ) {
ast_log ( LOG_WARNING , " Failed to load module '%s' again automatically \n " , resource_name ) ;
AST_VECTOR_FREE ( & dependents ) ;
return - 1 ;
}
res = 0 ;
/* Finally, load again any modules we had to unload in order to refresh the target.
* We must load modules in the reverse order that we unloaded them ,
* to preserve dependency requirements . */
for ( i = 0 ; i < AST_VECTOR_SIZE ( & dependents ) ; i + + ) {
const char * depname = AST_VECTOR_GET ( & dependents , i ) ;
int mres = ast_load_resource ( depname ) ;
if ( mres ) {
ast_log ( LOG_WARNING , " Could not load module '%s' again automatically \n " , depname ) ;
}
res | = mres ;
}
AST_VECTOR_FREE ( & dependents ) ;
return res ? - 1 : 0 ;
}
/* Simple case: just unload and load the module again */
if ( ast_unload_resource ( resource_name , force ) ) {
return 1 ;
}
return ast_load_resource ( resource_name ) ;
}
int ast_unload_resource ( const char * resource_name , enum ast_module_unload_mode force )
{
return auto_unload_resource ( resource_name , force , 0 , NULL ) ;
}
static int module_matches_helper_type ( struct ast_module * mod , enum ast_module_helper_type type )
static int module_matches_helper_type ( struct ast_module * mod , enum ast_module_helper_type type )
{
{
switch ( type ) {
switch ( type ) {