2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
7 * Kevin P. Fleming <kpfleming@digium.com>
8 * Luigi Rizzo <rizzo@icir.org>
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.
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.
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>
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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"
56 #include "asterisk/dlfcn-compat.h"
61 #include "asterisk/md5.h"
62 #include "asterisk/utils.h"
68 struct ast_module_user {
69 struct ast_channel *chan;
70 AST_LIST_ENTRY(ast_module_user) entry;
73 AST_LIST_HEAD(module_user_list, ast_module_user);
75 static unsigned char expected_key[] =
76 { 0x87, 0x76, 0x79, 0x35, 0x23, 0xea, 0x3a, 0xd3,
77 0x25, 0x2a, 0xbb, 0x35, 0x87, 0xe4, 0x22, 0x24 };
79 static unsigned int embedding = 1; /* we always start out by registering embedded modules,
80 since they are here before we dlopen() any
84 const struct ast_module_info *info;
85 void *lib; /* the shared lib, or NULL if embedded */
86 int usecount; /* the number of 'users' currently in this module */
87 struct module_user_list users; /* the list of users in the module */
89 unsigned int running:1;
90 unsigned int declined:1;
92 AST_LIST_ENTRY(ast_module) entry;
96 static AST_LIST_HEAD_STATIC(module_list, ast_module);
100 AST_LIST_ENTRY(loadupdate) entry;
103 static AST_LIST_HEAD_STATIC(updaters, loadupdate);
105 AST_MUTEX_DEFINE_STATIC(reloadlock);
107 /* when dynamic modules are being loaded, ast_module_register() will
108 need to know what filename the module was loaded from while it
111 struct ast_module *resource_being_loaded;
113 /* XXX: should we check for duplicate resource names here? */
115 void ast_module_register(const struct ast_module_info *info)
117 struct ast_module *mod;
120 if (!(mod = ast_calloc(1, sizeof(*mod) + strlen(info->name) + 1)))
122 strcpy(mod->resource, info->name);
124 mod = resource_being_loaded;
128 AST_LIST_HEAD_INIT(&mod->users);
130 /* during startup, before the loader has been initialized,
131 there are no threads, so there is no need to take the lock
132 on this list to manipulate it. it is also possible that it
133 might be unsafe to use the list lock at that point... so
134 let's avoid it altogether
137 AST_LIST_LOCK(&module_list);
139 /* it is paramount that the new entry be placed at the tail of
140 the list, otherwise the code that uses dlopen() to load
141 dynamic modules won't be able to find out if the module it
142 just opened was registered or failed to load
144 AST_LIST_INSERT_TAIL(&module_list, mod, entry);
147 AST_LIST_UNLOCK(&module_list);
149 /* give the module a copy of its own handle, for later use in registrations and the like */
150 *((struct ast_module **) &(info->self)) = mod;
153 void ast_module_unregister(const struct ast_module_info *info)
155 struct ast_module *mod = NULL;
157 /* it is assumed that the users list in the module structure
158 will already be empty, or we cannot have gotten to this
161 AST_LIST_LOCK(&module_list);
162 AST_LIST_TRAVERSE_SAFE_BEGIN(&module_list, mod, entry) {
163 if (mod->info == info) {
164 AST_LIST_REMOVE_CURRENT(&module_list, entry);
168 AST_LIST_TRAVERSE_SAFE_END;
169 AST_LIST_UNLOCK(&module_list);
172 AST_LIST_HEAD_DESTROY(&mod->users);
177 struct ast_module_user *__ast_module_user_add(struct ast_module *mod,
178 struct ast_channel *chan)
180 struct ast_module_user *u = ast_calloc(1, sizeof(*u));
187 AST_LIST_LOCK(&mod->users);
188 AST_LIST_INSERT_HEAD(&mod->users, u, entry);
189 AST_LIST_UNLOCK(&mod->users);
191 ast_atomic_fetchadd_int(&mod->usecount, +1);
193 ast_update_use_count();
198 void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u)
200 AST_LIST_LOCK(&mod->users);
201 AST_LIST_REMOVE(&mod->users, u, entry);
202 AST_LIST_UNLOCK(&mod->users);
203 ast_atomic_fetchadd_int(&mod->usecount, -1);
206 ast_update_use_count();
209 void __ast_module_user_hangup_all(struct ast_module *mod)
211 struct ast_module_user *u;
213 AST_LIST_LOCK(&mod->users);
214 while ((u = AST_LIST_REMOVE_HEAD(&mod->users, entry))) {
215 ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
216 ast_atomic_fetchadd_int(&mod->usecount, -1);
219 AST_LIST_UNLOCK(&mod->users);
221 ast_update_use_count();
225 * In addition to modules, the reload command handles some extra keywords
226 * which are listed here together with the corresponding handlers.
227 * This table is also used by the command completion code.
229 static struct reload_classes {
231 int (*reload_fn)(void);
232 } reload_classes[] = { /* list in alpha order, longest match first for cli completion */
233 { "cdr", ast_cdr_engine_reload },
234 { "dnsmgr", dnsmgr_reload },
235 { "extconfig", read_config_maps },
236 { "enum", ast_enum_reload },
237 { "manager", reload_manager },
238 { "rtp", ast_rtp_reload },
239 { "http", ast_http_reload },
243 static int printdigest(const unsigned char *d)
246 char buf[256]; /* large enough so we don't have to worry */
248 for (pos = 0, x = 0; x < 16; x++)
249 pos += sprintf(buf + pos, " %02x", *d++);
251 ast_debug(1, "Unexpected signature:%s\n", buf);
256 static int key_matches(const unsigned char *key1, const unsigned char *key2)
260 for (x = 0; x < 16; x++) {
261 if (key1[x] != key2[x])
268 static int verify_key(const unsigned char *key)
271 unsigned char digest[16];
274 MD5Update(&c, key, strlen((char *)key));
275 MD5Final(digest, &c);
277 if (key_matches(expected_key, digest))
285 static int resource_name_match(const char *name1_in, const char *name2_in)
287 char *name1 = (char *) name1_in;
288 char *name2 = (char *) name2_in;
290 /* trim off any .so extensions */
291 if (!strcasecmp(name1 + strlen(name1) - 3, ".so")) {
292 name1 = ast_strdupa(name1);
293 name1[strlen(name1) - 3] = '\0';
295 if (!strcasecmp(name2 + strlen(name2) - 3, ".so")) {
296 name2 = ast_strdupa(name2);
297 name2[strlen(name2) - 3] = '\0';
300 return strcasecmp(name1, name2);
303 static struct ast_module *find_resource(const char *resource, int do_lock)
305 struct ast_module *cur;
308 AST_LIST_LOCK(&module_list);
310 AST_LIST_TRAVERSE(&module_list, cur, entry) {
311 if (!resource_name_match(resource, cur->resource))
316 AST_LIST_UNLOCK(&module_list);
322 static void unload_dynamic_module(struct ast_module *mod)
324 void *lib = mod->lib;
326 /* WARNING: the structure pointed to by mod is going to
327 disappear when this operation succeeds, so we can't
331 while (!dlclose(lib));
334 static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only)
338 struct ast_module *mod;
339 char *resource = (char *) resource_in;
340 unsigned int wants_global;
342 if (strcasecmp(resource + strlen(resource) - 3, ".so")) {
343 resource = alloca(strlen(resource_in) + 3);
344 strcpy(resource, resource_in);
345 strcat(resource, ".so");
348 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_MODULE_DIR, resource);
350 /* make a first load of the module in 'quiet' mode... don't try to resolve
351 any symbols, and don't export any symbols. this will allow us to peek into
352 the module's info block (if available) to see what flags it has set */
354 if (!(resource_being_loaded = ast_calloc(1, sizeof(*resource_being_loaded) + strlen(resource) + 1)))
357 strcpy(resource_being_loaded->resource, resource);
359 if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_LOCAL))) {
360 ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
361 ast_free(resource_being_loaded);
365 /* the dlopen() succeeded, let's find out if the module
367 /* note that this will only work properly as long as
368 ast_module_register() (which is called by the module's
369 constructor) places the new module at the tail of the
372 if (resource_being_loaded != (mod = AST_LIST_LAST(&module_list))) {
373 ast_log(LOG_WARNING, "Module '%s' did not register itself during load\n", resource_in);
374 /* no, it did not, so close it and return */
375 while (!dlclose(lib));
376 /* note that the module's destructor will call ast_module_unregister(),
377 which will free the structure we allocated in resource_being_loaded */
381 wants_global = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS);
383 /* if we are being asked only to load modules that provide global symbols,
384 and this one does not, then close it and return */
385 if (global_symbols_only && !wants_global) {
386 while (!dlclose(lib));
390 /* if the system supports RTLD_NOLOAD, we can just 'promote' the flags
391 on the already-opened library to what we want... if not, we have to
392 close it and start over
394 #if defined(HAVE_RTLD_NOLOAD) && !defined(__Darwin__)
395 if (!dlopen(fn, RTLD_NOLOAD | (wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) {
396 ast_log(LOG_WARNING, "Unable to promote flags on module '%s': %s\n", resource_in, dlerror());
397 while (!dlclose(lib));
398 ast_free(resource_being_loaded);
402 while (!dlclose(lib));
403 resource_being_loaded = NULL;
405 /* start the load process again */
407 if (!(resource_being_loaded = ast_calloc(1, sizeof(*resource_being_loaded) + strlen(resource) + 1)))
410 strcpy(resource_being_loaded->resource, resource);
412 if (!(lib = dlopen(fn, wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) {
413 ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
414 ast_free(resource_being_loaded);
418 /* since the module was successfully opened, and it registered itself
419 the previous time we did that, we're going to assume it worked this
423 AST_LIST_LAST(&module_list)->lib = lib;
424 resource_being_loaded = NULL;
426 return AST_LIST_LAST(&module_list);
430 void ast_module_shutdown(void)
432 struct ast_module *mod;
433 AST_LIST_HEAD_NOLOCK_STATIC(local_module_list, ast_module);
435 /* We have to call the unload() callbacks in reverse order that the modules
436 * exist in the module list so it is the reverse order of how they were
439 AST_LIST_LOCK(&module_list);
440 while ((mod = AST_LIST_REMOVE_HEAD(&module_list, entry)))
441 AST_LIST_INSERT_HEAD(&local_module_list, mod, entry);
442 AST_LIST_UNLOCK(&module_list);
444 while ((mod = AST_LIST_REMOVE_HEAD(&local_module_list, entry))) {
445 if (mod->info->unload)
447 /* Since this should only be called when shutting down "gracefully",
448 * all channels should be down before we get to this point, meaning
449 * there will be no module users left. */
450 AST_LIST_HEAD_DESTROY(&mod->users);
455 int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode force)
457 struct ast_module *mod;
461 AST_LIST_LOCK(&module_list);
463 if (!(mod = find_resource(resource_name, 0))) {
464 AST_LIST_UNLOCK(&module_list);
468 if (!(mod->flags.running || mod->flags.declined))
471 if (!error && (mod->usecount > 0)) {
473 ast_log(LOG_WARNING, "Warning: Forcing removal of module '%s' with use count %d\n",
474 resource_name, mod->usecount);
476 ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name,
483 __ast_module_user_hangup_all(mod);
484 res = mod->info->unload();
487 ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
488 if (force <= AST_FORCE_FIRM)
491 ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
496 mod->flags.running = mod->flags.declined = 0;
498 AST_LIST_UNLOCK(&module_list);
500 if (!error && !mod->lib)
501 mod->info->restore_globals();
505 unload_dynamic_module(mod);
509 ast_update_use_count();
514 char *ast_module_helper(const char *line, const char *word, int pos, int state, int rpos, int needsreload)
516 struct ast_module *cur;
517 int i, which=0, l = strlen(word);
523 AST_LIST_LOCK(&module_list);
524 AST_LIST_TRAVERSE(&module_list, cur, entry) {
525 if (!strncasecmp(word, cur->resource, l) &&
526 (cur->info->reload || !needsreload) &&
528 ret = ast_strdup(cur->resource);
532 AST_LIST_UNLOCK(&module_list);
535 for (i=0; !ret && reload_classes[i].name; i++) {
536 if (!strncasecmp(word, reload_classes[i].name, l) && ++which > state)
537 ret = ast_strdup(reload_classes[i].name);
544 int ast_module_reload(const char *name)
546 struct ast_module *cur;
547 int res = 0; /* return value. 0 = not found, others, see below */
550 if (ast_mutex_trylock(&reloadlock)) {
551 ast_verbose("The previous reload command didn't finish yet\n");
552 return -1; /* reload already in progress */
554 ast_lastreloadtime = time(NULL);
556 /* Call "predefined" reload here first */
557 for (i = 0; reload_classes[i].name; i++) {
558 if (!name || !strcasecmp(name, reload_classes[i].name)) {
559 reload_classes[i].reload_fn(); /* XXX should check error ? */
560 res = 2; /* found and reloaded */
565 ast_mutex_unlock(&reloadlock);
569 AST_LIST_LOCK(&module_list);
570 AST_LIST_TRAVERSE(&module_list, cur, entry) {
571 const struct ast_module_info *info = cur->info;
573 if (name && resource_name_match(name, cur->resource))
576 if (!(cur->flags.running || cur->flags.declined))
579 if (!info->reload) { /* cannot be reloaded */
580 if (res < 1) /* store result if possible */
581 res = 1; /* 1 = no reload() method */
586 if (option_verbose > 2)
587 ast_verbose(VERBOSE_PREFIX_3 "Reloading module '%s' (%s)\n", cur->resource, info->description);
590 AST_LIST_UNLOCK(&module_list);
592 ast_mutex_unlock(&reloadlock);
597 static unsigned int inspect_module(const struct ast_module *mod)
599 if (!mod->info->description) {
600 ast_log(LOG_WARNING, "Module '%s' does not provide a description.\n", mod->resource);
604 if (!mod->info->key) {
605 ast_log(LOG_WARNING, "Module '%s' does not provide a license key.\n", mod->resource);
609 if (verify_key((unsigned char *) mod->info->key)) {
610 ast_log(LOG_WARNING, "Module '%s' did not provide a valid license key.\n", mod->resource);
617 static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only)
619 struct ast_module *mod;
620 enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS;
623 if ((mod = find_resource(resource_name, 0))) {
624 if (mod->flags.running) {
625 ast_log(LOG_WARNING, "Module '%s' already exists.\n", resource_name);
626 return AST_MODULE_LOAD_DECLINE;
628 if (global_symbols_only && !ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS))
629 return AST_MODULE_LOAD_SKIP;
632 if (!(mod = load_dynamic_module(resource_name, global_symbols_only))) {
633 /* don't generate a warning message during load_modules() */
634 if (!global_symbols_only) {
635 ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
636 return AST_MODULE_LOAD_DECLINE;
638 return AST_MODULE_LOAD_SKIP;
642 ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
643 return AST_MODULE_LOAD_DECLINE;
647 if (inspect_module(mod)) {
648 ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
650 unload_dynamic_module(mod);
652 return AST_MODULE_LOAD_DECLINE;
655 if (!mod->lib && mod->info->backup_globals()) {
656 ast_log(LOG_WARNING, "Module '%s' was unable to backup its global data.\n", resource_name);
657 return AST_MODULE_LOAD_DECLINE;
660 mod->flags.declined = 0;
663 res = mod->info->load();
666 case AST_MODULE_LOAD_SUCCESS:
667 if (!ast_fully_booted) {
669 ast_verbose("%s => (%s)\n", resource_name, term_color(tmp, mod->info->description, COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
670 if (ast_opt_console && !option_verbose)
674 ast_verbose(VERBOSE_PREFIX_1 "Loaded %s => (%s)\n", resource_name, mod->info->description);
677 mod->flags.running = 1;
679 ast_update_use_count();
681 case AST_MODULE_LOAD_DECLINE:
682 mod->flags.declined = 1;
684 case AST_MODULE_LOAD_FAILURE:
686 case AST_MODULE_LOAD_SKIP:
687 /* modules should never return this value */
694 int ast_load_resource(const char *resource_name)
696 AST_LIST_LOCK(&module_list);
697 load_resource(resource_name, 0);
698 AST_LIST_UNLOCK(&module_list);
703 struct load_order_entry {
705 AST_LIST_ENTRY(load_order_entry) entry;
708 AST_LIST_HEAD_NOLOCK(load_order, load_order_entry);
710 static struct load_order_entry *add_to_load_order(const char *resource, struct load_order *load_order)
712 struct load_order_entry *order;
714 AST_LIST_TRAVERSE(load_order, order, entry) {
715 if (!resource_name_match(order->resource, resource))
719 if (!(order = ast_calloc(1, sizeof(*order))))
722 order->resource = ast_strdup(resource);
723 AST_LIST_INSERT_TAIL(load_order, order, entry);
728 int load_modules(unsigned int preload_only)
730 struct ast_config *cfg;
731 struct ast_module *mod;
732 struct load_order_entry *order;
733 struct ast_variable *v;
734 unsigned int load_count;
735 struct load_order load_order;
738 struct dirent *dirent;
742 /* all embedded modules have registered themselves by now */
746 ast_verbose("Asterisk Dynamic Loader Starting:\n");
748 AST_LIST_HEAD_INIT_NOLOCK(&load_order);
750 AST_LIST_LOCK(&module_list);
752 if (!(cfg = ast_config_load(AST_MODULE_CONFIG))) {
753 ast_log(LOG_WARNING, "No '%s' found, no modules will be loaded.\n", AST_MODULE_CONFIG);
757 /* first, find all the modules we have been explicitly requested to load */
758 for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
759 if (!strcasecmp(v->name, preload_only ? "preload" : "load"))
760 add_to_load_order(v->value, &load_order);
763 /* check if 'autoload' is on */
764 if (!preload_only && ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
765 /* if so, first add all the embedded modules that are not already running to the load order */
766 AST_LIST_TRAVERSE(&module_list, mod, entry) {
767 /* if it's not embedded, skip it */
771 if (mod->flags.running)
774 order = add_to_load_order(mod->resource, &load_order);
778 /* if we are allowed to load dynamic modules, scan the directory for
779 for all available modules and add them as well */
780 if ((dir = opendir(ast_config_AST_MODULE_DIR))) {
781 while ((dirent = readdir(dir))) {
782 int ld = strlen(dirent->d_name);
784 /* Must end in .so to load it. */
789 if (strcasecmp(dirent->d_name + ld - 3, ".so"))
792 /* if there is already a module by this name in the module_list,
794 if (find_resource(dirent->d_name, 0))
797 add_to_load_order(dirent->d_name, &load_order);
803 ast_log(LOG_WARNING, "Unable to open modules directory '%s'.\n",
804 ast_config_AST_MODULE_DIR);
809 /* now scan the config for any modules we are prohibited from loading and
810 remove them from the load order */
811 for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
812 if (strcasecmp(v->name, "noload"))
815 AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
816 if (!resource_name_match(order->resource, v->value)) {
817 AST_LIST_REMOVE_CURRENT(&load_order, entry);
818 ast_free(order->resource);
822 AST_LIST_TRAVERSE_SAFE_END;
825 /* we are done with the config now, all the information we need is in the
827 ast_config_destroy(cfg);
830 AST_LIST_TRAVERSE(&load_order, order, entry)
834 ast_log(LOG_NOTICE, "%d modules will be loaded.\n", load_count);
836 /* first, load only modules that provide global symbols */
837 AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
838 switch (load_resource(order->resource, 1)) {
839 case AST_MODULE_LOAD_SUCCESS:
840 case AST_MODULE_LOAD_DECLINE:
841 AST_LIST_REMOVE_CURRENT(&load_order, entry);
842 ast_free(order->resource);
845 case AST_MODULE_LOAD_FAILURE:
848 case AST_MODULE_LOAD_SKIP:
849 /* try again later */
853 AST_LIST_TRAVERSE_SAFE_END;
855 /* now load everything else */
856 AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
857 switch (load_resource(order->resource, 0)) {
858 case AST_MODULE_LOAD_SUCCESS:
859 case AST_MODULE_LOAD_DECLINE:
860 AST_LIST_REMOVE_CURRENT(&load_order, entry);
861 ast_free(order->resource);
864 case AST_MODULE_LOAD_FAILURE:
867 case AST_MODULE_LOAD_SKIP:
868 /* should not happen */
872 AST_LIST_TRAVERSE_SAFE_END;
875 while ((order = AST_LIST_REMOVE_HEAD(&load_order, entry))) {
876 ast_free(order->resource);
880 AST_LIST_UNLOCK(&module_list);
885 void ast_update_use_count(void)
887 /* Notify any module monitors that the use count for a
888 resource has changed */
889 struct loadupdate *m;
891 AST_LIST_LOCK(&module_list);
892 AST_LIST_TRAVERSE(&updaters, m, entry)
894 AST_LIST_UNLOCK(&module_list);
897 int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),
900 struct ast_module *cur;
902 int total_mod_loaded = 0;
904 if (AST_LIST_TRYLOCK(&module_list))
907 AST_LIST_TRAVERSE(&module_list, cur, entry) {
908 total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, like);
912 AST_LIST_UNLOCK(&module_list);
914 return total_mod_loaded;
917 /*! \brief Check if module exists */
918 int ast_module_check(const char *name)
920 struct ast_module *cur;
922 if (ast_strlen_zero(name))
923 return 0; /* FALSE */
925 cur = find_resource(name, 1);
927 return (cur != NULL);
931 int ast_loader_register(int (*v)(void))
933 struct loadupdate *tmp;
935 if (!(tmp = ast_malloc(sizeof(*tmp))))
939 AST_LIST_LOCK(&module_list);
940 AST_LIST_INSERT_HEAD(&updaters, tmp, entry);
941 AST_LIST_UNLOCK(&module_list);
946 int ast_loader_unregister(int (*v)(void))
948 struct loadupdate *cur;
950 AST_LIST_LOCK(&module_list);
951 AST_LIST_TRAVERSE_SAFE_BEGIN(&updaters, cur, entry) {
952 if (cur->updater == v) {
953 AST_LIST_REMOVE_CURRENT(&updaters, entry);
957 AST_LIST_TRAVERSE_SAFE_END;
958 AST_LIST_UNLOCK(&module_list);
963 struct ast_module *ast_module_ref(struct ast_module *mod)
965 ast_atomic_fetchadd_int(&mod->usecount, +1);
966 ast_update_use_count();
971 void ast_module_unref(struct ast_module *mod)
973 ast_atomic_fetchadd_int(&mod->usecount, -1);
974 ast_update_use_count();