diff options
author | Nick Mathewson <nickm@torproject.org> | 2019-06-14 08:42:24 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2019-06-24 17:50:43 -0400 |
commit | c60a85d22ab87fef5a7de2fee616ad112835177b (patch) | |
tree | 246892533312b309894449df9760df7cd33a9a3e /src/lib/confmgt | |
parent | 5a2ab886baaa125fe715acca8f7daf35031855aa (diff) | |
download | tor-c60a85d22ab87fef5a7de2fee616ad112835177b.tar.gz tor-c60a85d22ab87fef5a7de2fee616ad112835177b.zip |
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.
Diffstat (limited to 'src/lib/confmgt')
-rw-r--r-- | src/lib/confmgt/.may_include | 2 | ||||
-rw-r--r-- | src/lib/confmgt/include.am | 7 | ||||
-rw-r--r-- | src/lib/confmgt/type_defs.c | 727 | ||||
-rw-r--r-- | src/lib/confmgt/type_defs.h | 17 | ||||
-rw-r--r-- | src/lib/confmgt/typedvar.c | 305 | ||||
-rw-r--r-- | src/lib/confmgt/typedvar.h | 49 | ||||
-rw-r--r-- | src/lib/confmgt/var_type_def_st.h | 147 |
7 files changed, 1253 insertions, 1 deletions
diff --git a/src/lib/confmgt/.may_include b/src/lib/confmgt/.may_include index 640acf3770..d85dbf6904 100644 --- a/src/lib/confmgt/.may_include +++ b/src/lib/confmgt/.may_include @@ -2,6 +2,8 @@ orconfig.h lib/cc/*.h lib/conf/*.h lib/confmgt/*.h +lib/container/*.h +lib/encoding/*.h lib/log/*.h lib/malloc/*.h lib/string/*.h diff --git a/src/lib/confmgt/include.am b/src/lib/confmgt/include.am index 729e6147ff..a2c7649957 100644 --- a/src/lib/confmgt/include.am +++ b/src/lib/confmgt/include.am @@ -6,6 +6,8 @@ endif # ADD_C_FILE: INSERT SOURCES HERE. src_lib_libtor_confmgt_a_SOURCES = \ + src/lib/confmgt/type_defs.c \ + src/lib/confmgt/typedvar.c \ src/lib/confmgt/unitparse.c 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. 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 diff --git a/src/lib/confmgt/type_defs.c b/src/lib/confmgt/type_defs.c new file mode 100644 index 0000000000..62b4c1019d --- /dev/null +++ b/src/lib/confmgt/type_defs.c @@ -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]; +} diff --git a/src/lib/confmgt/type_defs.h b/src/lib/confmgt/type_defs.h new file mode 100644 index 0000000000..ecf040529e --- /dev/null +++ b/src/lib/confmgt/type_defs.h @@ -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) */ diff --git a/src/lib/confmgt/typedvar.c b/src/lib/confmgt/typedvar.c new file mode 100644 index 0000000000..fc45c44481 --- /dev/null +++ b/src/lib/confmgt/typedvar.c @@ -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); +} diff --git a/src/lib/confmgt/typedvar.h b/src/lib/confmgt/typedvar.h new file mode 100644 index 0000000000..720ad54fc6 --- /dev/null +++ b/src/lib/confmgt/typedvar.h @@ -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) */ diff --git a/src/lib/confmgt/var_type_def_st.h b/src/lib/confmgt/var_type_def_st.h new file mode 100644 index 0000000000..c63ff66eff --- /dev/null +++ b/src/lib/confmgt/var_type_def_st.h @@ -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) */ |