login: fix re-use of users

If the last reference to a user is released, we queue stop-jobs for the
user-service and slice. Only once those are finished, we drop the
user-object. However, if a new session is opened before the user object is
fully dropped, we currently incorrectly re-use the object. This has the
effect, that we get stale sessions without a valid "systemd --user"
instance.

Fix this by properly allowing user_start() to be called, even if
user->stopping is true.
This commit is contained in:
David Herrmann
2015-11-16 15:34:41 +01:00
parent 090d92bd6f
commit a832ab6f99
+29 -12
View File
@@ -452,15 +452,32 @@ int user_start(User *u) {
assert(u);
if (u->started)
if (u->started && !u->stopping)
return 0;
log_debug("New user %s logged in.", u->name);
/*
* If u->stopping is set, the user is marked for removal and the slice
* and service stop-jobs are queued. We have to clear that flag before
* queing the start-jobs again. If they succeed, the user object can be
* re-used just fine (pid1 takes care of job-ordering and proper
* restart), but if they fail, we want to force another user_stop() so
* possibly pending units are stopped.
* Note that we don't clear u->started, as we have no clue what state
* the user is in on failure here. Hence, we pretend the user is
* running so it will be properly taken down by GC. However, we clearly
* return an error from user_start() in that case, so no further
* reference to the user is taken.
*/
u->stopping = false;
/* Make XDG_RUNTIME_DIR */
r = user_mkdir_runtime_path(u);
if (r < 0)
return r;
if (!u->started) {
log_debug("New user %s logged in.", u->name);
/* Make XDG_RUNTIME_DIR */
r = user_mkdir_runtime_path(u);
if (r < 0)
return r;
}
/* Create cgroup */
r = user_start_slice(u);
@@ -478,16 +495,16 @@ int user_start(User *u) {
if (r < 0)
return r;
if (!dual_timestamp_is_set(&u->timestamp))
dual_timestamp_get(&u->timestamp);
u->started = true;
if (!u->started) {
if (!dual_timestamp_is_set(&u->timestamp))
dual_timestamp_get(&u->timestamp);
user_send_signal(u, true);
u->started = true;
}
/* Save new user data */
user_save(u);
user_send_signal(u, true);
return 0;
}