summaryrefslogtreecommitdiff
path: root/res/stasis
diff options
context:
space:
mode:
authorMatt Jordan <mjordan@digium.com>2016-10-20 07:27:21 -0500
committerMark Michelson <mmichelson@digium.com>2016-11-01 09:43:46 -0500
commitc30d677333028f9768a3913bb73f49936805adc9 (patch)
treef4f160cc668f006dda96658201be7d2b4a06b33b /res/stasis
parent3ad4719917c106a79546676ffec22af9a0921eb4 (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.c91
-rw-r--r--res/stasis/app.h26
-rw-r--r--res/stasis/cli.c214
-rw-r--r--res/stasis/cli.h43
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 */