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