2006-08-21 02:11:39 +00:00
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
* Kevin P. Fleming <kpfleming@digium.com>
* Luigi Rizzo <rizzo@icir.org>
*
* 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 Module Loader
2006-09-15 05:00:27 +00:00
* \author Mark Spencer <markster@digium.com>
2006-08-21 02:11:39 +00:00
* \author Kevin P. Fleming <kpfleming@digium.com>
* \author Luigi Rizzo <rizzo@icir.org>
* - See ModMngMnt
*/
2012-06-15 16:20:16 +00:00
/*** MODULEINFO
<support_level>core</support_level>
***/
2006-08-21 02:11:39 +00:00
# include "asterisk.h"
2007-11-20 22:18:21 +00:00
# include "asterisk/_private.h"
2007-11-20 23:16:15 +00:00
# include "asterisk/paths.h" /* use ast_config_AST_MODULE_DIR */
2006-08-21 02:11:39 +00:00
# include <dirent.h>
2014-07-25 14:27:52 +00:00
# include "asterisk/dlinkedlists.h"
2006-08-21 02:11:39 +00:00
# include "asterisk/module.h"
# include "asterisk/config.h"
# include "asterisk/channel.h"
# include "asterisk/term.h"
# include "asterisk/manager.h"
2018-02-16 22:11:42 -05:00
# include "asterisk/io.h"
2006-08-21 02:11:39 +00:00
# include "asterisk/lock.h"
2017-11-21 01:28:48 -05:00
# include "asterisk/vector.h"
2010-01-27 18:29:49 +00:00
# include "asterisk/app.h"
2012-08-13 20:36:51 +00:00
# include "asterisk/test.h"
2018-01-25 13:06:12 -05:00
# include "asterisk/cli.h"
2006-08-21 02:11:39 +00:00
# include <dlfcn.h>
# include "asterisk/md5.h"
# include "asterisk/utils.h"
2012-07-11 02:06:05 +00:00
/*** DOCUMENTATION
2013-05-24 20:44:07 +00:00
<managerEvent language="en_US" name="Reload">
<managerEventInstance class="EVENT_FLAG_SYSTEM">
2025-01-23 16:35:58 -05:00
<since>
<version>12.0.0</version>
</since>
2013-05-24 20:44:07 +00:00
<synopsis>Raised when a module has been reloaded in Asterisk.</synopsis>
<syntax>
<parameter name="Module">
<para>The name of the module that was reloaded, or
<literal>All</literal> if all modules were reloaded</para>
</parameter>
<parameter name="Status">
<para>The numeric status code denoting the success or failure
of the reload request.</para>
<enumlist>
<enum name="0"><para>Success</para></enum>
<enum name="1"><para>Request queued</para></enum>
<enum name="2"><para>Module not found</para></enum>
<enum name="3"><para>Error</para></enum>
<enum name="4"><para>Reload already in progress</para></enum>
<enum name="5"><para>Module uninitialized</para></enum>
<enum name="6"><para>Reload not supported</para></enum>
</enumlist>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
2018-02-06 21:27:55 +01:00
<managerEvent language="en_US" name="Load">
<managerEventInstance class="EVENT_FLAG_SYSTEM">
2025-01-23 16:35:58 -05:00
<since>
<version>16.0.0</version>
</since>
2018-02-06 21:27:55 +01:00
<synopsis>Raised when a module has been loaded in Asterisk.</synopsis>
<syntax>
<parameter name="Module">
<para>The name of the module that was loaded</para>
</parameter>
<parameter name="Status">
<para>The result of the load request.</para>
<enumlist>
<enum name="Failure"><para>Module could not be loaded properly</para></enum>
<enum name="Success"><para>Module loaded and configured</para></enum>
<enum name="Decline"><para>Module is not configured</para></enum>
</enumlist>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="Unload">
<managerEventInstance class="EVENT_FLAG_SYSTEM">
2025-01-23 16:35:58 -05:00
<since>
<version>16.0.0</version>
</since>
2018-02-06 21:27:55 +01:00
<synopsis>Raised when a module has been unloaded in Asterisk.</synopsis>
<syntax>
<parameter name="Module">
<para>The name of the module that was unloaded</para>
</parameter>
<parameter name="Status">
<para>The result of the unload request.</para>
<enumlist>
<enum name="Success"><para>Module unloaded successfully</para></enum>
</enumlist>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
2012-07-11 02:06:05 +00:00
***/
2007-10-22 14:56:05 +00:00
# ifndef RTLD_NOW
2006-08-21 02:11:39 +00:00
# define RTLD_NOW 0
# endif
2007-11-17 09:51:45 +00:00
# ifndef RTLD_LOCAL
# define RTLD_LOCAL 0
# endif
2006-08-21 02:11:39 +00:00
struct ast_module_user {
struct ast_channel * chan ;
AST_LIST_ENTRY ( ast_module_user ) entry ;
} ;
2014-07-25 14:27:52 +00:00
AST_DLLIST_HEAD ( module_user_list , ast_module_user ) ;
2006-08-21 02:11:39 +00:00
2009-05-21 21:13:09 +00:00
static const unsigned char expected_key [ ] =
2006-08-21 02:11:39 +00:00
{ 0x87 , 0x76 , 0x79 , 0x35 , 0x23 , 0xea , 0x3a , 0xd3 ,
0x25 , 0x2a , 0xbb , 0x35 , 0x87 , 0xe4 , 0x22 , 0x24 } ;
2007-11-20 19:28:10 +00:00
static char buildopt_sum [ 33 ] = AST_BUILDOPT_SUM ;
2007-11-16 16:56:59 +00:00
2017-11-21 01:28:48 -05:00
AST_VECTOR ( module_vector , struct ast_module * ) ;
2018-09-28 11:13:39 -04:00
/*! Used with AST_VECTOR_CALLBACK_VOID to create a
* comma separated list of module names for error messages. */
# define STR_APPEND_TEXT(txt, str) \
ast_str_append(str, 0, "%s%s", \
ast_str_strlen(*(str)) > 0 ? ", " : "", \
txt)
2017-10-29 23:00:52 -04:00
/* Built-in module registrations need special handling at startup */
static unsigned int loader_ready ;
2018-09-28 11:13:39 -04:00
/*! String container for deferring output of startup errors. */
static struct ast_vector_string startup_errors ;
static struct ast_str * startup_error_builder ;
2021-03-10 11:03:11 -04:00
# if defined(HAVE_PERMANENT_DLOPEN) || defined(AST_XML_DOCS)
2019-04-02 22:49:52 +02:00
static char * get_name_from_resource ( const char * resource )
{
int len ;
const char * last_three ;
char * mod_name ;
if ( ! resource ) {
return NULL ;
}
len = strlen ( resource ) ;
if ( len > 3 ) {
last_three = & resource [ len - 3 ] ;
if ( ! strcasecmp ( last_three , " .so " ) ) {
mod_name = ast_calloc ( 1 , len - 2 ) ;
if ( mod_name ) {
ast_copy_string ( mod_name , resource , len - 2 ) ;
return mod_name ;
} else {
/* Unable to allocate memory. */
return NULL ;
}
}
}
/* Resource is the name - happens when manually unloading a module. */
mod_name = ast_calloc ( 1 , len + 1 ) ;
if ( mod_name ) {
ast_copy_string ( mod_name , resource , len + 1 ) ;
return mod_name ;
}
/* Unable to allocate memory. */
return NULL ;
}
2021-03-10 11:03:11 -04:00
# endif
# if defined(HAVE_PERMANENT_DLOPEN)
# define FIRST_DLOPEN 999
struct ao2_container * info_list = NULL ;
struct info_list_obj {
const struct ast_module_info * info ;
int dlopened ;
char name [ 0 ] ;
} ;
static struct info_list_obj * info_list_obj_alloc ( const char * name ,
const struct ast_module_info * info )
{
struct info_list_obj * new_entry ;
new_entry = ao2_alloc ( sizeof ( * new_entry ) + strlen ( name ) + 1 , NULL ) ;
if ( ! new_entry ) {
return NULL ;
}
strcpy ( new_entry - > name , name ) ; /* SAFE */
new_entry - > info = info ;
new_entry - > dlopened = FIRST_DLOPEN ;
return new_entry ;
}
AO2_STRING_FIELD_CMP_FN ( info_list_obj , name )
2019-04-02 22:49:52 +02:00
static void manual_mod_reg ( const void * lib , const char * resource )
{
struct info_list_obj * obj_tmp ;
char * mod_name ;
if ( lib ) {
mod_name = get_name_from_resource ( resource ) ;
if ( mod_name ) {
obj_tmp = ao2_find ( info_list , mod_name , OBJ_SEARCH_KEY ) ;
if ( obj_tmp ) {
if ( obj_tmp - > dlopened = = FIRST_DLOPEN ) {
obj_tmp - > dlopened = 1 ;
} else {
ast_module_register ( obj_tmp - > info ) ;
}
ao2_ref ( obj_tmp , - 1 ) ;
}
ast_free ( mod_name ) ;
}
}
}
static void manual_mod_unreg ( const char * resource )
{
struct info_list_obj * obj_tmp ;
char * mod_name ;
/* When Asterisk shuts down the destructor is called automatically. */
if ( ast_shutdown_final ( ) ) {
return ;
}
mod_name = get_name_from_resource ( resource ) ;
if ( mod_name ) {
obj_tmp = ao2_find ( info_list , mod_name , OBJ_SEARCH_KEY ) ;
if ( obj_tmp ) {
ast_module_unregister ( obj_tmp - > info ) ;
ao2_ref ( obj_tmp , - 1 ) ;
}
ast_free ( mod_name ) ;
}
}
# endif
2018-09-28 11:13:39 -04:00
static __attribute__ ( ( format ( printf , 1 , 2 ) ) ) void module_load_error ( const char * fmt , . . . )
{
char * copy = NULL ;
va_list ap ;
va_start ( ap , fmt ) ;
if ( startup_error_builder ) {
ast_str_set_va ( & startup_error_builder , 0 , fmt , ap ) ;
copy = ast_strdup ( ast_str_buffer ( startup_error_builder ) ) ;
if ( ! copy | | AST_VECTOR_APPEND ( & startup_errors , copy ) ) {
ast_log ( LOG_ERROR , " %s " , ast_str_buffer ( startup_error_builder ) ) ;
ast_free ( copy ) ;
}
} else {
ast_log_ap ( LOG_ERROR , fmt , ap ) ;
}
va_end ( ap ) ;
}
2015-04-09 23:08:10 +00:00
/*!
* \brief Internal flag to indicate all modules have been initially loaded.
*/
static int modules_loaded ;
2006-08-21 02:11:39 +00:00
struct ast_module {
const struct ast_module_info * info ;
2017-11-19 22:23:44 -05:00
/*! Used to get module references into refs log */
2015-02-11 17:03:04 +00:00
void * ref_debug ;
2017-11-19 22:23:44 -05:00
/*! The shared lib. */
void * lib ;
/*! Number of 'users' and other references currently holding the module. */
int usecount ;
/*! List of users holding the module. */
struct module_user_list users ;
2017-11-22 00:39:33 -05:00
/*! 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 ;
2007-05-03 16:43:49 +00:00
struct {
2017-11-19 22:23:44 -05:00
/*! The module running and ready to accept requests. */
After some study, thought, comparing, etc. I've backed out the previous universal mod to make ast_flags a 64 bit thing. Instead, I added a 64-bit version of ast_flags (ast_flags64), and 64-bit versions of the test-flag, set-flag, etc. macros, and an app_parse_options64 routine, and I use these in app_dial alone, to eliminate the 30-option limit it had grown to meet. There is room now for 32 more options and flags. I was heavily tempted to implement some of the other ideas that were presented, but this solution does not intro any new versions of dial, doesn't have a different API, has a minimal/zero impact on code outside of dial, and doesn't seriously (I hope) affect the code structure of dial. It's the best I can think of right now. My goal was NOT to rewrite dial. I leave that to a future, coordinated effort.
2007-07-19 23:24:27 +00:00
unsigned int running : 1 ;
2017-11-19 22:23:44 -05:00
/*! The module has declined to start. */
After some study, thought, comparing, etc. I've backed out the previous universal mod to make ast_flags a 64 bit thing. Instead, I added a 64-bit version of ast_flags (ast_flags64), and 64-bit versions of the test-flag, set-flag, etc. macros, and an app_parse_options64 routine, and I use these in app_dial alone, to eliminate the 30-option limit it had grown to meet. There is room now for 32 more options and flags. I was heavily tempted to implement some of the other ideas that were presented, but this solution does not intro any new versions of dial, doesn't have a different API, has a minimal/zero impact on code outside of dial, and doesn't seriously (I hope) affect the code structure of dial. It's the best I can think of right now. My goal was NOT to rewrite dial. I leave that to a future, coordinated effort.
2007-07-19 23:24:27 +00:00
unsigned int declined : 1 ;
2017-11-19 22:23:44 -05:00
/*! This module is being held open until it's time to shutdown. */
2015-02-11 17:03:04 +00:00
unsigned int keepuntilshutdown : 1 ;
2017-10-29 23:00:52 -04:00
/*! The module is built-in. */
unsigned int builtin : 1 ;
2018-07-15 14:58:15 -04:00
/*! The admin has declared this module is required. */
unsigned int required : 1 ;
/*! This module is marked for preload. */
unsigned int preload : 1 ;
2007-05-03 16:43:49 +00:00
} flags ;
2014-07-25 14:27:52 +00:00
AST_DLLIST_ENTRY ( ast_module ) entry ;
2006-08-21 02:11:39 +00:00
char resource [ 0 ] ;
} ;
2014-07-25 14:27:52 +00:00
static AST_DLLIST_HEAD_STATIC ( module_list , ast_module ) ;
2006-08-21 02:11:39 +00:00
2018-02-06 21:27:55 +01:00
struct load_results_map {
int result ;
const char * name ;
} ;
static const struct load_results_map load_results [ ] = {
{ AST_MODULE_LOAD_SUCCESS , " Success " } ,
{ AST_MODULE_LOAD_DECLINE , " Decline " } ,
{ AST_MODULE_LOAD_SKIP , " Skip " } ,
{ AST_MODULE_LOAD_PRIORITY , " Priority " } ,
{ AST_MODULE_LOAD_FAILURE , " Failure " } ,
} ;
# define AST_MODULE_LOAD_UNKNOWN_STRING "Unknown" /* Status string for unknown load status */
static void publish_load_message_type ( const char * type , const char * name , const char * status ) ;
static void publish_reload_message ( const char * name , enum ast_module_reload_result result ) ;
static void publish_load_message ( const char * name , enum ast_module_load_result result ) ;
static void publish_unload_message ( const char * name , const char * status ) ;
2017-10-29 23:00:52 -04:00
/*
* module_list is cleared by its constructor possibly after
* we start accumulating built-in modules, so we need to
* use another list (without the lock) to accumulate them.
*/
static struct module_list builtin_module_list ;
2017-12-11 18:07:54 -05:00
static int module_vector_strcasecmp ( struct ast_module * a , struct ast_module * b )
{
return strcasecmp ( a - > resource , b - > resource ) ;
}
2017-11-21 01:28:48 -05:00
static int module_vector_cmp ( struct ast_module * a , struct ast_module * b )
{
2018-07-15 14:58:15 -04:00
int preload_diff = ( int ) b - > flags . preload - ( int ) a - > flags . preload ;
2017-11-21 01:28:48 -05:00
/* if load_pri is not set, default is 128. Lower is better */
int a_pri = ast_test_flag ( a - > info , AST_MODFLAG_LOAD_ORDER )
? a - > info - > load_pri : AST_MODPRI_DEFAULT ;
int b_pri = ast_test_flag ( b - > info , AST_MODFLAG_LOAD_ORDER )
? b - > info - > load_pri : AST_MODPRI_DEFAULT ;
2018-07-15 14:58:15 -04:00
if ( preload_diff ) {
/* -1 preload a but not b */
/* 0 preload both or neither */
/* 1 preload b but not a */
return preload_diff ;
}
2017-11-21 01:28:48 -05:00
/*
* Returns comparison values for a vector sorted by priority.
* <0 a_pri < b_pri
* =0 a_pri == b_pri
* >0 a_pri > b_pri
*/
return a_pri - b_pri ;
}
2017-11-22 00:39:33 -05:00
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 ;
}
2011-10-10 14:16:27 +00:00
const char * ast_module_name ( const struct ast_module * mod )
{
if ( ! mod | | ! mod - > info ) {
return NULL ;
}
return mod - > info - > name ;
}
2006-08-21 02:11:39 +00:00
struct loadupdate {
int ( * updater ) ( void ) ;
AST_LIST_ENTRY ( loadupdate ) entry ;
} ;
2014-07-25 14:27:52 +00:00
static AST_DLLIST_HEAD_STATIC ( updaters , loadupdate ) ;
2006-08-21 02:11:39 +00:00
AST_MUTEX_DEFINE_STATIC ( reloadlock ) ;
2009-06-04 14:31:24 +00:00
struct reload_queue_item {
AST_LIST_ENTRY ( reload_queue_item ) entry ;
char module [ 0 ] ;
} ;
static int do_full_reload = 0 ;
2014-07-25 14:27:52 +00:00
static AST_DLLIST_HEAD_STATIC ( reload_queue , reload_queue_item ) ;
2009-06-04 14:31:24 +00:00
2017-11-21 21:34:56 -05:00
/*!
* \internal
*
* This variable is set by load_dynamic_module so ast_module_register
* can know what pointer is being registered.
*
* This is protected by the module_list lock.
*/
2017-12-23 23:51:13 -05:00
static struct ast_module * volatile resource_being_loaded ;
2006-08-21 02:11:39 +00:00
2017-11-21 21:34:56 -05:00
/*!
* \internal
* \brief Used by AST_MODULE_INFO to register with the module loader.
*
* This function is automatically called when each module is opened.
* It must never be used from outside AST_MODULE_INFO.
*/
2006-08-21 02:11:39 +00:00
void ast_module_register ( const struct ast_module_info * info )
{
2017-11-21 21:34:56 -05:00
struct ast_module * mod ;
2017-10-29 23:00:52 -04:00
if ( ! loader_ready ) {
2018-02-16 22:11:42 -05:00
mod = ast_std_calloc ( 1 , sizeof ( * mod ) + strlen ( info - > name ) + 1 ) ;
2017-10-29 23:00:52 -04:00
if ( ! mod ) {
/* We haven't even reached main() yet, if we can't
* allocate memory at this point just give up. */
2018-02-16 22:11:42 -05:00
fprintf ( stderr , " Allocation failure during startup. \n " ) ;
2017-10-29 23:00:52 -04:00
exit ( 2 ) ;
}
strcpy ( mod - > resource , info - > name ) ; /* safe */
mod - > info = info ;
mod - > flags . builtin = 1 ;
AST_DLLIST_INSERT_TAIL ( & builtin_module_list , mod , entry ) ;
/* ast_module_register for built-in modules is run again during module preload. */
return ;
}
2017-11-21 21:34:56 -05:00
/*
* This lock protects resource_being_loaded as well as the module
* list. Normally we already have a lock on module_list when we
* begin the load but locking again from here prevents corruption
* if an asterisk module is dlopen'ed from outside the module loader.
*/
AST_DLLIST_LOCK ( & module_list ) ;
mod = resource_being_loaded ;
if ( ! mod ) {
AST_DLLIST_UNLOCK ( & module_list ) ;
return ;
}
2006-08-21 02:11:39 +00:00
2014-05-28 22:54:12 +00:00
ast_debug ( 5 , " Registering module %s \n " , info - > name ) ;
2013-08-30 13:40:27 +00:00
2017-11-21 21:34:56 -05:00
/* This tells load_dynamic_module that we're registered. */
resource_being_loaded = NULL ;
2006-08-21 02:11:39 +00:00
mod - > info = info ;
2015-04-17 03:16:59 -04:00
if ( ast_opt_ref_debug ) {
2018-10-01 23:12:14 -04:00
mod - > ref_debug = ao2_t_alloc_options ( 0 , NULL , AO2_ALLOC_OPT_LOCK_NOLOCK , info - > name ) ;
2015-04-17 03:16:59 -04:00
}
2006-08-21 02:11:39 +00:00
AST_LIST_HEAD_INIT ( & mod - > users ) ;
2017-11-22 00:39:33 -05:00
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 ) ;
2006-08-21 02:11:39 +00:00
2017-03-24 08:43:05 -04:00
AST_DLLIST_INSERT_TAIL ( & module_list , mod , entry ) ;
AST_DLLIST_UNLOCK ( & module_list ) ;
2006-08-21 02:11:39 +00:00
/* give the module a copy of its own handle, for later use in registrations and the like */
* ( ( struct ast_module * * ) & ( info - > self ) ) = mod ;
2019-04-02 22:49:52 +02:00
# if defined(HAVE_PERMANENT_DLOPEN)
if ( mod - > flags . builtin ! = 1 ) {
struct info_list_obj * obj_tmp = ao2_find ( info_list , info - > name ,
OBJ_SEARCH_KEY ) ;
if ( ! obj_tmp ) {
obj_tmp = info_list_obj_alloc ( info - > name , info ) ;
if ( obj_tmp ) {
ao2_link ( info_list , obj_tmp ) ;
ao2_ref ( obj_tmp , - 1 ) ;
}
} else {
ao2_ref ( obj_tmp , - 1 ) ;
}
}
# endif
2006-08-21 02:11:39 +00:00
}
2018-07-26 13:52:46 -04:00
static int module_post_register ( struct ast_module * mod )
{
int res ;
/* 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 ) ;
return res ;
}
2017-11-21 21:34:56 -05:00
static void module_destroy ( struct ast_module * mod )
{
2017-11-22 00:39:33 -05:00
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 ) ;
2017-11-21 21:34:56 -05:00
AST_LIST_HEAD_DESTROY ( & mod - > users ) ;
ao2_cleanup ( mod - > ref_debug ) ;
2018-02-16 22:11:42 -05:00
if ( mod - > flags . builtin ) {
ast_std_free ( mod ) ;
} else {
ast_free ( mod ) ;
}
2017-11-21 21:34:56 -05:00
}
2006-08-21 02:11:39 +00:00
void ast_module_unregister ( const struct ast_module_info * info )
{
struct ast_module * mod = NULL ;
/* it is assumed that the users list in the module structure
will already be empty, or we cannot have gotten to this
point
*/
2014-07-25 14:27:52 +00:00
AST_DLLIST_LOCK ( & module_list ) ;
AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN ( & module_list , mod , entry ) {
2006-08-21 02:11:39 +00:00
if ( mod - > info = = info ) {
2014-07-25 14:27:52 +00:00
AST_DLLIST_REMOVE_CURRENT ( entry ) ;
2006-08-21 02:11:39 +00:00
break ;
}
}
2014-07-25 14:27:52 +00:00
AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END ;
AST_DLLIST_UNLOCK ( & module_list ) ;
2006-08-21 02:11:39 +00:00
2017-11-22 00:39:33 -05:00
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.
*/
2014-05-28 22:54:12 +00:00
ast_debug ( 5 , " Unregistering module %s \n " , info - > name ) ;
2017-11-21 21:34:56 -05:00
module_destroy ( mod ) ;
2006-08-21 02:11:39 +00:00
}
}
2012-11-05 21:42:49 +00:00
struct ast_module_user * __ast_module_user_add ( struct ast_module * mod , struct ast_channel * chan )
2006-08-21 02:11:39 +00:00
{
2012-11-05 21:42:49 +00:00
struct ast_module_user * u ;
2006-08-21 02:11:39 +00:00
2012-11-05 21:42:49 +00:00
u = ast_calloc ( 1 , sizeof ( * u ) ) ;
if ( ! u ) {
2006-08-21 02:11:39 +00:00
return NULL ;
2012-11-05 21:42:49 +00:00
}
2006-08-21 02:11:39 +00:00
u - > chan = chan ;
AST_LIST_LOCK ( & mod - > users ) ;
AST_LIST_INSERT_HEAD ( & mod - > users , u , entry ) ;
AST_LIST_UNLOCK ( & mod - > users ) ;
2015-04-17 03:16:59 -04:00
if ( mod - > ref_debug ) {
ao2_ref ( mod - > ref_debug , + 1 ) ;
}
2015-02-11 17:03:04 +00:00
2006-08-21 02:11:39 +00:00
ast_atomic_fetchadd_int ( & mod - > usecount , + 1 ) ;
ast_update_use_count ( ) ;
return u ;
}
void __ast_module_user_remove ( struct ast_module * mod , struct ast_module_user * u )
{
2012-11-05 21:42:49 +00:00
if ( ! u ) {
return ;
}
2012-12-17 23:10:42 +00:00
2006-08-21 02:11:39 +00:00
AST_LIST_LOCK ( & mod - > users ) ;
2012-12-17 23:10:42 +00:00
u = AST_LIST_REMOVE ( & mod - > users , u , entry ) ;
2006-08-21 02:11:39 +00:00
AST_LIST_UNLOCK ( & mod - > users ) ;
2012-12-17 23:10:42 +00:00
if ( ! u ) {
/*
* Was not in the list. Either a bad pointer or
* __ast_module_user_hangup_all() has been called.
*/
return ;
}
2015-04-17 03:16:59 -04:00
if ( mod - > ref_debug ) {
ao2_ref ( mod - > ref_debug , - 1 ) ;
}
2015-02-11 17:03:04 +00:00
2006-08-21 02:11:39 +00:00
ast_atomic_fetchadd_int ( & mod - > usecount , - 1 ) ;
2007-06-06 21:20:11 +00:00
ast_free ( u ) ;
2006-08-21 02:11:39 +00:00
ast_update_use_count ( ) ;
}
void __ast_module_user_hangup_all ( struct ast_module * mod )
{
struct ast_module_user * u ;
AST_LIST_LOCK ( & mod - > users ) ;
while ( ( u = AST_LIST_REMOVE_HEAD ( & mod - > users , entry ) ) ) {
2012-02-22 21:22:43 +00:00
if ( u - > chan ) {
ast_softhangup ( u - > chan , AST_SOFTHANGUP_APPUNLOAD ) ;
}
2015-02-11 17:03:04 +00:00
2015-04-17 03:16:59 -04:00
if ( mod - > ref_debug ) {
ao2_ref ( mod - > ref_debug , - 1 ) ;
}
2015-02-11 17:03:04 +00:00
2006-08-21 02:11:39 +00:00
ast_atomic_fetchadd_int ( & mod - > usecount , - 1 ) ;
2007-06-06 21:20:11 +00:00
ast_free ( u ) ;
2006-08-21 02:11:39 +00:00
}
AST_LIST_UNLOCK ( & mod - > users ) ;
2008-03-04 23:04:29 +00:00
ast_update_use_count ( ) ;
2006-08-21 02:11:39 +00:00
}
static int printdigest ( const unsigned char * d )
{
int x , pos ;
char buf [ 256 ] ; /* large enough so we don't have to worry */
for ( pos = 0 , x = 0 ; x < 16 ; x + + )
2014-12-17 10:23:32 +00:00
pos + = sprintf ( buf + pos , " %02hhx " , * d + + ) ;
2006-08-21 02:11:39 +00:00
2007-06-24 18:51:41 +00:00
ast_debug ( 1 , " Unexpected signature:%s \n " , buf ) ;
2006-08-21 02:11:39 +00:00
return 0 ;
}
2023-03-23 11:08:15 -04:00
# define key_matches(a, b) (memcmp((a), (b), 16) == 0)
2006-08-21 02:11:39 +00:00
static int verify_key ( const unsigned char * key )
{
struct MD5Context c ;
unsigned char digest [ 16 ] ;
MD5Init ( & c ) ;
MD5Update ( & c , key , strlen ( ( char * ) key ) ) ;
MD5Final ( digest , & c ) ;
if ( key_matches ( expected_key , digest ) )
return 0 ;
printdigest ( digest ) ;
return - 1 ;
}
2017-12-08 19:19:34 -05:00
static size_t resource_name_baselen ( const char * name )
2006-08-21 02:11:39 +00:00
{
2017-12-08 19:19:34 -05:00
size_t len = strlen ( name ) ;
2006-08-21 02:11:39 +00:00
2017-12-08 19:19:34 -05:00
if ( len > 3 & & ! strcasecmp ( name + len - 3 , " .so " ) ) {
return len - 3 ;
2006-08-21 02:11:39 +00:00
}
2017-12-08 19:19:34 -05:00
return len ;
}
static int resource_name_match ( const char * name1 , size_t baselen1 , const char * name2 )
{
if ( baselen1 ! = resource_name_baselen ( name2 ) ) {
return - 1 ;
2006-08-21 02:11:39 +00:00
}
2017-12-08 19:19:34 -05:00
return strncasecmp ( name1 , name2 , baselen1 ) ;
2006-08-21 02:11:39 +00:00
}
static struct ast_module * find_resource ( const char * resource , int do_lock )
{
struct ast_module * cur ;
2017-12-08 19:19:34 -05:00
size_t resource_baselen = resource_name_baselen ( resource ) ;
2006-08-21 02:11:39 +00:00
2014-07-25 14:27:52 +00:00
if ( do_lock ) {
AST_DLLIST_LOCK ( & module_list ) ;
}
2006-08-21 02:11:39 +00:00
2014-07-25 14:27:52 +00:00
AST_DLLIST_TRAVERSE ( & module_list , cur , entry ) {
2017-12-08 19:19:34 -05:00
if ( ! resource_name_match ( resource , resource_baselen , cur - > resource ) ) {
2006-08-21 02:11:39 +00:00
break ;
2017-12-08 19:19:34 -05:00
}
2006-08-21 02:11:39 +00:00
}
2014-07-25 14:27:52 +00:00
if ( do_lock ) {
AST_DLLIST_UNLOCK ( & module_list ) ;
}
2006-08-21 02:11:39 +00:00
return cur ;
}
2013-08-30 13:40:27 +00:00
/*!
* \brief dlclose(), with failure logging.
*/
static void logged_dlclose ( const char * name , void * lib )
{
2013-10-31 16:06:14 +00:00
char * error ;
if ( ! lib ) {
return ;
}
/* Clear any existing error */
dlerror ( ) ;
if ( dlclose ( lib ) ) {
error = dlerror ( ) ;
ast_log ( AST_LOG_ERROR , " Failure in dlclose for module '%s': %s \n " ,
S_OR ( name , " unknown " ) , S_OR ( error , " Unknown error " ) ) ;
2019-04-02 22:49:52 +02:00
# if defined(HAVE_PERMANENT_DLOPEN)
} else {
manual_mod_unreg ( name ) ;
# endif
2013-08-30 13:40:27 +00:00
}
}
# if defined(HAVE_RTLD_NOLOAD)
/*!
* \brief Check to see if the given resource is loaded.
*
* \param resource_name Name of the resource, including .so suffix.
* \return False (0) if module is not loaded.
* \return True (non-zero) if module is loaded.
*/
static int is_module_loaded ( const char * resource_name )
{
char fn [ PATH_MAX ] = " " ;
void * lib ;
snprintf ( fn , sizeof ( fn ) , " %s/%s " , ast_config_AST_MODULE_DIR ,
resource_name ) ;
lib = dlopen ( fn , RTLD_LAZY | RTLD_NOLOAD ) ;
if ( lib ) {
logged_dlclose ( resource_name , lib ) ;
return 1 ;
}
return 0 ;
}
# endif
2006-08-21 02:11:39 +00:00
static void unload_dynamic_module ( struct ast_module * mod )
{
2015-03-22 23:11:32 +00:00
# if defined(HAVE_RTLD_NOLOAD)
2013-08-30 13:40:27 +00:00
char * name = ast_strdupa ( ast_module_name ( mod ) ) ;
2015-03-22 23:11:32 +00:00
# endif
2006-08-26 19:45:16 +00:00
void * lib = mod - > lib ;
/* WARNING: the structure pointed to by mod is going to
disappear when this operation succeeds, so we can't
dereference it */
2013-10-31 16:06:14 +00:00
logged_dlclose ( ast_module_name ( mod ) , lib ) ;
2013-08-30 13:40:27 +00:00
/* There are several situations where the module might still be resident
* in memory.
*
* If somehow there was another dlopen() on the same module (unlikely,
* since that all is supposed to happen in loader.c).
*
* Avoid the temptation of repeating the dlclose(). The other code that
* dlopened the module still has its module reference, and should close
* it itself. In other situations, dlclose() will happily return success
* for as many times as you wish to call it.
*/
# if defined(HAVE_RTLD_NOLOAD)
if ( is_module_loaded ( name ) ) {
ast_log ( LOG_WARNING , " Module '%s' could not be completely unloaded \n " , name ) ;
}
# endif
2006-08-21 02:11:39 +00:00
}
2018-09-28 11:13:39 -04:00
static int load_dlopen_missing ( struct ast_str * * list , struct ast_vector_string * deps )
{
int i ;
int c = 0 ;
for ( i = 0 ; i < AST_VECTOR_SIZE ( deps ) ; i + + ) {
const char * dep = AST_VECTOR_GET ( deps , i ) ;
if ( ! find_resource ( dep , 0 ) ) {
STR_APPEND_TEXT ( dep , list ) ;
c + + ;
}
}
return c ;
}
2017-11-21 00:10:58 -05:00
/*!
* \internal
* \brief Attempt to dlopen a module.
*
* \param resource_in The module name to load.
* \param so_ext ".so" or blank if ".so" is already part of resource_in.
* \param filename Passed directly to dlopen.
* \param flags Passed directly to dlopen.
* \param suppress_logging Do not log any error from dlopen.
*
* \return Pointer to opened module, NULL on error.
*
* \warning module_list must be locked before calling this function.
*/
static struct ast_module * load_dlopen ( const char * resource_in , const char * so_ext ,
const char * filename , int flags , unsigned int suppress_logging )
2006-08-21 02:11:39 +00:00
{
struct ast_module * mod ;
2017-11-21 21:34:56 -05:00
ast_assert ( ! resource_being_loaded ) ;
2017-11-21 00:10:58 -05:00
mod = ast_calloc ( 1 , sizeof ( * mod ) + strlen ( resource_in ) + strlen ( so_ext ) + 1 ) ;
if ( ! mod ) {
return NULL ;
2006-08-21 02:11:39 +00:00
}
2017-11-21 00:10:58 -05:00
sprintf ( mod - > resource , " %s%s " , resource_in , so_ext ) ; /* safe */
2006-08-21 14:42:03 +00:00
2017-11-21 00:10:58 -05:00
resource_being_loaded = mod ;
mod - > lib = dlopen ( filename , flags ) ;
2019-04-02 22:49:52 +02:00
# if defined(HAVE_PERMANENT_DLOPEN)
manual_mod_reg ( mod - > lib , mod - > resource ) ;
# endif
2017-11-21 21:34:56 -05:00
if ( resource_being_loaded ) {
2018-09-28 11:13:39 -04:00
struct ast_str * list ;
int c = 0 ;
2019-07-29 07:31:56 -06:00
const char * dlerror_msg = ast_strdupa ( S_OR ( dlerror ( ) , " " ) ) ;
2018-09-28 11:13:39 -04:00
2017-11-21 21:34:56 -05:00
resource_being_loaded = NULL ;
2017-11-21 00:10:58 -05:00
if ( mod - > lib ) {
2018-09-28 11:13:39 -04:00
module_load_error ( " Module '%s' did not register itself during load \n " , resource_in ) ;
2017-11-21 00:10:58 -05:00
logged_dlclose ( resource_in , mod - > lib ) ;
2018-09-28 11:13:39 -04:00
goto error_return ;
}
if ( suppress_logging ) {
goto error_return ;
2016-01-23 14:50:57 -07:00
}
2018-09-28 11:13:39 -04:00
resource_being_loaded = mod ;
mod - > lib = dlopen ( filename , RTLD_LAZY | RTLD_LOCAL ) ;
2019-04-02 22:49:52 +02:00
# if defined(HAVE_PERMANENT_DLOPEN)
manual_mod_reg ( mod - > lib , mod - > resource ) ;
# endif
2018-09-28 11:13:39 -04:00
if ( resource_being_loaded ) {
resource_being_loaded = NULL ;
module_load_error ( " Error loading module '%s': %s \n " , resource_in , dlerror_msg ) ;
logged_dlclose ( resource_in , mod - > lib ) ;
goto error_return ;
}
list = ast_str_create ( 64 ) ;
if ( list ) {
if ( module_post_register ( mod ) ) {
goto loaded_error ;
}
c = load_dlopen_missing ( & list , & mod - > requires ) ;
c + = load_dlopen_missing ( & list , & mod - > enhances ) ;
# ifndef OPTIONAL_API
c + = load_dlopen_missing ( & list , & mod - > optional_modules ) ;
# endif
}
if ( list & & ast_str_strlen ( list ) ) {
module_load_error ( " Error loading module '%s', missing %s: %s \n " ,
resource_in , c = = 1 ? " dependency " : " dependencies " , ast_str_buffer ( list ) ) ;
} else {
module_load_error ( " Error loading module '%s': %s \n " , resource_in , dlerror_msg ) ;
}
loaded_error :
ast_free ( list ) ;
unload_dynamic_module ( mod ) ;
return NULL ;
error_return :
2017-11-21 21:34:56 -05:00
ast_free ( mod ) ;
2017-11-21 00:10:58 -05:00
2006-08-21 02:11:39 +00:00
return NULL ;
}
2017-11-21 00:10:58 -05:00
return mod ;
}
2006-08-21 14:42:03 +00:00
2017-12-09 00:30:43 -05:00
static struct ast_module * load_dynamic_module ( const char * resource_in , unsigned int suppress_logging )
2017-11-21 00:10:58 -05:00
{
char fn [ PATH_MAX ] ;
struct ast_module * mod ;
size_t resource_in_len = strlen ( resource_in ) ;
const char * so_ext = " " ;
if ( resource_in_len < 4 | | strcasecmp ( resource_in + resource_in_len - 3 , " .so " ) ) {
so_ext = " .so " ;
2006-08-23 19:28:13 +00:00
}
2017-11-21 00:10:58 -05:00
snprintf ( fn , sizeof ( fn ) , " %s/%s%s " , ast_config_AST_MODULE_DIR , resource_in , so_ext ) ;
2006-08-21 02:11:39 +00:00
2017-12-09 00:30:43 -05:00
/* Try loading in quiet mode first with RTLD_LOCAL. The majority of modules do not
* export symbols so this allows the least number of calls to dlopen. */
mod = load_dlopen ( resource_in , so_ext , fn , RTLD_NOW | RTLD_LOCAL , suppress_logging ) ;
2006-08-21 14:42:03 +00:00
2017-12-09 00:30:43 -05:00
if ( ! mod | | ! ast_test_flag ( mod - > info , AST_MODFLAG_GLOBAL_SYMBOLS ) ) {
2017-11-21 00:10:58 -05:00
return mod ;
}
2006-08-23 19:28:13 +00:00
2017-11-21 00:10:58 -05:00
/* Close the module so we can reopen with correct flags. */
logged_dlclose ( resource_in , mod - > lib ) ;
2006-08-21 14:42:03 +00:00
2017-12-09 00:30:43 -05:00
return load_dlopen ( resource_in , so_ext , fn , RTLD_NOW | RTLD_GLOBAL , 0 ) ;
2006-08-21 02:11:39 +00:00
}
2013-08-30 13:40:27 +00:00
2016-10-27 22:49:43 -04:00
int modules_shutdown ( void )
2007-06-05 15:54:36 +00:00
{
struct ast_module * mod ;
2018-01-17 01:28:57 -05:00
int somethingchanged ;
int res ;
2007-06-05 15:54:36 +00:00
2014-07-25 14:27:52 +00:00
AST_DLLIST_LOCK ( & module_list ) ;
2007-06-05 15:54:36 +00:00
2009-11-09 07:37:52 +00:00
/*!\note Some resources, like timers, are started up dynamically, and thus
* may be still in use, even if all channels are dead. We must therefore
* check the usecount before asking modules to unload. */
do {
/* Reset flag before traversing the list */
somethingchanged = 0 ;
2014-07-25 14:27:52 +00:00
AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN ( & module_list , mod , entry ) {
2018-01-17 01:28:57 -05:00
if ( mod - > usecount ) {
2014-07-25 14:27:52 +00:00
ast_debug ( 1 , " Passing on %s: its use count is %d \n " ,
mod - > resource , mod - > usecount ) ;
2009-11-09 07:37:52 +00:00
continue ;
}
2014-07-25 14:27:52 +00:00
AST_DLLIST_REMOVE_CURRENT ( entry ) ;
2010-07-09 17:50:45 +00:00
if ( mod - > flags . running & & ! mod - > flags . declined & & mod - > info - > unload ) {
2024-01-31 10:46:28 -07:00
ast_verb ( 4 , " Unloading %s \n " , mod - > resource ) ;
2009-11-09 07:37:52 +00:00
mod - > info - > unload ( ) ;
}
2017-11-21 21:34:56 -05:00
module_destroy ( mod ) ;
2009-11-09 07:37:52 +00:00
somethingchanged = 1 ;
}
2014-07-25 14:27:52 +00:00
AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END ;
2015-02-11 17:03:04 +00:00
if ( ! somethingchanged ) {
AST_DLLIST_TRAVERSE ( & module_list , mod , entry ) {
if ( mod - > flags . keepuntilshutdown ) {
ast_module_unref ( mod ) ;
mod - > flags . keepuntilshutdown = 0 ;
somethingchanged = 1 ;
}
}
}
2018-01-17 01:28:57 -05:00
} while ( somethingchanged ) ;
2009-11-09 07:37:52 +00:00
2018-01-17 01:28:57 -05:00
res = AST_DLLIST_EMPTY ( & module_list ) ;
2014-07-25 14:27:52 +00:00
AST_DLLIST_UNLOCK ( & module_list ) ;
2016-10-27 22:49:43 -04:00
2018-01-17 01:28:57 -05:00
return ! res ;
2007-06-05 15:54:36 +00:00
}
2023-12-02 18:07:02 -05:00
/*!
* \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 )
2006-08-21 02:11:39 +00:00
{
struct ast_module * mod ;
int res = - 1 ;
int error = 0 ;
2014-07-25 14:27:52 +00:00
AST_DLLIST_LOCK ( & module_list ) ;
2006-08-21 02:11:39 +00:00
2006-08-22 23:06:13 +00:00
if ( ! ( mod = find_resource ( resource_name , 0 ) ) ) {
2014-07-25 14:27:52 +00:00
AST_DLLIST_UNLOCK ( & module_list ) ;
2008-02-19 21:54:09 +00:00
ast_log ( LOG_WARNING , " Unload failed, '%s' could not be found \n " , resource_name ) ;
2010-05-12 19:59:16 +00:00
return - 1 ;
2006-08-22 23:06:13 +00:00
}
2006-08-21 02:11:39 +00:00
2010-07-09 18:24:03 +00:00
if ( ! mod - > flags . running | | mod - > flags . declined ) {
2022-12-08 21:44:25 +00:00
/* If the user asks to unload a module that didn't load, obey.
* Otherwise, we never dlclose() modules that fail to load,
* which means if the module (shared object) is updated,
* we can't load the updated module since we never dlclose()'d it.
* Accordingly, obey the unload request so we can load the module
* from scratch next time.
*/
ast_log ( LOG_NOTICE , " Unloading module '%s' that previously declined to load \n " , resource_name ) ;
error = 0 ;
res = 0 ;
goto exit ; /* Skip all the intervening !error checks, only the last one is relevant. */
2010-07-09 18:24:03 +00:00
}
2006-08-21 02:11:39 +00:00
2023-12-02 18:07:02 -05:00
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. */
}
}
2006-08-21 02:11:39 +00:00
if ( ! error & & ( mod - > usecount > 0 ) ) {
2023-12-02 18:07:02 -05:00
if ( force ) {
ast_log ( LOG_WARNING , " Warning: Forcing removal of module '%s' with use count %d \n " , resource_name , mod - > usecount ) ;
} else {
ast_log ( LOG_WARNING , " %s unload failed, '%s' has use count %d \n " , recursive ? " Recursive soft " : " Soft " , resource_name , mod - > usecount ) ;
2006-08-21 02:11:39 +00:00
error = 1 ;
}
}
if ( ! error ) {
2012-12-17 23:10:42 +00:00
/* Request any channels attached to the module to hangup. */
2006-08-21 02:11:39 +00:00
__ast_module_user_hangup_all ( mod ) ;
2024-01-31 10:46:28 -07:00
ast_verb ( 4 , " Unloading %s \n " , mod - > resource ) ;
2012-12-17 23:10:42 +00:00
res = mod - > info - > unload ( ) ;
2006-08-21 02:11:39 +00:00
if ( res ) {
ast_log ( LOG_WARNING , " Firm unload failed for %s \n " , resource_name ) ;
2012-12-17 23:10:42 +00:00
if ( force < = AST_FORCE_FIRM ) {
2006-08-21 02:11:39 +00:00
error = 1 ;
2012-12-17 23:10:42 +00:00
} else {
2006-08-21 02:11:39 +00:00
ast_log ( LOG_WARNING , " ** Dangerous **: Unloading resource anyway, at user request \n " ) ;
2012-12-17 23:10:42 +00:00
}
}
if ( ! error ) {
/*
* Request hangup on any channels that managed to get attached
* while we called the module unload function.
*/
__ast_module_user_hangup_all ( mod ) ;
sched_yield ( ) ;
2006-08-21 02:11:39 +00:00
}
}
if ( ! error )
2007-05-03 16:43:49 +00:00
mod - > flags . running = mod - > flags . declined = 0 ;
2006-08-21 02:11:39 +00:00
2022-12-08 21:44:25 +00:00
exit :
2014-07-25 14:27:52 +00:00
AST_DLLIST_UNLOCK ( & module_list ) ;
2006-08-21 02:11:39 +00:00
2012-08-13 20:36:51 +00:00
if ( ! error ) {
2006-08-21 02:11:39 +00:00
unload_dynamic_module ( mod ) ;
2012-08-13 20:36:51 +00:00
ast_test_suite_event_notify ( " MODULE_UNLOAD " , " Message: %s " , resource_name ) ;
2006-08-21 02:11:39 +00:00
ast_update_use_count ( ) ;
2018-02-06 21:27:55 +01:00
publish_unload_message ( resource_name , " Success " ) ;
2017-03-24 08:43:05 -04:00
}
2006-08-21 02:11:39 +00:00
return res ;
}
2023-12-02 18:07:02 -05:00
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 ) ;
}
2017-10-30 18:30:18 -04:00
static int module_matches_helper_type ( struct ast_module * mod , enum ast_module_helper_type type )
2006-08-21 02:11:39 +00:00
{
2017-10-30 18:30:18 -04:00
switch ( type ) {
case AST_MODULE_HELPER_UNLOAD :
return ! mod - > usecount & & mod - > flags . running & & ! mod - > flags . declined ;
2006-08-21 02:11:39 +00:00
2017-10-30 18:30:18 -04:00
case AST_MODULE_HELPER_RELOAD :
return mod - > flags . running & & mod - > info - > reload ;
case AST_MODULE_HELPER_RUNNING :
return mod - > flags . running ;
case AST_MODULE_HELPER_LOADED :
/* if we have a 'struct ast_module' then we're loaded. */
return 1 ;
default :
/* This function is not called for AST_MODULE_HELPER_LOAD. */
/* Unknown ast_module_helper_type. Assume it doesn't match. */
ast_assert ( 0 ) ;
return 0 ;
2017-10-30 01:32:32 -04:00
}
2017-10-30 18:30:18 -04:00
}
2018-01-25 13:06:12 -05:00
struct module_load_word {
const char * word ;
size_t len ;
size_t moddir_len ;
} ;
static int module_load_helper_on_file ( const char * dir_name , const char * filename , void * obj )
2017-10-30 18:30:18 -04:00
{
2018-01-25 13:06:12 -05:00
struct module_load_word * word = obj ;
2017-10-30 18:30:18 -04:00
struct ast_module * mod ;
2018-01-25 13:06:12 -05:00
char * filename_merged = NULL ;
2017-10-30 18:30:18 -04:00
2018-01-25 13:06:12 -05:00
/* dir_name will never be shorter than word->moddir_len. */
dir_name + = word - > moddir_len ;
if ( ! ast_strlen_zero ( dir_name ) ) {
ast_assert ( dir_name [ 0 ] = = ' / ' ) ;
2006-08-21 02:11:39 +00:00
2018-01-25 13:06:12 -05:00
dir_name + = 1 ;
if ( ast_asprintf ( & filename_merged , " %s/%s " , dir_name , filename ) < 0 ) {
/* If we can't allocate the string just give up! */
return - 1 ;
2006-08-21 02:11:39 +00:00
}
2018-01-25 13:06:12 -05:00
filename = filename_merged ;
2006-08-21 02:11:39 +00:00
}
2017-10-30 18:30:18 -04:00
2018-01-25 13:06:12 -05:00
if ( ! strncasecmp ( filename , word - > word , word - > len ) ) {
/* Don't list files that are already loaded! */
mod = find_resource ( filename , 0 ) ;
if ( ! mod | | ! mod - > flags . running ) {
ast_cli_completion_add ( ast_strdup ( filename ) ) ;
}
2017-10-30 18:30:18 -04:00
}
2018-01-25 13:06:12 -05:00
ast_free ( filename_merged ) ;
2017-10-30 18:30:18 -04:00
2018-01-25 13:06:12 -05:00
return 0 ;
}
2017-10-30 18:30:18 -04:00
2018-01-25 13:06:12 -05:00
static void module_load_helper ( const char * word )
{
struct module_load_word word_l = {
. word = word ,
. len = strlen ( word ) ,
. moddir_len = strlen ( ast_config_AST_MODULE_DIR ) ,
} ;
2017-10-30 18:30:18 -04:00
2018-01-25 13:06:12 -05:00
AST_DLLIST_LOCK ( & module_list ) ;
ast_file_read_dirs ( ast_config_AST_MODULE_DIR , module_load_helper_on_file , & word_l , - 1 ) ;
2014-07-25 14:27:52 +00:00
AST_DLLIST_UNLOCK ( & module_list ) ;
2017-10-30 18:30:18 -04:00
}
char * ast_module_helper ( const char * line , const char * word , int pos , int state , int rpos , enum ast_module_helper_type type )
{
struct ast_module * mod ;
int which = 0 ;
int wordlen = strlen ( word ) ;
char * ret = NULL ;
if ( pos ! = rpos ) {
return NULL ;
}
2022-05-03 12:44:07 +00:00
/* Tab completion can't be used during startup, or CLI and loader will deadlock. */
2025-07-21 13:12:40 -06:00
if ( ! ast_fully_booted ) {
2022-05-03 12:44:07 +00:00
return NULL ;
}
2017-10-30 18:30:18 -04:00
if ( type = = AST_MODULE_HELPER_LOAD ) {
2018-01-25 13:06:12 -05:00
module_load_helper ( word ) ;
return NULL ;
2017-10-30 18:30:18 -04:00
}
AST_DLLIST_LOCK ( & module_list ) ;
AST_DLLIST_TRAVERSE ( & module_list , mod , entry ) {
if ( ! module_matches_helper_type ( mod , type ) ) {
continue ;
}
if ( ! strncasecmp ( word , mod - > resource , wordlen ) & & + + which > state ) {
ret = ast_strdup ( mod - > resource ) ;
break ;
}
}
AST_DLLIST_UNLOCK ( & module_list ) ;
2006-08-21 02:11:39 +00:00
return ret ;
}
2009-06-04 14:31:24 +00:00
void ast_process_pending_reloads ( void )
{
struct reload_queue_item * item ;
2015-04-09 23:08:10 +00:00
modules_loaded = 1 ;
2009-06-04 14:31:24 +00:00
AST_LIST_LOCK ( & reload_queue ) ;
if ( do_full_reload ) {
do_full_reload = 0 ;
AST_LIST_UNLOCK ( & reload_queue ) ;
ast_log ( LOG_NOTICE , " Executing deferred reload request. \n " ) ;
ast_module_reload ( NULL ) ;
return ;
}
while ( ( item = AST_LIST_REMOVE_HEAD ( & reload_queue , entry ) ) ) {
ast_log ( LOG_NOTICE , " Executing deferred reload request for module '%s'. \n " , item - > module ) ;
ast_module_reload ( item - > module ) ;
ast_free ( item ) ;
}
AST_LIST_UNLOCK ( & reload_queue ) ;
}
static void queue_reload_request ( const char * module )
{
struct reload_queue_item * item ;
AST_LIST_LOCK ( & reload_queue ) ;
if ( do_full_reload ) {
AST_LIST_UNLOCK ( & reload_queue ) ;
return ;
}
if ( ast_strlen_zero ( module ) ) {
/* A full reload request (when module is NULL) wipes out any previous
reload requests and causes the queue to ignore future ones */
while ( ( item = AST_LIST_REMOVE_HEAD ( & reload_queue , entry ) ) ) {
ast_free ( item ) ;
}
do_full_reload = 1 ;
} else {
/* No reason to add the same module twice */
AST_LIST_TRAVERSE ( & reload_queue , item , entry ) {
if ( ! strcasecmp ( item - > module , module ) ) {
AST_LIST_UNLOCK ( & reload_queue ) ;
return ;
}
}
item = ast_calloc ( 1 , sizeof ( * item ) + strlen ( module ) + 1 ) ;
if ( ! item ) {
ast_log ( LOG_ERROR , " Failed to allocate reload queue item. \n " ) ;
AST_LIST_UNLOCK ( & reload_queue ) ;
return ;
}
strcpy ( item - > module , module ) ;
AST_LIST_INSERT_TAIL ( & reload_queue , item , entry ) ;
}
AST_LIST_UNLOCK ( & reload_queue ) ;
}
2013-05-24 20:44:07 +00:00
/*!
* \since 12
* \internal
2018-02-06 21:27:55 +01:00
* \brief Publish a \ref stasis message regarding the type.
2013-05-24 20:44:07 +00:00
*/
2018-02-06 21:27:55 +01:00
static void publish_load_message_type ( const char * type , const char * name , const char * status )
2013-05-24 20:44:07 +00:00
{
RAII_VAR ( struct stasis_message * , message , NULL , ao2_cleanup ) ;
RAII_VAR ( struct ast_json_payload * , payload , NULL , ao2_cleanup ) ;
RAII_VAR ( struct ast_json * , json_object , NULL , ast_json_unref ) ;
RAII_VAR ( struct ast_json * , event_object , NULL , ast_json_unref ) ;
2018-02-06 21:27:55 +01:00
ast_assert ( type ! = NULL ) ;
ast_assert ( ! ast_strlen_zero ( name ) ) ;
ast_assert ( ! ast_strlen_zero ( status ) ) ;
2013-05-24 20:44:07 +00:00
2014-08-06 12:55:28 +00:00
if ( ! ast_manager_get_generic_type ( ) ) {
return ;
}
2018-02-06 21:27:55 +01:00
event_object = ast_json_pack ( " {s:s, s:s} " ,
" Module " , name ,
" Status " , status ) ;
json_object = ast_json_pack ( " {s:s, s:i, s:o} " ,
" type " , type ,
2013-05-24 20:44:07 +00:00
" class_type " , EVENT_FLAG_SYSTEM ,
2015-12-14 14:04:15 -04:00
" event " , ast_json_ref ( event_object ) ) ;
2013-05-24 20:44:07 +00:00
if ( ! json_object ) {
return ;
}
payload = ast_json_payload_create ( json_object ) ;
if ( ! payload ) {
return ;
}
message = stasis_message_create ( ast_manager_get_generic_type ( ) , payload ) ;
if ( ! message ) {
return ;
}
stasis_publish ( ast_manager_get_topic ( ) , message ) ;
}
2018-02-06 21:27:55 +01:00
static const char * loadresult2str ( enum ast_module_load_result result )
{
int i ;
for ( i = 0 ; i < ARRAY_LEN ( load_results ) ; i + + ) {
if ( load_results [ i ] . result = = result ) {
return load_results [ i ] . name ;
}
}
ast_log ( LOG_WARNING , " Failed to find correct load result status. result %d \n " , result ) ;
return AST_MODULE_LOAD_UNKNOWN_STRING ;
}
/*!
* \internal
* \brief Publish a \ref stasis message regarding the load result
*/
static void publish_load_message ( const char * name , enum ast_module_load_result result )
{
const char * status ;
status = loadresult2str ( result ) ;
publish_load_message_type ( " Load " , name , status ) ;
}
/*!
* \internal
* \brief Publish a \ref stasis message regarding the unload result
*/
static void publish_unload_message ( const char * name , const char * status )
{
publish_load_message_type ( " Unload " , name , status ) ;
}
/*!
* \since 12
* \internal
* \brief Publish a \ref stasis message regarding the reload result
*/
static void publish_reload_message ( const char * name , enum ast_module_reload_result result )
{
char res_buffer [ 8 ] ;
snprintf ( res_buffer , sizeof ( res_buffer ) , " %u " , result ) ;
publish_load_message_type ( " Reload " , S_OR ( name , " All " ) , res_buffer ) ;
}
2013-05-24 20:44:07 +00:00
enum ast_module_reload_result ast_module_reload ( const char * name )
2006-08-21 02:11:39 +00:00
{
struct ast_module * cur ;
2013-05-24 20:44:07 +00:00
enum ast_module_reload_result res = AST_MODULE_RELOAD_NOT_FOUND ;
2017-12-08 19:19:34 -05:00
size_t name_baselen = name ? resource_name_baselen ( name ) : 0 ;
2006-08-21 02:11:39 +00:00
2009-06-04 14:31:24 +00:00
/* If we aren't fully booted, we just pretend we reloaded but we queue this
up to run once we are booted up. */
2015-04-09 23:08:10 +00:00
if ( ! modules_loaded ) {
2009-06-04 14:31:24 +00:00
queue_reload_request ( name ) ;
2013-05-24 20:44:07 +00:00
res = AST_MODULE_RELOAD_QUEUED ;
goto module_reload_exit ;
2009-06-04 14:31:24 +00:00
}
2006-08-21 02:11:39 +00:00
if ( ast_mutex_trylock ( & reloadlock ) ) {
2014-05-28 22:54:12 +00:00
ast_verb ( 3 , " The previous reload command didn't finish yet \n " ) ;
2013-05-24 20:44:07 +00:00
res = AST_MODULE_RELOAD_IN_PROGRESS ;
goto module_reload_exit ;
2006-08-21 02:11:39 +00:00
}
2024-05-05 14:53:11 +02:00
ast_sd_notify ( " RELOADING=1 " ) ;
2007-07-18 19:47:20 +00:00
ast_lastreloadtime = ast_tvnow ( ) ;
2006-08-21 02:11:39 +00:00
2010-01-27 18:29:49 +00:00
if ( ast_opt_lock_confdir ) {
int try ;
2018-10-02 02:33:44 -04:00
int lockres ;
for ( try = 1 , lockres = AST_LOCK_TIMEOUT ; try < 6 & & ( lockres = = AST_LOCK_TIMEOUT ) ; try + + ) {
lockres = ast_lock_path ( ast_config_AST_CONFIG_DIR ) ;
if ( lockres = = AST_LOCK_TIMEOUT ) {
2010-01-27 18:29:49 +00:00
ast_log ( LOG_WARNING , " Failed to grab lock on %s, try %d \n " , ast_config_AST_CONFIG_DIR , try ) ;
}
}
2018-10-02 02:33:44 -04:00
if ( lockres ! = AST_LOCK_SUCCESS ) {
2014-05-28 22:54:12 +00:00
ast_log ( AST_LOG_WARNING , " Cannot grab lock on %s \n " , ast_config_AST_CONFIG_DIR ) ;
2013-05-24 20:44:07 +00:00
res = AST_MODULE_RELOAD_ERROR ;
2016-06-27 21:26:54 +02:00
goto module_reload_done ;
2010-01-27 18:29:49 +00:00
}
}
2014-07-25 14:27:52 +00:00
AST_DLLIST_LOCK ( & module_list ) ;
AST_DLLIST_TRAVERSE ( & module_list , cur , entry ) {
2006-08-21 02:11:39 +00:00
const struct ast_module_info * info = cur - > info ;
2017-12-08 19:19:34 -05:00
if ( name & & resource_name_match ( name , name_baselen , cur - > resource ) ) {
2006-08-21 02:11:39 +00:00
continue ;
2017-12-08 19:19:34 -05:00
}
2006-08-21 02:11:39 +00:00
2008-02-15 19:50:47 +00:00
if ( ! cur - > flags . running | | cur - > flags . declined ) {
2013-05-24 20:44:07 +00:00
if ( res = = AST_MODULE_RELOAD_NOT_FOUND ) {
res = AST_MODULE_RELOAD_UNINITIALIZED ;
}
if ( ! name ) {
2008-02-15 19:50:47 +00:00
continue ;
2013-05-24 20:44:07 +00:00
}
2008-02-15 19:50:47 +00:00
break ;
}
2006-08-21 02:11:39 +00:00
if ( ! info - > reload ) { /* cannot be reloaded */
2013-05-24 20:44:07 +00:00
if ( res = = AST_MODULE_RELOAD_NOT_FOUND ) {
res = AST_MODULE_RELOAD_NOT_IMPLEMENTED ;
}
if ( ! name ) {
continue ;
}
break ;
2006-08-21 02:11:39 +00:00
}
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Reloading module '%s' (%s) \n " , cur - > resource , info - > description ) ;
2013-05-24 20:44:07 +00:00
if ( info - > reload ( ) = = AST_MODULE_LOAD_SUCCESS ) {
res = AST_MODULE_RELOAD_SUCCESS ;
2018-10-02 02:33:44 -04:00
} else if ( res = = AST_MODULE_RELOAD_NOT_FOUND ) {
res = AST_MODULE_RELOAD_ERROR ;
2013-05-24 20:44:07 +00:00
}
if ( name ) {
break ;
2012-08-16 22:45:33 +00:00
}
2006-08-21 02:11:39 +00:00
}
2014-07-25 14:27:52 +00:00
AST_DLLIST_UNLOCK ( & module_list ) ;
2006-08-21 02:11:39 +00:00
2010-01-27 18:29:49 +00:00
if ( ast_opt_lock_confdir ) {
ast_unlock_path ( ast_config_AST_CONFIG_DIR ) ;
}
2016-06-27 21:26:54 +02:00
module_reload_done :
2006-08-21 02:11:39 +00:00
ast_mutex_unlock ( & reloadlock ) ;
2016-06-27 21:26:54 +02:00
ast_sd_notify ( " READY=1 " ) ;
2006-08-21 02:11:39 +00:00
2013-05-24 20:44:07 +00:00
module_reload_exit :
publish_reload_message ( name , res ) ;
2006-08-21 02:11:39 +00:00
return res ;
}
static unsigned int inspect_module ( const struct ast_module * mod )
{
if ( ! mod - > info - > description ) {
2018-09-28 11:13:39 -04:00
module_load_error ( " Module '%s' does not provide a description. \n " , mod - > resource ) ;
2006-08-21 02:11:39 +00:00
return 1 ;
}
if ( ! mod - > info - > key ) {
2018-09-28 11:13:39 -04:00
module_load_error ( " Module '%s' does not provide a license key. \n " , mod - > resource ) ;
2006-08-21 02:11:39 +00:00
return 1 ;
}
if ( verify_key ( ( unsigned char * ) mod - > info - > key ) ) {
2018-09-28 11:13:39 -04:00
module_load_error ( " Module '%s' did not provide a valid license key. \n " , mod - > resource ) ;
2006-08-21 02:11:39 +00:00
return 1 ;
}
2007-11-20 19:28:10 +00:00
if ( ! ast_strlen_zero ( mod - > info - > buildopt_sum ) & &
strcmp ( buildopt_sum , mod - > info - > buildopt_sum ) ) {
2018-09-28 11:13:39 -04:00
module_load_error ( " Module '%s' was not compiled with the same compile-time options as this version of Asterisk. \n " , mod - > resource ) ;
module_load_error ( " Module '%s' will not be initialized as it may cause instability. \n " , mod - > resource ) ;
2007-11-16 16:56:59 +00:00
return 1 ;
}
2006-08-21 02:11:39 +00:00
return 0 ;
}
2009-06-09 16:22:04 +00:00
static enum ast_module_load_result start_resource ( struct ast_module * mod )
{
char tmp [ 256 ] ;
enum ast_module_load_result res ;
2012-09-27 16:53:19 +00:00
if ( mod - > flags . running ) {
return AST_MODULE_LOAD_SUCCESS ;
}
2009-06-09 16:22:04 +00:00
if ( ! mod - > info - > load ) {
2018-10-04 19:33:25 -04:00
mod - > flags . declined = 1 ;
return mod - > flags . required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE ;
2009-06-09 16:22:04 +00:00
}
2017-11-22 00:39:33 -05:00
if ( module_deps_reference ( mod , NULL ) ) {
struct module_vector missing ;
int i ;
AST_VECTOR_INIT ( & missing , 0 ) ;
if ( module_deps_missing_recursive ( mod , & missing ) ) {
2018-09-28 11:13:39 -04:00
module_load_error ( " %s has one or more unknown dependencies. \n " , mod - > info - > name ) ;
2017-11-22 00:39:33 -05:00
}
for ( i = 0 ; i < AST_VECTOR_SIZE ( & missing ) ; i + + ) {
2018-09-28 11:13:39 -04:00
module_load_error ( " %s loaded before dependency %s! \n " , mod - > info - > name ,
2017-11-22 00:39:33 -05:00
AST_VECTOR_GET ( & missing , i ) - > info - > name ) ;
}
AST_VECTOR_FREE ( & missing ) ;
2018-01-24 23:44:09 -05:00
return AST_MODULE_LOAD_DECLINE ;
2017-11-22 00:39:33 -05:00
}
2013-04-26 21:31:39 +00:00
if ( ! ast_fully_booted ) {
2024-01-31 10:46:28 -07:00
ast_verb ( 4 , " Loading %s. \n " , mod - > resource ) ;
2013-04-26 21:31:39 +00:00
}
2009-06-09 16:22:04 +00:00
res = mod - > info - > load ( ) ;
switch ( res ) {
case AST_MODULE_LOAD_SUCCESS :
if ( ! ast_fully_booted ) {
2024-01-31 10:46:28 -07:00
ast_verb ( 5 , " %s => (%s) \n " , mod - > resource , term_color ( tmp , mod - > info - > description , COLOR_BROWN , COLOR_BLACK , sizeof ( tmp ) ) ) ;
2009-06-09 16:22:04 +00:00
} else {
2024-01-31 10:46:28 -07:00
ast_verb ( 4 , " Loaded %s => (%s) \n " , mod - > resource , mod - > info - > description ) ;
2009-06-09 16:22:04 +00:00
}
mod - > flags . running = 1 ;
2018-02-16 22:11:42 -05:00
if ( mod - > flags . builtin ) {
/* Built-in modules cannot be unloaded. */
ast_module_shutdown_ref ( mod ) ;
}
2009-06-09 16:22:04 +00:00
ast_update_use_count ( ) ;
break ;
case AST_MODULE_LOAD_DECLINE :
mod - > flags . declined = 1 ;
2018-07-15 14:58:15 -04:00
if ( mod - > flags . required ) {
res = AST_MODULE_LOAD_FAILURE ;
}
2009-06-09 16:22:04 +00:00
break ;
case AST_MODULE_LOAD_FAILURE :
2018-10-04 19:33:25 -04:00
mod - > flags . declined = 1 ;
break ;
2009-06-22 15:33:35 +00:00
case AST_MODULE_LOAD_SKIP : /* modules should never return this value */
case AST_MODULE_LOAD_PRIORITY :
2009-06-09 16:22:04 +00:00
break ;
}
2014-07-25 14:27:52 +00:00
/* Make sure the newly started module is at the end of the list */
AST_DLLIST_LOCK ( & module_list ) ;
AST_DLLIST_REMOVE ( & module_list , mod , entry ) ;
AST_DLLIST_INSERT_TAIL ( & module_list , mod , entry ) ;
AST_DLLIST_UNLOCK ( & module_list ) ;
2009-06-09 16:22:04 +00:00
return res ;
}
/*! loads a resource based upon resource_name. If global_symbols_only is set
* only modules with global symbols will be loaded.
*
2017-11-21 01:28:48 -05:00
* If the module_vector is provided (not NULL) the module is found and added to the
* vector without running the module's load() function. By doing this, modules
* can be initialized later in order by priority and dependencies.
2009-06-09 16:22:04 +00:00
*
2017-11-21 01:28:48 -05:00
* If the module_vector is not provided, the module's load function will be executed
2009-06-09 16:22:04 +00:00
* immediately */
2018-07-15 14:58:15 -04:00
static enum ast_module_load_result load_resource ( const char * resource_name , unsigned int suppress_logging ,
struct module_vector * module_priorities , int required , int preload )
2006-08-21 02:11:39 +00:00
{
struct ast_module * mod ;
enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS ;
if ( ( mod = find_resource ( resource_name , 0 ) ) ) {
2007-05-03 16:43:49 +00:00
if ( mod - > flags . running ) {
2018-01-17 01:28:57 -05:00
ast_log ( LOG_WARNING , " Module '%s' already loaded and running. \n " , resource_name ) ;
2006-08-21 02:11:39 +00:00
return AST_MODULE_LOAD_DECLINE ;
}
} else {
2017-12-09 00:30:43 -05:00
mod = load_dynamic_module ( resource_name , suppress_logging ) ;
2016-01-23 14:50:57 -07:00
if ( ! mod ) {
return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE ;
2006-08-21 02:11:39 +00:00
}
2017-11-22 00:39:33 -05:00
2018-07-26 13:52:46 -04:00
if ( module_post_register ( mod ) ) {
2018-02-06 21:27:55 +01:00
goto prestart_error ;
2017-11-22 00:39:33 -05:00
}
2006-08-21 02:11:39 +00:00
}
2018-07-15 14:58:15 -04:00
mod - > flags . required | = required ;
mod - > flags . preload | = preload ;
2006-08-21 02:11:39 +00:00
if ( inspect_module ( mod ) ) {
2017-11-21 01:28:48 -05:00
goto prestart_error ;
2007-02-22 02:36:00 +00:00
}
2007-05-03 16:43:49 +00:00
mod - > flags . declined = 0 ;
2006-08-21 02:11:39 +00:00
2017-12-09 01:03:15 -05:00
if ( module_priorities ) {
if ( AST_VECTOR_ADD_SORTED ( module_priorities , mod , module_vector_cmp ) ) {
2017-11-21 01:28:48 -05:00
goto prestart_error ;
}
2009-06-22 15:33:35 +00:00
res = AST_MODULE_LOAD_PRIORITY ;
2009-06-09 16:22:04 +00:00
} else {
res = start_resource ( mod ) ;
2006-08-21 02:11:39 +00:00
}
2018-02-06 21:27:55 +01:00
if ( ast_fully_booted & & ! ast_shutdown_final ( ) ) {
publish_load_message ( resource_name , res ) ;
}
2006-08-21 02:11:39 +00:00
return res ;
2017-11-21 01:28:48 -05:00
prestart_error :
2018-09-28 11:13:39 -04:00
module_load_error ( " Module '%s' could not be loaded. \n " , resource_name ) ;
2017-11-21 01:28:48 -05:00
unload_dynamic_module ( mod ) ;
2018-02-06 21:27:55 +01:00
res = required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE ;
if ( ast_fully_booted & & ! ast_shutdown_final ( ) ) {
publish_load_message ( resource_name , res ) ;
}
return res ;
2006-08-21 02:11:39 +00:00
}
2023-06-09 08:41:32 -06:00
enum ast_module_load_result ast_load_resource ( const char * resource_name )
2006-08-21 02:11:39 +00:00
{
2022-12-08 21:44:25 +00:00
struct ast_module * mod ;
2023-06-09 08:41:32 -06:00
enum ast_module_load_result res ;
2022-12-08 21:44:25 +00:00
/* If we're trying to load a module that previously declined to load,
* transparently unload it first so we dlclose, then dlopen it afresh.
* Otherwise, we won't actually load a (potentially) updated module. */
mod = find_resource ( resource_name , 0 ) ;
if ( mod & & mod - > flags . declined ) {
ast_debug ( 1 , " Module %s previously declined to load, unloading it first before loading again \n " , resource_name ) ;
ast_unload_resource ( resource_name , 0 ) ;
}
2014-07-25 14:27:52 +00:00
AST_DLLIST_LOCK ( & module_list ) ;
2018-07-15 14:58:15 -04:00
res = load_resource ( resource_name , 0 , NULL , 0 , 0 ) ;
2012-08-16 22:45:33 +00:00
if ( ! res ) {
ast_test_suite_event_notify ( " MODULE_LOAD " , " Message: %s " , resource_name ) ;
}
2014-07-25 14:27:52 +00:00
AST_DLLIST_UNLOCK ( & module_list ) ;
2006-08-21 02:11:39 +00:00
2008-06-05 15:58:11 +00:00
return res ;
2006-08-21 02:11:39 +00:00
}
struct load_order_entry {
char * resource ;
2009-11-13 08:52:28 +00:00
int required ;
2018-07-15 14:58:15 -04:00
int preload ;
int builtin ;
2006-08-21 02:11:39 +00:00
AST_LIST_ENTRY ( load_order_entry ) entry ;
} ;
AST_LIST_HEAD_NOLOCK ( load_order , load_order_entry ) ;
2018-07-15 14:58:15 -04:00
static struct load_order_entry * add_to_load_order ( const char * resource , struct load_order * load_order , int required , int preload , int builtin )
2006-08-21 02:11:39 +00:00
{
struct load_order_entry * order ;
2017-12-08 19:19:34 -05:00
size_t resource_baselen = resource_name_baselen ( resource ) ;
2006-08-21 02:11:39 +00:00
AST_LIST_TRAVERSE ( load_order , order , entry ) {
2017-12-08 19:19:34 -05:00
if ( ! resource_name_match ( resource , resource_baselen , order - > resource ) ) {
2012-03-22 19:51:16 +00:00
/* Make sure we have the proper setting for the required field
2009-11-13 08:52:28 +00:00
(we might have both load= and required= lines in modules.conf) */
2009-11-13 10:53:14 +00:00
order - > required | = required ;
2018-07-15 14:58:15 -04:00
order - > preload | = preload ;
return order ;
2009-11-13 08:52:28 +00:00
}
2006-08-21 02:11:39 +00:00
}
2018-07-15 14:58:15 -04:00
order = ast_calloc ( 1 , sizeof ( * order ) ) ;
if ( ! order ) {
2006-08-21 02:11:39 +00:00
return NULL ;
2018-07-15 14:58:15 -04:00
}
2006-08-21 02:11:39 +00:00
order - > resource = ast_strdup ( resource ) ;
2018-01-17 01:28:57 -05:00
if ( ! order - > resource ) {
ast_free ( order ) ;
return NULL ;
}
2009-11-13 08:52:28 +00:00
order - > required = required ;
2018-07-15 14:58:15 -04:00
order - > preload = preload ;
order - > builtin = builtin ;
2006-08-21 02:11:39 +00:00
AST_LIST_INSERT_TAIL ( load_order , order , entry ) ;
return order ;
}
2008-06-12 17:27:55 +00:00
2016-01-23 14:50:57 -07:00
AST_LIST_HEAD_NOLOCK ( load_retries , load_order_entry ) ;
2017-11-22 00:39:33 -05:00
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 ) {
2018-09-28 11:13:39 -04:00
module_load_error ( " *** Failed to load %smodule %s \n " ,
mod - > flags . required ? " required " : " " ,
mod - > resource ) ;
2017-11-22 00:39:33 -05:00
}
return lres ;
}
2018-09-28 11:13:39 -04:00
static int resource_list_recursive_decline ( struct module_vector * resources , struct ast_module * mod ,
struct ast_str * * printmissing )
{
struct module_vector missingdeps ;
struct ast_vector_const_string localdeps ;
int i = 0 ;
int res = - 1 ;
mod - > flags . declined = 1 ;
if ( mod - > flags . required ) {
module_load_error ( " Required module %s declined to load. \n " , ast_module_name ( mod ) ) ;
return - 2 ;
}
module_load_error ( " %s declined to load. \n " , ast_module_name ( mod ) ) ;
if ( ! * printmissing ) {
* printmissing = ast_str_create ( 64 ) ;
if ( ! * printmissing ) {
return - 1 ;
}
} else {
ast_str_reset ( * printmissing ) ;
}
AST_VECTOR_INIT ( & missingdeps , 0 ) ;
AST_VECTOR_INIT ( & localdeps , 0 ) ;
/* Decline everything that depends on 'mod' from resources so we can
* print a concise list. */
while ( res ! = - 2 & & i < AST_VECTOR_SIZE ( resources ) ) {
struct ast_module * dep = AST_VECTOR_GET ( resources , i ) ;
i + + ;
AST_VECTOR_RESET ( & missingdeps , AST_VECTOR_ELEM_CLEANUP_NOOP ) ;
if ( dep - > flags . declined | | module_deps_missing_recursive ( dep , & missingdeps ) ) {
continue ;
}
if ( AST_VECTOR_GET_CMP ( & missingdeps , mod , AST_VECTOR_ELEM_DEFAULT_CMP ) ) {
dep - > flags . declined = 1 ;
if ( dep - > flags . required ) {
module_load_error ( " Cannot load required module %s that depends on %s \n " ,
ast_module_name ( dep ) , ast_module_name ( mod ) ) ;
res = - 2 ;
} else {
AST_VECTOR_APPEND ( & localdeps , ast_module_name ( dep ) ) ;
}
}
}
AST_VECTOR_FREE ( & missingdeps ) ;
if ( res ! = - 2 & & AST_VECTOR_SIZE ( & localdeps ) ) {
AST_VECTOR_CALLBACK_VOID ( & localdeps , STR_APPEND_TEXT , printmissing ) ;
module_load_error ( " Declined modules which depend on %s: %s \n " ,
ast_module_name ( mod ) , ast_str_buffer ( * printmissing ) ) ;
}
AST_VECTOR_FREE ( & localdeps ) ;
return res ;
}
2017-11-22 00:39:33 -05:00
static int start_resource_list ( struct module_vector * resources , int * mod_count )
{
struct module_vector missingdeps ;
int res = 0 ;
2018-09-28 11:13:39 -04:00
struct ast_str * printmissing = NULL ;
2017-11-22 00:39:33 -05:00
AST_VECTOR_INIT ( & missingdeps , 0 ) ;
2018-09-28 11:13:39 -04:00
while ( res ! = - 2 & & AST_VECTOR_SIZE ( resources ) ) {
2017-11-22 00:39:33 -05:00
struct ast_module * mod = AST_VECTOR_REMOVE ( resources , 0 , 1 ) ;
enum ast_module_load_result lres ;
2018-09-28 11:13:39 -04:00
if ( mod - > flags . declined ) {
ast_debug ( 1 , " %s is already declined, skipping \n " , ast_module_name ( mod ) ) ;
continue ;
}
2018-01-24 23:44:09 -05:00
retry_load :
2017-11-22 00:39:33 -05:00
lres = start_resource_attempt ( mod , mod_count ) ;
if ( lres = = AST_MODULE_LOAD_SUCCESS ) {
/* No missing dependencies, successful. */
continue ;
}
if ( lres = = AST_MODULE_LOAD_FAILURE ) {
2018-07-15 14:58:15 -04:00
res = - 2 ;
2017-11-22 00:39:33 -05:00
break ;
}
if ( lres = = AST_MODULE_LOAD_DECLINE ) {
2018-09-28 11:13:39 -04:00
res = resource_list_recursive_decline ( resources , mod , & printmissing ) ;
2017-11-22 00:39:33 -05:00
continue ;
}
2018-09-28 11:13:39 -04:00
if ( module_deps_missing_recursive ( mod , & missingdeps ) ) {
2018-01-24 23:44:09 -05:00
AST_VECTOR_RESET ( & missingdeps , AST_VECTOR_ELEM_CLEANUP_NOOP ) ;
2018-09-28 11:13:39 -04:00
module_load_error ( " Failed to resolve dependencies for %s \n " , ast_module_name ( mod ) ) ;
res = resource_list_recursive_decline ( resources , mod , & printmissing ) ;
2018-01-24 23:44:09 -05:00
continue ;
2017-11-22 00:39:33 -05:00
}
if ( ! AST_VECTOR_SIZE ( & missingdeps ) ) {
2018-09-28 11:13:39 -04:00
module_load_error ( " %s load function returned an invalid result. "
2018-01-24 23:44:09 -05:00
" This is a bug in the module. \n " , ast_module_name ( mod ) ) ;
/* Dependencies were met but the module failed to start and the result
* code was not AST_MODULE_LOAD_FAILURE or AST_MODULE_LOAD_DECLINE. */
2018-09-28 11:13:39 -04:00
res = resource_list_recursive_decline ( resources , mod , & printmissing ) ;
continue ;
2017-11-22 00:39:33 -05:00
}
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 ) ;
2018-09-28 11:13:39 -04:00
if ( dep - > flags . declined ) {
ast_debug ( 1 , " %s tried to start %s but it's already declined \n " ,
ast_module_name ( mod ) , ast_module_name ( dep ) ) ;
i + + ;
continue ;
}
2017-11-22 00:39:33 -05:00
ast_debug ( 1 , " %s trying to start %s \n " , ast_module_name ( mod ) , ast_module_name ( dep ) ) ;
2018-09-28 11:13:39 -04:00
lres = start_resource_attempt ( dep , mod_count ) ;
if ( lres = = AST_MODULE_LOAD_SUCCESS ) {
2017-11-22 00:39:33 -05:00
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 ;
}
2018-09-28 11:13:39 -04:00
if ( lres = = AST_MODULE_LOAD_FAILURE ) {
module_load_error ( " Failed to load %s. \n " , ast_module_name ( dep ) ) ;
res = - 2 ;
goto exitpoint ;
}
2017-11-22 00:39:33 -05:00
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 ) ) {
2018-09-28 11:13:39 -04:00
if ( ! printmissing ) {
printmissing = ast_str_create ( 64 ) ;
} else {
ast_str_reset ( printmissing ) ;
}
if ( printmissing ) {
struct ast_vector_const_string localdeps ;
AST_VECTOR_INIT ( & localdeps , 0 ) ;
module_deps_reference ( mod , & localdeps ) ;
AST_VECTOR_CALLBACK_VOID ( & localdeps , STR_APPEND_TEXT , & printmissing ) ;
AST_VECTOR_FREE ( & localdeps ) ;
}
module_load_error ( " Failed to load %s due to dependencies: %s. \n " ,
ast_module_name ( mod ) ,
printmissing ? ast_str_buffer ( printmissing ) : " allocation failure creating list " ) ;
res = resource_list_recursive_decline ( resources , mod , & printmissing ) ;
2018-01-24 23:44:09 -05:00
AST_VECTOR_RESET ( & missingdeps , AST_VECTOR_ELEM_CLEANUP_NOOP ) ;
2017-11-22 00:39:33 -05:00
2018-01-24 23:44:09 -05:00
continue ;
2017-11-22 00:39:33 -05:00
}
2018-01-24 23:44:09 -05:00
/* If we're here it means that we started with missingdeps and they're all loaded
* now. It's impossible to reach this point a second time for the same module. */
goto retry_load ;
2017-11-22 00:39:33 -05:00
}
2018-09-28 11:13:39 -04:00
exitpoint :
ast_free ( printmissing ) ;
2017-11-22 00:39:33 -05:00
AST_VECTOR_FREE ( & missingdeps ) ;
return res ;
}
2012-03-22 19:51:16 +00:00
/*! loads modules in order by load_pri, updates mod_count
2009-11-13 08:52:28 +00:00
\return -1 on failure to load module, -2 on failure to load required module, otherwise 0
*/
2017-12-09 00:30:43 -05:00
static int load_resource_list ( struct load_order * load_order , int * mod_count )
2009-06-09 16:22:04 +00:00
{
2017-12-09 01:03:15 -05:00
struct module_vector module_priorities ;
2009-06-09 16:22:04 +00:00
struct load_order_entry * order ;
2017-12-09 01:03:15 -05:00
int attempt = 0 ;
2009-06-09 16:22:04 +00:00
int count = 0 ;
int res = 0 ;
2017-12-09 01:03:15 -05:00
int didwork ;
int lasttry = 0 ;
2009-06-09 16:22:04 +00:00
2017-12-09 01:03:15 -05:00
if ( AST_VECTOR_INIT ( & module_priorities , 500 ) ) {
2017-11-21 01:28:48 -05:00
ast_log ( LOG_ERROR , " Failed to initialize module loader. \n " ) ;
2009-06-09 16:22:04 +00:00
return - 1 ;
}
2018-09-28 11:13:39 -04:00
while ( res ! = - 2 ) {
2017-12-09 01:03:15 -05:00
didwork = 0 ;
2016-01-23 14:50:57 -07:00
2017-12-09 01:03:15 -05:00
AST_LIST_TRAVERSE_SAFE_BEGIN ( load_order , order , entry ) {
2016-01-23 14:50:57 -07:00
enum ast_module_load_result lres ;
/* Suppress log messages unless this is the last pass */
2018-07-15 14:58:15 -04:00
lres = load_resource ( order - > resource , ! lasttry , & module_priorities , order - > required , order - > preload ) ;
2017-12-09 01:03:15 -05:00
ast_debug ( 3 , " PASS %d: %-46s %d \n " , attempt , order - > resource , lres ) ;
2016-01-23 14:50:57 -07:00
switch ( lres ) {
case AST_MODULE_LOAD_SUCCESS :
2017-12-09 01:03:15 -05:00
case AST_MODULE_LOAD_SKIP :
/* We're supplying module_priorities so SUCCESS isn't possible but we
* still have to test for it. SKIP is only used when we try to start a
* module that is missing dependencies. */
break ;
2016-01-23 14:50:57 -07:00
case AST_MODULE_LOAD_DECLINE :
2018-09-28 11:13:39 -04:00
res = - 1 ;
2016-01-23 14:50:57 -07:00
break ;
case AST_MODULE_LOAD_FAILURE :
/* LOAD_FAILURE only happens for required modules */
2017-12-09 01:03:15 -05:00
if ( lasttry ) {
/* This run is just to print errors. */
2018-09-28 11:13:39 -04:00
module_load_error ( " *** Failed to load module %s - Required \n " , order - > resource ) ;
2016-01-23 14:50:57 -07:00
fprintf ( stderr , " *** Failed to load module %s - Required \n " , order - > resource ) ;
res = - 2 ;
}
break ;
case AST_MODULE_LOAD_PRIORITY :
2017-12-09 01:03:15 -05:00
/* load_resource worked and the module was added to module_priorities */
2016-01-23 14:50:57 -07:00
AST_LIST_REMOVE_CURRENT ( entry ) ;
ast_free ( order - > resource ) ;
ast_free ( order ) ;
2017-12-09 01:03:15 -05:00
didwork = 1 ;
2016-01-23 14:50:57 -07:00
break ;
}
}
AST_LIST_TRAVERSE_SAFE_END ;
2017-12-09 01:03:15 -05:00
if ( ! didwork ) {
if ( lasttry ) {
break ;
}
/* We know the next try is going to fail, it's only being performed
* so we can print errors. */
lasttry = 1 ;
}
attempt + + ;
}
2009-06-09 16:22:04 +00:00
2018-09-28 11:13:39 -04:00
if ( res ! = - 2 ) {
2017-12-09 01:03:15 -05:00
res = start_resource_list ( & module_priorities , & count ) ;
2016-01-23 14:50:57 -07:00
}
2009-06-09 16:22:04 +00:00
if ( mod_count ) {
* mod_count + = count ;
}
2017-12-09 01:03:15 -05:00
AST_VECTOR_FREE ( & module_priorities ) ;
2009-06-09 16:22:04 +00:00
return res ;
}
2018-07-15 14:58:15 -04:00
static int loader_builtin_init ( struct load_order * load_order )
2006-08-21 02:11:39 +00:00
{
2018-07-15 14:58:15 -04:00
struct ast_module * mod ;
2006-08-21 02:11:39 +00:00
2017-10-29 23:00:52 -04:00
/*
* All built-in modules have registered the first time, now it's time to complete
* the registration and add them to the priority list.
*/
loader_ready = 1 ;
while ( ( resource_being_loaded = AST_DLLIST_REMOVE_HEAD ( & builtin_module_list , entry ) ) ) {
/* ast_module_register doesn't finish when first run by built-in modules. */
ast_module_register ( resource_being_loaded - > info ) ;
}
2018-07-15 14:58:15 -04:00
/* Add all built-in modules to the load order. */
AST_DLLIST_TRAVERSE ( & module_list , mod , entry ) {
if ( ! mod - > flags . builtin ) {
continue ;
}
2018-02-16 22:11:42 -05:00
2025-02-04 05:53:17 -05:00
/* Parse dependencies from mod->info. */
2018-07-26 13:52:46 -04:00
if ( module_post_register ( mod ) ) {
return - 1 ;
}
2018-07-15 14:58:15 -04:00
/* Built-in modules are not preloaded, most have an early load priority. */
if ( ! add_to_load_order ( mod - > resource , load_order , 0 , 0 , 1 ) ) {
return - 1 ;
2018-02-16 22:11:42 -05:00
}
}
2018-07-15 14:58:15 -04:00
return 0 ;
}
static int loader_config_init ( struct load_order * load_order )
{
int res = - 1 ;
struct load_order_entry * order ;
struct ast_config * cfg ;
struct ast_variable * v ;
struct ast_flags config_flags = { 0 } ;
2008-09-12 23:30:03 +00:00
cfg = ast_config_load2 ( AST_MODULE_CONFIG , " " /* core, can't reload */ , config_flags ) ;
if ( cfg = = CONFIG_STATUS_FILEMISSING | | cfg = = CONFIG_STATUS_FILEINVALID ) {
2018-07-15 14:58:15 -04:00
ast_log ( LOG_WARNING , " '%s' invalid or missing. \n " , AST_MODULE_CONFIG ) ;
return - 1 ;
2006-08-21 02:11:39 +00:00
}
2006-08-22 23:06:13 +00:00
/* first, find all the modules we have been explicitly requested to load */
for ( v = ast_variable_browse ( cfg , " modules " ) ; v ; v = v - > next ) {
2018-07-15 14:58:15 -04:00
int required ;
int preload = 0 ;
if ( ! strncasecmp ( v - > name , " preload " , strlen ( " preload " ) ) ) {
preload = 1 ;
if ( ! strcasecmp ( v - > name , " preload " ) ) {
required = 0 ;
} else if ( ! strcasecmp ( v - > name , " preload-require " ) ) {
required = 1 ;
} else {
ast_log ( LOG_ERROR , " Unknown configuration option '%s' " , v - > name ) ;
goto done ;
}
} else if ( ! strcasecmp ( v - > name , " load " ) ) {
required = 0 ;
} else if ( ! strcasecmp ( v - > name , " require " ) ) {
required = 1 ;
} else if ( ! strcasecmp ( v - > name , " noload " ) | | ! strcasecmp ( v - > name , " autoload " ) ) {
continue ;
} else {
ast_log ( LOG_ERROR , " Unknown configuration option '%s' " , v - > name ) ;
goto done ;
2009-11-13 08:52:28 +00:00
}
2018-07-15 14:58:15 -04:00
if ( required ) {
2009-11-13 08:52:28 +00:00
ast_debug ( 2 , " Adding module to required list: %s (%s) \n " , v - > value , v - > name ) ;
2008-06-12 17:27:55 +00:00
}
2018-07-15 14:58:15 -04:00
if ( ! add_to_load_order ( v - > value , load_order , required , preload , 0 ) ) {
goto done ;
}
2006-08-21 02:11:39 +00:00
}
/* check if 'autoload' is on */
2018-07-15 14:58:15 -04:00
if ( ast_true ( ast_variable_retrieve ( cfg , " modules " , " autoload " ) ) ) {
2006-08-21 02:11:39 +00:00
/* if we are allowed to load dynamic modules, scan the directory for
for all available modules and add them as well */
2018-07-15 14:58:15 -04:00
DIR * dir = opendir ( ast_config_AST_MODULE_DIR ) ;
struct dirent * dirent ;
if ( dir ) {
2006-08-21 02:11:39 +00:00
while ( ( dirent = readdir ( dir ) ) ) {
int ld = strlen ( dirent - > d_name ) ;
2006-09-15 05:00:27 +00:00
2006-08-21 02:11:39 +00:00
/* Must end in .so to load it. */
if ( ld < 4 )
continue ;
if ( strcasecmp ( dirent - > d_name + ld - 3 , " .so " ) )
2007-05-03 16:43:49 +00:00
continue ;
2006-08-21 02:11:39 +00:00
2007-05-03 16:43:49 +00:00
/* if there is already a module by this name in the module_list,
skip this file */
if ( find_resource ( dirent - > d_name , 0 ) )
continue ;
2006-08-21 02:11:39 +00:00
2018-07-15 14:58:15 -04:00
if ( ! add_to_load_order ( dirent - > d_name , load_order , 0 , 0 , 0 ) ) {
closedir ( dir ) ;
goto done ;
}
2006-08-21 02:11:39 +00:00
}
closedir ( dir ) ;
} else {
2018-07-15 14:58:15 -04:00
ast_log ( LOG_ERROR , " Unable to open modules directory '%s'. \n " , ast_config_AST_MODULE_DIR ) ;
goto done ;
2006-08-21 02:11:39 +00:00
}
}
/* now scan the config for any modules we are prohibited from loading and
remove them from the load order */
for ( v = ast_variable_browse ( cfg , " modules " ) ; v ; v = v - > next ) {
2017-12-08 19:19:34 -05:00
size_t baselen ;
if ( strcasecmp ( v - > name , " noload " ) ) {
2006-08-21 02:11:39 +00:00
continue ;
2017-12-08 19:19:34 -05:00
}
2006-08-21 02:11:39 +00:00
2017-12-08 19:19:34 -05:00
baselen = resource_name_baselen ( v - > value ) ;
2018-07-15 14:58:15 -04:00
AST_LIST_TRAVERSE_SAFE_BEGIN ( load_order , order , entry ) {
2017-12-08 19:19:34 -05:00
if ( ! resource_name_match ( v - > value , baselen , order - > resource ) ) {
2018-07-15 14:58:15 -04:00
if ( order - > builtin ) {
ast_log ( LOG_ERROR , " %s is a built-in module, you cannot specify 'noload'. \n " , v - > value ) ;
goto done ;
}
if ( order - > required ) {
ast_log ( LOG_ERROR , " %s is configured with '%s' and 'noload', this is impossible. \n " ,
v - > value , order - > preload ? " preload-require " : " require " ) ;
goto done ;
}
2007-11-08 05:28:47 +00:00
AST_LIST_REMOVE_CURRENT ( entry ) ;
2007-06-06 21:20:11 +00:00
ast_free ( order - > resource ) ;
ast_free ( order ) ;
2006-08-21 02:11:39 +00:00
}
}
AST_LIST_TRAVERSE_SAFE_END ;
}
2018-07-15 14:58:15 -04:00
res = 0 ;
done :
2006-08-21 02:11:39 +00:00
ast_config_destroy ( cfg ) ;
2018-07-15 14:58:15 -04:00
return res ;
}
int load_modules ( void )
{
struct load_order_entry * order ;
unsigned int load_count ;
struct load_order load_order ;
int res = 0 ;
int modulecount = 0 ;
2018-09-28 11:13:39 -04:00
int i ;
2021-03-10 11:03:11 -04:00
struct ast_module * cur ;
# ifdef AST_XML_DOCS
struct ast_str * warning_msg ;
char deprecated_in [ 33 ] ;
char removed_in [ 33 ] ;
char replacement [ 129 ] ;
# endif
struct timeval start_time = ast_tvnow ( ) ;
struct timeval end_time ;
int64_t usElapsed ;
2018-07-15 14:58:15 -04:00
ast_verb ( 1 , " Asterisk Dynamic Loader Starting: \n " ) ;
2019-04-02 22:49:52 +02:00
# if defined(HAVE_PERMANENT_DLOPEN)
info_list = ao2_container_alloc_list ( AO2_ALLOC_OPT_LOCK_NOLOCK , 0 , NULL ,
info_list_obj_cmp_fn ) ; /* must not be cleaned at shutdown */
if ( ! info_list ) {
fprintf ( stderr , " Module info list allocation failure. \n " ) ;
return 1 ;
}
# endif
2018-07-15 14:58:15 -04:00
AST_LIST_HEAD_INIT_NOLOCK ( & load_order ) ;
AST_DLLIST_LOCK ( & module_list ) ;
2018-09-28 11:13:39 -04:00
AST_VECTOR_INIT ( & startup_errors , 0 ) ;
startup_error_builder = ast_str_create ( 64 ) ;
2018-07-15 14:58:15 -04:00
res = loader_builtin_init ( & load_order ) ;
if ( res ) {
goto done ;
}
res = loader_config_init ( & load_order ) ;
if ( res ) {
goto done ;
}
2006-08-21 02:11:39 +00:00
load_count = 0 ;
AST_LIST_TRAVERSE ( & load_order , order , entry )
load_count + + ;
2006-08-22 23:06:13 +00:00
if ( load_count )
2014-05-09 22:49:26 +00:00
ast_log ( LOG_NOTICE , " %u modules will be loaded. \n " , load_count ) ;
2006-08-21 02:11:39 +00:00
2017-12-09 00:30:43 -05:00
res = load_resource_list ( & load_order , & modulecount ) ;
2018-09-28 11:13:39 -04:00
if ( res = = - 1 ) {
ast_log ( LOG_WARNING , " Some non-required modules failed to load. \n " ) ;
res = 0 ;
}
2006-08-21 02:11:39 +00:00
done :
while ( ( order = AST_LIST_REMOVE_HEAD ( & load_order , entry ) ) ) {
2007-06-06 21:20:11 +00:00
ast_free ( order - > resource ) ;
ast_free ( order ) ;
2006-08-21 02:11:39 +00:00
}
2021-03-10 11:03:11 -04:00
# ifdef AST_XML_DOCS
warning_msg = ast_str_create ( 512 ) ;
# endif
AST_DLLIST_TRAVERSE ( & module_list , cur , entry ) {
# ifdef AST_XML_DOCS
char * mod_name = NULL ;
struct ast_xml_xpath_results * results ;
# endif
if ( ! cur - > flags . running | | cur - > flags . declined ) {
continue ;
}
# ifdef AST_XML_DOCS
mod_name = get_name_from_resource ( cur - > resource ) ;
if ( ! warning_msg | | ! mod_name ) {
/* If we can't allocate memory, we have bigger issues */
ast_free ( mod_name ) ;
continue ;
}
2021-04-01 11:38:04 -04:00
/* Clear out the previous values */
deprecated_in [ 0 ] = removed_in [ 0 ] = replacement [ 0 ] = 0 ;
results = ast_xmldoc_query ( " /docs/module[@name='%s'] " , mod_name ) ;
2021-03-10 11:03:11 -04:00
if ( results ) {
2021-04-01 11:38:04 -04:00
struct ast_xml_node * deprecated_node , * removed_node , * replacement_node ;
struct ast_xml_node * metadata_nodes = ast_xml_node_get_children ( ast_xml_xpath_get_first_result ( results ) ) ;
deprecated_node = ast_xml_find_element ( metadata_nodes , " deprecated_in " , NULL , NULL ) ;
if ( deprecated_node ) {
const char * result_tmp = ast_xml_get_text ( deprecated_node ) ;
if ( ! ast_strlen_zero ( result_tmp ) ) {
ast_copy_string ( deprecated_in , result_tmp , sizeof ( deprecated_in ) ) ;
}
2021-03-10 11:03:11 -04:00
}
2021-04-01 11:38:04 -04:00
removed_node = ast_xml_find_element ( metadata_nodes , " removed_in " , NULL , NULL ) ;
if ( removed_node ) {
const char * result_tmp = ast_xml_get_text ( removed_node ) ;
if ( ! ast_strlen_zero ( result_tmp ) ) {
ast_copy_string ( removed_in , result_tmp , sizeof ( removed_in ) ) ;
}
2021-03-10 11:03:11 -04:00
}
2021-04-01 11:38:04 -04:00
replacement_node = ast_xml_find_element ( metadata_nodes , " replacement " , NULL , NULL ) ;
if ( replacement_node ) {
const char * result_tmp = ast_xml_get_text ( replacement_node ) ;
if ( ! ast_strlen_zero ( result_tmp ) ) {
ast_copy_string ( replacement , result_tmp , sizeof ( replacement ) ) ;
}
2021-03-10 11:03:11 -04:00
}
2021-04-01 11:38:04 -04:00
2021-03-10 11:03:11 -04:00
ast_xml_xpath_results_free ( results ) ;
}
ast_str_reset ( warning_msg ) ;
if ( cur - > info - > support_level = = AST_MODULE_SUPPORT_DEPRECATED | | ! ast_strlen_zero ( deprecated_in )
| | ! ast_strlen_zero ( removed_in ) | | ! ast_strlen_zero ( replacement ) ) {
int already_butted = 0 ;
ast_str_append ( & warning_msg , - 1 , " Module '%s' has been loaded " , mod_name ) ;
if ( ! ast_strlen_zero ( deprecated_in ) ) {
ast_str_append ( & warning_msg , - 1 , " but %s deprecated in Asterisk version %s " ,
cur - > info - > support_level = = AST_MODULE_SUPPORT_DEPRECATED ? " was " : " will be " , deprecated_in ) ;
already_butted = 1 ;
}
if ( ! ast_strlen_zero ( removed_in ) ) {
ast_str_append ( & warning_msg , - 1 , " %s will be removed in Asterisk version %s " , already_butted ? " and " : " but " , removed_in ) ;
} else {
ast_str_append ( & warning_msg , - 1 , " %s may be removed in a future release " , already_butted ? " and " : " but " ) ;
}
ast_str_append ( & warning_msg , - 1 , " . " ) ;
if ( ! ast_strlen_zero ( replacement ) ) {
ast_str_append ( & warning_msg , - 1 , " Its replacement is '%s'. " , replacement ) ;
}
}
if ( ast_str_strlen ( warning_msg ) ) {
ast_log ( LOG_WARNING , " %s \n " , ast_str_buffer ( warning_msg ) ) ;
}
ast_free ( mod_name ) ;
# else
if ( cur - > info - > support_level = = AST_MODULE_SUPPORT_DEPRECATED ) {
ast_log ( LOG_WARNING , " The deprecated module '%s' has been loaded and is running, it may be removed in a future version \n " , cur - > resource ) ;
}
# endif
}
# ifdef AST_XML_DOCS
ast_free ( warning_msg ) ;
# endif
2014-07-25 14:27:52 +00:00
AST_DLLIST_UNLOCK ( & module_list ) ;
2018-09-28 11:13:39 -04:00
2021-03-10 11:03:11 -04:00
2018-09-28 11:13:39 -04:00
for ( i = 0 ; i < AST_VECTOR_SIZE ( & startup_errors ) ; i + + ) {
char * str = AST_VECTOR_GET ( & startup_errors , i ) ;
ast_log ( LOG_ERROR , " %s " , str ) ;
ast_free ( str ) ;
}
AST_VECTOR_FREE ( & startup_errors ) ;
ast_free ( startup_error_builder ) ;
startup_error_builder = NULL ;
2021-03-10 11:03:11 -04:00
end_time = ast_tvnow ( ) ;
usElapsed = ast_tvdiff_us ( end_time , start_time ) ;
# ifdef AST_XML_DOCS
2022-05-17 10:18:58 -04:00
ast_debug ( 1 , " Loader time with AST_XML_DOCS: % " PRId64 " .%06 " PRId64 " \n " , usElapsed / 1000000 , usElapsed % 1000000 ) ;
2021-03-10 11:03:11 -04:00
# else
2022-05-17 10:18:58 -04:00
ast_debug ( 1 , " Loader time without AST_XML_DOCS: % " PRId64 " .%06 " PRId64 " \n " , usElapsed / 1000000 , usElapsed % 1000000 ) ;
2021-03-10 11:03:11 -04:00
# endif
2006-08-21 02:11:39 +00:00
return res ;
}
void ast_update_use_count ( void )
{
2006-09-15 05:00:27 +00:00
/* Notify any module monitors that the use count for a
2006-08-21 02:11:39 +00:00
resource has changed */
struct loadupdate * m ;
2008-02-27 17:12:08 +00:00
AST_LIST_LOCK ( & updaters ) ;
2006-08-21 02:11:39 +00:00
AST_LIST_TRAVERSE ( & updaters , m , entry )
m - > updater ( ) ;
2008-02-27 17:12:08 +00:00
AST_LIST_UNLOCK ( & updaters ) ;
2006-08-21 02:11:39 +00:00
}
2017-12-11 18:07:54 -05:00
/*!
* \internal
* \brief Build an alpha sorted list of modules.
*
* \param alpha_module_list Pointer to uninitialized module_vector.
*
* This function always initializes alpha_module_list.
*
* \pre module_list must be locked.
*/
static int alpha_module_list_create ( struct module_vector * alpha_module_list )
{
struct ast_module * cur ;
if ( AST_VECTOR_INIT ( alpha_module_list , 32 ) ) {
return - 1 ;
}
AST_DLLIST_TRAVERSE ( & module_list , cur , entry ) {
if ( AST_VECTOR_ADD_SORTED ( alpha_module_list , cur , module_vector_strcasecmp ) ) {
return - 1 ;
}
}
return 0 ;
}
2014-07-25 16:47:17 +00:00
int ast_update_module_list ( int ( * modentry ) ( const char * module , const char * description ,
int usecnt , const char * status , const char * like ,
enum ast_module_support_level support_level ) ,
const char * like )
2006-08-21 02:11:39 +00:00
{
int total_mod_loaded = 0 ;
2017-12-11 18:07:54 -05:00
struct module_vector alpha_module_list ;
2006-08-21 02:11:39 +00:00
2017-12-11 18:07:54 -05:00
AST_DLLIST_LOCK ( & module_list ) ;
2012-03-22 19:51:16 +00:00
2017-12-11 18:07:54 -05:00
if ( ! alpha_module_list_create ( & alpha_module_list ) ) {
int idx ;
2014-07-25 14:27:52 +00:00
2017-12-11 18:07:54 -05:00
for ( idx = 0 ; idx < AST_VECTOR_SIZE ( & alpha_module_list ) ; idx + + ) {
struct ast_module * cur = AST_VECTOR_GET ( & alpha_module_list , idx ) ;
2006-08-21 02:11:39 +00:00
2017-12-11 18:07:54 -05:00
total_mod_loaded + = modentry ( cur - > resource , cur - > info - > description , cur - > usecount ,
cur - > flags . running ? " Running " : " Not Running " , like , cur - > info - > support_level ) ;
}
2014-07-25 14:27:52 +00:00
}
2006-08-21 02:11:39 +00:00
2017-12-11 18:07:54 -05:00
AST_DLLIST_UNLOCK ( & module_list ) ;
AST_VECTOR_FREE ( & alpha_module_list ) ;
2006-08-21 02:11:39 +00:00
return total_mod_loaded ;
}
2015-06-26 10:57:15 -05:00
int ast_update_module_list_data ( int ( * modentry ) ( const char * module , const char * description ,
int usecnt , const char * status , const char * like ,
enum ast_module_support_level support_level ,
void * data ) ,
const char * like , void * data )
{
int total_mod_loaded = 0 ;
2017-12-11 18:07:54 -05:00
struct module_vector alpha_module_list ;
2015-06-26 10:57:15 -05:00
AST_DLLIST_LOCK ( & module_list ) ;
2017-12-11 18:07:54 -05:00
if ( ! alpha_module_list_create ( & alpha_module_list ) ) {
int idx ;
2015-06-26 10:57:15 -05:00
2017-12-11 18:07:54 -05:00
for ( idx = 0 ; idx < AST_VECTOR_SIZE ( & alpha_module_list ) ; idx + + ) {
struct ast_module * cur = AST_VECTOR_GET ( & alpha_module_list , idx ) ;
total_mod_loaded + = modentry ( cur - > resource , cur - > info - > description , cur - > usecount ,
cur - > flags . running ? " Running " : " Not Running " , like , cur - > info - > support_level , data ) ;
}
2015-06-26 10:57:15 -05:00
}
AST_DLLIST_UNLOCK ( & module_list ) ;
2017-12-11 18:07:54 -05:00
AST_VECTOR_FREE ( & alpha_module_list ) ;
2015-06-26 10:57:15 -05:00
return total_mod_loaded ;
}
2015-07-13 10:54:51 -05:00
int ast_update_module_list_condition ( int ( * modentry ) ( const char * module , const char * description ,
int usecnt , const char * status ,
const char * like ,
enum ast_module_support_level support_level ,
void * data , const char * condition ) ,
const char * like , void * data , const char * condition )
{
int conditions_met = 0 ;
2017-12-11 18:07:54 -05:00
struct module_vector alpha_module_list ;
2015-07-13 10:54:51 -05:00
AST_DLLIST_LOCK ( & module_list ) ;
2017-12-11 18:07:54 -05:00
if ( ! alpha_module_list_create ( & alpha_module_list ) ) {
int idx ;
2015-07-13 10:54:51 -05:00
2017-12-11 18:07:54 -05:00
for ( idx = 0 ; idx < AST_VECTOR_SIZE ( & alpha_module_list ) ; idx + + ) {
struct ast_module * cur = AST_VECTOR_GET ( & alpha_module_list , idx ) ;
conditions_met + = modentry ( cur - > resource , cur - > info - > description , cur - > usecount ,
cur - > flags . running ? " Running " : " Not Running " , like , cur - > info - > support_level , data ,
condition ) ;
}
2015-07-13 10:54:51 -05:00
}
AST_DLLIST_UNLOCK ( & module_list ) ;
2017-12-11 18:07:54 -05:00
AST_VECTOR_FREE ( & alpha_module_list ) ;
2015-07-13 10:54:51 -05:00
return conditions_met ;
}
2006-10-31 08:08:56 +00:00
/*! \brief Check if module exists */
2007-05-07 19:03:53 +00:00
int ast_module_check ( const char * name )
2006-10-30 21:48:41 +00:00
{
struct ast_module * cur ;
if ( ast_strlen_zero ( name ) )
return 0 ; /* FALSE */
2006-10-31 08:08:56 +00:00
cur = find_resource ( name , 1 ) ;
return ( cur ! = NULL ) ;
2006-10-30 21:48:41 +00:00
}
2006-09-15 05:00:27 +00:00
int ast_loader_register ( int ( * v ) ( void ) )
2006-08-21 02:11:39 +00:00
{
2006-09-15 05:00:27 +00:00
struct loadupdate * tmp ;
2006-08-21 02:11:39 +00:00
if ( ! ( tmp = ast_malloc ( sizeof ( * tmp ) ) ) )
return - 1 ;
tmp - > updater = v ;
2008-02-27 17:12:08 +00:00
AST_LIST_LOCK ( & updaters ) ;
2006-08-21 02:11:39 +00:00
AST_LIST_INSERT_HEAD ( & updaters , tmp , entry ) ;
2008-02-27 17:12:08 +00:00
AST_LIST_UNLOCK ( & updaters ) ;
2006-08-21 02:11:39 +00:00
return 0 ;
}
int ast_loader_unregister ( int ( * v ) ( void ) )
{
struct loadupdate * cur ;
2008-02-27 17:12:08 +00:00
AST_LIST_LOCK ( & updaters ) ;
2006-08-21 02:11:39 +00:00
AST_LIST_TRAVERSE_SAFE_BEGIN ( & updaters , cur , entry ) {
if ( cur - > updater = = v ) {
2007-11-08 05:28:47 +00:00
AST_LIST_REMOVE_CURRENT ( entry ) ;
2006-08-21 02:11:39 +00:00
break ;
}
}
AST_LIST_TRAVERSE_SAFE_END ;
2008-02-27 17:12:08 +00:00
AST_LIST_UNLOCK ( & updaters ) ;
2006-08-21 02:11:39 +00:00
return cur ? 0 : - 1 ;
}
2015-02-11 17:03:04 +00:00
struct ast_module * __ast_module_ref ( struct ast_module * mod , const char * file , int line , const char * func )
2006-08-21 02:11:39 +00:00
{
2010-02-17 07:01:13 +00:00
if ( ! mod ) {
return NULL ;
}
2015-04-17 03:16:59 -04:00
if ( mod - > ref_debug ) {
__ao2_ref ( mod - > ref_debug , + 1 , " " , file , line , func ) ;
}
2015-02-11 17:03:04 +00:00
2006-08-21 02:11:39 +00:00
ast_atomic_fetchadd_int ( & mod - > usecount , + 1 ) ;
ast_update_use_count ( ) ;
return mod ;
}
2017-12-29 19:24:02 -05:00
struct ast_module * __ast_module_running_ref ( struct ast_module * mod ,
const char * file , int line , const char * func )
{
if ( ! mod | | ! mod - > flags . running ) {
return NULL ;
}
return __ast_module_ref ( mod , file , line , func ) ;
}
2015-02-11 17:03:04 +00:00
void __ast_module_shutdown_ref ( struct ast_module * mod , const char * file , int line , const char * func )
{
2015-02-21 02:58:19 +00:00
if ( ! mod | | mod - > flags . keepuntilshutdown ) {
return ;
2015-02-11 17:03:04 +00:00
}
2015-02-21 02:58:19 +00:00
__ast_module_ref ( mod , file , line , func ) ;
mod - > flags . keepuntilshutdown = 1 ;
2015-02-11 17:03:04 +00:00
}
void __ast_module_unref ( struct ast_module * mod , const char * file , int line , const char * func )
2006-08-21 02:11:39 +00:00
{
2010-02-17 07:01:13 +00:00
if ( ! mod ) {
return ;
}
2015-04-17 03:16:59 -04:00
if ( mod - > ref_debug ) {
__ao2_ref ( mod - > ref_debug , - 1 , " " , file , line , func ) ;
}
2015-02-11 17:03:04 +00:00
2006-08-21 02:11:39 +00:00
ast_atomic_fetchadd_int ( & mod - > usecount , - 1 ) ;
ast_update_use_count ( ) ;
}
2014-07-25 16:47:17 +00:00
const char * support_level_map [ ] = {
[ AST_MODULE_SUPPORT_UNKNOWN ] = " unknown " ,
[ AST_MODULE_SUPPORT_CORE ] = " core " ,
[ AST_MODULE_SUPPORT_EXTENDED ] = " extended " ,
[ AST_MODULE_SUPPORT_DEPRECATED ] = " deprecated " ,
} ;
const char * ast_module_support_level_to_string ( enum ast_module_support_level support_level )
{
return support_level_map [ support_level ] ;
}