2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013, Digium, Inc.
6 * Kinsey Moore <kmoore@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief The Asterisk Management Interface - AMI (bridge event handling)
23 * \author Kinsey Moore <kmoore@digium.com>
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include "asterisk/stasis_bridging.h"
31 #include "asterisk/stasis_channels.h"
32 #include "asterisk/manager.h"
33 #include "asterisk/stasis_message_router.h"
35 /*! \brief Message router for cached bridge state snapshot updates */
36 static struct stasis_message_router *bridge_state_router;
39 <managerEvent language="en_US" name="BridgeCreate">
40 <managerEventInstance class="EVENT_FLAG_CALL">
41 <synopsis>Raised when a bridge is created.</synopsis>
43 <parameter name="BridgeUniqueid">
44 <para>The unique identifier of the bridge</para>
46 <parameter name="BridgeType">
47 <para>The type of bridge</para>
49 <parameter name="BridgeTechnology">
50 <para>Technology in use by the bridge</para>
52 <parameter name="BridgeNumChannels">
53 <para>Number of channels in the bridge</para>
56 </managerEventInstance>
58 <managerEvent language="en_US" name="BridgeDestroy">
59 <managerEventInstance class="EVENT_FLAG_CALL">
60 <synopsis>Raised when a bridge is destroyed.</synopsis>
62 <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
64 </managerEventInstance>
66 <managerEvent language="en_US" name="BridgeEnter">
67 <managerEventInstance class="EVENT_FLAG_CALL">
68 <synopsis>Raised when a channel enters a bridge.</synopsis>
70 <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
71 <parameter name="Uniqueid">
72 <para>The uniqueid of the channel entering the bridge</para>
75 </managerEventInstance>
77 <managerEvent language="en_US" name="BridgeLeave">
78 <managerEventInstance class="EVENT_FLAG_CALL">
79 <synopsis>Raised when a channel leaves a bridge.</synopsis>
81 <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
82 <parameter name="Uniqueid">
83 <para>The uniqueid of the channel leaving the bridge</para>
86 </managerEventInstance>
88 <manager name="BridgeList" language="en_US">
90 Get a list of bridges in the system.
93 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
94 <parameter name="BridgeType">
95 <para>Optional type for filtering the resulting list of bridges.</para>
99 <para>Returns a list of bridges, optionally filtering on a bridge type.</para>
102 <manager name="BridgeInfo" language="en_US">
104 Get information about a bridge.
107 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
108 <parameter name="BridgeUniqueid" required="true">
109 <para>The unique ID of the bridge about which to retreive information.</para>
113 <para>Returns detailed information about a bridge and the channels in it.</para>
118 /*! \brief The \ref stasis subscription returned by the forwarding of the channel topic
119 * to the manager topic
121 static struct stasis_subscription *topic_forwarder;
123 struct ast_str *ast_manager_build_bridge_state_string(
124 const struct ast_bridge_snapshot *snapshot,
127 struct ast_str *out = ast_str_create(128);
132 res = ast_str_set(&out, 0,
133 "BridgeUniqueid%s: %s\r\n"
134 "BridgeType%s: %s\r\n"
135 "BridgeTechnology%s: %s\r\n"
136 "BridgeNumChannels%s: %d\r\n",
137 suffix, snapshot->uniqueid,
138 suffix, snapshot->subclass,
139 suffix, snapshot->technology,
140 suffix, snapshot->num_channels);
150 /*! \brief Typedef for callbacks that get called on channel snapshot updates */
151 typedef struct ast_manager_event_blob *(*bridge_snapshot_monitor)(
152 struct ast_bridge_snapshot *old_snapshot,
153 struct ast_bridge_snapshot *new_snapshot);
155 /*! \brief Handle bridge creation */
156 static struct ast_manager_event_blob *bridge_create(
157 struct ast_bridge_snapshot *old_snapshot,
158 struct ast_bridge_snapshot *new_snapshot)
160 if (!new_snapshot || old_snapshot) {
164 return ast_manager_event_blob_create(
165 EVENT_FLAG_CALL, "BridgeCreate", NO_EXTRA_FIELDS);
168 /*! \brief Handle bridge destruction */
169 static struct ast_manager_event_blob *bridge_destroy(
170 struct ast_bridge_snapshot *old_snapshot,
171 struct ast_bridge_snapshot *new_snapshot)
173 if (new_snapshot || !old_snapshot) {
177 return ast_manager_event_blob_create(
178 EVENT_FLAG_CALL, "BridgeDestroy", NO_EXTRA_FIELDS);
182 bridge_snapshot_monitor bridge_monitors[] = {
187 static void bridge_snapshot_update(void *data, struct stasis_subscription *sub,
188 struct stasis_topic *topic,
189 struct stasis_message *message)
191 RAII_VAR(struct ast_str *, bridge_event_string, NULL, ast_free);
192 struct stasis_cache_update *update;
193 struct ast_bridge_snapshot *old_snapshot;
194 struct ast_bridge_snapshot *new_snapshot;
197 update = stasis_message_data(message);
199 ast_assert(ast_bridge_snapshot_type() == update->type);
201 old_snapshot = stasis_message_data(update->old_snapshot);
202 new_snapshot = stasis_message_data(update->new_snapshot);
204 for (i = 0; i < ARRAY_LEN(bridge_monitors); ++i) {
205 RAII_VAR(struct ast_manager_event_blob *, event, NULL, ao2_cleanup);
207 event = bridge_monitors[i](old_snapshot, new_snapshot);
212 /* If we haven't already, build the channel event string */
213 if (!bridge_event_string) {
214 bridge_event_string =
215 ast_manager_build_bridge_state_string(
216 new_snapshot ? new_snapshot : old_snapshot, "");
217 if (!bridge_event_string) {
222 manager_event(event->event_flags, event->manager_event, "%s%s",
223 ast_str_buffer(bridge_event_string),
224 event->extra_fields);
228 static void bridge_merge_cb(void *data, struct stasis_subscription *sub,
229 struct stasis_topic *topic,
230 struct stasis_message *message)
232 struct ast_bridge_merge_message *merge_msg = stasis_message_data(message);
233 RAII_VAR(struct ast_str *, to_text, NULL, ast_free);
234 RAII_VAR(struct ast_str *, from_text, NULL, ast_free);
236 ast_assert(merge_msg->to != NULL);
237 ast_assert(merge_msg->from != NULL);
239 to_text = ast_manager_build_bridge_state_string(merge_msg->to, "");
240 from_text = ast_manager_build_bridge_state_string(merge_msg->from, "From");
241 if (!to_text || !from_text) {
246 <managerEventInstance>
247 <synopsis>Raised when two bridges are merged.</synopsis>
249 <xi:include xpointer="xpointer(/docs/managerEvent[@name='BridgeCreate']/managerEventInstance/syntax/parameter)" />
250 <parameter name="BridgeUniqueidFrom">
251 <para>The uniqueid of the bridge being dissolved in the merge</para>
253 <parameter name="BridgeTypeFrom">
254 <para>The type of bridge that is being dissolved in the merge</para>
257 </managerEventInstance>
259 manager_event(EVENT_FLAG_CALL, "BridgeMerge",
262 ast_str_buffer(to_text),
263 ast_str_buffer(from_text));
266 static void channel_enter_cb(void *data, struct stasis_subscription *sub,
267 struct stasis_topic *topic,
268 struct stasis_message *message)
270 struct ast_bridge_blob *blob = stasis_message_data(message);
271 RAII_VAR(struct ast_str *, bridge_text, NULL, ast_free);
272 RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
274 bridge_text = ast_manager_build_bridge_state_string(blob->bridge, "");
275 channel_text = ast_manager_build_channel_state_string(blob->channel);
276 if (!bridge_text || !channel_text) {
280 manager_event(EVENT_FLAG_CALL, "BridgeEnter",
283 ast_str_buffer(bridge_text),
284 ast_str_buffer(channel_text));
287 static void channel_leave_cb(void *data, struct stasis_subscription *sub,
288 struct stasis_topic *topic,
289 struct stasis_message *message)
291 struct ast_bridge_blob *blob = stasis_message_data(message);
292 RAII_VAR(struct ast_str *, bridge_text, NULL, ast_free);
293 RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
295 bridge_text = ast_manager_build_bridge_state_string(blob->bridge, "");
296 channel_text = ast_manager_build_channel_state_string(blob->channel);
297 if (!bridge_text || !channel_text) {
301 manager_event(EVENT_FLAG_CALL, "BridgeLeave",
304 ast_str_buffer(bridge_text),
305 ast_str_buffer(channel_text));
308 static int filter_bridge_type_cb(void *obj, void *arg, int flags)
310 char *bridge_type = arg;
311 struct ast_bridge_snapshot *snapshot = stasis_message_data(obj);
312 /* unlink all the snapshots that do not match the bridge type */
313 return strcmp(bridge_type, snapshot->technology) ? CMP_MATCH : 0;
316 static int send_bridge_list_item_cb(void *obj, void *arg, void *data, int flags)
318 struct ast_bridge_snapshot *snapshot = stasis_message_data(obj);
319 struct mansession *s = arg;
320 char *id_text = data;
321 RAII_VAR(struct ast_str *, bridge_info, ast_manager_build_bridge_state_string(snapshot, ""), ast_free);
328 "Event: BridgeListItem\r\n"
332 ast_str_buffer(bridge_info),
337 static int manager_bridges_list(struct mansession *s, const struct message *m)
339 const char *id = astman_get_header(m, "ActionID");
340 const char *type_filter = astman_get_header(m, "BridgeType");
341 RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free);
342 RAII_VAR(struct ao2_container *, bridges, NULL, ao2_cleanup);
345 astman_send_error(s, m, "Internal error");
349 if (!ast_strlen_zero(id)) {
350 ast_str_set(&id_text, 0, "ActionID: %s\r\n", id);
353 bridges = stasis_cache_dump(ast_bridge_topic_all_cached(), ast_bridge_snapshot_type());
355 astman_send_error(s, m, "Internal error");
359 astman_send_ack(s, m, "Bridge listing will follow");
361 if (!ast_strlen_zero(type_filter)) {
362 char *type_filter_dup = ast_strdupa(type_filter);
363 ao2_callback(bridges, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK, filter_bridge_type_cb, type_filter_dup);
366 ao2_callback_data(bridges, OBJ_NODATA, send_bridge_list_item_cb, s, ast_str_buffer(id_text));
369 "Event: BridgeListComplete\r\n"
372 ast_str_buffer(id_text));
377 static int send_bridge_info_item_cb(void *obj, void *arg, void *data, int flags)
379 char *uniqueid = obj;
380 struct mansession *s = arg;
381 char *id_text = data;
382 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
383 struct ast_channel_snapshot *snapshot;
384 msg = stasis_cache_get(ast_channel_topic_all_cached(),
385 ast_channel_snapshot_type(), uniqueid);
391 snapshot = stasis_message_data(msg);
392 if (snapshot->tech_properties & (AST_CHAN_TP_ANNOUNCER | AST_CHAN_TP_RECORDER)) {
397 "Event: BridgeInfoChannel\r\n"
406 static int manager_bridge_info(struct mansession *s, const struct message *m)
408 const char *id = astman_get_header(m, "ActionID");
409 const char *bridge_uniqueid = astman_get_header(m, "BridgeUniqueid");
410 RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free);
411 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
412 RAII_VAR(struct ast_str *, bridge_info, NULL, ast_free);
413 struct ast_bridge_snapshot *snapshot;
416 astman_send_error(s, m, "Internal error");
420 if (ast_strlen_zero(bridge_uniqueid)) {
421 astman_send_error(s, m, "BridgeUniqueid must be provided");
425 if (!ast_strlen_zero(id)) {
426 ast_str_set(&id_text, 0, "ActionID: %s\r\n", id);
429 msg = stasis_cache_get(ast_bridge_topic_all_cached(), ast_bridge_snapshot_type(), bridge_uniqueid);
431 astman_send_error(s, m, "Specified BridgeUniqueid not found");
435 astman_send_ack(s, m, "Bridge channel listing will follow");
437 snapshot = stasis_message_data(msg);
438 bridge_info = ast_manager_build_bridge_state_string(snapshot, "");
440 ao2_callback_data(snapshot->channels, OBJ_NODATA, send_bridge_info_item_cb, s, ast_str_buffer(id_text));
443 "Event: BridgeInfoComplete\r\n"
447 S_COR(bridge_info, ast_str_buffer(bridge_info), ""),
448 ast_str_buffer(id_text));
453 static void manager_bridging_cleanup(void)
455 stasis_message_router_unsubscribe(bridge_state_router);
456 bridge_state_router = NULL;
457 stasis_unsubscribe(topic_forwarder);
458 topic_forwarder = NULL;
461 static void manager_bridging_shutdown(void)
463 ast_manager_unregister("BridgeList");
464 ast_manager_unregister("BridgeInfo");
467 int manager_bridging_init(void)
470 struct stasis_topic *manager_topic;
471 struct stasis_topic *bridge_topic;
473 if (bridge_state_router) {
474 /* Already initialized */
478 ast_register_atexit(manager_bridging_shutdown);
479 ast_register_cleanup(manager_bridging_cleanup);
481 manager_topic = ast_manager_get_topic();
482 if (!manager_topic) {
486 bridge_topic = stasis_caching_get_topic(ast_bridge_topic_all_cached());
491 topic_forwarder = stasis_forward_all(bridge_topic, manager_topic);
492 if (!topic_forwarder) {
496 bridge_state_router = ast_manager_get_message_router();
497 if (!bridge_state_router) {
501 ret |= stasis_message_router_add_cache_update(bridge_state_router,
502 ast_bridge_snapshot_type(), bridge_snapshot_update, NULL);
504 ret |= stasis_message_router_add(bridge_state_router,
505 ast_bridge_merge_message_type(), bridge_merge_cb, NULL);
507 ret |= stasis_message_router_add(bridge_state_router,
508 ast_channel_entered_bridge_type(), channel_enter_cb, NULL);
510 ret |= stasis_message_router_add(bridge_state_router,
511 ast_channel_left_bridge_type(), channel_leave_cb, NULL);
513 ret |= ast_manager_register_xml_core("BridgeList", 0, manager_bridges_list);
514 ret |= ast_manager_register_xml_core("BridgeInfo", 0, manager_bridge_info);
516 /* If somehow we failed to add any routes, just shut down the whole
520 manager_bridging_shutdown();