Merge pull request #21808 from DaanDeMeyer/path-trigger-limit

core: Add trigger limit for path units
This commit is contained in:
Luca Boccassi 2021-12-18 16:56:05 +00:00 committed by GitHub
commit 5cfe61f2de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 51 additions and 69 deletions

View File

@ -811,11 +811,6 @@ static void automount_enter_running(Automount *a) {
goto fail;
}
if (unit_has_failed_condition_or_assert(trigger)) {
automount_enter_dead(a, AUTOMOUNT_FAILURE_MOUNT_CONDITION_FAILED);
return;
}
r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL);
if (r < 0) {
log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r));
@ -1104,12 +1099,11 @@ static int automount_can_start(Unit *u) {
}
static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
[AUTOMOUNT_SUCCESS] = "success",
[AUTOMOUNT_FAILURE_RESOURCES] = "resources",
[AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit",
[AUTOMOUNT_FAILURE_UNMOUNTED] = "unmounted",
[AUTOMOUNT_FAILURE_MOUNT_CONDITION_FAILED] = "mount-condition-failed",
[AUTOMOUNT_SUCCESS] = "success",
[AUTOMOUNT_FAILURE_RESOURCES] = "resources",
[AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit",
[AUTOMOUNT_FAILURE_UNMOUNTED] = "unmounted",
};
DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult);

View File

@ -11,7 +11,6 @@ typedef enum AutomountResult {
AUTOMOUNT_FAILURE_UNMOUNTED,
AUTOMOUNT_FAILURE_START_LIMIT_HIT,
AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT,
AUTOMOUNT_FAILURE_MOUNT_CONDITION_FAILED,
_AUTOMOUNT_RESULT_MAX,
_AUTOMOUNT_RESULT_INVALID = -EINVAL,
} AutomountResult;

View File

@ -265,6 +265,9 @@ static void path_init(Unit *u) {
assert(u->load_state == UNIT_STUB);
p->directory_mode = 0755;
p->trigger_limit.interval = 2 * USEC_PER_SEC;
p->trigger_limit.burst = 200;
}
void path_free_specs(Path *p) {
@ -480,7 +483,7 @@ static void path_enter_dead(Path *p, PathResult f) {
p->result = f;
unit_log_result(UNIT(p), p->result == PATH_SUCCESS, path_result_to_string(p->result));
path_set_state(p, p->result == PATH_SUCCESS ? PATH_DEAD : PATH_FAILED);
path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
}
static void path_enter_running(Path *p) {
@ -494,6 +497,12 @@ static void path_enter_running(Path *p) {
if (unit_stop_pending(UNIT(p)))
return;
if (!ratelimit_below(&p->trigger_limit)) {
log_unit_warning(UNIT(p), "Trigger limit hit, refusing further activation.");
path_enter_dead(p, PATH_FAILURE_TRIGGER_LIMIT_HIT);
return;
}
trigger = UNIT_TRIGGER(UNIT(p));
if (!trigger) {
log_unit_error(UNIT(p), "Unit to trigger vanished.");
@ -780,11 +789,6 @@ static void path_trigger_notify(Unit *u, Unit *other) {
return;
}
if (unit_has_failed_condition_or_assert(other)) {
path_enter_dead(p, PATH_FAILURE_UNIT_CONDITION_FAILED);
return;
}
/* Don't propagate anything if there's still a job queued */
if (other->job)
return;
@ -837,11 +841,11 @@ static const char* const path_type_table[_PATH_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
static const char* const path_result_table[_PATH_RESULT_MAX] = {
[PATH_SUCCESS] = "success",
[PATH_FAILURE_RESOURCES] = "resources",
[PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[PATH_FAILURE_UNIT_START_LIMIT_HIT] = "unit-start-limit-hit",
[PATH_FAILURE_UNIT_CONDITION_FAILED] = "unit-condition-failed",
[PATH_SUCCESS] = "success",
[PATH_FAILURE_RESOURCES] = "resources",
[PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[PATH_FAILURE_UNIT_START_LIMIT_HIT] = "unit-start-limit-hit",
[PATH_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);

View File

@ -46,7 +46,7 @@ typedef enum PathResult {
PATH_FAILURE_RESOURCES,
PATH_FAILURE_START_LIMIT_HIT,
PATH_FAILURE_UNIT_START_LIMIT_HIT,
PATH_FAILURE_UNIT_CONDITION_FAILED,
PATH_FAILURE_TRIGGER_LIMIT_HIT,
_PATH_RESULT_MAX,
_PATH_RESULT_INVALID = -EINVAL,
} PathResult;
@ -62,6 +62,8 @@ struct Path {
mode_t directory_mode;
PathResult result;
RateLimit trigger_limit;
};
void path_free_specs(Path *p);

View File

@ -2336,15 +2336,6 @@ static void socket_enter_running(Socket *s, int cfd_in) {
goto refuse;
}
if (UNIT_ISSET(s->service) && cfd < 0) {
Unit *service = UNIT_DEREF(s->service);
if (unit_has_failed_condition_or_assert(service)) {
socket_enter_dead(s, SOCKET_FAILURE_SERVICE_CONDITION_FAILED);
return;
}
}
if (cfd < 0) {
bool pending = false;
Unit *other;
@ -3452,16 +3443,15 @@ static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
[SOCKET_SUCCESS] = "success",
[SOCKET_FAILURE_RESOURCES] = "resources",
[SOCKET_FAILURE_TIMEOUT] = "timeout",
[SOCKET_FAILURE_EXIT_CODE] = "exit-code",
[SOCKET_FAILURE_SIGNAL] = "signal",
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
[SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
[SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit",
[SOCKET_FAILURE_SERVICE_CONDITION_FAILED] = "service-condition-failed",
[SOCKET_SUCCESS] = "success",
[SOCKET_FAILURE_RESOURCES] = "resources",
[SOCKET_FAILURE_TIMEOUT] = "timeout",
[SOCKET_FAILURE_EXIT_CODE] = "exit-code",
[SOCKET_FAILURE_SIGNAL] = "signal",
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
[SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
[SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit"
};
DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);

View File

@ -38,7 +38,6 @@ typedef enum SocketResult {
SOCKET_FAILURE_START_LIMIT_HIT,
SOCKET_FAILURE_TRIGGER_LIMIT_HIT,
SOCKET_FAILURE_SERVICE_START_LIMIT_HIT,
SOCKET_FAILURE_SERVICE_CONDITION_FAILED,
_SOCKET_RESULT_MAX,
_SOCKET_RESULT_INVALID = -EINVAL,
} SocketResult;

View File

@ -598,11 +598,6 @@ static void timer_enter_running(Timer *t) {
return;
}
if (unit_has_failed_condition_or_assert(trigger)) {
timer_enter_dead(t, TIMER_FAILURE_UNIT_CONDITION_FAILED);
return;
}
r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL);
if (r < 0)
goto fail;
@ -916,10 +911,9 @@ static const char* const timer_base_table[_TIMER_BASE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
[TIMER_SUCCESS] = "success",
[TIMER_FAILURE_RESOURCES] = "resources",
[TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[TIMER_FAILURE_UNIT_CONDITION_FAILED] = "unit-condition-failed",
[TIMER_SUCCESS] = "success",
[TIMER_FAILURE_RESOURCES] = "resources",
[TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);

View File

@ -32,7 +32,6 @@ typedef enum TimerResult {
TIMER_SUCCESS,
TIMER_FAILURE_RESOURCES,
TIMER_FAILURE_START_LIMIT_HIT,
TIMER_FAILURE_UNIT_CONDITION_FAILED,
_TIMER_RESULT_MAX,
_TIMER_RESULT_INVALID = -EINVAL,
} TimerResult;

View File

@ -5866,16 +5866,6 @@ Condition *unit_find_failed_condition(Unit *u) {
return failed_trigger && !has_succeeded_trigger ? failed_trigger : NULL;
}
bool unit_has_failed_condition_or_assert(Unit *u) {
if (dual_timestamp_is_set(&u->condition_timestamp) && !u->condition_result)
return true;
if (dual_timestamp_is_set(&u->assert_timestamp) && !u->assert_result)
return true;
return false;
}
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
[COLLECT_INACTIVE] = "inactive",
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",

View File

@ -991,8 +991,6 @@ int unit_thaw_vtable_common(Unit *u);
Condition *unit_find_failed_condition(Unit *u);
bool unit_has_failed_condition_or_assert(Unit *u);
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full_errno_zerook(unit, level, error, ...) \

View File

@ -12,5 +12,5 @@ ExecStart=-nc -w20 -U /run/test.ctl
# systemd enough time even on slower machines, to reach the trigger limit.
ExecStart=sleep 10
ExecStart=sh -x -c 'test "$(systemctl show test10.socket -P ActiveState)" = failed'
ExecStart=sh -x -c 'test "$(systemctl show test10.socket -P Result)" = service-condition-failed'
ExecStart=sh -x -c 'test "$(systemctl show test10.socket -P Result)" = trigger-limit-hit'
ExecStart=sh -x -c 'echo OK >/testok'

View File

@ -5,13 +5,26 @@ Description=TEST-63-ISSUE-17433
[Service]
ExecStartPre=rm -f /failed /testok
Type=oneshot
# Test that a path unit continuously triggering a service that fails condition checks eventually fails with
# the trigger-limit-hit error.
ExecStart=rm -f /tmp/nonexistent
ExecStart=systemctl start test63.path
ExecStart=touch /tmp/test63
# Make sure systemd has sufficient time to hit the trigger limit for test63.path.
ExecStart=sleep 2
# Ensure both the service and the corresponding path unit go inactive due to the failed condition check.
ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = inactive'
ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = success'
ExecStart=sh -x -c 'test "$(systemctl show test63.path -P ActiveState)" = failed'
ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = unit-condition-failed'
ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = trigger-limit-hit'
# Test that starting the service manually doesn't affect the path unit.
ExecStart=rm -f /tmp/test63
ExecStart=systemctl reset-failed
ExecStart=systemctl start test63.path
ExecStart=systemctl start test63.service
ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = inactive'
ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = success'
ExecStart=sh -x -c 'test "$(systemctl show test63.path -P ActiveState)" = active'
ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = success'
ExecStart=sh -x -c 'echo OK >/testok'