More OpenBSD patches
[asterisk/asterisk.git] / loader.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Module Loader
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
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 #include <asterisk/module.h>
20 #include <asterisk/options.h>
21 #include <asterisk/config.h>
22 #include <asterisk/logger.h>
23 #include <asterisk/channel.h>
24 #include <asterisk/term.h>
25 #include <asterisk/manager.h>
26 #include <dlfcn.h>
27 #include <asterisk/md5.h>
28 #define __USE_GNU
29 #include <pthread.h>
30 #include "asterisk.h"
31 #include "astconf.h"
32
33 #ifndef RTLD_NOW
34 #define RTLD_NOW 0
35 #endif
36
37 static char expected_key[] =
38 { 0x8e, 0x93, 0x22, 0x83, 0xf5, 0xc3, 0xc0, 0x75,
39   0xff, 0x8b, 0xa9, 0xbe, 0x7c, 0x43, 0x74, 0x63 };
40
41 struct module {
42         int (*load_module)(void);
43         int (*unload_module)(void);
44         int (*usecount)(void);
45         char *(*description)(void);
46         char *(*key)(void);
47         int (*reload)(void);
48         void *lib;
49         char resource[256];
50         struct module *next;
51 };
52
53 static int printdigest(unsigned char *d)
54 {
55         int x;
56         char buf[256];
57         char buf2[16];
58         snprintf(buf, sizeof(buf), "Unexpected signature:");
59         for (x=0;x<16;x++) {
60                 snprintf(buf2, sizeof(buf2), " %02x", *(d++));
61                 strcat(buf, buf2);
62         }
63         strcat(buf, "\n");
64         ast_log(LOG_DEBUG, buf);
65         return 0;
66 }
67
68 static int key_matches(char *key1, char *key2)
69 {
70         int match = 1;
71         int x;
72         for (x=0;x<16;x++) {
73                 match &= (key1[x] == key2[x]);
74         }
75         return match;
76 }
77
78 static int verify_key(char *key)
79 {
80         struct MD5Context c;
81         char digest[16];
82         MD5Init(&c);
83         MD5Update(&c, key, strlen(key));
84         MD5Final(digest, &c);
85         if (key_matches(expected_key, digest))
86                 return 0;
87         printdigest(digest);
88         return -1;
89 }
90
91 static struct loadupdate {
92         int (*updater)(void);
93         struct loadupdate *next;
94 } *updaters = NULL;
95
96 static pthread_mutex_t modlock = AST_MUTEX_INITIALIZER;
97
98 static struct module *module_list=NULL;
99
100 int ast_unload_resource(char *resource_name, int force)
101 {
102         struct module *m, *ml = NULL;
103         int res = -1;
104         if (ast_pthread_mutex_lock(&modlock))
105                 ast_log(LOG_WARNING, "Failed to lock\n");
106         m = module_list;
107         while(m) {
108                 if (!strcasecmp(m->resource, resource_name)) {
109                         if ((res = m->usecount()) > 0)  {
110                                 if (force) 
111                                         ast_log(LOG_WARNING, "Warning:  Forcing removal of module %s with use count %d\n", resource_name, res);
112                                 else {
113                                         ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name, res);
114                                         ast_pthread_mutex_unlock(&modlock);
115                                         return -1;
116                                 }
117                         }
118                         res = m->unload_module();
119                         if (res) {
120                                 ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
121                                 if (force <= AST_FORCE_FIRM) {
122                                         ast_pthread_mutex_unlock(&modlock);
123                                         return -1;
124                                 } else
125                                         ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
126                         }
127                         if (ml)
128                                 ml->next = m->next;
129                         else
130                                 module_list = m->next;
131                         dlclose(m->lib);
132                         free(m);
133                 }
134                 ml = m;
135                 m = m->next;
136         }
137         ast_pthread_mutex_unlock(&modlock);
138         ast_update_use_count();
139         return res;
140 }
141
142 void ast_module_reload(void)
143 {
144         struct module *m;
145
146         /* We'll do the logger and manager the favor of calling its reload here first */
147         reload_manager();
148
149         ast_pthread_mutex_lock(&modlock);
150         m = module_list;
151         while(m) {
152                 if (m->reload) {
153                         if (option_verbose > 2) 
154                                 ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", m->resource, m->description());
155                         m->reload();
156                 }
157                 m = m->next;
158         }
159         ast_pthread_mutex_unlock(&modlock);
160 }
161
162 int ast_load_resource(char *resource_name)
163 {
164         static char fn[256];
165         int errors=0;
166         int res;
167         struct module *m;
168         int flags=RTLD_NOW;
169         char *val;
170         char *key;
171         int o;
172         struct ast_config *cfg;
173         char tmp[80];
174         /* Keep the module file parsing silent */
175         o = option_verbose;
176         if (strncasecmp(resource_name, "res_", 4)) {
177                 option_verbose = 0;
178                 cfg = ast_load(AST_MODULE_CONFIG);
179                 option_verbose = o;
180                 if (cfg) {
181 #ifdef RTLD_GLOBAL
182                         if ((val = ast_variable_retrieve(cfg, "global", resource_name))
183                                         && ast_true(val))
184                                 flags |= RTLD_GLOBAL;
185 #endif
186                         ast_destroy(cfg);
187                 }
188         } else {
189                 /* Resource modules are always loaded global and lazy */
190 #ifdef RTLD_GLOBAL
191                 flags = (RTLD_GLOBAL | RTLD_LAZY);
192 #else
193                 flags = RTLD_LAZY;
194 #endif
195         }
196         
197         if (ast_pthread_mutex_lock(&modlock))
198                 ast_log(LOG_WARNING, "Failed to lock\n");
199         m = module_list;
200         while(m) {
201                 if (!strcasecmp(m->resource, resource_name)) {
202                         ast_log(LOG_WARNING, "Module '%s' already exists\n", resource_name);
203                         ast_pthread_mutex_unlock(&modlock);
204                         return -1;
205                 }
206                 m = m->next;
207         }
208         m = malloc(sizeof(struct module));      
209         if (!m) {
210                 ast_log(LOG_WARNING, "Out of memory\n");
211                 ast_pthread_mutex_unlock(&modlock);
212                 return -1;
213         }
214         strncpy(m->resource, resource_name, sizeof(m->resource)-1);
215         if (resource_name[0] == '/') {
216                 strncpy(fn, resource_name, sizeof(fn)-1);
217         } else {
218                 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_MODULE_DIR, resource_name);
219         }
220         m->lib = dlopen(fn, flags);
221         if (!m->lib) {
222                 ast_log(LOG_WARNING, "%s\n", dlerror());
223                 free(m);
224                 ast_pthread_mutex_unlock(&modlock);
225                 return -1;
226         }
227         m->load_module = dlsym(m->lib, "load_module");
228         if (m->load_module == NULL)
229                 m->load_module = dlsym(m->lib, "_load_module");
230         if (!m->load_module) {
231                 ast_log(LOG_WARNING, "No load_module in module %s\n", fn);
232                 errors++;
233         }
234         m->unload_module = dlsym(m->lib, "unload_module");
235         if (m->unload_module == NULL)
236                 m->unload_module = dlsym(m->lib, "_unload_module");
237         if (!m->unload_module) {
238                 ast_log(LOG_WARNING, "No unload_module in module %s\n", fn);
239                 errors++;
240         }
241         m->usecount = dlsym(m->lib, "usecount");
242         if (m->usecount == NULL)
243                 m->usecount = dlsym(m->lib, "_usecount");
244         if (!m->usecount) {
245                 ast_log(LOG_WARNING, "No usecount in module %s\n", fn);
246                 errors++;
247         }
248         m->description = dlsym(m->lib, "description");
249         if (m->description == NULL)
250                 m->description = dlsym(m->lib, "_description");
251         if (!m->description) {
252                 ast_log(LOG_WARNING, "No description in module %s\n", fn);
253                 errors++;
254         }
255         m->key = dlsym(m->lib, "key");
256         if (m->key == NULL)
257                 m->key = dlsym(m->lib, "_key");
258         if (!m->key) {
259                 ast_log(LOG_WARNING, "No key routine in module %s\n", fn);
260                 errors++;
261         }
262         m->reload = dlsym(m->lib, "reload");
263         if (m->reload == NULL)
264                 m->reload = dlsym(m->lib, "_reload");
265         if (m->key && !(key = m->key())) {
266                 ast_log(LOG_WARNING, "Key routine returned NULL in module %s\n", fn);
267                 errors++;
268         } else
269                 key = NULL;
270         if (key && verify_key(key)) {
271                 ast_log(LOG_WARNING, "Unexpected key returned by module %s\n", fn);
272                 errors++;
273         }
274         if (errors) {
275                 ast_log(LOG_WARNING, "%d error(s) loading module %s, aborted\n", errors, fn);
276                 dlclose(m->lib);
277                 free(m);
278                 ast_pthread_mutex_unlock(&modlock);
279                 return -1;
280         }
281         if (!fully_booted) {
282                 if (option_verbose) 
283                         ast_verbose( " => (%s)\n", term_color(tmp, m->description(), COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
284                 if (option_console && !option_verbose)
285                         ast_verbose( ".");
286         } else {
287                 if (option_verbose)
288                         ast_verbose(VERBOSE_PREFIX_1 "Loaded %s => (%s)\n", fn, m->description());
289         }
290         m->next = module_list;
291         
292         module_list = m;
293         ast_pthread_mutex_unlock(&modlock);
294         if ((res = m->load_module())) {
295                 ast_log(LOG_WARNING, "%s: load_module failed, returning %d\n", m->resource, res);
296                 ast_unload_resource(resource_name, 0);
297                 return -1;
298         }
299         ast_update_use_count();
300         return 0;
301 }       
302
303 static int ast_resource_exists(char *resource)
304 {
305         struct module *m;
306         if (ast_pthread_mutex_lock(&modlock))
307                 ast_log(LOG_WARNING, "Failed to lock\n");
308         m = module_list;
309         while(m) {
310                 if (!strcasecmp(resource, m->resource))
311                         break;
312                 m = m->next;
313         }
314         ast_pthread_mutex_unlock(&modlock);
315         if (m)
316                 return -1;
317         else
318                 return 0;
319 }
320
321 int load_modules()
322 {
323         struct ast_config *cfg;
324         struct ast_variable *v;
325         char tmp[80];
326         if (option_verbose) 
327                 ast_verbose( "Asterisk Dynamic Loader Starting:\n");
328         cfg = ast_load(AST_MODULE_CONFIG);
329         if (cfg) {
330                 /* Load explicitly defined modules */
331                 v = ast_variable_browse(cfg, "modules");
332                 while(v) {
333                         if (!strcasecmp(v->name, "load")) {
334                                 if (option_debug && !option_verbose)
335                                         ast_log(LOG_DEBUG, "Loading module %s\n", v->value);
336                                 if (option_verbose) {
337                                         ast_verbose( VERBOSE_PREFIX_1 "[%s]", term_color(tmp, v->value, COLOR_BRWHITE, 0, sizeof(tmp)));
338                                         fflush(stdout);
339                                 }
340                                 if (ast_load_resource(v->value)) {
341                                         ast_log(LOG_WARNING, "Loading module %s failed!\n", v->value);
342                                         if (cfg)
343                                                 ast_destroy(cfg);
344                                         return -1;
345                                 }
346                         }
347                         v=v->next;
348                 }
349         }
350         if (!cfg || ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
351                 /* Load all modules */
352                 DIR *mods;
353                 struct dirent *d;
354                 int x;
355                 /* Make two passes.  First, load any resource modules, then load the others. */
356                 for (x=0;x<2;x++) {
357                         mods = opendir((char *)ast_config_AST_MODULE_DIR);
358                         if (mods) {
359                                 while((d = readdir(mods))) {
360                                         /* Must end in .so to load it.  */
361                                         if ((strlen(d->d_name) > 3) && (x || !strncasecmp(d->d_name, "res_", 4)) && 
362                                             !strcasecmp(d->d_name + strlen(d->d_name) - 3, ".so") &&
363                                                 !ast_resource_exists(d->d_name)) {
364                                                 /* It's a shared library -- Just be sure we're allowed to load it -- kinda
365                                                    an inefficient way to do it, but oh well. */
366                                                 if (cfg) {
367                                                         v = ast_variable_browse(cfg, "modules");
368                                                         while(v) {
369                                                                 if (!strcasecmp(v->name, "noload") &&
370                                                                     !strcasecmp(v->value, d->d_name)) 
371                                                                         break;
372                                                                 v = v->next;
373                                                         }
374                                                         if (v) {
375                                                                 if (option_verbose) {
376                                                                         ast_verbose( VERBOSE_PREFIX_1 "[skipping %s]\n", d->d_name);
377                                                                         fflush(stdout);
378                                                                 }
379                                                                 continue;
380                                                         }
381                                                         
382                                                 }
383                                             if (option_debug && !option_verbose)
384                                                         ast_log(LOG_DEBUG, "Loading module %s\n", d->d_name);
385                                                 if (option_verbose) {
386                                                         ast_verbose( VERBOSE_PREFIX_1 "[%s]", term_color(tmp, d->d_name, COLOR_BRWHITE, 0, sizeof(tmp)));
387                                                         fflush(stdout);
388                                                 }
389                                                 if (ast_load_resource(d->d_name)) {
390                                                         ast_log(LOG_WARNING, "Loading module %s failed!\n", d->d_name);
391                                                         if (cfg)
392                                                                 ast_destroy(cfg);
393                                                         return -1;
394                                                 }
395                                         }
396                                 }
397                                 closedir(mods);
398                         } else {
399                                 if (!option_quiet)
400                                         ast_log(LOG_WARNING, "Unable to open modules directory %s.\n", (char *)ast_config_AST_MODULE_DIR);
401                         }
402                 }
403         } 
404         ast_destroy(cfg);
405         return 0;
406 }
407
408 void ast_update_use_count(void)
409 {
410         /* Notify any module monitors that the use count for a 
411            resource has changed */
412         struct loadupdate *m;
413         if (ast_pthread_mutex_lock(&modlock))
414                 ast_log(LOG_WARNING, "Failed to lock\n");
415         m = updaters;
416         while(m) {
417                 m->updater();
418                 m = m->next;
419         }
420         ast_pthread_mutex_unlock(&modlock);
421         
422 }
423
424 int ast_update_module_list(int (*modentry)(char *module, char *description, int usecnt))
425 {
426         struct module *m;
427         int unlock = -1;
428         if (pthread_mutex_trylock(&modlock))
429                 unlock = 0;
430         m = module_list;
431         while(m) {
432                 modentry(m->resource, m->description(), m->usecount());
433                 m = m->next;
434         }
435         if (unlock)
436                 ast_pthread_mutex_unlock(&modlock);
437         return 0;
438 }
439
440 int ast_loader_register(int (*v)(void)) 
441 {
442         struct loadupdate *tmp;
443         /* XXX Should be more flexible here, taking > 1 verboser XXX */
444         if ((tmp = malloc(sizeof (struct loadupdate)))) {
445                 tmp->updater = v;
446                 if (ast_pthread_mutex_lock(&modlock))
447                         ast_log(LOG_WARNING, "Failed to lock\n");
448                 tmp->next = updaters;
449                 updaters = tmp;
450                 ast_pthread_mutex_unlock(&modlock);
451                 return 0;
452         }
453         return -1;
454 }
455
456 int ast_loader_unregister(int (*v)(void))
457 {
458         int res = -1;
459         struct loadupdate *tmp, *tmpl=NULL;
460         if (ast_pthread_mutex_lock(&modlock))
461                 ast_log(LOG_WARNING, "Failed to lock\n");
462         tmp = updaters;
463         while(tmp) {
464                 if (tmp->updater == v)  {
465                         if (tmpl)
466                                 tmpl->next = tmp->next;
467                         else
468                                 updaters = tmp->next;
469                         break;
470                 }
471                 tmpl = tmp;
472                 tmp = tmp->next;
473         }
474         if (tmp)
475                 res = 0;
476         ast_pthread_mutex_unlock(&modlock);
477         return res;
478 }