2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Dial plan macro Implementation
23 * \author Mark Spencer <markster@digium.com>
25 * \ingroup applications
29 <defaultenabled>no</defaultenabled>
30 <support_level>deprecated</support_level>
31 <replacement>app_stack (GoSub)</replacement>
36 #include "asterisk/file.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/module.h"
40 #include "asterisk/config.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/lock.h"
43 #include "asterisk/app.h"
46 <application name="Macro" language="en_US">
51 <parameter name="name" required="true">
52 <para>The name of the macro</para>
54 <parameter name="args">
55 <argument name="arg1" required="true" />
56 <argument name="arg2" multiple="true" />
60 <para>Executes a macro using the context macro-<replaceable>name</replaceable>,
61 jumping to the <literal>s</literal> extension of that context and executing each step,
62 then returning when the steps end.</para>
63 <para>The calling extension, context, and priority are stored in <variable>MACRO_EXTEN</variable>,
64 <variable>MACRO_CONTEXT</variable> and <variable>MACRO_PRIORITY</variable> respectively. Arguments
65 become <variable>ARG1</variable>, <variable>ARG2</variable>, etc in the macro context.</para>
66 <para>If you Goto out of the Macro context, the Macro will terminate and control will be returned
67 at the location of the Goto.</para>
68 <para>If <variable>MACRO_OFFSET</variable> is set at termination, Macro will attempt to continue
69 at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.</para>
70 <warning><para>Because of the way Macro is implemented (it executes the priorities contained within
71 it via sub-engine), and a fixed per-thread memory stack allowance, macros are limited to 7 levels
72 of nesting (macro calling macro calling macro, etc.); It may be possible that stack-intensive
73 applications in deeply nested macros could cause asterisk to crash earlier than this limit.
74 It is advised that if you need to deeply nest macro calls, that you use the Gosub application
75 (now allows arguments like a Macro) with explict Return() calls instead.</para></warning>
76 <warning><para>Use of the application <literal>WaitExten</literal> within a macro will not function
77 as expected. Please use the <literal>Read</literal> application in order to read DTMF from a channel
78 currently executing a macro.</para></warning>
81 <ref type="application">MacroExit</ref>
82 <ref type="application">Goto</ref>
83 <ref type="application">Gosub</ref>
86 <application name="MacroIf" language="en_US">
88 Conditional Macro implementation.
91 <parameter name="expr" required="true" />
92 <parameter name="destination" required="true" argsep=":">
93 <argument name="macroiftrue" required="true">
94 <argument name="macroiftrue" required="true" />
95 <argument name="arg1" multiple="true" />
97 <argument name="macroiffalse">
98 <argument name="macroiffalse" required="true" />
99 <argument name="arg1" multiple="true" />
104 <para>Executes macro defined in <replaceable>macroiftrue</replaceable> if
105 <replaceable>expr</replaceable> is true (otherwise <replaceable>macroiffalse</replaceable>
107 <para>Arguments and return values as in application Macro()</para>
108 <xi:include xpointer="xpointer(/docs/application[@name='Macro']/description/warning[2])" />
111 <ref type="application">GotoIf</ref>
112 <ref type="application">GosubIf</ref>
113 <ref type="function">IF</ref>
116 <application name="MacroExclusive" language="en_US">
118 Exclusive Macro Implementation.
121 <parameter name="name" required="true">
122 <para>The name of the macro</para>
124 <parameter name="arg1" />
125 <parameter name="arg2" multiple="true" />
128 <para>Executes macro defined in the context macro-<replaceable>name</replaceable>.
129 Only one call at a time may run the macro. (we'll wait if another call is busy
130 executing in the Macro)</para>
131 <para>Arguments and return values as in application Macro()</para>
132 <xi:include xpointer="xpointer(/docs/application[@name='Macro']/description/warning[2])" />
135 <ref type="application">Macro</ref>
138 <application name="MacroExit" language="en_US">
144 <para>Causes the currently running macro to exit as if it had
145 ended normally by running out of priorities to execute.
146 If used outside a macro, will likely cause unexpected behavior.</para>
149 <ref type="application">Macro</ref>
156 /* special result value used to force macro exit */
157 #define MACRO_EXIT_RESULT 1024
159 static char *app = "Macro";
160 static char *if_app = "MacroIf";
161 static char *exclusive_app = "MacroExclusive";
162 static char *exit_app = "MacroExit";
164 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
166 static const struct ast_datastore_info macro_ds_info = {
168 .chan_fixup = macro_fixup,
171 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
175 pbx_builtin_setvar_helper(new_chan, "MACRO_DEPTH", "0");
176 pbx_builtin_setvar_helper(new_chan, "MACRO_CONTEXT", NULL);
177 pbx_builtin_setvar_helper(new_chan, "MACRO_EXTEN", NULL);
178 pbx_builtin_setvar_helper(new_chan, "MACRO_PRIORITY", NULL);
179 pbx_builtin_setvar_helper(new_chan, "MACRO_OFFSET", NULL);
180 for (i = 1; i < 100; i++) {
181 snprintf(varname, sizeof(varname), "ARG%d", i);
182 while (pbx_builtin_getvar_helper(new_chan, varname)) {
183 /* Kill all levels of arguments */
184 pbx_builtin_setvar_helper(new_chan, varname, NULL);
189 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
192 struct ast_context *c2;
195 for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
196 if (ast_extension_match(ast_get_extension_name(e), exten)) {
197 int needmatch = ast_get_extension_matchcid(e);
198 if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
200 /* This is the matching extension we want */
202 for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
203 if (priority != ast_get_extension_priority(p))
211 /* No match; run through includes */
212 for (idx = 0; idx < ast_context_includes_count(c); idx++) {
213 const struct ast_include *i = ast_context_includes_get(c, idx);
215 for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
216 if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
217 e = find_matching_priority(c2, exten, priority, callerid);
226 static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive)
234 char runningapp[80], runningdata[1024];
235 char *oldargs[MAX_ARGS + 1] = { NULL, };
238 char oldexten[256]="";
239 int oldpriority, gosub_level = 0;
240 char pc[80], depthc[12];
241 char oldcontext[AST_MAX_CONTEXT] = "";
242 const char *inhangupc;
243 int offset, depth = 0, maxdepth = 7;
244 int setmacrocontext=0;
245 int autoloopflag, inhangup = 0;
246 struct ast_str *tmp_subst = NULL;
247 const char *my_macro_exten = NULL;
248 char *save_macro_exten;
249 char *save_macro_context;
250 char *save_macro_priority;
251 char *save_macro_offset;
252 int save_in_subroutine;
253 struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL);
254 static int deprecation_notice = 0;
256 if (ast_strlen_zero(data)) {
257 ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
261 if (!deprecation_notice) {
262 deprecation_notice = 1;
263 ast_log(LOG_WARNING, "Macro() is deprecated and will be removed from a future version of Asterisk.\n");
264 ast_log(LOG_WARNING, "Dialplan should be updated to use Gosub instead.\n");
271 if (!(macro_store = ast_datastore_alloc(¯o_ds_info, NULL))) {
272 ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
275 /* Just the existence of this datastore is enough. */
276 macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
277 ast_channel_datastore_add(chan, macro_store);
280 /* does the user want a deeper rabbit hole? */
281 ast_channel_lock(chan);
282 if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
283 sscanf(s, "%30d", &maxdepth);
286 /* Count how many levels deep the rabbit hole goes */
287 if ((s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH"))) {
288 sscanf(s, "%30d", &depth);
291 /* Used for detecting whether to return when a Macro is called from another Macro after hangup */
292 if (strcmp(ast_channel_exten(chan), "h") == 0)
293 pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
295 if ((inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP"))) {
296 sscanf(inhangupc, "%30d", &inhangup);
298 ast_channel_unlock(chan);
300 if (depth >= maxdepth) {
301 ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
304 snprintf(depthc, sizeof(depthc), "%d", depth + 1);
306 tmp = ast_strdupa(data);
308 macro = strsep(&rest, ",");
309 if (ast_strlen_zero(macro)) {
310 ast_log(LOG_WARNING, "Invalid macro name specified\n");
314 snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
316 /* first search for the macro */
317 if (!ast_context_find(fullmacro)) {
318 ast_log(LOG_WARNING, "No such context '%s' for macro '%s'. Was called by %s@%s\n",
319 fullmacro, macro, ast_channel_exten(chan), ast_channel_context(chan));
323 /* now search for the right extension */
324 if (ast_exists_extension(chan, fullmacro, "s", 1,
325 S_COR(ast_channel_caller(chan)->id.number.valid,
326 ast_channel_caller(chan)->id.number.str, NULL))) {
327 /* We have a normal macro */
328 my_macro_exten = "s";
329 } else if (ast_exists_extension(chan, fullmacro, "~~s~~", 1,
330 S_COR(ast_channel_caller(chan)->id.number.valid,
331 ast_channel_caller(chan)->id.number.str, NULL))) {
332 /* We have an AEL generated macro */
333 my_macro_exten = "~~s~~";
336 /* do we have a valid exten? */
337 if (!my_macro_exten) {
339 "Context '%s' for macro '%s' lacks 's' extension, priority 1\n",
344 /* If we are to run the macro exclusively, take the mutex */
346 ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
347 ast_autoservice_start(chan);
348 if (ast_context_lockmacro(fullmacro)) {
349 ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
350 ast_autoservice_stop(chan);
353 ast_autoservice_stop(chan);
356 if (!(tmp_subst = ast_str_create(16))) {
361 ast_channel_lock(chan);
362 oldpriority = ast_channel_priority(chan);
363 ast_copy_string(oldexten, ast_channel_exten(chan), sizeof(oldexten));
364 ast_copy_string(oldcontext, ast_channel_context(chan), sizeof(oldcontext));
365 if (ast_strlen_zero(ast_channel_macrocontext(chan))) {
366 ast_channel_macrocontext_set(chan, ast_channel_context(chan));
367 ast_channel_macroexten_set(chan, ast_channel_exten(chan));
368 ast_channel_macropriority_set(chan, ast_channel_priority(chan));
372 /* Save old macro variables */
373 save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
374 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
376 save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
377 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
379 save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
380 snprintf(pc, sizeof(pc), "%d", oldpriority);
381 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
383 save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
384 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
386 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
388 save_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
389 ast_set_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
391 /* Setup environment for new run */
392 ast_channel_exten_set(chan, my_macro_exten);
393 ast_channel_context_set(chan, fullmacro);
394 ast_channel_priority_set(chan, 1);
396 while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
398 /* Save copy of old arguments if we're overwriting some, otherwise
399 let them pass through to the other macro */
400 snprintf(varname, sizeof(varname), "ARG%d", argc);
401 if ((argp = pbx_builtin_getvar_helper(chan, varname))) {
402 oldargs[argc] = ast_strdup(argp);
404 pbx_builtin_setvar_helper(chan, varname, cur);
407 autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
408 ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
409 ast_channel_unlock(chan);
411 while (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
412 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
413 struct ast_context *c;
416 runningapp[0] = '\0';
417 runningdata[0] = '\0';
419 /* What application will execute? */
420 if (ast_rdlock_contexts()) {
421 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
423 for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
424 if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) {
425 if (ast_rdlock_context(c)) {
426 ast_log(LOG_WARNING, "Unable to lock context?\n");
428 e = find_matching_priority(c, ast_channel_exten(chan), ast_channel_priority(chan),
429 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
430 if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
431 ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
432 ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
434 ast_unlock_context(c);
440 ast_unlock_contexts();
442 /* Reset the macro depth, if it was changed in the last iteration */
443 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
445 res = ast_spawn_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
446 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
449 /* Something bad happened, or a hangup has been requested. */
450 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
451 (res == '*') || (res == '#')) {
452 /* Just return result as to the previous application as if it had been dialed */
453 ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
457 case MACRO_EXIT_RESULT:
461 ast_debug(2, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan), macro);
462 ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan), macro);
467 ast_debug(1, "Executed application: %s\n", runningapp);
469 if (!strcasecmp(runningapp, "GOSUB")) {
471 ast_debug(1, "Incrementing gosub_level\n");
472 } else if (!strcasecmp(runningapp, "GOSUBIF")) {
473 char *cond, *app_arg;
475 ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
476 app2 = ast_str_buffer(tmp_subst);
477 cond = strsep(&app2, "?");
478 app_arg = strsep(&app2, ":");
479 if (pbx_checkcondition(cond)) {
480 if (!ast_strlen_zero(app_arg)) {
482 ast_debug(1, "Incrementing gosub_level\n");
485 if (!ast_strlen_zero(app2)) {
487 ast_debug(1, "Incrementing gosub_level\n");
490 } else if (!strcasecmp(runningapp, "RETURN")) {
492 ast_debug(1, "Decrementing gosub_level\n");
493 } else if (!strcasecmp(runningapp, "STACKPOP")) {
495 ast_debug(1, "Decrementing gosub_level\n");
496 } else if (!strncasecmp(runningapp, "EXEC", 4)) {
497 /* Must evaluate args to find actual app */
498 char *tmp2, *tmp3 = NULL;
499 ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
500 tmp2 = ast_str_buffer(tmp_subst);
501 if (!strcasecmp(runningapp, "EXECIF")) {
502 if ((tmp3 = strchr(tmp2, '|'))) {
505 if (!pbx_checkcondition(tmp2)) {
513 ast_debug(1, "Last app: %s\n", tmp3);
516 if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
518 ast_debug(1, "Incrementing gosub_level\n");
519 } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
521 ast_debug(1, "Decrementing gosub_level\n");
522 } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
524 ast_debug(1, "Decrementing gosub_level\n");
528 if (gosub_level == 0 && strcasecmp(ast_channel_context(chan), fullmacro)) {
529 ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", ast_channel_name(chan), macro);
533 /* don't stop executing extensions when we're in "h" */
534 if (ast_check_hangup(chan) && !inhangup) {
535 ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
536 ast_channel_exten(chan),
537 ast_channel_macroexten(chan),
538 ast_channel_priority(chan));
541 ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
545 /* Don't let the channel change now. */
546 ast_channel_lock(chan);
548 /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
549 snprintf(depthc, sizeof(depthc), "%d", depth);
550 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
551 ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP);
552 ast_set2_flag(ast_channel_flags(chan), save_in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
554 for (x = 1; x < argc; x++) {
555 /* Restore old arguments and delete ours */
556 snprintf(varname, sizeof(varname), "ARG%d", x);
557 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
558 ast_free(oldargs[x]);
561 /* Restore macro variables */
562 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
563 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
564 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
565 ast_free(save_macro_exten);
566 ast_free(save_macro_context);
567 ast_free(save_macro_priority);
569 if (setmacrocontext) {
570 ast_channel_macrocontext_set(chan, "");
571 ast_channel_macroexten_set(chan, "");
572 ast_channel_macropriority_set(chan, 0);
575 if (!strcasecmp(ast_channel_context(chan), fullmacro)
576 && !(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
579 /* If we're leaving the macro normally, restore original information */
580 ast_channel_priority_set(chan, oldpriority);
581 ast_channel_context_set(chan, oldcontext);
582 ast_channel_exten_set(chan, oldexten);
583 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
584 /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
585 normally if there is any problem */
586 if (sscanf(offsets, "%30d", &offset) == 1) {
587 if (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan),
588 ast_channel_priority(chan) + offset + 1,
589 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
590 ast_channel_priority_set(chan, ast_channel_priority(chan) + offset);
596 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
597 ast_free(save_macro_offset);
599 /* Unlock the macro */
601 ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
602 if (ast_context_unlockmacro(fullmacro)) {
603 ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
607 ast_channel_unlock(chan);
613 static int macro_exec(struct ast_channel *chan, const char *data)
615 return _macro_exec(chan, data, 0);
618 static int macroexclusive_exec(struct ast_channel *chan, const char *data)
620 return _macro_exec(chan, data, 1);
623 static int macroif_exec(struct ast_channel *chan, const char *data)
625 char *expr = NULL, *label_a = NULL, *label_b = NULL;
628 expr = ast_strdupa(data);
630 if ((label_a = strchr(expr, '?'))) {
633 if ((label_b = strchr(label_a, ':'))) {
637 if (pbx_checkcondition(expr))
638 res = macro_exec(chan, label_a);
640 res = macro_exec(chan, label_b);
642 ast_log(LOG_WARNING, "Invalid Syntax.\n");
647 static int macro_exit_exec(struct ast_channel *chan, const char *data)
649 return MACRO_EXIT_RESULT;
652 static int unload_module(void)
656 res = ast_unregister_application(if_app);
657 res |= ast_unregister_application(exit_app);
658 res |= ast_unregister_application(app);
659 res |= ast_unregister_application(exclusive_app);
664 static int load_module(void)
668 res = ast_register_application_xml(exit_app, macro_exit_exec);
669 res |= ast_register_application_xml(if_app, macroif_exec);
670 res |= ast_register_application_xml(exclusive_app, macroexclusive_exec);
671 res |= ast_register_application_xml(app, macro_exec);
676 AST_MODULE_INFO_STANDARD_DEPRECATED(ASTERISK_GPL_KEY, "Extension Macros");