diff options
author | Nick Mathewson <nickm@torproject.org> | 2018-06-29 09:44:39 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2018-06-29 12:21:52 -0400 |
commit | 0362cdc169a785a386d3c60d354cd34fafde5770 (patch) | |
tree | b0a8a876f4b6996ce73e351dc36884f348286b00 /src/lib | |
parent | 973afcc40b52d71bfb1a1285806977fee9b843f6 (diff) | |
download | tor-0362cdc169a785a386d3c60d354cd34fafde5770.tar.gz tor-0362cdc169a785a386d3c60d354cd34fafde5770.zip |
Move fd and memory-info functions.
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/meminfo/.may_include | 8 | ||||
-rw-r--r-- | src/lib/meminfo/include.am | 17 | ||||
-rw-r--r-- | src/lib/meminfo/meminfo.c | 173 | ||||
-rw-r--r-- | src/lib/meminfo/meminfo.h | 15 | ||||
-rw-r--r-- | src/lib/process/.may_include | 1 | ||||
-rw-r--r-- | src/lib/process/restrict.c | 136 | ||||
-rw-r--r-- | src/lib/process/restrict.h | 10 |
7 files changed, 360 insertions, 0 deletions
diff --git a/src/lib/meminfo/.may_include b/src/lib/meminfo/.may_include new file mode 100644 index 0000000000..9e4d25fd6a --- /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 0000000000..d1fdde6313 --- /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 0000000000..34b4ad3b5d --- /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 0000000000..a970e992f0 --- /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 b195fb0056..c02e7fddb2 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 85c04efbbf..bb44cc3d15 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 d608c9c9bd..c7f76f8233 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) */ |