由于最近经常需要在实验室、教室和宿舍几头来回跑,每次把笔记本电脑背来背去太麻烦了。就想干脆电脑放在实验室不动,用平板来远程使用试试。这篇文章主要记录一下在折腾过程中遇到的问题和解决方法。

远程控制

Sunshine + Moonlight 这套方案对于玩远程的人基本都不陌生。如果你想在安卓平板上获取更好的触控体验,可以用 moonlight-android 的这个 fork 版来获取更好的体验。如果想把平板当电脑副屏,Sunshine 的这个 fork 版提供了内置的虚拟显示器支持。但很遗憾没有 Linux 版,在 Linux 上想要虚拟显示器的话最稳定的方法还得是显卡欺骗器。

在 Windows 上,Sunshine 服务是可以跟随系统自启动的,并且在锁屏界面也可以正常进行远程输密码的操作。这就给远程开机提供了方便。

但是在 Linux 平台,想让 Sunshine 虽系统启动有点麻烦。如果用 KDE 的自动启动程序的话,SDDM 登录界面它还是没启动,就没法远程输密码进系统。如果搞成系统级服务的话,就得让它以 root 身份运行,总感觉不太好。最后到了一个比较折中的方法:让 SDDM 开机时自动登录用户会话,同时给 KDE 设置一个自动启动脚本,在脚本中让 KDE 登录后立即锁屏。虽然感觉这个做法有点草台 班子,但实际操作也挺简单,也能远程输密码,就先用这个方案了。

组网方案

一提到组网立马就能想到 Tailscale, Zerotier 之类的平台。虽然确实不错,但是在实际使用的时候发现体验并不算太好,Tailscale 每次打洞都需要半天才能变成直连,直连也不见得是最优的路由。Zerotier 现在国内经常连都连不上。要说自建 Headscale 的话,我又没有国内服务器,用国外 VPS 自建效果肯定也一般。

既然我大部分情况都是在学校内来用,那我为什么不直接通过学校内网来远程呢?这就得用到最原始的方法了:WireGuard。

还好我在实验室和宿舍都有可以使用的软路由,既然这样都给它们上面装一个 WireGuard 吧。这里要吐槽一下, OpenWrt 上安装 WireGuard 竟然要重启才能用,因为我在装的时候也是远程到软路由上装的,一重启IP就变了我直接失联了,很离谱。装好之后配置一下端口转发,就可以用平板在学校任意位置瞬间连上隧道了,使用体验比 Tailscale 简直不要好太多。

网络唤醒

上面都提到要让 Sunshine 开机启动了,那网络唤醒必然少不了。我目前的笔记本是有有线网口的,但一个很尴尬的问题是,笔记本距离软路由的位置有点远,没办法拉网线直接接上去,平时都是用无线来上网的。那怎么用 WoL 来唤醒电脑呢?

这时我忽然想到,既然实验室的每个位置都已经分配了网线口,那我软路由那边的上游网口和我笔记本这边的网口应该是接在同一个交换机的。学校这边的认证策略目前还管不了未认证设备在同一个交换机下的相互通信。那我是不是可以用那边软路由的 wan 口来发送 WoL 唤醒包呢?

实践证明完全可以。在 OpenWrt 的 luci-app-wol 里面,可以选择发送唤醒包的接口为 eth1(也就是 wan 口),然后笔记本这边就能正常收到唤醒包了。

但是还有个问题,因为笔记本这边有线没有接到软路由下面获取 IP 地址,软路由的 DHCP 记录自然也就没有我笔记本有线口的 MAC 地址。这样的话每次用 OpenWrt 唤醒笔记本的时候,还非得手动输入电脑 MAC 地址,很麻烦。

于是我就在想:有没有这样一款网络唤醒的服务端,可以保存自定义的目标 MAC 地址到配置文件,每次要唤醒只需要在网页点一下目标电脑就行了。于是,我马上搜了一下,发现果然已经有很多人做过了。

我使用的是 wolweb 这个项目,并且经过我本地一番艰难的修改与编译之后,也勉强让它在 OpenWrt 上跑起来了。这个东西直接调用 API 就可以发唤醒包,再配合 HTTP Request Shortcuts 这个软件,就可以做到在平板的桌面上点一下,远程的电脑就会开机了,简直非常的 nice~

双系统切换

因为笔记本上装了 Win11 + ArchLinux 双系统,开机的时候需要通过 rEFInd 来选择进那个系统。但是如果是远程环境的话,就没法开机的时候进行系统选择了。那要是想切到另一个系统继续进行远程怎么办呢?

一个最容易想到的方法是修改 UEFI 启动顺序。虽然目前 rEFInd 是第一位,但是 UEFI 提供了可以在操作系统内修改启动顺序的接口,在 Linux 或者 Win 下面都有对应的工具可以修改。如果不想完全修改启动顺序,也可以临时指定下次启动使用哪一个选项。想进 Win 就指定下次启动 Window Boot Manager,想进 Linux 就指定下次启动 grub。

在 Linux 下,可以使用 efibootmgr 工具来修改,这个软件在大部分 Linux 发行版的仓库都能下载到。

1
2
3
4
5
6
7
8
9
$ efibootmgr
BootCurrent: 0000
Timeout: 0 seconds
BootOrder: 0000,0004,0006,2001,2002,2003,0005,0007,0008,0009,000A
Boot0000* rEFInd HD(1,GPT,ec78f53c-6ea2-4899-922d-f8d2471e9871,0x800,0x82000)/\EFI\refind\refind_x64.efi
Boot0001* EFI PXE 0 for IPv4 (00-E0-4C-58-1A-EB) PciRoot(0x0)/Pci(0x8,0x3)/Pci(0x0,0x4)/USB(1,0)/USB(0,0)/MAC(00e04c581aeb,0)/IPv4(0.0.0.0,0,DHCP,0.0.0.0,0.0.0.0,0.0.0.0)RC
Boot0002* EFI PXE 0 for IPv6 (00-E0-4C-58-1A-EB) PciRoot(0x0)/Pci(0x8,0x3)/Pci(0x0,0x4)/USB(1,0)/USB(0,0)/MAC(00e04c581aeb,0)/IPv6([::],0,Static,[::],[::],64)RC
Boot0004* arch HD(1,GPT,ec78f53c-6ea2-4899-922d-f8d2471e9871,0x800,0x82000)/\EFI\arch\grubx64.efi
Boot0005* Windows Boot Manager HD(1,GPT,ec78f53c-6ea2-4899-922d-f8d2471e9871,0x800,0x82000)/\EFI\Microsoft\Boot\bootmgfw.efi

如果想下次启动 Win11 / Linux,只需要执行:

1
2
$ sudo efibootmgr --bootnext 0005 # Windows Boot Manager
$ sudo efibootmgr --bootnext 0004 # GRUB

在 Windows 下,其实也有自带的工具可以改 UEFI:bcdedit.exe

但是这个命令行工具是使用方法就有点抽象了。先用下面的命令输出目前的 UEFI 启动项:

1
bcdedit.exe /enum firmware

然后找到你想要下次启动的选项,复制其 GUID,使用下面的命令指定下次的临时启动项:

1
bcdedit.exe /set {fwbootmgr} bootsequence {guid} # guid 是目标选项的标识符

这里要注意这个命令不能在 PowerShell 下面执行,不然会报错,最好在管理员 cmd 下执行。

当然了,如果是在 Windows 下面,修改 UEFI 启动顺序还有很多好用的软件可以用,我个人比较推荐 BOOTICE 这个工具。

一个比较脑洞大开的方法

由于我是用 rEFInd 来在开机的时候进行双系统切换的,它实际上会在每次启动的时候保存我在上一次的选择。那能不能把它保存的选项给改掉呢?

根据 rEFInd 的文档

Adjusting the Default Boot Option

Just before launching an OS, rEFInd stores the description in the EFI variable PreviousBoot with a GUID of 36d08fa7-cf0b-42f5-8f14-68df73ed3740. The next time rEFInd launches, it reads that same variable and sets the default boot loader to that value, if it’s still available and if the first item in default_selection in the refind.conf file is a plus sign (+).

但是我并没有发现 efibootmgr 的环境变量列表里面有这个变量,怎么回事呢?

再继续往下看文档:

Note: If you set use_nvram false in refind.conf, rEFInd stores its variables in files in the vars subdirectory of the rEFInd binary directory, rather than in NVRAM. You can read and modify these files in any OS that has access to the ESP.

然后我去看了一眼我的 refind.conf 配置文件,还真关掉了 use_nvram。这个东西已经是我四年前配的了,我也不记得当时配的时候为什么要关掉了。那既然这样就不动它了,直接改 vars/PreviousBoot 这个文件试试吧。

这个文件里的东西大概长这样:

1
2
$ sudo cat /boot/efi/EFI/refind/vars/PreviousBoot
Boot EFI\arch\grubx64.efi from SYSTEM_DRV YSTEM_DRV volume

但是当我尝试使用 sed 命令来替换文件里这个 efi 文件的路径时,发现无论怎么样都匹配不了字符串,好像这些字符串有什么特殊的魔力一样。

别急,等我再回去翻一下 rEFInd 的文档:

Under Linux, the variable that rEFInd uses to store this information is accessible as /sys/firmware/efi/efivars/PreviousBoot-36d08fa7-cf0b-42f5-8f14-68df73ed3740. Thus, you can back up this value, modify it, and write it back out to adjust your next-booted OS. Getting this string just right can be a bit tricky, though, and if the kernel doesn’t like its format, it will not let you modify the variable. If you try to modify the variable, be aware that it’s stored in UTF-16 format. As with the default_selection token in refind.conf, you can enter any substring that uniquely identifies the entry you want to boot.

也就是说这个文件里的字符串也是以 UTF-16 编码来存储的。那简单,我把它转成 UTF-8 再执行匹配,然后替换完了再转成 UTF-16 写入回去,不就可以了?

编码转换在 Linux 下可以用 iconv:

1
2
3
4
5
$ export PRE_BOOT=/boot/efi/EFI/refind/vars/PreviousBoot

$ iconv -f UTF-16LE -t UTF-8 $PRE_BOOT |
sed 's/arch\\grubx64.efi/Microsoft\\Boot\\bootmgfw.efi/' |
iconv -f UTF-8 -t UTF-16LE -o PreviousBoot

然后再 cat 一下:

1
2
$ cat PreviousBoot
Boot EFI\Microsoft\Boot\bootmgfw.efi from SYSTEM_DRV YSTEM_DRV volume

修改成功!重启试了一下,rEFInd 的系统选择界面也变成了选择 Windows。

那问题来了,在 Windows 下怎么修改这个文件来实现 rEFInd 选择 Linux 启动呢?


嘿嘿,这一部分就留给读者思考了,最好能写成bat脚本的形式(

绝对不是因为我自己懒得想怎么做了

遇到的一些其他问题

  1. 笔记本没法使用 WoL 唤醒

    这个问题一开始困扰了我很长时间,后来发现竟然是因为我用了 BIOS 修改工具强制开启了 S3 睡眠。但是只能说垃圾联想 BIOS 还是做的不行,要不是便宜真的不会再买联想了。

  2. Linux Sunshine 串流时连接不上

    没错,这个问题也是因为联想,它自带的无线网卡是 MT7921。这个垃圾网卡对 Linux 的兼容性似乎非常不好,总是莫名其妙的卡,后来换成 AX200 就没出过问题了。总之串流还是能用有线就用有线吧。