“USBデバイスブートモード” と呼ばれる起動方式によるカーネル起動とinitrdからのNFSブートによってRaspberry Pi ZeroなどをmicroSDカードなしで起動してRaspberry Pi OSを動かす。
この起動方式はZero系以外にPi A系/3A+/4BやCompute Module系にも対応しているとされているが、以下はPi Zero系を前提として手元のPi Zero WHについての内容を扱う。
2019年9月上旬に手元の環境で使用していたPi Zero WHが使用開始数日後に異常発熱するようになってしまい、当時異常発熱の原因がはっきりする前にOSの設定をいじっていたところ、SoC温度が75℃を超えた上に再起動などの全ての外部コマンドが突然I/Oエラーにより実行不能となり、危険を感じて直ちにケーブルを抜いて動作を止めたがmicroSDカードがこの時から認識されなくなり、本体に加えて同時に使用していたGPIO接続のサウンドカードも(microSDの故障によりOSが動かせなくなって)使えなくなってとても悔しい思いをした。
前述の通り、Pi ZeroにはmicroSDカードを使用しない起動方法が実際に存在し、記事冒頭のリンク先の公式ドキュメントにも書かれているのだが、異常発熱が起きてmicroSDカードが壊れた時点では目にする機会はなく、新しいカードを買うべきか悩みながら “microSDカードなしでのZero系の起動なんてできないだろう” と思いつつも調べていたら、偶然にもそれがこの方式によって可能だと分かり、途中色々と問題が出たものの、最終的にはOSが動くところまで持っていくことができ、サウンドカードも含めて再び使えるようにできた1。
なお、異常発熱を最初に確認した時に扇風機の風を送ってみたところ、SoC温度は風を送っている間だけ70℃台前半から57℃付近2まで下がったが
といったことから、異常な “熱” はmicroSDカードから来ていた可能性が極めて高いと考えている。
ここからが本題となるが、かなり長くなっている。
nfs-kernel-server
)をインストールしておくdwc2
の使用時のみで、これを使用していない場合は常に前者)起動に成功して稼働中のZeroからlsusb
やdmesg
を実行したときにUSBルートハブなどの情報が出力に含まれている場合、Zeroは(PCと同様)外部機器を制御する側として振る舞っている。この動作モード(ホストモード)ではUSBケーブルを接続してイーサネット通信をすることはできない(USBイーサネット用のネットワークインターフェース自体がZero内で認識されている場合も含む)。
手元のZero WHでは内蔵無線LANを通してNFSサーバ兼アクセスポイント3の3B+(常時稼働)に接続しつつ、USBケーブルはJACK Audio Connection Kit4の音データをPCから送信するためにPCのUSB端子と接続してイーサネット通信を行っている。これはZero WHにある2つの通信手段を両方活用しつつ給電も同時に行える形となる。
無線LANを使用する場合には無線LAN親機(アクセスポイント)が必要なのは当然だが、無線LANネットワーク経由でNFSサーバのマシンに到達できる必要がある。
NFSブートを行うにはinitrdが必要となるのだが、無線LANを使用するためのカーネルモジュールやファームウェアが標準では入っておらず、無線LANインターフェース特有の幾つかの処理を行うためのスクリプトもそれらとあわせて導入する必要がある。これらの追加手順については後述する。
ZeroのHDMIディスプレイ出力を用いずに使いたい場合であっても、Piの起動をさせてしばらく待ってもSSHでのログインができる状態にならなかった場合に問題解決の手がかりとなるエラーメッセージを画面に表示することがあり、実際に手がかりになったことが複数回あったので、Zero系の端子(Mini HDMI)に合うHDMIケーブルは用意しておいたほうがよい。
ただし、1回目の試行でSSHログインまでうまく動けば絶対に必要というわけでもないので、設定などを見直せるだけ見直した上で何回か試して、それでもうまくいかないという場合にのみ用意するとよい。
この準備はPCで行うことを推奨する。以下はDebian/Ubuntuで行うことを前提とする。
rpiboot
というツールを実行し、Raspberry Pi OSのイメージにおける第1パーティション(/boot/
に相当)内の起動用ファイル群(ブートローダや起動についての設定ファイル)をPiに送信するconfig.txt
に記述が必要・設定内容については後述)cmdline.txt
のip=
パラメータに従ってinitrd内でネットワークインターフェースが設定されるnfsroot=
パラメータに従ってinitrdからNFSサーバのファイルシステムに移動してOSが起動するinitrdの作成には別の稼働中のPiを用いる方法もあるが、qemu-user-static
パッケージを導入したDebian/UbuntuでRaspberry Pi OSのOSイメージからディレクトリツリーをコピーしてローカルディスクに配置してchroot
を実行することにする。Raspberry Pi OSの既存のイメージファイルに対してループバックデバイスを用いて操作することもできるが、毎回これを行うのは面倒な上に容量の制約も厳しいためこれは行わない。
下のスクリプトを保存して実行属性を付け、Raspberry Pi OS LiteのOSイメージの場所とローカルディスクの書き込み先ディレクトリを順に引数として指定して管理者権限で実行すると、イメージ内のファイルが指定ディレクトリにコピーされる。
create_raspbian_chroot.sh
ライセンス:CC0
#! /bin/sh
if test $# -ne 2; then
printf "USAGE: %s [RASPBIAN IMAGE] [DEST]\\n" "$0" >&2
exit 1
fi
img="$1"
chrootdir="$2"
if ! mkdir -p "$chrootdir"; then
echo "mkdir -p $chrootdir failed." >&2
exit 1
fi
if ! loopdev=$(losetup -f -P --show "$img"); then
echo "losetup -f -P --show $img failed." >&2
exit 1
fi
if ! mount "${loopdev}"p2 -o ro /mnt; then
echo "mount ${loopdev}p2 -o ro /mnt failed." >&2
losetup -d "$loopdev"
exit 1
fi
if ! cp -a /mnt/* "$chrootdir/"; then
echo "cp -a /mnt/* $chrootdir/ failed." >&2
umount /mnt
losetup -d "$loopdev"
exit 1
fi
umount /mnt
if ! mount "${loopdev}"p1 -o ro /mnt; then
echo "mount ${loopdev}p1 -o ro /mnt failed." >&2
losetup -d "$loopdev"
exit 1
fi
if ! cp -a /mnt/* "$chrootdir/boot/"; then
echo "cp -a /mnt/* $chrootdir/boot/ failed." >&2
umount /mnt
losetup -d "$loopdev"
exit 1
fi
umount /mnt
losetup -d "$loopdev"
echo "Done."
手元では特定バージョンのRaspberry Pi OS Liteに対して
の2つのディレクトリツリーを用意している。
コード中のCHROOT_DIR
を上のスクリプトで配置したディレクトリに合わせる。
/etc/ld.so.preload
の名前変更処理はエラー回避のためのものだが、なくても動作自体に支障はない。
chroot-raspbian.sh
ライセンス:CC0
#! /bin/sh
CHROOT_DIR=/path/to/raspbian/rootfs
for D in dev proc sys dev/shm dev/pts; do mount -o bind /${D} ${CHROOT_DIR}/${D}; done
mv ${CHROOT_DIR}/etc/ld.so.preload ${CHROOT_DIR}/etc/ld.so.preload.orig
chroot ${CHROOT_DIR} /bin/bash
mv ${CHROOT_DIR}/etc/ld.so.preload.orig ${CHROOT_DIR}/etc/ld.so.preload
for D in dev/pts dev/shm dev proc sys; do umount ${CHROOT_DIR}/${D}; done
手元ではZeroのOS用とパッケージのビルド用の2つの環境それぞれにスクリプトを用意している。
以下に記した中の一部は、OSを正しく起動させるために必須となる。一つずつ注意深く処理していく必要がある。
パッケージを削除してOSデータを小さくしたい場合、raspi-config
は依存パッケージの関係で削除対象となることがあるため、同ツールで設定しておきたいことは最初にこのツールで設定しておくとよい。
/etc/fstab
内の “microSDカード内のパーティションをマウント” する記述を削除またはコメントアウト
/etc/systemd/system/multi-user.target.wants/
に入って/lib/systemd/system/ssh.service
への同名のシンボリックリンクを作成する
/etc/ssh/sshd_config
でSSHのポート番号などの設定の変更を行う/etc/wpa_supplicant/wpa_supplicant.conf
に無線LANアクセスポイントの設定を記述/etc/init.d/networking
の先頭付近にexit 0
を記述
/etc/dhcpcd.conf
で設定を行うdpkg-reconfigure
でロケール(locales
)やタイムゾーン(tzdata
)を引数に指定してそれぞれ設定apt install packagename...
,apt remove|purge packagename...
)pi
のパスワードを変更(passwd pi
)したり、必要があれば他のユーザを追加したりする(後からでも可)raspi-config
の実行または/etc/hostname
と/etc/hosts
の直接編集)無線LANネットワークに接続して通信を行うためにはwpasupplicant
パッケージも必要で、Raspberry Pi OSにはLite版でもはじめからインストールされているが、これは間違って削除しないようにする。
カーネルの起動後にNFSサーバに接続してRaspberry Pi OSの起動を行うためにはinitrdの作成とこれを読み込ませるための設定が必要。
ここの設定が不適切だとRaspberry Pi OSの起動が失敗する原因となるので注意が必要。
initramfs-tools
をインストール
busybox
をインストールして、更に必要なファイル群を手動で配置する
initramfs-tools
で無線LANを用いるための仕組みは元々用意されていないが、GitHubのGistにこれを実現するためのサンプルを公開している人がいるので、それを元にZero W/WHの内蔵無線LANを使用するための修正を適用して公開した(元のバージョンの派生版一覧ページから “kakurasan” の “View fork” をたどる)
/etc/initramfs-tools/wpa_supplicant.conf
として保存するファイルは/etc/wpa_supplicant/wpa_supplicant.conf
に近いがctrl_interface=/tmp/wpa_supplicant
の行が入るようにする必要がある
wpa_supplicant.conf
の内容が正しくないと、NFSブートのためのネットワークインターフェースに無線LANを使う場合にインターフェース自体が認識されていても無線LANネットワークに入れずに起動がうまくいかなくなる/etc/initramfs-tools/modules
に以下のモジュール名を記述する(無印Zeroでは必須)
g_ether
libcomposite
u_ether
udc-core
usb_f_rndis
usb_f_ecm
initrdの作成にはupdate-initramfs
を用いるが、カーネルのバージョン(-k
)は/lib/modules/
以下にあるX.Y.Z+
の形(Zeroが使用するカーネルはこの形のもの)のディレクトリ名を用いて指定する。先述のGistのファイル群にあるfinal_commands.sh
の改造版では/lib/modules/
にある-
を含まないバージョンのディレクトリ名からカーネルのバージョンを指定している。
(手動でupdate-initramfsを実行する場合)
QEMU# update-initramfs -k "X.Y.Z+" -c
QEMU# mv "/boot/initrd.img-X.Y.Z+" /boot/initrd.img
(無線LANをinitrdで使うためのスクリプトに実行属性を付けた後で/boot/initrd.imgとしてinitrdを書き出す)
QEMU# /path/to/final_commands.sh
基本的には好みでよい。初期状態でも動作はする。
MODULES
: netboot
のカーネルモジュール群で十分なのでmost
よりinitrdのサイズがやや減らせるCOMPRESS
: gzip
とxz
を比べると結構サイズが変わるので、小さくしたいのであればxz
がよいが、Pi側で伸長する時間も長くなるinitrdを更に小さくしたい場合、MODULES
をlist
にしてinitrdに入れるカーネルモジュールを全て自分で管理するようにしたり、CPIO書庫から不必要なファームウェアファイルを手動で取り除いて書庫を作り直したりするなど、方法はある。
Raspberry Pi OSのイメージファイルを直接使ってもよいが、パーティションが複数あって処理が面倒なので、小さめのext4ファイルシステムのファイルを作って必要最小限のOSデータをここに入れてNFSサーバ側でマウントする形をとることにした。
dd
とmkfs.ext4
を組み合わせてext4ファイルシステムのファイルを用意し、NFSサーバのマシンに送ってNFSサーバ側でループバックマウントするのだが、-T small
オプションを指定すると指定なしと比べてある程度ディスク使用量が減らせた。
作成するイメージのサイズがどれぐらい必要かは、最初に大きめのサイズのファイルを作ってから中身のコピーまでを行ってdf -h
などを実行して使用量と空きを確認してから見直して決めるのがよい。つまりサイズのチェック用と本番用とで2回作業をすることになる。なお、/boot
以下は入れる必要がないので、コピー対象からは除外する。
(400MiBのext4ファイルシステムのファイルを作成する例)
$ dd if=/dev/zero of=raspbian.ext4 bs=1M count=400
$ mkfs.ext4 -m 0 -T small raspbian.ext4
(ファイルシステムをマウントしてローカルディスク上のRaspberry Pi OSのディレクトリの中身をこの中にコピーする)
$ sudo mount -t ext4 -o loop raspbian.ext4 /mnt
$ sudo cp -a /path/to/raspbian/{bin,etc,dev,home,lib,lost+found,media,mnt,opt,proc,root,run,sbin,srv,sys,tmp,usr,var} /mnt/
$ sudo umount /mnt
中身が全てこの中に入ったら、NFSサーバマシンが別の場合にのみ転送を行ってからNFSサーバマシン上でマウントする。
(/pizeroというディレクトリにマウントする場合の例・mkdirは一度行えばその後は不要)
NFSサーバマシン$ sudo mkdir /pizero
NFSサーバマシン$ sudo mount -t ext4 -o loop /path/to/raspbian.ext4 /pizero
下はZeroのIPアドレスを192.168.11.222
に固定した場合のNFSの設定の例。
/etc/exports
/pizero 192.168.11.222(rw,sync,no_subtree_check,no_root_squash)
設定反映も忘れずに行う。
(NFS設定変更の反映・Debian系の場合)
NFSサーバマシン$ sudo systemctl restart nfs-server; sudo exportfs -a
Lite版のRaspberry Pi OSをインストールしても消せるパッケージは多くあるので、NFSサーバ上に配置するOS用ファイルシステムのファイルを小さくしたい場合はファイルシステムへのコピーよりも前に不要なパッケージを削除しておく。apt clean
も実行しておくと空きが確保できる。
手元の環境ではZero用のファイルシステムのファイルを3B+のtmpfs領域に置いているため、メモリ圧迫を最低限にするためにOS内の不要なファイルは可能な限り消す必要があった。
パッケージとして削除できないファイルを動作に支障のない範囲で手動で消してもよい。パッケージ管理システム関係の一時ファイルも結構大きいので、消したほうが領域を確保できる。下はソフトウェアのビルドをしない用途で安全に消せる場所の例。
/usr/include/*
/usr/share/bug/*
/usr/share/common-licenses/*
/usr/share/doc/*
/usr/share/gcc-*
/usr/share/info/*
/usr/share/lintian/*
/usr/share/man/*
/usr/share/mime/*
/usr/share/sounds/alsa/*
/var/lib/apt/lists/*_InRelease
/var/lib/apt/lists/*_Packages
/var/cache/debconf/*-old
/var/lib/dpkg/*-old
/lib/modules/*-*
以下のディレクトリは一部のファイルやディレクトリのみが安全に消せる。
/usr/share/X11/locale/*
/usr/share/zoneinfo/*
/usr/share/locale/*
192.168.11.1:/pizero
192.168.11.222
192.168.11.1
255.255.255.0
zerowh
wlan0
とする場合の例。IPアドレスの固定設定はNFSブート後にサーバ側のZero用OSのファイルシステムにある/etc/dhcpcd.conf
から読み込まれ更新されるため、そちらにも等価な設定を記述する必要がある。
/boot/cmdline.txt
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=192.168.11.1:/pizero rw ip=192.168.11.222:192.168.11.1::255.255.255.0:zerowh:wlan0:off elevator=deadline fsck.repair=yes modules-load=dwc2,g_ether rootwait g_ether.host_addr=11:22:33:44:55:66
ip=
オプションの詳細な説明は以下などを参照: http://archive.linux.or.jp/JF/JFdocs/kernel-docs-2.6/filesystems/nfsroot.txt.html
USBイーサネット通信をNFSブートに用いる場合のネットワークインターフェース名はusb0
となる。無印Zeroの場合は無線LAN機能がないため、USBケーブルで接続された機器をNFSサーバにしてこのネットワークインターフェースで通信を行ってNFSブートを行う形しかとれない。
/boot/config.txt
# Zeroを周辺機器としてUSBケーブルでイーサネット通信するために必要
dtoverlay=dwc2,dr_mode=peripheral
# initrd.imgというファイル名のinitrdを読み込む
initramfs initrd.img followkernel
手元の環境では更にGPIO接続サウンドカードのJustBoom DAC Zeroに関する記述を入れているが、正常に動作している。
dwc2
の行についてはZeroが外部機器を制御するのではなく自身が周辺機器として振る舞う側になれるようにするために必要で、USBイーサネット通信をする場合はこちらのモードで動作している必要がある。
followkernel
はカーネルが使用するメモリ領域のすぐ後ろにinitrdを読み込むという指定。公式のドキュメントに説明がある。
Raspberry Pi公式のリポジトリで開発されている本ツールは、対応しているPi製品をUSBマスストレージデバイスとして使用するのにも使えるが、Raspberry Pi OSのOSイメージ内の第1パーティション(/boot/
)のディレクトリまたはこれを複製したディレクトリを指定することでファイル転送のやりとり5を行ってOSを起動することができる。
ビルドにはlibusb-1.0の開発パッケージ(Debian系ディストリではlibusb-1.0-0-dev
)が必要。
このリポジトリからソースを取得してmake
を実行するとrpiboot
という実行ファイルが生成されるので、これを任意の場所に配置しておく。ソースツリーには他のファイルもあるが、OSの起動にはこの実行ファイル以外のファイルは必要ない。
USB接続されたPiをマスストレージデバイスとして起動する場合は引数を付けずに実行すればよいが、Raspberry Pi OSの起動に用いる場合は事前にconfig.txt
やcmdline.txt
などを含んだディレクトリを-d
オプションで指定する。
この際、事前にconfig.txt
やcmdline.txt
が適切に設定されていないとOSの起動に失敗する。
(実行例と出力例)
$ /path/to/rpiboot -d /path/to/boot
Waiting for BCM2835/6/7
Sending bootcode.bin
Successful read 4 bytes
Waiting for BCM2835/6/7
Second stage boot server
File read: config.txt
File read: start.elf
...
File read: overlays/dwc2.dtbo
File read: overlays/justboom-dac.dtbo
Second stage boot server done
“Second stage boot server done” までが出力されればカーネルまでの起動処理は完了となり、Raspberry Pi OS側も含めて正しく設定されていれば、しばらく経過した後にSSHログインができるようになる。initrdの中やRaspberry Pi OSの起動処理で止まった場合はrpiboot
のメッセージからは何も分からず、Piの画面出力を見ないと詳細を確認できない。
Piがrpiboot
による起動を受け付ける状態となっているのはUSBケーブルをPCなどに接続して給電を開始してからmicroSDからの起動を試みて失敗した後だけとなる(他の状態で実行しても処理が進まない)。このときにlsusb
などではBroadcom社のデバイスがPiと接続された機器から認識される。検出されるデバイスの名前は “BCM2708 Boot” (初期状態)または “BCM2710 Boot” (“Second stage boot server” の段階)となり、デバイスのシリアル番号(“SerialNumber”)もその段階によってデバイス名と同時に変わる。
2019年秋に試したとき、 “Second stage boot server done” の段階で、Raspberry Pi OSのバージョンなどの条件によっては途中でメッセージの出力が止まってしまい、Pi側の画面はずっと虹色画面のままとなってしまうことがあった。
PCで完了しない(x86_64とx86_32の両方向けにビルドして確認)バージョンのカーネルでも3B+に接続してRaspberry Pi OSでビルドしたrpiboot
を実行すると普通に完了するケースもあり、条件はよく分からないが、2017-2018年頃のRaspberry Pi OSだと正しく動く場合が多い印象だった。
rpiboot
にはその後修正が入って新しめのカーネルでも止まらずに起動できるようになった。ただし手元では3B+からの接続でしかまだ試していない。
手元では試していないが、initrdのサイズが大きすぎると起動に失敗するらしい。rpiboot
側の問題は既に修正されているようだが、rpiboot
でのやりとりをしている際にPiのほうで扱えるメモリ領域に制約があるといった見方もされており、ファイルサイズが約32MiBを超えると正しく動かないという。
initrdをカスタマイズしてNFSサーバなども一切使用せずにinitrdだけを用いてOSを動かすことを考えている場合、initrdの圧縮をXZで行っても限界があるということは認識しておく必要がある。
ただ、 “OS用のファイルシステムのデータを別マシンからダウンロードしてPiのtmpfs領域に保存してマウントして使う” という形であればinitrdのスクリプトを編集することで実現可能と考えられ、NFSサーバも不要にできるかもしれない。その場合、Pi側のメモリを圧迫する点には注意が必要。
手元のZero WHではBusterのRaspberry Pi OSを使っているときにUSBケーブルで接続した機器(Ubuntu 19.04のPCとRaspberry Pi OS Stretchの3B+)から “イーサネットガジェット” のデバイスが検出されなかったので調べていたところ、 “2017-06-21では動いていたがStretchになって動かなくなった” という人の書き込みを公式のフォーラムで見つけたのでこの(Jessie時代の末期に出た)バージョンを試してみたところ、PCと3B+のいずれも “イーサネットガジェット” のデバイスと仮想的なネットワークインターフェースがRaspberry Pi OSの起動直後から検出された。
(lsusbでの見え方の例)
PC$ lsusb
...
Bus 001 Device 006: ID 0525:a4a2 Netchip Technology, Inc. Linux-USB Ethernet/RNDIS Gadget
...
PC$ lsusb -t
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/10p, 480M
...
|__ Port 7: Dev 6, If 0, Class=Communications, Driver=cdc_ether, 480M
...
手元のZero WHでは以前USBケーブルの種類を間違えて試しており、これを正しいものに替えてからの検証がまだ不十分なので、今後部分的にソフトウェア(ファームウェアやカーネル)のバージョンを変えてみたり、新しいバージョンのRaspberry Pi OSを試してみたりして追加で調査を行う必要がありそう。
Jessieではinitramfs-tools
で無線LANを使う場合の一部ファイルの書き方を変える必要があり、copy_file()
の代わりにcopy_exec()
を使うようにし、copy_file()
の最初の引数は消してその後ろの引数群をそのまま使うように書き換える。
なお、StretchでもPiに接続したマシンのOSがWindowsであればPiの “イーサネットガジェット” が検出されたという人もいる。
initramfs-tools
のスクリプト群をカスタマイズすることで、用途次第ではNFSサーバすら使わずにrpiboot
から送り出されるinitrdの中身だけでOSを稼働させることができる。
Zeroでは用途を選ぶが、メモリが豊富なモデルのPi 4であれば、GUI環境を含んだOSデータのファイルをLAN内の別マシンに事前に用意し、最小限構成のinitrd環境を起動した後でそれをダウンロードしてその環境に入るというような形でデスクトップ用途でも使えるかもしれない。
前述した通り、USBからのブートにおけるinitrdにはサイズの制約があるようなので、沢山のデータは含めることができない。
自分の用途はサウンドカードを接続したZero WHをUSB接続してSSHでログインして音を出すことだったので、NFSサーバは用いずにinitrd環境を用いる形に落ち着いた。
以下、手元ではJessieを用いているので、基本的にJessie前提の内容となる。
必要なカーネルモジュールはNFSサーバを用いる場合と同様だが、NFSサーバを用いる場合はサーバ上の/lib/modules/
以下の全モジュールが接続後に利用可能なのに対して、initrdのみの環境の場合は稼働中に実際に用いる全てのモジュールを/etc/initramfs-tools/modules
に記述する必要がある。
squashfs
と必要に応じて伸長に必要なモジュールも追加sound
以下にあるサウンド機能のコア部分から個別のドライバに至るまでの多数のモジュールが必要どれが必要なのかが分からない場合、/lib/modules/[バージョン]/
以下の全モジュールをテキストに出力して
QEMU# find /lib/modules/[バージョン]/ -name "*.ko" | while read line; do echo $(basename ${line%.ko}); done > modules
この中から明らかに不要なものだけを消したものを/etc/initramfs-tools/modules
に全て追加して後で不要なものを消すという方法もある。デバイスドライバについては稼働中にlsmod
に表示されたものが必要なものと言える。
SSHログインを行えるようにするため、chroot環境にて軽量なSSHサーバ実装dropbear
とそのinitramfs統合機能をインストールしておく。
(Jessieの場合)
QEMU# apt install dropbear
(Busterの場合)
QEMU# apt install dropbear dropbear-initramfs
Stretchからはinitramfs統合はdropbear-initramfs
という別パッケージに分離している。ファイル/etc/crypttab
がない場合、そのままでは統合機能が動作しないので、空のファイルを置いておく。
rootアカウント用の鍵ペアを作り、Zero上に配置するauthorized_keys
(公開鍵を含むテキストファイル)と、ローカルのssh
クライアントがログイン時に用いる秘密鍵id_rsa
を生成する。
Jessieではこれを手動で行い、適切な内容の/etc/initramfs-tools/root/.ssh/authorized_keys
がある状態にする。
(Jessieの場合)
QEMU# mkdir -m 700 -p /etc/initramfs-tools/root/.ssh
QEMU# dropbearkey -t rsa -s 4096 -f /etc/initramfs-tools/root/.ssh/id_rsa.dropbear | grep ^ssh-rsa | tee /etc/initramfs-tools/root/.ssh/id_rsa.pub > /etc/initramfs-tools/root/.ssh/authorized_keys
QEMU# /usr/lib/dropbear/dropbearconvert dropbear openssh /etc/initramfs-tools/root/.ssh/id_rsa.dropbear /etc/initramfs-tools/root/.ssh/id_rsa
QEMU# chmod 600 /etc/initramfs-tools/root/.ssh/*
その上で/etc/initramfs-tools/initramfs.conf
にDROPBEAR=y
の記述を追加する。
Busterの時点ではroot
のホームディレクトリの名前が変わっている関係で、/etc/dropbear-initramfs/
以下にid_*.pub
またはauthorized_keys
(両方ある場合は後者が優先される)を置いてdropbear-initramfs
の統合を用いてinitrdを作らないとauthorized_keys
が読み込めずにログインできない。こちらのほうが作業は少ないので楽。
(Busterの場合)
QEMU# dropbearkey -t rsa -s 4096 -f /etc/dropbear-initramfs/id_rsa.dropbear | grep ^ssh-rsa > /etc/dropbear-initramfs/id_rsa.pub
QEMU# /usr/lib/dropbear/dropbearconvert dropbear openssh /etc/dropbear-initramfs/id_rsa.dropbear /etc/dropbear-initramfs/id_rsa
いずれのバージョンのOSにおいても、生成されたid_rsa.pub
とid_rsa
はinitrd内には必要なく、ローカルの一般ユーザの~/.ssh/
以下に、必要に応じて名前を変更して保存する。
前者(.pub
ファイル)はログインに必須ではないが、SSHログイン時にload pubkey "/home/username/.ssh/filename": invalid format
形式のエラーが出るのを抑制するために配置しておく。
秘密鍵の名前をid_rsa
以外にした場合はSSHログイン時にssh
コマンドの-i
オプションでこのファイルの場所を指定する。
普通に起動しようとするとローカルのファイルシステムのマウントを試みて失敗して止まってしまうため、代わりにシェルを起動して処理が先に進まないようにする。
/usr/share/initramfs-tools/scripts/local
を/etc/initramfs-tools/scripts/
以下にコピーしてこれを編集することで、既定のlocal
スクリプトの動作をカスタマイズできる。
QEMU# cp -a /usr/share/initramfs-tools/scripts/local /etc/initramfs-tools/scripts/
編集するのはmountroot()
関数のみで、既存の同関数の内容を全て消して以下の内容(シェルを起動し、ログアウトしたら再度実行の繰り返し)に置き換える。処理が先に進まないようにすれば別の内容にしても問題ない。
/path/to/chroot/etc/initramfs-tools/scripts/local
ライセンス:CC0
mountroot()
{
while true; do
sh
done
}
SSHログインに必要なdropbear関係のファイルはinitramfs統合機能が自動的に追加してくれるが、他にファイルを追加したい場合は/etc/initramfs-tools/hooks/
以下にhooks
スクリプトを追加する(要実行属性)。
実行ファイルが動的にリンクする共有オブジェクト(動的リンクライブラリ)はupdate-initramfs
の実行時に自動的に検出されて追加される。
場合によっては依存パッケージを減らすために手動でソフトウェアをビルドしてその実行ファイルの場所を指定する。
/path/to/chroot/etc/initramfs-tools/hooks/custom
ライセンス:CC0
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
copy_exec /bin/nano
copy_exec /lib/terminfo/s/screen
copy_exec /opt/vc/bin/vcgencmd
copy_exec /usr/share/alsa/alsa.conf
Jessieではcopy_file()
が使えないので、実行ファイル以外を追加するのにもcopy_exec()
を用いることになって可読性が悪くなる。
端末内のグラフィカルな制御を行うalsamixer
などは/lib/terminfo/s/screen
のような追加のファイルを必要とする。また、音関係のアプリケーションは/usr/share/alsa/alsa.conf
がないと動かないなど、他にもファイルを追加する必要のある場合がある。
手元の環境では
vcgencmd
(各種情報取得や画面出力オン/オフ制御に使用)alsamixer
とamixer
/usr/lib/arm-linux-gnueabihf/jack/
以下の各ファイルや、/usr/bin/
以下のjack_connect
などの各ツール群も必要)ffmpeg
コマンドmpv
を追加して動いている。
JACKについては、少なくともJessieの場合はmountroot()
内に/dev/shm
をtmpfsとしてマウントする記述を
mountroot()
{
mkdir -p /dev/shm
mount -t tmpfs -o size=192M,rw,nosuid,nodev none /dev/shm
while true; do
sh
done
}
などのように追加しておかないとjackd -d alsa
を実行したときにメモリの確保に失敗してしまう。
microSDカードの故障時には本体やサウンドカードも含めて損傷した可能性も少しは考えていたので、microSDカードなしで実際に使えたのを確認したときに無事と分かって安心した ↩︎
異常発熱する前に安定していた温度で、かつCPUは500MHzにクロックダウンさせていた ↩︎
3B+をアクセスポイントにした理由は単純に自宅に無線LAN親機の専用機がないため ↩︎
“Netjack” と呼ばれ、ネットワーク上の他のマシンにJACK対応アプリケーションの音データをリアルタイムで渡せる ↩︎
一番最初の起動用ファイルをPiに送信した後は “Second stage boot server” というサーバとなってPiからの要求を待ち受けて要求されたファイルを送信する処理を必要なだけ繰り返す ↩︎