loader.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * Module Loader
  5. *
  6. * Copyright (C) 1999-2004, Digium, Inc.
  7. *
  8. * Mark Spencer <markster@digium.com>
  9. *
  10. * This program is free software, distributed under the terms of
  11. * the GNU General Public License
  12. */
  13. #include <stdio.h>
  14. #include <dirent.h>
  15. #include <unistd.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <asterisk/module.h>
  19. #include <asterisk/options.h>
  20. #include <asterisk/config.h>
  21. #include <asterisk/config_pvt.h>
  22. #include <asterisk/logger.h>
  23. #include <asterisk/channel.h>
  24. #include <asterisk/term.h>
  25. #include <asterisk/manager.h>
  26. #include <asterisk/enum.h>
  27. #include <asterisk/rtp.h>
  28. #include <asterisk/lock.h>
  29. #ifdef DLFCNCOMPAT
  30. #include <asterisk/dlfcn-compat.h>
  31. #else
  32. #include <dlfcn.h>
  33. #endif
  34. #include <asterisk/md5.h>
  35. #include "asterisk.h"
  36. #include "astconf.h"
  37. #ifndef RTLD_NOW
  38. #define RTLD_NOW 0
  39. #endif
  40. static char expected_key[] =
  41. { 0x8e, 0x93, 0x22, 0x83, 0xf5, 0xc3, 0xc0, 0x75,
  42. 0xff, 0x8b, 0xa9, 0xbe, 0x7c, 0x43, 0x74, 0x63 };
  43. struct module {
  44. int (*load_module)(void);
  45. int (*unload_module)(void);
  46. int (*usecount)(void);
  47. char *(*description)(void);
  48. char *(*key)(void);
  49. int (*reload)(void);
  50. void *lib;
  51. char resource[256];
  52. struct module *next;
  53. };
  54. static int printdigest(unsigned char *d)
  55. {
  56. int x;
  57. char buf[256];
  58. char buf2[16];
  59. snprintf(buf, sizeof(buf), "Unexpected signature:");
  60. for (x=0;x<16;x++) {
  61. snprintf(buf2, sizeof(buf2), " %02x", *(d++));
  62. strcat(buf, buf2);
  63. }
  64. strcat(buf, "\n");
  65. ast_log(LOG_DEBUG, "%s", buf);
  66. return 0;
  67. }
  68. static int key_matches(char *key1, char *key2)
  69. {
  70. int match = 1;
  71. int x;
  72. for (x=0;x<16;x++) {
  73. match &= (key1[x] == key2[x]);
  74. }
  75. return match;
  76. }
  77. static int verify_key(char *key)
  78. {
  79. struct MD5Context c;
  80. char digest[16];
  81. MD5Init(&c);
  82. MD5Update(&c, key, strlen(key));
  83. MD5Final(digest, &c);
  84. if (key_matches(expected_key, digest))
  85. return 0;
  86. printdigest(digest);
  87. return -1;
  88. }
  89. static struct loadupdate {
  90. int (*updater)(void);
  91. struct loadupdate *next;
  92. } *updaters = NULL;
  93. AST_MUTEX_DEFINE_STATIC(modlock);
  94. AST_MUTEX_DEFINE_STATIC(reloadlock);
  95. static struct module *module_list=NULL;
  96. static int modlistver = 0;
  97. int ast_unload_resource(char *resource_name, int force)
  98. {
  99. struct module *m, *ml = NULL;
  100. int res = -1;
  101. if (ast_mutex_lock(&modlock))
  102. ast_log(LOG_WARNING, "Failed to lock\n");
  103. m = module_list;
  104. while(m) {
  105. if (!strcasecmp(m->resource, resource_name)) {
  106. if ((res = m->usecount()) > 0) {
  107. if (force)
  108. ast_log(LOG_WARNING, "Warning: Forcing removal of module %s with use count %d\n", resource_name, res);
  109. else {
  110. ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name, res);
  111. ast_mutex_unlock(&modlock);
  112. return -1;
  113. }
  114. }
  115. res = m->unload_module();
  116. if (res) {
  117. ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
  118. if (force <= AST_FORCE_FIRM) {
  119. ast_mutex_unlock(&modlock);
  120. return -1;
  121. } else
  122. ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
  123. }
  124. if (ml)
  125. ml->next = m->next;
  126. else
  127. module_list = m->next;
  128. dlclose(m->lib);
  129. free(m);
  130. break;
  131. }
  132. ml = m;
  133. m = m->next;
  134. }
  135. modlistver = rand();
  136. ast_mutex_unlock(&modlock);
  137. ast_update_use_count();
  138. return res;
  139. }
  140. void ast_module_reload(const char *name)
  141. {
  142. struct module *m;
  143. int oldversion;
  144. int (*reload)(void);
  145. /* We'll do the logger and manager the favor of calling its reload here first */
  146. if (ast_mutex_trylock(&reloadlock)) {
  147. ast_verbose("The previous reload command didn't finish yet\n");
  148. return;
  149. }
  150. if (!name || !strcasecmp(name, "astconfig"))
  151. read_ast_cust_config();
  152. if (!name || !strcasecmp(name, "manager"))
  153. reload_manager();
  154. if (!name || !strcasecmp(name, "enum"))
  155. ast_enum_reload();
  156. if (!name || !strcasecmp(name, "rtp"))
  157. ast_rtp_reload();
  158. time(&ast_lastreloadtime);
  159. ast_mutex_lock(&modlock);
  160. oldversion = modlistver;
  161. m = module_list;
  162. while(m) {
  163. if (!name || !strcasecmp(name, m->resource)) {
  164. reload = m->reload;
  165. ast_mutex_unlock(&modlock);
  166. if (reload) {
  167. if (option_verbose > 2)
  168. ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", m->resource, m->description());
  169. reload();
  170. }
  171. ast_mutex_lock(&modlock);
  172. if (oldversion != modlistver)
  173. break;
  174. }
  175. m = m->next;
  176. }
  177. ast_mutex_unlock(&modlock);
  178. ast_mutex_unlock(&reloadlock);
  179. }
  180. int ast_load_resource(char *resource_name)
  181. {
  182. static char fn[256];
  183. int errors=0;
  184. int res;
  185. struct module *m;
  186. int flags=RTLD_NOW;
  187. #ifdef RTLD_GLOBAL
  188. char *val;
  189. #endif
  190. char *key;
  191. int o;
  192. struct ast_config *cfg;
  193. char tmp[80];
  194. /* Keep the module file parsing silent */
  195. o = option_verbose;
  196. if (strncasecmp(resource_name, "res_", 4)) {
  197. option_verbose = 0;
  198. cfg = ast_load(AST_MODULE_CONFIG);
  199. option_verbose = o;
  200. if (cfg) {
  201. #ifdef RTLD_GLOBAL
  202. if ((val = ast_variable_retrieve(cfg, "global", resource_name))
  203. && ast_true(val))
  204. flags |= RTLD_GLOBAL;
  205. #endif
  206. ast_destroy(cfg);
  207. }
  208. } else {
  209. /* Resource modules are always loaded global and lazy */
  210. #ifdef RTLD_GLOBAL
  211. flags = (RTLD_GLOBAL | RTLD_LAZY);
  212. #else
  213. flags = RTLD_LAZY;
  214. #endif
  215. }
  216. if (ast_mutex_lock(&modlock))
  217. ast_log(LOG_WARNING, "Failed to lock\n");
  218. m = module_list;
  219. while(m) {
  220. if (!strcasecmp(m->resource, resource_name)) {
  221. ast_log(LOG_WARNING, "Module '%s' already exists\n", resource_name);
  222. ast_mutex_unlock(&modlock);
  223. return -1;
  224. }
  225. m = m->next;
  226. }
  227. m = malloc(sizeof(struct module));
  228. if (!m) {
  229. ast_log(LOG_WARNING, "Out of memory\n");
  230. ast_mutex_unlock(&modlock);
  231. return -1;
  232. }
  233. strncpy(m->resource, resource_name, sizeof(m->resource)-1);
  234. if (resource_name[0] == '/') {
  235. strncpy(fn, resource_name, sizeof(fn)-1);
  236. } else {
  237. snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_MODULE_DIR, resource_name);
  238. }
  239. m->lib = dlopen(fn, flags);
  240. if (!m->lib) {
  241. ast_log(LOG_WARNING, "%s\n", dlerror());
  242. free(m);
  243. ast_mutex_unlock(&modlock);
  244. return -1;
  245. }
  246. m->load_module = dlsym(m->lib, "load_module");
  247. if (m->load_module == NULL)
  248. m->load_module = dlsym(m->lib, "_load_module");
  249. if (!m->load_module) {
  250. ast_log(LOG_WARNING, "No load_module in module %s\n", fn);
  251. errors++;
  252. }
  253. m->unload_module = dlsym(m->lib, "unload_module");
  254. if (m->unload_module == NULL)
  255. m->unload_module = dlsym(m->lib, "_unload_module");
  256. if (!m->unload_module) {
  257. ast_log(LOG_WARNING, "No unload_module in module %s\n", fn);
  258. errors++;
  259. }
  260. m->usecount = dlsym(m->lib, "usecount");
  261. if (m->usecount == NULL)
  262. m->usecount = dlsym(m->lib, "_usecount");
  263. if (!m->usecount) {
  264. ast_log(LOG_WARNING, "No usecount in module %s\n", fn);
  265. errors++;
  266. }
  267. m->description = dlsym(m->lib, "description");
  268. if (m->description == NULL)
  269. m->description = dlsym(m->lib, "_description");
  270. if (!m->description) {
  271. ast_log(LOG_WARNING, "No description in module %s\n", fn);
  272. errors++;
  273. }
  274. m->key = dlsym(m->lib, "key");
  275. if (m->key == NULL)
  276. m->key = dlsym(m->lib, "_key");
  277. if (!m->key) {
  278. ast_log(LOG_WARNING, "No key routine in module %s\n", fn);
  279. errors++;
  280. }
  281. m->reload = dlsym(m->lib, "reload");
  282. if (m->reload == NULL)
  283. m->reload = dlsym(m->lib, "_reload");
  284. if (!m->key || !(key = m->key())) {
  285. ast_log(LOG_WARNING, "Key routine returned NULL in module %s\n", fn);
  286. key = NULL;
  287. errors++;
  288. }
  289. if (key && verify_key(key)) {
  290. ast_log(LOG_WARNING, "Unexpected key returned by module %s\n", fn);
  291. errors++;
  292. }
  293. if (errors) {
  294. ast_log(LOG_WARNING, "%d error(s) loading module %s, aborted\n", errors, fn);
  295. dlclose(m->lib);
  296. free(m);
  297. ast_mutex_unlock(&modlock);
  298. return -1;
  299. }
  300. if (!fully_booted) {
  301. if (option_verbose)
  302. ast_verbose( " => (%s)\n", term_color(tmp, m->description(), COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
  303. if (option_console && !option_verbose)
  304. ast_verbose( ".");
  305. } else {
  306. if (option_verbose)
  307. ast_verbose(VERBOSE_PREFIX_1 "Loaded %s => (%s)\n", fn, m->description());
  308. }
  309. // add module 'm' to end of module_list chain
  310. // so reload commands will be issued in same order modules were loaded
  311. m->next = NULL;
  312. if (module_list == NULL) {
  313. // empty list so far, add at front
  314. module_list = m;
  315. }
  316. else {
  317. struct module *i;
  318. // find end of chain, and add there
  319. for (i = module_list; i->next; i = i->next)
  320. ;
  321. i->next = m;
  322. }
  323. modlistver = rand();
  324. ast_mutex_unlock(&modlock);
  325. if ((res = m->load_module())) {
  326. ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", m->resource, res);
  327. ast_unload_resource(resource_name, 0);
  328. return -1;
  329. }
  330. ast_update_use_count();
  331. return 0;
  332. }
  333. static int ast_resource_exists(char *resource)
  334. {
  335. struct module *m;
  336. if (ast_mutex_lock(&modlock))
  337. ast_log(LOG_WARNING, "Failed to lock\n");
  338. m = module_list;
  339. while(m) {
  340. if (!strcasecmp(resource, m->resource))
  341. break;
  342. m = m->next;
  343. }
  344. ast_mutex_unlock(&modlock);
  345. if (m)
  346. return -1;
  347. else
  348. return 0;
  349. }
  350. int load_modules()
  351. {
  352. struct ast_config *cfg;
  353. struct ast_variable *v;
  354. char tmp[80];
  355. if (option_verbose)
  356. ast_verbose( "Asterisk Dynamic Loader Starting:\n");
  357. cfg = ast_load(AST_MODULE_CONFIG);
  358. if (cfg) {
  359. /* Load explicitly defined modules */
  360. v = ast_variable_browse(cfg, "modules");
  361. while(v) {
  362. if (!strcasecmp(v->name, "load")) {
  363. if (option_debug && !option_verbose)
  364. ast_log(LOG_DEBUG, "Loading module %s\n", v->value);
  365. if (option_verbose) {
  366. ast_verbose( VERBOSE_PREFIX_1 "[%s]", term_color(tmp, v->value, COLOR_BRWHITE, 0, sizeof(tmp)));
  367. fflush(stdout);
  368. }
  369. if (ast_load_resource(v->value)) {
  370. ast_log(LOG_WARNING, "Loading module %s failed!\n", v->value);
  371. if (cfg)
  372. ast_destroy(cfg);
  373. return -1;
  374. }
  375. }
  376. v=v->next;
  377. }
  378. }
  379. if (!cfg || ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
  380. /* Load all modules */
  381. DIR *mods;
  382. struct dirent *d;
  383. int x;
  384. /* Make two passes. First, load any resource modules, then load the others. */
  385. for (x=0;x<2;x++) {
  386. mods = opendir((char *)ast_config_AST_MODULE_DIR);
  387. if (mods) {
  388. while((d = readdir(mods))) {
  389. /* Must end in .so to load it. */
  390. if ((strlen(d->d_name) > 3) && (x || !strncasecmp(d->d_name, "res_", 4)) &&
  391. !strcasecmp(d->d_name + strlen(d->d_name) - 3, ".so") &&
  392. !ast_resource_exists(d->d_name)) {
  393. /* It's a shared library -- Just be sure we're allowed to load it -- kinda
  394. an inefficient way to do it, but oh well. */
  395. if (cfg) {
  396. v = ast_variable_browse(cfg, "modules");
  397. while(v) {
  398. if (!strcasecmp(v->name, "noload") &&
  399. !strcasecmp(v->value, d->d_name))
  400. break;
  401. v = v->next;
  402. }
  403. if (v) {
  404. if (option_verbose) {
  405. ast_verbose( VERBOSE_PREFIX_1 "[skipping %s]\n", d->d_name);
  406. fflush(stdout);
  407. }
  408. continue;
  409. }
  410. }
  411. if (option_debug && !option_verbose)
  412. ast_log(LOG_DEBUG, "Loading module %s\n", d->d_name);
  413. if (option_verbose) {
  414. ast_verbose( VERBOSE_PREFIX_1 "[%s]", term_color(tmp, d->d_name, COLOR_BRWHITE, 0, sizeof(tmp)));
  415. fflush(stdout);
  416. }
  417. if (ast_load_resource(d->d_name)) {
  418. ast_log(LOG_WARNING, "Loading module %s failed!\n", d->d_name);
  419. if (cfg)
  420. ast_destroy(cfg);
  421. return -1;
  422. }
  423. }
  424. }
  425. closedir(mods);
  426. } else {
  427. if (!option_quiet)
  428. ast_log(LOG_WARNING, "Unable to open modules directory %s.\n", (char *)ast_config_AST_MODULE_DIR);
  429. }
  430. }
  431. }
  432. ast_destroy(cfg);
  433. return 0;
  434. }
  435. void ast_update_use_count(void)
  436. {
  437. /* Notify any module monitors that the use count for a
  438. resource has changed */
  439. struct loadupdate *m;
  440. if (ast_mutex_lock(&modlock))
  441. ast_log(LOG_WARNING, "Failed to lock\n");
  442. m = updaters;
  443. while(m) {
  444. m->updater();
  445. m = m->next;
  446. }
  447. ast_mutex_unlock(&modlock);
  448. }
  449. int ast_update_module_list(int (*modentry)(char *module, char *description, int usecnt))
  450. {
  451. struct module *m;
  452. int unlock = -1;
  453. if (ast_mutex_trylock(&modlock))
  454. unlock = 0;
  455. m = module_list;
  456. while(m) {
  457. modentry(m->resource, m->description(), m->usecount());
  458. m = m->next;
  459. }
  460. if (unlock)
  461. ast_mutex_unlock(&modlock);
  462. return 0;
  463. }
  464. int ast_loader_register(int (*v)(void))
  465. {
  466. struct loadupdate *tmp;
  467. /* XXX Should be more flexible here, taking > 1 verboser XXX */
  468. if ((tmp = malloc(sizeof (struct loadupdate)))) {
  469. tmp->updater = v;
  470. if (ast_mutex_lock(&modlock))
  471. ast_log(LOG_WARNING, "Failed to lock\n");
  472. tmp->next = updaters;
  473. updaters = tmp;
  474. ast_mutex_unlock(&modlock);
  475. return 0;
  476. }
  477. return -1;
  478. }
  479. int ast_loader_unregister(int (*v)(void))
  480. {
  481. int res = -1;
  482. struct loadupdate *tmp, *tmpl=NULL;
  483. if (ast_mutex_lock(&modlock))
  484. ast_log(LOG_WARNING, "Failed to lock\n");
  485. tmp = updaters;
  486. while(tmp) {
  487. if (tmp->updater == v) {
  488. if (tmpl)
  489. tmpl->next = tmp->next;
  490. else
  491. updaters = tmp->next;
  492. break;
  493. }
  494. tmpl = tmp;
  495. tmp = tmp->next;
  496. }
  497. if (tmp)
  498. res = 0;
  499. ast_mutex_unlock(&modlock);
  500. return res;
  501. }