Sat Apr 26 2014 22:01:37

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 /*** MODULEINFO
00031    <support_level>core</support_level>
00032  ***/
00033 
00034 #include "asterisk.h"
00035 
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 402288 $")
00037 
00038 #include "asterisk/_private.h"
00039 #include "asterisk/paths.h"   /* use ast_config_AST_MODULE_DIR */
00040 #include <dirent.h>
00041 
00042 #include "asterisk/linkedlists.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/config.h"
00045 #include "asterisk/channel.h"
00046 #include "asterisk/term.h"
00047 #include "asterisk/acl.h"
00048 #include "asterisk/manager.h"
00049 #include "asterisk/cdr.h"
00050 #include "asterisk/enum.h"
00051 #include "asterisk/http.h"
00052 #include "asterisk/lock.h"
00053 #include "asterisk/features.h"
00054 #include "asterisk/dsp.h"
00055 #include "asterisk/udptl.h"
00056 #include "asterisk/heap.h"
00057 #include "asterisk/app.h"
00058 #include "asterisk/test.h"
00059 
00060 #include <dlfcn.h>
00061 
00062 #include "asterisk/md5.h"
00063 #include "asterisk/utils.h"
00064 
00065 /*** DOCUMENTATION
00066  ***/
00067 
00068 #ifndef RTLD_NOW
00069 #define RTLD_NOW 0
00070 #endif
00071 
00072 #ifndef RTLD_LOCAL
00073 #define RTLD_LOCAL 0
00074 #endif
00075 
00076 struct ast_module_user {
00077    struct ast_channel *chan;
00078    AST_LIST_ENTRY(ast_module_user) entry;
00079 };
00080 
00081 AST_LIST_HEAD(module_user_list, ast_module_user);
00082 
00083 static const unsigned char expected_key[] =
00084 { 0x87, 0x76, 0x79, 0x35, 0x23, 0xea, 0x3a, 0xd3,
00085   0x25, 0x2a, 0xbb, 0x35, 0x87, 0xe4, 0x22, 0x24 };
00086 
00087 static char buildopt_sum[33] = AST_BUILDOPT_SUM;
00088 
00089 static unsigned int embedding = 1; /* we always start out by registering embedded modules,
00090                   since they are here before we dlopen() any
00091                */
00092 
00093 struct ast_module {
00094    const struct ast_module_info *info;
00095    void *lib;              /* the shared lib, or NULL if embedded */
00096    int usecount;              /* the number of 'users' currently in this module */
00097    struct module_user_list users;         /* the list of users in the module */
00098    struct {
00099       unsigned int running:1;
00100       unsigned int declined:1;
00101    } flags;
00102    AST_LIST_ENTRY(ast_module) entry;
00103    char resource[0];
00104 };
00105 
00106 static AST_LIST_HEAD_STATIC(module_list, ast_module);
00107 
00108 const char *ast_module_name(const struct ast_module *mod)
00109 {
00110    if (!mod || !mod->info) {
00111       return NULL;
00112    }
00113 
00114    return mod->info->name;
00115 }
00116 
00117 /*
00118  * module_list is cleared by its constructor possibly after
00119  * we start accumulating embedded modules, so we need to
00120  * use another list (without the lock) to accumulate them.
00121  * Then we update the main list when embedding is done.
00122  */
00123 static struct module_list embedded_module_list;
00124 
00125 struct loadupdate {
00126    int (*updater)(void);
00127    AST_LIST_ENTRY(loadupdate) entry;
00128 };
00129 
00130 static AST_LIST_HEAD_STATIC(updaters, loadupdate);
00131 
00132 AST_MUTEX_DEFINE_STATIC(reloadlock);
00133 
00134 struct reload_queue_item {
00135    AST_LIST_ENTRY(reload_queue_item) entry;
00136    char module[0];
00137 };
00138 
00139 static int do_full_reload = 0;
00140 
00141 static AST_LIST_HEAD_STATIC(reload_queue, reload_queue_item);
00142 
00143 /* when dynamic modules are being loaded, ast_module_register() will
00144    need to know what filename the module was loaded from while it
00145    is being registered
00146 */
00147 static struct ast_module *resource_being_loaded;
00148 
00149 /* XXX: should we check for duplicate resource names here? */
00150 
00151 void ast_module_register(const struct ast_module_info *info)
00152 {
00153    struct ast_module *mod;
00154 
00155    if (embedding) {
00156       if (!(mod = ast_calloc(1, sizeof(*mod) + strlen(info->name) + 1)))
00157          return;
00158       strcpy(mod->resource, info->name);
00159    } else {
00160       mod = resource_being_loaded;
00161    }
00162 
00163    mod->info = info;
00164    AST_LIST_HEAD_INIT(&mod->users);
00165 
00166    /* during startup, before the loader has been initialized,
00167       there are no threads, so there is no need to take the lock
00168       on this list to manipulate it. it is also possible that it
00169       might be unsafe to use the list lock at that point... so
00170       let's avoid it altogether
00171    */
00172    if (embedding) {
00173       AST_LIST_INSERT_TAIL(&embedded_module_list, mod, entry);
00174    } else {
00175       AST_LIST_LOCK(&module_list);
00176       /* it is paramount that the new entry be placed at the tail of
00177          the list, otherwise the code that uses dlopen() to load
00178          dynamic modules won't be able to find out if the module it
00179          just opened was registered or failed to load
00180       */
00181       AST_LIST_INSERT_TAIL(&module_list, mod, entry);
00182       AST_LIST_UNLOCK(&module_list);
00183    }
00184 
00185    /* give the module a copy of its own handle, for later use in registrations and the like */
00186    *((struct ast_module **) &(info->self)) = mod;
00187 }
00188 
00189 void ast_module_unregister(const struct ast_module_info *info)
00190 {
00191    struct ast_module *mod = NULL;
00192 
00193    /* it is assumed that the users list in the module structure
00194       will already be empty, or we cannot have gotten to this
00195       point
00196    */
00197    AST_LIST_LOCK(&module_list);
00198    AST_LIST_TRAVERSE_SAFE_BEGIN(&module_list, mod, entry) {
00199       if (mod->info == info) {
00200          AST_LIST_REMOVE_CURRENT(entry);
00201          break;
00202       }
00203    }
00204    AST_LIST_TRAVERSE_SAFE_END;
00205    AST_LIST_UNLOCK(&module_list);
00206 
00207    if (mod) {
00208       AST_LIST_HEAD_DESTROY(&mod->users);
00209       ast_free(mod);
00210    }
00211 }
00212 
00213 struct ast_module_user *__ast_module_user_add(struct ast_module *mod, struct ast_channel *chan)
00214 {
00215    struct ast_module_user *u;
00216 
00217    u = ast_calloc(1, sizeof(*u));
00218    if (!u) {
00219       return NULL;
00220    }
00221 
00222    u->chan = chan;
00223 
00224    AST_LIST_LOCK(&mod->users);
00225    AST_LIST_INSERT_HEAD(&mod->users, u, entry);
00226    AST_LIST_UNLOCK(&mod->users);
00227 
00228    ast_atomic_fetchadd_int(&mod->usecount, +1);
00229 
00230    ast_update_use_count();
00231 
00232    return u;
00233 }
00234 
00235 void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u)
00236 {
00237    if (!u) {
00238       return;
00239    }
00240 
00241    AST_LIST_LOCK(&mod->users);
00242    u = AST_LIST_REMOVE(&mod->users, u, entry);
00243    AST_LIST_UNLOCK(&mod->users);
00244    if (!u) {
00245       /*
00246        * Was not in the list.  Either a bad pointer or
00247        * __ast_module_user_hangup_all() has been called.
00248        */
00249       return;
00250    }
00251 
00252    ast_atomic_fetchadd_int(&mod->usecount, -1);
00253    ast_free(u);
00254 
00255    ast_update_use_count();
00256 }
00257 
00258 void __ast_module_user_hangup_all(struct ast_module *mod)
00259 {
00260    struct ast_module_user *u;
00261 
00262    AST_LIST_LOCK(&mod->users);
00263    while ((u = AST_LIST_REMOVE_HEAD(&mod->users, entry))) {
00264       if (u->chan) {
00265          ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
00266       }
00267       ast_atomic_fetchadd_int(&mod->usecount, -1);
00268       ast_free(u);
00269    }
00270    AST_LIST_UNLOCK(&mod->users);
00271 
00272    ast_update_use_count();
00273 }
00274 
00275 /*! \note
00276  * In addition to modules, the reload command handles some extra keywords
00277  * which are listed here together with the corresponding handlers.
00278  * This table is also used by the command completion code.
00279  */
00280 static struct reload_classes {
00281    const char *name;
00282    int (*reload_fn)(void);
00283 } reload_classes[] = {  /* list in alpha order, longest match first for cli completion */
00284    { "cdr", ast_cdr_engine_reload },
00285    { "dnsmgr", dnsmgr_reload },
00286    { "extconfig", read_config_maps },
00287    { "enum",   ast_enum_reload },
00288    { "acl", ast_named_acl_reload },
00289    { "manager",   reload_manager },
00290    { "http",   ast_http_reload },
00291    { "logger", logger_reload },
00292    { "features",  ast_features_reload },
00293    { "dsp", ast_dsp_reload},
00294    { "udptl",  ast_udptl_reload },
00295    { "indications", ast_indications_reload },
00296    { "cel",        ast_cel_engine_reload },
00297    { "plc",        ast_plc_reload },
00298    { NULL,  NULL }
00299 };
00300 
00301 static int printdigest(const unsigned char *d)
00302 {
00303    int x, pos;
00304    char buf[256]; /* large enough so we don't have to worry */
00305 
00306    for (pos = 0, x = 0; x < 16; x++)
00307       pos += sprintf(buf + pos, " %02x", *d++);
00308 
00309    ast_debug(1, "Unexpected signature:%s\n", buf);
00310 
00311    return 0;
00312 }
00313 
00314 static int key_matches(const unsigned char *key1, const unsigned char *key2)
00315 {
00316    int x;
00317 
00318    for (x = 0; x < 16; x++) {
00319       if (key1[x] != key2[x])
00320          return 0;
00321    }
00322 
00323    return 1;
00324 }
00325 
00326 static int verify_key(const unsigned char *key)
00327 {
00328    struct MD5Context c;
00329    unsigned char digest[16];
00330 
00331    MD5Init(&c);
00332    MD5Update(&c, key, strlen((char *)key));
00333    MD5Final(digest, &c);
00334 
00335    if (key_matches(expected_key, digest))
00336       return 0;
00337 
00338    printdigest(digest);
00339 
00340    return -1;
00341 }
00342 
00343 static int resource_name_match(const char *name1_in, const char *name2_in)
00344 {
00345    char *name1 = (char *) name1_in;
00346    char *name2 = (char *) name2_in;
00347 
00348    /* trim off any .so extensions */
00349    if (!strcasecmp(name1 + strlen(name1) - 3, ".so")) {
00350       name1 = ast_strdupa(name1);
00351       name1[strlen(name1) - 3] = '\0';
00352    }
00353    if (!strcasecmp(name2 + strlen(name2) - 3, ".so")) {
00354       name2 = ast_strdupa(name2);
00355       name2[strlen(name2) - 3] = '\0';
00356    }
00357 
00358    return strcasecmp(name1, name2);
00359 }
00360 
00361 static struct ast_module *find_resource(const char *resource, int do_lock)
00362 {
00363    struct ast_module *cur;
00364 
00365    if (do_lock)
00366       AST_LIST_LOCK(&module_list);
00367 
00368    AST_LIST_TRAVERSE(&module_list, cur, entry) {
00369       if (!resource_name_match(resource, cur->resource))
00370          break;
00371    }
00372 
00373    if (do_lock)
00374       AST_LIST_UNLOCK(&module_list);
00375 
00376    return cur;
00377 }
00378 
00379 #ifdef LOADABLE_MODULES
00380 
00381 static void close_lib(const char *name, void *lib)
00382 {
00383    char *error;
00384 
00385    if (!lib) {
00386       return;
00387    }
00388 
00389    /* Clear any existing error */
00390    dlerror();
00391    if (dlclose(lib)) {
00392       error = dlerror();
00393       ast_log(AST_LOG_ERROR, "Failure in dlclose for module '%s': %s\n",
00394          S_OR(name, "unknown"), S_OR(error, "Unknown error"));
00395    }
00396 }
00397 
00398 static void unload_dynamic_module(struct ast_module *mod)
00399 {
00400    void *lib = mod->lib;
00401 
00402    /* WARNING: the structure pointed to by mod is going to
00403       disappear when this operation succeeds, so we can't
00404       dereference it */
00405    close_lib(ast_module_name(mod), lib);
00406 }
00407 
00408 static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, struct ast_heap *resource_heap, int required);
00409 
00410 static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only, struct ast_heap *resource_heap)
00411 {
00412    char fn[PATH_MAX] = "";
00413    void *lib = NULL;
00414    struct ast_module *mod;
00415    unsigned int wants_global;
00416    int space;  /* room needed for the descriptor */
00417    int missing_so = 0;
00418 
00419    space = sizeof(*resource_being_loaded) + strlen(resource_in) + 1;
00420    if (strcasecmp(resource_in + strlen(resource_in) - 3, ".so")) {
00421       missing_so = 1;
00422       space += 3; /* room for the extra ".so" */
00423    }
00424 
00425    snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, missing_so ? ".so" : "");
00426 
00427    /* make a first load of the module in 'quiet' mode... don't try to resolve
00428       any symbols, and don't export any symbols. this will allow us to peek into
00429       the module's info block (if available) to see what flags it has set */
00430 
00431    resource_being_loaded = ast_calloc(1, space);
00432    if (!resource_being_loaded)
00433       return NULL;
00434    strcpy(resource_being_loaded->resource, resource_in);
00435    if (missing_so)
00436       strcat(resource_being_loaded->resource, ".so");
00437 
00438    if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_LOCAL))) {
00439       ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
00440       ast_free(resource_being_loaded);
00441       return NULL;
00442    }
00443 
00444    /* the dlopen() succeeded, let's find out if the module
00445       registered itself */
00446    /* note that this will only work properly as long as
00447       ast_module_register() (which is called by the module's
00448       constructor) places the new module at the tail of the
00449       module_list
00450    */
00451    if (resource_being_loaded != (mod = AST_LIST_LAST(&module_list))) {
00452       ast_log(LOG_WARNING, "Module '%s' did not register itself during load\n", resource_in);
00453       /* no, it did not, so close it and return */
00454       close_lib(resource_in, lib);
00455       /* note that the module's destructor will call ast_module_unregister(),
00456          which will free the structure we allocated in resource_being_loaded */
00457       return NULL;
00458    }
00459 
00460    wants_global = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS);
00461 
00462    /* if we are being asked only to load modules that provide global symbols,
00463       and this one does not, then close it and return */
00464    if (global_symbols_only && !wants_global) {
00465       close_lib(resource_in, lib);
00466       return NULL;
00467    }
00468 
00469    /* This section is a workaround for a gcc 4.1 bug that has already been
00470     * fixed in later versions.  Unfortunately, some distributions, such as
00471     * RHEL/CentOS 5, distribute gcc 4.1, so we're stuck with having to deal
00472     * with this issue.  This basically ensures that optional_api modules are
00473     * loaded before any module which requires their functionality. */
00474 #if !defined(HAVE_ATTRIBUTE_weak_import) && !defined(HAVE_ATTRIBUTE_weakref)
00475    if (!ast_strlen_zero(mod->info->nonoptreq)) {
00476       /* Force any required dependencies to load */
00477       char *each, *required_resource = ast_strdupa(mod->info->nonoptreq);
00478       while ((each = strsep(&required_resource, ","))) {
00479          struct ast_module *dependency;
00480          each = ast_strip(each);
00481          dependency = find_resource(each, 0);
00482          /* Is it already loaded? */
00483          if (!dependency) {
00484             load_resource(each, global_symbols_only, resource_heap, 1);
00485          }
00486       }
00487    }
00488 #endif
00489 
00490    close_lib(resource_in, lib);
00491    resource_being_loaded = NULL;
00492 
00493    /* start the load process again */
00494    resource_being_loaded = ast_calloc(1, space);
00495    if (!resource_being_loaded)
00496       return NULL;
00497    strcpy(resource_being_loaded->resource, resource_in);
00498    if (missing_so)
00499       strcat(resource_being_loaded->resource, ".so");
00500 
00501    if (!(lib = dlopen(fn, wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) {
00502       ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
00503       ast_free(resource_being_loaded);
00504       return NULL;
00505    }
00506 
00507    /* since the module was successfully opened, and it registered itself
00508       the previous time we did that, we're going to assume it worked this
00509       time too :) */
00510 
00511    AST_LIST_LAST(&module_list)->lib = lib;
00512    resource_being_loaded = NULL;
00513 
00514    return AST_LIST_LAST(&module_list);
00515 }
00516 #endif
00517 
00518 void ast_module_shutdown(void)
00519 {
00520    struct ast_module *mod;
00521    int somethingchanged = 1, final = 0;
00522 
00523    AST_LIST_LOCK(&module_list);
00524 
00525    /*!\note Some resources, like timers, are started up dynamically, and thus
00526     * may be still in use, even if all channels are dead.  We must therefore
00527     * check the usecount before asking modules to unload. */
00528    do {
00529       if (!somethingchanged) {
00530          /*!\note If we go through the entire list without changing
00531           * anything, ignore the usecounts and unload, then exit. */
00532          final = 1;
00533       }
00534 
00535       /* Reset flag before traversing the list */
00536       somethingchanged = 0;
00537 
00538       AST_LIST_TRAVERSE_SAFE_BEGIN(&module_list, mod, entry) {
00539          if (!final && mod->usecount) {
00540             continue;
00541          }
00542          AST_LIST_REMOVE_CURRENT(entry);
00543          if (mod->flags.running && !mod->flags.declined && mod->info->unload) {
00544             mod->info->unload();
00545          }
00546          AST_LIST_HEAD_DESTROY(&mod->users);
00547          free(mod);
00548          somethingchanged = 1;
00549       }
00550       AST_LIST_TRAVERSE_SAFE_END;
00551    } while (somethingchanged && !final);
00552 
00553    AST_LIST_UNLOCK(&module_list);
00554 }
00555 
00556 int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode force)
00557 {
00558    struct ast_module *mod;
00559    int res = -1;
00560    int error = 0;
00561 
00562    AST_LIST_LOCK(&module_list);
00563 
00564    if (!(mod = find_resource(resource_name, 0))) {
00565       AST_LIST_UNLOCK(&module_list);
00566       ast_log(LOG_WARNING, "Unload failed, '%s' could not be found\n", resource_name);
00567       return -1;
00568    }
00569 
00570    if (!mod->flags.running || mod->flags.declined) {
00571       ast_log(LOG_WARNING, "Unload failed, '%s' is not loaded.\n", resource_name);
00572       error = 1;
00573    }
00574 
00575    if (!error && (mod->usecount > 0)) {
00576       if (force)
00577          ast_log(LOG_WARNING, "Warning:  Forcing removal of module '%s' with use count %d\n",
00578             resource_name, mod->usecount);
00579       else {
00580          ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name,
00581             mod->usecount);
00582          error = 1;
00583       }
00584    }
00585 
00586    if (!error) {
00587       /* Request any channels attached to the module to hangup. */
00588       __ast_module_user_hangup_all(mod);
00589 
00590       res = mod->info->unload();
00591       if (res) {
00592          ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
00593          if (force <= AST_FORCE_FIRM) {
00594             error = 1;
00595          } else {
00596             ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
00597          }
00598       }
00599 
00600       if (!error) {
00601          /*
00602           * Request hangup on any channels that managed to get attached
00603           * while we called the module unload function.
00604           */
00605          __ast_module_user_hangup_all(mod);
00606          sched_yield();
00607       }
00608    }
00609 
00610    if (!error)
00611       mod->flags.running = mod->flags.declined = 0;
00612 
00613    AST_LIST_UNLOCK(&module_list);
00614 
00615    if (!error && !mod->lib && mod->info && mod->info->restore_globals)
00616       mod->info->restore_globals();
00617 
00618 #ifdef LOADABLE_MODULES
00619    if (!error) {
00620       unload_dynamic_module(mod);
00621       ast_test_suite_event_notify("MODULE_UNLOAD", "Message: %s", resource_name);
00622    }
00623 #endif
00624 
00625    if (!error)
00626       ast_update_use_count();
00627 
00628    return res;
00629 }
00630 
00631 char *ast_module_helper(const char *line, const char *word, int pos, int state, int rpos, int needsreload)
00632 {
00633    struct ast_module *cur;
00634    int i, which=0, l = strlen(word);
00635    char *ret = NULL;
00636 
00637    if (pos != rpos)
00638       return NULL;
00639 
00640    AST_LIST_LOCK(&module_list);
00641    AST_LIST_TRAVERSE(&module_list, cur, entry) {
00642       if (!strncasecmp(word, cur->resource, l) &&
00643           (cur->info->reload || !needsreload) &&
00644           ++which > state) {
00645          ret = ast_strdup(cur->resource);
00646          break;
00647       }
00648    }
00649    AST_LIST_UNLOCK(&module_list);
00650 
00651    if (!ret) {
00652       for (i=0; !ret && reload_classes[i].name; i++) {
00653          if (!strncasecmp(word, reload_classes[i].name, l) && ++which > state)
00654             ret = ast_strdup(reload_classes[i].name);
00655       }
00656    }
00657 
00658    return ret;
00659 }
00660 
00661 void ast_process_pending_reloads(void)
00662 {
00663    struct reload_queue_item *item;
00664 
00665    if (!ast_fully_booted) {
00666       return;
00667    }
00668 
00669    AST_LIST_LOCK(&reload_queue);
00670 
00671    if (do_full_reload) {
00672       do_full_reload = 0;
00673       AST_LIST_UNLOCK(&reload_queue);
00674       ast_log(LOG_NOTICE, "Executing deferred reload request.\n");
00675       ast_module_reload(NULL);
00676       return;
00677    }
00678 
00679    while ((item = AST_LIST_REMOVE_HEAD(&reload_queue, entry))) {
00680       ast_log(LOG_NOTICE, "Executing deferred reload request for module '%s'.\n", item->module);
00681       ast_module_reload(item->module);
00682       ast_free(item);
00683    }
00684 
00685    AST_LIST_UNLOCK(&reload_queue);
00686 }
00687 
00688 static void queue_reload_request(const char *module)
00689 {
00690    struct reload_queue_item *item;
00691 
00692    AST_LIST_LOCK(&reload_queue);
00693 
00694    if (do_full_reload) {
00695       AST_LIST_UNLOCK(&reload_queue);
00696       return;
00697    }
00698 
00699    if (ast_strlen_zero(module)) {
00700       /* A full reload request (when module is NULL) wipes out any previous
00701          reload requests and causes the queue to ignore future ones */
00702       while ((item = AST_LIST_REMOVE_HEAD(&reload_queue, entry))) {
00703          ast_free(item);
00704       }
00705       do_full_reload = 1;
00706    } else {
00707       /* No reason to add the same module twice */
00708       AST_LIST_TRAVERSE(&reload_queue, item, entry) {
00709          if (!strcasecmp(item->module, module)) {
00710             AST_LIST_UNLOCK(&reload_queue);
00711             return;
00712          }
00713       }
00714       item = ast_calloc(1, sizeof(*item) + strlen(module) + 1);
00715       if (!item) {
00716          ast_log(LOG_ERROR, "Failed to allocate reload queue item.\n");
00717          AST_LIST_UNLOCK(&reload_queue);
00718          return;
00719       }
00720       strcpy(item->module, module);
00721       AST_LIST_INSERT_TAIL(&reload_queue, item, entry);
00722    }
00723    AST_LIST_UNLOCK(&reload_queue);
00724 }
00725 
00726 int ast_module_reload(const char *name)
00727 {
00728    struct ast_module *cur;
00729    int res = 0; /* return value. 0 = not found, others, see below */
00730    int i;
00731 
00732    /* If we aren't fully booted, we just pretend we reloaded but we queue this
00733       up to run once we are booted up. */
00734    if (!ast_fully_booted) {
00735       queue_reload_request(name);
00736       return 0;
00737    }
00738 
00739    if (ast_mutex_trylock(&reloadlock)) {
00740       ast_verbose("The previous reload command didn't finish yet\n");
00741       return -1;  /* reload already in progress */
00742    }
00743    ast_lastreloadtime = ast_tvnow();
00744 
00745    if (ast_opt_lock_confdir) {
00746       int try;
00747       int res;
00748       for (try = 1, res = AST_LOCK_TIMEOUT; try < 6 && (res == AST_LOCK_TIMEOUT); try++) {
00749          res = ast_lock_path(ast_config_AST_CONFIG_DIR);
00750          if (res == AST_LOCK_TIMEOUT) {
00751             ast_log(LOG_WARNING, "Failed to grab lock on %s, try %d\n", ast_config_AST_CONFIG_DIR, try);
00752          }
00753       }
00754       if (res != AST_LOCK_SUCCESS) {
00755          ast_verbose("Cannot grab lock on %s\n", ast_config_AST_CONFIG_DIR);
00756          ast_mutex_unlock(&reloadlock);
00757          return -1;
00758       }
00759    }
00760 
00761    /* Call "predefined" reload here first */
00762    for (i = 0; reload_classes[i].name; i++) {
00763       if (!name || !strcasecmp(name, reload_classes[i].name)) {
00764          if (!reload_classes[i].reload_fn()) {
00765             ast_test_suite_event_notify("MODULE_RELOAD", "Message: %s", name);
00766          }
00767          res = 2; /* found and reloaded */
00768       }
00769    }
00770 
00771    if (name && res) {
00772       if (ast_opt_lock_confdir) {
00773          ast_unlock_path(ast_config_AST_CONFIG_DIR);
00774       }
00775       ast_mutex_unlock(&reloadlock);
00776       return res;
00777    }
00778 
00779    AST_LIST_LOCK(&module_list);
00780    AST_LIST_TRAVERSE(&module_list, cur, entry) {
00781       const struct ast_module_info *info = cur->info;
00782 
00783       if (name && resource_name_match(name, cur->resource))
00784          continue;
00785 
00786       if (!cur->flags.running || cur->flags.declined) {
00787          if (!name)
00788             continue;
00789          ast_log(LOG_NOTICE, "The module '%s' was not properly initialized.  "
00790             "Before reloading the module, you must run \"module load %s\" "
00791             "and fix whatever is preventing the module from being initialized.\n",
00792             name, name);
00793          res = 2; /* Don't report that the module was not found */
00794          break;
00795       }
00796 
00797       if (!info->reload) { /* cannot be reloaded */
00798          /* Nothing to reload, so reload is successful */
00799          ast_test_suite_event_notify("MODULE_RELOAD", "Message: %s", cur->resource);
00800          if (res < 1)   /* store result if possible */
00801             res = 1; /* 1 = no reload() method */
00802          continue;
00803       }
00804 
00805       res = 2;
00806       ast_verb(3, "Reloading module '%s' (%s)\n", cur->resource, info->description);
00807       if (!info->reload()) {
00808          ast_test_suite_event_notify("MODULE_RELOAD", "Message: %s", cur->resource);
00809       }
00810    }
00811    AST_LIST_UNLOCK(&module_list);
00812 
00813    if (ast_opt_lock_confdir) {
00814       ast_unlock_path(ast_config_AST_CONFIG_DIR);
00815    }
00816    ast_mutex_unlock(&reloadlock);
00817 
00818    return res;
00819 }
00820 
00821 static unsigned int inspect_module(const struct ast_module *mod)
00822 {
00823    if (!mod->info->description) {
00824       ast_log(LOG_WARNING, "Module '%s' does not provide a description.\n", mod->resource);
00825       return 1;
00826    }
00827 
00828    if (!mod->info->key) {
00829       ast_log(LOG_WARNING, "Module '%s' does not provide a license key.\n", mod->resource);
00830       return 1;
00831    }
00832 
00833    if (verify_key((unsigned char *) mod->info->key)) {
00834       ast_log(LOG_WARNING, "Module '%s' did not provide a valid license key.\n", mod->resource);
00835       return 1;
00836    }
00837 
00838    if (!ast_strlen_zero(mod->info->buildopt_sum) &&
00839        strcmp(buildopt_sum, mod->info->buildopt_sum)) {
00840       ast_log(LOG_WARNING, "Module '%s' was not compiled with the same compile-time options as this version of Asterisk.\n", mod->resource);
00841       ast_log(LOG_WARNING, "Module '%s' will not be initialized as it may cause instability.\n", mod->resource);
00842       return 1;
00843    }
00844 
00845    return 0;
00846 }
00847 
00848 static enum ast_module_load_result start_resource(struct ast_module *mod)
00849 {
00850    char tmp[256];
00851    enum ast_module_load_result res;
00852 
00853    if (mod->flags.running) {
00854       return AST_MODULE_LOAD_SUCCESS;
00855    }
00856 
00857    if (!mod->info->load) {
00858       return AST_MODULE_LOAD_FAILURE;
00859    }
00860 
00861    res = mod->info->load();
00862 
00863    switch (res) {
00864    case AST_MODULE_LOAD_SUCCESS:
00865       if (!ast_fully_booted) {
00866          ast_verb(1, "%s => (%s)\n", mod->resource, term_color(tmp, mod->info->description, COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
00867          if (ast_opt_console && !option_verbose) {
00868             /* This never looks good on anything but the root console, so
00869              * it's best not to try to funnel it through the logger. */
00870             fprintf(stdout, ".");
00871          }
00872       } else {
00873          ast_verb(1, "Loaded %s => (%s)\n", mod->resource, mod->info->description);
00874       }
00875 
00876       mod->flags.running = 1;
00877 
00878       ast_update_use_count();
00879       break;
00880    case AST_MODULE_LOAD_DECLINE:
00881       mod->flags.declined = 1;
00882       break;
00883    case AST_MODULE_LOAD_FAILURE:
00884    case AST_MODULE_LOAD_SKIP: /* modules should never return this value */
00885    case AST_MODULE_LOAD_PRIORITY:
00886       break;
00887    }
00888 
00889    return res;
00890 }
00891 
00892 /*! loads a resource based upon resource_name. If global_symbols_only is set
00893  *  only modules with global symbols will be loaded.
00894  *
00895  *  If the ast_heap is provided (not NULL) the module is found and added to the
00896  *  heap without running the module's load() function.  By doing this, modules
00897  *  added to the resource_heap can be initialized later in order by priority.
00898  *
00899  *  If the ast_heap is not provided, the module's load function will be executed
00900  *  immediately */
00901 static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, struct ast_heap *resource_heap, int required)
00902 {
00903    struct ast_module *mod;
00904    enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS;
00905 
00906    if ((mod = find_resource(resource_name, 0))) {
00907       if (mod->flags.running) {
00908          ast_log(LOG_WARNING, "Module '%s' already exists.\n", resource_name);
00909          return AST_MODULE_LOAD_DECLINE;
00910       }
00911       if (global_symbols_only && !ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS))
00912          return AST_MODULE_LOAD_SKIP;
00913    } else {
00914 #ifdef LOADABLE_MODULES
00915       if (!(mod = load_dynamic_module(resource_name, global_symbols_only, resource_heap))) {
00916          /* don't generate a warning message during load_modules() */
00917          if (!global_symbols_only) {
00918             ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
00919             return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE;
00920          } else {
00921             return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SKIP;
00922          }
00923       }
00924 #else
00925       ast_log(LOG_WARNING, "Module support is not available. Module '%s' could not be loaded.\n", resource_name);
00926       return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE;
00927 #endif
00928    }
00929 
00930    if (inspect_module(mod)) {
00931       ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
00932 #ifdef LOADABLE_MODULES
00933       unload_dynamic_module(mod);
00934 #endif
00935       return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE;
00936    }
00937 
00938    if (!mod->lib && mod->info->backup_globals && mod->info->backup_globals()) {
00939       ast_log(LOG_WARNING, "Module '%s' was unable to backup its global data.\n", resource_name);
00940       return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE;
00941    }
00942 
00943    mod->flags.declined = 0;
00944 
00945    if (resource_heap) {
00946       ast_heap_push(resource_heap, mod);
00947       res = AST_MODULE_LOAD_PRIORITY;
00948    } else {
00949       res = start_resource(mod);
00950    }
00951 
00952    /* Now make sure that the list is sorted */
00953    AST_LIST_LOCK(&module_list);
00954    AST_LIST_REMOVE(&module_list, mod, entry);
00955    AST_LIST_INSERT_SORTALPHA(&module_list, mod, entry, resource);
00956    AST_LIST_UNLOCK(&module_list);
00957 
00958    return res;
00959 }
00960 
00961 int ast_load_resource(const char *resource_name)
00962 {
00963    int res;
00964    AST_LIST_LOCK(&module_list);
00965    res = load_resource(resource_name, 0, NULL, 0);
00966    if (!res) {
00967       ast_test_suite_event_notify("MODULE_LOAD", "Message: %s", resource_name);
00968    }
00969    AST_LIST_UNLOCK(&module_list);
00970 
00971    return res;
00972 }
00973 
00974 struct load_order_entry {
00975    char *resource;
00976    int required;
00977    AST_LIST_ENTRY(load_order_entry) entry;
00978 };
00979 
00980 AST_LIST_HEAD_NOLOCK(load_order, load_order_entry);
00981 
00982 static struct load_order_entry *add_to_load_order(const char *resource, struct load_order *load_order, int required)
00983 {
00984    struct load_order_entry *order;
00985 
00986    AST_LIST_TRAVERSE(load_order, order, entry) {
00987       if (!resource_name_match(order->resource, resource)) {
00988          /* Make sure we have the proper setting for the required field
00989             (we might have both load= and required= lines in modules.conf) */
00990          order->required |= required;
00991          return NULL;
00992       }
00993    }
00994 
00995    if (!(order = ast_calloc(1, sizeof(*order))))
00996       return NULL;
00997 
00998    order->resource = ast_strdup(resource);
00999    order->required = required;
01000    AST_LIST_INSERT_TAIL(load_order, order, entry);
01001 
01002    return order;
01003 }
01004 
01005 static int mod_load_cmp(void *a, void *b)
01006 {
01007    struct ast_module *a_mod = (struct ast_module *) a;
01008    struct ast_module *b_mod = (struct ast_module *) b;
01009    int res = -1;
01010    /* if load_pri is not set, default is 128.  Lower is better*/
01011    unsigned char a_pri = ast_test_flag(a_mod->info, AST_MODFLAG_LOAD_ORDER) ? a_mod->info->load_pri : 128;
01012    unsigned char b_pri = ast_test_flag(b_mod->info, AST_MODFLAG_LOAD_ORDER) ? b_mod->info->load_pri : 128;
01013    if (a_pri == b_pri) {
01014       res = 0;
01015    } else if (a_pri < b_pri) {
01016       res = 1;
01017    }
01018    return res;
01019 }
01020 
01021 /*! loads modules in order by load_pri, updates mod_count
01022    \return -1 on failure to load module, -2 on failure to load required module, otherwise 0
01023 */
01024 static int load_resource_list(struct load_order *load_order, unsigned int global_symbols, int *mod_count)
01025 {
01026    struct ast_heap *resource_heap;
01027    struct load_order_entry *order;
01028    struct ast_module *mod;
01029    int count = 0;
01030    int res = 0;
01031 
01032    if(!(resource_heap = ast_heap_create(8, mod_load_cmp, -1))) {
01033       return -1;
01034    }
01035 
01036    /* first, add find and add modules to heap */
01037    AST_LIST_TRAVERSE_SAFE_BEGIN(load_order, order, entry) {
01038       switch (load_resource(order->resource, global_symbols, resource_heap, order->required)) {
01039       case AST_MODULE_LOAD_SUCCESS:
01040       case AST_MODULE_LOAD_DECLINE:
01041          AST_LIST_REMOVE_CURRENT(entry);
01042          ast_free(order->resource);
01043          ast_free(order);
01044          break;
01045       case AST_MODULE_LOAD_FAILURE:
01046          ast_log(LOG_ERROR, "*** Failed to load module %s - %s\n", order->resource, order->required ? "Required" : "Not required");
01047          fprintf(stderr, "*** Failed to load module %s - %s\n", order->resource, order->required ? "Required" : "Not required");
01048          res = order->required ? -2 : -1;
01049          goto done;
01050       case AST_MODULE_LOAD_SKIP:
01051          break;
01052       case AST_MODULE_LOAD_PRIORITY:
01053          AST_LIST_REMOVE_CURRENT(entry);
01054          ast_free(order->resource);
01055          ast_free(order);
01056          break;
01057       }
01058    }
01059    AST_LIST_TRAVERSE_SAFE_END;
01060 
01061    /* second remove modules from heap sorted by priority */
01062    while ((mod = ast_heap_pop(resource_heap))) {
01063       switch (start_resource(mod)) {
01064       case AST_MODULE_LOAD_SUCCESS:
01065          count++;
01066       case AST_MODULE_LOAD_DECLINE:
01067          break;
01068       case AST_MODULE_LOAD_FAILURE:
01069          res = -1;
01070          goto done;
01071       case AST_MODULE_LOAD_SKIP:
01072       case AST_MODULE_LOAD_PRIORITY:
01073          break;
01074       }
01075    }
01076 
01077 done:
01078    if (mod_count) {
01079       *mod_count += count;
01080    }
01081    ast_heap_destroy(resource_heap);
01082 
01083    return res;
01084 }
01085 
01086 int load_modules(unsigned int preload_only)
01087 {
01088    struct ast_config *cfg;
01089    struct ast_module *mod;
01090    struct load_order_entry *order;
01091    struct ast_variable *v;
01092    unsigned int load_count;
01093    struct load_order load_order;
01094    int res = 0;
01095    struct ast_flags config_flags = { 0 };
01096    int modulecount = 0;
01097 
01098 #ifdef LOADABLE_MODULES
01099    struct dirent *dirent;
01100    DIR *dir;
01101 #endif
01102 
01103    /* all embedded modules have registered themselves by now */
01104    embedding = 0;
01105 
01106    ast_verb(1, "Asterisk Dynamic Loader Starting:\n");
01107 
01108    AST_LIST_HEAD_INIT_NOLOCK(&load_order);
01109 
01110    AST_LIST_LOCK(&module_list);
01111 
01112    if (embedded_module_list.first) {
01113       module_list.first = embedded_module_list.first;
01114       module_list.last = embedded_module_list.last;
01115       embedded_module_list.first = NULL;
01116    }
01117 
01118    cfg = ast_config_load2(AST_MODULE_CONFIG, "" /* core, can't reload */, config_flags);
01119    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01120       ast_log(LOG_WARNING, "No '%s' found, no modules will be loaded.\n", AST_MODULE_CONFIG);
01121       goto done;
01122    }
01123 
01124    /* first, find all the modules we have been explicitly requested to load */
01125    for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
01126       if (!strcasecmp(v->name, preload_only ? "preload" : "load")) {
01127          add_to_load_order(v->value, &load_order, 0);
01128       }
01129       if (!strcasecmp(v->name, preload_only ? "preload-require" : "require")) {
01130          /* Add the module to the list and make sure it's required */
01131          add_to_load_order(v->value, &load_order, 1);
01132          ast_debug(2, "Adding module to required list: %s (%s)\n", v->value, v->name);
01133       }
01134 
01135    }
01136 
01137    /* check if 'autoload' is on */
01138    if (!preload_only && ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
01139       /* if so, first add all the embedded modules that are not already running to the load order */
01140       AST_LIST_TRAVERSE(&module_list, mod, entry) {
01141          /* if it's not embedded, skip it */
01142          if (mod->lib)
01143             continue;
01144 
01145          if (mod->flags.running)
01146             continue;
01147 
01148          add_to_load_order(mod->resource, &load_order, 0);
01149       }
01150 
01151 #ifdef LOADABLE_MODULES
01152       /* if we are allowed to load dynamic modules, scan the directory for
01153          for all available modules and add them as well */
01154       if ((dir = opendir(ast_config_AST_MODULE_DIR))) {
01155          while ((dirent = readdir(dir))) {
01156             int ld = strlen(dirent->d_name);
01157 
01158             /* Must end in .so to load it.  */
01159 
01160             if (ld < 4)
01161                continue;
01162 
01163             if (strcasecmp(dirent->d_name + ld - 3, ".so"))
01164                continue;
01165 
01166             /* if there is already a module by this name in the module_list,
01167                skip this file */
01168             if (find_resource(dirent->d_name, 0))
01169                continue;
01170 
01171             add_to_load_order(dirent->d_name, &load_order, 0);
01172          }
01173 
01174          closedir(dir);
01175       } else {
01176          if (!ast_opt_quiet)
01177             ast_log(LOG_WARNING, "Unable to open modules directory '%s'.\n",
01178                ast_config_AST_MODULE_DIR);
01179       }
01180 #endif
01181    }
01182 
01183    /* now scan the config for any modules we are prohibited from loading and
01184       remove them from the load order */
01185    for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
01186       if (strcasecmp(v->name, "noload"))
01187          continue;
01188 
01189       AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
01190          if (!resource_name_match(order->resource, v->value)) {
01191             AST_LIST_REMOVE_CURRENT(entry);
01192             ast_free(order->resource);
01193             ast_free(order);
01194          }
01195       }
01196       AST_LIST_TRAVERSE_SAFE_END;
01197    }
01198 
01199    /* we are done with the config now, all the information we need is in the
01200       load_order list */
01201    ast_config_destroy(cfg);
01202 
01203    load_count = 0;
01204    AST_LIST_TRAVERSE(&load_order, order, entry)
01205       load_count++;
01206 
01207    if (load_count)
01208       ast_log(LOG_NOTICE, "%d modules will be loaded.\n", load_count);
01209 
01210    /* first, load only modules that provide global symbols */
01211    if ((res = load_resource_list(&load_order, 1, &modulecount)) < 0) {
01212       goto done;
01213    }
01214 
01215    /* now load everything else */
01216    if ((res = load_resource_list(&load_order, 0, &modulecount)) < 0) {
01217       goto done;
01218    }
01219 
01220 done:
01221    while ((order = AST_LIST_REMOVE_HEAD(&load_order, entry))) {
01222       ast_free(order->resource);
01223       ast_free(order);
01224    }
01225 
01226    AST_LIST_UNLOCK(&module_list);
01227 
01228    /* Tell manager clients that are aggressive at logging in that we're done
01229       loading modules. If there's a DNS problem in chan_sip, we might not
01230       even reach this */
01231    /*** DOCUMENTATION
01232       <managerEventInstance>
01233          <synopsis>Raised when all dynamic modules have finished their initial loading.</synopsis>
01234          <syntax>
01235             <parameter name="ModuleSelection">
01236                <enumlist>
01237                   <enum name="Preload"/>
01238                   <enum name="All"/>
01239                </enumlist>
01240             </parameter>
01241          </syntax>
01242       </managerEventInstance>
01243    ***/
01244    manager_event(EVENT_FLAG_SYSTEM, "ModuleLoadReport", "ModuleLoadStatus: Done\r\nModuleSelection: %s\r\nModuleCount: %d\r\n", preload_only ? "Preload" : "All", modulecount);
01245 
01246    return res;
01247 }
01248 
01249 void ast_update_use_count(void)
01250 {
01251    /* Notify any module monitors that the use count for a
01252       resource has changed */
01253    struct loadupdate *m;
01254 
01255    AST_LIST_LOCK(&updaters);
01256    AST_LIST_TRAVERSE(&updaters, m, entry)
01257       m->updater();
01258    AST_LIST_UNLOCK(&updaters);
01259 }
01260 
01261 int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),
01262             const char *like)
01263 {
01264    struct ast_module *cur;
01265    int unlock = -1;
01266    int total_mod_loaded = 0;
01267 
01268    if (AST_LIST_TRYLOCK(&module_list))
01269       unlock = 0;
01270 
01271    AST_LIST_TRAVERSE(&module_list, cur, entry) {
01272       total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, like);
01273    }
01274 
01275    if (unlock)
01276       AST_LIST_UNLOCK(&module_list);
01277 
01278    return total_mod_loaded;
01279 }
01280 
01281 /*! \brief Check if module exists */
01282 int ast_module_check(const char *name)
01283 {
01284    struct ast_module *cur;
01285 
01286    if (ast_strlen_zero(name))
01287       return 0;       /* FALSE */
01288 
01289    cur = find_resource(name, 1);
01290 
01291    return (cur != NULL);
01292 }
01293 
01294 
01295 int ast_loader_register(int (*v)(void))
01296 {
01297    struct loadupdate *tmp;
01298 
01299    if (!(tmp = ast_malloc(sizeof(*tmp))))
01300       return -1;
01301 
01302    tmp->updater = v;
01303    AST_LIST_LOCK(&updaters);
01304    AST_LIST_INSERT_HEAD(&updaters, tmp, entry);
01305    AST_LIST_UNLOCK(&updaters);
01306 
01307    return 0;
01308 }
01309 
01310 int ast_loader_unregister(int (*v)(void))
01311 {
01312    struct loadupdate *cur;
01313 
01314    AST_LIST_LOCK(&updaters);
01315    AST_LIST_TRAVERSE_SAFE_BEGIN(&updaters, cur, entry) {
01316       if (cur->updater == v)  {
01317          AST_LIST_REMOVE_CURRENT(entry);
01318          break;
01319       }
01320    }
01321    AST_LIST_TRAVERSE_SAFE_END;
01322    AST_LIST_UNLOCK(&updaters);
01323 
01324    return cur ? 0 : -1;
01325 }
01326 
01327 struct ast_module *ast_module_ref(struct ast_module *mod)
01328 {
01329    if (!mod) {
01330       return NULL;
01331    }
01332 
01333    ast_atomic_fetchadd_int(&mod->usecount, +1);
01334    ast_update_use_count();
01335 
01336    return mod;
01337 }
01338 
01339 void ast_module_unref(struct ast_module *mod)
01340 {
01341    if (!mod) {
01342       return;
01343    }
01344 
01345    ast_atomic_fetchadd_int(&mod->usecount, -1);
01346    ast_update_use_count();
01347 }