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

Generated on Thu Nov 29 22:50:23 2007 for Asterisk by  doxygen 1.4.2