summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2013-07-15 23:20:55 +0000
committerRichard Mudgett <rmudgett@digium.com>2013-07-15 23:20:55 +0000
commitd43b17a872e8227aa8a9905a21f90bd48f9d5348 (patch)
treeba29a4b4e3e065ba935b5283c3b23decd7e1ec6d /main
parentd43bbfb74e9111da7440d0fd8cc0b480ed47a0af (diff)
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@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/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@394417 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r--main/bridging.c230
-rw-r--r--main/stasis_channels.c77
2 files changed, 249 insertions, 58 deletions
diff --git a/main/bridging.c b/main/bridging.c
index 07c04a160..6febf61b0 100644
--- a/main/bridging.c
+++ b/main/bridging.c
@@ -3242,17 +3242,72 @@ static struct ast_bridge_channel *bridge_channel_alloc(struct ast_bridge *bridge
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
* \brief Destroy the after bridge callback datastore.
* \since 12.0.0
*
@@ -3264,10 +3319,9 @@ static void after_bridge_cb_destroy(void *data)
{
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_destroy(void *data)
*/
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,47 +3349,67 @@ static const struct ast_datastore_info after_bridge_cb_info = {
/*!
* \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.
+ * \param chan Channel to setup/create the after bridge callback container on.
*
- * \retval datastore on success.
- * \retval NULL on error or not found.
+ * \retval after_bridge datastore container on success.
+ * \retval NULL on error.
*/
-static struct ast_datastore *after_bridge_cb_remove(struct ast_channel *chan)
+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;
+ SCOPED_CHANNELLOCK(lock, chan);
- 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;
+ if (datastore) {
+ return datastore->data;
}
- ast_channel_unlock(chan);
- return datastore;
+ /* 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;
}
-void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason)
+/*!
+ * \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 = 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);
+ 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 if possible.
+ * \brief Run any after bridge callback.
* \since 12.0.0
*
* \param chan Channel to run after bridge callback.
@@ -3345,33 +3418,75 @@ void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_
*/
static void after_bridge_callback_run(struct ast_channel *chan)
{
- struct ast_datastore *datastore;
struct after_bridge_cb_ds *after_bridge;
+ struct after_bridge_cb_node *node;
- if (ast_check_hangup(chan)) {
+ 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);
+ 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;
}
- /* Discard after bridge callback datastore. */
- ast_datastore_free(datastore);
+ 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 @@ int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb
return -1;
}
- /* Create a new datastore. */
- datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL);
- if (!datastore) {
- return -1;
- }
- after_bridge = ast_calloc(1, sizeof(*after_bridge));
+ after_bridge = after_bridge_cb_setup(chan);
if (!after_bridge) {
- ast_datastore_free(datastore);
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);
+ /* Create a new callback node. */
+ new_node = ast_calloc(1, sizeof(*new_node));
+ if (!new_node) {
+ return -1;
+ }
+ 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 @@ static void *bridge_channel_depart_thread(void *data)
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;
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index eb71531c2..be6521356 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -55,6 +55,35 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</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>
***/
#define NUM_MULTI_CHANNEL_BLOB_BUCKETS 7
@@ -627,6 +656,44 @@ static struct ast_manager_event_blob *varset_to_ami(struct stasis_message *msg)
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)
{
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
@@ -827,6 +894,12 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_moh_start_type);
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 @@ static void stasis_channels_cleanup(void)
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 @@ void ast_stasis_channels_init(void)
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);