1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006 |
- /* Copyright (c) 2001 Matej Pfajfar.
- * 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 */
- /**
- * \file confparse.c
- *
- * \brief Back-end for parsing and generating key-value files, used to
- * implement the torrc file format and the state file.
- *
- * This module is used by config.c to parse and encode torrc
- * configuration files, and by statefile.c to parse and encode the
- * $DATADIR/state file.
- *
- * To use this module, its callers provide an instance of
- * config_format_t to describe the mappings from a set of configuration
- * options to a number of fields in a C structure. With this mapping,
- * the functions here can convert back and forth between the C structure
- * specified, and a linked list of key-value pairs.
- */
- #define CONFPARSE_PRIVATE
- #include "orconfig.h"
- #include "app/config/confparse.h"
- #include "lib/confmgt/structvar.h"
- #include "lib/confmgt/unitparse.h"
- #include "lib/container/bitarray.h"
- #include "lib/container/smartlist.h"
- #include "lib/encoding/confline.h"
- #include "lib/log/escape.h"
- #include "lib/log/log.h"
- #include "lib/log/util_bug.h"
- #include "lib/string/compat_ctype.h"
- #include "lib/string/printf.h"
- #include "lib/string/util_string.h"
- #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_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;
- /** A smartlist of managed_var_t objects for all configuration formats. */
- smartlist_t *all_vars;
- /** A smartlist of config_abbrev_t objects for all abbreviations. These
- * objects are */
- 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->toplevel = toplevel_fmt;
- mgr->all_vars = smartlist_new();
- mgr->all_abbrevs = smartlist_new();
- mgr->all_deprecations = smartlist_new();
- config_mgr_register_fmt(mgr, toplevel_fmt, IDX_TOPLEVEL);
- 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.");
- /* 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);
- }
- }
- }
- /**
- * 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 modules.
- */
- static void *
- config_mgr_get_obj_mutable(const config_mgr_t *mgr, void *toplevel, int idx)
- {
- (void)mgr;
- tor_assert(idx == IDX_TOPLEVEL); // nothing else is implemented yet XXXX
- tor_assert(toplevel);
- return toplevel;
- }
- /** 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);
- 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. */
- 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. */
- 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, &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);
- }
- /** 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_mgr_t *mgr)
- {
- tor_assert(mgr->frozen);
- const config_format_t *fmt = mgr->toplevel;
- void *opts = tor_malloc_zero(fmt->size);
- struct_set_magic(opts, &mgr->toplevel_magic);
- CONFIG_CHECK(mgr, opts);
- return opts;
- }
- /*
- * Functions to parse config options
- */
- /** If <b>option</b> is an official abbreviation for a longer option,
- * return the longer option. Otherwise return <b>option</b>.
- * If <b>command_line</b> is set, apply all abbreviations. Otherwise, only
- * 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_mgr_t *mgr, const char *option,
- int command_line, int warn_obsolete)
- {
- SMARTLIST_FOREACH_BEGIN(mgr->all_abbrevs, const config_abbrev_t *, abbrev) {
- /* Abbreviations are case insensitive. */
- 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.",
- 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 = abbrev->full;
- }
- } SMARTLIST_FOREACH_END(abbrev);
- return option;
- }
- /** If <b>key</b> is a deprecated configuration option, return the message
- * 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_mgr_t *mgr, const char *key)
- {
- if (BUG(mgr == NULL) || BUG(key == NULL))
- return NULL; // LCOV_EXCL_LINE
- 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;
- }
- /**
- * 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->all_vars, or to -1 if the variable is
- * not found.
- */
- static const managed_var_t *
- config_mgr_find_var(const config_mgr_t *mgr,
- const char *key,
- bool allow_truncated, int *idx_out)
- {
- 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 */
- 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 */
- 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, 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)
- {
- 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_mgr_t *mgr)
- {
- return smartlist_len(mgr->all_vars);
- }
- bool
- config_var_is_cumulative(const config_var_t *var)
- {
- return struct_var_is_cumulative(&var->member);
- }
- bool
- config_var_is_settable(const config_var_t *var)
- {
- if (var->flags & CVFLAG_OBSOLETE)
- return false;
- return struct_var_is_settable(&var->member);
- }
- bool
- config_var_is_contained(const config_var_t *var)
- {
- return struct_var_is_contained(&var->member);
- }
- /*
- * Functions to assign config options.
- */
- /** <b>c</b>-\>key is known to be a real key. Update <b>options</b>
- * with <b>c</b>-\>value and return 0, or return -1 if bad value.
- *
- * Called from config_assign_line() and option_reset().
- */
- static int
- config_assign_value(const config_mgr_t *mgr, void *options,
- config_line_t *c, char **msg)
- {
- const managed_var_t *var;
- CONFIG_CHECK(mgr, options);
- var = config_mgr_find_var(mgr, c->key, true, NULL);
- tor_assert(var);
- 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(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_mgr_t *mgr, void *options)
- {
- tor_assert(mgr);
- tor_assert(options);
- 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
- warn_deprecated_option(const char *what, const char *why)
- {
- const char *space = (why && strlen(why)) ? " " : "";
- log_warn(LD_CONFIG, "The %s option is deprecated, and will most likely "
- "be removed in a future version of Tor.%s%s (If you think this is "
- "a mistake, please let us know!)",
- what, space, why);
- }
- /** If <b>c</b> is a syntactically valid configuration line, update
- * <b>options</b> with its value and return 0. Otherwise return -1 for bad
- * key, -2 for bad value.
- *
- * If <b>clear_first</b> is set, clear the value first. Then if
- * <b>use_defaults</b> is set, set the value to the default.
- *
- * Called from config_assign().
- */
- static int
- 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 managed_var_t *mvar;
- CONFIG_CHECK(mgr, options);
- 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,
- "Found unrecognized option '%s'; saving it.", c->key);
- config_line_append((config_line_t**)lvalue, c->key, c->value);
- return 0;
- } else {
- tor_asprintf(msg,
- "Unknown option '%s'. Failing.", c->key);
- return -1;
- }
- }
- const config_var_t *cvar = mvar->cvar;
- tor_assert(cvar);
- /* Put keyword into canonical case. */
- if (strcmp(cvar->member.name, c->key)) {
- tor_free(c->key);
- c->key = tor_strdup(cvar->member.name);
- }
- const char *deprecation_msg;
- if (warn_deprecations &&
- (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(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(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(mgr, options, mvar, use_defaults); // LCOV_EXCL_LINE
- }
- 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. */
- 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.", cvar->member.name);
- }
- bitarray_set(options_seen, var_index);
- }
- if (config_assign_value(mgr, options, c, msg) < 0)
- return -2;
- return 0;
- }
- /** Restore the option named <b>key</b> in options to its default value.
- * Called from config_assign(). */
- STATIC void
- config_reset_line(const config_mgr_t *mgr, void *options,
- const char *key, int use_defaults)
- {
- const managed_var_t *var;
- CONFIG_CHECK(mgr, options);
- var = config_mgr_find_var(mgr, key, true, NULL);
- if (!var)
- return; /* give error on next pass. */
- config_reset(mgr, options, var, use_defaults);
- }
- /** Return true iff value needs to be quoted and escaped to be used in
- * a configuration file. */
- static int
- config_value_needs_escape(const char *value)
- {
- if (*value == '\"')
- return 1;
- while (*value) {
- switch (*value)
- {
- case '\r':
- case '\n':
- case '#':
- /* Note: quotes and backspaces need special handling when we are using
- * quotes, not otherwise, so they don't trigger escaping on their
- * own. */
- return 1;
- default:
- if (!TOR_ISPRINT(*value))
- return 1;
- }
- ++value;
- }
- return 0;
- }
- /** Return newly allocated line or lines corresponding to <b>key</b> in the
- * configuration <b>options</b>. If <b>escape_val</b> is true and a
- * 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_mgr_t *mgr, const void *options,
- const char *key, int escape_val)
- {
- const managed_var_t *var;
- config_line_t *result;
- tor_assert(options && key);
- CONFIG_CHECK(mgr, options);
- 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(object, &var->cvar->member);
- if (escape_val) {
- config_line_t *line;
- for (line = result; line; line = line->next) {
- if (line->value && config_value_needs_escape(line->value)) {
- char *newval = esc_for_log(line->value);
- tor_free(line->value);
- line->value = newval;
- }
- }
- }
- return result;
- }
- /** Iterate through the linked list of requested options <b>list</b>.
- * For each item, convert as appropriate and assign to <b>options</b>.
- * If an item is unrecognized, set *msg and return -1 immediately,
- * else return 0 for success.
- *
- * If <b>clear_first</b>, interpret config options as replacing (not
- * extending) their previous values. If <b>clear_first</b> is set,
- * then <b>use_defaults</b> to decide if you set to defaults after
- * clearing, or make the value 0 or NULL.
- *
- * Here are the use cases:
- * 1. A non-empty AllowInvalid line in your torrc. Appends to current
- * if linelist, replaces current if csv.
- * 2. An empty AllowInvalid line in your torrc. Should clear it.
- * 3. "RESETCONF AllowInvalid" sets it to default.
- * 4. "SETCONF AllowInvalid" makes it NULL.
- * 5. "SETCONF AllowInvalid=foo" clears it and sets it to "foo".
- *
- * Use_defaults Clear_first
- * 0 0 "append"
- * 1 0 undefined, don't use
- * 0 1 "set to null first"
- * 1 1 "set to defaults first"
- * Return 0 on success, -1 on bad key, -2 on bad value.
- *
- * As an additional special case, if a LINELIST config option has
- * no value and clear_first is 0, then warn and ignore it.
- */
- /*
- There are three call cases for config_assign() currently.
- Case one: Torrc entry
- options_init_from_torrc() calls config_assign(0, 0)
- calls config_assign_line(0, 0).
- if value is empty, calls config_reset(0) and returns.
- calls config_assign_value(), appends.
- Case two: setconf
- options_trial_assign() calls config_assign(0, 1)
- calls config_reset_line(0)
- calls config_reset(0)
- calls option_clear().
- calls config_assign_line(0, 1).
- if value is empty, returns.
- calls config_assign_value(), appends.
- Case three: resetconf
- options_trial_assign() calls config_assign(1, 1)
- calls config_reset_line(1)
- calls config_reset(1)
- calls option_clear().
- calls config_assign_value(default)
- calls config_assign_line(1, 1).
- returns.
- */
- int
- 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(mgr);
- const unsigned clear_first = config_assign_flags & CAL_CLEAR_FIRST;
- const unsigned use_defaults = config_assign_flags & CAL_USE_DEFAULTS;
- CONFIG_CHECK(mgr, options);
- /* pass 1: normalize keys */
- for (p = list; p; p = p->next) {
- 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);
- }
- }
- /* pass 2: if we're reading from a resetting source, clear all
- * mentioned config options, and maybe set to their defaults. */
- if (clear_first) {
- for (p = list; p; p = p->next)
- 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(mgr, options, list, config_assign_flags,
- options_seen, msg))) {
- bitarray_free(options_seen);
- return r;
- }
- list = list->next;
- }
- bitarray_free(options_seen);
- /** 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(mgr, options);
- return 0;
- }
- /** 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_mgr_t *mgr, void *options, const managed_var_t *var)
- {
- 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_mgr_t *mgr, void *options,
- const managed_var_t *var, int use_defaults)
- {
- config_line_t *c;
- char *msg = NULL;
- CONFIG_CHECK(mgr, options);
- config_clear(mgr, options, var); /* clear it first */
- if (!use_defaults)
- return; /* all done */
- if (var->cvar->initvalue) {
- c = tor_malloc_zero(sizeof(config_line_t));
- 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 */
- // LCOV_EXCL_STOP
- }
- config_free_lines(c);
- }
- }
- /** Release storage held by <b>options</b>. */
- void
- config_free_(const config_mgr_t *mgr, void *options)
- {
- const config_format_t *fmt = mgr->toplevel;
- if (!options)
- return;
- tor_assert(fmt);
- if (mgr->toplevel->clear_fn) {
- mgr->toplevel->clear_fn(mgr, options);
- }
- SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
- config_clear(mgr, options, mv);
- } SMARTLIST_FOREACH_END(mv);
- if (fmt->extra) {
- config_line_t **linep = STRUCT_VAR_P(options, fmt->extra->offset);
- config_free_lines(*linep);
- *linep = NULL;
- }
- tor_free(options);
- }
- /** Return true iff the option <b>name</b> has the same value in <b>o1</b>
- * and <b>o2</b>. Must not be called for LINELIST_S or OBSOLETE options.
- */
- int
- config_is_same(const config_mgr_t *mgr,
- const void *o1, const void *o2,
- const char *name)
- {
- CONFIG_CHECK(mgr, o1);
- CONFIG_CHECK(mgr, o2);
- 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(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.
- **/
- 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_mgr_t *mgr, const void *old)
- {
- void *newopts;
- 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;
- }
- 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.",
- 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_mgr_t *mgr, void *options)
- {
- CONFIG_CHECK(mgr, options);
- SMARTLIST_FOREACH_BEGIN(mgr->all_vars, const managed_var_t *, mv) {
- if (!mv->cvar->initvalue)
- continue; /* defaults to NULL or 0 */
- config_reset(mgr, options, mv, 1);
- } SMARTLIST_FOREACH_END(mv);
- }
- /** Allocate and return a new string holding the written-out values of the vars
- * in 'options'. If 'minimal', do not write out any default-valued vars.
- * Else, if comment_defaults, write default values as comments.
- */
- char *
- 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;
- char *msg = NULL;
- if (defaults == NULL) {
- 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 */
- if (default_options == NULL) {
- if (fmt->validate_fn(NULL, defaults_tmp, defaults_tmp, 1, &msg) < 0) {
- // LCOV_EXCL_START
- log_err(LD_BUG, "Failed to validate default config: %s", msg);
- tor_free(msg);
- tor_assert(0);
- // LCOV_EXCL_STOP
- }
- }
- elements = smartlist_new();
- SMARTLIST_FOREACH_BEGIN(mgr->all_vars, managed_var_t *, mv) {
- int comment_option = 0;
- 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 (mv->cvar->flags & CVFLAG_NODUMP)
- continue;
- const char *name = mv->cvar->member.name;
- if (minimal && config_is_same(mgr, options, defaults, name))
- continue;
- else if (comment_defaults &&
- config_is_same(mgr, options, defaults, name))
- comment_option = 1;
- line = assigned =
- config_get_assigned_option(mgr, options, name, 1);
- for (; line; line = line->next) {
- if (!strcmpstart(line->key, "__")) {
- /* This check detects "hidden" variables inside LINELIST_V structures.
- */
- continue;
- }
- smartlist_add_asprintf(elements, "%s%s %s\n",
- comment_option ? "# " : "",
- 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);
- for (; line; line = line->next) {
- smartlist_add_asprintf(elements, "%s %s\n", line->key, line->value);
- }
- }
- result = smartlist_join_strings(elements, "", 0, NULL);
- SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
- smartlist_free(elements);
- config_free(mgr, defaults_tmp);
- return result;
- }
- /**
- * Return true if every member of <b>options</b> is in-range and well-formed.
- * Return false otherwise. Log errors at level <b>severity</b>.
- */
- bool
- config_check_ok(const config_mgr_t *mgr, const void *options, int severity)
- {
- bool all_ok = true;
- 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",
- mv->cvar->member.name);
- all_ok = false;
- }
- } SMARTLIST_FOREACH_END(mv);
- return all_ok;
- }
|