diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 5775e6c641..cbb2983d02 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -3366,7 +3366,9 @@ SystemCallErrorNumber=EPERM assignments made through Environment= or EnvironmentFile=, inherited from the system manager's global set of environment variables, inherited via PassEnvironment=, set by the service manager itself (such as $NOTIFY_SOCKET and such), or set by a PAM module - (in case PAMName= is used). + (in case PAMName= is used). Note that variable expansion in the command lines of + ExecStart= and friends is not affected by this option, as it happens before the + unsetting is applied. See "Environment Variables in Spawned Processes" below for a description of how those settings combine to form the inherited environment. See If the same environment variable is set by multiple of these sources, the later source — according to the order of the list above — wins. Note that as the final step all variables listed in UnsetEnvironment= are removed from the compiled environment variable list, immediately - before it is passed to the executed process. + before it is passed to the executed process. This unsetting happens after variable expansion in + the command lines of ExecStart= and friends is performed, hence command line + expansion is not affected by UnsetEnvironment=. The general philosophy is to expose a small curated list of environment variables to processes. Services started by the system manager (PID 1) will be started, without additional service-specific diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index 74a556fa3b..2fabfe3410 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -6665,18 +6665,6 @@ int exec_invoke( #endif } - if (!strv_isempty(context->unset_environment)) { - char **ee = NULL; - - ee = strv_env_delete(accum_env, 1, context->unset_environment); - if (!ee) { - *exit_status = EXIT_MEMORY; - return log_oom(); - } - - strv_free_and_replace(accum_env, ee); - } - _cleanup_strv_free_ char **replaced_argv = NULL, **argv_via_shell = NULL; char **final_argv = FLAGS_SET(command->flags, EXEC_COMMAND_VIA_SHELL) ? strv_skip(command->argv, 1) : command->argv; @@ -6701,6 +6689,21 @@ int exec_invoke( } } + /* Apply UnsetEnvironment= after the command line has been expanded, so that the variables it + * removes are still available for expansion. Only the environment block passed to the executed + * process is affected, not the expansion. */ + if (!strv_isempty(context->unset_environment)) { + char **ee = NULL; + + ee = strv_env_delete(accum_env, 1, context->unset_environment); + if (!ee) { + *exit_status = EXIT_MEMORY; + return log_oom(); + } + + strv_free_and_replace(accum_env, ee); + } + if (FLAGS_SET(command->flags, EXEC_COMMAND_VIA_SHELL)) { r = strv_extendf(&argv_via_shell, "%s%s", command->argv[0][0] == '-' ? "-" : "", path); if (r < 0) { diff --git a/test/test-execute/exec-unsetenvironment.service b/test/test-execute/exec-unsetenvironment.service index e64e261072..16a5559bfb 100644 --- a/test/test-execute/exec-unsetenvironment.service +++ b/test/test-execute/exec-unsetenvironment.service @@ -3,7 +3,9 @@ Description=Test for UnsetEnvironment [Service] -ExecStart=bash -x -c 'test "$$FOO" = "bar" && test "$${QUUX-X}" = "X" && test "$$VAR3" = "value3" && test "$${VAR4-X}" = "X" && test "$$VAR5" = "value5" && test "$${X%b-X}" = "X"' +# Variable expansion in the command line is done by the service manager before UnsetEnvironment= is +# applied, so $VAR6 is expanded, but the variable is not part of the environment of the executed bash. +ExecStart=bash -x -c 'test "$$FOO" = "bar" && test "$${QUUX-X}" = "X" && test "$$VAR3" = "value3" && test "$${VAR4-X}" = "X" && test "$$VAR5" = "value5" && test "$${X%b-X}" = "X" && test "$$1" = "value6" && test "$${VAR6-X}" = "X"' x $VAR6 Type=oneshot -Environment=FOO=bar QUUX=waldo VAR3=value3 VAR4=value4 VAR5=value5 X%b=%U -UnsetEnvironment=QUUX=waldo VAR3=somethingelse VAR4 X%b=%U +Environment=FOO=bar QUUX=waldo VAR3=value3 VAR4=value4 VAR5=value5 X%b=%U VAR6=value6 +UnsetEnvironment=QUUX=waldo VAR3=somethingelse VAR4 X%b=%U VAR6