]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
loader: Create ast_module_running_ref.
authorCorey Farrell <git@cfware.com>
Sat, 30 Dec 2017 00:24:02 +0000 (19:24 -0500)
committerCorey Farrell <git@cfware.com>
Wed, 3 Jan 2018 22:23:36 +0000 (17:23 -0500)
This function returns NULL if the module in question is not running.  I
did not change ast_module_ref as most callers do not check the result
and they always call ast_module_unref.

Make use of this function when running registered items from:
* app_stack API's
* bridge technologies
* CLI commands
* File formats
* Manager Actions
* RTP engines
* Sorcery Wizards
* Timing Interfaces
* Translators
* AGI Commands
* Fax Technologies

ASTERISK-20346 #close

Change-Id: Ia16fd28e188b2fc0b9d18b8a5d9cacc31df73fcc

13 files changed:
include/asterisk/module.h
main/app.c
main/bridge.c
main/cli.c
main/file.c
main/loader.c
main/manager.c
main/rtp_engine.c
main/sorcery.c
main/timing.c
main/translate.c
res/res_agi.c
res/res_fax.c

index d3b9ddc20964f85cdf136e29f8b0bf21e58aa849..103cd3053c30cc888dc1c77a542b0c32123f73e1 100644 (file)
@@ -357,6 +357,7 @@ void __ast_module_user_hangup_all(struct ast_module *);
 #define ast_module_user_hangup_all() __ast_module_user_hangup_all(AST_MODULE_SELF)
 
 struct ast_module *__ast_module_ref(struct ast_module *mod, const char *file, int line, const char *func);
+struct ast_module *__ast_module_running_ref(struct ast_module *mod, const char *file, int line, const char *func);
 void __ast_module_shutdown_ref(struct ast_module *mod, const char *file, int line, const char *func);
 void __ast_module_unref(struct ast_module *mod, const char *file, int line, const char *func);
 
@@ -369,6 +370,20 @@ void __ast_module_unref(struct ast_module *mod, const char *file, int line, cons
  * from being unloaded.
  */
 #define ast_module_ref(mod)           __ast_module_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+/*!
+ * \brief Hold a reference to the module if it is running.
+ * \param mod Module to reference
+ * \retval mod if running
+ * \retval NULL if not running
+ *
+ * The returned pointer should be released with ast_module_unref.
+ *
+ * \note A module reference will prevent the module from being unloaded.
+ */
+#define ast_module_running_ref(mod) \
+       __ast_module_running_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
 /*!
  * \brief Prevent unload of the module before shutdown
  * \param mod Module to hold
index 215ec04e8b2232786e19a1fb4561c23ca3099c97..2db9a8f5086039fcee559347fb274bae4f95ec08 100644 (file)
@@ -386,6 +386,7 @@ int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *
        return res;
 }
 
+/* BUGBUG this is not thread safe. */
 static const struct ast_app_stack_funcs *app_stack_callbacks;
 
 void ast_install_stack_functions(const struct ast_app_stack_funcs *funcs)
@@ -399,16 +400,16 @@ const char *ast_app_expand_sub_args(struct ast_channel *chan, const char *args)
        const char *new_args;
 
        funcs = app_stack_callbacks;
-       if (!funcs || !funcs->expand_sub_args) {
+       if (!funcs || !funcs->expand_sub_args || !ast_module_running_ref(funcs->module)) {
                ast_log(LOG_WARNING,
                        "Cannot expand 'Gosub(%s)' arguments.  The app_stack module is not available.\n",
                        args);
                return NULL;
        }
-       ast_module_ref(funcs->module);
 
        new_args = funcs->expand_sub_args(chan, args);
        ast_module_unref(funcs->module);
+
        return new_args;
 }
 
@@ -418,13 +419,12 @@ int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *s
        int res;
 
        funcs = app_stack_callbacks;
-       if (!funcs || !funcs->run_sub) {
+       if (!funcs || !funcs->run_sub || !ast_module_running_ref(funcs->module)) {
                ast_log(LOG_WARNING,
                        "Cannot run 'Gosub(%s)'.  The app_stack module is not available.\n",
                        sub_args);
                return -1;
        }
-       ast_module_ref(funcs->module);
 
        if (autoservice_chan) {
                ast_autoservice_start(autoservice_chan);
index 88d9e548775577ad046f8f48b46518a32d338ef3..4f79852cb7a78113df9bea71aa09f455f41cf3d1 100644 (file)
@@ -234,8 +234,21 @@ int __ast_bridge_technology_register(struct ast_bridge_technology *technology, s
        /* Copy module pointer so reference counting can keep the module from unloading */
        technology->mod = module;
 
-       /* Insert our new bridge technology into the list and print out a pretty message */
-       AST_RWLIST_INSERT_TAIL(&bridge_technologies, technology, entry);
+       /* Find the correct position to insert the technology. */
+       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&bridge_technologies, current, entry) {
+               /* Put the highest preference tech's first in the list. */
+               if (technology->preference >= current->preference) {
+                       AST_RWLIST_INSERT_BEFORE_CURRENT(technology, entry);
+
+                       break;
+               }
+       }
+       AST_RWLIST_TRAVERSE_SAFE_END;
+
+       if (!current) {
+               /* Insert our new bridge technology to the end of the list. */
+               AST_RWLIST_INSERT_TAIL(&bridge_technologies, technology, entry);
+       }
 
        AST_RWLIST_UNLOCK(&bridge_technologies);
 
@@ -517,12 +530,17 @@ static struct ast_bridge_technology *find_best_technology(uint32_t capabilities,
                                current->name);
                        continue;
                }
+               if (!ast_module_running_ref(current->mod)) {
+                       ast_debug(1, "Bridge technology %s is not running, skipping.\n", current->name);
+                       continue;
+               }
+               if (best) {
+                       ast_module_unref(best->mod);
+               }
                best = current;
        }
 
        if (best) {
-               /* Increment it's module reference count if present so it does not get unloaded while in use */
-               ast_module_ref(best->mod);
                ast_debug(1, "Chose bridge technology %s\n", best->name);
        }
 
index 1299c2aa7aea4aff7ef242535cd8c90509b528b7..80c1843281bff3a6af886bdd2e68e3599082cce3 100644 (file)
@@ -2679,9 +2679,12 @@ static char *__ast_cli_generator(const char *text, const char *word, int state,
                                        .n = state - matchnum,
                                        .argv = argv,
                                        .argc = x};
-                               ast_module_ref(e->module);
-                               ret = e->handler(e, CLI_GENERATE, &a);
-                               ast_module_unref(e->module);
+
+                               /* If the command is in a module it must be running. */
+                               if (!e->module || ast_module_running_ref(e->module)) {
+                                       ret = e->handler(e, CLI_GENERATE, &a);
+                                       ast_module_unref(e->module);
+                               }
                        }
                        if (ret)
                                break;
@@ -2760,9 +2763,11 @@ int ast_cli_command_full(int uid, int gid, int fd, const char *s)
         */
        args[0] = (char *)e;
 
-       ast_module_ref(e->module);
-       retval = e->handler(e, CLI_HANDLER, &a);
-       ast_module_unref(e->module);
+       /* If the command is in a module it must be running. */
+       if (!e->module || ast_module_running_ref(e->module)) {
+               retval = e->handler(e, CLI_HANDLER, &a);
+               ast_module_unref(e->module);
+       }
 
        if (retval == CLI_SHOWUSAGE) {
                ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n"));
index 41131f9eebc3dbc151157dbea8b4d3a593143c3b..8dded812382054f29344267bf4a2753d5ae79da7 100644 (file)
@@ -425,11 +425,17 @@ static void filestream_destructor(void *arg)
 static struct ast_filestream *get_filestream(struct ast_format_def *fmt, FILE *bfile)
 {
        struct ast_filestream *s;
-
        int l = sizeof(*s) + fmt->buf_size + fmt->desc_size;    /* total allocation size */
-       if ( (s = ao2_alloc(l, filestream_destructor)) == NULL)
+
+       if (!ast_module_running_ref(fmt->module)) {
+               return NULL;
+       }
+
+       s = ao2_alloc(l, filestream_destructor);
+       if (!s) {
+               ast_module_unref(fmt->module);
                return NULL;
-       ast_module_ref(fmt->module);
+       }
        s->fmt = fmt;
        s->f = bfile;
 
index fe18048efc9eaa01cf48d527539f20c879629e33..88c1cda97aeb4ebec6856a5c089cef6e5c747921 100644 (file)
@@ -1702,6 +1702,16 @@ struct ast_module *__ast_module_ref(struct ast_module *mod, const char *file, in
        return mod;
 }
 
+struct ast_module *__ast_module_running_ref(struct ast_module *mod,
+       const char *file, int line, const char *func)
+{
+       if (!mod || !mod->flags.running) {
+               return NULL;
+       }
+
+       return __ast_module_ref(mod, file, line, func);
+}
+
 void __ast_module_shutdown_ref(struct ast_module *mod, const char *file, int line, const char *func)
 {
        if (!mod || mod->flags.keepuntilshutdown) {
index 576978c31c3a205b14f4d4e58ecfdad90ee553a3..3aa910501a5b827fe391d9a1533334dc3e916767 100644 (file)
@@ -2923,34 +2923,41 @@ int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
        }
 
        action = astman_get_header(&m, "Action");
-       if (strcasecmp(action, "login")) {
+
+       do {
+               if (!strcasecmp(action, "login")) {
+                       break;
+               }
+
                act_found = action_find(action);
-               if (act_found) {
-                       /*
-                        * we have to simulate a session for this action request
-                        * to be able to pass it down for processing
-                        * This is necessary to meet the previous design of manager.c
-                        */
-                       s.hook = hook;
+               if (!act_found) {
+                       break;
+               }
 
-                       ao2_lock(act_found);
-                       if (act_found->registered && act_found->func) {
-                               if (act_found->module) {
-                                       ast_module_ref(act_found->module);
-                               }
-                               ao2_unlock(act_found);
+               /*
+                * we have to simulate a session for this action request
+                * to be able to pass it down for processing
+                * This is necessary to meet the previous design of manager.c
+                */
+               s.hook = hook;
+
+               ret = -1;
+               ao2_lock(act_found);
+               if (act_found->registered && act_found->func) {
+                       struct ast_module *mod_ref = ast_module_running_ref(act_found->module);
+
+                       ao2_unlock(act_found);
+                       /* If the action is in a module it must be running. */
+                       if (!act_found->module || mod_ref) {
                                ret = act_found->func(&s, &m);
-                               ao2_lock(act_found);
-                               if (act_found->module) {
-                                       ast_module_unref(act_found->module);
-                               }
-                       } else {
-                               ret = -1;
+                               ast_module_unref(mod_ref);
                        }
+               } else {
                        ao2_unlock(act_found);
-                       ao2_t_ref(act_found, -1, "done with found action object");
                }
-       }
+               ao2_t_ref(act_found, -1, "done with found action object");
+       } while (0);
+
        ast_free(dup_str);
        return ret;
 }
@@ -6442,21 +6449,21 @@ static int process_message(struct mansession *s, const struct message *m)
                if ((s->session->writeperm & act_found->authority)
                        || act_found->authority == 0) {
                        /* We have the authority to execute the action. */
+                       ret = -1;
                        ao2_lock(act_found);
                        if (act_found->registered && act_found->func) {
-                               ast_debug(1, "Running action '%s'\n", act_found->action);
-                               if (act_found->module) {
-                                       ast_module_ref(act_found->module);
-                               }
+                               struct ast_module *mod_ref = ast_module_running_ref(act_found->module);
+
                                ao2_unlock(act_found);
-                               ret = act_found->func(s, m);
-                               acted = 1;
-                               ao2_lock(act_found);
-                               if (act_found->module) {
-                                       ast_module_unref(act_found->module);
+                               if (mod_ref || !act_found->module) {
+                                       ast_debug(1, "Running action '%s'\n", act_found->action);
+                                       ret = act_found->func(s, m);
+                                       acted = 1;
+                                       ast_module_unref(mod_ref);
                                }
+                       } else {
+                               ao2_unlock(act_found);
                        }
-                       ao2_unlock(act_found);
                }
                if (!acted) {
                        /*
index 76bdf87b09359dc753e74fe0c70768876a7e0d18..1c734778fd3c4da118451675af52228260ba1f37 100644 (file)
@@ -428,6 +428,7 @@ struct ast_rtp_instance *ast_rtp_instance_new(const char *engine_name,
        struct ast_sockaddr address = {{0,}};
        struct ast_rtp_instance *instance = NULL;
        struct ast_rtp_engine *engine = NULL;
+       struct ast_module *mod_ref;
 
        AST_RWLIST_RDLOCK(&engines);
 
@@ -450,10 +451,15 @@ struct ast_rtp_instance *ast_rtp_instance_new(const char *engine_name,
        }
 
        /* Bump up the reference count before we return so the module can not be unloaded */
-       ast_module_ref(engine->mod);
+       mod_ref = ast_module_running_ref(engine->mod);
 
        AST_RWLIST_UNLOCK(&engines);
 
+       if (!mod_ref) {
+               /* BUGBUG: improve handling of this situation. */
+               return NULL;
+       }
+
        /* Allocate a new RTP instance */
        if (!(instance = ao2_alloc(sizeof(*instance), instance_destructor))) {
                ast_module_unref(engine->mod);
index a55d5c72e36f3acf3286f6021a7ab7a09bd9dddb..c79675cd8c7521055d6b896354b3ccf8b7caf6ca 100644 (file)
@@ -835,16 +835,19 @@ enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sor
        RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, ao2_alloc(sizeof(*object_wizard), sorcery_object_wizard_destructor), ao2_cleanup);
        int created = 0;
 
-       if (!wizard) {
+       if (!object_wizard) {
+               return AST_SORCERY_APPLY_FAIL;
+       }
+
+       if (!wizard || wizard->callbacks.module != ast_module_running_ref(wizard->callbacks.module)) {
                ast_log(LOG_ERROR, "Wizard '%s' could not be applied to object type '%s' as it was not found\n",
                        name, type);
                return AST_SORCERY_APPLY_FAIL;
-       } else if (!object_wizard) {
-               return AST_SORCERY_APPLY_FAIL;
        }
 
        if (!object_type) {
                if (!(object_type = sorcery_object_type_alloc(type, module))) {
+                       ast_module_unref(wizard->callbacks.module);
                        return AST_SORCERY_APPLY_FAIL;
                }
                created = 1;
@@ -861,6 +864,7 @@ enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sor
                        ast_debug(1, "Wizard %s already applied to object type %s\n",
                                        wizard->callbacks.name, object_type->name);
                        AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+                       ast_module_unref(wizard->callbacks.module);
                        return AST_SORCERY_APPLY_DUPLICATE;
                }
        }
@@ -871,11 +875,10 @@ enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sor
                ast_log(LOG_WARNING, "Wizard '%s' failed to open mapping for object type '%s' with data: %s\n",
                        name, object_type->name, S_OR(data, ""));
                AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+               ast_module_unref(wizard->callbacks.module);
                return AST_SORCERY_APPLY_FAIL;
        }
 
-       ast_module_ref(wizard->callbacks.module);
-
        object_wizard->wizard = ao2_bump(wizard);
        object_wizard->caching = caching;
 
index c6a9480c32ba906baac19ae6f073f9eeab40748c..18852c652a6654f47ebbfe18584c437ad1eade38 100644 (file)
@@ -124,17 +124,22 @@ struct ast_timer *ast_timer_open(void)
        void *data = NULL;
        struct timing_holder *h;
        struct ast_timer *t = NULL;
+       int idx = 1;
 
        ast_heap_rdlock(timing_interfaces);
 
-       if ((h = ast_heap_peek(timing_interfaces, 1))) {
-               data = h->iface->timer_open();
-               ast_module_ref(h->mod);
+       while ((h = ast_heap_peek(timing_interfaces, idx))) {
+               if (ast_module_running_ref(h->mod)) {
+                       data = h->iface->timer_open();
+                       break;
+               }
+               idx++;
        }
 
        if (data) {
                if (!(t = ast_calloc(1, sizeof(*t)))) {
                        h->iface->timer_close(data);
+                       ast_module_unref(h->mod);
                } else {
                        t->data = data;
                        t->holder = h;
index 02717c5ed1ed6da989645146224bc9c937654efd..3d4905723c905efcc7582c7ae2b036dc0fc2df24 100644 (file)
@@ -342,7 +342,10 @@ static struct ast_trans_pvt *newpvt(struct ast_translator *t, struct ast_format
         */
        pvt->explicit_dst = ao2_bump(explicit_dst);
 
-       ast_module_ref(t->module);
+       if (!ast_module_running_ref(t->module)) {
+               ast_free(pvt);
+               return NULL;
+       }
 
        /* call local init routine, if present */
        if (t->newpvt && t->newpvt(pvt)) {
index 85914c0189caf3f880e325ad40c214c43c87407d..393a503511a2139ad592848eccc322c5b6cc3e3f 100644 (file)
@@ -4040,14 +4040,19 @@ static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, ch
 
        parse_args(buf, &argc, argv);
        c = find_command(argv, 0);
-       if (c && (!dead || (dead && c->dead))) {
-               /* if this command wasn't registered by res_agi, be sure to usecount
-               the module we are using */
-               if (c->mod != ast_module_info->self)
-                       ast_module_ref(c->mod);
+       if (!c || !ast_module_running_ref(c->mod)) {
+               ami_res = "Invalid or unknown command";
+               resultcode = 510;
+
+               ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res);
+
+               publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
+
+               return AGI_RESULT_SUCCESS;
+       }
+
+       if (!dead || (dead && c->dead)) {
                res = c->handler(chan, agi, argc, argv);
-               if (c->mod != ast_module_info->self)
-                       ast_module_unref(c->mod);
                switch (res) {
                case RESULT_SHOWUSAGE:
                        ami_res = "Usage";
@@ -4094,21 +4099,15 @@ static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, ch
 
                        break;
                }
-       } else if (c) {
+       } else {
                ami_res = "Command Not Permitted on a dead channel or intercept routine";
                resultcode = 511;
 
                ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res);
 
-               publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
-       } else {
-               ami_res = "Invalid or unknown command";
-               resultcode = 510;
-
-               ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res);
-
                publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
        }
+       ast_module_unref(c->mod);
 
        return AGI_RESULT_SUCCESS;
 }
index 0938452460b5515e3137129c96ccc32b1ef24948..4be5aee75a3c82e5cef46c78ae362ddee1b439a5 100644 (file)
@@ -1161,8 +1161,10 @@ static struct ast_fax_session *fax_session_reserve(struct ast_fax_session_detail
                if ((faxmod->tech->caps & details->caps) != details->caps) {
                        continue;
                }
+               if (!ast_module_running_ref(faxmod->tech->module)) {
+                       continue;
+               }
                ast_debug(4, "Reserving a FAX session from '%s'.\n", faxmod->tech->description);
-               ast_module_ref(faxmod->tech->module);
                s->tech = faxmod->tech;
                break;
        }
@@ -1279,8 +1281,10 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d
                        if ((faxmod->tech->caps & details->caps) != details->caps) {
                                continue;
                        }
+                       if (!ast_module_running_ref(faxmod->tech->module)) {
+                               continue;
+                       }
                        ast_debug(4, "Requesting a new FAX session from '%s'.\n", faxmod->tech->description);
-                       ast_module_ref(faxmod->tech->module);
                        if (reserved) {
                                /* Balance module ref from reserved session */
                                ast_module_unref(reserved->tech->module);