diff options
author | Matthew Jordan <mjordan@digium.com> | 2013-07-25 04:06:32 +0000 |
---|---|---|
committer | Matthew Jordan <mjordan@digium.com> | 2013-07-25 04:06:32 +0000 |
commit | cafc1158961f28ad54ebcdc5e042bf8094cbc620 (patch) | |
tree | 2e54d30ea5d8db7e007ff5c653c5acd4ed3fb649 /main/manager_bridges.c | |
parent | 9d8a5ceb02f6559940bd94aaf163a544ce6e6f4c (diff) |
A great big renaming patch
This patch renames the bridging* files to bridge*. This may seem pedantic
and silly, but it fits better in line with current Asterisk naming conventions:
* channel is not "channeling"
* monitor is not "monitoring"
etc.
A bridge is an object. It is a first class citizen in Asterisk. "Bridging" is
the act of using a bridge on a set of channels - and the API that fulfills that
role is more than just the action.
(closes issue ASTERISK-22130)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395378 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/manager_bridges.c')
-rw-r--r-- | main/manager_bridges.c | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/main/manager_bridges.c b/main/manager_bridges.c new file mode 100644 index 000000000..0bc086d5b --- /dev/null +++ b/main/manager_bridges.c @@ -0,0 +1,531 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Kinsey Moore <kmoore@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@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"> + <para>The unique identifier of the bridge</para> + </parameter> + <parameter name="BridgeType"> + <para>The type of bridge</para> + </parameter> + <parameter name="BridgeTechnology"> + <para>Technology in use by the bridge</para> + </parameter> + <parameter name="BridgeNumChannels"> + <para>Number of channels in the 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 The \ref stasis subscription returned by the forwarding of the channel topic + * to the manager topic + */ +static struct stasis_subscription *topic_forwarder; + +struct ast_str *ast_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" + "BridgeTechnology%s: %s\r\n" + "BridgeNumChannels%s: %d\r\n", + suffix, snapshot->uniqueid, + suffix, snapshot->subclass, + suffix, snapshot->technology, + suffix, snapshot->num_channels); + + if (!res) { + ast_free(out); + 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); + + ast_assert(ast_bridge_snapshot_type() == update->type); + + 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 = + ast_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 = ast_manager_build_bridge_state_string(merge_msg->to, ""); + from_text = ast_manager_build_bridge_state_string(merge_msg->from, "From"); + if (!to_text || !from_text) { + return; + } + + /*** 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 channel_enter_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, + struct stasis_message *message) +{ + struct ast_bridge_blob *blob = stasis_message_data(message); + RAII_VAR(struct ast_str *, bridge_text, NULL, ast_free); + RAII_VAR(struct ast_str *, channel_text, NULL, ast_free); + + bridge_text = ast_manager_build_bridge_state_string(blob->bridge, ""); + channel_text = ast_manager_build_channel_state_string(blob->channel); + if (!bridge_text || !channel_text) { + return; + } + + manager_event(EVENT_FLAG_CALL, "BridgeEnter", + "%s" + "%s", + ast_str_buffer(bridge_text), + ast_str_buffer(channel_text)); +} + +static void channel_leave_cb(void *data, struct stasis_subscription *sub, + struct stasis_topic *topic, + struct stasis_message *message) +{ + struct ast_bridge_blob *blob = stasis_message_data(message); + RAII_VAR(struct ast_str *, bridge_text, NULL, ast_free); + RAII_VAR(struct ast_str *, channel_text, NULL, ast_free); + + bridge_text = ast_manager_build_bridge_state_string(blob->bridge, ""); + channel_text = ast_manager_build_channel_state_string(blob->channel); + if (!bridge_text || !channel_text) { + return; + } + + manager_event(EVENT_FLAG_CALL, "BridgeLeave", + "%s" + "%s", + ast_str_buffer(bridge_text), + ast_str_buffer(channel_text)); +} + +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, ast_manager_build_bridge_state_string(snapshot, ""), ast_free); + + if (!bridge_info) { + return 0; + } + + 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; + } + + astman_send_ack(s, m, "Bridge listing will follow"); + + 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; + RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); + struct ast_channel_snapshot *snapshot; + RAII_VAR(struct ast_str *, channel_text, NULL, ast_free); + msg = stasis_cache_get(ast_channel_topic_all_cached(), + ast_channel_snapshot_type(), uniqueid); + + if (!msg) { + return 0; + } + + snapshot = stasis_message_data(msg); + if (snapshot->tech_properties & (AST_CHAN_TP_ANNOUNCER | AST_CHAN_TP_RECORDER)) { + return 0; + } + + channel_text = ast_manager_build_channel_state_string(snapshot); + if (!channel_text) { + return 0; + } + + astman_append(s, + "Event: BridgeInfoChannel\r\n" + "%s" + "%s" + "\r\n", + ast_str_buffer(channel_text), + 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; + } + + astman_send_ack(s, m, "Bridge channel listing will follow"); + + snapshot = stasis_message_data(msg); + bridge_info = ast_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", + S_COR(bridge_info, ast_str_buffer(bridge_info), ""), + ast_str_buffer(id_text)); + + return 0; +} + +static void manager_bridging_cleanup(void) +{ + stasis_message_router_unsubscribe(bridge_state_router); + bridge_state_router = NULL; + stasis_unsubscribe(topic_forwarder); + topic_forwarder = NULL; +} + +static void manager_bridging_shutdown(void) +{ + ast_manager_unregister("BridgeList"); + ast_manager_unregister("BridgeInfo"); +} + +int manager_bridging_init(void) +{ + int ret = 0; + struct stasis_topic *manager_topic; + struct stasis_topic *bridge_topic; + + if (bridge_state_router) { + /* Already initialized */ + return 0; + } + + ast_register_atexit(manager_bridging_shutdown); + ast_register_cleanup(manager_bridging_cleanup); + + manager_topic = ast_manager_get_topic(); + if (!manager_topic) { + return -1; + } + + bridge_topic = stasis_caching_get_topic(ast_bridge_topic_all_cached()); + if (!bridge_topic) { + return -1; + } + + topic_forwarder = stasis_forward_all(bridge_topic, manager_topic); + if (!topic_forwarder) { + return -1; + } + + bridge_state_router = ast_manager_get_message_router(); + if (!bridge_state_router) { + return -1; + } + + ret |= stasis_message_router_add_cache_update(bridge_state_router, + ast_bridge_snapshot_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_channel_entered_bridge_type(), channel_enter_cb, NULL); + + ret |= stasis_message_router_add(bridge_state_router, + ast_channel_left_bridge_type(), channel_leave_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; +} |