]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #10428 from keszybz/failure-actions
authorLennart Poettering <lennart@poettering.net>
Wed, 17 Oct 2018 19:29:10 +0000 (21:29 +0200)
committerGitHub <noreply@github.com>
Wed, 17 Oct 2018 19:29:10 +0000 (21:29 +0200)
Implement manager status changes using SuccessAction=

20 files changed:
TODO
man/systemd.unit.xml
src/basic/cgroup-util.c
src/core/dbus-unit.c
src/core/emergency-action.c
src/core/emergency-action.h
src/core/job.c
src/core/load-fragment.c
src/core/manager.c
src/core/service.c
src/core/unit.c
src/systemctl/systemctl.c
src/test/meson.build
src/test/test-emergency-action.c [new file with mode: 0644]
units/meson.build
units/systemd-exit.service [moved from units/systemd-exit.service.in with 88% similarity]
units/systemd-poweroff.service [moved from units/systemd-poweroff.service.in with 89% similarity]
units/systemd-reboot.service [moved from units/systemd-reboot.service.in with 89% similarity]
units/user/meson.build
units/user/systemd-exit.service [moved from units/user/systemd-exit.service.in with 87% similarity]

diff --git a/TODO b/TODO
index 957dde27c3a538a73d374e17ceddc19326090fea..e61050051ab0358a828e3226d44dc8c04d42d538 100644 (file)
--- 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.
index f418f5b5eec4747deca5add05cd063f1f0d91e40..467b905f14e8ceb11966151de408208957258c8d 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>FailureAction=</varname></term>
+        <term><varname>SuccessAction=</varname></term>
+
+        <listitem><para>Configure the action to take when the unit stops and enters a failed state or inactive state.
+        Takes one of <option>none</option>, <option>reboot</option>, <option>reboot-force</option>,
+        <option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option>,
+        <option>poweroff-immediate</option>, <option>exit</option>, and <option>exit-force</option>. In system mode,
+        all options are allowed. In user mode, only <option>none</option>, <option>exit</option>, and
+        <option>exit-force</option> are allowed. Both options default to <option>none</option>.</para>
+
+        <para>If <option>none</option> is set, no action will be triggered. <option>reboot</option> causes a reboot
+        following the normal shutdown procedure (i.e. equivalent to <command>systemctl reboot</command>).
+        <option>reboot-force</option> causes a forced reboot which will terminate all processes forcibly but should
+        cause no dirty file systems on reboot (i.e. equivalent to <command>systemctl reboot -f</command>) and
+        <option>reboot-immediate</option> causes immediate execution of the
+        <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call, which
+        might result in data loss. Similarly, <option>poweroff</option>, <option>poweroff-force</option>,
+        <option>poweroff-immediate</option> have the effect of powering down the system with similar
+        semantics. <option>exit</option> causes the manager to exit following the normal shutdown procedure, and
+        <option>exit-force</option> causes it terminate without shutting down services.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>JobTimeoutSec=</varname></term>
         <term><varname>JobRunningTimeoutSec=</varname></term>
       <varlistentry>
         <term><varname>StartLimitAction=</varname></term>
 
-        <listitem><para>Configure the action to take if the rate limit configured with
-        <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit. Takes one of
-        <option>none</option>, <option>reboot</option>, <option>reboot-force</option>,
-        <option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option> or
-        <option>poweroff-immediate</option>. If <option>none</option> is set, hitting the rate limit will trigger no
-        action besides that the start will not be permitted. <option>reboot</option> causes a reboot following the
-        normal shutdown procedure (i.e. equivalent to <command>systemctl reboot</command>).
-        <option>reboot-force</option> causes a forced reboot which will terminate all processes forcibly but should
-        cause no dirty file systems on reboot (i.e. equivalent to <command>systemctl reboot -f</command>) and
-        <option>reboot-immediate</option> causes immediate execution of the
-        <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call, which
-        might result in data loss. Similarly, <option>poweroff</option>, <option>poweroff-force</option>,
-        <option>poweroff-immediate</option> have the effect of powering down the system with similar
-        semantics. Defaults to <option>none</option>.</para></listitem>
+        <listitem><para>Configure an additional action to take if the rate limit configured with
+        <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit.  Takes the same
+        values as the setting <varname>FailureAction=</varname>/<varname>SuccessAction=</varname> settings and executes
+        the same actions. If <option>none</option> is set, hitting the rate limit will trigger no action besides that
+        the start will not be permitted. Defaults to <option>none</option>.</para></listitem>
       </varlistentry>
 
-      <varlistentry>
-        <term><varname>FailureAction=</varname></term>
-        <term><varname>SuccessAction=</varname></term>
-        <listitem><para>Configure the action to take when the unit stops and enters a failed state or inactive
-        state. Takes the same values as the setting <varname>StartLimitAction=</varname> setting and executes the same
-        actions. Both options default to <option>none</option>.</para></listitem>
-      </varlistentry>
 
       <varlistentry>
         <term><varname>RebootArgument=</varname></term>
index 7728b63a5f2999250f49a5da3030a0ca0da926f3..b725a1776138909465208804bd1372267627279f 100644 (file)
@@ -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;
 
index c4530f0c8b7c26516aaaed2fd18425953fe3b471..daec85a73b74b642dff8fbc12e99bbf3fcf84d49 100644 (file)
@@ -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(
index 6f0b0a8f338a050ff7528355bd609b5e4425d497..c91d2bf5ff5a2e770391ea3cd3727afddfb023a9 100644 (file)
 #include "special.h"
 #include "string-table.h"
 #include "terminal-util.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);
+#include "virt.h"
+
+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;
+}
index ea6590e78a83beda7f1c39e17f68f51a3353faee..1fc4a11e306818bd4bdc4f3f7e067613837c0bdf 100644 (file)
@@ -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);
index 6b17cc1d6f6da22014d720e601cfd9bf5b4f3539..5a88f52f4827b7c6346dfb46332063abf874c07f 100644 (file)
@@ -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;
 }
index f96775ee7195c739f8830983a88117236904e7e5..bfd1d5b15f09bd2d0865584c77c86866ba80905c 100644 (file)
@@ -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) {
index 7b6a84108a21baf5afae6867e6447cbaca99d576..62703e1d7f435c6b719752b87e95163121cb9611 100644 (file)
@@ -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");
 }
 
index fa2c6996adc283d88cdf16b3929add0a5d78efa2..29a70d1eea50529527f232489be4eedaa5a4d38a 100644 (file)
@@ -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;
         }
index d90456e03c343c1d80bf378034dc54b448da6eac..0ce9318556ffc8dec593d5acd8685339a42ebee2 100644 (file)
@@ -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);
index 5e3040f221d3d02813844fc115473058e4772e0b..c8914f8f85634262329cbca6d0c396d0488d76bc 100644 (file)
@@ -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();
 
index 2e003f1fd2fa338afd632b16d741a65ee6e13be1..a1770213a801b633fcefe08bb8d6246592c6319f 100644 (file)
@@ -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 (file)
index 0000000..8ce28ed
--- /dev/null
@@ -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;
+}
index e4ac6ced64365c906c2b85cbfd1bca578e45892e..3cc86b3e92e6f494fbf382b2364d40bff31e07c8 100644 (file)
@@ -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',
similarity index 88%
rename from units/systemd-exit.service.in
rename to units/systemd-exit.service
index 2fb6ebd76792478454999f3b3b3bacb7fab30910..6029b13a0526e569ceb19966276a4021fac8a005 100644 (file)
@@ -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
similarity index 89%
rename from units/systemd-poweroff.service.in
rename to units/systemd-poweroff.service
index e9fd6555088ba026611c96c88249d8d1acd0309a..8d1d54389b9984760e560c7452031b53646507c7 100644 (file)
@@ -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
similarity index 89%
rename from units/systemd-reboot.service.in
rename to units/systemd-reboot.service
index 4763ccfdca75231756a5ef3a06f00bd0715ea35c..505f60aabf026a1bccc1fb3c982d9fd4886de334 100644 (file)
@@ -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
index b1c2e95597682c537210b486bc7d85a73e0cc2a9..36341a42f5abf48a22fadd7b075a18fb49a761ed 100644 (file)
@@ -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',
 ]
similarity index 87%
rename from units/user/systemd-exit.service.in
rename to units/user/systemd-exit.service
index d69273f6b30d31ad8f6dce5c2482647eb3b707df..1d3b61e3ab1e74d7d42dbc73f1db1c2dc1b4d949 100644 (file)
@@ -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