Added a warning to the documentation for Macro in response to bug 7776
[asterisk/asterisk.git] / apps / app_macro.c
index a937621..c35208f 100644 (file)
@@ -50,8 +50,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 /* special result value used to force macro exit */
 #define MACRO_EXIT_RESULT 1024
 
-static char *tdesc = "Extension Macros";
-
 static char *descrip =
 "  Macro(macroname|arg1|arg2...): Executes a macro using the context\n"
 "'macro-<macroname>', jumping to the 's' extension of that context and\n"
@@ -62,7 +60,15 @@ static char *descrip =
 "If you Goto out of the Macro context, the Macro will terminate and control\n"
 "will be returned at the location of the Goto.\n"
 "If ${MACRO_OFFSET} is set at termination, Macro will attempt to continue\n"
-"at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n";
+"at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n"
+"WARNING: Because of the way Macro is implemented (it executes the priorities\n"
+"         contained within it via sub-engine), and a fixed per-thread\n"
+"         memory stack allowance, macros are limited to 7 levels\n"
+"         of nesting (macro calling macro calling macro, etc.); It\n"
+"         may be possible that stack-intensive applications in deeply nested macros\n"
+"         could cause asterisk to crash earlier than this limit. It is advised that\n"
+"         if you need to deeply nest macro calls, that you use the Gosub application\n"
+"         (now allows arguments like a Macro) with explict Return() calls instead.\n";
 
 static char *if_descrip =
 "  MacroIf(<expr>?macroname_a[|arg1][:macroname_b[|arg1]])\n"
@@ -94,7 +100,6 @@ static char *if_synopsis = "Conditional Macro Implementation";
 static char *exclusive_synopsis = "Exclusive Macro Implementation";
 static char *exit_synopsis = "Exit From Macro";
 
-LOCAL_USER_DECL;
 
 static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
 {
@@ -112,7 +117,7 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
        int oldpriority;
        char pc[80], depthc[12];
        char oldcontext[AST_MAX_CONTEXT] = "";
-       int offset, depth = 0;
+       int offset, depth = 0, maxdepth = 7;
        int setmacrocontext=0;
        int autoloopflag, dead = 0;
   
@@ -120,22 +125,27 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
        char *save_macro_context;
        char *save_macro_priority;
        char *save_macro_offset;
-       struct localuser *u;
+       struct ast_module_user *u;
  
        if (ast_strlen_zero(data)) {
                ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n");
                return -1;
        }
 
-       LOCAL_USER_ADD(u);
+       u = ast_module_user_add(chan);
+
+       /* does the user want a deeper rabbit hole? */
+       s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
+       if (s)
+               sscanf(s, "%d", &maxdepth);
 
        /* Count how many levels deep the rabbit hole goes */
        s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
        if (s)
                sscanf(s, "%d", &depth);
-       if (depth >= 7) {
+       if (depth >= maxdepth) {
                ast_log(LOG_ERROR, "Macro():  possible infinite loop detected.  Returning early.\n");
-               LOCAL_USER_REMOVE(u);
+               ast_module_user_remove(u);
                return 0;
        }
        snprintf(depthc, sizeof(depthc), "%d", depth + 1);
@@ -146,7 +156,7 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
        macro = strsep(&rest, "|");
        if (ast_strlen_zero(macro)) {
                ast_log(LOG_WARNING, "Invalid macro name specified\n");
-               LOCAL_USER_REMOVE(u);
+               ast_module_user_remove(u);
                return 0;
        }
 
@@ -156,18 +166,20 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
                        ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
                else
                        ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
-               LOCAL_USER_REMOVE(u);
+               ast_module_user_remove(u);
                return 0;
        }
 
        /* If we are to run the macro exclusively, take the mutex */
        if (exclusive) {
-               ast_log(LOG_DEBUG, "Locking macrolock for '%s'\n", fullmacro);
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Locking macrolock for '%s'\n", fullmacro);
                ast_autoservice_start(chan);
                if (ast_context_lockmacro(fullmacro)) {
                        ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
                        ast_autoservice_stop(chan);
-                       LOCAL_USER_REMOVE(u);
+                       ast_module_user_remove(u);
+
                        return 0;
                }
                ast_autoservice_stop(chan);
@@ -225,7 +237,8 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
                        if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
                        (res == '*') || (res == '#')) {
                                /* Just return result as to the previous application as if it had been dialed */
-                               ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
                                break;
                        }
                        switch(res) {
@@ -254,9 +267,10 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
                        break;
                }
                /* don't stop executing extensions when we're in "h" */
-               if (chan->_softhangup && strcasecmp(oldexten,"h")) {
-                       ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
-                               chan->exten, chan->priority);
+               if (chan->_softhangup && strcasecmp(oldexten,"h") && strcasecmp(chan->macroexten,"h")) {
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
+                                       chan->exten, chan->macroexten, chan->priority);
                        goto out;
                }
                chan->priority++;
@@ -327,37 +341,39 @@ static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
 
        /* Unlock the macro */
        if (exclusive) {
-               ast_log(LOG_DEBUG, "Unlocking macrolock for '%s'\n", fullmacro);
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Unlocking macrolock for '%s'\n", fullmacro);
                if (ast_context_unlockmacro(fullmacro)) {
                        ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
                        res = 0;
                }
        }
        
-       LOCAL_USER_REMOVE(u);
+       ast_module_user_remove(u);
+
        return res;
 }
 
 static int macro_exec(struct ast_channel *chan, void *data)
 {
-       _macro_exec(chan, data, 0);
+       return _macro_exec(chan, data, 0);
 }
 
 static int macroexclusive_exec(struct ast_channel *chan, void *data)
 {
-       _macro_exec(chan, data, 1);
+       return _macro_exec(chan, data, 1);
 }
 
 static int macroif_exec(struct ast_channel *chan, void *data) 
 {
        char *expr = NULL, *label_a = NULL, *label_b = NULL;
        int res = 0;
-       struct localuser *u;
+       struct ast_module_user *u;
 
-       LOCAL_USER_ADD(u);
+       u = ast_module_user_add(chan);
 
        if (!(expr = ast_strdupa(data))) {
-               LOCAL_USER_REMOVE(u);
+               ast_module_user_remove(u);
                return -1;
        }
 
@@ -375,7 +391,7 @@ static int macroif_exec(struct ast_channel *chan, void *data)
        } else
                ast_log(LOG_WARNING, "Invalid Syntax.\n");
 
-       LOCAL_USER_REMOVE(u);
+       ast_module_user_remove(u);
 
        return res;
 }
@@ -385,7 +401,7 @@ static int macro_exit_exec(struct ast_channel *chan, void *data)
        return MACRO_EXIT_RESULT;
 }
 
-static int unload_module(void *mod)
+static int unload_module(void)
 {
        int res;
 
@@ -394,12 +410,12 @@ static int unload_module(void *mod)
        res |= ast_unregister_application(app);
        res |= ast_unregister_application(exclusive_app);
 
-       STANDARD_HANGUP_LOCALUSERS;
+       ast_module_user_hangup_all();
 
        return res;
 }
 
-static int load_module(void *mod)
+static int load_module(void)
 {
        int res;
 
@@ -411,14 +427,4 @@ static int load_module(void *mod)
        return res;
 }
 
-static const char *description(void)
-{
-       return tdesc;
-}
-
-static const char *key(void)
-{
-       return ASTERISK_GPL_KEY;
-}
-
-STD_MOD1;
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");