aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2009-06-04 03:16:26 -0400
committerNick Mathewson <nickm@torproject.org>2009-06-12 14:27:52 -0400
commitc0af3cdfb6816659031fed5a1b8feec5d457b608 (patch)
tree1241f7190e1d323f5777881fedb0505c538a9586 /src/common
parente5b88dc83fb44622f2b0f9c0c242907d1c02311f (diff)
downloadtor-c0af3cdfb6816659031fed5a1b8feec5d457b608.tar.gz
tor-c0af3cdfb6816659031fed5a1b8feec5d457b608.zip
Move the Libvent setup logic into compat_libevent from config.
This has been some pretty ugly and voodoo-laden code. I've tried to clean it up a bit, but more work probably remains.
Diffstat (limited to 'src/common')
-rw-r--r--src/common/compat_libevent.c305
-rw-r--r--src/common/compat_libevent.h11
2 files changed, 313 insertions, 3 deletions
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index d2ef112079..ce9f6a0fd3 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -23,6 +23,21 @@
#include <event.h>
#endif
+/** A number representing a version of Libevent.
+
+ This is a 4-byte number, with the first three bytes representing the
+ major, minor, and patchlevel respectively of the the library. The fourth
+ byte is unused.
+
+ This is equivalent to the format of LIBEVENT_VERSION_NUMBER on Libevent
+ 2.0.1 or later. For versions of Libevent before 1.4.0, which followed the
+ format of "1.0, 1.0a, 1.0b", we define 1.0 to be equivalent to 1.0.0, 1.0a
+ to be equivalent to 1.0.1, and so on.
+*/
+typedef uint32_t le_version_t;
+
+static le_version_t tor_get_libevent_version(const char **v_out);
+
#ifdef HAVE_EVENT_SET_LOG_CALLBACK
/** A string which, if it appears in a libevent log, should be ignored. */
static const char *suppress_msg = NULL;
@@ -119,16 +134,51 @@ tor_event_free(struct event *ev)
/** Global event base for use by the main thread. */
struct event_base *the_event_base = NULL;
+
+/* This is what passes for version detection on OSX. We set
+ * MACOSX_KQUEUE_IS_BROKEN to true iff we're on a version of OSX before
+ * 10.4.0 (aka 1040). */
+#ifdef __APPLE__
+#ifdef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
+#define MACOSX_KQUEUE_IS_BROKEN \
+ (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1040)
+#else
+#define MACOSX_KQUEUE_IS_BROKEN 0
+#endif
+#endif
+
/** Initialize the Libevent library and set up the event base. */
void
tor_libevent_initialize(void)
{
tor_assert(the_event_base == NULL);
+
+#ifdef __APPLE__
+ if (MACOSX_KQUEUE_IS_BROKEN ||
+ tor_get_libevent_version(NULL) < V_OLD(1,1,'b')) {
+ setenv("EVENT_NOKQUEUE","1",1);
+ }
+#endif
+
#ifdef HAVE_EVENT2_EVENT_H
the_event_base = event_base_new();
#else
the_event_base = event_init();
#endif
+
+#if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
+ /* Making this a NOTICE for now so we can link bugs to a libevent versions
+ * or methods better. */
+ log(LOG_NOTICE, LD_GENERAL,
+ "Initialized libevent version %s using method %s. Good.",
+ event_get_version(), tor_libevent_get_method());
+#else
+ log(LOG_NOTICE, LD_GENERAL,
+ "Initialized old libevent (version 1.0b or earlier).");
+ log(LOG_WARN, LD_GENERAL,
+ "You have a *VERY* old version of libevent. It is likely to be buggy; "
+ "please build Tor with a more recent version.");
+#endif
}
/** Return the current Libevent event base that we're set up to use. */
@@ -151,3 +201,258 @@ tor_libevent_get_method(void)
#endif
}
+/* Macros: returns the number of a libevent version. */
+#define V(major, minor, patch) \
+ (((major) << 24) | ((minor) << 16) | ((patch) << 8))
+#define V_OLD(major, minor, patch) \
+ V((major), (minor), (patch)-'a'+1)
+
+#define LE_OLD V(0,0,0)
+#define LE_OTHER V(0,0,99)
+
+/** Return the le_version_t for the current version of libevent. If the
+ * version is very new, return LE_OTHER. If the version is so old that it
+ * doesn't support event_get_version(), return LE_OLD. DOCDOC */
+static le_version_t
+tor_decode_libevent_version(const char *v)
+{
+ unsigned major, minor, patchlevel;
+ char c, extra;
+ int fields;
+
+ /* Try the new preferred "1.4.11-stable" format. */
+ fields = sscanf(v, "%u.%u.%u%c", &major, &minor, &patchlevel, &c);
+ if (fields == 3 ||
+ (fields == 4 && (c == '-' || c == '_'))) {
+ return V(major,minor,patchlevel);
+ }
+
+ /* Try the old "1.3e" format. */
+ fields = sscanf(v, "%u.%u%c%c", &major, &minor, &c, &extra);
+ if (fields == 3 && TOR_ISALPHA(c)) {
+ return V_OLD(major, minor, c);
+ } else if (fields == 2) {
+ return V(major, minor, 0);
+ }
+
+ return LE_OTHER;
+}
+
+/** Return an integer representing the binary interface of a Libevent library.
+ * Two different versions with different numbers are sure not to be binary
+ * compatible. Two different versions with the same numbers have a decent
+ * chance of binary compatibility.*/
+static int
+le_versions_compatibility(le_version_t v)
+{
+ if (v == LE_OTHER)
+ return 0;
+ if (v < V_OLD(1,0,'c'))
+ return 1;
+ else if (v < V(1,4,0))
+ return 2;
+ else if (v < V(1,4,99))
+ return 3;
+ else if (v < V(2,0,1))
+ return 4;
+ else /* Everything 2.0 and later should be compatible. */
+ return 5;
+}
+
+/** Return the version number of the currently running version of Libevent.
+ See le_version_t for info on the format.
+ */
+static le_version_t
+tor_get_libevent_version(const char **v_out)
+{
+ const char *v;
+ le_version_t r;
+#if defined(HAVE_EVENT_GET_VERSION_NUMBER)
+ v = event_get_version();
+ r = event_get_version_number();
+#elif defined (HAVE_EVENT_GET_VERSION)
+ v = event_get_version();
+ r = tor_decode_libevent_version(v);
+#else
+ v = "pre-1.0c";
+ r = LE_OLD;
+#endif
+ if (v_out)
+ *v_out = v;
+ return r;
+}
+
+/** Return a string representation of the version of the currently running
+ * version of Libevent. */
+const char *
+tor_libevent_get_version_str(void)
+{
+#ifdef HAVE_EVENT_GET_VERSION
+ return event_get_version();
+#else
+ return "pre-1.0c";
+#endif
+}
+
+/**
+ * Compare the current Libevent method and version to a list of versions
+ * which are known not to work. Warn the user as appropriate.
+ */
+void
+tor_check_libevent_version(const char *m, int server,
+ const char **badness_out)
+{
+ int buggy = 0, iffy = 0, slow = 0, thread_unsafe = 0;
+ le_version_t version;
+ const char *v = NULL;
+ const char *badness = NULL;
+ const char *sad_os = "";
+
+ version = tor_get_libevent_version(&v);
+
+ /* XXX Would it be worthwhile disabling the methods that we know
+ * are buggy, rather than just warning about them and then proceeding
+ * to use them? If so, we should probably not wrap this whole thing
+ * in HAVE_EVENT_GET_VERSION and HAVE_EVENT_GET_METHOD. -RD */
+ /* XXXX The problem is that it's not trivial to get libevent to change it's
+ * method once it's initialized, and it's not trivial to tell what method it
+ * will use without initializing it. I guess we could preemptively disable
+ * buggy libevent modes based on the version _before_ initializing it,
+ * though, but then there's no good way (afaict) to warn "I would have used
+ * kqueue, but instead I'm using select." -NM */
+ /* XXXX022 revist the above; it is fixable now. */
+ if (!strcmp(m, "kqueue")) {
+ if (version < V_OLD(1,1,'b'))
+ buggy = 1;
+ } else if (!strcmp(m, "epoll")) {
+ if (version < V(1,1,0))
+ iffy = 1;
+ } else if (!strcmp(m, "poll")) {
+ if (version < V_OLD(1,0,'e'))
+ buggy = 1;
+ if (version < V(1,1,0))
+ slow = 1;
+ } else if (!strcmp(m, "select")) {
+ if (version < V(1,1,0))
+ slow = 1;
+ } else if (!strcmp(m, "win32")) {
+ if (version < V_OLD(1,1,'b'))
+ buggy = 1;
+ }
+
+ /* Libevent versions before 1.3b do very badly on operating systems with
+ * user-space threading implementations. */
+#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ if (server && version < V_OLD(1,3,'b'))
+ thread_unsafe = 1;
+ sad_os = "BSD variants";
+ }
+#elif defined(__APPLE__) || defined(__darwin__)
+ if (server && version < V_OLD(1,3,'b'))
+ thread_unsafe = 1;
+ sad_os = "Mac OS X";
+ }
+#endif
+
+ if (thread_unsafe) {
+ log(LOG_WARN, LD_GENERAL,
+ "Libevent version %s often crashes when running a Tor server with %s. "
+ "Please use the latest version of libevent (1.3b or later)",v,sad_os);
+ badness = "BROKEN";
+ } else if (buggy) {
+ log(LOG_WARN, LD_GENERAL,
+ "There are serious bugs in using %s with libevent %s. "
+ "Please use the latest version of libevent.", m, v);
+ badness = "BROKEN";
+ } else if (iffy) {
+ log(LOG_WARN, LD_GENERAL,
+ "There are minor bugs in using %s with libevent %s. "
+ "You may want to use the latest version of libevent.", m, v);
+ badness = "BUGGY";
+ } else if (slow && server) {
+ log(LOG_WARN, LD_GENERAL,
+ "libevent %s can be very slow with %s. "
+ "When running a server, please use the latest version of libevent.",
+ v,m);
+ badness = "SLOW";
+ }
+
+ *badness_out = badness;
+}
+
+#if defined(LIBEVENT_VERSION)
+#define HEADER_VERSION LIBEVENT_VERSION
+#elif defined(_EVENT_VERSION)
+#define HEADER_VERSION _EVENT_VERSION
+#endif
+
+/** See whether the headers we were built against differ from the library we
+ * linked against so much that we're likely to crash. If so, warn the
+ * user. */
+void
+tor_check_libevent_header_compatibility(void)
+{
+ (void) le_versions_compatibility;
+ (void) tor_decode_libevent_version;
+
+ /* In libevent versions before 2.0, it's hard to keep binary compatibility
+ * between upgrades, and unpleasant to detect when the version we compiled
+ * against is unlike the version we have linked against. Here's how. */
+#if defined(HEADER_VERSION) && defined(HAVE_EVENT_GET_VERSION)
+ /* We have a header-file version and a function-call version. Easy. */
+ if (strcmp(HEADER_VERSION, event_get_version())) {
+ le_version_t v1, v2;
+ int compat1 = -1, compat2 = -1;
+ int verybad;
+ v1 = tor_decode_libevent_version(HEADER_VERSION);
+ v2 = tor_decode_libevent_version(event_get_version());
+ compat1 = le_versions_compatibility(v1);
+ compat2 = le_versions_compatibility(v2);
+
+ verybad = compat1 != compat2;
+
+ log(verybad ? LOG_WARN : LOG_NOTICE,
+ LD_GENERAL, "We were compiled with headers from version %s "
+ "of Libevent, but we're using a Libevent library that says it's "
+ "version %s.", HEADER_VERSION, event_get_version());
+ if (verybad)
+ log_warn(LD_GENERAL, "This will almost certainly make Tor crash.");
+ else
+ log_info(LD_GENERAL, "I think these versions are binary-compatible.");
+ }
+#elif defined(HAVE_EVENT_GET_VERSION)
+ /* event_get_version but no _EVENT_VERSION. We might be in 1.4.0-beta or
+ earlier, where that's normal. To see whether we were compiled with an
+ earlier version, let's see whether the struct event defines MIN_HEAP_IDX.
+ */
+#ifdef HAVE_STRUCT_EVENT_MIN_HEAP_IDX
+ /* The header files are 1.4.0-beta or later. If the version is not
+ * 1.4.0-beta, we are incompatible. */
+ {
+ if (strcmp(event_get_version(), "1.4.0-beta")) {
+ log_warn(LD_GENERAL, "It's a little hard to tell, but you seem to have "
+ "Libevent 1.4.0-beta header files, whereas you have linked "
+ "against Libevent %s. This will probably make Tor crash.",
+ event_get_version());
+ }
+ }
+#else
+ /* Our headers are 1.3e or earlier. If the library version is not 1.4.x or
+ later, we're probably fine. */
+ {
+ const char *v = event_get_version();
+ if ((v[0] == '1' && v[2] == '.' && v[3] > '3') || v[0] > '1') {
+ log_warn(LD_GENERAL, "It's a little hard to tell, but you seem to have "
+ "Libevent header file from 1.3e or earlier, whereas you have "
+ "linked against Libevent %s. This will probably make Tor "
+ "crash.", event_get_version());
+ }
+ }
+#endif
+
+#elif defined(HEADER_VERSION)
+#warn "_EVENT_VERSION is defined but not get_event_version(): Libevent is odd."
+#else
+ /* Your libevent is ancient. */
+#endif
+}
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index a95285f460..5482c479fe 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -6,10 +6,12 @@
#include "orconfig.h"
+struct event;
+struct event_base;
+
#ifdef HAVE_EVENT2_EVENT_H
-#include <event2/event.h>
+#include <event2/util.h>
#else
-#include <event.h>
#define evutil_socket_t int
#endif
@@ -34,6 +36,9 @@ void tor_event_free(struct event *ev);
void tor_libevent_initialize(void);
struct event_base *tor_libevent_get_base(void);
const char *tor_libevent_get_method(void);
+void tor_check_libevent_version(const char *m, int server,
+ const char **badness_out);
+void tor_check_libevent_header_compatibility(void);
+const char *tor_libevent_get_version_str(void);
#endif
-