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

Generated on Tue Aug 17 16:13:53 2004 for Asterisk by doxygen 1.3.8