Add the ability to append and clear linelist options from cmdline

This will be important for getting stuff to work right across zones.
This commit is contained in:
Nick Mathewson 2011-11-27 21:32:51 -05:00
parent 9ce5801e22
commit 73436a1d6f
6 changed files with 65 additions and 20 deletions

View File

@ -7,6 +7,12 @@
the user to override list options (like exit policies and the user to override list options (like exit policies and
ports to listen on) from the command line, rather than simply ports to listen on) from the command line, rather than simply
appending to the list. 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: o Minor bugfixes:
- Restore behavior of overriding SocksPort, ORPort, and similar - Restore behavior of overriding SocksPort, ORPort, and similar

View File

@ -1728,6 +1728,9 @@ 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],"--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. */
@ -1745,13 +1748,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 +1756,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;
@ -1796,9 +1810,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,6 +1831,22 @@ 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. */
@ -1821,6 +1854,7 @@ config_get_lines(const char *string, config_line_t **result)
(*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);
@ -2140,8 +2174,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,
@ -2151,6 +2186,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 &&
@ -4454,7 +4491,7 @@ options_init_from_string(const char *cf,
newoptions->command_arg = command_arg; newoptions->command_arg = command_arg;
/* get config lines, assign them */ /* get config lines, assign them */
retval = config_get_lines(cf, &cl); retval = config_get_lines(cf, &cl, 1);
if (retval < 0) { if (retval < 0) {
err = SETOPT_ERR_PARSE; err = SETOPT_ERR_PARSE;
goto err; goto err;
@ -4505,7 +4542,7 @@ 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); retval = config_get_lines(cf, &cl, 1);
if (retval < 0) { if (retval < 0) {
err = SETOPT_ERR_PARSE; err = SETOPT_ERR_PARSE;
goto err; goto err;
@ -6190,7 +6227,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);

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);

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);

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

@ -2841,6 +2841,8 @@ typedef struct port_cfg_t {
/** Appends to previous configuration for the same option, even if we /** Appends to previous configuration for the same option, even if we
* would ordinary replace it. */ * would ordinary replace it. */
#define CONFIG_LINE_APPEND 1 #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 {
@ -2848,7 +2850,7 @@ typedef struct config_line_t {
char *value; char *value;
struct config_line_t *next; struct config_line_t *next;
/** What special treatment (if any) does this line require? */ /** 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 /** If true, subsequent assignments to this linelist should replace
* it, not extend it. Set only on the first item in a linelist in an * it, not extend it. Set only on the first item in a linelist in an
* or_options_t. */ * or_options_t. */