mirror of
https://github.com/systemd/systemd
synced 2024-10-15 12:34:37 +00:00
Merge pull request #10428 from keszybz/failure-actions
Implement manager status changes using SuccessAction=
This commit is contained in:
commit
a42984dbc7
10
TODO
10
TODO
|
@ -2,6 +2,10 @@ Bugfixes:
|
||||||
|
|
||||||
* copy.c: set the right chattrs before copying files and others after
|
* 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:
|
External:
|
||||||
|
|
||||||
* Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros.
|
* 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
|
* 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
|
* optionally: turn on cgroup delegation for per-session scope units
|
||||||
|
|
||||||
* introduce per-unit (i.e. per-slice, per-service) journal log size limits.
|
* introduce per-unit (i.e. per-slice, per-service) journal log size limits.
|
||||||
|
|
|
@ -864,6 +864,29 @@
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><varname>JobTimeoutSec=</varname></term>
|
<term><varname>JobTimeoutSec=</varname></term>
|
||||||
<term><varname>JobRunningTimeoutSec=</varname></term>
|
<term><varname>JobRunningTimeoutSec=</varname></term>
|
||||||
|
@ -920,29 +943,13 @@
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>StartLimitAction=</varname></term>
|
<term><varname>StartLimitAction=</varname></term>
|
||||||
|
|
||||||
<listitem><para>Configure the action to take if the rate limit configured with
|
<listitem><para>Configure an additional action to take if the rate limit configured with
|
||||||
<varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit. Takes one of
|
<varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit. Takes the same
|
||||||
<option>none</option>, <option>reboot</option>, <option>reboot-force</option>,
|
values as the setting <varname>FailureAction=</varname>/<varname>SuccessAction=</varname> settings and executes
|
||||||
<option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option> or
|
the same actions. If <option>none</option> is set, hitting the rate limit will trigger no action besides that
|
||||||
<option>poweroff-immediate</option>. If <option>none</option> is set, hitting the rate limit will trigger no
|
the start will not be permitted. Defaults to <option>none</option>.</para></listitem>
|
||||||
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>
|
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><varname>RebootArgument=</varname></term>
|
<term><varname>RebootArgument=</varname></term>
|
||||||
|
|
|
@ -1166,7 +1166,7 @@ int cg_is_empty(const char *controller, const char *path) {
|
||||||
|
|
||||||
r = cg_enumerate_processes(controller, path, &f);
|
r = cg_enumerate_processes(controller, path, &f);
|
||||||
if (r == -ENOENT)
|
if (r == -ENOENT)
|
||||||
return 1;
|
return true;
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -1196,6 +1196,8 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
|
||||||
* via the "populated" attribute of "cgroup.events". */
|
* via the "populated" attribute of "cgroup.events". */
|
||||||
|
|
||||||
r = cg_read_event(controller, path, "populated", &t);
|
r = cg_read_event(controller, path, "populated", &t);
|
||||||
|
if (r == -ENOENT)
|
||||||
|
return true;
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -1210,7 +1212,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
|
||||||
|
|
||||||
r = cg_enumerate_subgroups(controller, path, &d);
|
r = cg_enumerate_subgroups(controller, path, &d);
|
||||||
if (r == -ENOENT)
|
if (r == -ENOENT)
|
||||||
return 1;
|
return true;
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|
|
@ -1299,8 +1299,43 @@ static int bus_unit_set_live_property(
|
||||||
return 0;
|
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(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 BUS_DEFINE_SET_TRANSIENT_PARSE(job_mode, JobMode, job_mode_from_string);
|
||||||
|
|
||||||
static int bus_set_transient_conditions(
|
static int bus_set_transient_conditions(
|
||||||
|
|
|
@ -10,17 +10,20 @@
|
||||||
#include "special.h"
|
#include "special.h"
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
#include "terminal-util.h"
|
#include "terminal-util.h"
|
||||||
|
#include "virt.h"
|
||||||
|
|
||||||
static void log_and_status(Manager *m, const char *message, const char *reason) {
|
static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
|
||||||
log_warning("%s: %s", message, reason);
|
log_full(warn ? LOG_WARNING : LOG_DEBUG, "%s: %s", message, reason);
|
||||||
manager_status_printf(m, STATUS_TYPE_EMERGENCY,
|
if (warn)
|
||||||
ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL,
|
manager_status_printf(m, STATUS_TYPE_EMERGENCY,
|
||||||
"%s: %s", message, reason);
|
ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL,
|
||||||
|
"%s: %s", message, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
int emergency_action(
|
int emergency_action(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
EmergencyAction action,
|
EmergencyAction action,
|
||||||
|
EmergencyActionFlags options,
|
||||||
const char *reboot_arg,
|
const char *reboot_arg,
|
||||||
const char *reason) {
|
const char *reason) {
|
||||||
|
|
||||||
|
@ -31,24 +34,17 @@ int emergency_action(
|
||||||
if (action == EMERGENCY_ACTION_NONE)
|
if (action == EMERGENCY_ACTION_NONE)
|
||||||
return -ECANCELED;
|
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);
|
log_warning("Watchdog disabled! Not acting on: %s", reason);
|
||||||
return -ECANCELED;
|
return -ECANCELED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MANAGER_IS_SYSTEM(m)) {
|
bool warn = FLAGS_SET(options, EMERGENCY_ACTION_WARN);
|
||||||
/* Downgrade all options to simply exiting if we run
|
|
||||||
* in user mode */
|
|
||||||
|
|
||||||
log_warning("Exiting: %s", reason);
|
|
||||||
m->objective = MANAGER_EXIT;
|
|
||||||
return -ECANCELED;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
|
||||||
case EMERGENCY_ACTION_REBOOT:
|
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) 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);
|
(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;
|
break;
|
||||||
|
|
||||||
case EMERGENCY_ACTION_REBOOT_FORCE:
|
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);
|
(void) update_reboot_parameter_and_warn(reboot_arg);
|
||||||
m->objective = MANAGER_REBOOT;
|
m->objective = MANAGER_REBOOT;
|
||||||
|
@ -64,7 +60,7 @@ int emergency_action(
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EMERGENCY_ACTION_REBOOT_IMMEDIATE:
|
case EMERGENCY_ACTION_REBOOT_IMMEDIATE:
|
||||||
log_and_status(m, "Rebooting immediately", reason);
|
log_and_status(m, warn, "Rebooting immediately", reason);
|
||||||
|
|
||||||
sync();
|
sync();
|
||||||
|
|
||||||
|
@ -78,18 +74,38 @@ int emergency_action(
|
||||||
(void) reboot(RB_AUTOBOOT);
|
(void) reboot(RB_AUTOBOOT);
|
||||||
break;
|
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:
|
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);
|
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
|
||||||
break;
|
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:
|
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;
|
m->objective = MANAGER_POWEROFF;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EMERGENCY_ACTION_POWEROFF_IMMEDIATE:
|
case EMERGENCY_ACTION_POWEROFF_IMMEDIATE:
|
||||||
log_and_status(m, "Powering off immediately", reason);
|
log_and_status(m, warn, "Powering off immediately", reason);
|
||||||
|
|
||||||
sync();
|
sync();
|
||||||
|
|
||||||
|
@ -111,6 +127,26 @@ static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
|
||||||
[EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
|
[EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
|
||||||
[EMERGENCY_ACTION_POWEROFF] = "poweroff",
|
[EMERGENCY_ACTION_POWEROFF] = "poweroff",
|
||||||
[EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force",
|
[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);
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -9,14 +9,26 @@ typedef enum EmergencyAction {
|
||||||
EMERGENCY_ACTION_POWEROFF,
|
EMERGENCY_ACTION_POWEROFF,
|
||||||
EMERGENCY_ACTION_POWEROFF_FORCE,
|
EMERGENCY_ACTION_POWEROFF_FORCE,
|
||||||
EMERGENCY_ACTION_POWEROFF_IMMEDIATE,
|
EMERGENCY_ACTION_POWEROFF_IMMEDIATE,
|
||||||
|
EMERGENCY_ACTION_EXIT,
|
||||||
|
_EMERGENCY_ACTION_FIRST_USER_ACTION = EMERGENCY_ACTION_EXIT,
|
||||||
|
EMERGENCY_ACTION_EXIT_FORCE,
|
||||||
_EMERGENCY_ACTION_MAX,
|
_EMERGENCY_ACTION_MAX,
|
||||||
_EMERGENCY_ACTION_INVALID = -1
|
_EMERGENCY_ACTION_INVALID = -1
|
||||||
} EmergencyAction;
|
} EmergencyAction;
|
||||||
|
|
||||||
|
typedef enum EmergencyActionFlags {
|
||||||
|
EMERGENCY_ACTION_IS_WATCHDOG = 1 << 0,
|
||||||
|
EMERGENCY_ACTION_WARN = 1 << 1,
|
||||||
|
} EmergencyActionFlags;
|
||||||
|
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "manager.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_;
|
const char* emergency_action_to_string(EmergencyAction i) _const_;
|
||||||
EmergencyAction emergency_action_from_string(const char *s) _pure_;
|
EmergencyAction emergency_action_from_string(const char *s) _pure_;
|
||||||
|
|
||||||
|
int parse_emergency_action(const char *value, bool system, EmergencyAction *ret);
|
||||||
|
|
|
@ -973,7 +973,9 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user
|
||||||
u = j->unit;
|
u = j->unit;
|
||||||
job_finish_and_invalidate(j, JOB_TIMEOUT, true, false);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(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_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_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_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_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");
|
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;
|
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
|
#define FOLLOW_MAX 8
|
||||||
|
|
||||||
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
|
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
|
||||||
|
|
|
@ -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)
|
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);
|
manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
|
||||||
else
|
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");
|
"Ctrl-Alt-Del was pressed more than 7 times within 2s");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -538,8 +538,13 @@ static int service_verify(Service *s) {
|
||||||
if (UNIT(s)->load_state != UNIT_LOADED)
|
if (UNIT(s)->load_state != UNIT_LOADED)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) {
|
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.");
|
&& 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;
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,8 +553,8 @@ static int service_verify(Service *s) {
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) {
|
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= setting, which is only allowed for RemainAfterExit=yes services. Refusing.");
|
log_unit_error(UNIT(s), "Service has no ExecStart= and no SuccessAction= settings and does not have RemainAfterExit=yes set. Refusing.");
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2024,6 +2029,12 @@ static void service_enter_start(Service *s) {
|
||||||
goto fail;
|
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);
|
service_enter_start_post(s);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1724,7 +1724,9 @@ int unit_start_limit_test(Unit *u) {
|
||||||
log_unit_warning(u, "Start request repeated too quickly.");
|
log_unit_warning(u, "Start request repeated too quickly.");
|
||||||
u->start_limit_hit = true;
|
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) {
|
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);
|
unit_check_binds_to(u);
|
||||||
|
|
||||||
if (os != UNIT_FAILED && ns == UNIT_FAILED)
|
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)
|
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);
|
unit_add_to_dbus_queue(u);
|
||||||
|
|
|
@ -8443,7 +8443,7 @@ static int start_with_fallback(void) {
|
||||||
|
|
||||||
static int halt_now(enum action a) {
|
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
|
/* 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)
|
if (!arg_no_sync && !arg_dry_run)
|
||||||
(void) sync();
|
(void) sync();
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,11 @@ tests += [
|
||||||
libmount,
|
libmount,
|
||||||
libblkid]],
|
libblkid]],
|
||||||
|
|
||||||
|
[['src/test/test-emergency-action.c'],
|
||||||
|
[libcore,
|
||||||
|
libshared],
|
||||||
|
[]],
|
||||||
|
|
||||||
[['src/test/test-job-type.c'],
|
[['src/test/test-job-type.c'],
|
||||||
[libcore,
|
[libcore,
|
||||||
libshared],
|
libshared],
|
||||||
|
|
51
src/test/test-emergency-action.c
Normal file
51
src/test/test-emergency-action.c
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -85,6 +85,7 @@ units = [
|
||||||
'multi-user.target.wants/'],
|
'multi-user.target.wants/'],
|
||||||
['systemd-coredump.socket', 'ENABLE_COREDUMP',
|
['systemd-coredump.socket', 'ENABLE_COREDUMP',
|
||||||
'sockets.target.wants/'],
|
'sockets.target.wants/'],
|
||||||
|
['systemd-exit.service', ''],
|
||||||
['systemd-initctl.socket', '',
|
['systemd-initctl.socket', '',
|
||||||
'sockets.target.wants/'],
|
'sockets.target.wants/'],
|
||||||
['systemd-journal-gatewayd.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'],
|
['systemd-journal-gatewayd.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'],
|
||||||
|
@ -97,6 +98,8 @@ units = [
|
||||||
'sockets.target.wants/'],
|
'sockets.target.wants/'],
|
||||||
['systemd-networkd.socket', 'ENABLE_NETWORKD',
|
['systemd-networkd.socket', 'ENABLE_NETWORKD',
|
||||||
join_paths(pkgsysconfdir, 'system/sockets.target.wants/')],
|
join_paths(pkgsysconfdir, 'system/sockets.target.wants/')],
|
||||||
|
['systemd-poweroff.service', ''],
|
||||||
|
['systemd-reboot.service', ''],
|
||||||
['systemd-rfkill.socket', 'ENABLE_RFKILL'],
|
['systemd-rfkill.socket', 'ENABLE_RFKILL'],
|
||||||
['systemd-tmpfiles-clean.timer', '',
|
['systemd-tmpfiles-clean.timer', '',
|
||||||
'timers.target.wants/'],
|
'timers.target.wants/'],
|
||||||
|
@ -133,7 +136,6 @@ in_units = [
|
||||||
['systemd-binfmt.service', 'ENABLE_BINFMT',
|
['systemd-binfmt.service', 'ENABLE_BINFMT',
|
||||||
'sysinit.target.wants/'],
|
'sysinit.target.wants/'],
|
||||||
['systemd-coredump@.service', 'ENABLE_COREDUMP'],
|
['systemd-coredump@.service', 'ENABLE_COREDUMP'],
|
||||||
['systemd-exit.service', ''],
|
|
||||||
['systemd-firstboot.service', 'ENABLE_FIRSTBOOT',
|
['systemd-firstboot.service', 'ENABLE_FIRSTBOOT',
|
||||||
'sysinit.target.wants/'],
|
'sysinit.target.wants/'],
|
||||||
['systemd-fsck-root.service', ''],
|
['systemd-fsck-root.service', ''],
|
||||||
|
@ -178,11 +180,9 @@ in_units = [
|
||||||
['systemd-nspawn@.service', ''],
|
['systemd-nspawn@.service', ''],
|
||||||
['systemd-portabled.service', 'ENABLE_PORTABLED',
|
['systemd-portabled.service', 'ENABLE_PORTABLED',
|
||||||
'dbus-org.freedesktop.portable1.service'],
|
'dbus-org.freedesktop.portable1.service'],
|
||||||
['systemd-poweroff.service', ''],
|
|
||||||
['systemd-quotacheck.service', 'ENABLE_QUOTACHECK'],
|
['systemd-quotacheck.service', 'ENABLE_QUOTACHECK'],
|
||||||
['systemd-random-seed.service', 'ENABLE_RANDOMSEED',
|
['systemd-random-seed.service', 'ENABLE_RANDOMSEED',
|
||||||
'sysinit.target.wants/'],
|
'sysinit.target.wants/'],
|
||||||
['systemd-reboot.service', ''],
|
|
||||||
['systemd-remount-fs.service', '',
|
['systemd-remount-fs.service', '',
|
||||||
'local-fs.target.wants/'],
|
'local-fs.target.wants/'],
|
||||||
['systemd-resolved.service', 'ENABLE_RESOLVE',
|
['systemd-resolved.service', 'ENABLE_RESOLVE',
|
||||||
|
|
|
@ -13,7 +13,4 @@ Documentation=man:systemd.special(7)
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
Requires=shutdown.target
|
Requires=shutdown.target
|
||||||
After=shutdown.target
|
After=shutdown.target
|
||||||
|
SuccessAction=exit
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=@SYSTEMCTL@ --force exit
|
|
|
@ -13,7 +13,4 @@ Documentation=man:systemd-halt.service(8)
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
Requires=shutdown.target umount.target final.target
|
Requires=shutdown.target umount.target final.target
|
||||||
After=shutdown.target umount.target final.target
|
After=shutdown.target umount.target final.target
|
||||||
|
SuccessAction=poweroff-force
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=@SYSTEMCTL@ --force poweroff
|
|
|
@ -13,7 +13,4 @@ Documentation=man:systemd-halt.service(8)
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
Requires=shutdown.target umount.target final.target
|
Requires=shutdown.target umount.target final.target
|
||||||
After=shutdown.target umount.target final.target
|
After=shutdown.target umount.target final.target
|
||||||
|
SuccessAction=reboot-force
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=@SYSTEMCTL@ --force reboot
|
|
|
@ -14,6 +14,7 @@ units = [
|
||||||
'sockets.target',
|
'sockets.target',
|
||||||
'sound.target',
|
'sound.target',
|
||||||
'timers.target',
|
'timers.target',
|
||||||
|
'systemd-exit.service',
|
||||||
'systemd-tmpfiles-clean.timer',
|
'systemd-tmpfiles-clean.timer',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -23,7 +24,6 @@ foreach file : units
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
in_units = [
|
in_units = [
|
||||||
'systemd-exit.service',
|
|
||||||
'systemd-tmpfiles-clean.service',
|
'systemd-tmpfiles-clean.service',
|
||||||
'systemd-tmpfiles-setup.service',
|
'systemd-tmpfiles-setup.service',
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,7 +13,4 @@ Documentation=man:systemd.special(7)
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
Requires=shutdown.target
|
Requires=shutdown.target
|
||||||
After=shutdown.target
|
After=shutdown.target
|
||||||
|
SuccessAction=exit-force
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=@SYSTEMCTL@ --user --force exit
|
|
Loading…
Reference in a new issue