株式会社はてなに入社しました
自宅kubernetesクラスタでTwitchのチャット読み上げツールを自作した話
概要
4年と9ヵ月働いたヤフーを退職して暇になったので、新しい盆栽を初めたという話。
システム構成
用意したもの
- kubernetes (1 master, 2 nodes)
- CoreDNS + etcd (物理サーバ, docker-composeで動いてる)
- TwitchとOAuthで認証するためのアプリケーション https://github.com/MysticDoll/twitch-oauth-docker/tree/k8s
- voicevoxのDeployment, Service, Ingress一式
- Twitchのチャットを拾ってきてvoicevoxのクエリに変更するWebSocketサーバ https://github.com/MysticDoll/tc2vv
- 雑にWebAudio APIで読み上げするフロントエンド(nginx pod生やして対応)
kubernetes、 CoreDNS+etcd
kubeadmを使って雑に構築、ingress-nginx-controller, flannel, metallb, external-dnsあたりを採用しています。以下を参考にして構築しました。
以下雑記
- ingress-nginx-controllerについては、別でrtmpのコンポーネントが動いていたりするので、tcp-services周りの設定を入れていますが、それ以外はほぼバニラです。
- external-dnsでは
*.mystic.doll
を良い感じに解決するように設定しています。
TwitchとOAuthで認証するやつ
Rustで書きました。axum
で雑に書きました。
今まではTwitchのAccessTokenを取るのに以下のシェルスクリプトでやっていたんですが、こちらをkubernetes上に移し、ついでにkubernetesのSecretとして保存するようにしました。
#/bin/bash echo access "https://id.twitch.tv/oauth2/authorize?client_id=${TWITCH_CLIENT_ID}&redirect_uri=http://localhost:8000/twark/&response_type=code&scope=chat:read+chat:edit+channel:read:redemptions" CODE=$(nc -lp8000 | head -1 | cut -d'?' -f2 | cut -d'=' -f2 | cut -d'&' -f1) export TWITCH_ACCESS_TOKEN=$(curl -XPOST "https://id.twitch.tv/oauth2/token?client_id=${TWITCH_CLIENT_ID}&client_secret=${TWITCH_CLIENT_SECRET}&code=${CODE}&grant_type=authorization_code&redirect_uri=http://localhost:8000/twark/" | jq -r '.access_token')
kubernetesのAPIを叩くクライアントのcrateもあるにはあるが、ドキュメントを読んで面倒になったので、自分でリクエスト投げることにしました。
kubernetesのAPIでSecretがなければ登録、あれば更新、というのが見つからなかったので自前で実装した結果可読性が最惡になっています。
voicevoxのあれこれ
解説が面倒なのでmanifestsを全部まとめて下記に置いておきます。 大体以下のことをしています。
- kubernetes上にpod配置してServiceでクラスタ内、ingressでクラスタ外からアクセスできるようにしてます。
- Default設定ではAccess-Control-Allow-OriginとかCORS周りの設定が厳しめなので、これを緩くしています。
- 適当に試していたときユーザー設定のところに置いた状態で初回起動したとき設定が反映されなくてキレたのでdefaultを置き換えています。
- そもそも初回起動時に設定をを反映させる手段がundocumentedだったのでコード読んでたんですがpythonのappdirsのデフォルトに配置されるっぽいがうまくいかなかった
--- apiVersion: apps/v1 kind: Deployment metadata: name: voicevox labels: app: voicevox spec: replicas: 1 selector: matchLabels: app: voicevox template: metadata: labels: app: voicevox spec: containers: - name: voicevox image: voicevox/voicevox_engine:cpu-ubuntu20.04-latest ports: - containerPort: 50021 volumeMounts: - name: default-setting mountPath: /opt/voicevox_engine/default_setting.yml subPath: default_setting.yml volumes: - name: default-setting configMap: name: voicevox-config items: - key: "default_setting.yml" path: "default_setting.yml" --- apiVersion: v1 kind: Service metadata: name: voicevox annotations: external-dns.alpha.kubernetes.io/hostname: voicevox.mystic.doll. spec: selector: app: voicevox ports: - name: voicevox protocol: TCP port: 80 targetPort: 50021 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: voicevox spec: ingressClassName: nginx rules: - host: voicevox.mystic.doll http: paths: - path: / pathType: Prefix backend: service: name: voicevox port: number: 80 --- apiVersion: v1 kind: ConfigMap metadata: name: voicevox-config data: default_setting.yml: | allow_origin: "*" cors_policy_mode: all
Twitchのチャットを拾ってきてvoicevoxのクエリに変更するWebSocketサーバ
やりたいこととしては WebSocket<TwitchChat> -> WebSocket<VoiceVoxQuery>
というマッピングを実現するサーバです。
通常読み上げ自体をやりたければ、フロントエンド側でチャットに接続→チャットの内容をvoicevoxに投げる、というだけで良いです。 ですが、これを実装しようと思うとフロントエンド側にAccessTokenを渡す必要があり、Secretsに渡した意味が皆無になってしまうので、こういった実装になりました。
tokio-tungstenite
でChatに接続、Message::Text
を受け取る度にvoicevoxにリクエストして、今自分が喋ってるWebSokectサーバに繋いできてるpeerにvoicevoxの音声合成クエリをbroadcastしています。
詳しくはリポジトリのコードを見てください。
雑にWebAudio APIで読み上げするフロントエンド
雑にnginxで一式ホストしています。Manifestとしては以下。
真面目にアプリケーションを書く必要が0なので、雑にFetchしてきてblobをWebAudio APIに投げるようなコードを書いています。
--- apiVersion: v1 kind: ConfigMap metadata: namespace: twitch-tools name: tts-html data: index.html: | <html> <body> <script> const connectWebSocket = async () => { const socket = new WebSocket("ws://tc2vv.mystic.doll"); socket.addEventListener("message", async (e) => { const data = JSON.parse(e.data); if (data.kana.length < 40) { const ctx = new AudioContext(); const res = await fetch("http://voicevox.mystic.doll/synthesis?speaker=1", {method:"POST", body: e.data, headers: {"Content-Type": "application/json"}}); const arrayBuffer = await res.arrayBuffer(); const audioBuffer = await ctx.decodeAudioData(arrayBuffer); const source = ctx.createBufferSource(); source.buffer = audioBuffer; source.connect(ctx.destination); source.start(); } }); socket.addEventListener("close", () => { connectWebSocket(); }); }; connectWebSocket(); </script> </body> </html> --- apiVersion: apps/v1 kind: Deployment metadata: name: tts namespace: twitch-tools spec: replicas: 2 selector: matchLabels: app: tts template: metadata: labels: app: tts spec: containers: - name: nginx image: nginx ports: - containerPort: 80 volumeMounts: - name: index-html mountPath: /usr/share/nginx/html/index.html subPath: index.html volumes: - name: index-html configMap: name: tts-html items: - key: "index.html" path: "index.html" --- apiVersion: v1 kind: Service metadata: name: tts namespace: twitch-tools spec: selector: app: tts ports: - name: tts protocol: TCP port: 80 targetPort: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tts namespace: twitch-tools spec: ingressClassName: nginx rules: - host: tts.mystic.doll http: paths: - path: / pathType: Prefix backend: service: name: tts port: number: 80
まとめ
普通に棒読みちゃんとか使ったほうがいいです。
あとうちみたいな場末の配信には別に読み上げいらないです。
令和最新版、全国1億2000万人のサンリオワールドスマッシュボーラーに贈るネトスマ環境導入ガイド
はじめに
一人用モードのRTAや友達との対戦等、様々な遊び方がありますが、今回はエミュレータを用いたネットワーク対戦、通称「ネトスマ*1」環境の導入方法をお伝えしていきます。
この記事の内容を実践する際は全て自己責任で行ってください。
必要なもの
- サンリオワールドスマッシュボール! のカセット
- レトロフリークカートリッジアダプター(SFC用)
- USB-A to USB-micro Bのケーブル
- RetroFreakDumper (ソフトウェア)
- Fightcade2 (対戦ソフト)
- 対戦用パッチ (SWSB discord内で配布中 招待リンク)
- WinIPS (パッチ適用のためのソフトウェア)
吸い出し
RetroFreakDumper をダウンロードし、起動します。このとき、COMポートの一覧に表示されている COM<数字>
を覚えておきます。
次にレトロフリークカートリッジアダプターにカセットを挿入し、PCに接続します。
その後RetroFreakDumperを再起動します。先程起動したときにCOMポートの一覧に表示されていなかった COM<数字>
がカートリッジアダプターに対応する COM<数字>
になるので、これを選択し、選択ボタンを押します。
選択ボタンを押すと、カセットの情報と吸い出しボタンが出るので、吸い出しボタンを押して吸い出しを開始します。設定は特に弄らなくて大丈夫です。
吸い出しが完了したら、RetroFreakDumperのあるフォルダ内の Roms\SFC
フォルダに Sanrio World Smash Ball!.sfc
というファイルが出力されます。これで吸い出しは完了になります。
対戦用パッチ
SWSBのコミュニティでは通常のROMでは2P側が不利となるバグが存在するため、有志により作成頂いた対戦用パッチを利用して対戦しています。
対戦用パッチをこちらのdiscordのリンクからダウンロードします。(事前にdiscordに参加しておいてください)
WinIPSを実行し、IPSファイル
に対戦用パッチ swsb_tournament.ips
を、 パッチするファイルに先程吸い出した Sanrio World Smash Ball!.sfc
を指定し、適用ボタンを押します。
パッチが成功すれば、対戦用パッチのフォルダ内に swsb_tournament.sfc
というファイルが出力されているはずです。
Fightcade2の準備
先程パッチした swsb_tournament.sfc
をZIP圧縮し、smashbal.zip
として保存しておきます。
https://www.fightcade.com/register からユーザー登録し、
https://www.fightcade.com/からFightcade2をダウンロードしインストールしてください。その後先程作成した smashbal.zip
を <Fightcadeのインストール先フォルダ>\Fightcade\emulator\snes9x\ROMs
フォルダに移動します。
インストール先フォルダが分からない場合は、Fightcadeのショートカットを右クリックし、ファイルの場所を開く→ ROMs
フォルダ → SNES9x ROMs
のショートカット の順でアクセスすればROMsフォルダに到達できます。
Fightcade2を起動したら先程登録した内容でログインし、左の検索アイコンから SANRIO WORLD SMASH BALL!
を検索し、+JOINを押します。
Fightcade2起動確認
左側にチャンネルが追加されているので、こちらをクリックし、右上のTEST GAME
からゲームを起動します。
SNES9xが起動するので、Input
→ Input Configuration
から入力設定を変更してください。斜め入力の設定も忘れずにしておきましょう。
また、Emulation
→ Pause When Inactive
のチェックもはずしておきましょう。
設定が終わったらSNES9xを終了して大丈夫です。
対戦
左下のアイコンをクリックにし、自分のログイン状態を Online
にしてください。
オンラインのユーザー名を選択すると対戦を申し込むことができます。対戦を申し込んだ側のプレイヤーが1Pとなります。
まとめ
以上でネトスマ環境導入は完了です。 不明な点等あればSWSB discordや、私宛てにTwitter等でリプライして頂ければ対応しますので、ご気軽にご相談ください。
全人類ネトスマやりましょう。
how to execute the binary in Windows host from the process in WSL2 guest VM
背景
最近久々に視聴者が操作を注入できるFall Guysを配信*1 でやろうとしていたのだが、久々にやろうとしたらbotのコードを一切変えてないのに操作を注入するpowershell scriptがLinux側から実行できなくなる現象が起きた。 結論としてはWindows 10 ビルド19042.782でのバグ(あるいはデフォルト設定の変更?)っぽいことが分かり、Windows Updateを適用したところ解決した。
この記事ではせっかくなのでWSL2 VMからのWindowsホストBinaryの実行について掘り下げたいと思う。
その際に調査した内容はあまり有益ではないので興味ある人だけ読んで欲しい。
調査
まず、問題のWSL2からWindows binaryが実行できない状況について以下の事実が確認できた。ただし実行ログなどは残っていないためあまり信用しないでほしい。
Build 19042.782 環境
- powershell から
wsl
コマンドでbash
を実行する際/mnt/c/Users/MysticDoll
以下ではpowershell.exe
が実行可能。$HOME
上ではpowershell.exe
を実行した際、以下の現象が起きた- ^C がttyから送信できない、またttyの応答が消える(仮想端末のキー入力が無視される)
- dbus-daemonを起動しそこから起動したgnome-terminalをX転送しWindows上のX serverで利用している際
- powershell.exe 等Windows側のバイナリを起動しようとした場合 ^C などが効かない(上記
$HOME
での実行時と同じ状態)- この時
$PWD
については関係なく同様の結果が得られた
- この時
- powershell.exe 等Windows側のバイナリを起動しようとした場合 ^C などが効かない(上記
また、Build 19042.789 においては上記は再現せず、問題なくWindows Binaryの実行が可能であった
WSL2からWindows Binaryを実行できる仕組み
こちらのドキュメントにWIndows/Linuxの相互運用性として纏められている、こちらの 相互運用性の無効化
の項目を見ると
ユーザーは、ルートとして次のコマンドを実行することで、1 つの WSL セッションに対して Windows ツールを実行する機能を無効にできます。
echo 0 > /proc/sys/fs/binfmt_misc/WSLInterop
Windows バイナリを再び有効にするには、すべての WSL セッションを終了して bash.exe を再実行するか、ルートとして次のコマンドを実行します。
echo 1 > /proc/sys/fs/binfmt_misc/WSLInterop
相互運用の無効化は、WSL セッション間で保持されません。新しいセッションが開始されると、相互運用は再び有効になります。
何やら /proc/sys/fs/binfmt_misc/WSLInterop
という procfs のなにかで管理しているらしい。
binfmt_misc
binfmt_miscというのは好きなフォーマットのバイナリを好きなインタプリタで解釈して実行できるようにするためにLinuxに用意されたカーネルの機能らしい www.kernel.org
とりあえずWSL2を起動して当該のbinfmt_miscを見てみると以下の出力が得られる
mysticdoll@Himalayan:~$ cat /proc/sys/fs/binfmt_misc/WSLInterop enabled interpreter /tools/init flags: F offset 0 magic 4d5a
とりあえず分かることは
- 現在有効である
/tools/init
というインタプリタで評価される- F フラグであるということ(他namespace/chroot環境下でも動くためにlazyにbinaryをロードせず、configuration timeにロードする? あまり分かっていない)
- 対象となるbinaryは
0x4d5a
から始まっている *2
ということである。
つまりWindows binaryを実行している本体は /tools/init
であり、これがどういうものかというのが知りたいわけである。
/tools/init
を探す
「探す」と言っている通り、WSL上で ls /tools/init
して見つからないというわけである。よってなんらかの検索をする前に思いつく方法で探すだけ探してみようと思う。
find
古典的なfindをする、当然こんな方法で見つかるとは思っていないが一応やってみることにした。
結果を載せても文字数の無駄なので割愛します
適当なWindows binaryを実行し、 /proc/{pid}/exe
を見てみる
とりあえず仮想端末の別のタブで PowerShellを起動してみる。するとそれっぽいのがいるのが分かる。
mysticdoll@Himalayan:~$ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 1396 784 ? Sl Feb07 0:00 /init mysticd+ 1145 0.0 0.0 10000 5128 pts/2 Ss 16:57 0:00 bash mysticd+ 1156 0.0 0.0 804 4 pts/2 S+ 16:57 0:00 /tools/init /mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe
とりあえず /tools/init
を使ってるのは 1156 らしいので見てみる
mysticdoll@Himalayan:~$ ls -al /proc/1156/exe lrwxrwxrwx 1 mysticdoll mysticdoll 0 Feb 8 16:59 /proc/1156/exe -> /tools/init
どうやらリンク先ファイルはユーザランドからは見れない様子なので諦めるしかないっぽい。
strace
でbash経由から動作を見る
$ sudo strace -p {pid} -f
で対象となるbashをtraceし、powershell.exeを実行する。trace結果は以下。
[pid 1297] execve("/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe", ["powershell.exe"], 0x55b487b67ae0 /* 25 vars */) = 0 [pid 1297] arch_prctl(ARCH_SET_FS, 0x29c800) = 0 [pid 1297] set_tid_address(0x29c838) = 1297 [pid 1297] brk(NULL) = 0xf55000 [pid 1297] brk(0xf56000) = 0xf56000 [pid 1297] sched_getaffinity(0, 128, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) = 32 [pid 1297] getpid() = 1297 [pid 1297] getcwd("/home/mysticdoll", 4096) = 17 [pid 1297] uname({sysname="Linux", nodename="Himalayan", ...}) = 0 [pid 1297] getcwd("/home/mysticdoll", 4096) = 17 [pid 1297] open("/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_PATH) = 3 [pid 1297] readlink("/proc/self/fd/3", "/mnt/c/WINDOWS/System32/WindowsP"..., 4095) = 61 [pid 1297] fstat(3, {st_mode=S_IFREG|0555, st_size=452608, ...}) = 0 [pid 1297] stat("/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe", {st_mode=S_IFREG|0555, st_size=452608, ...}) = 0 [pid 1297] close(3) = 0 [pid 1297] open("/proc/self/mountinfo", O_RDONLY) = 3 [pid 1297] readv(3, [{iov_base="", iov_len=0}, {iov_base="33 24 8:16 / / rw,relatime - ext"..., iov_len=1024}], 2) = 1024 [pid 1297] readv(3, [{iov_base="", iov_len=0}, {iov_base="de=755\n46 45 0:28 / /sys/fs/cgro"..., iov_len=1024}], 2) = 1024 [pid 1297] readv(3, [{iov_base="", iov_len=0}, {iov_base="group cgroup rw,net_prio\n57 45 0"..., iov_len=1024}], 2) = 461 [pid 1297] readv(3, [{iov_base="", iov_len=0}, {iov_base="", iov_len=1024}], 2) = 0 [pid 1297] close(3) = 0 [pid 1297] getcwd("/home/mysticdoll", 4096) = 17 [pid 1297] open("/proc/self/mountinfo", O_RDONLY) = 3 [pid 1297] readv(3, [{iov_base="", iov_len=0}, {iov_base="33 24 8:16 / / rw,relatime - ext"..., iov_len=1024}], 2) = 1024 [pid 1297] readv(3, [{iov_base="", iov_len=0}, {iov_base="de=755\n46 45 0:28 / /sys/fs/cgro"..., iov_len=1024}], 2) = 1024 [pid 1297] readv(3, [{iov_base="", iov_len=0}, {iov_base="group cgroup rw,net_prio\n57 45 0"..., iov_len=1024}], 2) = 461 [pid 1297] readv(3, [{iov_base="", iov_len=0}, {iov_base="", iov_len=1024}], 2) = 0 [pid 1297] close(3) = 0 [pid 1297] ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0 [pid 1297] ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0 [pid 1297] ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0 [pid 1297] ioctl(0, TIOCGPGRP, [1297]) = 0 [pid 1297] getpgid(0) = 1297 [pid 1297] fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x2), ...}) = 0 [pid 1297] fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x2), ...}) = 0 [pid 1297] fstat(2, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x2), ...}) = 0 [pid 1297] ioctl(0, TIOCGWINSZ, {ws_row=55, ws_col=166, ws_xpixel=0, ws_ypixel=0}) = 0 [pid 1297] ioctl(0, SNDCTL_TMR_START or TCSETS, {B38400 -opost -isig -icanon -echo ...}) = 0 [pid 1297] dup(0) = 3 [pid 1297] socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0) = 4 [pid 1297] bind(4, {sa_family=AF_VSOCK, sa_data="\0\0\377\377\377\377\377\377\377\377\0\0\0\0"}, 16) = 0 [pid 1297] getsockname(4, {sa_family=AF_VSOCK, sa_data="\0\0\322\22z\211\377\377\377\377\0\0\0\0"}, [16]) = 0 [pid 1297] listen(4, 4) = 0 [pid 1297] socket(AF_UNIX, SOCK_SEQPACKET, 0) = 5 [pid 1297] connect(5, {sa_family=AF_UNIX, sun_path="/run/WSL/948_interop"}, 110) = 0 [pid 1297] write(5, "\6\0\0\0\356\0\0\0\322\22z\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 238) = 238 [pid 1297] accept4(4, {sa_family=AF_VSOCK, sa_data="\0\0]p\351\360\2\0\0\0\0\0\0\0"}, [16], SOCK_CLOEXEC) = 6 [pid 1297] accept4(4, {sa_family=AF_VSOCK, sa_data="\0\0^p\351\360\2\0\0\0\0\0\0\0"}, [16], SOCK_CLOEXEC) = 7 [pid 1297] accept4(4, {sa_family=AF_VSOCK, sa_data="\0\0_p\351\360\2\0\0\0\0\0\0\0"}, [16], SOCK_CLOEXEC) = 8 [pid 1297] accept4(4, {sa_family=AF_VSOCK, sa_data="\0\0`p\351\360\2\0\0\0\0\0\0\0"}, [16], SOCK_CLOEXEC) = 9 [pid 1297] close(4) = 0 [pid 1297] rt_sigprocmask(SIG_BLOCK, [INT WINCH], NULL, 8) = 0 [pid 1297] signalfd4(-1, [INT WINCH], 8, 0) = 4 [pid 1297] poll([{fd=0, events=POLLIN}, {fd=7, events=POLLIN}, {fd=8, events=POLLIN}, {fd=9, events=POLLIN}, {fd=4, events=POLLIN}], 5, -1) = 1 ([{fd=9, revents=POLL IN}]) [pid 1297] recvfrom(9, "\t\0\0\0 \0\0\0", 8, MSG_WAITALL, NULL, NULL) = 8 [pid 1297] brk(0xf58000) = 0xf58000 [pid 1297] recvfrom(9, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 24, 0, NULL, NULL) = 24 [pid 1297] poll([{fd=0, events=POLLIN}, {fd=7, events=POLLIN}, {fd=8, events=POLLIN}, {fd=9, events=POLLIN}, {fd=4, events=POLLIN}], 5, -1) = 1 ([{fd=7, revents=POLL IN}])
このあたりがもの凄く怪しい
[pid 1297] connect(5, {sa_family=AF_UNIX, sun_path="/run/WSL/948_interop"}, 110) = 0
ここから推測するに、socketとして生えている /run/WSL/{id?}_interop
を経由してプロセス生成関連のやり取りをしていそう。
このbashで Exploroer.exe
を起動する場合以下のstrace出力が得られる。
[pid 1443] execve("/mnt/c/WINDOWS/Explorer.exe", ["Explorer.exe", "."], 0x55b487b67ae0 /* 25 vars */) = 0 (中略) [pid 1443] socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0) = 4 [pid 1443] bind(4, {sa_family=AF_VSOCK, sa_data="\0\0\377\377\377\377\377\377\377\377\0\0\0\0"}, 16) = 0 [pid 1443] getsockname(4, {sa_family=AF_VSOCK, sa_data="\0\0\327\22z\211\377\377\377\377\0\0\0\0"}, [16]) = 0 [pid 1443] listen(4, 4) = 0 [pid 1443] socket(AF_UNIX, SOCK_SEQPACKET, 0) = 5 [pid 1443] connect(5, {sa_family=AF_UNIX, sun_path="/run/WSL/948_interop"}, 110) = 0 [pid 1443] write(5, "\6\0\0\0\254\0\0\0\327\22z\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\09\0\0\0Q\0\0\0~\0\0\0\2\0\0\0u\0\0\0L\0000\1\1C:\\WINDOWS\\Explorer.exe\0\\\\wsl$\\Ubuntu-20.04\\home\\mysticdoll\0WSLENV=\0\0Explorer.exe\0.\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 172) = 172 (中略) [pid 1443] exit_group(1) = ? [pid 1443] +++ exited with 1 +++
ここからも /run/WSL/948_interop
がなにやらプロセスに対して引数他を渡していることが分かる。
また、PowerShellが出力した文字をLinux側に出力している部分を見ると
read(7, "Windows PowerShell\33[63X\r\nCopyright (C) Microsoft Corporation. All rights reserved.\33[24X\r\n\33[81X\r\n\346\226\260\343\201\227\343\201\204\343\202\257\343\203\255\343\202\271\343\203\227\343\203\251\343\203\203\343\203\210\343\203\225\343\202\251\343\203\274\343\203\240\343\20 1\256 PowerShell \343\202\222\343\201\212\350\251\246\343\201\227\343\201\217\343\201\240\343\201\225\343\201\204 https://aka.ms/pscore6", 4096) = 200
とあり、よって fd が7のsocketを探すと
socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0) = 4 (中略) accept4(4, {sa_family=AF_VSOCK, sa_data="\0\0&q\351\360\2\0\0\0\0\0\0\0"}, [16], SOCK_CLOEXEC) = 7
となっているため、対話部分の実装については vsock(7)
*3を立てることでホストマシン上のプロセスとやりとりしていることが分かった。
まとめ
これらから以下が推測できる
- WSL2上からのプロセス実行は
binfmt_misc
によって実現されている binfmt_misc
のinterpretor/tools/init
は以下の事をしている
細かいところは適当だけどひとまずこのあたりで終わりにします。