Linux 桌面有时候需要容器开 GUi,并且和系统深度集成(如:调用宿主输入法、桌面系统通知),需要给容器传这些文件(其中 -E 为环境变量,--bind 是挂载):
-E DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$UID/bus"
--bind="$XDG_RUNTIME_DIR"
但有个问题,文件选择器也会调用宿主的,没办法找容器自己的文件。
我使用的是 xdg-dbus-proxy 去解决。
# 1. 在 XDG_RUNTIME_DIR 下生成一个代理 socket 路径
PROXY_SOCKET="$XDG_RUNTIME_DIR/nspawn-bus-proxy-$"
# 2. 启动 xdg-dbus-proxy,开启过滤:只放行 fcitx 相关的通信,拦截其他所有服务(如 Portal)
xdg-dbus-proxy "unix:path=/run/user/$uid/bus" "$PROXY_SOCKET" \
--filter \
--own=org.fcitx.* \
--talk=org.fcitx.* &
PROXY_PID=$!
# 3. 确保脚本退出(容器关闭)时,自动清理后台的代理进程和 socket
trap "kill $PROXY_PID; rm -f $PROXY_SOCKET" EXIT
# 传递的环境变量(这里是 systemd-nspawn 的参数)
-E DBUS_SESSION_BUS_ADDRESS="unix:path=$PROXY_SOCKET" \
我使用的是 systemd-nspawn 容器,完整的启动脚本:
#!/bin/bash
xhost +local:
uid=1000
gid=1000
# 1. 在 XDG_RUNTIME_DIR 下生成一个代理 socket 路径
PROXY_SOCKET="$XDG_RUNTIME_DIR/nspawn-bus-proxy-$"
# 2. 启动 xdg-dbus-proxy,开启过滤:只放行 fcitx 相关的通信,拦截其他所有服务(如 Portal)
xdg-dbus-proxy "unix:path=/run/user/$uid/bus" "$PROXY_SOCKET" \
--filter \
--own=org.fcitx.* \
--talk=org.fcitx.* &
PROXY_PID=$!
# 3. 确保脚本退出(容器关闭)时,自动清理后台的代理进程和 socket
trap "kill $PROXY_PID; rm -f $PROXY_SOCKET" EXIT
# 4. 启动容器,将容器的 DBUS 地址指向这个“被过滤的”代理 Socket
sudo systemd-nspawn \
--hostname="$1" \
--directory="$2" \
-E DISPLAY="$DISPLAY" \
-E LANG="zh_CN.UTF-8" \
-E PULSE_SERVER="unix:/run/user/$uid/pulse/native" \
-E DBUS_SESSION_BUS_ADDRESS="unix:path=$PROXY_SOCKET" \
-E XMODIFIERS="@im=fcitx" \
-E QT_IM_MODULE="fcitx" \
-E GTK_IM_MODULE="fcitx" \
-E INPUT_METHOD="fcitx" \
-E HSA_OVERRIDE_GFX_VERSION="11.5.1" \
-E GLFW_IM_MODULE="fcitx" \
-E SDL_IM_MODULE="fcitx" \
-E XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" \
-E WAYLAND_DISPLAY=$WAYLAND_DISPLAY \
-E GTK_USE_PORTAL=0 \
--property=AppArmorProfile=unconfined \
--capability="all" \
--bind-ro="/run/dbus/system_bus_socket" \
--bind-ro="/dev/kfd" \
--bind-ro="/dev/dri" \
--bind-ro="/run/udev" \
--bind="/dev/shm" \
--bind-ro="/dev/input" \
--bind="$XDG_RUNTIME_DIR" \
--bind-ro="/etc/resolv.conf" \
-M "$1"