|
| 1 | +#!/bin/bash |
| 2 | +# shellcheck disable=all |
| 3 | + |
| 4 | + |
| 5 | +reboot_pi () { |
| 6 | + umount /boot |
| 7 | + mount / -o remount,ro |
| 8 | + sync |
| 9 | + if [ "$NOOBS" = "1" ]; then |
| 10 | + if [ "$NEW_KERNEL" = "1" ]; then |
| 11 | + reboot -f "$BOOT_PART_NUM" |
| 12 | + sleep 5 |
| 13 | + else |
| 14 | + echo "$BOOT_PART_NUM" > "/sys/module/${BCM_MODULE}/parameters/reboot_part" |
| 15 | + fi |
| 16 | + fi |
| 17 | + reboot -f |
| 18 | + sleep 5 |
| 19 | + exit 0 |
| 20 | +} |
| 21 | + |
| 22 | +check_noobs () { |
| 23 | + if [ "$BOOT_PART_NUM" = "1" ]; then |
| 24 | + NOOBS=0 |
| 25 | + else |
| 26 | + NOOBS=1 |
| 27 | + fi |
| 28 | +} |
| 29 | + |
| 30 | +get_variables () { |
| 31 | + ROOT_PART_DEV=$(findmnt / -o source -n) |
| 32 | + ROOT_PART_NAME=$(echo "$ROOT_PART_DEV" | cut -d "/" -f 3) |
| 33 | + ROOT_DEV_NAME=$(echo /sys/block/*/"${ROOT_PART_NAME}" | cut -d "/" -f 4) |
| 34 | + ROOT_DEV="/dev/${ROOT_DEV_NAME}" |
| 35 | + ROOT_PART_NUM=$(cat "/sys/block/${ROOT_DEV_NAME}/${ROOT_PART_NAME}/partition") |
| 36 | + |
| 37 | + BOOT_PART_DEV=$(findmnt /boot -o source -n) |
| 38 | + BOOT_PART_NAME=$(echo "$BOOT_PART_DEV" | cut -d "/" -f 3) |
| 39 | + BOOT_DEV_NAME=$(echo /sys/block/*/"${BOOT_PART_NAME}" | cut -d "/" -f 4) |
| 40 | + BOOT_PART_NUM=$(cat "/sys/block/${BOOT_DEV_NAME}/${BOOT_PART_NAME}/partition") |
| 41 | + |
| 42 | + OLD_DISKID=$(fdisk -l "$ROOT_DEV" | sed -n 's/Disk identifier: 0x\([^ ]*\)/\1/p') |
| 43 | + |
| 44 | + check_noobs |
| 45 | + |
| 46 | + ROOT_DEV_SIZE=$(cat "/sys/block/${ROOT_DEV_NAME}/size") |
| 47 | + TARGET_END=$((ROOT_DEV_SIZE - 1)) |
| 48 | + |
| 49 | + PARTITION_TABLE=$(parted -m "$ROOT_DEV" unit s print | tr -d 's') |
| 50 | + |
| 51 | + LAST_PART_NUM=$(echo "$PARTITION_TABLE" | tail -n 1 | cut -d ":" -f 1) |
| 52 | + |
| 53 | + ROOT_PART_LINE=$(echo "$PARTITION_TABLE" | grep -e "^${ROOT_PART_NUM}:") |
| 54 | + ROOT_PART_START=$(echo "$ROOT_PART_LINE" | cut -d ":" -f 2) |
| 55 | + ROOT_PART_END=$(echo "$ROOT_PART_LINE" | cut -d ":" -f 3) |
| 56 | + |
| 57 | + if [ "$NOOBS" = "1" ]; then |
| 58 | + EXT_PART_LINE=$(echo "$PARTITION_TABLE" | grep ":::;" | head -n 1) |
| 59 | + EXT_PART_NUM=$(echo "$EXT_PART_LINE" | cut -d ":" -f 1) |
| 60 | + EXT_PART_START=$(echo "$EXT_PART_LINE" | cut -d ":" -f 2) |
| 61 | + EXT_PART_END=$(echo "$EXT_PART_LINE" | cut -d ":" -f 3) |
| 62 | + fi |
| 63 | +} |
| 64 | + |
| 65 | +fix_partuuid() { |
| 66 | + mount -o remount,rw "$ROOT_PART_DEV" |
| 67 | + mount -o remount,rw "$BOOT_PART_DEV" |
| 68 | + MAJOR="$(uname -r | cut -f1 -d.)" |
| 69 | + if [[ "$MAJOR" -eq "6" ]] && [[ -c /dev/hwrng ]]; then |
| 70 | + dd if=/dev/hwrng of=/dev/urandom count=1 bs=256 status=none |
| 71 | + fi |
| 72 | + DISKID="$(tr -dc 'a-f0-9' < /dev/urandom | dd bs=1 count=8 2>/dev/null)" |
| 73 | + fdisk "$ROOT_DEV" > /dev/null <<EOF |
| 74 | +x |
| 75 | +i |
| 76 | +0x$DISKID |
| 77 | +r |
| 78 | +w |
| 79 | +EOF |
| 80 | + if [ "$?" -eq 0 ]; then |
| 81 | + sed -i "s/${OLD_DISKID}/${DISKID}/g" /etc/fstab |
| 82 | + sed -i "s/${OLD_DISKID}/${DISKID}/" /boot/cmdline.txt |
| 83 | + sync |
| 84 | + fi |
| 85 | + |
| 86 | + mount -o remount,ro "$ROOT_PART_DEV" |
| 87 | + mount -o remount,ro "$BOOT_PART_DEV" |
| 88 | +} |
| 89 | + |
| 90 | +fix_wpa() { |
| 91 | + if [ -e /boot/firstrun.sh ] \ |
| 92 | + && ! grep -q 'imager_custom set_wlan' /boot/firstrun.sh \ |
| 93 | + && grep -q wpa_supplicant.conf /boot/firstrun.sh; then |
| 94 | + mount -o remount,rw "$ROOT_PART_DEV" |
| 95 | + modprobe rfkill |
| 96 | + REGDOMAIN=$(sed -n 's/^\s*country=\(..\)$/\1/p' /boot/firstrun.sh) |
| 97 | + [ -n "$REGDOMAIN" ] && raspi-config nonint do_wifi_country "$REGDOMAIN" |
| 98 | + if systemctl -q is-enabled NetworkManager; then |
| 99 | + systemctl disable NetworkManager |
| 100 | + fi |
| 101 | + mount -o remount,ro "$ROOT_PART_DEV" |
| 102 | + fi |
| 103 | +} |
| 104 | + |
| 105 | +check_variables () { |
| 106 | + if [ "$NOOBS" = "1" ]; then |
| 107 | + if [ "$EXT_PART_NUM" -gt 4 ] || \ |
| 108 | + [ "$EXT_PART_START" -gt "$ROOT_PART_START" ] || \ |
| 109 | + [ "$EXT_PART_END" -lt "$ROOT_PART_END" ]; then |
| 110 | + FAIL_REASON="Unsupported extended partition\n$FAIL_REASON" |
| 111 | + return 1 |
| 112 | + fi |
| 113 | + fi |
| 114 | + |
| 115 | + if [ "$ROOT_PART_NUM" -ne "$LAST_PART_NUM" ]; then |
| 116 | + # Skip resize if root partition is not last |
| 117 | + return 1 |
| 118 | + fi |
| 119 | + |
| 120 | + if [ "$ROOT_PART_END" -gt "$TARGET_END" ]; then |
| 121 | + FAIL_REASON="Root partition runs past the end of device\n$FAIL_REASON" |
| 122 | + return 1 |
| 123 | + fi |
| 124 | + |
| 125 | + if [ ! -b "$ROOT_DEV" ] || [ ! -b "$ROOT_PART_DEV" ] || [ ! -b "$BOOT_PART_DEV" ] ; then |
| 126 | + FAIL_REASON="Could not determine partitions\n$FAIL_REASON" |
| 127 | + return 1 |
| 128 | + fi |
| 129 | + if [ "$ROOT_PART_END" -eq "$TARGET_END" ]; then |
| 130 | + # Root partition already the expected size |
| 131 | + return 1 |
| 132 | + fi |
| 133 | +} |
| 134 | + |
| 135 | +check_kernel () { |
| 136 | + MAJOR="$(uname -r | cut -f1 -d.)" |
| 137 | + MINOR="$(uname -r | cut -f2 -d.)" |
| 138 | + if [ "$MAJOR" -eq "4" ] && [ "$MINOR" -lt "9" ]; then |
| 139 | + return 0 |
| 140 | + fi |
| 141 | + if [ "$MAJOR" -lt "4" ]; then |
| 142 | + return 0 |
| 143 | + fi |
| 144 | + NEW_KERNEL=1 |
| 145 | +} |
| 146 | + |
| 147 | +do_resize () { |
| 148 | + check_kernel |
| 149 | + |
| 150 | + if [ "$NOOBS" = "1" ] && [ "$NEW_KERNEL" != "1" ]; then |
| 151 | + BCM_MODULE=$(grep -e "^Hardware" /proc/cpuinfo | cut -d ":" -f 2 | tr -d " " | tr '[:upper:]' '[:lower:]') |
| 152 | + if ! modprobe "$BCM_MODULE"; then |
| 153 | + FAIL_REASON="Couldn't load BCM module $BCM_MODULE\n$FAIL_REASON" |
| 154 | + return 1 |
| 155 | + fi |
| 156 | + fi |
| 157 | + |
| 158 | + whiptail --infobox "Resizing root filesystem...\n\nDepending on storage size and speed, this may take a while." 20 60 |
| 159 | + if [ "$NOOBS" = "1" ]; then |
| 160 | + if ! printf "resizepart %s\nyes\n%ss\n" "$EXT_PART_NUM" "$TARGET_END" | parted "$ROOT_DEV" ---pretend-input-tty; then |
| 161 | + FAIL_REASON="Extended partition resize failed\n$FAIL_REASON" |
| 162 | + return 1 |
| 163 | + fi |
| 164 | + fi |
| 165 | + |
| 166 | + if ! parted -m "$ROOT_DEV" u s resizepart "$ROOT_PART_NUM" "$TARGET_END"; then |
| 167 | + FAIL_REASON="Partition table resize of the root partition ($ROOT_PART_DEV) failed\n$FAIL_REASON" |
| 168 | + return 1 |
| 169 | + fi |
| 170 | + |
| 171 | + mount -o remount,rw / |
| 172 | + resize2fs "$ROOT_PART_DEV" > /dev/null 2>&1 |
| 173 | + RET="$?" |
| 174 | + if [ "$RET" -ne 0 ]; then |
| 175 | + FAIL_REASON="Root partition resize failed\n$FAIL_REASON" |
| 176 | + fi |
| 177 | + |
| 178 | + mount -o remount,ro / |
| 179 | + return "$RET" |
| 180 | +} |
| 181 | + |
| 182 | +regenerate_ssh_host_keys () { |
| 183 | + mount -o remount,rw / |
| 184 | + /usr/lib/raspberrypi-sys-mods/regenerate_ssh_host_keys |
| 185 | + RET="$?" |
| 186 | + mount -o remount,ro / |
| 187 | + return "$RET" |
| 188 | +} |
| 189 | + |
| 190 | +apply_custom () { |
| 191 | + CONFIG_FILE="$1" |
| 192 | + mount -o remount,rw / |
| 193 | + mount -o remount,rw /boot |
| 194 | + if ! python3 -c "import toml" 2> /dev/null; then |
| 195 | + FAIL_REASON="custom.toml provided, but python3-toml is not installed\n$FAIL_REASON" |
| 196 | + else |
| 197 | + set -o pipefail |
| 198 | + /usr/lib/raspberrypi-sys-mods/init_config "$CONFIG_FILE" |& tee /run/firstboot.log | while read -r line; do |
| 199 | + MSG="$MSG\n$line" |
| 200 | + whiptail --infobox "$MSG" 20 60 |
| 201 | + done |
| 202 | + if [ "$?" -ne 0 ]; then |
| 203 | + mv /run/firstboot.log /var/log/firstboot.log |
| 204 | + FAIL_REASON="Failed to apply customisations from custom.toml\n\nLog file saved as /var/log/firstboot.log\n$FAIL_REASON" |
| 205 | + fi |
| 206 | + set +o pipefail |
| 207 | + fi |
| 208 | + rm -f "$CONFIG_FILE" |
| 209 | + mount -o remount,ro /boot |
| 210 | + mount -o remount,ro / |
| 211 | +} |
| 212 | + |
| 213 | +main () { |
| 214 | + get_variables |
| 215 | + |
| 216 | + if check_variables; then |
| 217 | + do_resize |
| 218 | + fi |
| 219 | + |
| 220 | + # Switch to dhcpcd here if Imager < v1.7.3 was used to generate firstrun.sh |
| 221 | + fix_wpa > /dev/null 2>&1 |
| 222 | + |
| 223 | + whiptail --infobox "Generating SSH keys..." 20 60 |
| 224 | + regenerate_ssh_host_keys |
| 225 | + |
| 226 | + if [ -f "/boot/custom.toml" ]; then |
| 227 | + MSG="Applying customisations from custom.toml...\n" |
| 228 | + whiptail --infobox "$MSG" 20 60 |
| 229 | + apply_custom "/boot/custom.toml" |
| 230 | + fi |
| 231 | + |
| 232 | + whiptail --infobox "Fix PARTUUID..." 20 60 |
| 233 | + fix_partuuid |
| 234 | + |
| 235 | + return 0 |
| 236 | +} |
| 237 | + |
| 238 | +mount -t proc proc /proc |
| 239 | +mount -t sysfs sys /sys |
| 240 | +mount -t tmpfs tmp /run |
| 241 | +mkdir -p /run/systemd |
| 242 | + |
| 243 | +mount /boot |
| 244 | +mount / -o remount,ro |
| 245 | + |
| 246 | +sed -i 's| init=/usr/lib/raspberrypi-sys-mods/firstboot||' /boot/cmdline.txt |
| 247 | +sed -i 's| sdhci\.debug_quirks2=4||' /boot/cmdline.txt |
| 248 | + |
| 249 | +if ! grep -q splash /boot/cmdline.txt; then |
| 250 | + sed -i "s/ quiet//g" /boot/cmdline.txt |
| 251 | +fi |
| 252 | +mount /boot -o remount,ro |
| 253 | +sync |
| 254 | + |
| 255 | +main |
| 256 | + |
| 257 | +if [ -z "$FAIL_REASON" ]; then |
| 258 | + whiptail --infobox "Rebooting in 5 seconds..." 20 60 |
| 259 | + sleep 5 |
| 260 | +else |
| 261 | + whiptail --msgbox "Failed running firstboot:\n${FAIL_REASON}" 20 60 |
| 262 | +fi |
| 263 | + |
| 264 | +reboot_pi |
0 commit comments