From a8aee0bbdb09f5cfb49802f0c3c09e741814045c Mon Sep 17 00:00:00 2001 From: George Joseph <george.joseph@fairview5.com> Date: Tue, 20 Oct 2015 15:02:30 -0600 Subject: [PATCH] res_pjsip: Add "like" processing to pjsip list and show commands Add the ability to filter output from pjsip list and show commands using the "like" predicate like chan_sip. For endpoints, aors, auths, registrations, identifyies and transports, the modification was a simple change of an ast_sorcery_retrieve_by_fields call to ast_sorcery_retrieve_by_regex. For channels and contacts a little more work had to be done because neither of those objects are true sorcery objects. That was just removing the non-matching object from the final container. Of course, a little extra plumbing in the common pjsip_cli code was needed to parse the "like" and pass the regex to the get_container callbacks. Some of the get_container code in res_pjsip_endpoint_identifier was also refactored for simplicity. ASTERISK-25477 #close Reported by: Bryant Zimmerman Tested by: George Joseph Change-Id: I646d9326b778aac26bb3e2bcd7fa1346d24434f1 --- CHANGES | 5 +++ include/asterisk/res_pjsip_cli.h | 2 +- res/res_pjsip/config_auth.c | 15 +++---- res/res_pjsip/config_transport.c | 16 ++++---- res/res_pjsip/location.c | 55 ++++++++++++++++++------- res/res_pjsip/pjsip_cli.c | 15 ++++++- res/res_pjsip/pjsip_configuration.c | 54 ++++++++++++++++++------- res/res_pjsip_endpoint_identifier_ip.c | 56 ++++++++------------------ res/res_pjsip_outbound_registration.c | 15 ++++--- 9 files changed, 143 insertions(+), 90 deletions(-) diff --git a/CHANGES b/CHANGES index f4cbe629e8..1dff2046bc 100644 --- a/CHANGES +++ b/CHANGES @@ -205,6 +205,11 @@ Dialplan Functions event instead. +res_pjsip +------------------ + * The ability to use "like" has been added to the pjsip list and show + CLI commands. For instance: CLI> pjsip list endpoints like abc + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13.5.0 to Asterisk 13.6.0 ------------ ------------------------------------------------------------------------------ diff --git a/include/asterisk/res_pjsip_cli.h b/include/asterisk/res_pjsip_cli.h index 44979b701e..c2535218ac 100644 --- a/include/asterisk/res_pjsip_cli.h +++ b/include/asterisk/res_pjsip_cli.h @@ -61,7 +61,7 @@ struct ast_sip_cli_formatter_entry { /*! The callback used to print the details of the object. */ ao2_callback_fn *print_body; /*! The function used to retrieve a container of all objects of this type. */ - struct ao2_container *(* get_container)(void); + struct ao2_container *(* get_container)(const char *regex); /*! The function used to iterate over a container of objects. */ int (* iterate)(void *container, ao2_callback_fn callback, void *args); /*! The function used to retrieve a specific object from it's container. */ diff --git a/res/res_pjsip/config_auth.c b/res/res_pjsip/config_auth.c index 773889c7a7..9160e6709c 100644 --- a/res/res_pjsip/config_auth.c +++ b/res/res_pjsip/config_auth.c @@ -195,13 +195,12 @@ static struct ast_sip_endpoint_formatter endpoint_auth_formatter = { .format_ami = format_ami_endpoint_auth }; -static struct ao2_container *cli_get_container(void) +static struct ao2_container *cli_get_container(const char *regex) { RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); struct ao2_container *s_container; - container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "auth", - AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); + container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "auth", regex); if (!container) { return NULL; } @@ -272,12 +271,14 @@ static int cli_print_body(void *obj, void *arg, int flags) static struct ast_cli_entry cli_commands[] = { AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Auths", .command = "pjsip list auths", - .usage = "Usage: pjsip list auths\n" - " List the configured PJSIP Auths\n"), + .usage = "Usage: pjsip list auths [ like <pattern> ]\n" + " List the configured PJSIP Auths\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Auths", .command = "pjsip show auths", - .usage = "Usage: pjsip show auths\n" - " Show the configured PJSIP Auths\n"), + .usage = "Usage: pjsip show auths [ like <pattern> ]\n" + " Show the configured PJSIP Auths\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Auth", .command = "pjsip show auth", .usage = "Usage: pjsip show auth <id>\n" diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c index 73030b1559..e9986612c6 100644 --- a/res/res_pjsip/config_transport.c +++ b/res/res_pjsip/config_transport.c @@ -633,13 +633,13 @@ static int tos_to_str(const void *obj, const intptr_t *args, char **buf) return 0; } -static struct ao2_container *cli_get_container(void) +static struct ao2_container *cli_get_container(const char *regex) { RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); struct ao2_container *s_container; - container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", - AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); + container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "transport", + regex); if (!container) { return NULL; } @@ -720,12 +720,14 @@ static struct ast_cli_entry cli_commands[] = { AST_CLI_DEFINE(handle_pjsip_list_ciphers, "List available OpenSSL cipher names"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Transports", .command = "pjsip list transports", - .usage = "Usage: pjsip list transports\n" - " List the configured PJSIP Transports\n"), + .usage = "Usage: pjsip list transports [ like <pattern> ]\n" + " List the configured PJSIP Transports\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Transports", .command = "pjsip show transports", - .usage = "Usage: pjsip show transports\n" - " Show the configured PJSIP Transport\n"), + .usage = "Usage: pjsip show transports [ like <pattern> ]\n" + " Show the configured PJSIP Transport\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Transport", .command = "pjsip show transport", .usage = "Usage: pjsip show transport <id>\n" diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c index 331d839a1e..b6f88d20d4 100644 --- a/res/res_pjsip/location.c +++ b/res/res_pjsip/location.c @@ -594,13 +594,12 @@ struct ast_sip_endpoint_formatter endpoint_aor_formatter = { .format_ami = format_ami_endpoint_aor }; -static struct ao2_container *cli_aor_get_container(void) +static struct ao2_container *cli_aor_get_container(const char *regex) { RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); struct ao2_container *s_container; - container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "aor", - AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); + container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "aor", regex); if (!container) { return NULL; } @@ -698,12 +697,25 @@ static int cli_contact_iterate(void *container, ao2_callback_fn callback, void * return ast_sip_for_each_contact(container, callback, args); } -static struct ao2_container *cli_contact_get_container(void) +static int cli_filter_contacts(void *obj, void *arg, int flags) +{ + struct ast_sip_contact_wrapper *wrapper = obj; + regex_t *regexbuf = arg; + + if (!regexec(regexbuf, wrapper->contact_id, 0, NULL, 0)) { + return 0; + } + + return CMP_MATCH; +} + +static struct ao2_container *cli_contact_get_container(const char *regex) { RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup); struct ao2_container *child_container; + regex_t regexbuf; - parent_container = cli_aor_get_container(); + parent_container = cli_aor_get_container(""); if (!parent_container) { return NULL; } @@ -716,12 +728,23 @@ static struct ao2_container *cli_contact_get_container(void) ao2_callback(parent_container, OBJ_NODATA, cli_aor_gather_contacts, child_container); + if (!ast_strlen_zero(regex)) { + if (regcomp(®exbuf, regex, REG_EXTENDED | REG_NOSUB)) { + ao2_ref(child_container, -1); + return NULL; + } + ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_contacts, ®exbuf); + regfree(®exbuf); + } + return child_container; } static void *cli_contact_retrieve_by_id(const char *id) { - return ao2_find(cli_contact_get_container(), id, OBJ_KEY | OBJ_NOLOCK); + RAII_VAR(struct ao2_container *, container, cli_contact_get_container(""), ao2_cleanup); + + return ao2_find(container, id, OBJ_KEY | OBJ_NOLOCK); } static int cli_contact_print_header(void *obj, void *arg, int flags) @@ -858,12 +881,14 @@ static int cli_aor_print_body(void *obj, void *arg, int flags) static struct ast_cli_entry cli_commands[] = { AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Aors", .command = "pjsip list aors", - .usage = "Usage: pjsip list aors\n" - " List the configured PJSIP Aors\n"), + .usage = "Usage: pjsip list aors [ like <pattern> ]\n" + " List the configured PJSIP Aors\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Aors", .command = "pjsip show aors", - .usage = "Usage: pjsip show aors\n" - " Show the configured PJSIP Aors\n"), + .usage = "Usage: pjsip show aors [ like <pattern> ]\n" + " Show the configured PJSIP Aors\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Aor", .command = "pjsip show aor", .usage = "Usage: pjsip show aor <id>\n" @@ -871,12 +896,14 @@ static struct ast_cli_entry cli_commands[] = { AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Contacts", .command = "pjsip list contacts", - .usage = "Usage: pjsip list contacts\n" - " List the configured PJSIP contacts\n"), + .usage = "Usage: pjsip list contacts [ like <pattern> ]\n" + " List the configured PJSIP contacts\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Contacts", .command = "pjsip show contacts", - .usage = "Usage: pjsip show contacts\n" - " Show the configured PJSIP contacts\n"), + .usage = "Usage: pjsip show contacts [ like <pattern> ]\n" + " Show the configured PJSIP contacts\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Contact", .command = "pjsip show contact", .usage = "Usage: pjsip show contact\n" diff --git a/res/res_pjsip/pjsip_cli.c b/res/res_pjsip/pjsip_cli.c index 16df3f59c8..bbd0ac4b87 100644 --- a/res/res_pjsip/pjsip_cli.c +++ b/res/res_pjsip/pjsip_cli.c @@ -125,6 +125,7 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_ const char *cmd2; const char *object_id; char formatter_type[64]; + const char *regex; struct ast_sip_cli_context context = { .indent_level = 0, @@ -162,6 +163,18 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_ is_container = 1; } + if (cmd != CLI_GENERATE + && is_container + && a->argc >= 4 + && strcmp(object_id, "like") == 0) { + if (ast_strlen_zero(a->argv[4])) { + return CLI_SHOWUSAGE; + } + regex = a->argv[4]; + } else { + regex = ""; + } + if (cmd == CLI_GENERATE && (is_container || a->argc > 4 @@ -187,7 +200,7 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_ " =========================================================================================\n\n"); if (is_container || cmd == CLI_GENERATE) { - container = formatter_entry->get_container(); + container = formatter_entry->get_container(regex); if (!container) { ast_cli(a->fd, "No container returned for object type %s.\n", formatter_type); diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 57793c9037..aa753f49b2 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -1395,13 +1395,12 @@ static int ami_show_endpoints(struct mansession *s, const struct message *m) return 0; } -static struct ao2_container *cli_endpoint_get_container(void) +static struct ao2_container *cli_endpoint_get_container(const char *regex) { RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); struct ao2_container *s_container; - container = ast_sorcery_retrieve_by_fields(sip_sorcery, "endpoint", - AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); + container = ast_sorcery_retrieve_by_regex(sip_sorcery, "endpoint", regex); if (!container) { return NULL; } @@ -1515,12 +1514,26 @@ static int cli_endpoint_gather_channels(void *obj, void *arg, int flags) return 0; } -static struct ao2_container *cli_channel_get_container(void) +static int cli_filter_channels(void *obj, void *arg, int flags) +{ + struct ast_channel_snapshot *channel = obj; + regex_t *regexbuf = arg; + + if (!regexec(regexbuf, channel->name, 0, NULL, 0) + || !regexec(regexbuf, channel->appl, 0, NULL, 0)) { + return 0; + } + + return CMP_MATCH; +} + +static struct ao2_container *cli_channel_get_container(const char *regex) { RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup); struct ao2_container *child_container; + regex_t regexbuf; - parent_container = cli_endpoint_get_container(); + parent_container = cli_endpoint_get_container(""); if (!parent_container) { return NULL; } @@ -1532,6 +1545,15 @@ static struct ao2_container *cli_channel_get_container(void) ao2_callback(parent_container, OBJ_NODATA, cli_endpoint_gather_channels, child_container); + if (!ast_strlen_zero(regex)) { + if (regcomp(®exbuf, regex, REG_EXTENDED | REG_NOSUB)) { + ao2_ref(child_container, -1); + return NULL; + } + ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_channels, ®exbuf); + regfree(®exbuf); + } + return child_container; } @@ -1544,7 +1566,7 @@ static const char *cli_channel_get_id(const void *obj) static void *cli_channel_retrieve_by_id(const char *id) { - RAII_VAR(struct ao2_container *, container, cli_channel_get_container(), ao2_cleanup); + RAII_VAR(struct ao2_container *, container, cli_channel_get_container(""), ao2_cleanup); return ao2_find(container, id, OBJ_KEY | OBJ_NOLOCK); } @@ -1746,12 +1768,14 @@ static int cli_endpoint_print_body(void *obj, void *arg, int flags) static struct ast_cli_entry cli_commands[] = { AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Channels", .command = "pjsip list channels", - .usage = "Usage: pjsip list channels\n" - " List the active PJSIP channels\n"), + .usage = "Usage: pjsip list channels [ like <pattern> ]\n" + " List the active PJSIP channels\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channels", .command = "pjsip show channels", - .usage = "Usage: pjsip show channels\n" - " List(detailed) the active PJSIP channels\n"), + .usage = "Usage: pjsip show channels [ like <pattern> ]\n" + " List(detailed) the active PJSIP channels\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Channel", .command = "pjsip show channel", .usage = "Usage: pjsip show channel\n" @@ -1759,12 +1783,14 @@ static struct ast_cli_entry cli_commands[] = { AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Endpoints", .command = "pjsip list endpoints", - .usage = "Usage: pjsip list endpoints\n" - " List the configured PJSIP endpoints\n"), + .usage = "Usage: pjsip list endpoints [ like <pattern> ]\n" + " List the configured PJSIP endpoints\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Endpoints", .command = "pjsip show endpoints", - .usage = "Usage: pjsip show endpoints\n" - " List(detailed) the configured PJSIP endpoints\n"), + .usage = "Usage: pjsip show endpoints [ like <pattern> ]\n" + " List(detailed) the configured PJSIP endpoints\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Endpoint", .command = "pjsip show endpoint", .usage = "Usage: pjsip show endpoint <id>\n" diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c index bbf340761b..39d7573787 100644 --- a/res/res_pjsip_endpoint_identifier_ip.c +++ b/res/res_pjsip_endpoint_identifier_ip.c @@ -298,13 +298,6 @@ struct ast_sip_endpoint_formatter endpoint_identify_formatter = { .format_ami = format_ami_endpoint_identify }; -static int cli_populate_container(void *obj, void *arg, int flags) -{ - ao2_link(arg, obj); - - return 0; -} - static int cli_iterator(void *container, ao2_callback_fn callback, void *args) { const struct ast_sip_endpoint *endpoint = container; @@ -328,47 +321,28 @@ static int cli_iterator(void *container, ao2_callback_fn callback, void *args) return 0; } -static int cli_endpoint_gather_identifies(void *obj, void *arg, int flags) +static struct ao2_container *cli_get_container(const char *regex) { - struct ast_sip_endpoint *endpoint = obj; - struct ao2_container *container = arg; - - cli_iterator(endpoint, cli_populate_container, container); - - return 0; -} + RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); + struct ao2_container *s_container; -static struct ao2_container *cli_get_container(void) -{ - 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(ast_sip_get_sorcery(), "endpoint", - AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); - if (!parent_container) { + container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "identify", regex); + if (!container) { return NULL; } - s_parent_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, + s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, ast_sorcery_object_id_sort, ast_sorcery_object_id_compare); - if (!s_parent_container) { - return NULL; - } - - if (ao2_container_dup(s_parent_container, parent_container, 0)) { + if (!s_container) { return NULL; } - child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, - ast_sorcery_object_id_sort, ast_sorcery_object_id_compare); - if (!child_container) { + if (ao2_container_dup(s_container, container, 0)) { + ao2_ref(s_container, -1); return NULL; } - ao2_callback(s_parent_container, OBJ_NODATA, cli_endpoint_gather_identifies, child_container); - - return child_container; + return s_container; } static void *cli_retrieve_by_id(const char *id) @@ -461,12 +435,14 @@ static char *my_cli_traverse_objects(struct ast_cli_entry *e, int cmd, static struct ast_cli_entry cli_identify[] = { AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Identifies", .command = "pjsip list identifies", - .usage = "Usage: pjsip list identifies\n" - " List the configured PJSIP Identifies\n"), + .usage = "Usage: pjsip list identifies [ like <pattern> ]\n" + " List the configured PJSIP Identifies\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identifies", .command = "pjsip show identifies", - .usage = "Usage: pjsip show identifies\n" - " Show the configured PJSIP Identifies\n"), + .usage = "Usage: pjsip show identifies [ like <pattern> ]\n" + " Show the configured PJSIP Identifies\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Identify", .command = "pjsip show identify", .usage = "Usage: pjsip show identify <id>\n" diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 038cc0b200..7ff5f1619b 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -1639,11 +1639,12 @@ static int ami_show_outbound_registrations(struct mansession *s, return 0; } -static struct ao2_container *cli_get_container(void) +static struct ao2_container *cli_get_container(const char *regex) { - RAII_VAR(struct ao2_container *, container, get_registrations(), ao2_cleanup); + RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); struct ao2_container *s_container; + container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "registration", regex); if (!container) { return NULL; } @@ -1747,12 +1748,14 @@ static struct ast_cli_entry cli_outbound_registration[] = { AST_CLI_DEFINE(cli_register, "Registers an outbound registration target"), AST_CLI_DEFINE(my_cli_traverse_objects, "List PJSIP Registrations", .command = "pjsip list registrations", - .usage = "Usage: pjsip list registrations\n" - " List the configured PJSIP Registrations\n"), + .usage = "Usage: pjsip list registrations [ like <pattern> ]\n" + " List the configured PJSIP Registrations\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registrations", .command = "pjsip show registrations", - .usage = "Usage: pjsip show registrations\n" - " Show the configured PJSIP Registrations\n"), + .usage = "Usage: pjsip show registrations [ like <pattern> ]\n" + " Show the configured PJSIP Registrations\n" + " Optional regular expression pattern is used to filter the list.\n"), AST_CLI_DEFINE(my_cli_traverse_objects, "Show PJSIP Registration", .command = "pjsip show registration", .usage = "Usage: pjsip show registration <id>\n"