Newer
Older
/*
* 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
* \author Mark Spencer <markster@digium.com>
* \author Kevin P. Fleming <kpfleming@digium.com>
* \author Luigi Rizzo <rizzo@icir.org>
* - See ModMngMnt
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
#include "asterisk.h"
#include "asterisk/_private.h"
#include "asterisk/paths.h" /* use ast_config_AST_MODULE_DIR */
#include <dirent.h>
#include <editline/readline.h>
#include "asterisk/dlinkedlists.h"
#include "asterisk/module.h"
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/term.h"
#include "asterisk/acl.h"
#include "asterisk/manager.h"
#include "asterisk/cdr.h"
#include "asterisk/enum.h"
#include "asterisk/http.h"
#include "asterisk/lock.h"
#include "asterisk/dsp.h"
Tilghman Lesher
committed
#include "asterisk/udptl.h"
Jeff Peeler
committed
#include "asterisk/app.h"
#include <dlfcn.h>
#include "asterisk/md5.h"
#include "asterisk/utils.h"
Matthew Jordan
committed
/*** DOCUMENTATION
<managerEvent language="en_US" name="Reload">
<managerEventInstance class="EVENT_FLAG_SYSTEM">
<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>
Matthew Jordan
committed
***/
#define RTLD_NOW 0
#endif
#ifndef RTLD_LOCAL
#define RTLD_LOCAL 0
#endif
struct ast_module_user {
struct ast_channel *chan;
AST_LIST_ENTRY(ast_module_user) entry;
};
AST_DLLIST_HEAD(module_user_list, ast_module_user);
static const unsigned char expected_key[] =
{ 0x87, 0x76, 0x79, 0x35, 0x23, 0xea, 0x3a, 0xd3,
0x25, 0x2a, 0xbb, 0x35, 0x87, 0xe4, 0x22, 0x24 };
Kevin P. Fleming
committed
static char buildopt_sum[33] = AST_BUILDOPT_SUM;
AST_VECTOR(module_vector, struct ast_module *);
/* Built-in module registrations need special handling at startup */
static unsigned int loader_ready;
George Joseph
committed
/*!
* \brief Internal flag to indicate all modules have been initially loaded.
*/
static int modules_loaded;
struct ast_module {
const struct ast_module_info *info;
/*! Used to get module references into refs log */
void *ref_debug;
/*! 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;
/*! 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;
/*! The module running and ready to accept requests. */
unsigned int running:1;
/*! The module has declined to start. */
unsigned int declined:1;
/*! This module is being held open until it's time to shutdown. */
unsigned int keepuntilshutdown:1;
/*! The module is built-in. */
unsigned int builtin:1;
AST_DLLIST_ENTRY(ast_module) entry;
char resource[0];
};
static AST_DLLIST_HEAD_STATIC(module_list, ast_module);
/*
* 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;
static int module_vector_strcasecmp(struct ast_module *a, struct ast_module *b)
{
return strcasecmp(a->resource, b->resource);
}
static int module_vector_cmp(struct ast_module *a, struct ast_module *b)
{
/* 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;
/*
* 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;
}
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
static struct ast_module *find_resource(const char *resource, int do_lock);
/*!
* \internal
* \brief Add a reference from mod to dep.
*
* \param mod Owner of the new reference.
* \param dep Module to reference
* \param missing Vector to store name of \a dep if it is not running.
*
* This function returns failure if \a dep is not running and \a missing
* is NULL. If \a missing is not NULL errors will only be returned for
* allocation failures.
*
* \retval 0 Success
* \retval -1 Failure
*
* \note Adding a second reference to the same dep will return success
* without doing anything.
*/
static int module_reffed_deps_add(struct ast_module *mod, struct ast_module *dep,
struct ast_vector_const_string *missing)
{
if (!dep->flags.running) {
return !missing ? -1 : AST_VECTOR_APPEND(missing, dep->info->name);
}
if (AST_VECTOR_GET_CMP(&mod->reffed_deps, dep, AST_VECTOR_ELEM_DEFAULT_CMP)) {
/* Skip duplicate. */
return 0;
}
if (AST_VECTOR_APPEND(&mod->reffed_deps, dep)) {
return -1;
}
ast_module_ref(dep);
return 0;
}
/*!
* \internal
* \brief Add references for modules that enhance a dependency.
*
* \param mod Owner of the new references.
* \param dep Module to check for enhancers.
* \param missing Vector to store name of any enhancer that is not running or declined.
*
* \retval 0 Success
* \retval -1 Failure
*/
static int module_reffed_deps_add_dep_enhancers(struct ast_module *mod,
struct ast_module *dep, struct ast_vector_const_string *missing)
{
struct ast_module *cur;
AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
if (cur->flags.declined) {
continue;
}
if (!AST_VECTOR_GET_CMP(&cur->enhances, dep->info->name, !strcasecmp)) {
/* dep is not enhanced by cur. */
continue;
}
/* dep is enhanced by cur, therefore mod requires cur. */
if (module_reffed_deps_add(mod, cur, missing)) {
return -1;
}
}
return 0;
}
/*!
* \internal
* \brief Add references to a list of dependencies.
*
* \param mod Owner of the new references.
* \param vec List of required modules to process
* \param missing Vector to store names of modules that are not running.
* \param ref_enhancers Reference all enhancers of each required module.
* \param isoptional Modules that are not loaded can be ignored.
*
* \retval 0 Success
* \retval -1 Failure
*/
static int module_deps_process_reqlist(struct ast_module *mod,
struct ast_vector_string *vec, struct ast_vector_const_string *missing,
int ref_enhancers, int isoptional)
{
int idx;
for (idx = 0; idx < AST_VECTOR_SIZE(vec); idx++) {
const char *depname = AST_VECTOR_GET(vec, idx);
struct ast_module *dep = find_resource(depname, 0);
if (!dep || !dep->flags.running) {
if (isoptional && !dep) {
continue;
}
if (missing && !AST_VECTOR_APPEND(missing, depname)) {
continue;
}
return -1;
}
if (module_reffed_deps_add(mod, dep, missing)) {
return -1;
}
if (ref_enhancers && module_reffed_deps_add_dep_enhancers(mod, dep, missing)) {
return -1;
}
}
return 0;
}
/*!
* \internal
* \brief Grab all references required to start the module.
*
* \param mod The module we're trying to start.
* \param missing Vector to store a list of missing dependencies.
*
* \retval 0 Success
* \retval -1 Failure
*
* \note module_list must be locked.
*
* \note Caller is responsible for initializing and freeing the vector.
* Elements are safely read only while module_list remains locked.
*/
static int module_deps_reference(struct ast_module *mod, struct ast_vector_const_string *missing)
{
int res = 0;
/* Grab references to modules we enhance but not other enhancements. */
res |= module_deps_process_reqlist(mod, &mod->enhances, missing, 0, 0);
/* Grab references to modules we require plus enhancements. */
res |= module_deps_process_reqlist(mod, &mod->requires, missing, 1, 0);
/* Grab references to optional modules including enhancements. */
res |= module_deps_process_reqlist(mod, &mod->optional_modules, missing, 1, 1);
return res;
}
/*!
* \brief Recursively find required dependencies that are not running.
*
* \param mod Module to scan for dependencies.
* \param missingdeps Vector listing modules that must be started first.
*
* \retval 0 All dependencies resolved.
* \retval -1 Failed to resolve some dependencies.
*
* An error from this function usually means a required module is not even
* loaded. This function is safe from infinite recursion, but dependency
* loops are not reported as an error from here. On success missingdeps
* will contain a list of every module that needs to be running before this
* module can start. missingdeps is sorted by load priority so any missing
* dependencies can be started if needed.
*/
static int module_deps_missing_recursive(struct ast_module *mod, struct module_vector *missingdeps)
{
int i = 0;
int res = -1;
struct ast_vector_const_string localdeps;
struct ast_module *dep;
/*
* localdeps stores a copy of all dependencies that mod could not reference.
* First we discard modules that we've already found. We add all newly found
* modules to the missingdeps vector then scan them recursively. This will
* ensure we quickly run out of stuff to do.
*/
AST_VECTOR_INIT(&localdeps, 0);
if (module_deps_reference(mod, &localdeps)) {
goto clean_return;
}
while (i < AST_VECTOR_SIZE(&localdeps)) {
dep = find_resource(AST_VECTOR_GET(&localdeps, i), 0);
if (!dep) {
goto clean_return;
}
if (AST_VECTOR_GET_CMP(missingdeps, dep, AST_VECTOR_ELEM_DEFAULT_CMP)) {
/* Skip common dependency. We have already searched it. */
AST_VECTOR_REMOVE(&localdeps, i, 0);
} else {
/* missingdeps is the real list so keep it sorted. */
if (AST_VECTOR_ADD_SORTED(missingdeps, dep, module_vector_cmp)) {
goto clean_return;
}
i++;
}
}
res = 0;
for (i = 0; !res && i < AST_VECTOR_SIZE(&localdeps); i++) {
dep = find_resource(AST_VECTOR_GET(&localdeps, i), 0);
/* We've already confirmed dep is loaded in the first loop. */
res = module_deps_missing_recursive(dep, missingdeps);
}
clean_return:
AST_VECTOR_FREE(&localdeps);
return res;
}
const char *ast_module_name(const struct ast_module *mod)
{
if (!mod || !mod->info) {
return NULL;
}
return mod->info->name;
}
struct loadupdate {
int (*updater)(void);
AST_LIST_ENTRY(loadupdate) entry;
};
static AST_DLLIST_HEAD_STATIC(updaters, loadupdate);
AST_MUTEX_DEFINE_STATIC(reloadlock);
struct reload_queue_item {
AST_LIST_ENTRY(reload_queue_item) entry;
char module[0];
};
static int do_full_reload = 0;
static AST_DLLIST_HEAD_STATIC(reload_queue, reload_queue_item);
/*!
* \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.
*/
static struct ast_module * volatile resource_being_loaded;
/*!
* \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.
*/
void ast_module_register(const struct ast_module_info *info)
{
if (!loader_ready) {
mod = ast_calloc(1, sizeof(*mod) + strlen(info->name) + 1);
if (!mod) {
/* We haven't even reached main() yet, if we can't
* allocate memory at this point just give up. */
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;
}
/*
* 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;
}
ast_debug(5, "Registering module %s\n", info->name);
David M. Lee
committed
/* This tells load_dynamic_module that we're registered. */
resource_being_loaded = NULL;
mod->info = info;
if (ast_opt_ref_debug) {
mod->ref_debug = ao2_t_alloc(0, NULL, info->name);
}
AST_LIST_HEAD_INIT(&mod->users);
AST_VECTOR_INIT(&mod->requires, 0);
AST_VECTOR_INIT(&mod->optional_modules, 0);
AST_VECTOR_INIT(&mod->enhances, 0);
AST_VECTOR_INIT(&mod->reffed_deps, 0);
AST_DLLIST_INSERT_TAIL(&module_list, mod, entry);
AST_DLLIST_UNLOCK(&module_list);
/* give the module a copy of its own handle, for later use in registrations and the like */
*((struct ast_module **) &(info->self)) = mod;
}
static void module_destroy(struct ast_module *mod)
{
AST_VECTOR_CALLBACK_VOID(&mod->requires, ast_free);
AST_VECTOR_FREE(&mod->requires);
AST_VECTOR_CALLBACK_VOID(&mod->optional_modules, ast_free);
AST_VECTOR_FREE(&mod->optional_modules);
AST_VECTOR_CALLBACK_VOID(&mod->enhances, ast_free);
AST_VECTOR_FREE(&mod->enhances);
/* Release references to all dependencies. */
AST_VECTOR_CALLBACK_VOID(&mod->reffed_deps, ast_module_unref);
AST_VECTOR_FREE(&mod->reffed_deps);
AST_LIST_HEAD_DESTROY(&mod->users);
ao2_cleanup(mod->ref_debug);
ast_free(mod);
}
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
*/
AST_DLLIST_LOCK(&module_list);
AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN(&module_list, mod, entry) {
if (mod->info == info) {
AST_DLLIST_REMOVE_CURRENT(entry);
break;
}
}
AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END;
AST_DLLIST_UNLOCK(&module_list);
if (mod && !mod->usecount) {
/*
* We are intentionally leaking mod if usecount is not zero.
* This is necessary if the module is being forcefully unloaded.
* In addition module_destroy is not safe to run after exit()
* is called. ast_module_unregister is run during cleanup of
* the process when libc releases each module's shared object
* library.
*/
ast_debug(5, "Unregistering module %s\n", info->name);
}
}
struct ast_module_user *__ast_module_user_add(struct ast_module *mod, struct ast_channel *chan)
struct ast_module_user *u;
u = ast_calloc(1, sizeof(*u));
if (!u) {
return NULL;
u->chan = chan;
AST_LIST_LOCK(&mod->users);
AST_LIST_INSERT_HEAD(&mod->users, u, entry);
AST_LIST_UNLOCK(&mod->users);
if (mod->ref_debug) {
ao2_ref(mod->ref_debug, +1);
}
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)
{
if (!u) {
return;
}
AST_LIST_LOCK(&mod->users);
u = AST_LIST_REMOVE(&mod->users, u, entry);
AST_LIST_UNLOCK(&mod->users);
if (!u) {
/*
* Was not in the list. Either a bad pointer or
* __ast_module_user_hangup_all() has been called.
*/
return;
}
if (mod->ref_debug) {
ao2_ref(mod->ref_debug, -1);
}
ast_atomic_fetchadd_int(&mod->usecount, -1);
Tilghman Lesher
committed
ast_free(u);
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))) {
if (u->chan) {
ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
}
if (mod->ref_debug) {
ao2_ref(mod->ref_debug, -1);
}
ast_atomic_fetchadd_int(&mod->usecount, -1);
Tilghman Lesher
committed
ast_free(u);
}
AST_LIST_UNLOCK(&mod->users);
}
/*! \note
* In addition to modules, the reload command handles some extra keywords
* which are listed here together with the corresponding handlers.
* This table is also used by the command completion code.
*/
static struct reload_classes {
const char *name;
int (*reload_fn)(void);
} reload_classes[] = { /* list in alpha order, longest match first for cli completion */
{ "acl", ast_named_acl_reload },
{ "cdr", ast_cdr_engine_reload },
{ "cel", ast_cel_engine_reload },
{ "dnsmgr", dnsmgr_reload },
{ "dsp", ast_dsp_reload},
{ "extconfig", read_config_maps },
{ "enum", ast_enum_reload },
{ "features", ast_features_config_reload },
{ "http", ast_http_reload },
{ "indications", ast_indications_reload },
{ "logger", logger_reload },
{ "manager", reload_manager },
{ "plc", ast_plc_reload },
{ "sounds", ast_sounds_reindex },
{ "udptl", ast_udptl_reload },
{ NULL, NULL }
};
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++)
pos += sprintf(buf + pos, " %02hhx", *d++);
ast_debug(1, "Unexpected signature:%s\n", buf);
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
return 0;
}
static int key_matches(const unsigned char *key1, const unsigned char *key2)
{
int x;
for (x = 0; x < 16; x++) {
if (key1[x] != key2[x])
return 0;
}
return 1;
}
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;
}
static size_t resource_name_baselen(const char *name)
if (len > 3 && !strcasecmp(name + len - 3, ".so")) {
return len - 3;
return len;
}
static int resource_name_match(const char *name1, size_t baselen1, const char *name2)
{
if (baselen1 != resource_name_baselen(name2)) {
return -1;
return strncasecmp(name1, name2, baselen1);
}
static struct ast_module *find_resource(const char *resource, int do_lock)
{
struct ast_module *cur;
size_t resource_baselen = resource_name_baselen(resource);
if (do_lock) {
AST_DLLIST_LOCK(&module_list);
}
AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
if (!resource_name_match(resource, resource_baselen, cur->resource)) {
if (do_lock) {
AST_DLLIST_UNLOCK(&module_list);
}
return cur;
}
David M. Lee
committed
/*!
* \brief dlclose(), with failure logging.
*/
static void logged_dlclose(const char *name, void *lib)
{
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"));
David M. Lee
committed
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
}
}
#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
static void unload_dynamic_module(struct ast_module *mod)
{
David M. Lee
committed
char *name = ast_strdupa(ast_module_name(mod));
Kevin P. Fleming
committed
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 */
logged_dlclose(ast_module_name(mod), lib);
David M. Lee
committed
/* 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
/*!
* \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)
{
struct ast_module *mod;
ast_assert(!resource_being_loaded);
mod = ast_calloc(1, sizeof(*mod) + strlen(resource_in) + strlen(so_ext) + 1);
if (!mod) {
return NULL;
sprintf(mod->resource, "%s%s", resource_in, so_ext); /* safe */
Kevin P. Fleming
committed
resource_being_loaded = mod;
mod->lib = dlopen(filename, flags);
if (resource_being_loaded) {
resource_being_loaded = NULL;
ast_log(LOG_ERROR, "Module '%s' did not register itself during load\n", resource_in);
logged_dlclose(resource_in, mod->lib);
} else if (!suppress_logging) {
ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
}
return NULL;
}
Kevin P. Fleming
committed
static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int suppress_logging)
{
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";
Kevin P. Fleming
committed
}
snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, so_ext);
/* 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);
Kevin P. Fleming
committed
if (!mod || !ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS)) {
Kevin P. Fleming
committed
/* Close the module so we can reopen with correct flags. */
logged_dlclose(resource_in, mod->lib);
Kevin P. Fleming
committed
return load_dlopen(resource_in, so_ext, fn, RTLD_NOW | RTLD_GLOBAL, 0);
David M. Lee
committed
int modules_shutdown(void)
AST_DLLIST_LOCK(&module_list);
/*!\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;
AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN(&module_list, mod, entry) {
ast_debug(1, "Passing on %s: its use count is %d\n",
mod->resource, mod->usecount);
AST_DLLIST_REMOVE_CURRENT(entry);
if (mod->flags.running && !mod->flags.declined && mod->info->unload) {
ast_verb(1, "Unloading %s\n", mod->resource);
mod->info->unload();
}
somethingchanged = 1;
}
AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END;
if (!somethingchanged) {
AST_DLLIST_TRAVERSE(&module_list, mod, entry) {
if (mod->flags.keepuntilshutdown) {
ast_module_unref(mod);
mod->flags.keepuntilshutdown = 0;
somethingchanged = 1;
}
}
}
AST_DLLIST_UNLOCK(&module_list);
int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode force)
{
struct ast_module *mod;
int res = -1;
int error = 0;
AST_DLLIST_LOCK(&module_list);
AST_DLLIST_UNLOCK(&module_list);
Joshua Colp
committed
ast_log(LOG_WARNING, "Unload failed, '%s' could not be found\n", resource_name);
if (!mod->flags.running || mod->flags.declined) {
ast_log(LOG_WARNING, "Unload failed, '%s' is not loaded.\n", resource_name);
if (!error && (mod->usecount > 0)) {
ast_log(LOG_WARNING, "Warning: Forcing removal of module '%s' with use count %d\n",
resource_name, mod->usecount);
else {
ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name,
mod->usecount);
error = 1;
}
}
if (!error) {
/* Request any channels attached to the module to hangup. */
__ast_module_user_hangup_all(mod);
ast_verb(1, "Unloading %s\n", mod->resource);
res = mod->info->unload();
if (res) {
ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
if (force <= AST_FORCE_FIRM) {
ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
}
}
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();