9fc0f6
From 553a962a570cade5c44953593209d25e28ec5145 Mon Sep 17 00:00:00 2001
9fc0f6
From: Michal Sekletar <msekleta@redhat.com>
9fc0f6
Date: Mon, 3 Mar 2014 15:37:49 +0100
9fc0f6
Subject: [PATCH] logind: rework session shutdown logic
9fc0f6
9fc0f6
Simplify the shutdown logic a bit:
9fc0f6
9fc0f6
- Keep the session FIFO around in the PAM module, even after the session
9fc0f6
  shutdown hook has been finished. This allows logind to track precisely
9fc0f6
  when the PAM handler goes away.
9fc0f6
9fc0f6
- In the ReleaseSession() call start a timer, that will stop terminate
9fc0f6
  the session when elapsed.
9fc0f6
9fc0f6
- Never fiddle with the KillMode of scopes to configure whether user
9fc0f6
  processes should be killed or not. Instead, simply leave the scope
9fc0f6
  units around when we terminate a session whose processes should not be
9fc0f6
  killed.
9fc0f6
9fc0f6
- When killing is enabled, stop the session scope on FIFO EOF or after
9fc0f6
  the ReleaseSession() timeout. When killing is disabled, simply tell
9fc0f6
  PID 1 to abandon the scope.
9fc0f6
9fc0f6
Because the scopes stay around and hence all processes are always member
9fc0f6
of a scope, the system shutdown logic should be more robust, as the
9fc0f6
scopes can be shutdown as part of the usual shutdown logic.
9fc0f6
9fc0f6
Based-on: 5f41d1f10fd97e93517b6a762b1bec247f4d1171
9fc0f6
---
9fc0f6
 src/login/logind-dbus.c    |  51 +++++++++++-------
9fc0f6
 src/login/logind-session.c | 130 +++++++++++++++++++++++++++++++++++----------
9fc0f6
 src/login/logind-session.h |   4 ++
9fc0f6
 src/login/logind-user.c    |  23 +++++---
9fc0f6
 src/login/logind-user.h    |   1 +
9fc0f6
 src/login/logind.c         |  23 ++++++--
9fc0f6
 src/login/logind.h         |   4 +-
9fc0f6
 src/login/pam-module.c     |  11 ++--
9fc0f6
 8 files changed, 182 insertions(+), 65 deletions(-)
9fc0f6
9fc0f6
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
9fc0f6
index 69e94aa..8de301e 100644
9fc0f6
--- a/src/login/logind-dbus.c
9fc0f6
+++ b/src/login/logind-dbus.c
9fc0f6
@@ -1747,13 +1747,7 @@ static DBusHandlerResult manager_message_handler(
9fc0f6
                 if (!session)
9fc0f6
                         return bus_send_error_reply(connection, message, &error, -ENOENT);
9fc0f6
 
9fc0f6
-                /* We use the FIFO to detect stray sessions where the
9fc0f6
-                process invoking PAM dies abnormally. We need to make
9fc0f6
-                sure that that process is not killed if at the clean
9fc0f6
-                end of the session it closes the FIFO. Hence, with
9fc0f6
-                this call explicitly turn off the FIFO logic, so that
9fc0f6
-                the PAM code can finish clean up on its own */
9fc0f6
-                session_remove_fifo(session);
9fc0f6
+                session_release(session);
9fc0f6
 
9fc0f6
                 reply = dbus_message_new_method_return(message);
9fc0f6
                 if (!reply)
9fc0f6
@@ -2551,7 +2545,6 @@ int manager_start_scope(
9fc0f6
                 const char *slice,
9fc0f6
                 const char *description,
9fc0f6
                 const char *after,
9fc0f6
-                const char *kill_mode,
9fc0f6
                 DBusError *error,
9fc0f6
                 char **job) {
9fc0f6
 
9fc0f6
@@ -2623,18 +2616,6 @@ int manager_start_scope(
9fc0f6
                         return log_oom();
9fc0f6
         }
9fc0f6
 
9fc0f6
-        if (!isempty(kill_mode)) {
9fc0f6
-                const char *kill_mode_property = "KillMode";
9fc0f6
-
9fc0f6
-                if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
9fc0f6
-                    !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &kill_mode_property) ||
9fc0f6
-                    !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
9fc0f6
-                    !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &kill_mode) ||
9fc0f6
-                    !dbus_message_iter_close_container(&sub2, &sub3) ||
9fc0f6
-                    !dbus_message_iter_close_container(&sub, &sub2))
9fc0f6
-                        return log_oom();
9fc0f6
-        }
9fc0f6
-
9fc0f6
         /* cgroup empty notification is not available in containers
9fc0f6
          * currently. To make this less problematic, let's shorten the
9fc0f6
          * stop timeout for sessions, so that we don't wait
9fc0f6
@@ -2793,6 +2774,36 @@ int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char
9fc0f6
         return 1;
9fc0f6
 }
9fc0f6
 
9fc0f6
+int manager_abandon_scope(Manager *manager, const char *scope, DBusError *error) {
9fc0f6
+        _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
9fc0f6
+        _cleanup_free_ char *path = NULL;
9fc0f6
+        int r;
9fc0f6
+
9fc0f6
+        assert(manager);
9fc0f6
+        assert(scope);
9fc0f6
+
9fc0f6
+        path = unit_dbus_path_from_name(scope);
9fc0f6
+        if (!path)
9fc0f6
+                return -ENOMEM;
9fc0f6
+
9fc0f6
+        r = bus_method_call_with_reply(
9fc0f6
+                manager->bus,
9fc0f6
+                "org.freedesktop.systemd1",
9fc0f6
+                path,
9fc0f6
+                "org.freedesktop.systemd1.Scope",
9fc0f6
+                "Abandon",
9fc0f6
+                &reply,
9fc0f6
+                error,
9fc0f6
+                DBUS_TYPE_INVALID);
9fc0f6
+
9fc0f6
+        if (r < 0) {
9fc0f6
+                log_error("Failed to abandon scope %s", scope);
9fc0f6
+                return r;
9fc0f6
+        }
9fc0f6
+
9fc0f6
+        return 1;
9fc0f6
+}
9fc0f6
+
9fc0f6
 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
9fc0f6
         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
9fc0f6
         const char *w;
9fc0f6
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
9fc0f6
index 27aa335..78e6d74 100644
9fc0f6
--- a/src/login/logind-session.c
9fc0f6
+++ b/src/login/logind-session.c
9fc0f6
@@ -24,6 +24,7 @@
9fc0f6
 #include <unistd.h>
9fc0f6
 #include <sys/epoll.h>
9fc0f6
 #include <fcntl.h>
9fc0f6
+#include <sys/timerfd.h>
9fc0f6
 
9fc0f6
 #include <systemd/sd-id128.h>
9fc0f6
 #include <systemd/sd-messages.h>
9fc0f6
@@ -36,6 +37,8 @@
9fc0f6
 #include "dbus-common.h"
9fc0f6
 #include "logind-session.h"
9fc0f6
 
9fc0f6
+#define RELEASE_SEC 20
9fc0f6
+
9fc0f6
 static unsigned devt_hash_func(const void *p) {
9fc0f6
         uint64_t u = *(const dev_t*)p;
9fc0f6
 
9fc0f6
@@ -505,7 +508,6 @@ static int session_start_scope(Session *s) {
9fc0f6
 
9fc0f6
         if (!s->scope) {
9fc0f6
                 _cleanup_free_ char *description = NULL;
9fc0f6
-                const char *kill_mode;
9fc0f6
                 char *scope, *job;
9fc0f6
 
9fc0f6
                 description = strjoin("Session ", s->id, " of user ", s->user->name, NULL);
9fc0f6
@@ -516,9 +518,7 @@ static int session_start_scope(Session *s) {
9fc0f6
                 if (!scope)
9fc0f6
                         return log_oom();
9fc0f6
 
9fc0f6
-                kill_mode = manager_shall_kill(s->manager, s->user->name) ? "control-group" : "none";
9fc0f6
-
9fc0f6
-                r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "systemd-user-sessions.service", kill_mode, &error, &job;;
9fc0f6
+                r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "systemd-logind.service", &error, &job;;
9fc0f6
                 if (r < 0) {
9fc0f6
                         log_error("Failed to start session scope %s: %s %s",
9fc0f6
                                   scope, bus_error(&error, r), error.name);
9fc0f6
@@ -579,23 +579,22 @@ int session_start(Session *s) {
9fc0f6
 
9fc0f6
         s->started = true;
9fc0f6
 
9fc0f6
-        /* Save session data */
9fc0f6
+        /* Save data */
9fc0f6
         session_save(s);
9fc0f6
         user_save(s->user);
9fc0f6
+        if (s->seat)
9fc0f6
+                seat_save(s->seat);
9fc0f6
 
9fc0f6
+        /* Send signals */
9fc0f6
         session_send_signal(s, true);
9fc0f6
 
9fc0f6
         if (s->seat) {
9fc0f6
-                seat_save(s->seat);
9fc0f6
-
9fc0f6
                 if (s->seat->active == s)
9fc0f6
                         seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
9fc0f6
                 else
9fc0f6
                         seat_send_changed(s->seat, "Sessions\0");
9fc0f6
         }
9fc0f6
 
9fc0f6
-        user_send_changed(s->user, "Sessions\0");
9fc0f6
-
9fc0f6
         return 0;
9fc0f6
 }
9fc0f6
 
9fc0f6
@@ -611,15 +610,24 @@ static int session_stop_scope(Session *s) {
9fc0f6
         if (!s->scope)
9fc0f6
                 return 0;
9fc0f6
 
9fc0f6
-        r = manager_stop_unit(s->manager, s->scope, &error, &job;;
9fc0f6
-        if (r < 0) {
9fc0f6
-                log_error("Failed to stop session scope: %s", bus_error(&error, r));
9fc0f6
-                dbus_error_free(&error);
9fc0f6
-                return r;
9fc0f6
-        }
9fc0f6
+        if (manager_shall_kill(s->manager, s->user->name)) {
9fc0f6
+                r = manager_stop_unit(s->manager, s->scope, &error, &job;;
9fc0f6
+                if (r < 0) {
9fc0f6
+                        log_error("Failed to stop session scope: %s", bus_error(&error, r));
9fc0f6
+                        dbus_error_free(&error);
9fc0f6
+                        return r;
9fc0f6
+                }
9fc0f6
 
9fc0f6
-        free(s->scope_job);
9fc0f6
-        s->scope_job = job;
9fc0f6
+                free(s->scope_job);
9fc0f6
+                s->scope_job = job;
9fc0f6
+        } else {
9fc0f6
+                r = manager_abandon_scope(s->manager, s->scope, &error);
9fc0f6
+                if (r < 0) {
9fc0f6
+                        log_error("Failed to abandon session scope: %s", bus_error(&error, r));
9fc0f6
+                        dbus_error_free(&error);
9fc0f6
+                        return r;
9fc0f6
+                }
9fc0f6
+        }
9fc0f6
 
9fc0f6
         return 0;
9fc0f6
 }
9fc0f6
@@ -644,6 +652,19 @@ static int session_unlink_x11_socket(Session *s) {
9fc0f6
         return r < 0 ? -errno : 0;
9fc0f6
 }
9fc0f6
 
9fc0f6
+static void session_close_timer_fd(Session *s) {
9fc0f6
+        assert(s);
9fc0f6
+
9fc0f6
+        if (s->timer_fd <= 0)
9fc0f6
+                return;
9fc0f6
+
9fc0f6
+        hashmap_remove(s->manager->timer_fds, INT_TO_PTR(s->timer_fd + 1));
9fc0f6
+        epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->timer_fd, NULL);
9fc0f6
+
9fc0f6
+        close_nointr(s->timer_fd);
9fc0f6
+        s->timer_fd = -1;
9fc0f6
+}
9fc0f6
+
9fc0f6
 int session_stop(Session *s) {
9fc0f6
         int r;
9fc0f6
 
9fc0f6
@@ -652,11 +673,18 @@ int session_stop(Session *s) {
9fc0f6
         if (!s->user)
9fc0f6
                 return -ESTALE;
9fc0f6
 
9fc0f6
+        session_close_timer_fd(s);
9fc0f6
+
9fc0f6
+        /* We are going down, don't care about FIFOs anymore */
9fc0f6
+        session_remove_fifo(s);
9fc0f6
+
9fc0f6
         /* Kill cgroup */
9fc0f6
         r = session_stop_scope(s);
9fc0f6
 
9fc0f6
         session_save(s);
9fc0f6
 
9fc0f6
+        s->stopping = true;
9fc0f6
+
9fc0f6
         return r;
9fc0f6
 }
9fc0f6
 
9fc0f6
@@ -678,6 +706,8 @@ int session_finalize(Session *s) {
9fc0f6
                            "MESSAGE=Removed session %s.", s->id,
9fc0f6
                            NULL);
9fc0f6
 
9fc0f6
+        session_close_timer_fd(s);
9fc0f6
+
9fc0f6
         /* Kill session devices */
9fc0f6
         while ((sd = hashmap_first(s->devices)))
9fc0f6
                 session_device_free(sd);
9fc0f6
@@ -698,16 +728,64 @@ int session_finalize(Session *s) {
9fc0f6
                 if (s->seat->active == s)
9fc0f6
                         seat_set_active(s->seat, NULL);
9fc0f6
 
9fc0f6
-                seat_send_changed(s->seat, "Sessions\0");
9fc0f6
                 seat_save(s->seat);
9fc0f6
+                seat_send_changed(s->seat, "Sessions\0");
9fc0f6
         }
9fc0f6
 
9fc0f6
-        user_send_changed(s->user, "Sessions\0");
9fc0f6
         user_save(s->user);
9fc0f6
+        user_send_changed(s->user, "Sessions\0");
9fc0f6
 
9fc0f6
         return r;
9fc0f6
 }
9fc0f6
 
9fc0f6
+void session_release(Session *s) {
9fc0f6
+        int r;
9fc0f6
+
9fc0f6
+        struct itimerspec its = { .it_value.tv_sec = RELEASE_SEC };
9fc0f6
+        struct epoll_event ev = {};
9fc0f6
+
9fc0f6
+        assert(s);
9fc0f6
+
9fc0f6
+        if (!s->started || s->stopping)
9fc0f6
+                return;
9fc0f6
+
9fc0f6
+        if (s->timer_fd >= 0)
9fc0f6
+                return;
9fc0f6
+
9fc0f6
+        s->timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
9fc0f6
+        if (s->timer_fd < 0) {
9fc0f6
+                log_error("Failed to create session release timer fd");
9fc0f6
+                goto out;
9fc0f6
+        }
9fc0f6
+
9fc0f6
+        r = hashmap_put(s->manager->timer_fds, INT_TO_PTR(s->timer_fd + 1), s);
9fc0f6
+        if (r < 0) {
9fc0f6
+                log_error("Failed to store session release timer fd");
9fc0f6
+                goto out;
9fc0f6
+        }
9fc0f6
+
9fc0f6
+        ev.events = EPOLLONESHOT;
9fc0f6
+        ev.data.u32 = FD_OTHER_BASE + s->timer_fd;
9fc0f6
+
9fc0f6
+        r = epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->timer_fd, &ev;;
9fc0f6
+        if (r < 0) {
9fc0f6
+                log_error("Failed to add session release timer fd to epoll instance");
9fc0f6
+                goto out;
9fc0f6
+        }
9fc0f6
+
9fc0f6
+        r = timerfd_settime(s->timer_fd, TFD_TIMER_ABSTIME, &its, NULL);
9fc0f6
+        if (r < 0) {
9fc0f6
+                log_error("Failed to arm timer : %m");
9fc0f6
+                goto out;
9fc0f6
+        }
9fc0f6
+
9fc0f6
+out:
9fc0f6
+        if (s->timer_fd >= 0) {
9fc0f6
+                close_nointr(s->timer_fd);
9fc0f6
+                s->timer_fd = -1;
9fc0f6
+        }
9fc0f6
+}
9fc0f6
+
9fc0f6
 bool session_is_active(Session *s) {
9fc0f6
         assert(s);
9fc0f6
 
9fc0f6
@@ -904,8 +982,6 @@ void session_remove_fifo(Session *s) {
9fc0f6
 }
9fc0f6
 
9fc0f6
 int session_check_gc(Session *s, bool drop_not_started) {
9fc0f6
-        int r;
9fc0f6
-
9fc0f6
         assert(s);
9fc0f6
 
9fc0f6
         if (drop_not_started && !s->started)
9fc0f6
@@ -915,11 +991,7 @@ int session_check_gc(Session *s, bool drop_not_started) {
9fc0f6
                 return 0;
9fc0f6
 
9fc0f6
         if (s->fifo_fd >= 0) {
9fc0f6
-                r = pipe_eof(s->fifo_fd);
9fc0f6
-                if (r < 0)
9fc0f6
-                        return r;
9fc0f6
-
9fc0f6
-                if (r == 0)
9fc0f6
+                if (pipe_eof(s->fifo_fd) <= 0)
9fc0f6
                         return 1;
9fc0f6
         }
9fc0f6
 
9fc0f6
@@ -945,15 +1017,15 @@ void session_add_to_gc_queue(Session *s) {
9fc0f6
 SessionState session_get_state(Session *s) {
9fc0f6
         assert(s);
9fc0f6
 
9fc0f6
+        if (s->stopping || s->timer_fd >= 0)
9fc0f6
+                return SESSION_CLOSING;
9fc0f6
+
9fc0f6
         if (s->closing)
9fc0f6
                 return SESSION_CLOSING;
9fc0f6
 
9fc0f6
         if (s->scope_job)
9fc0f6
                 return SESSION_OPENING;
9fc0f6
 
9fc0f6
-        if (s->fifo_fd < 0)
9fc0f6
-                return SESSION_CLOSING;
9fc0f6
-
9fc0f6
         if (session_is_active(s))
9fc0f6
                 return SESSION_ACTIVE;
9fc0f6
 
9fc0f6
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
9fc0f6
index f175a89..9b76582 100644
9fc0f6
--- a/src/login/logind-session.h
9fc0f6
+++ b/src/login/logind-session.h
9fc0f6
@@ -98,11 +98,14 @@ struct Session {
9fc0f6
         int fifo_fd;
9fc0f6
         char *fifo_path;
9fc0f6
 
9fc0f6
+        int timer_fd;
9fc0f6
+
9fc0f6
         bool idle_hint;
9fc0f6
         dual_timestamp idle_hint_timestamp;
9fc0f6
 
9fc0f6
         bool in_gc_queue:1;
9fc0f6
         bool started:1;
9fc0f6
+        bool stopping:1;
9fc0f6
         bool closing:1;
9fc0f6
 
9fc0f6
         DBusMessage *create_message;
9fc0f6
@@ -130,6 +133,7 @@ void session_remove_fifo(Session *s);
9fc0f6
 int session_start(Session *s);
9fc0f6
 int session_stop(Session *s);
9fc0f6
 int session_finalize(Session *s);
9fc0f6
+void session_release(Session *s);
9fc0f6
 int session_save(Session *s);
9fc0f6
 int session_load(Session *s);
9fc0f6
 int session_kill(Session *s, KillWho who, int signo);
9fc0f6
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
9fc0f6
index 8e7256b..653574e 100644
9fc0f6
--- a/src/login/logind-user.c
9fc0f6
+++ b/src/login/logind-user.c
9fc0f6
@@ -487,6 +487,8 @@ int user_stop(User *u) {
9fc0f6
         if (k < 0)
9fc0f6
                 r = k;
9fc0f6
 
9fc0f6
+        u->stopping = true;
9fc0f6
+
9fc0f6
         user_save(u);
9fc0f6
 
9fc0f6
         return r;
9fc0f6
@@ -602,22 +604,27 @@ void user_add_to_gc_queue(User *u) {
9fc0f6
 
9fc0f6
 UserState user_get_state(User *u) {
9fc0f6
         Session *i;
9fc0f6
-        bool all_closing = true;
9fc0f6
 
9fc0f6
         assert(u);
9fc0f6
 
9fc0f6
+        if (u->stopping)
9fc0f6
+                return USER_CLOSING;
9fc0f6
+
9fc0f6
         if (u->slice_job || u->service_job)
9fc0f6
                 return USER_OPENING;
9fc0f6
 
9fc0f6
-        LIST_FOREACH(sessions_by_user, i, u->sessions) {
9fc0f6
-                if (session_is_active(i))
9fc0f6
-                        return USER_ACTIVE;
9fc0f6
-                if (session_get_state(i) != SESSION_CLOSING)
9fc0f6
-                        all_closing = false;
9fc0f6
-        }
9fc0f6
+        if (u->sessions) {
9fc0f6
+                bool all_closing = true;
9fc0f6
+
9fc0f6
+                LIST_FOREACH(sessions_by_user, i, u->sessions) {
9fc0f6
+                        if (session_is_active(i))
9fc0f6
+                                return USER_ACTIVE;
9fc0f6
+                        if (session_get_state(i) != SESSION_CLOSING)
9fc0f6
+                                all_closing = false;
9fc0f6
+                }
9fc0f6
 
9fc0f6
-        if (u->sessions)
9fc0f6
                 return all_closing ? USER_CLOSING : USER_ONLINE;
9fc0f6
+        }
9fc0f6
 
9fc0f6
         if (user_check_linger_file(u) > 0)
9fc0f6
                 return USER_LINGERING;
9fc0f6
diff --git a/src/login/logind-user.h b/src/login/logind-user.h
9fc0f6
index a36f456..a12532e 100644
9fc0f6
--- a/src/login/logind-user.h
9fc0f6
+++ b/src/login/logind-user.h
9fc0f6
@@ -61,6 +61,7 @@ struct User {
9fc0f6
 
9fc0f6
         bool in_gc_queue:1;
9fc0f6
         bool started:1;
9fc0f6
+        bool stopping:1;
9fc0f6
 
9fc0f6
         LIST_HEAD(Session, sessions);
9fc0f6
         LIST_FIELDS(User, gc_queue);
9fc0f6
diff --git a/src/login/logind.c b/src/login/logind.c
9fc0f6
index 0628032..5180be7 100644
9fc0f6
--- a/src/login/logind.c
9fc0f6
+++ b/src/login/logind.c
9fc0f6
@@ -80,10 +80,11 @@ Manager *manager_new(void) {
9fc0f6
         m->session_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
9fc0f6
         m->inhibitor_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
9fc0f6
         m->button_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
9fc0f6
+        m->timer_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
9fc0f6
 
9fc0f6
         if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->busnames ||
9fc0f6
             !m->user_units || !m->session_units ||
9fc0f6
-            !m->session_fds || !m->inhibitor_fds || !m->button_fds) {
9fc0f6
+            !m->session_fds || !m->inhibitor_fds || !m->button_fds || !m->timer_fds) {
9fc0f6
                 manager_free(m);
9fc0f6
                 return NULL;
9fc0f6
         }
9fc0f6
@@ -149,6 +150,7 @@ void manager_free(Manager *m) {
9fc0f6
         hashmap_free(m->session_fds);
9fc0f6
         hashmap_free(m->inhibitor_fds);
9fc0f6
         hashmap_free(m->button_fds);
9fc0f6
+        hashmap_free(m->timer_fds);
9fc0f6
 
9fc0f6
         if (m->console_active_fd >= 0)
9fc0f6
                 close_nointr_nofail(m->console_active_fd);
9fc0f6
@@ -620,6 +622,13 @@ static void manager_dispatch_other(Manager *m, int fd) {
9fc0f6
                 return;
9fc0f6
         }
9fc0f6
 
9fc0f6
+        s = hashmap_get(m->timer_fds, INT_TO_PTR(fd + 1));
9fc0f6
+        if (s) {
9fc0f6
+                assert(s->timer_fd == fd);
9fc0f6
+                session_stop(s);
9fc0f6
+                return;
9fc0f6
+        }
9fc0f6
+
9fc0f6
         i = hashmap_get(m->inhibitor_fds, INT_TO_PTR(fd + 1));
9fc0f6
         if (i) {
9fc0f6
                 assert(i->fifo_fd == fd);
9fc0f6
@@ -942,8 +951,12 @@ void manager_gc(Manager *m, bool drop_not_started) {
9fc0f6
                 LIST_REMOVE(Session, gc_queue, m->session_gc_queue, session);
9fc0f6
                 session->in_gc_queue = false;
9fc0f6
 
9fc0f6
-                if (session_check_gc(session, drop_not_started) == 0) {
9fc0f6
+                /* First, if we are not closing yet, initiate stopping */
9fc0f6
+                if (!session_check_gc(session, drop_not_started) &&
9fc0f6
+                    session_get_state(session) != SESSION_CLOSING)
9fc0f6
                         session_stop(session);
9fc0f6
+
9fc0f6
+                if (!session_check_gc(session, drop_not_started)) {
9fc0f6
                         session_finalize(session);
9fc0f6
                         session_free(session);
9fc0f6
                 }
9fc0f6
@@ -953,8 +966,11 @@ void manager_gc(Manager *m, bool drop_not_started) {
9fc0f6
                 LIST_REMOVE(User, gc_queue, m->user_gc_queue, user);
9fc0f6
                 user->in_gc_queue = false;
9fc0f6
 
9fc0f6
-                if (user_check_gc(user, drop_not_started) == 0) {
9fc0f6
+                if (!user_check_gc(user, drop_not_started) &&
9fc0f6
+                    user_get_state(user) != USER_CLOSING)
9fc0f6
                         user_stop(user);
9fc0f6
+
9fc0f6
+                if (!user_check_gc(user, drop_not_started)) {
9fc0f6
                         user_finalize(user);
9fc0f6
                         user_free(user);
9fc0f6
                 }
9fc0f6
@@ -1032,6 +1048,7 @@ finish:
9fc0f6
 
9fc0f6
         return r;
9fc0f6
 }
9fc0f6
+
9fc0f6
 int manager_startup(Manager *m) {
9fc0f6
         int r;
9fc0f6
         Seat *seat;
9fc0f6
diff --git a/src/login/logind.h b/src/login/logind.h
9fc0f6
index 9e6296c..0d2248f 100644
9fc0f6
--- a/src/login/logind.h
9fc0f6
+++ b/src/login/logind.h
9fc0f6
@@ -88,6 +88,7 @@ struct Manager {
9fc0f6
         Hashmap *session_fds;
9fc0f6
         Hashmap *inhibitor_fds;
9fc0f6
         Hashmap *button_fds;
9fc0f6
+        Hashmap *timer_fds;
9fc0f6
 
9fc0f6
         usec_t inhibit_delay_max;
9fc0f6
 
9fc0f6
@@ -183,9 +184,10 @@ int manager_send_changed(Manager *manager, const char *properties);
9fc0f6
 
9fc0f6
 int manager_dispatch_delayed(Manager *manager);
9fc0f6
 
9fc0f6
-int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *kill_mode, DBusError *error, char **job);
9fc0f6
+int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, DBusError *error, char **job);
9fc0f6
 int manager_start_unit(Manager *manager, const char *unit, DBusError *error, char **job);
9fc0f6
 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job);
9fc0f6
+int manager_abandon_scope(Manager *manager, const char *scope, DBusError *error);
9fc0f6
 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error);
9fc0f6
 int manager_unit_is_active(Manager *manager, const char *unit);
9fc0f6
 
9fc0f6
diff --git a/src/login/pam-module.c b/src/login/pam-module.c
9fc0f6
index 22d9733..7bd4783 100644
9fc0f6
--- a/src/login/pam-module.c
9fc0f6
+++ b/src/login/pam-module.c
9fc0f6
@@ -478,7 +478,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
9fc0f6
                 int flags,
9fc0f6
                 int argc, const char **argv) {
9fc0f6
 
9fc0f6
-        const void *p = NULL, *existing = NULL;
9fc0f6
+        const void *existing = NULL;
9fc0f6
         const char *id;
9fc0f6
         DBusConnection *bus = NULL;
9fc0f6
         DBusMessage *m = NULL, *reply = NULL;
9fc0f6
@@ -535,12 +535,15 @@ _public_ PAM_EXTERN int pam_sm_close_session(
9fc0f6
                 }
9fc0f6
         }
9fc0f6
 
9fc0f6
+
9fc0f6
+        /* Note that we are knowingly leaking the FIFO fd here. This
9fc0f6
+         * way, logind can watch us die. If we closed it here it would
9fc0f6
+         * not have any clue when that is completed. Given that one
9fc0f6
+         * cannot really have multiple PAM sessions open from the same
9fc0f6
+         * process this means we will leak one FD at max. */
9fc0f6
         r = PAM_SUCCESS;
9fc0f6
 
9fc0f6
 finish:
9fc0f6
-        pam_get_data(handle, "systemd.session-fd", &p);
9fc0f6
-        if (p)
9fc0f6
-                close_nointr(PTR_TO_INT(p) - 1);
9fc0f6
 
9fc0f6
         dbus_error_free(&error);
9fc0f6