[asterisk-commits] kmoore: branch kmoore/stasis-bridging_events r389960 - in /team/kmoore/stasis...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue May 28 15:45:50 CDT 2013


Author: kmoore
Date: Tue May 28 15:45:47 2013
New Revision: 389960

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=389960
Log:
Commit lots of things that are now in a working state

Things that now work:
* Stasis-HTTP
** GET stasis/bridges
** GET stasis/bridges/bridgeid
** POST stasis/bridges/bridgeid
** DELETE stasis/bridges/bridgeid
** POST stasis/bridges/bridgeid/addChannel
** POST stasis/bridges/bridgeid/removeChannel
* Stasis apps now receive bridge events when appropriate

There are several ugly pieces of code here that require refactoring.

Added:
    team/kmoore/stasis-bridging_events/res/stasis/bridge_command.c   (with props)
    team/kmoore/stasis-bridging_events/res/stasis/bridge_command.h   (with props)
    team/kmoore/stasis-bridging_events/res/stasis/bridge_control.c   (with props)
    team/kmoore/stasis-bridging_events/res/stasis/bridge_control.h   (with props)
Modified:
    team/kmoore/stasis-bridging_events/include/asterisk/stasis_app.h
    team/kmoore/stasis-bridging_events/include/asterisk/stasis_app_impl.h
    team/kmoore/stasis-bridging_events/res/res_stasis.c
    team/kmoore/stasis-bridging_events/res/stasis/app.c
    team/kmoore/stasis-bridging_events/res/stasis/control.c
    team/kmoore/stasis-bridging_events/res/stasis/control.h
    team/kmoore/stasis-bridging_events/res/stasis_http/resource_bridges.c

Modified: team/kmoore/stasis-bridging_events/include/asterisk/stasis_app.h
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging_events/include/asterisk/stasis_app.h?view=diff&rev=389960&r1=389959&r2=389960
==============================================================================
--- team/kmoore/stasis-bridging_events/include/asterisk/stasis_app.h (original)
+++ team/kmoore/stasis-bridging_events/include/asterisk/stasis_app.h Tue May 28 15:45:47 2013
@@ -186,6 +186,115 @@
 int stasis_app_control_queue_control(struct stasis_app_control *control,
 	enum ast_control_frame_type frame_type);
 
+/*! \brief Handler for controlling a bridge that is managed by a Stasis application */
+struct stasis_app_bridge_control;
+
+/*!
+ * \brief Create a bridge control object and bridge to control.
+ *
+ * \param type The type of bridge to be created
+ *
+ * \return New bridge control object.
+ * \return \c NULL on error.
+ */
+struct stasis_app_bridge_control *stasis_app_bridge_control_create_bridge(const char *type);
+
+/*!
+ * \brief Returns the handler for the given bridge.
+ * \param bridge Bridge to handle.
+ * \return NULL bridge not owned by Stasis application.
+ * \return Pointer to \c res_stasis handler.
+ */
+struct stasis_app_bridge_control *stasis_app_bridge_control_find_by_bridge(
+	const struct ast_bridge *bridge);
+
+/*!
+ * \brief Returns the handler for the bridge with the given id.
+ * \param bridge_id Uniqueid of the bridge.
+ * \return NULL bridge not in Stasis application, or bridge does not exist.
+ * \return Pointer to \c res_stasis handler.
+ */
+struct stasis_app_bridge_control *stasis_app_bridge_control_find_by_bridge_id(
+	const char *bridge_id);
+
+/*!
+ * \brief Returns the uniqueid of the bridge associated with this control
+ *
+ * \param control Control object.
+ * \return Uniqueid of the associate bridge.
+ * \return \c NULL if \a control is \c NULL.
+ */
+const char *stasis_app_bridge_control_get_bridge_id(
+	const struct stasis_app_bridge_control *control);
+
+/*!
+ * \brief Returns the most recent snapshot for the associated bridge.
+ *
+ * The returned pointer is AO2 managed, so ao2_cleanup() when you're done.
+ *
+ * \param control Control for \c res_stasis.
+ * \return Most recent snapshot. ao2_cleanup() when done.
+ * \return \c NULL if bridge isn't in cache.
+ */
+struct ast_bridge_snapshot *stasis_app_bridge_control_get_snapshot(
+	const struct stasis_app_bridge_control *control);
+
+/*!
+ * \brief Creates a snapshot for the associated bridge.
+ *
+ * The returned pointer is AO2 managed, so ao2_cleanup() when you're done.
+ *
+ * \param control Control for \c res_stasis.
+ * \return The new snapshot. ao2_cleanup() when done.
+ * \return \c NULL on error.
+ */
+struct ast_bridge_snapshot *stasis_app_bridge_control_create_snapshot(
+	const struct stasis_app_bridge_control *control);
+
+/*!
+ * \brief Publish a message to the \a control's bridge's topic.
+ *
+ * \param control Control to publish to
+ * \param message Message to publish
+ */
+void stasis_app_bridge_control_publish(
+	struct stasis_app_bridge_control *control, struct stasis_message *message);
+
+/*!
+ * \brief Add a channel to the \a control's bridge.
+ *
+ * \param control Control whose bridge should get the channel
+ * \param channel Pointer to the channel to add to the bridge (consumes a reference)
+ *
+ * \retval non-zero on failure
+ * \retval zero on success
+ */
+int stasis_app_bridge_control_add_channel(
+	struct stasis_app_bridge_control *control, struct ast_channel *channel);
+
+/*!
+ * \brief Remove a channel from the \a control's bridge.
+ *
+ * \param control Control whose bridge should have the channel removed
+ * \param channel Pointer to the channel to remove from the bridge (consumes a reference)
+ *
+ * \retval non-zero on failure
+ * \retval zero on success
+ */
+int stasis_app_bridge_control_remove_channel(
+	struct stasis_app_bridge_control *control, struct ast_channel *channel);
+
+/*!
+ * \brief Destroy the \a control's bridge.
+ *
+ * \param control Control whose bridge will be destroyed
+ *
+ * \retval non-zero on failure
+ * \retval zero on success
+ */
+int stasis_app_bridge_control_destroy_bridge(
+	struct stasis_app_bridge_control *control);
+
 /*!
  * \brief Increment the res_stasis reference count.
  *

Modified: team/kmoore/stasis-bridging_events/include/asterisk/stasis_app_impl.h
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging_events/include/asterisk/stasis_app_impl.h?view=diff&rev=389960&r1=389959&r2=389960
==============================================================================
--- team/kmoore/stasis-bridging_events/include/asterisk/stasis_app_impl.h (original)
+++ team/kmoore/stasis-bridging_events/include/asterisk/stasis_app_impl.h Tue May 28 15:45:47 2013
@@ -71,7 +71,7 @@
 
 /*!
  * \since 12
- * \brief Asynchronous version of stasis_app_send().
+ * \brief Asynchronous version of stasis_app_send_command().
  *
  * This function enqueues a command for execution, but returns immediately
  * without waiting for the response.
@@ -85,4 +85,62 @@
 int stasis_app_send_command_async(struct stasis_app_control *control,
 	stasis_app_command_cb command, void *data);
 
+/*!
+ * \since 12
+ * \brief Control a bridge using \c stasis_app.
+ *
+ * This function blocks until the bridge hangs up, or
+ * stasis_app_control_continue() is called on the bridge's \ref
+ * stasis_app_control struct.
+ *
+ * \param bridge Channel to control with Stasis.
+ * \param app_name Application controlling the bridge.
+ * \param argc Number of arguments for the application.
+ * \param argv Arguments for the application.
+ */
+int stasis_app_bridge_exec(struct ast_bridge *bridge, const char *app_name, int argc,
+	char *argv[]);
+
+/*! Callback type for stasis app bridge commands */
+typedef void *(*stasis_app_bridge_command_cb)(struct stasis_app_bridge_control *control, void *data);
+
+/*!
+ * \since 12
+ * \brief Invokes a \a command on a \a control's bridge.
+ *
+ * This function dispatches the command to be executed in the context of
+ * stasis_app_exec(), so this command will block waiting for the results of
+ * the command.
+ *
+ * \param control Control object for the bridge to send the command to.
+ * \param command Command function to execute.
+ * \param data Optional data to pass along with the control function.
+ * \return Return value from \a command.
+ * \return \c NULL on error.
+ */
+void *stasis_app_send_bridge_command(struct stasis_app_bridge_control *control,
+	stasis_app_bridge_command_cb command, void *data);
+
+/*!
+ * \since 12
+ * \brief Asynchronous version of stasis_app_send_bridge_command().
+ *
+ * This function enqueues a command for execution, but returns immediately
+ * without waiting for the response.
+ *
+ * \param control Control object for the bridge to send the command to.
+ * \param command Command function to execute.
+ * \param data Optional data to pass along with the control function.
+ * \return 0 on success.
+ * \return Non-zero on error.
+ */
+int stasis_app_send_bridge_command_async(struct stasis_app_bridge_control *control,
+	stasis_app_bridge_command_cb command, void *data);
+
+/*!
+ * \brief Remove the stasis_app_bridge_control from the searchable container of stasis_app_bridge_controls
+ *
+ * \param control The control to be removed
+ */
+void stasis_app_remove_bridge_control(struct stasis_app_bridge_control *control);
 #endif /* _ASTERISK_RES_STASIS_H */

Modified: team/kmoore/stasis-bridging_events/res/res_stasis.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging_events/res/res_stasis.c?view=diff&rev=389960&r1=389959&r2=389960
==============================================================================
--- team/kmoore/stasis-bridging_events/res/res_stasis.c (original)
+++ team/kmoore/stasis-bridging_events/res/res_stasis.c Tue May 28 15:45:47 2013
@@ -66,6 +66,8 @@
 #include "asterisk/strings.h"
 #include "stasis/app.h"
 #include "stasis/control.h"
+#include "stasis/bridge_control.h"
+#include "stasis/bridge_command.h" /* for init and cleanup */
 #include "stasis_json/resource_events.h"
 
 /*! Time to wait for a frame in the application */
@@ -90,6 +92,8 @@
 
 struct ao2_container *app_controls;
 
+struct ao2_container *app_bridge_controls;
+
 /*! \brief Message router for the channel caching topic */
 struct stasis_message_router *channel_router;
 
@@ -161,6 +165,48 @@
 	const char *channel_id)
 {
 	return ao2_find(app_controls, channel_id, OBJ_KEY);
+}
+
+/*! AO2 hash function for \ref stasis_app_bridge_control */
+static int bridge_control_hash(const void *obj, const int flags)
+{
+	const struct stasis_app_bridge_control *control = obj;
+	const char *id = flags & OBJ_KEY ?
+		obj : stasis_app_bridge_control_get_bridge_id(control);
+
+	return ast_str_hash(id);
+}
+
+/*! AO2 comparison function for \ref stasis_app_bridge_control */
+static int bridge_control_compare(void *lhs, void *rhs, int flags)
+{
+	const struct stasis_app_bridge_control *lhs_control = lhs;
+	const struct stasis_app_bridge_control *rhs_control = rhs;
+	const char *lhs_id = stasis_app_bridge_control_get_bridge_id(lhs_control);
+	const char *rhs_id = flags & OBJ_KEY ?
+		rhs : stasis_app_bridge_control_get_bridge_id(rhs_control);
+
+	if (strcmp(lhs_id, rhs_id) == 0) {
+		return CMP_MATCH | CMP_STOP;
+	} else {
+		return 0;
+	}
+}
+
+struct stasis_app_bridge_control *stasis_app_bridge_control_find_by_bridge(
+	const struct ast_bridge *bridge)
+{
+	if (bridge == NULL) {
+		return NULL;
+	}
+
+	return stasis_app_bridge_control_find_by_bridge_id(bridge->uniqueid);
+}
+
+struct stasis_app_bridge_control *stasis_app_bridge_control_find_by_bridge_id(
+	const char *bridge_id)
+{
+	return ao2_find(app_bridge_controls, bridge_id, OBJ_KEY);
 }
 
 /*! \brief Typedef for blob handler callbacks */
@@ -361,7 +407,7 @@
 
 /*!
  * \brief In addition to running ao2_cleanup(), this function also removes the
- * object from the app_controls() container.
+ * object from the app_controls container.
  */
 static void control_unlink(struct stasis_app_control *control)
 {
@@ -371,6 +417,48 @@
 
 	ao2_unlink_flags(app_controls, control,
 		OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
+	ao2_cleanup(control);
+}
+
+struct stasis_app_bridge_control *stasis_app_bridge_control_create_bridge(const char *type)
+{
+	struct stasis_app_bridge_control *control;
+	int flags;
+	if (ast_strlen_zero(type)) {
+		flags = AST_BRIDGE_CAPABILITY_MULTIMIX;
+	} else if (!strcmp(type, "two-party")) {
+		flags = AST_BRIDGE_CAPABILITY_1TO1MIX;
+	} else if (!strcmp(type, "multi-party")) {
+		flags = AST_BRIDGE_CAPABILITY_MULTIMIX;
+	} else if (!strcmp(type, "holding")) {
+		flags = AST_BRIDGE_CAPABILITY_HOLDING;
+	} else {
+		return NULL;
+	}
+
+	control = bridge_control_create(flags);
+	if (control) {
+		ao2_link(app_bridge_controls, control);
+	}
+	return control;
+}
+
+void stasis_app_remove_bridge_control(struct stasis_app_bridge_control *control)
+{
+	ao2_unlink(app_bridge_controls, control);
+}
+
+/*!
+ * \brief In addition to running ao2_cleanup(), this function also removes the
+ * object from the app_bridge_controls container.
+ */
+static void bridge_control_unlink(struct stasis_app_bridge_control *control)
+{
+	if (!control) {
+		return;
+	}
+
+	stasis_app_remove_bridge_control(control);
 	ao2_cleanup(control);
 }
 
@@ -483,7 +571,12 @@
 		int r;
 		int command_count;
 
-		r = ast_waitfor(chan, MAX_WAIT_MS);
+		if (control_is_bridged(control)) {
+			r = 0;
+			usleep(MAX_WAIT_MS);
+		} else {
+			r = ast_waitfor(chan, MAX_WAIT_MS);
+		}
 
 		if (r < 0) {
 			ast_debug(3, "%s: Poll error\n",
@@ -498,7 +591,7 @@
 			continue;
 		}
 
-		if (r == 0) {
+		if (r == 0 || control_is_bridged(control)) {
 			/* Timeout */
 			continue;
 		}
@@ -711,6 +804,12 @@
 	return watching_apps_iter->c;
 }
 
+static int remove_bridge_cb(void *obj, void *arg, int flags)
+{
+	app_remove_bridge(obj, arg);
+	return 0;
+}
+
 static void sub_bridge_snapshot_handler(void *data,
 		struct stasis_subscription *sub,
 		struct stasis_topic *topic,
@@ -728,7 +827,14 @@
 	}
 
 	if (!new_snapshot) {
+		RAII_VAR(char *, bridge_id, ast_strdup(old_snapshot->uniqueid), ast_free);
+
+		/* The bridge has gone away. Create the message, make sure no apps are
+		 * watching this bridge anymore, and destroy the bridge's control
+		 * structure */
 		msg = stasis_json_event_bridge_destroyed_create(old_snapshot);
+		ao2_callback(watching_apps, OBJ_NODATA, remove_bridge_cb, bridge_id);
+		bridge_control_unlink(stasis_app_bridge_control_find_by_bridge_id(old_snapshot->uniqueid));
 	} else if (!old_snapshot) {
 		msg = stasis_json_event_bridge_created_create(old_snapshot);
 	}
@@ -754,6 +860,18 @@
 	ao2_callback(src, OBJ_NODATA, list_merge_cb, dst);
 }
 
+static int app_add_bridge_cb(void *obj, void *arg, int flags)
+{
+	app_add_bridge(obj, arg);
+	return 0;
+}
+
+static void update_bridge_interest(struct ao2_container *apps, const char *bridge_id)
+{
+	RAII_VAR(char *, bridge_id_dup, ast_strdup(bridge_id), ast_free);
+	ao2_callback(apps, OBJ_NODATA, app_add_bridge_cb, bridge_id_dup);
+}
+
 static void sub_bridge_merge_handler(void *data,
 		struct stasis_subscription *sub,
 		struct stasis_topic *topic,
@@ -761,22 +879,21 @@
 {
 	RAII_VAR(struct ao2_container *, watching_apps_to, NULL, ao2_cleanup);
 	RAII_VAR(struct ao2_container *, watching_apps_from, NULL, ao2_cleanup);
-	RAII_VAR(struct ao2_container *, watching_apps_all, ast_str_container_alloc(1), ao2_cleanup);
+	RAII_VAR(struct ao2_container *, watching_apps_all, ao2_container_alloc(1, NULL, NULL), ao2_cleanup);
 	struct ast_bridge_merge_message *merge = stasis_message_data(message);
 	RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
 	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
 
 	watching_apps_to = get_apps_watching_bridge(merge->to->uniqueid);
-	if (!watching_apps_to) {
-		return;
-	}
-	update_apps_list(watching_apps_all, watching_apps_to);
+	if (watching_apps_to) {
+		update_apps_list(watching_apps_all, watching_apps_to);
+	}
 
 	watching_apps_from = get_apps_watching_bridge(merge->from->uniqueid);
-	if (!watching_apps_from) {
-		return;
-	}
-	update_apps_list(watching_apps_all, watching_apps_from);
+	if (watching_apps_from) {
+		update_bridge_interest(watching_apps_from, merge->to->uniqueid);
+		update_apps_list(watching_apps_all, watching_apps_from);
+	}
 
 	if (!ao2_container_count(watching_apps_all)) {
 		return;
@@ -802,21 +919,20 @@
 {
 	RAII_VAR(struct ao2_container *, watching_apps_channel, NULL, ao2_cleanup);
 	RAII_VAR(struct ao2_container *, watching_apps_bridge, NULL, ao2_cleanup);
-	RAII_VAR(struct ao2_container *, watching_apps_all, ast_str_container_alloc(1), ao2_cleanup);
+	RAII_VAR(struct ao2_container *, watching_apps_all, ao2_container_alloc(1, NULL, NULL), ao2_cleanup);
 	struct ast_bridge_blob *obj = stasis_message_data(message);
 	RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
 
 	watching_apps_bridge = get_apps_watching_bridge(obj->bridge->uniqueid);
-	if (!watching_apps_bridge) {
-		return;
-	}
-	update_apps_list(watching_apps_all, watching_apps_bridge);
+	if (watching_apps_bridge) {
+		update_apps_list(watching_apps_all, watching_apps_bridge);
+	}
 
 	watching_apps_channel = get_apps_watching_channel(obj->channel->uniqueid);
-	if (!watching_apps_channel) {
-		return;
-	}
-	update_apps_list(watching_apps_all, watching_apps_channel);
+	if (watching_apps_channel) {
+		update_bridge_interest(watching_apps_channel, obj->bridge->uniqueid);
+		update_apps_list(watching_apps_all, watching_apps_channel);
+	}
 
 	if (!ao2_container_count(watching_apps_all)) {
 		return;
@@ -832,9 +948,7 @@
 		struct stasis_topic *topic,
 		struct stasis_message *message)
 {
-	RAII_VAR(struct ao2_container *, watching_apps_channel, NULL, ao2_cleanup);
 	RAII_VAR(struct ao2_container *, watching_apps_bridge, NULL, ao2_cleanup);
-	RAII_VAR(struct ao2_container *, watching_apps_all, ast_str_container_alloc(1), ao2_cleanup);
 	struct ast_bridge_blob *obj = stasis_message_data(message);
 	RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
 
@@ -842,21 +956,10 @@
 	if (!watching_apps_bridge) {
 		return;
 	}
-	update_apps_list(watching_apps_all, watching_apps_bridge);
-
-	watching_apps_channel = get_apps_watching_channel(obj->channel->uniqueid);
-	if (!watching_apps_channel) {
-		return;
-	}
-	update_apps_list(watching_apps_all, watching_apps_channel);
-
-	if (!ao2_container_count(watching_apps_all)) {
-		return;
-	}
 
 	msg = stasis_json_event_channel_left_bridge_create(obj->bridge, obj->channel);
 
-	distribute_message(watching_apps_all, msg);
+	distribute_message(watching_apps_bridge, msg);
 }
 
 static int load_module(void)
@@ -872,6 +975,16 @@
 	app_controls = ao2_container_alloc(CONTROLS_NUM_BUCKETS,
 					     control_hash, control_compare);
 	if (app_controls == NULL) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
+	app_bridge_controls = ao2_container_alloc(CONTROLS_NUM_BUCKETS,
+					     bridge_control_hash, bridge_control_compare);
+	if (app_bridge_controls == NULL) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
+	if (bridge_command_init()) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
 
@@ -915,11 +1028,16 @@
 	stasis_message_router_unsubscribe_and_join(bridge_router);
 	bridge_router = NULL;
 
+	bridge_command_cleanup();
+
 	ao2_cleanup(apps_registry);
 	apps_registry = NULL;
 
 	ao2_cleanup(app_controls);
 	app_controls = NULL;
+
+	ao2_cleanup(app_bridge_controls);
+	app_bridge_controls = NULL;
 
 	return r;
 }

Modified: team/kmoore/stasis-bridging_events/res/stasis/app.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging_events/res/stasis/app.c?view=diff&rev=389960&r1=389959&r2=389960
==============================================================================
--- team/kmoore/stasis-bridging_events/res/stasis/app.c (original)
+++ team/kmoore/stasis-bridging_events/res/stasis/app.c Tue May 28 15:45:47 2013
@@ -134,7 +134,7 @@
 	ast_assert(uniqueid != NULL);
 	ast_assert(app != NULL);
 
-	ao2_find(app->bridges, uniqueid, OBJ_KEY | OBJ_NODATA | OBJ_UNLINK);
+	ao2_find(app->bridges, uniqueid, OBJ_KEY | OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE);
 }
 
 /*!

Added: team/kmoore/stasis-bridging_events/res/stasis/bridge_command.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging_events/res/stasis/bridge_command.c?view=auto&rev=389960
==============================================================================
--- team/kmoore/stasis-bridging_events/res/stasis/bridge_command.c (added)
+++ team/kmoore/stasis-bridging_events/res/stasis/bridge_command.c Tue May 28 15:45:47 2013
@@ -1,0 +1,154 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Stasis application command support.
+ *
+ * \author David M. Lee, II <dlee at digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "bridge_command.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_message_router.h"
+#include "asterisk/stasis_app_impl.h"
+
+struct stasis_app_bridge_command {
+	ast_mutex_t lock;
+	ast_cond_t condition;
+	stasis_app_bridge_command_cb callback;
+	struct stasis_app_bridge_control *control;
+	void *data;
+	void *retval;
+	int is_done:1;
+};
+
+static void bridge_command_dtor(void *obj)
+{
+	struct stasis_app_bridge_command *command = obj;
+	ast_mutex_destroy(&command->lock);
+	ast_cond_destroy(&command->condition);
+	ao2_cleanup(command->control);
+}
+
+struct stasis_app_bridge_command *bridge_command_create(
+	struct stasis_app_bridge_control *control,
+	stasis_app_bridge_command_cb callback, void *data)
+{
+	RAII_VAR(struct stasis_app_bridge_command *, command, NULL, ao2_cleanup);
+
+	command = ao2_alloc(sizeof(*command), bridge_command_dtor);
+	if (!command) {
+		return NULL;
+	}
+
+	ast_mutex_init(&command->lock);
+	ast_cond_init(&command->condition, 0);
+	command->callback = callback;
+	command->data = data;
+	command->control = control;
+	ao2_ref(control, +1);
+
+	ao2_ref(command, +1);
+	return command;
+}
+
+static void bridge_command_complete(struct stasis_app_bridge_command *command, void *retval)
+{
+	SCOPED_MUTEX(lock, &command->lock);
+
+	command->is_done = 1;
+	command->retval = retval;
+	ast_cond_signal(&command->condition);
+}
+
+void *bridge_command_join(struct stasis_app_bridge_command *command)
+{
+	SCOPED_MUTEX(lock, &command->lock);
+	while (!command->is_done) {
+		ast_cond_wait(&command->condition, &command->lock);
+	}
+
+	return command->retval;
+}
+
+void bridge_command_invoke(struct stasis_app_bridge_command *command,
+	struct stasis_app_bridge_control *control)
+{
+	void *retval = command->callback(control, command->data);
+	bridge_command_complete(command, retval);
+}
+
+struct stasis_topic *bridge_command_topic;
+struct stasis_message_type *bridge_command_type(void);
+STASIS_MESSAGE_TYPE_DEFN(bridge_command_type);
+struct stasis_message_router *bridge_command_router;
+
+static void bridge_command_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *msg)
+{
+	struct stasis_app_bridge_command *command = stasis_message_data(msg);
+	bridge_command_invoke(command, command->control);
+}
+
+int bridge_command_queue(struct stasis_app_bridge_command *command)
+{
+	RAII_VAR(struct stasis_message *, msg, stasis_message_create(bridge_command_type(), command), ao2_cleanup);
+	if (!msg) {
+		return -1;
+	}
+	stasis_publish(bridge_command_topic, msg);
+	return 0;
+}
+
+int bridge_command_init(void)
+{
+	int res = 0;
+	if (STASIS_MESSAGE_TYPE_INIT(bridge_command_type)) {
+		return -1;
+	}
+	bridge_command_topic = stasis_topic_create("bridge_command_topic");
+	if (!bridge_command_topic) {
+		return -1;
+	}
+	bridge_command_router = stasis_message_router_create(bridge_command_topic);
+	if (!bridge_command_router) {
+		return -1;
+	}
+
+	res = stasis_message_router_add(bridge_command_router, bridge_command_type(), bridge_command_cb, NULL);
+	if (res) {
+		return -1;
+	}
+
+	return 0;
+}
+
+void bridge_command_cleanup(void)
+{
+	stasis_message_router_unsubscribe(bridge_command_router);
+	bridge_command_router = NULL;
+	ao2_cleanup(bridge_command_topic);
+	bridge_command_topic = NULL;
+	STASIS_MESSAGE_TYPE_CLEANUP(bridge_command_type);
+}

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_command.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_command.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_command.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/kmoore/stasis-bridging_events/res/stasis/bridge_command.h
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging_events/res/stasis/bridge_command.h?view=auto&rev=389960
==============================================================================
--- team/kmoore/stasis-bridging_events/res/stasis/bridge_command.h (added)
+++ team/kmoore/stasis-bridging_events/res/stasis/bridge_command.h Tue May 28 15:45:47 2013
@@ -1,0 +1,47 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#ifndef _ASTERISK_RES_STASIS_COMMAND_H
+#define _ASTERISK_RES_STASIS_COMMAND_H
+
+/*! \file
+ *
+ * \brief Internal API for the Stasis application commands.
+ *
+ * \author David M. Lee, II <dlee at digium.com>
+ * \since 12
+ */
+
+#include "asterisk/stasis_app_impl.h"
+
+struct stasis_app_bridge_command;
+
+struct stasis_app_bridge_command *bridge_command_create(
+	struct stasis_app_bridge_control *control,
+	stasis_app_bridge_command_cb callback, void *data);
+
+void bridge_command_invoke(struct stasis_app_bridge_command *command,
+	struct stasis_app_bridge_control *control);
+
+void *bridge_command_join(struct stasis_app_bridge_command *command);
+
+int bridge_command_queue(struct stasis_app_bridge_command *command);
+
+void bridge_command_cleanup(void);
+int bridge_command_init(void);
+#endif /* _ASTERISK_RES_STASIS_CONTROL_H */

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_command.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_command.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_command.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/kmoore/stasis-bridging_events/res/stasis/bridge_control.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging_events/res/stasis/bridge_control.c?view=auto&rev=389960
==============================================================================
--- team/kmoore/stasis-bridging_events/res/stasis/bridge_control.c (added)
+++ team/kmoore/stasis-bridging_events/res/stasis/bridge_control.c Tue May 28 15:45:47 2013
@@ -1,0 +1,291 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * bridges for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Stasis application control support.
+ *
+ * \author David M. Lee, II <dlee at digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/stasis_bridging.h"
+
+#include "bridge_command.h"
+#include "bridge_control.h"
+#include "control.h"
+
+static int OK = 0;
+static int FAIL = -1;
+
+struct stasis_app_bridge_control {
+	AST_DECLARE_STRING_FIELDS(
+		/*!< The bridge ID must be held here in case the bridge goes away before the control structure */
+		AST_STRING_FIELD(bridge_id);
+	);
+	/*!
+	 * The associated bridge.
+	 * Be very careful with the threading associated w/ manipulating
+	 * the bridge.
+	 */
+	struct ast_bridge *bridge;
+};
+
+static void bridge_control_dtor(void *obj)
+{
+	struct stasis_app_bridge_control *control = obj;
+
+	ast_bridge_destroy(control->bridge);
+	control->bridge = NULL;
+
+	ast_string_field_free_memory(control);
+}
+
+struct stasis_app_bridge_control *bridge_control_create(int flags)
+{
+	RAII_VAR(struct stasis_app_bridge_control *, control, NULL, ao2_cleanup);
+
+	control = ao2_alloc(sizeof(*control), bridge_control_dtor);
+	if (!control || ast_string_field_init(control, 64)) {
+		return NULL;
+	}
+
+	control->bridge = ast_bridge_base_new(flags, 0);
+	if (!control->bridge) {
+		return NULL;
+	}
+
+	ast_string_field_set(control, bridge_id, control->bridge->uniqueid);
+
+	ao2_ref(control, +1);
+	return control;
+}
+
+static struct stasis_app_bridge_command *exec_bridge_command(
+	struct stasis_app_bridge_control *control, stasis_app_bridge_command_cb command_fn,
+	void *data)
+{
+	RAII_VAR(struct stasis_app_bridge_command *, command, NULL, ao2_cleanup);
+
+	command = bridge_command_create(control, command_fn, data);
+
+	if (!command) {
+		return NULL;
+	}
+
+	if (bridge_command_queue(command)) {
+		return NULL;
+	}
+
+	ao2_ref(command, +1);
+	return command;
+}
+
+struct ast_bridge_snapshot *stasis_app_bridge_control_get_snapshot(
+	const struct stasis_app_bridge_control *control)
+{
+	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+	struct stasis_caching_topic *caching_topic;
+	struct ast_bridge_snapshot *snapshot;
+
+	caching_topic = ast_bridge_topic_all_cached();
+	if (!caching_topic) {
+		return NULL;
+	}
+
+	msg = stasis_cache_get(caching_topic, ast_bridge_snapshot_type(),
+		stasis_app_bridge_control_get_bridge_id(control));
+	if (!msg) {
+		return NULL;
+	}
+
+	snapshot = stasis_message_data(msg);
+	ast_assert(snapshot != NULL);
+	
+	ao2_ref(snapshot, +1);
+	return snapshot;
+}
+
+struct ast_bridge_snapshot *stasis_app_bridge_control_create_snapshot(
+	const struct stasis_app_bridge_control *control)
+{
+	return ast_bridge_snapshot_create(control->bridge);
+}
+
+void *stasis_app_send_bridge_command(struct stasis_app_bridge_control *control,
+	stasis_app_bridge_command_cb command_fn, void *data)
+{
+	RAII_VAR(struct stasis_app_bridge_command *, command, NULL, ao2_cleanup);
+
+	if (control == NULL) {
+		return NULL;
+	}
+
+	command = exec_bridge_command(control, command_fn, data);
+	if (!command) {
+		return NULL;
+	}
+
+	return bridge_command_join(command);
+}
+
+int stasis_app_send_bridge_command_async(struct stasis_app_bridge_control *control,
+	stasis_app_bridge_command_cb command_fn, void *data)
+{
+	RAII_VAR(struct stasis_app_bridge_command *, command, NULL, ao2_cleanup);
+
+	if (control == NULL) {
+		return -1;
+	}
+
+	command = exec_bridge_command(control, command_fn, data);
+	if (!command) {
+		return -1;
+	}
+
+	return 0;
+}
+
+const char *stasis_app_bridge_control_get_bridge_id(
+	const struct stasis_app_bridge_control *control)
+{
+	return control->bridge_id;
+}
+
+void stasis_app_bridge_control_publish(
+	struct stasis_app_bridge_control *control, struct stasis_message *message)
+{
+	if (!control || !control->bridge || !message) {
+		return;
+	}
+	stasis_publish(ast_bridge_topic(control->bridge), message);
+}
+
+static int bridge_depart_cb(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	RAII_VAR(struct stasis_app_control *, channel_control,
+		stasis_app_control_find_by_channel(bridge_channel->chan), ao2_cleanup);
+	control_restore_pbx(channel_control);
+	control_set_bridged(channel_control, 0);
+	return -1;
+}
+
+static void *app_bridge_control_add_channel(
+	struct stasis_app_bridge_control *control,
+	void *data)
+{
+	struct ast_channel *channel = data;
+	RAII_VAR(struct stasis_app_control *, channel_control,
+		stasis_app_control_find_by_channel(channel), ao2_cleanup);
+	int res;
+	struct ast_bridge_features *features = ast_bridge_features_new();
+	if (!features) {
+		return &FAIL;
+	}
+
+	ast_bridge_leave_hook(features, bridge_depart_cb, NULL, NULL, 1);
+	control_strip_pbx(channel_control);
+
+	res = ast_bridge_impart(control->bridge, channel, NULL, features, 0);
+	if (res) {
+		control_restore_pbx(channel_control);
+	} else {
+		control_set_bridged(channel_control, 1);
+	}
+
+	return !res ? &OK : &FAIL;
+}
+
+int stasis_app_bridge_control_add_channel(
+	struct stasis_app_bridge_control *control, struct ast_channel *channel)
+{
+	int *retval;
+
+	ast_debug(3, "%s: Sending bridge add channel (%s) command\n",
+	                stasis_app_bridge_control_get_bridge_id(control),
+			ast_channel_uniqueid(channel));
+
+	retval = stasis_app_send_bridge_command(control, app_bridge_control_add_channel, channel);
+
+	if (retval == NULL || *retval != 0) {
+		ast_log(LOG_WARNING, "%s: Failed to add channel",
+			stasis_app_bridge_control_get_bridge_id(control));
+		return -1;
+	}
+
+	return 0;
+}
+
+static void *app_bridge_control_remove_channel(
+	struct stasis_app_bridge_control *control,
+	void *data)
+{
+	struct ast_channel *channel = data;
+	return !ast_bridge_depart(channel) ? &OK : &FAIL;
+}
+
+int stasis_app_bridge_control_remove_channel(
+	struct stasis_app_bridge_control *control, struct ast_channel *channel)
+{
+	int *retval;
+
+	ast_debug(3, "%s: Sending bridge remove channel (%s) command\n",
+	                stasis_app_bridge_control_get_bridge_id(control),
+			ast_channel_uniqueid(channel));
+
+	retval = stasis_app_send_bridge_command(control, app_bridge_control_remove_channel, channel);
+
+	if (retval == NULL || *retval != 0) {
+		ast_log(LOG_WARNING, "%s: Failed to remove channel",
+			stasis_app_bridge_control_get_bridge_id(control));
+		return -1;
+	}
+
+	return 0;
+}
+
+static void *app_bridge_control_destroy(
+	struct stasis_app_bridge_control *control,
+	void *data)
+{
+	stasis_app_remove_bridge_control(control);
+	return &OK;
+}
+
+int stasis_app_bridge_control_destroy_bridge(
+	struct stasis_app_bridge_control *control)
+{
+	int *retval;
+
+	ast_debug(3, "%s: Sending bridge delete command\n",
+	                stasis_app_bridge_control_get_bridge_id(control));
+
+	retval = stasis_app_send_bridge_command(control, app_bridge_control_destroy, NULL);
+
+	if (retval == NULL || *retval != 0) {
+		ast_log(LOG_WARNING, "%s: Failed to delete bridge",
+			stasis_app_bridge_control_get_bridge_id(control));
+		return -1;
+	}
+
+	return 0;
+}
+

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_control.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_control.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_control.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/kmoore/stasis-bridging_events/res/stasis/bridge_control.h
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging_events/res/stasis/bridge_control.h?view=auto&rev=389960
==============================================================================
--- team/kmoore/stasis-bridging_events/res/stasis/bridge_control.h (added)
+++ team/kmoore/stasis-bridging_events/res/stasis/bridge_control.h Tue May 28 15:45:47 2013
@@ -1,0 +1,42 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#ifndef _ASTERISK_RES_STASIS_BRIDGE_CONTROL_H
+#define _ASTERISK_RES_STASIS_BRIDGE_CONTROL_H
+
+/*! \file
+ *
+ * \brief Internal API for the Stasis application controller.
+ *
+ * \author David M. Lee, II <dlee at digium.com>
+ * \since 12
+ */
+
+#include "asterisk/stasis_app.h"
+
+/*!
+ * \brief Create a stasis_app_bridge_control structure and an associated bridge
+ *
+ * \param flags ast_bridge_capability flags denoting the type of bridge to be created
+ *
+ * \retval NULL on error
+ * \retval The new stasis_app_bridge_control structure
+ */
+struct stasis_app_bridge_control *bridge_control_create(int flags);
+
+#endif /* _ASTERISK_RES_STASIS_BRIDGE_CONTROL_H */

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_control.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_control.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/kmoore/stasis-bridging_events/res/stasis/bridge_control.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/kmoore/stasis-bridging_events/res/stasis/control.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging_events/res/stasis/control.c?view=diff&rev=389960&r1=389959&r2=389960
==============================================================================
--- team/kmoore/stasis-bridging_events/res/stasis/control.c (original)
+++ team/kmoore/stasis-bridging_events/res/stasis/control.c Tue May 28 15:45:47 2013
@@ -32,6 +32,7 @@
 #include "command.h"
 #include "control.h"
 
+/* XXX needs a destructor */
 struct stasis_app_control {
 	/*! Queue of commands to dispatch on the channel */
 	struct ao2_container *command_queue;
@@ -45,6 +46,8 @@
 	 * the channel.
 	 */
 	struct ast_channel *channel;
+	struct ast_pbx *pbx;
+	int bridged;
 };
 
 struct stasis_app_control *control_create(struct ast_channel *channel)
@@ -200,3 +203,29 @@
 	ao2_iterator_destroy(&i);
 	return count;
 }
+
+void control_set_bridged(struct stasis_app_control *control, int bridged)
+{
+	control->bridged = bridged;
+}
+
+int control_is_bridged(struct stasis_app_control *control)
+{
+	return control->bridged;
+}
+
+void control_strip_pbx(struct stasis_app_control *control)
+{
+	ast_channel_lock(control->channel);
+	control->pbx = ast_channel_pbx(control->channel);
+	ast_channel_pbx_set(control->channel, NULL);
+	ast_channel_unlock(control->channel);
+}
+
+void control_restore_pbx(struct stasis_app_control *control)
+{
+	ast_channel_lock(control->channel);
+	ast_channel_pbx_set(control->channel, control->pbx);
+	ast_channel_unlock(control->channel);
+	control->pbx = NULL;
+}

Modified: team/kmoore/stasis-bridging_events/res/stasis/control.h
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging_events/res/stasis/control.h?view=diff&rev=389960&r1=389959&r2=389960

[... 281 lines stripped ...]



More information about the asterisk-commits mailing list