[Asterisk-Dev] AGI IDE
David Pollak
dpp-asterisk at projectsinmotion.com
Wed Sep 29 07:34:25 MST 2004
Skipped content of type multipart/alternative-------------- next part --------------
? doc/api
? res/foo
Index: channel.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channel.c,v
retrieving revision 1.139
diff -r1.139 channel.c
273a274,424
> /*!
> * 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;
> }
>
571c722
<
---
>
601a753,755
>
> // free the magi from the channel
> ast_remove_all_magi_from_channel(chan, 0);
Index: manager.c
===================================================================
RCS file: /usr/cvsroot/asterisk/manager.c,v
retrieving revision 1.74
diff -r1.74 manager.c
75a76
> { EVENT_FLAG_MAGI, "magi" },
940a942,1007
> 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;
> }
>
1403a1471
> ast_manager_register( "magi", EVENT_FLAG_MAGI, action_magi, "Execute an AGI command via Manager API" );
Index: include/asterisk/agi.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/agi.h,v
retrieving revision 1.2
diff -r1.2 agi.h
20a21,29
> #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];
> };
>
22c31
< int fd; /* FD for general output */
---
> int outfd; /* FD for general output */
24a34,36
> struct agi_result_buffer buffer;
> struct agi_result_buffer *fd;
> int keep_running;
Index: include/asterisk/channel.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/channel.h,v
retrieving revision 1.55
diff -r1.55 channel.h
42a43,45
> #define MAX_AGI_CMD_LEN 1024
> #define MAX_MAGI_UNIQUE_ID 256
>
50a54,68
> //! 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
> };
>
108,109c126,133
< 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;
>
635a660,698
>
> /*!
> * 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);
Index: include/asterisk/manager.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/manager.h,v
retrieving revision 1.11
diff -r1.11 manager.h
50a51
> #define EVENT_FLAG_MAGI (1 << 7) /* Ability to read/set magi info */
Index: res/res_agi.c
===================================================================
RCS file: /usr/cvsroot/asterisk/res/res_agi.c,v
retrieving revision 1.10
diff -r1.10 res_agi.c
24a25
> #include <asterisk/manager.h>
56c57,59
< #define fdprintf agi_debug_cli
---
> // #define fdprintf ast_cli
> // #define fdprintf agi_debug_cli
> #define fdprintf printf_to_agi_result_buffer
62a66
> static char *mapp = "MAGI";
68a73
> static char *msynopsis = "Executes AGI commands sent to the channel via Maganer API";
71c76
< " [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"
77a83
> "Using 'MAGI' executes AGI commands sent to the channel via the Manager API\n"
288c294,386
< 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)
313a412
>
1027a1127,1133
> 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;
> }
>
1236a1343,1347
> static char usage_break[] =
> " Usage: BREAK\n"
> " Ends the MAGI loop.\n";
>
>
1267c1378,1379
< { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic }
---
> { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic },
> { { "break", NULL }, handle_break, "Breaks out of the MAGI loop", usage_break }
1457a1570
> send_agi_result_buffer(agi->fd);
1460a1574
> send_agi_result_buffer(agi->fd);
1463a1578
> send_agi_result_buffer(agi->fd);
1470a1586
> send_agi_result_buffer(agi->fd);
1494a1611
>
1495a1613
> send_agi_result_buffer(agi->fd);
1551a1670,1730
> 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 &&
> !chan->zombie && !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;
> }
>
1631c1810
< 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)
1644c1823
< if (!data || ast_strlen_zero(data)) {
---
> if (!use_magi && (!data || ast_strlen_zero(data))) {
1666c1845,1847
< 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);
1668c1849,1853
< agi.fd = fds[1];
---
> agi.outfd = -1;
> agi.ctrl = -1;
>
> if (!use_magi) {
> agi.outfd = fds[1];
1670c1855,1861
< agi.audio = efd;
---
> }
> agi.audio = efd;
> init_agi_result_buffer(&agi.buffer, agi.outfd, chan->name);
> agi.fd = &agi.buffer;
> agi.keep_running = 1;
>
> if (!use_magi)
1672c1863,1867
< close(fds[1]);
---
> else
> res = run_magi(chan, &agi);
>
> if (!use_magi)
> close(fds[1]);
1684c1879,1886
< 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);
1698c1900
< res = agi_exec_full(chan, data, 1, 0);
---
> res = agi_exec_full(chan, data, 1, 0, 0);
1709c1911
< return agi_exec_full(chan, data, 0, 1);
---
> return agi_exec_full(chan, data, 0, 1, 0);
1736a1939
> ast_unregister_application(mapp);
1747a1951
> ast_register_application(mapp, magi_exec, msynopsis, descrip);
More information about the asterisk-dev
mailing list