From: Jérémie Galarneau Date: Mon, 10 Feb 2014 16:44:55 +0000 (-0500) Subject: Add session configuration load capability to libconfig X-Git-Tag: v2.5.0-rc1~125 X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=commitdiff_plain;h=dcf266c050443412ec370bf86ddb3fddd2809eb9;hp=c864d6d788c58de94111911a9e10d221ec8bc398 Add session configuration load capability to libconfig Signed-off-by: Jérémie Galarneau Signed-off-by: David Goulet --- diff --git a/config/ac_define_dir.m4 b/config/ac_define_dir.m4 new file mode 100644 index 000000000..e15cea2c9 --- /dev/null +++ b/config/ac_define_dir.m4 @@ -0,0 +1,34 @@ +dnl @synopsis AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION]) +dnl +dnl This macro sets VARNAME to the expansion of the DIR variable, +dnl taking care of fixing up ${prefix} and such. +dnl +dnl VARNAME is then offered as both an output variable and a C +dnl preprocessor symbol. +dnl +dnl Example: +dnl +dnl AC_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.]) +dnl +dnl @category Misc +dnl @author Stepan Kasal +dnl @author Andreas Schwab +dnl @author Guido U. Draheim +dnl @author Alexandre Oliva +dnl @version 2006-10-13 +dnl @license AllPermissive + +AC_DEFUN([AC_DEFINE_DIR], [ + prefix_NONE= + exec_prefix_NONE= + test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix + test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix +dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn +dnl refers to ${prefix}. Thus we have to use `eval' twice. + eval ac_define_dir="\"[$]$2\"" + eval ac_define_dir="\"$ac_define_dir\"" + AC_SUBST($1, "$ac_define_dir") + AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3]) + test "$prefix_NONE" && prefix=NONE + test "$exec_prefix_NONE" && exec_prefix=NONE +]) diff --git a/configure.ac b/configure.ac index 4dfd37c31..56f7475cf 100644 --- a/configure.ac +++ b/configure.ac @@ -133,6 +133,8 @@ CONFDIR=`eval echo $sysconfdir` AC_SUBST(CONFDIR) AC_DEFINE_UNQUOTED([CONFIG_LTTNG_SYSTEM_CONFIGDIR],"$CONFDIR", [LTTng system configuration directory.]) +AC_DEFINE_DIR([CONFIG_LTTNG_SYSTEM_DATADIR],[datadir], [LTTng system data directory.]) + # # Check for pthread AC_CHECK_LIB([pthread], [pthread_create], [], diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index 818481771..87b6f84d6 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -143,6 +143,10 @@ enum lttng_error_code { LTTNG_ERR_EXCLUSION_INVAL = 110, /* Invalid event exclusion data */ LTTNG_ERR_EXCLUSION_NOMEM = 111, /* Lack of memory while processing event exclusions */ LTTNG_ERR_SAVE_IO_FAIL = 112, /* IO error while writting session configuration */ + LTTNG_ERR_LOAD_INVALID_CONFIG = 113, /* Invalid session configuration */ + LTTNG_ERR_LOAD_IO_FAIL = 114, /* IO error while reading a session configuration */ + LTTNG_ERR_LOAD_SESSION_NOT_FOUND = 115, /* Session configuration not found */ + LTTNG_ERR_LOAD_SESSION_NOENT = 116, /* Session file not found */ /* MUST be last element */ LTTNG_ERR_NR, /* Last element */ diff --git a/src/common/config/Makefile.am b/src/common/config/Makefile.am index 0bde30e4d..e8eaaca48 100644 --- a/src/common/config/Makefile.am +++ b/src/common/config/Makefile.am @@ -5,3 +5,7 @@ noinst_LTLIBRARIES = libconfig.la libconfig_la_SOURCES = ini.c ini.h config.c config.h libconfig_la_CPPFLAGS = $(XML_CPPFLAGS) libconfig_la_LIBADD = $(XML_LIBS) + +xmldir = $(datadir)/xml/lttng +xml_DATA = session.xsd +dist_xml_DATA = session.xsd diff --git a/src/common/config/config.c b/src/common/config/config.c index d4385738f..5e100a7ab 100644 --- a/src/common/config/config.c +++ b/src/common/config/config.c @@ -22,6 +22,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -32,6 +36,9 @@ #include #include #include +#include +#include +#include #include "config.h" #include "config-internal.h" @@ -42,6 +49,12 @@ struct handler_filter_args { void *user_data; }; +struct session_config_validation_ctx { + xmlSchemaParserCtxtPtr parser_ctx; + xmlSchemaPtr schema; + xmlSchemaValidCtxtPtr schema_validation_ctx; +}; + const char * const config_str_yes = "yes"; const char * const config_str_true = "true"; const char * const config_str_on = "on"; @@ -143,6 +156,13 @@ const char * const config_event_context_pthread_id = "PTHREAD_ID"; const char * const config_event_context_hostname = "HOSTNAME"; const char * const config_event_context_ip = "IP"; +struct consumer_output { + int enabled; + char *path; + char *control_uri; + char *data_uri; +}; + static int config_entry_handler_filter(struct handler_filter_args *args, const char *section, const char *name, const char *value) { @@ -504,9 +524,1974 @@ int config_writer_write_element_string(struct config_writer *writer, } ret = xmlTextWriterWriteElement(writer->writer, encoded_element_name, - encoded_value); + encoded_value); end: xmlFree(encoded_element_name); xmlFree(encoded_value); return ret > 0 ? 0 : ret; } + +static +void xml_error_handler(void *ctx, const char *format, ...) +{ + char *errMsg; + va_list args; + int ret; + + va_start(args, format); + ret = vasprintf(&errMsg, format, args); + if (ret == -1) { + ERR("String allocation failed in xml error handler"); + return; + } + va_end(args); + + fprintf(stderr, "XML Error: %s", errMsg); + free(errMsg); +} + +static +void fini_session_config_validation_ctx( + struct session_config_validation_ctx *ctx) +{ + if (ctx->parser_ctx) { + xmlSchemaFreeParserCtxt(ctx->parser_ctx); + } + + if (ctx->schema) { + xmlSchemaFree(ctx->schema); + } + + if (ctx->schema_validation_ctx) { + xmlSchemaFreeValidCtxt(ctx->schema_validation_ctx); + } + + memset(ctx, 0, sizeof(struct session_config_validation_ctx)); +} + +static +int init_session_config_validation_ctx( + struct session_config_validation_ctx *ctx) +{ + int ret; + + ctx->parser_ctx = xmlSchemaNewParserCtxt(DEFAULT_SESSION_CONFIG_XSD_PATH); + if (!ctx->parser_ctx) { + ERR("XSD parser context creation failed"); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + xmlSchemaSetParserErrors(ctx->parser_ctx, xml_error_handler, + xml_error_handler, NULL); + + ctx->schema = xmlSchemaParse(ctx->parser_ctx); + if (!ctx->schema) { + ERR("XSD parsing failed"); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + ctx->schema_validation_ctx = xmlSchemaNewValidCtxt(ctx->schema); + if (!ctx->schema_validation_ctx) { + ERR("XSD validation context creation failed"); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + xmlSchemaSetValidErrors(ctx->schema_validation_ctx, xml_error_handler, + xml_error_handler, NULL); + ret = 0; + +end: + if (ret) { + fini_session_config_validation_ctx(ctx); + } + + return ret; +} + +static +int parse_uint(xmlChar *str, uint64_t *val) +{ + int ret; + char *endptr; + + if (!str || !val) { + ret = -1; + goto end; + } + + *val = strtoull((const char *) str, &endptr, 10); + if (!endptr || *endptr) { + ret = -1; + } else { + ret = 0; + } + +end: + return ret; +} + +static +int parse_int(xmlChar *str, int64_t *val) +{ + int ret; + char *endptr; + + if (!str || !val) { + ret = -1; + goto end; + } + + *val = strtoll((const char *) str, &endptr, 10); + if (!endptr || *endptr) { + ret = -1; + } else { + ret = 0; + } + +end: + return ret; +} + +static +int parse_bool(xmlChar *str, int *val) +{ + int ret = 0; + + if (!str || !val) { + ret = -1; + goto end; + } + + if (!strcmp((const char *) str, config_xml_true)) { + *val = 1; + } else if (!strcmp((const char *) str, config_xml_false)) { + *val = 0; + } else { + WARN("Invalid boolean value encoutered (%s).", + (const char *) str); + ret = -1; + } +end: + return ret; +} + +static +int get_domain_type(xmlChar *domain) +{ + int ret; + + if (!domain) { + goto error; + } + + if (!strcmp((char *) domain, config_domain_type_kernel)) { + ret = LTTNG_DOMAIN_KERNEL; + } else if (!strcmp((char *) domain, config_domain_type_ust)) { + ret = LTTNG_DOMAIN_UST; + } else if (!strcmp((char *) domain, config_domain_type_jul)) { + ret = LTTNG_DOMAIN_JUL; + } else { + goto error; + } + + return ret; +error: + return -1; +} + +static +int get_buffer_type(xmlChar *buffer_type) +{ + int ret; + + if (!buffer_type) { + goto error; + } + + if (!strcmp((char *) buffer_type, config_buffer_type_global)) { + ret = LTTNG_BUFFER_GLOBAL; + } else if (!strcmp((char *) buffer_type, config_buffer_type_per_uid)) { + ret = LTTNG_BUFFER_PER_UID; + } else if (!strcmp((char *) buffer_type, config_buffer_type_per_pid)) { + ret = LTTNG_BUFFER_PER_PID; + } else { + goto error; + } + + return ret; +error: + return -1; +} + +static +int get_overwrite_mode(xmlChar *overwrite_mode) +{ + int ret; + + if (!overwrite_mode) { + goto error; + } + + if (!strcmp((char *) overwrite_mode, config_overwrite_mode_overwrite)) { + ret = 1; + } else if (!strcmp((char *) overwrite_mode, + config_overwrite_mode_discard)) { + ret = 0; + } else { + goto error; + } + + return ret; +error: + return -1; +} + +static +int get_output_type(xmlChar *output_type) +{ + int ret; + + if (!output_type) { + goto error; + } + + if (!strcmp((char *) output_type, config_output_type_mmap)) { + ret = LTTNG_EVENT_MMAP; + } else if (!strcmp((char *) output_type, config_output_type_splice)) { + ret = LTTNG_EVENT_SPLICE; + } else { + goto error; + } + + return ret; +error: + return -1; +} + +static +int get_event_type(xmlChar *event_type) +{ + int ret; + + if (!event_type) { + goto error; + } + + if (!strcmp((char *) event_type, config_event_type_all)) { + ret = LTTNG_EVENT_ALL; + } else if (!strcmp((char *) event_type, config_event_type_tracepoint)) { + ret = LTTNG_EVENT_TRACEPOINT; + } else if (!strcmp((char *) event_type, config_event_type_probe)) { + ret = LTTNG_EVENT_PROBE; + } else if (!strcmp((char *) event_type, config_event_type_function)) { + ret = LTTNG_EVENT_FUNCTION; + } else if (!strcmp((char *) event_type, + config_event_type_function_entry)) { + ret = LTTNG_EVENT_FUNCTION_ENTRY; + } else if (!strcmp((char *) event_type, config_event_type_noop)) { + ret = LTTNG_EVENT_NOOP; + } else if (!strcmp((char *) event_type, config_event_type_syscall)) { + ret = LTTNG_EVENT_SYSCALL; + } else { + goto error; + } + + return ret; +error: + return -1; +} + +static +int get_loglevel_type(xmlChar *loglevel_type) +{ + int ret; + + if (!loglevel_type) { + goto error; + } + + if (!strcmp((char *) loglevel_type, config_loglevel_type_all)) { + ret = LTTNG_EVENT_LOGLEVEL_ALL; + } else if (!strcmp((char *) loglevel_type, + config_loglevel_type_range)) { + ret = LTTNG_EVENT_LOGLEVEL_RANGE; + } else if (!strcmp((char *) loglevel_type, + config_loglevel_type_single)) { + ret = LTTNG_EVENT_LOGLEVEL_SINGLE; + } else { + goto error; + } + + return ret; +error: + return -1; +} + +/* + * Return the context type or -1 on error. + */ +static +int get_context_type(xmlChar *context_type) +{ + int ret; + + if (!context_type) { + goto error; + } + + if (!strcmp((char *) context_type, config_event_context_pid)) { + ret = LTTNG_EVENT_CONTEXT_PID; + } else if (!strcmp((char *) context_type, + config_event_context_procname)) { + ret = LTTNG_EVENT_CONTEXT_PROCNAME; + } else if (!strcmp((char *) context_type, + config_event_context_prio)) { + ret = LTTNG_EVENT_CONTEXT_PRIO; + } else if (!strcmp((char *) context_type, + config_event_context_nice)) { + ret = LTTNG_EVENT_CONTEXT_NICE; + } else if (!strcmp((char *) context_type, + config_event_context_vpid)) { + ret = LTTNG_EVENT_CONTEXT_VPID; + } else if (!strcmp((char *) context_type, + config_event_context_tid)) { + ret = LTTNG_EVENT_CONTEXT_TID; + } else if (!strcmp((char *) context_type, + config_event_context_vtid)) { + ret = LTTNG_EVENT_CONTEXT_VTID; + } else if (!strcmp((char *) context_type, + config_event_context_ppid)) { + ret = LTTNG_EVENT_CONTEXT_PPID; + } else if (!strcmp((char *) context_type, + config_event_context_vppid)) { + ret = LTTNG_EVENT_CONTEXT_VPPID; + } else if (!strcmp((char *) context_type, + config_event_context_pthread_id)) { + ret = LTTNG_EVENT_CONTEXT_PTHREAD_ID; + } else if (!strcmp((char *) context_type, + config_event_context_hostname)) { + ret = LTTNG_EVENT_CONTEXT_HOSTNAME; + } else if (!strcmp((char *) context_type, + config_event_context_ip)) { + ret = LTTNG_EVENT_CONTEXT_IP; + } else { + goto error; + } + + return ret; +error: + return -1; +} + +static +int init_domain(xmlNodePtr domain_node, struct lttng_domain *domain) +{ + int ret; + xmlNodePtr node; + + for (node = xmlFirstElementChild(domain_node); node; + node = xmlNextElementSibling(node)) { + if (!strcmp((const char *) node->name, config_element_type)) { + /* domain type */ + xmlChar *node_content = xmlNodeGetContent(node); + if (!node_content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = get_domain_type(node_content); + free(node_content); + if (ret < 0) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + domain->type = ret; + } else if (!strcmp((const char *) node->name, + config_element_buffer_type)) { + /* buffer type */ + xmlChar *node_content = xmlNodeGetContent(node); + if (!node_content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = get_buffer_type(node_content); + free(node_content); + if (ret < 0) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + domain->buf_type = ret; + } + } + ret = 0; +end: + return ret; +} + +static +int get_net_output_uris(xmlNodePtr net_output_node, char **control_uri, + char **data_uri) +{ + xmlNodePtr node; + + for (node = xmlFirstElementChild(net_output_node); node; + node = xmlNextElementSibling(node)) { + if (!strcmp((const char *) node->name, config_element_control_uri)) { + /* control_uri */ + *control_uri = (char *) xmlNodeGetContent(node); + if (!*control_uri) { + break; + } + } else { + /* data_uri */ + *data_uri = (char *) xmlNodeGetContent(node); + if (!*data_uri) { + break; + } + } + } + + return *control_uri || *data_uri ? 0 : -LTTNG_ERR_LOAD_INVALID_CONFIG; +} + +static +int process_consumer_output(xmlNodePtr consumer_output_node, + struct consumer_output *output) +{ + int ret; + xmlNodePtr node; + + assert(output); + + for (node = xmlFirstElementChild(consumer_output_node); node; + node = xmlNextElementSibling(node)) { + if (!strcmp((const char *) node->name, config_element_enabled)) { + xmlChar *enabled_str = xmlNodeGetContent(node); + + /* enabled */ + if (!enabled_str) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_bool(enabled_str, &output->enabled); + free(enabled_str); + if (ret) { + goto end; + } + } else { + xmlNodePtr output_type_node; + + /* destination */ + output_type_node = xmlFirstElementChild(node); + if (!output_type_node) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (!strcmp((const char *) output_type_node->name, + config_element_path)) { + /* path */ + output->path = (char *) xmlNodeGetContent(output_type_node); + if (!output->path) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + } else { + /* net_output */ + ret = get_net_output_uris(output_type_node, + &output->control_uri, &output->data_uri); + if (ret) { + goto end; + } + } + } + } + ret = 0; + +end: + if (ret) { + free(output->path); + free(output->control_uri); + free(output->data_uri); + memset(output, 0, sizeof(struct consumer_output)); + } + return ret; +} + +static +int create_session_net_output(const char *name, struct lttng_domain *domain, + 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); + if (!handle) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + if (!control_uri || !data_uri) { + uri = control_uri ? control_uri : data_uri; + control_uri = uri; + data_uri = uri; + } + + ret = lttng_set_consumer_url(handle, control_uri, data_uri); + lttng_destroy_handle(handle); +end: + return ret; +} + +static +int create_snapshot_session(const char *session_name, xmlNodePtr output_node) +{ + int ret; + xmlNodePtr node = NULL; + xmlNodePtr snapshot_output_list_node; + xmlNodePtr snapshot_output_node; + + assert(session_name); + + ret = lttng_create_session_snapshot(session_name, NULL); + if (ret) { + goto end; + } + + if (!output_node) { + goto end; + } + + snapshot_output_list_node = xmlFirstElementChild(output_node); + + /* Parse and create snapshot outputs */ + + for (snapshot_output_node = + xmlFirstElementChild(snapshot_output_list_node); + snapshot_output_node; snapshot_output_node = + xmlNextElementSibling(snapshot_output_node)) { + char *name = NULL; + uint64_t max_size = UINT64_MAX; + struct consumer_output output = { 0 }; + struct lttng_snapshot_output *snapshot_output = NULL; + + for (node = xmlFirstElementChild(snapshot_output_node); node; + node = xmlNextElementSibling(node)) { + if (!strcmp((const char *) node->name, + config_element_name)) { + /* name */ + name = (char *) xmlNodeGetContent(node); + if (!name) { + ret = -LTTNG_ERR_NOMEM; + goto error_snapshot_output; + } + } else if (!strcmp((const char *) node->name, + config_element_max_size)) { + xmlChar *content = xmlNodeGetContent(node); + + /* max_size */ + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto error_snapshot_output; + } + ret = parse_uint(content, &max_size); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto error_snapshot_output; + } + } else { + /* consumer_output */ + ret = process_consumer_output(node, &output); + if (ret) { + goto error_snapshot_output; + } + } + } + + snapshot_output = lttng_snapshot_output_create(); + if (!snapshot_output) { + ret = -LTTNG_ERR_NOMEM; + goto error_snapshot_output; + } + + ret = lttng_snapshot_output_set_name(name, snapshot_output); + if (ret) { + goto error_snapshot_output; + } + + ret = lttng_snapshot_output_set_size(max_size, snapshot_output); + if (ret) { + goto error_snapshot_output; + } + + if (output.path) { + ret = lttng_snapshot_output_set_ctrl_url(output.path, + snapshot_output); + if (ret) { + goto error_snapshot_output; + } + } else { + if (output.control_uri) { + ret = lttng_snapshot_output_set_ctrl_url(output.control_uri, + snapshot_output); + if (ret) { + goto error_snapshot_output; + } + } + + if (output.data_uri) { + ret = lttng_snapshot_output_set_data_url(output.data_uri, + snapshot_output); + if (ret) { + goto error_snapshot_output; + } + } + } + + ret = lttng_snapshot_add_output(session_name, snapshot_output); +error_snapshot_output: + free(name); + free(output.path); + free(output.control_uri); + free(output.data_uri); + lttng_snapshot_output_destroy(snapshot_output); + if (ret) { + goto end; + } + } +end: + return ret; +} + +static +int create_session(const char *name, + struct lttng_domain *kernel_domain, + struct lttng_domain *ust_domain, + struct lttng_domain *jul_domain, + xmlNodePtr output_node, + uint64_t live_timer_interval) +{ + int ret; + struct consumer_output output = { 0 }; + xmlNodePtr consumer_output_node; + + assert(name); + assert(kernel_domain); + assert(ust_domain); + assert(jul_domain); + + if (output_node) { + consumer_output_node = xmlFirstElementChild(output_node); + if (!consumer_output_node) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (strcmp((const char *) consumer_output_node->name, + config_element_consumer_output)) { + WARN("Invalid output type, expected %s node", + config_element_consumer_output); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + ret = process_consumer_output(consumer_output_node, &output); + if (ret) { + goto end; + } + } + + if (live_timer_interval != UINT64_MAX && + !output.control_uri && !output.data_uri) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (output.control_uri || output.data_uri) { + int i; + struct lttng_domain *domain; + struct lttng_domain *domains[] = + { kernel_domain, ust_domain, jul_domain }; + + /* network destination */ + if (live_timer_interval && live_timer_interval != UINT64_MAX) { + const char *url = output.control_uri ? + output.control_uri : output.data_uri; + + /* URL has to be provided, even if we'll overwrite it after. */ + ret = lttng_create_session_live(name, url, live_timer_interval); + } else { + ret = lttng_create_session(name, NULL); + } + if (ret) { + goto end; + } + + for (i = 0; i < (sizeof(domains) / sizeof(*domain)); i++) { + domain = domains[i]; + if (!domain) { + continue; + } + + ret = create_session_net_output(name, domain, output.control_uri, + output.data_uri); + if (ret) { + goto end; + } + } + } else { + /* either local output or no output */ + ret = lttng_create_session(name, output.path); + if (ret) { + goto end; + } + } +end: + free(output.path); + free(output.control_uri); + free(output.data_uri); + return ret; +} +static +int process_probe_attribute_node(xmlNodePtr probe_attribute_node, + struct lttng_event_probe_attr *attr) +{ + int ret; + + assert(probe_attribute_node); + assert(attr); + + if (!strcmp((const char *) probe_attribute_node->name, + config_element_address)) { + xmlChar *content; + uint64_t addr = 0; + + /* addr */ + content = xmlNodeGetContent(probe_attribute_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &addr); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + attr->addr = addr; + } else if (!strcmp((const char *) probe_attribute_node->name, + config_element_offset)) { + xmlChar *content; + uint64_t offset = 0; + + /* offset */ + content = xmlNodeGetContent(probe_attribute_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &offset); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + attr->offset = offset; + } else if (!strcmp((const char *) probe_attribute_node->name, + config_element_symbol_name)) { + xmlChar *content; + size_t name_len; + + /* symbol_name */ + content = xmlNodeGetContent(probe_attribute_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + name_len = strlen((char *) content); + if (name_len >= LTTNG_SYMBOL_NAME_LEN) { + WARN("symbol_name too long."); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + free(content); + goto end; + } + + strncpy(attr->symbol_name, (const char *) content, name_len); + free(content); + } + ret = 0; +end: + return ret; +} + +static +int process_event_node(xmlNodePtr event_node, struct lttng_handle *handle, + const char *channel_name) +{ + int ret, i; + xmlNodePtr node; + struct lttng_event event; + char **exclusions = NULL; + unsigned long exclusion_count = 0; + char *filter_expression = NULL; + + assert(event_node); + assert(handle); + assert(channel_name); + + memset(&event, 0, sizeof(event)); + + for (node = xmlFirstElementChild(event_node); node; + node = xmlNextElementSibling(node)) { + if (!strcmp((const char *) node->name, config_element_name)) { + xmlChar *content; + size_t name_len; + + /* name */ + content = xmlNodeGetContent(node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + name_len = strlen((char *) content); + if (name_len >= LTTNG_SYMBOL_NAME_LEN) { + WARN("Channel name too long."); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + free(content); + goto end; + } + + strncpy(event.name, (const char *) content, name_len); + free(content); + } else if (!strcmp((const char *) node->name, + config_element_enabled)) { + xmlChar *content = xmlNodeGetContent(node); + + /* enabled */ + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_bool(content, &event.enabled); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + } else if (!strcmp((const char *) node->name, + config_element_type)) { + xmlChar *content = xmlNodeGetContent(node); + + /* type */ + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = get_event_type(content); + free(content); + if (ret < 0) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + event.type = ret; + } else if (!strcmp((const char *) node->name, + config_element_loglevel_type)) { + xmlChar *content = xmlNodeGetContent(node); + + /* loglevel_type */ + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = get_loglevel_type(content); + free(content); + if (ret < 0) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + event.loglevel_type = ret; + } else if (!strcmp((const char *) node->name, + config_element_loglevel)) { + xmlChar *content; + int64_t loglevel = 0; + + /* loglevel */ + content = xmlNodeGetContent(node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_int(content, &loglevel); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (loglevel > INT_MAX || loglevel < INT_MIN) { + WARN("loglevel out of range."); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + event.loglevel = loglevel; + } else if (!strcmp((const char *) node->name, + config_element_filter)) { + xmlChar *content = + xmlNodeGetContent(node); + + /* filter */ + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + filter_expression = strdup((char *) content); + free(content); + if (!filter_expression) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + } else if (!strcmp((const char *) node->name, + config_element_exclusions)) { + xmlNodePtr exclusion_node; + int exclusion_index = 0; + + /* exclusions */ + if (exclusions) { + /* + * Exclusions has already been initialized, + * invalid file. + */ + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + exclusion_count = xmlChildElementCount(node); + if (!exclusion_count) { + continue; + } + + exclusions = zmalloc(exclusion_count * sizeof(char *)); + if (!exclusions) { + exclusion_count = 0; + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + for (exclusion_node = xmlFirstElementChild(node); exclusion_node; + exclusion_node = xmlNextElementSibling(exclusion_node)) { + xmlChar *content = + xmlNodeGetContent(exclusion_node); + + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + exclusions[exclusion_index] = strdup((const char *) content); + free(content); + if (!exclusions[exclusion_index]) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + exclusion_index++; + } + + event.exclusion = 1; + } else if (!strcmp((const char *) node->name, + config_element_attributes)) { + xmlNodePtr attribute_node = xmlFirstElementChild(node); + + /* attributes */ + if (!attribute_node) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (!strcmp((const char *) node->name, + config_element_probe_attributes)) { + xmlNodePtr probe_attribute_node; + + /* probe_attributes */ + for (probe_attribute_node = + xmlFirstElementChild(attribute_node); probe_attribute_node; + probe_attribute_node = xmlNextElementSibling( + probe_attribute_node)) { + + ret = process_probe_attribute_node(probe_attribute_node, + &event.attr.probe); + if (ret) { + goto end; + } + } + } else { + size_t sym_len; + xmlChar *content; + xmlNodePtr symbol_node = xmlFirstElementChild(attribute_node); + + /* function_attributes */ + content = xmlNodeGetContent(symbol_node); + if (!content) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + sym_len = strlen((char *) content); + if (sym_len >= LTTNG_SYMBOL_NAME_LEN) { + WARN("Function name too long."); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + free(content); + goto end; + } + + strncpy(event.attr.ftrace.symbol_name, (char *) content, + sym_len); + free(content); + } + } + } + + ret = lttng_enable_event_with_exclusions(handle, &event, channel_name, + filter_expression, exclusion_count, exclusions); +end: + for (i = 0; i < exclusion_count; i++) { + free(exclusions[i]); + } + + free(exclusions); + free(filter_expression); + return ret; +} + +static +int process_events_node(xmlNodePtr events_node, struct lttng_handle *handle, + const char *channel_name) +{ + int ret = 0; + xmlNodePtr node; + + assert(events_node); + assert(handle); + assert(channel_name); + + for (node = xmlFirstElementChild(events_node); node; + node = xmlNextElementSibling(node)) { + ret = process_event_node(node, handle, channel_name); + 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 ret; + + assert(attr_node); + assert(channel); + assert(contexts_node); + assert(events_node); + + if (!strcmp((const char *) attr_node->name, config_element_name)) { + xmlChar *content; + size_t name_len; + + /* name */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + name_len = strlen((char *) content); + if (name_len >= LTTNG_SYMBOL_NAME_LEN) { + WARN("Channel name too long."); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + free(content); + goto end; + } + + strncpy(channel->name, (const char *) content, name_len); + free(content); + } else if (!strcmp((const char *) attr_node->name, + config_element_enabled)) { + xmlChar *content; + int enabled; + + /* enabled */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_bool(content, &enabled); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + channel->enabled = enabled; + } else if (!strcmp((const char *) attr_node->name, + config_element_overwrite_mode)) { + xmlChar *content; + + /* overwrite_mode */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = get_overwrite_mode(content); + free(content); + if (ret < 0) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + channel->attr.overwrite = ret; + } else if (!strcmp((const char *) attr_node->name, + config_element_subbuf_size)) { + xmlChar *content; + + /* subbuffer_size */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &channel->attr.subbuf_size); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + } else if (!strcmp((const char *) attr_node->name, + config_element_num_subbuf)) { + xmlChar *content; + + /* subbuffer_count */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &channel->attr.num_subbuf); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + } else if (!strcmp((const char *) attr_node->name, + config_element_switch_timer_interval)) { + xmlChar *content; + uint64_t switch_timer_interval = 0; + + /* switch_timer_interval */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &switch_timer_interval); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (switch_timer_interval > UINT_MAX) { + WARN("switch_timer_interval out of range."); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + channel->attr.switch_timer_interval = + switch_timer_interval; + } else if (!strcmp((const char *) attr_node->name, + config_element_read_timer_interval)) { + xmlChar *content; + uint64_t read_timer_interval = 0; + + /* read_timer_interval */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &read_timer_interval); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (read_timer_interval > UINT_MAX) { + WARN("read_timer_interval out of range."); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + channel->attr.read_timer_interval = + read_timer_interval; + } else if (!strcmp((const char *) attr_node->name, + config_element_output_type)) { + xmlChar *content; + + /* output_type */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = get_output_type(content); + free(content); + if (ret < 0) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + channel->attr.output = ret; + } else if (!strcmp((const char *) attr_node->name, + config_element_tracefile_size)) { + xmlChar *content; + + /* tracefile_size */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &channel->attr.tracefile_size); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + } else if (!strcmp((const char *) attr_node->name, + config_element_tracefile_count)) { + xmlChar *content; + + /* tracefile_count */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &channel->attr.tracefile_count); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + } else if (!strcmp((const char *) attr_node->name, + config_element_live_timer_interval)) { + xmlChar *content; + uint64_t live_timer_interval = 0; + + /* live_timer_interval */ + content = xmlNodeGetContent(attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &live_timer_interval); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (live_timer_interval > UINT_MAX) { + WARN("live_timer_interval out of range."); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + channel->attr.live_timer_interval = + live_timer_interval; + } else if (!strcmp((const char *) attr_node->name, + config_element_events)) { + /* events */ + *events_node = attr_node; + } else { + /* contexts */ + *contexts_node = attr_node; + } + ret = 0; +end: + return ret; +} + +static +int process_context_node(xmlNodePtr context_node, + struct lttng_handle *handle, const char *channel_name) +{ + int ret; + struct lttng_event_context context; + xmlNodePtr context_child_node = xmlFirstElementChild(context_node); + + assert(handle); + assert(channel_name); + + if (!context_child_node) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + memset(&context, 0, sizeof(context)); + + if (!strcmp((const char *) context_child_node->name, + config_element_type)) { + /* type */ + xmlChar *content = xmlNodeGetContent(context_child_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = get_context_type(content); + free(content); + if (ret < 0) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + context.ctx = ret; + } else { + xmlNodePtr perf_attr_node; + + /* perf */ + context.ctx = LTTNG_EVENT_CONTEXT_PERF_COUNTER; + for (perf_attr_node = xmlFirstElementChild(context_child_node); + perf_attr_node; perf_attr_node = + xmlNextElementSibling(perf_attr_node)) { + if (!strcmp((const char *) perf_attr_node->name, + config_element_type)) { + xmlChar *content; + uint64_t type = 0; + + /* type */ + content = xmlNodeGetContent(perf_attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &type); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (type > UINT32_MAX) { + WARN("perf context type out of range."); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + context.u.perf_counter.type = type; + } else if (!strcmp((const char *) perf_attr_node->name, + config_element_config)) { + xmlChar *content; + uint64_t config = 0; + + /* config */ + content = xmlNodeGetContent(perf_attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(content, &config); + free(content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + context.u.perf_counter.config = config; + } else if (!strcmp((const char *) perf_attr_node->name, + config_element_name)) { + xmlChar *content; + size_t name_len; + + /* name */ + content = xmlNodeGetContent(perf_attr_node); + if (!content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + name_len = strlen((char *) content); + if (name_len >= LTTNG_SYMBOL_NAME_LEN) { + WARN("perf context name too long."); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + free(content); + goto end; + } + + strncpy(context.u.perf_counter.name, (const char *) content, + name_len); + free(content); + } + } + } + + ret = lttng_add_context(handle, &context, NULL, channel_name); +end: + return ret; +} + +static +int process_contexts_node(xmlNodePtr contexts_node, + struct lttng_handle *handle, const char *channel_name) +{ + int ret = 0; + xmlNodePtr context_node; + + for (context_node = xmlFirstElementChild(contexts_node); context_node; + context_node = xmlNextElementSibling(context_node)) { + ret = process_context_node(context_node, handle, channel_name); + if (ret) { + goto end; + } + } +end: + return ret; +} + +static +int process_domain_node(xmlNodePtr domain_node, const char *session_name) +{ + int ret; + struct lttng_domain domain = { 0 }; + struct lttng_handle *handle = NULL; + xmlNodePtr channels_node = NULL; + xmlNodePtr node; + + assert(session_name); + + ret = init_domain(domain_node, &domain); + if (ret) { + goto end; + } + + handle = lttng_create_handle(session_name, &domain); + if (!handle) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* get the channels node */ + for (node = xmlFirstElementChild(domain_node); node; + node = xmlNextElementSibling(node)) { + if (!strcmp((const char *) node->name, + config_element_channels)) { + channels_node = node; + break; + } + } + + if (!channels_node) { + goto end; + } + + /* create all channels */ + for (node = xmlFirstElementChild(channels_node); node; + node = xmlNextElementSibling(node)) { + struct lttng_channel channel; + xmlNodePtr contexts_node = NULL; + xmlNodePtr events_node = NULL; + xmlNodePtr channel_attr_node; + + memset(&channel, 0, sizeof(channel)); + lttng_channel_set_default_attr(&domain, &channel.attr); + + for (channel_attr_node = xmlFirstElementChild(node); + channel_attr_node; channel_attr_node = + xmlNextElementSibling(channel_attr_node)) { + ret = process_channel_attr_node(channel_attr_node, + &channel, &contexts_node, &events_node); + if (ret) { + goto end; + } + } + + ret = lttng_enable_channel(handle, &channel); + if (ret < 0) { + goto end; + } + + ret = process_events_node(events_node, handle, channel.name); + if (ret) { + goto end; + } + + ret = process_contexts_node(contexts_node, handle, + channel.name); + if (ret) { + goto end; + } + } +end: + lttng_destroy_handle(handle); + return ret; +} + +static +int process_session_node(xmlNodePtr session_node, const char *session_name, + int override) +{ + int ret, started = -1, snapshot_mode = -1; + uint64_t live_timer_interval = UINT64_MAX; + char *name = NULL; + xmlNodePtr domains_node = NULL; + xmlNodePtr output_node = NULL; + xmlNodePtr node; + struct lttng_domain *kernel_domain = NULL; + struct lttng_domain *ust_domain = NULL; + struct lttng_domain *jul_domain = NULL; + + for (node = xmlFirstElementChild(session_node); node; + node = xmlNextElementSibling(node)) { + if (!name && !strcmp((const char *) node->name, + config_element_name)) { + /* name */ + xmlChar *node_content = xmlNodeGetContent(node); + if (!node_content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + name = (char *) node_content; + } else if (!domains_node && !strcmp((const char *) node->name, + config_element_domains)) { + /* domains */ + domains_node = node; + } else if (started == -1 && !strcmp((const char *) node->name, + config_element_started)) { + /* started */ + xmlChar *node_content = xmlNodeGetContent(node); + if (!node_content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_bool(node_content, &started); + free(node_content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + } else if (!output_node && !strcmp((const char *) node->name, + config_element_output)) { + /* output */ + output_node = node; + } else { + /* attributes, snapshot_mode or live_timer_interval */ + xmlNodePtr attributes_child = + xmlFirstElementChild(node); + + if (!strcmp((const char *) attributes_child->name, + config_element_snapshot_mode)) { + /* snapshot_mode */ + xmlChar *snapshot_mode_content = + xmlNodeGetContent(attributes_child); + if (!snapshot_mode_content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_bool(snapshot_mode_content, &snapshot_mode); + free(snapshot_mode_content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + } else { + /* live_timer_interval */ + xmlChar *timer_interval_content = + xmlNodeGetContent(attributes_child); + if (!timer_interval_content) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = parse_uint(timer_interval_content, &live_timer_interval); + free(timer_interval_content); + if (ret) { + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + } + } + } + + if (!name) { + /* Mandatory attribute, as defined in the session XSD */ + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (session_name && strcmp(name, session_name)) { + /* This is not the session we are looking for */ + ret = -LTTNG_ERR_LOAD_SESSION_NOT_FOUND; + goto end; + } + + /* Init domains to create the session handles */ + for (node = xmlFirstElementChild(domains_node); node; + node = xmlNextElementSibling(node)) { + struct lttng_domain *domain; + + domain = zmalloc(sizeof(*domain)); + if (!domain) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + ret = init_domain(node, domain); + if (ret) { + goto domain_init_error; + } + + switch (domain->type) { + case LTTNG_DOMAIN_KERNEL: + kernel_domain = domain; + break; + case LTTNG_DOMAIN_UST: + ust_domain = domain; + break; + case LTTNG_DOMAIN_JUL: + jul_domain = domain; + break; + default: + WARN("Invalid domain type"); + goto domain_init_error; + } + continue; +domain_init_error: + free(domain); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + if (override) { + /* Destroy session if it exists */ + ret = lttng_destroy_session(name); + if (ret && ret != -LTTNG_ERR_SESS_NOT_FOUND) { + ERR("Failed to destroy existing session."); + goto end; + } + } + + /* Create session type depending on output type */ + if (snapshot_mode && snapshot_mode != -1) { + ret = create_snapshot_session(name, output_node); + } else if (live_timer_interval && + live_timer_interval != UINT64_MAX) { + ret = create_session(name, kernel_domain, ust_domain, jul_domain, + output_node, live_timer_interval); + } else { + /* regular session */ + ret = create_session(name, kernel_domain, ust_domain, jul_domain, + output_node, UINT64_MAX); + } + + if (ret) { + goto end; + } + + for (node = xmlFirstElementChild(domains_node); node; + node = xmlNextElementSibling(node)) { + ret = process_domain_node(node, name); + if (ret) { + goto end; + } + } + + if (started) { + ret = lttng_start_tracing(name); + if (ret) { + goto end; + } + } +end: + free(kernel_domain); + free(ust_domain); + free(jul_domain); + free(name); + return ret; +} + +static +int load_session_from_file(const char *path, const char *session_name, + struct session_config_validation_ctx *validation_ctx, int override) +{ + int ret, session_found = !session_name; + xmlDocPtr doc = NULL; + xmlNodePtr sessions_node; + xmlNodePtr session_node; + struct stat sb; + + assert(path); + assert(validation_ctx); + + ret = stat(path, &sb); + if (ret) { + ret = -LTTNG_ERR_LOAD_SESSION_NOENT; + goto end; + } + + doc = xmlParseFile(path); + if (!doc) { + ret = -LTTNG_ERR_LOAD_IO_FAIL; + goto end; + } + + ret = xmlSchemaValidateDoc(validation_ctx->schema_validation_ctx, doc); + if (ret) { + ERR("Session configuration file validation failed"); + ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; + goto end; + } + + sessions_node = xmlDocGetRootElement(doc); + 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: + xmlFreeDoc(doc); + if (!ret) { + ret = session_found ? 0 : -LTTNG_ERR_LOAD_SESSION_NOT_FOUND; + } + return ret; +} + +static +int load_session_from_path(const char *path, const char *session_name, + struct session_config_validation_ctx *validation_ctx, int override) +{ + int ret, session_found = !session_name; + struct stat sb; + DIR *directory = NULL; + + assert(path); + assert(validation_ctx); + + ret = stat(path, &sb); + if (ret) { + ret = -LTTNG_ERR_LOAD_SESSION_NOENT; + goto end; + } + if (S_ISDIR(sb.st_mode)) { + struct dirent *entry; + struct dirent *result; + char *file_path = NULL; + size_t path_len = strlen(path); + + if (path_len >= PATH_MAX) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + entry = zmalloc(sizeof(*entry)); + if (!entry) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + directory = opendir(path); + if (!directory) { + ret = -LTTNG_ERR_LOAD_IO_FAIL; + free(entry); + goto end; + } + + file_path = zmalloc(PATH_MAX); + if (!file_path) { + ret = -LTTNG_ERR_NOMEM; + free(entry); + goto end; + } + + strncpy(file_path, path, path_len); + if (file_path[path_len - 1] != '/') { + file_path[path_len++] = '/'; + } + + /* Search for *.lttng files */ + while (!readdir_r(directory, entry, &result) && result) { + size_t file_name_len = strlen(result->d_name); + + if (file_name_len <= + sizeof(DEFAULT_SESSION_CONFIG_FILE_EXTENSION)) { + continue; + } + + if (path_len + file_name_len > PATH_MAX) { + continue; + } + + if (strcmp(DEFAULT_SESSION_CONFIG_FILE_EXTENSION, + result->d_name + file_name_len - sizeof( + DEFAULT_SESSION_CONFIG_FILE_EXTENSION) + 1)) { + continue; + } + + strncpy(file_path + path_len, result->d_name, file_name_len); + file_path[path_len + file_name_len] = '\0'; + + ret = load_session_from_file(file_path, session_name, + validation_ctx, override); + if (session_name && !ret) { + session_found = 1; + break; + } + } + + free(entry); + free(file_path); + } else { + ret = load_session_from_file(path, session_name, + validation_ctx, override); + if (ret) { + goto end; + } else { + session_found = 1; + } + } + +end: + if (directory) { + if (closedir(directory)) { + PERROR("closedir"); + } + } + + if (!session_found) { + ret = -LTTNG_ERR_LOAD_SESSION_NOT_FOUND; + } + + return ret; +} + +LTTNG_HIDDEN +int config_load_session(const char *path, 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; + } + + if (!path) { + /* Try home path */ + char *home_path = utils_get_home_dir(); + if (home_path) { + char *path; + + ret = asprintf(&path, DEFAULT_SESSION_HOME_CONFIGPATH, + home_path); + if (ret < 0) { + goto end; + } + + ret = load_session_from_path(path, NULL, + &validation_ctx, 0); + if (ret && ret != -LTTNG_ERR_LOAD_SESSION_NOENT) { + free(path); + goto end; + } + + free(path); + } + + /* Try system session configuration path */ + ret = load_session_from_path(DEFAULT_SESSION_SYSTEM_CONFIGPATH, NULL, + &validation_ctx, 0); + if (ret && ret != -LTTNG_ERR_LOAD_SESSION_NOENT) { + goto end; + } + } else { + ret = access(path, F_OK); + if (ret < 0) { + PERROR("access"); + switch (errno) { + case ENOENT: + ret = -LTTNG_ERR_INVALID; + WARN("Session configuration path does not exist."); + break; + case EACCES: + ret = -LTTNG_ERR_EPERM; + break; + default: + ret = -LTTNG_ERR_UNK; + break; + } + goto end; + } + + ret = load_session_from_path(path, session_name, + &validation_ctx, override); + } +end: + fini_session_config_validation_ctx(&validation_ctx); + return ret; +} diff --git a/src/common/config/config.h b/src/common/config/config.h index f5ca42485..f840dd05d 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -185,4 +185,25 @@ LTTNG_HIDDEN int config_writer_write_element_string(struct config_writer *writer, const char *element_name, const char *value); +/* + * Load session configurations from a file. + * + * path Path to an LTTng session configuration file. All *.lttng files + * will be loaded if path is a directory. If path is NULL, the default + * paths will be searched in the following order: + * 1) $HOME/.lttng/sessions + * 2) /etc/lttng/sessions + * + * session_name Name of the session to load. Will load all + * sessions from path 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_session(const char *path, const char *session_name, + int override); + #endif /* _CONFIG_H */ diff --git a/src/common/defaults.h b/src/common/defaults.h index 221bb4811..7acabf181 100644 --- a/src/common/defaults.h +++ b/src/common/defaults.h @@ -125,6 +125,9 @@ #define DEFAULT_SESSION_SYSTEM_CONFIGPATH DEFAULT_SYSTEM_CONFIGPATH "/" \ DEFAULT_SESSION_PATH #define DEFAULT_SESSION_CONFIG_FILE_EXTENSION ".lttng" +#define DEFAULT_SESSION_CONFIG_XSD_PATH CONFIG_LTTNG_SYSTEM_DATADIR \ + "/xml/lttng/session.xsd" + #define DEFAULT_GLOBAL_APPS_UNIX_SOCK \ DEFAULT_LTTNG_RUNDIR "/" LTTNG_UST_SOCK_FILENAME diff --git a/tests/unit/ini_config/Makefile.am b/tests/unit/ini_config/Makefile.am index 98956f76a..1856828a0 100644 --- a/tests/unit/ini_config/Makefile.am +++ b/tests/unit/ini_config/Makefile.am @@ -9,7 +9,8 @@ noinst_PROGRAMS = ini_config EXTRA_DIST = test_ini_config ini_config_SOURCES = ini_config.c -ini_config_LDADD = $(LIBTAP) $(LIBCONFIG) $(LIBCOMMON) $(LIBHASHTABLE) +ini_config_LDADD = $(LIBTAP) $(LIBCONFIG) $(LIBCOMMON) $(LIBHASHTABLE) \ + $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la all-local: @if [ x"$(srcdir)" != x"$(builddir)" ]; then \