#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <stdbool.h>
#include <common/defaults.h>
#include <common/error.h>
#include <libxml/valid.h>
#include <libxml/xmlschemas.h>
#include <libxml/tree.h>
+#include <libxml/xpath.h>
#include <lttng/lttng.h>
#include <lttng/snapshot.h>
const char * const config_element_tracefile_size = "tracefile_size";
const char * const config_element_tracefile_count = "tracefile_count";
const char * const config_element_live_timer_interval = "live_timer_interval";
+LTTNG_HIDDEN const char * const config_element_discarded_events = "discarded_events";
+LTTNG_HIDDEN const char * const config_element_lost_packets = "lost_packets";
const char * const config_element_type = "type";
const char * const config_element_buffer_type = "buffer_type";
const char * const config_element_session = "session";
const char * const config_element_sessions = "sessions";
-const char * const config_element_context_perf = "perf";
-const char * const config_element_context_app = "app";
-const char * const config_element_context_app_provider_name = "provider_name";
-const char * const config_element_context_app_ctx_name = "ctx_name";
+LTTNG_HIDDEN const char * const config_element_context_perf = "perf";
+LTTNG_HIDDEN const char * const config_element_context_app = "app";
+LTTNG_HIDDEN const char * const config_element_context_app_provider_name = "provider_name";
+LTTNG_HIDDEN const char * const config_element_context_app_ctx_name = "ctx_name";
const char * const config_element_config = "config";
const char * const config_element_started = "started";
const char * const config_element_snapshot_mode = "snapshot_mode";
const char * const config_element_loglevel = "loglevel";
const char * const config_element_loglevel_type = "loglevel_type";
const char * const config_element_filter = "filter";
-const char * const config_element_filter_expression = "filter_expression";
+LTTNG_HIDDEN const char * const config_element_filter_expression = "filter_expression";
const char * const config_element_snapshot_outputs = "snapshot_outputs";
const char * const config_element_consumer_output = "consumer_output";
const char * const config_element_destination = "destination";
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";
const char * const config_event_context_hostname = "HOSTNAME";
const char * const config_event_context_ip = "IP";
const char * const config_event_context_perf_thread_counter = "PERF_THREAD_COUNTER";
-const char * const config_event_context_app = "APP";
-
+LTTNG_HIDDEN const char * const config_event_context_app = "APP";
+LTTNG_HIDDEN const char * const config_event_context_interruptible = "INTERRUPTIBLE";
+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,
+};
struct consumer_output {
int enabled;
ret = xmlTextWriterStartElement(writer->writer, encoded_element_name);
xmlFree(encoded_element_name);
end:
- return ret > 0 ? 0 : ret;
+ return ret >= 0 ? 0 : ret;
+}
+
+LTTNG_HIDDEN
+int config_writer_write_attribute(struct config_writer *writer,
+ const char *name, const char *value)
+{
+ int ret;
+ xmlChar *encoded_name = NULL;
+ xmlChar *encoded_value = NULL;
+
+ if (!writer || !writer->writer || !name || !name[0]) {
+ ret = -1;
+ goto end;
+ }
+
+ encoded_name = encode_string(name);
+ if (!encoded_name) {
+ ret = -1;
+ goto end;
+ }
+
+ encoded_value = encode_string(value);
+ if (!encoded_value) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = xmlTextWriterWriteAttribute(writer->writer, encoded_name,
+ encoded_value);
+end:
+ xmlFree(encoded_name);
+ xmlFree(encoded_value);
+ return ret >= 0 ? 0 : ret;
}
LTTNG_HIDDEN
ret = xmlTextWriterEndElement(writer->writer);
end:
- return ret > 0 ? 0 : ret;
+ return ret >= 0 ? 0 : ret;
}
LTTNG_HIDDEN
encoded_element_name, "%" PRIu64, value);
xmlFree(encoded_element_name);
end:
- return ret > 0 ? 0 : ret;
+ return ret >= 0 ? 0 : ret;
}
LTTNG_HIDDEN
encoded_element_name, "%" PRIi64, value);
xmlFree(encoded_element_name);
end:
- return ret > 0 ? 0 : ret;
+ return ret >= 0 ? 0 : ret;
}
LTTNG_HIDDEN
end:
xmlFree(encoded_element_name);
xmlFree(encoded_value);
- return ret > 0 ? 0 : ret;
+ return ret >= 0 ? 0 : ret;
}
static
} else if (!strcmp((char *) context_type,
config_event_context_ip)) {
ret = LTTNG_EVENT_CONTEXT_IP;
+ } else if (!strcmp((char *) context_type,
+ config_event_context_interruptible)) {
+ ret = LTTNG_EVENT_CONTEXT_INTERRUPTIBLE;
+ } else if (!strcmp((char *) context_type,
+ config_event_context_preemptible)) {
+ ret = LTTNG_EVENT_CONTEXT_PREEMPTIBLE;
+ } else if (!strcmp((char *) context_type,
+ config_event_context_need_reschedule)) {
+ ret = LTTNG_EVENT_CONTEXT_NEED_RESCHEDULE;
+ } else if (!strcmp((char *) context_type,
+ config_event_context_migratable)) {
+ ret = LTTNG_EVENT_CONTEXT_MIGRATABLE;
} else {
goto error;
}
}
static
-int create_session_net_output(const char *name, struct lttng_domain *domain,
- const char *control_uri, const char *data_uri)
+int create_session_net_output(const char *name, const char *control_uri,
+ const char *data_uri)
{
int ret;
struct lttng_handle *handle;
const char *uri = NULL;
assert(name);
- assert(domain);
- handle = lttng_create_handle(name, domain);
+ handle = lttng_create_handle(name, NULL);
if (!handle) {
ret = -LTTNG_ERR_NOMEM;
goto end;
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;
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;
}
if (output.control_uri || output.data_uri) {
- int i;
- struct lttng_domain *domain;
- struct lttng_domain *domains[] =
- { kernel_domain, ust_domain, jul_domain, log4j_domain };
-
/* network destination */
if (live_timer_interval && live_timer_interval != UINT64_MAX) {
/*
goto end;
}
- for (i = 0; i < (sizeof(domains) / sizeof(domains[0])); i++) {
- domain = domains[i];
- if (!domain) {
- continue;
- }
-
- ret = create_session_net_output(name, domain, output.control_uri,
- output.data_uri);
- if (ret) {
- goto end;
- }
+ ret = create_session_net_output(name, output.control_uri,
+ output.data_uri);
+ if (ret) {
+ goto end;
}
+
} else {
/* either local output or no output */
ret = lttng_create_session(name, output.path);
static
int process_event_node(xmlNodePtr event_node, struct lttng_handle *handle,
- const char *channel_name)
+ const char *channel_name, const enum process_event_node_phase phase)
{
- int ret, i;
+ int ret = 0, i;
xmlNodePtr node;
struct lttng_event event;
char **exclusions = NULL;
goto end;
}
+ free(filter_expression);
filter_expression = strdup((char *) content);
free(content);
if (!filter_expression) {
}
}
- ret = lttng_enable_event_with_exclusions(handle, &event, channel_name,
- filter_expression, exclusion_count, exclusions);
- if (ret) {
- goto end;
- }
-
- if (!event.enabled) {
- /*
- * Note that we should use lttng_disable_event_ext() (2.6+) to
- * eliminate the risk of clashing on events of the same
- * name (with different event types and loglevels).
- *
- * Unfortunately, lttng_disable_event_ext() only performs a
- * match on the name and event type and errors out if any other
- * event attribute is not set to its default value.
- *
- * This will disable all events that match this name.
- */
- ret = lttng_disable_event(handle, event.name, channel_name);
+ if ((event.enabled && phase == ENABLE) || phase == CREATION) {
+ ret = lttng_enable_event_with_exclusions(handle, &event, channel_name,
+ filter_expression, exclusion_count, exclusions);
+ if (ret < 0) {
+ WARN("Enabling event (name:%s) on load failed.", event.name);
+ ret = -LTTNG_ERR_LOAD_INVALID_CONFIG;
+ goto end;
+ }
}
end:
for (i = 0; i < exclusion_count; i++) {
const char *channel_name)
{
int ret = 0;
+ struct lttng_event event;
xmlNodePtr node;
assert(events_node);
for (node = xmlFirstElementChild(events_node); node;
node = xmlNextElementSibling(node)) {
- ret = process_event_node(node, handle, channel_name);
+ ret = process_event_node(node, handle, channel_name, CREATION);
if (ret) {
goto end;
}
}
+
+ /*
+ * Disable all events to enable only the necessary events.
+ * Limitations regarding lttng_disable_events and tuple descriptor
+ * force this approach.
+ */
+ memset(&event, 0, sizeof(event));
+ event.loglevel = -1;
+ event.type = LTTNG_EVENT_ALL;
+ ret = lttng_disable_event_ext(handle, &event, channel_name, NULL);
+ if (ret) {
+ goto end;
+ }
+
+ for (node = xmlFirstElementChild(events_node); node;
+ node = xmlNextElementSibling(node)) {
+ ret = process_event_node(node, handle, channel_name, ENABLE);
+ if (ret) {
+ goto end;
+ }
+ }
+
end:
return ret;
}
static
-int process_channel_attr_node(xmlNodePtr attr_node,
- struct lttng_channel *channel, xmlNodePtr *contexts_node,
- xmlNodePtr *events_node)
+int process_channel_attr_node(xmlNodePtr attr_node, bool snapshot_mode,
+ enum lttng_domain_type domain, struct lttng_channel *channel,
+ xmlNodePtr *contexts_node, xmlNodePtr *events_node)
{
int ret;
/* contexts */
*contexts_node = attr_node;
}
+
ret = 0;
end:
return ret;
static
-int process_domain_node(xmlNodePtr domain_node, const char *session_name)
+int process_domain_node(xmlNodePtr domain_node, const char *session_name,
+ bool snapshot_mode)
{
int ret;
struct lttng_domain domain = { 0 };
channel_attr_node; channel_attr_node =
xmlNextElementSibling(channel_attr_node)) {
ret = process_channel_attr_node(channel_attr_node,
- &channel, &contexts_node, &events_node);
+ snapshot_mode, domain.type, &channel,
+ &contexts_node, &events_node);
if (ret) {
goto end;
}
}
+ if (!channel.name) {
+ ERR("Encountered a channel with no name attribute.");
+ ret = -LTTNG_ERR_LOAD_INVALID_CONFIG;
+ goto end;
+ }
+
ret = lttng_enable_channel(handle, &channel);
if (ret < 0) {
goto end;
for (node = xmlFirstElementChild(domains_node); node;
node = xmlNextElementSibling(node)) {
- ret = process_domain_node(node, (const char *) name);
+ ret = process_domain_node(node, (const char *) name,
+ snapshot_mode == 1);
if (ret) {
goto 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)
}
}
- if (!session_found) {
- ret = -LTTNG_ERR_LOAD_SESSION_NOENT;
+ if (session_found) {
+ ret = 0;
}
return ret;
int override, unsigned int autoload)
{
int ret;
+ bool session_loaded = false;
const char *path_ptr = NULL;
struct session_config_validation_ctx validation_ctx = { 0 };
* Continue even if the session was found since we have to try
* the system wide sessions.
*/
+ session_loaded = true;
}
}
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);
*/
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);
+}