diff --git a/doc/TODO b/doc/TODO
index 7147a8ab47..cc74630274 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -53,7 +53,10 @@ ARMA - arma claims
o stop calling a *_policy an exit_policy_t
- Regenerate our server descriptor when a relevant option is changed from
control.c.
- - Writing out the machine-readable torrc file
+ . Writing out the machine-readable torrc file
+ o Function to check whether an option has changed.
+ o Function to generate the contents for a torrc file.
+ - Function to safely replace a torrc file.
- fix print_usage()
- Download and use running-routers
- document signals in man page
diff --git a/src/or/config.c b/src/or/config.c
index dadaadbad9..dc1816a0a9 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -160,6 +160,8 @@ static config_var_t config_vars[] = {
static void option_reset(or_options_t *options, config_var_t *var);
static void options_free(or_options_t *options);
+static int option_is_same(or_options_t *o1, or_options_t *o2,
+ config_var_t *var);
static or_options_t *options_dup(or_options_t *old);
static int options_validate(or_options_t *options);
static int options_transition_allowed(or_options_t *old, or_options_t *new);
@@ -311,6 +313,18 @@ options_act(void) {
return -1;
}
+#if 0
+ {
+ char *smin, *smax;
+ smin = config_dump_options(options, 1);
+ smax = config_dump_options(options, 0);
+ log_fn(LOG_DEBUG, "These are our options:\n%s",smax);
+ log_fn(LOG_DEBUG, "We changed these options:\n%s",smin);
+ tor_free(smin);
+ tor_free(smax);
+ }
+#endif
+
return 0;
}
@@ -938,6 +952,71 @@ options_free(or_options_t *options)
}
}
+/** Return true iff the option var has the same value in o1
+ * and o2. Must not be called for LINELIST_S or OBSOLETE options.
+ */
+static int
+option_is_same(or_options_t *o1, or_options_t *o2, config_var_t *var)
+{
+ void *v1, *v2;
+ tor_assert(o1);
+ tor_assert(o2);
+ tor_assert(var);
+
+ v1 = ((char*)o1) + var-> var_offset;
+ v2 = ((char*)o2) + var-> var_offset;
+ switch (var->type)
+ {
+ case CONFIG_TYPE_UINT:
+ case CONFIG_TYPE_BOOL:
+ return (*(int*)v1) == (*(int*)v2);
+ case CONFIG_TYPE_STRING: {
+ const char *s1 = *(const char**)v1;
+ const char *s2 = *(const char**)v2;
+ return (!s1 && !s2) || (s1 && s2 && !strcmp(s1, s2));
+ }
+ case CONFIG_TYPE_DOUBLE:
+ return (*(double*)v1) == (*(double*)v2);
+ case CONFIG_TYPE_CSV: {
+ smartlist_t *sl1 = *(smartlist_t**)v1;
+ smartlist_t *sl2 = *(smartlist_t**)v2;
+ int i;
+ if ((sl1 && !sl2) || (!sl1 && sl2))
+ return 0;
+ else if (!sl1 && !sl2)
+ return 1;
+ else if (smartlist_len(sl1) != smartlist_len(sl2))
+ return 0;
+ for (i=0;ikey,cl2->key) || strcmp(cl1->value,cl2->value))
+ return 0;
+ cl1 = cl1->next;
+ cl2 = cl2->next;
+ }
+ if (!cl1 && !cl2)
+ return 1;
+ else
+ return 0;
+ }
+ case CONFIG_TYPE_LINELIST_S:
+ case CONFIG_TYPE_OBSOLETE:
+ default:
+ log_fn(LOG_ERR,"Internal error: can't compare configuration option '%s'",
+ var->name);
+ tor_assert(0);
+ }
+}
+
/** Copy storage held by old into a new or_options_t and return it. */
static or_options_t *
options_dup(or_options_t *old)
@@ -981,6 +1060,50 @@ options_init(or_options_t *options)
}
}
+/** Return a string containing a possible configuration file that would give
+ * the configuration in options. If minimal is true, do not
+ * include options that are the same as Tor's defaults.
+ */
+char *
+config_dump_options(or_options_t *options, int minimal)
+{
+ smartlist_t *elements;
+ or_options_t *defaults;
+ struct config_line_t *line;
+ char *result;
+ int i;
+
+ defaults = tor_malloc_zero(sizeof(or_options_t));
+ options_init(defaults);
+ options_validate(defaults); /* ??? will this work? */
+
+ elements = smartlist_create();
+ for (i=0; config_vars[i].name; ++i) {
+ if (config_vars[i].type == CONFIG_TYPE_OBSOLETE ||
+ config_vars[i].type == CONFIG_TYPE_LINELIST_S)
+ continue;
+ if (minimal && option_is_same(options, defaults, &config_vars[i]))
+ continue;
+ line = config_get_assigned_option(options, config_vars[i].name);
+ for (; line; line = line->next) {
+ size_t len = strlen(line->key) + strlen(line->value) + 3;
+ char *tmp;
+ tmp = tor_malloc(len);
+ if (tor_snprintf(tmp, len, "%s %s\n", line->key, line->value)<0) {
+ log_fn(LOG_ERR, "Internal error writing log option");
+ tor_assert(0);
+ }
+ smartlist_add(elements, tmp);
+ }
+ config_free_lines(line);
+ }
+
+ result = smartlist_join_strings(elements, "", 0, NULL);
+ SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
+ smartlist_free(elements);
+ return result;
+}
+
/** Return 0 if every setting in options is reasonable. Else
* warn and return -1. Should have no side effects, except for
* normalizing the contents of options. */
diff --git a/src/or/or.h b/src/or/or.h
index dcd38e8ccc..35a4879211 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1116,6 +1116,7 @@ struct config_line_t *config_get_assigned_option(or_options_t *options,
const char *key);
struct config_line_t *config_line_prepend(struct config_line_t *front,
const char *key, const char *val);
+char *config_dump_options(or_options_t *options, int minimal);
/********************************* connection.c ***************************/