diff options
Diffstat (limited to 'main/loader.c')
-rw-r--r-- | main/loader.c | 374 |
1 files changed, 214 insertions, 160 deletions
diff --git a/main/loader.c b/main/loader.c index 23a29d994..2a203c6a7 100644 --- a/main/loader.c +++ b/main/loader.c @@ -52,7 +52,7 @@ #include "asterisk/features_config.h" #include "asterisk/dsp.h" #include "asterisk/udptl.h" -#include "asterisk/heap.h" +#include "asterisk/vector.h" #include "asterisk/app.h" #include "asterisk/test.h" #include "asterisk/sounds_index.h" @@ -110,6 +110,8 @@ static const unsigned char expected_key[] = static char buildopt_sum[33] = AST_BUILDOPT_SUM; +AST_VECTOR(module_vector, struct ast_module *); + /*! * \brief Internal flag to indicate all modules have been initially loaded. */ @@ -133,13 +135,34 @@ struct ast_module { /*! This module is being held open until it's time to shutdown. */ unsigned int keepuntilshutdown:1; } flags; - AST_LIST_ENTRY(ast_module) list_entry; AST_DLLIST_ENTRY(ast_module) entry; char resource[0]; }; static AST_DLLIST_HEAD_STATIC(module_list, ast_module); +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; +} + const char *ast_module_name(const struct ast_module *mod) { if (!mod || !mod->info) { @@ -167,38 +190,51 @@ static int do_full_reload = 0; static AST_DLLIST_HEAD_STATIC(reload_queue, reload_queue_item); -/* when dynamic modules are being loaded, ast_module_register() will - need to know what filename the module was loaded from while it - is being registered -*/ +/*! + * \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 *resource_being_loaded; -/* XXX: should we check for duplicate resource names here? */ - +/*! + * \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 = resource_being_loaded; + struct ast_module *mod; + + /* + * 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; + mod->info = info; if (ast_opt_ref_debug) { mod->ref_debug = ao2_t_alloc(0, NULL, info->name); } AST_LIST_HEAD_INIT(&mod->users); - /* during startup, before the loader has been initialized, - there are no threads, so there is no need to take the lock - on this list to manipulate it. it is also possible that it - might be unsafe to use the list lock at that point... so - let's avoid it altogether - */ - AST_DLLIST_LOCK(&module_list); - /* it is paramount that the new entry be placed at the tail of - the list, otherwise the code that uses dlopen() to load - dynamic modules won't be able to find out if the module it - just opened was registered or failed to load - */ AST_DLLIST_INSERT_TAIL(&module_list, mod, entry); AST_DLLIST_UNLOCK(&module_list); @@ -206,6 +242,13 @@ void ast_module_register(const struct ast_module_info *info) *((struct ast_module **) &(info->self)) = mod; } +static void module_destroy(struct ast_module *mod) +{ + 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; @@ -226,9 +269,7 @@ void ast_module_unregister(const struct ast_module_info *info) if (mod) { ast_debug(5, "Unregistering module %s\n", info->name); - AST_LIST_HEAD_DESTROY(&mod->users); - ao2_cleanup(mod->ref_debug); - ast_free(mod); + module_destroy(mod); } } @@ -498,96 +539,93 @@ static void unload_dynamic_module(struct ast_module *mod) #endif } -static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, unsigned int suppress_logging, struct ast_heap *resource_heap, int required); - #define MODULE_LOCAL_ONLY (void *)-1 -static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only, unsigned int suppress_logging, struct ast_heap *resource_heap) +/*! + * \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) { - char fn[PATH_MAX] = ""; - void *lib = NULL; struct ast_module *mod; - unsigned int wants_global; - int space; /* room needed for the descriptor */ - int missing_so = 0; - - space = sizeof(*resource_being_loaded) + strlen(resource_in) + 1; - if (strcasecmp(resource_in + strlen(resource_in) - 3, ".so")) { - missing_so = 1; - space += 3; /* room for the extra ".so" */ - } - - snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, missing_so ? ".so" : ""); - /* make a first load of the module in 'quiet' mode... don't try to resolve - any symbols, and don't export any symbols. this will allow us to peek into - the module's info block (if available) to see what flags it has set */ + ast_assert(!resource_being_loaded); - resource_being_loaded = ast_calloc(1, space); - if (!resource_being_loaded) + mod = ast_calloc(1, sizeof(*mod) + strlen(resource_in) + strlen(so_ext) + 1); + if (!mod) { return NULL; - strcpy(resource_being_loaded->resource, resource_in); - if (missing_so) - strcat(resource_being_loaded->resource, ".so"); + } - if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_GLOBAL))) { - if (!suppress_logging) { + 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()); } - ast_free(resource_being_loaded); - return NULL; - } + ast_free(mod); - /* the dlopen() succeeded, let's find out if the module - registered itself */ - /* note that this will only work properly as long as - ast_module_register() (which is called by the module's - constructor) places the new module at the tail of the - module_list - */ - if (resource_being_loaded != (mod = AST_DLLIST_LAST(&module_list))) { - ast_log(LOG_WARNING, "Module '%s' did not register itself during load\n", resource_in); - /* no, it did not, so close it and return */ - logged_dlclose(resource_in, lib); - /* note that the module's destructor will call ast_module_unregister(), - which will free the structure we allocated in resource_being_loaded */ return NULL; } - wants_global = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS); + return mod; +} - /* if we are being asked only to load modules that provide global symbols, - and this one does not, then close it and return */ - if (global_symbols_only && !wants_global) { - logged_dlclose(resource_in, lib); - return MODULE_LOCAL_ONLY; +static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only, unsigned int suppress_logging) +{ + char fn[PATH_MAX]; + struct ast_module *mod; + size_t resource_in_len = strlen(resource_in); + int exports_globals; + const char *so_ext = ""; + + if (resource_in_len < 4 || strcasecmp(resource_in + resource_in_len - 3, ".so")) { + so_ext = ".so"; } - logged_dlclose(resource_in, lib); - resource_being_loaded = NULL; + snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, so_ext); - /* start the load process again */ - resource_being_loaded = ast_calloc(1, space); - if (!resource_being_loaded) - return NULL; - strcpy(resource_being_loaded->resource, resource_in); - if (missing_so) - strcat(resource_being_loaded->resource, ".so"); + /* Try loading in quiet mode first with flags to export global symbols. + * If the module does not want to export globals we will close and reopen. */ + mod = load_dlopen(resource_in, so_ext, fn, + global_symbols_only ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL, + suppress_logging); - if (!(lib = dlopen(fn, wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) { - ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror()); - ast_free(resource_being_loaded); + if (!mod) { return NULL; } - /* since the module was successfully opened, and it registered itself - the previous time we did that, we're going to assume it worked this - time too :) */ + exports_globals = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS); + if ((global_symbols_only && exports_globals) || (!global_symbols_only && !exports_globals)) { + /* The first dlopen had the correct flags. */ + return mod; + } - AST_DLLIST_LAST(&module_list)->lib = lib; - resource_being_loaded = NULL; + /* Close the module so we can reopen with correct flags. */ + logged_dlclose(resource_in, mod->lib); + if (global_symbols_only) { + return MODULE_LOCAL_ONLY; + } - return AST_DLLIST_LAST(&module_list); + return load_dlopen(resource_in, so_ext, fn, + exports_globals ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL, + 0); } int modules_shutdown(void) @@ -621,9 +659,7 @@ int modules_shutdown(void) ast_verb(1, "Unloading %s\n", mod->resource); mod->info->unload(); } - AST_LIST_HEAD_DESTROY(&mod->users); - ao2_cleanup(mod->ref_debug); - ast_free(mod); + module_destroy(mod); somethingchanged = 1; } AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END; @@ -1120,13 +1156,13 @@ static enum ast_module_load_result start_resource(struct ast_module *mod) /*! loads a resource based upon resource_name. If global_symbols_only is set * only modules with global symbols will be loaded. * - * If the ast_heap is provided (not NULL) the module is found and added to the - * heap without running the module's load() function. By doing this, modules - * added to the resource_heap can be initialized later in order by priority. + * If the module_vector is provided (not NULL) the module is found and added to the + * vector without running the module's load() function. By doing this, modules + * can be initialized later in order by priority and dependencies. * - * If the ast_heap is not provided, the module's load function will be executed + * If the module_vector is not provided, the module's load function will be executed * immediately */ -static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, unsigned int suppress_logging, struct ast_heap *resource_heap, int required) +static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, unsigned int suppress_logging, struct module_vector *resource_heap, int required) { struct ast_module *mod; enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS; @@ -1139,7 +1175,7 @@ static enum ast_module_load_result load_resource(const char *resource_name, unsi if (global_symbols_only && !ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS)) return AST_MODULE_LOAD_SKIP; } else { - mod = load_dynamic_module(resource_name, global_symbols_only, suppress_logging, resource_heap); + mod = load_dynamic_module(resource_name, global_symbols_only, suppress_logging); if (mod == MODULE_LOCAL_ONLY) { return AST_MODULE_LOAD_SKIP; } @@ -1152,21 +1188,26 @@ static enum ast_module_load_result load_resource(const char *resource_name, unsi } if (inspect_module(mod)) { - ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name); - unload_dynamic_module(mod); - return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE; + goto prestart_error; } mod->flags.declined = 0; if (resource_heap) { - ast_heap_push(resource_heap, mod); + if (AST_VECTOR_ADD_SORTED(resource_heap, mod, module_vector_cmp)) { + goto prestart_error; + } res = AST_MODULE_LOAD_PRIORITY; } else { res = start_resource(mod); } return res; + +prestart_error: + ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name); + unload_dynamic_module(mod); + return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE; } int ast_load_resource(const char *resource_name) @@ -1214,23 +1255,6 @@ static struct load_order_entry *add_to_load_order(const char *resource, struct l return order; } -static int mod_load_cmp(void *a, void *b) -{ - struct ast_module *a_mod = (struct ast_module *) a; - struct ast_module *b_mod = (struct ast_module *) b; - /* if load_pri is not set, default is 128. Lower is better */ - int a_pri = ast_test_flag(a_mod->info, AST_MODFLAG_LOAD_ORDER) ? a_mod->info->load_pri : 128; - int b_pri = ast_test_flag(b_mod->info, AST_MODFLAG_LOAD_ORDER) ? b_mod->info->load_pri : 128; - - /* - * Returns comparison values for a min-heap - * <0 a_pri > b_pri - * =0 a_pri == b_pri - * >0 a_pri < b_pri - */ - return b_pri - a_pri; -} - AST_LIST_HEAD_NOLOCK(load_retries, load_order_entry); /*! loads modules in order by load_pri, updates mod_count @@ -1238,9 +1262,8 @@ AST_LIST_HEAD_NOLOCK(load_retries, load_order_entry); */ static int load_resource_list(struct load_order *load_order, unsigned int global_symbols, int *mod_count) { - struct ast_heap *resource_heap; + struct module_vector resource_heap; struct load_order_entry *order; - struct ast_module *mod; struct load_retries load_retries; int count = 0; int res = 0; @@ -1249,7 +1272,9 @@ static int load_resource_list(struct load_order *load_order, unsigned int global AST_LIST_HEAD_INIT_NOLOCK(&load_retries); - if(!(resource_heap = ast_heap_create(8, mod_load_cmp, -1))) { + if (AST_VECTOR_INIT(&resource_heap, 500)) { + ast_log(LOG_ERROR, "Failed to initialize module loader.\n"); + return -1; } @@ -1258,7 +1283,7 @@ static int load_resource_list(struct load_order *load_order, unsigned int global enum ast_module_load_result lres; /* Suppress log messages unless this is the last pass */ - lres = load_resource(order->resource, global_symbols, 1, resource_heap, order->required); + lres = load_resource(order->resource, global_symbols, 1, &resource_heap, order->required); ast_debug(3, "PASS 0: %-46s %d %d\n", order->resource, lres, global_symbols); switch (lres) { case AST_MODULE_LOAD_SUCCESS: @@ -1282,7 +1307,7 @@ static int load_resource_list(struct load_order *load_order, unsigned int global */ break; case AST_MODULE_LOAD_PRIORITY: - /* load_resource worked and the module was added to the priority heap */ + /* load_resource worked and the module was added to the priority vector */ AST_LIST_REMOVE_CURRENT(entry); ast_free(order->resource); ast_free(order); @@ -1297,7 +1322,7 @@ static int load_resource_list(struct load_order *load_order, unsigned int global enum ast_module_load_result lres; /* Suppress log messages unless this is the last pass */ - lres = load_resource(order->resource, global_symbols, (i < LOAD_RETRIES - 1), resource_heap, order->required); + lres = load_resource(order->resource, global_symbols, (i < LOAD_RETRIES - 1), &resource_heap, order->required); ast_debug(3, "PASS %d %-46s %d %d\n", i + 1, order->resource, lres, global_symbols); switch (lres) { /* These are all retryable. */ @@ -1335,7 +1360,8 @@ static int load_resource_list(struct load_order *load_order, unsigned int global } /* second remove modules from heap sorted by priority */ - while ((mod = ast_heap_pop(resource_heap))) { + 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); @@ -1365,7 +1391,7 @@ done: if (mod_count) { *mod_count += count; } - ast_heap_destroy(resource_heap); + AST_VECTOR_FREE(&resource_heap); return res; } @@ -1503,33 +1529,57 @@ void ast_update_use_count(void) AST_LIST_UNLOCK(&updaters); } +/*! + * \internal + * \brief Build an alpha sorted list of modules. + * + * \param alpha_module_list Pointer to uninitialized module_vector. + * + * This function always initializes alpha_module_list. + * + * \pre module_list must be locked. + */ +static int alpha_module_list_create(struct module_vector *alpha_module_list) +{ + struct ast_module *cur; + + if (AST_VECTOR_INIT(alpha_module_list, 32)) { + return -1; + } + + AST_DLLIST_TRAVERSE(&module_list, cur, entry) { + if (AST_VECTOR_ADD_SORTED(alpha_module_list, cur, module_vector_strcasecmp)) { + return -1; + } + } + + return 0; +} + int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *status, const char *like, enum ast_module_support_level support_level), const char *like) { - struct ast_module *cur; - int unlock = -1; int total_mod_loaded = 0; - AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + struct module_vector alpha_module_list; - if (AST_DLLIST_TRYLOCK(&module_list)) { - unlock = 0; - } + AST_DLLIST_LOCK(&module_list); - AST_DLLIST_TRAVERSE(&module_list, cur, entry) { - AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource); - } + if (!alpha_module_list_create(&alpha_module_list)) { + int idx; - while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) { - total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, - cur->flags.running ? "Running" : "Not Running", like, cur->info->support_level); - } + for (idx = 0; idx < AST_VECTOR_SIZE(&alpha_module_list); idx++) { + struct ast_module *cur = AST_VECTOR_GET(&alpha_module_list, idx); - if (unlock) { - AST_DLLIST_UNLOCK(&module_list); + total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, + cur->flags.running ? "Running" : "Not Running", like, cur->info->support_level); + } } + AST_DLLIST_UNLOCK(&module_list); + AST_VECTOR_FREE(&alpha_module_list); + return total_mod_loaded; } @@ -1539,22 +1589,24 @@ int ast_update_module_list_data(int (*modentry)(const char *module, const char * void *data), const char *like, void *data) { - struct ast_module *cur; int total_mod_loaded = 0; - AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + struct module_vector alpha_module_list; AST_DLLIST_LOCK(&module_list); - AST_DLLIST_TRAVERSE(&module_list, cur, entry) { - AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource); - } + if (!alpha_module_list_create(&alpha_module_list)) { + int idx; + + for (idx = 0; idx < AST_VECTOR_SIZE(&alpha_module_list); idx++) { + struct ast_module *cur = AST_VECTOR_GET(&alpha_module_list, idx); - while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) { - total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, - cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data); + total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, + cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data); + } } AST_DLLIST_UNLOCK(&module_list); + AST_VECTOR_FREE(&alpha_module_list); return total_mod_loaded; } @@ -1566,23 +1618,25 @@ int ast_update_module_list_condition(int (*modentry)(const char *module, const c void *data, const char *condition), const char *like, void *data, const char *condition) { - struct ast_module *cur; int conditions_met = 0; - AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + struct module_vector alpha_module_list; AST_DLLIST_LOCK(&module_list); - AST_DLLIST_TRAVERSE(&module_list, cur, entry) { - AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource); - } + if (!alpha_module_list_create(&alpha_module_list)) { + int idx; + + for (idx = 0; idx < AST_VECTOR_SIZE(&alpha_module_list); idx++) { + struct ast_module *cur = AST_VECTOR_GET(&alpha_module_list, idx); - while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) { - conditions_met += modentry(cur->resource, cur->info->description, cur->usecount, - cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data, - condition); + conditions_met += modentry(cur->resource, cur->info->description, cur->usecount, + cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data, + condition); + } } AST_DLLIST_UNLOCK(&module_list); + AST_VECTOR_FREE(&alpha_module_list); return conditions_met; } |