Merge branch 'multilevel_cfg'

This commit is contained in:
Nick Mathewson 2011-11-29 17:38:19 -05:00
commit 9e25422eed
6 changed files with 272 additions and 99 deletions

26
changes/config Normal file
View File

@ -0,0 +1,26 @@
o Minor features
- Slightly change behavior of "list" options (that is, options that
can appear more than once) when they appear both in torrc and on
the command line. Previously, the command-line options would be
appended to the ones from torrc. Now, the command-line options
override the torrc options entirely. This new behavior allows
the user to override list options (like exit policies and
ports to listen on) from the command line, rather than simply
appending to the list.
- You can get the old (appending) command-line behavior for "list"
"list" options, by prefixing the option name with a "+".
- You can remove all the values for a "list" option from the command
line without adding any new ones by prefixing the option name
with a "/".
- Add *experimental* support for a "defaults" torrc file to be parsed
before the regular torrc. Torrc options override the defaults file's
options in the same way that the command line overrides the torrc.
The SAVECONF controller command saves only those options which differ
between the current configuration and the defaults file. HUP reloads
both files. (Note: This is an experimental feature; its behavior will
probably be refined in future 0.2.3.x-alpha versions to better meet
packagers' needs.)
o Minor bugfixes:
- Restore behavior of overriding SocksPort, ORPort, and similar
options from the command line. Bugfix on 0.2.3.3-alpha.

View File

@ -661,8 +661,12 @@ static const config_format_t state_format = {
/** Command-line and config-file options. */ /** Command-line and config-file options. */
static or_options_t *global_options = NULL; static or_options_t *global_options = NULL;
/** DOCDOC */
static or_options_t *global_default_options = NULL;
/** Name of most recently read torrc file. */ /** Name of most recently read torrc file. */
static char *torrc_fname = NULL; static char *torrc_fname = NULL;
/** DOCDOC */
static char *torrc_defaults_fname;
/** Persistent serialized state. */ /** Persistent serialized state. */
static or_state_t *global_state = NULL; static or_state_t *global_state = NULL;
/** Configuration Options set by command line. */ /** Configuration Options set by command line. */
@ -806,6 +810,8 @@ config_free_all(void)
{ {
or_options_free(global_options); or_options_free(global_options);
global_options = NULL; global_options = NULL;
or_options_free(global_default_options);
global_default_options = NULL;
config_free(&state_format, global_state); config_free(&state_format, global_state);
global_state = NULL; global_state = NULL;
@ -821,6 +827,7 @@ config_free_all(void)
} }
tor_free(torrc_fname); tor_free(torrc_fname);
tor_free(torrc_defaults_fname);
tor_free(_version); tor_free(_version);
tor_free(global_dirfrontpagecontents); tor_free(global_dirfrontpagecontents);
} }
@ -1728,7 +1735,11 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
int i = 1; int i = 1;
while (i < argc) { while (i < argc) {
unsigned command = CONFIG_LINE_NORMAL;
int want_arg = 1;
if (!strcmp(argv[i],"-f") || if (!strcmp(argv[i],"-f") ||
!strcmp(argv[i],"--defaults-torrc") ||
!strcmp(argv[i],"--hash-password")) { !strcmp(argv[i],"--hash-password")) {
i += 2; /* command-line option with argument. ignore them. */ i += 2; /* command-line option with argument. ignore them. */
continue; continue;
@ -1745,13 +1756,6 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
continue; continue;
} }
if (i == argc-1) {
log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.",
argv[i]);
config_free_lines(front);
return -1;
}
*new = tor_malloc_zero(sizeof(config_line_t)); *new = tor_malloc_zero(sizeof(config_line_t));
s = argv[i]; s = argv[i];
@ -1760,15 +1764,33 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
s++; s++;
if (*s == '-') if (*s == '-')
s++; s++;
/* Figure out the command, if any. */
if (*s == '+') {
s++;
command = CONFIG_LINE_APPEND;
} else if (*s == '/') {
s++;
command = CONFIG_LINE_CLEAR;
/* A 'clear' command has no argument. */
want_arg = 0;
}
if (want_arg && i == argc-1) {
log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.",
argv[i]);
config_free_lines(front);
return -1;
}
(*new)->key = tor_strdup(expand_abbrev(&options_format, s, 1, 1)); (*new)->key = tor_strdup(expand_abbrev(&options_format, s, 1, 1));
(*new)->value = tor_strdup(argv[i+1]); (*new)->value = want_arg ? tor_strdup(argv[i+1]) : tor_strdup("");
(*new)->command = command;
(*new)->next = NULL; (*new)->next = NULL;
log(LOG_DEBUG, LD_CONFIG, "command line: parsed keyword '%s', value '%s'", log(LOG_DEBUG, LD_CONFIG, "command line: parsed keyword '%s', value '%s'",
(*new)->key, (*new)->value); (*new)->key, (*new)->value);
new = &((*new)->next); new = &((*new)->next);
i += 2; i += want_arg ? 2 : 1;
} }
*result = front; *result = front;
return 0; return 0;
@ -1783,7 +1805,7 @@ config_line_append(config_line_t **lst,
{ {
config_line_t *newline; config_line_t *newline;
newline = tor_malloc(sizeof(config_line_t)); newline = tor_malloc_zero(sizeof(config_line_t));
newline->key = tor_strdup(key); newline->key = tor_strdup(key);
newline->value = tor_strdup(val); newline->value = tor_strdup(val);
newline->next = NULL; newline->next = NULL;
@ -1796,9 +1818,12 @@ config_line_append(config_line_t **lst,
/** Helper: parse the config string and strdup into key/value /** Helper: parse the config string and strdup into key/value
* strings. Set *result to the list, or NULL if parsing the string * strings. Set *result to the list, or NULL if parsing the string
* failed. Return 0 on success, -1 on failure. Warn and ignore any * failed. Return 0 on success, -1 on failure. Warn and ignore any
* misformatted lines. */ * misformatted lines.
*
* If <b>extended</b> is set, then treat keys beginning with / and with + as
* indicating "clear" and "append" respectively. */
int int
config_get_lines(const char *string, config_line_t **result) config_get_lines(const char *string, config_line_t **result, int extended)
{ {
config_line_t *list = NULL, **next; config_line_t *list = NULL, **next;
char *k, *v; char *k, *v;
@ -1814,13 +1839,30 @@ config_get_lines(const char *string, config_line_t **result)
return -1; return -1;
} }
if (k && v) { if (k && v) {
unsigned command = CONFIG_LINE_NORMAL;
if (extended) {
if (k[0] == '+') {
char *k_new = tor_strdup(k+1);
tor_free(k);
k = k_new;
command = CONFIG_LINE_APPEND;
} else if (k[0] == '/') {
char *k_new = tor_strdup(k+1);
tor_free(k);
k = k_new;
tor_free(v);
v = tor_strdup("");
command = CONFIG_LINE_CLEAR;
}
}
/* This list can get long, so we keep a pointer to the end of it /* This list can get long, so we keep a pointer to the end of it
* rather than using config_line_append over and over and getting * rather than using config_line_append over and over and getting
* n^2 performance. */ * n^2 performance. */
*next = tor_malloc(sizeof(config_line_t)); *next = tor_malloc_zero(sizeof(config_line_t));
(*next)->key = k; (*next)->key = k;
(*next)->value = v; (*next)->value = v;
(*next)->next = NULL; (*next)->next = NULL;
(*next)->command = command;
next = &((*next)->next); next = &((*next)->next);
} else { } else {
tor_free(k); tor_free(k);
@ -2048,7 +2090,19 @@ config_assign_value(const config_format_t *fmt, or_options_t *options,
case CONFIG_TYPE_LINELIST: case CONFIG_TYPE_LINELIST:
case CONFIG_TYPE_LINELIST_S: 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); config_line_append((config_line_t**)lvalue, c->key, c->value);
}
break; break;
case CONFIG_TYPE_OBSOLETE: case CONFIG_TYPE_OBSOLETE:
log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key); log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key);
@ -2064,6 +2118,28 @@ config_assign_value(const config_format_t *fmt, or_options_t *options,
return 0; return 0;
} }
/** Mark every linelist in <b>options<b> "fragile", so that fresh assignments
* to it will replace old ones. */
static void
config_mark_lists_fragile(const config_format_t *fmt, or_options_t *options)
{
int i;
tor_assert(fmt);
tor_assert(options);
for (i = 0; fmt->vars[i].name; ++i) {
const config_var_t *var = &fmt->vars[i];
config_line_t *list;
if (var->type != CONFIG_TYPE_LINELIST &&
var->type != CONFIG_TYPE_LINELIST_V)
continue;
list = *(config_line_t **)STRUCT_VAR_P(options, var->var_offset);
if (list)
list->fragile = 1;
}
}
/** If <b>c</b> is a syntactically valid configuration line, update /** If <b>c</b> is a syntactically valid configuration line, update
* <b>options</b> with its value and return 0. Otherwise return -1 for bad * <b>options</b> with its value and return 0. Otherwise return -1 for bad
* key, -2 for bad value. * key, -2 for bad value.
@ -2106,8 +2182,9 @@ config_assign_line(const config_format_t *fmt, or_options_t *options,
if (!strlen(c->value)) { if (!strlen(c->value)) {
/* reset or clear it, then return */ /* reset or clear it, then return */
if (!clear_first) { if (!clear_first) {
if (var->type == CONFIG_TYPE_LINELIST || if ((var->type == CONFIG_TYPE_LINELIST ||
var->type == CONFIG_TYPE_LINELIST_S) { var->type == CONFIG_TYPE_LINELIST_S) &&
c->command != CONFIG_LINE_CLEAR) {
/* We got an empty linelist from the torrc or command line. /* We got an empty linelist from the torrc or command line.
As a special case, call this an error. Warn and ignore. */ As a special case, call this an error. Warn and ignore. */
log_warn(LD_CONFIG, log_warn(LD_CONFIG,
@ -2117,6 +2194,8 @@ config_assign_line(const config_format_t *fmt, or_options_t *options,
} }
} }
return 0; return 0;
} else if (c->command == CONFIG_LINE_CLEAR && !clear_first) {
option_reset(fmt, options, var, use_defaults);
} }
if (options_seen && (var->type != CONFIG_TYPE_LINELIST && if (options_seen && (var->type != CONFIG_TYPE_LINELIST &&
@ -2211,7 +2290,7 @@ config_lines_dup(const config_line_t *inp)
config_line_t *result = NULL; config_line_t *result = NULL;
config_line_t **next_out = &result; config_line_t **next_out = &result;
while (inp) { while (inp) {
*next_out = tor_malloc(sizeof(config_line_t)); *next_out = tor_malloc_zero(sizeof(config_line_t));
(*next_out)->key = tor_strdup(inp->key); (*next_out)->key = tor_strdup(inp->key);
(*next_out)->value = tor_strdup(inp->value); (*next_out)->value = tor_strdup(inp->value);
inp = inp->next; inp = inp->next;
@ -2448,6 +2527,12 @@ config_assign(const config_format_t *fmt, void *options, config_line_t *list,
list = list->next; list = list->next;
} }
bitarray_free(options_seen); bitarray_free(options_seen);
/** Now we're done assigning a group of options to the configuration.
* Subsequent group assignments should _replace_ linelists, not extend
* them. */
config_mark_lists_fragile(fmt, options);
return 0; return 0;
} }
@ -2943,25 +3028,31 @@ config_init(const config_format_t *fmt, void *options)
* Else, if comment_defaults, write default values as comments. * Else, if comment_defaults, write default values as comments.
*/ */
static char * static char *
config_dump(const config_format_t *fmt, const void *options, int minimal, config_dump(const config_format_t *fmt, const void *default_options,
const void *options, int minimal,
int comment_defaults) int comment_defaults)
{ {
smartlist_t *elements; smartlist_t *elements;
or_options_t *defaults; const or_options_t *defaults = default_options;
void *defaults_tmp = NULL;
config_line_t *line, *assigned; config_line_t *line, *assigned;
char *result; char *result;
int i; int i;
char *msg = NULL; char *msg = NULL;
defaults = config_alloc(fmt); if (defaults == NULL) {
config_init(fmt, defaults); defaults = defaults_tmp = config_alloc(fmt);
config_init(fmt, defaults_tmp);
}
/* XXX use a 1 here so we don't add a new log line while dumping */ /* XXX use a 1 here so we don't add a new log line while dumping */
if (fmt->validate_fn(NULL,defaults, 1, &msg) < 0) { if (default_options == NULL) {
if (fmt->validate_fn(NULL, defaults_tmp, 1, &msg) < 0) {
log_err(LD_BUG, "Failed to validate default config."); log_err(LD_BUG, "Failed to validate default config.");
tor_free(msg); tor_free(msg);
tor_assert(0); tor_assert(0);
} }
}
elements = smartlist_create(); elements = smartlist_create();
for (i=0; fmt->vars[i].name; ++i) { for (i=0; fmt->vars[i].name; ++i) {
@ -3002,7 +3093,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal,
result = smartlist_join_strings(elements, "", 0, NULL); result = smartlist_join_strings(elements, "", 0, NULL);
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements); smartlist_free(elements);
config_free(fmt, defaults); if (defaults_tmp)
config_free(fmt, defaults_tmp);
return result; return result;
} }
@ -3013,7 +3105,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal,
char * char *
options_dump(const or_options_t *options, int minimal) options_dump(const or_options_t *options, int minimal)
{ {
return config_dump(&options_format, options, minimal, 0); return config_dump(&options_format, global_default_options,
options, minimal, 0);
} }
/** Return 0 if every element of sl is a string holding a decimal /** Return 0 if every element of sl is a string holding a decimal
@ -4167,17 +4260,25 @@ get_windows_conf_root(void)
} }
#endif #endif
/** Return the default location for our torrc file. */ /** Return the default location for our torrc file.
* DOCDOC defaults_file */
static const char * static const char *
get_default_conf_file(void) get_default_conf_file(int defaults_file)
{ {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (defaults_file) {
static char defaults_path[MAX_PATH+1];
tor_snprintf(defaults_path, MAX_PATH, "%s\\torrc-defaults",
get_windows_conf_root());
return defaults_path;
} else {
static char path[MAX_PATH+1]; static char path[MAX_PATH+1];
strlcpy(path, get_windows_conf_root(), MAX_PATH); tor_snprintf(path, MAX_PATH, "%s\\torrc",
strlcat(path,"\\torrc",MAX_PATH); get_windows_conf_root());
return path; return path;
}
#else #else
return (CONFDIR "/torrc"); return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc";
#endif #endif
} }
@ -4210,36 +4311,45 @@ check_nickname_list(const char *lst, const char *name, char **msg)
return r; return r;
} }
/** Learn config file name from command line arguments, or use the default */ /** Learn config file name from command line arguments, or use the default,
* DOCDOC defaults_file */
static char * static char *
find_torrc_filename(int argc, char **argv, find_torrc_filename(int argc, char **argv,
int defaults_file,
int *using_default_torrc, int *ignore_missing_torrc) int *using_default_torrc, int *ignore_missing_torrc)
{ {
char *fname=NULL; char *fname=NULL;
int i; int i;
const char *fname_opt = defaults_file ? "--defaults-torrc" : "-f";
const char *ignore_opt = defaults_file ? NULL : "--ignore-missing-torrc";
if (defaults_file)
*ignore_missing_torrc = 1;
for (i = 1; i < argc; ++i) { for (i = 1; i < argc; ++i) {
if (i < argc-1 && !strcmp(argv[i],"-f")) { if (i < argc-1 && !strcmp(argv[i],fname_opt)) {
if (fname) { if (fname) {
log(LOG_WARN, LD_CONFIG, "Duplicate -f options on command line."); log(LOG_WARN, LD_CONFIG, "Duplicate %s options on command line.",
fname_opt);
tor_free(fname); tor_free(fname);
} }
fname = expand_filename(argv[i+1]); fname = expand_filename(argv[i+1]);
*using_default_torrc = 0; *using_default_torrc = 0;
++i; ++i;
} else if (!strcmp(argv[i],"--ignore-missing-torrc")) { } else if (ignore_opt && !strcmp(argv[i],ignore_opt)) {
*ignore_missing_torrc = 1; *ignore_missing_torrc = 1;
} }
} }
if (*using_default_torrc) { if (*using_default_torrc) {
/* didn't find one, try CONFDIR */ /* didn't find one, try CONFDIR */
const char *dflt = get_default_conf_file(); const char *dflt = get_default_conf_file(defaults_file);
if (dflt && file_status(dflt) == FN_FILE) { if (dflt && file_status(dflt) == FN_FILE) {
fname = tor_strdup(dflt); fname = tor_strdup(dflt);
} else { } else {
#ifndef MS_WINDOWS #ifndef MS_WINDOWS
char *fn; char *fn = NULL;
if (!defaults_file)
fn = expand_filename("~/.torrc"); fn = expand_filename("~/.torrc");
if (fn && file_status(fn) == FN_FILE) { if (fn && file_status(fn) == FN_FILE) {
fname = fn; fname = fn;
@ -4255,31 +4365,34 @@ find_torrc_filename(int argc, char **argv,
return fname; return fname;
} }
/** Load torrc from disk, setting torrc_fname if successful */ /** Load torrc from disk, setting torrc_fname if successful.
* DOCDOC defaults_file */
static char * static char *
load_torrc_from_disk(int argc, char **argv) load_torrc_from_disk(int argc, char **argv, int defaults_file)
{ {
char *fname=NULL; char *fname=NULL;
char *cf = NULL; char *cf = NULL;
int using_default_torrc = 1; int using_default_torrc = 1;
int ignore_missing_torrc = 0; int ignore_missing_torrc = 0;
char **fname_var = defaults_file ? &torrc_fname : &torrc_defaults_fname;
fname = find_torrc_filename(argc, argv, fname = find_torrc_filename(argc, argv, defaults_file,
&using_default_torrc, &ignore_missing_torrc); &using_default_torrc, &ignore_missing_torrc);
tor_assert(fname); tor_assert(fname);
log(LOG_DEBUG, LD_CONFIG, "Opening config file \"%s\"", fname); log(LOG_DEBUG, LD_CONFIG, "Opening config file \"%s\"", fname);
tor_free(torrc_fname); tor_free(*fname_var);
torrc_fname = fname; *fname_var = fname;
/* Open config file */ /* Open config file */
if (file_status(fname) != FN_FILE || if (file_status(fname) != FN_FILE ||
!(cf = read_file_to_str(fname,0,NULL))) { !(cf = read_file_to_str(fname,0,NULL))) {
if (using_default_torrc == 1 || ignore_missing_torrc ) { if (using_default_torrc == 1 || ignore_missing_torrc) {
if (!defaults_file)
log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, " log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, "
"using reasonable defaults.", fname); "using reasonable defaults.", fname);
tor_free(fname); /* sets fname to NULL */ tor_free(fname); /* sets fname to NULL */
torrc_fname = NULL; *fname_var = NULL;
cf = tor_strdup(""); cf = tor_strdup("");
} else { } else {
log(LOG_WARN, LD_CONFIG, log(LOG_WARN, LD_CONFIG,
@ -4293,7 +4406,7 @@ load_torrc_from_disk(int argc, char **argv)
return cf; return cf;
err: err:
tor_free(fname); tor_free(fname);
torrc_fname = NULL; *fname_var = NULL;
return NULL; return NULL;
} }
@ -4304,7 +4417,7 @@ load_torrc_from_disk(int argc, char **argv)
int int
options_init_from_torrc(int argc, char **argv) options_init_from_torrc(int argc, char **argv)
{ {
char *cf=NULL; char *cf=NULL, *cf_defaults=NULL;
int i, retval, command; int i, retval, command;
static char **backup_argv; static char **backup_argv;
static int backup_argc; static int backup_argc;
@ -4364,13 +4477,15 @@ options_init_from_torrc(int argc, char **argv)
if (command == CMD_HASH_PASSWORD) { if (command == CMD_HASH_PASSWORD) {
cf = tor_strdup(""); cf = tor_strdup("");
} else { } else {
cf = load_torrc_from_disk(argc, argv); cf_defaults = load_torrc_from_disk(argc, argv, 1);
cf = load_torrc_from_disk(argc, argv, 0);
if (!cf) if (!cf)
goto err; goto err;
} }
retval = options_init_from_string(cf, command, command_arg, &errmsg); retval = options_init_from_string(cf_defaults, cf, command, command_arg, &errmsg);
tor_free(cf); tor_free(cf);
tor_free(cf_defaults);
if (retval < 0) if (retval < 0)
goto err; goto err;
@ -4394,13 +4509,13 @@ options_init_from_torrc(int argc, char **argv)
* * -4 for error while setting the new options * * -4 for error while setting the new options
*/ */
setopt_err_t setopt_err_t
options_init_from_string(const char *cf, options_init_from_string(const char *cf_defaults, const char *cf,
int command, const char *command_arg, int command, const char *command_arg,
char **msg) char **msg)
{ {
or_options_t *oldoptions, *newoptions; or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL;
config_line_t *cl; config_line_t *cl;
int retval; int retval, i;
setopt_err_t err = SETOPT_ERR_MISC; setopt_err_t err = SETOPT_ERR_MISC;
tor_assert(msg); tor_assert(msg);
@ -4413,8 +4528,12 @@ options_init_from_string(const char *cf,
newoptions->command = command; newoptions->command = command;
newoptions->command_arg = command_arg; newoptions->command_arg = command_arg;
for (i = 0; i < 2; ++i) {
const char *body = i==0 ? cf_defaults : cf;
if (!body)
continue;
/* get config lines, assign them */ /* get config lines, assign them */
retval = config_get_lines(cf, &cl); retval = config_get_lines(body, &cl, 1);
if (retval < 0) { if (retval < 0) {
err = SETOPT_ERR_PARSE; err = SETOPT_ERR_PARSE;
goto err; goto err;
@ -4425,6 +4544,9 @@ options_init_from_string(const char *cf,
err = SETOPT_ERR_PARSE; err = SETOPT_ERR_PARSE;
goto err; goto err;
} }
if (i==0)
newdefaultoptions = options_dup(&options_format, newoptions);
}
/* Go through command-line variables too */ /* Go through command-line variables too */
retval = config_assign(&options_format, newoptions, retval = config_assign(&options_format, newoptions,
@ -4458,6 +4580,8 @@ options_init_from_string(const char *cf,
/* Clear newoptions and re-initialize them with new defaults. */ /* Clear newoptions and re-initialize them with new defaults. */
config_free(&options_format, newoptions); config_free(&options_format, newoptions);
config_free(&options_format, newdefaultoptions);
newdefaultoptions = NULL;
newoptions = tor_malloc_zero(sizeof(or_options_t)); newoptions = tor_malloc_zero(sizeof(or_options_t));
newoptions->_magic = OR_OPTIONS_MAGIC; newoptions->_magic = OR_OPTIONS_MAGIC;
options_init(newoptions); options_init(newoptions);
@ -4465,7 +4589,12 @@ options_init_from_string(const char *cf,
newoptions->command_arg = command_arg; newoptions->command_arg = command_arg;
/* Assign all options a second time. */ /* Assign all options a second time. */
retval = config_get_lines(cf, &cl); for (i = 0; i < 2; ++i) {
const char *body = i==0 ? cf_defaults : cf;
if (!body)
continue;
/* get config lines, assign them */
retval = config_get_lines(body, &cl, 1);
if (retval < 0) { if (retval < 0) {
err = SETOPT_ERR_PARSE; err = SETOPT_ERR_PARSE;
goto err; goto err;
@ -4476,11 +4605,8 @@ options_init_from_string(const char *cf,
err = SETOPT_ERR_PARSE; err = SETOPT_ERR_PARSE;
goto err; goto err;
} }
retval = config_assign(&options_format, newoptions, if (i==0)
global_cmdline_options, 0, 0, msg); newdefaultoptions = options_dup(&options_format, newoptions);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
goto err;
} }
} }
@ -4499,11 +4625,14 @@ options_init_from_string(const char *cf,
err = SETOPT_ERR_SETTING; err = SETOPT_ERR_SETTING;
goto err; /* frees and replaces old options */ goto err; /* frees and replaces old options */
} }
config_free(&options_format, global_default_options);
global_default_options = newdefaultoptions;
return SETOPT_OK; return SETOPT_OK;
err: err:
config_free(&options_format, newoptions); config_free(&options_format, newoptions);
config_free(&options_format, newdefaultoptions);
if (*msg) { if (*msg) {
char *old_msg = *msg; char *old_msg = *msg;
tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg); tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg);
@ -4515,12 +4644,14 @@ options_init_from_string(const char *cf,
/** Return the location for our configuration file. /** Return the location for our configuration file.
*/ */
const char * const char *
get_torrc_fname(void) get_torrc_fname(int defaults_fname)
{ {
if (torrc_fname) const char *fname = defaults_fname ? torrc_defaults_fname : torrc_fname;
return torrc_fname;
if (fname)
return fname;
else else
return get_default_conf_file(); return get_default_conf_file(defaults_fname);
} }
/** Adjust the address map based on the MapAddress elements in the /** Adjust the address map based on the MapAddress elements in the
@ -5125,7 +5256,7 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
* clause once Tor 0.1.2.17 is obsolete. */ * clause once Tor 0.1.2.17 is obsolete. */
log_warn(LD_CONFIG, "Dangerous dirserver line. To correct, erase your " log_warn(LD_CONFIG, "Dangerous dirserver line. To correct, erase your "
"torrc file (%s), or reinstall Tor and use the default torrc.", "torrc file (%s), or reinstall Tor and use the default torrc.",
get_torrc_fname()); get_torrc_fname(0));
goto err; goto err;
} }
if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) { if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) {
@ -5684,7 +5815,7 @@ options_save_current(void)
* If we try falling back to datadirectory or something, we have a better * If we try falling back to datadirectory or something, we have a better
* chance of saving the configuration, but a better chance of doing * chance of saving the configuration, but a better chance of doing
* something the user never expected. */ * something the user never expected. */
return write_configuration_file(get_torrc_fname(), get_options()); return write_configuration_file(get_torrc_fname(0), get_options());
} }
/** Mapping from a unit name to a multiplier for converting that unit into a /** Mapping from a unit name to a multiplier for converting that unit into a
@ -6150,7 +6281,7 @@ or_state_load(void)
if (contents) { if (contents) {
config_line_t *lines=NULL; config_line_t *lines=NULL;
int assign_retval; int assign_retval;
if (config_get_lines(contents, &lines)<0) if (config_get_lines(contents, &lines, 0)<0)
goto done; goto done;
assign_retval = config_assign(&state_format, new_state, assign_retval = config_assign(&state_format, new_state,
lines, 0, 0, &errmsg); lines, 0, 0, &errmsg);
@ -6254,7 +6385,7 @@ or_state_save(time_t now)
tor_free(global_state->TorVersion); tor_free(global_state->TorVersion);
tor_asprintf(&global_state->TorVersion, "Tor %s", get_version()); tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
state = config_dump(&state_format, global_state, 1, 0); state = config_dump(&state_format, NULL, global_state, 1, 0);
format_local_iso_time(tbuf, now); format_local_iso_time(tbuf, now);
tor_asprintf(&contents, tor_asprintf(&contents,
"# Tor state file last generated on %s local time\n" "# Tor state file last generated on %s local time\n"

View File

@ -23,7 +23,7 @@ const char *escaped_safe_str_client(const char *address);
const char *escaped_safe_str(const char *address); const char *escaped_safe_str(const char *address);
const char *get_version(void); const char *get_version(void);
int config_get_lines(const char *string, config_line_t **result); int config_get_lines(const char *string, config_line_t **result, int extended);
void config_free_lines(config_line_t *front); void config_free_lines(config_line_t *front);
setopt_err_t options_trial_assign(config_line_t *list, int use_defaults, setopt_err_t options_trial_assign(config_line_t *list, int use_defaults,
int clear_first, char **msg); int clear_first, char **msg);
@ -33,14 +33,14 @@ int is_local_addr(const tor_addr_t *addr);
void options_init(or_options_t *options); void options_init(or_options_t *options);
char *options_dump(const or_options_t *options, int minimal); char *options_dump(const or_options_t *options, int minimal);
int options_init_from_torrc(int argc, char **argv); int options_init_from_torrc(int argc, char **argv);
setopt_err_t options_init_from_string(const char *cf, setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf,
int command, const char *command_arg, char **msg); int command, const char *command_arg, char **msg);
int option_is_recognized(const char *key); int option_is_recognized(const char *key);
const char *option_get_canonical_name(const char *key); const char *option_get_canonical_name(const char *key);
config_line_t *option_get_assignment(const or_options_t *options, config_line_t *option_get_assignment(const or_options_t *options,
const char *key); const char *key);
int options_save_current(void); int options_save_current(void);
const char *get_torrc_fname(void); const char *get_torrc_fname(int defaults_fname);
char *options_get_datadir_fname2_suffix(const or_options_t *options, char *options_get_datadir_fname2_suffix(const or_options_t *options,
const char *sub1, const char *sub2, const char *sub1, const char *sub2,
const char *suffix); const char *suffix);

View File

@ -737,7 +737,7 @@ control_setconf_helper(control_connection_t *conn, uint32_t len, char *body,
SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp)); SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp));
smartlist_free(entries); smartlist_free(entries);
if (config_get_lines(config, &lines) < 0) { if (config_get_lines(config, &lines, 0) < 0) {
log_warn(LD_CONTROL,"Controller gave us config lines we can't parse."); log_warn(LD_CONTROL,"Controller gave us config lines we can't parse.");
connection_write_str_to_buf("551 Couldn't parse configuration\r\n", connection_write_str_to_buf("551 Couldn't parse configuration\r\n",
conn); conn);
@ -883,7 +883,7 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len,
const char *msg = NULL; const char *msg = NULL;
(void) len; (void) len;
retval = options_init_from_string(body, CMD_RUN_TOR, NULL, &errstring); retval = options_init_from_string(NULL, body, CMD_RUN_TOR, NULL, &errstring);
if (retval != SETOPT_OK) if (retval != SETOPT_OK)
log_warn(LD_CONTROL, log_warn(LD_CONTROL,
@ -1378,7 +1378,9 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
if (!strcmp(question, "version")) { if (!strcmp(question, "version")) {
*answer = tor_strdup(get_version()); *answer = tor_strdup(get_version());
} else if (!strcmp(question, "config-file")) { } else if (!strcmp(question, "config-file")) {
*answer = tor_strdup(get_torrc_fname()); *answer = tor_strdup(get_torrc_fname(0));
} else if (!strcmp(question, "config-defaults-file")) {
*answer = tor_strdup(get_torrc_fname(1));
} else if (!strcmp(question, "config-text")) { } else if (!strcmp(question, "config-text")) {
*answer = options_dump(get_options(), 1); *answer = options_dump(get_options(), 1);
} else if (!strcmp(question, "info/names")) { } else if (!strcmp(question, "info/names")) {

View File

@ -232,7 +232,7 @@ dirserv_load_fingerprint_file(void)
} }
tor_free(fname); tor_free(fname);
result = config_get_lines(cf, &front); result = config_get_lines(cf, &front, 0);
tor_free(cf); tor_free(cf);
if (result < 0) { if (result < 0) {
log_warn(LD_CONFIG, "Error reading from fingerprint file"); log_warn(LD_CONFIG, "Error reading from fingerprint file");

View File

@ -2838,11 +2838,25 @@ typedef struct port_cfg_t {
char unix_addr[FLEXIBLE_ARRAY_MEMBER]; char unix_addr[FLEXIBLE_ARRAY_MEMBER];
} port_cfg_t; } port_cfg_t;
/** Ordinary configuration line. */
#define CONFIG_LINE_NORMAL 0
/** Appends to previous configuration for the same option, even if we
* would ordinary replace it. */
#define CONFIG_LINE_APPEND 1
/* Removes all previous configuration for an option. */
#define CONFIG_LINE_CLEAR 2
/** A linked list of lines in a config file. */ /** A linked list of lines in a config file. */
typedef struct config_line_t { typedef struct config_line_t {
char *key; char *key;
char *value; char *value;
struct config_line_t *next; struct config_line_t *next;
/** What special treatment (if any) does this line require? */
unsigned int command:2;
/** If true, subsequent assignments to this linelist should replace
* it, not extend it. Set only on the first item in a linelist in an
* or_options_t. */
unsigned int fragile:1;
} config_line_t; } config_line_t;
typedef struct routerset_t routerset_t; typedef struct routerset_t routerset_t;