tlsdate: skip backoff periods on external events

If tlsdate is waiting for E_TLSDATE during backoff, then
action_kickoff_time_sync won't schedule another time check sooner than
the end of the backoff period.  This means that if tlsdate is waiting
out a backoff, plugging a network cable into the system will have no
effect until the end of the backoff.

Make tlsdate allow external events to force the scheduling of a time
sync before the end of a backoff, but only at least wait_between_tries
seconds in the future.  This means that callers will still have to wait
wait_between_tries seconds between checks, but that they no longer need
to wait for the end of the backoff period.

BUG=chrome-os-partner:38718
TEST=manual, saw "pending tlsdate being rescheduled" while in a backoff
     period after a network configuration change, and "called while
     tries are in progress" after change during rescheduled backoff
     period

Change-Id: I37b64a9717bebd329594e8d9c43d60e88f812093
Reviewed-on: https://chromium-review.googlesource.com/263794
Commit-Ready: Garret Kelly <gdk@chromium.org>
Tested-by: Garret Kelly <gdk@chromium.org>
Reviewed-by: Will Drewry <wad@chromium.org>
diff --git a/src/events/kickoff_time_sync.c b/src/events/kickoff_time_sync.c
index 228a37f..82d7aaa 100644
--- a/src/events/kickoff_time_sync.c
+++ b/src/events/kickoff_time_sync.c
@@ -94,6 +94,7 @@
   debug ("[event:%s] fired", __func__);
   time_t delta = state->clock_delta;
   int jitter = 0;
+  int reschedule = 0;
   if (check_continuity (&delta) > 0)
     {
       info ("[event:%s] clock delta desync detected (%d != %d)", __func__,
@@ -114,19 +115,34 @@
   if (state->tries > 0)
     {
       state->tries -= 1;
-      /* Don't bother re-triggering tlsdate */
-      debug ("[event:%s] called while tries are in progress", __func__);
-      return;
+      /* Add an extra attempt to be performed after the current attempt
+       * completes in case there is new data. Don't automatically reschedule
+       * because flapping could mean we never resolve the time.
+       */
+      if (state->backoff == state->opts.wait_between_tries)
+        {
+          debug ("[event:%s] called while tries are in progress", __func__);
+          return;
+        }
+      reschedule = 1;
+      state->backoff = state->opts.wait_between_tries;
     }
-  /* Don't over-schedule if the first attempt hasn't fired. If a wake event
-   * impacts the result of a proxy resolution, then the updated value can be
-   * acquired on the next run. If the wake comes in after E_TLSDATE is
-   * serviced, then the tries count will be decremented.
+  /* If a wake event arrives while a request to start tlsdate is pending, do
+   * not reschedule automatically.  Doing so would allow a flood of wake events
+   * to block the event from ever running.  Instead, only reschedule if
+   * requested above and never allow less than wait_between_tries between
+   * tlsdate events.
    */
   if (event_pending (state->events[E_TLSDATE], EV_TIMEOUT, NULL))
     {
-      debug ("[event:%s] called while tlsdate is pending", __func__);
-      return;
+      if (!reschedule)
+        {
+          debug ("[event:%s] tlsdate pending and not being rescheduled",
+                 __func__);
+          return;
+        }
+      debug ("[event:%s] pending tlsdate being rescheduled", __func__);
+      jitter = state->backoff;
     }
   if (!state->events[E_RESOLVER])
     {