@ -200,10 +200,10 @@ 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 ) ;
@ -218,11 +218,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 ;
@ -290,8 +293,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 ) ;
@ -305,7 +309,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 ) ) ) {
@ -316,23 +321,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 ;
@ -352,12 +364,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_channel_context_set ( chan , oldframe - > context ) ;
ast_channel_exten_set ( chan , oldframe - > extension ) ;
if ( ast_test_flag ( ast_channel_flags ( chan ) , AST_FLAG_IN_AUTOLOOP ) ) {
- - oldframe - > priority ;
}
ast_channel_priority_set ( chan , oldframe - > priority ) ;
gosub_release_frame ( chan , oldframe ) ;
/* Set a return value, if any */
@ -366,10 +390,95 @@ 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 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 = ast_channel_exten ( chan ) ;
}
if ( ast_strlen_zero ( context ) ) {
context = ast_channel_context ( chan ) ;
}
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 ] ;
@ -560,7 +669,7 @@ 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 ;
@ -595,7 +704,7 @@ 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 ;
ast_channel_lock ( chan ) ;
@ -662,7 +771,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 ,
@ -729,6 +838,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 ) ;
@ -742,11 +852,211 @@ static struct ast_custom_function stackpeek_function = {
. read2 = stackpeek_read ,
} ;
/*!
* \ internal
* \ brief Pop stack frames until remove a special return location .
* \ 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 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 " ,
ast_channel_name ( chan ) , app_gosub , sub_args ) ;
/* Save non-hangup softhangup flags. */
saved_hangup_flags = ast_channel_softhangup_internal_flag ( chan )
& ( AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE ) ;
if ( saved_hangup_flags ) {
ast_channel_clear_softhangup ( chan ,
AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE ) ;
}
/* Save autoloop flag */
saved_autoloopflag = ast_test_flag ( ast_channel_flags ( chan ) , AST_FLAG_IN_AUTOLOOP ) ;
ast_set_flag ( ast_channel_flags ( chan ) , AST_FLAG_IN_AUTOLOOP ) ;
/* Save current dialplan location */
saved_context = ast_strdupa ( ast_channel_context ( chan ) ) ;
saved_exten = ast_strdupa ( ast_channel_exten ( chan ) ) ;
saved_priority = ast_channel_priority ( chan ) ;
ast_debug ( 4 , " %s Original location: %s,%s,%d \n " , ast_channel_name ( chan ) ,
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 ( ast_channel_softhangup_internal_flag ( chan ) & AST_SOFTHANGUP_UNBRIDGE ) {
saved_hangup_flags | = AST_SOFTHANGUP_UNBRIDGE ;
ast_channel_clear_softhangup ( chan , AST_SOFTHANGUP_UNBRIDGE ) ;
}
if ( ast_check_hangup ( chan ) ) {
if ( ast_channel_softhangup_internal_flag ( chan ) & AST_SOFTHANGUP_ASYNCGOTO ) {
ast_log ( LOG_ERROR , " %s An async goto just messed up our execution location. \n " ,
ast_channel_name ( chan ) ) ;
break ;
}
if ( ! ignore_hangup ) {
break ;
}
}
/* Next dialplan priority. */
ast_channel_priority_set ( chan , ast_channel_priority ( chan ) + 1 ) ;
ast_channel_unlock ( chan ) ;
res = ast_spawn_extension ( chan , ast_channel_context ( chan ) ,
ast_channel_exten ( chan ) , ast_channel_priority ( chan ) ,
S_COR ( ast_channel_caller ( chan ) - > id . number . valid ,
ast_channel_caller ( chan ) - > 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 " ,
ast_channel_context ( chan ) , ast_channel_exten ( chan ) ,
ast_channel_priority ( chan ) , res , ast_channel_name ( chan ) ) ;
ast_verb ( 2 , " Spawn extension (%s, %s, %d) exited non-zero on '%s' \n " ,
ast_channel_context ( chan ) , ast_channel_exten ( chan ) ,
ast_channel_priority ( chan ) , ast_channel_name ( chan ) ) ;
}
/* Did the routine return? */
if ( ast_channel_priority ( chan ) = = saved_priority
& & ! strcmp ( ast_channel_context ( chan ) , saved_context )
& & ! strcmp ( ast_channel_exten ( chan ) , saved_exten ) ) {
ast_verb ( 3 , " %s Internal %s(%s) complete GOSUB_RETVAL=%s \n " ,
ast_channel_name ( chan ) , 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 " ,
ast_channel_name ( chan ) , 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 " , ast_channel_name ( chan ) ,
ast_channel_context ( chan ) , ast_channel_exten ( chan ) ,
ast_channel_priority ( chan ) ) ;
/* Restore dialplan location */
if ( ! ( ast_channel_softhangup_internal_flag ( chan ) & AST_SOFTHANGUP_ASYNCGOTO ) ) {
ast_channel_context_set ( chan , saved_context ) ;
ast_channel_exten_set ( chan , saved_exten ) ;
ast_channel_priority_set ( chan , saved_priority ) ;
}
/* Restore autoloop flag */
ast_set2_flag ( ast_channel_flags ( 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 ) {
@ -770,80 +1080,125 @@ 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 , ast_channel_context ( chan ) , sizeof ( old_context ) ) ;
ast_copy_string ( old_extension , ast_channel_exten ( chan ) , sizeof ( old_extension ) ) ;
old_priority = ast_channel_priority ( chan ) ;
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 + ( ast_channel_pbx ( chan ) ? 1 : 0 ) , argv [ 4 ] ) < 0 ) {
if ( asprintf ( & gosub_args , " %s,%s,%d(%s) " , argv [ 1 ] , argv [ 2 ] , priority , argv [ 4 ] ) < 0 ) {
ast_log ( LOG_WARNING , " asprintf() failed: %s \n " , strerror ( errno ) ) ;
gosub_args = NULL ;
}
} else {
if ( asprintf ( & gosub_args , " %s,%s,%d " , argv [ 1 ] , argv [ 2 ] , priority + ( ast_channel_pbx ( chan ) ? 1 : 0 ) ) < 0 ) {
if ( asprintf ( & gosub_args , " %s,%s,%d " , argv [ 1 ] , argv [ 2 ] , priority ) < 0 ) {
ast_log ( LOG_WARNING , " asprintf() failed: %s \n " , strerror ( errno ) ) ;
gosub_args = NULL ;
}
}
if ( ! gosub_args ) {
ast_agi_send ( agi - > fd , chan , " 503 result=-2 Memory allocation failure \n " ) ;
return RESULT_FAILURE ;
}
ast_channel_lock ( chan ) ;
if ( gosub_args ) {
int res ;
ast_verb ( 3 , " %s AGI %s(%s) start \n " , ast_channel_name ( chan ) , app_gosub , gosub_args ) ;
ast_debug ( 1 , " Trying gosub with arguments '%s' \n " , gosub_args ) ;
/* Save autoloop flag */
old_autoloopflag = ast_test_flag ( ast_channel_flags ( chan ) , AST_FLAG_IN_AUTOLOOP ) ;
ast_set_flag ( ast_channel_flags ( chan ) , AST_FLAG_IN_AUTOLOOP ) ;
/* Save previous location, since we're going to change it */
old_context = ast_strdupa ( ast_channel_context ( chan ) ) ;
old_extension = ast_strdupa ( ast_channel_exten ( chan ) ) ;
old_priority = ast_channel_priority ( chan ) ;
ast_debug ( 4 , " %s Original location: %s,%s,%d \n " , ast_channel_name ( chan ) ,
old_context , old_extension , old_priority ) ;
ast_channel_unlock ( chan ) ;
if ( ( res = pbx_exec ( chan , theapp , gosub_args ) ) = = 0 ) {
struct ast_pbx * pbx = ast_channel_pbx ( chan ) ;
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 ;
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 */
ast_channel_pbx_set ( chan , 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 ( ast_channel_pbx ( chan ) ) {
ast_free ( ast_channel_pbx ( chan ) ) ;
}
ast_channel_pbx_set ( chan , 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. */
ast_channel_priority_set ( chan , ast_channel_priority ( chan ) + 1 ) ;
/* Suppress warning about PBX already existing */
pbx = ast_channel_pbx ( chan ) ;
ast_channel_pbx_set ( chan , 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 ( ast_channel_pbx ( chan ) ) ;
ast_channel_pbx_set ( chan , pbx ) ;
/* Did the routine return? */
if ( ast_channel_priority ( chan ) = = old_priority
& & ! strcmp ( ast_channel_context ( chan ) , old_context )
& & ! strcmp ( ast_channel_exten ( chan ) , old_extension ) ) {
ast_verb ( 3 , " %s AGI %s(%s) complete GOSUB_RETVAL=%s \n " ,
ast_channel_name ( chan ) , 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 " ,
ast_channel_name ( chan ) , 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 " , ast_channel_name ( chan ) ,
ast_channel_context ( chan ) , ast_channel_exten ( chan ) ,
ast_channel_priority ( chan ) ) ;
/* Restore previous location */
ast_channel_context_set ( chan , old_context ) ;
ast_channel_exten_set ( chan , old_extension ) ;
ast_channel_priority_set ( chan , old_priority ) ;
/* Restore autoloop flag */
ast_set2_flag ( ast_channel_flags ( chan ) , old_autoloopflag , AST_FLAG_IN_AUTOLOOP ) ;
ast_channel_unlock ( chan ) ;
return RESULT_SUCCESS ;
}
@ -852,7 +1207,7 @@ static struct agi_command gosub_agi_command =
static int unload_module ( void )
{
struct ast_context * con ;
ast_install_stack_functions ( NULL ) ;
ast_agi_unregister ( ast_module_info - > self , & gosub_agi_command ) ;
@ -864,29 +1219,16 @@ static int unload_module(void)
ast_custom_function_unregister ( & peek_function ) ;
ast_custom_function_unregister ( & stackpeek_function ) ;
con = ast_context_find ( " gosub_virtual_context " ) ;
if ( con ) {
/* leave nothing behind */
ast_context_remove_extension2 ( con , " s " , 1 , NULL , 0 ) ;
ast_context_destroy ( con , " app_stack " ) ;
}
return 0 ;
}
static int load_module ( void )
{
struct ast_context * con ;
/* Create internal gosub return target to indicate successful completion. */
con = ast_context_find_or_create ( NULL , NULL , " gosub_virtual_context " , " app_stack " ) ;
if ( ! con ) {
ast_log ( LOG_ERROR , " 'gosub_virtual_context' does not exist and unable to create \n " ) ;
} else {
ast_add_extension2 ( con , 1 , " s " , 1 , NULL , NULL , " NoOp " ,
ast_strdup ( " Internal Gosub call complete GOSUB_RETVAL=${GOSUB_RETVAL} " ) ,
ast_free_ptr , " app_stack " ) ;
}
/* 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 ) ;
@ -898,6 +1240,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 ;
}