summaryrefslogtreecommitdiff
path: root/res/res_agi.c
diff options
context:
space:
mode:
authorGeorge Joseph <gjoseph@digium.com>2016-04-22 16:53:23 -0600
committerGeorge Joseph <gjoseph@digium.com>2016-04-25 09:56:00 -0500
commit99fcf2a791c2c78ae0ffffce2ac3e8db18ca5be0 (patch)
treee98d1c4cd924d93d3271061791cc35d0003aa319 /res/res_agi.c
parent7f8d83fef42f4b1d2240ea543f61a5a70754e862 (diff)
res_agi: Prevent run_agi from eating frames it shouldn't
The run_agi function is eating control frames when it shouldn't be. This is causing issues when an AGI is run from CONNECTED_LINE_SEND_SUB in a blond transfer. Alice calls Bob. Bob attended transfers to Charlie but hangs up before Charlie answers. Alice gets the COLP UPDATE indicating Charlie but Charlie never gets an UPDATE and is left thinking he's connected to Bob. In this case, when CONNECTED_LINE_SEND_SUB runs on Alice's channel and it calls an AGI, the extra eaten frames prevent CONNECTED_LINE_SEND_SUB from running on Charlie's channel. The fix was to accumulate deferrable frames in the "forever" loop instead of dropping them, and re-queue them just before running the actual agi command or exiting. ASTERISK-25951 #close Change-Id: I0f4bbfd72fc1126c2aaba41da3233a33d0433645
Diffstat (limited to 'res/res_agi.c')
-rw-r--r--res/res_agi.c40
1 files changed, 39 insertions, 1 deletions
diff --git a/res/res_agi.c b/res/res_agi.c
index f6ce74960..e8249e202 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -3736,6 +3736,24 @@ static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, ch
return AGI_RESULT_SUCCESS;
}
+
+AST_LIST_HEAD_NOLOCK(deferred_frames, ast_frame);
+
+static void queue_deferred_frames(struct deferred_frames *deferred_frames,
+ struct ast_channel *chan)
+{
+ struct ast_frame *f;
+
+ if (!AST_LIST_EMPTY(deferred_frames)) {
+ ast_channel_lock(chan);
+ while ((f = AST_LIST_REMOVE_HEAD(deferred_frames, frame_list))) {
+ ast_queue_frame_head(chan, f);
+ ast_frfree(f);
+ }
+ ast_channel_unlock(chan);
+ }
+}
+
static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
{
struct ast_channel *c;
@@ -3754,6 +3772,9 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
const char *sighup_str;
const char *exit_on_hangup_str;
int exit_on_hangup;
+ struct deferred_frames deferred_frames;
+
+ AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);
ast_channel_lock(chan);
sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP");
@@ -3815,8 +3836,20 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
/* Write, ignoring errors */
if (write(agi->audio, f->data.ptr, f->datalen) < 0) {
}
+ ast_frfree(f);
+ } else if (ast_is_deferrable_frame(f)) {
+ struct ast_frame *dup_f;
+
+ if ((dup_f = ast_frisolate(f))) {
+ AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list);
+ }
+
+ if (dup_f != f) {
+ ast_frfree(f);
+ }
+ } else {
+ ast_frfree(f);
}
- ast_frfree(f);
}
} else if (outfd > -1) {
size_t len = sizeof(buf);
@@ -3864,6 +3897,8 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
buf[buflen - 1] = '\0';
}
+ queue_deferred_frames(&deferred_frames, chan);
+
if (agidebug)
ast_verbose("<%s>AGI Rx << %s\n", ast_channel_name(chan), buf);
cmd_status = agi_handle_command(chan, agi, buf, dead);
@@ -3885,6 +3920,9 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
}
}
}
+
+ queue_deferred_frames(&deferred_frames, chan);
+
if (agi->speech) {
ast_speech_destroy(agi->speech);
}