1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-23 10:50:16 +03:00

pam-systemd: introduce "user-light" session type, and make "background-light" the default for system users (#35987)

This implements one idea from #34988: default to "user-light" and
"background-light" for system users, so that the service manager is only
pulled in for sessions that likely need them, i.e. not cron jobs or ftp
logins.

This is a compat break to some degree, but I think a worthy one. I
updated the NEWS file to explain this.
This commit is contained in:
Lennart Poettering 2025-01-15 20:55:01 +01:00 committed by GitHub
commit f8214e2cca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 296 additions and 44 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

@ -224,6 +224,21 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--lightweight=<replaceable>BOOLEAN</replaceable></option></term>
<listitem><para>Controls whether to activate the per-user service manager for the target user. By
default if the target user is <literal>root</literal> or a system user the per-user service manager
is not activated as effect of the <command>run0</command> invocation, otherwise it is.</para>
<para>This ultimately controls the <varname>$XDG_SESSION_CLASS</varname> variable
<citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
respects.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--machine=</option></term>
@ -321,6 +336,7 @@
<member><citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>

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;
@ -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;

View File

@ -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",

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,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,

View File

@ -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"))

View File

@ -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) {

View File

@ -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);

View File

@ -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);

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,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() {