Sat Mar 24 23:26:05 2007

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: 8162 $")
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    if (!name || !strcasecmp(name, "extconfig")) {
00219       read_config_maps();
00220       reloaded = 2;
00221    }
00222    if (!name || !strcasecmp(name, "manager")) {
00223       reload_manager();
00224       reloaded = 2;
00225    }
00226    if (!name || !strcasecmp(name, "cdr")) {
00227       ast_cdr_engine_reload();
00228       reloaded = 2;
00229    }
00230    if (!name || !strcasecmp(name, "enum")) {
00231       ast_enum_reload();
00232       reloaded = 2;
00233    }
00234    if (!name || !strcasecmp(name, "rtp")) {
00235       ast_rtp_reload();
00236       reloaded = 2;
00237    }
00238    if (!name || !strcasecmp(name, "dnsmgr")) {
00239       dnsmgr_reload();
00240       reloaded = 2;
00241    }
00242    time(&ast_lastreloadtime);
00243 
00244    ast_mutex_lock(&modlock);
00245    oldversion = modlistver;
00246    m = module_list;
00247    while(m) {
00248       if (!name || !strcasecmp(name, m->resource)) {
00249          if (reloaded < 1)
00250             reloaded = 1;
00251          reload = m->reload;
00252          ast_mutex_unlock(&modlock);
00253          if (reload) {
00254             reloaded = 2;
00255             if (option_verbose > 2) 
00256                ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", m->resource, m->description());
00257             reload();
00258          }
00259          ast_mutex_lock(&modlock);
00260          if (oldversion != modlistver)
00261             break;
00262       }
00263       m = m->next;
00264    }
00265    ast_mutex_unlock(&modlock);
00266    ast_mutex_unlock(&reloadlock);
00267    return reloaded;
00268 }
00269 
00270 static int __load_resource(const char *resource_name, const struct ast_config *cfg)
00271 {
00272    static char fn[256];
00273    int errors=0;
00274    int res;
00275    struct module *m;
00276    int flags=RTLD_NOW;
00277 #ifdef RTLD_GLOBAL
00278    char *val;
00279 #endif
00280    unsigned char *key;
00281    char tmp[80];
00282 
00283    if (strncasecmp(resource_name, "res_", 4)) {
00284 #ifdef RTLD_GLOBAL
00285       if (cfg) {
00286          if ((val = ast_variable_retrieve(cfg, "global", resource_name))
00287                && ast_true(val))
00288             flags |= RTLD_GLOBAL;
00289       }
00290 #endif
00291    } else {
00292       /* Resource modules are always loaded global and lazy */
00293 #ifdef RTLD_GLOBAL
00294       flags = (RTLD_GLOBAL | RTLD_LAZY);
00295 #else
00296       flags = RTLD_LAZY;
00297 #endif
00298    }
00299    
00300    if (ast_mutex_lock(&modlock))
00301       ast_log(LOG_WARNING, "Failed to lock\n");
00302    m = module_list;
00303    while(m) {
00304       if (!strcasecmp(m->resource, resource_name)) {
00305          ast_log(LOG_WARNING, "Module '%s' already exists\n", resource_name);
00306          ast_mutex_unlock(&modlock);
00307          return -1;
00308       }
00309       m = m->next;
00310    }
00311    m = malloc(sizeof(struct module));  
00312    if (!m) {
00313       ast_log(LOG_WARNING, "Out of memory\n");
00314       ast_mutex_unlock(&modlock);
00315       return -1;
00316    }
00317    strncpy(m->resource, resource_name, sizeof(m->resource)-1);
00318    if (resource_name[0] == '/') {
00319       strncpy(fn, resource_name, sizeof(fn)-1);
00320    } else {
00321       snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_MODULE_DIR, resource_name);
00322    }
00323    m->lib = dlopen(fn, flags);
00324    if (!m->lib) {
00325       ast_log(LOG_WARNING, "%s\n", dlerror());
00326       free(m);
00327       ast_mutex_unlock(&modlock);
00328       return -1;
00329    }
00330    m->load_module = dlsym(m->lib, "load_module");
00331    if (m->load_module == NULL)
00332       m->load_module = dlsym(m->lib, "_load_module");
00333    if (!m->load_module) {
00334       ast_log(LOG_WARNING, "No load_module in module %s\n", fn);
00335       errors++;
00336    }
00337    m->unload_module = dlsym(m->lib, "unload_module");
00338    if (m->unload_module == NULL)
00339       m->unload_module = dlsym(m->lib, "_unload_module");
00340    if (!m->unload_module) {
00341       ast_log(LOG_WARNING, "No unload_module in module %s\n", fn);
00342       errors++;
00343    }
00344    m->usecount = dlsym(m->lib, "usecount");
00345    if (m->usecount == NULL)
00346       m->usecount = dlsym(m->lib, "_usecount");
00347    if (!m->usecount) {
00348       ast_log(LOG_WARNING, "No usecount in module %s\n", fn);
00349       errors++;
00350    }
00351    m->description = dlsym(m->lib, "description");
00352    if (m->description == NULL)
00353       m->description = dlsym(m->lib, "_description");
00354    if (!m->description) {
00355       ast_log(LOG_WARNING, "No description in module %s\n", fn);
00356       errors++;
00357    }
00358    m->key = dlsym(m->lib, "key");
00359    if (m->key == NULL)
00360       m->key = dlsym(m->lib, "_key");
00361    if (!m->key) {
00362       ast_log(LOG_WARNING, "No key routine in module %s\n", fn);
00363       errors++;
00364    }
00365 
00366    m->reload = dlsym(m->lib, "reload");
00367    if (m->reload == NULL)
00368       m->reload = dlsym(m->lib, "_reload");
00369 
00370    if (!m->key || !(key = (unsigned char *) m->key())) {
00371       ast_log(LOG_WARNING, "Key routine returned NULL in module %s\n", fn);
00372       key = NULL;
00373       errors++;
00374    }
00375    if (key && verify_key(key)) {
00376       ast_log(LOG_WARNING, "Unexpected key returned by module %s\n", fn);
00377       errors++;
00378    }
00379    if (errors) {
00380       ast_log(LOG_WARNING, "%d error%s loading module %s, aborted\n", errors, (errors != 1) ? "s" : "", fn);
00381       dlclose(m->lib);
00382       free(m);
00383       ast_mutex_unlock(&modlock);
00384       return -1;
00385    }
00386    if (!fully_booted) {
00387       if (option_verbose) 
00388          ast_verbose( " => (%s)\n", term_color(tmp, m->description(), COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
00389       if (option_console && !option_verbose)
00390          ast_verbose( ".");
00391    } else {
00392       if (option_verbose)
00393          ast_verbose(VERBOSE_PREFIX_1 "Loaded %s => (%s)\n", fn, m->description());
00394    }
00395 
00396    /* add module 'm' to end of module_list chain
00397       so reload commands will be issued in same order modules were loaded */
00398    m->next = NULL;
00399    if (module_list == NULL) {
00400       /* empty list so far, add at front */
00401       module_list = m;
00402    }
00403    else {
00404       struct module *i;
00405       /* find end of chain, and add there */
00406       for (i = module_list; i->next; i = i->next)
00407          ;
00408       i->next = m;
00409    }
00410    
00411    modlistver = rand();
00412    ast_mutex_unlock(&modlock);
00413    if ((res = m->load_module())) {
00414       ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", m->resource, res);
00415       ast_unload_resource(resource_name, 0);
00416       return -1;
00417    }
00418    ast_update_use_count();
00419    return 0;
00420 }
00421 
00422 int ast_load_resource(const char *resource_name)
00423 {
00424    int o;
00425    struct ast_config *cfg = NULL;
00426    int res;
00427 
00428    /* Keep the module file parsing silent */
00429    o = option_verbose;
00430    option_verbose = 0;
00431    cfg = ast_config_load(AST_MODULE_CONFIG);
00432    option_verbose = o;
00433    res = __load_resource(resource_name, cfg);
00434    if (cfg)
00435       ast_config_destroy(cfg);
00436    return res;
00437 }  
00438 
00439 static int ast_resource_exists(char *resource)
00440 {
00441    struct module *m;
00442    if (ast_mutex_lock(&modlock))
00443       ast_log(LOG_WARNING, "Failed to lock\n");
00444    m = module_list;
00445    while(m) {
00446       if (!strcasecmp(resource, m->resource))
00447          break;
00448       m = m->next;
00449    }
00450    ast_mutex_unlock(&modlock);
00451    if (m)
00452       return -1;
00453    else
00454       return 0;
00455 }
00456 
00457 static const char *loadorder[] =
00458 {
00459    "res_",
00460    "pbx_",
00461    "chan_",
00462    NULL,
00463 };
00464 
00465 int load_modules(const int preload_only)
00466 {
00467    struct ast_config *cfg;
00468    struct ast_variable *v;
00469    char tmp[80];
00470 
00471    if (option_verbose) {
00472       if (preload_only)
00473          ast_verbose("Asterisk Dynamic Loader loading preload modules:\n");
00474       else
00475          ast_verbose("Asterisk Dynamic Loader Starting:\n");
00476    }
00477 
00478    cfg = ast_config_load(AST_MODULE_CONFIG);
00479    if (cfg) {
00480       int doload;
00481 
00482       /* Load explicitly defined modules */
00483       for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
00484          doload = 0;
00485 
00486          if (preload_only)
00487             doload = !strcasecmp(v->name, "preload");
00488          else
00489             doload = !strcasecmp(v->name, "load");
00490 
00491              if (doload) {
00492             if (option_debug && !option_verbose)
00493                ast_log(LOG_DEBUG, "Loading module %s\n", v->value);
00494             if (option_verbose) {
00495                ast_verbose(VERBOSE_PREFIX_1 "[%s]", term_color(tmp, v->value, COLOR_BRWHITE, 0, sizeof(tmp)));
00496                fflush(stdout);
00497             }
00498             if (__load_resource(v->value, cfg)) {
00499                ast_log(LOG_WARNING, "Loading module %s failed!\n", v->value);
00500                ast_config_destroy(cfg);
00501                return -1;
00502             }
00503          }
00504       }
00505    }
00506 
00507    if (preload_only) {
00508       ast_config_destroy(cfg);
00509       return 0;
00510    }
00511 
00512    if (!cfg || ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
00513       /* Load all modules */
00514       DIR *mods;
00515       struct dirent *d;
00516       int x;
00517 
00518       /* Loop through each order */
00519       for (x=0; x<sizeof(loadorder) / sizeof(loadorder[0]); x++) {
00520          mods = opendir((char *)ast_config_AST_MODULE_DIR);
00521          if (mods) {
00522             while((d = readdir(mods))) {
00523                /* Must end in .so to load it.  */
00524                if ((strlen(d->d_name) > 3) && 
00525                    (!loadorder[x] || !strncasecmp(d->d_name, loadorder[x], strlen(loadorder[x]))) && 
00526                    !strcasecmp(d->d_name + strlen(d->d_name) - 3, ".so") &&
00527                   !ast_resource_exists(d->d_name)) {
00528                   /* It's a shared library -- Just be sure we're allowed to load it -- kinda
00529                      an inefficient way to do it, but oh well. */
00530                   if (cfg) {
00531                      v = ast_variable_browse(cfg, "modules");
00532                      while(v) {
00533                         if (!strcasecmp(v->name, "noload") &&
00534                             !strcasecmp(v->value, d->d_name)) 
00535                            break;
00536                         v = v->next;
00537                      }
00538                      if (v) {
00539                         if (option_verbose) {
00540                            ast_verbose( VERBOSE_PREFIX_1 "[skipping %s]\n", d->d_name);
00541                            fflush(stdout);
00542                         }
00543                         continue;
00544                      }
00545                      
00546                   }
00547                   if (option_debug && !option_verbose)
00548                      ast_log(LOG_DEBUG, "Loading module %s\n", d->d_name);
00549                   if (option_verbose) {
00550                      ast_verbose( VERBOSE_PREFIX_1 "[%s]", term_color(tmp, d->d_name, COLOR_BRWHITE, 0, sizeof(tmp)));
00551                      fflush(stdout);
00552                   }
00553                   if (__load_resource(d->d_name, cfg)) {
00554                      ast_log(LOG_WARNING, "Loading module %s failed!\n", d->d_name);
00555                      if (cfg)
00556                         ast_config_destroy(cfg);
00557                      return -1;
00558                   }
00559                }
00560             }
00561             closedir(mods);
00562          } else {
00563             if (!option_quiet)
00564                ast_log(LOG_WARNING, "Unable to open modules directory %s.\n", (char *)ast_config_AST_MODULE_DIR);
00565          }
00566       }
00567    } 
00568    ast_config_destroy(cfg);
00569    return 0;
00570 }
00571 
00572 void ast_update_use_count(void)
00573 {
00574    /* Notify any module monitors that the use count for a 
00575       resource has changed */
00576    struct loadupdate *m;
00577    if (ast_mutex_lock(&modlock))
00578       ast_log(LOG_WARNING, "Failed to lock\n");
00579    m = updaters;
00580    while(m) {
00581       m->updater();
00582       m = m->next;
00583    }
00584    ast_mutex_unlock(&modlock);
00585    
00586 }
00587 
00588 int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),
00589             const char *like)
00590 {
00591    struct module *m;
00592    int unlock = -1;
00593    int total_mod_loaded = 0;
00594 
00595    if (ast_mutex_trylock(&modlock))
00596       unlock = 0;
00597    m = module_list;
00598    while (m) {
00599       total_mod_loaded += modentry(m->resource, m->description(), m->usecount(), like);
00600       m = m->next;
00601    }
00602    if (unlock)
00603       ast_mutex_unlock(&modlock);
00604 
00605    return total_mod_loaded;
00606 }
00607 
00608 int ast_loader_register(int (*v)(void)) 
00609 {
00610    struct loadupdate *tmp;
00611    /* XXX Should be more flexible here, taking > 1 verboser XXX */
00612    if ((tmp = malloc(sizeof (struct loadupdate)))) {
00613       tmp->updater = v;
00614       if (ast_mutex_lock(&modlock))
00615          ast_log(LOG_WARNING, "Failed to lock\n");
00616       tmp->next = updaters;
00617       updaters = tmp;
00618       ast_mutex_unlock(&modlock);
00619       return 0;
00620    }
00621    return -1;
00622 }
00623 
00624 int ast_loader_unregister(int (*v)(void))
00625 {
00626    int res = -1;
00627    struct loadupdate *tmp, *tmpl=NULL;
00628    if (ast_mutex_lock(&modlock))
00629       ast_log(LOG_WARNING, "Failed to lock\n");
00630    tmp = updaters;
00631    while(tmp) {
00632       if (tmp->updater == v)  {
00633          if (tmpl)
00634             tmpl->next = tmp->next;
00635          else
00636             updaters = tmp->next;
00637          break;
00638       }
00639       tmpl = tmp;
00640       tmp = tmp->next;
00641    }
00642    if (tmp)
00643       res = 0;
00644    ast_mutex_unlock(&modlock);
00645    return res;
00646 }

Generated on Sat Mar 24 23:26:05 2007 for Asterisk - the Open Source PBX by  doxygen 1.4.6