cli_commands.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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. * \file
  18. *
  19. * \author \verbatim George Joseph <george.joseph@fairview5.com> \endverbatim
  20. *
  21. * \ingroup functions
  22. *
  23. * \brief PJSIP channel CLI functions
  24. */
  25. #include "asterisk.h"
  26. ASTERISK_REGISTER_FILE()
  27. #include <pjsip.h>
  28. #include <pjlib.h>
  29. #include <pjsip_ua.h>
  30. #include "asterisk/astobj2.h"
  31. #include "asterisk/channel.h"
  32. #include "asterisk/format.h"
  33. #include "asterisk/res_pjsip.h"
  34. #include "asterisk/res_pjsip_session.h"
  35. #include "asterisk/res_pjsip_cli.h"
  36. #include "asterisk/stasis.h"
  37. #include "asterisk/time.h"
  38. #include "include/chan_pjsip.h"
  39. #include "include/cli_functions.h"
  40. static int cli_channel_iterate(void *endpoint, ao2_callback_fn callback, void *arg)
  41. {
  42. return ast_sip_for_each_channel(endpoint, callback, arg);
  43. }
  44. static int cli_channelstats_iterate(void *endpoint, ao2_callback_fn callback, void *arg)
  45. {
  46. return ast_sip_for_each_channel(endpoint, callback, arg);
  47. }
  48. static int cli_channel_sort(const void *obj, const void *arg, int flags)
  49. {
  50. const struct ast_channel_snapshot *left_obj = obj;
  51. const struct ast_channel_snapshot *right_obj = arg;
  52. const char *right_key = arg;
  53. int cmp;
  54. switch (flags & OBJ_SEARCH_MASK) {
  55. case OBJ_SEARCH_OBJECT:
  56. right_key = right_obj->name;
  57. /* Fall through */
  58. case OBJ_SEARCH_KEY:
  59. cmp = strcmp(left_obj->name, right_key);
  60. break;
  61. case OBJ_SEARCH_PARTIAL_KEY:
  62. cmp = strncmp(left_obj->name, right_key, strlen(right_key));
  63. break;
  64. default:
  65. cmp = 0;
  66. break;
  67. }
  68. return cmp;
  69. }
  70. static int cli_channelstats_sort(const void *obj, const void *arg, int flags)
  71. {
  72. const struct ast_channel_snapshot *left_obj = obj;
  73. const struct ast_channel_snapshot *right_obj = arg;
  74. const char *right_key = arg;
  75. int cmp;
  76. switch (flags & OBJ_SEARCH_MASK) {
  77. case OBJ_SEARCH_OBJECT:
  78. cmp = strcmp(left_obj->bridgeid, right_obj->bridgeid);
  79. if (cmp) {
  80. return cmp;
  81. }
  82. right_key = right_obj->name;
  83. /* Fall through */
  84. case OBJ_SEARCH_KEY:
  85. cmp = strcmp(left_obj->name, right_key);
  86. break;
  87. case OBJ_SEARCH_PARTIAL_KEY:
  88. cmp = strncmp(left_obj->name, right_key, strlen(right_key));
  89. break;
  90. default:
  91. cmp = 0;
  92. break;
  93. }
  94. return cmp;
  95. }
  96. static int cli_channel_compare(void *obj, void *arg, int flags)
  97. {
  98. const struct ast_channel_snapshot *left_obj = obj;
  99. const struct ast_channel_snapshot *right_obj = arg;
  100. const char *right_key = arg;
  101. int cmp = 0;
  102. switch (flags & OBJ_SEARCH_MASK) {
  103. case OBJ_SEARCH_OBJECT:
  104. right_key = right_obj->name;
  105. /* Fall through */
  106. case OBJ_SEARCH_KEY:
  107. if (strcmp(left_obj->name, right_key) == 0) {
  108. cmp = CMP_MATCH | CMP_STOP;
  109. }
  110. break;
  111. case OBJ_SEARCH_PARTIAL_KEY:
  112. if (strncmp(left_obj->name, right_key, strlen(right_key)) == 0) {
  113. cmp = CMP_MATCH;
  114. }
  115. break;
  116. default:
  117. cmp = 0;
  118. break;
  119. }
  120. return cmp;
  121. }
  122. static int cli_channelstats_compare(void *obj, void *arg, int flags)
  123. {
  124. const struct ast_channel_snapshot *left_obj = obj;
  125. const struct ast_channel_snapshot *right_obj = arg;
  126. const char *right_key = arg;
  127. int cmp = 0;
  128. switch (flags & OBJ_SEARCH_MASK) {
  129. case OBJ_SEARCH_OBJECT:
  130. if (strcmp(left_obj->bridgeid, right_obj->bridgeid) == 0
  131. && strcmp(left_obj->name, right_obj->name) == 0) {
  132. return CMP_MATCH | CMP_STOP;
  133. }
  134. break;
  135. case OBJ_SEARCH_KEY:
  136. if (strcmp(left_obj->name, right_key) == 0) {
  137. cmp = CMP_MATCH | CMP_STOP;
  138. }
  139. break;
  140. case OBJ_SEARCH_PARTIAL_KEY:
  141. if (strncmp(left_obj->name, right_key, strlen(right_key)) == 0) {
  142. cmp = CMP_MATCH;
  143. }
  144. break;
  145. default:
  146. cmp = 0;
  147. break;
  148. }
  149. return cmp;
  150. }
  151. static int cli_message_to_snapshot(void *obj, void *arg, int flags)
  152. {
  153. struct stasis_message *message = obj;
  154. struct ao2_container *snapshots = arg;
  155. struct ast_channel_snapshot *snapshot = stasis_message_data(message);
  156. if (!strcmp(snapshot->type, "PJSIP")) {
  157. ao2_link(snapshots, snapshot);
  158. return CMP_MATCH;
  159. }
  160. return 0;
  161. }
  162. static int cli_filter_channels(void *obj, void *arg, int flags)
  163. {
  164. struct ast_channel_snapshot *channel = obj;
  165. regex_t *regexbuf = arg;
  166. if (!regexec(regexbuf, channel->name, 0, NULL, 0)
  167. || !regexec(regexbuf, channel->appl, 0, NULL, 0)) {
  168. return 0;
  169. }
  170. return CMP_MATCH;
  171. }
  172. static struct ao2_container *get_container(const char *regex, ao2_sort_fn sort_fn, ao2_callback_fn compare_fn)
  173. {
  174. struct ao2_container *child_container;
  175. regex_t regexbuf;
  176. RAII_VAR(struct ao2_container *, parent_container,
  177. stasis_cache_dump(ast_channel_cache_by_name(), ast_channel_snapshot_type()), ao2_cleanup);
  178. if (!parent_container) {
  179. return NULL;
  180. }
  181. child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, sort_fn, compare_fn);
  182. if (!child_container) {
  183. return NULL;
  184. }
  185. ao2_callback(parent_container, OBJ_MULTIPLE | OBJ_NODATA, cli_message_to_snapshot, child_container);
  186. if (!ast_strlen_zero(regex)) {
  187. if (regcomp(&regexbuf, regex, REG_EXTENDED | REG_NOSUB)) {
  188. ao2_ref(child_container, -1);
  189. return NULL;
  190. }
  191. ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_channels, &regexbuf);
  192. regfree(&regexbuf);
  193. }
  194. return child_container;
  195. }
  196. static struct ao2_container *cli_channel_get_container(const char *regex)
  197. {
  198. return get_container(regex, cli_channel_sort, cli_channel_compare);
  199. }
  200. static struct ao2_container *cli_channelstats_get_container(const char *regex)
  201. {
  202. return get_container(regex, cli_channelstats_sort, cli_channelstats_compare);
  203. }
  204. static const char *cli_channel_get_id(const void *obj)
  205. {
  206. const struct ast_channel_snapshot *snapshot = obj;
  207. return snapshot->name;
  208. }
  209. static void *cli_channel_retrieve_by_id(const char *id)
  210. {
  211. return ast_channel_snapshot_get_latest_by_name(id);
  212. }
  213. static int cli_channel_print_header(void *obj, void *arg, int flags)
  214. {
  215. struct ast_sip_cli_context *context = arg;
  216. int indent = CLI_INDENT_TO_SPACES(context->indent_level);
  217. int filler = CLI_LAST_TABSTOP - indent - 13;
  218. ast_assert(context->output_buffer != NULL);
  219. ast_str_append(&context->output_buffer, 0,
  220. "%*s: <ChannelId%*.*s> <State.....> <Time.....>\n",
  221. indent, "Channel", filler, filler, CLI_HEADER_FILLER);
  222. if (context->recurse) {
  223. context->indent_level++;
  224. indent = CLI_INDENT_TO_SPACES(context->indent_level);
  225. filler = CLI_LAST_TABSTOP - indent - 38;
  226. ast_str_append(&context->output_buffer, 0,
  227. "%*s: <DialedExten%*.*s> CLCID: <ConnectedLineCID.......>\n",
  228. indent, "Exten", filler, filler, CLI_HEADER_FILLER);
  229. context->indent_level--;
  230. }
  231. return 0;
  232. }
  233. static int cli_channel_print_body(void *obj, void *arg, int flags)
  234. {
  235. const struct ast_channel_snapshot *snapshot = obj;
  236. struct ast_sip_cli_context *context = arg;
  237. char *print_name = NULL;
  238. int print_name_len;
  239. int indent;
  240. int flexwidth;
  241. char *print_time = alloca(32);
  242. ast_assert(context->output_buffer != NULL);
  243. print_name_len = strlen(snapshot->name) + strlen(snapshot->appl) + 2;
  244. print_name = alloca(print_name_len);
  245. /* Append the application */
  246. snprintf(print_name, print_name_len, "%s/%s", snapshot->name, snapshot->appl);
  247. indent = CLI_INDENT_TO_SPACES(context->indent_level);
  248. flexwidth = CLI_LAST_TABSTOP - indent;
  249. ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->creationtime.tv_sec, print_time, 32);
  250. ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %-12.12s %-11.11s\n",
  251. CLI_INDENT_TO_SPACES(context->indent_level), "Channel",
  252. flexwidth, flexwidth,
  253. print_name,
  254. ast_state2str(snapshot->state),
  255. print_time);
  256. if (context->recurse) {
  257. context->indent_level++;
  258. indent = CLI_INDENT_TO_SPACES(context->indent_level);
  259. flexwidth = CLI_LAST_TABSTOP - indent - 25;
  260. ast_str_append(&context->output_buffer, 0,
  261. "%*s: %-*.*s CLCID: \"%s\" <%s>\n",
  262. indent, "Exten",
  263. flexwidth, flexwidth,
  264. snapshot->exten,
  265. snapshot->connected_name,
  266. snapshot->connected_number
  267. );
  268. context->indent_level--;
  269. if (context->indent_level == 0) {
  270. ast_str_append(&context->output_buffer, 0, "\n");
  271. }
  272. }
  273. return 0;
  274. }
  275. static int cli_channelstats_print_header(void *obj, void *arg, int flags)
  276. {
  277. struct ast_sip_cli_context *context = arg;
  278. ast_assert(context->output_buffer != NULL);
  279. ast_str_append(&context->output_buffer, 0,
  280. " ...........Receive......... .........Transmit..........\n"
  281. " BridgeId ChannelId ........ UpTime.. Codec. Count Lost Pct Jitter Count Lost Pct Jitter RTT....\n"
  282. " =================");
  283. return 0;
  284. }
  285. static int cli_channelstats_print_body(void *obj, void *arg, int flags)
  286. {
  287. struct ast_sip_cli_context *context = arg;
  288. const struct ast_channel_snapshot *snapshot = obj;
  289. struct ast_channel *channel = ast_channel_get_by_name(snapshot->name);
  290. struct ast_sip_channel_pvt *cpvt = channel ? ast_channel_tech_pvt(channel) : NULL;
  291. struct chan_pjsip_pvt *pvt = cpvt ? cpvt->pvt : NULL;
  292. struct ast_sip_session_media *media = pvt ? pvt->media[SIP_MEDIA_AUDIO] : NULL;
  293. struct ast_rtp_instance_stats stats;
  294. char *print_name = NULL;
  295. char *print_time = alloca(32);
  296. char codec_in_use[7];
  297. ast_assert(context->output_buffer != NULL);
  298. if (!media || !media->rtp) {
  299. ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->name);
  300. ao2_cleanup(channel);
  301. return -1;
  302. }
  303. codec_in_use[0] = '\0';
  304. if (channel) {
  305. ast_channel_lock(channel);
  306. if (ast_channel_rawreadformat(channel)) {
  307. ast_copy_string(codec_in_use, ast_format_get_name(ast_channel_rawreadformat(channel)), sizeof(codec_in_use));
  308. }
  309. ast_channel_unlock(channel);
  310. }
  311. print_name = ast_strdupa(snapshot->name);
  312. /* Skip the PJSIP/. We know what channel type it is and we need the space. */
  313. print_name += 6;
  314. ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->creationtime.tv_sec, print_time, 32);
  315. if (ast_rtp_instance_get_stats(media->rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) {
  316. ast_str_append(&context->output_buffer, 0, "%s direct media\n", snapshot->name);
  317. } else {
  318. ast_str_append(&context->output_buffer, 0,
  319. " %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",
  320. snapshot->bridgeid,
  321. print_name,
  322. print_time,
  323. codec_in_use,
  324. stats.rxcount > 100000 ? stats.rxcount / 1000 : stats.rxcount,
  325. stats.rxcount > 100000 ? "K": " ",
  326. stats.rxploss > 100000 ? stats.rxploss / 1000 : stats.rxploss,
  327. stats.rxploss > 100000 ? "K": " ",
  328. stats.rxcount ? (stats.rxploss * 100) / stats.rxcount : 0,
  329. MIN(stats.rxjitter, 999.999),
  330. stats.txcount > 100000 ? stats.txcount / 1000 : stats.txcount,
  331. stats.txcount > 100000 ? "K": " ",
  332. stats.txploss > 100000 ? stats.txploss / 1000 : stats.txploss,
  333. stats.txploss > 100000 ? "K": " ",
  334. stats.txcount ? (stats.txploss * 100) / stats.txcount : 0,
  335. MIN(stats.txjitter, 999.999),
  336. MIN(stats.normdevrtt, 999.999)
  337. );
  338. }
  339. ao2_cleanup(channel);
  340. return 0;
  341. }
  342. static struct ast_cli_entry cli_commands[] = {
  343. AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Channels",
  344. .command = "pjsip list channels",
  345. .usage = "Usage: pjsip list channels [ like <pattern> ]\n"
  346. " List the active PJSIP channels\n"
  347. " Optional regular expression pattern is used to filter the list.\n"),
  348. AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channels",
  349. .command = "pjsip show channels",
  350. .usage = "Usage: pjsip show channels [ like <pattern> ]\n"
  351. " List(detailed) the active PJSIP channels\n"
  352. " Optional regular expression pattern is used to filter the list.\n"),
  353. AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channel",
  354. .command = "pjsip show channel",
  355. .usage = "Usage: pjsip show channel\n"
  356. " List(detailed) the active PJSIP channel\n"),
  357. AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channel Stats",
  358. .command = "pjsip show channelstats",
  359. .usage = "Usage: pjsip show channelstats [ like <pattern> ]\n"
  360. " List(detailed) the active PJSIP channel stats\n"
  361. " Optional regular expression pattern is used to filter the list.\n"),
  362. };
  363. struct ast_sip_cli_formatter_entry *channelstats_formatter;
  364. struct ast_sip_cli_formatter_entry *channel_formatter;
  365. int pjsip_channel_cli_register(void)
  366. {
  367. channel_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
  368. if (!channel_formatter) {
  369. ast_log(LOG_ERROR, "Unable to allocate memory for channel_formatter\n");
  370. return -1;
  371. }
  372. channel_formatter->name = "channel";
  373. channel_formatter->print_header = cli_channel_print_header;
  374. channel_formatter->print_body = cli_channel_print_body;
  375. channel_formatter->get_container = cli_channel_get_container;
  376. channel_formatter->iterate = cli_channel_iterate;
  377. channel_formatter->retrieve_by_id = cli_channel_retrieve_by_id;
  378. channel_formatter->get_id = cli_channel_get_id;
  379. channelstats_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
  380. if (!channelstats_formatter) {
  381. ao2_ref(channel_formatter, -1);
  382. ast_log(LOG_ERROR, "Unable to allocate memory for channelstats_formatter\n");
  383. return -1;
  384. }
  385. channelstats_formatter->name = "channelstat";
  386. channelstats_formatter->print_header = cli_channelstats_print_header;
  387. channelstats_formatter->print_body = cli_channelstats_print_body;
  388. channelstats_formatter->get_container = cli_channelstats_get_container;
  389. channelstats_formatter->iterate = cli_channelstats_iterate;
  390. channelstats_formatter->retrieve_by_id = cli_channel_retrieve_by_id;
  391. channelstats_formatter->get_id = cli_channel_get_id;
  392. ast_sip_register_cli_formatter(channel_formatter);
  393. ast_sip_register_cli_formatter(channelstats_formatter);
  394. ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
  395. return 0;
  396. }
  397. void pjsip_channel_cli_unregister(void)
  398. {
  399. ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
  400. ast_sip_unregister_cli_formatter(channel_formatter);
  401. ast_sip_unregister_cli_formatter(channelstats_formatter);
  402. }