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