diff --git a/main/manager.c b/main/manager.c index c5a48fd4cb..650c632d1d 100644 --- a/main/manager.c +++ b/main/manager.c @@ -68,6 +68,7 @@ #include "asterisk/module.h" #include "asterisk/config.h" #include "asterisk/callerid.h" +#include "asterisk/core_local.h" #include "asterisk/lock.h" #include "asterisk/cli.h" #include "asterisk/app.h" @@ -1041,6 +1042,33 @@ + + + Raised at the end of the CoreShowChannelMap list produced by the CoreShowChannelMap command. + + + Conveys the status of the command response list + + + The total number of list items produced + + + + + + + List all channels connected to the specified channel. + + + + The channel to get the mapping for. Requires a channel name. + + + + List all channels currently connected to the specified channel. This can be any channel, including + Local channels, and Local channels will be followed through to their other half. + + Reload and rotate the Asterisk logger. @@ -6873,6 +6901,180 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m return 0; } +/*! \brief Helper function to add a channel name to the vector */ +static int coreshowchannelmap_add_to_map(struct ao2_container *c, const char *s) +{ + char *str; + + str = ast_strdup(s); + if (!str) { + ast_log(LOG_ERROR, "Unable to append channel to channel map\n"); + return 1; + } + + /* If this is a duplicate, it will be ignored */ + ast_str_container_add(c, str); + + return 0; +} + +/*! \brief Recursive function to get all channels in a bridge. Follow local channels as well */ +static int coreshowchannelmap_add_connected_channels(struct ao2_container *channel_map, + struct ast_channel_snapshot *channel_snapshot, struct ast_bridge_snapshot *bridge_snapshot) +{ + int res = 0; + struct ao2_iterator iter; + char *current_channel_uid; + + iter = ao2_iterator_init(bridge_snapshot->channels, 0); + while ((current_channel_uid = ao2_iterator_next(&iter))) { + struct ast_channel_snapshot *current_channel_snapshot; + int add_channel_res; + + /* Don't add the original channel to the list - it's either already in there, + * or it's the channel we want the map for */ + if (!strcmp(current_channel_uid, channel_snapshot->base->uniqueid)) { + ao2_ref(current_channel_uid, -1); + continue; + } + + current_channel_snapshot = ast_channel_snapshot_get_latest(current_channel_uid); + if (!current_channel_snapshot) { + ast_debug(5, "Unable to get channel snapshot\n"); + ao2_ref(current_channel_uid, -1); + continue; + } + + add_channel_res = coreshowchannelmap_add_to_map(channel_map, current_channel_snapshot->base->name); + if (add_channel_res) { + res = 1; + ao2_ref(current_channel_snapshot, -1); + ao2_ref(current_channel_uid, -1); + break; + } + + /* If this is a local channel that we haven't seen yet, let's go ahead and find out what else is connected to it */ + if (ast_begins_with(current_channel_snapshot->base->name, "Local")) { + struct ast_channel_snapshot *other_local_snapshot; + struct ast_bridge_snapshot *other_bridge_snapshot; + int size = strlen(current_channel_snapshot->base->name); + char other_local[size + 1]; + + /* Don't copy the trailing number - set it to 1 or 2, whichever one it currently is not */ + ast_copy_string(other_local, current_channel_snapshot->base->name, size); + other_local[size - 1] = ast_ends_with(current_channel_snapshot->base->name, "1") ? '2' : '1'; + other_local[size] = '\0'; + + other_local_snapshot = ast_channel_snapshot_get_latest_by_name(other_local); + if (!other_local_snapshot) { + ast_debug(5, "Unable to get other local channel snapshot\n"); + ao2_ref(current_channel_snapshot, -1); + ao2_ref(current_channel_uid, -1); + continue; + } + + if (coreshowchannelmap_add_to_map(channel_map, other_local_snapshot->base->name)) { + res = 1; + ao2_ref(current_channel_snapshot, -1); + ao2_ref(current_channel_uid, -1); + ao2_ref(other_local_snapshot, -1); + break; + } + + other_bridge_snapshot = ast_bridge_get_snapshot_by_uniqueid(other_local_snapshot->bridge->id); + if (other_bridge_snapshot) { + res = coreshowchannelmap_add_connected_channels(channel_map, other_local_snapshot, other_bridge_snapshot); + } + + ao2_ref(current_channel_snapshot, -1); + ao2_ref(current_channel_uid, -1); + ao2_ref(other_local_snapshot, -1); + ao2_ref(other_bridge_snapshot, -1); + + if (res) { + break; + } + } + } + ao2_iterator_destroy(&iter); + + return res; +} + +/*! \brief Manager command "CoreShowChannelMap" - Lists all channels connected to + * the specified channel. */ +static int action_coreshowchannelmap(struct mansession *s, const struct message *m) +{ + const char *actionid = astman_get_header(m, "ActionID"); + const char *channel_name = astman_get_header(m, "Channel"); + char *current_channel_name; + char id_text[256]; + int total = 0; + struct ao2_container *channel_map; + struct ao2_iterator i; + RAII_VAR(struct ast_bridge_snapshot *, bridge_snapshot, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel_snapshot *, channel_snapshot, NULL, ao2_cleanup); + + if (!ast_strlen_zero(actionid)) { + snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid); + } else { + id_text[0] = '\0'; + } + + if (ast_strlen_zero(channel_name)) { + astman_send_error(s, m, "CoreShowChannelMap requires a channel.\n"); + return 0; + } + + channel_snapshot = ast_channel_snapshot_get_latest_by_name(channel_name); + if (!channel_snapshot) { + astman_send_error(s, m, "Could not get channel snapshot\n"); + return 0; + } + + bridge_snapshot = ast_bridge_get_snapshot_by_uniqueid(channel_snapshot->bridge->id); + if (!bridge_snapshot) { + astman_send_listack(s, m, "Channel map will follow", "start"); + astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", 0); + astman_send_list_complete_end(s); + return 0; + } + + channel_map = ast_str_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK | AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT, 1); + if (!channel_map) { + astman_send_error(s, m, "Could not create channel map\n"); + return 0; + } + + astman_send_listack(s, m, "Channel map will follow", "start"); + + if (coreshowchannelmap_add_connected_channels(channel_map, channel_snapshot, bridge_snapshot)) { + astman_send_error(s, m, "Could not complete channel map\n"); + ao2_ref(channel_map, -1); + return 0; + } + + i = ao2_iterator_init(channel_map, 0); + while ((current_channel_name = ao2_iterator_next(&i))) { + astman_append(s, + "Event: CoreShowChannelMap\r\n" + "%s" + "Channel: %s\r\n" + "ConnectedChannel: %s\r\n\n", + id_text, + channel_name, + current_channel_name); + total++; + } + ao2_iterator_destroy(&i); + + ao2_ref(channel_map, -1); + astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", total); + astman_send_list_complete_end(s); + + return 0; +} + /*! \brief Manager command "LoggerRotate" - reloads and rotates the logger in * the same manner as the CLI command 'logger rotate'. */ static int action_loggerrotate(struct mansession *s, const struct message *m) @@ -9450,6 +9652,7 @@ static void manager_shutdown(void) ast_manager_unregister("Reload"); ast_manager_unregister("LoggerRotate"); ast_manager_unregister("CoreShowChannels"); + ast_manager_unregister("CoreShowChannelMap"); ast_manager_unregister("ModuleLoad"); ast_manager_unregister("ModuleCheck"); ast_manager_unregister("AOCMessage"); @@ -9665,6 +9868,7 @@ static int __init_manager(int reload, int by_external_config) ast_manager_register_xml_core("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload); ast_manager_register_xml_core("LoggerRotate", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_loggerrotate); ast_manager_register_xml_core("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels); + ast_manager_register_xml_core("CoreShowChannelMap", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannelmap); ast_manager_register_xml_core("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload); ast_manager_register_xml_core("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck); ast_manager_register_xml_core("AOCMessage", EVENT_FLAG_AOC, action_aocmessage);