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