naccyde / rpms / systemd

Forked from rpms/systemd a year ago
Clone
b12df0
From a05c1077911652954c8b9e82cfdc0fc643eca782 Mon Sep 17 00:00:00 2001
b12df0
From: Lennart Poettering <lennart@poettering.net>
b12df0
Date: Mon, 6 Aug 2018 21:44:45 +0200
b12df0
Subject: [PATCH] logind: rework how we manage the slice and
b12df0
 user-runtime-dir@.service unit for each user
b12df0
b12df0
Instead of managing it explicitly, let's simplify things and rely on
b12df0
regular Wants=/Requires= dependencies to pull in these units from
b12df0
user@.service and the session scope, and StopWhenUneeded= to stop these
b12df0
auxiliary units again. This way, they can be pulled in easily by
b12df0
unrelated units too.
b12df0
b12df0
This simplifies things quite a bit: for each session we now only need to
b12df0
manage the session scope, and for each user the user@.service, the other
b12df0
units are not something we need to manage anymore.
b12df0
b12df0
This patch also makes sure that if user@.service of a user is masked we
b12df0
will continue to work, and user-runtime-dir@.service will still be
b12df0
correctly pulled in, as it is now a dependency of the scope unit.
b12df0
b12df0
Fixes: #9461
b12df0
Replaces: #5546
b12df0
(cherry picked from commit 25a1ab4ed48b72e974f77a68dcbe3521014787bb)
b12df0
b12df0
Related: #1642460
b12df0
---
b12df0
 src/login/logind-dbus.c    |  58 ++++++++--------
b12df0
 src/login/logind-session.c |  64 ++++++++++--------
b12df0
 src/login/logind-session.h |   2 +-
b12df0
 src/login/logind-user.c    | 134 ++++++++++++++-----------------------
b12df0
 src/login/logind-user.h    |   7 +-
b12df0
 src/login/logind.c         |   2 +-
b12df0
 src/login/logind.h         |   2 +-
b12df0
 7 files changed, 123 insertions(+), 146 deletions(-)
b12df0
b12df0
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
b12df0
index 4b2c418453..7eba617fff 100644
b12df0
--- a/src/login/logind-dbus.c
b12df0
+++ b/src/login/logind-dbus.c
b12df0
@@ -847,7 +847,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
b12df0
         if (r < 0)
b12df0
                 goto fail;
b12df0
 
b12df0
-        r = session_start(session, message);
b12df0
+        r = session_start(session, message, error);
b12df0
         if (r < 0)
b12df0
                 goto fail;
b12df0
 
b12df0
@@ -3110,24 +3110,20 @@ const sd_bus_vtable manager_vtable[] = {
b12df0
 };
b12df0
 
b12df0
 static int session_jobs_reply(Session *s, const char *unit, const char *result) {
b12df0
-        int r = 0;
b12df0
-
b12df0
         assert(s);
b12df0
         assert(unit);
b12df0
 
b12df0
         if (!s->started)
b12df0
-                return r;
b12df0
+                return 0;
b12df0
 
b12df0
-        if (streq(result, "done"))
b12df0
-                r = session_send_create_reply(s, NULL);
b12df0
-        else {
b12df0
+        if (result && !streq(result, "done")) {
b12df0
                 _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
b12df0
 
b12df0
-                sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
b12df0
-                r = session_send_create_reply(s, &e);
b12df0
+                sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit '%s' failed with '%s'", unit, result);
b12df0
+                return session_send_create_reply(s, &e);
b12df0
         }
b12df0
 
b12df0
-        return r;
b12df0
+        return session_send_create_reply(s, NULL);
b12df0
 }
b12df0
 
b12df0
 int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
b12df0
@@ -3160,30 +3156,29 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
b12df0
         }
b12df0
 
b12df0
         session = hashmap_get(m->session_units, unit);
b12df0
-        if (session && streq_ptr(path, session->scope_job)) {
b12df0
-                session->scope_job = mfree(session->scope_job);
b12df0
-                session_jobs_reply(session, unit, result);
b12df0
+        if (session) {
b12df0
+                if (streq_ptr(path, session->scope_job)) {
b12df0
+                        session->scope_job = mfree(session->scope_job);
b12df0
+                        (void) session_jobs_reply(session, unit, result);
b12df0
+
b12df0
+                        session_save(session);
b12df0
+                        user_save(session->user);
b12df0
+                }
b12df0
 
b12df0
-                session_save(session);
b12df0
-                user_save(session->user);
b12df0
                 session_add_to_gc_queue(session);
b12df0
         }
b12df0
 
b12df0
         user = hashmap_get(m->user_units, unit);
b12df0
-        if (user &&
b12df0
-            (streq_ptr(path, user->service_job) ||
b12df0
-             streq_ptr(path, user->slice_job))) {
b12df0
-
b12df0
-                if (streq_ptr(path, user->service_job))
b12df0
+        if (user) {
b12df0
+                if (streq_ptr(path, user->service_job)) {
b12df0
                         user->service_job = mfree(user->service_job);
b12df0
 
b12df0
-                if (streq_ptr(path, user->slice_job))
b12df0
-                        user->slice_job = mfree(user->slice_job);
b12df0
+                        LIST_FOREACH(sessions_by_user, session, user->sessions)
b12df0
+                                (void) session_jobs_reply(session, unit, NULL /* don't propagate user service failures to the client */);
b12df0
 
b12df0
-                LIST_FOREACH(sessions_by_user, session, user->sessions)
b12df0
-                        session_jobs_reply(session, unit, result);
b12df0
+                        user_save(user);
b12df0
+                }
b12df0
 
b12df0
-                user_save(user);
b12df0
                 user_add_to_gc_queue(user);
b12df0
         }
b12df0
 
b12df0
@@ -3315,13 +3310,14 @@ int manager_start_scope(
b12df0
                 pid_t pid,
b12df0
                 const char *slice,
b12df0
                 const char *description,
b12df0
-                const char *after,
b12df0
-                const char *after2,
b12df0
+                char **wants,
b12df0
+                char **after,
b12df0
                 sd_bus_message *more_properties,
b12df0
                 sd_bus_error *error,
b12df0
                 char **job) {
b12df0
 
b12df0
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
b12df0
+        char **i;
b12df0
         int r;
b12df0
 
b12df0
         assert(manager);
b12df0
@@ -3359,14 +3355,14 @@ int manager_start_scope(
b12df0
                         return r;
b12df0
         }
b12df0
 
b12df0
-        if (!isempty(after)) {
b12df0
-                r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after);
b12df0
+        STRV_FOREACH(i, wants) {
b12df0
+                r = sd_bus_message_append(m, "(sv)", "Wants", "as", 1, *i);
b12df0
                 if (r < 0)
b12df0
                         return r;
b12df0
         }
b12df0
 
b12df0
-        if (!isempty(after2)) {
b12df0
-                r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2);
b12df0
+        STRV_FOREACH(i, after) {
b12df0
+                r = sd_bus_message_append(m, "(sv)", "After", "as", 1, *i);
b12df0
                 if (r < 0)
b12df0
                         return r;
b12df0
         }
b12df0
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
b12df0
index 960a24d1a7..d56b48a732 100644
b12df0
--- a/src/login/logind-session.c
b12df0
+++ b/src/login/logind-session.c
b12df0
@@ -27,6 +27,7 @@
b12df0
 #include "path-util.h"
b12df0
 #include "process-util.h"
b12df0
 #include "string-table.h"
b12df0
+#include "strv.h"
b12df0
 #include "terminal-util.h"
b12df0
 #include "user-util.h"
b12df0
 #include "util.h"
b12df0
@@ -560,17 +561,18 @@ int session_activate(Session *s) {
b12df0
         return 0;
b12df0
 }
b12df0
 
b12df0
-static int session_start_scope(Session *s, sd_bus_message *properties) {
b12df0
+static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_error *error) {
b12df0
         int r;
b12df0
 
b12df0
         assert(s);
b12df0
         assert(s->user);
b12df0
 
b12df0
         if (!s->scope) {
b12df0
-                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b12df0
-                char *scope, *job = NULL;
b12df0
+                _cleanup_free_ char *scope = NULL;
b12df0
                 const char *description;
b12df0
 
b12df0
+                s->scope_job = mfree(s->scope_job);
b12df0
+
b12df0
                 scope = strjoin("session-", s->id, ".scope");
b12df0
                 if (!scope)
b12df0
                         return log_oom();
b12df0
@@ -583,21 +585,15 @@ static int session_start_scope(Session *s, sd_bus_message *properties) {
b12df0
                                 s->leader,
b12df0
                                 s->user->slice,
b12df0
                                 description,
b12df0
-                                "systemd-logind.service",
b12df0
-                                "systemd-user-sessions.service",
b12df0
+                                STRV_MAKE(s->user->runtime_dir_service, s->user->service), /* These two have StopWhenUnneeded= set, hence add a dep towards them */
b12df0
+                                STRV_MAKE("systemd-logind.service", "systemd-user-sessions.service", s->user->runtime_dir_service, s->user->service), /* And order us after some more */
b12df0
                                 properties,
b12df0
-                                &error,
b12df0
-                                &job;;
b12df0
-                if (r < 0) {
b12df0
-                        log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r));
b12df0
-                        free(scope);
b12df0
-                        return r;
b12df0
-                } else {
b12df0
-                        s->scope = scope;
b12df0
+                                error,
b12df0
+                                &s->scope_job);
b12df0
+                if (r < 0)
b12df0
+                        return log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(error, r));
b12df0
 
b12df0
-                        free(s->scope_job);
b12df0
-                        s->scope_job = job;
b12df0
-                }
b12df0
+                s->scope = TAKE_PTR(scope);
b12df0
         }
b12df0
 
b12df0
         if (s->scope)
b12df0
@@ -606,7 +602,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties) {
b12df0
         return 0;
b12df0
 }
b12df0
 
b12df0
-int session_start(Session *s, sd_bus_message *properties) {
b12df0
+int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
b12df0
         int r;
b12df0
 
b12df0
         assert(s);
b12df0
@@ -614,6 +610,9 @@ int session_start(Session *s, sd_bus_message *properties) {
b12df0
         if (!s->user)
b12df0
                 return -ESTALE;
b12df0
 
b12df0
+        if (s->stopping)
b12df0
+                return -EINVAL;
b12df0
+
b12df0
         if (s->started)
b12df0
                 return 0;
b12df0
 
b12df0
@@ -621,8 +620,7 @@ int session_start(Session *s, sd_bus_message *properties) {
b12df0
         if (r < 0)
b12df0
                 return r;
b12df0
 
b12df0
-        /* Create cgroup */
b12df0
-        r = session_start_scope(s, properties);
b12df0
+        r = session_start_scope(s, properties, error);
b12df0
         if (r < 0)
b12df0
                 return r;
b12df0
 
b12df0
@@ -673,21 +671,24 @@ static int session_stop_scope(Session *s, bool force) {
b12df0
          * that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log
b12df0
          * when killing any processes left after this point. */
b12df0
         r = manager_abandon_scope(s->manager, s->scope, &error);
b12df0
-        if (r < 0)
b12df0
+        if (r < 0) {
b12df0
                 log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r));
b12df0
+                sd_bus_error_free(&error);
b12df0
+        }
b12df0
+
b12df0
+        s->scope_job = mfree(s->scope_job);
b12df0
 
b12df0
         /* Optionally, let's kill everything that's left now. */
b12df0
         if (force || manager_shall_kill(s->manager, s->user->name)) {
b12df0
-                char *job = NULL;
b12df0
 
b12df0
-                r = manager_stop_unit(s->manager, s->scope, &error, &job;;
b12df0
-                if (r < 0)
b12df0
-                        return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r));
b12df0
+                r = manager_stop_unit(s->manager, s->scope, &error, &s->scope_job);
b12df0
+                if (r < 0) {
b12df0
+                        if (force)
b12df0
+                                return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r));
b12df0
 
b12df0
-                free(s->scope_job);
b12df0
-                s->scope_job = job;
b12df0
+                        log_warning_errno(r, "Failed to stop session scope, ignoring: %s", bus_error_message(&error, r));
b12df0
+                }
b12df0
         } else {
b12df0
-                s->scope_job = mfree(s->scope_job);
b12df0
 
b12df0
                 /* With no killing, this session is allowed to persist in "closing" state indefinitely.
b12df0
                  * Therefore session stop and session removal may be two distinct events.
b12df0
@@ -707,8 +708,17 @@ int session_stop(Session *s, bool force) {
b12df0
 
b12df0
         assert(s);
b12df0
 
b12df0
+        /* This is called whenever we begin with tearing down a session record. It's called in four cases: explicit API
b12df0
+         * request via the bus (either directly for the session object or for the seat or user object this session
b12df0
+         * belongs to; 'force' is true), or due to automatic GC (i.e. scope vanished; 'force' is false), or because the
b12df0
+         * session FIFO saw an EOF ('force' is false), or because the release timer hit ('force' is false). */
b12df0
+
b12df0
         if (!s->user)
b12df0
                 return -ESTALE;
b12df0
+        if (!s->started)
b12df0
+                return 0;
b12df0
+        if (s->stopping)
b12df0
+                return 0;
b12df0
 
b12df0
         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
b12df0
 
b12df0
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
b12df0
index 572f2545c1..7d17d9a25f 100644
b12df0
--- a/src/login/logind-session.h
b12df0
+++ b/src/login/logind-session.h
b12df0
@@ -124,7 +124,7 @@ void session_set_idle_hint(Session *s, bool b);
b12df0
 int session_get_locked_hint(Session *s);
b12df0
 void session_set_locked_hint(Session *s, bool b);
b12df0
 int session_create_fifo(Session *s);
b12df0
-int session_start(Session *s, sd_bus_message *properties);
b12df0
+int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error);
b12df0
 int session_stop(Session *s, bool force);
b12df0
 int session_finalize(Session *s);
b12df0
 int session_release(Session *s);
b12df0
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
b12df0
index 3e4c99bdbd..39fc76f4dc 100644
b12df0
--- a/src/login/logind-user.c
b12df0
+++ b/src/login/logind-user.c
b12df0
@@ -68,6 +68,10 @@ int user_new(User **ret, Manager *m, uid_t uid, gid_t gid, const char *name) {
b12df0
         if (r < 0)
b12df0
                 return r;
b12df0
 
b12df0
+        r = unit_name_build("user-runtime-dir", lu, ".service", &u->runtime_dir_service);
b12df0
+        if (r < 0)
b12df0
+                return r;
b12df0
+
b12df0
         r = hashmap_put(m->users, UID_TO_PTR(uid), u);
b12df0
         if (r < 0)
b12df0
                 return r;
b12df0
@@ -80,6 +84,10 @@ int user_new(User **ret, Manager *m, uid_t uid, gid_t gid, const char *name) {
b12df0
         if (r < 0)
b12df0
                 return r;
b12df0
 
b12df0
+        r = hashmap_put(m->user_units, u->runtime_dir_service, u);
b12df0
+        if (r < 0)
b12df0
+                return r;
b12df0
+
b12df0
         *ret = TAKE_PTR(u);
b12df0
         return 0;
b12df0
 }
b12df0
@@ -97,15 +105,18 @@ User *user_free(User *u) {
b12df0
         if (u->service)
b12df0
                 hashmap_remove_value(u->manager->user_units, u->service, u);
b12df0
 
b12df0
+        if (u->runtime_dir_service)
b12df0
+                hashmap_remove_value(u->manager->user_units, u->runtime_dir_service, u);
b12df0
+
b12df0
         if (u->slice)
b12df0
                 hashmap_remove_value(u->manager->user_units, u->slice, u);
b12df0
 
b12df0
         hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u);
b12df0
 
b12df0
-        u->slice_job = mfree(u->slice_job);
b12df0
         u->service_job = mfree(u->service_job);
b12df0
 
b12df0
         u->service = mfree(u->service);
b12df0
+        u->runtime_dir_service = mfree(u->runtime_dir_service);
b12df0
         u->slice = mfree(u->slice);
b12df0
         u->runtime_path = mfree(u->runtime_path);
b12df0
         u->state_file = mfree(u->state_file);
b12df0
@@ -149,9 +160,6 @@ static int user_save_internal(User *u) {
b12df0
         if (u->service_job)
b12df0
                 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
b12df0
 
b12df0
-        if (u->slice_job)
b12df0
-                fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
b12df0
-
b12df0
         if (u->display)
b12df0
                 fprintf(f, "DISPLAY=%s\n", u->display->id);
b12df0
 
b12df0
@@ -311,66 +319,46 @@ int user_load(User *u) {
b12df0
         return 0;
b12df0
 }
b12df0
 
b12df0
-static int user_start_service(User *u) {
b12df0
+static void user_start_service(User *u) {
b12df0
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b12df0
-        char *job;
b12df0
         int r;
b12df0
 
b12df0
         assert(u);
b12df0
 
b12df0
+        /* Start the service containing the "systemd --user" instance (user@.service). Note that we don't explicitly
b12df0
+         * start the per-user slice or the systemd-runtime-dir@.service instance, as those are pulled in both by
b12df0
+         * user@.service and the session scopes as dependencies. */
b12df0
+
b12df0
         u->service_job = mfree(u->service_job);
b12df0
 
b12df0
-        r = manager_start_unit(
b12df0
-                        u->manager,
b12df0
-                        u->service,
b12df0
-                        &error,
b12df0
-                        &job;;
b12df0
+        r = manager_start_unit(u->manager, u->service, &error, &u->service_job);
b12df0
         if (r < 0)
b12df0
                 /* we don't fail due to this, let's try to continue */
b12df0
                 log_full_errno(sd_bus_error_has_name(&error, BUS_ERROR_UNIT_MASKED) ? LOG_DEBUG : LOG_WARNING, r,
b12df0
                                "Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r));
b12df0
-        else
b12df0
-                u->service_job = job;
b12df0
-
b12df0
-        return 0;
b12df0
 }
b12df0
 
b12df0
 int user_start(User *u) {
b12df0
-        int r;
b12df0
-
b12df0
         assert(u);
b12df0
 
b12df0
         if (u->started && !u->stopping)
b12df0
                 return 0;
b12df0
 
b12df0
-        /*
b12df0
-         * If u->stopping is set, the user is marked for removal and the slice
b12df0
-         * and service stop-jobs are queued. We have to clear that flag before
b12df0
-         * queing the start-jobs again. If they succeed, the user object can be
b12df0
-         * re-used just fine (pid1 takes care of job-ordering and proper
b12df0
-         * restart), but if they fail, we want to force another user_stop() so
b12df0
-         * possibly pending units are stopped.
b12df0
-         * Note that we don't clear u->started, as we have no clue what state
b12df0
-         * the user is in on failure here. Hence, we pretend the user is
b12df0
-         * running so it will be properly taken down by GC. However, we clearly
b12df0
-         * return an error from user_start() in that case, so no further
b12df0
-         * reference to the user is taken.
b12df0
-         */
b12df0
+        /* If u->stopping is set, the user is marked for removal and service stop-jobs are queued. We have to clear
b12df0
+         * that flag before queing the start-jobs again. If they succeed, the user object can be re-used just fine
b12df0
+         * (pid1 takes care of job-ordering and proper restart), but if they fail, we want to force another user_stop()
b12df0
+         * so possibly pending units are stopped. */
b12df0
         u->stopping = false;
b12df0
 
b12df0
         if (!u->started)
b12df0
                 log_debug("Starting services for new user %s.", u->name);
b12df0
 
b12df0
-        /* Save the user data so far, because pam_systemd will read the
b12df0
-         * XDG_RUNTIME_DIR out of it while starting up systemd --user.
b12df0
-         * We need to do user_save_internal() because we have not
b12df0
-         * "officially" started yet. */
b12df0
+        /* Save the user data so far, because pam_systemd will read the XDG_RUNTIME_DIR out of it while starting up
b12df0
+         * systemd --user.  We need to do user_save_internal() because we have not "officially" started yet. */
b12df0
         user_save_internal(u);
b12df0
 
b12df0
-        /* Spawn user systemd */
b12df0
-        r = user_start_service(u);
b12df0
-        if (r < 0)
b12df0
-                return r;
b12df0
+        /* Start user@UID.service */
b12df0
+        user_start_service(u);
b12df0
 
b12df0
         if (!u->started) {
b12df0
                 if (!dual_timestamp_is_set(&u->timestamp))
b12df0
@@ -385,68 +373,50 @@ int user_start(User *u) {
b12df0
         return 0;
b12df0
 }
b12df0
 
b12df0
-static int user_stop_slice(User *u) {
b12df0
+static void user_stop_service(User *u) {
b12df0
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b12df0
-        char *job;
b12df0
         int r;
b12df0
 
b12df0
         assert(u);
b12df0
+        assert(u->service);
b12df0
 
b12df0
-        r = manager_stop_unit(u->manager, u->slice, &error, &job;;
b12df0
-        if (r < 0) {
b12df0
-                log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
b12df0
-                return r;
b12df0
-        }
b12df0
+        /* The reverse of user_start_service(). Note that we only stop user@UID.service here, and let StopWhenUnneeded=
b12df0
+         * deal with the slice and the user-runtime-dir@.service instance. */
b12df0
 
b12df0
-        free(u->slice_job);
b12df0
-        u->slice_job = job;
b12df0
-
b12df0
-        return r;
b12df0
-}
b12df0
-
b12df0
-static int user_stop_service(User *u) {
b12df0
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b12df0
-        char *job;
b12df0
-        int r;
b12df0
-
b12df0
-        assert(u);
b12df0
-
b12df0
-        r = manager_stop_unit(u->manager, u->service, &error, &job;;
b12df0
-        if (r < 0) {
b12df0
-                log_error("Failed to stop user service: %s", bus_error_message(&error, r));
b12df0
-                return r;
b12df0
-        }
b12df0
+        u->service_job = mfree(u->service_job);
b12df0
 
b12df0
-        free_and_replace(u->service_job, job);
b12df0
-        return r;
b12df0
+        r = manager_stop_unit(u->manager, u->service, &error, &u->service_job);
b12df0
+        if (r < 0)
b12df0
+                log_warning_errno(r, "Failed to stop user service '%s', ignoring: %s", u->service, bus_error_message(&error, r));
b12df0
 }
b12df0
 
b12df0
 int user_stop(User *u, bool force) {
b12df0
         Session *s;
b12df0
-        int r = 0, k;
b12df0
+        int r = 0;
b12df0
         assert(u);
b12df0
 
b12df0
-        /* Stop jobs have already been queued */
b12df0
-        if (u->stopping) {
b12df0
+        /* This is called whenever we begin with tearing down a user record. It's called in two cases: explicit API
b12df0
+         * request to do so via the bus (in which case 'force' is true) and automatically due to GC, if there's no
b12df0
+         * session left pinning it (in which case 'force' is false). Note that this just initiates tearing down of the
b12df0
+         * user, the User object will remain in memory until user_finalize() is called, see below. */
b12df0
+
b12df0
+        if (!u->started)
b12df0
+                return 0;
b12df0
+
b12df0
+        if (u->stopping) { /* Stop jobs have already been queued */
b12df0
                 user_save(u);
b12df0
-                return r;
b12df0
+                return 0;
b12df0
         }
b12df0
 
b12df0
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
b12df0
+                int k;
b12df0
+
b12df0
                 k = session_stop(s, force);
b12df0
                 if (k < 0)
b12df0
                         r = k;
b12df0
         }
b12df0
 
b12df0
-        /* Kill systemd */
b12df0
-        k = user_stop_service(u);
b12df0
-        if (k < 0)
b12df0
-                r = k;
b12df0
-
b12df0
-        /* Kill cgroup */
b12df0
-        k = user_stop_slice(u);
b12df0
-        if (k < 0)
b12df0
-                r = k;
b12df0
+        user_stop_service(u);
b12df0
 
b12df0
         u->stopping = true;
b12df0
 
b12df0
@@ -461,6 +431,9 @@ int user_finalize(User *u) {
b12df0
 
b12df0
         assert(u);
b12df0
 
b12df0
+        /* Called when the user is really ready to be freed, i.e. when all unit stop jobs and suchlike for it are
b12df0
+         * done. This is called as a result of an earlier user_done() when all jobs are completed. */
b12df0
+
b12df0
         if (u->started)
b12df0
                 log_debug("User %s logged out.", u->name);
b12df0
 
b12df0
@@ -554,9 +527,6 @@ bool user_may_gc(User *u, bool drop_not_started) {
b12df0
         if (user_check_linger_file(u) > 0)
b12df0
                 return false;
b12df0
 
b12df0
-        if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
b12df0
-                return false;
b12df0
-
b12df0
         if (u->service_job && manager_job_is_active(u->manager, u->service_job))
b12df0
                 return false;
b12df0
 
b12df0
@@ -581,7 +551,7 @@ UserState user_get_state(User *u) {
b12df0
         if (u->stopping)
b12df0
                 return USER_CLOSING;
b12df0
 
b12df0
-        if (!u->started || u->slice_job || u->service_job)
b12df0
+        if (!u->started || u->service_job)
b12df0
                 return USER_OPENING;
b12df0
 
b12df0
         if (u->sessions) {
b12df0
diff --git a/src/login/logind-user.h b/src/login/logind-user.h
b12df0
index 03e020b870..5e1f7b813a 100644
b12df0
--- a/src/login/logind-user.h
b12df0
+++ b/src/login/logind-user.h
b12df0
@@ -25,11 +25,12 @@ struct User {
b12df0
         char *name;
b12df0
         char *state_file;
b12df0
         char *runtime_path;
b12df0
-        char *slice;
b12df0
-        char *service;
b12df0
+
b12df0
+        char *slice;                     /* user-UID.slice */
b12df0
+        char *service;                   /* user@UID.service */
b12df0
+        char *runtime_dir_service;       /* user-runtime-dir@UID.service */
b12df0
 
b12df0
         char *service_job;
b12df0
-        char *slice_job;
b12df0
 
b12df0
         Session *display;
b12df0
 
b12df0
diff --git a/src/login/logind.c b/src/login/logind.c
b12df0
index 1b366cd55f..6c208c8e89 100644
b12df0
--- a/src/login/logind.c
b12df0
+++ b/src/login/logind.c
b12df0
@@ -1158,7 +1158,7 @@ static int manager_startup(Manager *m) {
b12df0
                 user_start(user);
b12df0
 
b12df0
         HASHMAP_FOREACH(session, m->sessions, i)
b12df0
-                session_start(session, NULL);
b12df0
+                (void) session_start(session, NULL, NULL);
b12df0
 
b12df0
         HASHMAP_FOREACH(inhibitor, m->inhibitors, i)
b12df0
                 inhibitor_start(inhibitor);
b12df0
diff --git a/src/login/logind.h b/src/login/logind.h
b12df0
index a6ebc9e152..ae4d74076b 100644
b12df0
--- a/src/login/logind.h
b12df0
+++ b/src/login/logind.h
b12df0
@@ -161,7 +161,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name
b12df0
 
b12df0
 int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
b12df0
 
b12df0
-int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_message *more_properties, sd_bus_error *error, char **job);
b12df0
+int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, char **wants, char **after, sd_bus_message *more_properties, sd_bus_error *error, char **job);
b12df0
 int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
b12df0
 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
b12df0
 int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error);