core: don't exit 0 when a failure action has nothing to propagate (#43647)

Fixes #43645.

`FailureAction=exit` propagates the exit status of the unit's main
process. When there is none, `emergency_action()` left `m->return_value`
at its initial 0, so the manager exited 0 for a unit that failed —
indistinguishable from an orderly shutdown to whoever supervises it.
This commit is contained in:
Luca Boccassi
2026-09-05 11:54:29 +01:00
committed by GitHub
6 changed files with 98 additions and 7 deletions
+9
View File
@@ -4,6 +4,7 @@
#include "ansi-color.h"
#include "emergency-action.h"
#include "exit-status.h"
#include "manager.h"
#include "reboot-util.h"
#include "special.h"
@@ -154,6 +155,10 @@ void emergency_action(
if (exit_status >= 0)
m->return_value = exit_status;
else if (FLAGS_SET(flags, EMERGENCY_ACTION_IS_FAILURE) && m->return_value == EXIT_SUCCESS)
/* Nothing to propagate, but we act on a failure: don't report success. An exit
* status set explicitly via SetExitCode() stays. */
m->return_value = EXIT_EXCEPTION;
if (MANAGER_IS_USER(m) || detect_container() > 0) {
log_and_status(m, action, flags, "Exiting", reason);
@@ -173,6 +178,10 @@ void emergency_action(
if (exit_status >= 0)
m->return_value = exit_status;
else if (FLAGS_SET(flags, EMERGENCY_ACTION_IS_FAILURE) && m->return_value == EXIT_SUCCESS)
/* Nothing to propagate, but we act on a failure: don't report success. An exit
* status set explicitly via SetExitCode() stays. */
m->return_value = EXIT_EXCEPTION;
if (MANAGER_IS_USER(m) || detect_container() > 0) {
log_and_status(m, action, flags, "Exiting immediately", reason);
+2 -1
View File
@@ -29,7 +29,8 @@ typedef enum EmergencyActionFlags {
EMERGENCY_ACTION_IS_WATCHDOG = 1 << 0, /* this action triggered by a watchdog or other kind of timeout */
EMERGENCY_ACTION_WARN = 1 << 1, /* log at LOG_WARNING + write to system console */
EMERGENCY_ACTION_SLEEP_5S = 1 << 2, /* wait 5s before executing action; only honoured together with EMERGENCY_ACTION_WARN */
_EMERGENCY_ACTION_FLAGS_MAX = (1 << 3) - 1,
EMERGENCY_ACTION_IS_FAILURE = 1 << 3, /* this action is triggered by a failure, hence must not make us exit 0 */
_EMERGENCY_ACTION_FLAGS_MAX = (1 << 4) - 1,
} EmergencyActionFlags;
void emergency_action(
+1 -1
View File
@@ -1141,7 +1141,7 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user
emergency_action(
u->manager,
u->job_timeout_action,
EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN|EMERGENCY_ACTION_SLEEP_5S,
EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN|EMERGENCY_ACTION_SLEEP_5S|EMERGENCY_ACTION_IS_FAILURE,
u->job_timeout_reboot_arg,
/* exit_status= */ -1,
"job timed out");
+5 -4
View File
@@ -29,6 +29,7 @@
#include "escape.h"
#include "exec-credential.h"
#include "execute.h"
#include "exit-status.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@@ -1887,7 +1888,7 @@ int unit_test_start_limit(Unit *u) {
emergency_action(
u->manager,
u->start_limit_action,
EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN|EMERGENCY_ACTION_SLEEP_5S,
EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN|EMERGENCY_ACTION_SLEEP_5S|EMERGENCY_ACTION_IS_FAILURE,
u->reboot_arg,
/* exit_status= */ -1,
reason);
@@ -2843,7 +2844,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
if (os != UNIT_FAILED && ns == UNIT_FAILED) {
reason = strjoina("unit ", u->id, " failed");
emergency_action(m, u->failure_action, EMERGENCY_ACTION_WARN|EMERGENCY_ACTION_SLEEP_5S, u->reboot_arg, unit_failure_action_exit_status(u), reason);
emergency_action(m, u->failure_action, EMERGENCY_ACTION_WARN|EMERGENCY_ACTION_SLEEP_5S|EMERGENCY_ACTION_IS_FAILURE, u->reboot_arg, unit_failure_action_exit_status(u), reason);
} else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && ns == UNIT_INACTIVE) {
reason = strjoina("unit ", u->id, " succeeded");
emergency_action(m, u->success_action, /* flags= */ 0, u->reboot_arg, unit_success_action_exit_status(u), reason);
@@ -6410,7 +6411,7 @@ int unit_failure_action_exit_status(Unit *u) {
r = unit_exit_status(u);
if (r == -EBADE) /* Exited, but not cleanly (i.e. by signal or such) */
return 255;
return EXIT_EXCEPTION;
return r;
}
@@ -6427,7 +6428,7 @@ int unit_success_action_exit_status(Unit *u) {
r = unit_exit_status(u);
if (r == -EBADE) /* Exited, but not cleanly (i.e. by signal or such) */
return 255;
return EXIT_EXCEPTION;
return r;
}