aboutsummaryrefslogtreecommitdiff
path: root/src/app/config/confparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/config/confparse.c')
-rw-r--r--src/app/config/confparse.c746
1 files changed, 589 insertions, 157 deletions
diff --git a/src/app/config/confparse.c b/src/app/config/confparse.c
index 6e2624466a..f20a361ba3 100644
--- a/src/app/config/confparse.c
+++ b/src/app/config/confparse.c
@@ -37,16 +37,349 @@
#include "lib/string/printf.h"
#include "lib/string/util_string.h"
-static void config_reset(const config_format_t *fmt, void *options,
- const config_var_t *var, int use_defaults);
+#include "ext/siphash.h"
+
+/**
+ * A managed_var_t is an internal wrapper around a config_var_t in
+ * a config_format_t structure. It is used by config_mgr_t to
+ * keep track of which option goes with which structure. */
+typedef struct managed_var_t {
+ /**
+ * A pointer to the config_var_t for this option.
+ */
+ const config_var_t *cvar;
+ /**
+ * The index of the object in which this option is stored. It is
+ * IDX_TOPLEVEL to indicate that the object is the top-level object.
+ **/
+ int object_idx;
+} managed_var_t;
+
+static void config_reset(const config_mgr_t *fmt, void *options,
+ const managed_var_t *var, int use_defaults);
+static void config_mgr_register_fmt(config_mgr_t *mgr,
+ const config_format_t *fmt,
+ int object_idx);
+
+/** Release all storage held in a managed_var_t. */
+static void
+managed_var_free_(managed_var_t *mv)
+{
+ if (!mv)
+ return;
+ tor_free(mv);
+}
+#define managed_var_free(mv) \
+ FREE_AND_NULL(managed_var_t, managed_var_free_, (mv))
+
+struct config_suite_t {
+ /** A list of configuration objects managed by a given configuration
+ * manager. They are stored in the same order as the config_format_t
+ * objects in the manager's list of subformats. */
+ smartlist_t *configs;
+};
+
+/**
+ * Allocate a new empty config_suite_t.
+ **/
+static config_suite_t *
+config_suite_new(void)
+{
+ config_suite_t *suite = tor_malloc_zero(sizeof(config_suite_t));
+ suite->configs = smartlist_new();
+ return suite;
+}
+
+/** Release all storage held by a config_suite_t. (Does not free
+ * any configuration objects it holds; the caller must do that first.) */
+static void
+config_suite_free_(config_suite_t *suite)
+{
+ if (!suite)
+ return;
+ smartlist_free(suite->configs);
+ tor_free(suite);
+}
+
+#define config_suite_free(suite) \
+ FREE_AND_NULL(config_suite_t, config_suite_free_, (suite))
+
+struct config_mgr_t {
+ /** The 'top-level' configuration format. This one is used for legacy
+ * options that have not yet been assigned to different sub-modules.
+ *
+ * (NOTE: for now, this is the only config_format_t that a config_mgr_t
+ * contains. A subsequent commit will add more. XXXX)
+ */
+ const config_format_t *toplevel;
+ /**
+ * List of second-level configuration format objects that this manager
+ * also knows about.
+ */
+ smartlist_t *subconfigs;
+ /** A smartlist of managed_var_t objects for all configuration formats. */
+ smartlist_t *all_vars;
+ /** A smartlist of config_abbrev_t objects for all configuration
+ * formats. These objects are used to track synonyms and abbreviations for
+ * different configuration options. */
+ smartlist_t *all_abbrevs;
+ /** A smartlist of config_deprecation_t for all configuration formats. */
+ smartlist_t *all_deprecations;
+ /** True if this manager has been frozen and cannot have any more formats
+ * added to it. A manager must be frozen before it can be used to construct
+ * or manipulate objects. */
+ bool frozen;
+ /** A replacement for the magic number of the toplevel object. We override
+ * that number to make it unique for this particular config_mgr_t, so that
+ * an object constructed with one mgr can't be used with another, even if
+ * those managers' contents are equal.
+ */
+ struct_magic_decl_t toplevel_magic;
+};
+
+#define IDX_TOPLEVEL (-1)
+
+/** Create a new config_mgr_t to manage a set of configuration objects to be
+ * wrapped under <b>toplevel_fmt</b>. */
+config_mgr_t *
+config_mgr_new(const config_format_t *toplevel_fmt)
+{
+ config_mgr_t *mgr = tor_malloc_zero(sizeof(config_mgr_t));
+ mgr->subconfigs = smartlist_new();
+ mgr->all_vars = smartlist_new();
+ mgr->all_abbrevs = smartlist_new();
+ mgr->all_deprecations = smartlist_new();
+
+ config_mgr_register_fmt(mgr, toplevel_fmt, IDX_TOPLEVEL);
+ mgr->toplevel = toplevel_fmt;
+
+ return mgr;
+}
+
+/** Add a config_format_t to a manager, with a specified (unique) index. */
+static void
+config_mgr_register_fmt(config_mgr_t *mgr,
+ const config_format_t *fmt,
+ int object_idx)
+{
+ int i;
+
+ tor_assertf(!mgr->frozen,
+ "Tried to add a format to a configuration manager after "
+ "it had been frozen.");
+
+ if (object_idx != IDX_TOPLEVEL) {
+ tor_assertf(fmt->config_suite_offset < 0,
+ "Tried to register a toplevel format in a non-toplevel position");
+ }
+ tor_assertf(fmt != mgr->toplevel &&
+ ! smartlist_contains(mgr->subconfigs, fmt),
+ "Tried to register an already-registered format.");
+
+ /* register variables */
+ for (i = 0; fmt->vars[i].member.name; ++i) {
+ managed_var_t *mv = tor_malloc_zero(sizeof(managed_var_t));
+ mv->cvar = &fmt->vars[i];
+ mv->object_idx = object_idx;
+ smartlist_add(mgr->all_vars, mv);
+ }
+
+ /* register abbrevs */
+ if (fmt->abbrevs) {
+ for (i = 0; fmt->abbrevs[i].abbreviated; ++i) {
+ smartlist_add(mgr->all_abbrevs, (void*)&fmt->abbrevs[i]);
+ }
+ }
+
+ /* register deprecations. */
+ if (fmt->deprecations) {
+ const config_deprecation_t *d;
+ for (d = fmt->deprecations; d->name; ++d) {
+ smartlist_add(mgr->all_deprecations, (void*)d);
+ }
+ }
+}
+
+/**
+ * Add a new format to this configuration object. Asserts on failure.
+ *
+ **/
+int
+config_mgr_add_format(config_mgr_t *mgr,
+ const config_format_t *fmt)
+{
+ tor_assert(mgr);
+ int idx = smartlist_len(mgr->subconfigs);
+ config_mgr_register_fmt(mgr, fmt, idx);
+ smartlist_add(mgr->subconfigs, (void *)fmt);
+ return idx;
+}
+
+/** Return a pointer to the config_suite_t * pointer inside a
+ * configuration object; returns NULL if there is no such member. */
+static inline config_suite_t **
+config_mgr_get_suite_ptr(const config_mgr_t *mgr, void *toplevel)
+{
+ if (mgr->toplevel->config_suite_offset < 0)
+ return NULL;
+ return STRUCT_VAR_P(toplevel, mgr->toplevel->config_suite_offset);
+}
+
+/**
+ * Return a pointer to the configuration object within <b>toplevel</b> whose
+ * index is <b>idx</b>.
+ *
+ * NOTE: XXXX Eventually, there will be multiple objects supported within the
+ * toplevel object. For example, the or_options_t will contain pointers
+ * to configuration objects for other modules. This function gets
+ * the sub-object for a particular module.
+ */
+STATIC void *
+config_mgr_get_obj_mutable(const config_mgr_t *mgr, void *toplevel, int idx)
+{
+ tor_assert(mgr);
+ tor_assert(toplevel);
+ if (idx == IDX_TOPLEVEL)
+ return toplevel;
+
+ tor_assertf(idx >= 0 && idx < smartlist_len(mgr->subconfigs),
+ "Index %d is out of range.", idx);
+ config_suite_t **suite = config_mgr_get_suite_ptr(mgr, toplevel);
+ tor_assert(suite);
+ tor_assert(smartlist_len(mgr->subconfigs) ==
+ smartlist_len((*suite)->configs));
+
+ return smartlist_get((*suite)->configs, idx);
+}
+
+/** As config_mgr_get_obj_mutable(), but return a const pointer. */
+STATIC const void *
+config_mgr_get_obj(const config_mgr_t *mgr, const void *toplevel, int idx)
+{
+ return config_mgr_get_obj_mutable(mgr, (void*)toplevel, idx);
+}
+
+/** Sorting helper for smartlist of managed_var_t */
+static int
+managed_var_cmp(const void **a, const void **b)
+{
+ const managed_var_t *mv1 = *(const managed_var_t**)a;
+ const managed_var_t *mv2 = *(const managed_var_t**)b;
+
+ return strcasecmp(mv1->cvar->member.name, mv2->cvar->member.name);
+}
+
+/**
+ * Mark a configuration manager as "frozen", so that no more formats can be
+ * added, and so that it can be used for manipulating configuration objects.
+ **/
+void
+config_mgr_freeze(config_mgr_t *mgr)
+{
+ static uint64_t mgr_count = 0;
+
+ smartlist_sort(mgr->all_vars, managed_var_cmp);
+ memcpy(&mgr->toplevel_magic, &mgr->toplevel->magic,
+ sizeof(struct_magic_decl_t));
+ uint64_t magic_input[3] = { mgr->toplevel_magic.magic_val,
+ (uint64_t) (uintptr_t) mgr,
+ ++mgr_count };
+ mgr->toplevel_magic.magic_val =
+ (uint32_t)siphash24g(magic_input, sizeof(magic_input));
+ mgr->frozen = true;
+}
+
+/** Release all storage held in <b>mgr</b> */
+void
+config_mgr_free_(config_mgr_t *mgr)
+{
+ if (!mgr)
+ return;
+ SMARTLIST_FOREACH(mgr->all_vars, managed_var_t *, mv, managed_var_free(mv));
+ smartlist_free(mgr->all_vars);
+ smartlist_free(mgr->all_abbrevs);
+ smartlist_free(mgr->all_deprecations);
+ smartlist_free(mgr->subconfigs);
+ memset(mgr, 0, sizeof(*mgr));
+ tor_free(mgr);
+}
+
+/** Return a new smartlist_t containing a config_var_t for every variable that
+ * <b>mgr</b> knows about. The elements of this smartlist do not need
+ * to be freed; they have the same lifespan as <b>mgr</b>. */
+smartlist_t *
+config_mgr_list_vars(const config_mgr_t *mgr)
+{
+ smartlist_t *result = smartlist_new();
+ tor_assert(mgr);
+ SMARTLIST_FOREACH(mgr->all_vars, managed_var_t *, mv,
+ smartlist_add(result, (void*) mv->cvar));
+ return result;
+}
+
+/** Return a new smartlist_t containing the names of all deprecated variables.
+ * The elements of this smartlist do not need to be freed; they have the same
+ * lifespan as <b>mgr</b>.
+ */
+smartlist_t *
+config_mgr_list_deprecated_vars(const config_mgr_t *mgr)
+{
+ smartlist_t *result = smartlist_new();
+ tor_assert(mgr);
+ SMARTLIST_FOREACH(mgr->all_deprecations, config_deprecation_t *, d,
+ smartlist_add(result, (char*)d->name));
+ return result;
+}
+
+/** Assert that the magic fields in <b>options</b> and its subsidiary
+ * objects are all okay. */
+static void
+config_mgr_assert_magic_ok(const config_mgr_t *mgr,
+ const void *options)
+{
+ tor_assert(mgr);
+ tor_assert(options);
+ tor_assert(mgr->frozen);
+ struct_check_magic(options, &mgr->toplevel_magic);
+
+ config_suite_t **suitep = config_mgr_get_suite_ptr(mgr, (void*)options);
+ if (suitep == NULL) {
+ tor_assert(smartlist_len(mgr->subconfigs) == 0);
+ return;
+ }
+
+ tor_assert(smartlist_len((*suitep)->configs) ==
+ smartlist_len(mgr->subconfigs));
+ SMARTLIST_FOREACH_BEGIN(mgr->subconfigs, const config_format_t *, fmt) {
+ void *obj = smartlist_get((*suitep)->configs, fmt_sl_idx);
+ tor_assert(obj);
+ struct_check_magic(obj, &fmt->magic);
+ } SMARTLIST_FOREACH_END(fmt);
+}
+
+/** Macro: assert that <b>cfg</b> has the right magic field for
+ * <b>mgr</b>. */
+#define CONFIG_CHECK(mgr, cfg) STMT_BEGIN \
+ config_mgr_assert_magic_ok((mgr), (cfg)); \
+ STMT_END
/** Allocate an empty configuration object of a given format type. */
void *
-config_new(const config_format_t *fmt)
+config_new(const config_mgr_t *mgr)
{
- void *opts = tor_malloc_zero(fmt->size);
- struct_set_magic(opts, &fmt->magic);
- CONFIG_CHECK(fmt, opts);
+ tor_assert(mgr->frozen);
+ void *opts = tor_malloc_zero(mgr->toplevel->size);
+ struct_set_magic(opts, &mgr->toplevel_magic);
+ config_suite_t **suitep = config_mgr_get_suite_ptr(mgr, opts);
+ if (suitep) {
+ *suitep = config_suite_new();
+ SMARTLIST_FOREACH_BEGIN(mgr->subconfigs, const config_format_t *, fmt) {
+ void *obj = tor_malloc_zero(fmt->size);
+ struct_set_magic(obj, &fmt->magic);
+ smartlist_add((*suitep)->configs, obj);
+ } SMARTLIST_FOREACH_END(fmt);
+ }
+ CONFIG_CHECK(mgr, opts);
return opts;
}
@@ -60,29 +393,26 @@ config_new(const config_format_t *fmt)
* apply abbreviations that work for the config file and the command line.
* If <b>warn_obsolete</b> is set, warn about deprecated names. */
const char *
-config_expand_abbrev(const config_format_t *fmt, const char *option,
+config_expand_abbrev(const config_mgr_t *mgr, const char *option,
int command_line, int warn_obsolete)
{
- int i;
- if (! fmt->abbrevs)
- return option;
- for (i=0; fmt->abbrevs[i].abbreviated; ++i) {
+ SMARTLIST_FOREACH_BEGIN(mgr->all_abbrevs, const config_abbrev_t *, abbrev) {
/* Abbreviations are case insensitive. */
- if (!strcasecmp(option,fmt->abbrevs[i].abbreviated) &&
- (command_line || !fmt->abbrevs[i].commandline_only)) {
- if (warn_obsolete && fmt->abbrevs[i].warn) {
+ if (!strcasecmp(option, abbrev->abbreviated) &&
+ (command_line || !abbrev->commandline_only)) {
+ if (warn_obsolete && abbrev->warn) {
log_warn(LD_CONFIG,
"The configuration option '%s' is deprecated; "
"use '%s' instead.",
- fmt->abbrevs[i].abbreviated,
- fmt->abbrevs[i].full);
+ abbrev->abbreviated,
+ abbrev->full);
}
/* Keep going through the list in case we want to rewrite it more.
* (We could imagine recursing here, but I don't want to get the
* user into an infinite loop if we craft our list wrong.) */
- option = fmt->abbrevs[i].full;
+ option = abbrev->full;
}
- }
+ } SMARTLIST_FOREACH_END(abbrev);
return option;
}
@@ -90,61 +420,92 @@ config_expand_abbrev(const config_format_t *fmt, const char *option,
* explaining why it is deprecated (which may be an empty string). Return NULL
* if it is not deprecated. The <b>key</b> field must be fully expanded. */
const char *
-config_find_deprecation(const config_format_t *fmt, const char *key)
+config_find_deprecation(const config_mgr_t *mgr, const char *key)
{
- if (BUG(fmt == NULL) || BUG(key == NULL))
+ if (BUG(mgr == NULL) || BUG(key == NULL))
return NULL; // LCOV_EXCL_LINE
- if (fmt->deprecations == NULL)
- return NULL;
- const config_deprecation_t *d;
- for (d = fmt->deprecations; d->name; ++d) {
+ SMARTLIST_FOREACH_BEGIN(mgr->all_deprecations, const config_deprecation_t *,
+ d) {
if (!strcasecmp(d->name, key)) {
return d->why_deprecated ? d->why_deprecated : "";
}
- }
+ } SMARTLIST_FOREACH_END(d);
return NULL;
}
-/** If <b>key</b> is a configuration option, return the corresponding const
- * config_var_t. Otherwise, if <b>key</b> is a non-standard abbreviation,
- * warn, and return the corresponding const config_var_t. Otherwise return
- * NULL.
+/**
+ * Find the managed_var_t object for a variable whose name is <b>name</b>
+ * according to <b>mgr</b>. Return that object, or NULL if none exists.
+ *
+ * If <b>allow_truncated</b> is true, then accept any variable whose
+ * name begins with <b>name</b>.
+ *
+ * If <b>idx_out</b> is not NULL, set *<b>idx_out</b> to the position of
+ * that variable within mgr-&gt;all_vars, or to -1 if the variable is
+ * not found.
*/
-const config_var_t *
-config_find_option(const config_format_t *fmt, const char *key)
+static const managed_var_t *
+config_mgr_find_var(const config_mgr_t *mgr,
+ const char *key,
+ bool allow_truncated, int *idx_out)
{
- int i;
- size_t keylen = strlen(key);
+ const size_t keylen = strlen(key);
+ if (idx_out)
+ *idx_out = -1;
+
if (!keylen)
return NULL; /* if they say "--" on the command line, it's not an option */
+
/* First, check for an exact (case-insensitive) match */
- for (i=0; fmt->vars[i].member.name; ++i) {
- if (!strcasecmp(key, fmt->vars[i].member.name)) {
- return &fmt->vars[i];
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ if (!strcasecmp(mv->cvar->member.name, key)) {
+ if (idx_out)
+ *idx_out = mv_sl_idx;
+ return mv;
}
- }
+ } SMARTLIST_FOREACH_END(mv);
+
+ if (!allow_truncated)
+ return NULL;
+
/* If none, check for an abbreviated match */
- for (i=0; fmt->vars[i].member.name; ++i) {
- if (!strncasecmp(key, fmt->vars[i].member.name, keylen)) {
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ if (!strncasecmp(key, mv->cvar->member.name, keylen)) {
log_warn(LD_CONFIG, "The abbreviation '%s' is deprecated. "
"Please use '%s' instead",
- key, fmt->vars[i].member.name);
- return &fmt->vars[i];
+ key, mv->cvar->member.name);
+ if (idx_out)
+ *idx_out = mv_sl_idx;
+ return mv;
}
- }
+ } SMARTLIST_FOREACH_END(mv);
+
/* Okay, unrecognized option */
return NULL;
}
+/**
+ * If <b>key</b> is a name or an abbreviation configuration option, return
+ * the corresponding canonical name for it. Warn if the abbreviation is
+ * non-standard. Return NULL if the option does not exist.
+ */
+const char *
+config_find_option_name(const config_mgr_t *mgr, const char *key)
+{
+ key = config_expand_abbrev(mgr, key, 0, 0);
+ const managed_var_t *mv = config_mgr_find_var(mgr, key, true, NULL);
+ if (mv)
+ return mv->cvar->member.name;
+ else
+ return NULL;
+}
+
/** Return the number of option entries in <b>fmt</b>. */
static int
-config_count_options(const config_format_t *fmt)
+config_count_options(const config_mgr_t *mgr)
{
- int i;
- for (i=0; fmt->vars[i].member.name; ++i)
- ;
- return i;
+ return smartlist_len(mgr->all_vars);
}
bool
@@ -185,33 +546,33 @@ config_var_is_dumpable(const config_var_t *var)
* Called from config_assign_line() and option_reset().
*/
static int
-config_assign_value(const config_format_t *fmt, void *options,
+config_assign_value(const config_mgr_t *mgr, void *options,
config_line_t *c, char **msg)
{
- const config_var_t *var;
+ const managed_var_t *var;
- CONFIG_CHECK(fmt, options);
+ CONFIG_CHECK(mgr, options);
- var = config_find_option(fmt, c->key);
+ var = config_mgr_find_var(mgr, c->key, true, NULL);
tor_assert(var);
- tor_assert(!strcmp(c->key, var->member.name));
+ tor_assert(!strcmp(c->key, var->cvar->member.name));
+ void *object = config_mgr_get_obj_mutable(mgr, options, var->object_idx);
- return struct_var_kvassign(options, c, msg, &var->member);
+ return struct_var_kvassign(object, c, msg, &var->cvar->member);
}
/** Mark every linelist in <b>options</b> "fragile", so that fresh assignments
* to it will replace old ones. */
static void
-config_mark_lists_fragile(const config_format_t *fmt, void *options)
+config_mark_lists_fragile(const config_mgr_t *mgr, void *options)
{
- int i;
- tor_assert(fmt);
+ tor_assert(mgr);
tor_assert(options);
- for (i = 0; fmt->vars[i].member.name; ++i) {
- const config_var_t *var = &fmt->vars[i];
- struct_var_mark_fragile(options, &var->member);
- }
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ void *object = config_mgr_get_obj_mutable(mgr, options, mv->object_idx);
+ struct_var_mark_fragile(object, &mv->cvar->member);
+ } SMARTLIST_FOREACH_END(mv);
}
void
@@ -234,19 +595,21 @@ warn_deprecated_option(const char *what, const char *why)
* Called from config_assign().
*/
static int
-config_assign_line(const config_format_t *fmt, void *options,
+config_assign_line(const config_mgr_t *mgr, void *options,
config_line_t *c, unsigned flags,
bitarray_t *options_seen, char **msg)
{
const unsigned use_defaults = flags & CAL_USE_DEFAULTS;
const unsigned clear_first = flags & CAL_CLEAR_FIRST;
const unsigned warn_deprecations = flags & CAL_WARN_DEPRECATIONS;
- const config_var_t *var;
+ const managed_var_t *mvar;
- CONFIG_CHECK(fmt, options);
+ CONFIG_CHECK(mgr, options);
- var = config_find_option(fmt, c->key);
- if (!var) {
+ int var_index = -1;
+ mvar = config_mgr_find_var(mgr, c->key, true, &var_index);
+ if (!mvar) {
+ const config_format_t *fmt = mgr->toplevel;
if (fmt->extra) {
void *lvalue = STRUCT_VAR_P(options, fmt->extra->offset);
log_info(LD_CONFIG,
@@ -260,49 +623,52 @@ config_assign_line(const config_format_t *fmt, void *options,
}
}
+ const config_var_t *cvar = mvar->cvar;
+ tor_assert(cvar);
+
/* Put keyword into canonical case. */
- if (strcmp(var->member.name, c->key)) {
+ if (strcmp(cvar->member.name, c->key)) {
tor_free(c->key);
- c->key = tor_strdup(var->member.name);
+ c->key = tor_strdup(cvar->member.name);
}
const char *deprecation_msg;
if (warn_deprecations &&
- (deprecation_msg = config_find_deprecation(fmt, var->member.name))) {
- warn_deprecated_option(var->member.name, deprecation_msg);
+ (deprecation_msg = config_find_deprecation(mgr, cvar->member.name))) {
+ warn_deprecated_option(cvar->member.name, deprecation_msg);
}
if (!strlen(c->value)) {
/* reset or clear it, then return */
if (!clear_first) {
- if (config_var_is_cumulative(var) && c->command != CONFIG_LINE_CLEAR) {
+ if (config_var_is_cumulative(cvar) && c->command != CONFIG_LINE_CLEAR) {
/* We got an empty linelist from the torrc or command line.
As a special case, call this an error. Warn and ignore. */
log_warn(LD_CONFIG,
"Linelist option '%s' has no value. Skipping.", c->key);
} else { /* not already cleared */
- config_reset(fmt, options, var, use_defaults);
+ config_reset(mgr, options, mvar, use_defaults);
}
}
return 0;
} else if (c->command == CONFIG_LINE_CLEAR && !clear_first) {
// XXXX This is unreachable, since a CLEAR line always has an
// XXXX empty value.
- config_reset(fmt, options, var, use_defaults); // LCOV_EXCL_LINE
+ config_reset(mgr, options, mvar, use_defaults); // LCOV_EXCL_LINE
}
- if (options_seen && ! config_var_is_cumulative(var)) {
+ if (options_seen && ! config_var_is_cumulative(cvar)) {
/* We're tracking which options we've seen, and this option is not
* supposed to occur more than once. */
- int var_index = (int)(var - fmt->vars);
+ tor_assert(var_index >= 0);
if (bitarray_is_set(options_seen, var_index)) {
log_warn(LD_CONFIG, "Option '%s' used more than once; all but the last "
- "value will be ignored.", var->member.name);
+ "value will be ignored.", cvar->member.name);
}
bitarray_set(options_seen, var_index);
}
- if (config_assign_value(fmt, options, c, msg) < 0)
+ if (config_assign_value(mgr, options, c, msg) < 0)
return -2;
return 0;
}
@@ -310,18 +676,18 @@ config_assign_line(const config_format_t *fmt, void *options,
/** Restore the option named <b>key</b> in options to its default value.
* Called from config_assign(). */
STATIC void
-config_reset_line(const config_format_t *fmt, void *options,
+config_reset_line(const config_mgr_t *mgr, void *options,
const char *key, int use_defaults)
{
- const config_var_t *var;
+ const managed_var_t *var;
- CONFIG_CHECK(fmt, options);
+ CONFIG_CHECK(mgr, options);
- var = config_find_option(fmt, key);
+ var = config_mgr_find_var(mgr, key, true, NULL);
if (!var)
return; /* give error on next pass. */
- config_reset(fmt, options, var, use_defaults);
+ config_reset(mgr, options, var, use_defaults);
}
/** Return true iff value needs to be quoted and escaped to be used in
@@ -355,22 +721,24 @@ config_value_needs_escape(const char *value)
* value needs to be quoted before it's put in a config file, quote and
* escape that value. Return NULL if no such key exists. */
config_line_t *
-config_get_assigned_option(const config_format_t *fmt, const void *options,
+config_get_assigned_option(const config_mgr_t *mgr, const void *options,
const char *key, int escape_val)
{
- const config_var_t *var;
+ const managed_var_t *var;
config_line_t *result;
+
tor_assert(options && key);
- CONFIG_CHECK(fmt, options);
+ CONFIG_CHECK(mgr, options);
- var = config_find_option(fmt, key);
+ var = config_mgr_find_var(mgr, key, true, NULL);
if (!var) {
log_warn(LD_CONFIG, "Unknown option '%s'. Failing.", key);
return NULL;
}
+ const void *object = config_mgr_get_obj(mgr, options, var->object_idx);
- result = struct_var_kvencode(options, &var->member);
+ result = struct_var_kvencode(object, &var->cvar->member);
if (escape_val) {
config_line_t *line;
@@ -442,20 +810,20 @@ options_trial_assign() calls config_assign(1, 1)
returns.
*/
int
-config_assign(const config_format_t *fmt, void *options, config_line_t *list,
+config_assign(const config_mgr_t *mgr, void *options, config_line_t *list,
unsigned config_assign_flags, char **msg)
{
config_line_t *p;
bitarray_t *options_seen;
- const int n_options = config_count_options(fmt);
+ const int n_options = config_count_options(mgr);
const unsigned clear_first = config_assign_flags & CAL_CLEAR_FIRST;
const unsigned use_defaults = config_assign_flags & CAL_USE_DEFAULTS;
- CONFIG_CHECK(fmt, options);
+ CONFIG_CHECK(mgr, options);
/* pass 1: normalize keys */
for (p = list; p; p = p->next) {
- const char *full = config_expand_abbrev(fmt, p->key, 0, 1);
+ const char *full = config_expand_abbrev(mgr, p->key, 0, 1);
if (strcmp(full,p->key)) {
tor_free(p->key);
p->key = tor_strdup(full);
@@ -466,14 +834,14 @@ config_assign(const config_format_t *fmt, void *options, config_line_t *list,
* mentioned config options, and maybe set to their defaults. */
if (clear_first) {
for (p = list; p; p = p->next)
- config_reset_line(fmt, options, p->key, use_defaults);
+ config_reset_line(mgr, options, p->key, use_defaults);
}
options_seen = bitarray_init_zero(n_options);
/* pass 3: assign. */
while (list) {
int r;
- if ((r=config_assign_line(fmt, options, list, config_assign_flags,
+ if ((r=config_assign_line(mgr, options, list, config_assign_flags,
options_seen, msg))) {
bitarray_free(options_seen);
return r;
@@ -485,7 +853,7 @@ config_assign(const config_format_t *fmt, void *options, config_line_t *list,
/** Now we're done assigning a group of options to the configuration.
* Subsequent group assignments should _replace_ linelists, not extend
* them. */
- config_mark_lists_fragile(fmt, options);
+ config_mark_lists_fragile(mgr, options);
return 0;
}
@@ -493,33 +861,32 @@ config_assign(const config_format_t *fmt, void *options, config_line_t *list,
/** Reset config option <b>var</b> to 0, 0.0, NULL, or the equivalent.
* Called from config_reset() and config_free(). */
static void
-config_clear(const config_format_t *fmt, void *options,
- const config_var_t *var)
+config_clear(const config_mgr_t *mgr, void *options, const managed_var_t *var)
{
-
- (void)fmt; /* unused */
-
- struct_var_free(options, &var->member);
+ void *object = config_mgr_get_obj_mutable(mgr, options, var->object_idx);
+ struct_var_free(object, &var->cvar->member);
}
/** Clear the option indexed by <b>var</b> in <b>options</b>. Then if
* <b>use_defaults</b>, set it to its default value.
* Called by config_init() and option_reset_line() and option_assign_line(). */
static void
-config_reset(const config_format_t *fmt, void *options,
- const config_var_t *var, int use_defaults)
+config_reset(const config_mgr_t *mgr, void *options,
+ const managed_var_t *var, int use_defaults)
{
config_line_t *c;
char *msg = NULL;
- CONFIG_CHECK(fmt, options);
- config_clear(fmt, options, var); /* clear it first */
+ CONFIG_CHECK(mgr, options);
+ config_clear(mgr, options, var); /* clear it first */
+
if (!use_defaults)
return; /* all done */
- if (var->initvalue) {
+
+ if (var->cvar->initvalue) {
c = tor_malloc_zero(sizeof(config_line_t));
- c->key = tor_strdup(var->member.name);
- c->value = tor_strdup(var->initvalue);
- if (config_assign_value(fmt, options, c, &msg) < 0) {
+ c->key = tor_strdup(var->cvar->member.name);
+ c->value = tor_strdup(var->cvar->initvalue);
+ if (config_assign_value(mgr, options, c, &msg) < 0) {
// LCOV_EXCL_START
log_warn(LD_BUG, "Failed to assign default: %s", msg);
tor_free(msg); /* if this happens it's a bug */
@@ -531,23 +898,44 @@ config_reset(const config_format_t *fmt, void *options,
/** Release storage held by <b>options</b>. */
void
-config_free_(const config_format_t *fmt, void *options)
+config_free_(const config_mgr_t *mgr, void *options)
{
- int i;
-
if (!options)
return;
- tor_assert(fmt);
+ tor_assert(mgr);
- for (i=0; fmt->vars[i].member.name; ++i)
- config_clear(fmt, options, &(fmt->vars[i]));
+ if (mgr->toplevel->clear_fn) {
+ mgr->toplevel->clear_fn(mgr, options);
+ }
+ config_suite_t **suitep = config_mgr_get_suite_ptr(mgr, options);
+ if (suitep) {
+ tor_assert(smartlist_len((*suitep)->configs) ==
+ smartlist_len(mgr->subconfigs));
+ SMARTLIST_FOREACH_BEGIN(mgr->subconfigs, const config_format_t *, fmt) {
+ void *obj = smartlist_get((*suitep)->configs, fmt_sl_idx);
+ if (fmt->clear_fn) {
+ fmt->clear_fn(mgr, obj);
+ }
+ } SMARTLIST_FOREACH_END(fmt);
+ }
- if (fmt->extra) {
- config_line_t **linep = STRUCT_VAR_P(options, fmt->extra->offset);
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ config_clear(mgr, options, mv);
+ } SMARTLIST_FOREACH_END(mv);
+
+ if (mgr->toplevel->extra) {
+ config_line_t **linep = STRUCT_VAR_P(options,
+ mgr->toplevel->extra->offset);
config_free_lines(*linep);
*linep = NULL;
}
+
+ if (suitep) {
+ SMARTLIST_FOREACH((*suitep)->configs, void *, obj, tor_free(obj));
+ config_suite_free(*suitep);
+ }
+
tor_free(options);
}
@@ -555,59 +943,103 @@ config_free_(const config_format_t *fmt, void *options)
* and <b>o2</b>. Must not be called for LINELIST_S or OBSOLETE options.
*/
int
-config_is_same(const config_format_t *fmt,
+config_is_same(const config_mgr_t *mgr,
const void *o1, const void *o2,
const char *name)
{
- CONFIG_CHECK(fmt, o1);
- CONFIG_CHECK(fmt, o2);
+ CONFIG_CHECK(mgr, o1);
+ CONFIG_CHECK(mgr, o2);
- const config_var_t *var = config_find_option(fmt, name);
+ const managed_var_t *var = config_mgr_find_var(mgr, name, true, NULL);
if (!var) {
return true;
}
+ const void *obj1 = config_mgr_get_obj(mgr, o1, var->object_idx);
+ const void *obj2 = config_mgr_get_obj(mgr, o2, var->object_idx);
- return struct_var_eq(o1, o2, &var->member);
+ return struct_var_eq(obj1, obj2, &var->cvar->member);
+}
+
+/**
+ * Return a list of the options which have changed between <b>options1</b> and
+ * <b>options2</b>. If an option has reverted to its default value, it has a
+ * value entry of NULL.
+ *
+ * <b>options1</b> and <b>options2</b> must be top-level configuration objects
+ * of the type managed by <b>mgr</b>.
+ **/
+config_line_t *
+config_get_changes(const config_mgr_t *mgr,
+ const void *options1, const void *options2)
+{
+ config_line_t *result = NULL;
+ config_line_t **next = &result;
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, managed_var_t *, mv) {
+ if (config_var_is_contained(mv->cvar)) {
+ /* something else will check this var, or it doesn't need checking */
+ continue;
+ }
+ const void *obj1 = config_mgr_get_obj(mgr, options1, mv->object_idx);
+ const void *obj2 = config_mgr_get_obj(mgr, options2, mv->object_idx);
+
+ if (struct_var_eq(obj1, obj2, &mv->cvar->member)) {
+ continue;
+ }
+
+ const char *varname = mv->cvar->member.name;
+ config_line_t *line =
+ config_get_assigned_option(mgr, options2, varname, 1);
+
+ if (line) {
+ *next = line;
+ } else {
+ *next = tor_malloc_zero(sizeof(config_line_t));
+ (*next)->key = tor_strdup(varname);
+ }
+ while (*next)
+ next = &(*next)->next;
+ } SMARTLIST_FOREACH_END(mv);
+
+ return result;
}
/** Copy storage held by <b>old</b> into a new or_options_t and return it. */
void *
-config_dup(const config_format_t *fmt, const void *old)
+config_dup(const config_mgr_t *mgr, const void *old)
{
void *newopts;
- int i;
- newopts = config_new(fmt);
- for (i=0; fmt->vars[i].member.name; ++i) {
- if (config_var_is_contained(&fmt->vars[i])) {
+ newopts = config_new(mgr);
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, managed_var_t *, mv) {
+ if (config_var_is_contained(mv->cvar)) {
// Something else will copy this option, or it doesn't need copying.
continue;
}
- if (struct_var_copy(newopts, old, &fmt->vars[i].member) < 0) {
+ const void *oldobj = config_mgr_get_obj(mgr, old, mv->object_idx);
+ void *newobj = config_mgr_get_obj_mutable(mgr, newopts, mv->object_idx);
+ if (struct_var_copy(newobj, oldobj, &mv->cvar->member) < 0) {
// LCOV_EXCL_START
log_err(LD_BUG, "Unable to copy value for %s.",
- fmt->vars[i].member.name);
+ mv->cvar->member.name);
tor_assert_unreached();
// LCOV_EXCL_STOP
}
- }
+ } SMARTLIST_FOREACH_END(mv);
+
return newopts;
}
/** Set all vars in the configuration object <b>options</b> to their default
* values. */
void
-config_init(const config_format_t *fmt, void *options)
+config_init(const config_mgr_t *mgr, void *options)
{
- int i;
- const config_var_t *var;
- CONFIG_CHECK(fmt, options);
+ CONFIG_CHECK(mgr, options);
- for (i=0; fmt->vars[i].member.name; ++i) {
- var = &fmt->vars[i];
- if (!var->initvalue)
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ if (!mv->cvar->initvalue)
continue; /* defaults to NULL or 0 */
- config_reset(fmt, options, var, 1);
- }
+ config_reset(mgr, options, mv, 1);
+ } SMARTLIST_FOREACH_END(mv);
}
/** Allocate and return a new string holding the written-out values of the vars
@@ -615,21 +1047,21 @@ config_init(const config_format_t *fmt, void *options)
* Else, if comment_defaults, write default values as comments.
*/
char *
-config_dump(const config_format_t *fmt, const void *default_options,
+config_dump(const config_mgr_t *mgr, const void *default_options,
const void *options, int minimal,
int comment_defaults)
{
+ const config_format_t *fmt = mgr->toplevel;
smartlist_t *elements;
const void *defaults = default_options;
void *defaults_tmp = NULL;
config_line_t *line, *assigned;
char *result;
- int i;
char *msg = NULL;
if (defaults == NULL) {
- defaults = defaults_tmp = config_new(fmt);
- config_init(fmt, defaults_tmp);
+ defaults = defaults_tmp = config_new(mgr);
+ config_init(mgr, defaults_tmp);
}
/* XXX use a 1 here so we don't add a new log line while dumping */
@@ -644,24 +1076,24 @@ config_dump(const config_format_t *fmt, const void *default_options,
}
elements = smartlist_new();
- for (i=0; fmt->vars[i].member.name; ++i) {
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, managed_var_t *, mv) {
int comment_option = 0;
- if (config_var_is_contained(&fmt->vars[i])) {
+ if (config_var_is_contained(mv->cvar)) {
// Something else will dump this option, or it doesn't need dumping.
continue;
}
/* Don't save 'hidden' control variables. */
- if (! config_var_is_dumpable(&fmt->vars[i]))
+ if (! config_var_is_dumpable(mv->cvar))
continue;
- if (minimal && config_is_same(fmt, options, defaults,
- fmt->vars[i].member.name))
+ const char *name = mv->cvar->member.name;
+ if (minimal && config_is_same(mgr, options, defaults, name))
continue;
else if (comment_defaults &&
- config_is_same(fmt, options, defaults, fmt->vars[i].member.name))
+ config_is_same(mgr, options, defaults, name))
comment_option = 1;
line = assigned =
- config_get_assigned_option(fmt, options, fmt->vars[i].member.name, 1);
+ config_get_assigned_option(mgr, options, name, 1);
for (; line; line = line->next) {
if (!strcmpstart(line->key, "__")) {
@@ -674,7 +1106,7 @@ config_dump(const config_format_t *fmt, const void *default_options,
line->key, line->value);
}
config_free_lines(assigned);
- }
+ } SMARTLIST_FOREACH_END(mv);
if (fmt->extra) {
line = *(config_line_t**)STRUCT_VAR_P(options, fmt->extra->offset);
@@ -686,9 +1118,7 @@ config_dump(const config_format_t *fmt, const void *default_options,
result = smartlist_join_strings(elements, "", 0, NULL);
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements);
- if (defaults_tmp) {
- fmt->free_fn(defaults_tmp);
- }
+ config_free(mgr, defaults_tmp);
return result;
}
@@ -697,15 +1127,17 @@ config_dump(const config_format_t *fmt, const void *default_options,
* Return false otherwise. Log errors at level <b>severity</b>.
*/
bool
-config_check_ok(const config_format_t *fmt, const void *options, int severity)
+config_check_ok(const config_mgr_t *mgr, const void *options, int severity)
{
bool all_ok = true;
- for (int i=0; fmt->vars[i].member.name; ++i) {
- if (!struct_var_ok(options, &fmt->vars[i].member)) {
+
+ SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
+ if (!struct_var_ok(options, &mv->cvar->member)) {
log_fn(severity, LD_BUG, "Invalid value for %s",
- fmt->vars[i].member.name);
+ mv->cvar->member.name);
all_ok = false;
}
- }
+ } SMARTLIST_FOREACH_END(mv);
+
return all_ok;
}