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

Generated on Sat Jun 12 16:40:58 2004 for Asterisk by doxygen 1.3.7