[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