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