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