diff options
Diffstat (limited to 'src/common/compat_libevent.c')
-rw-r--r-- | src/common/compat_libevent.c | 536 |
1 files changed, 0 insertions, 536 deletions
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c deleted file mode 100644 index e60eb148d8..0000000000 --- a/src/common/compat_libevent.c +++ /dev/null @@ -1,536 +0,0 @@ -/* Copyright (c) 2009-2017, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file compat_libevent.c - * \brief Wrappers and utility functions for Libevent. - */ - -#include "orconfig.h" -#include "compat.h" -#define COMPAT_LIBEVENT_PRIVATE -#include "compat_libevent.h" - -#include "crypto_rand.h" - -#include "util.h" -#include "torlog.h" - -#include <event2/event.h> -#include <event2/thread.h> - -/** A string which, if it appears in a libevent log, should be ignored. */ -static const char *suppress_msg = NULL; -/** Callback function passed to event_set_log() so we can intercept - * log messages from libevent. */ -STATIC void -libevent_logging_callback(int severity, const char *msg) -{ - char buf[1024]; - size_t n; - if (suppress_msg && strstr(msg, suppress_msg)) - return; - n = strlcpy(buf, msg, sizeof(buf)); - if (n && n < sizeof(buf) && buf[n-1] == '\n') { - buf[n-1] = '\0'; - } - switch (severity) { - case _EVENT_LOG_DEBUG: - log_debug(LD_NOCB|LD_NET, "Message from libevent: %s", buf); - break; - case _EVENT_LOG_MSG: - log_info(LD_NOCB|LD_NET, "Message from libevent: %s", buf); - break; - case _EVENT_LOG_WARN: - log_warn(LD_NOCB|LD_GENERAL, "Warning from libevent: %s", buf); - break; - case _EVENT_LOG_ERR: - log_err(LD_NOCB|LD_GENERAL, "Error from libevent: %s", buf); - break; - default: - log_warn(LD_NOCB|LD_GENERAL, "Message [%d] from libevent: %s", - severity, buf); - break; - } -} -/** Set hook to intercept log messages from libevent. */ -void -configure_libevent_logging(void) -{ - event_set_log_callback(libevent_logging_callback); -} - -/** Ignore any libevent log message that contains <b>msg</b>. */ -void -suppress_libevent_log_msg(const char *msg) -{ - suppress_msg = msg; -} - -/* Wrapper for event_free() that tolerates tor_event_free(NULL) */ -void -tor_event_free_(struct event *ev) -{ - if (ev == NULL) - return; - event_free(ev); -} - -/** Global event base for use by the main thread. */ -static struct event_base *the_event_base = NULL; - -/** - * @defgroup postloop post-loop event helpers - * - * If we're not careful, Libevent can susceptible to infinite event chains: - * one event can activate another, whose callback activates another, whose - * callback activates another, ad infinitum. While this is happening, - * Libevent won't be checking timeouts, socket-based events, signals, and so - * on. - * - * We solve this problem by marking some events as "post-loop". A post-loop - * event behaves like any ordinary event, but any events that _it_ activates - * cannot run until Libevent has checked for other events at least once. - * - * @{ */ - -/** - * An event that stops Libevent from running any more events on the current - * iteration of its loop, until it has re-checked for socket events, signal - * events, timeouts, etc. - */ -static struct event *rescan_mainloop_ev = NULL; - -/** - * Callback to implement rescan_mainloop_ev: it simply exits the mainloop, - * and relies on Tor to re-enter the mainloop since no error has occurred. - */ -static void -rescan_mainloop_cb(evutil_socket_t fd, short events, void *arg) -{ - (void)fd; - (void)events; - struct event_base *the_base = arg; - event_base_loopbreak(the_base); -} - -/** @} */ - -/* 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 /* defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) */ -#endif /* defined(__APPLE__) */ - -/** 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; - - { - int attempts = 0; - struct event_config *cfg; - - ++attempts; - cfg = event_config_new(); - tor_assert(cfg); - - /* 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 (torcfg->num_cpus > 0) - event_config_set_num_cpus_hint(cfg, torcfg->num_cpus); - - /* 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); - - the_event_base = event_base_new_with_config(cfg); - - event_config_free(cfg); - } - - if (!the_event_base) { - /* LCOV_EXCL_START */ - log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue."); - exit(1); // exit ok: libevent is broken. - /* LCOV_EXCL_STOP */ - } - - rescan_mainloop_ev = event_new(the_event_base, -1, 0, - rescan_mainloop_cb, the_event_base); - if (!rescan_mainloop_ev) { - /* LCOV_EXCL_START */ - log_err(LD_GENERAL, "Unable to create rescan event: cannot continue."); - exit(1); // exit ok: libevent is broken. - /* LCOV_EXCL_STOP */ - } - - log_info(LD_GENERAL, - "Initialized libevent version %s using method %s. Good.", - event_get_version(), tor_libevent_get_method()); -} - -/** Return the current Libevent event base that we're set up to use. */ -MOCK_IMPL(struct event_base *, -tor_libevent_get_base, (void)) -{ - tor_assert(the_event_base != NULL); - return the_event_base; -} - -/** Return the name of the Libevent backend we're using. */ -const char * -tor_libevent_get_method(void) -{ - return event_base_get_method(the_event_base); -} - -/** Return a string representation of the version of the currently running - * version of Libevent. */ -const char * -tor_libevent_get_version_str(void) -{ - return event_get_version(); -} - -/** Return a string representation of the version of Libevent that was used -* at compilation time. */ -const char * -tor_libevent_get_header_version_str(void) -{ - return LIBEVENT_VERSION; -} - -/** Represents a timer that's run every N microseconds by Libevent. */ -struct periodic_timer_t { - /** Underlying event used to implement this periodic event. */ - struct event *ev; - /** The callback we'll be invoking whenever the event triggers */ - void (*cb)(struct periodic_timer_t *, void *); - /** User-supplied data for the callback */ - void *data; -}; - -/** Libevent callback to implement a periodic event. */ -static void -periodic_timer_cb(evutil_socket_t fd, short what, void *arg) -{ - periodic_timer_t *timer = arg; - (void) what; - (void) fd; - timer->cb(timer, timer->data); -} - -/** Create and schedule a new timer that will run every <b>tv</b> in - * the event loop of <b>base</b>. When the timer fires, it will - * run the timer in <b>cb</b> with the user-supplied data in <b>data</b>. */ -periodic_timer_t * -periodic_timer_new(struct event_base *base, - const struct timeval *tv, - void (*cb)(periodic_timer_t *timer, void *data), - void *data) -{ - periodic_timer_t *timer; - tor_assert(base); - tor_assert(tv); - tor_assert(cb); - timer = tor_malloc_zero(sizeof(periodic_timer_t)); - if (!(timer->ev = tor_event_new(base, -1, EV_PERSIST, - periodic_timer_cb, timer))) { - tor_free(timer); - return NULL; - } - timer->cb = cb; - timer->data = data; - periodic_timer_launch(timer, tv); - return timer; -} - -/** - * Launch the timer <b>timer</b> to run at <b>tv</b> from now, and every - * <b>tv</b> thereafter. - * - * If the timer is already enabled, this function does nothing. - */ -void -periodic_timer_launch(periodic_timer_t *timer, const struct timeval *tv) -{ - tor_assert(timer); - if (event_pending(timer->ev, EV_TIMEOUT, NULL)) - return; - event_add(timer->ev, tv); -} - -/** - * Disable the provided <b>timer</b>, but do not free it. - * - * You can reenable the same timer later with periodic_timer_launch. - * - * If the timer is already disabled, this function does nothing. - */ -void -periodic_timer_disable(periodic_timer_t *timer) -{ - tor_assert(timer); - (void) event_del(timer->ev); -} - -/** Stop and free a periodic timer */ -void -periodic_timer_free_(periodic_timer_t *timer) -{ - if (!timer) - return; - tor_event_free(timer->ev); - tor_free(timer); -} - -/** - * Type used to represent events that run directly from the main loop, - * either because they are activated from elsewhere in the code, or - * because they have a simple timeout. - * - * We use this type to avoid exposing Libevent's API throughout the rest - * of the codebase. - * - * This type can't be used for all events: it doesn't handle events that - * are triggered by signals or by sockets. - */ -struct mainloop_event_t { - struct event *ev; - void (*cb)(mainloop_event_t *, void *); - void *userdata; -}; - -/** - * Internal: Implements mainloop event using a libevent event. - */ -static void -mainloop_event_cb(evutil_socket_t fd, short what, void *arg) -{ - (void)fd; - (void)what; - mainloop_event_t *mev = arg; - mev->cb(mev, mev->userdata); -} - -/** - * As mainloop_event_cb, but implements a post-loop event. - */ -static void -mainloop_event_postloop_cb(evutil_socket_t fd, short what, void *arg) -{ - (void)fd; - (void)what; - - /* Note that if rescan_mainloop_ev is already activated, - * event_active() will do nothing: only the first post-loop event that - * happens each time through the event loop will cause it to be - * activated. - * - * Because event_active() puts events on a FIFO queue, every event - * that is made active _after_ rescan_mainloop_ev will get its - * callback run after rescan_mainloop_cb is called -- that is, on the - * next iteration of the loop. - */ - event_active(rescan_mainloop_ev, EV_READ, 1); - - mainloop_event_t *mev = arg; - mev->cb(mev, mev->userdata); -} - -/** - * Helper for mainloop_event_new() and mainloop_event_postloop_new(). - */ -static mainloop_event_t * -mainloop_event_new_impl(int postloop, - void (*cb)(mainloop_event_t *, void *), - void *userdata) -{ - tor_assert(cb); - - struct event_base *base = tor_libevent_get_base(); - mainloop_event_t *mev = tor_malloc_zero(sizeof(mainloop_event_t)); - mev->ev = tor_event_new(base, -1, 0, - postloop ? mainloop_event_postloop_cb : mainloop_event_cb, - mev); - tor_assert(mev->ev); - mev->cb = cb; - mev->userdata = userdata; - return mev; -} - -/** - * Create and return a new mainloop_event_t to run the function <b>cb</b>. - * - * When run, the callback function will be passed the mainloop_event_t - * and <b>userdata</b> as its arguments. The <b>userdata</b> pointer - * must remain valid for as long as the mainloop_event_t event exists: - * it is your responsibility to free it. - * - * The event is not scheduled by default: Use mainloop_event_activate() - * or mainloop_event_schedule() to make it run. - */ -mainloop_event_t * -mainloop_event_new(void (*cb)(mainloop_event_t *, void *), - void *userdata) -{ - return mainloop_event_new_impl(0, cb, userdata); -} - -/** - * As mainloop_event_new(), but create a post-loop event. - * - * A post-loop event behaves like any ordinary event, but any events - * that _it_ activates cannot run until Libevent has checked for other - * events at least once. - */ -mainloop_event_t * -mainloop_event_postloop_new(void (*cb)(mainloop_event_t *, void *), - void *userdata) -{ - return mainloop_event_new_impl(1, cb, userdata); -} - -/** - * Schedule <b>event</b> to run in the main loop, immediately. If it is - * not scheduled, it will run anyway. If it is already scheduled to run - * later, it will run now instead. This function will have no effect if - * the event is already scheduled to run. - * - * This function may only be called from the main thread. - */ -void -mainloop_event_activate(mainloop_event_t *event) -{ - tor_assert(event); - event_active(event->ev, EV_READ, 1); -} - -/** Schedule <b>event</b> to run in the main loop, after a delay of <b>tv</b>. - * - * If the event is scheduled for a different time, cancel it and run - * after this delay instead. If the event is currently pending to run - * <em>now</b>, has no effect. - * - * Do not call this function with <b>tv</b> == NULL -- use - * mainloop_event_activate() instead. - * - * This function may only be called from the main thread. - */ -int -mainloop_event_schedule(mainloop_event_t *event, const struct timeval *tv) -{ - tor_assert(event); - if (BUG(tv == NULL)) { - // LCOV_EXCL_START - mainloop_event_activate(event); - return 0; - // LCOV_EXCL_STOP - } - return event_add(event->ev, tv); -} - -/** Cancel <b>event</b> if it is currently active or pending. (Do nothing if - * the event is not currently active or pending.) */ -void -mainloop_event_cancel(mainloop_event_t *event) -{ - if (!event) - return; - (void) event_del(event->ev); -} - -/** Cancel <b>event</b> and release all storage associated with it. */ -void -mainloop_event_free_(mainloop_event_t *event) -{ - if (!event) - return; - tor_event_free(event->ev); - memset(event, 0xb8, sizeof(*event)); - tor_free(event); -} - -int -tor_init_libevent_rng(void) -{ - int rv = 0; - char buf[256]; - if (evutil_secure_rng_init() < 0) { - rv = -1; - } - crypto_rand(buf, 32); -#ifdef HAVE_EVUTIL_SECURE_RNG_ADD_BYTES - evutil_secure_rng_add_bytes(buf, 32); -#endif - evutil_secure_rng_get_bytes(buf, sizeof(buf)); - return rv; -} - -/** - * Un-initialize libevent in preparation for an exit - */ -void -tor_libevent_free_all(void) -{ - tor_event_free(rescan_mainloop_ev); - if (the_event_base) - event_base_free(the_event_base); - the_event_base = NULL; -} - -/** - * Run the event loop for the provided event_base, handling events until - * something stops it. If <b>once</b> is set, then just poll-and-run - * once, then exit. Return 0 on success, -1 if an error occurred, or 1 - * if we exited because no events were pending or active. - * - * This isn't reentrant or multithreaded. - */ -int -tor_libevent_run_event_loop(struct event_base *base, int once) -{ - const int flags = once ? EVLOOP_ONCE : 0; - return event_base_loop(base, flags); -} - -/** Tell the event loop to exit after <b>delay</b>. If <b>delay</b> is NULL, - * instead exit after we're done running the currently active events. */ -void -tor_libevent_exit_loop_after_delay(struct event_base *base, - const struct timeval *delay) -{ - event_base_loopexit(base, delay); -} - -/** Tell the event loop to exit after running whichever callback is currently - * active. */ -void -tor_libevent_exit_loop_after_callback(struct event_base *base) -{ - event_base_loopbreak(base); -} - -#if defined(TOR_UNIT_TESTS) -/** For testing: called post-fork to make libevent reinitialize - * kernel structures. */ -void -tor_libevent_postfork(void) -{ - int r = event_reinit(tor_libevent_get_base()); - tor_assert(r == 0); -} -#endif /* defined(TOR_UNIT_TESTS) */ - |