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