aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2019-08-28 09:46:59 -0400
committerNick Mathewson <nickm@torproject.org>2019-08-28 09:46:59 -0400
commitb1d7ddfb02cdbd23fce0c30d6ab4897ede18e7fe (patch)
tree123e890d0e61f38d5a4ff091d7244c1a233a0ccc
parent35e978da61efa04af9a5ab2399dff863bc6fb20a (diff)
parenta3e99c5f1e2eb9dd9ead51bcbed61dc2b99cf256 (diff)
downloadtor-b1d7ddfb02cdbd23fce0c30d6ab4897ede18e7fe.tar.gz
tor-b1d7ddfb02cdbd23fce0c30d6ab4897ede18e7fe.zip
Merge branch 'ticket31240v2' into ticket31240v2_merged_2
-rw-r--r--src/app/config/config.c157
-rw-r--r--src/app/config/config.h5
-rw-r--r--src/app/config/confparse.c746
-rw-r--r--src/app/config/confparse.h83
-rw-r--r--src/app/config/or_options_st.h9
-rw-r--r--src/app/config/or_state_st.h9
-rw-r--r--src/app/config/statefile.c43
-rw-r--r--src/feature/dirauth/shared_random_state.c43
-rw-r--r--src/test/fuzz/fuzzing_common.c5
-rw-r--r--src/test/include.am1
-rw-r--r--src/test/test.c1
-rw-r--r--src/test/test.h1
-rw-r--r--src/test/test_config.c92
-rw-r--r--src/test/test_confmgr.c325
-rw-r--r--src/test/test_confparse.c381
-rw-r--r--src/test/test_dir_handle_get.c3
-rw-r--r--src/test/test_entrynodes.c30
-rw-r--r--src/test/test_helpers.c2
-rw-r--r--src/test/test_hs_service.c10
-rw-r--r--src/test/test_options.c14
-rw-r--r--src/test/test_pt.c2
21 files changed, 1474 insertions, 488 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c
index 740315f3e4..0cbc223d02 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -843,15 +843,15 @@ static void config_maybe_load_geoip_files_(const or_options_t *options,
static int options_validate_cb(void *old_options, void *options,
void *default_options,
int from_setconf, char **msg);
-static void options_free_cb(void *options);
static void cleanup_protocol_warning_severity_level(void);
static void set_protocol_warning_severity_level(int warning_severity);
+static void options_clear_cb(const config_mgr_t *mgr, void *opts);
/** Magic value for or_options_t. */
#define OR_OPTIONS_MAGIC 9090909
/** Configuration format for or_options_t. */
-STATIC const config_format_t options_format = {
+static const config_format_t options_format = {
sizeof(or_options_t),
{
"or_options_t",
@@ -862,8 +862,9 @@ STATIC const config_format_t options_format = {
option_deprecation_notes_,
option_vars_,
options_validate_cb,
- options_free_cb,
- NULL
+ options_clear_cb,
+ NULL,
+ offsetof(or_options_t, subconfigs_),
};
/*
@@ -895,6 +896,20 @@ static int in_option_validation = 0;
/* True iff we've initialized libevent */
static int libevent_initialized = 0;
+/* A global configuration manager to handle all configuration objects. */
+static config_mgr_t *options_mgr = NULL;
+
+/** Return the global configuration manager object for torrc options. */
+STATIC const config_mgr_t *
+get_options_mgr(void)
+{
+ if (PREDICT_UNLIKELY(options_mgr == NULL)) {
+ options_mgr = config_mgr_new(&options_format);
+ config_mgr_freeze(options_mgr);
+ }
+ return options_mgr;
+}
+
/** Return the contents of our frontpage string, or NULL if not configured. */
MOCK_IMPL(const char*,
get_dirportfrontpage, (void))
@@ -951,9 +966,6 @@ get_options_defaults(void)
int
set_options(or_options_t *new_val, char **msg)
{
- int i;
- smartlist_t *elements;
- config_line_t *line;
or_options_t *old_options = global_options;
global_options = new_val;
/* Note that we pass the *old* options below, for comparison. It
@@ -975,35 +987,16 @@ set_options(or_options_t *new_val, char **msg)
/* Issues a CONF_CHANGED event to notify controller of the change. If Tor is
* just starting up then the old_options will be undefined. */
if (old_options && old_options != global_options) {
- elements = smartlist_new();
- for (i=0; options_format.vars[i].member.name; ++i) {
- const config_var_t *var = &options_format.vars[i];
- const char *var_name = var->member.name;
- if (config_var_is_contained(var)) {
- /* something else will check this var, or it doesn't need checking */
- continue;
- }
- if (!config_is_same(&options_format, new_val, old_options, var_name)) {
- line = config_get_assigned_option(&options_format, new_val,
- var_name, 1);
-
- if (line) {
- config_line_t *next;
- for (; line; line = next) {
- next = line->next;
- smartlist_add(elements, line->key);
- smartlist_add(elements, line->value);
- tor_free(line);
- }
- } else {
- smartlist_add_strdup(elements, options_format.vars[i].member.name);
- smartlist_add(elements, NULL);
- }
- }
+ smartlist_t *elements = smartlist_new();
+ config_line_t *changes =
+ config_get_changes(get_options_mgr(), old_options, new_val);
+ for (config_line_t *line = changes; line; line = line->next) {
+ smartlist_add(elements, line->key);
+ smartlist_add(elements, line->value);
}
control_event_conf_changed(elements);
- SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements);
+ config_free_lines(changes);
}
if (old_options != global_options) {
@@ -1019,11 +1012,11 @@ set_options(or_options_t *new_val, char **msg)
/** Release additional memory allocated in options
*/
-STATIC void
-or_options_free_(or_options_t *options)
+static void
+options_clear_cb(const config_mgr_t *mgr, void *opts)
{
- if (!options)
- return;
+ (void)mgr;
+ or_options_t *options = opts;
routerset_free(options->ExcludeExitNodesUnion_);
if (options->NodeFamilySets) {
@@ -1046,7 +1039,14 @@ or_options_free_(or_options_t *options)
tor_free(options->command_arg);
tor_free(options->master_key_fname);
config_free_lines(options->MyFamily);
- config_free(&options_format, options);
+}
+
+/** Release all memory allocated in options
+ */
+STATIC void
+or_options_free_(or_options_t *options)
+{
+ config_free(get_options_mgr(), options);
}
/** Release all memory and resources held by global configuration structures.
@@ -1080,6 +1080,8 @@ config_free_all(void)
have_parsed_cmdline = 0;
libevent_initialized = 0;
+
+ config_mgr_free(options_mgr);
}
/** Make <b>address</b> -- a piece of information related to our operation as
@@ -2547,7 +2549,7 @@ config_parse_commandline(int argc, char **argv, int ignore_errors,
param = tor_malloc_zero(sizeof(config_line_t));
param->key = is_cmdline ? tor_strdup(argv[i]) :
- tor_strdup(config_expand_abbrev(&options_format, s, 1, 1));
+ tor_strdup(config_expand_abbrev(get_options_mgr(), s, 1, 1));
param->value = arg;
param->command = command;
param->next = NULL;
@@ -2573,8 +2575,7 @@ config_parse_commandline(int argc, char **argv, int ignore_errors,
int
option_is_recognized(const char *key)
{
- const config_var_t *var = config_find_option(&options_format, key);
- return (var != NULL);
+ return config_find_option_name(get_options_mgr(), key) != NULL;
}
/** Return the canonical name of a configuration option, or NULL
@@ -2582,8 +2583,7 @@ option_is_recognized(const char *key)
const char *
option_get_canonical_name(const char *key)
{
- const config_var_t *var = config_find_option(&options_format, key);
- return var ? var->member.name : NULL;
+ return config_find_option_name(get_options_mgr(), key);
}
/** Return a canonical list of the options assigned for key.
@@ -2591,7 +2591,7 @@ option_get_canonical_name(const char *key)
config_line_t *
option_get_assignment(const or_options_t *options, const char *key)
{
- return config_get_assigned_option(&options_format, options, key, 1);
+ return config_get_assigned_option(get_options_mgr(), options, key, 1);
}
/** Try assigning <b>list</b> to the global options. You do this by duping
@@ -2607,9 +2607,9 @@ setopt_err_t
options_trial_assign(config_line_t *list, unsigned flags, char **msg)
{
int r;
- or_options_t *trial_options = config_dup(&options_format, get_options());
+ or_options_t *trial_options = config_dup(get_options_mgr(), get_options());
- if ((r=config_assign(&options_format, trial_options,
+ if ((r=config_assign(get_options_mgr(), trial_options,
list, flags, msg)) < 0) {
or_options_free(trial_options);
return r;
@@ -2664,25 +2664,25 @@ print_usage(void)
static void
list_torrc_options(void)
{
- int i;
- for (i = 0; option_vars_[i].member.name; ++i) {
- const config_var_t *var = &option_vars_[i];
+ smartlist_t *vars = config_mgr_list_vars(get_options_mgr());
+ SMARTLIST_FOREACH_BEGIN(vars, const config_var_t *, var) {
if (! config_var_is_settable(var)) {
/* This variable cannot be set, or cannot be set by this name. */
continue;
}
printf("%s\n", var->member.name);
- }
+ } SMARTLIST_FOREACH_END(var);
+ smartlist_free(vars);
}
/** Print all deprecated but non-obsolete torrc options. */
static void
list_deprecated_options(void)
{
- const config_deprecation_t *d;
- for (d = option_deprecation_notes_; d->name; ++d) {
- printf("%s\n", d->name);
- }
+ smartlist_t *deps = config_mgr_list_deprecated_vars(get_options_mgr());
+ SMARTLIST_FOREACH(deps, const char *, name,
+ printf("%s\n", name));
+ smartlist_free(deps);
}
/** Print all compile-time modules and their enabled/disabled status. */
@@ -2992,7 +2992,7 @@ is_local_addr, (const tor_addr_t *addr))
or_options_t *
options_new(void)
{
- return config_new(&options_format);
+ return config_new(get_options_mgr());
}
/** Set <b>options</b> to hold reasonable defaults for most options.
@@ -3000,10 +3000,10 @@ options_new(void)
void
options_init(or_options_t *options)
{
- config_init(&options_format, options);
+ config_init(get_options_mgr(), options);
config_line_t *dflts = get_options_defaults();
char *msg=NULL;
- if (config_assign(&options_format, options, dflts,
+ if (config_assign(get_options_mgr(), options, dflts,
CAL_WARN_DEPRECATIONS, &msg)<0) {
log_err(LD_BUG, "Unable to set default options: %s", msg);
tor_free(msg);
@@ -3040,7 +3040,7 @@ options_dump(const or_options_t *options, int how_to_dump)
return NULL;
}
- return config_dump(&options_format, use_defaults, options, minimal, 0);
+ return config_dump(get_options_mgr(), use_defaults, options, minimal, 0);
}
/** Return 0 if every element of sl is a string holding a decimal
@@ -3172,13 +3172,6 @@ options_validate_cb(void *old_options, void *options, void *default_options,
return rv;
}
-/** Callback to free an or_options_t */
-static void
-options_free_cb(void *options)
-{
- or_options_free_(options);
-}
-
#define REJECT(arg) \
STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
#if defined(__GNUC__) && __GNUC__ <= 3
@@ -4435,7 +4428,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
STMT_BEGIN \
if (!options->TestingTorNetwork && \
!options->UsingTestNetworkDefaults_ && \
- !config_is_same(&options_format,options, \
+ !config_is_same(get_options_mgr(),options, \
default_options,#arg)) { \
REJECT(#arg " may only be changed in testing Tor " \
"networks!"); \
@@ -5411,8 +5404,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
oldoptions = global_options; /* get_options unfortunately asserts if
this is the first time we run*/
- newoptions = tor_malloc_zero(sizeof(or_options_t));
- newoptions->magic_ = OR_OPTIONS_MAGIC;
+ newoptions = options_new();
options_init(newoptions);
newoptions->command = command;
newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL;
@@ -5431,7 +5423,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
err = SETOPT_ERR_PARSE;
goto err;
}
- retval = config_assign(&options_format, newoptions, cl,
+ retval = config_assign(get_options_mgr(), newoptions, cl,
CAL_WARN_DEPRECATIONS, msg);
config_free_lines(cl);
if (retval < 0) {
@@ -5439,15 +5431,15 @@ options_init_from_string(const char *cf_defaults, const char *cf,
goto err;
}
if (i==0)
- newdefaultoptions = config_dup(&options_format, newoptions);
+ newdefaultoptions = config_dup(get_options_mgr(), newoptions);
}
if (newdefaultoptions == NULL) {
- newdefaultoptions = config_dup(&options_format, global_default_options);
+ newdefaultoptions = config_dup(get_options_mgr(), global_default_options);
}
/* Go through command-line variables too */
- retval = config_assign(&options_format, newoptions,
+ retval = config_assign(get_options_mgr(), newoptions,
global_cmdline_options, CAL_WARN_DEPRECATIONS, msg);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
@@ -8133,9 +8125,8 @@ getinfo_helper_config(control_connection_t *conn,
(void) errmsg;
if (!strcmp(question, "config/names")) {
smartlist_t *sl = smartlist_new();
- int i;
- for (i = 0; option_vars_[i].member.name; ++i) {
- const config_var_t *var = &option_vars_[i];
+ smartlist_t *vars = config_mgr_list_vars(get_options_mgr());
+ SMARTLIST_FOREACH_BEGIN(vars, const config_var_t *, var) {
/* don't tell controller about invisible options */
if (config_var_is_invisible(var))
continue;
@@ -8143,26 +8134,27 @@ getinfo_helper_config(control_connection_t *conn,
if (!type)
continue;
smartlist_add_asprintf(sl, "%s %s\n",var->member.name,type);
- }
+ } SMARTLIST_FOREACH_END(var);
*answer = smartlist_join_strings(sl, "", 0, NULL);
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
+ smartlist_free(vars);
} else if (!strcmp(question, "config/defaults")) {
smartlist_t *sl = smartlist_new();
int dirauth_lines_seen = 0, fallback_lines_seen = 0;
- for (int i = 0; option_vars_[i].member.name; ++i) {
- const config_var_t *var = &option_vars_[i];
+ smartlist_t *vars = config_mgr_list_vars(get_options_mgr());
+ SMARTLIST_FOREACH_BEGIN(vars, const config_var_t *, var) {
if (var->initvalue != NULL) {
- if (strcmp(option_vars_[i].member.name, "DirAuthority") == 0) {
+ if (strcmp(var->member.name, "DirAuthority") == 0) {
/*
* Count dirauth lines we have a default for; we'll use the
* count later to decide whether to add the defaults manually
*/
++dirauth_lines_seen;
}
- if (strcmp(option_vars_[i].member.name, "FallbackDir") == 0) {
+ if (strcmp(var->member.name, "FallbackDir") == 0) {
/*
- * Similarly count fallback lines, so that we can decided later
+ * Similarly count fallback lines, so that we can decide later
* to add the defaults manually.
*/
++fallback_lines_seen;
@@ -8171,7 +8163,8 @@ getinfo_helper_config(control_connection_t *conn,
smartlist_add_asprintf(sl, "%s %s\n",var->member.name,val);
tor_free(val);
}
- }
+ } SMARTLIST_FOREACH_END(var);
+ smartlist_free(vars);
if (dirauth_lines_seen == 0) {
/*
diff --git a/src/app/config/config.h b/src/app/config/config.h
index c6feb89fe7..44f09e5ee9 100644
--- a/src/app/config/config.h
+++ b/src/app/config/config.h
@@ -247,9 +247,8 @@ int options_any_client_port_set(const or_options_t *options);
#define CL_PORT_DFLT_GROUP_WRITABLE (1u<<7)
STATIC int options_act(const or_options_t *old_options);
-#ifdef TOR_UNIT_TESTS
-extern const struct config_format_t options_format;
-#endif
+struct config_mgr_t;
+STATIC const struct config_mgr_t *get_options_mgr(void);
STATIC port_cfg_t *port_cfg_new(size_t namelen);
#define port_cfg_free(port) \
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;
}
diff --git a/src/app/config/confparse.h b/src/app/config/confparse.h
index d6571692f9..0ff33f6edc 100644
--- a/src/app/config/confparse.h
+++ b/src/app/config/confparse.h
@@ -39,8 +39,16 @@ typedef struct config_deprecation_t {
* of arguments. */
typedef int (*validate_fn_t)(void*,void*,void*,int,char**);
-/** Callback to free a configuration object. */
-typedef void (*free_cfg_fn_t)(void*);
+struct config_mgr_t;
+
+/**
+ * Callback to clear all non-managed fields of a configuration object.
+ *
+ * (Regular fields get cleared by config_reset(), but you might have fields
+ * in the object that do not correspond to configuration variables. If those
+ * fields need to be cleared or freed, this is where to do it.)
+ */
+typedef void (*clear_cfg_fn_t)(const struct config_mgr_t *mgr, void*);
/** Information on the keys, value types, key-to-struct-member mappings,
* variable descriptions, validation functions, and abbreviations for a
@@ -55,51 +63,70 @@ typedef struct config_format_t {
* values, and where we stick them in the
* structure. */
validate_fn_t validate_fn; /**< Function to validate config. */
- free_cfg_fn_t free_fn; /**< Function to free the configuration. */
+ clear_cfg_fn_t clear_fn; /**< Function to clear the configuration. */
/** If present, extra denotes a LINELIST variable for unrecognized
* lines. Otherwise, unrecognized lines are an error. */
const struct_member_t *extra;
+ /** The position of a config_suite_t pointer within the toplevel object,
+ * or -1 if there is no such pointer. */
+ int config_suite_offset;
} config_format_t;
-/** Macro: assert that <b>cfg</b> has the right magic field for format
- * <b>fmt</b>. */
-#define CONFIG_CHECK(fmt, cfg) STMT_BEGIN \
- tor_assert(fmt); \
- struct_check_magic((cfg), &fmt->magic); \
- STMT_END
+/**
+ * A collection of config_format_t objects to describe several objects
+ * that are all configured with the same configuration file.
+ *
+ * (NOTE: for now, this only handles a single config_format_t.)
+ **/
+typedef struct config_mgr_t config_mgr_t;
+
+config_mgr_t *config_mgr_new(const config_format_t *toplevel_fmt);
+void config_mgr_free_(config_mgr_t *mgr);
+int config_mgr_add_format(config_mgr_t *mgr,
+ const config_format_t *fmt);
+void config_mgr_freeze(config_mgr_t *mgr);
+#define config_mgr_free(mgr) \
+ FREE_AND_NULL(config_mgr_t, config_mgr_free_, (mgr))
+struct smartlist_t *config_mgr_list_vars(const config_mgr_t *mgr);
+struct smartlist_t *config_mgr_list_deprecated_vars(const config_mgr_t *mgr);
+
+/** A collection of managed configuration objects. */
+typedef struct config_suite_t config_suite_t;
#define CAL_USE_DEFAULTS (1u<<0)
#define CAL_CLEAR_FIRST (1u<<1)
#define CAL_WARN_DEPRECATIONS (1u<<2)
-void *config_new(const config_format_t *fmt);
-void config_free_(const config_format_t *fmt, void *options);
-#define config_free(fmt, options) do { \
- config_free_((fmt), (options)); \
+void *config_new(const config_mgr_t *fmt);
+void config_free_(const config_mgr_t *fmt, void *options);
+#define config_free(mgr, options) do { \
+ config_free_((mgr), (options)); \
(options) = NULL; \
} while (0)
-struct config_line_t *config_get_assigned_option(const config_format_t *fmt,
+struct config_line_t *config_get_assigned_option(const config_mgr_t *mgr,
const void *options, const char *key,
int escape_val);
-int config_is_same(const config_format_t *fmt,
+int config_is_same(const config_mgr_t *fmt,
const void *o1, const void *o2,
const char *name);
-void config_init(const config_format_t *fmt, void *options);
-void *config_dup(const config_format_t *fmt, const void *old);
-char *config_dump(const config_format_t *fmt, const void *default_options,
+struct config_line_t *config_get_changes(const config_mgr_t *mgr,
+ const void *options1, const void *options2);
+void config_init(const config_mgr_t *mgr, void *options);
+void *config_dup(const config_mgr_t *mgr, const void *old);
+char *config_dump(const config_mgr_t *mgr, const void *default_options,
const void *options, int minimal,
int comment_defaults);
-bool config_check_ok(const config_format_t *fmt, const void *options,
+bool config_check_ok(const config_mgr_t *mgr, const void *options,
int severity);
-int config_assign(const config_format_t *fmt, void *options,
+int config_assign(const config_mgr_t *mgr, void *options,
struct config_line_t *list,
unsigned flags, char **msg);
-const char *config_find_deprecation(const config_format_t *fmt,
- const char *key);
-const config_var_t *config_find_option(const config_format_t *fmt,
- const char *key);
-const char *config_expand_abbrev(const config_format_t *fmt,
+const char *config_find_deprecation(const config_mgr_t *mgr,
+ const char *key);
+const char *config_find_option_name(const config_mgr_t *mgr,
+ const char *key);
+const char *config_expand_abbrev(const config_mgr_t *mgr,
const char *option,
int command_line, int warn_obsolete);
void warn_deprecated_option(const char *what, const char *why);
@@ -119,8 +146,12 @@ bool config_var_is_dumpable(const config_var_t *var);
#define CFG_EQ_ROUTERSET(a,b,opt) routerset_equal((a)->opt, (b)->opt)
#ifdef CONFPARSE_PRIVATE
-STATIC void config_reset_line(const config_format_t *fmt, void *options,
+STATIC void config_reset_line(const config_mgr_t *mgr, void *options,
const char *key, int use_defaults);
+STATIC void *config_mgr_get_obj_mutable(const config_mgr_t *mgr,
+ void *toplevel, int idx);
+STATIC const void *config_mgr_get_obj(const config_mgr_t *mgr,
+ const void *toplevel, int idx);
#endif
#endif /* !defined(TOR_CONFPARSE_H) */
diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h
index 8156d2ca11..32dcd9fb18 100644
--- a/src/app/config/or_options_st.h
+++ b/src/app/config/or_options_st.h
@@ -18,6 +18,7 @@
struct smartlist_t;
struct config_line_t;
+struct config_suite_t;
/** Enumeration of outbound address configuration types:
* Exit-only, OR-only, or both */
@@ -1107,6 +1108,14 @@ struct or_options_t {
* a possible previous dormant state.
**/
int DormantCanceledByStartup;
+
+ /**
+ * Configuration objects for individual modules.
+ *
+ * Never access this field or its members directly: instead, use the module
+ * in question to get its relevant configuration object.
+ */
+ struct config_suite_t *subconfigs_;
};
#endif /* !defined(TOR_OR_OPTIONS_ST_H) */
diff --git a/src/app/config/or_state_st.h b/src/app/config/or_state_st.h
index f45c6196cc..225003bb7e 100644
--- a/src/app/config/or_state_st.h
+++ b/src/app/config/or_state_st.h
@@ -15,6 +15,7 @@
#include "lib/cc/torint.h"
struct smartlist_t;
+struct config_suite_t;
/** Persistent state for an onion router, as saved to disk. */
struct or_state_t {
@@ -94,6 +95,14 @@ struct or_state_t {
/** True if we were dormant when we last wrote the file; false if we
* weren't. "auto" on initial startup. */
int Dormant;
+
+ /**
+ * State objects for individual modules.
+ *
+ * Never access this field or its members directly: instead, use the module
+ * in question to get its relevant state object if you must.
+ */
+ struct config_suite_t *substates_;
};
#endif /* !defined(TOR_OR_STATE_ST_H) */
diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c
index d997d3932e..bcc06809b3 100644
--- a/src/app/config/statefile.c
+++ b/src/app/config/statefile.c
@@ -145,8 +145,6 @@ static int or_state_validate_cb(void *old_options, void *options,
void *default_options,
int from_setconf, char **msg);
-static void or_state_free_cb(void *state);
-
/** Magic value for or_state_t. */
#define OR_STATE_MAGIC 0x57A73f57
@@ -170,10 +168,25 @@ static const config_format_t state_format = {
NULL,
state_vars_,
or_state_validate_cb,
- or_state_free_cb,
+ NULL,
&state_extra_var,
+ offsetof(or_state_t, substates_),
};
+/* A global configuration manager for state-file objects */
+static config_mgr_t *state_mgr = NULL;
+
+/** Return the configuration manager for state-file objects. */
+static const config_mgr_t *
+get_state_mgr(void)
+{
+ if (PREDICT_UNLIKELY(state_mgr == NULL)) {
+ state_mgr = config_mgr_new(&state_format);
+ config_mgr_freeze(state_mgr);
+ }
+ return state_mgr;
+}
+
/** Persistent serialized state. */
static or_state_t *global_state = NULL;
@@ -268,12 +281,6 @@ or_state_validate_cb(void *old_state, void *state, void *default_state,
return or_state_validate(state, msg);
}
-static void
-or_state_free_cb(void *state)
-{
- or_state_free_(state);
-}
-
/** Return 0 if every setting in <b>state</b> is reasonable, and a
* permissible transition from <b>old_state</b>. Else warn and return -1.
* Should have no side effects, except for normalizing the contents of
@@ -298,7 +305,7 @@ or_state_set(or_state_t *new_state)
char *err = NULL;
int ret = 0;
tor_assert(new_state);
- config_free(&state_format, global_state);
+ config_free(get_state_mgr(), global_state);
global_state = new_state;
if (entry_guards_parse_state(global_state, 1, &err)<0) {
log_warn(LD_GENERAL,"%s",err);
@@ -361,9 +368,8 @@ or_state_save_broken(char *fname)
STATIC or_state_t *
or_state_new(void)
{
- or_state_t *new_state = tor_malloc_zero(sizeof(or_state_t));
- new_state->magic_ = OR_STATE_MAGIC;
- config_init(&state_format, new_state);
+ or_state_t *new_state = config_new(get_state_mgr());
+ config_init(get_state_mgr(), new_state);
return new_state;
}
@@ -404,7 +410,7 @@ or_state_load(void)
int assign_retval;
if (config_get_lines(contents, &lines, 0)<0)
goto done;
- assign_retval = config_assign(&state_format, new_state,
+ assign_retval = config_assign(get_state_mgr(), new_state,
lines, 0, &errmsg);
config_free_lines(lines);
if (assign_retval<0)
@@ -431,7 +437,7 @@ or_state_load(void)
or_state_save_broken(fname);
tor_free(contents);
- config_free(&state_format, new_state);
+ config_free(get_state_mgr(), new_state);
new_state = or_state_new();
} else if (contents) {
@@ -464,7 +470,7 @@ or_state_load(void)
tor_free(fname);
tor_free(contents);
if (new_state)
- config_free(&state_format, new_state);
+ config_free(get_state_mgr(), new_state);
return r;
}
@@ -517,7 +523,7 @@ or_state_save(time_t now)
tor_free(global_state->TorVersion);
tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
- state = config_dump(&state_format, NULL, global_state, 1, 0);
+ state = config_dump(get_state_mgr(), NULL, global_state, 1, 0);
format_local_iso_time(tbuf, now);
tor_asprintf(&contents,
"# Tor state file last generated on %s local time\n"
@@ -727,7 +733,7 @@ or_state_free_(or_state_t *state)
if (!state)
return;
- config_free(&state_format, state);
+ config_free(get_state_mgr(), state);
}
void
@@ -735,4 +741,5 @@ or_state_free_all(void)
{
or_state_free(global_state);
global_state = NULL;
+ config_mgr_free(state_mgr);
}
diff --git a/src/feature/dirauth/shared_random_state.c b/src/feature/dirauth/shared_random_state.c
index c2ad3e7cca..f2a626c738 100644
--- a/src/feature/dirauth/shared_random_state.c
+++ b/src/feature/dirauth/shared_random_state.c
@@ -62,7 +62,6 @@ DUMMY_TYPECHECK_INSTANCE(sr_disk_state_t);
static int
disk_state_validate_cb(void *old_state, void *state, void *default_state,
int from_setconf, char **msg);
-static void disk_state_free_cb(void *);
/* Array of variables that are saved to disk as a persistent state. */
static const config_var_t state_vars[] = {
@@ -99,10 +98,25 @@ static const config_format_t state_format = {
NULL,
state_vars,
disk_state_validate_cb,
- disk_state_free_cb,
+ NULL,
&state_extra_var,
+ -1,
};
+/* Global configuration manager for the shared-random state file */
+static config_mgr_t *shared_random_state_mgr = NULL;
+
+/** Return the configuration manager for the shared-random state file. */
+static const config_mgr_t *
+get_srs_mgr(void)
+{
+ if (PREDICT_UNLIKELY(shared_random_state_mgr == NULL)) {
+ shared_random_state_mgr = config_mgr_new(&state_format);
+ config_mgr_freeze(shared_random_state_mgr);
+ }
+ return shared_random_state_mgr;
+}
+
static void state_query_del_(sr_state_object_t obj_type, void *data);
/* Return a string representation of a protocol phase. */
@@ -264,23 +278,22 @@ disk_state_free_(sr_disk_state_t *state)
if (state == NULL) {
return;
}
- config_free(&state_format, state);
+ config_free(get_srs_mgr(), state);
}
/* Allocate a new disk state, initialize it and return it. */
static sr_disk_state_t *
disk_state_new(time_t now)
{
- sr_disk_state_t *new_state = tor_malloc_zero(sizeof(*new_state));
+ sr_disk_state_t *new_state = config_new(get_srs_mgr());
- new_state->magic_ = SR_DISK_STATE_MAGIC;
new_state->Version = SR_PROTO_VERSION;
new_state->TorVersion = tor_strdup(get_version());
new_state->ValidUntil = get_state_valid_until_time(now);
new_state->ValidAfter = now;
/* Init config format. */
- config_init(&state_format, new_state);
+ config_init(get_srs_mgr(), new_state);
return new_state;
}
@@ -348,12 +361,6 @@ disk_state_validate_cb(void *old_state, void *state, void *default_state,
return 0;
}
-static void
-disk_state_free_cb(void *state)
-{
- disk_state_free_(state);
-}
-
/* Parse the Commit line(s) in the disk state and translate them to the
* the memory state. Return 0 on success else -1 on error. */
static int
@@ -584,11 +591,12 @@ disk_state_reset(void)
config_free_lines(sr_disk_state->ExtraLines);
tor_free(sr_disk_state->TorVersion);
- /* Clean up the struct */
- memset(sr_disk_state, 0, sizeof(*sr_disk_state));
+ /* Clear other fields. */
+ sr_disk_state->ValidAfter = 0;
+ sr_disk_state->ValidUntil = 0;
+ sr_disk_state->Version = 0;
/* Reset it with useful data */
- sr_disk_state->magic_ = SR_DISK_STATE_MAGIC;
sr_disk_state->TorVersion = tor_strdup(get_version());
}
@@ -683,7 +691,7 @@ disk_state_load_from_disk_impl(const char *fname)
}
disk_state = disk_state_new(time(NULL));
- config_assign(&state_format, disk_state, lines, 0, &errmsg);
+ config_assign(get_srs_mgr(), disk_state, lines, 0, &errmsg);
config_free_lines(lines);
if (errmsg) {
log_warn(LD_DIR, "SR: Reading state error: %s", errmsg);
@@ -736,7 +744,7 @@ disk_state_save_to_disk(void)
/* Make sure that our disk state is up to date with our memory state
* before saving it to disk. */
disk_state_update();
- state = config_dump(&state_format, NULL, sr_disk_state, 0, 0);
+ state = config_dump(get_srs_mgr(), NULL, sr_disk_state, 0, 0);
format_local_iso_time(tbuf, now);
tor_asprintf(&content,
"# Tor shared random state file last generated on %s "
@@ -1278,6 +1286,7 @@ sr_state_free_all(void)
/* Nullify our global state. */
sr_state = NULL;
sr_disk_state = NULL;
+ config_mgr_free(shared_random_state_mgr);
}
/* Save our current state in memory to disk. */
diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c
index 6d0f9d7d60..862acb2b35 100644
--- a/src/test/fuzz/fuzzing_common.c
+++ b/src/test/fuzz/fuzzing_common.c
@@ -1,6 +1,7 @@
/* Copyright (c) 2016-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define CRYPTO_ED25519_PRIVATE
+#define CONFIG_PRIVATE
#include "orconfig.h"
#include "core/or/or.h"
#include "app/main/subsysmgr.h"
@@ -111,7 +112,7 @@ global_init(void)
}
/* set up the options. */
- mock_options = tor_malloc_zero(sizeof(or_options_t));
+ mock_options = options_new();
MOCK(get_options, mock_get_options);
/* Make BUG() and nonfatal asserts crash */
@@ -189,7 +190,7 @@ main(int argc, char **argv)
if (fuzz_cleanup() < 0)
abort();
- tor_free(mock_options);
+ or_options_free(mock_options);
UNMOCK(get_options);
return 0;
}
diff --git a/src/test/include.am b/src/test/include.am
index 1e20f3f53f..1ec54a286f 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -124,6 +124,7 @@ src_test_test_SOURCES += \
src/test/test_circuitstats.c \
src/test/test_compat_libevent.c \
src/test/test_config.c \
+ src/test/test_confmgr.c \
src/test/test_confparse.c \
src/test/test_connection.c \
src/test/test_conscache.c \
diff --git a/src/test/test.c b/src/test/test.c
index b9a1da06f0..65e169b38e 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -840,6 +840,7 @@ struct testgroup_t testgroups[] = {
{ "circuituse/", circuituse_tests },
{ "compat/libevent/", compat_libevent_tests },
{ "config/", config_tests },
+ { "config/mgr/", confmgr_tests },
{ "config/parse/", confparse_tests },
{ "connection/", connection_tests },
{ "conscache/", conscache_tests },
diff --git a/src/test/test.h b/src/test/test.h
index f5c21bfe88..d6a1d19ea1 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -197,6 +197,7 @@ extern struct testcase_t circuitstats_tests[];
extern struct testcase_t circuituse_tests[];
extern struct testcase_t compat_libevent_tests[];
extern struct testcase_t config_tests[];
+extern struct testcase_t confmgr_tests[];
extern struct testcase_t confparse_tests[];
extern struct testcase_t connection_tests[];
extern struct testcase_t conscache_tests[];
diff --git a/src/test/test_config.c b/src/test/test_config.c
index a415ca4480..58e05e5094 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -1755,6 +1755,18 @@ add_default_fallback_dir_servers_known_default(void)
n_add_default_fallback_dir_servers_known_default++;
}
+/* Helper for test_config_adding_dir_servers(), which should be
+ * refactored: clear the fields in the options which the options object
+ * does not really own. */
+static void
+ads_clear_helper(or_options_t *options)
+{
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = NULL;
+ options->AlternateDirAuthority = NULL;
+ options->FallbackDir = NULL;
+}
+
/* Test all the different combinations of adding dir servers */
static void
test_config_adding_dir_servers(void *arg)
@@ -1762,7 +1774,7 @@ test_config_adding_dir_servers(void *arg)
(void)arg;
/* allocate options */
- or_options_t *options = tor_malloc_zero(sizeof(or_options_t));
+ or_options_t *options = options_new();
/* Allocate and populate configuration lines:
*
@@ -1885,7 +1897,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -1967,7 +1981,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2108,7 +2124,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2249,7 +2267,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2391,7 +2411,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2543,7 +2565,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2697,7 +2721,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -2860,7 +2886,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -3017,7 +3045,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -3183,7 +3213,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -3346,7 +3378,9 @@ test_config_adding_dir_servers(void *arg)
n_add_default_fallback_dir_servers_known_default = 0;
/* clear options*/
- memset(options, 0, sizeof(or_options_t));
+ ads_clear_helper(options);
+ or_options_free(options);
+ options = options_new();
/* clear any previous dir servers:
consider_adding_dir_servers() should do this anyway */
@@ -3515,10 +3549,7 @@ test_config_adding_dir_servers(void *arg)
tor_free(test_fallback_directory->value);
tor_free(test_fallback_directory);
- options->DirAuthorities = NULL;
- options->AlternateBridgeAuthority = NULL;
- options->AlternateDirAuthority = NULL;
- options->FallbackDir = NULL;
+ ads_clear_helper(options);
or_options_free(options);
UNMOCK(add_default_fallback_dir_servers);
@@ -3533,7 +3564,7 @@ test_config_default_dir_servers(void *arg)
int fallback_count = 0;
/* new set of options should stop fallback parsing */
- opts = tor_malloc_zero(sizeof(or_options_t));
+ opts = options_new();
opts->UseDefaultFallbackDirs = 0;
/* set old_options to NULL to force dir update */
consider_adding_dir_servers(opts, NULL);
@@ -3547,7 +3578,7 @@ test_config_default_dir_servers(void *arg)
/* if we disable the default fallbacks, there must not be any extra */
tt_assert(fallback_count == trusted_count);
- opts = tor_malloc_zero(sizeof(or_options_t));
+ opts = options_new();
opts->UseDefaultFallbackDirs = 1;
consider_adding_dir_servers(opts, opts);
trusted_count = smartlist_len(router_get_trusted_dir_servers());
@@ -3607,7 +3638,7 @@ test_config_directory_fetch(void *arg)
(void)arg;
/* Test Setup */
- or_options_t *options = tor_malloc_zero(sizeof(or_options_t));
+ or_options_t *options = options_new();
routerinfo_t routerinfo;
memset(&routerinfo, 0, sizeof(routerinfo));
mock_router_pick_published_address_result = -1;
@@ -3619,9 +3650,10 @@ test_config_directory_fetch(void *arg)
mock_router_my_exit_policy_is_reject_star);
MOCK(advertised_server_mode, mock_advertised_server_mode);
MOCK(router_get_my_routerinfo, mock_router_get_my_routerinfo);
+ or_options_free(options);
+ options = options_new();
/* Clients can use multiple directory mirrors for bootstrap */
- memset(options, 0, sizeof(or_options_t));
options->ClientOnly = 1;
tt_assert(server_mode(options) == 0);
tt_assert(public_server_mode(options) == 0);
@@ -3630,7 +3662,8 @@ test_config_directory_fetch(void *arg)
OP_EQ, 1);
/* Bridge Clients can use multiple directory mirrors for bootstrap */
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->UseBridges = 1;
tt_assert(server_mode(options) == 0);
tt_assert(public_server_mode(options) == 0);
@@ -3640,7 +3673,8 @@ test_config_directory_fetch(void *arg)
/* Bridge Relays (Bridges) must act like clients, and use multiple
* directory mirrors for bootstrap */
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->BridgeRelay = 1;
options->ORPort_set = 1;
tt_assert(server_mode(options) == 1);
@@ -3651,7 +3685,8 @@ test_config_directory_fetch(void *arg)
/* Clients set to FetchDirInfoEarly must fetch it from the authorities,
* but can use multiple authorities for bootstrap */
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->FetchDirInfoEarly = 1;
tt_assert(server_mode(options) == 0);
tt_assert(public_server_mode(options) == 0);
@@ -3662,7 +3697,8 @@ test_config_directory_fetch(void *arg)
/* OR servers only fetch the consensus from the authorities when they don't
* know their own address, but never use multiple directories for bootstrap
*/
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->ORPort_set = 1;
mock_router_pick_published_address_result = -1;
@@ -3682,7 +3718,8 @@ test_config_directory_fetch(void *arg)
/* Exit OR servers only fetch the consensus from the authorities when they
* refuse unknown exits, but never use multiple directories for bootstrap
*/
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->ORPort_set = 1;
options->ExitRelay = 1;
mock_router_pick_published_address_result = 0;
@@ -3712,7 +3749,8 @@ test_config_directory_fetch(void *arg)
* advertising their dirport, and never use multiple directories for
* bootstrap. This only applies if they are also OR servers.
* (We don't care much about the behaviour of non-OR directory servers.) */
- memset(options, 0, sizeof(or_options_t));
+ or_options_free(options);
+ options = options_new();
options->DirPort_set = 1;
options->ORPort_set = 1;
options->DirCache = 1;
@@ -3766,7 +3804,7 @@ test_config_directory_fetch(void *arg)
OP_EQ, 0);
done:
- tor_free(options);
+ or_options_free(options);
UNMOCK(router_pick_published_address);
UNMOCK(router_get_my_routerinfo);
UNMOCK(advertised_server_mode);
diff --git a/src/test/test_confmgr.c b/src/test/test_confmgr.c
new file mode 100644
index 0000000000..5f73d9754b
--- /dev/null
+++ b/src/test/test_confmgr.c
@@ -0,0 +1,325 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/*
+ * Tests for confparse.c's features that support multiple configuration
+ * formats and configuration objects.
+ */
+
+#define CONFPARSE_PRIVATE
+#include "orconfig.h"
+
+#include "core/or/or.h"
+#include "lib/encoding/confline.h"
+#include "app/config/confparse.h"
+#include "test/test.h"
+#include "test/log_test_helpers.h"
+
+/*
+ * Set up a few objects: a pasture_cfg is toplevel; it has a llama_cfg and an
+ * alpaca_cfg.
+ */
+
+typedef struct {
+ uint32_t magic;
+ char *address;
+ int opentopublic;
+ config_suite_t *subobjs;
+} pasture_cfg_t;
+
+typedef struct {
+ char *llamaname;
+ int cuteness;
+ uint32_t magic;
+ int eats_meat; /* deprecated; llamas are never carnivorous. */
+
+ char *description; // derived from other fields.
+} llama_cfg_t;
+
+typedef struct {
+ uint32_t magic;
+ int fuzziness;
+ char *alpacaname;
+ int n_wings; /* deprecated; alpacas don't have wings. */
+} alpaca_cfg_t;
+
+/*
+ * Make the above into configuration objects.
+ */
+
+static pasture_cfg_t pasture_cfg_t_dummy;
+static llama_cfg_t llama_cfg_t_dummy;
+static alpaca_cfg_t alpaca_cfg_t_dummy;
+
+#define PV(name, type, dflt) \
+ CONFIG_VAR_ETYPE(pasture_cfg_t, #name, type, name, 0, dflt)
+#define LV(name, type, dflt) \
+ CONFIG_VAR_ETYPE(llama_cfg_t, #name, type, name, 0, dflt)
+#define AV(name, type, dflt) \
+ CONFIG_VAR_ETYPE(alpaca_cfg_t, #name, type, name, 0, dflt)
+static const config_var_t pasture_vars[] = {
+ PV(address, STRING, NULL),
+ PV(opentopublic, BOOL, "1"),
+ END_OF_CONFIG_VARS
+};
+static const config_var_t llama_vars[] =
+{
+ LV(llamaname, STRING, NULL),
+ LV(eats_meat, BOOL, NULL),
+ LV(cuteness, POSINT, "100"),
+ END_OF_CONFIG_VARS
+};
+static const config_var_t alpaca_vars[] =
+{
+ AV(alpacaname, STRING, NULL),
+ AV(fuzziness, POSINT, "50"),
+ AV(n_wings, POSINT, "0"),
+ END_OF_CONFIG_VARS
+};
+
+static config_deprecation_t llama_deprecations[] = {
+ { "eats_meat", "Llamas are herbivores." },
+ {NULL,NULL}
+};
+
+static config_deprecation_t alpaca_deprecations[] = {
+ { "n_wings", "Alpacas are quadrupeds." },
+ {NULL,NULL}
+};
+
+static int clear_llama_cfg_called = 0;
+static void
+clear_llama_cfg(const config_mgr_t *mgr, void *llamacfg)
+{
+ (void)mgr;
+ llama_cfg_t *lc = llamacfg;
+ tor_free(lc->description);
+ ++clear_llama_cfg_called;
+}
+
+static config_abbrev_t llama_abbrevs[] = {
+ { "gracia", "cuteness", 0, 0 },
+ { "gentillesse", "cuteness", 0, 0 },
+ { NULL, NULL, 0, 0 },
+};
+
+static const config_format_t pasture_fmt = {
+ sizeof(pasture_cfg_t),
+ {
+ "pasture_cfg_t",
+ 8989,
+ offsetof(pasture_cfg_t, magic)
+ },
+ .vars = pasture_vars,
+ .config_suite_offset = offsetof(pasture_cfg_t, subobjs),
+};
+
+static const config_format_t llama_fmt = {
+ sizeof(llama_cfg_t),
+ {
+ "llama_cfg_t",
+ 0x11aa11,
+ offsetof(llama_cfg_t, magic)
+ },
+ .vars = llama_vars,
+ .config_suite_offset = -1,
+ .deprecations = llama_deprecations,
+ .abbrevs = llama_abbrevs,
+ .clear_fn = clear_llama_cfg,
+};
+
+static const config_format_t alpaca_fmt = {
+ sizeof(alpaca_cfg_t),
+ {
+ "alpaca_cfg_t",
+ 0xa15aca,
+ offsetof(alpaca_cfg_t, magic)
+ },
+ .vars = alpaca_vars,
+ .config_suite_offset = -1,
+ .deprecations = alpaca_deprecations,
+};
+
+#define LLAMA_IDX 0
+#define ALPACA_IDX 1
+
+static config_mgr_t *
+get_mgr(bool freeze)
+{
+ config_mgr_t *mgr = config_mgr_new(&pasture_fmt);
+ tt_int_op(LLAMA_IDX, OP_EQ, config_mgr_add_format(mgr, &llama_fmt));
+ tt_int_op(ALPACA_IDX, OP_EQ, config_mgr_add_format(mgr, &alpaca_fmt));
+ if (freeze)
+ config_mgr_freeze(mgr);
+ return mgr;
+
+ done:
+ config_mgr_free(mgr);
+ return NULL;
+}
+
+static void
+test_confmgr_init(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = get_mgr(true);
+ smartlist_t *vars = NULL;
+ tt_ptr_op(mgr, OP_NE, NULL);
+
+ vars = config_mgr_list_vars(mgr);
+ tt_int_op(smartlist_len(vars), OP_EQ, 8); // 8 vars total.
+
+ tt_str_op("cuteness", OP_EQ, config_find_option_name(mgr, "CUTENESS"));
+ tt_str_op("cuteness", OP_EQ, config_find_option_name(mgr, "GRACIA"));
+ smartlist_free(vars);
+
+ vars = config_mgr_list_deprecated_vars(mgr); // 2 deprecated vars.
+ tt_int_op(smartlist_len(vars), OP_EQ, 2);
+ tt_assert(smartlist_contains_string(vars, "eats_meat"));
+ tt_assert(smartlist_contains_string(vars, "n_wings"));
+
+ tt_str_op("Llamas are herbivores.", OP_EQ,
+ config_find_deprecation(mgr, "EATS_MEAT"));
+ tt_str_op("Alpacas are quadrupeds.", OP_EQ,
+ config_find_deprecation(mgr, "N_WINGS"));
+
+ done:
+ smartlist_free(vars);
+ config_mgr_free(mgr);
+}
+
+static void
+test_confmgr_magic(void *args)
+{
+ (void)args;
+ // Every time we build a manager, it is supposed to get a different magic
+ // number. Let's test that.
+ config_mgr_t *mgr1 = get_mgr(true);
+ config_mgr_t *mgr2 = get_mgr(true);
+ config_mgr_t *mgr3 = get_mgr(true);
+
+ pasture_cfg_t *p1 = NULL, *p2 = NULL, *p3 = NULL;
+
+ tt_assert(mgr1);
+ tt_assert(mgr2);
+ tt_assert(mgr3);
+
+ p1 = config_new(mgr1);
+ p2 = config_new(mgr2);
+ p3 = config_new(mgr3);
+
+ tt_assert(p1);
+ tt_assert(p2);
+ tt_assert(p3);
+
+ // By chance, two managers get the same magic with P=2^-32. Let's
+ // make sure that at least two of them are different, so that our
+ // odds of a false positive are 1/2^-64.
+ tt_assert((p1->magic != p2->magic) || (p2->magic != p3->magic));
+
+ done:
+ config_free(mgr1, p1);
+ config_free(mgr2, p2);
+ config_free(mgr3, p3);
+
+ config_mgr_free(mgr1);
+ config_mgr_free(mgr2);
+ config_mgr_free(mgr3);
+}
+
+static const char *simple_pasture =
+ "LLamaname hugo\n"
+ "Alpacaname daphne\n"
+ "gentillesse 42\n"
+ "address 123 Camelid ave\n";
+
+static void
+test_confmgr_parse(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = get_mgr(true);
+ pasture_cfg_t *p = config_new(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+
+ config_init(mgr, p); // set defaults.
+
+ int r = config_get_lines(simple_pasture, &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ r = config_assign(mgr, p, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ tt_int_op(p->opentopublic, OP_EQ, 1);
+ tt_str_op(p->address, OP_EQ, "123 Camelid ave");
+
+ // We are using this API directly; modules outside confparse will, in the
+ // future, not.
+ const alpaca_cfg_t *ac = config_mgr_get_obj(mgr, p, ALPACA_IDX);
+ const llama_cfg_t *lc = config_mgr_get_obj(mgr, p, LLAMA_IDX);
+ tt_str_op(lc->llamaname, OP_EQ, "hugo");
+ tt_str_op(ac->alpacaname, OP_EQ, "daphne");
+ tt_int_op(lc->cuteness, OP_EQ, 42);
+ tt_int_op(ac->fuzziness, OP_EQ, 50);
+
+ // We set the description for the llama here, so that the clear function
+ // can clear it. (Later we can do this in a verification function.)
+ clear_llama_cfg_called = 0;
+ llama_cfg_t *mut_lc = config_mgr_get_obj_mutable(mgr, p, LLAMA_IDX);
+ mut_lc->description = tor_strdup("A llama named Hugo.");
+ config_free(mgr, p);
+ tt_int_op(clear_llama_cfg_called, OP_EQ, 1);
+
+ done:
+ config_free_lines(lines);
+ config_free(mgr, p);
+ config_mgr_free(mgr);
+ tor_free(msg);
+}
+
+static void
+test_confmgr_dump(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = get_mgr(true);
+ pasture_cfg_t *p = config_new(mgr);
+ pasture_cfg_t *defaults = config_new(mgr);
+ config_line_t *lines = NULL;
+ char *msg = NULL;
+ char *s = NULL;
+
+ config_init(mgr, p); // set defaults.
+ config_init(mgr, defaults); // set defaults.
+
+ int r = config_get_lines(simple_pasture, &lines, 0);
+ tt_int_op(r, OP_EQ, 0);
+ r = config_assign(mgr, p, lines, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ s = config_dump(mgr, defaults, p, 1, 0);
+ tt_str_op("address 123 Camelid ave\n"
+ "alpacaname daphne\n"
+ "cuteness 42\n"
+ "llamaname hugo\n", OP_EQ, s);
+
+ done:
+ config_free_lines(lines);
+ config_free(mgr, p);
+ config_free(mgr, defaults);
+ config_mgr_free(mgr);
+
+ tor_free(msg);
+ tor_free(s);
+}
+
+#define CONFMGR_TEST(name, flags) \
+ { #name, test_confmgr_ ## name, flags, NULL, NULL }
+
+struct testcase_t confmgr_tests[] = {
+ CONFMGR_TEST(init, 0),
+ CONFMGR_TEST(magic, 0),
+ CONFMGR_TEST(parse, 0),
+ CONFMGR_TEST(dump, 0),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_confparse.c b/src/test/test_confparse.c
index ec018f0c52..f04c412c0e 100644
--- a/src/test/test_confparse.c
+++ b/src/test/test_confparse.c
@@ -119,8 +119,6 @@ test_validate_cb(void *old_options, void *options, void *default_options,
return 0;
}
-static void test_free_cb(void *options);
-
#define TEST_MAGIC 0x1337
static const config_format_t test_fmt = {
@@ -134,29 +132,22 @@ static const config_format_t test_fmt = {
test_deprecation_notes,
test_vars,
test_validate_cb,
- test_free_cb,
NULL,
+ NULL,
+ -1,
};
-static void
-test_free_cb(void *options)
-{
- if (!options)
- return;
-
- config_free(&test_fmt, options);
-}
-
/* Make sure that config_init sets everything to the right defaults. */
static void
test_confparse_init(void *arg)
{
(void)arg;
- test_struct_t *tst = config_new(&test_fmt);
- config_init(&test_fmt, tst);
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = config_new(mgr);
+ config_init(mgr, tst);
// Make sure that options are initialized right. */
- tt_uint_op(tst->magic, OP_EQ, TEST_MAGIC);
tt_str_op(tst->s, OP_EQ, "hello");
tt_ptr_op(tst->fn, OP_EQ, NULL);
tt_int_op(tst->pos, OP_EQ, 0);
@@ -178,7 +169,8 @@ test_confparse_init(void *arg)
tt_int_op(tst->hidden_int, OP_EQ, 0);
done:
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
+ config_mgr_free(mgr);
}
static const char simple_settings[] =
@@ -207,18 +199,18 @@ static const char simple_settings[] =
/* Return a configuration object set up from simple_settings above. */
static test_struct_t *
-get_simple_config(void)
+get_simple_config(const config_mgr_t *mgr)
{
test_struct_t *result = NULL;
- test_struct_t *tst = config_new(&test_fmt);
+ test_struct_t *tst = config_new(mgr);
config_line_t *lines = NULL;
char *msg = NULL;
- config_init(&test_fmt, tst);
+ config_init(mgr, tst);
int r = config_get_lines(simple_settings, &lines, 0);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&test_fmt, tst, lines, 0, &msg);
+ r = config_assign(mgr, tst, lines, 0, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
@@ -227,7 +219,7 @@ get_simple_config(void)
done:
tor_free(msg);
config_free_lines(lines);
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
return result;
}
@@ -236,7 +228,9 @@ static void
test_confparse_assign_simple(void *arg)
{
(void)arg;
- test_struct_t *tst = get_simple_config();
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
tt_str_op(tst->s, OP_EQ, "this is a");
tt_str_op(tst->fn, OP_EQ, "/simple/test of the");
@@ -284,10 +278,11 @@ test_confparse_assign_simple(void *arg)
tt_str_op(tst->mixed_hidden_lines->next->value, OP_EQ, "ABC");
tt_assert(!tst->mixed_hidden_lines->next->next);
- tt_assert(config_check_ok(&test_fmt, tst, LOG_ERR));
+ tt_assert(config_check_ok(mgr, tst, LOG_ERR));
done:
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
+ config_mgr_free(mgr);
}
/* Try to assign to an obsolete option, and make sure we get a warning. */
@@ -295,26 +290,29 @@ static void
test_confparse_assign_obsolete(void *arg)
{
(void)arg;
- test_struct_t *tst = config_new(&test_fmt);
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
config_line_t *lines = NULL;
char *msg = NULL;
- config_init(&test_fmt, tst);
+ config_init(mgr, tst);
int r = config_get_lines("obsolete option here",
&lines, 0);
tt_int_op(r, OP_EQ, 0);
setup_capture_of_logs(LOG_WARN);
- r = config_assign(&test_fmt, tst, lines, 0, &msg);
+ r = config_assign(mgr, tst, lines, 0, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
expect_single_log_msg_containing("Skipping obsolete configuration option");
done:
teardown_capture_of_logs();
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
config_free_lines(lines);
tor_free(msg);
+ config_mgr_free(mgr);
}
/* Try to assign to an deprecated option, and make sure we get a warning
@@ -323,30 +321,33 @@ static void
test_confparse_assign_deprecated(void *arg)
{
(void)arg;
- test_struct_t *tst = config_new(&test_fmt);
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
config_line_t *lines = NULL;
char *msg = NULL;
- config_init(&test_fmt, tst);
+ config_init(mgr, tst);
int r = config_get_lines("deprecated_int 7",
&lines, 0);
tt_int_op(r, OP_EQ, 0);
setup_capture_of_logs(LOG_WARN);
- r = config_assign(&test_fmt, tst, lines, CAL_WARN_DEPRECATIONS, &msg);
+ r = config_assign(mgr, tst, lines, CAL_WARN_DEPRECATIONS, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
expect_single_log_msg_containing("This integer is deprecated.");
tt_int_op(tst->deprecated_int, OP_EQ, 7);
- tt_assert(config_check_ok(&test_fmt, tst, LOG_ERR));
+ tt_assert(config_check_ok(mgr, tst, LOG_ERR));
done:
teardown_capture_of_logs();
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
config_free_lines(lines);
tor_free(msg);
+ config_mgr_free(mgr);
}
/* Try to re-assign an option name that has been depreacted in favor of
@@ -355,16 +356,18 @@ static void
test_confparse_assign_replaced(void *arg)
{
(void)arg;
- test_struct_t *tst = config_new(&test_fmt);
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
config_line_t *lines = NULL;
char *msg = NULL;
- config_init(&test_fmt, tst);
+ config_init(mgr, tst);
int r = config_get_lines("float 1000\n", &lines, 0);
tt_int_op(r, OP_EQ, 0);
setup_capture_of_logs(LOG_WARN);
- r = config_assign(&test_fmt, tst, lines, CAL_WARN_DEPRECATIONS, &msg);
+ r = config_assign(mgr, tst, lines, CAL_WARN_DEPRECATIONS, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
expect_single_log_msg_containing("use 'dbl' instead.");
@@ -374,9 +377,10 @@ test_confparse_assign_replaced(void *arg)
done:
teardown_capture_of_logs();
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
config_free_lines(lines);
tor_free(msg);
+ config_mgr_free(mgr);
}
/* Try to set a linelist value with no option. */
@@ -384,25 +388,28 @@ static void
test_confparse_assign_emptystring(void *arg)
{
(void)arg;
- test_struct_t *tst = config_new(&test_fmt);
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
config_line_t *lines = NULL;
char *msg = NULL;
- config_init(&test_fmt, tst);
+ config_init(mgr, tst);
int r = config_get_lines("lines\n", &lines, 0);
tt_int_op(r, OP_EQ, 0);
setup_capture_of_logs(LOG_WARN);
- r = config_assign(&test_fmt, tst, lines, 0, &msg);
+ r = config_assign(mgr, tst, lines, 0, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
expect_single_log_msg_containing("has no value");
done:
teardown_capture_of_logs();
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
config_free_lines(lines);
tor_free(msg);
+ config_mgr_free(mgr);
}
/* Try to set a the same option twice; make sure we get a warning. */
@@ -410,26 +417,29 @@ static void
test_confparse_assign_twice(void *arg)
{
(void)arg;
- test_struct_t *tst = config_new(&test_fmt);
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
config_line_t *lines = NULL;
char *msg = NULL;
- config_init(&test_fmt, tst);
+ config_init(mgr, tst);
int r = config_get_lines("pos 10\n"
"pos 99\n", &lines, 0);
tt_int_op(r, OP_EQ, 0);
setup_capture_of_logs(LOG_WARN);
- r = config_assign(&test_fmt, tst, lines, 0, &msg);
+ r = config_assign(mgr, tst, lines, 0, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
expect_single_log_msg_containing("used more than once");
done:
teardown_capture_of_logs();
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
config_free_lines(lines);
tor_free(msg);
+ config_mgr_free(mgr);
}
typedef struct badval_test_t {
@@ -443,16 +453,18 @@ static void
test_confparse_assign_badval(void *arg)
{
const badval_test_t *bt = arg;
- test_struct_t *tst = config_new(&test_fmt);
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
config_line_t *lines = NULL;
char *msg = NULL;
- config_init(&test_fmt, tst);
+ config_init(mgr, tst);
int r = config_get_lines(bt->cfg, &lines, 0);
tt_int_op(r, OP_EQ, 0);
setup_capture_of_logs(LOG_WARN);
- r = config_assign(&test_fmt, tst, lines, 0, &msg);
+ r = config_assign(mgr, tst, lines, 0, &msg);
tt_int_op(r, OP_LT, 0);
tt_ptr_op(msg, OP_NE, NULL);
if (! strstr(msg, bt->expect_msg)) {
@@ -461,9 +473,10 @@ test_confparse_assign_badval(void *arg)
done:
teardown_capture_of_logs();
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
config_free_lines(lines);
tor_free(msg);
+ config_mgr_free(mgr);
}
/* Various arguments for badval test.
@@ -495,88 +508,90 @@ static void
test_confparse_dump(void *arg)
{
(void)arg;
- test_struct_t *tst = get_simple_config();
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
char *dumped = NULL;
/* Minimal version. */
- dumped = config_dump(&test_fmt, NULL, tst, 1, 0);
+ dumped = config_dump(mgr, NULL, tst, 1, 0);
tt_str_op(dumped, OP_EQ,
- "s this is a\n"
- "fn /simple/test of the\n"
- "pos 77\n"
- "i 3\n"
- "u64 1000000000000\n"
- "interval 300\n"
- "msec_interval 300000\n"
- "mem 10\n"
- "dbl 6.060842\n"
- "boolean 1\n"
"autobool 0\n"
- "time 2019-06-14 13:58:51\n"
+ "boolean 1\n"
"csv configuration,parsing,system\n"
"csv_interval 10\n"
+ "dbl 6.060842\n"
+ "fn /simple/test of the\n"
+ "i 3\n"
+ "interval 300\n"
"lines hello\n"
"lines world\n"
+ "mem 10\n"
+ "VisibleLineB ABC\n"
"LineTypeA i d\n"
"LineTypeB i c\n"
+ "msec_interval 300000\n"
+ "pos 77\n"
"routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n"
- "VisibleLineB ABC\n");
+ "s this is a\n"
+ "time 2019-06-14 13:58:51\n"
+ "u64 1000000000000\n");
- /* Maximal */
tor_free(dumped);
- dumped = config_dump(&test_fmt, NULL, tst, 0, 0);
+ dumped = config_dump(mgr, NULL, tst, 0, 0);
tt_str_op(dumped, OP_EQ,
- "s this is a\n"
- "fn /simple/test of the\n"
- "pos 77\n"
- "i 3\n"
- "deprecated_int 3\n"
- "u64 1000000000000\n"
- "interval 300\n"
- "msec_interval 300000\n"
- "mem 10\n"
- "dbl 6.060842\n"
- "boolean 1\n"
"autobool 0\n"
- "time 2019-06-14 13:58:51\n"
+ "boolean 1\n"
"csv configuration,parsing,system\n"
"csv_interval 10\n"
+ "dbl 6.060842\n"
+ "deprecated_int 3\n"
+ "fn /simple/test of the\n"
+ "i 3\n"
+ "interval 300\n"
"lines hello\n"
"lines world\n"
+ "mem 10\n"
+ "VisibleLineB ABC\n"
"LineTypeA i d\n"
"LineTypeB i c\n"
+ "msec_interval 300000\n"
+ "pos 77\n"
"routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n"
- "VisibleLineB ABC\n");
+ "s this is a\n"
+ "time 2019-06-14 13:58:51\n"
+ "u64 1000000000000\n");
/* commented */
tor_free(dumped);
- dumped = config_dump(&test_fmt, NULL, tst, 0, 1);
+ dumped = config_dump(mgr, NULL, tst, 0, 1);
tt_str_op(dumped, OP_EQ,
- "s this is a\n"
- "fn /simple/test of the\n"
- "pos 77\n"
- "i 3\n"
- "# deprecated_int 3\n"
- "u64 1000000000000\n"
- "interval 300\n"
- "msec_interval 300000\n"
- "mem 10\n"
- "dbl 6.060842\n"
- "boolean 1\n"
"autobool 0\n"
- "time 2019-06-14 13:58:51\n"
+ "boolean 1\n"
"csv configuration,parsing,system\n"
"csv_interval 10\n"
+ "dbl 6.060842\n"
+ "# deprecated_int 3\n"
+ "fn /simple/test of the\n"
+ "i 3\n"
+ "interval 300\n"
"lines hello\n"
"lines world\n"
+ "mem 10\n"
+ "VisibleLineB ABC\n"
"LineTypeA i d\n"
"LineTypeB i c\n"
+ "msec_interval 300000\n"
+ "pos 77\n"
"routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n"
- "VisibleLineB ABC\n");
+ "s this is a\n"
+ "time 2019-06-14 13:58:51\n"
+ "u64 1000000000000\n");
done:
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
tor_free(dumped);
+ config_mgr_free(mgr);
}
/* Try confparse_reset_line(), and make sure it behaves correctly */
@@ -584,16 +599,19 @@ static void
test_confparse_reset(void *arg)
{
(void)arg;
- test_struct_t *tst = get_simple_config();
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
- config_reset_line(&test_fmt, tst, "interval", 0);
+ config_reset_line(mgr, tst, "interval", 0);
tt_int_op(tst->interval, OP_EQ, 0);
- config_reset_line(&test_fmt, tst, "interval", 1);
+ config_reset_line(mgr, tst, "interval", 1);
tt_int_op(tst->interval, OP_EQ, 10);
done:
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
+ config_mgr_free(mgr);
}
/* Try setting options a second time on a config object, and make sure
@@ -602,7 +620,9 @@ static void
test_confparse_reassign(void *arg)
{
(void)arg;
- test_struct_t *tst = get_simple_config();
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
config_line_t *lines = NULL;
char *msg = NULL, *rs = NULL;
@@ -613,7 +633,7 @@ test_confparse_reassign(void *arg)
"csv 14,15\n"
"routerset 127.0.0.1\n",
&lines, 0);
- r = config_assign(&test_fmt, tst,lines, 0, &msg);
+ r = config_assign(mgr, tst,lines, 0, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
@@ -633,7 +653,7 @@ test_confparse_reassign(void *arg)
tt_str_op(rs, OP_EQ, "127.0.0.1");
// Try again with the CLEAR_FIRST and USE_DEFAULTS flags
- r = config_assign(&test_fmt, tst, lines,
+ r = config_assign(mgr, tst, lines,
CAL_CLEAR_FIRST|CAL_USE_DEFAULTS, &msg);
tt_int_op(r, OP_EQ, 0);
@@ -644,10 +664,11 @@ test_confparse_reassign(void *arg)
tt_int_op(tst->i, OP_EQ, 12);
done:
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
config_free_lines(lines);
tor_free(msg);
tor_free(rs);
+ config_mgr_free(mgr);
}
/* Try setting options a second time on a config object, using the +foo
@@ -656,7 +677,9 @@ static void
test_confparse_reassign_extend(void *arg)
{
(void)arg;
- test_struct_t *tst = get_simple_config();
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
config_line_t *lines = NULL;
char *msg = NULL;
@@ -664,7 +687,7 @@ test_confparse_reassign_extend(void *arg)
"+lines 13\n",
&lines, 1); // allow extended format.
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&test_fmt, tst,lines, 0, &msg);
+ r = config_assign(mgr, tst,lines, 0, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
@@ -684,27 +707,28 @@ test_confparse_reassign_extend(void *arg)
"/lines\n",
&lines, 1); // allow extended format.
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&test_fmt, tst, lines, 0, &msg);
+ r = config_assign(mgr, tst, lines, 0, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
tt_assert(tst->lines == NULL);
config_free_lines(lines);
- config_free(&test_fmt, tst);
- tst = get_simple_config();
+ config_free(mgr, tst);
+ tst = get_simple_config(mgr);
r = config_get_lines(
"/lines away!\n",
&lines, 1); // allow extended format.
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&test_fmt, tst, lines, 0, &msg);
+ r = config_assign(mgr, tst, lines, 0, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
tt_assert(tst->lines == NULL);
done:
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
config_free_lines(lines);
tor_free(msg);
+ config_mgr_free(mgr);
}
/* Test out confparse_get_assigned(). */
@@ -712,30 +736,33 @@ static void
test_confparse_get_assigned(void *arg)
{
(void)arg;
- test_struct_t *tst = get_simple_config();
+
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = get_simple_config(mgr);
config_line_t *lines = NULL;
- lines = config_get_assigned_option(&test_fmt, tst, "I", 1);
+ lines = config_get_assigned_option(mgr, tst, "I", 1);
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "i");
tt_str_op(lines->value, OP_EQ, "3");
tt_assert(lines->next == NULL);
config_free_lines(lines);
- lines = config_get_assigned_option(&test_fmt, tst, "s", 1);
+ lines = config_get_assigned_option(mgr, tst, "s", 1);
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "s");
tt_str_op(lines->value, OP_EQ, "this is a");
tt_assert(lines->next == NULL);
config_free_lines(lines);
- lines = config_get_assigned_option(&test_fmt, tst, "obsolete", 1);
+ lines = config_get_assigned_option(mgr, tst, "obsolete", 1);
tt_assert(!lines);
- lines = config_get_assigned_option(&test_fmt, tst, "nonesuch", 1);
+ lines = config_get_assigned_option(mgr, tst, "nonesuch", 1);
tt_assert(!lines);
- lines = config_get_assigned_option(&test_fmt, tst, "mixedlines", 1);
+ lines = config_get_assigned_option(mgr, tst, "mixedlines", 1);
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "LineTypeA");
tt_str_op(lines->value, OP_EQ, "i d");
@@ -745,7 +772,7 @@ test_confparse_get_assigned(void *arg)
tt_assert(lines->next->next == NULL);
config_free_lines(lines);
- lines = config_get_assigned_option(&test_fmt, tst, "linetypeb", 1);
+ lines = config_get_assigned_option(mgr, tst, "linetypeb", 1);
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "LineTypeB");
tt_str_op(lines->value, OP_EQ, "i c");
@@ -754,7 +781,7 @@ test_confparse_get_assigned(void *arg)
tor_free(tst->s);
tst->s = tor_strdup("Hello\nWorld");
- lines = config_get_assigned_option(&test_fmt, tst, "s", 1);
+ lines = config_get_assigned_option(mgr, tst, "s", 1);
tt_assert(lines);
tt_str_op(lines->key, OP_EQ, "s");
tt_str_op(lines->value, OP_EQ, "\"Hello\\nWorld\"");
@@ -762,8 +789,9 @@ test_confparse_get_assigned(void *arg)
config_free_lines(lines);
done:
- config_free(&test_fmt, tst);
+ config_free(mgr, tst);
config_free_lines(lines);
+ config_mgr_free(mgr);
}
/* Another variant, which accepts and stores unrecognized lines.*/
@@ -786,8 +814,9 @@ static config_format_t etest_fmt = {
test_deprecation_notes,
test_vars,
test_validate_cb,
- test_free_cb,
+ NULL,
&extra,
+ -1,
};
/* Try out the feature where we can store unrecognized lines and dump them
@@ -796,24 +825,26 @@ static void
test_confparse_extra_lines(void *arg)
{
(void)arg;
- test_struct_t *tst = config_new(&etest_fmt);
+ config_mgr_t *mgr = config_mgr_new(&etest_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = config_new(mgr);
config_line_t *lines = NULL;
char *msg = NULL, *dump = NULL;
- config_init(&etest_fmt, tst);
+ config_init(mgr, tst);
int r = config_get_lines(
"unknotty addita\n"
"pos 99\n"
"wombat knish\n", &lines, 0);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&etest_fmt, tst, lines, 0, &msg);
+ r = config_assign(mgr, tst, lines, 0, &msg);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(msg, OP_EQ, NULL);
tt_assert(tst->extra_lines);
- dump = config_dump(&etest_fmt, NULL, tst, 1, 0);
+ dump = config_dump(mgr, NULL, tst, 1, 0);
tt_str_op(dump, OP_EQ,
"pos 99\n"
"unknotty addita\n"
@@ -823,7 +854,8 @@ test_confparse_extra_lines(void *arg)
tor_free(msg);
tor_free(dump);
config_free_lines(lines);
- config_free(&etest_fmt, tst);
+ config_free(mgr, tst);
+ config_mgr_free(mgr);
}
static void
@@ -889,12 +921,106 @@ static void
test_confparse_check_ok_fail(void *arg)
{
(void)arg;
- test_struct_t *tst = config_new(&test_fmt);
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ config_mgr_freeze(mgr);
+ test_struct_t *tst = config_new(mgr);
tst->pos = -10;
- tt_assert(! config_check_ok(&test_fmt, tst, LOG_INFO));
+ tt_assert(! config_check_ok(mgr, tst, LOG_INFO));
+
+ done:
+ config_free(mgr, tst);
+ config_mgr_free(mgr);
+}
+
+static void
+test_confparse_list_vars(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ smartlist_t *vars = config_mgr_list_vars(mgr);
+ smartlist_t *varnames = smartlist_new();
+ char *joined = NULL;
+
+ tt_assert(vars);
+ SMARTLIST_FOREACH(vars, config_var_t *, cv,
+ smartlist_add(varnames, (void*)cv->member.name));
+ smartlist_sort_strings(varnames);
+ joined = smartlist_join_strings(varnames, "::", 0, NULL);
+ tt_str_op(joined, OP_EQ,
+ "LineTypeA::"
+ "LineTypeB::"
+ "MixedHiddenLines::"
+ "MixedLines::"
+ "VisibleLineB::"
+ "__HiddenInt::"
+ "__HiddenLineA::"
+ "autobool::"
+ "boolean::"
+ "csv::"
+ "csv_interval::"
+ "dbl::"
+ "deprecated_int::"
+ "fn::"
+ "i::"
+ "interval::"
+ "lines::"
+ "mem::"
+ "msec_interval::"
+ "obsolete::"
+ "pos::"
+ "routerset::"
+ "s::"
+ "time::"
+ "u64");
+
+ done:
+ tor_free(joined);
+ smartlist_free(varnames);
+ smartlist_free(vars);
+ config_mgr_free(mgr);
+}
+
+static void
+test_confparse_list_deprecated(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+ smartlist_t *vars = config_mgr_list_deprecated_vars(mgr);
+ char *joined = NULL;
+
+ tt_assert(vars);
+ smartlist_sort_strings(vars);
+ joined = smartlist_join_strings(vars, "::", 0, NULL);
+
+ tt_str_op(joined, OP_EQ, "deprecated_int");
+
+ done:
+ tor_free(joined);
+ smartlist_free(vars);
+ config_mgr_free(mgr);
+}
+
+static void
+test_confparse_find_option_name(void *arg)
+{
+ (void)arg;
+ config_mgr_t *mgr = config_mgr_new(&test_fmt);
+
+ // exact match
+ tt_str_op(config_find_option_name(mgr, "u64"), OP_EQ, "u64");
+ // case-insensitive match
+ tt_str_op(config_find_option_name(mgr, "S"), OP_EQ, "s");
+ tt_str_op(config_find_option_name(mgr, "linetypea"), OP_EQ, "LineTypeA");
+ // prefix match
+ tt_str_op(config_find_option_name(mgr, "deprec"), OP_EQ, "deprecated_int");
+ // explicit abbreviation
+ tt_str_op(config_find_option_name(mgr, "uint"), OP_EQ, "pos");
+ tt_str_op(config_find_option_name(mgr, "UINT"), OP_EQ, "pos");
+ // no match
+ tt_ptr_op(config_find_option_name(mgr, "absent"), OP_EQ, NULL);
done:
- config_free(&test_fmt, tst);
+ config_mgr_free(mgr);
}
#define CONFPARSE_TEST(name, flags) \
@@ -933,5 +1059,8 @@ struct testcase_t confparse_tests[] = {
CONFPARSE_TEST(extra_lines, 0),
CONFPARSE_TEST(unitparse, 0),
CONFPARSE_TEST(check_ok_fail, 0),
+ CONFPARSE_TEST(list_vars, 0),
+ CONFPARSE_TEST(list_deprecated, 0),
+ CONFPARSE_TEST(find_option_name, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index e57bd02584..edfd0c74e1 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -479,8 +479,7 @@ static or_options_t *mock_options = NULL;
static void
init_mock_options(void)
{
- mock_options = tor_malloc(sizeof(or_options_t));
- memset(mock_options, 0, sizeof(or_options_t));
+ mock_options = options_new();
mock_options->TestingTorNetwork = 1;
mock_options->DataDirectory = tor_strdup(get_fname_rnd("datadir_tmp"));
mock_options->CacheDirectory = tor_strdup(mock_options->DataDirectory);
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index c43b21c673..c8dd4b03ea 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -5,6 +5,7 @@
#define CIRCUITLIST_PRIVATE
#define CIRCUITBUILD_PRIVATE
+#define CONFIG_PRIVATE
#define STATEFILE_PRIVATE
#define ENTRYNODES_PRIVATE
#define ROUTERLIST_PRIVATE
@@ -201,7 +202,7 @@ big_fake_network_setup(const struct testcase_t *testcase)
smartlist_add(big_fake_net_nodes, n);
}
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
dummy_consensus = tor_malloc_zero(sizeof(networkstatus_t));
if (reasonably_future_consensus) {
/* Make the dummy consensus valid in 6 hours, and expiring in 7 hours. */
@@ -235,12 +236,12 @@ mock_randomize_time_no_randomization(time_t a, time_t b)
return a;
}
-static or_options_t mocked_options;
+static or_options_t *mocked_options;
static const or_options_t *
mock_get_options(void)
{
- return &mocked_options;
+ return mocked_options;
}
#define TEST_IPV4_ADDR "123.45.67.89"
@@ -259,7 +260,7 @@ test_node_preferred_orport(void *arg)
tor_addr_port_t ap;
/* Setup options */
- memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options = options_new();
/* We don't test ClientPreferIPv6ORPort here, because it's used in
* nodelist_set_consensus to setup node.ipv6_preferred, which we set
* directly. */
@@ -282,8 +283,8 @@ test_node_preferred_orport(void *arg)
/* Check the preferred address is IPv4 if we're only using IPv4, regardless
* of whether we prefer it or not */
- mocked_options.ClientUseIPv4 = 1;
- mocked_options.ClientUseIPv6 = 0;
+ mocked_options->ClientUseIPv4 = 1;
+ mocked_options->ClientUseIPv6 = 0;
node.ipv6_preferred = 0;
node_get_pref_orport(&node, &ap);
tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr));
@@ -296,8 +297,8 @@ test_node_preferred_orport(void *arg)
/* Check the preferred address is IPv4 if we're using IPv4 and IPv6, but
* don't prefer the IPv6 address */
- mocked_options.ClientUseIPv4 = 1;
- mocked_options.ClientUseIPv6 = 1;
+ mocked_options->ClientUseIPv4 = 1;
+ mocked_options->ClientUseIPv6 = 1;
node.ipv6_preferred = 0;
node_get_pref_orport(&node, &ap);
tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr));
@@ -305,28 +306,29 @@ test_node_preferred_orport(void *arg)
/* Check the preferred address is IPv6 if we prefer it and
* ClientUseIPv6 is 1, regardless of ClientUseIPv4 */
- mocked_options.ClientUseIPv4 = 1;
- mocked_options.ClientUseIPv6 = 1;
+ mocked_options->ClientUseIPv4 = 1;
+ mocked_options->ClientUseIPv6 = 1;
node.ipv6_preferred = 1;
node_get_pref_orport(&node, &ap);
tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
tt_assert(ap.port == ipv6_port);
- mocked_options.ClientUseIPv4 = 0;
+ mocked_options->ClientUseIPv4 = 0;
node_get_pref_orport(&node, &ap);
tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
tt_assert(ap.port == ipv6_port);
/* Check the preferred address is IPv6 if we don't prefer it, but
* ClientUseIPv4 is 0 */
- mocked_options.ClientUseIPv4 = 0;
- mocked_options.ClientUseIPv6 = 1;
- node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(&mocked_options);
+ mocked_options->ClientUseIPv4 = 0;
+ mocked_options->ClientUseIPv6 = 1;
+ node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(mocked_options);
node_get_pref_orport(&node, &ap);
tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
tt_assert(ap.port == ipv6_port);
done:
+ or_options_free(mocked_options);
UNMOCK(get_options);
}
diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c
index b4389f2d17..31a6540efc 100644
--- a/src/test/test_helpers.c
+++ b/src/test/test_helpers.c
@@ -295,7 +295,7 @@ helper_parse_options(const char *conf)
if (ret != 0) {
goto done;
}
- ret = config_assign(&options_format, opt, line, 0, &msg);
+ ret = config_assign(get_options_mgr(), opt, line, 0, &msg);
if (ret != 0) {
goto done;
}
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index c0803a5a59..c5854f0ff8 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -1178,7 +1178,7 @@ test_introduce2(void *arg)
MOCK(get_or_state,
get_or_state_replacement);
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_INTRO, flags);
tt_assert(circ);
@@ -1343,7 +1343,7 @@ test_rotate_descriptors(void *arg)
(void) arg;
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
hs_init();
MOCK(get_or_state, get_or_state_replacement);
@@ -1460,7 +1460,7 @@ test_build_update_descriptors(void *arg)
MOCK(networkstatus_get_live_consensus,
mock_networkstatus_get_live_consensus);
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
ret = parse_rfc1123_time("Sat, 26 Oct 1985 03:00:00 UTC",
&mock_ns.valid_after);
@@ -1691,7 +1691,7 @@ test_build_descriptors(void *arg)
MOCK(networkstatus_get_live_consensus,
mock_networkstatus_get_live_consensus);
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
ret = parse_rfc1123_time("Sat, 26 Oct 1985 03:00:00 UTC",
&mock_ns.valid_after);
@@ -1792,7 +1792,7 @@ test_upload_descriptors(void *arg)
MOCK(networkstatus_get_live_consensus,
mock_networkstatus_get_live_consensus);
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
&mock_ns.valid_after);
diff --git a/src/test/test_options.c b/src/test/test_options.c
index 64fcd011e7..a6bccdc524 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -96,7 +96,7 @@ clear_log_messages(void)
opt->command = CMD_RUN_TOR; \
options_init(opt); \
\
- dflt = config_dup(&options_format, opt); \
+ dflt = config_dup(get_options_mgr(), opt); \
clear_log_messages(); \
} while (0)
@@ -196,7 +196,7 @@ test_options_validate_impl(const char *configuration,
if (r)
goto done;
- r = config_assign(&options_format, opt, cl, 0, &msg);
+ r = config_assign(get_options_mgr(), opt, cl, 0, &msg);
if (phase == PH_ASSIGN) {
if (test_options_checkmsgs(configuration, expect_errmsg,
expect_log_severity,
@@ -304,7 +304,7 @@ test_have_enough_mem_for_dircache(void *arg)
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&options_format, opt, cl, 0, &msg);
+ r = config_assign(get_options_mgr(), opt, cl, 0, &msg);
tt_int_op(r, OP_EQ, 0);
/* 300 MB RAM available, DirCache enabled */
@@ -327,7 +327,7 @@ test_have_enough_mem_for_dircache(void *arg)
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&options_format, opt, cl, 0, &msg);
+ r = config_assign(get_options_mgr(), opt, cl, 0, &msg);
tt_int_op(r, OP_EQ, 0);
/* 300 MB RAM available, DirCache enabled, Bridge */
@@ -350,7 +350,7 @@ test_have_enough_mem_for_dircache(void *arg)
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&options_format, opt, cl, 0, &msg);
+ r = config_assign(get_options_mgr(), opt, cl, 0, &msg);
tt_int_op(r, OP_EQ, 0);
/* 200 MB RAM available, DirCache disabled */
@@ -438,7 +438,7 @@ get_options_test_data(const char *conf)
rv = config_get_lines(conf, &cl, 1);
tt_int_op(rv, OP_EQ, 0);
- rv = config_assign(&options_format, result->opt, cl, 0, &msg);
+ rv = config_assign(get_options_mgr(), result->opt, cl, 0, &msg);
if (msg) {
/* Display the parse error message by comparing it with an empty string */
tt_str_op(msg, OP_EQ, "");
@@ -449,7 +449,7 @@ get_options_test_data(const char *conf)
result->opt->TokenBucketRefillInterval = 1;
rv = config_get_lines(TEST_OPTIONS_OLD_VALUES, &cl, 1);
tt_int_op(rv, OP_EQ, 0);
- rv = config_assign(&options_format, result->def_opt, cl, 0, &msg);
+ rv = config_assign(get_options_mgr(), result->def_opt, cl, 0, &msg);
if (msg) {
/* Display the parse error message by comparing it with an empty string */
tt_str_op(msg, OP_EQ, "");
diff --git a/src/test/test_pt.c b/src/test/test_pt.c
index 87e3ba356c..362c6664db 100644
--- a/src/test/test_pt.c
+++ b/src/test/test_pt.c
@@ -352,7 +352,7 @@ test_pt_configure_proxy(void *arg)
managed_proxy_t *mp = NULL;
(void) arg;
- dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ dummy_state = or_state_new();
MOCK(process_read_stdout, process_read_stdout_replacement);
MOCK(get_or_state,