WSL2とsystemdとnslogin(補足)
こちらの記事の補足です。
ubuntu.exeの処理
%LOCALAPPDATA%\Microsoft\WindowsApps\の下にあるexeファイルにsystemd PID namespaceに入るための仕組みがあるのかな?と前の記事で書いたことをもうちょっと調べました。
Windowsにはexeファイルに含まれる文字列を検索するためのstringsコマンドがあります。(Linux(Ubuntu)のstringsコマンドとは別物です)
これをダウンロードしてexeファイルをちょっと覗いてみました。
Microsoft StoreのUbuntu22.04
C:\tmp>strings -nobanner "C:\Program Files\WindowsApps\CanonicalGroupLimited.Ubuntu22.04LTS_2204.0.10.0_x64__79rhkp1fndgsc\ubuntu2204.exe" | findstr systemd
/usr/libexec/wsl-systemd
C:\tmp>strings -nobanner "C:\Program Files\WindowsApps\CanonicalGroupLimited.Ubuntu22.04LTS_2204.0.10.0_x64__79rhkp1fndgsc\ubuntu2204.exe" | findstr nslogin
/usr/libexec/nslogin
Microsoft Storeからダウンロードした「Ubuntu 22.04 LTS」アプリの実行ファイルの中に/usr/libexec/wsl-systemd、/usr/libexec/nsloginという文字列が入っていることが分かります。
Microsoft StoreのUbuntu
C:\tmp>strings -nobanner "C:\Program Files\WindowsApps\CanonicalGroupLimited.Ubuntu_2004.4.4.0_x64__79rhkp1fndgsc\ubuntu.exe" | findstr systemd
/usr/libexec/wsl-systemd
C:\tmp>strings -nobanner "C:\Program Files\WindowsApps\CanonicalGroupLimited.Ubuntu_2004.4.4.0_x64__79rhkp1fndgsc\ubuntu.exe" | findstr nslogin
/usr/libexec/nslogin
Microsoft Storeからダウンロードした「Ubuntu」アプリの実行ファイルの中にも/usr/libexec/wsl-systemd、/usr/libexec/nsloginという文字列が入っていることが分かります。
wsl --install -d ubuntuのUbuntu
C:\tmp>strings -nobanner "C:\Program Files\WindowsApps\CanonicalGroupLimited.UbuntuonWindows_2004.2022.1.0_x64__79rhkp1fndgsc\ubuntu.exe" | findstr systemd
C:\tmp>strings -nobanner "C:\Program Files\WindowsApps\CanonicalGroupLimited.UbuntuonWindows_2004.2022.1.0_x64__79rhkp1fndgsc\ubuntu.exe" | findstr nslogin
「wsl --install -d ubuntu」でダウンロード、インストールした「Ubuntu」アプリの実行ファイルです。wsl-systemd、nsloginという文字列は見つかりませんでした。
以上、3つのバイナリの比較から考えるとやはりMicrosoft Storeからダウンロードしたアプリの実行ファイルの中にnsloginする処理が含まれているものと思われます。
bash -cコマンドについて
前の記事でwsl.exe -d Ubuntu -e /bin/bash -c /usr/libexec/nslogin --cd ~
だとホームディレクトリに移動してくれなくて、wsl.exe -d Ubuntu --cd ~ -e /bin/bash -c /usr/libexec/nslogin
だとちゃんとホームディレクトリに移動してくれるということを書きました。
ただ、前者のコマンドを打っても(ディレクトリ移動しない以外は)問題なくbashの起動できているように見えます。文法エラーとか想定外の処理が起きたりしないの?という点が気になりました。
結論から言うと、WSLとかは関係なく、単純にbashの仕組みでした。
bashのマニュアルから抜粋します。
SYNOPSIS
bash [options] [command_string | file]
-c
If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, the first argument is assigned to $0 and any remaining arguments are assigned to the positional parameters. The assignment to $0 sets the name of the shell, which is used in warning and error messages.
(意訳)-cオプションがあると最初の非オプション引数であるcommand_stringからコマンドが読み込まれます。command_stringの後に引数があると、その最初が$0、続きがpositional parameterに入れられます。$0に入った文字列はシェルの名前であり、警告・エラーメッセージに使われます。
分かるような分からないようなちょっと難しい文章です。
positional parameterというのは$1, $2, $3...というやつです。
/bin/bash -c /usr/libexec/nslogin --cd ~
がどう解釈されるかというと、以下のように解釈されます。
- command_stringが"/usr/libexec/nslogin"
- $0が"--cd"
- $1が"~"
"/usr/libexec/nslogin"というコマンド文字列の中に$0や$1を参照している記述がないので、$0と$1にセットされた文字列は使われることなく、単純に/usr/libexec/nsloginが実行されます。
$0、$1を使う例はこういう感じです。
引数を1個受け取ってそれをparametr=XXXと出力するコマンド文字列'if [ -n "$1" ]; then echo parameter=$1; else echo "USAGE: $0 parameter"; fi'
を-cオプションに渡しています。
コマンド文字列の次に"MY_PROGRAM"という文字列を渡していてこれはコマンド文字列の中で$0として参照されます。
その次に"Hello"という文字列を渡していてこれはコマンド文字列の中で$1として参照されます。
# /bin/bash -c 'if [ -n "$1" ]; then echo parameter=$1; else echo "USAGE: $0 parameter"; fi' MY_PROGRAM Hello
parameter=Hello
$1を渡さない場合にエラーメッセージを出力しています。自プログラムの名前として$0の値を使っています。
# /bin/bash -c 'if [ -n "$1" ]; then echo parameter=$1; else echo "USAGE: $0 parameter"; fi' MY_PROGRAM
USAGE: MY_PROGRAM parameter
さきほどのマニュアルで分かりづらいな、と思った最後の文章は、「$0は一般的にはこういう風に使うよ」ってアドバイスで、絶対そうしなきゃいけないわけじゃなさそうです。
nsloginに引数を渡す場合
(あまり使う意味を感じないですが)nsloginに引数を渡すにはどうするのか?
-cに渡す文字列をダブルクォートでくるみます。環境変数SHELLは(wsl.exeの-eで指定した)/bin/bashで、ps $$で自分のプロセス番号をみると/bin/dashが起動していることが分かります。
C:\>wsl.exe -d Ubuntu -e /bin/bash -c "/usr/libexec/nslogin /bin/dash"
# echo $SHELL
/bin/bash
# ps $$
PID TTY STAT TIME COMMAND
41910 pts/4 S 0:00 /bin/dash
ダブルクォートなしだと先ほどの説明通り$0に/bin/dashが入りますが、誰にも参照されません。(そのため、環境変数SHELLの値である/bin/bashが実行されます)
C:\>wsl.exe -d Ubuntu -e /bin/bash -c /usr/libexec/nslogin /bin/dash
root@Win11:/mnt/c# echo $SHELL
/bin/bash
root@Win11:/mnt/c# ps $$
PID TTY STAT TIME COMMAND
41922 pts/4 S 0:00 /bin/bash
無理やり$0を使うとするとこんな感じです。$0は一般的にはプログラムの名前として使うけど、このようにpositional parameter的に使うこともできます。
C:\>wsl.exe -d Ubuntu -e /bin/bash -c "/usr/libexec/nslogin $0" /bin/dash
# echo $SHELL
/bin/bash
# ps $$
PID TTY STAT TIME COMMAND
41937 pts/4 S 0:00 /bin/dash
参考文献
Discussion