pjsip_cli.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2013, Fairview 5 Engineering, LLC
  5. *
  6. * George Joseph <george.joseph@fairview5.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. #include "asterisk.h"
  19. #include <pjsip.h>
  20. #include <pjsip_ua.h>
  21. #include "asterisk/res_pjsip.h"
  22. #include "include/res_pjsip_private.h"
  23. #include "asterisk/res_pjsip_cli.h"
  24. #include "asterisk/acl.h"
  25. #include "asterisk/cli.h"
  26. #include "asterisk/astobj2.h"
  27. #include "asterisk/hashtab.h"
  28. #include "asterisk/utils.h"
  29. #include "asterisk/sorcery.h"
  30. static struct ao2_container *formatter_registry;
  31. int ast_sip_cli_print_sorcery_objectset(void *obj, void *arg, int flags)
  32. {
  33. struct ast_sip_cli_context *context = arg;
  34. struct ast_variable *i;
  35. int max_name_width = 13;
  36. int max_value_width = 14;
  37. int width;
  38. char *separator;
  39. struct ast_variable *objset;
  40. if (!context->output_buffer) {
  41. return -1;
  42. }
  43. objset = ast_sorcery_objectset_create(ast_sip_get_sorcery(),obj);
  44. if (!objset) {
  45. return -1;
  46. }
  47. for (i = objset; i; i = i->next) {
  48. if (i->name) {
  49. width = strlen(i->name);
  50. max_name_width = width > max_name_width ? width : max_name_width;
  51. }
  52. if (i->value) {
  53. width = strlen(i->value);
  54. max_value_width = width > max_value_width ? width : max_value_width;
  55. }
  56. }
  57. separator = ast_alloca(max_name_width + max_value_width + 8);
  58. memset(separator, '=', max_name_width + max_value_width + 3);
  59. separator[max_name_width + max_value_width + 3] = 0;
  60. ast_str_append(&context->output_buffer, 0, " %-*s : %s\n", max_name_width, "ParameterName", "ParameterValue");
  61. ast_str_append(&context->output_buffer, 0, " %s\n", separator);
  62. objset = ast_variable_list_sort(objset);
  63. for (i = objset; i; i = i->next) {
  64. ast_str_append(&context->output_buffer, 0, " %-*s : %s\n", max_name_width, i->name, i->value);
  65. }
  66. ast_variables_destroy(objset);
  67. return 0;
  68. }
  69. static void complete_show_sorcery_object(struct ao2_container *container,
  70. struct ast_sip_cli_formatter_entry *formatter_entry,
  71. const char *word)
  72. {
  73. size_t wordlen = strlen(word);
  74. void *object;
  75. struct ao2_iterator i = ao2_iterator_init(container, 0);
  76. while ((object = ao2_t_iterator_next(&i, "iterate thru endpoints table"))) {
  77. const char *id = formatter_entry->get_id(object);
  78. if (!strncasecmp(word, id, wordlen)) {
  79. ast_cli_completion_add(ast_strdup(id));
  80. }
  81. ao2_t_ref(object, -1, "toss iterator endpoint ptr before break");
  82. }
  83. ao2_iterator_destroy(&i);
  84. }
  85. static void dump_str_and_free(int fd, struct ast_str *buf)
  86. {
  87. ast_cli(fd, "%s", ast_str_buffer(buf));
  88. ast_free(buf);
  89. }
  90. char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  91. {
  92. RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
  93. RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup);
  94. RAII_VAR(void *, object, NULL, ao2_cleanup);
  95. int is_container = 0;
  96. const char *cmd1;
  97. const char *cmd2;
  98. const char *object_id;
  99. char formatter_type[64];
  100. const char *regex;
  101. struct ast_sip_cli_context context = {
  102. .indent_level = 0,
  103. .show_details = 0,
  104. .show_details_only_level_0 = 0,
  105. .recurse = 0,
  106. };
  107. if (cmd == CLI_INIT) {
  108. return NULL;
  109. }
  110. cmd1 = e->cmda[1];
  111. cmd2 = e->cmda[2];
  112. object_id = a->argv[3];
  113. if (!ast_ends_with(cmd2, "s")) {
  114. ast_copy_string(formatter_type, cmd2, sizeof(formatter_type));
  115. is_container = 0;
  116. } else if (ast_ends_with(cmd2, "ies")) {
  117. /* Take the plural "ies" off of the object name and re[place with "y". */
  118. int l = strlen(cmd2);
  119. snprintf(formatter_type, 64, "%*.*sy", l - 3, l - 3, cmd2);
  120. is_container = 1;
  121. } else {
  122. /* Take the plural "s" off of the object name. */
  123. ast_copy_string(formatter_type, cmd2, strlen(cmd2));
  124. is_container = 1;
  125. }
  126. if (!strcmp(cmd1, "show")) {
  127. context.show_details_only_level_0 = !is_container;
  128. context.recurse = 1;
  129. } else {
  130. is_container = 1;
  131. }
  132. if (cmd != CLI_GENERATE
  133. && is_container
  134. && a->argc >= 4
  135. && strcmp(object_id, "like") == 0) {
  136. if (ast_strlen_zero(a->argv[4])) {
  137. return CLI_SHOWUSAGE;
  138. }
  139. regex = a->argv[4];
  140. } else {
  141. regex = "";
  142. }
  143. if (cmd == CLI_GENERATE
  144. && (is_container
  145. || a->argc > 4
  146. || (a->argc == 4 && ast_strlen_zero(a->word)))) {
  147. return CLI_SUCCESS;
  148. }
  149. context.output_buffer = ast_str_create(256);
  150. if (!context.output_buffer) {
  151. return CLI_FAILURE;
  152. }
  153. formatter_entry = ast_sip_lookup_cli_formatter(formatter_type);
  154. if (!formatter_entry) {
  155. ast_log(LOG_ERROR, "No formatter registered for object type %s.\n",
  156. formatter_type);
  157. ast_free(context.output_buffer);
  158. return CLI_FAILURE;
  159. }
  160. ast_str_append(&context.output_buffer, 0, "\n");
  161. formatter_entry->print_header(NULL, &context, 0);
  162. ast_str_append(&context.output_buffer, 0,
  163. "==========================================================================================\n\n");
  164. if (is_container || cmd == CLI_GENERATE) {
  165. container = formatter_entry->get_container(regex);
  166. if (!container) {
  167. ast_cli(a->fd, "No container returned for object type %s.\n",
  168. formatter_type);
  169. ast_free(context.output_buffer);
  170. return CLI_FAILURE;
  171. }
  172. }
  173. if (cmd == CLI_GENERATE) {
  174. ast_free(context.output_buffer);
  175. complete_show_sorcery_object(container, formatter_entry, a->word);
  176. return NULL;
  177. }
  178. if (is_container) {
  179. if (!ao2_container_count(container)) {
  180. ast_free(context.output_buffer);
  181. ast_cli(a->fd, "No objects found.\n\n");
  182. return CLI_SUCCESS;
  183. }
  184. ao2_callback(container, OBJ_NODATA, formatter_entry->print_body, &context);
  185. ast_str_append(&context.output_buffer, 0, "\nObjects found: %d\n", ao2_container_count(container));
  186. } else {
  187. if (ast_strlen_zero(object_id)) {
  188. ast_free(context.output_buffer);
  189. ast_cli(a->fd, "No object specified.\n");
  190. return CLI_FAILURE;
  191. }
  192. object = formatter_entry->retrieve_by_id(object_id);
  193. if (!object) {
  194. ast_free(context.output_buffer);
  195. ast_cli(a->fd, "Unable to find object %s.\n\n", object_id);
  196. return CLI_SUCCESS;
  197. }
  198. formatter_entry->print_body(object, &context, 0);
  199. }
  200. ast_str_append(&context.output_buffer, 0, "\n");
  201. dump_str_and_free(a->fd, context.output_buffer);
  202. return CLI_SUCCESS;
  203. }
  204. static int formatter_sort(const void *obj, const void *arg, int flags)
  205. {
  206. const struct ast_sip_cli_formatter_entry *left_obj = obj;
  207. const struct ast_sip_cli_formatter_entry *right_obj = arg;
  208. const char *right_key = arg;
  209. int cmp = 0;
  210. switch (flags & OBJ_SEARCH_MASK) {
  211. case OBJ_SEARCH_OBJECT:
  212. right_key = right_obj->name;
  213. /* Fall through */
  214. case OBJ_SEARCH_KEY:
  215. cmp = strcmp(left_obj->name, right_key);
  216. break;
  217. case OBJ_SEARCH_PARTIAL_KEY:
  218. cmp = strncmp(left_obj->name, right_key, strlen(right_key));
  219. break;
  220. default:
  221. cmp = 0;
  222. break;
  223. }
  224. return cmp;
  225. }
  226. static int formatter_compare(void *obj, void *arg, int flags)
  227. {
  228. const struct ast_sip_cli_formatter_entry *left_obj = obj;
  229. const struct ast_sip_cli_formatter_entry *right_obj = arg;
  230. const char *right_key = arg;
  231. int cmp = 0;
  232. switch (flags & OBJ_SEARCH_MASK) {
  233. case OBJ_SEARCH_OBJECT:
  234. right_key = right_obj->name;
  235. /* Fall through */
  236. case OBJ_SEARCH_KEY:
  237. if (strcmp(left_obj->name, right_key) == 0) {;
  238. cmp = CMP_MATCH | CMP_STOP;
  239. }
  240. break;
  241. case OBJ_SEARCH_PARTIAL_KEY:
  242. if (strncmp(left_obj->name, right_key, strlen(right_key)) == 0) {
  243. cmp = CMP_MATCH;
  244. }
  245. break;
  246. default:
  247. cmp = 0;
  248. break;
  249. }
  250. return cmp;
  251. }
  252. static int formatter_hash(const void *obj, int flags)
  253. {
  254. const struct ast_sip_cli_formatter_entry *left_obj = obj;
  255. if (flags & OBJ_SEARCH_OBJECT) {
  256. return ast_str_hash(left_obj->name);
  257. } else if (flags & OBJ_SEARCH_KEY) {
  258. return ast_str_hash(obj);
  259. }
  260. return -1;
  261. }
  262. struct ast_sip_cli_formatter_entry *ast_sip_lookup_cli_formatter(const char *name)
  263. {
  264. return ao2_find(formatter_registry, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
  265. }
  266. int ast_sip_register_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
  267. {
  268. ast_assert(formatter != NULL);
  269. ast_assert(formatter->name != NULL);
  270. ast_assert(formatter->print_body != NULL);
  271. ast_assert(formatter->print_header != NULL);
  272. ast_assert(formatter->get_container != NULL);
  273. ast_assert(formatter->iterate != NULL);
  274. ast_assert(formatter->get_id != NULL);
  275. ast_assert(formatter->retrieve_by_id != NULL);
  276. ao2_link(formatter_registry, formatter);
  277. return 0;
  278. }
  279. int ast_sip_unregister_cli_formatter(struct ast_sip_cli_formatter_entry *formatter)
  280. {
  281. if (formatter) {
  282. ao2_wrlock(formatter_registry);
  283. if (ao2_ref(formatter, -1) == 2) {
  284. ao2_unlink_flags(formatter_registry, formatter, OBJ_NOLOCK);
  285. }
  286. ao2_unlock(formatter_registry);
  287. }
  288. return 0;
  289. }
  290. static char *handle_pjsip_show_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  291. {
  292. switch(cmd) {
  293. case CLI_INIT:
  294. e->command = "pjsip show version";
  295. e->usage =
  296. "Usage: pjsip show version\n"
  297. " Show the version of pjproject that res_pjsip is running against\n";
  298. return NULL;
  299. case CLI_GENERATE:
  300. return NULL;
  301. }
  302. ast_cli(a->fd, "PJPROJECT version currently running against: %s\n", pj_get_version());
  303. return CLI_SUCCESS;
  304. }
  305. static struct ast_cli_entry pjsip_cli[] = {
  306. AST_CLI_DEFINE(handle_pjsip_show_version, "Show the version of pjproject in use"),
  307. };
  308. int ast_sip_initialize_cli(void)
  309. {
  310. formatter_registry = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, 17,
  311. formatter_hash, formatter_sort, formatter_compare);
  312. if (!formatter_registry) {
  313. ast_log(LOG_ERROR, "Unable to create formatter_registry.\n");
  314. return -1;
  315. }
  316. ast_cli_register_multiple(pjsip_cli, ARRAY_LEN(pjsip_cli));
  317. return 0;
  318. }
  319. void ast_sip_destroy_cli(void)
  320. {
  321. ast_cli_unregister_multiple(pjsip_cli, ARRAY_LEN(pjsip_cli));
  322. ao2_ref(formatter_registry, -1);
  323. }