diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h index a28481c90c40583392999bf6ca3b565929d7be40..53673c7f7ff16b11be67dd9da97fd56992ce6946 100644 --- a/include/asterisk/bridge.h +++ b/include/asterisk/bridge.h @@ -1020,6 +1020,17 @@ struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channe */ void ast_bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags flags); +/*! + * \brief Find bridge by id + * \since 12.0.0 + * + * \param bridge_id Bridge identifier + * + * \return NULL bridge not found + * \return non-NULL reference to bridge + */ +struct ast_bridge *ast_bridge_find_by_id(const char *bridge_id); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/main/bridge.c b/main/bridge.c index a7b61847d215a95504f679efaab8eb90cffce9f0..1c3bf16e3a29f7312a283c46e139fbe33355d5ca 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -27,6 +27,48 @@ <support_level>core</support_level> ***/ +/*** DOCUMENTATION + <manager name="BridgeTechnologyList" language="en_US"> + <synopsis> + List available bridging technologies and their statuses. + </synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /> + </syntax> + <description> + <para>Returns detailed information about the available bridging technologies.</para> + </description> + </manager> + <manager name="BridgeTechnologySuspend" language="en_US"> + <synopsis> + Suspend a bridging technology. + </synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /> + <parameter name="BridgeTechnology" required="true"> + <para>The name of the bridging technology to suspend.</para> + </parameter> + </syntax> + <description> + <para>Marks a bridging technology as suspended, which prevents subsequently created bridges from using it.</para> + </description> + </manager> + <manager name="BridgeTechnologyUnsuspend" language="en_US"> + <synopsis> + Unsuspend a bridging technology. + </synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /> + <parameter name="BridgeTechnology" required="true"> + <para>The name of the bridging technology to unsuspend.</para> + </parameter> + </syntax> + <description> + <para>Clears a previously suspended bridging technology, which allows subsequently created bridges to use it.</para> + </description> + </manager> +***/ + #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") @@ -4485,6 +4527,11 @@ static int bridge_sort_cmp(const void *obj_left, const void *obj_right, int flag return cmp; } +struct ast_bridge *ast_bridge_find_by_id(const char *bridge_id) +{ + return ao2_find(bridges, bridge_id, OBJ_SEARCH_KEY); +} + struct bridge_complete { /*! Nth match to return. */ int state; @@ -4673,7 +4720,7 @@ static char *handle_bridge_destroy_specific(struct ast_cli_entry *e, int cmd, st return CLI_SHOWUSAGE; } - bridge = ao2_find(bridges, a->argv[2], OBJ_KEY); + bridge = ast_bridge_find_by_id(a->argv[2]); if (!bridge) { ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]); return CLI_SUCCESS; @@ -4692,7 +4739,7 @@ static char *complete_bridge_participant(const char *bridge_name, const char *li int which; int wordlen; - bridge = ao2_find(bridges, bridge_name, OBJ_KEY); + bridge = ast_bridge_find_by_id(bridge_name); if (!bridge) { return NULL; } @@ -4739,7 +4786,7 @@ static char *handle_bridge_kick_channel(struct ast_cli_entry *e, int cmd, struct return CLI_SHOWUSAGE; } - bridge = ao2_find(bridges, a->argv[2], OBJ_KEY); + bridge = ast_bridge_find_by_id(a->argv[2]); if (!bridge) { ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]); return CLI_SUCCESS; @@ -4891,13 +4938,102 @@ static char *handle_bridge_technology_suspend(struct ast_cli_entry *e, int cmd, static struct ast_cli_entry bridge_cli[] = { AST_CLI_DEFINE(handle_bridge_show_all, "List all bridges"), AST_CLI_DEFINE(handle_bridge_show_specific, "Show information about a bridge"), -/* XXX ASTERISK-22356 need AMI action equivalents to the following CLI commands. */ AST_CLI_DEFINE(handle_bridge_destroy_specific, "Destroy a bridge"), AST_CLI_DEFINE(handle_bridge_kick_channel, "Kick a channel from a bridge"), AST_CLI_DEFINE(handle_bridge_technology_show, "List registered bridge technologies"), AST_CLI_DEFINE(handle_bridge_technology_suspend, "Suspend/unsuspend a bridge technology"), }; + +static int handle_manager_bridge_tech_suspend(struct mansession *s, const struct message *m, int suspend) +{ + const char *name = astman_get_header(m, "BridgeTechnology"); + struct ast_bridge_technology *cur; + int successful = 0; + + if (ast_strlen_zero(name)) { + astman_send_error(s, m, "BridgeTechnology must be provided"); + return 0; + } + + AST_RWLIST_RDLOCK(&bridge_technologies); + AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) { + + if (!strcasecmp(cur->name, name)) { + successful = 1; + if (suspend) { + ast_bridge_technology_suspend(cur); + } else { + ast_bridge_technology_unsuspend(cur); + } + break; + } + } + AST_RWLIST_UNLOCK(&bridge_technologies); + if (!successful) { + astman_send_error(s, m, "BridgeTechnology not found"); + return 0; + } + + astman_send_ack(s, m, (suspend ? "Suspended bridge technology" : "Unsuspended bridge technology")); + return 0; +} + +static int manager_bridge_tech_suspend(struct mansession *s, const struct message *m) +{ + return handle_manager_bridge_tech_suspend(s, m, 1); +} + +static int manager_bridge_tech_unsuspend(struct mansession *s, const struct message *m) +{ + return handle_manager_bridge_tech_suspend(s, m, 0); +} + +static int manager_bridge_tech_list(struct mansession *s, const struct message *m) +{ + const char *id = astman_get_header(m, "ActionID"); + RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free); + struct ast_bridge_technology *cur; + + 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); + } + + astman_send_ack(s, m, "Bridge technology listing will follow"); + + AST_RWLIST_RDLOCK(&bridge_technologies); + AST_RWLIST_TRAVERSE(&bridge_technologies, cur, entry) { + const char *type; + + type = tech_capability2str(cur->capabilities); + + astman_append(s, + "Event: BridgeTechnologyListItem\r\n" + "BridgeTechnology: %s\r\n" + "BridgeType: %s\r\n" + "BridgePriority: %d\r\n" + "BridgeSuspended: %s\r\n" + "%s" + "\r\n", + cur->name, type, cur->preference, AST_YESNO(cur->suspended), + ast_str_buffer(id_text)); + } + AST_RWLIST_UNLOCK(&bridge_technologies); + + astman_append(s, + "Event: BridgeTechnologyListComplete\r\n" + "%s" + "\r\n", + ast_str_buffer(id_text)); + + return 0; +} + /*! * \internal * \brief Print bridge object key (name). @@ -4929,6 +5065,9 @@ static void bridge_prnt_obj(void *v_obj, void *where, ao2_prnt_fn *prnt) */ static void bridge_shutdown(void) { + ast_manager_unregister("BridgeTechnologyList"); + ast_manager_unregister("BridgeTechnologySuspend"); + ast_manager_unregister("BridgeTechnologyUnsuspend"); ast_cli_unregister_multiple(bridge_cli, ARRAY_LEN(bridge_cli)); ao2_container_unregister("bridges"); ao2_cleanup(bridges); @@ -4961,5 +5100,9 @@ int ast_bridging_init(void) ast_cli_register_multiple(bridge_cli, ARRAY_LEN(bridge_cli)); + ast_manager_register_xml_core("BridgeTechnologyList", 0, manager_bridge_tech_list); + ast_manager_register_xml_core("BridgeTechnologySuspend", 0, manager_bridge_tech_suspend); + ast_manager_register_xml_core("BridgeTechnologyUnsuspend", 0, manager_bridge_tech_unsuspend); + return 0; } diff --git a/main/manager_bridges.c b/main/manager_bridges.c index fad676b567ca9ec9a120329c7eb9045fce63623f..e012ebb6d64a694de78dcf7ae2b59d6ec070d8c8 100644 --- a/main/manager_bridges.c +++ b/main/manager_bridges.c @@ -101,6 +101,39 @@ static struct stasis_message_router *bridge_state_router; <para>Returns detailed information about a bridge and the channels in it.</para> </description> </manager> + <manager name="BridgeDestroy" language="en_US"> + <synopsis> + Destroy 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 to destroy.</para> + </parameter> + </syntax> + <description> + <para>Deletes the bridge, causing channels to continue or hang up.</para> + </description> + </manager> + <manager name="BridgeKick" language="en_US"> + <synopsis> + Kick a channel from a bridge. + </synopsis> + <syntax> + <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /> + <parameter name="BridgeUniqueid" required="false"> + <para>The unique ID of the bridge containing the channel to + destroy. This parameter can be omitted, or supplied to insure + that the channel is not removed from the wrong bridge.</para> + </parameter> + <parameter name="Channel" required="true"> + <para>The channel to kick out of a bridge.</para> + </parameter> + </syntax> + <description> + <para>The channel is removed from the bridge.</para> + </description> + </manager> ***/ /*! \brief The \ref stasis subscription returned by the forwarding of the channel topic @@ -419,7 +452,7 @@ static int manager_bridge_info(struct mansession *s, const struct message *m) if (ast_strlen_zero(bridge_uniqueid)) { astman_send_error(s, m, "BridgeUniqueid must be provided"); - return -1; + return 0; } if (!ast_strlen_zero(id)) { @@ -429,7 +462,7 @@ static int manager_bridge_info(struct mansession *s, const struct message *m) msg = stasis_cache_get(ast_bridge_cache(), ast_bridge_snapshot_type(), bridge_uniqueid); if (!msg) { astman_send_error(s, m, "Specified BridgeUniqueid not found"); - return -1; + return 0; } astman_send_ack(s, m, "Bridge channel listing will follow"); @@ -450,6 +483,72 @@ static int manager_bridge_info(struct mansession *s, const struct message *m) return 0; } +static int manager_bridge_destroy(struct mansession *s, const struct message *m) +{ + const char *bridge_uniqueid = astman_get_header(m, "BridgeUniqueid"); + struct ast_bridge *bridge; + + if (ast_strlen_zero(bridge_uniqueid)) { + astman_send_error(s, m, "BridgeUniqueid must be provided"); + return 0; + } + + bridge = ast_bridge_find_by_id(bridge_uniqueid); + if (!bridge) { + astman_send_error(s, m, "Specified BridgeUniqueid not found"); + return 0; + } + ast_bridge_destroy(bridge, 0); + + astman_send_ack(s, m, "Bridge has been destroyed"); + + return 0; +} + +static int manager_bridge_kick(struct mansession *s, const struct message *m) +{ + const char *bridge_uniqueid = astman_get_header(m, "BridgeUniqueid"); + const char *channel_name = astman_get_header(m, "Channel"); + RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel *, channel, NULL, ao2_cleanup); + + if (ast_strlen_zero(channel_name)) { + astman_send_error(s, m, "Channel must be provided"); + return 0; + } + + channel = ast_channel_get_by_name(channel_name); + if (!channel) { + astman_send_error(s, m, "Channel does not exist"); + return 0; + } + + if (ast_strlen_zero(bridge_uniqueid)) { + /* get the bridge from the channel */ + ast_channel_lock(channel); + bridge = ast_channel_get_bridge(channel); + ast_channel_unlock(channel); + if (!bridge) { + astman_send_error(s, m, "Channel is not in a bridge"); + return 0; + } + } else { + bridge = ast_bridge_find_by_id(bridge_uniqueid); + if (!bridge) { + astman_send_error(s, m, "Bridge not found"); + return 0; + } + } + + if (ast_bridge_kick(bridge, channel)) { + astman_send_error(s, m, "Channel kick from bridge failed"); + return 0; + } + + astman_send_ack(s, m, "Channel has been kicked"); + return 0; +} + static void manager_bridging_cleanup(void) { stasis_forward_cancel(topic_forwarder); @@ -460,6 +559,8 @@ static void manager_bridging_shutdown(void) { ast_manager_unregister("BridgeList"); ast_manager_unregister("BridgeInfo"); + ast_manager_unregister("BridgeDestroy"); + ast_manager_unregister("BridgeKick"); } int manager_bridging_init(void) @@ -510,6 +611,8 @@ int manager_bridging_init(void) ret |= ast_manager_register_xml_core("BridgeList", 0, manager_bridges_list); ret |= ast_manager_register_xml_core("BridgeInfo", 0, manager_bridge_info); + ret |= ast_manager_register_xml_core("BridgeDestroy", 0, manager_bridge_destroy); + ret |= ast_manager_register_xml_core("BridgeKick", 0, manager_bridge_kick); /* If somehow we failed to add any routes, just shut down the whole * thing and fail it.