-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <fcntl.h>
#include "audit-util.h"
#include "bus-error.h"
#include "bus-util.h"
+#include "devnum-util.h"
#include "env-file.h"
#include "escape.h"
#include "fd-util.h"
#include "logind-session-dbus.h"
#include "logind-session.h"
#include "logind-user-dbus.h"
-#include "mkdir.h"
+#include "mkdir-label.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "tmpfile-util.h"
+#include "uid-alloc-range.h"
#include "user-util.h"
#include "util.h"
free(s->state_file);
free(s->fifo_path);
+ sd_event_source_unref(s->stop_on_idle_event_source);
+
return mfree(s);
}
fprintf(f, "DISPLAY=%s\n", s->display);
if (s->remote_host) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(s->remote_host);
if (!escaped) {
}
if (s->remote_user) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(s->remote_user);
if (!escaped) {
}
if (s->service) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(s->service);
if (!escaped) {
}
if (s->desktop) {
- _cleanup_free_ char *escaped;
+ _cleanup_free_ char *escaped = NULL;
escaped = cescape(s->desktop);
if (!escaped) {
}
static int session_load_devices(Session *s, const char *devices) {
- const char *p;
int r = 0;
assert(s);
- for (p = devices;;) {
+ for (const char *p = devices;;) {
_cleanup_free_ char *word = NULL;
SessionDevice *sd;
dev_t dev;
break;
}
- k = parse_dev(word, &dev);
+ k = parse_devnum(word, &dev);
if (k < 0) {
r = k;
continue;
"ACTIVE", &active,
"DEVICES", &devices,
"IS_DISPLAY", &is_display);
-
if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", s->state_file);
s->class = c;
}
- if (state && streq(state, "closing"))
+ if (streq_ptr(state, "closing"))
s->stopping = true;
if (s->fifo_path) {
assert(s->user);
if (!s->scope) {
+ _cleanup_strv_free_ char **after = NULL;
_cleanup_free_ char *scope = NULL;
const char *description;
if (!scope)
return log_oom();
- description = strjoina("Session ", s->id, " of user ", s->user->user_record->user_name);
+ description = strjoina("Session ", s->id, " of User ", s->user->user_record->user_name);
+
+ /* We usually want to order session scopes after systemd-user-sessions.service since the
+ * latter unit is used as login session barrier for unprivileged users. However the barrier
+ * doesn't apply for root as sysadmin should always be able to log in (and without waiting
+ * for any timeout to expire) in case something goes wrong during the boot process. Since
+ * ordering after systemd-user-sessions.service and the user instance is optional we make use
+ * of STRV_IGNORE with strv_new() to skip these order constraints when needed. */
+ after = strv_new("systemd-logind.service",
+ s->user->runtime_dir_service,
+ !uid_is_system(s->user->user_record->uid) ? "systemd-user-sessions.service" : STRV_IGNORE,
+ s->user->service);
+ if (!after)
+ return log_oom();
r = manager_start_scope(
s->manager,
/* These two have StopWhenUnneeded= set, hence add a dep towards them */
STRV_MAKE(s->user->runtime_dir_service,
s->user->service),
- /* And order us after some more */
- STRV_MAKE("systemd-logind.service",
- "systemd-user-sessions.service",
- s->user->runtime_dir_service,
- s->user->service),
+ after,
user_record_home_directory(s->user->user_record),
properties,
error,
return 0;
}
+static int session_dispatch_stop_on_idle(sd_event_source *source, uint64_t t, void *userdata) {
+ Session *s = userdata;
+ dual_timestamp ts;
+ int r, idle;
+
+ assert(s);
+
+ if (s->stopping)
+ return 0;
+
+ idle = session_get_idle_hint(s, &ts);
+ if (idle) {
+ log_debug("Session \"%s\" of user \"%s\" is idle, stopping.", s->id, s->user->user_record->user_name);
+
+ return session_stop(s, /* force */ true);
+ }
+
+ r = sd_event_source_set_time(source, usec_add(ts.monotonic, s->manager->stop_idle_session_usec));
+ if (r < 0)
+ return log_error_errno(r, "Failed to configure stop on idle session event source: %m");
+
+ r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable stop on idle session event source: %m");
+
+ return 1;
+}
+
+static int session_setup_stop_on_idle_timer(Session *s) {
+ int r;
+
+ assert(s);
+
+ if (s->manager->stop_idle_session_usec == USEC_INFINITY)
+ return 0;
+
+ r = sd_event_add_time_relative(
+ s->manager->event,
+ &s->stop_on_idle_event_source,
+ CLOCK_MONOTONIC,
+ s->manager->stop_idle_session_usec,
+ 0,
+ session_dispatch_stop_on_idle, s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add stop on idle session event source: %m");
+
+ return 0;
+}
+
int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
int r;
if (r < 0)
return r;
+ r = session_setup_stop_on_idle_timer(s);
+ if (r < 0)
+ return r;
+
log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SESSION_START_STR,
"SESSION_ID=%s", s->id,
}
static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
- Session *s = userdata;
+ Session *s = ASSERT_PTR(userdata);
assert(es);
- assert(s);
session_stop(s, /* force = */ false);
return 0;
}
int session_get_idle_hint(Session *s, dual_timestamp *t) {
- usec_t atime = 0;
+ usec_t atime = 0, dtime = 0;
int r;
assert(s);
if (t)
dual_timestamp_from_realtime(t, atime);
- if (s->manager->idle_action_usec <= 0)
+ if (s->manager->idle_action_usec > 0 && s->manager->stop_idle_session_usec != USEC_INFINITY)
+ dtime = MIN(s->manager->idle_action_usec, s->manager->stop_idle_session_usec);
+ else if (s->manager->idle_action_usec > 0)
+ dtime = s->manager->idle_action_usec;
+ else if (s->manager->stop_idle_session_usec != USEC_INFINITY)
+ dtime = s->manager->stop_idle_session_usec;
+ else
return false;
- return usec_add(atime, s->manager->idle_action_usec) <= now(CLOCK_REALTIME);
+ return usec_add(atime, dtime) <= now(CLOCK_REALTIME);
}
int session_set_idle_hint(Session *s, bool b) {
session_send_changed(s, "Type", NULL);
}
-static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
- Session *s = userdata;
+int session_set_display(Session *s, const char *display) {
+ int r;
assert(s);
+ assert(display);
+
+ r = free_and_strdup(&s->display, display);
+ if (r <= 0) /* 0 means the strings were equal */
+ return r;
+
+ session_save(s);
+
+ session_send_changed(s, "Display", NULL);
+
+ return 1;
+}
+
+static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+ Session *s = ASSERT_PTR(userdata);
+
assert(s->fifo_fd == fd);
/* EOF on the FIFO means the session died abnormally. */
}
/* Open writing side */
- r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
- if (r < 0)
- return -errno;
-
- return r;
+ return RET_NERRNO(open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK));
}
static void session_remove_fifo(Session *s) {
}
bool session_is_controller(Session *s, const char *sender) {
- assert(s);
-
- return streq_ptr(s->controller, sender);
+ return streq_ptr(ASSERT_PTR(s)->controller, sender);
}
static void session_release_controller(Session *s, bool notify) {
- _cleanup_free_ char *name = NULL;
+ _unused_ _cleanup_free_ char *name = NULL;
SessionDevice *sd;
if (!s->controller)
}
static int on_bus_track(sd_bus_track *track, void *userdata) {
- Session *s = userdata;
+ Session *s = ASSERT_PTR(userdata);
assert(track);
- assert(s);
session_drop_controller(s);