From a8980738c4ef8fba4fd8995ab69669d438ad7a9f Mon Sep 17 00:00:00 2001 From: Michal Sekletar Date: Mon, 8 Aug 2022 09:13:50 +0200 Subject: [PATCH] logind: add option to stop idle sessions after specified timeout Thanks to Jan Pazdziora for providing a patch which implemeted a PoC of this feature. (cherry picked from commit 82325af3ae41bc7efb3d5cd8f56a4652fef498c2) Resolves: #2100464 --- man/logind.conf.xml | 11 ++++++ man/org.freedesktop.login1.xml | 6 +++ src/login/logind-core.c | 2 + src/login/logind-dbus.c | 1 + src/login/logind-gperf.gperf | 1 + src/login/logind-session.c | 67 ++++++++++++++++++++++++++++++++-- src/login/logind-session.h | 2 + src/login/logind.conf.in | 1 + src/login/logind.h | 2 + 9 files changed, 90 insertions(+), 3 deletions(-) diff --git a/man/logind.conf.xml b/man/logind.conf.xml index 96fa076239..c37fd97be1 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -357,6 +357,17 @@ are excluded from the effect of this setting. Defaults to no. + + StopIdleSessionSec= + + Specifies a timeout in seconds, or a time span value after which + systemd-logind checks the idle state of all sessions. Every session that is idle for + longer then the timeout will be stopped. Defaults to infinity + (systemd-logind is not checking the idle state of sessions). For details about the syntax + of time spans, see + systemd.time7. + + diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml index c11324ee3b..0ebcfb84ab 100644 --- a/man/org.freedesktop.login1.xml +++ b/man/org.freedesktop.login1.xml @@ -235,6 +235,8 @@ node /org/freedesktop/login1 { readonly t SessionsMax = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t NCurrentSessions = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t StopIdleSessionUSec = ...; }; interface org.freedesktop.DBus.Peer { ... }; interface org.freedesktop.DBus.Introspectable { ... }; @@ -242,6 +244,8 @@ node /org/freedesktop/login1 { }; + + @@ -460,6 +464,8 @@ node /org/freedesktop/login1 { + + diff --git a/src/login/logind-core.c b/src/login/logind-core.c index 616c08132a..9807c85b8c 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -71,6 +71,8 @@ void manager_reset_config(Manager *m) { m->kill_only_users = strv_free(m->kill_only_users); m->kill_exclude_users = strv_free(m->kill_exclude_users); + + m->stop_idle_session_usec = USEC_INFINITY; } int manager_parse_config_file(Manager *m) { diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 31a41bd271..c97a89c37d 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -3389,6 +3389,7 @@ static const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_hashmap_size, offsetof(Manager, sessions), 0), SD_BUS_PROPERTY("UserTasksMax", "t", property_get_compat_user_tasks_max, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("StopIdleSessionUSec", "t", NULL, offsetof(Manager, stop_idle_session_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_METHOD_WITH_ARGS("GetSession", SD_BUS_ARGS("s", session_id), diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 867db36533..300d56542d 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -50,3 +50,4 @@ Login.RemoveIPC, config_parse_bool, 0, offse Login.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max) Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max) Login.UserTasksMax, config_parse_compat_user_tasks_max, 0, 0 +Login.StopIdleSessionSec, config_parse_sec_fix_0, 0, offsetof(Manager, stop_idle_session_usec) diff --git a/src/login/logind-session.c b/src/login/logind-session.c index a052596e57..a110b6f1c1 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -150,6 +150,8 @@ Session* session_free(Session *s) { free(s->state_file); free(s->fifo_path); + sd_event_source_unref(s->stop_on_idle_event_source); + return mfree(s); } @@ -687,6 +689,55 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er 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; @@ -709,6 +760,10 @@ int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) { 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, @@ -949,7 +1004,7 @@ static int get_process_ctty_atime(pid_t pid, usec_t *atime) { } int session_get_idle_hint(Session *s, dual_timestamp *t) { - usec_t atime = 0; + usec_t atime = 0, dtime = 0; int r; assert(s); @@ -986,10 +1041,16 @@ found_atime: 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) { diff --git a/src/login/logind-session.h b/src/login/logind-session.h index 6b6ac2d573..4c28607986 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -115,6 +115,8 @@ struct Session { Hashmap *devices; sd_bus_track *track; + sd_event_source *stop_on_idle_event_source; + LIST_FIELDS(Session, sessions_by_user); LIST_FIELDS(Session, sessions_by_seat); diff --git a/src/login/logind.conf.in b/src/login/logind.conf.in index 79d685b3de..fcdbad58c7 100644 --- a/src/login/logind.conf.in +++ b/src/login/logind.conf.in @@ -43,3 +43,4 @@ #RemoveIPC=no #InhibitorsMax=8192 #SessionsMax=8192 +#StopIdleSessionSec=infinity diff --git a/src/login/logind.h b/src/login/logind.h index 730c14a46a..e5c68a74cb 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -101,6 +101,8 @@ struct Manager { usec_t idle_action_not_before_usec; HandleAction idle_action; + usec_t stop_idle_session_usec; + HandleAction handle_power_key; HandleAction handle_power_key_long_press; HandleAction handle_reboot_key;