]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: support CollectMode in Varlink StartTransient
authorTim Culverhouse <tim@timculverhouse.com>
Sat, 18 Jul 2026 00:42:37 +0000 (00:42 +0000)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 25 Jul 2026 06:35:28 +0000 (15:35 +0900)
The io.systemd.Unit context exposes CollectMode, but StartTransient
rejects it as unsupported. This prevents Varlink clients from selecting
whether failed transient units should be garbage-collected.

Accept CollectMode when creating transient units and persist the setting
in the runtime unit configuration. Extend the integration test to verify
the value in both the Varlink response and the resulting unit.

src/core/varlink-unit.c
src/shared/varlink-io.systemd.Unit.c
test/units/TEST-74-AUX-UTILS.varlinkctl-unit.sh

index cd6df5a5b844eb11fb03a142b63b3076a422a8cc..e43395385ea0d7c3f5b998a964ef85aa274f27df 100644 (file)
@@ -663,6 +663,7 @@ static void transient_exec_command_item_done(TransientExecCommandItem *i) {
 
 static JSON_DISPATCH_ENUM_DEFINE(dispatch_service_type, ServiceType, service_type_from_string);
 static JSON_DISPATCH_ENUM_DEFINE(dispatch_job_mode, JobMode, job_mode_from_string);
+static JSON_DISPATCH_ENUM_DEFINE(dispatch_collect_mode, CollectMode, collect_mode_from_string);
 
 typedef struct TransientWorkingDirectory {
         const char *path;
@@ -811,6 +812,7 @@ static int dispatch_transient_exec_command(const char *name, sd_json_variant *va
 typedef struct StartTransientContextParameters {
         const char *id;
         const char *description;
+        CollectMode collect_mode;
         TransientExecContextParameters exec;
         TransientServiceParameters service;
         const char *bad_exec_field;    /* Set by inner Exec dispatcher to the unknown sub-property name */
@@ -827,7 +829,9 @@ static void transient_exec_context_parameters_init(TransientExecContextParameter
 
 static void start_transient_context_parameters_init(StartTransientContextParameters *p) {
         assert(p);
-        *p = (StartTransientContextParameters) {};
+        *p = (StartTransientContextParameters) {
+                .collect_mode = _COLLECT_MODE_INVALID,
+        };
         transient_exec_context_parameters_init(&p->exec);
         transient_service_parameters_init(&p->service);
 }
@@ -973,10 +977,11 @@ static int dispatch_transient_service(const char *name, sd_json_variant *variant
 
 static int dispatch_transient_context(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
         static const sd_json_dispatch_field context_dispatch[] = {
-                { "ID",          SD_JSON_VARIANT_STRING, json_dispatch_const_unit_name,   offsetof(StartTransientContextParameters, id),          SD_JSON_MANDATORY },
-                { "Description", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string,   offsetof(StartTransientContextParameters, description), 0                 },
-                { "Exec",        SD_JSON_VARIANT_OBJECT, dispatch_transient_exec_context, 0,                                                      0                 },
-                { "Service",     SD_JSON_VARIANT_OBJECT, dispatch_transient_service,      0,                                                      0                 },
+                { "ID",          SD_JSON_VARIANT_STRING, json_dispatch_const_unit_name,   offsetof(StartTransientContextParameters, id),           SD_JSON_MANDATORY },
+                { "Description", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string,   offsetof(StartTransientContextParameters, description),  0                 },
+                { "CollectMode", SD_JSON_VARIANT_STRING, dispatch_collect_mode,           offsetof(StartTransientContextParameters, collect_mode), 0                 },
+                { "Exec",        SD_JSON_VARIANT_OBJECT, dispatch_transient_exec_context, 0,                                                       0                 },
+                { "Service",     SD_JSON_VARIANT_OBJECT, dispatch_transient_service,      0,                                                       0                 },
                 {}
         };
 
@@ -1017,6 +1022,11 @@ static int transient_unit_apply_properties(Unit *u, StartTransientContextParamet
                 unit_write_settingf(u, UNIT_RUNTIME|UNIT_ESCAPE_SPECIFIERS, "Description", "Description=%s", p->description);
         }
 
+        if (p->collect_mode >= 0) {
+                u->collect_mode = p->collect_mode;
+                unit_write_settingf(u, UNIT_RUNTIME, "CollectMode", "CollectMode=%s", collect_mode_to_string(p->collect_mode));
+        }
+
         return 0;
 }
 
index 202b529e879f701a8f34fdeb27f2e88b60ddad91..3c3b63b93e14d24a2624662bd0fcbf115c452a15 100644 (file)
@@ -1570,7 +1570,7 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
                 SD_VARLINK_DEFINE_FIELD(parameter, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
 
 /* UnitContext is used both as input to StartTransient (subset settable at creation time: ID,
- * Description, Service, and the Exec subset {WorkingDirectory, Environment, SetCredential,
+ * Description, CollectMode, Service, and the Exec subset {WorkingDirectory, Environment, SetCredential,
  * SetCredentialEncrypted}) and as output from List/StartTransient (full unit configuration). Fields
  * that are not settable at creation time are rejected with PropertyNotSupported when supplied as
  * input. */
index f20a7fd4257c5879f457b3e7edc47b9ffc12ce75..b684da923851fad576581082aff1ca79a9f41a93 100755 (executable)
@@ -160,12 +160,14 @@ printf '%s' "$result" | jq --seq --slurp -e 'any(.[]; .job.Result == "done")' >/
 # Transient service with Description and RemainAfterExit
 defer_transient_cleanup varlink-transient-desc.service
 result=$(varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \
-    '{"context":{"ID":"varlink-transient-desc.service","Description":"Test description property","Service":{"Type":"oneshot","RemainAfterExit":true,"ExecStart":[{"path":"/bin/true"}]}}}')
+    '{"context":{"ID":"varlink-transient-desc.service","Description":"Test description property","CollectMode":"inactive_or_failed","Service":{"Type":"oneshot","RemainAfterExit":true,"ExecStart":[{"path":"/bin/true"}]}}}')
 echo "$result" | jq -e '.context.Description == "Test description property"'
+echo "$result" | jq -e '.context.CollectMode == "inactive_or_failed"'
 echo "$result" | jq -e '.context.Service.Type == "oneshot"'
 echo "$result" | jq -e '.context.Service.RemainAfterExit == true'
 echo "$result" | jq -e '.context.Service.ExecStart[0].path == "/bin/true"'
 echo "$result" | jq -e '.runtime'
+systemctl show -P CollectMode varlink-transient-desc.service | grep '^inactive-or-failed$' >/dev/null
 
 # Transient service with explicit arguments
 defer_transient_cleanup varlink-transient-args.service