diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h index d19c58967ed8ecf55df949d1243bfb53ecd83046..89a8f54b6c9c202746b68dc90c86db0f65957912 100644 --- a/include/asterisk/_private.h +++ b/include/asterisk/_private.h @@ -20,7 +20,7 @@ void load_asterisk_conf(void); void set_asterisk_conf_path(const char *path); void set_socket_path(const char *path); -int load_modules(unsigned int); /*!< Provided by loader.c */ +int load_modules(void); /*!< Provided by loader.c */ int modules_shutdown(void); /*!< Provided by loader.c */ int load_pbx(void); /*!< Provided by pbx.c */ int load_pbx_builtins(void); /*!< Provided by pbx_builtins.c */ diff --git a/main/asterisk.c b/main/asterisk.c index 7ff167ef73dffdff6dbea603cc98735b2e82028d..2fb32ac86270575e330252d77c5c2fa7e1bc3a82 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -4139,10 +4139,7 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou check_init(ast_local_init(), "Local Proxy Channel Driver"); /* We should avoid most config loads before this point as they can't use realtime. */ - check_init(load_modules(1), "Module Preload"); - - /* Load remaining modules */ - check_init(load_modules(0), "Module"); + check_init(load_modules(), "Module"); /* * This has to load after the dynamic modules load, as items in the media diff --git a/main/loader.c b/main/loader.c index bc78962050cf45e5a7a3b6f7ec548a9c1929b75a..d6837f860196f8e640d5db7c5391f156a901020b 100644 --- a/main/loader.c +++ b/main/loader.c @@ -181,6 +181,10 @@ struct ast_module { unsigned int keepuntilshutdown:1; /*! The module is built-in. */ unsigned int builtin:1; + /*! The admin has declared this module is required. */ + unsigned int required:1; + /*! This module is marked for preload. */ + unsigned int preload:1; } flags; AST_DLLIST_ENTRY(ast_module) entry; char resource[0]; @@ -223,12 +227,20 @@ static int module_vector_strcasecmp(struct ast_module *a, struct ast_module *b) static int module_vector_cmp(struct ast_module *a, struct ast_module *b) { + int preload_diff = (int)b->flags.preload - (int)a->flags.preload; /* 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; + if (preload_diff) { + /* -1 preload a but not b */ + /* 0 preload both or neither */ + /* 1 preload b but not a */ + return preload_diff; + } + /* * Returns comparison values for a vector sorted by priority. * <0 a_pri < b_pri @@ -1469,6 +1481,9 @@ static enum ast_module_load_result start_resource(struct ast_module *mod) break; case AST_MODULE_LOAD_DECLINE: mod->flags.declined = 1; + if (mod->flags.required) { + res = AST_MODULE_LOAD_FAILURE; + } break; case AST_MODULE_LOAD_FAILURE: case AST_MODULE_LOAD_SKIP: /* modules should never return this value */ @@ -1494,7 +1509,8 @@ static enum ast_module_load_result start_resource(struct ast_module *mod) * * 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 suppress_logging, struct module_vector *module_priorities, int required) +static enum ast_module_load_result load_resource(const char *resource_name, unsigned int suppress_logging, + struct module_vector *module_priorities, int required, int preload) { struct ast_module *mod; enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS; @@ -1519,6 +1535,9 @@ static enum ast_module_load_result load_resource(const char *resource_name, unsi } } + mod->flags.required |= required; + mod->flags.preload |= preload; + if (inspect_module(mod)) { goto prestart_error; } @@ -1554,7 +1573,7 @@ int ast_load_resource(const char *resource_name) { int res; AST_DLLIST_LOCK(&module_list); - res = load_resource(resource_name, 0, NULL, 0); + res = load_resource(resource_name, 0, NULL, 0, 0); if (!res) { ast_test_suite_event_notify("MODULE_LOAD", "Message: %s", resource_name); } @@ -1566,12 +1585,14 @@ int ast_load_resource(const char *resource_name) struct load_order_entry { char *resource; int required; + int preload; + int builtin; AST_LIST_ENTRY(load_order_entry) entry; }; AST_LIST_HEAD_NOLOCK(load_order, load_order_entry); -static struct load_order_entry *add_to_load_order(const char *resource, struct load_order *load_order, int required) +static struct load_order_entry *add_to_load_order(const char *resource, struct load_order *load_order, int required, int preload, int builtin) { struct load_order_entry *order; size_t resource_baselen = resource_name_baselen(resource); @@ -1581,12 +1602,15 @@ static struct load_order_entry *add_to_load_order(const char *resource, struct l /* Make sure we have the proper setting for the required field (we might have both load= and required= lines in modules.conf) */ order->required |= required; - return NULL; + order->preload |= preload; + return order; } } - if (!(order = ast_calloc(1, sizeof(*order)))) + order = ast_calloc(1, sizeof(*order)); + if (!order) { return NULL; + } order->resource = ast_strdup(resource); if (!order->resource) { @@ -1595,6 +1619,8 @@ static struct load_order_entry *add_to_load_order(const char *resource, struct l return NULL; } order->required = required; + order->preload = preload; + order->builtin = builtin; AST_LIST_INSERT_TAIL(load_order, order, entry); return order; @@ -1647,7 +1673,7 @@ retry_load: if (lres == AST_MODULE_LOAD_FAILURE) { ast_log(LOG_ERROR, "Failed to load %s.\n", ast_module_name(mod)); - res = -1; + res = -2; break; } @@ -1745,7 +1771,7 @@ static int load_resource_list(struct load_order *load_order, int *mod_count) enum ast_module_load_result lres; /* Suppress log messages unless this is the last pass */ - lres = load_resource(order->resource, !lasttry, &module_priorities, order->required); + lres = load_resource(order->resource, !lasttry, &module_priorities, order->required, order->preload); ast_debug(3, "PASS %d: %-46s %d\n", attempt, order->resource, lres); switch (lres) { case AST_MODULE_LOAD_SUCCESS: @@ -1799,24 +1825,9 @@ static int load_resource_list(struct load_order *load_order, int *mod_count) return res; } -int load_modules(unsigned int preload_only) +static int loader_builtin_init(struct load_order *load_order) { - struct ast_config *cfg; - struct load_order_entry *order; - struct ast_variable *v; - unsigned int load_count; - struct load_order load_order; - int res = 0; - struct ast_flags config_flags = { 0 }; - int modulecount = 0; - struct dirent *dirent; - DIR *dir; - - ast_verb(1, "Asterisk Dynamic Loader Starting:\n"); - - AST_LIST_HEAD_INIT_NOLOCK(&load_order); - - AST_DLLIST_LOCK(&module_list); + struct ast_module *mod; /* * All built-in modules have registered the first time, now it's time to complete @@ -1829,42 +1840,79 @@ int load_modules(unsigned int preload_only) ast_module_register(resource_being_loaded->info); } - if (!preload_only) { - struct ast_module *mod; - - /* Add all built-in modules to the load order. */ - AST_DLLIST_TRAVERSE(&module_list, mod, entry) { - if (!mod->flags.builtin) { - continue; - } + /* Add all built-in modules to the load order. */ + AST_DLLIST_TRAVERSE(&module_list, mod, entry) { + if (!mod->flags.builtin) { + continue; + } - add_to_load_order(mod->resource, &load_order, 0); + /* Built-in modules are not preloaded, most have an early load priority. */ + if (!add_to_load_order(mod->resource, load_order, 0, 0, 1)) { + return -1; } } + return 0; +} + +static int loader_config_init(struct load_order *load_order) +{ + int res = -1; + struct load_order_entry *order; + struct ast_config *cfg; + struct ast_variable *v; + struct ast_flags config_flags = { 0 }; + cfg = ast_config_load2(AST_MODULE_CONFIG, "" /* core, can't reload */, config_flags); if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) { - ast_log(LOG_WARNING, "No '%s' found, no modules will be loaded.\n", AST_MODULE_CONFIG); - goto done; + ast_log(LOG_WARNING, "'%s' invalid or missing.\n", AST_MODULE_CONFIG); + + return -1; } /* first, find all the modules we have been explicitly requested to load */ for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) { - if (!strcasecmp(v->name, preload_only ? "preload" : "load")) { - add_to_load_order(v->value, &load_order, 0); + int required; + int preload = 0; + + if (!strncasecmp(v->name, "preload", strlen("preload"))) { + preload = 1; + if (!strcasecmp(v->name, "preload")) { + required = 0; + } else if (!strcasecmp(v->name, "preload-require")) { + required = 1; + } else { + ast_log(LOG_ERROR, "Unknown configuration option '%s'", v->name); + goto done; + } + } else if (!strcasecmp(v->name, "load")) { + required = 0; + } else if (!strcasecmp(v->name, "require")) { + required = 1; + } else if (!strcasecmp(v->name, "noload") || !strcasecmp(v->name, "autoload")) { + continue; + } else { + ast_log(LOG_ERROR, "Unknown configuration option '%s'", v->name); + goto done; } - if (!strcasecmp(v->name, preload_only ? "preload-require" : "require")) { - /* Add the module to the list and make sure it's required */ - add_to_load_order(v->value, &load_order, 1); + + if (required) { ast_debug(2, "Adding module to required list: %s (%s)\n", v->value, v->name); } + + if (!add_to_load_order(v->value, load_order, required, preload, 0)) { + goto done; + } } /* check if 'autoload' is on */ - if (!preload_only && ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) { + if (ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) { /* if we are allowed to load dynamic modules, scan the directory for for all available modules and add them as well */ - if ((dir = opendir(ast_config_AST_MODULE_DIR))) { + DIR *dir = opendir(ast_config_AST_MODULE_DIR); + struct dirent *dirent; + + if (dir) { while ((dirent = readdir(dir))) { int ld = strlen(dirent->d_name); @@ -1881,14 +1929,16 @@ int load_modules(unsigned int preload_only) if (find_resource(dirent->d_name, 0)) continue; - add_to_load_order(dirent->d_name, &load_order, 0); + if (!add_to_load_order(dirent->d_name, load_order, 0, 0, 0)) { + closedir(dir); + goto done; + } } closedir(dir); } else { - if (!ast_opt_quiet) - ast_log(LOG_WARNING, "Unable to open modules directory '%s'.\n", - ast_config_AST_MODULE_DIR); + ast_log(LOG_ERROR, "Unable to open modules directory '%s'.\n", ast_config_AST_MODULE_DIR); + goto done; } } @@ -1902,8 +1952,18 @@ int load_modules(unsigned int preload_only) } baselen = resource_name_baselen(v->value); - AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) { + AST_LIST_TRAVERSE_SAFE_BEGIN(load_order, order, entry) { if (!resource_name_match(v->value, baselen, order->resource)) { + if (order->builtin) { + ast_log(LOG_ERROR, "%s is a built-in module, you cannot specify 'noload'.\n", v->value); + goto done; + } + + if (order->required) { + ast_log(LOG_ERROR, "%s is configured with '%s' and 'noload', this is impossible.\n", + v->value, order->preload ? "preload-require" : "require"); + goto done; + } AST_LIST_REMOVE_CURRENT(entry); ast_free(order->resource); ast_free(order); @@ -1912,10 +1972,36 @@ int load_modules(unsigned int preload_only) AST_LIST_TRAVERSE_SAFE_END; } - /* we are done with the config now, all the information we need is in the - load_order list */ + res = 0; +done: ast_config_destroy(cfg); + return res; +} + +int load_modules(void) +{ + struct load_order_entry *order; + unsigned int load_count; + struct load_order load_order; + int res = 0; + int modulecount = 0; + + ast_verb(1, "Asterisk Dynamic Loader Starting:\n"); + + AST_LIST_HEAD_INIT_NOLOCK(&load_order); + AST_DLLIST_LOCK(&module_list); + + res = loader_builtin_init(&load_order); + if (res) { + goto done; + } + + res = loader_config_init(&load_order); + if (res) { + goto done; + } + load_count = 0; AST_LIST_TRAVERSE(&load_order, order, entry) load_count++;