Browse Source

Merge remote-tracking branch 'origin/maint-0.2.2'

Conflicts:
	src/common/Makefile.am
	src/or/control.c
Nick Mathewson 13 years ago
parent
commit
2527acb2dc

+ 5 - 0
changes/exit-policy-default-is-not-a-prefix

@@ -0,0 +1,5 @@
+  o Minor bugfixes:
+    - Remove a trailing asterisk from "exit-policy/default" in the
+      output of the control port command "GETINFO info/names".  Bugfix
+      on 0.1.2.5-alpha.
+

+ 6 - 0
changes/feature3049

@@ -0,0 +1,6 @@
+  o Major features:
+    - Add an __OwningControllerProcess configuration option and a
+      TAKEOWNERSHIP control-port command, so that a Tor controller can
+      ensure that when it exits, Tor will shut down.  Implements
+      feature 3049.
+

+ 2 - 0
src/common/Makefile.am

@@ -19,6 +19,7 @@ libor_a_SOURCES = \
   log.c						\
   memarea.c					\
   mempool.c					\
+  procmon.c					\
   util.c					\
   util_codedigest.c				\
   $(libor_extra_source)
@@ -43,6 +44,7 @@ noinst_HEADERS = 				\
   ht.h						\
   memarea.h					\
   mempool.h					\
+  procmon.h					\
   strlcat.c					\
   strlcpy.c					\
   torgzip.h					\

+ 1 - 1
src/common/compat.h

@@ -403,7 +403,7 @@ int get_n_open_sockets(void);
 #define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags)
 #define tor_socket_recv(s, buf, len, flags) recv(s, buf, len, flags)
 
-/** Implementatino of struct in6_addr for platforms that do not have it.
+/** Implementation of struct in6_addr for platforms that do not have it.
  * Generally, these platforms are ones without IPv6 support, but we want to
  * have a working in6_addr there anyway, so we can use it to parse IPv6
  * addresses. */

+ 1 - 1
src/common/compat_libevent.c

@@ -48,7 +48,7 @@ typedef uint32_t le_version_t;
  * it is. */
 #define LE_OLD V(0,0,0)
 /** Represents a version of libevent so weird we can't figure out what version
- * it it. */
+ * it is. */
 #define LE_OTHER V(0,0,99)
 
 static le_version_t tor_get_libevent_version(const char **v_out);

+ 1 - 1
src/common/log.c

@@ -409,7 +409,7 @@ logv(int severity, log_domain_mask_t domain, const char *funcname,
 
 /** Output a message to the log.  It gets logged to all logfiles that
  * care about messages with <b>severity</b> in <b>domain</b>. The content
- * is formatted printf style basedc on <b>format</b> and extra arguments.
+ * is formatted printf-style based on <b>format</b> and extra arguments.
  * */
 void
 tor_log(int severity, log_domain_mask_t domain, const char *format, ...)

+ 332 - 0
src/common/procmon.c

@@ -0,0 +1,332 @@
+
+/**
+ * \file procmon.c
+ * \brief Process-termination monitor functions
+ **/
+
+#include "procmon.h"
+
+#include "util.h"
+
+#ifdef HAVE_EVENT2_EVENT_H
+#include <event2/event.h>
+#else
+#include <event.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifdef MS_WINDOWS
+#include <windows.h>
+
+/* Windows does not define pid_t, but _getpid() returns an int. */
+typedef int pid_t;
+#endif
+
+/* Define to 1 if process-termination monitors on this OS and Libevent
+   version must poll for process termination themselves. */
+#define PROCMON_POLLS 1
+/* Currently we need to poll in some way on all systems. */
+
+#ifdef PROCMON_POLLS
+static void tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2,
+                                        void *procmon_);
+#endif
+
+/* This struct may contain pointers into the original process
+ * specifier string, but it should *never* contain anything which
+ * needs to be freed. */
+struct parsed_process_specifier_t {
+  pid_t pid;
+};
+
+/** Parse the process specifier given in <b>process_spec</b> into
+ * *<b>ppspec</b>.  Return 0 on success; return -1 and store an error
+ * message into *<b>msg</b> on failure.  The caller must not free the
+ * returned error message. */
+static int
+parse_process_specifier(const char *process_spec,
+                        struct parsed_process_specifier_t *ppspec,
+                        const char **msg)
+{
+  long pid_l;
+  int pid_ok = 0;
+  char *pspec_next;
+
+  /* If we're lucky, long will turn out to be large enough to hold a
+   * PID everywhere that Tor runs. */
+  pid_l = tor_parse_long(process_spec, 0, 1, LONG_MAX, &pid_ok, &pspec_next);
+
+  /* Reserve room in the ‘process specifier’ for additional
+   * (platform-specific) identifying information beyond the PID, to
+   * make our process-existence checks a bit less racy in a future
+   * version. */
+  if ((*pspec_next != 0) && (*pspec_next != ' ') && (*pspec_next != ':')) {
+    pid_ok = 0;
+  }
+
+  ppspec->pid = (pid_t)(pid_l);
+  if (!pid_ok || (pid_l != (long)(ppspec->pid))) {
+    *msg = "invalid PID";
+    goto err;
+  }
+
+  return 0;
+ err:
+  return -1;
+}
+
+struct tor_process_monitor_t {
+  /** Log domain for warning messages. */
+  log_domain_mask_t log_domain;
+
+  /** All systems: The best we can do in general is poll for the
+   * process's existence by PID periodically, and hope that the kernel
+   * doesn't reassign the same PID to another process between our
+   * polls. */
+  pid_t pid;
+
+#ifdef MS_WINDOWS
+  /** Windows-only: Should we poll hproc?  If false, poll pid
+   * instead. */
+  int poll_hproc;
+
+  /** Windows-only: Get a handle to the process (if possible) and
+   * periodically check whether the process we have a handle to has
+   * ended. */
+  HANDLE hproc;
+  /* XXX023 We can and should have Libevent watch hproc for us,
+   * if/when some version of Libevent 2.x can be told to do so. */
+#endif
+
+  /* XXX023 On Linux, we can and should receive the 22nd
+   * (space-delimited) field (‘starttime’) of /proc/$PID/stat from the
+   * owning controller and store it, and poll once in a while to see
+   * whether it has changed -- if so, the kernel has *definitely*
+   * reassigned the owning controller's PID and we should exit.  On
+   * FreeBSD, we can do the same trick using either the 8th
+   * space-delimited field of /proc/$PID/status on the seven FBSD
+   * systems whose admins have mounted procfs, or the start-time field
+   * of the process-information structure returned by kvmgetprocs() on
+   * any system.  The latter is ickier. */
+  /* XXX023 On FreeBSD (and possibly other kqueue systems), we can and
+   * should arrange to receive EVFILT_PROC NOTE_EXIT notifications for
+   * pid, so we don't have to do such a heavyweight poll operation in
+   * order to avoid the PID-reassignment race condition.  (We would
+   * still need to poll our own kqueue periodically until some version
+   * of Libevent 2.x learns to receive these events for us.) */
+
+  /** A Libevent event structure, to either poll for the process's
+   * existence or receive a notification when the process ends. */
+  struct event *e;
+
+  /** A callback to be called when the process ends. */
+  tor_procmon_callback_t cb;
+  void *cb_arg; /**< A user-specified pointer to be passed to cb. */
+};
+
+/** Verify that the process specifier given in <b>process_spec</b> is
+ * syntactically valid.  Return 0 on success; return -1 and store an
+ * error message into *<b>msg</b> on failure.  The caller must not
+ * free the returned error message. */
+int
+tor_validate_process_specifier(const char *process_spec,
+                               const char **msg)
+{
+  struct parsed_process_specifier_t ppspec;
+
+  tor_assert(msg != NULL);
+  *msg = NULL;
+
+  return parse_process_specifier(process_spec, &ppspec, msg);
+}
+
+#ifdef HAVE_EVENT2_EVENT_H
+#define PERIODIC_TIMER_FLAGS EV_PERSIST
+#else
+#define PERIODIC_TIMER_FLAGS (0)
+#endif
+
+static const struct timeval poll_interval_tv = {15, 0};
+
+/** Create a process-termination monitor for the process specifier
+ * given in <b>process_spec</b>.  Return a newly allocated
+ * tor_process_monitor_t on success; return NULL and store an error
+ * message into *<b>msg</b> on failure.  The caller must not free
+ * the returned error message.
+ *
+ * When the monitored process terminates, call
+ * <b>cb</b>(<b>cb_arg</b>).
+ */
+tor_process_monitor_t *
+tor_process_monitor_new(struct event_base *base,
+                        const char *process_spec,
+                        log_domain_mask_t log_domain,
+                        tor_procmon_callback_t cb, void *cb_arg,
+                        const char **msg)
+{
+  tor_process_monitor_t *procmon = tor_malloc(sizeof(tor_process_monitor_t));
+  struct parsed_process_specifier_t ppspec;
+
+  tor_assert(msg != NULL);
+  *msg = NULL;
+
+  if (procmon == NULL) {
+    *msg = "out of memory";
+    goto err;
+  }
+
+  procmon->log_domain = log_domain;
+
+  if (parse_process_specifier(process_spec, &ppspec, msg))
+    goto err;
+
+  procmon->pid = ppspec.pid;
+
+#ifdef MS_WINDOWS
+  procmon->hproc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
+                               FALSE,
+                               procmon->pid);
+
+  if (procmon->hproc != NULL) {
+    procmon->poll_hproc = 1;
+    log_info(procmon->log_domain, "Successfully opened handle to process %d; "
+             "monitoring it.",
+             (int)(procmon->pid));
+  } else {
+    /* If we couldn't get a handle to the process, we'll try again the
+     * first time we poll. */
+    log_info(procmon->log_domain, "Failed to open handle to process %d; will "
+             "try again later.",
+             (int)(procmon->pid));
+  }
+#endif
+
+  procmon->cb = cb;
+  procmon->cb_arg = cb_arg;
+
+#ifdef PROCMON_POLLS
+  procmon->e = tor_event_new(base, -1 /* no FD */, PERIODIC_TIMER_FLAGS,
+                             tor_process_monitor_poll_cb, procmon);
+  /* Note: If you port this file to plain Libevent 2, check that
+   * procmon->e is non-NULL.  We don't need to here because
+   * tor_evtimer_new never returns NULL. */
+
+  evtimer_add(procmon->e, &poll_interval_tv);
+#else
+#error OOPS?
+#endif
+
+  return procmon;
+ err:
+  tor_process_monitor_free(procmon);
+  return NULL;
+}
+
+#ifdef PROCMON_POLLS
+/** Libevent callback to poll for the existence of the process
+ * monitored by <b>procmon_</b>. */
+static void
+tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2,
+                            void *procmon_)
+{
+  tor_process_monitor_t *procmon = (tor_process_monitor_t *)(procmon_);
+  int its_dead_jim;
+
+  (void)unused1; (void)unused2;
+
+  tor_assert(procmon != NULL);
+
+#ifdef MS_WINDOWS
+  if (procmon->poll_hproc) {
+    DWORD exit_code;
+    if (!GetExitCodeProcess(procmon->hproc, &exit_code)) {
+      char *errmsg = format_win32_error(GetLastError());
+      log_warn(procmon->log_domain, "Error \"%s\" occurred while polling "
+               "handle for monitored process %d; assuming it's dead."
+               errmsg, procmon->pid);
+      tor_free(errmsg);
+      its_dead_jim = 1;
+    } else {
+      its_dead_jim = (exit_code != STILL_ACTIVE);
+    }
+  } else {
+    /* All we can do is try to open the process, and look at the error
+     * code if it fails again. */
+    procmon->hproc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
+                                 FALSE,
+                                 procmon->pid);
+
+    if (procmon->hproc != NULL) {
+      log_info(procmon->log_domain, "Successfully opened handle to monitored "
+               "process %d.",
+               procmon->pid);
+      its_dead_jim = 0;
+      procmon->poll_hproc = 1;
+    } else {
+      DWORD err_code = GetLastError();
+      char *errmsg = format_win32_error(err_code);
+
+      /* When I tested OpenProcess's error codes on Windows 7, I
+       * received error code 5 (ERROR_ACCESS_DENIED) for PIDs of
+       * existing processes that I could not open and error code 87
+       * (ERROR_INVALID_PARAMETER) for PIDs that were not in use.
+       * Since the nonexistent-process error code is sane, I'm going
+       * to assume that all errors other than ERROR_INVALID_PARAMETER
+       * mean that the process we are monitoring is still alive. */
+      its_dead_jim = (err_code == ERROR_INVALID_PARAMETER);
+
+      if (!its_dead_jim)
+        log_info(procmon->log_domain, "Failed to open handle to monitored "
+                 "process %d, and error code %d (%s) is not 'invalid "
+                 "parameter' -- assuming the process is still alive.",
+                 procmon->pid,
+                 err_code, err_msg);
+
+      tor_free(err_msg);
+    }
+  }
+#else
+  /* Unix makes this part easy, if a bit racy. */
+  its_dead_jim = kill(procmon->pid, 0);
+  its_dead_jim = its_dead_jim && (errno == ESRCH);
+#endif
+
+  log(its_dead_jim ? LOG_NOTICE : LOG_INFO,
+      procmon->log_domain, "Monitored process %d is %s.",
+      (int)procmon->pid,
+      its_dead_jim ? "dead" : "still alive");
+
+  if (its_dead_jim) {
+    procmon->cb(procmon->cb_arg);
+#ifndef HAVE_EVENT2_EVENT_H
+  } else {
+    evtimer_add(procmon->e, &poll_interval_tv);
+#endif
+  }
+}
+#endif
+
+/** Free the process-termination monitor <b>procmon</b>. */
+void
+tor_process_monitor_free(tor_process_monitor_t *procmon)
+{
+  if (procmon == NULL)
+    return;
+
+#ifdef MS_WINDOWS
+  if (procmon->hproc != NULL)
+    CloseHandle(procmon->hproc);
+#endif
+
+  if (procmon->e != NULL)
+    tor_event_free(procmon->e);
+
+  tor_free(procmon);
+}
+

+ 30 - 0
src/common/procmon.h

@@ -0,0 +1,30 @@
+
+/**
+ * \file procmon.h
+ * \brief Headers for procmon.c
+ **/
+
+#ifndef TOR_PROCMON_H
+#define TOR_PROCMON_H
+
+#include "compat.h"
+#include "compat_libevent.h"
+
+#include "torlog.h"
+
+typedef struct tor_process_monitor_t tor_process_monitor_t;
+
+typedef void (*tor_procmon_callback_t)(void *);
+
+int tor_validate_process_specifier(const char *process_spec,
+                                   const char **msg);
+tor_process_monitor_t *tor_process_monitor_new(struct event_base *base,
+                                               const char *process_spec,
+                                               log_domain_mask_t log_domain,
+                                               tor_procmon_callback_t cb,
+                                               void *cb_arg,
+                                               const char **msg);
+void tor_process_monitor_free(tor_process_monitor_t *procmon);
+
+#endif
+

+ 0 - 1
src/common/torlog.h

@@ -149,7 +149,6 @@ void flush_pending_log_callbacks(void);
 void log_set_application_name(const char *name);
 void set_log_time_granularity(int granularity_msec);
 
-/* Outputs a message to stdout */
 void tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
   CHECK_PRINTF(3,4);
 #define log tor_log /* hack it so we don't conflict with log() as much */

+ 11 - 7
src/common/util.c

@@ -776,13 +776,17 @@ tor_digest256_is_zero(const char *digest)
   if (next) *next = endptr;                             \
   return 0
 
-/** Extract a long from the start of s, in the given numeric base.  If
- * there is unconverted data and next is provided, set *next to the
- * first unconverted character.  An error has occurred if no characters
- * are converted; or if there are unconverted characters and next is NULL; or
- * if the parsed value is not between min and max.  When no error occurs,
- * return the parsed value and set *ok (if provided) to 1.  When an error
- * occurs, return 0 and set *ok (if provided) to 0.
+/** Extract a long from the start of <b>s</b>, in the given numeric
+ * <b>base</b>.  If <b>base</b> is 0, <b>s</b> is parsed as a decimal,
+ * octal, or hex number in the syntax of a C integer literal.  If
+ * there is unconverted data and <b>next</b> is provided, set
+ * *<b>next</b> to the first unconverted character.  An error has
+ * occurred if no characters are converted; or if there are
+ * unconverted characters and <b>next</b> is NULL; or if the parsed
+ * value is not between <b>min</b> and <b>max</b>.  When no error
+ * occurs, return the parsed value and set *<b>ok</b> (if provided) to
+ * 1.  When an error occurs, return 0 and set *<b>ok</b> (if provided)
+ * to 0.
  */
 long
 tor_parse_long(const char *s, int base, long min, long max,

+ 15 - 0
src/or/config.c

@@ -38,6 +38,8 @@
 #include <shlobj.h>
 #endif
 
+#include "procmon.h"
+
 /** Enumeration of types which option values can take */
 typedef enum config_type_t {
   CONFIG_TYPE_STRING = 0,   /**< An arbitrary string. */
@@ -411,6 +413,7 @@ static config_var_t _option_vars[] = {
   VAR("__LeaveStreamsUnattached",BOOL,  LeaveStreamsUnattached,   "0"),
   VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword,
       NULL),
+  VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL),
   V(MinUptimeHidServDirectoryV2, INTERVAL, "24 hours"),
   V(_UsingTestNetworkDefaults,   BOOL,     "0"),
 
@@ -1263,6 +1266,8 @@ options_act(or_options_t *old_options)
     return -1;
   }
 
+  monitor_owning_controller_process(options->OwningControllerProcess);
+
   /* reload keys as needed for rendezvous services. */
   if (rend_service_load_keys()<0) {
     log_warn(LD_GENERAL,"Error loading rendezvous service keys");
@@ -3563,6 +3568,16 @@ options_validate(or_options_t *old_options, or_options_t *options,
     }
   }
 
+  if (options->OwningControllerProcess) {
+    const char *validate_pspec_msg = NULL;
+    if (tor_validate_process_specifier(options->OwningControllerProcess,
+                                       &validate_pspec_msg)) {
+      tor_asprintf(msg, "Bad OwningControllerProcess: %s",
+                   validate_pspec_msg);
+      return -1;
+    }
+  }
+
   if (options->ControlListenAddress) {
     int all_are_local = 1;
     config_line_t *ln;

+ 1 - 2
src/or/connection.c

@@ -523,8 +523,7 @@ connection_free(connection_t *conn)
     }
   }
   if (conn->type == CONN_TYPE_CONTROL) {
-    TO_CONTROL_CONN(conn)->event_mask = 0;
-    control_update_global_event_mask();
+    connection_control_closed(TO_CONTROL_CONN(conn));
   }
   connection_unregister_events(conn);
   _connection_free(conn);

+ 136 - 2
src/or/control.c

@@ -38,6 +38,8 @@
 #include <sys/resource.h>
 #endif
 
+#include "procmon.h"
+
 /** Yield true iff <b>s</b> is the state of a control_connection_t that has
  * finished authentication and is accepting commands. */
 #define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN)
@@ -1284,6 +1286,26 @@ handle_control_signal(control_connection_t *conn, uint32_t len,
   return 0;
 }
 
+/** Called when we get a TAKEOWNERSHIP command.  Mark this connection
+ * as an owning connection, so that we will exit if the connection
+ * closes. */
+static int
+handle_control_takeownership(control_connection_t *conn, uint32_t len,
+                             const char *body)
+{
+  (void)len;
+  (void)body;
+
+  conn->is_owning_control_connection = 1;
+
+  log_info(LD_CONTROL, "Control connection %d has taken ownership of this "
+           "Tor instance.",
+           (int)(conn->_base.s));
+
+  send_control_done(conn);
+  return 0;
+}
+
 /** Called when we get a MAPADDRESS command; try to bind all listed addresses,
  * and report success or failure. */
 static int
@@ -2084,8 +2106,8 @@ static const getinfo_item_t getinfo_items[] = {
          "v2 networkstatus docs as retrieved from a DirPort."),
   ITEM("dir/status-vote/current/consensus", dir,
        "v3 Networkstatus consensus as retrieved from a DirPort."),
-  PREFIX("exit-policy/default", policies,
-         "The default value appended to the configured exit policy."),
+  ITEM("exit-policy/default", policies,
+       "The default value appended to the configured exit policy."),
   PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"),
   { NULL, NULL, NULL, 0 }
 };
@@ -2919,6 +2941,43 @@ connection_control_reached_eof(control_connection_t *conn)
   return 0;
 }
 
+/** Shut down this Tor instance in the same way that SIGINT would, but
+ * with a log message appropriate for the loss of an owning controller. */
+static void
+lost_owning_controller(const char *owner_type, const char *loss_manner)
+{
+  int shutdown_slowly = server_mode(get_options());
+
+  log_notice(LD_CONTROL, "Owning controller %s has %s -- %s.",
+             owner_type, loss_manner,
+             shutdown_slowly ? "shutting down" : "exiting now");
+
+  /* XXXX Perhaps this chunk of code should be a separate function,
+   * called here and by process_signal(SIGINT). */
+
+  if (!shutdown_slowly) {
+    tor_cleanup();
+    exit(0);
+  }
+  /* XXXX This will close all listening sockets except control-port
+   * listeners.  Perhaps we should close those too. */
+  hibernate_begin_shutdown();
+}
+
+/** Called when <b>conn</b> is being freed. */
+void
+connection_control_closed(control_connection_t *conn)
+{
+  tor_assert(conn);
+
+  conn->event_mask = 0;
+  control_update_global_event_mask();
+
+  if (conn->is_owning_control_connection) {
+    lost_owning_controller("connection", "closed");
+  }
+}
+
 /** Return true iff <b>cmd</b> is allowable (or at least forgivable) at this
  * stage of the protocol. */
 static int
@@ -3083,6 +3142,9 @@ connection_control_process_inbuf(control_connection_t *conn)
     return 0;
   }
 
+  /* XXXX Why is this not implemented as a table like the GETINFO
+   * items are?  Even handling the plus signs at the beginnings of
+   * commands wouldn't be very hard with proper macros. */
   cmd_data_len = (uint32_t)data_len;
   if (!strcasecmp(conn->incoming_cmd, "SETCONF")) {
     if (handle_control_setconf(conn, cmd_data_len, args))
@@ -3108,6 +3170,9 @@ connection_control_process_inbuf(control_connection_t *conn)
   } else if (!strcasecmp(conn->incoming_cmd, "SIGNAL")) {
     if (handle_control_signal(conn, cmd_data_len, args))
       return -1;
+  } else if (!strcasecmp(conn->incoming_cmd, "TAKEOWNERSHIP")) {
+    if (handle_control_takeownership(conn, cmd_data_len, args))
+      return -1;
   } else if (!strcasecmp(conn->incoming_cmd, "MAPADDRESS")) {
     if (handle_control_mapaddress(conn, cmd_data_len, args))
       return -1;
@@ -4006,6 +4071,75 @@ init_cookie_authentication(int enabled)
   return 0;
 }
 
+/** A copy of the process specifier of Tor's owning controller, or
+ * NULL if this Tor instance is not currently owned by a process. */
+static char *owning_controller_process_spec = NULL;
+
+/** A process-termination monitor for Tor's owning controller, or NULL
+ * if this Tor instance is not currently owned by a process. */
+static tor_process_monitor_t *owning_controller_process_monitor = NULL;
+
+/** Process-termination monitor callback for Tor's owning controller
+ * process. */
+static void
+owning_controller_procmon_cb(void *unused)
+{
+  (void)unused;
+
+  lost_owning_controller("process", "vanished");
+}
+
+/** Set <b>process_spec</b> as Tor's owning controller process.
+ * Exit on failure. */
+void
+monitor_owning_controller_process(const char *process_spec)
+{
+  const char *msg;
+
+  tor_assert((owning_controller_process_spec == NULL) ==
+             (owning_controller_process_monitor == NULL));
+
+  if (owning_controller_process_spec != NULL) {
+    if ((process_spec != NULL) && !strcmp(process_spec,
+                                          owning_controller_process_spec)) {
+      /* Same process -- return now, instead of disposing of and
+       * recreating the process-termination monitor. */
+      return;
+    }
+
+    /* We are currently owned by a process, and we should no longer be
+     * owned by it.  Free the process-termination monitor. */
+    tor_process_monitor_free(owning_controller_process_monitor);
+    owning_controller_process_monitor = NULL;
+
+    tor_free(owning_controller_process_spec);
+    owning_controller_process_spec = NULL;
+  }
+
+  tor_assert((owning_controller_process_spec == NULL) &&
+             (owning_controller_process_monitor == NULL));
+
+  if (process_spec == NULL)
+    return;
+
+  owning_controller_process_spec = tor_strdup(process_spec);
+  owning_controller_process_monitor =
+    tor_process_monitor_new(tor_libevent_get_base(),
+                            owning_controller_process_spec,
+                            LD_CONTROL,
+                            owning_controller_procmon_cb, NULL,
+                            &msg);
+
+  if (owning_controller_process_monitor == NULL) {
+    log_err(LD_BUG, "Couldn't create process-termination monitor for "
+            "owning controller: %s.  Exiting.",
+            msg);
+    owning_controller_process_spec = NULL;
+    tor_cleanup();
+    exit(0);
+  }
+}
+
 /** Convert the name of a bootstrapping phase <b>s</b> into strings
  * <b>tag</b> and <b>summary</b> suitable for display by the controller. */
 static int

+ 4 - 0
src/or/control.h

@@ -27,6 +27,8 @@ void control_ports_write_to_file(void);
 
 int connection_control_finished_flushing(control_connection_t *conn);
 int connection_control_reached_eof(control_connection_t *conn);
+void connection_control_closed(control_connection_t *conn);
+
 int connection_control_process_inbuf(control_connection_t *conn);
 
 #define EVENT_AUTHDIR_NEWDESCS 0x000D
@@ -73,6 +75,8 @@ smartlist_t *decode_hashed_passwords(config_line_t *passwords);
 void disable_control_logging(void);
 void enable_control_logging(void);
 
+void monitor_owning_controller_process(const char *process_spec);
+
 void control_event_bootstrap(bootstrap_status_t status, int progress);
 void control_event_bootstrap_problem(const char *warn, int reason);
 

+ 9 - 1
src/or/or.h

@@ -1027,7 +1027,7 @@ typedef struct connection_t {
   /* XXXX023 move this field, and all the listener-only fields (just
      socket_family, I think), into a new listener_connection_t subtype. */
   /** If the connection is a CONN_TYPE_AP_DNS_LISTENER, this field points
-   * to the evdns_server_port is uses to listen to and answer connections. */
+   * to the evdns_server_port it uses to listen to and answer connections. */
   struct evdns_server_port *dns_server_port;
 
   /** Unique ID for measuring tunneled network status requests. */
@@ -1271,6 +1271,9 @@ typedef struct control_connection_t {
 
   /** True if we have sent a protocolinfo reply on this connection. */
   unsigned int have_sent_protocolinfo:1;
+  /** True if we have received a takeownership command on this
+   * connection. */
+  unsigned int is_owning_control_connection:1;
 
   /** Amount of space allocated in incoming_cmd. */
   uint32_t incoming_cmd_len;
@@ -2842,6 +2845,11 @@ typedef struct {
   int DisablePredictedCircuits; /**< Boolean: does Tor preemptively
                                  * make circuits in the background (0),
                                  * or not (1)? */
+
+  /** Process specifier for a controller that ‘owns’ this Tor
+   * instance.  Tor will terminate if its owning controller does. */
+  char *OwningControllerProcess;
+
   int ShutdownWaitLength; /**< When we get a SIGINT and we're a server, how
                            * long do we wait before exiting? */
   char *SafeLogging; /**< Contains "relay", "1", "0" (meaning no scrubbing). */

+ 4 - 3
src/or/router.c

@@ -492,8 +492,8 @@ init_keys(void)
   char fingerprint_line[MAX_NICKNAME_LEN+FINGERPRINT_LEN+3];
   const char *mydesc;
   crypto_pk_env_t *prkey;
-  char digest[20];
-  char v3_digest[20];
+  char digest[DIGEST_LEN];
+  char v3_digest[DIGEST_LEN];
   char *cp;
   or_options_t *options = get_options();
   dirinfo_type_t type;
@@ -505,7 +505,8 @@ init_keys(void)
   if (!key_lock)
     key_lock = tor_mutex_new();
 
-  /* There are a couple of paths that put us here before */
+  /* There are a couple of paths that put us here before we've asked
+   * openssl to initialize itself. */
   if (crypto_global_init(get_options()->HardwareAccel,
                          get_options()->AccelName,
                          get_options()->AccelDir)) {