From 4ced73553ff2b2e7058ca86a3a9424dbe418dc0b Mon Sep 17 00:00:00 2001 From: fuleyi Date: Wed, 24 Jun 2026 14:41:57 +0800 Subject: [PATCH] fix: handle X server disconnection gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When X server is killed (e.g., during init 3 to init 5 switch), libX11's default I/O error handler calls exit(), bypassing Qt's normal shutdown. This causes plugins to be destroyed during global destructors when objects are already partially destroyed, leading to SIGSEGV. Changes made: 1. Added X11 package dependency to CMakeLists.txt for Xlib headers and linking 2. Implemented custom X I/O error handler that calls QCoreApplication::quit() instead of exit() 3. Added SIGPIPE signal handler to prevent crashes when X pipe is broken 4. Handlers are installed only on xcb platform, not needed on Wayland This ensures plugins receive aboutToQuit signal and can clean up properly. Log: Fixed crash when X server disconnects by installing custom error handlers Influence: 1. Test graceful shutdown when X server is terminated (init 3 to init 5 switch) 2. Verify no SIGSEGV occurs during X server disconnection 3. Confirm application exits cleanly with proper plugin cleanup 4. Verify fix does not affect Wayland sessions 5. Test that aboutToQuit signal fires correctly for plugin cleanup fix: 优雅处理X服务器断开连接 当X服务器被终止时(例如从init 3切换到init 5),libX11的默认I/O错误处理器 会调用exit(),绕过Qt的正常关闭流程。这导致插件在全局析构函数中被销毁,此 时对象已部分销毁,从而引发SIGSEGV崩溃。 修改内容: 1. 在CMakeLists.txt中添加X11包依赖,引入Xlib头文件和链接库 2. 实现自定义X I/O错误处理器,调用QCoreApplication::quit()替代exit() 3. 添加SIGPIPE信号处理器,防止X管道断开时崩溃 4. 仅在xcb平台安装处理器,Wayland不需要 这确保插件能收到aboutToQuit信号并正确清理资源。 Log: 通过安装自定义错误处理器修复X服务器断开时的崩溃问题 Influence: 1. 测试X服务器终止时的优雅关闭(init 3到init 5切换) 2. 验证X服务器断开时不会出现SIGSEGV崩溃 3. 确认应用程序能正常退出并正确清理插件 4. 验证修复不影响Wayland会话 5. 测试aboutToQuit信号是否正确触发以清理插件 PMS: BUG-363347 --- shell/CMakeLists.txt | 2 ++ shell/main.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt index c201baa5b..3d3bc3be2 100644 --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -4,6 +4,7 @@ find_package(Dtk${DTK_VERSION_MAJOR} COMPONENTS Widget REQUIRED) find_package(Qt${QT_VERSION_MAJOR} ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Widgets Gui WaylandClient) +pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11) find_package(TreelandProtocols REQUIRED) pkg_check_modules(WaylandClient REQUIRED IMPORTED_TARGET wayland-client) @@ -46,6 +47,7 @@ target_link_libraries(dde-shell PRIVATE Dtk${DTK_VERSION_MAJOR}::Widget Qt${QT_VERSION_MAJOR}::DBus PkgConfig::WaylandClient + PkgConfig::X11 ) configure_file(${CMAKE_SOURCE_DIR}/misc/dde-shell-plugin@.service.in diff --git a/shell/main.cpp b/shell/main.cpp index eebd0069c..f32283d07 100644 --- a/shell/main.cpp +++ b/shell/main.cpp @@ -16,6 +16,8 @@ #include #include +#include + #include "applet.h" #include "containment.h" #include "pluginloader.h" @@ -46,6 +48,28 @@ static void disableLogOutput() QLoggingCategory::setFilterRules("*.debug=false"); } +// Handle X server disconnection gracefully. +// When X server is killed (e.g. init 3), libX11's _XDefaultIOError calls exit(), +// bypassing Qt's normal shutdown. This causes plugins to be destroyed during global +// destructors when objects are already partially destroyed, leading to SIGSEGV. +// +// By installing a custom X I/O error handler, we prevent exit() from being called +// and instead trigger a clean Qt shutdown via quit(), which fires aboutToQuit +// and allows plugins to be properly destroyed. +static int xio_error_handler(Display * /*dpy*/) +{ + qCCritical(dsLog) << "X I/O error detected, shutting down gracefully."; + QCoreApplication::quit(); + return 0; +} + +static void sigpipe_handler(int /*sig*/) +{ + // 信号处理函数中禁止调用非异步信号安全函数(如 qCCritical), + // 否则在持有相关锁时触发信号会导致死锁。 + QCoreApplication::quit(); +} + class AppletManager { public: @@ -218,5 +242,19 @@ int main(int argc, char *argv[]) manager.quit(); }); + // Install X11-specific error handlers to prevent exit() from being called + // when X server is killed (e.g. during init 3 → init 5 switch). + // On Wayland, these are not needed as the error path is different. + if (QGuiApplication::platformName().startsWith(QStringLiteral("xcb"))) { + XSetIOErrorHandler(xio_error_handler); + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigpipe_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGPIPE, &sa, nullptr); + } + return a.exec(); }