diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libstrongswan/tests/suites/test_settings.c | 109 | ||||
-rw-r--r-- | src/libstrongswan/utils/settings.c | 154 | ||||
-rw-r--r-- | src/libstrongswan/utils/settings.h | 26 |
3 files changed, 276 insertions, 13 deletions
diff --git a/src/libstrongswan/tests/suites/test_settings.c b/src/libstrongswan/tests/suites/test_settings.c index 72ee94e86..0172d5265 100644 --- a/src/libstrongswan/tests/suites/test_settings.c +++ b/src/libstrongswan/tests/suites/test_settings.c @@ -664,6 +664,109 @@ START_TEST(test_load_files_section) } END_TEST +START_SETUP(setup_fallback_config) +{ + create_settings(chunk_from_str( + "main {\n" + " key1 = val1\n" + " sub1 {\n" + " key1 = val1\n" + " }\n" + "}\n" + "sub {\n" + " key1 = subval1\n" + " key2 = subval2\n" + " subsub {\n" + " subkey1 = subsubval1\n" + " }\n" + "}\n" + "base {\n" + " key1 = baseval1\n" + " key2 = baseval2\n" + " sub1 {\n" + " key1 = subbase1\n" + " key2 = subbase2\n" + " key3 = subbase3\n" + " subsub {\n" + " subkey1 = subsubbaseval1\n" + " subkey2 = subsubbaseval2\n" + " }\n" + " }\n" + " sub2 {\n" + " key4 = subbase4\n" + " }\n" + "}")); +} +END_SETUP + +START_TEST(test_add_fallback) +{ + linked_list_t *keys, *values; + + settings->add_fallback(settings, "main.sub1", "sub"); + verify_string("val1", "main.sub1.key1"); + verify_string("subval2", "main.sub1.key2"); + verify_string("subsubval1", "main.sub1.subsub.subkey1"); + + /* fallbacks are preserved even if the complete config is replaced */ + settings->load_files(settings, path, FALSE); + verify_string("val1", "main.sub1.key1"); + verify_string("subval2", "main.sub1.key2"); + verify_string("subsubval1", "main.sub1.subsub.subkey1"); + + /* fallbacks currently have no effect on section & key/value enumerators */ + keys = linked_list_create_with_items(NULL); + verify_sections(keys, "main.sub1"); + + keys = linked_list_create_with_items("key1", NULL); + values = linked_list_create_with_items("val1", NULL); + verify_key_values(keys, values, "main.sub1"); + + settings->add_fallback(settings, "main", "base"); + verify_string("val1", "main.key1"); + verify_string("baseval2", "main.key2"); + verify_string("val1", "main.sub1.key1"); + verify_string("subval2", "main.sub1.key2"); + verify_string("subsubval1", "main.sub1.subsub.subkey1"); + verify_string("subsubbaseval2", "main.sub1.subsub.subkey2"); + verify_string("subbase3", "main.sub1.key3"); + verify_string("subbase4", "main.sub2.key4"); + + keys = linked_list_create_with_items(NULL); + verify_sections(keys, "main.sub1"); + keys = linked_list_create_with_items("sub1", NULL); + verify_sections(keys, "main"); + + keys = linked_list_create_with_items("key1", NULL); + values = linked_list_create_with_items("val1", NULL); + verify_key_values(keys, values, "main.sub1"); + + keys = linked_list_create_with_items("key1", NULL); + values = linked_list_create_with_items("val1", NULL); + verify_key_values(keys, values, "main"); + + settings->set_str(settings, "main.sub1.key2", "val2"); + verify_string("val2", "main.sub1.key2"); + settings->set_str(settings, "main.sub1.subsub.subkey2", "val2"); + verify_string("val2", "main.sub1.subsub.subkey2"); + verify_string("subsubval1", "main.sub1.subsub.subkey1"); +} +END_TEST + +START_TEST(test_add_fallback_printf) +{ + settings->add_fallback(settings, "%s.sub1", "sub", "main"); + verify_string("val1", "main.sub1.key1"); + verify_string("subval2", "main.sub1.key2"); + verify_string("subsubval1", "main.sub1.subsub.subkey1"); + + settings->add_fallback(settings, "%s.%s2", "%s.%s1", "main", "sub"); + verify_string("val1", "main.sub2.key1"); + verify_string("subval2", "main.sub2.key2"); + verify_string("subsubval1", "main.sub2.subsub.subkey1"); +} +END_TEST + Suite *settings_suite_create() { Suite *s; @@ -721,5 +824,11 @@ Suite *settings_suite_create() tcase_add_test(tc, test_load_files_section); suite_add_tcase(s, tc); + tc = tcase_create("fallback"); + tcase_add_checked_fixture(tc, setup_fallback_config, teardown_config); + tcase_add_test(tc, test_add_fallback); + tcase_add_test(tc, test_add_fallback_printf); + suite_add_tcase(s, tc); + return s; } diff --git a/src/libstrongswan/utils/settings.c b/src/libstrongswan/utils/settings.c index c3ab52a0e..2727495f0 100644 --- a/src/libstrongswan/utils/settings.c +++ b/src/libstrongswan/utils/settings.c @@ -31,6 +31,7 @@ #include "settings.h" +#include "collections/array.h" #include "collections/linked_list.h" #include "threading/rwlock.h" #include "utils/debug.h" @@ -78,6 +79,11 @@ struct section_t { char *name; /** + * fallback sections, as section_t + */ + array_t *fallbacks; + + /** * subsections, as section_t */ linked_list_t *sections; @@ -147,19 +153,45 @@ static void section_destroy(section_t *this) { this->kv->destroy_function(this->kv, (void*)kv_destroy); this->sections->destroy_function(this->sections, (void*)section_destroy); + array_destroy(this->fallbacks); free(this->name); free(this); } +/* + * forward declaration + */ +static bool section_purge(section_t *this); + +/** + * Check if it is safe to remove the given section. + */ +static bool section_remove(section_t *this) +{ + if (section_purge(this)) + { + return FALSE; + } + section_destroy(this); + return TRUE; +} + /** - * Purge contents of a section + * Purge contents of a section, returns TRUE if section has to be kept due to + * any subsections. */ -static void section_purge(section_t *this) +static bool section_purge(section_t *this) { + int count, removed; + this->kv->destroy_function(this->kv, (void*)kv_destroy); this->kv = linked_list_create(); - this->sections->destroy_function(this->sections, (void*)section_destroy); - this->sections = linked_list_create(); + /* we ensure sections used as fallback, or configured with fallbacks (or + * having any such subsections) are not removed */ + count = this->sections->get_count(this->sections); + removed = this->sections->remove(this->sections, NULL, + (void*)section_remove); + return this->fallbacks || removed < count; } /** @@ -291,7 +323,7 @@ static section_t *find_section(private_settings_t *this, section_t *section, * Ensure that the section with the given key exists (thread-safe). */ static section_t *ensure_section(private_settings_t *this, section_t *section, - char *key, va_list args) + const char *key, va_list args) { char buf[128], keybuf[512]; section_t *found; @@ -309,13 +341,72 @@ static section_t *ensure_section(private_settings_t *this, section_t *section, } /** + * Check if the given fallback section already exists + */ +static bool fallback_exists(section_t *section, section_t *fallback) +{ + if (section == fallback) + { + return TRUE; + } + else if (section->fallbacks) + { + section_t *existing; + int i; + + for (i = 0; i < array_count(section->fallbacks); i++) + { + array_get(section->fallbacks, i, &existing); + if (existing == fallback) + { + return TRUE; + } + } + } + return FALSE; +} + +/** + * Ensure that the section with the given key exists and add the given fallback + * section (thread-safe). + */ +static void add_fallback_to_section(private_settings_t *this, + section_t *section, const char *key, va_list args, + section_t *fallback) +{ + char buf[128], keybuf[512]; + section_t *found; + + if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf)) + { + return; + } + this->lock->write_lock(this->lock); + found = find_section_buffered(section, keybuf, keybuf, args, buf, + sizeof(buf), TRUE); + if (!fallback_exists(found, fallback)) + { + /* to ensure sections referred to as fallback are not purged, we create + * the array there too */ + if (!fallback->fallbacks) + { + fallback->fallbacks = array_create(0, 0); + } + array_insert_create(&found->fallbacks, ARRAY_TAIL, fallback); + } + this->lock->unlock(this->lock); +} + +/** * Find the key/value pair for a key, using buffered key, reusable buffer * If "ensure" is TRUE, the sections (and key/value pair) are created if they * don't exist. + * Fallbacks are only considered if "ensure" is FALSE. */ static kv_t *find_value_buffered(section_t *section, char *start, char *key, va_list args, char *buf, int len, bool ensure) { + int i; char *pos; kv_t *kv = NULL; section_t *found = NULL; @@ -329,12 +420,12 @@ static kv_t *find_value_buffered(section_t *section, char *start, char *key, if (pos) { *pos = '\0'; - pos++; - if (!print_key(buf, len, start, key, args)) { return NULL; } + /* restore so we can retry for fallbacks */ + *pos = '.'; if (!strlen(buf)) { found = section; @@ -343,15 +434,26 @@ static kv_t *find_value_buffered(section_t *section, char *start, char *key, (linked_list_match_t)section_find, (void**)&found, buf) != SUCCESS) { - if (!ensure) + if (ensure) { - return NULL; + found = section_create(buf); + section->sections->insert_last(section->sections, found); + } + } + if (found) + { + kv = find_value_buffered(found, start, pos+1, args, buf, len, + ensure); + } + if (!kv && !ensure && section->fallbacks) + { + for (i = 0; !kv && i < array_count(section->fallbacks); i++) + { + array_get(section->fallbacks, i, &found); + kv = find_value_buffered(found, start, key, args, buf, len, + ensure); } - found = section_create(buf); - section->sections->insert_last(section->sections, found); } - return find_value_buffered(found, start, pos, args, buf, len, - ensure); } else { @@ -367,6 +469,15 @@ static kv_t *find_value_buffered(section_t *section, char *start, char *key, kv = kv_create(buf, NULL); section->kv->insert_last(section->kv, kv); } + else if (section->fallbacks) + { + for (i = 0; !kv && i < array_count(section->fallbacks); i++) + { + array_get(section->fallbacks, i, &found); + kv = find_value_buffered(found, start, key, args, buf, len, + ensure); + } + } } } return kv; @@ -727,6 +838,22 @@ METHOD(settings_t, create_key_value_enumerator, enumerator_t*, (void*)kv_filter, this->lock, (void*)this->lock->unlock); } +METHOD(settings_t, add_fallback, void, + private_settings_t *this, const char *key, const char *fallback, ...) +{ + section_t *section; + va_list args; + + /* find/create the fallback */ + va_start(args, fallback); + section = ensure_section(this, this->top, fallback, args); + va_end(args); + + va_start(args, fallback); + add_fallback_to_section(this, this->top, key, args, section); + va_end(args); +} + /** * parse text, truncate "skip" chars, delimited by term respecting brackets. * @@ -1235,6 +1362,7 @@ settings_t *settings_create(char *file) .set_default_str = _set_default_str, .create_section_enumerator = _create_section_enumerator, .create_key_value_enumerator = _create_key_value_enumerator, + .add_fallback = _add_fallback, .load_files = _load_files, .load_files_section = _load_files_section, .destroy = _destroy, diff --git a/src/libstrongswan/utils/settings.h b/src/libstrongswan/utils/settings.h index df0c534e9..6154ad8b9 100644 --- a/src/libstrongswan/utils/settings.h +++ b/src/libstrongswan/utils/settings.h @@ -269,6 +269,32 @@ struct settings_t { char *section, ...); /** + * Add a fallback for the given section. + * + * Example: When the fallback 'section-two' is configured for + * 'section-one.two' any failed lookup for a section or key in + * 'section-one.two' will result in a lookup for the same section/key + * in 'section-two'. + * + * @note This has only an effect on single value lookups. Enumerators for + * sections and key/value-pairs are not affected. The reason is that it is + * rather tricky to implement such a merge without requiring allocations for + * each lookup. For instance, if charon.tls has libtls as fallback and + * charon has libstrongswan as fallback, the key/value-enumerator for + * charon.tls had to enumerate the libtls and libstrongswan.tls sections + * too, but it would have to keep track of the already enumerated keys. + * + * @note Additional arguments will be applied to both section format + * strings so they must be compatible. + * + * @param section section for which a fallback is configured, printf style + * @param fallback fallback section, printf style + * @param ... argument list for section and fallback + */ + void (*add_fallback)(settings_t *this, const char *section, + const char *fallback, ...); + + /** * Load settings from the files matching the given pattern. * * If merge is TRUE, existing sections are extended, existing values |