diff options
author | Matt Jordan <mjordan@digium.com> | 2016-10-20 07:27:21 -0500 |
---|---|---|
committer | Mark Michelson <mmichelson@digium.com> | 2016-11-01 09:43:46 -0500 |
commit | c30d677333028f9768a3913bb73f49936805adc9 (patch) | |
tree | f4f160cc668f006dda96658201be7d2b4a06b33b /res/stasis | |
parent | 3ad4719917c106a79546676ffec22af9a0921eb4 (diff) |
res/stasis: Add CLI commands for displaying/debugging ARI apps
This patch adds three new CLI commands:
- ari show apps: list the registered ARI applications
- ari show app: show detailed information about an ARI application
- ari set debug: dump events being sent to an ARI application
Note that while these CLI commands live in the res_stasis module, we use
the 'ari' family for these commands. This was done as most users of
Asterisk aren't aware of the semantic differences between ARI and
res_stasis, and some 'ari' CLI commands already exist.
ASTERISK-26488 #close
Change-Id: I51ad6ff0cabee0d69db06858c13f18b1c513c9f5
Diffstat (limited to 'res/stasis')
-rw-r--r-- | res/stasis/app.c | 91 | ||||
-rw-r--r-- | res/stasis/app.h | 26 | ||||
-rw-r--r-- | res/stasis/cli.c | 214 | ||||
-rw-r--r-- | res/stasis/cli.h | 43 |
4 files changed, 374 insertions, 0 deletions
diff --git a/res/stasis/app.c b/res/stasis/app.c index 3301d926c..ac316fac0 100644 --- a/res/stasis/app.c +++ b/res/stasis/app.c @@ -30,6 +30,7 @@ #include "messaging.h" #include "asterisk/callerid.h" +#include "asterisk/cli.h" #include "asterisk/stasis_app.h" #include "asterisk/stasis_bridges.h" #include "asterisk/stasis_channels.h" @@ -59,6 +60,8 @@ struct stasis_app { void *data; /*! Subscription model for the application */ enum stasis_app_subscription_model subscription_model; + /*! Whether or not someone wants to see debug messages about this app */ + int debug; /*! Name of the Stasis application */ char name[]; }; @@ -830,6 +833,18 @@ static void bridge_default_handler(void *data, struct stasis_subscription *sub, } } +void app_set_debug(struct stasis_app *app, int debug) +{ + if (!app) { + return; + } + + { + SCOPED_AO2LOCK(lock, app); + app->debug = debug; + } +} + struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model) { RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup); @@ -927,6 +942,7 @@ struct stasis_topic *ast_app_get_topic(struct stasis_app *app) void app_send(struct stasis_app *app, struct ast_json *message) { stasis_app_cb handler; + int debug; char eid[20]; RAII_VAR(void *, data, NULL, ao2_cleanup); @@ -939,6 +955,7 @@ void app_send(struct stasis_app *app, struct ast_json *message) /* Copy off mutable state with lock held */ { SCOPED_AO2LOCK(lock, app); + debug = app->debug; handler = app->handler; if (app->data) { ao2_ref(app->data, +1); @@ -947,6 +964,13 @@ void app_send(struct stasis_app *app, struct ast_json *message) /* Name is immutable; no need to copy */ } + if (debug) { + char *dump = ast_json_dump_string_format(message, AST_JSON_PRETTY); + ast_verb(0, "Dispatching message to Stasis app '%s':\n%s\n", + app->name, dump); + ast_json_free(dump); + } + if (!handler) { ast_verb(3, "Inactive Stasis app '%s' missed message\n", app->name); @@ -1024,6 +1048,73 @@ const char *app_name(const struct stasis_app *app) return app->name; } +static int forwards_filter_by_type(void *obj, void *arg, int flags) +{ + struct app_forwards *forward = obj; + enum forward_type *forward_type = arg; + + if (forward->forward_type == *forward_type) { + return CMP_MATCH; + } + + return 0; +} + +void app_to_cli(const struct stasis_app *app, struct ast_cli_args *a) +{ + struct ao2_iterator *channels; + struct ao2_iterator *endpoints; + struct ao2_iterator *bridges; + struct app_forwards *forward; + enum forward_type forward_type; + + ast_cli(a->fd, "Name: %s\n" + " Debug: %s\n" + " Subscription Model: %s\n", + app->name, + app->debug ? "Yes" : "No", + app->subscription_model == STASIS_APP_SUBSCRIBE_ALL ? + "Global Resource Subscription" : + "Application/Explicit Resource Subscription"); + ast_cli(a->fd, " Subscriptions: %d\n", ao2_container_count(app->forwards)); + + ast_cli(a->fd, " Channels:\n"); + forward_type = FORWARD_CHANNEL; + channels = ao2_callback(app->forwards, OBJ_MULTIPLE, + forwards_filter_by_type, &forward_type); + if (channels) { + while ((forward = ao2_iterator_next(channels))) { + ast_cli(a->fd, " %s (%d)\n", forward->id, forward->interested); + ao2_ref(forward, -1); + } + ao2_iterator_destroy(channels); + } + + ast_cli(a->fd, " Bridges:\n"); + forward_type = FORWARD_BRIDGE; + bridges = ao2_callback(app->forwards, OBJ_MULTIPLE, + forwards_filter_by_type, &forward_type); + if (bridges) { + while ((forward = ao2_iterator_next(bridges))) { + ast_cli(a->fd, " %s (%d)\n", forward->id, forward->interested); + ao2_ref(forward, -1); + } + ao2_iterator_destroy(bridges); + } + + ast_cli(a->fd, " Endpoints:\n"); + forward_type = FORWARD_ENDPOINT; + endpoints = ao2_callback(app->forwards, OBJ_MULTIPLE, + forwards_filter_by_type, &forward_type); + if (endpoints) { + while ((forward = ao2_iterator_next(endpoints))) { + ast_cli(a->fd, " %s (%d)\n", forward->id, forward->interested); + ao2_ref(forward, -1); + } + ao2_iterator_destroy(endpoints); + } +} + struct ast_json *app_to_json(const struct stasis_app *app) { RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); diff --git a/res/stasis/app.h b/res/stasis/app.h index 2c8db1ccd..6ed6a295b 100644 --- a/res/stasis/app.h +++ b/res/stasis/app.h @@ -127,8 +127,26 @@ void app_send(struct stasis_app *app, struct ast_json *message); struct app_forwards; +/*! + * \brief Create a JSON representation of a \c stasis_app + * + * \param app The application + * + * \return \c JSON blob on success + * \return \c NULL on error + */ struct ast_json *app_to_json(const struct stasis_app *app); +struct ast_cli_args; + +/*! + * \brief Dump properties of a \c stasis_app to the CLI + * + * \param app The application + * \param a The CLI arguments + */ +void app_to_cli(const struct stasis_app *app, struct ast_cli_args *a); + /*! * \brief Subscribes an application to a channel. * @@ -282,4 +300,12 @@ char *app_get_replace_channel_app(struct ast_channel *chan); */ int app_send_end_msg(struct stasis_app *app, struct ast_channel *chan); +/*! + * \brief Enable/disable debugging on an application + * + * \param app The app to debug + * \param debug If non-zero, enable debugging. If zero, disable. + */ +void app_set_debug(struct stasis_app *app, int debug); + #endif /* _ASTERISK_RES_STASIS_APP_H */ diff --git a/res/stasis/cli.c b/res/stasis/cli.c new file mode 100644 index 000000000..e6065b0ac --- /dev/null +++ b/res/stasis/cli.c @@ -0,0 +1,214 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2016, Digium, Inc. + * + * Matt Jordan <mjordan@digium.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. + */ + +/*! \file + * + * \brief Stasis CLI commands. + * + * \author Matt Jordan <mjordan@digium.com> + */ + +#include "asterisk.h" + +#include "asterisk/cli.h" +#include "asterisk/astobj2.h" + +#include "cli.h" +#include "app.h" + + +static char *ari_show_apps(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + struct ao2_container *apps; + struct ao2_iterator it_apps; + char *app; + + switch (cmd) { + case CLI_INIT: + e->command = "ari show apps"; + e->usage = + "Usage: ari show apps\n" + " Lists all registered applications.\n" + ; + return NULL; + case CLI_GENERATE: + return NULL; + default: + break; + } + + if (a->argc != 3) { + return CLI_SHOWUSAGE; + } + + apps = stasis_app_get_all(); + if (!apps) { + ast_cli(a->fd, "Unable to retrieve registered applications!\n"); + return CLI_FAILURE; + } + + ast_cli(a->fd, "Application Name \n"); + ast_cli(a->fd, "=========================\n"); + it_apps = ao2_iterator_init(apps, 0); + while ((app = ao2_iterator_next(&it_apps))) { + ast_cli(a->fd, "%-25.25s\n", app); + ao2_ref(app, -1); + } + + ao2_iterator_destroy(&it_apps); + ao2_ref(apps, -1); + + return CLI_SUCCESS; +} + +struct app_complete { + /*! Nth app to search for */ + int state; + /*! Which app currently on */ + int which; +}; + +static int complete_ari_app_search(void *obj, void *arg, void *data, int flags) +{ + struct app_complete *search = data; + + if (++search->which > search->state) { + return CMP_MATCH; + } + return 0; +} + +static char *complete_ari_app(struct ast_cli_args *a) +{ + RAII_VAR(struct ao2_container *, apps, stasis_app_get_all(), ao2_cleanup); + RAII_VAR(char *, app, NULL, ao2_cleanup); + + struct app_complete search = { + .state = a->n, + }; + + if (!apps) { + ast_cli(a->fd, "Error getting ARI applications\n"); + return CLI_FAILURE; + } + + app = ao2_callback_data(apps, + ast_strlen_zero(a->word) ? 0 : OBJ_PARTIAL_KEY, + complete_ari_app_search, (char*)a->word, &search); + + return app ? ast_strdup(app) : NULL; +} + +static char *complete_ari_show_app(struct ast_cli_args *a) +{ + if (a->pos == 3) { + return complete_ari_app(a); + } + + return NULL; +} + +static char *ari_show_app(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + void *app; + + switch (cmd) { + case CLI_INIT: + e->command = "ari show app"; + e->usage = + "Usage: ari show app <application>\n" + " Provide detailed information about a registered application.\n" + ; + return NULL; + case CLI_GENERATE: + return complete_ari_show_app(a); + default: + break; + } + + if (a->argc != 4) { + return CLI_SHOWUSAGE; + } + + app = stasis_app_get_by_name(a->argv[3]); + if (!app) { + return CLI_FAILURE; + } + + app_to_cli(app, a); + + ao2_ref(app, -1); + + return CLI_SUCCESS; +} + +static char *ari_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + void *app; + int debug; + + switch (cmd) { + case CLI_INIT: + e->command = "ari set debug"; + e->usage = + "Usage: ari set debug <application> <on|off>\n" + " Enable or disable debugging on a specific application.\n" + ; + return NULL; + case CLI_GENERATE: + return complete_ari_show_app(a); + default: + break; + } + + if (a->argc != 5) { + return CLI_SHOWUSAGE; + } + + app = stasis_app_get_by_name(a->argv[3]); + if (!app) { + return CLI_FAILURE; + } + + debug = !strcmp(a->argv[4], "on"); + app_set_debug(app, debug); + ast_cli(a->fd, "Debugging on '%s' %s\n", + app_name(app), + debug ? "enabled" : "disabled"); + + ao2_ref(app, -1); + + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_ari[] = { + AST_CLI_DEFINE(ari_show_apps, "List registered ARI applications"), + AST_CLI_DEFINE(ari_show_app, "Display details of a registered ARI application"), + AST_CLI_DEFINE(ari_set_debug, "Enable/disable debugging of an ARI application"), +}; + + +int cli_init(void) +{ + return ast_cli_register_multiple(cli_ari, ARRAY_LEN(cli_ari)); +} + +void cli_cleanup(void) +{ + ast_cli_unregister_multiple(cli_ari, ARRAY_LEN(cli_ari)); +} diff --git a/res/stasis/cli.h b/res/stasis/cli.h new file mode 100644 index 000000000..49235c7b3 --- /dev/null +++ b/res/stasis/cli.h @@ -0,0 +1,43 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2016, Digium, Inc. + * + * Matt Jordan <mjordan@digium.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 _ASTERISK_RES_STASIS_CLI_H +#define _ASTERISK_RES_STASIS_CLI_H + +/*! \file + * + * \brief Internal API for Stasis application CLI commands + * + * \author Matt Jordan <mjordan@digium.com> + * \since 13.13.0 + */ + +/*! + * \brief Initialize the CLI commands + * + * \retval 0 on success + * \retval non-zero on error + */ +int cli_init(void); + +/*! + * \brief Cleanup the CLI commands + */ +void cli_cleanup(void); + +#endif /* _ASTERISK_RES_STASIS_CLI_H */ |