[asterisk-commits] kmoore: branch group/bridge_construction r385352 - in /team/group/bridge_cons...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Apr 11 14:28:23 CDT 2013


Author: kmoore
Date: Thu Apr 11 14:28:18 2013
New Revision: 385352

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=385352
Log:
Add Stasis infrastructure for bridging

Add the base necessary to publish bridge state information to whatever
consumers need it. The first of these consumers is bridge AMI events
and AMI actions used to query for information about the bridges.

Review: https://reviewboard.asterisk.org/r/2438/
(issue ASTERISK-21057)

Added:
    team/group/bridge_construction/include/asterisk/stasis_bridging.h   (with props)
    team/group/bridge_construction/main/manager_bridging.c   (with props)
    team/group/bridge_construction/main/stasis_bridging.c   (with props)
Modified:
    team/group/bridge_construction/include/asterisk/manager.h
    team/group/bridge_construction/include/asterisk/strings.h
    team/group/bridge_construction/main/asterisk.c
    team/group/bridge_construction/main/bridging.c
    team/group/bridge_construction/main/manager.c
    team/group/bridge_construction/main/manager_channels.c
    team/group/bridge_construction/main/strings.c

Modified: team/group/bridge_construction/include/asterisk/manager.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/manager.h?view=diff&rev=385352&r1=385351&r2=385352
==============================================================================
--- team/group/bridge_construction/include/asterisk/manager.h (original)
+++ team/group/bridge_construction/include/asterisk/manager.h Thu Apr 11 14:28:18 2013
@@ -316,6 +316,38 @@
  */
 struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid);
 
+/*! \brief Struct containing info for an AMI event to send out. */
+struct ast_manager_event_blob {
+	int event_flags;		/*!< Flags the event should be raised with. */
+	const char *manager_event;	/*!< The event to be raised, should be a string literal. */
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(extra_fields);	/*!< Extra fields to include in the event. */
+	);
+};
+
+/*!
+ * \since 12
+ * \brief Construct a \ref snapshot_manager_event.
+ *
+ * \param event_flags Flags the event should be raised with.
+ * \param manager_event The event to be raised, should be a string literal.
+ * \param extra_fields_fmt Format string for extra fields to include.
+ *                         Or NO_EXTRA_FIELDS for no extra fields.
+ *
+ * \return New \ref ast_manager_snapshot_event object.
+ * \return \c NULL on error.
+ */
+struct ast_manager_event_blob *
+__attribute__((format(printf, 3, 4)))
+ast_manager_event_blob_create(
+	int event_flags,
+	const char *manager_event,
+	const char *extra_fields_fmt,
+	...);
+
+/*! GCC warns about blank or NULL format strings. So, shenanigans! */
+#define NO_EXTRA_FIELDS "%s", ""
+
 /*!
  * \brief Initialize support for AMI channel events.
  * \return 0 on success.
@@ -324,4 +356,12 @@
  */
 int manager_channels_init(void);
 
+/*!
+ * \brief Initialize support for AMI channel events.
+ * \return 0 on success.
+ * \return non-zero on error.
+ * \since 12
+ */
+int manager_bridging_init(void);
+
 #endif /* _ASTERISK_MANAGER_H */

Added: team/group/bridge_construction/include/asterisk/stasis_bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/stasis_bridging.h?view=auto&rev=385352
==============================================================================
--- team/group/bridge_construction/include/asterisk/stasis_bridging.h (added)
+++ team/group/bridge_construction/include/asterisk/stasis_bridging.h Thu Apr 11 14:28:18 2013
@@ -1,0 +1,220 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013 Digium, Inc.
+ *
+ * Kinsey Moore <kmoore 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 _STASIS_BRIDGING_H
+#define _STASIS_BRIDGING_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/stringfields.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/channel.h"
+#include "asterisk/bridging.h"
+
+/*!
+ * \brief Structure that contains a snapshot of information about a bridge
+ */
+struct ast_bridge_snapshot {
+	AST_DECLARE_STRING_FIELDS(
+		/*! Immutable bridge UUID. */
+		AST_STRING_FIELD(uniqueid);
+		/*! Bridge technology that is handling the bridge */
+		AST_STRING_FIELD(technology);
+	);
+	/*! AO2 container of bare channel uniqueid strings participating in the bridge.
+	 * Allocated from ast_str_container_alloc() */
+	struct ao2_container *channels;
+	/*! Bridge flags to tweak behavior */
+	struct ast_flags feature_flags;
+	/*! Number of channels participating in the bridge */
+	unsigned int num_channels;
+	/*! Number of active channels in the bridge. */
+	unsigned int num_active;
+};
+
+/*!
+ * \since 12
+ * \brief Generate a snapshot of the bridge state. This is an ao2 object, so
+ * ao2_cleanup() to deallocate.
+ *
+ * \param bridge The bridge from which to generate a snapshot
+ *
+ * \retval AO2 refcounted snapshot on success
+ * \retval NULL on error
+ */
+struct ast_bridge_snapshot *ast_bridge_snapshot_create(struct ast_bridge *bridge);
+
+/*!
+ * \since 12
+ * \brief Message type for \ref ast_bridge_snapshot.
+ *
+ * \retval Message type for \ref ast_bridge_snapshot.
+ */
+struct stasis_message_type *ast_bridge_snapshot_type(void);
+
+/*!
+ * \since 12
+ * \brief A topic which publishes the events for a particular bridge.
+ *
+ * If the given \a bridge is \c NULL, ast_bridge_topic_all() is returned.
+ *
+ * \param bridge Bridge for which to get a topic or \c NULL.
+ *
+ * \retval Topic for bridge's events.
+ * \retval ast_bridge_topic_all() if \a bridge is \c NULL.
+ */
+struct stasis_topic *ast_bridge_topic(struct ast_bridge *bridge);
+
+/*!
+ * \since 12
+ * \brief A topic which publishes the events for all bridges.
+ * \retval Topic for all bridge events.
+ */
+struct stasis_topic *ast_bridge_topic_all(void);
+
+/*!
+ * \since 12
+ * \brief A caching topic which caches \ref ast_bridge_snapshot messages from
+ * ast_bridge_events_all(void).
+ *
+ * \retval Caching topic for all bridge events.
+ */
+struct stasis_caching_topic *ast_bridge_topic_all_cached(void);
+
+/*!
+ * \since 12
+ * \brief Publish the state of a bridge
+ *
+ * \param bridge The bridge for which to publish state
+ */
+void ast_bridge_publish_state(struct ast_bridge *bridge);
+
+/*! \brief Message representing the merge of two bridges */
+struct ast_bridge_merge_message {
+	struct ast_bridge_snapshot *from;	/*!< Bridge from which channels will be removed during the merge */
+	struct ast_bridge_snapshot *to;		/*!< Bridge to which channels will be added during the merge */
+};
+
+/*!
+ * \since 12
+ * \brief Message type for \ref ast_bridge_merge_message.
+ *
+ * \retval Message type for \ref ast_bridge_merge_message.
+ */
+struct stasis_message_type *ast_bridge_merge_message_type(void);
+
+/*!
+ * \since 12
+ * \brief Publish a bridge merge
+ *
+ * \param to The bridge to which channels are being added
+ * \param from The bridge from which channels are being removed
+ */
+void ast_bridge_publish_merge(struct ast_bridge *to, struct ast_bridge *from);
+
+/*!
+ * \since 12
+ * \brief Blob of data associated with a bridge.
+ *
+ * The \c blob is actually a JSON object of structured data. It has a "type" field
+ * which contains the type string describing this blob.
+ */
+struct ast_bridge_blob {
+	/*! Bridge blob is associated with (or NULL for global/all bridges) */
+	struct ast_bridge_snapshot *bridge;
+	/*! Channel blob is associated with (may be NULL for some messages) */
+	struct ast_channel_snapshot *channel;
+	/*! JSON blob of data */
+	struct ast_json *blob;
+};
+
+/*!
+ * \since 12
+ * \brief Message type for \ref ast_bridge_blob messages.
+ *
+ * \retval Message type for \ref ast_bridge_blob messages.
+ */
+struct stasis_message_type *ast_bridge_blob_type(void);
+
+/*!
+ * \since 12
+ * \brief Creates a \ref ast_bridge_blob message.
+ *
+ * The \a blob JSON object requires a \c "type" field describing the blob. It
+ * should also be treated as immutable and not modified after it is put into the
+ * message.
+ *
+ * \param bridge Channel blob is associated with, or NULL for global/all bridges.
+ * \param blob JSON object representing the data.
+ * \return \ref ast_bridge_blob message.
+ * \return \c NULL on error
+ */
+struct stasis_message *ast_bridge_blob_create(struct ast_bridge *bridge,
+					struct ast_channel *chan,
+					struct ast_json *blob);
+
+/*!
+ * \since 12
+ * \brief Extracts the type field from a \ref ast_bridge_blob.
+ *
+ * Returned \c char* is still owned by \a obj
+ *
+ * \param obj Channel blob object.
+ *
+ * \retval Type field value from the blob.
+ * \retval \c NULL on error.
+ */
+const char *ast_bridge_blob_json_type(struct ast_bridge_blob *obj);
+
+/*!
+ * \since 12
+ * \brief Publish a bridge channel enter event
+ *
+ * \param bridge The bridge a channel entered
+ * \param chan The channel that entered the bridge
+ */
+void ast_bridge_publish_enter(struct ast_bridge *bridge, struct ast_channel *chan);
+
+/*!
+ * \since 12
+ * \brief Publish a bridge channel leave event
+ *
+ * \param bridge The bridge a channel left
+ * \param chan The channel that left the bridge
+ */
+void ast_bridge_publish_leave(struct ast_bridge *bridge, struct ast_channel *chan);
+
+/*!
+ * \brief Dispose of the stasis bridging topics and message types
+ */
+void ast_stasis_bridging_shutdown(void);
+
+/*!
+ * \brief Initialize the stasis bridging topic and message types
+ */
+void ast_stasis_bridging_init(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif	/* _STASIS_BRIDGING_H */

Propchange: team/group/bridge_construction/include/asterisk/stasis_bridging.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/bridge_construction/include/asterisk/stasis_bridging.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/bridge_construction/include/asterisk/stasis_bridging.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/group/bridge_construction/include/asterisk/strings.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/strings.h?view=diff&rev=385352&r1=385351&r2=385352
==============================================================================
--- team/group/bridge_construction/include/asterisk/strings.h (original)
+++ team/group/bridge_construction/include/asterisk/strings.h Thu Apr 11 14:28:18 2013
@@ -1013,4 +1013,26 @@
 	return abs(hash);
 }
 
+/*!
+ * \since 12
+ * \brief Allocates a hash container for bare strings
+ *
+ * \param buckets The number of buckets to use for the hash container
+ *
+ * \retval AO2 container for strings
+ * \retval NULL if allocation failed
+ */
+struct ao2_container *ast_str_container_alloc(int buckets);
+
+/*!
+ * \since 12
+ * \brief Adds a string to a string container allocated by ast_str_container_alloc
+ *
+ * \param str_container The container to which to add a string
+ * \param add The string to add to the container
+ *
+ * \retval zero on success
+ * \retval non-zero if the operation failed
+ */
+int ast_str_container_add(struct ao2_container *str_container, const char *add);
 #endif /* _ASTERISK_STRINGS_H */

Modified: team/group/bridge_construction/main/asterisk.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/asterisk.c?view=diff&rev=385352&r1=385351&r2=385352
==============================================================================
--- team/group/bridge_construction/main/asterisk.c (original)
+++ team/group/bridge_construction/main/asterisk.c Thu Apr 11 14:28:18 2013
@@ -4220,11 +4220,6 @@
 
 	ast_http_init();		/* Start the HTTP server, if needed */
 
-	if (init_manager()) {
-		printf("%s", term_quit());
-		exit(1);
-	}
-
 	if (ast_cdr_engine_init()) {
 		printf("%s", term_quit());
 		exit(1);
@@ -4274,6 +4269,11 @@
 	}
 
 	if (ast_bridging_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
+
+	if (init_manager()) {
 		printf("%s", term_quit());
 		exit(1);
 	}

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=385352&r1=385351&r2=385352
==============================================================================
--- team/group/bridge_construction/main/bridging.c (original)
+++ team/group/bridge_construction/main/bridging.c Thu Apr 11 14:28:18 2013
@@ -40,6 +40,7 @@
 #include "asterisk/lock.h"
 #include "asterisk/linkedlists.h"
 #include "asterisk/bridging.h"
+#include "asterisk/stasis_bridging.h"
 #include "asterisk/bridging_technology.h"
 #include "asterisk/app.h"
 #include "asterisk/file.h"
@@ -439,6 +440,7 @@
 	bridge->v_table->pull(bridge, bridge_channel);
 
 	bridge->reconfigured = 1;
+	ast_bridge_publish_leave(bridge, bridge_channel->chan);
 }
 
 /*!
@@ -510,6 +512,7 @@
 	}
 
 	bridge->reconfigured = 1;
+	ast_bridge_publish_enter(bridge, bridge_channel->chan);
 }
 
 /*!
@@ -1129,6 +1132,12 @@
 static void destroy_bridge(void *obj)
 {
 	struct ast_bridge *bridge = obj;
+	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+
+	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);
@@ -1229,6 +1238,11 @@
 		return NULL;
 	}
 
+	if (!ast_bridge_topic(self)) {
+		ao2_ref(self, -1);
+		return NULL;
+	}
+
 	return self;
 }
 
@@ -1335,6 +1349,9 @@
 
 	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);
+	}
 	return bridge;
 }
 
@@ -3032,6 +3049,8 @@
 	ast_debug(1, "Merging bridge %s into bridge %s\n",
 		bridge2->uniqueid, bridge1->uniqueid);
 
+	ast_bridge_publish_merge(bridge1, bridge2);
+
 	/* Move channels from bridge2 over to bridge1 */
 	while ((bridge_channel = AST_LIST_FIRST(&bridge2->channels))) {
 		bridge_channel_pull(bridge_channel);
@@ -4049,6 +4068,7 @@
 {
 	ao2_cleanup(bridge_manager);
 	bridge_manager = NULL;
+	ast_stasis_bridging_shutdown();
 }
 
 int ast_bridging_init(void)
@@ -4058,6 +4078,8 @@
 		return -1;
 	}
 
+	ast_stasis_bridging_init();
+
 	ast_register_atexit(bridge_shutdown);
 	return 0;
 }

Modified: team/group/bridge_construction/main/manager.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/manager.c?view=diff&rev=385352&r1=385351&r2=385352
==============================================================================
--- team/group/bridge_construction/main/manager.c (original)
+++ team/group/bridge_construction/main/manager.c Thu Apr 11 14:28:18 2013
@@ -7500,6 +7500,10 @@
 		return -1;
 	}
 
+	if (manager_bridging_init()) {
+		return -1;
+	}
+
 	if (!registered) {
 		/* Register default actions */
 		ast_manager_register_xml_core("Ping", 0, action_ping);
@@ -8010,3 +8014,45 @@
 
 	return datastore;
 }
+
+static void manager_event_blob_dtor(void *obj)
+{
+	struct ast_manager_event_blob *ev = obj;
+	ast_string_field_free_memory(ev);
+}
+
+struct ast_manager_event_blob *
+__attribute__((format(printf, 3, 4)))
+ast_manager_event_blob_create(
+	int event_flags,
+	const char *manager_event,
+	const char *extra_fields_fmt,
+	...)
+{
+	RAII_VAR(struct ast_manager_event_blob *, ev, NULL, ao2_cleanup);
+	va_list argp;
+
+	ast_assert(extra_fields_fmt != NULL);
+	ast_assert(manager_event != NULL);
+
+	ev = ao2_alloc(sizeof(*ev), manager_event_blob_dtor);
+	if (!ev) {
+		return NULL;
+	}
+
+	if (ast_string_field_init(ev, 20)) {
+		return NULL;
+	}
+
+	ev->manager_event = manager_event;
+	ev->event_flags = event_flags;
+
+	va_start(argp, extra_fields_fmt);
+	ast_string_field_ptr_build_va(ev, &ev->extra_fields, extra_fields_fmt,
+				      argp);
+	va_end(argp);
+
+	ao2_ref(ev, +1);
+	return ev;
+}
+

Added: team/group/bridge_construction/main/manager_bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/manager_bridging.c?view=auto&rev=385352
==============================================================================
--- team/group/bridge_construction/main/manager_bridging.c (added)
+++ team/group/bridge_construction/main/manager_bridging.c Thu Apr 11 14:28:18 2013
@@ -1,0 +1,461 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Kinsey Moore <kmoore 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 The Asterisk Management Interface - AMI (bridge event handling)
+ *
+ * \author Kinsey Moore <kmoore at digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/stasis_bridging.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/manager.h"
+#include "asterisk/stasis_message_router.h"
+
+/*! \brief Message router for cached bridge state snapshot updates */
+static struct stasis_message_router *bridge_state_router;
+
+/*** DOCUMENTATION
+	<managerEvent language="en_US" name="BridgeCreate">
+		<managerEventInstance class="EVENT_FLAG_CALL">
+			<synopsis>Raised when a bridge is created.</synopsis>
+			<syntax>
+				<parameter name="BridgeUniqueid">
+				</parameter>
+				<parameter name="BridgeType">
+					<para>The type of bridge</para>
+				</parameter>
+			</syntax>
+		</managerEventInstance>
+	</managerEvent>
+	<managerEvent language="en_US" name="BridgeDestroy">
+		<managerEventInstance class="EVENT_FLAG_CALL">
+			<synopsis>Raised when a bridge is destroyed.</synopsis>
+			<syntax>
+				<xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+			</syntax>
+		</managerEventInstance>
+	</managerEvent>
+	<managerEvent language="en_US" name="BridgeEnter">
+		<managerEventInstance class="EVENT_FLAG_CALL">
+			<synopsis>Raised when a channel enters a bridge.</synopsis>
+			<syntax>
+				<xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+				<parameter name="Uniqueid">
+					<para>The uniqueid of the channel entering the bridge</para>
+				</parameter>
+			</syntax>
+		</managerEventInstance>
+	</managerEvent>
+	<managerEvent language="en_US" name="BridgeLeave">
+		<managerEventInstance class="EVENT_FLAG_CALL">
+			<synopsis>Raised when a channel leaves a bridge.</synopsis>
+			<syntax>
+				<xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+				<parameter name="Uniqueid">
+					<para>The uniqueid of the channel leaving the bridge</para>
+				</parameter>
+			</syntax>
+		</managerEventInstance>
+	</managerEvent>
+	<manager name="BridgeList" language="en_US">
+		<synopsis>
+			Get a list of bridges in the system.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="BridgeType">
+				<para>Optional type for filtering the resulting list of bridges.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Returns a list of bridges, optionally filtering on a bridge type.</para>
+		</description>
+	</manager>
+	<manager name="BridgeInfo" language="en_US">
+		<synopsis>
+			Get information about a bridge.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="BridgeUniqueid" required="true">
+				<para>The unique ID of the bridge about which to retreive information.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Returns detailed information about a bridge and the channels in it.</para>
+		</description>
+	</manager>
+ ***/
+
+/*!
+ * \brief Generate the AMI message body from a bridge snapshot
+ * \internal
+ *
+ * \param snapshot the bridge snapshot for which to generate an AMI message
+ *                 body
+ *
+ * \retval NULL on error
+ * \retval ast_str* on success (must be ast_freed by caller)
+ */
+static struct ast_str *manager_build_bridge_state_string(
+	const struct ast_bridge_snapshot *snapshot,
+	const char *suffix)
+{
+	struct ast_str *out = ast_str_create(128);
+	int res = 0;
+	if (!out) {
+		return NULL;
+	}
+	res = ast_str_set(&out, 0,
+		"BridgeUniqueid%s: %s\r\n"
+		"BridgeType%s: %s\r\n",
+		suffix, snapshot->uniqueid,
+		suffix, snapshot->technology);
+
+	if (!res) {
+		return NULL;
+	}
+
+	return out;
+}
+
+/*! \brief Typedef for callbacks that get called on channel snapshot updates */
+typedef struct ast_manager_event_blob *(*bridge_snapshot_monitor)(
+	struct ast_bridge_snapshot *old_snapshot,
+	struct ast_bridge_snapshot *new_snapshot);
+
+/*! \brief Handle bridge creation */
+static struct ast_manager_event_blob *bridge_create(
+	struct ast_bridge_snapshot *old_snapshot,
+	struct ast_bridge_snapshot *new_snapshot)
+{
+	if (!new_snapshot || old_snapshot) {
+		return NULL;
+	}
+
+	return ast_manager_event_blob_create(
+		EVENT_FLAG_CALL, "BridgeCreate", NO_EXTRA_FIELDS);
+}
+
+/*! \brief Handle bridge destruction */
+static struct ast_manager_event_blob *bridge_destroy(
+	struct ast_bridge_snapshot *old_snapshot,
+	struct ast_bridge_snapshot *new_snapshot)
+{
+	if (new_snapshot || !old_snapshot) {
+		return NULL;
+	}
+
+	return ast_manager_event_blob_create(
+		EVENT_FLAG_CALL, "BridgeDestroy", NO_EXTRA_FIELDS);
+}
+
+
+bridge_snapshot_monitor bridge_monitors[] = {
+	bridge_create,
+	bridge_destroy,
+};
+
+static void bridge_snapshot_update(void *data, struct stasis_subscription *sub,
+				    struct stasis_topic *topic,
+				    struct stasis_message *message)
+{
+	RAII_VAR(struct ast_str *, bridge_event_string, NULL, ast_free);
+	struct stasis_cache_update *update;
+	struct ast_bridge_snapshot *old_snapshot;
+	struct ast_bridge_snapshot *new_snapshot;
+	size_t i;
+
+	update = stasis_message_data(message);
+
+	if (ast_bridge_snapshot_type() != update->type) {
+		return;
+	}
+
+	old_snapshot = stasis_message_data(update->old_snapshot);
+	new_snapshot = stasis_message_data(update->new_snapshot);
+
+	for (i = 0; i < ARRAY_LEN(bridge_monitors); ++i) {
+		RAII_VAR(struct ast_manager_event_blob *, event, NULL, ao2_cleanup);
+
+		event = bridge_monitors[i](old_snapshot, new_snapshot);
+		if (!event) {
+			continue;
+		}
+
+		/* If we haven't already, build the channel event string */
+		if (!bridge_event_string) {
+			bridge_event_string =
+				manager_build_bridge_state_string(
+					new_snapshot ? new_snapshot : old_snapshot, "");
+			if (!bridge_event_string) {
+				return;
+			}
+		}
+
+		manager_event(event->event_flags, event->manager_event, "%s%s",
+			ast_str_buffer(bridge_event_string),
+			event->extra_fields);
+	}
+}
+
+static void bridge_merge_cb(void *data, struct stasis_subscription *sub,
+				    struct stasis_topic *topic,
+				    struct stasis_message *message)
+{
+	struct ast_bridge_merge_message *merge_msg = stasis_message_data(message);
+	RAII_VAR(struct ast_str *, to_text, NULL, ast_free);
+	RAII_VAR(struct ast_str *, from_text, NULL, ast_free);
+
+	ast_assert(merge_msg->to != NULL);
+	ast_assert(merge_msg->from != NULL);
+
+	to_text = manager_build_bridge_state_string(merge_msg->to, "");
+	from_text = manager_build_bridge_state_string(merge_msg->from, "From");
+
+	/*** DOCUMENTATION
+		<managerEventInstance>
+			<synopsis>Raised when two bridges are merged.</synopsis>
+			<syntax>
+				<xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
+				<parameter name="BridgeUniqueidFrom">
+					<para>The uniqueid of the bridge being dissolved in the merge</para>
+				</parameter>
+				<parameter name="BridgeTypeFrom">
+					<para>The type of bridge that is being dissolved in the merge</para>
+				</parameter>
+			</syntax>
+		</managerEventInstance>
+	***/
+	manager_event(EVENT_FLAG_CALL, "BridgeMerge",
+		"%s"
+		"%s",
+		ast_str_buffer(to_text),
+		ast_str_buffer(from_text));
+}
+
+static void bridge_blob_cb(void *data, struct stasis_subscription *sub,
+				    struct stasis_topic *topic,
+				    struct stasis_message *message)
+{
+	struct ast_bridge_blob *blob = stasis_message_data(message);
+	const char *type = ast_bridge_blob_json_type(blob);
+	RAII_VAR(struct ast_str *, bridge_text, NULL, ast_free);
+	char *event;
+
+	if (!strcmp("leave", type)) {
+		event = "BridgeLeave";
+	} else if (!strcmp("enter", type)) {
+		event = "BridgeEnter";
+	} else {
+		return;
+	}
+
+	bridge_text = manager_build_bridge_state_string(blob->bridge, "");
+
+	manager_event(EVENT_FLAG_CALL, event,
+		"%s"
+		"Uniqueid: %s\r\n",
+		ast_str_buffer(bridge_text),
+		blob->channel->uniqueid);
+}
+
+static int filter_bridge_type_cb(void *obj, void *arg, int flags)
+{
+	char *bridge_type = arg;
+	struct ast_bridge_snapshot *snapshot = stasis_message_data(obj);
+	/* unlink all the snapshots that do not match the bridge type */
+	return strcmp(bridge_type, snapshot->technology) ? CMP_MATCH : 0;
+}
+
+static int send_bridge_list_item_cb(void *obj, void *arg, void *data, int flags)
+{
+	struct ast_bridge_snapshot *snapshot = stasis_message_data(obj);
+	struct mansession *s = arg;
+	char *id_text = data;
+	RAII_VAR(struct ast_str *, bridge_info, manager_build_bridge_state_string(snapshot, ""), ast_free);
+
+	astman_append(s,
+		"Event: BridgeListItem\r\n"
+		"%s"
+		"%s"
+		"\r\n",
+		ast_str_buffer(bridge_info),
+		id_text);
+	return 0;
+}
+
+static int manager_bridges_list(struct mansession *s, const struct message *m)
+{
+	const char *id = astman_get_header(m, "ActionID");
+	const char *type_filter = astman_get_header(m, "BridgeType");
+	RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free);
+	RAII_VAR(struct ao2_container *, bridges, NULL, ao2_cleanup);
+
+	if (!id_text) {
+		astman_send_error(s, m, "Internal error");
+		return -1;
+	}
+
+	if (!ast_strlen_zero(id)) {
+		ast_str_set(&id_text, 0, "ActionID: %s\r\n", id);
+	}
+
+	bridges = stasis_cache_dump(ast_bridge_topic_all_cached(), ast_bridge_snapshot_type());
+	if (!bridges) {
+		astman_send_error(s, m, "Internal error");
+		return -1;
+	}
+
+	if (!ast_strlen_zero(type_filter)) {
+		char *type_filter_dup = ast_strdupa(type_filter);
+		ao2_callback(bridges, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK, filter_bridge_type_cb, type_filter_dup);
+	}
+
+	ao2_callback_data(bridges, OBJ_NODATA, send_bridge_list_item_cb, s, ast_str_buffer(id_text));
+
+	astman_append(s,
+		"Event: BridgeListComplete\r\n"
+		"%s"
+		"\r\n",
+		ast_str_buffer(id_text));
+
+	return 0;
+}
+
+static int send_bridge_info_item_cb(void *obj, void *arg, void *data, int flags)
+{
+	char *uniqueid = obj;
+	struct mansession *s = arg;
+	char *id_text = data;
+
+	astman_append(s,
+		"Event: BridgeInfoChannel\r\n"
+		"Uniqueid: %s\r\n"
+		"%s"
+		"\r\n",
+		uniqueid,
+		id_text);
+	return 0;
+}
+
+static int manager_bridge_info(struct mansession *s, const struct message *m)
+{
+	const char *id = astman_get_header(m, "ActionID");
+	const char *bridge_uniqueid = astman_get_header(m, "BridgeUniqueid");
+	RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free);
+	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_str *, bridge_info, NULL, ast_free);
+	struct ast_bridge_snapshot *snapshot;
+
+	if (!id_text) {
+		astman_send_error(s, m, "Internal error");
+		return -1;
+	}
+
+	if (!ast_strlen_zero(bridge_uniqueid)) {
+		astman_send_error(s, m, "BridgeUniqueid must be provided");
+		return -1;
+	}
+
+	if (!ast_strlen_zero(id)) {
+		ast_str_set(&id_text, 0, "ActionID: %s\r\n", id);
+	}
+
+	msg = stasis_cache_get(ast_bridge_topic_all_cached(), ast_bridge_snapshot_type(), bridge_uniqueid);
+	if (!msg) {
+		astman_send_error(s, m, "Specified BridgeUniqueid not found");
+		return -1;
+	}
+
+	snapshot = stasis_message_data(msg);
+	bridge_info = manager_build_bridge_state_string(snapshot, "");
+
+	ao2_callback_data(snapshot->channels, OBJ_NODATA, send_bridge_info_item_cb, s, ast_str_buffer(id_text));
+
+	astman_append(s,
+		"Event: BridgeInfoComplete\r\n"
+		"%s"
+		"%s"
+		"\r\n",
+		ast_str_buffer(bridge_info),
+		ast_str_buffer(id_text));
+
+	return 0;
+}
+
+static void manager_bridging_shutdown(void)
+{
+	stasis_message_router_unsubscribe(bridge_state_router);
+	bridge_state_router = NULL;
+	ast_manager_unregister("BridgeList");
+	ast_manager_unregister("BridgeInfo");
+}
+
+int manager_bridging_init(void)
+{
+	int ret = 0;
+
+	if (bridge_state_router) {
+		/* Already initialized */
+		return 0;
+	}
+
+	ast_register_atexit(manager_bridging_shutdown);
+
+	bridge_state_router = stasis_message_router_create(
+		stasis_caching_get_topic(ast_bridge_topic_all_cached()));
+
+	if (!bridge_state_router) {
+		return -1;
+	}
+
+	ret |= stasis_message_router_add(bridge_state_router,
+					 stasis_cache_update_type(),
+					 bridge_snapshot_update,
+					 NULL);
+
+	ret |= stasis_message_router_add(bridge_state_router,
+					 ast_bridge_merge_message_type(),
+					 bridge_merge_cb,
+					 NULL);
+
+	ret |= stasis_message_router_add(bridge_state_router,
+					 ast_bridge_blob_type(),
+					 bridge_blob_cb,
+					 NULL);
+
+	ret |= ast_manager_register_xml_core("BridgeList", 0, manager_bridges_list);
+	ret |= ast_manager_register_xml_core("BridgeInfo", 0, manager_bridge_info);
+
+	/* If somehow we failed to add any routes, just shut down the whole
+	 * thing and fail it.
+	 */
+	if (ret) {
+		manager_bridging_shutdown();
+		return -1;
+	}
+
+	return 0;
+}

Propchange: team/group/bridge_construction/main/manager_bridging.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/bridge_construction/main/manager_bridging.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/bridge_construction/main/manager_bridging.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/group/bridge_construction/main/manager_channels.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/manager_channels.c?view=diff&rev=385352&r1=385351&r2=385352
==============================================================================
--- team/group/bridge_construction/main/manager_channels.c (original)
+++ team/group/bridge_construction/main/manager_channels.c Thu Apr 11 14:28:18 2013
@@ -305,83 +305,18 @@
  * \retval ast_str* on success (must be ast_freed by caller)
  */
 static struct ast_str *manager_build_channel_state_string(
-	const struct ast_channel_snapshot *snapshot)
+		const struct ast_channel_snapshot *snapshot)
 {
 	return manager_build_channel_state_string_suffix(snapshot, "");
 }
 
-/*! \brief Struct containing info for an AMI channel event to send out. */
-struct snapshot_manager_event {
-	/*! event_flags manager_event() flags parameter. */
-	int event_flags;
-	/*!  manager_event manager_event() category. */
-	const char *manager_event;
-	AST_DECLARE_STRING_FIELDS(
-		/* extra fields to include in the event. */
-		AST_STRING_FIELD(extra_fields);
-		);
-};
-
-static void snapshot_manager_event_dtor(void *obj)
-{
-	struct snapshot_manager_event *ev = obj;
-	ast_string_field_free_memory(ev);
-}
-
-/*!
- * \brief Construct a \ref snapshot_manager_event.
- * \param event_flags manager_event() flags parameter.
- * \param manager_event manager_event() category.
- * \param extra_fields_fmt Format string for extra fields to include.
- *                         Or NO_EXTRA_FIELDS for no extra fields.
- * \return New \ref snapshot_manager_event object.
- * \return \c NULL on error.
- */
-static struct snapshot_manager_event *
-__attribute__((format(printf, 3, 4)))
-snapshot_manager_event_create(
-	int event_flags,
-	const char *manager_event,
-	const char *extra_fields_fmt,
-	...)
-{
-	RAII_VAR(struct snapshot_manager_event *, ev, NULL, ao2_cleanup);
-	va_list argp;
-
-	ast_assert(extra_fields_fmt != NULL);
-	ast_assert(manager_event != NULL);
-
-	ev = ao2_alloc(sizeof(*ev), snapshot_manager_event_dtor);
-	if (!ev) {
-		return NULL;
-	}
-
-	if (ast_string_field_init(ev, 20)) {
-		return NULL;
-	}
-
-	ev->manager_event = manager_event;
-	ev->event_flags = event_flags;
-
-	va_start(argp, extra_fields_fmt);
-	ast_string_field_ptr_build_va(ev, &ev->extra_fields, extra_fields_fmt,
-				      argp);
-	va_end(argp);
-
-	ao2_ref(ev, +1);
-	return ev;
-}
-
-/*! GCC warns about blank or NULL format strings. So, shenanigans! */
-#define NO_EXTRA_FIELDS "%s", ""
-
 /*! \brief Typedef for callbacks that get called on channel snapshot updates */
-typedef struct snapshot_manager_event *(*snapshot_monitor)(
+typedef struct ast_manager_event_blob *(*channel_snapshot_monitor)(
 	struct ast_channel_snapshot *old_snapshot,
 	struct ast_channel_snapshot *new_snapshot);
 
 /*! \brief Handle channel state changes */
-static struct snapshot_manager_event *channel_state_change(
+static struct ast_manager_event_blob *channel_state_change(
 	struct ast_channel_snapshot *old_snapshot,
 	struct ast_channel_snapshot *new_snapshot)
 {
@@ -398,7 +333,7 @@
 	 */
 
 	if (!old_snapshot) {
-		return snapshot_manager_event_create(
+		return ast_manager_event_blob_create(
 			EVENT_FLAG_CALL, "Newchannel", NO_EXTRA_FIELDS);
 	}
 
@@ -406,7 +341,7 @@
 	is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_ZOMBIE) ? 1 : 0;
 
 	if (!was_hungup && is_hungup) {
-		return snapshot_manager_event_create(
+		return ast_manager_event_blob_create(
 			EVENT_FLAG_CALL, "Hangup",
 			"Cause: %d\r\n"
 			"Cause-txt: %s\r\n",
@@ -415,7 +350,7 @@
 	}
 
 	if (old_snapshot->state != new_snapshot->state) {
-		return snapshot_manager_event_create(
+		return ast_manager_event_blob_create(
 			EVENT_FLAG_CALL, "Newstate", NO_EXTRA_FIELDS);
 	}
 
@@ -451,7 +386,7 @@
 		strcmp(old_snapshot->exten, new_snapshot->exten) == 0;
 }
 
-static struct snapshot_manager_event *channel_newexten(
+static struct ast_manager_event_blob *channel_newexten(
 	struct ast_channel_snapshot *old_snapshot,
 	struct ast_channel_snapshot *new_snapshot)
 {
@@ -470,7 +405,7 @@
 	}
 
 	/* DEPRECATED: Extension field deprecated in 12; remove in 14 */
-	return snapshot_manager_event_create(
+	return ast_manager_event_blob_create(
 		EVENT_FLAG_CALL, "Newexten",
 		"Extension: %s\r\n"
 		"Application: %s\r\n"
@@ -497,7 +432,7 @@
 		strcmp(old_snapshot->caller_name, new_snapshot->caller_name) == 0;
 }
 
-static struct snapshot_manager_event *channel_new_callerid(
+static struct ast_manager_event_blob *channel_new_callerid(
 	struct ast_channel_snapshot *old_snapshot,
 	struct ast_channel_snapshot *new_snapshot)
 {
@@ -510,14 +445,14 @@
 		return NULL;
 	}
 
-	return snapshot_manager_event_create(
+	return ast_manager_event_blob_create(
 		EVENT_FLAG_CALL, "NewCallerid",
 		"CID-CallingPres: %d (%s)\r\n",
 		new_snapshot->caller_pres,
 		ast_describe_caller_presentation(new_snapshot->caller_pres));
 }
 
-snapshot_monitor monitors[] = {
+channel_snapshot_monitor channel_monitors[] = {
 	channel_state_change,
 	channel_newexten,
 	channel_new_callerid
@@ -542,9 +477,9 @@
 	old_snapshot = stasis_message_data(update->old_snapshot);
 	new_snapshot = stasis_message_data(update->new_snapshot);
 
-	for (i = 0; i < ARRAY_LEN(monitors); ++i) {
-		RAII_VAR(struct snapshot_manager_event *, ev, NULL, ao2_cleanup);
-		ev = monitors[i](old_snapshot, new_snapshot);
+	for (i = 0; i < ARRAY_LEN(channel_monitors); ++i) {
+		RAII_VAR(struct ast_manager_event_blob *, ev, NULL, ao2_cleanup);
+		ev = channel_monitors[i](old_snapshot, new_snapshot);
 
 		if (!ev) {
 			continue;

Added: team/group/bridge_construction/main/stasis_bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/stasis_bridging.c?view=auto&rev=385352
==============================================================================
--- team/group/bridge_construction/main/stasis_bridging.c (added)
+++ team/group/bridge_construction/main/stasis_bridging.c Thu Apr 11 14:28:18 2013
@@ -1,0 +1,350 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Kinsey Moore <kmoore 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 Messages and Data Types for Bridge Objects
+ *
+ * \author Kinsey Moore <kmoore at digium.com>
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/astobj2.h"
+#include "asterisk/stasis.h"
+#include "asterisk/channel.h"
+#include "asterisk/stasis_bridging.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_technology.h"
+
+#define SNAPSHOT_CHANNELS_BUCKETS 13
+
+/*! \brief Message type for bridge snapshot messages */
+static struct stasis_message_type *bridge_snapshot_type;
+
+/*! \brief Message type for bridge blob messages */
+static struct stasis_message_type *bridge_blob_type;
+
+/*! \brief Message type for bridge merge messages */
+static struct stasis_message_type *bridge_merge_message_type;
+
+/*! \brief Aggregate topic for bridge messages */
+static struct stasis_topic *bridge_topic_all;
+
+/*! \brief Caching aggregate topic for bridge snapshots */
+static struct stasis_caching_topic *bridge_topic_all_cached;
+
+/*! \brief Topic pool for individual bridge topics */
+static struct stasis_topic_pool *bridge_topic_pool;
+
+/*! \brief Destructor for bridge snapshots */
+static void bridge_snapshot_dtor(void *obj)
+{
+	struct ast_bridge_snapshot *snapshot = obj;
+	ast_string_field_free_memory(snapshot);
+	ao2_cleanup(snapshot->channels);
+	snapshot->channels = NULL;
+}
+
+struct ast_bridge_snapshot *ast_bridge_snapshot_create(struct ast_bridge *bridge)
+{
+	RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
+	struct ast_bridge_channel *bridge_channel;
+
+	snapshot = ao2_alloc(sizeof(*snapshot), bridge_snapshot_dtor);
+	if (!snapshot || ast_string_field_init(snapshot, 128)) {
+		return NULL;
+	}
+
+	snapshot->channels = ast_str_container_alloc(SNAPSHOT_CHANNELS_BUCKETS);
+	if (!snapshot->channels) {
+		return NULL;
+	}
+
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		if (ast_str_container_add(snapshot->channels,

[... 313 lines stripped ...]



More information about the asterisk-commits mailing list