]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
loader: Allow declined modules to be unloaded.
authorNaveen Albert <asterisk@phreaknet.org>
Thu, 8 Dec 2022 21:44:25 +0000 (21:44 +0000)
committerGeorge Joseph <gjoseph@digium.com>
Thu, 5 Jan 2023 12:13:18 +0000 (06:13 -0600)
Currently, if a module declines to load, dlopen is called
to register the module but dlclose never gets called.
Furthermore, loader.c currently doesn't allow dlclose
to ever get called on the module, since it declined to
load and the unload function bails early in this case.

This can be problematic if a module is updated, since the
new module cannot be loaded into memory since we haven't
closed all references to it. To fix this, we now allow
modules to be unloaded, even if they never "loaded" in
Asterisk itself, so that dlclose is called and the module
can be properly cleaned up, allowing the updated module
to be loaded from scratch next time.

ASTERISK-30345 #close

Change-Id: Ifc743aadfa85ebe3284e02a63e124dafa64988d5

main/loader.c

index 549e3f0e52cd7d912ca60c54e8b4956ce26402c1..208e5a2c6f18f5606f93621c41414584f07c6a41 100644 (file)
@@ -1241,8 +1241,17 @@ int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode f
        }
 
        if (!mod->flags.running || mod->flags.declined) {
-               ast_log(LOG_WARNING, "Unload failed, '%s' is not loaded.\n", resource_name);
-               error = 1;
+               /* If the user asks to unload a module that didn't load, obey.
+                * Otherwise, we never dlclose() modules that fail to load,
+                * which means if the module (shared object) is updated,
+                * we can't load the updated module since we never dlclose()'d it.
+                * Accordingly, obey the unload request so we can load the module
+                * from scratch next time.
+                */
+               ast_log(LOG_NOTICE, "Unloading module '%s' that previously declined to load\n", resource_name);
+               error = 0;
+               res = 0;
+               goto exit; /* Skip all the intervening !error checks, only the last one is relevant. */
        }
 
        if (!error && (mod->usecount > 0)) {
@@ -1284,6 +1293,7 @@ int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode f
        if (!error)
                mod->flags.running = mod->flags.declined = 0;
 
+exit:
        AST_DLLIST_UNLOCK(&module_list);
 
        if (!error) {
@@ -1823,7 +1833,18 @@ prestart_error:
 
 int ast_load_resource(const char *resource_name)
 {
+       struct ast_module *mod;
        int res;
+
+       /* If we're trying to load a module that previously declined to load,
+        * transparently unload it first so we dlclose, then dlopen it afresh.
+        * Otherwise, we won't actually load a (potentially) updated module. */
+       mod = find_resource(resource_name, 0);
+       if (mod && mod->flags.declined) {
+               ast_debug(1, "Module %s previously declined to load, unloading it first before loading again\n", resource_name);
+               ast_unload_resource(resource_name, 0);
+       }
+
        AST_DLLIST_LOCK(&module_list);
        res = load_resource(resource_name, 0, NULL, 0, 0);
        if (!res) {