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

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Apr 12 11:38:32 CDT 2013


Author: rmudgett
Date: Fri Apr 12 11:38:28 2013
New Revision: 385505

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=385505
Log:
Add bridge CLI commands.

* Added bridge CLI commands:
bridge destroy <bridge-id>
bridge kick <bridge-id> <channel>
bridge show all
bridge show <bridge-id>
bridge technology show
bridge technology {suspend|unsuspend} <technology>

* Created global bridges container for manipulation by bridge CLI
commands.

* Add ast_bridge_register() for ast_bridge_xxx_new() calls to register the
bridge for access by the CLI commands.

(closes issue ASTERISK-21331)

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

Modified:
    team/group/bridge_construction/include/asterisk/bridging.h
    team/group/bridge_construction/main/bridging.c
    team/group/bridge_construction/main/features.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=385505&r1=385504&r2=385505
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging.h Fri Apr 12 11:38:28 2013
@@ -254,6 +254,19 @@
 typedef void (*ast_bridge_destructor_fn)(struct ast_bridge *self);
 
 /*!
+ * \brief The bridge is being dissolved.
+ *
+ * \param self Bridge to operate upon.
+ *
+ * \details
+ * The bridge is being dissolved.  Remove any external
+ * references to the bridge so it can be destroyed.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_bridge_dissolving_fn)(struct ast_bridge *self);
+
+/*!
  * \brief Can this channel be pushed into the bridge.
  *
  * \param self Bridge to operate upon.
@@ -330,6 +343,8 @@
 	const char *name;
 	/*! Destroy the bridge. */
 	ast_bridge_destructor_fn destroy;
+	/*! The bridge is being dissolved.  Remove any references to the bridge. */
+	ast_bridge_dissolving_fn dissolving;
 	/*! TRUE if can push the bridge channel into the bridge. */
 	ast_bridge_can_push_channel_fn can_push;
 	/*! Push the bridge channel into the bridge. */
@@ -387,6 +402,37 @@
 };
 
 /*!
+ * \brief Register the new bridge with the system.
+ * \since 12.0.0
+ *
+ * \param bridge What to register. (Tolerates a NULL pointer)
+ *
+ * \code
+ * struct ast_bridge *ast_bridge_basic_new(uint32_t capabilities, int flags, uint32 dtmf_features)
+ * {
+ *     void *bridge;
+ *
+ *     bridge = ast_bridge_alloc(sizeof(struct ast_bridge_basic), &ast_bridge_basic_v_table);
+ *     bridge = ast_bridge_base_init(bridge, capabilities, flags);
+ *     bridge = ast_bridge_basic_init(bridge, dtmf_features);
+ *     bridge = ast_bridge_register(bridge);
+ *     return bridge;
+ * }
+ * \endcode
+ *
+ * \note This must be done after a bridge constructor has
+ * completed setting up the new bridge but before it returns.
+ *
+ * \note After a bridge is registered, the bridge must be
+ * explicitly destroyed by ast_bridge_destroy() to get rid of
+ * the bridge.
+ *
+ * \retval bridge on success.
+ * \retval NULL on error.
+ */
+struct ast_bridge *ast_bridge_register(struct ast_bridge *bridge);
+
+/*!
  * \internal
  * \brief Allocate the bridge class object memory.
  * \since 12.0.0
@@ -405,7 +451,7 @@
 /*!
  * \brief Initialize the base class of the bridge.
  *
- * \param self Bridge to operate upon. (Tollerates a NULL pointer)
+ * \param self Bridge to operate upon. (Tolerates a NULL pointer)
  * \param capabilities The capabilities that we require to be used on the bridge
  * \param flags Flags that will alter the behavior of 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=385505&r1=385504&r2=385505
==============================================================================
--- team/group/bridge_construction/main/bridging.c (original)
+++ team/group/bridge_construction/main/bridging.c Fri Apr 12 11:38:28 2013
@@ -56,6 +56,10 @@
 #include "asterisk/stringfields.h"
 #include "asterisk/musiconhold.h"
 #include "asterisk/features.h"
+#include "asterisk/cli.h"
+
+/*! All bridges container. */
+static struct ao2_container *bridges;
 
 static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
 
@@ -398,6 +402,82 @@
 
 /*!
  * \internal
+ * \brief Dissolve the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge Bridge to eject all channels
+ *
+ * \details
+ * Force out all channels that are not already going out of the
+ * bridge.  Any new channels joining will leave immediately.
+ *
+ * \note On entry, bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void bridge_dissolve(struct ast_bridge *bridge)
+{
+	struct ast_bridge_channel *bridge_channel;
+
+	if (bridge->dissolved) {
+		return;
+	}
+	bridge->dissolved = 1;
+
+	ast_debug(1, "Bridge %s: dissolving bridge\n", bridge->uniqueid);
+
+/* BUGBUG need a cause code on the bridge for the later ejected channels. */
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+	}
+	bridge->v_table->dissolving(bridge);
+}
+
+/*!
+ * \internal
+ * \brief Check if a bridge should dissolve and do it.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel causing the check.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge *bridge = bridge_channel->bridge;
+
+	if (bridge->dissolved) {
+		return;
+	}
+
+	if (!bridge->num_channels
+		&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY)) {
+		/* Last channel leaving the bridge turns off the lights. */
+		bridge_dissolve(bridge);
+		return;
+	}
+
+	switch (bridge_channel->state) {
+	case AST_BRIDGE_CHANNEL_STATE_END:
+		/* Do we need to dissolve the bridge because this channel hung up? */
+		if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP)
+			|| (bridge_channel->features
+				&& bridge_channel->features->usable
+				&& ast_test_flag(&bridge_channel->features->feature_flags,
+					AST_BRIDGE_FLAG_DISSOLVE_HANGUP))) {
+			bridge_dissolve(bridge);
+			return;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/*!
+ * \internal
  * \brief Pull the bridge channel out of its current bridge.
  * \since 12.0.0
  *
@@ -439,6 +519,8 @@
 	AST_LIST_REMOVE(&bridge->channels, bridge_channel, entry);
 	bridge->v_table->pull(bridge, bridge_channel);
 
+	bridge_dissolve_check(bridge_channel);
+
 	bridge->reconfigured = 1;
 	ast_bridge_publish_leave(bridge, bridge_channel->chan);
 }
@@ -513,53 +595,6 @@
 
 	bridge->reconfigured = 1;
 	ast_bridge_publish_enter(bridge, bridge_channel->chan);
-}
-
-/*!
- * \internal
- * \brief Force out all channels that are not already going out of the bridge.
- * \since 12.0.0
- *
- * \param bridge Bridge to eject all channels
- *
- * \note On entry, bridge is already locked.
- *
- * \return Nothing
- */
-static void bridge_force_out_all(struct ast_bridge *bridge)
-{
-	struct ast_bridge_channel *bridge_channel;
-
-	bridge->dissolved = 1;
-
-/* BUGBUG need a cause code on the bridge for the later ejected channels. */
-	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
-	}
-}
-
-/*!
- * \internal
- * \brief Check if a bridge should dissolve and then do it.
- * \since 12.0.0
- *
- * \param bridge_channel Channel causing the check.
- *
- * \note On entry, bridge_channel->bridge is already locked.
- *
- * \return Nothing
- */
-static void bridge_check_dissolve(struct ast_bridge_channel *bridge_channel)
-{
-	if (!ast_test_flag(&bridge_channel->bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP)
-		&& (!bridge_channel->features
-			|| !bridge_channel->features->usable
-			|| !ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP))) {
-		return;
-	}
-
-	ast_debug(1, "Bridge %s: dissolving bridge\n", bridge_channel->bridge->uniqueid);
-	bridge_force_out_all(bridge_channel->bridge);
 }
 
 /*! \brief Internal function to handle DTMF from a channel */
@@ -1134,13 +1169,13 @@
 	struct ast_bridge *bridge = obj;
 	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
 
+	ast_debug(1, "Bridge %s: actually destroying %s bridge, nobody wants it anymore\n",
+		bridge->uniqueid, bridge->v_table->name);
+
 	msg = stasis_cache_clear_create(ast_bridge_snapshot_type(), bridge->uniqueid);
 	if (msg) {
 		stasis_publish(ast_bridge_topic(bridge), msg);
 	}
-
-	ast_debug(1, "Bridge %s: actually destroying %s bridge, nobody wants it anymore\n",
-		bridge->uniqueid, bridge->v_table->name);
 
 	/* Do any pending actions in the context of destruction. */
 	ast_bridge_lock(bridge);
@@ -1172,6 +1207,18 @@
 	cleanup_video_mode(bridge);
 }
 
+struct ast_bridge *ast_bridge_register(struct ast_bridge *bridge)
+{
+	if (bridge) {
+		ast_bridge_publish_state(bridge);
+		if (!ao2_link(bridges, bridge)) {
+			ast_bridge_destroy(bridge);
+			bridge = NULL;
+		}
+	}
+	return bridge;
+}
+
 struct ast_bridge *ast_bridge_alloc(size_t size, const struct ast_bridge_methods *v_table)
 {
 	struct ast_bridge *bridge;
@@ -1180,6 +1227,7 @@
 	if (!v_table
 		|| !v_table->name
 		|| !v_table->destroy
+		|| !v_table->dissolving
 		|| !v_table->can_push
 		|| !v_table->push
 		|| !v_table->pull
@@ -1263,6 +1311,20 @@
 
 /*!
  * \internal
+ * \brief The bridge is being dissolved.
+ * \since 12.0.0
+ *
+ * \param self Bridge to operate upon.
+ *
+ * \return Nothing
+ */
+static void bridge_base_dissolving(struct ast_bridge *self)
+{
+	ao2_unlink(bridges, self);
+}
+
+/*!
+ * \internal
  * \brief ast_bridge base can_push method.
  * \since 12.0.0
  *
@@ -1337,6 +1399,7 @@
 struct ast_bridge_methods ast_bridge_base_v_table = {
 	.name = "base",
 	.destroy = bridge_base_destroy,
+	.dissolving = bridge_base_dissolving,
 	.can_push = bridge_base_can_push,
 	.push = bridge_base_push,
 	.pull = bridge_base_pull,
@@ -1349,9 +1412,7 @@
 
 	bridge = ast_bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_base_v_table);
 	bridge = ast_bridge_base_init(bridge, capabilities, flags);
-	if (bridge) {
-		ast_bridge_publish_state(bridge);
-	}
+	bridge = ast_bridge_register(bridge);
 	return bridge;
 }
 
@@ -1372,7 +1433,7 @@
 {
 	ast_debug(1, "Bridge %s: telling all channels to leave the party\n", bridge->uniqueid);
 	ast_bridge_lock(bridge);
-	bridge_force_out_all(bridge);
+	bridge_dissolve(bridge);
 	ast_bridge_unlock(bridge);
 
 	ao2_ref(bridge, -1);
@@ -1647,8 +1708,8 @@
 	bridge->reconfigured = 0;
 	if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART)
 		&& smart_bridge_operation(bridge)) {
-		/* Smart bridge failed.  Dissolve the bridge. */
-		bridge_force_out_all(bridge);
+		/* Smart bridge failed. */
+		bridge_dissolve(bridge);
 		return;
 	}
 	bridge_complete_join(bridge);
@@ -2314,15 +2375,6 @@
 	bridge_channel_pull(bridge_channel);
 	bridge_reconfigured(bridge_channel->bridge);
 
-	/* See if we need to dissolve the bridge itself if they hung up */
-	switch (bridge_channel->state) {
-	case AST_BRIDGE_CHANNEL_STATE_END:
-		bridge_check_dissolve(bridge_channel);
-		break;
-	default:
-		break;
-	}
-
 	ast_bridge_unlock(bridge_channel->bridge);
 
 	/* Indicate a source change since this channel is leaving the bridge system. */
@@ -4059,6 +4111,446 @@
 
 /*!
  * \internal
+ * \brief Bridge ao2 container sort function.
+ * \since 12.0.0
+ *
+ * \param obj_left pointer to the (user-defined part) of an object.
+ * \param obj_right pointer to the (user-defined part) of an object.
+ * \param flags flags from ao2_callback()
+ *   OBJ_POINTER - if set, 'obj_right', is an object.
+ *   OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
+ *   OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
+ *
+ * \retval <0 if obj_left < obj_right
+ * \retval =0 if obj_left == obj_right
+ * \retval >0 if obj_left > obj_right
+ */
+static int bridge_sort_cmp(const void *obj_left, const void *obj_right, int flags)
+{
+	const struct ast_bridge *bridge_left = obj_left;
+	const struct ast_bridge *bridge_right = obj_right;
+	const char *right_key = obj_right;
+	int cmp;
+
+	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+	default:
+	case OBJ_POINTER:
+		right_key = bridge_right->uniqueid;
+		/* Fall through */
+	case OBJ_KEY:
+		cmp = strcmp(bridge_left->uniqueid, right_key);
+		break;
+	case OBJ_PARTIAL_KEY:
+		cmp = strncmp(bridge_left->uniqueid, right_key, strlen(right_key));
+		break;
+	}
+	return cmp;
+}
+
+struct bridge_complete {
+	/*! Nth match to return. */
+	int state;
+	/*! Which match currently on. */
+	int which;
+};
+
+static int complete_bridge_search(void *obj, void *arg, void *data, int flags)
+{
+	struct bridge_complete *search = data;
+
+	if (++search->which > search->state) {
+		return CMP_MATCH;
+	}
+	return 0;
+}
+
+static char *complete_bridge(const char *word, int state)
+{
+	char *ret;
+	struct ast_bridge *bridge;
+	struct bridge_complete search = {
+		.state = state,
+		};
+
+	bridge = ao2_callback_data(bridges, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
+		complete_bridge_search, (char *) word, &search);
+	if (!bridge) {
+		return NULL;
+	}
+	ret = ast_strdup(bridge->uniqueid);
+	ao2_ref(bridge, -1);
+	return ret;
+}
+
+static char *handle_bridge_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT_HDR "%-36s %5s %-15s %s\n"
+#define FORMAT_ROW "%-36s %5u %-15s %s\n"
+
+	struct ao2_iterator iter;
+	struct ast_bridge *bridge;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "bridge show all";
+		e->usage =
+			"Usage: bridge show all\n"
+			"       List all bridges\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+/* BUGBUG this command may need to be changed to look at the stasis cache. */
+	ast_cli(a->fd, FORMAT_HDR, "Bridge-ID", "Chans", "Type", "Technology");
+	iter = ao2_iterator_init(bridges, 0);
+	for (; (bridge = ao2_iterator_next(&iter)); ao2_ref(bridge, -1)) {
+		ast_bridge_lock(bridge);
+		ast_cli(a->fd, FORMAT_ROW,
+			bridge->uniqueid,
+			bridge->num_channels,
+			bridge->v_table ? bridge->v_table->name : "<unknown>",
+			bridge->technology ? bridge->technology->name : "<unknown>");
+		ast_bridge_unlock(bridge);
+	}
+	ao2_iterator_destroy(&iter);
+	return CLI_SUCCESS;
+
+#undef FORMAT_HDR
+#undef FORMAT_ROW
+}
+
+static char *handle_bridge_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ast_bridge *bridge;
+	struct ast_bridge_channel *bridge_channel;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "bridge show";
+		e->usage =
+			"Usage: bridge show <bridge-id>\n"
+			"       Show information about the <bridge-id> bridge\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 2) {
+			return complete_bridge(a->word, a->n);
+		}
+		return NULL;
+	}
+
+/* BUGBUG this command may need to be changed to look at the stasis cache. */
+	if (a->argc != 3) {
+		return CLI_SHOWUSAGE;
+	}
+
+	bridge = ao2_find(bridges, a->argv[2], OBJ_KEY);
+	if (!bridge) {
+		ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]);
+		return CLI_SUCCESS;
+	}
+
+	ast_bridge_lock(bridge);
+	ast_cli(a->fd, "Id: %s\n", bridge->uniqueid);
+	ast_cli(a->fd, "Type: %s\n", bridge->v_table ? bridge->v_table->name : "<unknown>");
+	ast_cli(a->fd, "Technology: %s\n",
+		bridge->technology ? bridge->technology->name : "<unknown>");
+	ast_cli(a->fd, "Num-Channels: %u\n", bridge->num_channels);
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		ast_cli(a->fd, "Channel: %s\n", ast_channel_name(bridge_channel->chan));
+	}
+	ast_bridge_unlock(bridge);
+	ao2_ref(bridge, -1);
+
+	return CLI_SUCCESS;
+}
+
+static char *handle_bridge_destroy_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ast_bridge *bridge;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "bridge destroy";
+		e->usage =
+			"Usage: bridge destroy <bridge-id>\n"
+			"       Destroy the <bridge-id> bridge\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 2) {
+			return complete_bridge(a->word, a->n);
+		}
+		return NULL;
+	}
+
+	if (a->argc != 3) {
+		return CLI_SHOWUSAGE;
+	}
+
+	bridge = ao2_find(bridges, a->argv[2], OBJ_KEY);
+	if (!bridge) {
+		ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]);
+		return CLI_SUCCESS;
+	}
+
+	ast_cli(a->fd, "Destroying bridge '%s'\n", a->argv[2]);
+	ast_bridge_destroy(bridge);
+
+	return CLI_SUCCESS;
+}
+
+static char *complete_bridge_participant(const char *bridge_name, const char *line, const char *word, int pos, int state)
+{
+	RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+	struct ast_bridge_channel *bridge_channel;
+	int which;
+	int wordlen;
+
+	bridge = ao2_find(bridges, bridge_name, OBJ_KEY);
+	if (!bridge) {
+		return NULL;
+	}
+
+	{
+		SCOPED_LOCK(bridge_lock, bridge, ast_bridge_lock, ast_bridge_unlock);
+
+		which = 0;
+		wordlen = strlen(word);
+		AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+			if (!strncasecmp(ast_channel_name(bridge_channel->chan), word, wordlen)
+				&& ++which > state) {
+				return ast_strdup(ast_channel_name(bridge_channel->chan));
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static char *handle_bridge_kick_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "bridge kick";
+		e->usage =
+			"Usage: bridge kick <bridge-id> <channel-name>\n"
+			"       Kick the <channel-name> channel out of the <bridge-id> bridge\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 2) {
+			return complete_bridge(a->word, a->n);
+		}
+		if (a->pos == 3) {
+			return complete_bridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
+		}
+		return NULL;
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	bridge = ao2_find(bridges, a->argv[2], OBJ_KEY);
+	if (!bridge) {
+		ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]);
+		return CLI_SUCCESS;
+	}
+
+	chan = ast_channel_get_by_name_prefix(a->argv[3], strlen(a->argv[3]));
+	if (!chan) {
+		ast_cli(a->fd, "Channel '%s' not found\n", a->argv[3]);
+		return CLI_SUCCESS;
+	}
+
+/*
+ * BUGBUG the CLI kick needs to get the bridge to decide if it should dissolve.
+ *
+ * Likely the best way to do this is to add a kick method.  The
+ * basic bridge class can then decide to dissolve the bridge if
+ * one of two channels is kicked.
+ *
+ * SIP/foo -- Local;1==Local;2 -- .... -- Local;1==Local;2 -- SIP/bar
+ * Kick a ;1 channel and the chain toward SIP/foo goes away.
+ * Kick a ;2 channel and the chain toward SIP/bar goes away.
+ *
+ * This can leave a local channel chain between the kicked ;1
+ * and ;2 channels that are orphaned until you manually request
+ * one of those channels to hangup or request the bridge to
+ * dissolve.
+ */
+	ast_cli(a->fd, "Kicking channel '%s' from bridge '%s'\n",
+		ast_channel_name(chan), a->argv[2]);
+	ast_bridge_remove(bridge, chan);
+
+	return CLI_SUCCESS;
+}
+
+/*! Bridge technology capabilities to string. */
+static const char *tech_capability2str(uint32_t capabilities)
+{
+	const char *type;
+
+	if (capabilities & AST_BRIDGE_CAPABILITY_HOLDING) {
+		type = "Holding";
+	} else if (capabilities & AST_BRIDGE_CAPABILITY_EARLY) {
+		type = "Early";
+	} else if (capabilities & AST_BRIDGE_CAPABILITY_NATIVE) {
+		type = "Native";
+	} else if (capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX) {
+		type = "1to1Mix";
+	} else if (capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
+		type = "MultiMix";
+	} else {
+		type = "<Unknown>";
+	}
+	return type;
+}
+
+/*! Bridge technology priority preference to string. */
+static const char *tech_preference2str(enum ast_bridge_preference preference)
+{
+	const char *priority;
+
+	priority = "<Unknown>";
+	switch (preference) {
+	case AST_BRIDGE_PREFERENCE_HIGH:
+		priority = "High";
+		break;
+	case AST_BRIDGE_PREFERENCE_MEDIUM:
+		priority = "Medium";
+		break;
+	case AST_BRIDGE_PREFERENCE_LOW:
+		priority = "Low";
+		break;
+	}
+	return priority;
+}
+
+static char *handle_bridge_technology_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-20s %-20s %-10s %s\n"
+
+	struct ast_bridge_technology *cur;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "bridge technology show";
+		e->usage =
+			"Usage: bridge technology show\n"
+			"       List registered bridge technologies\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	ast_cli(a->fd, FORMAT, "Name", "Type", "Priority", "Suspended");
+	AST_RWLIST_RDLOCK(&bridge_technologies);
+	AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) {
+		const char *type;
+		const char *priority;
+
+		/* Decode type for display */
+		type = tech_capability2str(cur->capabilities);
+
+		/* Decode priority preference for display */
+		priority = tech_preference2str(cur->preference);
+
+		ast_cli(a->fd, FORMAT, cur->name, type, priority, AST_CLI_YESNO(cur->suspended));
+	}
+	AST_RWLIST_UNLOCK(&bridge_technologies);
+	return CLI_SUCCESS;
+
+#undef FORMAT
+}
+
+static char *complete_bridge_technology(const char *word, int state)
+{
+	struct ast_bridge_technology *cur;
+	char *res;
+	int which;
+	int wordlen;
+
+	which = 0;
+	wordlen = strlen(word);
+	AST_RWLIST_RDLOCK(&bridge_technologies);
+	AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) {
+		if (!strncasecmp(cur->name, word, wordlen) && ++which > state) {
+			res = ast_strdup(cur->name);
+			AST_RWLIST_UNLOCK(&bridge_technologies);
+			return res;
+		}
+	}
+	AST_RWLIST_UNLOCK(&bridge_technologies);
+	return NULL;
+}
+
+static char *handle_bridge_technology_suspend(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ast_bridge_technology *cur;
+	int suspend;
+	int successful;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "bridge technology {suspend|unsuspend}";
+		e->usage =
+			"Usage: bridge technology {suspend|unsuspend} <technology-name>\n"
+			"       Suspend or unsuspend a bridge technology.\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 3) {
+			return complete_bridge_technology(a->word, a->n);
+		}
+		return NULL;
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	suspend = !strcasecmp(a->argv[2], "suspend");
+	successful = 0;
+	AST_RWLIST_WRLOCK(&bridge_technologies);
+	AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) {
+		if (!strcasecmp(cur->name, a->argv[3])) {
+			successful = 1;
+			if (suspend) {
+				ast_bridge_technology_suspend(cur);
+			} else {
+				ast_bridge_technology_unsuspend(cur);
+			}
+			break;
+		}
+	}
+	AST_RWLIST_UNLOCK(&bridge_technologies);
+
+	if (successful) {
+		if (suspend) {
+			ast_cli(a->fd, "Suspended bridge technology '%s'\n", a->argv[3]);
+		} else {
+			ast_cli(a->fd, "Unsuspended bridge technology '%s'\n", a->argv[3]);
+		}
+	} else {
+		ast_cli(a->fd, "Bridge technology '%s' not found\n", a->argv[3]);
+	}
+
+	return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry bridge_cli[] = {
+	AST_CLI_DEFINE(handle_bridge_show_all, "List all bridges"),
+	AST_CLI_DEFINE(handle_bridge_show_specific, "Show information about a bridge"),
+	AST_CLI_DEFINE(handle_bridge_destroy_specific, "Destroy a bridge"),
+	AST_CLI_DEFINE(handle_bridge_kick_channel, "Kick a channel from a bridge"),
+	AST_CLI_DEFINE(handle_bridge_technology_show, "List registered bridge technologies"),
+	AST_CLI_DEFINE(handle_bridge_technology_suspend, "Suspend/unsuspend a bridge technology"),
+};
+
+/*!
+ * \internal
  * \brief Shutdown the bridging system.
  * \since 12.0.0
  *
@@ -4066,6 +4558,9 @@
  */
 static void bridge_shutdown(void)
 {
+	ast_cli_unregister_multiple(bridge_cli, ARRAY_LEN(bridge_cli));
+	ao2_cleanup(bridges);
+	bridges = NULL;
 	ao2_cleanup(bridge_manager);
 	bridge_manager = NULL;
 	ast_stasis_bridging_shutdown();
@@ -4080,8 +4575,19 @@
 
 	bridge_manager = bridge_manager_create();
 	if (!bridge_manager) {
+		bridge_shutdown();
 		return -1;
 	}
+
+	bridges = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_MUTEX,
+		AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, bridge_sort_cmp, NULL);
+	if (!bridges) {
+		bridge_shutdown();
+		return -1;
+	}
+
+/* BUGBUG need AMI action equivalents to the CLI commands. */
+	ast_cli_register_multiple(bridge_cli, ARRAY_LEN(bridge_cli));
 
 	ast_register_atexit(bridge_shutdown);
 	return 0;

Modified: team/group/bridge_construction/main/features.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/features.c?view=diff&rev=385505&r1=385504&r2=385505
==============================================================================
--- team/group/bridge_construction/main/features.c (original)
+++ team/group/bridge_construction/main/features.c Fri Apr 12 11:38:28 2013
@@ -4576,7 +4576,7 @@
 /* BUGBUG need to create the basic bridge class that will manage the DTMF feature hooks. */
 	bridge = ast_bridge_base_new(
 		AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_MULTIMIX,
-		AST_BRIDGE_FLAG_DISSOLVE_HANGUP | AST_BRIDGE_FLAG_SMART);
+		AST_BRIDGE_FLAG_DISSOLVE_HANGUP | AST_BRIDGE_FLAG_DISSOLVE_EMPTY | AST_BRIDGE_FLAG_SMART);
 	if (!bridge) {
 		ast_bridge_features_destroy(peer_features);
 		ast_bridge_features_cleanup(&chan_features);




More information about the asterisk-commits mailing list