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 Fri Sep 24 21:03:47 2004 for Asterisk by doxygen 1.3.8