From 02fd6c05b8c847fd0077b14e12ab3da417f8e757 Mon Sep 17 00:00:00 2001
From: Damian Johnson <atagar@torproject.org>
Date: Sat, 18 Dec 2010 15:19:03 -0800
Subject: [PATCH] Adding a CONF_CHANGED event type
 Emits CONF_CHANGED events whenever Tor's configuration values change.

---
 doc/spec/control-spec.txt |   19 ++++++++++++++++++-
 src/or/config.c           |   35 +++++++++++++++++++++++++++++++++++
 src/or/control.c          |   19 +++++++++++++++++--
 src/or/control.h          |    1 +
 4 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/doc/spec/control-spec.txt b/doc/spec/control-spec.txt
index e8a314c..7ce2142 100644
--- a/doc/spec/control-spec.txt
+++ b/doc/spec/control-spec.txt
@@ -231,7 +231,8 @@
          "INFO" / "NOTICE" / "WARN" / "ERR" / "NEWDESC" / "ADDRMAP" /
          "AUTHDIR_NEWDESCS" / "DESCCHANGED" / "STATUS_GENERAL" /
          "STATUS_CLIENT" / "STATUS_SERVER" / "GUARD" / "NS" / "STREAM_BW" /
-         "CLIENTS_SEEN" / "NEWCONSENSUS" / "BUILDTIMEOUT_SET" / "SIGNAL"
+         "CLIENTS_SEEN" / "NEWCONSENSUS" / "BUILDTIMEOUT_SET" / "SIGNAL" /
+         "CONF_CHANGE"
 
   Any events *not* listed in the SETEVENTS line are turned off; thus, sending
   SETEVENTS with an empty body turns off all event reporting.
@@ -1759,6 +1760,22 @@
 
   [First added in 0.2.3.1-alpha]
 
+4.1.17. Configuration value changed
+
+  The syntax is:
+     "650" SP "CONF_CHANGE" SP AttributeList CRLF
+
+  Tor configuration options have changed (such as via a SETCONF or RELOAD
+  signal). The AttributeList is a newline separated list of space separated
+  key/value pairs. Undefined values just contain the attributes (without a
+  space). For instance:
+  650 CONF_CHANGED ControlListenAddress\nFetchDirInfoEarly 0
+
+  indicates that a change has occured making the ControlListenAddress
+  undefined and FetchDirInfoEarly false.
+
+  [First added in XXX]
+
 5. Implementation notes
 
 5.1. Authentication
diff --git a/src/or/config.c b/src/or/config.c
index 0d1fee5..e72fa93 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -649,6 +649,10 @@ get_options(void)
 int
 set_options(or_options_t *new_val, char **msg)
 {
+  int i;
+  char *result;
+  smartlist_t *elements;
+  config_line_t *line;
   or_options_t *old_options = global_options;
   global_options = new_val;
   /* Note that we pass the *old* options below, for comparison. It
@@ -664,6 +668,37 @@ set_options(or_options_t *new_val, char **msg)
     exit(1);
   }
 
+  /* Issues a CONF_CHANGED event to notify controller of the change. If Tor is
+   * just starting up then the old_options will be undefined. */
+  if (old_options) {
+    elements = smartlist_create();
+
+    for (i=0; options_format.vars[i].name; ++i) {
+      if (!option_is_same(&options_format, new_val, old_options,
+        options_format.vars[i].name)) {
+        line = get_assigned_option(&options_format, new_val,
+        options_format.vars[i].name, 1);
+
+        if (line) {
+          for (; line; line = line->next) {
+            char *tmp;
+            tor_asprintf(&tmp, "%s %s", line->key, line->value);
+            smartlist_add(elements, tmp);
+          }
+        } else {
+          char *tmp;
+          tor_asprintf(&tmp, "%s", options_format.vars[i].name);
+          smartlist_add(elements, tmp);
+        }
+      }
+    }
+
+    result = smartlist_join_strings(elements, "\n", 0, NULL);
+    control_event_conf_changed(result);
+    smartlist_free(elements);
+    tor_free(result);
+  }
+
   config_free(&options_format, old_options);
 
   return 0;
diff --git a/src/or/control.c b/src/or/control.c
index 5e2bcc7..d27189a 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -66,7 +66,8 @@
 #define EVENT_NEWCONSENSUS     0x0016
 #define EVENT_BUILDTIMEOUT_SET     0x0017
 #define EVENT_SIGNAL           0x0018
-#define _EVENT_MAX             0x0018
+#define EVENT_CONF_CHANGED     0x0019
+#define _EVENT_MAX             0x0019
 /* If _EVENT_MAX ever hits 0x0020, we need to make the mask wider. */
 
 /** Bitfield: The bit 1&lt;&lt;e is set if <b>any</b> open control
@@ -937,6 +938,8 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
         event_code = EVENT_STATUS_CLIENT;
       else if (!strcasecmp(ev, "STATUS_SERVER"))
         event_code = EVENT_STATUS_SERVER;
+      else if (!strcasecmp(ev, "CONF_CHANGED"))
+        event_code = EVENT_CONF_CHANGED;
       else if (!strcasecmp(ev, "GUARD"))
         event_code = EVENT_GUARD;
       else if (!strcasecmp(ev, "STREAM_BW"))
@@ -1335,7 +1338,8 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
     *answer = tor_strdup("CIRC STREAM ORCONN BW DEBUG INFO NOTICE WARN ERR "
                          "NEWDESC ADDRMAP AUTHDIR_NEWDESCS DESCCHANGED "
                          "NS STATUS_GENERAL STATUS_CLIENT STATUS_SERVER "
-                         "GUARD STREAM_BW CLIENTS_SEEN NEWCONSENSUS");
+                         "GUARD STREAM_BW CLIENTS_SEEN NEWCONSENSUS "
+                         "CONF_CHANGED");
   } else if (!strcmp(question, "features/names")) {
     *answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS");
   } else if (!strcmp(question, "address")) {
@@ -3778,6 +3782,17 @@ control_event_guard(const char *nickname, const char *digest,
   return 0;
 }
 
+/** Called when a configuration attribute changes. This is generally triggered
+ * by SETCONF requests and RELOAD/SIGHUP signals. The <b>values</b> are the
+ * attribute/value pairs for the configuration changes tor is using. */
+int
+control_event_conf_changed(const char *values)
+{
+  send_control_event(EVENT_CONF_CHANGED, 0,
+    "650 CONF_CHANGED %s\r\n", values);
+  return 0;
+}
+
 /** Helper: Return a newly allocated string containing a path to the
  * file where we store our authentication cookie. */
 static char *
diff --git a/src/or/control.h b/src/or/control.h
index ef91f06..f897104 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -62,6 +62,7 @@ int control_event_server_status(int severity, const char *format, ...)
   CHECK_PRINTF(2,3);
 int control_event_guard(const char *nickname, const char *digest,
                         const char *status);
+int control_event_conf_changed(const char *values);
 int control_event_buildtimeout_set(const circuit_build_times_t *cbt,
                                    buildtimeout_set_event_t type);
 int control_event_signal(uintptr_t signal);
-- 
1.6.3.3

