From: Michael Vogt Date: Wed, 29 Apr 2026 13:45:10 +0000 (+0200) Subject: core: add support for Environment in io.systemd.Unit.StartTransient X-Git-Tag: v261-rc1~158^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=27fa92aa3cc3e5b8b7511c52c32b49d136a2d092;p=thirdparty%2Fsystemd.git core: add support for Environment in io.systemd.Unit.StartTransient This commit adds support to set `Environment` in the `io.systemd.Unit.StartTransient` varlink call. The behavior is similar to D-Bus, i.e. a `null` or `[]` clears the environment. This is not needed for StartTransient() as there the env always starts empty but it seems a good property to have if this is reused. --- diff --git a/src/core/varlink-unit.c b/src/core/varlink-unit.c index cb67dfa70fd..10fbc1d7bcf 100644 --- a/src/core/varlink-unit.c +++ b/src/core/varlink-unit.c @@ -712,8 +712,15 @@ typedef struct TransientExecContextParameters { bool present; bool working_directory_set; TransientWorkingDirectory working_directory; + bool environment_set; + char **environment; } TransientExecContextParameters; +static void transient_exec_context_parameters_done(TransientExecContextParameters *p) { + assert(p); + strv_free(p->environment); +} + typedef struct TransientServiceParameters { bool present; ServiceType type; @@ -772,6 +779,7 @@ typedef struct StartTransientContextParameters { static void start_transient_context_parameters_done(StartTransientContextParameters *p) { assert(p); + transient_exec_context_parameters_done(&p->exec); transient_service_parameters_done(&p->service); } @@ -813,10 +821,17 @@ static int dispatch_transient_working_directory(const char *name, sd_json_varian return sd_json_dispatch(variant, dispatch, flags, &p->working_directory); } +static int dispatch_transient_environment(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + TransientExecContextParameters *p = ASSERT_PTR(userdata); + p->environment_set = true; + return sd_json_dispatch_strv(name, variant, flags, &p->environment); +} + static int dispatch_transient_exec_context(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { /* Key names compatible with D-Bus property names */ static const sd_json_dispatch_field exec_dispatch[] = { - { "WorkingDirectory", SD_JSON_VARIANT_OBJECT, dispatch_transient_working_directory, 0, 0 }, + { "WorkingDirectory", SD_JSON_VARIANT_OBJECT, dispatch_transient_working_directory, 0, 0 }, + { "Environment", _SD_JSON_VARIANT_TYPE_INVALID, dispatch_transient_environment, 0, 0 }, {} }; @@ -925,6 +940,16 @@ static int transient_exec_context_apply_properties(Unit *u, ExecContext *c, Tran c->working_directory_home ? "~" : strempty(c->working_directory)); } + if (p->environment_set) { + r = exec_context_apply_environment(u, c, p->environment, UNIT_RUNTIME|UNIT_PRIVATE); + if (r == -E2BIG) + return log_debug_errno(r, "Too many environment assignments."); + if (r == -EINVAL) + return log_debug_errno(r, "Invalid Environment list."); + if (r < 0) + return r; + } + return 0; } diff --git a/src/shared/varlink-io.systemd.Unit.c b/src/shared/varlink-io.systemd.Unit.c index dada1d58139..ae0f78db9c2 100644 --- a/src/shared/varlink-io.systemd.Unit.c +++ b/src/shared/varlink-io.systemd.Unit.c @@ -1184,7 +1184,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}) and as output from + * Description, Service, and the Exec subset {WorkingDirectory, Environment}) 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. */ static SD_VARLINK_DEFINE_STRUCT_TYPE( diff --git a/test/units/TEST-26-SYSTEMCTL.sh b/test/units/TEST-26-SYSTEMCTL.sh index ec3f88b3a92..6f615eb1c3a 100755 --- a/test/units/TEST-26-SYSTEMCTL.sh +++ b/test/units/TEST-26-SYSTEMCTL.sh @@ -621,13 +621,17 @@ result=$(varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \ echo "$result" | jq -e '.context.Service.ExecStart[0].arguments == ["/bin/true"]' timeout 30 bash -c 'until systemctl is-active varlink-transient-noargs.service; do sleep 0.5; done' -# Exec.WorkingDirectory +# Exec.WorkingDirectory and Exec.Environment defer_transient_cleanup varlink-transient-exec.service result=$(varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \ - '{"context":{"ID":"varlink-transient-exec.service","Exec":{"WorkingDirectory":{"path":"/tmp","missingOK":false}},"Service":{"Type":"oneshot","RemainAfterExit":true,"ExecStart":[{"path":"/bin/true"}]}}}') + '{"context":{"ID":"varlink-transient-exec.service","Exec":{"WorkingDirectory":{"path":"/tmp","missingOK":false},"Environment":["FOO=bar","BAZ=qux"]},"Service":{"Type":"oneshot","RemainAfterExit":true,"ExecStart":[{"path":"/bin/true"}]}}}') echo "$result" | jq -e '.context.Exec.WorkingDirectory.path == "/tmp"' +echo "$result" | jq -e '.context.Exec.Environment | index("FOO=bar") != null' +echo "$result" | jq -e '.context.Exec.Environment | index("BAZ=qux") != null' timeout 30 bash -c 'until systemctl is-active varlink-transient-exec.service; do sleep 0.5; done' systemctl show -P WorkingDirectory varlink-transient-exec.service | grep '^/tmp$' >/dev/null +systemctl show -P Environment varlink-transient-exec.service | grep 'FOO=bar' >/dev/null +systemctl show -P Environment varlink-transient-exec.service | grep 'BAZ=qux' >/dev/null # WorkingDirectory with missingOK=true (path does not exist but unit still starts) defer_transient_cleanup varlink-transient-wd-missing.service @@ -659,6 +663,10 @@ varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \ defer_transient_cleanup varlink-transient-bad-wd.service varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \ '{"context":{"ID":"varlink-transient-bad-wd.service","Exec":{"WorkingDirectory":{"path":"relative/path","missingOK":false}},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' |& grep "io.systemd.Unit.BadUnitSetting" +# Malformed environment entry (not KEY=VALUE) +defer_transient_cleanup varlink-transient-bad-env.service +varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \ + '{"context":{"ID":"varlink-transient-bad-env.service","Exec":{"Environment":["not_an_env_var"]},"Service":{"Type":"oneshot","ExecStart":[{"path":"/bin/true"}]}}}' |& grep "io.systemd.Unit.BadUnitSetting" # Exec on a unit type without an exec context (.target) is rejected varlinkctl call "$MANAGER_SOCKET" io.systemd.Unit.StartTransient \ '{"context":{"ID":"varlink-transient-exec.slice","Exec":{"WorkingDirectory":{"path":"/tmp","missingOK":false}}}}' |& grep "io.systemd.Unit.UnitTypeNotSupported"