记录一下排查 qt.qpa.xcb 错误的全过程

背景

Qt 的位置信息库 QtPositioning 中获取位置信息的输入源设置方法为:

QGeoPositionInfoSource::createDefaultSource()

该方法默认选择当前系统的位置信息提供服务获取经纬度坐标等。
我的系统是树莓派 Raspbian,因此安装了 geoclue2 作为位置服务,用上述方法创建位置信息源后开始获取树莓派的经纬度,再用 GeocodeModel 从 OpenStreetMap 处查询经纬度对应的实际地址。

以上是自研发软件的一些背景。

起因

在开发环境中调通了地址显示的功能后,编译部署到树莓派上,发现数据源一直是 Null 。但是 geoclue2 很确定是正常工作的,不管是示例程序还是开发环境都明确显示位置源没有问题,但就是在实际程序内不显示位置。

开 qDebug() 打印,supportedPositioningMethods 是正确的。终端给出的是这么一行提示:

libEGL warning: DRI2: failed to authenticate
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'
qt.positioning.geoclue2: Unable to start the client: "org.freedesktop.DBus.Error.AccessDenied" "An authorization request is already pending"
qml: Source error: 0
qt.positioning.geoclue2: Unable to start the client: "org.freedesktop.DBus.Error.AccessDenied" "'[myapp]' disallowed, no agent for UID 0"

很显然,qt.positioning.geoclue2 报错那两行跟获取不到位置信息的错误高度相关,于是理所当然的从这里下手了。

排错

一番操作之后,可耻地发现啥也没查出来。因为 qtpositioning 对 geoclue2 的 DBus 信息获取高度过程封装,根本没有啥可以设置的地方,除非我要去 QtPositioning 库里查源码,不然除了给 Qt 提 issue 以外我根本没啥可以做的。但因为开发环境下使用是正常的,因此这个也不太可能是 QtPositionging 的 bug.

由于实际生产环境需要把程序部署到树莓派,而树莓派不方便插显示器键盘,因此理所当然是通过 ssh 连过去调试的。Qt 程序理所当然是个 GUI 程序,因此需要从把本地的 Xorg 服务连到树莓派上,通过 DISPLAY 这个环境变量来选择本地窗口显示还是远程界面显示。

这种情况下自然要从远程 ssh 终端启动 GUI 程序并查看 stdout 信息。可是在我如往常般 ssh -Y 登入并尝试启动程序后,得到了如下报错:

X11 connection rejected because of wrong authentication. 
qt.qpa.xcb: could not connect to display localhost:10.0 
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found. 
This application failed to start because no Qt platform plugin could be initialized. 
Reinstalling the application may fix this problem.

Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, xcb.

Aborted

相信经常需要 ssh -Y 的朋友对这条报错一定不陌生,任何尝试过远程连 Xorg 的人不敢说 100% 见过这个报错吧,99%估计是少不了的。因此我非常淡定,检查遍本机的 xorg 服务、检查防火墙、检查 DISPLAY 环境变量、ldd libqxcb.so 检查依赖。
……
全是正常的。直接跑 xclock 也是正常的,本地和远程皆是。

转机

到这里我已经有点烦躁了,问了一圈谁动过树莓派系统或改过设置,都说没有。

sudo apt update && sudo apt upgrade -y梭了一把更新,问题依旧。找 Google,stackoverflow、github,各种说法都有,都尝试了一遍也没啥收获。毫无进展地试错几小时后,我回到最开始的那行报错信息:

QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'

export -p 了一下,XDG_RUNTIME_DIR好好地挂在那儿,嗯?
我干了啥会导致环境变量不生效的?
莫非,是 sudo ? 是了,我跑 xclock 是不需要 sudo 的,所以能正常开,而我自己的程序因为要调用串口读写,因此是需要 sudo 运行的。

sudo visudo打开设置页,第一行赫然写着
Defaults env_reset
没跑了就你了。

改成Defaults !env_reset保存,运行,正常连接!
同时更神奇的是,之前一直 Null 的位置信息也出现了…

我一脸懵逼的看着

qt.positioning.geoclue2: Unable to start the client: "org.freedesktop.DBus.Error.AccessDenied" "An authorization request is already pending"
qml: Source error: 0
qt.positioning.geoclue2: Unable to start the client: "org.freedesktop.DBus.Error.AccessDenied" "'myapp' disallowed, no agent for UID 0"

这两行报错依然跳了出来却正常出现的位置信息,满脑子的MMP不知当讲不当讲。

心得

总之,时刻警惕 sudo 改你的环境变量

End