Skip to content

Commit

Permalink
core: make the StartLimitXYZ= settings generic and apply to any kind …
Browse files Browse the repository at this point in the history
…of unit, not just services

This moves the StartLimitBurst=, StartLimitInterval=, StartLimitAction=, RebootArgument= from the [Service] section
into the [Unit] section of unit files, and thus support it in all unit types, not just in services.

This way we can enforce the start limit much earlier, in particular before testing the unit conditions, so that
repeated start-up failure due to failed conditions is also considered for the start limit logic.

For compatibility the four options may also be configured in the [Service] section still, but we only document them in
their new section [Unit].

This also renamed the socket unit failure code "service-failed-permanent" into "service-start-limit-hit" to express
more clearly what it is about, after all it's only triggered through the start limit being hit.

Finally, the code in busname_trigger_notify() and socket_trigger_notify() is altered to become more alike.

Fixes: #2467
  • Loading branch information
poettering committed Feb 10, 2016
1 parent bae687d commit 6bf0f40
Show file tree
Hide file tree
Showing 13 changed files with 143 additions and 172 deletions.
82 changes: 4 additions & 78 deletions man/systemd.service.xml
Original file line number Diff line number Diff line change
Expand Up @@ -873,86 +873,12 @@
effect.</para></listitem>
</varlistentry>

<varlistentry>
<term><varname>StartLimitInterval=</varname></term>
<term><varname>StartLimitBurst=</varname></term>

<listitem><para>Configure service start rate limiting. By
default, services which are started more than 5 times within
10 seconds are not permitted to start any more times until the
10 second interval ends. With these two options, this rate
limiting may be modified. Use
<varname>StartLimitInterval=</varname> to configure the
checking interval (defaults to
<varname>DefaultStartLimitInterval=</varname> in manager
configuration file, set to 0 to disable any kind of rate
limiting). Use <varname>StartLimitBurst=</varname> to
configure how many starts per interval are allowed (defaults
to <varname>DefaultStartLimitBurst=</varname> in manager
configuration file). These configuration options are
particularly useful in conjunction with
<varname>Restart=</varname>; however, they apply to all kinds
of starts (including manual), not just those triggered by the
<varname>Restart=</varname> logic. Note that units which are
configured for <varname>Restart=</varname> and which reach the
start limit are not attempted to be restarted anymore;
however, they may still be restarted manually at a later
point, from which point on, the restart logic is again
activated. Note that <command>systemctl reset-failed</command>
will cause the restart rate counter for a service to be
flushed, which is useful if the administrator wants to
manually start a service and the start limit interferes with
that.</para></listitem>
</varlistentry>

<varlistentry>
<term><varname>StartLimitAction=</varname></term>

<listitem><para>Configure the action to take if the rate limit
configured with <varname>StartLimitInterval=</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>
</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
<varname>StartLimitAction=</varname> and executes the same
actions. Defaults to <option>none</option>. </para></listitem>
</varlistentry>

<varlistentry>
<term><varname>RebootArgument=</varname></term>
<listitem><para>Configure the optional argument for the
<citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry>
system call if <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>
<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>
Expand Down
49 changes: 49 additions & 0 deletions man/systemd.unit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,55 @@
system call.</para></listitem>
</varlistentry>

<varlistentry>
<term><varname>StartLimitInterval=</varname></term>
<term><varname>StartLimitBurst=</varname></term>

<listitem><para>Configure unit start rate limiting. By default, units which are started more than 5 times
within 10 seconds are not permitted to start any more times until the 10 second interval ends. With these two
options, this rate limiting may be modified. Use <varname>StartLimitInterval=</varname> to configure the
checking interval (defaults to <varname>DefaultStartLimitInterval=</varname> in manager configuration file, set
to 0 to disable any kind of rate limiting). Use <varname>StartLimitBurst=</varname> to configure how many
starts per interval are allowed (defaults to <varname>DefaultStartLimitBurst=</varname> in manager
configuration file). These configuration options are particularly useful in conjunction with the service
setting <varname>Restart=</varname> (see
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>); however,
they apply to all kinds of starts (including manual), not just those triggered by the
<varname>Restart=</varname> logic. Note that units which are configured for <varname>Restart=</varname> and
which reach the start limit are not attempted to be restarted anymore; however, they may still be restarted
manually at a later point, from which point on, the restart logic is again activated. Note that
<command>systemctl reset-failed</command> will cause the restart rate counter for a service to be flushed,
which is useful if the administrator wants to manually start a unit and the start limit interferes with
that.</para></listitem>
</varlistentry>

<varlistentry>
<term><varname>StartLimitAction=</varname></term>

<listitem><para>Configure the action to take if the rate limit configured with
<varname>StartLimitInterval=</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>
</varlistentry>

<varlistentry>
<term><varname>RebootArgument=</varname></term>
<listitem><para>Configure the optional argument for the
<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
works just like the optional argument to <command>systemctl reboot</command> command.</para></listitem>
</varlistentry>

<varlistentry>
<term><varname>ConditionArchitecture=</varname></term>
<term><varname>ConditionVirtualization=</varname></term>
Expand Down
24 changes: 13 additions & 11 deletions src/core/busname.c
Original file line number Diff line number Diff line change
Expand Up @@ -945,27 +945,29 @@ static void busname_reset_failed(Unit *u) {

static void busname_trigger_notify(Unit *u, Unit *other) {
BusName *n = BUSNAME(u);
Service *s;

assert(n);
assert(other);

if (!IN_SET(n->state, BUSNAME_RUNNING, BUSNAME_LISTENING))
return;

if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE)
if (other->start_limit_hit) {
busname_enter_dead(n, BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT);
return;
}

s = SERVICE(other);
if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE)
return;

if (s->state == SERVICE_FAILED && s->result == SERVICE_FAILURE_START_LIMIT)
busname_enter_dead(n, BUSNAME_FAILURE_SERVICE_FAILED_PERMANENT);
else if (IN_SET(s->state,
SERVICE_DEAD, SERVICE_FAILED,
SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
SERVICE_AUTO_RESTART))
if (IN_SET(SERVICE(other)->state,
SERVICE_DEAD, SERVICE_FAILED,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
SERVICE_AUTO_RESTART))
busname_enter_listening(n);

if (SERVICE(other)->state == SERVICE_RUNNING)
busname_set_state(n, BUSNAME_RUNNING);
}

static int busname_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
Expand Down Expand Up @@ -1006,7 +1008,7 @@ static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = {
[BUSNAME_FAILURE_EXIT_CODE] = "exit-code",
[BUSNAME_FAILURE_SIGNAL] = "signal",
[BUSNAME_FAILURE_CORE_DUMP] = "core-dump",
[BUSNAME_FAILURE_SERVICE_FAILED_PERMANENT] = "service-failed-permanent",
[BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit",
};

DEFINE_STRING_TABLE_LOOKUP(busname_result, BusNameResult);
Expand Down
2 changes: 1 addition & 1 deletion src/core/busname.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ typedef enum BusNameResult {
BUSNAME_FAILURE_EXIT_CODE,
BUSNAME_FAILURE_SIGNAL,
BUSNAME_FAILURE_CORE_DUMP,
BUSNAME_FAILURE_SERVICE_FAILED_PERMANENT,
BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT,
_BUSNAME_RESULT_MAX,
_BUSNAME_RESULT_INVALID = -1
} BusNameResult;
Expand Down
9 changes: 5 additions & 4 deletions src/core/dbus-service.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ 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("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),
SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Service, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Service, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Service, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Service, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
/* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */
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("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_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("FailureAction", "s", property_get_failure_action, offsetof(Service, failure_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("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
Expand Down
4 changes: 4 additions & 0 deletions src/core/dbus-unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,10 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NetClass", "u", NULL, offsetof(Unit, cgroup_netclass_id), 0),
SD_BUS_PROPERTY("StartLimitInterval", "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("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),

SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED),
Expand Down
12 changes: 8 additions & 4 deletions src/core/load-fragment-gperf.gperf.m4
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LE
Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout)
Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action)
Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg)
Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
Unit.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action)
Unit.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg)
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.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions)
Expand Down Expand Up @@ -216,10 +220,10 @@ Service.TimeoutStartSec, config_parse_service_timeout, 0,
Service.TimeoutStopSec, config_parse_service_timeout, 0, offsetof(Service, timeout_stop_usec)
Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
Service.StartLimitInterval, config_parse_sec, 0, offsetof(Service, start_limit.interval)
Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst)
Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Service, start_limit_action)
Service.RebootArgument, config_parse_string, 0, offsetof(Service, reboot_arg)
Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action)
Service.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg)
Service.FailureAction, config_parse_failure_action, 0, offsetof(Service, failure_action)
Service.Type, config_parse_service_type, 0, offsetof(Service, type)
Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart)
Expand Down
Loading

0 comments on commit 6bf0f40

Please sign in to comment.