aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libstrongswan/tests/suites/test_settings.c34
-rw-r--r--src/libstrongswan/utils/settings.c185
-rw-r--r--src/libstrongswan/utils/settings.h13
3 files changed, 187 insertions, 45 deletions
diff --git a/src/libstrongswan/tests/suites/test_settings.c b/src/libstrongswan/tests/suites/test_settings.c
index b97a70656..096465191 100644
--- a/src/libstrongswan/tests/suites/test_settings.c
+++ b/src/libstrongswan/tests/suites/test_settings.c
@@ -714,14 +714,23 @@ START_TEST(test_add_fallback)
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);
+ keys = linked_list_create_with_items("sub1", NULL);
+ verify_sections(keys, "main");
+ keys = linked_list_create_with_items("subsub", 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");
+
+ keys = linked_list_create_with_items("key1", "key2", NULL);
+ values = linked_list_create_with_items("val1", "subval2", NULL);
verify_key_values(keys, values, "main.sub1");
+ keys = linked_list_create_with_items("subkey1", NULL);
+ values = linked_list_create_with_items("subsubval1", NULL);
+ verify_key_values(keys, values, "main.sub1.subsub");
+
settings->add_fallback(settings, "main", "base");
verify_string("val1", "main.key1");
verify_string("baseval2", "main.key2");
@@ -732,18 +741,23 @@ START_TEST(test_add_fallback)
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);
+
+ keys = linked_list_create_with_items("sub1", "sub2", NULL);
verify_sections(keys, "main");
+ keys = linked_list_create_with_items("subsub", NULL);
+ verify_sections(keys, "main.sub1");
- keys = linked_list_create_with_items("key1", NULL);
- values = linked_list_create_with_items("val1", NULL);
+ keys = linked_list_create_with_items("key1", "key2", NULL);
+ values = linked_list_create_with_items("val1", "baseval2", NULL);
+ verify_key_values(keys, values, "main");
+
+ keys = linked_list_create_with_items("key1", "key2", "key3", NULL);
+ values = linked_list_create_with_items("val1", "subval2", "subbase3", 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");
+ keys = linked_list_create_with_items("subkey1", "subkey2", NULL);
+ values = linked_list_create_with_items("subsubval1", "subsubbaseval2", NULL);
+ verify_key_values(keys, values, "main.sub1.subsub");
settings->set_str(settings, "main.sub1.key2", "val2");
verify_string("val2", "main.sub1.key2");
diff --git a/src/libstrongswan/utils/settings.c b/src/libstrongswan/utils/settings.c
index ce098c995..a2c892211 100644
--- a/src/libstrongswan/utils/settings.c
+++ b/src/libstrongswan/utils/settings.c
@@ -32,6 +32,7 @@
#include "settings.h"
#include "collections/array.h"
+#include "collections/hashtable.h"
#include "collections/linked_list.h"
#include "threading/rwlock.h"
#include "utils/debug.h"
@@ -307,23 +308,67 @@ static section_t *find_section_buffered(section_t *section,
}
/**
- * Find a section by a given key (thread-safe).
+ * Find all sections via a given key considering fallbacks, using buffered key,
+ * reusable buffer.
*/
-static section_t *find_section(private_settings_t *this, section_t *section,
- char *key, va_list args)
+static void find_sections_buffered(section_t *section, char *start, char *key,
+ va_list args, char *buf, int len, array_t **sections)
{
- char buf[128], keybuf[512];
- section_t *found;
+ section_t *found = NULL, *fallback;
+ char *pos;
+ int i;
- if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
+ if (!section)
{
- return NULL;
+ return;
+ }
+ pos = strchr(key, '.');
+ if (pos)
+ {
+ *pos = '\0';
+ }
+ if (!print_key(buf, len, start, key, args))
+ {
+ return;
+ }
+ if (pos)
+ { /* restore so we can follow fallbacks */
+ *pos = '.';
+ }
+ if (!strlen(buf))
+ {
+ found = section;
+ }
+ else
+ {
+ array_bsearch(section->sections, buf, section_find, &found);
+ }
+ if (found)
+ {
+ if (pos)
+ {
+ find_sections_buffered(found, start, pos+1, args, buf, len,
+ sections);
+ }
+ else
+ {
+ array_insert_create(sections, ARRAY_TAIL, found);
+ for (i = 0; i < array_count(found->fallbacks); i++)
+ {
+ array_get(found->fallbacks, i, &fallback);
+ array_insert_create(sections, ARRAY_TAIL, fallback);
+ }
+ }
+ }
+ if (section->fallbacks)
+ {
+ for (i = 0; i < array_count(section->fallbacks); i++)
+ {
+ array_get(section->fallbacks, i, &fallback);
+ find_sections_buffered(fallback, start, key, args, buf, len,
+ sections);
+ }
}
- this->lock->read_lock(this->lock);
- found = find_section_buffered(section, keybuf, keybuf, args, buf,
- sizeof(buf), FALSE);
- this->lock->unlock(this->lock);
- return found;
}
/**
@@ -348,6 +393,26 @@ static section_t *ensure_section(private_settings_t *this, section_t *section,
}
/**
+ * Find a section by a given key with its fallbacks (not thread-safe!).
+ * Sections are returned in depth-first order (array is allocated). NULL is
+ * returned if no sections are found.
+ */
+static array_t *find_sections(private_settings_t *this, section_t *section,
+ char *key, va_list args)
+{
+ char buf[128], keybuf[512];
+ array_t *sections = NULL;
+
+ if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
+ {
+ return NULL;
+ }
+ find_sections_buffered(section, keybuf, keybuf, args, buf,
+ sizeof(buf), &sections);
+ return sections;
+}
+
+/**
* Check if the given fallback section already exists
*/
static bool fallback_exists(section_t *section, section_t *fallback)
@@ -786,63 +851,127 @@ METHOD(settings_t, set_default_str, bool,
}
/**
+ * Data for enumerators
+ */
+typedef struct {
+ /** settings_t instance */
+ private_settings_t *settings;
+ /** sections to enumerate */
+ array_t *sections;
+ /** sections/keys that were already enumerated */
+ hashtable_t *seen;
+} enumerator_data_t;
+
+/**
+ * Destroy enumerator data
+ */
+static void enumerator_destroy(enumerator_data_t *this)
+{
+ this->settings->lock->unlock(this->settings->lock);
+ this->seen->destroy(this->seen);
+ array_destroy(this->sections);
+ free(this);
+}
+
+/**
* Enumerate section names, not sections
*/
-static bool section_filter(void *null, section_t **in, char **out)
+static bool section_filter(hashtable_t *seen, section_t **in, char **out)
{
*out = (*in)->name;
+ if (seen->get(seen, *out))
+ {
+ return FALSE;
+ }
+ seen->put(seen, *out, *out);
return TRUE;
}
+/**
+ * Enumerate sections of the given section
+ */
+static enumerator_t *section_enumerator(section_t *section,
+ enumerator_data_t *data)
+{
+ return enumerator_create_filter(array_create_enumerator(section->sections),
+ (void*)section_filter, data->seen, NULL);
+}
+
METHOD(settings_t, create_section_enumerator, enumerator_t*,
private_settings_t *this, char *key, ...)
{
- section_t *section;
+ enumerator_data_t *data;
+ array_t *sections;
va_list args;
+ this->lock->read_lock(this->lock);
va_start(args, key);
- section = find_section(this, this->top, key, args);
+ sections = find_sections(this, this->top, key, args);
va_end(args);
- if (!section)
+ if (!sections)
{
+ this->lock->unlock(this->lock);
return enumerator_create_empty();
}
- this->lock->read_lock(this->lock);
- return enumerator_create_filter(
- array_create_enumerator(section->sections),
- (void*)section_filter, this->lock, (void*)this->lock->unlock);
+ INIT(data,
+ .settings = this,
+ .sections = sections,
+ .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
+ );
+ return enumerator_create_nested(array_create_enumerator(sections),
+ (void*)section_enumerator, data, (void*)enumerator_destroy);
}
/**
* Enumerate key and values, not kv_t entries
*/
-static bool kv_filter(void *null, kv_t **in, char **key,
+static bool kv_filter(hashtable_t *seen, kv_t **in, char **key,
void *none, char **value)
{
*key = (*in)->key;
+ if (seen->get(seen, *key))
+ {
+ return FALSE;
+ }
*value = (*in)->value;
+ seen->put(seen, *key, *key);
return TRUE;
}
+/**
+ * Enumerate key/value pairs of the given section
+ */
+static enumerator_t *kv_enumerator(section_t *section, enumerator_data_t *data)
+{
+ return enumerator_create_filter(array_create_enumerator(section->kv),
+ (void*)kv_filter, data->seen, NULL);
+}
+
METHOD(settings_t, create_key_value_enumerator, enumerator_t*,
private_settings_t *this, char *key, ...)
{
- section_t *section;
+ enumerator_data_t *data;
+ array_t *sections;
va_list args;
+ this->lock->read_lock(this->lock);
va_start(args, key);
- section = find_section(this, this->top, key, args);
+ sections = find_sections(this, this->top, key, args);
va_end(args);
- if (!section)
+ if (!sections)
{
+ this->lock->unlock(this->lock);
return enumerator_create_empty();
}
- this->lock->read_lock(this->lock);
- return enumerator_create_filter(
- array_create_enumerator(section->kv),
- (void*)kv_filter, this->lock, (void*)this->lock->unlock);
+ INIT(data,
+ .settings = this,
+ .sections = sections,
+ .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
+ );
+ return enumerator_create_nested(array_create_enumerator(sections),
+ (void*)kv_enumerator, data, (void*)enumerator_destroy);
}
METHOD(settings_t, add_fallback, void,
diff --git a/src/libstrongswan/utils/settings.h b/src/libstrongswan/utils/settings.h
index 6154ad8b9..46403c4d3 100644
--- a/src/libstrongswan/utils/settings.h
+++ b/src/libstrongswan/utils/settings.h
@@ -276,13 +276,12 @@ struct settings_t {
* '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 Lookups are depth-first and currently strictly top-down.
+ * For instance, if app.sec had lib1.sec as fallback and lib1 had lib2 as
+ * fallback the keys/sections in lib2.sec would not be considered. But if
+ * app had lib3 as fallback the contents of lib3.sec would (as app is passed
+ * during the initial lookup). In the last example the order during
+ * enumerations would be app.sec, lib1.sec, lib3.sec.
*
* @note Additional arguments will be applied to both section format
* strings so they must be compatible.