callcc.dev

WSL2 因为是基于 Hyper-V 虚拟化的一个 Linux,因此我想着在 WSL 里跑 Docker。当我高高兴兴地安装好了 Docker 之后发现不能启动,因为 PID 为 1 的进程不是 systemd,而是 init,让我这个菜鸡呆了。一番搜索之后发现有人在 GitHub 给出一个解决方案,Genie,原理是利用 Linux namespace 实现隔离,在隔离出的空间里运行 systemd,这样 systemd 的 PID 就是 1 了。
看过代码之后得到里几个关键的命令
  1. unshare:在新的 namespace 里运行命令。man page
  2. nsenter:在某个 namespace 里运行命令。man page
  3. runuser:切换执行命令的用户。man page
我们的步骤是
  1. pidof检查是否有systemd进程,有则跳到 3,否则 2。
  2. unshare运行systemd,这样systemd在新的 namespace 里的 PID 就是 1。整个命令用daemonize命令执行,这样创建出来的systemd以后台进程形式一直运行。
  3. nsenterrunuser进入新创建的 namespace 里以某个用户身份运行 shell。
于是我们可以得到这样的一个脚本,GitHub 传送门
#!/bin/bash

systemd_pid=

if [ -z $(pidof systemd) ]; then
        daemonize /usr/bin/unshare -fp --propagation shared --mount-proc systemd
fi

while [ -z $systemd_pid ]
do
        for pid in $(pidof systemd)
        do
                systemd_pid=$pid
        done
done

if [ -z $WHO ]; then
        WHO=root
        WD=/
else
        WD=/home/$WHO
fi

nsenter -t $systemd_pid -m -p --wd=$WD runuser -m -p -u $WHO zsh
使用方法:
WHO=`whoami` sudo -E ./wslsystemd.sh