[tor-commits] [tor/master] Support for a defaults torrc file.

nickm at torproject.org nickm at torproject.org
Tue Nov 29 22:39:36 UTC 2011


commit 230422b955e1708f27f42cdd25e8b21a33fdd3dd
Author: Nick Mathewson <nickm at torproject.org>
Date:   Sun Nov 27 22:25:52 2011 -0500

    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.
---
 changes/config   |    9 ++-
 src/or/config.c  |  202 ++++++++++++++++++++++++++++++++++--------------------
 src/or/config.h  |    4 +-
 src/or/control.c |    6 +-
 4 files changed, 142 insertions(+), 79 deletions(-)

diff --git a/changes/config b/changes/config
index 08874eb..3a1c7d1 100644
--- a/changes/config
+++ b/changes/config
@@ -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
diff --git a/src/or/config.c b/src/or/config.c
index fa44793..120c8ce 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -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,24 +3028,30 @@ 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) {
-    log_err(LD_BUG, "Failed to validate default config.");
-    tor_free(msg);
-    tor_assert(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();
@@ -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
-  static char path[MAX_PATH+1];
-  strlcpy(path, get_windows_conf_root(), MAX_PATH);
-  strlcat(path,"\\torrc",MAX_PATH);
-  return path;
+  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];
+    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,37 +4311,46 @@ 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;
-      fn = expand_filename("~/.torrc");
+      char *fn = NULL;
+      if (!defaults_file)
+        fn = expand_filename("~/.torrc");
       if (fn && file_status(fn) == FN_FILE) {
         fname = fn;
       } else {
@@ -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 ) {
-      log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, "
-          "using reasonable defaults.", fname);
+    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,17 +4528,24 @@ options_init_from_string(const char *cf,
   newoptions->command = command;
   newoptions->command_arg = command_arg;
 
-  /* get config lines, assign them */
-  retval = config_get_lines(cf, &cl, 1);
-  if (retval < 0) {
-    err = SETOPT_ERR_PARSE;
-    goto err;
-  }
-  retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
-  config_free_lines(cl);
-  if (retval < 0) {
-    err = SETOPT_ERR_PARSE;
-    goto err;
+  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;
+    }
+    retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
+    config_free_lines(cl);
+    if (retval < 0) {
+      err = SETOPT_ERR_PARSE;
+      goto err;
+    }
+    if (i==0)
+      newdefaultoptions = options_dup(&options_format, newoptions);
   }
 
   /* Go through command-line variables too */
@@ -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,22 +4589,24 @@ 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);
-    if (retval < 0) {
-      err = SETOPT_ERR_PARSE;
-      goto err;
-    }
-    retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
-    config_free_lines(cl);
-    if (retval < 0) {
-      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;
+    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;
+      }
+      retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
+      config_free_lines(cl);
+      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"
diff --git a/src/or/config.h b/src/or/config.h
index 73095de..e1fc5cf 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -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);
diff --git a/src/or/control.c b/src/or/control.c
index 19904dd..8d924b4 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -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")) {





More information about the tor-commits mailing list