diff -Nur asterisk-1.2.1/channel.c asterisk-1.2.1-magi-support/channel.c --- asterisk-1.2.1/channel.c 2005-12-01 17:34:58.000000000 -0600 +++ asterisk-1.2.1-magi-support/channel.c 2005-12-17 10:30:06.000000000 -0600 @@ -286,6 +286,156 @@ ast_queue_frame(chan, &fr); return; } +/*! + * Get the next magi from the channel (non-blocking) null if there's none + * Removes the ast_magi from the channel. It's the responsibility of + * the called to ast_magi_free() the structure + * \param channel the channel to get the magi from + */ +struct ast_magi *ast_dequeue_magi_for_channel(struct ast_channel *channel) +{ + if (!channel) return NULL; + + struct ast_magi *ret = NULL; + ast_mutex_lock(&channel->lock); + ret = channel->magi; + + if (ret) { + channel->magi = ret->next; + } + ast_mutex_unlock(&channel->lock); + + if (ret) { + manager_event(EVENT_FLAG_MAGI, "MAGIDequeue", + "Uniqueid: %s\r\n", + ret->uniqueid); + } + + return ret; +} + +//! Free the ast_magi data structure +/*! + * \param the_magi the magi to free + */ +void ast_magi_free(struct ast_magi *the_magi) +{ + if (the_magi != NULL) + free(the_magi); +} + +struct ast_magi *ast_magi_new(void) +{ + struct ast_magi *ret = malloc(sizeof(struct ast_magi)); + if (ret) { + memset(ret, 0, sizeof(struct ast_magi)); + } else { + ast_log(LOG_WARNING, "Out of memory\n"); + } + + return ret; +} + +/*! + * Remove all magi from the channel, freeing the magi and marking channel + * as not accepting more magi + * \param channel the channel to clean up + */ +void ast_remove_all_magi_from_channel(struct ast_channel *channel, int lock) +{ + if (!channel) return; + + if (lock) + ast_mutex_lock(&channel->lock); + + struct ast_magi *magi = channel->magi; + // don't allow any magi to be added + channel->no_more_magi = 1; + channel->magi = NULL; + + // unlock the channel as early as we can + if (lock) + ast_mutex_unlock(&channel->lock); + + // walk the magi list, freeing each one + while (magi) { + // get the next magi in the list + struct ast_magi *next = magi->next; + // the MAGI was destroyed... make sure that the + // calling app is notified so it can clean up state + manager_event(EVENT_FLAG_MAGI, "MAGIDestroy", + "Uniqueid: %s\r\n", + magi->uniqueid); + ast_magi_free(magi); + magi = next; + } +} + +/*! + * Add the magi to the channel. If the channel is not accepting + * magi, the magi is free'd. + * + * \param channel channel the channel to add the magi to + * \param magi the magi to add to the channel + */ +int ast_add_magi_to_channel(struct ast_channel *channel, + struct ast_magi *magi, + int lock) +{ + int freeMe = 0; // free the magi because we're not going to add it + + // don't add if we're shutting down, if there's no channel or no magi + if (shutting_down || !channel || !magi) { + freeMe = 1; + } + + if (!freeMe) { + if (lock) + ast_mutex_lock(&channel->lock); + + // if the channel is accepting magi, add it + if (!channel->no_more_magi) { + // make sure the magi isn't linked to anything + magi->next = NULL; + + // if we've got a magi queue, walk the linked list + if (channel->magi) { + struct ast_magi *last = channel->magi; + struct ast_magi *thisOne = last->next; + while (thisOne) { + last = thisOne; + thisOne = thisOne->next; + } + last->next = magi; + } else { + channel->magi = magi; + } + } else { + freeMe = 1; + } + + if (lock) + ast_mutex_unlock(&channel->lock); + + manager_event(EVENT_FLAG_MAGI, "MAGIEnqueue", + "Uniqueid: %s\r\n", + magi->uniqueid); + + } + + // the magi wasn't added, destroy it + if (freeMe) { + if (magi) { + manager_event(EVENT_FLAG_MAGI, "MAGIFailedEnqueue", + "Uniqueid: %s\r\n", + magi->uniqueid); + + ast_magi_free(magi); + } + } + + return freeMe ? 0 : 1; +} /*--- ast_channel_cmpwhentohangup: Compare a offset with when to hangup channel */ int ast_channel_cmpwhentohangup(struct ast_channel *chan, time_t offset) @@ -902,7 +1052,8 @@ /* Lock and unlock the channel just to be sure nobody has it locked still */ ast_mutex_lock(&cur->lock); - ast_mutex_unlock(&cur->lock); + ast_mutex_unlock(&cur->lock); + } if (chan->tech_pvt) { ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name); @@ -947,6 +1098,9 @@ ast_frfree(fp); } + /* free the magi from the channel */ + ast_remove_all_magi_from_channel(chan, 0); + /* loop over the variables list, freeing all data and deleting list items */ /* no need to lock the list, as the channel is already locked */ diff -Nur asterisk-1.2.1/include/asterisk/agi.h asterisk-1.2.1-magi-support/include/asterisk/agi.h --- asterisk-1.2.1/include/asterisk/agi.h 2005-11-29 12:24:39.000000000 -0600 +++ asterisk-1.2.1-magi-support/include/asterisk/agi.h 2005-12-17 10:33:17.000000000 -0600 @@ -27,10 +27,22 @@ extern "C" { #endif +#define MAX_AGI_RESULT_BUFFER_LEN 8192 + +struct agi_result_buffer { + int fd; + char *uniqueid; + char *channel; + char message[MAX_AGI_RESULT_BUFFER_LEN + 1]; +}; + typedef struct agi_state { - int fd; /* FD for general output */ + int outfd; /* FD for general output */ int audio; /* FD for audio output */ int ctrl; /* FD for input control */ + struct agi_result_buffer buffer; + struct agi_result_buffer *fd; + int keep_running; } AGI; typedef struct agi_command { @@ -46,8 +58,11 @@ struct agi_command *next; } agi_command; + int agi_register(agi_command *cmd); void agi_unregister(agi_command *cmd); + + #if defined(__cplusplus) || defined(c_plusplus) } diff -Nur asterisk-1.2.1/include/asterisk/channel.h asterisk-1.2.1-magi-support/include/asterisk/channel.h --- asterisk-1.2.1/include/asterisk/channel.h 2005-11-29 12:24:39.000000000 -0600 +++ asterisk-1.2.1-magi-support/include/asterisk/channel.h 2005-12-17 10:40:01.000000000 -0600 @@ -130,6 +130,24 @@ typedef unsigned long long ast_group_t; +#define MAX_AGI_CMD_LEN 1024 +#define MAX_MAGI_UNIQUE_ID 256 + + //! Manager API/AGI integration structure + /*! + * The data structure that holds Manager API/AGI commands + */ + struct ast_magi { + /*! the AGI command line */ + char cmd[MAX_AGI_CMD_LEN + 1]; // add 1 for the null terminator + + /*! the unique ID for this command */ + char uniqueid[MAX_MAGI_UNIQUE_ID + 1]; // the unique ID of the command + + /*! the next magi in the list */ + struct ast_magi *next; // linked list + }; + struct ast_generator { void *(*alloc)(struct ast_channel *chan, void *params); void (*release)(struct ast_channel *chan, void *data); @@ -299,7 +317,14 @@ /*! Current application */ char *appl; /*! Data passed to current application */ - char *data; + char *data; + + /*! the list of magi commands */ + struct ast_magi *magi; + + /*! don't allow any more magi structs to be added */ + int no_more_magi; + /*! Which fd had an event detected on */ int fdno; @@ -732,6 +757,47 @@ /*! This version works on fd's only. Be careful with it. */ int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception); +/*! + * Get the next magi from the channel (non-blocking) null if there's none + * Removes the ast_magi from the channel. It's the responsibility of + * the called to ast_magi_free() the structure + * \param channel the channel to get the magi from + */ +struct ast_magi *ast_dequeue_magi_for_channel(struct ast_channel *channel); + +/*! Allocate an ast_magi data structure */ +struct ast_magi *ast_magi_new(void); + +/*! Free the ast_magi data structure */ + +/*! + * \param the_magi the magi to free + */ +void ast_magi_free(struct ast_magi *the_magi); + +/*! + * Remove all magi from the channel, freeing the magi and marking channel + * as not accepting more magi + * \param channel the channel to clean up + * \param lock non-zero if the channel should be locked/unlocked + */ +void ast_remove_all_magi_from_channel(struct ast_channel *channel, + int lock); + +/*! + * Add the magi to the channel. If the channel is not accepting + * magi, the magi is free'd. + * + * \param channel channel the channel to add the magi to + * \param magi the magi to add to the channel + * \param lock non-zero if the channel's mutex should be locked/unlocked + * RETURNS 0 is failed, non-zero if successful + */ +int ast_add_magi_to_channel(struct ast_channel *channel, + struct ast_magi *magi, + int lock); + + /*! Reads a frame */ /*! diff -Nur asterisk-1.2.1/include/asterisk/manager.h asterisk-1.2.1-magi-support/include/asterisk/manager.h --- asterisk-1.2.1/include/asterisk/manager.h 2005-11-29 12:24:39.000000000 -0600 +++ asterisk-1.2.1-magi-support/include/asterisk/manager.h 2005-12-17 10:45:57.000000000 -0600 @@ -54,7 +54,7 @@ #define EVENT_FLAG_COMMAND (1 << 4) /* Ability to read/set commands */ #define EVENT_FLAG_AGENT (1 << 5) /* Ability to read/set agent info */ #define EVENT_FLAG_USER (1 << 6) /* Ability to read/set user info */ - +#define EVENT_FLAG_MAGI (1 << 7) /* Ability to read/set magi info */ /* Export manager structures */ #define MAX_HEADERS 80 #define MAX_LEN 256 diff -Nur asterisk-1.2.1/manager.c asterisk-1.2.1-magi-support/manager.c --- asterisk-1.2.1/manager.c 2005-11-29 12:24:39.000000000 -0600 +++ asterisk-1.2.1-magi-support/manager.c 2005-12-17 10:46:12.000000000 -0600 @@ -98,6 +98,7 @@ { EVENT_FLAG_COMMAND, "command" }, { EVENT_FLAG_AGENT, "agent" }, { EVENT_FLAG_USER, "user" }, + { EVENT_FLAG_MAGI, "magi" }, { -1, "all" }, { 0, "none" }, }; @@ -758,6 +759,80 @@ return 0; } +static char mandescr_magi[] = +"Description: Starts MAGI and launch AMI MAGI event.\n" +"Variables: (Names marked with * are required)\n" +" *Command: Agi Comand\n" +" ActionID: Optional ActionID for message matching.\n" +" MagiID: Call ID to uniquely identify an AGI process" +"\n"; + +static int action_magi(struct mansession *s, struct message *m) +{ + char *command = astman_get_header(m, "Command"); + char *channel = astman_get_header(m, "Channel"); + char *id = astman_get_header(m, "Uniqueid"); + + if (!command || ast_strlen_zero(command)) { + astman_send_error(s, m, "Command not specified"); + return 0; + } + + if (!channel || ast_strlen_zero(channel)) { + astman_send_error(s, m, "Channel not specified"); + return 0; + } + + if (!id || ast_strlen_zero(id)) { + astman_send_error(s, m, "UniqueID not specified"); + return 0; + } + + struct ast_channel *theChan = ast_get_channel_by_name_locked(channel); + + if (!theChan) { + astman_send_error(s, m, "Channel not found"); + return 0; + } + + // allocate the structure here... it will be + // free'ed if enqueuing fails or if + struct ast_magi *magi = ast_magi_new(); + + if (!magi) { + astman_send_error(s, m, "MAGI not allocated -- out of memory"); + ast_mutex_unlock(&theChan->lock); + return 0; + } + + // being smart, cmd is 1 char longer than MAX_AGI_CMD_LEN + // so we strncpy, and then set the last char to 0 + strncpy(magi->cmd, command, MAX_AGI_CMD_LEN); + magi->cmd[MAX_AGI_CMD_LEN] = 0; + + // see above re field lengths + strncpy(magi->uniqueid, id, MAX_MAGI_UNIQUE_ID); + magi->uniqueid[MAX_MAGI_UNIQUE_ID] = 0; + + + // if the add fails, this routine cleans up + // the magi + int added = ast_add_magi_to_channel(theChan, magi, 0); + + ast_mutex_unlock(&theChan->lock); + + if (!added) { + astman_send_error(s, m, "MAGI not added to channel"); + return 0; + } + + ast_cli(s->fd, "Response: Success\r\n" + "Uniqueid: %s\r\n\r\n", + id); + + return 0; +} + /*! \brief action_status: Manager "status" command to show channels */ /* Needs documentation... */ @@ -1669,6 +1744,7 @@ ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus ); ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount ); ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands); + ast_manager_register2( "magi", EVENT_FLAG_MAGI, action_magi, "Execute an AGI command via Manager API", mandescr_magi); ast_cli_register(&show_mancmd_cli); ast_cli_register(&show_mancmds_cli); diff -Nur asterisk-1.2.1/res/res_agi.c asterisk-1.2.1-magi-support/res/res_agi.c --- asterisk-1.2.1/res/res_agi.c 2005-11-29 12:24:39.000000000 -0600 +++ asterisk-1.2.1-magi-support/res/res_agi.c 2005-12-17 11:06:42.000000000 -0600 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -68,7 +69,8 @@ #define MAX_COMMANDS 128 /* Recycle some stuff from the CLI interface */ -#define fdprintf agi_debug_cli +// #define fdprintf agi_debug_cli +#define fdprintf printf_to_agi_result_buffer static char *tdesc = "Asterisk Gateway Interface (AGI)"; @@ -78,12 +80,15 @@ static char *deadapp = "DeadAGI"; +static char *mapp = "MAGI"; + static char *synopsis = "Executes an AGI compliant application"; static char *esynopsis = "Executes an EAGI compliant application"; static char *deadsynopsis = "Executes AGI on a hungup channel"; +static char *msynopsis = "Executes AGI commands sent to the channel via Maganer API"; static char *descrip = -" [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n" +" [M|E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n" "program on a channel. AGI allows Asterisk to launch external programs\n" "written in any language to control a telephony channel, play audio,\n" "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n" @@ -92,6 +97,7 @@ " hangup, or 0 on non-hangup exit. \n" "Using 'EAGI' provides enhanced AGI, with incoming audio available out of band" "on file descriptor 3\n\n" +"Using 'MAGI' executes AGI commands sent to the channel via the Manager API\n" "Use the CLI command 'show agi' to list available agi commands\n"; static int agidebug = 0; @@ -204,7 +210,7 @@ /* If we have a script parameter, relay it to the fastagi server */ if (!ast_strlen_zero(script)) - fdprintf(s, "agi_network_script: %s\n", script); + agi_debug_cli(s, "agi_network_script: %s\n", script); if (option_debug > 3) ast_log(LOG_DEBUG, "Wow, connected!\n"); @@ -320,7 +326,99 @@ } -static void setup_env(struct ast_channel *chan, char *request, int fd, int enhanced) +static void init_agi_result_buffer(struct agi_result_buffer *rb, + int fd, + char *channelname) +{ + if (rb) { + memset(rb, 0, sizeof(struct agi_result_buffer)); + rb->fd = fd; + rb->channel=channelname; + } +} + +static char hex_encode(int i) +{ + i &= 0xf; + char c = (char) i; + if (i < 10) { + return '0' + c; + } + i -= 10; + c = (char) i; + return 'a' + c; +} + +static void url_encode(char *in, char *out, int maxlen) +{ + char *origOut = out; + maxlen -= 6; + while (*in && (out - origOut) < maxlen) { + int i = *in; + i &= 255; // grab the byte part + in++; + if (i <= ' ' || i >= 127 || i == '&' || i == '?') { + *(out++) = '%'; + *(out++) = hex_encode(i >> 4); + *(out++) = hex_encode(i); + } else { + *out = (char) i; + out++; + } + } + *out = 0; + return; +} + +static void send_agi_result_buffer(struct agi_result_buffer *rb) +{ + // do nothing for empty buffer + if (!rb->message[0]) return; + + if (rb->fd > 0) { + ast_cli(rb->fd, "%s", rb->message); + } + + if (rb->uniqueid) { + char url_encoded[MAX_AGI_RESULT_BUFFER_LEN * 4]; + url_encode(rb->message, url_encoded, sizeof(url_encoded)); + + manager_event(EVENT_FLAG_MAGI, "MAGIExec", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "Result: %s\r\n", + rb->channel, + rb->uniqueid, url_encoded); + } + // reset buffer + rb->message[0] = 0; +} + +static void printf_to_agi_result_buffer(struct agi_result_buffer *rb, + char *fmt, + ...) +{ + if (!rb) return; + + char *stuff; + int res = 0; + + va_list ap; + va_start(ap, fmt); + res = vasprintf(&stuff, fmt, ap); + va_end(ap); + if (res == -1) { + ast_log(LOG_ERROR, "Out of memory\n"); + } else { + strncat(rb->message, stuff, MAX_AGI_RESULT_BUFFER_LEN); + // the message buffer is 1 char longer than MAX_AGI_RESULT_BUFFER_LEN + // so we can always null terminate + rb->message[MAX_AGI_RESULT_BUFFER_LEN] = 0; + free(stuff); + } +} + +static void setup_env(struct ast_channel *chan, char *request, struct agi_result_buffer * /*int*/ fd, int enhanced) { /* Print initial environment, with agi_request always being the first thing */ @@ -1022,6 +1120,13 @@ return RESULT_SUCCESS; } +static int handle_break(struct ast_channel *chan, AGI *agi, int arg, char *argv[]) +{ + agi->keep_running = 0; + fdprintf(agi->fd, "200 result=0\n"); + return RESULT_SUCCESS; +} + static int handle_autohangup(struct ast_channel *chan, AGI *agi, int argc, char *argv[]) { int timeout; @@ -1287,6 +1392,10 @@ return RESULT_SUCCESS; } +static char usage_break[] = +" Usage: BREAK\n" +" Ends the MAGI loop.\n"; + static char debug_usage[] = "Usage: agi debug\n" " Enables dumping of AGI transactions for debugging purposes\n"; @@ -1619,6 +1728,7 @@ { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension }, { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }, { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority }, + { { "break", NULL }, handle_break, "Breaks out of the MAGI loop", usage_break }, { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable }, { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile }, { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile }, @@ -1820,18 +1930,22 @@ fdprintf(agi->fd, "520-Invalid command syntax. Proper usage follows:\n"); fdprintf(agi->fd, c->usage); fdprintf(agi->fd, "520 End of proper usage.\n"); + send_agi_result_buffer(agi->fd); break; case AST_PBX_KEEPALIVE: /* We've been asked to keep alive, so do so */ + send_agi_result_buffer(agi->fd); return AST_PBX_KEEPALIVE; break; case RESULT_FAILURE: + send_agi_result_buffer(agi->fd); /* They've already given the failure. We've been hung up on so handle this appropriately */ return -1; } } else { fdprintf(agi->fd, "510 Invalid or unknown command\n"); + send_agi_result_buffer(agi->fd); } return 0; } @@ -1858,6 +1972,7 @@ } setlinebuf(readf); setup_env(chan, request, agi->fd, (agi->audio > -1)); + send_agi_result_buffer(agi->fd); for (;;) { ms = -1; c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms); @@ -1916,6 +2031,68 @@ return returnstatus; } +static int run_magi(struct ast_channel *chan, AGI *agi) +{ + int returnstatus = 0; + + manager_event(EVENT_FLAG_MAGI, "MAGIStart", + "Channel: %s\r\n", + chan->name); + + setup_env(chan, "null: running magi", agi->fd, (agi->audio > -1)); + agi->fd->uniqueid="N/A"; + send_agi_result_buffer(agi->fd); + agi->fd->uniqueid=NULL; + + while (agi->keep_running && !chan->no_more_magi && + !ast_check_hangup(chan)) { + struct ast_magi *the_command = ast_dequeue_magi_for_channel(chan); + + if (the_command) { + agi->fd->uniqueid = the_command->uniqueid; + returnstatus |= agi_handle_command(chan, agi, the_command->cmd); + agi->fd->uniqueid = NULL; + ast_magi_free(the_command); + if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) { + break; + // return returnstatus; + } + } else { // no command, wait 100 ms and check again + int ms; + struct ast_frame *f; + + ms = ast_waitfor(chan, 100); + if (ms < 0) { + break; + // return returnstatus; + } + + if (ms > 0) { + /* Read something */ + f = ast_read(chan); + if (f) { + if (f->frametype == AST_FRAME_DTMF) { + char digit = f->subclass; + manager_event(EVENT_FLAG_MAGI, "MAGIDTMF", + "Channel: %s\r\n" + "DTMFDigit: %c\r\n", + chan->name, + digit); + } + ast_frfree(f); + } + } + } + } + + manager_event(EVENT_FLAG_MAGI, "MAGIEnd", + "Channel: %s\r\n", + chan->name); + + return returnstatus; +} + + static int handle_showagi(int fd, int argc, char *argv[]) { struct agi_command *e; char fullcmd[80]; @@ -1995,7 +2172,7 @@ return RESULT_SUCCESS; } -static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead) +static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int dead, int use_magi) { int res=0; struct localuser *u; @@ -2009,7 +2186,7 @@ char *stringp; AGI agi; - if (ast_strlen_zero(data)) { + if (!use_magi && ast_strlen_zero(data)) { ast_log(LOG_WARNING, "AGI requires an argument (script)\n"); return -1; } @@ -2031,13 +2208,27 @@ } } #endif - res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid); + if (!use_magi) + res = launch_script(argv[0], argv, fds, enhanced ? &efd : + NULL, &pid); if (!res) { - agi.fd = fds[1]; - agi.ctrl = fds[0]; + agi.outfd = -1; + agi.ctrl = -1; + + if (!use_magi) { + agi.outfd = fds[1]; + agi.ctrl = fds[0]; + } agi.audio = efd; - res = run_agi(chan, argv[0], &agi, pid, dead); - close(fds[1]); + init_agi_result_buffer(&agi.buffer, agi.outfd, chan->name); + agi.fd = &agi.buffer; + agi.keep_running = 1; + if (!use_magi) + res = run_agi(chan, argv[0], &agi, pid, dead); + else + res = run_magi(chan, &agi); + if (!use_magi) + close(fds[1]); if (efd > -1) close(efd); } @@ -2049,7 +2240,14 @@ { if (chan->_softhangup) ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n"); - return agi_exec_full(chan, data, 0, 0); + return agi_exec_full(chan, data, 0, 0, 0); +} + +static int magi_exec(struct ast_channel *chan, void *data) +{ + if (chan->_softhangup) + ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n"); + return agi_exec_full(chan, data, 0, 0, 1); } static int eagi_exec(struct ast_channel *chan, void *data) @@ -2064,7 +2262,7 @@ ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name); return -1; } - res = agi_exec_full(chan, data, 1, 0); + res = agi_exec_full(chan, data, 1, 0, 0); if (!res) { if (ast_set_read_format(chan, readformat)) { ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat)); @@ -2075,7 +2273,7 @@ static int deadagi_exec(struct ast_channel *chan, void *data) { - return agi_exec_full(chan, data, 0, 1); + return agi_exec_full(chan, data, 0, 1, 0); } static char showagi_help[] = @@ -2104,6 +2302,7 @@ ast_cli_unregister(&cli_no_debug); ast_unregister_application(eapp); ast_unregister_application(deadapp); + ast_unregister_application(mapp); return ast_unregister_application(app); } @@ -2115,6 +2314,7 @@ ast_cli_register(&cli_no_debug); ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip); ast_register_application(eapp, eagi_exec, esynopsis, descrip); + ast_register_application(mapp, magi_exec, msynopsis, descrip); return ast_register_application(app, agi_exec, synopsis, descrip); }