Stack applications Gosub, Return, etc. More...
#include "asterisk.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/manager.h"
#include "asterisk/channel.h"
#include "asterisk/agi.h"
Go to the source code of this file.
Data Structures | |
struct | gosub_stack_frame |
Functions | |
AST_LIST_HEAD (gosub_stack_list, gosub_stack_frame) | |
AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT|AST_MODFLAG_LOAD_ORDER,"Dialplan subroutines (Gosub, Return, etc)",.load=load_module,.unload=unload_module,.load_pri=AST_MODPRI_APP_DEPEND,.nonoptreq="res_agi",) | |
static void | balance_stack (struct ast_channel *chan) |
static const char * | expand_gosub_args (struct ast_channel *chan, const char *args) |
static int | frame_set_var (struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value) |
static struct gosub_stack_frame * | gosub_allocate_frame (const char *context, const char *extension, int priority, unsigned char arguments) |
static int | gosub_exec (struct ast_channel *chan, const char *data) |
static void | gosub_free (void *data) |
static void | gosub_release_frame (struct ast_channel *chan, struct gosub_stack_frame *frame) |
static int | gosub_run (struct ast_channel *chan, const char *sub_args, int ignore_hangup) |
static int | gosubif_exec (struct ast_channel *chan, const char *data) |
static int | handle_gosub (struct ast_channel *chan, AGI *agi, int argc, const char *const *argv) |
static int | load_module (void) |
static int | local_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
static int | local_write (struct ast_channel *chan, const char *cmd, char *var, const char *value) |
static int | peek_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
static int | pop_exec (struct ast_channel *chan, const char *data) |
static int | return_exec (struct ast_channel *chan, const char *data) |
static int | stackpeek_read (struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len) |
static int | unload_module (void) |
Variables | |
static const char | app_gosub [] = "Gosub" |
static const char | app_gosubif [] = "GosubIf" |
static const char | app_pop [] = "StackPop" |
static const char | app_return [] = "Return" |
static struct agi_command | gosub_agi_command |
static struct ast_custom_function | local_function |
static struct ast_custom_function | peek_function |
static struct ast_datastore_info | stack_info |
static struct ast_custom_function | stackpeek_function |
Stack applications Gosub, Return, etc.
Definition in file app_stack.c.
AST_LIST_HEAD | ( | gosub_stack_list | , | |
gosub_stack_frame | ||||
) |
AST_MODULE_INFO | ( | ASTERISK_GPL_KEY | , | |
AST_MODFLAG_DEFAULT| | AST_MODFLAG_LOAD_ORDER, | |||
"Dialplan subroutines (Gosub, Return, etc)" | , | |||
. | load = load_module , |
|||
. | unload = unload_module , |
|||
. | load_pri = AST_MODPRI_APP_DEPEND , |
|||
. | nonoptreq = "res_agi" | |||
) |
static void balance_stack | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 895 of file app_stack.c.
References ast_channel_datastore_find(), AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), ast_datastore::data, gosub_release_frame(), and LOG_WARNING.
Referenced by gosub_run(), and handle_gosub().
00896 { 00897 struct ast_datastore *stack_store; 00898 struct gosub_stack_list *oldlist; 00899 struct gosub_stack_frame *oldframe; 00900 int found; 00901 00902 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); 00903 if (!stack_store) { 00904 ast_log(LOG_WARNING, "No %s stack allocated.\n", app_gosub); 00905 return; 00906 } 00907 00908 oldlist = stack_store->data; 00909 AST_LIST_LOCK(oldlist); 00910 do { 00911 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries); 00912 if (!oldframe) { 00913 break; 00914 } 00915 found = oldframe->is_special; 00916 gosub_release_frame(chan, oldframe); 00917 } while (!found); 00918 AST_LIST_UNLOCK(oldlist); 00919 }
static const char* expand_gosub_args | ( | struct ast_channel * | chan, | |
const char * | args | |||
) | [static] |
Definition at line 419 of file app_stack.c.
References ast_channel_lock, ast_channel_unlock, ast_debug, ast_log(), ast_malloc, ast_strdupa, ast_strlen_zero(), ast_channel::context, context, ast_channel::exten, exten, len(), LOG_WARNING, and parse().
Referenced by load_module().
00420 { 00421 int len; 00422 char *parse; 00423 char *label; 00424 char *new_args; 00425 const char *context; 00426 const char *exten; 00427 const char *pri; 00428 00429 /* Separate the context,exten,pri from the optional routine arguments. */ 00430 parse = ast_strdupa(args); 00431 label = strsep(&parse, "("); 00432 if (parse) { 00433 char *endparen; 00434 00435 endparen = strrchr(parse, ')'); 00436 if (endparen) { 00437 *endparen = '\0'; 00438 } else { 00439 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", args); 00440 } 00441 } 00442 00443 /* Split context,exten,pri */ 00444 context = strsep(&label, ","); 00445 exten = strsep(&label, ","); 00446 pri = strsep(&label, ","); 00447 if (!exten) { 00448 /* Only a priority in this one */ 00449 pri = context; 00450 exten = NULL; 00451 context = NULL; 00452 } else if (!pri) { 00453 /* Only an extension and priority in this one */ 00454 pri = exten; 00455 exten = context; 00456 context = NULL; 00457 } 00458 00459 ast_channel_lock(chan); 00460 if (ast_strlen_zero(exten)) { 00461 exten = chan->exten; 00462 } 00463 if (ast_strlen_zero(context)) { 00464 context = chan->context; 00465 } 00466 len = strlen(context) + strlen(exten) + strlen(pri) + 3; 00467 if (!ast_strlen_zero(parse)) { 00468 len += 2 + strlen(parse); 00469 } 00470 new_args = ast_malloc(len); 00471 if (new_args) { 00472 if (ast_strlen_zero(parse)) { 00473 snprintf(new_args, len, "%s,%s,%s", context, exten, pri); 00474 } else { 00475 snprintf(new_args, len, "%s,%s,%s(%s)", context, exten, pri, parse); 00476 } 00477 } 00478 ast_channel_unlock(chan); 00479 00480 ast_debug(4, "Gosub args:%s new_args:%s\n", args, new_args ? new_args : ""); 00481 00482 return new_args; 00483 }
static int frame_set_var | ( | struct ast_channel * | chan, | |
struct gosub_stack_frame * | frame, | |||
const char * | var, | |||
const char * | value | |||
) | [static] |
Definition at line 234 of file app_stack.c.
References AST_LIST_INSERT_HEAD, AST_LIST_TRAVERSE, ast_var_assign(), ast_var_name(), EVENT_FLAG_DIALPLAN, manager_event, pbx_builtin_pushvar_helper(), and pbx_builtin_setvar_helper().
Referenced by gosub_exec(), and local_write().
00235 { 00236 struct ast_var_t *variables; 00237 int found = 0; 00238 00239 /* Does this variable already exist? */ 00240 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) { 00241 if (!strcmp(var, ast_var_name(variables))) { 00242 found = 1; 00243 break; 00244 } 00245 } 00246 00247 if (!found) { 00248 if ((variables = ast_var_assign(var, ""))) { 00249 AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries); 00250 } 00251 pbx_builtin_pushvar_helper(chan, var, value); 00252 } else { 00253 pbx_builtin_setvar_helper(chan, var, value); 00254 } 00255 00256 manager_event(EVENT_FLAG_DIALPLAN, "VarSet", 00257 "Channel: %s\r\n" 00258 "Variable: LOCAL(%s)\r\n" 00259 "Value: %s\r\n" 00260 "Uniqueid: %s\r\n", 00261 chan->name, var, value, chan->uniqueid); 00262 return 0; 00263 }
static struct gosub_stack_frame* gosub_allocate_frame | ( | const char * | context, | |
const char * | extension, | |||
int | priority, | |||
unsigned char | arguments | |||
) | [static, read] |
Definition at line 284 of file app_stack.c.
References ast_calloc, and AST_LIST_HEAD_INIT_NOLOCK.
Referenced by gosub_exec().
00285 { 00286 struct gosub_stack_frame *new = NULL; 00287 int len_extension = strlen(extension), len_context = strlen(context); 00288 00289 if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) { 00290 AST_LIST_HEAD_INIT_NOLOCK(&new->varshead); 00291 strcpy(new->extension, extension); 00292 new->context = new->extension + len_extension + 1; 00293 strcpy(new->context, context); 00294 new->priority = priority; 00295 new->arguments = arguments; 00296 } 00297 return new; 00298 }
static int gosub_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 485 of file app_stack.c.
References AST_APP_ARG, ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_datastore_alloc, ast_datastore_free(), ast_debug, AST_DECLARE_APP_ARGS, ast_exists_extension(), AST_FLAG_IN_AUTOLOOP, AST_LIST_FIRST, AST_LIST_HEAD_INIT, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_parseable_goto(), AST_STANDARD_RAW_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_channel::caller, ast_channel::context, ast_datastore::data, ast_channel::exten, frame_set_var(), gosub_allocate_frame(), ast_party_caller::id, LOG_ERROR, LOG_WARNING, ast_party_id::number, orig_exten(), parse(), ast_channel::priority, S_COR, ast_party_number::str, and ast_party_number::valid.
Referenced by gosub_run(), gosubif_exec(), handle_gosub(), and load_module().
00486 { 00487 struct ast_datastore *stack_store; 00488 struct gosub_stack_list *oldlist; 00489 struct gosub_stack_frame *newframe; 00490 struct gosub_stack_frame *lastframe; 00491 char argname[15]; 00492 char *parse; 00493 char *label; 00494 char *caller_id; 00495 char *orig_context; 00496 char *orig_exten; 00497 char *dest_context; 00498 char *dest_exten; 00499 int orig_priority; 00500 int dest_priority; 00501 int i; 00502 int max_argc = 0; 00503 AST_DECLARE_APP_ARGS(args2, 00504 AST_APP_ARG(argval)[100]; 00505 ); 00506 00507 if (ast_strlen_zero(data)) { 00508 ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub); 00509 return -1; 00510 } 00511 00512 /* 00513 * Separate the arguments from the label 00514 * 00515 * NOTE: You cannot use ast_app_separate_args for this, because 00516 * '(' cannot be used as a delimiter. 00517 */ 00518 parse = ast_strdupa(data); 00519 label = strsep(&parse, "("); 00520 if (parse) { 00521 char *endparen; 00522 00523 endparen = strrchr(parse, ')'); 00524 if (endparen) { 00525 *endparen = '\0'; 00526 } else { 00527 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", data); 00528 } 00529 AST_STANDARD_RAW_ARGS(args2, parse); 00530 } else { 00531 args2.argc = 0; 00532 } 00533 00534 ast_channel_lock(chan); 00535 orig_context = ast_strdupa(chan->context); 00536 orig_exten = ast_strdupa(chan->exten); 00537 orig_priority = chan->priority; 00538 ast_channel_unlock(chan); 00539 00540 if (ast_parseable_goto(chan, label)) { 00541 ast_log(LOG_ERROR, "%s address is invalid: '%s'\n", app_gosub, data); 00542 goto error_exit; 00543 } 00544 00545 ast_channel_lock(chan); 00546 dest_context = ast_strdupa(chan->context); 00547 dest_exten = ast_strdupa(chan->exten); 00548 dest_priority = chan->priority; 00549 if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP)) { 00550 ++dest_priority; 00551 } 00552 caller_id = S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL); 00553 if (caller_id) { 00554 caller_id = ast_strdupa(caller_id); 00555 } 00556 ast_channel_unlock(chan); 00557 00558 if (!ast_exists_extension(chan, dest_context, dest_exten, dest_priority, caller_id)) { 00559 ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for %s: (Context:%s, Extension:%s, Priority:%d)\n", 00560 app_gosub, dest_context, dest_exten, dest_priority); 00561 goto error_exit; 00562 } 00563 00564 /* Now we know that we're going to a new location */ 00565 00566 ast_channel_lock(chan); 00567 00568 /* Find stack datastore return list. */ 00569 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) { 00570 ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", 00571 chan->name); 00572 stack_store = ast_datastore_alloc(&stack_info, NULL); 00573 if (!stack_store) { 00574 ast_log(LOG_ERROR, "Unable to allocate new datastore. %s failed.\n", 00575 app_gosub); 00576 goto error_exit_locked; 00577 } 00578 00579 oldlist = ast_calloc(1, sizeof(*oldlist)); 00580 if (!oldlist) { 00581 ast_log(LOG_ERROR, "Unable to allocate datastore list head. %s failed.\n", 00582 app_gosub); 00583 ast_datastore_free(stack_store); 00584 goto error_exit_locked; 00585 } 00586 AST_LIST_HEAD_INIT(oldlist); 00587 00588 stack_store->data = oldlist; 00589 ast_channel_datastore_add(chan, stack_store); 00590 } else { 00591 oldlist = stack_store->data; 00592 } 00593 00594 if ((lastframe = AST_LIST_FIRST(oldlist))) { 00595 max_argc = lastframe->arguments; 00596 } 00597 00598 /* Mask out previous Gosub arguments in this invocation */ 00599 if (args2.argc > max_argc) { 00600 max_argc = args2.argc; 00601 } 00602 00603 /* Create the return address */ 00604 newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, max_argc); 00605 if (!newframe) { 00606 goto error_exit_locked; 00607 } 00608 00609 /* Set our arguments */ 00610 for (i = 0; i < max_argc; i++) { 00611 snprintf(argname, sizeof(argname), "ARG%d", i + 1); 00612 frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : ""); 00613 ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : ""); 00614 } 00615 snprintf(argname, sizeof(argname), "%u", args2.argc); 00616 frame_set_var(chan, newframe, "ARGC", argname); 00617 00618 /* And finally, save our return address */ 00619 AST_LIST_LOCK(oldlist); 00620 AST_LIST_INSERT_HEAD(oldlist, newframe, entries); 00621 AST_LIST_UNLOCK(oldlist); 00622 ast_channel_unlock(chan); 00623 00624 return 0; 00625 00626 error_exit: 00627 ast_channel_lock(chan); 00628 00629 error_exit_locked: 00630 /* Restore the original dialplan location. */ 00631 ast_copy_string(chan->context, orig_context, sizeof(chan->context)); 00632 ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten)); 00633 chan->priority = orig_priority; 00634 ast_channel_unlock(chan); 00635 return -1; 00636 }
static void gosub_free | ( | void * | data | ) | [static] |
Definition at line 300 of file app_stack.c.
References ast_free, AST_LIST_HEAD_DESTROY, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and gosub_release_frame().
00301 { 00302 struct gosub_stack_list *oldlist = data; 00303 struct gosub_stack_frame *oldframe; 00304 00305 AST_LIST_LOCK(oldlist); 00306 while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) { 00307 gosub_release_frame(NULL, oldframe); 00308 } 00309 AST_LIST_UNLOCK(oldlist); 00310 AST_LIST_HEAD_DESTROY(oldlist); 00311 ast_free(oldlist); 00312 }
static void gosub_release_frame | ( | struct ast_channel * | chan, | |
struct gosub_stack_frame * | frame | |||
) | [static] |
Definition at line 265 of file app_stack.c.
References ast_free, AST_LIST_REMOVE_HEAD, ast_var_delete(), ast_var_name(), and pbx_builtin_setvar_helper().
Referenced by balance_stack(), gosub_free(), pop_exec(), and return_exec().
00266 { 00267 struct ast_var_t *vardata; 00268 00269 /* If chan is not defined, then we're calling it as part of gosub_free, 00270 * and the channel variables will be deallocated anyway. Otherwise, we're 00271 * just releasing a single frame, so we need to clean up the arguments for 00272 * that frame, so that we re-expose the variables from the previous frame 00273 * that were hidden by this one. 00274 */ 00275 while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) { 00276 if (chan) 00277 pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL); 00278 ast_var_delete(vardata); 00279 } 00280 00281 ast_free(frame); 00282 }
static int gosub_run | ( | struct ast_channel * | chan, | |
const char * | sub_args, | |||
int | ignore_hangup | |||
) | [static] |
Definition at line 936 of file app_stack.c.
References ast_channel::_softhangup, ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_check_hangup(), ast_copy_string(), ast_debug, AST_FLAG_IN_AUTOLOOP, AST_LIST_FIRST, ast_log(), ast_set2_flag, ast_set_flag, AST_SOFTHANGUP_ASYNCGOTO, ast_softhangup_nolock(), AST_SOFTHANGUP_UNBRIDGE, ast_spawn_extension(), ast_strdupa, ast_test_flag, ast_verb, balance_stack(), ast_channel::caller, ast_channel::context, ast_datastore::data, ast_channel::exten, gosub_exec(), ast_party_caller::id, LOG_ERROR, LOG_NOTICE, ast_party_id::number, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_channel::priority, S_COR, S_OR, ast_party_number::str, and ast_party_number::valid.
Referenced by load_module().
00937 { 00938 const char *saved_context; 00939 const char *saved_exten; 00940 int saved_priority; 00941 int saved_hangup_flags; 00942 int saved_autoloopflag; 00943 int res; 00944 00945 ast_channel_lock(chan); 00946 00947 ast_verb(3, "%s Internal %s(%s) start\n", chan->name, app_gosub, sub_args); 00948 00949 /* Save non-hangup softhangup flags. */ 00950 saved_hangup_flags = chan->_softhangup 00951 & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE); 00952 if (saved_hangup_flags) { 00953 chan->_softhangup &= ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE); 00954 } 00955 00956 /* Save autoloop flag */ 00957 saved_autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP); 00958 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP); 00959 00960 /* Save current dialplan location */ 00961 saved_context = ast_strdupa(chan->context); 00962 saved_exten = ast_strdupa(chan->exten); 00963 saved_priority = chan->priority; 00964 00965 ast_debug(4, "%s Original location: %s,%s,%d\n", chan->name, 00966 saved_context, saved_exten, saved_priority); 00967 00968 ast_channel_unlock(chan); 00969 res = gosub_exec(chan, sub_args); 00970 ast_debug(4, "%s exited with status %d\n", app_gosub, res); 00971 ast_channel_lock(chan); 00972 if (!res) { 00973 struct ast_datastore *stack_store; 00974 00975 /* Mark the return location as special. */ 00976 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); 00977 if (!stack_store) { 00978 /* Should never happen! */ 00979 ast_log(LOG_ERROR, "No %s stack!\n", app_gosub); 00980 res = -1; 00981 } else { 00982 struct gosub_stack_list *oldlist; 00983 struct gosub_stack_frame *cur; 00984 00985 oldlist = stack_store->data; 00986 cur = AST_LIST_FIRST(oldlist); 00987 cur->is_special = 1; 00988 } 00989 } 00990 if (!res) { 00991 int found = 0; /* set if we find at least one match */ 00992 00993 /* 00994 * Run gosub body autoloop. 00995 * 00996 * Note that this loop is inverted from the normal execution 00997 * loop because we just executed the Gosub application as the 00998 * first extension of the autoloop. 00999 */ 01000 do { 01001 /* Check for hangup. */ 01002 if (chan->_softhangup & AST_SOFTHANGUP_UNBRIDGE) { 01003 saved_hangup_flags |= AST_SOFTHANGUP_UNBRIDGE; 01004 chan->_softhangup &= ~AST_SOFTHANGUP_UNBRIDGE; 01005 } 01006 if (ast_check_hangup(chan)) { 01007 if (chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO) { 01008 ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n", 01009 chan->name); 01010 break; 01011 } 01012 if (!ignore_hangup) { 01013 break; 01014 } 01015 } 01016 01017 /* Next dialplan priority. */ 01018 ++chan->priority; 01019 01020 ast_channel_unlock(chan); 01021 res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, 01022 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL), 01023 &found, 1); 01024 ast_channel_lock(chan); 01025 } while (!res); 01026 if (found && res) { 01027 /* Something bad happened, or a hangup has been requested. */ 01028 ast_debug(1, "Spawn extension (%s,%s,%d) exited with %d on '%s'\n", 01029 chan->context, chan->exten, chan->priority, res, chan->name); 01030 ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", 01031 chan->context, chan->exten, chan->priority, chan->name); 01032 } 01033 01034 /* Did the routine return? */ 01035 if (chan->priority == saved_priority 01036 && !strcmp(chan->context, saved_context) 01037 && !strcmp(chan->exten, saved_exten)) { 01038 ast_verb(3, "%s Internal %s(%s) complete GOSUB_RETVAL=%s\n", 01039 chan->name, app_gosub, sub_args, 01040 S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), "")); 01041 } else { 01042 ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n", 01043 chan->name, app_gosub, sub_args); 01044 balance_stack(chan); 01045 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", ""); 01046 } 01047 01048 /* We executed the requested subroutine to the best of our ability. */ 01049 res = 0; 01050 } 01051 01052 ast_debug(4, "%s Ending location: %s,%s,%d\n", chan->name, 01053 chan->context, chan->exten, chan->priority); 01054 01055 /* Restore dialplan location */ 01056 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) { 01057 ast_copy_string(chan->context, saved_context, sizeof(chan->context)); 01058 ast_copy_string(chan->exten, saved_exten, sizeof(chan->exten)); 01059 chan->priority = saved_priority; 01060 } 01061 01062 /* Restore autoloop flag */ 01063 ast_set2_flag(chan, saved_autoloopflag, AST_FLAG_IN_AUTOLOOP); 01064 01065 /* Restore non-hangup softhangup flags. */ 01066 if (saved_hangup_flags) { 01067 ast_softhangup_nolock(chan, saved_hangup_flags); 01068 } 01069 01070 ast_channel_unlock(chan); 01071 01072 return res; 01073 }
static int gosubif_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 638 of file app_stack.c.
References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_NONSTANDARD_RAW_ARGS, ast_strdupa, ast_strlen_zero(), cond, gosub_exec(), LOG_WARNING, and pbx_checkcondition().
Referenced by load_module().
00639 { 00640 char *args; 00641 int res=0; 00642 AST_DECLARE_APP_ARGS(cond, 00643 AST_APP_ARG(ition); 00644 AST_APP_ARG(labels); 00645 ); 00646 AST_DECLARE_APP_ARGS(label, 00647 AST_APP_ARG(iftrue); 00648 AST_APP_ARG(iffalse); 00649 ); 00650 00651 if (ast_strlen_zero(data)) { 00652 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n"); 00653 return 0; 00654 } 00655 00656 args = ast_strdupa(data); 00657 AST_NONSTANDARD_RAW_ARGS(cond, args, '?'); 00658 if (cond.argc != 2) { 00659 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n"); 00660 return 0; 00661 } 00662 00663 AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':'); 00664 00665 if (pbx_checkcondition(cond.ition)) { 00666 if (!ast_strlen_zero(label.iftrue)) 00667 res = gosub_exec(chan, label.iftrue); 00668 } else if (!ast_strlen_zero(label.iffalse)) { 00669 res = gosub_exec(chan, label.iffalse); 00670 } 00671 00672 return res; 00673 }
static int handle_gosub | ( | struct ast_channel * | chan, | |
AGI * | agi, | |||
int | argc, | |||
const char *const * | argv | |||
) | [static] |
Definition at line 1075 of file app_stack.c.
References ast_agi_send(), ast_asprintf, ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_debug, ast_exists_extension(), ast_findlabel_extension(), AST_FLAG_IN_AUTOLOOP, ast_free, AST_LIST_FIRST, ast_log(), ast_pbx_run_args(), ast_set2_flag, ast_set_flag, ast_strdupa, ast_test_flag, ast_verb, balance_stack(), ast_channel::caller, ast_channel::context, ast_datastore::data, ast_channel::exten, agi_state::fd, free, gosub_exec(), ast_party_caller::id, LOG_ERROR, LOG_NOTICE, ast_pbx_args::no_hangup_chan, ast_party_id::number, ast_channel::pbx, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_channel::priority, RESULT_FAILURE, RESULT_SHOWUSAGE, RESULT_SUCCESS, S_COR, S_OR, ast_party_number::str, and ast_party_number::valid.
01076 { 01077 int res; 01078 int priority; 01079 int old_autoloopflag; 01080 int old_priority; 01081 const char *old_context; 01082 const char *old_extension; 01083 char *gosub_args; 01084 01085 if (argc < 4 || argc > 5) { 01086 return RESULT_SHOWUSAGE; 01087 } 01088 01089 ast_debug(1, "Gosub called with %d arguments: 0:%s 1:%s 2:%s 3:%s 4:%s\n", argc, argv[0], argv[1], argv[2], argv[3], argc == 5 ? argv[4] : ""); 01090 01091 if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) { 01092 /* Lookup the priority label */ 01093 priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3], 01094 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL)); 01095 if (priority < 0) { 01096 ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]); 01097 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n"); 01098 return RESULT_FAILURE; 01099 } 01100 } else if (!ast_exists_extension(chan, argv[1], argv[2], priority, 01101 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) { 01102 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n"); 01103 return RESULT_FAILURE; 01104 } 01105 01106 if (argc == 5) { 01107 if (ast_asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) { 01108 gosub_args = NULL; 01109 } 01110 } else { 01111 if (ast_asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) { 01112 gosub_args = NULL; 01113 } 01114 } 01115 if (!gosub_args) { 01116 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n"); 01117 return RESULT_FAILURE; 01118 } 01119 01120 ast_channel_lock(chan); 01121 01122 ast_verb(3, "%s AGI %s(%s) start\n", chan->name, app_gosub, gosub_args); 01123 01124 /* Save autoloop flag */ 01125 old_autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP); 01126 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP); 01127 01128 /* Save previous location, since we're going to change it */ 01129 old_context = ast_strdupa(chan->context); 01130 old_extension = ast_strdupa(chan->exten); 01131 old_priority = chan->priority; 01132 01133 ast_debug(4, "%s Original location: %s,%s,%d\n", chan->name, 01134 old_context, old_extension, old_priority); 01135 ast_channel_unlock(chan); 01136 01137 res = gosub_exec(chan, gosub_args); 01138 if (!res) { 01139 struct ast_datastore *stack_store; 01140 01141 /* Mark the return location as special. */ 01142 ast_channel_lock(chan); 01143 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); 01144 if (!stack_store) { 01145 /* Should never happen! */ 01146 ast_log(LOG_ERROR, "No %s stack!\n", app_gosub); 01147 res = -1; 01148 } else { 01149 struct gosub_stack_list *oldlist; 01150 struct gosub_stack_frame *cur; 01151 01152 oldlist = stack_store->data; 01153 cur = AST_LIST_FIRST(oldlist); 01154 cur->is_special = 1; 01155 } 01156 ast_channel_unlock(chan); 01157 } 01158 if (!res) { 01159 struct ast_pbx *pbx; 01160 struct ast_pbx_args args; 01161 int abnormal_exit; 01162 01163 memset(&args, 0, sizeof(args)); 01164 args.no_hangup_chan = 1; 01165 01166 ast_channel_lock(chan); 01167 01168 /* Next dialplan priority. */ 01169 ++chan->priority; 01170 01171 /* Suppress warning about PBX already existing */ 01172 pbx = chan->pbx; 01173 chan->pbx = NULL; 01174 ast_channel_unlock(chan); 01175 01176 ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n"); 01177 ast_pbx_run_args(chan, &args); 01178 01179 ast_channel_lock(chan); 01180 ast_free(chan->pbx); 01181 chan->pbx = pbx; 01182 01183 /* Did the routine return? */ 01184 if (chan->priority == old_priority 01185 && !strcmp(chan->context, old_context) 01186 && !strcmp(chan->exten, old_extension)) { 01187 ast_verb(3, "%s AGI %s(%s) complete GOSUB_RETVAL=%s\n", 01188 chan->name, app_gosub, gosub_args, 01189 S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), "")); 01190 abnormal_exit = 0; 01191 } else { 01192 ast_log(LOG_NOTICE, "%s Abnormal AGI %s(%s) exit. Popping routine return locations.\n", 01193 chan->name, app_gosub, gosub_args); 01194 balance_stack(chan); 01195 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", ""); 01196 abnormal_exit = 1; 01197 } 01198 ast_channel_unlock(chan); 01199 01200 ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete%s\n", 01201 abnormal_exit ? " (abnormal exit)" : ""); 01202 } else { 01203 ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res); 01204 } 01205 01206 /* Must use free because the memory was allocated by asprintf(). */ 01207 free(gosub_args); 01208 01209 ast_channel_lock(chan); 01210 ast_debug(4, "%s Ending location: %s,%s,%d\n", chan->name, 01211 chan->context, chan->exten, chan->priority); 01212 01213 /* Restore previous location */ 01214 ast_copy_string(chan->context, old_context, sizeof(chan->context)); 01215 ast_copy_string(chan->exten, old_extension, sizeof(chan->exten)); 01216 chan->priority = old_priority; 01217 01218 /* Restore autoloop flag */ 01219 ast_set2_flag(chan, old_autoloopflag, AST_FLAG_IN_AUTOLOOP); 01220 ast_channel_unlock(chan); 01221 01222 return RESULT_SUCCESS; 01223 }
static int load_module | ( | void | ) | [static] |
Definition at line 1245 of file app_stack.c.
References ast_agi_register(), ast_custom_function_register, ast_install_stack_functions(), ast_register_application_xml, expand_gosub_args(), gosub_exec(), gosub_run(), gosubif_exec(), ast_app_stack_funcs::module, pop_exec(), return_exec(), and ast_app_stack_funcs::run_sub.
01246 { 01247 /* Setup the stack application callback functions. */ 01248 static struct ast_app_stack_funcs funcs = { 01249 .run_sub = gosub_run, 01250 .expand_sub_args = expand_gosub_args, 01251 }; 01252 01253 ast_agi_register(ast_module_info->self, &gosub_agi_command); 01254 01255 ast_register_application_xml(app_pop, pop_exec); 01256 ast_register_application_xml(app_return, return_exec); 01257 ast_register_application_xml(app_gosubif, gosubif_exec); 01258 ast_register_application_xml(app_gosub, gosub_exec); 01259 ast_custom_function_register(&local_function); 01260 ast_custom_function_register(&peek_function); 01261 ast_custom_function_register(&stackpeek_function); 01262 01263 funcs.module = ast_module_info->self, 01264 ast_install_stack_functions(&funcs); 01265 01266 return 0; 01267 }
static int local_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 675 of file app_stack.c.
References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_var_name(), ast_datastore::data, LOG_WARNING, pbx_builtin_getvar_helper(), and S_OR.
00676 { 00677 struct ast_datastore *stack_store; 00678 struct gosub_stack_list *oldlist; 00679 struct gosub_stack_frame *frame; 00680 struct ast_var_t *variables; 00681 00682 if (!chan) { 00683 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd); 00684 return -1; 00685 } 00686 00687 ast_channel_lock(chan); 00688 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) { 00689 ast_channel_unlock(chan); 00690 return -1; 00691 } 00692 00693 oldlist = stack_store->data; 00694 AST_LIST_LOCK(oldlist); 00695 if (!(frame = AST_LIST_FIRST(oldlist))) { 00696 /* Not within a Gosub routine */ 00697 AST_LIST_UNLOCK(oldlist); 00698 ast_channel_unlock(chan); 00699 return -1; 00700 } 00701 00702 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) { 00703 if (!strcmp(data, ast_var_name(variables))) { 00704 const char *tmp; 00705 tmp = pbx_builtin_getvar_helper(chan, data); 00706 ast_copy_string(buf, S_OR(tmp, ""), len); 00707 break; 00708 } 00709 } 00710 AST_LIST_UNLOCK(oldlist); 00711 ast_channel_unlock(chan); 00712 return 0; 00713 }
static int local_write | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | var, | |||
const char * | value | |||
) | [static] |
Definition at line 715 of file app_stack.c.
References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_datastore::data, frame_set_var(), LOG_ERROR, and LOG_WARNING.
00716 { 00717 struct ast_datastore *stack_store; 00718 struct gosub_stack_list *oldlist; 00719 struct gosub_stack_frame *frame; 00720 00721 if (!chan) { 00722 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd); 00723 return -1; 00724 } 00725 00726 ast_channel_lock(chan); 00727 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) { 00728 ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var); 00729 ast_channel_unlock(chan); 00730 return -1; 00731 } 00732 00733 oldlist = stack_store->data; 00734 AST_LIST_LOCK(oldlist); 00735 frame = AST_LIST_FIRST(oldlist); 00736 00737 if (frame) { 00738 frame_set_var(chan, frame, var, value); 00739 } 00740 00741 AST_LIST_UNLOCK(oldlist); 00742 ast_channel_unlock(chan); 00743 00744 return 0; 00745 }
static int peek_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 753 of file app_stack.c.
References args, AST_APP_ARG, ast_channel_lock, ast_channel_unlock, ast_copy_string(), AST_DECLARE_APP_ARGS, AST_LIST_TRAVERSE, ast_log(), AST_STANDARD_RAW_ARGS, ast_strlen_zero(), ast_var_name(), ast_var_value(), LOG_ERROR, name, and ast_channel::varshead.
00754 { 00755 int found = 0, n; 00756 struct ast_var_t *variables; 00757 AST_DECLARE_APP_ARGS(args, 00758 AST_APP_ARG(n); 00759 AST_APP_ARG(name); 00760 ); 00761 00762 if (!chan) { 00763 ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n"); 00764 return -1; 00765 } 00766 00767 AST_STANDARD_RAW_ARGS(args, data); 00768 00769 if (ast_strlen_zero(args.n) || ast_strlen_zero(args.name)) { 00770 ast_log(LOG_ERROR, "LOCAL_PEEK requires parameters n and varname\n"); 00771 return -1; 00772 } 00773 00774 n = atoi(args.n); 00775 *buf = '\0'; 00776 00777 ast_channel_lock(chan); 00778 AST_LIST_TRAVERSE(&chan->varshead, variables, entries) { 00779 if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) { 00780 ast_copy_string(buf, ast_var_value(variables), len); 00781 break; 00782 } 00783 } 00784 ast_channel_unlock(chan); 00785 return 0; 00786 }
static int pop_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 314 of file app_stack.c.
References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_debug, AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), ast_datastore::data, gosub_release_frame(), and LOG_WARNING.
Referenced by load_module().
00315 { 00316 struct ast_datastore *stack_store; 00317 struct gosub_stack_frame *oldframe; 00318 struct gosub_stack_list *oldlist; 00319 int res = 0; 00320 00321 ast_channel_lock(chan); 00322 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) { 00323 ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop); 00324 ast_channel_unlock(chan); 00325 return 0; 00326 } 00327 00328 oldlist = stack_store->data; 00329 AST_LIST_LOCK(oldlist); 00330 oldframe = AST_LIST_FIRST(oldlist); 00331 if (oldframe) { 00332 if (oldframe->is_special) { 00333 ast_debug(1, "%s attempted to pop special return location.\n", app_pop); 00334 00335 /* Abort the special routine dialplan execution. Dialplan programming error. */ 00336 res = -1; 00337 } else { 00338 AST_LIST_REMOVE_HEAD(oldlist, entries); 00339 gosub_release_frame(chan, oldframe); 00340 } 00341 } else { 00342 ast_debug(1, "%s called with an empty gosub stack\n", app_pop); 00343 } 00344 AST_LIST_UNLOCK(oldlist); 00345 ast_channel_unlock(chan); 00346 return res; 00347 }
static int return_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 349 of file app_stack.c.
References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), AST_FLAG_IN_AUTOLOOP, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), ast_test_flag, ast_channel::context, ast_datastore::data, ast_channel::exten, gosub_release_frame(), LOG_ERROR, pbx_builtin_setvar_helper(), ast_channel::priority, and S_OR.
Referenced by load_module().
00350 { 00351 struct ast_datastore *stack_store; 00352 struct gosub_stack_frame *oldframe; 00353 struct gosub_stack_list *oldlist; 00354 const char *retval = data; 00355 int res = 0; 00356 00357 ast_channel_lock(chan); 00358 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) { 00359 ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n"); 00360 ast_channel_unlock(chan); 00361 return -1; 00362 } 00363 00364 oldlist = stack_store->data; 00365 AST_LIST_LOCK(oldlist); 00366 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries); 00367 AST_LIST_UNLOCK(oldlist); 00368 00369 if (!oldframe) { 00370 ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n"); 00371 ast_channel_unlock(chan); 00372 return -1; 00373 } 00374 if (oldframe->is_special) { 00375 /* Exit from special routine. */ 00376 res = -1; 00377 } 00378 00379 /* 00380 * We cannot use ast_explicit_goto() because we MUST restore 00381 * what was there before. Channels that do not have a PBX may 00382 * not have the context or exten set. 00383 */ 00384 ast_copy_string(chan->context, oldframe->context, sizeof(chan->context)); 00385 ast_copy_string(chan->exten, oldframe->extension, sizeof(chan->exten)); 00386 if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP)) { 00387 --oldframe->priority; 00388 } 00389 chan->priority = oldframe->priority; 00390 00391 gosub_release_frame(chan, oldframe); 00392 00393 /* Set a return value, if any */ 00394 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, "")); 00395 ast_channel_unlock(chan); 00396 return res; 00397 }
static int stackpeek_read | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
struct ast_str ** | str, | |||
ssize_t | len | |||
) | [static] |
Definition at line 793 of file app_stack.c.
References args, AST_APP_ARG, ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, AST_DECLARE_APP_ARGS, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_skip_blanks(), AST_STANDARD_APP_ARGS, ast_str_set(), ast_strdupa, ast_strlen_zero(), ast_true(), ast_datastore::data, and LOG_ERROR.
00794 { 00795 struct ast_datastore *stack_store; 00796 struct gosub_stack_list *oldlist; 00797 struct gosub_stack_frame *frame; 00798 int n; 00799 AST_DECLARE_APP_ARGS(args, 00800 AST_APP_ARG(n); 00801 AST_APP_ARG(which); 00802 AST_APP_ARG(suppress); 00803 ); 00804 00805 if (!chan) { 00806 ast_log(LOG_ERROR, "STACK_PEEK must be called on an active channel\n"); 00807 return -1; 00808 } 00809 00810 data = ast_strdupa(data); 00811 AST_STANDARD_APP_ARGS(args, data); 00812 00813 if (ast_strlen_zero(args.n) || ast_strlen_zero(args.which)) { 00814 ast_log(LOG_ERROR, "STACK_PEEK requires parameters n and which\n"); 00815 return -1; 00816 } 00817 00818 n = atoi(args.n); 00819 if (n <= 0) { 00820 ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n"); 00821 return -1; 00822 } 00823 00824 ast_channel_lock(chan); 00825 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) { 00826 if (!ast_true(args.suppress)) { 00827 ast_log(LOG_ERROR, "STACK_PEEK called on a channel without a gosub stack\n"); 00828 } 00829 ast_channel_unlock(chan); 00830 return -1; 00831 } 00832 00833 oldlist = stack_store->data; 00834 00835 AST_LIST_LOCK(oldlist); 00836 AST_LIST_TRAVERSE(oldlist, frame, entries) { 00837 if (--n == 0) { 00838 break; 00839 } 00840 } 00841 00842 if (!frame) { 00843 /* Too deep */ 00844 if (!ast_true(args.suppress)) { 00845 ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n); 00846 } 00847 AST_LIST_UNLOCK(oldlist); 00848 ast_channel_unlock(chan); 00849 return -1; 00850 } 00851 00852 args.which = ast_skip_blanks(args.which); 00853 00854 switch (args.which[0]) { 00855 case 'l': /* label */ 00856 ast_str_set(str, len, "%s,%s,%d", frame->context, frame->extension, frame->priority - 1); 00857 break; 00858 case 'c': /* context */ 00859 ast_str_set(str, len, "%s", frame->context); 00860 break; 00861 case 'e': /* extension */ 00862 ast_str_set(str, len, "%s", frame->extension); 00863 break; 00864 case 'p': /* priority */ 00865 ast_str_set(str, len, "%d", frame->priority - 1); 00866 break; 00867 default: 00868 ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which); 00869 break; 00870 } 00871 00872 AST_LIST_UNLOCK(oldlist); 00873 ast_channel_unlock(chan); 00874 00875 return 0; 00876 }
static int unload_module | ( | void | ) | [static] |
Definition at line 1228 of file app_stack.c.
References ast_agi_unregister(), ast_custom_function_unregister(), ast_install_stack_functions(), and ast_unregister_application().
01229 { 01230 ast_install_stack_functions(NULL); 01231 01232 ast_agi_unregister(ast_module_info->self, &gosub_agi_command); 01233 01234 ast_unregister_application(app_return); 01235 ast_unregister_application(app_pop); 01236 ast_unregister_application(app_gosubif); 01237 ast_unregister_application(app_gosub); 01238 ast_custom_function_unregister(&local_function); 01239 ast_custom_function_unregister(&peek_function); 01240 ast_custom_function_unregister(&stackpeek_function); 01241 01242 return 0; 01243 }
const char app_gosub[] = "Gosub" [static] |
Definition at line 208 of file app_stack.c.
const char app_gosubif[] = "GosubIf" [static] |
Definition at line 209 of file app_stack.c.
const char app_pop[] = "StackPop" [static] |
Definition at line 211 of file app_stack.c.
const char app_return[] = "Return" [static] |
Definition at line 210 of file app_stack.c.
struct agi_command gosub_agi_command [static] |
{ { "gosub", NULL }, handle_gosub, NULL, NULL, 0 }
Definition at line 1225 of file app_stack.c.
struct ast_custom_function local_function [static] |
{ .name = "LOCAL", .write = local_write, .read = local_read, }
Definition at line 747 of file app_stack.c.
struct ast_custom_function peek_function [static] |
{ .name = "LOCAL_PEEK", .read = peek_read, }
Definition at line 788 of file app_stack.c.
struct ast_datastore_info stack_info [static] |
{ .type = "GOSUB", .destroy = gosub_free, }
Definition at line 215 of file app_stack.c.
struct ast_custom_function stackpeek_function [static] |
{ .name = "STACK_PEEK", .read2 = stackpeek_read, }
Definition at line 878 of file app_stack.c.