[asterisk-commits] rmudgett: branch certified-1.8.28 r419678 - in /certified/branches/1.8.28: ./...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jul 25 18:47:13 CDT 2014


Author: rmudgett
Date: Fri Jul 25 18:47:09 2014
New Revision: 419678

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=419678
Log:
features.c: Allow appliationmap to use Gosub.

Using DYNAMIC_FEATURES with a Gosub application as the mapped application
does not work.  It does not work because Gosub just pushes the current
dialplan context, exten, and priority onto a stack and sets the specified
Gosub location.  Gosub does not have a dialplan execution loop to run
dialplan like Macro.

* Made the DYNAMIC_FEATURES application mapping feature call
ast_app_exec_macro() and ast_app_exec_sub() for the Macro and Gosub
applications respectively.

* Backported ast_app_exec_macro() and ast_app_exec_sub() from v11 to
execute dialplan routines from the DYNAMIC_FEATURES application mapping
feature.

NOTE: This issue does not affect v12+ because it already does what this
patch implements.

AST-1391 #close
Reported by: Guenther Kelleter

Review: https://reviewboard.asterisk.org/r/3844/
........

Merged revisions 419630 from http://svn.asterisk.org/svn/asterisk/branches/1.8

Modified:
    certified/branches/1.8.28/   (props changed)
    certified/branches/1.8.28/apps/app_stack.c
    certified/branches/1.8.28/include/asterisk/app.h
    certified/branches/1.8.28/main/app.c
    certified/branches/1.8.28/main/features.c

Propchange: certified/branches/1.8.28/
------------------------------------------------------------------------------
--- branch-1.8-merged (original)
+++ branch-1.8-merged Fri Jul 25 18:47:09 2014
@@ -1,1 +1,1 @@
-/branches/1.8:1-415260,415841,416066
+/branches/1.8:1-415260,415841,416066,419630

Modified: certified/branches/1.8.28/apps/app_stack.c
URL: http://svnview.digium.com/svn/asterisk/certified/branches/1.8.28/apps/app_stack.c?view=diff&rev=419678&r1=419677&r2=419678
==============================================================================
--- certified/branches/1.8.28/apps/app_stack.c (original)
+++ certified/branches/1.8.28/apps/app_stack.c Fri Jul 25 18:47:09 2014
@@ -26,7 +26,7 @@
  */
 
 /*** MODULEINFO
-	<use>res_agi</use>
+	<use type="module">res_agi</use>
 	<support_level>core</support_level>
  ***/
 
@@ -205,10 +205,10 @@
 	</agi>
  ***/
 
-static const char * const app_gosub = "Gosub";
-static const char * const app_gosubif = "GosubIf";
-static const char * const app_return = "Return";
-static const char * const app_pop = "StackPop";
+static const char app_gosub[] = "Gosub";
+static const char app_gosubif[] = "GosubIf";
+static const char app_return[] = "Return";
+static const char app_pop[] = "StackPop";
 
 static void gosub_free(void *data);
 
@@ -223,10 +223,13 @@
 	unsigned char arguments;
 	struct varshead varshead;
 	int priority;
-	unsigned int is_agi:1;
+	/*! TRUE if the return location marks the end of a special routine. */
+	unsigned int is_special:1;
 	char *context;
 	char extension[0];
 };
+
+AST_LIST_HEAD(gosub_stack_list, gosub_stack_frame);
 
 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
 {
@@ -296,8 +299,9 @@
 
 static void gosub_free(void *data)
 {
-	AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
+	struct gosub_stack_list *oldlist = data;
 	struct gosub_stack_frame *oldframe;
+
 	AST_LIST_LOCK(oldlist);
 	while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
 		gosub_release_frame(NULL, oldframe);
@@ -311,7 +315,8 @@
 {
 	struct ast_datastore *stack_store;
 	struct gosub_stack_frame *oldframe;
-	AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+	struct gosub_stack_list *oldlist;
+	int res = 0;
 
 	ast_channel_lock(chan);
 	if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
@@ -322,23 +327,30 @@
 
 	oldlist = stack_store->data;
 	AST_LIST_LOCK(oldlist);
-	oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
-	AST_LIST_UNLOCK(oldlist);
-
+	oldframe = AST_LIST_FIRST(oldlist);
 	if (oldframe) {
-		gosub_release_frame(chan, oldframe);
+		if (oldframe->is_special) {
+			ast_debug(1, "%s attempted to pop special return location.\n", app_pop);
+
+			/* Abort the special routine dialplan execution.  Dialplan programming error. */
+			res = -1;
+		} else {
+			AST_LIST_REMOVE_HEAD(oldlist, entries);
+			gosub_release_frame(chan, oldframe);
+		}
 	} else {
 		ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
 	}
-	ast_channel_unlock(chan);
-	return 0;
+	AST_LIST_UNLOCK(oldlist);
+	ast_channel_unlock(chan);
+	return res;
 }
 
 static int return_exec(struct ast_channel *chan, const char *data)
 {
 	struct ast_datastore *stack_store;
 	struct gosub_stack_frame *oldframe;
-	AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+	struct gosub_stack_list *oldlist;
 	const char *retval = data;
 	int res = 0;
 
@@ -358,12 +370,24 @@
 		ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
 		ast_channel_unlock(chan);
 		return -1;
-	} else if (oldframe->is_agi) {
-		/* Exit from AGI */
+	}
+	if (oldframe->is_special) {
+		/* Exit from special routine. */
 		res = -1;
 	}
 
-	ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
+	/*
+	 * We cannot use ast_explicit_goto() because we MUST restore
+	 * what was there before.  Channels that do not have a PBX may
+	 * not have the context or exten set.
+	 */
+	ast_copy_string(chan->context, oldframe->context, sizeof(chan->context));
+	ast_copy_string(chan->exten, oldframe->extension, sizeof(chan->exten));
+	if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP)) {
+		--oldframe->priority;
+	}
+	chan->priority = oldframe->priority;
+
 	gosub_release_frame(chan, oldframe);
 
 	/* Set a return value, if any */
@@ -372,10 +396,96 @@
 	return res;
 }
 
+/*!
+ * \internal
+ * \brief Add missing context and/or exten to Gosub application argument string.
+ * \since 1.8.30.0
+ * \since 11.0
+ *
+ * \param chan Channel to obtain context/exten.
+ * \param args Gosub application argument string.
+ *
+ * \details
+ * Fills in the optional context and exten from the given channel.
+ * Convert: [[context,]exten,]priority[(arg1[,...][,argN])]
+ * To: context,exten,priority[(arg1[,...][,argN])]
+ *
+ * \retval expanded Gosub argument string on success.  Must be freed.
+ * \retval NULL on error.
+ *
+ * \note The parsing needs to be kept in sync with the
+ * gosub_exec() argument format.
+ */
+static const char *expand_gosub_args(struct ast_channel *chan, const char *args)
+{
+	int len;
+	char *parse;
+	char *label;
+	char *new_args;
+	const char *context;
+	const char *exten;
+	const char *pri;
+
+	/* Separate the context,exten,pri from the optional routine arguments. */
+	parse = ast_strdupa(args);
+	label = strsep(&parse, "(");
+	if (parse) {
+		char *endparen;
+
+		endparen = strrchr(parse, ')');
+		if (endparen) {
+			*endparen = '\0';
+		} else {
+			ast_log(LOG_WARNING, "Ouch.  No closing paren: '%s'?\n", args);
+		}
+	}
+
+	/* Split context,exten,pri */
+	context = strsep(&label, ",");
+	exten = strsep(&label, ",");
+	pri = strsep(&label, ",");
+	if (!exten) {
+		/* Only a priority in this one */
+		pri = context;
+		exten = NULL;
+		context = NULL;
+	} else if (!pri) {
+		/* Only an extension and priority in this one */
+		pri = exten;
+		exten = context;
+		context = NULL;
+	}
+
+	ast_channel_lock(chan);
+	if (ast_strlen_zero(exten)) {
+		exten = chan->exten;
+	}
+	if (ast_strlen_zero(context)) {
+		context = chan->context;
+	}
+	len = strlen(context) + strlen(exten) + strlen(pri) + 3;
+	if (!ast_strlen_zero(parse)) {
+		len += 2 + strlen(parse);
+	}
+	new_args = ast_malloc(len);
+	if (new_args) {
+		if (ast_strlen_zero(parse)) {
+			snprintf(new_args, len, "%s,%s,%s", context, exten, pri);
+		} else {
+			snprintf(new_args, len, "%s,%s,%s(%s)", context, exten, pri, parse);
+		}
+	}
+	ast_channel_unlock(chan);
+
+	ast_debug(4, "Gosub args:%s new_args:%s\n", args, new_args ? new_args : "");
+
+	return new_args;
+}
+
 static int gosub_exec(struct ast_channel *chan, const char *data)
 {
 	struct ast_datastore *stack_store;
-	AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+	struct gosub_stack_list *oldlist;
 	struct gosub_stack_frame *newframe;
 	struct gosub_stack_frame *lastframe;
 	char argname[15];
@@ -565,7 +675,7 @@
 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 {
 	struct ast_datastore *stack_store;
-	AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+	struct gosub_stack_list *oldlist;
 	struct gosub_stack_frame *frame;
 	struct ast_var_t *variables;
 
@@ -605,7 +715,7 @@
 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
 {
 	struct ast_datastore *stack_store;
-	AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+	struct gosub_stack_list *oldlist;
 	struct gosub_stack_frame *frame;
 
 	if (!chan) {
@@ -683,7 +793,7 @@
 static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
 {
 	struct ast_datastore *stack_store;
-	AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+	struct gosub_stack_list *oldlist;
 	struct gosub_stack_frame *frame;
 	int n;
 	AST_DECLARE_APP_ARGS(args,
@@ -756,6 +866,7 @@
 		break;
 	default:
 		ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
+		break;
 	}
 
 	AST_LIST_UNLOCK(oldlist);
@@ -769,11 +880,206 @@
 	.read2 = stackpeek_read,
 };
 
+/*!
+ * \internal
+ * \brief Pop stack frames until remove a special return location.
+ * \since 1.8.30.0
+ * \since 11.0
+ *
+ * \param chan Channel to balance stack on.
+ *
+ * \note The channel is already locked when called.
+ *
+ * \return Nothing
+ */
+static void balance_stack(struct ast_channel *chan)
+{
+	struct ast_datastore *stack_store;
+	struct gosub_stack_list *oldlist;
+	struct gosub_stack_frame *oldframe;
+	int found;
+
+	stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
+	if (!stack_store) {
+		ast_log(LOG_WARNING, "No %s stack allocated.\n", app_gosub);
+		return;
+	}
+
+	oldlist = stack_store->data;
+	AST_LIST_LOCK(oldlist);
+	do {
+		oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
+		if (!oldframe) {
+			break;
+		}
+		found = oldframe->is_special;
+		gosub_release_frame(chan, oldframe);
+	} while (!found);
+	AST_LIST_UNLOCK(oldlist);
+}
+
+/*!
+ * \internal
+ * \brief Run a subroutine on a channel.
+ * \since 1.8.30.0
+ * \since 11.0
+ *
+ * \note Absolutely _NO_ channel locks should be held before calling this function.
+ *
+ * \param chan Channel to execute subroutine on.
+ * \param sub_args Gosub application argument string.
+ * \param ignore_hangup TRUE if a hangup does not stop execution of the routine.
+ *
+ * \retval 0 success
+ * \retval -1 on error
+ */
+static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_hangup)
+{
+	const char *saved_context;
+	const char *saved_exten;
+	int saved_priority;
+	int saved_hangup_flags;
+	int saved_autoloopflag;
+	int res;
+
+	ast_channel_lock(chan);
+
+	ast_verb(3, "%s Internal %s(%s) start\n", chan->name, app_gosub, sub_args);
+
+	/* Save non-hangup softhangup flags. */
+	saved_hangup_flags = chan->_softhangup
+		& (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
+	if (saved_hangup_flags) {
+		chan->_softhangup &= ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
+	}
+
+	/* Save autoloop flag */
+	saved_autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
+	ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
+
+	/* Save current dialplan location */
+	saved_context = ast_strdupa(chan->context);
+	saved_exten = ast_strdupa(chan->exten);
+	saved_priority = chan->priority;
+
+	ast_debug(4, "%s Original location: %s,%s,%d\n", chan->name,
+		saved_context, saved_exten, saved_priority);
+
+	ast_channel_unlock(chan);
+	res = gosub_exec(chan, sub_args);
+	ast_debug(4, "%s exited with status %d\n", app_gosub, res);
+	ast_channel_lock(chan);
+	if (!res) {
+		struct ast_datastore *stack_store;
+
+		/* Mark the return location as special. */
+		stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
+		if (!stack_store) {
+			/* Should never happen! */
+			ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
+			res = -1;
+		} else {
+			struct gosub_stack_list *oldlist;
+			struct gosub_stack_frame *cur;
+
+			oldlist = stack_store->data;
+			cur = AST_LIST_FIRST(oldlist);
+			cur->is_special = 1;
+		}
+	}
+	if (!res) {
+		int found = 0;	/* set if we find at least one match */
+
+		/*
+		 * Run gosub body autoloop.
+		 *
+		 * Note that this loop is inverted from the normal execution
+		 * loop because we just executed the Gosub application as the
+		 * first extension of the autoloop.
+		 */
+		do {
+			/* Check for hangup. */
+			if (chan->_softhangup & AST_SOFTHANGUP_UNBRIDGE) {
+				saved_hangup_flags |= AST_SOFTHANGUP_UNBRIDGE;
+				chan->_softhangup &= ~AST_SOFTHANGUP_UNBRIDGE;
+			}
+			if (ast_check_hangup(chan)) {
+				if (chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO) {
+					ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n",
+						chan->name);
+					break;
+				}
+				if (!ignore_hangup) {
+					break;
+				}
+			}
+
+			/* Next dialplan priority. */
+			++chan->priority;
+
+			ast_channel_unlock(chan);
+			res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority,
+				S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
+				&found, 1);
+			ast_channel_lock(chan);
+		} while (!res);
+		if (found && res) {
+			/* Something bad happened, or a hangup has been requested. */
+			ast_debug(1, "Spawn extension (%s,%s,%d) exited with %d on '%s'\n",
+				chan->context, chan->exten, chan->priority, res, chan->name);
+			ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
+				chan->context, chan->exten, chan->priority, chan->name);
+		}
+
+		/* Did the routine return? */
+		if (chan->priority == saved_priority
+			&& !strcmp(chan->context, saved_context)
+			&& !strcmp(chan->exten, saved_exten)) {
+			ast_verb(3, "%s Internal %s(%s) complete GOSUB_RETVAL=%s\n",
+				chan->name, app_gosub, sub_args,
+				S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
+		} else {
+			ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit.  Popping routine return locations.\n",
+				chan->name, app_gosub, sub_args);
+			balance_stack(chan);
+			pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
+		}
+
+		/* We executed the requested subroutine to the best of our ability. */
+		res = 0;
+	}
+
+	ast_debug(4, "%s Ending location: %s,%s,%d\n", chan->name,
+		chan->context, chan->exten, chan->priority);
+
+	/* Restore dialplan location */
+	if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
+		ast_copy_string(chan->context, saved_context, sizeof(chan->context));
+		ast_copy_string(chan->exten, saved_exten, sizeof(chan->exten));
+		chan->priority = saved_priority;
+	}
+
+	/* Restore autoloop flag */
+	ast_set2_flag(chan, saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
+
+	/* Restore non-hangup softhangup flags. */
+	if (saved_hangup_flags) {
+		ast_softhangup_nolock(chan, saved_hangup_flags);
+	}
+
+	ast_channel_unlock(chan);
+
+	return res;
+}
+
 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
 {
-	int old_priority, priority;
-	char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
-	struct ast_app *theapp;
+	int res;
+	int priority;
+	int old_autoloopflag;
+	int old_priority;
+	const char *old_context;
+	const char *old_extension;
 	char *gosub_args;
 
 	if (argc < 4 || argc > 5) {
@@ -797,78 +1103,122 @@
 		return RESULT_FAILURE;
 	}
 
+	if (argc == 5) {
+		if (ast_asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) {
+			gosub_args = NULL;
+		}
+	} else {
+		if (ast_asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) {
+			gosub_args = NULL;
+		}
+	}
+	if (!gosub_args) {
+		ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
+		return RESULT_FAILURE;
+	}
+
+	ast_channel_lock(chan);
+
+	ast_verb(3, "%s AGI %s(%s) start\n", chan->name, app_gosub, gosub_args);
+
+	/* Save autoloop flag */
+	old_autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
+	ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
+
 	/* Save previous location, since we're going to change it */
-	ast_copy_string(old_context, chan->context, sizeof(old_context));
-	ast_copy_string(old_extension, chan->exten, sizeof(old_extension));
+	old_context = ast_strdupa(chan->context);
+	old_extension = ast_strdupa(chan->exten);
 	old_priority = chan->priority;
 
-	if (!(theapp = pbx_findapp("Gosub"))) {
-		ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
-		ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
-		return RESULT_FAILURE;
-	}
-
-	/* Apparently, if you run ast_pbx_run on a channel that already has a pbx
-	 * structure, you need to add 1 to the priority to get it to go to the
-	 * right place.  But if it doesn't have a pbx structure, then leaving off
-	 * the 1 is the right thing to do.  See how this code differs when we
-	 * call a Gosub for the CALLEE channel in Dial or Queue.
-	 */
-	if (argc == 5) {
-		if (ast_asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + (chan->pbx ? 1 : 0), argv[4]) < 0) {
-			gosub_args = NULL;
-		}
-	} else {
-		if (ast_asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + (chan->pbx ? 1 : 0)) < 0) {
-			gosub_args = NULL;
-		}
-	}
-
-	if (gosub_args) {
-		int res;
-
-		ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
-
-		if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
-			struct ast_pbx *pbx = chan->pbx;
-			struct ast_pbx_args args;
-			struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
-			AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+	ast_debug(4, "%s Original location: %s,%s,%d\n", chan->name,
+		old_context, old_extension, old_priority);
+	ast_channel_unlock(chan);
+
+	res = gosub_exec(chan, gosub_args);
+	if (!res) {
+		struct ast_datastore *stack_store;
+
+		/* Mark the return location as special. */
+		ast_channel_lock(chan);
+		stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
+		if (!stack_store) {
+			/* Should never happen! */
+			ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
+			res = -1;
+		} else {
+			struct gosub_stack_list *oldlist;
 			struct gosub_stack_frame *cur;
-			if (!stack_store) {
-				ast_log(LOG_WARNING, "No GoSub stack remaining after AGI GoSub execution.\n");
-				ast_free(gosub_args);
-				return RESULT_FAILURE;
-			}
+
 			oldlist = stack_store->data;
 			cur = AST_LIST_FIRST(oldlist);
-			cur->is_agi = 1;
-
-			memset(&args, 0, sizeof(args));
-			args.no_hangup_chan = 1;
-			/* Suppress warning about PBX already existing */
-			chan->pbx = NULL;
-			ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
-			ast_pbx_run_args(chan, &args);
-			ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
-			if (chan->pbx) {
-				ast_free(chan->pbx);
-			}
-			chan->pbx = pbx;
+			cur->is_special = 1;
+		}
+		ast_channel_unlock(chan);
+	}
+	if (!res) {
+		struct ast_pbx *pbx;
+		struct ast_pbx_args args;
+		int abnormal_exit;
+
+		memset(&args, 0, sizeof(args));
+		args.no_hangup_chan = 1;
+
+		ast_channel_lock(chan);
+
+		/* Next dialplan priority. */
+		++chan->priority;
+
+		/* Suppress warning about PBX already existing */
+		pbx = chan->pbx;
+		chan->pbx = NULL;
+		ast_channel_unlock(chan);
+
+		ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
+		ast_pbx_run_args(chan, &args);
+
+		ast_channel_lock(chan);
+		ast_free(chan->pbx);
+		chan->pbx = pbx;
+
+		/* Did the routine return? */
+		if (chan->priority == old_priority
+			&& !strcmp(chan->context, old_context)
+			&& !strcmp(chan->exten, old_extension)) {
+			ast_verb(3, "%s AGI %s(%s) complete GOSUB_RETVAL=%s\n",
+				chan->name, app_gosub, gosub_args,
+				S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
+			abnormal_exit = 0;
 		} else {
-			ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
-		}
-		ast_free(gosub_args);
+			ast_log(LOG_NOTICE, "%s Abnormal AGI %s(%s) exit.  Popping routine return locations.\n",
+				chan->name, app_gosub, gosub_args);
+			balance_stack(chan);
+			pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
+			abnormal_exit = 1;
+		}
+		ast_channel_unlock(chan);
+
+		ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete%s\n",
+			abnormal_exit ? " (abnormal exit)" : "");
 	} else {
-		ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
-		return RESULT_FAILURE;
-	}
+		ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
+	}
+
+	/* Must use free because the memory was allocated by asprintf(). */
+	free(gosub_args);
+
+	ast_channel_lock(chan);
+	ast_debug(4, "%s Ending location: %s,%s,%d\n", chan->name,
+		chan->context, chan->exten, chan->priority);
 
 	/* Restore previous location */
 	ast_copy_string(chan->context, old_context, sizeof(chan->context));
 	ast_copy_string(chan->exten, old_extension, sizeof(chan->exten));
 	chan->priority = old_priority;
 
+	/* Restore autoloop flag */
+	ast_set2_flag(chan, old_autoloopflag, AST_FLAG_IN_AUTOLOOP);
+	ast_channel_unlock(chan);
+
 	return RESULT_SUCCESS;
 }
 
@@ -877,6 +1227,8 @@
 
 static int unload_module(void)
 {
+	ast_install_stack_functions(NULL);
+
 	ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
 
 	ast_unregister_application(app_return);
@@ -892,6 +1244,12 @@
 
 static int load_module(void)
 {
+	/* Setup the stack application callback functions. */
+	static struct ast_app_stack_funcs funcs = {
+		.run_sub = gosub_run,
+		.expand_sub_args = expand_gosub_args,
+	};
+
 	ast_agi_register(ast_module_info->self, &gosub_agi_command);
 
 	ast_register_application_xml(app_pop, pop_exec);
@@ -902,6 +1260,9 @@
 	ast_custom_function_register(&peek_function);
 	ast_custom_function_register(&stackpeek_function);
 
+	funcs.module = ast_module_info->self,
+	ast_install_stack_functions(&funcs);
+
 	return 0;
 }
 

Modified: certified/branches/1.8.28/include/asterisk/app.h
URL: http://svnview.digium.com/svn/asterisk/certified/branches/1.8.28/include/asterisk/app.h?view=diff&rev=419678&r1=419677&r2=419678
==============================================================================
--- certified/branches/1.8.28/include/asterisk/app.h (original)
+++ certified/branches/1.8.28/include/asterisk/app.h Fri Jul 25 18:47:09 2014
@@ -139,24 +139,161 @@
 int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd);
 
 /*!
+ * \brief Run a macro on a channel, placing an optional second channel into autoservice.
+ * \since 1.8.30.0
+ * \since 11.0
+ *
+ * \details
+ * This is a shorthand method that makes it very easy to run a
+ * macro on any given channel.  It is perfectly reasonable to
+ * supply a NULL autoservice_chan here in case there is no
+ * channel to place into autoservice.
+ *
+ * \note Absolutely _NO_ channel locks should be held before calling this function.
+ *
+ * \param autoservice_chan A channel to place into autoservice while the macro is run
+ * \param macro_chan Channel to execute macro on.
+ * \param macro_args Macro application argument string.
+ *
+ * \retval 0 success
+ * \retval -1 on error
+ */
+int ast_app_exec_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_args);
+
+/*!
  * \since 1.8
- * \brief Run a macro on a channel, placing a second channel into autoservice.
- *
- * This is a shorthand method that makes it very easy to run a macro on any given 
- * channel. It is perfectly reasonable to supply a NULL autoservice_chan here in case
- * there is no channel to place into autoservice. It is very important that the 
- * autoservice_chan parameter is not locked prior to calling ast_app_run_macro. A 
- * deadlock could result, otherwise.
+ * \brief Run a macro on a channel, placing an optional second channel into autoservice.
+ *
+ * \details
+ * This is a shorthand method that makes it very easy to run a
+ * macro on any given channel.  It is perfectly reasonable to
+ * supply a NULL autoservice_chan here in case there is no
+ * channel to place into autoservice.
+ *
+ * \note Absolutely _NO_ channel locks should be held before calling this function.
  *
  * \param autoservice_chan A channel to place into autoservice while the macro is run
- * \param macro_chan The channel to run the macro on
- * \param macro_name The name of the macro to run
- * \param macro_args The arguments to pass to the macro
+ * \param macro_chan Channel to execute macro on.
+ * \param macro_name The name of the macro to run.
+ * \param macro_args The arguments to pass to the macro.
+ *
  * \retval 0 success
- * \retval -1 failure
- */
-int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel 
-		*macro_chan, const char * const macro_name, const char * const macro_args);
+ * \retval -1 on error
+ */
+int ast_app_run_macro(struct ast_channel *autoservice_chan,
+	struct ast_channel *macro_chan, const char *macro_name, const char *macro_args);
+
+/*!
+ * \brief Stack applications callback functions.
+ */
+struct ast_app_stack_funcs {
+	/*!
+	 * Module reference pointer so the module will stick around
+	 * while a callback is active.
+	 */
+	void *module;
+
+	/*!
+	 * \brief Callback for the routine to run a subroutine on a channel.
+	 *
+	 * \note Absolutely _NO_ channel locks should be held before calling this function.
+	 *
+	 * \param chan Channel to execute subroutine on.
+	 * \param args Gosub application argument string.
+	 * \param ignore_hangup TRUE if a hangup does not stop execution of the routine.
+	 *
+	 * \retval 0 success
+	 * \retval -1 on error
+	 */
+	int (*run_sub)(struct ast_channel *chan, const char *args, int ignore_hangup);
+
+	/*!
+	 * \brief Add missing context/exten to Gosub application argument string.
+	 *
+	 * \param chan Channel to obtain context/exten.
+	 * \param args Gosub application argument string.
+	 *
+	 * \details
+	 * Fills in the optional context and exten from the given channel.
+	 *
+	 * \retval New-args Gosub argument string on success.  Must be freed.
+	 * \retval NULL on error.
+	 */
+	const char *(*expand_sub_args)(struct ast_channel *chan, const char *args);
+
+	/* Add new API calls to the end here. */
+};
+
+/*!
+ * \since 1.8.30.0
+ * \since 11
+ * \brief Set stack application function callbacks
+ * \param funcs Stack applications callback functions.
+ */
+void ast_install_stack_functions(const struct ast_app_stack_funcs *funcs);
+
+/*!
+ * \brief Add missing context/exten to subroutine argument string.
+ * \since 1.8.30.0
+ *
+ * \param chan Channel to obtain context/exten.
+ * \param args Gosub application argument string.
+ *
+ * \details
+ * Fills in the optional context and exten from the given channel.
+ *
+ * \retval New-args Gosub argument string on success.  Must be freed.
+ * \retval NULL on error.
+ */
+const char *ast_app_expand_sub_args(struct ast_channel *chan, const char *args);
+
+/*!
+ * \since 1.8.30.0
+ * \since 11
+ * \brief Run a subroutine on a channel, placing an optional second channel into autoservice.
+ *
+ * \details
+ * This is a shorthand method that makes it very easy to run a
+ * subroutine on any given channel.  It is perfectly reasonable
+ * to supply a NULL autoservice_chan here in case there is no
+ * channel to place into autoservice.
+ *
+ * \note Absolutely _NO_ channel locks should be held before calling this function.
+ *
+ * \param autoservice_chan A channel to place into autoservice while the subroutine is run
+ * \param sub_chan Channel to execute subroutine on.
+ * \param sub_args Gosub application argument string.
+ * \param ignore_hangup TRUE if a hangup does not stop execution of the routine.
+ *
+ * \retval 0 success
+ * \retval -1 on error
+ */
+int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args, int ignore_hangup);
+
+/*!
+ * \since 1.8.30.0
+ * \since 11
+ * \brief Run a subroutine on a channel, placing an optional second channel into autoservice.
+ *
+ * \details
+ * This is a shorthand method that makes it very easy to run a
+ * subroutine on any given channel.  It is perfectly reasonable
+ * to supply a NULL autoservice_chan here in case there is no
+ * channel to place into autoservice.
+ *
+ * \note Absolutely _NO_ channel locks should be held before calling this function.
+ *
+ * \param autoservice_chan A channel to place into autoservice while the subroutine is run
+ * \param sub_chan Channel to execute subroutine on.
+ * \param sub_location The location of the subroutine to run.
+ * \param sub_args The arguments to pass to the subroutine.
+ * \param ignore_hangup TRUE if a hangup does not stop execution of the routine.
+ *
+ * \retval 0 success
+ * \retval -1 on error
+ */
+int ast_app_run_sub(struct ast_channel *autoservice_chan,
+	struct ast_channel *sub_chan, const char *sub_location, const char *sub_args, int ignore_hangup);
 
 enum ast_vm_snapshot_sort_val {
 	AST_VM_SNAPSHOT_SORT_BY_ID = 0,

Modified: certified/branches/1.8.28/main/app.c
URL: http://svnview.digium.com/svn/asterisk/certified/branches/1.8.28/main/app.c?view=diff&rev=419678&r1=419677&r2=419678
==============================================================================
--- certified/branches/1.8.28/main/app.c (original)
+++ certified/branches/1.8.28/main/app.c Fri Jul 25 18:47:09 2014
@@ -59,6 +59,7 @@
 #include "asterisk/linkedlists.h"
 #include "asterisk/threadstorage.h"
 #include "asterisk/test.h"
+#include "asterisk/module.h"
 
 AST_THREADSTORAGE_PUBLIC(ast_str_thread_global_buf);
 
@@ -250,25 +251,142 @@
 	return res;
 }
 
-int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char * const macro_name, const char * const macro_args)
+int ast_app_exec_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_args)
 {
 	struct ast_app *macro_app;
 	int res;
-	char buf[1024];
 
 	macro_app = pbx_findapp("Macro");
 	if (!macro_app) {
-		ast_log(LOG_WARNING, "Cannot run macro '%s' because the 'Macro' application in not available\n", macro_name);
+		ast_log(LOG_WARNING,
+			"Cannot run 'Macro(%s)'.  The application is not available.\n", macro_args);
 		return -1;
 	}
-	snprintf(buf, sizeof(buf), "%s%s%s", macro_name, ast_strlen_zero(macro_args) ? "" : ",", S_OR(macro_args, ""));
 	if (autoservice_chan) {
 		ast_autoservice_start(autoservice_chan);
 	}
-	res = pbx_exec(macro_chan, macro_app, buf);
+
+	ast_debug(4, "%s Original location: %s,%s,%d\n", macro_chan->name,
+		macro_chan->context, macro_chan->exten, macro_chan->priority);
+
+	res = pbx_exec(macro_chan, macro_app, macro_args);
+	ast_debug(4, "Macro exited with status %d\n", res);
+
+	/*
+	 * Assume anything negative from Macro is an error.
+	 * Anything else is success.
+	 */
+	if (res < 0) {
+		res = -1;
+	} else {
+		res = 0;
+	}
+
+	ast_debug(4, "%s Ending location: %s,%s,%d\n", macro_chan->name,
+		macro_chan->context, macro_chan->exten, macro_chan->priority);
+
 	if (autoservice_chan) {
 		ast_autoservice_stop(autoservice_chan);
 	}
+	return res;
+}
+
+int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_name, const char *macro_args)
+{
+	int res;
+	char *args_str;
+	size_t args_len;
+
+	if (ast_strlen_zero(macro_args)) {
+		return ast_app_exec_macro(autoservice_chan, macro_chan, macro_name);
+	}
+
+	/* Create the Macro application argument string. */
+	args_len = strlen(macro_name) + strlen(macro_args) + 2;
+	args_str = ast_malloc(args_len);
+	if (!args_str) {
+		return -1;
+	}
+	snprintf(args_str, args_len, "%s,%s", macro_name, macro_args);
+
+	res = ast_app_exec_macro(autoservice_chan, macro_chan, args_str);
+	ast_free(args_str);
+	return res;
+}
+
+static const struct ast_app_stack_funcs *app_stack_callbacks;
+
+void ast_install_stack_functions(const struct ast_app_stack_funcs *funcs)
+{
+	app_stack_callbacks = funcs;
+}
+
+const char *ast_app_expand_sub_args(struct ast_channel *chan, const char *args)
+{
+	const struct ast_app_stack_funcs *funcs;
+	const char *new_args;
+
+	funcs = app_stack_callbacks;
+	if (!funcs || !funcs->expand_sub_args) {
+		ast_log(LOG_WARNING,
+			"Cannot expand 'Gosub(%s)' arguments.  The app_stack module is not available.\n",
+			args);
+		return NULL;
+	}
+	ast_module_ref(funcs->module);
+
+	new_args = funcs->expand_sub_args(chan, args);
+	ast_module_unref(funcs->module);
+	return new_args;
+}
+
+int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args, int ignore_hangup)
+{
+	const struct ast_app_stack_funcs *funcs;
+	int res;
+
+	funcs = app_stack_callbacks;
+	if (!funcs || !funcs->run_sub) {
+		ast_log(LOG_WARNING,
+			"Cannot run 'Gosub(%s)'.  The app_stack module is not available.\n",
+			sub_args);
+		return -1;
+	}
+	ast_module_ref(funcs->module);
+
+	if (autoservice_chan) {
+		ast_autoservice_start(autoservice_chan);
+	}
+
+	res = funcs->run_sub(sub_chan, sub_args, ignore_hangup);
+	ast_module_unref(funcs->module);
+
+	if (autoservice_chan) {
+		ast_autoservice_stop(autoservice_chan);
+	}
+	return res;
+}
+
+int ast_app_run_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_location, const char *sub_args, int ignore_hangup)
+{
+	int res;
+	char *args_str;
+	size_t args_len;
+
+	if (ast_strlen_zero(sub_args)) {
+		return ast_app_exec_sub(autoservice_chan, sub_chan, sub_location, ignore_hangup);
+	}
+
+	/* Create the Gosub application argument string. */
+	args_len = strlen(sub_location) + strlen(sub_args) + 3;
+	args_str = ast_malloc(args_len);
+	if (!args_str) {
+		return -1;
+	}
+	snprintf(args_str, args_len, "%s(%s)", sub_location, sub_args);
+
+	res = ast_app_exec_sub(autoservice_chan, sub_chan, args_str, ignore_hangup);
+	ast_free(args_str);
 	return res;
 }
 

Modified: certified/branches/1.8.28/main/features.c
URL: http://svnview.digium.com/svn/asterisk/certified/branches/1.8.28/main/features.c?view=diff&rev=419678&r1=419677&r2=419678
==============================================================================
--- certified/branches/1.8.28/main/features.c (original)
+++ certified/branches/1.8.28/main/features.c Fri Jul 25 18:47:09 2014
@@ -3222,7 +3222,13 @@
 	if (!ast_strlen_zero(feature->moh_class))
 		ast_moh_start(idle, feature->moh_class, NULL);
 
-	res = pbx_exec(work, app, feature->app_args);
+	if (!strcasecmp("Gosub", feature->app)) {
+		res = ast_app_exec_sub(NULL, work, feature->app_args, 0);
+	} else if (!strcasecmp("Macro", feature->app)) {
+		res = ast_app_exec_macro(NULL, work, feature->app_args);
+	} else {
+		res = pbx_exec(work, app, feature->app_args);
+	}
 
 	if (!ast_strlen_zero(feature->moh_class))
 		ast_moh_stop(idle);




More information about the asterisk-commits mailing list