commit 0362cdc169a785a386d3c60d354cd34fafde5770 Author: Nick Mathewson nickm@torproject.org Date: Fri Jun 29 09:44:39 2018 -0400
Move fd and memory-info functions. --- .gitignore | 2 + Makefile.am | 2 + src/common/compat.c | 246 ------------------------------------------- src/common/compat.h | 6 -- src/common/util.c | 24 ----- src/common/util.h | 2 - src/include.am | 1 + src/lib/meminfo/.may_include | 8 ++ src/lib/meminfo/include.am | 17 +++ src/lib/meminfo/meminfo.c | 173 ++++++++++++++++++++++++++++++ src/lib/meminfo/meminfo.h | 15 +++ src/lib/process/.may_include | 1 + src/lib/process/restrict.c | 136 ++++++++++++++++++++++++ src/lib/process/restrict.h | 10 ++ src/or/config.c | 1 + src/or/main.c | 1 + src/test/test_config.c | 1 + src/test/test_util.c | 1 + 18 files changed, 369 insertions(+), 278 deletions(-)
diff --git a/.gitignore b/.gitignore index 0b45af3b6..2e37c3436 100644 --- a/.gitignore +++ b/.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 diff --git a/Makefile.am b/Makefile.am index 162e56e76..f44f64732 100644 --- a/Makefile.am +++ b/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 \ diff --git a/src/common/compat.c b/src/common/compat.c index b0a0a302c..6eb4ee77b 100644 --- a/src/common/compat.c +++ b/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. diff --git a/src/common/compat.h b/src/common/compat.h index ec4df1cc5..85e760d04 100644 --- a/src/common/compat.h +++ b/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. */ diff --git a/src/common/util.c b/src/common/util.c index 135af77c6..5d1d91153 100644 --- a/src/common/util.c +++ b/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 * ===== */ diff --git a/src/common/util.h b/src/common/util.h index ffd958c42..566d16d0a 100644 --- a/src/common/util.h +++ b/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> diff --git a/src/include.am b/src/include.am index ba5421e09..703aab17e 100644 --- a/src/include.am +++ b/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 diff --git a/src/lib/meminfo/.may_include b/src/lib/meminfo/.may_include new file mode 100644 index 000000000..9e4d25fd6 --- /dev/null +++ b/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 diff --git a/src/lib/meminfo/include.am b/src/lib/meminfo/include.am new file mode 100644 index 000000000..d1fdde631 --- /dev/null +++ b/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 diff --git a/src/lib/meminfo/meminfo.c b/src/lib/meminfo/meminfo.c new file mode 100644 index 000000000..34b4ad3b5 --- /dev/null +++ b/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; +} diff --git a/src/lib/meminfo/meminfo.h b/src/lib/meminfo/meminfo.h new file mode 100644 index 000000000..a970e992f --- /dev/null +++ b/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 diff --git a/src/lib/process/.may_include b/src/lib/process/.may_include index b195fb005..c02e7fddb 100644 --- a/src/lib/process/.may_include +++ b/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 diff --git a/src/lib/process/restrict.c b/src/lib/process/restrict.c index 85c04efbb..bb44cc3d1 100644 --- a/src/lib/process/restrict.c +++ b/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; +} diff --git a/src/lib/process/restrict.h b/src/lib/process/restrict.h index d608c9c9b..c7f76f823 100644 --- a/src/lib/process/restrict.h +++ b/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) */ diff --git a/src/or/config.c b/src/or/config.c index 55dd1d7f8..b98f124a8 100644 --- a/src/or/config.c +++ b/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" diff --git a/src/or/main.c b/src/or/main.c index 856e988cd..53be1b103 100644 --- a/src/or/main.c +++ b/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" diff --git a/src/test/test_config.c b/src/test/test_config.c index 0fadb6ebc..113898982 100644 --- a/src/test/test_config.c +++ b/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 diff --git a/src/test/test_util.c b/src/test/test_util.c index 539782e93..49c8ce359 100644 --- a/src/test/test_util.c +++ b/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