1
0
mirror of https://gitlab.xiph.org/xiph/icecast-server.git synced 2024-12-04 14:46:30 -05:00

Feature: Added JSON output for admin commands listclients, listmounts, streamlist, moveclients, and updatemetadata

This commit is contained in:
Philipp Schafft 2020-10-11 08:18:55 +00:00
parent 25f23c4192
commit 5e9feb611a
2 changed files with 45 additions and 10 deletions

View File

@ -74,6 +74,7 @@
#define METADATA_JSON_REQUEST "metadata.json" #define METADATA_JSON_REQUEST "metadata.json"
#define LISTCLIENTS_RAW_REQUEST "listclients" #define LISTCLIENTS_RAW_REQUEST "listclients"
#define LISTCLIENTS_HTML_REQUEST "listclients.xsl" #define LISTCLIENTS_HTML_REQUEST "listclients.xsl"
#define LISTCLIENTS_JSON_REQUEST "listclients.json"
#define STATS_RAW_REQUEST "stats" #define STATS_RAW_REQUEST "stats"
#define STATS_HTML_REQUEST "stats.xsl" #define STATS_HTML_REQUEST "stats.xsl"
#define STATS_JSON_REQUEST "stats.json" #define STATS_JSON_REQUEST "stats.json"
@ -82,11 +83,14 @@
#define QUEUE_RELOAD_JSON_REQUEST "reloadconfig.json" #define QUEUE_RELOAD_JSON_REQUEST "reloadconfig.json"
#define LISTMOUNTS_RAW_REQUEST "listmounts" #define LISTMOUNTS_RAW_REQUEST "listmounts"
#define LISTMOUNTS_HTML_REQUEST "listmounts.xsl" #define LISTMOUNTS_HTML_REQUEST "listmounts.xsl"
#define LISTMOUNTS_JSON_REQUEST "listmounts.json"
#define STREAMLIST_RAW_REQUEST "streamlist" #define STREAMLIST_RAW_REQUEST "streamlist"
#define STREAMLIST_HTML_REQUEST "streamlist.xsl" #define STREAMLIST_HTML_REQUEST "streamlist.xsl"
#define STREAMLIST_JSON_REQUEST "streamlist.json"
#define STREAMLIST_PLAINTEXT_REQUEST "streamlist.txt" #define STREAMLIST_PLAINTEXT_REQUEST "streamlist.txt"
#define MOVECLIENTS_RAW_REQUEST "moveclients" #define MOVECLIENTS_RAW_REQUEST "moveclients"
#define MOVECLIENTS_HTML_REQUEST "moveclients.xsl" #define MOVECLIENTS_HTML_REQUEST "moveclients.xsl"
#define MOVECLIENTS_JSON_REQUEST "moveclients.json"
#define KILLCLIENT_RAW_REQUEST "killclient" #define KILLCLIENT_RAW_REQUEST "killclient"
#define KILLCLIENT_HTML_REQUEST "killclient.xsl" #define KILLCLIENT_HTML_REQUEST "killclient.xsl"
#define KILLCLIENT_JSON_REQUEST "killclient.json" #define KILLCLIENT_JSON_REQUEST "killclient.json"
@ -98,6 +102,7 @@
#define MANAGEAUTH_HTML_REQUEST "manageauth.xsl" #define MANAGEAUTH_HTML_REQUEST "manageauth.xsl"
#define UPDATEMETADATA_RAW_REQUEST "updatemetadata" #define UPDATEMETADATA_RAW_REQUEST "updatemetadata"
#define UPDATEMETADATA_HTML_REQUEST "updatemetadata.xsl" #define UPDATEMETADATA_HTML_REQUEST "updatemetadata.xsl"
#define UPDATEMETADATA_JSON_REQUEST "updatemetadata.json"
#define SHOWLOG_RAW_REQUEST "showlog" #define SHOWLOG_RAW_REQUEST "showlog"
#define SHOWLOG_HTML_REQUEST "showlog.xsl" #define SHOWLOG_HTML_REQUEST "showlog.xsl"
#define SHOWLOG_JSON_REQUEST "showlog.json" #define SHOWLOG_JSON_REQUEST "showlog.json"
@ -146,6 +151,7 @@ static const admin_command_handler_t handlers[] = {
{ SHOUTCAST_METADATA_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_shoutcast_metadata, NULL}, { SHOUTCAST_METADATA_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_shoutcast_metadata, NULL},
{ LISTCLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_show_listeners, NULL}, { LISTCLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_show_listeners, NULL},
{ LISTCLIENTS_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_show_listeners, NULL}, { LISTCLIENTS_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_show_listeners, NULL},
{ LISTCLIENTS_JSON_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_JSON, command_show_listeners, NULL},
{ STATS_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats, NULL}, { STATS_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats, NULL},
{ STATS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL}, { STATS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL},
{ STATS_JSON_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_JSON, command_stats, NULL}, { STATS_JSON_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_JSON, command_stats, NULL},
@ -155,11 +161,14 @@ static const admin_command_handler_t handlers[] = {
{ QUEUE_RELOAD_JSON_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_JSON, command_queue_reload, NULL}, { QUEUE_RELOAD_JSON_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_JSON, command_queue_reload, NULL},
{ LISTMOUNTS_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts, NULL}, { LISTMOUNTS_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts, NULL},
{ LISTMOUNTS_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts, NULL}, { LISTMOUNTS_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts, NULL},
{ LISTMOUNTS_JSON_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_JSON, command_list_mounts, NULL},
{ STREAMLIST_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts, NULL}, { STREAMLIST_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts, NULL},
{ STREAMLIST_PLAINTEXT_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_PLAINTEXT, command_list_mounts, NULL}, { STREAMLIST_PLAINTEXT_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_PLAINTEXT, command_list_mounts, NULL},
{ STREAMLIST_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts, NULL}, { STREAMLIST_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts, NULL},
{ STREAMLIST_JSON_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_JSON, command_list_mounts, NULL},
{ MOVECLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_move_clients, NULL}, { MOVECLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_move_clients, NULL},
{ MOVECLIENTS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_move_clients, NULL}, { MOVECLIENTS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_move_clients, NULL},
{ MOVECLIENTS_JSON_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_JSON, command_move_clients, NULL},
{ KILLCLIENT_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_client, NULL}, { KILLCLIENT_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_client, NULL},
{ KILLCLIENT_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_client, NULL}, { KILLCLIENT_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_client, NULL},
{ KILLCLIENT_JSON_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_JSON, command_kill_client, NULL}, { KILLCLIENT_JSON_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_JSON, command_kill_client, NULL},
@ -170,6 +179,7 @@ static const admin_command_handler_t handlers[] = {
{ MANAGEAUTH_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_manageauth, NULL}, { MANAGEAUTH_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_manageauth, NULL},
{ UPDATEMETADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_updatemetadata, NULL}, { UPDATEMETADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_updatemetadata, NULL},
{ UPDATEMETADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_updatemetadata, NULL}, { UPDATEMETADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_updatemetadata, NULL},
{ UPDATEMETADATA_JSON_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_JSON, command_updatemetadata, NULL},
{ BUILDM3U_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_buildm3u, NULL}, { BUILDM3U_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_buildm3u, NULL},
{ SHOWLOG_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_show_log, NULL}, { SHOWLOG_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_show_log, NULL},
{ SHOWLOG_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_show_log, NULL}, { SHOWLOG_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_show_log, NULL},
@ -371,7 +381,7 @@ xmlNodePtr admin_build_rootnode(xmlDocPtr doc, const char *name)
/* build an XML doc containing information about currently running sources. /* build an XML doc containing information about currently running sources.
* If a mountpoint is passed then that source will not be added to the XML * If a mountpoint is passed then that source will not be added to the XML
* doc even if the source is running */ * doc even if the source is running */
xmlDocPtr admin_build_sourcelist(const char *mount) xmlDocPtr admin_build_sourcelist(const char *mount, client_t *client, admin_format_t format)
{ {
avl_node *node; avl_node *node;
source_t *source; source_t *source;
@ -406,9 +416,12 @@ xmlDocPtr admin_build_sourcelist(const char *mount)
srcnode = xmlNewChild(xmlnode, NULL, XMLSTR("source"), NULL); srcnode = xmlNewChild(xmlnode, NULL, XMLSTR("source"), NULL);
xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount)); xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount));
xmlNewTextChild(srcnode, NULL, XMLSTR("fallback"), if (source->fallback_mount) {
(source->fallback_mount != NULL)? xmlNewTextChild(srcnode, NULL, XMLSTR("fallback"), XMLSTR(source->fallback_mount));
XMLSTR(source->fallback_mount):XMLSTR("")); } else {
if (format == ADMIN_FORMAT_RAW && client->mode != OMODE_STRICT)
xmlNewTextChild(srcnode, NULL, XMLSTR("fallback"), XMLSTR(""));
}
snprintf(buf, sizeof(buf), "%lu", source->listeners); snprintf(buf, sizeof(buf), "%lu", source->listeners);
xmlNewTextChild(srcnode, NULL, XMLSTR("listeners"), XMLSTR(buf)); xmlNewTextChild(srcnode, NULL, XMLSTR("listeners"), XMLSTR(buf));
@ -428,7 +441,11 @@ xmlDocPtr admin_build_sourcelist(const char *mount)
if (source->client) { if (source->client) {
snprintf(buf, sizeof(buf), "%lu", snprintf(buf, sizeof(buf), "%lu",
(unsigned long)(now - source->con->con_time)); (unsigned long)(now - source->con->con_time));
if (format == ADMIN_FORMAT_RAW && client->mode != OMODE_STRICT) {
xmlNewTextChild(srcnode, NULL, XMLSTR("Connected"), XMLSTR(buf)); xmlNewTextChild(srcnode, NULL, XMLSTR("Connected"), XMLSTR(buf));
} else {
xmlNewTextChild(srcnode, NULL, XMLSTR("connected"), XMLSTR(buf));
}
} }
xmlNewTextChild(srcnode, NULL, XMLSTR("content-type"), xmlNewTextChild(srcnode, NULL, XMLSTR("content-type"),
XMLSTR(source->format->contenttype)); XMLSTR(source->format->contenttype));
@ -741,7 +758,7 @@ static void command_move_clients(client_t *client,
} }
ICECAST_LOG_DEBUG("Done optional check (%d)", parameters_passed); ICECAST_LOG_DEBUG("Done optional check (%d)", parameters_passed);
if (!parameters_passed) { if (!parameters_passed) {
xmlDocPtr doc = admin_build_sourcelist(source->mount); xmlDocPtr doc = admin_build_sourcelist(source->mount, client, response);
if (idtext) { if (idtext) {
xmlNodePtr root = xmlDocGetRootElement(doc); xmlNodePtr root = xmlDocGetRootElement(doc);
@ -1100,7 +1117,7 @@ static void command_fallback(client_t *client,
if (client->mode == OMODE_STRICT) { if (client->mode == OMODE_STRICT) {
if (!(COMMAND_OPTIONAL(client, "fallback", fallback))) { if (!(COMMAND_OPTIONAL(client, "fallback", fallback))) {
xmlDocPtr doc = admin_build_sourcelist(source->mount); xmlDocPtr doc = admin_build_sourcelist(source->mount, client, response);
admin_send_response(doc, client, response, FALLBACK_HTML_REQUEST); admin_send_response(doc, client, response, FALLBACK_HTML_REQUEST);
xmlFreeDoc(doc); xmlFreeDoc(doc);
return; return;
@ -1273,7 +1290,7 @@ static void command_list_mounts(client_t *client, source_t *source, admin_format
} else { } else {
xmlDocPtr doc; xmlDocPtr doc;
avl_tree_rlock(global.source_tree); avl_tree_rlock(global.source_tree);
doc = admin_build_sourcelist(NULL); doc = admin_build_sourcelist(NULL, client, response);
avl_tree_unlock(global.source_tree); avl_tree_unlock(global.source_tree);
admin_send_response(doc, client, response, admin_send_response(doc, client, response,

View File

@ -330,7 +330,7 @@ static void render_node_legacystats(json_renderer_t *renderer, xmlDocPtr doc, xm
}; };
static const char * number_keys_source[] = { static const char * number_keys_source[] = {
"audio_bitrate", "audio_channels", "audio_samplerate", "ice-bitrate", "listener_peak", "listeners", "slow_listeners", "audio_bitrate", "audio_channels", "audio_samplerate", "ice-bitrate", "listener_peak", "listeners", "slow_listeners",
"total_bytes_read", "total_bytes_sent", NULL "total_bytes_read", "total_bytes_sent", "connected", NULL
}; };
static const char * boolean_keys_source[] = { static const char * boolean_keys_source[] = {
"public", NULL "public", NULL
@ -341,7 +341,7 @@ static void render_node_legacystats(json_renderer_t *renderer, xmlDocPtr doc, xm
if (node->type == XML_ELEMENT_NODE) { if (node->type == XML_ELEMENT_NODE) {
const char *nodename = (const char *)node->name; const char *nodename = (const char *)node->name;
handled = 1; handled = 1;
if (strcmp(nodename, "icestats") == 0 || strcmp(nodename, "source") == 0) { if (strcmp(nodename, "icestats") == 0 || strcmp(nodename, "source") == 0 || strcmp(nodename, "listener") == 0) {
int is_icestats = strcmp(nodename, "icestats") == 0; int is_icestats = strcmp(nodename, "icestats") == 0;
struct nodelist nodelist; struct nodelist nodelist;
size_t i; size_t i;
@ -402,6 +402,24 @@ static void render_node_legacystats(json_renderer_t *renderer, xmlDocPtr doc, xm
json_renderer_end(renderer); json_renderer_end(renderer);
nodelist_unset(&nodelist, i); nodelist_unset(&nodelist, i);
} else if (strcmp((const char *)cur->name, "listener") == 0) {
size_t j;
json_renderer_write_key(renderer, (const char *)cur->name, JSON_RENDERER_FLAGS_NONE);
json_renderer_begin(renderer, JSON_ELEMENT_TYPE_ARRAY);
for (j = i; j < len; j++) {
xmlNodePtr subcur = nodelist_get(&nodelist, j);
if (subcur == NULL)
continue;
if (subcur->type == XML_ELEMENT_NODE && subcur->name && strcmp((const char *)cur->name, (const char *)subcur->name) == 0) {
nodelist_unset(&nodelist, j);
render_node_legacystats(renderer, doc, subcur, cur, cache);
}
}
json_renderer_end(renderer);
} else if (strcmp((const char *)cur->name, "metadata") == 0) { } else if (strcmp((const char *)cur->name, "metadata") == 0) {
size_t j; size_t j;