Fix session-config: mem leak and uninitialized ret value
[lttng-tools.git] / src / common / config / session-config.c
index 743b5143893a4364cf5bf4f314e408102f77824d..3a1ff795c6e5e9ae66e7cbdb51782a34a2e583de 100644 (file)
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <stdbool.h>
 
 #include <common/defaults.h>
 #include <common/error.h>
@@ -37,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>
 
@@ -127,6 +129,8 @@ const char * const config_element_pid_tracker = "pid_tracker";
 const char * const config_element_trackers = "trackers";
 const char * const config_element_targets = "targets";
 const char * const config_element_target_pid = "pid_target";
+const char * const config_element_omit_name = "omit_name";
+const char * const config_element_omit_output = "omit_output";
 
 const char * const config_domain_type_kernel = "KERNEL";
 const char * const config_domain_type_ust = "UST";
@@ -177,6 +181,9 @@ LTTNG_HIDDEN const char * const config_event_context_preemptible = "PREEMPTIBLE"
 LTTNG_HIDDEN const char * const config_event_context_need_reschedule = "NEED_RESCHEDULE";
 LTTNG_HIDDEN const char * const config_event_context_migratable = "MIGRATABLE";
 
+/* Deprecated symbols */
+const char * const config_element_perf;
+
 enum process_event_node_phase {
        CREATION = 0,
        ENABLE = 1,
@@ -1211,7 +1218,7 @@ int create_snapshot_session(const char *session_name, xmlNodePtr output_node)
                        snapshot_output_node; snapshot_output_node =
                        xmlNextElementSibling(snapshot_output_node)) {
                char *name = NULL;
-               uint64_t max_size = UINT64_MAX;
+               uint64_t max_size = DEFAULT_SNAPSHOT_MAX_SIZE;
                struct consumer_output output = { 0 };
                struct lttng_snapshot_output *snapshot_output = NULL;
 
@@ -1255,6 +1262,18 @@ int create_snapshot_session(const char *session_name, xmlNodePtr output_node)
                        goto error_snapshot_output;
                }
 
+               if (!name) {
+                       /* Generate a default name */
+                       int pret;
+                       pret = asprintf(&name, DEFAULT_SNAPSHOT_NAME "-%" PRIu32,
+                                       lttng_snapshot_output_get_id(snapshot_output));
+                       if (pret < 0) {
+                               name = NULL;
+                               PERROR("snprintf output name");
+                               goto error_snapshot_output;
+                       }
+               }
+
                ret = lttng_snapshot_output_set_name(name, snapshot_output);
                if (ret) {
                        goto error_snapshot_output;
@@ -1607,6 +1626,7 @@ int process_event_node(xmlNodePtr event_node, struct lttng_handle *handle,
                                goto end;
                        }
 
+                       free(filter_expression);
                        filter_expression = strdup((char *) content);
                        free(content);
                        if (!filter_expression) {
@@ -2717,6 +2737,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)
@@ -2837,8 +2897,8 @@ end:
                }
        }
 
-       if (!session_found) {
-               ret = -LTTNG_ERR_LOAD_SESSION_NOENT;
+       if (session_found) {
+               ret = 0;
        }
 
        return ret;
@@ -2883,6 +2943,7 @@ int config_load_session(const char *path, const char *session_name,
                int override, unsigned int autoload)
 {
        int ret;
+       bool session_loaded = false;
        const char *path_ptr = NULL;
        struct session_config_validation_ctx validation_ctx = { 0 };
 
@@ -2941,6 +3002,7 @@ int config_load_session(const char *path, const char *session_name,
                                 * Continue even if the session was found since we have to try
                                 * the system wide sessions.
                                 */
+                               session_loaded = true;
                        }
                }
 
@@ -2963,6 +3025,9 @@ int config_load_session(const char *path, const char *session_name,
                if (path_ptr) {
                        ret = load_session_from_path(path_ptr, session_name,
                                        &validation_ctx, override);
+                       if (!ret) {
+                               session_loaded = true;
+                       }
                }
        } else {
                ret = access(path, F_OK);
@@ -2995,5 +3060,585 @@ end:
                 */
                ret = 0;
        }
+
+       if (session_loaded && ret == -LTTNG_ERR_LOAD_SESSION_NOENT) {
+               /* A matching session was found in one of the search paths. */
+               ret = 0;
+       }
        return ret;
 }
+
+static
+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:
+       fini_session_config_validation_ctx(&validation_ctx);
+       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;
+       }
+
+       free(document);
+}
+
+LTTNG_HIDDEN
+int config_document_replace_element_value(struct config_document *document,
+               const char *xpath, const char *value)
+{
+       int ret = 0;
+       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:
+       fini_session_config_validation_ctx(&validation_ctx);
+       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 (xpath_result_set->nodeTab[0]->type != XML_NAMESPACE_DECL)
+                  xpath_result_set->nodeTab[0] = NULL;
+       if (!old_node) {
+               ret = -LTTNG_ERR_CONFIG_REPLACEMENT;
+               xmlFreeNode(copy);
+               goto end;
+       }
+       xmlUnlinkNode(old_node);
+       xmlFreeNode(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;
+               goto end;
+       }
+
+       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->element) {
+               xmlFreeNode(element->element);
+       }
+
+       free(element);
+}
This page took 0.031722 seconds and 5 git commands to generate.