📑

WSL2とsystemdとnslogin(補足)

2022/07/20に公開

こちらの記事の補足です。

https://zenn.dev/takai404/articles/8088f8688018de

ubuntu.exeの処理

%LOCALAPPDATA%\Microsoft\WindowsApps\の下にあるexeファイルにsystemd PID namespaceに入るための仕組みがあるのかな?と前の記事で書いたことをもうちょっと調べました。

Windowsにはexeファイルに含まれる文字列を検索するためのstringsコマンドがあります。(Linux(Ubuntu)のstringsコマンドとは別物です)
これをダウンロードしてexeファイルをちょっと覗いてみました。

https://docs.microsoft.com/ja-jp/sysinternals/downloads/strings

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

参考文献

https://unix.stackexchange.com/questions/144514/add-arguments-to-bash-c

Discussion