Forráskód Böngészése

Add the ability to append and clear linelist options from cmdline

This will be important for getting stuff to work right across zones.
Nick Mathewson 12 éve
szülő
commit
73436a1d6f
6 módosított fájl, 65 hozzáadás és 20 törlés
  1. 6 0
      changes/config
  2. 53 16
      src/or/config.c
  3. 1 1
      src/or/config.h
  4. 1 1
      src/or/control.c
  5. 1 1
      src/or/dirserv.c
  6. 3 1
      src/or/or.h

+ 6 - 0
changes/config

@@ -7,6 +7,12 @@
       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 "/".
+
 
   o Minor bugfixes:
     - Restore behavior of overriding SocksPort, ORPort, and similar

+ 53 - 16
src/or/config.c

@@ -1728,6 +1728,9 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
   int i = 1;
 
   while (i < argc) {
+    unsigned command = CONFIG_LINE_NORMAL;
+    int want_arg = 1;
+
     if (!strcmp(argv[i],"-f") ||
         !strcmp(argv[i],"--hash-password")) {
       i += 2; /* command-line option with argument. ignore them. */
@@ -1745,13 +1748,6 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
       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));
     s = argv[i];
 
@@ -1760,15 +1756,33 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
       s++;
     if (*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)->value = tor_strdup(argv[i+1]);
+    (*new)->value = want_arg ? tor_strdup(argv[i+1]) : tor_strdup("");
+    (*new)->command = command;
     (*new)->next = NULL;
     log(LOG_DEBUG, LD_CONFIG, "command line: parsed keyword '%s', value '%s'",
         (*new)->key, (*new)->value);
 
     new = &((*new)->next);
-    i += 2;
+    i += want_arg ? 2 : 1;
   }
   *result = front;
   return 0;
@@ -1796,9 +1810,12 @@ config_line_append(config_line_t **lst,
 /** Helper: parse the config string and strdup into key/value
  * strings. Set *result to the list, or NULL if parsing the string
  * 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
-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;
   char *k, *v;
@@ -1814,6 +1831,22 @@ config_get_lines(const char *string, config_line_t **result)
       return -1;
     }
     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
        * rather than using config_line_append over and over and getting
        * n^2 performance. */
@@ -1821,6 +1854,7 @@ config_get_lines(const char *string, config_line_t **result)
       (*next)->key = k;
       (*next)->value = v;
       (*next)->next = NULL;
+      (*next)->command = command;
       next = &((*next)->next);
     } else {
       tor_free(k);
@@ -2140,8 +2174,9 @@ config_assign_line(const config_format_t *fmt, or_options_t *options,
   if (!strlen(c->value)) {
     /* reset or clear it, then return */
     if (!clear_first) {
-      if (var->type == CONFIG_TYPE_LINELIST ||
-          var->type == CONFIG_TYPE_LINELIST_S) {
+      if ((var->type == CONFIG_TYPE_LINELIST ||
+           var->type == CONFIG_TYPE_LINELIST_S) &&
+          c->command != CONFIG_LINE_CLEAR) {
         /* We got an empty linelist from the torrc or command line.
            As a special case, call this an error. Warn and ignore. */
         log_warn(LD_CONFIG,
@@ -2151,6 +2186,8 @@ config_assign_line(const config_format_t *fmt, or_options_t *options,
       }
     }
     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 &&
@@ -4454,7 +4491,7 @@ options_init_from_string(const char *cf,
   newoptions->command_arg = command_arg;
 
   /* get config lines, assign them */
-  retval = config_get_lines(cf, &cl);
+  retval = config_get_lines(cf, &cl, 1);
   if (retval < 0) {
     err = SETOPT_ERR_PARSE;
     goto err;
@@ -4505,7 +4542,7 @@ options_init_from_string(const char *cf,
     newoptions->command_arg = command_arg;
 
     /* Assign all options a second time. */
-    retval = config_get_lines(cf, &cl);
+    retval = config_get_lines(cf, &cl, 1);
     if (retval < 0) {
       err = SETOPT_ERR_PARSE;
       goto err;
@@ -6190,7 +6227,7 @@ or_state_load(void)
   if (contents) {
     config_line_t *lines=NULL;
     int assign_retval;
-    if (config_get_lines(contents, &lines)<0)
+    if (config_get_lines(contents, &lines, 0)<0)
       goto done;
     assign_retval = config_assign(&state_format, new_state,
                                   lines, 0, 0, &errmsg);

+ 1 - 1
src/or/config.h

@@ -23,7 +23,7 @@ const char *escaped_safe_str_client(const char *address);
 const char *escaped_safe_str(const char *address);
 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);
 setopt_err_t options_trial_assign(config_line_t *list, int use_defaults,
                                   int clear_first, char **msg);

+ 1 - 1
src/or/control.c

@@ -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_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.");
     connection_write_str_to_buf("551 Couldn't parse configuration\r\n",
                                 conn);

+ 1 - 1
src/or/dirserv.c

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

+ 3 - 1
src/or/or.h

@@ -2841,6 +2841,8 @@ typedef struct port_cfg_t {
 /** 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. */
 typedef struct config_line_t {
@@ -2848,7 +2850,7 @@ typedef struct config_line_t {
   char *value;
   struct config_line_t *next;
   /** What special treatment (if any) does this line require? */
-  unsigned int command:1;
+  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. */