]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: implement RuntimeMaxDeltaSec directive
authorAlbert Brox <albert@exypno.tech>
Tue, 13 Jul 2021 17:38:08 +0000 (13:38 -0400)
committerLennart Poettering <lennart@poettering.net>
Tue, 28 Sep 2021 14:46:20 +0000 (16:46 +0200)
14 files changed:
docs/TRANSIENT-SETTINGS.md
man/org.freedesktop.systemd1.xml
man/systemd.scope.xml
man/systemd.service.xml
src/core/dbus-scope.c
src/core/dbus-service.c
src/core/load-fragment-gperf.gperf.in
src/core/scope.c
src/core/scope.h
src/core/service.c
src/core/service.h
src/shared/bus-unit-util.c
test/fuzz/fuzz-unit-file/directives.scope
test/fuzz/fuzz-unit-file/directives.service

index d67f7f95e26604d27632d0ef526abcfcde7a94ee..218a43c6b7cd5fe6046e61bc6d7ddb188da1ca2c 100644 (file)
@@ -320,6 +320,7 @@ Most service unit settings are available for transient units.
 ✓ RestartSec=
 ✓ RootDirectoryStartOnly=
 ✓ RuntimeMaxSec=
+✓ RuntimeRandomizedExtraSec=
   Sockets=
 ✓ SuccessExitStatus=
 ✓ TimeoutAbortSec=
@@ -395,6 +396,7 @@ such).
 
 ```
 ✓ RuntimeMaxSec=
+✓ RuntimeRandomizedExtraSec=
 ✓ TimeoutStopSec=
 ```
 
index e4972104d95963ea4284e5711678e7c4123395da..ee968e194d7539a1da04e27539f35f1b90aff592 100644 (file)
@@ -2317,6 +2317,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       readonly s TimeoutStopFailureMode = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t RuntimeMaxUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RuntimeRandomizedExtraUSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t WatchdogUSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
@@ -2890,6 +2892,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property RuntimeMaxUSec is not documented!-->
 
+    <!--property RuntimeRandomizedExtraUSec is not documented!-->
+
     <!--property WatchdogUSec is not documented!-->
 
     <!--property RootDirectoryStartOnly is not documented!-->
@@ -3426,6 +3430,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="RuntimeMaxUSec"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeRandomizedExtraUSec"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="WatchdogUSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="WatchdogTimestamp"/>
@@ -9745,6 +9751,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
       readonly s Result = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t RuntimeMaxUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RuntimeRandomizedExtraUSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly s Slice = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
@@ -9915,6 +9923,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <!--property RuntimeMaxUSec is not documented!-->
 
+    <!--property RuntimeRandomizedExtraUSec is not documented!-->
+
     <!--property Slice is not documented!-->
 
     <!--property MemoryCurrent is not documented!-->
@@ -10093,6 +10103,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <variablelist class="dbus-property" generated="True" extra-ref="RuntimeMaxUSec"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeRandomizedExtraUSec"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="Slice"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="ControlGroup"/>
index 6d991b915fdc99b6c57014310f1e384d02eb5818..acede4a10b22f6f4d7b4e185eb4e1e1939a243b0 100644 (file)
         active for longer than the specified time it is terminated and put into a failure state. Pass
         <literal>infinity</literal> (the default) to configure no runtime limit.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>RuntimeRandomizedExtraSec=</varname></term>
+
+        <listitem><para>This option modifies <varname>RuntimeMaxSec=</varname> by increasing the maximum runtime by an
+        evenly distributed duration between 0 and the specified value (in seconds). If <varname>RuntimeMaxSec=</varname> is
+        unspecified, then this feature will be disabled.
+        </para></listitem>
+      </varlistentry>
     </variablelist>
 
     <xi:include href="systemd.service.xml" xpointer="shared-unit-options" />
index 4891f27ebad81407f9929e2a6ddc3952a1a3c568..5042066d0d1e8705cc8d2312ed084fea1a05eba9 100644 (file)
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RuntimeRandomizedExtraSec=</varname></term>
+
+        <listitem><para>This option modifies <varname>RuntimeMaxSec=</varname> by increasing the maximum runtime by an
+        evenly distributed duration between 0 and the specified value (in seconds). If <varname>RuntimeMaxSec=</varname> is
+        unspecified, then this feature will be disabled.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>WatchdogSec=</varname></term>
         <listitem><para>Configures the watchdog timeout for a service.
index 90ec6a686cdf46c3ff5095cc6c8fad453970c40b..109ad6f2ef59a8e29fecef4390ed3597c78b8a65 100644 (file)
@@ -47,6 +47,7 @@ const sd_bus_vtable bus_scope_vtable[] = {
         SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Scope, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Scope, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Scope, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RuntimeRandomizedExtraUSec", "t", bus_property_get_usec, offsetof(Scope, runtime_rand_extra_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_SIGNAL("RequestStop", NULL, 0),
         SD_BUS_METHOD("Abandon", NULL, NULL, bus_scope_method_abandon, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_VTABLE_END
@@ -74,6 +75,9 @@ static int bus_scope_set_transient_property(
         if (streq(name, "RuntimeMaxUSec"))
                 return bus_set_transient_usec(u, name, &s->runtime_max_usec, message, flags, error);
 
+        if (streq(name, "RuntimeRandomizedExtraUSec"))
+                return bus_set_transient_usec(u, name, &s->runtime_rand_extra_usec, message, flags, error);
+
         if (streq(name, "PIDs")) {
                 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
                 unsigned n = 0;
index 02628cd39eab1f97f6b5367b83c92662a937a130..f42d97afac85e88cb017d67a7b6fe71d7ae709b2 100644 (file)
@@ -202,6 +202,7 @@ const sd_bus_vtable bus_service_vtable[] = {
         SD_BUS_PROPERTY("TimeoutStartFailureMode", "s", property_get_timeout_failure_mode, offsetof(Service, timeout_start_failure_mode), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("TimeoutStopFailureMode", "s", property_get_timeout_failure_mode, offsetof(Service, timeout_stop_failure_mode), 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("RuntimeRandomizedExtraUSec", "t", bus_property_get_usec, offsetof(Service, runtime_rand_extra_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("WatchdogUSec", "t", property_get_watchdog_usec, 0, 0),
         BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
         SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* 😷 deprecated */
@@ -447,6 +448,9 @@ static int bus_service_set_transient_property(
         if (streq(name, "RuntimeMaxUSec"))
                 return bus_set_transient_usec(u, name, &s->runtime_max_usec, message, flags, error);
 
+        if (streq(name, "RuntimeRandomizedExtraUSec"))
+                return bus_set_transient_usec(u, name, &s->runtime_rand_extra_usec, message, flags, error);
+
         if (streq(name, "WatchdogUSec"))
                 return bus_set_transient_usec(u, name, &s->watchdog_usec, message, flags, error);
 
index 7bef95bec758d80b830cbc6840da36037f0c6799..799a179430d0b33cded8e9b6e5f0e14da90d8603 100644 (file)
@@ -382,6 +382,7 @@ Service.TimeoutAbortSec,                 config_parse_service_timeout_abort,
 Service.TimeoutStartFailureMode,         config_parse_service_timeout_failure_mode,   0,                                  offsetof(Service, timeout_start_failure_mode)
 Service.TimeoutStopFailureMode,          config_parse_service_timeout_failure_mode,   0,                                  offsetof(Service, timeout_stop_failure_mode)
 Service.RuntimeMaxSec,                   config_parse_sec,                            0,                                  offsetof(Service, runtime_max_usec)
+Service.RuntimeRandomizedExtraSec,       config_parse_sec,                            0,                                  offsetof(Service, runtime_rand_extra_usec)
 Service.WatchdogSec,                     config_parse_sec,                            0,                                  offsetof(Service, watchdog_usec)
 {# The following five only exist for compatibility, they moved into Unit, see above #}
 Service.StartLimitInterval,              config_parse_sec,                            0,                                  offsetof(Unit, start_ratelimit.interval)
@@ -534,6 +535,7 @@ Path.DirectoryMode,                      config_parse_mode,
 {{ CGROUP_CONTEXT_CONFIG_ITEMS('Scope') }}
 {{ KILL_CONTEXT_CONFIG_ITEMS('Scope') }}
 Scope.RuntimeMaxSec,                     config_parse_sec,                            0,                                  offsetof(Scope, runtime_max_usec)
+Scope.RuntimeRandomizedExtraSec,         config_parse_sec,                            0,                                  offsetof(Scope, runtime_rand_extra_usec)
 Scope.TimeoutStopSec,                    config_parse_sec,                            0,                                  offsetof(Scope, timeout_stop_usec)
 {# The [Install] section is ignored here #}
 Install.Alias,                           NULL,                                        0,                                  0
index 560a7d9f3eed2b7ee8714ef06163d7e080ff569b..8fdba646aa4bc8ccf035122c462e17c27ee50b9e 100644 (file)
@@ -9,6 +9,7 @@
 #include "load-dropin.h"
 #include "log.h"
 #include "process-util.h"
+#include "random-util.h"
 #include "scope.h"
 #include "serialize.h"
 #include "special.h"
@@ -51,6 +52,21 @@ static void scope_done(Unit *u) {
         s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
 }
 
+static int scope_running_timeout(Scope *s) {
+        usec_t delta = 0;
+
+        assert(s);
+
+        if (s->runtime_rand_extra_usec != 0) {
+                delta = random_u64_range(s->runtime_rand_extra_usec);
+                log_unit_debug(UNIT(s), "Adding delta of %s sec to timeout", FORMAT_TIMESPAN(delta, USEC_PER_SEC));
+        }
+
+        return usec_add(usec_add(UNIT(s)->active_enter_timestamp.monotonic,
+                                 s->runtime_max_usec),
+                        delta);
+}
+
 static int scope_arm_timer(Scope *s, usec_t usec) {
         int r;
 
@@ -209,7 +225,7 @@ static usec_t scope_coldplug_timeout(Scope *s) {
         switch (s->deserialized_state) {
 
         case SCOPE_RUNNING:
-                return usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec);
+                return scope_running_timeout(s);
 
         case SCOPE_STOP_SIGKILL:
         case SCOPE_STOP_SIGTERM:
@@ -262,10 +278,12 @@ static void scope_dump(Unit *u, FILE *f, const char *prefix) {
         fprintf(f,
                 "%sScope State: %s\n"
                 "%sResult: %s\n"
-                "%sRuntimeMaxSec: %s\n",
+                "%sRuntimeMaxSec: %s\n"
+                "%sRuntimeRandomizedExtraSec: %s\n",
                 prefix, scope_state_to_string(s->state),
                 prefix, scope_result_to_string(s->result),
-                prefix, FORMAT_TIMESPAN(s->runtime_max_usec, USEC_PER_SEC));
+                prefix, FORMAT_TIMESPAN(s->runtime_max_usec, USEC_PER_SEC),
+                prefix, FORMAT_TIMESPAN(s->runtime_rand_extra_usec, USEC_PER_SEC));
 
         cgroup_context_dump(UNIT(s), f, prefix);
         kill_context_dump(&s->kill_context, f, prefix);
@@ -379,7 +397,7 @@ static int scope_start(Unit *u) {
         scope_set_state(s, SCOPE_RUNNING);
 
         /* Set the maximum runtime timeout. */
-        scope_arm_timer(s, usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec));
+        scope_arm_timer(s, scope_running_timeout(s));
 
         /* On unified we use proper notifications hence we can unwatch the PIDs
          * we just attached to the scope. This can also be done on legacy as
index 0b0e1f8730f387cf23df1fd79b42afa55c358063..03a9ba432438daac75d6cc7166b81eaf79a55f32 100644 (file)
@@ -25,6 +25,7 @@ struct Scope {
         ScopeResult result;
 
         usec_t runtime_max_usec;
+        usec_t runtime_rand_extra_usec;
         usec_t timeout_stop_usec;
 
         char *controller;
index 9d8eef1f7428792e448ebdf2214a96c0d070e83a..68485a241f4b8ee1624cd75b5d62e2b4cbaf4465 100644 (file)
@@ -29,6 +29,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "random-util.h"
 #include "serialize.h"
 #include "service.h"
 #include "signal-util.h"
@@ -514,6 +515,21 @@ static void service_remove_fd_store(Service *s, const char *name) {
         }
 }
 
+static int service_running_timeout(Service *s) {
+        usec_t delta = 0;
+
+        assert(s);
+
+        if (s->runtime_rand_extra_usec != 0) {
+                delta = random_u64_range(s->runtime_rand_extra_usec);
+                log_unit_debug(UNIT(s), "Adding delta of %s sec to timeout", FORMAT_TIMESPAN(delta, USEC_PER_SEC));
+        }
+
+        return usec_add(usec_add(UNIT(s)->active_enter_timestamp.monotonic,
+                                 s->runtime_max_usec),
+                        delta);
+}
+
 static int service_arm_timer(Service *s, usec_t usec) {
         int r;
 
@@ -587,6 +603,9 @@ static int service_verify(Service *s) {
         if (s->runtime_max_usec != USEC_INFINITY && s->type == SERVICE_ONESHOT)
                 log_unit_warning(UNIT(s), "RuntimeMaxSec= has no effect in combination with Type=oneshot. Ignoring.");
 
+        if (s->runtime_max_usec == USEC_INFINITY && s->runtime_rand_extra_usec != 0)
+                log_unit_warning(UNIT(s), "Service has RuntimeRandomizedExtraSec= setting, but no RuntimeMaxSec=. Ignoring.");
+
         return 0;
 }
 
@@ -855,8 +874,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
 
         fprintf(f,
                 "%sRuntimeMaxSec: %s\n"
+                "%sRuntimeRandomizedExtraSec: %s\n"
                 "%sWatchdogSec: %s\n",
                 prefix, FORMAT_TIMESPAN(s->runtime_max_usec, USEC_PER_SEC),
+                prefix, FORMAT_TIMESPAN(s->runtime_rand_extra_usec, USEC_PER_SEC),
                 prefix, FORMAT_TIMESPAN(s->watchdog_usec, USEC_PER_SEC));
 
         kill_context_dump(&s->kill_context, f, prefix);
@@ -1118,7 +1139,7 @@ static usec_t service_coldplug_timeout(Service *s) {
                 return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec);
 
         case SERVICE_RUNNING:
-                return usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec);
+                return service_running_timeout(s);
 
         case SERVICE_STOP:
         case SERVICE_STOP_SIGTERM:
@@ -1989,7 +2010,7 @@ static void service_enter_running(Service *s, ServiceResult f) {
                         service_enter_stop_by_notify(s);
                 else {
                         service_set_state(s, SERVICE_RUNNING);
-                        service_arm_timer(s, usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec));
+                        service_arm_timer(s, service_running_timeout(s));
                 }
 
         } else if (s->remain_after_exit)
index 6d931c3d5e4aeb86bfb39de3099104d662364398..eaad95df6d2f9c000af23aa864f73249bf49d96b 100644 (file)
@@ -111,6 +111,7 @@ struct Service {
         usec_t timeout_abort_usec;
         bool timeout_abort_set;
         usec_t runtime_max_usec;
+        usec_t runtime_rand_extra_usec;
         ServiceTimeoutFailureMode timeout_start_failure_mode;
         ServiceTimeoutFailureMode timeout_stop_failure_mode;
 
index e33e0c21e025422f8a62ff8a3df3d4298f423d2a..01b78952cdbd1f4b64f32fbbddf3aaa94400c1c6 100644 (file)
@@ -1999,6 +1999,9 @@ static int bus_append_scope_property(sd_bus_message *m, const char *field, const
         if (streq(field, "RuntimeMaxSec"))
                 return bus_append_parse_sec_rename(m, field, eq);
 
+        if (streq(field, "RuntimeRandomizedExtraSec"))
+                return bus_append_parse_sec_rename(m, field, eq);
+
         if (streq(field, "TimeoutStopSec"))
                 return bus_append_parse_sec_rename(m, field, eq);
 
@@ -2031,6 +2034,7 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
                               "TimeoutStopSec",
                               "TimeoutAbortSec",
                               "RuntimeMaxSec",
+                              "RuntimeRandomizedExtraSec",
                               "WatchdogSec"))
                 return bus_append_parse_sec_rename(m, field, eq);
 
index aa91ebbf58869b34ac801981b74503e8181d15a2..4552d0b403dec221278ee01042dbc20e59447d8d 100644 (file)
@@ -50,6 +50,7 @@ NetClass=
 RestartKillSignal=
 RestrictNetworkInterfaces=
 RuntimeMaxSec=
+RuntimeRandomizedExtraSec=
 SendSIGHUP=
 SendSIGKILL=
 Slice=
index aeea1d8731c8017d6dedbbc395631b13415c5f8c..d9781f0492f22879c67399b534dfbb3755a05643 100644 (file)
@@ -290,6 +290,7 @@ RuntimeDirectory=
 RuntimeDirectoryMode=
 RuntimeDirectoryPreserve=
 RuntimeMaxSec=
+RuntimeRandomizedExtraSec=
 SELinuxContext=
 SecureBits=
 SendSIGHUP=