<p>Corey Farrell has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/7770">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">loader: Create ast_module_running_ref.<br><br>This function returns NULL if the module in question is not running. I<br>did not change ast_module_ref as most callers do not check the result<br>and they always call ast_module_unref.<br><br>Make use of this function when running registered items from:<br>* app_stack API's<br>* bridge technologies<br>* CLI commands<br>* File formats<br>* Manager Actions<br>* RTP engines<br>* Sorcery Wizards<br>* Timing Interfaces<br>* Translators<br>* AGI Commands<br>* Fax Technologies<br><br>ASTERISK-20346 #close<br><br>Change-Id: Ia16fd28e188b2fc0b9d18b8a5d9cacc31df73fcc<br>---<br>M include/asterisk/module.h<br>M main/app.c<br>M main/bridge.c<br>M main/cli.c<br>M main/file.c<br>M main/loader.c<br>M main/manager.c<br>M main/rtp_engine.c<br>M main/sorcery.c<br>M main/timing.c<br>M main/translate.c<br>M res/res_agi.c<br>M res/res_fax.c<br>13 files changed, 143 insertions(+), 78 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/70/7770/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/include/asterisk/module.h b/include/asterisk/module.h<br>index d3b9ddc..103cd30 100644<br>--- a/include/asterisk/module.h<br>+++ b/include/asterisk/module.h<br>@@ -357,6 +357,7 @@<br> #define ast_module_user_hangup_all() __ast_module_user_hangup_all(AST_MODULE_SELF)<br> <br> struct ast_module *__ast_module_ref(struct ast_module *mod, const char *file, int line, const char *func);<br>+struct ast_module *__ast_module_running_ref(struct ast_module *mod, const char *file, int line, const char *func);<br> void __ast_module_shutdown_ref(struct ast_module *mod, const char *file, int line, const char *func);<br> void __ast_module_unref(struct ast_module *mod, const char *file, int line, const char *func);<br> <br>@@ -369,6 +370,20 @@<br> * from being unloaded.<br> */<br> #define ast_module_ref(mod) __ast_module_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__)<br>+<br>+/*!<br>+ * \brief Hold a reference to the module if it is running.<br>+ * \param mod Module to reference<br>+ * \retval mod if running<br>+ * \retval NULL if not running<br>+ *<br>+ * The returned pointer should be released with ast_module_unref.<br>+ *<br>+ * \note A module reference will prevent the module from being unloaded.<br>+ */<br>+#define ast_module_running_ref(mod) \<br>+ __ast_module_running_ref(mod, __FILE__, __LINE__, __PRETTY_FUNCTION__)<br>+<br> /*!<br> * \brief Prevent unload of the module before shutdown<br> * \param mod Module to hold<br>diff --git a/main/app.c b/main/app.c<br>index 215ec04..2db9a8f 100644<br>--- a/main/app.c<br>+++ b/main/app.c<br>@@ -386,6 +386,7 @@<br> return res;<br> }<br> <br>+/* BUGBUG this is not thread safe. */<br> static const struct ast_app_stack_funcs *app_stack_callbacks;<br> <br> void ast_install_stack_functions(const struct ast_app_stack_funcs *funcs)<br>@@ -399,16 +400,16 @@<br> const char *new_args;<br> <br> funcs = app_stack_callbacks;<br>- if (!funcs || !funcs->expand_sub_args) {<br>+ if (!funcs || !funcs->expand_sub_args || !ast_module_running_ref(funcs->module)) {<br> ast_log(LOG_WARNING,<br> "Cannot expand 'Gosub(%s)' arguments. The app_stack module is not available.\n",<br> args);<br> return NULL;<br> }<br>- ast_module_ref(funcs->module);<br> <br> new_args = funcs->expand_sub_args(chan, args);<br> ast_module_unref(funcs->module);<br>+<br> return new_args;<br> }<br> <br>@@ -418,13 +419,12 @@<br> int res;<br> <br> funcs = app_stack_callbacks;<br>- if (!funcs || !funcs->run_sub) {<br>+ if (!funcs || !funcs->run_sub || !ast_module_running_ref(funcs->module)) {<br> ast_log(LOG_WARNING,<br> "Cannot run 'Gosub(%s)'. The app_stack module is not available.\n",<br> sub_args);<br> return -1;<br> }<br>- ast_module_ref(funcs->module);<br> <br> if (autoservice_chan) {<br> ast_autoservice_start(autoservice_chan);<br>diff --git a/main/bridge.c b/main/bridge.c<br>index 88d9e54..48f7a49 100644<br>--- a/main/bridge.c<br>+++ b/main/bridge.c<br>@@ -522,8 +522,13 @@<br> <br> if (best) {<br> /* Increment it's module reference count if present so it does not get unloaded while in use */<br>- ast_module_ref(best->mod);<br>- ast_debug(1, "Chose bridge technology %s\n", best->name);<br>+ if (ast_module_running_ref(best->mod)) {<br>+ ast_debug(1, "Chose bridge technology %s\n", best->name);<br>+ } else {<br>+ /* The bridge module is not actually running. */<br>+ /* BUGBUG: improve handling of this situation. */<br>+ best = NULL;<br>+ }<br> }<br> <br> AST_RWLIST_UNLOCK(&bridge_technologies);<br>diff --git a/main/cli.c b/main/cli.c<br>index 1299c2a..214ead7 100644<br>--- a/main/cli.c<br>+++ b/main/cli.c<br>@@ -2679,9 +2679,11 @@<br> .n = state - matchnum,<br> .argv = argv,<br> .argc = x};<br>- ast_module_ref(e->module);<br>- ret = e->handler(e, CLI_GENERATE, &a);<br>- ast_module_unref(e->module);<br>+<br>+ if (!e->module || ast_module_running_ref(e->module)) {<br>+ ret = e->handler(e, CLI_GENERATE, &a);<br>+ ast_module_unref(e->module);<br>+ }<br> }<br> if (ret)<br> break;<br>@@ -2760,9 +2762,10 @@<br> */<br> args[0] = (char *)e;<br> <br>- ast_module_ref(e->module);<br>- retval = e->handler(e, CLI_HANDLER, &a);<br>- ast_module_unref(e->module);<br>+ if (!e->module || ast_module_running_ref(e->module)) {<br>+ retval = e->handler(e, CLI_HANDLER, &a);<br>+ ast_module_unref(e->module);<br>+ }<br> <br> if (retval == CLI_SHOWUSAGE) {<br> ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n"));<br>diff --git a/main/file.c b/main/file.c<br>index 41131f9..8dded81 100644<br>--- a/main/file.c<br>+++ b/main/file.c<br>@@ -425,11 +425,17 @@<br> static struct ast_filestream *get_filestream(struct ast_format_def *fmt, FILE *bfile)<br> {<br> struct ast_filestream *s;<br>-<br> int l = sizeof(*s) + fmt->buf_size + fmt->desc_size; /* total allocation size */<br>- if ( (s = ao2_alloc(l, filestream_destructor)) == NULL)<br>+<br>+ if (!ast_module_running_ref(fmt->module)) {<br> return NULL;<br>- ast_module_ref(fmt->module);<br>+ }<br>+<br>+ s = ao2_alloc(l, filestream_destructor);<br>+ if (!s) {<br>+ ast_module_unref(fmt->module);<br>+ return NULL;<br>+ }<br> s->fmt = fmt;<br> s->f = bfile;<br> <br>diff --git a/main/loader.c b/main/loader.c<br>index fe18048..88c1cda 100644<br>--- a/main/loader.c<br>+++ b/main/loader.c<br>@@ -1702,6 +1702,16 @@<br> return mod;<br> }<br> <br>+struct ast_module *__ast_module_running_ref(struct ast_module *mod,<br>+ const char *file, int line, const char *func)<br>+{<br>+ if (!mod || !mod->flags.running) {<br>+ return NULL;<br>+ }<br>+<br>+ return __ast_module_ref(mod, file, line, func);<br>+}<br>+<br> void __ast_module_shutdown_ref(struct ast_module *mod, const char *file, int line, const char *func)<br> {<br> if (!mod || mod->flags.keepuntilshutdown) {<br>diff --git a/main/manager.c b/main/manager.c<br>index 576978c..60e0d43 100644<br>--- a/main/manager.c<br>+++ b/main/manager.c<br>@@ -2923,34 +2923,40 @@<br> }<br> <br> action = astman_get_header(&m, "Action");<br>- if (strcasecmp(action, "login")) {<br>- act_found = action_find(action);<br>- if (act_found) {<br>- /*<br>- * we have to simulate a session for this action request<br>- * to be able to pass it down for processing<br>- * This is necessary to meet the previous design of manager.c<br>- */<br>- s.hook = hook;<br> <br>- ao2_lock(act_found);<br>- if (act_found->registered && act_found->func) {<br>- if (act_found->module) {<br>- ast_module_ref(act_found->module);<br>- }<br>- ao2_unlock(act_found);<br>- ret = act_found->func(&s, &m);<br>- ao2_lock(act_found);<br>- if (act_found->module) {<br>- ast_module_unref(act_found->module);<br>- }<br>- } else {<br>- ret = -1;<br>- }<br>- ao2_unlock(act_found);<br>- ao2_t_ref(act_found, -1, "done with found action object");<br>+ do {<br>+ if (!strcasecmp(action, "login")) {<br>+ break;<br> }<br>- }<br>+<br>+ act_found = action_find(action);<br>+ if (!act_found) {<br>+ break;<br>+ }<br>+<br>+ /*<br>+ * we have to simulate a session for this action request<br>+ * to be able to pass it down for processing<br>+ * This is necessary to meet the previous design of manager.c<br>+ */<br>+ s.hook = hook;<br>+<br>+ ret = -1;<br>+ ao2_lock(act_found);<br>+ if (act_found->registered && act_found->func) {<br>+ struct ast_module *mod_ref = ast_module_running_ref(act_found->module);<br>+<br>+ ao2_unlock(act_found);<br>+ if (mod_ref || !act_found->module) {<br>+ ret = act_found->func(&s, &m);<br>+ ast_module_unref(mod_ref);<br>+ }<br>+ } else {<br>+ ao2_unlock(act_found);<br>+ }<br>+ ao2_t_ref(act_found, -1, "done with found action object");<br>+ } while (0);<br>+<br> ast_free(dup_str);<br> return ret;<br> }<br>@@ -6442,21 +6448,21 @@<br> if ((s->session->writeperm & act_found->authority)<br> || act_found->authority == 0) {<br> /* We have the authority to execute the action. */<br>+ ret = -1;<br> ao2_lock(act_found);<br> if (act_found->registered && act_found->func) {<br>- ast_debug(1, "Running action '%s'\n", act_found->action);<br>- if (act_found->module) {<br>- ast_module_ref(act_found->module);<br>- }<br>+ struct ast_module *mod_ref = ast_module_running_ref(act_found->module);<br>+<br> ao2_unlock(act_found);<br>- ret = act_found->func(s, m);<br>- acted = 1;<br>- ao2_lock(act_found);<br>- if (act_found->module) {<br>- ast_module_unref(act_found->module);<br>+ if (mod_ref || !act_found->module) {<br>+ ast_debug(1, "Running action '%s'\n", act_found->action);<br>+ ret = act_found->func(s, m);<br>+ acted = 1;<br>+ ast_module_unref(mod_ref);<br> }<br>+ } else {<br>+ ao2_unlock(act_found);<br> }<br>- ao2_unlock(act_found);<br> }<br> if (!acted) {<br> /*<br>diff --git a/main/rtp_engine.c b/main/rtp_engine.c<br>index 76bdf87..1c73477 100644<br>--- a/main/rtp_engine.c<br>+++ b/main/rtp_engine.c<br>@@ -428,6 +428,7 @@<br> struct ast_sockaddr address = {{0,}};<br> struct ast_rtp_instance *instance = NULL;<br> struct ast_rtp_engine *engine = NULL;<br>+ struct ast_module *mod_ref;<br> <br> AST_RWLIST_RDLOCK(&engines);<br> <br>@@ -450,10 +451,15 @@<br> }<br> <br> /* Bump up the reference count before we return so the module can not be unloaded */<br>- ast_module_ref(engine->mod);<br>+ mod_ref = ast_module_running_ref(engine->mod);<br> <br> AST_RWLIST_UNLOCK(&engines);<br> <br>+ if (!mod_ref) {<br>+ /* BUGBUG: improve handling of this situation. */<br>+ return NULL;<br>+ }<br>+<br> /* Allocate a new RTP instance */<br> if (!(instance = ao2_alloc(sizeof(*instance), instance_destructor))) {<br> ast_module_unref(engine->mod);<br>diff --git a/main/sorcery.c b/main/sorcery.c<br>index 51b55c5..eb78487 100644<br>--- a/main/sorcery.c<br>+++ b/main/sorcery.c<br>@@ -1011,16 +1011,19 @@<br> RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, ao2_alloc(sizeof(*object_wizard), sorcery_object_wizard_destructor), ao2_cleanup);<br> int created = 0;<br> <br>- if (!wizard) {<br>+ if (!object_wizard) {<br>+ return AST_SORCERY_APPLY_FAIL;<br>+ }<br>+<br>+ if (!wizard || wizard->callbacks.module != ast_module_running_ref(wizard->callbacks.module)) {<br> ast_log(LOG_ERROR, "Wizard '%s' could not be applied to object type '%s' as it was not found\n",<br> name, type);<br>- return AST_SORCERY_APPLY_FAIL;<br>- } else if (!object_wizard) {<br> return AST_SORCERY_APPLY_FAIL;<br> }<br> <br> if (!object_type) {<br> if (!(object_type = sorcery_object_type_alloc(type, module))) {<br>+ ast_module_unref(wizard->callbacks.module);<br> return AST_SORCERY_APPLY_FAIL;<br> }<br> created = 1;<br>@@ -1037,6 +1040,7 @@<br> ast_debug(1, "Wizard %s already applied to object type %s\n",<br> wizard->callbacks.name, object_type->name);<br> AST_VECTOR_RW_UNLOCK(&object_type->wizards);<br>+ ast_module_unref(wizard->callbacks.module);<br> return AST_SORCERY_APPLY_DUPLICATE;<br> }<br> }<br>@@ -1047,10 +1051,9 @@<br> ast_log(LOG_WARNING, "Wizard '%s' failed to open mapping for object type '%s' with data: %s\n",<br> name, object_type->name, S_OR(data, ""));<br> AST_VECTOR_RW_UNLOCK(&object_type->wizards);<br>+ ast_module_unref(wizard->callbacks.module);<br> return AST_SORCERY_APPLY_FAIL;<br> }<br>-<br>- ast_module_ref(wizard->callbacks.module);<br> <br> object_wizard->wizard = ao2_bump(wizard);<br> object_wizard->caching = caching;<br>diff --git a/main/timing.c b/main/timing.c<br>index c6a9480..18852c6 100644<br>--- a/main/timing.c<br>+++ b/main/timing.c<br>@@ -124,17 +124,22 @@<br> void *data = NULL;<br> struct timing_holder *h;<br> struct ast_timer *t = NULL;<br>+ int idx = 1;<br> <br> ast_heap_rdlock(timing_interfaces);<br> <br>- if ((h = ast_heap_peek(timing_interfaces, 1))) {<br>- data = h->iface->timer_open();<br>- ast_module_ref(h->mod);<br>+ while ((h = ast_heap_peek(timing_interfaces, idx))) {<br>+ if (ast_module_running_ref(h->mod)) {<br>+ data = h->iface->timer_open();<br>+ break;<br>+ }<br>+ idx++;<br> }<br> <br> if (data) {<br> if (!(t = ast_calloc(1, sizeof(*t)))) {<br> h->iface->timer_close(data);<br>+ ast_module_unref(h->mod);<br> } else {<br> t->data = data;<br> t->holder = h;<br>diff --git a/main/translate.c b/main/translate.c<br>index 02717c5..3d49057 100644<br>--- a/main/translate.c<br>+++ b/main/translate.c<br>@@ -342,7 +342,10 @@<br> */<br> pvt->explicit_dst = ao2_bump(explicit_dst);<br> <br>- ast_module_ref(t->module);<br>+ if (!ast_module_running_ref(t->module)) {<br>+ ast_free(pvt);<br>+ return NULL;<br>+ }<br> <br> /* call local init routine, if present */<br> if (t->newpvt && t->newpvt(pvt)) {<br>diff --git a/res/res_agi.c b/res/res_agi.c<br>index 85914c0..393a503 100644<br>--- a/res/res_agi.c<br>+++ b/res/res_agi.c<br>@@ -4040,14 +4040,19 @@<br> <br> parse_args(buf, &argc, argv);<br> c = find_command(argv, 0);<br>- if (c && (!dead || (dead && c->dead))) {<br>- /* if this command wasn't registered by res_agi, be sure to usecount<br>- the module we are using */<br>- if (c->mod != ast_module_info->self)<br>- ast_module_ref(c->mod);<br>+ if (!c || !ast_module_running_ref(c->mod)) {<br>+ ami_res = "Invalid or unknown command";<br>+ resultcode = 510;<br>+<br>+ ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res);<br>+<br>+ publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);<br>+<br>+ return AGI_RESULT_SUCCESS;<br>+ }<br>+<br>+ if (!dead || (dead && c->dead)) {<br> res = c->handler(chan, agi, argc, argv);<br>- if (c->mod != ast_module_info->self)<br>- ast_module_unref(c->mod);<br> switch (res) {<br> case RESULT_SHOWUSAGE:<br> ami_res = "Usage";<br>@@ -4094,21 +4099,15 @@<br> <br> break;<br> }<br>- } else if (c) {<br>+ } else {<br> ami_res = "Command Not Permitted on a dead channel or intercept routine";<br> resultcode = 511;<br> <br> ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res);<br> <br> publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);<br>- } else {<br>- ami_res = "Invalid or unknown command";<br>- resultcode = 510;<br>-<br>- ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res);<br>-<br>- publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);<br> }<br>+ ast_module_unref(c->mod);<br> <br> return AGI_RESULT_SUCCESS;<br> }<br>diff --git a/res/res_fax.c b/res/res_fax.c<br>index 0938452..4be5aee 100644<br>--- a/res/res_fax.c<br>+++ b/res/res_fax.c<br>@@ -1161,8 +1161,10 @@<br> if ((faxmod->tech->caps & details->caps) != details->caps) {<br> continue;<br> }<br>+ if (!ast_module_running_ref(faxmod->tech->module)) {<br>+ continue;<br>+ }<br> ast_debug(4, "Reserving a FAX session from '%s'.\n", faxmod->tech->description);<br>- ast_module_ref(faxmod->tech->module);<br> s->tech = faxmod->tech;<br> break;<br> }<br>@@ -1279,8 +1281,10 @@<br> if ((faxmod->tech->caps & details->caps) != details->caps) {<br> continue;<br> }<br>+ if (!ast_module_running_ref(faxmod->tech->module)) {<br>+ continue;<br>+ }<br> ast_debug(4, "Requesting a new FAX session from '%s'.\n", faxmod->tech->description);<br>- ast_module_ref(faxmod->tech->module);<br> if (reserved) {<br> /* Balance module ref from reserved session */<br> ast_module_unref(reserved->tech->module);<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/7770">change 7770</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/7770"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: Ia16fd28e188b2fc0b9d18b8a5d9cacc31df73fcc </div>
<div style="display:none"> Gerrit-Change-Number: 7770 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Corey Farrell <git@cfware.com> </div>