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 |
| struct | gosub_stack_list |
Functions | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| 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 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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .nonoptreq = "res_agi", } |
| 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 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 1287 of file app_stack.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 1287 of file app_stack.c.
| static void balance_stack | ( | struct ast_channel * | chan | ) | [static] |
Definition at line 901 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_stack_frame::entries, gosub_release_frame(), gosub_stack_frame::is_special, and LOG_WARNING.
Referenced by gosub_run(), and handle_gosub().
{
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);
}
| static const char* expand_gosub_args | ( | struct ast_channel * | chan, |
| const char * | args | ||
| ) | [static] |
Definition at line 426 of file app_stack.c.
References ast_channel_context(), ast_channel_exten(), ast_channel_lock, ast_channel_unlock, ast_debug, ast_log(), ast_malloc, ast_strlen_zero(), gosub_stack_frame::context, exten, len(), LOG_WARNING, and parse().
Referenced by load_module().
{
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 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_channel_name(), ast_channel_uniqueid(), 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().
{
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) {
if ((variables = ast_var_assign(var, ""))) {
AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
}
pbx_builtin_pushvar_helper(chan, var, value);
} else {
pbx_builtin_setvar_helper(chan, var, value);
}
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a LOCAL channel variable is set due to a subroutine call.</synopsis>
<see-also>
<ref type="application">GoSub</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
"Channel: %s\r\n"
"Variable: LOCAL(%s)\r\n"
"Value: %s\r\n"
"Uniqueid: %s\r\n",
ast_channel_name(chan), var, value, ast_channel_uniqueid(chan));
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 292 of file app_stack.c.
References gosub_stack_frame::arguments, ast_calloc, AST_LIST_HEAD_INIT_NOLOCK, and gosub_stack_frame::priority.
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 492 of file app_stack.c.
References gosub_stack_frame::arguments, AST_APP_ARG, ast_calloc, ast_channel_caller(), ast_channel_context(), ast_channel_context_set(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_exten(), ast_channel_exten_set(), ast_channel_flags(), ast_channel_lock, ast_channel_name(), ast_channel_priority(), ast_channel_priority_set(), ast_channel_unlock, 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_strlen_zero(), ast_test_flag, ast_datastore::data, gosub_stack_frame::entries, frame_set_var(), gosub_allocate_frame(), LOG_ERROR, LOG_WARNING, orig_exten(), parse(), and S_COR.
Referenced by gosub_run(), gosubif_exec(), handle_gosub(), and load_module().
{
struct ast_datastore *stack_store;
struct gosub_stack_list *oldlist;
struct gosub_stack_frame *newframe;
struct gosub_stack_frame *lastframe;
char argname[15];
char *parse;
char *label;
char *caller_id;
char *orig_context;
char *orig_exten;
char *dest_context;
char *dest_exten;
int orig_priority;
int dest_priority;
int i;
int 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;
}
/*
* Separate the arguments from the label
*
* NOTE: You cannot use ast_app_separate_args for this, because
* '(' cannot be used as a delimiter.
*/
parse = ast_strdupa(data);
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", data);
}
AST_STANDARD_RAW_ARGS(args2, parse);
} else {
args2.argc = 0;
}
ast_channel_lock(chan);
orig_context = ast_strdupa(ast_channel_context(chan));
orig_exten = ast_strdupa(ast_channel_exten(chan));
orig_priority = ast_channel_priority(chan);
ast_channel_unlock(chan);
if (ast_parseable_goto(chan, label)) {
ast_log(LOG_ERROR, "%s address is invalid: '%s'\n", app_gosub, data);
goto error_exit;
}
ast_channel_lock(chan);
dest_context = ast_strdupa(ast_channel_context(chan));
dest_exten = ast_strdupa(ast_channel_exten(chan));
dest_priority = ast_channel_priority(chan);
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
++dest_priority;
}
caller_id = S_COR(ast_channel_caller(chan)->id.number.valid,
ast_channel_caller(chan)->id.number.str, NULL);
if (caller_id) {
caller_id = ast_strdupa(caller_id);
}
ast_channel_unlock(chan);
if (!ast_exists_extension(chan, dest_context, dest_exten, dest_priority, caller_id)) {
ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for %s: (Context:%s, Extension:%s, Priority:%d)\n",
app_gosub, dest_context, dest_exten, dest_priority);
goto error_exit;
}
/* Now we know that we're going to a new location */
ast_channel_lock(chan);
/* Find stack datastore return list. */
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",
ast_channel_name(chan));
stack_store = ast_datastore_alloc(&stack_info, NULL);
if (!stack_store) {
ast_log(LOG_ERROR, "Unable to allocate new datastore. %s failed.\n",
app_gosub);
goto error_exit_locked;
}
oldlist = ast_calloc(1, sizeof(*oldlist));
if (!oldlist) {
ast_log(LOG_ERROR, "Unable to allocate datastore list head. %s failed.\n",
app_gosub);
ast_datastore_free(stack_store);
goto error_exit_locked;
}
AST_LIST_HEAD_INIT(oldlist);
stack_store->data = oldlist;
ast_channel_datastore_add(chan, stack_store);
} else {
oldlist = stack_store->data;
}
if ((lastframe = AST_LIST_FIRST(oldlist))) {
max_argc = lastframe->arguments;
}
/* Mask out previous Gosub arguments in this invocation */
if (args2.argc > max_argc) {
max_argc = args2.argc;
}
/* Create the return address */
newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, max_argc);
if (!newframe) {
goto error_exit_locked;
}
/* 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 */
AST_LIST_LOCK(oldlist);
AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
AST_LIST_UNLOCK(oldlist);
ast_channel_unlock(chan);
return 0;
error_exit:
ast_channel_lock(chan);
error_exit_locked:
/* Restore the original dialplan location. */
ast_channel_context_set(chan, orig_context);
ast_channel_exten_set(chan, orig_exten);
ast_channel_priority_set(chan, orig_priority);
ast_channel_unlock(chan);
return -1;
}
| static void gosub_free | ( | void * | data | ) | [static] |
Definition at line 308 of file app_stack.c.
References ast_free, AST_LIST_HEAD_DESTROY, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, gosub_stack_frame::entries, and gosub_release_frame().
{
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);
}
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 273 of file app_stack.c.
References ast_free, AST_LIST_REMOVE_HEAD, ast_var_delete(), ast_var_name(), gosub_stack_frame::entries, pbx_builtin_setvar_helper(), and gosub_stack_frame::varshead.
Referenced by balance_stack(), 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 gosub_run | ( | struct ast_channel * | chan, |
| const char * | sub_args, | ||
| int | ignore_hangup | ||
| ) | [static] |
Definition at line 941 of file app_stack.c.
References ast_channel_caller(), ast_channel_clear_softhangup(), ast_channel_context(), ast_channel_context_set(), ast_channel_datastore_find(), ast_channel_exten(), ast_channel_exten_set(), ast_channel_flags(), ast_channel_lock, ast_channel_name(), ast_channel_priority(), ast_channel_priority_set(), ast_channel_softhangup_internal_flag(), ast_channel_unlock, ast_check_hangup(), 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_test_flag, ast_verb, balance_stack(), ast_datastore::data, gosub_exec(), gosub_stack_frame::is_special, LOG_ERROR, LOG_NOTICE, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), S_COR, and S_OR.
Referenced by load_module().
{
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 gosubif_exec | ( | struct ast_channel * | chan, |
| const char * | data | ||
| ) | [static] |
Definition at line 646 of file app_stack.c.
References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_NONSTANDARD_RAW_ARGS, ast_strlen_zero(), cond, gosub_exec(), LOG_WARNING, and pbx_checkcondition().
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 1087 of file app_stack.c.
References ast_agi_send(), ast_asprintf, ast_channel_caller(), ast_channel_context(), ast_channel_context_set(), ast_channel_datastore_find(), ast_channel_exten(), ast_channel_exten_set(), ast_channel_flags(), ast_channel_lock, ast_channel_name(), ast_channel_pbx(), ast_channel_pbx_set(), ast_channel_priority(), ast_channel_priority_set(), ast_channel_unlock, 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_test_flag, ast_verb, balance_stack(), ast_datastore::data, agi_state::fd, free, gosub_exec(), gosub_stack_frame::is_special, LOG_ERROR, LOG_NOTICE, ast_pbx_args::no_hangup_chan, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), gosub_stack_frame::priority, RESULT_FAILURE, RESULT_SHOWUSAGE, RESULT_SUCCESS, S_COR, and S_OR.
{
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) {
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(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->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(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
return RESULT_FAILURE;
}
if (argc == 5) {
if (ast_asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) {
gosub_args = NULL;
}
} else {
if (ast_asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) {
gosub_args = NULL;
}
}
if (!gosub_args) {
ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
return RESULT_FAILURE;
}
ast_channel_lock(chan);
ast_verb(3, "%s AGI %s(%s) start\n", ast_channel_name(chan), app_gosub, 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);
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;
oldlist = stack_store->data;
cur = AST_LIST_FIRST(oldlist);
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_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_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, "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;
}
| static int load_module | ( | void | ) | [static] |
Definition at line 1258 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_agi_command, gosub_exec(), gosub_run(), gosubif_exec(), local_function, ast_app_stack_funcs::module, peek_function, pop_exec(), return_exec(), ast_app_stack_funcs::run_sub, ast_module_info::self, and stackpeek_function.
{
/* Setup the stack application callback functions. */
static struct ast_app_stack_funcs funcs = {
.run_sub = gosub_run,
.expand_sub_args = expand_gosub_args,
};
ast_agi_register(ast_module_info->self, &gosub_agi_command);
ast_register_application_xml(app_pop, pop_exec);
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);
funcs.module = ast_module_info->self,
ast_install_stack_functions(&funcs);
return 0;
}
| static int local_read | ( | struct ast_channel * | chan, |
| const char * | cmd, | ||
| char * | data, | ||
| char * | buf, | ||
| size_t | len | ||
| ) | [static] |
Definition at line 683 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, gosub_stack_frame::entries, LOG_WARNING, pbx_builtin_getvar_helper(), S_OR, and gosub_stack_frame::varshead.
{
struct ast_datastore *stack_store;
struct gosub_stack_list *oldlist;
struct gosub_stack_frame *frame;
struct ast_var_t *variables;
if (!chan) {
ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
return -1;
}
ast_channel_lock(chan);
if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
ast_channel_unlock(chan);
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 723 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.
{
struct ast_datastore *stack_store;
struct gosub_stack_list *oldlist;
struct gosub_stack_frame *frame;
if (!chan) {
ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
return -1;
}
ast_channel_lock(chan);
if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
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 761 of file app_stack.c.
References args, AST_APP_ARG, ast_channel_lock, ast_channel_unlock, ast_channel_varshead(), 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(), gosub_stack_frame::entries, LOG_ERROR, and name.
{
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);
if (ast_strlen_zero(args.n) || ast_strlen_zero(args.name)) {
ast_log(LOG_ERROR, "LOCAL_PEEK requires parameters n and varname\n");
return -1;
}
n = atoi(args.n);
*buf = '\0';
ast_channel_lock(chan);
AST_LIST_TRAVERSE(ast_channel_varshead(chan), 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 322 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_stack_frame::entries, gosub_release_frame(), gosub_stack_frame::is_special, and LOG_WARNING.
Referenced by load_module().
{
struct ast_datastore *stack_store;
struct gosub_stack_frame *oldframe;
struct gosub_stack_list *oldlist;
int res = 0;
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_FIRST(oldlist);
if (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 res;
}
| static int return_exec | ( | struct ast_channel * | chan, |
| const char * | data | ||
| ) | [static] |
Definition at line 357 of file app_stack.c.
References ast_channel_context_set(), ast_channel_datastore_find(), ast_channel_exten_set(), ast_channel_flags(), ast_channel_lock, ast_channel_priority_set(), ast_channel_unlock, AST_FLAG_IN_AUTOLOOP, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), ast_test_flag, gosub_stack_frame::context, ast_datastore::data, gosub_stack_frame::entries, gosub_stack_frame::extension, gosub_release_frame(), gosub_stack_frame::is_special, LOG_ERROR, pbx_builtin_setvar_helper(), gosub_stack_frame::priority, and S_OR.
Referenced by load_module().
{
struct ast_datastore *stack_store;
struct gosub_stack_frame *oldframe;
struct gosub_stack_list *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;
}
if (oldframe->is_special) {
/* Exit from special routine. */
res = -1;
}
/*
* 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 */
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 801 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_strlen_zero(), ast_true(), gosub_stack_frame::context, ast_datastore::data, gosub_stack_frame::entries, gosub_stack_frame::extension, LOG_ERROR, and gosub_stack_frame::priority.
{
struct ast_datastore *stack_store;
struct gosub_stack_list *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);
if (ast_strlen_zero(args.n) || ast_strlen_zero(args.which)) {
ast_log(LOG_ERROR, "STACK_PEEK requires parameters n and which\n");
return -1;
}
n = atoi(args.n);
if (n <= 0) {
ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
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);
break;
}
AST_LIST_UNLOCK(oldlist);
ast_channel_unlock(chan);
return 0;
}
| static int unload_module | ( | void | ) | [static] |
Definition at line 1241 of file app_stack.c.
References ast_agi_unregister(), ast_custom_function_unregister(), ast_install_stack_functions(), ast_unregister_application(), gosub_agi_command, local_function, peek_function, ast_module_info::self, and stackpeek_function.
{
ast_install_stack_functions(NULL);
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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND, .nonoptreq = "res_agi", } [static] |
Definition at line 1287 of file app_stack.c.
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 ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 1287 of file app_stack.c.
struct agi_command gosub_agi_command [static] |
{ { "gosub", NULL }, handle_gosub, NULL, NULL, 0 }
Definition at line 1238 of file app_stack.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function local_function [static] |
{
.name = "LOCAL",
.write = local_write,
.read = local_read,
}
Definition at line 755 of file app_stack.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function peek_function [static] |
{
.name = "LOCAL_PEEK",
.read = peek_read,
}
Definition at line 796 of file app_stack.c.
Referenced by load_module(), and unload_module().
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 885 of file app_stack.c.
Referenced by load_module(), and unload_module().