[svn-commits] rmudgett: branch 12 r414747 - in /branches/12: CHANGES apps/app_agent_pool.c

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed May 28 11:24:58 CDT 2014


Author: rmudgett
Date: Wed May 28 11:24:54 2014
New Revision: 414747

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=414747
Log:
app_agent_pool: Return to dialplan if the agent fails to ack the call.

Improvements to the agent pool functionality.

* AgentRequest no longer hangs up the caller if the agent fails to connect
with the caller.  It now continues in the dialplan.

* AgentRequest returns AGENT_STATUS set to NOT_CONNECTED if the agent
failed to connect with the call.  Most likely because the agent did not
acknowledge the call in time or got disconnected.

* The agent alerting play file configured by the agent.conf custom_beep
option can now be disabled by setting the option to an empty string.  The
agent is effectively alerted to a call presence when MOH stops.

* Fixed bridge reference leak when the agent connects with a caller.

ASTERISK-23499 #close
Reported by: Matt Jordan

Review: https://reviewboard.asterisk.org/r/3551/

Modified:
    branches/12/CHANGES
    branches/12/apps/app_agent_pool.c

Modified: branches/12/CHANGES
URL: http://svnview.digium.com/svn/asterisk/branches/12/CHANGES?view=diff&rev=414747&r1=414746&r2=414747
==============================================================================
--- branches/12/CHANGES (original)
+++ branches/12/CHANGES Wed May 28 11:24:54 2014
@@ -7,6 +7,17 @@
 === and the other UPGRADE files for older releases.
 ===
 ==============================================================================
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 12.3.0 to Asterisk 12.4.0 ------------
+------------------------------------------------------------------------------
+
+AgentRequest
+------------------
+ * Returns new AGENT_STATUS value "NOT_CONNECTED" if the agent fails to
+   connect with an incoming caller after being alerted to the presence
+   of the incoming caller.  The most likely reason this would happen is
+   the agent did not acknowledge the call in time.
 
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 12.2.0 to Asterisk 12.3.0 ------------

Modified: branches/12/apps/app_agent_pool.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/apps/app_agent_pool.c?view=diff&rev=414747&r1=414746&r2=414747
==============================================================================
--- branches/12/apps/app_agent_pool.c (original)
+++ branches/12/apps/app_agent_pool.c Wed May 28 11:24:54 2014
@@ -108,13 +108,16 @@
 			<parameter name="AgentId" required="true" />
 		</syntax>
 		<description>
-			<para>Request an agent to connect with the channel.  Failure to find and
-			alert an agent will continue in the dialplan with <variable>AGENT_STATUS</variable> set.</para>
+			<para>Request an agent to connect with the channel.  Failure to find,
+			alert the agent, or acknowledge the call will continue in the dialplan
+			with <variable>AGENT_STATUS</variable> set.</para>
 			<para><variable>AGENT_STATUS</variable> enumeration values:</para>
 			<enumlist>
 				<enum name = "INVALID"><para>The specified agent is invalid.</para></enum>
 				<enum name = "NOT_LOGGED_IN"><para>The agent is not available.</para></enum>
 				<enum name = "BUSY"><para>The agent is on another call.</para></enum>
+				<enum name = "NOT_CONNECTED"><para>The agent did not connect with the
+				call.  The agent most likely did not acknowledge the call.</para></enum>
 				<enum name = "ERROR"><para>Alerting the agent failed.</para></enum>
 			</enumlist>
 		</description>
@@ -545,7 +548,7 @@
 	aco_option_register(&cfg_info, "wrapuptime", ACO_EXACT, agent_types, "0", OPT_UINT_T, 0, FLDSET(struct agent_cfg, wrapup_time));
 	aco_option_register(&cfg_info, "musiconhold", ACO_EXACT, agent_types, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, moh));
 	aco_option_register(&cfg_info, "recordagentcalls", ACO_EXACT, agent_types, "no", OPT_BOOL_T, 1, FLDSET(struct agent_cfg, record_agent_calls));
-	aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 1, STRFLDSET(struct agent_cfg, beep_sound));
+	aco_option_register(&cfg_info, "custom_beep", ACO_EXACT, agent_types, "beep", OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, beep_sound));
 	aco_option_register(&cfg_info, "fullname", ACO_EXACT, agent_types, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct agent_cfg, full_name));
 
 	if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
@@ -761,7 +764,7 @@
 
 	ast_party_connected_line_free(&doomed->waiting_colp);
 	if (doomed->caller_bridge) {
-		ast_bridge_destroy(doomed->caller_bridge, AST_CAUSE_USER_BUSY);
+		ast_bridge_destroy(doomed->caller_bridge, 0);
 		doomed->caller_bridge = NULL;
 	}
 	if (doomed->logged) {
@@ -995,6 +998,23 @@
 
 /*!
  * \internal
+ * \brief Callback to clear AGENT_STATUS on the caller channel.
+ *
+ * \param bridge_channel Which channel to operate on.
+ * \param payload Data to pass to the callback. (NULL if none).
+ * \param payload_size Size of the payload if payload is non-NULL.  A number otherwise.
+ *
+ * \note The payload MUST NOT have any resources that need to be freed.
+ *
+ * \return Nothing
+ */
+static void clear_agent_status(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
+{
+	pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", NULL);
+}
+
+/*!
+ * \internal
  * \brief Connect the agent with the waiting caller.
  * \since 12.0.0
  *
@@ -1028,12 +1048,18 @@
 		NULL, 0);
 	if (res) {
 		/* Reset agent. */
-		ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
+		ast_bridge_destroy(caller_bridge, 0);
 		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
 			AST_CAUSE_NORMAL_CLEARING);
 		return;
 	}
-	ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
+	res = ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0)
+		|| ast_bridge_channel_write_callback(bridge_channel, 0, clear_agent_status, NULL, 0);
+	if (res) {
+		/* Reset agent. */
+		ast_bridge_destroy(caller_bridge, 0);
+		return;
+	}
 
 	if (record_agent_calls) {
 		struct ast_bridge_features_automixmonitor options = {
@@ -1046,6 +1072,8 @@
 		 */
 		ast_bridge_features_do(AST_BRIDGE_BUILTIN_AUTOMIXMON, bridge_channel, &options);
 	}
+
+	ao2_t_ref(caller_bridge, -1, "Agent successfully in caller_bridge");
 }
 
 static int bridge_agent_hold_ack(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
@@ -1462,7 +1490,7 @@
 	agent_devstate_changed(agent->username);
 
 	if (caller_bridge) {
-		ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
+		ast_bridge_destroy(caller_bridge, 0);
 	}
 
 	ast_channel_lock(logged);
@@ -1548,7 +1576,7 @@
 		agent_unlock(agent);
 		ao2_ref(cfg_old, -1);
 		if (caller_bridge) {
-			ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
+			ast_bridge_destroy(caller_bridge, 0);
 		}
 
 		if (agent->state == AGENT_STATE_LOGGING_OUT
@@ -1674,7 +1702,7 @@
 		agent->caller_bridge = NULL;
 		agent_unlock(agent);
 		if (caller_bridge) {
-			ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY);
+			ast_bridge_destroy(caller_bridge, 0);
 		}
 		return;
 	}
@@ -1690,9 +1718,11 @@
 	struct agent_pvt *agent = hook_pvt;
 
 	if (agent->state == AGENT_STATE_CALL_PRESENT) {
-		ast_verb(3, "Agent '%s' did not respond.  Safety timeout.\n", agent->username);
-		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
-			AST_CAUSE_USER_BUSY);
+		ast_log(LOG_WARNING, "Agent '%s' process did not respond.  Safety timeout.\n",
+			agent->username);
+		pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
+
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
 		caller_abort_agent(agent);
 	}
 
@@ -1767,6 +1797,49 @@
 
 	return ast_bridge_channel_queue_control_data(bridge_channel,
 		AST_CONTROL_CONNECTED_LINE, data, datalen);
+}
+
+/*!
+ * \internal
+ * \brief Caller joined the bridge event callback.
+ *
+ * \param bridge_channel Channel executing the feature
+ * \param hook_pvt Private data passed in when the hook was created
+ *
+ * \retval 0 Keep the callback hook.
+ * \retval -1 Remove the callback hook.
+ */
+static int caller_joined_bridge(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	struct agent_pvt *agent = hook_pvt;
+	struct ast_bridge_channel *logged;
+	int res;
+
+	logged = agent_bridge_channel_get_lock(agent);
+	if (!logged) {
+		ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
+		pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_LOGGED_IN");
+
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
+		caller_abort_agent(agent);
+		return -1;
+	}
+
+	res = send_alert_to_agent(logged, agent->username);
+	ast_bridge_channel_unlock(logged);
+	ao2_ref(logged, -1);
+	if (res) {
+		ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
+		pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "ERROR");
+
+		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
+		caller_abort_agent(agent);
+		return -1;
+	}
+
+	pbx_builtin_setvar_helper(bridge_channel->chan, "AGENT_STATUS", "NOT_CONNECTED");
+	ast_indicate(bridge_channel->chan, AST_CONTROL_RINGING);
+	return -1;
 }
 
 /*!
@@ -1826,24 +1899,26 @@
 		return -1;
 	}
 
+	/* Setup the alert agent on caller joining the bridge hook. */
+	ao2_ref(agent, +1);
+	if (ast_bridge_join_hook(&caller_features, caller_joined_bridge, agent,
+		__ao2_cleanup, 0)) {
+		ao2_ref(agent, -1);
+		ast_bridge_features_cleanup(&caller_features);
+		return -1;
+	}
+
 	caller_bridge = ast_bridge_basic_new();
 	if (!caller_bridge) {
 		ast_bridge_features_cleanup(&caller_features);
 		return -1;
 	}
-
-	/* Get COLP for agent. */
-	ast_party_connected_line_init(&connected);
-	ast_channel_lock(chan);
-	ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
-	ast_channel_unlock(chan);
 
 	agent_lock(agent);
 	switch (agent->state) {
 	case AGENT_STATE_LOGGED_OUT:
 	case AGENT_STATE_LOGGING_OUT:
 		agent_unlock(agent);
-		ast_party_connected_line_free(&connected);
 		ast_bridge_destroy(caller_bridge, 0);
 		ast_bridge_features_cleanup(&caller_features);
 		ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
@@ -1857,7 +1932,6 @@
 		break;
 	default:
 		agent_unlock(agent);
-		ast_party_connected_line_free(&connected);
 		ast_bridge_destroy(caller_bridge, 0);
 		ast_bridge_features_cleanup(&caller_features);
 		ast_verb(3, "Agent '%s' is busy.\n", agent->username);
@@ -1867,38 +1941,55 @@
 	agent_unlock(agent);
 	agent_devstate_changed(agent->username);
 
+	/* Get COLP for agent. */
+	ast_party_connected_line_init(&connected);
+	ast_channel_lock(chan);
+	ast_connected_line_copy_from_caller(&connected, ast_channel_caller(chan));
+	ast_channel_unlock(chan);
+
 	logged = agent_bridge_channel_get_lock(agent);
 	if (!logged) {
 		ast_party_connected_line_free(&connected);
+		caller_abort_agent(agent);
 		ast_bridge_destroy(caller_bridge, 0);
 		ast_bridge_features_cleanup(&caller_features);
 		ast_verb(3, "Agent '%s' not logged in.\n", agent->username);
 		pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN");
-		caller_abort_agent(agent);
 		return 0;
 	}
 
 	send_colp_to_agent(logged, &connected);
-	ast_party_connected_line_free(&connected);
-
-	res = send_alert_to_agent(logged, agent->username);
 	ast_bridge_channel_unlock(logged);
 	ao2_ref(logged, -1);
-	if (res) {
-		ast_bridge_destroy(caller_bridge, 0);
-		ast_bridge_features_cleanup(&caller_features);
-		ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username);
+	ast_party_connected_line_free(&connected);
+
+	if (ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
+		AST_BRIDGE_JOIN_PASS_REFERENCE)) {
+		caller_abort_agent(agent);
+		ast_verb(3, "Agent '%s': Caller %s failed to join the bridge.\n",
+			agent->username, ast_channel_name(chan));
 		pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR");
-		caller_abort_agent(agent);
-		return 0;
-	}
-
-	ast_indicate(chan, AST_CONTROL_RINGING);
-	ast_bridge_join(caller_bridge, chan, NULL, &caller_features, NULL,
-		AST_BRIDGE_JOIN_PASS_REFERENCE);
+	}
 	ast_bridge_features_cleanup(&caller_features);
 
-	return -1;
+	/* Determine if we need to continue in the dialplan after the bridge. */
+	ast_channel_lock(chan);
+	if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
+		/*
+		 * The bridge was broken for a hangup that isn't real.
+		 * Don't run the h extension, because the channel isn't
+		 * really hung up.  This should really only happen with
+		 * AST_SOFTHANGUP_ASYNCGOTO.
+		 */
+		res = 0;
+	} else {
+		res = ast_check_hangup(chan)
+			|| ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
+			|| ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENT_STATUS"));
+	}
+	ast_channel_unlock(chan);
+
+	return res ? -1 : 0;
 }
 
 /*!
@@ -2040,9 +2131,8 @@
 
 	agent_login_channel_config(agent, chan);
 
-	if (!ast_test_flag(&opts, OPT_SILENT)
-		&& !ast_streamfile(chan, "agent-loginok", ast_channel_language(chan))) {
-		ast_waitstream(chan, "");
+	if (!ast_test_flag(&opts, OPT_SILENT)) {
+		ast_stream_and_wait(chan, "agent-loginok", AST_DIGIT_NONE);
 	}
 
 	ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", agent->username,




More information about the svn-commits mailing list