]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
loader: Fix startup issues.
authorCorey Farrell <git@cfware.com>
Sun, 15 Jul 2018 18:58:15 +0000 (14:58 -0400)
committerCorey Farrell <git@cfware.com>
Mon, 16 Jul 2018 22:21:52 +0000 (18:21 -0400)
* Merge the preload and load stages, use load ordering to try preload's
  first.  This fixes an issue where `preload=res_config_curl` would fail
  unless res_curl and func_curl were also preloaded.  Now it is only
  required that those modules be loaded during startup: autoload or
  regular load is good enough.
* The configuration option `require` and `preload-require` were only
  effective if the modules failed to load.  These options will now abort
  Asterisk startup if required modules fail to reach the 'Running'
  state.
* Missing or invalid 'module.conf' did not prevent startup.  Asterisk
  doesn't do anything without modules so this a fatal error.

Change-Id: Ie4176699133f0e3a823b43f90c3348677e43a5f3

include/asterisk/_private.h
main/asterisk.c
main/loader.c

index d19c58967ed8ecf55df949d1243bfb53ecd83046..89a8f54b6c9c202746b68dc90c86db0f65957912 100644 (file)
@@ -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 */
index 7ff167ef73dffdff6dbea603cc98735b2e82028d..2fb32ac86270575e330252d77c5c2fa7e1bc3a82 100644 (file)
@@ -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
index bc78962050cf45e5a7a3b6f7ec548a9c1929b75a..d6837f860196f8e640d5db7c5391f156a901020b 100644 (file)
@@ -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++;