From: Simon Marchi Date: Fri, 15 Oct 2021 19:15:51 +0000 (-0400) Subject: common: split ini-config in its own convenience library X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=commitdiff_plain;h=3299fd310c0fab63e912004cdd404d586f936f9e common: split ini-config in its own convenience library The src/common/config contains code related to two kinds of unrelated "config": the ini config, used for configuration files, and the XML session configuration (used for loading/saving sessions, and incidentally MI). Split the ini config in its own convenience library, in src/common/ini-config and keep the rest under src/common/config. Move ini-related things out of config/session-config.{cpp,h} and into ini-config/ini-config.{cpp,h}. Change-Id: Ia0b2b6cdcc15198e20444aa30f1fc86c053176d9 Signed-off-by: Simon Marchi Signed-off-by: Jérémie Galarneau --- diff --git a/configure.ac b/configure.ac index abb61d27f..b2f73dc27 100644 --- a/configure.ac +++ b/configure.ac @@ -1155,6 +1155,7 @@ AC_CONFIG_FILES([ src/common/index/Makefile src/common/health/Makefile src/common/config/Makefile + src/common/ini-config/Makefile src/common/string-utils/Makefile src/common/fd-tracker/Makefile src/common/filter/Makefile diff --git a/src/bin/lttng-relayd/main.cpp b/src/bin/lttng-relayd/main.cpp index 1da83086e..71690f1da 100644 --- a/src/bin/lttng-relayd/main.cpp +++ b/src/bin/lttng-relayd/main.cpp @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/bin/lttng-relayd/tcp_keep_alive.cpp b/src/bin/lttng-relayd/tcp_keep_alive.cpp index 94bf4ab93..c52a55ffb 100644 --- a/src/bin/lttng-relayd/tcp_keep_alive.cpp +++ b/src/bin/lttng-relayd/tcp_keep_alive.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include "tcp_keep_alive.h" diff --git a/src/bin/lttng-sessiond/main.cpp b/src/bin/lttng-sessiond/main.cpp index e5e718891..a676b257f 100644 --- a/src/bin/lttng-sessiond/main.cpp +++ b/src/bin/lttng-sessiond/main.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 48e18f311..a12edcb58 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -7,6 +7,7 @@ SUBDIRS = \ bytecode \ compat \ config \ + ini-config \ fd-tracker \ string-utils \ filter \ @@ -26,6 +27,7 @@ DIST_SUBDIRS = \ testpoint \ index \ config \ + ini-config \ consumer \ string-utils \ fd-tracker \ @@ -118,6 +120,7 @@ endif libcommon_la_LIBADD = \ $(top_builddir)/src/common/bytecode/libbytecode.la \ $(top_builddir)/src/common/config/libconfig.la \ + $(top_builddir)/src/common/ini-config/libini-config.la \ $(top_builddir)/src/common/compat/libcompat.la \ $(top_builddir)/src/common/hashtable/libhashtable.la \ $(top_builddir)/src/common/fd-tracker/libfd-tracker.la \ diff --git a/src/common/config/Makefile.am b/src/common/config/Makefile.am index faa4a3835..a273523cf 100644 --- a/src/common/config/Makefile.am +++ b/src/common/config/Makefile.am @@ -5,8 +5,6 @@ noinst_LTLIBRARIES = libconfig.la libconfig_la_SOURCES = \ config-internal.h \ config-session-abi.h \ - ini.cpp \ - ini.h \ session-config.cpp \ session-config.h libconfig_la_CPPFLAGS = $(libxml2_CFLAGS) $(AM_CPPFLAGS) diff --git a/src/common/config/ini.cpp b/src/common/config/ini.cpp deleted file mode 100644 index 1d967a49d..000000000 --- a/src/common/config/ini.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* - * inih -- simple .INI file parser - * - * The "inih" library is distributed under the New BSD license: - * - * Copyright (C) 2009 Brush Technology - All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Brush Technology nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://code.google.com/p/inih/ - */ - -#include -#include -#include -#include - -#include "ini.h" - -#if !INI_USE_STACK -#include -#endif - -#define MAX_SECTION 50 -#define MAX_NAME 50 - -/* Strip whitespace chars off end of given string, in place. Return s. */ -static char* rstrip(char* s) -{ - char* p = s + strlen(s); - - while (p > s && isspace((unsigned char)(*--p))) - *p = '\0'; - return s; -} - -/* Return pointer to first non-whitespace char in given string. */ -static char* lskip(const char* s) -{ - while (*s && isspace((unsigned char)(*s))) - s++; - return (char*)s; -} - -/* - * Return pointer to first char c or ';' comment in given string, or pointer to - * null at end of string if neither found. ';' must be prefixed by a whitespace - * character to register as a comment. - */ -static char* find_char_or_comment(const char* s, char c) -{ - int was_whitespace = 0; - - while (*s && *s != c && !(was_whitespace && *s == ';')) { - was_whitespace = isspace((unsigned char)(*s)); - s++; - } - return (char*)s; -} - -/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ -static char* strncpy0(char* dest, const char* src, size_t size) -{ - strncpy(dest, src, size - 1); - dest[size - 1] = '\0'; - return dest; -} - -/* See documentation in header file. */ -int ini_parse_file(FILE* file, ini_entry_handler handler, void* user) -{ - /* Uses a fair bit of stack (use heap instead if you need to) */ -#if INI_USE_STACK - char line[INI_MAX_LINE]; -#else - char* line; -#endif - char section[MAX_SECTION] = ""; - char prev_name[MAX_NAME] = ""; - - char* start; - char* end; - char* name; - char* value; - int lineno = 0; - int error = 0; - -#if !INI_USE_STACK - line = (char*)zmalloc(INI_MAX_LINE); - if (!line) { - return -2; - } -#endif - - /* Scan through file line by line */ - while (fgets(line, INI_MAX_LINE, file) != NULL) { - lineno++; - - start = line; -#if INI_ALLOW_BOM - if (lineno == 1 && (unsigned char)start[0] == 0xEF && - (unsigned char)start[1] == 0xBB && - (unsigned char)start[2] == 0xBF) { - start += 3; - } -#endif - start = lskip(rstrip(start)); - - if (*start == ';' || *start == '#') { - /* - * Per Python ConfigParser, allow '#' comments at - * start of line. - */ - } -#if INI_ALLOW_MULTILINE - else if (*prev_name && *start && start > line) { - /* Non-black line with leading whitespace, treat as - * continuation of previous name's value - * (as per Python ConfigParser). - */ - if (handler(user, section, prev_name, start) < 0 && - !error) { - error = lineno; - } - } -#endif - else if (*start == '[') { - /* A "[section]" line */ - end = find_char_or_comment(start + 1, ']'); - if (*end == ']') { - *end = '\0'; - strncpy0(section, start + 1, sizeof(section)); - *prev_name = '\0'; - } - else if (!error) { - /* No ']' found on section line */ - error = lineno; - } - } - else if (*start && *start != ';') { - /* Not a comment, must be a name[=:]value pair */ - end = find_char_or_comment(start, '='); - if (*end != '=') { - end = find_char_or_comment(start, ':'); - } - if (*end == '=' || *end == ':') { - *end = '\0'; - name = rstrip(start); - value = lskip(end + 1); - end = find_char_or_comment(value, '\0'); - if (*end == ';') { - *end = '\0'; - } - - rstrip(value); - - /* - * Valid name[=:]value pair found, call - * handler - */ - strncpy0(prev_name, name, sizeof(prev_name)); - if (handler(user, section, name, value) < 0 && - !error) { - error = lineno; - } - } - else if (!error) { - /* No '=' or ':' found on name[=:]value line */ - error = lineno; - } - } - } - -#if !INI_USE_STACK - free(line); -#endif - - return error; -} - -/* See documentation in header file. */ -int ini_parse(const char* filename, ini_entry_handler handler, void* user) -{ - FILE* file; - int error; - - file = fopen(filename, "r"); - if (!file) { - return -1; - } - - error = ini_parse_file(file, handler, user); - fclose(file); - return error; -} diff --git a/src/common/config/ini.h b/src/common/config/ini.h deleted file mode 100644 index 34b3654ea..000000000 --- a/src/common/config/ini.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * inih -- simple .INI file parser - * - * The "inih" library is distributed under the New BSD license: - * - * Copyright (C) 2009 Brush Technology - All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Brush Technology nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://code.google.com/p/inih/ - */ - -#ifndef __INI_H__ -#define __INI_H__ - -/* Make this header file easier to include in C++ code */ -#include - -typedef int (*ini_entry_handler)(void *, const char *, const char *, - const char *); - -/* - * Parse given INI-style file. May have [section]s, name=value pairs - * (whitespace stripped), and comments starting with ';' (semicolon). Section - * is "" if name=value pair parsed before any section heading. name:value - * pairs are also supported as a concession to Python's ConfigParser. - * - * For each name=value pair parsed, call handler function with given user - * pointer as well as section, name, and value (data only valid for duration - * of handler call). Handler should return zero on success, < 0 on error. - * - * Returns 0 on success, line number of first error on parse error (doesn't - * stop on first error), -1 on file open error, or -2 on memory allocation - * error (only when INI_USE_STACK is zero). - */ -int ini_parse(const char *filename, ini_entry_handler handler, void *user); - -/* - * Same as ini_parse(), but takes a FILE* instead of filename. This doesn't - * close the file when it's finished -- the caller must do that. - */ -int ini_parse_file(FILE *file, ini_entry_handler handler, void *user); - -/* - * Nonzero to allow multi-line value parsing, in the style of Python's - * ConfigParser. If allowed, ini_parse() will call the handler with the same - * name for each subsequent line parsed. - */ -#ifndef INI_ALLOW_MULTILINE -#define INI_ALLOW_MULTILINE 1 -#endif - -/* - * Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of - * the file. See http://code.google.com/p/inih/issues/detail?id=21 - */ -#ifndef INI_ALLOW_BOM -#define INI_ALLOW_BOM 1 -#endif - -/* Nonzero to use stack, zero to use heap (malloc/free). */ -#ifndef INI_USE_STACK -#define INI_USE_STACK 1 -#endif - -/* Maximum line length for any line in INI file. */ -#ifndef INI_MAX_LINE -#define INI_MAX_LINE 200 -#endif - -#endif /* __INI_H__ */ diff --git a/src/common/config/session-config.cpp b/src/common/config/session-config.cpp index 4d57d581a..19d587c61 100644 --- a/src/common/config/session-config.cpp +++ b/src/common/config/session-config.cpp @@ -39,12 +39,6 @@ #define CONFIG_USERSPACE_PROBE_LOOKUP_METHOD_NAME_MAX_LEN 7 -struct handler_filter_args { - const char* section; - config_entry_handler_cb handler; - void *user_data; -}; - struct session_config_validation_ctx { xmlSchemaParserCtxtPtr parser_ctx; xmlSchemaPtr schema; @@ -52,12 +46,6 @@ struct session_config_validation_ctx { }; const char * const config_element_all = "all"; -LTTNG_EXPORT const char *config_str_yes = "yes"; -LTTNG_EXPORT const char *config_str_true = "true"; -LTTNG_EXPORT const char *config_str_on = "on"; -LTTNG_EXPORT const char *config_str_no = "no"; -LTTNG_EXPORT const char *config_str_false = "false"; -LTTNG_EXPORT const char *config_str_off = "off"; LTTNG_EXPORT const char *config_xml_encoding = "UTF-8"; LTTNG_EXPORT size_t config_xml_encoding_bytes_per_char = 2; /* Size of the encoding's largest character */ LTTNG_EXPORT const char *config_xml_indent_string = "\t"; @@ -252,151 +240,6 @@ struct consumer_output { char *data_uri; }; -static int config_entry_handler_filter(struct handler_filter_args *args, - const char *section, const char *name, const char *value) -{ - int ret = 0; - struct config_entry entry = { section, name, value }; - - LTTNG_ASSERT(args); - - if (!section || !name || !value) { - ret = -EIO; - goto end; - } - - if (args->section) { - if (strcmp(args->section, section)) { - goto end; - } - } - - ret = args->handler(&entry, args->user_data); -end: - return ret; -} - -int config_get_section_entries(const char *override_path, const char *section, - config_entry_handler_cb handler, void *user_data) -{ - int ret = 0; - const 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("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 error; - } - } - - /* Everything went well. */ - ret = 0; - -error: - return ret; -} - -int config_parse_value(const char *value) -{ - int i, ret = 0; - char *endptr, *lower_str; - size_t len; - unsigned long v; - - len = strlen(value); - if (!len) { - ret = -1; - goto end; - } - - v = strtoul(value, &endptr, 10); - if (endptr != value) { - ret = v; - goto end; - } - - lower_str = (char *) zmalloc(len + 1); - if (!lower_str) { - PERROR("zmalloc"); - ret = -errno; - goto end; - } - - for (i = 0; i < len; i++) { - lower_str[i] = tolower(value[i]); - } - - if (!strcmp(lower_str, config_str_yes) || - !strcmp(lower_str, config_str_true) || - !strcmp(lower_str, config_str_on)) { - ret = 1; - } else if (!strcmp(lower_str, config_str_no) || - !strcmp(lower_str, config_str_false) || - !strcmp(lower_str, config_str_off)) { - ret = 0; - } else { - ret = -1; - } - - free(lower_str); -end: - return ret; -} - /* * Returns a xmlChar string which must be released using xmlFree(). */ diff --git a/src/common/config/session-config.h b/src/common/config/session-config.h index a6955b0b9..b97c5ad41 100644 --- a/src/common/config/session-config.h +++ b/src/common/config/session-config.h @@ -8,18 +8,10 @@ #ifndef _CONFIG_H #define _CONFIG_H -#include #include #include #include -struct config_entry { - /* section is NULL if the entry is not in a section */ - const char *section; - const char *name; - const char *value; -}; - struct config_load_session_override_attr { char *path_url; char *ctrl_url; @@ -30,46 +22,6 @@ struct config_load_session_override_attr { /* Instance of a configuration writer. */ struct config_writer; -/* - * A config_entry_handler_cb receives config_entry structures belonging to the - * sections the handler has been registered to. - * - * The config_entry and its members are only valid for the duration of the call - * and must not be freed. - * - * config_entry_handler_cb may return negative value to indicate an error in - * the configuration file. - */ -typedef int (*config_entry_handler_cb)(const struct config_entry *, void *); - -/* - * Read a section's entries in an INI configuration file. - * - * path may be NULL, in which case the following paths will be tried: - * 1) $HOME/.lttng/lttng.conf - * 2) /etc/lttng/lttng.conf - * - * handler will only be called with entries belonging to the provided section. - * If section is NULL, all entries will be relayed to handler. If section is - * "", only the global entries are relayed. - * - * Returns 0 on success. Negative values are error codes. If the return value - * is positive, it represents the line number on which a parsing error occurred. - */ -int config_get_section_entries(const char *path, const char *section, - config_entry_handler_cb handler, void *user_data); - -/* - * Parse a configuration value. - * - * This function expects either an unsigned integer or a boolean text option. - * The following strings are recognized: true, yes, on, false, no and off. - * - * Returns either the value of the parsed integer, or 0/1 if a boolean text - * string was recognized. Negative values indicate an error. - */ -int config_parse_value(const char *value); - /* * Create an instance of a configuration writer. * diff --git a/src/common/ini-config/Makefile.am b/src/common/ini-config/Makefile.am new file mode 100644 index 000000000..7f0ee8dfe --- /dev/null +++ b/src/common/ini-config/Makefile.am @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only + +noinst_LTLIBRARIES = libini-config.la + +libini_config_la_SOURCES = \ + ini.cpp \ + ini.h \ + ini-config.cpp \ + ini-config.h diff --git a/src/common/ini-config/ini-config.cpp b/src/common/ini-config/ini-config.cpp new file mode 100644 index 000000000..797de0164 --- /dev/null +++ b/src/common/ini-config/ini-config.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "ini-config.h" + +#include +#include +#include +#include +#include +#include + +LTTNG_EXPORT const char *config_str_yes = "yes"; +LTTNG_EXPORT const char *config_str_true = "true"; +LTTNG_EXPORT const char *config_str_on = "on"; +LTTNG_EXPORT const char *config_str_no = "no"; +LTTNG_EXPORT const char *config_str_false = "false"; +LTTNG_EXPORT const char *config_str_off = "off"; + +struct handler_filter_args { + const char* section; + config_entry_handler_cb handler; + void *user_data; +}; + +static int config_entry_handler_filter(struct handler_filter_args *args, + const char *section, const char *name, const char *value) +{ + int ret = 0; + struct config_entry entry = { section, name, value }; + + LTTNG_ASSERT(args); + + if (!section || !name || !value) { + ret = -EIO; + goto end; + } + + if (args->section) { + if (strcmp(args->section, section)) { + goto end; + } + } + + ret = args->handler(&entry, args->user_data); +end: + return ret; +} + +int config_get_section_entries(const char *override_path, const char *section, + config_entry_handler_cb handler, void *user_data) +{ + int ret = 0; + const 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("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 error; + } + } + + /* Everything went well. */ + ret = 0; + +error: + return ret; +} + +int config_parse_value(const char *value) +{ + int i, ret = 0; + char *endptr, *lower_str; + size_t len; + unsigned long v; + + len = strlen(value); + if (!len) { + ret = -1; + goto end; + } + + v = strtoul(value, &endptr, 10); + if (endptr != value) { + ret = v; + goto end; + } + + lower_str = (char *) zmalloc(len + 1); + if (!lower_str) { + PERROR("zmalloc"); + ret = -errno; + goto end; + } + + for (i = 0; i < len; i++) { + lower_str[i] = tolower(value[i]); + } + + if (!strcmp(lower_str, config_str_yes) || + !strcmp(lower_str, config_str_true) || + !strcmp(lower_str, config_str_on)) { + ret = 1; + } else if (!strcmp(lower_str, config_str_no) || + !strcmp(lower_str, config_str_false) || + !strcmp(lower_str, config_str_off)) { + ret = 0; + } else { + ret = -1; + } + + free(lower_str); +end: + return ret; +} diff --git a/src/common/ini-config/ini-config.h b/src/common/ini-config/ini-config.h new file mode 100644 index 000000000..3e4160596 --- /dev/null +++ b/src/common/ini-config/ini-config.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#ifndef INI_CONFIG_H +#define INI_CONFIG_H + +struct config_entry { + /* section is NULL if the entry is not in a section */ + const char *section; + const char *name; + const char *value; +}; + +/* + * A config_entry_handler_cb receives config_entry structures belonging to the + * sections the handler has been registered to. + * + * The config_entry and its members are only valid for the duration of the call + * and must not be freed. + * + * config_entry_handler_cb may return negative value to indicate an error in + * the configuration file. + */ +typedef int (*config_entry_handler_cb)(const struct config_entry *, void *); + +/* + * Read a section's entries in an INI configuration file. + * + * path may be NULL, in which case the following paths will be tried: + * 1) $HOME/.lttng/lttng.conf + * 2) /etc/lttng/lttng.conf + * + * handler will only be called with entries belonging to the provided section. + * If section is NULL, all entries will be relayed to handler. If section is + * "", only the global entries are relayed. + * + * Returns 0 on success. Negative values are error codes. If the return value + * is positive, it represents the line number on which a parsing error occurred. + */ +int config_get_section_entries(const char *path, const char *section, + config_entry_handler_cb handler, void *user_data); + +/* + * Parse a configuration value. + * + * This function expects either an unsigned integer or a boolean text option. + * The following strings are recognized: true, yes, on, false, no and off. + * + * Returns either the value of the parsed integer, or 0/1 if a boolean text + * string was recognized. Negative values indicate an error. + */ +int config_parse_value(const char *value); + +#endif /* INI_CONFIG_H */ diff --git a/src/common/ini-config/ini.cpp b/src/common/ini-config/ini.cpp new file mode 100644 index 000000000..1d967a49d --- /dev/null +++ b/src/common/ini-config/ini.cpp @@ -0,0 +1,218 @@ +/* + * inih -- simple .INI file parser + * + * The "inih" library is distributed under the New BSD license: + * + * Copyright (C) 2009 Brush Technology - All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Brush Technology nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://code.google.com/p/inih/ + */ + +#include +#include +#include +#include + +#include "ini.h" + +#if !INI_USE_STACK +#include +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* + * Return pointer to first char c or ';' comment in given string, or pointer to + * null at end of string if neither found. ';' must be prefixed by a whitespace + * character to register as a comment. + */ +static char* find_char_or_comment(const char* s, char c) +{ + int was_whitespace = 0; + + while (*s && *s != c && !(was_whitespace && *s == ';')) { + was_whitespace = isspace((unsigned char)(*s)); + s++; + } + return (char*)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy(dest, src, size - 1); + dest[size - 1] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, ini_entry_handler handler, void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; +#else + char* line; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)zmalloc(INI_MAX_LINE); + if (!line) { + return -2; + } +#endif + + /* Scan through file line by line */ + while (fgets(line, INI_MAX_LINE, file) != NULL) { + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (*start == ';' || *start == '#') { + /* + * Per Python ConfigParser, allow '#' comments at + * start of line. + */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-black line with leading whitespace, treat as + * continuation of previous name's value + * (as per Python ConfigParser). + */ + if (handler(user, section, prev_name, start) < 0 && + !error) { + error = lineno; + } + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_char_or_comment(start + 1, ']'); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start && *start != ';') { + /* Not a comment, must be a name[=:]value pair */ + end = find_char_or_comment(start, '='); + if (*end != '=') { + end = find_char_or_comment(start, ':'); + } + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); + end = find_char_or_comment(value, '\0'); + if (*end == ';') { + *end = '\0'; + } + + rstrip(value); + + /* + * Valid name[=:]value pair found, call + * handler + */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (handler(user, section, name, value) < 0 && + !error) { + error = lineno; + } + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + } + +#if !INI_USE_STACK + free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, ini_entry_handler handler, void* user) +{ + FILE* file; + int error; + + file = fopen(filename, "r"); + if (!file) { + return -1; + } + + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} diff --git a/src/common/ini-config/ini.h b/src/common/ini-config/ini.h new file mode 100644 index 000000000..34b3654ea --- /dev/null +++ b/src/common/ini-config/ini.h @@ -0,0 +1,94 @@ +/* + * inih -- simple .INI file parser + * + * The "inih" library is distributed under the New BSD license: + * + * Copyright (C) 2009 Brush Technology - All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Brush Technology nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://code.google.com/p/inih/ + */ + +#ifndef __INI_H__ +#define __INI_H__ + +/* Make this header file easier to include in C++ code */ +#include + +typedef int (*ini_entry_handler)(void *, const char *, const char *, + const char *); + +/* + * Parse given INI-style file. May have [section]s, name=value pairs + * (whitespace stripped), and comments starting with ';' (semicolon). Section + * is "" if name=value pair parsed before any section heading. name:value + * pairs are also supported as a concession to Python's ConfigParser. + * + * For each name=value pair parsed, call handler function with given user + * pointer as well as section, name, and value (data only valid for duration + * of handler call). Handler should return zero on success, < 0 on error. + * + * Returns 0 on success, line number of first error on parse error (doesn't + * stop on first error), -1 on file open error, or -2 on memory allocation + * error (only when INI_USE_STACK is zero). + */ +int ini_parse(const char *filename, ini_entry_handler handler, void *user); + +/* + * Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + * close the file when it's finished -- the caller must do that. + */ +int ini_parse_file(FILE *file, ini_entry_handler handler, void *user); + +/* + * Nonzero to allow multi-line value parsing, in the style of Python's + * ConfigParser. If allowed, ini_parse() will call the handler with the same + * name for each subsequent line parsed. + */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* + * Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + * the file. See http://code.google.com/p/inih/issues/detail?id=21 + */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Nonzero to use stack, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file. */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +#endif /* __INI_H__ */ diff --git a/tests/unit/ini_config/Makefile.am b/tests/unit/ini_config/Makefile.am index 003969775..f7bf28770 100644 --- a/tests/unit/ini_config/Makefile.am +++ b/tests/unit/ini_config/Makefile.am @@ -3,7 +3,6 @@ AM_CPPFLAGS += -I$(top_srcdir)/tests/utils/ LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la -LIBCONFIG=$(top_builddir)/src/common/config/libconfig.la LIBCOMMON=$(top_builddir)/src/common/libcommon.la LIBHASHTABLE=$(top_builddir)/src/common/hashtable/libhashtable.la @@ -11,8 +10,7 @@ noinst_PROGRAMS = ini_config EXTRA_DIST = test_ini_config sample.ini ini_config_SOURCES = ini_config.cpp -ini_config_LDADD = $(LIBTAP) $(LIBCONFIG) $(LIBCOMMON) $(LIBHASHTABLE) \ - $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la +ini_config_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBHASHTABLE) all-local: @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ diff --git a/tests/unit/ini_config/ini_config.cpp b/tests/unit/ini_config/ini_config.cpp index 6775ddd3f..c2a753ede 100644 --- a/tests/unit/ini_config/ini_config.cpp +++ b/tests/unit/ini_config/ini_config.cpp @@ -6,7 +6,7 @@ */ #include -#include +#include #include #include #include