summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/autoservice.c70
-rw-r--r--main/pbx.c24
2 files changed, 88 insertions, 6 deletions
diff --git a/main/autoservice.c b/main/autoservice.c
index 39bad2f7c..2622e9509 100644
--- a/main/autoservice.c
+++ b/main/autoservice.c
@@ -47,6 +47,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
struct asent {
struct ast_channel *chan;
+ /*! This gets incremented each time autoservice gets started on the same
+ * channel. It will ensure that it doesn't actually get stopped until
+ * it gets stopped for the last time. */
+ unsigned int use_count;
+ AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
AST_LIST_ENTRY(asent) list;
};
@@ -74,8 +79,42 @@ static void *autoservice_run(void *ign)
AST_RWLIST_UNLOCK(&aslist);
if ((chan = ast_waitfor_n(mons, x, &ms))) {
- /* Read and ignore anything that occurs */
struct ast_frame *f = ast_read(chan);
+
+ /* Do not add a default entry in this switch statement. Each new
+ * frame type should be addressed directly as to whether it should
+ * be queued up or not. */
+ switch (f->frametype) {
+ /* Save these frames */
+ case AST_FRAME_DTMF_BEGIN:
+ case AST_FRAME_DTMF_END:
+ case AST_FRAME_CONTROL:
+ case AST_FRAME_TEXT:
+ case AST_FRAME_IMAGE:
+ case AST_FRAME_HTML:
+ {
+ struct ast_frame *dup_f;
+
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_TRAVERSE(&aslist, as, list) {
+ if (as->chan != chan)
+ continue;
+ if ((dup_f = ast_frdup(f)))
+ AST_LIST_INSERT_TAIL(&as->dtmf_frames, dup_f, frame_list);
+ }
+ AST_RWLIST_UNLOCK(&aslist);
+ }
+
+ /* Throw these frames away */
+ case AST_FRAME_VOICE:
+ case AST_FRAME_VIDEO:
+ case AST_FRAME_NULL:
+ case AST_FRAME_IAX:
+ case AST_FRAME_CNG:
+ case AST_FRAME_MODEM:
+ break;
+ }
+
if (f)
ast_frfree(f);
}
@@ -95,13 +134,16 @@ int ast_autoservice_start(struct ast_channel *chan)
/* Check if the channel already has autoservice */
AST_RWLIST_TRAVERSE(&aslist, as, list) {
- if (as->chan == chan)
+ if (as->chan == chan) {
+ as->use_count++;
break;
+ }
}
/* If not, start autoservice on channel */
if (!as && (as = ast_calloc(1, sizeof(*as)))) {
as->chan = chan;
+ as->use_count = 1;
AST_RWLIST_INSERT_HEAD(&aslist, as, list);
res = 0;
if (asthread == AST_PTHREADT_NULL) { /* need start the thread */
@@ -116,7 +158,9 @@ int ast_autoservice_start(struct ast_channel *chan)
pthread_kill(asthread, SIGURG);
}
}
+
AST_RWLIST_UNLOCK(&aslist);
+
return res;
}
@@ -124,11 +168,22 @@ int ast_autoservice_stop(struct ast_channel *chan)
{
int res = -1;
struct asent *as;
+ AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
+ struct ast_frame *f;
+ int removed = 1;
+
+ AST_LIST_HEAD_INIT_NOLOCK(&dtmf_frames);
AST_RWLIST_WRLOCK(&aslist);
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {
if (as->chan == chan) {
AST_RWLIST_REMOVE_CURRENT(list);
+ as->use_count--;
+ if (as->use_count) {
+ removed = 0;
+ break;
+ }
+ AST_LIST_APPEND_LIST(&dtmf_frames, &as->dtmf_frames, frame_list);
ast_free(as);
if (!ast_check_hangup(chan))
res = 0;
@@ -137,13 +192,22 @@ int ast_autoservice_stop(struct ast_channel *chan)
}
AST_RWLIST_TRAVERSE_SAFE_END;
- if (asthread != AST_PTHREADT_NULL)
+ if (removed && asthread != AST_PTHREADT_NULL)
pthread_kill(asthread, SIGURG);
+
AST_RWLIST_UNLOCK(&aslist);
+ if (!removed)
+ return 0;
+
/* Wait for it to un-block */
while (ast_test_flag(chan, AST_FLAG_BLOCKING))
usleep(1000);
+ while ((f = AST_LIST_REMOVE_HEAD(&dtmf_frames, frame_list))) {
+ ast_queue_frame(chan, f);
+ ast_frfree(f);
+ }
+
return res;
}
diff --git a/main/pbx.c b/main/pbx.c
index 5ab13babc..c23b211a6 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -2579,7 +2579,8 @@ static void pbx_substitute_variables(char *passdata, int datalen, struct ast_cha
pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
}
-/*! \brief The return value depends on the action:
+/*!
+ * \brief The return value depends on the action:
*
* E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
* and return 0 on failure, -1 on match;
@@ -2589,6 +2590,12 @@ static void pbx_substitute_variables(char *passdata, int datalen, struct ast_cha
*
* \retval 0 on success.
* \retval -1 on failure.
+ *
+ * \note The channel is auto-serviced in this function, because doing an extension
+ * match may block for a long time. For example, if the lookup has to use a network
+ * dialplan switch, such as DUNDi or IAX2, it may take a while. However, the channel
+ * auto-service code will queue up any important signalling frames to be processed
+ * after this is done.
*/
static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
const char *context, const char *exten, int priority,
@@ -2601,20 +2608,25 @@ static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
char passdata[EXT_DATA_SIZE];
int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
+
+ ast_autoservice_start(c);
ast_rdlock_contexts();
if (found)
*found = 0;
+
e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
if (e) {
if (found)
*found = 1;
if (matching_action) {
ast_unlock_contexts();
+ ast_autoservice_stop(c);
return -1; /* success, we found it */
} else if (action == E_FINDLABEL) { /* map the label to a priority */
res = e->priority;
ast_unlock_contexts();
+ ast_autoservice_stop(c);
return res; /* the priority we were looking for */
} else { /* spawn */
if (!e->cached_app)
@@ -2623,6 +2635,7 @@ static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
ast_unlock_contexts();
if (!app) {
ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
+ ast_autoservice_stop(c);
return -1;
}
if (c->context != context)
@@ -2658,17 +2671,20 @@ static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
"AppData: %s\r\n"
"Uniqueid: %s\r\n",
c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid);
+ ast_autoservice_stop(c);
return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
}
} else if (q.swo) { /* not found here, but in another switch */
ast_unlock_contexts();
- if (matching_action)
+ if (matching_action) {
+ ast_autoservice_stop(c);
return -1;
- else {
+ } else {
if (!q.swo->exec) {
ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
res = -1;
}
+ ast_autoservice_stop(c);
return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
}
} else { /* not found anywhere, see what happened */
@@ -2694,6 +2710,8 @@ static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
ast_debug(1, "Shouldn't happen!\n");
}
+ ast_autoservice_stop(c);
+
return (matching_action) ? 0 : -1;
}
}