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

Generated on Fri Oct 31 07:05:06 2003 for Asterisk by doxygen 1.3.4