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       }
00140       ml = m;
00141       m = m->next;
00142    }
00143    ast_mutex_unlock(&modlock);
00144    ast_update_use_count();
00145    return res;
00146 }
00147 
00148 void ast_module_reload(void)
00149 {
00150    struct module *m;
00151 
00152    /* We'll do the logger and manager the favor of calling its reload here first */
00153 
00154    if (ast_mutex_trylock(&reloadlock)) {
00155       ast_verbose("The previous reload command didn't finish yet\n");
00156       return;
00157    }
00158    reload_manager();
00159    ast_enum_reload();
00160    ast_rtp_reload();
00161    time(&ast_lastreloadtime);
00162 
00163    ast_mutex_lock(&modlock);
00164    m = module_list;
00165    while(m) {
00166       if (m->reload) {
00167          if (option_verbose > 2) 
00168             ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", m->resource, m->description());
00169          m->reload();
00170       }
00171       m = m->next;
00172    }
00173    ast_mutex_unlock(&modlock);
00174    ast_mutex_unlock(&reloadlock);
00175 }
00176 
00177 int ast_load_resource(char *resource_name)
00178 {
00179    static char fn[256];
00180    int errors=0;
00181    int res;
00182    struct module *m;
00183    int flags=RTLD_NOW;
00184 #ifdef RTLD_GLOBAL
00185    char *val;
00186 #endif
00187    char *key;
00188    int o;
00189    struct ast_config *cfg;
00190    char tmp[80];
00191    /* Keep the module file parsing silent */
00192    o = option_verbose;
00193    if (strncasecmp(resource_name, "res_", 4)) {
00194       option_verbose = 0;
00195       cfg = ast_load(AST_MODULE_CONFIG);
00196       option_verbose = o;
00197       if (cfg) {
00198 #ifdef RTLD_GLOBAL
00199          if ((val = ast_variable_retrieve(cfg, "global", resource_name))
00200                && ast_true(val))
00201             flags |= RTLD_GLOBAL;
00202 #endif
00203          ast_destroy(cfg);
00204       }
00205    } else {
00206       /* Resource modules are always loaded global and lazy */
00207 #ifdef RTLD_GLOBAL
00208       flags = (RTLD_GLOBAL | RTLD_LAZY);
00209 #else
00210       flags = RTLD_LAZY;
00211 #endif
00212    }
00213    
00214    if (ast_mutex_lock(&modlock))
00215       ast_log(LOG_WARNING, "Failed to lock\n");
00216    m = module_list;
00217    while(m) {
00218       if (!strcasecmp(m->resource, resource_name)) {
00219          ast_log(LOG_WARNING, "Module '%s' already exists\n", resource_name);
00220          ast_mutex_unlock(&modlock);
00221          return -1;
00222       }
00223       m = m->next;
00224    }
00225    m = malloc(sizeof(struct module));  
00226    if (!m) {
00227       ast_log(LOG_WARNING, "Out of memory\n");
00228       ast_mutex_unlock(&modlock);
00229       return -1;
00230    }
00231    strncpy(m->resource, resource_name, sizeof(m->resource)-1);
00232    if (resource_name[0] == '/') {
00233       strncpy(fn, resource_name, sizeof(fn)-1);
00234    } else {
00235       snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_MODULE_DIR, resource_name);
00236    }
00237    m->lib = dlopen(fn, flags);
00238    if (!m->lib) {
00239       ast_log(LOG_WARNING, "%s\n", dlerror());
00240       free(m);
00241       ast_mutex_unlock(&modlock);
00242       return -1;
00243    }
00244    m->load_module = dlsym(m->lib, "load_module");
00245    if (m->load_module == NULL)
00246       m->load_module = dlsym(m->lib, "_load_module");
00247    if (!m->load_module) {
00248       ast_log(LOG_WARNING, "No load_module in module %s\n", fn);
00249       errors++;
00250    }
00251    m->unload_module = dlsym(m->lib, "unload_module");
00252    if (m->unload_module == NULL)
00253       m->unload_module = dlsym(m->lib, "_unload_module");
00254    if (!m->unload_module) {
00255       ast_log(LOG_WARNING, "No unload_module in module %s\n", fn);
00256       errors++;
00257    }
00258    m->usecount = dlsym(m->lib, "usecount");
00259    if (m->usecount == NULL)
00260       m->usecount = dlsym(m->lib, "_usecount");
00261    if (!m->usecount) {
00262       ast_log(LOG_WARNING, "No usecount in module %s\n", fn);
00263       errors++;
00264    }
00265    m->description = dlsym(m->lib, "description");
00266    if (m->description == NULL)
00267       m->description = dlsym(m->lib, "_description");
00268    if (!m->description) {
00269       ast_log(LOG_WARNING, "No description in module %s\n", fn);
00270       errors++;
00271    }
00272    m->key = dlsym(m->lib, "key");
00273    if (m->key == NULL)
00274       m->key = dlsym(m->lib, "_key");
00275    if (!m->key) {
00276       ast_log(LOG_WARNING, "No key routine in module %s\n", fn);
00277       errors++;
00278    }
00279    m->reload = dlsym(m->lib, "reload");
00280    if (m->reload == NULL)
00281       m->reload = dlsym(m->lib, "_reload");
00282    if (m->key && !(key = m->key())) {
00283       ast_log(LOG_WARNING, "Key routine returned NULL in module %s\n", fn);
00284       errors++;
00285    } else
00286       key = NULL;
00287    if (key && verify_key(key)) {
00288       ast_log(LOG_WARNING, "Unexpected key returned by module %s\n", fn);
00289       errors++;
00290    }
00291    if (errors) {
00292       ast_log(LOG_WARNING, "%d error(s) loading module %s, aborted\n", errors, fn);
00293       dlclose(m->lib);
00294       free(m);
00295       ast_mutex_unlock(&modlock);
00296       return -1;
00297    }
00298    if (!fully_booted) {
00299       if (option_verbose) 
00300          ast_verbose( " => (%s)\n", term_color(tmp, m->description(), COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
00301       if (option_console && !option_verbose)
00302          ast_verbose( ".");
00303    } else {
00304       if (option_verbose)
00305          ast_verbose(VERBOSE_PREFIX_1 "Loaded %s => (%s)\n", fn, m->description());
00306    }
00307    m->next = module_list;
00308    
00309    module_list = m;
00310    ast_mutex_unlock(&modlock);
00311    if ((res = m->load_module())) {
00312       ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", m->resource, res);
00313       ast_unload_resource(resource_name, 0);
00314       return -1;
00315    }
00316    ast_update_use_count();
00317    return 0;
00318 }  
00319 
00320 static int ast_resource_exists(char *resource)
00321 {
00322    struct module *m;
00323    if (ast_mutex_lock(&modlock))
00324       ast_log(LOG_WARNING, "Failed to lock\n");
00325    m = module_list;
00326    while(m) {
00327       if (!strcasecmp(resource, m->resource))
00328          break;
00329       m = m->next;
00330    }
00331    ast_mutex_unlock(&modlock);
00332    if (m)
00333       return -1;
00334    else
00335       return 0;
00336 }
00337 
00338 int load_modules()
00339 {
00340    struct ast_config *cfg;
00341    struct ast_variable *v;
00342    char tmp[80];
00343    if (option_verbose) 
00344       ast_verbose( "Asterisk Dynamic Loader Starting:\n");
00345    cfg = ast_load(AST_MODULE_CONFIG);
00346    if (cfg) {
00347       /* Load explicitly defined modules */
00348       v = ast_variable_browse(cfg, "modules");
00349       while(v) {
00350          if (!strcasecmp(v->name, "load")) {
00351             if (option_debug && !option_verbose)
00352                ast_log(LOG_DEBUG, "Loading module %s\n", v->value);
00353             if (option_verbose) {
00354                ast_verbose( VERBOSE_PREFIX_1 "[%s]", term_color(tmp, v->value, COLOR_BRWHITE, 0, sizeof(tmp)));
00355                fflush(stdout);
00356             }
00357             if (ast_load_resource(v->value)) {
00358                ast_log(LOG_WARNING, "Loading module %s failed!\n", v->value);
00359                if (cfg)
00360                   ast_destroy(cfg);
00361                return -1;
00362             }
00363          }
00364          v=v->next;
00365       }
00366    }
00367    if (!cfg || ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
00368       /* Load all modules */
00369       DIR *mods;
00370       struct dirent *d;
00371       int x;
00372       /* Make two passes.  First, load any resource modules, then load the others. */
00373       for (x=0;x<2;x++) {
00374          mods = opendir((char *)ast_config_AST_MODULE_DIR);
00375          if (mods) {
00376             while((d = readdir(mods))) {
00377                /* Must end in .so to load it.  */
00378                if ((strlen(d->d_name) > 3) && (x || !strncasecmp(d->d_name, "res_", 4)) && 
00379                    !strcasecmp(d->d_name + strlen(d->d_name) - 3, ".so") &&
00380                   !ast_resource_exists(d->d_name)) {
00381                   /* It's a shared library -- Just be sure we're allowed to load it -- kinda
00382                      an inefficient way to do it, but oh well. */
00383                   if (cfg) {
00384                      v = ast_variable_browse(cfg, "modules");
00385                      while(v) {
00386                         if (!strcasecmp(v->name, "noload") &&
00387                             !strcasecmp(v->value, d->d_name)) 
00388                            break;
00389                         v = v->next;
00390                      }
00391                      if (v) {
00392                         if (option_verbose) {
00393                            ast_verbose( VERBOSE_PREFIX_1 "[skipping %s]\n", d->d_name);
00394                            fflush(stdout);
00395                         }
00396                         continue;
00397                      }
00398                      
00399                   }
00400                    if (option_debug && !option_verbose)
00401                      ast_log(LOG_DEBUG, "Loading module %s\n", d->d_name);
00402                   if (option_verbose) {
00403                      ast_verbose( VERBOSE_PREFIX_1 "[%s]", term_color(tmp, d->d_name, COLOR_BRWHITE, 0, sizeof(tmp)));
00404                      fflush(stdout);
00405                   }
00406                   if (ast_load_resource(d->d_name)) {
00407                      ast_log(LOG_WARNING, "Loading module %s failed!\n", d->d_name);
00408                      if (cfg)
00409                         ast_destroy(cfg);
00410                      return -1;
00411                   }
00412                }
00413             }
00414             closedir(mods);
00415          } else {
00416             if (!option_quiet)
00417                ast_log(LOG_WARNING, "Unable to open modules directory %s.\n", (char *)ast_config_AST_MODULE_DIR);
00418          }
00419       }
00420    } 
00421    ast_destroy(cfg);
00422    return 0;
00423 }
00424 
00425 void ast_update_use_count(void)
00426 {
00427    /* Notify any module monitors that the use count for a 
00428       resource has changed */
00429    struct loadupdate *m;
00430    if (ast_mutex_lock(&modlock))
00431       ast_log(LOG_WARNING, "Failed to lock\n");
00432    m = updaters;
00433    while(m) {
00434       m->updater();
00435       m = m->next;
00436    }
00437    ast_mutex_unlock(&modlock);
00438    
00439 }
00440 
00441 int ast_update_module_list(int (*modentry)(char *module, char *description, int usecnt))
00442 {
00443    struct module *m;
00444    int unlock = -1;
00445    if (ast_mutex_trylock(&modlock))
00446       unlock = 0;
00447    m = module_list;
00448    while(m) {
00449       modentry(m->resource, m->description(), m->usecount());
00450       m = m->next;
00451    }
00452    if (unlock)
00453       ast_mutex_unlock(&modlock);
00454    return 0;
00455 }
00456 
00457 int ast_loader_register(int (*v)(void)) 
00458 {
00459    struct loadupdate *tmp;
00460    /* XXX Should be more flexible here, taking > 1 verboser XXX */
00461    if ((tmp = malloc(sizeof (struct loadupdate)))) {
00462       tmp->updater = v;
00463       if (ast_mutex_lock(&modlock))
00464          ast_log(LOG_WARNING, "Failed to lock\n");
00465       tmp->next = updaters;
00466       updaters = tmp;
00467       ast_mutex_unlock(&modlock);
00468       return 0;
00469    }
00470    return -1;
00471 }
00472 
00473 int ast_loader_unregister(int (*v)(void))
00474 {
00475    int res = -1;
00476    struct loadupdate *tmp, *tmpl=NULL;
00477    if (ast_mutex_lock(&modlock))
00478       ast_log(LOG_WARNING, "Failed to lock\n");
00479    tmp = updaters;
00480    while(tmp) {
00481       if (tmp->updater == v)  {
00482          if (tmpl)
00483             tmpl->next = tmp->next;
00484          else
00485             updaters = tmp->next;
00486          break;
00487       }
00488       tmpl = tmp;
00489       tmp = tmp->next;
00490    }
00491    if (tmp)
00492       res = 0;
00493    ast_mutex_unlock(&modlock);
00494    return res;
00495 }

Generated on Fri Feb 27 12:19:43 2004 for Asterisk by doxygen 1.3.5