[Asterisk-Dev] Changing Asterisk's module invocation

David Pollak dpp-asterisk at projectsinmotion.com
Tue Sep 14 15:34:45 MST 2004


Juan Jose,

You don't need to change Asterisk.  In your glue layer between SWIG and 
Asterisk, you can determine the unique identifier of the channel.  Based 
on this unique ID, you can look up the void * data structure in your own 
store.  Put another way, you manage your own cookies and use the 
channel's unique ID as the lookup for the cookie.  The only additional 
bit of work is that you'll have to monitor to see when a channel is 
destroyed so you can free the cookie entry for the destroyed channel.

Depending on exactly what you're using Java for, you might want to take 
a look at a patch I made (enclosed) to Asterisk.  This patch merges AGI 
with the Manager API.  It's useful to have a single socket open between 
a Java app server and Asterisk and control all the Asterisk channels 
through that socket.

If you've got more questions, please feel free to ping me.

Thanks,

David

Juan Jose Comellas wrote:

>I am writing Java bindings for the Asterisk API using SWIG 
>(http://www.swig.org) so that I can create Asterisk modules written in Java. 
>The main problem I'm having is that in order to make this process efficient I 
>need to have more than one application per module (I load only one instance 
>of the JVM). To be able to build a generic solution (where I don't have to 
>add one entry point in C per Java application), I need to make a slight 
>change to the ast_register_application() function and to the code that 
>invokes each application's callback function. Currently all application 
>callbacks follow the following prototype:
>
>int (*execute)(struct ast_channel *, void *data);
>
>The problem is that when building Java modules this function acts as a bridge 
>to the Java invocation and I need extra data to be able to map the call into 
>a Java class/method. To solve this problem I need to make the following 
>changes to Asterisk's public interface:
>
>1) Add one argument to the application callback to be able to pass the 
>"cookie" that will let me map to the Java class/method:
>
>int (*execute)(struct ast_channel *, void *data, void *cookie);
>
>2) Change the ast_app struct to be able to store the "cookie". With my changes 
>it looks like this:
>
>struct ast_app {
> char name[AST_MAX_APP];   /* Name of the application */
> int (*execute)(struct ast_channel *chan, void *data, void *cookie);
> char *synopsis;    /* Synopsis text for 'show applications' */
> char *description;   /* Description (help text) for 'show application <name>' 
>*/
> void *cookie;    /* Data used to map the call when using Java modules */
> struct ast_app *next;   /* Next app in list */
>};
>
>3) Change the ast_register_application() function to pass the cookie to the 
>ast_app struct:
>
>int ast_register_application(char *app, int (*execute)(struct ast_channel *, 
>void *data, void *cookie), char *synopsis, char *description, void *cookie);
>
>Do these modifications have any chance of being accepted into Asterisk?
>
>
>  
>
-------------- next part --------------
? doc/api
? res/foo
Index: channel.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channel.c,v
retrieving revision 1.138
diff -r1.138 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.71
diff -r1.71 manager.c
74a75
> 	{ EVENT_FLAG_MAGI, "magi" },
926a928,993
> 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;
> }
> 
1389a1457
> 		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.54
diff -r1.54 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;
> 
633a658,696
> 
> /*!
>  * 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.3
diff -r1.3 res_agi.c
19a20
> #include <asterisk/manager.h>
49c50,51
< #define fdprintf ast_cli
---
> // #define fdprintf ast_cli
> #define fdprintf printf_to_agi_result_buffer
55a58
> static char *mapp = "MAGI";
61a65
> static char *msynopsis = "Executes AGI commands sent to the channel via Maganer API";
64c68
< "  [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"
70a75
> "Using 'MAGI' executes AGI commands sent to the channel via the Manager API\n"
172c177,269
< 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)
197a295
> 	
879a978,984
> 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;
> }
> 
1088a1194,1198
> static char usage_break[] =
> " Usage: BREAK\n"
> "    Ends the MAGI loop.\n";
> 
> 
1119c1229,1230
< 	{ { "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 }
1309a1421
> 			send_agi_result_buffer(agi->fd);
1312a1425
> 		        send_agi_result_buffer(agi->fd);
1315a1429
>                  	send_agi_result_buffer(agi->fd);
1322a1437
> 	send_agi_result_buffer(agi->fd);
1339,1345c1454,1461
< 	if (!(readf = fdopen(agi->ctrl, "r"))) {
< 		ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
< 		kill(pid, SIGHUP);
< 		close(agi->ctrl);
< 		return -1;
< 	}
< 	setlinebuf(readf);
---
> 	  if (!(readf = fdopen(agi->ctrl, "r"))) {
> 	    ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
> 	    kill(pid, SIGHUP);
> 	    close(agi->ctrl);
> 	    return -1;
> 	  }
> 	  setlinebuf(readf);
> 
1346a1463
> 	send_agi_result_buffer(agi->fd);
1401a1519,1579
> 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;
> }
> 
1481c1659
< 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)
1494c1672
< 	if (!data || ast_strlen_zero(data)) {
---
> 	if (!use_magi && (!data || ast_strlen_zero(data))) {
1516c1694,1696
< 	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);
1518c1698,1702
< 		agi.fd = fds[1];
---
> 	  agi.outfd = -1;
> 	  agi.ctrl = -1;
> 	  
> 	  if (!use_magi) {
> 		agi.outfd = fds[1];
1520c1704,1710
< 		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)
1522c1712,1716
< 		close(fds[1]);
---
> 	  else
> 	    res = run_magi(chan, &agi);
> 
> 		if (!use_magi) 
> 		  close(fds[1]);
1534c1728,1735
< 	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);
1548c1749
< 	res = agi_exec_full(chan, data, 1, 0);
---
> 	res = agi_exec_full(chan, data, 1, 0, 0);
1559c1760
< 	return agi_exec_full(chan, data, 0, 1);
---
> 	return agi_exec_full(chan, data, 0, 1, 0);
1584a1786
> 	ast_unregister_application(mapp);
1593a1796
> 	ast_register_application(mapp, magi_exec, msynopsis, descrip);


More information about the asterisk-dev mailing list