Skip to content

Commit 6579a62

Browse files
authored
Merge pull request systemd#6790 from poettering/unit-unsetenv
add UnsetEnvironment= unit file setting, in order to fix systemd#6407
2 parents bff8f25 + 1c68232 commit 6579a62

File tree

13 files changed

+291
-69
lines changed

13 files changed

+291
-69
lines changed

man/systemd.exec.xml

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -423,17 +423,17 @@
423423
<varlistentry>
424424
<term><varname>PassEnvironment=</varname></term>
425425

426-
<listitem><para>Pass environment variables from the systemd system
427-
manager to executed processes. Takes a space-separated list of variable
428-
names. This option may be specified more than once, in which case all
429-
listed variables will be set. If the empty string is assigned to this
430-
option, the list of environment variables is reset, all prior
431-
assignments have no effect. Variables that are not set in the system
432-
manager will not be passed and will be silently ignored.</para>
433-
434-
<para>Variables passed from this setting are overridden by those passed
435-
from <varname>Environment=</varname> or
436-
<varname>EnvironmentFile=</varname>.</para>
426+
<listitem><para>Pass environment variables set for the system service manager to executed processes. Takes a
427+
space-separated list of variable names. This option may be specified more than once, in which case all listed
428+
variables will be passed. If the empty string is assigned to this option, the list of environment variables to
429+
pass is reset, all prior assignments have no effect. Variables specified that are not set for the system
430+
manager will not be passed and will be silently ignored. Note that this option is only relevant for the system
431+
service manager, as system services by default do not automatically inherit any environment variables set for
432+
the service manager itself. However, in case of the user service manager all environment variables are passed
433+
to the executed processes anyway, hence this option is without effect for the user service manager.</para>
434+
435+
<para>Variables set for invoked processes due to this setting are subject to being overridden by those
436+
configured with <varname>Environment=</varname> or <varname>EnvironmentFile=</varname>.</para>
437437

438438
<para>Example:
439439
<programlisting>PassEnvironment=VAR1 VAR2 VAR3</programlisting>
@@ -447,6 +447,30 @@
447447
for details about environment variables.</para></listitem>
448448
</varlistentry>
449449

450+
<varlistentry>
451+
<term><varname>UnsetEnvironment=</varname></term>
452+
453+
<listitem><para>Explicitly unset environment variable assignments that would normally be passed from the
454+
service manager to invoked processes of this unit. Takes a space-separated list of variable names or variable
455+
assignments. This option may be specified more than once, in which case all listed variables/assignments will
456+
be unset. If the empty string is assigned to this option, the list of environment variables/assignments to
457+
unset is reset. If a variable assignment is specified (that is: a variable name, followed by
458+
<literal>=</literal>, followed by its value), then any environment variable matching this precise assignment is
459+
removed. If a variable name is specified (that is a variable name without any following <literal>=</literal> or
460+
value), then any assignment matching the variable name, regardless of its value is removed. Note that the
461+
effect of <varname>UnsetEnvironment=</varname> is applied as final step when the environment list passed to
462+
executed processes is compiled. That means it may undo assignments from any configuration source, including
463+
assignments made through <varname>Environment=</varname> or <varname>EnvironmentFile=</varname>, inherited from
464+
the system manager's global set of environment variables, inherited via <varname>PassEnvironment=</varname>,
465+
set by the service manager itself (such as <varname>$NOTIFY_SOCKET</varname> and such), or set by a PAM module
466+
(in case <varname>PAMName=</varname> is used).</para>
467+
468+
<para>
469+
See
470+
<citerefentry project='man-pages'><refentrytitle>environ</refentrytitle><manvolnum>7</manvolnum></citerefentry>
471+
for details about environment variables.</para></listitem>
472+
</varlistentry>
473+
450474
<varlistentry>
451475
<term><varname>StandardInput=</varname></term>
452476
<listitem><para>Controls where file descriptor 0 (STDIN) of
@@ -1799,12 +1823,38 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
17991823
<refsect1>
18001824
<title>Environment variables in spawned processes</title>
18011825

1802-
<para>Processes started by the system are executed in a clean
1803-
environment in which select variables listed below are set. System
1804-
processes started by systemd do not inherit variables from PID 1,
1805-
but processes started by user systemd instances inherit all
1806-
environment variables from the user systemd instance.
1807-
</para>
1826+
<para>Processes started by the service manager are executed with an environment variable block assembled from
1827+
multiple sources. Processes started by the system service manager generally do not inherit environment variables
1828+
set for the service manager itself (but this may be altered via <varname>PassEnvironment=</varname>), but processes
1829+
started by the user service manager instances generally do inherit all environment variables set for the service
1830+
manager itself.</para>
1831+
1832+
<para>For each invoked process the list of environment variables set is compiled from the following sources:</para>
1833+
1834+
<itemizedlist>
1835+
<listitem><para>Variables globally configured for the service manager, using the
1836+
<varname>DefaultEnvironment=</varname> setting in
1837+
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, the kernel command line option <varname>systemd.setenv=</varname> (see
1838+
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>) or via
1839+
<command>systemctl set-environment</command> (see <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>).</para></listitem>
1840+
1841+
<listitem><para>Variables defined by the service manager itself (see the list below)</para></listitem>
1842+
1843+
<listitem><para>Variables set in the service manager's own environment variable block (subject to <varname>PassEnvironment=</varname> for the system service manager)</para></listitem>
1844+
1845+
<listitem><para>Variables set via <varname>Environment=</varname> in the unit file</para></listitem>
1846+
1847+
<listitem><para>Variables read from files specified via <varname>EnvironmentFiles=</varname> in the unit file</para></listitem>
1848+
1849+
<listitem><para>Variables set by any PAM modules in case <varname>PAMName=</varname> is in effect, cf. <citerefentry project='man-pages'><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry></para></listitem>
1850+
</itemizedlist>
1851+
1852+
<para>If the same environment variables are set by multiple of these sources, the later source — according to the
1853+
order of the list above — wins. Note that as final step all variables listed in
1854+
<varname>UnsetEnvironment=</varname> are removed again from the compiled environment variable list, immediately
1855+
before it is passed to the executed process.</para>
1856+
1857+
<para>The following select environment variables are set by the service manager itself for each invoked process:</para>
18081858

18091859
<variablelist class='environment-variables'>
18101860
<varlistentry>
@@ -2120,18 +2170,6 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
21202170
</listitem>
21212171
</varlistentry>
21222172
</variablelist>
2123-
2124-
<para>Additional variables may be configured by the following
2125-
means: for processes spawned in specific units, use the
2126-
<varname>Environment=</varname>, <varname>EnvironmentFile=</varname>
2127-
and <varname>PassEnvironment=</varname> options above; to specify
2128-
variables globally, use <varname>DefaultEnvironment=</varname>
2129-
(see
2130-
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
2131-
or the kernel option <varname>systemd.setenv=</varname> (see
2132-
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>).
2133-
Additional variables may also be set through PAM,
2134-
cf. <citerefentry project='man-pages'><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
21352173
</refsect1>
21362174

21372175
<refsect1>

src/core/dbus-execute.c

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
758758
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
759759
SD_BUS_PROPERTY("EnvironmentFiles", "a(sb)", property_get_environment_files, 0, SD_BUS_VTABLE_PROPERTY_CONST),
760760
SD_BUS_PROPERTY("PassEnvironment", "as", NULL, offsetof(ExecContext, pass_environment), SD_BUS_VTABLE_PROPERTY_CONST),
761+
SD_BUS_PROPERTY("UnsetEnvironment", "as", NULL, offsetof(ExecContext, unset_environment), SD_BUS_VTABLE_PROPERTY_CONST),
761762
SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST),
762763
SD_BUS_PROPERTY("LimitCPU", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
763764
SD_BUS_PROPERTY("LimitCPUSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1816,13 +1817,13 @@ int bus_exec_context_set_transient_property(
18161817
if (r < 0)
18171818
return r;
18181819

1819-
if (!strv_env_is_valid(l))
1820-
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block.");
1821-
18221820
r = unit_full_printf_strv(u, l, &q);
18231821
if (r < 0)
18241822
return r;
18251823

1824+
if (!strv_env_is_valid(q))
1825+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block.");
1826+
18261827
if (mode != UNIT_CHECK) {
18271828
if (strv_length(q) == 0) {
18281829
c->environment = strv_free(c->environment);
@@ -1839,7 +1840,7 @@ int bus_exec_context_set_transient_property(
18391840
c->environment = e;
18401841

18411842
/* We write just the new settings out to file, with unresolved specifiers */
1842-
joined = strv_join_quoted(q);
1843+
joined = strv_join_quoted(l);
18431844
if (!joined)
18441845
return -ENOMEM;
18451846

@@ -1849,6 +1850,47 @@ int bus_exec_context_set_transient_property(
18491850

18501851
return 1;
18511852

1853+
} else if (streq(name, "UnsetEnvironment")) {
1854+
1855+
_cleanup_strv_free_ char **l = NULL, **q = NULL;
1856+
1857+
r = sd_bus_message_read_strv(message, &l);
1858+
if (r < 0)
1859+
return r;
1860+
1861+
r = unit_full_printf_strv(u, l, &q);
1862+
if (r < 0)
1863+
return r;
1864+
1865+
if (!strv_env_name_or_assignment_is_valid(q))
1866+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UnsetEnvironment= list.");
1867+
1868+
if (mode != UNIT_CHECK) {
1869+
if (strv_length(q) == 0) {
1870+
c->unset_environment = strv_free(c->unset_environment);
1871+
unit_write_drop_in_private_format(u, mode, name, "UnsetEnvironment=");
1872+
} else {
1873+
_cleanup_free_ char *joined = NULL;
1874+
char **e;
1875+
1876+
e = strv_env_merge(2, c->unset_environment, q);
1877+
if (!e)
1878+
return -ENOMEM;
1879+
1880+
strv_free(c->unset_environment);
1881+
c->unset_environment = e;
1882+
1883+
/* We write just the new settings out to file, with unresolved specifiers */
1884+
joined = strv_join_quoted(l);
1885+
if (!joined)
1886+
return -ENOMEM;
1887+
1888+
unit_write_drop_in_private_format(u, mode, name, "UnsetEnvironment=%s", joined);
1889+
}
1890+
}
1891+
1892+
return 1;
1893+
18521894
} else if (streq(name, "TimerSlackNSec")) {
18531895

18541896
nsec_t n;
@@ -1958,14 +2000,18 @@ int bus_exec_context_set_transient_property(
19582000

19592001
} else if (streq(name, "PassEnvironment")) {
19602002

1961-
_cleanup_strv_free_ char **l = NULL;
2003+
_cleanup_strv_free_ char **l = NULL, **q = NULL;
19622004

19632005
r = sd_bus_message_read_strv(message, &l);
19642006
if (r < 0)
19652007
return r;
19662008

1967-
if (!strv_env_name_is_valid(l))
1968-
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment block.");
2009+
r = unit_full_printf_strv(u, l, &q);
2010+
if (r < 0)
2011+
return r;
2012+
2013+
if (!strv_env_name_is_valid(q))
2014+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment= block.");
19692015

19702016
if (mode != UNIT_CHECK) {
19712017
if (strv_isempty(l)) {
@@ -1974,11 +2020,12 @@ int bus_exec_context_set_transient_property(
19742020
} else {
19752021
_cleanup_free_ char *joined = NULL;
19762022

1977-
r = strv_extend_strv(&c->pass_environment, l, true);
2023+
r = strv_extend_strv(&c->pass_environment, q, true);
19782024
if (r < 0)
19792025
return r;
19802026

1981-
joined = strv_join_quoted(c->pass_environment);
2027+
/* We write just the new settings out to file, with unresolved specifiers. */
2028+
joined = strv_join_quoted(l);
19822029
if (!joined)
19832030
return -ENOMEM;
19842031

src/core/execute.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,8 +1673,10 @@ static int build_pass_environment(const ExecContext *c, char ***ret) {
16731673
x = strjoin(*i, "=", v);
16741674
if (!x)
16751675
return -ENOMEM;
1676+
16761677
if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2))
16771678
return -ENOMEM;
1679+
16781680
pass_env[n_env++] = x;
16791681
pass_env[n_env] = NULL;
16801682
x = NULL;
@@ -3031,6 +3033,19 @@ static int exec_child(
30313033
#endif
30323034
}
30333035

3036+
if (!strv_isempty(context->unset_environment)) {
3037+
char **ee = NULL;
3038+
3039+
ee = strv_env_delete(accum_env, 1, context->unset_environment);
3040+
if (!ee) {
3041+
*exit_status = EXIT_MEMORY;
3042+
return -ENOMEM;
3043+
}
3044+
3045+
strv_free(accum_env);
3046+
accum_env = ee;
3047+
}
3048+
30343049
final_argv = replace_env_argv(argv, accum_env);
30353050
if (!final_argv) {
30363051
*exit_status = EXIT_MEMORY;
@@ -3222,6 +3237,7 @@ void exec_context_done(ExecContext *c) {
32223237
c->environment = strv_free(c->environment);
32233238
c->environment_files = strv_free(c->environment_files);
32243239
c->pass_environment = strv_free(c->pass_environment);
3240+
c->unset_environment = strv_free(c->unset_environment);
32253241

32263242
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
32273243
c->rlimit[l] = mfree(c->rlimit[l]);
@@ -3582,6 +3598,9 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
35823598
STRV_FOREACH(e, c->pass_environment)
35833599
fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
35843600

3601+
STRV_FOREACH(e, c->unset_environment)
3602+
fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
3603+
35853604
fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
35863605

35873606
for (dt = 0; dt < _EXEC_DIRECTORY_MAX; dt++) {

src/core/execute.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ struct ExecContext {
133133
char **environment;
134134
char **environment_files;
135135
char **pass_environment;
136+
char **unset_environment;
136137

137138
struct rlimit *rlimit[_RLIMIT_MAX];
138139
char *working_directory, *root_directory, *root_image;

src/core/load-fragment-gperf.gperf.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ $1.UMask, config_parse_mode, 0,
3535
$1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment)
3636
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files)
3737
$1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment)
38+
$1.UnsetEnvironment, config_parse_unset_environ, 0, offsetof($1, exec_context.unset_environment)
3839
$1.DynamicUser, config_parse_bool, true, offsetof($1, exec_context.dynamic_user)
3940
$1.StandardInput, config_parse_exec_input, 0, offsetof($1, exec_context)
4041
$1.StandardOutput, config_parse_exec_output, 0, offsetof($1, exec_context)

0 commit comments

Comments
 (0)