Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:54 2009

## Asterisk developer's documentation

- Main Page
- Related Pages
- Modules
- Data Structures
- Files
- Directories

# loader.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 1999 - 2005, Digium, Inc.
    00005  *
    00006  * Mark Spencer <markster@digium.com>
    00007  *
    00008  * See http://www.asterisk.org for more information about
    00009  * the Asterisk project. Please do not directly contact
    00010  * any of the maintainers of this project for assistance;
    00011  * the project provides a web site, mailing lists and IRC
    00012  * channels for your use.
    00013  *
    00014  * This program is free software, distributed under the terms of
    00015  * the GNU General Public License Version 2. See the LICENSE file
    00016  * at the top of the source tree.
    00017  */
    00018
    00019 /*! \file
    00020  *
    00021  * \brief Module Loader
    00022  *
    00023  */
    00024
    00025 #include <stdio.h>
    00026 #include <dirent.h>
    00027 #include <unistd.h>
    00028 #include <stdlib.h>
    00029 #include <string.h>
    00030
    00031 #include "asterisk.h"
    00032
    00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 61704 $")
    00034
    00035 #include "asterisk/module.h"
    00036 #include "asterisk/options.h"
    00037 #include "asterisk/config.h"
    00038 #include "asterisk/logger.h"
    00039 #include "asterisk/channel.h"
    00040 #include "asterisk/term.h"
    00041 #include "asterisk/manager.h"
    00042 #include "asterisk/cdr.h"
    00043 #include "asterisk/enum.h"
    00044 #include "asterisk/rtp.h"
    00045 #include "asterisk/lock.h"
    00046 #ifdef DLFCNCOMPAT
    00047 #include "asterisk/dlfcn-compat.h"
    00048 #else
    00049 #include <dlfcn.h>
    00050 #endif
    00051 #include "asterisk/md5.h"
    00052
    00053 #ifndef RTLD_NOW
    00054 #define RTLD_NOW 0
    00055 #endif
    00056
    00057 AST_MUTEX_DEFINE_STATIC(modlock);
    00058 AST_MUTEX_DEFINE_STATIC(reloadlock);
    00059
    00060 static struct module *module_list=NULL;
    00061 static int modlistver = 0;
    00062
    00063 static unsigned char expected_key[] =
    00064 { 0x8e, 0x93, 0x22, 0x83, 0xf5, 0xc3, 0xc0, 0x75,
    00065   0xff, 0x8b, 0xa9, 0xbe, 0x7c, 0x43, 0x74, 0x63 };
    00066
    00067 struct module {
    00068    int (*load_module)(void);
    00069    int (*unload_module)(void);
    00070    int (*usecount)(void);
    00071    char *(*description)(void);
    00072    char *(*key)(void);
    00073    int (*reload)(void);
    00074    void *lib;
    00075    char resource[256];
    00076    struct module *next;
    00077 };
    00078
    00079 static struct loadupdate {
    00080    int (*updater)(void);
    00081    struct loadupdate *next;
    00082 } *updaters = NULL;
    00083
    00084 static int printdigest(unsigned char *d)
    00085 {
    00086    int x;
    00087    char buf[256];
    00088    char buf2[16];
    00089    snprintf(buf, sizeof(buf), "Unexpected signature:");
    00090    for (x=0; x<16; x++) {
    00091       snprintf(buf2, sizeof(buf2), " %02x", *(d++));
    00092       strcat(buf, buf2);
    00093    }
    00094    strcat(buf, "\n");
    00095    ast_log(LOG_DEBUG, "%s", buf);
    00096    return 0;
    00097 }
    00098
    00099 static int key_matches(unsigned char *key1, unsigned char *key2)
    00100 {
    00101    int match = 1;
    00102    int x;
    00103    for (x=0; x<16; x++) {
    00104       match &= (key1[x] == key2[x]);
    00105    }
    00106    return match;
    00107 }
    00108
    00109 static int verify_key(unsigned char *key)
    00110 {
    00111    struct MD5Context c;
    00112    unsigned char digest[16];
    00113    MD5Init(&c);
    00114    MD5Update(&c, key, strlen((char *)key));
    00115    MD5Final(digest, &c);
    00116    if (key_matches(expected_key, digest))
    00117       return 0;
    00118    printdigest(digest);
    00119    return -1;
    00120 }
    00121
    00122 int ast_unload_resource(const char *resource_name, int force)
    00123 {
    00124    struct module *m, *ml = NULL;
    00125    int res = -1;
    00126    if (ast_mutex_lock(&modlock))
    00127       ast_log(LOG_WARNING, "Failed to lock\n");
    00128    m = module_list;
    00129    while(m) {
    00130       if (!strcasecmp(m->resource, resource_name)) {
    00131          if ((res = m->usecount()) > 0)  {
    00132             if (force)
    00133                ast_log(LOG_WARNING, "Warning:  Forcing removal of module %s with use count %d\n", resource_name, res);
    00134             else {
    00135                ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name, res);
    00136                ast_mutex_unlock(&modlock);
    00137                return -1;
    00138             }
    00139          }
    00140          res = m->unload_module();
    00141          if (res) {
    00142             ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
    00143             if (force <= AST_FORCE_FIRM) {
    00144                ast_mutex_unlock(&modlock);
    00145                return -1;
    00146             } else
    00147                ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
    00148          }
    00149          if (ml)
    00150             ml->next = m->next;
    00151          else
    00152             module_list = m->next;
    00153          dlclose(m->lib);
    00154          free(m);
    00155          break;
    00156       }
    00157       ml = m;
    00158       m = m->next;
    00159    }
    00160    modlistver = rand();
    00161    ast_mutex_unlock(&modlock);
    00162    ast_update_use_count();
    00163    return res;
    00164 }
    00165
    00166 char *ast_module_helper(char *line, char *word, int pos, int state, int rpos, int needsreload)
    00167 {
    00168    struct module *m;
    00169    int which=0;
    00170    char *ret;
    00171
    00172    if (pos != rpos)
    00173       return NULL;
    00174    ast_mutex_lock(&modlock);
    00175    m = module_list;
    00176    while(m) {
    00177       if (!strncasecmp(word, m->resource, strlen(word)) && (m->reload || !needsreload)) {
    00178          if (++which > state)
    00179             break;
    00180       }
    00181       m = m->next;
    00182    }
    00183    if (m) {
    00184       ret = strdup(m->resource);
    00185    } else {
    00186       ret = NULL;
    00187       if (!strncasecmp(word, "extconfig", strlen(word))) {
    00188          if (++which > state)
    00189             ret = strdup("extconfig");
    00190       } else if (!strncasecmp(word, "manager", strlen(word))) {
    00191          if (++which > state)
    00192             ret = strdup("manager");
    00193       } else if (!strncasecmp(word, "enum", strlen(word))) {
    00194          if (++which > state)
    00195             ret = strdup("enum");
    00196       } else if (!strncasecmp(word, "rtp", strlen(word))) {
    00197          if (++which > state)
    00198             ret = strdup("rtp");
    00199       }
    00200
    00201    }
    00202    ast_mutex_unlock(&modlock);
    00203    return ret;
    00204 }
    00205
    00206 int ast_module_reload(const char *name)
    00207 {
    00208    struct module *m;
    00209    int reloaded = 0;
    00210    int oldversion;
    00211    int (*reload)(void);
    00212    /* We'll do the logger and manager the favor of calling its reload here first */
    00213
    00214    if (ast_mutex_trylock(&reloadlock)) {
    00215       ast_verbose("The previous reload command didn't finish yet\n");
    00216       return -1;
    00217    }
    00218    time(&ast_lastreloadtime);
    00219
    00220    if (!name || !strcasecmp(name, "extconfig")) {
    00221       read_config_maps();
    00222       reloaded = 2;
    00223    }
    00224    if (!name || !strcasecmp(name, "manager")) {
    00225       reload_manager();
    00226       reloaded = 2;
    00227    }
    00228    if (!name || !strcasecmp(name, "cdr")) {
    00229       ast_cdr_engine_reload();
    00230       reloaded = 2;
    00231    }
    00232    if (!name || !strcasecmp(name, "enum")) {
    00233       ast_enum_reload();
    00234       reloaded = 2;
    00235    }
    00236    if (!name || !strcasecmp(name, "rtp")) {
    00237       ast_rtp_reload();
    00238       reloaded = 2;
    00239    }
    00240    if (!name || !strcasecmp(name, "dnsmgr")) {
    00241       dnsmgr_reload();
    00242       reloaded = 2;
    00243    }
    00244
    00245    ast_mutex_lock(&modlock);
    00246    oldversion = modlistver;
    00247    m = module_list;
    00248    while(m) {
    00249       if (!name || !strcasecmp(name, m->resource)) {
    00250          if (reloaded < 1)
    00251             reloaded = 1;
    00252          reload = m->reload;
    00253          ast_mutex_unlock(&modlock);
    00254          if (reload) {
    00255             reloaded = 2;
    00256             if (option_verbose > 2)
    00257                ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", m->resource, m->description());
    00258             reload();
    00259          }
    00260          ast_mutex_lock(&modlock);
    00261          if (oldversion != modlistver)
    00262             break;
    00263       }
    00264       m = m->next;
    00265    }
    00266    ast_mutex_unlock(&modlock);
    00267    ast_mutex_unlock(&reloadlock);
    00268    return reloaded;
    00269 }
    00270
    00271 static int __load_resource(const char *resource_name, const struct ast_config *cfg)
    00272 {
    00273    static char fn[256];
    00274    int errors=0;
    00275    int res;
    00276    struct module *m;
    00277    int flags=RTLD_NOW;
    00278 #ifdef RTLD_GLOBAL
    00279    char *val;
    00280 #endif
    00281    unsigned char *key;
    00282    char tmp[80];
    00283
    00284    if (strncasecmp(resource_name, "res_", 4)) {
    00285 #ifdef RTLD_GLOBAL
    00286       if (cfg) {
    00287          if ((val = ast_variable_retrieve(cfg, "global", resource_name))
    00288                && ast_true(val))
    00289             flags |= RTLD_GLOBAL;
    00290       }
    00291 #endif
    00292    } else {
    00293       /* Resource modules are always loaded global and lazy */
    00294 #ifdef RTLD_GLOBAL
    00295       flags = (RTLD_GLOBAL | RTLD_LAZY);
    00296 #else
    00297       flags = RTLD_LAZY;
    00298 #endif
    00299    }
    00300
    00301    if (ast_mutex_lock(&modlock))
    00302       ast_log(LOG_WARNING, "Failed to lock\n");
    00303    m = module_list;
    00304    while(m) {
    00305       if (!strcasecmp(m->resource, resource_name)) {
    00306          ast_log(LOG_WARNING, "Module '%s' already exists\n", resource_name);
    00307          ast_mutex_unlock(&modlock);
    00308          return -1;
    00309       }
    00310       m = m->next;
    00311    }
    00312    m = malloc(sizeof(struct module));
    00313    if (!m) {
    00314       ast_log(LOG_WARNING, "Out of memory\n");
    00315       ast_mutex_unlock(&modlock);
    00316       return -1;
    00317    }
    00318    strncpy(m->resource, resource_name, sizeof(m->resource)-1);
    00319    if (resource_name[0] == '/') {
    00320       strncpy(fn, resource_name, sizeof(fn)-1);
    00321    } else {
    00322       snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_MODULE_DIR, resource_name);
    00323    }
    00324    m->lib = dlopen(fn, flags);
    00325    if (!m->lib) {
    00326       ast_log(LOG_WARNING, "%s\n", dlerror());
    00327       free(m);
    00328       ast_mutex_unlock(&modlock);
    00329       return -1;
    00330    }
    00331    m->load_module = dlsym(m->lib, "load_module");
    00332    if (m->load_module == NULL)
    00333       m->load_module = dlsym(m->lib, "_load_module");
    00334    if (!m->load_module) {
    00335       ast_log(LOG_WARNING, "No load_module in module %s\n", fn);
    00336       errors++;
    00337    }
    00338    m->unload_module = dlsym(m->lib, "unload_module");
    00339    if (m->unload_module == NULL)
    00340       m->unload_module = dlsym(m->lib, "_unload_module");
    00341    if (!m->unload_module) {
    00342       ast_log(LOG_WARNING, "No unload_module in module %s\n", fn);
    00343       errors++;
    00344    }
    00345    m->usecount = dlsym(m->lib, "usecount");
    00346    if (m->usecount == NULL)
    00347       m->usecount = dlsym(m->lib, "_usecount");
    00348    if (!m->usecount) {
    00349       ast_log(LOG_WARNING, "No usecount in module %s\n", fn);
    00350       errors++;
    00351    }
    00352    m->description = dlsym(m->lib, "description");
    00353    if (m->description == NULL)
    00354       m->description = dlsym(m->lib, "_description");
    00355    if (!m->description) {
    00356       ast_log(LOG_WARNING, "No description in module %s\n", fn);
    00357       errors++;
    00358    }
    00359    m->key = dlsym(m->lib, "key");
    00360    if (m->key == NULL)
    00361       m->key = dlsym(m->lib, "_key");
    00362    if (!m->key) {
    00363       ast_log(LOG_WARNING, "No key routine in module %s\n", fn);
    00364       errors++;
    00365    }
    00366
    00367    m->reload = dlsym(m->lib, "reload");
    00368    if (m->reload == NULL)
    00369       m->reload = dlsym(m->lib, "_reload");
    00370
    00371    if (!m->key || !(key = (unsigned char *) m->key())) {
    00372       ast_log(LOG_WARNING, "Key routine returned NULL in module %s\n", fn);
    00373       key = NULL;
    00374       errors++;
    00375    }
    00376    if (key && verify_key(key)) {
    00377       ast_log(LOG_WARNING, "Unexpected key returned by module %s\n", fn);
    00378       errors++;
    00379    }
    00380    if (errors) {
    00381       ast_log(LOG_WARNING, "%d error%s loading module %s, aborted\n", errors, (errors != 1) ? "s" : "", fn);
    00382       dlclose(m->lib);
    00383       free(m);
    00384       ast_mutex_unlock(&modlock);
    00385       return -1;
    00386    }
    00387    if (!fully_booted) {
    00388       if (option_verbose)
    00389          ast_verbose( " => (%s)\n", term_color(tmp, m->description(), COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
    00390       if (option_console && !option_verbose)
    00391          ast_verbose( ".");
    00392    } else {
    00393       if (option_verbose)
    00394          ast_verbose(VERBOSE_PREFIX_1 "Loaded %s => (%s)\n", fn, m->description());
    00395    }
    00396
    00397    /* add module 'm' to end of module_list chain
    00398       so reload commands will be issued in same order modules were loaded */
    00399    m->next = NULL;
    00400    if (module_list == NULL) {
    00401       /* empty list so far, add at front */
    00402       module_list = m;
    00403    }
    00404    else {
    00405       struct module *i;
    00406       /* find end of chain, and add there */
    00407       for (i = module_list; i->next; i = i->next)
    00408          ;
    00409       i->next = m;
    00410    }
    00411
    00412    modlistver = rand();
    00413    ast_mutex_unlock(&modlock);
    00414    if ((res = m->load_module())) {
    00415       ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", m->resource, res);
    00416       ast_unload_resource(resource_name, 0);
    00417       return -1;
    00418    }
    00419    ast_update_use_count();
    00420    return 0;
    00421 }
    00422
    00423 int ast_load_resource(const char *resource_name)
    00424 {
    00425    int o;
    00426    struct ast_config *cfg = NULL;
    00427    int res;
    00428
    00429    /* Keep the module file parsing silent */
    00430    o = option_verbose;
    00431    option_verbose = 0;
    00432    cfg = ast_config_load(AST_MODULE_CONFIG);
    00433    option_verbose = o;
    00434    res = __load_resource(resource_name, cfg);
    00435    if (cfg)
    00436       ast_config_destroy(cfg);
    00437    return res;
    00438 }
    00439
    00440 static int ast_resource_exists(char *resource)
    00441 {
    00442    struct module *m;
    00443    if (ast_mutex_lock(&modlock))
    00444       ast_log(LOG_WARNING, "Failed to lock\n");
    00445    m = module_list;
    00446    while(m) {
    00447       if (!strcasecmp(resource, m->resource))
    00448          break;
    00449       m = m->next;
    00450    }
    00451    ast_mutex_unlock(&modlock);
    00452    if (m)
    00453       return -1;
    00454    else
    00455       return 0;
    00456 }
    00457
    00458 static const char *loadorder[] =
    00459 {
    00460    "res_",
    00461    "pbx_",
    00462    "chan_",
    00463    NULL,
    00464 };
    00465
    00466 int load_modules(const int preload_only)
    00467 {
    00468    struct ast_config *cfg;
    00469    struct ast_variable *v;
    00470    char tmp[80];
    00471
    00472    if (option_verbose) {
    00473       if (preload_only)
    00474          ast_verbose("Asterisk Dynamic Loader loading preload modules:\n");
    00475       else
    00476          ast_verbose("Asterisk Dynamic Loader Starting:\n");
    00477    }
    00478
    00479    cfg = ast_config_load(AST_MODULE_CONFIG);
    00480    if (cfg) {
    00481       int doload;
    00482
    00483       /* Load explicitly defined modules */
    00484       for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
    00485          doload = 0;
    00486
    00487          if (preload_only)
    00488             doload = !strcasecmp(v->name, "preload");
    00489          else
    00490             doload = !strcasecmp(v->name, "load");
    00491
    00492              if (doload) {
    00493             if (option_debug && !option_verbose)
    00494                ast_log(LOG_DEBUG, "Loading module %s\n", v->value);
    00495             if (option_verbose) {
    00496                ast_verbose(VERBOSE_PREFIX_1 "[%s]", term_color(tmp, v->value, COLOR_BRWHITE, 0, sizeof(tmp)));
    00497                fflush(stdout);
    00498             }
    00499             if (__load_resource(v->value, cfg)) {
    00500                ast_log(LOG_WARNING, "Loading module %s failed!\n", v->value);
    00501                ast_config_destroy(cfg);
    00502                return -1;
    00503             }
    00504          }
    00505       }
    00506    }
    00507
    00508    if (preload_only) {
    00509       ast_config_destroy(cfg);
    00510       return 0;
    00511    }
    00512
    00513    if (!cfg || ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
    00514       /* Load all modules */
    00515       DIR *mods;
    00516       struct dirent *d;
    00517       int x;
    00518
    00519       /* Loop through each order */
    00520       for (x=0; x<sizeof(loadorder) / sizeof(loadorder[0]); x++) {
    00521          mods = opendir((char *)ast_config_AST_MODULE_DIR);
    00522          if (mods) {
    00523             while((d = readdir(mods))) {
    00524                /* Must end in .so to load it.  */
    00525                if ((strlen(d->d_name) > 3) &&
    00526                    (!loadorder[x] || !strncasecmp(d->d_name, loadorder[x], strlen(loadorder[x]))) &&
    00527                    !strcasecmp(d->d_name + strlen(d->d_name) - 3, ".so") &&
    00528                   !ast_resource_exists(d->d_name)) {
    00529                   /* It's a shared library -- Just be sure we're allowed to load it -- kinda
    00530                      an inefficient way to do it, but oh well. */
    00531                   if (cfg) {
    00532                      v = ast_variable_browse(cfg, "modules");
    00533                      while(v) {
    00534                         if (!strcasecmp(v->name, "noload") &&
    00535                             !strcasecmp(v->value, d->d_name))
    00536                            break;
    00537                         v = v->next;
    00538                      }
    00539                      if (v) {
    00540                         if (option_verbose) {
    00541                            ast_verbose( VERBOSE_PREFIX_1 "[skipping %s]\n", d->d_name);
    00542                            fflush(stdout);
    00543                         }
    00544                         continue;
    00545                      }
    00546
    00547                   }
    00548                   if (option_debug && !option_verbose)
    00549                      ast_log(LOG_DEBUG, "Loading module %s\n", d->d_name);
    00550                   if (option_verbose) {
    00551                      ast_verbose( VERBOSE_PREFIX_1 "[%s]", term_color(tmp, d->d_name, COLOR_BRWHITE, 0, sizeof(tmp)));
    00552                      fflush(stdout);
    00553                   }
    00554                   if (__load_resource(d->d_name, cfg)) {
    00555                      ast_log(LOG_WARNING, "Loading module %s failed!\n", d->d_name);
    00556                      if (cfg)
    00557                         ast_config_destroy(cfg);
    00558                      return -1;
    00559                   }
    00560                }
    00561             }
    00562             closedir(mods);
    00563          } else {
    00564             if (!option_quiet)
    00565                ast_log(LOG_WARNING, "Unable to open modules directory %s.\n", (char *)ast_config_AST_MODULE_DIR);
    00566          }
    00567       }
    00568    }
    00569    ast_config_destroy(cfg);
    00570    return 0;
    00571 }
    00572
    00573 void ast_update_use_count(void)
    00574 {
    00575    /* Notify any module monitors that the use count for a
    00576       resource has changed */
    00577    struct loadupdate *m;
    00578    if (ast_mutex_lock(&modlock))
    00579       ast_log(LOG_WARNING, "Failed to lock\n");
    00580    m = updaters;
    00581    while(m) {
    00582       m->updater();
    00583       m = m->next;
    00584    }
    00585    ast_mutex_unlock(&modlock);
    00586
    00587 }
    00588
    00589 int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),
    00590             const char *like)
    00591 {
    00592    struct module *m;
    00593    int unlock = -1;
    00594    int total_mod_loaded = 0;
    00595
    00596    if (ast_mutex_trylock(&modlock))
    00597       unlock = 0;
    00598    m = module_list;
    00599    while (m) {
    00600       total_mod_loaded += modentry(m->resource, m->description(), m->usecount(), like);
    00601       m = m->next;
    00602    }
    00603    if (unlock)
    00604       ast_mutex_unlock(&modlock);
    00605
    00606    return total_mod_loaded;
    00607 }
    00608
    00609 int ast_loader_register(int (*v)(void))
    00610 {
    00611    struct loadupdate *tmp;
    00612    /* XXX Should be more flexible here, taking > 1 verboser XXX */
    00613    if ((tmp = malloc(sizeof (struct loadupdate)))) {
    00614       tmp->updater = v;
    00615       if (ast_mutex_lock(&modlock))
    00616          ast_log(LOG_WARNING, "Failed to lock\n");
    00617       tmp->next = updaters;
    00618       updaters = tmp;
    00619       ast_mutex_unlock(&modlock);
    00620       return 0;
    00621    }
    00622    return -1;
    00623 }
    00624
    00625 int ast_loader_unregister(int (*v)(void))
    00626 {
    00627    int res = -1;
    00628    struct loadupdate *tmp, *tmpl=NULL;
    00629    if (ast_mutex_lock(&modlock))
    00630       ast_log(LOG_WARNING, "Failed to lock\n");
    00631    tmp = updaters;
    00632    while(tmp) {
    00633       if (tmp->updater == v)  {
    00634          if (tmpl)
    00635             tmpl->next = tmp->next;
    00636          else
    00637             updaters = tmp->next;
    00638          break;
    00639       }
    00640       tmpl = tmp;
    00641       tmp = tmp->next;
    00642    }
    00643    if (tmp)
    00644       res = 0;
    00645    ast_mutex_unlock(&modlock);
    00646    return res;
    00647 }