12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124 |
- /*
- * 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
- */
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include "asterisk/_private.h"
- #include "asterisk/paths.h" /* use ast_config_AST_MODULE_DIR */
- #include <dirent.h>
- #include "asterisk/linkedlists.h"
- #include "asterisk/module.h"
- #include "asterisk/config.h"
- #include "asterisk/channel.h"
- #include "asterisk/term.h"
- #include "asterisk/manager.h"
- #include "asterisk/cdr.h"
- #include "asterisk/enum.h"
- #include "asterisk/rtp.h"
- #include "asterisk/http.h"
- #include "asterisk/lock.h"
- #include "asterisk/features.h"
- #include <dlfcn.h>
- #include "asterisk/md5.h"
- #include "asterisk/utils.h"
- #ifndef RTLD_NOW
- #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_LIST_HEAD(module_user_list, ast_module_user);
- static unsigned char expected_key[] =
- { 0x87, 0x76, 0x79, 0x35, 0x23, 0xea, 0x3a, 0xd3,
- 0x25, 0x2a, 0xbb, 0x35, 0x87, 0xe4, 0x22, 0x24 };
- static char buildopt_sum[33] = AST_BUILDOPT_SUM;
- static unsigned int embedding = 1; /* we always start out by registering embedded modules,
- since they are here before we dlopen() any
- */
- struct ast_module {
- const struct ast_module_info *info;
- void *lib; /* the shared lib, or NULL if embedded */
- int usecount; /* the number of 'users' currently in this module */
- struct module_user_list users; /* the list of users in the module */
- struct {
- unsigned int running:1;
- unsigned int declined:1;
- } flags;
- AST_LIST_ENTRY(ast_module) entry;
- char resource[0];
- };
- static AST_LIST_HEAD_STATIC(module_list, ast_module);
- /*
- * module_list is cleared by its constructor possibly after
- * we start accumulating embedded modules, so we need to
- * use another list (without the lock) to accumulate them.
- * Then we update the main list when embedding is done.
- */
- static struct module_list embedded_module_list;
- struct loadupdate {
- int (*updater)(void);
- AST_LIST_ENTRY(loadupdate) entry;
- };
- static AST_LIST_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_LIST_HEAD_STATIC(reload_queue, reload_queue_item);
- /* when dynamic modules are being loaded, ast_module_register() will
- need to know what filename the module was loaded from while it
- is being registered
- */
- struct ast_module *resource_being_loaded;
- /*! \brief Load modules in this order. */
- enum module_load_pass {
- /*! \brief AST_MODFLAG_LOAD_FIRST */
- LOAD_FIRST,
- /*! \brief AST_MODFLAG_GLOBAL_SYMBOLS */
- LOAD_GLOBAL_SYMBOLS,
- /*! \brief everything that is left */
- LOAD_ALL,
- /*! \brief Must remain at the end. */
- LOAD_DONE,
- };
- /* XXX: should we check for duplicate resource names here? */
- void ast_module_register(const struct ast_module_info *info)
- {
- struct ast_module *mod;
- if (embedding) {
- if (!(mod = ast_calloc(1, sizeof(*mod) + strlen(info->name) + 1)))
- return;
- strcpy(mod->resource, info->name);
- } else {
- mod = resource_being_loaded;
- }
- mod->info = info;
- AST_LIST_HEAD_INIT(&mod->users);
- /* during startup, before the loader has been initialized,
- there are no threads, so there is no need to take the lock
- on this list to manipulate it. it is also possible that it
- might be unsafe to use the list lock at that point... so
- let's avoid it altogether
- */
- if (embedding) {
- AST_LIST_INSERT_TAIL(&embedded_module_list, mod, entry);
- } else {
- AST_LIST_LOCK(&module_list);
- /* it is paramount that the new entry be placed at the tail of
- the list, otherwise the code that uses dlopen() to load
- dynamic modules won't be able to find out if the module it
- just opened was registered or failed to load
- */
- AST_LIST_INSERT_TAIL(&module_list, mod, entry);
- AST_LIST_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;
- }
- 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_LIST_LOCK(&module_list);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&module_list, mod, entry) {
- if (mod->info == info) {
- AST_LIST_REMOVE_CURRENT(entry);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- AST_LIST_UNLOCK(&module_list);
- if (mod) {
- AST_LIST_HEAD_DESTROY(&mod->users);
- ast_free(mod);
- }
- }
- struct ast_module_user *__ast_module_user_add(struct ast_module *mod,
- struct ast_channel *chan)
- {
- struct ast_module_user *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);
- 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)
- {
- AST_LIST_LOCK(&mod->users);
- AST_LIST_REMOVE(&mod->users, u, entry);
- AST_LIST_UNLOCK(&mod->users);
- ast_atomic_fetchadd_int(&mod->usecount, -1);
- 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))) {
- ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
- ast_atomic_fetchadd_int(&mod->usecount, -1);
- ast_free(u);
- }
- AST_LIST_UNLOCK(&mod->users);
- ast_update_use_count();
- }
- /*! \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 */
- { "cdr", ast_cdr_engine_reload },
- { "dnsmgr", dnsmgr_reload },
- { "extconfig", read_config_maps },
- { "enum", ast_enum_reload },
- { "manager", reload_manager },
- { "rtp", ast_rtp_reload },
- { "http", ast_http_reload },
- { "logger", logger_reload },
- { "features", ast_features_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, " %02x", *d++);
- ast_debug(1, "Unexpected signature:%s\n", buf);
- 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 int resource_name_match(const char *name1_in, const char *name2_in)
- {
- char *name1 = (char *) name1_in;
- char *name2 = (char *) name2_in;
- /* trim off any .so extensions */
- if (!strcasecmp(name1 + strlen(name1) - 3, ".so")) {
- name1 = ast_strdupa(name1);
- name1[strlen(name1) - 3] = '\0';
- }
- if (!strcasecmp(name2 + strlen(name2) - 3, ".so")) {
- name2 = ast_strdupa(name2);
- name2[strlen(name2) - 3] = '\0';
- }
- return strcasecmp(name1, name2);
- }
- static struct ast_module *find_resource(const char *resource, int do_lock)
- {
- struct ast_module *cur;
- if (do_lock)
- AST_LIST_LOCK(&module_list);
- AST_LIST_TRAVERSE(&module_list, cur, entry) {
- if (!resource_name_match(resource, cur->resource))
- break;
- }
- if (do_lock)
- AST_LIST_UNLOCK(&module_list);
- return cur;
- }
- #ifdef LOADABLE_MODULES
- static void unload_dynamic_module(struct ast_module *mod)
- {
- 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 */
- if (lib)
- while (!dlclose(lib));
- }
- static struct ast_module *load_dynamic_module(const char *resource_in, enum module_load_pass load_pass)
- {
- char fn[PATH_MAX] = "";
- void *lib = NULL;
- struct ast_module *mod;
- unsigned int wants_global = 0, not_yet = 0;
- int space; /* room needed for the descriptor */
- int missing_so = 0;
- space = sizeof(*resource_being_loaded) + strlen(resource_in) + 1;
- if (strcasecmp(resource_in + strlen(resource_in) - 3, ".so")) {
- missing_so = 1;
- space += 3; /* room for the extra ".so" */
- }
- snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, missing_so ? ".so" : "");
- /* make a first load of the module in 'quiet' mode... don't try to resolve
- any symbols, and don't export any symbols. this will allow us to peek into
- the module's info block (if available) to see what flags it has set */
- resource_being_loaded = ast_calloc(1, space);
- if (!resource_being_loaded)
- return NULL;
- strcpy(resource_being_loaded->resource, resource_in);
- if (missing_so)
- strcat(resource_being_loaded->resource, ".so");
- if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_LOCAL))) {
- ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
- ast_free(resource_being_loaded);
- return NULL;
- }
- /* the dlopen() succeeded, let's find out if the module
- registered itself */
- /* note that this will only work properly as long as
- ast_module_register() (which is called by the module's
- constructor) places the new module at the tail of the
- module_list
- */
- if (resource_being_loaded != (mod = AST_LIST_LAST(&module_list))) {
- ast_log(LOG_WARNING, "Module '%s' did not register itself during load\n", resource_in);
- /* no, it did not, so close it and return */
- while (!dlclose(lib));
- /* note that the module's destructor will call ast_module_unregister(),
- which will free the structure we allocated in resource_being_loaded */
- return NULL;
- }
- switch (load_pass) {
- case LOAD_FIRST:
- not_yet = !ast_test_flag(mod->info, AST_MODFLAG_LOAD_FIRST);
- break;
- case LOAD_GLOBAL_SYMBOLS:
- wants_global = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS);
- not_yet = !wants_global;
- break;
- case LOAD_ALL:
- break;
- case LOAD_DONE:
- ast_log(LOG_ERROR, "Satan just bought a snowblower! (This should never happen, btw.)\n");
- break;
- }
- if (not_yet) {
- while (!dlclose(lib));
- return NULL;
- }
- while (!dlclose(lib));
- resource_being_loaded = NULL;
- /* start the load process again */
- resource_being_loaded = ast_calloc(1, space);
- if (!resource_being_loaded)
- return NULL;
- strcpy(resource_being_loaded->resource, resource_in);
- if (missing_so)
- strcat(resource_being_loaded->resource, ".so");
- if (!(lib = dlopen(fn, wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) {
- ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
- ast_free(resource_being_loaded);
- return NULL;
- }
- /* since the module was successfully opened, and it registered itself
- the previous time we did that, we're going to assume it worked this
- time too :) */
- AST_LIST_LAST(&module_list)->lib = lib;
- resource_being_loaded = NULL;
- return AST_LIST_LAST(&module_list);
- }
- #endif
- void ast_module_shutdown(void)
- {
- struct ast_module *mod;
- int somethingchanged = 1, final = 0;
- AST_LIST_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 {
- if (!somethingchanged) {
- /*!\note If we go through the entire list without changing
- * anything, ignore the usecounts and unload, then exit. */
- final = 1;
- }
- /* Reset flag before traversing the list */
- somethingchanged = 0;
- AST_LIST_TRAVERSE_SAFE_BEGIN(&module_list, mod, entry) {
- if (!final && mod->usecount) {
- continue;
- }
- AST_LIST_REMOVE_CURRENT(entry);
- if (mod->info->unload) {
- mod->info->unload();
- }
- AST_LIST_HEAD_DESTROY(&mod->users);
- free(mod);
- somethingchanged = 1;
- }
- AST_LIST_TRAVERSE_SAFE_END;
- } while (somethingchanged && !final);
- AST_LIST_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_LIST_LOCK(&module_list);
- if (!(mod = find_resource(resource_name, 0))) {
- AST_LIST_UNLOCK(&module_list);
- ast_log(LOG_WARNING, "Unload failed, '%s' could not be found\n", resource_name);
- return -1;
- }
- if (!(mod->flags.running || mod->flags.declined))
- error = 1;
- if (!error && (mod->usecount > 0)) {
- 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, "Soft unload failed, '%s' has use count %d\n", resource_name,
- mod->usecount);
- error = 1;
- }
- }
- if (!error) {
- __ast_module_user_hangup_all(mod);
- res = mod->info->unload();
- if (res) {
- ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
- if (force <= AST_FORCE_FIRM)
- error = 1;
- else
- ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
- }
- }
- if (!error)
- mod->flags.running = mod->flags.declined = 0;
- AST_LIST_UNLOCK(&module_list);
- if (!error && !mod->lib && mod->info && mod->info->restore_globals)
- mod->info->restore_globals();
- #ifdef LOADABLE_MODULES
- if (!error)
- unload_dynamic_module(mod);
- #endif
- if (!error)
- ast_update_use_count();
- return res;
- }
- char *ast_module_helper(const char *line, const char *word, int pos, int state, int rpos, int needsreload)
- {
- struct ast_module *cur;
- int i, which=0, l = strlen(word);
- char *ret = NULL;
- if (pos != rpos)
- return NULL;
- AST_LIST_LOCK(&module_list);
- AST_LIST_TRAVERSE(&module_list, cur, entry) {
- if (!strncasecmp(word, cur->resource, l) &&
- (cur->info->reload || !needsreload) &&
- ++which > state) {
- ret = ast_strdup(cur->resource);
- break;
- }
- }
- AST_LIST_UNLOCK(&module_list);
- if (!ret) {
- for (i=0; !ret && reload_classes[i].name; i++) {
- if (!strncasecmp(word, reload_classes[i].name, l) && ++which > state)
- ret = ast_strdup(reload_classes[i].name);
- }
- }
- return ret;
- }
- void ast_process_pending_reloads(void)
- {
- struct reload_queue_item *item;
- if (!ast_fully_booted) {
- return;
- }
- 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);
- }
- int ast_module_reload(const char *name)
- {
- struct ast_module *cur;
- int res = 0; /* return value. 0 = not found, others, see below */
- int i;
- /* If we aren't fully booted, we just pretend we reloaded but we queue this
- up to run once we are booted up. */
- if (!ast_fully_booted) {
- queue_reload_request(name);
- return 0;
- }
- if (ast_mutex_trylock(&reloadlock)) {
- ast_verbose("The previous reload command didn't finish yet\n");
- return -1; /* reload already in progress */
- }
- ast_lastreloadtime = ast_tvnow();
- /* Call "predefined" reload here first */
- for (i = 0; reload_classes[i].name; i++) {
- if (!name || !strcasecmp(name, reload_classes[i].name)) {
- reload_classes[i].reload_fn(); /* XXX should check error ? */
- res = 2; /* found and reloaded */
- }
- }
- if (name && res) {
- ast_mutex_unlock(&reloadlock);
- return res;
- }
- AST_LIST_LOCK(&module_list);
- AST_LIST_TRAVERSE(&module_list, cur, entry) {
- const struct ast_module_info *info = cur->info;
- if (name && resource_name_match(name, cur->resource))
- continue;
- if (!cur->flags.running || cur->flags.declined) {
- if (!name)
- continue;
- ast_log(LOG_NOTICE, "The module '%s' was not properly initialized. "
- "Before reloading the module, you must run \"module load %s\" "
- "and fix whatever is preventing the module from being initialized.\n",
- name, name);
- res = 2; /* Don't report that the module was not found */
- break;
- }
- if (!info->reload) { /* cannot be reloaded */
- if (res < 1) /* store result if possible */
- res = 1; /* 1 = no reload() method */
- continue;
- }
- res = 2;
- ast_verb(3, "Reloading module '%s' (%s)\n", cur->resource, info->description);
- info->reload();
- }
- AST_LIST_UNLOCK(&module_list);
- ast_mutex_unlock(&reloadlock);
- return res;
- }
- static unsigned int inspect_module(const struct ast_module *mod)
- {
- if (!mod->info->description) {
- ast_log(LOG_WARNING, "Module '%s' does not provide a description.\n", mod->resource);
- return 1;
- }
- if (!mod->info->key) {
- ast_log(LOG_WARNING, "Module '%s' does not provide a license key.\n", mod->resource);
- return 1;
- }
- if (verify_key((unsigned char *) mod->info->key)) {
- ast_log(LOG_WARNING, "Module '%s' did not provide a valid license key.\n", mod->resource);
- return 1;
- }
- if (!ast_strlen_zero(mod->info->buildopt_sum) &&
- strcmp(buildopt_sum, mod->info->buildopt_sum)) {
- ast_log(LOG_WARNING, "Module '%s' was not compiled with the same compile-time options as this version of Asterisk.\n", mod->resource);
- ast_log(LOG_WARNING, "Module '%s' will not be initialized as it may cause instability.\n", mod->resource);
- return 1;
- }
- return 0;
- }
- static enum ast_module_load_result load_resource(const char *resource_name, enum module_load_pass load_pass)
- {
- struct ast_module *mod;
- enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS;
- char tmp[256];
- if ((mod = find_resource(resource_name, 0))) {
- if (mod->flags.running) {
- ast_log(LOG_WARNING, "Module '%s' already exists.\n", resource_name);
- return AST_MODULE_LOAD_DECLINE;
- }
- switch (load_pass) {
- case LOAD_FIRST:
- if (!ast_test_flag(mod->info, AST_MODFLAG_LOAD_FIRST)) {
- return AST_MODULE_LOAD_SKIP;
- }
- break;
- case LOAD_GLOBAL_SYMBOLS:
- if (!ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS)) {
- return AST_MODULE_LOAD_SKIP;
- }
- break;
- case LOAD_ALL:
- break;
- case LOAD_DONE:
- ast_log(LOG_ERROR, "This should never happen, -EFLAMES!\n");
- break;
- }
- } else {
- #ifdef LOADABLE_MODULES
- if (!(mod = load_dynamic_module(resource_name, load_pass))) {
- /* don't generate a warning message during load_modules() */
- if (load_pass == LOAD_ALL) {
- ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
- return AST_MODULE_LOAD_DECLINE;
- } else {
- return AST_MODULE_LOAD_SKIP;
- }
- }
- #else
- ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
- return AST_MODULE_LOAD_DECLINE;
- #endif
- }
- if (inspect_module(mod)) {
- ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
- #ifdef LOADABLE_MODULES
- unload_dynamic_module(mod);
- #endif
- return AST_MODULE_LOAD_DECLINE;
- }
- if (!mod->lib && mod->info->backup_globals()) {
- ast_log(LOG_WARNING, "Module '%s' was unable to backup its global data.\n", resource_name);
- return AST_MODULE_LOAD_DECLINE;
- }
- mod->flags.declined = 0;
- if (mod->info->load)
- res = mod->info->load();
- switch (res) {
- case AST_MODULE_LOAD_SUCCESS:
- if (!ast_fully_booted) {
- ast_verb(1, "%s => (%s)\n", resource_name, term_color(tmp, mod->info->description, COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
- if (ast_opt_console && !option_verbose)
- ast_verbose( ".");
- } else {
- ast_verb(1, "Loaded %s => (%s)\n", resource_name, mod->info->description);
- }
- mod->flags.running = 1;
- ast_update_use_count();
- break;
- case AST_MODULE_LOAD_DECLINE:
- mod->flags.declined = 1;
- break;
- case AST_MODULE_LOAD_FAILURE:
- break;
- case AST_MODULE_LOAD_SKIP:
- /* modules should never return this value */
- break;
- }
- return res;
- }
- int ast_load_resource(const char *resource_name)
- {
- int res;
- AST_LIST_LOCK(&module_list);
- res = load_resource(resource_name, LOAD_ALL);
- AST_LIST_UNLOCK(&module_list);
- return res;
- }
- struct load_order_entry {
- char *resource;
- AST_LIST_ENTRY(load_order_entry) entry;
- };
- AST_LIST_HEAD_NOLOCK(load_order, load_order_entry);
- static struct load_order_entry *add_to_load_order(const char *resource, struct load_order *load_order)
- {
- struct load_order_entry *order;
- AST_LIST_TRAVERSE(load_order, order, entry) {
- if (!resource_name_match(order->resource, resource))
- return NULL;
- }
- if (!(order = ast_calloc(1, sizeof(*order))))
- return NULL;
- order->resource = ast_strdup(resource);
- AST_LIST_INSERT_TAIL(load_order, order, entry);
- return order;
- }
- int load_modules(unsigned int preload_only)
- {
- struct ast_config *cfg;
- struct ast_module *mod;
- struct load_order_entry *order;
- struct ast_variable *v;
- unsigned int load_count;
- struct load_order load_order;
- int res = 0;
- struct ast_flags config_flags = { 0 };
- int modulecount = 0;
- int load_pass;
- #ifdef LOADABLE_MODULES
- struct dirent *dirent;
- DIR *dir;
- #endif
- /* all embedded modules have registered themselves by now */
- embedding = 0;
- ast_verb(1, "Asterisk Dynamic Loader Starting:\n");
- AST_LIST_HEAD_INIT_NOLOCK(&load_order);
- AST_LIST_LOCK(&module_list);
- if (embedded_module_list.first) {
- module_list.first = embedded_module_list.first;
- module_list.last = embedded_module_list.last;
- embedded_module_list.first = NULL;
- }
- if (!(cfg = ast_config_load(AST_MODULE_CONFIG, config_flags))) {
- ast_log(LOG_WARNING, "No '%s' found, no modules will be loaded.\n", AST_MODULE_CONFIG);
- goto done;
- }
- /* first, find all the modules we have been explicitly requested to load */
- for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
- if (!strcasecmp(v->name, preload_only ? "preload" : "load")) {
- add_to_load_order(v->value, &load_order);
- }
- }
- /* check if 'autoload' is on */
- if (!preload_only && ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
- /* if so, first add all the embedded modules that are not already running to the load order */
- AST_LIST_TRAVERSE(&module_list, mod, entry) {
- /* if it's not embedded, skip it */
- if (mod->lib)
- continue;
- if (mod->flags.running)
- continue;
- order = add_to_load_order(mod->resource, &load_order);
- }
- #ifdef LOADABLE_MODULES
- /* if we are allowed to load dynamic modules, scan the directory for
- for all available modules and add them as well */
- if ((dir = opendir(ast_config_AST_MODULE_DIR))) {
- while ((dirent = readdir(dir))) {
- int ld = strlen(dirent->d_name);
- /* Must end in .so to load it. */
- if (ld < 4)
- continue;
- if (strcasecmp(dirent->d_name + ld - 3, ".so"))
- continue;
- /* if there is already a module by this name in the module_list,
- skip this file */
- if (find_resource(dirent->d_name, 0))
- continue;
- add_to_load_order(dirent->d_name, &load_order);
- }
- closedir(dir);
- } else {
- if (!ast_opt_quiet)
- ast_log(LOG_WARNING, "Unable to open modules directory '%s'.\n",
- ast_config_AST_MODULE_DIR);
- }
- #endif
- }
- /* 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) {
- if (strcasecmp(v->name, "noload"))
- continue;
- AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
- if (!resource_name_match(order->resource, v->value)) {
- AST_LIST_REMOVE_CURRENT(entry);
- ast_free(order->resource);
- ast_free(order);
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- }
- /* we are done with the config now, all the information we need is in the
- load_order list */
- ast_config_destroy(cfg);
- load_count = 0;
- AST_LIST_TRAVERSE(&load_order, order, entry)
- load_count++;
- if (load_count)
- ast_log(LOG_NOTICE, "%d modules will be loaded.\n", load_count);
- for (load_pass = 0; load_pass < LOAD_DONE; load_pass++) {
- AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
- switch (load_resource(order->resource, load_pass)) {
- case AST_MODULE_LOAD_SUCCESS:
- modulecount++;
- case AST_MODULE_LOAD_DECLINE:
- AST_LIST_REMOVE_CURRENT(entry);
- ast_free(order->resource);
- ast_free(order);
- break;
- case AST_MODULE_LOAD_FAILURE:
- res = -1;
- goto done;
- case AST_MODULE_LOAD_SKIP:
- /*
- * Try again later. This result is received when a module is
- * deferred because it is not a part of the current pass.
- */
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- }
- done:
- while ((order = AST_LIST_REMOVE_HEAD(&load_order, entry))) {
- ast_free(order->resource);
- ast_free(order);
- }
- AST_LIST_UNLOCK(&module_list);
-
- /* Tell manager clients that are aggressive at logging in that we're done
- loading modules. If there's a DNS problem in chan_sip, we might not
- even reach this */
- manager_event(EVENT_FLAG_SYSTEM, "ModuleLoadReport", "ModuleLoadStatus: Done\r\nModuleSelection: %s\r\nModuleCount: %d\r\n", preload_only ? "Preload" : "All", modulecount);
-
- return res;
- }
- void ast_update_use_count(void)
- {
- /* Notify any module monitors that the use count for a
- resource has changed */
- struct loadupdate *m;
- AST_LIST_LOCK(&updaters);
- AST_LIST_TRAVERSE(&updaters, m, entry)
- m->updater();
- AST_LIST_UNLOCK(&updaters);
- }
- int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),
- const char *like)
- {
- struct ast_module *cur;
- int unlock = -1;
- int total_mod_loaded = 0;
- if (AST_LIST_TRYLOCK(&module_list))
- unlock = 0;
-
- AST_LIST_TRAVERSE(&module_list, cur, entry) {
- total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, like);
- }
- if (unlock)
- AST_LIST_UNLOCK(&module_list);
- return total_mod_loaded;
- }
- /*! \brief Check if module exists */
- int ast_module_check(const char *name)
- {
- struct ast_module *cur;
- if (ast_strlen_zero(name))
- return 0; /* FALSE */
- cur = find_resource(name, 1);
- return (cur != NULL);
- }
- int ast_loader_register(int (*v)(void))
- {
- struct loadupdate *tmp;
- if (!(tmp = ast_malloc(sizeof(*tmp))))
- return -1;
- tmp->updater = v;
- AST_LIST_LOCK(&updaters);
- AST_LIST_INSERT_HEAD(&updaters, tmp, entry);
- AST_LIST_UNLOCK(&updaters);
- return 0;
- }
- int ast_loader_unregister(int (*v)(void))
- {
- struct loadupdate *cur;
- AST_LIST_LOCK(&updaters);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&updaters, cur, entry) {
- if (cur->updater == v) {
- AST_LIST_REMOVE_CURRENT(entry);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- AST_LIST_UNLOCK(&updaters);
- return cur ? 0 : -1;
- }
- struct ast_module *ast_module_ref(struct ast_module *mod)
- {
- ast_atomic_fetchadd_int(&mod->usecount, +1);
- ast_update_use_count();
- return mod;
- }
- void ast_module_unref(struct ast_module *mod)
- {
- ast_atomic_fetchadd_int(&mod->usecount, -1);
- ast_update_use_count();
- }
|