[asterisk-commits] rmudgett: branch rmudgett/bridge_phase r384584 - in /team/rmudgett/bridge_pha...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Apr 2 13:27:40 CDT 2013


Author: rmudgett
Date: Tue Apr  2 13:27:36 2013
New Revision: 384584

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=384584
Log:
* 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.

* Add AST_BRIDGE_CAPABILITY_EARLY in anticipation of early bridge.

Modified:
    team/rmudgett/bridge_phase/include/asterisk/bridging.h
    team/rmudgett/bridge_phase/main/bridging.c

Modified: team/rmudgett/bridge_phase/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/include/asterisk/bridging.h?view=diff&rev=384584&r1=384583&r2=384584
==============================================================================
--- team/rmudgett/bridge_phase/include/asterisk/bridging.h (original)
+++ team/rmudgett/bridge_phase/include/asterisk/bridging.h Tue Apr  2 13:27:36 2013
@@ -78,12 +78,14 @@
 enum ast_bridge_capability {
 	/*! Bridge technology can service calls on hold */
 	AST_BRIDGE_CAPABILITY_HOLDING = (1 << 0),
+	/*! Bridge waits for channel to answer.  Passes early media. */
+	AST_BRIDGE_CAPABILITY_EARLY = (1 << 1),
 	/*! Bridge should natively bridge two channels if possible */
-	AST_BRIDGE_CAPABILITY_NATIVE = (1 << 1),
+	AST_BRIDGE_CAPABILITY_NATIVE = (1 << 2),
 	/*! Bridge is only capable of mixing 2 channels */
-	AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 2),
+	AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 3),
 	/*! Bridge is capable of mixing 2 or more channels */
-	AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 3),
+	AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 4),
 };
 
 /*! \brief State information about a bridged channel */
@@ -394,6 +396,37 @@
 };
 
 /*!
+ * \brief Register the new bridge with the system.
+ * \since 12.0.0
+ *
+ * \param bridge What to register. (Tollerates a NULL pointer)
+ *
+ * \code
+ * struct ast_bridge *ast_bridge_basic_new(uint32_t capabilities, int flags)
+ * {
+ *     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

Modified: team/rmudgett/bridge_phase/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/main/bridging.c?view=diff&rev=384584&r1=384583&r2=384584
==============================================================================
--- team/rmudgett/bridge_phase/main/bridging.c (original)
+++ team/rmudgett/bridge_phase/main/bridging.c Tue Apr  2 13:27:36 2013
@@ -55,6 +55,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);
 
@@ -952,6 +956,15 @@
 	cleanup_video_mode(bridge);
 }
 
+struct ast_bridge *ast_bridge_register(struct ast_bridge *bridge)
+{
+	if (bridge && !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;
@@ -1048,8 +1061,7 @@
  */
 static void bridge_base_dissolving(struct ast_bridge *self)
 {
-/* BUGBUG unlink from the global bridges container. */
-	/*! \todo BUGBUG bridge_base_dissolving() not written */
+	ao2_unlink(bridges, self);
 }
 
 /*!
@@ -1141,6 +1153,7 @@
 
 	bridge = ast_bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_base_v_table);
 	bridge = ast_bridge_base_init(bridge, capabilities, flags);
+	bridge = ast_bridge_register(bridge);
 	return bridge;
 }
 
@@ -3668,6 +3681,410 @@
 
 /*!
  * \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"
+			"       Lists active bridges\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	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 a specific 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_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 a specific 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>\n"
+			"       Kick a channel out of a 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;
+	}
+
+	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;
+}
+
+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"
+			"       Lists 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 */
+		if (cur->capabilities & AST_BRIDGE_CAPABILITY_HOLDING) {
+			type = "Holding";
+		} else if (cur->capabilities & AST_BRIDGE_CAPABILITY_EARLY) {
+			type = "Early";
+		} else if (cur->capabilities & AST_BRIDGE_CAPABILITY_NATIVE) {
+			type = "Native";
+		} else if (cur->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX) {
+			type = "1to1Mix";
+		} else if (cur->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
+			type = "MultiMix";
+		} else {
+			type = "<Unknown>";
+		}
+
+		/* Decode priority for display */
+		priority = "<Unknown>";
+		switch (cur->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;
+		}
+
+		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>\n"
+			"       Suspend or unsuspend the specified 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 bridges"),
+	AST_CLI_DEFINE(handle_bridge_show_specific, "Show a specific bridge"),
+	AST_CLI_DEFINE(handle_bridge_destroy_specific, "Destroy specific bridge"),
+	AST_CLI_DEFINE(handle_bridge_kick_channel, "Kick a channel from a bridge"),
+	AST_CLI_DEFINE(handle_bridge_technology_show, "List bridge technologies"),
+	AST_CLI_DEFINE(handle_bridge_technology_suspend, "Suspend/unsuspend bridge technology"),
+};
+
+/*!
+ * \internal
  * \brief Shutdown the bridging system.
  * \since 12.0.0
  *
@@ -3675,6 +4092,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;
 }
@@ -3683,8 +4103,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;




More information about the asterisk-commits mailing list