Merge "loader: Process module dependencies."

pull/9/head
Jenkins2 7 years ago committed by Gerrit Code Review
commit 2c27205e4e

@ -127,6 +127,21 @@ struct ast_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;
struct {
/*! The module running and ready to accept requests. */
unsigned int running:1;
@ -163,6 +178,225 @@ static int module_vector_cmp(struct ast_module *a, struct ast_module *b)
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) {
@ -234,6 +468,10 @@ void ast_module_register(const struct ast_module_info *info)
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);
@ -244,6 +482,19 @@ void ast_module_register(const struct ast_module_info *info)
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);
@ -267,7 +518,15 @@ void ast_module_unregister(const struct ast_module_info *info)
AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END;
AST_DLLIST_UNLOCK(&module_list);
if (mod) {
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);
}
@ -1109,6 +1368,23 @@ static enum ast_module_load_result start_resource(struct ast_module *mod)
return AST_MODULE_LOAD_FAILURE;
}
if (module_deps_reference(mod, NULL)) {
struct module_vector missing;
int i;
AST_VECTOR_INIT(&missing, 0);
if (module_deps_missing_recursive(mod, &missing)) {
ast_log(LOG_ERROR, "%s has one or more unknown dependencies.\n", mod->info->name);
}
for (i = 0; i < AST_VECTOR_SIZE(&missing); i++) {
ast_log(LOG_ERROR, "%s loaded before dependency %s!\n", mod->info->name,
AST_VECTOR_GET(&missing, i)->info->name);
}
AST_VECTOR_FREE(&missing);
return AST_MODULE_LOAD_FAILURE;
}
if (!ast_fully_booted) {
ast_verb(1, "Loading %s.\n", mod->resource);
}
@ -1176,6 +1452,17 @@ static enum ast_module_load_result load_resource(const char *resource_name, unsi
}
return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE;
}
/* Split lists from mod->info. */
res = ast_vector_string_split(&mod->requires, mod->info->requires, ",", 0, strcasecmp);
res |= ast_vector_string_split(&mod->optional_modules, mod->info->optional_modules, ",", 0, strcasecmp);
res |= ast_vector_string_split(&mod->enhances, mod->info->enhances, ",", 0, strcasecmp);
if (res) {
ast_log(LOG_WARNING, "Failed to initialize dependency structures for module '%s'.\n", resource_name);
unload_dynamic_module(mod);
return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE;
}
}
if (inspect_module(mod)) {
@ -1253,6 +1540,117 @@ static struct load_order_entry *add_to_load_order(const char *resource, struct l
AST_LIST_HEAD_NOLOCK(load_retries, load_order_entry);
static enum ast_module_load_result start_resource_attempt(struct ast_module *mod, int *count)
{
enum ast_module_load_result lres;
/* Try to grab required references. */
if (module_deps_reference(mod, NULL)) {
/* We're likely to retry so not an error. */
ast_debug(1, "Module %s is missing dependencies\n", mod->resource);
return AST_MODULE_LOAD_SKIP;
}
lres = start_resource(mod);
ast_debug(3, "START: %-46s[%d] %d\n",
mod->resource,
ast_test_flag(mod->info, AST_MODFLAG_LOAD_ORDER) ? mod->info->load_pri : AST_MODPRI_DEFAULT,
lres);
if (lres == AST_MODULE_LOAD_SUCCESS) {
(*count)++;
} else if (lres == AST_MODULE_LOAD_FAILURE) {
ast_log(LOG_ERROR, "*** Failed to load module %s\n", mod->resource);
}
return lres;
}
static int start_resource_list(struct module_vector *resources, int *mod_count)
{
struct module_vector missingdeps;
int res = 0;
AST_VECTOR_INIT(&missingdeps, 0);
while (AST_VECTOR_SIZE(resources)) {
struct ast_module *mod = AST_VECTOR_REMOVE(resources, 0, 1);
enum ast_module_load_result lres;
lres = start_resource_attempt(mod, mod_count);
if (lres == AST_MODULE_LOAD_SUCCESS) {
/* No missing dependencies, successful. */
continue;
}
if (lres == AST_MODULE_LOAD_FAILURE) {
ast_log(LOG_ERROR, "Failed to load %s.\n", ast_module_name(mod));
res = -1;
break;
}
if (lres == AST_MODULE_LOAD_DECLINE) {
continue;
}
res = module_deps_missing_recursive(mod, &missingdeps);
if (res) {
break;
}
if (!AST_VECTOR_SIZE(&missingdeps)) {
ast_log(LOG_WARNING, "%s isn't missing any dependencies but still didn't start\n",
ast_module_name(mod));
/* Dependencies were met but the module failed to start. */
res = -1;
break;
}
ast_debug(1, "%s has %d dependencies\n",
ast_module_name(mod), (int)AST_VECTOR_SIZE(&missingdeps));
while (AST_VECTOR_SIZE(&missingdeps)) {
int didwork = 0;
int i = 0;
while (i < AST_VECTOR_SIZE(&missingdeps)) {
struct ast_module *dep = AST_VECTOR_GET(&missingdeps, i);
ast_debug(1, "%s trying to start %s\n", ast_module_name(mod), ast_module_name(dep));
if (!start_resource_attempt(dep, mod_count)) {
ast_debug(1, "%s started %s\n", ast_module_name(mod), ast_module_name(dep));
AST_VECTOR_REMOVE(&missingdeps, i, 1);
AST_VECTOR_REMOVE_CMP_ORDERED(resources, dep,
AST_VECTOR_ELEM_DEFAULT_CMP, AST_VECTOR_ELEM_CLEANUP_NOOP);
didwork++;
continue;
}
ast_debug(1, "%s failed to start %s\n", ast_module_name(mod), ast_module_name(dep));
i++;
}
if (!didwork) {
break;
}
}
if (AST_VECTOR_SIZE(&missingdeps)) {
ast_log(LOG_ERROR, "Failed to load %s due to unfilled dependencies.\n",
ast_module_name(mod));
res = -1;
break;
}
res = start_resource_attempt(mod, mod_count);
if (res) {
ast_log(LOG_ERROR, "Failed to load %s: %d\n", ast_module_name(mod), res);
break;
}
}
AST_VECTOR_FREE(&missingdeps);
return res;
}
/*! loads modules in order by load_pri, updates mod_count
\return -1 on failure to load module, -2 on failure to load required module, otherwise 0
*/
@ -1355,30 +1753,9 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
AST_LIST_TRAVERSE_SAFE_END;
}
/* second remove modules from heap sorted by priority */
for (i = 0; i < AST_VECTOR_SIZE(&resource_heap); i++) {
struct ast_module *mod = AST_VECTOR_GET(&resource_heap, i);
enum ast_module_load_result lres;
lres = start_resource(mod);
ast_debug(3, "START: %-46s %d %d\n", mod->resource, lres, global_symbols);
switch (lres) {
case AST_MODULE_LOAD_SUCCESS:
count++;
case AST_MODULE_LOAD_DECLINE:
break;
case AST_MODULE_LOAD_FAILURE:
ast_log(LOG_ERROR, "*** Failed to load module %s\n", mod->resource);
res = -1;
goto done;
case AST_MODULE_LOAD_SKIP:
case AST_MODULE_LOAD_PRIORITY:
break;
}
}
res = start_resource_list(&resource_heap, &count);
done:
while ((order = AST_LIST_REMOVE_HEAD(&load_retries, entry))) {
ast_free(order->resource);
ast_free(order);

Loading…
Cancel
Save