@ -26,7 +26,7 @@
*/
/*** MODULEINFO
< use > res_agi < / use >
< use type = " module " > res_agi < / use >
< support_level > core < / support_level >
* * */
@ -205,14 +205,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
< / agi >
* * */
static const char * const app_gosub = " Gosub " ;
static const char * const app_gosubif = " GosubIf " ;
static const char * const app_return = " Return " ;
static const char * const app_pop = " StackPop " ;
static const char app_gosub [ ] = " Gosub " ;
static const char app_gosubif [ ] = " GosubIf " ;
static const char app_return [ ] = " Return " ;
static const char app_pop [ ] = " StackPop " ;
static void gosub_free ( void * data ) ;
static struct ast_datastore_info stack_info = {
static const struct ast_datastore_info stack_info = {
. type = " GOSUB " ,
. destroy = gosub_free ,
} ;
@ -223,11 +223,14 @@ struct gosub_stack_frame {
unsigned char arguments ;
struct varshead varshead ;
int priority ;
unsigned int is_agi : 1 ;
/*! TRUE if the return location marks the end of a special routine. */
unsigned int is_special : 1 ;
char * context ;
char extension [ 0 ] ;
} ;
AST_LIST_HEAD ( gosub_stack_list , gosub_stack_frame ) ;
static int frame_set_var ( struct ast_channel * chan , struct gosub_stack_frame * frame , const char * var , const char * value )
{
struct ast_var_t * variables ;
@ -242,8 +245,9 @@ static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *fra
}
if ( ! found ) {
variables = ast_var_assign ( var , " " ) ;
AST_LIST_INSERT_HEAD ( & frame - > varshead , variables , entries ) ;
if ( ( variables = ast_var_assign ( var , " " ) ) ) {
AST_LIST_INSERT_HEAD ( & frame - > varshead , variables , entries ) ;
}
pbx_builtin_pushvar_helper ( chan , var , value ) ;
} else {
pbx_builtin_setvar_helper ( chan , var , value ) ;
@ -295,8 +299,9 @@ static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const
static void gosub_free ( void * data )
{
AST_LIST_HEAD ( , gosub_stack_frame ) * oldlist = data ;
struct gosub_stack_list * oldlist = data ;
struct gosub_stack_frame * oldframe ;
AST_LIST_LOCK ( oldlist ) ;
while ( ( oldframe = AST_LIST_REMOVE_HEAD ( oldlist , entries ) ) ) {
gosub_release_frame ( NULL , oldframe ) ;
@ -310,7 +315,8 @@ static int pop_exec(struct ast_channel *chan, const char *data)
{
struct ast_datastore * stack_store ;
struct gosub_stack_frame * oldframe ;
AST_LIST_HEAD ( , gosub_stack_frame ) * oldlist ;
struct gosub_stack_list * oldlist ;
int res = 0 ;
ast_channel_lock ( chan ) ;
if ( ! ( stack_store = ast_channel_datastore_find ( chan , & stack_info , NULL ) ) ) {
@ -321,23 +327,30 @@ static int pop_exec(struct ast_channel *chan, const char *data)
oldlist = stack_store - > data ;
AST_LIST_LOCK ( oldlist ) ;
oldframe = AST_LIST_REMOVE_HEAD ( oldlist , entries ) ;
AST_LIST_UNLOCK ( oldlist ) ;
oldframe = AST_LIST_FIRST ( oldlist ) ;
if ( oldframe ) {
gosub_release_frame ( chan , oldframe ) ;
if ( oldframe - > is_special ) {
ast_debug ( 1 , " %s attempted to pop special return location. \n " , app_pop ) ;
/* Abort the special routine dialplan execution. Dialplan programming error. */
res = - 1 ;
} else {
AST_LIST_REMOVE_HEAD ( oldlist , entries ) ;
gosub_release_frame ( chan , oldframe ) ;
}
} else {
ast_debug ( 1 , " %s called with an empty gosub stack \n " , app_pop ) ;
}
AST_LIST_UNLOCK ( oldlist ) ;
ast_channel_unlock ( chan ) ;
return 0 ;
return res ;
}
static int return_exec ( struct ast_channel * chan , const char * data )
{
struct ast_datastore * stack_store ;
struct gosub_stack_frame * oldframe ;
AST_LIST_HEAD ( , gosub_stack_frame ) * oldlist ;
struct gosub_stack_list * oldlist ;
const char * retval = data ;
int res = 0 ;
@ -357,12 +370,24 @@ static int return_exec(struct ast_channel *chan, const char *data)
ast_log ( LOG_ERROR , " Return without Gosub: stack is empty \n " ) ;
ast_channel_unlock ( chan ) ;
return - 1 ;
} else if ( oldframe - > is_agi ) {
/* Exit from AGI */
}
if ( oldframe - > is_special ) {
/* Exit from special routine. */
res = - 1 ;
}
ast_explicit_goto ( chan , oldframe - > context , oldframe - > extension , oldframe - > priority ) ;
/*
* We cannot use ast_explicit_goto ( ) because we MUST restore
* what was there before . Channels that do not have a PBX may
* not have the context or exten set .
*/
ast_copy_string ( chan - > context , oldframe - > context , sizeof ( chan - > context ) ) ;
ast_copy_string ( chan - > exten , oldframe - > extension , sizeof ( chan - > exten ) ) ;
if ( ast_test_flag ( chan , AST_FLAG_IN_AUTOLOOP ) ) {
- - oldframe - > priority ;
}
chan - > priority = oldframe - > priority ;
gosub_release_frame ( chan , oldframe ) ;
/* Set a return value, if any */
@ -371,10 +396,96 @@ static int return_exec(struct ast_channel *chan, const char *data)
return res ;
}
/*!
* \ internal
* \ brief Add missing context and / or exten to Gosub application argument string .
* \ since 1.8 .30 .0
* \ since 11.0
*
* \ param chan Channel to obtain context / exten .
* \ param args Gosub application argument string .
*
* \ details
* Fills in the optional context and exten from the given channel .
* Convert : [ [ context , ] exten , ] priority [ ( arg1 [ , . . . ] [ , argN ] ) ]
* To : context , exten , priority [ ( arg1 [ , . . . ] [ , argN ] ) ]
*
* \ retval expanded Gosub argument string on success . Must be freed .
* \ retval NULL on error .
*
* \ note The parsing needs to be kept in sync with the
* gosub_exec ( ) argument format .
*/
static const char * expand_gosub_args ( struct ast_channel * chan , const char * args )
{
int len ;
char * parse ;
char * label ;
char * new_args ;
const char * context ;
const char * exten ;
const char * pri ;
/* Separate the context,exten,pri from the optional routine arguments. */
parse = ast_strdupa ( args ) ;
label = strsep ( & parse , " ( " ) ;
if ( parse ) {
char * endparen ;
endparen = strrchr ( parse , ' ) ' ) ;
if ( endparen ) {
* endparen = ' \0 ' ;
} else {
ast_log ( LOG_WARNING , " Ouch. No closing paren: '%s'? \n " , args ) ;
}
}
/* Split context,exten,pri */
context = strsep ( & label , " , " ) ;
exten = strsep ( & label , " , " ) ;
pri = strsep ( & label , " , " ) ;
if ( ! exten ) {
/* Only a priority in this one */
pri = context ;
exten = NULL ;
context = NULL ;
} else if ( ! pri ) {
/* Only an extension and priority in this one */
pri = exten ;
exten = context ;
context = NULL ;
}
ast_channel_lock ( chan ) ;
if ( ast_strlen_zero ( exten ) ) {
exten = chan - > exten ;
}
if ( ast_strlen_zero ( context ) ) {
context = chan - > context ;
}
len = strlen ( context ) + strlen ( exten ) + strlen ( pri ) + 3 ;
if ( ! ast_strlen_zero ( parse ) ) {
len + = 2 + strlen ( parse ) ;
}
new_args = ast_malloc ( len ) ;
if ( new_args ) {
if ( ast_strlen_zero ( parse ) ) {
snprintf ( new_args , len , " %s,%s,%s " , context , exten , pri ) ;
} else {
snprintf ( new_args , len , " %s,%s,%s(%s) " , context , exten , pri , parse ) ;
}
}
ast_channel_unlock ( chan ) ;
ast_debug ( 4 , " Gosub args:%s new_args:%s \n " , args , new_args ? new_args : " " ) ;
return new_args ;
}
static int gosub_exec ( struct ast_channel * chan , const char * data )
{
struct ast_datastore * stack_store ;
AST_LIST_HEAD ( , gosub_stack_frame ) * oldlist ;
struct gosub_stack_list * oldlist ;
struct gosub_stack_frame * newframe ;
struct gosub_stack_frame * lastframe ;
char argname [ 15 ] ;
@ -501,7 +612,7 @@ static int gosub_exec(struct ast_channel *chan, const char *data)
frame_set_var ( chan , newframe , argname , i < args2 . argc ? args2 . argval [ i ] : " " ) ;
ast_debug ( 1 , " Setting '%s' to '%s' \n " , argname , i < args2 . argc ? args2 . argval [ i ] : " " ) ;
}
snprintf ( argname , sizeof ( argname ) , " % d " , args2 . argc ) ;
snprintf ( argname , sizeof ( argname ) , " % u " , args2 . argc ) ;
frame_set_var ( chan , newframe , " ARGC " , argname ) ;
/* And finally, save our return address */
@ -564,10 +675,15 @@ static int gosubif_exec(struct ast_channel *chan, const char *data)
static int local_read ( struct ast_channel * chan , const char * cmd , char * data , char * buf , size_t len )
{
struct ast_datastore * stack_store ;
AST_LIST_HEAD ( , gosub_stack_frame ) * oldlist ;
struct gosub_stack_list * oldlist ;
struct gosub_stack_frame * frame ;
struct ast_var_t * variables ;
if ( ! chan ) {
ast_log ( LOG_WARNING , " No channel was provided to %s function. \n " , cmd ) ;
return - 1 ;
}
ast_channel_lock ( chan ) ;
if ( ! ( stack_store = ast_channel_datastore_find ( chan , & stack_info , NULL ) ) ) {
ast_channel_unlock ( chan ) ;
@ -599,9 +715,14 @@ static int local_read(struct ast_channel *chan, const char *cmd, char *data, cha
static int local_write ( struct ast_channel * chan , const char * cmd , char * var , const char * value )
{
struct ast_datastore * stack_store ;
AST_LIST_HEAD ( , gosub_stack_frame ) * oldlist ;
struct gosub_stack_list * oldlist ;
struct gosub_stack_frame * frame ;
if ( ! chan ) {
ast_log ( LOG_WARNING , " No channel was provided to %s function. \n " , cmd ) ;
return - 1 ;
}
ast_channel_lock ( chan ) ;
if ( ! ( stack_store = ast_channel_datastore_find ( chan , & stack_info , NULL ) ) ) {
ast_log ( LOG_ERROR , " Tried to set LOCAL(%s), but we aren't within a Gosub routine \n " , var ) ;
@ -644,6 +765,12 @@ static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char
}
AST_STANDARD_RAW_ARGS ( args , data ) ;
if ( ast_strlen_zero ( args . n ) | | ast_strlen_zero ( args . name ) ) {
ast_log ( LOG_ERROR , " LOCAL_PEEK requires parameters n and varname \n " ) ;
return - 1 ;
}
n = atoi ( args . n ) ;
* buf = ' \0 ' ;
@ -666,7 +793,7 @@ static struct ast_custom_function peek_function = {
static int stackpeek_read ( struct ast_channel * chan , const char * cmd , char * data , struct ast_str * * str , ssize_t len )
{
struct ast_datastore * stack_store ;
AST_LIST_HEAD ( , gosub_stack_frame ) * oldlist ;
struct gosub_stack_list * oldlist ;
struct gosub_stack_frame * frame ;
int n ;
AST_DECLARE_APP_ARGS ( args ,
@ -683,6 +810,11 @@ static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data,
data = ast_strdupa ( data ) ;
AST_STANDARD_APP_ARGS ( args , data ) ;
if ( ast_strlen_zero ( args . n ) | | ast_strlen_zero ( args . which ) ) {
ast_log ( LOG_ERROR , " STACK_PEEK requires parameters n and which \n " ) ;
return - 1 ;
}
n = atoi ( args . n ) ;
if ( n < = 0 ) {
ast_log ( LOG_ERROR , " STACK_PEEK must be called with a positive peek value \n " ) ;
@ -712,6 +844,7 @@ static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data,
if ( ! ast_true ( args . suppress ) ) {
ast_log ( LOG_ERROR , " Stack peek of '%s' is more stack frames than I have \n " , args . n ) ;
}
AST_LIST_UNLOCK ( oldlist ) ;
ast_channel_unlock ( chan ) ;
return - 1 ;
}
@ -733,6 +866,7 @@ static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data,
break ;
default :
ast_log ( LOG_ERROR , " Unknown argument '%s' to STACK_PEEK \n " , args . which ) ;
break ;
}
AST_LIST_UNLOCK ( oldlist ) ;
@ -746,11 +880,206 @@ static struct ast_custom_function stackpeek_function = {
. read2 = stackpeek_read ,
} ;
/*!
* \ internal
* \ brief Pop stack frames until remove a special return location .
* \ since 1.8 .30 .0
* \ since 11.0
*
* \ param chan Channel to balance stack on .
*
* \ note The channel is already locked when called .
*
* \ return Nothing
*/
static void balance_stack ( struct ast_channel * chan )
{
struct ast_datastore * stack_store ;
struct gosub_stack_list * oldlist ;
struct gosub_stack_frame * oldframe ;
int found ;
stack_store = ast_channel_datastore_find ( chan , & stack_info , NULL ) ;
if ( ! stack_store ) {
ast_log ( LOG_WARNING , " No %s stack allocated. \n " , app_gosub ) ;
return ;
}
oldlist = stack_store - > data ;
AST_LIST_LOCK ( oldlist ) ;
do {
oldframe = AST_LIST_REMOVE_HEAD ( oldlist , entries ) ;
if ( ! oldframe ) {
break ;
}
found = oldframe - > is_special ;
gosub_release_frame ( chan , oldframe ) ;
} while ( ! found ) ;
AST_LIST_UNLOCK ( oldlist ) ;
}
/*!
* \ internal
* \ brief Run a subroutine on a channel .
* \ since 1.8 .30 .0
* \ since 11.0
*
* \ note Absolutely _NO_ channel locks should be held before calling this function .
*
* \ param chan Channel to execute subroutine on .
* \ param sub_args Gosub application argument string .
* \ param ignore_hangup TRUE if a hangup does not stop execution of the routine .
*
* \ retval 0 success
* \ retval - 1 on error
*/
static int gosub_run ( struct ast_channel * chan , const char * sub_args , int ignore_hangup )
{
const char * saved_context ;
const char * saved_exten ;
int saved_priority ;
int saved_hangup_flags ;
int saved_autoloopflag ;
int res ;
ast_channel_lock ( chan ) ;
ast_verb ( 3 , " %s Internal %s(%s) start \n " , chan - > name , app_gosub , sub_args ) ;
/* Save non-hangup softhangup flags. */
saved_hangup_flags = chan - > _softhangup
& ( AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE ) ;
if ( saved_hangup_flags ) {
chan - > _softhangup & = ~ ( AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE ) ;
}
/* Save autoloop flag */
saved_autoloopflag = ast_test_flag ( chan , AST_FLAG_IN_AUTOLOOP ) ;
ast_set_flag ( chan , AST_FLAG_IN_AUTOLOOP ) ;
/* Save current dialplan location */
saved_context = ast_strdupa ( chan - > context ) ;
saved_exten = ast_strdupa ( chan - > exten ) ;
saved_priority = chan - > priority ;
ast_debug ( 4 , " %s Original location: %s,%s,%d \n " , chan - > name ,
saved_context , saved_exten , saved_priority ) ;
ast_channel_unlock ( chan ) ;
res = gosub_exec ( chan , sub_args ) ;
ast_debug ( 4 , " %s exited with status %d \n " , app_gosub , res ) ;
ast_channel_lock ( chan ) ;
if ( ! res ) {
struct ast_datastore * stack_store ;
/* Mark the return location as special. */
stack_store = ast_channel_datastore_find ( chan , & stack_info , NULL ) ;
if ( ! stack_store ) {
/* Should never happen! */
ast_log ( LOG_ERROR , " No %s stack! \n " , app_gosub ) ;
res = - 1 ;
} else {
struct gosub_stack_list * oldlist ;
struct gosub_stack_frame * cur ;
oldlist = stack_store - > data ;
cur = AST_LIST_FIRST ( oldlist ) ;
cur - > is_special = 1 ;
}
}
if ( ! res ) {
int found = 0 ; /* set if we find at least one match */
/*
* Run gosub body autoloop .
*
* Note that this loop is inverted from the normal execution
* loop because we just executed the Gosub application as the
* first extension of the autoloop .
*/
do {
/* Check for hangup. */
if ( chan - > _softhangup & AST_SOFTHANGUP_UNBRIDGE ) {
saved_hangup_flags | = AST_SOFTHANGUP_UNBRIDGE ;
chan - > _softhangup & = ~ AST_SOFTHANGUP_UNBRIDGE ;
}
if ( ast_check_hangup ( chan ) ) {
if ( chan - > _softhangup & AST_SOFTHANGUP_ASYNCGOTO ) {
ast_log ( LOG_ERROR , " %s An async goto just messed up our execution location. \n " ,
chan - > name ) ;
break ;
}
if ( ! ignore_hangup ) {
break ;
}
}
/* Next dialplan priority. */
+ + chan - > priority ;
ast_channel_unlock ( chan ) ;
res = ast_spawn_extension ( chan , chan - > context , chan - > exten , chan - > priority ,
S_COR ( chan - > caller . id . number . valid , chan - > caller . id . number . str , NULL ) ,
& found , 1 ) ;
ast_channel_lock ( chan ) ;
} while ( ! res ) ;
if ( found & & res ) {
/* Something bad happened, or a hangup has been requested. */
ast_debug ( 1 , " Spawn extension (%s,%s,%d) exited with %d on '%s' \n " ,
chan - > context , chan - > exten , chan - > priority , res , chan - > name ) ;
ast_verb ( 2 , " Spawn extension (%s, %s, %d) exited non-zero on '%s' \n " ,
chan - > context , chan - > exten , chan - > priority , chan - > name ) ;
}
/* Did the routine return? */
if ( chan - > priority = = saved_priority
& & ! strcmp ( chan - > context , saved_context )
& & ! strcmp ( chan - > exten , saved_exten ) ) {
ast_verb ( 3 , " %s Internal %s(%s) complete GOSUB_RETVAL=%s \n " ,
chan - > name , app_gosub , sub_args ,
S_OR ( pbx_builtin_getvar_helper ( chan , " GOSUB_RETVAL " ) , " " ) ) ;
} else {
ast_log ( LOG_NOTICE , " %s Abnormal '%s(%s)' exit. Popping routine return locations. \n " ,
chan - > name , app_gosub , sub_args ) ;
balance_stack ( chan ) ;
pbx_builtin_setvar_helper ( chan , " GOSUB_RETVAL " , " " ) ;
}
/* We executed the requested subroutine to the best of our ability. */
res = 0 ;
}
ast_debug ( 4 , " %s Ending location: %s,%s,%d \n " , chan - > name ,
chan - > context , chan - > exten , chan - > priority ) ;
/* Restore dialplan location */
if ( ! ( chan - > _softhangup & AST_SOFTHANGUP_ASYNCGOTO ) ) {
ast_copy_string ( chan - > context , saved_context , sizeof ( chan - > context ) ) ;
ast_copy_string ( chan - > exten , saved_exten , sizeof ( chan - > exten ) ) ;
chan - > priority = saved_priority ;
}
/* Restore autoloop flag */
ast_set2_flag ( chan , saved_autoloopflag , AST_FLAG_IN_AUTOLOOP ) ;
/* Restore non-hangup softhangup flags. */
if ( saved_hangup_flags ) {
ast_softhangup_nolock ( chan , saved_hangup_flags ) ;
}
ast_channel_unlock ( chan ) ;
return res ;
}
static int handle_gosub ( struct ast_channel * chan , AGI * agi , int argc , const char * const * argv )
{
int old_priority , priority ;
char old_context [ AST_MAX_CONTEXT ] , old_extension [ AST_MAX_EXTENSION ] ;
struct ast_app * theapp ;
int res ;
int priority ;
int old_autoloopflag ;
int old_priority ;
const char * old_context ;
const char * old_extension ;
char * gosub_args ;
if ( argc < 4 | | argc > 5 ) {
@ -774,80 +1103,122 @@ static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char
return RESULT_FAILURE ;
}
/* Save previous location, since we're going to change it */
ast_copy_string ( old_context , chan - > context , sizeof ( old_context ) ) ;
ast_copy_string ( old_extension , chan - > exten , sizeof ( old_extension ) ) ;
old_priority = chan - > priority ;
if ( ! ( theapp = pbx_findapp ( " Gosub " ) ) ) {
ast_log ( LOG_ERROR , " Gosub() cannot be found in the list of loaded applications \n " ) ;
ast_agi_send ( agi - > fd , chan , " 503 result=-2 Gosub is not loaded \n " ) ;
return RESULT_FAILURE ;
}
/* Apparently, if you run ast_pbx_run on a channel that already has a pbx
* structure , you need to add 1 to the priority to get it to go to the
* right place . But if it doesn ' t have a pbx structure , then leaving off
* the 1 is the right thing to do . See how this code differs when we
* call a Gosub for the CALLEE channel in Dial or Queue .
*/
if ( argc = = 5 ) {
if ( asprintf ( & gosub_args , " %s,%s,%d(%s) " , argv [ 1 ] , argv [ 2 ] , priority + ( chan - > pbx ? 1 : 0 ) , argv [ 4 ] ) < 0 ) {
ast_log ( LOG_WARNING , " asprintf() failed: %s \n " , strerror ( errno ) ) ;
if ( ast_asprintf ( & gosub_args , " %s,%s,%d(%s) " , argv [ 1 ] , argv [ 2 ] , priority , argv [ 4 ] ) < 0 ) {
gosub_args = NULL ;
}
} else {
if ( asprintf ( & gosub_args , " %s,%s,%d " , argv [ 1 ] , argv [ 2 ] , priority + ( chan - > pbx ? 1 : 0 ) ) < 0 ) {
ast_log ( LOG_WARNING , " asprintf() failed: %s \n " , strerror ( errno ) ) ;
if ( ast_asprintf ( & gosub_args , " %s,%s,%d " , argv [ 1 ] , argv [ 2 ] , priority ) < 0 ) {
gosub_args = NULL ;
}
}
if ( ! gosub_args ) {
ast_agi_send ( agi - > fd , chan , " 503 result=-2 Memory allocation failure \n " ) ;
return RESULT_FAILURE ;
}
if ( gosub_args ) {
int res ;
ast_channel_lock ( chan ) ;
ast_debug ( 1 , " Trying gosub with arguments '%s' \n " , gosub_args ) ;
ast_verb ( 3 , " %s AGI %s(%s) start \n " , chan - > name , app_gosub , gosub_args ) ;
if ( ( res = pbx_exec ( chan , theapp , gosub_args ) ) = = 0 ) {
struct ast_pbx * pbx = chan - > pbx ;
struct ast_pbx_args args ;
struct ast_datastore * stack_store = ast_channel_datastore_find ( chan , & stack_info , NULL ) ;
AST_LIST_HEAD ( , gosub_stack_frame ) * oldlist ;
/* Save autoloop flag */
old_autoloopflag = ast_test_flag ( chan , AST_FLAG_IN_AUTOLOOP ) ;
ast_set_flag ( chan , AST_FLAG_IN_AUTOLOOP ) ;
/* Save previous location, since we're going to change it */
old_context = ast_strdupa ( chan - > context ) ;
old_extension = ast_strdupa ( chan - > exten ) ;
old_priority = chan - > priority ;
ast_debug ( 4 , " %s Original location: %s,%s,%d \n " , chan - > name ,
old_context , old_extension , old_priority ) ;
ast_channel_unlock ( chan ) ;
res = gosub_exec ( chan , gosub_args ) ;
if ( ! res ) {
struct ast_datastore * stack_store ;
/* Mark the return location as special. */
ast_channel_lock ( chan ) ;
stack_store = ast_channel_datastore_find ( chan , & stack_info , NULL ) ;
if ( ! stack_store ) {
/* Should never happen! */
ast_log ( LOG_ERROR , " No %s stack! \n " , app_gosub ) ;
res = - 1 ;
} else {
struct gosub_stack_list * oldlist ;
struct gosub_stack_frame * cur ;
if ( ! stack_store ) {
ast_log ( LOG_WARNING , " No GoSub stack remaining after AGI GoSub execution. \n " ) ;
ast_free ( gosub_args ) ;
return RESULT_FAILURE ;
}
oldlist = stack_store - > data ;
cur = AST_LIST_FIRST ( oldlist ) ;
cur - > is_agi = 1 ;
memset ( & args , 0 , sizeof ( args ) ) ;
args . no_hangup_chan = 1 ;
/* Suppress warning about PBX already existing */
chan - > pbx = NULL ;
ast_agi_send ( agi - > fd , chan , " 100 result=0 Trying... \n " ) ;
ast_pbx_run_args ( chan , & args ) ;
ast_agi_send ( agi - > fd , chan , " 200 result=0 Gosub complete \n " ) ;
if ( chan - > pbx ) {
ast_free ( chan - > pbx ) ;
}
chan - > pbx = pbx ;
cur - > is_special = 1 ;
}
ast_channel_unlock ( chan ) ;
}
if ( ! res ) {
struct ast_pbx * pbx ;
struct ast_pbx_args args ;
int abnormal_exit ;
memset ( & args , 0 , sizeof ( args ) ) ;
args . no_hangup_chan = 1 ;
ast_channel_lock ( chan ) ;
/* Next dialplan priority. */
+ + chan - > priority ;
/* Suppress warning about PBX already existing */
pbx = chan - > pbx ;
chan - > pbx = NULL ;
ast_channel_unlock ( chan ) ;
ast_agi_send ( agi - > fd , chan , " 100 result=0 Trying... \n " ) ;
ast_pbx_run_args ( chan , & args ) ;
ast_channel_lock ( chan ) ;
ast_free ( chan - > pbx ) ;
chan - > pbx = pbx ;
/* Did the routine return? */
if ( chan - > priority = = old_priority
& & ! strcmp ( chan - > context , old_context )
& & ! strcmp ( chan - > exten , old_extension ) ) {
ast_verb ( 3 , " %s AGI %s(%s) complete GOSUB_RETVAL=%s \n " ,
chan - > name , app_gosub , gosub_args ,
S_OR ( pbx_builtin_getvar_helper ( chan , " GOSUB_RETVAL " ) , " " ) ) ;
abnormal_exit = 0 ;
} else {
ast_agi_send ( agi - > fd , chan , " 200 result=%d Gosub failed \n " , res ) ;
ast_log ( LOG_NOTICE , " %s Abnormal AGI %s(%s) exit. Popping routine return locations. \n " ,
chan - > name , app_gosub , gosub_args ) ;
balance_stack ( chan ) ;
pbx_builtin_setvar_helper ( chan , " GOSUB_RETVAL " , " " ) ;
abnormal_exit = 1 ;
}
ast_free ( gosub_args ) ;
ast_channel_unlock ( chan ) ;
ast_agi_send ( agi - > fd , chan , " 200 result=0 Gosub complete%s \n " ,
abnormal_exit ? " (abnormal exit) " : " " ) ;
} else {
ast_agi_send ( agi - > fd , chan , " 503 result=-2 Memory allocation failure \n " ) ;
return RESULT_FAILURE ;
ast_agi_send ( agi - > fd , chan , " 200 result=%d Gosub failed \n " , res ) ;
}
/* Must use free because the memory was allocated by asprintf(). */
free ( gosub_args ) ;
ast_channel_lock ( chan ) ;
ast_debug ( 4 , " %s Ending location: %s,%s,%d \n " , chan - > name ,
chan - > context , chan - > exten , chan - > priority ) ;
/* Restore previous location */
ast_copy_string ( chan - > context , old_context , sizeof ( chan - > context ) ) ;
ast_copy_string ( chan - > exten , old_extension , sizeof ( chan - > exten ) ) ;
chan - > priority = old_priority ;
/* Restore autoloop flag */
ast_set2_flag ( chan , old_autoloopflag , AST_FLAG_IN_AUTOLOOP ) ;
ast_channel_unlock ( chan ) ;
return RESULT_SUCCESS ;
}
@ -856,6 +1227,8 @@ static struct agi_command gosub_agi_command =
static int unload_module ( void )
{
ast_install_stack_functions ( NULL ) ;
ast_agi_unregister ( ast_module_info - > self , & gosub_agi_command ) ;
ast_unregister_application ( app_return ) ;
@ -871,6 +1244,12 @@ static int unload_module(void)
static int load_module ( void )
{
/* Setup the stack application callback functions. */
static struct ast_app_stack_funcs funcs = {
. run_sub = gosub_run ,
. expand_sub_args = expand_gosub_args ,
} ;
ast_agi_register ( ast_module_info - > self , & gosub_agi_command ) ;
ast_register_application_xml ( app_pop , pop_exec ) ;
@ -881,6 +1260,9 @@ static int load_module(void)
ast_custom_function_register ( & peek_function ) ;
ast_custom_function_register ( & stackpeek_function ) ;
funcs . module = ast_module_info - > self ,
ast_install_stack_functions ( & funcs ) ;
return 0 ;
}