Skip to content
Snippets Groups Projects
loader.c 55.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Asterisk -- An open source telephony toolkit.
     *
     * Copyright (C) 1999 - 2006, Digium, Inc.
     *
     * Mark Spencer <markster@digium.com>
     * Kevin P. Fleming <kpfleming@digium.com>
     * Luigi Rizzo <rizzo@icir.org>
     *
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    /*! \file
     *
     * \brief Module Loader
    
     * \author Mark Spencer <markster@digium.com>
    
     * \author Kevin P. Fleming <kpfleming@digium.com>
     * \author Luigi Rizzo <rizzo@icir.org>
     * - See ModMngMnt
     */
    
    
    /*** MODULEINFO
    	<support_level>core</support_level>
     ***/
    
    
    #include "asterisk/_private.h"
    
    #include "asterisk/paths.h"	/* use ast_config_AST_MODULE_DIR */
    
    #include <editline/readline.h>
    
    #include "asterisk/dlinkedlists.h"
    
    #include "asterisk/module.h"
    #include "asterisk/config.h"
    #include "asterisk/channel.h"
    #include "asterisk/term.h"
    
    #include "asterisk/manager.h"
    #include "asterisk/cdr.h"
    #include "asterisk/enum.h"
    #include "asterisk/http.h"
    #include "asterisk/lock.h"
    
    Matthew Jordan's avatar
    Matthew Jordan committed
    #include "asterisk/features_config.h"
    
    #include "asterisk/vector.h"
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    #include "asterisk/test.h"
    
    #include "asterisk/sounds_index.h"
    
    
    #include <dlfcn.h>
    
    #include "asterisk/md5.h"
    #include "asterisk/utils.h"
    
    
    	<managerEvent language="en_US" name="Reload">
    		<managerEventInstance class="EVENT_FLAG_SYSTEM">
    			<synopsis>Raised when a module has been reloaded in Asterisk.</synopsis>
    			<syntax>
    				<parameter name="Module">
    					<para>The name of the module that was reloaded, or
    					<literal>All</literal> if all modules were reloaded</para>
    				</parameter>
    				<parameter name="Status">
    					<para>The numeric status code denoting the success or failure
    					of the reload request.</para>
    					<enumlist>
    						<enum name="0"><para>Success</para></enum>
    						<enum name="1"><para>Request queued</para></enum>
    						<enum name="2"><para>Module not found</para></enum>
    						<enum name="3"><para>Error</para></enum>
    						<enum name="4"><para>Reload already in progress</para></enum>
    						<enum name="5"><para>Module uninitialized</para></enum>
    						<enum name="6"><para>Reload not supported</para></enum>
    					</enumlist>
    				</parameter>
    			</syntax>
    		</managerEventInstance>
    	</managerEvent>
    
    #ifndef RTLD_LOCAL
    #define RTLD_LOCAL 0
    #endif
    
    
    struct ast_module_user {
    	struct ast_channel *chan;
    	AST_LIST_ENTRY(ast_module_user) entry;
    };
    
    
    AST_DLLIST_HEAD(module_user_list, ast_module_user);
    
    static const unsigned char expected_key[] =
    
    { 0x87, 0x76, 0x79, 0x35, 0x23, 0xea, 0x3a, 0xd3,
      0x25, 0x2a, 0xbb, 0x35, 0x87, 0xe4, 0x22, 0x24 };
    
    
    static char buildopt_sum[33] = AST_BUILDOPT_SUM;
    
    AST_VECTOR(module_vector, struct ast_module *);
    
    
    /* Built-in module registrations need special handling at startup */
    static unsigned int loader_ready;
    
    
    /*!
     * \brief Internal flag to indicate all modules have been initially loaded.
     */
    static int modules_loaded;
    
    
    struct ast_module {
    	const struct ast_module_info *info;
    
    	/*! Used to get module references into refs log */
    
    	/*! The shared lib. */
    	void *lib;
    	/*! Number of 'users' and other references currently holding the module. */
    	int usecount;
    	/*! List of users holding the module. */
    	struct module_user_list users;
    
    
    	/*! List of required module names. */
    	struct ast_vector_string requires;
    	/*! List of optional api modules. */
    	struct ast_vector_string optional_modules;
    	/*! List of modules this enhances. */
    	struct ast_vector_string enhances;
    
    	/*!
    	 * \brief Vector holding pointers to modules we have a reference to.
    	 *
    	 * When one module requires another, the required module gets added
    	 * to this list with a reference.
    	 */
    	struct module_vector reffed_deps;
    
    		/*! The module running and ready to accept requests. */
    
    		/*! The module has declined to start. */
    
    		/*! This module is being held open until it's time to shutdown. */
    
    		unsigned int keepuntilshutdown:1;
    
    		/*! The module is built-in. */
    		unsigned int builtin:1;
    
    	AST_DLLIST_ENTRY(ast_module) entry;
    
    static AST_DLLIST_HEAD_STATIC(module_list, ast_module);
    
    /*
     * module_list is cleared by its constructor possibly after
     * we start accumulating built-in modules, so we need to
     * use another list (without the lock) to accumulate them.
     */
    static struct module_list builtin_module_list;
    
    
    static int module_vector_strcasecmp(struct ast_module *a, struct ast_module *b)
    {
    	return strcasecmp(a->resource, b->resource);
    }
    
    
    static int module_vector_cmp(struct ast_module *a, struct ast_module *b)
    {
    	/* if load_pri is not set, default is 128.  Lower is better */
    	int a_pri = ast_test_flag(a->info, AST_MODFLAG_LOAD_ORDER)
    		? a->info->load_pri : AST_MODPRI_DEFAULT;
    	int b_pri = ast_test_flag(b->info, AST_MODFLAG_LOAD_ORDER)
    		? b->info->load_pri : AST_MODPRI_DEFAULT;
    
    	/*
    	 * Returns comparison values for a vector sorted by priority.
    	 * <0 a_pri < b_pri
    	 * =0 a_pri == b_pri
    	 * >0 a_pri > b_pri
    	 */
    	return a_pri - b_pri;
    }
    
    
    static struct ast_module *find_resource(const char *resource, int do_lock);
    
    /*!
     * \internal
     * \brief Add a reference from mod to dep.
     *
     * \param mod Owner of the new reference.
     * \param dep Module to reference
     * \param missing Vector to store name of \a dep if it is not running.
     *
     * This function returns failure if \a dep is not running and \a missing
     * is NULL.  If \a missing is not NULL errors will only be returned for
     * allocation failures.
     *
     * \retval 0 Success
     * \retval -1 Failure
     *
     * \note Adding a second reference to the same dep will return success
     *       without doing anything.
     */
    static int module_reffed_deps_add(struct ast_module *mod, struct ast_module *dep,
    	struct ast_vector_const_string *missing)
    {
    	if (!dep->flags.running) {
    		return !missing ? -1 : AST_VECTOR_APPEND(missing, dep->info->name);
    	}
    
    	if (AST_VECTOR_GET_CMP(&mod->reffed_deps, dep, AST_VECTOR_ELEM_DEFAULT_CMP)) {
    		/* Skip duplicate. */
    		return 0;
    	}
    
    	if (AST_VECTOR_APPEND(&mod->reffed_deps, dep)) {
    		return -1;
    	}
    
    	ast_module_ref(dep);
    
    	return 0;
    }
    
    /*!
     * \internal
     * \brief Add references for modules that enhance a dependency.
     *
     * \param mod Owner of the new references.
     * \param dep Module to check for enhancers.
     * \param missing Vector to store name of any enhancer that is not running or declined.
     *
     * \retval 0 Success
     * \retval -1 Failure
     */
    static int module_reffed_deps_add_dep_enhancers(struct ast_module *mod,
    	struct ast_module *dep, struct ast_vector_const_string *missing)
    {
    	struct ast_module *cur;
    
    	AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
    		if (cur->flags.declined) {
    			continue;
    		}
    
    		if (!AST_VECTOR_GET_CMP(&cur->enhances, dep->info->name, !strcasecmp)) {
    			/* dep is not enhanced by cur. */
    			continue;
    		}
    
    		/* dep is enhanced by cur, therefore mod requires cur. */
    		if (module_reffed_deps_add(mod, cur, missing)) {
    			return -1;
    		}
    	}
    
    	return 0;
    }
    
    /*!
     * \internal
     * \brief Add references to a list of dependencies.
     *
     * \param mod Owner of the new references.
     * \param vec List of required modules to process
     * \param missing Vector to store names of modules that are not running.
     * \param ref_enhancers Reference all enhancers of each required module.
     * \param isoptional Modules that are not loaded can be ignored.
     *
     * \retval 0 Success
     * \retval -1 Failure
     */
    static int module_deps_process_reqlist(struct ast_module *mod,
    	struct ast_vector_string *vec, struct ast_vector_const_string *missing,
    	int ref_enhancers, int isoptional)
    {
    	int idx;
    
    	for (idx = 0; idx < AST_VECTOR_SIZE(vec); idx++) {
    		const char *depname = AST_VECTOR_GET(vec, idx);
    		struct ast_module *dep = find_resource(depname, 0);
    
    		if (!dep || !dep->flags.running) {
    			if (isoptional && !dep) {
    				continue;
    			}
    
    			if (missing && !AST_VECTOR_APPEND(missing, depname)) {
    				continue;
    			}
    
    			return -1;
    		}
    
    		if (module_reffed_deps_add(mod, dep, missing)) {
    			return -1;
    		}
    
    		if (ref_enhancers && module_reffed_deps_add_dep_enhancers(mod, dep, missing)) {
    			return -1;
    		}
    	}
    
    	return 0;
    }
    
    /*!
     * \internal
     * \brief Grab all references required to start the module.
     *
     * \param mod The module we're trying to start.
     * \param missing Vector to store a list of missing dependencies.
     *
     * \retval 0 Success
     * \retval -1 Failure
     *
     * \note module_list must be locked.
     *
     * \note Caller is responsible for initializing and freeing the vector.
     *       Elements are safely read only while module_list remains locked.
     */
    static int module_deps_reference(struct ast_module *mod, struct ast_vector_const_string *missing)
    {
    	int res = 0;
    
    	/* Grab references to modules we enhance but not other enhancements. */
    	res |= module_deps_process_reqlist(mod, &mod->enhances, missing, 0, 0);
    
    	/* Grab references to modules we require plus enhancements. */
    	res |= module_deps_process_reqlist(mod, &mod->requires, missing, 1, 0);
    
    	/* Grab references to optional modules including enhancements. */
    	res |= module_deps_process_reqlist(mod, &mod->optional_modules, missing, 1, 1);
    
    	return res;
    }
    
    /*!
     * \brief Recursively find required dependencies that are not running.
     *
     * \param mod Module to scan for dependencies.
     * \param missingdeps Vector listing modules that must be started first.
     *
     * \retval 0 All dependencies resolved.
     * \retval -1 Failed to resolve some dependencies.
     *
     * An error from this function usually means a required module is not even
     * loaded.  This function is safe from infinite recursion, but dependency
     * loops are not reported as an error from here.  On success missingdeps
     * will contain a list of every module that needs to be running before this
     * module can start.  missingdeps is sorted by load priority so any missing
     * dependencies can be started if needed.
     */
    static int module_deps_missing_recursive(struct ast_module *mod, struct module_vector *missingdeps)
    {
    	int i = 0;
    	int res = -1;
    	struct ast_vector_const_string localdeps;
    	struct ast_module *dep;
    
    	/*
    	 * localdeps stores a copy of all dependencies that mod could not reference.
    	 * First we discard modules that we've already found. We add all newly found
    	 * modules to the missingdeps vector then scan them recursively.  This will
    	 * ensure we quickly run out of stuff to do.
    	 */
    	AST_VECTOR_INIT(&localdeps, 0);
    	if (module_deps_reference(mod, &localdeps)) {
    		goto clean_return;
    	}
    
    	while (i < AST_VECTOR_SIZE(&localdeps)) {
    		dep = find_resource(AST_VECTOR_GET(&localdeps, i), 0);
    		if (!dep) {
    			goto clean_return;
    		}
    
    		if (AST_VECTOR_GET_CMP(missingdeps, dep, AST_VECTOR_ELEM_DEFAULT_CMP)) {
    			/* Skip common dependency.  We have already searched it. */
    			AST_VECTOR_REMOVE(&localdeps, i, 0);
    		} else {
    			/* missingdeps is the real list so keep it sorted. */
    			if (AST_VECTOR_ADD_SORTED(missingdeps, dep, module_vector_cmp)) {
    				goto clean_return;
    			}
    			i++;
    		}
    	}
    
    	res = 0;
    	for (i = 0; !res && i < AST_VECTOR_SIZE(&localdeps); i++) {
    		dep = find_resource(AST_VECTOR_GET(&localdeps, i), 0);
    		/* We've already confirmed dep is loaded in the first loop. */
    		res = module_deps_missing_recursive(dep, missingdeps);
    	}
    
    clean_return:
    	AST_VECTOR_FREE(&localdeps);
    
    	return res;
    }
    
    
    const char *ast_module_name(const struct ast_module *mod)
    {
    	if (!mod || !mod->info) {
    		return NULL;
    	}
    
    	return mod->info->name;
    }
    
    
    struct loadupdate {
    	int (*updater)(void);
    	AST_LIST_ENTRY(loadupdate) entry;
    };
    
    
    static AST_DLLIST_HEAD_STATIC(updaters, loadupdate);
    
    
    AST_MUTEX_DEFINE_STATIC(reloadlock);
    
    
    struct reload_queue_item {
    	AST_LIST_ENTRY(reload_queue_item) entry;
    	char module[0];
    };
    
    static int do_full_reload = 0;
    
    
    static AST_DLLIST_HEAD_STATIC(reload_queue, reload_queue_item);
    
    /*!
     * \internal
     *
     * This variable is set by load_dynamic_module so ast_module_register
     * can know what pointer is being registered.
     *
     * This is protected by the module_list lock.
     */
    
    static struct ast_module * volatile resource_being_loaded;
    
    /*!
     * \internal
     * \brief Used by AST_MODULE_INFO to register with the module loader.
     *
     * This function is automatically called when each module is opened.
     * It must never be used from outside AST_MODULE_INFO.
     */
    
    void ast_module_register(const struct ast_module_info *info)
    {
    
    	struct ast_module *mod;
    
    
    	if (!loader_ready) {
    		mod = ast_calloc(1, sizeof(*mod) + strlen(info->name) + 1);
    		if (!mod) {
    			/* We haven't even reached main() yet, if we can't
    			 * allocate memory at this point just give up. */
    			exit(2);
    		}
    		strcpy(mod->resource, info->name); /* safe */
    		mod->info = info;
    		mod->flags.builtin = 1;
    		AST_DLLIST_INSERT_TAIL(&builtin_module_list, mod, entry);
    
    		/* ast_module_register for built-in modules is run again during module preload. */
    		return;
    	}
    
    
    	/*
    	 * This lock protects resource_being_loaded as well as the module
    	 * list.  Normally we already have a lock on module_list when we
    	 * begin the load but locking again from here prevents corruption
    	 * if an asterisk module is dlopen'ed from outside the module loader.
    	 */
    	AST_DLLIST_LOCK(&module_list);
    	mod = resource_being_loaded;
    	if (!mod) {
    		AST_DLLIST_UNLOCK(&module_list);
    		return;
    	}
    
    	ast_debug(5, "Registering module %s\n", info->name);
    
    	/* This tells load_dynamic_module that we're registered. */
    	resource_being_loaded = NULL;
    
    
    	if (ast_opt_ref_debug) {
    		mod->ref_debug = ao2_t_alloc(0, NULL, info->name);
    	}
    
    	AST_LIST_HEAD_INIT(&mod->users);
    
    	AST_VECTOR_INIT(&mod->requires, 0);
    	AST_VECTOR_INIT(&mod->optional_modules, 0);
    	AST_VECTOR_INIT(&mod->enhances, 0);
    	AST_VECTOR_INIT(&mod->reffed_deps, 0);
    
    	AST_DLLIST_INSERT_TAIL(&module_list, mod, entry);
    	AST_DLLIST_UNLOCK(&module_list);
    
    
    	/* give the module a copy of its own handle, for later use in registrations and the like */
    	*((struct ast_module **) &(info->self)) = mod;
    }
    
    
    static void module_destroy(struct ast_module *mod)
    {
    
    	AST_VECTOR_CALLBACK_VOID(&mod->requires, ast_free);
    	AST_VECTOR_FREE(&mod->requires);
    
    	AST_VECTOR_CALLBACK_VOID(&mod->optional_modules, ast_free);
    	AST_VECTOR_FREE(&mod->optional_modules);
    
    	AST_VECTOR_CALLBACK_VOID(&mod->enhances, ast_free);
    	AST_VECTOR_FREE(&mod->enhances);
    
    	/* Release references to all dependencies. */
    	AST_VECTOR_CALLBACK_VOID(&mod->reffed_deps, ast_module_unref);
    	AST_VECTOR_FREE(&mod->reffed_deps);
    
    
    	AST_LIST_HEAD_DESTROY(&mod->users);
    	ao2_cleanup(mod->ref_debug);
    	ast_free(mod);
    }
    
    
    void ast_module_unregister(const struct ast_module_info *info)
    {
    	struct ast_module *mod = NULL;
    
    	/* it is assumed that the users list in the module structure
    	   will already be empty, or we cannot have gotten to this
    	   point
    	*/
    
    	AST_DLLIST_LOCK(&module_list);
    	AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN(&module_list, mod, entry) {
    
    			AST_DLLIST_REMOVE_CURRENT(entry);
    
    	AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END;
    	AST_DLLIST_UNLOCK(&module_list);
    
    	if (mod && !mod->usecount) {
    		/*
    		 * We are intentionally leaking mod if usecount is not zero.
    		 * This is necessary if the module is being forcefully unloaded.
    		 * In addition module_destroy is not safe to run after exit()
    		 * is called.  ast_module_unregister is run during cleanup of
    		 * the process when libc releases each module's shared object
    		 * library.
    		 */
    
    		ast_debug(5, "Unregistering module %s\n", info->name);
    
    		module_destroy(mod);
    
    struct ast_module_user *__ast_module_user_add(struct ast_module *mod, struct ast_channel *chan)
    
    	struct ast_module_user *u;
    
    	u = ast_calloc(1, sizeof(*u));
    	if (!u) {
    
    
    	u->chan = chan;
    
    	AST_LIST_LOCK(&mod->users);
    	AST_LIST_INSERT_HEAD(&mod->users, u, entry);
    	AST_LIST_UNLOCK(&mod->users);
    
    
    	if (mod->ref_debug) {
    		ao2_ref(mod->ref_debug, +1);
    	}
    
    	ast_atomic_fetchadd_int(&mod->usecount, +1);
    
    	ast_update_use_count();
    
    	return u;
    }
    
    void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u)
    {
    
    	AST_LIST_LOCK(&mod->users);
    
    	u = AST_LIST_REMOVE(&mod->users, u, entry);
    
    	AST_LIST_UNLOCK(&mod->users);
    
    	if (!u) {
    		/*
    		 * Was not in the list.  Either a bad pointer or
    		 * __ast_module_user_hangup_all() has been called.
    		 */
    		return;
    	}
    
    
    	if (mod->ref_debug) {
    		ao2_ref(mod->ref_debug, -1);
    	}
    
    	ast_atomic_fetchadd_int(&mod->usecount, -1);
    
    
    	ast_update_use_count();
    }
    
    void __ast_module_user_hangup_all(struct ast_module *mod)
    {
    	struct ast_module_user *u;
    
    	AST_LIST_LOCK(&mod->users);
    	while ((u = AST_LIST_REMOVE_HEAD(&mod->users, entry))) {
    
    		if (u->chan) {
    			ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
    		}
    
    		if (mod->ref_debug) {
    			ao2_ref(mod->ref_debug, -1);
    		}
    
    		ast_atomic_fetchadd_int(&mod->usecount, -1);
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	ast_update_use_count();
    
    }
    
    /*! \note
     * In addition to modules, the reload command handles some extra keywords
     * which are listed here together with the corresponding handlers.
     * This table is also used by the command completion code.
     */
    static struct reload_classes {
    	const char *name;
    	int (*reload_fn)(void);
    } reload_classes[] = {	/* list in alpha order, longest match first for cli completion */
    
    	{ "acl",         ast_named_acl_reload },
    	{ "cdr",         ast_cdr_engine_reload },
    	{ "cel",         ast_cel_engine_reload },
    	{ "dnsmgr",      dnsmgr_reload },
    	{ "dsp",         ast_dsp_reload},
    	{ "extconfig",   read_config_maps },
    	{ "enum",        ast_enum_reload },
    	{ "features",    ast_features_config_reload },
    	{ "http",        ast_http_reload },
    
    	{ "indications", ast_indications_reload },
    
    	{ "logger",      logger_reload },
    	{ "manager",     reload_manager },
    	{ "plc",         ast_plc_reload },
    	{ "sounds",      ast_sounds_reindex },
    	{ "udptl",       ast_udptl_reload },
    	{ NULL,          NULL }
    
    };
    
    static int printdigest(const unsigned char *d)
    {
    	int x, pos;
    	char buf[256]; /* large enough so we don't have to worry */
    
    	for (pos = 0, x = 0; x < 16; x++)
    
    		pos += sprintf(buf + pos, " %02hhx", *d++);
    
    	ast_debug(1, "Unexpected signature:%s\n", buf);
    
    
    	return 0;
    }
    
    static int key_matches(const unsigned char *key1, const unsigned char *key2)
    {
    	int x;
    
    	for (x = 0; x < 16; x++) {
    		if (key1[x] != key2[x])
    			return 0;
    	}
    
    	return 1;
    }
    
    static int verify_key(const unsigned char *key)
    {
    	struct MD5Context c;
    	unsigned char digest[16];
    
    	MD5Init(&c);
    	MD5Update(&c, key, strlen((char *)key));
    	MD5Final(digest, &c);
    
    	if (key_matches(expected_key, digest))
    		return 0;
    
    	printdigest(digest);
    
    	return -1;
    }
    
    
    static size_t resource_name_baselen(const char *name)
    
    	size_t len = strlen(name);
    
    	if (len > 3 && !strcasecmp(name + len - 3, ".so")) {
    		return len - 3;
    
    
    	return len;
    }
    
    static int resource_name_match(const char *name1, size_t baselen1, const char *name2)
    {
    	if (baselen1 != resource_name_baselen(name2)) {
    		return -1;
    
    	return strncasecmp(name1, name2, baselen1);
    
    }
    
    static struct ast_module *find_resource(const char *resource, int do_lock)
    {
    	struct ast_module *cur;
    
    	size_t resource_baselen = resource_name_baselen(resource);
    
    	if (do_lock) {
    		AST_DLLIST_LOCK(&module_list);
    	}
    
    	AST_DLLIST_TRAVERSE(&module_list, cur, entry) {
    
    		if (!resource_name_match(resource, resource_baselen, cur->resource)) {
    
    	if (do_lock) {
    		AST_DLLIST_UNLOCK(&module_list);
    	}
    
    /*!
     * \brief dlclose(), with failure logging.
     */
    static void logged_dlclose(const char *name, void *lib)
    {
    
    	char *error;
    
    	if (!lib) {
    		return;
    	}
    
    	/* Clear any existing error */
    	dlerror();
    	if (dlclose(lib)) {
    		error = dlerror();
    		ast_log(AST_LOG_ERROR, "Failure in dlclose for module '%s': %s\n",
    			S_OR(name, "unknown"), S_OR(error, "Unknown error"));
    
    	}
    }
    
    #if defined(HAVE_RTLD_NOLOAD)
    /*!
     * \brief Check to see if the given resource is loaded.
     *
     * \param resource_name Name of the resource, including .so suffix.
     * \return False (0) if module is not loaded.
     * \return True (non-zero) if module is loaded.
     */
    static int is_module_loaded(const char *resource_name)
    {
    	char fn[PATH_MAX] = "";
    	void *lib;
    
    	snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_MODULE_DIR,
    		resource_name);
    
    	lib = dlopen(fn, RTLD_LAZY | RTLD_NOLOAD);
    
    	if (lib) {
    		logged_dlclose(resource_name, lib);
    		return 1;
    	}
    
    	return 0;
    }
    #endif
    
    
    static void unload_dynamic_module(struct ast_module *mod)
    {
    
    #if defined(HAVE_RTLD_NOLOAD)
    
    	char *name = ast_strdupa(ast_module_name(mod));
    
    	void *lib = mod->lib;
    
    	/* WARNING: the structure pointed to by mod is going to
    	   disappear when this operation succeeds, so we can't
    	   dereference it */
    
    	logged_dlclose(ast_module_name(mod), lib);
    
    
    	/* There are several situations where the module might still be resident
    	 * in memory.
    	 *
    	 * If somehow there was another dlopen() on the same module (unlikely,
    	 * since that all is supposed to happen in loader.c).
    	 *
    	 * Avoid the temptation of repeating the dlclose(). The other code that
    	 * dlopened the module still has its module reference, and should close
    	 * it itself. In other situations, dlclose() will happily return success
    	 * for as many times as you wish to call it.
    	 */
    #if defined(HAVE_RTLD_NOLOAD)
    	if (is_module_loaded(name)) {
    		ast_log(LOG_WARNING, "Module '%s' could not be completely unloaded\n", name);
    	}
    #endif
    
    /*!
     * \internal
     * \brief Attempt to dlopen a module.
     *
     * \param resource_in The module name to load.
     * \param so_ext ".so" or blank if ".so" is already part of resource_in.
     * \param filename Passed directly to dlopen.
     * \param flags Passed directly to dlopen.
     * \param suppress_logging Do not log any error from dlopen.
     *
     * \return Pointer to opened module, NULL on error.
     *
     * \warning module_list must be locked before calling this function.
     */
    static struct ast_module *load_dlopen(const char *resource_in, const char *so_ext,
    	const char *filename, int flags, unsigned int suppress_logging)
    
    	ast_assert(!resource_being_loaded);
    
    
    	mod = ast_calloc(1, sizeof(*mod) + strlen(resource_in) + strlen(so_ext) + 1);
    	if (!mod) {
    		return NULL;
    
    	sprintf(mod->resource, "%s%s", resource_in, so_ext); /* safe */
    
    	resource_being_loaded = mod;
    	mod->lib = dlopen(filename, flags);
    
    	if (resource_being_loaded) {
    		resource_being_loaded = NULL;
    
    		if (mod->lib) {
    
    			ast_log(LOG_ERROR, "Module '%s' did not register itself during load\n", resource_in);
    
    			logged_dlclose(resource_in, mod->lib);
    
    		} else if (!suppress_logging) {
    
    			ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
    		}
    
    static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int suppress_logging)
    
    {
    	char fn[PATH_MAX];
    	struct ast_module *mod;
    	size_t resource_in_len = strlen(resource_in);
    	const char *so_ext = "";
    
    	if (resource_in_len < 4 || strcasecmp(resource_in + resource_in_len - 3, ".so")) {
    		so_ext = ".so";
    
    	snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, so_ext);
    
    	/* Try loading in quiet mode first with RTLD_LOCAL.  The majority of modules do not
    	 * export symbols so this allows the least number of calls to dlopen. */
    	mod = load_dlopen(resource_in, so_ext, fn, RTLD_NOW | RTLD_LOCAL, suppress_logging);
    
    	if (!mod || !ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS)) {
    
    	/* Close the module so we can reopen with correct flags. */
    	logged_dlclose(resource_in, mod->lib);
    
    	return load_dlopen(resource_in, so_ext, fn, RTLD_NOW | RTLD_GLOBAL, 0);
    
    int modules_shutdown(void)
    
    {
    	struct ast_module *mod;
    
    	int somethingchanged;
    	int res;
    
    	/*!\note Some resources, like timers, are started up dynamically, and thus
    	 * may be still in use, even if all channels are dead.  We must therefore
    	 * check the usecount before asking modules to unload. */
    	do {
    		/* Reset flag before traversing the list */
    		somethingchanged = 0;
    
    
    		AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN(&module_list, mod, entry) {
    
    			if (mod->usecount) {
    
    				ast_debug(1, "Passing on %s: its use count is %d\n",
    					mod->resource, mod->usecount);
    
    			AST_DLLIST_REMOVE_CURRENT(entry);
    
    			if (mod->flags.running && !mod->flags.declined && mod->info->unload) {
    
    				ast_verb(1, "Unloading %s\n", mod->resource);
    
    				mod->info->unload();
    			}
    
    			module_destroy(mod);
    
    			somethingchanged = 1;
    		}
    
    		AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END;
    
    		if (!somethingchanged) {
    			AST_DLLIST_TRAVERSE(&module_list, mod, entry) {
    				if (mod->flags.keepuntilshutdown) {
    					ast_module_unref(mod);
    					mod->flags.keepuntilshutdown = 0;
    					somethingchanged = 1;
    				}
    			}
    		}
    
    	} while (somethingchanged);
    
    	res = AST_DLLIST_EMPTY(&module_list);
    
    	AST_DLLIST_UNLOCK(&module_list);
    
    	return !res;
    
    int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode force)
    {
    	struct ast_module *mod;
    	int res = -1;
    	int error = 0;
    
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (!(mod = find_resource(resource_name, 0))) {
    
    		AST_DLLIST_UNLOCK(&module_list);
    
    		ast_log(LOG_WARNING, "Unload failed, '%s' could not be found\n", resource_name);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	}
    
    	if (!mod->flags.running || mod->flags.declined) {
    		ast_log(LOG_WARNING, "Unload failed, '%s' is not loaded.\n", resource_name);
    
    
    	if (!error && (mod->usecount > 0)) {
    
    			ast_log(LOG_WARNING, "Warning:  Forcing removal of module '%s' with use count %d\n",
    				resource_name, mod->usecount);
    		else {
    			ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name,
    				mod->usecount);
    			error = 1;
    		}
    	}
    
    	if (!error) {
    
    		/* Request any channels attached to the module to hangup. */
    
    		__ast_module_user_hangup_all(mod);
    
    
    		ast_verb(1, "Unloading %s\n", mod->resource);
    
    		res = mod->info->unload();
    
    		if (res) {
    			ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
    
    			if (force <= AST_FORCE_FIRM) {
    
    				ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
    
    			}
    		}
    
    		if (!error) {
    			/*
    			 * Request hangup on any channels that managed to get attached
    			 * while we called the module unload function.
    			 */
    			__ast_module_user_hangup_all(mod);
    			sched_yield();