Thu Jul 9 13:40:36 2009

Asterisk developer's documentation


loader.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  * Kevin P. Fleming <kpfleming@digium.com>
00008  * Luigi Rizzo <rizzo@icir.org>
00009  *
00010  * See http://www.asterisk.org for more information about
00011  * the Asterisk project. Please do not directly contact
00012  * any of the maintainers of this project for assistance;
00013  * the project provides a web site, mailing lists and IRC
00014  * channels for your use.
00015  *
00016  * This program is free software, distributed under the terms of
00017  * the GNU General Public License Version 2. See the LICENSE file
00018  * at the top of the source tree.
00019  */
00020 
00021 /*! \file
00022  *
00023  * \brief Module Loader
00024  * \author Mark Spencer <markster@digium.com>
00025  * \author Kevin P. Fleming <kpfleming@digium.com>
00026  * \author Luigi Rizzo <rizzo@icir.org>
00027  * - See ModMngMnt
00028  */
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 199052 $")
00033 
00034 #include "asterisk/_private.h"
00035 #include "asterisk/paths.h"   /* use ast_config_AST_MODULE_DIR */
00036 #include <dirent.h>
00037 
00038 #include "asterisk/linkedlists.h"
00039 #include "asterisk/module.h"
00040 #include "asterisk/config.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/term.h"
00043 #include "asterisk/manager.h"
00044 #include "asterisk/cdr.h"
00045 #include "asterisk/enum.h"
00046 #include "asterisk/rtp.h"
00047 #include "asterisk/http.h"
00048 #include "asterisk/lock.h"
00049 #include "asterisk/features.h"
00050 
00051 #include <dlfcn.h>
00052 
00053 #include "asterisk/md5.h"
00054 #include "asterisk/utils.h"
00055 
00056 #ifndef RTLD_NOW
00057 #define RTLD_NOW 0
00058 #endif
00059 
00060 #ifndef RTLD_LOCAL
00061 #define RTLD_LOCAL 0
00062 #endif
00063 
00064 struct ast_module_user {
00065    struct ast_channel *chan;
00066    AST_LIST_ENTRY(ast_module_user) entry;
00067 };
00068 
00069 AST_LIST_HEAD(module_user_list, ast_module_user);
00070 
00071 static unsigned char expected_key[] =
00072 { 0x87, 0x76, 0x79, 0x35, 0x23, 0xea, 0x3a, 0xd3,
00073   0x25, 0x2a, 0xbb, 0x35, 0x87, 0xe4, 0x22, 0x24 };
00074 
00075 static char buildopt_sum[33] = AST_BUILDOPT_SUM;
00076 
00077 static unsigned int embedding = 1; /* we always start out by registering embedded modules,
00078                   since they are here before we dlopen() any
00079                */
00080 
00081 struct ast_module {
00082    const struct ast_module_info *info;
00083    void *lib;              /* the shared lib, or NULL if embedded */
00084    int usecount;              /* the number of 'users' currently in this module */
00085    struct module_user_list users;         /* the list of users in the module */
00086    struct {
00087       unsigned int running:1;
00088       unsigned int declined:1;
00089    } flags;
00090    AST_LIST_ENTRY(ast_module) entry;
00091    char resource[0];
00092 };
00093 
00094 static AST_LIST_HEAD_STATIC(module_list, ast_module);
00095 
00096 /*
00097  * module_list is cleared by its constructor possibly after
00098  * we start accumulating embedded modules, so we need to
00099  * use another list (without the lock) to accumulate them.
00100  * Then we update the main list when embedding is done.
00101  */
00102 static struct module_list embedded_module_list;
00103 
00104 struct loadupdate {
00105    int (*updater)(void);
00106    AST_LIST_ENTRY(loadupdate) entry;
00107 };
00108 
00109 static AST_LIST_HEAD_STATIC(updaters, loadupdate);
00110 
00111 AST_MUTEX_DEFINE_STATIC(reloadlock);
00112 
00113 struct reload_queue_item {
00114    AST_LIST_ENTRY(reload_queue_item) entry;
00115    char module[0];
00116 };
00117 
00118 static int do_full_reload = 0;
00119 
00120 static AST_LIST_HEAD_STATIC(reload_queue, reload_queue_item);
00121 
00122 /* when dynamic modules are being loaded, ast_module_register() will
00123    need to know what filename the module was loaded from while it
00124    is being registered
00125 */
00126 struct ast_module *resource_being_loaded;
00127 
00128 /* XXX: should we check for duplicate resource names here? */
00129 
00130 void ast_module_register(const struct ast_module_info *info)
00131 {
00132    struct ast_module *mod;
00133 
00134    if (embedding) {
00135       if (!(mod = ast_calloc(1, sizeof(*mod) + strlen(info->name) + 1)))
00136          return;
00137       strcpy(mod->resource, info->name);
00138    } else {
00139       mod = resource_being_loaded;
00140    }
00141 
00142    mod->info = info;
00143    AST_LIST_HEAD_INIT(&mod->users);
00144 
00145    /* during startup, before the loader has been initialized,
00146       there are no threads, so there is no need to take the lock
00147       on this list to manipulate it. it is also possible that it
00148       might be unsafe to use the list lock at that point... so
00149       let's avoid it altogether
00150    */
00151    if (embedding) {
00152       AST_LIST_INSERT_TAIL(&embedded_module_list, mod, entry);
00153    } else {
00154       AST_LIST_LOCK(&module_list);
00155       /* it is paramount that the new entry be placed at the tail of
00156          the list, otherwise the code that uses dlopen() to load
00157          dynamic modules won't be able to find out if the module it
00158          just opened was registered or failed to load
00159       */
00160       AST_LIST_INSERT_TAIL(&module_list, mod, entry);
00161       AST_LIST_UNLOCK(&module_list);
00162    }
00163 
00164    /* give the module a copy of its own handle, for later use in registrations and the like */
00165    *((struct ast_module **) &(info->self)) = mod;
00166 }
00167 
00168 void ast_module_unregister(const struct ast_module_info *info)
00169 {
00170    struct ast_module *mod = NULL;
00171 
00172    /* it is assumed that the users list in the module structure
00173       will already be empty, or we cannot have gotten to this
00174       point
00175    */
00176    AST_LIST_LOCK(&module_list);
00177    AST_LIST_TRAVERSE_SAFE_BEGIN(&module_list, mod, entry) {
00178       if (mod->info == info) {
00179          AST_LIST_REMOVE_CURRENT(entry);
00180          break;
00181       }
00182    }
00183    AST_LIST_TRAVERSE_SAFE_END;
00184    AST_LIST_UNLOCK(&module_list);
00185 
00186    if (mod) {
00187       AST_LIST_HEAD_DESTROY(&mod->users);
00188       ast_free(mod);
00189    }
00190 }
00191 
00192 struct ast_module_user *__ast_module_user_add(struct ast_module *mod,
00193                      struct ast_channel *chan)
00194 {
00195    struct ast_module_user *u = ast_calloc(1, sizeof(*u));
00196 
00197    if (!u)
00198       return NULL;
00199 
00200    u->chan = chan;
00201 
00202    AST_LIST_LOCK(&mod->users);
00203    AST_LIST_INSERT_HEAD(&mod->users, u, entry);
00204    AST_LIST_UNLOCK(&mod->users);
00205 
00206    ast_atomic_fetchadd_int(&mod->usecount, +1);
00207 
00208    ast_update_use_count();
00209 
00210    return u;
00211 }
00212 
00213 void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u)
00214 {
00215    AST_LIST_LOCK(&mod->users);
00216    AST_LIST_REMOVE(&mod->users, u, entry);
00217    AST_LIST_UNLOCK(&mod->users);
00218    ast_atomic_fetchadd_int(&mod->usecount, -1);
00219    ast_free(u);
00220 
00221    ast_update_use_count();
00222 }
00223 
00224 void __ast_module_user_hangup_all(struct ast_module *mod)
00225 {
00226    struct ast_module_user *u;
00227 
00228    AST_LIST_LOCK(&mod->users);
00229    while ((u = AST_LIST_REMOVE_HEAD(&mod->users, entry))) {
00230       ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
00231       ast_atomic_fetchadd_int(&mod->usecount, -1);
00232       ast_free(u);
00233    }
00234    AST_LIST_UNLOCK(&mod->users);
00235 
00236    ast_update_use_count();
00237 }
00238 
00239 /*! \note
00240  * In addition to modules, the reload command handles some extra keywords
00241  * which are listed here together with the corresponding handlers.
00242  * This table is also used by the command completion code.
00243  */
00244 static struct reload_classes {
00245    const char *name;
00246    int (*reload_fn)(void);
00247 } reload_classes[] = {  /* list in alpha order, longest match first for cli completion */
00248    { "cdr", ast_cdr_engine_reload },
00249    { "dnsmgr", dnsmgr_reload },
00250    { "extconfig", read_config_maps },
00251    { "enum",   ast_enum_reload },
00252    { "manager",   reload_manager },
00253    { "rtp", ast_rtp_reload },
00254    { "http",   ast_http_reload },
00255    { "logger", logger_reload },
00256    { "features",  ast_features_reload },
00257    { NULL,  NULL }
00258 };
00259 
00260 static int printdigest(const unsigned char *d)
00261 {
00262    int x, pos;
00263    char buf[256]; /* large enough so we don't have to worry */
00264 
00265    for (pos = 0, x = 0; x < 16; x++)
00266       pos += sprintf(buf + pos, " %02x", *d++);
00267 
00268    ast_debug(1, "Unexpected signature:%s\n", buf);
00269 
00270    return 0;
00271 }
00272 
00273 static int key_matches(const unsigned char *key1, const unsigned char *key2)
00274 {
00275    int x;
00276 
00277    for (x = 0; x < 16; x++) {
00278       if (key1[x] != key2[x])
00279          return 0;
00280    }
00281 
00282    return 1;
00283 }
00284 
00285 static int verify_key(const unsigned char *key)
00286 {
00287    struct MD5Context c;
00288    unsigned char digest[16];
00289 
00290    MD5Init(&c);
00291    MD5Update(&c, key, strlen((char *)key));
00292    MD5Final(digest, &c);
00293 
00294    if (key_matches(expected_key, digest))
00295       return 0;
00296 
00297    printdigest(digest);
00298 
00299    return -1;
00300 }
00301 
00302 static int resource_name_match(const char *name1_in, const char *name2_in)
00303 {
00304    char *name1 = (char *) name1_in;
00305    char *name2 = (char *) name2_in;
00306 
00307    /* trim off any .so extensions */
00308    if (!strcasecmp(name1 + strlen(name1) - 3, ".so")) {
00309       name1 = ast_strdupa(name1);
00310       name1[strlen(name1) - 3] = '\0';
00311    }
00312    if (!strcasecmp(name2 + strlen(name2) - 3, ".so")) {
00313       name2 = ast_strdupa(name2);
00314       name2[strlen(name2) - 3] = '\0';
00315    }
00316 
00317    return strcasecmp(name1, name2);
00318 }
00319 
00320 static struct ast_module *find_resource(const char *resource, int do_lock)
00321 {
00322    struct ast_module *cur;
00323 
00324    if (do_lock)
00325       AST_LIST_LOCK(&module_list);
00326 
00327    AST_LIST_TRAVERSE(&module_list, cur, entry) {
00328       if (!resource_name_match(resource, cur->resource))
00329          break;
00330    }
00331 
00332    if (do_lock)
00333       AST_LIST_UNLOCK(&module_list);
00334 
00335    return cur;
00336 }
00337 
00338 #ifdef LOADABLE_MODULES
00339 static void unload_dynamic_module(struct ast_module *mod)
00340 {
00341    void *lib = mod->lib;
00342 
00343    /* WARNING: the structure pointed to by mod is going to
00344       disappear when this operation succeeds, so we can't
00345       dereference it */
00346 
00347    if (lib)
00348       while (!dlclose(lib));
00349 }
00350 
00351 static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only)
00352 {
00353    char fn[PATH_MAX] = "";
00354    void *lib = NULL;
00355    struct ast_module *mod;
00356    unsigned int wants_global;
00357    int space;  /* room needed for the descriptor */
00358    int missing_so = 0;
00359 
00360    space = sizeof(*resource_being_loaded) + strlen(resource_in) + 1;
00361    if (strcasecmp(resource_in + strlen(resource_in) - 3, ".so")) {
00362       missing_so = 1;
00363       space += 3; /* room for the extra ".so" */
00364    }
00365 
00366    snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, missing_so ? ".so" : "");
00367 
00368    /* make a first load of the module in 'quiet' mode... don't try to resolve
00369       any symbols, and don't export any symbols. this will allow us to peek into
00370       the module's info block (if available) to see what flags it has set */
00371 
00372    resource_being_loaded = ast_calloc(1, space);
00373    if (!resource_being_loaded)
00374       return NULL;
00375    strcpy(resource_being_loaded->resource, resource_in);
00376    if (missing_so)
00377       strcat(resource_being_loaded->resource, ".so");
00378 
00379    if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_LOCAL))) {
00380       ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
00381       ast_free(resource_being_loaded);
00382       return NULL;
00383    }
00384 
00385    /* the dlopen() succeeded, let's find out if the module
00386       registered itself */
00387    /* note that this will only work properly as long as
00388       ast_module_register() (which is called by the module's
00389       constructor) places the new module at the tail of the
00390       module_list
00391    */
00392    if (resource_being_loaded != (mod = AST_LIST_LAST(&module_list))) {
00393       ast_log(LOG_WARNING, "Module '%s' did not register itself during load\n", resource_in);
00394       /* no, it did not, so close it and return */
00395       while (!dlclose(lib));
00396       /* note that the module's destructor will call ast_module_unregister(),
00397          which will free the structure we allocated in resource_being_loaded */
00398       return NULL;
00399    }
00400 
00401    wants_global = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS);
00402 
00403    /* if we are being asked only to load modules that provide global symbols,
00404       and this one does not, then close it and return */
00405    if (global_symbols_only && !wants_global) {
00406       while (!dlclose(lib));
00407       return NULL;
00408    }
00409 
00410    while (!dlclose(lib));
00411    resource_being_loaded = NULL;
00412 
00413    /* start the load process again */
00414    resource_being_loaded = ast_calloc(1, space);
00415    if (!resource_being_loaded)
00416       return NULL;
00417    strcpy(resource_being_loaded->resource, resource_in);
00418    if (missing_so)
00419       strcat(resource_being_loaded->resource, ".so");
00420 
00421    if (!(lib = dlopen(fn, wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) {
00422       ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
00423       ast_free(resource_being_loaded);
00424       return NULL;
00425    }
00426 
00427    /* since the module was successfully opened, and it registered itself
00428       the previous time we did that, we're going to assume it worked this
00429       time too :) */
00430 
00431    AST_LIST_LAST(&module_list)->lib = lib;
00432    resource_being_loaded = NULL;
00433 
00434    return AST_LIST_LAST(&module_list);
00435 }
00436 #endif
00437 
00438 void ast_module_shutdown(void)
00439 {
00440    struct ast_module *mod;
00441    AST_LIST_HEAD_NOLOCK_STATIC(local_module_list, ast_module);
00442 
00443    /* We have to call the unload() callbacks in reverse order that the modules
00444     * exist in the module list so it is the reverse order of how they were
00445     * loaded. */
00446 
00447    AST_LIST_LOCK(&module_list);
00448    while ((mod = AST_LIST_REMOVE_HEAD(&module_list, entry)))
00449       AST_LIST_INSERT_HEAD(&local_module_list, mod, entry);
00450    AST_LIST_UNLOCK(&module_list);
00451 
00452    while ((mod = AST_LIST_REMOVE_HEAD(&local_module_list, entry))) {
00453       if (mod->info->unload)
00454          mod->info->unload();
00455       /* Since this should only be called when shutting down "gracefully",
00456        * all channels should be down before we get to this point, meaning
00457        * there will be no module users left. */
00458       AST_LIST_HEAD_DESTROY(&mod->users);
00459       free(mod);
00460    }
00461 }
00462 
00463 int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode force)
00464 {
00465    struct ast_module *mod;
00466    int res = -1;
00467    int error = 0;
00468 
00469    AST_LIST_LOCK(&module_list);
00470 
00471    if (!(mod = find_resource(resource_name, 0))) {
00472       AST_LIST_UNLOCK(&module_list);
00473       ast_log(LOG_WARNING, "Unload failed, '%s' could not be found\n", resource_name);
00474       return 0;
00475    }
00476 
00477    if (!(mod->flags.running || mod->flags.declined))
00478       error = 1;
00479 
00480    if (!error && (mod->usecount > 0)) {
00481       if (force)
00482          ast_log(LOG_WARNING, "Warning:  Forcing removal of module '%s' with use count %d\n",
00483             resource_name, mod->usecount);
00484       else {
00485          ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name,
00486             mod->usecount);
00487          error = 1;
00488       }
00489    }
00490 
00491    if (!error) {
00492       __ast_module_user_hangup_all(mod);
00493       res = mod->info->unload();
00494 
00495       if (res) {
00496          ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
00497          if (force <= AST_FORCE_FIRM)
00498             error = 1;
00499          else
00500             ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
00501       }
00502    }
00503 
00504    if (!error)
00505       mod->flags.running = mod->flags.declined = 0;
00506 
00507    AST_LIST_UNLOCK(&module_list);
00508 
00509    if (!error && !mod->lib && mod->info && mod->info->restore_globals)
00510       mod->info->restore_globals();
00511 
00512 #ifdef LOADABLE_MODULES
00513    if (!error)
00514       unload_dynamic_module(mod);
00515 #endif
00516 
00517    if (!error)
00518       ast_update_use_count();
00519 
00520    return res;
00521 }
00522 
00523 char *ast_module_helper(const char *line, const char *word, int pos, int state, int rpos, int needsreload)
00524 {
00525    struct ast_module *cur;
00526    int i, which=0, l = strlen(word);
00527    char *ret = NULL;
00528 
00529    if (pos != rpos)
00530       return NULL;
00531 
00532    AST_LIST_LOCK(&module_list);
00533    AST_LIST_TRAVERSE(&module_list, cur, entry) {
00534       if (!strncasecmp(word, cur->resource, l) &&
00535           (cur->info->reload || !needsreload) &&
00536           ++which > state) {
00537          ret = ast_strdup(cur->resource);
00538          break;
00539       }
00540    }
00541    AST_LIST_UNLOCK(&module_list);
00542 
00543    if (!ret) {
00544       for (i=0; !ret && reload_classes[i].name; i++) {
00545          if (!strncasecmp(word, reload_classes[i].name, l) && ++which > state)
00546             ret = ast_strdup(reload_classes[i].name);
00547       }
00548    }
00549 
00550    return ret;
00551 }
00552 
00553 void ast_process_pending_reloads(void)
00554 {
00555    struct reload_queue_item *item;
00556 
00557    if (!ast_fully_booted) {
00558       return;
00559    }
00560 
00561    AST_LIST_LOCK(&reload_queue);
00562 
00563    if (do_full_reload) {
00564       do_full_reload = 0;
00565       AST_LIST_UNLOCK(&reload_queue);
00566       ast_log(LOG_NOTICE, "Executing deferred reload request.\n");
00567       ast_module_reload(NULL);
00568       return;
00569    }
00570 
00571    while ((item = AST_LIST_REMOVE_HEAD(&reload_queue, entry))) {
00572       ast_log(LOG_NOTICE, "Executing deferred reload request for module '%s'.\n", item->module);
00573       ast_module_reload(item->module);
00574       ast_free(item);
00575    }
00576 
00577    AST_LIST_UNLOCK(&reload_queue);
00578 }
00579 
00580 static void queue_reload_request(const char *module)
00581 {
00582    struct reload_queue_item *item;
00583 
00584    AST_LIST_LOCK(&reload_queue);
00585 
00586    if (do_full_reload) {
00587       AST_LIST_UNLOCK(&reload_queue);
00588       return;
00589    }
00590 
00591    if (ast_strlen_zero(module)) {
00592       /* A full reload request (when module is NULL) wipes out any previous
00593          reload requests and causes the queue to ignore future ones */
00594       while ((item = AST_LIST_REMOVE_HEAD(&reload_queue, entry))) {
00595          ast_free(item);
00596       }
00597       do_full_reload = 1;
00598    } else {
00599       /* No reason to add the same module twice */
00600       AST_LIST_TRAVERSE(&reload_queue, item, entry) {
00601          if (!strcasecmp(item->module, module)) {
00602             AST_LIST_UNLOCK(&reload_queue);
00603             return;
00604          }
00605       }
00606       item = ast_calloc(1, sizeof(*item) + strlen(module) + 1);
00607       if (!item) {
00608          ast_log(LOG_ERROR, "Failed to allocate reload queue item.\n");
00609          AST_LIST_UNLOCK(&reload_queue);
00610          return;
00611       }
00612       strcpy(item->module, module);
00613       AST_LIST_INSERT_TAIL(&reload_queue, item, entry);
00614    }
00615    AST_LIST_UNLOCK(&reload_queue);
00616 }
00617 
00618 int ast_module_reload(const char *name)
00619 {
00620    struct ast_module *cur;
00621    int res = 0; /* return value. 0 = not found, others, see below */
00622    int i;
00623 
00624    /* If we aren't fully booted, we just pretend we reloaded but we queue this
00625       up to run once we are booted up. */
00626    if (!ast_fully_booted) {
00627       queue_reload_request(name);
00628       return 0;
00629    }
00630 
00631    if (ast_mutex_trylock(&reloadlock)) {
00632       ast_verbose("The previous reload command didn't finish yet\n");
00633       return -1;  /* reload already in progress */
00634    }
00635    ast_lastreloadtime = ast_tvnow();
00636 
00637    /* Call "predefined" reload here first */
00638    for (i = 0; reload_classes[i].name; i++) {
00639       if (!name || !strcasecmp(name, reload_classes[i].name)) {
00640          reload_classes[i].reload_fn();   /* XXX should check error ? */
00641          res = 2; /* found and reloaded */
00642       }
00643    }
00644 
00645    if (name && res) {
00646       ast_mutex_unlock(&reloadlock);
00647       return res;
00648    }
00649 
00650    AST_LIST_LOCK(&module_list);
00651    AST_LIST_TRAVERSE(&module_list, cur, entry) {
00652       const struct ast_module_info *info = cur->info;
00653 
00654       if (name && resource_name_match(name, cur->resource))
00655          continue;
00656 
00657       if (!cur->flags.running || cur->flags.declined) {
00658          if (!name)
00659             continue;
00660          ast_log(LOG_NOTICE, "The module '%s' was not properly initialized.  "
00661             "Before reloading the module, you must run \"module load %s\" "
00662             "and fix whatever is preventing the module from being initialized.\n",
00663             name, name);
00664          res = 2; /* Don't report that the module was not found */
00665          break;
00666       }
00667 
00668       if (!info->reload) { /* cannot be reloaded */
00669          if (res < 1)   /* store result if possible */
00670             res = 1; /* 1 = no reload() method */
00671          continue;
00672       }
00673 
00674       res = 2;
00675       ast_verb(3, "Reloading module '%s' (%s)\n", cur->resource, info->description);
00676       info->reload();
00677    }
00678    AST_LIST_UNLOCK(&module_list);
00679 
00680    ast_mutex_unlock(&reloadlock);
00681 
00682    return res;
00683 }
00684 
00685 static unsigned int inspect_module(const struct ast_module *mod)
00686 {
00687    if (!mod->info->description) {
00688       ast_log(LOG_WARNING, "Module '%s' does not provide a description.\n", mod->resource);
00689       return 1;
00690    }
00691 
00692    if (!mod->info->key) {
00693       ast_log(LOG_WARNING, "Module '%s' does not provide a license key.\n", mod->resource);
00694       return 1;
00695    }
00696 
00697    if (verify_key((unsigned char *) mod->info->key)) {
00698       ast_log(LOG_WARNING, "Module '%s' did not provide a valid license key.\n", mod->resource);
00699       return 1;
00700    }
00701 
00702    if (!ast_strlen_zero(mod->info->buildopt_sum) &&
00703        strcmp(buildopt_sum, mod->info->buildopt_sum)) {
00704       ast_log(LOG_WARNING, "Module '%s' was not compiled with the same compile-time options as this version of Asterisk.\n", mod->resource);
00705       ast_log(LOG_WARNING, "Module '%s' will not be initialized as it may cause instability.\n", mod->resource);
00706       return 1;
00707    }
00708 
00709    return 0;
00710 }
00711 
00712 static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only)
00713 {
00714    struct ast_module *mod;
00715    enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS;
00716    char tmp[256];
00717 
00718    if ((mod = find_resource(resource_name, 0))) {
00719       if (mod->flags.running) {
00720          ast_log(LOG_WARNING, "Module '%s' already exists.\n", resource_name);
00721          return AST_MODULE_LOAD_DECLINE;
00722       }
00723       if (global_symbols_only && !ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS))
00724          return AST_MODULE_LOAD_SKIP;
00725    } else {
00726 #ifdef LOADABLE_MODULES
00727       if (!(mod = load_dynamic_module(resource_name, global_symbols_only))) {
00728          /* don't generate a warning message during load_modules() */
00729          if (!global_symbols_only) {
00730             ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
00731             return AST_MODULE_LOAD_DECLINE;
00732          } else {
00733             return AST_MODULE_LOAD_SKIP;
00734          }
00735       }
00736 #else
00737       ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
00738       return AST_MODULE_LOAD_DECLINE;
00739 #endif
00740    }
00741 
00742    if (inspect_module(mod)) {
00743       ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
00744 #ifdef LOADABLE_MODULES
00745       unload_dynamic_module(mod);
00746 #endif
00747       return AST_MODULE_LOAD_DECLINE;
00748    }
00749 
00750    if (!mod->lib && mod->info->backup_globals()) {
00751       ast_log(LOG_WARNING, "Module '%s' was unable to backup its global data.\n", resource_name);
00752       return AST_MODULE_LOAD_DECLINE;
00753    }
00754 
00755    mod->flags.declined = 0;
00756 
00757    if (mod->info->load)
00758       res = mod->info->load();
00759 
00760    switch (res) {
00761    case AST_MODULE_LOAD_SUCCESS:
00762       if (!ast_fully_booted) {
00763          ast_verb(1, "%s => (%s)\n", resource_name, term_color(tmp, mod->info->description, COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
00764          if (ast_opt_console && !option_verbose)
00765             ast_verbose( ".");
00766       } else {
00767          ast_verb(1, "Loaded %s => (%s)\n", resource_name, mod->info->description);
00768       }
00769 
00770       mod->flags.running = 1;
00771 
00772       ast_update_use_count();
00773       break;
00774    case AST_MODULE_LOAD_DECLINE:
00775       mod->flags.declined = 1;
00776       break;
00777    case AST_MODULE_LOAD_FAILURE:
00778       break;
00779    case AST_MODULE_LOAD_SKIP:
00780       /* modules should never return this value */
00781       break;
00782    }
00783 
00784    return res;
00785 }
00786 
00787 int ast_load_resource(const char *resource_name)
00788 {
00789    int res;
00790    AST_LIST_LOCK(&module_list);
00791    res = load_resource(resource_name, 0);
00792    AST_LIST_UNLOCK(&module_list);
00793 
00794    return res;
00795 }
00796 
00797 struct load_order_entry {
00798    char *resource;
00799    AST_LIST_ENTRY(load_order_entry) entry;
00800 };
00801 
00802 AST_LIST_HEAD_NOLOCK(load_order, load_order_entry);
00803 
00804 static struct load_order_entry *add_to_load_order(const char *resource, struct load_order *load_order)
00805 {
00806    struct load_order_entry *order;
00807 
00808    AST_LIST_TRAVERSE(load_order, order, entry) {
00809       if (!resource_name_match(order->resource, resource))
00810          return NULL;
00811    }
00812 
00813    if (!(order = ast_calloc(1, sizeof(*order))))
00814       return NULL;
00815 
00816    order->resource = ast_strdup(resource);
00817    AST_LIST_INSERT_TAIL(load_order, order, entry);
00818 
00819    return order;
00820 }
00821 
00822 int load_modules(unsigned int preload_only)
00823 {
00824    struct ast_config *cfg;
00825    struct ast_module *mod;
00826    struct load_order_entry *order;
00827    struct ast_variable *v;
00828    unsigned int load_count;
00829    struct load_order load_order;
00830    int res = 0;
00831    struct ast_flags config_flags = { 0 };
00832    int modulecount = 0;
00833 
00834 #ifdef LOADABLE_MODULES
00835    struct dirent *dirent;
00836    DIR *dir;
00837 #endif
00838 
00839    /* all embedded modules have registered themselves by now */
00840    embedding = 0;
00841 
00842    ast_verb(1, "Asterisk Dynamic Loader Starting:\n");
00843 
00844    AST_LIST_HEAD_INIT_NOLOCK(&load_order);
00845 
00846    AST_LIST_LOCK(&module_list);
00847 
00848    if (embedded_module_list.first) {
00849       module_list.first = embedded_module_list.first;
00850       module_list.last = embedded_module_list.last;
00851       embedded_module_list.first = NULL;
00852    }
00853 
00854    if (!(cfg = ast_config_load(AST_MODULE_CONFIG, config_flags))) {
00855       ast_log(LOG_WARNING, "No '%s' found, no modules will be loaded.\n", AST_MODULE_CONFIG);
00856       goto done;
00857    }
00858 
00859    /* first, find all the modules we have been explicitly requested to load */
00860    for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
00861       if (!strcasecmp(v->name, preload_only ? "preload" : "load")) {
00862          add_to_load_order(v->value, &load_order);
00863       }
00864    }
00865 
00866    /* check if 'autoload' is on */
00867    if (!preload_only && ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
00868       /* if so, first add all the embedded modules that are not already running to the load order */
00869       AST_LIST_TRAVERSE(&module_list, mod, entry) {
00870          /* if it's not embedded, skip it */
00871          if (mod->lib)
00872             continue;
00873 
00874          if (mod->flags.running)
00875             continue;
00876 
00877          order = add_to_load_order(mod->resource, &load_order);
00878       }
00879 
00880 #ifdef LOADABLE_MODULES
00881       /* if we are allowed to load dynamic modules, scan the directory for
00882          for all available modules and add them as well */
00883       if ((dir  = opendir(ast_config_AST_MODULE_DIR))) {
00884          while ((dirent = readdir(dir))) {
00885             int ld = strlen(dirent->d_name);
00886 
00887             /* Must end in .so to load it.  */
00888 
00889             if (ld < 4)
00890                continue;
00891 
00892             if (strcasecmp(dirent->d_name + ld - 3, ".so"))
00893                continue;
00894 
00895             /* if there is already a module by this name in the module_list,
00896                skip this file */
00897             if (find_resource(dirent->d_name, 0))
00898                continue;
00899 
00900             add_to_load_order(dirent->d_name, &load_order);
00901          }
00902 
00903          closedir(dir);
00904       } else {
00905          if (!ast_opt_quiet)
00906             ast_log(LOG_WARNING, "Unable to open modules directory '%s'.\n",
00907                ast_config_AST_MODULE_DIR);
00908       }
00909 #endif
00910    }
00911 
00912    /* now scan the config for any modules we are prohibited from loading and
00913       remove them from the load order */
00914    for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
00915       if (strcasecmp(v->name, "noload"))
00916          continue;
00917 
00918       AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
00919          if (!resource_name_match(order->resource, v->value)) {
00920             AST_LIST_REMOVE_CURRENT(entry);
00921             ast_free(order->resource);
00922             ast_free(order);
00923          }
00924       }
00925       AST_LIST_TRAVERSE_SAFE_END;
00926    }
00927 
00928    /* we are done with the config now, all the information we need is in the
00929       load_order list */
00930    ast_config_destroy(cfg);
00931 
00932    load_count = 0;
00933    AST_LIST_TRAVERSE(&load_order, order, entry)
00934       load_count++;
00935 
00936    if (load_count)
00937       ast_log(LOG_NOTICE, "%d modules will be loaded.\n", load_count);
00938 
00939    /* first, load only modules that provide global symbols */
00940    AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
00941       switch (load_resource(order->resource, 1)) {
00942       case AST_MODULE_LOAD_SUCCESS:
00943          modulecount++;
00944       case AST_MODULE_LOAD_DECLINE:
00945          AST_LIST_REMOVE_CURRENT(entry);
00946          ast_free(order->resource);
00947          ast_free(order);
00948          break;
00949       case AST_MODULE_LOAD_FAILURE:
00950          res = -1;
00951          goto done;
00952       case AST_MODULE_LOAD_SKIP:
00953          /* try again later */
00954          break;
00955       }
00956    }
00957    AST_LIST_TRAVERSE_SAFE_END;
00958 
00959    /* now load everything else */
00960    AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
00961       switch (load_resource(order->resource, 0)) {
00962       case AST_MODULE_LOAD_SUCCESS:
00963          modulecount++;
00964       case AST_MODULE_LOAD_DECLINE:
00965          AST_LIST_REMOVE_CURRENT(entry);
00966          ast_free(order->resource);
00967          ast_free(order);
00968          break;
00969       case AST_MODULE_LOAD_FAILURE:
00970          res = -1;
00971          goto done;
00972       case AST_MODULE_LOAD_SKIP:
00973          /* should not happen */
00974          break;
00975       }
00976    }
00977    AST_LIST_TRAVERSE_SAFE_END;
00978 
00979 done:
00980    while ((order = AST_LIST_REMOVE_HEAD(&load_order, entry))) {
00981       ast_free(order->resource);
00982       ast_free(order);
00983    }
00984 
00985    AST_LIST_UNLOCK(&module_list);
00986    
00987    /* Tell manager clients that are aggressive at logging in that we're done
00988       loading modules. If there's a DNS problem in chan_sip, we might not
00989       even reach this */
00990    manager_event(EVENT_FLAG_SYSTEM, "ModuleLoadReport", "ModuleLoadStatus: Done\r\nModuleSelection: %s\r\nModuleCount: %d\r\n", preload_only ? "Preload" : "All", modulecount);
00991    
00992    return res;
00993 }
00994 
00995 void ast_update_use_count(void)
00996 {
00997    /* Notify any module monitors that the use count for a
00998       resource has changed */
00999    struct loadupdate *m;
01000 
01001    AST_LIST_LOCK(&updaters);
01002    AST_LIST_TRAVERSE(&updaters, m, entry)
01003       m->updater();
01004    AST_LIST_UNLOCK(&updaters);
01005 }
01006 
01007 int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),
01008             const char *like)
01009 {
01010    struct ast_module *cur;
01011    int unlock = -1;
01012    int total_mod_loaded = 0;
01013 
01014    if (AST_LIST_TRYLOCK(&module_list))
01015       unlock = 0;
01016  
01017    AST_LIST_TRAVERSE(&module_list, cur, entry) {
01018       total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, like);
01019    }
01020 
01021    if (unlock)
01022       AST_LIST_UNLOCK(&module_list);
01023 
01024    return total_mod_loaded;
01025 }
01026 
01027 /*! \brief Check if module exists */
01028 int ast_module_check(const char *name)
01029 {
01030    struct ast_module *cur;
01031 
01032    if (ast_strlen_zero(name))
01033       return 0;       /* FALSE */
01034 
01035    cur = find_resource(name, 1);
01036 
01037    return (cur != NULL);
01038 }
01039 
01040 
01041 int ast_loader_register(int (*v)(void))
01042 {
01043    struct loadupdate *tmp;
01044 
01045    if (!(tmp = ast_malloc(sizeof(*tmp))))
01046       return -1;
01047 
01048    tmp->updater = v;
01049    AST_LIST_LOCK(&updaters);
01050    AST_LIST_INSERT_HEAD(&updaters, tmp, entry);
01051    AST_LIST_UNLOCK(&updaters);
01052 
01053    return 0;
01054 }
01055 
01056 int ast_loader_unregister(int (*v)(void))
01057 {
01058    struct loadupdate *cur;
01059 
01060    AST_LIST_LOCK(&updaters);
01061    AST_LIST_TRAVERSE_SAFE_BEGIN(&updaters, cur, entry) {
01062       if (cur->updater == v)  {
01063          AST_LIST_REMOVE_CURRENT(entry);
01064          break;
01065       }
01066    }
01067    AST_LIST_TRAVERSE_SAFE_END;
01068    AST_LIST_UNLOCK(&updaters);
01069 
01070    return cur ? 0 : -1;
01071 }
01072 
01073 struct ast_module *ast_module_ref(struct ast_module *mod)
01074 {
01075    ast_atomic_fetchadd_int(&mod->usecount, +1);
01076    ast_update_use_count();
01077 
01078    return mod;
01079 }
01080 
01081 void ast_module_unref(struct ast_module *mod)
01082 {
01083    ast_atomic_fetchadd_int(&mod->usecount, -1);
01084    ast_update_use_count();
01085 }

Generated on Thu Jul 9 13:40:36 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7