[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] [tor/master] Merge remote branch 'origin/maint-0.2.2'



commit cc9809c1f79344f2419195ce1b01d210da8faa67
Merge: f25fc6e d43470a
Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date:   Tue Feb 8 14:37:31 2011 -0500

    Merge remote branch 'origin/maint-0.2.2'

 changes/bug2470 |    5 +++++
 src/or/config.c |    8 +++++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --combined src/or/config.c
index 087049c,eae92b2..aadcbec
--- a/src/or/config.c
+++ b/src/or/config.c
@@@ -44,8 -44,6 +44,8 @@@ typedef enum config_type_t 
    CONFIG_TYPE_FILENAME,     /**< A filename: some prefixes get expanded. */
    CONFIG_TYPE_UINT,         /**< A non-negative integer less than MAX_INT */
    CONFIG_TYPE_INTERVAL,     /**< A number of seconds, with optional units*/
 +  CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional
 +                              * units */
    CONFIG_TYPE_MEMUNIT,      /**< A number of bytes, with optional units*/
    CONFIG_TYPE_DOUBLE,       /**< A floating-point value */
    CONFIG_TYPE_BOOL,         /**< A boolean value, expressed as 0 or 1. */
@@@ -201,7 -199,6 +201,7 @@@ static config_var_t _option_vars[] = 
    V(ClientOnly,                  BOOL,     "0"),
    V(ConsensusParams,             STRING,   NULL),
    V(ConnLimit,                   UINT,     "1000"),
 +  V(ConnDirectionStatistics,     BOOL,     "0"),
    V(ConstrainedSockets,          BOOL,     "0"),
    V(ConstrainedSockSize,         MEMUNIT,  "8192"),
    V(ContactInfo,                 STRING,   NULL),
@@@ -225,10 -222,9 +225,10 @@@
    OBSOLETE("DirRecordUsageGranularity"),
    OBSOLETE("DirRecordUsageRetainIPs"),
    OBSOLETE("DirRecordUsageSaveInterval"),
 -  V(DirReqStatistics,            BOOL,     "0"),
 +  V(DirReqStatistics,            BOOL,     "1"),
    VAR("DirServer",               LINELIST, DirServers, NULL),
    V(DisableAllSwap,              BOOL,     "0"),
 +  V(DisableIOCP,                 BOOL,     "1"),
    V(DNSPort,                     UINT,     "0"),
    V(DNSListenAddress,            LINELIST, NULL),
    V(DownloadExtraInfo,           BOOL,     "0"),
@@@ -243,7 -239,7 +243,7 @@@
    V(ExitPolicy,                  LINELIST, NULL),
    V(ExitPolicyRejectPrivate,     BOOL,     "1"),
    V(ExitPortStatistics,          BOOL,     "0"),
 -  V(ExtraInfoStatistics,         BOOL,     "0"),
 +  V(ExtraInfoStatistics,         BOOL,     "1"),
  
  #if defined (WINCE)
    V(FallbackNetworkstatusFile,   FILENAME, "fallback-consensus"),
@@@ -295,7 -291,6 +295,7 @@@
    OBSOLETE("LinkPadding"),
    OBSOLETE("LogLevel"),
    OBSOLETE("LogFile"),
 +  V(LogTimeGranularity,          MSEC_INTERVAL, "1 second"),
    V(LongLivedPorts,              CSV,
                           "21,22,706,1863,5050,5190,5222,5223,6667,6697,8300"),
    VAR("MapAddress",              LINELIST, AddressMap,           NULL),
@@@ -312,7 -307,7 +312,7 @@@
    V(WarnUnsafeSocks,              BOOL,     "1"),
    OBSOLETE("NoPublish"),
    VAR("NodeFamily",              LINELIST, NodeFamilies,         NULL),
 -  V(NumCPUs,                     UINT,     "1"),
 +  V(NumCPUs,                     UINT,     "0"),
    V(NumEntryGuards,              UINT,     "3"),
    V(ORListenAddress,             LINELIST, NULL),
    V(ORPort,                      UINT,     "0"),
@@@ -322,8 -317,6 +322,8 @@@
    V(PerConnBWRate,               MEMUNIT,  "0"),
    V(PidFile,                     STRING,   NULL),
    V(TestingTorNetwork,           BOOL,     "0"),
 +  V(PortForwarding,              BOOL,     "0"),
 +  V(PortForwardingHelper,        FILENAME, "tor-fw-helper"),
    V(PreferTunneledDirConns,      BOOL,     "1"),
    V(ProtocolWarnings,            BOOL,     "0"),
    V(PublishServerDescriptor,     CSV,      "1"),
@@@ -392,7 -385,6 +392,7 @@@
    VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
    V(VirtualAddrNetwork,          STRING,   "127.192.0.0/10"),
    V(WarnPlaintextPorts,          CSV,      "23,109,110,143"),
 +  V(_UseFilteringSSLBufferevents, BOOL,    "0"),
    VAR("__ReloadTorrcOnSIGHUP",   BOOL,  ReloadTorrcOnSIGHUP,      "1"),
    VAR("__AllDirActionsPrivate",  BOOL,  AllDirActionsPrivate,     "0"),
    VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"),
@@@ -565,9 -557,8 +565,9 @@@ static int is_listening_on_low_port(uin
                                      const config_line_t *listen_options);
  
  static uint64_t config_parse_memunit(const char *s, int *ok);
 +static int config_parse_msec_interval(const char *s, int *ok);
  static int config_parse_interval(const char *s, int *ok);
 -static void init_libevent(void);
 +static void init_libevent(const or_options_t *options);
  static int opt_streq(const char *s1, const char *s2);
  
  /** Magic value for or_options_t. */
@@@ -701,11 -692,6 +701,11 @@@ or_options_free(or_options_t *options
      return;
  
    routerset_free(options->_ExcludeExitNodesUnion);
 +  if (options->NodeFamilySets) {
 +    SMARTLIST_FOREACH(options->NodeFamilySets, routerset_t *,
 +                      rs, routerset_free(rs));
 +    smartlist_free(options->NodeFamilySets);
 +  }
    config_free(&options_format, options);
  }
  
@@@ -973,7 -959,7 +973,7 @@@ options_act_reversible(or_options_t *ol
      /* Set up libevent.  (We need to do this before we can register the
       * listeners as listeners.) */
      if (running_tor && !libevent_initialized) {
 -      init_libevent();
 +      init_libevent(options);
        libevent_initialized = 1;
      }
  
@@@ -1249,17 -1235,6 +1249,17 @@@ options_act(or_options_t *old_options
    if (accounting_is_enabled(options))
      configure_accounting(time(NULL));
  
 +#ifdef USE_BUFFEREVENTS
 +  /* If we're using the bufferevents implementation and our rate limits
 +   * changed, we need to tell the rate-limiting system about it. */
 +  if (!old_options ||
 +      old_options->BandwidthRate != options->BandwidthRate ||
 +      old_options->BandwidthBurst != options->BandwidthBurst ||
 +      old_options->RelayBandwidthRate != options->RelayBandwidthRate ||
 +      old_options->RelayBandwidthBurst != options->RelayBandwidthBurst)
 +    connection_bucket_init();
 +#endif
 +
    /* parse RefuseUnknownExits tristate */
    if (!strcmp(options->RefuseUnknownExits, "0"))
      options->RefuseUnknownExits_ = 0;
@@@ -1371,52 -1346,44 +1371,52 @@@
      tor_free(actual_fname);
    }
  
 -  if (options->DirReqStatistics && !geoip_is_loaded()) {
 -    /* Check if GeoIP database could be loaded. */
 -    log_warn(LD_CONFIG, "Configured to measure directory request "
 -             "statistics, but no GeoIP database found!");
 -    return -1;
 -  }
 -
 -  if (options->EntryStatistics) {
 -    if (should_record_bridge_info(options)) {
 -      /* Don't allow measuring statistics on entry guards when configured
 -       * as bridge. */
 -      log_warn(LD_CONFIG, "Bridges cannot be configured to measure "
 -               "additional GeoIP statistics as entry guards.");
 -      return -1;
 -    } else if (!geoip_is_loaded()) {
 -      /* Check if GeoIP database could be loaded. */
 -      log_warn(LD_CONFIG, "Configured to measure entry node statistics, "
 -               "but no GeoIP database found!");
 -      return -1;
 -    }
 -  }
 -
    if (options->CellStatistics || options->DirReqStatistics ||
 -      options->EntryStatistics || options->ExitPortStatistics) {
 +      options->EntryStatistics || options->ExitPortStatistics ||
 +      options->ConnDirectionStatistics) {
      time_t now = time(NULL);
 +    int print_notice = 0;
      if ((!old_options || !old_options->CellStatistics) &&
 -        options->CellStatistics)
 +        options->CellStatistics) {
        rep_hist_buffer_stats_init(now);
 +      print_notice = 1;
 +    }
      if ((!old_options || !old_options->DirReqStatistics) &&
 -        options->DirReqStatistics)
 -      geoip_dirreq_stats_init(now);
 +        options->DirReqStatistics) {
 +      if (geoip_is_loaded()) {
 +        geoip_dirreq_stats_init(now);
 +        print_notice = 1;
 +      } else {
 +        options->DirReqStatistics = 0;
 +        log_notice(LD_CONFIG, "Configured to measure directory request "
 +                              "statistics, but no GeoIP database found! "
 +                              "Please specify a GeoIP database using the "
 +                              "GeoIPFile option!");
 +      }
 +    }
      if ((!old_options || !old_options->EntryStatistics) &&
 -        options->EntryStatistics)
 -      geoip_entry_stats_init(now);
 +        options->EntryStatistics && !should_record_bridge_info(options)) {
 +      if (geoip_is_loaded()) {
 +        geoip_entry_stats_init(now);
 +        print_notice = 1;
 +      } else {
 +        options->EntryStatistics = 0;
 +        log_notice(LD_CONFIG, "Configured to measure entry node "
 +                              "statistics, but no GeoIP database found! "
 +                              "Please specify a GeoIP database using the "
 +                              "GeoIPFile option!");
 +      }
 +    }
      if ((!old_options || !old_options->ExitPortStatistics) &&
 -        options->ExitPortStatistics)
 +        options->ExitPortStatistics) {
        rep_hist_exit_stats_init(now);
 -    if (!old_options)
 +      print_notice = 1;
 +    }
 +    if ((!old_options || !old_options->ConnDirectionStatistics) &&
 +        options->ConnDirectionStatistics) {
 +      rep_hist_conn_stats_init(now);
 +    }
 +    if (print_notice)
        log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
                   "the *-stats files that will first be written to the "
                   "data directory in 24 hours from now.");
@@@ -1434,9 -1401,6 +1434,9 @@@
    if (old_options && old_options->ExitPortStatistics &&
        !options->ExitPortStatistics)
      rep_hist_exit_stats_term();
 +  if (old_options && old_options->ConnDirectionStatistics &&
 +      !options->ConnDirectionStatistics)
 +    rep_hist_conn_stats_term();
  
    /* Check if we need to parse and add the EntryNodes config option. */
    if (options->EntryNodes &&
@@@ -1742,18 -1706,6 +1742,18 @@@ config_assign_value(config_format_t *fm
      break;
    }
  
 +  case CONFIG_TYPE_MSEC_INTERVAL: {
 +    i = config_parse_msec_interval(c->value, &ok);
 +    if (!ok) {
 +      tor_asprintf(msg,
 +          "Msec interval '%s %s' is malformed or out of bounds.",
 +          c->key, c->value);
 +      return -1;
 +    }
 +    *(int *)lvalue = i;
 +    break;
 +  }
 +
    case CONFIG_TYPE_MEMUNIT: {
      uint64_t u64 = config_parse_memunit(c->value, &ok);
      if (!ok) {
@@@ -2041,7 -1993,6 +2041,7 @@@ get_assigned_option(config_format_t *fm
        escape_val = 0; /* Can't need escape. */
        break;
      case CONFIG_TYPE_INTERVAL:
 +    case CONFIG_TYPE_MSEC_INTERVAL:
      case CONFIG_TYPE_UINT:
        /* This means every or_options_t uint or bool element
         * needs to be an int. Not, say, a uint16_t or char. */
@@@ -2269,7 -2220,6 +2269,7 @@@ option_clear(config_format_t *fmt, or_o
        *(time_t*)lvalue = 0;
        break;
      case CONFIG_TYPE_INTERVAL:
 +    case CONFIG_TYPE_MSEC_INTERVAL:
      case CONFIG_TYPE_UINT:
      case CONFIG_TYPE_BOOL:
        *(int*)lvalue = 0;
@@@ -2376,7 -2326,7 +2376,7 @@@ resolve_my_address(int warn_severity, o
    int explicit_ip=1;
    int explicit_hostname=1;
    int from_interface=0;
 -  char tmpbuf[INET_NTOA_BUF_LEN];
 +  char *addr_string = NULL;
    const char *address = options->Address;
    int notice_severity = warn_severity <= LOG_NOTICE ?
                            LOG_NOTICE : warn_severity;
@@@ -2418,43 -2368,48 +2418,43 @@@
          return -1;
        }
        from_interface = 1;
 -      in.s_addr = htonl(interface_ip);
 -      tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
 +      addr = interface_ip;
        log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for "
 -             "local interface. Using that.", tmpbuf);
 +             "local interface. Using that.", fmt_addr32(addr));
        strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
      } else { /* resolved hostname into addr */
 -      in.s_addr = htonl(addr);
 -
        if (!explicit_hostname &&
 -          is_internal_IP(ntohl(in.s_addr), 0)) {
 +          is_internal_IP(addr, 0)) {
          uint32_t interface_ip;
  
 -        tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
          log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' "
 -               "resolves to a private IP address (%s).  Trying something "
 -               "else.", hostname, tmpbuf);
 +               "resolves to a private IP address (%s). Trying something "
 +               "else.", hostname, fmt_addr32(addr));
  
          if (get_interface_address(warn_severity, &interface_ip)) {
            log_fn(warn_severity, LD_CONFIG,
                   "Could not get local interface IP address. Too bad.");
          } else if (is_internal_IP(interface_ip, 0)) {
 -          struct in_addr in2;
 -          in2.s_addr = htonl(interface_ip);
 -          tor_inet_ntoa(&in2,tmpbuf,sizeof(tmpbuf));
            log_fn(notice_severity, LD_CONFIG,
                   "Interface IP address '%s' is a private address too. "
 -                 "Ignoring.", tmpbuf);
 +                 "Ignoring.", fmt_addr32(interface_ip));
          } else {
            from_interface = 1;
 -          in.s_addr = htonl(interface_ip);
 -          tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
 +          addr = interface_ip;
            log_fn(notice_severity, LD_CONFIG,
                   "Learned IP address '%s' for local interface."
 -                 " Using that.", tmpbuf);
 +                 " Using that.", fmt_addr32(addr));
            strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
          }
        }
      }
 +  } else {
 +    addr = ntohl(in.s_addr); /* set addr so that addr_string is not
 +                              * illformed */
    }
  
 -  tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
 -  if (is_internal_IP(ntohl(in.s_addr), 0)) {
 +  addr_string = tor_dup_ip(addr);
 +  if (is_internal_IP(addr, 0)) {
      /* make sure we're ok with publishing an internal IP */
      if (!options->DirServers && !options->AlternateDirAuthority) {
        /* if they are using the default dirservers, disallow internal IPs
@@@ -2462,8 -2417,7 +2462,8 @@@
        log_fn(warn_severity, LD_CONFIG,
               "Address '%s' resolves to private IP address '%s'. "
               "Tor servers that use the default DirServers must have public "
 -             "IP addresses.", hostname, tmpbuf);
 +             "IP addresses.", hostname, addr_string);
 +      tor_free(addr_string);
        return -1;
      }
      if (!explicit_ip) {
@@@ -2471,20 -2425,19 +2471,20 @@@
         * they're using an internal address. */
        log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private "
               "IP address '%s'. Please set the Address config option to be "
 -             "the IP address you want to use.", hostname, tmpbuf);
 +             "the IP address you want to use.", hostname, addr_string);
 +      tor_free(addr_string);
        return -1;
      }
    }
  
 -  log_debug(LD_CONFIG, "Resolved Address to '%s'.", tmpbuf);
 -  *addr_out = ntohl(in.s_addr);
 +  log_debug(LD_CONFIG, "Resolved Address to '%s'.", fmt_addr32(addr));
 +  *addr_out = addr;
    if (last_resolved_addr && last_resolved_addr != *addr_out) {
      /* Leave this as a notice, regardless of the requested severity,
       * at least until dynamic IP address support becomes bulletproof. */
      log_notice(LD_NET,
                 "Your IP address seems to have changed to %s. Updating.",
 -               tmpbuf);
 +               addr_string);
      ip_address_changed(0);
    }
    if (last_resolved_addr != *addr_out) {
@@@ -2503,12 -2456,11 +2503,12 @@@
      }
      control_event_server_status(LOG_NOTICE,
                                  "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s %s%s",
 -                                tmpbuf, method, h?"HOSTNAME=":"", h);
 +                                addr_string, method, h?"HOSTNAME=":"", h);
    }
    last_resolved_addr = *addr_out;
    if (hostname_out)
      *hostname_out = tor_strdup(hostname);
 +  tor_free(addr_string);
    return 0;
  }
  
@@@ -3113,24 -3065,17 +3113,24 @@@ options_validate(or_options_t *old_opti
      routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeNodes);
    }
  
 +  if (options->NodeFamilies) {
 +    options->NodeFamilySets = smartlist_create();
 +    for (cl = options->NodeFamilies; cl; cl = cl->next) {
 +      routerset_t *rs = routerset_new();
 +      if (routerset_parse(rs, cl->value, cl->key) == 0) {
 +        smartlist_add(options->NodeFamilySets, rs);
 +      } else {
 +        routerset_free(rs);
 +      }
 +    }
 +  }
 +
    if (options->ExcludeNodes && options->StrictNodes) {
      COMPLAIN("You have asked to exclude certain relays from all positions "
               "in your circuits. Expect hidden services and other Tor "
               "features to be broken in unpredictable ways.");
    }
  
 -  if (options->EntryNodes && !routerset_is_list(options->EntryNodes)) {
 -    /* XXXX fix this; see entry_guards_prepend_from_config(). */
 -    REJECT("IPs or countries are not yet supported in EntryNodes.");
 -  }
 -
    if (options->AuthoritativeDir) {
      if (!options->ContactInfo && !options->TestingTorNetwork)
        REJECT("Authoritative directory servers must set ContactInfo");
@@@ -3400,6 -3345,11 +3400,11 @@@
                             "PerConnBWBurst", msg) < 0)
      return -1;
  
+   if (options->RelayBandwidthRate && !options->RelayBandwidthBurst)
+     options->RelayBandwidthBurst = options->RelayBandwidthRate;
+   if (options->RelayBandwidthBurst && !options->RelayBandwidthRate)
+     options->RelayBandwidthRate = options->RelayBandwidthBurst;
+ 
    if (server_mode(options)) {
      if (options->BandwidthRate < ROUTER_REQUIRED_MIN_BANDWIDTH) {
        tor_asprintf(msg,
@@@ -3428,9 -3378,6 +3433,6 @@@
      }
    }
  
-   if (options->RelayBandwidthRate && !options->RelayBandwidthBurst)
-     options->RelayBandwidthBurst = options->RelayBandwidthRate;
- 
    if (options->RelayBandwidthRate > options->RelayBandwidthBurst)
      REJECT("RelayBandwidthBurst must be at least equal "
             "to RelayBandwidthRate.");
@@@ -3591,12 -3538,8 +3593,12 @@@
    if (check_nickname_list(options->MyFamily, "MyFamily", msg))
      return -1;
    for (cl = options->NodeFamilies; cl; cl = cl->next) {
 -    if (check_nickname_list(cl->value, "NodeFamily", msg))
 +    routerset_t *rs = routerset_new();
 +    if (routerset_parse(rs, cl->value, cl->key)) {
 +      routerset_free(rs);
        return -1;
 +    }
 +    routerset_free(rs);
    }
  
    if (validate_addr_policies(options, msg) < 0)
@@@ -4377,35 -4320,6 +4379,35 @@@ options_init_logs(or_options_t *options
                 options->RunAsDaemon;
  #endif
  
 +  if (options->LogTimeGranularity <= 0) {
 +    log_warn(LD_CONFIG, "Log time granularity '%d' has to be positive.",
 +             options->LogTimeGranularity);
 +    return -1;
 +  } else if (1000 % options->LogTimeGranularity != 0 &&
 +             options->LogTimeGranularity % 1000 != 0) {
 +    int granularity = options->LogTimeGranularity;
 +    if (granularity < 40) {
 +      do granularity++;
 +      while (1000 % granularity != 0);
 +    } else if (granularity < 1000) {
 +      granularity = 1000 / granularity;
 +      while (1000 % granularity != 0)
 +        granularity--;
 +      granularity = 1000 / granularity;
 +    } else {
 +      granularity = 1000 * ((granularity / 1000) + 1);
 +    }
 +    log_warn(LD_CONFIG, "Log time granularity '%d' has to be either a "
 +                        "divisor or a multiple of 1 second. Changing to "
 +                        "'%d'.",
 +             options->LogTimeGranularity, granularity);
 +    if (!validate_only)
 +      set_log_time_granularity(granularity);
 +  } else {
 +    if (!validate_only)
 +      set_log_time_granularity(options->LogTimeGranularity);
 +  }
 +
    ok = 1;
    elts = smartlist_create();
  
@@@ -4894,26 -4808,6 +4896,26 @@@ static struct unit_table_t time_units[
    { NULL, 0 },
  };
  
 +/** Table to map the names of time units to the number of milliseconds
 + * they contain. */
 +static struct unit_table_t time_msec_units[] = {
 +  { "",         1 },
 +  { "msec",     1 },
 +  { "millisecond", 1 },
 +  { "milliseconds", 1 },
 +  { "second",   1000 },
 +  { "seconds",  1000 },
 +  { "minute",   60*1000 },
 +  { "minutes",  60*1000 },
 +  { "hour",     60*60*1000 },
 +  { "hours",    60*60*1000 },
 +  { "day",      24*60*60*1000 },
 +  { "days",     24*60*60*1000 },
 +  { "week",     7*24*60*60*1000 },
 +  { "weeks",    7*24*60*60*1000 },
 +  { NULL, 0 },
 +};
 +
  /** Parse a string <b>val</b> containing a number, zero or more
   * spaces, and an optional unit string.  If the unit appears in the
   * table <b>u</b>, then multiply the number by the unit multiplier.
@@@ -4977,25 -4871,6 +4979,25 @@@ config_parse_memunit(const char *s, in
    return u;
  }
  
 +/** Parse a string in the format "number unit", where unit is a unit of
 + * time in milliseconds.  On success, set *<b>ok</b> to true and return
 + * the number of milliseconds in the provided interval.  Otherwise, set
 + * *<b>ok</b> to 0 and return -1. */
 +static int
 +config_parse_msec_interval(const char *s, int *ok)
 +{
 +  uint64_t r;
 +  r = config_parse_units(s, time_msec_units, ok);
 +  if (!ok)
 +    return -1;
 +  if (r > INT_MAX) {
 +    log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
 +    *ok = 0;
 +    return -1;
 +  }
 +  return (int)r;
 +}
 +
  /** Parse a string in the format "number unit", where unit is a unit of time.
   * On success, set *<b>ok</b> to true and return the number of seconds in
   * the provided interval.  Otherwise, set *<b>ok</b> to 0 and return -1.
@@@ -5015,29 -4890,13 +5017,29 @@@ config_parse_interval(const char *s, in
    return (int)r;
  }
  
 +/** Return the number of cpus configured in <b>options</b>.  If we are
 + * told to auto-detect the number of cpus, return the auto-detected number. */
 +int
 +get_num_cpus(const or_options_t *options)
 +{
 +  if (options->NumCPUs == 0) {
 +    int n = compute_num_cpus();
 +    return (n >= 1) ? n : 1;
 +  } else {
 +    return options->NumCPUs;
 +  }
 +}
 +
  /**
   * Initialize the libevent library.
   */
  static void
 -init_libevent(void)
 +init_libevent(const or_options_t *options)
  {
    const char *badness=NULL;
 +  tor_libevent_cfg cfg;
 +
 +  tor_assert(options);
  
    configure_libevent_logging();
    /* If the kernel complains that some method (say, epoll) doesn't
@@@ -5047,11 -4906,7 +5049,11 @@@
  
    tor_check_libevent_header_compatibility();
  
 -  tor_libevent_initialize();
 +  memset(&cfg, 0, sizeof(cfg));
 +  cfg.disable_iocp = options->DisableIOCP;
 +  cfg.num_cpus = get_num_cpus(options);
 +
 +  tor_libevent_initialize(&cfg);
  
    suppress_libevent_log_msg(NULL);
  
@@@ -5392,7 -5247,6 +5394,7 @@@ getinfo_helper_config(control_connectio
          case CONFIG_TYPE_FILENAME: type = "Filename"; break;
          case CONFIG_TYPE_UINT: type = "Integer"; break;
          case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break;
 +        case CONFIG_TYPE_MSEC_INTERVAL: type = "TimeMsecInterval"; break;
          case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break;
          case CONFIG_TYPE_DOUBLE: type = "Float"; break;
          case CONFIG_TYPE_BOOL: type = "Boolean"; break;