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 | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| 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 | 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 struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER , .description = "Dialplan subroutines (Gosub, Return, etc)" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "95089850e3c922fa176f9bd274fd8109" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .nonoptreq = "res_agi", } |
| static const char *const | app_gosub = "Gosub" |
| static const char *const | app_gosubif = "GosubIf" |
| static const char *const | app_pop = "StackPop" |
| static const char *const | app_return = "Return" |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| 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.
| static void __reg_module | ( | void | ) | [static] |
Definition at line 837 of file app_stack.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 837 of file app_stack.c.
| static int frame_set_var | ( | struct ast_channel * | chan, |
| struct gosub_stack_frame * | frame, | ||
| const char * | var, | ||
| const char * | value | ||
| ) | [static] |
Definition at line 226 of file app_stack.c.
References AST_LIST_TRAVERSE, ast_var_name(), ast_var_assign(), AST_LIST_INSERT_HEAD, pbx_builtin_pushvar_helper(), pbx_builtin_setvar_helper(), manager_event, and EVENT_FLAG_DIALPLAN.
Referenced by gosub_exec(), and local_write().
{
struct ast_var_t *variables;
int found = 0;
/* Does this variable already exist? */
AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
if (!strcmp(var, ast_var_name(variables))) {
found = 1;
break;
}
}
if (!found) {
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);
}
manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
"Channel: %s\r\n"
"Variable: LOCAL(%s)\r\n"
"Value: %s\r\n"
"Uniqueid: %s\r\n",
chan->name, var, value, chan->uniqueid);
return 0;
}
| static struct gosub_stack_frame* gosub_allocate_frame | ( | const char * | context, |
| const char * | extension, | ||
| int | priority, | ||
| unsigned char | arguments | ||
| ) | [static, read] |
Definition at line 275 of file app_stack.c.
References ast_calloc, AST_LIST_HEAD_INIT_NOLOCK, gosub_stack_frame::priority, and gosub_stack_frame::arguments.
Referenced by gosub_exec().
{
struct gosub_stack_frame *new = NULL;
int len_extension = strlen(extension), len_context = strlen(context);
if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
strcpy(new->extension, extension);
new->context = new->extension + len_extension + 1;
strcpy(new->context, context);
new->priority = priority;
new->arguments = arguments;
}
return new;
}
| static int gosub_exec | ( | struct ast_channel * | chan, |
| const char * | data | ||
| ) | [static] |
Definition at line 369 of file app_stack.c.
References AST_LIST_HEAD, ast_strdupa, AST_DECLARE_APP_ARGS, AST_APP_ARG, ast_strlen_zero(), ast_log(), LOG_ERROR, ast_channel_lock, ast_channel_datastore_find(), ast_debug, ast_channel::name, ast_datastore_alloc(), ast_channel_unlock, ast_calloc, ast_datastore_free(), ast_datastore::data, AST_LIST_HEAD_INIT, ast_channel_datastore_add(), AST_LIST_FIRST, gosub_stack_frame::arguments, strsep(), LOG_WARNING, AST_STANDARD_RAW_ARGS, gosub_allocate_frame(), ast_channel::context, ast_channel::exten, ast_channel::priority, ast_parseable_goto(), ast_free, ast_exists_extension(), ast_test_flag, AST_FLAG_IN_AUTOLOOP, S_COR, ast_channel::caller, ast_party_caller::id, ast_party_id::number, ast_party_number::valid, ast_party_number::str, ast_copy_string(), gosub_stack_frame::context, gosub_stack_frame::extension, gosub_stack_frame::priority, frame_set_var(), AST_LIST_LOCK, AST_LIST_INSERT_HEAD, gosub_stack_frame::entries, and AST_LIST_UNLOCK.
Referenced by gosubif_exec(), and load_module().
{
struct ast_datastore *stack_store;
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
struct gosub_stack_frame *newframe, *lastframe;
char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
int i, max_argc = 0;
AST_DECLARE_APP_ARGS(args2,
AST_APP_ARG(argval)[100];
);
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
return -1;
}
ast_channel_lock(chan);
if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", chan->name);
stack_store = ast_datastore_alloc(&stack_info, NULL);
if (!stack_store) {
ast_log(LOG_ERROR, "Unable to allocate new datastore. Gosub will fail.\n");
ast_channel_unlock(chan);
return -1;
}
oldlist = ast_calloc(1, sizeof(*oldlist));
if (!oldlist) {
ast_log(LOG_ERROR, "Unable to allocate datastore list head. Gosub will fail.\n");
ast_datastore_free(stack_store);
ast_channel_unlock(chan);
return -1;
}
stack_store->data = oldlist;
AST_LIST_HEAD_INIT(oldlist);
ast_channel_datastore_add(chan, stack_store);
} else {
oldlist = stack_store->data;
}
if ((lastframe = AST_LIST_FIRST(oldlist))) {
max_argc = lastframe->arguments;
}
/* Separate the arguments from the label */
/* NOTE: you cannot use ast_app_separate_args for this, because '(' cannot be used as a delimiter. */
label = strsep(&tmp, "(");
if (tmp) {
endparen = strrchr(tmp, ')');
if (endparen)
*endparen = '\0';
else
ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
AST_STANDARD_RAW_ARGS(args2, tmp);
} else
args2.argc = 0;
/* Mask out previous arguments in this invocation */
if (args2.argc > max_argc) {
max_argc = args2.argc;
}
/* Create the return address, but don't save it until we know that the Gosub destination exists */
newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, max_argc);
if (!newframe) {
ast_channel_unlock(chan);
return -1;
}
if (ast_parseable_goto(chan, label)) {
ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
ast_free(newframe);
ast_channel_unlock(chan);
return -1;
}
if (!ast_exists_extension(chan, chan->context, chan->exten,
ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP) ? chan->priority + 1 : chan->priority,
S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for gosub: (Context:%s, Extension:%s, Priority:%d)\n",
chan->context, chan->exten, ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP) ? chan->priority + 1 : chan->priority);
ast_copy_string(chan->context, newframe->context, sizeof(chan->context));
ast_copy_string(chan->exten, newframe->extension, sizeof(chan->exten));
chan->priority = newframe->priority - 1;
ast_free(newframe);
ast_channel_unlock(chan);
return -1;
}
/* Now that we know for certain that we're going to a new location, set our arguments */
for (i = 0; i < max_argc; i++) {
snprintf(argname, sizeof(argname), "ARG%d", i + 1);
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);
frame_set_var(chan, newframe, "ARGC", argname);
/* And finally, save our return address */
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
AST_LIST_UNLOCK(oldlist);
ast_channel_unlock(chan);
return 0;
}
| static void gosub_free | ( | void * | data | ) | [static] |
Definition at line 291 of file app_stack.c.
References AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, gosub_stack_frame::entries, gosub_release_frame(), AST_LIST_UNLOCK, AST_LIST_HEAD_DESTROY, and ast_free.
{
AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
struct gosub_stack_frame *oldframe;
AST_LIST_LOCK(oldlist);
while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
gosub_release_frame(NULL, oldframe);
}
AST_LIST_UNLOCK(oldlist);
AST_LIST_HEAD_DESTROY(oldlist);
ast_free(oldlist);
}
| static void gosub_release_frame | ( | struct ast_channel * | chan, |
| struct gosub_stack_frame * | frame | ||
| ) | [static] |
Definition at line 256 of file app_stack.c.
References AST_LIST_REMOVE_HEAD, gosub_stack_frame::varshead, gosub_stack_frame::entries, pbx_builtin_setvar_helper(), ast_var_name(), ast_var_delete(), and ast_free.
Referenced by gosub_free(), pop_exec(), and return_exec().
{
struct ast_var_t *vardata;
/* If chan is not defined, then we're calling it as part of gosub_free,
* and the channel variables will be deallocated anyway. Otherwise, we're
* just releasing a single frame, so we need to clean up the arguments for
* that frame, so that we re-expose the variables from the previous frame
* that were hidden by this one.
*/
while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
if (chan)
pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
ast_var_delete(vardata);
}
ast_free(frame);
}
| static int gosubif_exec | ( | struct ast_channel * | chan, |
| const char * | data | ||
| ) | [static] |
Definition at line 479 of file app_stack.c.
References args, AST_DECLARE_APP_ARGS, cond, AST_APP_ARG, ast_strlen_zero(), ast_log(), LOG_WARNING, ast_strdupa, AST_NONSTANDARD_RAW_ARGS, pbx_checkcondition(), and gosub_exec().
Referenced by load_module().
{
char *args;
int res=0;
AST_DECLARE_APP_ARGS(cond,
AST_APP_ARG(ition);
AST_APP_ARG(labels);
);
AST_DECLARE_APP_ARGS(label,
AST_APP_ARG(iftrue);
AST_APP_ARG(iffalse);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
return 0;
}
args = ast_strdupa(data);
AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
if (cond.argc != 2) {
ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
return 0;
}
AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
if (pbx_checkcondition(cond.ition)) {
if (!ast_strlen_zero(label.iftrue))
res = gosub_exec(chan, label.iftrue);
} else if (!ast_strlen_zero(label.iffalse)) {
res = gosub_exec(chan, label.iffalse);
}
return res;
}
| static int handle_gosub | ( | struct ast_channel * | chan, |
| AGI * | agi, | ||
| int | argc, | ||
| const char *const * | argv | ||
| ) | [static] |
Definition at line 701 of file app_stack.c.
References gosub_stack_frame::priority, AST_MAX_CONTEXT, AST_MAX_EXTENSION, RESULT_SHOWUSAGE, ast_debug, ast_findlabel_extension(), S_COR, ast_channel::caller, ast_party_caller::id, ast_party_id::number, ast_party_number::valid, ast_party_number::str, ast_log(), LOG_ERROR, ast_agi_send(), agi_state::fd, RESULT_FAILURE, ast_exists_extension(), ast_copy_string(), ast_channel::context, ast_channel::exten, ast_channel::priority, pbx_findapp(), asprintf(), ast_channel::pbx, LOG_WARNING, errno, pbx_exec(), ast_channel_datastore_find(), AST_LIST_HEAD, ast_datastore::data, AST_LIST_FIRST, gosub_stack_frame::is_agi, ast_pbx_args::no_hangup_chan, ast_pbx_run_args(), ast_free, and RESULT_SUCCESS.
{
int old_priority, priority;
char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
struct ast_app *theapp;
char *gosub_args;
if (argc < 4 || argc > 5) {
return RESULT_SHOWUSAGE;
}
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] : "");
if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
/* Lookup the priority label */
priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL));
if (priority < 0) {
ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
return RESULT_FAILURE;
}
} else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
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));
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));
gosub_args = NULL;
}
}
if (gosub_args) {
int res;
ast_debug(1, "Trying gosub with arguments '%s'\n", 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 = stack_store->data;
struct gosub_stack_frame *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;
} else {
ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
}
ast_free(gosub_args);
} else {
ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
return RESULT_FAILURE;
}
/* 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;
return RESULT_SUCCESS;
}
| static int load_module | ( | void | ) | [static] |
Definition at line 817 of file app_stack.c.
References ast_agi_register(), ast_module_info::self, gosub_agi_command, ast_register_application_xml, pop_exec(), return_exec(), gosubif_exec(), gosub_exec(), ast_custom_function_register, local_function, peek_function, and stackpeek_function.
{
ast_agi_register(ast_module_info->self, &gosub_agi_command);
ast_register_application_xml(app_pop, pop_exec);
ast_register_application_xml(app_return, return_exec);
ast_register_application_xml(app_gosubif, gosubif_exec);
ast_register_application_xml(app_gosub, gosub_exec);
ast_custom_function_register(&local_function);
ast_custom_function_register(&peek_function);
ast_custom_function_register(&stackpeek_function);
return 0;
}
| static int local_read | ( | struct ast_channel * | chan, |
| const char * | cmd, | ||
| char * | data, | ||
| char * | buf, | ||
| size_t | len | ||
| ) | [static] |
Definition at line 516 of file app_stack.c.
References AST_LIST_HEAD, ast_channel_lock, ast_channel_datastore_find(), ast_channel_unlock, ast_datastore::data, AST_LIST_LOCK, AST_LIST_FIRST, AST_LIST_UNLOCK, AST_LIST_TRAVERSE, gosub_stack_frame::varshead, gosub_stack_frame::entries, ast_var_name(), pbx_builtin_getvar_helper(), ast_copy_string(), and S_OR.
{
struct ast_datastore *stack_store;
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
struct gosub_stack_frame *frame;
struct ast_var_t *variables;
ast_channel_lock(chan);
if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
ast_channel_unlock(chan);
return -1;
}
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
if (!(frame = AST_LIST_FIRST(oldlist))) {
/* Not within a Gosub routine */
AST_LIST_UNLOCK(oldlist);
ast_channel_unlock(chan);
return -1;
}
AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
if (!strcmp(data, ast_var_name(variables))) {
const char *tmp;
tmp = pbx_builtin_getvar_helper(chan, data);
ast_copy_string(buf, S_OR(tmp, ""), len);
break;
}
}
AST_LIST_UNLOCK(oldlist);
ast_channel_unlock(chan);
return 0;
}
| static int local_write | ( | struct ast_channel * | chan, |
| const char * | cmd, | ||
| char * | var, | ||
| const char * | value | ||
| ) | [static] |
Definition at line 551 of file app_stack.c.
References AST_LIST_HEAD, ast_channel_lock, ast_channel_datastore_find(), ast_log(), LOG_ERROR, ast_channel_unlock, ast_datastore::data, AST_LIST_LOCK, AST_LIST_FIRST, frame_set_var(), and AST_LIST_UNLOCK.
{
struct ast_datastore *stack_store;
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
struct gosub_stack_frame *frame;
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);
ast_channel_unlock(chan);
return -1;
}
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
frame = AST_LIST_FIRST(oldlist);
if (frame) {
frame_set_var(chan, frame, var, value);
}
AST_LIST_UNLOCK(oldlist);
ast_channel_unlock(chan);
return 0;
}
| static int peek_read | ( | struct ast_channel * | chan, |
| const char * | cmd, | ||
| char * | data, | ||
| char * | buf, | ||
| size_t | len | ||
| ) | [static] |
Definition at line 584 of file app_stack.c.
References AST_DECLARE_APP_ARGS, args, AST_APP_ARG, name, ast_log(), LOG_ERROR, AST_STANDARD_RAW_ARGS, ast_channel_lock, AST_LIST_TRAVERSE, ast_channel::varshead, gosub_stack_frame::entries, ast_var_name(), ast_copy_string(), ast_var_value(), and ast_channel_unlock.
{
int found = 0, n;
struct ast_var_t *variables;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(n);
AST_APP_ARG(name);
);
if (!chan) {
ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
return -1;
}
AST_STANDARD_RAW_ARGS(args, data);
n = atoi(args.n);
*buf = '\0';
ast_channel_lock(chan);
AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
ast_copy_string(buf, ast_var_value(variables), len);
break;
}
}
ast_channel_unlock(chan);
return 0;
}
| static int pop_exec | ( | struct ast_channel * | chan, |
| const char * | data | ||
| ) | [static] |
Definition at line 304 of file app_stack.c.
References AST_LIST_HEAD, ast_channel_lock, ast_channel_datastore_find(), ast_log(), LOG_WARNING, ast_channel_unlock, ast_datastore::data, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, gosub_stack_frame::entries, AST_LIST_UNLOCK, gosub_release_frame(), and ast_debug.
Referenced by load_module().
{
struct ast_datastore *stack_store;
struct gosub_stack_frame *oldframe;
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
ast_channel_lock(chan);
if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
ast_channel_unlock(chan);
return 0;
}
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
AST_LIST_UNLOCK(oldlist);
if (oldframe) {
gosub_release_frame(chan, oldframe);
} else {
ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
}
ast_channel_unlock(chan);
return 0;
}
| static int return_exec | ( | struct ast_channel * | chan, |
| const char * | data | ||
| ) | [static] |
Definition at line 331 of file app_stack.c.
References AST_LIST_HEAD, ast_channel_lock, ast_channel_datastore_find(), ast_log(), LOG_ERROR, ast_channel_unlock, ast_datastore::data, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, gosub_stack_frame::entries, AST_LIST_UNLOCK, gosub_stack_frame::is_agi, ast_explicit_goto(), gosub_stack_frame::context, gosub_stack_frame::extension, gosub_stack_frame::priority, gosub_release_frame(), pbx_builtin_setvar_helper(), and S_OR.
Referenced by load_module().
{
struct ast_datastore *stack_store;
struct gosub_stack_frame *oldframe;
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
const char *retval = data;
int res = 0;
ast_channel_lock(chan);
if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
ast_channel_unlock(chan);
return -1;
}
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
AST_LIST_UNLOCK(oldlist);
if (!oldframe) {
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 */
res = -1;
}
ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
gosub_release_frame(chan, oldframe);
/* Set a return value, if any */
pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
ast_channel_unlock(chan);
return res;
}
| static int stackpeek_read | ( | struct ast_channel * | chan, |
| const char * | cmd, | ||
| char * | data, | ||
| struct ast_str ** | str, | ||
| ssize_t | len | ||
| ) | [static] |
Definition at line 618 of file app_stack.c.
References AST_LIST_HEAD, AST_DECLARE_APP_ARGS, args, AST_APP_ARG, ast_log(), LOG_ERROR, ast_strdupa, AST_STANDARD_APP_ARGS, ast_channel_lock, ast_channel_datastore_find(), ast_true(), ast_channel_unlock, ast_datastore::data, AST_LIST_LOCK, AST_LIST_TRAVERSE, gosub_stack_frame::entries, ast_skip_blanks(), ast_str_set(), gosub_stack_frame::context, gosub_stack_frame::extension, gosub_stack_frame::priority, and AST_LIST_UNLOCK.
{
struct ast_datastore *stack_store;
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
struct gosub_stack_frame *frame;
int n;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(n);
AST_APP_ARG(which);
AST_APP_ARG(suppress);
);
if (!chan) {
ast_log(LOG_ERROR, "STACK_PEEK must be called on an active channel\n");
return -1;
}
data = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, data);
n = atoi(args.n);
if (n <= 0) {
ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
return -1;
}
ast_channel_lock(chan);
if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
if (!ast_true(args.suppress)) {
ast_log(LOG_ERROR, "STACK_PEEK called on a channel without a gosub stack\n");
}
ast_channel_unlock(chan);
return -1;
}
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
AST_LIST_TRAVERSE(oldlist, frame, entries) {
if (--n == 0) {
break;
}
}
if (!frame) {
/* Too deep */
if (!ast_true(args.suppress)) {
ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n);
}
ast_channel_unlock(chan);
return -1;
}
args.which = ast_skip_blanks(args.which);
switch (args.which[0]) {
case 'l': /* label */
ast_str_set(str, len, "%s,%s,%d", frame->context, frame->extension, frame->priority - 1);
break;
case 'c': /* context */
ast_str_set(str, len, "%s", frame->context);
break;
case 'e': /* extension */
ast_str_set(str, len, "%s", frame->extension);
break;
case 'p': /* priority */
ast_str_set(str, len, "%d", frame->priority - 1);
break;
default:
ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
}
AST_LIST_UNLOCK(oldlist);
ast_channel_unlock(chan);
return 0;
}
| static int unload_module | ( | void | ) | [static] |
Definition at line 802 of file app_stack.c.
References ast_agi_unregister(), ast_module_info::self, gosub_agi_command, ast_unregister_application(), ast_custom_function_unregister(), local_function, peek_function, and stackpeek_function.
{
ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
ast_unregister_application(app_return);
ast_unregister_application(app_pop);
ast_unregister_application(app_gosubif);
ast_unregister_application(app_gosub);
ast_custom_function_unregister(&local_function);
ast_custom_function_unregister(&peek_function);
ast_custom_function_unregister(&stackpeek_function);
return 0;
}
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER , .description = "Dialplan subroutines (Gosub, Return, etc)" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "95089850e3c922fa176f9bd274fd8109" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .nonoptreq = "res_agi", } [static] |
Definition at line 837 of file app_stack.c.
const char* const app_gosub = "Gosub" [static] |
Definition at line 203 of file app_stack.c.
const char* const app_gosubif = "GosubIf" [static] |
Definition at line 204 of file app_stack.c.
const char* const app_pop = "StackPop" [static] |
Definition at line 206 of file app_stack.c.
const char* const app_return = "Return" [static] |
Definition at line 205 of file app_stack.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 837 of file app_stack.c.
struct agi_command gosub_agi_command [static] |
{ { "gosub", NULL }, handle_gosub, NULL, NULL, 0 }
Definition at line 799 of file app_stack.c.
Referenced by unload_module(), and load_module().
struct ast_custom_function local_function [static] |
{
.name = "LOCAL",
.write = local_write,
.read = local_read,
}
Definition at line 578 of file app_stack.c.
Referenced by unload_module(), and load_module().
struct ast_custom_function peek_function [static] |
{
.name = "LOCAL_PEEK",
.read = peek_read,
}
Definition at line 613 of file app_stack.c.
Referenced by unload_module(), and load_module().
struct ast_datastore_info stack_info [static] |
{
.type = "GOSUB",
.destroy = gosub_free,
}
Definition at line 210 of file app_stack.c.
struct ast_custom_function stackpeek_function [static] |
{
.name = "STACK_PEEK",
.read2 = stackpeek_read,
}
Definition at line 696 of file app_stack.c.
Referenced by unload_module(), and load_module().