Wed Jan 8 2020 09:49:40

Asterisk developer's documentation


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