在去年,我在博客里写了一篇关于远程串流的文章。其中提到了在 Linux 上串流的一个问题:

但是在 Linux 平台,想让 Sunshine 虽系统启动有点麻烦。如果用 KDE 的自动启动程序的话,SDDM 登录界面它还是没启动,就没法远程输密码进系统。如果搞成系统级服务的话,就得让它以 root 身份运行,总感觉不太好。

最后想到了一个比较折中的方法:让 SDDM 开机时自动登录用户会话,同时给 KDE 设置一个自动启动脚本,在脚本中让 KDE 登录后立即锁屏。虽然感觉这个做法有点草台班子,但实际操作也挺简单,也能远程输密码,就先用这个方案了。

这个方法大体上算是勉强能用,但是开着自动登录总归是是有点不爽。而且在系统中偶尔需要注销用户重新登录,这样每次重新登录之后就又会触发自己写的自动锁屏脚本,所以就需要输两次密码,还是有点烦人了。


不久前,在 Sunshine 开发者官方博客上看到了这样的一篇文章:Autostart Sunshine on Boot without Auto-Login,文章中使用了一个很绕的方法让系统开机后自动 ssh 到本地用户,然后触发 sunshine 的启动脚本。虽然没太看懂,但大致上就是开机时先用系统级别的 systemd 开一个 sunshine,登录之后再切换成用户的 systemd 来运行 sunshine。

那能不能不用 ssh,直接让 systemd 用 root 身份启动一个 sunshine 呢?

其实是可以的。可以直接验证一下:注销之后停在 SDDM/GDM 的登录界面。然后切 tty 进 root 之后运行 sunshine,是可以正常运行并且串流到登录界面的。LizardByte 的文章中也有提到,只是这样不能串流音频也没有 tray icon,所有用户登录之后还是要切回 systemd user 的 sunshine 服务。

参照以上内容,我们可以先创建一个服务 :

文件:/etc/systemd/system/sunshine-sddm.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Unit]
Description=Self-hosted game stream host for Moonlight
StartLimitIntervalSec=500
StartLimitBurst=5
Requires=sddm.service
After=sddm.service

[Service]
User=root
Group=root
Environment="HOME=/home/username"
ExecStartPre=/bin/sleep 2
ExecStart=/usr/bin/sunshine
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

这个服务是为了开机之后能正常连接到登录界面。这里我使用的 Display Manager 是 SDDM,所以就让它在 SDDM 服务之后启动了,用 GDM 或者其他 DM 也是类似。

这里还设置了 HOME 环境变量,是为了这个以 root 运行的 sunshine 能复用自己用户的 sunshine 的配置文件,这样的话 Moolight 那边就不会需要显示两个设备了,使用同一个设备连接即可。记得把 /home/username 替换为自己的家目录。

在用户登录之后,切回用户的 sunshine 就是直接参照上面文章中的内容了。但是这里我们不直接修改 sunshine 的 service,使用使用 systemd override。这样可以防止 sunshine 更新之后把我们修改的内容覆盖掉。

在用户桌面环境下执行命令:

1
systemctl --user edit sunshine

然后按照下面的内容填写覆盖选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
### Editing /home/revincx/.config/systemd/user/sunshine.service.d/override.conf
### Anything between here and the comment below will become the contents of the drop-in file

[Service]
ExecStartPre=/usr/bin/systemctl stop sunshine-sddm
ExecStartPre=/bin/sleep 2
ExecStopPost=/usr/bin/systemctl start sunshine-sddm

### Edits below this comment will be discarded


### /usr/lib/systemd/user/sunshine.service
# [Unit]
# Description=Self-hosted game stream host for Moonlight
# StartLimitIntervalSec=500
# StartLimitBurst=5
#
# [Service]
# # Avoid starting Sunshine before the desktop is fully initialized.
# ExecStartPre=/bin/sleep 3
# ExecStart=/usr/bin/sunshine
#
# Restart=on-failure
# RestartSec=5s
#
# [Install]
# WantedBy=xdg-desktop-autostart.target

之后,覆盖的选项内容就会保存到 /home/usename/.config/systemd/user/sunshine.service.d/override.conf。

这里的覆盖内容主要是添加 Hook,让用户层的 sunshine 在启动前关闭 root 层的 sunshine,在结束后开启 root 层的 sunshine。

但是用户在执行 systemctl 的时候是要输密码授权的,为了跳过用户登录之后又要输入一次密码的步骤,这里需要单独写一条 polkit 规则:

创建文件:/etc/polkit-1/rules.d/sunshine-sddm.rules

1
2
3
4
5
6
7
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units" &&
action.lookup("unit") == "sunshine-sddm.service")
{
return polkit.Result.YES;
}
})

添加这条规则之后,重启一下 polkit.service,之后普通用户使用 systemctl 来开关 sunshine-sddm.service 就不用输密码了。

对了,最后别忘了 systemctl enable sunshine-sddm 以及 systemctl --user enable sunshine

完成了以上所有步骤之后,你就可以在使用 WoL 远程唤醒电脑之后,顺利地使用 Moonlight 串流到登录界面了。登录之后会断开一下连接,再重新连接就可以使用桌面了。


小彩蛋

在用 hexo 创建这篇文章之前,发现 git 仓库里有一个未跟踪的文件:

1
2
3
4
5
6
7
8
9
$ git status
位于分支 master
您的分支与上游分支 'origin/master' 一致。

未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
source/_posts/2024-review.md

提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)

我去,这是什么来着,我怎么一点印象没有了呢(