[asterisk-commits] tilghman: trunk r84580 - in /trunk: doc/tex/ include/asterisk/ main/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Oct 3 17:14:10 CDT 2007


Author: tilghman
Date: Wed Oct  3 17:14:09 2007
New Revision: 84580

URL: http://svn.digium.com/view/asterisk?view=rev&rev=84580
Log:
Create a universal exception handling extension, "e" (closes issue #9785)

Modified:
    trunk/doc/tex/extensions.tex
    trunk/include/asterisk/pbx.h
    trunk/main/pbx.c

Modified: trunk/doc/tex/extensions.tex
URL: http://svn.digium.com/view/asterisk/trunk/doc/tex/extensions.tex?view=diff&rev=84580&r1=84579&r2=84580
==============================================================================
--- trunk/doc/tex/extensions.tex (original)
+++ trunk/doc/tex/extensions.tex Wed Oct  3 17:14:09 2007
@@ -66,6 +66,14 @@
           timeout is reached.  See "show function TIMEOUT" for more
           information on setting timeouts.	
   \end{itemize}
+  \item e
+  \begin{itemize}
+    \item This extension will substitute as a catchall for any of the
+          'i', 't', or 'T' extensions, if any of them do not exist and
+          catching the error in a single routine is desired.  The
+          function EXCEPTION may be used to query the type of exception
+          or the location where it occurred.
+  \end{itemize}
 \end{itemize}
 
 And finally, the extension context "default" is used when either a) an 

Modified: trunk/include/asterisk/pbx.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/pbx.h?view=diff&rev=84580&r1=84579&r2=84580
==============================================================================
--- trunk/include/asterisk/pbx.h (original)
+++ trunk/include/asterisk/pbx.h Wed Oct  3 17:14:09 2007
@@ -37,6 +37,9 @@
 #define AST_PBX_REPLACE 1
 
 /*! \brief Special return values from applications to the PBX { */
+#define AST_PBX_HANGUP	        -1	/*!< Jump to the 'h' exten */
+#define AST_PBX_OK	        0	/*!< No errors */
+#define AST_PBX_ERROR	        1	/*!< Jump to the 'e' exten */
 #define AST_PBX_KEEPALIVE	10	/*!< Destroy the thread, but don't hang up the channel */
 #define AST_PBX_NO_HANGUP_PEER	11
 /*! } */
@@ -840,6 +843,7 @@
 void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp);
 void pbx_builtin_clear_globals(void);
 int pbx_builtin_setvar(struct ast_channel *chan, void *data);
+int pbx_builtin_raise_exception(struct ast_channel *chan, void *data);
 void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count);
 

Modified: trunk/main/pbx.c
URL: http://svn.digium.com/view/asterisk/trunk/main/pbx.c?view=diff&rev=84580&r1=84579&r2=84580
==============================================================================
--- trunk/main/pbx.c (original)
+++ trunk/main/pbx.c Wed Oct  3 17:14:09 2007
@@ -248,6 +248,16 @@
 	.thread = AST_PTHREADT_NULL,
 };
 
+struct pbx_exception {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(context);	/*!< Context associated with this exception */
+		AST_STRING_FIELD(exten);	/*!< Exten associated with this exception */
+		AST_STRING_FIELD(type);		/*!< The type of exception */
+	);
+
+	int priority;				/*!< Priority associated with this exception */
+};
+
 static int pbx_builtin_answer(struct ast_channel *, void *);
 static int pbx_builtin_goto(struct ast_channel *, void *);
 static int pbx_builtin_hangup(struct ast_channel *, void *);
@@ -272,6 +282,7 @@
 int pbx_builtin_setvar(struct ast_channel *, void *);
 static int pbx_builtin_setvar_multiple(struct ast_channel *, void *);
 static int pbx_builtin_importvar(struct ast_channel *, void *);
+static void set_ext_pri(struct ast_channel *c, const char *exten, int pri);
 
 AST_RWLOCK_DEFINE_STATIC(globalslock);
 static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
@@ -432,6 +443,13 @@
 	"Indicate progress",
 	"  Progress(): This application will request that in-band progress information\n"
 	"be provided to the calling channel.\n"
+	},
+
+	{ "RaiseException", pbx_builtin_raise_exception,
+	"Handle an exceptional condition",
+	"  RaiseException(<reason>): This application will jump to the \"e\" extension\n"
+	"in the current context, setting the dialplan function EXCEPTION().  If the \"e\"\n"
+	"extension does not exist, the call will hangup.\n"
 	},
 
 	{ "ResetCDR", pbx_builtin_resetcdr,
@@ -1259,6 +1277,84 @@
 			*ret = substring(*ret, offset, length, workspace, workspacelen);
 	}
 }
+
+static void exception_store_free(void *data)
+{
+	struct pbx_exception *exception = data;
+	ast_string_field_free_pools(exception);
+	ast_free(exception);
+}
+
+static struct ast_datastore_info exception_store_info = {
+	.type = "EXCEPTION",
+	.destroy = exception_store_free,
+};
+
+int pbx_builtin_raise_exception(struct ast_channel *chan, void *vtype)
+{
+	const char *type = vtype;
+	struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
+	struct pbx_exception *exception = NULL;
+
+	if (!ds) {
+		ds = ast_channel_datastore_alloc(&exception_store_info, NULL);
+		if (!ds)
+			return -1;
+		exception = ast_calloc(1, sizeof(struct pbx_exception));
+		if (!ds->data) {
+			ast_channel_datastore_free(ds);
+			return -1;
+		}
+		if (ast_string_field_init(exception, 128)) {
+			ast_free(exception);
+			ast_channel_datastore_free(ds);
+			return -1;
+		}
+		ds->data = exception;
+		ast_channel_datastore_add(chan, ds);
+	} else
+		exception = ds->data;
+
+	ast_string_field_set(exception, type, type);
+	ast_string_field_set(exception, context, chan->context);
+	ast_string_field_set(exception, exten, chan->exten);
+	exception->priority = chan->priority;
+	set_ext_pri(chan, "e", 1);
+	return 0;
+}
+
+static int acf_exception_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
+{
+	struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
+	struct pbx_exception *exception = NULL;
+	if (!ds || !ds->data)
+		return -1;
+	exception = ds->data;
+	if (!strcasecmp(data, "TYPE"))
+		ast_copy_string(buf, exception->type, buflen);
+	else if (!strcasecmp(data, "CONTEXT"))
+		ast_copy_string(buf, exception->context, buflen);
+	else if (!strncasecmp(data, "EXTEN", 5))
+		ast_copy_string(buf, exception->exten, buflen);
+	else if (!strcasecmp(data, "PRIORITY"))
+		snprintf(buf, buflen, "%d", exception->priority);
+	else
+		return -1;
+	return 0;
+}
+
+static struct ast_custom_function exception_function = {
+	.name = "EXCEPTION",
+	.synopsis = "Retrieve the details of the current dialplan exception",
+	.desc =
+"The following fields are available for retrieval:\n"
+"  type      INVALID, ERROR, RESPONSETIMEOUT, or ABSOLUTETIMEOUT\n"
+"  context   The context executing when the exception occurred\n"
+"  exten     The extension executing when the exception occurred\n"
+"  priority  The numeric priority executing when the exception occurred\n",
+	.syntax = "EXCEPTION(<field>)",
+	.read = acf_exception_read,
+};
 
 static char *handle_show_functions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -2414,8 +2510,22 @@
 				}
 				ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
 				ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+
+ 				if ((res == AST_PBX_ERROR) && ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ 					/* if we are already on the 'e' exten, don't jump to it again */
+ 					if (!strcmp(c->exten, "e")) {
+ 						if (option_verbose > 1)
+ 							ast_verbose(VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited ERROR while already on 'e' exten on '%s'\n", c->context, c->exten, c->priority, c->name);
+ 						error = 1;
+						break;
+ 					} else {
+						pbx_builtin_raise_exception(c, "ERROR");
+ 						continue;
+ 					}
+ 				}
+
 				if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
-					c->_softhangup =0;
+					c->_softhangup = 0;
 				} else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
 					/* atimeout, nothing bad */
 				} else {
@@ -2425,8 +2535,13 @@
 					break;
 				}
 			}
-			if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c,c->context,"T",1,c->cid.cid_num)) {
+			if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "T", 1, c->cid.cid_num)) {
 				set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
+				/* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
+				c->whentohangup = 0;
+				c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
+			} else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+				pbx_builtin_raise_exception(c, "ABSOLUTETIMEOUT");
 				/* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
 				c->whentohangup = 0;
 				c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
@@ -2441,16 +2556,23 @@
 		if (error)
 			break;
 
-		/* XXX we get here on non-existing extension or a keypress or hangup ? */
+		/*!\note
+		 * We get here on a failure of some kind:  non-existing extension or
+		 * hangup.  We have options, here.  We can either catch the failure
+		 * and continue, or we can drop out entirely. */
 
 		if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
-			/* If there is no match at priority 1, it is not a valid extension anymore.
-			 * Try to continue at "i", 1 or exit if the latter does not exist.
+			/*!\note
+			 * If there is no match at priority 1, it is not a valid extension anymore.
+			 * Try to continue at "i" (for invalid) or "e" (for exception) or exit if
+			 * neither exist.
 			 */
 			if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
 				ast_verb(3, "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
 				pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
 				set_ext_pri(c, "i", 1);
+			} else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+				pbx_builtin_raise_exception(c, "INVALID");
 			} else {
 				ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
 					c->name, c->exten, c->context);
@@ -2493,6 +2615,8 @@
 						ast_verb(3, "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
 						pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
 						set_ext_pri(c, "i", 1);
+					} else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+						pbx_builtin_raise_exception(c, "INVALID");
 					} else {
 						ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
 						found = 1; /* XXX disable message */
@@ -2503,6 +2627,8 @@
 					if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
 						ast_verb(3, "Timeout on %s\n", c->name);
 						set_ext_pri(c, "t", 1);
+					} else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+						pbx_builtin_raise_exception(c, "RESPONSETIMEOUT");
 					} else {
 						ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
 						found = 1; /* XXX disable message */
@@ -6098,6 +6224,7 @@
 	ast_verb(1, "Registering builtin applications:\n");
 	
 	ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(struct ast_cli_entry));
+	ast_custom_function_register(&exception_function);
 
 	/* Register builtin applications */
 	for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {




More information about the asterisk-commits mailing list