mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-02 03:48:02 +00:00
This rather large commit changes the way modules are loaded.
As partly documented in loader.c and include/asterisk/module.h, modules are now expected to return all of their methods and flags into a structure 'mod_data', and are normally loaded with RTLD_NOW | RTLD_LOCAL, so symbols are resolved immediately and conflicts should be less likely. Only in a small number of cases (res_*, typically) modules are loaded RTLD_GLOBAL, so they can export symbols. The core of the change is only the two files loader.c and include/asterisk/module.h, all the rest is simply adaptation of the existing modules to the new API, a rather mechanical (but believe me, time and finger-consuming!) process whose detail you can figure out by svn diff'ing any single module. Expect some minor compilation issue after this change, please report it on mantis http://bugs.digium.com/view.php?id=6968 so we collect all the feedback in one place. I am just sorry that this change missed SVN version number 20000! git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@20003 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
168
loader.c
168
loader.c
@@ -30,6 +30,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MOD_LOADER /* prevent some module-specific stuff from being compiled */
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
@@ -94,7 +95,7 @@ enum st_t { /* possible states of a module */
|
||||
*/
|
||||
struct module {
|
||||
AST_LIST_ENTRY(module) next;
|
||||
struct module_symbols cb;
|
||||
struct module_symbols *cb;
|
||||
void *lib; /* the shared lib */
|
||||
char resource[256];
|
||||
|
||||
@@ -118,43 +119,53 @@ AST_MUTEX_DEFINE_STATIC(reloadlock);
|
||||
* the extra function call will be totally negligible in all cases.
|
||||
*/
|
||||
|
||||
struct localuser *ast_localuser_add(struct ast_module_lock *m,
|
||||
struct localuser *ast_localuser_add(struct module_symbols *me,
|
||||
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_mutex_lock(&me->lock);
|
||||
u->next = me->lu_head;
|
||||
me->lu_head = u;
|
||||
ast_mutex_unlock(&me->lock);
|
||||
ast_atomic_fetchadd_int(&me->usecnt, +1);
|
||||
ast_update_use_count();
|
||||
return u;
|
||||
}
|
||||
|
||||
void ast_localuser_remove(struct ast_module_lock *m, struct localuser *u)
|
||||
void ast_localuser_remove(struct module_symbols *me, struct localuser *u)
|
||||
{
|
||||
ast_mutex_lock(&m->lock);
|
||||
AST_LIST_REMOVE(&m->u, u, next);
|
||||
m->usecnt--;
|
||||
struct localuser *x, *prev = NULL;
|
||||
ast_mutex_lock(&me->lock);
|
||||
/* unlink from the list */
|
||||
for (x = me->lu_head; x; prev = x, x = x->next) {
|
||||
if (x == u) {
|
||||
if (prev)
|
||||
prev->next = x->next;
|
||||
else
|
||||
me->lu_head = x->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ast_mutex_unlock(&me->lock);
|
||||
ast_atomic_fetchadd_int(&me->usecnt, -1);
|
||||
free(u);
|
||||
ast_mutex_unlock(&m->lock);
|
||||
ast_update_use_count();
|
||||
}
|
||||
|
||||
void ast_hangup_localusers(struct ast_module_lock *m)
|
||||
void ast_hangup_localusers(struct module_symbols *me)
|
||||
{
|
||||
struct localuser *u;
|
||||
ast_mutex_lock(&m->lock);
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&m->u, u, next) {
|
||||
struct localuser *u, *next;
|
||||
ast_mutex_lock(&me->lock);
|
||||
for (u = me->lu_head; u; u = next) {
|
||||
next = u->next;
|
||||
ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
|
||||
ast_atomic_fetchadd_int(&me->usecnt, -1);
|
||||
free(u);
|
||||
AST_LIST_REMOVE_CURRENT(&m->u, next);
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END
|
||||
m->usecnt = 0;
|
||||
ast_mutex_unlock(&m->lock);
|
||||
ast_mutex_unlock(&me->lock);
|
||||
ast_update_use_count();
|
||||
}
|
||||
|
||||
@@ -169,8 +180,7 @@ void ast_hangup_localusers(struct ast_module_lock *m)
|
||||
* 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_1 The generic callbacks are all into a structure, mod_data.
|
||||
*
|
||||
* MOD_2 this is the 'new style' format for modules. The module must
|
||||
* explictly declare which simbols are exported and which
|
||||
@@ -221,7 +231,7 @@ static void *module_symbol_helper(const char *name,
|
||||
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++) {
|
||||
for (es = m->cb->exported_symbols; ret == NULL && es && es->name; es++) {
|
||||
if (!strcmp(es->name, name)) {
|
||||
ret = es->value;
|
||||
m->export_refcount += delta;
|
||||
@@ -256,7 +266,7 @@ static void release_module(struct module *m)
|
||||
{
|
||||
struct symbol_entry *s;
|
||||
|
||||
for (s = m->cb.required_symbols; s && s->name != NULL; s++) {
|
||||
for (s = m->cb->required_symbols; s && s->name != NULL; s++) {
|
||||
if (s->value != NULL) {
|
||||
release_module_symbol(s->name);
|
||||
s->value = NULL;
|
||||
@@ -268,7 +278,7 @@ static void release_module(struct module *m)
|
||||
/*! \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;
|
||||
struct symbol_entry *es = m->cb->exported_symbols;
|
||||
int errors = 0;
|
||||
|
||||
if (es == NULL)
|
||||
@@ -311,7 +321,7 @@ static int resolve(struct module *m)
|
||||
* resolve and verify symbols, and downgrade as appropriate.
|
||||
*/
|
||||
m->state = MS_CANLOAD;
|
||||
for (s = m->cb.required_symbols; s && s->name != NULL; s++) {
|
||||
for (s = m->cb->required_symbols; s && s->name != NULL; s++) {
|
||||
void **p = (void **)(s->value);
|
||||
|
||||
if (*p == NULL) /* symbol not resolved yet */
|
||||
@@ -369,11 +379,11 @@ static int fixup(const char *caller)
|
||||
new++;
|
||||
/* print some debugging info for new modules */
|
||||
if (m->state == MS_NEW &&
|
||||
(m->cb.exported_symbols || m->cb.required_symbols))
|
||||
(m->cb->exported_symbols || m->cb->required_symbols))
|
||||
ast_log(LOG_NOTICE,
|
||||
"module %-30s exports %p requires %p state %s(%d)\n",
|
||||
m->resource, m->cb.exported_symbols,
|
||||
m->cb.required_symbols,
|
||||
m->resource, m->cb->exported_symbols,
|
||||
m->cb->required_symbols,
|
||||
st_name(m->state), m->state);
|
||||
}
|
||||
ast_log(LOG_DEBUG, "---- fixup (%s): %d modules, %d new ---\n",
|
||||
@@ -388,7 +398,7 @@ static int fixup(const char *caller)
|
||||
if (m->state != MS_CANLOAD) /* for now, done with this module */
|
||||
continue;
|
||||
/* try to run the load routine */
|
||||
if (m->cb.load_module()) { /* error */
|
||||
if (m->cb->load_module(m)) { /* error */
|
||||
ast_log(LOG_WARNING, "load_module %s fail\n",
|
||||
m->resource);
|
||||
release_module(m); /* and set to MS_FAIL */
|
||||
@@ -498,7 +508,7 @@ static int verify_key(const unsigned char *key)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ast_unload_resource(const char *resource_name, int force)
|
||||
int ast_unload_resource(const char *resource_name, enum unload_mode force)
|
||||
{
|
||||
struct module *cur;
|
||||
int res = -1;
|
||||
@@ -506,11 +516,11 @@ int ast_unload_resource(const char *resource_name, int force)
|
||||
if (AST_LIST_LOCK(&module_list)) /* XXX should fail here ? */
|
||||
ast_log(LOG_WARNING, "Failed to lock\n");
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&module_list, cur, next) {
|
||||
struct module_symbols *m = &cur->cb;
|
||||
struct module_symbols *m = cur->cb;
|
||||
|
||||
if (strcasecmp(cur->resource, resource_name)) /* not us */
|
||||
continue;
|
||||
if ((res = m->usecount()) > 0) {
|
||||
if (m->usecnt > 0 || m->flags & NO_UNLOAD) {
|
||||
if (force)
|
||||
ast_log(LOG_WARNING, "Warning: Forcing removal of module %s with use count %d\n", resource_name, res);
|
||||
else {
|
||||
@@ -519,7 +529,8 @@ int ast_unload_resource(const char *resource_name, int force)
|
||||
break;
|
||||
}
|
||||
}
|
||||
res = m->unload_module();
|
||||
ast_hangup_localusers(m);
|
||||
res = m->unload_module(m);
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
|
||||
if (force <= AST_FORCE_FIRM) {
|
||||
@@ -553,7 +564,7 @@ char *ast_module_helper(const char *line, const char *word, int pos, int state,
|
||||
return NULL;
|
||||
AST_LIST_LOCK(&module_list);
|
||||
AST_LIST_TRAVERSE(&module_list, cur, next) {
|
||||
if (!strncasecmp(word, cur->resource, l) && (cur->cb.reload || !needsreload) &&
|
||||
if (!strncasecmp(word, cur->resource, l) && (cur->cb->reload || !needsreload) &&
|
||||
++which > state) {
|
||||
ret = strdup(cur->resource);
|
||||
break;
|
||||
@@ -574,7 +585,6 @@ int ast_module_reload(const char *name)
|
||||
struct module *cur;
|
||||
int res = 0; /* return value. 0 = not found, others, see below */
|
||||
int i, oldversion;
|
||||
int (*reload)(void);
|
||||
|
||||
if (ast_mutex_trylock(&reloadlock)) {
|
||||
ast_verbose("The previous reload command didn't finish yet\n");
|
||||
@@ -592,11 +602,10 @@ int ast_module_reload(const char *name)
|
||||
AST_LIST_LOCK(&module_list);
|
||||
oldversion = modlistver;
|
||||
AST_LIST_TRAVERSE(&module_list, cur, next) {
|
||||
struct module_symbols *m = &cur->cb;
|
||||
struct module_symbols *m = cur->cb;
|
||||
if (name && strcasecmp(name, cur->resource)) /* not ours */
|
||||
continue;
|
||||
reload = m->reload;
|
||||
if (!reload) { /* cannot be reloaded */
|
||||
if (!m->reload) { /* cannot be reloaded */
|
||||
if (res < 1) /* store result if possible */
|
||||
res = 1; /* 1 = no reload() method */
|
||||
continue;
|
||||
@@ -606,7 +615,7 @@ int ast_module_reload(const char *name)
|
||||
res = 2;
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", cur->resource, m->description());
|
||||
reload();
|
||||
m->reload(m);
|
||||
AST_LIST_LOCK(&module_list);
|
||||
if (oldversion != modlistver) /* something changed, abort */
|
||||
break;
|
||||
@@ -659,7 +668,7 @@ static struct module * __load_resource(const char *resource_name,
|
||||
int errors=0;
|
||||
int res;
|
||||
struct module *cur;
|
||||
struct module_symbols *m, *m1;
|
||||
struct module_symbols *m = NULL;
|
||||
int flags = RTLD_NOW;
|
||||
unsigned char *key;
|
||||
char tmp[80];
|
||||
@@ -687,7 +696,6 @@ static struct module * __load_resource(const char *resource_name,
|
||||
AST_LIST_UNLOCK(&module_list);
|
||||
return NULL;
|
||||
}
|
||||
m = &cur->cb;
|
||||
ast_copy_string(cur->resource, resource_name, sizeof(cur->resource));
|
||||
if (resource_name[0] == '/')
|
||||
ast_copy_string(fn, resource_name, sizeof(fn));
|
||||
@@ -697,11 +705,13 @@ static struct module * __load_resource(const char *resource_name,
|
||||
/* open in a sane way */
|
||||
cur->lib = dlopen(fn, RTLD_NOW | RTLD_LOCAL);
|
||||
if (cur->lib) {
|
||||
if ((m1 = find_symbol(cur, "mod_data", 0)) == NULL || m1->type == MOD_0) {
|
||||
if ((m = find_symbol(cur, "mod_data", 0)) == NULL ||
|
||||
(m->flags & MOD_MASK) == MOD_0) {
|
||||
/* old-style module, close and reload with standard flags */
|
||||
dlclose(cur->lib);
|
||||
cur->lib = NULL;
|
||||
}
|
||||
m = NULL;
|
||||
}
|
||||
if (cur->lib == NULL) /* try reopen with the old style */
|
||||
cur->lib = dlopen(fn, flags);
|
||||
@@ -712,26 +722,21 @@ static struct module * __load_resource(const char *resource_name,
|
||||
AST_LIST_UNLOCK(&module_list);
|
||||
return NULL;
|
||||
}
|
||||
m1 = find_symbol(cur, "mod_data", 0);
|
||||
if (m1 != NULL) { /* new style module */
|
||||
ast_log(LOG_WARNING, "new style %s (%d) loaded RTLD_LOCAL\n",
|
||||
resource_name, m1->type);
|
||||
if (m == NULL) /* MOD_0 modules may still have a mod_data entry */
|
||||
m = find_symbol(cur, "mod_data", 0);
|
||||
if (m != NULL) { /* new style module */
|
||||
ast_log(LOG_WARNING, "new style %s (0x%x) loaded RTLD_LOCAL\n",
|
||||
resource_name, m->flags);
|
||||
cur->cb = m; /* use the mod_data from the module itself */
|
||||
errors = check_exported(cur);
|
||||
*m = *m1;
|
||||
} 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);
|
||||
ast_log(LOG_WARNING, "misstng mod_data for %s\n",
|
||||
resource_name);
|
||||
errors++;
|
||||
}
|
||||
if (!m->load_module)
|
||||
errors++;
|
||||
if (!m->unload_module)
|
||||
errors++;
|
||||
if (!m->usecount)
|
||||
if (!m->unload_module && !(m->flags & NO_UNLOAD) )
|
||||
errors++;
|
||||
if (!m->description)
|
||||
errors++;
|
||||
@@ -753,6 +758,10 @@ static struct module * __load_resource(const char *resource_name,
|
||||
AST_LIST_UNLOCK(&module_list);
|
||||
return NULL;
|
||||
}
|
||||
/* init mutex and usecount */
|
||||
ast_mutex_init(&cur->cb->lock);
|
||||
cur->cb->lu_head = NULL;
|
||||
|
||||
if (!ast_fully_booted) {
|
||||
if (option_verbose)
|
||||
ast_verbose( " => (%s)\n", term_color(tmp, m->description(), COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
|
||||
@@ -768,7 +777,7 @@ static struct module * __load_resource(const char *resource_name,
|
||||
so reload commands will be issued in same order modules were loaded */
|
||||
|
||||
modlistver++;
|
||||
if (m->type == MOD_2) {
|
||||
if ( (m->flags & MOD_MASK) == MOD_2) {
|
||||
ast_log(LOG_WARNING, "new-style module %s, deferring load()\n",
|
||||
resource_name);
|
||||
cur->state = MS_NEW;
|
||||
@@ -777,7 +786,7 @@ static struct module * __load_resource(const char *resource_name,
|
||||
/* XXX make sure the usecount is 1 before releasing the lock */
|
||||
AST_LIST_UNLOCK(&module_list);
|
||||
|
||||
if (cur->state == MS_CANLOAD && (res = m->load_module())) {
|
||||
if (cur->state == MS_CANLOAD && (res = m->load_module(m))) {
|
||||
ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", resource_name, res);
|
||||
ast_unload_resource(resource_name, 0);
|
||||
return NULL;
|
||||
@@ -808,22 +817,22 @@ int ast_load_resource(const char *resource_name)
|
||||
}
|
||||
|
||||
#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;
|
||||
+}
|
||||
/*
|
||||
* 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
|
||||
|
||||
/*! \brief if enabled, log and output on console the module's name, and try load it */
|
||||
@@ -947,13 +956,15 @@ done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <errno.h> /* for errno... */
|
||||
|
||||
void ast_update_use_count(void)
|
||||
{
|
||||
/* Notify any module monitors that the use count for a
|
||||
resource has changed */
|
||||
struct loadupdate *m;
|
||||
if (AST_LIST_LOCK(&module_list))
|
||||
ast_log(LOG_WARNING, "Failed to lock\n");
|
||||
ast_log(LOG_WARNING, "Failed to lock, errno %d\n", errno);
|
||||
AST_LIST_TRAVERSE(&updaters, m, next)
|
||||
m->updater();
|
||||
AST_LIST_UNLOCK(&module_list);
|
||||
@@ -968,8 +979,9 @@ int ast_update_module_list(int (*modentry)(const char *module, const char *descr
|
||||
|
||||
if (ast_mutex_trylock(&module_list.lock))
|
||||
unlock = 0;
|
||||
AST_LIST_TRAVERSE(&module_list, cur, next)
|
||||
total_mod_loaded += modentry(cur->resource, cur->cb.description(), cur->cb.usecount(), like);
|
||||
AST_LIST_TRAVERSE(&module_list, cur, next) {
|
||||
total_mod_loaded += modentry(cur->resource, cur->cb->description(), cur->cb->usecnt, like);
|
||||
}
|
||||
if (unlock)
|
||||
AST_LIST_UNLOCK(&module_list);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user