Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals  

loader.c

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

Generated on Thu Oct 28 11:32:54 2004 for Asterisk by doxygen1.2.15