tor  0.4.2.0-alpha-dev
confparse.c
Go to the documentation of this file.
1 /* Copyright (c) 2001 Matej Pfajfar.
2  * Copyright (c) 2001-2004, Roger Dingledine.
3  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4  * Copyright (c) 2007-2019, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
6 
24 #define CONFPARSE_PRIVATE
25 #include "core/or/or.h"
26 #include "app/config/confparse.h"
27 #include "feature/nodelist/routerset.h"
28 
29 #include "lib/container/bitarray.h"
30 #include "lib/encoding/confline.h"
31 
32 static uint64_t config_parse_memunit(const char *s, int *ok);
33 static int config_parse_msec_interval(const char *s, int *ok);
34 static int config_parse_interval(const char *s, int *ok);
35 static void config_reset(const config_format_t *fmt, void *options,
36  const config_var_t *var, int use_defaults);
37 
39 void *
41 {
42  void *opts = tor_malloc_zero(fmt->size);
43  *(uint32_t*)STRUCT_VAR_P(opts, fmt->magic_offset) = fmt->magic;
44  CONFIG_CHECK(fmt, opts);
45  return opts;
46 }
47 
48 /*
49  * Functions to parse config options
50  */
51 
57 const char *
58 config_expand_abbrev(const config_format_t *fmt, const char *option,
59  int command_line, int warn_obsolete)
60 {
61  int i;
62  if (! fmt->abbrevs)
63  return option;
64  for (i=0; fmt->abbrevs[i].abbreviated; ++i) {
65  /* Abbreviations are case insensitive. */
66  if (!strcasecmp(option,fmt->abbrevs[i].abbreviated) &&
67  (command_line || !fmt->abbrevs[i].commandline_only)) {
68  if (warn_obsolete && fmt->abbrevs[i].warn) {
69  log_warn(LD_CONFIG,
70  "The configuration option '%s' is deprecated; "
71  "use '%s' instead.",
72  fmt->abbrevs[i].abbreviated,
73  fmt->abbrevs[i].full);
74  }
75  /* Keep going through the list in case we want to rewrite it more.
76  * (We could imagine recursing here, but I don't want to get the
77  * user into an infinite loop if we craft our list wrong.) */
78  option = fmt->abbrevs[i].full;
79  }
80  }
81  return option;
82 }
83 
87 const char *
88 config_find_deprecation(const config_format_t *fmt, const char *key)
89 {
90  if (BUG(fmt == NULL) || BUG(key == NULL))
91  return NULL; // LCOV_EXCL_LINE
92  if (fmt->deprecations == NULL)
93  return NULL;
94 
95  const config_deprecation_t *d;
96  for (d = fmt->deprecations; d->name; ++d) {
97  if (!strcasecmp(d->name, key)) {
98  return d->why_deprecated ? d->why_deprecated : "";
99  }
100  }
101  return NULL;
102 }
103 
105 config_var_t *
107 {
108  int i;
109  size_t keylen = strlen(key);
110  if (!keylen)
111  return NULL; /* if they say "--" on the command line, it's not an option */
112  /* First, check for an exact (case-insensitive) match */
113  for (i=0; fmt->vars[i].name; ++i) {
114  if (!strcasecmp(key, fmt->vars[i].name)) {
115  return &fmt->vars[i];
116  }
117  }
118  /* If none, check for an abbreviated match */
119  for (i=0; fmt->vars[i].name; ++i) {
120  if (!strncasecmp(key, fmt->vars[i].name, keylen)) {
121  log_warn(LD_CONFIG, "The abbreviation '%s' is deprecated. "
122  "Please use '%s' instead",
123  key, fmt->vars[i].name);
124  return &fmt->vars[i];
125  }
126  }
127  /* Okay, unrecognized option */
128  return NULL;
129 }
130 
136 const config_var_t *
137 config_find_option(const config_format_t *fmt, const char *key)
138 {
139  return config_find_option_mutable((config_format_t*)fmt, key);
140 }
141 
143 static int
145 {
146  int i;
147  for (i=0; fmt->vars[i].name; ++i)
148  ;
149  return i;
150 }
151 
152 /*
153  * Functions to assign config options.
154  */
155 
161 static int
162 config_assign_value(const config_format_t *fmt, void *options,
163  config_line_t *c, char **msg)
164 {
165  int i, ok;
166  const config_var_t *var;
167  void *lvalue;
168 
169  CONFIG_CHECK(fmt, options);
170 
171  var = config_find_option(fmt, c->key);
172  tor_assert(var);
173 
174  lvalue = STRUCT_VAR_P(options, var->var_offset);
175 
176  switch (var->type) {
177 
178  case CONFIG_TYPE_INT:
179  case CONFIG_TYPE_POSINT:
180  i = (int)tor_parse_long(c->value, 10,
181  var->type==CONFIG_TYPE_INT ? INT_MIN : 0,
182  INT_MAX,
183  &ok, NULL);
184  if (!ok) {
185  tor_asprintf(msg,
186  "Int keyword '%s %s' is malformed or out of bounds.",
187  c->key, c->value);
188  return -1;
189  }
190  *(int *)lvalue = i;
191  break;
192 
193  case CONFIG_TYPE_UINT64: {
194  uint64_t u64 = tor_parse_uint64(c->value, 10,
195  0, UINT64_MAX, &ok, NULL);
196  if (!ok) {
197  tor_asprintf(msg,
198  "uint64 keyword '%s %s' is malformed or out of bounds.",
199  c->key, c->value);
200  return -1;
201  }
202  *(uint64_t *)lvalue = u64;
203  break;
204  }
205 
207  /* We used to have entire smartlists here. But now that all of our
208  * download schedules use exponential backoff, only the first part
209  * matters. */
210  const char *comma = strchr(c->value, ',');
211  const char *val = c->value;
212  char *tmp = NULL;
213  if (comma) {
214  tmp = tor_strndup(c->value, comma - c->value);
215  val = tmp;
216  }
217 
218  i = config_parse_interval(val, &ok);
219  if (!ok) {
220  tor_asprintf(msg,
221  "Interval '%s %s' is malformed or out of bounds.",
222  c->key, c->value);
223  tor_free(tmp);
224  return -1;
225  }
226  *(int *)lvalue = i;
227  tor_free(tmp);
228  break;
229  }
230 
231  case CONFIG_TYPE_INTERVAL: {
232  i = config_parse_interval(c->value, &ok);
233  if (!ok) {
234  tor_asprintf(msg,
235  "Interval '%s %s' is malformed or out of bounds.",
236  c->key, c->value);
237  return -1;
238  }
239  *(int *)lvalue = i;
240  break;
241  }
242 
244  i = config_parse_msec_interval(c->value, &ok);
245  if (!ok) {
246  tor_asprintf(msg,
247  "Msec interval '%s %s' is malformed or out of bounds.",
248  c->key, c->value);
249  return -1;
250  }
251  *(int *)lvalue = i;
252  break;
253  }
254 
255  case CONFIG_TYPE_MEMUNIT: {
256  uint64_t u64 = config_parse_memunit(c->value, &ok);
257  if (!ok) {
258  tor_asprintf(msg,
259  "Value '%s %s' is malformed or out of bounds.",
260  c->key, c->value);
261  return -1;
262  }
263  *(uint64_t *)lvalue = u64;
264  break;
265  }
266 
267  case CONFIG_TYPE_BOOL:
268  i = (int)tor_parse_long(c->value, 10, 0, 1, &ok, NULL);
269  if (!ok) {
270  tor_asprintf(msg,
271  "Boolean '%s %s' expects 0 or 1.",
272  c->key, c->value);
273  return -1;
274  }
275  *(int *)lvalue = i;
276  break;
277 
279  if (!strcasecmp(c->value, "auto"))
280  *(int *)lvalue = -1;
281  else if (!strcmp(c->value, "0"))
282  *(int *)lvalue = 0;
283  else if (!strcmp(c->value, "1"))
284  *(int *)lvalue = 1;
285  else {
286  tor_asprintf(msg, "Boolean '%s %s' expects 0, 1, or 'auto'.",
287  c->key, c->value);
288  return -1;
289  }
290  break;
291 
292  case CONFIG_TYPE_STRING:
294  tor_free(*(char **)lvalue);
295  *(char **)lvalue = tor_strdup(c->value);
296  break;
297 
298  case CONFIG_TYPE_DOUBLE:
299  *(double *)lvalue = atof(c->value);
300  break;
301 
302  case CONFIG_TYPE_ISOTIME:
303  if (parse_iso_time(c->value, (time_t *)lvalue)) {
304  tor_asprintf(msg,
305  "Invalid time '%s' for keyword '%s'", c->value, c->key);
306  return -1;
307  }
308  break;
309 
311  if (*(routerset_t**)lvalue) {
312  routerset_free(*(routerset_t**)lvalue);
313  }
314  *(routerset_t**)lvalue = routerset_new();
315  if (routerset_parse(*(routerset_t**)lvalue, c->value, c->key)<0) {
316  tor_asprintf(msg, "Invalid exit list '%s' for option '%s'",
317  c->value, c->key);
318  return -1;
319  }
320  break;
321 
322  case CONFIG_TYPE_CSV:
323  if (*(smartlist_t**)lvalue) {
324  SMARTLIST_FOREACH(*(smartlist_t**)lvalue, char *, cp, tor_free(cp));
325  smartlist_clear(*(smartlist_t**)lvalue);
326  } else {
327  *(smartlist_t**)lvalue = smartlist_new();
328  }
329 
330  smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",",
331  SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
332  break;
333 
336  {
337  config_line_t *lastval = *(config_line_t**)lvalue;
338  if (lastval && lastval->fragile) {
339  if (c->command != CONFIG_LINE_APPEND) {
340  config_free_lines(lastval);
341  *(config_line_t**)lvalue = NULL;
342  } else {
343  lastval->fragile = 0;
344  }
345  }
346 
347  config_line_append((config_line_t**)lvalue, c->key, c->value);
348  }
349  break;
351  log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key);
352  break;
354  tor_asprintf(msg,
355  "You may not provide a value for virtual option '%s'", c->key);
356  return -1;
357  // LCOV_EXCL_START
358  default:
359  tor_assert_unreached();
360  break;
361  // LCOV_EXCL_STOP
362  }
363  return 0;
364 }
365 
368 static void
369 config_mark_lists_fragile(const config_format_t *fmt, void *options)
370 {
371  int i;
372  tor_assert(fmt);
373  tor_assert(options);
374 
375  for (i = 0; fmt->vars[i].name; ++i) {
376  const config_var_t *var = &fmt->vars[i];
378  if (var->type != CONFIG_TYPE_LINELIST &&
380  continue;
381 
382  list = *(config_line_t **)STRUCT_VAR_P(options, var->var_offset);
383  if (list)
384  list->fragile = 1;
385  }
386 }
387 
388 void
389 warn_deprecated_option(const char *what, const char *why)
390 {
391  const char *space = (why && strlen(why)) ? " " : "";
392  log_warn(LD_CONFIG, "The %s option is deprecated, and will most likely "
393  "be removed in a future version of Tor.%s%s (If you think this is "
394  "a mistake, please let us know!)",
395  what, space, why);
396 }
397 
407 static int
408 config_assign_line(const config_format_t *fmt, void *options,
409  config_line_t *c, unsigned flags,
410  bitarray_t *options_seen, char **msg)
411 {
412  const unsigned use_defaults = flags & CAL_USE_DEFAULTS;
413  const unsigned clear_first = flags & CAL_CLEAR_FIRST;
414  const unsigned warn_deprecations = flags & CAL_WARN_DEPRECATIONS;
415  const config_var_t *var;
416 
417  CONFIG_CHECK(fmt, options);
418 
419  var = config_find_option(fmt, c->key);
420  if (!var) {
421  if (fmt->extra) {
422  void *lvalue = STRUCT_VAR_P(options, fmt->extra->var_offset);
423  log_info(LD_CONFIG,
424  "Found unrecognized option '%s'; saving it.", c->key);
425  config_line_append((config_line_t**)lvalue, c->key, c->value);
426  return 0;
427  } else {
428  tor_asprintf(msg,
429  "Unknown option '%s'. Failing.", c->key);
430  return -1;
431  }
432  }
433 
434  /* Put keyword into canonical case. */
435  if (strcmp(var->name, c->key)) {
436  tor_free(c->key);
437  c->key = tor_strdup(var->name);
438  }
439 
440  const char *deprecation_msg;
441  if (warn_deprecations &&
442  (deprecation_msg = config_find_deprecation(fmt, var->name))) {
443  warn_deprecated_option(var->name, deprecation_msg);
444  }
445 
446  if (!strlen(c->value)) {
447  /* reset or clear it, then return */
448  if (!clear_first) {
449  if ((var->type == CONFIG_TYPE_LINELIST ||
450  var->type == CONFIG_TYPE_LINELIST_S) &&
451  c->command != CONFIG_LINE_CLEAR) {
452  /* We got an empty linelist from the torrc or command line.
453  As a special case, call this an error. Warn and ignore. */
454  log_warn(LD_CONFIG,
455  "Linelist option '%s' has no value. Skipping.", c->key);
456  } else { /* not already cleared */
457  config_reset(fmt, options, var, use_defaults);
458  }
459  }
460  return 0;
461  } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) {
462  // XXXX This is unreachable, since a CLEAR line always has an
463  // XXXX empty value.
464  config_reset(fmt, options, var, use_defaults); // LCOV_EXCL_LINE
465  }
466 
467  if (options_seen && (var->type != CONFIG_TYPE_LINELIST &&
468  var->type != CONFIG_TYPE_LINELIST_S)) {
469  /* We're tracking which options we've seen, and this option is not
470  * supposed to occur more than once. */
471  int var_index = (int)(var - fmt->vars);
472  if (bitarray_is_set(options_seen, var_index)) {
473  log_warn(LD_CONFIG, "Option '%s' used more than once; all but the last "
474  "value will be ignored.", var->name);
475  }
476  bitarray_set(options_seen, var_index);
477  }
478 
479  if (config_assign_value(fmt, options, c, msg) < 0)
480  return -2;
481  return 0;
482 }
483 
486 STATIC void
487 config_reset_line(const config_format_t *fmt, void *options,
488  const char *key, int use_defaults)
489 {
490  const config_var_t *var;
491 
492  CONFIG_CHECK(fmt, options);
493 
494  var = config_find_option(fmt, key);
495  if (!var)
496  return; /* give error on next pass. */
497 
498  config_reset(fmt, options, var, use_defaults);
499 }
500 
503 static int
504 config_value_needs_escape(const char *value)
505 {
506  if (*value == '\"')
507  return 1;
508  while (*value) {
509  switch (*value)
510  {
511  case '\r':
512  case '\n':
513  case '#':
514  /* Note: quotes and backspaces need special handling when we are using
515  * quotes, not otherwise, so they don't trigger escaping on their
516  * own. */
517  return 1;
518  default:
519  if (!TOR_ISPRINT(*value))
520  return 1;
521  }
522  ++value;
523  }
524  return 0;
525 }
526 
532 config_get_assigned_option(const config_format_t *fmt, const void *options,
533  const char *key, int escape_val)
534 {
535  const config_var_t *var;
536  const void *value;
537  config_line_t *result;
538  tor_assert(options && key);
539 
540  CONFIG_CHECK(fmt, options);
541 
542  var = config_find_option(fmt, key);
543  if (!var) {
544  log_warn(LD_CONFIG, "Unknown option '%s'. Failing.", key);
545  return NULL;
546  }
547  value = STRUCT_VAR_P(options, var->var_offset);
548 
549  result = tor_malloc_zero(sizeof(config_line_t));
550  result->key = tor_strdup(var->name);
551  switch (var->type)
552  {
553  case CONFIG_TYPE_STRING:
555  if (*(char**)value) {
556  result->value = tor_strdup(*(char**)value);
557  } else {
558  tor_free(result->key);
559  tor_free(result);
560  return NULL;
561  }
562  break;
563  case CONFIG_TYPE_ISOTIME:
564  if (*(time_t*)value) {
565  result->value = tor_malloc(ISO_TIME_LEN+1);
566  format_iso_time(result->value, *(time_t*)value);
567  } else {
568  tor_free(result->key);
569  tor_free(result);
570  }
571  escape_val = 0; /* Can't need escape. */
572  break;
576  case CONFIG_TYPE_POSINT:
577  case CONFIG_TYPE_INT:
578  /* This means every or_options_t uint or bool element
579  * needs to be an int. Not, say, a uint16_t or char. */
580  tor_asprintf(&result->value, "%d", *(int*)value);
581  escape_val = 0; /* Can't need escape. */
582  break;
583  case CONFIG_TYPE_UINT64: /* Fall through */
584  case CONFIG_TYPE_MEMUNIT:
585  tor_asprintf(&result->value, "%"PRIu64,
586  (*(uint64_t*)value));
587  escape_val = 0; /* Can't need escape. */
588  break;
589  case CONFIG_TYPE_DOUBLE:
590  tor_asprintf(&result->value, "%f", *(double*)value);
591  escape_val = 0; /* Can't need escape. */
592  break;
593 
595  if (*(int*)value == -1) {
596  result->value = tor_strdup("auto");
597  escape_val = 0;
598  break;
599  }
600  /* fall through */
601  case CONFIG_TYPE_BOOL:
602  result->value = tor_strdup(*(int*)value ? "1" : "0");
603  escape_val = 0; /* Can't need escape. */
604  break;
606  result->value = routerset_to_string(*(routerset_t**)value);
607  break;
608  case CONFIG_TYPE_CSV:
609  if (*(smartlist_t**)value)
610  result->value =
611  smartlist_join_strings(*(smartlist_t**)value, ",", 0, NULL);
612  else
613  result->value = tor_strdup("");
614  break;
617  "You asked me for the value of an obsolete config option '%s'.",
618  key);
619  tor_free(result->key);
620  tor_free(result);
621  return NULL;
623  tor_free(result->key);
624  tor_free(result);
625  result = config_lines_dup_and_filter(*(const config_line_t **)value,
626  key);
627  break;
630  tor_free(result->key);
631  tor_free(result);
632  result = config_lines_dup(*(const config_line_t**)value);
633  break;
634  // LCOV_EXCL_START
635  default:
636  tor_free(result->key);
637  tor_free(result);
638  log_warn(LD_BUG,"Unknown type %d for known key '%s'",
639  var->type, key);
640  return NULL;
641  // LCOV_EXCL_STOP
642  }
643 
644  if (escape_val) {
645  config_line_t *line;
646  for (line = result; line; line = line->next) {
647  if (line->value && config_value_needs_escape(line->value)) {
648  char *newval = esc_for_log(line->value);
649  tor_free(line->value);
650  line->value = newval;
651  }
652  }
653  }
654 
655  return result;
656 }
686 /*
687 There are three call cases for config_assign() currently.
688 
689 Case one: Torrc entry
690 options_init_from_torrc() calls config_assign(0, 0)
691  calls config_assign_line(0, 0).
692  if value is empty, calls config_reset(0) and returns.
693  calls config_assign_value(), appends.
694 
695 Case two: setconf
696 options_trial_assign() calls config_assign(0, 1)
697  calls config_reset_line(0)
698  calls config_reset(0)
699  calls option_clear().
700  calls config_assign_line(0, 1).
701  if value is empty, returns.
702  calls config_assign_value(), appends.
703 
704 Case three: resetconf
705 options_trial_assign() calls config_assign(1, 1)
706  calls config_reset_line(1)
707  calls config_reset(1)
708  calls option_clear().
709  calls config_assign_value(default)
710  calls config_assign_line(1, 1).
711  returns.
712 */
713 int
714 config_assign(const config_format_t *fmt, void *options, config_line_t *list,
715  unsigned config_assign_flags, char **msg)
716 {
717  config_line_t *p;
718  bitarray_t *options_seen;
719  const int n_options = config_count_options(fmt);
720  const unsigned clear_first = config_assign_flags & CAL_CLEAR_FIRST;
721  const unsigned use_defaults = config_assign_flags & CAL_USE_DEFAULTS;
722 
723  CONFIG_CHECK(fmt, options);
724 
725  /* pass 1: normalize keys */
726  for (p = list; p; p = p->next) {
727  const char *full = config_expand_abbrev(fmt, p->key, 0, 1);
728  if (strcmp(full,p->key)) {
729  tor_free(p->key);
730  p->key = tor_strdup(full);
731  }
732  }
733 
734  /* pass 2: if we're reading from a resetting source, clear all
735  * mentioned config options, and maybe set to their defaults. */
736  if (clear_first) {
737  for (p = list; p; p = p->next)
738  config_reset_line(fmt, options, p->key, use_defaults);
739  }
740 
741  options_seen = bitarray_init_zero(n_options);
742  /* pass 3: assign. */
743  while (list) {
744  int r;
745  if ((r=config_assign_line(fmt, options, list, config_assign_flags,
746  options_seen, msg))) {
747  bitarray_free(options_seen);
748  return r;
749  }
750  list = list->next;
751  }
752  bitarray_free(options_seen);
753 
757  config_mark_lists_fragile(fmt, options);
758 
759  return 0;
760 }
761 
764 static void
765 config_clear(const config_format_t *fmt, void *options,
766  const config_var_t *var)
767 {
768  void *lvalue = STRUCT_VAR_P(options, var->var_offset);
769  (void)fmt; /* unused */
770  switch (var->type) {
771  case CONFIG_TYPE_STRING:
773  tor_free(*(char**)lvalue);
774  break;
775  case CONFIG_TYPE_DOUBLE:
776  *(double*)lvalue = 0.0;
777  break;
778  case CONFIG_TYPE_ISOTIME:
779  *(time_t*)lvalue = 0;
780  break;
784  case CONFIG_TYPE_POSINT:
785  case CONFIG_TYPE_INT:
786  case CONFIG_TYPE_BOOL:
787  *(int*)lvalue = 0;
788  break;
790  *(int*)lvalue = -1;
791  break;
792  case CONFIG_TYPE_UINT64:
793  case CONFIG_TYPE_MEMUNIT:
794  *(uint64_t*)lvalue = 0;
795  break;
797  if (*(routerset_t**)lvalue) {
798  routerset_free(*(routerset_t**)lvalue);
799  *(routerset_t**)lvalue = NULL;
800  }
801  break;
802  case CONFIG_TYPE_CSV:
803  if (*(smartlist_t**)lvalue) {
804  SMARTLIST_FOREACH(*(smartlist_t **)lvalue, char *, cp, tor_free(cp));
805  smartlist_free(*(smartlist_t **)lvalue);
806  *(smartlist_t **)lvalue = NULL;
807  }
808  break;
811  config_free_lines(*(config_line_t **)lvalue);
812  *(config_line_t **)lvalue = NULL;
813  break;
815  /* handled by linelist_s. */
816  break;
818  break;
819  }
820 }
821 
825 static void
826 config_reset(const config_format_t *fmt, void *options,
827  const config_var_t *var, int use_defaults)
828 {
829  config_line_t *c;
830  char *msg = NULL;
831  CONFIG_CHECK(fmt, options);
832  config_clear(fmt, options, var); /* clear it first */
833  if (!use_defaults)
834  return; /* all done */
835  if (var->initvalue) {
836  c = tor_malloc_zero(sizeof(config_line_t));
837  c->key = tor_strdup(var->name);
838  c->value = tor_strdup(var->initvalue);
839  if (config_assign_value(fmt, options, c, &msg) < 0) {
840  // LCOV_EXCL_START
841  log_warn(LD_BUG, "Failed to assign default: %s", msg);
842  tor_free(msg); /* if this happens it's a bug */
843  // LCOV_EXCL_STOP
844  }
845  config_free_lines(c);
846  }
847 }
848 
850 void
851 config_free_(const config_format_t *fmt, void *options)
852 {
853  int i;
854 
855  if (!options)
856  return;
857 
858  tor_assert(fmt);
859 
860  for (i=0; fmt->vars[i].name; ++i)
861  config_clear(fmt, options, &(fmt->vars[i]));
862  if (fmt->extra) {
863  config_line_t **linep = STRUCT_VAR_P(options, fmt->extra->var_offset);
864  config_free_lines(*linep);
865  *linep = NULL;
866  }
867  tor_free(options);
868 }
869 
873 int
875  const void *o1, const void *o2,
876  const char *name)
877 {
878  config_line_t *c1, *c2;
879  int r = 1;
880  CONFIG_CHECK(fmt, o1);
881  CONFIG_CHECK(fmt, o2);
882 
883  c1 = config_get_assigned_option(fmt, o1, name, 0);
884  c2 = config_get_assigned_option(fmt, o2, name, 0);
885  r = config_lines_eq(c1, c2);
886  config_free_lines(c1);
887  config_free_lines(c2);
888  return r;
889 }
890 
892 void *
893 config_dup(const config_format_t *fmt, const void *old)
894 {
895  void *newopts;
896  int i;
897  config_line_t *line;
898 
899  newopts = config_new(fmt);
900  for (i=0; fmt->vars[i].name; ++i) {
901  if (fmt->vars[i].type == CONFIG_TYPE_LINELIST_S)
902  continue;
903  if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE)
904  continue;
905  line = config_get_assigned_option(fmt, old, fmt->vars[i].name, 0);
906  if (line) {
907  char *msg = NULL;
908  if (config_assign(fmt, newopts, line, 0, &msg) < 0) {
909  // LCOV_EXCL_START
910  log_err(LD_BUG, "config_get_assigned_option() generated "
911  "something we couldn't config_assign(): %s", msg);
912  tor_free(msg);
913  tor_assert(0);
914  // LCOV_EXCL_STOP
915  }
916  }
917  config_free_lines(line);
918  }
919  return newopts;
920 }
923 void
924 config_init(const config_format_t *fmt, void *options)
925 {
926  int i;
927  const config_var_t *var;
928  CONFIG_CHECK(fmt, options);
929 
930  for (i=0; fmt->vars[i].name; ++i) {
931  var = &fmt->vars[i];
932  if (!var->initvalue)
933  continue; /* defaults to NULL or 0 */
934  config_reset(fmt, options, var, 1);
935  }
936 }
937 
942 char *
943 config_dump(const config_format_t *fmt, const void *default_options,
944  const void *options, int minimal,
945  int comment_defaults)
946 {
947  smartlist_t *elements;
948  const void *defaults = default_options;
949  void *defaults_tmp = NULL;
950  config_line_t *line, *assigned;
951  char *result;
952  int i;
953  char *msg = NULL;
954 
955  if (defaults == NULL) {
956  defaults = defaults_tmp = config_new(fmt);
957  config_init(fmt, defaults_tmp);
958  }
959 
960  /* XXX use a 1 here so we don't add a new log line while dumping */
961  if (default_options == NULL) {
962  if (fmt->validate_fn(NULL, defaults_tmp, defaults_tmp, 1, &msg) < 0) {
963  // LCOV_EXCL_START
964  log_err(LD_BUG, "Failed to validate default config: %s", msg);
965  tor_free(msg);
966  tor_assert(0);
967  // LCOV_EXCL_STOP
968  }
969  }
970 
971  elements = smartlist_new();
972  for (i=0; fmt->vars[i].name; ++i) {
973  int comment_option = 0;
974  if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE ||
975  fmt->vars[i].type == CONFIG_TYPE_LINELIST_S)
976  continue;
977  /* Don't save 'hidden' control variables. */
978  if (!strcmpstart(fmt->vars[i].name, "__"))
979  continue;
980  if (minimal && config_is_same(fmt, options, defaults, fmt->vars[i].name))
981  continue;
982  else if (comment_defaults &&
983  config_is_same(fmt, options, defaults, fmt->vars[i].name))
984  comment_option = 1;
985 
986  line = assigned =
987  config_get_assigned_option(fmt, options, fmt->vars[i].name, 1);
988 
989  for (; line; line = line->next) {
990  if (!strcmpstart(line->key, "__")) {
991  /* This check detects "hidden" variables inside LINELIST_V structures.
992  */
993  continue;
994  }
995  smartlist_add_asprintf(elements, "%s%s %s\n",
996  comment_option ? "# " : "",
997  line->key, line->value);
998  }
999  config_free_lines(assigned);
1000  }
1001 
1002  if (fmt->extra) {
1003  line = *(config_line_t**)STRUCT_VAR_P(options, fmt->extra->var_offset);
1004  for (; line; line = line->next) {
1005  smartlist_add_asprintf(elements, "%s %s\n", line->key, line->value);
1006  }
1007  }
1008 
1009  result = smartlist_join_strings(elements, "", 0, NULL);
1010  SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
1011  smartlist_free(elements);
1012  if (defaults_tmp) {
1013  fmt->free_fn(defaults_tmp);
1014  }
1015  return result;
1016 }
1017 
1021  const char *unit;
1022  uint64_t multiplier;
1023 };
1024 
1027 static struct unit_table_t memory_units[] = {
1028  { "", 1 },
1029  { "b", 1<< 0 },
1030  { "byte", 1<< 0 },
1031  { "bytes", 1<< 0 },
1032  { "kb", 1<<10 },
1033  { "kbyte", 1<<10 },
1034  { "kbytes", 1<<10 },
1035  { "kilobyte", 1<<10 },
1036  { "kilobytes", 1<<10 },
1037  { "kilobits", 1<<7 },
1038  { "kilobit", 1<<7 },
1039  { "kbits", 1<<7 },
1040  { "kbit", 1<<7 },
1041  { "m", 1<<20 },
1042  { "mb", 1<<20 },
1043  { "mbyte", 1<<20 },
1044  { "mbytes", 1<<20 },
1045  { "megabyte", 1<<20 },
1046  { "megabytes", 1<<20 },
1047  { "megabits", 1<<17 },
1048  { "megabit", 1<<17 },
1049  { "mbits", 1<<17 },
1050  { "mbit", 1<<17 },
1051  { "gb", 1<<30 },
1052  { "gbyte", 1<<30 },
1053  { "gbytes", 1<<30 },
1054  { "gigabyte", 1<<30 },
1055  { "gigabytes", 1<<30 },
1056  { "gigabits", 1<<27 },
1057  { "gigabit", 1<<27 },
1058  { "gbits", 1<<27 },
1059  { "gbit", 1<<27 },
1060  { "tb", UINT64_C(1)<<40 },
1061  { "tbyte", UINT64_C(1)<<40 },
1062  { "tbytes", UINT64_C(1)<<40 },
1063  { "terabyte", UINT64_C(1)<<40 },
1064  { "terabytes", UINT64_C(1)<<40 },
1065  { "terabits", UINT64_C(1)<<37 },
1066  { "terabit", UINT64_C(1)<<37 },
1067  { "tbits", UINT64_C(1)<<37 },
1068  { "tbit", UINT64_C(1)<<37 },
1069  { NULL, 0 },
1070 };
1071 
1074 static struct unit_table_t time_units[] = {
1075  { "", 1 },
1076  { "second", 1 },
1077  { "seconds", 1 },
1078  { "minute", 60 },
1079  { "minutes", 60 },
1080  { "hour", 60*60 },
1081  { "hours", 60*60 },
1082  { "day", 24*60*60 },
1083  { "days", 24*60*60 },
1084  { "week", 7*24*60*60 },
1085  { "weeks", 7*24*60*60 },
1086  { "month", 2629728, }, /* about 30.437 days */
1087  { "months", 2629728, },
1088  { NULL, 0 },
1089 };
1090 
1093 static struct unit_table_t time_msec_units[] = {
1094  { "", 1 },
1095  { "msec", 1 },
1096  { "millisecond", 1 },
1097  { "milliseconds", 1 },
1098  { "second", 1000 },
1099  { "seconds", 1000 },
1100  { "minute", 60*1000 },
1101  { "minutes", 60*1000 },
1102  { "hour", 60*60*1000 },
1103  { "hours", 60*60*1000 },
1104  { "day", 24*60*60*1000 },
1105  { "days", 24*60*60*1000 },
1106  { "week", 7*24*60*60*1000 },
1107  { "weeks", 7*24*60*60*1000 },
1108  { NULL, 0 },
1109 };
1110 
1117 static uint64_t
1118 config_parse_units(const char *val, struct unit_table_t *u, int *ok)
1119 {
1120  uint64_t v = 0;
1121  double d = 0;
1122  int use_float = 0;
1123  char *cp;
1124 
1125  tor_assert(ok);
1126 
1127  v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
1128  if (!*ok || (cp && *cp == '.')) {
1129  d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp);
1130  if (!*ok)
1131  goto done;
1132  use_float = 1;
1133  }
1134 
1135  if (!cp) {
1136  *ok = 1;
1137  v = use_float ? ((uint64_t)d) : v;
1138  goto done;
1139  }
1140 
1141  cp = (char*) eat_whitespace(cp);
1142 
1143  for ( ;u->unit;++u) {
1144  if (!strcasecmp(u->unit, cp)) {
1145  if (use_float)
1146  v = (uint64_t)(u->multiplier * d);
1147  else
1148  v *= u->multiplier;
1149  *ok = 1;
1150  goto done;
1151  }
1152  }
1153  log_warn(LD_CONFIG, "Unknown unit '%s'.", cp);
1154  *ok = 0;
1155  done:
1156 
1157  if (*ok)
1158  return v;
1159  else
1160  return 0;
1161 }
1162 
1167 static uint64_t
1168 config_parse_memunit(const char *s, int *ok)
1169 {
1170  uint64_t u = config_parse_units(s, memory_units, ok);
1171  return u;
1172 }
1173 
1178 static int
1179 config_parse_msec_interval(const char *s, int *ok)
1180 {
1181  uint64_t r;
1182  r = config_parse_units(s, time_msec_units, ok);
1183  if (r > INT_MAX) {
1184  log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
1185  *ok = 0;
1186  return -1;
1187  }
1188  return (int)r;
1189 }
1190 
1195 static int
1196 config_parse_interval(const char *s, int *ok)
1197 {
1198  uint64_t r;
1199  r = config_parse_units(s, time_units, ok);
1200  if (r > INT_MAX) {
1201  log_warn(LD_CONFIG, "Interval '%s' is too long", s);
1202  *ok = 0;
1203  return -1;
1204  }
1205  return (int)r;
1206 }
off_t var_offset
Definition: confparse.h:103
void config_init(const config_format_t *fmt, void *options)
Definition: confparse.c:924
Header for confline.c.
static struct unit_table_t time_msec_units[]
Definition: confparse.c:1093
static uint64_t config_parse_memunit(const char *s, int *ok)
Definition: confparse.c:1168
static int config_parse_interval(const char *s, int *ok)
Definition: confparse.c:1196
unsigned int bitarray_t
Definition: bitarray.h:30
uint64_t tor_parse_uint64(const char *s, int base, uint64_t min, uint64_t max, int *ok, char **next)
Definition: parse_int.c:107
config_abbrev_t * abbrevs
Definition: confparse.h:167
off_t magic_offset
Definition: confparse.h:166
Implements a variable-sized (but non-resizeable) bit-array.
#define LOG_INFO
Definition: log.h:42
#define CONFIG_LINE_APPEND
Definition: confline.h:22
config_var_t * extra
Definition: confparse.h:176
const char * config_expand_abbrev(const config_format_t *fmt, const char *option, int command_line, int warn_obsolete)
Definition: confparse.c:58
int strcmpstart(const char *s1, const char *s2)
Definition: util_string.c:206
#define tor_free(p)
Definition: malloc.h:52
void ** list
int config_lines_eq(config_line_t *a, config_line_t *b)
Definition: confline.c:259
char * config_dump(const config_format_t *fmt, const void *default_options, const void *options, int minimal, int comment_defaults)
Definition: confparse.c:943
config_line_t * config_get_assigned_option(const config_format_t *fmt, const void *options, const char *key, int escape_val)
Definition: confparse.c:532
config_type_t type
Definition: confparse.h:101
static void bitarray_set(bitarray_t *b, int bit)
Definition: bitarray.h:68
const config_var_t * config_find_option(const config_format_t *fmt, const char *key)
Definition: confparse.c:137
char * routerset_to_string(const routerset_t *set)
Definition: routerset.c:410
tor_assert(buffer)
config_line_t * config_lines_dup_and_filter(const config_line_t *inp, const char *key)
Definition: confline.c:236
unsigned int command
Definition: confline.h:35
static int config_assign_line(const config_format_t *fmt, void *options, config_line_t *c, unsigned flags, bitarray_t *options_seen, char **msg)
Definition: confparse.c:408
static struct unit_table_t time_units[]
Definition: confparse.c:1074
static uint64_t config_parse_units(const char *val, struct unit_table_t *u, int *ok)
Definition: confparse.c:1118
int tor_asprintf(char **strp, const char *fmt,...)
Definition: printf.c:75
#define STRUCT_VAR_P(st, off)
void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern,...)
Definition: smartlist.c:36
Master header file for Tor-specific functionality.
free_cfg_fn_t free_fn
Definition: confparse.h:173
const char * eat_whitespace(const char *s)
Definition: util_string.c:268
config_var_t * config_find_option_mutable(config_format_t *fmt, const char *key)
Definition: confparse.c:106
const char * name
Definition: confparse.h:100
static bitarray_t * bitarray_init_zero(unsigned int n_bits)
Definition: bitarray.h:33
void * config_dup(const config_format_t *fmt, const void *old)
Definition: confparse.c:893
static int config_assign_value(const config_format_t *fmt, void *options, config_line_t *c, char **msg)
Definition: confparse.c:162
void config_free_(const config_format_t *fmt, void *options)
Definition: confparse.c:851
const char * initvalue
Definition: confparse.h:104
config_var_t * vars
Definition: confparse.h:170
int routerset_parse(routerset_t *target, const char *s, const char *description)
Definition: routerset.c:110
static struct unit_table_t memory_units[]
Definition: confparse.c:1027
unsigned int fragile
Definition: confline.h:39
config_line_t * config_lines_dup(const config_line_t *inp)
Definition: confline.c:227
static void config_reset(const config_format_t *fmt, void *options, const config_var_t *var, int use_defaults)
Definition: confparse.c:826
const char * config_find_deprecation(const config_format_t *fmt, const char *key)
Definition: confparse.c:88
void format_iso_time(char *buf, time_t t)
Definition: time_fmt.c:295
char * smartlist_join_strings(smartlist_t *sl, const char *join, int terminate, size_t *len_out)
Definition: smartlist.c:279
#define SMARTLIST_FOREACH(sl, type, var, cmd)
STATIC void config_reset_line(const config_format_t *fmt, void *options, const char *key, int use_defaults)
Definition: confparse.c:487
static void config_clear(const config_format_t *fmt, void *options, const config_var_t *var)
Definition: confparse.c:765
double tor_parse_double(const char *s, double min, double max, int *ok, char **next)
Definition: parse_int.c:94
const char * unit
Definition: confparse.c:1021
int config_is_same(const config_format_t *fmt, const void *o1, const void *o2, const char *name)
Definition: confparse.c:874
#define log_fn(severity, domain, args,...)
Definition: log.h:272
int config_assign(const config_format_t *fmt, void *options, config_line_t *list, unsigned config_assign_flags, char **msg)
Definition: confparse.c:714
#define CONFIG_CHECK(fmt, cfg)
Definition: confparse.h:181
uint32_t magic
Definition: confparse.h:164
validate_fn_t validate_fn
Definition: confparse.h:172
void config_line_append(config_line_t **lst, const char *key, const char *val)
Definition: confline.c:32
routerset_t * routerset_new(void)
Definition: routerset.c:47
long tor_parse_long(const char *s, int base, long min, long max, int *ok, char **next)
Definition: parse_int.c:56
char * esc_for_log(const char *s)
Definition: escape.c:30
static int config_count_options(const config_format_t *fmt)
Definition: confparse.c:144
int parse_iso_time(const char *cp, time_t *t)
Definition: time_fmt.c:392
static int config_parse_msec_interval(const char *s, int *ok)
Definition: confparse.c:1179
void smartlist_clear(smartlist_t *sl)
static int config_value_needs_escape(const char *value)
Definition: confparse.c:504
static unsigned int bitarray_is_set(bitarray_t *b, int bit)
Definition: bitarray.h:81
void * config_new(const config_format_t *fmt)
Definition: confparse.c:40
#define LD_BUG
Definition: log.h:83
#define LD_CONFIG
Definition: log.h:65
static void config_mark_lists_fragile(const config_format_t *fmt, void *options)
Definition: confparse.c:369
uint64_t multiplier
Definition: confparse.c:1022
Header for confparse.c.
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, int flags, int max)