rpi-imager OSのカスタマイズ機能を理解する

### rpi-imager OSのカスタマイズ機能を理解する

rpi-imager をターミナルから実行すると非常に有用な情報を提供してくれます。

![rpi-imager_show_all.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/0ad10677-6074-ef0f-d51e-4675de5d5891.jpeg)

設定を変更するたびに関連する項目を処理するスクリプトをコンソールに出力します。

![rpi-imager_console_output_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/003782c3-4a01-e741-1ce9-2d8839fc31f0.jpeg)

コンソールに表示されたスクリプトはブートフアイルシステム上に出力されます。OSをインストールする前であれば内容を確認することができます。
※このスクリプトは OS のインストールが完了すると削除されしまいます。


## 1 環境

### 1-1 検証用デバイス

+ Raspberry Pi 2w
    + Raspberry Pi OS (32bit) with desktop (bookworm)
    2024-11-19-raspios-bookworm-armhf.img.xz

### 1-2 開発PC

+ Ubuntu 22-04 Desktop
+ rpi-imager インストール済み
**```sudo apt-get install rpi-imager```**

## 2 OSカスタマイズ用スクリプト

### 2-1 SDカードのマウント

書き込み完了後のSDカードを取り外し、再度 USBに挿したときのマウント先
※1 Ubuntu では **bootfs は読み取り専用、rootfs は読み書きモードでマウントされます。**
※2 私の環境では /dev/sdc1 (bootfs), /dev/sdc2 (rootfs1) でした。

```bash
$ LANG=C;lsblk -e7 | grep sdc
sdc                       8:32   1  59.5G  0 disk 
|-sdc1                    8:33   1   512M  0 part /media/yukio/bootfs
`-sdc2                    8:34   1  58.9G  0 part /media/yukio/rootfs1
# bootfs に移動
$ cd $ cd /media/yukio/bootfs/
/media/yukio/bootfs$ ls -l --time-style long-iso *.sh | grep -v total
-rw-r--r-- 1 yukio yukio 2520 2024-12-19 21:27 firstrun.sh
```

### 2-2 ブート時の OSカスタマイズ用スクリプト

ブートファイルシステム (/dev/sdc1) 上に出力されたスクリプトを以下に示します。
**```/media/yukio/bootfs/firstrun.sh```**

**カスタマイズ用のスクリプトと呼び出されるスクリプトを理解すると、ネットワーク接続を固定IPアドレスに設定する等の設定ファイルの作成方法がわかるようになります。**

スクリプト中 **imager_custom**スクリプトの存在チェックをしている箇所が有りますが、通常は存在するので else 側の代替処理は実行されることありません。
※**imager_custom スクリプトは代替処理では出力されないファイルも出力します。**

```shell
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom [設定項目] [設定値]
else
   # 代替処理
fi
```

最初にスクリプト全体を下記に示します。
※ スクリプト内の SSID とパスワードは適当に加工しています。

+ カスタマイズした項目
    + 一般
        + ホスト名を変更
        + ユーザー名の変更とパスワード設定
        + WiFi接続を有効化
    + サービス
        + SSH有効、パスワード認証

```firstrun.sh
#!/bin/bash

set +e

CURRENT_HOSTNAME=`cat /etc/hostname | tr -d " \t\n\r"`
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_hostname raspi-4-dev
else
   echo raspi-4-dev >/etc/hostname
   sed -i "s/127.0.1.1.*$CURRENT_HOSTNAME/127.0.1.1\traspi-4-dev/g" /etc/hosts
fi
FIRSTUSER=`getent passwd 1000 | cut -d: -f1`
FIRSTUSERHOME=`getent passwd 1000 | cut -d: -f6`
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom enable_ssh
else
   systemctl enable ssh
fi
if [ -f /usr/lib/userconf-pi/userconf ]; then
   /usr/lib/userconf-pi/userconf 'raspi' '$5$jMUnTaL7a9$3hl.a1JPfHrvoKtbZ.gFpqac1NAWoIGYG.N8Ht2J5m8'
else
   echo "$FIRSTUSER:"'$5$jMUnTaL7a9$3hl.a1JPfHrvoKtbZ.gFpqac1NAWoIGYG.N8Ht2J5m8' | chpasswd -e
   if [ "$FIRSTUSER" != "raspi" ]; then
      usermod -l "raspi" "$FIRSTUSER"
      usermod -m -d "/home/raspi" "raspi"
      groupmod -n "raspi" "$FIRSTUSER"
      if grep -q "^autologin-user=" /etc/lightdm/lightdm.conf ; then
         sed /etc/lightdm/lightdm.conf -i -e "s/^autologin-user=.*/autologin-user=raspi/"
      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/raspi/"
      fi
      if [ -f /etc/sudoers.d/010_pi-nopasswd ]; then
         sed -i "s/^$FIRSTUSER /raspi /" /etc/sudoers.d/010_pi-nopasswd
      fi
   fi
fi
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_wlan 'xxxxxx2g-yyyyyy' '1234ec0f4cb652cc3873de1780a95ef8aec245c838e7ac61251c20b8ecb56789' 'JP'
else
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="xxxxx-yyyyy"
	psk=1234ec0f4cb652cc3873de1780a95ef8aec245c838e7ac61251c20b8ecb56789
}

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
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_keymap 'jp'
   /usr/lib/raspberrypi-sys-mods/imager_custom set_timezone 'Asia/Tokyo'
else
   rm -f /etc/localtime
   echo "Asia/Tokyo" >/etc/timezone
   dpkg-reconfigure -f noninteractive tzdata
cat >/etc/default/keyboard <<'KBEOF'
XKBMODEL="pc105"
XKBLAYOUT="jp"
XKBVARIANT=""
XKBOPTIONS=""

KBEOF
   dpkg-reconfigure -f noninteractive keyboard-configuration
fi
rm -f /boot/firstrun.sh
sed -i 's| systemd.run.*||g' /boot/cmdline.txt
exit 0
```

#### 2-2 (1) 一般: ホスト名変更

[既定値] raspberryPi
    
![rpi-imager_osCustom_1_general_1_hostname.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/5841fb9e-c12f-fdaf-11d8-3dd996de9e79.jpeg)

ルートファイルシステム上の値

```bash
$ cd /media/yukio/rootfs1/etc/
# Default hostname
/media/yukio/rootfs1/etc$ cat hostname
raspberrypi
#
# hosts
/media/yukio/rootfs1/etc$ cat hosts
127.0.0.1	localhost
::1		localhost ip6-localhost ip6-loopback
ff02::1		ip6-allnodes
ff02::2		ip6-allrouters

127.0.1.1		raspberrypi
```

ホスト名変更処理

```firstrun.sh
#!/bin/bash

set +e

CURRENT_HOSTNAME=`cat /etc/hostname | tr -d " \t\n\r"`
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_hostname raspi-4-dev
else
   echo raspi-4-dev >/etc/hostname
   sed -i "s/127.0.1.1.*$CURRENT_HOSTNAME/127.0.1.1\traspi-4-dev/g" /etc/hosts
fi
```

#### 2-2 (2) 一般: ユーザ名とパスワード

[既定値] pi 

![rpi-imager_osCustom_1_general_2_user_passwd.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/e668207b-1ec0-77c6-4bd2-13e50b8c7132.jpeg)

```shell
FIRSTUSER=`getent passwd 1000 | cut -d: -f1`
FIRSTUSERHOME=`getent passwd 1000 | cut -d: -f6`
if [ -f /usr/lib/userconf-pi/userconf ]; then
   /usr/lib/userconf-pi/userconf 'raspi' '$5$jMUnTaL7a9$3hl.a1JPfHrvoKtbZ.gFpqac1NAWoIGYG.N8Ht2J5m8'
else
   echo "$FIRSTUSER:"'$5$jMUnTaL7a9$3hl.a1JPfHrvoKtbZ.gFpqac1NAWoIGYG.N8Ht2J5m8' | chpasswd -e
   if [ "$FIRSTUSER" != "raspi" ]; then
      usermod -l "raspi" "$FIRSTUSER"
      usermod -m -d "/home/raspi" "raspi"
      groupmod -n "raspi" "$FIRSTUSER"
      if grep -q "^autologin-user=" /etc/lightdm/lightdm.conf ; then
         sed /etc/lightdm/lightdm.conf -i -e "s/^autologin-user=.*/autologin-user=raspi/"
      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/raspi/"
      fi
      if [ -f /etc/sudoers.d/010_pi-nopasswd ]; then
         sed -i "s/^$FIRSTUSER /raspi /" /etc/sudoers.d/010_pi-nopasswd
      fi
   fi
fi
```

[補足] FIRSTUSER と FIRSTUSERHOME を開発PCで確認すると以下のようになりました。
```bash
$ getent passwd 1000      
yukio:x:1000:1000:yukio,,,:/home/yukio:/bin/bash
$ getent passwd 1000 | cut -d: -f1
yukio
$ getent passwd 1000 | cut -d: -f6
/home/yukio
```

#### 2-2 (3) 一般: Wi-Fi設定

![rpi-imager_osCustom_1_general_3_wifi_settings.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/0b17e25d-f736-516c-dc73-d1bcc892c3c7.jpeg)

+ 作成される設定ファイル
    + /etc/wpa_supplicant/wpa_supplicant.conf
    **※代替処理では NetworkManager用のWiFi接続設定ファイルは出力しません。**

```shell
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_wlan 'xxxxx-yyyyy' '1234ec0f4cb652cc3873de1780a95ef8aec245c838e7ac61251c20b8ecb56789' 'JP'
else
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="xxxxx-yyyyy"
	psk=1234ec0f4cb652cc3873de1780a95ef8aec245c838e7ac61251c20b8ecb56789
}

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
```

#### 2-2 (4) 一般: ロケール変更

![rpi-imager_osCustom_1_general_4_keyboard_timezone.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/cbce74f8-17c0-e299-df00-3b61193f8268.jpeg)


```shell
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom set_keymap 'jp'
   /usr/lib/raspberrypi-sys-mods/imager_custom set_timezone 'Asia/Tokyo'
else
   rm -f /etc/localtime
   echo "Asia/Tokyo" >/etc/timezone
   dpkg-reconfigure -f noninteractive tzdata
cat >/etc/default/keyboard <<'KBEOF'
XKBMODEL="pc105"
XKBLAYOUT="jp"
XKBVARIANT=""
XKBOPTIONS=""

KBEOF
   dpkg-reconfigure -f noninteractive keyboard-configuration
fi
```

#### 2-2 (5) サービス: SSHの使用

![rpi-imager_osCustom_2_service_1_ssh_withPasswd.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/647dc8b3-1a8c-5f7d-a030-979b22b82cd8.jpeg)

```shell
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom enable_ssh
else
   systemctl enable ssh
fi
```

[補足] 「公開鍵認証のみを許可する」を選択し開発PCの公開鍵を指定したときの例

![rpi-imager_osCustomize_2_service_2_sshWithPublicKey.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/c00eb97b-c279-dccd-2d5e-c3f9c8bdf2e6.jpeg)

※公開鍵は一部省略しています。

```shell
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
   /usr/lib/raspberrypi-sys-mods/imager_custom enable_ssh -k 'ssh-rsa AA...一部省略...@Dell-T7500'
else
   install -o "$FIRSTUSER" -m 700 -d "$FIRSTUSERHOME/.ssh"
   install -o "$FIRSTUSER" -m 600 <(printf "ssh-rsa AA...一部省略...@Dell-T7500") "$FIRSTUSERHOME/.ssh/authorized_keys"
   echo 'PasswordAuthentication no' >>/etc/ssh/sshd_config
   systemctl enable ssh
fi
```



#### 2-2 (6) クリーンアップ

(1) ```/boot/firstrun.sh``` の削除
(2) cmdline.txt から **firstrun.sh** 実行部分を削除する

```shell
rm -f /boot/firstrun.sh
sed -i 's| systemd.run.*||g' /boot/cmdline.txt
exit 0
```

### 2-3 カスタマイズ処理メインスクリプト

firstrun.sh スクリプトから呼び出されるスクリプト一覧

```bash
# ユーザーとパスワード設定を除く設定
/media/yukio/rootfs1/usr/lib$ tree raspberrypi-sys-mods/ | grep -v dir | grep -v ^$
raspberrypi-sys-mods/
|-- firstboot
|-- get_fw_loc
|-- i2cprobe
|-- imager_custom            # ★★ シェルスクリプト
|-- init_config
|-- regenerate_ssh_host_keys
`-- sshswitch
#
# ユーザーとパスワード設定
/media/yukio/rootfs1/usr/lib$ tree userconf-pi/ | grep -v dir | grep -v ^$
userconf-pi/
|-- userconf                 # ★★ シェルスクリプト
`-- userconf-service
```

**```/usr/lib/raspberrypi-sys-mods/imager_custom```**

上記 [2-2] で呼び出しされた処理のみ抜粋します。

このスクリプトで実行している **``` raspi-config CLI```** については下記をご覧ください。
[(公式) Raspberry Pi Documentation Configuration: raspi-config#raspi-config-cli](https://www.raspberrypi.com/documentation/computers/configuration.html#raspi-config-cli)


#### 2-3 (1) 一般: ホスト名変更

**``` raspi-config CLI```** コマンドを実行してる部分が代替処理とは異なります。


```shell
set_hostname () (
  if [ "$#" -ne 1 ]; then
    usage set_hostname
    exit 1
  fi
  HOSTNAME="$1"
  raspi-config nonint do_hostname "$HOSTNAME" # ★この部分が代替処理と異なる
  echo "$HOSTNAME" > /etc/hostname
)
```

#### 2-3 (2) 一般: ユーザ名とパスワード

この処理のみ **```/usr/lib/userconf-pi/userconf```** スクリプトを使っています。

ソースを見る限り代替処理とほぼ同じですが、最後の **```cancel-rename```** スクリプトの中で ```raspi-config CLI``` コマンドを実行してる部分が異なっていました。

```shell
#!/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
    FIRSTUSER="$1"
    FIRSTGROUP="$1"
    shift
else
    FIRSTUSER="$(getent passwd 1000 | cut -d: -f1)"
    FIRSTGROUP="$(getent group 1000 | cut -d: -f1)"
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"  # ★ シェルスクリプト
```

#### 2-3 (3) 一般: Wi-Fi設定

**NetworkManager** 用のWiFi接続設定ファイルを出力します。
+ 出力先ディレクトリ: **/etc/NetworkManager/system-connections/**
+ WiFi接続設定ファイル: **preconfigured.nmconnection**

※1 **wpa_supplicant.conf** は WiFi接続には使われないため作成されません。
**※2 Wi-Fi接続設定ファイルは DHCPサーバーからIPアドレスを取得する仕様になっておりこの設定をOSイントール時に変更することはできません。**
※3 以下のコードを参考に 有線ネットワークのIPv4 固定アドレスを設定する接続ファイルを作成することが出来そうです。


```shell
set_wlan_country () (
  if [ "$#" -ne 1 ]; then
    usage set_wlan_country
    exit 1
  fi
  # shellcheck disable=SC2030
  COUNTRY="$1"
  raspi-config nonint do_wifi_country "$COUNTRY"
)

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}
)
```

[参考 (1)] 実際にインストール後に生成されたファイルを下記に示します。
※ ssid と psk は適当な値に修正しています。

```preconfigured.nmconnection
[connection]
id=preconfigured
uuid=c9677410-652a-457f-abe2-0a3f7a1dd8af
type=wifi
[wifi]
mode=infrastructure
ssid=xxxxx-yyyyy
hidden=false
[ipv4]
method=auto
[ipv6]
addr-gen-mode=default
method=auto
[proxy]
[wifi-security]
key-mgmt=wpa-psk
psk=1234ec0f4cb652cc3873de1780a95ef8aec245c838e7ac61251c20b8ecb56789
```

[補足] NetworkManager のディレクトリ構成

```bash
/media/yukio/rootfs1/etc$ tree NetworkManager/ | grep -v dir | grep -v ^$
NetworkManager/
├── NetworkManager.conf
├── conf.d
├── dispatcher.d
│   ├── 01-ifupdown
│   ├── no-wait.d
│   ├── pre-down.d
│   └── pre-up.d
├── dnsmasq-shared.d
├── dnsmasq.d
└── system-connections # ★ このディレクトリに接続設定ファイル格納する
```

ルートファイルシステム上の **system-connections** ディレクトリ(★)に、**固定IPアドレスを設定した有線ネットワーク接続設定ファイルをコピーしておくと OSインストール中に固定IPアドレスによるネットワーク接続が可能になります。**
※ Ubuntu では ルートファイルシステムは読み書きモードになっているのでコピーされた設定ファイルはSDカードに保持されます。


#### 2-3 (4) 一般: ロケール変更

**rpi-imager の画面では「ロケールを設定する」となっていますが、カスタマイズ処理ではロケール自体は処理されません。OSインストール後に手動で実行する必要が有ります。**

キーボード設定: [引数] "jp"

```shell
set_keymap () (
  if [ "$#" -ne 1 ]; then
    usage set_keymap
    exit 1
  fi
  raspi-config nonint do_configure_keyboard "$1"
)
```

タイムゾーンゾーン設定: [引数] "Asia/Tokyo"

```shell
set_timezone () (
  if [ "$#" -ne 1 ]; then
    usage set_timezone
    exit 1
  fi
  raspi-config nonint do_change_timezone "$1"
)
```

[参考] OSインストール終了後にSSHログインしてロケールを確認したところ

```bash
# Default: UK English
raspi@raspi-2w-dev:~ $ echo $LANG
en_GB.UTF-8
raspi@raspi-2w-dev:~ $ cat /etc/locale.gen | grep -v ^# | grep -v ^$
en_GB.UTF-8 UTF-8
```


#### 2-3 (5) サービス: SSHの使用

+ 引数に応じて **```/etc/ssh/sshd_conf```** の設定を変更
+ SSH サービスを有効に設定

```shell
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
)
```

[補足] 公開鍵が指定された場合、下記のようにパスワード認証がで不可に設定されます。 

**```/etc/ssh/ssh_config```**
```bash
raspi@raspi-2w-dev:~ $ cat /etc/ssh/sshd_config \
> | grep -v ^# | grep -v ^$ | grep Password
PasswordAuthentication no
```

**OSのカスタマイズ機能が OSインストール時にどのように処理されているか、呼び出し先のスクリプトを見ることで理解できるようになりました。**

**特に WiFi接続処理ではどのディレクトリにどのような接続設定ファイルを設定するとWiFi接続が利用可能になるかも理解できました。**

多くの処理が **raspi-config CLI** を呼び出すことによりカスタマイズ機能を実現しています。 


### 3 raspi-config CLI の活用

最近の Raspberry Pi は性能が良くなったとはいえ、GUIによる操作は普段使いのパソコンのようにサクサクと快適に行えるものではありません。

**今回検証に使用した Raspberry Pi Zero 2W では Rapberry Pi Configuration 画面を表示持するのに約8秒近くかかりました。**

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/89516161-1bb0-a7b0-818a-d1f3621ebd7e.png">


さすがに OSインストール時のみモニターを接続する必要が有りますが、Raspberry Pi をサーバー用途で使う場合、リモートから SSHでログインして保守作業を行なえるので普段はモニターもキーボードも端末に接続することはありません。

![RaspiALL_overview.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/9ef2110c-e97f-e12b-1e7a-7fa829b6de95.jpeg)

**OSインストール完了直後に表示れさる画面には WiFi 接続の IPアドレスが数秒間トーストで表示されます。そのIPアドレスに SSHでログインすればあとはコマンドラインから固定IPアドレスに変更することも可能です。**

![Raspi2w_0_32bit_boot1_GUI_networkConnected.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/01b1bd99-9c9c-6385-a755-0d3226081e25.jpeg)
※このスクリーンショットを取るためにキーボードは繋いでいます。


#### 3-1 キャラクタベースの設定画面からの変更 

開発用PCからディスクトップ画面に表示されたIPアドレスにSSHログインします。

管理者権限 (sudo) で raspi-config スクリプトを引数なしで実行するとテキストモード用のウィンドウが開きメニューを選択しながら各種設定変更が可能です。

```bash
raspi@raspi-2w-dev:~ $ sudo raspi-config
```

以下は最初に表示されるトップ画面です。

![raspi-config_00_start.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/00869ad3-6090-2641-5f1e-960848fc42b2.png)

例えば I2C プロトコルを有効にするには、インターフェイスメニューを辿り I2C を選択
![Raspi-config_01_changerInterface_1_menu.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/941548fc-6f84-60c3-a6ed-5b64dc2cd893.jpeg)

有効化確認画面で &lt;Yes&gt; を選択し、変更済み画面で &lt;ok&gt; を押下することになります。

![Raspi-config_01_changerInterface_2_enable_I2C.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/793e9695-ddd6-c1eb-3bc9-a3ed58247f49.jpeg)

ただしこの方法も変更する項目が多いと結構ストレスの溜る作業になります。

#### 3-2 raspi-config CLI で変更する

**これ以降の中級者以上向けの内容となります。** 

初心者の方は失敗しても良いようにOSインストール後にシャットダウンし、SDカードをパソコンにバックアップしてから実行してください。

[公式ドキュメント Configuration non-interactive raspi-config](https://www.raspberrypi.com/documentation/computers/configuration.html#raspi-config-cli)

コマンド実行オブションとして **nonint (non-interactive)** を最初に指定します。

$ sudo raspi-config **nonint** <command> <arguments> [optional-argument]

先の I2Cの例だと raspi-config CLIを使うとたった1行で済んでしまいます。。

```bash
raspi@raspi-2w-dev:~ $ sudo raspi-config nonint do_i2c 0
# 設定値の確認 ※0: 有効
raspi@raspi-2w-dev:/boot $ raspi-config nonint get_i2c
0
```

以降の設定は Raspberry Pi をサーバーとして利用する場合の最低限の設定になります。

##### 3-2-1 ロケールを日本語に変更する

もし Raspberry Pi を日本語を表示するようなスクリプトを実行するには(例えば matplotlib 等) ロケールを日本語に変える必要があります。項目 [ 2-3 (4) ] で簡単に説明しましたがカスタマイズしてもロケールはデフォルトのままです。

```bash
# OSインストール直後
raspi@raspi-2w-dev:~ $ echo $LANG
en_GB.UTF-8
```

最近の Raspberr Pi OS (Debian) でサポートしている日本語ロケールは2つしかありません。
※ただし 2行目の EUC-JP は使うことは無いでしょう。

```bash
raspi@raspi-2w-dev:~ $ cat /usr/share/i18n/SUPPORTED | grep ja
ja_JP.UTF-8 UTF-8
ja_JP.EUC-JP EUC-JP
```

値はスペース区切りで下記のように2つ設定します。
**```do_change_locale ja_JP.UTF-8 UTF-8```**

```bash
raspi@raspi-2w-dev:~ $ sudo raspi-config nonint do_change_locale ja_JP.UTF-8 UTF-8
Generating locales (this might take a while)...
  ja_JP.UTF-8... done
Generation complete.
raspi@raspi-2w-dev:~ $ sudo reboot

Broadcast message from root@raspi-2w-dev on pts/1 (Mon 2024-12-23 16:53:04 JST):

The system will reboot now!

```

リブート後に変更が反映されます。

```bash
raspi@raspi-2w-dev:~ $ localectl status
System Locale: LANG=ja_JP.UTF-8
    VC Keymap: (unset)         
   X11 Layout: jp
    X11 Model: pc105
```

##### 3-2-2 ブートモードをコンソールログインに変更

コンソール起動、ログインが必要 (B1) に変更します。

**```do_boot_behavior B1```**

```bash
# OSインストール直後は GUI
raspi@raspi-2w-dev:~ $ systemctl get-default
graphical.target
# コンソール起動、ログインが必要 (B1) に変更
raspi@raspi-2w-dev:~ $ sudo raspi-config nonint do_boot_behaviour B1
# コンソール起動に変更されます
raspi@raspi-2w-dev:~ $ systemctl get-default
multi-user.target
```
リブート後に画面はコンソール画面に切り替わりログイン待受けとなりますが、待受状態でもサービスは稼働しています。


##### 3-2-3 インターフェイスを有効する

I2C と SPI、シリアルポート (UART) を有効にしておけば当面は問題ないでしょう。

初期設定を確認します。値が1なら利用不可です。

```bash
raspi@raspi-2w-dev:~ $ raspi-config nonint get_i2c
1
raspi@raspi-2w-dev:~ $ raspi-config nonint get_spi
1
raspi@raspi-2w-dev:~ $ raspi-config nonint get_serial_hw 
1
```

それぞれ有効に設定する

```bash
raspi@raspi-2w-dev:~ $ sudo raspi-config nonint do_i2c 0
raspi@raspi-2w-dev:~ $ sudo raspi-config nonint do_spi 0
raspi@raspi-2w-dev:~ $ sudo raspi-config nonint do_serial_hw 0
```

それぞれ変更結果を確認する

```bash
raspi@raspi-2w-dev:~ $ raspi-config nonint get_i2c
0
raspi@raspi-2w-dev:~ $ raspi-config nonint get_spi
0
raspi@raspi-2w-dev:~ $ raspi-config nonint get_serial_hw 
0
```

変更した項目は /boot/firmware/config.txt に反映されます。
※変更前にファイルをコピー (config_org.txt) し、設定後に diffで差分を確認
※先頭: '+' のものが反映された行の値、'-'のものはコメントアウトされた行

```bash
$ diff --unified config_org.txt config.txt
*****************************************************************************************
--- config_org.txt	2024-12-22 15:31:18.063320145 +0900
+++ config.txt	2024-12-22 15:31:51.571455916 +0900
@@ -3,9 +3,9 @@
 # Some settings may impact device functionality. See link above for details
 
 # Uncomment some or all of these to enable the optional hardware interfaces
-#dtparam=i2c_arm=on
+dtparam=i2c_arm=on
 #dtparam=i2s=on
-#dtparam=spi=on
+dtparam=spi=on
 
 # Enable audio (loads snd_bcm2835)
 dtparam=audio=on
@@ -46,4 +46,5 @@
 dtoverlay=dwc2,dr_mode=host
 
 [all]
+enable_uart=1
```

### 最後に

**raspi-config** はバイナリではなく普通のシェルスクリプトで作られています。

```bash
raspi@raspi-2w-dev:~ $ which raspi-config
/usr/bin/raspi-config
raspi@raspi-2w-dev:~ $ LANG=C;ls -l /usr/bin/raspi-config --time-style long-iso \                             
 | grep -v total
-rwxr-xr-x 1 root root 120254 2024-12-10 20:47 /usr/bin/raspi-config
```

ローカルの PC にコピーしてソースを見れば、実行するコマンドと必要なパラメータが確認できます。さらにはシェルスクリプトの勉強にもなるので一度ご覧になってみてください。
※**くれぐれも端末上のvimで直接スクリプトを見ないように。**

以下は開発用PCのディスクトップにコピーしたもの。

```bash
$ scp raspi@192.168.0.23:/usr/bin/raspi-config ~/Desktop
raspi-config                       100%  117KB   8.0MB/s   00:00    
```

ちなみにロケール変更は以下の関数です。

```bash
do_change_locale() {
  if [ "$INTERACTIVE" = True ]; then
    dpkg-reconfigure locales
  else
    if ! LOCALE_LINE="$(grep -E "^$1( |$)" /usr/share/i18n/SUPPORTED)"; then
      return 1
    fi
    export LC_ALL=C
    export LANG=C
    LG="/etc/locale.gen"
    NEW_LANG="$(echo $LOCALE_LINE | cut -f1 -d " ")"
    [ -L "$LG" ] && [ "$(readlink $LG)" = "/usr/share/i18n/SUPPORTED" ] && rm -f "$LG"
    echo "$LOCALE_LINE" > /etc/locale.gen
    update-locale --no-checks LANG
    update-locale --no-checks "LANG=$NEW_LANG"
    dpkg-reconfigure -f noninteractive locales
  fi
}
```

こちらの画面は開発用PCで **```dpkg-reconfigure locales```** 実行したもの。

```bash
$ sudo dpkg-reconfigure locales
```

![dpkg-reconfigure_locales.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/7874dea3-3407-8e17-c902-9285d65f4a91.png)

次回は、今回紹介した imager_custom スクリプトの WiFi接続処理を参考に、NetworkManager用に固定IPアドレスを設定した有線接続設定ファイルとWiFi接続設定ファイルを生成するシェルスクリプトを紹介する予定です。