]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
scope: Support RuntimeMaxSec= directive in scope units
authorPhilip Withnall <withnall@endlessm.com>
Wed, 12 Jun 2019 07:45:26 +0000 (08:45 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 28 Oct 2019 08:44:31 +0000 (09:44 +0100)
Just as `RuntimeMaxSec=` is supported for service units, add support for
it to scope units. This will gracefully kill a scope after the timeout
expires from the moment the scope enters the running state.

This could be used for time-limited login sessions, for example.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
Fixes: #12035
docs/TRANSIENT-SETTINGS.md
man/systemd.scope.xml
src/core/dbus-scope.c
src/core/load-fragment-gperf.gperf.m4
src/core/scope.c
src/core/scope.h
src/shared/bus-unit-util.c
test/TEST-03-JOBS/test-jobs.sh
test/fuzz/fuzz-unit-file/directives.scope [new file with mode: 0644]

index 7ba5837e813aa460894d688b972de7e3de99c538..615342943dc65e59bf609639324f76fcf097d8b5 100644 (file)
@@ -368,6 +368,7 @@ Scope units are fully supported as transient units (in fact they only exist as
 such).
 
 ```
+✓ RuntimeMaxSec=
 ✓ TimeoutStopSec=
 ```
 
index 503a480dd08e093db7182ef98f3ec0c52eb2e8e8..daf3554db2b663b457e93acd576c2188d2eb3417 100644 (file)
     </refsect2>
   </refsect1>
 
+  <refsect1>
+    <title>Options</title>
+
+    <para>Scope files may include a <literal>[Scope]</literal>
+    section, which carries information about the scope and the
+    units it contains. A number of options that may be used in
+    this section are shared with other unit types. These options are
+    documented in
+    <citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+    The options specific to the <literal>[Scope]</literal> section
+    of scope units are the following:</para>
+
+    <variablelist class='unit-directives'>
+      <varlistentry>
+        <term><varname>RuntimeMaxSec=</varname></term>
+
+        <listitem><para>Configures a maximum time for the scope to run. If this is used and the scope has been
+        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>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para>
index 9b8fed69cb7088de9b38ab240ea222df08f7602b..84d91dcfa3538c14692c0f3de3ccfe4e4534f12e 100644 (file)
@@ -47,6 +47,7 @@ const sd_bus_vtable bus_scope_vtable[] = {
         SD_BUS_PROPERTY("Controller", "s", NULL, offsetof(Scope, controller), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         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_SIGNAL("RequestStop", NULL, 0),
         SD_BUS_METHOD("Abandon", NULL, NULL, bus_scope_method_abandon, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_VTABLE_END
@@ -71,6 +72,9 @@ static int bus_scope_set_transient_property(
         if (streq(name, "TimeoutStopUSec"))
                 return bus_set_transient_usec(u, name, &s->timeout_stop_usec, message, flags, error);
 
+        if (streq(name, "RuntimeMaxUSec"))
+                return bus_set_transient_usec(u, name, &s->runtime_max_usec, message, flags, error);
+
         if (streq(name, "PIDs")) {
                 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
                 unsigned n = 0;
index 10e4801cfe6b8402a95825d37ea3ef853f7cb991..0f25a3b39eb89ad945545cdcc98523a7ab82bdef 100644 (file)
@@ -468,6 +468,7 @@ CGROUP_CONTEXT_CONFIG_ITEMS(Slice)m4_dnl
 m4_dnl
 CGROUP_CONTEXT_CONFIG_ITEMS(Scope)m4_dnl
 KILL_CONTEXT_CONFIG_ITEMS(Scope)m4_dnl
+Scope.RuntimeMaxSec,             config_parse_sec,                   0,                             offsetof(Scope, runtime_max_usec)
 Scope.TimeoutStopSec,            config_parse_sec,                   0,                             offsetof(Scope, timeout_stop_usec)
 m4_dnl The [Install] section is ignored here.
 Install.Alias,                   NULL,                               0,                             0
index 5303142d09d0bebd23db2d4c7b45bfb614ecdd99..874511c98cb7f9e9b67142915d3112d134512bcf 100644 (file)
@@ -34,6 +34,7 @@ static void scope_init(Unit *u) {
         assert(u);
         assert(u->load_state == UNIT_STUB);
 
+        s->runtime_max_usec = USEC_INFINITY;
         s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
         u->ignore_on_isolate = true;
 }
@@ -202,6 +203,9 @@ 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);
+
         case SCOPE_STOP_SIGKILL:
         case SCOPE_STOP_SIGTERM:
                 return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_stop_usec);
@@ -236,15 +240,18 @@ static int scope_coldplug(Unit *u) {
 
 static void scope_dump(Unit *u, FILE *f, const char *prefix) {
         Scope *s = SCOPE(u);
+        char buf_runtime[FORMAT_TIMESPAN_MAX];
 
         assert(s);
         assert(f);
 
         fprintf(f,
                 "%sScope State: %s\n"
-                "%sResult: %s\n",
+                "%sResult: %s\n"
+                "%sRuntimeMaxSec: %s\n",
                 prefix, scope_state_to_string(s->state),
-                prefix, scope_result_to_string(s->result));
+                prefix, scope_result_to_string(s->result),
+                prefix, format_timespan(buf_runtime, sizeof(buf_runtime), s->runtime_max_usec, USEC_PER_SEC));
 
         cgroup_context_dump(&s->cgroup_context, f, prefix);
         kill_context_dump(&s->kill_context, f, prefix);
@@ -357,6 +364,9 @@ 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));
+
         /* Start watching the PIDs currently in the scope */
         (void) unit_enqueue_rewatch_pids(u);
         return 1;
@@ -491,6 +501,11 @@ static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *user
 
         switch (s->state) {
 
+        case SCOPE_RUNNING:
+                log_unit_warning(UNIT(s), "Scope reached runtime time limit. Stopping.");
+                scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_FAILURE_TIMEOUT);
+                break;
+
         case SCOPE_STOP_SIGTERM:
                 if (s->kill_context.send_sigkill) {
                         log_unit_warning(UNIT(s), "Stopping timed out. Killing.");
index c38afb5e5d54fd815ff7d61cd9c855a201935306..ae2bb80e55679e4f9bca22378f920d90c520c73d 100644 (file)
@@ -24,6 +24,7 @@ struct Scope {
         ScopeState state, deserialized_state;
         ScopeResult result;
 
+        usec_t runtime_max_usec;
         usec_t timeout_stop_usec;
 
         char *controller;
index dc7c2f41aa82769b45409afc7b4e9d0f66b6a9f0..ace17da0c7314a7447490742633935b9c77f5de9 100644 (file)
@@ -1402,6 +1402,10 @@ static int bus_append_path_property(sd_bus_message *m, const char *field, const
 }
 
 static int bus_append_scope_property(sd_bus_message *m, const char *field, const char *eq) {
+        if (streq(field, "RuntimeMaxSec"))
+
+                return bus_append_parse_sec_rename(m, field, eq);
+
         if (streq(field, "TimeoutStopSec"))
 
                 return bus_append_parse_sec_rename(m, field, eq);
index 42190cf47800fafca06fa3095c4772493153e5e6..fca6cccb4fb1820944f9f640090fe606b474e00d 100755 (executable)
@@ -84,4 +84,14 @@ END_SEC=$(date -u '+%s')
 ELAPSED=$(($END_SEC-$START_SEC))
 [[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1
 
+# Test time-limited scopes
+START_SEC=$(date -u '+%s')
+set +e
+systemd-run --scope --property=RuntimeMaxSec=3s sleep 10
+RESULT=$?
+END_SEC=$(date -u '+%s')
+ELAPSED=$(($END_SEC-$START_SEC))
+[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1
+[[ "$RESULT" -ne 0 ]] || exit 1
+
 touch /testok
diff --git a/test/fuzz/fuzz-unit-file/directives.scope b/test/fuzz/fuzz-unit-file/directives.scope
new file mode 100644 (file)
index 0000000..d0e194c
--- /dev/null
@@ -0,0 +1,2 @@
+scope
+RuntimeMaxSec=