@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b DynamicUser = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly b SetLoginEnvironment = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b RemoveIPC = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(say) SetCredential = [...];
<!--property DynamicUser is not documented!-->
+ <!--property SetLoginEnvironment is not documented!-->
+
<!--property RemoveIPC is not documented!-->
<!--property SetCredential is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="SetLoginEnvironment"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
<variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b DynamicUser = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly b SetLoginEnvironment = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b RemoveIPC = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(say) SetCredential = [...];
<!--property DynamicUser is not documented!-->
+ <!--property SetLoginEnvironment is not documented!-->
+
<!--property RemoveIPC is not documented!-->
<!--property SetCredential is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="SetLoginEnvironment"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
<variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b DynamicUser = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly b SetLoginEnvironment = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b RemoveIPC = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(say) SetCredential = [...];
<!--property DynamicUser is not documented!-->
+ <!--property SetLoginEnvironment is not documented!-->
+
<!--property RemoveIPC is not documented!-->
<!--property SetCredential is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="SetLoginEnvironment"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
<variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b DynamicUser = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly b SetLoginEnvironment = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b RemoveIPC = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(say) SetCredential = [...];
<!--property DynamicUser is not documented!-->
+ <!--property SetLoginEnvironment is not documented!-->
+
<!--property RemoveIPC is not documented!-->
<!--property SetCredential is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="SetLoginEnvironment"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
<variablelist class="dbus-property" generated="True" extra-ref="SetCredential"/>
<varname>RootImagePolicy</varname>,
<varname>MountImagePolicy</varname>, and
<varname>ExtensionImagePolicy</varname> were added in version 254.</para>
- <para><varname>NFTSet</varname> was added in version 255.</para>
+ <para><varname>NFTSet</varname> and
+ <varname>SetLoginEnvironment</varname> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Socket Unit Objects</title>
<varname>MountImagePolicy</varname>, and
<varname>ExtensionImagePolicy</varname> were added in version 254.</para>
<para><varname>PollLimitIntervalUSec</varname>,
- <varname>PollLimitBurst</varname>, and
- <varname>NFTSet</varname> were added in version 255.</para>
+ <varname>PollLimitBurst</varname>,
+ <varname>NFTSet</varname>, and
+ <varname>SetLoginEnvironment</varname> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Mount Unit Objects</title>
<varname>RootImagePolicy</varname>,
<varname>MountImagePolicy</varname>, and
<varname>ExtensionImagePolicy</varname> were added in version 254.</para>
- <para><varname>NFTSet</varname> was added in version 255.</para>
+ <para><varname>NFTSet</varname> and
+ <varname>SetLoginEnvironment</varname> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Swap Unit Objects</title>
<varname>RootImagePolicy</varname>,
<varname>MountImagePolicy</varname>, and
<varname>ExtensionImagePolicy</varname> were added in version 254.</para>
- <para><varname>NFTSet</varname> was added in version 255.</para>
+ <para><varname>NFTSet</varname> and
+ <varname>SetLoginEnvironment</varname> were added in version 255.</para>
</refsect2>
<refsect2>
<title>Slice Unit Objects</title>
commands prefixed with <literal>+</literal>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>SetLoginEnvironment=</varname></term>
+
+ <listitem><para>Takes a boolean parameter. If true, <varname>$HOME</varname>, <varname>$LOGNAME</varname>,
+ and <varname>$SHELL</varname> environment variables will be set for system services even if
+ <varname>User=</varname> is not set, i.e. when the default user <literal>root</literal> is used.
+ If false, the mentioned variables are not set by systemd, no matter whether <varname>User=</varname>
+ is set or not. This option normally has no effect on user services, since these variables are typically
+ inherited from user manager's own environment anyway.</para>
+
+ <xi:include href="version-info.xml" xpointer="v255"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>PAMName=</varname></term>
<term><varname>$HOME</varname></term>
<term><varname>$SHELL</varname></term>
- <listitem><para>User name (twice), home directory, and the
- login shell. The variables are set for the units that have
- <varname>User=</varname> set, which includes user
- <command>systemd</command> instances. See
+ <listitem><para>User name (twice), home directory, and the login shell. <varname>$USER</varname> is
+ set unconditionally, while <varname>$HOME</varname>, <varname>$LOGNAME</varname>, and <varname>$SHELL</varname>
+ are only set for the units that have <varname>User=</varname> set and <varname>SetLoginEnvironment=</varname>
+ unset or set to true. For user services, these variables are typically inherited from the user manager itself. See
<citerefentry project='die-net'><refentrytitle>passwd</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SetLoginEnvironment", "b", bus_property_get_tristate, offsetof(ExecContext, set_login_environment), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SetCredential", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SetCredentialEncrypted", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
if (streq(name, "Group"))
return bus_set_transient_user_relaxed(u, name, &c->group, message, flags, error);
+ if (streq(name, "SetLoginEnvironment"))
+ return bus_set_transient_tristate(u, name, &c->set_login_environment, message, flags, error);
+
if (streq(name, "TTYPath"))
return bus_set_transient_path(u, name, &c->tty_path, message, flags, error);
our_env[n_env++] = x;
}
- if (home) {
- x = strjoin("HOME=", home);
- if (!x)
- return -ENOMEM;
-
- path_simplify(x + 5);
- our_env[n_env++] = x;
+ /* We query "root" if this is a system unit and User= is not specified. $USER is always set. $HOME
+ * could cause problem for e.g. getty, since login doesn't override $HOME, and $LOGNAME and $SHELL don't
+ * really make much sense since we're not logged in. Hence we conditionalize the three based on
+ * SetLoginEnvironment= switch. */
+ if (!c->user && !c->dynamic_user && p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
+ r = get_fixed_user("root", &username, NULL, NULL, &home, &shell);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to determine user credentials for root: %m");
}
+ bool set_user_login_env = c->set_login_environment >= 0 ? c->set_login_environment : (c->user || c->dynamic_user);
+
if (username) {
- x = strjoin("LOGNAME=", username);
+ x = strjoin("USER=", username);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
- x = strjoin("USER=", username);
+ if (set_user_login_env) {
+ x = strjoin("LOGNAME=", username);
+ if (!x)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+ }
+ }
+
+ if (home && set_user_login_env) {
+ x = strjoin("HOME=", home);
if (!x)
return -ENOMEM;
+
+ path_simplify(x + 5);
our_env[n_env++] = x;
}
- if (shell) {
+ if (shell && set_user_login_env) {
x = strjoin("SHELL=", shell);
if (!x)
return -ENOMEM;
.tty_cols = UINT_MAX,
.private_mounts = -1,
.memory_ksm = -1,
+ .set_login_environment = -1,
};
FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
char *group;
char **supplementary_groups;
+ int set_login_environment;
+
char *pam_name;
char *utmp_id;
{{type}}.User, config_parse_user_group_compat, 0, offsetof({{type}}, exec_context.user)
{{type}}.Group, config_parse_user_group_compat, 0, offsetof({{type}}, exec_context.group)
{{type}}.SupplementaryGroups, config_parse_user_group_strv_compat, 0, offsetof({{type}}, exec_context.supplementary_groups)
+{{type}}.SetLoginEnvironment, config_parse_tristate, 0, offsetof({{type}}, exec_context.set_login_environment)
{{type}}.Nice, config_parse_exec_nice, 0, offsetof({{type}}, exec_context)
{{type}}.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof({{type}}, exec_context)
{{type}}.CoredumpFilter, config_parse_exec_coredump_filter, 0, offsetof({{type}}, exec_context)
"ProtectHostname",
"MemoryKSM",
"RestrictSUIDSGID",
- "RootEphemeral"))
+ "RootEphemeral",
+ "SetLoginEnvironment"))
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "ReadWriteDirectories",