Config: utility functions for direct configurations element manipulations
authorJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Tue, 28 Jun 2016 19:30:27 +0000 (15:30 -0400)
committerJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Wed, 29 Jun 2016 22:35:15 +0000 (18:35 -0400)
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 <jonathan.rajotte-julien@efficios.com>
src/common/config/config-internal.h
src/common/config/session-config.c
src/common/config/session-config.h

index ff3e6293ec485a4485701810f028445794100375..f6de9005f274976d16241c0097cfc0366be0eba1 100644 (file)
  */
 
 #include <libxml/xmlwriter.h>
+#include <libxml/tree.h>
 #include <stdio.h>
 
 struct config_writer {
        xmlTextWriterPtr writer;
 };
+
+struct config_document {
+       xmlDocPtr document;
+};
+
+struct config_element {
+       xmlNodePtr element;
+};
+
+
+
index f032e1a34c558fa682feb3ab2a62d684052811d5..616434ecf572955d7c1952b9ba88b3fa9c17d0e3 100644 (file)
@@ -38,6 +38,7 @@
 #include <libxml/valid.h>
 #include <libxml/xmlschemas.h>
 #include <libxml/tree.h>
+#include <libxml/xpath.h>
 #include <lttng/lttng.h>
 #include <lttng/snapshot.h>
 
@@ -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);
+}
index 939cb6cbf0ff1cd4e76e2c819ffc2e178e241bbd..7b068e838c1da68f782e5e8b3b225b0dbbec2ab6 100644 (file)
@@ -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 */
This page took 0.041205 seconds and 5 git commands to generate.