aboutsummaryrefslogtreecommitdiff
path: root/src/common/compat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/compat.c')
-rw-r--r--src/common/compat.c274
1 files changed, 232 insertions, 42 deletions
diff --git a/src/common/compat.c b/src/common/compat.c
index b2dab5c341..0fb169b734 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -307,6 +307,100 @@ tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
return r;
}
+/**
+ * Portable asprintf implementation. Does a printf() into a newly malloc'd
+ * string. Sets *<b>strp</b> to this string, and returns its length (not
+ * including the terminating NUL character).
+ *
+ * You can treat this function as if its implementation were something like
+ <pre>
+ char buf[_INFINITY_];
+ tor_snprintf(buf, sizeof(buf), fmt, args);
+ *strp = tor_strdup(buf);
+ return strlen(*strp):
+ </pre>
+ * Where _INFINITY_ is an imaginary constant so big that any string can fit
+ * into it.
+ */
+int
+tor_asprintf(char **strp, const char *fmt, ...)
+{
+ int r;
+ va_list args;
+ va_start(args, fmt);
+ r = tor_vasprintf(strp, fmt, args);
+ va_end(args);
+ if (!*strp || r < 0) {
+ log_err(LD_BUG, "Internal error in asprintf");
+ tor_assert(0);
+ }
+ return r;
+}
+
+/**
+ * Portable vasprintf implementation. Does a printf() into a newly malloc'd
+ * string. Differs from regular vasprintf in the same ways that
+ * tor_asprintf() differs from regular asprintf.
+ */
+int
+tor_vasprintf(char **strp, const char *fmt, va_list args)
+{
+ /* use a temporary variable in case *strp is in args. */
+ char *strp_tmp=NULL;
+#ifdef HAVE_VASPRINTF
+ /* If the platform gives us one, use it. */
+ int r = vasprintf(&strp_tmp, fmt, args);
+ if (r < 0)
+ *strp = NULL;
+ else
+ *strp = strp_tmp;
+ return r;
+#elif defined(_MSC_VER)
+ /* On Windows, _vsnprintf won't tell us the length of the string if it
+ * overflows, so we need to use _vcsprintf to tell how much to allocate */
+ int len, r;
+ char *res;
+ len = _vscprintf(fmt, args);
+ if (len < 0) {
+ *strp = NULL;
+ return -1;
+ }
+ strp_tmp = tor_malloc(len + 1);
+ r = _vsnprintf(strp_tmp, len+1, fmt, args);
+ if (r != len) {
+ tor_free(strp_tmp);
+ *strp = NULL;
+ return -1;
+ }
+ *strp = strp_tmp;
+ return len;
+#else
+ /* Everywhere else, we have a decent vsnprintf that tells us how many
+ * characters we need. We give it a try on a short buffer first, since
+ * it might be nice to avoid the second vsnprintf call.
+ */
+ char buf[128];
+ int len, r;
+ va_list tmp_args;
+ va_copy(tmp_args, args);
+ len = vsnprintf(buf, sizeof(buf), fmt, tmp_args);
+ va_end(tmp_args);
+ if (len < (int)sizeof(buf)) {
+ *strp = tor_strdup(buf);
+ return len;
+ }
+ strp_tmp = tor_malloc(len+1);
+ r = vsnprintf(strp_tmp, len+1, fmt, args);
+ if (r != len) {
+ tor_free(strp_tmp);
+ *strp = NULL;
+ return -1;
+ }
+ *strp = strp_tmp;
+ return len;
+#endif
+}
+
/** Given <b>hlen</b> bytes at <b>haystack</b> and <b>nlen</b> bytes at
* <b>needle</b>, return a pointer to the first occurrence of the needle
* within the haystack, or NULL if there is no such occurrence.
@@ -398,6 +492,37 @@ const char TOR_TOLOWER_TABLE[256] = {
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,
};
+/** Implementation of strtok_r for platforms whose coders haven't figured out
+ * how to write one. Hey guys! You can use this code here for free! */
+char *
+tor_strtok_r_impl(char *str, const char *sep, char **lasts)
+{
+ char *cp, *start;
+ if (str)
+ start = cp = *lasts = str;
+ else if (!*lasts)
+ return NULL;
+ else
+ start = cp = *lasts;
+
+ tor_assert(*sep);
+ if (sep[1]) {
+ while (*cp && !strchr(sep, *cp))
+ ++cp;
+ } else {
+ tor_assert(strlen(sep) == 1);
+ cp = strchr(cp, *sep);
+ }
+
+ if (!cp || !*cp) {
+ *lasts = NULL;
+ } else {
+ *cp++ = '\0';
+ *lasts = cp;
+ }
+ return start;
+}
+
#ifdef MS_WINDOWS
/** Take a filename and return a pointer to its final element. This
* function is called on __FILE__ to fix a MSVC nit where __FILE__
@@ -449,8 +574,8 @@ get_uint32(const char *cp)
return v;
}
/**
- * Read a 32-bit value beginning at <b>cp</b>. Equivalent to
- * *(uint32_t*)(cp), but will not cause segfaults on platforms that forbid
+ * Read a 64-bit value beginning at <b>cp</b>. Equivalent to
+ * *(uint64_t*)(cp), but will not cause segfaults on platforms that forbid
* unaligned memory access.
*/
uint64_t
@@ -978,9 +1103,6 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
#if defined(CYGWIN) || defined(__CYGWIN__)
const char *platform = "Cygwin";
const unsigned long MAX_CONNECTIONS = 3200;
-#elif defined(IPHONE)
- const char *platform = "iPhone";
- const unsigned long MAX_CONNECTIONS = 9999;
#elif defined(MS_WINDOWS)
const char *platform = "Windows";
const unsigned long MAX_CONNECTIONS = 15000;
@@ -1577,13 +1699,13 @@ get_uname(void)
#ifdef MS_WINDOWS
OSVERSIONINFOEX info;
int i;
- unsigned int leftover_mask;
const char *plat = NULL;
const char *extra = NULL;
static struct {
unsigned major; unsigned minor; const char *version;
} win_version_table[] = {
- { 6, 0, "Windows \"Longhorn\"" },
+ { 6, 1, "Windows 7" },
+ { 6, 0, "Windows Vista" },
{ 5, 2, "Windows Server 2003" },
{ 5, 1, "Windows XP" },
{ 5, 0, "Windows 2000" },
@@ -1594,25 +1716,6 @@ get_uname(void)
{ 3, 51, "Windows NT 3.51" },
{ 0, 0, NULL }
};
-#ifdef VER_SUITE_BACKOFFICE
- static struct {
- unsigned int mask; const char *str;
- } win_mask_table[] = {
- { VER_SUITE_BACKOFFICE, " {backoffice}" },
- { VER_SUITE_BLADE, " {\"blade\" (2003, web edition)}" },
- { VER_SUITE_DATACENTER, " {datacenter}" },
- { VER_SUITE_ENTERPRISE, " {enterprise}" },
- { VER_SUITE_EMBEDDEDNT, " {embedded}" },
- { VER_SUITE_PERSONAL, " {personal}" },
- { VER_SUITE_SINGLEUSERTS,
- " {terminal services, single user}" },
- { VER_SUITE_SMALLBUSINESS, " {small business}" },
- { VER_SUITE_SMALLBUSINESS_RESTRICTED,
- " {small business, restricted}" },
- { VER_SUITE_TERMINAL, " {terminal services}" },
- { 0, NULL },
- };
-#endif
memset(&info, 0, sizeof(info));
info.dwOSVersionInfoSize = sizeof(info);
if (! GetVersionEx((LPOSVERSIONINFO)&info)) {
@@ -1652,7 +1755,7 @@ get_uname(void)
plat, extra);
} else {
if (info.dwMajorVersion > 6 ||
- (info.dwMajorVersion==6 && info.dwMinorVersion>0))
+ (info.dwMajorVersion==6 && info.dwMinorVersion>1))
tor_snprintf(uname_result, sizeof(uname_result),
"Very recent version of Windows [major=%d,minor=%d] %s",
(int)info.dwMajorVersion,(int)info.dwMinorVersion,
@@ -1671,18 +1774,6 @@ get_uname(void)
} else if (info.wProductType == VER_NT_WORKSTATION) {
strlcat(uname_result, " [workstation]", sizeof(uname_result));
}
- leftover_mask = info.wSuiteMask;
- for (i = 0; win_mask_table[i].mask; ++i) {
- if (info.wSuiteMask & win_mask_table[i].mask) {
- strlcat(uname_result, win_mask_table[i].str, sizeof(uname_result));
- leftover_mask &= ~win_mask_table[i].mask;
- }
- }
- if (leftover_mask) {
- size_t len = strlen(uname_result);
- tor_snprintf(uname_result+len, sizeof(uname_result)-len,
- " {0x%x}", info.wSuiteMask);
- }
#endif
#else
strlcpy(uname_result, "Unknown platform", sizeof(uname_result));
@@ -1792,7 +1883,6 @@ spawn_exit(void)
* call _exit, not exit, from child processes. */
_exit(0);
#endif
-
}
/** Set *timeval to the current time of day. On error, log and terminate.
@@ -2013,6 +2103,8 @@ tor_mutex_new(void)
void
tor_mutex_free(tor_mutex_t *m)
{
+ if (!m)
+ return;
tor_mutex_uninit(m);
tor_free(m);
}
@@ -2040,7 +2132,8 @@ tor_cond_new(void)
void
tor_cond_free(tor_cond_t *cond)
{
- tor_assert(cond);
+ if (!cond)
+ return;
if (pthread_cond_destroy(&cond->cond)) {
log_warn(LD_GENERAL,"Error freeing condition: %s", strerror(errno));
return;
@@ -2097,7 +2190,8 @@ tor_cond_new(void)
void
tor_cond_free(tor_cond_t *cond)
{
- tor_assert(cond);
+ if (!cond)
+ return;
DeleteCriticalSection(&cond->mutex);
/* XXXX notify? */
smartlist_free(cond->events);
@@ -2173,6 +2267,102 @@ tor_threads_init(void)
}
#endif
+#if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK)
+/** Attempt to raise the current and max rlimit to infinity for our process.
+ * This only needs to be done once and can probably only be done when we have
+ * not already dropped privileges.
+ */
+static int
+tor_set_max_memlock(void)
+{
+ /* Future consideration for Windows is probably SetProcessWorkingSetSize
+ * This is similar to setting the memory rlimit of RLIMIT_MEMLOCK
+ * http://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx
+ */
+
+ struct rlimit limit;
+ int ret;
+
+ /* Do we want to report current limits first? This is not really needed. */
+ ret = getrlimit(RLIMIT_MEMLOCK, &limit);
+ if (ret == -1) {
+ log_warn(LD_GENERAL, "Could not get RLIMIT_MEMLOCK: %s", strerror(errno));
+ return -1;
+ }
+
+ /* RLIM_INFINITY is -1 on some platforms. */
+ limit.rlim_cur = RLIM_INFINITY;
+ limit.rlim_max = RLIM_INFINITY;
+
+ ret = setrlimit(RLIMIT_MEMLOCK, &limit);
+ if (ret == -1) {
+ if (errno == EPERM) {
+ log_warn(LD_GENERAL, "You appear to lack permissions to change memory "
+ "limits. Are you root?");
+ log_warn(LD_GENERAL, "Unable to raise RLIMIT_MEMLOCK: %s",
+ strerror(errno));
+ } else {
+ log_warn(LD_GENERAL, "Could not raise RLIMIT_MEMLOCK: %s",
+ strerror(errno));
+ }
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+/** Attempt to lock all current and all future memory pages.
+ * This should only be called once and while we're privileged.
+ * Like mlockall() we return 0 when we're successful and -1 when we're not.
+ * Unlike mlockall() we return 1 if we've already attempted to lock memory.
+ */
+int
+tor_mlockall(void)
+{
+ static int memory_lock_attempted = 0;
+
+ if (memory_lock_attempted) {
+ return 1;
+ }
+
+ memory_lock_attempted = 1;
+
+ /*
+ * Future consideration for Windows may be VirtualLock
+ * VirtualLock appears to implement mlock() but not mlockall()
+ *
+ * http://msdn.microsoft.com/en-us/library/aa366895(VS.85).aspx
+ */
+
+#if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK)
+ if (tor_set_max_memlock() == 0) {
+ /* Perhaps we only want to log this if we're in a verbose mode? */
+ log_notice(LD_GENERAL, "RLIMIT_MEMLOCK is now set to RLIM_INFINITY.");
+ }
+
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) == 0) {
+ log_notice(LD_GENERAL, "Insecure OS paging is effectively disabled.");
+ return 0;
+ } else {
+ if (errno == ENOSYS) {
+ /* Apple - it's 2009! I'm looking at you. Grrr. */
+ log_notice(LD_GENERAL, "It appears that mlockall() is not available on "
+ "your platform.");
+ } else if (errno == EPERM) {
+ log_notice(LD_GENERAL, "It appears that you lack the permissions to "
+ "lock memory. Are you root?");
+ }
+ log_notice(LD_GENERAL, "Unable to lock all current and future memory "
+ "pages: %s", strerror(errno));
+ return -1;
+ }
+#else
+ log_warn(LD_GENERAL, "Unable to lock memory pages. mlockall() unsupported?");
+ return -1;
+#endif
+}
+
/** Identity of the "main" thread */
static unsigned long main_thread_id = -1;