aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--src/common/compat.c119
-rw-r--r--src/common/compat.h2
-rw-r--r--src/or/main.c1
-rw-r--r--src/test/test_util.c29
5 files changed, 153 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index 6e4104196..d8543b0d2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -353,6 +353,7 @@ AC_CHECK_FUNCS(
strtok_r \
strtoull \
sysconf \
+ sysctl \
uname \
vasprintf \
_vscprintf
@@ -877,6 +878,7 @@ AC_CHECK_HEADERS(
sys/prctl.h \
sys/resource.h \
sys/socket.h \
+ sys/sysctl.h \
sys/syslimits.h \
sys/time.h \
sys/types.h \
diff --git a/src/common/compat.c b/src/common/compat.c
index 135f2c9af..58dd7f5da 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -38,6 +38,9 @@
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
@@ -3311,3 +3314,119 @@ format_win32_error(DWORD err)
}
#endif
+#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
+
+/**
+ * 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;
+
+ err:
+ tor_free(s);
+ close(fd);
+ return 0;
+#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 -1;
+
+ return memsize;
+
+#else
+ /* I have no clue. */
+ return 0;
+#endif
+}
+
+/**
+ * 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.
+ */
+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) {
+ /* 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;
+ }
+
+#if SIZE_T_MAX != UINT64_MAX
+ if (m > SIZE_T_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_T_MAX;
+ }
+#endif
+
+ *mem_out = mem_cached = (size_t) m;
+
+ return -1;
+}
+
diff --git a/src/common/compat.h b/src/common/compat.h
index bb88818d8..01faf14ae 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -637,6 +637,8 @@ char *make_path_absolute(char *fname);
char **get_environment(void);
+int get_total_system_memory(size_t *mem_out);
+
int spawn_func(void (*func)(void *), void *data);
void spawn_exit(void) ATTR_NORETURN;
diff --git a/src/or/main.c b/src/or/main.c
index feca35c44..8c75a3b5e 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -2751,6 +2751,7 @@ sandbox_init_filter(void)
"/dev/srandom", 0,
"/dev/urandom", 0,
"/dev/random", 0,
+ "/proc/meminfo", 0,
NULL, 0
);
diff --git a/src/test/test_util.c b/src/test/test_util.c
index a471b8eb1..20e65853d 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -3606,6 +3606,34 @@ test_util_socketpair(void *arg)
tor_close_socket(fds[1]);
}
+static void
+test_util_max_mem(void *arg)
+{
+ size_t memory1, memory2;
+ int r, r2;
+ (void) arg;
+
+ r = get_total_system_memory(&memory1);
+ r2 = get_total_system_memory(&memory2);
+ tt_int_op(r, ==, r2);
+ tt_int_op(memory2, ==, memory1);
+
+ TT_BLATHER(("System memory: "U64_FORMAT, U64_PRINTF_ARG(memory1)));
+
+ if (r==0) {
+ /* You have at least a megabyte. */
+ tt_int_op(memory1, >, (1<<20));
+ } else {
+ /* You do not have a petabyte. */
+#if SIZEOF_SIZE_T == SIZEOF_UINT64_T
+ tt_int_op(memory1, <, (U64_LITERAL(1)<<50));
+#endif
+ }
+
+ done:
+ ;
+}
+
struct testcase_t util_tests[] = {
UTIL_LEGACY(time),
UTIL_TEST(parse_http_time, 0),
@@ -3669,6 +3697,7 @@ struct testcase_t util_tests[] = {
(void*)"0" },
{ "socketpair_ersatz", test_util_socketpair, TT_FORK,
&socketpair_setup, (void*)"1" },
+ UTIL_TEST(max_mem, 0),
END_OF_TESTCASES
};