2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (c) 2004-2006 Tilghman Lesher <app_stack_v003@the-tilghman.com>.
6 * This code is released by the author with no restrictions on usage.
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Stack applications Gosub, Return, etc.
23 * \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
25 * \ingroup applications
29 <use type="module">res_agi</use>
30 <support_level>core</support_level>
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37 #include "asterisk/pbx.h"
38 #include "asterisk/module.h"
39 #include "asterisk/app.h"
40 #include "asterisk/manager.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/agi.h"
43 #include "asterisk/stasis_channels.h"
46 <application name="Gosub" language="en_US">
48 Jump to label, saving return address.
51 <parameter name="context" />
52 <parameter name="exten" />
53 <parameter name="priority" required="true" hasparams="optional">
54 <argument name="arg1" multiple="true" required="true" />
55 <argument name="argN" />
59 <para>Jumps to the label specified, saving the return address.</para>
62 <ref type="application">GosubIf</ref>
63 <ref type="application">Macro</ref>
64 <ref type="application">Goto</ref>
65 <ref type="application">Return</ref>
66 <ref type="application">StackPop</ref>
69 <application name="GosubIf" language="en_US">
71 Conditionally jump to label, saving return address.
74 <parameter name="condition" required="true" />
75 <parameter name="destination" required="true" argsep=":">
76 <argument name="labeliftrue" hasparams="optional">
77 <para>Continue at <replaceable>labeliftrue</replaceable> if the condition is true.
78 Takes the form similar to Goto() of [[context,]extension,]priority.</para>
79 <argument name="arg1" required="true" multiple="true" />
80 <argument name="argN" />
82 <argument name="labeliffalse" hasparams="optional">
83 <para>Continue at <replaceable>labeliffalse</replaceable> if the condition is false.
84 Takes the form similar to Goto() of [[context,]extension,]priority.</para>
85 <argument name="arg1" required="true" multiple="true" />
86 <argument name="argN" />
91 <para>If the condition is true, then jump to labeliftrue. If false, jumps to
92 labeliffalse, if specified. In either case, a jump saves the return point
93 in the dialplan, to be returned to with a Return.</para>
96 <ref type="application">Gosub</ref>
97 <ref type="application">Return</ref>
98 <ref type="application">MacroIf</ref>
99 <ref type="function">IF</ref>
100 <ref type="application">GotoIf</ref>
101 <ref type="application">Goto</ref>
104 <application name="Return" language="en_US">
106 Return from gosub routine.
109 <parameter name="value">
110 <para>Return value.</para>
114 <para>Jumps to the last label on the stack, removing it. The return <replaceable>value</replaceable>, if
115 any, is saved in the channel variable <variable>GOSUB_RETVAL</variable>.</para>
118 <ref type="application">Gosub</ref>
119 <ref type="application">StackPop</ref>
122 <application name="StackPop" language="en_US">
124 Remove one address from gosub stack.
128 <para>Removes last label on the stack, discarding it.</para>
131 <ref type="application">Return</ref>
132 <ref type="application">Gosub</ref>
135 <function name="LOCAL" language="en_US">
137 Manage variables local to the gosub stack frame.
140 <parameter name="varname" required="true" />
143 <para>Read and write a variable local to the gosub stack frame, once we Return() it will be lost
144 (or it will go back to whatever value it had before the Gosub()).</para>
147 <ref type="application">Gosub</ref>
148 <ref type="application">GosubIf</ref>
149 <ref type="application">Return</ref>
152 <function name="LOCAL_PEEK" language="en_US">
154 Retrieve variables hidden by the local gosub stack frame.
157 <parameter name="n" required="true" />
158 <parameter name="varname" required="true" />
161 <para>Read a variable <replaceable>varname</replaceable> hidden by
162 <replaceable>n</replaceable> levels of gosub stack frames. Note that ${LOCAL_PEEK(0,foo)}
163 is the same as <variable>foo</variable>, since the value of <replaceable>n</replaceable>
164 peeks under 0 levels of stack frames; in other words, 0 is the current level. If
165 <replaceable>n</replaceable> exceeds the available number of stack frames, then an empty
166 string is returned.</para>
169 <ref type="application">Gosub</ref>
170 <ref type="application">GosubIf</ref>
171 <ref type="application">Return</ref>
174 <function name="STACK_PEEK" language="en_US">
176 View info about the location which called Gosub
179 <parameter name="n" required="true" />
180 <parameter name="which" required="true" />
181 <parameter name="suppress" required="false" />
184 <para>Read the calling <literal>c</literal>ontext, <literal>e</literal>xtension,
185 <literal>p</literal>riority, or <literal>l</literal>abel, as specified by
186 <replaceable>which</replaceable>, by going up <replaceable>n</replaceable> frames
187 in the Gosub stack. If <replaceable>suppress</replaceable> is true, then if the
188 number of available stack frames is exceeded, then no error message will be
192 <agi name="gosub" language="en_US">
194 Cause the channel to execute the specified dialplan subroutine.
197 <parameter name="context" required="true" />
198 <parameter name="extension" required="true" />
199 <parameter name="priority" required="true" />
200 <parameter name="optional-argument" />
203 <para>Cause the channel to execute the specified dialplan subroutine,
204 returning to the dialplan with execution of a Return().</para>
207 <ref type="application">GoSub</ref>
210 <managerEvent language="en_US" name="VarSet">
211 <managerEventInstance class="EVENT_FLAG_DIALPLAN">
212 <synopsis>Raised when a variable local to the gosub stack frame is set due to a subroutine call.</synopsis>
215 <parameter name="Variable">
216 <para>The LOCAL variable being set.</para>
217 <note><para>The variable name will always be enclosed with
218 <literal>LOCAL()</literal></para></note>
220 <parameter name="Value">
221 <para>The new value of the variable.</para>
225 <ref type="application">GoSub</ref>
226 <ref type="agi">gosub</ref>
227 <ref type="function">LOCAL</ref>
228 <ref type="function">LOCAL_PEEK</ref>
230 </managerEventInstance>
234 static const char app_gosub[] = "Gosub";
235 static const char app_gosubif[] = "GosubIf";
236 static const char app_return[] = "Return";
237 static const char app_pop[] = "StackPop";
239 static void gosub_free(void *data);
241 static const struct ast_datastore_info stack_info = {
243 .destroy = gosub_free,
246 struct gosub_stack_frame {
247 AST_LIST_ENTRY(gosub_stack_frame) entries;
248 /* 100 arguments is all that we support anyway, but this will handle up to 255 */
249 unsigned char arguments;
250 struct varshead varshead;
252 /*! TRUE if the return location marks the end of a special routine. */
253 unsigned int is_special:1;
258 AST_LIST_HEAD(gosub_stack_list, gosub_stack_frame);
260 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
262 struct ast_var_t *variables;
265 RAII_VAR(char *, local_buffer, NULL, ast_free);
267 /* Does this variable already exist? */
268 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
269 if (!strcmp(var, ast_var_name(variables))) {
276 if ((variables = ast_var_assign(var, ""))) {
277 AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
279 pbx_builtin_pushvar_helper(chan, var, value);
281 pbx_builtin_setvar_helper(chan, var, value);
284 len = 8 + strlen(var); /* LOCAL() + var */
285 local_buffer = ast_malloc(len);
289 sprintf(local_buffer, "LOCAL(%s)", var);
290 ast_channel_publish_varset(chan, local_buffer, value);
294 static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
296 struct ast_var_t *vardata;
298 /* If chan is not defined, then we're calling it as part of gosub_free,
299 * and the channel variables will be deallocated anyway. Otherwise, we're
300 * just releasing a single frame, so we need to clean up the arguments for
301 * that frame, so that we re-expose the variables from the previous frame
302 * that were hidden by this one.
304 while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
306 pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
307 ast_var_delete(vardata);
313 static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
315 struct gosub_stack_frame *new = NULL;
316 int len_extension = strlen(extension), len_context = strlen(context);
318 if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
319 AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
320 strcpy(new->extension, extension);
321 new->context = new->extension + len_extension + 1;
322 strcpy(new->context, context);
323 new->priority = priority;
324 new->arguments = arguments;
329 static void gosub_free(void *data)
331 struct gosub_stack_list *oldlist = data;
332 struct gosub_stack_frame *oldframe;
334 AST_LIST_LOCK(oldlist);
335 while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
336 gosub_release_frame(NULL, oldframe);
338 AST_LIST_UNLOCK(oldlist);
339 AST_LIST_HEAD_DESTROY(oldlist);
343 static int pop_exec(struct ast_channel *chan, const char *data)
345 struct ast_datastore *stack_store;
346 struct gosub_stack_frame *oldframe;
347 struct gosub_stack_list *oldlist;
350 ast_channel_lock(chan);
351 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
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;
358 AST_LIST_LOCK(oldlist);
359 oldframe = AST_LIST_FIRST(oldlist);
361 if (oldframe->is_special) {
362 ast_debug(1, "%s attempted to pop special return location.\n", app_pop);
364 /* Abort the special routine dialplan execution. Dialplan programming error. */
367 AST_LIST_REMOVE_HEAD(oldlist, entries);
368 gosub_release_frame(chan, oldframe);
371 ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
373 AST_LIST_UNLOCK(oldlist);
374 ast_channel_unlock(chan);
378 static int return_exec(struct ast_channel *chan, const char *data)
380 struct ast_datastore *stack_store;
381 struct gosub_stack_frame *oldframe;
382 struct gosub_stack_list *oldlist;
383 const char *retval = data;
386 ast_channel_lock(chan);
387 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
388 ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
389 ast_channel_unlock(chan);
393 oldlist = stack_store->data;
394 AST_LIST_LOCK(oldlist);
395 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
396 AST_LIST_UNLOCK(oldlist);
399 ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
400 ast_channel_unlock(chan);
403 if (oldframe->is_special) {
404 /* Exit from special routine. */
409 * We cannot use ast_explicit_goto() because we MUST restore
410 * what was there before. Channels that do not have a PBX may
411 * not have the context or exten set.
413 ast_channel_context_set(chan, oldframe->context);
414 ast_channel_exten_set(chan, oldframe->extension);
415 if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
416 --oldframe->priority;
418 ast_channel_priority_set(chan, oldframe->priority);
420 gosub_release_frame(chan, oldframe);
422 /* Set a return value, if any */
423 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
424 ast_channel_unlock(chan);
430 * \brief Add missing context and/or exten to Gosub application argument string.
433 * \param chan Channel to obtain context/exten.
434 * \param args Gosub application argument string.
437 * Fills in the optional context and exten from the given channel.
438 * Convert: [[context,]exten,]priority[(arg1[,...][,argN])]
439 * To: context,exten,priority[(arg1[,...][,argN])]
441 * \retval expanded Gosub argument string on success. Must be freed.
442 * \retval NULL on error.
444 * \note The parsing needs to be kept in sync with the
445 * gosub_exec() argument format.
447 static const char *expand_gosub_args(struct ast_channel *chan, const char *args)
457 /* Separate the context,exten,pri from the optional routine arguments. */
458 parse = ast_strdupa(args);
459 label = strsep(&parse, "(");
463 endparen = strrchr(parse, ')');
467 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", args);
471 /* Split context,exten,pri */
472 context = strsep(&label, ",");
473 exten = strsep(&label, ",");
474 pri = strsep(&label, ",");
476 /* Only a priority in this one */
481 /* Only an extension and priority in this one */
487 ast_channel_lock(chan);
488 if (ast_strlen_zero(exten)) {
489 exten = ast_channel_exten(chan);
491 if (ast_strlen_zero(context)) {
492 context = ast_channel_context(chan);
494 len = strlen(context) + strlen(exten) + strlen(pri) + 3;
495 if (!ast_strlen_zero(parse)) {
496 len += 2 + strlen(parse);
498 new_args = ast_malloc(len);
500 if (ast_strlen_zero(parse)) {
501 snprintf(new_args, len, "%s,%s,%s", context, exten, pri);
503 snprintf(new_args, len, "%s,%s,%s(%s)", context, exten, pri, parse);
506 ast_channel_unlock(chan);
508 ast_debug(4, "Gosub args:%s new_args:%s\n", args, new_args ? new_args : "");
513 static int gosub_exec(struct ast_channel *chan, const char *data)
515 struct ast_datastore *stack_store;
516 struct gosub_stack_list *oldlist;
517 struct gosub_stack_frame *newframe;
518 struct gosub_stack_frame *lastframe;
531 AST_DECLARE_APP_ARGS(args2,
532 AST_APP_ARG(argval)[100];
535 if (ast_strlen_zero(data)) {
536 ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
541 * Separate the arguments from the label
543 * NOTE: You cannot use ast_app_separate_args for this, because
544 * '(' cannot be used as a delimiter.
546 parse = ast_strdupa(data);
547 label = strsep(&parse, "(");
551 endparen = strrchr(parse, ')');
555 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", data);
557 AST_STANDARD_RAW_ARGS(args2, parse);
562 ast_channel_lock(chan);
563 orig_context = ast_strdupa(ast_channel_context(chan));
564 orig_exten = ast_strdupa(ast_channel_exten(chan));
565 orig_priority = ast_channel_priority(chan);
566 ast_channel_unlock(chan);
568 if (ast_parseable_goto(chan, label)) {
569 ast_log(LOG_ERROR, "%s address is invalid: '%s'\n", app_gosub, data);
573 ast_channel_lock(chan);
574 dest_context = ast_strdupa(ast_channel_context(chan));
575 dest_exten = ast_strdupa(ast_channel_exten(chan));
576 dest_priority = ast_channel_priority(chan);
577 if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
580 caller_id = S_COR(ast_channel_caller(chan)->id.number.valid,
581 ast_channel_caller(chan)->id.number.str, NULL);
583 caller_id = ast_strdupa(caller_id);
585 ast_channel_unlock(chan);
587 if (!ast_exists_extension(chan, dest_context, dest_exten, dest_priority, caller_id)) {
588 ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for %s: (Context:%s, Extension:%s, Priority:%d)\n",
589 app_gosub, dest_context, dest_exten, dest_priority);
593 /* Now we know that we're going to a new location */
595 ast_channel_lock(chan);
597 /* Find stack datastore return list. */
598 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
599 ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n",
600 ast_channel_name(chan));
601 stack_store = ast_datastore_alloc(&stack_info, NULL);
603 ast_log(LOG_ERROR, "Unable to allocate new datastore. %s failed.\n",
605 goto error_exit_locked;
608 oldlist = ast_calloc(1, sizeof(*oldlist));
610 ast_log(LOG_ERROR, "Unable to allocate datastore list head. %s failed.\n",
612 ast_datastore_free(stack_store);
613 goto error_exit_locked;
615 AST_LIST_HEAD_INIT(oldlist);
617 stack_store->data = oldlist;
618 ast_channel_datastore_add(chan, stack_store);
620 oldlist = stack_store->data;
623 if ((lastframe = AST_LIST_FIRST(oldlist))) {
624 max_argc = lastframe->arguments;
627 /* Mask out previous Gosub arguments in this invocation */
628 if (args2.argc > max_argc) {
629 max_argc = args2.argc;
632 /* Create the return address */
633 newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, max_argc);
635 goto error_exit_locked;
638 /* Set our arguments */
639 for (i = 0; i < max_argc; i++) {
640 snprintf(argname, sizeof(argname), "ARG%d", i + 1);
641 frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : "");
642 ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : "");
644 snprintf(argname, sizeof(argname), "%d", args2.argc);
645 frame_set_var(chan, newframe, "ARGC", argname);
647 /* And finally, save our return address */
648 AST_LIST_LOCK(oldlist);
649 AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
650 AST_LIST_UNLOCK(oldlist);
651 ast_channel_unlock(chan);
656 ast_channel_lock(chan);
659 /* Restore the original dialplan location. */
660 ast_channel_context_set(chan, orig_context);
661 ast_channel_exten_set(chan, orig_exten);
662 ast_channel_priority_set(chan, orig_priority);
663 ast_channel_unlock(chan);
667 static int gosubif_exec(struct ast_channel *chan, const char *data)
671 AST_DECLARE_APP_ARGS(cond,
675 AST_DECLARE_APP_ARGS(label,
677 AST_APP_ARG(iffalse);
680 if (ast_strlen_zero(data)) {
681 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
685 args = ast_strdupa(data);
686 AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
687 if (cond.argc != 2) {
688 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
692 AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
694 if (pbx_checkcondition(cond.ition)) {
695 if (!ast_strlen_zero(label.iftrue))
696 res = gosub_exec(chan, label.iftrue);
697 } else if (!ast_strlen_zero(label.iffalse)) {
698 res = gosub_exec(chan, label.iffalse);
704 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
706 struct ast_datastore *stack_store;
707 struct gosub_stack_list *oldlist;
708 struct gosub_stack_frame *frame;
709 struct ast_var_t *variables;
712 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
716 ast_channel_lock(chan);
717 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
718 ast_channel_unlock(chan);
722 oldlist = stack_store->data;
723 AST_LIST_LOCK(oldlist);
724 if (!(frame = AST_LIST_FIRST(oldlist))) {
725 /* Not within a Gosub routine */
726 AST_LIST_UNLOCK(oldlist);
727 ast_channel_unlock(chan);
731 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
732 if (!strcmp(data, ast_var_name(variables))) {
734 tmp = pbx_builtin_getvar_helper(chan, data);
735 ast_copy_string(buf, S_OR(tmp, ""), len);
739 AST_LIST_UNLOCK(oldlist);
740 ast_channel_unlock(chan);
744 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
746 struct ast_datastore *stack_store;
747 struct gosub_stack_list *oldlist;
748 struct gosub_stack_frame *frame;
751 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
755 ast_channel_lock(chan);
756 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
757 ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
758 ast_channel_unlock(chan);
762 oldlist = stack_store->data;
763 AST_LIST_LOCK(oldlist);
764 frame = AST_LIST_FIRST(oldlist);
767 frame_set_var(chan, frame, var, value);
770 AST_LIST_UNLOCK(oldlist);
771 ast_channel_unlock(chan);
776 static struct ast_custom_function local_function = {
778 .write = local_write,
782 static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
785 struct ast_var_t *variables;
786 AST_DECLARE_APP_ARGS(args,
792 ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
796 AST_STANDARD_RAW_ARGS(args, data);
798 if (ast_strlen_zero(args.n) || ast_strlen_zero(args.name)) {
799 ast_log(LOG_ERROR, "LOCAL_PEEK requires parameters n and varname\n");
806 ast_channel_lock(chan);
807 AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
808 if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
809 ast_copy_string(buf, ast_var_value(variables), len);
813 ast_channel_unlock(chan);
817 static struct ast_custom_function peek_function = {
818 .name = "LOCAL_PEEK",
822 static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
824 struct ast_datastore *stack_store;
825 struct gosub_stack_list *oldlist;
826 struct gosub_stack_frame *frame;
828 AST_DECLARE_APP_ARGS(args,
831 AST_APP_ARG(suppress);
835 ast_log(LOG_ERROR, "STACK_PEEK must be called on an active channel\n");
839 data = ast_strdupa(data);
840 AST_STANDARD_APP_ARGS(args, data);
842 if (ast_strlen_zero(args.n) || ast_strlen_zero(args.which)) {
843 ast_log(LOG_ERROR, "STACK_PEEK requires parameters n and which\n");
849 ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
853 ast_channel_lock(chan);
854 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
855 if (!ast_true(args.suppress)) {
856 ast_log(LOG_ERROR, "STACK_PEEK called on a channel without a gosub stack\n");
858 ast_channel_unlock(chan);
862 oldlist = stack_store->data;
864 AST_LIST_LOCK(oldlist);
865 AST_LIST_TRAVERSE(oldlist, frame, entries) {
873 if (!ast_true(args.suppress)) {
874 ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n);
876 AST_LIST_UNLOCK(oldlist);
877 ast_channel_unlock(chan);
881 args.which = ast_skip_blanks(args.which);
883 switch (args.which[0]) {
884 case 'l': /* label */
885 ast_str_set(str, len, "%s,%s,%d", frame->context, frame->extension, frame->priority - 1);
887 case 'c': /* context */
888 ast_str_set(str, len, "%s", frame->context);
890 case 'e': /* extension */
891 ast_str_set(str, len, "%s", frame->extension);
893 case 'p': /* priority */
894 ast_str_set(str, len, "%d", frame->priority - 1);
897 ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
901 AST_LIST_UNLOCK(oldlist);
902 ast_channel_unlock(chan);
907 static struct ast_custom_function stackpeek_function = {
908 .name = "STACK_PEEK",
909 .read2 = stackpeek_read,
914 * \brief Pop stack frames until remove a special return location.
917 * \param chan Channel to balance stack on.
919 * \note The channel is already locked when called.
923 static void balance_stack(struct ast_channel *chan)
925 struct ast_datastore *stack_store;
926 struct gosub_stack_list *oldlist;
927 struct gosub_stack_frame *oldframe;
930 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
932 ast_log(LOG_WARNING, "No %s stack allocated.\n", app_gosub);
936 oldlist = stack_store->data;
937 AST_LIST_LOCK(oldlist);
939 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
943 found = oldframe->is_special;
944 gosub_release_frame(chan, oldframe);
946 AST_LIST_UNLOCK(oldlist);
951 * \brief Run a subroutine on a channel.
954 * \note Absolutely _NO_ channel locks should be held before calling this function.
956 * \param chan Channel to execute subroutine on.
957 * \param sub_args Gosub application argument string.
958 * \param ignore_hangup TRUE if a hangup does not stop execution of the routine.
961 * \retval -1 on error
963 static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_hangup)
965 const char *saved_context;
966 const char *saved_exten;
968 int saved_hangup_flags;
969 int saved_autoloopflag;
972 ast_channel_lock(chan);
974 ast_verb(3, "%s Internal %s(%s) start\n",
975 ast_channel_name(chan), app_gosub, sub_args);
977 /* Save non-hangup softhangup flags. */
978 saved_hangup_flags = ast_channel_softhangup_internal_flag(chan)
979 & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
980 if (saved_hangup_flags) {
981 ast_channel_clear_softhangup(chan,
982 AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
985 /* Save autoloop flag */
986 saved_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
987 ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
989 /* Save current dialplan location */
990 saved_context = ast_strdupa(ast_channel_context(chan));
991 saved_exten = ast_strdupa(ast_channel_exten(chan));
992 saved_priority = ast_channel_priority(chan);
994 ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
995 saved_context, saved_exten, saved_priority);
997 ast_channel_unlock(chan);
998 res = gosub_exec(chan, sub_args);
999 ast_debug(4, "%s exited with status %d\n", app_gosub, res);
1000 ast_channel_lock(chan);
1002 struct ast_datastore *stack_store;
1004 /* Mark the return location as special. */
1005 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
1007 /* Should never happen! */
1008 ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
1011 struct gosub_stack_list *oldlist;
1012 struct gosub_stack_frame *cur;
1014 oldlist = stack_store->data;
1015 cur = AST_LIST_FIRST(oldlist);
1016 cur->is_special = 1;
1020 int found = 0; /* set if we find at least one match */
1023 * Run gosub body autoloop.
1025 * Note that this loop is inverted from the normal execution
1026 * loop because we just executed the Gosub application as the
1027 * first extension of the autoloop.
1030 /* Check for hangup. */
1031 if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_UNBRIDGE) {
1032 saved_hangup_flags |= AST_SOFTHANGUP_UNBRIDGE;
1033 ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_UNBRIDGE);
1035 if (ast_check_hangup(chan)) {
1036 if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
1037 ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n",
1038 ast_channel_name(chan));
1041 if (!ignore_hangup) {
1046 /* Next dialplan priority. */
1047 ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
1049 ast_channel_unlock(chan);
1050 res = ast_spawn_extension(chan, ast_channel_context(chan),
1051 ast_channel_exten(chan), ast_channel_priority(chan),
1052 S_COR(ast_channel_caller(chan)->id.number.valid,
1053 ast_channel_caller(chan)->id.number.str, NULL),
1055 ast_channel_lock(chan);
1058 /* Something bad happened, or a hangup has been requested. */
1059 ast_debug(1, "Spawn extension (%s,%s,%d) exited with %d on '%s'\n",
1060 ast_channel_context(chan), ast_channel_exten(chan),
1061 ast_channel_priority(chan), res, ast_channel_name(chan));
1062 ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
1063 ast_channel_context(chan), ast_channel_exten(chan),
1064 ast_channel_priority(chan), ast_channel_name(chan));
1067 /* Did the routine return? */
1068 if (ast_channel_priority(chan) == saved_priority
1069 && !strcmp(ast_channel_context(chan), saved_context)
1070 && !strcmp(ast_channel_exten(chan), saved_exten)) {
1071 ast_verb(3, "%s Internal %s(%s) complete GOSUB_RETVAL=%s\n",
1072 ast_channel_name(chan), app_gosub, sub_args,
1073 S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
1075 ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
1076 ast_channel_name(chan), app_gosub, sub_args);
1077 balance_stack(chan);
1078 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
1081 /* We executed the requested subroutine to the best of our ability. */
1085 ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
1086 ast_channel_context(chan), ast_channel_exten(chan),
1087 ast_channel_priority(chan));
1089 /* Restore dialplan location */
1090 if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
1091 ast_channel_context_set(chan, saved_context);
1092 ast_channel_exten_set(chan, saved_exten);
1093 ast_channel_priority_set(chan, saved_priority);
1096 /* Restore autoloop flag */
1097 ast_set2_flag(ast_channel_flags(chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
1099 /* Restore non-hangup softhangup flags. */
1100 if (saved_hangup_flags) {
1101 ast_softhangup_nolock(chan, saved_hangup_flags);
1104 ast_channel_unlock(chan);
1109 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
1113 int old_autoloopflag;
1115 const char *old_context;
1116 const char *old_extension;
1119 if (argc < 4 || argc > 5) {
1120 return RESULT_SHOWUSAGE;
1123 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] : "");
1125 if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
1126 /* Lookup the priority label */
1127 priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
1128 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
1130 ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
1131 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
1132 return RESULT_FAILURE;
1134 } else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
1135 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
1136 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
1137 return RESULT_FAILURE;
1141 if (ast_asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) {
1145 if (ast_asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) {
1150 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
1151 return RESULT_FAILURE;
1154 ast_channel_lock(chan);
1156 ast_verb(3, "%s AGI %s(%s) start\n", ast_channel_name(chan), app_gosub, gosub_args);
1158 /* Save autoloop flag */
1159 old_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
1160 ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
1162 /* Save previous location, since we're going to change it */
1163 old_context = ast_strdupa(ast_channel_context(chan));
1164 old_extension = ast_strdupa(ast_channel_exten(chan));
1165 old_priority = ast_channel_priority(chan);
1167 ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
1168 old_context, old_extension, old_priority);
1169 ast_channel_unlock(chan);
1171 res = gosub_exec(chan, gosub_args);
1173 struct ast_datastore *stack_store;
1175 /* Mark the return location as special. */
1176 ast_channel_lock(chan);
1177 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
1179 /* Should never happen! */
1180 ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
1183 struct gosub_stack_list *oldlist;
1184 struct gosub_stack_frame *cur;
1186 oldlist = stack_store->data;
1187 cur = AST_LIST_FIRST(oldlist);
1188 cur->is_special = 1;
1190 ast_channel_unlock(chan);
1193 struct ast_pbx *pbx;
1194 struct ast_pbx_args args;
1197 memset(&args, 0, sizeof(args));
1198 args.no_hangup_chan = 1;
1200 ast_channel_lock(chan);
1202 /* Next dialplan priority. */
1203 ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
1205 /* Suppress warning about PBX already existing */
1206 pbx = ast_channel_pbx(chan);
1207 ast_channel_pbx_set(chan, NULL);
1208 ast_channel_unlock(chan);
1210 ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
1211 ast_pbx_run_args(chan, &args);
1213 ast_channel_lock(chan);
1214 ast_free(ast_channel_pbx(chan));
1215 ast_channel_pbx_set(chan, pbx);
1217 /* Did the routine return? */
1218 if (ast_channel_priority(chan) == old_priority
1219 && !strcmp(ast_channel_context(chan), old_context)
1220 && !strcmp(ast_channel_exten(chan), old_extension)) {
1221 ast_verb(3, "%s AGI %s(%s) complete GOSUB_RETVAL=%s\n",
1222 ast_channel_name(chan), app_gosub, gosub_args,
1223 S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
1226 ast_log(LOG_NOTICE, "%s Abnormal AGI %s(%s) exit. Popping routine return locations.\n",
1227 ast_channel_name(chan), app_gosub, gosub_args);
1228 balance_stack(chan);
1229 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
1232 ast_channel_unlock(chan);
1234 ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete%s\n",
1235 abnormal_exit ? " (abnormal exit)" : "");
1237 ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
1240 ast_free(gosub_args);
1242 ast_channel_lock(chan);
1243 ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
1244 ast_channel_context(chan), ast_channel_exten(chan),
1245 ast_channel_priority(chan));
1247 /* Restore previous location */
1248 ast_channel_context_set(chan, old_context);
1249 ast_channel_exten_set(chan, old_extension);
1250 ast_channel_priority_set(chan, old_priority);
1252 /* Restore autoloop flag */
1253 ast_set2_flag(ast_channel_flags(chan), old_autoloopflag, AST_FLAG_IN_AUTOLOOP);
1254 ast_channel_unlock(chan);
1256 return RESULT_SUCCESS;
1259 static struct agi_command gosub_agi_command =
1260 { { "gosub", NULL }, handle_gosub, NULL, NULL, 0 };
1262 static int unload_module(void)
1264 ast_install_stack_functions(NULL);
1266 ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
1268 ast_unregister_application(app_return);
1269 ast_unregister_application(app_pop);
1270 ast_unregister_application(app_gosubif);
1271 ast_unregister_application(app_gosub);
1272 ast_custom_function_unregister(&local_function);
1273 ast_custom_function_unregister(&peek_function);
1274 ast_custom_function_unregister(&stackpeek_function);
1279 static int load_module(void)
1281 /* Setup the stack application callback functions. */
1282 static struct ast_app_stack_funcs funcs = {
1283 .run_sub = gosub_run,
1284 .expand_sub_args = expand_gosub_args,
1287 ast_agi_register(ast_module_info->self, &gosub_agi_command);
1289 ast_register_application_xml(app_pop, pop_exec);
1290 ast_register_application_xml(app_return, return_exec);
1291 ast_register_application_xml(app_gosubif, gosubif_exec);
1292 ast_register_application_xml(app_gosub, gosub_exec);
1293 ast_custom_function_register(&local_function);
1294 ast_custom_function_register(&peek_function);
1295 ast_custom_function_register(&stackpeek_function);
1297 funcs.module = ast_module_info->self,
1298 ast_install_stack_functions(&funcs);
1303 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER, "Dialplan subroutines (Gosub, Return, etc)",
1304 .load = load_module,
1305 .unload = unload_module,
1306 .load_pri = AST_MODPRI_APP_DEPEND,
1307 .nonoptreq = "res_agi",