6

在 Windows Subsystem For Linux 2 内使用 systemd 的三种方法

 3 years ago
source link: https://blog.251.sh/three-ways-to-use-systemd-in-wsl2
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在 Windows Subsystem For Linux 2 内使用 systemd 的三种方法

April 17, 2021

一切后果自行承担。

WSL1 时代就有人提出了这个问题,到 WSL2 依然没有解决。

这个方法只支持 Windows 10 版本号高于 20201 的系统。意味着在本文撰稿时的 20H2 (19042) 无法使用。

/etc/wsl.conf 内添加如下内容:

[boot]
command = "sudo daemonize /usr/bin/unshare -fp --mount-proc /lib/systemd/systemd --system-unit=basic.target"

这个方法是由 @Biswa96 发现的。因为在初始化 WSL 之前,/init 进程会加载 wsl.conf,所以一个子进程会被分出来。这个子进程然后在系统调用层执行这行代码,最后通过 posix_spawn(3) 生成一个进程。

这个方法来自 wsl2-hacks 这个项目。大致是创建一个 fake shell,然后这个 shell 会截断所有去 wsl.exe bash ...的调用,并且会将这个请求发送给在 real bash 内运行的 systemd。我在 Debian 10、Ubuntu 20.04、Arch Linux 以及 RHEL 8 里测试通过了。

首先,更新系统并安装必要的包(Debian 系):

$ sudo apt update
$ sudo apt install dbus policykit-1 daemonize

ArchLinux 需要安装 dbus policykit daemonize (daemonize 需要 archlinuxcn 源,或者手动从 AUR 安装) 这三个包。

RHEL 需要安装 dbus polkit,而 daemonize 这个包需要从 EPEL 下载并手动安装,可以在这里获取到。

第二步,就是创建一个假的 bash。

$ sudo touch /usr/bin/bash-bootstrap-services
$ sudo chmod +x /usr/bin/bash-bootstrap-services
$ sudo editor /usr/bin/bash-bootstrap-services

接下来,将 <YOURUSER> 替换成你的用户名。

#!/bin/bash
# your WSL2 username
UNAME="<YOURUSER>"

UUID=$(id -u "${UNAME}")
UGID=$(id -g "${UNAME}")
UHOME=$(getent passwd "${UNAME}" | cut -d: -f6)
USHELL=$(getent passwd "${UNAME}" | cut -d: -f7)

if [[ -p /dev/stdin || "${BASH_ARGC}" > 0 && "${BASH_ARGV[1]}" != "-c" ]]; then
    USHELL=/bin/bash
fi

if [[ "${PWD}" = "/root" ]]; then
    cd "${UHOME}"
fi

# get pid of systemd
SYSTEMD_PID=$(pgrep -xo systemd)

# if we're already in the systemd environment
if [[ "${SYSTEMD_PID}" -eq "1" ]]; then
    exec "${USHELL}" "$@"
fi

# start systemd if not started
/usr/bin/daemonize -l "${HOME}/.systemd.lock" /usr/bin/unshare -fp --mount-proc /lib/systemd/systemd --system-unit=basic.target 2>/dev/null
# wait for systemd to start
while [[ "${SYSTEMD_PID}" = "" ]]; do
    sleep 0.05
    SYSTEMD_PID=$(pgrep -xo systemd)
done

# enter systemd namespace
exec /usr/bin/nsenter -t "${SYSTEMD_PID}" -m -p --wd="${PWD}" /sbin/runuser -s "${USHELL}" "${UNAME}" -- "${@}"

在保存之前,最好看一下 daemonize 的二进制文件在哪里,可以通过 whereis daemonize 来看到。有的发行版在 /usr/bin 下,而有的在 /usr/sbin 下。如果这个写错了会导致重进时 WSL2 宕机。

第三步,将这个 fake bash 设置成用户 root 的 shell。因为我们要 root 权限才能完成这个 hack。

$ sudo editor /etc/passwd

通过编辑 /etc/passwd 来更改 shell,编辑后看起来应该像这样:

root:x:0:0:root:/root:/usr/bin/bash-bootstrap-services

第四步,保存并在 PowerShell 内运行如下命令:

> wsl --shutdown
> ubuntu2004.exe config --default-user root

最后一行根据不同的 Distro,需要运行不同的 exe。例如 Arch 就是 arch.exe,Debian 就是 debian.exe

第五步,就是重新启动你的 WSL2 实例啦!看看是不是和往常一样登入进了自己的用户,并且可以用 systemd 了呢?

$ systemctl is-active dbus
active
$ sudo systemctl status mysql
● mariadb.service - MariaDB 10.3.27 database server
   Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
....

如果你还想在实例启动的时候运行一些命令,那么可以写入 /etc/rc.local 这个文件。

$ sudo touch /etc/rc.local
$ sudo chmod +x /etc/rc.local
$ sudo editor /etc/rc.local

创建之后,这么写就行了:

#!/bin/sh -e

# your commands here...

exit 0

注意,/etc/rc.local 只在第一次开启 WSL2 时有效。

使用 genie


如果喜欢本文,欢迎点击下方的「鼓掌」按钮!

如果上面没有加载出任何东西,可以点击这里


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK