diff options
-rw-r--r-- | common/ini/Makefile.am | 7 | ||||
-rw-r--r-- | common/ini/configure.ac | 2 | ||||
-rw-r--r-- | common/ini/ini_config.c | 534 | ||||
-rw-r--r-- | common/ini/ini_config.h | 390 | ||||
-rw-r--r-- | common/ini/ini_config_ut.c | 113 | ||||
-rw-r--r-- | common/ini/ini_metadata.c | 104 | ||||
-rw-r--r-- | common/ini/ini_metadata.h | 42 |
7 files changed, 915 insertions, 277 deletions
diff --git a/common/ini/Makefile.am b/common/ini/Makefile.am index af81a9f89..76205262d 100644 --- a/common/ini/Makefile.am +++ b/common/ini/Makefile.am @@ -33,12 +33,15 @@ dist_include_HEADERS = \ # Build library lib_LTLIBRARIES = libini_config.la libini_config_la_SOURCES = \ - ini_config.c + ini_config.c \ + ini_metadata.c \ + ini_metadata.h + libini_config_la_LIBADD = \ -L$(topbuilddir)/collection \ -lcollection libini_config_la_LDFLAGS = \ - -version-info 1:0:0 + -version-info 2:0:0 # Build unit test check_PROGRAMS = ini_config_ut diff --git a/common/ini/configure.ac b/common/ini/configure.ac index a8c751fd8..aa2033c1e 100644 --- a/common/ini/configure.ac +++ b/common/ini/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([ini_config],[0.4.0],[sssd-devel@lists.fedorahosted.org]) +AC_INIT([ini_config],[0.5.0],[sssd-devel@lists.fedorahosted.org]) AC_CONFIG_SRCDIR([ini_config.c]) AC_CONFIG_AUX_DIR([build]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) diff --git a/common/ini/ini_config.c b/common/ini/ini_config.c index e87419a30..9015590f5 100644 --- a/common/ini/ini_config.c +++ b/common/ini/ini_config.c @@ -28,6 +28,7 @@ #include <stdlib.h> #include <locale.h> #include <fcntl.h> +#include <unistd.h> #include "config.h" /* For error text */ #include <libintl.h> @@ -37,6 +38,7 @@ #include "collection_tools.h" #include "trace.h" #include "ini_config.h" +#include "ini_metadata.h" #define NAME_OVERHEAD 10 @@ -79,20 +81,40 @@ #define INI_ERROR "errors" #define INI_ERROR_NAME "errname" - /* Internal sizes. MAX_KEY is defined in config.h */ #define MAX_VALUE PATH_MAX #define BUFFER_SIZE MAX_KEY + MAX_VALUE + 3 -/* Internally used functions */ -static int config_with_lines(const char *application, - FILE *config_file, - const char *config_source, - struct collection_item **ini_config, - int error_level, - struct collection_item **error_list, - struct collection_item **lines); +/*============================================================*/ +/* The following classes moved here from the public header + * They are reserved for future use. + * + * NOTE: before exposing these constants again in the common header + * check that the class IDs did not get reused over time by + * other classes. + */ +/** @brief Collection of grammar errors. + * + * Reserved for future use. + */ +#define COL_CLASS_INI_GERROR COL_CLASS_INI_BASE + 5 +/** @brief Collection of validation errors. + * + * Reserved for future use. + */ +#define COL_CLASS_INI_VERROR COL_CLASS_INI_BASE + 6 + +#ifdef HAVE_VALIDATION + +/** @brief Collection of lines from the INI file. + * + * Reserved for future use + */ +#define COL_CLASS_INI_LINES COL_CLASS_INI_BASE + 7 + +#endif /* HAVE_VALIDATION */ +/*============================================================*/ /* Different error string functions can be passed as callbacks */ @@ -122,7 +144,23 @@ const char *parsing_error_str(int parsing_error) * This function is currently not used. * It is planned to be used by the INI * file grammar parser. + * + * The following doxygen description is moved here. + * When the function gets exposed move it into + * the header file. */ +/** @brief Function to return a grammar error in template. + * + * EXPERIMENTAL. Reserved for future use. + * + * This error is returned when the template + * is translated into the grammar object. + * + * @param[in] parsing_error Error code for the grammar error. + * + * @return Error string. + */ + const char *grammar_error_str(int grammar_error) { const char *placeholder= _("Unknown grammar error."); @@ -147,6 +185,22 @@ const char *grammar_error_str(int grammar_error) * This function is currently not used. * It is planned to be used by the INI * file grammar validator. + * + * The following doxygen description is moved here. + * When the function gets exposed move it into + * the header file. + */ +/** @brief Function to return a validation error. + * + * EXPERIMENTAL. Reserved for future use. + * + * This is the error that it is returned when + * the INI file is validated against the + * grammar object. + * + * @param[in] parsing_error Error code for the validation error. + * + * @return Error string. */ const char *validation_error_str(int validation_error) { @@ -186,7 +240,7 @@ static int ini_to_collection(FILE *file, struct collection_item *ini_config, int error_level, struct collection_item **error_list, - struct collection_item **lines) + struct collection_item *lines) { int error; int status; @@ -204,25 +258,18 @@ static int ini_to_collection(FILE *file, TRACE_FLOW_STRING("ini_to_collection", "Entry"); - if (file == NULL) { - TRACE_ERROR_NUMBER("No file handle", EINVAL); - return EINVAL; - } - /* Open the collection of errors */ if (error_list != NULL) { *error_list = NULL; error = col_create_collection(error_list, INI_ERROR, COL_CLASS_INI_PERROR); if (error) { TRACE_ERROR_NUMBER("Failed to create error collection", error); - fclose(file); return error; } /* Add file name as the first item */ error = col_add_str_property(*error_list, NULL, INI_ERROR_NAME, config_filename, 0); if (error) { TRACE_ERROR_NUMBER("Failed to and name to collection", error); - fclose(file); col_destroy_collection(*error_list); return error; } @@ -239,16 +286,18 @@ static int ini_to_collection(FILE *file, switch (status) { case RET_PAIR: + +#ifdef HAVE_VALIDATION + /* Add line to the collection of lines. * It is pretty safe in this case to just type cast the value to * int32_t since it is unrealistic that ini file will ever have * so many lines. */ if (lines) { - error = col_add_int_property(*lines, NULL, key, (int32_t)line); + error = col_add_int_property(lines, NULL, key, (int32_t)line); if (error) { TRACE_ERROR_NUMBER("Failed to add line to line collection", error); - fclose(file); col_destroy_collection(current_section); if (created) { col_destroy_collection(*error_list); @@ -258,6 +307,8 @@ static int ini_to_collection(FILE *file, } } +#endif /* HAVE_VALIDATION */ + /* Do we have a section at the top of the file ? */ if (section_count == 0) { /* Check if collection already exists */ @@ -273,7 +324,6 @@ static int ini_to_collection(FILE *file, current_section, COL_ADD_MODE_REFERENCE))) { TRACE_ERROR_NUMBER("Failed to create collection", error); - fclose(file); col_destroy_collection(current_section); if (created) { col_destroy_collection(*error_list); @@ -297,7 +347,6 @@ static int ini_to_collection(FILE *file, length); if (error != EOK) { TRACE_ERROR_NUMBER("Failed to add pair to collection", error); - fclose(file); col_destroy_collection(current_section); if (created) { col_destroy_collection(*error_list); @@ -308,6 +357,9 @@ static int ini_to_collection(FILE *file, break; case RET_SECTION: + +#ifdef HAVE_VALIDATION + /* Add line to the collection of lines */ if (lines) { /* For easier search make line numbers for the sections negative. @@ -316,10 +368,9 @@ static int ini_to_collection(FILE *file, * int32_t since it is unrealistic that ini file will ever have * so many lines. */ - error = col_add_int_property(*lines, NULL, key, (int32_t)(-1 * line)); + error = col_add_int_property(lines, NULL, key, (int32_t)(-1 * line)); if (error) { TRACE_ERROR_NUMBER("Failed to add line to line collection", error); - fclose(file); col_destroy_collection(current_section); if (created) { col_destroy_collection(*error_list); @@ -329,6 +380,8 @@ static int ini_to_collection(FILE *file, } } +#endif /* HAVE_VALIDATION */ + /* Read a new section */ col_destroy_collection(current_section); current_section = NULL; @@ -343,7 +396,6 @@ static int ini_to_collection(FILE *file, current_section, COL_ADD_MODE_REFERENCE))) { TRACE_ERROR_NUMBER("Failed to add collection", error); - fclose(file); col_destroy_collection(current_section); if (created) { col_destroy_collection(*error_list); @@ -370,7 +422,6 @@ static int ini_to_collection(FILE *file, ERROR_TXT, &pe, sizeof(pe)); if (error) { TRACE_ERROR_NUMBER("Failed to add error to collection", error); - fclose(file); col_destroy_collection(current_section); if (created) { col_destroy_collection(*error_list); @@ -382,7 +433,6 @@ static int ini_to_collection(FILE *file, if (error_level != INI_STOP_ON_NONE) { TRACE_ERROR_STRING("Invalid format of the file", ""); col_destroy_collection(current_section); - fclose(file); return EIO; } break; @@ -395,7 +445,6 @@ static int ini_to_collection(FILE *file, WARNING_TXT, &pe, sizeof(pe)); if (error) { TRACE_ERROR_NUMBER("Failed to add warning to collection", error); - fclose(file); col_destroy_collection(current_section); if (created) { col_destroy_collection(*error_list); @@ -407,7 +456,6 @@ static int ini_to_collection(FILE *file, if (error_level == INI_STOP_ON_ANY) { TRACE_ERROR_STRING("Invalid format of the file", ""); if (created) col_destroy_collection(current_section); - fclose(file); return EIO; } TRACE_ERROR_STRING("Invalid string", ""); @@ -416,8 +464,11 @@ static int ini_to_collection(FILE *file, ext_err = -1; } - /* Close file */ - fclose(file); + /* Note: File is not closed on this level any more. + * It opened on the level above, checked and closed there. + * It is not the responsibility of this function to close + * file any more. + */ COL_DEBUG_COLLECTION(ini_config); @@ -447,7 +498,23 @@ void free_ini_config_errors(struct collection_item *error_set) TRACE_FLOW_STRING("free_ini_config_errors", "Exit"); } -/* Function to free configuration lines list */ +#ifdef HAVE_VALIDATION + +/* Function to free configuration lines list. + * + * The following doxygen description is moved here. + * When the function gets exposed move it into + * the header file. + */ +/** + * @brief Function to free lines object. + * + * EXPERIMENTAL. Reserved for future use. + * + * @param[in] lines Lines object. + * + */ + void free_ini_config_lines(struct collection_item *lines) { TRACE_FLOW_STRING("free_ini_config_lines", "Entry"); @@ -455,6 +522,8 @@ void free_ini_config_lines(struct collection_item *lines) TRACE_FLOW_STRING("free_ini_config_lines", "Exit"); } +#endif /* HAVE_VALIDATION */ + /* Read configuration information from a file */ int config_from_file(const char *application, @@ -466,12 +535,13 @@ int config_from_file(const char *application, int error; TRACE_FLOW_STRING("config_from_file", "Entry"); - error = config_from_file_with_lines(application, - config_filename, - ini_config, - error_level, - error_list, - NULL); + error = config_from_file_with_metadata(application, + config_filename, + ini_config, + error_level, + error_list, + 0, + NULL); TRACE_FLOW_NUMBER("config_from_file. Returns", error); return error; } @@ -487,97 +557,43 @@ int config_from_fd(const char *application, int error; TRACE_FLOW_STRING("config_from_fd", "Entry"); - error = config_from_fd_with_lines(application, fd, config_source, - ini_config, error_level, - error_list, NULL); + error = config_from_fd_with_metadata(application, + fd, + config_source, + ini_config, + error_level, + error_list, + 0, + NULL); TRACE_FLOW_NUMBER("config_from_fd. Returns", error); return error; } -/* Function to read the ini file and have a collection - * of which item appers on which line - */ -int config_from_file_with_lines(const char *application, - const char *config_filename, - struct collection_item **ini_config, - int error_level, - struct collection_item **error_list, - struct collection_item **lines) -{ - int error = EOK; - FILE *config_file = NULL; - TRACE_FLOW_STRING("config_from_file_with_lines", "Entry"); - - config_file = fopen(config_filename, "r"); - if(!config_file) { - error = errno; - TRACE_ERROR_NUMBER("Failed to open file", error); - return error; - } - - error = config_with_lines(application, config_file, - config_filename, ini_config, - error_level, error_list, - lines); - TRACE_FLOW_NUMBER("config_from_file_with_lines. Returns", error); - return error; -} - -/* Function to read the ini file and have a collection - * of which item appers on which line - */ -int config_from_fd_with_lines(const char *application, - int fd, - const char *config_source, - struct collection_item **ini_config, - int error_level, - struct collection_item **error_list, - struct collection_item **lines) -{ - int error = EOK; - FILE *config_file; - - TRACE_FLOW_STRING("config_from_fd_with_lines", "Entry"); - - config_file = fdopen(fd, "r"); - if (!config_file) { - error = errno; - TRACE_ERROR_NUMBER("Failed to dup file", error); - return error; - } - - error = config_with_lines(application, config_file, - config_source, ini_config, - error_level, error_list, - lines); - TRACE_FLOW_NUMBER("config_from_fd_with_lines. Returns", error); - - return error; -} /* Low level function that prepares the collection * and calls parser. */ -static int config_with_lines(const char *application, - FILE *config_file, - const char *config_source, - struct collection_item **ini_config, - int error_level, - struct collection_item **error_list, - struct collection_item **lines) +static int config_with_metadata(const char *application, + FILE *config_file, + const char *config_source, + struct collection_item **ini_config, + int error_level, + struct collection_item **error_list, + uint32_t metaflags, + struct collection_item *metadata) { int error; int created = 0; + struct collection_item *lines = NULL; + +#ifdef HAVE_VALIDATION int created_lines = 0; +#endif TRACE_FLOW_STRING("config_from_file", "Entry"); - if ((ini_config == NULL) || - (application == NULL)) { - TRACE_ERROR_NUMBER("Invalid argument", EINVAL); - return EINVAL; - } + /* Now we check arguments in the calling functions. */ /* Create collection if needed */ if (*ini_config == NULL) { @@ -591,40 +607,32 @@ static int config_with_lines(const char *application, created = 1; } /* Is the collection of the right class? */ - else if (col_is_of_class(*ini_config, COL_CLASS_INI_CONFIG)) { + else if (((col_is_of_class(*ini_config, COL_CLASS_INI_CONFIG))== 0) && + ((col_is_of_class(*ini_config, COL_CLASS_INI_META))== 0)) { TRACE_ERROR_NUMBER("Wrong collection type", EINVAL); return EINVAL; } - - /* Create collection if needed */ - if (lines) { - - /* Make sure that the lines collection is empty */ - if (*lines) { - TRACE_ERROR_NUMBER("Collection of lines is not empty", EINVAL); - if (created) { - col_destroy_collection(*ini_config); - *ini_config = NULL; - } - return EINVAL; - } - - error = col_create_collection(lines, - application, - COL_CLASS_INI_LINES); - if (error != EOK) { - TRACE_ERROR_NUMBER("Failed to create collection", error); - if (created) { - col_destroy_collection(*ini_config); - *ini_config = NULL; - } - return error; +#ifdef HAVE_VALIDATION + /* This code is preserved for future use */ + error = col_create_collection(lines, + application, + COL_CLASS_INI_LINES); + if (error != EOK) { + TRACE_ERROR_NUMBER("Failed to create collection", error); + if (created) { + col_destroy_collection(*ini_config); + *ini_config = NULL; } - created_lines = 1; + return error; } + created_lines = 1; +#else + /* Until we implement validation do not read the lines. */ + lines = NULL; +#endif /* HAVE_VALIDATION */ - /* Do the actual work */ + /* Do the actual work - for now do not read lines.*/ error = ini_to_collection(config_file, config_source, *ini_config, error_level, error_list, lines); @@ -633,57 +641,146 @@ static int config_with_lines(const char *application, col_destroy_collection(*ini_config); *ini_config = NULL; } - /* Also create collection of lines if we created it */ - if (error && created_lines) { - col_destroy_collection(*lines); - *lines = NULL; - } + + /* FIXME - put lines collection into the metadata */ TRACE_FLOW_NUMBER("config_from_file. Returns", error); return error; } -/* Special wrapper around the inernal parser - * to open the file first. - * Used in conf_for_app function. +/* Function to read the ini file from fd + * with meta data. */ -static int ini_to_col_from_file(const char *config_filename, - struct collection_item *ini_config, - int error_level, - struct collection_item **error_list, - struct collection_item **lines) +int config_from_fd_with_metadata(const char *application, + int ext_fd, + const char *config_filename, + struct collection_item **ini_config, + int error_level, + struct collection_item **error_list, + uint32_t metaflags, + struct collection_item **metadata) { int error = EOK; + int file_error = EOK; + int save_error = 0; + int fd = -1; FILE *config_file = NULL; - TRACE_FLOW_STRING("ini_to_col_from_file", "Entry"); + TRACE_FLOW_STRING("config_from_fd_with_metadata", "Entry"); + + /* We need to check arguments before we can move on, + * and start allocating memory. + */ + if ((ini_config == NULL) || + (application == NULL)) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + } + + /* Prepare meta data */ + error = prepare_metadata(metaflags, metadata, &save_error); + if (error) { + TRACE_ERROR_NUMBER("Failed to prepare metadata", error); + return error; + } + + errno = 0; + + if (ext_fd == -1) { + /* No file descriptor so use name */ + config_file = fopen(config_filename, "r"); + } + else { + /* Create a copy of the descriptor so that we can close it if needed */ + fd = dup(ext_fd); + if (fd != -1) config_file = fdopen(fd, "r"); + } + file_error = errno; + + if (save_error) { + /* Record the result of the open file operation in metadata */ + error = col_add_int_property(*metadata, + INI_META_SEC_ERROR, + INI_META_KEY_READ_ERROR, + file_error); + if (error) { + /* Something is really wrong if we failed here */ + TRACE_ERROR_NUMBER("Failed to save file open error", error); + if (config_file) fclose(config_file); + return error; + } + } - config_file = fopen(config_filename, "r"); if(!config_file) { - error = errno; - TRACE_ERROR_NUMBER("ini_to_col_from_file. Returns", error); - return ENOENT; + TRACE_ERROR_NUMBER("Failed to open file", file_error); + return file_error; } - error = ini_to_collection(config_file, - config_filename, - ini_config, - error_level, - error_list, - lines); - TRACE_FLOW_NUMBER("ini_to_col_from_file. Returns", error); + /* Collect meta data before actually parsing the file */ + error = collect_metadata(metaflags, metadata, config_file); + if(error) { + TRACE_ERROR_NUMBER("Failed to collect metadata", error); + return error; + } + + if (!(metaflags & INI_META_ACTION_NOPARSE)) { + /* Parse data if needed */ + error = config_with_metadata(application, + config_file, + config_filename, + ini_config, + error_level, + error_list, + metaflags, + *metadata); + } + + /* We opened the file we close it */ + fclose(config_file); + + TRACE_FLOW_NUMBER("config_from_fd_with_metadata. Returns", error); + return error; +} + +/* Function to read the ini file with metadata + * using file name. + */ +int config_from_file_with_metadata(const char *application, + const char *config_filename, + struct collection_item **ini_config, + int error_level, + struct collection_item **error_list, + uint32_t metaflags, + struct collection_item **metadata) +{ + int error = EOK; + TRACE_FLOW_STRING("config_from_file_with_metadata", "Entry"); + + error = config_from_fd_with_metadata(application, + -1, + config_filename, + ini_config, + error_level, + error_list, + metaflags, + metadata); + + TRACE_FLOW_STRING("config_from_file_with_metadata", "Exit"); return error; } /* Read default config file and then overwrite it with a specific one * from the directory */ -int config_for_app(const char *application, - const char *config_file, - const char *config_dir, - struct collection_item **ini_config, - int error_level, - struct collection_item **error_set) +int config_for_app_with_metadata(const char *application, + const char *config_file, + const char *config_dir, + struct collection_item **ini_config, + int error_level, + struct collection_item **error_set, + uint32_t metaflags, + struct collection_item **meta_default, + struct collection_item **meta_appini) { int error = EOK; char *file_name; @@ -695,7 +792,7 @@ int config_for_app(const char *application, int tried = 0; int noents = 0; - TRACE_FLOW_STRING("config_to_collection", "Entry"); + TRACE_FLOW_STRING("config_for_app", "Entry"); if (ini_config == NULL) { TRACE_ERROR_NUMBER("Invalid parameter", EINVAL); @@ -745,7 +842,8 @@ int config_for_app(const char *application, created = 1; } /* Is the collection of the right class? */ - else if (col_is_of_class(*ini_config, COL_CLASS_INI_CONFIG)) { + else if ((col_is_of_class(*ini_config, COL_CLASS_INI_CONFIG) == 0) && + (col_is_of_class(*ini_config, COL_CLASS_INI_META) == 0)) { TRACE_ERROR_NUMBER("Wrong collection type", EINVAL); return EINVAL; } @@ -753,8 +851,14 @@ int config_for_app(const char *application, /* Read master file */ if (config_file != NULL) { TRACE_INFO_STRING("Reading master file:", config_file); - error = ini_to_col_from_file(config_file, *ini_config, - error_level, pass_common, NULL); + /* Get configuration information from the file */ + error = config_from_file_with_metadata(application, + config_file, + ini_config, + error_level, + pass_common, + metaflags, + meta_default); tried++; /* ENOENT and EOK are Ok */ if (error) { @@ -812,8 +916,13 @@ int config_for_app(const char *application, sprintf(file_name, "%s%s%s.conf", config_dir, SLASH, application); TRACE_INFO_STRING("Opening file:", file_name); /* Read specific file */ - error = ini_to_col_from_file(file_name, *ini_config, - error_level, pass_specific, NULL); + error = config_from_file_with_metadata(application, + file_name, + ini_config, + error_level, + pass_specific, + metaflags, + meta_appini); tried++; free(file_name); /* ENOENT and EOK are Ok */ @@ -874,6 +983,36 @@ int config_for_app(const char *application, return EOK; } + +/* Function to return configuration data + * for the application without meta data. + */ +int config_for_app(const char *application, + const char *config_file, + const char *config_dir, + struct collection_item **ini_config, + int error_level, + struct collection_item **error_set) +{ + int error = EOK; + TRACE_FLOW_STRING("config_for_app", "Entry"); + + error = config_for_app_with_metadata(application, + config_file, + config_dir, + ini_config, + error_level, + error_set, + 0, + NULL, + NULL); + + TRACE_FLOW_NUMBER("config_for_app. Returning", error); + return error; +} + + + /* Reads a line from the file */ int read_line(FILE *file, char *buf, @@ -1127,7 +1266,22 @@ void print_file_parsing_errors(FILE *file, } -/* Print errors and warnings that were detected while processing grammar */ +/* Print errors and warnings that were detected while processing grammar. + * + * The following doxygen description is moved here. + * When the function gets exposed move it into + * the header file. + */ +/** + * @brief Print errors and warnings that were detected while + * checking grammar of the template. + * + * EXPERIMENTAL. Reserved for future use. + * + * @param[in] file File descriptor. + * @param[in] error_list List of the parsing errors. + * + */ void print_grammar_errors(FILE *file, struct collection_item *error_list) { @@ -1141,7 +1295,22 @@ void print_grammar_errors(FILE *file, grammar_error_str); } -/* Print errors and warnings that were detected while validating INI file. */ +/* Print errors and warnings that were detected while validating INI file. + * + * The following doxygen description is moved here. + * When the function gets exposed move it into + * the header file. + */ +/** + * @brief Print errors and warnings that were detected while + * checking INI file against the grammar object. + * + * EXPERIMENTAL. Reserved for future use. + * + * @param[in] file File descriptor. + * @param[in] error_list List of the parsing errors. + * + */ void print_validation_errors(FILE *file, struct collection_item *error_list) { @@ -1243,7 +1412,8 @@ int get_config_item(const char *section, } /* Is the collection of a right type */ - if (!col_is_of_class(ini_config, COL_CLASS_INI_CONFIG)) { + if ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) && + (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0)) { TRACE_ERROR_NUMBER("Wrong collection type", EINVAL); return EINVAL; } @@ -2054,7 +2224,8 @@ char **get_section_list(struct collection_item *ini_config, int *size, int *erro TRACE_FLOW_STRING("get_section_list","Entry"); /* Do we have the item ? */ if ((ini_config == NULL) || - !col_is_of_class(ini_config, COL_CLASS_INI_CONFIG)) { + ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) && + (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0))) { TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); if (error) *error = EINVAL; return NULL; @@ -2079,7 +2250,8 @@ char **get_attribute_list(struct collection_item *ini_config, const char *sectio TRACE_FLOW_STRING("get_attribute_list","Entry"); /* Do we have the item ? */ if ((ini_config == NULL) || - !col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) || + ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) && + (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0)) || (section == NULL)) { TRACE_ERROR_NUMBER("Invalid argument.", EINVAL); if (error) *error = EINVAL; diff --git a/common/ini/ini_config.h b/common/ini/ini_config.h index da8227b55..bd2bdd998 100644 --- a/common/ini/ini_config.h +++ b/common/ini/ini_config.h @@ -180,22 +180,13 @@ * of such sets. */ #define COL_CLASS_INI_PESET COL_CLASS_INI_BASE + 3 -/** @brief Collection of grammar errors. - * - * Reserved for future use. - */ -#define COL_CLASS_INI_GERROR COL_CLASS_INI_BASE + 4 -/** @brief Collection of validation errors. - * - * Reserved for future use. - */ -#define COL_CLASS_INI_VERROR COL_CLASS_INI_BASE + 5 -/** @brief Collection of lines from the INI file. + +/** + * @brief Collection of metadata. * - * Reserved for future use + * Collection that stores metadata. */ -#define COL_CLASS_INI_LINES COL_CLASS_INI_BASE + 6 - +#define COL_CLASS_INI_META COL_CLASS_INI_BASE + 4 /** * @} */ @@ -295,44 +286,185 @@ struct parse_error { */ /** - * @defgroup functions Functions + * @defgroup metadata Meta data + * + * Metadata is a collection of a similar structure as any ini file. + * The difference is that there are some predefined sections + * and attributes inside these sections. + * Using meta flags one can specify what section he is interested + * in including into the meta data. If a flag for a corresponding + * meta data section is specified the data for this section will + * be included into the meta data collection. The caller can then + * use meta data collection to get items from it and then get + * a specific value using a corresponding conversion function. + * + * Think about the meta data as an INI file that looks like this: + * + * <b> + * [ACCESS] + * - uid = <i>\<ini file owner uid\></i> + * - gid = <i>\<ini file group gid\></i> + * - perm = <i>\<permissions word\></i> + * - name = <i>\<file name\></i> + * - created = <i>\<time stamp\></i> + * - modified = <i>\<time stamp\></i> + * - ... + * + * [ERROR] + * - read_error = <i><file open error if any\></i> + * - ... + * + * [<i>TBD</i>] + * - ... + * + * </b> + * + * The names of the keys and sections provide an example + * of how the meta data is structured. Look information + * about specific sections and available keys in this manual + * to get the exact set of currently supported sections + * and keys. + * * @{ */ -/** @brief Function to return a parsing error as a string. +/** + * @brief Collect only meta data. * - * @param[in] parsing_error Error code for the parsing error. + * Special flag that indicates that only meta data + * needs to be collected. No parsing should be performed. * - * @return Error string. */ -const char *parsing_error_str(int parsing_error); +#define INI_META_ACTION_NOPARSE 0x10000000 -/** @brief Function to return a grammar error in template. +/** + * @defgroup metasection Meta data section names * - * EXPERIMENTAL. Reserved for future use. + * @{ + */ + +/** + * @brief Meta data section that stores file access information + * and ownership. + */ +#define INI_META_SEC_ACCESS "ACCESS" + +/** + * @brief Meta data "access" section flag to include access section + * into the output. + */ +#define INI_META_SEC_ACCESS_FLAG 0x00000001 + + +/** + * @defgroup metaaccesskeys Key names available in the "ACCESS" section * - * This error is returned when the template - * is translated into the grammar object. + * @{ * - * @param[in] parsing_error Error code for the grammar error. + */ + +/** + * @brief The value for this key will store user ID of the INI file owner. + * + */ +#define INI_META_KEY_UID "uid" + +/** + * @brief The value for this key will store group ID of the INI file owner. + * + */ +#define INI_META_KEY_GID "gid" + +/** + * @brief The value for this key will store INI file access permissions. + * + */ +#define INI_META_KEY_PERM "perm" + +/** + * @brief The value for this key will store INI file creation time stamp. + * + */ +#define INI_META_KEY_CREATED "created" + +/** + * @brief The value for this key will store INI file modification time stamp. + * + */ +#define INI_META_KEY_MODIFIED "modified" + +/** + * @brief The value for this key will store INI file full name. * - * @return Error string. */ -const char *grammar_error_str(int parsing_error); +#define INI_META_KEY_NAME "name" + +/** + * @} + */ + +/** + * @brief Meta data section that stores error related information. + */ +#define INI_META_SEC_ERROR "ERROR" + +/** + * @brief Meta data "error" section flag to include access section + * into the output. + */ +#define INI_META_SEC_ERROR_FLAG 0x00000002 + -/** @brief Function to return a validation error. +/** + * @defgroup metaerrorkeys Key names available in the "ERROR" section * - * EXPERIMENTAL. Reserved for future use. + * @{ * - * This is the error that it is returned when - * the INI file is validated against the - * grammar object. + */ + +/** + * @brief The value for this key will store read error when file was opened. * - * @param[in] parsing_error Error code for the validation error. + * If file was opened by caller first but this section was requested + * the value will be zero. + */ +#define INI_META_KEY_READ_ERROR "read_error" + +/** + * @brief The value for this key will store read error message if any. + * + * If file was opened by caller first but this section was requested + * the key will no be present. Also the key will no exist if no error + * occured. + */ +#define INI_META_KEY_READ_ERRMSG "err_msg" + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + + +/** + * @defgroup functions Functions + * @{ + */ + +/** @brief Function to return a parsing error as a string. + * + * @param[in] parsing_error Error code for the parsing error. * * @return Error string. */ -const char *validation_error_str(int parsing_error); +const char *parsing_error_str(int parsing_error); + /** * @brief Read configuration information from a file. @@ -397,36 +529,112 @@ int config_from_fd(const char *application, struct collection_item **error_list); + /** * @brief Read configuration information from a file with - * extra collection of line numbers. + * additional meta data. + * + * Meta data consists of addition information about + * the file for example when it was created + * or who is the owner. For the detailed description + * of the meta data content and structure see + * \ref metadata "meta data" section. + * + * If the metadata argument is not NULL + * the calling function MUST always free meta data since it can + * be allocated even if the function returned error. + * + * @param[in] application Name of the application, + * will be used as name of + * the collection. + * @param[in] config_filename Name of the config file, + * if NULL the configuration + * collection will be empty. + * @param[out] ini_config If *ini_config is NULL + * a new ini object will be + * allocated, otherwise + * the one that is pointed to + * will be updated. + * @param[in] error_level Break for errors, warnings + * or best effort (don't break). + * @param[out] error_list List of errors for the file + * detected during parsing. + * @param[in] metaflags A bit mask of flags that define + * what kind of metadata should + * be collected. + * @param[out] metadata Collection of metadata + * values. See \ref metadata "meta data" + * section for more details. + * Can be NULL. + * + * @return 0 - Success. + * @return EINVAL - Invalid parameter. + * @return Any error returned by fopen(). * - * EXPERIMENTAL. Reserved for future use. * */ -int config_from_file_with_lines( +int config_from_file_with_metadata( const char *application, const char *config_filename, struct collection_item **ini_config, int error_level, struct collection_item **error_list, - struct collection_item **lines); + uint32_t metaflags, + struct collection_item **metadata); + /** - * @brief Read configuration information from a file descriptor with - * extra collection of line numbers. + * @brief Read configuration information from a file descriptor + * with additional meta data. * - * EXPERIMENTAL. Reserved for future use. + * Meta data consists of addition information about + * the file for example when it was created + * or who is the owner. For the detailed description + * of the meta data content and structure see + * \ref metadata "meta data" section. + * + * If the metadata argument is not NULL + * the calling function MUST always free meta data since it can + * be allocated even if the function returned error. + * + * @param[in] application Name of the application, + * will be used as name of + * the collection. + * @param[in] fd Previously opened file + * descriptor for the config file. + * @param[in] config_source Name of the file being parsed, + * for use when printing the error + * list. + * @param[out] ini_config If *ini_config is NULL + * a new ini object will be + * allocated, otherwise + * the one that is pointed to + * will be updated. + * @param[in] error_level Break for errors, warnings + * or best effort (don't break). + * @param[out] error_list List of errors for the file + * detected during parsing. + * @param[in] metaflags A bit mask of flags that define + * what kind of metadata should + * be collected. + * @param[out] metadata Collection of metadata + * values. See \ref metadata "meta data" + * section for more details. + * Can be NULL. + * + * @return 0 - Success. + * @return EINVAL - Invalid parameter. * */ -int config_from_fd_with_lines( +int config_from_fd_with_metadata( const char *application, int fd, const char *config_source, struct collection_item **ini_config, int error_level, struct collection_item **error_list, - struct collection_item **lines); + uint32_t metaflags, + struct collection_item **metadata); /** @@ -443,7 +651,7 @@ int config_from_fd_with_lines( * the configuration files for * different applications reside. * Function will look for file - * with the name name constructed by + * with the name constructed by * appending ".ini" to the end of * the "application" argument. * @param[out] ini_config A new configuration object. @@ -464,6 +672,64 @@ int config_for_app(const char *application, struct collection_item **error_set); /** + * @brief Read default configuration file and then + * overwrite it with a specific one from the directory. + * + * If requested collect meta data for both. + * + * If the metadata argument is not NULL + * the calling function MUST always free meta data since it can + * be allocated even if the function returned error. + * + * @param[in] application Name of the application, + * will be used as name of + * the collection. + * @param[in] config_file Name of the configuration file, + * with default settings for all + * appplications. + * @param[in] config_dir Name of the directory where + * the configuration files for + * different applications reside. + * Function will look for file + * with the name constructed by + * appending ".ini" to the end of + * the "application" argument. + * @param[out] ini_config A new configuration object. + * @param[in] error_level Break for errors, warnings + * or best effort (don't break). + * @param[out] error_set Collection of error lists. + * One list per file. + * @param[in] metaflags A bit mask of flags that define + * what kind of metadata should + * be collected. + * @param[out] meta_default Collection of metadata + * values for the default common + * config file for all applications. + * See \ref metadata "meta data" + * section for more details. + * Can be NULL. + * @param[out] meta_appini Collection of metadata + * values for the application + * specific config file. + * See \ref metadata "meta data" + * section for more details. + * Can be NULL. + * + * @return 0 - Success. + * @return EINVAL - Invalid parameter. + * @return Any error returned by fopen(). + */ +int config_for_app_with_metadata( + const char *application, + const char *config_file, + const char *config_dir, + struct collection_item **ini_config, + int error_level, + struct collection_item **error_set, + uint32_t metaflags, + struct collection_item **meta_default, + struct collection_item **meta_appini); +/** * @brief Function to free configuration object. * * @param[in] ini_config Configuration object. @@ -479,16 +745,14 @@ void free_ini_config(struct collection_item *ini_config); */ void free_ini_config_errors(struct collection_item *error_set); + /** - * @brief Function to free lines object. - * - * EXPERIMENTAL. Reserved for future use. + * @brief Function to free metadata. * - * @param[in] lines Lines object. + * @param[in] error_set Configuration meta data object. * */ -void free_ini_config_lines(struct collection_item *lines); - +void free_ini_config_metadata(struct collection_item *metadata); /** @@ -501,34 +765,6 @@ void free_ini_config_lines(struct collection_item *lines); void print_file_parsing_errors(FILE *file, struct collection_item *error_list); -/** - * @brief Print errors and warnings that were detected while - * checking grammar of the template. - * - * EXPERIMENTAL. Reserved for future use. - * - * @param[in] file File descriptor. - * @param[in] error_list List of the parsing errors. - * - */ -void print_grammar_errors(FILE *file, - struct collection_item *error_list); - -/** - * @brief Print errors and warnings that were detected while - * checking INI file against the grammar object. - * - * EXPERIMENTAL. Reserved for future use. - * - * @param[in] file File descriptor. - * @param[in] error_list List of the parsing errors. - * - */ -void print_validation_errors(FILE *file, - struct collection_item *error_list); - - - /** * @brief Print errors and warnings that were detected diff --git a/common/ini/ini_config_ut.c b/common/ini/ini_config_ut.c index 1c82a0579..dde1c52cc 100644 --- a/common/ini/ini_config_ut.c +++ b/common/ini/ini_config_ut.c @@ -87,7 +87,8 @@ int single_file(void) int error; struct collection_item *ini_config = NULL; struct collection_item *error_set = NULL; - struct collection_item *lines = NULL; + struct collection_item *metadata = NULL; + uint32_t flags; error = config_from_file("test", "./not_exist_ini.conf", &ini_config, INI_STOP_ON_NONE, &error_set); @@ -123,18 +124,54 @@ int single_file(void) ini_config = NULL; error_set = NULL; - COLOUT(printf("TEST WITH LINES\n")); + COLOUT(printf("TEST WITH METADATA NO PARSE\n")); + flags = INI_META_SEC_ACCESS_FLAG | + INI_META_SEC_ERROR_FLAG | + INI_META_ACTION_NOPARSE; - error = config_from_file_with_lines("test", "./ini.conf", - &ini_config, INI_STOP_ON_NONE, - &error_set, &lines); + error = config_from_file_with_metadata("test", "./ini.conf", + &ini_config, INI_STOP_ON_NONE, + NULL, + flags, + &metadata); if (error) { printf("Attempt to read configuration returned error: %d\n",error); + printf("\n\nMetadata\n"); + col_debug_collection(metadata, COL_TRAVERSE_DEFAULT); + free_ini_config_metadata(metadata); return error; } + if (ini_config) { + printf("Expected no config but got some.\n"); + col_debug_collection(ini_config, COL_TRAVERSE_DEFAULT); + free_ini_config(ini_config); + printf("\n\nMetadata\n"); + col_debug_collection(metadata, COL_TRAVERSE_DEFAULT); + free_ini_config_metadata(metadata); + return EINVAL; + } + + COLOUT(printf("\n\nMeta data\n")); + COLOUT(col_debug_collection(metadata, COL_TRAVERSE_DEFAULT)); + free_ini_config_metadata(metadata); + + COLOUT(printf("\n\n----------------------\n")); + + error = config_from_file_with_metadata("test", "./ini.conf", + &ini_config, INI_STOP_ON_NONE, + &error_set, + 0, + NULL); + if (error) { + printf("Attempt to read configuration returned error: %d\n",error); + print_file_parsing_errors(stdout, error_set); + free_ini_config_errors(error_set); + return error; + } + + COLOUT(printf("\n\n----------------------\n")); COLOUT(col_debug_collection(ini_config, COL_TRAVERSE_DEFAULT)); - COLOUT(col_debug_collection(lines, COL_TRAVERSE_DEFAULT)); COLOUT(printf("\n\n----------------------\n")); /* Output parsing errors (if any) */ @@ -144,7 +181,6 @@ int single_file(void) free_ini_config(ini_config); free_ini_config_errors(error_set); - free_ini_config_lines(lines); return 0; } @@ -154,7 +190,8 @@ int single_fd(void) int error; struct collection_item *ini_config = NULL; struct collection_item *error_set = NULL; - struct collection_item *lines = NULL; + struct collection_item *metadata = NULL; + uint32_t flags; int fd = open("./ini.conf", O_RDONLY); if (fd < 0) { @@ -187,7 +224,7 @@ int single_fd(void) ini_config = NULL; error_set = NULL; - COLOUT(printf("TEST WITH LINES\n")); + COLOUT(printf("TEST WITH FILE FD & META DATA\n")); fd = open("./ini.conf", O_RDONLY); if (fd < 0) { @@ -195,18 +232,63 @@ int single_fd(void) printf("Attempt to read configuration returned error: %d\n", error); return error; } - error = config_from_fd_with_lines("test", fd, - "./ini.conf", - &ini_config, - INI_STOP_ON_NONE, - &error_set, &lines); + + flags = INI_META_SEC_ACCESS_FLAG | + INI_META_SEC_ERROR_FLAG | + INI_META_ACTION_NOPARSE; + + error = config_from_fd_with_metadata("test", fd, + "./ini.conf", + &ini_config, + INI_STOP_ON_NONE, + &error_set, + flags, + &metadata); + if (error) { + printf("Attempt to read configuration returned error: %d\n",error); + printf("\n\nErrors\n"); + print_file_parsing_errors(stdout, error_set); + free_ini_config_errors(error_set); + printf("\n\nMetadata\n"); + col_debug_collection(metadata, COL_TRAVERSE_DEFAULT); + free_ini_config_metadata(metadata); + return error; + } + + if (ini_config) { + printf("Expected no config but got some.\n"); + col_debug_collection(ini_config, COL_TRAVERSE_DEFAULT); + free_ini_config(ini_config); + return EINVAL; + } + + /* FIXME get elements of the meta data and check them */ + + + COLOUT(printf("\n\nMeta data\n")); + COLOUT(col_debug_collection(metadata, COL_TRAVERSE_DEFAULT)); + free_ini_config_metadata(metadata); + + + error = config_from_fd_with_metadata("test", fd, + "./ini.conf", + &ini_config, + INI_STOP_ON_NONE, + &error_set, + 0, + NULL); + + close(fd); + if (error) { printf("Attempt to read configuration returned error: %d\n",error); + printf("\n\nErrors\n"); + print_file_parsing_errors(stdout, error_set); + free_ini_config_errors(error_set); return error; } COLOUT(col_debug_collection(ini_config, COL_TRAVERSE_DEFAULT)); - COLOUT(col_debug_collection(lines, COL_TRAVERSE_DEFAULT)); COLOUT(printf("\n\n----------------------\n")); /* Output parsing errors (if any) */ @@ -216,7 +298,6 @@ int single_fd(void) free_ini_config(ini_config); free_ini_config_errors(error_set); - free_ini_config_lines(lines); return 0; } diff --git a/common/ini/ini_metadata.c b/common/ini/ini_metadata.c new file mode 100644 index 000000000..630de699b --- /dev/null +++ b/common/ini/ini_metadata.c @@ -0,0 +1,104 @@ +/* + INI LIBRARY + + Functions to process metadata. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2010 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _GNU_SOURCE +#include "config.h" +#include "collection.h" +#include "collection_tools.h" +#include "trace.h" +#include "ini_config.h" +#include "ini_metadata.h" + +#define INI_METADATA "meta" + +/* Prepare metadata */ +int prepare_metadata(uint32_t metaflags, + struct collection_item **metadata, + int *save_error) +{ + int error = EOK; + struct collection_item *metasec = NULL; + + TRACE_FLOW_STRING("prepare_metadata", "Entry"); + + /* Are we supposed to collect or process meta data ? */ + if (!metadata) { + TRACE_FLOW_STRING("No meta data", "Exit"); + return EOK; + } + + /* Allocate metadata */ + error = col_create_collection(metadata, + INI_METADATA, + COL_CLASS_INI_META); + if (error) { + TRACE_ERROR_NUMBER("Failed to create meta data", error); + return error; + } + + /* Check and create section for file error if needed */ + if (metaflags & INI_META_SEC_ERROR_FLAG) { + /* Create ERROR collection */ + if ((error = col_create_collection(&metasec, + INI_META_SEC_ERROR, + COL_CLASS_INI_SECTION)) || + (error = col_add_collection_to_collection( + *metadata, + NULL, + NULL, + metasec, + COL_ADD_MODE_REFERENCE))) { + TRACE_ERROR_NUMBER("Failed to create error section", error); + col_destroy_collection(metasec); + col_destroy_collection(*metadata); + return error; + } + /* If we are here we would have to save file open error */ + *save_error = 1; + col_destroy_collection(metasec); + } + + TRACE_FLOW_STRING("prepare_metadata", "Exit"); + return error; +} + + + +/* Collect metadata for the file */ +int collect_metadata(uint32_t metaflags, + struct collection_item **metadata, + FILE *config_file) +{ + int error = EOK; + + TRACE_FLOW_STRING("collect_metadata", "Entry"); + + TRACE_FLOW_STRING("collect_metadata", "Exit"); + return error; +} + +/* Function to free metadata */ +void free_ini_config_metadata(struct collection_item *metadata) +{ + TRACE_FLOW_STRING("free_ini_config_metadata", "Entry"); + col_destroy_collection(metadata); + TRACE_FLOW_STRING("free_ini_config_metadata", "Exit"); +} diff --git a/common/ini/ini_metadata.h b/common/ini/ini_metadata.h new file mode 100644 index 000000000..2839453a6 --- /dev/null +++ b/common/ini/ini_metadata.h @@ -0,0 +1,42 @@ +/* + INI LIBRARY + + Header file for the meta data related functions. + + Copyright (C) Dmitri Pal <dpal@redhat.com> 2009 + + INI Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + INI Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with INI Library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef INI_METADATA_H +#define INI_METADATA_H + +#include <stdint.h> +#include <stdio.h> +#include "collection.h" + + +/* Prepare metadata */ +int prepare_metadata(uint32_t metaflags, + struct collection_item **metadata, + int *save_error); + +/* Collect metadata for the file */ +int collect_metadata(uint32_t metaflags, + struct collection_item **metadata, + FILE *config_file); + + + +#endif |