Asterisk - The Open Source Telephony Project GIT-master-55f4e6d
app_stack.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (c) 2004-2006 Tilghman Lesher <app_stack_v003@the-tilghman.com>.
5 *
6 * This code is released by the author with no restrictions on usage.
7 *
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.
13 *
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.
17 */
18
19/*! \file
20 *
21 * \brief Stack applications Gosub, Return, etc.
22 *
23 * \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
24 *
25 * \ingroup applications
26 */
27
28/*** MODULEINFO
29 <use type="module">res_agi</use>
30 <support_level>core</support_level>
31 ***/
32
33#include "asterisk.h"
34
35#include "asterisk/pbx.h"
36#include "asterisk/module.h"
37#include "asterisk/app.h"
38#include "asterisk/manager.h"
39#include "asterisk/channel.h"
40#include "asterisk/agi.h"
42
43/*** DOCUMENTATION
44 <application name="Gosub" language="en_US">
45 <synopsis>
46 Jump to label, saving return address.
47 </synopsis>
48 <syntax>
49 <parameter name="context" />
50 <parameter name="exten" />
51 <parameter name="priority" required="true" hasparams="optional">
52 <argument name="arg1" multiple="true" required="true" />
53 <argument name="argN" />
54 </parameter>
55 </syntax>
56 <description>
57 <para>Jumps to the label specified, saving the return address.</para>
58 </description>
59 <see-also>
60 <ref type="application">GosubIf</ref>
61 <ref type="application">Goto</ref>
62 <ref type="application">Return</ref>
63 <ref type="application">StackPop</ref>
64 </see-also>
65 </application>
66 <application name="GosubIf" language="en_US">
67 <synopsis>
68 Conditionally jump to label, saving return address.
69 </synopsis>
70 <syntax argsep="?">
71 <parameter name="condition" required="true" />
72 <parameter name="destination" required="true" argsep=":">
73 <argument name="labeliftrue" hasparams="optional">
74 <para>Continue at <replaceable>labeliftrue</replaceable> if the condition is true.
75 Takes the form similar to Goto() of [[context,]extension,]priority.</para>
76 <argument name="arg1" required="true" multiple="true" />
77 <argument name="argN" />
78 </argument>
79 <argument name="labeliffalse" hasparams="optional">
80 <para>Continue at <replaceable>labeliffalse</replaceable> if the condition is false.
81 Takes the form similar to Goto() of [[context,]extension,]priority.</para>
82 <argument name="arg1" required="true" multiple="true" />
83 <argument name="argN" />
84 </argument>
85 </parameter>
86 </syntax>
87 <description>
88 <para>If the condition is true, then jump to labeliftrue. If false, jumps to
89 labeliffalse, if specified. In either case, a jump saves the return point
90 in the dialplan, to be returned to with a Return.</para>
91 </description>
92 <see-also>
93 <ref type="application">Gosub</ref>
94 <ref type="application">Return</ref>
95 <ref type="function">IF</ref>
96 <ref type="application">GotoIf</ref>
97 <ref type="application">Goto</ref>
98 </see-also>
99 </application>
100 <application name="Return" language="en_US">
101 <synopsis>
102 Return from gosub routine.
103 </synopsis>
104 <syntax>
105 <parameter name="value">
106 <para>Return value.</para>
107 </parameter>
108 </syntax>
109 <description>
110 <para>Jumps to the last label on the stack, removing it. The return <replaceable>value</replaceable>, if
111 any, is saved in the channel variable <variable>GOSUB_RETVAL</variable>.</para>
112 </description>
113 <see-also>
114 <ref type="application">Gosub</ref>
115 <ref type="application">StackPop</ref>
116 </see-also>
117 </application>
118 <application name="StackPop" language="en_US">
119 <synopsis>
120 Remove one address from gosub stack.
121 </synopsis>
122 <syntax />
123 <description>
124 <para>Removes last label on the stack, discarding it.</para>
125 </description>
126 <see-also>
127 <ref type="application">Return</ref>
128 <ref type="application">Gosub</ref>
129 </see-also>
130 </application>
131 <function name="LOCAL" language="en_US">
132 <synopsis>
133 Manage variables local to the gosub stack frame.
134 </synopsis>
135 <syntax>
136 <parameter name="varname" required="true" />
137 </syntax>
138 <description>
139 <para>Read and write a variable local to the gosub stack frame, once we Return() it will be lost
140 (or it will go back to whatever value it had before the Gosub()).</para>
141 </description>
142 <see-also>
143 <ref type="application">Gosub</ref>
144 <ref type="application">GosubIf</ref>
145 <ref type="application">Return</ref>
146 </see-also>
147 </function>
148 <function name="LOCAL_PEEK" language="en_US">
149 <synopsis>
150 Retrieve variables hidden by the local gosub stack frame.
151 </synopsis>
152 <syntax>
153 <parameter name="n" required="true" />
154 <parameter name="varname" required="true" />
155 </syntax>
156 <description>
157 <para>Read a variable <replaceable>varname</replaceable> hidden by
158 <replaceable>n</replaceable> levels of gosub stack frames. Note that ${LOCAL_PEEK(0,foo)}
159 is the same as <variable>foo</variable>, since the value of <replaceable>n</replaceable>
160 peeks under 0 levels of stack frames; in other words, 0 is the current level. If
161 <replaceable>n</replaceable> exceeds the available number of stack frames, then an empty
162 string is returned.</para>
163 </description>
164 <see-also>
165 <ref type="application">Gosub</ref>
166 <ref type="application">GosubIf</ref>
167 <ref type="application">Return</ref>
168 </see-also>
169 </function>
170 <function name="STACK_PEEK" language="en_US">
171 <synopsis>
172 View info about the location which called Gosub
173 </synopsis>
174 <syntax>
175 <parameter name="n" required="true" />
176 <parameter name="which" required="true" />
177 <parameter name="suppress" required="false" />
178 </syntax>
179 <description>
180 <para>Read the calling <literal>c</literal>ontext, <literal>e</literal>xtension,
181 <literal>p</literal>riority, or <literal>l</literal>abel, as specified by
182 <replaceable>which</replaceable>, by going up <replaceable>n</replaceable> frames
183 in the Gosub stack. If <replaceable>suppress</replaceable> is true, then if the
184 number of available stack frames is exceeded, then no error message will be
185 printed.</para>
186 </description>
187 </function>
188 <agi name="gosub" language="en_US">
189 <synopsis>
190 Cause the channel to execute the specified dialplan subroutine.
191 </synopsis>
192 <syntax>
193 <parameter name="context" required="true" />
194 <parameter name="extension" required="true" />
195 <parameter name="priority" required="true" />
196 <parameter name="optional-argument" />
197 </syntax>
198 <description>
199 <para>Cause the channel to execute the specified dialplan subroutine,
200 returning to the dialplan with execution of a Return().</para>
201 </description>
202 <see-also>
203 <ref type="application">Gosub</ref>
204 </see-also>
205 </agi>
206 <managerEvent language="en_US" name="VarSet">
207 <managerEventInstance class="EVENT_FLAG_DIALPLAN">
208 <synopsis>Raised when a variable local to the gosub stack frame is set due to a subroutine call.</synopsis>
209 <syntax>
210 <channel_snapshot/>
211 <parameter name="Variable">
212 <para>The LOCAL variable being set.</para>
213 <note><para>The variable name will always be enclosed with
214 <literal>LOCAL()</literal></para></note>
215 </parameter>
216 <parameter name="Value">
217 <para>The new value of the variable.</para>
218 </parameter>
219 </syntax>
220 <see-also>
221 <ref type="application">Gosub</ref>
222 <ref type="agi">gosub</ref>
223 <ref type="function">LOCAL</ref>
224 <ref type="function">LOCAL_PEEK</ref>
225 </see-also>
226 </managerEventInstance>
227 </managerEvent>
228 ***/
229
230static const char app_gosub[] = "Gosub";
231static const char app_gosubif[] = "GosubIf";
232static const char app_return[] = "Return";
233static const char app_pop[] = "StackPop";
234
235static void gosub_free(void *data);
236
237static const struct ast_datastore_info stack_info = {
238 .type = "GOSUB",
239 .destroy = gosub_free,
240};
241
244 /* 100 arguments is all that we support anyway, but this will handle up to 255 */
245 unsigned char arguments;
248 /*! TRUE if the return location marks the end of a special routine. */
249 unsigned int is_special:1;
250 /*! Whether or not we were in a subroutine when this one was created */
251 unsigned int in_subroutine:1;
252 char *context;
253 char extension[0];
254};
255
257
258static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
259{
260 struct ast_var_t *variables;
261 int found = 0;
262 int len;
263 RAII_VAR(char *, local_buffer, NULL, ast_free);
264
265 /* Does this variable already exist? */
266 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
267 if (!strcmp(var, ast_var_name(variables))) {
268 found = 1;
269 break;
270 }
271 }
272
273 if (!found) {
274 if ((variables = ast_var_assign(var, ""))) {
275 AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
276 }
278 } else {
280 }
281
282 len = 8 + strlen(var); /* LOCAL() + var */
283 local_buffer = ast_malloc(len);
284 if (!local_buffer) {
285 return 0;
286 }
287 sprintf(local_buffer, "LOCAL(%s)", var);
288 ast_channel_publish_varset(chan, local_buffer, value);
289 return 0;
290}
291
292static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
293{
294 struct ast_var_t *vardata;
295
296 /* If chan is not defined, then we're calling it as part of gosub_free,
297 * and the channel variables will be deallocated anyway. Otherwise, we're
298 * just releasing a single frame, so we need to clean up the arguments for
299 * that frame, so that we re-expose the variables from the previous frame
300 * that were hidden by this one.
301 */
302 while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
303 if (chan)
305 ast_var_delete(vardata);
306 }
307
308 ast_free(frame);
309}
310
311static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, int in_subroutine, unsigned char arguments)
312{
313 struct gosub_stack_frame *new = NULL;
314 int len_extension = strlen(extension) + 1;
315 int len_context = strlen(context) + 1;
316
317 if ((new = ast_calloc(1, sizeof(*new) + len_extension + len_context))) {
318 AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
319 ast_copy_string(new->extension, extension, len_extension);
320 new->context = new->extension + len_extension;
321 ast_copy_string(new->context, context, len_context);
322 new->priority = priority;
323 new->in_subroutine = in_subroutine ? 1 : 0;
324 new->arguments = arguments;
325 }
326 return new;
327}
328
329static void gosub_free(void *data)
330{
331 struct gosub_stack_list *oldlist = data;
332 struct gosub_stack_frame *oldframe;
333
334 AST_LIST_LOCK(oldlist);
335 while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
336 gosub_release_frame(NULL, oldframe);
337 }
338 AST_LIST_UNLOCK(oldlist);
339 AST_LIST_HEAD_DESTROY(oldlist);
340 ast_free(oldlist);
341}
342
343static int pop_exec(struct ast_channel *chan, const char *data)
344{
345 struct ast_datastore *stack_store;
346 struct gosub_stack_frame *oldframe;
347 struct gosub_stack_list *oldlist;
348 int res = 0;
349
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);
354 return 0;
355 }
356
357 oldlist = stack_store->data;
358 AST_LIST_LOCK(oldlist);
359 oldframe = AST_LIST_FIRST(oldlist);
360 if (oldframe) {
361 if (oldframe->is_special) {
362 ast_debug(1, "%s attempted to pop special return location.\n", app_pop);
363
364 /* Abort the special routine dialplan execution. Dialplan programming error. */
365 res = -1;
366 } else {
367 AST_LIST_REMOVE_HEAD(oldlist, entries);
368 gosub_release_frame(chan, oldframe);
369 }
370 } else {
371 ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
372 }
373 AST_LIST_UNLOCK(oldlist);
374 ast_channel_unlock(chan);
375 return res;
376}
377
378static int return_exec(struct ast_channel *chan, const char *data)
379{
380 struct ast_datastore *stack_store;
381 struct gosub_stack_frame *oldframe;
382 struct gosub_stack_list *oldlist;
383 const char *retval = data;
384 int res = 0;
385
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);
390 return -1;
391 }
392
393 oldlist = stack_store->data;
394 AST_LIST_LOCK(oldlist);
395 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
396 AST_LIST_UNLOCK(oldlist);
397
398 if (!oldframe) {
399 ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
400 ast_channel_unlock(chan);
401 return -1;
402 }
403 if (oldframe->is_special) {
404 /* Exit from special routine. */
405 res = -1;
406 }
407
408 /*
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.
412 */
413 ast_channel_context_set(chan, oldframe->context);
414 ast_channel_exten_set(chan, oldframe->extension);
416 --oldframe->priority;
417 }
418 ast_channel_priority_set(chan, oldframe->priority);
420
421 gosub_release_frame(chan, oldframe);
422
423 /* Set a return value, if any */
424 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
425 ast_channel_unlock(chan);
426 return res;
427}
428
429/*!
430 * \internal
431 * \brief Add missing context and/or exten to Gosub application argument string.
432 * \since 11.0
433 *
434 * \param chan Channel to obtain context/exten.
435 * \param args Gosub application argument string.
436 *
437 * \details
438 * Fills in the optional context and exten from the given channel.
439 * Convert: [[context,]exten,]priority[(arg1[,...][,argN])]
440 * To: context,exten,priority[(arg1[,...][,argN])]
441 *
442 * \retval expanded Gosub argument string on success. Must be freed.
443 * \retval NULL on error.
444 *
445 * \note The parsing needs to be kept in sync with the
446 * gosub_exec() argument format.
447 */
448static const char *expand_gosub_args(struct ast_channel *chan, const char *args)
449{
450 int len;
451 char *parse;
452 char *label;
453 char *new_args;
454 const char *context;
455 const char *exten;
456 const char *pri;
457
458 /* Separate the context,exten,pri from the optional routine arguments. */
459 parse = ast_strdupa(args);
460 label = strsep(&parse, "(");
461 if (parse) {
462 char *endparen;
463
464 endparen = strrchr(parse, ')');
465 if (endparen) {
466 *endparen = '\0';
467 } else {
468 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", args);
469 }
470 }
471
472 /* Split context,exten,pri */
473 context = strsep(&label, ",");
474 exten = strsep(&label, ",");
475 pri = strsep(&label, ",");
476 if (!exten) {
477 /* Only a priority in this one */
478 pri = context;
479 exten = NULL;
480 context = NULL;
481 } else if (!pri) {
482 /* Only an extension and priority in this one */
483 pri = exten;
484 exten = context;
485 context = NULL;
486 }
487
488 ast_channel_lock(chan);
489 if (ast_strlen_zero(exten)) {
490 exten = ast_channel_exten(chan);
491 }
494 }
495 len = strlen(context) + strlen(exten) + strlen(pri) + 3;
496 if (!ast_strlen_zero(parse)) {
497 len += 2 + strlen(parse);
498 }
499 new_args = ast_malloc(len);
500 if (new_args) {
501 if (ast_strlen_zero(parse)) {
502 snprintf(new_args, len, "%s,%s,%s", context, exten, pri);
503 } else {
504 snprintf(new_args, len, "%s,%s,%s(%s)", context, exten, pri, parse);
505 }
506 }
507 ast_channel_unlock(chan);
508
509 ast_debug(4, "Gosub args:%s new_args:%s\n", args, new_args ? new_args : "");
510
511 return new_args;
512}
513
514static int gosub_exec(struct ast_channel *chan, const char *data)
515{
516 struct ast_datastore *stack_store;
517 struct gosub_stack_list *oldlist;
518 struct gosub_stack_frame *newframe;
519 struct gosub_stack_frame *lastframe;
520 char argname[15];
521 char *parse;
522 char *label;
523 char *caller_id;
524 char *orig_context;
525 char *orig_exten;
526 char *dest_context;
527 char *dest_exten;
528 int orig_in_subroutine;
529 int orig_priority;
530 int dest_priority;
531 int i;
532 int max_argc = 0;
534 AST_APP_ARG(argval)[100];
535 );
536
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);
539 return -1;
540 }
541
542 /*
543 * Separate the arguments from the label
544 *
545 * NOTE: You cannot use ast_app_separate_args for this, because
546 * '(' cannot be used as a delimiter.
547 */
548 parse = ast_strdupa(data);
549 label = strsep(&parse, "(");
550 if (parse) {
551 char *endparen;
552
553 endparen = strrchr(parse, ')');
554 if (endparen) {
555 *endparen = '\0';
556 } else {
557 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", data);
558 }
559 AST_STANDARD_RAW_ARGS(args2, parse);
560 } else {
561 args2.argc = 0;
562 }
563
564 ast_channel_lock(chan);
565 orig_context = ast_strdupa(ast_channel_context(chan));
567 orig_priority = ast_channel_priority(chan);
568 orig_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
569 ast_channel_unlock(chan);
570
571 if (ast_parseable_goto(chan, label)) {
572 ast_log(LOG_ERROR, "%s address is invalid: '%s'\n", app_gosub, data);
573 goto error_exit;
574 }
575
576 ast_channel_lock(chan);
577 dest_context = ast_strdupa(ast_channel_context(chan));
578 dest_exten = ast_strdupa(ast_channel_exten(chan));
579 dest_priority = ast_channel_priority(chan);
581 ++dest_priority;
582 }
583 caller_id = S_COR(ast_channel_caller(chan)->id.number.valid,
584 ast_channel_caller(chan)->id.number.str, NULL);
585 if (caller_id) {
586 caller_id = ast_strdupa(caller_id);
587 }
588 ast_channel_unlock(chan);
589
590 if (!ast_exists_extension(chan, dest_context, dest_exten, dest_priority, caller_id)) {
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);
593 goto error_exit;
594 }
595
596 /* Now we know that we're going to a new location */
597
598 ast_channel_lock(chan);
599
600 /* Find stack datastore return list. */
601 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
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);
605 if (!stack_store) {
606 ast_log(LOG_ERROR, "Unable to allocate new datastore. %s failed.\n",
607 app_gosub);
608 goto error_exit_locked;
609 }
610
611 oldlist = ast_calloc(1, sizeof(*oldlist));
612 if (!oldlist) {
613 ast_log(LOG_ERROR, "Unable to allocate datastore list head. %s failed.\n",
614 app_gosub);
615 ast_datastore_free(stack_store);
616 goto error_exit_locked;
617 }
618 AST_LIST_HEAD_INIT(oldlist);
619
620 stack_store->data = oldlist;
621 ast_channel_datastore_add(chan, stack_store);
622 } else {
623 oldlist = stack_store->data;
624 }
625
626 if ((lastframe = AST_LIST_FIRST(oldlist))) {
627 max_argc = lastframe->arguments;
628 }
629
630 /* Mask out previous Gosub arguments in this invocation */
631 if (args2.argc > max_argc) {
632 max_argc = args2.argc;
633 }
634
635 /* Create the return address */
636 newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, orig_in_subroutine, max_argc);
637 if (!newframe) {
638 goto error_exit_locked;
639 }
640
641 /* Set our arguments */
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] : "");
646 }
647 snprintf(argname, sizeof(argname), "%u", args2.argc);
648 frame_set_var(chan, newframe, "ARGC", argname);
649
651
652 /* And finally, save our return address */
653 AST_LIST_LOCK(oldlist);
654 AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
655 AST_LIST_UNLOCK(oldlist);
656 ast_channel_unlock(chan);
657
658 return 0;
659
660error_exit:
661 ast_channel_lock(chan);
662
663error_exit_locked:
664 /* Restore the original dialplan location. */
665 ast_channel_context_set(chan, orig_context);
667 ast_channel_priority_set(chan, orig_priority);
668 ast_channel_unlock(chan);
669 return -1;
670}
671
672static int gosubif_exec(struct ast_channel *chan, const char *data)
673{
674 char *args;
675 int res=0;
677 AST_APP_ARG(ition);
678 AST_APP_ARG(labels);
679 );
681 AST_APP_ARG(iftrue);
682 AST_APP_ARG(iffalse);
683 );
684
685 if (ast_strlen_zero(data)) {
686 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
687 return 0;
688 }
689
690 args = ast_strdupa(data);
692 if (cond.argc != 2) {
693 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
694 return 0;
695 }
696
697 AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
698
699 if (pbx_checkcondition(cond.ition)) {
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);
704 }
705
706 return res;
707}
708
709static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
710{
711 struct ast_datastore *stack_store;
712 struct gosub_stack_list *oldlist;
713 struct gosub_stack_frame *frame;
714 struct ast_var_t *variables;
715
716 if (!chan) {
717 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
718 return -1;
719 }
720
721 ast_channel_lock(chan);
722 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
723 ast_channel_unlock(chan);
724 return -1;
725 }
726
727 oldlist = stack_store->data;
728 AST_LIST_LOCK(oldlist);
729 if (!(frame = AST_LIST_FIRST(oldlist))) {
730 /* Not within a Gosub routine */
731 AST_LIST_UNLOCK(oldlist);
732 ast_channel_unlock(chan);
733 return -1;
734 }
735
736 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
737 if (!strcmp(data, ast_var_name(variables))) {
738 const char *tmp;
739 tmp = pbx_builtin_getvar_helper(chan, data);
741 break;
742 }
743 }
744 AST_LIST_UNLOCK(oldlist);
745 ast_channel_unlock(chan);
746 return 0;
747}
748
749static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
750{
751 struct ast_datastore *stack_store;
752 struct gosub_stack_list *oldlist;
753 struct gosub_stack_frame *frame;
754
755 if (!chan) {
756 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
757 return -1;
758 }
759
760 ast_channel_lock(chan);
761 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
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);
764 return -1;
765 }
766
767 oldlist = stack_store->data;
768 AST_LIST_LOCK(oldlist);
769 frame = AST_LIST_FIRST(oldlist);
770
771 if (frame) {
772 frame_set_var(chan, frame, var, value);
773 }
774
775 AST_LIST_UNLOCK(oldlist);
776 ast_channel_unlock(chan);
777
778 return 0;
779}
780
782 .name = "LOCAL",
783 .write = local_write,
784 .read = local_read,
785};
786
787static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
788{
789 int found = 0, n;
790 struct ast_var_t *variables;
792 AST_APP_ARG(n);
794 );
795
796 if (!chan) {
797 ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
798 return -1;
799 }
800
802
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");
805 return -1;
806 }
807
808 n = atoi(args.n);
809 *buf = '\0';
810
811 ast_channel_lock(chan);
813 if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
814 ast_copy_string(buf, ast_var_value(variables), len);
815 break;
816 }
817 }
818 ast_channel_unlock(chan);
819 return 0;
820}
821
823 .name = "LOCAL_PEEK",
824 .read = peek_read,
825};
826
827static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
828{
829 struct ast_datastore *stack_store;
830 struct gosub_stack_list *oldlist;
831 struct gosub_stack_frame *frame;
832 int n;
834 AST_APP_ARG(n);
835 AST_APP_ARG(which);
836 AST_APP_ARG(suppress);
837 );
838
839 if (!chan) {
840 ast_log(LOG_ERROR, "STACK_PEEK must be called on an active channel\n");
841 return -1;
842 }
843
844 data = ast_strdupa(data);
846
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");
849 return -1;
850 }
851
852 n = atoi(args.n);
853 if (n <= 0) {
854 ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
855 return -1;
856 }
857
858 ast_channel_lock(chan);
859 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
860 if (!ast_true(args.suppress)) {
861 ast_log(LOG_ERROR, "STACK_PEEK called on a channel without a gosub stack\n");
862 }
863 ast_channel_unlock(chan);
864 return -1;
865 }
866
867 oldlist = stack_store->data;
868
869 AST_LIST_LOCK(oldlist);
870 AST_LIST_TRAVERSE(oldlist, frame, entries) {
871 if (--n == 0) {
872 break;
873 }
874 }
875
876 if (!frame) {
877 /* Too deep */
878 if (!ast_true(args.suppress)) {
879 ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n);
880 }
881 AST_LIST_UNLOCK(oldlist);
882 ast_channel_unlock(chan);
883 return -1;
884 }
885
886 args.which = ast_skip_blanks(args.which);
887
888 switch (args.which[0]) {
889 case 'l': /* label */
890 ast_str_set(str, len, "%s,%s,%d", frame->context, frame->extension, frame->priority - 1);
891 break;
892 case 'c': /* context */
893 ast_str_set(str, len, "%s", frame->context);
894 break;
895 case 'e': /* extension */
896 ast_str_set(str, len, "%s", frame->extension);
897 break;
898 case 'p': /* priority */
899 ast_str_set(str, len, "%d", frame->priority - 1);
900 break;
901 default:
902 ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
903 break;
904 }
905
906 AST_LIST_UNLOCK(oldlist);
907 ast_channel_unlock(chan);
908
909 return 0;
910}
911
913 .name = "STACK_PEEK",
914 .read2 = stackpeek_read,
915};
916
917/*!
918 * \internal
919 * \brief Pop stack frames until remove a special return location.
920 * \since 11.0
921 *
922 * \param chan Channel to balance stack on.
923 *
924 * \note The channel is already locked when called.
925 */
926static void balance_stack(struct ast_channel *chan)
927{
928 struct ast_datastore *stack_store;
929 struct gosub_stack_list *oldlist;
930 struct gosub_stack_frame *oldframe;
931 int found;
932
933 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
934 if (!stack_store) {
935 ast_log(LOG_WARNING, "No %s stack allocated.\n", app_gosub);
936 return;
937 }
938
939 oldlist = stack_store->data;
940 AST_LIST_LOCK(oldlist);
941 do {
942 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
943 if (!oldframe) {
944 break;
945 }
946 found = oldframe->is_special;
947 gosub_release_frame(chan, oldframe);
948 } while (!found);
949 AST_LIST_UNLOCK(oldlist);
950}
951
952/*!
953 * \internal
954 * \brief Run a subroutine on a channel.
955 * \since 11.0
956 *
957 * \note Absolutely _NO_ channel locks should be held before calling this function.
958 *
959 * \param chan Channel to execute subroutine on.
960 * \param sub_args Gosub application argument string.
961 * \param ignore_hangup TRUE if a hangup does not stop execution of the routine.
962 *
963 * \retval 0 success
964 * \retval -1 on error
965 */
966static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_hangup)
967{
968 const char *saved_context;
969 const char *saved_exten;
970 int saved_priority;
971 int saved_hangup_flags;
972 int saved_autoloopflag;
973 int saved_in_subroutine;
974 int res;
975
976 ast_channel_lock(chan);
977
978 ast_verb(3, "%s Internal %s(%s) start\n",
979 ast_channel_name(chan), app_gosub, sub_args);
980
981 /* Save non-hangup softhangup flags. */
982 saved_hangup_flags = ast_channel_softhangup_internal_flag(chan)
984 if (saved_hangup_flags) {
986 }
987
988 /* Save autoloop flag */
989 saved_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
991
992 /* Save current dialplan location */
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);
996
997 /* Save whether or not we are in a subroutine */
998 saved_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
999
1000 ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
1001 saved_context, saved_exten, saved_priority);
1002
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);
1007 if (!res) {
1008 struct ast_datastore *stack_store;
1009
1010 /* Mark the return location as special. */
1011 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
1012 if (!stack_store) {
1013 /* Should never happen! */
1014 ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
1015 res = -1;
1016 } else {
1017 struct gosub_stack_list *oldlist;
1018 struct gosub_stack_frame *cur;
1019
1020 oldlist = stack_store->data;
1021 cur = AST_LIST_FIRST(oldlist);
1022 cur->is_special = 1;
1023 }
1024 }
1025 if (!res) {
1026 int found = 0; /* set if we find at least one match */
1027
1028 /*
1029 * Run gosub body autoloop.
1030 *
1031 * Note that this loop is inverted from the normal execution
1032 * loop because we just executed the Gosub application as the
1033 * first extension of the autoloop.
1034 */
1035 do {
1036 /* Check for hangup. */
1037 if (ast_check_hangup(chan)) {
1039 ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n",
1040 ast_channel_name(chan));
1041 break;
1042 }
1043 if (!ignore_hangup) {
1044 break;
1045 }
1046 }
1047
1048 /* Next dialplan priority. */
1050
1051 ast_channel_unlock(chan);
1052 res = ast_spawn_extension(chan, ast_channel_context(chan),
1054 S_COR(ast_channel_caller(chan)->id.number.valid,
1055 ast_channel_caller(chan)->id.number.str, NULL),
1056 &found, 1);
1057 ast_channel_lock(chan);
1058 } while (!res);
1059 if (found && res) {
1060 /* Something bad happened, or a hangup has been requested. */
1061 ast_debug(1, "Spawn extension (%s,%s,%d) exited with %d on '%s'\n",
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",
1067 }
1068
1069 /* Did the routine return? */
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,
1075 S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
1076 } else {
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);
1080 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
1081 }
1082
1083 /* We executed the requested subroutine to the best of our ability. */
1084 res = 0;
1085 }
1086
1087 ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
1089 ast_channel_priority(chan));
1090
1091 /* Restore dialplan location */
1093 ast_channel_context_set(chan, saved_context);
1094 ast_channel_exten_set(chan, saved_exten);
1095 ast_channel_priority_set(chan, saved_priority);
1096 }
1097
1098 /* Restore autoloop flag */
1099 ast_set2_flag(ast_channel_flags(chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
1100
1101 /* Restore subroutine flag */
1102 ast_set2_flag(ast_channel_flags(chan), saved_in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
1103
1104 /* Restore non-hangup softhangup flags. */
1105 if (saved_hangup_flags) {
1106 ast_softhangup_nolock(chan, saved_hangup_flags);
1107 }
1108
1109 ast_channel_unlock(chan);
1110
1111 return res;
1112}
1113
1114static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
1115{
1116 int res;
1117 int priority;
1118 int old_autoloopflag;
1119 int old_in_subroutine;
1120 int old_priority;
1121 const char *old_context;
1122 const char *old_extension;
1123 char *gosub_args;
1124
1125 if (argc < 4 || argc > 5) {
1126 return RESULT_SHOWUSAGE;
1127 }
1128
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] : "");
1130
1131 if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
1132 /* Lookup the priority label */
1133 priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
1134 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
1135 if (priority < 0) {
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;
1139 }
1140 } else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
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;
1144 }
1145
1146 if (argc == 5) {
1147 if (ast_asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) {
1148 gosub_args = NULL;
1149 }
1150 } else {
1151 if (ast_asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) {
1152 gosub_args = NULL;
1153 }
1154 }
1155 if (!gosub_args) {
1156 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
1157 return RESULT_FAILURE;
1158 }
1159
1160 ast_channel_lock(chan);
1161
1162 ast_verb(3, "%s AGI %s(%s) start\n", ast_channel_name(chan), app_gosub, gosub_args);
1163
1164 /* Save autoloop flag */
1165 old_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
1167
1168 /* Save subroutine flag */
1169 old_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
1170
1171 /* Save previous location, since we're going to change it */
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);
1175
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);
1179
1180 res = gosub_exec(chan, gosub_args);
1181 if (!res) {
1182 struct ast_datastore *stack_store;
1183
1184 /* Mark the return location as special. */
1185 ast_channel_lock(chan);
1186 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
1187 if (!stack_store) {
1188 /* Should never happen! */
1189 ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
1190 res = -1;
1191 } else {
1192 struct gosub_stack_list *oldlist;
1193 struct gosub_stack_frame *cur;
1194
1195 oldlist = stack_store->data;
1196 cur = AST_LIST_FIRST(oldlist);
1197 cur->is_special = 1;
1198 }
1199 ast_channel_unlock(chan);
1200 }
1201 if (!res) {
1202 struct ast_pbx *pbx;
1203 struct ast_pbx_args args;
1204 int abnormal_exit;
1205
1206 memset(&args, 0, sizeof(args));
1207 args.no_hangup_chan = 1;
1208
1209 ast_channel_lock(chan);
1210
1211 /* Next dialplan priority. */
1213
1214 /* Suppress warning about PBX already existing */
1215 pbx = ast_channel_pbx(chan);
1217 ast_channel_unlock(chan);
1218
1219 ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
1220 ast_pbx_run_args(chan, &args);
1221
1222 ast_channel_lock(chan);
1224 ast_channel_pbx_set(chan, pbx);
1225
1226 /* Did the routine return? */
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,
1232 S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
1233 abnormal_exit = 0;
1234 } else {
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);
1238 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
1239 abnormal_exit = 1;
1240 }
1241 ast_channel_unlock(chan);
1242
1243 ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete%s\n",
1244 abnormal_exit ? " (abnormal exit)" : "");
1245 } else {
1246 ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
1247 }
1248
1249 ast_free(gosub_args);
1250
1251 ast_channel_lock(chan);
1252 ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
1254 ast_channel_priority(chan));
1255
1256 /* Restore previous location */
1257 ast_channel_context_set(chan, old_context);
1258 ast_channel_exten_set(chan, old_extension);
1259 ast_channel_priority_set(chan, old_priority);
1260
1261 /* Restore autoloop flag */
1262 ast_set2_flag(ast_channel_flags(chan), old_autoloopflag, AST_FLAG_IN_AUTOLOOP);
1263
1264 /* Restore subroutine flag */
1265 ast_set2_flag(ast_channel_flags(chan), old_in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
1266 ast_channel_unlock(chan);
1267
1268 return RESULT_SUCCESS;
1269}
1270
1272 { { "gosub", NULL }, handle_gosub, NULL, NULL, 0 };
1273
1274static int unload_module(void)
1275{
1277
1279
1287
1288 return 0;
1289}
1290
1291static int load_module(void)
1292{
1293 /* Setup the stack application callback functions. */
1294 static struct ast_app_stack_funcs funcs = {
1295 .run_sub = gosub_run,
1296 .expand_sub_args = expand_gosub_args,
1297 };
1298
1300
1308
1309 funcs.module = ast_module_info->self,
1311
1312 return 0;
1313}
1314
1315AST_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,
1319 .load_pri = AST_MODPRI_APP_DEPEND,
1320 .optional_modules = "res_agi",
AGI Extension interfaces - Asterisk Gateway Interface.
int ast_agi_send(int fd, struct ast_channel *chan, char *fmt,...)
Sends a string of text to an application connected via AGI.
Definition: res_agi.c:1481
int ast_agi_register(struct ast_module *mod, agi_command *cmd)
Registers an AGI command.
Definition: res_agi.c:3815
int ast_agi_unregister(agi_command *cmd)
Unregisters an AGI command.
Definition: res_agi.c:3859
@ ignore_hangup
const char * str
Definition: app_jack.c:147
ast_cond_t cond
Definition: app_sla.c:330
static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
Definition: app_stack.c:292
static int gosubif_exec(struct ast_channel *chan, const char *data)
Definition: app_stack.c:672
static struct ast_custom_function local_function
Definition: app_stack.c:781
static int gosub_exec(struct ast_channel *chan, const char *data)
Definition: app_stack.c:514
static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char *const *argv)
Definition: app_stack.c:1114
static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_hangup)
Definition: app_stack.c:966
static const char * expand_gosub_args(struct ast_channel *chan, const char *args)
Definition: app_stack.c:448
static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
Definition: app_stack.c:827
static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
Definition: app_stack.c:749
static struct agi_command gosub_agi_command
Definition: app_stack.c:1271
static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: app_stack.c:709
static int return_exec(struct ast_channel *chan, const char *data)
Definition: app_stack.c:378
static void gosub_free(void *data)
Definition: app_stack.c:329
static const char app_return[]
Definition: app_stack.c:232
static const char app_gosub[]
Definition: app_stack.c:230
static struct ast_custom_function peek_function
Definition: app_stack.c:822
static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
Definition: app_stack.c:258
static struct gosub_stack_frame * gosub_allocate_frame(const char *context, const char *extension, int priority, int in_subroutine, unsigned char arguments)
Definition: app_stack.c:311
static void balance_stack(struct ast_channel *chan)
Definition: app_stack.c:926
static const struct ast_datastore_info stack_info
Definition: app_stack.c:237
static const char app_gosubif[]
Definition: app_stack.c:231
static const char app_pop[]
Definition: app_stack.c:233
static int load_module(void)
Definition: app_stack.c:1291
static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: app_stack.c:787
static int unload_module(void)
Definition: app_stack.c:1274
static struct ast_custom_function stackpeek_function
Definition: app_stack.c:912
static int pop_exec(struct ast_channel *chan, const char *data)
Definition: app_stack.c:343
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
static int tmp()
Definition: bt_open.c:389
static int priority
General Asterisk PBX channel definitions.
void ast_channel_exten_set(struct ast_channel *chan, const char *value)
const char * ast_channel_name(const struct ast_channel *chan)
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
void ast_channel_clear_softhangup(struct ast_channel *chan, int flag)
Clear a set of softhangup flags from a channel.
Definition: channel.c:2432
struct varshead * ast_channel_varshead(struct ast_channel *chan)
#define ast_channel_lock(chan)
Definition: channel.h:2922
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
int ast_channel_priority(const struct ast_channel *chan)
const char * ast_channel_context(const struct ast_channel *chan)
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
@ AST_SOFTHANGUP_ASYNCGOTO
Definition: channel.h:1126
void ast_channel_context_set(struct ast_channel *chan, const char *value)
int ast_softhangup_nolock(struct ast_channel *chan, int cause)
Softly hangup up a channel (no channel lock)
Definition: channel.c:2458
@ AST_FLAG_IN_AUTOLOOP
Definition: channel.h:997
@ AST_FLAG_SUBROUTINE_EXEC
Definition: channel.h:1058
struct ast_pbx * ast_channel_pbx(const struct ast_channel *chan)
int ast_channel_softhangup_internal_flag(struct ast_channel *chan)
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
void ast_channel_priority_set(struct ast_channel *chan, int value)
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2923
void ast_channel_pbx_set(struct ast_channel *chan, struct ast_pbx *value)
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.
Definition: channel.c:2399
const char * ast_var_name(const struct ast_var_t *var)
Definition: chanvars.c:60
#define ast_var_assign(name, value)
Definition: chanvars.h:40
const char * ast_var_value(const struct ast_var_t *var)
Definition: chanvars.c:80
void ast_var_delete(struct ast_var_t *var)
Definition: extconf.c:2471
#define RESULT_SHOWUSAGE
Definition: cli.h:41
#define RESULT_SUCCESS
Definition: cli.h:40
#define RESULT_FAILURE
Definition: cli.h:42
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static const char name[]
Definition: format_mp3.c:68
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_channel_publish_varset(struct ast_channel *chan, const char *variable, const char *value)
Publish a ast_channel_publish_varset for a channel.
static ENTRY retval
Definition: hsearch.c:50
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_STANDARD_RAW_ARGS(args, parse)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
void ast_install_stack_functions(const struct ast_app_stack_funcs *funcs)
Set stack application function callbacks.
Definition: main/app.c:273
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_NONSTANDARD_RAW_ARGS(args, parse, sep)
char * strsep(char **str, const char *delims)
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:653
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
Definition: linkedlists.h:626
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:173
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_DEFAULT
Definition: module.h:329
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_APP_DEPEND
Definition: module.h:342
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Core PBX routines and definitions.
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.
Definition: pbx.c:4180
enum ast_pbx_result ast_pbx_run_args(struct ast_channel *c, struct ast_pbx_args *args)
Execute the PBX in the current thread.
Definition: pbx.c:4735
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4175
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_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
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)
Definition: pbx.c:4200
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
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 pbx_checkcondition(const char *condition)
Evaluate a condition.
Definition: pbx.c:8282
int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
Definition: pbx.c:8866
static char * orig_exten(int fd, const char *chan, const char *data)
orginate from extension
#define NULL
Definition: resample.c:96
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:161
Definition: agi.h:34
int fd
Definition: agi.h:35
Stack applications callback functions.
int(* run_sub)(struct ast_channel *chan, const char *args, int ignore_hangup)
Callback for the routine to run a subroutine on a channel.
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
struct ast_module * self
Definition: module.h:356
Options for ast_pbx_run()
Definition: pbx.h:407
Definition: pbx.h:214
Support for dynamic strings.
Definition: strings.h:623
struct ast_var_t::@211 entries
structure to hold extensions
unsigned char arguments
Definition: app_stack.c:245
char extension[0]
Definition: app_stack.c:253
unsigned int in_subroutine
Definition: app_stack.c:251
struct gosub_stack_frame::@81 entries
struct varshead varshead
Definition: app_stack.c:246
unsigned int is_special
Definition: app_stack.c:249
Number structure.
Definition: app_followme.c:154
int value
Definition: syslog.c:37
const char * args
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_set_flag(p, flag)
Definition: utils.h:70