[asterisk-commits] mmichelson: branch mmichelson/queue_bugbug r394824 - /team/mmichelson/queue_b...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jul 19 17:17:18 CDT 2013


Author: mmichelson
Date: Fri Jul 19 17:17:16 2013
New Revision: 394824

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=394824
Log:
Completely rework local optimization handling.

The issue here is that when local channels are involved, the other
side of the local channel is a complete mystery to you. There could
be one or more parties on the other side of the local channel. If
more than one party is in that local channel, then the result could
be that we end up with the queue needing to monitor a multi-party
call. Any channels that originate from the caller's side of the
original bridge will be considered callers and any channels that
originate from the agent's side of the of the bridge will be
considered agents. We keep a container for each and modify the
containers as necessary when local channel optimizations occur.
We also track hangups accordingly and can see when the last
of the callers or last of the agents has hung up. It is not
until one of these happens that we send an agent complete event.

This is pretty overkill considering that 99.999% of the time,
even if there are local channels involved, we'll be dealing with
a two-party call. However, for those weirdos out there that end
up with local channels that call local channels that call into
a bridge that then splits out into seven local channels that
each bridge into more local channels that then bridge to actual
real things, this should work for them too. Hopefully.

Oh, this isn't tested. Testing and bugfixing begins Monday.


Modified:
    team/mmichelson/queue_bugbug/apps/app_queue.c

Modified: team/mmichelson/queue_bugbug/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/queue_bugbug/apps/app_queue.c?view=diff&rev=394824&r1=394823&r2=394824
==============================================================================
--- team/mmichelson/queue_bugbug/apps/app_queue.c (original)
+++ team/mmichelson/queue_bugbug/apps/app_queue.c Fri Jul 19 17:17:16 2013
@@ -5172,21 +5172,17 @@
 }
 
 struct local_optimization {
-	AST_DECLARE_STRING_FIELDS(
-		AST_STRING_FIELD(dest_bridge_uniqueid);
-		AST_STRING_FIELD(dest_chan_uniqueid);
-		AST_STRING_FIELD(source_chan_uniqueid);
-	);
-	int in_progress;
+	char dest_bridge[AST_UUID_STR_LEN];
+	struct ao2_container *source_channels;
+	struct ao2_container *dest_channels;
 	unsigned int id;
+	enum ast_unreal_channel_indicator dest;
 };
 
 struct queue_stasis_data {
-	AST_DECLARE_STRING_FIELDS(
-		AST_STRING_FIELD(caller_uniqueid);
-		AST_STRING_FIELD(agent_uniqueid);
-		AST_STRING_FIELD(bridge_uniqueid);
-	);
+	char bridge_uniqueid[AST_UUID_STR_LEN];
+	struct ao2_container *callers;
+	struct ao2_container *agents;
 	struct call_queue *queue;
 	struct member *member;
 	struct stasis_topic *caller_topic;
@@ -5197,8 +5193,7 @@
 	int dying;
 	struct stasis_subscription *bridge_sub;
 	struct stasis_subscription *channel_sub;
-	struct local_optimization caller_optimize;
-	struct local_optimization agent_optimize;
+	struct ao2_container *optimizations;
 };
 
 static void queue_stasis_data_destructor(void *obj)
@@ -5209,9 +5204,9 @@
 
 	ao2_cleanup(queue_data->member);
 	ao2_cleanup(queue_data->queue);
-	ast_string_field_free_memory(&queue_data->caller_optimize);
-	ast_string_field_free_memory(&queue_data->agent_optimize);
-	ast_string_field_free_memory(queue_data);
+	ao2_cleanup(queue_data->callers);
+	ao2_cleanup(queue_data->agents);
+	ao2_cleanup(queue_data->optimizations);
 }
 
 static void remove_stasis_subscriptions(struct queue_stasis_data *queue_data)
@@ -5223,6 +5218,46 @@
 	queue_data->channel_sub = stasis_unsubscribe(queue_data->channel_sub);
 }
 
+static int optimization_hash(const void *obj, int flags)
+{
+	const struct local_optimization *optimize;
+	const unsigned int *key;
+
+	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+	case OBJ_KEY:
+		key = obj;
+		return *key;
+	case OBJ_PARTIAL_KEY:
+		ast_assert(0);
+		return 0;
+	case OBJ_POINTER:
+	default:
+		optimize = obj;
+		return optimize->id;
+	}
+}
+
+static int optimization_cmp(void *obj, void *arg, int flags)
+{
+	struct local_optimization *optimize1 = obj;
+	struct local_optimization *optimize2;
+	unsigned int *key2;
+
+	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+	case OBJ_KEY:
+		key2 = arg;
+		return *key2 == optimize1->id ? CMP_MATCH : 0;
+	case OBJ_PARTIAL_KEY:
+		ast_assert(0);
+		return 0;
+	case OBJ_POINTER:
+		optimize2 = arg;
+		return optimize1->id == optimize2->id ? CMP_MATCH : 0;
+	default:
+		return CMP_STOP;
+	}
+}
+
 static struct queue_stasis_data *queue_stasis_data_alloc(struct queue_ent *qe,
 		struct ast_channel *peer, struct member *mem, time_t holdstart,
 		time_t starttime, int callcompletedinsl)
@@ -5234,16 +5269,26 @@
 		return NULL;
 	}
 
-	if (ast_string_field_init(queue_data, 64) || ast_string_field_init(&queue_data->agent_optimize, 64) ||
-			ast_string_field_init(&queue_data->caller_optimize, 64)) {
+	queue_data->callers = ast_str_container_alloc(5);
+	if (!queue_data->callers || ast_str_container_add(queue_data->callers, 
+				ast_channel_uniqueid(qe->chan))) {
 		ao2_cleanup(queue_data);
 		return NULL;
 	}
 
-	ast_string_field_set(queue_data, caller_uniqueid, ast_channel_uniqueid(qe->chan));
-	ast_log(LOG_NOTICE, "Initial caller uniqueid is %s\n", queue_data->caller_uniqueid);
-	ast_string_field_set(queue_data, agent_uniqueid, ast_channel_uniqueid(peer));
-	ast_log(LOG_NOTICE, "Initial agent uniqueid is %s\n", queue_data->agent_uniqueid);
+	queue_data->agents = ast_str_container_alloc(5);
+	if (!queue_data->agents || ast_str_container_add(queue_data->agents,
+				ast_channel_uniqueid(peer))) {
+		ao2_cleanup(queue_data);
+		return NULL;
+	}
+
+	queue_data->optimizations = ao2_container_alloc(5, optimization_hash, optimization_cmp);
+	if (!queue_data->optimizations) {
+		ao2_cleanup(queue_data);
+		return NULL;
+	}
+
 	queue_data->queue = queue_ref(qe->parent);
 	queue_data->starttime = starttime;
 	queue_data->holdstart = holdstart;
@@ -5269,20 +5314,29 @@
 	
 	if (ast_channel_entered_bridge_type() == stasis_message_type(msg)) {
 		struct ast_bridge_blob *enter_blob = stasis_message_data(msg);
+		const char *caller;
 
 		if (!ast_strlen_zero(queue_data->bridge_uniqueid)) {
 			return;
 		}
 
-		if (!strcmp(enter_blob->channel->uniqueid, queue_data->caller_uniqueid)) {
-			ast_string_field_set(queue_data, bridge_uniqueid,
-					enter_blob->bridge->uniqueid);
+		caller = ao2_callback(queue_data->callers, 0, NULL, NULL);
+		
+		if (!caller) {
+			return;
+		}
+
+		if (!strcmp(enter_blob->channel->uniqueid, caller)) {
+			ast_copy_string(queue_data->bridge_uniqueid, enter_blob->bridge->uniqueid,
+					sizeof(queue_data->bridge_uniqueid));
 			ast_log(LOG_NOTICE, "Detected entry into bridge %s\n", queue_data->bridge_uniqueid);
 		}
 	} else if (ast_blind_transfer_type() == stasis_message_type(msg)) {
 		struct ast_bridge_blob *blind_blob = stasis_message_data(msg);
-		RAII_VAR(struct ast_channel_snapshot *, caller, NULL, ao2_cleanup);
-		RAII_VAR(struct ast_channel_snapshot *, agent, NULL, ao2_cleanup);
+		const char *caller;
+		const char *agent;
+		RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
+		RAII_VAR(struct ast_channel_snapshot *, agent_snapshot, NULL, ao2_cleanup);
 
 		if (ast_json_integer_get(ast_json_object_get(blind_blob->blob, "result")) == AST_BRIDGE_TRANSFER_FAIL) {
 			return;
@@ -5295,27 +5349,32 @@
 			return;
 		}
 
-		caller = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
-		agent = ast_channel_snapshot_get_latest(queue_data->agent_uniqueid);
+		caller = ao2_callback(queue_data->callers, 0, NULL, NULL);
+		agent = ao2_callback(queue_data->agents, 0, NULL, NULL);
+
+		caller_snapshot = ast_channel_snapshot_get_latest(caller);
+		agent_snapshot = ast_channel_snapshot_get_latest(agent);
 
 		ao2_unlock(queue_data);
 
 		ast_log(LOG_NOTICE, "Detected blind transfer in queue %s\n", queue_data->queue->name);
-		ast_queue_log(queue_data->queue->name, caller->uniqueid, queue_data->member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
+		ast_queue_log(queue_data->queue->name, caller, queue_data->member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
 				ast_json_string_get(ast_json_object_get(blind_blob->blob, "exten")),
 				ast_json_string_get(ast_json_object_get(blind_blob->blob, "context")),
 				(long) queue_data->starttime - queue_data->holdstart,
 				(long) time(NULL) - queue_data->starttime, queue_data->caller_pos);
 
-		send_agent_complete(queue_data->queue->name, caller, agent, queue_data->member,
+		send_agent_complete(queue_data->queue->name, caller_snapshot, agent_snapshot, queue_data->member,
 				queue_data->holdstart, queue_data->starttime, TRANSFER);
 		update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
 				time(NULL) - queue_data->starttime);
 		remove_stasis_subscriptions(queue_data);
 	} else if (ast_attended_transfer_type() == stasis_message_type(msg)) {
 		struct ast_attended_transfer_message *atxfer_msg = stasis_message_data(msg);
-		RAII_VAR(struct ast_channel_snapshot *, caller, NULL, ao2_cleanup);
-		RAII_VAR(struct ast_channel_snapshot *, agent, NULL, ao2_cleanup);
+		const char *caller;
+		const char *agent;
+		RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
+		RAII_VAR(struct ast_channel_snapshot *, agent_snapshot, NULL, ao2_cleanup);
 
 		ast_log(LOG_NOTICE, "Getting a transfer event...\n");
 		/* BUGBUG Once atxfer_features is merged, we need to also return when
@@ -5340,8 +5399,11 @@
 			return;
 		}
 
-		caller = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
-		agent = ast_channel_snapshot_get_latest(queue_data->agent_uniqueid);
+		caller = ao2_callback(queue_data->callers, 0, NULL, NULL);
+		agent = ao2_callback(queue_data->agents, 0, NULL, NULL);
+
+		caller_snapshot = ast_channel_snapshot_get_latest(caller);
+		agent_snapshot = ast_channel_snapshot_get_latest(agent);
 
 		ao2_unlock(queue_data);
 
@@ -5350,7 +5412,7 @@
 		 * sense to specify an exten and context on an attended transfer. Perhaps separate queue log message
 		 * type?
 		 */
-		send_agent_complete(queue_data->queue->name, caller, agent, queue_data->member,
+		send_agent_complete(queue_data->queue->name, caller_snapshot, agent_snapshot, queue_data->member,
 				queue_data->holdstart, queue_data->starttime, TRANSFER);
 		update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
 				time(NULL) - queue_data->starttime);
@@ -5358,128 +5420,216 @@
 	}
 }
 
+static int get_ids(void *obj, void *arg, void *data, int flags)
+{
+	struct ast_channel *peer = obj;
+	struct ao2_container *id_container = arg;
+	struct ast_channel *exception = data;
+
+	if (peer != exception) {
+		ast_str_container_add(id_container, ast_channel_uniqueid(peer));
+	}
+
+	return 0;
+}
+
+static int get_channels(struct ao2_container *id_container, struct ast_channel_snapshot *snapshot)
+{
+	RAII_VAR(struct ast_channel *, chan, NULL, ast_channel_cleanup);
+	RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+	RAII_VAR(struct ao2_container *, peers, NULL, ao2_cleanup);
+
+	chan = ast_channel_get_by_name(snapshot->name);
+	if (!chan) {
+		return -1;
+	}
+
+	ast_channel_lock(chan);
+	bridge = ast_channel_get_bridge(chan);
+	ast_channel_unlock(chan);
+
+	if (!bridge) {
+		return -1;
+	}
+
+	ast_bridge_lock(bridge);
+	peers = ast_bridge_peers(bridge);
+	ast_bridge_unlock(bridge);
+
+	if (!peers) {
+		return -1;
+	}
+
+	ao2_callback_data(peers, 0, get_ids, id_container, chan);
+	return 0;
+}
+
+static void optimize_destructor(void *obj)
+{
+	struct local_optimization *optimize = obj;
+
+	ao2_cleanup(optimize->source_channels);
+	ao2_cleanup(optimize->dest_channels);
+}
+
+static struct local_optimization *local_optimization_alloc(struct queue_stasis_data *queue_data,
+		unsigned int id, enum ast_unreal_channel_indicator dest, struct ast_channel_snapshot *local_one,
+		struct ast_channel_snapshot *local_two)
+{
+	RAII_VAR(struct local_optimization *, optimize, NULL, ao2_cleanup);
+
+	optimize = ao2_alloc(sizeof(*optimize), optimize_destructor);
+	if (!optimize) {
+		return NULL;
+	}
+
+	optimize->source_channels = ast_str_container_alloc(5);
+	if (!optimize->source_channels) {
+		return NULL;
+	}
+
+	optimize->dest_channels = ast_str_container_alloc(5);
+	if (!optimize->dest_channels) {
+		return NULL;
+	}
+
+	optimize->id = id;
+	optimize->dest = dest;
+	
+	if (get_channels(optimize->source_channels, dest == AST_UNREAL_OWNER ? local_two : local_one)) {
+		return NULL;
+	}
+
+	if (get_channels(optimize->dest_channels, dest == AST_UNREAL_OWNER ? local_one : local_two)) {
+		return NULL;
+	}
+
+	ast_copy_string(optimize->dest_bridge, dest == AST_UNREAL_OWNER ? local_one->bridgeid : local_two->bridgeid,
+			sizeof(optimize->dest_bridge));
+
+	ao2_ref(optimize, +1);
+	return optimize;
+}
+
 static void handle_local_optimization_begin(struct queue_stasis_data *queue_data, struct stasis_message *msg)
 {
+	RAII_VAR(struct local_optimization *, optimize, NULL, ao2_cleanup);
 	struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
 	struct ast_channel_snapshot *local_one = ast_multi_channel_blob_get_channel(optimization_blob, "1");
 	struct ast_channel_snapshot *local_two = ast_multi_channel_blob_get_channel(optimization_blob, "2");
-	struct ast_channel_snapshot *source = ast_multi_channel_blob_get_channel(optimization_blob, "source");
 	enum ast_unreal_channel_indicator dest;
-	struct local_optimization *optimization;
+	int bridge_on_local_one;
 	unsigned int id;
 
-	if (!strcmp(local_one->uniqueid, queue_data->agent_uniqueid)) {
-		optimization = &queue_data->agent_optimize;
-	} else if (!strcmp(local_two->uniqueid, queue_data->caller_uniqueid)) {
-		optimization = &queue_data->caller_optimize;
+	if (!strcmp(local_one->bridgeid, queue_data->bridge_uniqueid)) {
+		bridge_on_local_one = 1;
+	} else if (!strcmp(local_two->bridgeid, queue_data->bridge_uniqueid)) {
+		bridge_on_local_one = 0;
 	} else {
+		/* We're not involved here */
 		return;
 	}
 
-	/* First going to make sure things are detected properly. */
-	ast_log(LOG_NOTICE, "OPTIMIZATION IS HAPPENING. PREPARE FOR DUMPAGE!\n");
-	ast_log(LOG_NOTICE, "Local ;1 channel is %s\n", local_one->name);
-	ast_log(LOG_NOTICE, "Local ;2 channel is %s\n", local_two->name);
-	if (source) {
-		ast_log(LOG_NOTICE, "Source channel is %s\n", source->name);
-	}
-
+	id = ast_json_integer_get(ast_json_object_get(ast_multi_channel_blob_get_json(optimization_blob), "id"));
 	dest = ast_json_integer_get(ast_json_object_get(ast_multi_channel_blob_get_json(optimization_blob), "dest"));
 
-	ast_log(LOG_NOTICE, "dest is %s\n", dest == AST_UNREAL_OWNER ? "OWNER" : "CHAN");
-
-	ast_log(LOG_NOTICE, "Destination bridge is %s\n",
-			dest == AST_UNREAL_OWNER ? local_one->bridgeid : local_two->bridgeid);
+	optimize = local_optimization_alloc(queue_data, id, dest, local_one, local_two);
+	if (!optimize) {
+		ast_log(LOG_WARNING, "Unable to deal properly with local optimization. Will not be able to report "
+				"when Queue call has been completed\n");
+		return;
+	}
+	ao2_link(queue_data->optimizations, optimize);
+
+	return;
+}
+
+static void update_ids(struct ao2_container *to_update, struct ao2_container *new_values)
+{
+	ao2_callback(to_update, OBJ_NOLOCK | OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, NULL, NULL);
+	ao2_container_dup(to_update, new_values, OBJ_NOLOCK);
+}
+
+static void handle_local_optimization_end(struct queue_stasis_data *queue_data, struct stasis_message *msg)
+{
+	struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
+	RAII_VAR(struct local_optimization *, optimize, NULL, ao2_cleanup);
+	unsigned int id;
+	unsigned int success;
 
 	id = ast_json_integer_get(ast_json_object_get(ast_multi_channel_blob_get_json(optimization_blob), "id"));
 
-	ast_log(LOG_NOTICE, "ID for optimization is %u\n", id);
-
-	ast_string_field_set(optimization, dest_bridge_uniqueid, dest == AST_UNREAL_OWNER ? local_one->bridgeid : local_two->bridgeid);
-	ast_string_field_set(optimization, dest_chan_uniqueid, dest == AST_UNREAL_OWNER ? local_one->uniqueid : local_two->uniqueid);
-	if (source) {
-		ast_string_field_set(optimization, source_chan_uniqueid, source->uniqueid);
-	}
-	optimization->id = id;
-	optimization->in_progress = 1;
-}
-
-static void handle_local_optimization_end(struct queue_stasis_data *queue_data, struct stasis_message *msg)
-{
-	struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
-	struct ast_channel_snapshot *local_one = ast_multi_channel_blob_get_channel(optimization_blob, "1");
-	struct ast_channel_snapshot *local_two = ast_multi_channel_blob_get_channel(optimization_blob, "2");
-	struct local_optimization *optimization;
-	unsigned int id;
-	int is_caller;
-
-	if (!strcmp(local_one->uniqueid, queue_data->agent_uniqueid)) {
-		optimization = &queue_data->agent_optimize;
-		is_caller = 0;
-	} else if (!strcmp(local_two->uniqueid, queue_data->caller_uniqueid)) {
-		optimization = &queue_data->caller_optimize;
-		is_caller = 1;
+	optimize = ao2_find(queue_data->optimizations, &id, OBJ_KEY | OBJ_UNLINK);
+	if (!optimize) {
+		return;
+	}
+
+	success = ast_json_integer_get(ast_json_object_get(ast_multi_channel_blob_get_json(optimization_blob), "success"));
+	if (!success) {
+		/* If optimization was unsuccessful, we don't need to do anything */
+		return;
+	}
+
+	update_ids(optimize->dest == AST_UNREAL_OWNER ? queue_data->agents : queue_data->callers, optimize->source_channels);
+
+	if (strcmp(optimize->dest_bridge, queue_data->bridge_uniqueid)) {
+		update_ids(optimize->dest == AST_UNREAL_OWNER ? queue_data->callers : queue_data->agents, optimize->dest_channels);
+		ast_copy_string(queue_data->bridge_uniqueid, optimize->dest_bridge, sizeof(queue_data->bridge_uniqueid));
+	}
+}
+
+static void handle_hangup(struct queue_stasis_data *queue_data, struct stasis_message *msg)
+{
+	struct ast_channel_blob *channel_blob = stasis_message_data(msg);
+	const char *caller;
+	const char *agent;
+	RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel_snapshot *, agent_snapshot, NULL, ao2_cleanup);
+	enum agent_complete_reason reason;
+	const char *match;
+
+	ao2_lock(queue_data);
+
+	match = ao2_find(queue_data->callers, channel_blob->snapshot->uniqueid, OBJ_KEY | OBJ_NOLOCK | OBJ_UNLINK);
+	if (match) {
+		if (ao2_container_count(queue_data->callers) == 0) {
+			/* Last caller has hung up */
+			reason = CALLER;
+		} else {
+			/* Caller hung up, but others remain */
+			ao2_unlock(queue_data);
+			return;
+		}
 	} else {
-		return;
-	}
-
-	id = ast_json_integer_get(ast_json_object_get(ast_multi_channel_blob_get_json(optimization_blob), "id"));
-
-	if (!optimization->in_progress) {
-		ast_log(LOG_WARNING, "Told of a local optimization end when we had no previous begin\n");
-		return;
-	}
-
-	if (id != optimization->id) {
-		ast_log(LOG_WARNING, "Local optimization end event ID does not match begin (%u != %u)\n",
-				id, optimization->id);
-		return;
-	}
-
-	ast_string_field_set(queue_data, bridge_uniqueid, optimization->dest_bridge_uniqueid);
-	if (is_caller) {
-		ast_log(LOG_NOTICE, "Changing caller uniqueid from %s to %s\n",
-				queue_data->caller_uniqueid, optimization->source_chan_uniqueid);
-		ast_string_field_set(queue_data, caller_uniqueid, optimization->source_chan_uniqueid);
-	} else {
-		ast_log(LOG_NOTICE, "Changing agent uniqueid from %s to %s\n",
-				queue_data->agent_uniqueid, optimization->source_chan_uniqueid);
-		ast_string_field_set(queue_data, agent_uniqueid, optimization->source_chan_uniqueid);
-	}
-
-	optimization->in_progress = 0;
-}
-
-static void handle_hangup(struct queue_stasis_data *queue_data, struct stasis_message *msg)
-{
-	struct ast_channel_blob *channel_blob = stasis_message_data(msg);
-	RAII_VAR(struct ast_channel_snapshot *, caller, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_channel_snapshot *, agent, NULL, ao2_cleanup);
-	enum agent_complete_reason reason;
-
-	ao2_lock(queue_data);
-	if (!strcmp(channel_blob->snapshot->uniqueid, queue_data->caller_uniqueid)) {
-		reason = CALLER;
-	} else if (!strcmp(channel_blob->snapshot->uniqueid, queue_data->agent_uniqueid)) {
-		reason = AGENT;
-	} else {
-		ao2_unlock(queue_data);
-		return;
-	}
-
-	caller = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
-	agent = ast_channel_snapshot_get_latest(queue_data->agent_uniqueid);
+		match = ao2_find(queue_data->agents, channel_blob->snapshot->uniqueid, OBJ_KEY | OBJ_NOLOCK | OBJ_UNLINK);
+		if (match) {
+			if (ao2_container_count(queue_data->agents) == 0) {
+				/* Last agent has hung up */
+				reason = AGENT;
+			} else {
+				/* Agent hung up, but others remain */
+				ao2_unlock(queue_data);
+				return;
+			}
+		}
+	}
+
+	caller = reason == CALLER ? match : ao2_callback(queue_data->callers, 0, NULL, NULL);
+	agent = reason == AGENT ? match : ao2_callback(queue_data->agents, 0, NULL, NULL);
+
+	caller_snapshot = ast_channel_snapshot_get_latest(caller);
+	agent_snapshot = ast_channel_snapshot_get_latest(agent);
 
 	ao2_unlock(queue_data);
 
-	ast_queue_log(queue_data->queue->name, caller->uniqueid, queue_data->member->membername,
+	ast_queue_log(queue_data->queue->name, match, queue_data->member->membername,
 			reason == CALLER ? "COMPLETECALLER" : "COMPLETEAGENT", "%ld|%ld|%d",
 		(long) (queue_data->starttime - queue_data->holdstart),
 		(long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
 
 	ast_log(LOG_NOTICE, "Detected hangup request on channel %s\n", channel_blob->snapshot->name);
 
-	send_agent_complete(queue_data->queue->name, caller, agent, queue_data->member,
+	send_agent_complete(queue_data->queue->name, caller_snapshot, agent_snapshot, queue_data->member,
 			queue_data->holdstart, queue_data->starttime, reason);
 	update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
 			time(NULL) - queue_data->starttime);




More information about the asterisk-commits mailing list