/* Copyright (c) 2015-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file periodic.c * * \brief Generic backend for handling periodic events. * * The events in this module are used by main.c to track items that need * to fire once every N seconds, possibly picking a new interval each time * that they fire. See periodic_events[] in main.c for examples. */ #include "or.h" #include "compat_libevent.h" #include "config.h" #include "periodic.h" /** We disable any interval greater than this number of seconds, on the * grounds that it is probably an absolute time mistakenly passed in as a * relative time. */ static const int MAX_INTERVAL = 10 * 365 * 86400; /** Set the event event to run in next_interval seconds from * now. */ static void periodic_event_set_interval(periodic_event_item_t *event, time_t next_interval) { tor_assert(next_interval < MAX_INTERVAL); struct timeval tv; tv.tv_sec = next_interval; tv.tv_usec = 0; mainloop_event_schedule(event->ev, &tv); } /** Wraps dispatches for periodic events, data will be a pointer to the * event that needs to be called */ static void periodic_event_dispatch(mainloop_event_t *ev, void *data) { periodic_event_item_t *event = data; tor_assert(ev == event->ev); time_t now = time(NULL); const or_options_t *options = get_options(); // log_debug(LD_GENERAL, "Dispatching %s", event->name); int r = event->fn(now, options); int next_interval = 0; /* update the last run time if action was taken */ if (r==0) { log_err(LD_BUG, "Invalid return value for periodic event from %s.", event->name); tor_assert(r != 0); } else if (r > 0) { event->last_action_time = now; /* If the event is meant to happen after ten years, that's likely * a bug, and somebody gave an absolute time rather than an interval. */ tor_assert(r < MAX_INTERVAL); next_interval = r; } else { /* no action was taken, it is likely a precondition failed, * we should reschedule for next second incase the precondition * passes then */ next_interval = 1; } // log_debug(LD_GENERAL, "Scheduling %s for %d seconds", event->name, // next_interval); struct timeval tv = { next_interval , 0 }; mainloop_event_schedule(ev, &tv); } /** Schedules event to run as soon as possible from now. */ void periodic_event_reschedule(periodic_event_item_t *event) { /* Don't reschedule a disabled event. */ if (periodic_event_is_enabled(event)) { periodic_event_set_interval(event, 1); } } /** Initializes the libevent backend for a periodic event. */ void periodic_event_setup(periodic_event_item_t *event) { if (event->ev) { /* Already setup? This is a bug */ log_err(LD_BUG, "Initial dispatch should only be done once."); tor_assert(0); } event->ev = mainloop_event_new(periodic_event_dispatch, event); tor_assert(event->ev); } /** Handles initial dispatch for periodic events. It should happen 1 second * after the events are created to mimic behaviour before #3199's refactor */ void periodic_event_launch(periodic_event_item_t *event) { if (! event->ev) { /* Not setup? This is a bug */ log_err(LD_BUG, "periodic_event_launch without periodic_event_setup"); tor_assert(0); } /* Event already enabled? This is a bug */ if (periodic_event_is_enabled(event)) { log_err(LD_BUG, "periodic_event_launch on an already enabled event"); tor_assert(0); } // Initial dispatch periodic_event_dispatch(event->ev, event); event->enabled = 1; } /** Release all storage associated with event */ void periodic_event_destroy(periodic_event_item_t *event) { if (!event) return; mainloop_event_free(event->ev); event->last_action_time = 0; event->enabled = 0; }