chan_pjsip: Changed to continued after invalid media for pjsip show channelstats
[asterisk/asterisk.git] / channels / pjsip / cli_commands.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2016, Fairview 5 Engineering, LLC
5  *
6  * See http://www.asterisk.org for more information about
7  * the Asterisk project. Please do not directly contact
8  * any of the maintainers of this project for assistance;
9  * the project provides a web site, mailing lists and IRC
10  * channels for your use.
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License Version 2. See the LICENSE file
14  * at the top of the source tree.
15  */
16
17 /*!
18  * \file
19  *
20  * \author \verbatim George Joseph <george.joseph@fairview5.com> \endverbatim
21  *
22  * \ingroup functions
23  *
24  * \brief PJSIP channel CLI functions
25  */
26
27 #include "asterisk.h"
28
29 #include <pjsip.h>
30 #include <pjlib.h>
31 #include <pjsip_ua.h>
32
33 #include "asterisk/astobj2.h"
34 #include "asterisk/channel.h"
35 #include "asterisk/format.h"
36 #include "asterisk/res_pjsip.h"
37 #include "asterisk/res_pjsip_session.h"
38 #include "asterisk/res_pjsip_cli.h"
39 #include "asterisk/stasis.h"
40 #include "asterisk/time.h"
41 #include "include/chan_pjsip.h"
42 #include "include/cli_functions.h"
43
44
45 static int cli_channel_iterate(void *endpoint, ao2_callback_fn callback, void *arg)
46 {
47         return ast_sip_for_each_channel(endpoint, callback, arg);
48 }
49
50 static int cli_channelstats_iterate(void *endpoint, ao2_callback_fn callback, void *arg)
51 {
52         return ast_sip_for_each_channel(endpoint, callback, arg);
53 }
54
55 static int cli_channel_sort(const void *obj, const void *arg, int flags)
56 {
57         const struct ast_channel_snapshot *left_obj = obj;
58         const struct ast_channel_snapshot *right_obj = arg;
59         const char *right_key = arg;
60         int cmp;
61
62         switch (flags & OBJ_SEARCH_MASK) {
63         case OBJ_SEARCH_OBJECT:
64                 right_key = right_obj->base->name;
65                 /* Fall through */
66         case OBJ_SEARCH_KEY:
67                 cmp = strcmp(left_obj->base->name, right_key);
68                 break;
69         case OBJ_SEARCH_PARTIAL_KEY:
70                 cmp = strncmp(left_obj->base->name, right_key, strlen(right_key));
71                 break;
72         default:
73                 cmp = 0;
74                 break;
75         }
76
77         return cmp;
78 }
79
80 static int cli_channelstats_sort(const void *obj, const void *arg, int flags)
81 {
82         const struct ast_channel_snapshot *left_obj = obj;
83         const struct ast_channel_snapshot *right_obj = arg;
84         const char *right_key = arg;
85         int cmp;
86
87         switch (flags & OBJ_SEARCH_MASK) {
88         case OBJ_SEARCH_OBJECT:
89                 cmp = strcmp(left_obj->bridge->id, right_obj->bridge->id);
90                 if (cmp) {
91                         return cmp;
92                 }
93                 right_key = right_obj->base->name;
94                 /* Fall through */
95         case OBJ_SEARCH_KEY:
96                 cmp = strcmp(left_obj->base->name, right_key);
97                 break;
98         case OBJ_SEARCH_PARTIAL_KEY:
99                 cmp = strncmp(left_obj->base->name, right_key, strlen(right_key));
100                 break;
101         default:
102                 cmp = 0;
103                 break;
104         }
105
106         return cmp;
107 }
108
109 static int cli_channel_compare(void *obj, void *arg, int flags)
110 {
111         const struct ast_channel_snapshot *left_obj = obj;
112         const struct ast_channel_snapshot *right_obj = arg;
113         const char *right_key = arg;
114         int cmp = 0;
115
116         switch (flags & OBJ_SEARCH_MASK) {
117         case OBJ_SEARCH_OBJECT:
118                 right_key = right_obj->base->name;
119                 /* Fall through */
120         case OBJ_SEARCH_KEY:
121                 if (strcmp(left_obj->base->name, right_key) == 0) {
122                         cmp = CMP_MATCH | CMP_STOP;
123                 }
124                 break;
125         case OBJ_SEARCH_PARTIAL_KEY:
126                 if (strncmp(left_obj->base->name, right_key, strlen(right_key)) == 0) {
127                         cmp = CMP_MATCH;
128                 }
129                 break;
130         default:
131                 cmp = 0;
132                 break;
133         }
134
135         return cmp;
136 }
137
138 static int cli_channelstats_compare(void *obj, void *arg, int flags)
139 {
140         const struct ast_channel_snapshot *left_obj = obj;
141         const struct ast_channel_snapshot *right_obj = arg;
142         const char *right_key = arg;
143         int cmp = 0;
144
145         switch (flags & OBJ_SEARCH_MASK) {
146         case OBJ_SEARCH_OBJECT:
147                 if (strcmp(left_obj->bridge->id, right_obj->bridge->id) == 0
148                         && strcmp(left_obj->base->name, right_obj->base->name) == 0) {
149                         return CMP_MATCH | CMP_STOP;
150                 }
151                 break;
152         case OBJ_SEARCH_KEY:
153                 if (strcmp(left_obj->base->name, right_key) == 0) {
154                         cmp = CMP_MATCH | CMP_STOP;
155                 }
156                 break;
157         case OBJ_SEARCH_PARTIAL_KEY:
158                 if (strncmp(left_obj->base->name, right_key, strlen(right_key)) == 0) {
159                         cmp = CMP_MATCH;
160                 }
161                 break;
162         default:
163                 cmp = 0;
164                 break;
165         }
166
167         return cmp;
168 }
169
170 static int cli_message_to_snapshot(void *obj, void *arg, int flags)
171 {
172         struct ast_channel_snapshot *snapshot = obj;
173         struct ao2_container *snapshots = arg;
174
175         if (!strcmp(snapshot->base->type, "PJSIP")) {
176                 ao2_link(snapshots, snapshot);
177                 return CMP_MATCH;
178         }
179
180         return 0;
181 }
182
183 static int cli_filter_channels(void *obj, void *arg, int flags)
184 {
185         struct ast_channel_snapshot *channel = obj;
186         regex_t *regexbuf = arg;
187
188         if (!regexec(regexbuf, channel->base->name, 0, NULL, 0)
189                 || !regexec(regexbuf, channel->dialplan->appl, 0, NULL, 0)) {
190                 return 0;
191         }
192
193         return CMP_MATCH;
194 }
195
196 static struct ao2_container *get_container(const char *regex, ao2_sort_fn sort_fn, ao2_callback_fn compare_fn)
197 {
198         struct ao2_container *child_container;
199         regex_t regexbuf;
200         RAII_VAR(struct ao2_container *, parent_container, ast_channel_cache_by_name(), ao2_cleanup);
201
202         if (!parent_container) {
203                 return NULL;
204         }
205
206         child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, sort_fn, compare_fn);
207         if (!child_container) {
208                 return NULL;
209         }
210
211         ao2_callback(parent_container, OBJ_MULTIPLE | OBJ_NODATA, cli_message_to_snapshot, child_container);
212
213         if (!ast_strlen_zero(regex)) {
214                 if (regcomp(&regexbuf, regex, REG_EXTENDED | REG_NOSUB)) {
215                         ao2_ref(child_container, -1);
216                         return NULL;
217                 }
218                 ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_channels, &regexbuf);
219                 regfree(&regexbuf);
220         }
221
222         return child_container;
223 }
224
225 static struct ao2_container *cli_channel_get_container(const char *regex)
226 {
227         return get_container(regex, cli_channel_sort, cli_channel_compare);
228 }
229
230 static struct ao2_container *cli_channelstats_get_container(const char *regex)
231 {
232         return get_container(regex, cli_channelstats_sort, cli_channelstats_compare);
233 }
234
235 static const char *cli_channel_get_id(const void *obj)
236 {
237         const struct ast_channel_snapshot *snapshot = obj;
238
239         return snapshot->base->name;
240 }
241
242 static void *cli_channel_retrieve_by_id(const char *id)
243 {
244         return ast_channel_snapshot_get_latest_by_name(id);
245 }
246
247 static int cli_channel_print_header(void *obj, void *arg, int flags)
248 {
249         struct ast_sip_cli_context *context = arg;
250         int indent = CLI_INDENT_TO_SPACES(context->indent_level);
251         int filler = CLI_LAST_TABSTOP - indent - 13;
252
253         ast_assert(context->output_buffer != NULL);
254
255         ast_str_append(&context->output_buffer, 0,
256                 "%*s:  <ChannelId%*.*s>  <State.....>  <Time.....>\n",
257                 indent, "Channel", filler, filler, CLI_HEADER_FILLER);
258         if (context->recurse) {
259                 context->indent_level++;
260                 indent = CLI_INDENT_TO_SPACES(context->indent_level);
261                 filler = CLI_LAST_TABSTOP - indent - 38;
262                 ast_str_append(&context->output_buffer, 0,
263                         "%*s: <DialedExten%*.*s>  CLCID: <ConnectedLineCID.......>\n",
264                         indent, "Exten", filler, filler, CLI_HEADER_FILLER);
265                 context->indent_level--;
266         }
267
268         return 0;
269 }
270
271 static int cli_channel_print_body(void *obj, void *arg, int flags)
272 {
273         const struct ast_channel_snapshot *snapshot = obj;
274         struct ast_sip_cli_context *context = arg;
275         char *print_name = NULL;
276         int print_name_len;
277         int indent;
278         int flexwidth;
279         char *print_time = alloca(32);
280
281         ast_assert(context->output_buffer != NULL);
282
283         print_name_len = strlen(snapshot->base->name) + strlen(snapshot->dialplan->appl) + 2;
284         print_name = alloca(print_name_len);
285
286         /* Append the application */
287         snprintf(print_name, print_name_len, "%s/%s", snapshot->base->name, snapshot->dialplan->appl);
288
289         indent = CLI_INDENT_TO_SPACES(context->indent_level);
290         flexwidth = CLI_LAST_TABSTOP - indent;
291
292         ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->base->creationtime.tv_sec, print_time, 32);
293
294         ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %-12.12s  %-11.11s\n",
295                 CLI_INDENT_TO_SPACES(context->indent_level), "Channel",
296                 flexwidth, flexwidth,
297                 print_name,
298                 ast_state2str(snapshot->state),
299                 print_time);
300
301         if (context->recurse) {
302                 context->indent_level++;
303                 indent = CLI_INDENT_TO_SPACES(context->indent_level);
304                 flexwidth = CLI_LAST_TABSTOP - indent - 25;
305
306                 ast_str_append(&context->output_buffer, 0,
307                         "%*s: %-*.*s  CLCID: \"%s\" <%s>\n",
308                         indent, "Exten",
309                         flexwidth, flexwidth,
310                         snapshot->dialplan->exten,
311                         snapshot->connected->name,
312                         snapshot->connected->number
313                         );
314                 context->indent_level--;
315                 if (context->indent_level == 0) {
316                         ast_str_append(&context->output_buffer, 0, "\n");
317                 }
318         }
319
320         return 0;
321 }
322
323 static int cli_channelstats_print_header(void *obj, void *arg, int flags)
324 {
325         struct ast_sip_cli_context *context = arg;
326
327         ast_assert(context->output_buffer != NULL);
328
329         ast_str_append(&context->output_buffer, 0,
330                 "                                             ...........Receive......... .........Transmit..........\n"
331                 " BridgeId ChannelId ........ UpTime.. Codec.   Count    Lost Pct  Jitter   Count    Lost Pct  Jitter RTT....\n"
332                 " =================");
333
334         return 0;
335 }
336
337 static int cli_channelstats_print_body(void *obj, void *arg, int flags)
338 {
339         struct ast_sip_cli_context *context = arg;
340         const struct ast_channel_snapshot *snapshot = obj;
341         struct ast_channel *channel = ast_channel_get_by_name(snapshot->base->name);
342         struct ast_sip_channel_pvt *cpvt = channel ? ast_channel_tech_pvt(channel) : NULL;
343         struct ast_sip_session *session;
344         struct ast_sip_session_media *media;
345         struct ast_rtp_instance *rtp;
346         struct ast_rtp_instance_stats stats;
347         char *print_name = NULL;
348         char *print_time = alloca(32);
349         char codec_in_use[7];
350
351         ast_assert(context->output_buffer != NULL);
352
353         if (!channel) {
354                 ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->base->name);
355                 return 0;
356         }
357
358         ast_channel_lock(channel);
359
360         session = cpvt->session;
361         if (!session) {
362                 ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->base->name);
363                 ast_channel_unlock(channel);
364                 ao2_cleanup(channel);
365                 return 0;
366         }
367
368         media = session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
369         if (!media || !media->rtp) {
370                 ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->base->name);
371                 ast_channel_unlock(channel);
372                 ao2_cleanup(channel);
373                 return 0;
374         }
375
376         rtp = ao2_bump(media->rtp);
377
378         codec_in_use[0] = '\0';
379
380         if (ast_channel_rawreadformat(channel)) {
381                 ast_copy_string(codec_in_use, ast_format_get_name(ast_channel_rawreadformat(channel)), sizeof(codec_in_use));
382         }
383
384         ast_channel_unlock(channel);
385
386         print_name = ast_strdupa(snapshot->base->name);
387         /* Skip the PJSIP/.  We know what channel type it is and we need the space. */
388         print_name += 6;
389
390         ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->base->creationtime.tv_sec, print_time, 32);
391
392         if (ast_rtp_instance_get_stats(rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) {
393                 ast_str_append(&context->output_buffer, 0, "%s direct media\n", snapshot->base->name);
394         } else {
395                 ast_str_append(&context->output_buffer, 0,
396                         " %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",
397                         snapshot->bridge->id,
398                         print_name,
399                         print_time,
400                         codec_in_use,
401                         stats.rxcount > 100000 ? stats.rxcount / 1000 : stats.rxcount,
402                         stats.rxcount > 100000 ? "K": " ",
403                         stats.rxploss > 100000 ? stats.rxploss / 1000 : stats.rxploss,
404                         stats.rxploss > 100000 ? "K": " ",
405                         stats.rxcount ? (stats.rxploss * 100) / stats.rxcount : 0,
406                         MIN(stats.rxjitter, 999.999),
407                         stats.txcount > 100000 ? stats.txcount / 1000 : stats.txcount,
408                         stats.txcount > 100000 ? "K": " ",
409                         stats.txploss > 100000 ? stats.txploss / 1000 : stats.txploss,
410                         stats.txploss > 100000 ? "K": " ",
411                         stats.txcount ? (stats.txploss * 100) / stats.txcount : 0,
412                         MIN(stats.txjitter, 999.999),
413                         MIN(stats.normdevrtt, 999.999)
414                 );
415         }
416
417         ao2_cleanup(rtp);
418         ao2_cleanup(channel);
419
420         return 0;
421 }
422
423 static struct ast_cli_entry cli_commands[] = {
424         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Channels",
425                 .command = "pjsip list channels",
426                 .usage = "Usage: pjsip list channels [ like <pattern> ]\n"
427                                 "       List the active PJSIP channels\n"
428                                 "       Optional regular expression pattern is used to filter the list.\n"),
429         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channels",
430                 .command = "pjsip show channels",
431                 .usage = "Usage: pjsip show channels [ like <pattern> ]\n"
432                                 "       List(detailed) the active PJSIP channels\n"
433                                 "       Optional regular expression pattern is used to filter the list.\n"),
434         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channel",
435                 .command = "pjsip show channel",
436                 .usage = "Usage: pjsip show channel\n"
437                                  "       List(detailed) the active PJSIP channel\n"),
438
439         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channel Stats",
440                 .command = "pjsip show channelstats",
441                 .usage = "Usage: pjsip show channelstats [ like <pattern> ]\n"
442                                 "       List(detailed) the active PJSIP channel stats\n"
443                                 "       Optional regular expression pattern is used to filter the list.\n"),
444 };
445
446 struct ast_sip_cli_formatter_entry *channelstats_formatter;
447 struct ast_sip_cli_formatter_entry *channel_formatter;
448
449 int pjsip_channel_cli_register(void)
450 {
451         channel_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
452         if (!channel_formatter) {
453                 ast_log(LOG_ERROR, "Unable to allocate memory for channel_formatter\n");
454                 return -1;
455         }
456         channel_formatter->name = "channel";
457         channel_formatter->print_header = cli_channel_print_header;
458         channel_formatter->print_body = cli_channel_print_body;
459         channel_formatter->get_container = cli_channel_get_container;
460         channel_formatter->iterate = cli_channel_iterate;
461         channel_formatter->retrieve_by_id = cli_channel_retrieve_by_id;
462         channel_formatter->get_id = cli_channel_get_id;
463
464         channelstats_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
465         if (!channelstats_formatter) {
466                 ao2_ref(channel_formatter, -1);
467                 ast_log(LOG_ERROR, "Unable to allocate memory for channelstats_formatter\n");
468                 return -1;
469         }
470         channelstats_formatter->name = "channelstat";
471         channelstats_formatter->print_header = cli_channelstats_print_header;
472         channelstats_formatter->print_body = cli_channelstats_print_body;
473         channelstats_formatter->get_container = cli_channelstats_get_container;
474         channelstats_formatter->iterate = cli_channelstats_iterate;
475         channelstats_formatter->retrieve_by_id = cli_channel_retrieve_by_id;
476         channelstats_formatter->get_id = cli_channel_get_id;
477
478         ast_sip_register_cli_formatter(channel_formatter);
479         ast_sip_register_cli_formatter(channelstats_formatter);
480         ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
481
482         return 0;
483 }
484
485 void pjsip_channel_cli_unregister(void)
486 {
487         ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
488         ast_sip_unregister_cli_formatter(channel_formatter);
489         ast_sip_unregister_cli_formatter(channelstats_formatter);
490 }