Merge pull request #7363 from poettering/success-action

Generalize FailureAction=, and add SuccessAction=
This commit is contained in:
Lennart Poettering 2017-11-21 11:57:42 +01:00 committed by GitHub
commit 0b0c55fafd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 129 additions and 19 deletions

8
TODO
View file

@ -82,6 +82,14 @@ Features:
the quota of a the user indicated in User= via unit file settings, like the the quota of a the user indicated in User= via unit file settings, like the
other resource management concepts. Would mix nicely with DynamicUser=1 other resource management concepts. Would mix nicely with DynamicUser=1
* Introduce "exit" as an EmergencyAction value, and allow to configure a
per-unit success/failure exit code to configure. This would be useful for
running commands inside of services inside of containers, which could then
propagate their failure state all the way up.
* In DynamicUser= mode: before selecting a UID, use disk quota APIs on relevant
disks to see if the UID is already in use.
* add dissect_image_warn() as a wrapper around dissect_image() that prints * add dissect_image_warn() as a wrapper around dissect_image() that prints
friendly log messages for the returned errors, so that we don't have to friendly log messages for the returned errors, so that we don't have to
duplicate that in nspawn, systemd-dissect and PID 1. duplicate that in nspawn, systemd-dissect and PID 1.

View file

@ -893,14 +893,6 @@
effect.</para></listitem> effect.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>FailureAction=</varname></term>
<listitem><para>Configure the action to take when the service enters a failed state. Takes the same values as
the unit setting <varname>StartLimitAction=</varname> and executes the same actions (see
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>). Defaults to
<option>none</option>. </para></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>FileDescriptorStoreMax=</varname></term> <term><varname>FileDescriptorStoreMax=</varname></term>
<listitem><para>Configure how many file descriptors may be stored in the service manager for the service using <listitem><para>Configure how many file descriptors may be stored in the service manager for the service using

View file

@ -877,11 +877,21 @@
semantics. Defaults to <option>none</option>.</para></listitem> 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 (see
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>). Both options
default to <option>none</option>.</para></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>RebootArgument=</varname></term> <term><varname>RebootArgument=</varname></term>
<listitem><para>Configure the optional argument for the <listitem><para>Configure the optional argument for the
<citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call if <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call if
<varname>StartLimitAction=</varname> or a service's <varname>FailureAction=</varname> is a reboot action. This <varname>StartLimitAction=</varname> or <varname>FailureAction=</varname> is a reboot action. This
works just like the optional argument to <command>systemctl reboot</command> command.</para></listitem> works just like the optional argument to <command>systemctl reboot</command> command.</para></listitem>
</varlistentry> </varlistentry>

View file

@ -51,7 +51,6 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0), BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
SD_BUS_PROPERTY("FailureAction", "s", property_get_emergency_action, offsetof(Service, emergency_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemainAfterExit", "b", bus_property_get_bool, offsetof(Service, remain_after_exit), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RemainAfterExit", "b", bus_property_get_bool, offsetof(Service, remain_after_exit), SD_BUS_VTABLE_PROPERTY_CONST),
@ -82,6 +81,7 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("FailureAction", "s", property_get_emergency_action, offsetof(Unit, failure_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_VTABLE_END SD_BUS_VTABLE_END
}; };

View file

@ -798,6 +798,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("FailureAction", "s", property_get_emergency_action, offsetof(Unit, failure_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SuccessAction", "s", property_get_emergency_action, offsetof(Unit, success_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), 0), SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), 0),
SD_BUS_PROPERTY("CollectMode", "s", property_get_collect_mode, offsetof(Unit, collect_mode), 0), SD_BUS_PROPERTY("CollectMode", "s", property_get_collect_mode, offsetof(Unit, collect_mode), 0),
@ -1470,6 +1472,30 @@ static int bus_unit_set_transient_property(
return 1; return 1;
} else if (STR_IN_SET(name, "FailureAction", "SuccessAction")) {
EmergencyAction action;
const char *s;
r = sd_bus_message_read(message, "s", &s);
if (r < 0)
return r;
action = emergency_action_from_string(s);
if (action < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid emergency action: %s", s);
if (mode != UNIT_CHECK) {
if (streq(name, "FailureAction"))
u->failure_action = action;
else
u->success_action = action;
unit_write_drop_in_format(u, mode, name, "%s=%s", name, emergency_action_to_string(action));
}
return 1;
} else if (streq(name, "AddRef")) { } else if (streq(name, "AddRef")) {
int b; int b;

View file

@ -223,6 +223,8 @@ m4_dnl The following is a legacy alias name for compatibility
Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
Unit.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action) Unit.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action)
Unit.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_action)
Unit.SuccessAction, config_parse_emergency_action, 0, offsetof(Unit, success_action)
Unit.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg) Unit.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions) Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions)
Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions) Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions)
@ -281,12 +283,12 @@ Service.TimeoutStartSec, config_parse_service_timeout, 0,
Service.TimeoutStopSec, config_parse_service_timeout, 0, 0 Service.TimeoutStopSec, config_parse_service_timeout, 0, 0
Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec) Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec) Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
m4_dnl The following three only exist for compatibility, they moved into Unit, see above m4_dnl The following five only exist for compatibility, they moved into Unit, see above
Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
Service.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action) Service.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action)
Service.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_action)
Service.RebootArgument, config_parse_unit_path_printf, 0, offsetof(Unit, reboot_arg) Service.RebootArgument, config_parse_unit_path_printf, 0, offsetof(Unit, reboot_arg)
Service.FailureAction, config_parse_emergency_action, 0, offsetof(Service, emergency_action)
Service.Type, config_parse_service_type, 0, offsetof(Service, type) Service.Type, config_parse_service_type, 0, offsetof(Service, type)
Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart) Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart)
Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only) Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only)

View file

@ -1532,9 +1532,6 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
if (s->result != SERVICE_SUCCESS)
emergency_action(UNIT(s)->manager, s->emergency_action, UNIT(s)->reboot_arg, "service failed");
if (allow_restart && service_shall_restart(s)) { if (allow_restart && service_shall_restart(s)) {
r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec)); r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec));

View file

@ -174,8 +174,6 @@ struct Service {
char *status_text; char *status_text;
int status_errno; int status_errno;
EmergencyAction emergency_action;
UnitRef accept_socket; UnitRef accept_socket;
sd_event_source *timer_event_source; sd_event_source *timer_event_source;

View file

@ -1181,6 +1181,11 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
STRV_FOREACH(j, u->dropin_paths) STRV_FOREACH(j, u->dropin_paths)
fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j); fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
if (u->failure_action != EMERGENCY_ACTION_NONE)
fprintf(f, "%s\tFailure Action: %s\n", prefix, emergency_action_to_string(u->failure_action));
if (u->success_action != EMERGENCY_ACTION_NONE)
fprintf(f, "%s\tSuccess Action: %s\n", prefix, emergency_action_to_string(u->success_action));
if (u->job_timeout != USEC_INFINITY) if (u->job_timeout != USEC_INFINITY)
fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0)); fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
@ -2503,6 +2508,11 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
* units go directly from starting to inactive, * units go directly from starting to inactive,
* without ever entering started.) */ * without ever entering started.) */
unit_check_binds_to(u); 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");
else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && ns == UNIT_INACTIVE)
(void) emergency_action(u->manager, u->success_action, u->reboot_arg, "unit succeeded");
} }
unit_add_to_dbus_queue(u); unit_add_to_dbus_queue(u);

View file

@ -248,6 +248,9 @@ struct Unit {
/* Put a ratelimit on unit starting */ /* Put a ratelimit on unit starting */
RateLimit start_limit; RateLimit start_limit;
EmergencyAction start_limit_action; EmergencyAction start_limit_action;
EmergencyAction failure_action;
EmergencyAction success_action;
char *reboot_arg; char *reboot_arg;
/* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */ /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */

View file

@ -408,7 +408,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"RootDirectory", "SyslogIdentifier", "ProtectSystem", "RootDirectory", "SyslogIdentifier", "ProtectSystem",
"ProtectHome", "SELinuxContext", "Restart", "RootImage", "ProtectHome", "SELinuxContext", "Restart", "RootImage",
"NotifyAccess", "RuntimeDirectoryPreserve", "Personality", "NotifyAccess", "RuntimeDirectoryPreserve", "Personality",
"KeyringMode", "CollectMode")) "KeyringMode", "CollectMode", "FailureAction", "SuccessAction"))
r = sd_bus_message_append(m, "v", "s", eq); r = sd_bus_message_append(m, "v", "s", eq);
else if (streq(field, "StandardInputData")) { else if (streq(field, "StandardInputData")) {

View file

@ -0,0 +1,4 @@
include ../Makefile.guess
all setup clean run:
@basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@

View file

@ -0,0 +1,42 @@
#!/bin/bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
TEST_DESCRIPTION="FailureAction= operation"
. $TEST_BASE_DIR/test-functions
QEMU_TIMEOUT=180
test_setup() {
create_empty_image
mkdir -p $TESTDIR/root
mount ${LOOPDEV}p1 $TESTDIR/root
(
LOG_LEVEL=5
eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
setup_basic_environment
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
Description=Testsuite service
[Service]
ExecStart=/bin/bash -x /testsuite.sh
Type=oneshot
StandardOutput=tty
StandardError=tty
EOF
cp testsuite.sh $initdir/
setup_testsuite
) || return 1
setup_nspawn_root
ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
}
do_test "$@"

View file

@ -0,0 +1,18 @@
#!/bin/bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -ex
set -o pipefail
systemd-run --wait -p FailureAction=poweroff true
! systemd-run --wait -p SuccessAction=poweroff false
if test -f /firstphase ; then
echo OK > /firstphase
systemd-run --wait -p SuccessAction=reboot true
else
echo OK > /testok
systemd-run --wait -p FailureAction=poweroff false
fi
sleep infinity

View file

@ -21,7 +21,7 @@ if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
ROOTLIBDIR=/usr/lib/systemd ROOTLIBDIR=/usr/lib/systemd
fi fi
BASICTOOLS="sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm" BASICTOOLS="sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false"
DEBUGTOOLS="df free ls stty cat ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find" DEBUGTOOLS="df free ls stty cat ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find"
STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))" STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"