summaryrefslogtreecommitdiff
path: root/res/res_pjsip
diff options
context:
space:
mode:
authorMatthew Jordan <mjordan@digium.com>2013-12-20 21:32:13 +0000
committerMatthew Jordan <mjordan@digium.com>2013-12-20 21:32:13 +0000
commitb172d369c4057cddad18c0d6b1100233c07ebbd7 (patch)
tree0665c20e48161773916473a951bac27315d26d2b /res/res_pjsip
parenta0c288bb23164c6ff91019172980c8aff6fd8c8d (diff)
res_pjsip: Add PJSIP CLI commands
Implements the following cli commands: pjsip list aors pjsip list auths pjsip list channels pjsip list contacts pjsip list endpoints pjsip show aor(s) pjsip show auth(s) pjsip show channels pjsip show endpoint(s) Also... Minor modifications made to the AMI command implementations to facilitate reuse. New function ast_variable_list_sort added to config.c and config.h to implement variable list sorting. (issue ASTERISK-22610) patches: pjsip_cli_v2.patch uploaded by george.joseph (License 6322) ........ Merged revisions 404480 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@404507 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res/res_pjsip')
-rw-r--r--res/res_pjsip/config_auth.c57
-rw-r--r--res/res_pjsip/include/res_pjsip_private.h27
-rw-r--r--res/res_pjsip/location.c245
-rw-r--r--res/res_pjsip/pjsip_cli.c319
-rw-r--r--res/res_pjsip/pjsip_configuration.c421
5 files changed, 875 insertions, 194 deletions
diff --git a/res/res_pjsip/config_auth.c b/res/res_pjsip/config_auth.c
index 047e9337d..d7e759f5d 100644
--- a/res/res_pjsip/config_auth.c
+++ b/res/res_pjsip/config_auth.c
@@ -23,7 +23,9 @@
#include "asterisk/res_pjsip.h"
#include "asterisk/logger.h"
#include "asterisk/sorcery.h"
+#include "asterisk/cli.h"
#include "include/res_pjsip_private.h"
+#include "asterisk/res_pjsip_cli.h"
static void auth_destroy(void *obj)
{
@@ -197,6 +199,59 @@ static struct ast_sip_endpoint_formatter endpoint_auth_formatter = {
.format_ami = format_ami_endpoint_auth
};
+static struct ao2_container *cli_get_auth_container(struct ast_sorcery *sip_sorcery)
+{
+ return ast_sorcery_retrieve_by_fields(sip_sorcery, "auth",
+ AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+}
+
+static int cli_print_auth_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_MAX_WIDTH - indent - 20;
+
+ if (!context->output_buffer) {
+ return -1;
+ }
+
+ ast_str_append(&context->output_buffer, 0,
+ "%*s: <AuthId/UserName%*.*s>\n", indent, "I/OAuth", filler, filler, CLI_HEADER_FILLER);
+
+ return 0;
+}
+
+static int cli_print_auth_body(void *obj, void *arg, int flags) {
+ struct ast_sip_auth *auth = obj;
+ struct ast_sip_cli_context *context = arg;
+ char title[32];
+
+ context->current_auth = auth;
+
+ if (!context->output_buffer) {
+ return -1;
+ }
+
+ snprintf(title, 32, "%sAuth",context->auth_direction ? context->auth_direction : "");
+
+ ast_str_append(&context->output_buffer, 0, "%*s: %s/%s\n",
+ CLI_INDENT_TO_SPACES(context->indent_level), title,
+ ast_sorcery_object_get_id(auth), auth->auth_user);
+
+ if (context->show_details || (context->show_details_only_level_0 && context->indent_level == 0)) {
+ ast_str_append(&context->output_buffer, 0, "\n");
+ ast_sip_cli_print_sorcery_objectset(auth, context, 0);
+ }
+
+ return 0;
+}
+
+static struct ast_sip_cli_formatter_entry cli_auth_formatter = {
+ .name = SIP_SORCERY_AUTH_TYPE,
+ .print_header = cli_print_auth_header,
+ .print_body = cli_print_auth_body,
+ .get_container = cli_get_auth_container,
+};
+
/*! \brief Initialize sorcery with auth support */
int ast_sip_initialize_sorcery_auth(struct ast_sorcery *sorcery)
{
@@ -222,5 +277,7 @@ int ast_sip_initialize_sorcery_auth(struct ast_sorcery *sorcery)
"userpass", auth_type_handler, auth_type_to_str, 0, 0);
ast_sip_register_endpoint_formatter(&endpoint_auth_formatter);
+ ast_sip_register_cli_formatter(&cli_auth_formatter);
+
return 0;
}
diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h
index 368bdd04a..a2d1c6eb4 100644
--- a/res/res_pjsip/include/res_pjsip_private.h
+++ b/res/res_pjsip/include/res_pjsip_private.h
@@ -9,8 +9,6 @@
#define RES_PJSIP_PRIVATE_H_
#include "asterisk/module.h"
-#include "asterisk/stasis_channels.h"
-#include "asterisk/stasis_endpoints.h"
struct ao2_container;
struct ast_threadpool_options;
@@ -87,25 +85,6 @@ void ast_res_pjsip_cleanup_options_handling(void);
void sip_get_threadpool_options(struct ast_threadpool_options *threadpool_options);
/*!
- * \brief Function pointer for channel snapshot callbacks.
- */
-typedef int (*on_channel_snapshot_t)(
- const struct ast_channel_snapshot *snapshot, int last, void *arg);
-
-/*!
- * \brief For every channel snapshot on an endpoint snapshot call the given
- * 'on_channel_snapshot' handler.
- *
- * \param endpoint_snapshot snapshot of an endpoint
- * \param on_channel_snapshot callback for each channel snapshot
- * \param arg user data passed to handler
- * \retval 0 Success, non-zero on failure
- */
-int ast_sip_for_each_channel_snapshot(const struct ast_endpoint_snapshot *endpoint_snapshot,
- on_channel_snapshot_t on_channel_snapshot,
- void *arg);
-
-/*!
* \brief Retrieve the name of the default outbound endpoint.
*
* \note This returns a memory allocated copy of the name that
@@ -116,4 +95,10 @@ int ast_sip_for_each_channel_snapshot(const struct ast_endpoint_snapshot *endpoi
*/
char *ast_sip_global_default_outbound_endpoint(void);
+/*!
+ * \brief Functions for initializing and destroying the CLI.
+ */
+int ast_sip_initialize_cli(struct ast_sorcery *sip_sorcery);
+void ast_sip_destroy_cli(void);
+
#endif /* RES_PJSIP_PRIVATE_H_ */
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 52f69cfd8..3bd4dea10 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -25,6 +25,7 @@
#include "asterisk/astobj2.h"
#include "asterisk/sorcery.h"
#include "include/res_pjsip_private.h"
+#include "asterisk/res_pjsip_cli.h"
/*! \brief Destructor for AOR */
static void aor_destroy(void *obj)
@@ -284,12 +285,37 @@ int ast_sip_for_each_aor(const char *aors, ao2_callback_fn on_aor, void *arg)
return 0;
}
-int ast_sip_for_each_contact(const struct ast_sip_aor *aor,
- on_contact_t on_contact, void *arg)
+static void destroy_contact_pair(void *obj)
+{
+ struct ast_sip_aor_contact_pair *pair = obj;
+ ao2_cleanup(pair->aor);
+ ao2_cleanup(pair->contact);
+}
+
+static struct ast_sip_aor_contact_pair *create_contact_pair(
+ struct ast_sip_aor *aor, struct ast_sip_contact *contact)
+{
+ struct ast_sip_aor_contact_pair *pair = ao2_alloc(
+ sizeof(*pair), destroy_contact_pair);
+
+ if (!pair) {
+ return NULL;
+ }
+
+ pair->aor = aor;
+ pair->contact = contact;
+
+ ao2_ref(pair->aor, +1);
+ ao2_ref(pair->contact, +1);
+
+ return pair;
+}
+
+int ast_sip_for_each_contact(struct ast_sip_aor *aor,
+ ao2_callback_fn on_contact, void *arg)
{
RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
struct ast_sip_contact *contact;
- int num;
struct ao2_iterator i;
if (!on_contact ||
@@ -297,13 +323,13 @@ int ast_sip_for_each_contact(const struct ast_sip_aor *aor,
return 0;
}
- num = ao2_container_count(contacts);
i = ao2_iterator_init(contacts, 0);
while ((contact = ao2_iterator_next(&i))) {
- int res = on_contact(aor, contact, --num == 0, arg);
+ int res;
+ RAII_VAR(struct ast_sip_aor_contact_pair *,
+ acp, create_contact_pair(aor, contact), ao2_cleanup);
- ao2_ref(contact, -1);
- if (res) {
+ if (!acp || (res = on_contact(acp, arg, 0))) {
ao2_iterator_destroy(&i);
return -1;
}
@@ -312,18 +338,13 @@ int ast_sip_for_each_contact(const struct ast_sip_aor *aor,
return 0;
}
-int ast_sip_contact_to_str(const struct ast_sip_aor *aor,
- const struct ast_sip_contact *contact,
- int last, void *arg)
+int ast_sip_contact_to_str(void *object, void *arg, int flags)
{
+ struct ast_sip_aor_contact_pair *acp = object;
struct ast_str **buf = arg;
- ast_str_append(buf, 0, "%s/%s",
- ast_sorcery_object_get_id(aor), contact->uri);
-
- if (!last) {
- ast_str_append(buf, 0, ",");
- }
+ ast_str_append(buf, 0, "%s/%s,",
+ ast_sorcery_object_get_id(acp->aor), acp->contact->uri);
return 0;
}
@@ -335,7 +356,7 @@ static int sip_aor_to_ami(const struct ast_sip_aor *aor, struct ast_str **buf)
static int format_ami_aor_handler(void *obj, void *arg, int flags)
{
- const struct ast_sip_aor *aor = obj;
+ struct ast_sip_aor *aor = obj;
struct ast_sip_ami *ami = arg;
const struct ast_sip_endpoint *endpoint = ami->arg;
RAII_VAR(struct ast_str *, buf,
@@ -352,6 +373,7 @@ static int format_ami_aor_handler(void *obj, void *arg, int flags)
sip_aor_to_ami(aor, &buf);
ast_str_append(&buf, 0, "Contacts: ");
ast_sip_for_each_contact(aor, ast_sip_contact_to_str, &buf);
+ ast_str_truncate(buf, -1);
ast_str_append(&buf, 0, "\r\n");
num = ao2_container_count(contacts);
@@ -377,6 +399,193 @@ struct ast_sip_endpoint_formatter endpoint_aor_formatter = {
.format_ami = format_ami_endpoint_aor
};
+static int populate_contact_container(void *obj, void *arg, int flags)
+{
+ struct ast_sip_aor_contact_pair *acp = obj;
+ struct ao2_container *container = arg;
+ ao2_link_flags(container, acp, OBJ_NOLOCK);
+ return 0;
+}
+
+static int gather_aor_channels(void *obj, void *arg, int flags)
+{
+ struct ast_sip_aor *aor = obj;
+ struct ao2_container *container = arg;
+ ast_sip_for_each_contact(aor, populate_contact_container, container);
+ return 0;
+}
+
+static struct ao2_container *cli_get_contact_container(struct ast_sorcery *sip_sorcery)
+{
+ RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup);
+ RAII_VAR(struct ao2_container *, s_parent_container, NULL, ao2_cleanup);
+ struct ao2_container *child_container;
+
+ parent_container = ast_sorcery_retrieve_by_fields(sip_sorcery, "aor",
+ AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+ if (!parent_container) {
+ return NULL;
+ }
+
+ s_parent_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, &ast_sorcery_object_id_compare, NULL);
+ if (!s_parent_container) {
+ return NULL;
+ }
+
+ ao2_container_dup(s_parent_container, parent_container, OBJ_ORDER_ASCENDING);
+
+ child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL);
+ if (!child_container) {
+ return NULL;
+ }
+
+ ao2_callback(s_parent_container, OBJ_NODATA, gather_aor_channels, child_container);
+
+ return child_container;
+}
+
+
+static int cli_print_contact_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 - 18;
+
+ if (!context->output_buffer) {
+ return -1;
+ }
+ ast_str_append(&context->output_buffer, 0,
+ "%*s: <Aor/ContactUri%*.*s> <Status....> <RTT(ms)..>\n",
+ indent, "Contact", filler, filler, CLI_HEADER_FILLER);
+
+ return 0;
+}
+
+static int cli_print_contact_body(void *obj, void *arg, int flags)
+{
+ struct ast_sip_aor_contact_pair *acp = obj;
+ struct ast_sip_cli_context *context = arg;
+ char *print_name = NULL;
+ int print_name_len;
+ int indent;
+ int flexwidth;
+
+ RAII_VAR(struct ast_sip_contact_status *, status,
+ ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(acp->contact)),
+ ao2_cleanup);
+
+ if (!context->output_buffer) {
+ return -1;
+ }
+
+ print_name_len = strlen(ast_sorcery_object_get_id(acp->aor))
+ + strlen(acp->contact->uri) + 2;
+ if (!(print_name = alloca(print_name_len))) {
+ return -1;
+ }
+ snprintf(print_name, print_name_len, "%s/%s",
+ ast_sorcery_object_get_id(acp->aor), acp->contact->uri);
+
+ indent = CLI_INDENT_TO_SPACES(context->indent_level);
+ flexwidth = CLI_LAST_TABSTOP - indent - 2;
+
+ ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %-12.12s %11.3f\n",
+ indent,
+ "Contact",
+ flexwidth, flexwidth,
+ print_name,
+ (status ? (status->status == AVAILABLE ? "Avail" : "Unavail") : "Unknown"),
+ (status ? ((long long) status->rtt) / 1000.0 : NAN));
+
+ return 0;
+}
+
+static struct ao2_container *cli_get_aor_container(struct ast_sorcery *sip_sorcery)
+{
+ return ast_sorcery_retrieve_by_fields(sip_sorcery, "aor",
+ AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+}
+
+static int cli_print_aor_header(void *obj, void *arg, int flags)
+{
+ struct ast_sip_cli_context *context = arg;
+ struct ast_sip_cli_formatter_entry *formatter_entry;
+
+ int indent = CLI_INDENT_TO_SPACES(context->indent_level);
+ int filler = CLI_LAST_TABSTOP - indent - 7;
+
+ if (!context->output_buffer) {
+ return -1;
+ }
+ ast_str_append(&context->output_buffer, 0,
+ "%*s: <Aor%*.*s> <MaxContact>\n",
+ indent, "Aor", filler, filler, CLI_HEADER_FILLER);
+
+ if (context->recurse) {
+ context->indent_level++;
+ formatter_entry = ast_sip_lookup_cli_formatter("contact");
+ if (formatter_entry) {
+ formatter_entry->print_header(NULL, context, 0);
+ }
+ context->indent_level--;
+ }
+ return 0;
+}
+
+static int cli_print_aor_body(void *obj, void *arg, int flags)
+{
+ struct ast_sip_aor *aor = obj;
+ struct ast_sip_cli_context *context = arg;
+ struct ast_sip_cli_formatter_entry *formatter_entry;
+ int indent;
+ int flexwidth;
+
+ if (!context->output_buffer) {
+ return -1;
+ }
+
+ context->current_aor = aor;
+
+ indent = CLI_INDENT_TO_SPACES(context->indent_level);
+ flexwidth = CLI_LAST_TABSTOP - indent - 12;
+
+ ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %12d\n",
+ indent,
+ "Aor",
+ flexwidth, flexwidth,
+ ast_sorcery_object_get_id(aor), aor->max_contacts);
+
+ if (context->recurse) {
+ context->indent_level++;
+ formatter_entry = ast_sip_lookup_cli_formatter("contact");
+ if (formatter_entry) {
+ ast_sip_for_each_contact(aor, formatter_entry->print_body, context);
+ }
+ context->indent_level--;
+ }
+
+ if (context->show_details || (context->show_details_only_level_0 && context->indent_level == 0)) {
+ ast_str_append(&context->output_buffer, 0, "\n");
+ ast_sip_cli_print_sorcery_objectset(aor, context, 0);
+ }
+
+ return 0;
+}
+
+static struct ast_sip_cli_formatter_entry cli_contact_formatter = {
+ .name = "contact",
+ .print_header = cli_print_contact_header,
+ .print_body = cli_print_contact_body,
+ .get_container = cli_get_contact_container,
+};
+
+static struct ast_sip_cli_formatter_entry cli_aor_formatter = {
+ .name = "aor",
+ .print_header = cli_print_aor_header,
+ .print_body = cli_print_aor_body,
+ .get_container = cli_get_aor_container,
+};
+
/*! \brief Initialize sorcery with location support */
int ast_sip_initialize_sorcery_location(struct ast_sorcery *sorcery)
{
@@ -408,6 +617,8 @@ int ast_sip_initialize_sorcery_location(struct ast_sorcery *sorcery)
ast_sorcery_object_field_register(sorcery, "aor", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, outbound_proxy));
ast_sip_register_endpoint_formatter(&endpoint_aor_formatter);
+ ast_sip_register_cli_formatter(&cli_contact_formatter);
+ ast_sip_register_cli_formatter(&cli_aor_formatter);
return 0;
}
diff --git a/res/res_pjsip/pjsip_cli.c b/res/res_pjsip/pjsip_cli.c
new file mode 100644
index 000000000..5f8157d2d
--- /dev/null
+++ b/res/res_pjsip/pjsip_cli.c
@@ -0,0 +1,319 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Fairview 5 Engineering, LLC
+ *
+ * George Joseph <george.joseph@fairview5.com>
+ *
+ * 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.
+ */
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_ua.h>
+#include "asterisk/res_pjsip.h"
+#include "include/res_pjsip_private.h"
+#include "asterisk/res_pjsip_cli.h"
+#include "asterisk/acl.h"
+#include "asterisk/cli.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/hashtab.h"
+#include "asterisk/utils.h"
+#include "asterisk/sorcery.h"
+
+static struct ast_hashtab *formatter_registry;
+
+static struct ast_sorcery *sip_sorcery;
+
+struct ast_sip_cli_formatter_entry *ast_sip_lookup_cli_formatter(const char *name)
+{
+ struct ast_sip_cli_formatter_entry fake_entry = {
+ .name = name,
+ };
+ return ast_hashtab_lookup(formatter_registry, &fake_entry);
+}
+
+int ast_sip_cli_print_sorcery_objectset(void *obj, void *arg, int flags)
+{
+ struct ast_sip_cli_context *context = arg;
+ struct ast_variable *i;
+ int max_name_width = 13;
+ int max_value_width = 14;
+ int width;
+ char *separator;
+ struct ast_variable *objset;
+
+ if (!context->output_buffer) {
+ return -1;
+ }
+
+ objset = ast_sorcery_objectset_create(ast_sip_get_sorcery(),obj);
+ if (!objset) {
+ return -1;
+ }
+
+ for (i = objset; i; i = i->next) {
+ if (i->name) {
+ width = strlen(i->name);
+ max_name_width = width > max_name_width ? width : max_name_width;
+ }
+ if (i->value) {
+ width = strlen(i->value);
+ max_value_width = width > max_value_width ? width : max_value_width;
+ }
+ }
+
+ if (!(separator = alloca(max_name_width + max_value_width + 8))) {
+ return -1;
+ }
+ memset(separator, '=', max_name_width + max_value_width + 3);
+ separator[max_name_width + max_value_width + 3] = 0;
+
+ ast_str_append(&context->output_buffer, 0, " %-*s : %s\n", max_name_width, "ParameterName", "ParameterValue");
+ ast_str_append(&context->output_buffer, 0, " %s\n", separator);
+
+ objset = ast_variable_list_sort(objset);
+
+ for (i = objset; i; i = i->next) {
+ ast_str_append(&context->output_buffer, 0, " %-*s : %s\n", max_name_width, i->name, i->value);
+ }
+
+ return 0;
+}
+
+static char *complete_show_sorcery_object(struct ao2_container *container,
+ const char *word, int state)
+{
+ char *result = NULL;
+ int wordlen = strlen(word);
+ int which = 0;
+
+ struct ao2_iterator i = ao2_iterator_init(container, 0);
+ void *object;
+
+ while ((object = ao2_t_iterator_next(&i, "iterate thru endpoints table"))) {
+ if (!strncasecmp(word, ast_sorcery_object_get_id(object), wordlen)
+ && ++which > state) {
+ result = ast_strdup(ast_sorcery_object_get_id(object));
+ }
+ ao2_t_ref(object, -1, "toss iterator endpoint ptr before break");
+ if (result) {
+ break;
+ }
+ }
+ ao2_iterator_destroy(&i);
+ return result;
+}
+
+static void dump_str_and_free(int fd, struct ast_str *buf) {
+ ast_cli(fd, "%s", ast_str_buffer(buf));
+ ast_free(buf);
+}
+
+static char *cli_traverse_objects(struct ast_cli_entry *e, int cmd,
+ struct ast_cli_args *a)
+{
+ RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
+ RAII_VAR(struct ao2_container *, s_container, NULL, ao2_cleanup);
+ RAII_VAR(void *, object, NULL, ao2_cleanup);
+ int is_container = 0;
+ const char *cmd1 = NULL;
+ const char *cmd2 = NULL;
+ const char *object_id = NULL;
+ char formatter_type[64];
+ struct ast_sip_cli_formatter_entry *formatter_entry;
+ int is_plural = 0;
+
+ struct ast_sip_cli_context context = {
+ .peers_mon_online = 0,
+ .peers_mon_offline = 0,
+ .peers_unmon_online = 0,
+ .peers_unmon_offline = 0,
+ .a = a,
+ .indent_level = 0,
+ };
+
+ if (cmd == CLI_INIT) {
+ return NULL;
+ }
+
+ cmd1 = e->cmda[1];
+ cmd2 = e->cmda[2];
+ object_id = a->argv[3];
+
+ if (!ast_ends_with(cmd2, "s")) {
+ ast_copy_string(formatter_type, cmd2, strlen(cmd2)+1);
+ is_plural = 0;
+ } else {
+ ast_copy_string(formatter_type, cmd2, strlen(cmd2));
+ is_plural = 1;
+ }
+
+ if (!strcmp(cmd1, "show")) {
+ if (is_plural) {
+ context.recurse = 1;
+ context.show_details = 0;
+ context.show_details_only_level_0 = 0;
+ is_container = 1;
+ } else {
+ context.recurse = 1;
+ context.show_details = 0;
+ context.show_details_only_level_0 = 1;
+ is_container = 0;
+ }
+ } else {
+ context.recurse = 0;
+ context.show_details = 0;
+ context.show_details_only_level_0 = 0;
+ is_container = 1;
+ }
+
+ context.output_buffer = ast_str_create(256);
+ formatter_entry = ast_sip_lookup_cli_formatter(formatter_type);
+ if (!formatter_entry) {
+ ast_log(LOG_ERROR, "CLI TRAVERSE failure. No container found for object type %s\n", formatter_type);
+ return CLI_FAILURE;
+ }
+ ast_str_append(&context.output_buffer, 0, "\n");
+ formatter_entry->print_header(NULL, &context, 0);
+ ast_str_append(&context.output_buffer, 0, " =========================================================================================\n\n");
+
+ if (is_container || cmd == CLI_GENERATE) {
+ container = formatter_entry->get_container(sip_sorcery);
+ if (!container) {
+ ast_cli(a->fd, "CLI TRAVERSE failure. No container found for object type %s\n", formatter_type);
+ return CLI_FAILURE ;
+ }
+ }
+
+ if (!is_container && cmd == CLI_GENERATE) {
+ return complete_show_sorcery_object(container, a->word, a->n);
+ }
+
+ if (is_container) {
+ if (!ao2_container_count(container)) {
+ dump_str_and_free(a->fd, context.output_buffer);
+ ast_cli(a->fd, "No objects found.\n\n");
+ return CLI_SUCCESS ;
+ }
+
+ if (!strcmp(formatter_type, "channel") || !strcmp(formatter_type, "contact")) {
+ s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL);
+ } else {
+ s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, &ast_sorcery_object_id_compare, NULL);
+ }
+
+ ao2_container_dup(s_container, container, OBJ_ORDER_ASCENDING);
+
+ ao2_callback(s_container, OBJ_NODATA, formatter_entry->print_body, &context);
+ } else {
+ if (!(object = ast_sorcery_retrieve_by_id(
+ ast_sip_get_sorcery(), formatter_type, object_id))) {
+ dump_str_and_free(a->fd, context.output_buffer);
+ ast_cli(a->fd, "Unable to retrieve object %s\n", object_id);
+ return CLI_FAILURE ;
+ }
+ formatter_entry->print_body(object, &context, 0);
+ }
+
+ ast_str_append(&context.output_buffer, 0, "\n");
+ dump_str_and_free(a->fd, context.output_buffer);
+ return CLI_SUCCESS ;
+}
+
+static struct ast_cli_entry cli_commands[] = {
+ AST_CLI_DEFINE(cli_traverse_objects, "List PJSIP Channels", .command = "pjsip list channels",
+ .usage = "Usage: pjsip list channels\n List the active PJSIP channels\n"),
+ AST_CLI_DEFINE(cli_traverse_objects, "Show PJSIP Channels", .command = "pjsip show channels",
+ .usage = "Usage: pjsip show channels\n List(detailed) the active PJSIP channels\n"),
+
+ AST_CLI_DEFINE(cli_traverse_objects, "List PJSIP Aors", .command = "pjsip list aors",
+ .usage = "Usage: pjsip list aors\n List the configured PJSIP Aors\n"),
+ AST_CLI_DEFINE(cli_traverse_objects, "Show PJSIP Aors", .command = "pjsip show aors",
+ .usage = "Usage: pjsip show aors\n Show the configured PJSIP Aors\n"),
+ AST_CLI_DEFINE(cli_traverse_objects, "Show PJSIP Aor", .command = "pjsip show aor",
+ .usage = "Usage: pjsip show aor\n Show the configured PJSIP Aor\n"),
+
+ AST_CLI_DEFINE(cli_traverse_objects, "List PJSIP Contacts", .command = "pjsip list contacts",
+ .usage = "Usage: pjsip list contacts\n List the configured PJSIP contacts\n"),
+
+ AST_CLI_DEFINE(cli_traverse_objects, "List PJSIP Endpoints", .command = "pjsip list endpoints",
+ .usage = "Usage: pjsip list endpoints\n List the configured PJSIP endpoints\n"),
+ AST_CLI_DEFINE(cli_traverse_objects, "Show PJSIP Endpoints", .command = "pjsip show endpoints",
+ .usage = "Usage: pjsip show endpoints\n List(detailed) the configured PJSIP endpoints\n"),
+ AST_CLI_DEFINE(cli_traverse_objects, "Show PJSIP Endpoint", .command = "pjsip show endpoint",
+ .usage = "Usage: pjsip show endpoint <id>\n Show the configured PJSIP endpoint\n"),
+
+ AST_CLI_DEFINE(cli_traverse_objects, "List PJSIP Auths", .command = "pjsip list auths",
+ .usage = "Usage: pjsip list auths\n List the configured PJSIP Auths\n"),
+ AST_CLI_DEFINE(cli_traverse_objects, "Show PJSIP Auths", .command = "pjsip show auths",
+ .usage = "Usage: pjsip show auths\n Show the configured PJSIP Auths\n"),
+ AST_CLI_DEFINE(cli_traverse_objects, "Show PJSIP Auth", .command = "pjsip show auth",
+ .usage = "Usage: pjsip show auth\n Show the configured PJSIP Auth\n"),
+
+};
+
+
+static int compare_formatters(const void *a, const void *b) {
+ const struct ast_sip_cli_formatter_entry *afe = a;
+ const struct ast_sip_cli_formatter_entry *bfe = b;
+ if (!afe || !bfe) {
+ ast_log(LOG_ERROR, "One of the arguments to compare_formatters was NULL\n");
+ return -1;
+ }
+ return strcmp(afe->name, bfe->name);
+}
+
+static unsigned int hash_formatters(const void *a) {
+ const struct ast_sip_cli_formatter_entry *afe = a;
+ return ast_hashtab_hash_string(afe->name);
+}
+
+int ast_sip_register_cli_formatter(struct ast_sip_cli_formatter_entry *formatter) {
+ ast_hashtab_insert_safe(formatter_registry, formatter);
+ return 0;
+}
+
+int ast_sip_unregister_cli_formatter(struct ast_sip_cli_formatter_entry *formatter) {
+ struct ast_sip_cli_formatter_entry *entry = ast_hashtab_lookup(formatter_registry, formatter);
+ if (!entry) {
+ return -1;
+ }
+ ast_hashtab_remove_this_object(formatter_registry, entry);
+ return 0;
+}
+
+int ast_sip_initialize_cli(struct ast_sorcery *sorcery)
+{
+ formatter_registry = ast_hashtab_create(17, compare_formatters,
+ ast_hashtab_resize_java, ast_hashtab_newsize_java, hash_formatters, 0);
+ if (!formatter_registry) {
+ ast_log(LOG_ERROR, "Unable to create formatter_registry.\n");
+ return -1;
+ }
+
+ if (ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands))) {
+ ast_log(LOG_ERROR, "Failed to register pjsip cli commands.\n");
+ ast_hashtab_destroy(formatter_registry, ast_free);
+ return -1;
+ }
+ sip_sorcery = sorcery;
+ return 0;
+}
+
+void ast_sip_destroy_cli(void)
+{
+ ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
+ if (formatter_registry) {
+ ast_hashtab_destroy(formatter_registry, ast_free);
+ }
+}
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 010eeb697..c8e8e3bf1 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -12,7 +12,8 @@
#include "asterisk/res_pjsip.h"
#include "include/res_pjsip_private.h"
-#include "asterisk/cli.h"
+#include "asterisk/res_pjsip_cli.h"
+#include "asterisk/acl.h"
#include "asterisk/manager.h"
#include "asterisk/astobj2.h"
#include "asterisk/utils.h"
@@ -97,131 +98,6 @@ static const struct ast_sorcery_observer state_contact_observer = {
.deleted = persistent_endpoint_contact_observer,
};
-static char *handle_cli_show_endpoints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- RAII_VAR(struct ao2_container *, endpoints, NULL, ao2_cleanup);
- struct ao2_iterator it_endpoints;
- struct ast_sip_endpoint *endpoint;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "pjsip show endpoints";
- e->usage =
- "Usage: pjsip show endpoints\n"
- " Show the registered PJSIP endpoints\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- endpoints = ast_sip_get_endpoints();
- if (!endpoints) {
- return CLI_FAILURE;
- }
-
- if (!ao2_container_count(endpoints)) {
- ast_cli(a->fd, "No endpoints found\n");
- return CLI_SUCCESS;
- }
-
- ast_cli(a->fd, "Endpoints:\n");
- it_endpoints = ao2_iterator_init(endpoints, 0);
- while ((endpoint = ao2_iterator_next(&it_endpoints))) {
- ast_cli(a->fd, "%s\n", ast_sorcery_object_get_id(endpoint));
- ao2_ref(endpoint, -1);
- }
- ao2_iterator_destroy(&it_endpoints);
- return CLI_SUCCESS;
-}
-
-static int show_contact(void *obj, void *arg, int flags)
-{
- struct ast_sip_contact *contact = obj;
- struct ast_cli_args *a = arg;
- RAII_VAR(struct ast_sip_contact_status *, status, ast_sorcery_retrieve_by_id(
- ast_sip_get_sorcery(), CONTACT_STATUS,
- ast_sorcery_object_get_id(contact)), ao2_cleanup);
-
- ast_cli(a->fd, "\tContact %s:\n", contact->uri);
-
- if (!status) {
- ast_cli(a->fd, "\tStatus not found!\n");
- return 0;
- }
-
- ast_cli(a->fd, "\t\tavailable = %s\n", status->status ? "yes" : "no");
-
- if (status->status) {
- ast_cli(a->fd, "\t\tRTT = %lld microseconds\n", (long long)status->rtt);
- }
-
- return 0;
-}
-
-static void show_endpoint(struct ast_sip_endpoint *endpoint, struct ast_cli_args *a)
-{
- char *aor_name, *aors;
-
- if (ast_strlen_zero(endpoint->aors)) {
- return;
- }
-
- aors = ast_strdupa(endpoint->aors);
-
- while ((aor_name = strsep(&aors, ","))) {
- RAII_VAR(struct ast_sip_aor *, aor,
- ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
- RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
-
- if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
- continue;
- }
-
- ast_cli(a->fd, "AOR %s:\n", ast_sorcery_object_get_id(aor));
- ao2_callback(contacts, OBJ_NODATA, show_contact, a);
- }
-
- return;
-}
-
-static char *cli_show_endpoint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
- const char *endpoint_name;
-
- switch (cmd) {
- case CLI_INIT:
- e->command = "pjsip show endpoint";
- e->usage =
- "Usage: pjsip show endpoint <endpoint>\n"
- " Show the given PJSIP endpoint.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- if (a->argc != 4) {
- return CLI_SHOWUSAGE;
- }
-
- endpoint_name = a->argv[3];
-
- if (!(endpoint = ast_sorcery_retrieve_by_id(
- ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
- ast_cli(a->fd, "Unable to retrieve endpoint %s\n", endpoint_name);
- return CLI_FAILURE;
- }
-
- ast_cli(a->fd, "Endpoint %s:\n", endpoint_name);
- show_endpoint(endpoint, a);
-
- return CLI_SUCCESS;
-}
-
-static struct ast_cli_entry cli_commands[] = {
- AST_CLI_DEFINE(handle_cli_show_endpoints, "Show PJSIP Endpoints"),
- AST_CLI_DEFINE(cli_show_endpoint, "Show PJSIP Endpoint")
-};
static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
@@ -956,7 +832,7 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o
return 0;
}
-static const char *get_device_state(const struct ast_sip_endpoint *endpoint)
+const char *ast_sip_get_device_state(const struct ast_sip_endpoint *endpoint)
{
char device[MAX_OBJECT_FIELD];
@@ -964,7 +840,7 @@ static const char *get_device_state(const struct ast_sip_endpoint *endpoint)
return ast_devstate2str(ast_device_state(device));
}
-static struct ast_endpoint_snapshot *sip_get_endpoint_snapshot(
+struct ast_endpoint_snapshot *ast_sip_get_endpoint_snapshot(
const struct ast_sip_endpoint *endpoint)
{
return ast_endpoint_latest_snapshot(
@@ -974,46 +850,44 @@ static struct ast_endpoint_snapshot *sip_get_endpoint_snapshot(
int ast_sip_for_each_channel_snapshot(
const struct ast_endpoint_snapshot *endpoint_snapshot,
- on_channel_snapshot_t on_channel_snapshot, void *arg)
+ ao2_callback_fn on_channel_snapshot, void *arg)
{
int num, num_channels = endpoint_snapshot->num_channels;
- RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);
- if (!on_channel_snapshot || !num_channels ||
- !(cache = ast_channel_cache())) {
+ if (!on_channel_snapshot || !num_channels) {
return 0;
}
- ao2_ref(cache, +1);
-
for (num = 0; num < num_channels; ++num) {
- RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
- struct ast_channel_snapshot *snapshot;
-
- msg = stasis_cache_get(cache, ast_channel_snapshot_type(),
- endpoint_snapshot->channel_ids[num]);
+ RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
+ int res;
- if (!(snapshot = stasis_message_data(msg))) {
+ snapshot = ast_channel_snapshot_get_latest(endpoint_snapshot->channel_ids[num]);
+ if (!snapshot) {
continue;
}
- if (on_channel_snapshot(
- snapshot, num == (num_channels - 1), arg)) {
+ res = on_channel_snapshot(snapshot, arg, 0);
+ if (res) {
return -1;
}
}
return 0;
}
-static int active_channels_to_str_cb(const struct ast_channel_snapshot *snapshot,
- int last, void *arg)
+int ast_sip_for_each_channel(
+ const struct ast_sip_endpoint *endpoint,
+ ao2_callback_fn on_channel_snapshot, void *arg)
+{
+ RAII_VAR(struct ast_endpoint_snapshot *, endpoint_snapshot, ast_sip_get_endpoint_snapshot(endpoint), ao2_cleanup);
+ return ast_sip_for_each_channel_snapshot(endpoint_snapshot, on_channel_snapshot, arg);
+}
+
+static int active_channels_to_str_cb(void *object, void *arg, int flags)
{
+ const struct ast_channel_snapshot *snapshot = object;
struct ast_str **buf = arg;
- if (last) {
- ast_str_append(buf, 0, "%s", snapshot->name);
- } else {
- ast_str_append(buf, 0, "%s,", snapshot->name);
- }
+ ast_str_append(buf, 0, "%s,", snapshot->name);
return 0;
}
@@ -1022,7 +896,7 @@ static void active_channels_to_str(const struct ast_sip_endpoint *endpoint,
{
RAII_VAR(struct ast_endpoint_snapshot *, endpoint_snapshot,
- sip_get_endpoint_snapshot(endpoint), ao2_cleanup);
+ ast_sip_get_endpoint_snapshot(endpoint), ao2_cleanup);
if (endpoint_snapshot) {
return;
@@ -1030,6 +904,7 @@ static void active_channels_to_str(const struct ast_sip_endpoint *endpoint,
ast_sip_for_each_channel_snapshot(endpoint_snapshot,
active_channels_to_str_cb, str);
+ ast_str_truncate(*str, -1);
}
#define AMI_DEFAULT_STR_SIZE 512
@@ -1078,7 +953,7 @@ int ast_sip_sorcery_object_to_ami(const void *obj, struct ast_str **buf)
static int sip_endpoints_aors_ami(void *obj, void *arg, int flags)
{
- const struct ast_sip_aor *aor = obj;
+ struct ast_sip_aor *aor = obj;
struct ast_str **buf = arg;
ast_str_append(buf, 0, "Contacts: ");
@@ -1096,7 +971,7 @@ static int sip_endpoint_to_ami(const struct ast_sip_endpoint *endpoint,
}
ast_str_append(buf, 0, "DeviceState: %s\r\n",
- get_device_state(endpoint));
+ ast_sip_get_device_state(endpoint));
ast_str_append(buf, 0, "ActiveChannels: ");
active_channels_to_str(endpoint, buf);
@@ -1206,7 +1081,7 @@ static int format_ami_endpoints(void *obj, void *arg, int flags)
sip_endpoints_aors_ami, &buf);
ast_str_append(&buf, 0, "DeviceState: %s\r\n",
- get_device_state(endpoint));
+ ast_sip_get_device_state(endpoint));
ast_str_append(&buf, 0, "ActiveChannels: ");
active_channels_to_str(endpoint, &buf);
@@ -1244,11 +1119,241 @@ static int ami_show_endpoints(struct mansession *s, const struct message *m)
return 0;
}
-int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_module_info)
+static int populate_channel_container(void *obj, void *arg, int flags) {
+ struct ast_channel_snapshot *snapshot = obj;
+ struct ao2_container *container = arg;
+ ao2_link_flags(container, snapshot, OBJ_NOLOCK);
+ return 0;
+}
+
+static int gather_endpoint_channels(void *obj, void *arg, int flags) {
+ struct ast_sip_endpoint *endpoint = obj;
+ struct ao2_container *channels = arg;
+ ast_sip_for_each_channel(endpoint, populate_channel_container, channels);
+ return 0;
+}
+
+static struct ao2_container *cli_get_channel_container(struct ast_sorcery *sip_sorcery)
+{
+ RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup);
+ RAII_VAR(struct ao2_container *, s_parent_container, NULL, ao2_cleanup);
+ struct ao2_container *child_container;
+
+ parent_container = ast_sorcery_retrieve_by_fields(sip_sorcery, "endpoint",
+ AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+ if (!parent_container) {
+ return NULL;
+ }
+
+ s_parent_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, &ast_sorcery_object_id_compare, NULL);
+ if (!s_parent_container) {
+ return NULL;
+ }
+
+ if (ao2_container_dup(s_parent_container, parent_container, OBJ_ORDER_ASCENDING)) {
+ return NULL;
+ }
+
+ child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL);
+ if (!child_container) {
+ return NULL;
+ }
+
+ ao2_callback(s_parent_container, OBJ_NODATA, gather_endpoint_channels, child_container);
+
+ return child_container;
+}
+
+static int cli_print_channel_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;
+
+ if (!context->output_buffer) {
+ return -1;
+ }
+ ast_str_append(&context->output_buffer, 0,
+ "%*s: <ChannelId%*.*s> <State.....> <Time(sec)>\n",
+ indent, "Channel", filler, filler, CLI_HEADER_FILLER);
+
+ 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: <Codec> Exten: <DialedExten%*.*s> CLCID: <ConnectedLineCID.......>\n",
+ indent, "Codec", filler, filler, CLI_HEADER_FILLER);
+ context->indent_level--;
+ return 0;
+}
+
+static int cli_print_channel_body(void *obj, void *arg, int flags) {
+ 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;
+
+ if (!context->output_buffer) {
+ return -1;
+ }
+
+ gettimeofday(&current_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);
+
+ 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: %-7s Exten: %-*.*s CLCID: \"%s\" <%s>\n",
+ indent, "Codec",
+ snapshot->nativeformats,
+ flexwidth, flexwidth,
+ snapshot->exten,
+ snapshot->connected_name,
+ snapshot->connected_number
+ );
+ context->indent_level--;
+ return 0;
+}
+
+static struct ao2_container *cli_get_endpoint_container(struct ast_sorcery *sip_sorcery)
+{
+ return ast_sip_get_endpoints();
+}
+
+static int cli_print_endpoint_header(void *obj, void *arg, int flags)
{
- if (ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands))) {
+ struct ast_sip_cli_context *context = arg;
+ struct ast_sip_cli_formatter_entry *formatter_entry;
+
+ if (!context->output_buffer) {
return -1;
}
+ ast_str_append(&context->output_buffer, 0,
+ " <Endpoint/CID................................................> <State.....> <Channels.>\n");
+
+ if (context->recurse) {
+ context->indent_level++;
+ formatter_entry = ast_sip_lookup_cli_formatter("auth");
+ if (formatter_entry) {
+ formatter_entry->print_header(NULL, context, 0);
+ }
+ formatter_entry = ast_sip_lookup_cli_formatter("aor");
+ if (formatter_entry) {
+ formatter_entry->print_header(NULL, context, 0);
+ }
+ formatter_entry = ast_sip_lookup_cli_formatter("identify");
+ if (formatter_entry) {
+ formatter_entry->print_header(NULL, context, 0);
+ }
+ formatter_entry = ast_sip_lookup_cli_formatter("channel");
+ if (formatter_entry) {
+ formatter_entry->print_header(NULL, context, 0);
+ }
+ context->indent_level--;
+ }
+ return 0;
+}
+
+static int cli_print_endpoint_body(void *obj, void *arg, int flags) {
+ struct ast_sip_endpoint *endpoint = obj;
+ RAII_VAR(struct ast_endpoint_snapshot *, endpoint_snapshot, ast_sip_get_endpoint_snapshot(endpoint), ao2_cleanup);
+ struct ast_sip_cli_context *context = arg;
+ const char *id = ast_sorcery_object_get_id(endpoint);
+ struct ast_sip_cli_formatter_entry *formatter_entry;
+ char *print_name = NULL;
+ int print_name_len;
+ char *number = S_COR(endpoint->id.self.number.valid,
+ endpoint->id.self.number.str, NULL);
+
+ if (!context->output_buffer) {
+ return -1;
+ }
+
+ context->current_endpoint = endpoint;
+
+ if (number) {
+ print_name_len = strlen(id) + strlen(number) + 2;
+ if (!(print_name = alloca(print_name_len))) {
+ return -1;
+ }
+ snprintf(print_name, print_name_len, "%s/%s", id, number);
+ }
+
+ ast_str_append(&context->output_buffer, 0, " %-62s %-12.12s %d of %.0f\n",
+ print_name ? print_name : id,
+ ast_sip_get_device_state(endpoint),
+ endpoint_snapshot->num_channels,
+ (double) endpoint->devicestate_busy_at ? endpoint->devicestate_busy_at :
+ INFINITY
+ );
+
+ if (context->recurse) {
+ context->indent_level++;
+
+ formatter_entry = ast_sip_lookup_cli_formatter("auth");
+ if (formatter_entry) {
+ context->auth_direction = "Out";
+ ast_sip_for_each_auth(&endpoint->outbound_auths, formatter_entry->print_body, context);
+ context->auth_direction = "In";
+ ast_sip_for_each_auth(&endpoint->inbound_auths, formatter_entry->print_body, context);
+ }
+ formatter_entry = ast_sip_lookup_cli_formatter("aor");
+ if (formatter_entry) {
+ ast_sip_for_each_aor(endpoint->aors, formatter_entry->print_body, context);
+ }
+ formatter_entry = ast_sip_lookup_cli_formatter("channel");
+ if (formatter_entry) {
+ ast_sip_for_each_channel(endpoint, formatter_entry->print_body, context);
+ }
+
+ context->indent_level--;
+ }
+
+ if (context->show_details || (context->show_details_only_level_0 && context->indent_level == 0)) {
+ ast_str_append(&context->output_buffer, 0, "\n");
+ ast_sip_cli_print_sorcery_objectset(endpoint, context, 0);
+ }
+
+ return 0;
+}
+
+static struct ast_sip_cli_formatter_entry cli_channel_formatter = {
+ .name = "channel",
+ .print_header = cli_print_channel_header,
+ .print_body = cli_print_channel_body,
+ .get_container = cli_get_channel_container,
+};
+
+static struct ast_sip_cli_formatter_entry cli_endpoint_formatter = {
+ .name = "endpoint",
+ .print_header = cli_print_endpoint_header,
+ .print_body = cli_print_endpoint_body,
+ .get_container = cli_get_endpoint_container,
+};
+
+
+int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_module_info)
+{
if (ast_manager_register_xml(AMI_SHOW_ENDPOINTS, EVENT_FLAG_SYSTEM, ami_show_endpoints) ||
ast_manager_register_xml(AMI_SHOW_ENDPOINT, EVENT_FLAG_SYSTEM, ami_show_endpoint)) {
@@ -1266,6 +1371,11 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
ast_sorcery_apply_config(sip_sorcery, "res_pjsip");
+ ast_sip_initialize_cli(sip_sorcery);
+ ast_sip_register_cli_formatter(&cli_channel_formatter);
+ ast_sip_register_cli_formatter(&cli_endpoint_formatter);
+
+
if (ast_sip_initialize_sorcery_auth(sip_sorcery)) {
ast_log(LOG_ERROR, "Failed to register SIP authentication support\n");
ast_sorcery_unref(sip_sorcery);
@@ -1411,7 +1521,6 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
void ast_res_pjsip_destroy_configuration(void)
{
- ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
ast_manager_unregister(AMI_SHOW_ENDPOINT);
ast_manager_unregister(AMI_SHOW_ENDPOINTS);
ast_sorcery_unref(sip_sorcery);