41 #include "asterisk/stasis_channels.h"
230 static const char app_gosub[] =
"Gosub";
231 static const char app_gosubif[] =
"GosubIf";
232 static const char app_return[] =
"Return";
233 static const char app_pop[] =
"StackPop";
235 static void gosub_free(
void *data);
239 .destroy = gosub_free,
245 unsigned char arguments;
263 RAII_VAR(
char *, local_buffer, NULL, ast_free);
267 if (!strcmp(var, ast_var_name(variables))) {
274 if ((variables = ast_var_assign(var,
""))) {
282 len = 8 + strlen(var);
287 sprintf(local_buffer,
"LOCAL(%s)", var);
305 ast_var_delete(vardata);
314 int len_extension = strlen(extension) + 1;
315 int len_context = strlen(context) + 1;
317 if ((
new =
ast_calloc(1,
sizeof(*
new) + len_extension + len_context))) {
320 new->context =
new->extension + len_extension;
322 new->priority = priority;
323 new->in_subroutine = in_subroutine ? 1 : 0;
324 new->arguments = arguments;
329 static void gosub_free(
void *data)
336 gosub_release_frame(NULL, oldframe);
343 static int pop_exec(
struct ast_channel *chan,
const char *data)
350 ast_channel_lock(chan);
352 ast_log(LOG_WARNING,
"%s called with no gosub stack allocated.\n", app_pop);
353 ast_channel_unlock(chan);
357 oldlist = stack_store->
data;
362 ast_debug(1,
"%s attempted to pop special return location.\n", app_pop);
368 gosub_release_frame(chan, oldframe);
371 ast_debug(1,
"%s called with an empty gosub stack\n", app_pop);
374 ast_channel_unlock(chan);
378 static int return_exec(
struct ast_channel *chan,
const char *data)
383 const char *retval = data;
386 ast_channel_lock(chan);
388 ast_log(LOG_ERROR,
"Return without Gosub: stack is unallocated\n");
389 ast_channel_unlock(chan);
393 oldlist = stack_store->
data;
399 ast_log(LOG_ERROR,
"Return without Gosub: stack is empty\n");
400 ast_channel_unlock(chan);
413 ast_channel_context_set(chan, oldframe->context);
414 ast_channel_exten_set(chan, oldframe->extension);
416 --oldframe->priority;
418 ast_channel_priority_set(chan, oldframe->priority);
421 gosub_release_frame(chan, oldframe);
425 ast_channel_unlock(chan);
448 static const char *expand_gosub_args(
struct ast_channel *chan,
const char *args)
460 label = strsep(&parse,
"(");
464 endparen = strrchr(parse,
')');
468 ast_log(LOG_WARNING,
"Ouch. No closing paren: '%s'?\n", args);
473 context = strsep(&label,
",");
474 exten = strsep(&label,
",");
475 pri = strsep(&label,
",");
488 ast_channel_lock(chan);
489 if (ast_strlen_zero(exten)) {
490 exten = ast_channel_exten(chan);
492 if (ast_strlen_zero(context)) {
493 context = ast_channel_context(chan);
495 len = strlen(context) + strlen(exten) + strlen(pri) + 3;
496 if (!ast_strlen_zero(parse)) {
497 len += 2 + strlen(parse);
501 if (ast_strlen_zero(parse)) {
502 snprintf(new_args, len,
"%s,%s,%s", context, exten, pri);
504 snprintf(new_args, len,
"%s,%s,%s(%s)", context, exten, pri, parse);
507 ast_channel_unlock(chan);
509 ast_debug(4,
"Gosub args:%s new_args:%s\n", args, new_args ? new_args :
"");
514 static int gosub_exec(
struct ast_channel *chan,
const char *data)
528 int orig_in_subroutine;
537 if (ast_strlen_zero(data)) {
538 ast_log(LOG_ERROR,
"%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
549 label = strsep(&parse,
"(");
553 endparen = strrchr(parse,
')');
557 ast_log(LOG_WARNING,
"Ouch. No closing paren: '%s'?\n", data);
559 AST_STANDARD_RAW_ARGS(args2, parse);
564 ast_channel_lock(chan);
565 orig_context =
ast_strdupa(ast_channel_context(chan));
567 orig_priority = ast_channel_priority(chan);
569 ast_channel_unlock(chan);
572 ast_log(LOG_ERROR,
"%s address is invalid: '%s'\n", app_gosub, data);
576 ast_channel_lock(chan);
577 dest_context =
ast_strdupa(ast_channel_context(chan));
579 dest_priority = ast_channel_priority(chan);
583 caller_id =
S_COR(ast_channel_caller(chan)->
id.
number.valid,
584 ast_channel_caller(chan)->
id.
number.str, NULL);
588 ast_channel_unlock(chan);
591 ast_log(LOG_ERROR,
"%s attempted to reach non-existent destination '%s,%s,%d' from '%s,%s,%d'",
592 app_gosub, dest_context, dest_exten, dest_priority, orig_context, orig_exten, orig_priority);
598 ast_channel_lock(chan);
602 ast_debug(1,
"Channel %s has no datastore, so we're allocating one.\n",
603 ast_channel_name(chan));
604 stack_store = ast_datastore_alloc(&stack_info, NULL);
606 ast_log(LOG_ERROR,
"Unable to allocate new datastore. %s failed.\n",
608 goto error_exit_locked;
613 ast_log(LOG_ERROR,
"Unable to allocate datastore list head. %s failed.\n",
616 goto error_exit_locked;
620 stack_store->
data = oldlist;
623 oldlist = stack_store->
data;
627 max_argc = lastframe->arguments;
631 if (args2.argc > max_argc) {
632 max_argc = args2.argc;
636 newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, orig_in_subroutine, max_argc);
638 goto error_exit_locked;
642 for (i = 0; i < max_argc; i++) {
643 snprintf(argname,
sizeof(argname),
"ARG%d", i + 1);
644 frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] :
"");
645 ast_debug(1,
"Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] :
"");
647 snprintf(argname,
sizeof(argname),
"%u", args2.argc);
648 frame_set_var(chan, newframe,
"ARGC", argname);
656 ast_channel_unlock(chan);
661 ast_channel_lock(chan);
665 ast_channel_context_set(chan, orig_context);
666 ast_channel_exten_set(chan, orig_exten);
667 ast_channel_priority_set(chan, orig_priority);
668 ast_channel_unlock(chan);
672 static int gosubif_exec(
struct ast_channel *chan,
const char *data)
685 if (ast_strlen_zero(data)) {
686 ast_log(LOG_WARNING,
"GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
691 AST_NONSTANDARD_RAW_ARGS(cond, args,
'?');
692 if (cond.argc != 2) {
693 ast_log(LOG_WARNING,
"GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
697 AST_NONSTANDARD_RAW_ARGS(label, cond.labels,
':');
700 if (!ast_strlen_zero(label.iftrue))
701 res = gosub_exec(chan, label.iftrue);
702 }
else if (!ast_strlen_zero(label.iffalse)) {
703 res = gosub_exec(chan, label.iffalse);
709 static int local_read(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len)
717 ast_log(LOG_WARNING,
"No channel was provided to %s function.\n", cmd);
721 ast_channel_lock(chan);
723 ast_channel_unlock(chan);
727 oldlist = stack_store->
data;
732 ast_channel_unlock(chan);
737 if (!strcmp(data, ast_var_name(variables))) {
745 ast_channel_unlock(chan);
749 static int local_write(
struct ast_channel *chan,
const char *cmd,
char *var,
const char *value)
756 ast_log(LOG_WARNING,
"No channel was provided to %s function.\n", cmd);
760 ast_channel_lock(chan);
762 ast_log(LOG_ERROR,
"Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
763 ast_channel_unlock(chan);
767 oldlist = stack_store->
data;
772 frame_set_var(chan, frame, var, value);
776 ast_channel_unlock(chan);
783 .write = local_write,
787 static int peek_read(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len)
797 ast_log(LOG_ERROR,
"LOCAL_PEEK must be called on an active channel\n");
801 AST_STANDARD_RAW_ARGS(args, data);
803 if (ast_strlen_zero(args.n) || ast_strlen_zero(args.name)) {
804 ast_log(LOG_ERROR,
"LOCAL_PEEK requires parameters n and varname\n");
811 ast_channel_lock(chan);
813 if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
818 ast_channel_unlock(chan);
823 .
name =
"LOCAL_PEEK",
827 static int stackpeek_read(
struct ast_channel *chan,
const char *cmd,
char *data,
struct ast_str **str, ssize_t len)
840 ast_log(LOG_ERROR,
"STACK_PEEK must be called on an active channel\n");
847 if (ast_strlen_zero(args.n) || ast_strlen_zero(args.which)) {
848 ast_log(LOG_ERROR,
"STACK_PEEK requires parameters n and which\n");
854 ast_log(LOG_ERROR,
"STACK_PEEK must be called with a positive peek value\n");
858 ast_channel_lock(chan);
861 ast_log(LOG_ERROR,
"STACK_PEEK called on a channel without a gosub stack\n");
863 ast_channel_unlock(chan);
867 oldlist = stack_store->
data;
879 ast_log(LOG_ERROR,
"Stack peek of '%s' is more stack frames than I have\n", args.n);
882 ast_channel_unlock(chan);
888 switch (args.which[0]) {
890 ast_str_set(str, len,
"%s,%s,%d", frame->context, frame->extension, frame->priority - 1);
902 ast_log(LOG_ERROR,
"Unknown argument '%s' to STACK_PEEK\n", args.which);
907 ast_channel_unlock(chan);
913 .
name =
"STACK_PEEK",
914 .read2 = stackpeek_read,
926 static void balance_stack(
struct ast_channel *chan)
935 ast_log(LOG_WARNING,
"No %s stack allocated.\n", app_gosub);
939 oldlist = stack_store->
data;
947 gosub_release_frame(chan, oldframe);
966 static int gosub_run(
struct ast_channel *chan,
const char *sub_args,
int ignore_hangup)
968 const char *saved_context;
969 const char *saved_exten;
971 int saved_hangup_flags;
972 int saved_autoloopflag;
973 int saved_in_subroutine;
976 ast_channel_lock(chan);
978 ast_verb(3,
"%s Internal %s(%s) start\n",
979 ast_channel_name(chan), app_gosub, sub_args);
982 saved_hangup_flags = ast_channel_softhangup_internal_flag(chan)
984 if (saved_hangup_flags) {
993 saved_context =
ast_strdupa(ast_channel_context(chan));
994 saved_exten =
ast_strdupa(ast_channel_exten(chan));
995 saved_priority = ast_channel_priority(chan);
1000 ast_debug(4,
"%s Original location: %s,%s,%d\n", ast_channel_name(chan),
1001 saved_context, saved_exten, saved_priority);
1003 ast_channel_unlock(chan);
1004 res = gosub_exec(chan, sub_args);
1005 ast_debug(4,
"%s exited with status %d\n", app_gosub, res);
1006 ast_channel_lock(chan);
1014 ast_log(LOG_ERROR,
"No %s stack!\n", app_gosub);
1020 oldlist = stack_store->
data;
1039 ast_log(LOG_ERROR,
"%s An async goto just messed up our execution location.\n",
1040 ast_channel_name(chan));
1043 if (!ignore_hangup) {
1049 ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
1051 ast_channel_unlock(chan);
1053 ast_channel_exten(chan), ast_channel_priority(chan),
1055 ast_channel_caller(chan)->
id.
number.str, NULL),
1057 ast_channel_lock(chan);
1061 ast_debug(1,
"Spawn extension (%s,%s,%d) exited with %d on '%s'\n",
1062 ast_channel_context(chan), ast_channel_exten(chan),
1063 ast_channel_priority(chan), res, ast_channel_name(chan));
1064 ast_verb(2,
"Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
1065 ast_channel_context(chan), ast_channel_exten(chan),
1066 ast_channel_priority(chan), ast_channel_name(chan));
1070 if (ast_channel_priority(chan) == saved_priority
1071 && !strcmp(ast_channel_context(chan), saved_context)
1072 && !strcmp(ast_channel_exten(chan), saved_exten)) {
1073 ast_verb(3,
"%s Internal %s(%s) complete GOSUB_RETVAL=%s\n",
1074 ast_channel_name(chan), app_gosub, sub_args,
1077 ast_log(LOG_WARNING,
"%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
1078 ast_channel_name(chan), app_gosub, sub_args);
1079 balance_stack(chan);
1087 ast_debug(4,
"%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
1088 ast_channel_context(chan), ast_channel_exten(chan),
1089 ast_channel_priority(chan));
1092 if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
1093 ast_channel_context_set(chan, saved_context);
1094 ast_channel_exten_set(chan, saved_exten);
1095 ast_channel_priority_set(chan, saved_priority);
1105 if (saved_hangup_flags) {
1109 ast_channel_unlock(chan);
1114 static int handle_gosub(
struct ast_channel *chan,
AGI *agi,
int argc,
const char *
const *argv)
1118 int old_autoloopflag;
1119 int old_in_subroutine;
1121 const char *old_context;
1122 const char *old_extension;
1125 if (argc < 4 || argc > 5) {
1126 return RESULT_SHOWUSAGE;
1129 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] :
"");
1131 if (sscanf(argv[3],
"%30d", &priority) != 1 || priority < 1) {
1134 S_COR(ast_channel_caller(chan)->
id.
number.valid, ast_channel_caller(chan)->
id.
number.str, NULL));
1136 ast_log(LOG_ERROR,
"Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
1137 ast_agi_send(agi->
fd, chan,
"200 result=-1 Gosub label not found\n");
1138 return RESULT_FAILURE;
1141 S_COR(ast_channel_caller(chan)->
id.
number.valid, ast_channel_caller(chan)->
id.
number.str, NULL))) {
1142 ast_agi_send(agi->
fd, chan,
"200 result=-1 Gosub label not found\n");
1143 return RESULT_FAILURE;
1147 if (
ast_asprintf(&gosub_args,
"%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) {
1151 if (
ast_asprintf(&gosub_args,
"%s,%s,%d", argv[1], argv[2], priority) < 0) {
1156 ast_agi_send(agi->
fd, chan,
"503 result=-2 Memory allocation failure\n");
1157 return RESULT_FAILURE;
1160 ast_channel_lock(chan);
1162 ast_verb(3,
"%s AGI %s(%s) start\n", ast_channel_name(chan), app_gosub, gosub_args);
1172 old_context =
ast_strdupa(ast_channel_context(chan));
1173 old_extension =
ast_strdupa(ast_channel_exten(chan));
1174 old_priority = ast_channel_priority(chan);
1176 ast_debug(4,
"%s Original location: %s,%s,%d\n", ast_channel_name(chan),
1177 old_context, old_extension, old_priority);
1178 ast_channel_unlock(chan);
1180 res = gosub_exec(chan, gosub_args);
1185 ast_channel_lock(chan);
1189 ast_log(LOG_ERROR,
"No %s stack!\n", app_gosub);
1195 oldlist = stack_store->
data;
1199 ast_channel_unlock(chan);
1206 memset(&args, 0,
sizeof(args));
1207 args.no_hangup_chan = 1;
1209 ast_channel_lock(chan);
1212 ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
1215 pbx = ast_channel_pbx(chan);
1216 ast_channel_pbx_set(chan, NULL);
1217 ast_channel_unlock(chan);
1219 ast_agi_send(agi->
fd, chan,
"100 result=0 Trying...\n");
1222 ast_channel_lock(chan);
1223 ast_free(ast_channel_pbx(chan));
1224 ast_channel_pbx_set(chan, pbx);
1227 if (ast_channel_priority(chan) == old_priority
1228 && !strcmp(ast_channel_context(chan), old_context)
1229 && !strcmp(ast_channel_exten(chan), old_extension)) {
1230 ast_verb(3,
"%s AGI %s(%s) complete GOSUB_RETVAL=%s\n",
1231 ast_channel_name(chan), app_gosub, gosub_args,
1235 ast_log(LOG_NOTICE,
"%s Abnormal AGI %s(%s) exit. Popping routine return locations.\n",
1236 ast_channel_name(chan), app_gosub, gosub_args);
1237 balance_stack(chan);
1241 ast_channel_unlock(chan);
1243 ast_agi_send(agi->
fd, chan,
"200 result=0 Gosub complete%s\n",
1244 abnormal_exit ?
" (abnormal exit)" :
"");
1246 ast_agi_send(agi->
fd, chan,
"200 result=%d Gosub failed\n", res);
1249 ast_free(gosub_args);
1251 ast_channel_lock(chan);
1252 ast_debug(4,
"%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
1253 ast_channel_context(chan), ast_channel_exten(chan),
1254 ast_channel_priority(chan));
1257 ast_channel_context_set(chan, old_context);
1258 ast_channel_exten_set(chan, old_extension);
1259 ast_channel_priority_set(chan, old_priority);
1266 ast_channel_unlock(chan);
1268 return RESULT_SUCCESS;
1272 { {
"gosub", NULL }, handle_gosub, NULL, NULL, 0 };
1274 static int unload_module(
void)
1278 ast_agi_unregister(&gosub_agi_command);
1291 static int load_module(
void)
1296 .expand_sub_args = expand_gosub_args,
1315 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER,
"Dialplan subroutines (Gosub, Return, etc)",
1316 .support_level = AST_MODULE_SUPPORT_CORE,
1317 .load = load_module,
1318 .unload = unload_module,
1320 .optional_modules =
"res_agi",
void ast_install_stack_functions(const struct ast_app_stack_funcs *funcs)
Set stack application function callbacks.
Options for ast_pbx_run()
Main Channel structure associated with a channel.
#define AST_LIST_LOCK(head)
Locks a list.
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
unsigned int in_subroutine
AGI Extension interfaces - Asterisk Gateway Interface.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
int pbx_checkcondition(const char *condition)
Evaluate a condition.
Structure for a data store type.
Structure for a data store object.
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, without removing any previously set value...
int ast_unregister_application(const char *app)
Unregister an application.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
General Asterisk PBX channel definitions.
void ast_channel_clear_softhangup(struct ast_channel *chan, int flag)
Clear a set of softhangup flags from a channel.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Data structure associated with a custom dialplan function.
enum ast_pbx_result ast_pbx_run_args(struct ast_channel *c, struct ast_pbx_args *args)
Execute the PBX in the current thread.
int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
structure to hold extensions
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
static char * orig_exten(int fd, const char *chan, const char *data)
orginate from extension
#define ast_malloc(len)
A wrapper for malloc()
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Core PBX routines and definitions.
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Support for dynamic strings.
void ast_channel_publish_varset(struct ast_channel *chan, const char *variable, const char *value)
Publish a ast_channel_publish_varset for a channel.
Stack applications callback functions.
int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
Find the priority of an extension that has the specified label.
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
#define ast_calloc(num, len)
A wrapper for calloc()
int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid, int *found, int combined_find_spawn)
Launch a new extension (i.e. new stack)
int(* run_sub)(struct ast_channel *chan, const char *args, int ignore_hangup)
Callback for the routine to run a subroutine on a channel.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
int ast_softhangup_nolock(struct ast_channel *chan, int cause)
Softly hangup up a channel (no channel lock)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_custom_function_register(acf)
Register a custom function.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
#define AST_APP_ARG(name)
Define an application argument.