Allow AMI action callback to be reentrant.

Fix AMI module reload deadlock regression from ASTERISK-18479 when it
tried to fix the race between calling an AMI action callback and
unregistering that action.  Refixes ASTERISK-13784 broken by
ASTERISK-17785 change.

Locking the ao2 object guaranteed that there were no active callbacks that
mattered when ast_manager_unregister() was called.  Unfortunately, this
causes the deadlock situation.  The patch stops locking the ao2 object to
allow multiple threads to invoke the callback re-entrantly.  There is no
way to guarantee a module unload will not crash because of an active
callback.  The code attempts to minimize the chance with the registered
flag and the maximum 5 second delay before ast_manager_unregister()
returns.

The trunk version of the patch changes the API to fix the race condition
correctly to prevent the module code from unloading from memory while an
action callback is active.

* Don't hold the lock while calling the AMI action callback.

(closes issue ASTERISK-19487)
Reported by: Philippe Lindheimer

Review: https://reviewboard.asterisk.org/r/1818/
Review: https://reviewboard.asterisk.org/r/1820/
........

Merged revisions 359979 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 359980 from http://svn.asterisk.org/svn/asterisk/branches/10


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@359981 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Richard Mudgett
2012-03-20 17:31:28 +00:00
parent 3714e8b1e5
commit 334f13d8b8
6 changed files with 91 additions and 58 deletions

View File

@@ -149,6 +149,7 @@ struct manager_action {
int authority;
/*! Function to be called */
int (*func)(struct mansession *s, const struct message *m);
struct ast_module *module; /*!< Module this action belongs to */
/*! Where the documentation come from. */
enum ast_doc_src docsrc;
/*! For easy linking */
@@ -164,29 +165,44 @@ struct manager_action {
/*! \brief External routines may register/unregister manager callbacks this way
* \note Use ast_manager_register2() to register with help text for new manager commands */
#define ast_manager_register(a, b, c, d) ast_manager_register2(a, b, c, d, NULL)
#define ast_manager_register(action, authority, func, synopsis) ast_manager_register2(action, authority, func, ast_module_info->self, synopsis, NULL)
/*! \brief Register a manager callback using XML documentation to describe the manager. */
#define ast_manager_register_xml(a, b, c) ast_manager_register2(a, b, c, NULL, NULL)
#define ast_manager_register_xml(action, authority, func) ast_manager_register2(action, authority, func, ast_module_info->self, NULL, NULL)
/*! \brief Register a manager command with the manager interface
\param action Name of the requested Action:
\param authority Required authority for this command
\param func Function to call for this command
\param synopsis Help text (one line, up to 30 chars) for CLI manager show commands
\param description Help text, several lines
/*!
* \brief Register a manager callback using XML documentation to describe the manager.
*
* \note For Asterisk core modules that are not independently
* loadable.
*
* \warning If you use ast_manager_register_xml() instead when
* you need to use this function, Asterisk will crash on load.
*/
#define ast_manager_register_xml_core(action, authority, func) ast_manager_register2(action, authority, func, NULL, NULL, NULL)
/*!
* \brief Register a manager command with the manager interface
* \param action Name of the requested Action:
* \param authority Required authority for this command
* \param func Function to call for this command
* \param module The module containing func. (NULL if module is part of core and not loadable)
* \param synopsis Help text (one line, up to 30 chars) for CLI manager show commands
* \param description Help text, several lines
*/
int ast_manager_register2(
const char *action,
int authority,
int (*func)(struct mansession *s, const struct message *m),
struct ast_module *module,
const char *synopsis,
const char *description);
/*! \brief Unregister a registered manager command
\param action Name of registered Action:
/*!
* \brief Unregister a registered manager command
* \param action Name of registered Action:
*/
int ast_manager_unregister( char *action );
int ast_manager_unregister(const char *action);
/*!
* \brief Verify a session's read permissions against a permission mask.

View File

@@ -3319,7 +3319,7 @@ int ast_data_init(void)
res |= ast_cli_register_multiple(cli_data, ARRAY_LEN(cli_data));
res |= ast_manager_register_xml("DataGet", 0, manager_data_get);
res |= ast_manager_register_xml_core("DataGet", 0, manager_data_get);
#ifdef TEST_FRAMEWORK
AST_TEST_REGISTER(test_data_get);

View File

@@ -933,9 +933,9 @@ int astdb_init(void)
ast_register_atexit(astdb_atexit);
ast_cli_register_multiple(cli_database, ARRAY_LEN(cli_database));
ast_manager_register_xml("DBGet", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_dbget);
ast_manager_register_xml("DBPut", EVENT_FLAG_SYSTEM, manager_dbput);
ast_manager_register_xml("DBDel", EVENT_FLAG_SYSTEM, manager_dbdel);
ast_manager_register_xml("DBDelTree", EVENT_FLAG_SYSTEM, manager_dbdeltree);
ast_manager_register_xml_core("DBGet", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_dbget);
ast_manager_register_xml_core("DBPut", EVENT_FLAG_SYSTEM, manager_dbput);
ast_manager_register_xml_core("DBDel", EVENT_FLAG_SYSTEM, manager_dbdel);
ast_manager_register_xml_core("DBDelTree", EVENT_FLAG_SYSTEM, manager_dbdeltree);
return 0;
}

View File

@@ -8193,9 +8193,9 @@ int ast_features_init(void)
if (!res)
res = ast_register_application2(parkcall, park_call_exec, NULL, NULL, NULL);
if (!res) {
ast_manager_register_xml("ParkedCalls", 0, manager_parking_status);
ast_manager_register_xml("Park", EVENT_FLAG_CALL, manager_park);
ast_manager_register_xml("Bridge", EVENT_FLAG_CALL, action_bridge);
ast_manager_register_xml_core("ParkedCalls", 0, manager_parking_status);
ast_manager_register_xml_core("Park", EVENT_FLAG_CALL, manager_park);
ast_manager_register_xml_core("Bridge", EVENT_FLAG_CALL, action_bridge);
}
res |= ast_devstate_prov_add("Park", metermaidstate);

View File

@@ -1975,7 +1975,15 @@ int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
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);
ret = act_found->func(&s, &m);
ao2_lock(act_found);
if (act_found->module) {
ast_module_unref(act_found->module);
}
} else {
ret = -1;
}
@@ -4789,8 +4797,16 @@ static int process_message(struct mansession *s, const struct message *m)
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);
}
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);
}
}
ao2_unlock(act_found);
}
@@ -5283,7 +5299,7 @@ int __ast_manager_event_multichan(int category, const char *event, int chancount
/*! \brief
* support functions to register/unregister AMI action handlers,
*/
int ast_manager_unregister(char *action)
int ast_manager_unregister(const char *action)
{
struct manager_action *cur;
@@ -5378,7 +5394,7 @@ static void action_destroy(void *obj)
/*! \brief register a new command with manager, including online help. This is
the preferred way to register a manager command */
int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), struct ast_module *module, const char *synopsis, const char *description)
{
struct manager_action *cur;
@@ -5394,6 +5410,7 @@ int ast_manager_register2(const char *action, int auth, int (*func)(struct manse
cur->action = action;
cur->authority = auth;
cur->func = func;
cur->module = module;
#ifdef AST_XML_DOCS
if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
char *tmpxml;
@@ -6658,40 +6675,40 @@ static int __init_manager(int reload)
if (!registered) {
/* Register default actions */
ast_manager_register_xml("Ping", 0, action_ping);
ast_manager_register_xml("Events", 0, action_events);
ast_manager_register_xml("Logoff", 0, action_logoff);
ast_manager_register_xml("Login", 0, action_login);
ast_manager_register_xml("Challenge", 0, action_challenge);
ast_manager_register_xml("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup);
ast_manager_register_xml("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status);
ast_manager_register_xml("Setvar", EVENT_FLAG_CALL, action_setvar);
ast_manager_register_xml("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar);
ast_manager_register_xml("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig);
ast_manager_register_xml("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson);
ast_manager_register_xml("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig);
ast_manager_register_xml("CreateConfig", EVENT_FLAG_CONFIG, action_createconfig);
ast_manager_register_xml("ListCategories", EVENT_FLAG_CONFIG, action_listcategories);
ast_manager_register_xml("Redirect", EVENT_FLAG_CALL, action_redirect);
ast_manager_register_xml("Atxfer", EVENT_FLAG_CALL, action_atxfer);
ast_manager_register_xml("Originate", EVENT_FLAG_ORIGINATE, action_originate);
ast_manager_register_xml("Command", EVENT_FLAG_COMMAND, action_command);
ast_manager_register_xml("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate);
ast_manager_register_xml("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout);
ast_manager_register_xml("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus);
ast_manager_register_xml("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount);
ast_manager_register_xml("ListCommands", 0, action_listcommands);
ast_manager_register_xml("SendText", EVENT_FLAG_CALL, action_sendtext);
ast_manager_register_xml("UserEvent", EVENT_FLAG_USER, action_userevent);
ast_manager_register_xml("WaitEvent", 0, action_waitevent);
ast_manager_register_xml("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings);
ast_manager_register_xml("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus);
ast_manager_register_xml("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload);
ast_manager_register_xml("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels);
ast_manager_register_xml("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload);
ast_manager_register_xml("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck);
ast_manager_register_xml("AOCMessage", EVENT_FLAG_AOC, action_aocmessage);
ast_manager_register_xml("Filter", EVENT_FLAG_SYSTEM, action_filter);
ast_manager_register_xml_core("Ping", 0, action_ping);
ast_manager_register_xml_core("Events", 0, action_events);
ast_manager_register_xml_core("Logoff", 0, action_logoff);
ast_manager_register_xml_core("Login", 0, action_login);
ast_manager_register_xml_core("Challenge", 0, action_challenge);
ast_manager_register_xml_core("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup);
ast_manager_register_xml_core("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status);
ast_manager_register_xml_core("Setvar", EVENT_FLAG_CALL, action_setvar);
ast_manager_register_xml_core("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar);
ast_manager_register_xml_core("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig);
ast_manager_register_xml_core("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson);
ast_manager_register_xml_core("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig);
ast_manager_register_xml_core("CreateConfig", EVENT_FLAG_CONFIG, action_createconfig);
ast_manager_register_xml_core("ListCategories", EVENT_FLAG_CONFIG, action_listcategories);
ast_manager_register_xml_core("Redirect", EVENT_FLAG_CALL, action_redirect);
ast_manager_register_xml_core("Atxfer", EVENT_FLAG_CALL, action_atxfer);
ast_manager_register_xml_core("Originate", EVENT_FLAG_ORIGINATE, action_originate);
ast_manager_register_xml_core("Command", EVENT_FLAG_COMMAND, action_command);
ast_manager_register_xml_core("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate);
ast_manager_register_xml_core("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout);
ast_manager_register_xml_core("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus);
ast_manager_register_xml_core("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount);
ast_manager_register_xml_core("ListCommands", 0, action_listcommands);
ast_manager_register_xml_core("SendText", EVENT_FLAG_CALL, action_sendtext);
ast_manager_register_xml_core("UserEvent", EVENT_FLAG_USER, action_userevent);
ast_manager_register_xml_core("WaitEvent", 0, action_waitevent);
ast_manager_register_xml_core("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings);
ast_manager_register_xml_core("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus);
ast_manager_register_xml_core("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload);
ast_manager_register_xml_core("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels);
ast_manager_register_xml_core("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload);
ast_manager_register_xml_core("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck);
ast_manager_register_xml_core("AOCMessage", EVENT_FLAG_AOC, action_aocmessage);
ast_manager_register_xml_core("Filter", EVENT_FLAG_SYSTEM, action_filter);
ast_cli_register_multiple(cli_manager, ARRAY_LEN(cli_manager));
ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);

View File

@@ -10469,7 +10469,7 @@ int load_pbx(void)
}
/* Register manager application */
ast_manager_register_xml("ShowDialPlan", EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING, manager_show_dialplan);
ast_manager_register_xml_core("ShowDialPlan", EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING, manager_show_dialplan);
if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "pbx Device State Change", NULL,
AST_EVENT_IE_END))) {