M5Stack UnitV2でtmuxを動かす

# TL;DR

- M5Stack UnitV2上で動作するtmuxをビルドした
- 手っ取り早くビルドするために、ARM用UbuntuのDockerイメージを使った
- terminfoやlocaleの追加、パーミッション修正が必要だった

# はじめに

先日、[M5Stack UnitV2](http://docs.m5stack.com/en/unit/unitv2)を購入しました。Linux搭載のAIカメラということで、色々と遊べそうです。  
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">M5Stack UnitV2が気になったので購入しました。 <a href="https://t.co/av0VIgqqcz">pic.twitter.com/av0VIgqqcz</a></p>&mdash; NALGO Lab. (@NalgoLab) <a href="https://twitter.com/NalgoLab/status/1547129376671936512?ref_src=twsrc%5Etfw">July 13, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

PCからSSH接続することも可能で、初期状態でも`git`や`scp`、`zsh`など、比較的豊富なコマンドが入っているようでした。  
しかし、`tmux`や`screen`のようなターミナルマルチプレクサは無いようでした。  

OSは[Buildroot](https://buildroot.org/)という組み込み用Linuxのようで、パッケージマネージャで手軽に何かを追加する、という訳にもいかなさそうでした。  
困りました。多数のシェルを同時に操る現代人にとって、ターミナルマルチプレクサは生活必需品と言えるでしょう。[^kojin]

仕方ないので自分で`tmux`をビルドすることにしました。

[^kojin]: 個人の感想です。

# 環境

今回ビルドに使用したマシンの環境は以下です。
- OS: Ubuntu 18.04 (x86_64)[^sorosoro]
- Docker: 20.10.17

[^sorosoro]: そろそろ22.04とかにしたい。

# ARM用UbuntuのDockerコンテナを起動できるようにする

UnitV2は32bit ARM環境なので、ARM用のtmuxをビルドする必要があります。  
わざわざクロスコンパイル環境を用意するのも面倒[^zigcc]なので、ARM環境用のUbuntuコンテナ内でtmuxをビルドします。  
この方法であれば、通常通りビルドするだけで自動的にARM用のtmuxバイナリが得られます。

まずはx86_64な環境でARM用コンテナを起動出来るようにします。  
以下のコマンドを実行すれば良いです。[^qemu-user-static]

```shell-session
$ docker run --rm --privileged docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
```

以下のコマンドの結果を確認し、`linux/arm/v7`が表示されていればOKです。[^binfmt]  
この状態になると、コンテナ内のARM用バイナリが自動的にQEMU経由で実行されるようになるため、  
ARM用コンテナの起動やARM用Dockerイメージのビルドが可能になります。

```shell-session
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS  PLATFORMS
default * docker
  default default         running linux/amd64, linux/386, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/arm/v7, linux/arm/v6
```

それではARM用Ubuntuのコンテナを起動します。  
普通に起動するとx86_64用のイメージが選択されるため、ARM用を指定する必要があります。  
まずは以下のコマンドで対応アーキテクチャ一覧を確認します。
```shell-session
$ docker buildx imagetools inspect ubuntu:22.04
Name:      docker.io/library/ubuntu:22.04
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest:    sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac

Manifests:
  Name:      docker.io/library/ubuntu:22.04@sha256:bace9fb0d5923a675c894d5c815da75ffe35e24970166a48a4460a48ae6e0d19
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/amd64

  Name:      docker.io/library/ubuntu:22.04@sha256:02ef83b72fb2fb709c356d0669a433555bcc5d07e31dc0016d036f1ca201bf13
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/arm/v7

  Name:      docker.io/library/ubuntu:22.04@sha256:0f744430d9643a0ec647a4addcac14b1fbb11424be434165c15e2cc7269f70f8
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/arm64/v8

  Name:      docker.io/library/ubuntu:22.04@sha256:37897c7c32dfc8f0b7de513548b48dd967186f3aec144a6d031ab7943ef1abde
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/ppc64le

  Name:      docker.io/library/ubuntu:22.04@sha256:30b98fdf87f8f8fa733c15f1ffb8bb931ae63fed4b203987162910682ad51284
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/riscv64

  Name:      docker.io/library/ubuntu:22.04@sha256:cd3d86f1fb368c6a53659d467560010ab9e0695528127ea336fe32f68f7ba09f
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/s390x
```

`linux/arm/v7`用のイメージがあるので、DIGESTを指定してコンテナを起動します。
```shell-session
$ docker run -it --rm ubuntu:22.04@sha256:02ef83b72fb2fb709c356d0669a433555bcc5d07e31dc0016d036f1ca201bf13
Unable to find image 'ubuntu:22.04@sha256:02ef83b72fb2fb709c356d0669a433555bcc5d07e31dc0016d036f1ca201bf13' locally
sha256:02ef83b72fb2fb709c356d0669a433555bcc5d07e31dc0016d036f1ca201bf13: Pulling from library/ubuntu
5aeb313108e1: Pull complete
Digest: sha256:02ef83b72fb2fb709c356d0669a433555bcc5d07e31dc0016d036f1ca201bf13
Status: Downloaded newer image for ubuntu@sha256:02ef83b72fb2fb709c356d0669a433555bcc5d07e31dc0016d036f1ca201bf13

root@f0b8a9d045a0:/# uname -m
armv7l
```
無事起動できました。

[^zigcc]: 最近だと[`zig cc`](https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html)などで簡単に出来たりするのかも。  
[^qemu-user-static]: [multiarch/qemu-user-static](https://hub.docker.com/r/multiarch/qemu-user-static/) でも可能。  
[^binfmt]: 最近の公式ドキュメントには[tonistiigi/binfmtを使用するように書かれています](https://docs.docker.com/build/buildx/multiplatform-images/)が、手元の環境では`linux/arm/v7`が表示されませんでした。  


# tmuxをビルドする

起動したDockerコンテナの中で作業します。

## tmux本体のビルド

```shell-session
root@f0b8a9d045a0:/# apt update
root@f0b8a9d045a0:/# apt install git make autoconf automake pkg-config bison gcc libevent-dev libncurses-dev
root@f0b8a9d045a0:/# git clone https://github.com/tmux/tmux.git ~/tmux
root@f0b8a9d045a0:/# cd ~/tmux
root@f0b8a9d045a0:~/tmux# sh autogen.sh
root@f0b8a9d045a0:~/tmux# ./configure --enable-static
root@f0b8a9d045a0:~/tmux# make -j
```

aptで依存ライブラリを入れて普通にmakeするだけですが、`./configure`時に`--enable-static`オプションを付けるのがポイントです。  
これで依存無しのシングルバイナリが得られます。

```shell-session
root@f0b8a9d045a0:~/tmux# ./tmux -V
tmux next-3.4
root@f0b8a9d045a0:~/tmux# ldd tmux
        not a dynamic executable
```

得られたtmuxバイナリは`docker cp`等でコンテナから取り出し、  
UnitV2上のパスが通った場所(`/usr/bin/tmux`など)に配置しましょう。

## terminfoの追加

UnitV2上でtmuxを起動すると、そのままでは表示が崩れます。[^backspace]  
`tmux-256color`のterminfoがあれば良いため、こちらも[用意しておきます](https://github.com/Homebrew/homebrew-core/issues/102748#issuecomment-1145383249)。

```shell-session
root@f0b8a9d045a0:~/tmux# infocmp -x tmux-256color > tmux-256color.src
root@f0b8a9d045a0:~/tmux# tic -x tmux-256color.src
```

これでコンテナ内に`/etc/terminfo/t/tmux-256color`が生成されます。  
こちらを取り出してUnitV2上の`/usr/share/terminfo/t/tmux-256color`に配置すれば良いです。

[^backspace]: バックスペースでカーソルが右に移動するなど

# localeの追加

UnitV2上でtmuxを起動すると、そのままでは以下のようなエラーが発生します。

```shell-session
unitv2% tmux
tmux: need UTF-8 locale (LC_CTYPE) but have ANSI_X3.4-1968
```

こちらはコンテナ内の`/usr/lib/locale/C.utf8`を取り出し、UnitV2上の同じ場所に配置すれば良いです。  
配置後に一度UnitV2を再起動すると、エラーが発生しなくなります。

# パーミッション修正

localeのエラーを解消すると、今度は「rootユーザでしかtmuxを起動出来ない」[^root]という状態になります。  
`/dev/ptmx`のパーミッションの問題[^strace]なので、修正してやれば起動出来るようになります。

```shell-session
unitv2% sudo chmod 666 /dev/ptmx
```

再起動すると元に戻ってしまうため、シェルスクリプト化して`/etc/init.d/`に置いておくと良いでしょう。  

[^root]: 一般ユーザで起動すると、エラー表示もなく即座に終了してしまいます。
[^strace]: `strace`の結果を眺めていたら判明しました。

# 起動

ここまで対応すれば、以下のように無事tmuxが正常起動出来るようになります。

![cap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542709/e5fdcbf0-6d8a-6ee9-18cd-a89f1e4789e9.png)

この後、普段PCで使用している`.tmux.conf`を使ってみたりもしましたが、特に問題もなく動作していました。

# おわりに

今回はM5Stack UnitV2上で`tmux`コマンドを使用出来るようにしました。  
今後はカメラやマイクなどの機能を使用して、何かしてみたいと思います。