pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
-selectors/traffic_selector.c threading/thread.c threading/thread_value.c \
+selectors/traffic_selector.c settings/settings.c \
+threading/thread.c threading/thread_value.c \
threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
-utils/printf_hook/printf_hook_builtin.c utils/settings.c utils/test.c \
-utils/utils/strerror.c
+utils/printf_hook/printf_hook_builtin.c utils/test.c utils/utils/strerror.c
# adding the plugin source files
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
-selectors/traffic_selector.c threading/thread.c threading/thread_value.c \
+selectors/traffic_selector.c settings/settings.c \
+threading/thread.c threading/thread_value.c \
threading/mutex.c threading/semaphore.c threading/rwlock.c threading/spinlock.c \
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
-utils/settings.c utils/test.c \
-utils/utils/strerror.c
+utils/test.c utils/utils/strerror.c
if USE_DEV_HEADERS
strongswan_includedir = ${dev_headers}
plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \
processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h \
processing/scheduler.h processing/watcher.h selectors/traffic_selector.h \
-threading/thread.h threading/thread_value.h \
+settings/settings.h threading/thread.h threading/thread_value.h \
threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \
threading/rwlock.h threading/rwlock_condvar.h threading/lock_profiler.h \
utils/utils.h utils/chunk.h utils/debug.h utils/enum.h utils/identification.h \
utils/lexparser.h utils/optionsfrom.h utils/capabilities.h utils/backtrace.h \
utils/leak_detective.h utils/printf_hook/printf_hook.h \
utils/printf_hook/printf_hook_vstr.h utils/printf_hook/printf_hook_builtin.h \
-utils/settings.h utils/test.h utils/integrity_checker.h \
-utils/utils/strerror.h
+utils/test.h utils/integrity_checker.h utils/utils/strerror.h
endif
library.lo : $(top_builddir)/config.status
#include "utils/capabilities.h"
#include "utils/integrity_checker.h"
#include "utils/leak_detective.h"
-#include "utils/settings.h"
#include "plugins/plugin_loader.h"
+#include "settings/settings.h"
typedef struct library_t library_t;
--- /dev/null
+/*
+ * Copyright (C) 2010-2014 Tobias Brunner
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#endif /* HAVE_GLOB_H */
+
+#include "settings.h"
+
+#include "collections/array.h"
+#include "collections/hashtable.h"
+#include "collections/linked_list.h"
+#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
+ */
+struct private_settings_t {
+
+ /**
+ * public functions
+ */
+ settings_t public;
+
+ /**
+ * top level section
+ */
+ section_t *top;
+
+ /**
+ * contents of loaded files and in-memory settings (char*)
+ */
+ linked_list_t *contents;
+
+ /**
+ * 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)
+{
+ 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);
+}
+
+/**
+ * Purge contents of a section, returns if section can be safely removed.
+ */
+static bool section_purge(section_t *this)
+{
+ section_t *current;
+ int i;
+
+ array_destroy_function(this->kv, (void*)kv_destroy, NULL);
+ 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))
+ {
+ array_remove(this->sections, i, NULL);
+ section_destroy(current);
+ }
+ }
+ 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
+ */
+static bool print_key(char *buf, int len, char *start, char *key, va_list args)
+{
+ va_list copy;
+ char *pos = start;
+ bool res;
+
+ va_copy(copy, args);
+ while (TRUE)
+ {
+ pos = memchr(pos, '%', key - pos);
+ if (!pos)
+ {
+ break;
+ }
+ pos++;
+ switch (*pos)
+ {
+ case 'd':
+ va_arg(copy, int);
+ break;
+ case 's':
+ va_arg(copy, char*);
+ break;
+ case 'N':
+ va_arg(copy, enum_name_t*);
+ va_arg(copy, int);
+ break;
+ case '%':
+ break;
+ default:
+ DBG1(DBG_CFG, "settings with %%%c not supported!", *pos);
+ break;
+ }
+ pos++;
+ }
+ res = vsnprintf(buf, len, key, copy) < len;
+ va_end(copy);
+ return res;
+}
+
+/**
+ * Find a section by a given key, using buffered key, reusable buffer.
+ * If "ensure" is TRUE, the sections are created if they don't exist.
+ */
+static section_t *find_section_buffered(section_t *section,
+ char *start, char *key, va_list args, char *buf, int len,
+ bool ensure)
+{
+ char *pos;
+ section_t *found = NULL;
+
+ if (section == NULL)
+ {
+ return NULL;
+ }
+ pos = strchr(key, '.');
+ if (pos)
+ {
+ *pos = '\0';
+ pos++;
+ }
+ if (!print_key(buf, len, start, key, args))
+ {
+ return NULL;
+ }
+ if (!strlen(buf))
+ {
+ found = section;
+ }
+ else if (array_bsearch(section->sections, buf, section_find, &found) == -1)
+ {
+ if (ensure)
+ {
+ found = section_create(buf);
+ array_insert_create(§ion->sections, ARRAY_TAIL, found);
+ array_sort(section->sections, section_sort, NULL);
+ }
+ }
+ if (found && pos)
+ {
+ return find_section_buffered(found, start, pos, args, buf, len, ensure);
+ }
+ return found;
+}
+
+/**
+ * Find all sections via a given key considering fallbacks, using buffered key,
+ * reusable buffer.
+ */
+static void find_sections_buffered(section_t *section, char *start, char *key,
+ va_list args, char *buf, int len, array_t **sections)
+{
+ section_t *found = NULL, *fallback;
+ char *pos;
+ int i;
+
+ if (!section)
+ {
+ 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);
+ }
+ }
+}
+
+/**
+ * Ensure that the section with the given key exists (thread-safe).
+ */
+static section_t *ensure_section(private_settings_t *this, section_t *section,
+ const char *key, va_list args)
+{
+ char buf[128], keybuf[512];
+ section_t *found;
+
+ if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
+ {
+ return NULL;
+ }
+ /* we might have to change the tree */
+ this->lock->write_lock(this->lock);
+ found = find_section_buffered(section, keybuf, keybuf, args, buf,
+ sizeof(buf), TRUE);
+ this->lock->unlock(this->lock);
+ return found;
+}
+
+/**
+ * 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), §ions);
+ return sections;
+}
+
+/**
+ * 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;
+
+ if (section == NULL)
+ {
+ return NULL;
+ }
+
+ pos = strchr(key, '.');
+ if (pos)
+ {
+ *pos = '\0';
+ if (!print_key(buf, len, start, key, args))
+ {
+ return NULL;
+ }
+ /* restore so we can retry for fallbacks */
+ *pos = '.';
+ if (!strlen(buf))
+ {
+ found = section;
+ }
+ else if (array_bsearch(section->sections, buf, section_find,
+ &found) == -1)
+ {
+ if (ensure)
+ {
+ found = section_create(buf);
+ array_insert_create(§ion->sections, ARRAY_TAIL, found);
+ array_sort(section->sections, section_sort, NULL);
+ }
+ }
+ 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);
+ }
+ }
+ }
+ else
+ {
+ if (!print_key(buf, len, start, key, args))
+ {
+ return NULL;
+ }
+ if (array_bsearch(section->kv, buf, kv_find, &kv) == -1)
+ {
+ if (ensure)
+ {
+ kv = kv_create(buf, NULL);
+ array_insert_create(§ion->kv, ARRAY_TAIL, kv);
+ array_sort(section->kv, kv_sort, NULL);
+ }
+ 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;
+}
+
+/**
+ * Find the string value for a key (thread-safe).
+ */
+static char *find_value(private_settings_t *this, section_t *section,
+ char *key, va_list args)
+{
+ char buf[128], keybuf[512], *value = NULL;
+ kv_t *kv;
+
+ if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
+ {
+ return NULL;
+ }
+ this->lock->read_lock(this->lock);
+ kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
+ FALSE);
+ if (kv)
+ {
+ value = kv->value;
+ }
+ this->lock->unlock(this->lock);
+ return value;
+}
+
+/**
+ * Set a value to a copy of the given string (thread-safe).
+ */
+static void set_value(private_settings_t *this, section_t *section,
+ char *key, va_list args, char *value)
+{
+ char buf[128], keybuf[512];
+ kv_t *kv;
+
+ if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
+ {
+ return;
+ }
+ this->lock->write_lock(this->lock);
+ kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
+ TRUE);
+ if (kv)
+ {
+ if (!value)
+ {
+ kv->value = NULL;
+ }
+ else if (kv->value && (strlen(value) <= strlen(kv->value)))
+ { /* overwrite in-place, if possible */
+ strcpy(kv->value, value);
+ }
+ else
+ { /* otherwise clone the string and store it in the cache */
+ kv->value = strdup(value);
+ this->contents->insert_last(this->contents, kv->value);
+ }
+ }
+ this->lock->unlock(this->lock);
+}
+
+METHOD(settings_t, get_str, char*,
+ private_settings_t *this, char *key, char *def, ...)
+{
+ char *value;
+ va_list args;
+
+ va_start(args, def);
+ value = find_value(this, this->top, key, args);
+ va_end(args);
+ if (value)
+ {
+ return value;
+ }
+ return def;
+}
+
+/**
+ * Described in header
+ */
+inline bool settings_value_as_bool(char *value, bool def)
+{
+ if (value)
+ {
+ if (strcaseeq(value, "1") ||
+ strcaseeq(value, "yes") ||
+ strcaseeq(value, "true") ||
+ strcaseeq(value, "enabled"))
+ {
+ return TRUE;
+ }
+ else if (strcaseeq(value, "0") ||
+ strcaseeq(value, "no") ||
+ strcaseeq(value, "false") ||
+ strcaseeq(value, "disabled"))
+ {
+ return FALSE;
+ }
+ }
+ return def;
+}
+
+METHOD(settings_t, get_bool, bool,
+ private_settings_t *this, char *key, bool def, ...)
+{
+ char *value;
+ va_list args;
+
+ va_start(args, def);
+ value = find_value(this, this->top, key, args);
+ va_end(args);
+ return settings_value_as_bool(value, def);
+}
+
+/**
+ * Described in header
+ */
+inline int settings_value_as_int(char *value, int def)
+{
+ int intval;
+ if (value)
+ {
+ errno = 0;
+ intval = strtol(value, NULL, 10);
+ if (errno == 0)
+ {
+ return intval;
+ }
+ }
+ return def;
+}
+
+METHOD(settings_t, get_int, int,
+ private_settings_t *this, char *key, int def, ...)
+{
+ char *value;
+ va_list args;
+
+ va_start(args, def);
+ value = find_value(this, this->top, key, args);
+ va_end(args);
+ return settings_value_as_int(value, def);
+}
+
+/**
+ * Described in header
+ */
+inline double settings_value_as_double(char *value, double def)
+{
+ double dval;
+ if (value)
+ {
+ errno = 0;
+ dval = strtod(value, NULL);
+ if (errno == 0)
+ {
+ return dval;
+ }
+ }
+ return def;
+}
+
+METHOD(settings_t, get_double, double,
+ private_settings_t *this, char *key, double def, ...)
+{
+ char *value;
+ va_list args;
+
+ va_start(args, def);
+ value = find_value(this, this->top, key, args);
+ va_end(args);
+ return settings_value_as_double(value, def);
+}
+
+/**
+ * Described in header
+ */
+inline u_int32_t settings_value_as_time(char *value, u_int32_t def)
+{
+ char *endptr;
+ u_int32_t timeval;
+ if (value)
+ {
+ errno = 0;
+ timeval = strtoul(value, &endptr, 10);
+ if (errno == 0)
+ {
+ switch (*endptr)
+ {
+ case 'd': /* time in days */
+ timeval *= 24 * 3600;
+ break;
+ case 'h': /* time in hours */
+ timeval *= 3600;
+ break;
+ case 'm': /* time in minutes */
+ timeval *= 60;
+ break;
+ case 's': /* time in seconds */
+ default:
+ break;
+ }
+ return timeval;
+ }
+ }
+ return def;
+}
+
+METHOD(settings_t, get_time, u_int32_t,
+ private_settings_t *this, char *key, u_int32_t def, ...)
+{
+ char *value;
+ va_list args;
+
+ va_start(args, def);
+ value = find_value(this, this->top, key, args);
+ va_end(args);
+ return settings_value_as_time(value, def);
+}
+
+METHOD(settings_t, set_str, void,
+ private_settings_t *this, char *key, char *value, ...)
+{
+ va_list args;
+ va_start(args, value);
+ set_value(this, this->top, key, args, value);
+ va_end(args);
+}
+
+METHOD(settings_t, set_bool, void,
+ private_settings_t *this, char *key, bool value, ...)
+{
+ va_list args;
+ va_start(args, value);
+ set_value(this, this->top, key, args, value ? "1" : "0");
+ va_end(args);
+}
+
+METHOD(settings_t, set_int, void,
+ private_settings_t *this, char *key, int value, ...)
+{
+ char val[16];
+ va_list args;
+ va_start(args, value);
+ if (snprintf(val, sizeof(val), "%d", value) < sizeof(val))
+ {
+ set_value(this, this->top, key, args, val);
+ }
+ va_end(args);
+}
+
+METHOD(settings_t, set_double, void,
+ private_settings_t *this, char *key, double value, ...)
+{
+ char val[64];
+ va_list args;
+ va_start(args, value);
+ if (snprintf(val, sizeof(val), "%f", value) < sizeof(val))
+ {
+ set_value(this, this->top, key, args, val);
+ }
+ va_end(args);
+}
+
+METHOD(settings_t, set_time, void,
+ private_settings_t *this, char *key, u_int32_t value, ...)
+{
+ char val[16];
+ va_list args;
+ va_start(args, value);
+ if (snprintf(val, sizeof(val), "%u", value) < sizeof(val))
+ {
+ set_value(this, this->top, key, args, val);
+ }
+ va_end(args);
+}
+
+METHOD(settings_t, set_default_str, bool,
+ private_settings_t *this, char *key, char *value, ...)
+{
+ char *old;
+ va_list args;
+
+ va_start(args, value);
+ old = find_value(this, this->top, key, args);
+ va_end(args);
+
+ if (!old)
+ {
+ va_start(args, value);
+ set_value(this, this->top, key, args, value);
+ va_end(args);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * 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(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, ...)
+{
+ enumerator_data_t *data;
+ array_t *sections;
+ va_list args;
+
+ this->lock->read_lock(this->lock);
+ va_start(args, key);
+ sections = find_sections(this, this->top, key, args);
+ va_end(args);
+
+ if (!sections)
+ {
+ this->lock->unlock(this->lock);
+ return enumerator_create_empty();
+ }
+ 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(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, ...)
+{
+ enumerator_data_t *data;
+ array_t *sections;
+ va_list args;
+
+ this->lock->read_lock(this->lock);
+ va_start(args, key);
+ sections = find_sections(this, this->top, key, args);
+ va_end(args);
+
+ if (!sections)
+ {
+ this->lock->unlock(this->lock);
+ return enumerator_create_empty();
+ }
+ 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,
+ 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.
+ *
+ * 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)
+{
+ bool success = TRUE;
+ char pat[PATH_MAX];
+
+ 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);
+ }
+#ifdef HAVE_GLOB_H
+ {
+ int status;
+ glob_t buf;
+
+ status = glob(pat, GLOB_ERR, NULL, &buf);
+ if (status == GLOB_NOMATCH)
+ {
+ DBG1(DBG_LIB, "no files found matching '%s', ignored", pat);
+ }
+ else if (status != 0)
+ {
+ DBG1(DBG_LIB, "expanding file pattern '%s' failed", pat);
+ success = FALSE;
+ }
+ else
+ {
+ char **expanded;
+ for (expanded = buf.gl_pathv; *expanded != NULL; expanded++)
+ {
+ success &= parse_file(contents, *expanded, level + 1, section);
+ if (!success)
+ {
+ break;
+ }
+ }
+ }
+ globfree(&buf);
+ }
+#else /* HAVE_GLOB_H */
+ /* if glob(3) is not available, try to load pattern directly */
+ success = parse_file(contents, pat, level + 1, section);
+#endif /* HAVE_GLOB_H */
+ 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.
+ */
+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)
+ {
+#ifdef STRONGSWAN_CONF
+ pattern = STRONGSWAN_CONF;
+#else
+ return FALSE;
+#endif
+ }
+
+ contents = linked_list_create();
+ section = section_create(NULL);
+
+ if (!parse_files(contents, NULL, 0, pattern, section))
+ {
+ contents->destroy_function(contents, (void*)free);
+ section_destroy(section);
+ return FALSE;
+ }
+
+ this->lock->write_lock(this->lock);
+ if (!merge)
+ {
+ section_purge(parent);
+ }
+ /* 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);
+ }
+ this->lock->unlock(this->lock);
+
+ section_destroy(section);
+ contents->destroy(contents);
+ return TRUE;
+}
+
+METHOD(settings_t, load_files, bool,
+ private_settings_t *this, char *pattern, bool merge)
+{
+ return load_files_internal(this, this->top, pattern, merge);
+}
+
+METHOD(settings_t, load_files_section, bool,
+ private_settings_t *this, char *pattern, bool merge, char *key, ...)
+{
+ section_t *section;
+ va_list args;
+
+ va_start(args, key);
+ section = ensure_section(this, this->top, key, args);
+ va_end(args);
+
+ if (!section)
+ {
+ return FALSE;
+ }
+ return load_files_internal(this, section, pattern, merge);
+}
+
+METHOD(settings_t, destroy, void,
+ private_settings_t *this)
+{
+ section_destroy(this->top);
+ this->contents->destroy_function(this->contents, (void*)free);
+ this->lock->destroy(this->lock);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+settings_t *settings_create(char *file)
+{
+ private_settings_t *this;
+
+ INIT(this,
+ .public = {
+ .get_str = _get_str,
+ .get_int = _get_int,
+ .get_double = _get_double,
+ .get_time = _get_time,
+ .get_bool = _get_bool,
+ .set_str = _set_str,
+ .set_int = _set_int,
+ .set_double = _set_double,
+ .set_time = _set_time,
+ .set_bool = _set_bool,
+ .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,
+ },
+ .top = section_create(NULL),
+ .contents = linked_list_create(),
+ .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ );
+
+ load_files(this, file, FALSE);
+
+ return &this->public;
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Brunner
+ * Copyright (C) 2008 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup settings settings
+ * @ingroup libstrongswan
+ *
+ * @defgroup settings_t settings
+ * @{ @ingroup settings
+ */
+
+#ifndef SETTINGS_H_
+#define SETTINGS_H_
+
+typedef struct settings_t settings_t;
+
+#include "utils/utils.h"
+#include "collections/enumerator.h"
+
+/**
+ * Convert a string value returned by a key/value enumerator to a boolean.
+ *
+ * @see settings_t.create_key_value_enumerator()
+ * @see settings_t.get_bool()
+ * @param value the string value
+ * @param def the default value, if value is NULL or invalid
+ */
+bool settings_value_as_bool(char *value, bool def);
+
+/**
+ * Convert a string value returned by a key/value enumerator to an integer.
+ *
+ * @see settings_t.create_key_value_enumerator()
+ * @see settings_t.get_int()
+ * @param value the string value
+ * @param def the default value, if value is NULL or invalid
+ */
+int settings_value_as_int(char *value, int def);
+
+/**
+ * Convert a string value returned by a key/value enumerator to a double.
+ *
+ * @see settings_t.create_key_value_enumerator()
+ * @see settings_t.get_double()
+ * @param value the string value
+ * @param def the default value, if value is NULL or invalid
+ */
+double settings_value_as_double(char *value, double def);
+
+/**
+ * Convert a string value returned by a key/value enumerator to a time value.
+ *
+ * @see settings_t.create_key_value_enumerator()
+ * @see settings_t.get_time()
+ * @param value the string value
+ * @param def the default value, if value is NULL or invalid
+ */
+u_int32_t settings_value_as_time(char *value, u_int32_t def);
+
+/**
+ * Generic configuration options read from a config file.
+ *
+ * The syntax is quite simple:
+ * @code
+ * settings := (section|keyvalue)*
+ * section := name { settings }
+ * keyvalue := key = value\n
+ * @endcode
+ * E.g.:
+ * @code
+ a = b
+ section-one {
+ somevalue = asdf
+ subsection {
+ othervalue = xxx
+ }
+ yetanother = zz
+ }
+ section-two {
+ }
+ @endcode
+ *
+ * The values are accessed using the get() functions using dotted keys, e.g.
+ * section-one.subsection.othervalue
+ *
+ * Currently only a limited set of printf format specifiers are supported
+ * (namely %s, %d and %N, see implementation for details).
+ *
+ * \section includes Including other files
+ * Other files can be included, using the include statement e.g.
+ * @code
+ * include /somepath/subconfig.conf
+ * @endcode
+ * Shell patterns like *.conf are possible.
+ *
+ * If the path is relative, the directory of the file containing the include
+ * statement is used as base.
+ *
+ * Sections loaded from included files extend previously loaded sections,
+ * already existing values are replaced.
+ *
+ * All settings included from files are added relative to the section the
+ * include statement is in.
+ *
+ * The following files result in the same final config as above:
+ *
+ * @code
+ a = b
+ section-one {
+ somevalue = before include
+ include include.conf
+ }
+ include two.conf
+ @endcode
+ * include.conf
+ * @code
+ somevalue = asdf
+ subsection {
+ othervalue = yyy
+ }
+ yetanother = zz
+ @endcode
+ * two.conf
+ * @code
+ section-one {
+ subsection {
+ othervalue = xxx
+ }
+ }
+ section-two {
+ }
+ @endcode
+ */
+struct settings_t {
+
+ /**
+ * Get a settings value as a string.
+ *
+ * @param key key including sections, printf style format
+ * @param def value returned if key not found
+ * @param ... argument list for key
+ * @return value pointing to internal string
+ */
+ char* (*get_str)(settings_t *this, char *key, char *def, ...);
+
+ /**
+ * Get a boolean yes|no, true|false value.
+ *
+ * @param key key including sections, printf style format
+ * @param def value returned if key not found
+ * @param ... argument list for key
+ * @return value of the key
+ */
+ bool (*get_bool)(settings_t *this, char *key, bool def, ...);
+
+ /**
+ * Get an integer value.
+ *
+ * @param key key including sections, printf style format
+ * @param def value returned if key not found
+ * @param ... argument list for key
+ * @return value of the key
+ */
+ int (*get_int)(settings_t *this, char *key, int def, ...);
+
+ /**
+ * Get an double value.
+ *
+ * @param key key including sections, printf style format
+ * @param def value returned if key not found
+ * @param ... argument list for key
+ * @return value of the key
+ */
+ double (*get_double)(settings_t *this, char *key, double def, ...);
+
+ /**
+ * Get a time value.
+ *
+ * @param key key including sections, printf style format
+ * @param def value returned if key not found
+ * @param ... argument list for key
+ * @return value of the key (in seconds)
+ */
+ u_int32_t (*get_time)(settings_t *this, char *key, u_int32_t def, ...);
+
+ /**
+ * Set a string value.
+ *
+ * @param key key including sections, printf style format
+ * @param value value to set (gets cloned)
+ * @param ... argument list for key
+ */
+ void (*set_str)(settings_t *this, char *key, char *value, ...);
+
+ /**
+ * Set a boolean value.
+ *
+ * @param key key including sections, printf style format
+ * @param value value to set
+ * @param ... argument list for key
+ */
+ void (*set_bool)(settings_t *this, char *key, bool value, ...);
+
+ /**
+ * Set an integer value.
+ *
+ * @param key key including sections, printf style format
+ * @param value value to set
+ * @param ... argument list for key
+ */
+ void (*set_int)(settings_t *this, char *key, int value, ...);
+
+ /**
+ * Set an double value.
+ *
+ * @param key key including sections, printf style format
+ * @param value value to set
+ * @param ... argument list for key
+ */
+ void (*set_double)(settings_t *this, char *key, double value, ...);
+
+ /**
+ * Set a time value.
+ *
+ * @param key key including sections, printf style format
+ * @param def value to set
+ * @param ... argument list for key
+ */
+ void (*set_time)(settings_t *this, char *key, u_int32_t value, ...);
+
+ /**
+ * Set a default for string value.
+ *
+ * @param key key including sections, printf style format
+ * @param def value to set if unconfigured
+ * @param ... argument list for key
+ * @return TRUE if a new default value for key has been set
+ */
+ bool (*set_default_str)(settings_t *this, char *key, char *value, ...);
+
+ /**
+ * Create an enumerator over subsection names of a section.
+ *
+ * @param section section including parents, printf style format
+ * @param ... argument list for key
+ * @return enumerator over subsection names
+ */
+ enumerator_t* (*create_section_enumerator)(settings_t *this,
+ char *section, ...);
+
+ /**
+ * Create an enumerator over key/value pairs in a section.
+ *
+ * @param section section name to list key/value pairs of, printf style
+ * @param ... argument list for section
+ * @return enumerator over (char *key, char *value)
+ */
+ enumerator_t* (*create_key_value_enumerator)(settings_t *this,
+ 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 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.
+ *
+ * @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
+ * replaced, by those found in the loaded files. If it is FALSE, existing
+ * sections are purged before reading the new config.
+ *
+ * @note If any of the files matching the pattern fails to load, no settings
+ * are added at all. So, it's all or nothing.
+ *
+ * @param pattern file pattern
+ * @param merge TRUE to merge config with existing values
+ * @return TRUE, if settings were loaded successfully
+ */
+ bool (*load_files)(settings_t *this, char *pattern, bool merge);
+
+ /**
+ * Load settings from the files matching the given pattern.
+ *
+ * If merge is TRUE, existing sections are extended, existing values
+ * replaced, by those found in the loaded files. If it is FALSE, existing
+ * sections are purged before reading the new config.
+ *
+ * All settings are loaded relative to the given section. The section is
+ * created, if it does not yet exist.
+ *
+ * @note If any of the files matching the pattern fails to load, no settings
+ * are added at all. So, it's all or nothing.
+ *
+ * @param pattern file pattern
+ * @param merge TRUE to merge config with existing values
+ * @param section section name of parent section, printf style
+ * @param ... argument list for section
+ * @return TRUE, if settings were loaded successfully
+ */
+ bool (*load_files_section)(settings_t *this, char *pattern, bool merge,
+ char *section, ...);
+
+ /**
+ * Destroy a settings instance.
+ */
+ void (*destroy)(settings_t *this);
+};
+
+/**
+ * Load settings from a file.
+ *
+ * @param file file to read settings from, NULL for default
+ * @return settings object
+ */
+settings_t *settings_create(char *file);
+
+#endif /** SETTINGS_H_ @}*/
#include <unistd.h>
-#include <utils/settings.h>
+#include <settings/settings.h>
#include <utils/chunk.h>
#include <utils/utils.h>
#include <collections/linked_list.h>
+++ /dev/null
-/*
- * Copyright (C) 2010-2014 Tobias Brunner
- * Copyright (C) 2008 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program 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 General Public License
- * for more details.
- */
-
-#define _GNU_SOURCE
-#include <string.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#ifdef HAVE_GLOB_H
-#include <glob.h>
-#endif /* HAVE_GLOB_H */
-
-#include "settings.h"
-
-#include "collections/array.h"
-#include "collections/hashtable.h"
-#include "collections/linked_list.h"
-#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
- */
-struct private_settings_t {
-
- /**
- * public functions
- */
- settings_t public;
-
- /**
- * top level section
- */
- section_t *top;
-
- /**
- * contents of loaded files and in-memory settings (char*)
- */
- linked_list_t *contents;
-
- /**
- * 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)
-{
- 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);
-}
-
-/**
- * Purge contents of a section, returns if section can be safely removed.
- */
-static bool section_purge(section_t *this)
-{
- section_t *current;
- int i;
-
- array_destroy_function(this->kv, (void*)kv_destroy, NULL);
- 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))
- {
- array_remove(this->sections, i, NULL);
- section_destroy(current);
- }
- }
- 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
- */
-static bool print_key(char *buf, int len, char *start, char *key, va_list args)
-{
- va_list copy;
- char *pos = start;
- bool res;
-
- va_copy(copy, args);
- while (TRUE)
- {
- pos = memchr(pos, '%', key - pos);
- if (!pos)
- {
- break;
- }
- pos++;
- switch (*pos)
- {
- case 'd':
- va_arg(copy, int);
- break;
- case 's':
- va_arg(copy, char*);
- break;
- case 'N':
- va_arg(copy, enum_name_t*);
- va_arg(copy, int);
- break;
- case '%':
- break;
- default:
- DBG1(DBG_CFG, "settings with %%%c not supported!", *pos);
- break;
- }
- pos++;
- }
- res = vsnprintf(buf, len, key, copy) < len;
- va_end(copy);
- return res;
-}
-
-/**
- * Find a section by a given key, using buffered key, reusable buffer.
- * If "ensure" is TRUE, the sections are created if they don't exist.
- */
-static section_t *find_section_buffered(section_t *section,
- char *start, char *key, va_list args, char *buf, int len,
- bool ensure)
-{
- char *pos;
- section_t *found = NULL;
-
- if (section == NULL)
- {
- return NULL;
- }
- pos = strchr(key, '.');
- if (pos)
- {
- *pos = '\0';
- pos++;
- }
- if (!print_key(buf, len, start, key, args))
- {
- return NULL;
- }
- if (!strlen(buf))
- {
- found = section;
- }
- else if (array_bsearch(section->sections, buf, section_find, &found) == -1)
- {
- if (ensure)
- {
- found = section_create(buf);
- array_insert_create(§ion->sections, ARRAY_TAIL, found);
- array_sort(section->sections, section_sort, NULL);
- }
- }
- if (found && pos)
- {
- return find_section_buffered(found, start, pos, args, buf, len, ensure);
- }
- return found;
-}
-
-/**
- * Find all sections via a given key considering fallbacks, using buffered key,
- * reusable buffer.
- */
-static void find_sections_buffered(section_t *section, char *start, char *key,
- va_list args, char *buf, int len, array_t **sections)
-{
- section_t *found = NULL, *fallback;
- char *pos;
- int i;
-
- if (!section)
- {
- 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);
- }
- }
-}
-
-/**
- * Ensure that the section with the given key exists (thread-safe).
- */
-static section_t *ensure_section(private_settings_t *this, section_t *section,
- const char *key, va_list args)
-{
- char buf[128], keybuf[512];
- section_t *found;
-
- if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
- {
- return NULL;
- }
- /* we might have to change the tree */
- this->lock->write_lock(this->lock);
- found = find_section_buffered(section, keybuf, keybuf, args, buf,
- sizeof(buf), TRUE);
- this->lock->unlock(this->lock);
- return found;
-}
-
-/**
- * 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), §ions);
- return sections;
-}
-
-/**
- * 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;
-
- if (section == NULL)
- {
- return NULL;
- }
-
- pos = strchr(key, '.');
- if (pos)
- {
- *pos = '\0';
- if (!print_key(buf, len, start, key, args))
- {
- return NULL;
- }
- /* restore so we can retry for fallbacks */
- *pos = '.';
- if (!strlen(buf))
- {
- found = section;
- }
- else if (array_bsearch(section->sections, buf, section_find,
- &found) == -1)
- {
- if (ensure)
- {
- found = section_create(buf);
- array_insert_create(§ion->sections, ARRAY_TAIL, found);
- array_sort(section->sections, section_sort, NULL);
- }
- }
- 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);
- }
- }
- }
- else
- {
- if (!print_key(buf, len, start, key, args))
- {
- return NULL;
- }
- if (array_bsearch(section->kv, buf, kv_find, &kv) == -1)
- {
- if (ensure)
- {
- kv = kv_create(buf, NULL);
- array_insert_create(§ion->kv, ARRAY_TAIL, kv);
- array_sort(section->kv, kv_sort, NULL);
- }
- 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;
-}
-
-/**
- * Find the string value for a key (thread-safe).
- */
-static char *find_value(private_settings_t *this, section_t *section,
- char *key, va_list args)
-{
- char buf[128], keybuf[512], *value = NULL;
- kv_t *kv;
-
- if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
- {
- return NULL;
- }
- this->lock->read_lock(this->lock);
- kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
- FALSE);
- if (kv)
- {
- value = kv->value;
- }
- this->lock->unlock(this->lock);
- return value;
-}
-
-/**
- * Set a value to a copy of the given string (thread-safe).
- */
-static void set_value(private_settings_t *this, section_t *section,
- char *key, va_list args, char *value)
-{
- char buf[128], keybuf[512];
- kv_t *kv;
-
- if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
- {
- return;
- }
- this->lock->write_lock(this->lock);
- kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
- TRUE);
- if (kv)
- {
- if (!value)
- {
- kv->value = NULL;
- }
- else if (kv->value && (strlen(value) <= strlen(kv->value)))
- { /* overwrite in-place, if possible */
- strcpy(kv->value, value);
- }
- else
- { /* otherwise clone the string and store it in the cache */
- kv->value = strdup(value);
- this->contents->insert_last(this->contents, kv->value);
- }
- }
- this->lock->unlock(this->lock);
-}
-
-METHOD(settings_t, get_str, char*,
- private_settings_t *this, char *key, char *def, ...)
-{
- char *value;
- va_list args;
-
- va_start(args, def);
- value = find_value(this, this->top, key, args);
- va_end(args);
- if (value)
- {
- return value;
- }
- return def;
-}
-
-/**
- * Described in header
- */
-inline bool settings_value_as_bool(char *value, bool def)
-{
- if (value)
- {
- if (strcaseeq(value, "1") ||
- strcaseeq(value, "yes") ||
- strcaseeq(value, "true") ||
- strcaseeq(value, "enabled"))
- {
- return TRUE;
- }
- else if (strcaseeq(value, "0") ||
- strcaseeq(value, "no") ||
- strcaseeq(value, "false") ||
- strcaseeq(value, "disabled"))
- {
- return FALSE;
- }
- }
- return def;
-}
-
-METHOD(settings_t, get_bool, bool,
- private_settings_t *this, char *key, bool def, ...)
-{
- char *value;
- va_list args;
-
- va_start(args, def);
- value = find_value(this, this->top, key, args);
- va_end(args);
- return settings_value_as_bool(value, def);
-}
-
-/**
- * Described in header
- */
-inline int settings_value_as_int(char *value, int def)
-{
- int intval;
- if (value)
- {
- errno = 0;
- intval = strtol(value, NULL, 10);
- if (errno == 0)
- {
- return intval;
- }
- }
- return def;
-}
-
-METHOD(settings_t, get_int, int,
- private_settings_t *this, char *key, int def, ...)
-{
- char *value;
- va_list args;
-
- va_start(args, def);
- value = find_value(this, this->top, key, args);
- va_end(args);
- return settings_value_as_int(value, def);
-}
-
-/**
- * Described in header
- */
-inline double settings_value_as_double(char *value, double def)
-{
- double dval;
- if (value)
- {
- errno = 0;
- dval = strtod(value, NULL);
- if (errno == 0)
- {
- return dval;
- }
- }
- return def;
-}
-
-METHOD(settings_t, get_double, double,
- private_settings_t *this, char *key, double def, ...)
-{
- char *value;
- va_list args;
-
- va_start(args, def);
- value = find_value(this, this->top, key, args);
- va_end(args);
- return settings_value_as_double(value, def);
-}
-
-/**
- * Described in header
- */
-inline u_int32_t settings_value_as_time(char *value, u_int32_t def)
-{
- char *endptr;
- u_int32_t timeval;
- if (value)
- {
- errno = 0;
- timeval = strtoul(value, &endptr, 10);
- if (errno == 0)
- {
- switch (*endptr)
- {
- case 'd': /* time in days */
- timeval *= 24 * 3600;
- break;
- case 'h': /* time in hours */
- timeval *= 3600;
- break;
- case 'm': /* time in minutes */
- timeval *= 60;
- break;
- case 's': /* time in seconds */
- default:
- break;
- }
- return timeval;
- }
- }
- return def;
-}
-
-METHOD(settings_t, get_time, u_int32_t,
- private_settings_t *this, char *key, u_int32_t def, ...)
-{
- char *value;
- va_list args;
-
- va_start(args, def);
- value = find_value(this, this->top, key, args);
- va_end(args);
- return settings_value_as_time(value, def);
-}
-
-METHOD(settings_t, set_str, void,
- private_settings_t *this, char *key, char *value, ...)
-{
- va_list args;
- va_start(args, value);
- set_value(this, this->top, key, args, value);
- va_end(args);
-}
-
-METHOD(settings_t, set_bool, void,
- private_settings_t *this, char *key, bool value, ...)
-{
- va_list args;
- va_start(args, value);
- set_value(this, this->top, key, args, value ? "1" : "0");
- va_end(args);
-}
-
-METHOD(settings_t, set_int, void,
- private_settings_t *this, char *key, int value, ...)
-{
- char val[16];
- va_list args;
- va_start(args, value);
- if (snprintf(val, sizeof(val), "%d", value) < sizeof(val))
- {
- set_value(this, this->top, key, args, val);
- }
- va_end(args);
-}
-
-METHOD(settings_t, set_double, void,
- private_settings_t *this, char *key, double value, ...)
-{
- char val[64];
- va_list args;
- va_start(args, value);
- if (snprintf(val, sizeof(val), "%f", value) < sizeof(val))
- {
- set_value(this, this->top, key, args, val);
- }
- va_end(args);
-}
-
-METHOD(settings_t, set_time, void,
- private_settings_t *this, char *key, u_int32_t value, ...)
-{
- char val[16];
- va_list args;
- va_start(args, value);
- if (snprintf(val, sizeof(val), "%u", value) < sizeof(val))
- {
- set_value(this, this->top, key, args, val);
- }
- va_end(args);
-}
-
-METHOD(settings_t, set_default_str, bool,
- private_settings_t *this, char *key, char *value, ...)
-{
- char *old;
- va_list args;
-
- va_start(args, value);
- old = find_value(this, this->top, key, args);
- va_end(args);
-
- if (!old)
- {
- va_start(args, value);
- set_value(this, this->top, key, args, value);
- va_end(args);
- return TRUE;
- }
- return FALSE;
-}
-
-/**
- * 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(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, ...)
-{
- enumerator_data_t *data;
- array_t *sections;
- va_list args;
-
- this->lock->read_lock(this->lock);
- va_start(args, key);
- sections = find_sections(this, this->top, key, args);
- va_end(args);
-
- if (!sections)
- {
- this->lock->unlock(this->lock);
- return enumerator_create_empty();
- }
- 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(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, ...)
-{
- enumerator_data_t *data;
- array_t *sections;
- va_list args;
-
- this->lock->read_lock(this->lock);
- va_start(args, key);
- sections = find_sections(this, this->top, key, args);
- va_end(args);
-
- if (!sections)
- {
- this->lock->unlock(this->lock);
- return enumerator_create_empty();
- }
- 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,
- 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.
- *
- * 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)
-{
- bool success = TRUE;
- char pat[PATH_MAX];
-
- 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);
- }
-#ifdef HAVE_GLOB_H
- {
- int status;
- glob_t buf;
-
- status = glob(pat, GLOB_ERR, NULL, &buf);
- if (status == GLOB_NOMATCH)
- {
- DBG1(DBG_LIB, "no files found matching '%s', ignored", pat);
- }
- else if (status != 0)
- {
- DBG1(DBG_LIB, "expanding file pattern '%s' failed", pat);
- success = FALSE;
- }
- else
- {
- char **expanded;
- for (expanded = buf.gl_pathv; *expanded != NULL; expanded++)
- {
- success &= parse_file(contents, *expanded, level + 1, section);
- if (!success)
- {
- break;
- }
- }
- }
- globfree(&buf);
- }
-#else /* HAVE_GLOB_H */
- /* if glob(3) is not available, try to load pattern directly */
- success = parse_file(contents, pat, level + 1, section);
-#endif /* HAVE_GLOB_H */
- 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.
- */
-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)
- {
-#ifdef STRONGSWAN_CONF
- pattern = STRONGSWAN_CONF;
-#else
- return FALSE;
-#endif
- }
-
- contents = linked_list_create();
- section = section_create(NULL);
-
- if (!parse_files(contents, NULL, 0, pattern, section))
- {
- contents->destroy_function(contents, (void*)free);
- section_destroy(section);
- return FALSE;
- }
-
- this->lock->write_lock(this->lock);
- if (!merge)
- {
- section_purge(parent);
- }
- /* 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);
- }
- this->lock->unlock(this->lock);
-
- section_destroy(section);
- contents->destroy(contents);
- return TRUE;
-}
-
-METHOD(settings_t, load_files, bool,
- private_settings_t *this, char *pattern, bool merge)
-{
- return load_files_internal(this, this->top, pattern, merge);
-}
-
-METHOD(settings_t, load_files_section, bool,
- private_settings_t *this, char *pattern, bool merge, char *key, ...)
-{
- section_t *section;
- va_list args;
-
- va_start(args, key);
- section = ensure_section(this, this->top, key, args);
- va_end(args);
-
- if (!section)
- {
- return FALSE;
- }
- return load_files_internal(this, section, pattern, merge);
-}
-
-METHOD(settings_t, destroy, void,
- private_settings_t *this)
-{
- section_destroy(this->top);
- this->contents->destroy_function(this->contents, (void*)free);
- this->lock->destroy(this->lock);
- free(this);
-}
-
-/*
- * see header file
- */
-settings_t *settings_create(char *file)
-{
- private_settings_t *this;
-
- INIT(this,
- .public = {
- .get_str = _get_str,
- .get_int = _get_int,
- .get_double = _get_double,
- .get_time = _get_time,
- .get_bool = _get_bool,
- .set_str = _set_str,
- .set_int = _set_int,
- .set_double = _set_double,
- .set_time = _set_time,
- .set_bool = _set_bool,
- .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,
- },
- .top = section_create(NULL),
- .contents = linked_list_create(),
- .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
- );
-
- load_files(this, file, FALSE);
-
- return &this->public;
-}
+++ /dev/null
-/*
- * Copyright (C) 2010 Tobias Brunner
- * Copyright (C) 2008 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program 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 General Public License
- * for more details.
- */
-
-/**
- * @defgroup settings settings
- * @{ @ingroup utils
- */
-
-#ifndef SETTINGS_H_
-#define SETTINGS_H_
-
-typedef struct settings_t settings_t;
-
-#include "utils.h"
-#include "collections/enumerator.h"
-
-/**
- * Convert a string value returned by a key/value enumerator to a boolean.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_bool()
- * @param value the string value
- * @param def the default value, if value is NULL or invalid
- */
-bool settings_value_as_bool(char *value, bool def);
-
-/**
- * Convert a string value returned by a key/value enumerator to an integer.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_int()
- * @param value the string value
- * @param def the default value, if value is NULL or invalid
- */
-int settings_value_as_int(char *value, int def);
-
-/**
- * Convert a string value returned by a key/value enumerator to a double.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_double()
- * @param value the string value
- * @param def the default value, if value is NULL or invalid
- */
-double settings_value_as_double(char *value, double def);
-
-/**
- * Convert a string value returned by a key/value enumerator to a time value.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_time()
- * @param value the string value
- * @param def the default value, if value is NULL or invalid
- */
-u_int32_t settings_value_as_time(char *value, u_int32_t def);
-
-/**
- * Generic configuration options read from a config file.
- *
- * The syntax is quite simple:
- * @code
- * settings := (section|keyvalue)*
- * section := name { settings }
- * keyvalue := key = value\n
- * @endcode
- * E.g.:
- * @code
- a = b
- section-one {
- somevalue = asdf
- subsection {
- othervalue = xxx
- }
- yetanother = zz
- }
- section-two {
- }
- @endcode
- *
- * The values are accessed using the get() functions using dotted keys, e.g.
- * section-one.subsection.othervalue
- *
- * Currently only a limited set of printf format specifiers are supported
- * (namely %s, %d and %N, see implementation for details).
- *
- * \section includes Including other files
- * Other files can be included, using the include statement e.g.
- * @code
- * include /somepath/subconfig.conf
- * @endcode
- * Shell patterns like *.conf are possible.
- *
- * If the path is relative, the directory of the file containing the include
- * statement is used as base.
- *
- * Sections loaded from included files extend previously loaded sections,
- * already existing values are replaced.
- *
- * All settings included from files are added relative to the section the
- * include statement is in.
- *
- * The following files result in the same final config as above:
- *
- * @code
- a = b
- section-one {
- somevalue = before include
- include include.conf
- }
- include two.conf
- @endcode
- * include.conf
- * @code
- somevalue = asdf
- subsection {
- othervalue = yyy
- }
- yetanother = zz
- @endcode
- * two.conf
- * @code
- section-one {
- subsection {
- othervalue = xxx
- }
- }
- section-two {
- }
- @endcode
- */
-struct settings_t {
-
- /**
- * Get a settings value as a string.
- *
- * @param key key including sections, printf style format
- * @param def value returned if key not found
- * @param ... argument list for key
- * @return value pointing to internal string
- */
- char* (*get_str)(settings_t *this, char *key, char *def, ...);
-
- /**
- * Get a boolean yes|no, true|false value.
- *
- * @param key key including sections, printf style format
- * @param def value returned if key not found
- * @param ... argument list for key
- * @return value of the key
- */
- bool (*get_bool)(settings_t *this, char *key, bool def, ...);
-
- /**
- * Get an integer value.
- *
- * @param key key including sections, printf style format
- * @param def value returned if key not found
- * @param ... argument list for key
- * @return value of the key
- */
- int (*get_int)(settings_t *this, char *key, int def, ...);
-
- /**
- * Get an double value.
- *
- * @param key key including sections, printf style format
- * @param def value returned if key not found
- * @param ... argument list for key
- * @return value of the key
- */
- double (*get_double)(settings_t *this, char *key, double def, ...);
-
- /**
- * Get a time value.
- *
- * @param key key including sections, printf style format
- * @param def value returned if key not found
- * @param ... argument list for key
- * @return value of the key (in seconds)
- */
- u_int32_t (*get_time)(settings_t *this, char *key, u_int32_t def, ...);
-
- /**
- * Set a string value.
- *
- * @param key key including sections, printf style format
- * @param value value to set (gets cloned)
- * @param ... argument list for key
- */
- void (*set_str)(settings_t *this, char *key, char *value, ...);
-
- /**
- * Set a boolean value.
- *
- * @param key key including sections, printf style format
- * @param value value to set
- * @param ... argument list for key
- */
- void (*set_bool)(settings_t *this, char *key, bool value, ...);
-
- /**
- * Set an integer value.
- *
- * @param key key including sections, printf style format
- * @param value value to set
- * @param ... argument list for key
- */
- void (*set_int)(settings_t *this, char *key, int value, ...);
-
- /**
- * Set an double value.
- *
- * @param key key including sections, printf style format
- * @param value value to set
- * @param ... argument list for key
- */
- void (*set_double)(settings_t *this, char *key, double value, ...);
-
- /**
- * Set a time value.
- *
- * @param key key including sections, printf style format
- * @param def value to set
- * @param ... argument list for key
- */
- void (*set_time)(settings_t *this, char *key, u_int32_t value, ...);
-
- /**
- * Set a default for string value.
- *
- * @param key key including sections, printf style format
- * @param def value to set if unconfigured
- * @param ... argument list for key
- * @return TRUE if a new default value for key has been set
- */
- bool (*set_default_str)(settings_t *this, char *key, char *value, ...);
-
- /**
- * Create an enumerator over subsection names of a section.
- *
- * @param section section including parents, printf style format
- * @param ... argument list for key
- * @return enumerator over subsection names
- */
- enumerator_t* (*create_section_enumerator)(settings_t *this,
- char *section, ...);
-
- /**
- * Create an enumerator over key/value pairs in a section.
- *
- * @param section section name to list key/value pairs of, printf style
- * @param ... argument list for section
- * @return enumerator over (char *key, char *value)
- */
- enumerator_t* (*create_key_value_enumerator)(settings_t *this,
- 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 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.
- *
- * @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
- * replaced, by those found in the loaded files. If it is FALSE, existing
- * sections are purged before reading the new config.
- *
- * @note If any of the files matching the pattern fails to load, no settings
- * are added at all. So, it's all or nothing.
- *
- * @param pattern file pattern
- * @param merge TRUE to merge config with existing values
- * @return TRUE, if settings were loaded successfully
- */
- bool (*load_files)(settings_t *this, char *pattern, bool merge);
-
- /**
- * Load settings from the files matching the given pattern.
- *
- * If merge is TRUE, existing sections are extended, existing values
- * replaced, by those found in the loaded files. If it is FALSE, existing
- * sections are purged before reading the new config.
- *
- * All settings are loaded relative to the given section. The section is
- * created, if it does not yet exist.
- *
- * @note If any of the files matching the pattern fails to load, no settings
- * are added at all. So, it's all or nothing.
- *
- * @param pattern file pattern
- * @param merge TRUE to merge config with existing values
- * @param section section name of parent section, printf style
- * @param ... argument list for section
- * @return TRUE, if settings were loaded successfully
- */
- bool (*load_files_section)(settings_t *this, char *pattern, bool merge,
- char *section, ...);
-
- /**
- * Destroy a settings instance.
- */
- void (*destroy)(settings_t *this);
-};
-
-/**
- * Load settings from a file.
- *
- * @param file file to read settings from, NULL for default
- * @return settings object
- */
-settings_t *settings_create(char *file);
-
-#endif /** SETTINGS_H_ @}*/