Debian noroot 環境で擬似端末が使用できない問題を回避する

# はじめに

[Debian noroot][DBNR] とは [Android OS][ANDR] 上において root 権限を取ること無く、 [Debian 環境][DEBI]を構築するためのアプリケーションです。
CPU の性能とメモリ容量が潤沢にある Android 端末であれば、 [Debian noroot アプリ][DBNR]の導入によって Android 端末上で非常に軽快な [Debian 環境][DEBI]を実現することができます。

[Debian noroot 環境][DBNR]において、一部の擬似端末を利用したソフトウェアがデバイスファイル /dev/ptmx を経由せずに擬似端末にアクセスしている為に正常に動作しない問題が発生しています。

そこで、 [Android OS][ANDR] の C 標準ライブラリである [bionic のソースコード][BION]から擬似端末関連の関数が含まれる C++ 言語のソースコードを抽出し、そのコードを完全な C 言語に修正した動的ライブラリのコードを作成しました。

そして、 [Debian noroot 環境][DBNR]における C 標準ライブラリの擬似端末関連の関数を [bionic][BION] 由来の関数に置き換えることにより、表記問題を回避することが出来ました。

本稿では、 [Debian noroot 環境][DBNR]において一部の擬似端末を利用したソフトウェアが使用できない問題について、 C 標準ライブラリの擬似端末関連の関数を [bionic][BION] 由来の関数に置き換えて問題を回避するための手法について述べます。

本稿では最初に、 "[Debian noroot アプリについて][CHAP01]" の章において、 [Debian noroot アプリ][DBNR]の概要について述べます。

次に、 "[Debian noroot 環境にて擬似端末が使用できない問題][CHAP02]" の章において、表題で述べられている問題について具体的に述べ、 "[上記問題を回避する][CHAP03]" の章において、問題の具体的な回避方法について述べます。

最後に、 "[結論][CHAP04]" の章において、今回の問題回避に関する結論について述べます。

# Debian noroot アプリについて

[Debian noroot][DBNR] とは、 [Android OS][ANDR] 上において root 権限を取ることなく [Debian 環境][DEBI]を構築するためのアプリケーションです。

CPU の性能とメモリ容量が潤沢にある [Android OS][ANDR] 端末であれば、このアプリケーションの導入によって [Android OS][ANDR] 端末上で非常に軽快な [Debian 環境][DEBI]を実現できます。

現在、当方は手元の [Sony Xperia Z Ultra][ZULT] 及び [Everypad III][EVER] 上に [Debian noroot アプリ][DBNR]をインストールして [Android][ANDR] 上で [Debian noroot 環境][DBNR]を運用しています。

# Debian noroot 環境にて擬似端末が使用できない問題

ここで、 [Debian noroot][DBNR] を使用する上で最も困る問題として /dev/pts/* 以下のデバイスファイルが叩けないために一部の擬似端末を利用したソフトウェアが使用できない問題が挙げられます。

具体的には、端末エミュレータのうち [sakura][SAKU] が全く起動しない不具合や、 [tmux][TMUX] や [screen][SCRE] といった端末多重化ソフトウェアが動作しない不具合が発生しています。

この為に、多くのテキストベースのアプリケーションが導入できないか、もしくは導入が困難になり、非常に [Debian noroot 環境][DBNR]の利便性が著しく低下していることが問題となっています。

# 上記問題を回避する

本章では、[前章][CHAP02]で述べた問題について、 C 標準ライブラリの擬似端末関連の関数を [bionic][BION] 由来のコードの物に置き換える手法に基づいた、表記問題の具体的な回避手法について述べます。

本章では最初に、 "[問題回避の概要][SEC0301]" の節において、擬似端末が使用できない問題の回避についての概要について述べます。

次に、 "[動的ライブラリのコンパイルとインストール][SEC0302]" の節において、問題回避において用いる動的ライブラリのコンパイルとインストールの手法について述べます。

そして、 "[設定と再起動][SEC0303]" の節において、[前節][SEC0303]において行った動的ライブラリのインストール後に行う設定の手法について述べます。

最後に、 "[Linuxbrew による問題回避][SEC0304]" の節において、[前々節][SEC0302]及び[前節][SEC0303]において行った動的ライブラリのインストール及び設定を、ソースコードのビルドに基づいたパッケージ管理システムである [Linuxbrew][BREW] を用いて行う為の手法について述べます。

## 問題回避の概要

本節では、擬似端末が使用できない問題の回避について、擬似端末関連の C 標準ライブラリの置き換えに基づく具体的な手法の概要について述べます。

Android OS において、 [Android OS の標準 C ライブラリである bionic][BION] や各種アプリケーションでは、擬似端末にアクセスする場合は、デバイスファイル /dev/ptmx を経由することになっています。

即ち、上記のような擬似端末が使用できない問題を回避するには、 C 標準ライブラリのうち、擬似端末関連の関数について、デバイスファイル /dev/ptmx を経由して擬似端末デバイスを操作する関数に置き換えれば良いということになります。

- [https://raw.githubusercontent.com/android/platform_bionic/30a36273ab761fb07892f7816b4873b6c48a0cea/libc/bionic/pty.cpp][PTYC]
- [https://raw.githubusercontent.com/android/platform_bionic/30a36273ab761fb07892f7816b4873b6c48a0cea/libc/private/ThreadLocalBuffer.h][TLBH]

そこで、 [bionic][BION] より擬似端末関連の関数を含む上記の C++ 言語のソースコードを基に完全な C 言語に修正したコードを作成し、動的ライブラリとしてコンパイルしました。

そして、その C 言語のコードから生成した動的ライブラリを適切なパスにインストールした後、環境変数 ```LD_PRELOAD``` に動的ライブラリのパスを追加することにより、これらの問題を回避することが可能になりました。

## 動的ライブラリのコンパイルとインストール

まずは [Debian noroot 環境][DBNR]において、```apt-get``` コマンドにより、 [Github][GITH] のリポジトリを取得するための [git][GITS] 及びソースコードをダウンロードする為の [wget][WGET] と C++ のコードをコンパイルするための環境をインストールしておきます。

```shell-session
 # apt-get install git wget
 # apt-get install build-essential
```

次に、 [Github][GITH] 上にある[当方が作成した C 言語のコードが置かれているリポジトリ][LIBA]を ```git clone``` により取得します。

```shell-session
 # git clone https://github.com/z80oolong/libandroid-pty.git
```

その後、ディレクトリ ```./libandroid-pty``` にカレントディレクトリを移動して ```make``` コマンドを実行し、動的ライブラリ ```libandroid-pty.so``` が生成されていることを確認します。

```shell-session
 # cd ./libandroid-pty
 # make
 arm-linux-gnueabihf-gcc -o libandroid-pty.so -fPIC -shared -Wall -Os -Wl,--version-script,libandroid-pty.map libandroid-pty.c -lpthread
 arm-linux-gnueabihf-strip libandroid-pty.so
 cp -p libandroid-pty.so libandroid-pty.so.arm
 #
```

最後に、生成された動的ライブラリ ```libandroid-pty.so``` を ```libandroid-shmem.so``` 等と同様にルートディレクトリにインストールします。

```shell-session
 # make install INSTALL_DIR=/
```

もし、ソースコードをコンパイルする事が煩わしい場合や何らかの問題で困難である場合は、次に示す URL から下記のようにダウンロードして、 ```libandroid-pty.so.{arm,x86-32,x86-64}``` を下記のようにルートディレクトリにインストールしても構いません。

- 安定版 (v0.1.1)
    - [libandroid-pty.so のバイナリファイル (ARM 対応) → **https://git.io/libandroid-pty.so-0.1.1.arm**][LARM]
    - [libandroid-pty.so のバイナリファイル (32 ビット x86 対応) → **https://git.io/libandroid-pty.so-0.1.1.x32**][LX32]
    - [libandroid-pty.so のバイナリファイル (64 ビット x86 対応) → **https://git.io/libandroid-pty.so-0.1.1.x64**][LX64]

```shell-session
 $ wget -O libandroid-pty.so.arm https://git.io/libandroid-pty.so-0.1.1.arm		# (ARM 用のバイナリファイルのダウンロードの場合)
 $ wget -O libandroid-pty.so.x86-32 https://git.io/libandroid-pty.so-0.1.1.x32	# (32 ビット x86 用のバイナリファイルのダウンロードの場合)
 $ wget -O libandroid-pty.so.x86-64 https://git.io/libandroid-pty.so-0.1.1.x64	# (64 ビット x86 用のバイナリファイルのダウンロードの場合)
 $ sudo install -m 0700 libandroid-pty.so.arm /libandroid-pty.so				# (x86-32 搭載の端末の場合はinstall -m 0700 libandroid-pty.so.x86-32 /libandroid-pty.so)
																				# (x86-64 搭載の端末の場合はinstall -m 0700 libandroid-pty.so.x86-64 /libandroid-pty.so)
```

## 設定と再起動

[前節][SEC0302]で述べた動的ライブラリのインストールを行った後は、ルートディレクトリ直下にある ```/proot.sh``` において、環境変数 ```LD_PRELOAD``` の設定が書かれている行を下記のように修正して [Debian noroot][DBNR] を再起動します。

```sh:/proot.sh
...
 # export "LD_PRELOAD=/libdisabledselinux.so /libandroid-shmem.so"
 export "LD_PRELOAD=/libdisabledselinux.so /libandroid-shmem.so /libandroid-pty.so"　# 末尾に /libandroid-pty.so を追加する。
...

```

再起動後は、 [tmux][TMUX], [screen][SCRE] といった端末多重化ソフトウェアや [sakura][SAKU] 等の一部のターミナルエミュレータをインストールしてこれらが正常に動作することを確認します。

## Linuxbrew による問題回避

[前々節][SEC0302]及び[前節][SEC0303]において述べた擬似端末に関する問題回避のための動的ライブラリは、Linux の各ディストリビューション上におけるソースコードの取得及びビルドに基づいたパッケージ管理システムである [Linuxbrew][BREW] を利用することによっても導入を行うことが可能です。

本節では、 [Linuxbrew][BREW] に基づいて前述の動的ライブラリの導入を行い、問題回避のための設定を行う手法について述べます。

まずは、 "[Debian noroot 環境に Linuxbrew を導入する][ZOLQ]" の投稿及び [Linuxbrew の公式ページ][BREW]に基づいて [Linuxbrew][BREW] に依存するパッケージと [Linuxbrew][BREW] 本体のリポジトリを導入します。

```shell-session
 $ sudo apt-get install build-essential curl file git python-setuptools ruby
 $ sudo apt-get install m4 texinfo libbz2-dev libcurl4-openssl-dev libexpat-dev libncurses-dev zlib1g-dev gettext    # 必要に応じて任意に導入する。
 $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install)"
```

なお、 [32 bit ARM アーキテクチャ][ARMP]及び [64 bit x86 アーキテクチャ][X86P]以外を搭載した携帯端末にインストールされた [Debian noroot 環境][DBNR]に [Linuxbrew][BREW] を導入する場合、前述の [Linuxbrew][BREW] の本体のリポジトリを導入するコマンドを実行すると、途中で [Linuxbrew][BREW] が使用する [ruby 処理系][RUBY]の bottle ファイルの取得に失敗して、導入作業が正常に行われません。

従って、これらの携帯端末における環境の場合は、 "[Linuxbrew の自動的なインストールスクリプトが異常終了する場合における代替の導入手法][ZOQ2]" の投稿に基づき、 [Linuxbrew の開発コミュニティ][BREW]による [Linuxbrew][BREW] のインストールスクリプトに代えて、下記のようにして [Linuxbrew][BREW] を導入する必要が有ります。

```shell-session
 $ sudo apt-get install build-essential curl file git python-setuptools ruby
 $ sudo apt-get install m4 texinfo libbz2-dev libcurl4-openssl-dev libexpat-dev libncurses-dev zlib1g-dev gettext    # 必要に応じて任意に導入する。
 $ sudo apt-get install libssl-dev libreadline6-dev libyaml-dev libgdbm-dev libdb-dev
 $ sh -c "$(curl -fsSL https://raw.githubusercontent.com/z80oolong/linuxbrew-altinstall/master/install.sh)"
```

そして、 ```bash``` の初期化ファイルである ```${HOME}/.bashrc``` について、以下の通りに環境変数の設定を行い、  ```bashrc``` を再起動します。

```sh:${HOME}/.bashrc
...
# for Linuxbrew

export HOMEBREW_PREFIX="/home/linuxbrew/.linuxbrew"
export PATH="${HOMEBREW_PREFIX}/sbin:${HOMEBREW_PREFIX}/bin:$PATH"
export MANPATH="${HOMEBREW_PREFIX}/share/man:${MANPATH}"
export INFOPATH="${HOMEBREW_PREFIX}/share/info:${INFOPATH}"
export HOMEBREW_NO_AUTO_UPDATE=1
...
```

次に、以下の通りに ```brew tap``` コマンド及び ```brew install``` コマンドを実行して、[当方が作成した C 言語による動的ライブラリ][LIBA]を導入します。

```shell-session
 $ brew tap z80oolong/debian-noroot
 $ brew install -dv z80oolong/debian-noroot/libandroid-pty
```

**この時、ソースコードのコンパイルに若干時間が掛かると思われますが、我慢強くインストールの完了を待つ必要が有ります。**

ここで、 ```brew install``` コマンドの完了時に、以下のような ```Caveats``` で始まるメッセージが表示されます。なお、このメッセージは ```brew info z80oolong/debian-noroot/libandroid-pty``` を実行しても表示させることが出来ます。

```shell-session
...
==> Caveats
To use a preloaded dynamic library `libandroid-pty.so`, set environment variable LD_PRELOAD to `/home/linuxbrew/.linuxbrew/preload/libandroid-pty.so`:

for example:    
$ export LD_PRELOAD="... /home/linuxbrew/.linuxbrew/preload/libandroid-pty.so"
or
$ /usr/bin/env LD_PRELOAD="... /home/linuxbrew/.linuxbrew/preload/libandroid-pty.so" linux_command
...
```

最後に、ルートディレクトリ直下にある ```/proot.sh``` において、環境変数 ```LD_PRELOAD``` の設定が書かれている行を下記のように修正して [Debian noroot][DBNR] を再起動します。

```sh:/proot.sh
...
 # export "LD_PRELOAD=/libdisabledselinux.so /libandroid-shmem.so"
 export HOMEBREW_PREFIX="/home/linuxbrew/.linuxbrew"                                             # ここに、環境変数 HOMEBREW_PREFIX は、 Linuxbrew 本体のリポジトリのインストール先のディレクトリ。
 export "LD_PRELOAD=/libdisabledselinux.so /libandroid-shmem.so ${HOMEBREW_PREFIX}/preload/libandroid-pty.so"　# 末尾に ${HOMEBREW_PREFIX}/preload/libandroid-pty.so を追加する。
...

```

ここで、**環境変数 ```LD_PRELOAD``` に設定する動的ライブラリのパス名の設定は、必ず ```${HOMEBREW_PREFIX}/preload/libandroid-pty.so``` を用いることに注意して下さい。**

**これは、 [Linuxbrew][BREW] を用いて動的ライブラリを導入する際に、 ```brew install``` コマンドが既存に導入した動的ライブラリを削除若しくは上書きすることを防ぎ、```brew install``` コマンドの完了まで既存の動的ライブラリを残すことで、 [Debian noroot 環境][DBNR]全体が不具合を起こすのを防ぐ為です。**

# 結論

[Debian noroot 環境][DBNR]において、擬似端末を利用したソフトウェアが使用できない問題を回避するために、  [bionic のソースコード][BION]から擬似端末関連の関数が含まれるソースコードを抽出して動的ライブラリとしてコンパイルし、環境変数 ```LD_PRELOAD``` の設定を変更することにより、 [Debian noroot 環境][DBNR]の C 標準ライブラリの擬似端末関連の関数を [bionic][BION] 由来の関数に置換しました。

また、本稿で述べた一連の問題回避のための動的ライブラリのインストール及び設定に、 Linux の各ディストリビューション上におけるソースコードの取得及びビルドに基づいたパッケージ管理システムである [Linuxbrew][BREW] を利用することが有用であることも述べました。

これにより、 [tmux][TMUX], [screen][SCRE] といった端末多重化ソフトウェアや [sakura][SAKU] 等の一部のターミナルエミュレータが正常に動作し、上記問題を回避することが可能になったことを確認しました。

特にテキストベースのソフトウェアの多くが問題無く導入できるようになり、 [Debian noroot][DBNR] の利便性が向上しました。

# 謝辞

本稿の記述に当たって、 [bionic][BION] の開発に携わった [Android Open Source Project][AOSP] に関わる全ての方々に心より感謝致します。

また、 [Android OS][ANDR] 端末上で非常に軽快な [Debian 環境][DEBI]を実現することを可能にした [Debian noroot 環境][DBNR]の開発者である [pelya 氏][PELY]に心より感謝致します。

そして、[本稿で述べた一連の問題回避のための動的ライブラリ][LIBA]の導入にあたってパッケージ管理システムとして使用した [Linuxbrew][BREW] の導入に関しては [thermes 氏][THER]による "[Linuxbrew のススメ][THBR]" 及び [Linuxbrew の公式ページ][BREW]等各種関連記事を参考にしました。 [thermes 氏][THER]及び [Linuxbrew の開発コミュニティ][BREW]の各氏に心より感謝致します。

最後に、 [Debian noroot 環境][DBNR]と [Android OS][ANDR] 及び [Debian 環境][DEBI]の全ての事に関わる全ての皆様に心より感謝致します。

# 追記とお断り

## 2017/03/21 現在の追記とお断り

2017/03/21 現在の [bionic のソースコード][BION]において、 [pthread][PTHR] における [thread local buffer 関連の大幅なソースコード][TLBH]の変更に伴い、本稿の加筆及び修正を行いました。どうか御了承下さい。

## 2017/08/27 現在の追記

[bionic のソースコード][BION]を基にした [C 言語による動的ライブラリのコード][LIBA]の作成に伴い、本稿の修正を行いました。

## 2017/10/31 現在の追記

本稿において、 [Linuxbrew][BREW] を基にした[問題回避のための動的ライブラリ][LIBA]の導入に関する記述の追記を行いました。

## 2018/01/22 現在の追記とお断り

[64 bit ARM アーキテクチャ等の環境で動作する Debian noroot 環境への Linuxbrew の導入に関する問題の解決手法][ZOQ2]について加筆を行い、本稿を改稿致しました。どうか御了承下さい。

## 2018/02/04 現在の追記とお断り

[Linuxbrew][BREW] を[自動的にインストールするための代替となるスクリプトの作成と公開][GIT1]に伴い、本稿を改稿致しました。どうか御了承下さい。

## 2018/04/02 現在の追記とお断り

バイナリファイル ```libandroid-pty.so``` を直接ダウンロードできる URL を追記しました。どうか御了承下さい。

## 2018/06/08 現在の追記とお断り

[Linuxbrew][BREW] を[自動的にインストールするための代替となるスクリプト][GIT1]の更新と、代替スクリプトの実行手法の変更に伴い、本稿を改稿致しました。どうか御了承下さい。

## 2018/07/26 現在の追記とお断り

git リポジトリ版の動的ライブラリのバイナリファイルの同梱の廃止に伴い、本稿の改稿を行いました。改稿が遅れましたことを御詫び致しますと共に、どうか御了承下さいますよう御願い致します。

## 2018/10/11 現在の追記とお断り

[Linuxbrew][BREW] を[自動的にインストールするための代替となるスクリプト][GIT1]の実行の際に、 [Linuxbrew][BREW] が使用する [ruby 処理系][RUBY]をソースコードからコンパイルする際に必要となるパッケージについて訂正を行いました。どうか御了承下さい。

## 2018/12/12 現在の追記とお断り

"[動的ライブラリのコンパイルとインストール][SEC0302]" の節において、一部誤字が有りました。心より御詫び致しますと共に、本稿を訂正致しました。

## 2018/12/25 現在の追記とお断り

"[Linuxbrew による問題回避][SEC0304]" の節において、 [Linuxbrew][BREW] による libandroid-pty.so の導入手法について、大幅な簡略化に伴う変更がありましたので、当該節について改稿を行いました。

<!-- 各章及び各節のリンク一覧 -->

[CHAP01]:#debian-noroot-アプリについて
[CHAP02]:#debian-noroot-環境にて擬似端末が使用できない問題
[CHAP03]:#上記問題を回避する
[CHAP04]:#結論
[SEC0301]:#問題回避の概要
[SEC0302]:#動的ライブラリのコンパイルとインストール
[SEC0303]:#設定と再起動
[SEC0304]:#linuxbrew-による問題回避

<!-- 外部リンク一覧 -->

[PTHR]:http://man7.org/linux/man-pages/man7/pthreads.7.html
[DBNR]:https://play.google.com/store/apps/details?id=com.cuntubuntu&hl=ja
[ANDR]:https://www.android.com/intl/ja_jp/
[DEBI]:https://www.debian.org/index.ja.html
[ZULT]:http://www.sony.jp/tablet/products/Z_ultra/
[EVER]:http://www.yamada-denki.jp/service/everypad3/
[SAKU]:http://www.pleyades.net/david/projects/sakura
[TMUX]:https://github.com/tmux/tmux/wiki
[SCRE]:https://www.gnu.org/s/screen/
[BION]:https://github.com/android/platform_bionic/tree/master/libc/bionic
[PTYC]:https://raw.githubusercontent.com/android/platform_bionic/30a36273ab761fb07892f7816b4873b6c48a0cea/libc/bionic/pty.cpp
[TLBH]:https://raw.githubusercontent.com/android/platform_bionic/30a36273ab761fb07892f7816b4873b6c48a0cea/libc/private/ThreadLocalBuffer.h
[GITH]:https://github.com/
[GITS]:https://git-scm.com/
[WGET]:https://www.gnu.org/software/wget/
[LIBA]:https://github.com/z80oolong/libandroid-pty.git
[LARM]:https://git.io/libandroid-pty.so-0.1.1.arm
[LX32]:https://git.io/libandroid-pty.so-0.1.1.x32
[LX64]:https://git.io/libandroid-pty.so-0.1.1.x64
[LHAM]:https://git.io/libandroid-pty.so-head.arm
[LH32]:https://git.io/libandroid-pty.so-head.x32
[LH64]:https://git.io/libandroid-pty.so-head.x64
[BREW]:https://linuxbrew.sh
[ZOLQ]:https://qiita.com/z80oolong/items/bfa986438adf8aac51ab
[ZOQ2]:https://qiita.com/z80oolong/items/61feb20a9356532a15da
[GIT1]:https://github.com/z80oolong/linuxbrew-altinstall
[RUBY]:https://www.ruby-lang.org/ja/
[ARMP]:https://www.arm.com/
[X86P]:https://www.intel.co.jp/content/www/jp/ja/homepage.html
[AOSP]:https://source.android.com/
[PELY]:https://github.com/pelya
[THER]:https://qiita.com/thermes
[THBR]:https://qiita.com/thermes/items/926b478ff6e3758ecfea