diff --git a/admin/includes/header.xsl b/admin/includes/header.xsl index 323919d2..f3f7dc6a 100644 --- a/admin/includes/header.xsl +++ b/admin/includes/header.xsl @@ -14,6 +14,11 @@ 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); 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 f12759d3..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 @@ -301,7 +303,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 +324,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 +604,72 @@ 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; + + 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); + } + + client_get_reportxml__add_basic_stats(report); + + 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); diff --git a/src/module.c b/src/module.c index 07e3d59a..997d4819 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; @@ -26,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; }; @@ -118,6 +121,37 @@ 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))); + 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); + } + 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 *); @@ -128,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)); } @@ -153,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 8d2ad781..8b29accc 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,9 +25,12 @@ 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); +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); 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); 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) {