diff --git a/include/asterisk/module.h b/include/asterisk/module.h index d3b9ddc20964f85cdf136e29f8b0bf21e58aa849..103cd3053c30cc888dc1c77a542b0c32123f73e1 100644 --- a/include/asterisk/module.h +++ b/include/asterisk/module.h @@ -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 diff --git a/main/app.c b/main/app.c index 215ec04e8b2232786e19a1fb4561c23ca3099c97..2db9a8f5086039fcee559347fb274bae4f95ec08 100644 --- a/main/app.c +++ b/main/app.c @@ -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); diff --git a/main/bridge.c b/main/bridge.c index 88d9e548775577ad046f8f48b46518a32d338ef3..4f79852cb7a78113df9bea71aa09f455f41cf3d1 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -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); } diff --git a/main/cli.c b/main/cli.c index 1299c2aa7aea4aff7ef242535cd8c90509b528b7..80c1843281bff3a6af886bdd2e68e3599082cce3 100644 --- a/main/cli.c +++ b/main/cli.c @@ -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")); diff --git a/main/file.c b/main/file.c index 41131f9eebc3dbc151157dbea8b4d3a593143c3b..8dded812382054f29344267bf4a2753d5ae79da7 100644 --- a/main/file.c +++ b/main/file.c @@ -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; diff --git a/main/loader.c b/main/loader.c index fe18048efc9eaa01cf48d527539f20c879629e33..88c1cda97aeb4ebec6856a5c089cef6e5c747921 100644 --- a/main/loader.c +++ b/main/loader.c @@ -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) { diff --git a/main/manager.c b/main/manager.c index 576978c31c3a205b14f4d4e58ecfdad90ee553a3..3aa910501a5b827fe391d9a1533334dc3e916767 100644 --- a/main/manager.c +++ b/main/manager.c @@ -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) { /* diff --git a/main/rtp_engine.c b/main/rtp_engine.c index 76bdf87b09359dc753e74fe0c70768876a7e0d18..1c734778fd3c4da118451675af52228260ba1f37 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -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); diff --git a/main/sorcery.c b/main/sorcery.c index a55d5c72e36f3acf3286f6021a7ab7a09bd9dddb..c79675cd8c7521055d6b896354b3ccf8b7caf6ca 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -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; diff --git a/main/timing.c b/main/timing.c index c6a9480c32ba906baac19ae6f073f9eeab40748c..18852c652a6654f47ebbfe18584c437ad1eade38 100644 --- a/main/timing.c +++ b/main/timing.c @@ -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; diff --git a/main/translate.c b/main/translate.c index 02717c5ed1ed6da989645146224bc9c937654efd..3d4905723c905efcc7582c7ae2b036dc0fc2df24 100644 --- a/main/translate.c +++ b/main/translate.c @@ -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)) { diff --git a/res/res_agi.c b/res/res_agi.c index 85914c0189caf3f880e325ad40c214c43c87407d..393a503511a2139ad592848eccc322c5b6cc3e3f 100644 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -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; } diff --git a/res/res_fax.c b/res/res_fax.c index 0938452460b5515e3137129c96ccc32b1ef24948..4be5aee75a3c82e5cef46c78ae362ddee1b439a5 100644 --- a/res/res_fax.c +++ b/res/res_fax.c @@ -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);