Split AGI manager events, to remove SubEvent field.

This moves them to stasis, in the process.

(closes issue ASTERISK-21470)
Review: https://reviewboard.asterisk.org/r/2587/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@390701 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Jason Parker 12 years ago
parent 9f54568010
commit 0613100e63

@ -106,6 +106,13 @@ AMI (Asterisk Manager Interface)
core, and is now two events: Hold and Unhold. The status field has been core, and is now two events: Hold and Unhold. The status field has been
removed. removed.
AGI (Asterisk Gateway Interface)
------------------
* The manager event AGIExec has been split into AGIExecStart and AGIExecEnd.
* The manager event AsyncAGI has been split into AsyncAGIStart, AsyncAGIExec,
and AsyncAGIEnd.
Channel Drivers Channel Drivers
------------------ ------------------
* When a channel driver is configured to enable jiterbuffers, they are now * When a channel driver is configured to enable jiterbuffers, they are now

@ -66,6 +66,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/srv.h" #include "asterisk/srv.h"
#include "asterisk/test.h" #include "asterisk/test.h"
#include "asterisk/netsock2.h" #include "asterisk/netsock2.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/stasis_message_router.h"
#define AST_API_MODULE #define AST_API_MODULE
#include "asterisk/agi.h" #include "asterisk/agi.h"
@ -928,6 +930,68 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Add an AGI command to the execute queue of the channel in Async AGI.</para> <para>Add an AGI command to the execute queue of the channel in Async AGI.</para>
</description> </description>
</manager> </manager>
<managerEvent language="en_US" name="AsyncAGIStart">
<managerEventInstance class="EVENT_FLAG_AGI">
<synopsis>Raised when a channel starts AsyncAGI command processing.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
<parameter name="Env">
<para>URL encoded string read from the AsyncAGI server.</para>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AsyncAGIEnd">
<managerEventInstance class="EVENT_FLAG_AGI">
<synopsis>Raised when a channel stops AsyncAGI command processing.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AsyncAGIExec">
<managerEventInstance class="EVENT_FLAG_AGI">
<synopsis>Raised when AsyncAGI completes an AGI command.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
<parameter name="CommandID" required="false">
<para>Optional command ID sent by the AsyncAGI server to identify the command.</para>
</parameter>
<parameter name="Result">
<para>URL encoded result string from the executed AGI command.</para>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AGIExecStart">
<managerEventInstance class="EVENT_FLAG_AGI">
<synopsis>Raised when a received AGI command starts processing.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
<parameter name="Command">
<para>The AGI command as received from the external source.</para>
</parameter>
<parameter name="CommandId">
<para>Random identification number assigned to the execution of this command.</para>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AGIExecEnd">
<managerEventInstance class="EVENT_FLAG_AGI">
<synopsis>Raised when a received AGI command completes processing.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AGIExecStart']/managerEventInstance/syntax/parameter)" />
<parameter name="ResultCode">
<para>The numeric result code from AGI</para>
</parameter
<parameter name="Result">
<para>The text result reason from AGI</para>
</parameter
</syntax>
</managerEventInstance>
</managerEvent>
***/ ***/
#define MAX_ARGS 128 #define MAX_ARGS 128
@ -963,6 +1027,44 @@ enum agi_result {
AGI_RESULT_HANGUP, AGI_RESULT_HANGUP,
}; };
struct stasis_message_type *agi_exec_start_type(void);
struct stasis_message_type *agi_exec_end_type(void);
struct stasis_message_type *agi_async_start_type(void);
struct stasis_message_type *agi_async_exec_type(void);
struct stasis_message_type *agi_async_end_type(void);
STASIS_MESSAGE_TYPE_DEFN(agi_exec_start_type);
STASIS_MESSAGE_TYPE_DEFN(agi_exec_end_type);
STASIS_MESSAGE_TYPE_DEFN(agi_async_start_type);
STASIS_MESSAGE_TYPE_DEFN(agi_async_exec_type);
STASIS_MESSAGE_TYPE_DEFN(agi_async_end_type);
static void agi_channel_manager_event(void *data,
struct stasis_subscription *sub, struct stasis_topic *topic,
struct stasis_message *message)
{
const char *type = data;
struct ast_channel_blob *obj = stasis_message_data(message);
RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
channel_event_string = ast_manager_build_channel_state_string(obj->snapshot);
if (!channel_event_string) {
return;
}
event_string = ast_manager_str_from_json_object(obj->blob, NULL);
if (!event_string) {
return;
}
manager_event(EVENT_FLAG_AGI, type,
"%s"
"%s",
ast_str_buffer(channel_event_string),
ast_str_buffer(event_string));
}
static agi_command *find_command(const char * const cmds[], int exact); static agi_command *find_command(const char * const cmds[], int exact);
AST_THREADSTORAGE(agi_buf); AST_THREADSTORAGE(agi_buf);
@ -1300,6 +1402,7 @@ static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], i
char ami_buffer[AMI_BUF_SIZE]; char ami_buffer[AMI_BUF_SIZE];
enum agi_result returnstatus = AGI_RESULT_SUCCESS; enum agi_result returnstatus = AGI_RESULT_SUCCESS;
AGI async_agi; AGI async_agi;
RAII_VAR(struct ast_json *, startblob, NULL, ast_json_unref);
if (efd) { if (efd) {
ast_log(LOG_WARNING, "Async AGI does not support Enhanced AGI yet\n"); ast_log(LOG_WARNING, "Async AGI does not support Enhanced AGI yet\n");
@ -1349,32 +1452,9 @@ static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], i
care of AGI commands on this channel can decide which AGI commands care of AGI commands on this channel can decide which AGI commands
to execute based on the setup info */ to execute based on the setup info */
ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, ast_uri_http); ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, ast_uri_http);
/*** DOCUMENTATION startblob = ast_json_pack("{s: s}", "Env", ami_buffer);
<managerEventInstance> ast_channel_publish_blob(chan, agi_async_start_type(), startblob);
<synopsis>Raised when a channel starts AsyncAGI command processing.</synopsis>
<syntax>
<parameter name="SubEvent">
<para>A sub event type, specifying the channel AsyncAGI processing status.</para>
<enumlist>
<enum name="Start"/>
<enum name="Exec"/>
<enum name="End"/>
</enumlist>
</parameter>
<parameter name="Env">
<para>URL encoded string read from the AsyncAGI server.</para>
</parameter>
</syntax>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGI, "AsyncAGI",
"SubEvent: Start\r\n"
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
"Env: %s\r\n",
ast_channel_name(chan),
ast_channel_uniqueid(chan),
ami_buffer);
hungup = ast_check_hangup(chan); hungup = ast_check_hangup(chan);
for (;;) { for (;;) {
/* /*
@ -1382,6 +1462,7 @@ static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], i
* the manager or the cli threads. * the manager or the cli threads.
*/ */
while (!hungup) { while (!hungup) {
RAII_VAR(struct ast_json *, execblob, NULL, ast_json_unref);
res = get_agi_cmd(chan, &cmd); res = get_agi_cmd(chan, &cmd);
if (res) { if (res) {
@ -1413,40 +1494,13 @@ static enum agi_result launch_asyncagi(struct ast_channel *chan, char *argv[], i
*/ */
agi_buffer[res] = '\0'; agi_buffer[res] = '\0';
ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, ast_uri_http); ast_uri_encode(agi_buffer, ami_buffer, AMI_BUF_SIZE, ast_uri_http);
if (ast_strlen_zero(cmd->cmd_id)) {
manager_event(EVENT_FLAG_AGI, "AsyncAGI", execblob = ast_json_pack("{s: s}", "Result", ami_buffer);
"SubEvent: Exec\r\n" if (execblob && !ast_strlen_zero(cmd->cmd_id)) {
"Channel: %s\r\n" ast_json_object_set(execblob, "CommandId", ast_json_string_create(cmd->cmd_id));
"Uniqueid: %s\r\n"
"Result: %s\r\n",
ast_channel_name(chan),
ast_channel_uniqueid(chan),
ami_buffer);
} else {
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when AsyncAGI completes an AGI command.</synopsis>
<syntax>
<parameter name="CommandID" required="false">
<para>Optional command ID sent by the AsyncAGI server to identify the command.</para>
</parameter>
<parameter name="Result">
<para>URL encoded result string from the executed AGI command.</para>
</parameter>
</syntax>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGI, "AsyncAGI",
"SubEvent: Exec\r\n"
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
"CommandID: %s\r\n"
"Result: %s\r\n",
ast_channel_name(chan),
ast_channel_uniqueid(chan),
cmd->cmd_id,
ami_buffer);
} }
ast_channel_publish_blob(chan, agi_async_exec_type(), execblob);
free_agi_cmd(cmd); free_agi_cmd(cmd);
/* /*
@ -1505,17 +1559,7 @@ async_agi_done:
ast_speech_destroy(async_agi.speech); ast_speech_destroy(async_agi.speech);
} }
/* notify manager users this channel cannot be controlled anymore by Async AGI */ /* notify manager users this channel cannot be controlled anymore by Async AGI */
/*** DOCUMENTATION ast_channel_publish_blob(chan, agi_async_end_type(), NULL);
<managerEventInstance>
<synopsis>Raised when a channel stops AsyncAGI command processing.</synopsis>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGI, "AsyncAGI",
"SubEvent: End\r\n"
"Channel: %s\r\n"
"Uniqueid: %s\r\n",
ast_channel_name(chan),
ast_channel_uniqueid(chan));
async_agi_abort: async_agi_abort:
/* close the pipe */ /* close the pipe */
@ -3546,47 +3590,34 @@ normal:
return 0; return 0;
} }
static void publish_async_exec_end(struct ast_channel *chan, int command_id, const char *command, int result_code, const char *result)
{
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
blob = ast_json_pack("{s: i, s: s, s: i, s: s}",
"CommandId", command_id,
"Command", command,
"ResultCode", result_code,
"Result", result);
ast_channel_publish_blob(chan, agi_exec_end_type(), blob);
}
static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead) static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead)
{ {
const char *argv[MAX_ARGS]; const char *argv[MAX_ARGS];
int argc = MAX_ARGS; int argc = MAX_ARGS;
int res; int res;
agi_command *c; agi_command *c;
const char *ami_res;
char *ami_cmd = ast_strdupa(buf); char *ami_cmd = ast_strdupa(buf);
const char *ami_res;
int command_id = ast_random(); int command_id = ast_random();
int resultcode; int resultcode = 0;
RAII_VAR(struct ast_json *, startblob, NULL, ast_json_unref);
startblob = ast_json_pack("{s: i, s: s}",
"CommandId", command_id,
"Command", ami_cmd);
ast_channel_publish_blob(chan, agi_exec_start_type(), startblob);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a received AGI command starts processing.</synopsis>
<syntax>
<parameter name="SubEvent">
<para>A sub event type, specifying whether the AGI command has begun or ended.</para>
<enumlist>
<enum name="Start"/>
<enum name="End"/>
</enumlist>
</parameter>
<parameter name="CommandId">
<para>Random identification number assigned to the execution of this command.</para>
</parameter>
<parameter name="Command">
<para>The AGI command as received from the external source.</para>
</parameter>
</syntax>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGI, "AGIExec",
"SubEvent: Start\r\n"
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
"CommandId: %d\r\n"
"Command: %s\r\n",
ast_channel_name(chan),
ast_channel_uniqueid(chan),
command_id,
ami_cmd);
parse_args(buf, &argc, argv); parse_args(buf, &argc, argv);
c = find_command(argv, 0); c = find_command(argv, 0);
if (c && (!dead || (dead && c->dead))) { if (c && (!dead || (dead && c->dead))) {
@ -3606,42 +3637,9 @@ static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, ch
case RESULT_SHOWUSAGE: case RESULT_SHOWUSAGE:
ami_res = "Usage"; ami_res = "Usage";
resultcode = 520; resultcode = 520;
break;
case RESULT_FAILURE: publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
ami_res = "Failure";
resultcode = -1;
break;
case ASYNC_AGI_BREAK:
case RESULT_SUCCESS:
ami_res = "Success";
resultcode = 200;
break;
default:
ami_res = "Unknown Result";
resultcode = 200;
break;
}
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a received AGI command completes processing.</synopsis>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGI, "AGIExec",
"SubEvent: End\r\n"
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
"CommandId: %d\r\n"
"Command: %s\r\n"
"ResultCode: %d\r\n"
"Result: %s\r\n",
ast_channel_name(chan),
ast_channel_uniqueid(chan),
command_id,
ami_cmd,
resultcode,
ami_res);
switch (res) {
case RESULT_SHOWUSAGE:
if (ast_strlen_zero(c->usage)) { if (ast_strlen_zero(c->usage)) {
ast_agi_send(agi->fd, chan, "520 Invalid command syntax. Proper usage not available.\n"); ast_agi_send(agi->fd, chan, "520 Invalid command syntax. Proper usage not available.\n");
} else { } else {
@ -3649,44 +3647,54 @@ static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, ch
ast_agi_send(agi->fd, chan, "%s", c->usage); ast_agi_send(agi->fd, chan, "%s", c->usage);
ast_agi_send(agi->fd, chan, "520 End of proper usage.\n"); ast_agi_send(agi->fd, chan, "520 End of proper usage.\n");
} }
break; break;
case ASYNC_AGI_BREAK:
return AGI_RESULT_SUCCESS_ASYNC;
case RESULT_FAILURE: case RESULT_FAILURE:
ami_res = "Failure";
resultcode = -1;
publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
/* The RESULT_FAILURE code is usually because the channel hungup. */ /* The RESULT_FAILURE code is usually because the channel hungup. */
return AGI_RESULT_FAILURE; return AGI_RESULT_FAILURE;
case ASYNC_AGI_BREAK:
ami_res = "Success";
resultcode = 200;
publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
return AGI_RESULT_SUCCESS_ASYNC;
case RESULT_SUCCESS:
ami_res = "Success";
resultcode = 200;
publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
break;
default: default:
ami_res = "Unknown Result";
resultcode = 200;
publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
break; break;
} }
} else if (c) { } else if (c) {
ast_agi_send(agi->fd, chan, "511 Command Not Permitted on a dead channel\n"); ami_res = "Command Not Permitted on a dead channel";
manager_event(EVENT_FLAG_AGI, "AGIExec", resultcode = 511;
"SubEvent: End\r\n"
"Channel: %s\r\n" ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res);
"Uniqueid: %s\r\n"
"CommandId: %d\r\n" publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
"Command: %s\r\n"
"ResultCode: 511\r\n"
"Result: Command not permitted on a dead channel\r\n",
ast_channel_name(chan),
ast_channel_uniqueid(chan),
command_id,
ami_cmd);
} else { } else {
ast_agi_send(agi->fd, chan, "510 Invalid or unknown command\n"); ami_res = "Invalid or unknown command";
manager_event(EVENT_FLAG_AGI, "AGIExec", resultcode = 510;
"SubEvent: End\r\n"
"Channel: %s\r\n" ast_agi_send(agi->fd, chan, "%d %s\n", resultcode, ami_res);
"Uniqueid: %s\r\n"
"CommandId: %d\r\n" publish_async_exec_end(chan, command_id, ami_cmd, resultcode, ami_res);
"Command: %s\r\n"
"ResultCode: 510\r\n"
"Result: Invalid or unknown command\r\n",
ast_channel_name(chan),
ast_channel_uniqueid(chan),
command_id,
ami_cmd);
} }
return AGI_RESULT_SUCCESS; return AGI_RESULT_SUCCESS;
} }
static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[]) static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
@ -4228,6 +4236,23 @@ AST_TEST_DEFINE(test_agi_null_docs)
static int unload_module(void) static int unload_module(void)
{ {
struct stasis_message_router *message_router;
message_router = ast_manager_get_message_router();
if (message_router) {
stasis_message_router_remove(message_router, agi_exec_start_type());
stasis_message_router_remove(message_router, agi_exec_end_type());
stasis_message_router_remove(message_router, agi_async_start_type());
stasis_message_router_remove(message_router, agi_async_exec_type());
stasis_message_router_remove(message_router, agi_async_end_type());
}
STASIS_MESSAGE_TYPE_CLEANUP(agi_exec_start_type);
STASIS_MESSAGE_TYPE_CLEANUP(agi_exec_end_type);
STASIS_MESSAGE_TYPE_CLEANUP(agi_async_start_type);
STASIS_MESSAGE_TYPE_CLEANUP(agi_async_exec_type);
STASIS_MESSAGE_TYPE_CLEANUP(agi_async_end_type);
ast_cli_unregister_multiple(cli_agi, ARRAY_LEN(cli_agi)); ast_cli_unregister_multiple(cli_agi, ARRAY_LEN(cli_agi));
/* we can safely ignore the result of ast_agi_unregister_multiple() here, since it cannot fail, as /* we can safely ignore the result of ast_agi_unregister_multiple() here, since it cannot fail, as
we know that these commands were registered by this module and are still registered we know that these commands were registered by this module and are still registered
@ -4242,6 +4267,44 @@ static int unload_module(void)
static int load_module(void) static int load_module(void)
{ {
struct stasis_message_router *message_router;
message_router = ast_manager_get_message_router();
if (!message_router) {
return AST_MODULE_LOAD_DECLINE;
}
STASIS_MESSAGE_TYPE_INIT(agi_exec_start_type);
STASIS_MESSAGE_TYPE_INIT(agi_exec_end_type);
STASIS_MESSAGE_TYPE_INIT(agi_async_start_type);
STASIS_MESSAGE_TYPE_INIT(agi_async_exec_type);
STASIS_MESSAGE_TYPE_INIT(agi_async_end_type);
stasis_message_router_add(message_router,
agi_exec_start_type(),
agi_channel_manager_event,
"AGIExecStart");
stasis_message_router_add(message_router,
agi_exec_end_type(),
agi_channel_manager_event,
"AGIExecEnd");
stasis_message_router_add(message_router,
agi_async_start_type(),
agi_channel_manager_event,
"AsyncAGIStart");
stasis_message_router_add(message_router,
agi_async_exec_type(),
agi_channel_manager_event,
"AsyncAGIExec");
stasis_message_router_add(message_router,
agi_async_end_type(),
agi_channel_manager_event,
"AsyncAGIEnd");
ast_cli_register_multiple(cli_agi, ARRAY_LEN(cli_agi)); ast_cli_register_multiple(cli_agi, ARRAY_LEN(cli_agi));
/* we can safely ignore the result of ast_agi_register_multiple() here, since it cannot fail, as /* we can safely ignore the result of ast_agi_register_multiple() here, since it cannot fail, as
no other commands have been registered yet no other commands have been registered yet

Loading…
Cancel
Save