From: Tobias Brunner Date: Fri, 7 Mar 2014 17:20:28 +0000 (+0100) Subject: settings: Use generated parser instead of our own X-Git-Tag: 5.2.0dr4~1^2~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=47a3ed979beef405bdf2627c5a1f8c5df37ce0ff;p=thirdparty%2Fstrongswan.git settings: Use generated parser instead of our own --- diff --git a/src/libstrongswan/settings/settings.c b/src/libstrongswan/settings/settings.c index acfa7c1ea9..25c5d6ea43 100644 --- a/src/libstrongswan/settings/settings.c +++ b/src/libstrongswan/settings/settings.c @@ -25,6 +25,7 @@ #include #include "settings.h" +#include "settings_types.h" #include "collections/array.h" #include "collections/hashtable.h" @@ -32,188 +33,71 @@ #include "threading/rwlock.h" #include "utils/debug.h" -#define MAX_INCLUSION_LEVEL 10 - typedef struct private_settings_t private_settings_t; -typedef struct section_t section_t; -typedef struct kv_t kv_t; /** - * private data of settings + * Parse function provided by the generated parser. + */ +bool settings_parser_parse_file(section_t *root, char *name); + +/** + * Private data of settings */ struct private_settings_t { /** - * public functions + * Public interface */ settings_t public; /** - * top level section + * Top level section */ section_t *top; /** - * contents of loaded files and in-memory settings (char*) + * Contents of replaced settings (char*) + * + * FIXME: This is required because the pointer returned by get_str() + * is not refcounted. Might cause ever increasing usage stats. */ - linked_list_t *contents; + array_t *contents; /** - * lock to safely access the settings + * Lock to safely access the settings */ rwlock_t *lock; }; -/** - * section containing subsections and key value pairs - */ -struct section_t { - - /** - * name of the section - */ - char *name; - - /** - * fallback sections, as section_t - */ - array_t *fallbacks; - - /** - * subsections, as section_t - */ - array_t *sections; - - /** - * key value pairs, as kv_t - */ - array_t *kv; -}; - -/** - * Key value pair - */ -struct kv_t { - - /** - * key string, relative - */ - char *key; - - /** - * value as string - */ - char *value; -}; - -/** - * create a key/value pair - */ -static kv_t *kv_create(char *key, char *value) -{ - kv_t *this; - INIT(this, - .key = strdup(key), - .value = value, - ); - return this; -} - -/** - * destroy a key/value pair - */ -static void kv_destroy(kv_t *this) +static void kv_destroy(kv_t *kv, int idx, array_t *contents) { - free(this->key); - free(this); -} - -/** - * create a section with the given name - */ -static section_t *section_create(char *name) -{ - section_t *this; - INIT(this, - .name = strdupnull(name), - ); - return this; -} - -/** - * destroy a section - */ -static void section_destroy(section_t *this) -{ - array_destroy_function(this->sections, (void*)section_destroy, NULL); - array_destroy_function(this->kv, (void*)kv_destroy, NULL); - array_destroy(this->fallbacks); - free(this->name); - free(this); + settings_kv_destroy(kv, contents); } /** * Purge contents of a section, returns if section can be safely removed. */ -static bool section_purge(section_t *this) +static bool section_purge(section_t *this, array_t *contents) { section_t *current; int i; - array_destroy_function(this->kv, (void*)kv_destroy, NULL); + array_destroy_function(this->kv, (void*)kv_destroy, contents); this->kv = NULL; /* we ensure sections used as fallback, or configured with fallbacks (or * having any such subsections) are not removed */ for (i = array_count(this->sections) - 1; i >= 0; i--) { array_get(this->sections, i, ¤t); - if (section_purge(current)) + if (section_purge(current, contents)) { array_remove(this->sections, i, NULL); - section_destroy(current); + settings_section_destroy(current, contents); } } return !this->fallbacks && !array_count(this->sections); } -/** - * callback to find a section by name - */ -static int section_find(const void *a, const void *b) -{ - const char *key = a; - const section_t *item = b; - return strcmp(key, item->name); -} - -/** - * callback to sort sections by name - */ -static int section_sort(const void *a, const void *b, void *user) -{ - const section_t *sa = a, *sb = b; - return strcmp(sa->name, sb->name); -} - -/** - * callback to find a kv pair by key - */ -static int kv_find(const void *a, const void *b) -{ - const char *key = a; - const kv_t *item = b; - return strcmp(key, item->key); -} - -/** - * callback to sort kv pairs by key - */ -static int kv_sort(const void *a, const void *b, void *user) -{ - const kv_t *kva = a, *kvb = b; - return strcmp(kva->key, kvb->key); -} - /** * Print a format key, but consume already processed arguments */ @@ -286,13 +170,14 @@ static section_t *find_section_buffered(section_t *section, { found = section; } - else if (array_bsearch(section->sections, buf, section_find, &found) == -1) + else if (array_bsearch(section->sections, buf, settings_section_find, + &found) == -1) { if (ensure) { - found = section_create(buf); + found = settings_section_create(strdup(buf)); array_insert_create(§ion->sections, ARRAY_TAIL, found); - array_sort(section->sections, section_sort, NULL); + array_sort(section->sections, settings_section_sort, NULL); } } if (found && pos) @@ -336,7 +221,7 @@ static void find_sections_buffered(section_t *section, char *start, char *key, } else { - array_bsearch(section->sections, buf, section_find, &found); + array_bsearch(section->sections, buf, settings_section_find, &found); } if (found) { @@ -497,14 +382,14 @@ static kv_t *find_value_buffered(section_t *section, char *start, char *key, { found = section; } - else if (array_bsearch(section->sections, buf, section_find, + else if (array_bsearch(section->sections, buf, settings_section_find, &found) == -1) { if (ensure) { - found = section_create(buf); + found = settings_section_create(strdup(buf)); array_insert_create(§ion->sections, ARRAY_TAIL, found); - array_sort(section->sections, section_sort, NULL); + array_sort(section->sections, settings_section_sort, NULL); } } if (found) @@ -528,13 +413,13 @@ static kv_t *find_value_buffered(section_t *section, char *start, char *key, { return NULL; } - if (array_bsearch(section->kv, buf, kv_find, &kv) == -1) + if (array_bsearch(section->kv, buf, settings_kv_find, &kv) == -1) { if (ensure) { - kv = kv_create(buf, NULL); + kv = settings_kv_create(strdup(buf), NULL); array_insert_create(§ion->kv, ARRAY_TAIL, kv); - array_sort(section->kv, kv_sort, NULL); + array_sort(section->kv, settings_kv_sort, NULL); } else if (section->fallbacks) { @@ -594,6 +479,10 @@ static void set_value(private_settings_t *this, section_t *section, { if (!value) { + if (kv->value) + { + array_insert(this->contents, ARRAY_TAIL, kv->value); + } kv->value = NULL; } else if (kv->value && (strlen(value) <= strlen(kv->value))) @@ -601,9 +490,12 @@ static void set_value(private_settings_t *this, section_t *section, strcpy(kv->value, value); } else - { /* otherwise clone the string and store it in the cache */ + { /* otherwise clone the string and cache the replaced one */ + if (kv->value) + { + array_insert(this->contents, ARRAY_TAIL, kv->value); + } kv->value = strdup(value); - this->contents->insert_last(this->contents, kv->value); } } this->lock->unlock(this->lock); @@ -985,412 +877,16 @@ METHOD(settings_t, add_fallback, void, va_end(args); } -/** - * parse text, truncate "skip" chars, delimited by term respecting brackets. - * - * Chars in "skip" are truncated at the beginning and the end of the resulting - * token. "term" contains a list of characters to read up to (first match), - * while "br" contains bracket counterparts found in "term" to skip. - */ -static char parse(char **text, char *skip, char *term, char *br, char **token) -{ - char *best = NULL; - char best_term = '\0'; - - /* skip leading chars */ - while (strchr(skip, **text)) - { - (*text)++; - if (!**text) - { - return 0; - } - } - /* mark begin of subtext */ - *token = *text; - while (*term) - { - char *pos = *text; - int level = 1; - - /* find terminator */ - while (*pos) - { - if (*pos == *term) - { - level--; - } - else if (br && *pos == *br) - { - level++; - } - if (level == 0) - { - if (best == NULL || best > pos) - { - best = pos; - best_term = *term; - } - break; - } - pos++; - } - /* try next terminator */ - term++; - if (br) - { - br++; - } - } - if (best) - { - /* update input */ - *text = best; - /* null trailing bytes */ - do - { - *best = '\0'; - best--; - } - while (best >= *token && strchr(skip, *best)); - /* return found terminator */ - return best_term; - } - return 0; -} - -/** - * Check if "text" starts with "pattern". - * Characters in "skip" are skipped first. If found, TRUE is returned and "text" - * is modified to point to the character right after "pattern". - */ -static bool starts_with(char **text, char *skip, char *pattern) -{ - char *pos = *text; - int len = strlen(pattern); - while (strchr(skip, *pos)) - { - pos++; - if (!*pos) - { - return FALSE; - } - } - if (strlen(pos) < len || !strneq(pos, pattern, len)) - { - return FALSE; - } - *text = pos + len; - return TRUE; -} - -/** - * Check if what follows in "text" is an include statement. - * If this function returns TRUE, "text" will point to the character right after - * the include pattern, which is returned in "pattern". - */ -static bool parse_include(char **text, char **pattern) -{ - char *pos = *text; - if (!starts_with(&pos, "\n\t ", "include")) - { - return FALSE; - } - if (starts_with(&pos, "\t ", "=")) - { /* ignore "include = value" */ - return FALSE; - } - *text = pos; - return parse(text, "\t ", "\n", NULL, pattern) != 0; -} - -/** - * Forward declaration. - */ -static bool parse_files(linked_list_t *contents, char *file, int level, - char *pattern, section_t *section); - -/** - * Parse a section - */ -static bool parse_section(linked_list_t *contents, char *file, int level, - char **text, section_t *section) -{ - bool finished = FALSE; - char *key, *value, *inner; - - while (!finished) - { - if (parse_include(text, &value)) - { - if (!parse_files(contents, file, level, value, section)) - { - DBG1(DBG_LIB, "failed to include '%s'", value); - return FALSE; - } - continue; - } - switch (parse(text, "\t\n ", "{=#", NULL, &key)) - { - case '{': - if (parse(text, "\t ", "}", "{", &inner)) - { - section_t *sub; - if (!strlen(key)) - { - DBG1(DBG_LIB, "skipping section without name in '%s'", - section->name); - continue; - } - if (array_bsearch(section->sections, key, section_find, - &sub) == -1) - { - sub = section_create(key); - if (parse_section(contents, file, level, &inner, sub)) - { - array_insert_create(§ion->sections, ARRAY_TAIL, - sub); - array_sort(section->sections, section_sort, NULL); - continue; - } - section_destroy(sub); - } - else - { /* extend the existing section */ - if (parse_section(contents, file, level, &inner, sub)) - { - continue; - } - } - DBG1(DBG_LIB, "parsing subsection '%s' failed", key); - break; - } - DBG1(DBG_LIB, "matching '}' not found near %s", *text); - break; - case '=': - if (parse(text, "\t ", "\n", NULL, &value)) - { - kv_t *kv; - if (!strlen(key)) - { - DBG1(DBG_LIB, "skipping value without key in '%s'", - section->name); - continue; - } - if (array_bsearch(section->kv, key, kv_find, &kv) == -1) - { - kv = kv_create(key, value); - array_insert_create(§ion->kv, ARRAY_TAIL, kv); - array_sort(section->kv, kv_sort, NULL); - } - else - { /* replace with the most recently read value */ - kv->value = value; - } - continue; - } - DBG1(DBG_LIB, "parsing value failed near %s", *text); - break; - case '#': - parse(text, "", "\n", NULL, &value); - continue; - default: - finished = TRUE; - continue; - } - return FALSE; - } - return TRUE; -} - -/** - * Parse a file and add the settings to the given section. - */ -static bool parse_file(linked_list_t *contents, char *file, int level, - section_t *section) -{ - bool success; - char *text, *pos; - struct stat st; - FILE *fd; - int len; - - DBG2(DBG_LIB, "loading config file '%s'", file); - if (stat(file, &st) == -1) - { - if (errno == ENOENT) - { -#ifdef STRONGSWAN_CONF - if (streq(file, STRONGSWAN_CONF)) - { - DBG2(DBG_LIB, "'%s' does not exist, ignored", file); - } - else -#endif - { - DBG1(DBG_LIB, "'%s' does not exist, ignored", file); - } - return TRUE; - } - DBG1(DBG_LIB, "failed to stat '%s': %s", file, strerror(errno)); - return FALSE; - } - else if (!S_ISREG(st.st_mode)) - { - DBG1(DBG_LIB, "'%s' is not a regular file", file); - return FALSE; - } - fd = fopen(file, "r"); - if (fd == NULL) - { - DBG1(DBG_LIB, "'%s' is not readable", file); - return FALSE; - } - fseek(fd, 0, SEEK_END); - len = ftell(fd); - rewind(fd); - text = malloc(len + 2); - text[len] = text[len + 1] = '\0'; - if (fread(text, 1, len, fd) != len) - { - free(text); - fclose(fd); - return FALSE; - } - fclose(fd); - - pos = text; - success = parse_section(contents, file, level, &pos, section); - if (!success) - { - free(text); - } - else - { - contents->insert_last(contents, text); - } - return success; -} - -/** - * Load the files matching "pattern", which is resolved with glob(3), if - * available. - * If the pattern is relative, the directory of "file" is used as base. - */ -static bool parse_files(linked_list_t *contents, char *file, int level, - char *pattern, section_t *section) -{ - enumerator_t *enumerator; - bool success = TRUE; - char pat[PATH_MAX], *expanded; - - if (level > MAX_INCLUSION_LEVEL) - { - DBG1(DBG_LIB, "maximum level of %d includes reached, ignored", - MAX_INCLUSION_LEVEL); - return TRUE; - } - - if (!strlen(pattern)) - { - DBG1(DBG_LIB, "empty include pattern, ignored"); - return TRUE; - } - - if (!file || pattern[0] == '/') - { /* absolute path */ - if (snprintf(pat, sizeof(pat), "%s", pattern) >= sizeof(pat)) - { - DBG1(DBG_LIB, "include pattern too long, ignored"); - return TRUE; - } - } - else - { /* base relative paths to the directory of the current file */ - char *dir = path_dirname(file); - if (snprintf(pat, sizeof(pat), "%s/%s", dir, pattern) >= sizeof(pat)) - { - DBG1(DBG_LIB, "include pattern too long, ignored"); - free(dir); - return TRUE; - } - free(dir); - } - enumerator = enumerator_create_glob(pat); - if (enumerator) - { - while (enumerator->enumerate(enumerator, &expanded, NULL)) - { - success &= parse_file(contents, expanded, level + 1, section); - if (!success) - { - break; - } - } - enumerator->destroy(enumerator); - } - else - { /* if glob(3) is not available, try to load pattern directly */ - success = parse_file(contents, pat, level + 1, section); - } - return success; -} - -/** - * Recursivly extends "base" with "extension". - */ -static void section_extend(section_t *base, section_t *extension) -{ - enumerator_t *enumerator; - section_t *sec; - kv_t *kv; - - enumerator = array_create_enumerator(extension->sections); - while (enumerator->enumerate(enumerator, (void**)&sec)) - { - section_t *found; - if (array_bsearch(base->sections, sec->name, section_find, - &found) != -1) - { - section_extend(found, sec); - } - else - { - array_remove_at(extension->sections, enumerator); - array_insert_create(&base->sections, ARRAY_TAIL, sec); - array_sort(base->sections, section_sort, NULL); - } - } - enumerator->destroy(enumerator); - - enumerator = array_create_enumerator(extension->kv); - while (enumerator->enumerate(enumerator, (void**)&kv)) - { - kv_t *found; - if (array_bsearch(base->kv, kv->key, kv_find, &found) != -1) - { - found->value = kv->value; - } - else - { - array_remove_at(extension->kv, enumerator); - array_insert_create(&base->kv, ARRAY_TAIL, kv); - array_sort(base->kv, kv_sort, NULL); - } - } - enumerator->destroy(enumerator); -} - /** * Load settings from files matching the given file pattern. * All sections and values are added relative to "parent". * All files (even included ones) have to be loaded successfully. + * If merge is FALSE the contents of parent are replaced with the parsed + * contents, otherwise they are merged together. */ static bool load_files_internal(private_settings_t *this, section_t *parent, char *pattern, bool merge) { - char *text; - linked_list_t *contents; section_t *section; if (pattern == NULL) @@ -1402,32 +898,23 @@ static bool load_files_internal(private_settings_t *this, section_t *parent, #endif } - contents = linked_list_create(); - section = section_create(NULL); - - if (!parse_files(contents, NULL, 0, pattern, section)) + section = settings_section_create(NULL); + if (!settings_parser_parse_file(section, pattern)) { - contents->destroy_function(contents, (void*)free); - section_destroy(section); + settings_section_destroy(section, NULL); return FALSE; } this->lock->write_lock(this->lock); if (!merge) { - section_purge(parent); + section_purge(parent, this->contents); } /* extend parent section */ - section_extend(parent, section); - /* move contents of loaded files to main store */ - while (contents->remove_first(contents, (void**)&text) == SUCCESS) - { - this->contents->insert_last(this->contents, text); - } + settings_section_extend(parent, section, this->contents); this->lock->unlock(this->lock); - section_destroy(section); - contents->destroy(contents); + settings_section_destroy(section, NULL); return TRUE; } @@ -1457,8 +944,8 @@ METHOD(settings_t, load_files_section, bool, METHOD(settings_t, destroy, void, private_settings_t *this) { - section_destroy(this->top); - this->contents->destroy_function(this->contents, (void*)free); + settings_section_destroy(this->top, NULL); + array_destroy_function(this->contents, (void*)free, NULL); this->lock->destroy(this->lock); free(this); } @@ -1490,8 +977,8 @@ settings_t *settings_create(char *file) .load_files_section = _load_files_section, .destroy = _destroy, }, - .top = section_create(NULL), - .contents = linked_list_create(), + .top = settings_section_create(NULL), + .contents = array_create(0, 0), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), );