diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 01d8f659d50..7247f3d2fc6 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -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 {
+
+
@@ -4368,6 +4372,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
@@ -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 {
+
+
@@ -6403,6 +6413,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
@@ -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 {
+
+
@@ -8154,6 +8170,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
+
+
@@ -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 {
+
+
@@ -10000,6 +10022,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+
+
@@ -11650,7 +11674,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
RootImagePolicy,
MountImagePolicy, and
ExtensionImagePolicy were added in version 254.
- NFTSet was added in version 255.
+ NFTSet and
+ SetLoginEnvironment were added in version 255.
Socket Unit Objects
@@ -11674,8 +11699,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
MountImagePolicy, and
ExtensionImagePolicy were added in version 254.
PollLimitIntervalUSec,
- PollLimitBurst, and
- NFTSet were added in version 255.
+ PollLimitBurst,
+ NFTSet, and
+ SetLoginEnvironment were added in version 255.
Mount Unit Objects
@@ -11698,7 +11724,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
RootImagePolicy,
MountImagePolicy, and
ExtensionImagePolicy were added in version 254.
- NFTSet was added in version 255.
+ NFTSet and
+ SetLoginEnvironment were added in version 255.
Swap Unit Objects
@@ -11721,7 +11748,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
RootImagePolicy,
MountImagePolicy, and
ExtensionImagePolicy were added in version 254.
- NFTSet was added in version 255.
+ NFTSet and
+ SetLoginEnvironment were added in version 255.
Slice Unit Objects
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 1988d624cf0..e648aeedaa0 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -696,6 +696,19 @@
commands prefixed with +.
+
+ SetLoginEnvironment=
+
+ Takes a boolean parameter. If true, $HOME, $LOGNAME,
+ and $SHELL environment variables will be set for system services even if
+ User= is not set, i.e. when the default user root is used.
+ If false, the mentioned variables are not set by systemd, no matter whether User=
+ 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.
+
+
+
+
PAMName=
@@ -3661,10 +3674,10 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
$HOME
$SHELL
- User name (twice), home directory, and the
- login shell. The variables are set for the units that have
- User= set, which includes user
- systemd instances. See
+ User name (twice), home directory, and the login shell. $USER is
+ set unconditionally, while $HOME, $LOGNAME, and $SHELL
+ are only set for the units that have User= set and SetLoginEnvironment=
+ unset or set to true. For user services, these variables are typically inherited from the user manager itself. See
passwd5.
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 571d8c83980..74322645dec 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -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);
diff --git a/src/core/execute.c b/src/core/execute.c
index 89c3868d55f..e25552daa67 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -989,44 +989,45 @@ restore_stdio:
return r;
}
-static int get_fixed_user(const ExecContext *c, const char **user,
- uid_t *uid, gid_t *gid,
- const char **home, const char **shell) {
+static int get_fixed_user(
+ const char *username,
+ const char **ret_user,
+ uid_t *ret_uid,
+ gid_t *ret_gid,
+ const char **ret_home,
+ const char **ret_shell) {
+
int r;
- const char *name;
- assert(c);
-
- if (!c->user)
- return 0;
+ assert(username);
+ assert(ret_user);
/* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
* (i.e. are "/" or "/bin/nologin"). */
- name = c->user;
- r = get_user_creds(&name, uid, gid, home, shell, USER_CREDS_CLEAN);
+ r = get_user_creds(&username, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN);
if (r < 0)
return r;
- *user = name;
+ *ret_user = username;
return 0;
}
-static int get_fixed_group(const ExecContext *c, const char **group, gid_t *gid) {
+static int get_fixed_group(
+ const char *groupname,
+ const char **ret_group,
+ gid_t *ret_gid) {
+
int r;
- const char *name;
- assert(c);
+ assert(groupname);
+ assert(ret_group);
- if (!c->group)
- return 0;
-
- name = c->group;
- r = get_group_creds(&name, gid, 0);
+ r = get_group_creds(&groupname, ret_gid, /* flags = */ 0);
if (r < 0)
return r;
- *group = name;
+ *ret_group = groupname;
return 0;
}
@@ -1922,7 +1923,33 @@ static int build_environment(
our_env[n_env++] = x;
}
- if (home) {
+ /* 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("USER=", username);
+ if (!x)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+
+ 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;
@@ -1931,19 +1958,7 @@ static int build_environment(
our_env[n_env++] = x;
}
- if (username) {
- x = strjoin("LOGNAME=", username);
- if (!x)
- return -ENOMEM;
- our_env[n_env++] = x;
-
- x = strjoin("USER=", username);
- if (!x)
- return -ENOMEM;
- our_env[n_env++] = x;
- }
-
- if (shell) {
+ if (shell && set_user_login_env) {
x = strjoin("SHELL=", shell);
if (!x)
return -ENOMEM;
@@ -4153,16 +4168,20 @@ static int exec_child(
username = runtime->dynamic_creds->user->name;
} else {
- r = get_fixed_user(context, &username, &uid, &gid, &home, &shell);
- if (r < 0) {
- *exit_status = EXIT_USER;
- return log_unit_error_errno(unit, r, "Failed to determine user credentials: %m");
+ if (context->user) {
+ r = get_fixed_user(context->user, &username, &uid, &gid, &home, &shell);
+ if (r < 0) {
+ *exit_status = EXIT_USER;
+ return log_unit_error_errno(unit, r, "Failed to determine user credentials: %m");
+ }
}
- r = get_fixed_group(context, &groupname, &gid);
- if (r < 0) {
- *exit_status = EXIT_GROUP;
- return log_unit_error_errno(unit, r, "Failed to determine group credentials: %m");
+ if (context->group) {
+ r = get_fixed_group(context->group, &groupname, &gid);
+ if (r < 0) {
+ *exit_status = EXIT_GROUP;
+ return log_unit_error_errno(unit, r, "Failed to determine group credentials: %m");
+ }
}
}
@@ -5257,10 +5276,11 @@ void exec_context_init(ExecContext *c) {
.tty_cols = UINT_MAX,
.private_mounts = -1,
.memory_ksm = -1,
+ .set_login_environment = -1,
};
- for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
- c->directories[t].mode = 0755;
+ FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
+ d->mode = 0755;
numa_policy_reset(&c->numa_policy);
diff --git a/src/core/execute.h b/src/core/execute.h
index 33fe77bf7c4..a68ea37d830 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -254,6 +254,8 @@ struct ExecContext {
char *group;
char **supplementary_groups;
+ int set_login_environment;
+
char *pam_name;
char *utmp_id;
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index 77a0dce529f..0ab468ba176 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -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)
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index eeeabc66b15..634a8f08c29 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -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",