23 April 2024

Raspberry Pi Zero WにRaspberry Pi OS Liteをインストールする時の考察(Debian bookworm 12版)

Raspberry Pi OS (Debian bookworm 12)のインストールを、Raspberry Pi Imagerと、OSイメージファイルのSDカード直接書き込みで「処理の中身」がどう違うのか考察してみた。

OSのバージョンアップにより、2018年5月に書いた『 Raspberry Pi Zero Wの初期設定(Wifi/SSH有効化, /tmpと/var/logのRAM Disk化, 基本ツール類インストールほか) 』と現在の方法では、Wifi設定で劇的な変更点がある

OSインストールの2種類の方法

Raspberry Pi Imager を使う場合

20240423-rpi-imager.jpg
Raspberry Pi Imager

Raspberry Pi Imagerを利用すると、セットアップ時に「ユーザ名・パスワード、SSHサーバの有効化、Wifi SSIDとパスワード」などの個別設定を行うことができる。

この個別設定情報は、初回起動時に bootパーティション(bootfs)のfirstrun.shという設定ファイルから読み込まれる。

OSイメージファイルのSDカード直接書き込みする場合

公式サイトより 2024-03-15-raspios-bookworm-armhf-lite.img.xz のようなイメージファイルをダウンロードする。

Ubuntuの場合xz形式で圧縮されたファイルをダブルクリックすると、このような手動書き込みダイアログが表示される。

20240423-rpi-imagewriter.jpg
Ubuntu標準機能を使ってイメージファイルをSDメモリーカードに直接書き込み

もしくは、xz圧縮を展開して次のようにSDメモリーカードに直接書き込む。

$ sudo dd bs=4M if=2024-03-15-raspios-bookworm-armhf-lite.img of=/dev/sdb

OSバージョンアップによるWifi設定方法の劇的な変化

かつては、bootパーティションにwpa_supplicant.confファイルを作成すれば、初回起動時に認識され/etc/wpa_supplicant/wpa_supplicant.confにコピーされていた。

現在は、Network Managerに対応した方法でないと認識されなくなった。

Raspberry Pi OSのRelease notesで変更点を追ってみると次のようになっていた。

  • 2023-10-10
    • Based on Debian bookworm
    • NetworkManager used instead of dhcpcd as networking interface; various changes made to networking plugin to support this
  • 2022-09-06:
    • raspi-config - option to switch between dhcpcd and Network Manager added
  • 2021-10-30:
    • Based on Debian version 11 (bullseye)
  • 2019-06-20:
    • Based on Debian Buster
  • 2017-08-16:
    • Based on Raspbian Stretch (Debian version 9)

今後の予想として、現在はbootパーティションに空の(長さゼロの)sshというファイルがあれば、初回起動時にsshdサービスが有効化されるのだが、この機能も除去されるかもしれない。

Raspberry Pi Imagerで作成される自動セットアップファイルを解析してみた

/etc/fstabの設定により、bootパーティション(bootfs)は、rootパーティション(rootfs)の /boot ディレクトリにマウントされる。

rootfs:/etc/fstab
proc            /proc           proc    defaults          0       0
PARTUUID=6b2ecde3-01  /boot           vfat    defaults          0       2      ← bootパーティション(bootfs)
PARTUUID=6b2ecde3-02  /               ext4    defaults,noatime  0       1      ← rootパーティション(rootfs)

/bootにマウントされるbootパーティション(bootfs)のファイル一覧をここで見ておく。セットアップ時に重要なファイルはcmdline.txtfirstrun.sh の2つ。
また、ファイル config.txt は初回起動時に /boot/firmware/ ディレクトリにコピーされる。

$ ls -la /boot
合計 50512
drwxr-xr-x  3 vm   vm      4096 1970-01-01 09:00:00 ./
drwxr-x---+ 4 root root    4096 2024-04-23 16:06:50 ../
-rw-r--r--  1 vm   vm     18693 2023-04-05 11:32:16 COPYING.linux
-rw-r--r--  1 vm   vm      1594 2023-04-05 11:32:16 LICENCE.broadcom
-rw-r--r--  1 vm   vm     28822 2023-04-05 11:32:16 bcm2708-rpi-b-plus.dtb
-rw-r--r--  1 vm   vm     28182 2023-04-05 11:32:16 bcm2708-rpi-b-rev1.dtb
-rw-r--r--  1 vm   vm     28503 2023-04-05 11:32:16 bcm2708-rpi-b.dtb
-rw-r--r--  1 vm   vm     28246 2023-04-05 11:32:16 bcm2708-rpi-cm.dtb
-rw-r--r--  1 vm   vm     29539 2023-04-05 11:32:16 bcm2708-rpi-zero-w.dtb
-rw-r--r--  1 vm   vm     28128 2023-04-05 11:32:16 bcm2708-rpi-zero.dtb
 〜 省略 〜
-rw-r--r--  1 vm   vm     53202 2023-04-05 11:32:16 bcm2711-rpi-cm4.dtb
-rw-r--r--  1 vm   vm     50504 2023-04-05 11:32:16 bcm2711-rpi-cm4s.dtb
-rw-r--r--  1 vm   vm     52476 2023-04-05 11:32:16 bootcode.bin
-rw-r--r--  1 vm   vm       286 2024-04-23 13:35:16 cmdline.txt
-rw-r--r--  1 vm   vm      2075 2024-03-12 01:06:00 config.txt
-rw-r--r--  1 vm   vm      2332 2024-04-23 13:35:16 firstrun.sh
-rw-r--r--  1 vm   vm      7266 2023-04-05 11:32:16 fixup.dat
-rw-r--r--  1 vm   vm      5399 2023-04-05 11:32:16 fixup4.dat
 〜 省略 〜
-rw-r--r--  1 vm   vm    805756 2023-04-05 11:32:16 start_cd.elf
-rw-r--r--  1 vm   vm   4819624 2023-04-05 11:32:16 start_db.elf
-rw-r--r--  1 vm   vm   3722504 2023-04-05 11:32:16 start_x.elf

rootパーティション(rootfs)では、/usr/lib/raspberrypi-sys-mods ディレクトリなどには、この後に使われる各種ファイルも格納されているので、ここでその一覧を見ておく。重要そうなファイル行は赤着色している。

$ ls -la /usr/lib/raspberrypi-sys-mods
合計 40
drwxr-xr-x  2 root root 4096 2024-03-12 10:06:41 ./
drwxr-xr-x 66 root root 4096 2024-03-12 10:07:19 ../
-rwxr-xr-x  1 root root 7020 2023-05-10 15:00:36 firstboot*
-rwxr-xr-x  1 root root  419 2022-01-06 17:45:24 i2cprobe*
-rwxr-xr-x  1 root root 6370 2023-05-10 15:37:04 imager_custom*
-rwxr-xr-x  1 root root 7243 2023-05-10 15:00:36 init_config*
-rwxr-xr-x  1 root root  210 2023-05-10 15:00:36 regenerate_ssh_host_keys*

$ ls -la /usr/lib/raspberrypi-net-mods
合計 12
drwxr-xr-x  2 root root 4096 2024-03-12 10:07:19 ./
drwxr-xr-x 66 root root 4096 2024-03-12 10:07:19 ../
-rwxr-xr-x  1 root root  324 2022-08-08 19:20:23 wpa_copy*

$ ls -la /usr/lib/userconf-pi
合計 16
drwxr-xr-x  2 root root 4096 2024-03-12 10:06:42 ./
drwxr-xr-x 66 root root 4096 2024-03-12 10:07:19 ../
-rwxr-xr-x  1 root root  768 2022-03-21 20:38:07 userconf*
-rwxr-xr-x  1 root root 3408 2022-06-19 05:23:53 userconf-service*

(Raspberry Pi Imager) カーネルパラメータの設定からfirstbootスクリプトが呼び出される

Linux起動時に使われるカーネルパラメータは、/boot/cmdline.txt ファイルに記載されている。(カーネルパラメータの書式については、Debianの「bootparam - Linux カーネル起動時パラメーター」ドキュメントに詳述されている)

/boot/cmdline.txt (カーネルパラメータ)
console=serial0,115200 console=tty1 root=PARTUUID=6b2ecde3-02 rootfstype=ext4 fsck.repair=yes rootwait quiet init=/usr/lib/raspberrypi-sys-mods/firstboot cfg80211.ieee80211_regdom=JP systemd.run=/boot/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target

着色部分のinit設定により、/usr/lib/raspberrypi-sys-mods/firstboot が初期コマンドとして実行される。

また、この部分はfirstbootスクリプトが一度実行された後、次の記述により自動削除される。

/usr/lib/raspberrypi-sys-mods/firstboot より抜粋
sed -i 's| init=/usr/lib/raspberrypi-sys-mods/firstboot||' "$FWLOC/cmdline.txt"

(Raspberry Pi Imager) カーネルパラメータの設定からfirstrun.shスクリプトが呼び出される

/boot/cmdline.txt ファイルに記載されているカーネルパラメータのsystemdのコマンドラインにより、/boot/firstrun.sh が実行される。(systemdのコマンドラインについては、Debianの「systemd : Kernel command line parameters」ドキュメントに詳述されている)

/boot/cmdline.txt (カーネルパラメータ)
console=serial0,115200 console=tty1 root=PARTUUID=6b2ecde3-02 rootfstype=ext4 fsck.repair=yes rootwait quiet init=/usr/lib/raspberrypi-sys-mods/firstboot cfg80211.ieee80211_regdom=JP systemd.run=/boot/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target

また、この部分はfirstrun.shが一度実行された後、次の記述により自動削除される。そして、firstrun.sh ファイルも自己削除される。

/boot/firstrun.sh より最後の3行を抜粋
rm -f /boot/firstrun.sh
sed -i 's| systemd.run.*||g' /boot/cmdline.txt
exit 0

(Raspberry Pi Imager) firstrun.shスクリプトで行われること

大まかな概要として、次のような処理が行われている。

  • sshd の有効化
    • /usr/lib/raspberrypi-sys-mods/imager_custom ファイルが存在すれば、それが実行される。
      その中で systemd enable sshd が実行される。
    • 存在しない場合は systemd enable sshd が実行される。
  • Wifi 接続の設定
    • /usr/lib/raspberrypi-sys-mods/imager ファイルが存在すれば、それが実行される。
      その中で /etc/wpa_supplicant/wpa_supplicant.conf が作成される。
    • 存在しない場合は /etc/wpa_supplicant/wpa_supplicant.conf が作成される。
  • ユーザ名・パスワードの変更
    • /usr/lib/userconf-pi/userconf ファイルが存在すれば、その中で
    • 存在しない場合は firstrun.sh 内で同様の処理を行う

これらの処理について、詳細にスクリプトを読んで以下に記載してみる。

(Raspberry Pi Imager) SSH接続の有効化方法を詳しく見てみる

bootパーティション(bootfs)に空の(長さゼロの)sshというファイルを作るのではなく、firstrun.sh で実行されることをなぞってみる。

/boot/firstrun.sh より該当箇所
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then      ← この判定式はTRUE(imager_customファイルが存在するため)
   /usr/lib/raspberrypi-sys-mods/imager_custom enable_ssh      ← この行が実行される
else
   systemctl enable ssh
fi
/usr/lib/raspberrypi-sys-mods/imager_custom より該当箇所
enable_ssh () (
  ENABLE=1
  KEY_ONLY_SED_STR='s/^[#\s]*PasswordAuthentication\s\+\S\+$/PasswordAuthentication no/'
  PASSAUTH_SED_STR='s/^[#\s]*PasswordAuthentication\s\+\S\+$/PasswordAuthentication yes/'
  for arg in "$@"; do      ← 引数なしなので、このループは飛ばされる
    if [ "$arg" = "-k" ] || [ "$arg" = "--key-only" ]; then
      sed -i "$KEY_ONLY_SED_STR" /etc/ssh/sshd_config
    elif [ "$arg" = "-p" ] || [ "$arg" = "--pass-auth" ]; then
      sed -i "$PASSAUTH_SED_STR" /etc/ssh/sshd_config
    elif [ "$arg" = "-d" ] || [ "$arg" = "--disabled" ]; then
      ENABLE=0
    else
      add_ssh_keys "$arg"
    fi
  done
  if [ "$ENABLE" = 1 ]; then
    systemctl -q enable ssh      ← この行が実行される
  fi
)

(Raspberry Pi Imager) Wifi接続の設定方法を詳しく見てみる

/boot/firstrun.sh よりWifi接続設定の該当箇所
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then      ← この判定式はTRUE(imager_customファイルが存在するため)
   /usr/lib/raspberrypi-sys-mods/imager_custom set_wlan 'SSID_TEST' '0c1faf2119bbafcaa4cbd97ba674727951b3ae958317178e4655fa1298a3b24c' 'JP'      ← この行が実行される
else      ← この行より下は実行されない(かつてのwpa_supplicant.confが作成されるスクリプト)
cat >/etc/wpa_supplicant/wpa_supplicant.conf <<'WPAEOF'
country=JP
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
ap_scan=1

update_config=1
network={
	ssid="SSID_TEST"
	psk=0c1faf2119bbafcaa4cbd97ba674727951b3ae958317178e4655fa1298a3b24c
}

WPAEOF
   chmod 600 /etc/wpa_supplicant/wpa_supplicant.conf
   rfkill unblock wifi
   for filename in /var/lib/systemd/rfkill/*:wlan ; do
       echo 0 > $filename
   done
fi

firstrun.shから呼び出されるimager_customスクリプトから、核心部を抜粋してみる。

/usr/lib/raspberrypi-sys-mods/imager_custom よりWifi接続設定の該当箇所
set_wlan () (
  HIDDEN=0
  PLAIN=0
  SCRIPT='/var/lib/raspberrypi-sys-mods/set-wlan'      ← 新規作成されるスクリプトのフルパス名
  for arg in "$@"; do
    # shellcheck disable=SC2031
    if [ "$arg" = "-h" ] || [ "$arg" = "--hidden" ]; then
      HIDDEN=1
    elif [ "$arg" = "-p" ] || [ "$arg" = "--plain" ]; then
      PLAIN=1
    elif [ -z "${SSID+set}" ]; then
      SSID="$arg"
    elif [ -z "${PASS+set}" ]; then
      PASS="$arg"
    elif [ -z "${COUNTRY+set}" ]; then
      COUNTRY="$arg"
    else
      usage set_wlan
      exit 1
    fi
  done
  if [ -z "${SSID+set}" ]; then
    usage set_wlan
    exit 1
  fi
  if [ -e /boot/wpa_supplicant.conf ]; then
    echo "Ignoring network configuration: /boot/wpa_supplicant.conf exists"
    exit 0
  fi

  if [ -n "$COUNTRY" ]; then
    set_wlan_country "$COUNTRY"
  fi

  # Replace ' with '\'' in SSID and PASS to support single-quote characters
  SSID=$(printf '%s' "$SSID" | sed "s/'/'\\\\''/g")
  PASS=$(printf '%s' "$PASS" | sed "s/'/'\\\\''/g")

  mkdir -p "$(dirname "$SCRIPT")"
  # shellcheck disable=SC2094
  cat <<- EOF > "$SCRIPT"      ← 新規作成されるスクリプトの内容はここから下
	#!/bin/sh
	COUNTER=0
	while [ "\$COUNTER" -lt 10 ]; do
	  COUNTER=\$((COUNTER + 1))
	  if raspi-config nonint do_wifi_ssid_passphrase  '$SSID' '$PASS' '$HIDDEN' '$PLAIN'; then
	    break
	  fi
	  sleep 5
	done
	systemctl stop set-wlan.timer
	systemctl disable set-wlan.timer
	rm -f "\$0"
	rm -f /etc/systemd/system/set-wlan.timer
	rm -f /etc/systemd/system/set-wlan.service
	rmdir --ignore-fail-on-non-empty '$(dirname "$SCRIPT")'
	EOF
  chmod 700 "$SCRIPT"

  cat <<- EOF > /etc/systemd/system/set-wlan.timer      ← このファイルも新規作成される
		[Unit]
		Description=Configure WLAN for rpi-imager
		[Timer]
		OnBootSec=1
		OnUnitActiveSec=10
		[Install]
		WantedBy=timers.target
	EOF

  cat <<- EOF > /etc/systemd/system/set-wlan.service      ← このファイルも新規作成される
		[Unit]
		Description=Configure WLAN for rpi-imager
		After=NetworkManager.service dhcpcd.service
		ConditionPathIsDirectory=|/run/wpa_supplicant
		ConditionPathExists=|/run/dhcpcd.pid
		[Service]
		Type=oneshot
		ExecStart=$SCRIPT
	EOF
  ln -f -s /etc/systemd/system/set-wlan.timer \
  /etc/systemd/system/timers.target.wants/set-wlan.timer
)

/var/lib/raspberrypi-sys-mods/set-wlan スクリプトファイルが新規作成され、このスクリプトはsystemd から起動される set-wlan.service で呼び出される。

そして、set-wlanから/usr/bin/raspi-configスクリプトが呼び出される。その中で、NetworkManagerのnmcliコマンドでWifi接続が行われる。

/usr/bin/raspi-config よりWifi接続設定の該当箇所
do_wifi_ssid_passphrase() {
  RET=0
  if [ "$INTERACTIVE" = True ] && [ -z "$(get_wifi_country)" ]; then
    do_wifi_country
  fi

〜 省略 〜

  if systemctl -q is-active dhcpcd; then      ← 現在のRPi OSはdhcpcdではないので、このifセクションはFALSEとなる
    wpa_cli -i "$IFACE" list_networks \

〜 省略 〜

  else
    if [ "$HIDDEN" -ne 0 ]; then
      nmcli device wifi connect "$SSID"  password "$PASSPHRASE" hidden true | grep -q "activated"
    else
      nmcli device wifi connect "$SSID"  password "$PASSPHRASE" | grep -q "activated"
    fi
    RET=$((RET + $?))
  fi

  return "$RET"
}

/etc/NetworkManager/system-connections/ ディレクトリにキーファイルを作成するなら、次のようなコマンドを使うべきだが...

nmcli connection add con-name コネクション名 ifname wlp2s0 type wifi ssid SSID名
nmcli connection modify コネクション名 wifi-sec.key-mgmt wpa-psk
nmcli connection modify コネクション名 wifi-sec.psk パスワード
nmcli connection modify コネクション名 connection.autoconnect yes

毎回、nmcliコマンドを直接叩くというのは、どうなんだろう。

(Raspberry Pi Imager) ユーザ名・パスワード設定方法を詳しく見てみる

/boot/firstrun.sh より抜粋
if [ -f /usr/lib/userconf-pi/userconf ]; then      ← userconfファイルが存在するため、ここはTRUEとなる
   /usr/lib/userconf-pi/userconf 'pi' '$5$nI92OMl4c.CLlr5D$3t8fHwPV3R0.DyayqWgz.n8HTc.UE6MQTRWZL1ufVW2'
else
   echo "$FIRSTUSER:"'$5$nI92OMl4c.CLlr5D$3t8fHwPV3R0.DyayqWgz.n8HTc.UE6MQTRWZL1ufVW2' | chpasswd -e
   if [ "$FIRSTUSER" != "pi" ]; then
      usermod -l "pi" "$FIRSTUSER"
      usermod -m -d "/home/pi" "pi"
      groupmod -n "pi" "$FIRSTUSER"
      if grep -q "^autologin-user=" /etc/lightdm/lightdm.conf ; then
         sed /etc/lightdm/lightdm.conf -i -e "s/^autologin-user=.*/autologin-user=pi/"
      fi
      if [ -f /etc/systemd/system/getty@tty1.service.d/autologin.conf ]; then
         sed /etc/systemd/system/getty@tty1.service.d/autologin.conf -i -e "s/$FIRSTUSER/pi/"
      fi
      if [ -f /etc/sudoers.d/010_pi-nopasswd ]; then
         sed -i "s/^$FIRSTUSER /pi /" /etc/sudoers.d/010_pi-nopasswd
      fi
   fi
fi
/usr/lib/userconf-pi/userconf
#!/bin/sh

rename_user () {
    usermod -l "$NEWNAME" "$FIRSTUSER"
    usermod -m -d "/home/$NEWNAME" "$NEWNAME"
    groupmod -n "$NEWNAME" "$FIRSTGROUP"
    for file in /etc/subuid /etc/subgid; do
        sed -i "s/^$FIRSTUSER:/$NEWNAME:/" "$file"
    done
    if [ -f /etc/sudoers.d/010_pi-nopasswd ]; then
        sed -i "s/^$FIRSTUSER /$NEWNAME /" /etc/sudoers.d/010_pi-nopasswd
    fi
}

if [ $# -eq 3 ]; then      ← 引数はユーザ名・パスワードの2個なので、ここはFALSEとなる
    FIRSTUSER="$1"
    FIRSTGROUP="$1"
    shift
else
    FIRSTUSER="$(getent passwd 1000 | cut -d: -f1)"      ← ビルトインではなく作成されたUID先頭1000のユーザ名
    FIRSTGROUP="$(getent group 1000 | cut -d: -f1)"      ← ビルトインではなく作成されたUID先頭1000のグループ名
fi

NEWNAME=$1
NEWPASS=$2

if [ "$FIRSTUSER" != "$NEWNAME" ]; then
    rename_user
fi

if [ -n "$NEWPASS" ]; then
    echo "$NEWNAME:$NEWPASS" | chpasswd -e
fi

/usr/bin/cancel-rename "$NEWNAME"

また、sudoで全てのコマンドをパスワード無しで実行できる設定となっている。

/etc/sudoers.d/010_pi-nopasswd
pi ALL=(ALL) NOPASSWD: ALL

ちなみに/etc/passwdと/etc/groupの初期値は、$FIRSTUSER = "pi", $FIRSTGROUP = "pi" である。passwdの2番めのフィールドにパスワードではなく「x」のばあいは、shadowファイルにパスワードが記録されていることを示す。

/etc/passwd より抜粋
pi:x:1000:1000:,,,:/home/pi:/bin/bash
/etc/group より抜粋
pi:x:1000:

shadowの2番めのフィールドが「*」の場合は、パスワードが設定されておらず、このままではログオンできない。

/etc/shadow より抜粋
pi:*:19794:0:99999:7:::

OSイメージファイルのSDカード直接書き込みで作成される自動セットアップファイルを解析してみた

今回解析したのは 2024-03-15-raspios-bookworm-armhf-lite.img.xz

/etc/fstabの設定により、bootパーティション(bootfs)は、rootパーティション(rootfs)の /boot ディレクトリにマウントされる。

/etc/fstab
proc            /proc           proc    defaults          0       0
PARTUUID=662b4900-01  /boot/firmware  vfat    defaults          0       2
PARTUUID=662b4900-02  /               ext4    defaults,noatime  0       1

bootパーティション(bootfs)に含まれるファイルは次のようになっていて、firstrun.sh が存在しないなど、Raspberry Pi Imagerの場合とかなり違う。

ソースコード
$ ls -la /boot
合計 95036
drwxr-xr-x  3 vm   vm       6144 1970-01-01 09:00:00 ./
drwxr-x---+ 4 root root     4096 2024-05-01 11:29:15 ../
-rw-r--r--  1 vm   vm       1594 2024-03-15 14:59:32 LICENCE.broadcom
-rw-r--r--  1 vm   vm      29578 2024-03-07 14:51:30 bcm2708-rpi-b-plus.dtb
-rw-r--r--  1 vm   vm      28937 2024-03-07 14:51:30 bcm2708-rpi-b-rev1.dtb
-rw-r--r--  1 vm   vm      29275 2024-03-07 14:51:30 bcm2708-rpi-b.dtb
-rw-r--r--  1 vm   vm      29018 2024-03-07 14:51:30 bcm2708-rpi-cm.dtb
-rw-r--r--  1 vm   vm      30755 2024-03-07 14:51:30 bcm2708-rpi-zero-w.dtb
〜 省略 〜
-rw-r--r--  1 vm   vm      77691 2024-03-07 14:51:30 bcm2712-rpi-cm5-cm5io.dtb
-rw-r--r--  1 vm   vm      77739 2024-03-07 14:51:30 bcm2712d0-rpi-5-b.dtb
-rw-r--r--  1 vm   vm      52476 2024-03-15 14:59:34 bootcode.bin
-rw-r--r--  1 vm   vm        154 2024-03-15 15:06:52 cmdline.txt
-rw-r--r--  1 vm   vm       1179 2024-03-15 14:59:38 config.txt
-rw-r--r--  1 vm   vm       7303 2024-03-15 14:59:34 fixup.dat
-rw-r--r--  1 vm   vm       5434 2024-03-15 14:59:34 fixup4.dat
〜 省略 〜
-rw-r--r--  1 vm   vm    4825352 2024-03-15 14:59:34 start_db.elf
-rw-r--r--  1 vm   vm    3727656 2024-03-15 14:59:34 start_x.elf

rootパーティション(rootfs)では、/usr/lib/raspberrypi-sys-mods ディレクトリなどには、この後に使われる各種ファイルも格納されているので、ここでその一覧を見ておく。重要そうなファイル行は赤着色している。

Raspberry Pi Imagerの場合とファイルの中身が違うものも多い。

$ ls -la /usr/lib/raspberrypi-sys-mods
合計 44
drwxr-xr-x  2 root root 4096 2024-03-16 00:00:17 ./
drwxr-xr-x 65 root root 4096 2024-03-16 00:00:46 ../
-rwxr-xr-x  1 root root 3227 2023-11-07 00:03:19 firstboot*
-rwxr-xr-x  1 root root  445 2023-12-18 19:51:31 get_fw_loc*
-rwxr-xr-x  1 root root  419 2022-01-06 17:45:24 i2cprobe*
-rwxr-xr-x  1 root root 5486 2023-11-07 18:30:53 imager_custom*
-rwxr-xr-x  1 root root 7319 2023-07-26 00:09:46 init_config*
-rwxr-xr-x  1 root root  117 2023-08-31 20:35:11 regenerate_ssh_host_keys*
-rwxr-xr-x  1 root root  339 2023-07-26 00:09:46 sshswitch*

$ ls -la /usr/lib/userconf-pi
合計 16
drwxr-xr-x  2 root root 4096 2024-03-16 00:00:20 ./
drwxr-xr-x 65 root root 4096 2024-03-16 00:00:46 ../
-rwxr-xr-x  1 root root  768 2022-03-21 20:38:07 userconf*
-rwxr-xr-x  1 root root 3521 2023-08-30 03:09:11 userconf-service*


(OSイメージファイル) カーネルパラメータの設定からfirstbootスクリプトが呼び出される

カーネルパラメータは前述した「Raspberry Pi Imager」の場合より大幅に少なく、firstrun.shスクリプトの実行が含まれていない。

/boot/cmdline.txt
console=serial0,115200 console=tty1 root=PARTUUID=662b4900-02 rootfstype=ext4 fsck.repair=yes rootwait quiet init=/usr/lib/raspberrypi-sys-mods/firstboot

カーネルパラメータのinitプロセス設定より、firstboot が実行される。

また、この部分はfirstbootが一度実行された後、次の記述により自動削除される。

/usr/lib/raspberrypi-sys-mods/firstboot より抜粋
sed -i 's| init=/usr/lib/raspberrypi-sys-mods/firstboot||' "$FWLOC/cmdline.txt"

(OSイメージファイル) firstbootスクリプトで行われること

/boot/custom.toml ファイルに記述された「設定情報」を、/usr/lib/raspberrypi-sys-mods/init_config スクリプトに引き渡すのが、firstbootの主な役割。

なお、「OSイメージファイル」には/boot/custom.tomlは含まれていないので、初回起動時までに新たに作成して/bootディレクトリに保存しておくこと。

/usr/lib/raspberrypi-sys-mods/firstboot より抜粋
〜 省略 〜

apply_custom () {
  CONFIG_FILE="$1"
  mount -o remount,rw /
  mount -o remount,rw "$FWLOC"
  if ! python3 -c "import toml" 2> /dev/null; then
    FAIL_REASON="custom.toml provided, but python3-toml is not installed\n$FAIL_REASON"
  else
    set -o pipefail
    /usr/lib/raspberrypi-sys-mods/init_config "$CONFIG_FILE" |& tee /run/firstboot.log | while read -r line; do
        MSG="$MSG\n$line"
        whiptail --infobox "$MSG" 20 60
    done
    if [ "$?" -ne 0 ]; then
      mv /run/firstboot.log /var/log/firstboot.log
      FAIL_REASON="Failed to apply customisations from custom.toml\n\nLog file saved as /var/log/firstboot.log\n$FAIL_REASON"
    fi
    set +o pipefail
  fi
  rm -f "$CONFIG_FILE"
  mount -o remount,ro "$FWLOC"
  mount -o remount,ro /
}

main () {
  get_variables

  whiptail --infobox "Generating SSH keys..." 20 60
  regenerate_ssh_host_keys

  if [ -f "$FWLOC/custom.toml" ]; then
    MSG="Applying customisations from custom.toml...\n"
    whiptail --infobox "$MSG" 20 60
    apply_custom "$FWLOC/custom.toml"
  fi

  whiptail --infobox "Fix PARTUUID..." 20 60
  fix_partuuid

  return 0
}

〜 省略 〜

main

〜 省略 〜

新規作成する custom.toml ファイルの書式例は次のようなものらしい(ここを参考にした)

/boot/custom.toml
config_version = 1

[system]
hostname = "raspberrypi"

[user]
# If present, the default "rpi" user gets renamed to this "name"
name = "rpi"
# The password can be encrypted or plain. To encrypt, we can use "openssl passwd -5 raspberry"
password = "$5$pN7oRnie.WDOHoJY$aWEYmKUytN/S/bxMza5ksBiurbSJmcvcysBKHSmYa45"
password_encrypted = true

[ssh]
# ssh_import_id = "gh:user" # import public keys from github
enabled = true
password_authentication = false
# We can also seed the ssh public keys configured for the default user:
# authorized_keys = [ "ssh-rsa ... user@host", ... ]

[wlan]
ssid = "mywifi"
password = "$5$pN7oRnie.WDOHoJY$aWEYmKUytN/S/bxMza5ksBiurbSJmcvcysBKHSmYa45"
password_encrypted = true
hidden = false
# The country is written to /etc/default/crda
# Reference: https://wireless.wiki.kernel.org/en/developers/Regulatory
country = "JP"

[locale]
keymap = "jp"
timezone = "Asia/Tokyo"

init_configスクリプトでは、custom.tomlの変数をセクションごとに取り出して、セクションごとの関数に渡し、その関数からimager_customスクリプトに変数を引き渡している。

/usr/lib/raspberrypi-sys-mods/init_config より抜粋
〜 省略 〜

imager_custom_path = os.path.join('/', 'usr', 'lib', 'raspberrypi-sys-mods', 'imager_custom')

〜 省略 〜

def config_ssh (ssh_config):      ← 例として、ssh設定の関数を抜粋してみた
    if not ssh_config:
        return
    ssh_import_id = ssh_config.pop("ssh_import_id", None)
    import_ssh_id(ssh_import_id)
    cmd = (imager_custom_path, "enable_ssh")
    ssh_enabled = ssh_config.pop("enabled", False)
    ssh_password_authentication = ssh_config.pop("password_authentication", None)
    ssh_authorized_keys = ssh_config.pop("authorized_keys", [])
    if not ssh_enabled:
        cmd = cmd + ('-d',)
    if ssh_password_authentication is not None:
        cmd = cmd + ('-p' if ssh_password_authentication else '-k',)
    cmd = cmd + tuple(ssh_authorized_keys)
    try:
        subprocess.run(cmd, encoding='UTF-8', check=True)
        logging.info ("Configured SSH")

〜 省略 〜

try:
    config = toml.load(args.toml_file)      ← tomlファイルを解析し、データをconfigディクショナリに格納する
except toml.decoder.TomlDecodeError as err:
    logging.error ("Error parsing %s: %s", args.toml_file, err)
    sys.exit(1)

〜 省略 〜

supported_sections = ("system", "user", "ssh", "wlan", "locale")
for s in supported_sections:
    section_config = config.pop(s, {})      ← tomlデータを格納したディクショナリconfigから、指定したセクション名 s のものを抽出
    locals()[f"config_{s}"](section_config)      ← 指定したセクション名の関数 (例:config_system)を実行する
    for key in section_config:
        logging.warning("Unknown key in [%s]: %s", s, key)

〜 省略 〜

(OSイメージファイル) SSH接続の有効化方法を詳しく見てみる

bootパーティション(bootfs)に空の(長さゼロの)sshというファイルを作るのではなく、imager_customスクリプト で実行されることをなぞってみる。

一つ前のセクションでinit_configスクリプトまで読み解いたが、init_configスクリプトの

subprocess.run("/usr/lib/raspberrypi-sys-mods/imager_custom enable_ssh", encoding='UTF-8', check=True)

によって処理がimager_customスクリプトに移る。そして、imager_customの中で次のようにsystemdでsshサービスを有効化している。

systemctl -q enable ssh

/usr/lib/raspberrypi-sys-mods/imager_custom から抜粋
〜 省略 〜

enable_ssh () (
  ENABLE=1
  KEY_ONLY_SED_STR='s/^[#\s]*PasswordAuthentication\s\+\S\+$/PasswordAuthentication no/'
  PASSAUTH_SED_STR='s/^[#\s]*PasswordAuthentication\s\+\S\+$/PasswordAuthentication yes/'
  for arg in "$@"; do
    if [ "$arg" = "-k" ] || [ "$arg" = "--key-only" ]; then
      sed -i "$KEY_ONLY_SED_STR" /etc/ssh/sshd_config
    elif [ "$arg" = "-p" ] || [ "$arg" = "--pass-auth" ]; then
      sed -i "$PASSAUTH_SED_STR" /etc/ssh/sshd_config
    elif [ "$arg" = "-d" ] || [ "$arg" = "--disabled" ]; then
      ENABLE=0
    else
      add_ssh_keys "$arg"
    fi
  done
  if [ "$ENABLE" = 1 ]; then
    systemctl -q enable ssh
  fi
)

〜 省略 〜

command="$1"; shift
case "$command" in
  set_hostname|import_ssh_id|enable_ssh|set_wlan_country|set_wlan|set_keymap|set_timezone)
    "$command" "$@"
    ;;
  *)
    echo "Unsupported command: $command"
    usage
    exit 1
    ;;
esac

(OSイメージファイル) Wifi接続の設定方法を詳しく見てみる

一つ前のセクションで読み解いたSSH有効化のときと同じく、init_configスクリプトからimager_customが呼び出される。

次に示すように、nmcli コマンドを使う正攻法ではなく、/etc/NetworkManager/system-connections/ ディレクトリにキーファイルを直接作成する方法が取られている。

/usr/lib/raspberrypi-sys-mods/imager_custom から抜粋
〜 省略 〜

set_wlan () (
  HIDDEN="false"
  PLAIN=0
  for arg in "$@"; do
    # shellcheck disable=SC2031
    if [ "$arg" = "-h" ] || [ "$arg" = "--hidden" ]; then
      HIDDEN="true"
    elif [ "$arg" = "-p" ] || [ "$arg" = "--plain" ]; then
      PLAIN=1
    elif [ -z "${SSID+set}" ]; then
      SSID="$arg"
    elif [ -z "${PASS+set}" ]; then
      PASS="$arg"
    elif [ -z "${COUNTRY+set}" ]; then
      COUNTRY="$arg"
    else
      usage set_wlan
      exit 1
    fi
  done
  if [ -z "${SSID+set}" ]; then
    usage set_wlan
    exit 1
  fi

  if [ -n "$COUNTRY" ]; then
    set_wlan_country "$COUNTRY"
  fi

  CONNFILE=/etc/NetworkManager/system-connections/preconfigured.nmconnection
  UUID=$(uuid -v4)
  cat <<- EOF >${CONNFILE}
	[connection]
	id=preconfigured
	uuid=${UUID}
	type=wifi
	[wifi]
	mode=infrastructure
	ssid=${SSID}
	hidden=${HIDDEN}
	[ipv4]
	method=auto
	[ipv6]
	addr-gen-mode=default
	method=auto
	[proxy]
	EOF

  if [ ! -z "${PASS}" ]; then
    cat <<- EOF >>${CONNFILE}
	[wifi-security]
	key-mgmt=wpa-psk
	psk=${PASS}
	EOF
  fi

  # NetworkManager will ignore nmconnection files with incorrect permissions,
  # to prevent Wi-Fi credentials accidentally being world-readable.
  chmod 600 ${CONNFILE}
)

〜 省略 〜

(OSイメージファイル) ユーザ名・パスワード設定方法を詳しく見てみる

init_configスクリプトの処理の流れを見ていくと、赤字のところが主な処理内容で、userconfスクリプトに「ユーザ名」「SHA512化したパスワード」を引き渡している。

/usr/lib/raspberrypi-sys-mods/init_config より抜粋
def config_user(user_config):
    name = user_config.pop("name", None)
    password = user_config.pop("password", None)
    is_encrypted = user_config.pop("password_encrypted", True)
    rename_user(name, password, is_encrypted)

userconf_path = os.path.join('/', 'usr', 'lib', 'userconf-pi', 'userconf')

def rename_user(name, password, is_encrypted=True):
〜 省略 〜
    elif not is_encrypted:
        password = crypt.crypt(password, crypt.mksalt(crypt.METHOD_SHA512))
    try:
        cmd = (userconf_path, name, password)
        subprocess.run(cmd, encoding='UTF-8', check=True)
そして、userconfスクリプトでは、システムに登録された「最初のユーザ」(UID=1000)を変更すしている。
/usr/lib/userconf-pi/userconf
if [ $# -eq 3 ]; then
    FIRSTUSER="$1"
    FIRSTGROUP="$1"
    shift
else      ← 引数はnew_user,new_passwordの2個なので、こちらが実行される
    FIRSTUSER="$(getent passwd 1000 | cut -d: -f1)"      ← UID=1000のユーザ名
    FIRSTGROUP="$(getent group 1000 | cut -d: -f1)"      ← GID=1000のグループ名
fi

NEWNAME=$1
NEWPASS=$2

if [ "$FIRSTUSER" != "$NEWNAME" ]; then      ← ユーザ名に変更があるなら、rename_user 関数に移る
    rename_user
fi
if [ -n "$NEWPASS" ]; then      ← $NEWPASSが0文字以上なら、パスワード変更する
    echo "$NEWNAME:$NEWPASS" | chpasswd -e
fi

rename_user () {
    usermod -l "$NEWNAME" "$FIRSTUSER"      ← 既存のユーザ名を、新しいユーザ名に変更
    usermod -m -d "/home/$NEWNAME" "$NEWNAME"
    groupmod -n "$NEWNAME" "$FIRSTGROUP"
    for file in /etc/subuid /etc/subgid; do
        sed -i "s/^$FIRSTUSER:/$NEWNAME:/" "$file"
    done
    if [ -f /etc/sudoers.d/010_pi-nopasswd ]; then
        sed -i "s/^$FIRSTUSER /$NEWNAME /" /etc/sudoers.d/010_pi-nopasswd
    fi
}

ちなみに/etc/passwdと/etc/groupの初期値は、$FIRSTUSER = "pi", $FIRSTGROUP = "pi" である。また、/etc/shadowの2番めのフィールドがパスワードロックの "!" となっている。

/etc/passwd
pi:x:1000:1000:,,,:/home/pi:/bin/bash
/etc/group
pi:x:1000:
/etc/shadow
pi:!:19797:0:99999:7:::

(OSイメージファイル) custom.toml ファイルを作成し自動セットアップしてみた

OSイメージファイルをSDカードに書き込み、bootパーティション(bootfs)に次の内容の custom.toml ファイルを新規作成保存した。

/boot/custom.toml
config_version = 1

[system]
hostname = "raspberrypi"

[user]
# If present, the default "rpi" user gets renamed to this "name"
name = "pi"
# The password can be encrypted or plain. To encrypt, we can use "openssl passwd -5 raspberry"
password = "●パスワードを平文で記入●"
password_encrypted = false

[ssh]
# ssh_import_id = "gh:user" # import public keys from github
enabled = true
password_authentication = true
# We can also seed the ssh public keys configured for the default user:
# authorized_keys = [ "ssh-rsa ... user@host", ... ]

[wlan]
ssid = "●WifiルータのSSID名●"
password = "●Wifiパスワードを平文で記入●"
password_encrypted = false
hidden = false
# The country is written to /etc/default/crda
# Reference: https://wireless.wiki.kernel.org/en/developers/Regulatory
country = "JP"

[locale]
keymap = "jp"
timezone = "Asia/Tokyo"

初回電源投入後、数回リブートを繰り返し、5分程度でWifi接続されsshdも稼働し正常起動した。

/bootディレクトリに置かれていた各種セットアップファイルが、正常起動後にどうなったか確認してみる

/boot 直下に置かれていた cmdline.txtとconfig.txt は、/boot/firmware ディレクトリに移動されるとともに、cmdline.txt から firstboot スクリプトを起動するカーネルパラメータのinit設定がなくなっている。
init設定がなくなったのは、正常にfirstbootスクリプトが処理終了し、自己削除したためだ。

$ ls -la /boot
total 73274
drwxr-xr-x  3 root root     4096 Mar 16 00:07 .
drwxr-xr-x 18 root root     4096 Mar 16 00:08 ..
-rw-r--r--  1 root root       92 Mar 15 23:59 cmdline.txt
-rw-r--r--  1 root root   204639 Mar  7 23:51 config-6.6.20+rpt-rpi-v6
-rw-r--r--  1 root root   210131 Mar  7 23:51 config-6.6.20+rpt-rpi-v7
-rw-r--r--  1 root root   227912 Mar  7 23:51 config-6.6.20+rpt-rpi-v7l
-rw-r--r--  1 root root   236765 Mar  7 23:51 config-6.6.20+rpt-rpi-v8
-rw-r--r--  1 root root       91 Mar 15 23:59 config.txt
drwxr-xr-x  3 root root     6144 Jan  1  1970 firmware
-rw-r--r--  1 root root 10446553 Mar 16 00:06 initrd.img-6.6.20+rpt-rpi-v6
-rw-r--r--  1 root root 10595497 Mar 16 00:07 initrd.img-6.6.20+rpt-rpi-v7
-rw-r--r--  1 root root 10693834 Mar 16 00:07 initrd.img-6.6.20+rpt-rpi-v7l
-rw-r--r--  1 root root 10578299 Mar 16 00:07 initrd.img-6.6.20+rpt-rpi-v8
lrwxrwxrwx  1 root root       18 Mar 16 00:07 issue.txt -> firmware/issue.txt
lrwxrwxrwx  1 root root       17 Mar 15 23:59 overlays -> firmware/overlays
-rw-r--r--  1 root root       83 Mar  7 23:51 System.map-6.6.20+rpt-rpi-v6
-rw-r--r--  1 root root       83 Mar  7 23:51 System.map-6.6.20+rpt-rpi-v7
-rw-r--r--  1 root root       83 Mar  7 23:51 System.map-6.6.20+rpt-rpi-v7l
-rw-r--r--  1 root root       83 Mar  7 23:51 System.map-6.6.20+rpt-rpi-v8
-rw-r--r--  1 root root  7091784 Mar  7 23:51 vmlinuz-6.6.20+rpt-rpi-v6
-rw-r--r--  1 root root  7427672 Mar  7 23:51 vmlinuz-6.6.20+rpt-rpi-v7
-rw-r--r--  1 root root  7853280 Mar  7 23:51 vmlinuz-6.6.20+rpt-rpi-v7l
-rw-r--r--  1 root root  9259827 Mar  7 23:51 vmlinuz-6.6.20+rpt-rpi-v8

$ ls -la /boot/firmware/
total 95036
drwxr-xr-x 3 root root     6144 Jan  1  1970 .
drwxr-xr-x 3 root root     4096 Mar 16 00:07 ..
-rwxr-xr-x 1 root root    29275 Mar  7 23:51 bcm2708-rpi-b.dtb
-rwxr-xr-x 1 root root    29578 Mar  7 23:51 bcm2708-rpi-b-plus.dtb
-rwxr-xr-x 1 root root    28937 Mar  7 23:51 bcm2708-rpi-b-rev1.dtb
-rwxr-xr-x 1 root root    29018 Mar  7 23:51 bcm2708-rpi-cm.dtb
-rwxr-xr-x 1 root root    28888 Mar  7 23:51 bcm2708-rpi-zero.dtb
-rwxr-xr-x 1 root root    30755 Mar  7 23:51 bcm2708-rpi-zero-w.dtb
-rwxr-xr-x 1 root root    31272 Mar  7 23:51 bcm2709-rpi-2-b.dtb
-rwxr-xr-x 1 root root    31195 Mar  7 23:51 bcm2709-rpi-cm2.dtb
-rwxr-xr-x 1 root root    31401 Mar  7 23:51 bcm2710-rpi-2-b.dtb
-rwxr-xr-x 1 root root    33593 Mar  7 23:51 bcm2710-rpi-3-b.dtb
-rwxr-xr-x 1 root root    34228 Mar  7 23:51 bcm2710-rpi-3-b-plus.dtb
-rwxr-xr-x 1 root root    31312 Mar  7 23:51 bcm2710-rpi-cm3.dtb
-rwxr-xr-x 1 root root    32570 Mar  7 23:51 bcm2710-rpi-zero-2.dtb
-rwxr-xr-x 1 root root    32570 Mar  7 23:51 bcm2710-rpi-zero-2-w.dtb
-rwxr-xr-x 1 root root    54813 Mar  7 23:51 bcm2711-rpi-400.dtb
-rwxr-xr-x 1 root root    54809 Mar  7 23:51 bcm2711-rpi-4-b.dtb
-rwxr-xr-x 1 root root    55449 Mar  7 23:51 bcm2711-rpi-cm4.dtb
-rwxr-xr-x 1 root root    38349 Mar  7 23:51 bcm2711-rpi-cm4-io.dtb
-rwxr-xr-x 1 root root    52228 Mar  7 23:51 bcm2711-rpi-cm4s.dtb
-rwxr-xr-x 1 root root    77739 Mar  7 23:51 bcm2712d0-rpi-5-b.dtb
-rwxr-xr-x 1 root root    77755 Mar  7 23:51 bcm2712-rpi-5-b.dtb
-rwxr-xr-x 1 root root    77699 Mar  7 23:51 bcm2712-rpi-cm5-cm4io.dtb
-rwxr-xr-x 1 root root    77691 Mar  7 23:51 bcm2712-rpi-cm5-cm5io.dtb
-rwxr-xr-x 1 root root    52476 Mar 15 23:59 bootcode.bin
-rwxr-xr-x 1 root root      132 Jan  1  1980 cmdline.txt
-rwxr-xr-x 1 root root     1179 Mar 15 23:59 config.txt
-rwxr-xr-x 1 root root     3204 Mar 15 23:59 fixup4cd.dat
-rwxr-xr-x 1 root root     5434 Mar 15 23:59 fixup4.dat
-rwxr-xr-x 1 root root     8423 Mar 15 23:59 fixup4db.dat
-rwxr-xr-x 1 root root     8425 Mar 15 23:59 fixup4x.dat
-rwxr-xr-x 1 root root     3204 Mar 15 23:59 fixup_cd.dat
-rwxr-xr-x 1 root root     7303 Mar 15 23:59 fixup.dat
-rwxr-xr-x 1 root root    10268 Mar 15 23:59 fixup_db.dat
-rwxr-xr-x 1 root root    10268 Mar 15 23:59 fixup_x.dat
-rwxr-xr-x 1 root root 10446553 Mar 16 00:07 initramfs
-rwxr-xr-x 1 root root 10595497 Mar 16 00:07 initramfs7
-rwxr-xr-x 1 root root 10693834 Mar 16 00:07 initramfs7l
-rwxr-xr-x 1 root root 10578299 Mar 16 00:07 initramfs8
-rwxr-xr-x 1 root root      145 Mar 16 00:07 issue.txt
-rwxr-xr-x 1 root root  7427672 Mar 15 23:59 kernel7.img
-rwxr-xr-x 1 root root  7853280 Mar 15 23:59 kernel7l.img
-rwxr-xr-x 1 root root  9259827 Mar 15 23:59 kernel8.img
-rwxr-xr-x 1 root root  7091784 Mar 15 23:59 kernel.img
-rwxr-xr-x 1 root root     1594 Mar 15 23:59 LICENCE.broadcom
drwxr-xr-x 2 root root    28672 Mar 15 23:59 overlays
-rwxr-xr-x 1 root root   808892 Mar 15 23:59 start4cd.elf
-rwxr-xr-x 1 root root  3753480 Mar 15 23:59 start4db.elf
-rwxr-xr-x 1 root root  2256224 Mar 15 23:59 start4.elf
-rwxr-xr-x 1 root root  3004040 Mar 15 23:59 start4x.elf
-rwxr-xr-x 1 root root   808892 Mar 15 23:59 start_cd.elf
-rwxr-xr-x 1 root root  4825352 Mar 15 23:59 start_db.elf
-rwxr-xr-x 1 root root  2980544 Mar 15 23:59 start.elf
-rwxr-xr-x 1 root root  3727656 Mar 15 23:59 start_x.elf

メモリー、ディスク、サービスの一覧は

ソースコード
$ free -h -t
               total        used        free      shared  buff/cache   available
Mem:           427Mi        91Mi       295Mi       948Ki        88Mi       336Mi
Swap:           99Mi          0B        99Mi
Total:         527Mi        91Mi       395Mi

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev             81M     0   81M   0% /dev
tmpfs            43M  940K   42M   3% /run
/dev/mmcblk0p2  3.0G  1.7G  1.2G  59% /
tmpfs           214M     0  214M   0% /dev/shm
tmpfs           5.0M  8.0K  5.0M   1% /run/lock
/dev/mmcblk0p1  510M   95M  416M  19% /boot/firmware
tmpfs            43M     0   43M   0% /run/user/1000

$ sudo systemctl list-unit-files -t service | grep -e enabled
alsa-utils.service                         masked          enabled
apparmor.service                           enabled         enabled
avahi-daemon.service                       enabled         enabled
bluetooth.service                          enabled         enabled
console-setup.service                      enabled         enabled
cron.service                               enabled         enabled
cryptdisks-early.service                   masked          enabled
cryptdisks.service                         masked          enabled
dphys-swapfile.service                     enabled         enabled
e2scrub_reap.service                       enabled         enabled
fake-hwclock.service                       enabled         enabled
getty@.service                             enabled         enabled
hciuart.service                            enabled         enabled
hwclock.service                            masked          enabled
ifupdown-wait-online.service               disabled        enabled
keyboard-setup.service                     enabled         enabled
ModemManager.service                       enabled         enabled
networking.service                         enabled         enabled
NetworkManager-dispatcher.service          enabled         enabled
NetworkManager-wait-online.service         enabled         enabled
NetworkManager.service                     enabled         enabled
nfs-common.service                         masked          enabled
nftables.service                           disabled        enabled
paxctld.service                            disabled        enabled
pigpiod.service                            disabled        enabled
rc-local.service                           enabled-runtime enabled
rc.service                                 masked          enabled
rcS.service                                masked          enabled
regenerate_ssh_host_keys.service           disabled        enabled
rpcbind.service                            disabled        enabled
rpi-display-backlight.service              enabled         enabled
rpi-eeprom-update.service                  enabled         enabled
rsync.service                              disabled        enabled
serial-getty@.service                      disabled        enabled
ssh.service                                enabled         enabled
sshswitch.service                          enabled         enabled
sudo.service                               masked          enabled
systemd-fsck-root.service                  enabled-runtime enabled
systemd-network-generator.service          disabled        enabled
systemd-networkd-wait-online@.service      disabled        enabled
systemd-networkd.service                   disabled        enabled
systemd-pstore.service                     enabled         enabled
systemd-remount-fs.service                 enabled-runtime enabled
systemd-sysext.service                     disabled        enabled
systemd-timesyncd.service                  enabled         enabled
triggerhappy.service                       enabled         enabled
udisks2.service                            enabled         enabled
userconfig.service                         disabled        enabled
wpa_supplicant-nl80211@.service            disabled        enabled
wpa_supplicant-wired@.service              disabled        enabled
wpa_supplicant.service                     enabled         enabled
wpa_supplicant@.service                    disabled        enabled
x11-common.service                         masked          enabled

完全手動設定で、SSHとWifiを設定する方法

Raspberry Piを初回電源投入(起動)した後、Wifiやsshが動いていないことがわかった場合、ここで書く方法で「手動での有効化」を行うことができる。

SSHを有効化

cmdline.txtに黄色着色した部分を追加し、systemdのkernel-command-lineで実行するスクリプトファイルを指定する。(この作業をする前に、/boot/firstrun.sh ファイルが存在しないことを確認してから行うこと)

/boot/cmdline.txt に黄色着色部を追記する
console=serial0,115200 console=tty1 root=PARTUUID=6b2ecde3-02 rootfstype=ext4 fsck.repair=yes rootwait quiet systemd.run=/boot/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target

fstab設定によって/bootディレクトリにマウントされるbootパーティション(bootfs)に、firstrun.sh を次の内容で新規作成する。

/boot/firstrun.sh を新規作成する
#!/bin/bash
systemctl -q enable ssh

# 処理後に、追加した処理設定を削除する(または、この記載をなくして起動後に手動削除)
rm -f /boot/firstrun.sh
sed -i 's| systemd.run.*||g' /boot/cmdline.txt
exit 0

Wifi接続を有効化

NetwormManagerのキーファイルを作成する。

/etc/NetworkManager/system-connections/SSID名.nmconnection
[connection]
id=●SSID名称(キーファイルの拡張子除いたファイル名)●
uuid=●自動生成したUUID●
type=wifi

[wifi]
mode=infrastructure
ssid=●SSID名●

[wifi-security]
key-mgmt=wpa-psk
psk=●パスワード●

[ipv4]
method=auto

[ipv6]
addr-gen-mode=default
method=auto

[proxy]