X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Fconfig%2Fconfig.c;h=f93c5a678f12df5e8024bc21197a7900df12ce84;hp=4eaa274e82ce9e24d7f06d29c0c73ba8a80793d4;hb=6c1c0768320135c6936c371b09731851b508c023;hpb=c33e6729ebef69f69a239dd7aee61e802ea29ab6 diff --git a/src/common/config/config.c b/src/common/config/config.c index 4eaa274e8..f93c5a678 100644 --- a/src/common/config/config.c +++ b/src/common/config/config.c @@ -16,6 +16,7 @@ */ #define _GNU_SOURCE +#define _LGPL_SOURCE #include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -118,6 +118,8 @@ const char * const config_element_max_size = "max_size"; const char * const config_domain_type_kernel = "KERNEL"; const char * const config_domain_type_ust = "UST"; const char * const config_domain_type_jul = "JUL"; +const char * const config_domain_type_log4j = "LOG4J"; +const char * const config_domain_type_python = "PYTHON"; const char * const config_buffer_type_per_pid = "PER_PID"; const char * const config_buffer_type_per_uid = "PER_UID"; @@ -155,6 +157,7 @@ const char * const config_event_context_vppid = "VPPID"; 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"; +const char * const config_event_context_perf_thread_counter = "PERF_THREAD_COUNTER"; struct consumer_output { int enabled; @@ -192,54 +195,73 @@ int config_get_section_entries(const char *override_path, const char *section, config_entry_handler_cb handler, void *user_data) { int ret = 0; + char *path; FILE *config_file = NULL; struct handler_filter_args filter = { section, handler, user_data }; + /* First, try system-wide conf. file. */ + path = DEFAULT_DAEMON_SYSTEM_CONFIGPATH; + + config_file = fopen(path, "r"); + if (config_file) { + DBG("Loading daemon conf file at %s", path); + /* + * Return value is not very important here since error or not, we + * continue and try the next possible conf. file. + */ + (void) ini_parse_file(config_file, + (ini_entry_handler) config_entry_handler_filter, + (void *) &filter); + fclose(config_file); + } + + /* Second is the user local configuration. */ + path = utils_get_home_dir(); + if (path) { + char fullpath[PATH_MAX]; + + ret = snprintf(fullpath, sizeof(fullpath), + DEFAULT_DAEMON_HOME_CONFIGPATH, path); + if (ret < 0) { + PERROR("snprintf user conf. path"); + goto error; + } + + config_file = fopen(fullpath, "r"); + if (config_file) { + DBG("Loading daemon user conf file at %s", path); + /* + * Return value is not very important here since error or not, we + * continue and try the next possible conf. file. + */ + (void) ini_parse_file(config_file, + (ini_entry_handler) config_entry_handler_filter, + (void *) &filter); + fclose(config_file); + } + } + + /* Final path is the one that the user might have provided. */ if (override_path) { config_file = fopen(override_path, "r"); if (config_file) { - DBG("Loaded daemon configuration file at %s", - override_path); + DBG("Loading daemon command line conf file at %s", override_path); + (void) ini_parse_file(config_file, + (ini_entry_handler) config_entry_handler_filter, + (void *) &filter); + fclose(config_file); } else { ERR("Failed to open daemon configuration file at %s", override_path); ret = -ENOENT; - goto end; - } - } else { - char *path = utils_get_home_dir(); - - /* Try to open the user's daemon configuration file */ - if (path) { - ret = asprintf(&path, DEFAULT_DAEMON_HOME_CONFIGPATH, path); - if (ret < 0) { - goto end; - } - - ret = 0; - config_file = fopen(path, "r"); - if (config_file) { - DBG("Loaded daemon configuration file at %s", path); - } - - free(path); - } - - /* Try to open the system daemon configuration file */ - if (!config_file) { - config_file = fopen(DEFAULT_DAEMON_HOME_CONFIGPATH, "r"); + goto error; } } - if (!config_file) { - DBG("No daemon configuration file found."); - goto end; - } - - ret = ini_parse_file(config_file, - (ini_entry_handler) config_entry_handler_filter, (void *) &filter); + /* Everything went well. */ + ret = 0; -end: +error: return ret; } @@ -334,7 +356,7 @@ end: } LTTNG_HIDDEN -struct config_writer *config_writer_create(int fd_output) +struct config_writer *config_writer_create(int fd_output, int indent) { int ret; struct config_writer *writer; @@ -360,12 +382,12 @@ struct config_writer *config_writer_create(int fd_output) ret = xmlTextWriterSetIndentString(writer->writer, BAD_CAST config_xml_indent_string); - if (ret) { + if (ret) { goto error_destroy; } - ret = xmlTextWriterSetIndent(writer->writer, 1); - if (ret) { + ret = xmlTextWriterSetIndent(writer->writer, indent); + if (ret) { goto error_destroy; } @@ -540,11 +562,11 @@ void xml_error_handler(void *ctx, const char *format, ...) va_start(args, format); ret = vasprintf(&errMsg, format, args); + va_end(args); if (ret == -1) { ERR("String allocation failed in xml error handler"); return; } - va_end(args); fprintf(stderr, "XML Error: %s", errMsg); free(errMsg); @@ -569,13 +591,50 @@ void fini_session_config_validation_ctx( memset(ctx, 0, sizeof(struct session_config_validation_ctx)); } +static +char *get_session_config_xsd_path() +{ + char *xsd_path; + const char *base_path = getenv(DEFAULT_SESSION_CONFIG_XSD_PATH_ENV); + size_t base_path_len; + size_t max_path_len; + + if (!base_path) { + base_path = DEFAULT_SESSION_CONFIG_XSD_PATH; + } + + base_path_len = strlen(base_path); + max_path_len = base_path_len + + sizeof(DEFAULT_SESSION_CONFIG_XSD_FILENAME) + 1; + xsd_path = zmalloc(max_path_len); + if (!xsd_path) { + goto end; + } + + strncpy(xsd_path, base_path, max_path_len); + if (xsd_path[base_path_len - 1] != '/') { + xsd_path[base_path_len++] = '/'; + } + + strncpy(xsd_path + base_path_len, DEFAULT_SESSION_CONFIG_XSD_FILENAME, + max_path_len - base_path_len); +end: + return xsd_path; +} + static int init_session_config_validation_ctx( struct session_config_validation_ctx *ctx) { int ret; + char *xsd_path = get_session_config_xsd_path(); + + if (!xsd_path) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } - ctx->parser_ctx = xmlSchemaNewParserCtxt(DEFAULT_SESSION_CONFIG_XSD_PATH); + ctx->parser_ctx = xmlSchemaNewParserCtxt(xsd_path); if (!ctx->parser_ctx) { ERR("XSD parser context creation failed"); ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; @@ -607,6 +666,7 @@ end: fini_session_config_validation_ctx(ctx); } + free(xsd_path); return ret; } @@ -692,6 +752,10 @@ int get_domain_type(xmlChar *domain) ret = LTTNG_DOMAIN_UST; } else if (!strcmp((char *) domain, config_domain_type_jul)) { ret = LTTNG_DOMAIN_JUL; + } else if (!strcmp((char *) domain, config_domain_type_log4j)) { + ret = LTTNG_DOMAIN_LOG4J; + } else if (!strcmp((char *) domain, config_domain_type_python)) { + ret = LTTNG_DOMAIN_PYTHON; } else { goto error; } @@ -1179,6 +1243,7 @@ int create_session(const char *name, struct lttng_domain *kernel_domain, struct lttng_domain *ust_domain, struct lttng_domain *jul_domain, + struct lttng_domain *log4j_domain, xmlNodePtr output_node, uint64_t live_timer_interval) { @@ -1187,9 +1252,6 @@ int create_session(const char *name, xmlNodePtr consumer_output_node; assert(name); - assert(kernel_domain); - assert(ust_domain); - assert(jul_domain); if (output_node) { consumer_output_node = xmlFirstElementChild(output_node); @@ -1222,15 +1284,16 @@ int create_session(const char *name, int i; struct lttng_domain *domain; struct lttng_domain *domains[] = - { kernel_domain, ust_domain, jul_domain }; + { kernel_domain, ust_domain, jul_domain, log4j_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); + /* + * URLs are provided for sure since the test above make sure that + * with a live timer the data and control URIs are provided. So, + * NULL is passed here and will be set right after. + */ + ret = lttng_create_session_live(name, NULL, live_timer_interval); } else { ret = lttng_create_session(name, NULL); } @@ -1238,7 +1301,7 @@ int create_session(const char *name, goto end; } - for (i = 0; i < (sizeof(domains) / sizeof(*domain)); i++) { + for (i = 0; i < (sizeof(domains) / sizeof(domains[0])); i++) { domain = domains[i]; if (!domain) { continue; @@ -1901,7 +1964,9 @@ int process_context_node(xmlNodePtr context_node, xmlNodePtr perf_attr_node; /* perf */ - context.ctx = LTTNG_EVENT_CONTEXT_PERF_COUNTER; + context.ctx = handle->domain.type == LTTNG_DOMAIN_KERNEL ? + LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER : + LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER; for (perf_attr_node = xmlFirstElementChild(context_child_node); perf_attr_node; perf_attr_node = xmlNextElementSibling(perf_attr_node)) { @@ -2092,6 +2157,8 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, struct lttng_domain *kernel_domain = NULL; struct lttng_domain *ust_domain = NULL; struct lttng_domain *jul_domain = NULL; + struct lttng_domain *log4j_domain = NULL; + struct lttng_domain *python_domain = NULL; for (node = xmlFirstElementChild(session_node); node; node = xmlNextElementSibling(node)) { @@ -2101,7 +2168,7 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, xmlChar *node_content = xmlNodeGetContent(node); if (!node_content) { ret = -LTTNG_ERR_NOMEM; - goto end; + goto error; } name = (char *) node_content; @@ -2115,14 +2182,14 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, xmlChar *node_content = xmlNodeGetContent(node); if (!node_content) { ret = -LTTNG_ERR_NOMEM; - goto end; + goto error; } ret = parse_bool(node_content, &started); free(node_content); if (ret) { ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; - goto end; + goto error; } } else if (!output_node && !strcmp((const char *) node->name, config_element_output)) { @@ -2140,14 +2207,14 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, xmlNodeGetContent(attributes_child); if (!snapshot_mode_content) { ret = -LTTNG_ERR_NOMEM; - goto end; + goto error; } ret = parse_bool(snapshot_mode_content, &snapshot_mode); free(snapshot_mode_content); if (ret) { ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; - goto end; + goto error; } } else { /* live_timer_interval */ @@ -2155,14 +2222,14 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, xmlNodeGetContent(attributes_child); if (!timer_interval_content) { ret = -LTTNG_ERR_NOMEM; - goto end; + goto error; } ret = parse_uint(timer_interval_content, &live_timer_interval); free(timer_interval_content); if (ret) { ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; - goto end; + goto error; } } } @@ -2171,13 +2238,13 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, if (!name) { /* Mandatory attribute, as defined in the session XSD */ ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; - goto end; + goto error; } 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; + ret = -LTTNG_ERR_NO_SESSION; + goto error; } /* Init domains to create the session handles */ @@ -2188,7 +2255,7 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, domain = zmalloc(sizeof(*domain)); if (!domain) { ret = -LTTNG_ERR_NOMEM; - goto end; + goto error; } ret = init_domain(node, domain); @@ -2218,6 +2285,20 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, } jul_domain = domain; break; + case LTTNG_DOMAIN_LOG4J: + if (log4j_domain) { + /* Same domain seen twice, invalid! */ + goto domain_init_error; + } + log4j_domain = domain; + break; + case LTTNG_DOMAIN_PYTHON: + if (python_domain) { + /* Same domain seen twice, invalid! */ + goto domain_init_error; + } + python_domain = domain; + break; default: WARN("Invalid domain type"); goto domain_init_error; @@ -2226,7 +2307,7 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, domain_init_error: free(domain); ret = -LTTNG_ERR_LOAD_INVALID_CONFIG; - goto end; + goto error; } if (override) { @@ -2234,7 +2315,7 @@ domain_init_error: ret = lttng_destroy_session(name); if (ret && ret != -LTTNG_ERR_SESS_NOT_FOUND) { ERR("Failed to destroy existing session."); - goto end; + goto error; } } @@ -2244,15 +2325,14 @@ domain_init_error: } 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); + log4j_domain, output_node, live_timer_interval); } else { /* regular session */ ret = create_session(name, kernel_domain, ust_domain, jul_domain, - output_node, UINT64_MAX); + log4j_domain, output_node, UINT64_MAX); } - if (ret) { - goto end; + goto error; } for (node = xmlFirstElementChild(domains_node); node; @@ -2269,14 +2349,48 @@ domain_init_error: goto end; } } + end: + if (ret < 0) { + ERR("Failed to load session %s: %s", name, lttng_strerror(ret)); + lttng_destroy_session(name); + } + +error: free(kernel_domain); free(ust_domain); free(jul_domain); + free(log4j_domain); + free(python_domain); free(name); return ret; } +/* + * Return 1 if the given path is readable by the current UID or 0 if not. + * Return -1 if the path is EPERM. + */ +static int validate_file_read_creds(const char *path) +{ + int ret; + + assert(path); + + /* Can we read the file. */ + ret = access(path, R_OK); + if (!ret) { + goto valid; + } + if (errno == EACCES) { + return -1; + } else { + /* Invalid. */ + return 0; + } +valid: + return 1; +} + static int load_session_from_file(const char *path, const char *session_name, struct session_config_validation_ctx *validation_ctx, int override) @@ -2285,14 +2399,17 @@ int load_session_from_file(const char *path, const char *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; + ret = validate_file_read_creds(path); + if (ret != 1) { + if (ret == -1) { + ret = -LTTNG_ERR_EPERM; + } else { + ret = -LTTNG_ERR_LOAD_SESSION_NOENT; + } goto end; } @@ -2328,7 +2445,7 @@ int load_session_from_file(const char *path, const char *session_name, end: xmlFreeDoc(doc); if (!ret) { - ret = session_found ? 0 : -LTTNG_ERR_LOAD_SESSION_NOT_FOUND; + ret = session_found ? 0 : -LTTNG_ERR_LOAD_SESSION_NOENT; } return ret; } @@ -2338,18 +2455,26 @@ 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; + directory = opendir(path); + if (!directory) { + switch (errno) { + case ENOTDIR: + /* Try the file loading. */ + break; + case ENOENT: + ret = -LTTNG_ERR_LOAD_SESSION_NOENT; + goto end; + default: + ret = -LTTNG_ERR_LOAD_IO_FAIL; + goto end; + } } - if (S_ISDIR(sb.st_mode)) { + if (directory) { struct dirent *entry; struct dirent *result; char *file_path = NULL; @@ -2366,13 +2491,6 @@ int load_session_from_path(const char *path, const char *session_name, 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; @@ -2385,6 +2503,7 @@ int load_session_from_path(const char *path, const char *session_name, file_path[path_len++] = '/'; } + ret = 0; /* Search for *.lttng files */ while (!readdir_r(directory, entry, &result) && result) { size_t file_name_len = strlen(result->d_name); @@ -2394,7 +2513,7 @@ int load_session_from_path(const char *path, const char *session_name, continue; } - if (path_len + file_name_len > PATH_MAX) { + if (path_len + file_name_len >= PATH_MAX) { continue; } @@ -2435,17 +2554,52 @@ end: } if (!session_found) { - ret = -LTTNG_ERR_LOAD_SESSION_NOT_FOUND; + ret = -LTTNG_ERR_LOAD_SESSION_NOENT; } return ret; } +/* + * Validate that the given path's credentials and the current process have the + * same UID. If so, return 1 else return 0 if it does NOT match. + */ +static int validate_path_creds(const char *path) +{ + int ret, uid = getuid(); + struct stat buf; + + assert(path); + + if (uid == 0) { + goto valid; + } + + ret = stat(path, &buf); + if (ret < 0) { + if (errno != ENOENT) { + PERROR("stat"); + } + ret = -LTTNG_ERR_INVALID; + goto valid; + } + + if (buf.st_uid != uid) { + goto invalid; + } + +valid: + return 1; +invalid: + return 0; +} + LTTNG_HIDDEN int config_load_session(const char *path, const char *session_name, - int override) + int override, unsigned int autoload) { int ret; + const char *path_ptr = NULL; struct session_config_validation_ctx validation_ctx = { 0 }; ret = init_session_config_validation_ctx(&validation_ctx); @@ -2454,32 +2608,77 @@ int config_load_session(const char *path, const char *session_name, } if (!path) { + char *home_path; + const char *sys_path; + /* Try home path */ - char *home_path = utils_get_home_dir(); + home_path = utils_get_home_dir(); if (home_path) { - char *path; + char path[PATH_MAX]; + + /* + * Try user session configuration path. Ignore error here so we can + * continue loading the system wide sessions. + */ + if (autoload) { + ret = snprintf(path, sizeof(path), + DEFAULT_SESSION_HOME_CONFIGPATH "/" + DEFAULT_SESSION_CONFIG_AUTOLOAD, home_path); + if (ret < 0) { + PERROR("snprintf session autoload home config path"); + goto end; + } - ret = asprintf(&path, DEFAULT_SESSION_HOME_CONFIGPATH, - home_path); - if (ret < 0) { - goto end; + /* + * Credentials are only validated for the autoload in order to + * avoid any user session daemon to try to load kernel sessions + * automatically and failing all the times. + */ + ret = validate_path_creds(path); + if (ret) { + path_ptr = path; + } + } else { + ret = snprintf(path, sizeof(path), + DEFAULT_SESSION_HOME_CONFIGPATH, home_path); + if (ret < 0) { + PERROR("snprintf session home config path"); + goto end; + } + path_ptr = path; } - - ret = load_session_from_path(path, NULL, - &validation_ctx, 0); - if (ret && ret != -LTTNG_ERR_LOAD_SESSION_NOENT) { - free(path); - goto end; + if (path_ptr) { + ret = load_session_from_path(path_ptr, session_name, + &validation_ctx, override); + if (ret && ret != -LTTNG_ERR_LOAD_SESSION_NOENT) { + goto end; + } + /* + * Continue even if the session was found since we have to try + * the system wide sessions. + */ } + } + + /* Reset path pointer for the system wide dir. */ + path_ptr = NULL; - free(path); + /* Try system wide configuration directory. */ + if (autoload) { + sys_path = DEFAULT_SESSION_SYSTEM_CONFIGPATH "/" + DEFAULT_SESSION_CONFIG_AUTOLOAD; + ret = validate_path_creds(sys_path); + if (ret) { + path_ptr = sys_path; + } + } else { + sys_path = DEFAULT_SESSION_SYSTEM_CONFIGPATH; + path_ptr = sys_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; + if (path_ptr) { + ret = load_session_from_path(path_ptr, session_name, + &validation_ctx, override); } } else { ret = access(path, F_OK); @@ -2505,5 +2704,12 @@ int config_load_session(const char *path, const char *session_name, } end: fini_session_config_validation_ctx(&validation_ctx); + if (ret == -LTTNG_ERR_LOAD_SESSION_NOENT && !session_name && !path) { + /* + * Don't report an error if no sessions are found when called + * without a session_name or a search path. + */ + ret = 0; + } return ret; }