/** Return the le_version_t for the version of libevent specified in the * string <b>v</b>. If the version is very new or uses an unrecognized * version, format, return LE_OTHER. */ static le_version_t tor_decode_libevent_version(const char *v) { unsigned major, minor, patchlevel; char c, e, extra; int fields; /* Try the new preferred "1.4.11-stable" format. * Also accept "1.4.14b-stable". */ fields = tor_sscanf(v, "%u.%u.%u%c%c", &major, &minor, &patchlevel, &c, &e); if (fields == 3 || ((fields == 4 || fields == 5 ) && (c == '-' || c == '_')) || (fields == 5 && TOR_ISALPHA(c) && (e == '-' || e == '_'))) { return V(major,minor,patchlevel); } /* Try the old "1.3e" format. */ fields = tor_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; }
/** 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 { struct event_config *cfg = event_config_new(); tor_assert(cfg); /* In 0.2.2, we don't use locking at all. Telling Libevent not to try to * turn it on can avoid a needless socketpair() attempt. */ event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK); the_event_base = event_base_new_with_config(cfg); event_config_free(cfg); } #else the_event_base = event_init(); #endif if (!the_event_base) { log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue."); exit(1); } #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 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; }
/** * 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); /* It would be better to disable known-buggy methods rather than warning * about them. But the problem is that with older versions of Libevent, * it's not trivial to get them to change their methods once they're * initialized... and with newer versions of Libevent, they aren't actually * broken. But we should revisit this if we ever find a post-1.4 version * of Libevent where we need to disable a given method. */ 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; }
/** Initialize the Libevent library and set up the event base. */ void tor_libevent_initialize(tor_libevent_cfg *torcfg) { tor_assert(the_event_base == NULL); /* some paths below don't use torcfg, so avoid unused variable warnings */ (void)torcfg; #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 { int attempts = 0; int using_threads; struct event_config *cfg; retry: ++attempts; using_threads = 0; cfg = event_config_new(); tor_assert(cfg); #if defined(_WIN32) && defined(USE_BUFFEREVENTS) if (! torcfg->disable_iocp) { evthread_use_windows_threads(); event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP); using_iocp_bufferevents = 1; using_threads = 1; } else { using_iocp_bufferevents = 0; } #endif if (!using_threads) { /* Telling Libevent not to try to turn locking on can avoid a needless * socketpair() attempt. */ event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK); } #if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,0,7) if (torcfg->num_cpus > 0) event_config_set_num_cpus_hint(cfg, torcfg->num_cpus); #endif #if LIBEVENT_VERSION_NUMBER >= V(2,0,9) /* We can enable changelist support with epoll, since we don't give * Libevent any dup'd fds. This lets us avoid some syscalls. */ event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST); #endif the_event_base = event_base_new_with_config(cfg); event_config_free(cfg); if (using_threads && the_event_base == NULL && attempts < 2) { /* This could be a socketpair() failure, which can happen sometimes on * windows boxes with obnoxious firewall rules. Downgrade and try * again. */ #if defined(_WIN32) && defined(USE_BUFFEREVENTS) if (torcfg->disable_iocp == 0) { log_warn(LD_GENERAL, "Unable to initialize Libevent. Trying again " "with IOCP disabled."); } else #endif { log_warn(LD_GENERAL, "Unable to initialize Libevent. Trying again."); } torcfg->disable_iocp = 1; goto retry; } } #else the_event_base = event_init(); #endif if (!the_event_base) { log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue."); exit(1); } #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 #ifdef USE_BUFFEREVENTS tor_libevent_set_tick_timeout(torcfg->msec_per_tick); #endif }