]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core/execute: always set $USER and introduce SetLoginEnvironment= 29493/head
authorMike Yuan <me@yhndnzj.com>
Sat, 7 Oct 2023 12:08:21 +0000 (20:08 +0800)
committerMike Yuan <me@yhndnzj.com>
Mon, 9 Oct 2023 16:00:26 +0000 (00:00 +0800)
Before this commit, $USER, $HOME, $LOGNAME and $SHELL are only
set when User= is set for the unit. For system service, this
results in different behaviors depending on whether User=root is set.

$USER always makes sense on its own, so let's set it unconditionally.
Ideally $HOME should be set too, but it causes trouble when e.g. getty
passes '-p' to login(1), which then doesn't override $HOME. $LOGNAME and
$SHELL are more like "login environments", and are generally not
suitable for system services. Therefore, a new option SetLoginEnvironment=
is also added to control the latter three variables.

Fixes #23438

Replaces #8227

man/org.freedesktop.systemd1.xml
man/systemd.exec.xml
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.in
src/shared/bus-unit-util.c

index 01d8f659d501054a4790ed181d9aadbca3df23b3..7247f3d2fc6700ab61787092dbce297a2bd1a2c0 100644 (file)
@@ -3117,6 +3117,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @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 = [...];
@@ -3710,6 +3712,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property DynamicUser is not documented!-->
 
+    <!--property SetLoginEnvironment is not documented!-->
+
     <!--property RemoveIPC is not documented!-->
 
     <!--property SetCredential is not documented!-->
@@ -4368,6 +4372,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <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"/>
@@ -5160,6 +5166,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @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 = [...];
@@ -5763,6 +5771,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property DynamicUser is not documented!-->
 
+    <!--property SetLoginEnvironment is not documented!-->
+
     <!--property RemoveIPC is not documented!-->
 
     <!--property SetCredential is not documented!-->
@@ -6403,6 +6413,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <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"/>
@@ -7069,6 +7081,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       @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 = [...];
@@ -7600,6 +7614,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <!--property DynamicUser is not documented!-->
 
+    <!--property SetLoginEnvironment is not documented!-->
+
     <!--property RemoveIPC is not documented!-->
 
     <!--property SetCredential is not documented!-->
@@ -8154,6 +8170,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <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"/>
@@ -8943,6 +8961,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       @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 = [...];
@@ -9460,6 +9480,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <!--property DynamicUser is not documented!-->
 
+    <!--property SetLoginEnvironment is not documented!-->
+
     <!--property RemoveIPC is not documented!-->
 
     <!--property SetCredential is not documented!-->
@@ -10000,6 +10022,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <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"/>
@@ -11650,7 +11674,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <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>
@@ -11674,8 +11699,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <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>
@@ -11698,7 +11724,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <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>
@@ -11721,7 +11748,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <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>
index 1988d624cf03d3f34e498264bbc695337cfe14d9..e648aeedaa02801746d5e8cce053f45a65aa7b2a 100644 (file)
         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>
 
@@ -3661,10 +3674,10 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
           <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>
 
index 571d8c839804eb1dd73eee745f69cf0f9f6f2472..74322645dec13632509c37e11b08202b73460f45 100644 (file)
@@ -1273,6 +1273,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
         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),
@@ -1730,6 +1731,9 @@ int bus_exec_context_set_transient_property(
         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);
 
index 04f6bcd703eb8d05ac5264576eeaa324bda7171d..e25552daa67d36012e15d6a5975c8e15ff5fd51d 100644 (file)
@@ -1923,28 +1923,42 @@ static int build_environment(
                 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;
@@ -5262,6 +5276,7 @@ void exec_context_init(ExecContext *c) {
                 .tty_cols = UINT_MAX,
                 .private_mounts = -1,
                 .memory_ksm = -1,
+                .set_login_environment = -1,
         };
 
         FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
index 33fe77bf7c448033f8d69d70361e04425074655b..a68ea37d830450dc7d9db36a06e83ecb747ea13d 100644 (file)
@@ -254,6 +254,8 @@ struct ExecContext {
         char *group;
         char **supplementary_groups;
 
+        int set_login_environment;
+
         char *pam_name;
 
         char *utmp_id;
index 77a0dce529f7311c1c7aad3e41ffbc3455b27616..0ab468ba176a4f5dd736d6abe88785f1b7b96526 100644 (file)
@@ -19,6 +19,7 @@
 {{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)
index eeeabc66b15721c6676bc79e9cb0cd05931eeaa6..634a8f08c29b008800556f654e4cae41c38eab67 100644 (file)
@@ -1078,7 +1078,8 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                               "ProtectHostname",
                               "MemoryKSM",
                               "RestrictSUIDSGID",
-                              "RootEphemeral"))
+                              "RootEphemeral",
+                              "SetLoginEnvironment"))
                 return bus_append_parse_boolean(m, field, eq);
 
         if (STR_IN_SET(field, "ReadWriteDirectories",