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;
static void start_transient_context_parameters_done(StartTransientContextParameters *p) {
assert(p);
+ transient_exec_context_parameters_done(&p->exec);
transient_service_parameters_done(&p->service);
}
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 },
{}
};
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;
}
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(
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
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"