diff options
Diffstat (limited to 'src/common/compat.c')
-rw-r--r-- | src/common/compat.c | 569 |
1 files changed, 499 insertions, 70 deletions
diff --git a/src/common/compat.c b/src/common/compat.c index b77f25b467..53d0623c84 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -23,6 +23,7 @@ * we can also take out the configure check. */ #define _GNU_SOURCE +#define COMPAT_PRIVATE #include "compat.h" #ifdef _WIN32 @@ -34,6 +35,15 @@ #ifdef HAVE_UNAME #include <sys/utsname.h> #endif +#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 #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -104,11 +114,18 @@ /* Only use the linux prctl; the IRIX prctl is totally different */ #include <sys/prctl.h> #endif +#ifdef TOR_UNIT_TESTS +#if !defined(HAVE_USLEEP) && defined(HAVE_SYS_SELECT_H) +/* as fallback implementation for tor_sleep_msec */ +#include <sys/select.h> +#endif +#endif #include "torlog.h" #include "util.h" #include "container.h" #include "address.h" +#include "sandbox.h" /* Inline the strl functions if the platform doesn't have them. */ #ifndef HAVE_STRLCPY @@ -125,6 +142,7 @@ tor_open_cloexec(const char *path, int flags, unsigned mode) { int fd; #ifdef O_CLOEXEC + path = sandbox_intern_string(path); fd = open(path, flags|O_CLOEXEC, mode); if (fd >= 0) return fd; @@ -135,6 +153,7 @@ tor_open_cloexec(const char *path, int flags, unsigned mode) return -1; #endif + log_debug(LD_FS, "Opening %s with flags %x", path, flags); fd = open(path, flags, mode); #ifdef FD_CLOEXEC if (fd >= 0) { @@ -166,6 +185,15 @@ tor_fopen_cloexec(const char *path, const char *mode) return result; } +/** As rename(), but work correctly with the sandbox. */ +int +tor_rename(const char *path_old, const char *path_new) +{ + log_debug(LD_FS, "Renaming %s to %s", path_old, path_new); + return rename(sandbox_intern_string(path_old), + sandbox_intern_string(path_new)); +} + #if defined(HAVE_SYS_MMAN_H) || defined(RUNNING_DOXYGEN) /** Try to create a memory mapping for <b>filename</b> and return it. On * failure, return NULL. Sets errno properly, using ERANGE to mean @@ -175,9 +203,10 @@ tor_mmap_file(const char *filename) { int fd; /* router file */ char *string; - int page_size; + int page_size, result; tor_mmap_t *res; size_t size, filesize; + struct stat st; tor_assert(filename); @@ -191,9 +220,22 @@ tor_mmap_file(const char *filename) return NULL; } - /* XXXX why not just do fstat here? */ - size = filesize = (size_t) lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); + /* Get the size of the file */ + result = fstat(fd, &st); + if (result != 0) { + int save_errno = errno; + log_warn(LD_FS, + "Couldn't fstat opened descriptor for \"%s\" during mmap: %s", + filename, strerror(errno)); + close(fd); + errno = save_errno; + return NULL; + } + size = filesize = (size_t)(st.st_size); + /* + * Should we check for weird crap like mmapping a named pipe here, + * or just wait for if (!size) below to fail? + */ /* ensure page alignment */ page_size = getpagesize(); size += (size%page_size) ? page_size-(size%page_size) : 0; @@ -224,12 +266,27 @@ tor_mmap_file(const char *filename) return res; } -/** Release storage held for a memory mapping. */ -void +/** Release storage held for a memory mapping; returns 0 on success, + * or -1 on failure (and logs a warning). */ +int tor_munmap_file(tor_mmap_t *handle) { - munmap((char*)handle->data, handle->mapping_size); - tor_free(handle); + int res; + + if (handle == NULL) + return 0; + + res = munmap((char*)handle->data, handle->mapping_size); + if (res == 0) { + /* munmap() succeeded */ + tor_free(handle); + } else { + log_warn(LD_FS, "Failed to munmap() in tor_munmap_file(): %s", + strerror(errno)); + res = -1; + } + + return res; } #elif defined(_WIN32) tor_mmap_t * @@ -311,17 +368,29 @@ tor_mmap_file(const char *filename) tor_munmap_file(res); return NULL; } -void + +/* Unmap the file, and return 0 for success or -1 for failure */ +int tor_munmap_file(tor_mmap_t *handle) { - if (handle->data) + if (handle == NULL) + return 0; + + if (handle->data) { /* This is an ugly cast, but without it, "data" in struct tor_mmap_t would have to be redefined as non-const. */ - UnmapViewOfFile( (LPVOID) handle->data); + BOOL ok = UnmapViewOfFile( (LPVOID) handle->data); + if (!ok) { + log_warn(LD_FS, "Failed to UnmapViewOfFile() in tor_munmap_file(): %d", + (int)GetLastError()); + } + } if (handle->mmap_handle != NULL) CloseHandle(handle->mmap_handle); tor_free(handle); + + return 0; } #else tor_mmap_t * @@ -337,13 +406,25 @@ tor_mmap_file(const char *filename) handle->size = st.st_size; return handle; } -void + +/** Unmap the file mapped with tor_mmap_file(), and return 0 for success + * or -1 for failure. + */ + +int tor_munmap_file(tor_mmap_t *handle) { - char *d = (char*)handle->data; + char *d = NULL; + if (handle == NULL) + return 0; + + d = (char*)handle->data; tor_free(d); memwipe(handle, 0, sizeof(tor_mmap_t)); tor_free(handle); + + /* Can't fail in this mmap()/munmap()-free case */ + return 0; } #endif @@ -498,21 +579,29 @@ tor_memmem(const void *_haystack, size_t hlen, #else /* This isn't as fast as the GLIBC implementation, but it doesn't need to * be. */ - const char *p, *end; + const char *p, *last_possible_start; const char *haystack = (const char*)_haystack; const char *needle = (const char*)_needle; char first; tor_assert(nlen); + if (nlen > hlen) + return NULL; + p = haystack; - end = haystack + hlen; + /* Last position at which the needle could start. */ + last_possible_start = haystack + hlen - nlen; first = *(const char*)needle; - while ((p = memchr(p, first, end-p))) { - if (p+nlen > end) - return NULL; + while ((p = memchr(p, first, last_possible_start + 1 - p))) { if (fast_memeq(p, needle, nlen)) return p; - ++p; + if (++p > last_possible_start) { + /* This comparison shouldn't be necessary, since if p was previously + * equal to last_possible_start, the next memchr call would be + * "memchr(p, first, 0)", which will return NULL. But it clarifies the + * logic. */ + return NULL; + } } return NULL; #endif @@ -729,7 +818,7 @@ int replace_file(const char *from, const char *to) { #ifndef _WIN32 - return rename(from,to); + return tor_rename(from, to); #else switch (file_status(to)) { @@ -744,7 +833,7 @@ replace_file(const char *from, const char *to) errno = EISDIR; return -1; } - return rename(from,to); + return tor_rename(from,to); #endif } @@ -948,24 +1037,40 @@ socket_accounting_unlock(void) } /** As close(), but guaranteed to work for sockets across platforms (including - * Windows, where close()ing a socket doesn't work. Returns 0 on success, -1 - * on failure. */ + * Windows, where close()ing a socket doesn't work. Returns 0 on success and + * the socket error code on failure. */ int -tor_close_socket(tor_socket_t s) +tor_close_socket_simple(tor_socket_t s) { int r = 0; /* On Windows, you have to call close() on fds returned by open(), - * and closesocket() on fds returned by socket(). On Unix, everything - * gets close()'d. We abstract this difference by always using - * tor_close_socket to close sockets, and always using close() on - * files. - */ -#if defined(_WIN32) - r = closesocket(s); -#else - r = close(s); -#endif + * and closesocket() on fds returned by socket(). On Unix, everything + * gets close()'d. We abstract this difference by always using + * tor_close_socket to close sockets, and always using close() on + * files. + */ + #if defined(_WIN32) + r = closesocket(s); + #else + r = close(s); + #endif + + if (r != 0) { + int err = tor_socket_errno(-1); + log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err)); + return err; + } + + return r; +} + +/** As tor_close_socket_simple(), but keeps track of the number + * of open sockets. Returns 0 on success, -1 on failure. */ +int +tor_close_socket(tor_socket_t s) +{ + int r = tor_close_socket_simple(s); socket_accounting_lock(); #ifdef DEBUG_SOCKET_COUNTING @@ -980,13 +1085,11 @@ tor_close_socket(tor_socket_t s) if (r == 0) { --n_sockets_open; } else { - int err = tor_socket_errno(-1); - log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err)); #ifdef _WIN32 - if (err != WSAENOTSOCK) + if (r != WSAENOTSOCK) --n_sockets_open; #else - if (err != EBADF) + if (r != EBADF) --n_sockets_open; #endif r = -1; @@ -1032,33 +1135,61 @@ mark_socket_open(tor_socket_t s) tor_socket_t tor_open_socket(int domain, int type, int protocol) { + return tor_open_socket_with_extensions(domain, type, protocol, 1, 0); +} + +/** As socket(), but creates a nonblocking socket and + * counts the number of open sockets. */ +tor_socket_t +tor_open_socket_nonblocking(int domain, int type, int protocol) +{ + return tor_open_socket_with_extensions(domain, type, protocol, 1, 1); +} + +/** As socket(), but counts the number of open sockets and handles + * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified. + * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate + * if the corresponding extension should be used.*/ +tor_socket_t +tor_open_socket_with_extensions(int domain, int type, int protocol, + int cloexec, int nonblock) +{ tor_socket_t s; -#ifdef SOCK_CLOEXEC - s = socket(domain, type|SOCK_CLOEXEC, protocol); +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) | + (nonblock ? SOCK_NONBLOCK : 0); + s = socket(domain, type|ext_flags, protocol); if (SOCKET_OK(s)) goto socket_ok; /* If we got an error, see if it is EINVAL. EINVAL might indicate that, - * even though we were built on a system with SOCK_CLOEXEC support, we - * are running on one without. */ + * even though we were built on a system with SOCK_CLOEXEC and SOCK_NONBLOCK + * support, we are running on one without. */ if (errno != EINVAL) return s; -#endif /* SOCK_CLOEXEC */ +#endif /* SOCK_CLOEXEC && SOCK_NONBLOCK */ s = socket(domain, type, protocol); if (! SOCKET_OK(s)) return s; #if defined(FD_CLOEXEC) - if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { - log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno)); -#if defined(_WIN32) - closesocket(s); + if (cloexec) { + if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { + log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno)); + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } + } #else - close(s); + (void)cloexec; #endif - return -1; + + if (nonblock) { + if (set_socket_nonblocking(s) == -1) { + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } } -#endif goto socket_ok; /* So that socket_ok will not be unused. */ @@ -1070,19 +1201,41 @@ tor_open_socket(int domain, int type, int protocol) return s; } -/** As socket(), but counts the number of open sockets. */ +/** As accept(), but counts the number of open sockets. */ tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len) { + return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 0); +} + +/** As accept(), but returns a nonblocking socket and + * counts the number of open sockets. */ +tor_socket_t +tor_accept_socket_nonblocking(tor_socket_t sockfd, struct sockaddr *addr, + socklen_t *len) +{ + return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 1); +} + +/** As accept(), but counts the number of open sockets and handles + * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified. + * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate + * if the corresponding extension should be used.*/ +tor_socket_t +tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr, + socklen_t *len, int cloexec, int nonblock) +{ tor_socket_t s; -#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - s = accept4(sockfd, addr, len, SOCK_CLOEXEC); +#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) | + (nonblock ? SOCK_NONBLOCK : 0); + s = accept4(sockfd, addr, len, ext_flags); if (SOCKET_OK(s)) goto socket_ok; /* If we got an error, see if it is ENOSYS. ENOSYS indicates that, * even though we were built on a system with accept4 support, we * are running on one without. Also, check for EINVAL, which indicates that - * we are missing SOCK_CLOEXEC support. */ + * we are missing SOCK_CLOEXEC/SOCK_NONBLOCK support. */ if (errno != EINVAL && errno != ENOSYS) return s; #endif @@ -1092,13 +1245,24 @@ tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len) return s; #if defined(FD_CLOEXEC) - if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { - log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno)); - close(s); - return TOR_INVALID_SOCKET; + if (cloexec) { + if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { + log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno)); + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } } +#else + (void)cloexec; #endif + if (nonblock) { + if (set_socket_nonblocking(s) == -1) { + tor_close_socket_simple(s); + return TOR_INVALID_SOCKET; + } + } + goto socket_ok; /* So that socket_ok will not be unused. */ socket_ok: @@ -1220,6 +1384,18 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) return 0; #else + return tor_ersatz_socketpair(family, type, protocol, fd); +#endif +} + +#ifdef NEED_ERSATZ_SOCKETPAIR +/** + * Helper used to implement socketpair on systems that lack it, by + * making a direct connection to localhost. + */ +STATIC int +tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) +{ /* This socketpair does not work when localhost is down. So * it's really not the same thing at all. But it's close enough * for now, and really, when localhost is down sometimes, we @@ -1230,7 +1406,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) tor_socket_t acceptor = TOR_INVALID_SOCKET; struct sockaddr_in listen_addr; struct sockaddr_in connect_addr; - int size; + socklen_t size; int saved_errno = -1; if (protocol @@ -1313,8 +1489,8 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]) if (SOCKET_OK(acceptor)) tor_close_socket(acceptor); return -saved_errno; -#endif } +#endif /** Number of extra file descriptors to keep in reserve beyond those that we * tell Tor it's allowed to use. */ @@ -1532,6 +1708,106 @@ log_credential_status(void) } #endif +#ifndef _WIN32 +/** Cached struct from the last getpwname() call we did successfully. */ +static struct passwd *passwd_cached = NULL; + +/** Helper: copy a struct passwd object. + * + * We only copy the fields pw_uid, pw_gid, pw_name, pw_dir. Tor doesn't use + * any others, and I don't want to run into incompatibilities. + */ +static struct passwd * +tor_passwd_dup(const struct passwd *pw) +{ + struct passwd *new_pw = tor_malloc_zero(sizeof(struct passwd)); + if (pw->pw_name) + new_pw->pw_name = tor_strdup(pw->pw_name); + if (pw->pw_dir) + new_pw->pw_dir = tor_strdup(pw->pw_dir); + new_pw->pw_uid = pw->pw_uid; + new_pw->pw_gid = pw->pw_gid; + + return new_pw; +} + +/** Helper: free one of our cached 'struct passwd' values. */ +static void +tor_passwd_free(struct passwd *pw) +{ + if (!pw) + return; + + tor_free(pw->pw_name); + tor_free(pw->pw_dir); + tor_free(pw); +} + +/** Wrapper around getpwnam() that caches result. Used so that we don't need + * to give the sandbox access to /etc/passwd. + * + * The following fields alone will definitely be copied in the output: pw_uid, + * pw_gid, pw_name, pw_dir. Other fields are not present in cached values. + * + * When called with a NULL argument, this function clears storage associated + * with static variables it uses. + **/ +const struct passwd * +tor_getpwnam(const char *username) +{ + struct passwd *pw; + + if (username == NULL) { + tor_passwd_free(passwd_cached); + passwd_cached = NULL; + return NULL; + } + + if ((pw = getpwnam(username))) { + tor_passwd_free(passwd_cached); + passwd_cached = tor_passwd_dup(pw); + log_notice(LD_GENERAL, "Caching new entry %s for %s", + passwd_cached->pw_name, username); + return pw; + } + + /* Lookup failed */ + if (! passwd_cached || ! passwd_cached->pw_name) + return NULL; + + if (! strcmp(username, passwd_cached->pw_name)) + return passwd_cached; + + return NULL; +} + +/** Wrapper around getpwnam() that can use cached result from + * tor_getpwnam(). Used so that we don't need to give the sandbox access to + * /etc/passwd. + * + * The following fields alone will definitely be copied in the output: pw_uid, + * pw_gid, pw_name, pw_dir. Other fields are not present in cached values. + */ +const struct passwd * +tor_getpwuid(uid_t uid) +{ + struct passwd *pw; + + if ((pw = getpwuid(uid))) { + return pw; + } + + /* Lookup failed */ + if (! passwd_cached) + return NULL; + + if (uid == passwd_cached->pw_uid) + return passwd_cached; + + return NULL; +} +#endif + /** Call setuid and setgid to run as <b>user</b> and switch to their * primary group. Return 0 on success. On failure, log and return -1. */ @@ -1539,7 +1815,7 @@ int switch_id(const char *user) { #ifndef _WIN32 - struct passwd *pw = NULL; + const struct passwd *pw = NULL; uid_t old_uid; gid_t old_gid; static int have_already_switched_id = 0; @@ -1560,7 +1836,7 @@ switch_id(const char *user) old_gid = getgid(); /* Lookup the user and group information, if we have a problem, bail out. */ - pw = getpwnam(user); + pw = tor_getpwnam(user); if (pw == NULL) { log_warn(LD_CONFIG, "Error setting configured user: %s not found", user); return -1; @@ -1731,10 +2007,10 @@ tor_disable_debugger_attach(void) char * get_user_homedir(const char *username) { - struct passwd *pw; + const struct passwd *pw; tor_assert(username); - if (!(pw = getpwnam(username))) { + if (!(pw = tor_getpwnam(username))) { log_err(LD_CONFIG,"User \"%s\" not found.", username); return NULL; } @@ -1746,6 +2022,15 @@ get_user_homedir(const char *username) * actually examine the filesystem; does a purely syntactic modification. * * The parent of the root director is considered to be iteself. + * + * Path separators are the forward slash (/) everywhere and additionally + * the backslash (\) on Win32. + * + * Cuts off any number of trailing path separators but otherwise ignores + * them for purposes of finding the parent directory. + * + * Returns 0 if a parent directory was successfully found, -1 otherwise (fname + * did not have any path separators or only had them at the end). * */ int get_parent_directory(char *fname) @@ -2019,8 +2304,10 @@ tor_inet_pton(int af, const char *src, void *dst) else { unsigned byte1,byte2,byte3,byte4; char more; - for (eow = dot-1; eow >= src && TOR_ISDIGIT(*eow); --eow) + for (eow = dot-1; eow > src && TOR_ISDIGIT(*eow); --eow) ; + if (*eow != ':') + return 0; ++eow; /* We use "scanf" because some platform inet_aton()s are too lax @@ -2252,6 +2539,12 @@ tor_pthread_helper_fn(void *_data) func(arg); return NULL; } +/** + * A pthread attribute to make threads start detached. + */ +static pthread_attr_t attr_detached; +/** True iff we've called tor_threads_init() */ +static int threads_initialized = 0; #endif /** Minimalist interface to run a void function in the background. On @@ -2275,12 +2568,12 @@ spawn_func(void (*func)(void *), void *data) #elif defined(USE_PTHREADS) pthread_t thread; tor_pthread_data_t *d; + if (PREDICT_UNLIKELY(!threads_initialized)) + tor_threads_init(); d = tor_malloc(sizeof(tor_pthread_data_t)); d->data = data; d->func = func; - if (pthread_create(&thread,NULL,tor_pthread_helper_fn,d)) - return -1; - if (pthread_detach(thread)) + if (pthread_create(&thread,&attr_detached,tor_pthread_helper_fn,d)) return -1; return 0; #else @@ -2637,8 +2930,6 @@ tor_get_thread_id(void) * "reentrant" mutexes (i.e., once we can re-lock if we're already holding * them.) */ static pthread_mutexattr_t attr_reentrant; -/** True iff we've called tor_threads_init() */ -static int threads_initialized = 0; /** Initialize <b>mutex</b> so it can be locked. Every mutex must be set * up with tor_mutex_init() or tor_mutex_new(); not both. */ void @@ -2782,6 +3073,8 @@ tor_threads_init(void) if (!threads_initialized) { pthread_mutexattr_init(&attr_reentrant); pthread_mutexattr_settype(&attr_reentrant, PTHREAD_MUTEX_RECURSIVE); + tor_assert(0==pthread_attr_init(&attr_detached)); + tor_assert(0==pthread_attr_setdetachstate(&attr_detached, 1)); threads_initialized = 1; set_main_thread(); } @@ -3157,3 +3450,139 @@ 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 0; +} + +#ifdef TOR_UNIT_TESTS +/** Delay for <b>msec</b> milliseconds. Only used in tests. */ +void +tor_sleep_msec(int msec) +{ +#ifdef _WIN32 + Sleep(msec); +#elif defined(HAVE_USLEEP) + sleep(msec / 1000); + /* Some usleep()s hate sleeping more than 1 sec */ + usleep((msec % 1000) * 1000); +#elif defined(HAVE_SYS_SELECT_H) + struct timeval tv = { msec / 1000, (msec % 1000) * 1000}; + select(0, NULL, NULL, NULL, &tv); +#else + sleep(CEIL_DIV(msec, 1000)); +#endif +} +#endif + |