Turn several properties of types or variables into flags.

"unsettable" is a property of types.  LINELIST_V and OBSOLETE are
unsettable, meaning that they cannot be set by name.

"contained" is a property of types.  I'm hoping to find a better
name here.  LINELIST_S is "contained" because it always appears
within a LINELIST_V, and as such doesn't need to be dumped ore
copied independently.

"cumulative" is a property of types. Cumulative types can appear
more than once in a torrc without causing a warning, because they
add to each other rather than replacing each other.

"obsolete" is a property of variables.

"marking fragile" is now a command that struct members can accept.

With these changes, confparse and config no longer ever need to
mention CONFIG_TYPE_XYZ values by name.
This commit is contained in:
Nick Mathewson 2019-06-21 09:58:40 -04:00
parent b6457d4c08
commit a7835202cf
11 changed files with 203 additions and 43 deletions

View File

@ -960,8 +960,8 @@ set_options(or_options_t *new_val, char **msg)
for (i=0; options_format.vars[i].member.name; ++i) { for (i=0; options_format.vars[i].member.name; ++i) {
const config_var_t *var = &options_format.vars[i]; const config_var_t *var = &options_format.vars[i];
const char *var_name = var->member.name; const char *var_name = var->member.name;
if (var->member.type == CONFIG_TYPE_LINELIST_S || if (config_var_is_contained(var)) {
var->member.type == CONFIG_TYPE_OBSOLETE) { /* something else will check this var, or it doesn't need checking */
continue; continue;
} }
if (!config_is_same(&options_format, new_val, old_options, var_name)) { if (!config_is_same(&options_format, new_val, old_options, var_name)) {
@ -2663,9 +2663,10 @@ list_torrc_options(void)
int i; int i;
for (i = 0; option_vars_[i].member.name; ++i) { for (i = 0; option_vars_[i].member.name; ++i) {
const config_var_t *var = &option_vars_[i]; const config_var_t *var = &option_vars_[i];
if (var->member.type == CONFIG_TYPE_OBSOLETE || if (! config_var_is_settable(var)) {
var->member.type == CONFIG_TYPE_LINELIST_V) /* This variable cannot be set, or cannot be set by this name. */
continue; continue;
}
printf("%s\n", var->member.name); printf("%s\n", var->member.name);
} }
} }

View File

@ -148,6 +148,24 @@ config_count_options(const config_format_t *fmt)
return i; return i;
} }
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. * Functions to assign config options.
*/ */
@ -183,14 +201,7 @@ config_mark_lists_fragile(const config_format_t *fmt, void *options)
for (i = 0; fmt->vars[i].member.name; ++i) { for (i = 0; fmt->vars[i].member.name; ++i) {
const config_var_t *var = &fmt->vars[i]; const config_var_t *var = &fmt->vars[i];
config_line_t *list; struct_var_mark_fragile(options, &var->member);
if (var->member.type != CONFIG_TYPE_LINELIST &&
var->member.type != CONFIG_TYPE_LINELIST_V)
continue;
list = *(config_line_t **)STRUCT_VAR_P(options, var->member.offset);
if (list)
list->fragile = 1;
} }
} }
@ -255,9 +266,7 @@ config_assign_line(const config_format_t *fmt, void *options,
if (!strlen(c->value)) { if (!strlen(c->value)) {
/* reset or clear it, then return */ /* reset or clear it, then return */
if (!clear_first) { if (!clear_first) {
if ((var->member.type == CONFIG_TYPE_LINELIST || if (config_var_is_cumulative(var) && c->command != CONFIG_LINE_CLEAR) {
var->member.type == CONFIG_TYPE_LINELIST_S) &&
c->command != CONFIG_LINE_CLEAR) {
/* We got an empty linelist from the torrc or command line. /* We got an empty linelist from the torrc or command line.
As a special case, call this an error. Warn and ignore. */ As a special case, call this an error. Warn and ignore. */
log_warn(LD_CONFIG, log_warn(LD_CONFIG,
@ -273,8 +282,7 @@ config_assign_line(const config_format_t *fmt, void *options,
config_reset(fmt, options, var, use_defaults); // LCOV_EXCL_LINE config_reset(fmt, options, var, use_defaults); // LCOV_EXCL_LINE
} }
if (options_seen && (var->member.type != CONFIG_TYPE_LINELIST && if (options_seen && ! config_var_is_cumulative(var)) {
var->member.type != CONFIG_TYPE_LINELIST_S)) {
/* We're tracking which options we've seen, and this option is not /* We're tracking which options we've seen, and this option is not
* supposed to occur more than once. */ * supposed to occur more than once. */
int var_index = (int)(var - fmt->vars); int var_index = (int)(var - fmt->vars);
@ -562,10 +570,10 @@ config_dup(const config_format_t *fmt, const void *old)
newopts = config_new(fmt); newopts = config_new(fmt);
for (i=0; fmt->vars[i].member.name; ++i) { for (i=0; fmt->vars[i].member.name; ++i) {
if (fmt->vars[i].member.type == CONFIG_TYPE_LINELIST_S) if (config_var_is_contained(&fmt->vars[i])) {
continue; // Something else will copy this option, or it doesn't need copying.
if (fmt->vars[i].member.type == CONFIG_TYPE_OBSOLETE)
continue; continue;
}
if (struct_var_copy(newopts, old, &fmt->vars[i].member) < 0) { if (struct_var_copy(newopts, old, &fmt->vars[i].member) < 0) {
// LCOV_EXCL_START // LCOV_EXCL_START
log_err(LD_BUG, "Unable to copy value for %s.", log_err(LD_BUG, "Unable to copy value for %s.",
@ -629,9 +637,10 @@ config_dump(const config_format_t *fmt, const void *default_options,
elements = smartlist_new(); elements = smartlist_new();
for (i=0; fmt->vars[i].member.name; ++i) { for (i=0; fmt->vars[i].member.name; ++i) {
int comment_option = 0; int comment_option = 0;
if (fmt->vars[i].member.type == CONFIG_TYPE_OBSOLETE || if (config_var_is_contained(&fmt->vars[i])) {
fmt->vars[i].member.type == CONFIG_TYPE_LINELIST_S) // Something else will dump this option, or it doesn't need dumping.
continue; continue;
}
/* Don't save 'hidden' control variables. */ /* Don't save 'hidden' control variables. */
if (!strcmpstart(fmt->vars[i].member.name, "__")) if (!strcmpstart(fmt->vars[i].member.name, "__"))
continue; continue;

View File

@ -104,6 +104,10 @@ const char *config_expand_abbrev(const config_format_t *fmt,
int command_line, int warn_obsolete); int command_line, int warn_obsolete);
void warn_deprecated_option(const char *what, const char *why); void warn_deprecated_option(const char *what, const char *why);
bool config_var_is_cumulative(const config_var_t *var);
bool config_var_is_settable(const config_var_t *var);
bool config_var_is_contained(const config_var_t *var);
/* Helper macros to compare an option across two configuration objects */ /* Helper macros to compare an option across two configuration objects */
#define CFG_EQ_BOOL(a,b,opt) ((a)->opt == (b)->opt) #define CFG_EQ_BOOL(a,b,opt) ((a)->opt == (b)->opt)
#define CFG_EQ_INT(a,b,opt) ((a)->opt == (b)->opt) #define CFG_EQ_INT(a,b,opt) ((a)->opt == (b)->opt)

View File

@ -60,6 +60,8 @@
} }
#define CONFIG_VAR_OBSOLETE(varname) \ #define CONFIG_VAR_OBSOLETE(varname) \
{ .member = { .name = varname, .type = CONFIG_TYPE_OBSOLETE } } { .member = { .name = varname, .type = CONFIG_TYPE_OBSOLETE }, \
.flags = CVFLAG_OBSOLETE \
}
#endif /* !defined(TOR_LIB_CONF_CONFMACROS_H) */ #endif /* !defined(TOR_LIB_CONF_CONFMACROS_H) */

View File

@ -105,6 +105,12 @@ typedef struct struct_magic_decl_t {
int magic_offset; int magic_offset;
} struct_magic_decl_t; } struct_magic_decl_t;
/**
* Flag to indicate that an option is obsolete. Any attempt to set or
* fetch this option should produce a warning.
**/
#define CVFLAG_OBSOLETE (1u<<0)
/** A variable allowed in the configuration file or on the command line. */ /** A variable allowed in the configuration file or on the command line. */
typedef struct config_var_t { typedef struct config_var_t {
struct_member_t member; /** A struct member corresponding to this struct_member_t member; /** A struct member corresponding to this

View File

@ -201,6 +201,19 @@ struct_var_kvencode(const void *object, const struct_member_t *member)
return typed_var_kvencode_ex(member->name, p, def); return typed_var_kvencode_ex(member->name, p, def);
} }
/**
* Mark the field in <b>object</b> determined by <b>member</b> -- a variable
* that ordinarily would be extended by assignment -- as "fragile", so that it
* will get replaced by the next assignment instead.
*/
void
struct_var_mark_fragile(void *object, const struct_member_t *member)
{
void *p = struct_get_mptr(object, member);
const var_type_def_t *def = get_type_def(member);
return typed_var_mark_fragile_ex(p, def);
}
/** /**
* Return the official name of this struct member. * Return the official name of this struct member.
**/ **/
@ -224,3 +237,27 @@ struct_var_get_typename(const struct_member_t *member)
return def ? def->name : NULL; return def ? def->name : NULL;
} }
bool
struct_var_is_cumulative(const struct_member_t *member)
{
const var_type_def_t *def = get_type_def(member);
return def ? def->is_cumulative : false;
}
bool
struct_var_is_settable(const struct_member_t *member)
{
const var_type_def_t *def = get_type_def(member);
return def ? !def->is_unsettable : true;
}
bool
struct_var_is_contained(const struct_member_t *member)
{
const var_type_def_t *def = get_type_def(member);
return def ? def->is_contained : false;
}

View File

@ -40,9 +40,14 @@ bool struct_var_eq(const void *a, const void *b,
const struct struct_member_t *member); const struct struct_member_t *member);
bool struct_var_ok(const void *object, bool struct_var_ok(const void *object,
const struct struct_member_t *member); const struct struct_member_t *member);
void struct_var_mark_fragile(void *object,
const struct struct_member_t *member);
const char *struct_var_get_name(const struct struct_member_t *member); const char *struct_var_get_name(const struct struct_member_t *member);
const char *struct_var_get_typename(const struct struct_member_t *member); const char *struct_var_get_typename(const struct struct_member_t *member);
bool struct_var_is_cumulative(const struct struct_member_t *member);
bool struct_var_is_settable(const struct struct_member_t *member);
bool struct_var_is_contained(const struct struct_member_t *member);
int struct_var_kvassign(void *object, const struct config_line_t *line, int struct_var_kvassign(void *object, const struct config_line_t *line,
char **errmsg, char **errmsg,

View File

@ -620,12 +620,22 @@ linelist_copy(void *target, const void *value, const void *params)
return 0; return 0;
} }
static void
linelist_mark_fragile(void *target, const void *params)
{
(void)params;
config_line_t **ptr = (config_line_t **)target;
if (*ptr)
(*ptr)->fragile = 1;
}
static const var_type_fns_t linelist_fns = { static const var_type_fns_t linelist_fns = {
.kv_parse = linelist_kv_parse, .kv_parse = linelist_kv_parse,
.kv_encode = linelist_kv_encode, .kv_encode = linelist_kv_encode,
.clear = linelist_clear, .clear = linelist_clear,
.eq = linelist_eq, .eq = linelist_eq,
.copy = linelist_copy, .copy = linelist_copy,
.mark_fragile = linelist_mark_fragile,
}; };
static const var_type_fns_t linelist_v_fns = { static const var_type_fns_t linelist_v_fns = {
@ -634,6 +644,7 @@ static const var_type_fns_t linelist_v_fns = {
.clear = linelist_clear, .clear = linelist_clear,
.eq = linelist_eq, .eq = linelist_eq,
.copy = linelist_copy, .copy = linelist_copy,
.mark_fragile = linelist_mark_fragile,
}; };
static const var_type_fns_t linelist_s_fns = { static const var_type_fns_t linelist_s_fns = {
@ -690,26 +701,40 @@ static const var_type_fns_t ignore_fns = {
* Table mapping conf_type_t values to var_type_def_t objects. * Table mapping conf_type_t values to var_type_def_t objects.
**/ **/
static const var_type_def_t type_definitions_table[] = { static const var_type_def_t type_definitions_table[] = {
[CONFIG_TYPE_STRING] = { "String", &string_fns, NULL }, [CONFIG_TYPE_STRING] = { .name="String", .fns=&string_fns },
[CONFIG_TYPE_FILENAME] = { "Filename", &string_fns, NULL }, [CONFIG_TYPE_FILENAME] = { .name="Filename", .fns=&string_fns },
[CONFIG_TYPE_INT] = { "SignedInteger", &int_fns, &INT_PARSE_UNRESTRICTED }, [CONFIG_TYPE_INT] = { .name="SignedInteger", .fns=&int_fns,
[CONFIG_TYPE_POSINT] = { "Integer", &int_fns, &INT_PARSE_POSINT }, .params=&INT_PARSE_UNRESTRICTED },
[CONFIG_TYPE_UINT64] = { "Integer", &uint64_fns, NULL, }, [CONFIG_TYPE_POSINT] = { .name="Integer", .fns=&int_fns,
[CONFIG_TYPE_MEMUNIT] = { "DataSize", &memunit_fns, &memory_units }, .params=&INT_PARSE_POSINT },
[CONFIG_TYPE_INTERVAL] = { "TimeInterval", &interval_fns, &time_units }, [CONFIG_TYPE_UINT64] = { .name="Integer", .fns=&uint64_fns, },
[CONFIG_TYPE_MSEC_INTERVAL] = { "TimeMsecInterval", &interval_fns, [CONFIG_TYPE_MEMUNIT] = { .name="DataSize", .fns=&memunit_fns,
&time_msec_units }, .params=&memory_units },
[CONFIG_TYPE_DOUBLE] = { "Float", &double_fns, NULL }, [CONFIG_TYPE_INTERVAL] = { .name="TimeInterval", .fns=&interval_fns,
[CONFIG_TYPE_BOOL] = { "Boolean", &enum_fns, &enum_table_bool }, .params=&time_units },
[CONFIG_TYPE_AUTOBOOL] = { "Boolean+Auto", &enum_fns, &enum_table_autobool }, [CONFIG_TYPE_MSEC_INTERVAL] = { .name="TimeMsecInterval",
[CONFIG_TYPE_ISOTIME] = { "Time", &time_fns, NULL }, .fns=&interval_fns,
[CONFIG_TYPE_CSV] = { "CommaList", &csv_fns, NULL }, .params=&time_msec_units },
[CONFIG_TYPE_CSV_INTERVAL] = { "TimeInterval", &legacy_csv_interval_fns, [CONFIG_TYPE_DOUBLE] = { .name="Float", .fns=&double_fns, },
NULL }, [CONFIG_TYPE_BOOL] = { .name="Boolean", .fns=&enum_fns,
[CONFIG_TYPE_LINELIST] = { "LineList", &linelist_fns, NULL }, .params=&enum_table_bool },
[CONFIG_TYPE_LINELIST_S] = { "Dependent", &linelist_s_fns, NULL }, [CONFIG_TYPE_AUTOBOOL] = { .name="Boolean+Auto", .fns=&enum_fns,
[CONFIG_TYPE_LINELIST_V] = { "Virtual", &linelist_v_fns, NULL }, .params=&enum_table_autobool },
[CONFIG_TYPE_OBSOLETE] = { "Obsolete", &ignore_fns, NULL } [CONFIG_TYPE_ISOTIME] = { .name="Time", .fns=&time_fns, },
[CONFIG_TYPE_CSV] = { .name="CommaList", .fns=&csv_fns, },
[CONFIG_TYPE_CSV_INTERVAL] = { .name="TimeInterval",
.fns=&legacy_csv_interval_fns, },
[CONFIG_TYPE_LINELIST] = { .name="LineList", .fns=&linelist_fns,
.is_cumulative=true},
[CONFIG_TYPE_LINELIST_S] = { .name="Dependent", .fns=&linelist_s_fns,
.is_cumulative=true,
.is_contained=true, },
[CONFIG_TYPE_LINELIST_V] = { .name="Virtual", .fns=&linelist_v_fns,
.is_cumulative=true,
.is_unsettable=true },
[CONFIG_TYPE_OBSOLETE] = { .name="Obsolete", .fns=&ignore_fns,
.is_unsettable=true,
.is_contained=true, }
}; };
/** /**

View File

@ -210,6 +210,51 @@ typed_var_ok_ex(const void *value, const var_type_def_t *def)
return true; return true;
} }
/**
* Mark <b>value</b> -- a variable that ordinarily would be extended by
* assignment -- as "fragile", so that it will get replaced by the next
* assignment instead.
**/
void
typed_var_mark_fragile_ex(void *value, const var_type_def_t *def)
{
if (BUG(!def)) {
return; // LCOV_EXCL_LINE
}
if (def->fns->mark_fragile)
def->fns->mark_fragile(value, def->params);
}
/**
* Return true iff multiple assignments to a variable will extend its
* value, rather than replacing it.
**/
bool
var_type_is_cumulative(const var_type_def_t *def)
{
return def->is_cumulative;
}
/**
* Return true iff this variable type is always contained in another variable,
* and as such doesn't need to be dumped or copied independently.
**/
bool
var_type_is_contained(const var_type_def_t *def)
{
return def->is_contained;
}
/**
* Return true iff this type can not be assigned directly by the user.
**/
bool
var_type_is_settable(const var_type_def_t *def)
{
return ! def->is_unsettable;
}
/* ===== /* =====
* The functions below take a config_type_t instead of a var_type_def_t. * The functions below take a config_type_t instead of a var_type_def_t.
* I'd like to deprecate them eventually and use var_type_def_t everywhere, * I'd like to deprecate them eventually and use var_type_def_t everywhere,

View File

@ -46,4 +46,10 @@ int typed_var_kvassign_ex(void *target, const struct config_line_t *line,
struct config_line_t *typed_var_kvencode_ex(const char *key, const void *value, struct config_line_t *typed_var_kvencode_ex(const char *key, const void *value,
const var_type_def_t *def); const var_type_def_t *def);
void typed_var_mark_fragile_ex(void *value, const var_type_def_t *def);
bool var_type_is_cumulative(const var_type_def_t *def);
bool var_type_is_contained(const var_type_def_t *def);
bool var_type_is_settable(const var_type_def_t *def);
#endif /* !defined(TOR_LIB_CONFMGT_TYPEDVAR_H) */ #endif /* !defined(TOR_LIB_CONFMGT_TYPEDVAR_H) */

View File

@ -122,6 +122,15 @@ struct var_type_fns_t {
* values are valid. * values are valid.
**/ **/
bool (*ok)(const void *value, const void *params); bool (*ok)(const void *value, const void *params);
/**
* Mark a value of this variable as "fragile", so that future attempts to
* assign to this variable will replace rather than extending it.
*
* The default implementation for this function does nothing.
*
* Only meaningful for types with is_cumulative set.
**/
void (*mark_fragile)(void *value, const void *params);
}; };
/** /**
@ -142,6 +151,17 @@ struct var_type_def_t {
* calling the functions in this type's function table. * calling the functions in this type's function table.
*/ */
const void *params; const void *params;
/** True iff a variable of this type can never be set directly by name. */
bool is_unsettable;
/** True iff a variable of this type is always contained in another
* variable, and as such doesn't need to be dumped or copied
* independently. */
bool is_contained;
/** True iff a variable of this type can be set more than once without
* destroying older values. Such variables should implement "mark_fragile".
*/
bool is_cumulative;
}; };
#endif /* !defined(TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H) */ #endif /* !defined(TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H) */