概要
Androidで動く一番軽い部類のLinuxであるTermuxの上で、Visual Studio CodeのRemote SSHを動かそうという記事です。
TermuxでVS Code Remote SSHを使うとなると、もう少しまともな(そのかわり重い)Linux技術であるprootを使うのが普通で、調べてもそれしか出てこないのですが、頑張ればproot無しのTermuxでも動きました。
ブラウザで動くcoder/code-serverと違って、プロセス起動を伴うような拡張機能も(全てかは分かりませんが)動作します。
- 追記: code-serverでも、Androidを一般のLinuxとして扱うよう設定することで多くの拡張機能を動作させられるようです。しかし、いずれにしろ操作感がVS Code Remote SSHより劣るのは否定しがたいところです。
glibcとbionic libc、3つの環境変数
まず、VS Code 1.99以降(2025年3月頃~)のLinuxでの動作要件には、以下のようなものが含まれています。
glibc >=2.28, libstdc++ >= 3.4.25
これは、VS Codeが使用するNode.jsの本体やバイナリモジュールの実行に必要なものです。
一方で、たとえばUbuntu 18.04だとglibcバージョンは2.27だったりするなど、未だに使われているLinuxの中にもこの要件を満たせないものは多くあります。
そこで、1.99からは、以下の3つの環境変数が導入され、これによってもともと要件を満たすglibcが入っていない環境でも、自分でglibc関連のファイルを用意すれば(参考例)、(一応サポートされていないという警告は出ますが)VS Code Remote SSHが動かせるようになりました。
- VSCODE_SERVER_CUSTOM_GLIBC_LINKER
- VSCODE_SERVER_CUSTOM_GLIBC_PATH
- VSCODE_SERVER_PATCHELF_PATH
glibcについては、https://github.com/hsfzxjy/vscode-remote-glibc-patchのように有志がビルドしてくれているものを使うことも可能です。
一方でTermuxは、別に古い環境というわけではないのですが、そもそもglibcではなくbionic libcという、GoogleがAndroid用に開発した特殊なlibc環境で動いています。
従ってglibc用にコンパイルされたプログラムは動かないですし、上記のVS Codeの動作要件も満たしません。やはり自分でglibcを導入する必要があります。
glibc導入
実はTermuxにおいては、ビルド済みのglibcがパッケージとして配布されています。
pkg install glibc-repoをしたあとにpkg install glibcとすると入れられます。合計400-500Mくらいあって、多分削れるものもあるんでしょうが、考えるのが面倒なので入れます。一緒に入るglibc-runnerというパッケージにはgrunというコマンドがあり、これはglibc用のバイナリをいい感じに実行してくれるラッパー的なもので、動作確認に便利です。
導入すると、$PREFIX/glibc/以下にbin/とかlib/とかが作られてglibc関連のファイルがたくさん追加されます。
環境変数の設定
sshでログインできる状態にはなっているとして、とりあえずVS Code Remote SSHで接続してみると、以下のようなエラーが出ると思います。
リモート ホストは、glibc および libstdc++ の VS Code Server の前提条件を満たしていない可能性があります (リモート ホストは、VS Code Server を実行するための前提条件を満たしていません)
glibcが入っていてもさきほどの3つの環境変数が設定されていないためです。これをどうにかする必要があります。
VS CodeがRemote SSHをするときはssh -T -D 54321 example.com sh(54321のところはランダムなポート番号)のようなコマンドラインで接続してきます。これに対して環境変数を設定することになりますが、いくつかの選択肢があります。
- authorized_keysでcommandを指定する…基本的には一番まともな方法だと思います。ただし副作用として、通常ログイン(コマンドを指定せずssh example.comだけするとき)で見えるはずのウェルカム的なメッセージ(motd)が見えなくなります。どうせTermuxが相手なのでそれでも困らないとは思いますが、気になるのであれば鍵を分けるとよいです。
- クライアント側のVS CodeのSSHの実行ファイルパス(remote.SSH.path)で独自のラッパーなどを指定し、コマンドライン引数をいじる…サーバー側ではなくクライアント側で解決する方法です。ただプロファイルを分けない限り他のサーバーにも影響するのであまり嬉しくないとは思います。
- .bashrcや.profileのようなファイルで設定する…上記のshコマンドはログインシェルではないので.profileや.bash_profileは読んでくれません。.bashrcについては、他のLinuxでは読んでくれる場合もありますが、Termuxでは($PREFIX/bin/shをdashではなくbashへのシンボリックリンクにした場合でさえ)読んでくれないようです。他に方法があるかもしれませんが、知る限りこの方法はダメそうです。
ここでは一番上の方法を採用します。
具体的には、例えば以下のようなシェルスクリプトを$HOME/ssh-login.shとして作成します。
#!/bin/sh
if [ -z "$1" ]; then
exec $SHELL -l
else
if [ ! -t 0 ] && [ "$1" = "sh" ] ; then #VS Code
export VSCODE_SERVER_CUSTOM_GLIBC_LINKER='/data/data/com.termux/files/usr/glibc/lib/ld-linux-aarch64.so.1'
export VSCODE_SERVER_CUSTOM_GLIBC_PATH='/data/data/com.termux/files/usr/glibc/lib/'
export VSCODE_SERVER_PATCHELF_PATH='/data/data/com.termux/files/usr/glibc/bin/patchelf'
export XDG_RUNTIME_DIR=$TMPDIR
export BASH_ENV=$HOME/.vscode-server/vscode-sleep
exec bash
fi
eval "$1"
exit
そして、authorized_keysで以下のようにします。
command="$HOME/ssh-login.sh \"$SSH_ORIGINAL_COMMAND\"" ssh-ed25519 AAAAC3NzaC1lZ............
これにより、$SSH_ORIGINAL_COMMANDが$1として渡されてきます。これが空文字列のときは引数無しでの通常ログインなのでログインシェルを実行し(前述の通りmotdは出なくなる)、そうでない場合は与えられたコマンドをそのまま実行するが、shかつターミナル割り当て無しのときはVS Codeを想定して環境変数を設定する、というロジックになっています。
ちなみにこの方法はVS Code以外でも結構使える方法だと思います。
XDG_RUNTIME_DIR=$TMPDIRというのは、VS Codeが使う一時フォルダとしてまずXDG_RUNTIME_DIRを見て、無かったら/tmpを使うロジックになっていて、/tmpが読み書き不可でXDG_RUNTIME_DIRもないTermuxでは不十分だからです。無くても接続はできますが、ssh-agent関連(後述)で失敗します。
BASH_ENVとexec bashについては後述します。
ここまでで、さっきのエラーがとりあえず出なくなります。
ダウンロードで無限ループする問題
この状態で接続してみると、「VS Code サーバーをダウンロードしています…」というのが出て、ダウンロードが進行しているのが見えるのですが、ダウンロードと展開が終わったところで、接続が成功せず、また最初からダウンロードが始まって無限ループしてしまいます。
明確なエラーが出ないので結構デバッグが難しいのですが、結論からいうと、これは.vscode-server/cli/servers/Stable-xxxxxx/server/bin/code-serverのshebang(1行目)が#!/usr/bin/env shとなっていて、Termuxには/usr/binがないので実行できない、というのが原因です。(エラーメッセージが出るとしたら、/usr/bin/env: bad interpreter: No such file or directoryのような感じ)
Termuxでは、これに対処するため、LD_PRELOADに$PREFIX/lib/libtermux-exec-ld-preload.soが設定されているのですが、VS Code側の.vscode-server/code-xxxxxxはlibcではなくmuslの静的リンクのバイナリなので効きません。
そこで、termux-fix-shebangという、Termuxで実行できる形にshebangを書き換えてくれるコマンドを使う必要があります。
termux-fix-shebangをする方法①(追記)
- 最初に書いていた方法が不安定だったのでこちらを追記しました。最初の内容も次節に残してあります。
VS Codeのサーバー側ファイルの展開においては、.vscode-server/cli/servers/Stable-xxxxxx.stagingというようなフォルダに展開されてから.stagingが外れる(リネームされる)動作になっています。
そこで、確実にtermux-fix-shebangを実行できるように、先回りして.stagingがないほうのフォルダを作成してしまい、偽物のcode-serverを配置しておきます。
この偽物のcode-serverが、自分自身を削除した上で、本物のcode-serverを配置し、termux-fix-shebangを実行し、本物のcode-serverを実行する、という流れになります。また、この際ついでにserver/bin/remote-cli/code(VS Code Remote SSHの統合ターミナルで使えるcodeコマンド)のほうもtermux-fix-shebangします。
.stagingのmvに関するエラーは出ますが、これでうまくいきます。
まず偽物のcode-serverの中身です。
#!/bin/sh
#remove myself and run the real code-server after fix-shebang
cd $(dirname $0)/../..
ROOT=$(pwd)
rm -rf $ROOT/server
mv $ROOT.staging/server $ROOT/server
rm -rf $ROOT.staging
termux-fix-shebang $ROOT/server/bin/code-server
sed -i '1a unset LD_PRELOAD' "$ROOT/server/bin/code-server"
termux-fix-shebang $ROOT/server/bin/remote-cli/code
sed -i '1a unset LD_PRELOAD' "$ROOT/server/bin/remote-cli/code"
exec $ROOT/server/bin/code-server "$@"
これをvs-code-server-initという名前で保存します。最後のほうのsedについては後述します。
それと同じフォルダに以下のようなスクリプトを作成します。xxxxxx.stagingという名前のフォルダができたら上記のスクリプトをbin/code-serverに配置するだけです。
#!/bin/sh
cd $(dirname $0)
INIT_SCRIPT=$(pwd)/vs-code-server-init
BASE_DIR="$HOME/.vscode-server/cli/servers"
mkdir -p "$BASE_DIR"
echo "Monitoring $BASE_DIR..."
cd $BASE_DIR
while true; do
TARGET_DIR=$(find "$BASE_DIR" -maxdepth 1 -type d \( -name "*.staging" \) | head -n 1)
if [ -n "$TARGET_DIR" ]; then
echo "Target directory found: $TARGET_DIR"
TARGET_DIR2=$(echo $TARGET_DIR | sed "s/\.staging$//")
mkdir -p $TARGET_DIR2/server/bin
cp $INIT_SCRIPT $TARGET_DIR2/server/bin/code-server
exit
fi
sleep 1
done
ここまでの設定がうまくできていれば、無事にサーバーへの接続が成功するはずです。
一旦ダウンロードが済めばこのスクリプトは必要ありません。ただしバージョンアップ時にはまた必要になります。アップデートの頻度は多分1か月に1回とかなのでそんなに困らないと思います。どうせTermuxなんてメインで使わないですし。
termux-fix-shebangをする方法②
- 最初に書いていたのはこちらの方法です。しかしアーカイブの展開順によってはかなりタイミングがシビアになるため、やや不安定に感じます。
アーカイブの展開が終わってからcode-serverが実行されるまでのわずかな時間の間にtermux-fix-shebangを突っ込みます。
#!/bin/sh
BASE_DIR="$HOME/.vscode-server/cli/servers"
mkdir -p "$BASE_DIR"
echo "Monitoring $BASE_DIR..."
cd $BASE_DIR
while true; do
TARGET_DIR=$(find "$BASE_DIR" -maxdepth 1 -type d \( -name "*.staging" \) | head -n 1)
if [ -n "$TARGET_DIR" ]; then
TARGET_FILE="$TARGET_DIR/server/bin/code-server"
echo "Target directory found: $TARGET_DIR"
while true; do
if [ -f "$TARGET_FILE" ]; then
echo "File detected. Starting repetitive patch loop..."
while [ -f "$TARGET_FILE" ]; do
termux-fix-shebang "$TARGET_FILE"
sed -i '1a unset LD_PRELOAD' "$TARGET_FILE"
# sleep 0.01
done
# fix `code`
sleep 0.3
TARGET_DIR2=$(echo $TARGET_DIR | sed "s/\.staging$//")
termux-fix-shebang "$TARGET_DIR2/server/bin/remote-cli/code"
sed -i '1a unset LD_PRELOAD' "$TARGET_DIR2/server/bin/remote-cli/code"
exit 0
fi
# resume if directory is disappeared
if [ ! -d "$TARGET_DIR" ]; then
break
fi
# sleep 0.01
done
fi
sleep 1
done
patchelf・nodeが動かない問題
先ほどのスクリプトでは、code-serverの中でLD_PRELOADをunsetするように変えました。この理由は、デフォルトでLD_PRELOADに設定されている$PREFIX/lib/libtermux-exec-ld-preload.soが、glibcではなくbionic libcのバイナリであり、glibc系のバイナリ(たとえば$PREFIX/glibc/bin/patchelfや、それを使って実際にパッチされたバイナリ)と一緒に実行すると
error while loading shared libraries: /data/data/com.termux/files/usr/glibc/lib/libc.so: invalid ELF header
というようなエラーが出てしまうからです。(結果的にはこれもダウンロードループになります)
glibc-runnerに入っているgrunコマンドはこのあたりも上手くやってくれます。が、VS Code Remote SSHを動かすにあたってはgrunを使うよりLD_PRELOADをunsetする方法のほうがうまく行くようです。
署名の検証ができない
一通り問題なく動作はしているのですが、拡張機能を入れようとすると、「Signature verification failed with 'ENOENT' error」というエラーが出ます。これは、署名の検証に使用される
~/.vscode-server/cli/servers/Stable-xxxxxx/server/node_modules/@vscode/vsce-sign/bin/vsce-sign
というファイルがglibc依存で、依存ライブラリがなくて実行できないからです。
それならばと、grunなどを使ってglibcで実行しようとしても、possible file corruptionとかいうエラーが出てしまいます。このvsce-signはクローズドソースなので、自分でビルドすることもできません。
HTTPSのおかげで、そう簡単に悪意のあるバイナリに置き換えられることはないと思うので、さしあたっては、警告を無視して拡張をインストールするのでも良さそうです。あるいは、vsce-signを
#!/bin/sh
true
のような必ず成功する実行ファイルに置き換えれば、警告は出なくなります。
設定でExtensions: Verify Signatureのチェックを外すこともできますが、リモート先を限定して設定することはできないのであまり望ましくないです。
きちんと検証したい場合は、一応、https://github.com/filiptronicek/node-ovsx-signに非公式な互換実装があるようです。あるいは自前でサーバーを立ててネットワーク経由で検証するという選択肢もあります。
統合ターミナルでのLD_PRELOAD
unset LD_PRELOADをした状態でリモート側サーバーが起動しているので、統合ターミナルでもその状態になります。これにより、普通のTermuxのシェルで問題なく実行できるシェルスクリプトが統合ターミナルでは実行できないという事態が発生します。
VS Codeの内側である統合ターミナルではLD_PRELOADを再び設定しておいても実行に支障はないはずなので、
#!/bin/sh
export LD_PRELOAD=$PREFIX/lib/libtermux-exec-ld-preload.so
exec bash
というスクリプトをmybashという名前で作成して、
terminal.integrated.profiles.linux(リモート先ごとに設定できます)を編集して、
"mybash": {
"path": "/path/to/mybash"
}
を追加しておけばいいです。
ただしcodeコマンドではnodeが内部で実行されるので再びunsetする必要があります。先ほどsedでcodeについてもunsetの行を入れていたのはこのためです。
拡張機能の動作状況
拡張機能については、glibcに依存しないものであれば問題なく動くと思います。例えば、多くの場合、Goのバイナリはlibc非依存ですが、Rustはlibc依存です。従って、(Goで実装されている)Goの拡張機能(golang.go)は基本的に問題なく動きますが、(Rustで実装されている)Rustの拡張機能(rust-lang.rust-analyzer)は、rust-analyzerのパスを変更してgrunを挟むようにしないと動きませんでした。
phantom process killing対策 ①sshをシングルプロセスに
ちょっと前の【Android】非rootなTermuxでTailscaleを動かす - turgenev’s blogで説明した通り、最近のAndroidにはphantom process killingというのがあり、32を超えるプロセスがあるとランダムにkillされます。
普通にVS Code Remote SSHを使っていると結構プロセス数を圧迫するので、いくつか削りたくなります。
まず削れそうな部分として、sshdのプロセスがあります。
sshで新たに接続すると、sshdの下にsshd-session -Rというのが2つできて、その下にようやくbashのようなメインのプロセスが来ます。
この2つのプロセスの目的はわかりませんが、すくなくとも単一ユーザーのTermuxであれば、プロセス1個だけで、公開鍵認証をしつつ適切なコマンドを起動して接続を受けることは可能なはずです。
実際、たとえばhttps://github.com/gliderlabs/sshがこれをやってくれます。以下のように書くことで、さきほどのauthorized_keysと同等の動作をさせることができます。見やすさのため、ssh-login.shにはパスが通っているものとしました。リモート・ローカル双方のポートフォワードも有効です(ローカルのほうは無いとVS Code Remote SSHが動きません)。
func ssh_main() {
authKeyStr := "ssh-ed25519 AAAAC3NzaC1............... user@pc"
pubKey, _, _, _, _ := ssh.ParseAuthorizedKey([]byte(authKeyStr))
publicKeyOption := ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool {
return ssh.KeysEqual(key, pubKey)
})
forwardHandler := &ssh.ForwardedTCPHandler{}
server := ssh.Server{
Addr: "localhost:8522",
Handler: func(s ssh.Session) {
authorizedKey := gossh.MarshalAuthorizedKey(s.PublicKey())
io.WriteString(s, fmt.Sprintf("public key used by %s:\n", s.User()))
s.Write(authorizedKey)
cmd := exec.Command("ssh-login.sh", s.RawCommand())
done := make(chan struct{})
defer close(done)
go func() {
<-s.Context().Done()
if cmd.Process != nil {
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}
}()
ptyReq, winCh, isPty := s.Pty()
if isPty {
cmd.Env = append(os.Environ(), fmt.Sprintf("TERM=%s", ptyReq.Term))
f, err := pty.Start(cmd)
if err != nil {
panic(err)
}
defer f.Close()
go func() {
for win := range winCh {
pty.Setsize(f, &pty.Winsize{
Rows: uint16(win.Height),
Cols: uint16(win.Width),
})
}
}()
go func() {
io.Copy(f, s) // stdin
}()
io.Copy(s, f) // stdout
cmd.Wait()
} else {
cmd.Stdin = s
cmd.Stdout = s
cmd.Stderr = s.Stderr()
cmd.Run()
}
},
LocalPortForwardingCallback: ssh.LocalPortForwardingCallback(func(ctx ssh.Context, dhost string, dport uint32) bool { return true }),
ReversePortForwardingCallback: ssh.ReversePortForwardingCallback(func(ctx ssh.Context, host string, port uint32) bool { return true }),
RequestHandlers: map[string]ssh.RequestHandler{
"tcpip-forward": forwardHandler.HandleSSHRequest,
"cancel-tcpip-forward": forwardHandler.HandleSSHRequest,
},
ChannelHandlers: map[string]ssh.ChannelHandler{
"direct-tcpip": ssh.DirectTCPIPHandler,
"session": ssh.DefaultSessionHandler,
},
}
server.SetOption(publicKeyOption)
//server.SetOption(ssh.HostKeyFile("/data/data/com.termux/files/usr/etc/ssh/ssh_host_dsa_key"))//not supported by x/crypto/ssh
server.SetOption(ssh.HostKeyFile("/data/data/com.termux/files/usr/etc/ssh/ssh_host_rsa_key"))
server.SetOption(ssh.HostKeyFile("/data/data/com.termux/files/usr/etc/ssh/ssh_host_ecdsa_key"))
server.SetOption(ssh.HostKeyFile("/data/data/com.termux/files/usr/etc/ssh/ssh_host_ed25519_key"))
log.Println("starting ssh server...")
log.Fatal(server.ListenAndServe())
}
自分は他のものと一緒に動かしているので、該当の関数だけ書きました。importなどについてはGitHubのサンプルを参照してください。
phantom process killing対策 ②sleep 180を組み込みコマンドに
起動中、ps auxしてみると、sleep 180というプロセスがあることがわかります。これはクライアント側の~/.vscode/extensions/ms-vscode-remote.remote-ssh-0.xxx.x\out\install-script\scripts\linux-exec-server-installer.shの最後の
while true; do sleep 180; printf ' '; done
に由来するもので、VS Codeのウインドウ1つごとに新規に立ち上げられ、アイドル状態での待機のようなことを行っているようです。ウインドウを閉じてしばらくすると親プロセスであるシェルごと消えます。
sleepと同じことを組み込みコマンドでやるにはBash/Zsh + POSIX で sleep 0.01 する方法 #ShellScript - Qiitaのようにreadを使う方法があり、これを使えばsleepのプロセス数を削れます。ついでに秒数も短くしておくと早めに消えてくれます(詳細な仕様は不明)。ただしこのreadの-tオプションはbashなど一部のシェルでしかサポートされていないことには注意が必要です。
実際にreadに置き換えるにあたっては、まず以下のような内容で、$HOME/.vscode-server/vscode-sleep (場所はどこでもOK)を作成しておきます。
unset BASH_ENV
function sleep {
if [ "$1" = "180" ]; then
mkfifo $PREFIX/tmp/sleep.tmp
exec 9<> $PREFIX/tmp/sleep.tmp
rm -f $PREFIX/tmp/sleep.tmp
read -t 10 <&9
else
/bin/sleep $1
fi
}
秒数で分岐させているのは、linux-exec-server-installer.shにはsleep 1やsleep 3なども含まれているからです。
そして、既に載せた通り、ssh-login.shでこのファイルパスをBASH_ENVに設定した上で、exec bashとしています。非インタラクティブシェルなので--rcfileだと効果が出ないようです。先頭でBASH_ENVをunsetしているので、統合ターミナルなどに影響することはありません。
ちなみにsleepやreadではなくwaitなどを使うと、ウインドウを閉じてもいつまで経っても親プロセスであるシェルが生き残ってしまうのでダメです。
その他エラー
これも【Android】非rootなTermuxでTailscaleを動かす - turgenev’s blogの通り、最近のAndroidではネットワークインターフェース取得のAPIが変わったせいで、VS Codeでも
A system error occurred: uv_interface_addresses returned Unknown system error 13 (Unknown system error 13)
というエラーが出ていますが、今のところ具体的な悪影響はありません。
(追記)ssh-agentおよび後日の記事の内容に対応
ssh-agent、およびこれより後に公開した普通のSSHから"code"コマンドでVS Code Remote SSHを起動する方法 - turgenev’s blogの記事の内容への対応に関して若干補足します。
まず後者の記事に関してですが、基本的には環境変数や自動実行のたぐいはssh-login.shでなんでも設定できるのでそんなに苦労することはないです。ただし、gliderlab/sshを使う場合、Unixドメインソケット転送のプルリクがマージされていないので自分で取り込む必要があるのと、記事のように同一パスにUnix socketを複数回転送しようとすると拒否される仕様になっている(OpenSSHはそうではない)ので修正します。
また、ssh-agentに関してですが、上の記事で書いた通り、VS Codeの統合ターミナルに設定されるSSH_AUTH_SOCKは、VS Codeから見たときの元のSSH_AUTH_SOCKへのシンボリックリンクのパスになっています。このシンボリックリンクがXDG_RUNTIME_DIRに置かれるのでssh-login.shでの設定が必要だったわけです。
ただ、ここで1つバグがあり、元のSSH_AUTH_SOCKの長さが64文字を超えるときに、65文字目が2つ並んだ誤ったパス(存在しないパス)がシンボリックリンクのリンク先に設定されてしまいます。Termuxのパスは基本的に/data/data/com.termux/files/...みたいなのが付いていて長くなりがちで、普通のsshdでもgliderlabs/sshでも64文字を若干超えてしまいます。
しかし、ちょうど上記の自分の記事のように(SSH_CONNECTIONを得る目的で)SSH_AUTH_SOCKへのシンボリックリンクをもっと短いパスにして自前で設定しておけば、この問題もついでに解決します。
(追記)F-05Jでの動作確認
#!/data/data/com.termux/files/usr/bin/sh
while true; do
HASH=$(ps -ef | grep "[w]get" | grep "vscode-server.tar.gz" | grep "commit" | sed -n 's/.*commit:\([a-f0-9]\{40\}\).*/\1/p')
if [ -n "$HASH" ]; then
echo hash:$HASH
cd ~/.vscode-server/bin/$HASH
while true; do
if [ -f "bin/code-server" ]; then
sleep 0.001
termux-fix-shebang "bin/code-server"
exit
fi
sleep 0.001
done
fi
sleep 1
done
!/data/data/com.termux/files/usr/bin/sh
#remove myself and run the real code-server after fix-shebang
cd $(dirname $0)/../..
tar -xf bak.tar.gz \
--strip-components=1 \
-C . \
vscode-server-linux-arm64/bin
termux-fix-shebang bin/code-server
termux-fix-shebang bin/remote-cli/code
sed -i '1a unset LD_PRELOAD' bin/remote-cli/code
rm bak.tar.gz
exec bin/helpers/check-requirements.sh "$@"
#!/data/data/com.termux/files/usr/bin/sh
# This version is more stable because there are a lot of time during wget download.
# However, uses more CPU and disk IO for extracting tar again.
cd $(dirname $0)
INIT_SCRIPT=$(pwd)/vs-check-requirements.sh-init
while true; do
HASH=$(ps -ef | grep "[w]get" | grep "vscode-server.tar.gz" | grep "commit" | sed -n 's/.*commit:\([a-f0-9]\{40\}\).*/\1/p')
if [ -n "$HASH" ]; then
echo hash:$HASH
cd ~/.vscode-server/bin/$HASH
while true; do
if [ -f "vscode-server.tar.gz" ]; then
mv vscode-server.tar.gz bak.tar.gz
ln -s bak.tar.gz vscode-server.tar.gz
mkdir -p bin/helpers
cp $INIT_SCRIPT bin/helpers/check-requirements.sh
touch bin/code-server
exit
fi
sleep 0.1
done
fi
sleep 0.1
done