htop 鼠标失效、vim 行错乱:一次 sshpass 引发的终端故障排查
问题描述
在 Windows Terminal 通过 SSH 连接到一台 openEuler 22.03 ARM 开发板(RK3528)时,出现两个异常:
htop运行后鼠标点击无响应或乱跳,无法正常操作vim打开文件编辑时段落错乱、行跳位,无法正常使用
但在同一台机器上用 Xshell 8 连接,htop 和 vim 完全正常。
另外用 Windows Terminal 连接另一台配置完全相同的 openEuler 服务器,也完全正常。
环境信息
- 问题机器:openEuler 22.03 LTS-SP4,aarch64,自定义 RK35xx 内核
- 正常机器:openEuler 22.03 LTS-SP4,aarch64,标准 openEuler 内核
- 客户端:Windows Terminal(内置 OpenSSH 客户端)
排查过程
第一步:对比 TERM 环境变量
最直观的怀疑是终端类型不匹配。对比两个客户端的环境变量:
- Windows Terminal 连接时:
TERM=xterm-256color - Xshell 8 连接时:
TERM=xterm
初步怀疑是 TERM=xterm-256color 导致问题,建议在服务端 .bashrc 中强制覆盖:
1 | |
但Windows Terminal 连接另一台相同系统的机器时,TERM 同样是 xterm-256color,htop 却完全正常。因此排除 TERM 变量的影响。
第二步:全面对比两台机器的软件栈
在两台机器上分别执行以下检查:
1 | |
结果:两台机器的 htop 版本(3.1.2)、ncurses 版本(6.3-15)、terminfo 数据库、htoprc 配置完全一致,连二进制的 MD5 校验值都相同。软件层面无差异。
第三步:排查 PROMPT_COMMAND 与 OSC 转义序列
深入对比两台机器的 Shell 初始化脚本,发现:
正常机器有 /etc/profile.d/hiscmd.sh,在 /etc/profile 阶段提前设置:
1 | |
问题机器没有该脚本,/etc/bashrc 检测到 PROMPT_COMMAND 为空后设置:
1 | |
问题机器每次显示提示符都会向终端发送 OSC 标题序列 \033]0;...\007,而正常机器不会输出任何终端控制序列。
怀疑这条序列破坏了 Windows Terminal 的 VT 状态机,导致后续鼠标跟踪序列被错误解析。
验证:在连接后手动执行:
1 | |
结果:无效,问题依然存在。 排除 PROMPT_COMMAND 的影响。
第四步:发现 OpenSSH 服务端版本差异
对比两台机器的 SSH 服务端版本:
1 | |
- 问题机器:
OpenSSH_8.8p1 - 正常机器:
OpenSSH_10.2p1(自定义编译,无签名)
版本差距近两个大版本。怀疑 Windows Terminal 内置的新版 OpenSSH 客户端与旧版 8.8p1 服务端存在 PTY 协商兼容性问题,导致终端模式参数传递不完整。
第五步:升级问题机器的 OpenSSH 至 10.2p1
由于官方源只有 8.8p1,正常机器的 RPM 安装包也已删除,决定从正常机器直接打包二进制文件传输:
1 | |
升级前先在 2222 端口启动测试实例验证可用性:
1 | |
通过 Windows Terminal 连接 2222 端口测试:htop 鼠标完全正常!
随后替换系统主程序并重启服务(注意运行中的二进制需先删除再复制):
1 | |
通过 Windows Terminal 重新连接 22 端口:htop 鼠标仍然乱跳。
OpenSSH 版本升级后问题依然存在,排除版本因素。
第六步:对比 systemd 管理与手动启动的差异
此时发现关键现象:
- Windows Terminal 连接 22 端口(systemd 管理)→ 异常
- Windows Terminal 连接 2222 端口(手动启动)→ 正常
- 两个实例使用完全相同的二进制和配置
对比两个 sshd 进程的文件描述符:
1 | |
发现差异:
1 | |
systemd 将 stdout/stderr 重定向到 journal 套接字,而新版 OpenSSH 10.2p1 编译时未包含 systemd 支持(不链接 libsystemd),sshd-session 继承这些 journal 套接字后处理 PTY 时出现异常。
创建 systemd override 修复:
1 | |
验证 fd 1/fd 2 已变为 /dev/null——问题仍未解决。
第七步:停用 systemd 管理,手动在 22 端口启动
为彻底排除 systemd 影响,停止 systemd sshd,直接手动启动:
1 | |
连接手动启动的 22 端口:htop 鼠标仍然乱跳。
此时两个端口(22 和 2222)的进程在各方面已无任何差异:相同二进制、相同配置、相同 cgroup、相同 fd、SELinux 已关闭、环境变量相同、stty size 输出相同。
第八步:对比两个会话的 env 输出
分别在 22 端口和 2222 端口的 Windows Terminal 会话中执行 env,对比输出。
两个会话的环境变量完全一致,唯一区别是 SSH_CONNECTION 中的端口号和 SSH_TTY 的设备编号。
此时注意到关键线索:连接 2222 端口时是在测试期间手动输入命令,而连接 22 端口使用的是 Windows Terminal 中保存的 Profile。
第九步:定位根因——Windows Terminal Profile 配置
在 Windows Terminal 新建标签页(默认 Profile),手动输入:
1 | |
结果:htop 鼠标完全正常,非常稳定!
问题锁定在 Windows Terminal 的保存 Profile 上。
查看 settings.json,找到问题 Profile:
1 | |
根本原因
sshpass 是本次问题的唯一根因。
sshpass 为了自动注入密码,在 Windows Terminal 与 SSH 之间插入了一个中间层来拦截 stdin/stdout。这导致:
- PTY 终端模式参数(terminal modes)无法完整传递到 SSH 服务端
- 鼠标跟踪转义序列(SGR mouse mode
\E[<...M)在传输中被破坏 - vim 的光标控制和屏幕刷新序列出现异常
Xshell 8 自身实现了密码管理,不依赖 sshpass,因此不受影响。正常机器的测试也因使用的是直接 SSH 命令(无 sshpass)而正常。
解决方案
改用 SSH 密钥免密登录,彻底去除 sshpass:
在 Windows 端生成并部署密钥:
1 | |
更新 Windows Terminal Profile:
1 | |
去掉 sshpass 后,htop 鼠标和 vim 显示均完全恢复正常。
系统恢复
由于问题与 OpenSSH 版本无关,将问题机器恢复至原版 OpenSSH 8.8p1:
1 | |
恢复后 rpm 校验:
1 | |
额外优化
顺手关闭不必要的 X11 转发(openEuler 官方不建议禁用 PAM):
1 | |
总结
本次排查历经以下方向,逐一排除:
- TERM 环境变量差异
- htop / ncurses / terminfo 版本差异
- PROMPT_COMMAND 输出 OSC 转义序列
- OpenSSH 服务端版本差异(8.8p1 vs 10.2p1)
- systemd journal 套接字污染 fd 1/fd 2
- cgroup、capabilities、SELinux 等系统安全设置
- 终端尺寸(stty size / COLUMNS / LINES)
最终根因在客户端,与服务端完全无关。
核心教训:sshpass 通过拦截 stdin/stdout 注入密码的机制,会破坏终端 PTY 的完整性,导致依赖鼠标跟踪和精确光标控制的全屏应用(htop、vim 等)出现异常。 生产环境应使用 SSH 密钥认证替代 sshpass。