Hello, Extensive travel in recent months allowed me to finish this code.
Here is the updated design: https://fedorahosted.org/sssd/wiki/DesignDocs/ding-libs/INIConfigMerge Patches: 0001 - Fix in case the Ref array is empty and we need to print/debug it 0002 - Declaration of the new function to do access checks 0003 - Big patch with core functionality 0004 - Updated access check code to use new internal access control function 0005 - File with expected output for unit test validation 0006 - Makefile and related changes to start building new code No rush, take your time. :-) -- Thank you, Dmitri Pal Sr. Engineering Manager IdM portfolio Red Hat, Inc.
>From 9ce3ef526118ee8cf5e5dd8d28ddce971bba606e Mon Sep 17 00:00:00 2001 From: Dmitri Pal <d...@dpal.csb> Date: Sun, 7 Sep 2014 22:55:02 +0200 Subject: [PATCH 6/6] [INI] Make the merge function build --- Makefile.am | 15 +++++- ini/ini_configobj.h | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ ini/libini_config.sym | 1 + 3 files changed, 128 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 32fcfae..7f38e50 100644 --- a/Makefile.am +++ b/Makefile.am @@ -255,6 +255,7 @@ libini_config_la_SOURCES = \ ini/ini_get_valueobj.c \ ini/ini_get_array_valueobj.c \ ini/ini_list_valueobj.c \ + ini/ini_augment.c \ trace/trace.h libini_config_la_DEPENDENCIES = ini/libini_config.sym libini_config_la_LIBADD = \ @@ -286,19 +287,23 @@ dist_noinst_DATA += \ ini/ini.d/real32be.conf \ ini/ini.d/real32le.conf \ ini/ini.d/symbols.conf \ - ini/ini.d/new_line.conf + ini/ini.d/new_line.conf \ + ini/ini.d/merge.validator check_PROGRAMS += \ ini_config_ut \ ini_comment_ut \ ini_valueobj_ut \ - ini_parse_ut + ini_parse_ut \ + ini_augment_ut TESTS += \ ini_config_ut \ ini_comment_ut \ ini_valueobj_ut \ - ini_parse_ut + ini_parse_ut \ + ini_augment_ut + ini_config_ut_SOURCES = ini/ini_config_ut.c ini_config_ut_LDADD = \ @@ -314,6 +319,9 @@ ini_valueobj_ut_LDADD = libini_config.la libbasicobjects.la ini_parse_ut_SOURCES = ini/ini_parse_ut.c ini_parse_ut_LDADD = libini_config.la libcollection.la libbasicobjects.la +ini_augment_ut_SOURCES = ini/ini_augment_ut.c +ini_augment_ut_LDADD = libini_config.la libcollection.la libbasicobjects.la + ini_config-docs: if HAVE_DOXYGEN cd ini; \ @@ -324,6 +332,7 @@ clean-local-ini_config: rm -f ./*.out rm -f test.ini rm -f ./foo.conf ./bom* #From ini_parse_ut + rm -f ./merge.validator.in #From ini_augment_ut ############################################################################## # Additional rules diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h index 4ef98dc..620b545 100644 --- a/ini/ini_configobj.h +++ b/ini/ini_configobj.h @@ -374,6 +374,46 @@ enum INI_GET { * @} */ + +/** + * @defgroup augment Constants and structures related to augmentation. + * + * @{ + */ + +/** Structure to pass access check parameters to augmentation function. + * + * flags Define what to check. + * One can check file + * permissions with mask, + * uid, and gid of the file. + * uid Expected uid of the file. + * gid Expected gid of the file. + * mode Expected mode of the file. + * mask Mask to use in the mode check. + * Mask is always adjusted to + * include at least S_IRWXU, + * S_IRWXG and S_IRWXO + */ +struct access_check { + uint32_t flags; + uid_t uid; + gid_t gid; + mode_t mode; + mode_t mask; +}; + +/** Enumeration of augmentation modes. */ +enum augmode { + INI_AUG_ANY = 0, /**< Allow any augmentation. */ + INI_AUG_ADD = 1, /**< Allow only new sections. */ + INI_AUG_OVER = 2 /**< Allow section updates. */ +}; + +/** + * @} + */ + /** * @brief Name of the default section. * @@ -780,6 +820,81 @@ int ini_config_merge(struct ini_cfgobj *first, uint32_t collision_flags, struct ini_cfgobj **result); + +/** + * @brief Augment configuration + * + * Function merges the main configuration file + * with the configuration file snippets + * read from a specified directory. + * + * @param[in] base_cfg A configuration object + * that will be augmented. + * @param[in] path Path to a directory where + * configuration snippets + * will be read from. + * @param[in] patterns List of regular expressions + * that the name of a snippet file + * has to match to be considered + * for merge. + * @param[in] sections List of regular expressions + * that the section names in the snippet + * file need to match. If file contains + * sections that do not match any patterns + * the file is skipped and error is recorded. + * @param[in] check_perm Pointer to structure that + * holds criteria for the + * access check. + * @param[in] error_level Flags that control actions + * in case of parsing error in a snippet file. + * @param[in] collision_flags These flags control how the potential + * collisions between keys and sections + * within the snippet file will be handled. + * For more information + * see collision flag definitions. + * @param[in] parse_flags Flags that control parsing process, + * for example how to handle spaces at + * the beginning of the line. + * @param[in] merge_flags Flags that control handling + * of the duplicate sections or keys + * during merging of the snippets. + * They are different from the collision flags + * because duplicate sections and keys inside + * are snippets most likely will be handled as + * 'last value wins' while during merge + * the attempt to overwrite + * a specific section might be treated as + * an error. + * @param[out] result_cfg A new configuration object, + * the result of the merge. + * @param[out] error_list List of strings that + * contains all encountered + * errors. + * It can be NULL, in this case list of errors + * is not populated. + * @param[out] success_list List of strings that + * contains file names of snippets that were + * successfully merged. + * It can be NULL, in this case list of files + * is not populated. + * + * @return 0 - Success. + * @return EINVAL - Invalid parameter. + * @return ENOMEM - No memory. + */ +int ini_config_augment(struct ini_cfgobj *base_cfg, + const char *path, + const char **patterns, + const char **sections, + struct access_check *check_perm, + int error_level, + uint32_t collision_flags, + uint32_t parse_flags, + uint32_t merge_flags, + struct ini_cfgobj **result_cfg, + struct ref_array **error_list, + struct ref_array **success_list); + /** * @brief Set the folding boundary * diff --git a/ini/libini_config.sym b/ini/libini_config.sym index 3ca15bb..33d2246 100644 --- a/ini/libini_config.sym +++ b/ini/libini_config.sym @@ -62,6 +62,7 @@ global: ini_config_parse; ini_config_copy; ini_config_merge; + ini_config_augment; ini_config_set_wrap; ini_config_serialize; ini_get_section_list; -- 1.7.1
>From 9f80182fecbf1c1800a22ff279bf8915f4196f42 Mon Sep 17 00:00:00 2001 From: Dmitri Pal <d...@dpal.csb> Date: Sun, 7 Sep 2014 16:39:58 +0200 Subject: [PATCH 2/6] [INI] Declaring new internal access check function --- ini/ini_config_priv.h | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/ini/ini_config_priv.h b/ini/ini_config_priv.h index e1292f7..331363f 100644 --- a/ini/ini_config_priv.h +++ b/ini/ini_config_priv.h @@ -99,6 +99,13 @@ int valid_collision_flags(uint32_t collision_flags); /* Empty section */ int empty_section(struct collection_item *sec); +/* Internal access check function */ +int access_check_int(struct stat *file_stats, + uint32_t flags, + uid_t uid, + gid_t gid, + mode_t mode, + mode_t mask); #endif -- 1.7.1
>From 09a23b41da6f7a4584f652c4a5a95bb158b0dfe2 Mon Sep 17 00:00:00 2001 From: Dmitri Pal <d...@dpal.csb> Date: Sun, 7 Sep 2014 16:42:00 +0200 Subject: [PATCH 3/6] [INI] New function to merge snippets The patch includes the implementation of the function and the unit test --- ini/ini_augment.c | 924 ++++++++++++++++++++++++++++++++++++++++++++++++++ ini/ini_augment_ut.c | 353 +++++++++++++++++++ 2 files changed, 1277 insertions(+), 0 deletions(-) create mode 100644 ini/ini_augment.c create mode 100644 ini/ini_augment_ut.c diff --git a/ini/ini_augment.c b/ini/ini_augment.c new file mode 100644 index 0000000..7f59d38 --- /dev/null +++ b/ini/ini_augment.c @@ -0,0 +1,924 @@ +/* + INI LIBRARY + + Module represents part of the INI interface. + The main function in this module allows to merge + snippets of different config files. + + Copyright (C) Dmitri Pal <d...@redhat.com> 2014 + + 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 <errno.h> +#include <stdarg.h> +#include <dirent.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <sys/types.h> +#include <regex.h> +#include "trace.h" +#include "collection.h" +#include "collection_tools.h" +#include "ini_configobj.h" +#include "ini_config_priv.h" +#include "ini_defines.h" +#include "path_utils.h" + +/* Constants to match */ +#define INI_CURRENT_DIR "." +#define INI_PARENT_DIR ".." + +/* Size of incremental growth for ref of the array of strings */ +#define INI_AUG_ARR_SIZE_INC 50 + + +/* Function to add an error to the array */ +static void ini_aug_add_string(struct ref_array *ra, + const char *format, + ...) +{ + va_list args; + char *result = NULL; + + TRACE_FLOW_ENTRY(); + + va_start(args, format); + + if(vasprintf(&result, format, args )) { + TRACE_INFO_STRING("String:", result); + ref_array_append(ra, (void *)&result); + } + + va_end(args); + + TRACE_FLOW_EXIT(); +} + +/* Add error about opening directory */ +static void add_dir_open_error(int error, char *dirname, + struct ref_array *ra_err) +{ + + TRACE_FLOW_ENTRY(); + + switch(error) { + case EACCES: + ini_aug_add_string(ra_err, + "Permission denied opening %s.", + dirname); + break; + case EMFILE: + case ENFILE: + ini_aug_add_string(ra_err, + "Too many file descriptors in use while opening %s.", + dirname); + break; + case ENOENT: + ini_aug_add_string(ra_err, + "Directory %s does not exist.", + dirname); + break; + case ENOTDIR: + ini_aug_add_string(ra_err, + "Path %s is not a directory.", + dirname); + break; + case ENOMEM: + ini_aug_add_string(ra_err, + "Insufficient memory while opening %s.", + dirname); + break; + default: + ini_aug_add_string(ra_err, + "Unknown error while opening %s.", + dirname); + break; + } + + TRACE_FLOW_EXIT(); +} + +/* Cleanup callback for regex array */ +static void regex_cleanup(void *elem, + ref_array_del_enum type, + void *data) +{ + TRACE_FLOW_ENTRY(); + regfree(*((regex_t **)elem)); + free(*((regex_t **)elem)); + TRACE_FLOW_EXIT(); +} + + +/* Prepare array of regular expressions */ +static int ini_aug_regex_prepare(const char **patterns, + struct ref_array *ra_err, + struct ref_array **ra_regex) +{ + int error = EOK; + int reg_err = 0; + char **pat = NULL; + struct ref_array *ra = NULL; + regex_t *preg = NULL; + size_t buf_size = 0; + char *err_str = NULL; + + TRACE_FLOW_ENTRY(); + + if (patterns) { + + /* Create array to mark bad patterns */ + error = ref_array_create(&ra, + sizeof(regex_t *), + INI_AUG_ARR_SIZE_INC, + regex_cleanup, + NULL); + if (error) { + TRACE_ERROR_NUMBER("Failed to create array.", error); + return error; + } + + memcpy(&pat, &patterns, sizeof(char **)); + + /* Run through the list and save precompiled patterns */ + while (*pat) { + TRACE_INFO_STRING("Pattern:", *pat); + + preg = calloc(1, sizeof(regex_t)); + if (preg == NULL) { + TRACE_ERROR_NUMBER("Failed to create array.", ENOMEM); + ref_array_destroy(ra); + return ENOMEM; + } + reg_err = regcomp(preg, *pat, REG_NOSUB); + if (reg_err) { + /* Get size, allocate buffer, record error... */ + buf_size = regerror(reg_err, preg, NULL, 0); + err_str = malloc (buf_size); + if (err_str == NULL) { + TRACE_ERROR_NUMBER("Failed to create array.", ENOMEM); + ref_array_destroy(ra); + free(preg); + return ENOMEM; + } + regerror(reg_err, preg, err_str, buf_size); + free(preg); + ini_aug_add_string(ra_err, + "Failed to process expression: %s." + " Compilation returned error: %s", + *pat, err_str); + + /* All error processing is done - advance to next pattern */ + pat++; + continue; + } + /* In case of no error add compiled expression into the buffer */ + error = ref_array_append(ra, (void *)&preg); + if (error) { + TRACE_ERROR_NUMBER("Failed to add element to array.", error); + ref_array_destroy(ra); + free(preg); + return error; + } + /* Advance */ + pat++; + } + } + + *ra_regex = ra; + /* ref_array_debug(*ra_regex, 1); */ + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Match file name */ +static bool ini_aug_match_name(char *name, + struct ref_array *ra_regex) +{ + uint32_t len = 0; + uint32_t i = 0; + bool match = false; + regex_t *preg = NULL; + + TRACE_FLOW_ENTRY(); + + len = ref_array_len(ra_regex); + if (len == 0) { + /* List is empty - nothing to do */ + TRACE_FLOW_EXIT(); + return true; + } + + TRACE_INFO_STRING("Name to match:", name); + TRACE_INFO_NUMBER("Number of regexes:", len); + /* ref_array_debug(ra_regex, 1);*/ + + for (i = 0; i < len; i++) { + preg = *((regex_t **)ref_array_get(ra_regex, i, NULL)); + if (regexec(preg, name, 0, NULL, 0) == 0) { + TRACE_INFO_NUMBER("Name matched regex number:", i); + match = true; + break; + } + } + + TRACE_FLOW_EXIT(); + return match; +} + +/* Check if this is a file and validate permission */ +static bool ini_check_file_perm(char *name, + struct access_check *check_perm, + struct ref_array *ra_err) +{ + bool ret = false; + int error = EOK; + struct stat file_info; + + TRACE_FLOW_ENTRY(); + + errno = 0; + if (stat(name, &file_info) == -1) { + error = errno; + TRACE_ERROR_NUMBER("Failed to get file stats", error); + ini_aug_add_string(ra_err, + "Failed to read metadata for file %s." + " Skipping.", + name); + return false; + } + + if (!S_ISREG(file_info.st_mode)) { + ini_aug_add_string(ra_err, + "File %s is not a regular file. Skipping.", + name); + return false; + } + + if ((check_perm) && (check_perm->flags)) { + error = access_check_int(&file_info, + check_perm->flags, + check_perm->uid, + check_perm->gid, + check_perm->mode, + check_perm->mask); + if(error) { + TRACE_ERROR_NUMBER("Access check returned", error); + ini_aug_add_string(ra_err, + "File %s did not pass access check. Skipping.", + name); + return false; + } + } + + ret = true; + + TRACE_INFO_STRING("Returning", (ret ? "true" : "false")); + TRACE_FLOW_EXIT(); + + return ret; +} + +/* Sort array */ +static void ini_aug_sort_list(struct ref_array *ra_list) +{ + unsigned len = 0, i = 0, j = 0, k = 0; + + TRACE_FLOW_ENTRY(); + + len = ref_array_len(ra_list); + if (len == 0) return; + + /* If have trace output array before sorting */ +/* +#ifdef HAVE_TRACE + for (i = 0; i < len; i++) { + TRACE_INFO_STRING("Before:", + *((char **) ref_array_get(ra_list, i, NULL))); + + } +#endif +*/ + + for (k = 0; k < len-1; k++) { + i = k; + j = k + 1; + + while ((j > 0) && (strncmp(*((char **) ref_array_get(ra_list, i, NULL)), + *((char **) ref_array_get(ra_list, j, NULL)), + PATH_MAX) > 0)) { + ref_array_swap(ra_list, i, j); + i--; + j--; + } + } + + /* And after sorting */ +/* +#ifdef HAVE_TRACE + for (i = 0; i < len; i++) { + TRACE_INFO_STRING("After:", + *((char **) ref_array_get(ra_list, i, NULL))); + + } +#endif +*/ + + TRACE_FLOW_EXIT(); +} + +/* Construct snippet lists based on the directory */ +static int ini_aug_construct_list(char *dirname , + const char **patterns, + struct access_check *check_perm, + struct ref_array *ra_list, + struct ref_array *ra_err) +{ + + int error = EOK; + DIR *dir = NULL; + struct dirent *entry = NULL; + char *snipname = NULL; + char fullname[PATH_MAX + 1] = {0}; + struct ref_array *ra_regex = NULL; + bool match = false; + + TRACE_FLOW_ENTRY(); + + /* Prepare patterns */ + error = ini_aug_regex_prepare(patterns, + ra_err, + &ra_regex); + if (error) { + TRACE_ERROR_NUMBER("Failed to prepare regex array.", error); + return error; + } + + /* Open directory */ + errno = 0; + dir = opendir(dirname); + if (!dir) { + error = errno; + if (error == ENOMEM) { + TRACE_ERROR_NUMBER("No memory to open dir.", ENOMEM); + ref_array_destroy(ra_regex); + return ENOMEM; + } + /* Log an error, it is a recoverable error */ + add_dir_open_error(error, dirname, ra_err); + return EOK; + } + + /* Loop through the directory */ + while ((entry = readdir(dir)) != NULL) + { + TRACE_INFO_STRING("Processing", entry->d_name); + + /* Always skip current and parent dirs */ + if ((strncmp(entry->d_name, + INI_CURRENT_DIR, + sizeof(INI_CURRENT_DIR)) == 0) || + (strncmp(entry->d_name, + INI_PARENT_DIR, + sizeof(INI_PARENT_DIR)) == 0)) continue; + + /* Match names */ + match = ini_aug_match_name(entry->d_name, ra_regex); + + if (match) { + + snprintf(fullname, PATH_MAX, "%s/%s", dirname, entry->d_name); + + if(ini_check_file_perm(fullname, check_perm, ra_err)) { + + /* Dup name and add to the array */ + snipname = NULL; + snipname = strdup(fullname); + if (error) { + TRACE_ERROR_NUMBER("Failed to dup string.", ENOMEM); + ref_array_destroy(ra_regex); + return ENOMEM; + } + + error = ref_array_append(ra_list, (void *)&snipname); + if (error) { + TRACE_ERROR_NUMBER("No memory to add file to " + "the snippet list.", + ENOMEM); + ref_array_destroy(ra_regex); + return ENOMEM; + } + } + } + else { + ini_aug_add_string(ra_err, + "File %s did not match provided patterns." + " Skipping.", + entry->d_name); + } + } + + closedir(dir); + ref_array_destroy(ra_regex); + + ini_aug_sort_list(ra_list); + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Construct the full dir path */ +static int ini_aug_expand_path(const char *path, char **fullname) +{ + int error = EOK; + char *dirname = NULL; + + TRACE_FLOW_ENTRY(); + TRACE_INFO_STRING("Input path", path); + + dirname = malloc(PATH_MAX + 1); + if (!dirname) { + TRACE_ERROR_NUMBER("Failed to allocate memory for file path.", ENOMEM); + return ENOMEM; + } + + /* Make the path */ + error = make_normalized_absolute_path(dirname, + PATH_MAX, + path); + if (error) { + TRACE_ERROR_NUMBER("Failed to resolve path", error); + free(dirname); + /* This is a recoverable error */ + *fullname = NULL; + } + else *fullname = dirname; + + TRACE_INFO_STRING("Output path", *fullname); + TRACE_FLOW_EXIT(); + + return EOK; +} + +/* Prepare the lists of the files that need to be merged */ +static int ini_aug_preprare(const char *path, + const char **patterns, + struct access_check *check_perm, + struct ref_array *ra_list, + struct ref_array *ra_err) +{ + int error = EOK; + char *dirname = NULL; + + TRACE_FLOW_ENTRY(); + + /* Contruct path */ + error = ini_aug_expand_path(path, &dirname); + if (error) { + TRACE_ERROR_NUMBER("Failed to allocate memory for dir path.", error); + return error; + } + + /* Was it a good path? */ + if (!dirname) { + TRACE_ERROR_NUMBER("Failed to resolve path", error); + ini_aug_add_string(ra_err, "Could not resolve directory path %s.", + path); + /* Path might not exist so it is a recoverable error */ + return EOK; + } + + /* Construct snipet lists */ + error = ini_aug_construct_list(dirname, + patterns, + check_perm, + ra_list, + ra_err); + free(dirname); + + TRACE_FLOW_EXIT(); + return error; +} + +/* Cleanup callback for string arrays */ +static void array_cleanup(void *elem, + ref_array_del_enum type, + void *data) +{ + TRACE_FLOW_ENTRY(); + free(*((char **)elem)); + TRACE_FLOW_EXIT(); +} + +/* Check that sections are in the given list */ +static int ini_aug_match_sec(struct ini_cfgobj *snip_cfg, + struct ref_array *ra_regex, + struct ref_array *ra_err, + char *snip_name, + bool *skip) +{ + int error = EOK; + char **section_list = NULL; + char **section_iter = NULL; + int size = 0; + bool match = false; + int match_count = 0; + int section_count = 0; + + TRACE_FLOW_ENTRY(); + + section_list = ini_get_section_list(snip_cfg, &size, &error); + if (error) { + TRACE_ERROR_NUMBER("Failed create section list", error); + return error; + } + + if (section_list == NULL) { + /* No sections in the file */ + ini_aug_add_string(ra_err, "No sections found in file %s. Skipping.", + snip_name); + *skip = true; + TRACE_FLOW_EXIT(); + return EOK; + } + + section_iter = section_list; + + while (*section_iter) { + match = ini_aug_match_name(*section_iter, ra_regex); + if (match) { + match_count++; + TRACE_INFO_STRING("Matched section", *section_iter); + } + else { + TRACE_INFO_STRING("Section not matched", *section_iter); + ini_aug_add_string(ra_err, "Section [%s] found in file %s is" + " not allowed.", + *section_iter, snip_name); + } + section_count++; + section_iter++; + } + + ini_free_section_list(section_list); + + /* Just in case check that we processed anything */ + if (section_count == 0) { + ini_aug_add_string(ra_err, "No sections found in file %s. Skipping.", + snip_name); + *skip = true; + TRACE_FLOW_EXIT(); + return EOK; + } + + /* Were all sections matched? */ + if (section_count != match_count) { + /* Snippet containes sections that are not allowed */ + ini_aug_add_string(ra_err, "File %s contains sections that" + " are not allowed. Skipping.", + snip_name); + *skip = true; + TRACE_FLOW_EXIT(); + return EOK; + } + + /* Everything matched OK so we give green light to merge */ + TRACE_INFO_STRING("File will be included", snip_name); + *skip = false; + TRACE_FLOW_EXIT(); + return EOK; +} + + +/* Apply snippets */ +static int ini_aug_apply(struct ini_cfgobj *cfg, + struct ref_array *ra_list, + const char **sections, + int error_level, + uint32_t collision_flags, + uint32_t parse_flags, + uint32_t merge_flags, + struct ref_array *ra_err, + struct ref_array *ra_ok, + struct ini_cfgobj **out_cfg) +{ + int error = EOK; + uint32_t len = 0; + uint32_t i = 0; + uint32_t j = 0; + struct ini_cfgfile *file_ctx = NULL; + struct ini_cfgobj *snip_cfg = NULL; + struct ini_cfgobj *res_cfg = NULL; + struct ini_cfgobj *tmp_cfg = NULL; + char **error_list = NULL; + unsigned cnt = 0; + bool skip = false; + struct ref_array *ra_regex = NULL; + char *snip_name = NULL; + + TRACE_FLOW_ENTRY(); + + len = ref_array_len(ra_list); + if (len == 0) { + /* List is empty - nothing to do */ + *out_cfg = NULL; + TRACE_FLOW_EXIT(); + return EOK; + } + + error = ini_config_copy(cfg, &res_cfg); + if (error) { + TRACE_ERROR_NUMBER("Failed to copy config object", error); + return error; + } + + /* Prepare patterns */ + error = ini_aug_regex_prepare(sections, + ra_err, + &ra_regex); + if (error) { + TRACE_ERROR_NUMBER("Failed to prepare regex array.", error); + ini_config_destroy(res_cfg); + return error; + } + + /* Loop through the snippets */ + for (i = 0; i < len; i++) { + + /* Prepare config object */ + error = ini_config_create(&snip_cfg); + if (error) { + TRACE_ERROR_NUMBER("Failed to create config object", error); + ini_config_destroy(res_cfg); + ref_array_destroy(ra_regex); + return error; + } + + /* Process snippet */ + snip_name = *((char **)ref_array_get (ra_list, i, NULL)); + TRACE_INFO_STRING("Processing", snip_name); + + /* Open file */ + error = ini_config_file_open(snip_name, + INI_META_NONE, + &file_ctx); + if (error) { + TRACE_ERROR_NUMBER("Failed to open snippet.", error); + ini_aug_add_string(ra_err, "Failed to open file %s.", snip_name); + ini_config_destroy(snip_cfg); + /* We can recover so go on */ + continue; + } + + TRACE_INFO_NUMBER("Error level:", error_level); + TRACE_INFO_NUMBER("Collision flags:", collision_flags); + TRACE_INFO_NUMBER("Parse level:", parse_flags); + + /* Read config */ + error = ini_config_parse(file_ctx, + error_level, + collision_flags, + parse_flags, + snip_cfg); + + ini_config_file_destroy(file_ctx); + file_ctx = NULL; + + if (error) { + TRACE_ERROR_NUMBER("Failed to parse configuration.", error); + cnt = ini_config_error_count(snip_cfg); + if (cnt) { + ini_aug_add_string(ra_err, + "Errors detected while parsing: %s.", + snip_name); + + /* Extract errors */ + error = ini_config_get_errors(snip_cfg, &error_list); + if (error) { + TRACE_ERROR_NUMBER("Can't get errors.", error); + ini_config_destroy(snip_cfg); + ini_config_destroy(res_cfg); + ref_array_destroy(ra_regex); + return error; + } + + /* Copy errors into error array */ + for (j=0; j< cnt; j++) { + ini_aug_add_string(ra_err, error_list[j]); + } + ini_config_free_errors(error_list); + } + /* The snippet was malformed, this is OK, go on */ + if (error_level != INI_STOP_ON_NONE) { + ini_aug_add_string(ra_err, + "Due to errors file %s is not considered." + " Skipping.", + snip_name); + ini_config_destroy(snip_cfg); + continue; + } + /* If we are told to not stop try to process anyway */ + } + + /* Validate that file contains only allowed sections */ + if (sections) { + /* Use a safe default, function should update it anyways + * but it is better to not merge than to allow bad snippet */ + skip = true; + error = ini_aug_match_sec(snip_cfg, ra_regex, ra_err, + snip_name, &skip); + if (error) { + TRACE_ERROR_NUMBER("Failed to validate section.", error); + ini_config_destroy(snip_cfg); + ini_config_destroy(res_cfg); + ref_array_destroy(ra_regex); + return error; + } + } + + /* Merge */ + if (!skip) { + /* col_debug_collection(res_cfg->cfg, COL_TRAVERSE_DEFAULT); */ + error = ini_config_merge(res_cfg, snip_cfg, merge_flags, &tmp_cfg); + if (error) { + if (error == ENOMEM) { + TRACE_ERROR_NUMBER("Merge failed.", error); + ini_config_destroy(snip_cfg); + ini_config_destroy(res_cfg); + ref_array_destroy(ra_regex); + return error; + } + else if + ((error == EEXIST) && + ((((merge_flags & INI_MS_MASK) == INI_MS_DETECT) && + ((merge_flags & INI_MV2S_MASK) != INI_MV2S_ERROR)) || + (((merge_flags & INI_MS_MASK) != INI_MS_ERROR) && + ((merge_flags & INI_MV2S_MASK) == INI_MV2S_DETECT)))) { + TRACE_ERROR_NUMBER("Got error in detect mode", error); + /* Fall through! */ + } + else { + ini_aug_add_string(ra_err, + "Errors during merge." + " Snippet ignored %s.", + snip_name); + /* The snippet failed to merge, this is OK, go on */ + TRACE_INFO_NUMBER("Merge failure.Continue. Error", error); + ini_config_destroy(snip_cfg); + continue; + } + } + TRACE_INFO_STRING("Merged file.", snip_name); + /* col_debug_collection(tmp_cfg->cfg, COL_TRAVERSE_DEFAULT); */ + ini_config_destroy(res_cfg); + res_cfg = tmp_cfg; + + /* Record that snippet was successfully merged */ + ini_aug_add_string(ra_ok, "%s", snip_name); + } + /* Cleanup */ + ini_config_destroy(snip_cfg); + } + + ref_array_destroy(ra_regex); + *out_cfg = res_cfg; + TRACE_FLOW_EXIT(); + return error; +} + +/* Function to merge additional snippets of the config file + * from a provided directory. + */ +int ini_config_augment(struct ini_cfgobj *base_cfg, + const char *path, + const char **patterns, + const char **sections, + struct access_check *check_perm, + int error_level, + uint32_t collision_flags, + uint32_t parse_flags, + uint32_t merge_flags, + struct ini_cfgobj **result_cfg, + struct ref_array **error_list, + struct ref_array **success_list) +{ + int error = EOK; + /* The internal list that will hold snippet file names */ + struct ref_array *ra_list = NULL; + /* List of error strings that will be returned to the caller */ + struct ref_array *ra_err = NULL; + /* List of files that were merged */ + struct ref_array *ra_ok = NULL; + /* Resulting configuration object */ + struct ini_cfgobj *out_cfg = NULL; + + /* Check arguments */ + if (base_cfg == NULL) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + } + + if (result_cfg == NULL) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + } + + + /* Create arrays for lists */ + if ((ref_array_create(&ra_list, + sizeof(char *), + INI_AUG_ARR_SIZE_INC, + array_cleanup, + NULL) != 0) || + (ref_array_create(&ra_err, + sizeof(char *), + INI_AUG_ARR_SIZE_INC * 5, + array_cleanup, + NULL) != 0) || + (ref_array_create(&ra_ok, + sizeof(char *), + INI_AUG_ARR_SIZE_INC * 5, + array_cleanup, + NULL) != 0)) { + TRACE_ERROR_NUMBER("Failed to allocate memory for arrays.", + ENOMEM); + ref_array_destroy(ra_list); + ref_array_destroy(ra_err); + ref_array_destroy(ra_ok); + return ENOMEM; + } + + /* Construct snipet lists */ + error = ini_aug_preprare(path, + patterns, + check_perm, + ra_list, + ra_err); + if (error) { + TRACE_ERROR_NUMBER("Failed to prepare lists of snippets.", + error); + ref_array_destroy(ra_list); + ref_array_destroy(ra_err); + ref_array_destroy(ra_ok); + return error; + } + + /* Apply snippets */ + error = ini_aug_apply(base_cfg, + ra_list, + sections, + error_level, + collision_flags, + parse_flags, + merge_flags, + ra_err, + ra_ok, + &out_cfg); + if (error) { + TRACE_ERROR_NUMBER("Failed to process snippet list.", + error); + ref_array_destroy(ra_list); + ref_array_destroy(ra_err); + ref_array_destroy(ra_ok); + return error; + } + + /* Cleanup */ + ref_array_destroy(ra_list); + + *result_cfg = out_cfg; + + if (error_list) { + *error_list = ra_err; + } + else { + ref_array_destroy(ra_err); + } + + if (success_list) { + *success_list = ra_ok; + } + else { + ref_array_destroy(ra_ok); + } + + TRACE_FLOW_EXIT(); + return error; +} + diff --git a/ini/ini_augment_ut.c b/ini/ini_augment_ut.c new file mode 100644 index 0000000..94d610e --- /dev/null +++ b/ini/ini_augment_ut.c @@ -0,0 +1,353 @@ +/* + INI LIBRARY + + Unit test for the comment object. + + Copyright (C) Dmitri Pal <d...@redhat.com> 2014 + + 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/>. +*/ + +#include "config.h" +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +/* #define TRACE_LEVEL 7 */ +#define TRACE_HOME +#include "trace.h" +#include "ini_configobj.h" +#include "ini_config_priv.h" +#include "collection_tools.h" +#include "path_utils.h" + +int verbose = 0; + +#define INIOUT(foo) \ + do { \ + if (verbose) { printf("%30s(%4d): ",__FUNCTION__,__LINE__); foo; } \ + } while(0) + +typedef int (*test_fn)(void); + +void print_list(struct ref_array *list); +int print_list_to_file(struct ref_array *list, + const char *filename, + const char *mode); +static int expand_path(const char *path, char **fullname); + + +/* Construct the full dir path */ +static int expand_path(const char *path, char **fullname) +{ + int error = EOK; + char *dirname = NULL; + + TRACE_FLOW_ENTRY(); + TRACE_INFO_STRING("Input path", path); + + dirname = malloc(PATH_MAX + 1); + if (!dirname) { + INIOUT(printf("Failed to allocate memory for file path.")); + return ENOMEM; + } + + /* Make the path */ + error = make_normalized_absolute_path(dirname, + PATH_MAX, + path); + if (error) { + INIOUT(printf("Failed to resolve path %d\n", error)); + free(dirname); + return error; + } + else *fullname = dirname; + + TRACE_INFO_STRING("Output path", *fullname); + TRACE_FLOW_EXIT(); + + return EOK; +} + +static int prepare_results(const char *srcdir, + const char *srcfile, + const char *destfile) +{ + int error = EOK; + char *exp_src= NULL; + FILE *fsrc = NULL; + FILE *fout = NULL; + char *line = NULL; + size_t len = 0; + ssize_t rd; + + TRACE_FLOW_ENTRY(); + + error = expand_path(srcdir, &exp_src); + if (error) { + INIOUT(printf("Expand path returned error %d\n", error)); + return error; + } + + INIOUT(printf("Source file: %s\n", srcfile)); + INIOUT(printf("Output file: %s\n", destfile)); + + fsrc = fopen(srcfile, "r"); + if (!fsrc) { + error = errno; + free(exp_src); + INIOUT(printf("Failed to open source file %d\n", error)); + return error; + } + + fout = fopen(destfile, "w"); + if (!fsrc) { + error = errno; + fclose(fsrc); + free(exp_src); + INIOUT(printf("Failed to open output file %d\n", error)); + return error; + } + + INIOUT(printf("Path %s\n", exp_src)); + + while ((rd = getline(&line, &len, fsrc)) != -1) { + if (strchr(line, '%')) fprintf(fout, line, exp_src, "/ini/ini.d"); + else fprintf(fout, "%s", line); + } + + if (line) + free(line); + + fclose(fsrc); + fclose(fout); + free(exp_src); + + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Function to print contents of the list */ +void print_list(struct ref_array *list) +{ + uint32_t i = 0; + char *ret = NULL; + void *ptr = NULL; + + for (;;) { + ptr = ref_array_get(list, i, &ret); + if (ptr) { + INIOUT(printf("%s\n", ret)); + i++; + } + else break; + } +} + +/* Function to print contents of the list */ +int print_list_to_file(struct ref_array *list, + const char *filename, + const char *mode) +{ + uint32_t i = 0; + char *ret = NULL; + void *ptr = NULL; + FILE *file = NULL; + + file = fopen(filename, mode); + if (file) { + for (;;) { + ptr = ref_array_get(list, i, &ret); + if (ptr) { + fprintf(file,"%s\n", ret); + i++; + } + else break; + } + } + else { + printf("Failed to open file for results\n"); + return -1; + } + fclose(file); + return 0; +} + + +/* Basic test */ +static int basic_test(void) +{ + int error = EOK; + int error1 = EOK; + char indir[PATH_MAX]; + char srcname[PATH_MAX]; + char filename[PATH_MAX]; + char resname[PATH_MAX]; + char command[PATH_MAX * 3]; + char *builddir = NULL; + char *srcdir = NULL; + struct ini_cfgobj *in_cfg = NULL; + struct ini_cfgobj *result_cfg = NULL; + struct ref_array *error_list = NULL; + struct ref_array *success_list = NULL; + struct access_check ac = { INI_ACCESS_CHECK_MODE, + 0, + 0, + 0444, + 0444 }; + + /* Match all that do not start with 'r' + * and end with '.conf' and then match all + * ending with '.conf' */ + const char **patterns = (const char *[]) { "#", + "^[^r][a-z]*\\.conf$", + "^real\\.conf$", + NULL }; + + /* Match all that do not start with 'r' + * and end with '.conf' and then match all + * ending with '.conf' */ + const char **sections = (const char *[]) { "config", + "monitor", + "domains", + "services", + "provider", + NULL }; + + + INIOUT(printf("<==== Start ====>\n")); + + srcdir = getenv("srcdir"); + + builddir = getenv("builddir"); + + snprintf(indir, PATH_MAX, "%s/ini/ini.d", + (srcdir == NULL) ? "." : srcdir); + + + /* When run in dev environment there can be some temp files which + * we need to clean. */ + snprintf(command, PATH_MAX * 3, "rm %s/*~", indir); + system(command); + + /* Make the file path independent */ + snprintf(srcname, PATH_MAX, "%s/ini/ini.d/merge.validator", + (srcdir == NULL) ? "." : srcdir); + + snprintf(filename, PATH_MAX, "%s/merge.validator.in", + (builddir == NULL) ? "." : builddir); + + + snprintf(resname, PATH_MAX, "%s/merge.validator.out", + (builddir == NULL) ? "." : builddir); + + /* Prepare results file so that we can compare results */ + error = prepare_results(srcdir, srcname, filename); + if (error) { + INIOUT(printf("Failed to results file. Error %d.\n", error)); + return error; + } + + /* Create config collection */ + error = ini_config_create(&in_cfg); + if (error) { + INIOUT(printf("Failed to create collection. Error %d.\n", error)); + return error; + } + + error = ini_config_augment(in_cfg, + indir, + patterns, + sections, + &ac, + INI_STOP_ON_NONE, + INI_MV1S_DETECT|INI_MV2S_DETECT|INI_MS_DETECT, + INI_PARSE_NOSPACE|INI_PARSE_NOTAB, + INI_MV2S_DETECT|INI_MS_DETECT, + &result_cfg, + &error_list, + &success_list); + if (error) { + INIOUT(printf("Augmentation failed with error %d!\n", error)); + } + + print_list(error_list); + print_list(success_list); + + if (!result_cfg) { + error = -1; + printf("Configuration is empty.\n"); + } + else INIOUT(col_debug_collection(result_cfg->cfg, COL_TRAVERSE_DEFAULT)); + + error = print_list_to_file(error_list, "merge.validator.out", "w"); + error1 = print_list_to_file(success_list, "merge.validator.out", "a"); + /* Save error */ + if ((error1 !=0) && (error == 0)) error = error1; + + snprintf(command, PATH_MAX * 3, "diff -q %s %s", filename, resname); + error = system(command); + INIOUT(printf("Comparison of %s %s returned: %d\n", + filename, resname, error)); + + if ((error) || (WEXITSTATUS(error))) { + printf("Failed to run diff command %d %d.\n", error, + WEXITSTATUS(error)); + ref_array_destroy(error_list); + ref_array_destroy(success_list); + ini_config_destroy(in_cfg); + ini_config_destroy(result_cfg); + return -1; + } + + /* Cleanup */ + ref_array_destroy(error_list); + ref_array_destroy(success_list); + ini_config_destroy(in_cfg); + ini_config_destroy(result_cfg); + + INIOUT(printf("<==== End ====>\n")); + + return error; +} + + +int main(int argc, char *argv[]) +{ + int error = EOK; + test_fn tests[] = { basic_test, + NULL }; + test_fn t; + int i = 0; + char *var; + + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1; + else { + var = getenv("COMMON_TEST_VERBOSE"); + if (var) verbose = 1; + } + + INIOUT(printf("Start\n")); + + while ((t = tests[i++])) { + error = t(); + if (error) { + printf("Failed with error %d!\n", error); + return error; + } + } + + INIOUT(printf("Success!\n")); + return 0; +} -- 1.7.1
>From 8f0209cb995fd88e71e96efb0b98a0256df58d5d Mon Sep 17 00:00:00 2001 From: Dmitri Pal <d...@dpal.csb> Date: Sun, 7 Sep 2014 16:43:31 +0200 Subject: [PATCH 4/6] [INI] Refactored access control check The patch includes implementation of the new internal function. --- ini/ini_fileobj.c | 66 +++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 47 insertions(+), 19 deletions(-) diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c index da8339e..252c23e 100644 --- a/ini/ini_fileobj.c +++ b/ini/ini_fileobj.c @@ -705,14 +705,13 @@ const struct stat *ini_config_get_stat(struct ini_cfgfile *file_ctx) return ret; } - /* Check access */ -int ini_config_access_check(struct ini_cfgfile *file_ctx, - uint32_t flags, - uid_t uid, - gid_t gid, - mode_t mode, - mode_t mask) +int access_check_int(struct stat *file_stats, + uint32_t flags, + uid_t uid, + gid_t gid, + mode_t mode, + mode_t mask) { mode_t st_mode; @@ -722,23 +721,18 @@ int ini_config_access_check(struct ini_cfgfile *file_ctx, INI_ACCESS_CHECK_GID | INI_ACCESS_CHECK_UID; - if ((file_ctx == NULL) || (flags == 0)) { + if (flags == 0) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } - if (file_ctx->stats_read == 0) { - TRACE_ERROR_NUMBER("Stats were not collected.", EINVAL); - return EINVAL; - } - /* Check mode */ if (flags & INI_ACCESS_CHECK_MODE) { TRACE_INFO_NUMBER("File mode as saved.", - file_ctx->file_stats.st_mode); + file_stats->st_mode); - st_mode = file_ctx->file_stats.st_mode; + st_mode = file_stats->st_mode; st_mode &= S_IRWXU | S_IRWXG | S_IRWXO; TRACE_INFO_NUMBER("File mode adjusted.", st_mode); @@ -761,8 +755,8 @@ int ini_config_access_check(struct ini_cfgfile *file_ctx, /* Check uid */ if (flags & INI_ACCESS_CHECK_UID) { - if (file_ctx->file_stats.st_uid != uid) { - TRACE_ERROR_NUMBER("GID:", file_ctx->file_stats.st_uid); + if (file_stats->st_uid != uid) { + TRACE_ERROR_NUMBER("GID:", file_stats->st_uid); TRACE_ERROR_NUMBER("GID passed in.", uid); TRACE_ERROR_NUMBER("Access denied.", EACCES); return EACCES; @@ -771,8 +765,8 @@ int ini_config_access_check(struct ini_cfgfile *file_ctx, /* Check gid */ if (flags & INI_ACCESS_CHECK_GID) { - if (file_ctx->file_stats.st_gid != gid) { - TRACE_ERROR_NUMBER("GID:", file_ctx->file_stats.st_gid); + if (file_stats->st_gid != gid) { + TRACE_ERROR_NUMBER("GID:", file_stats->st_gid); TRACE_ERROR_NUMBER("GID passed in.", gid); TRACE_ERROR_NUMBER("Access denied.", EACCES); return EACCES; @@ -784,6 +778,40 @@ int ini_config_access_check(struct ini_cfgfile *file_ctx, } +/* Check access */ +int ini_config_access_check(struct ini_cfgfile *file_ctx, + uint32_t flags, + uid_t uid, + gid_t gid, + mode_t mode, + mode_t mask) +{ + int error = EOK; + + TRACE_FLOW_ENTRY(); + + if (file_ctx == NULL) { + TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); + return EINVAL; + } + + if (file_ctx->stats_read == 0) { + TRACE_ERROR_NUMBER("Stats were not collected.", EINVAL); + return EINVAL; + } + + error = access_check_int(&(file_ctx->file_stats), + flags, + uid, + gid, + mode, + mask); + + TRACE_FLOW_EXIT(); + return error; + +} + /* Determines if two file contexts are different by comparing: * - time stamp * - device ID -- 1.7.1
>From d91b8bd98854a00448c021a42a551e5700a6ab82 Mon Sep 17 00:00:00 2001 From: Dmitri Pal <d...@dpal.csb> Date: Sun, 7 Sep 2014 22:53:19 +0200 Subject: [PATCH 5/6] [INI] Test file for unit test --- ini/ini.d/merge.validator | 60 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 60 insertions(+), 0 deletions(-) create mode 100644 ini/ini.d/merge.validator diff --git a/ini/ini.d/merge.validator b/ini/ini.d/merge.validator new file mode 100644 index 0000000..dc6d6aa --- /dev/null +++ b/ini/ini.d/merge.validator @@ -0,0 +1,60 @@ +File merge.validator did not match provided patterns. Skipping. +File real8.conf did not match provided patterns. Skipping. +File new_line.conf did not match provided patterns. Skipping. +File real32be.conf did not match provided patterns. Skipping. +File real32le.conf did not match provided patterns. Skipping. +File real16be.conf did not match provided patterns. Skipping. +File real16le.conf did not match provided patterns. Skipping. +File foo.conf.in did not match provided patterns. Skipping. +Errors detected while parsing: %s%s/comment.conf. +Error (9) on line 22: Invalid space character at the beginning of the line. +Error (9) on line 24: Invalid space character at the beginning of the line. +Error (9) on line 26: Invalid space character at the beginning of the line. +Error (15) on line 32: Incomplete comment at the end of the file. +No sections found in file %s%s/comment.conf. Skipping. +Section [section_a] found in file %s%s/first.conf is not allowed. +Section [section_c] found in file %s%s/first.conf is not allowed. +Section [section_b] found in file %s%s/first.conf is not allowed. +File %s%s/first.conf contains sections that are not allowed. Skipping. +Section [section_a] found in file %s%s/mergecheck.conf is not allowed. +Section [section_c] found in file %s%s/mergecheck.conf is not allowed. +Section [section_b] found in file %s%s/mergecheck.conf is not allowed. +Section [section_d] found in file %s%s/mergecheck.conf is not allowed. +File %s%s/mergecheck.conf contains sections that are not allowed. Skipping. +Section [service] found in file %s%s/mysssd.conf is not allowed. +Section [sssd] found in file %s%s/mysssd.conf is not allowed. +Section [nss] found in file %s%s/mysssd.conf is not allowed. +Section [pam] found in file %s%s/mysssd.conf is not allowed. +Section [domain] found in file %s%s/mysssd.conf is not allowed. +File %s%s/mysssd.conf contains sections that are not allowed. Skipping. +Section [section_a] found in file %s%s/second.conf is not allowed. +Section [section_b] found in file %s%s/second.conf is not allowed. +Section [section_d] found in file %s%s/second.conf is not allowed. +File %s%s/second.conf contains sections that are not allowed. Skipping. +Section [section1] found in file %s%s/sexpect.conf is not allowed. +Section [section2] found in file %s%s/sexpect.conf is not allowed. +File %s%s/sexpect.conf contains sections that are not allowed. Skipping. +Section [section1] found in file %s%s/smerge.conf is not allowed. +Section [section2] found in file %s%s/smerge.conf is not allowed. +File %s%s/smerge.conf contains sections that are not allowed. Skipping. +Errors detected while parsing: %s%s/space.conf. +Error (9) on line 1: Invalid space character at the beginning of the line. +Error (9) on line 2: Invalid space character at the beginning of the line. +Error (9) on line 3: Invalid space character at the beginning of the line. +Error (9) on line 4: Invalid space character at the beginning of the line. +No sections found in file %s%s/space.conf. Skipping. +Section [info] found in file %s%s/symbols.conf is not allowed. +Section [languages] found in file %s%s/symbols.conf is not allowed. +Section [text] found in file %s%s/symbols.conf is not allowed. +File %s%s/symbols.conf contains sections that are not allowed. Skipping. +Errors detected while parsing: %s%s/test.conf. +Error (9) on line 11: Invalid space character at the beginning of the line. +Error (9) on line 12: Invalid space character at the beginning of the line. +Error (9) on line 14: Invalid space character at the beginning of the line. +Error (9) on line 15: Invalid space character at the beginning of the line. +Error (9) on line 16: Invalid space character at the beginning of the line. +Error (9) on line 26: Invalid space character at the beginning of the line. +Error (9) on line 35: Invalid space character at the beginning of the line. +No sections found in file %s%s/test.conf. Skipping. +%s%s/ipa.conf +%s%s/real.conf -- 1.7.1
>From aff0f323b2996df7a8f1539f46ca651d2d9fd5e8 Mon Sep 17 00:00:00 2001 From: Dmitri Pal <d...@dpal.csb> Date: Sun, 7 Sep 2014 16:38:19 +0200 Subject: [PATCH 1/6] [RA] Print info when array is empty --- refarray/ref_array.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/refarray/ref_array.c b/refarray/ref_array.c index 273ac4f..e5becf9 100644 --- a/refarray/ref_array.c +++ b/refarray/ref_array.c @@ -548,6 +548,11 @@ void ref_array_debug(struct ref_array *ra, int num) { int i,j; + if (!ra) { + printf("\nARRAY is NULL\n"); + return; + } + printf("\nARRAY DUMP START\n"); printf("Length = %u\n", ra->len); printf("Size = %u\n", ra->size); -- 1.7.1
_______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/sssd-devel