diff --git a/NEWS b/NEWS index dde530e396..b6b1c80907 100644 --- a/NEWS +++ b/NEWS @@ -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 diff --git a/TODO b/TODO index 48ab8eac84..81d8151cae 100644 --- a/TODO +++ b/TODO @@ -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. diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml index 18c3636b6b..1093df9f82 100644 --- a/man/pam_systemd.xml +++ b/man/pam_systemd.xml @@ -115,6 +115,14 @@ user-early Similar to user but sessions of this class are not ordered after systemd-user-sessions.service8, 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 user class, see above. (Added in v256.) + + user-light + Similar to user, but sessions of this class will not pull in the user@.service5 of the user, and thus possibly have no service manager of the user running. (Added in v258.) + + + user-early-light + Similar to user-early, but sessions of this class will not pull in the user@.service5 of the user, and thus possibly have no service manager of the user running. (Added in v258.) + user-incomplete Similar to user but for sessions which are not fully set up yet, i.e. have no home directory mounted or similar. This is used by systemd-homed.service8 to allow users to log in via ssh1 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 user class once the home directory is activated. @@ -133,7 +141,7 @@ background-light - Similar to background, but sessions of this class will not pull in the user@.service5 of the user, and thus possibly have no services of the user running. (Added in v256.) + Similar to background, but sessions of this class will not pull in the user@.service5 of the user, and thus possibly have no service manager of the user running. (Added in v256.) manager diff --git a/man/run0.xml b/man/run0.xml index 16774813d4..59aa6d05d9 100644 --- a/man/run0.xml +++ b/man/run0.xml @@ -224,6 +224,21 @@ + + + + Controls whether to activate the per-user service manager for the target user. By + default if the target user is root or a system user the per-user service manager + is not activated as effect of the run0 invocation, otherwise it is. + + This ultimately controls the $XDG_SESSION_CLASS variable + pam_systemd8 + respects. + + + + + @@ -321,6 +336,7 @@ systemd-run1 sudo8 machinectl1 + pam_systemd8 diff --git a/man/sd_session_is_active.xml b/man/sd_session_is_active.xml index 1cbef64e00..92cd669dc4 100644 --- a/man/sd_session_is_active.xml +++ b/man/sd_session_is_active.xml @@ -216,10 +216,10 @@ sd_session_get_class() may be used to determine the class of the session identified by the specified session identifier. The returned string is one of user, - user-early, user-incomplete, greeter, - lock-screen, background, background-light, - manager or manager-early and needs to be freed with the libc - user-early, user-light, user-early-light, + user-incomplete, greeter, lock-screen, + background, background-light, manager or + manager-early and needs to be freed with the libc free3 call after use. diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 28bc7b6cef..5d3e2e01b5 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -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; @@ -1835,7 +1859,7 @@ static int have_multiple_sessions( /* Check for other users' sessions. Greeter sessions do not * count, and non-login sessions do not count either. */ HASHMAP_FOREACH(session, m->sessions) - if (IN_SET(session->class, SESSION_USER, SESSION_USER_EARLY) && + if (SESSION_CLASS_IS_INHIBITOR_LIKE(session->class) && session->user->user_record->uid != uid) return true; diff --git a/src/login/logind-session.c b/src/login/logind-session.c index bc48609c26..e8c3ae621b 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -859,7 +859,13 @@ int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) { "SESSION_ID=%s", s->id, "USER_ID=%s", s->user->user_record->user_name, "LEADER="PID_FMT, s->leader.pid, - LOG_MESSAGE("New session %s of user %s.", s->id, s->user->user_record->user_name)); + "CLASS=%s", session_class_to_string(s->class), + "TYPE=%s", session_type_to_string(s->type), + LOG_MESSAGE("New session '%s' of user '%s' with class '%s' and type '%s'.", + s->id, + s->user->user_record->user_name, + session_class_to_string(s->class), + session_type_to_string(s->type))); if (!dual_timestamp_is_set(&s->timestamp)) dual_timestamp_now(&s->timestamp); @@ -1704,6 +1710,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", diff --git a/src/login/logind-session.h b/src/login/logind-session.h index 8ecd90b7ae..c0cf03ff54 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -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,22 +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, SESSION_USER_LIGHT, SESSION_USER_EARLY_LIGHT) typedef enum SessionType { SESSION_UNSPECIFIED, diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index fbc172e9a5..00fc2c360d 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -755,7 +755,7 @@ static int apply_user_record_settings( if (nice_is_valid(ur->nice_level)) { if (nice(ur->nice_level) < 0) - pam_syslog_errno(handle, LOG_ERR, errno, + pam_syslog_errno(handle, LOG_WARNING, errno, "Failed to set nice level to %i, ignoring: %m", ur->nice_level); else pam_debug_syslog(handle, debug, @@ -763,7 +763,6 @@ static int apply_user_record_settings( } for (int rl = 0; rl < _RLIMIT_MAX; rl++) { - if (!ur->rlimits[rl]) continue; @@ -1007,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")) diff --git a/src/run/run.c b/src/run/run.c index 3f7e0a6360..7538029548 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -88,6 +88,7 @@ static bool arg_ignore_failure = false; static char *arg_background = NULL; static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; static char *arg_shell_prompt_prefix = NULL; +static int arg_lightweight = -1; STATIC_DESTRUCTOR_REGISTER(arg_description, freep); STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep); @@ -199,6 +200,8 @@ static int help_sudo_mode(void) { " --pty Request allocation of a pseudo TTY for stdio\n" " --pipe Request direct pipe for stdio\n" " --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n" + " --lightweight=BOOLEAN Control whether to register a session with service manager\n" + " or without\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -778,6 +781,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { ARG_PTY, ARG_PIPE, ARG_SHELL_PROMPT_PREFIX, + ARG_LIGHTWEIGHT, }; /* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions @@ -802,6 +806,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { { "pty", no_argument, NULL, ARG_PTY }, { "pipe", no_argument, NULL, ARG_PIPE }, { "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX }, + { "lightweight", required_argument, NULL, ARG_LIGHTWEIGHT }, {}, }; @@ -914,6 +919,12 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { return r; break; + case ARG_LIGHTWEIGHT: + r = parse_tristate_argument("--lightweight=", optarg, &arg_lightweight); + if (r < 0) + return r; + break; + case '?': return -EINVAL; @@ -947,6 +958,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { if (IN_SET(arg_stdio, ARG_STDIO_NONE, ARG_STDIO_AUTO)) arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT; + log_debug("Using %s stdio mode.", arg_stdio == ARG_STDIO_PTY ? "pty" : "direct"); + arg_expand_environment = false; arg_send_sighup = true; @@ -1045,6 +1058,28 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m"); } + /* When using run0 to acquire privileges temporarily, let's not pull in session manager by + * default. Note that pam_logind/systemd-logind doesn't distinguish between run0-style privilege + * escalation on a TTY and first class (getty-style) TTY logins (and thus gives root a per-session + * manager for interactive TTY sessions), hence let's override the logic explicitly here. We only do + * this for root though, under the assumption that if a regular user temporarily transitions into + * another regular user it's a better default that the full user environment is uniformly + * available. */ + if (arg_lightweight < 0 && !strv_env_get(arg_environment, "XDG_SESSION_CLASS") && privileged_execution()) + arg_lightweight = true; + + if (arg_lightweight >= 0) { + const char *class = + arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early-light" : "user-light") : "background-light") : + (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early" : "user") : "background"); + + log_debug("Setting XDG_SESSION_CLASS to '%s'.", class); + + r = strv_env_assign(&arg_environment, "XDG_SESSION_CLASS", class); + if (r < 0) + return log_error_errno(r, "Failed to set $XDG_SESSION_CLASS environment variable: %m"); + } + return 1; } @@ -1901,6 +1936,46 @@ static int print_unit_invocation(const char *unit, sd_id128_t invocation_id) { return sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL); } +typedef struct JobDoneContext { + char *unit; + char *start_job; + sd_bus_slot *match; +} JobDoneContext; + +static void job_done_context_done(JobDoneContext *c) { + assert(c); + + c->unit = mfree(c->unit); + c->start_job = mfree(c->start_job); + c->match = sd_bus_slot_unref(c->match); +} + +static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + JobDoneContext *c = ASSERT_PTR(userdata); + const char *path; + int r; + + assert(m); + + r = sd_bus_message_read(m, "uoss", /* id = */ NULL, &path, /* unit= */ NULL, /* result= */ NULL); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + if (!streq_ptr(path, c->start_job)) + return 0; + + /* Notify our caller that the service is now running, just in case. */ + (void) sd_notifyf(/* unset_environment= */ false, + "READY=1\n" + "RUN_UNIT=%s", + c->unit); + + job_done_context_done(c); + return 0; +} + static int start_transient_service(sd_bus *bus) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1974,16 +2049,6 @@ static int start_transient_service(sd_bus *bus) { assert_not_reached(); } - /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin - * lets skip this however, because we should start that already when the start job is running, and - * there's little point in waiting for the start job to complete in that case anyway, as we'll wait - * for EOF anyway, which is going to be much later. */ - if (!arg_no_block && arg_stdio == ARG_STDIO_NONE) { - r = bus_wait_for_jobs_new(bus, &w); - if (r < 0) - return log_error_errno(r, "Could not watch jobs: %m"); - } - if (arg_unit) { r = unit_name_mangle_with_suffix(arg_unit, "as unit", arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, @@ -1996,6 +2061,36 @@ static int start_transient_service(sd_bus *bus) { return r; } + /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin + * lets skip this however, because we should start that already when the start job is running, and + * there's little point in waiting for the start job to complete in that case anyway, as we'll wait + * for EOF anyway, which is going to be much later. */ + _cleanup_(job_done_context_done) JobDoneContext job_done_context = {}; + if (!arg_no_block) { + if (arg_stdio == ARG_STDIO_NONE) { + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_error_errno(r, "Could not watch jobs: %m"); + } else { + job_done_context.unit = strdup(service); + if (!job_done_context.unit) + return log_oom(); + + /* When we are a bus client we match by sender. Direct connections OTOH have no + * initialized sender field, and hence we ignore the sender then */ + r = sd_bus_match_signal_async( + bus, + &job_done_context.match, + sd_bus_is_bus_client(bus) ? "org.freedesktop.systemd1" : NULL, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved", + match_job_removed, NULL, &job_done_context); + if (r < 0) + return log_error_errno(r, "Failed to install JobRemove match: %m"); + } + } + r = make_transient_service_unit(bus, &m, service, pty_path, peer_fd); if (r < 0) return r; @@ -2005,19 +2100,22 @@ static int start_transient_service(sd_bus *bus) { if (r < 0) return r; + const char *object; + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) + return bus_log_parse_error(r); + if (w) { - const char *object; - - r = sd_bus_message_read(reply, "o", &object); - if (r < 0) - return bus_log_parse_error(r); - r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL); if (r < 0) return r; + } else if (job_done_context.match) { + job_done_context.start_job = strdup(object); + if (!job_done_context.start_job) + return log_oom(); } if (!arg_quiet) { diff --git a/src/shared/parse-argument.c b/src/shared/parse-argument.c index e6159c1482..177028e424 100644 --- a/src/shared/parse-argument.c +++ b/src/shared/parse-argument.c @@ -31,6 +31,26 @@ int parse_boolean_argument(const char *optname, const char *s, bool *ret) { } } +int parse_tristate_argument(const char *optname, const char *s, int *ret) { + int r; + + if (s) { + r = parse_boolean(s); + if (r < 0) + return log_error_errno(r, "Failed to parse boolean argument to %s: %s.", optname, s); + + if (ret) + *ret = r; + + return r; + } else { + if (ret) + *ret = -1; + + return 0; + } +} + int parse_json_argument(const char *s, sd_json_format_flags_t *ret) { assert(s); assert(ret); diff --git a/src/shared/parse-argument.h b/src/shared/parse-argument.h index 6a1a67aef7..bd577033b8 100644 --- a/src/shared/parse-argument.h +++ b/src/shared/parse-argument.h @@ -6,6 +6,7 @@ #include "sd-json.h" int parse_boolean_argument(const char *optname, const char *s, bool *ret); +int parse_tristate_argument(const char *optname, const char *s, int *ret); int parse_json_argument(const char *s, sd_json_format_flags_t *ret); int parse_path_argument(const char *path, bool suppress_root, char **arg); int parse_signal_argument(const char *s, int *ret); diff --git a/src/shared/varlink-io.systemd.Login.c b/src/shared/varlink-io.systemd.Login.c index 9076d47456..f5c5664f66 100644 --- a/src/shared/varlink-io.systemd.Login.c +++ b/src/shared/varlink-io.systemd.Login.c @@ -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"), diff --git a/test/units/TEST-35-LOGIN.sh b/test/units/TEST-35-LOGIN.sh index 060e1fb18a..5a83b5847a 100755 --- a/test/units/TEST-35-LOGIN.sh +++ b/test/units/TEST-35-LOGIN.sh @@ -711,6 +711,12 @@ testcase_background() { TRANSIENTUNIT0="none$RANDOM.service" TRANSIENTUNIT1="bg$RANDOM.service" TRANSIENTUNIT2="bgg$RANDOM.service" + TRANSIENTUNIT3="bggg$RANDOM.service" + TRANSIENTUNIT4="bgggg$RANDOM.service" + RUN0UNIT0="run0$RANDOM.service" + RUN0UNIT1="runn0$RANDOM.service" + RUN0UNIT2="runnn0$RANDOM.service" + RUN0UNIT3="runnnn0$RANDOM.service" trap background_at_return RETURN @@ -745,6 +751,34 @@ 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" + + # Now check that run0's session class control works + systemd-run --service-type=notify run0 -u lightuser --unit="$RUN0UNIT0" sleep infinity + loginctl | grep lightuser | grep -q "background-light " + systemctl stop "$RUN0UNIT0" + + systemd-run --service-type=notify run0 -u lightuser --unit="$RUN0UNIT1" --lightweight=yes sleep infinity + loginctl | grep lightuser | grep -q "background-light " + systemctl stop "$RUN0UNIT1" + + systemd-run --service-type=notify run0 -u lightuser --unit="$RUN0UNIT2" --lightweight=no sleep infinity + loginctl | grep lightuser | grep -q "background " + systemctl stop "$RUN0UNIT2" + + systemd-run --service-type=notify run0 -u root --unit="$RUN0UNIT3" sleep infinity + loginctl | grep root | grep -q "background-light " + systemctl stop "$RUN0UNIT3" } testcase_varlink() {