Bring in the new loader code as described in mantis #4377

and updated to today's version.

The core of the patch is only two files, loader.c
and include/asterisk/module.h, with the other files
touched only to adapt non-standard usages of the
reference counts and localuser lists.



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@15784 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.4
Luigi Rizzo 19 years ago
parent f7debd1823
commit 2326d0d499

@ -316,7 +316,7 @@ char *description(void)
int usecount(void) int usecount(void)
{ {
int res; int res;
STANDARD_USECOUNT(res); OLD_STANDARD_USECOUNT(res);
return res; return res;
} }

@ -616,7 +616,7 @@ char *description(void)
int usecount(void) int usecount(void)
{ {
int res; int res;
STANDARD_USECOUNT(res); OLD_STANDARD_USECOUNT(res);
return res; return res;
} }

@ -416,7 +416,7 @@ char *description(void)
int usecount(void) int usecount(void)
{ {
int res; int res;
STANDARD_USECOUNT(res); OLD_STANDARD_USECOUNT(res);
return res; return res;
} }

@ -407,7 +407,7 @@ char *description(void)
int usecount(void) int usecount(void)
{ {
int res; int res;
STANDARD_USECOUNT(res); OLD_STANDARD_USECOUNT(res);
return res; return res;
} }

@ -1078,7 +1078,7 @@ int
usecount (void) usecount (void)
{ {
int res; int res;
STANDARD_USECOUNT (res); OLD_STANDARD_USECOUNT (res);
return res; return res;
} }

@ -347,7 +347,7 @@ char *description(void)
int usecount(void) int usecount(void)
{ {
int res; int res;
STANDARD_USECOUNT(res); OLD_STANDARD_USECOUNT(res);
return res; return res;
} }

@ -307,7 +307,7 @@ char *description(void)
int usecount(void) int usecount(void)
{ {
int res; int res;
STANDARD_USECOUNT(res); OLD_STANDARD_USECOUNT(res);
return res; return res;
} }

@ -420,7 +420,7 @@ char *description(void)
int usecount(void) int usecount(void)
{ {
int res; int res;
STANDARD_USECOUNT(res); OLD_STANDARD_USECOUNT(res);
return res; return res;
} }

@ -602,7 +602,7 @@ char *description(void)
int usecount(void) int usecount(void)
{ {
int res; int res;
STANDARD_USECOUNT(res); OLD_STANDARD_USECOUNT(res);
return res; return res;
} }

@ -418,7 +418,7 @@ char *description(void)
int usecount(void) int usecount(void)
{ {
int res; int res;
STANDARD_USECOUNT(res); OLD_STANDARD_USECOUNT(res);
return res; return res;
} }

@ -26,14 +26,22 @@
#ifndef _ASTERISK_MODULE_H #ifndef _ASTERISK_MODULE_H
#define _ASTERISK_MODULE_H #define _ASTERISK_MODULE_H
#include "asterisk/linkedlists.h" /* XXX needed here */
#include "asterisk/utils.h" #include "asterisk/utils.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
/* Every module should provide these functions */ #ifndef STATIC_MODULE
#define STATIC_MODULE /* empty - symbols are global */
#else
#undef STATIC_MODULE
#define STATIC_MODULE static /* symbols are static */
#endif
/* Every module should provide these functions */
/*! /*!
* \brief Initialize the module. * \brief Initialize the module.
* *
@ -45,7 +53,7 @@ extern "C" {
* If the module is not loaded successfully, Asterisk will call its * If the module is not loaded successfully, Asterisk will call its
* unload_module() function. * unload_module() function.
*/ */
int load_module(void); STATIC_MODULE int load_module(void);
/*! /*!
* \brief Cleanup all module structures, sockets, etc. * \brief Cleanup all module structures, sockets, etc.
@ -56,7 +64,7 @@ int load_module(void);
* *
* \return Zero on success, or non-zero on error. * \return Zero on success, or non-zero on error.
*/ */
int unload_module(void); STATIC_MODULE int unload_module(void);
/*! /*!
* \brief Provides a usecount. * \brief Provides a usecount.
@ -68,13 +76,13 @@ int unload_module(void);
* *
* \return The module's usecount. * \return The module's usecount.
*/ */
int usecount(void); /* How many channels provided by this module are in use? */ STATIC_MODULE int usecount(void); /* How many channels provided by this module are in use? */
/*! \brief Provides a description of the module. /*! \brief Provides a description of the module.
* *
* \return a short description of your module * \return a short description of your module
*/ */
char *description(void); /* Description of this module */ STATIC_MODULE char *description(void); /* Description of this module */
/*! /*!
* \brief Returns the ASTERISK_GPL_KEY * \brief Returns the ASTERISK_GPL_KEY
@ -91,7 +99,7 @@ char *description(void); /* Description of this module */
* *
* \return ASTERISK_GPL_KEY * \return ASTERISK_GPL_KEY
*/ */
char *key(void); /* Return the below mentioned key, unmodified */ STATIC_MODULE char *key(void); /* Return the below mentioned key, unmodified */
/*! /*!
* \brief Reload stuff. * \brief Reload stuff.
@ -101,7 +109,7 @@ char *key(void); /* Return the below mentioned key, unmodified */
* *
* \return The return value is not used. * \return The return value is not used.
*/ */
int reload(void); /* reload configs */ STATIC_MODULE int reload(void); /* reload configs */
/*! \brief The text the key() function should return. */ /*! \brief The text the key() function should return. */
#define ASTERISK_GPL_KEY \ #define ASTERISK_GPL_KEY \
@ -260,50 +268,66 @@ int ast_register_atexit(void (*func)(void));
*/ */
void ast_unregister_atexit(void (*func)(void)); void ast_unregister_atexit(void (*func)(void));
/*!
* \brief Given a function address, find the corresponding module.
* This is required as a workaround to the fact that we do not
* have a module argument to the load_module() function.
* Hopefully the performance implications are small.
*/
struct module *ast_find_module(int (*load_fn)(void));
/* Local user routines keep track of which channels are using a given module /* Local user routines keep track of which channels are using a given module
resource. They can help make removing modules safer, particularly if resource. They can help make removing modules safer, particularly if
they're in use at the time they have been requested to be removed */ they're in use at the time they have been requested to be removed */
/*!
* \brief Standard localuser struct definition.
* used to keep track of channels using a given resource.
*/
struct localuser { struct localuser {
struct ast_channel *chan; struct ast_channel *chan;
struct localuser *next; AST_LIST_ENTRY(localuser) next;
}; };
/*! structure used for lock and refcount of module users.
* The mutex protects the usecnt field and whatever needs to be
* protected (typically, a list of struct localuser).
* As a trick, if usecnt is initialized with -1,
* ast_format_register will init the mutex for you.
*/
struct ast_module_lock {
ast_mutex_t lock;
AST_LIST_HEAD_NOLOCK(localuser_head, localuser) u;
int usecnt; /* number of active clients */
};
struct localuser *ast_localuser_add(struct ast_module_lock *m, struct ast_channel *chan);
void ast_localuser_remove(struct ast_module_lock *m, struct localuser *u);
void ast_hangup_localusers(struct ast_module_lock *m);
/*! /*!
* \brief The localuser declaration. * \brief create a localuser mutex and several other variables used for keeping the
* * use count.
* This creates a localuser mutex and the head of a list of localusers
* that is used for keeping track of channels using a resource, as well
* as the use count.
* *
* <b>Sample Usage:</b> * <b>Sample Usage:</b>
* \code * \code
* LOCAL_USER_DECL; * LOCAL_USER_DECL;
* \endcode * \endcode
*/ */
#define LOCAL_USER_DECL AST_MUTEX_DEFINE_STATIC(localuser_lock); \ #define LOCAL_USER_DECL \
static struct localuser *localusers = NULL; \ static struct ast_module_lock me = { \
static int localusecnt = 0; .u = AST_LIST_HEAD_NOLOCK_INIT_VALUE, \
.usecnt = 0, \
.lock = AST_MUTEX_INIT_VALUE }
#define STANDARD_USECOUNT_DECL \ #define STANDARD_USECOUNT_DECL LOCAL_USER_DECL /* XXX lock remains unused */
AST_MUTEX_DEFINE_STATIC(localuser_lock); \
static int localusecnt = 0;
#define STANDARD_INCREMENT_USECOUNT \ /*! run 'x' protected by lock, then call ast_update_use_count() */
ast_mutex_lock(&localuser_lock); \ #define __MOD_PROTECT(x) do { \
localusecnt++; \ ast_mutex_lock(&me.lock); \
ast_mutex_unlock(&localuser_lock); \ x; \
ast_update_use_count(); ast_mutex_unlock(&me.lock); \
ast_update_use_count(); \
} while (0)
#define STANDARD_DECREMENT_USECOUNT \ #define STANDARD_INCREMENT_USECOUNT __MOD_PROTECT(me.usecnt++)
ast_mutex_lock(&localuser_lock); \ #define STANDARD_DECREMENT_USECOUNT __MOD_PROTECT(me.usecnt--)
localusecnt--; \
ast_mutex_unlock(&localuser_lock); \
ast_update_use_count();
/*! /*!
* \brief Add a localuser. * \brief Add a localuser.
@ -316,18 +340,11 @@ struct localuser {
* \note This function dynamically allocates memory. If this operation fails * \note This function dynamically allocates memory. If this operation fails
* it will cause your function to return -1 to the caller. * it will cause your function to return -1 to the caller.
*/ */
#define LOCAL_USER_ADD(u) { \ #define LOCAL_USER_ADD(u) do { \
\ u = ast_localuser_add(&me, chan); \
if (!(u = ast_calloc(1, sizeof(*u)))) \ if (!u) \
return -1; \ return -1; \
ast_mutex_lock(&localuser_lock); \ } while (0)
u->chan = chan; \
u->next = localusers; \
localusers = u; \
localusecnt++; \
ast_mutex_unlock(&localuser_lock); \
ast_update_use_count(); \
}
/*! /*!
* \brief Remove a localuser. * \brief Remove a localuser.
@ -336,26 +353,7 @@ struct localuser {
* This macro removes a localuser from the list of users and decrements the * This macro removes a localuser from the list of users and decrements the
* usecount. * usecount.
*/ */
#define LOCAL_USER_REMOVE(u) { \ #define LOCAL_USER_REMOVE(u) ast_localuser_remove(&me, u)
struct localuser *uc, *ul = NULL; \
ast_mutex_lock(&localuser_lock); \
uc = localusers; \
while (uc) { \
if (uc == u) { \
if (ul) \
ul->next = uc->next; \
else \
localusers = uc->next; \
break; \
} \
ul = uc; \
uc = uc->next; \
}\
free(u); \
localusecnt--; \
ast_mutex_unlock(&localuser_lock); \
ast_update_use_count(); \
}
/*! /*!
* \brief Hangup all localusers. * \brief Hangup all localusers.
@ -363,20 +361,7 @@ struct localuser {
* This macro hangs up on all current localusers and sets the usecount to zero * This macro hangs up on all current localusers and sets the usecount to zero
* when finished. * when finished.
*/ */
#define STANDARD_HANGUP_LOCALUSERS { \ #define STANDARD_HANGUP_LOCALUSERS ast_hangup_localusers(&me)
struct localuser *u, *ul; \
ast_mutex_lock(&localuser_lock); \
u = localusers; \
while(u) { \
ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD); \
ul = u; \
u = u->next; \
free(ul); \
} \
localusecnt=0; \
ast_mutex_unlock(&localuser_lock); \
ast_update_use_count(); \
}
/*! /*!
* \brief Set the specfied integer to the current usecount. * \brief Set the specfied integer to the current usecount.
@ -394,9 +379,130 @@ struct localuser {
* } * }
* \endcode * \endcode
*/ */
#define STANDARD_USECOUNT(res) { \ #define STANDARD_USECOUNT(res) do { res = me.usecnt; } while (0)
res = localusecnt; \
} /*
* XXX The following macro is deprecated, and only used by modules
* in codecs/ and a few other places which do their own manipulation
* of the usecount variable.
* Its use is supposed to be gradually phased away as those modules
* are updated to use the standard mechanism.
*/
#define OLD_STANDARD_USECOUNT(res) do { res = localusecnt; } while (0)
/*!
* \brief The following is part of the new module management code.
*
* All modules must implement the module API (load, unload...)
* whose functions are exported through fields of a "struct module_symbol";
*
* Modules exporting extra symbols (data or functions), should list
* them into an array of struct symbol_entry:
* struct symbol_entry exported_symbols[]
* of symbols, with a NULL name on the last entry
* Functions should be added with MOD_FUNC(name),
* data structures with MOD_DATA(_name).
* The array in turn is referenced by struct module_symbol.
* (Typically, a module will export only a single symbol, which points
* to a record containing all the methods. This is the API of the module,
* and should be known to the module's clients as well.
*
* Modules that require symbols supplied by other modules should
* provide an array
* struct symbol_entry required_symbols[]
* of symbols, with a NULL name on the last entry, containing the
* name of the desired symbol.
* For good measure, we also provide the size in both caller and calle
* to figure out if there is a mismatch (not terribly useful because most
* objects are a single word, but still... )
* The symbol can be added to the array with MOD_WANT(symbol) macro.
* required_symbols is also pointed by through struct module_symbol.
*
* Typically, the whole interface exported by a module should be
* in a single structure named after the module, as follows.
* Say the module high level name is 'foo', then we should have
* - in include/asterisk/foo.h
* struct foo_interface {
* int (*f)(int, char *); -- first function exported
* const char (*g)(int); -- second function exported
* char *buf;
* ... -- other fields
* }
* - in the module exporting the interface, e.g. res/res_foo.c
* static int f(int, char *);
* static const char *g(int);
* const char buf[BUFSZ];
* struct foo_interface foo = {
* .f = f,
* .g = g,
* .buf = buf,
* }
*
* NOTE: symbol names are 'global' in this module namespace, so it
* will be wiser to name exported symbols with a prefix indicating the module
* supplying it, e.g. foo_f, foo_g, foo_buf. Internally to the module,
* symbols are still static so they can keep short and meaningful names.
* The macros MOD_FIELD and METHOD_BASE() below help setting these entries.
*
* MOD_FIELD(f1), -- field and function name are the same
* METHOD_BASE(foo_, f1), -- field and function name differ by a prefix
* .f1 = function_name, -- generic case
* }
*
* Note that the loader requires that no fields of exported_symbols
* are NULL, because that is used as an indication of the end of the array.
*/
struct symbol_entry {
const char *name;
void *value;
int size;
struct module *src; /* module sourcing it, filled by loader */
};
/*
* Constructors for symbol_entry values
*/
#define MOD_FUNC(f) { .name = #f, .value = f, .size = sizeof(f) }
#define MOD_DATA(d) { .name = #d, .value = &d, .size = sizeof(_name) }
#define MOD_WANT(s) { .name = #s, .value = &s, 0 } /* required symbols */
/*
* Constructors for fields of foo_interface
*/
#define MOD_FIELD(f) . ## f = f
#define METHOD_BASE(_base, _name) . ## _name = _base ## _name
struct module_symbols {
int (*load_module)(void);
int (*unload_module)(void);
int (*usecount)(void);
char *(*description)(void);
char *(*key)(void);
int (*reload)(void);
enum module_type {
MOD_0, /* old module style */
MOD_1, /* old style, but symbols here */
MOD_2, /* new style, exported symbols */
} type;
struct symbol_entry *exported_symbols;
struct symbol_entry *required_symbols;
};
#define STD_MOD(t, exp, req) \
struct module_symbols mod_data = { \
.load_module = load_module, \
.unload_module = unload_module, \
.description = description, \
.key = key, \
.reload = reload, \
.usecount = usecount, \
.type = t, \
.exported_symbols = exp, \
.required_symbols = req \
};
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} }

@ -33,6 +33,7 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$") ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/linkedlists.h"
#include "asterisk/module.h" #include "asterisk/module.h"
#include "asterisk/options.h" #include "asterisk/options.h"
#include "asterisk/config.h" #include "asterisk/config.h"
@ -57,13 +58,29 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define RTLD_NOW 0 #define RTLD_NOW 0
#endif #endif
static int modlistver = 0; /* increase whenever the list changes, to protect reload */ static int modlistver = 0; /* increase whenever the list changes, to protect reload */
static unsigned char expected_key[] = static unsigned char expected_key[] =
{ 0x8e, 0x93, 0x22, 0x83, 0xf5, 0xc3, 0xc0, 0x75, { 0x8e, 0x93, 0x22, 0x83, 0xf5, 0xc3, 0xc0, 0x75,
0xff, 0x8b, 0xa9, 0xbe, 0x7c, 0x43, 0x74, 0x63 }; 0xff, 0x8b, 0xa9, 0xbe, 0x7c, 0x43, 0x74, 0x63 };
/*
* Modules can be in a number of different states, as below:
* MS_FAILED attempt to load failed. This is final.
* MS_NEW just added to the list, symbols unresolved.
* MS_RESOLVED all symbols resolved, but supplier modules not active yet.
* MS_CANLOAD all symbols resolved and suppliers are all active
* (or we are in a cyclic dependency and we are breaking a loop)
* MS_ACTIVE load() returned successfully.
*/
enum st_t { /* possible states of a module */
MS_FAILED = 0, /* cannot load */
MS_NEW = 1, /* nothing known */
MS_RESOLVED = 2, /* all required resolved */
MS_CANLOAD = 3, /* as above, plus cyclic depend.*/
MS_ACTIVE = 4, /* all done */
};
/* /*
* All module symbols are in module_symbols. * All module symbols are in module_symbols.
* Modules are then linked in a list of struct module, * Modules are then linked in a list of struct module,
@ -74,21 +91,18 @@ static unsigned char expected_key[] =
* *
* A second lock, reloadlock, is used to prevent concurrent reloads * A second lock, reloadlock, is used to prevent concurrent reloads
*/ */
struct module_symbols {
int (*load_module)(void);
int (*unload_module)(void);
int (*usecount)(void);
char *(*description)(void);
char *(*key)(void);
int (*reload)(void);
};
struct module { struct module {
AST_LIST_ENTRY(module) next; AST_LIST_ENTRY(module) next;
struct module_symbols cb; struct module_symbols cb;
void *lib; /* the shared lib */ void *lib; /* the shared lib */
char resource[256]; char resource[256];
enum st_t state;
int export_refcount; /* how many users of exported symbols */
ast_mutex_t mtx; /* XXX use yet ? */
struct localuser *lu;
int localusecnt;
}; };
@ -101,6 +115,340 @@ static AST_LIST_HEAD_STATIC(module_list, module);
static AST_LIST_HEAD_STATIC(updaters, loadupdate); static AST_LIST_HEAD_STATIC(updaters, loadupdate);
AST_MUTEX_DEFINE_STATIC(reloadlock); AST_MUTEX_DEFINE_STATIC(reloadlock);
/*
* helper localuser routines.
* All of these routines are extremely expensive, so the use of
* macros is totally unnecessary from the point of view of performance:
* the extra function call will be totally negligible in all cases.
*/
struct localuser *ast_localuser_add(struct ast_module_lock *m,
struct ast_channel *chan)
{
struct localuser *u = ast_calloc(1, sizeof(*u));
if (u == NULL)
return NULL;
u->chan = chan;
ast_mutex_lock(&m->lock);
AST_LIST_INSERT_HEAD(&m->u, u, next);
m->usecnt++;
ast_mutex_unlock(&m->lock);
ast_update_use_count();
return u;
}
void ast_localuser_remove(struct ast_module_lock *m, struct localuser *u)
{
ast_mutex_lock(&m->lock);
AST_LIST_REMOVE(&m->u, u, next);
m->usecnt--;
free(u);
ast_mutex_unlock(&m->lock);
ast_update_use_count();
}
void ast_hangup_localusers(struct ast_module_lock *m)
{
struct localuser *u;
ast_mutex_lock(&m->lock);
AST_LIST_TRAVERSE_SAFE_BEGIN(&m->u, u, next) {
ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
free(u);
AST_LIST_REMOVE_CURRENT(&m->u, next);
}
AST_LIST_TRAVERSE_SAFE_END
m->usecnt = 0;
ast_mutex_unlock(&m->lock);
ast_update_use_count();
}
/*--- new-style loader routines ---*/
/*
* For backward compatibility, we have 3 types of loadable modules:
*
* MOD_0 these are the 'old style' modules, which export a number
* of callbacks, and their full interface, as globally visible
* symbols. The module needs to be loaded with RTLD_LAZY and
* RTLD_GLOBAL to make symbols visible to other modules, and
* to avoid load failures due to cross dependencies.
*
* MOD_1 almost as above, but the generic callbacks are all into a
* a structure, mod_data. Same load requirements as above.
*
* MOD_2 this is the 'new style' format for modules. The module must
* explictly declare which simbols are exported and which
* symbols from other modules are used, and the code in this
* loader will implement appropriate checks to load the modules
* in the correct order. Also this allows to load modules
* with RTLD_NOW and RTLD_LOCAL so there is no chance of run-time
* bugs due to unresolved symbols or name conflicts.
*/
/*
* helper routine to print the symbolic name associated to a state
*/
static const char *st_name(enum st_t state)
{
/* try to resolve required symbols */
const char *st;
switch (state) {
#define ST(x) case x: st = # x; break;
ST(MS_NEW);
ST(MS_FAILED);
ST(MS_RESOLVED);
ST(MS_ACTIVE);
ST(MS_CANLOAD);
default:
st = "unknown";
}
return st;
#undef ST
}
/*! \brief
* Fetch/release an exported symbol - modify export_refcount by delta
* \param delta 1 to fetch a symbol, -1 to release it.
* on success, return symbol value.
* Note, modules in MS_FAIL will never match in a 'get' request.
* If src is non-NULL, on exit *src points to the source module.
*
* Must be called with the lock held.
*/
static void *module_symbol_helper(const char *name,
int delta, struct module **src)
{
void *ret = NULL;
struct module *m;
AST_LIST_TRAVERSE(&module_list, m, next) {
struct symbol_entry *es;
if (delta > 0 && m->state == MS_FAILED)
continue; /* cannot 'get' a symbol from a failed module */
for (es = m->cb.exported_symbols; ret == NULL && es && es->name; es++) {
if (!strcmp(es->name, name)) {
ret = es->value;
m->export_refcount += delta;
if (src)
*src = m;
break;
}
}
if (ret)
break;
}
if (ret == NULL)
ast_log(LOG_WARNING, "symbol %s not found\n", name);
return ret;
}
static void *release_module_symbol(const char *name)
{
return module_symbol_helper(name, -1, NULL);
}
static void *get_module_symbol(const char *name, struct module **src)
{
return module_symbol_helper(name, +1, src);
}
/*!
* \brief Release refcounts to all imported symbols,
* and change module state to MS_FAILED.
*/
static void release_module(struct module *m)
{
struct symbol_entry *s;
for (s = m->cb.required_symbols; s && s->name != NULL; s++) {
if (s->value != NULL) {
release_module_symbol(s->name);
s->value = NULL;
}
}
m->state = MS_FAILED;
}
/*! \brief check that no NULL symbols are exported - the algorithms rely on that. */
static int check_exported(struct module *m)
{
struct symbol_entry *es = m->cb.exported_symbols;
int errors = 0;
if (es == NULL)
return 0;
ast_log(LOG_WARNING, "module %s exports the following symbols\n",
es->name);
for (; es->name; es++) {
void **p = es->value;
int i;
ast_log(LOG_WARNING, "\taddr %p size %8d %s\n",
es->value, es->size, es->name);
for (i = 0; i < es->size / sizeof(void *); i++, p++) {
if (*p == NULL) {
ast_log(LOG_WARNING, "\t *** null field at offset %d\n", i);
errors++;
}
}
}
return errors;
}
/*!
* \brief Resolve symbols and change state accordingly.
* Return 1 if state changed, 0 otherwise.
* If MS_FAILED, MS_ACTIVE or MS_CANLOAD there is nothing to do.
* If a symbol cannot be resolved (no supplier or supplier in MS_FAIL),
* move to MS_FAIL and release all symbols;
* If all suppliers are MS_ACTIVE, move to MS_CANLOAD
* otherwise move to MS_RESOLVED.
*/
static int resolve(struct module *m)
{
struct symbol_entry *s;
if (m->state == MS_FAILED || m->state == MS_ACTIVE || m->state == MS_CANLOAD)
return 0; /* already decided what to do */
/* now it's either MS_NEW or MS_RESOLVED.
* Be optimistic and put it in MS_CANLOAD, then try to
* resolve and verify symbols, and downgrade as appropriate.
*/
m->state = MS_CANLOAD;
for (s = m->cb.required_symbols; s && s->name != NULL; s++) {
void **p = (void **)(s->value);
if (*p == NULL) /* symbol not resolved yet */
*p = get_module_symbol(s->name, &s->src);
if (*p == NULL || s->src->state == MS_FAILED) { /* fail */
ast_log(LOG_WARNING,
"Unresolved symbol %s for module %s\n",
s->name, m->resource);
release_module(m); /* and set to MS_FAILED */
break;
}
if (s->src->state != MS_ACTIVE)
m->state = MS_RESOLVED; /* downgrade */
}
return 1;
}
/*
* Fixup references and load modules according to their dependency order.
* Called when new modules are added to the list.
* The algorithm is as follows:
* - all modules MS_FAILED are changed to MS_NEW, in case something
* happened that could help them.
* - first try to resolve symbols. If successful, change the
* module's state to MS_RESOLVED otherwise to MS_FAILED
* - repeat on all modules until there is progress:
* . if it is MS_ACTIVE or MS_FAILED, continue (no progress)
* . if one has all required modules in MS_ACTIVE, try to load it.
* If successful it becomes MS_ACTIVE itself, otherwise
* MS_FAILED and releases all symbols.
* In any case, we have progress.
* . if one of the dependencies is MS_FAILED, release and set to
* MS_FAILED here too. We have progress.
* - if we have no progress there is a cyclic dependency.
* Take first and change to MS_CANLOAD, i.e. as if all required are
* MS_ACTIVE. we have progress, so repeat.
* NOTE:
* must be called with lock held
* recursive calls simply return success.
*/
static int fixup(const char *caller)
{
struct module *m;
int total = 0, new = 0, cycle = 0;
static int in_fixup = 0; /* disable recursive calls */
if (in_fixup)
return 0;
in_fixup++;
AST_LIST_TRAVERSE(&module_list, m, next) {
total++;
if (m->state == MS_FAILED)
m->state = MS_NEW;
if (m->state == MS_NEW)
new++;
/* print some debugging info for new modules */
if (m->state == MS_NEW &&
(m->cb.exported_symbols || m->cb.required_symbols))
ast_log(LOG_WARNING,
"module %-30s exports %p requires %p state %s(%d)\n",
m->resource, m->cb.exported_symbols,
m->cb.required_symbols,
st_name(m->state), m->state);
}
ast_log(LOG_WARNING, "---- fixup (%s): %d modules, %d new ---\n",
caller, total, new);
for (;;cycle++) {
int again = 0; /* set if we need another round */
ast_log(LOG_WARNING, "---- fixup: cycle %d ---\n", cycle);
AST_LIST_TRAVERSE(&module_list, m, next) {
if (resolve(m))
again = 1; /* something changed */
if (m->state != MS_CANLOAD) /* for now, done with this module */
continue;
/* try to run the load routine */
if (m->cb.load_module()) { /* error */
ast_log(LOG_WARNING, "load_module %s fail\n",
m->resource);
release_module(m); /* and set to MS_FAIL */
} else {
ast_log(LOG_WARNING, "load_module %s success\n",
m->resource);
m->state = MS_ACTIVE;
}
again = 1; /* something has changed */
}
/* Modules in MS_RESOLVED mean a possible cyclic dependency.
* Break the indecision by setting one to CANLOAD, and repeat.
*/
AST_LIST_TRAVERSE(&module_list, m, next) {
if (m->state == MS_RESOLVED) {
m->state = MS_CANLOAD;
again = 1;
break;
}
}
if (!again) /* we are done */
break;
}
ast_log(LOG_WARNING, "---- fixup complete ---\n");
in_fixup--;
return 0;
}
/* test routines to see which modules depend on global symbols
* exported by other modules.
*/
static void check_symbols(void)
{
struct dirent *d;
DIR *mods = opendir(ast_config_AST_MODULE_DIR);
void *lib;
char buf[1024];
ast_log(LOG_WARNING, "module dir <%s>\n", ast_config_AST_MODULE_DIR);
if (!mods)
return;
while((d = readdir(mods))) {
int ld = strlen(d->d_name);
/* Must end in .so to load it. */
if (ld <= 3 || strcasecmp(d->d_name + ld - 3, ".so"))
continue;
snprintf(buf, sizeof(buf), "%s/%s", ast_config_AST_MODULE_DIR, d->d_name);
lib = dlopen(buf, RTLD_NOW | RTLD_LOCAL);
if (lib == NULL) {
ast_log(LOG_WARNING, "(notice only) module %s error %s\n", d->d_name, dlerror());
}
dlclose(lib);
}
}
/*--- end new-style routines ---*/
/* /*
* In addition to modules, the reload command handles some extra keywords * In addition to modules, the reload command handles some extra keywords
* which are listed here together with the corresponding handlers. * which are listed here together with the corresponding handlers.
@ -184,6 +532,7 @@ int ast_unload_resource(const char *resource_name, int force)
} else } else
ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n"); ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
} }
release_module(cur); /* XXX */
AST_LIST_REMOVE_CURRENT(&module_list, next); AST_LIST_REMOVE_CURRENT(&module_list, next);
dlclose(cur->lib); dlclose(cur->lib);
free(cur); free(cur);
@ -224,6 +573,24 @@ char *ast_module_helper(const char *line, const char *word, int pos, int state,
return ret; return ret;
} }
/*!
* \brief Given a function address, find the corresponding module.
* This is required as a workaround to the fact that we do not
* have a module argument to the load_module() function.
* Hopefully the performance implications are small.
*/
struct module *ast_find_module(int (*load_fn)(void))
{
struct module *cur;
AST_LIST_LOCK(&module_list);
AST_LIST_TRAVERSE(&module_list, cur, next) {
if (cur->cb.load_module == load_fn)
break;
}
AST_LIST_UNLOCK(&module_list);
return cur;
}
int ast_module_reload(const char *name) int ast_module_reload(const char *name)
{ {
struct module *cur; struct module *cur;
@ -307,13 +674,14 @@ static void *find_symbol(struct module *m, const char *name, int verbose)
} }
/* XXX cfg is only used for !res_* and #ifdef RTLD_GLOBAL */ /* XXX cfg is only used for !res_* and #ifdef RTLD_GLOBAL */
static int __load_resource(const char *resource_name, const struct ast_config *cfg) static struct module * __load_resource(const char *resource_name,
const struct ast_config *cfg)
{ {
static char fn[256]; static char fn[256];
int errors=0; int errors=0;
int res; int res;
struct module *cur; struct module *cur;
struct module_symbols *m; struct module_symbols *m, *m1;
int flags=RTLD_NOW; int flags=RTLD_NOW;
unsigned char *key; unsigned char *key;
char tmp[80]; char tmp[80];
@ -341,11 +709,11 @@ static int __load_resource(const char *resource_name, const struct ast_config *c
if (resource_exists(resource_name, 0)) { if (resource_exists(resource_name, 0)) {
ast_log(LOG_WARNING, "Module '%s' already exists\n", resource_name); ast_log(LOG_WARNING, "Module '%s' already exists\n", resource_name);
AST_LIST_UNLOCK(&module_list); AST_LIST_UNLOCK(&module_list);
return -1; return NULL;
} }
if (!(cur = ast_calloc(1, sizeof(*cur)))) { if (!(cur = ast_calloc(1, sizeof(*cur)))) {
AST_LIST_UNLOCK(&module_list); AST_LIST_UNLOCK(&module_list);
return -1; return NULL;
} }
m = &cur->cb; m = &cur->cb;
ast_copy_string(cur->resource, resource_name, sizeof(cur->resource)); ast_copy_string(cur->resource, resource_name, sizeof(cur->resource));
@ -353,29 +721,47 @@ static int __load_resource(const char *resource_name, const struct ast_config *c
ast_copy_string(fn, resource_name, sizeof(fn)); ast_copy_string(fn, resource_name, sizeof(fn));
else else
snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_MODULE_DIR, resource_name); snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_MODULE_DIR, resource_name);
#if 0
/* XXX test, open in a sane way */
cur->lib = dlopen(fn, RTLD_NOW | RTLD_LOCAL);
if (cur->lib == NULL) {
ast_log(LOG_WARNING, "test %s\n", dlerror());
} else
dlclose(cur->lib);
#endif
cur->lib = dlopen(fn, flags); cur->lib = dlopen(fn, flags);
if (!cur->lib) { if (!cur->lib) {
ast_log(LOG_WARNING, "%s\n", dlerror()); ast_log(LOG_WARNING, "%s\n", dlerror());
free(cur); free(cur);
AST_LIST_UNLOCK(&module_list); AST_LIST_UNLOCK(&module_list);
return -1; return NULL;
}
m1 = find_symbol(cur, "mod_data", 0);
if (m1 != NULL) { /* new style module */
errors = check_exported(cur);
*m = *m1;
if (m->type == MOD_2)
ast_log(LOG_WARNING, "new style %s, should unload and reload with RTLD_LOCAL\n", resource_name);
} else {
m->type = MOD_0;
m->load_module = find_symbol(cur, "load_module", 1);
m->unload_module = find_symbol(cur, "unload_module", 1);
m->usecount = find_symbol(cur, "usecount", 1);
m->description = find_symbol(cur, "description", 1);
m->key = find_symbol(cur, "key", 1);
m->reload = find_symbol(cur, "reload", 0);
} }
m->load_module = find_symbol(cur, "load_module", 1);
if (!m->load_module) if (!m->load_module)
errors++; errors++;
m->unload_module = find_symbol(cur, "unload_module", 1);
if (!m->unload_module) if (!m->unload_module)
errors++; errors++;
m->usecount = find_symbol(cur, "usecount", 1);
if (!m->usecount) if (!m->usecount)
errors++; errors++;
m->description = find_symbol(cur, "description", 1);
if (!m->description) if (!m->description)
errors++; errors++;
m->key = find_symbol(cur, "key", 1);
if (!m->key) if (!m->key)
errors++; errors++;
m->reload = find_symbol(cur, "reload", 0);
if (!m->key || !(key = (unsigned char *) m->key())) { if (!m->key || !(key = (unsigned char *) m->key())) {
ast_log(LOG_WARNING, "Key routine returned NULL in module %s\n", fn); ast_log(LOG_WARNING, "Key routine returned NULL in module %s\n", fn);
key = NULL; key = NULL;
@ -390,7 +776,7 @@ static int __load_resource(const char *resource_name, const struct ast_config *c
dlclose(cur->lib); dlclose(cur->lib);
free(cur); free(cur);
AST_LIST_UNLOCK(&module_list); AST_LIST_UNLOCK(&module_list);
return -1; return NULL;
} }
if (!ast_fully_booted) { if (!ast_fully_booted) {
if (option_verbose) if (option_verbose)
@ -407,30 +793,64 @@ static int __load_resource(const char *resource_name, const struct ast_config *c
so reload commands will be issued in same order modules were loaded */ so reload commands will be issued in same order modules were loaded */
modlistver++; modlistver++;
if (m->type == MOD_2) {
ast_log(LOG_WARNING, "new-style module %s, deferring load()\n",
resource_name);
cur->state = MS_NEW;
} else
cur->state = MS_CANLOAD;
/* XXX make sure the usecount is 1 before releasing the lock */
AST_LIST_UNLOCK(&module_list); AST_LIST_UNLOCK(&module_list);
if ((res = m->load_module())) {
if (cur->state == MS_CANLOAD && (res = m->load_module())) {
ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", resource_name, res); ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", resource_name, res);
ast_unload_resource(resource_name, 0); ast_unload_resource(resource_name, 0);
return -1; return NULL;
} }
cur->state = MS_ACTIVE;
ast_update_use_count(); ast_update_use_count();
return 0; return cur;
} }
/*
* load a single module (API call).
* (recursive calls from load_module() succeed.
* Returns 0 on success, -1 on error.
*/
int ast_load_resource(const char *resource_name) int ast_load_resource(const char *resource_name)
{ {
int res, o = option_verbose; int o = option_verbose;
struct ast_config *cfg = NULL; struct ast_config *cfg = NULL;
struct module *m;
option_verbose = 0; /* Keep the module file parsing silent */ option_verbose = 0; /* Keep the module file parsing silent */
cfg = ast_config_load(AST_MODULE_CONFIG); cfg = ast_config_load(AST_MODULE_CONFIG);
option_verbose = o; /* restore verbosity */ option_verbose = o; /* restore verbosity */
res = __load_resource(resource_name, cfg); m = __load_resource(resource_name, cfg);
if (cfg) if (cfg)
ast_config_destroy(cfg); ast_config_destroy(cfg);
return res; return m ? 0 : -1;
} }
#if 0
+/*
+ * load a single module (API call).
+ * (recursive calls from load_module() succeed.
+ */
+int ast_load_resource(const char *resource_name)
+{
+ struct module *m;
+ int ret;
+
+ ast_mutex_lock(&modlock);
+ m = __load_resource(resource_name, 0);
+ fixup(resource_name);
+ ret = (m->state == MS_FAILED) ? -1 : 0;
+ ast_mutex_unlock(&modlock);
+ return ret;
+}
#endif
/* if enabled, log and output on console the module's name, and try load it */ /* if enabled, log and output on console the module's name, and try load it */
static int print_and_load(const char *s, struct ast_config *cfg) static int print_and_load(const char *s, struct ast_config *cfg)
{ {
@ -443,7 +863,7 @@ static int print_and_load(const char *s, struct ast_config *cfg)
term_color(tmp, s, COLOR_BRWHITE, 0, sizeof(tmp))); term_color(tmp, s, COLOR_BRWHITE, 0, sizeof(tmp)));
fflush(stdout); fflush(stdout);
} }
if (!__load_resource(s, cfg)) if (__load_resource(s, cfg))
return 0; /* success */ return 0; /* success */
ast_log(LOG_WARNING, "Loading module %s failed!\n", s); ast_log(LOG_WARNING, "Loading module %s failed!\n", s);
return -1; return -1;
@ -460,7 +880,6 @@ static const char *loadorder[] =
int load_modules(const int preload_only) int load_modules(const int preload_only)
{ {
struct ast_config *cfg; struct ast_config *cfg;
struct ast_variable *v;
int x; int x;
if (option_verbose) { if (option_verbose) {
@ -469,9 +888,14 @@ int load_modules(const int preload_only)
"Asterisk Dynamic Loader Starting:\n"); "Asterisk Dynamic Loader Starting:\n");
} }
if (0)
check_symbols();
cfg = ast_config_load(AST_MODULE_CONFIG); cfg = ast_config_load(AST_MODULE_CONFIG);
if (cfg) { if (cfg) {
const char *cmd = preload_only ? "preload" : "load"; const char *cmd = preload_only ? "preload" : "load";
struct ast_variable *v;
/* Load explicitly defined modules */ /* Load explicitly defined modules */
for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) { for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
if (strcasecmp(v->name, cmd)) /* not what we are looking for */ if (strcasecmp(v->name, cmd)) /* not what we are looking for */
@ -483,16 +907,12 @@ int load_modules(const int preload_only)
} }
} }
if (preload_only) { if (preload_only)
ast_config_destroy(cfg); goto done;
return 0;
}
if (cfg && !ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) { if (cfg && !ast_true(ast_variable_retrieve(cfg, "modules", "autoload")))
/* no autoload */ /* no autoload */
ast_config_destroy(cfg); goto done;
return 0;
}
/* /*
* Load all modules. To help resolving dependencies, we load modules * Load all modules. To help resolving dependencies, we load modules
* in the order defined by loadorder[], with the final step for * in the order defined by loadorder[], with the final step for
@ -519,9 +939,10 @@ int load_modules(const int preload_only)
!strcasecmp(d->d_name + ld - 3, ".so") && !strcasecmp(d->d_name + ld - 3, ".so") &&
!resource_exists(d->d_name, 1)) { !resource_exists(d->d_name, 1)) {
/* It's a shared library, check if we are allowed to load it /* It's a shared library, check if we are allowed to load it
* (very inefficient, but oh well. * (very inefficient, but oh well).
*/ */
if (cfg) { if (cfg) {
struct ast_variable *v;
for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) { for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
if (!strcasecmp(v->name, "noload") && if (!strcasecmp(v->name, "noload") &&
!strcasecmp(v->value, d->d_name)) !strcasecmp(v->value, d->d_name))
@ -545,6 +966,8 @@ int load_modules(const int preload_only)
} }
closedir(mods); closedir(mods);
} }
done:
fixup("load_modules");
ast_config_destroy(cfg); ast_config_destroy(cfg);
return 0; return 0;
} }
@ -559,7 +982,6 @@ void ast_update_use_count(void)
AST_LIST_TRAVERSE(&updaters, m, next) AST_LIST_TRAVERSE(&updaters, m, next)
m->updater(); m->updater();
AST_LIST_UNLOCK(&module_list); AST_LIST_UNLOCK(&module_list);
} }
int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like), int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),

@ -740,12 +740,6 @@ int unload_module(void)
ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy); ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces); ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
/*
* localusers = NULL; is just to silence the compiler warning
* about an unused variable. It will be removed soon, when the
* LOCALUSER-related functions are rewritten.
*/
localusers = NULL;
return 0; return 0;
} }

Loading…
Cancel
Save