dect
/
asterisk
Archived
13
0
Fork 0

Create a universal exception handling extension, "e" (closes issue #9785)

git-svn-id: http://svn.digium.com/svn/asterisk/trunk@84580 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
tilghman 2007-10-03 22:14:09 +00:00
parent 87fd913202
commit 35dffc0dc7
3 changed files with 144 additions and 5 deletions

View File

@ -66,6 +66,14 @@ There are some extensions with important meanings:
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

View File

@ -37,6 +37,9 @@ extern "C" {
#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_builtin_setvar_helper(struct ast_channel *chan, const char *name, const
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);

View File

@ -248,6 +248,16 @@ static struct {
.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 @@ static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
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;
@ -434,6 +445,13 @@ static struct pbx_builtin {
"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,
"Resets the Call Data Record",
" ResetCDR([options]): This application causes the Call Data Record to be\n"
@ -1260,6 +1278,84 @@ void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, c
}
}
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)
{
struct ast_custom_function *acf;
@ -2414,8 +2510,22 @@ static int __ast_pbx_run(struct ast_channel *c)
}
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,11 +2535,16 @@ static int __ast_pbx_run(struct ast_channel *c)
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;
} else if (ast_check_hangup(c)) {
ast_debug(1, "Extension %s, priority %d returned normally even though call was hung up\n",
c->exten, c->priority);
@ -2441,16 +2556,23 @@ static int __ast_pbx_run(struct ast_channel *c)
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 @@ static int __ast_pbx_run(struct ast_channel *c)
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 @@ static int __ast_pbx_run(struct ast_channel *c)
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 @@ int load_pbx(void)
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++) {