使用 USB tty 控制你的 Linux 主机

之所以有这个小需求,是遇到了两件事情。

一个是朋友的 PVE 在配置坏了网络、没有屏幕可用的时候,需要一种能控制系统的方式;另一个是在打 CTF 线下赛的时候,因为自己现在主力是 macOS 了,所以整了个 x86_64 的小机器来调试 x86 的程序,而现场的网络环境不一定可以直接使用 DHCP 或者其他方式打通网络,然后走 SSH 交互——所以为了这个无显示器配置网络的需求,就有了这个需求。

需要什么硬件

  • 2 * USB 转 TTL 的串口线
  • 一些杜邦线

这个硬件来说基本就是不超过 10 块钱就能搞定的水平,为的是能不直接走串口接线的情况下,能够在 USB 上直接使用串口终端。

一般来说也会被别人讲是 “刷机线” 之类的,之所以需要两个,是因为控制设备和被控制设备都需要一个。

要通讯需要至少连接 GNDRXTX 三个引脚,如果是需要控制电源的话,还需要 VCC 引脚。

连接方式是两个 USB 转 TTL 的串口线的 RXTX 两个引脚交叉连接,也即两端的 “收发” 引脚互相连接,然后 GND 引脚连接在一起,VCC 引脚连接在一起。

连接好之后插到终端上就能看到类似于 /dev/tty.usbserial-xxxx 或者 /dev/ttyUSBx 的设备了。

使用 USB tty 进行交互

这里主要描述的是 Linux 作为受控端的情况,其他类型的话这里不涉及了。

实现方式的话是基于 systemd 的,对于不使用 systemd 的发行版,这里也不涉及了——不过现在跑在物理机上的 Linux 应该没有人不用 systemd 了吧。

在 Linux 上接入 USB 转 TTL 的串口线后,通过 ls /dev/ttyUSB* 可以看到类似于 /dev/ttyUSB0 的设备。

需要注明的是,下文中出现的设备名都会使用 ttyUSB0 代替。确认有设备之后使用如下指令来创建一个 systemd 的服务:

sudo systemctl enable [email protected] --now

这样就能在 /dev/ttyUSB0 上暴露一个 getty 服务了,之后我们在另一个终端上使用 screen 来连接这个设备。

需要注意的是这一侧的设备需要自己查找一下,在 macOS 上的话是 /dev/tty.usbserial-xxxx,Windows 上的话应该(可能)是 COMx。下面的例子中使用的是 *unix 系统进行,如需 Windows 的话可能需要安装一些额外软件,比如 PuTTY 之类的(Copilot 说的)。

screen /dev/tty.usbserial-xxxx 9600

按几下回车之后如果能正常看到登陆提示的话,就说明串口连接成功了。

你可能会需要的小知识:

退出 screen 的话可以使用 Ctrl + A 然后再按 k,然后再按 y 确认退出。

不过这样的串口通讯是不持久的,拔掉 USB 转 TTL 的串口线之后,这个 getty 服务也会被关闭,再次插入的时候也不会自动启用,并且波特率的协商可能会使用最低的 9600,这样的话在交互的时候会有延迟,所以我们需要做一些额外的配置。

使用 systemd 管理 USB tty

我们可以使用 systemctl edit 来编辑一个服务的配置,生成一个 Drop-in file 来覆盖默认的配置,并且不会影响到更新和原有的配置。

sudo systemctl edit [email protected]

在编辑器中输入如下内容:

### Editing /etc/systemd/system/[email protected]/override.conf
### Anything between here and the comment below will become the contents of the drop-in file

[Service]
ExecStart=
ExecStart=-/sbin/agetty -o '-p -- \\u' 115200 - $TERM

[Install]
WantedBy=dev-%i.device

### Edits below this comment will be discarded

# ...

其中,ExecStart 覆盖原有的命令行,强制使用 115200 的波特率,可以参考 getty 的文档来了解更多的参数。连接的波特率也要和这里的一致,否则会出现乱码。

WantedBy 用于指定这个服务的启动依赖,这里使用 dev-%i.device 来指定依赖于 /dev/ttyUSB0 这个设备,这样的话在插入设备的时候就会自动启动这个服务。

至此我们就完成了一个可以自动启动的 USB tty 服务了,可以在没有网络的情况下使用 USB tty 来进行一些基本的配置。

碎碎念

感觉现在写技术文档越来越离谱了,好多东西写个需求就被 Copilot / GPT 抢答了……

哦,还有个建议,在有网络的情况下给 Linux 主机跑个 tailscale 什么的,SSH 起来可以说是非常方便了。