Browse Source

Move fd and memory-info functions.

Nick Mathewson 5 years ago
parent
commit
0362cdc169

+ 2 - 0
.gitignore

@@ -191,6 +191,8 @@ uptime-*.json
 /src/lib/libtor-math-testing.a
 /src/lib/libtor-memarea.a
 /src/lib/libtor-memarea-testing.a
+/src/lib/libtor-meminfo.a
+/src/lib/libtor-meminfo-testing.a
 /src/lib/libtor-net.a
 /src/lib/libtor-net-testing.a
 /src/lib/libtor-process.a

+ 2 - 0
Makefile.am

@@ -50,6 +50,7 @@ TOR_UTIL_LIBS = \
 	src/lib/libtor-thread.a \
 	src/lib/libtor-memarea.a \
 	src/lib/libtor-math.a \
+        src/lib/libtor-meminfo.a \
 	src/lib/libtor-log.a \
 	src/lib/libtor-lock.a \
 	src/lib/libtor-fdio.a \
@@ -75,6 +76,7 @@ TOR_UTIL_TESTING_LIBS = \
 	src/lib/libtor-thread-testing.a \
 	src/lib/libtor-memarea-testing.a \
 	src/lib/libtor-math-testing.a \
+        src/lib/libtor-meminfo-testing.a \
 	src/lib/libtor-log-testing.a \
 	src/lib/libtor-lock-testing.a \
 	src/lib/libtor-fdio-testing.a \

+ 0 - 246
src/common/compat.c

@@ -128,132 +128,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt)
 #include "lib/net/address.h"
 #include "lib/sandbox/sandbox.h"
 
-/** Number of extra file descriptors to keep in reserve beyond those that we
- * tell Tor it's allowed to use. */
-#define ULIMIT_BUFFER 32 /* keep 32 extra fd's beyond ConnLimit_ */
-
-/** Learn the maximum allowed number of file descriptors, and tell the
- * system we want to use up to that number. (Some systems have a low soft
- * limit, and let us set it higher.)  We compute this by finding the largest
- * number that we can use.
- *
- * If the limit is below the reserved file descriptor value (ULIMIT_BUFFER),
- * return -1 and <b>max_out</b> is untouched.
- *
- * If we can't find a number greater than or equal to <b>limit</b>, then we
- * fail by returning -1 and <b>max_out</b> is untouched.
- *
- * If we are unable to set the limit value because of setrlimit() failing,
- * return 0 and <b>max_out</b> is set to the current maximum value returned
- * by getrlimit().
- *
- * Otherwise, return 0 and store the maximum we found inside <b>max_out</b>
- * and set <b>max_sockets</b> with that value as well.*/
-int
-set_max_file_descriptors(rlim_t limit, int *max_out)
-{
-  if (limit < ULIMIT_BUFFER) {
-    log_warn(LD_CONFIG,
-             "ConnLimit must be at least %d. Failing.", ULIMIT_BUFFER);
-    return -1;
-  }
-
-  /* Define some maximum connections values for systems where we cannot
-   * automatically determine a limit. Re Cygwin, see
-   * http://archives.seul.org/or/talk/Aug-2006/msg00210.html
-   * For an iPhone, 9999 should work. For Windows and all other unknown
-   * systems we use 15000 as the default. */
-#ifndef HAVE_GETRLIMIT
-#if defined(CYGWIN) || defined(__CYGWIN__)
-  const char *platform = "Cygwin";
-  const unsigned long MAX_CONNECTIONS = 3200;
-#elif defined(_WIN32)
-  const char *platform = "Windows";
-  const unsigned long MAX_CONNECTIONS = 15000;
-#else
-  const char *platform = "unknown platforms with no getrlimit()";
-  const unsigned long MAX_CONNECTIONS = 15000;
-#endif /* defined(CYGWIN) || defined(__CYGWIN__) || ... */
-  log_fn(LOG_INFO, LD_NET,
-         "This platform is missing getrlimit(). Proceeding.");
-  if (limit > MAX_CONNECTIONS) {
-    log_warn(LD_CONFIG,
-             "We do not support more than %lu file descriptors "
-             "on %s. Tried to raise to %lu.",
-             (unsigned long)MAX_CONNECTIONS, platform, (unsigned long)limit);
-    return -1;
-  }
-  limit = MAX_CONNECTIONS;
-#else /* !(!defined(HAVE_GETRLIMIT)) */
-  struct rlimit rlim;
-
-  if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
-    log_warn(LD_NET, "Could not get maximum number of file descriptors: %s",
-             strerror(errno));
-    return -1;
-  }
-  if (rlim.rlim_max < limit) {
-    log_warn(LD_CONFIG,"We need %lu file descriptors available, and we're "
-             "limited to %lu. Please change your ulimit -n.",
-             (unsigned long)limit, (unsigned long)rlim.rlim_max);
-    return -1;
-  }
-
-  if (rlim.rlim_max > rlim.rlim_cur) {
-    log_info(LD_NET,"Raising max file descriptors from %lu to %lu.",
-             (unsigned long)rlim.rlim_cur, (unsigned long)rlim.rlim_max);
-  }
-  /* Set the current limit value so if the attempt to set the limit to the
-   * max fails at least we'll have a valid value of maximum sockets. */
-  *max_out = (int)rlim.rlim_cur - ULIMIT_BUFFER;
-  set_max_sockets(*max_out);
-  rlim.rlim_cur = rlim.rlim_max;
-
-  if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
-    int couldnt_set = 1;
-    const int setrlimit_errno = errno;
-#ifdef OPEN_MAX
-    uint64_t try_limit = OPEN_MAX - ULIMIT_BUFFER;
-    if (errno == EINVAL && try_limit < (uint64_t) rlim.rlim_cur) {
-      /* On some platforms, OPEN_MAX is the real limit, and getrlimit() is
-       * full of nasty lies.  I'm looking at you, OSX 10.5.... */
-      rlim.rlim_cur = MIN((rlim_t) try_limit, rlim.rlim_cur);
-      if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) {
-        if (rlim.rlim_cur < (rlim_t)limit) {
-          log_warn(LD_CONFIG, "We are limited to %lu file descriptors by "
-                   "OPEN_MAX (%lu), and ConnLimit is %lu.  Changing "
-                   "ConnLimit; sorry.",
-                   (unsigned long)try_limit, (unsigned long)OPEN_MAX,
-                   (unsigned long)limit);
-        } else {
-          log_info(LD_CONFIG, "Dropped connection limit to %lu based on "
-                   "OPEN_MAX (%lu); Apparently, %lu was too high and rlimit "
-                   "lied to us.",
-                   (unsigned long)try_limit, (unsigned long)OPEN_MAX,
-                   (unsigned long)rlim.rlim_max);
-        }
-        couldnt_set = 0;
-      }
-    }
-#endif /* defined(OPEN_MAX) */
-    if (couldnt_set) {
-      log_warn(LD_CONFIG,"Couldn't set maximum number of file descriptors: %s",
-               strerror(setrlimit_errno));
-    }
-  }
-  /* leave some overhead for logs, etc, */
-  limit = rlim.rlim_cur;
-#endif /* !defined(HAVE_GETRLIMIT) */
-
-  if (limit > INT_MAX)
-    limit = INT_MAX;
-  tor_assert(max_out);
-  *max_out = (int)limit - ULIMIT_BUFFER;
-  set_max_sockets(*max_out);
-
-  return 0;
-}
-
 /** Hold the result of our call to <b>uname</b>. */
 static char uname_result[256];
 /** True iff uname_result is set. */
@@ -351,126 +225,6 @@ get_uname,(void))
  *   Process control
  */
 
-#if defined(HW_PHYSMEM64)
-/* This appears to be an OpenBSD thing */
-#define INT64_HW_MEM HW_PHYSMEM64
-#elif defined(HW_MEMSIZE)
-/* OSX defines this one */
-#define INT64_HW_MEM HW_MEMSIZE
-#endif /* defined(HW_PHYSMEM64) || ... */
-
-/**
- * Helper: try to detect the total system memory, and return it. On failure,
- * return 0.
- */
-static uint64_t
-get_total_system_memory_impl(void)
-{
-#if defined(__linux__)
-  /* On linux, sysctl is deprecated. Because proc is so awesome that you
-   * shouldn't _want_ to write portable code, I guess? */
-  unsigned long long result=0;
-  int fd = -1;
-  char *s = NULL;
-  const char *cp;
-  size_t file_size=0;
-  if (-1 == (fd = tor_open_cloexec("/proc/meminfo",O_RDONLY,0)))
-    return 0;
-  s = read_file_to_str_until_eof(fd, 65536, &file_size);
-  if (!s)
-    goto err;
-  cp = strstr(s, "MemTotal:");
-  if (!cp)
-    goto err;
-  /* Use the system sscanf so that space will match a wider number of space */
-  if (sscanf(cp, "MemTotal: %llu kB\n", &result) != 1)
-    goto err;
-
-  close(fd);
-  tor_free(s);
-  return result * 1024;
-
-  /* LCOV_EXCL_START Can't reach this unless proc is broken. */
- err:
-  tor_free(s);
-  close(fd);
-  return 0;
-  /* LCOV_EXCL_STOP */
-#elif defined (_WIN32)
-  /* Windows has MEMORYSTATUSEX; pretty straightforward. */
-  MEMORYSTATUSEX ms;
-  memset(&ms, 0, sizeof(ms));
-  ms.dwLength = sizeof(ms);
-  if (! GlobalMemoryStatusEx(&ms))
-    return 0;
-
-  return ms.ullTotalPhys;
-
-#elif defined(HAVE_SYSCTL) && defined(INT64_HW_MEM)
-  /* On many systems, HW_PYHSMEM is clipped to 32 bits; let's use a better
-   * variant if we know about it. */
-  uint64_t memsize = 0;
-  size_t len = sizeof(memsize);
-  int mib[2] = {CTL_HW, INT64_HW_MEM};
-  if (sysctl(mib,2,&memsize,&len,NULL,0))
-    return 0;
-
-  return memsize;
-
-#elif defined(HAVE_SYSCTL) && defined(HW_PHYSMEM)
-  /* On some systems (like FreeBSD I hope) you can use a size_t with
-   * HW_PHYSMEM. */
-  size_t memsize=0;
-  size_t len = sizeof(memsize);
-  int mib[2] = {CTL_HW, HW_USERMEM};
-  if (sysctl(mib,2,&memsize,&len,NULL,0))
-    return 0;
-
-  return memsize;
-
-#else
-  /* I have no clue. */
-  return 0;
-#endif /* defined(__linux__) || ... */
-}
-
-/**
- * Try to find out how much physical memory the system has. On success,
- * return 0 and set *<b>mem_out</b> to that value. On failure, return -1.
- */
-MOCK_IMPL(int,
-get_total_system_memory, (size_t *mem_out))
-{
-  static size_t mem_cached=0;
-  uint64_t m = get_total_system_memory_impl();
-  if (0 == m) {
-    /* LCOV_EXCL_START -- can't make this happen without mocking. */
-    /* We couldn't find our memory total */
-    if (0 == mem_cached) {
-      /* We have no cached value either */
-      *mem_out = 0;
-      return -1;
-    }
-
-    *mem_out = mem_cached;
-    return 0;
-    /* LCOV_EXCL_STOP */
-  }
-
-#if SIZE_MAX != UINT64_MAX
-  if (m > SIZE_MAX) {
-    /* I think this could happen if we're a 32-bit Tor running on a 64-bit
-     * system: we could have more system memory than would fit in a
-     * size_t. */
-    m = SIZE_MAX;
-  }
-#endif /* SIZE_MAX != UINT64_MAX */
-
-  *mem_out = mem_cached = (size_t) m;
-
-  return 0;
-}
-
 /** Emit the password prompt <b>prompt</b>, then read up to <b>buflen</b>
  * bytes of passphrase into <b>output</b>. Return the number of bytes in
  * the passphrase, excluding terminating NUL.

+ 0 - 6
src/common/compat.h

@@ -87,12 +87,6 @@ typedef enum {
 /* ===== OS compatibility */
 MOCK_DECL(const char *, get_uname, (void));
 
-#if !defined(HAVE_RLIM_T)
-typedef unsigned long rlim_t;
-#endif
-int set_max_file_descriptors(rlim_t limit, int *max);
-MOCK_DECL(int, get_total_system_memory, (size_t *mem_out));
-
 ssize_t tor_getpass(const char *prompt, char *output, size_t buflen);
 
 /* This needs some of the declarations above so we include it here. */

+ 0 - 24
src/common/util.c

@@ -99,30 +99,6 @@
 /* =====
  * Memory management
  * ===== */
-
-DISABLE_GCC_WARNING(aggregate-return)
-/** Call the platform malloc info function, and dump the results to the log at
- * level <b>severity</b>.  If no such function exists, do nothing. */
-void
-tor_log_mallinfo(int severity)
-{
-#ifdef HAVE_MALLINFO
-  struct mallinfo mi;
-  memset(&mi, 0, sizeof(mi));
-  mi = mallinfo();
-  tor_log(severity, LD_MM,
-      "mallinfo() said: arena=%d, ordblks=%d, smblks=%d, hblks=%d, "
-      "hblkhd=%d, usmblks=%d, fsmblks=%d, uordblks=%d, fordblks=%d, "
-      "keepcost=%d",
-      mi.arena, mi.ordblks, mi.smblks, mi.hblks,
-      mi.hblkhd, mi.usmblks, mi.fsmblks, mi.uordblks, mi.fordblks,
-      mi.keepcost);
-#else /* !(defined(HAVE_MALLINFO)) */
-  (void)severity;
-#endif /* defined(HAVE_MALLINFO) */
-}
-ENABLE_GCC_WARNING(aggregate-return)
-
 /* =====
  * Math
  * ===== */

+ 0 - 2
src/common/util.h

@@ -42,8 +42,6 @@
 #include "lib/encoding/cstring.h"
 #include "lib/fs/winlib.h"
 
-void tor_log_mallinfo(int severity);
-
 /** Macro: yield a pointer to an enclosing structure given a pointer to
  * a substructure at offset <b>off</b>. Example:
  * <pre>

+ 1 - 0
src/include.am

@@ -16,6 +16,7 @@ include src/lib/lock/include.am
 include src/lib/log/include.am
 include src/lib/math/include.am
 include src/lib/memarea/include.am
+include src/lib/meminfo/include.am
 include src/lib/malloc/include.am
 include src/lib/net/include.am
 include src/lib/process/include.am

+ 8 - 0
src/lib/meminfo/.may_include

@@ -0,0 +1,8 @@
+orconfig.h
+
+lib/cc/*.h
+lib/fs/*.h
+lib/log/*.h
+lib/malloc/*.h
+lib/meminfo/*.h
+lib/testsupport/*.h

+ 17 - 0
src/lib/meminfo/include.am

@@ -0,0 +1,17 @@
+
+noinst_LIBRARIES += src/lib/libtor-meminfo.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += src/lib/libtor-meminfo-testing.a
+endif
+
+src_lib_libtor_meminfo_a_SOURCES =			\
+	src/lib/meminfo/meminfo.c
+
+src_lib_libtor_meminfo_testing_a_SOURCES = \
+	$(src_lib_libtor_meminfo_a_SOURCES)
+src_lib_libtor_meminfo_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_lib_libtor_meminfo_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
+noinst_HEADERS +=					\
+	src/lib/meminfo/meminfo.h

+ 173 - 0
src/lib/meminfo/meminfo.c

@@ -0,0 +1,173 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "lib/meminfo/meminfo.h"
+
+#include "lib/cc/compat_compiler.h"
+#include "lib/cc/torint.h"
+#include "lib/fs/files.h"
+#include "lib/log/torlog.h"
+#include "lib/malloc/util_malloc.h"
+
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <string.h>
+
+DISABLE_GCC_WARNING(aggregate-return)
+/** Call the platform malloc info function, and dump the results to the log at
+ * level <b>severity</b>.  If no such function exists, do nothing. */
+void
+tor_log_mallinfo(int severity)
+{
+#ifdef HAVE_MALLINFO
+  struct mallinfo mi;
+  memset(&mi, 0, sizeof(mi));
+  mi = mallinfo();
+  tor_log(severity, LD_MM,
+      "mallinfo() said: arena=%d, ordblks=%d, smblks=%d, hblks=%d, "
+      "hblkhd=%d, usmblks=%d, fsmblks=%d, uordblks=%d, fordblks=%d, "
+      "keepcost=%d",
+      mi.arena, mi.ordblks, mi.smblks, mi.hblks,
+      mi.hblkhd, mi.usmblks, mi.fsmblks, mi.uordblks, mi.fordblks,
+      mi.keepcost);
+#else /* !(defined(HAVE_MALLINFO)) */
+  (void)severity;
+#endif /* defined(HAVE_MALLINFO) */
+}
+ENABLE_GCC_WARNING(aggregate-return)
+
+#if defined(HW_PHYSMEM64)
+/* This appears to be an OpenBSD thing */
+#define INT64_HW_MEM HW_PHYSMEM64
+#elif defined(HW_MEMSIZE)
+/* OSX defines this one */
+#define INT64_HW_MEM HW_MEMSIZE
+#endif /* defined(HW_PHYSMEM64) || ... */
+
+/**
+ * Helper: try to detect the total system memory, and return it. On failure,
+ * return 0.
+ */
+static uint64_t
+get_total_system_memory_impl(void)
+{
+#if defined(__linux__)
+  /* On linux, sysctl is deprecated. Because proc is so awesome that you
+   * shouldn't _want_ to write portable code, I guess? */
+  unsigned long long result=0;
+  int fd = -1;
+  char *s = NULL;
+  const char *cp;
+  size_t file_size=0;
+  if (-1 == (fd = tor_open_cloexec("/proc/meminfo",O_RDONLY,0)))
+    return 0;
+  s = read_file_to_str_until_eof(fd, 65536, &file_size);
+  if (!s)
+    goto err;
+  cp = strstr(s, "MemTotal:");
+  if (!cp)
+    goto err;
+  /* Use the system sscanf so that space will match a wider number of space */
+  if (sscanf(cp, "MemTotal: %llu kB\n", &result) != 1)
+    goto err;
+
+  close(fd);
+  tor_free(s);
+  return result * 1024;
+
+  /* LCOV_EXCL_START Can't reach this unless proc is broken. */
+ err:
+  tor_free(s);
+  close(fd);
+  return 0;
+  /* LCOV_EXCL_STOP */
+#elif defined (_WIN32)
+  /* Windows has MEMORYSTATUSEX; pretty straightforward. */
+  MEMORYSTATUSEX ms;
+  memset(&ms, 0, sizeof(ms));
+  ms.dwLength = sizeof(ms);
+  if (! GlobalMemoryStatusEx(&ms))
+    return 0;
+
+  return ms.ullTotalPhys;
+
+#elif defined(HAVE_SYSCTL) && defined(INT64_HW_MEM)
+  /* On many systems, HW_PYHSMEM is clipped to 32 bits; let's use a better
+   * variant if we know about it. */
+  uint64_t memsize = 0;
+  size_t len = sizeof(memsize);
+  int mib[2] = {CTL_HW, INT64_HW_MEM};
+  if (sysctl(mib,2,&memsize,&len,NULL,0))
+    return 0;
+
+  return memsize;
+
+#elif defined(HAVE_SYSCTL) && defined(HW_PHYSMEM)
+  /* On some systems (like FreeBSD I hope) you can use a size_t with
+   * HW_PHYSMEM. */
+  size_t memsize=0;
+  size_t len = sizeof(memsize);
+  int mib[2] = {CTL_HW, HW_USERMEM};
+  if (sysctl(mib,2,&memsize,&len,NULL,0))
+    return 0;
+
+  return memsize;
+
+#else
+  /* I have no clue. */
+  return 0;
+#endif /* defined(__linux__) || ... */
+}
+
+/**
+ * Try to find out how much physical memory the system has. On success,
+ * return 0 and set *<b>mem_out</b> to that value. On failure, return -1.
+ */
+MOCK_IMPL(int,
+get_total_system_memory, (size_t *mem_out))
+{
+  static size_t mem_cached=0;
+  uint64_t m = get_total_system_memory_impl();
+  if (0 == m) {
+    /* LCOV_EXCL_START -- can't make this happen without mocking. */
+    /* We couldn't find our memory total */
+    if (0 == mem_cached) {
+      /* We have no cached value either */
+      *mem_out = 0;
+      return -1;
+    }
+
+    *mem_out = mem_cached;
+    return 0;
+    /* LCOV_EXCL_STOP */
+  }
+
+#if SIZE_MAX != UINT64_MAX
+  if (m > SIZE_MAX) {
+    /* I think this could happen if we're a 32-bit Tor running on a 64-bit
+     * system: we could have more system memory than would fit in a
+     * size_t. */
+    m = SIZE_MAX;
+  }
+#endif /* SIZE_MAX != UINT64_MAX */
+
+  *mem_out = mem_cached = (size_t) m;
+
+  return 0;
+}

+ 15 - 0
src/lib/meminfo/meminfo.h

@@ -0,0 +1,15 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_MEMINFO_H
+#define TOR_MEMINFO_H
+
+#include "lib/testsupport/testsupport.h"
+#include <stddef.h>
+
+void tor_log_mallinfo(int severity);
+MOCK_DECL(int, get_total_system_memory, (size_t *mem_out));
+
+#endif

+ 1 - 0
src/lib/process/.may_include

@@ -7,6 +7,7 @@ lib/err/*.h
 lib/fs/*.h
 lib/log/*.h
 lib/malloc/*.h
+lib/net/*.h
 lib/process/*.h
 lib/string/*.h
 lib/testsupport/*.h

+ 136 - 0
src/lib/process/restrict.c

@@ -5,7 +5,17 @@
 
 #include "orconfig.h"
 #include "lib/process/restrict.h"
+#include "lib/intmath/cmp.h"
 #include "lib/log/torlog.h"
+#include "lib/log/util_bug.h"
+#include "lib/net/socket.h"
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
 
 /* We only use the linux prctl for now. There is no Win32 support; this may
  * also work on various BSD systems and Mac OS X - send testing feedback!
@@ -142,3 +152,129 @@ tor_mlockall(void)
   return -1;
 #endif /* defined(HAVE_UNIX_MLOCKALL) */
 }
+
+/** Number of extra file descriptors to keep in reserve beyond those that we
+ * tell Tor it's allowed to use. */
+#define ULIMIT_BUFFER 32 /* keep 32 extra fd's beyond ConnLimit_ */
+
+/** Learn the maximum allowed number of file descriptors, and tell the
+ * system we want to use up to that number. (Some systems have a low soft
+ * limit, and let us set it higher.)  We compute this by finding the largest
+ * number that we can use.
+ *
+ * If the limit is below the reserved file descriptor value (ULIMIT_BUFFER),
+ * return -1 and <b>max_out</b> is untouched.
+ *
+ * If we can't find a number greater than or equal to <b>limit</b>, then we
+ * fail by returning -1 and <b>max_out</b> is untouched.
+ *
+ * If we are unable to set the limit value because of setrlimit() failing,
+ * return 0 and <b>max_out</b> is set to the current maximum value returned
+ * by getrlimit().
+ *
+ * Otherwise, return 0 and store the maximum we found inside <b>max_out</b>
+ * and set <b>max_sockets</b> with that value as well.*/
+int
+set_max_file_descriptors(rlim_t limit, int *max_out)
+{
+  if (limit < ULIMIT_BUFFER) {
+    log_warn(LD_CONFIG,
+             "ConnLimit must be at least %d. Failing.", ULIMIT_BUFFER);
+    return -1;
+  }
+
+  /* Define some maximum connections values for systems where we cannot
+   * automatically determine a limit. Re Cygwin, see
+   * http://archives.seul.org/or/talk/Aug-2006/msg00210.html
+   * For an iPhone, 9999 should work. For Windows and all other unknown
+   * systems we use 15000 as the default. */
+#ifndef HAVE_GETRLIMIT
+#if defined(CYGWIN) || defined(__CYGWIN__)
+  const char *platform = "Cygwin";
+  const unsigned long MAX_CONNECTIONS = 3200;
+#elif defined(_WIN32)
+  const char *platform = "Windows";
+  const unsigned long MAX_CONNECTIONS = 15000;
+#else
+  const char *platform = "unknown platforms with no getrlimit()";
+  const unsigned long MAX_CONNECTIONS = 15000;
+#endif /* defined(CYGWIN) || defined(__CYGWIN__) || ... */
+  log_fn(LOG_INFO, LD_NET,
+         "This platform is missing getrlimit(). Proceeding.");
+  if (limit > MAX_CONNECTIONS) {
+    log_warn(LD_CONFIG,
+             "We do not support more than %lu file descriptors "
+             "on %s. Tried to raise to %lu.",
+             (unsigned long)MAX_CONNECTIONS, platform, (unsigned long)limit);
+    return -1;
+  }
+  limit = MAX_CONNECTIONS;
+#else /* !(!defined(HAVE_GETRLIMIT)) */
+  struct rlimit rlim;
+
+  if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+    log_warn(LD_NET, "Could not get maximum number of file descriptors: %s",
+             strerror(errno));
+    return -1;
+  }
+  if (rlim.rlim_max < limit) {
+    log_warn(LD_CONFIG,"We need %lu file descriptors available, and we're "
+             "limited to %lu. Please change your ulimit -n.",
+             (unsigned long)limit, (unsigned long)rlim.rlim_max);
+    return -1;
+  }
+
+  if (rlim.rlim_max > rlim.rlim_cur) {
+    log_info(LD_NET,"Raising max file descriptors from %lu to %lu.",
+             (unsigned long)rlim.rlim_cur, (unsigned long)rlim.rlim_max);
+  }
+  /* Set the current limit value so if the attempt to set the limit to the
+   * max fails at least we'll have a valid value of maximum sockets. */
+  *max_out = (int)rlim.rlim_cur - ULIMIT_BUFFER;
+  set_max_sockets(*max_out);
+  rlim.rlim_cur = rlim.rlim_max;
+
+  if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+    int couldnt_set = 1;
+    const int setrlimit_errno = errno;
+#ifdef OPEN_MAX
+    uint64_t try_limit = OPEN_MAX - ULIMIT_BUFFER;
+    if (errno == EINVAL && try_limit < (uint64_t) rlim.rlim_cur) {
+      /* On some platforms, OPEN_MAX is the real limit, and getrlimit() is
+       * full of nasty lies.  I'm looking at you, OSX 10.5.... */
+      rlim.rlim_cur = MIN((rlim_t) try_limit, rlim.rlim_cur);
+      if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) {
+        if (rlim.rlim_cur < (rlim_t)limit) {
+          log_warn(LD_CONFIG, "We are limited to %lu file descriptors by "
+                   "OPEN_MAX (%lu), and ConnLimit is %lu.  Changing "
+                   "ConnLimit; sorry.",
+                   (unsigned long)try_limit, (unsigned long)OPEN_MAX,
+                   (unsigned long)limit);
+        } else {
+          log_info(LD_CONFIG, "Dropped connection limit to %lu based on "
+                   "OPEN_MAX (%lu); Apparently, %lu was too high and rlimit "
+                   "lied to us.",
+                   (unsigned long)try_limit, (unsigned long)OPEN_MAX,
+                   (unsigned long)rlim.rlim_max);
+        }
+        couldnt_set = 0;
+      }
+    }
+#endif /* defined(OPEN_MAX) */
+    if (couldnt_set) {
+      log_warn(LD_CONFIG,"Couldn't set maximum number of file descriptors: %s",
+               strerror(setrlimit_errno));
+    }
+  }
+  /* leave some overhead for logs, etc, */
+  limit = rlim.rlim_cur;
+#endif /* !defined(HAVE_GETRLIMIT) */
+
+  if (limit > INT_MAX)
+    limit = INT_MAX;
+  tor_assert(max_out);
+  *max_out = (int)limit - ULIMIT_BUFFER;
+  set_max_sockets(*max_out);
+
+  return 0;
+}

+ 10 - 0
src/lib/process/restrict.h

@@ -11,7 +11,17 @@
 #ifndef TOR_RESTRICT_H
 #define TOR_RESTRICT_H
 
+#include "orconfig.h"
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
 int tor_disable_debugger_attach(void);
 int tor_mlockall(void);
 
+#if !defined(HAVE_RLIM_T)
+typedef unsigned long rlim_t;
+#endif
+int set_max_file_descriptors(rlim_t limit, int *max_out);
+
 #endif /* !defined(TOR_RESTRICT_H) */

+ 1 - 0
src/or/config.c

@@ -111,6 +111,7 @@
 #include <shlobj.h>
 #endif
 
+#include "lib/meminfo/meminfo.h"
 #include "lib/process/daemon.h"
 #include "lib/process/pidfile.h"
 #include "lib/process/restrict.h"

+ 1 - 0
src/or/main.c

@@ -111,6 +111,7 @@
 #include "lib/process/waitpid.h"
 #include "or/ext_orport.h"
 #include "lib/memarea/memarea.h"
+#include "lib/meminfo/meminfo.h"
 #include "lib/sandbox/sandbox.h"
 #include "lib/fs/lockfile.h"
 #include "lib/net/buffers_net.h"

+ 1 - 0
src/test/test_config.c

@@ -49,6 +49,7 @@
 #include "or/routerinfo_st.h"
 
 #include "lib/fs/conffile.h"
+#include "lib/meminfo/meminfo.h"
 #include "lib/net/gethostname.h"
 
 static void

+ 1 - 0
src/test/test_util.c

@@ -32,6 +32,7 @@
 #include "lib/thread/numcpus.h"
 #include "lib/math/fp.h"
 #include "lib/math/laplace.h"
+#include "lib/meminfo/meminfo.h"
 #include "lib/time/tvdiff.h"
 
 #ifdef HAVE_PWD_H