diff --git a/doc/TODO b/doc/TODO
index 6d3499c823..c75238db88 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -87,14 +87,19 @@ N . Controller improvements
o Implement main controller interface
o Glue code
o Testing
-N - Make configuration parsing code switchable to different sets of
+N . Make configuration parsing code switchable to different sets of
variables so we can use it for persistence.
+ o Implement
+ o Add simple type-checking
+ - Rename functions to distinguish configuration-only functions from
+ cross-format functions
N . helper nodes (Choose N nodes randomly; if a node dies (goes down for a
long time), replace it. Store nodes on disk.
o Implement (basic case)
- Implement (persistence)
- Document
- Test, debug
+ - On sighup, if usehelpernodes changed to 1, use new circs.
N - Make a FirewallIPs to correspond to firewallPorts so I can use Tor at
MIT when my directory is out of date.
- switch accountingmax to count total in+out, not either in or
diff --git a/src/or/config.c b/src/or/config.c
index f7eb57495b..e3b3b6cf48 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -47,7 +47,7 @@ typedef struct config_abbrev_t {
#define PLURAL(tok) { #tok, #tok "s", 0 }
/* A list of command-line abbreviations. */
-static config_abbrev_t config_abbrevs[] = {
+static config_abbrev_t _config_abbrevs[] = {
PLURAL(ExitNode),
PLURAL(EntryNode),
PLURAL(ExcludeNode),
@@ -92,7 +92,7 @@ typedef struct config_var_t {
* abbreviations, order is significant, since the first matching option will
* be chosen first.
*/
-static config_var_t config_vars[] = {
+static config_var_t _config_vars[] = {
VAR("Address", STRING, Address, NULL),
VAR("AccountingStart", STRING, AccountingStart, NULL),
VAR("AllowUnverifiedNodes",CSV, AllowUnverifiedNodes, "middle,rendezvous"),
@@ -187,15 +187,33 @@ static config_var_t config_vars[] = {
#undef VAR
#undef OBSOLETE
+typedef int (*validate_fn_t)(void*);
+
+typedef struct {
+ size_t size;
+ uint32_t magic;
+ off_t magic_offset;
+ config_abbrev_t *abbrevs;
+ config_var_t *vars;
+ validate_fn_t validate_fn;
+} config_format_t;
+
+#define CHECK(fmt, cfg) do { \
+ tor_assert(fmt && cfg); \
+ tor_assert((fmt)->magic == *(uint32_t*)(((char*)(cfg))+fmt->magic_offset)); \
+ } while (0)
+
/** Largest allowed config line */
#define CONFIG_LINE_T_MAXLEN 4096
static void config_line_append(struct config_line_t **lst,
const char *key, const char *val);
-static void option_reset(or_options_t *options, config_var_t *var);
-static void options_free(or_options_t *options);
-static int option_is_same(or_options_t *o1, or_options_t *o2,const char *name);
-static or_options_t *options_dup(or_options_t *old);
+static void option_reset(config_format_t *fmt, or_options_t *options,
+ config_var_t *var);
+static void options_free(config_format_t *fmt, or_options_t *options);
+static int option_is_same(config_format_t *fmt,
+ or_options_t *o1, or_options_t *o2,const char *name);
+static or_options_t *options_dup(config_format_t *fmt, or_options_t *old);
static int options_validate(or_options_t *options);
static int options_transition_allowed(or_options_t *old, or_options_t *new);
static int check_nickname_list(const char *lst, const char *name);
@@ -215,6 +233,9 @@ static int add_single_log_option(or_options_t *options, int minSeverity,
static int normalize_log_options(or_options_t *options);
static int validate_data_directory(or_options_t *options);
static int write_configuration_file(const char *fname, or_options_t *options);
+static struct config_line_t *get_assigned_option(config_format_t *fmt,
+ or_options_t *options, const char *key);
+static void config_init(config_format_t *fmt, or_options_t *options);
static uint64_t config_parse_memunit(const char *s, int *ok);
static int config_parse_interval(const char *s, int *ok);
@@ -224,6 +245,17 @@ static int init_libevent(void);
static void check_libevent_version(const char *m, const char *v, int server);
#endif
+#define OR_OPTIONS_MAGIC 9090909
+
+static config_format_t config_format = {
+ sizeof(or_options_t),
+ OR_OPTIONS_MAGIC,
+ STRUCT_OFFSET(or_options_t, _magic),
+ _config_abbrevs,
+ _config_vars,
+ (validate_fn_t)options_validate
+};
+
/*
* Functions to read and write the global options pointer.
*/
@@ -233,6 +265,15 @@ static or_options_t *global_options=NULL;
/** Name of most recently read torrc file. */
static char *config_fname = NULL;
+static void *
+config_alloc(config_format_t *fmt)
+{
+ void *opts = opts = tor_malloc_zero(fmt->size);
+ *(uint32_t*)(((char*)opts)+fmt->magic_offset) = fmt->magic;
+ CHECK(fmt, opts);
+ return opts;
+}
+
/** Return the currently configured options. */
or_options_t *
get_options(void)
@@ -248,14 +289,14 @@ void
set_options(or_options_t *new_val)
{
if (global_options)
- options_free(global_options);
+ options_free(&config_format, global_options);
global_options = new_val;
}
void
config_free_all(void)
{
- options_free(global_options);
+ options_free(&config_format, global_options);
tor_free(config_fname);
}
@@ -417,14 +458,16 @@ options_act(void)
* If command_line is set, apply all abbreviations. Otherwise, only
* apply abbreviations that work for the config file and the command line. */
static const char *
-expand_abbrev(const char *option, int command_line)
+expand_abbrev(config_format_t *fmt, const char *option, int command_line)
{
int i;
- for (i=0; config_abbrevs[i].abbreviated; ++i) {
+ if (! fmt->abbrevs)
+ return option;
+ for (i=0; fmt->abbrevs[i].abbreviated; ++i) {
/* Abbreviations aren't casei. */
- if (!strcasecmp(option,config_abbrevs[i].abbreviated) &&
- (command_line || !config_abbrevs[i].commandline_only)) {
- return config_abbrevs[i].full;
+ if (!strcasecmp(option,fmt->abbrevs[i].abbreviated) &&
+ (command_line || !fmt->abbrevs[i].commandline_only)) {
+ return fmt->abbrevs[i].full;
}
}
return option;
@@ -458,7 +501,7 @@ config_get_commandlines(int argc, char **argv)
while (*s == '-')
s++;
- (*new)->key = tor_strdup(expand_abbrev(s, 1));
+ (*new)->key = tor_strdup(expand_abbrev(&config_format, s, 1));
(*new)->value = tor_strdup(argv[i+1]);
(*new)->next = NULL;
log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'",
@@ -545,24 +588,24 @@ config_free_lines(struct config_line_t *front)
* warn, and return the corresponding config_var_t. Otherwise return NULL.
*/
static config_var_t *
-config_find_option(const char *key)
+config_find_option(config_format_t *fmt, const char *key)
{
int i;
size_t keylen = strlen(key);
if (!keylen)
return NULL; /* if they say "--" on the commandline, it's not an option */
/* First, check for an exact (case-insensitive) match */
- for (i=0; config_vars[i].name; ++i) {
- if (!strcasecmp(key, config_vars[i].name))
- return &config_vars[i];
+ for (i=0; fmt->vars[i].name; ++i) {
+ if (!strcasecmp(key, fmt->vars[i].name))
+ return &fmt->vars[i];
}
/* If none, check for an abbreviated match */
- for (i=0; config_vars[i].name; ++i) {
- if (!strncasecmp(key, config_vars[i].name, keylen)) {
+ for (i=0; fmt->vars[i].name; ++i) {
+ if (!strncasecmp(key, fmt->vars[i].name, keylen)) {
log_fn(LOG_WARN, "The abbreviation '%s' is deprecated. "
"Tell Nick and Roger to make it official, or just use '%s' instead",
- key, config_vars[i].name);
- return &config_vars[i];
+ key, fmt->vars[i].name);
+ return &fmt->vars[i];
}
}
/* Okay, unrecognized options */
@@ -577,13 +620,16 @@ config_find_option(const char *key)
* option to its default value.
*/
static int
-config_assign_line(or_options_t *options, struct config_line_t *c, int reset)
+config_assign_line(config_format_t *fmt,
+ or_options_t *options, struct config_line_t *c, int reset)
{
int i, ok;
config_var_t *var;
void *lvalue;
- var = config_find_option(c->key);
+ CHECK(fmt, options);
+
+ var = config_find_option(fmt, c->key);
if (!var) {
log_fn(LOG_WARN, "Unknown option '%s'. Failing.", c->key);
return -1;
@@ -595,7 +641,7 @@ config_assign_line(or_options_t *options, struct config_line_t *c, int reset)
}
if (reset && !strlen(c->value)) {
- option_reset(options, var);
+ option_reset(fmt, options, var);
return 0;
}
@@ -680,22 +726,24 @@ config_assign_line(or_options_t *options, struct config_line_t *c, int reset)
/** restore the option named key in options to its default value. */
static void
-config_reset_line(or_options_t *options, const char *key)
+config_reset_line(config_format_t *fmt, or_options_t *options, const char *key)
{
config_var_t *var;
- var = config_find_option(key);
+ CHECK(fmt, options);
+
+ var = config_find_option(fmt, key);
if (!var)
return; /* give error on next pass. */
- option_reset(options, var);
+ option_reset(fmt, options, var);
}
/** Return true iff key is a valid configuration option. */
int
config_option_is_recognized(const char *key)
{
- config_var_t *var = config_find_option(key);
+ config_var_t *var = config_find_option(&config_format, key);
return (var != NULL);
}
@@ -703,14 +751,21 @@ config_option_is_recognized(const char *key)
const char *
config_option_get_canonical_name(const char *key)
{
- config_var_t *var = config_find_option(key);
+ config_var_t *var = config_find_option(&config_format, key);
return var->name;
}
+
/** Return a canonicalized list of the options assigned for key.
*/
struct config_line_t *
config_get_assigned_option(or_options_t *options, const char *key)
+{
+ return get_assigned_option(&config_format, options, key);
+}
+
+static struct config_line_t *
+get_assigned_option(config_format_t *fmt, or_options_t *options, const char *key)
{
config_var_t *var;
const void *value;
@@ -718,7 +773,9 @@ config_get_assigned_option(or_options_t *options, const char *key)
struct config_line_t *result;
tor_assert(options && key);
- var = config_find_option(key);
+ CHECK(fmt, options);
+
+ var = config_find_option(fmt, key);
if (!var) {
log_fn(LOG_WARN, "Unknown option '%s'. Failing.", key);
return NULL;
@@ -808,14 +865,16 @@ config_get_assigned_option(or_options_t *options, const char *key)
* -2 on bad value.
*/
static int
-config_assign(or_options_t *options, struct config_line_t *list, int reset)
+config_assign(config_format_t *fmt,
+ or_options_t *options, struct config_line_t *list, int reset)
{
struct config_line_t *p;
- tor_assert(options);
+
+ CHECK(fmt, options);
/* pass 1: normalize keys */
for (p = list; p; p = p->next) {
- const char *full = expand_abbrev(p->key, 0);
+ const char *full = expand_abbrev(fmt, p->key, 0);
if (strcmp(full,p->key)) {
tor_free(p->key);
p->key = tor_strdup(full);
@@ -826,13 +885,13 @@ config_assign(or_options_t *options, struct config_line_t *list, int reset)
* linelists. */
if (reset) {
for (p = list; p; p = p->next)
- config_reset_line(options, p->key);
+ config_reset_line(fmt, options, p->key);
}
/* pass 3: assign. */
while (list) {
int r;
- if ((r=config_assign_line(options, list, reset)))
+ if ((r=config_assign_line(fmt, options, list, reset)))
return r;
list = list->next;
}
@@ -849,20 +908,20 @@ int
config_trial_assign(struct config_line_t *list, int reset)
{
int r;
- or_options_t *trial_options = options_dup(get_options());
+ or_options_t *trial_options = options_dup(&config_format, get_options());
- if ((r=config_assign(trial_options, list, reset)) < 0) {
- options_free(trial_options);
+ if ((r=config_assign(&config_format, trial_options, list, reset)) < 0) {
+ options_free(&config_format, trial_options);
return r;
}
if (options_validate(trial_options) < 0) {
- options_free(trial_options);
+ options_free(&config_format, trial_options);
return -2;
}
if (options_transition_allowed(get_options(), trial_options) < 0) {
- options_free(trial_options);
+ options_free(&config_format, trial_options);
return -3;
}
@@ -873,11 +932,13 @@ config_trial_assign(struct config_line_t *list, int reset)
/** Replace the option indexed by var in options with its
* default value. */
static void
-option_reset(or_options_t *options, config_var_t *var)
+option_reset(config_format_t *fmt, or_options_t *options, config_var_t *var)
{
struct config_line_t *c;
void *lvalue;
+ CHECK(fmt, options);
+
lvalue = ((char*)options) + var->var_offset;
switch (var->type) {
case CONFIG_TYPE_STRING:
@@ -916,7 +977,7 @@ option_reset(or_options_t *options, config_var_t *var)
c = tor_malloc_zero(sizeof(struct config_line_t));
c->key = tor_strdup(var->name);
c->value = tor_strdup(var->initvalue);
- config_assign_line(options,c,0);
+ config_assign_line(fmt, options,c,0);
config_free_lines(c);
}
}
@@ -1071,16 +1132,16 @@ get_default_nickname(void)
/** Release storage held by options */
static void
-options_free(or_options_t *options)
+options_free(config_format_t *fmt,or_options_t *options)
{
int i;
void *lvalue;
tor_assert(options);
- for (i=0; config_vars[i].name; ++i) {
- lvalue = ((char*)options) + config_vars[i].var_offset;
- switch (config_vars[i].type) {
+ for (i=0; fmt->vars[i].name; ++i) {
+ lvalue = ((char*)options) + fmt->vars[i].var_offset;
+ switch (fmt->vars[i].type) {
case CONFIG_TYPE_MEMUNIT:
case CONFIG_TYPE_INTERVAL:
case CONFIG_TYPE_UINT:
@@ -1115,12 +1176,16 @@ options_free(or_options_t *options)
* and o2. Must not be called for LINELIST_S or OBSOLETE options.
*/
static int
-option_is_same(or_options_t *o1, or_options_t *o2, const char *name)
+option_is_same(config_format_t *fmt,
+ or_options_t *o1, or_options_t *o2, const char *name)
{
struct config_line_t *c1, *c2;
int r = 1;
- c1 = config_get_assigned_option(o1, name);
- c2 = config_get_assigned_option(o2, name);
+ CHECK(fmt, o1);
+ CHECK(fmt, o2);
+
+ c1 = get_assigned_option(fmt, o1, name);
+ c2 = get_assigned_option(fmt, o2, name);
while (c1 && c2) {
if (strcasecmp(c1->key, c2->key) ||
strcmp(c1->value, c2->value)) {
@@ -1140,21 +1205,21 @@ option_is_same(or_options_t *o1, or_options_t *o2, const char *name)
/** Copy storage held by old into a new or_options_t and return it. */
static or_options_t *
-options_dup(or_options_t *old)
+options_dup(config_format_t *fmt, or_options_t *old)
{
or_options_t *newopts;
int i;
struct config_line_t *line;
- newopts = tor_malloc_zero(sizeof(or_options_t));
- for (i=0; config_vars[i].name; ++i) {
- if (config_vars[i].type == CONFIG_TYPE_LINELIST_S)
+ newopts = config_alloc(fmt);
+ for (i=0; fmt->vars[i].name; ++i) {
+ if (fmt->vars[i].type == CONFIG_TYPE_LINELIST_S)
continue;
- if (config_vars[i].type == CONFIG_TYPE_OBSOLETE)
+ if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE)
continue;
- line = config_get_assigned_option(old, config_vars[i].name);
+ line = get_assigned_option(fmt, old, fmt->vars[i].name);
if (line) {
- if (config_assign(newopts, line, 0) < 0) {
+ if (config_assign(fmt, newopts, line, 0) < 0) {
log_fn(LOG_WARN,"Bug: config_get_assigned_option() generated "
"something we couldn't config_assign().");
tor_assert(0);
@@ -1169,24 +1234,27 @@ options_dup(or_options_t *old)
* Each option defaults to zero. */
void
options_init(or_options_t *options)
+{
+ config_init(&config_format, options);
+}
+
+static void
+config_init(config_format_t *fmt, or_options_t *options)
{
int i;
config_var_t *var;
+ CHECK(fmt, options);
- for (i=0; config_vars[i].name; ++i) {
- var = &config_vars[i];
+ for (i=0; fmt->vars[i].name; ++i) {
+ var = &fmt->vars[i];
if (!var->initvalue)
continue; /* defaults to NULL or 0 */
- option_reset(options, var);
+ option_reset(fmt, options, var);
}
}
-/** Return a string containing a possible configuration file that would give
- * the configuration in options. If minimal is true, do not
- * include options that are the same as Tor's defaults.
- */
-char *
-config_dump_options(or_options_t *options, int minimal)
+static char *
+config_dump(config_format_t *fmt, or_options_t *options, int minimal)
{
smartlist_t *elements;
or_options_t *defaults;
@@ -1194,21 +1262,21 @@ config_dump_options(or_options_t *options, int minimal)
char *result;
int i;
- defaults = tor_malloc_zero(sizeof(or_options_t));
- options_init(defaults);
- options_validate(defaults); /* ??? will this work? */
+ defaults = config_alloc(fmt);
+ config_init(fmt, defaults);
+ fmt->validate_fn(options);
elements = smartlist_create();
- for (i=0; config_vars[i].name; ++i) {
- if (config_vars[i].type == CONFIG_TYPE_OBSOLETE ||
- config_vars[i].type == CONFIG_TYPE_LINELIST_S)
+ for (i=0; fmt->vars[i].name; ++i) {
+ if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE ||
+ fmt->vars[i].type == CONFIG_TYPE_LINELIST_S)
continue;
/* Don't save 'hidden' control variables. */
- if (!strcmpstart(config_vars[i].name, "__"))
+ if (!strcmpstart(fmt->vars[i].name, "__"))
continue;
- if (minimal && option_is_same(options, defaults, config_vars[i].name))
+ if (minimal && option_is_same(fmt, options, defaults, fmt->vars[i].name))
continue;
- line = config_get_assigned_option(options, config_vars[i].name);
+ line = get_assigned_option(fmt, options, fmt->vars[i].name);
for (; line; line = line->next) {
size_t len = strlen(line->key) + strlen(line->value) + 3;
char *tmp;
@@ -1228,6 +1296,16 @@ config_dump_options(or_options_t *options, int minimal)
return result;
}
+/** Return a string containing a possible configuration file that would give
+ * the configuration in options. If minimal is true, do not
+ * include options that are the same as Tor's defaults.
+ */
+char *
+config_dump_options(or_options_t *options, int minimal)
+{
+ return config_dump(&config_format, options, minimal);
+}
+
static int
validate_ports_csv(smartlist_t *sl, const char *name)
{
@@ -1809,6 +1887,7 @@ init_from_config(int argc, char **argv)
}
newoptions = tor_malloc_zero(sizeof(or_options_t));
+ newoptions->_magic = OR_OPTIONS_MAGIC;
options_init(newoptions);
/* learn config file name, get config lines, assign them */
@@ -1875,7 +1954,7 @@ init_from_config(int argc, char **argv)
tor_free(cf);
if (retval < 0)
goto err;
- retval = config_assign(newoptions, cl, 0);
+ retval = config_assign(&config_format, newoptions, cl, 0);
config_free_lines(cl);
if (retval < 0)
goto err;
@@ -1883,7 +1962,7 @@ init_from_config(int argc, char **argv)
/* Go through command-line variables too */
cl = config_get_commandlines(argc,argv);
- retval = config_assign(newoptions,cl,0);
+ retval = config_assign(&config_format, newoptions,cl,0);
config_free_lines(cl);
if (retval < 0)
goto err;
@@ -1905,7 +1984,7 @@ init_from_config(int argc, char **argv)
return 0;
err:
tor_free(fname);
- options_free(newoptions);
+ options_free(&config_format, newoptions);
return -1;
}
diff --git a/src/or/or.h b/src/or/or.h
index 3ccfa8f1ad..a579a5967b 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1017,6 +1017,8 @@ typedef struct exit_redirect_t {
/** Configuration options for a Tor process */
typedef struct {
+ uint32_t _magic;
+
/** What should the tor process actually do? */
enum {
CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT, CMD_HASH_PASSWORD,