[asterisk-commits] rmudgett: branch group/bridge_construction r389031 - in /team/group/bridge_co...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri May 17 16:44:53 CDT 2013


Author: rmudgett
Date: Fri May 17 16:44:50 2013
New Revision: 389031

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=389031
Log:
Add ability to call a function when a channel leaves the bridging system.

There are times when a module would like to gain control of a channel that
is already in the bridging system but the module does not want the channel
in the bridging system when it gains control.

1) The mythical app_queue holding priority bridge when the channel is
selected to dial an agent.

2) Stasis-HTTP when it needs to gain control before it initiates dialing.

3) Attended transferring a bridged channel to an application.

Example usage:
ast_after_bridge_callback_set(chan, callback_fn, failed_fn, data);
ast_bridge_remove(bridge, chan)

When the channel leaves the bridge the callback_fn(chan, data) will be
made.

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

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

Modified:
    team/group/bridge_construction/include/asterisk/bridging.h
    team/group/bridge_construction/main/bridging.c

Modified: team/group/bridge_construction/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/bridging.h?view=diff&rev=389031&r1=389030&r2=389031
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging.h Fri May 17 16:44:50 2013
@@ -615,6 +615,9 @@
  * \param tech_args Optional Bridging tech optimization parameters for this channel.
  * \param pass_reference TRUE if the bridge reference is being passed by the caller.
  *
+ * \note Absolutely _NO_ locks should be held before calling
+ * this function since it blocks.
+ *
  * \retval state that channel exited the bridge with
  *
  * Example usage:
@@ -654,6 +657,8 @@
  * ast_bridge_features_new().  You must not dereference features
  * after calling even if the call fails.
  *
+ * \note chan is locked by this function.
+ *
  * \retval 0 on success
  * \retval -1 on failure
  *
@@ -698,6 +703,8 @@
  *
  * \param chan Channel to depart
  *
+ * \note chan is locked by this function.
+ *
  * \retval 0 on success
  * \retval -1 on failure
  *
@@ -749,6 +756,9 @@
  * \param kick_me Array of channels to kick from the bridges.
  * \param num_kick Number of channels in the kick_me array.
  *
+ * \note Absolutely _NO_ bridge or channel locks should be held
+ * before calling this function.
+ *
  * \retval 0 on success
  * \retval -1 on failure
  *
@@ -772,6 +782,9 @@
  * \param chan Channel to move.
  * \param swap Channel to replace in dst_bridge.
  * \param attempt_recovery TRUE if failure attempts to push channel back into original bridge.
+ *
+ * \note Absolutely _NO_ bridge or channel locks should be held
+ * before calling this function.
  *
  * \retval 0 on success.
  * \retval -1 on failure.
@@ -1265,14 +1278,14 @@
 void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan);
 
 enum ast_transfer_result {
-    /*! The transfer completed successfully */
-    AST_BRIDGE_TRANSFER_SUCCESS,
-    /*! A bridge involved does not permit transferring */
-    AST_BRIDGE_TRANSFER_NOT_PERMITTED,
-    /*! The current bridge setup makes transferring an invalid operation */
-    AST_BRIDGE_TRANSFER_INVALID,
-    /*! The transfer operation failed for a miscellaneous reason */
-    AST_BRIDGE_TRANSFER_FAIL,
+	/*! The transfer completed successfully */
+	AST_BRIDGE_TRANSFER_SUCCESS,
+	/*! A bridge involved does not permit transferring */
+	AST_BRIDGE_TRANSFER_NOT_PERMITTED,
+	/*! The current bridge setup makes transferring an invalid operation */
+	AST_BRIDGE_TRANSFER_INVALID,
+	/*! The transfer operation failed for a miscellaneous reason */
+	AST_BRIDGE_TRANSFER_FAIL,
 };
 
 typedef void (*transfer_channel_cb)(struct ast_channel *chan, void *user_data);
@@ -1290,7 +1303,8 @@
  * This callback is guaranteed to be called in the same thread as
  * ast_bridge_transfer_blind() and before ast_bridge_transfer_blind() returns.
  *
- * \note Do not call this function with the transferer or its tech_pvt locked.
+ * \note Absolutely _NO_ channel locks should be held before
+ * calling this function.
  *
  * \param transferer The channel performing the blind transfer
  * \param exten The dialplan extension to send the call to
@@ -1316,8 +1330,8 @@
  * resulting call after the transfer completes. If the transfer fails, the
  * hook will not be attached to any call.
  *
- * \note Do not call this function with either of the channels or their
- * tech_pvts locked.
+ * \note Absolutely _NO_ channel locks should be held before
+ * calling this function.
  *
  * \param to_transferee Transferer channel on initial call (presumably bridged to transferee)
  * \param to_transfer_target Transferer channel on consultation call (presumably bridged to transfer target)
@@ -1335,6 +1349,8 @@
  * \param exten Exten to goto after bridge.
  * \param priority Priority to goto after bridge.
  *
+ * \note chan is locked by this function.
+ *
  * \details Add a channel datastore to setup the goto location
  * when the channel leaves the bridge and run a PBX from there.
  *
@@ -1348,6 +1364,8 @@
  *
  * \param chan Channel to setup after bridge goto location.
  * \param context Context to goto after bridge.
+ *
+ * \note chan is locked by this function.
  *
  * \details Add a channel datastore to setup the goto location
  * when the channel leaves the bridge and run a PBX from there.
@@ -1366,6 +1384,8 @@
  * \param priority Current priority of the caller channel
  * \param parseable_goto User specified goto string from dialplan.
  *
+ * \note chan is locked by this function.
+ *
  * \details Add a channel datastore to setup the goto location
  * when the channel leaves the bridge and run a PBX from there.
  *
@@ -1383,6 +1403,8 @@
  *
  * \param chan Channel to setup after bridge goto location.
  *
+ * \note chan is locked by this function.
+ *
  * \details Pull off any after bridge goto location datastore and
  * setup for dialplan execution there.
  *
@@ -1400,6 +1422,8 @@
  *
  * \param chan Channel to execute after bridge goto location.
  *
+ * \note chan is locked by this function.
+ *
  * \details Pull off any after bridge goto location datastore
  * and run a PBX at that location.
  *
@@ -1416,9 +1440,76 @@
  *
  * \param chan Channel to discard after bridge goto location.
  *
+ * \note chan is locked by this function.
+ *
  * \return Nothing
  */
 void ast_after_bridge_goto_discard(struct ast_channel *chan);
+
+/*! 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. */
+	AST_AFTER_BRIDGE_CB_REASON_DESTROY,
+	/*! Something else replaced the callback with another. */
+	AST_AFTER_BRIDGE_CB_REASON_REPLACED,
+	/*! The callback was removed because of a masquerade. (fixup) */
+	AST_AFTER_BRIDGE_CB_REASON_MASQUERADE,
+	/*! The channel departed bridge. */
+	AST_AFTER_BRIDGE_CB_REASON_DEPART,
+	/*! Was explicitly removed by external code. */
+	AST_AFTER_BRIDGE_CB_REASON_REMOVED,
+};
+
+/*!
+ * \brief After bridge callback failed.
+ * \since 12.0.0
+ *
+ * \param reason Reason callback is failing.
+ * \param data Extra data what setup the callback wanted to pass.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_after_bridge_cb_failed)(enum ast_after_bridge_cb_reason reason, void *data);
+
+/*!
+ * \brief After bridge callback function.
+ * \since 12.0.0
+ *
+ * \param chan Channel just leaving bridging system.
+ * \param data Extra data what setup the callback wanted to pass.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_after_bridge_cb)(struct ast_channel *chan, void *data);
+
+/*!
+ * \brief Discard channel after bridge callback.
+ * \since 12.0.0
+ *
+ * \param chan Channel to discard after bridge callback.
+ * \param reason Why are we doing this.
+ *
+ * \note chan is locked by this function.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason);
+
+/*!
+ * \brief Setup an after bridge callback for when the channel leaves the bridging system.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup an after bridge callback on.
+ * \param callback Function to call when the channel leaves the bridging system.
+ * \param failed Function to call when it will not be calling the callback.
+ * \param data Extra data to pass with the callback.
+ *
+ * \note chan is locked by this function.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data);
 
 /*!
  * \brief Get a container of all channels in the bridge

Modified: team/group/bridge_construction/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/bridging.c?view=diff&rev=389031&r1=389030&r2=389031
==============================================================================
--- team/group/bridge_construction/main/bridging.c (original)
+++ team/group/bridge_construction/main/bridging.c Fri May 17 16:44:50 2013
@@ -2552,6 +2552,168 @@
 	return bridge_channel;
 }
 
+struct after_bridge_cb_ds {
+	/*! 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;
+};
+
+/*!
+ * \internal
+ * \brief Destroy the after bridge callback datastore.
+ * \since 12.0.0
+ *
+ * \param data After bridge callback data to destroy.
+ *
+ * \return Nothing
+ */
+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;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Fixup the after bridge callback datastore.
+ * \since 12.0.0
+ *
+ * \param data After bridge callback data to fixup.
+ * \param old_chan The datastore is moving from this channel.
+ * \param new_chan The datastore is moving to this channel.
+ *
+ * \return Nothing
+ */
+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);
+}
+
+static const struct ast_datastore_info after_bridge_cb_info = {
+	.type = "after-bridge-cb",
+	.destroy = after_bridge_cb_destroy,
+	.chan_fixup = after_bridge_cb_fixup,
+};
+
+/*!
+ * \internal
+ * \brief Remove channel after the bridge callback and return it.
+ * \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)
+{
+	struct ast_datastore *datastore;
+	struct after_bridge_cb_ds *after_bridge;
+
+	if (ast_check_hangup(chan)) {
+		return;
+	}
+
+	/* Get after bridge goto datastore. */
+	datastore = after_bridge_cb_remove(chan);
+	if (!datastore) {
+		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);
+}
+
+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;
+
+	/* Sanity checks. */
+	ast_assert(chan != NULL);
+	if (!chan || !callback) {
+		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));
+	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);
+
+	return 0;
+}
+
 struct after_bridge_goto_ds {
 	/*! Goto string that can be parsed by ast_parseable_goto(). */
 	const char *parseable_goto;
@@ -2922,6 +3084,7 @@
 
 join_exit:;
 /* BUGBUG this is going to cause problems for DTMF atxfer attended bridge between B & C.  Maybe an ast_bridge_join_internal() that does not do the after bridge goto for this case. */
+	after_bridge_callback_run(chan);
 	if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)
 		&& !ast_after_bridge_goto_setup(chan)) {
 		/* Claim the after bridge goto is an async goto destination. */
@@ -2948,6 +3111,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);
 	ast_after_bridge_goto_discard(bridge_channel->chan);
 
 	return NULL;
@@ -2977,6 +3141,7 @@
 
 	ao2_ref(bridge_channel, -1);
 
+	after_bridge_callback_run(chan);
 	ast_after_bridge_goto_run(chan);
 	return NULL;
 }




More information about the asterisk-commits mailing list