1
0
mirror of https://github.com/systemd/systemd synced 2026-04-03 13:44:55 +02:00

Compare commits

...

10 Commits

Author SHA1 Message Date
Lennart Poettering
3663f754f1
Merge pull request #21070 from medhefgo/boot-save
sd-boot: Add support to boot last select entry
2021-11-04 17:37:12 +01:00
Lennart Poettering
71bb9594e9
Merge pull request #21030 from DaanDeMeyer/path-skipped
core: Propagate condition failed state from service to path unit.
2021-11-04 15:20:00 +01:00
Zbigniew Jędrzejewski-Szmek
a4eba5d8cf update-helper: add missing loop over user units
Noticed by Luca.

shellcheck doens't catch this, and somehow it was missed in review
and testing ;(
2021-11-04 11:39:04 +01:00
Daan De Meyer
12ab94a1e4 core: Propagate condition failed state to triggering units.
Alternative to https://github.com/systemd/systemd/pull/20531.

Whenever a service triggered by another unit fails condition checks,
stop the triggering unit to prevent systemd busy looping trying to
start the triggered unit.
2021-11-03 20:25:14 +00:00
Daan De Meyer
7454acd73f mkosi: Add netcat to fedora image
This helps with testing socket units.
2021-10-30 22:13:46 +01:00
Daan De Meyer
ce2146f525 core: Delay start rate limit check when starting a unit
Doing start rate limit checks before doing condition checks made
condition check failures count towards the start rate limit which
broke existing assumptions (see #21025). Run the rate limit checks
after the condition checks again to restore the previous behaviour.
2021-10-30 22:13:45 +01:00
Daan De Meyer
5f37c1a955 core: Move 'r' variable declaration to start of unit_start() 2021-10-30 22:13:31 +01:00
Jan Janssen
ee4fd9cbd4 sd-boot: Add support to boot last selected entry
Fixes: #18994
2021-10-29 13:57:24 +02:00
Jan Janssen
0c674ce5f2 sd-boot: Allow glob patterns for default and oneshot EFI vars too 2021-10-29 13:57:24 +02:00
Jan Janssen
bbe51db664 sd-boot: Keep all EFI var loading together
This also delays the last remaining EFI var write in menu_run.
2021-10-29 13:57:24 +02:00
19 changed files with 185 additions and 110 deletions

View File

@ -99,8 +99,8 @@
<term><option>set-default</option> <replaceable>ID</replaceable></term>
<term><option>set-oneshot</option> <replaceable>ID</replaceable></term>
<listitem><para>Sets the default boot loader entry. Takes a single boot loader entry ID string as
argument. The <option>set-oneshot</option> command will set the default entry only for the next boot,
<listitem><para>Sets the default boot loader entry. Takes a single boot loader entry ID string or a glob
pattern as argument. The <option>set-oneshot</option> command will set the default entry only for the next boot,
the <option>set-default</option> will set it persistently for all future boots.</para></listitem>
<listitem><para>Optionally, the boot loader entry ID may be specified as one of: <option>@default</option>,
@ -111,8 +111,12 @@
see <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> for details.
These special IDs are primarily useful as a quick way to persistently make the currently booted boot loader
entry the default choice, or to upgrade the default boot loader entry for the next boot to the default boot
loader entry for all future boots, but may be used for other operations too.
When an empty string ("") is specified as an ID, then the corresponding EFI variable will be unset.
loader entry for all future boots, but may be used for other operations too.</para>
<para>If set to <option>@saved</option> the chosen entry will be saved as an EFI variable
on every boot and automatically selected the next time the boot loader starts.</para>
<para>When an empty string ("") is specified as an ID, then the corresponding EFI variable will be unset.
</para></listitem>
</varlistentry>

View File

@ -60,6 +60,9 @@
selected entry will be stored as an EFI variable, overriding this option.
</para>
<para>If set to <literal>@saved</literal> the chosen entry will be saved as an EFI variable
on every boot and automatically selected the next time the boot loader starts.</para>
<table>
<title>Automatically detected entries will use the following names:</title>

View File

@ -72,3 +72,4 @@ Packages=
strace
tpm2-tss
less
netcat

View File

@ -1834,7 +1834,7 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target
if (r < 0)
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m");
} else if (arg1[0] == '@')
} else if (arg1[0] == '@' && !streq(arg1, "@saved"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
else {
encoded = utf8_to_utf16(arg1, strlen(arg1));

View File

@ -67,13 +67,17 @@ typedef struct {
UINT32 timeout_sec; /* Actual timeout used (efi_main() override > efivar > config). */
UINT32 timeout_sec_config;
UINT32 timeout_sec_efivar;
CHAR16 *entry_default_pattern;
CHAR16 *entry_default_config;
CHAR16 *entry_default_efivar;
CHAR16 *entry_oneshot;
CHAR16 *entry_saved;
CHAR16 *options_edit;
BOOLEAN editor;
BOOLEAN auto_entries;
BOOLEAN auto_firmware;
BOOLEAN force_menu;
BOOLEAN use_saved_entry;
BOOLEAN use_saved_entry_efivar;
INT64 console_mode;
INT64 console_mode_efivar;
RandomSeedMode random_seed_mode;
@ -435,7 +439,7 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) {
UINT64 key;
UINTN x_max, y_max;
SecureBootMode secure;
_cleanup_freepool_ CHAR16 *device_part_uuid = NULL, *default_efivar = NULL;
_cleanup_freepool_ CHAR16 *device_part_uuid = NULL;
assert(config);
assert(loaded_image_path);
@ -445,7 +449,6 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) {
secure = secure_boot_mode();
(void) efivar_get(LOADER_GUID, L"LoaderDevicePartUUID", &device_part_uuid);
(void) efivar_get(LOADER_GUID, L"LoaderEntryDefault", &default_efivar);
/* We employ some unusual indentation here for readability. */
@ -486,9 +489,10 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) {
Print(L" timeout (EFI var): %lu s\n", config->timeout_sec_efivar);
}
ps_string(L" default: %s\n", config->entry_default_pattern);
ps_string(L" default: %s\n", config->entry_default_config);
ps_string(L" default (EFI var): %s\n", config->entry_default_efivar);
ps_string(L" default (one-shot): %s\n", config->entry_oneshot);
ps_string(L" default (EFI var): %s\n", default_efivar);
ps_string(L" saved entry: %s\n", config->entry_saved);
ps_bool(L" editor: %s\n", config->editor);
ps_bool(L" auto-entries: %s\n", config->auto_entries);
ps_bool(L" auto-firmware: %s\n", config->auto_firmware);
@ -586,6 +590,7 @@ static BOOLEAN menu_run(
INT16 idx;
BOOLEAN exit = FALSE, run = TRUE, firmware_setup = FALSE;
INT64 console_mode_initial = ST->ConOut->Mode->Mode, console_mode_efivar_saved = config->console_mode_efivar;
INTN default_efivar_saved = config->idx_default_efivar;
graphics_mode(FALSE);
ST->ConIn->Reset(ST->ConIn, FALSE);
@ -831,20 +836,20 @@ static BOOLEAN menu_run(
case KEYPRESS(0, 0, 'd'):
case KEYPRESS(0, 0, 'D'):
if (config->idx_default_efivar != (INTN)idx_highlight) {
/* store the selected entry in a persistent EFI variable */
efivar_set(
LOADER_GUID,
L"LoaderEntryDefault",
config->entries[idx_highlight]->id,
EFI_VARIABLE_NON_VOLATILE);
FreePool(config->entry_default_efivar);
config->entry_default_efivar = StrDuplicate(config->entries[idx_highlight]->id);
if (!config->entry_default_efivar) {
log_oom();
return FALSE;
}
config->idx_default_efivar = idx_highlight;
status = StrDuplicate(L"Default boot entry selected.");
} else {
/* clear the default entry EFI variable */
efivar_set(LOADER_GUID, L"LoaderEntryDefault", NULL, EFI_VARIABLE_NON_VOLATILE);
config->entry_default_efivar = mfree(config->entry_default_efivar);
config->idx_default_efivar = -1;
status = StrDuplicate(L"Default boot entry cleared.");
}
config->use_saved_entry_efivar = FALSE;
refresh = TRUE;
break;
@ -954,6 +959,9 @@ static BOOLEAN menu_run(
/* Update EFI vars after we left the menu to reduce NVRAM writes. */
if (default_efivar_saved != config->idx_default_efivar)
efivar_set(LOADER_GUID, L"LoaderEntryDefault", config->entry_default_efivar, EFI_VARIABLE_NON_VOLATILE);
if (console_mode_efivar_saved != config->console_mode_efivar) {
if (config->console_mode_efivar == CONSOLE_MODE_KEEP)
efivar_set(LOADER_GUID, L"LoaderConfigConsoleMode", NULL, EFI_VARIABLE_NON_VOLATILE);
@ -1109,9 +1117,12 @@ static void config_defaults_load_from_file(Config *config, CHAR8 *content) {
}
if (strcmpa((CHAR8 *)"default", key) == 0) {
FreePool(config->entry_default_pattern);
config->entry_default_pattern = stra_to_str(value);
StrLwr(config->entry_default_pattern);
if (value[0] == '@' && strcmpa((CHAR8 *)"@saved", value) != 0) {
log_error_stall(L"Unsupported special entry identifier: %a", value);
continue;
}
FreePool(config->entry_default_config);
config->entry_default_config = stra_to_str(value);
continue;
}
@ -1537,6 +1548,18 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigConsoleMode", &value);
if (!EFI_ERROR(err))
config->console_mode_efivar = value;
err = efivar_get(LOADER_GUID, L"LoaderEntryOneShot", &config->entry_oneshot);
if (!EFI_ERROR(err))
/* Unset variable now, after all it's "one shot". */
(void) efivar_set(LOADER_GUID, L"LoaderEntryOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
(void) efivar_get(LOADER_GUID, L"LoaderEntryDefault", &config->entry_default_efivar);
config->use_saved_entry = streq_ptr(config->entry_default_config, L"@saved");
config->use_saved_entry_efivar = streq_ptr(config->entry_default_efivar, L"@saved");
if (config->use_saved_entry || config->use_saved_entry_efivar)
(void) efivar_get(LOADER_GUID, L"LoaderEntryLastBooted", &config->entry_saved);
}
static void config_load_entries(
@ -1622,70 +1645,46 @@ static void config_sort_entries(Config *config) {
sort_pointer_array((void**) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare);
}
static INTN config_entry_find(Config *config, CHAR16 *id) {
static INTN config_entry_find(Config *config, CHAR16 *needle) {
assert(config);
assert(id);
if (!needle)
return -1;
for (UINTN i = 0; i < config->entry_count; i++)
if (StrCmp(config->entries[i]->id, id) == 0)
if (MetaiMatch(config->entries[i]->id, needle))
return (INTN) i;
return -1;
}
static void config_default_entry_select(Config *config) {
_cleanup_freepool_ CHAR16 *entry_default = NULL;
EFI_STATUS err;
INTN i;
assert(config);
/*
* The EFI variable to specify a boot entry for the next, and only the
* next reboot. The variable is always cleared directly after it is read.
*/
err = efivar_get(LOADER_GUID, L"LoaderEntryOneShot", &config->entry_oneshot);
if (!EFI_ERROR(err)) {
efivar_set(LOADER_GUID, L"LoaderEntryOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
i = config_entry_find(config, config->entry_oneshot);
if (i >= 0) {
config->idx_default = i;
return;
}
}
/*
* The EFI variable to select the default boot entry overrides the
* configured pattern. The variable can be set and cleared by pressing
* the 'd' key in the loader selection menu.
*/
err = efivar_get(LOADER_GUID, L"LoaderEntryDefault", &entry_default);
if (!EFI_ERROR(err)) {
i = config_entry_find(config, entry_default);
i = config_entry_find(config, config->use_saved_entry_efivar ? config->entry_saved : config->entry_default_efivar);
if (i >= 0) {
config->idx_default = i;
config->idx_default_efivar = i;
return;
}
}
if (config->entry_count == 0)
return;
/*
* Match the pattern from the end of the list to the start, find last
* entry (largest number) matching the given pattern.
*/
if (config->entry_default_pattern) {
i = config->entry_count;
while (i--) {
if (MetaiMatch(config->entries[i]->id, config->entry_default_pattern)) {
if (config->use_saved_entry)
/* No need to do the same thing twice. */
i = config->use_saved_entry_efivar ? -1 : config_entry_find(config, config->entry_saved);
else
i = config_entry_find(config, config->entry_default_config);
if (i >= 0) {
config->idx_default = i;
return;
}
}
}
/* select the last suitable entry */
i = config->entry_count;
@ -2224,7 +2223,7 @@ static void config_free(Config *config) {
for (UINTN i = 0; i < config->entry_count; i++)
config_entry_free(config->entries[i]);
FreePool(config->entries);
FreePool(config->entry_default_pattern);
FreePool(config->entry_default_config);
FreePool(config->options_edit);
FreePool(config->entry_oneshot);
}
@ -2256,6 +2255,29 @@ static void config_write_entries_to_variable(Config *config) {
(void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, sz, 0);
}
static void save_selected_entry(const Config *config, const ConfigEntry *entry) {
assert(config);
assert(entry);
assert(!entry->call);
/* Always export the selected boot entry to the system in a volatile var. */
(void) efivar_set(LOADER_GUID, L"LoaderEntrySelected", entry->id, 0);
/* Do not save or delete if this was a oneshot boot. */
if (streq_ptr(config->entry_oneshot, entry->id))
return;
if (config->use_saved_entry_efivar || (!config->entry_default_efivar && config->use_saved_entry)) {
/* Avoid unnecessary NVRAM writes. */
if (streq_ptr(config->entry_saved, entry->id))
return;
(void) efivar_set(LOADER_GUID, L"LoaderEntryLastBooted", entry->id, EFI_VARIABLE_NON_VOLATILE);
} else
/* Delete the non-volatile var if not needed. */
(void) efivar_set(LOADER_GUID, L"LoaderEntryLastBooted", NULL, EFI_VARIABLE_NON_VOLATILE);
}
static void export_variables(
EFI_LOADED_IMAGE *loaded_image,
const CHAR16 *loaded_image_path,
@ -2434,9 +2456,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
}
config_entry_bump_counters(entry, root_dir);
/* Export the selected boot entry to the system */
(void) efivar_set(LOADER_GUID, L"LoaderEntrySelected", entry->id, 0);
save_selected_entry(&config, entry);
/* Optionally, read a random seed off the ESP and pass it to the OS */
(void) process_random_seed(root_dir, config.random_seed_mode);

View File

@ -786,6 +786,11 @@ 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));
@ -1079,6 +1084,7 @@ static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
[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",
};
DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult);

View File

@ -11,6 +11,7 @@ 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

@ -480,7 +480,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_FAILED : PATH_DEAD);
path_set_state(p, p->result == PATH_SUCCESS ? PATH_DEAD : PATH_FAILED);
}
static void path_enter_running(Path *p) {
@ -780,6 +780,11 @@ 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;
@ -836,6 +841,7 @@ static const char* const path_result_table[_PATH_RESULT_MAX] = {
[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",
};
DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);

View File

@ -46,6 +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_RESULT_MAX,
_PATH_RESULT_INVALID = -EINVAL,
} PathResult;

View File

@ -2335,6 +2335,15 @@ 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,7 +3461,8 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
[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_START_LIMIT_HIT] = "service-start-limit-hit",
[SOCKET_FAILURE_SERVICE_CONDITION_FAILED] = "service-condition-failed",
};
DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);

View File

@ -38,6 +38,7 @@ 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,6 +598,11 @@ 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;
@ -914,6 +919,7 @@ 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",
};
DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);

View File

@ -32,6 +32,7 @@ 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

@ -1851,16 +1851,10 @@ static bool unit_verify_deps(Unit *u) {
int unit_start(Unit *u) {
UnitActiveState state;
Unit *following;
int r;
assert(u);
/* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */
if (UNIT_VTABLE(u)->test_start_limit) {
int r = UNIT_VTABLE(u)->test_start_limit(u);
if (r < 0)
return r;
}
/* If this is already started, then this will succeed. Note that this will even succeed if this unit
* is not startable by the user. This is relied on to detect when we need to wait for units and when
* waiting is finished. */
@ -1910,6 +1904,13 @@ int unit_start(Unit *u) {
return unit_start(following);
}
/* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */
if (UNIT_VTABLE(u)->test_start_limit) {
r = UNIT_VTABLE(u)->test_start_limit(u);
if (r < 0)
return r;
}
/* If it is stopped, but we cannot start it, then fail */
if (!UNIT_VTABLE(u)->start)
return -EBADR;
@ -5864,6 +5865,16 @@ 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

@ -988,6 +988,8 @@ 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

@ -52,9 +52,11 @@ case "$command" in
users=$(systemctl list-units 'user@*' --legend=no | sed -n -r 's/.*user@([0-9]+).service.*/\1/p')
for user in $users; do
for unit in "$@"; do
SYSTEMD_BUS_TIMEOUT={{UPDATE_HELPER_USER_TIMEOUT}} \
systemctl --user -M "$user@" set-property "$unit" Markers=+needs-restart &
done
done
wait
;;

View File

@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
ConditionPathExists=!/tmp/nonexistent
ConditionPathExists=/tmp/nonexistent
[Service]
ExecStart=true

View File

@ -13,5 +13,5 @@ ExecStart=-socat -T20 OPEN:test.file UNIX-CONNECT:/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)" = trigger-limit-hit'
ExecStart=sh -x -c 'test "$(systemctl show test10.socket -P Result)" = service-condition-failed'
ExecStart=sh -x -c 'echo OK >/testok'

View File

@ -8,10 +8,10 @@ Type=oneshot
ExecStart=rm -f /tmp/nonexistent
ExecStart=systemctl start test63.path
ExecStart=touch /tmp/test63
# Make sure systemd has sufficient time to hit the start limit for test63.service.
ExecStart=sleep 2
ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = failed'
ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = start-limit-hit'
# 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-start-limit-hit'
ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = unit-condition-failed'
ExecStart=sh -x -c 'echo OK >/testok'