[asterisk-commits] rmudgett: trunk r394417 - in /trunk: ./ apps/ channels/ configs/ include/aste...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Jul 15 18:21:02 CDT 2013


Author: rmudgett
Date: Mon Jul 15 18:20:55 2013
New Revision: 394417

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=394417
Log:
Replace chan_agent with app_agent_pool.

The ill conceived chan_agent is no more.  It is now replaced by
app_agent_pool.

Agents login using the AgentLogin() application as before.  The
AgentLogin() application no longer does any authentication.
Authentication is now the responsibility of the dialplan.  (Besides, the
authentication done by chan_agent did not match what the voice prompts
asked for.)

Sample extensions.conf
[login]
; Sample agent 1001 login
; Set COLP for in between calls so the agent does not see the last caller COLP.
exten => 1001,1,Set(CONNECTEDLINE(all)="Agent Waiting" <1001>)
; Give the agent DTMF transfer and disconnect features when connected to a caller.
same => n,Set(CHANNEL(dtmf-features)=TX)
same => n,AgentLogin(1001)
same => n,NoOp(AGENT_STATUS is ${AGENT_STATUS})
same => n,Hangup()

[caller]
; Sample caller direct connect to agent 1001
exten => 800,1,AgentRequest(1001)
same => n,NoOp(AGENT_STATUS is ${AGENT_STATUS})
same => n,Hangup()

; Sample caller going through a Queue to agent 1001
exten => 900,1,Queue(agent_q)
same => n,Hangup()

Sample queues.conf
[agent_q]
member => Local/800 at caller,,SuperAgent,Agent:1001

Under the hood operation overview:
1) Logged in agents wait for callers in an agents holding bridge.
2) Caller requests an agent using AgentRequest()
3) A basic bridge is created, the agent is notified, and caller joins the
   basic bridge to wait for the agent.
4) The agent is either automatically connected to the caller or must ack
   the call to connect.
5) The agent is moved from the agents holding bridge to the basic bridge.
6) The agent and caller talk.
7) The connection is ended by either party.
8) The agent goes back to the agents holding bridge.

To avoid some locking issues with the agent holding bridge, I needed to
make some changes to the after bridge callback support.  The after bridge
callback is now a list of requested callbacks with the last to be added
the only active callback.  The after bridge callback for failed callbacks
will always happen in the channel thread when the channel leaves the
bridging system or is destroyed.

(closes issue ASTERISK-21554)
Reported by: Matt Jordan

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

Added:
    trunk/apps/app_agent_pool.c
      - copied unchanged from r394412, team/rmudgett/bridge_phase/apps/app_agent_pool.c
Removed:
    trunk/channels/chan_agent.c
Modified:
    trunk/CHANGES
    trunk/UPGRADE.txt
    trunk/configs/agents.conf.sample
    trunk/configs/queues.conf.sample
    trunk/include/asterisk/bridging.h
    trunk/include/asterisk/config_options.h
    trunk/include/asterisk/stasis_channels.h
    trunk/main/bridging.c
    trunk/main/stasis_channels.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=394417&r1=394416&r2=394417
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Mon Jul 15 18:20:55 2013
@@ -14,10 +14,17 @@
 Applications
 ------------------
 
+AgentLogin
+------------------
+ * The application no longer does agent authentication.  The dialplan needs to
+   perform this function before running AgentLogin.  If the agent is already
+   logged in, dialplan will continue with the AGENT_STATUS channel variable
+   set to ALREADY_LOGGED_IN.
+
 AgentMonitorOutgoing
 ------------------
- * The 'c' option has been removed. It is not possible to modify the name of a
-   channel involved in a CDR.
+ * Application removed.  It was a holdover from when AgentCallbackLogin was
+   removed.
 
 ForkCDR
 ------------------
@@ -260,8 +267,8 @@
    of "CallerID" and "ConnectedID" to avoid confusion with similarly named
    parameters in the channel snapshot.
 
- * The "Agentlogin" and "Agentlogoff" events have been renamed "AgentLogin" and
-   "AgentLogoff" respectively.
+ * The AMI events "Agentlogin" and "Agentlogoff" have been renamed
+   "AgentLogin" and "AgentLogoff" respectively.
 
  * The "Channel" key used in the "AlarmClear", "Alarm", and "DNDState" has been
    renamed "DAHDIChannel" since it does not convey an Asterisk channel name.
@@ -453,6 +460,21 @@
    and pretending otherwise helps no one.
  * The AGENTUPDATECDR channel variable has also been removed, for the same
    reason as the updatecdr option.
+ * The driver is no longer a Data retrieval API data provider for the
+   AMI DataGet action.
+ * The endcall and enddtmf configuration options are removed.  Use the
+   dialplan function CHANNEL(dtmf-features) to set DTMF features on the agent
+   channel before calling AgentLogin.
+ * chan_agent is removed and replaced with AgentLogin and AgentRequest dialplan
+   applications.  Agents are connected with callers using the new AgentRequest
+   dialplan application.  The Agents:<agent-id> device state is available to
+   monitor the status of an agent.  See agents.conf.sample for valid
+   configuration options.
+
+chan_bridge
+------------------
+ * chan_bridge is removed and its functionality is incorporated into ConfBridge
+   itself.
 
 chan_local
 ------------------

Modified: trunk/UPGRADE.txt
URL: http://svnview.digium.com/svn/asterisk/trunk/UPGRADE.txt?view=diff&rev=394417&r1=394416&r2=394417
==============================================================================
--- trunk/UPGRADE.txt (original)
+++ trunk/UPGRADE.txt Mon Jul 15 18:20:55 2013
@@ -22,8 +22,8 @@
 ===========================================================
 
 AgentMonitorOutgoing
- - The 'c' option has been removed. It is not possible to modify the name of a
-   channel involved in a CDR.
+ - Application removed.  It was a holdover from when AgentCallbackLogin was
+   removed.
 
 NoCDR:
  - This application is deprecated. Please use the CDR_PROP function instead.
@@ -140,6 +140,15 @@
    and pretending otherwise helps no one.
  - The AGENTUPDATECDR channel variable has also been removed, for the same
    reason as the updatecdr option.
+ - chan_agent is removed and replaced with AgentLogin and AgentRequest dialplan
+   applications.  Agents are connected with callers using the new AgentRequest
+   dialplan application.  The Agents:<agent-id> device state is available to
+   monitor the status of an agent.  See agents.conf.sample for valid
+   configuration options.
+
+chan_bridge
+ - chan_bridge is removed and its functionality is incorporated into ConfBridge
+   itself.
 
 chan_dahdi:
  - Analog port dialing and deferred DTMF dialing for PRI now distinguishes

Modified: trunk/configs/agents.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/agents.conf.sample?view=diff&rev=394417&r1=394416&r2=394417
==============================================================================
--- trunk/configs/agents.conf.sample (original)
+++ trunk/configs/agents.conf.sample Mon Jul 15 18:20:55 2013
@@ -1,102 +1,70 @@
 ;
-; Agent configuration
+; Agent pool configuration
 ;
 
 [general]
+; The general section of this config is not currently used, but reserved
+; for future use.
 
-[agents]
+;[agent-id]
+; Define ackcall to require the agent to give a DTMF acknowledgement
+; when the agent receives a call.
+; The channel variable AGENTACKCALL overrides on agent login.
+; Default is "no".
+;ackcall=no
 ;
-; Define maxlogintries to allow agent to try max logins before
-; failed.
-; default to 3
+; Set what DTMF key sequence the agent should use to acknowledge a call.
+; The channel variable AGENTACCEPTDTMF overrides on agent login.
+; This option is ignored unless ackcall is enabled.
+; Default is "#".
+;acceptdtmf=##
 ;
-;maxlogintries=5
-;
-;
-; Define autologoff times if appropriate.  This is how long
-; the phone has to ring with no answer before the agent is
-; automatically logged off (in seconds)
-;
+; Set how many seconds a call for the agent has to wait for the agent to
+; acknowledge the call before the agent is automatically logged off.  If
+; set to zero then the call will wait forever for the agent to acknowledge.
+; The channel variable AGENTAUTOLOGOFF overrides on agent login.
+; This option is ignored unless ackcall is enabled.
+; Default is 0.
 ;autologoff=15
 ;
-; Define autologoffunavail to have agents automatically logged
-; out when the extension that they are at returns a CHANUNAVAIL
-; status when a call is attempted to be sent there.
-; Default is "no".
-;
-;autologoffunavail=yes
-;
-; Define ackcall to require a DTMF acknowledgement when
-; a logged-in agent receives a call.  Default is "no".
-; Use the acceptdtmf option to configure what DTMF key
-; press should be used to acknowledge the call. The
-; default is '#'.
-;
-;ackcall=no
-;acceptdtmf=#
-;
-; Define endcall to allow an agent to hangup a call with a
-; DTMF keypress. Default is "yes". Use the enddtmf option to
-; configure which DTMF key will end a call. The default is
-; '*'.
-;
-;endcall=yes
-;enddtmf=*
-;
-; Define wrapuptime.  This is the minimum amount of time when
-; after disconnecting before the caller can receive a new call
-; note this is in milliseconds.
-;
+; Set the minimum amount of time after disconnecting a call before
+; the agent can receive a new call in milliseconds.
+; The channel variable AGENTWRAPUPTIME overrides on agent login.
+; Default is 0.
 ;wrapuptime=5000
 ;
-; Define the default musiconhold for agents
-; musiconhold => music_class
+; Set the musiconhold class for the agent.
+; Default is "default".
+;musiconhold=default
 ;
-;musiconhold => default
-;
-; Define the default good bye sound file for agents
-; default to vm-goodbye
-;
-;goodbye => goodbye_file
-;
-; Define updatecdr. This is whether or not to change the source
-; channel in the CDR record for this call to agent/agent_id so
-; that we know which agent generates the call
-;
-;updatecdr=no
-;
-; Group memberships for agents (may change in mid-file)
-;
-;group=3
-;group=1,2
-;group=
-;
-; --------------------------------------------------
-; This section is devoted to recording agent's calls
-; The keywords are global to the chan_agent channel driver
-;
-; Enable recording calls addressed to agents. It's turned off by default.
+; Enable recording calls the agent takes automatically by invoking the
+; DTMF automixmon feature when the agent connects to a caller.
+; See features.conf.sample for information about the automixmon feature.
+; Default is "no".
 ;recordagentcalls=yes
 ;
-; The format to be used to record the calls: wav, gsm, wav49.
-; By default its "wav".
-;recordformat=gsm
+; The sound file played to alert the agent when a call is present.
+; Default is "beep".
+;custom_beep=beep
 ;
-; The text to be added to the name of the recording. Allows forming a url link.
-;urlprefix=http://localhost/calls/
-;
-; The optional directory to save the conversations in. The default is
-; /var/spool/asterisk/monitor
-;savecallsin=/var/calls
-;
-; An optional custom beep sound file to play to always-connected agents.
-;custom_beep=beep
+; A friendly name for the agent used in log messages.
+; Default is "".
+;fullname=Mark Spencer
 ;
 ; --------------------------------------------------
 ;
-; This section contains the agent definitions, in the form:
+; This section contains example agent definitions:
 ;
-; agent => agentid,agentpassword,name
+; Define a template called my-agents:
+;[my-agents](!)
+;autologoff=15
+;ackcall=yes
+;acceptdtmf=##
 ;
-;agent => 1001,4321,Mark Spencer
-;agent => 1002,4321,Will Meadows
+; Define agent 1001 using the my-agents template:
+;[1001](my-agents)
+;fullname=Mark Spencer
+;
+; Define agent 1002 using the my-agents template:
+;[1002](my-agents)
+;fullname=Will Meadows

Modified: trunk/configs/queues.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/queues.conf.sample?view=diff&rev=394417&r1=394416&r2=394417
==============================================================================
--- trunk/configs/queues.conf.sample (original)
+++ trunk/configs/queues.conf.sample Mon Jul 15 18:20:55 2013
@@ -543,18 +543,7 @@
 ;member => DAHDI/1
 ;member => DAHDI/2,10
 ;member => DAHDI/3,10,Bob Johnson
-;member => Agent/1001
-;member => Agent/1002
+;member => Local/1001 at agents,0,May Flowers,Agent:1001
+;member => Local/1002 at agents,0,John Doe,Agent:1002
 ;member => Local/1000 at default,0,John Smith,SIP/1000
 ;member => Local/2000 at default,0,Lorem Ipsum,SIP/2000,no
-
-;
-; Note that using agent groups is probably not what you want.  Strategies do
-; not propagate down to the Agent system so if you want round robin, least
-; recent, etc, you should list all the agents in this file individually and not
-; use agent groups.
-;
-;member => Agent/@1		; Any agent in group 1
-;member => Agent/:1,1		; Any agent in group 1, wait for first
-                                ; available, but consider with penalty
-

Modified: trunk/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/bridging.h?view=diff&rev=394417&r1=394416&r2=394417
==============================================================================
--- trunk/include/asterisk/bridging.h (original)
+++ trunk/include/asterisk/bridging.h Mon Jul 15 18:20:55 2013
@@ -1651,7 +1651,7 @@
 
 /*! Reason the the after bridge callback will not be called. */
 enum ast_after_bridge_cb_reason {
-	/*! The datastore is being destroyed.  Likely due to hangup. */
+	/*! The datastore is being destroyed.  Likely due to hangup. (Enum value must be zero.) */
 	AST_AFTER_BRIDGE_CB_REASON_DESTROY,
 	/*! Something else replaced the callback with another. */
 	AST_AFTER_BRIDGE_CB_REASON_REPLACED,
@@ -1670,6 +1670,9 @@
  * \param reason Reason callback is failing.
  * \param data Extra data what setup the callback wanted to pass.
  *
+ * \note Called when the channel leaves the bridging system or
+ * is destroyed.
+ *
  * \return Nothing
  */
 typedef void (*ast_after_bridge_cb_failed)(enum ast_after_bridge_cb_reason reason, void *data);
@@ -1709,6 +1712,9 @@
  *
  * \note chan is locked by this function.
  *
+ * \note failed is called when the channel leaves the bridging
+ * system or is destroyed.
+ *
  * \retval 0 on success.
  * \retval -1 on error.
  */

Modified: trunk/include/asterisk/config_options.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/config_options.h?view=diff&rev=394417&r1=394416&r2=394417
==============================================================================
--- trunk/include/asterisk/config_options.h (original)
+++ trunk/include/asterisk/config_options.h Mon Jul 15 18:20:55 2013
@@ -461,7 +461,7 @@
 /*! \brief Process a config info via the options registered with an aco_info
  *
  * \param info The config_options_info to be used for handling the config
- * \param reload Whether or not this is a reload
+ * \param reload Non-zero if this is for a reload.
  *
  * \retval ACO_PROCESS_OK Success
  * \retval ACO_PROCESS_ERROR Failure

Modified: trunk/include/asterisk/stasis_channels.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/stasis_channels.h?view=diff&rev=394417&r1=394416&r2=394417
==============================================================================
--- trunk/include/asterisk/stasis_channels.h (original)
+++ trunk/include/asterisk/stasis_channels.h Mon Jul 15 18:20:55 2013
@@ -436,6 +436,22 @@
 struct stasis_message_type *ast_channel_monitor_stop_type(void);
 
 /*!
+ * \since 12.0.0
+ * \brief Message type for agent login on a channel
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_channel_agent_login_type(void);
+
+/*!
+ * \since 12.0.0
+ * \brief Message type for agent logoff on a channel
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_channel_agent_logoff_type(void);
+
+/*!
  * \since 12
  * \brief Message type for starting music on hold on a channel
  *

Modified: trunk/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/bridging.c?view=diff&rev=394417&r1=394416&r2=394417
==============================================================================
--- trunk/main/bridging.c (original)
+++ trunk/main/bridging.c Mon Jul 15 18:20:55 2013
@@ -3242,14 +3242,69 @@
 	return bridge_channel;
 }
 
-struct after_bridge_cb_ds {
+struct after_bridge_cb_node {
+	/*! Next list node. */
+	AST_LIST_ENTRY(after_bridge_cb_node) list;
 	/*! Desired callback function. */
 	ast_after_bridge_cb callback;
 	/*! After bridge callback will not be called and destroy any resources data may contain. */
 	ast_after_bridge_cb_failed failed;
 	/*! Extra data to pass to the callback. */
 	void *data;
+	/*! Reason the after bridge callback failed. */
+	enum ast_after_bridge_cb_reason reason;
 };
+
+struct after_bridge_cb_ds {
+	/*! After bridge callbacks container. */
+	AST_LIST_HEAD(, after_bridge_cb_node) callbacks;
+};
+
+/*!
+ * \internal
+ * \brief Indicate after bridge callback failed.
+ * \since 12.0.0
+ *
+ * \param node After bridge callback node.
+ *
+ * \return Nothing
+ */
+static void after_bridge_cb_failed(struct after_bridge_cb_node *node)
+{
+	if (node->failed) {
+		node->failed(node->reason, node->data);
+		node->failed = NULL;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Run discarding any after bridge callbacks.
+ * \since 12.0.0
+ *
+ * \param after_bridge After bridge callback container process.
+ * \param reason Why are we doing this.
+ *
+ * \return Nothing
+ */
+static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_after_bridge_cb_reason reason)
+{
+	struct after_bridge_cb_node *node;
+
+	for (;;) {
+		AST_LIST_LOCK(&after_bridge->callbacks);
+		node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
+		AST_LIST_UNLOCK(&after_bridge->callbacks);
+		if (!node) {
+			break;
+		}
+		if (!node->reason) {
+			node->reason = reason;
+		}
+		after_bridge_cb_failed(node);
+		ast_free(node);
+	}
+}
 
 /*!
  * \internal
@@ -3264,10 +3319,9 @@
 {
 	struct after_bridge_cb_ds *after_bridge = data;
 
-	if (after_bridge->failed) {
-		after_bridge->failed(AST_AFTER_BRIDGE_CB_REASON_DESTROY, after_bridge->data);
-		after_bridge->failed = NULL;
-	}
+	after_bridge_cb_run_discard(after_bridge, AST_AFTER_BRIDGE_CB_REASON_DESTROY);
+
+	AST_LIST_HEAD_DESTROY(&after_bridge->callbacks);
 	ast_free(after_bridge);
 }
 
@@ -3284,7 +3338,6 @@
  */
 static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
 {
-	/* There can be only one.  Discard any already on the new channel. */
 	ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE);
 }
 
@@ -3296,82 +3349,144 @@
 
 /*!
  * \internal
- * \brief Remove channel after the bridge callback and return it.
+ * \brief Setup/create an after bridge callback datastore container.
  * \since 12.0.0
  *
- * \param chan Channel to remove after bridge callback.
- *
- * \retval datastore on success.
- * \retval NULL on error or not found.
- */
-static struct ast_datastore *after_bridge_cb_remove(struct ast_channel *chan)
-{
-	struct ast_datastore *datastore;
-
-	ast_channel_lock(chan);
-	datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
-	if (datastore && ast_channel_datastore_remove(chan, datastore)) {
-		datastore = NULL;
-	}
-	ast_channel_unlock(chan);
-
-	return datastore;
-}
-
-void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
-{
-	struct ast_datastore *datastore;
-
-	datastore = after_bridge_cb_remove(chan);
-	if (datastore) {
-		struct after_bridge_cb_ds *after_bridge = datastore->data;
-
-		if (after_bridge && after_bridge->failed) {
-			after_bridge->failed(reason, after_bridge->data);
-			after_bridge->failed = NULL;
-		}
-		ast_datastore_free(datastore);
-	}
-}
-
-/*!
- * \internal
- * \brief Run any after bridge callback if possible.
- * \since 12.0.0
- *
- * \param chan Channel to run after bridge callback.
- *
- * \return Nothing
- */
-static void after_bridge_callback_run(struct ast_channel *chan)
+ * \param chan Channel to setup/create the after bridge callback container on.
+ *
+ * \retval after_bridge datastore container on success.
+ * \retval NULL on error.
+ */
+static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan)
 {
 	struct ast_datastore *datastore;
 	struct after_bridge_cb_ds *after_bridge;
-
-	if (ast_check_hangup(chan)) {
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
+	if (datastore) {
+		return datastore->data;
+	}
+
+	/* Create a new datastore. */
+	datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
+	if (!datastore) {
+		return NULL;
+	}
+	after_bridge = ast_calloc(1, sizeof(*after_bridge));
+	if (!after_bridge) {
+		ast_datastore_free(datastore);
+		return NULL;
+	}
+	AST_LIST_HEAD_INIT(&after_bridge->callbacks);
+	datastore->data = after_bridge;
+	ast_channel_datastore_add(chan, datastore);
+
+	return datastore->data;
+}
+
+/*!
+ * \internal
+ * \brief Find an after bridge callback datastore container.
+ * \since 12.0.0
+ *
+ * \param chan Channel to find the after bridge callback container on.
+ *
+ * \retval after_bridge datastore container on success.
+ * \retval NULL on error.
+ */
+static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+	SCOPED_CHANNELLOCK(lock, chan);
+
+	datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL);
+	if (!datastore) {
+		return NULL;
+	}
+	return datastore->data;
+}
+
+/*!
+ * \internal
+ * \brief Run any after bridge callback.
+ * \since 12.0.0
+ *
+ * \param chan Channel to run after bridge callback.
+ *
+ * \return Nothing
+ */
+static void after_bridge_callback_run(struct ast_channel *chan)
+{
+	struct after_bridge_cb_ds *after_bridge;
+	struct after_bridge_cb_node *node;
+
+	after_bridge = after_bridge_cb_find(chan);
+	if (!after_bridge) {
 		return;
 	}
 
-	/* Get after bridge goto datastore. */
-	datastore = after_bridge_cb_remove(chan);
-	if (!datastore) {
+	for (;;) {
+		AST_LIST_LOCK(&after_bridge->callbacks);
+		node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list);
+		AST_LIST_UNLOCK(&after_bridge->callbacks);
+		if (!node) {
+			break;
+		}
+		if (node->reason) {
+			after_bridge_cb_failed(node);
+		} else {
+			node->failed = NULL;
+			node->callback(chan, node->data);
+		}
+		ast_free(node);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Run discarding any after bridge callbacks.
+ * \since 12.0.0
+ *
+ * \param chan Channel to run after bridge callback.
+ *
+ * \return Nothing
+ */
+static void after_bridge_callback_run_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
+{
+	struct after_bridge_cb_ds *after_bridge;
+
+	after_bridge = after_bridge_cb_find(chan);
+	if (!after_bridge) {
 		return;
 	}
 
-	after_bridge = datastore->data;
-	if (after_bridge) {
-		after_bridge->failed = NULL;
-		after_bridge->callback(chan, after_bridge->data);
-	}
-
-	/* Discard after bridge callback datastore. */
-	ast_datastore_free(datastore);
+	after_bridge_cb_run_discard(after_bridge, reason);
+}
+
+void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
+{
+	struct after_bridge_cb_ds *after_bridge;
+	struct after_bridge_cb_node *node;
+
+	after_bridge = after_bridge_cb_find(chan);
+	if (!after_bridge) {
+		return;
+	}
+
+	AST_LIST_LOCK(&after_bridge->callbacks);
+	node = AST_LIST_LAST(&after_bridge->callbacks);
+	if (node && !node->reason) {
+		node->reason = reason;
+	}
+	AST_LIST_UNLOCK(&after_bridge->callbacks);
 }
 
 int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data)
 {
-	struct ast_datastore *datastore;
 	struct after_bridge_cb_ds *after_bridge;
+	struct after_bridge_cb_node *new_node;
+	struct after_bridge_cb_node *last_node;
 
 	/* Sanity checks. */
 	ast_assert(chan != NULL);
@@ -3379,29 +3494,28 @@
 		return -1;
 	}
 
-	/* Create a new datastore. */
-	datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
-	if (!datastore) {
+	after_bridge = after_bridge_cb_setup(chan);
+	if (!after_bridge) {
 		return -1;
 	}
-	after_bridge = ast_calloc(1, sizeof(*after_bridge));
-	if (!after_bridge) {
-		ast_datastore_free(datastore);
+
+	/* Create a new callback node. */
+	new_node = ast_calloc(1, sizeof(*new_node));
+	if (!new_node) {
 		return -1;
 	}
-
-	/* Initialize it. */
-	after_bridge->callback = callback;
-	after_bridge->failed = failed;
-	after_bridge->data = data;
-	datastore->data = after_bridge;
-
-	/* Put it on the channel replacing any existing one. */
-	ast_channel_lock(chan);
-	ast_after_bridge_callback_discard(chan, AST_AFTER_BRIDGE_CB_REASON_REPLACED);
-	ast_channel_datastore_add(chan, datastore);
-	ast_channel_unlock(chan);
-
+	new_node->callback = callback;
+	new_node->failed = failed;
+	new_node->data = data;
+
+	/* Put it in the container disabling any previously active one. */
+	AST_LIST_LOCK(&after_bridge->callbacks);
+	last_node = AST_LIST_LAST(&after_bridge->callbacks);
+	if (last_node && !last_node->reason) {
+		last_node->reason = AST_AFTER_BRIDGE_CB_REASON_REPLACED;
+	}
+	AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list);
+	AST_LIST_UNLOCK(&after_bridge->callbacks);
 	return 0;
 }
 
@@ -3866,7 +3980,7 @@
 	ast_bridge_features_destroy(bridge_channel->features);
 	bridge_channel->features = NULL;
 
-	ast_after_bridge_callback_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART);
+	after_bridge_callback_run_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART);
 	ast_after_bridge_goto_discard(bridge_channel->chan);
 
 	return NULL;

Modified: trunk/main/stasis_channels.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/stasis_channels.c?view=diff&rev=394417&r1=394416&r2=394417
==============================================================================
--- trunk/main/stasis_channels.c (original)
+++ trunk/main/stasis_channels.c Mon Jul 15 18:20:55 2013
@@ -53,6 +53,35 @@
 					<para>The new value of the variable.</para>
 				</parameter>
 			</syntax>
+		</managerEventInstance>
+	</managerEvent>
+	<managerEvent language="en_US" name="AgentLogin">
+		<managerEventInstance class="EVENT_FLAG_AGENT">
+			<synopsis>Raised when an Agent has logged in.</synopsis>
+			<syntax>
+				<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+				<parameter name="Agent">
+					<para>Agent ID of the agent.</para>
+				</parameter>
+			</syntax>
+			<see-also>
+				<ref type="application">AgentLogin</ref>
+				<ref type="managerEvent">AgentLogoff</ref>
+			</see-also>
+		</managerEventInstance>
+	</managerEvent>
+	<managerEvent language="en_US" name="AgentLogoff">
+		<managerEventInstance class="EVENT_FLAG_AGENT">
+			<synopsis>Raised when an Agent has logged off.</synopsis>
+			<syntax>
+				<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentLogin']/managerEventInstance/syntax/parameter)" />
+				<parameter name="Logintime">
+					<para>The number of seconds the agent was logged in.</para>
+				</parameter>
+			</syntax>
+			<see-also>
+				<ref type="managerEvent">AgentLogin</ref>
+			</see-also>
 		</managerEventInstance>
 	</managerEvent>
 ***/
@@ -625,6 +654,44 @@
 		"Variable: %s\r\n"
 		"Value: %s\r\n",
 		ast_str_buffer(channel_event_string), variable, value);
+}
+
+static struct ast_manager_event_blob *agent_login_to_ami(struct stasis_message *msg)
+{
+	RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
+	RAII_VAR(struct ast_str *, party_string, ast_str_create(256), ast_free);
+	struct ast_channel_blob *obj = stasis_message_data(msg);
+	const char *agent = ast_json_string_get(ast_json_object_get(obj->blob, "agent"));
+
+	channel_string = ast_manager_build_channel_state_string(obj->snapshot);
+	if (!channel_string) {
+		return NULL;
+	}
+
+	return ast_manager_event_blob_create(EVENT_FLAG_AGENT, "AgentLogin",
+		"%s"
+		"Agent: %s\r\n",
+		ast_str_buffer(channel_string), agent);
+}
+
+static struct ast_manager_event_blob *agent_logoff_to_ami(struct stasis_message *msg)
+{
+	RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
+	RAII_VAR(struct ast_str *, party_string, ast_str_create(256), ast_free);
+	struct ast_channel_blob *obj = stasis_message_data(msg);
+	const char *agent = ast_json_string_get(ast_json_object_get(obj->blob, "agent"));
+	long logintime = ast_json_integer_get(ast_json_object_get(obj->blob, "logintime"));
+
+	channel_string = ast_manager_build_channel_state_string(obj->snapshot);
+	if (!channel_string) {
+		return NULL;
+	}
+
+	return ast_manager_event_blob_create(EVENT_FLAG_AGENT, "AgentLogoff",
+		"%s"
+		"Agent: %s\r\n"
+		"Logintime: %ld\r\n",
+		ast_str_buffer(channel_string), agent, logintime);
 }
 
 void ast_publish_channel_state(struct ast_channel *chan)
@@ -827,6 +894,12 @@
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_stop_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_start_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_monitor_stop_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_agent_login_type,
+	.to_ami = agent_login_to_ami,
+	);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_agent_logoff_type,
+	.to_ami = agent_logoff_to_ami,
+	);
 
 /*! @} */
 
@@ -853,6 +926,8 @@
 	STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_moh_stop_type);
 	STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_start_type);
 	STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_monitor_stop_type);
+	STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_login_type);
+	STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_agent_logoff_type);
 }
 
 void ast_stasis_channels_init(void)
@@ -876,6 +951,8 @@
 	STASIS_MESSAGE_TYPE_INIT(ast_channel_moh_stop_type);
 	STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_start_type);
 	STASIS_MESSAGE_TYPE_INIT(ast_channel_monitor_stop_type);
+	STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_login_type);
+	STASIS_MESSAGE_TYPE_INIT(ast_channel_agent_logoff_type);
 
 	channel_topic_all = stasis_topic_create("ast_channel_topic_all");
 	channel_topic_all_cached = stasis_caching_topic_create(channel_topic_all, channel_snapshot_get_id);




More information about the asterisk-commits mailing list