LCOV - code coverage report
Current view: top level - core/mainloop - periodic.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 74 120 61.7 %
Date: 2021-11-24 03:28:48 Functions: 12 16 75.0 %

          Line data    Source code
       1             : /* Copyright (c) 2015-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file periodic.c
       6             :  *
       7             :  * \brief Generic backend for handling periodic events.
       8             :  *
       9             :  * The events in this module are used to track items that need
      10             :  * to fire once every N seconds, possibly picking a new interval each time
      11             :  * that they fire.  See periodic_events[] in mainloop.c for examples.
      12             :  *
      13             :  * This module manages a global list of periodic_event_item_t objects,
      14             :  * each corresponding to a single event.  To register an event, pass it to
      15             :  * periodic_events_register() when initializing your subsystem.
      16             :  *
      17             :  * Registering an event makes the periodic event subsystem know about it, but
      18             :  * doesn't cause the event to get created immediately.  Before the event can
      19             :  * be started, periodic_event_connect_all() must be called by mainloop.c to
      20             :  * connect all the events to Libevent.
      21             :  *
      22             :  * We expect that periodic_event_item_t objects will be statically allocated;
      23             :  * we set them up and tear them down here, but we don't take ownership of
      24             :  * them.
      25             :  */
      26             : 
      27             : #include "core/or/or.h"
      28             : #include "lib/evloop/compat_libevent.h"
      29             : #include "app/config/config.h"
      30             : #include "core/mainloop/mainloop.h"
      31             : #include "core/mainloop/periodic.h"
      32             : 
      33             : /** We disable any interval greater than this number of seconds, on the
      34             :  * grounds that it is probably an absolute time mistakenly passed in as a
      35             :  * relative time.
      36             :  */
      37             : static const int MAX_INTERVAL = 10 * 365 * 86400;
      38             : 
      39             : /**
      40             :  * Global list of periodic events that have been registered with
      41             :  * <b>periodic_event_register</b>.
      42             :  **/
      43             : static smartlist_t *the_periodic_events = NULL;
      44             : 
      45             : /** Set the event <b>event</b> to run in <b>next_interval</b> seconds from
      46             :  * now. */
      47             : static void
      48           0 : periodic_event_set_interval(periodic_event_item_t *event,
      49             :                             time_t next_interval)
      50             : {
      51           0 :   tor_assert(next_interval < MAX_INTERVAL);
      52           0 :   struct timeval tv;
      53           0 :   tv.tv_sec = next_interval;
      54           0 :   tv.tv_usec = 0;
      55           0 :   mainloop_event_schedule(event->ev, &tv);
      56           0 : }
      57             : 
      58             : /** Wraps dispatches for periodic events, <b>data</b> will be a pointer to the
      59             :  * event that needs to be called */
      60             : static void
      61           0 : periodic_event_dispatch(mainloop_event_t *ev, void *data)
      62             : {
      63           0 :   periodic_event_item_t *event = data;
      64           0 :   tor_assert(ev == event->ev);
      65             : 
      66           0 :   time_t now = time(NULL);
      67           0 :   update_current_time(now);
      68           0 :   const or_options_t *options = get_options();
      69             : //  log_debug(LD_GENERAL, "Dispatching %s", event->name);
      70           0 :   int r = event->fn(now, options);
      71           0 :   int next_interval = 0;
      72             : 
      73           0 :   if (!periodic_event_is_enabled(event)) {
      74             :     /* The event got disabled from inside its callback, or before: no need to
      75             :      * reschedule. */
      76           0 :     return;
      77             :   }
      78             : 
      79             :   /* update the last run time if action was taken */
      80           0 :   if (r==0) {
      81           0 :     log_err(LD_BUG, "Invalid return value for periodic event from %s.",
      82             :                       event->name);
      83           0 :     tor_assert(r != 0);
      84           0 :   } else if (r > 0) {
      85           0 :     event->last_action_time = now;
      86             :     /* If the event is meant to happen after ten years, that's likely
      87             :      * a bug, and somebody gave an absolute time rather than an interval.
      88             :      */
      89           0 :     tor_assert(r < MAX_INTERVAL);
      90             :     next_interval = r;
      91             :   } else {
      92             :     /* no action was taken, it is likely a precondition failed,
      93             :      * we should reschedule for next second in case the precondition
      94             :      * passes then */
      95             :     next_interval = 1;
      96             :   }
      97             : 
      98             : //  log_debug(LD_GENERAL, "Scheduling %s for %d seconds", event->name,
      99             : //           next_interval);
     100           0 :   struct timeval tv = { next_interval , 0 };
     101           0 :   mainloop_event_schedule(ev, &tv);
     102             : }
     103             : 
     104             : /** Schedules <b>event</b> to run as soon as possible from now. */
     105             : void
     106         129 : periodic_event_reschedule(periodic_event_item_t *event)
     107             : {
     108             :   /* Don't reschedule a disabled or uninitialized event. */
     109         129 :   if (event->ev && periodic_event_is_enabled(event)) {
     110           0 :     periodic_event_set_interval(event, 1);
     111             :   }
     112         129 : }
     113             : 
     114             : /** Connects a periodic event to the Libevent backend.  Does not launch the
     115             :  * event immediately. */
     116             : void
     117         105 : periodic_event_connect(periodic_event_item_t *event)
     118             : {
     119         105 :   if (event->ev) { /* Already setup? This is a bug */
     120           0 :     log_err(LD_BUG, "Initial dispatch should only be done once.");
     121           0 :     tor_assert(0);
     122             :   }
     123             : 
     124         105 :   event->ev = mainloop_event_new(periodic_event_dispatch,
     125             :                                  event);
     126         105 :   tor_assert(event->ev);
     127         105 : }
     128             : 
     129             : /** Handles initial dispatch for periodic events. It should happen 1 second
     130             :  * after the events are created to mimic behaviour before #3199's refactor */
     131             : void
     132           0 : periodic_event_launch(periodic_event_item_t *event)
     133             : {
     134           0 :   if (! event->ev) { /* Not setup? This is a bug */
     135           0 :     log_err(LD_BUG, "periodic_event_launch without periodic_event_connect");
     136           0 :     tor_assert(0);
     137             :   }
     138             :   /* Event already enabled? This is a bug */
     139           0 :   if (periodic_event_is_enabled(event)) {
     140           0 :     log_err(LD_BUG, "periodic_event_launch on an already enabled event");
     141           0 :     tor_assert(0);
     142             :   }
     143             : 
     144             :   // Initial dispatch
     145           0 :   event->enabled = 1;
     146           0 :   periodic_event_dispatch(event->ev, event);
     147           0 : }
     148             : 
     149             : /** Disconnect and unregister the periodic event in <b>event</b> */
     150             : static void
     151        8295 : periodic_event_disconnect(periodic_event_item_t *event)
     152             : {
     153        8295 :   if (!event)
     154             :     return;
     155             : 
     156             :   /* First disable the event so we first cancel the event and set its enabled
     157             :    * flag properly. */
     158        8295 :   periodic_event_disable(event);
     159             : 
     160        8295 :   mainloop_event_free(event->ev);
     161        8295 :   event->last_action_time = 0;
     162             : }
     163             : 
     164             : /** Enable the given event by setting its "enabled" flag and scheduling it to
     165             :  * run immediately in the event loop. This can be called for an event that is
     166             :  * already enabled. */
     167             : void
     168         167 : periodic_event_enable(periodic_event_item_t *event)
     169             : {
     170         167 :   tor_assert(event);
     171             :   /* Safely and silently ignore if this event is already enabled. */
     172         167 :   if (periodic_event_is_enabled(event)) {
     173             :     return;
     174             :   }
     175             : 
     176          76 :   tor_assert(event->ev);
     177          76 :   event->enabled = 1;
     178          76 :   mainloop_event_activate(event->ev);
     179             : }
     180             : 
     181             : /** Disable the given event which means the event is destroyed and then the
     182             :  * event's enabled flag is unset. This can be called for an event that is
     183             :  * already disabled. */
     184             : void
     185        8437 : periodic_event_disable(periodic_event_item_t *event)
     186             : {
     187        8437 :   tor_assert(event);
     188             :   /* Safely and silently ignore if this event is already disabled. */
     189        8437 :   if (!periodic_event_is_enabled(event)) {
     190             :     return;
     191             :   }
     192          40 :   mainloop_event_cancel(event->ev);
     193          40 :   event->enabled = 0;
     194             : }
     195             : 
     196             : /**
     197             :  * Disable an event, then schedule it to run once.
     198             :  * Do nothing if the event was already disabled.
     199             :  */
     200             : void
     201          18 : periodic_event_schedule_and_disable(periodic_event_item_t *event)
     202             : {
     203          18 :   tor_assert(event);
     204          18 :   if (!periodic_event_is_enabled(event))
     205             :     return;
     206             : 
     207          12 :   periodic_event_disable(event);
     208             : 
     209          12 :   mainloop_event_activate(event->ev);
     210             : }
     211             : 
     212             : /**
     213             :  * Add <b>item</b> to the list of periodic events.
     214             :  *
     215             :  * Note that <b>item</b> should be statically allocated: we do not
     216             :  * take ownership of it.
     217             :  **/
     218             : void
     219        8540 : periodic_events_register(periodic_event_item_t *item)
     220             : {
     221        8540 :   if (!the_periodic_events)
     222         244 :     the_periodic_events = smartlist_new();
     223             : 
     224        8540 :   if (BUG(smartlist_contains(the_periodic_events, item)))
     225           0 :     return;
     226             : 
     227        8540 :   smartlist_add(the_periodic_events, item);
     228             : }
     229             : 
     230             : /**
     231             :  * Make all registered periodic events connect to the libevent backend.
     232             :  */
     233             : void
     234           3 : periodic_events_connect_all(void)
     235             : {
     236           3 :   if (! the_periodic_events)
     237             :     return;
     238             : 
     239         108 :   SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
     240         105 :     if (item->ev)
     241           0 :       continue;
     242         105 :     periodic_event_connect(item);
     243         105 :   } SMARTLIST_FOREACH_END(item);
     244             : }
     245             : 
     246             : /**
     247             :  * Reset all the registered periodic events so we'll do all our actions again
     248             :  * as if we just started up.
     249             :  *
     250             :  * Useful if our clock just moved back a long time from the future,
     251             :  * so we don't wait until that future arrives again before acting.
     252             :  */
     253             : void
     254           1 : periodic_events_reset_all(void)
     255             : {
     256           1 :   if (! the_periodic_events)
     257             :     return;
     258             : 
     259          36 :   SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
     260          35 :     if (!item->ev)
     261          35 :       continue;
     262             : 
     263           0 :     periodic_event_reschedule(item);
     264          35 :   } SMARTLIST_FOREACH_END(item);
     265             : }
     266             : 
     267             : /**
     268             :  * Return the registered periodic event whose name is <b>name</b>.
     269             :  * Return NULL if no such event is found.
     270             :  */
     271             : periodic_event_item_t *
     272        1220 : periodic_events_find(const char *name)
     273             : {
     274        1220 :   if (! the_periodic_events)
     275             :     return NULL;
     276             : 
     277       14884 :   SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
     278       14640 :     if (strcmp(name, item->name) == 0)
     279         976 :       return item;
     280       13664 :   } SMARTLIST_FOREACH_END(item);
     281             :   return NULL;
     282             : }
     283             : 
     284             : /**
     285             :  * Start or stop registered periodic events, depending on our current set of
     286             :  * roles.
     287             :  *
     288             :  * Invoked when our list of roles, or the net_disabled flag has changed.
     289             :  **/
     290             : void
     291         297 : periodic_events_rescan_by_roles(int roles, bool net_disabled)
     292             : {
     293         297 :   if (! the_periodic_events)
     294             :     return;
     295             : 
     296       10692 :   SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
     297       10395 :     if (!item->ev)
     298       10080 :       continue;
     299             : 
     300         315 :     int enable = !!(item->roles & roles);
     301             : 
     302             :     /* Handle the event flags. */
     303         315 :     if (net_disabled &&
     304          70 :         (item->flags & PERIODIC_EVENT_FLAG_NEED_NET)) {
     305             :       enable = 0;
     306             :     }
     307             : 
     308             :     /* Enable the event if needed. It is safe to enable an event that was
     309             :      * already enabled. Same goes for disabling it. */
     310         293 :     if (enable) {
     311         167 :       log_debug(LD_GENERAL, "Launching periodic event %s", item->name);
     312         167 :       periodic_event_enable(item);
     313             :     } else {
     314         148 :       log_debug(LD_GENERAL, "Disabling periodic event %s", item->name);
     315         148 :       if (item->flags & PERIODIC_EVENT_FLAG_RUN_ON_DISABLE) {
     316          18 :         periodic_event_schedule_and_disable(item);
     317             :       } else {
     318         130 :         periodic_event_disable(item);
     319             :       }
     320             :     }
     321       10395 :   } SMARTLIST_FOREACH_END(item);
     322             : }
     323             : 
     324             : /**
     325             :  * Invoked at shutdown: disconnect and unregister all periodic events.
     326             :  *
     327             :  * Does not free the periodic_event_item_t object themselves, because we do
     328             :  * not own them.
     329             :  */
     330             : void
     331         237 : periodic_events_disconnect_all(void)
     332             : {
     333         237 :   if (! the_periodic_events)
     334             :     return;
     335             : 
     336        8532 :   SMARTLIST_FOREACH_BEGIN(the_periodic_events, periodic_event_item_t *, item) {
     337        8295 :     periodic_event_disconnect(item);
     338        8295 :   } SMARTLIST_FOREACH_END(item);
     339             : 
     340         237 :   smartlist_free(the_periodic_events);
     341             : }
     342             : 
     343             : #define LONGEST_TIMER_PERIOD (30 * 86400)
     344             : /** Helper: Return the number of seconds between <b>now</b> and <b>next</b>,
     345             :  * clipped to the range [1 second, LONGEST_TIMER_PERIOD].
     346             :  *
     347             :  * We use this to answer the question, "how many seconds is it from now until
     348             :  * next" in periodic timer callbacks.  Don't use it for other purposes
     349             :  **/
     350             : int
     351           0 : safe_timer_diff(time_t now, time_t next)
     352             : {
     353           0 :   if (next > now) {
     354             :     /* There were no computers at signed TIME_MIN (1902 on 32-bit systems),
     355             :      * and nothing that could run Tor. It's a bug if 'next' is around then.
     356             :      * On 64-bit systems with signed TIME_MIN, TIME_MIN is before the Big
     357             :      * Bang. We cannot extrapolate past a singularity, but there was probably
     358             :      * nothing that could run Tor then, either.
     359             :      **/
     360           0 :     tor_assert(next > TIME_MIN + LONGEST_TIMER_PERIOD);
     361             : 
     362           0 :     if (next - LONGEST_TIMER_PERIOD > now)
     363             :       return LONGEST_TIMER_PERIOD;
     364           0 :     return (int)(next - now);
     365             :   } else {
     366             :     return 1;
     367             :   }
     368             : }

Generated by: LCOV version 1.14