Termux Environment Setup Script

嗨嗨,我愛 Termux

Termux Environment Setup

因為最近買了一隻想拿來亂玩亂刷 ROM 的手機,每次刷都有可能會清掉裡面所存好的資料, 那就會需要重新建立帳號或是環境。其他的程式可能沒有太大的問題, 雖然也沒有使用鈦備份之類的,但大多是登入帳號頂多稍微選個佈景主題之類的就好了, 但還是有些需要有比較多設定,就有點麻煩。

Termux 的環境設定會是需要花比較多步驟才能達到順手的地步。

所以就寫了一個環境設定自動化的 Script,以減少設定成本, 可以好好來刷機而不用擔心設定好麻煩。

About Termux

Termux 是一個在 Android 下的終端機模擬器,但它又附上了一個可以用擴充的套件系統, 就像使用一般 Ubuntu/Debian Linux 一樣,透過 apt 這個命令, 就可以經由網路安裝或是更新程式了。比起之前用的方法,大致上都是在裡面掛上一個真正的 Linux 影像檔,然後再 chroot 過去,**Termux** 這種明顯跟原本的 Android 系統就比較好的整合性,比如可以在 Termux 裡面寫 Script 分享網址, 或是查詢聯絡人之類的,都可以做得到。

關於 Termux 一定會寫好幾篇的。

Termux Environment Setup Script

因為設定 Script 相當個人化,雖然已經儘量減少洩漏但還是有些風險, 所以就沒有放出來公開分享。不過還是會把裡面有用到值得一提的部份寫一下。

Initial script

首先是一開始裝完 Termux 之後,是一個很乾淨的系統狀態下, 要怎麼很快速簡潔又安全地執行一個 Script 呢?就是放到一個自己的網頁位址上, 然後用 goo.gl 縮個網址,就可以得到一組很簡單的短網址, 像是這樣 http://goo.gl/123abc

而乾淨的 Termux 系統中,已經有一個 Busyboxwget 了, 所以只要這樣就可以:

1
$ rm 123abc; wget goo.gl/123abc && bash 123abc

[Y]/n in apt install

接下來要安裝以及更新一些必要的套件,但通常下 apt install 的命令的時候, 都會再問一下說有確定要裝嗎 [Y]/n**?每次都要按個 **Enter**,也是有點麻煩。 關於這個部份,apt** 有提供一個參數 -y**,就可以預設是 **yes 了。

1
2
$ apt upgrade -y
$ apt install -y proot

(yes) and password: in ssh

一樣地,**ssh** 與 scp 命令,也會遇到第一次連線的時候,會需要輸入 (yes)**,已建立新的連線。而在還沒有把 **private key 抓回來之前, 也會需要輸入一次密碼,遇到了就會停下來等待輸入才有辦法繼續,不太自動。

所以會在一開始的時候,就先把密碼問一下,然後再利用 sshpass 這個工具來解決第一次需要密碼的問題。而第一次的連線的時候,可以下 StrictHostKeyChecking=no 參數,避免要等待輸入 **(yes)**。

1
2
3
4
5
echo "Please input password for the first time"
read -s P

[ -f $HOME/.ssh/id_rsa ] && SSHPASS="" || SSHPASS="sshpass -p $P"
$SSHPASS ssh -o StrictHostKeyChecking=no sshserver ls

Here document to config file

因為 Termux 環境跟 Ubuntu/Debian Linux 環境還是有點不一樣, 有些程式所參考的位置,在 Termux 就不一定會出現。像是 /etc/shells 這個檔案會紀錄著 Linux 系統中所有可能的 shells,而 oh-my-zsh 安裝的時候會檢查這個檔案中有沒有 zsh,不然就不裝了。

只好自己產生一個:

1
2
3
4
5
6
7
8
9
# shells
cat > /etc/shells <<EOF
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/tmux
/bin/zsh
/use/bin/zsh
EOF

Multiple stages before/after chroot

好的,問題來了。 如果進去到 Termux 的視窗中,就會發現它的確還是在 Android 底下, 不信可以先 ls / 看看,完全就是個 Android 根目錄的樣子。

shebang

這代表一件事,如果沒有特別處理,開頭長得像這樣的 Script 是沒有辦法直接執行的。

1
#!/bin/sh

這東西有個專有名詞叫作 **Shebang**。

Termux 有提供一個指令 termux-fix-shebang**,它會自動把上面的 **Shebang 改成 Termux 的路徑:

1
#!/data/data/com.termux/files/user/bin/bash

chroot

但是每個 Script 都需要 fix 的話,相當不好用啊。

所以 Termux 也有另外一個指令 termux-chroot**,是基於 **proot 包裝成的 chroot 環境,其實就是把根目錄換成剛剛的 /data/data/com.termux/files

1
2
3
4
$ termux-chroot
$ ls /
bin dev home proc storage tmp var
data etc lib share system usr

chroot in script

在安裝個人環境的 Setup Script,會需要進入到 termux-chroot 環境中的 不然會遇到路徑找不到的錯誤,像是用 repo init -u 的時候,就會有問題。

但是直接寫在 termux-chroot 後面命令,是不會被執行到的,更正確地說, 是離開了 chroot 環境才會繼續執行下去。

1
2
3
4
$ cat setup.sh
...
termux-chroot
ls /

這很常見,因為 termux-chroot 會產生一個互動的 login shell。 通常 chroot 會可以接受參數,指定新環境下要跑的命令。

1
2
$ chroot --help
Usage: chroot [OPTION] NEWROOT [COMMAND [ARG]...]

不過 termux-chroot 就不允許這樣了,看了一下原始碼,是不接受參數的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if [ $# != 0 ]; then
echo "termux-chroot: Setup a chroot to mimic a normal Linux file system"
echo ""
echo "Execute without arguments to run a chroot with traditional file system"
echo "hierarchy (having e.g. the folders /bin, /etc and /usr) within Termux."
exit
fi

...

ARGS="$ARGS $PROGRAM -l"

export HOME=/home
$PREFIX/bin/proot $ARGS

雖然 termux-chroot 不行,但原始碼看得出來 proot 是可以指定 $PROGRAM 等等命令的,因此只要照抄 termux-chroot 的前面部份, 獲得到 $ARGS 變數,換成自己的,就可以寫在 Script 自動執行 chroot 後的命令了。

mutiple stages

所以最後的 Setup Script 架構大概長成這樣子,分成兩階段執行, 這樣就可以放下去跑,然後休息一下,等它們全部設定完就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
zero() {
# before termux-chroot
apt install proot

...

prepare-termux-chroot
export HOME=/home
$PREFIX/bin/proot $ARGS /bin/bash $0 1

}

first() {
# after termux-chroot
ls /
}

if [ $# -eq 0 ]; then
zero
fi
if [ $# -eq 1 ]; then
first
fi

termux-exec (2018)

Sat, 28 Jul 2018 23:22:14 +0800

其實這個 setup script ,現在已經不是上面寫的這種程序了,現在的版本簡單很多, 大致上改了底下幾個部份:

termux-exec

首先就是使用 termux-exec 取代了 proot**,proot** 在某些 Android O 的手機上會有權限的問題,原本需要使用 proot 的地方現在可以使用 termux-exec 來替換了喔,問題就是發生在許多的 Linux scripts 寫的時候都會想要先去找** /bin/sh 或是 /usr/bin/env , 這個上面 **Shebang 也有提到。偏偏 Android 並不是這樣放的 termux-exec 利用 LD_PRELOAD 換掉 execve() ,然後接到 Termux 環境相對應的執行檔,是個相當漂亮又乾淨的作法。

StrictHostKeyChecking

ssh 在第一次連接到 Host 的時候都會問像下面的訊息,

1
2
3
The authenticity of host hostname '(192.188.1.1)' can\'t be established.
ECDSA key fingerprint is SHA256:+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no)?

其實可以直接將 StrictHostKeyChecking no 寫進 .ssh/config 初始設定裡, 避免 setup script 卡住等待回應,

1
2
3
4
5
6
7
[ -d $HOME/.ssh ] || mkdir -p $HOME/.ssh
cat > $HOME/.ssh/config << EOF
Host git
Hostname git.domain.name
User git
StrictHostKeyChecking no
EOF

here document for private key

本來是另外要抓 ssh 的 private key 回來放的,但其實也是可以利用 **Here Documents**,直接嵌入在這個 setup script 裡,如此一來只要抓取一個檔案就夠, 當然這個檔案其實也就不好放在大家都看得到的地方了。

1
2
3
4
5
6
7
# use here document with id_rsa-termux
cat > $HOME/.ssh/id_rsa << EOF_ID_RSA
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
EOF_ID_RSA
mod 600 $HOME/.ssh/id_rsa

repo and zsh

因為 Termux 上的 /usr/bin/env pythonpython3,所以抓下來的 repo 要提換成 python2

1
2
3
mkdir -p $HOME/bin
curl https://storage.googleapis.com/git-repo-downloads/repo | sed 's/env python$/env python2/g' > $HOME/bin/repo
chmod a+x $HOME/bin/repo

而在設定 oh-my-zsh 的時候,本來是需要檢查 /etc/shells,現在則改用替換 oh-my-zsh 的安裝 script 方式,避掉這個檢查,

1
2
3
4
5
6
7
8
9
zsh: SHELL:=bash
zsh:
[ -d ~/.oh-my-zsh ] || { \
if [ "$$OSTYPE" = linux-android ]; then \
curl -L http://install.ohmyz.sh | sed 's/chsh -s.*$$/chsh -s zsh/g' | bash; \
else \
curl -L http://install.ohmyz.sh | bash; \
fi \
}

repo (2019)

Thu, 28 Nov 2019 23:35:34 +0800

某次更新的時候,發覺 repo 自己抓新的 scripts,就出現找不到 env python 類似的訊息,就不能正常使用了。又不想要弄個 proot,所以趁這次機會就把 repo 換成 gitsubmodules,這樣只要 git 一隻程式就可以了。
後來也為了自己的 submodules 寫了一些幫忙的工具,也是相當方便。