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