WSL2とsystemdとnslogin
はじめに
こちらの記事でWSL2でsystemdを動かしました。
systemdが新規のPID namespaceで動いているために、systemctlなどのコマンドを打つためには/usr/libexec/nslogin
コマンドを打ってそのnamespaceに入る必要がありました。
毎回nsloginを打つのは面倒くさいのと、そもそも普通のLinuxにはnsloginがないのでできれば同じように手動でnsloginせずにsystemctlしたいです。
結論
以下のどれかがいいと思います。
- Microsoft StoreからUbuntuをインストール
- 設定→アプリ→アプリと機能→その他の設定→アプリ実行エイリアスで新規インストールした方をONにする
- 500MBほどディスク領域を余分に使う
-
C:\Windows\System32\wsl.exe -d Ubuntu --cd ~ -e /bin/bash -c /usr/libexec/nslogin
で起動する- デスクトップなどにショートカットを置く、ランチャーソフト・Windows TerminalのWSL起動コマンドを書き換える必要あり
- Microsoft StoreからUbuntu 22.04 LTSをインストール
- 現在別のWSLインスタンスを使っている場合は作り直しになります。
- 将来24.04にバージョンアップした後もWSLインスタンスの名前が「Ubuntu-22.04」のままです。
調べたこと
先人の知恵
こちらの記事によると以下のようになるらしい。
-
wsl -e /usr/libexec/nslogin /bin/bash
で起動するとsystemd用のPID namespaceで入れる- ただし環境変数SHELLがnsloginになる
- Ubuntu22.04だと問題ない
環境変数SHELL
実際に試してみるとその通りで、wsl -e /usr/libexec/nslogin /bin/bash
だと確かに環境変数SHELLが/usr/libexec/nsloginでした。
C:\>wsl -e /usr/libexec/nslogin /bin/bash
root@Win11:/mnt/c# echo $SHELL
/usr/libexec/nslogin
例えば、viでファイル編集中に:r! ls
とかやってカレントディレクトリのファイルを挿入したりするときに、環境変数SHELLが適切に設定されていないと以下のエラーがでます。これは/usr/libexec/nslogin -c (ls)
というコマンドを実行して、nsloginが-cオプションを受け付けないのでエラーになっています。
Executing "-c" failed: No such file or directory
shell returned 7
環境変数SHELLをbashにするのがいいんですが、どこでセットするかですよね?
.bashrcとかにexport SHELL=/bin/bash
と書くのでもよいとは思うのですが、普通のLinuxだと.bashrcで環境変数SHELLをセットしたりしないのでなんとなく気持ちが悪いです。
そもそも誰が環境変数SHELLに/usr/libexec/nsloginをセットしているのか?
nslogin
最初はnsloginコマンドが環境変数SHELLを/usr/libexec/nsloginにセットしていると想像しました。
ただソースコードをざっとみても環境変数SHELLをセットしているところを見つけられないです。逆に環境変数SHELLから値を読みだしている処理はあります。nsloginに引数を与えない場合は環境変数SHELLの値をexecvpに渡してプログラム実行しています。
bash
nsloginがセットしているのではないとすると誰がセットしているのか?bashでしょうか?
man bash
を見ると以下の記述があります。bash起動時に環境変数SHELLがセットされていない場合は現在のログインシェルのフルパスをセットするとのこと。
SHELL
This variable expands to the full pathname to the shell. If it is not set when the shell starts, bash assigns to it the full pathname of the current user's login shell.
ログインシェルは/etc/passwdに書かれてるシェルです。
普通は/bin/bashですよね。bashの処理で環境変数SHELLが/usr/libexec/nsloginになるとは考えづらいです。
# grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
実験
nsloginが起動した時点で環境変数SHELLに何が入っているのかを確かめるためにwsl -e /usr/libexec/nslogin /bin/bash
の最後の引数を省略してwsl -e /usr/libexec/nslogin
を実行してみます。
ps -ef
をするとおびただしい量のnsloginコマンドがPIDをインクリメントしながら起動されていることが分かります。
root 169942 169941 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169943 169942 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169944 169943 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169945 169944 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169946 169945 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169947 169946 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169948 169947 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169949 169948 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169950 169949 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169951 169950 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169952 169951 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169953 169952 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169954 169953 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169955 169954 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169956 169955 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169957 169956 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169958 169957 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169959 169958 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169960 169959 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169961 169960 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169962 169961 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169963 169962 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169964 169963 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169965 169964 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169966 169965 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169967 169966 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169968 169967 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169969 169968 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169970 169969 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
root 169971 169970 0 21:02 pts/1 00:00:00 /usr/libexec/nslogin
さきほどのnsloginのソースコードを見ると引数がないときは環境変数SHELLのプログラムにexecvpしています。[1]
nsloginがnsloginを起動し続けることと、nsloginに環境変数SHELLをセットする処理が見当たらないことは、nslogin呼び出し前に環境変数SHELLが/usr/libexec/nsloginにセットされていることを示しています。
wsl.exe
以上から考えると、wsl.exeが環境変数SHELLに/usr/libexec/nsloginをセットしています。おそらく-eオプション(の最初の単語)を環境変数SHELLにセットしているのでしょう。
wsl.exe -eの次に/bin/bashが来れば、それが環境変数SHELLに入るだろうと思い、以下のコマンドを打つと環境変数SHELLが予想通り/bin/bashになりました。
C:\>wsl.exe -e /bin/bash -c /usr/libexec/nslogin
root@Win11:/mnt/c# echo $SHELL
/bin/bash
- wsl.exeは-eオプションに従い、環境変数SHELLに/bin/bashをセットして、
/bin/bash -c /usr/libexec/nslogin
を実行 -
/bin/bash
は-cオプションに従い、/usr/libexec/nslogin
を実行し、そのコマンドが終了したらbashも終了する -
/usr/libexec/nslogin
は環境変数SHELLの値を読みだしてその値(/bin/bash)をsystemdのPID namespaceにて実行
wsl.exeは-eオプションがない場合は、デフォルトユーザのホームディレクトリでデフォルトユーザのログインシェルを実行します。-eを付けると引数で指定したプログラムを実行するのですが、ユーザのホームディレクトリには移動しなくなります。
移動するためには--cd ~
オプションを指定します。
この--cd
を-e
より後ろに指定すると--cd ~
も-e
のコマンドの一部だと認識するようで、思うようにディレクトリ移動しません。-e
の前に--cd
指定する必要があります。
C:\>wsl.exe -e /bin/bash -c /usr/libexec/nslogin --cd ~
root@Win11:/mnt/c# pwd
/mnt/c
root@Win11:/mnt/c# exit
exit
C:\>wsl.exe --cd ~ -e /bin/bash -c /usr/libexec/nslogin
root@Win11:~# pwd
/root
ショートカットで対処
以下のショートカットをデスクトップや自分の使っているランチャーソフトに登録することで明示的にnsloginしなくてもsystemctlなどのコマンドを実行できるようになります。「-d Ubuntu」のところはお使いのWSL環境に応じて変えてください。(一覧はwsl -lで出ます)
C:\Windows\System32\wsl.exe -d Ubuntu --cd ~ -e /bin/bash -c /usr/libexec/nslogin
Ubuntu22.04で対処
Ubuntu22.04なら難しいことを考えなくてもsystemd環境に入れるというのを試してみました。
Microsoft Storeには「Ubuntu 22.04 LTS」という名前のアプリがあります。
これをインストール[2]して/etc/wsl.confにsystemdの設定をして再起動します。
[boot]
command=/usr/libexec/wsl-systemd
スタートメニューから「Ubuntu 22.04 LTS」を選択するとあっさりとsystemd PID namespaceに入ることができました。(systemdのPIDが1になっているということはsystemdと同じPID namespaceにいることを意味しています)
$ pidof systemd
1
コマンドプロンプトなどから起動するときは「ubuntu2204」コマンドで起動できます。これもあっさりsystemd PID namespaceに入ることができました。
C:\>ubuntu2204
user01@DESKTOP-QUCRLP5:~$ pidof systemd
1
「ubuntu2204」コマンドがどこにあるのかというと以下にあります。
C:\>where ubuntu2204
C:\Users\user01\AppData\Local\Microsoft\WindowsApps\ubuntu2204.exe
ただこのファイルはサイズが0バイトです。
なんで0バイトのファイルが実行できるのかというと、NTFSのreparse pointとかいう機能らしい。
C:\>fsutil reparsepoint query %LOCALAPPDATA%\Microsoft\WindowsApps\ubuntu2204.exe
再解析タグ値 : 0x8000001b
タグ値: Microsoft
再解析データの長さ: 0x1ca
再解析データ:
0000: 03 00 00 00 43 00 61 00 6e 00 6f 00 6e 00 69 00 ....C.a.n.o.n.i.
0010: 63 00 61 00 6c 00 47 00 72 00 6f 00 75 00 70 00 c.a.l.G.r.o.u.p.
0020: 4c 00 69 00 6d 00 69 00 74 00 65 00 64 00 2e 00 L.i.m.i.t.e.d...
0030: 55 00 62 00 75 00 6e 00 74 00 75 00 32 00 32 00 U.b.u.n.t.u.2.2.
0040: 2e 00 30 00 34 00 4c 00 54 00 53 00 5f 00 37 00 ..0.4.L.T.S._.7.
0050: 39 00 72 00 68 00 6b 00 70 00 31 00 66 00 6e 00 9.r.h.k.p.1.f.n.
0060: 64 00 67 00 73 00 63 00 00 00 43 00 61 00 6e 00 d.g.s.c...C.a.n.
0070: 6f 00 6e 00 69 00 63 00 61 00 6c 00 47 00 72 00 o.n.i.c.a.l.G.r.
0080: 6f 00 75 00 70 00 4c 00 69 00 6d 00 69 00 74 00 o.u.p.L.i.m.i.t.
0090: 65 00 64 00 2e 00 55 00 62 00 75 00 6e 00 74 00 e.d...U.b.u.n.t.
00a0: 75 00 32 00 32 00 2e 00 30 00 34 00 4c 00 54 00 u.2.2...0.4.L.T.
00b0: 53 00 5f 00 37 00 39 00 72 00 68 00 6b 00 70 00 S._.7.9.r.h.k.p.
00c0: 31 00 66 00 6e 00 64 00 67 00 73 00 63 00 21 00 1.f.n.d.g.s.c.!.
00d0: 75 00 62 00 75 00 6e 00 74 00 75 00 32 00 32 00 u.b.u.n.t.u.2.2.
00e0: 30 00 34 00 00 00 43 00 3a 00 5c 00 50 00 72 00 0.4...C.:.\.P.r.
00f0: 6f 00 67 00 72 00 61 00 6d 00 20 00 46 00 69 00 o.g.r.a.m. .F.i.
0100: 6c 00 65 00 73 00 5c 00 57 00 69 00 6e 00 64 00 l.e.s.\.W.i.n.d.
0110: 6f 00 77 00 73 00 41 00 70 00 70 00 73 00 5c 00 o.w.s.A.p.p.s.\.
0120: 43 00 61 00 6e 00 6f 00 6e 00 69 00 63 00 61 00 C.a.n.o.n.i.c.a.
0130: 6c 00 47 00 72 00 6f 00 75 00 70 00 4c 00 69 00 l.G.r.o.u.p.L.i.
0140: 6d 00 69 00 74 00 65 00 64 00 2e 00 55 00 62 00 m.i.t.e.d...U.b.
0150: 75 00 6e 00 74 00 75 00 32 00 32 00 2e 00 30 00 u.n.t.u.2.2...0.
0160: 34 00 4c 00 54 00 53 00 5f 00 32 00 32 00 30 00 4.L.T.S._.2.2.0.
0170: 34 00 2e 00 30 00 2e 00 31 00 30 00 2e 00 30 00 4...0...1.0...0.
0180: 5f 00 78 00 36 00 34 00 5f 00 5f 00 37 00 39 00 _.x.6.4._._.7.9.
0190: 72 00 68 00 6b 00 70 00 31 00 66 00 6e 00 64 00 r.h.k.p.1.f.n.d.
01a0: 67 00 73 00 63 00 5c 00 75 00 62 00 75 00 6e 00 g.s.c.\.u.b.u.n.
01b0: 74 00 75 00 32 00 32 00 30 00 34 00 2e 00 65 00 t.u.2.2.0.4...e.
01c0: 78 00 65 00 00 00 30 00 00 00 x.e...0...
IO_REPARSE_TAG_APPEXECLINK
0x8000001B
Used by Universal Windows Platform (UWP) packages to encode information that allows the application to be launched by CreateProcess. Server-side interpretation only, not meaningful over the wire.
reparse pointのAPPEXECLINKという種類でプロセス起動の時の情報をもっているとのこと。うーん、結局symbolic linkみたいなものととらえればよいのか???
こちらに実体があるけど結局exeファイルなのでこれ以上解析できないですね。[3]
C:\Program Files\WindowsApps\CanonicalGroupLimited.Ubuntu22.04LTS_2204.0.10.0_x64__79rhkp1fndgsc\ubuntu2204.exe
一方でwslコマンドから起動するとデフォルトのPID namespace(=非systemd PID namespace)で起動します。
C:\>wsl -d Ubuntu-22.04
user01@DESKTOP-QUCRLP5:/mnt/c$ pidof systemd
19
-eで明示的にnsloginを指定すると、冒頭のUbuntuと同じようにsystemd PID namespaceに入れます。
C:\>wsl -d Ubuntu-22.04 --cd ~ -e /bin/bash -c /usr/libexec/nslogin
user01@DESKTOP-QUCRLP5:~$ pidof systemd
1
おそらくUbuntu-22.04のイメージにsystemd PID namespaceに入る仕組みがあるのではなくてubuntu2204.exeバイナリにその仕組みがあるのでしょう。
起動コマンドの違い
Microsoft StoreのUbuntu
ところで、こちらの記事にあるようにwsl --install -d ubuntu
でUbuntu(20.04だったのを22.04にアップグレード)をインストールした環境でも、Microsoft StoreではUbuntuが「入手」になっています。もう入っているのだから「インストール済み」になるのでは?と思いつつも押してみます。
500MB程度のファイルをダウンロードします。
完了しました。(「開く」は押さなくてもいいです)
スタートメニューが「Ubuntu on Windows」だったのが
「Ubuntu」が増えています。
「Ubuntu」で起動したものが左、「Ubuntu on Windows」で起動したものが右です。新しくMicrosoftからインストールした「Ubuntu」はsystemd PID namespaceに入っています。localhost 123番ポート同士で通信できていることからわかるように、どちらも同じLinuxインスタンスに入っています。
wsl.exeで見てもLinuxインスタンスは1個しか動いていないです。
C:\>wsl -l -v
NAME STATE VERSION
* Ubuntu Running 2
コマンドプロンプトで「ubuntu」コマンドを打つとデフォルトのPID namespaceです。おそらく「Ubuntu on Windows」アプリが起動されているんでしょう。
C:\>where ubuntu
C:\Users\user01\AppData\Local\Microsoft\WindowsApps\ubuntu.exe
C:\>ubuntu
root@Win11:~# pidof systemd
16
設定→アプリ→アプリと機能→その他の設定→アプリ実行エイリアス がMicrosoft Storeの「Ubuntu」インストール前がこうだったのが
こうなっています。「Ubuntu」が増えました。
「Ubuntu」をオンにすると「Ubuntu on Windows」が自動的にオフになります。
C:\>where ubuntu
C:\Users\user01\AppData\Local\Microsoft\WindowsApps\ubuntu.exe
C:\>ubuntu
root@Win11:~# pidof systemd
1
コマンドプロンプトで「ubuntu」コマンドを打つとsystemdのPID namespaceに入るようになりました。おそらく「Ubuntu」アプリが起動されているんでしょう。
アプリ実行エイリアスを切り替える前後でreparse pointの指す先も変わっています。
インストール時期による違い
先の例では以下の時期にインストールしました。
2022/05にwsl --install -d ubuntuでインストール:「Ubuntu on Windows」
2022/07にMicrosoft Storeでインストール:「Ubuntu」
実験のために別環境でインストールしてみたところ、どちらも「Ubuntu」で同名になりました。orz
2022/07にwsl --install -d ubuntuでインストール:「Ubuntu」
2022/07にMicrosoft Storeでインストール:「Ubuntu」
名前は同じですが、アプリとしては別物のようで、それぞれオン・オフできます。
Microsoft Storeの「Ubuntu」(四角アイコン)とwsl --installコマンドの「Ubuntu」(丸アイコン)での違い。
どうもwsl --install -d ubuntuでインストールするものは時期によって名前が変わる模様。ただ、2022/07の時点ではMicrosoft Storeのubuntu.exeで起動したものだけがsystemd PID namespaceに入る機能を持っている模様。
もはやカオス。
まとめ
冒頭の結論を再掲
- Microsoft StoreからUbuntuをインストール
- 設定→アプリ→アプリと機能→その他の設定→アプリ実行エイリアスで新規インストールした方をONにする
- 500MBほどディスク領域を余分に使う
-
C:\Windows\System32\wsl.exe -d Ubuntu --cd ~ -e /bin/bash -c /usr/libexec/nslogin
で起動する- デスクトップなどにショートカットを置く、ランチャーソフト・Windows TerminalのWSL起動コマンドを書き換える必要あり
- Microsoft StoreからUbuntu 22.04 LTSをインストール
- 現在別のWSLインスタンスを使っている場合は作り直しになります。
- 将来24.04にバージョンアップした後もWSLインスタンスの名前が「Ubuntu-22.04」のままです。
設定→アプリ→アプリと機能 で見てもwsl --install -d ubuntuとMicrosoft Storeのものは別物なんですよね。これからインストールするひとはMicrosoft Storeの「Ubuntu」をインストールするのが安全かもしれない。(ただ、Windowsの機能の有効化(仮想マシンプラットフォーム、Linux用Windowsサブシステム)、最新カーネルのダウンロード、WSL ver2をデフォルトにする、という処理を自動でやってくれるのかどうかは疑問)
すでに使用中のUbuntuがある場合は、500MBの容量は無駄ですが1番目の方法がいいかと思います。
できるだけ今の環境を壊したくないとか、exeバイナリが何をしているかわからないというブラックボックスを排除したい場合は2番目ですね。
参考文献
2022/07/20追記:補足書きました。
Discussion