From f541cb5c8af07a6c9934d6969c0543779e09b79a Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 3 Aug 2018 09:13:49 +0000 Subject: [PATCH 1/8] Feature: List loaded modules in stats XML --- src/module.c | 28 ++++++++++++++++++++++++++++ src/module.h | 3 +++ src/stats.c | 4 ++++ 3 files changed, 35 insertions(+) diff --git a/src/module.c b/src/module.c index 07e3d59a..46542748 100644 --- a/src/module.c +++ b/src/module.c @@ -18,6 +18,7 @@ #include "refobject.h" #include "module.h" +#include "cfgfile.h" /* for XMLSTR() */ struct module_tag { refobject_base_t __base; @@ -118,6 +119,33 @@ module_t * module_container_get_module(module_container_t *self, co return ret; } +xmlNodePtr module_container_get_modulelist_as_xml(module_container_t *self) +{ + xmlNodePtr root; + avl_node *avlnode; + + if (!self) + return NULL; + + root = xmlNewNode(NULL, XMLSTR("modules")); + if (!root) + return NULL; + + thread_mutex_lock(&(self->lock)); + avlnode = avl_get_first(self->module); + while (avlnode) { + module_t *module = avlnode->key; + xmlNodePtr node = xmlNewChild(root, NULL, XMLSTR("module"), NULL); + + xmlSetProp(node, XMLSTR("name"), XMLSTR(refobject_get_name(module))); + + avlnode = avl_get_next(avlnode); + } + thread_mutex_unlock(&(self->lock)); + + return root; +} + static void __module_free(refobject_t self, void **userdata) { module_t *mod = REFOBJECT_TO_TYPE(self, module_t *); diff --git a/src/module.h b/src/module.h index 8d2ad781..f0f1ee33 100644 --- a/src/module.h +++ b/src/module.h @@ -9,6 +9,8 @@ #ifndef __MODULE_H__ #define __MODULE_H__ +#include + #include "icecasttypes.h" typedef void (*module_client_handler_function_t)(module_t *self, client_t *client, const char *uri); @@ -23,6 +25,7 @@ module_container_t * module_container_new(void); int module_container_add_module(module_container_t *self, module_t *module); int module_container_delete_module(module_container_t *self, const char *name); module_t * module_container_get_module(module_container_t *self, const char *name); +xmlNodePtr module_container_get_modulelist_as_xml(module_container_t *self); module_t * module_new(const char *name, module_setup_handler_t newcb, module_setup_handler_t freecb, void *userdata); diff --git a/src/stats.c b/src/stats.c index 0f9d9ce9..73ec6a28 100644 --- a/src/stats.c +++ b/src/stats.c @@ -1063,12 +1063,16 @@ xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount, client_t *clien { xmlDocPtr doc; xmlNodePtr node; + xmlNodePtr modules; source_t * source; doc = xmlNewDoc (XMLSTR("1.0")); node = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL); xmlDocSetRootElement(doc, node); + modules = module_container_get_modulelist_as_xml(global.modulecontainer); + xmlAddChild(node, modules); + node = _dump_stats_to_doc(node, show_mount, show_hidden, client); if (show_mount && node) { From e89f1bba8d864adea86a1ed7a1df197ac7962d2c Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 3 Aug 2018 09:50:22 +0000 Subject: [PATCH 2/8] Feature: Added way to add links to modules --- src/module.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/module.h | 2 ++ 2 files changed, 47 insertions(+) diff --git a/src/module.c b/src/module.c index 46542748..997d4819 100644 --- a/src/module.c +++ b/src/module.c @@ -27,6 +27,8 @@ struct module_tag { size_t client_handlers_len; module_setup_handler_t freecb; void *userdata; + char *management_link_url; + char *management_link_title; }; @@ -138,6 +140,10 @@ xmlNodePtr module_container_get_modulelist_as_xml(module_co xmlNodePtr node = xmlNewChild(root, NULL, XMLSTR("module"), NULL); xmlSetProp(node, XMLSTR("name"), XMLSTR(refobject_get_name(module))); + if (module->management_link_url) + xmlSetProp(node, XMLSTR("management-url"), XMLSTR(module->management_link_url)); + if (module->management_link_title) + xmlSetProp(node, XMLSTR("management-title"), XMLSTR(module->management_link_title)); avlnode = avl_get_next(avlnode); } @@ -156,6 +162,9 @@ static void __module_free(refobject_t self, void **userdata) if (mod->userdata) free(mod->userdata); + free(mod->management_link_url); + free(mod->management_link_title); + thread_mutex_destroy(&(mod->lock)); } @@ -181,6 +190,42 @@ module_t * module_new(const char *name, module_setup_handler_t newc return ret; } +int module_add_link(module_t *self, const char *type, const char *url, const char *title) +{ + char *n_url = NULL; + char *n_title = NULL; + + if (!self || !type) + return -1; + + if (strcmp(type, "management-url") != 0) + return -1; + + if (url) { + n_url = strdup(url); + if (!n_url) + return -1; + } + + if (title) { + n_title = strdup(title); + if (!n_title) { + free(n_url); + return -1; + } + } + + thread_mutex_lock(&(self->lock)); + free(self->management_link_url); + free(self->management_link_title); + + self->management_link_url = n_url; + self->management_link_title = n_title; + thread_mutex_unlock(&(self->lock)); + + return 0; +} + const module_client_handler_t * module_get_client_handler(module_t *self, const char *name) { size_t i; diff --git a/src/module.h b/src/module.h index f0f1ee33..8b29accc 100644 --- a/src/module.h +++ b/src/module.h @@ -29,6 +29,8 @@ xmlNodePtr module_container_get_modulelist_as_xml(module_co module_t * module_new(const char *name, module_setup_handler_t newcb, module_setup_handler_t freecb, void *userdata); +int module_add_link(module_t *self, const char *type, const char *url, const char *title); + /* Note: Those functions are not really thread safe as (module_client_handler_t) is not thread safe. This is by design. */ const module_client_handler_t * module_get_client_handler(module_t *self, const char *name); int module_add_client_handler(module_t *self, const module_client_handler_t *handlers, size_t len); From 2a370cccd0a73d5d32148b47fdea357f3eca3c16 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Fri, 3 Aug 2018 10:06:47 +0000 Subject: [PATCH 3/8] Feature: Display management links from modules in admin/ --- admin/includes/header.xsl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/admin/includes/header.xsl b/admin/includes/header.xsl index 323919d2..16a1e438 100644 --- a/admin/includes/header.xsl +++ b/admin/includes/header.xsl @@ -14,6 +14,11 @@ From c67d9de500d1cee6d1c111ae0053d432ecf46bea Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 8 Aug 2018 12:16:14 +0000 Subject: [PATCH 4/8] Feature: Added functions to get a node from report XML by it's type --- src/reportxml.c | 35 +++++++++++++++++++++++++++++++++++ src/reportxml.h | 4 ++++ 2 files changed, 39 insertions(+) diff --git a/src/reportxml.c b/src/reportxml.c index cfc21a1f..b6ea1c7e 100644 --- a/src/reportxml.c +++ b/src/reportxml.c @@ -286,6 +286,14 @@ reportxml_node_t * reportxml_get_node_by_attribute(reportxml_t *report, con return reportxml_node_get_child_by_attribute(report->root, key, value, include_definitions); } +reportxml_node_t * reportxml_get_node_by_type(reportxml_t *report, reportxml_node_type_t type, int include_definitions) +{ + if (!report) + return NULL; + + return reportxml_node_get_child_by_type(report->root, type, include_definitions); +} + reportxml_t * reportxml_parse_xmldoc(xmlDocPtr doc) { reportxml_node_t *root; @@ -822,6 +830,33 @@ reportxml_node_t * reportxml_node_get_child_by_attribute(reportxml_node_t * return NULL; } +reportxml_node_t * reportxml_node_get_child_by_type(reportxml_node_t *node, reportxml_node_type_t type, int include_definitions) +{ + size_t i; + + if (!node) + return NULL; + + if (node->type == type) { + if (refobject_ref(node) != 0) + return NULL; + return node; + } + + if (node->type == REPORTXML_NODE_TYPE_DEFINITION && !include_definitions) + return NULL; + + for (i = 0; i < node->childs_len; i++) { + reportxml_node_t *ret; + + ret = reportxml_node_get_child_by_type(node->childs[i], type, include_definitions); + if (ret != NULL) + return ret; + } + + return NULL; +} + int reportxml_node_set_content(reportxml_node_t *node, const char *value) { const struct nodedef *nodedef; diff --git a/src/reportxml.h b/src/reportxml.h index 2be09dc8..68d4b3d8 100644 --- a/src/reportxml.h +++ b/src/reportxml.h @@ -77,6 +77,8 @@ reportxml_node_t * reportxml_get_root_node(reportxml_t *report); * s are skipped. */ reportxml_node_t * reportxml_get_node_by_attribute(reportxml_t *report, const char *key, const char *value, int include_definitions); +/* This gets a node by it's type. Otherwise identical to reportxml_get_node_by_attribute() */ +reportxml_node_t * reportxml_get_node_by_type(reportxml_t *report, reportxml_node_type_t type, int include_definitions); /* This function parses an XML document and returns the parst report XML document */ reportxml_t * reportxml_parse_xmldoc(xmlDocPtr doc); /* This function renders an report XML document as XML structure */ @@ -108,6 +110,8 @@ ssize_t reportxml_node_count_child(reportxml_node_t *node); reportxml_node_t * reportxml_node_get_child(reportxml_node_t *node, size_t idx); /* This gets an child by it's value of the given attribute. See reportxml_get_node_by_attribute() for more details. */ reportxml_node_t * reportxml_node_get_child_by_attribute(reportxml_node_t *node, const char *key, const char *value, int include_definitions); +/* This gets a child by it's type. Otherwise identical to reportxml_node_get_child_by_attribute() */ +reportxml_node_t * reportxml_node_get_child_by_type(reportxml_node_t *node, reportxml_node_type_t type, int include_definitions); /* This gets and sets the text content of an node (used for ) */ int reportxml_node_set_content(reportxml_node_t *node, const char *value); char * reportxml_node_get_content(reportxml_node_t *node); From 51712ebb2a238f62b2866d97d3bb4029507363cd Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 8 Aug 2018 12:17:00 +0000 Subject: [PATCH 5/8] Feature: Provide a unified interface to get an basic report --- src/client.c | 62 +++++++++++++++++++++++++++++++++------------------- src/client.h | 1 + 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/client.c b/src/client.c index f12759d3..65b6301e 100644 --- a/src/client.c +++ b/src/client.c @@ -301,7 +301,6 @@ int client_read_bytes(client_t *client, void *buf, unsigned len) static inline void _client_send_error(client_t *client, const icecast_error_t *error) { - ice_config_t *config; reportxml_t *report; admin_format_t admin_format; const char *xslt = NULL; @@ -323,28 +322,8 @@ static inline void _client_send_error(client_t *client, const icecast_error_t *e break; } - - config = config_get_config(); - report = reportxml_database_build_report(config->reportxml_db, error->uuid, -1); - config_release_config(); - if (!report) { - reportxml_node_t *root, *incident, *state, *text; - - report = reportxml_new(); - root = reportxml_get_root_node(report); - incident = reportxml_node_new(REPORTXML_NODE_TYPE_INCIDENT, NULL, NULL, NULL); - state = reportxml_node_new(REPORTXML_NODE_TYPE_STATE, NULL, error->uuid, NULL); - text = reportxml_node_new(REPORTXML_NODE_TYPE_TEXT, NULL, NULL, NULL); - reportxml_node_set_content(text, error->message); - reportxml_node_add_child(state, text); - reportxml_node_add_child(incident, state); - reportxml_node_add_child(root, incident); - refobject_unref(text); - refobject_unref(state); - refobject_unref(incident); - refobject_unref(root); - } + report = client_get_reportxml(error->uuid, NULL, error->message); client_send_reportxml(client, report, DOCUMENT_DOMAIN_ADMIN, xslt, admin_format, error->http_status); @@ -623,6 +602,45 @@ void client_send_reportxml(client_t *client, reportxml_t *report, document_domai xmlFreeDoc(doc); } +reportxml_t *client_get_reportxml(const char *state_definition, const char *state_akindof, const char *state_text) +{ + reportxml_t *report = NULL; + + if (state_definition) { + ice_config_t *config; + + config = config_get_config(); + report = reportxml_database_build_report(config->reportxml_db, state_definition, -1); + config_release_config(); + } + + if (!report) { + reportxml_node_t *rootnode, *incidentnode, *statenode; + + report = reportxml_new(); + rootnode = reportxml_get_root_node(report); + incidentnode = reportxml_node_new(REPORTXML_NODE_TYPE_INCIDENT, NULL, NULL, NULL); + statenode = reportxml_node_new(REPORTXML_NODE_TYPE_STATE, NULL, state_definition, state_akindof); + + if (state_text) { + reportxml_node_t *textnode; + + textnode = reportxml_node_new(REPORTXML_NODE_TYPE_TEXT, NULL, NULL, NULL); + reportxml_node_set_content(textnode, state_text); + reportxml_node_add_child(statenode, textnode); + refobject_unref(textnode); + } + + reportxml_node_add_child(incidentnode, statenode); + reportxml_node_add_child(rootnode, incidentnode); + refobject_unref(statenode); + refobject_unref(incidentnode); + refobject_unref(rootnode); + } + + return report; +} + admin_format_t client_get_admin_format_by_content_negotiation(client_t *client) { const char *pref; diff --git a/src/client.h b/src/client.h index ce34f5e0..059fbcc1 100644 --- a/src/client.h +++ b/src/client.h @@ -144,6 +144,7 @@ void client_send_101(client_t *client, reuse_t reuse); void client_send_204(client_t *client); void client_send_426(client_t *client, reuse_t reuse); void client_send_reportxml(client_t *client, reportxml_t *report, document_domain_t domain, const char *xsl, admin_format_t admin_format_hint, int status); +reportxml_t *client_get_reportxml(const char *state_definition, const char *state_akindof, const char *state_text); admin_format_t client_get_admin_format_by_content_negotiation(client_t *client); int client_send_bytes (client_t *client, const void *buf, unsigned len); int client_read_bytes (client_t *client, void *buf, unsigned len); From d0efb73a7bf5cf81fd9944b19b08e3a9b13807c4 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 8 Aug 2018 13:20:07 +0000 Subject: [PATCH 6/8] Feature: Also report loaded modules in report XML --- src/admin.h | 2 ++ src/client.c | 31 ++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/admin.h b/src/admin.h index 6d4b03c6..1245a194 100644 --- a/src/admin.h +++ b/src/admin.h @@ -21,6 +21,8 @@ #include "compat.h" #include "resourcematch.h" +#define ADMIN_ICESTATS_LEGACY_EXTENSION_APPLICATION "http://icecast.org/specs/legacy-icestats" + /* types */ #define ADMINTYPE_ERROR (-1) #define ADMINTYPE_GENERAL 1 diff --git a/src/client.c b/src/client.c index 65b6301e..41f5ce2d 100644 --- a/src/client.c +++ b/src/client.c @@ -24,6 +24,8 @@ #include #include +#include + #include "common/thread/thread.h" #include "common/avl/avl.h" #include "common/httpp/httpp.h" @@ -51,7 +53,7 @@ #include "listensocket.h" #include "fastevent.h" -/* for ADMIN_COMMAND_ERROR */ +/* for ADMIN_COMMAND_ERROR, and ADMIN_ICESTATS_LEGACY_EXTENSION_APPLICATION */ #include "admin.h" #ifdef _WIN32 @@ -602,6 +604,31 @@ void client_send_reportxml(client_t *client, reportxml_t *report, document_domai xmlFreeDoc(doc); } +static void client_get_reportxml__add_basic_stats(reportxml_t *report) +{ + reportxml_node_t *rootnode, *extension; + xmlNodePtr xmlroot; + xmlNodePtr modules; + + rootnode = reportxml_get_root_node(report); + + extension = reportxml_node_new(REPORTXML_NODE_TYPE_EXTENSION, NULL, NULL, NULL); + reportxml_node_set_attribute(extension, "application", ADMIN_ICESTATS_LEGACY_EXTENSION_APPLICATION); + + reportxml_node_add_child(rootnode, extension); + + refobject_unref(rootnode); + + xmlroot = xmlNewNode(NULL, XMLSTR("icestats")); + modules = module_container_get_modulelist_as_xml(global.modulecontainer); + xmlAddChild(xmlroot, modules); + + + reportxml_node_add_xml_child(extension, xmlroot); + refobject_unref(extension); + xmlFreeNode(xmlroot); +} + reportxml_t *client_get_reportxml(const char *state_definition, const char *state_akindof, const char *state_text) { reportxml_t *report = NULL; @@ -638,6 +665,8 @@ reportxml_t *client_get_reportxml(const char *state_definition, const char *stat refobject_unref(rootnode); } + client_get_reportxml__add_basic_stats(report); + return report; } From d711ea15bd02b35f60d7f396ab7989842bc9858b Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 8 Aug 2018 13:26:41 +0000 Subject: [PATCH 7/8] Feature: Use /icestats/modules/module even if it's just within an report XML extention --- admin/includes/header.xsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/includes/header.xsl b/admin/includes/header.xsl index 16a1e438..49023afc 100644 --- a/admin/includes/header.xsl +++ b/admin/includes/header.xsl @@ -14,7 +14,7 @@
  • Administration
  • Mountpoint list
  • - +
  • From bfcac5d30ce3a0d6a6fd5961b68f91eb4ef39628 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Wed, 8 Aug 2018 14:02:22 +0000 Subject: [PATCH 8/8] Feature: Unifiy response root node generation a bit and make the module list universally available --- admin/includes/header.xsl | 2 +- src/admin.c | 31 +++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/admin/includes/header.xsl b/admin/includes/header.xsl index 49023afc..f3f7dc6a 100644 --- a/admin/includes/header.xsl +++ b/admin/includes/header.xsl @@ -14,7 +14,7 @@
    • Administration
    • Mountpoint list
    • - +
    • diff --git a/src/admin.c b/src/admin.c index 382bf1ba..69463a1f 100644 --- a/src/admin.c +++ b/src/admin.c @@ -317,6 +317,17 @@ int admin_command_table_unregister(const char *prefix) return -1; } +/* build an XML root node including some common tags */ +xmlNodePtr admin_build_rootnode(xmlDocPtr doc, const char *name) +{ + xmlNodePtr rootnode = xmlNewDocNode(doc, NULL, XMLSTR(name), NULL); + xmlNodePtr modules = module_container_get_modulelist_as_xml(global.modulecontainer); + + xmlAddChild(rootnode, modules); + + return rootnode; +} + /* 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 * doc even if the source is running */ @@ -330,7 +341,7 @@ xmlDocPtr admin_build_sourcelist(const char *mount) time_t now = time(NULL); doc = xmlNewDoc (XMLSTR("1.0")); - xmlnode = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL); + xmlnode = admin_build_rootnode(doc, "icestats"); xmlDocSetRootElement(doc, xmlnode); if (mount) { @@ -649,7 +660,7 @@ static void command_move_clients(client_t *client, ICECAST_LOG_INFO("source is \"%s\", destination is \"%s\"", source->mount, dest->mount); doc = xmlNewDoc(XMLSTR("1.0")); - node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL); + node = admin_build_rootnode(doc, "iceresponse"); xmlDocSetRootElement(doc, node); source_move_clients(source, dest); @@ -744,7 +755,7 @@ static void command_show_listeners(client_t *client, char buf[22]; doc = xmlNewDoc(XMLSTR("1.0")); - node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL); + node = admin_build_rootnode(doc, "icestats"); srcnode = xmlNewChild(node, NULL, XMLSTR("source"), NULL); xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount)); xmlDocSetRootElement(doc, node); @@ -902,12 +913,12 @@ static void command_manageauth(client_t *client, source_t *source, admin_format_ } doc = xmlNewDoc(XMLSTR("1.0")); - node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL); + node = admin_build_rootnode(doc, "icestats"); rolenode = admin_add_role_to_authentication(auth, node); if (message) { - msgnode = xmlNewChild(node, NULL, XMLSTR("iceresponse"), NULL); + msgnode = admin_build_rootnode(doc, "iceresponse"); xmlNewTextChild(msgnode, NULL, XMLSTR("message"), XMLSTR(message)); } @@ -941,7 +952,7 @@ static void command_kill_source(client_t *client, xmlNodePtr node; doc = xmlNewDoc (XMLSTR("1.0")); - node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL); + node = admin_build_rootnode(doc, "iceresponse"); xmlNewTextChild(node, NULL, XMLSTR("message"), XMLSTR("Source Removed")); xmlNewTextChild(node, NULL, XMLSTR("return"), XMLSTR("1")); xmlDocSetRootElement(doc, node); @@ -971,7 +982,7 @@ static void command_kill_client(client_t *client, listener = source_find_client(source, id); doc = xmlNewDoc(XMLSTR("1.0")); - node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL); + node = admin_build_rootnode(doc, "iceresponse"); xmlDocSetRootElement(doc, node); ICECAST_LOG_DEBUG("Response is %d", response); @@ -1028,7 +1039,7 @@ static void command_metadata(client_t *client, int same_ip = 1; doc = xmlNewDoc(XMLSTR("1.0")); - node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL); + node = admin_build_rootnode(doc, "iceresponse"); xmlDocSetRootElement(doc, node); ICECAST_LOG_DEBUG("Got metadata update request"); @@ -1155,7 +1166,7 @@ static void command_queue_reload(client_t *client, source_t *source, admin_forma global_unlock(); doc = xmlNewDoc (XMLSTR("1.0")); - node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL); + node = admin_build_rootnode(doc, "iceresponse"); xmlNewTextChild(node, NULL, XMLSTR("message"), XMLSTR("Config reload queued")); xmlNewTextChild(node, NULL, XMLSTR("return"), XMLSTR("1")); xmlDocSetRootElement(doc, node); @@ -1207,7 +1218,7 @@ static void command_updatemetadata(client_t *client, xmlNodePtr node, srcnode; doc = xmlNewDoc(XMLSTR("1.0")); - node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL); + node = admin_build_rootnode(doc, "icestats"); srcnode = xmlNewChild(node, NULL, XMLSTR("source"), NULL); xmlSetProp(srcnode, XMLSTR("mount"), XMLSTR(source->mount)); xmlDocSetRootElement(doc, node);