diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index 2eb88d02f7..e9c7cb238c 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1004,10 +1004,12 @@
inactive state. Takes one of , ,
, , ,
, , ,
- , and . In
- system mode, all options are allowed. In user mode, only ,
- , , and
- are allowed. Both options default to .
+ , , ,
+ , , ,
+ and . In system mode, all options are
+ allowed. In user mode, only , ,
+ , and are
+ allowed. Both options default to .
If is set, no action will be triggered. causes a
reboot following the normal shutdown procedure (i.e. equivalent to systemctl
@@ -1016,18 +1018,20 @@
systemctl reboot -f) and causes immediate
execution of the
reboot2 system
- call, which might result in data loss (i.e. equivalent to systemctl reboot
- -ff). Similarly, , ,
- have the effect of powering down the system with similar
+ call, which might result in data loss (i.e. equivalent to systemctl reboot -ff).
+ Similarly, , ,
+ , , ,
+ , and have the
+ effect of powering down the system, executing kexec, and halting the system respectively with similar
semantics. causes the manager to exit following the normal shutdown procedure,
and causes it terminate without shutting down services. When
or is used by default the exit status of the main
process of the unit (if this applies) is returned from the service manager. However, this may be
overridden with
- FailureActionExitStatus=/SuccessActionExitStatus=, see
- below. will trigger a userspace reboot
- operation. does that too, but does not go through the shutdown
- transaction beforehand.
+ FailureActionExitStatus=/SuccessActionExitStatus=, see below.
+ will trigger a userspace reboot operation.
+ does that too, but does not go through the shutdown transaction
+ beforehand.
diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c
index 0b458ebf28..e2cd931671 100644
--- a/src/core/emergency-action.c
+++ b/src/core/emergency-action.c
@@ -24,6 +24,11 @@ static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
[EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
[EMERGENCY_ACTION_SOFT_REBOOT] = "soft-reboot",
[EMERGENCY_ACTION_SOFT_REBOOT_FORCE] = "soft-reboot-force",
+ [EMERGENCY_ACTION_KEXEC] = "kexec",
+ [EMERGENCY_ACTION_KEXEC_FORCE] = "kexec-force",
+ [EMERGENCY_ACTION_HALT] = "halt",
+ [EMERGENCY_ACTION_HALT_FORCE] = "halt-force",
+ [EMERGENCY_ACTION_HALT_IMMEDIATE] = "halt-immediate",
};
static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
@@ -49,7 +54,13 @@ void emergency_action(
assert(action < _EMERGENCY_ACTION_MAX);
/* Is the special shutdown target active or queued? If so, we are in shutdown state */
- if (IN_SET(action, EMERGENCY_ACTION_REBOOT, EMERGENCY_ACTION_SOFT_REBOOT, EMERGENCY_ACTION_POWEROFF, EMERGENCY_ACTION_EXIT)) {
+ if (IN_SET(action,
+ EMERGENCY_ACTION_REBOOT,
+ EMERGENCY_ACTION_SOFT_REBOOT,
+ EMERGENCY_ACTION_POWEROFF,
+ EMERGENCY_ACTION_EXIT,
+ EMERGENCY_ACTION_KEXEC,
+ EMERGENCY_ACTION_HALT)) {
u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
if (u && unit_active_or_pending(u)) {
log_notice("Shutdown is already active. Skipping emergency action request %s.",
@@ -158,6 +169,35 @@ void emergency_action(
(void) reboot(RB_POWER_OFF);
break;
+ case EMERGENCY_ACTION_KEXEC:
+ log_and_status(m, warn, "Executing kexec", reason);
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_KEXEC_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
+ break;
+
+ case EMERGENCY_ACTION_KEXEC_FORCE:
+ log_and_status(m, warn, "Forcibly executing kexec", reason);
+ m->objective = MANAGER_KEXEC;
+ break;
+
+ case EMERGENCY_ACTION_HALT:
+ log_and_status(m, warn, "Halting", reason);
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL);
+ break;
+
+ case EMERGENCY_ACTION_HALT_FORCE:
+ log_and_status(m, warn, "Forcibly halting", reason);
+ m->objective = MANAGER_HALT;
+ break;
+
+ case EMERGENCY_ACTION_HALT_IMMEDIATE:
+ log_and_status(m, warn, "Halting immediately", reason);
+
+ sync();
+
+ log_info("Halting.");
+ (void) reboot(RB_HALT_SYSTEM);
+ break;
+
default:
assert_not_reached();
}
diff --git a/src/core/emergency-action.h b/src/core/emergency-action.h
index 2c61c9c7d6..33e0ec6ffc 100644
--- a/src/core/emergency-action.h
+++ b/src/core/emergency-action.h
@@ -18,6 +18,11 @@ typedef enum EmergencyAction {
EMERGENCY_ACTION_EXIT_FORCE,
EMERGENCY_ACTION_SOFT_REBOOT,
EMERGENCY_ACTION_SOFT_REBOOT_FORCE,
+ EMERGENCY_ACTION_KEXEC,
+ EMERGENCY_ACTION_KEXEC_FORCE,
+ EMERGENCY_ACTION_HALT,
+ EMERGENCY_ACTION_HALT_FORCE,
+ EMERGENCY_ACTION_HALT_IMMEDIATE,
_EMERGENCY_ACTION_MAX,
_EMERGENCY_ACTION_INVALID = -EINVAL,
} EmergencyAction;
diff --git a/src/test/test-emergency-action.c b/src/test/test-emergency-action.c
index b2e6af8d62..5c0ce7f053 100644
--- a/src/test/test-emergency-action.c
+++ b/src/test/test-emergency-action.c
@@ -38,6 +38,14 @@ TEST(parse_emergency_action) {
assert_se(parse_emergency_action("exit-force", RUNTIME_SCOPE_SYSTEM, &x) == 0);
assert_se(parse_emergency_action("exit-forcee", RUNTIME_SCOPE_SYSTEM, &x) == -EINVAL);
assert_se(x == EMERGENCY_ACTION_EXIT_FORCE);
+ assert_se(parse_emergency_action("kexec", RUNTIME_SCOPE_SYSTEM, &x) == 0);
+ assert_se(parse_emergency_action("kexec-force", RUNTIME_SCOPE_SYSTEM, &x) == 0);
+ assert_se(parse_emergency_action("kexec-forcee", RUNTIME_SCOPE_SYSTEM, &x) == -EINVAL);
+ assert_se(x == EMERGENCY_ACTION_KEXEC_FORCE);
+ assert_se(parse_emergency_action("halt", RUNTIME_SCOPE_SYSTEM, &x) == 0);
+ assert_se(parse_emergency_action("halt-force", RUNTIME_SCOPE_SYSTEM, &x) == 0);
+ assert_se(parse_emergency_action("halt-forcee", RUNTIME_SCOPE_SYSTEM, &x) == -EINVAL);
+ assert_se(x == EMERGENCY_ACTION_HALT_FORCE);
}
DEFINE_TEST_MAIN(LOG_INFO);