chiark / gitweb /
logind: make sure to terminate systemd user on logouts
authorDjalal Harouni <tixxdz@opendz.org>
Thu, 13 Feb 2014 17:31:43 +0000 (18:31 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 13 Feb 2014 20:07:13 +0000 (21:07 +0100)
Currently if the user logs out, the GC may never call user_stop(),
this will not terminate the systemd user and (sd-pam) of that user.

To fix this, remove the USER_CLOSING state check that is blocking the
GC from calling user_stop(). Since if user_check_gc() returns false
this means that all the sessions of the user were removed which will
make user_get_state() return USER_CLOSING.

Conclusion: that test will never be statisfied.

So we remove the USER_CLOSING check and replace it with a check inside
user_stop() this way we know that user_stop() has already queued stop
jobs, no need to redo.

This ensures that the GC will get its two steps correctly as pointed out
by Lennart:
http://lists.freedesktop.org/archives/systemd-devel/2014-February/016825.html

Note: this also fixes another bug that prevents creating the user
private dbus socket which will break communications with the user
manager.

src/login/logind-user.c
src/login/logind.c

index ac4a651f367323d8b876c2afb7c831dc35f9ba3c..4af0e90c2232d36d9c790085151d5765166eb09f 100644 (file)
@@ -499,6 +499,12 @@ int user_stop(User *u, bool force) {
         int r = 0, k;
         assert(u);
 
         int r = 0, k;
         assert(u);
 
+        /* Stop jobs have already been queued */
+        if (u->stopping) {
+                user_save(u);
+                return r;
+        }
+
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
                 k = session_stop(s, force);
                 if (k < 0)
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
                 k = session_stop(s, force);
                 if (k < 0)
index 554409926a120b0b6345c36b4861c874f159d83b..2add2410664102c1391991c71880a781266431b4 100644 (file)
@@ -889,10 +889,11 @@ void manager_gc(Manager *m, bool drop_not_started) {
                 LIST_REMOVE(gc_queue, m->user_gc_queue, user);
                 user->in_gc_queue = false;
 
                 LIST_REMOVE(gc_queue, m->user_gc_queue, user);
                 user->in_gc_queue = false;
 
-                if (!user_check_gc(user, drop_not_started) &&
-                    user_get_state(user) != USER_CLOSING)
+                /* First step: queue stop jobs */
+                if (!user_check_gc(user, drop_not_started))
                         user_stop(user, false);
 
                         user_stop(user, false);
 
+                /* Second step: finalize user */
                 if (!user_check_gc(user, drop_not_started)) {
                         user_finalize(user);
                         user_free(user);
                 if (!user_check_gc(user, drop_not_started)) {
                         user_finalize(user);
                         user_free(user);