From: Jonathan Rajotte Date: Tue, 28 Jun 2016 19:30:27 +0000 (-0400) Subject: Config: utility functions for direct configurations element manipulations X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=commitdiff_plain;h=fa8b52a2f46edd1727cb6dfe381d772a925d57e1 Config: utility functions for direct configurations element manipulations Introduce two new config struct: config_document and config_element. config_document represent an instance of a complete configuration (xmlDoc). config_element represent an instance of a sub configuration element (xmlNode). Signed-off-by: Jonathan Rajotte --- diff --git a/src/common/config/config-internal.h b/src/common/config/config-internal.h index ff3e6293e..f6de9005f 100644 --- a/src/common/config/config-internal.h +++ b/src/common/config/config-internal.h @@ -16,8 +16,20 @@ */ #include +#include #include struct config_writer { xmlTextWriterPtr writer; }; + +struct config_document { + xmlDocPtr document; +}; + +struct config_element { + xmlNodePtr element; +}; + + + diff --git a/src/common/config/session-config.c b/src/common/config/session-config.c index f032e1a34..616434ecf 100644 --- a/src/common/config/session-config.c +++ b/src/common/config/session-config.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -2724,6 +2725,46 @@ end: return ret; } +static +int load_session_from_document(struct config_document *document, const char *session_name, + struct session_config_validation_ctx *validation_ctx, int override) +{ + int ret, session_found = !session_name; + xmlNodePtr sessions_node; + xmlNodePtr session_node; + + assert(validation_ctx); + + ret = xmlSchemaValidateDoc(validation_ctx->schema_validation_ctx, document->document); + if (ret) { + ERR("Session configuration file validation failed"); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + sessions_node = xmlDocGetRootElement(document->document); + if (!sessions_node) { + goto end; + } + + for (session_node = xmlFirstElementChild(sessions_node); + session_node; session_node = + xmlNextElementSibling(session_node)) { + ret = process_session_node(session_node, + session_name, override); + if (session_name && ret == 0) { + /* Target session found and loaded */ + session_found = 1; + break; + } + } +end: + if (!ret) { + ret = session_found ? 0 : -LTTNG_ERR_LOAD_SESSION_NOENT; + } + return ret; +} + /* Allocate dirent as recommended by READDIR(3), NOTES on readdir_r */ static struct dirent *alloc_dirent(const char *path) @@ -3020,3 +3061,565 @@ void __attribute__((destructor)) session_config_exit(void) { xmlCleanupParser(); } + +LTTNG_HIDDEN +struct config_document *config_document_get(const char *path) +{ + int ret; + struct config_document *document = NULL; + struct session_config_validation_ctx validation_ctx = { 0 }; + + assert(path); + + ret = access(path, F_OK); + if (ret < 0) { + PERROR("access"); + switch (errno) { + case ENOENT: + ERR("Configuration document path does not exist."); + break; + case EACCES: + ERR("Configuration document permission denied."); + break; + default: + ERR("Configuration document unknown error."); + break; + } + goto end; + } + + ret = init_session_config_validation_ctx(&validation_ctx); + if (ret) { + goto end; + } + + ret = validate_file_read_creds(path); + if (ret != 1) { + if (ret == -1) { + ERR("Configuration document permission denied."); + } else { + ERR("Configuration document does not exist."); + } + goto end; + } + + document = zmalloc(sizeof(struct config_document)); + if (!document) { + PERROR("zmalloc"); + goto end; + } + + document->document = xmlParseFile(path); + if (!document->document) { + ERR("Configuration document parsing failed."); + goto error; + } + + ret = xmlSchemaValidateDoc(validation_ctx.schema_validation_ctx, + document->document); + if (ret) { + ERR("Session configuration file validation failed"); + goto error; + } + +end: + return document; +error: + xmlFreeDoc(document->document); + free(document); + return NULL; +} + +LTTNG_HIDDEN +void config_document_free(struct config_document *document) +{ + if (document) { + xmlFreeDoc(document->document); + document->document = NULL; + } +} + +LTTNG_HIDDEN +int config_document_replace_element_value(struct config_document *document, + const char *xpath, const char *value) +{ + int ret; + int xpath_result_size; + + xmlXPathContextPtr xpath_context = NULL; + xmlXPathObjectPtr xpath_object = NULL; + xmlNodeSetPtr xpath_result_set = NULL; + xmlChar *internal_xpath = NULL; + xmlChar *internal_value = NULL; + + assert(document); + assert(xpath); + assert(value); + + internal_xpath = encode_string(xpath); + if (!internal_xpath) { + ret = -LTTNG_ERR_NOMEM; + ERR("Unable to encode xpath string"); + goto end; + } + + internal_value = encode_string(value); + if (!internal_value) { + ret = -LTTNG_ERR_NOMEM; + ERR("Unable to encode xpath replace value string"); + goto end; + } + + /* Initialize xpath context */ + xpath_context = xmlXPathNewContext(document->document); + if (!xpath_context) { + ret = -LTTNG_ERR_NOMEM; + ERR("Unable to create xpath context"); + goto end; + } + + /* Evaluate de xpath expression */ + xpath_object = xmlXPathEvalExpression(internal_xpath, xpath_context); + if (!xpath_object) { + ret = -LTTNG_ERR_CONFIG_INVALID_QUERY; + goto end; + } + + /* TODO: from here could be extracted and previopus could be a step in + * subsequent operation, modify/substitute/delete node. + */ + xpath_result_set = xpath_object->nodesetval; + if (!xpath_result_set) { + ret = -LTTNG_ERR_CONFIG_EMPTY_SET; + goto end; + } + + xpath_result_size = xpath_result_set->nodeNr; + + /* + * Reverse traversal since last element could be nested under parent + * element present in the result set. + */ + for (int i = xpath_result_size - 1; i >=0; i--) { + assert(xpath_result_set->nodeTab[i]); + xmlNodeSetContent(xpath_result_set->nodeTab[i], internal_value); + + /* + * Libxml2 quirk regarding the freing and namesplace node see + * libxml2 example and documentation for more details. + */ + if (xpath_result_set->nodeTab[i]->type != XML_NAMESPACE_DECL) { + xpath_result_set->nodeTab[i] = NULL; + } + } + +end: + xmlXPathFreeContext(xpath_context); + xmlXPathFreeObject(xpath_object); + xmlFree(internal_xpath); + xmlFree(internal_value); + return ret; +} + +LTTNG_HIDDEN +int config_load_configuration_sessions(struct config_document *document, + const char *session_name, int override) +{ + int ret; + struct session_config_validation_ctx validation_ctx = { 0 }; + + ret = init_session_config_validation_ctx(&validation_ctx); + if (ret) { + goto end; + } + ret = load_session_from_document(document, session_name, + &validation_ctx, override); +end: + return ret; +} + +LTTNG_HIDDEN +int config_document_replace_element(struct config_document *document, + const char *xpath, const struct config_element *element) +{ + int ret = 0; + int xpath_result_size; + + xmlXPathContextPtr xpath_context = NULL; + xmlXPathObjectPtr xpath_object = NULL; + xmlNodeSetPtr xpath_result_set = NULL; + xmlChar *internal_xpath = NULL; + xmlNodePtr old_node = NULL; + xmlNodePtr copy = NULL; + + assert(document); + assert(xpath); + assert(element); + assert(element->element); + + internal_xpath = encode_string(xpath); + if (!internal_xpath) { + ret = -LTTNG_ERR_NOMEM; + ERR("Unable to encode xpath string"); + goto end; + } + + /* Initialize xpath context */ + xpath_context = xmlXPathNewContext(document->document); + if (!xpath_context) { + ret = -LTTNG_ERR_NOMEM; + ERR("Unable to create xpath context"); + goto end; + } + + /* Evaluate the xpath expression */ + xpath_object = xmlXPathEvalExpression(internal_xpath, xpath_context); + if (!xpath_object) { + ret = -LTTNG_ERR_CONFIG_INVALID_QUERY; + goto end; + } + + xpath_result_set = xpath_object->nodesetval; + if (!xpath_result_set) { + ret = -LTTNG_ERR_CONFIG_EMPTY_SET; + goto end; + } + + xpath_result_size = xpath_result_set->nodeNr; + + if (xpath_result_size > 1) { + ret = -LTTNG_ERR_CONFIG_INVALID_COUNT; + goto end; + } + + if (xpath_result_size == 0) { + ret = -LTTNG_ERR_CONFIG_REPLACEMENT; + goto end; + } + + assert(xpath_result_set->nodeTab[0]); + + /* Do a copy of the element to ease caller memory management */ + copy = xmlCopyNode(element->element, 1); + if (!copy) { + ret = -LTTNG_ERR_NOMEM; + ERR("Copy failed for node replacement"); + goto end; + } + + + old_node = xmlReplaceNode(xpath_result_set->nodeTab[0], copy); + if (!old_node) { + ret = -LTTNG_ERR_CONFIG_REPLACEMENT; + xmlFreeNode(copy); + goto end; + } + xmlFree(old_node); +end: + xmlXPathFreeContext(xpath_context); + xmlXPathFreeObject(xpath_object); + xmlFree(internal_xpath); + return ret; +} + +LTTNG_HIDDEN +char *config_document_get_element_value(struct config_document *document, + const char *xpath) +{ + char *value = NULL; + int xpath_result_size; + int value_result_size; + + xmlXPathContextPtr xpath_context = NULL; + xmlXPathObjectPtr xpath_object = NULL; + xmlNodeSetPtr xpath_result_set = NULL; + xmlChar *internal_xpath = NULL; + xmlChar *internal_value = NULL; + + assert(document); + assert(xpath); + + internal_xpath = encode_string(xpath); + if (!internal_xpath) { + value = NULL; + ERR("Unable to encode xpath string"); + goto end; + } + + /* Initialize xpath context */ + xpath_context = xmlXPathNewContext(document->document); + if (!xpath_context) { + value = NULL; + ERR("Unable to create xpath context"); + goto end; + } + + /* Evaluate the xpath expression */ + xpath_object = xmlXPathEvalExpression(internal_xpath, xpath_context); + if (!xpath_object) { + value = NULL; + ERR("Unable to evaluate xpath query (invalid query format)"); + goto end; + } + + xpath_result_set = xpath_object->nodesetval; + if (!xpath_result_set) { + value = NULL; + goto end; + } + + xpath_result_size = xpath_result_set->nodeNr; + + if (xpath_result_size > 1) { + ERR("To many result while fetching config element value"); + value = NULL; + goto end; + } + + if (xpath_result_size == 0) { + value = NULL; + goto end; + } + + /* + * Reverse traversal since last element could be nested under parent + * element present in the result set. + */ + assert(xpath_result_set->nodeTab[0]); + internal_value = xmlNodeGetContent(xpath_result_set->nodeTab[0]); + if (!internal_value) { + value = NULL; + goto end; + } + + value_result_size = xmlStrlen(internal_value); + value = calloc(value_result_size + 1, sizeof(char)); + strncpy(value, (char *) internal_value, value_result_size); + +end: + xmlXPathFreeContext(xpath_context); + xmlXPathFreeObject(xpath_object); + xmlFree(internal_xpath); + xmlFree(internal_value); + return value; +} + +LTTNG_HIDDEN +int config_document_element_exist(struct config_document *document, + const char *xpath) +{ + int exist = 0; + int xpath_result_size; + + xmlXPathContextPtr xpath_context = NULL; + xmlXPathObjectPtr xpath_object = NULL; + xmlNodeSetPtr xpath_result_set = NULL; + xmlChar *internal_xpath = NULL; + + assert(document); + assert(document->document); + assert(xpath); + + internal_xpath = encode_string(xpath); + if (!internal_xpath) { + ERR("Unable to encode xpath string"); + goto end; + } + + /* Initialize xpath context */ + xpath_context = xmlXPathNewContext(document->document); + if (!xpath_context) { + ERR("Unable to create xpath context"); + goto end; + } + + /* Evaluate the xpath expression */ + xpath_object = xmlXPathEvalExpression(internal_xpath, xpath_context); + if (!xpath_object) { + ERR("Unable to evaluate xpath query (invalid query format)"); + goto end; + } + + xpath_result_set = xpath_object->nodesetval; + if (!xpath_result_set) { + goto end; + } + + xpath_result_size = xpath_result_set->nodeNr; + + if (xpath_result_size > 0) { + exist = 1; + } +end: + xmlXPathFreeContext(xpath_context); + xmlXPathFreeObject(xpath_object); + xmlFree(internal_xpath); + return exist; + +} + +LTTNG_HIDDEN +struct config_element *config_element_create(const char *name, + const char* value) +{ + struct config_element *element; + + assert(name); + + xmlChar *internal_name = NULL; + xmlChar *internal_value = NULL; + + internal_name = encode_string(name); + if (!internal_name) { + ERR("Unable to encode config element name string"); + element = NULL; + goto end; + } + + + if (value) { + internal_value = encode_string(value); + if (!internal_value) { + ERR("Unable to encode config_element value string"); + element = NULL; + goto end; + } + } + + element = zmalloc(sizeof(struct config_element)); + if (!element) { + goto end; + } + + element->element = xmlNewNode(NULL, internal_name); + if (!element->element) { + free(element); + element = NULL; + } + + if (internal_value) { + xmlNodeAddContent(element->element, internal_value); + } +end: + xmlFree(internal_name); + xmlFree(internal_value); + return element; +} + +LTTNG_HIDDEN +int config_element_add_child(struct config_element *parent, + const struct config_element *child) +{ + assert(parent); + assert(child); + assert(parent->element); + assert(child->element); + + int ret = 0; + xmlNodePtr node = NULL; + xmlNodePtr copy = NULL; + + /* Do a copy to ease the memory management for caller */ + copy = xmlCopyNode(child->element, 1); + if (!copy) { + ret = -LTTNG_ERR_NOMEM; + goto error; + } + node = xmlAddChild(parent->element, copy); + if (!node) { + xmlFreeNode(copy); + ret = -LTTNG_ERR_CONFIG_ADD_CHILD; + goto error; + } +error: + return ret; +} + + +LTTNG_HIDDEN +int config_document_insert_element(struct config_document *document, + const char *xpath, const struct config_element *element) +{ + int ret = 0; + int xpath_result_size; + + xmlXPathContextPtr xpath_context = NULL; + xmlXPathObjectPtr xpath_object = NULL; + xmlNodeSetPtr xpath_result_set = NULL; + xmlChar *internal_xpath = NULL; + xmlNodePtr child_node = NULL; + xmlNodePtr local_copy = NULL; + + assert(document); + assert(xpath); + assert(element); + assert(element->element); + + internal_xpath = encode_string(xpath); + if (!internal_xpath) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Initialize xpath context */ + xpath_context = xmlXPathNewContext(document->document); + if (!xpath_context) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Evaluate the xpath expression */ + xpath_object = xmlXPathEvalExpression(internal_xpath, xpath_context); + if (!xpath_object) { + ret = -LTTNG_ERR_CONFIG_INVALID_QUERY; + goto end; + } + + xpath_result_set = xpath_object->nodesetval; + if (!xpath_result_set) { + ret = -LTTNG_ERR_CONFIG_EMPTY_SET; + goto end; + } + + xpath_result_size = xpath_result_set->nodeNr; + + if (xpath_result_size > 1) { + ret = -LTTNG_ERR_CONFIG_INVALID_COUNT; + goto end; + } + + if (xpath_result_size == 0) { + ret = -LTTNG_ERR_CONFIG_INVALID_COUNT; + goto end; + } + + assert(xpath_result_set->nodeTab[0]); + /* Do a copy to simply memory management */ + local_copy = xmlCopyNode(element->element, 1); + if (!local_copy) { + ret = -LTTNG_ERR_NOMEM; + ERR("Duplication of node to be insert failed"); + goto end; + } + + child_node = xmlAddChild(xpath_result_set->nodeTab[0], local_copy); + if (!child_node) { + ret = -LTTNG_ERR_CONFIG_ADD_CHILD; + xmlFreeNode(local_copy); + goto end; + } +end: + xmlXPathFreeContext(xpath_context); + xmlXPathFreeObject(xpath_object); + xmlFree(internal_xpath); + return ret; +} + +LTTNG_HIDDEN +void config_element_free(struct config_element *element) { + if (element->element) { + xmlFree(element->element); + } + + free(element); +} diff --git a/src/common/config/session-config.h b/src/common/config/session-config.h index 939cb6cbf..7b068e838 100644 --- a/src/common/config/session-config.h +++ b/src/common/config/session-config.h @@ -33,6 +33,11 @@ struct config_entry { /* Instance of a configuration writer. */ struct config_writer; +/* Instance of a configuration document */ +struct config_document; + +struct config_element; + /* * A config_entry_handler_cb receives config_entry structures belonging to the * sections the handler has been registered to. @@ -225,4 +230,126 @@ LTTNG_HIDDEN int config_load_session(const char *path, const char *session_name, int override, unsigned int autoload); +/* + * Load session configuration from a document + * + * document The document to be loaded as a configuration + * session_name Name of the session to load. Will load all sessions from the + * passed document if NULL + * + * override Override current session configuration if it exists. + * + * Returns zero if the session could be loaded successfully. Returns + * a negative LTTNG_ERR code on error. + */ +LTTNG_HIDDEN +int config_load_configuration_sessions(struct config_document *document, + const char *session_name, int override); + +/* + * Get the document corresponding to the path. + * + * path Path to a configuration file. + * xsd_validation Whether or not to do a xsd validation + * + * Returns an new allocated config_document when successful. + * Returns NULL on error; + * + * The caller is responsible of freeing the document via config_document_free. + */ +LTTNG_HIDDEN +struct config_document *config_document_get(const char *path); + +/* + * Free an allocated document. + * + * document The document to free. + */ +LTTNG_HIDDEN +void config_document_free(struct config_document *document); + +/* + * Replace the value of a document element + * + * document The document containing the element to be modified. + * xpath The xpath string to the element. + * value The value to be placed inside the element. + * + * Returns zero if the session could be loaded successfully. Returns + * a negative LTTNG_ERR code on error. + * */ +LTTNG_HIDDEN +int config_document_replace_element_value(struct config_document *document, const char *xpath, const char *value); + +/* + * Swap a document node by the passed element. + * + * document The document containing the element to be modified. + * xpath The xpath string to the element. + * element The replacement element. + * + * Returns zero if the session could be loaded successfully. Returns + * a negative LTTNG_ERR code on error. + */ +LTTNG_HIDDEN +int config_document_replace_element(struct config_document *document, const char *xpath, const struct config_element *element); + +/* + * Get the value of a document element. + * + * document The document to be searched. + * xpath The xpath string to the element. + * + * Return null if multiple values exists or there is no + * value for the passed path. + */ +LTTNG_HIDDEN +char *config_document_get_element_value(struct config_document *document, const char *xpath); + +/* + * Check if an element exists inside a document. + * + * document The document to be searched. + * xpath The xpath string to the element. + * + * Returns 1 on success and 0 on failure. + */ +LTTNG_HIDDEN +int config_document_element_exist(struct config_document *document, const char *xpath); + +/* + * + */ +LTTNG_HIDDEN +void config_element_free(struct config_element *element); + +LTTNG_HIDDEN +struct config_element *config_element_create(const char *name, const char *value); + +/* + * Add a child element to an element. + * + * parent The parent element. + * child The element to add as a child. + * + * Returns zero if the session could be loaded successfully. Returns + * a negative LTTNG_ERR code on error. + */ +LTTNG_HIDDEN +int config_element_add_child(struct config_element *parent, const struct config_element *child); + +/* + * Insert element to an existing document. + * + * document The document to be modified. + * xpath The xpath string to the insertion path. + * element The element to be inserted. + * + * Returns zero if the session could be loaded successfully. Returns + * a negative LTTNG_ERR code on error. + */ +LTTNG_HIDDEN +int config_document_insert_element(struct config_document *document, const char *xpath, const struct config_element *element); + + #endif /* _CONFIG_H */