c28716f
From 33f835ec7c74629d4a6982c3b75630af2cd5d65b Mon Sep 17 00:00:00 2001
c28716f
From: Simon McVittie <simon.mcvittie@collabora.co.uk>
c28716f
Date: Wed, 17 Jun 2015 16:45:49 +0100
c28716f
Subject: [PATCH] logind: save /run/systemd/users/UID before starting
c28716f
 user@.service
c28716f
c28716f
Previously, this had a race condition during a user's first login.
c28716f
Some component calls CreateSession (most likely by a PAM service
c28716f
other than 'systemd-user' running pam_systemd), with the following
c28716f
results:
c28716f
c28716f
- logind:
c28716f
  * create the user's XDG_RUNTIME_DIR
c28716f
  * tell pid 1 to create user-UID.slice
c28716f
  * tell pid 1 to start user@UID.service
c28716f
c28716f
Then these two processes race:
c28716f
c28716f
- logind:
c28716f
  * save information including XDG_RUNTIME_DIR to /run/systemd/users/UID
c28716f
c28716f
- the subprocess of pid 1 responsible for user@service:
c28716f
  * start a 'systemd-user' PAM session, which reads XDG_RUNTIME_DIR
c28716f
    and puts it in the environment
c28716f
  * run systemd --user, which requires XDG_RUNTIME_DIR in the
c28716f
    environment
c28716f
c28716f
If logind wins the race, which usually happens, everything is fine;
c28716f
but if the subprocesses of pid 1 win the race, which can happen
c28716f
under load, then systemd --user exits unsuccessfully.
c28716f
c28716f
To avoid this race, we have to write out /run/systemd/users/UID
c28716f
even though the service has not "officially" started yet;
c28716f
previously this did an early-return without saving anything.
c28716f
Record its state as OPENING in this case.
c28716f
c28716f
Bug: https://github.com/systemd/systemd/issues/232
c28716f
Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
c28716f
(cherry picked from commit 71161305f191d1fe1242ccca47657f9ab51caad4)
c28716f
---
c28716f
 src/login/logind-user.c | 22 +++++++++++++++++-----
c28716f
 1 file changed, 17 insertions(+), 5 deletions(-)
c28716f
c28716f
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
c28716f
index f4c4490e8f..21acf2afe3 100644
c28716f
--- a/src/login/logind-user.c
c28716f
+++ b/src/login/logind-user.c
c28716f
@@ -104,7 +104,7 @@ void user_free(User *u) {
c28716f
         free(u);
c28716f
 }
c28716f
 
c28716f
-int user_save(User *u) {
c28716f
+static int user_save_internal(User *u) {
c28716f
         _cleanup_free_ char *temp_path = NULL;
c28716f
         _cleanup_fclose_ FILE *f = NULL;
c28716f
         int r;
c28716f
@@ -112,9 +112,6 @@ int user_save(User *u) {
c28716f
         assert(u);
c28716f
         assert(u->state_file);
c28716f
 
c28716f
-        if (!u->started)
c28716f
-                return 0;
c28716f
-
c28716f
         r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
c28716f
         if (r < 0)
c28716f
                 goto finish;
c28716f
@@ -257,6 +254,15 @@ finish:
c28716f
         return r;
c28716f
 }
c28716f
 
c28716f
+int user_save(User *u) {
c28716f
+        assert(u);
c28716f
+
c28716f
+        if (!u->started)
c28716f
+                return 0;
c28716f
+
c28716f
+        return user_save_internal (u);
c28716f
+}
c28716f
+
c28716f
 int user_load(User *u) {
c28716f
         _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
c28716f
         Session *s = NULL;
c28716f
@@ -452,6 +458,12 @@ int user_start(User *u) {
c28716f
         if (r < 0)
c28716f
                 return r;
c28716f
 
c28716f
+        /* Save the user data so far, because pam_systemd will read the
c28716f
+         * XDG_RUNTIME_DIR out of it while starting up systemd --user.
c28716f
+         * We need to do user_save_internal() because we have not
c28716f
+         * "officially" started yet. */
c28716f
+        user_save_internal(u);
c28716f
+
c28716f
         /* Spawn user systemd */
c28716f
         r = user_start_service(u);
c28716f
         if (r < 0)
c28716f
@@ -703,7 +715,7 @@ UserState user_get_state(User *u) {
c28716f
         if (u->stopping)
c28716f
                 return USER_CLOSING;
c28716f
 
c28716f
-        if (u->slice_job || u->service_job)
c28716f
+        if (!u->started || u->slice_job || u->service_job)
c28716f
                 return USER_OPENING;
c28716f
 
c28716f
         if (u->sessions) {