aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libstrongswan/tests/suites/test_settings.c109
-rw-r--r--src/libstrongswan/utils/settings.c154
-rw-r--r--src/libstrongswan/utils/settings.h26
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