No subject


Fri Sep 2 03:59:05 CDT 2011


* Fixed ability to support relative non-normal dialplan execution
routines.  (i.e., The context and exten are optional for the specified
dialplan location.) Predial routines are the only non-normal routines that
it makes sense to optionally omit the context and exten.

* Fixed the AGI gosub application so it cleans up the dialplan execution
stack and handles the autoloop priority increments correctly.

* Eliminated the need for the gosub_virtual_context return location.

Modified:
    team/rmudgett/hangup_handlers/apps/app_stack.c

Modified: team/rmudgett/hangup_handlers/apps/app_stack.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/apps/app_stack.c?view=diff&rev=368849&r1=368848&r2=368849
==============================================================================
--- team/rmudgett/hangup_handlers/apps/app_stack.c (original)
+++ team/rmudgett/hangup_handlers/apps/app_stack.c Tue Jun 12 20:03:45 2012
@@ -204,12 +204,6 @@
 static const char app_gosubif[] = "GosubIf";
 static const char app_return[] = "Return";
 static const char app_pop[] = "StackPop";
-static const char mod_name[] = "app_stack";
-
-/*! gosub_run() return context */
-static const char ret_context[] = "gosub_virtual_context";
-/*! gosub_run() return exten */
-static const char ret_exten[] = "s";
 
 static void gosub_free(void *data);
 
@@ -224,10 +218,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 +293,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 +309,7 @@
 {
 	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);
@@ -325,12 +323,10 @@
 	AST_LIST_LOCK(oldlist);
 	oldframe = AST_LIST_FIRST(oldlist);
 	if (oldframe) {
-		if (oldframe->priority == 1
-			&& !strcmp(oldframe->context, ret_context)
-			&& !strcmp(oldframe->extension, ret_exten)) {
-			ast_debug(1, "%s attempted to pop gosub_run() return location.\n", app_pop);
-
-			/* Abort the gosub_run() dialplan execution.  Dialplan programming error. */
+		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);
@@ -348,7 +344,7 @@
 {
 	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;
 
@@ -368,8 +364,9 @@
 		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;
 	}
 
@@ -385,7 +382,7 @@
 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];
@@ -576,7 +573,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;
 
@@ -611,7 +608,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;
 
 	ast_channel_lock(chan);
@@ -678,7 +675,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,
@@ -745,6 +742,7 @@
 		break;
 	default:
 		ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
+		break;
 	}
 
 	AST_LIST_UNLOCK(oldlist);
@@ -760,13 +758,10 @@
 
 /*!
  * \internal
- * \brief Helper function removes stack frames until remove an expected return location.
+ * \brief Pop stack frames until remove a special return location.
  * \since 11.0
  *
  * \param chan Channel to balance stack on.
- *
- * \details
- * Pop stack frames until find a gosub_run() return location.
  *
  * \note The channel is already locked when called.
  *
@@ -775,29 +770,24 @@
 static void balance_stack(struct ast_channel *chan)
 {
 	struct ast_datastore *stack_store;
-	AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
+	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 gosub stack allocated.\n");
+		ast_log(LOG_WARNING, "No %s stack allocated.\n", app_gosub);
 		return;
 	}
 
 	oldlist = stack_store->data;
 	AST_LIST_LOCK(oldlist);
-	found = 0;
 	do {
 		oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
 		if (!oldframe) {
 			break;
 		}
-		if (oldframe->priority == 1
-			&& !strcmp(oldframe->context, ret_context)
-			&& !strcmp(oldframe->extension, ret_exten)) {
-			found = 1;
-		}
+		found = oldframe->is_special;
 		gosub_release_frame(chan, oldframe);
 	} while (!found);
 	AST_LIST_UNLOCK(oldlist);
@@ -836,20 +826,15 @@
 			AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
 	}
 
+	/* Save autoloop flag */
+	saved_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
+	ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
+
 	/* Save current dialplan location */
 	saved_context = ast_strdupa(ast_channel_context(chan));
 	saved_exten = ast_strdupa(ast_channel_exten(chan));
 	saved_priority = ast_channel_priority(chan);
 
-	/* Set known location for Gosub to return - 1 */
-	ast_channel_context_set(chan, ret_context);
-	ast_channel_exten_set(chan, ret_exten);
-	ast_channel_priority_set(chan, 1 - 1);
-
-	/* Save autoloop flag */
-	saved_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
-	ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
-
 	ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
 		saved_context, saved_exten, saved_priority);
 
@@ -858,8 +843,25 @@
 	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 */
-		int sub_returned = 0;
 
 		/*
 		 * Run gosub body autoloop.
@@ -876,7 +878,8 @@
 			}
 			if (ast_check_hangup(chan)) {
 				if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
-					ast_log(LOG_ERROR, "An async goto just messed up our execution location.\n");
+					ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n",
+						ast_channel_name(chan));
 					break;
 				}
 				if (!ignore_hangup) {
@@ -894,13 +897,6 @@
 					ast_channel_caller(chan)->id.number.str, NULL),
 				&found, 1);
 			ast_channel_lock(chan);
-
-			/* Did the routine return? */
-			if (ast_channel_priority(chan) == 1 - 1
-				&& !strcmp(ast_channel_context(chan), ret_context)
-				&& !strcmp(ast_channel_exten(chan), ret_exten)) {
-				sub_returned = 1;
-			}
 		} while (!res);
 		if (found && res) {
 			/* Something bad happened, or a hangup has been requested. */
@@ -911,10 +907,19 @@
 				ast_channel_context(chan), ast_channel_exten(chan),
 				ast_channel_priority(chan), ast_channel_name(chan));
 		}
-		if (!sub_returned) {
-			ast_log(LOG_NOTICE, "Abnormal '%s(%s)' exit.  Popping routine return locations.\n",
-				app_gosub, sub_args);
+
+		/* Did the routine return? */
+		if (ast_channel_priority(chan) == saved_priority
+			&& !strcmp(ast_channel_context(chan), saved_context)
+			&& !strcmp(ast_channel_exten(chan), saved_exten)) {
+			ast_verb(3, "%s Internal Gosub call complete GOSUB_RETVAL=%s\n",
+				ast_channel_name(chan),
+				S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
+		} else {
+			ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit.  Popping routine return locations.\n",
+				ast_channel_name(chan), 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. */
@@ -924,9 +929,6 @@
 	ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
 		ast_channel_context(chan), ast_channel_exten(chan),
 		ast_channel_priority(chan));
-
-	/* Restore autoloop flag */
-	ast_set2_flag(ast_channel_flags(chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
 
 	/* Restore dialplan location */
 	if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
@@ -935,6 +937,9 @@
 		ast_channel_priority_set(chan, saved_priority);
 	}
 
+	/* Restore autoloop flag */
+	ast_set2_flag(ast_channel_flags(chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
+
 	/* Restore non-hangup softhangup flags. */
 	if (saved_hangup_flags) {
 		ast_softhangup_nolock(chan, saved_hangup_flags);
@@ -947,9 +952,12 @@
 
 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) {
@@ -973,80 +981,125 @@
 		return RESULT_FAILURE;
 	}
 
-	/* Save previous location, since we're going to change it */
-	ast_copy_string(old_context, ast_channel_context(chan), sizeof(old_context));
-	ast_copy_string(old_extension, ast_channel_exten(chan), sizeof(old_extension));
-	old_priority = ast_channel_priority(chan);
-
-	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 (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + (ast_channel_pbx(chan) ? 1 : 0), argv[4]) < 0) {
+		if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) {
 			ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
 			gosub_args = NULL;
 		}
 	} else {
-		if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + (ast_channel_pbx(chan) ? 1 : 0)) < 0) {
+		if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) {
 			ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
 			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 = ast_channel_pbx(chan);
-			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;
+	if (!gosub_args) {
+		ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
+		return RESULT_FAILURE;
+	}
+
+	ast_channel_lock(chan);
+
+	ast_debug(1, "%s Trying AGI %s(%s)\n", ast_channel_name(chan), app_gosub, gosub_args);
+
+	/* Save autoloop flag */
+	old_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
+	ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
+
+	/* Save previous location, since we're going to change it */
+	old_context = ast_strdupa(ast_channel_context(chan));
+	old_extension = ast_strdupa(ast_channel_exten(chan));
+	old_priority = ast_channel_priority(chan);
+
+	ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
+		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 */
-			ast_channel_pbx_set(chan, 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 (ast_channel_pbx(chan)) {
-				ast_free(ast_channel_pbx(chan));
-			}
-			ast_channel_pbx_set(chan, 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. */
+		ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
+
+		/* Suppress warning about PBX already existing */
+		pbx = ast_channel_pbx(chan);
+		ast_channel_pbx_set(chan, 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(ast_channel_pbx(chan));
+		ast_channel_pbx_set(chan, pbx);
+
+		/* Did the routine return? */
+		if (ast_channel_priority(chan) == old_priority
+			&& !strcmp(ast_channel_context(chan), old_context)
+			&& !strcmp(ast_channel_exten(chan), old_extension)) {
+			ast_verb(3, "%s Internal Gosub call complete GOSUB_RETVAL=%s\n",
+				ast_channel_name(chan),
+				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",
+				ast_channel_name(chan), 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", ast_channel_name(chan),
+		ast_channel_context(chan), ast_channel_exten(chan),
+		ast_channel_priority(chan));
 
 	/* Restore previous location */
 	ast_channel_context_set(chan, old_context);
 	ast_channel_exten_set(chan, old_extension);
 	ast_channel_priority_set(chan, old_priority);
 
+	/* Restore autoloop flag */
+	ast_set2_flag(ast_channel_flags(chan), old_autoloopflag, AST_FLAG_IN_AUTOLOOP);
+	ast_channel_unlock(chan);
+
 	return RESULT_SUCCESS;
 }
 
@@ -1055,8 +1108,6 @@
 
 static int unload_module(void)
 {
-	struct ast_context *con;
-
 	ast_install_stack_functions(NULL);
 
 	ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
@@ -1069,34 +1120,15 @@
 	ast_custom_function_unregister(&peek_function);
 	ast_custom_function_unregister(&stackpeek_function);
 
-	con = ast_context_find(ret_context);
-	if (con) {
-		/* leave nothing behind */
-		ast_context_remove_extension2(con, ret_exten, 1, NULL, 0);
-		ast_context_destroy(con, mod_name);
-	}
-
 	return 0;
 }
 
 static int load_module(void)
 {
-	struct ast_context *con;
-
-	/*! Setup the stack application callback functions. */
+	/* Setup the stack application callback functions. */
 	static struct ast_app_stack_funcs funcs = {
 		.run_sub = gosub_run,
 	};
-
-	/* Create internal gosub return target to indicate successful completion. */
-	con = ast_context_find_or_create(NULL, NULL, ret_context, mod_name);
-	if (!con) {
-		ast_log(LOG_ERROR, "'%s' does not exist and unable to create\n", ret_context);
-	} else {
-		ast_add_extension2(con, 1, ret_exten, 1, NULL, NULL, "NoOp",
-			ast_strdup("Internal Gosub call complete GOSUB_RETVAL=${GOSUB_RETVAL}"),
-			ast_free_ptr, mod_name);
-	}
 
 	ast_agi_register(ast_module_info->self, &gosub_agi_command);
 




More information about the asterisk-commits mailing list