core: check start limit on condition checks too
authorLennart Poettering <lennart@poettering.net>
Mon, 18 Mar 2019 12:14:19 +0000 (13:14 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 18 Mar 2019 15:06:36 +0000 (16:06 +0100)
Let's add a safety precaution: if the start condition checks for a unit
are tested too often and fail each time, let's rate limit this too.

This should add extra safety in case people define .path, .timer or
.automount units that trigger a service that as a conditoin that always
fails.

src/core/unit.c

index d4060a7..96b520f 100644 (file)
@@ -1729,7 +1729,7 @@ static bool unit_verify_deps(Unit *u) {
  *         -EBADR:      This unit type does not support starting.
  *         -EALREADY:   Unit is already started.
  *         -EAGAIN:     An operation is already in progress. Retry later.
- *         -ECANCELED:  Too many requests for now.
+ *         -ECANCELED:  Start limit hit, too many requests for now
  *         -ECOMM:      Condition failed
  *         -EPROTO:     Assert failed
  *         -EINVAL:     Unit not loaded
@@ -1741,6 +1741,7 @@ static bool unit_verify_deps(Unit *u) {
 int unit_start(Unit *u) {
         UnitActiveState state;
         Unit *following;
+        int r;
 
         assert(u);
 
@@ -1763,8 +1764,25 @@ int unit_start(Unit *u) {
          * still be useful to speed up activation in case there is some hold-off time, but we don't want to
          * recheck the condition in that case. */
         if (state != UNIT_ACTIVATING &&
-            !unit_test_condition(u))
+            !unit_test_condition(u)) {
+
+                /* Let's also check the start limit here. Normally, the start limit is only checked by the
+                 * .start() method of the unit type after it did some additional checks verifying everything
+                 * is in order (so that those other checks can propagate errors properly). However, if a
+                 * condition check doesn't hold we don't get that far but we should still ensure we are not
+                 * called in a tight loop without a rate limit check enforced, hence do the check here. Note
+                 * that ECOMM is generally not a reason for a job to fail, unlike most other errors here,
+                 * hence the chance is big that any triggering unit for us will trigger us again. Note this
+                 * condition check is a bit different from the condition check inside the per-unit .start()
+                 * function, as this one will not change the unit's state in any way (and we shouldn't here,
+                 * after all the condition failed). */
+
+                r = unit_test_start_limit(u);
+                if (r < 0)
+                        return r;
+
                 return log_unit_debug_errno(u, SYNTHETIC_ERRNO(ECOMM), "Starting requested but condition failed. Not starting unit.");
+        }
 
         /* If the asserts failed, fail the entire job */
         if (state != UNIT_ACTIVATING &&