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

Generated on Sun Apr 18 23:33:52 2004 for Asterisk by doxygen 1.3.6-20040222