when loadable modules are disabled, if someone tries to load a module that does not...
[asterisk/asterisk.git] / main / loader.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  * Kevin P. Fleming <kpfleming@digium.com>
8  * Luigi Rizzo <rizzo@icir.org>
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20
21 /*! \file
22  *
23  * \brief Module Loader
24  * \author Mark Spencer <markster@digium.com> 
25  * \author Kevin P. Fleming <kpfleming@digium.com>
26  * \author Luigi Rizzo <rizzo@icir.org>
27  * - See ModMngMnt
28  */
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include <stdio.h>
35 #include <dirent.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <string.h>
40
41 #include "asterisk/linkedlists.h"
42 #include "asterisk/module.h"
43 #include "asterisk/options.h"
44 #include "asterisk/config.h"
45 #include "asterisk/logger.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/term.h"
48 #include "asterisk/manager.h"
49 #include "asterisk/cdr.h"
50 #include "asterisk/enum.h"
51 #include "asterisk/rtp.h"
52 #include "asterisk/http.h"
53 #include "asterisk/lock.h"
54
55 #ifdef DLFCNCOMPAT
56 #include "asterisk/dlfcn-compat.h"
57 #else
58 #include <dlfcn.h>
59 #endif
60
61 #include "asterisk/md5.h"
62 #include "asterisk/utils.h"
63
64 #ifndef RTLD_NOW
65 #define RTLD_NOW 0
66 #endif
67
68 struct ast_module_user {
69         struct ast_channel *chan;
70         AST_LIST_ENTRY(ast_module_user) entry;
71 };
72
73 AST_LIST_HEAD(module_user_list, ast_module_user);
74
75 static unsigned char expected_key[] =
76 { 0x87, 0x76, 0x79, 0x35, 0x23, 0xea, 0x3a, 0xd3,
77   0x25, 0x2a, 0xbb, 0x35, 0x87, 0xe4, 0x22, 0x24 };
78
79 static unsigned int embedding = 1; /* we always start out by registering embedded modules,
80                                       since they are here before we dlopen() any
81                                    */
82
83 enum flags {
84         FLAG_RUNNING = (1 << 1),                /* module successfully initialized */
85         FLAG_DECLINED = (1 << 2),               /* module declined to initialize */
86 };
87
88 struct ast_module {
89         const struct ast_module_info *info;
90         void *lib;                                      /* the shared lib, or NULL if embedded */
91         int usecount;                                   /* the number of 'users' currently in this module */
92         struct module_user_list users;                  /* the list of users in the module */
93         unsigned int flags;                             /* flags for this module */
94         AST_LIST_ENTRY(ast_module) entry;
95         char resource[0];
96 };
97
98 static AST_LIST_HEAD_STATIC(module_list, ast_module);
99
100 struct loadupdate {
101         int (*updater)(void);
102         AST_LIST_ENTRY(loadupdate) entry;
103 };
104
105 static AST_LIST_HEAD_STATIC(updaters, loadupdate);
106
107 AST_MUTEX_DEFINE_STATIC(reloadlock);
108
109 /* when dynamic modules are being loaded, ast_module_register() will
110    need to know what filename the module was loaded from while it
111    is being registered
112 */
113 struct ast_module *resource_being_loaded;
114
115 /* XXX: should we check for duplicate resource names here? */
116
117 void ast_module_register(const struct ast_module_info *info)
118 {
119         struct ast_module *mod;
120
121         if (embedding) {
122                 if (!(mod = ast_calloc(1, sizeof(*mod) + strlen(info->name) + 1)))
123                         return;
124                 strcpy(mod->resource, info->name);
125         } else {
126                 mod = resource_being_loaded;
127         }
128
129         mod->info = info;
130         AST_LIST_HEAD_INIT(&mod->users);
131
132         /* during startup, before the loader has been initialized,
133            there are no threads, so there is no need to take the lock
134            on this list to manipulate it. it is also possible that it
135            might be unsafe to use the list lock at that point... so
136            let's avoid it altogether
137         */
138         if (!embedding)
139                 AST_LIST_LOCK(&module_list);
140
141         /* it is paramount that the new entry be placed at the tail of
142            the list, otherwise the code that uses dlopen() to load
143            dynamic modules won't be able to find out if the module it
144            just opened was registered or failed to load
145         */
146         AST_LIST_INSERT_TAIL(&module_list, mod, entry);
147
148         if (!embedding)
149                 AST_LIST_UNLOCK(&module_list);
150
151         /* give the module a copy of its own handle, for later use in registrations and the like */
152         *((struct ast_module **) &(info->self)) = mod;
153 }
154
155 void ast_module_unregister(const struct ast_module_info *info)
156 {
157         struct ast_module *mod = NULL;
158
159         /* it is assumed that the users list in the module structure
160            will already be empty, or we cannot have gotten to this
161            point
162         */
163         AST_LIST_LOCK(&module_list);
164         AST_LIST_TRAVERSE_SAFE_BEGIN(&module_list, mod, entry) {
165                 if (mod->info == info) {
166                         AST_LIST_REMOVE_CURRENT(&module_list, entry);
167                         break;
168                 }
169         }
170         AST_LIST_TRAVERSE_SAFE_END;
171         AST_LIST_UNLOCK(&module_list);
172
173         if (mod) {
174                 AST_LIST_HEAD_DESTROY(&mod->users);
175                 free(mod);
176         }
177 }
178
179 struct ast_module_user *__ast_module_user_add(struct ast_module *mod,
180                                               struct ast_channel *chan)
181 {
182         struct ast_module_user *u = ast_calloc(1, sizeof(*u));
183
184         if (!u)
185                 return NULL;
186
187         u->chan = chan;
188
189         AST_LIST_LOCK(&mod->users);
190         AST_LIST_INSERT_HEAD(&mod->users, u, entry);
191         AST_LIST_UNLOCK(&mod->users);
192
193         ast_atomic_fetchadd_int(&mod->usecount, +1);
194
195         ast_update_use_count();
196
197         return u;
198 }
199
200 void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u)
201 {
202         AST_LIST_LOCK(&mod->users);
203         AST_LIST_REMOVE(&mod->users, u, entry);
204         AST_LIST_UNLOCK(&mod->users);
205         ast_atomic_fetchadd_int(&mod->usecount, -1);
206         free(u);
207
208         ast_update_use_count();
209 }
210
211 void __ast_module_user_hangup_all(struct ast_module *mod)
212 {
213         struct ast_module_user *u;
214
215         AST_LIST_LOCK(&mod->users);
216         while ((u = AST_LIST_REMOVE_HEAD(&mod->users, entry))) {
217                 ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
218                 ast_atomic_fetchadd_int(&mod->usecount, -1);
219                 free(u);
220         }
221         AST_LIST_UNLOCK(&mod->users);
222
223         ast_update_use_count();
224 }
225
226 /*! \note
227  * In addition to modules, the reload command handles some extra keywords
228  * which are listed here together with the corresponding handlers.
229  * This table is also used by the command completion code.
230  */
231 static struct reload_classes {
232         const char *name;
233         int (*reload_fn)(void);
234 } reload_classes[] = {  /* list in alpha order, longest match first for cli completion */
235         { "cdr",        ast_cdr_engine_reload },
236         { "dnsmgr",     dnsmgr_reload },
237         { "extconfig",  read_config_maps },
238         { "enum",       ast_enum_reload },
239         { "manager",    reload_manager },
240         { "rtp",        ast_rtp_reload },
241         { "http",       ast_http_reload },
242         { NULL,         NULL }
243 };
244
245 static int printdigest(const unsigned char *d)
246 {
247         int x, pos;
248         char buf[256]; /* large enough so we don't have to worry */
249
250         for (pos = 0, x = 0; x < 16; x++)
251                 pos += sprintf(buf + pos, " %02x", *d++);
252
253         ast_log(LOG_DEBUG, "Unexpected signature:%s\n", buf);
254
255         return 0;
256 }
257
258 static int key_matches(const unsigned char *key1, const unsigned char *key2)
259 {
260         int x;
261
262         for (x = 0; x < 16; x++) {
263                 if (key1[x] != key2[x])
264                         return 0;
265         }
266
267         return 1;
268 }
269
270 static int verify_key(const unsigned char *key)
271 {
272         struct MD5Context c;
273         unsigned char digest[16];
274
275         MD5Init(&c);
276         MD5Update(&c, key, strlen((char *)key));
277         MD5Final(digest, &c);
278
279         if (key_matches(expected_key, digest))
280                 return 0;
281
282         printdigest(digest);
283
284         return -1;
285 }
286
287 static int resource_name_match(const char *name1_in, const char *name2_in)
288 {
289         char *name1 = (char *) name1_in;
290         char *name2 = (char *) name2_in;
291
292         /* trim off any .so extensions */
293         if (!strcasecmp(name1 + strlen(name1) - 3, ".so")) {
294                 name1 = ast_strdupa(name1);
295                 name1[strlen(name1) - 3] = '\0';
296         }
297         if (!strcasecmp(name2 + strlen(name2) - 3, ".so")) {
298                 name2 = ast_strdupa(name2);
299                 name2[strlen(name2) - 3] = '\0';
300         }
301
302         return strcasecmp(name1, name2);
303 }
304
305 static struct ast_module *find_resource(const char *resource, int do_lock)
306 {
307         struct ast_module *cur;
308
309         if (do_lock)
310                 AST_LIST_LOCK(&module_list);
311
312         AST_LIST_TRAVERSE(&module_list, cur, entry) {
313                 if (!resource_name_match(resource, cur->resource))
314                         break;
315         }
316
317         if (do_lock)
318                 AST_LIST_UNLOCK(&module_list);
319
320         return cur;
321 }
322
323 #if LOADABLE_MODULES
324 static void unload_dynamic_module(struct ast_module *mod)
325 {
326         if (mod->lib)
327                 dlclose(mod->lib);
328         /* WARNING: the structure pointed to by mod is now gone! */
329 }
330
331 static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only)
332 {
333         char fn[256];
334         void *lib;
335         struct ast_module *mod;
336         char *resource = (char *) resource_in;
337         unsigned int wants_global;
338
339         if (strcasecmp(resource + strlen(resource) - 3, ".so")) {
340                 resource = alloca(strlen(resource_in) + 3);
341                 strcpy(resource, resource_in);
342                 strcat(resource, ".so");
343         }
344
345         snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_MODULE_DIR, resource);
346
347         /* make a first load of the module in 'quiet' mode... don't try to resolve
348            any symbols, and don't export any symbols. this will allow us to peek into
349            the module's info block (if available) to see what flags it has set */
350
351         if (!(resource_being_loaded = ast_calloc(1, sizeof(*resource_being_loaded) + strlen(resource) + 1)))
352                 return NULL;
353
354         strcpy(resource_being_loaded->resource, resource);
355
356         if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_LOCAL))) {
357                 ast_log(LOG_WARNING, "%s\n", dlerror());
358                 free(resource_being_loaded);
359                 return NULL;
360         }
361
362         /* the dlopen() succeeded, let's find out if the module
363            registered itself */
364         /* note that this will only work properly as long as
365            ast_module_register() (which is called by the module's
366            constructor) places the new module at the tail of the
367            module_list
368         */
369         if (resource_being_loaded != (mod = AST_LIST_LAST(&module_list))) {
370                 /* no, it did not, so close it and return */
371                 dlclose(lib);
372                 /* note that the module's destructor will call ast_module_unregister(),
373                    which will free the structure we allocated in resource_being_loaded */
374                 return NULL;
375         }
376
377         wants_global = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS);
378
379         /* we are done with this first load, so clean up and start over */
380
381         dlclose(lib);
382         resource_being_loaded = NULL;
383
384         /* if we are being asked only to load modules that provide global symbols,
385            and this one does not, then close it and return */
386         if (global_symbols_only && !wants_global)
387                 return NULL;
388
389         /* start the load process again */
390
391         if (!(resource_being_loaded = ast_calloc(1, sizeof(*resource_being_loaded) + strlen(resource) + 1)))
392                 return NULL;
393
394         strcpy(resource_being_loaded->resource, resource);
395
396         if (!(lib = dlopen(fn, wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) {
397                 ast_log(LOG_WARNING, "%s\n", dlerror());
398                 free(resource_being_loaded);
399                 return NULL;
400         }
401
402         /* since the module was successfully opened, and it registered itself
403            the previous time we did that, we're going to assume it worked this
404            time too :) */
405         AST_LIST_LAST(&module_list)->lib = lib;
406         resource_being_loaded = NULL;
407
408         return AST_LIST_LAST(&module_list);
409 }
410 #endif
411
412 int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode force)
413 {
414         struct ast_module *mod;
415         int res = -1;
416         int error = 0;
417
418         AST_LIST_LOCK(&module_list);
419
420         mod = find_resource(resource_name, 0);
421
422         if (!ast_test_flag(mod, FLAG_RUNNING | FLAG_DECLINED))
423                 error = 1;
424
425         if (!error && (mod->usecount > 0)) {
426                 if (force) 
427                         ast_log(LOG_WARNING, "Warning:  Forcing removal of module '%s' with use count %d\n",
428                                 resource_name, mod->usecount);
429                 else {
430                         ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name,
431                                 mod->usecount);
432                         error = 1;
433                 }
434         }
435
436         if (!error) {
437                 __ast_module_user_hangup_all(mod);
438                 res = mod->info->unload();
439
440                 if (res) {
441                         ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
442                         if (force <= AST_FORCE_FIRM)
443                                 error = 1;
444                         else
445                                 ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
446                 }
447         }
448
449         if (!error)
450                 ast_clear_flag(mod, FLAG_RUNNING | FLAG_DECLINED);
451
452         AST_LIST_UNLOCK(&module_list);
453
454 #if LOADABLE_MODULES
455         if (!error)
456                 unload_dynamic_module(mod);
457 #endif
458
459         if (!error)
460                 ast_update_use_count();
461
462         return res;
463 }
464
465 char *ast_module_helper(const char *line, const char *word, int pos, int state, int rpos, int needsreload)
466 {
467         struct ast_module *cur;
468         int i, which=0, l = strlen(word);
469         char *ret = NULL;
470
471         if (pos != rpos)
472                 return NULL;
473
474         AST_LIST_LOCK(&module_list);
475         AST_LIST_TRAVERSE(&module_list, cur, entry) {
476                 if (!strncasecmp(word, cur->resource, l) &&
477                     (cur->info->reload || !needsreload) &&
478                     ++which > state) {
479                         ret = strdup(cur->resource);
480                         break;
481                 }
482         }
483         AST_LIST_UNLOCK(&module_list);
484
485         if (!ret) {
486                 for (i=0; !ret && reload_classes[i].name; i++) {
487                         if (!strncasecmp(word, reload_classes[i].name, l) && ++which > state)
488                                 ret = strdup(reload_classes[i].name);
489                 }
490         }
491
492         return ret;
493 }
494
495 int ast_module_reload(const char *name)
496 {
497         struct ast_module *cur;
498         int res = 0; /* return value. 0 = not found, others, see below */
499         int i;
500
501         if (ast_mutex_trylock(&reloadlock)) {
502                 ast_verbose("The previous reload command didn't finish yet\n");
503                 return -1;      /* reload already in progress */
504         }
505
506         /* Call "predefined" reload here first */
507         for (i = 0; reload_classes[i].name; i++) {
508                 if (!name || !strcasecmp(name, reload_classes[i].name)) {
509                         reload_classes[i].reload_fn();  /* XXX should check error ? */
510                         res = 2;        /* found and reloaded */
511                 }
512         }
513         ast_lastreloadtime = time(NULL);
514
515         if (name && res)
516                 return res;
517
518         AST_LIST_LOCK(&module_list);
519         AST_LIST_TRAVERSE(&module_list, cur, entry) {
520                 const struct ast_module_info *info = cur->info;
521
522                 if (name && resource_name_match(name, cur->resource))
523                         continue;
524
525                 if (!ast_test_flag(cur, FLAG_RUNNING | FLAG_DECLINED))
526                         continue;
527
528                 if (!info->reload) {    /* cannot be reloaded */
529                         if (res < 1)    /* store result if possible */
530                                 res = 1;        /* 1 = no reload() method */
531                         continue;
532                 }
533
534                 res = 2;
535                 if (option_verbose > 2) 
536                         ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", cur->resource, info->description);
537                 info->reload();
538         }
539         AST_LIST_UNLOCK(&module_list);
540
541         ast_mutex_unlock(&reloadlock);
542
543         return res;
544 }
545
546 static unsigned int inspect_module(const struct ast_module *mod)
547 {
548         if (!mod->info->description) {
549                 ast_log(LOG_WARNING, "Module '%s' does not provide a description.\n", mod->resource);
550                 return 1;
551         }
552
553         if (!mod->info->key) {
554                 ast_log(LOG_WARNING, "Module '%s' does not provide a license key.\n", mod->resource);
555                 return 1;
556         }
557
558         if (verify_key((unsigned char *) mod->info->key)) {
559                 ast_log(LOG_WARNING, "Module '%s' did not provide a valid license key.\n", mod->resource);
560                 return 1;
561         }
562
563         return 0;
564 }
565
566 static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only)
567 {
568         struct ast_module *mod;
569         enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS;
570         char tmp[256];
571
572         if ((mod = find_resource(resource_name, 0))) {
573                 if (ast_test_flag(mod, FLAG_RUNNING)) {
574                         ast_log(LOG_WARNING, "Module '%s' already exists.\n", resource_name);
575                         return AST_MODULE_LOAD_DECLINE;
576                 }
577                 if (global_symbols_only && !ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS))
578                         return AST_MODULE_LOAD_SKIP;
579 #if LOADABLE_MODULES
580         } else {
581                 if (!(mod = load_dynamic_module(resource_name, global_symbols_only))) {
582                         /* don't generate a warning message during load_modules() */
583                         if (!global_symbols_only) {
584                                 ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
585                                 return AST_MODULE_LOAD_DECLINE;
586                         } else {
587                                 return AST_MODULE_LOAD_SKIP;
588                         }
589                 }
590 #else
591                 return AST_MODULE_LOAD_DECLINE;
592 #endif
593         }
594
595         if (inspect_module(mod)) {
596                 ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
597 #if LOADABLE_MODULES
598                 unload_dynamic_module(mod);
599 #endif
600                 return AST_MODULE_LOAD_DECLINE;
601         }
602
603         ast_clear_flag(mod, FLAG_DECLINED);
604
605         if (mod->info->load)
606                 res = mod->info->load();
607
608         switch (res) {
609         case AST_MODULE_LOAD_SUCCESS:
610                 if (!ast_fully_booted) {
611                         if (option_verbose) 
612                                 ast_verbose("%s => (%s)\n", resource_name, term_color(tmp, mod->info->description, COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
613                         if (ast_opt_console && !option_verbose)
614                                 ast_verbose( ".");
615                 } else {
616                         if (option_verbose)
617                                 ast_verbose(VERBOSE_PREFIX_1 "Loaded %s => (%s)\n", resource_name, mod->info->description);
618                 }
619
620                 ast_set_flag(mod, FLAG_RUNNING);
621                 
622                 ast_update_use_count();
623                 break;
624         case AST_MODULE_LOAD_DECLINE:
625                 ast_set_flag(mod, FLAG_DECLINED);
626                 break;
627         case AST_MODULE_LOAD_FAILURE:
628                 break;
629         case AST_MODULE_LOAD_SKIP:
630                 /* modules should never return this value */
631                 break;
632         }
633
634         return res;
635 }
636
637 int ast_load_resource(const char *resource_name)
638 {
639        AST_LIST_LOCK(&module_list);
640        load_resource(resource_name, 0);
641        AST_LIST_UNLOCK(&module_list);
642
643        return 0;
644 }
645
646 struct load_order_entry {
647         char *resource;
648         unsigned int embedded;
649         AST_LIST_ENTRY(load_order_entry) entry;
650 };
651
652 AST_LIST_HEAD_NOLOCK(load_order, load_order_entry);
653
654 static struct load_order_entry *add_to_load_order(const char *resource, struct load_order *load_order)
655 {
656         struct load_order_entry *order;
657
658         AST_LIST_TRAVERSE(load_order, order, entry) {
659                 if (!resource_name_match(order->resource, resource))
660                         return NULL;
661         }
662
663         if (!(order = ast_calloc(1, sizeof(*order))))
664                 return NULL;
665
666         order->resource = ast_strdup(resource);
667         AST_LIST_INSERT_TAIL(load_order, order, entry);
668
669         return order;
670 }
671
672 int load_modules(unsigned int preload_only)
673 {
674         struct ast_config *cfg;
675         struct ast_module *mod;
676         struct load_order_entry *order;
677         struct ast_variable *v;
678         unsigned int load_count;
679         struct load_order load_order;
680         int res = 0;
681 #if LOADABLE_MODULES
682         struct dirent *dirent;
683         DIR *dir;
684 #endif
685
686         /* all embedded modules have registered themselves by now */
687         embedding = 0;
688
689         if (option_verbose)
690                 ast_verbose("Asterisk Dynamic Loader Starting:\n");
691
692         AST_LIST_TRAVERSE(&module_list, mod, entry) {
693                 if (option_debug > 1)
694                         ast_log(LOG_DEBUG, "Embedded module found: %s\n", mod->resource);
695         }
696
697         if (!(cfg = ast_config_load(AST_MODULE_CONFIG))) {
698                 ast_log(LOG_WARNING, "No '%s' found, no modules will be loaded.\n", AST_MODULE_CONFIG);
699                 return 0;
700         }
701
702         AST_LIST_HEAD_INIT_NOLOCK(&load_order);
703
704         if (preload_only) {
705                 /* first, find all the modules we have been explicitly requested to load */
706                 for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
707                         if (!strcasecmp(v->name, "preload"))
708                                 add_to_load_order(v->value, &load_order);
709                 }
710         } else {
711                 /* first, find all the modules we have been explicitly requested to load */
712                 for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
713                         if (!strcasecmp(v->name, "load"))
714                                 add_to_load_order(v->value, &load_order);
715                 }
716         }
717
718         /* check if 'autoload' is on */
719         if (!preload_only && ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
720                 /* if so, first add all the embedded modules to the load order */
721                 AST_LIST_TRAVERSE(&module_list, mod, entry) {
722                         order = add_to_load_order(mod->resource, &load_order);
723
724                         if (order)
725                                 order->embedded = 1;
726                 }
727
728                 /* if we are allowed to load dynamic modules, scan the directory for
729                    for all available modules and add them as well */
730 #if LOADABLE_MODULES
731                 if ((dir  = opendir(ast_config_AST_MODULE_DIR))) {
732                         while ((dirent = readdir(dir))) {
733                                 int ld = strlen(dirent->d_name);
734                                 
735                                 /* Must end in .so to load it.  */
736
737                                 if (ld < 4)
738                                         continue;
739
740                                 if (strcasecmp(dirent->d_name + ld - 3, ".so"))
741                                     continue;
742
743                                 add_to_load_order(dirent->d_name, &load_order);
744
745                         }
746
747                         closedir(dir);
748                 } else {
749                         if (!ast_opt_quiet)
750                                 ast_log(LOG_WARNING, "Unable to open modules directory '%s'.\n",
751                                         ast_config_AST_MODULE_DIR);
752                 }
753 #endif
754         }
755
756         /* now scan the config for any modules we are prohibited from loading and
757            remove them from the load order */
758         for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
759                 if (strcasecmp(v->name, "noload"))
760                         continue;
761
762                 AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
763                         if (!resource_name_match(order->resource, v->value)) {
764                                 AST_LIST_REMOVE_CURRENT(&load_order, entry);
765                                 free(order->resource);
766                                 free(order);
767                         }
768                 }
769                 AST_LIST_TRAVERSE_SAFE_END;
770         }
771
772         /* we are done with the config now, all the information we need is in the
773            load_order list */
774         ast_config_destroy(cfg);
775
776         load_count = 0;
777         AST_LIST_TRAVERSE(&load_order, order, entry)
778                 load_count++;
779
780         ast_log(LOG_NOTICE, "%d modules will be loaded.\n", load_count);
781
782         /* first, load only modules that provide global symbols */
783         AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
784                 switch (load_resource(order->resource, 1)) {
785                 case AST_MODULE_LOAD_SUCCESS:
786                 case AST_MODULE_LOAD_DECLINE:
787                         AST_LIST_REMOVE_CURRENT(&load_order, entry);
788                         free(order->resource);
789                         free(order);
790                         break;
791                 case AST_MODULE_LOAD_FAILURE:
792                         res = -1;
793                         goto done;
794                 case AST_MODULE_LOAD_SKIP:
795                         /* try again later */
796                         break;
797                 }
798         }
799         AST_LIST_TRAVERSE_SAFE_END;
800
801         /* now load everything else */
802         AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
803                 switch (load_resource(order->resource, 0)) {
804                 case AST_MODULE_LOAD_SUCCESS:
805                 case AST_MODULE_LOAD_DECLINE:
806                         AST_LIST_REMOVE_CURRENT(&load_order, entry);
807                         free(order->resource);
808                         free(order);
809                         break;
810                 case AST_MODULE_LOAD_FAILURE:
811                         res = -1;
812                         goto done;
813                 case AST_MODULE_LOAD_SKIP:
814                         /* should not happen */
815                         break;
816                 }
817         }
818         AST_LIST_TRAVERSE_SAFE_END;
819
820 done:
821         while ((order = AST_LIST_REMOVE_HEAD(&load_order, entry))) {
822                 free(order->resource);
823                 free(order);
824         }
825
826         return res;
827 }
828
829 void ast_update_use_count(void)
830 {
831         /* Notify any module monitors that the use count for a 
832            resource has changed */
833         struct loadupdate *m;
834
835         AST_LIST_LOCK(&module_list);
836         AST_LIST_TRAVERSE(&updaters, m, entry)
837                 m->updater();
838         AST_LIST_UNLOCK(&module_list);
839 }
840
841 int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),
842                            const char *like)
843 {
844         struct ast_module *cur;
845         int unlock = -1;
846         int total_mod_loaded = 0;
847
848         if (AST_LIST_TRYLOCK(&module_list))
849                 unlock = 0;
850
851         AST_LIST_TRAVERSE(&module_list, cur, entry) {
852                 total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, like);
853         }
854
855         if (unlock)
856                 AST_LIST_UNLOCK(&module_list);
857
858         return total_mod_loaded;
859 }
860
861 int ast_loader_register(int (*v)(void)) 
862 {
863         struct loadupdate *tmp; 
864
865         if (!(tmp = ast_malloc(sizeof(*tmp))))
866                 return -1;
867
868         tmp->updater = v;
869         AST_LIST_LOCK(&module_list);
870         AST_LIST_INSERT_HEAD(&updaters, tmp, entry);
871         AST_LIST_UNLOCK(&module_list);
872
873         return 0;
874 }
875
876 int ast_loader_unregister(int (*v)(void))
877 {
878         struct loadupdate *cur;
879
880         AST_LIST_LOCK(&module_list);
881         AST_LIST_TRAVERSE_SAFE_BEGIN(&updaters, cur, entry) {
882                 if (cur->updater == v)  {
883                         AST_LIST_REMOVE_CURRENT(&updaters, entry);
884                         break;
885                 }
886         }
887         AST_LIST_TRAVERSE_SAFE_END;
888         AST_LIST_UNLOCK(&module_list);
889
890         return cur ? 0 : -1;
891 }
892
893 struct ast_module *ast_module_ref(struct ast_module *mod)
894 {
895         ast_atomic_fetchadd_int(&mod->usecount, +1);
896         ast_update_use_count();
897
898         return mod;
899 }
900
901 void ast_module_unref(struct ast_module *mod)
902 {
903         ast_atomic_fetchadd_int(&mod->usecount, -1);
904         ast_update_use_count();
905 }