From 865e4500f9d7ada353d39b6b682325377e85f16f Mon Sep 17 00:00:00 2001 From: deepin-wm Date: Thu, 18 Jun 2026 20:05:08 +0800 Subject: [PATCH] fix(session): force-destroy sandbox surfaces on session removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add sessionForClient() that uses uid matching for sandbox clients and socket chain walking for normal clients 2. Force-destroy all surfaces belonging to removed session in removeSession() to clean up sandbox apps that ignore close 3. Fix surfaceBelongsToCurrentSession() to use sessionForClient() instead of rootSocket-based check which fails for sandbox apps Log: Fixed sandbox app windows remaining visible after logout Influence: 1. Test sandbox apps (linglong) are closed on logout and re-login 2. Verify system UI (Dock, launcher) is not affected on logout 3. Test multi-user switching with sandbox apps fix(session): 会话移除时强制销毁沙盒应用窗口 1. 新增 sessionForClient() 方法,对沙盒客户端使用 uid 匹配,对普通客户端使用 socket 父链遍历 2. 在 removeSession() 中强制销毁属于该会话的所有表面, 清理忽略关闭请求的沙盒应用 3. 修复 surfaceBelongsToCurrentSession(),使用 sessionForClient() 替代基于 rootSocket 的判断 Log: 修复注销后沙盒应用窗口残留的问题 Influence: 1. 测试沙盒应用(玲珑应用商店)注销后重新登录窗口是否关闭 2. 验证系统 UI(任务栏、启动器)注销时不受影响 3. 测试多用户切换场景下沙盒应用的隔离性 Fixes: #347759 --- src/seat/helper.cpp | 16 +++++++++-- src/session/session.cpp | 61 +++++++++++++++++++++++++++++++++++++++++ src/session/session.h | 1 + 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/seat/helper.cpp b/src/seat/helper.cpp index 369aed990..5d86e3abb 100644 --- a/src/seat/helper.cpp +++ b/src/seat/helper.cpp @@ -1282,8 +1282,20 @@ bool Helper::surfaceBelongsToCurrentSession(SurfaceWrapper *wrapper) return true; } WClient *client = wrapper->surface()->waylandClient(); - WSocket *socket = client ? client->socket()->rootSocket() : nullptr; - return socket && socket->isEnabled(); + if (!client) + return false; + + auto session = m_sessionManager->sessionForClient(client); + // If session lookup fails, conservatively treat as not belonging to current + // session rather than falling back to rootSocket check (which would + // incorrectly classify sandbox apps as belonging to the current session). + if (!session) + return false; + // Global session surfaces (Dock, launcher) are always visible + if (session == m_sessionManager->globalSession()) + return true; + // User session surfaces are visible only when their session is active + return m_sessionManager->activeSession().lock() == session; } void Helper::deleteTaskSwitch() diff --git a/src/session/session.cpp b/src/session/session.cpp index 151748550..afcf3990d 100644 --- a/src/session/session.cpp +++ b/src/session/session.cpp @@ -199,6 +199,22 @@ void SessionManager::removeSession(std::shared_ptr session) Helper::instance()->activateSurface(nullptr); } + // Force-destroy all surfaces belonging to this session so that sandbox + // apps (which bypass the closeSurface request) are cleaned up on logout. + // Collect first to avoid mutating the surface list during iteration. + auto *container = Helper::instance()->rootSurfaceContainer(); + QList toDestroy; + for (auto *wrapper : std::as_const(container->surfaces())) { + WClient *client = wrapper->surface()->waylandClient(); + if (!client) + continue; + auto wrapperSession = sessionForClient(client); + if (wrapperSession == session) + toDestroy.append(wrapper); + } + for (auto *wrapper : std::as_const(toDestroy)) + container->destroyForSurface(wrapper); + for (auto s : std::as_const(m_sessions)) { if (s.get() == session.get()) { m_sessions.removeOne(s); @@ -436,6 +452,51 @@ std::shared_ptr SessionManager::sessionForSocket(WSocket *socket) const return nullptr; } +/** + * Find the session for the given WClient + * + * For sandbox clients (socket has parentSocket), uid matching is used first + * because their socket chain traverses the global socket rather than a user + * session socket. Falls back to walking the socket parent chain for normal + * and system UI clients. + * + * @param client WClient to find session for + * @returns Session for the given client, or nullptr if not found + */ +std::shared_ptr SessionManager::sessionForClient(WClient *client) const +{ + if (!client) + return nullptr; + + auto global = globalSession(); + WSocket *socket = client->socket(); + + // Sandbox clients connect via a child socket whose parent chain goes + // through the global socket, so socket-based lookup would always find + // the global session. Use uid matching instead to find the real user session. + // The uid comes from Wayland client credentials (wl_client_get_credentials), + // which reflects the OS-level uid of the process that opened the Wayland + // connection — for sandbox apps this is the user who launched them, even + // though their socket chain only reaches the global socket. + if (socket && socket->parentSocket()) { + auto creds = client->credentials(); + if (creds) { + for (const auto &session : std::as_const(m_sessions)) { + if (session && session != global && session->uid() == creds->uid) + return session; + } + } + } + + // Walk socket parent chain for normal and system UI clients + while (socket) { + if (auto session = sessionForSocket(socket)) + return session; + socket = socket->parentSocket(); + } + return nullptr; +} + bool SessionManager::isDDEUserClient(WClient *client) { return client->socket() == globalSession()->socket(); diff --git a/src/session/session.h b/src/session/session.h index 73daf8bc9..2d6babbe8 100644 --- a/src/session/session.h +++ b/src/session/session.h @@ -69,6 +69,7 @@ class SessionManager : public QObject { std::shared_ptr sessionForUser(const QString &username) const; std::shared_ptr sessionForXWayland(WXWayland *xwayland) const; std::shared_ptr sessionForSocket(WSocket *socket) const; + std::shared_ptr sessionForClient(WClient *client) const; bool isDDEUserClient(WClient *client); void syncActiveSessionCursorSettings();