1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-19 22:50:17 +03:00

logind: introduce "user-light" session class

This new session class is to "user" what "background" is to
"background-light": it doesn't cause the per-user service manager to
start.

This new session class is now the default if no session class was
provided at session registration time and the following conditions hold:

1. The session is not graphical
2. The user is not a regular user (but not root)

Or in other words root and system users won't get a service manager
started automatically if they go through a PAM session as part of things
like cron or ftp. They will however still get one if they log in
graphically.

This changes behaviour a bit, but hopefully in OK was.

This also makes "background-light" for system users incl. root.

This addresses one of the ideas discussed in #34988.
This commit is contained in:
Lennart Poettering 2025-01-10 09:32:18 +01:00
parent a6ad410ffa
commit cf8f6cd057
10 changed files with 113 additions and 25 deletions

17
NEWS
View File

@ -16,6 +16,23 @@ CHANGES WITH 258 in spe:
enabled by default. This brings cmdline expansion of transient
scopes on par with services.
* systemd-logind PAM sessions that previously were automatically
determined to be of class "background", and which are owned by root
or system accounts, will now automatically be set to class
"background-light" instead. PAM sessions that previously were
automatically determined to be of class "user", and which are owned
by non-root system users, will now automatically be set to class
"user-light" instead. This effectively means that cron jobs or FTP
sessions (i.e. all PAM sessions that have no TTY assigned and neither
are graphical) for system users no longer pull in a service manager
by default. This behaviour can be changed by explicitly setting the
session class (for example via the class= parameter to
pam_systemd.so, or by setting the XDG_SESSION_CLASS environment
variable as input for the service's PAM stack). This change does not
affect graphical sessions, nor does it affect regular users. This is
an incompatible change of sorts, since per-user services will
typically not be available for such PAM sessions of system users.
Announcements of Future Feature Removals:
* The D-Bus method org.freedesktop.systemd1.StartAuxiliaryScope() is

6
TODO
View File

@ -134,12 +134,6 @@ Features:
really be recognizable via a message id and come with an explanatory catalog
message
* logind: introduce "user-light" session class, that is to "user" what
"background-light" is to "background". Then, in logind, if no user class is
specified, and we are not logging in graphically default to this session
class for non-regular users. Effect: if you log into a system user for some
reason, yu won't get the service manager by default.
* introduce new ANSI sequence for communicating log level and structured error
metadata to terminals.

View File

@ -115,6 +115,14 @@
<entry><constant>user-early</constant></entry>
<entry>Similar to <literal>user</literal> but sessions of this class are not ordered after <citerefentry><refentrytitle>systemd-user-sessions.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, i.e. may be started before regular sessions are allowed to be established. This session class is the default for sessions of the root user that would otherwise qualify for the <constant>user</constant> class, see above. (Added in v256.)</entry>
</row>
<row>
<entry><constant>user-light</constant></entry>
<entry>Similar to <constant>user</constant>, but sessions of this class will not pull in the <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the user, and thus possibly have no service manager of the user running. (Added in v258.)</entry>
</row>
<row>
<entry><constant>user-early-light</constant></entry>
<entry>Similar to <constant>user-early</constant>, but sessions of this class will not pull in the <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the user, and thus possibly have no service manager of the user running. (Added in v258.)</entry>
</row>
<row>
<entry><constant>user-incomplete</constant></entry>
<entry>Similar to <literal>user</literal> but for sessions which are not fully set up yet, i.e. have no home directory mounted or similar. This is used by <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> to allow users to log in via <citerefentry project='man-pages'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry> before their home directory is mounted, delaying the mount until the user provided the unlock password. Sessions of this class are upgraded to the regular <constant>user</constant> class once the home directory is activated.</entry>
@ -133,7 +141,7 @@
</row>
<row>
<entry><constant>background-light</constant></entry>
<entry>Similar to <constant>background</constant>, but sessions of this class will not pull in the <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the user, and thus possibly have no services of the user running. (Added in v256.)</entry>
<entry>Similar to <constant>background</constant>, but sessions of this class will not pull in the <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the user, and thus possibly have no service manager of the user running. (Added in v256.)</entry>
</row>
<row>
<entry><constant>manager</constant></entry>

View File

@ -216,10 +216,10 @@
<para><function>sd_session_get_class()</function> may be used to determine the class of the session
identified by the specified session identifier. The returned string is one of <literal>user</literal>,
<literal>user-early</literal>, <literal>user-incomplete</literal>, <literal>greeter</literal>,
<literal>lock-screen</literal>, <literal>background</literal>, <literal>background-light</literal>,
<literal>manager</literal> or <literal>manager-early</literal> and needs to be freed with the libc
<citerefentry
<literal>user-early</literal>, <literal>user-light</literal>, <literal>user-early-light</literal>,
<literal>user-incomplete</literal>, <literal>greeter</literal>, <literal>lock-screen</literal>,
<literal>background</literal>, <literal>background-light</literal>, <literal>manager</literal> or
<literal>manager-early</literal> and needs to be freed with the libc <citerefentry
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after
use.</para>

View File

@ -893,6 +893,7 @@ int manager_create_session(
const char *remote_host,
Session **ret_session) {
bool mangle_class = false;
int r;
assert(m);
@ -920,6 +921,10 @@ int manager_create_session(
class = SESSION_BACKGROUND;
else
class = SESSION_USER;
/* If we determined the class automatically, then let's later potentially change it to early
* or light flavours, once we learn the disposition of the user */
mangle_class = true;
}
/* Check if we are already in a logind session, and if so refuse. */
@ -962,6 +967,25 @@ int manager_create_session(
if (r < 0)
goto fail;
/* If we picked the session class on our own, and the user is not a regular one, and the session is
* not a graphical one then do not pull in session manager by default. For root make a special
* exception: for TTY logins leave the service manager on, but relax the /run/nologin
* restrictions. */
if (mangle_class &&
IN_SET(user_record_disposition(user->user_record), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC)) {
if (class == SESSION_USER) {
if (user_record_is_root(user->user_record))
class = SESSION_USER_EARLY;
else if (SESSION_TYPE_IS_GRAPHICAL(type))
class = SESSION_USER;
else
class = SESSION_USER_LIGHT;
} else if (class == SESSION_BACKGROUND)
class = SESSION_BACKGROUND_LIGHT;
}
r = manager_add_session(m, id, &session);
if (r < 0)
goto fail;

View File

@ -1704,6 +1704,8 @@ static const char* const session_class_table[_SESSION_CLASS_MAX] = {
[SESSION_USER] = "user",
[SESSION_USER_EARLY] = "user-early",
[SESSION_USER_INCOMPLETE] = "user-incomplete",
[SESSION_USER_LIGHT] = "user-light",
[SESSION_USER_EARLY_LIGHT] = "user-early-light",
[SESSION_GREETER] = "greeter",
[SESSION_LOCK_SCREEN] = "lock-screen",
[SESSION_BACKGROUND] = "background",

View File

@ -23,6 +23,8 @@ typedef enum SessionClass {
SESSION_USER, /* A regular user session */
SESSION_USER_EARLY, /* A user session, that is not ordered after systemd-user-sessions.service (i.e. for root) */
SESSION_USER_INCOMPLETE, /* A user session that is only half-way set up and doesn't pull in the service manager, and can be upgraded to a full user session later */
SESSION_USER_LIGHT, /* Just like SESSION_USER, but doesn't pull in service manager */
SESSION_USER_EARLY_LIGHT, /* Just like SESSION_USER_EARLY, but doesn't pull in service manager */
SESSION_GREETER, /* A login greeter pseudo-session */
SESSION_LOCK_SCREEN, /* A lock screen */
SESSION_BACKGROUND, /* Things like cron jobs, which are non-interactive */
@ -36,10 +38,12 @@ typedef enum SessionClass {
/* Whether we shall allow sessions of this class to run before 'systemd-user-sessions.service'. It's
* generally set for root sessions, but no one else. */
#define SESSION_CLASS_IS_EARLY(class) IN_SET((class), SESSION_USER_EARLY, SESSION_MANAGER_EARLY)
#define SESSION_CLASS_IS_EARLY(class) IN_SET((class), SESSION_USER_EARLY, SESSION_USER_EARLY_LIGHT, SESSION_MANAGER_EARLY)
/* Which session classes want their own scope units? (all of them, except the manager, which comes in its own service unit already */
#define SESSION_CLASS_WANTS_SCOPE(class) IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_INCOMPLETE, SESSION_GREETER, SESSION_LOCK_SCREEN, SESSION_BACKGROUND, SESSION_BACKGROUND_LIGHT)
#define SESSION_CLASS_WANTS_SCOPE(class) IN_SET((class), \
SESSION_USER, SESSION_USER_EARLY, SESSION_USER_INCOMPLETE, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT, \
SESSION_GREETER, SESSION_LOCK_SCREEN, SESSION_BACKGROUND, SESSION_BACKGROUND_LIGHT)
/* Which session classes want their own per-user service manager? */
#define SESSION_CLASS_WANTS_SERVICE_MANAGER(class) IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER, SESSION_LOCK_SCREEN, SESSION_BACKGROUND)
@ -48,25 +52,25 @@ typedef enum SessionClass {
#define SESSION_CLASS_PIN_USER(class) (!IN_SET((class), SESSION_MANAGER, SESSION_MANAGER_EARLY, SESSION_NONE))
/* Which session classes decide whether system is idle? (should only cover sessions that have input, and are not idle screens themselves)*/
#define SESSION_CLASS_CAN_IDLE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER))
#define SESSION_CLASS_CAN_IDLE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_EARLY_LIGHT, SESSION_USER_LIGHT, SESSION_GREETER))
/* Which session classes have a lock screen concept? */
#define SESSION_CLASS_CAN_LOCK(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY))
#define SESSION_CLASS_CAN_LOCK(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_EARLY_LIGHT, SESSION_USER_LIGHT))
/* Which sessions are candidates to become "display" sessions */
#define SESSION_CLASS_CAN_DISPLAY(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER))
#define SESSION_CLASS_CAN_DISPLAY(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_EARLY_LIGHT, SESSION_USER_LIGHT, SESSION_GREETER))
/* Which sessions classes should be subject to stop-in-idle */
#define SESSION_CLASS_CAN_STOP_ON_IDLE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY))
#define SESSION_CLASS_CAN_STOP_ON_IDLE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT))
/* Which session classes can take control of devices */
#define SESSION_CLASS_CAN_TAKE_DEVICE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER, SESSION_LOCK_SCREEN))
#define SESSION_CLASS_CAN_TAKE_DEVICE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT, SESSION_GREETER, SESSION_LOCK_SCREEN))
/* Which session classes allow changing session types */
#define SESSION_CLASS_CAN_CHANGE_TYPE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_GREETER, SESSION_LOCK_SCREEN))
#define SESSION_CLASS_CAN_CHANGE_TYPE(class) (IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT, SESSION_GREETER, SESSION_LOCK_SCREEN))
/* Which session classes are taken into acccount when deciding whether shutdown shall be allowed if other users are logged in */
#define SESSION_CLASS_IS_INHIBITOR_LIKE(class) IN_SET((class), SESSION_USER, SESSION_USER_EARLY)
#define SESSION_CLASS_IS_INHIBITOR_LIKE(class) IN_SET((class), SESSION_USER, SESSION_USER_EARLY, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT)
typedef enum SessionType {
SESSION_UNSPECIFIED,

View File

@ -1006,14 +1006,36 @@ static void session_context_mangle(
c->vtnr = 0;
}
if (isempty(c->type))
if (isempty(c->type)) {
c->type = !isempty(c->display) ? "x11" :
!isempty(c->tty) ? "tty" : "unspecified";
pam_debug_syslog(handle, debug, "Automatically chose session type '%s'.", c->type);
}
if (isempty(c->class))
c->class = streq(c->type, "unspecified") ? "background" :
((IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) &&
streq(c->type, "tty")) ? "user-early" : "user");
if (isempty(c->class)) {
c->class = streq(c->type, "unspecified") ? "background" : "user";
/* For non-regular users tweak the type a bit:
*
* - Allow root tty logins *before* systemd-user-sessions.service is run, to allow early boot
* logins to debug things.
*
* - Non-graphical sessions shall be invoked without service manager.
*
* (Note that this somewhat replicates the class mangling logic on systemd-logind.service's
* server side to some degree, in case clients allocate a session and don't specify a
* class. This is somewhat redundant, but we need the class set up properly below.) */
if (IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC)) {
if (streq(c->class, "user"))
c->class = user_record_is_root(ur) ? "user-early" :
(STR_IN_SET(c->type, "x11", "wayland", "mir") ? "user" : "user-light");
else if (streq(c->class, "background"))
c->class = "background-light";
}
pam_debug_syslog(handle, debug, "Automatically chose session class '%s'.", c->class);
}
if (c->incomplete) {
if (streq(c->class, "user"))

View File

@ -21,6 +21,10 @@ static SD_VARLINK_DEFINE_ENUM_TYPE(
SD_VARLINK_DEFINE_ENUM_VALUE(user_early),
SD_VARLINK_FIELD_COMMENT("Regular user session whose home directory is not available right now, but will be later, at which point the session class can be upgraded to 'user'"),
SD_VARLINK_DEFINE_ENUM_VALUE(user_incomplete),
SD_VARLINK_FIELD_COMMENT("A user session that doesn't pull in the per-user service manager"),
SD_VARLINK_DEFINE_ENUM_VALUE(user_light),
SD_VARLINK_FIELD_COMMENT("The combination of user_early and user_light"),
SD_VARLINK_DEFINE_ENUM_VALUE(user_early_light),
SD_VARLINK_FIELD_COMMENT("Display manager greeter screen used for login"),
SD_VARLINK_DEFINE_ENUM_VALUE(greeter),
SD_VARLINK_FIELD_COMMENT("Similar, but a a lock screen"),

View File

@ -711,6 +711,8 @@ testcase_background() {
TRANSIENTUNIT0="none$RANDOM.service"
TRANSIENTUNIT1="bg$RANDOM.service"
TRANSIENTUNIT2="bgg$RANDOM.service"
TRANSIENTUNIT3="bggg$RANDOM.service"
TRANSIENTUNIT4="bgggg$RANDOM.service"
trap background_at_return RETURN
@ -745,6 +747,17 @@ EOF
systemctl stop "$TRANSIENTUNIT2"
systemctl stop user@"$uid".service
# Now check that system users automatically get the light session class assigned
systemd-sysusers --inline "u lightuser"
systemd-run -u "$TRANSIENTUNIT3" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_TYPE=unspecified" -p Type=exec -p User=lightuser sleep infinity
loginctl | grep lightuser | grep -q background-light
systemctl stop "$TRANSIENTUNIT3"
systemd-run -u "$TRANSIENTUNIT4" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_TYPE=tty" -p Type=exec -p User=lightuser sleep infinity
loginctl | grep lightuser | grep -q user-light
systemctl stop "$TRANSIENTUNIT4"
}
testcase_varlink() {