Thu Oct 9 06:44:55 2008

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 - 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 }

Generated on Thu Oct 9 06:44:55 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1