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