diff options
Diffstat (limited to 'channels/pjsip/cli_commands.c')
-rw-r--r-- | channels/pjsip/cli_commands.c | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/channels/pjsip/cli_commands.c b/channels/pjsip/cli_commands.c new file mode 100644 index 000000000..8d99379ff --- /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); +} |