richardphibel / rpms / systemd

Forked from rpms/systemd 2 years ago
Clone
b677e7
From 35455408393cb29a5e49fd769c4823b6a6f886b4 Mon Sep 17 00:00:00 2001
b677e7
From: Lennart Poettering <lennart@poettering.net>
b677e7
Date: Tue, 7 Aug 2018 11:02:00 +0200
b677e7
Subject: [PATCH] logind: optionally, keep the user@.service instance for
b677e7
 eached logged in user around for a while
b677e7
b677e7
This should speed up rapid logout/login cycles a bit.
b677e7
b677e7
By default this timeout is now set to 10s.
b677e7
b677e7
Fixes: #8410
b677e7
Replaces: #4434
b677e7
(cherry picked from commit 9afe9efb9340588db553950727a2a9672dc3db24)
b677e7
b677e7
Resolves: #1642460
b677e7
---
b677e7
 man/logind.conf.xml          | 11 +++++
b677e7
 src/login/logind-core.c      |  2 +
b677e7
 src/login/logind-dbus.c      |  1 +
b677e7
 src/login/logind-gperf.gperf |  1 +
b677e7
 src/login/logind-session.c   |  4 ++
b677e7
 src/login/logind-user.c      | 88 +++++++++++++++++++++++++++++++++---
b677e7
 src/login/logind-user.h      |  7 ++-
b677e7
 src/login/logind.h           |  1 +
b677e7
 8 files changed, 107 insertions(+), 8 deletions(-)
b677e7
b677e7
diff --git a/man/logind.conf.xml b/man/logind.conf.xml
b677e7
index 7d7e869a26..0cf8a7d1f2 100644
b677e7
--- a/man/logind.conf.xml
b677e7
+++ b/man/logind.conf.xml
b677e7
@@ -184,6 +184,17 @@
b677e7
         5.</para></listitem>
b677e7
       </varlistentry>
b677e7
 
b677e7
+      <varlistentry>
b677e7
+        <term><varname>UserStopDelaySec=</varname></term>
b677e7
+
b677e7
+        <listitem><para>Specifies how long to keep the user record and per-user service
b677e7
+        <filename>user@.service</filename> around for a user after they logged out fully. If set to zero, the per-user
b677e7
+        service is terminated immediately when the last session of the user has ended. If this option is configured to
b677e7
+        non-zero rapid logout/login cycles are sped up, as the user's service manager is not constantly restarted. If
b677e7
+        set to <literal>infinity</literal> the per-user service for a user is never terminated again after first login,
b677e7
+        and continues to run until system shutdown. Defaults to 10s.</para></listitem>
b677e7
+      </varlistentry>
b677e7
+
b677e7
       <varlistentry>
b677e7
         <term><varname>HandlePowerKey=</varname></term>
b677e7
         <term><varname>HandleSuspendKey=</varname></term>
b677e7
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
b677e7
index f598bbaa1c..678c708df1 100644
b677e7
--- a/src/login/logind-core.c
b677e7
+++ b/src/login/logind-core.c
b677e7
@@ -27,6 +27,8 @@ void manager_reset_config(Manager *m) {
b677e7
         m->reserve_vt = 6;
b677e7
         m->remove_ipc = false;
b677e7
         m->inhibit_delay_max = 5 * USEC_PER_SEC;
b677e7
+        m->user_stop_delay = 10 * USEC_PER_SEC;
b677e7
+
b677e7
         m->handle_power_key = HANDLE_POWEROFF;
b677e7
         m->handle_suspend_key = HANDLE_SUSPEND;
b677e7
         m->handle_hibernate_key = HANDLE_HIBERNATE;
b677e7
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
b677e7
index 7eba617fff..6586280269 100644
b677e7
--- a/src/login/logind-dbus.c
b677e7
+++ b/src/login/logind-dbus.c
b677e7
@@ -2695,6 +2695,7 @@ const sd_bus_vtable manager_vtable[] = {
b677e7
         SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
b677e7
         SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
b677e7
         SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST),
b677e7
+        SD_BUS_PROPERTY("UserStopDelayUSec", "t", NULL, offsetof(Manager, user_stop_delay), SD_BUS_VTABLE_PROPERTY_CONST),
b677e7
         SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST),
b677e7
         SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST),
b677e7
         SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST),
b677e7
diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf
b677e7
index c85339dcd3..8829ce7d85 100644
b677e7
--- a/src/login/logind-gperf.gperf
b677e7
+++ b/src/login/logind-gperf.gperf
b677e7
@@ -23,6 +23,7 @@ Login.KillUserProcesses,            config_parse_bool,                  0, offse
b677e7
 Login.KillOnlyUsers,                config_parse_strv,                  0, offsetof(Manager, kill_only_users)
b677e7
 Login.KillExcludeUsers,             config_parse_strv,                  0, offsetof(Manager, kill_exclude_users)
b677e7
 Login.InhibitDelayMaxSec,           config_parse_sec,                   0, offsetof(Manager, inhibit_delay_max)
b677e7
+Login.UserStopDelaySec,             config_parse_sec,                   0, offsetof(Manager, user_stop_delay)
b677e7
 Login.HandlePowerKey,               config_parse_handle_action,         0, offsetof(Manager, handle_power_key)
b677e7
 Login.HandleSuspendKey,             config_parse_handle_action,         0, offsetof(Manager, handle_suspend_key)
b677e7
 Login.HandleHibernateKey,           config_parse_handle_action,         0, offsetof(Manager, handle_hibernate_key)
b677e7
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
b677e7
index d56b48a732..dd4ac9482a 100644
b677e7
--- a/src/login/logind-session.c
b677e7
+++ b/src/login/logind-session.c
b677e7
@@ -101,6 +101,8 @@ Session* session_free(Session *s) {
b677e7
 
b677e7
                 if (s->user->display == s)
b677e7
                         s->user->display = NULL;
b677e7
+
b677e7
+                user_update_last_session_timer(s->user);
b677e7
         }
b677e7
 
b677e7
         if (s->seat) {
b677e7
@@ -142,6 +144,8 @@ void session_set_user(Session *s, User *u) {
b677e7
 
b677e7
         s->user = u;
b677e7
         LIST_PREPEND(sessions_by_user, u->sessions, s);
b677e7
+
b677e7
+        user_update_last_session_timer(u);
b677e7
 }
b677e7
 
b677e7
 static void session_save_devices(Session *s, FILE *f) {
b677e7
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
b677e7
index 39fc76f4dc..f23fcbe674 100644
b677e7
--- a/src/login/logind-user.c
b677e7
+++ b/src/login/logind-user.c
b677e7
@@ -47,6 +47,7 @@ int user_new(User **ret, Manager *m, uid_t uid, gid_t gid, const char *name) {
b677e7
                 .manager = m,
b677e7
                 .uid = uid,
b677e7
                 .gid = gid,
b677e7
+                .last_session_timestamp = USEC_INFINITY,
b677e7
         };
b677e7
 
b677e7
         u->name = strdup(name);
b677e7
@@ -113,6 +114,8 @@ User *user_free(User *u) {
b677e7
 
b677e7
         hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u);
b677e7
 
b677e7
+        (void) sd_event_source_unref(u->timer_event_source);
b677e7
+
b677e7
         u->service_job = mfree(u->service_job);
b677e7
 
b677e7
         u->service = mfree(u->service);
b677e7
@@ -170,6 +173,10 @@ static int user_save_internal(User *u) {
b677e7
                         u->timestamp.realtime,
b677e7
                         u->timestamp.monotonic);
b677e7
 
b677e7
+        if (u->last_session_timestamp != USEC_INFINITY)
b677e7
+                fprintf(f, "LAST_SESSION_TIMESTAMP=" USEC_FMT "\n",
b677e7
+                        u->last_session_timestamp);
b677e7
+
b677e7
         if (u->sessions) {
b677e7
                 Session *i;
b677e7
                 bool first;
b677e7
@@ -287,16 +294,17 @@ int user_save(User *u) {
b677e7
 }
b677e7
 
b677e7
 int user_load(User *u) {
b677e7
-        _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL;
b677e7
+        _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL, *last_session_timestamp = NULL;
b677e7
         int r;
b677e7
 
b677e7
         assert(u);
b677e7
 
b677e7
         r = parse_env_file(NULL, u->state_file, NEWLINE,
b677e7
-                           "SERVICE_JOB", &u->service_job,
b677e7
-                           "STOPPING",    &stopping,
b677e7
-                           "REALTIME",    &realtime,
b677e7
-                           "MONOTONIC",   &monotonic,
b677e7
+                           "SERVICE_JOB",            &u->service_job,
b677e7
+                           "STOPPING",               &stopping,
b677e7
+                           "REALTIME",               &realtime,
b677e7
+                           "MONOTONIC",              &monotonic,
b677e7
+                           "LAST_SESSION_TIMESTAMP", &last_session_timestamp,
b677e7
                            NULL);
b677e7
         if (r == -ENOENT)
b677e7
                 return 0;
b677e7
@@ -312,9 +320,11 @@ int user_load(User *u) {
b677e7
         }
b677e7
 
b677e7
         if (realtime)
b677e7
-                timestamp_deserialize(realtime, &u->timestamp.realtime);
b677e7
+                (void) timestamp_deserialize(realtime, &u->timestamp.realtime);
b677e7
         if (monotonic)
b677e7
-                timestamp_deserialize(monotonic, &u->timestamp.monotonic);
b677e7
+                (void) timestamp_deserialize(monotonic, &u->timestamp.monotonic);
b677e7
+        if (last_session_timestamp)
b677e7
+                (void) timestamp_deserialize(last_session_timestamp, &u->last_session_timestamp);
b677e7
 
b677e7
         return 0;
b677e7
 }
b677e7
@@ -524,6 +534,17 @@ bool user_may_gc(User *u, bool drop_not_started) {
b677e7
         if (u->sessions)
b677e7
                 return false;
b677e7
 
b677e7
+        if (u->last_session_timestamp != USEC_INFINITY) {
b677e7
+                /* All sessions have been closed. Let's see if we shall leave the user record around for a bit */
b677e7
+
b677e7
+                if (u->manager->user_stop_delay == USEC_INFINITY)
b677e7
+                        return false; /* Leave it around forever! */
b677e7
+                if (u->manager->user_stop_delay > 0 &&
b677e7
+                    now(CLOCK_MONOTONIC) < usec_add(u->last_session_timestamp, u->manager->user_stop_delay))
b677e7
+                        return false; /* Leave it around for a bit longer. */
b677e7
+        }
b677e7
+
b677e7
+        /* Is this a user that shall stay around forever? */
b677e7
         if (user_check_linger_file(u) > 0)
b677e7
                 return false;
b677e7
 
b677e7
@@ -649,6 +670,59 @@ void user_elect_display(User *u) {
b677e7
         }
b677e7
 }
b677e7
 
b677e7
+static int user_stop_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
b677e7
+        User *u = userdata;
b677e7
+
b677e7
+        assert(u);
b677e7
+        user_add_to_gc_queue(u);
b677e7
+
b677e7
+        return 0;
b677e7
+}
b677e7
+
b677e7
+void user_update_last_session_timer(User *u) {
b677e7
+        int r;
b677e7
+
b677e7
+        assert(u);
b677e7
+
b677e7
+        if (u->sessions) {
b677e7
+                /* There are sessions, turn off the timer */
b677e7
+                u->last_session_timestamp = USEC_INFINITY;
b677e7
+                u->timer_event_source = sd_event_source_unref(u->timer_event_source);
b677e7
+                return;
b677e7
+        }
b677e7
+
b677e7
+        if (u->last_session_timestamp != USEC_INFINITY)
b677e7
+                return; /* Timer already started */
b677e7
+
b677e7
+        u->last_session_timestamp = now(CLOCK_MONOTONIC);
b677e7
+
b677e7
+        assert(!u->timer_event_source);
b677e7
+
b677e7
+        if (u->manager->user_stop_delay == 0 || u->manager->user_stop_delay == USEC_INFINITY)
b677e7
+                return;
b677e7
+
b677e7
+        if (sd_event_get_state(u->manager->event) == SD_EVENT_FINISHED) {
b677e7
+                log_debug("Not allocating user stop timeout, since we are already exiting.");
b677e7
+                return;
b677e7
+        }
b677e7
+
b677e7
+        r = sd_event_add_time(u->manager->event,
b677e7
+                              &u->timer_event_source,
b677e7
+                              CLOCK_MONOTONIC,
b677e7
+                              usec_add(u->last_session_timestamp, u->manager->user_stop_delay), 0,
b677e7
+                              user_stop_timeout_callback, u);
b677e7
+        if (r < 0)
b677e7
+                log_warning_errno(r, "Failed to enqueue user stop event source, ignoring: %m");
b677e7
+
b677e7
+        if (DEBUG_LOGGING) {
b677e7
+                char s[FORMAT_TIMESPAN_MAX];
b677e7
+
b677e7
+                log_debug("Last session of user '%s' logged out, terminating user context in %s.",
b677e7
+                          u->name,
b677e7
+                          format_timespan(s, sizeof(s), u->manager->user_stop_delay, USEC_PER_MSEC));
b677e7
+        }
b677e7
+}
b677e7
+
b677e7
 static const char* const user_state_table[_USER_STATE_MAX] = {
b677e7
         [USER_OFFLINE] = "offline",
b677e7
         [USER_OPENING] = "opening",
b677e7
diff --git a/src/login/logind-user.h b/src/login/logind-user.h
b677e7
index 5e1f7b813a..e05646adc9 100644
b677e7
--- a/src/login/logind-user.h
b677e7
+++ b/src/login/logind-user.h
b677e7
@@ -34,7 +34,11 @@ struct User {
b677e7
 
b677e7
         Session *display;
b677e7
 
b677e7
-        dual_timestamp timestamp;
b677e7
+        dual_timestamp timestamp;      /* When this User object was 'started' the first time */
b677e7
+        usec_t last_session_timestamp; /* When the number of sessions of this user went from 1 to 0 the last time */
b677e7
+
b677e7
+        /* Set up when the last session of the user logs out */
b677e7
+        sd_event_source *timer_event_source;
b677e7
 
b677e7
         bool in_gc_queue:1;
b677e7
 
b677e7
@@ -62,6 +66,7 @@ int user_load(User *u);
b677e7
 int user_kill(User *u, int signo);
b677e7
 int user_check_linger_file(User *u);
b677e7
 void user_elect_display(User *u);
b677e7
+void user_update_last_session_timer(User *u);
b677e7
 
b677e7
 extern const sd_bus_vtable user_vtable[];
b677e7
 int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
b677e7
diff --git a/src/login/logind.h b/src/login/logind.h
b677e7
index ae4d74076b..7288dd7445 100644
b677e7
--- a/src/login/logind.h
b677e7
+++ b/src/login/logind.h
b677e7
@@ -62,6 +62,7 @@ struct Manager {
b677e7
         Hashmap *user_units;
b677e7
 
b677e7
         usec_t inhibit_delay_max;
b677e7
+        usec_t user_stop_delay;
b677e7
 
b677e7
         /* If an action is currently being executed or is delayed,
b677e7
          * this is != 0 and encodes what is being done */