From c4064727d208d61dc29721c52f450f67eb22a445 Mon Sep 17 00:00:00 2001 From: George Joseph <george.joseph@fairview5.com> Date: Sat, 26 Mar 2016 21:33:14 -0600 Subject: [PATCH] chan_pjsip: Add 'pjsip show channelstats' Added the ability to show channel statistics to chan_pjsip (cli_functions.c) Moved the existing 'pjsip show channel(s)' functionality from pjsip_configuration to cli_functions.c. The stats needed chan_pjsip's private header so it made sense to move the existing channel commands as well. Now using stasis_cache_dump to get the channel snapshots rather than retrieving all endpoints, then getting each one's channel snapshots. Much more efficient. Change-Id: I03b114522126d27434030b285bf6d531ddd79869 --- CHANGES | 3 + channels/chan_pjsip.c | 12 + channels/pjsip/cli_commands.c | 467 +++++++++++++++++++++++++ channels/pjsip/include/cli_functions.h | 45 +++ res/res_pjsip/pjsip_cli.c | 2 +- res/res_pjsip/pjsip_configuration.c | 261 -------------- 6 files changed, 528 insertions(+), 262 deletions(-) create mode 100644 channels/pjsip/cli_commands.c create mode 100644 channels/pjsip/include/cli_functions.h diff --git a/CHANGES b/CHANGES index 4b607b7a2d..f44df32843 100644 --- a/CHANGES +++ b/CHANGES @@ -263,6 +263,9 @@ res_parking: for these variables. The indefinite inheritance is also recommended for the PARKINGEXTEN variable. +chan_pjsip +------------------ + * Added 'pjsip show channelstats' CLI command. ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13.7.0 to Asterisk 13.8.0 ------------ diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index cd55400c3b..370075d2b7 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -69,6 +69,7 @@ ASTERISK_REGISTER_FILE() #include "pjsip/include/chan_pjsip.h" #include "pjsip/include/dialplan_functions.h" +#include "pjsip/include/cli_functions.h" AST_THREADSTORAGE(uniqueid_threadbuf); #define UNIQUEID_BUFSIZE 256 @@ -2468,6 +2469,15 @@ static int load_module(void) goto end; } + if (pjsip_channel_cli_register()) { + ast_log(LOG_ERROR, "Unable to register PJSIP Channel CLI\n"); + ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement); + ast_sip_session_unregister_supplement(&pbx_start_supplement); + ast_sip_session_unregister_supplement(&chan_pjsip_supplement); + ast_sip_session_unregister_supplement(&call_pickup_supplement); + goto end; + } + /* since endpoints are loaded before the channel driver their device states get set to 'invalid', so they need to be updated */ if ((endpoints = ast_sip_get_endpoints())) { @@ -2494,6 +2504,8 @@ static int unload_module(void) ao2_cleanup(pjsip_uids_onhold); pjsip_uids_onhold = NULL; + pjsip_channel_cli_unregister(); + ast_sip_session_unregister_supplement(&chan_pjsip_supplement); ast_sip_session_unregister_supplement(&pbx_start_supplement); ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement); diff --git a/channels/pjsip/cli_commands.c b/channels/pjsip/cli_commands.c new file mode 100644 index 0000000000..8d99379ffc --- /dev/null +++ b/channels/pjsip/cli_commands.c @@ -0,0 +1,467 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2016, Fairview 5 Engineering, LLC + * + * 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 + * + * \author \verbatim George Joseph <george.joseph@fairview5.com> \endverbatim + * + * \ingroup functions + * + * \brief PJSIP channel CLI functions + */ + +#include "asterisk.h" + +ASTERISK_REGISTER_FILE() + +#include <pjsip.h> +#include <pjlib.h> +#include <pjsip_ua.h> + +#include "asterisk/astobj2.h" +#include "asterisk/channel.h" +#include "asterisk/format.h" +#include "asterisk/res_pjsip.h" +#include "asterisk/res_pjsip_session.h" +#include "asterisk/res_pjsip_cli.h" +#include "asterisk/stasis.h" +#include "asterisk/time.h" +#include "include/chan_pjsip.h" +#include "include/cli_functions.h" + + +static int cli_channel_iterate(void *endpoint, ao2_callback_fn callback, void *arg) +{ + return ast_sip_for_each_channel(endpoint, callback, arg); +} + +static int cli_channelstats_iterate(void *endpoint, ao2_callback_fn callback, void *arg) +{ + return ast_sip_for_each_channel(endpoint, callback, arg); +} + +static int cli_channel_sort(const void *obj, const void *arg, int flags) +{ + const struct ast_channel_snapshot *left_obj = obj; + const struct ast_channel_snapshot *right_obj = arg; + const char *right_key = arg; + int cmp; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + right_key = right_obj->name; + /* Fall through */ + case OBJ_SEARCH_KEY: + cmp = strcmp(left_obj->name, right_key); + break; + case OBJ_SEARCH_PARTIAL_KEY: + cmp = strncmp(left_obj->name, right_key, strlen(right_key)); + break; + default: + cmp = 0; + break; + } + + return cmp; +} + +static int cli_channelstats_sort(const void *obj, const void *arg, int flags) +{ + const struct ast_channel_snapshot *left_obj = obj; + const struct ast_channel_snapshot *right_obj = arg; + const char *right_key = arg; + int cmp; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + cmp = strcmp(left_obj->bridgeid, right_obj->bridgeid); + if (cmp) { + return cmp; + } + right_key = right_obj->name; + /* Fall through */ + case OBJ_SEARCH_KEY: + cmp = strcmp(left_obj->name, right_key); + break; + case OBJ_SEARCH_PARTIAL_KEY: + cmp = strncmp(left_obj->name, right_key, strlen(right_key)); + break; + default: + cmp = 0; + break; + } + + return cmp; +} + +static int cli_channel_compare(void *obj, void *arg, int flags) +{ + const struct ast_channel_snapshot *left_obj = obj; + const struct ast_channel_snapshot *right_obj = arg; + const char *right_key = arg; + int cmp = 0; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + right_key = right_obj->name; + /* Fall through */ + case OBJ_SEARCH_KEY: + if (strcmp(left_obj->name, right_key) == 0) { + cmp = CMP_MATCH | CMP_STOP; + } + break; + case OBJ_SEARCH_PARTIAL_KEY: + if (strncmp(left_obj->name, right_key, strlen(right_key)) == 0) { + cmp = CMP_MATCH; + } + break; + default: + cmp = 0; + break; + } + + return cmp; +} + +static int cli_channelstats_compare(void *obj, void *arg, int flags) +{ + const struct ast_channel_snapshot *left_obj = obj; + const struct ast_channel_snapshot *right_obj = arg; + const char *right_key = arg; + int cmp = 0; + + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + if (strcmp(left_obj->bridgeid, right_obj->bridgeid) == 0 + && strcmp(left_obj->name, right_obj->name) == 0) { + return CMP_MATCH | CMP_STOP; + } + break; + case OBJ_SEARCH_KEY: + if (strcmp(left_obj->name, right_key) == 0) { + cmp = CMP_MATCH | CMP_STOP; + } + break; + case OBJ_SEARCH_PARTIAL_KEY: + if (strncmp(left_obj->name, right_key, strlen(right_key)) == 0) { + cmp = CMP_MATCH; + } + break; + default: + cmp = 0; + break; + } + + return cmp; +} + +static int cli_message_to_snapshot(void *obj, void *arg, int flags) +{ + struct stasis_message *message = obj; + struct ao2_container *snapshots = arg; + struct ast_channel_snapshot *snapshot = stasis_message_data(message); + + if (!strcmp(snapshot->type, "PJSIP")) { + ao2_link(snapshots, snapshot); + return CMP_MATCH; + } + + return 0; +} + +static int cli_filter_channels(void *obj, void *arg, int flags) +{ + struct ast_channel_snapshot *channel = obj; + regex_t *regexbuf = arg; + + if (!regexec(regexbuf, channel->name, 0, NULL, 0) + || !regexec(regexbuf, channel->appl, 0, NULL, 0)) { + return 0; + } + + return CMP_MATCH; +} + +static struct ao2_container *get_container(const char *regex, ao2_sort_fn sort_fn, ao2_callback_fn compare_fn) +{ + struct ao2_container *child_container; + regex_t regexbuf; + RAII_VAR(struct ao2_container *, parent_container, + stasis_cache_dump(ast_channel_cache_by_name(), ast_channel_snapshot_type()), ao2_cleanup); + + if (!parent_container) { + return NULL; + } + + child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, sort_fn, compare_fn); + if (!child_container) { + return NULL; + } + + ao2_callback(parent_container, OBJ_MULTIPLE | OBJ_NODATA, cli_message_to_snapshot, child_container); + + if (!ast_strlen_zero(regex)) { + if (regcomp(®exbuf, regex, REG_EXTENDED | REG_NOSUB)) { + ao2_ref(child_container, -1); + return NULL; + } + ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_channels, ®exbuf); + regfree(®exbuf); + } + + return child_container; +} + +static struct ao2_container *cli_channel_get_container(const char *regex) +{ + return get_container(regex, cli_channel_sort, cli_channel_compare); +} + +static struct ao2_container *cli_channelstats_get_container(const char *regex) +{ + return get_container(regex, cli_channelstats_sort, cli_channelstats_compare); +} + +static const char *cli_channel_get_id(const void *obj) +{ + const struct ast_channel_snapshot *snapshot = obj; + + return snapshot->name; +} + +static void *cli_channel_retrieve_by_id(const char *id) +{ + return ast_channel_snapshot_get_latest_by_name(id); +} + +static int cli_channel_print_header(void *obj, void *arg, int flags) +{ + struct ast_sip_cli_context *context = arg; + int indent = CLI_INDENT_TO_SPACES(context->indent_level); + int filler = CLI_LAST_TABSTOP - indent - 13; + + ast_assert(context->output_buffer != NULL); + + ast_str_append(&context->output_buffer, 0, + "%*s: <ChannelId%*.*s> <State.....> <Time.....>\n", + indent, "Channel", filler, filler, CLI_HEADER_FILLER); + if (context->recurse) { + context->indent_level++; + indent = CLI_INDENT_TO_SPACES(context->indent_level); + filler = CLI_LAST_TABSTOP - indent - 38; + ast_str_append(&context->output_buffer, 0, + "%*s: <DialedExten%*.*s> CLCID: <ConnectedLineCID.......>\n", + indent, "Exten", filler, filler, CLI_HEADER_FILLER); + context->indent_level--; + } + + return 0; +} + +static int cli_channel_print_body(void *obj, void *arg, int flags) +{ + const struct ast_channel_snapshot *snapshot = obj; + struct ast_sip_cli_context *context = arg; + char *print_name = NULL; + int print_name_len; + int indent; + int flexwidth; + char *print_time = alloca(32); + + ast_assert(context->output_buffer != NULL); + + print_name_len = strlen(snapshot->name) + strlen(snapshot->appl) + 2; + print_name = alloca(print_name_len); + + /* Append the application */ + snprintf(print_name, print_name_len, "%s/%s", snapshot->name, snapshot->appl); + + indent = CLI_INDENT_TO_SPACES(context->indent_level); + flexwidth = CLI_LAST_TABSTOP - indent; + + ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->creationtime.tv_sec, print_time, 32); + + ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %-12.12s %-11.11s\n", + CLI_INDENT_TO_SPACES(context->indent_level), "Channel", + flexwidth, flexwidth, + print_name, + ast_state2str(snapshot->state), + print_time); + + if (context->recurse) { + context->indent_level++; + indent = CLI_INDENT_TO_SPACES(context->indent_level); + flexwidth = CLI_LAST_TABSTOP - indent - 25; + + ast_str_append(&context->output_buffer, 0, + "%*s: %-*.*s CLCID: \"%s\" <%s>\n", + indent, "Exten", + flexwidth, flexwidth, + snapshot->exten, + snapshot->connected_name, + snapshot->connected_number + ); + context->indent_level--; + if (context->indent_level == 0) { + ast_str_append(&context->output_buffer, 0, "\n"); + } + } + + return 0; +} + +static int cli_channelstats_print_header(void *obj, void *arg, int flags) +{ + struct ast_sip_cli_context *context = arg; + + ast_assert(context->output_buffer != NULL); + + ast_str_append(&context->output_buffer, 0, + " ...........Receive......... .........Transmit..........\n" + " BridgeId ChannelId ........ UpTime.. Codec. Count Lost Pct Jitter Count Lost Pct Jitter RTT....\n" + " ================="); + + return 0; +} + +static int cli_channelstats_print_body(void *obj, void *arg, int flags) +{ + struct ast_sip_cli_context *context = arg; + const struct ast_channel_snapshot *snapshot = obj; + struct ast_channel *channel = ast_channel_get_by_name(snapshot->name); + struct ast_sip_channel_pvt *cpvt = channel ? ast_channel_tech_pvt(channel) : NULL; + struct chan_pjsip_pvt *pvt = cpvt ? cpvt->pvt : NULL; + struct ast_sip_session_media *media = pvt ? pvt->media[SIP_MEDIA_AUDIO] : NULL; + struct ast_rtp_codecs *codecs = media && media->rtp ? ast_rtp_instance_get_codecs(media->rtp) : NULL; + struct ast_format *format = codecs ? ast_rtp_codecs_get_payload_format(codecs, 0) : NULL; + struct ast_rtp_instance_stats stats; + char *print_name = NULL; + char *print_time = alloca(32); + + ast_assert(context->output_buffer != NULL); + + if (!media || !media->rtp) { + ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->name); + ao2_cleanup(channel); + return -1; + } + + print_name = ast_strdupa(snapshot->name); + /* Skip the PJSIP/. We know what channel type it is and we need the space. */ + print_name += 6; + + ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->creationtime.tv_sec, print_time, 32); + + if (ast_rtp_instance_get_stats(media->rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) { + ast_str_append(&context->output_buffer, 0, "%s direct media\n", snapshot->name); + } else { + ast_str_append(&context->output_buffer, 0, + " %8.8s %-18.18s %-8.8s %-6.6s %6u%s %6u%s %3u %7.3f %6u%s %6u%s %3u %7.3f %7.3f\n", + snapshot->bridgeid, + print_name, + print_time, + format ? ast_format_get_name(format) : "", + stats.rxcount > 100000 ? stats.rxcount / 1000 : stats.rxcount, + stats.rxcount > 100000 ? "K": " ", + stats.rxploss > 100000 ? stats.rxploss / 1000 : stats.rxploss, + stats.rxploss > 100000 ? "K": " ", + stats.rxcount ? (stats.rxploss * 100) / stats.rxcount : 0, + MIN(stats.rxjitter, 999.999), + stats.txcount > 100000 ? stats.txcount / 1000 : stats.txcount, + stats.txcount > 100000 ? "K": " ", + stats.txploss > 100000 ? stats.txploss / 1000 : stats.txploss, + stats.txploss > 100000 ? "K": " ", + stats.txcount ? (stats.txploss * 100) / stats.txcount : 0, + MIN(stats.txjitter, 999.999), + MIN(stats.normdevrtt, 999.999) + ); + } + + ao2_cleanup(format); + ao2_cleanup(channel); + + return 0; +} + +static struct ast_cli_entry cli_commands[] = { + AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Channels", + .command = "pjsip list channels", + .usage = "Usage: pjsip list channels [ like <pattern> ]\n" + " List the active PJSIP channels\n" + " Optional regular expression pattern is used to filter the list.\n"), + AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channels", + .command = "pjsip show channels", + .usage = "Usage: pjsip show channels [ like <pattern> ]\n" + " List(detailed) the active PJSIP channels\n" + " Optional regular expression pattern is used to filter the list.\n"), + AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channel", + .command = "pjsip show channel", + .usage = "Usage: pjsip show channel\n" + " List(detailed) the active PJSIP channel\n"), + + AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channel Stats", + .command = "pjsip show channelstats", + .usage = "Usage: pjsip show channelstats [ like <pattern> ]\n" + " List(detailed) the active PJSIP channel stats\n" + " Optional regular expression pattern is used to filter the list.\n"), +}; + +struct ast_sip_cli_formatter_entry *channelstats_formatter; +struct ast_sip_cli_formatter_entry *channel_formatter; + +int pjsip_channel_cli_register(void) +{ + channel_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL); + if (!channel_formatter) { + ast_log(LOG_ERROR, "Unable to allocate memory for channel_formatter\n"); + return -1; + } + channel_formatter->name = "channel"; + channel_formatter->print_header = cli_channel_print_header; + channel_formatter->print_body = cli_channel_print_body; + channel_formatter->get_container = cli_channel_get_container; + channel_formatter->iterate = cli_channel_iterate; + channel_formatter->retrieve_by_id = cli_channel_retrieve_by_id; + channel_formatter->get_id = cli_channel_get_id; + + channelstats_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL); + if (!channelstats_formatter) { + ao2_ref(channel_formatter, -1); + ast_log(LOG_ERROR, "Unable to allocate memory for channelstats_formatter\n"); + return -1; + } + channelstats_formatter->name = "channelstat"; + channelstats_formatter->print_header = cli_channelstats_print_header; + channelstats_formatter->print_body = cli_channelstats_print_body; + channelstats_formatter->get_container = cli_channelstats_get_container; + channelstats_formatter->iterate = cli_channelstats_iterate; + channelstats_formatter->retrieve_by_id = cli_channel_retrieve_by_id; + channelstats_formatter->get_id = cli_channel_get_id; + + ast_sip_register_cli_formatter(channel_formatter); + ast_sip_register_cli_formatter(channelstats_formatter); + ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands)); + + return 0; +} + +void pjsip_channel_cli_unregister(void) +{ + ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands)); + ast_sip_unregister_cli_formatter(channel_formatter); + ast_sip_unregister_cli_formatter(channelstats_formatter); +} diff --git a/channels/pjsip/include/cli_functions.h b/channels/pjsip/include/cli_functions.h new file mode 100644 index 0000000000..87200774ed --- /dev/null +++ b/channels/pjsip/include/cli_functions.h @@ -0,0 +1,45 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2016, Fairview 5 Engineering, LLC. + * + * 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 + * + * \author \verbatim George Joseph <george.joseph@fairview5.com> \endverbatim + * + * \brief PJSIP CLI functions header file + */ + +#ifndef _PJSIP_CLI_FUNCTIONS +#define _PJSIP_CLI_FUNCTIONS + + +/*! + * \brief Registers the channel cli commands + * \since 13.9.0 + * + * \retval 0 on success + * \retval -1 on failure + */ +int pjsip_channel_cli_register(void); + +/*! + * \brief Unregisters the channel cli commands + * \since 13.9.0 + */ +void pjsip_channel_cli_unregister(void); + + +#endif /* _PJSIP_CLI_FUNCTIONS */ diff --git a/res/res_pjsip/pjsip_cli.c b/res/res_pjsip/pjsip_cli.c index bbd0ac4b87..e6433f4358 100644 --- a/res/res_pjsip/pjsip_cli.c +++ b/res/res_pjsip/pjsip_cli.c @@ -197,7 +197,7 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_ ast_str_append(&context.output_buffer, 0, "\n"); formatter_entry->print_header(NULL, &context, 0); ast_str_append(&context.output_buffer, 0, - " =========================================================================================\n\n"); + "==========================================================================================\n\n"); if (is_container || cmd == CLI_GENERATE) { container = formatter_entry->get_container(regex); diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 371e4318ba..b497f02a27 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -1432,235 +1432,6 @@ static struct ao2_container *cli_endpoint_get_container(const char *regex) return s_container; } -static int cli_channel_populate_container(void *obj, void *arg, int flags) -{ - struct ast_channel_snapshot *snapshot = obj; - - ao2_link(arg, snapshot); - - return 0; -} - -static int cli_channel_iterate(void *container, ao2_callback_fn callback, void *args) -{ - const struct ast_sip_endpoint *endpoint = container; - - ast_sip_for_each_channel(endpoint, callback, args); - - return 0; -} - -static int cli_channel_sort(const void *obj, const void *arg, int flags) -{ - const struct ast_channel_snapshot *left_obj = obj; - const struct ast_channel_snapshot *right_obj = arg; - const char *right_key = arg; - int cmp; - - switch (flags & OBJ_SEARCH_MASK) { - case OBJ_SEARCH_OBJECT: - right_key = right_obj->name; - /* Fall through */ - case OBJ_SEARCH_KEY: - cmp = strcmp(left_obj->name, right_key); - break; - case OBJ_SEARCH_PARTIAL_KEY: - cmp = strncmp(left_obj->name, right_key, strlen(right_key)); - break; - default: - cmp = 0; - break; - } - - return cmp; -} - -static int cli_channel_compare(void *obj, void *arg, int flags) -{ - const struct ast_channel_snapshot *left_obj = obj; - const struct ast_channel_snapshot *right_obj = arg; - const char *right_key = arg; - int cmp = 0; - - switch (flags & OBJ_SEARCH_MASK) { - case OBJ_SEARCH_OBJECT: - right_key = right_obj->name; - /* Fall through */ - case OBJ_SEARCH_KEY: - if (strcmp(left_obj->name, right_key) == 0) { - cmp = CMP_MATCH | CMP_STOP; - } - break; - case OBJ_SEARCH_PARTIAL_KEY: - if (strncmp(left_obj->name, right_key, strlen(right_key)) == 0) { - cmp = CMP_MATCH; - } - break; - default: - cmp = 0; - break; - } - - return cmp; -} - -static int cli_channel_hash(const void *obj, int flags) -{ - const struct ast_channel_snapshot *snapshot = obj; - - if (flags & OBJ_SEARCH_OBJECT) { - return ast_str_hash(snapshot->name); - } else if (flags & OBJ_SEARCH_KEY) { - return ast_str_hash(obj); - } - - return -1; -} - -static int cli_endpoint_gather_channels(void *obj, void *arg, int flags) -{ - struct ast_sip_endpoint *endpoint = obj; - struct ao2_container *channels = arg; - - ast_sip_for_each_channel(endpoint, cli_channel_populate_container, channels); - - return 0; -} - -static int cli_filter_channels(void *obj, void *arg, int flags) -{ - struct ast_channel_snapshot *channel = obj; - regex_t *regexbuf = arg; - - if (!regexec(regexbuf, channel->name, 0, NULL, 0) - || !regexec(regexbuf, channel->appl, 0, NULL, 0)) { - return 0; - } - - return CMP_MATCH; -} - -static struct ao2_container *cli_channel_get_container(const char *regex) -{ - RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup); - struct ao2_container *child_container; - regex_t regexbuf; - - parent_container = cli_endpoint_get_container(""); - if (!parent_container) { - return NULL; - } - child_container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, 17, - cli_channel_hash, cli_channel_sort, cli_channel_compare); - if (!child_container) { - return NULL; - } - - ao2_callback(parent_container, OBJ_NODATA, cli_endpoint_gather_channels, child_container); - - if (!ast_strlen_zero(regex)) { - if (regcomp(®exbuf, regex, REG_EXTENDED | REG_NOSUB)) { - ao2_ref(child_container, -1); - return NULL; - } - ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_channels, ®exbuf); - regfree(®exbuf); - } - - return child_container; -} - -static const char *cli_channel_get_id(const void *obj) -{ - const struct ast_channel_snapshot *snapshot = obj; - - return snapshot->name; -} - -static void *cli_channel_retrieve_by_id(const char *id) -{ - RAII_VAR(struct ao2_container *, container, cli_channel_get_container(""), ao2_cleanup); - - return ao2_find(container, id, OBJ_KEY | OBJ_NOLOCK); -} - -static int cli_channel_print_header(void *obj, void *arg, int flags) -{ - struct ast_sip_cli_context *context = arg; - int indent = CLI_INDENT_TO_SPACES(context->indent_level); - int filler = CLI_LAST_TABSTOP - indent - 13; - - ast_assert(context->output_buffer != NULL); - - ast_str_append(&context->output_buffer, 0, - "%*s: <ChannelId%*.*s> <State.....> <Time(sec)>\n", - indent, "Channel", filler, filler, CLI_HEADER_FILLER); - if (context->recurse) { - context->indent_level++; - indent = CLI_INDENT_TO_SPACES(context->indent_level); - filler = CLI_LAST_TABSTOP - indent - 38; - ast_str_append(&context->output_buffer, 0, - "%*s: <DialedExten%*.*s> CLCID: <ConnectedLineCID.......>\n", - indent, "Exten", filler, filler, CLI_HEADER_FILLER); - context->indent_level--; - } - - return 0; -} - -static int cli_channel_print_body(void *obj, void *arg, int flags) -{ - const struct ast_channel_snapshot *snapshot = obj; - struct ast_sip_cli_context *context = arg; - struct timeval current_time; - char *print_name = NULL; - int print_name_len; - int indent; - int flexwidth; - - ast_assert(context->output_buffer != NULL); - - gettimeofday(¤t_time, NULL); - - print_name_len = strlen(snapshot->name) + strlen(snapshot->appl) + 2; - if (!(print_name = alloca(print_name_len))) { - return -1; - } - - snprintf(print_name, print_name_len, "%s/%s", snapshot->name, snapshot->appl); - - indent = CLI_INDENT_TO_SPACES(context->indent_level); - flexwidth = CLI_LAST_TABSTOP - indent; - - ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %-12.12s %11ld\n", - CLI_INDENT_TO_SPACES(context->indent_level), "Channel", - flexwidth, flexwidth, - print_name, - ast_state2str(snapshot->state), - current_time.tv_sec - snapshot->creationtime.tv_sec); - - if (context->recurse) { - context->indent_level++; - indent = CLI_INDENT_TO_SPACES(context->indent_level); - flexwidth = CLI_LAST_TABSTOP - indent - 25; - - ast_str_append(&context->output_buffer, 0, - "%*s: %-*.*s CLCID: \"%s\" <%s>\n", - indent, "Exten", - flexwidth, flexwidth, - snapshot->exten, - snapshot->connected_name, - snapshot->connected_number - ); - context->indent_level--; - if (context->indent_level == 0) { - ast_str_append(&context->output_buffer, 0, "\n"); - } - } - - return 0; -} - static int cli_endpoint_iterate(void *obj, ao2_callback_fn callback, void *args) { ao2_callback(obj, OBJ_NODATA, callback, args); @@ -1779,21 +1550,6 @@ static int cli_endpoint_print_body(void *obj, void *arg, int flags) } static struct ast_cli_entry cli_commands[] = { - AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Channels", - .command = "pjsip list channels", - .usage = "Usage: pjsip list channels [ like <pattern> ]\n" - " List the active PJSIP channels\n" - " Optional regular expression pattern is used to filter the list.\n"), - AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channels", - .command = "pjsip show channels", - .usage = "Usage: pjsip show channels [ like <pattern> ]\n" - " List(detailed) the active PJSIP channels\n" - " Optional regular expression pattern is used to filter the list.\n"), - AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channel", - .command = "pjsip show channel", - .usage = "Usage: pjsip show channel\n" - " List(detailed) the active PJSIP channel\n"), - AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Endpoints", .command = "pjsip list endpoints", .usage = "Usage: pjsip list endpoints [ like <pattern> ]\n" @@ -1987,21 +1743,6 @@ int ast_res_pjsip_initialize_configuration(void) return -1; } - channel_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL); - if (!channel_formatter) { - ast_log(LOG_ERROR, "Unable to allocate memory for channel_formatter\n"); - ast_sorcery_unref(sip_sorcery); - sip_sorcery = NULL; - return -1; - } - channel_formatter->name = "channel"; - channel_formatter->print_header = cli_channel_print_header; - channel_formatter->print_body = cli_channel_print_body; - channel_formatter->get_container = cli_channel_get_container; - channel_formatter->iterate = cli_channel_iterate; - channel_formatter->retrieve_by_id = cli_channel_retrieve_by_id; - channel_formatter->get_id = cli_channel_get_id; - endpoint_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL); if (!endpoint_formatter) { ast_log(LOG_ERROR, "Unable to allocate memory for endpoint_formatter\n"); @@ -2017,7 +1758,6 @@ int ast_res_pjsip_initialize_configuration(void) endpoint_formatter->retrieve_by_id = cli_endpoint_retrieve_by_id; endpoint_formatter->get_id = ast_sorcery_object_get_id; - ast_sip_register_cli_formatter(channel_formatter); ast_sip_register_cli_formatter(endpoint_formatter); ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands)); @@ -2044,7 +1784,6 @@ void ast_res_pjsip_destroy_configuration(void) ast_manager_unregister(AMI_SHOW_ENDPOINTS); ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands)); ast_sip_unregister_cli_formatter(endpoint_formatter); - ast_sip_unregister_cli_formatter(channel_formatter); ast_sip_destroy_cli(); ao2_cleanup(persistent_endpoints); } -- GitLab