diff --git a/TODO b/TODO
index 957dde27c3..e61050051a 100644
--- a/TODO
+++ b/TODO
@@ -2,6 +2,10 @@ Bugfixes:
* copy.c: set the right chattrs before copying files and others after
+* Many manager configuration settings that are only applicable to user
+ manager or system manager can be always set. It would be better to reject
+ them when parsing config.
+
External:
* Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros.
@@ -40,12 +44,6 @@ Features:
* chown() tty a service is attached to after the service goes down
-* replace systemd-reboot.service's ExecStart= with a single SuccessAction=
- line, so that we don't need to fork() for executing the reboot
- service. Similar for other services like this, such as systemd-exit.service
- and so on. Of course, for this to work service units with no ExecYYZ= set but
- SuccessAction= set need to be acceptable.
-
* optionally: turn on cgroup delegation for per-session scope units
* introduce per-unit (i.e. per-slice, per-service) journal log size limits.
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index f418f5b5ee..467b905f14 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -864,6 +864,29 @@
+
+ FailureAction=
+ SuccessAction=
+
+ Configure the action to take when the unit stops and enters a failed state or inactive state.
+ Takes one of , , ,
+ , , ,
+ , , 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 reboot).
+ causes a forced reboot which will terminate all processes forcibly but should
+ cause no dirty file systems on reboot (i.e. equivalent to systemctl reboot -f) and
+ causes immediate execution of the
+ reboot2 system call, which
+ might result in data loss. Similarly, , ,
+ have the effect of powering down the system with similar
+ semantics. causes the manager to exit following the normal shutdown procedure, and
+ causes it terminate without shutting down services.
+
+
JobTimeoutSec=
JobRunningTimeoutSec=
@@ -920,29 +943,13 @@
StartLimitAction=
- Configure the action to take if the rate limit configured with
- StartLimitIntervalSec= and StartLimitBurst= is hit. Takes one of
- , , ,
- , , or
- . If is set, hitting the rate limit will trigger no
- action besides that the start will not be permitted. causes a reboot following the
- normal shutdown procedure (i.e. equivalent to systemctl reboot).
- causes a forced reboot which will terminate all processes forcibly but should
- cause no dirty file systems on reboot (i.e. equivalent to systemctl reboot -f) and
- causes immediate execution of the
- reboot2 system call, which
- might result in data loss. Similarly, , ,
- have the effect of powering down the system with similar
- semantics. Defaults to .
+ Configure an additional action to take if the rate limit configured with
+ StartLimitIntervalSec= and StartLimitBurst= is hit. Takes the same
+ values as the setting FailureAction=/SuccessAction= settings and executes
+ the same actions. If is set, hitting the rate limit will trigger no action besides that
+ the start will not be permitted. Defaults to .
-
- FailureAction=
- SuccessAction=
- Configure the action to take when the unit stops and enters a failed state or inactive
- state. Takes the same values as the setting StartLimitAction= setting and executes the same
- actions. Both options default to .
-
RebootArgument=
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 7728b63a5f..b725a17761 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -1166,7 +1166,7 @@ int cg_is_empty(const char *controller, const char *path) {
r = cg_enumerate_processes(controller, path, &f);
if (r == -ENOENT)
- return 1;
+ return true;
if (r < 0)
return r;
@@ -1196,6 +1196,8 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
* via the "populated" attribute of "cgroup.events". */
r = cg_read_event(controller, path, "populated", &t);
+ if (r == -ENOENT)
+ return true;
if (r < 0)
return r;
@@ -1210,7 +1212,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
r = cg_enumerate_subgroups(controller, path, &d);
if (r == -ENOENT)
- return 1;
+ return true;
if (r < 0)
return r;
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index c4530f0c8b..daec85a73b 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -1299,8 +1299,43 @@ static int bus_unit_set_live_property(
return 0;
}
+static int bus_set_transient_emergency_action(
+ Unit *u,
+ const char *name,
+ EmergencyAction *p,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+
+ const char *s;
+ EmergencyAction v;
+ int r;
+ bool system;
+
+ assert(p);
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ system = MANAGER_IS_SYSTEM(u->manager);
+ r = parse_emergency_action(s, system, &v);
+ if (v < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ v == -EOPNOTSUPP ? "EmergencyAction setting invalid for manager type: %s"
+ : "Invalid %s setting: %s",
+ name, s);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ *p = v;
+ unit_write_settingf(u, flags, name,
+ "%s=%s", name, s);
+ }
+
+ return 1;
+}
+
static BUS_DEFINE_SET_TRANSIENT_PARSE(collect_mode, CollectMode, collect_mode_from_string);
-static BUS_DEFINE_SET_TRANSIENT_PARSE(emergency_action, EmergencyAction, emergency_action_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(job_mode, JobMode, job_mode_from_string);
static int bus_set_transient_conditions(
diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c
index 6f0b0a8f33..c91d2bf5ff 100644
--- a/src/core/emergency-action.c
+++ b/src/core/emergency-action.c
@@ -10,17 +10,20 @@
#include "special.h"
#include "string-table.h"
#include "terminal-util.h"
+#include "virt.h"
-static void log_and_status(Manager *m, const char *message, const char *reason) {
- log_warning("%s: %s", message, reason);
- manager_status_printf(m, STATUS_TYPE_EMERGENCY,
- ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL,
- "%s: %s", message, reason);
+static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
+ log_full(warn ? LOG_WARNING : LOG_DEBUG, "%s: %s", message, reason);
+ if (warn)
+ manager_status_printf(m, STATUS_TYPE_EMERGENCY,
+ ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL,
+ "%s: %s", message, reason);
}
int emergency_action(
Manager *m,
EmergencyAction action,
+ EmergencyActionFlags options,
const char *reboot_arg,
const char *reason) {
@@ -31,24 +34,17 @@ int emergency_action(
if (action == EMERGENCY_ACTION_NONE)
return -ECANCELED;
- if (!m->service_watchdogs) {
+ if (FLAGS_SET(options, EMERGENCY_ACTION_IS_WATCHDOG) && !m->service_watchdogs) {
log_warning("Watchdog disabled! Not acting on: %s", reason);
return -ECANCELED;
}
- if (!MANAGER_IS_SYSTEM(m)) {
- /* Downgrade all options to simply exiting if we run
- * in user mode */
-
- log_warning("Exiting: %s", reason);
- m->objective = MANAGER_EXIT;
- return -ECANCELED;
- }
+ bool warn = FLAGS_SET(options, EMERGENCY_ACTION_WARN);
switch (action) {
case EMERGENCY_ACTION_REBOOT:
- log_and_status(m, "Rebooting", reason);
+ log_and_status(m, warn, "Rebooting", reason);
(void) update_reboot_parameter_and_warn(reboot_arg);
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
@@ -56,7 +52,7 @@ int emergency_action(
break;
case EMERGENCY_ACTION_REBOOT_FORCE:
- log_and_status(m, "Forcibly rebooting", reason);
+ log_and_status(m, warn, "Forcibly rebooting", reason);
(void) update_reboot_parameter_and_warn(reboot_arg);
m->objective = MANAGER_REBOOT;
@@ -64,7 +60,7 @@ int emergency_action(
break;
case EMERGENCY_ACTION_REBOOT_IMMEDIATE:
- log_and_status(m, "Rebooting immediately", reason);
+ log_and_status(m, warn, "Rebooting immediately", reason);
sync();
@@ -78,18 +74,38 @@ int emergency_action(
(void) reboot(RB_AUTOBOOT);
break;
+ case EMERGENCY_ACTION_EXIT:
+ if (MANAGER_IS_USER(m) || detect_container() > 0) {
+ log_and_status(m, warn, "Exiting", reason);
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_EXIT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
+ break;
+ }
+
+ log_notice("Doing \"poweroff\" action instead of an \"exit\" emergency action.");
+ _fallthrough_;
+
case EMERGENCY_ACTION_POWEROFF:
- log_and_status(m, "Powering off", reason);
+ log_and_status(m, warn, "Powering off", reason);
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
break;
+ case EMERGENCY_ACTION_EXIT_FORCE:
+ if (MANAGER_IS_USER(m) || detect_container() > 0) {
+ log_and_status(m, warn, "Exiting immediately", reason);
+ m->objective = MANAGER_EXIT;
+ break;
+ }
+
+ log_notice("Doing \"poweroff-force\" action instead of an \"exit-force\" emergency action.");
+ _fallthrough_;
+
case EMERGENCY_ACTION_POWEROFF_FORCE:
- log_and_status(m, "Forcibly powering off", reason);
+ log_and_status(m, warn, "Forcibly powering off", reason);
m->objective = MANAGER_POWEROFF;
break;
case EMERGENCY_ACTION_POWEROFF_IMMEDIATE:
- log_and_status(m, "Powering off immediately", reason);
+ log_and_status(m, warn, "Powering off immediately", reason);
sync();
@@ -111,6 +127,26 @@ static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
[EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
[EMERGENCY_ACTION_POWEROFF] = "poweroff",
[EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force",
- [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate"
+ [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
+ [EMERGENCY_ACTION_EXIT] = "exit",
+ [EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
};
DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction);
+
+int parse_emergency_action(
+ const char *value,
+ bool system,
+ EmergencyAction *ret) {
+
+ EmergencyAction x;
+
+ x = emergency_action_from_string(value);
+ if (x < 0)
+ return -EINVAL;
+
+ if (!system && x != EMERGENCY_ACTION_NONE && x < _EMERGENCY_ACTION_FIRST_USER_ACTION)
+ return -EOPNOTSUPP;
+
+ *ret = x;
+ return 0;
+}
diff --git a/src/core/emergency-action.h b/src/core/emergency-action.h
index ea6590e78a..1fc4a11e30 100644
--- a/src/core/emergency-action.h
+++ b/src/core/emergency-action.h
@@ -9,14 +9,26 @@ typedef enum EmergencyAction {
EMERGENCY_ACTION_POWEROFF,
EMERGENCY_ACTION_POWEROFF_FORCE,
EMERGENCY_ACTION_POWEROFF_IMMEDIATE,
+ EMERGENCY_ACTION_EXIT,
+ _EMERGENCY_ACTION_FIRST_USER_ACTION = EMERGENCY_ACTION_EXIT,
+ EMERGENCY_ACTION_EXIT_FORCE,
_EMERGENCY_ACTION_MAX,
_EMERGENCY_ACTION_INVALID = -1
} EmergencyAction;
+typedef enum EmergencyActionFlags {
+ EMERGENCY_ACTION_IS_WATCHDOG = 1 << 0,
+ EMERGENCY_ACTION_WARN = 1 << 1,
+} EmergencyActionFlags;
+
#include "macro.h"
#include "manager.h"
-int emergency_action(Manager *m, EmergencyAction action, const char *reboot_arg, const char *reason);
+int emergency_action(Manager *m,
+ EmergencyAction action, EmergencyActionFlags options,
+ const char *reboot_arg, const char *reason);
const char* emergency_action_to_string(EmergencyAction i) _const_;
EmergencyAction emergency_action_from_string(const char *s) _pure_;
+
+int parse_emergency_action(const char *value, bool system, EmergencyAction *ret);
diff --git a/src/core/job.c b/src/core/job.c
index 6b17cc1d6f..5a88f52f48 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -973,7 +973,9 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user
u = j->unit;
job_finish_and_invalidate(j, JOB_TIMEOUT, true, false);
- emergency_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg, "job timed out");
+ emergency_action(u->manager, u->job_timeout_action,
+ EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN,
+ u->job_timeout_reboot_arg, "job timed out");
return 0;
}
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index f96775ee71..bfd1d5b15f 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -76,7 +76,6 @@ DEFINE_CONFIG_PARSE(config_parse_socket_protocol, supported_socket_protocol_from
DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits");
DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
@@ -4185,6 +4184,57 @@ int config_parse_job_running_timeout_sec(
return 0;
}
+int config_parse_emergency_action(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Manager *m = NULL;
+ EmergencyAction *x = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (unit)
+ m = ((Unit*) userdata)->manager;
+ else
+ m = data;
+
+ r = parse_emergency_action(rvalue, MANAGER_IS_SYSTEM(m), x);
+ if (r < 0) {
+ if (r == -EOPNOTSUPP && MANAGER_IS_USER(m)) {
+ /* Compat mode: remove for systemd 241. */
+
+ log_syntax(unit, LOG_INFO, filename, line, r,
+ "%s= in user mode specified as \"%s\", using \"exit-force\" instead.",
+ lvalue, rvalue);
+ *x = EMERGENCY_ACTION_EXIT_FORCE;
+ return 0;
+ }
+
+ if (r == -EOPNOTSUPP)
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "%s= specified as %s mode action, ignoring: %s",
+ lvalue, MANAGER_IS_SYSTEM(m) ? "user" : "system", rvalue);
+ else
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
diff --git a/src/core/manager.c b/src/core/manager.c
index 7b6a84108a..62703e1d7f 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -2540,7 +2540,7 @@ static void manager_handle_ctrl_alt_del(Manager *m) {
if (ratelimit_below(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == EMERGENCY_ACTION_NONE)
manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
else
- emergency_action(m, m->cad_burst_action, NULL,
+ emergency_action(m, m->cad_burst_action, EMERGENCY_ACTION_WARN, NULL,
"Ctrl-Alt-Del was pressed more than 7 times within 2s");
}
diff --git a/src/core/service.c b/src/core/service.c
index fa2c6996ad..29a70d1eea 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -538,8 +538,13 @@ static int service_verify(Service *s) {
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
- if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) {
- log_unit_error(UNIT(s), "Service lacks both ExecStart= and ExecStop= setting. Refusing.");
+ if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]
+ && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
+ /* FailureAction= only makes sense if one of the start or stop commands is specified.
+ * SuccessAction= will be executed unconditionally if no commands are specified. Hence,
+ * either a command or SuccessAction= are required. */
+
+ log_unit_error(UNIT(s), "Service has no ExecStart=, ExecStop=, or SuccessAction=. Refusing.");
return -ENOEXEC;
}
@@ -548,8 +553,8 @@ static int service_verify(Service *s) {
return -ENOEXEC;
}
- if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) {
- log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing.");
+ if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START] && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
+ log_unit_error(UNIT(s), "Service has no ExecStart= and no SuccessAction= settings and does not have RemainAfterExit=yes set. Refusing.");
return -ENOEXEC;
}
@@ -2024,6 +2029,12 @@ static void service_enter_start(Service *s) {
goto fail;
}
+ /* We force a fake state transition here. Otherwise, the unit would go directly from
+ * SERVICE_DEAD to SERVICE_DEAD without SERVICE_ACTIVATING or SERVICE_ACTIVE
+ * inbetween. This way we can later trigger actions that depend on the state
+ * transition, including SuccessAction=. */
+ service_set_state(s, SERVICE_START);
+
service_enter_start_post(s);
return;
}
diff --git a/src/core/unit.c b/src/core/unit.c
index d90456e03c..0ce9318556 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -1724,7 +1724,9 @@ int unit_start_limit_test(Unit *u) {
log_unit_warning(u, "Start request repeated too quickly.");
u->start_limit_hit = true;
- return emergency_action(u->manager, u->start_limit_action, u->reboot_arg, "unit failed");
+ return emergency_action(u->manager, u->start_limit_action,
+ EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN,
+ u->reboot_arg, "unit failed");
}
bool unit_shall_confirm_spawn(Unit *u) {
@@ -2498,9 +2500,11 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
unit_check_binds_to(u);
if (os != UNIT_FAILED && ns == UNIT_FAILED)
- (void) emergency_action(u->manager, u->failure_action, u->reboot_arg, "unit failed");
+ (void) emergency_action(u->manager, u->failure_action, 0,
+ u->reboot_arg, "unit failed");
else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && ns == UNIT_INACTIVE)
- (void) emergency_action(u->manager, u->success_action, u->reboot_arg, "unit succeeded");
+ (void) emergency_action(u->manager, u->success_action, 0,
+ u->reboot_arg, "unit succeeded");
}
unit_add_to_dbus_queue(u);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 5e3040f221..c8914f8f85 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -8443,7 +8443,7 @@ static int start_with_fallback(void) {
static int halt_now(enum action a) {
/* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
- * synce'd explicitly in advance. */
+ * synced explicitly in advance. */
if (!arg_no_sync && !arg_dry_run)
(void) sync();
diff --git a/src/test/meson.build b/src/test/meson.build
index 2e003f1fd2..a1770213a8 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -63,6 +63,11 @@ tests += [
libmount,
libblkid]],
+ [['src/test/test-emergency-action.c'],
+ [libcore,
+ libshared],
+ []],
+
[['src/test/test-job-type.c'],
[libcore,
libshared],
diff --git a/src/test/test-emergency-action.c b/src/test/test-emergency-action.c
new file mode 100644
index 0000000000..8ce28ed9f5
--- /dev/null
+++ b/src/test/test-emergency-action.c
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "emergency-action.h"
+#include "tests.h"
+
+static void test_parse_emergency_action(void) {
+ EmergencyAction x;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(parse_emergency_action("none", false, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_NONE);
+ assert_se(parse_emergency_action("reboot", false, &x) == -EOPNOTSUPP);
+ assert_se(parse_emergency_action("reboot-force", false, &x) == -EOPNOTSUPP);
+ assert_se(parse_emergency_action("reboot-immediate", false, &x) == -EOPNOTSUPP);
+ assert_se(parse_emergency_action("poweroff", false, &x) == -EOPNOTSUPP);
+ assert_se(parse_emergency_action("poweroff-force", false, &x) == -EOPNOTSUPP);
+ assert_se(parse_emergency_action("poweroff-immediate", false, &x) == -EOPNOTSUPP);
+ assert_se(x == EMERGENCY_ACTION_NONE);
+ assert_se(parse_emergency_action("exit", false, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_EXIT);
+ assert_se(parse_emergency_action("exit-force", false, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_EXIT_FORCE);
+ assert_se(parse_emergency_action("exit-forcee", false, &x) == -EINVAL);
+
+ assert_se(parse_emergency_action("none", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_NONE);
+ assert_se(parse_emergency_action("reboot", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_REBOOT);
+ assert_se(parse_emergency_action("reboot-force", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_REBOOT_FORCE);
+ assert_se(parse_emergency_action("reboot-immediate", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_REBOOT_IMMEDIATE);
+ assert_se(parse_emergency_action("poweroff", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_POWEROFF);
+ assert_se(parse_emergency_action("poweroff-force", true, &x) == 0);
+ assert_se(x == EMERGENCY_ACTION_POWEROFF_FORCE);
+ assert_se(parse_emergency_action("poweroff-immediate", true, &x) == 0);
+ assert_se(parse_emergency_action("exit", true, &x) == 0);
+ assert_se(parse_emergency_action("exit-force", true, &x) == 0);
+ assert_se(parse_emergency_action("exit-forcee", true, &x) == -EINVAL);
+ assert_se(x == EMERGENCY_ACTION_EXIT_FORCE);
+}
+
+int main(int argc, char **argv) {
+ test_setup_logging(LOG_INFO);
+
+ test_parse_emergency_action();
+
+ return EXIT_SUCCESS;
+}
diff --git a/units/meson.build b/units/meson.build
index e4ac6ced64..3cc86b3e92 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -85,6 +85,7 @@ units = [
'multi-user.target.wants/'],
['systemd-coredump.socket', 'ENABLE_COREDUMP',
'sockets.target.wants/'],
+ ['systemd-exit.service', ''],
['systemd-initctl.socket', '',
'sockets.target.wants/'],
['systemd-journal-gatewayd.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'],
@@ -97,6 +98,8 @@ units = [
'sockets.target.wants/'],
['systemd-networkd.socket', 'ENABLE_NETWORKD',
join_paths(pkgsysconfdir, 'system/sockets.target.wants/')],
+ ['systemd-poweroff.service', ''],
+ ['systemd-reboot.service', ''],
['systemd-rfkill.socket', 'ENABLE_RFKILL'],
['systemd-tmpfiles-clean.timer', '',
'timers.target.wants/'],
@@ -133,7 +136,6 @@ in_units = [
['systemd-binfmt.service', 'ENABLE_BINFMT',
'sysinit.target.wants/'],
['systemd-coredump@.service', 'ENABLE_COREDUMP'],
- ['systemd-exit.service', ''],
['systemd-firstboot.service', 'ENABLE_FIRSTBOOT',
'sysinit.target.wants/'],
['systemd-fsck-root.service', ''],
@@ -178,11 +180,9 @@ in_units = [
['systemd-nspawn@.service', ''],
['systemd-portabled.service', 'ENABLE_PORTABLED',
'dbus-org.freedesktop.portable1.service'],
- ['systemd-poweroff.service', ''],
['systemd-quotacheck.service', 'ENABLE_QUOTACHECK'],
['systemd-random-seed.service', 'ENABLE_RANDOMSEED',
'sysinit.target.wants/'],
- ['systemd-reboot.service', ''],
['systemd-remount-fs.service', '',
'local-fs.target.wants/'],
['systemd-resolved.service', 'ENABLE_RESOLVE',
diff --git a/units/systemd-exit.service.in b/units/systemd-exit.service
similarity index 88%
rename from units/systemd-exit.service.in
rename to units/systemd-exit.service
index 2fb6ebd767..6029b13a05 100644
--- a/units/systemd-exit.service.in
+++ b/units/systemd-exit.service
@@ -13,7 +13,4 @@ Documentation=man:systemd.special(7)
DefaultDependencies=no
Requires=shutdown.target
After=shutdown.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --force exit
+SuccessAction=exit
diff --git a/units/systemd-poweroff.service.in b/units/systemd-poweroff.service
similarity index 89%
rename from units/systemd-poweroff.service.in
rename to units/systemd-poweroff.service
index e9fd655508..8d1d54389b 100644
--- a/units/systemd-poweroff.service.in
+++ b/units/systemd-poweroff.service
@@ -13,7 +13,4 @@ Documentation=man:systemd-halt.service(8)
DefaultDependencies=no
Requires=shutdown.target umount.target final.target
After=shutdown.target umount.target final.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --force poweroff
+SuccessAction=poweroff-force
diff --git a/units/systemd-reboot.service.in b/units/systemd-reboot.service
similarity index 89%
rename from units/systemd-reboot.service.in
rename to units/systemd-reboot.service
index 4763ccfdca..505f60aabf 100644
--- a/units/systemd-reboot.service.in
+++ b/units/systemd-reboot.service
@@ -13,7 +13,4 @@ Documentation=man:systemd-halt.service(8)
DefaultDependencies=no
Requires=shutdown.target umount.target final.target
After=shutdown.target umount.target final.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --force reboot
+SuccessAction=reboot-force
diff --git a/units/user/meson.build b/units/user/meson.build
index b1c2e95597..36341a42f5 100644
--- a/units/user/meson.build
+++ b/units/user/meson.build
@@ -14,6 +14,7 @@ units = [
'sockets.target',
'sound.target',
'timers.target',
+ 'systemd-exit.service',
'systemd-tmpfiles-clean.timer',
]
@@ -23,7 +24,6 @@ foreach file : units
endforeach
in_units = [
- 'systemd-exit.service',
'systemd-tmpfiles-clean.service',
'systemd-tmpfiles-setup.service',
]
diff --git a/units/user/systemd-exit.service.in b/units/user/systemd-exit.service
similarity index 87%
rename from units/user/systemd-exit.service.in
rename to units/user/systemd-exit.service
index d69273f6b3..1d3b61e3ab 100644
--- a/units/user/systemd-exit.service.in
+++ b/units/user/systemd-exit.service
@@ -13,7 +13,4 @@ Documentation=man:systemd.special(7)
DefaultDependencies=no
Requires=shutdown.target
After=shutdown.target
-
-[Service]
-Type=oneshot
-ExecStart=@SYSTEMCTL@ --user --force exit
+SuccessAction=exit-force