Support for a defaults torrc file.

This will mainly help distributors by giving a way to set system or package
defaults that a user can override, and that a later package can replace.

No promises about the particular future location or semantics for this:
we will probably want to tweak it some before 0.2.3.x-rc

The file is searched for in CONFDIR/torrc-defaults , which can be
overridden with the "--defaults-torrc" option on the command line.
This commit is contained in:
Nick Mathewson 2011-11-27 22:25:52 -05:00
parent 73436a1d6f
commit 230422b955
4 changed files with 142 additions and 79 deletions

View File

@ -12,7 +12,14 @@
- 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

View File

@ -661,8 +661,12 @@ static const config_format_t state_format = {
/** Command-line and config-file options. */
static or_options_t *global_options = NULL;
/** DOCDOC */
static or_options_t *global_default_options = NULL;
/** Name of most recently read torrc file. */
static char *torrc_fname = NULL;
/** DOCDOC */
static char *torrc_defaults_fname;
/** Persistent serialized state. */
static or_state_t *global_state = NULL;
/** Configuration Options set by command line. */
@ -806,6 +810,8 @@ config_free_all(void)
{
or_options_free(global_options);
global_options = NULL;
or_options_free(global_default_options);
global_default_options = NULL;
config_free(&state_format, global_state);
global_state = NULL;
@ -821,6 +827,7 @@ config_free_all(void)
}
tor_free(torrc_fname);
tor_free(torrc_defaults_fname);
tor_free(_version);
tor_free(global_dirfrontpagecontents);
}
@ -1732,6 +1739,7 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
int want_arg = 1;
if (!strcmp(argv[i],"-f") ||
!strcmp(argv[i],"--defaults-torrc") ||
!strcmp(argv[i],"--hash-password")) {
i += 2; /* command-line option with argument. ignore them. */
continue;
@ -3020,25 +3028,31 @@ config_init(const config_format_t *fmt, void *options)
* Else, if comment_defaults, write default values as comments.
*/
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)
{
smartlist_t *elements;
or_options_t *defaults;
const or_options_t *defaults = default_options;
void *defaults_tmp = NULL;
config_line_t *line, *assigned;
char *result;
int i;
char *msg = NULL;
defaults = config_alloc(fmt);
config_init(fmt, defaults);
if (defaults == NULL) {
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 */
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.");
tor_free(msg);
tor_assert(0);
}
}
elements = smartlist_create();
for (i=0; fmt->vars[i].name; ++i) {
@ -3079,7 +3093,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal,
result = smartlist_join_strings(elements, "", 0, NULL);
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements);
config_free(fmt, defaults);
if (defaults_tmp)
config_free(fmt, defaults_tmp);
return result;
}
@ -3090,7 +3105,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal,
char *
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
@ -4244,17 +4260,25 @@ get_windows_conf_root(void)
}
#endif
/** Return the default location for our torrc file. */
/** Return the default location for our torrc file.
* DOCDOC defaults_file */
static const char *
get_default_conf_file(void)
get_default_conf_file(int defaults_file)
{
#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];
strlcpy(path, get_windows_conf_root(), MAX_PATH);
strlcat(path,"\\torrc",MAX_PATH);
tor_snprintf(path, MAX_PATH, "%s\\torrc",
get_windows_conf_root());
return path;
}
#else
return (CONFDIR "/torrc");
return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc";
#endif
}
@ -4287,36 +4311,45 @@ check_nickname_list(const char *lst, const char *name, char **msg)
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 *
find_torrc_filename(int argc, char **argv,
int defaults_file,
int *using_default_torrc, int *ignore_missing_torrc)
{
char *fname=NULL;
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) {
if (i < argc-1 && !strcmp(argv[i],"-f")) {
if (i < argc-1 && !strcmp(argv[i],fname_opt)) {
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);
}
fname = expand_filename(argv[i+1]);
*using_default_torrc = 0;
++i;
} else if (!strcmp(argv[i],"--ignore-missing-torrc")) {
} else if (ignore_opt && !strcmp(argv[i],ignore_opt)) {
*ignore_missing_torrc = 1;
}
}
if (*using_default_torrc) {
/* 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) {
fname = tor_strdup(dflt);
} else {
#ifndef MS_WINDOWS
char *fn;
char *fn = NULL;
if (!defaults_file)
fn = expand_filename("~/.torrc");
if (fn && file_status(fn) == FN_FILE) {
fname = fn;
@ -4332,31 +4365,34 @@ find_torrc_filename(int argc, char **argv,
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 *
load_torrc_from_disk(int argc, char **argv)
load_torrc_from_disk(int argc, char **argv, int defaults_file)
{
char *fname=NULL;
char *cf = NULL;
int using_default_torrc = 1;
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);
tor_assert(fname);
log(LOG_DEBUG, LD_CONFIG, "Opening config file \"%s\"", fname);
tor_free(torrc_fname);
torrc_fname = fname;
tor_free(*fname_var);
*fname_var = fname;
/* Open config file */
if (file_status(fname) != FN_FILE ||
!(cf = read_file_to_str(fname,0,NULL))) {
if (using_default_torrc == 1 || ignore_missing_torrc) {
if (!defaults_file)
log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, "
"using reasonable defaults.", fname);
tor_free(fname); /* sets fname to NULL */
torrc_fname = NULL;
*fname_var = NULL;
cf = tor_strdup("");
} else {
log(LOG_WARN, LD_CONFIG,
@ -4370,7 +4406,7 @@ load_torrc_from_disk(int argc, char **argv)
return cf;
err:
tor_free(fname);
torrc_fname = NULL;
*fname_var = NULL;
return NULL;
}
@ -4381,7 +4417,7 @@ load_torrc_from_disk(int argc, char **argv)
int
options_init_from_torrc(int argc, char **argv)
{
char *cf=NULL;
char *cf=NULL, *cf_defaults=NULL;
int i, retval, command;
static char **backup_argv;
static int backup_argc;
@ -4441,13 +4477,15 @@ options_init_from_torrc(int argc, char **argv)
if (command == CMD_HASH_PASSWORD) {
cf = tor_strdup("");
} 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)
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_defaults);
if (retval < 0)
goto err;
@ -4471,13 +4509,13 @@ options_init_from_torrc(int argc, char **argv)
* * -4 for error while setting the new options
*/
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,
char **msg)
{
or_options_t *oldoptions, *newoptions;
or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL;
config_line_t *cl;
int retval;
int retval, i;
setopt_err_t err = SETOPT_ERR_MISC;
tor_assert(msg);
@ -4490,8 +4528,12 @@ options_init_from_string(const char *cf,
newoptions->command = command;
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 */
retval = config_get_lines(cf, &cl, 1);
retval = config_get_lines(body, &cl, 1);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
goto err;
@ -4502,6 +4544,9 @@ options_init_from_string(const char *cf,
err = SETOPT_ERR_PARSE;
goto err;
}
if (i==0)
newdefaultoptions = options_dup(&options_format, newoptions);
}
/* Go through command-line variables too */
retval = config_assign(&options_format, newoptions,
@ -4535,6 +4580,8 @@ options_init_from_string(const char *cf,
/* Clear newoptions and re-initialize them with new defaults. */
config_free(&options_format, newoptions);
config_free(&options_format, newdefaultoptions);
newdefaultoptions = NULL;
newoptions = tor_malloc_zero(sizeof(or_options_t));
newoptions->_magic = OR_OPTIONS_MAGIC;
options_init(newoptions);
@ -4542,7 +4589,12 @@ options_init_from_string(const char *cf,
newoptions->command_arg = command_arg;
/* Assign all options a second time. */
retval = config_get_lines(cf, &cl, 1);
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) {
err = SETOPT_ERR_PARSE;
goto err;
@ -4553,11 +4605,8 @@ options_init_from_string(const char *cf,
err = SETOPT_ERR_PARSE;
goto err;
}
retval = config_assign(&options_format, newoptions,
global_cmdline_options, 0, 0, msg);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
goto err;
if (i==0)
newdefaultoptions = options_dup(&options_format, newoptions);
}
}
@ -4576,11 +4625,14 @@ options_init_from_string(const char *cf,
err = SETOPT_ERR_SETTING;
goto err; /* frees and replaces old options */
}
config_free(&options_format, global_default_options);
global_default_options = newdefaultoptions;
return SETOPT_OK;
err:
config_free(&options_format, newoptions);
config_free(&options_format, newdefaultoptions);
if (*msg) {
char *old_msg = *msg;
tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg);
@ -4592,12 +4644,14 @@ options_init_from_string(const char *cf,
/** Return the location for our configuration file.
*/
const char *
get_torrc_fname(void)
get_torrc_fname(int defaults_fname)
{
if (torrc_fname)
return torrc_fname;
const char *fname = defaults_fname ? torrc_defaults_fname : torrc_fname;
if (fname)
return fname;
else
return get_default_conf_file();
return get_default_conf_file(defaults_fname);
}
/** Adjust the address map based on the MapAddress elements in the
@ -5202,7 +5256,7 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
* clause once Tor 0.1.2.17 is obsolete. */
log_warn(LD_CONFIG, "Dangerous dirserver line. To correct, erase your "
"torrc file (%s), or reinstall Tor and use the default torrc.",
get_torrc_fname());
get_torrc_fname(0));
goto err;
}
if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) {
@ -5761,7 +5815,7 @@ options_save_current(void)
* If we try falling back to datadirectory or something, we have a better
* chance of saving the configuration, but a better chance of doing
* 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
@ -6331,7 +6385,7 @@ or_state_save(time_t now)
tor_free(global_state->TorVersion);
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);
tor_asprintf(&contents,
"# Tor state file last generated on %s local time\n"

View File

@ -33,14 +33,14 @@ int is_local_addr(const tor_addr_t *addr);
void options_init(or_options_t *options);
char *options_dump(const or_options_t *options, int minimal);
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 option_is_recognized(const char *key);
const char *option_get_canonical_name(const char *key);
config_line_t *option_get_assignment(const or_options_t *options,
const char *key);
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,
const char *sub1, const char *sub2,
const char *suffix);

View File

@ -883,7 +883,7 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len,
const char *msg = NULL;
(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)
log_warn(LD_CONTROL,
@ -1378,7 +1378,9 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
if (!strcmp(question, "version")) {
*answer = tor_strdup(get_version());
} 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")) {
*answer = options_dump(get_options(), 1);
} else if (!strcmp(question, "info/names")) {