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