mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 22:03:31 +01:00
Add a "typed_var" abstraction to implement lvalue access in C.
Right now, this has been done at a high level by confparse.c, but it makes more sense to lower it. This API is radically un-typesafe as it stands; we'll be wrapping it in a safer API as we do #30914 and lower the struct manipulation code as well. Closes ticket 30864.
This commit is contained in:
parent
5a2ab886ba
commit
c60a85d22a
@ -41,6 +41,7 @@ TOR_UTIL_LIBS = \
|
|||||||
src/lib/libtor-geoip.a \
|
src/lib/libtor-geoip.a \
|
||||||
src/lib/libtor-process.a \
|
src/lib/libtor-process.a \
|
||||||
src/lib/libtor-buf.a \
|
src/lib/libtor-buf.a \
|
||||||
|
src/lib/libtor-confmgt.a \
|
||||||
src/lib/libtor-pubsub.a \
|
src/lib/libtor-pubsub.a \
|
||||||
src/lib/libtor-dispatch.a \
|
src/lib/libtor-dispatch.a \
|
||||||
src/lib/libtor-time.a \
|
src/lib/libtor-time.a \
|
||||||
@ -54,7 +55,6 @@ TOR_UTIL_LIBS = \
|
|||||||
src/lib/libtor-math.a \
|
src/lib/libtor-math.a \
|
||||||
src/lib/libtor-meminfo.a \
|
src/lib/libtor-meminfo.a \
|
||||||
src/lib/libtor-osinfo.a \
|
src/lib/libtor-osinfo.a \
|
||||||
src/lib/libtor-confmgt.a \
|
|
||||||
src/lib/libtor-log.a \
|
src/lib/libtor-log.a \
|
||||||
src/lib/libtor-lock.a \
|
src/lib/libtor-lock.a \
|
||||||
src/lib/libtor-fdio.a \
|
src/lib/libtor-fdio.a \
|
||||||
@ -75,6 +75,7 @@ TOR_UTIL_TESTING_LIBS = \
|
|||||||
src/lib/libtor-geoip-testing.a \
|
src/lib/libtor-geoip-testing.a \
|
||||||
src/lib/libtor-process-testing.a \
|
src/lib/libtor-process-testing.a \
|
||||||
src/lib/libtor-buf-testing.a \
|
src/lib/libtor-buf-testing.a \
|
||||||
|
src/lib/libtor-confmgt-testing.a \
|
||||||
src/lib/libtor-pubsub-testing.a \
|
src/lib/libtor-pubsub-testing.a \
|
||||||
src/lib/libtor-dispatch-testing.a \
|
src/lib/libtor-dispatch-testing.a \
|
||||||
src/lib/libtor-time-testing.a \
|
src/lib/libtor-time-testing.a \
|
||||||
@ -89,7 +90,6 @@ TOR_UTIL_TESTING_LIBS = \
|
|||||||
src/lib/libtor-meminfo-testing.a \
|
src/lib/libtor-meminfo-testing.a \
|
||||||
src/lib/libtor-osinfo-testing.a \
|
src/lib/libtor-osinfo-testing.a \
|
||||||
src/lib/libtor-term-testing.a \
|
src/lib/libtor-term-testing.a \
|
||||||
src/lib/libtor-confmgt-testing.a \
|
|
||||||
src/lib/libtor-log-testing.a \
|
src/lib/libtor-log-testing.a \
|
||||||
src/lib/libtor-lock-testing.a \
|
src/lib/libtor-lock-testing.a \
|
||||||
src/lib/libtor-fdio-testing.a \
|
src/lib/libtor-fdio-testing.a \
|
||||||
|
3
changes/ticket30864
Normal file
3
changes/ticket30864
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
o Code simplification and refactoring:
|
||||||
|
- Extract our variable manipulation code from confparse.c to a new
|
||||||
|
lower-level typedvar.h module. Closes ticket 30864.
|
@ -125,8 +125,8 @@ class AutomakeChunk:
|
|||||||
Y \
|
Y \
|
||||||
Z
|
Z
|
||||||
"""
|
"""
|
||||||
self.prespace = "\t"
|
prespace = "\t"
|
||||||
self.postspace = "\t\t"
|
postspace = "\t\t"
|
||||||
for lineno, line in enumerate(self.lines):
|
for lineno, line in enumerate(self.lines):
|
||||||
m = re.match(r'(\s+)(\S+)(\s+)\\', line)
|
m = re.match(r'(\s+)(\S+)(\s+)\\', line)
|
||||||
if not m:
|
if not m:
|
||||||
@ -135,7 +135,7 @@ class AutomakeChunk:
|
|||||||
if fname > member:
|
if fname > member:
|
||||||
self.insert_before(lineno, member, prespace, postspace)
|
self.insert_before(lineno, member, prespace, postspace)
|
||||||
return
|
return
|
||||||
self.insert_at_end(member)
|
self.insert_at_end(member, prespace, postspace)
|
||||||
|
|
||||||
def insert_before(self, lineno, member, prespace, postspace):
|
def insert_before(self, lineno, member, prespace, postspace):
|
||||||
self.lines.insert(lineno,
|
self.lines.insert(lineno,
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
#include "lib/container/bitarray.h"
|
#include "lib/container/bitarray.h"
|
||||||
#include "lib/encoding/confline.h"
|
#include "lib/encoding/confline.h"
|
||||||
|
|
||||||
|
#include "lib/confmgt/typedvar.h"
|
||||||
|
|
||||||
static void config_reset(const config_format_t *fmt, void *options,
|
static void config_reset(const config_format_t *fmt, void *options,
|
||||||
const config_var_t *var, int use_defaults);
|
const config_var_t *var, int use_defaults);
|
||||||
|
|
||||||
@ -160,7 +162,6 @@ static int
|
|||||||
config_assign_value(const config_format_t *fmt, void *options,
|
config_assign_value(const config_format_t *fmt, void *options,
|
||||||
config_line_t *c, char **msg)
|
config_line_t *c, char **msg)
|
||||||
{
|
{
|
||||||
int i, ok;
|
|
||||||
const config_var_t *var;
|
const config_var_t *var;
|
||||||
void *lvalue;
|
void *lvalue;
|
||||||
|
|
||||||
@ -168,144 +169,14 @@ config_assign_value(const config_format_t *fmt, void *options,
|
|||||||
|
|
||||||
var = config_find_option(fmt, c->key);
|
var = config_find_option(fmt, c->key);
|
||||||
tor_assert(var);
|
tor_assert(var);
|
||||||
|
tor_assert(!strcmp(c->key, var->name));
|
||||||
|
|
||||||
lvalue = STRUCT_VAR_P(options, var->var_offset);
|
lvalue = STRUCT_VAR_P(options, var->var_offset);
|
||||||
|
|
||||||
switch (var->type) {
|
if (var->type == CONFIG_TYPE_ROUTERSET) {
|
||||||
|
// XXXX make the backend extensible so that we don't have to
|
||||||
|
// XXXX handle ROUTERSET specially.
|
||||||
|
|
||||||
case CONFIG_TYPE_INT:
|
|
||||||
case CONFIG_TYPE_POSINT:
|
|
||||||
i = (int)tor_parse_long(c->value, 10,
|
|
||||||
var->type==CONFIG_TYPE_INT ? INT_MIN : 0,
|
|
||||||
INT_MAX,
|
|
||||||
&ok, NULL);
|
|
||||||
if (!ok) {
|
|
||||||
tor_asprintf(msg,
|
|
||||||
"Int keyword '%s %s' is malformed or out of bounds.",
|
|
||||||
c->key, c->value);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*(int *)lvalue = i;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONFIG_TYPE_UINT64: {
|
|
||||||
uint64_t u64 = tor_parse_uint64(c->value, 10,
|
|
||||||
0, UINT64_MAX, &ok, NULL);
|
|
||||||
if (!ok) {
|
|
||||||
tor_asprintf(msg,
|
|
||||||
"uint64 keyword '%s %s' is malformed or out of bounds.",
|
|
||||||
c->key, c->value);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*(uint64_t *)lvalue = u64;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CONFIG_TYPE_CSV_INTERVAL: {
|
|
||||||
/* We used to have entire smartlists here. But now that all of our
|
|
||||||
* download schedules use exponential backoff, only the first part
|
|
||||||
* matters. */
|
|
||||||
const char *comma = strchr(c->value, ',');
|
|
||||||
const char *val = c->value;
|
|
||||||
char *tmp = NULL;
|
|
||||||
if (comma) {
|
|
||||||
tmp = tor_strndup(c->value, comma - c->value);
|
|
||||||
val = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = config_parse_interval(val, &ok);
|
|
||||||
if (!ok) {
|
|
||||||
tor_asprintf(msg,
|
|
||||||
"Interval '%s %s' is malformed or out of bounds.",
|
|
||||||
c->key, c->value);
|
|
||||||
tor_free(tmp);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*(int *)lvalue = i;
|
|
||||||
tor_free(tmp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CONFIG_TYPE_INTERVAL: {
|
|
||||||
i = config_parse_interval(c->value, &ok);
|
|
||||||
if (!ok) {
|
|
||||||
tor_asprintf(msg,
|
|
||||||
"Interval '%s %s' is malformed or out of bounds.",
|
|
||||||
c->key, c->value);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*(int *)lvalue = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CONFIG_TYPE_MSEC_INTERVAL: {
|
|
||||||
i = config_parse_msec_interval(c->value, &ok);
|
|
||||||
if (!ok) {
|
|
||||||
tor_asprintf(msg,
|
|
||||||
"Msec interval '%s %s' is malformed or out of bounds.",
|
|
||||||
c->key, c->value);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*(int *)lvalue = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CONFIG_TYPE_MEMUNIT: {
|
|
||||||
uint64_t u64 = config_parse_memunit(c->value, &ok);
|
|
||||||
if (!ok) {
|
|
||||||
tor_asprintf(msg,
|
|
||||||
"Value '%s %s' is malformed or out of bounds.",
|
|
||||||
c->key, c->value);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*(uint64_t *)lvalue = u64;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CONFIG_TYPE_BOOL:
|
|
||||||
i = (int)tor_parse_long(c->value, 10, 0, 1, &ok, NULL);
|
|
||||||
if (!ok) {
|
|
||||||
tor_asprintf(msg,
|
|
||||||
"Boolean '%s %s' expects 0 or 1.",
|
|
||||||
c->key, c->value);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*(int *)lvalue = i;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONFIG_TYPE_AUTOBOOL:
|
|
||||||
if (!strcasecmp(c->value, "auto"))
|
|
||||||
*(int *)lvalue = -1;
|
|
||||||
else if (!strcmp(c->value, "0"))
|
|
||||||
*(int *)lvalue = 0;
|
|
||||||
else if (!strcmp(c->value, "1"))
|
|
||||||
*(int *)lvalue = 1;
|
|
||||||
else {
|
|
||||||
tor_asprintf(msg, "Boolean '%s %s' expects 0, 1, or 'auto'.",
|
|
||||||
c->key, c->value);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONFIG_TYPE_STRING:
|
|
||||||
case CONFIG_TYPE_FILENAME:
|
|
||||||
tor_free(*(char **)lvalue);
|
|
||||||
*(char **)lvalue = tor_strdup(c->value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONFIG_TYPE_DOUBLE:
|
|
||||||
*(double *)lvalue = atof(c->value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONFIG_TYPE_ISOTIME:
|
|
||||||
if (parse_iso_time(c->value, (time_t *)lvalue)) {
|
|
||||||
tor_asprintf(msg,
|
|
||||||
"Invalid time '%s' for keyword '%s'", c->value, c->key);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONFIG_TYPE_ROUTERSET:
|
|
||||||
if (*(routerset_t**)lvalue) {
|
if (*(routerset_t**)lvalue) {
|
||||||
routerset_free(*(routerset_t**)lvalue);
|
routerset_free(*(routerset_t**)lvalue);
|
||||||
}
|
}
|
||||||
@ -315,50 +186,10 @@ config_assign_value(const config_format_t *fmt, void *options,
|
|||||||
c->value, c->key);
|
c->value, c->key);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case CONFIG_TYPE_CSV:
|
|
||||||
if (*(smartlist_t**)lvalue) {
|
|
||||||
SMARTLIST_FOREACH(*(smartlist_t**)lvalue, char *, cp, tor_free(cp));
|
|
||||||
smartlist_clear(*(smartlist_t**)lvalue);
|
|
||||||
} else {
|
|
||||||
*(smartlist_t**)lvalue = smartlist_new();
|
|
||||||
}
|
|
||||||
|
|
||||||
smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",",
|
|
||||||
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONFIG_TYPE_LINELIST:
|
|
||||||
case CONFIG_TYPE_LINELIST_S:
|
|
||||||
{
|
|
||||||
config_line_t *lastval = *(config_line_t**)lvalue;
|
|
||||||
if (lastval && lastval->fragile) {
|
|
||||||
if (c->command != CONFIG_LINE_APPEND) {
|
|
||||||
config_free_lines(lastval);
|
|
||||||
*(config_line_t**)lvalue = NULL;
|
|
||||||
} else {
|
|
||||||
lastval->fragile = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config_line_append((config_line_t**)lvalue, c->key, c->value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_OBSOLETE:
|
|
||||||
log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key);
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_LINELIST_V:
|
|
||||||
tor_asprintf(msg,
|
|
||||||
"You may not provide a value for virtual option '%s'", c->key);
|
|
||||||
return -1;
|
|
||||||
// LCOV_EXCL_START
|
|
||||||
default:
|
|
||||||
tor_assert_unreached();
|
|
||||||
break;
|
|
||||||
// LCOV_EXCL_STOP
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return typed_var_kvassign(lvalue, c, msg, var->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Mark every linelist in <b>options</b> "fragile", so that fresh assignments
|
/** Mark every linelist in <b>options</b> "fragile", so that fresh assignments
|
||||||
@ -544,99 +375,14 @@ config_get_assigned_option(const config_format_t *fmt, const void *options,
|
|||||||
}
|
}
|
||||||
value = STRUCT_VAR_P(options, var->var_offset);
|
value = STRUCT_VAR_P(options, var->var_offset);
|
||||||
|
|
||||||
|
if (var->type == CONFIG_TYPE_ROUTERSET) {
|
||||||
|
// XXXX make the backend extensible so that we don't have to
|
||||||
|
// XXXX handle ROUTERSET specially.
|
||||||
result = tor_malloc_zero(sizeof(config_line_t));
|
result = tor_malloc_zero(sizeof(config_line_t));
|
||||||
result->key = tor_strdup(var->name);
|
result->key = tor_strdup(var->name);
|
||||||
switch (var->type)
|
|
||||||
{
|
|
||||||
case CONFIG_TYPE_STRING:
|
|
||||||
case CONFIG_TYPE_FILENAME:
|
|
||||||
if (*(char**)value) {
|
|
||||||
result->value = tor_strdup(*(char**)value);
|
|
||||||
} else {
|
|
||||||
tor_free(result->key);
|
|
||||||
tor_free(result);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_ISOTIME:
|
|
||||||
if (*(time_t*)value) {
|
|
||||||
result->value = tor_malloc(ISO_TIME_LEN+1);
|
|
||||||
format_iso_time(result->value, *(time_t*)value);
|
|
||||||
} else {
|
|
||||||
tor_free(result->key);
|
|
||||||
tor_free(result);
|
|
||||||
}
|
|
||||||
escape_val = 0; /* Can't need escape. */
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_CSV_INTERVAL:
|
|
||||||
case CONFIG_TYPE_INTERVAL:
|
|
||||||
case CONFIG_TYPE_MSEC_INTERVAL:
|
|
||||||
case CONFIG_TYPE_POSINT:
|
|
||||||
case CONFIG_TYPE_INT:
|
|
||||||
/* This means every or_options_t uint or bool element
|
|
||||||
* needs to be an int. Not, say, a uint16_t or char. */
|
|
||||||
tor_asprintf(&result->value, "%d", *(int*)value);
|
|
||||||
escape_val = 0; /* Can't need escape. */
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_UINT64: /* Fall through */
|
|
||||||
case CONFIG_TYPE_MEMUNIT:
|
|
||||||
tor_asprintf(&result->value, "%"PRIu64,
|
|
||||||
(*(uint64_t*)value));
|
|
||||||
escape_val = 0; /* Can't need escape. */
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_DOUBLE:
|
|
||||||
tor_asprintf(&result->value, "%f", *(double*)value);
|
|
||||||
escape_val = 0; /* Can't need escape. */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONFIG_TYPE_AUTOBOOL:
|
|
||||||
if (*(int*)value == -1) {
|
|
||||||
result->value = tor_strdup("auto");
|
|
||||||
escape_val = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* fall through */
|
|
||||||
case CONFIG_TYPE_BOOL:
|
|
||||||
result->value = tor_strdup(*(int*)value ? "1" : "0");
|
|
||||||
escape_val = 0; /* Can't need escape. */
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_ROUTERSET:
|
|
||||||
result->value = routerset_to_string(*(routerset_t**)value);
|
result->value = routerset_to_string(*(routerset_t**)value);
|
||||||
break;
|
} else {
|
||||||
case CONFIG_TYPE_CSV:
|
result = typed_var_kvencode(var->name, value, var->type);
|
||||||
if (*(smartlist_t**)value)
|
|
||||||
result->value =
|
|
||||||
smartlist_join_strings(*(smartlist_t**)value, ",", 0, NULL);
|
|
||||||
else
|
|
||||||
result->value = tor_strdup("");
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_OBSOLETE:
|
|
||||||
log_fn(LOG_INFO, LD_CONFIG,
|
|
||||||
"You asked me for the value of an obsolete config option '%s'.",
|
|
||||||
key);
|
|
||||||
tor_free(result->key);
|
|
||||||
tor_free(result);
|
|
||||||
return NULL;
|
|
||||||
case CONFIG_TYPE_LINELIST_S:
|
|
||||||
tor_free(result->key);
|
|
||||||
tor_free(result);
|
|
||||||
result = config_lines_dup_and_filter(*(const config_line_t **)value,
|
|
||||||
key);
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_LINELIST:
|
|
||||||
case CONFIG_TYPE_LINELIST_V:
|
|
||||||
tor_free(result->key);
|
|
||||||
tor_free(result);
|
|
||||||
result = config_lines_dup(*(const config_line_t**)value);
|
|
||||||
break;
|
|
||||||
// LCOV_EXCL_START
|
|
||||||
default:
|
|
||||||
tor_free(result->key);
|
|
||||||
tor_free(result);
|
|
||||||
log_warn(LD_BUG,"Unknown type %d for known key '%s'",
|
|
||||||
var->type, key);
|
|
||||||
return NULL;
|
|
||||||
// LCOV_EXCL_STOP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (escape_val) {
|
if (escape_val) {
|
||||||
@ -765,56 +511,17 @@ config_clear(const config_format_t *fmt, void *options,
|
|||||||
{
|
{
|
||||||
void *lvalue = STRUCT_VAR_P(options, var->var_offset);
|
void *lvalue = STRUCT_VAR_P(options, var->var_offset);
|
||||||
(void)fmt; /* unused */
|
(void)fmt; /* unused */
|
||||||
switch (var->type) {
|
if (var->type == CONFIG_TYPE_ROUTERSET) {
|
||||||
case CONFIG_TYPE_STRING:
|
// XXXX make the backend extensible so that we don't have to
|
||||||
case CONFIG_TYPE_FILENAME:
|
// XXXX handle ROUTERSET specially.
|
||||||
tor_free(*(char**)lvalue);
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_DOUBLE:
|
|
||||||
*(double*)lvalue = 0.0;
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_ISOTIME:
|
|
||||||
*(time_t*)lvalue = 0;
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_CSV_INTERVAL:
|
|
||||||
case CONFIG_TYPE_INTERVAL:
|
|
||||||
case CONFIG_TYPE_MSEC_INTERVAL:
|
|
||||||
case CONFIG_TYPE_POSINT:
|
|
||||||
case CONFIG_TYPE_INT:
|
|
||||||
case CONFIG_TYPE_BOOL:
|
|
||||||
*(int*)lvalue = 0;
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_AUTOBOOL:
|
|
||||||
*(int*)lvalue = -1;
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_UINT64:
|
|
||||||
case CONFIG_TYPE_MEMUNIT:
|
|
||||||
*(uint64_t*)lvalue = 0;
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_ROUTERSET:
|
|
||||||
if (*(routerset_t**)lvalue) {
|
if (*(routerset_t**)lvalue) {
|
||||||
routerset_free(*(routerset_t**)lvalue);
|
routerset_free(*(routerset_t**)lvalue);
|
||||||
*(routerset_t**)lvalue = NULL;
|
*(routerset_t**)lvalue = NULL;
|
||||||
}
|
}
|
||||||
break;
|
return;
|
||||||
case CONFIG_TYPE_CSV:
|
|
||||||
if (*(smartlist_t**)lvalue) {
|
|
||||||
SMARTLIST_FOREACH(*(smartlist_t **)lvalue, char *, cp, tor_free(cp));
|
|
||||||
smartlist_free(*(smartlist_t **)lvalue);
|
|
||||||
*(smartlist_t **)lvalue = NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_LINELIST:
|
|
||||||
case CONFIG_TYPE_LINELIST_S:
|
|
||||||
config_free_lines(*(config_line_t **)lvalue);
|
|
||||||
*(config_line_t **)lvalue = NULL;
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_LINELIST_V:
|
|
||||||
/* handled by linelist_s. */
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_OBSOLETE:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typed_var_free(lvalue, var->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clear the option indexed by <b>var</b> in <b>options</b>. Then if
|
/** Clear the option indexed by <b>var</b> in <b>options</b>. Then if
|
||||||
|
@ -7,6 +7,22 @@
|
|||||||
/**
|
/**
|
||||||
* @file conftypes.h
|
* @file conftypes.h
|
||||||
* @brief Types used to specify configurable options.
|
* @brief Types used to specify configurable options.
|
||||||
|
*
|
||||||
|
* This header defines the types that different modules will use in order to
|
||||||
|
* declare their configuration and state variables, and tell the configuration
|
||||||
|
* management code about those variables. From the individual module's point
|
||||||
|
* of view, its configuration and state are simply data structures.
|
||||||
|
*
|
||||||
|
* For defining new variable types, see var_type_def_st.h.
|
||||||
|
*
|
||||||
|
* For the code that manipulates variables defined via this module, see
|
||||||
|
* lib/confmgt/, especially typedvar.h and (later) structvar.h. The
|
||||||
|
* configuration manager is responsible for encoding, decoding, and
|
||||||
|
* maintaining the configuration structures used by the various modules.
|
||||||
|
*
|
||||||
|
* STATUS NOTE: This is a work in process refactoring. It is not yet possible
|
||||||
|
* for modules to define their own variables, and much of the configuration
|
||||||
|
* management code is still in src/app/config/.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
#ifndef TOR_SRC_LIB_CONF_CONFTYPES_H
|
#ifndef TOR_SRC_LIB_CONF_CONFTYPES_H
|
||||||
|
@ -2,6 +2,8 @@ orconfig.h
|
|||||||
lib/cc/*.h
|
lib/cc/*.h
|
||||||
lib/conf/*.h
|
lib/conf/*.h
|
||||||
lib/confmgt/*.h
|
lib/confmgt/*.h
|
||||||
|
lib/container/*.h
|
||||||
|
lib/encoding/*.h
|
||||||
lib/log/*.h
|
lib/log/*.h
|
||||||
lib/malloc/*.h
|
lib/malloc/*.h
|
||||||
lib/string/*.h
|
lib/string/*.h
|
||||||
|
@ -6,6 +6,8 @@ endif
|
|||||||
|
|
||||||
# ADD_C_FILE: INSERT SOURCES HERE.
|
# ADD_C_FILE: INSERT SOURCES HERE.
|
||||||
src_lib_libtor_confmgt_a_SOURCES = \
|
src_lib_libtor_confmgt_a_SOURCES = \
|
||||||
|
src/lib/confmgt/type_defs.c \
|
||||||
|
src/lib/confmgt/typedvar.c \
|
||||||
src/lib/confmgt/unitparse.c
|
src/lib/confmgt/unitparse.c
|
||||||
|
|
||||||
src_lib_libtor_confmgt_testing_a_SOURCES = \
|
src_lib_libtor_confmgt_testing_a_SOURCES = \
|
||||||
@ -15,4 +17,7 @@ src_lib_libtor_confmgt_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
|
|||||||
|
|
||||||
# ADD_C_FILE: INSERT HEADERS HERE.
|
# ADD_C_FILE: INSERT HEADERS HERE.
|
||||||
noinst_HEADERS += \
|
noinst_HEADERS += \
|
||||||
src/lib/confmgt/unitparse.h
|
src/lib/confmgt/type_defs.h \
|
||||||
|
src/lib/confmgt/typedvar.h \
|
||||||
|
src/lib/confmgt/unitparse.h \
|
||||||
|
src/lib/confmgt/var_type_def_st.h
|
||||||
|
727
src/lib/confmgt/type_defs.c
Normal file
727
src/lib/confmgt/type_defs.c
Normal file
@ -0,0 +1,727 @@
|
|||||||
|
/* 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 type_defs.c
|
||||||
|
* @brief Definitions for various low-level configuration types.
|
||||||
|
*
|
||||||
|
* This module creates a number of var_type_def_t objects, to be used by
|
||||||
|
* typedvar.c in manipulating variables.
|
||||||
|
*
|
||||||
|
* The types here are common types that can be implemented with Tor's
|
||||||
|
* low-level functionality. To define new types, see var_type_def_st.h.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "orconfig.h"
|
||||||
|
#include "lib/conf/conftypes.h"
|
||||||
|
#include "lib/confmgt/typedvar.h"
|
||||||
|
#include "lib/confmgt/type_defs.h"
|
||||||
|
#include "lib/confmgt/unitparse.h"
|
||||||
|
|
||||||
|
#include "lib/cc/compat_compiler.h"
|
||||||
|
#include "lib/conf/conftypes.h"
|
||||||
|
#include "lib/container/smartlist.h"
|
||||||
|
#include "lib/encoding/confline.h"
|
||||||
|
#include "lib/encoding/time_fmt.h"
|
||||||
|
#include "lib/log/escape.h"
|
||||||
|
#include "lib/log/log.h"
|
||||||
|
#include "lib/log/util_bug.h"
|
||||||
|
#include "lib/malloc/malloc.h"
|
||||||
|
#include "lib/string/parse_int.h"
|
||||||
|
#include "lib/string/printf.h"
|
||||||
|
|
||||||
|
#include "lib/confmgt/var_type_def_st.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//////
|
||||||
|
// CONFIG_TYPE_STRING
|
||||||
|
// CONFIG_TYPE_FILENAME
|
||||||
|
//
|
||||||
|
// These two types are the same for now, but they have different names.
|
||||||
|
//////
|
||||||
|
|
||||||
|
static int
|
||||||
|
string_parse(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)errmsg;
|
||||||
|
char **p = (char**)target;
|
||||||
|
*p = tor_strdup(value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
string_encode(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
const char **p = (const char**)value;
|
||||||
|
return *p ? tor_strdup(*p) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
string_clear(void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
char **p = (char**)value;
|
||||||
|
tor_free(*p); // sets *p to NULL.
|
||||||
|
}
|
||||||
|
|
||||||
|
static const var_type_fns_t string_fns = {
|
||||||
|
.parse = string_parse,
|
||||||
|
.encode = string_encode,
|
||||||
|
.clear = string_clear,
|
||||||
|
};
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_INT
|
||||||
|
// CONFIG_TYPE_POSINT
|
||||||
|
//
|
||||||
|
// These types are implemented as int, possibly with a restricted range.
|
||||||
|
/////
|
||||||
|
|
||||||
|
typedef struct int_type_params_t {
|
||||||
|
int minval;
|
||||||
|
int maxval;
|
||||||
|
} int_parse_params_t;
|
||||||
|
|
||||||
|
static const int_parse_params_t INT_PARSE_UNRESTRICTED = {
|
||||||
|
.minval = INT_MIN,
|
||||||
|
.maxval = INT_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int_parse_params_t INT_PARSE_POSINT = {
|
||||||
|
.minval = 0,
|
||||||
|
.maxval = INT_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
int_parse(void *target, const char *value, char **errmsg, const void *params)
|
||||||
|
{
|
||||||
|
const int_parse_params_t *pp;
|
||||||
|
if (params) {
|
||||||
|
pp = params;
|
||||||
|
} else {
|
||||||
|
pp = &INT_PARSE_UNRESTRICTED;
|
||||||
|
}
|
||||||
|
int *p = target;
|
||||||
|
int ok=0;
|
||||||
|
*p = (int)tor_parse_long(value, 10, pp->minval, pp->maxval, &ok, NULL);
|
||||||
|
if (!ok) {
|
||||||
|
tor_asprintf(errmsg, "Integer %s is malformed or out of bounds.",
|
||||||
|
value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
int_encode(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
int v = *(int*)value;
|
||||||
|
char *result;
|
||||||
|
tor_asprintf(&result, "%d", v);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
int_clear(void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
*(int*)value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
int_ok(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
const int_parse_params_t *pp = params;
|
||||||
|
if (pp) {
|
||||||
|
int v = *(int*)value;
|
||||||
|
return pp->minval <= v && v <= pp->maxval;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const var_type_fns_t int_fns = {
|
||||||
|
.parse = int_parse,
|
||||||
|
.encode = int_encode,
|
||||||
|
.clear = int_clear,
|
||||||
|
.ok = int_ok,
|
||||||
|
};
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_UINT64
|
||||||
|
//
|
||||||
|
// This type is an unrestricted u64.
|
||||||
|
/////
|
||||||
|
|
||||||
|
static int
|
||||||
|
uint64_parse(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)errmsg;
|
||||||
|
uint64_t *p = target;
|
||||||
|
int ok=0;
|
||||||
|
*p = tor_parse_uint64(value, 10, 0, UINT64_MAX, &ok, NULL);
|
||||||
|
if (!ok) {
|
||||||
|
tor_asprintf(errmsg, "Integer %s is malformed or out of bounds.",
|
||||||
|
value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
uint64_encode(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
uint64_t v = *(uint64_t*)value;
|
||||||
|
char *result;
|
||||||
|
tor_asprintf(&result, "%"PRIu64, v);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
uint64_clear(void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
*(uint64_t*)value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const var_type_fns_t uint64_fns = {
|
||||||
|
.parse = uint64_parse,
|
||||||
|
.encode = uint64_encode,
|
||||||
|
.clear = uint64_clear,
|
||||||
|
};
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_INTERVAL
|
||||||
|
// CONFIG_TYPE_MSEC_INTERVAL
|
||||||
|
// CONFIG_TYPE_MEMUNIT
|
||||||
|
//
|
||||||
|
// These types are implemented using the config_parse_units() function.
|
||||||
|
// The intervals are stored as ints, whereas memory units are stored as
|
||||||
|
// uint64_ts.
|
||||||
|
/////
|
||||||
|
|
||||||
|
static int
|
||||||
|
units_parse_u64(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
const unit_table_t *table = params;
|
||||||
|
tor_assert(table);
|
||||||
|
uint64_t *v = (uint64_t*)target;
|
||||||
|
int ok=1;
|
||||||
|
*v = config_parse_units(value, table, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
*errmsg = tor_strdup("Provided value is malformed or out of bounds.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
units_parse_int(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
const unit_table_t *table = params;
|
||||||
|
tor_assert(table);
|
||||||
|
int *v = (int*)target;
|
||||||
|
int ok=1;
|
||||||
|
uint64_t u64 = config_parse_units(value, table, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
*errmsg = tor_strdup("Provided value is malformed or out of bounds.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (u64 > INT_MAX) {
|
||||||
|
tor_asprintf(errmsg, "Provided value %s is too large", value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*v = (int) u64;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
units_ok_int(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
int v = *(int*)value;
|
||||||
|
return v >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const var_type_fns_t memunit_fns = {
|
||||||
|
.parse = units_parse_u64,
|
||||||
|
.encode = uint64_encode, // doesn't use params
|
||||||
|
.clear = uint64_clear, // doesn't use params
|
||||||
|
};
|
||||||
|
|
||||||
|
static const var_type_fns_t interval_fns = {
|
||||||
|
.parse = units_parse_int,
|
||||||
|
.encode = int_encode, // doesn't use params
|
||||||
|
.clear = int_clear, // doesn't use params,
|
||||||
|
.ok = units_ok_int // can't use int_ok, since that expects int params.
|
||||||
|
};
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_DOUBLE
|
||||||
|
//
|
||||||
|
// This is a nice simple double.
|
||||||
|
/////
|
||||||
|
|
||||||
|
static int
|
||||||
|
double_parse(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)errmsg;
|
||||||
|
double *v = (double*)target;
|
||||||
|
// XXXX This is the preexisting behavior, but we should detect errors here.
|
||||||
|
*v = atof(value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
double_encode(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
double v = *(double*)value;
|
||||||
|
char *result;
|
||||||
|
tor_asprintf(&result, "%f", v);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
double_clear(void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
double *v = (double *)value;
|
||||||
|
*v = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const var_type_fns_t double_fns = {
|
||||||
|
.parse = double_parse,
|
||||||
|
.encode = double_encode,
|
||||||
|
.clear = double_clear,
|
||||||
|
};
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_BOOL
|
||||||
|
// CONFIG_TYPE_AUTOBOOL
|
||||||
|
//
|
||||||
|
// These types are implemented as a case-insensitive string-to-integer
|
||||||
|
// mapping.
|
||||||
|
/////
|
||||||
|
|
||||||
|
typedef struct enumeration_table_t {
|
||||||
|
const char *name;
|
||||||
|
int value;
|
||||||
|
} enumeration_table_t;
|
||||||
|
|
||||||
|
static int
|
||||||
|
enum_parse(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
const enumeration_table_t *table = params;
|
||||||
|
int *p = (int *)target;
|
||||||
|
for (; table->name; ++table) {
|
||||||
|
if (!strcasecmp(value, table->name)) {
|
||||||
|
*p = table->value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tor_asprintf(errmsg, "Unrecognized value %s.", value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
enum_encode(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
int v = *(const int*)value;
|
||||||
|
const enumeration_table_t *table = params;
|
||||||
|
for (; table->name; ++table) {
|
||||||
|
if (v == table->value)
|
||||||
|
return tor_strdup(table->name);
|
||||||
|
}
|
||||||
|
return NULL; // error.
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
enum_clear(void *value, const void *params)
|
||||||
|
{
|
||||||
|
int *p = (int*)value;
|
||||||
|
const enumeration_table_t *table = params;
|
||||||
|
tor_assert(table->name);
|
||||||
|
*p = table->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
enum_ok(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
int v = *(const int*)value;
|
||||||
|
const enumeration_table_t *table = params;
|
||||||
|
for (; table->name; ++table) {
|
||||||
|
if (v == table->value)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const enumeration_table_t enum_table_bool[] = {
|
||||||
|
{ "0", 0 },
|
||||||
|
{ "1", 1 },
|
||||||
|
{ NULL, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const enumeration_table_t enum_table_autobool[] = {
|
||||||
|
{ "0", 0 },
|
||||||
|
{ "1", 1 },
|
||||||
|
{ "auto", -1 },
|
||||||
|
{ NULL, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const var_type_fns_t enum_fns = {
|
||||||
|
.parse = enum_parse,
|
||||||
|
.encode = enum_encode,
|
||||||
|
.clear = enum_clear,
|
||||||
|
.ok = enum_ok,
|
||||||
|
};
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_ISOTIME
|
||||||
|
//
|
||||||
|
// This is a time_t, encoded in ISO8601 format.
|
||||||
|
/////
|
||||||
|
|
||||||
|
static int
|
||||||
|
time_parse(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
(void) params;
|
||||||
|
time_t *p = target;
|
||||||
|
if (parse_iso_time(value, p) < 0) {
|
||||||
|
tor_asprintf(errmsg, "Invalid time %s", escaped(value));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
time_encode(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
time_t v = *(const time_t *)value;
|
||||||
|
char *result = tor_malloc(ISO_TIME_LEN+1);
|
||||||
|
format_iso_time(result, v);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
time_clear(void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
time_t *t = value;
|
||||||
|
*t = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const var_type_fns_t time_fns = {
|
||||||
|
.parse = time_parse,
|
||||||
|
.encode = time_encode,
|
||||||
|
.clear = time_clear,
|
||||||
|
};
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_CSV
|
||||||
|
//
|
||||||
|
// This type is a comma-separated list of strings, stored in a smartlist_t.
|
||||||
|
// An empty list may be encoded either as an empty smartlist, or as NULL.
|
||||||
|
/////
|
||||||
|
|
||||||
|
static int
|
||||||
|
csv_parse(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)errmsg;
|
||||||
|
smartlist_t **sl = (smartlist_t**)target;
|
||||||
|
*sl = smartlist_new();
|
||||||
|
smartlist_split_string(*sl, value, ",",
|
||||||
|
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
csv_encode(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
const smartlist_t *sl = *(const smartlist_t **)value;
|
||||||
|
if (! sl)
|
||||||
|
return tor_strdup("");
|
||||||
|
|
||||||
|
return smartlist_join_strings(*(smartlist_t**)value, ",", 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
csv_clear(void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
smartlist_t **sl = (smartlist_t**)value;
|
||||||
|
if (!*sl)
|
||||||
|
return;
|
||||||
|
SMARTLIST_FOREACH(*sl, char *, cp, tor_free(cp));
|
||||||
|
smartlist_free(*sl); // clears pointer.
|
||||||
|
}
|
||||||
|
|
||||||
|
static const var_type_fns_t csv_fns = {
|
||||||
|
.parse = csv_parse,
|
||||||
|
.encode = csv_encode,
|
||||||
|
.clear = csv_clear,
|
||||||
|
};
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_CSV_INTERVAL
|
||||||
|
//
|
||||||
|
// This type used to be a list of time intervals, used to determine a download
|
||||||
|
// schedule. Now, only the first interval counts: everything after the first
|
||||||
|
// comma is discarded.
|
||||||
|
/////
|
||||||
|
|
||||||
|
static int
|
||||||
|
legacy_csv_interval_parse(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
/* We used to have entire smartlists here. But now that all of our
|
||||||
|
* download schedules use exponential backoff, only the first part
|
||||||
|
* matters. */
|
||||||
|
const char *comma = strchr(value, ',');
|
||||||
|
const char *val = value;
|
||||||
|
char *tmp = NULL;
|
||||||
|
if (comma) {
|
||||||
|
tmp = tor_strndup(val, comma - val);
|
||||||
|
val = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rv = units_parse_int(target, val, errmsg, &time_units);
|
||||||
|
tor_free(tmp);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const var_type_fns_t legacy_csv_interval_fns = {
|
||||||
|
.parse = legacy_csv_interval_parse,
|
||||||
|
.encode = int_encode,
|
||||||
|
.clear = int_clear,
|
||||||
|
};
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_LINELIST
|
||||||
|
// CONFIG_TYPE_LINELIST_S
|
||||||
|
// CONFIG_TYPE_LINELIST_V
|
||||||
|
//
|
||||||
|
// A linelist is a raw config_line_t list. Order is preserved.
|
||||||
|
//
|
||||||
|
// The LINELIST type is used for homogeneous lists, where all the lines
|
||||||
|
// have the same key.
|
||||||
|
//
|
||||||
|
// The LINELIST_S and LINELIST_V types are used for the case where multiple
|
||||||
|
// lines of different keys are kept in a single list, to preserve their
|
||||||
|
// relative order. The unified list is stored as a "virtual" variable whose
|
||||||
|
// type is LINELIST_V; the individual sublists are treated as variables of
|
||||||
|
// type LINELIST_S.
|
||||||
|
//
|
||||||
|
// A linelist may be fragile or non-fragile. Assigning a line to a fragile
|
||||||
|
// linelist replaces the list with the line. If the line has the "APPEND"
|
||||||
|
// command set on it, or if the list is non-fragile, the line is appended.
|
||||||
|
// Either way, the new list is non-fragile.
|
||||||
|
/////
|
||||||
|
|
||||||
|
static int
|
||||||
|
linelist_kv_parse(void *target, const struct config_line_t *line,
|
||||||
|
char **errmsg, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)errmsg;
|
||||||
|
config_line_t **lines = target;
|
||||||
|
|
||||||
|
if (*lines && (*lines)->fragile) {
|
||||||
|
if (line->command == CONFIG_LINE_APPEND) {
|
||||||
|
(*lines)->fragile = 0;
|
||||||
|
} else {
|
||||||
|
config_free_lines(*lines); // sets it to NULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config_line_append(lines, line->key, line->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
linelist_kv_virt_noparse(void *target, const struct config_line_t *line,
|
||||||
|
char **errmsg, const void *params)
|
||||||
|
{
|
||||||
|
(void)target;
|
||||||
|
(void)line;
|
||||||
|
(void)params;
|
||||||
|
*errmsg = tor_strdup("Cannot assign directly to virtual option.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct config_line_t *
|
||||||
|
linelist_kv_encode(const char *key, const void *value,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
(void)key;
|
||||||
|
(void)params;
|
||||||
|
config_line_t *lines = *(config_line_t **)value;
|
||||||
|
return config_lines_dup(lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct config_line_t *
|
||||||
|
linelist_s_kv_encode(const char *key, const void *value,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
config_line_t *lines = *(config_line_t **)value;
|
||||||
|
return config_lines_dup_and_filter(lines, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
linelist_clear(void *target, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
config_line_t **lines = target;
|
||||||
|
config_free_lines(*lines); // sets it to NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
linelist_eq(const void *a, const void *b, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
const config_line_t *lines_a = *(const config_line_t **)a;
|
||||||
|
const config_line_t *lines_b = *(const config_line_t **)b;
|
||||||
|
return config_lines_eq(lines_a, lines_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
linelist_copy(void *target, const void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
config_line_t **ptr = (config_line_t **)target;
|
||||||
|
const config_line_t *val = *(const config_line_t **)value;
|
||||||
|
config_free_lines(*ptr);
|
||||||
|
*ptr = config_lines_dup(val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const var_type_fns_t linelist_fns = {
|
||||||
|
.kv_parse = linelist_kv_parse,
|
||||||
|
.kv_encode = linelist_kv_encode,
|
||||||
|
.clear = linelist_clear,
|
||||||
|
.eq = linelist_eq,
|
||||||
|
.copy = linelist_copy,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const var_type_fns_t linelist_v_fns = {
|
||||||
|
.kv_parse = linelist_kv_virt_noparse,
|
||||||
|
.kv_encode = linelist_kv_encode,
|
||||||
|
.clear = linelist_clear,
|
||||||
|
.eq = linelist_eq,
|
||||||
|
.copy = linelist_copy,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const var_type_fns_t linelist_s_fns = {
|
||||||
|
.kv_parse = linelist_kv_parse,
|
||||||
|
.kv_encode = linelist_s_kv_encode,
|
||||||
|
.clear = linelist_clear,
|
||||||
|
.eq = linelist_eq,
|
||||||
|
.copy = linelist_copy,
|
||||||
|
};
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_ROUTERSET
|
||||||
|
//
|
||||||
|
// XXXX This type is not implemented here, since routerset_t is not available
|
||||||
|
// XXXX to this module.
|
||||||
|
/////
|
||||||
|
|
||||||
|
/////
|
||||||
|
// CONFIG_TYPE_OBSOLETE
|
||||||
|
//
|
||||||
|
// Used to indicate an obsolete option.
|
||||||
|
//
|
||||||
|
// XXXX This is not a type, and should be handled at a higher level of
|
||||||
|
// XXXX abstraction.
|
||||||
|
/////
|
||||||
|
|
||||||
|
static int
|
||||||
|
ignore_parse(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params)
|
||||||
|
{
|
||||||
|
(void)target;
|
||||||
|
(void)value;
|
||||||
|
(void)errmsg;
|
||||||
|
(void)params;
|
||||||
|
// XXXX move this to a higher level, once such a level exists.
|
||||||
|
log_warn(LD_GENERAL, "Skipping obsolete configuration option.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ignore_encode(const void *value, const void *params)
|
||||||
|
{
|
||||||
|
(void)value;
|
||||||
|
(void)params;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const var_type_fns_t ignore_fns = {
|
||||||
|
.parse = ignore_parse,
|
||||||
|
.encode = ignore_encode,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table mapping conf_type_t values to var_type_def_t objects.
|
||||||
|
**/
|
||||||
|
static const var_type_def_t type_definitions_table[] = {
|
||||||
|
[CONFIG_TYPE_STRING] = { "String", &string_fns, NULL },
|
||||||
|
[CONFIG_TYPE_FILENAME] = { "Filename", &string_fns, NULL },
|
||||||
|
[CONFIG_TYPE_INT] = { "SignedInteger", &int_fns, &INT_PARSE_UNRESTRICTED },
|
||||||
|
[CONFIG_TYPE_POSINT] = { "Integer", &int_fns, &INT_PARSE_POSINT },
|
||||||
|
[CONFIG_TYPE_UINT64] = { "Integer", &uint64_fns, NULL, },
|
||||||
|
[CONFIG_TYPE_MEMUNIT] = { "DataSize", &memunit_fns, &memory_units },
|
||||||
|
[CONFIG_TYPE_INTERVAL] = { "TimeInterval", &interval_fns, &time_units },
|
||||||
|
[CONFIG_TYPE_MSEC_INTERVAL] = { "TimeMsecInterval", &interval_fns,
|
||||||
|
&time_msec_units },
|
||||||
|
[CONFIG_TYPE_DOUBLE] = { "Float", &double_fns, NULL },
|
||||||
|
[CONFIG_TYPE_BOOL] = { "Boolean", &enum_fns, &enum_table_bool },
|
||||||
|
[CONFIG_TYPE_AUTOBOOL] = { "Boolean+Auto", &enum_fns, &enum_table_autobool },
|
||||||
|
[CONFIG_TYPE_ISOTIME] = { "Time", &time_fns, NULL },
|
||||||
|
[CONFIG_TYPE_CSV] = { "CommaList", &csv_fns, NULL },
|
||||||
|
[CONFIG_TYPE_CSV_INTERVAL] = { "TimeInterval", &legacy_csv_interval_fns,
|
||||||
|
NULL },
|
||||||
|
[CONFIG_TYPE_LINELIST] = { "LineList", &linelist_fns, NULL },
|
||||||
|
[CONFIG_TYPE_LINELIST_S] = { "Dependent", &linelist_s_fns, NULL },
|
||||||
|
[CONFIG_TYPE_LINELIST_V] = { "Virtual", &linelist_v_fns, NULL },
|
||||||
|
[CONFIG_TYPE_OBSOLETE] = { "Obsolete", &ignore_fns, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a pointer to the var_type_def_t object for the given
|
||||||
|
* config_type_t value, or NULL if no such type definition exists.
|
||||||
|
**/
|
||||||
|
const var_type_def_t *
|
||||||
|
lookup_type_def(config_type_t type)
|
||||||
|
{
|
||||||
|
int t = type;
|
||||||
|
tor_assert(t >= 0);
|
||||||
|
if (t >= (int)ARRAY_LENGTH(type_definitions_table))
|
||||||
|
return NULL;
|
||||||
|
return &type_definitions_table[t];
|
||||||
|
}
|
17
src/lib/confmgt/type_defs.h
Normal file
17
src/lib/confmgt/type_defs.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/* 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 type_defs.h
|
||||||
|
* @brief Header for lib/confmgt/type_defs.c
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef TOR_LIB_CONFMGT_TYPE_DEFS_H
|
||||||
|
#define TOR_LIB_CONFMGT_TYPE_DEFS_H
|
||||||
|
|
||||||
|
const struct var_type_def_t *lookup_type_def(config_type_t type);
|
||||||
|
|
||||||
|
#endif /* !defined(TOR_LIB_CONFMGT_TYPE_DEFS_H) */
|
305
src/lib/confmgt/typedvar.c
Normal file
305
src/lib/confmgt/typedvar.c
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
/* 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 typedvar.c
|
||||||
|
* @brief Functions for accessing a pointer as an object of a given type.
|
||||||
|
*
|
||||||
|
* These functions represent a low-level API for accessing a typed variable.
|
||||||
|
* They are used in the configuration system to examine and set fields in
|
||||||
|
* configuration objects used by individual modules.
|
||||||
|
*
|
||||||
|
* Almost no code should call these directly.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "orconfig.h"
|
||||||
|
#include "lib/conf/conftypes.h"
|
||||||
|
#include "lib/confmgt/type_defs.h"
|
||||||
|
#include "lib/confmgt/typedvar.h"
|
||||||
|
#include "lib/encoding/confline.h"
|
||||||
|
#include "lib/log/escape.h"
|
||||||
|
#include "lib/log/log.h"
|
||||||
|
#include "lib/log/util_bug.h"
|
||||||
|
#include "lib/malloc/malloc.h"
|
||||||
|
#include "lib/string/util_string.h"
|
||||||
|
|
||||||
|
#include "lib/confmgt/var_type_def_st.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to parse a string in <b>value</b> that encodes an object of the type
|
||||||
|
* defined by <b>def</b>.
|
||||||
|
*
|
||||||
|
* On success, adjust the lvalue pointed to by <b>target</b> to hold that
|
||||||
|
* value, and return 0. On failure, set *<b>errmsg</b> to a newly allocated
|
||||||
|
* string holding an error message, and return -1.
|
||||||
|
**/
|
||||||
|
int
|
||||||
|
typed_var_assign_ex(void *target, const char *value, char **errmsg,
|
||||||
|
const var_type_def_t *def)
|
||||||
|
{
|
||||||
|
if (BUG(!def))
|
||||||
|
return -1;
|
||||||
|
// clear old value if needed.
|
||||||
|
typed_var_free_ex(target, def);
|
||||||
|
|
||||||
|
tor_assert(def->fns->parse);
|
||||||
|
return def->fns->parse(target, value, errmsg, def->params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to parse a single line from the head of<b>line</b> that encodes an
|
||||||
|
* object of the type defined in <b>def</b>. On success and failure, behave as
|
||||||
|
* typed_var_assign_ex().
|
||||||
|
*
|
||||||
|
* All types for which keys are significant should use this function.
|
||||||
|
*
|
||||||
|
* Note that although multiple lines may be provided in <b>line</b>,
|
||||||
|
* only the first one is handled by this function.
|
||||||
|
**/
|
||||||
|
int
|
||||||
|
typed_var_kvassign_ex(void *target, const config_line_t *line,
|
||||||
|
char **errmsg, const var_type_def_t *def)
|
||||||
|
{
|
||||||
|
if (BUG(!def))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (def->fns->kv_parse) {
|
||||||
|
// We do _not_ free the old value here, since linelist options
|
||||||
|
// sometimes have append semantics.
|
||||||
|
return def->fns->kv_parse(target, line, errmsg, def->params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return typed_var_assign_ex(target, line->value, errmsg, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release storage held by a variable in <b>target</b> of type defined by
|
||||||
|
* <b>def</b>, and set <b>target</b> to a reasonable default.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
typed_var_free_ex(void *target, const var_type_def_t *def)
|
||||||
|
{
|
||||||
|
if (BUG(!def))
|
||||||
|
return;
|
||||||
|
if (def->fns->clear) {
|
||||||
|
def->fns->clear(target, def->params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a value of type <b>def</b> pointed to by <b>value</b>, and return
|
||||||
|
* its result in a newly allocated string. The string may need to be escaped.
|
||||||
|
*
|
||||||
|
* Returns NULL if this option has a NULL value, or on internal error.
|
||||||
|
**/
|
||||||
|
char *
|
||||||
|
typed_var_encode_ex(const void *value, const var_type_def_t *def)
|
||||||
|
{
|
||||||
|
if (BUG(!def))
|
||||||
|
return NULL;
|
||||||
|
tor_assert(def->fns->encode);
|
||||||
|
return def->fns->encode(value, def->params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As typed_var_encode_ex(), but returns a newly allocated config_line_t
|
||||||
|
* object. The provided <b>key</b> is used as the key of the lines, unless
|
||||||
|
* the type is one (line a linelist) that encodes its own keys.
|
||||||
|
*
|
||||||
|
* This function may return a list of multiple lines.
|
||||||
|
*
|
||||||
|
* Returns NULL if there are no lines to encode, or on internal error.
|
||||||
|
*/
|
||||||
|
config_line_t *
|
||||||
|
typed_var_kvencode_ex(const char *key, const void *value,
|
||||||
|
const var_type_def_t *def)
|
||||||
|
{
|
||||||
|
if (BUG(!def))
|
||||||
|
return NULL;
|
||||||
|
if (def->fns->kv_encode) {
|
||||||
|
return def->fns->kv_encode(key, value, def->params);
|
||||||
|
}
|
||||||
|
char *encoded_value = typed_var_encode_ex(value, def);
|
||||||
|
if (!encoded_value)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
config_line_t *result = tor_malloc_zero(sizeof(config_line_t));
|
||||||
|
result->key = tor_strdup(key);
|
||||||
|
result->value = encoded_value;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set <b>dest</b> to contain the same value as <b>src</b>. Both types
|
||||||
|
* must be as defined by <b>def</b>.
|
||||||
|
*
|
||||||
|
* Return 0 on success, and -1 on failure.
|
||||||
|
**/
|
||||||
|
int
|
||||||
|
typed_var_copy_ex(void *dest, const void *src, const var_type_def_t *def)
|
||||||
|
{
|
||||||
|
if (BUG(!def))
|
||||||
|
return -1;
|
||||||
|
if (def->fns->copy) {
|
||||||
|
// If we have been provided a copy fuction, use it.
|
||||||
|
return def->fns->copy(dest, src, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, encode 'src' and parse the result into 'def'.
|
||||||
|
char *enc = typed_var_encode_ex(src, def);
|
||||||
|
if (!enc) {
|
||||||
|
typed_var_free_ex(dest, def);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
char *err = NULL;
|
||||||
|
int rv = typed_var_assign_ex(dest, enc, &err, def);
|
||||||
|
if (BUG(rv < 0)) {
|
||||||
|
log_warn(LD_BUG, "Encoded value %s was not parseable as a %s: %s",
|
||||||
|
escaped(enc), def->name, err?err:"");
|
||||||
|
}
|
||||||
|
tor_free(err);
|
||||||
|
tor_free(enc);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if <b>a</b> and <b>b</b> are semantically equivalent.
|
||||||
|
* Both types must be as defined by <b>def</b>.
|
||||||
|
**/
|
||||||
|
bool
|
||||||
|
typed_var_eq_ex(const void *a, const void *b, const var_type_def_t *def)
|
||||||
|
{
|
||||||
|
if (BUG(!def))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (def->fns->eq) {
|
||||||
|
// Use a provided eq function if we got one.
|
||||||
|
return def->fns->eq(a, b, def->params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, encode the values and compare them.
|
||||||
|
char *enc_a = typed_var_encode_ex(a, def);
|
||||||
|
char *enc_b = typed_var_encode_ex(b, def);
|
||||||
|
bool eq = !strcmp_opt(enc_a,enc_b);
|
||||||
|
tor_free(enc_a);
|
||||||
|
tor_free(enc_b);
|
||||||
|
return eq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether <b>value</b> encodes a valid value according to the
|
||||||
|
* type definition in <b>def</b>.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
typed_var_ok_ex(const void *value, const var_type_def_t *def)
|
||||||
|
{
|
||||||
|
if (BUG(!def))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (def->fns->ok)
|
||||||
|
return def->fns->ok(value, def->params);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =====
|
||||||
|
* 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,
|
||||||
|
* but for now they make migration easier.
|
||||||
|
* ===== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As typed_var_assign_ex(), but look up the definition of the configuration
|
||||||
|
* type from a provided config_type_t enum.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
typed_var_assign(void *target, const char *value, char **errmsg,
|
||||||
|
config_type_t type)
|
||||||
|
{
|
||||||
|
const var_type_def_t *def = lookup_type_def(type);
|
||||||
|
return typed_var_assign_ex(target, value, errmsg, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As typed_var_kvassign_ex(), but look up the definition of the configuration
|
||||||
|
* type from a provided config_type_t enum.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
typed_var_kvassign(void *target, const config_line_t *line, char **errmsg,
|
||||||
|
config_type_t type)
|
||||||
|
{
|
||||||
|
const var_type_def_t *def = lookup_type_def(type);
|
||||||
|
return typed_var_kvassign_ex(target, line, errmsg, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As typed_var_free_ex(), but look up the definition of the configuration
|
||||||
|
* type from a provided config_type_t enum.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
typed_var_free(void *target, config_type_t type)
|
||||||
|
{
|
||||||
|
const var_type_def_t *def = lookup_type_def(type);
|
||||||
|
return typed_var_free_ex(target, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As typed_var_encode_ex(), but look up the definition of the configuration
|
||||||
|
* type from a provided config_type_t enum.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
typed_var_encode(const void *value, config_type_t type)
|
||||||
|
{
|
||||||
|
const var_type_def_t *def = lookup_type_def(type);
|
||||||
|
return typed_var_encode_ex(value, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As typed_var_kvencode_ex(), but look up the definition of the configuration
|
||||||
|
* type from a provided config_type_t enum.
|
||||||
|
*/
|
||||||
|
config_line_t *
|
||||||
|
typed_var_kvencode(const char *key, const void *value, config_type_t type)
|
||||||
|
{
|
||||||
|
const var_type_def_t *def = lookup_type_def(type);
|
||||||
|
return typed_var_kvencode_ex(key, value, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As typed_var_copy_ex(), but look up the definition of the configuration type
|
||||||
|
* from a provided config_type_t enum.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
typed_var_copy(void *dest, const void *src, config_type_t type)
|
||||||
|
{
|
||||||
|
const var_type_def_t *def = lookup_type_def(type);
|
||||||
|
return typed_var_copy_ex(dest, src, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As typed_var_eq_ex(), but look up the definition of the configuration type
|
||||||
|
* from a provided config_type_t enum.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
typed_var_eq(const void *a, const void *b, config_type_t type)
|
||||||
|
{
|
||||||
|
const var_type_def_t *def = lookup_type_def(type);
|
||||||
|
return typed_var_eq_ex(a, b, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As typed_var_ok_ex(), but look up the definition of the configuration type
|
||||||
|
* from a provided config_type_t enum.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
typed_var_ok(const void *value, config_type_t type)
|
||||||
|
{
|
||||||
|
const var_type_def_t *def = lookup_type_def(type);
|
||||||
|
return typed_var_ok_ex(value, def);
|
||||||
|
}
|
49
src/lib/confmgt/typedvar.h
Normal file
49
src/lib/confmgt/typedvar.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/* 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 typedvar.h
|
||||||
|
* @brief Header for lib/confmgt/typedvar.c
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef TOR_LIB_CONFMGT_TYPEDVAR_H
|
||||||
|
#define TOR_LIB_CONFMGT_TYPEDVAR_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
enum config_type_t;
|
||||||
|
struct config_line_t;
|
||||||
|
|
||||||
|
typedef struct var_type_fns_t var_type_fns_t;
|
||||||
|
typedef struct var_type_def_t var_type_def_t;
|
||||||
|
|
||||||
|
int typed_var_assign(void *target, const char *value, char **errmsg,
|
||||||
|
enum config_type_t type);
|
||||||
|
void typed_var_free(void *target, enum config_type_t type);
|
||||||
|
char *typed_var_encode(const void *value, enum config_type_t type);
|
||||||
|
int typed_var_copy(void *dest, const void *src, enum config_type_t type);
|
||||||
|
bool typed_var_eq(const void *a, const void *b, enum config_type_t type);
|
||||||
|
bool typed_var_ok(const void *value, enum config_type_t type);
|
||||||
|
|
||||||
|
int typed_var_kvassign(void *target, const struct config_line_t *line,
|
||||||
|
char **errmsg, enum config_type_t type);
|
||||||
|
struct config_line_t *typed_var_kvencode(const char *key, const void *value,
|
||||||
|
enum config_type_t type);
|
||||||
|
|
||||||
|
int typed_var_assign_ex(void *target, const char *value, char **errmsg,
|
||||||
|
const var_type_def_t *def);
|
||||||
|
void typed_var_free_ex(void *target, const var_type_def_t *def);
|
||||||
|
char *typed_var_encode_ex(const void *value, const var_type_def_t *def);
|
||||||
|
int typed_var_copy_ex(void *dest, const void *src, const var_type_def_t *def);
|
||||||
|
bool typed_var_eq_ex(const void *a, const void *b, const var_type_def_t *def);
|
||||||
|
bool typed_var_ok_ex(const void *value, const var_type_def_t *def);
|
||||||
|
|
||||||
|
int typed_var_kvassign_ex(void *target, const struct config_line_t *line,
|
||||||
|
char **errmsg, const var_type_def_t *def);
|
||||||
|
struct config_line_t *typed_var_kvencode_ex(const char *key, const void *value,
|
||||||
|
const var_type_def_t *def);
|
||||||
|
|
||||||
|
#endif /* !defined(TOR_LIB_CONFMGT_TYPEDVAR_H) */
|
147
src/lib/confmgt/var_type_def_st.h
Normal file
147
src/lib/confmgt/var_type_def_st.h
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/* 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 typedvar.h
|
||||||
|
* @brief Structure declarations for typedvar type definitions.
|
||||||
|
*
|
||||||
|
* This structure is used for defining new variable types. If you are not
|
||||||
|
* defining a new variable type for use by the configuration management
|
||||||
|
* system, you don't need this structure.
|
||||||
|
*
|
||||||
|
* For defining new variables, see the types in conftypes.h.
|
||||||
|
*
|
||||||
|
* For data-driven access to configuration variables, see the other members of
|
||||||
|
* lib/confmgt/.
|
||||||
|
*
|
||||||
|
* STATUS NOTE: It is not yet possible to actually define new variables
|
||||||
|
* outside of config.c, and many of the types that will eventually be used
|
||||||
|
* to do so are not yet moved. This will change as more of #29211 is
|
||||||
|
* completed.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H
|
||||||
|
#define TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct config_line_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structure full of functions pointers to implement a variable type.
|
||||||
|
*
|
||||||
|
* Every type MUST implement parse or kv_parse and encode or kv_encode;
|
||||||
|
* the other functions pointers MAY be NULL.
|
||||||
|
*
|
||||||
|
* All functions here take a <b>params</b> argument, whose value
|
||||||
|
* is determined by the type definition. Two types may have the
|
||||||
|
* same functions, but differ only in parameters.
|
||||||
|
**/
|
||||||
|
struct var_type_fns_t {
|
||||||
|
/**
|
||||||
|
* Try to parse a string in <b>value</b> that encodes an object of this
|
||||||
|
* type. On success, adjust the lvalue pointed to by <b>target</b> to hold
|
||||||
|
* that value, and return 0. On failure, set *<b>errmsg</b> to a newly
|
||||||
|
* allocated string holding an error message, and return -1.
|
||||||
|
**/
|
||||||
|
int (*parse)(void *target, const char *value, char **errmsg,
|
||||||
|
const void *params);
|
||||||
|
/**
|
||||||
|
* Try to parse a single line from the head of<b>line</b> that encodes
|
||||||
|
* an object of this type. On success and failure, behave as in the parse()
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* If this function is absent, it is implemented in terms of parse().
|
||||||
|
*
|
||||||
|
* All types for which keys are significant should use this method. For
|
||||||
|
* example, a "linelist" type records the actual keys that are given
|
||||||
|
* for each line, and so should use this method.
|
||||||
|
*
|
||||||
|
* Note that although multiple lines may be provided in <b>line</b>,
|
||||||
|
* only the first one should be handled by this function.
|
||||||
|
**/
|
||||||
|
int (*kv_parse)(void *target, const struct config_line_t *line,
|
||||||
|
char **errmsg, const void *params);
|
||||||
|
/**
|
||||||
|
* Encode a value pointed to by <b>value</b> and return its result
|
||||||
|
* in a newly allocated string. The string may need to be escaped.
|
||||||
|
*
|
||||||
|
* If this function is absent, it is implemented in terms of kv_encode().
|
||||||
|
*
|
||||||
|
* Returns NULL if this option has a NULL value, or on internal error.
|
||||||
|
*
|
||||||
|
* Requirement: all strings generated by encode() should produce a
|
||||||
|
* semantically equivalent value when given to parse().
|
||||||
|
**/
|
||||||
|
char *(*encode)(const void *value, const void *params);
|
||||||
|
/**
|
||||||
|
* As encode(), but returns a newly allocated config_line_t object. The
|
||||||
|
* provided <b>key</b> is used as the key of the lines, unless the type is
|
||||||
|
* one that encodes its own keys.
|
||||||
|
*
|
||||||
|
* Unlike kv_parse(), this function will return a list of multiple lines,
|
||||||
|
* if <b>value</b> is such that it must be encoded by multiple lines.
|
||||||
|
*
|
||||||
|
* Returns NULL if there are no lines to encode, or on internal error.
|
||||||
|
*
|
||||||
|
* If this function is absent, it is implemented in terms of encode().
|
||||||
|
**/
|
||||||
|
struct config_line_t *(*kv_encode)(const char *key, const void *value,
|
||||||
|
const void *params);
|
||||||
|
/**
|
||||||
|
* Free all storage held in <b>arg</b>, and set <b>arg</b> to a default
|
||||||
|
* value -- usually zero or NULL.
|
||||||
|
*
|
||||||
|
* If this function is absent, the default implementation does nothing.
|
||||||
|
**/
|
||||||
|
void (*clear)(void *arg, const void *params);
|
||||||
|
/**
|
||||||
|
* Return true if <b>a</b> and <b>b</b> hold the same value, and false
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* If this function is absent, it is implemented by encoding both a and
|
||||||
|
* b and comparing their encoded strings for equality.
|
||||||
|
**/
|
||||||
|
bool (*eq)(const void *a, const void *b, const void *params);
|
||||||
|
/**
|
||||||
|
* Try to copy the value from <b>value</b> into <b>target</b>.
|
||||||
|
* On success return 0; on failure return -1.
|
||||||
|
*
|
||||||
|
* If this function is absent, it is implemented by encoding the value
|
||||||
|
* into a string, and then parsing it into the target.
|
||||||
|
**/
|
||||||
|
int (*copy)(void *target, const void *value, const void *params);
|
||||||
|
/**
|
||||||
|
* Check whether <b>value</b> holds a valid value according to the
|
||||||
|
* rules of this type; return true if it does and false if it doesn't.
|
||||||
|
*
|
||||||
|
* The default implementation for this function assumes that all
|
||||||
|
* values are valid.
|
||||||
|
**/
|
||||||
|
bool (*ok)(const void *value, const void *params);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structure describing a type that can be manipulated with the typedvar_*
|
||||||
|
* functions.
|
||||||
|
**/
|
||||||
|
struct var_type_def_t {
|
||||||
|
/**
|
||||||
|
* The name of this type. Should not include spaces. Used for
|
||||||
|
* debugging, log messages, and the controller API. */
|
||||||
|
const char *name;
|
||||||
|
/**
|
||||||
|
* A function table for this type.
|
||||||
|
*/
|
||||||
|
const struct var_type_fns_t *fns;
|
||||||
|
/**
|
||||||
|
* A pointer to a value that should be passed as the 'params' argument when
|
||||||
|
* calling the functions in this type's function table.
|
||||||
|
*/
|
||||||
|
const void *params;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* !defined(TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H) */
|
@ -469,9 +469,9 @@ static const badval_test_t bv_badcsvi2 =
|
|||||||
{ "csv_interval cl,10\n", "malformed" };
|
{ "csv_interval cl,10\n", "malformed" };
|
||||||
static const badval_test_t bv_nonoption = { "fnord 10\n", "Unknown option" };
|
static const badval_test_t bv_nonoption = { "fnord 10\n", "Unknown option" };
|
||||||
static const badval_test_t bv_badmem = { "mem 3 trits\n", "malformed" };
|
static const badval_test_t bv_badmem = { "mem 3 trits\n", "malformed" };
|
||||||
static const badval_test_t bv_badbool = { "boolean 7\n", "expects 0 or 1" };
|
static const badval_test_t bv_badbool = { "boolean 7\n", "Unrecognized value"};
|
||||||
static const badval_test_t bv_badabool =
|
static const badval_test_t bv_badabool =
|
||||||
{ "autobool 7\n", "expects 0, 1, or 'auto'" };
|
{ "autobool 7\n", "Unrecognized value" };
|
||||||
static const badval_test_t bv_badtime = { "time lunchtime\n", "Invalid time" };
|
static const badval_test_t bv_badtime = { "time lunchtime\n", "Invalid time" };
|
||||||
static const badval_test_t bv_virt = { "MixedLines 7\n", "virtual option" };
|
static const badval_test_t bv_virt = { "MixedLines 7\n", "virtual option" };
|
||||||
static const badval_test_t bv_rs = { "Routerset 2.2.2.2.2\n", "Invalid" };
|
static const badval_test_t bv_rs = { "Routerset 2.2.2.2.2\n", "Invalid" };
|
||||||
|
@ -258,13 +258,17 @@ test_options_validate(void *arg)
|
|||||||
WANT_ERR("BridgeRelay 1\nDirCache 0",
|
WANT_ERR("BridgeRelay 1\nDirCache 0",
|
||||||
"We're a bridge but DirCache is disabled.", PH_VALIDATE);
|
"We're a bridge but DirCache is disabled.", PH_VALIDATE);
|
||||||
|
|
||||||
|
// XXXX We should replace this with a more full error message once #29211
|
||||||
|
// XXXX is done. It is truncated for now because at the current stage
|
||||||
|
// XXXX of refactoring, we can't give a full error message like before.
|
||||||
WANT_ERR_LOG("HeartbeatPeriod 21 snarks",
|
WANT_ERR_LOG("HeartbeatPeriod 21 snarks",
|
||||||
"Interval 'HeartbeatPeriod 21 snarks' is malformed or"
|
"malformed or out of bounds", LOG_WARN,
|
||||||
" out of bounds.", LOG_WARN, "Unknown unit 'snarks'.",
|
"Unknown unit 'snarks'.",
|
||||||
PH_ASSIGN);
|
PH_ASSIGN);
|
||||||
|
// XXXX As above.
|
||||||
WANT_ERR_LOG("LogTimeGranularity 21 snarks",
|
WANT_ERR_LOG("LogTimeGranularity 21 snarks",
|
||||||
"Msec interval 'LogTimeGranularity 21 snarks' is malformed or"
|
"malformed or out of bounds", LOG_WARN,
|
||||||
" out of bounds.", LOG_WARN, "Unknown unit 'snarks'.",
|
"Unknown unit 'snarks'.",
|
||||||
PH_ASSIGN);
|
PH_ASSIGN);
|
||||||
OK("HeartbeatPeriod 1 hour", PH_VALIDATE);
|
OK("HeartbeatPeriod 1 hour", PH_VALIDATE);
|
||||||
OK("LogTimeGranularity 100 milliseconds", PH_VALIDATE);
|
OK("LogTimeGranularity 100 milliseconds", PH_VALIDATE);
|
||||||
|
Loading…
Reference in New Issue
Block a user