diff --git a/CREDITS b/CREDITS index a7673963cafa4b634531dfc69b1df56963c2f4e3..65609768b0b778f184bac1bc95d1862af2a87d9e 100644 --- a/CREDITS +++ b/CREDITS @@ -276,6 +276,8 @@ * Andrew "lathama" Latham <lathama at gmail dot com> Doxygen, HTTP-Static, Phoneprov, make update + * George Joseph - PJSIP CLI commands, PJSIP_HEADER dialplan function + === OTHER CONTRIBUTIONS === We'd like to thank the following for their listed contributions. diff --git a/include/asterisk/config.h b/include/asterisk/config.h index d669e7a81e8ec78c469d825d339bb51a0d5d3a5c..6d341db612d8372cc877f20fbe5b5a9f719eb836 100644 --- a/include/asterisk/config.h +++ b/include/asterisk/config.h @@ -691,6 +691,7 @@ void ast_include_rename(struct ast_config *conf, const char *from_file, const ch void ast_variable_append(struct ast_category *category, struct ast_variable *variable); void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line); int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line); +struct ast_variable *ast_variable_list_sort(struct ast_variable *start); /*! * \brief Update variable value within a config diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index b3701c0445d8128b497ebd436d17c15836dd00cc..f4f9ba7c71585bd9ea27b047179a9efe26389924 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -40,6 +40,9 @@ #include "asterisk/rtp_engine.h" /* Needed for AST_VECTOR macro */ #include "asterisk/vector.h" +/* Needed for ast_sip_for_each_channel_snapshot struct */ +#include "asterisk/stasis_channels.h" +#include "asterisk/stasis_endpoints.h" /* Forward declarations of PJSIP stuff */ struct pjsip_rx_data; @@ -213,6 +216,17 @@ struct ast_sip_aor { struct ao2_container *permanent_contacts; }; +/*! + * \brief Aor/Contact pair used for ast_sip_for_each_contact callback. + */ +struct ast_sip_aor_contact_pair { + SORCERY_OBJECT(details); + /*! Aor */ + struct ast_sip_aor *aor; + /*! Contact */ + struct ast_sip_contact *contact; +}; + /*! * \brief DTMF modes for SIP endpoints */ @@ -1546,13 +1560,6 @@ void *ast_sip_dict_set(pj_pool_t* pool, void *ht, #define ast_sip_mod_data_set(pool, mod_data, id, key, val) \ mod_data[id] = ast_sip_dict_set(pool, mod_data[id], key, val) -/*! - * \brief Function pointer for contact callbacks. - */ -typedef int (*on_contact_t)(const struct ast_sip_aor *aor, - const struct ast_sip_contact *contact, - int last, void *arg); - /*! * \brief For every contact on an AOR call the given 'on_contact' handler. * @@ -1561,21 +1568,18 @@ typedef int (*on_contact_t)(const struct ast_sip_aor *aor, * \param arg user data passed to handler * \retval 0 Success, non-zero on failure */ -int ast_sip_for_each_contact(const struct ast_sip_aor *aor, - on_contact_t on_contact, void *arg); +int ast_sip_for_each_contact(struct ast_sip_aor *aor, + ao2_callback_fn on_contact, void *arg); /*! * \brief Handler used to convert a contact to a string. * - * \param aor the aor containing a list of contacts to iterate - * \param contact the contact to convert - * \param last is this the last contact + * \param object the ast_sip_aor_contact_pair containing a list of contacts to iterate and the contact * \param arg user data passed to handler + * \param flags * \retval 0 Success, non-zero on failure */ -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); /*! * \brief For every aor in the comma separated aors string call the @@ -1696,4 +1700,47 @@ int ast_sip_format_endpoint_ami(struct ast_sip_endpoint *endpoint, int ast_sip_format_auths_ami(const struct ast_sip_auth_vector *auths, struct ast_sip_ami *ami); +/*! + * \brief Retrieve the endpoint snapshot for an endpoint + * + * \param endpoint The endpoint whose snapshot is to be retreieved. + * \retval The endpoint snapshot + */ +struct ast_endpoint_snapshot *ast_sip_get_endpoint_snapshot( + const struct ast_sip_endpoint *endpoint); + +/*! + * \brief Retrieve the device state for an endpoint. + * + * \param endpoint The endpoint whose state is to be retrieved. + * \retval The device state. + */ +const char *ast_sip_get_device_state(const struct ast_sip_endpoint *endpoint); + +/*! + * \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, + ao2_callback_fn on_channel_snapshot, + void *arg); + +/*! + * \brief For every channel snapshot on an endpoint all the given + * 'on_channel_snapshot' handler. + * + * \param endpoint 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(const struct ast_sip_endpoint *endpoint, + ao2_callback_fn on_channel_snapshot, + void *arg); + #endif /* _RES_PJSIP_H */ diff --git a/include/asterisk/res_pjsip_cli.h b/include/asterisk/res_pjsip_cli.h new file mode 100644 index 0000000000000000000000000000000000000000..cd7d81ed299756bc9607d5e1b48cc1d6ef9445d7 --- /dev/null +++ b/include/asterisk/res_pjsip_cli.h @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#ifndef RES_PJSIP_CLI_H_ +#define RES_PJSIP_CLI_H_ + +#define CLI_HEADER_FILLER ".........................................................................................." +#define CLI_DETAIL_FILLER " " +#define CLI_MAX_WIDTH 90 +#define CLI_LAST_TABSTOP 62 +#define CLI_MAX_TITLE_NAME 8 +#define CLI_INDENT_TO_SPACES(x) ((x * 2) + 1 + CLI_MAX_TITLE_NAME) + +/* + * \brief CLI Formatter Context + */ +struct ast_sip_cli_context { + int peers_mon_online; + int peers_mon_offline; + int peers_unmon_offline; + int peers_unmon_online; + struct ast_str *output_buffer; + const struct ast_cli_args *a; + const struct ast_sip_endpoint *current_endpoint; + const struct ast_sip_auth *current_auth; + const struct ast_sip_aor *current_aor; + char *auth_direction; + unsigned int print_flags; + int indent_level; + unsigned show_details : 1; + unsigned recurse : 1; + unsigned show_details_only_level_0 : 1; +}; + +/* + * \brief CLI Formatter Registry Entry + */ +struct ast_sip_cli_formatter_entry { + const char *name; + ao2_callback_fn *print_header; + ao2_callback_fn *print_body; + struct ao2_container *(* get_container)(struct ast_sorcery *); +}; + +/*! + * \brief Registers a CLI formatter. + * + * \param name The name of the formatter, usually the sorcery object type. + * \param formatter An ao2_callback_fn that outputs the formatted data. + * \retval 0 Success, non-zero on failure + */ +int ast_sip_register_cli_formatter(struct ast_sip_cli_formatter_entry *formatter); + +/*! + * \brief Unregisters a CLI formatter. + * + * \param name The name of the formatter, usually the sorcery object type. + * \retval 0 Success, non-zero on failure + */ +int ast_sip_unregister_cli_formatter(struct ast_sip_cli_formatter_entry *formatter); + +/*! + * \brief Looks up a CLI formatter by type. + * + * \param name The name of the formatter, usually the sorcery object type. + * \retval Pointer to formatter entry structure + */ +struct ast_sip_cli_formatter_entry *ast_sip_lookup_cli_formatter(const char *name); + +/*! + * \brief Prints a sorcery object's ast_variable list + * + * \param obj The sorcery object + * \param arg The ast_sip_cli_context. + * \retval 0 Success, non-zero on failure + */ +int ast_sip_cli_print_sorcery_objectset(void *obj, void *arg, int flags); + + +#endif /* RES_PJSIP_CLI_H_ */ diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h index 1402c583394ea1829d5e5914ace83f97022fa39d..47ad5e40f70bc9fafa304124cb6d110f8c05066b 100644 --- a/include/asterisk/sorcery.h +++ b/include/asterisk/sorcery.h @@ -823,6 +823,12 @@ const char *ast_sorcery_object_get_extended(const void *object, const char *name */ int ast_sorcery_object_set_extended(const void *object, const char *name, const char *value); +/*! + * \brief Sorcery object comparator based on id. + */ +int ast_sorcery_object_id_compare(const void *obj_left, const void *obj_right, int flags); + + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/main/channel.c b/main/channel.c index 4789943c042269d529911c9066672714d4ebcb17..9ff3d0746ee05efb18e31ae5e3d666f06980b582 100644 --- a/main/channel.c +++ b/main/channel.c @@ -733,6 +733,8 @@ const char *ast_state2str(enum ast_channel_state state) return "Dialing Offhook"; case AST_STATE_PRERING: return "Pre-ring"; + case AST_STATE_MUTE: + return "Mute"; default: if (!(buf = ast_threadstorage_get(&state2str_threadbuf, STATE2STR_BUFSIZE))) return "Unknown"; diff --git a/main/config.c b/main/config.c index b8356a9bc9ebd224e239e7b23cfa9a19e269e36d..c6458caafbb55dfd6d8a2662ac646b34bf48d5a4 100644 --- a/main/config.c +++ b/main/config.c @@ -70,6 +70,7 @@ static char *extconfig_conf = "extconfig.conf"; static struct ao2_container *cfg_hooks; static void config_hook_exec(const char *filename, const char *module, struct ast_config *cfg); +inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2); /*! \brief Structure to keep comments for rewriting configuration files */ struct ast_comment { @@ -581,6 +582,39 @@ struct ast_variable *ast_variable_browse(const struct ast_config *config, const return (cat) ? cat->root : NULL; } +inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2) +{ + l1->next = l2->next; + l2->next = l1; + return l2; +} + +struct ast_variable *ast_variable_list_sort(struct ast_variable *start) +{ + struct ast_variable *p, *q, *top; + int changed = 1; + top = ast_calloc(1, sizeof(struct ast_variable)); + top->next = start; + if (start != NULL && start->next != NULL) { + while (changed) { + changed = 0; + q = top; + p = top->next; + while (p->next != NULL) { + if (p->next != NULL && strcmp(p->name, p->next->name) > 0) { + q->next = variable_list_switch(p, p->next); + + changed = 1; + } + q = p; + if (p->next != NULL) + p = p->next; + } + } + } + return top->next; +} + const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var) { const char *tmp; diff --git a/main/sorcery.c b/main/sorcery.c index c7e1a03f36f5bd152bb2b3c66cc699beadfcea9f..1753ba198a0ac4d659a2a9aaba18f37ab4ae9805 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -1583,3 +1583,11 @@ void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char * ao2_callback(object_type->observers, OBJ_NODATA | OBJ_UNLINK, sorcery_observer_remove, cbs); } + +int ast_sorcery_object_id_compare(const void *obj_left, const void *obj_right, int flags) +{ + if (!obj_left || !obj_right) { + return 0; + } + return strcmp(ast_sorcery_object_get_id(obj_left), ast_sorcery_object_get_id(obj_right)); +} diff --git a/res/res_pjsip/config_auth.c b/res/res_pjsip/config_auth.c index 047e9337d535f81f69a3c664706fe249b0e5a8b4..d7e759f5dcc9b57f8ee645a6ab215e4b456795b2 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 368bdd04a6fe9f353182864d85150fb4289fb22c..a2d1c6eb468b078fe9a63898a1c2d8280cfbd3be 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; @@ -86,25 +84,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. * @@ -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 52f69cfd8ed54e06714dfdcce6bf8345b048aa76..3bd4dea104e7dae4cd49c46062fe4a81a11d4c11 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 0000000000000000000000000000000000000000..5f8157d2d8dbcc183215ac44c9bb066b8f2db1d8 --- /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 010eeb6970a4ea09ba145f61051e0bcb9ad0864f..c8e8e3bf13c966b79ef40ca4cbe5c2229c717f66 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(¤t_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); diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c index fe2948a1635b425497f8dd2fe3a647f0fa4eaeb3..34c8f2c393f6278ec2065eb884026371c840d4b1 100644 --- a/res/res_pjsip_endpoint_identifier_ip.c +++ b/res/res_pjsip_endpoint_identifier_ip.c @@ -255,6 +255,7 @@ static int reload_module(void) static int unload_module(void) { + ast_sip_unregister_endpoint_formatter(&endpoint_identify_formatter); ast_sip_unregister_endpoint_identifier(&ip_identifier); return 0; } diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c index 9771bdc98ed41a00984cc1f8d777ee5b6588ad58..89e8cd141319e90dc4ca77976e8fa2d34f97a4fe 100644 --- a/res/res_pjsip_registrar.c +++ b/res/res_pjsip_registrar.c @@ -590,11 +590,9 @@ static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata) /* function pointer to callback needs to be within the module in order to avoid problems with an undefined symbol */ -static int sip_contact_to_str(const struct ast_sip_aor *aor, - const struct ast_sip_contact *contact, - int last, void *arg) +static int sip_contact_to_str(void *acp, void *arg, int flags) { - return ast_sip_contact_to_str(aor, contact, last, arg); + return ast_sip_contact_to_str(acp, arg, flags); } static int ami_registrations_aor(void *obj, void *arg, int flags)