562 lines
17 KiB
Bash
Executable File
562 lines
17 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
# ==============================================================================
|
|
# Configuration
|
|
# ==============================================================================
|
|
|
|
VERSION="3.24.1"
|
|
BRANCH="3.24"
|
|
CONFIG_FILE="NenjimHub.conf"
|
|
|
|
IMAGE="alpine-rpi-${VERSION}-aarch64.img"
|
|
HEADLESS_OVERLAY="headless.apkovl.tar.gz"
|
|
WPA_CONFIG="wpa_supplicant.conf"
|
|
UNATTENDED_SCRIPT="unattended.sh"
|
|
ZEROTIER_INSTALLER="installZeroTier.sh"
|
|
BUILD_TIME_FILE="nenjim-build-time"
|
|
|
|
BUILDER_IMAGE="alpine:${BRANCH}"
|
|
KERNEL_MKSQUASHFS_MEMORY="128M"
|
|
|
|
BOOT_MOUNT="/mnt/usb"
|
|
|
|
TEMP_DIRECTORY=""
|
|
SOURCE_MOUNT=""
|
|
LOOP_DEVICE=""
|
|
ZEROTIER_PREBUILT_AVAILABLE=0
|
|
|
|
|
|
# ==============================================================================
|
|
# Helpers
|
|
# ==============================================================================
|
|
|
|
die()
|
|
{
|
|
echo "ERROR: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
require_uint()
|
|
{
|
|
local name="$1"
|
|
local value="${!name-}"
|
|
|
|
[[ "$value" =~ ^[0-9]+$ ]] ||
|
|
die "$name must be a non-negative integer."
|
|
}
|
|
|
|
require_swap_priority()
|
|
{
|
|
local name="$1"
|
|
local value="${!name-}"
|
|
|
|
require_uint "$name"
|
|
(( value <= 32767 )) ||
|
|
die "$name must be between 0 and 32767."
|
|
}
|
|
|
|
partition_path()
|
|
{
|
|
local number="$1"
|
|
|
|
if [[ "$DEVICE" =~ [0-9]$ ]]; then
|
|
printf '%sp%s\n' "$DEVICE" "$number"
|
|
else
|
|
printf '%s%s\n' "$DEVICE" "$number"
|
|
fi
|
|
}
|
|
|
|
wait_for_block_device()
|
|
{
|
|
local path="$1"
|
|
local attempt
|
|
|
|
for attempt in {1..50}; do
|
|
[[ -b "$path" ]] && return 0
|
|
sleep 0.2
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
cleanup()
|
|
{
|
|
set +e
|
|
|
|
if [[ -n "$SOURCE_MOUNT" ]] && mountpoint -q "$SOURCE_MOUNT" 2>/dev/null; then
|
|
umount "$SOURCE_MOUNT"
|
|
fi
|
|
|
|
if mountpoint -q "$BOOT_MOUNT" 2>/dev/null; then
|
|
umount "$BOOT_MOUNT"
|
|
fi
|
|
|
|
if [[ -n "$LOOP_DEVICE" ]]; then
|
|
losetup -d "$LOOP_DEVICE" 2>/dev/null || true
|
|
fi
|
|
|
|
if [[ -n "$TEMP_DIRECTORY" && -d "$TEMP_DIRECTORY" ]]; then
|
|
case "$TEMP_DIRECTORY" in
|
|
/tmp/nenjim-flash.*)
|
|
rm -rf -- "$TEMP_DIRECTORY"
|
|
;;
|
|
*)
|
|
echo "WARNING: Refusing to remove unexpected path: $TEMP_DIRECTORY" >&2
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
preserve_sysop_home()
|
|
{
|
|
local overlay overlay_count extract_root
|
|
local -a final_overlays=()
|
|
|
|
mapfile -t final_overlays < <(
|
|
find "$BOOT_MOUNT" \
|
|
-maxdepth 1 \
|
|
-type f \
|
|
-name '*.apkovl.tar.gz' \
|
|
! -name 'headless.apkovl.tar.gz' \
|
|
-print
|
|
)
|
|
|
|
overlay_count="${#final_overlays[@]}"
|
|
|
|
if (( overlay_count == 0 )); then
|
|
echo "No final APKoVL found; this appears to be a fresh installation."
|
|
return 0
|
|
fi
|
|
|
|
if (( overlay_count > 1 )); then
|
|
printf 'Found more than one final APKoVL:\n' >&2
|
|
printf ' %s\n' "${final_overlays[@]}" >&2
|
|
die "Keep only the active final APKoVL before updating partition 1."
|
|
fi
|
|
|
|
overlay="${final_overlays[0]}"
|
|
extract_root="$TEMP_DIRECTORY/old-overlay"
|
|
mkdir -p "$extract_root"
|
|
|
|
echo "Preserving /sysop from $(basename "$overlay")"
|
|
|
|
tar \
|
|
--extract \
|
|
--gzip \
|
|
--file "$overlay" \
|
|
--directory "$extract_root" \
|
|
--no-same-owner
|
|
|
|
if [[ ! -d "$extract_root/sysop" ]]; then
|
|
echo "The previous APKoVL does not contain /sysop; nothing to preserve."
|
|
return 0
|
|
fi
|
|
|
|
tar \
|
|
--create \
|
|
--gzip \
|
|
--file "$TEMP_DIRECTORY/sysop-preserve.tar.gz" \
|
|
--directory "$extract_root" \
|
|
sysop
|
|
}
|
|
|
|
run_kernel_builder()
|
|
{
|
|
local runtime="$1"
|
|
local kernel_work="$TEMP_DIRECTORY/kernel-work"
|
|
|
|
mkdir -p "$kernel_work"
|
|
|
|
"$runtime" run \
|
|
--rm \
|
|
--volume "$BOOT_MOUNT:/media/boot" \
|
|
--volume "$kernel_work:/work" \
|
|
--env KERNEL_MKSQUASHFS_MEMORY="$KERNEL_MKSQUASHFS_MEMORY" \
|
|
"$BUILDER_IMAGE" \
|
|
/bin/sh -euxc '
|
|
apk add --no-cache alpine-conf alpine-keys squashfs-tools xz
|
|
|
|
# update-kernel creates a temporary aarch64 APK root. The
|
|
# architecture-specific keys are required when the builder
|
|
# container itself is x86_64. The minimal Docker image can have
|
|
# the shared key files removed even though alpine-keys is marked
|
|
# as installed, so restore all package-owned key files first.
|
|
apk fix --no-cache alpine-keys
|
|
|
|
APK_KEYS_DIR=/usr/share/apk/keys
|
|
find "$APK_KEYS_DIR" -maxdepth 1 -type f -name "*.rsa.pub" -print
|
|
test -n "$(find "$APK_KEYS_DIR" -maxdepth 1 -type f -name "*.rsa.pub" -print -quit)"
|
|
apk \
|
|
--arch aarch64 \
|
|
--keys-dir "$APK_KEYS_DIR" \
|
|
update
|
|
|
|
MKSQUASHFS_OPTS="-processors 1 -mem $KERNEL_MKSQUASHFS_MEMORY" \
|
|
TMPDIR=/work \
|
|
update-kernel \
|
|
-a aarch64 \
|
|
-f rpi \
|
|
-p zfs-rpi \
|
|
--keys-dir "$APK_KEYS_DIR" \
|
|
--repositories-file /etc/apk/repositories \
|
|
-M /media/boot
|
|
|
|
test -s /media/boot/boot/vmlinuz-rpi
|
|
test -s /media/boot/boot/initramfs-rpi
|
|
test -s /media/boot/boot/modloop-rpi
|
|
|
|
unsquashfs -ll /media/boot/boot/modloop-rpi |
|
|
grep -E "/zfs[.]ko([.](gz|xz|zst))?$"
|
|
'
|
|
}
|
|
|
|
|
|
# ==============================================================================
|
|
# Initialization and validation
|
|
# ==============================================================================
|
|
|
|
cd -- "$(dirname -- "${BASH_SOURCE[0]}")"
|
|
|
|
[[ -f "$CONFIG_FILE" ]] || die "Missing shared configuration: $CONFIG_FILE"
|
|
# shellcheck source=NenjimHub.conf
|
|
source "./$CONFIG_FILE"
|
|
|
|
[[ $EUID -eq 0 ]] || die "This script must be run as root."
|
|
|
|
for name in \
|
|
BOOT_PARTITION_SIZE_MIB VIRT_ZVOL_SIZE_MIB \
|
|
SWAP_SIZE_MIB_PART SWAP_SIZE_MIB_ZFS SWAP_SIZE_PCT_ZRAM \
|
|
DISKLESS_ROOT_SIZE_MIB ZFS_ARC_MAX_MIB ZEROTIER_REQUIRED_SWAP_MIB \
|
|
ZEROTIER_MAKE_JOBS ZEROTIER_ENABLE_SSO
|
|
do
|
|
require_uint "$name"
|
|
done
|
|
|
|
for name in SWAP_PRI_ZRAM SWAP_PRI_ZFS SWAP_PRI_PART; do
|
|
require_swap_priority "$name"
|
|
done
|
|
|
|
(( BOOT_PARTITION_SIZE_MIB > 0 )) ||
|
|
die "BOOT_PARTITION_SIZE_MIB must be greater than zero."
|
|
(( VIRT_ZVOL_SIZE_MIB > 0 )) ||
|
|
die "VIRT_ZVOL_SIZE_MIB must be greater than zero."
|
|
(( DISKLESS_ROOT_SIZE_MIB > 0 )) ||
|
|
die "DISKLESS_ROOT_SIZE_MIB must be greater than zero."
|
|
(( ZFS_ARC_MAX_MIB > 0 )) ||
|
|
die "ZFS_ARC_MAX_MIB must be greater than zero."
|
|
(( ZEROTIER_REQUIRED_SWAP_MIB > 0 )) ||
|
|
die "ZEROTIER_REQUIRED_SWAP_MIB must be greater than zero."
|
|
(( ZEROTIER_MAKE_JOBS > 0 )) ||
|
|
die "ZEROTIER_MAKE_JOBS must be greater than zero."
|
|
(( ZEROTIER_ENABLE_SSO == 0 || ZEROTIER_ENABLE_SSO == 1 )) ||
|
|
die "ZEROTIER_ENABLE_SSO must be either 0 or 1."
|
|
(( SWAP_SIZE_PCT_ZRAM <= 100 )) ||
|
|
die "SWAP_SIZE_PCT_ZRAM must be between 0 and 100."
|
|
|
|
[[ "$NENJIMHUB_HOSTNAME" =~ ^[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?$ ]] ||
|
|
die "NENJIMHUB_HOSTNAME must be one 1-63 character DNS hostname label."
|
|
[[ "$CONSOLE_KEYMAP_LAYOUT" =~ ^[A-Za-z0-9][A-Za-z0-9_-]*$ ]] ||
|
|
die "Invalid CONSOLE_KEYMAP_LAYOUT: $CONSOLE_KEYMAP_LAYOUT"
|
|
[[ "$CONSOLE_KEYMAP_VARIANT" =~ ^[A-Za-z0-9][A-Za-z0-9_-]*$ ]] ||
|
|
die "Invalid CONSOLE_KEYMAP_VARIANT: $CONSOLE_KEYMAP_VARIANT"
|
|
[[ "$ZEROTIER_VERSION" =~ ^[0-9]+([.][0-9]+){2}([._-][A-Za-z0-9]+)*$ ]] ||
|
|
die "Invalid ZEROTIER_VERSION: $ZEROTIER_VERSION"
|
|
[[ "$ZEROTIER_PREBUILT_BINARY" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] ||
|
|
die "ZEROTIER_PREBUILT_BINARY must be a plain filename without directories."
|
|
|
|
DISKLESS_ROOT_SIZE="${DISKLESS_ROOT_SIZE_MIB}M"
|
|
|
|
for command in \
|
|
blkid blockdev cp date find install losetup lsblk mkfs.vfat mount mountpoint \
|
|
sed sync tar udevadm umount
|
|
do
|
|
command -v "$command" >/dev/null 2>&1 ||
|
|
die "Missing required command: $command"
|
|
done
|
|
|
|
for file in \
|
|
"$CONFIG_FILE" \
|
|
"$IMAGE" \
|
|
"$HEADLESS_OVERLAY" \
|
|
"$WPA_CONFIG" \
|
|
"$UNATTENDED_SCRIPT" \
|
|
"$ZEROTIER_INSTALLER"
|
|
do
|
|
[[ -f "$file" ]] || die "Missing input file: $file"
|
|
done
|
|
|
|
if [[ -f "$ZEROTIER_PREBUILT_BINARY" ]]; then
|
|
[[ -s "$ZEROTIER_PREBUILT_BINARY" ]] ||
|
|
die "Optional ZeroTier binary is empty: $ZEROTIER_PREBUILT_BINARY"
|
|
ZEROTIER_PREBUILT_AVAILABLE=1
|
|
echo "Using optional precompiled ZeroTier binary: $ZEROTIER_PREBUILT_BINARY"
|
|
elif [[ -e "$ZEROTIER_PREBUILT_BINARY" ]]; then
|
|
die "Optional ZeroTier binary is not a regular file: $ZEROTIER_PREBUILT_BINARY"
|
|
else
|
|
echo "No precompiled ZeroTier binary found; the Pi will compile it on first boot."
|
|
fi
|
|
|
|
if command -v podman >/dev/null 2>&1; then
|
|
CONTAINER_RUNTIME="podman"
|
|
elif command -v docker >/dev/null 2>&1; then
|
|
CONTAINER_RUNTIME="docker"
|
|
else
|
|
die "Podman or Docker is required to build the matched ARM64 kernel/modloop."
|
|
fi
|
|
|
|
[[ -b "$DEVICE" ]] || die "Not a block device: $DEVICE"
|
|
|
|
[[ "$(lsblk -dnro PTTYPE "$DEVICE" 2>/dev/null || true)" == "dos" ]] ||
|
|
die "The card does not use the Raspberry Pi-compatible MBR layout. Run the updated prepare-storage.sh first."
|
|
|
|
if mountpoint -q "$BOOT_MOUNT" 2>/dev/null; then
|
|
die "$BOOT_MOUNT is already in use. Unmount it before continuing."
|
|
fi
|
|
|
|
if lsblk -nrpo MOUNTPOINT "$DEVICE" | grep -q '[^[:space:]]'; then
|
|
die "A partition on $DEVICE is mounted. Export rpool and unmount the card first."
|
|
fi
|
|
|
|
BOOT_PARTITION="$(partition_path 1)"
|
|
SWAP_PARTITION="$(partition_path 2)"
|
|
ZFS_PARTITION="$(partition_path 3)"
|
|
|
|
[[ -b "$BOOT_PARTITION" ]] || die "Missing partition 1: run prepare-storage.sh first."
|
|
[[ -b "$ZFS_PARTITION" ]] || die "Missing ZFS partition 3: run the updated prepare-storage.sh first."
|
|
|
|
EXPECTED_BOOT_PARTITION_BYTES=$((BOOT_PARTITION_SIZE_MIB * 1024 * 1024))
|
|
ACTUAL_BOOT_PARTITION_BYTES="$(blockdev --getsize64 "$BOOT_PARTITION")"
|
|
[[ "$ACTUAL_BOOT_PARTITION_BYTES" == "$EXPECTED_BOOT_PARTITION_BYTES" ]] ||
|
|
die "$BOOT_PARTITION is $ACTUAL_BOOT_PARTITION_BYTES bytes, but $CONFIG_FILE specifies $EXPECTED_BOOT_PARTITION_BYTES bytes."
|
|
|
|
[[ "$(blkid -s TYPE -o value "$BOOT_PARTITION" 2>/dev/null || true)" == "vfat" ]] ||
|
|
die "$BOOT_PARTITION is not the expected FAT32 Alpine partition."
|
|
|
|
if (( SWAP_SIZE_MIB_PART > 0 )); then
|
|
[[ -b "$SWAP_PARTITION" ]] ||
|
|
die "Missing configured raw swap partition 2: run prepare-storage.sh first."
|
|
EXPECTED_SWAP_PARTITION_BYTES=$((SWAP_SIZE_MIB_PART * 1024 * 1024))
|
|
ACTUAL_SWAP_PARTITION_BYTES="$(blockdev --getsize64 "$SWAP_PARTITION")"
|
|
[[ "$ACTUAL_SWAP_PARTITION_BYTES" == "$EXPECTED_SWAP_PARTITION_BYTES" ]] ||
|
|
die "$SWAP_PARTITION is $ACTUAL_SWAP_PARTITION_BYTES bytes, but $CONFIG_FILE specifies $EXPECTED_SWAP_PARTITION_BYTES bytes."
|
|
[[ "$(blkid -s TYPE -o value "$SWAP_PARTITION" 2>/dev/null || true)" == "swap" ]] ||
|
|
die "$SWAP_PARTITION is not a Linux swap area."
|
|
[[ "$(blkid -s LABEL -o value "$SWAP_PARTITION" 2>/dev/null || true)" == "$SWAP_PART_LABEL" ]] ||
|
|
die "$SWAP_PARTITION does not have the expected label $SWAP_PART_LABEL."
|
|
elif [[ -b "$SWAP_PARTITION" ]]; then
|
|
die "Partition 2 exists although SWAP_SIZE_MIB_PART=0; rerun prepare-storage.sh with the selected layout."
|
|
fi
|
|
|
|
[[ "$(blkid -s TYPE -o value "$ZFS_PARTITION" 2>/dev/null || true)" == "zfs_member" ]] ||
|
|
die "$ZFS_PARTITION is not an OpenZFS pool member."
|
|
|
|
TEMP_DIRECTORY="$(mktemp -d /tmp/nenjim-flash.XXXXXX)"
|
|
SOURCE_MOUNT="$TEMP_DIRECTORY/image-boot"
|
|
mkdir -p "$SOURCE_MOUNT" "$BOOT_MOUNT"
|
|
|
|
trap cleanup EXIT
|
|
|
|
|
|
# ==============================================================================
|
|
# Confirmation: only partition 1 is replaced
|
|
# ==============================================================================
|
|
|
|
echo
|
|
echo "Partition 1 (${BOOT_PARTITION}) will be replaced with Alpine ${VERSION}."
|
|
if (( SWAP_SIZE_MIB_PART > 0 )); then
|
|
echo "Partition 2 (${SWAP_PARTITION}) is raw swap and will NOT be changed."
|
|
else
|
|
echo "Partition 2 is unused."
|
|
fi
|
|
echo "Partition 3 (${ZFS_PARTITION}) and $POOL_NAME will NOT be changed."
|
|
echo
|
|
lsblk -f "$DEVICE"
|
|
echo
|
|
read -r -p "Type NENJIMHUB to update partition 1: " answer
|
|
|
|
[[ "$answer" == "NENJIMHUB" ]] || die "Aborted."
|
|
|
|
|
|
# ==============================================================================
|
|
# Attach the Alpine image read-only
|
|
# ==============================================================================
|
|
|
|
echo
|
|
echo "=== Reading the Alpine image ==="
|
|
|
|
LOOP_DEVICE="$(losetup --find --show --partscan --read-only "$IMAGE")"
|
|
SOURCE_PARTITION="${LOOP_DEVICE}p1"
|
|
|
|
wait_for_block_device "$SOURCE_PARTITION" ||
|
|
die "The image partition did not appear: $SOURCE_PARTITION"
|
|
|
|
mount -o ro "$SOURCE_PARTITION" "$SOURCE_MOUNT"
|
|
|
|
[[ -f "$SOURCE_MOUNT/cmdline.txt" ]] ||
|
|
die "The image does not contain a Raspberry Pi cmdline.txt."
|
|
|
|
|
|
# ==============================================================================
|
|
# Preserve the rescue home from the previous Alpine overlay
|
|
# ==============================================================================
|
|
|
|
echo
|
|
echo "=== Preserving the sysop rescue home, if present ==="
|
|
|
|
mount "$BOOT_PARTITION" "$BOOT_MOUNT"
|
|
preserve_sysop_home
|
|
|
|
ACTUAL_BOOT_LABEL="$(blkid -s LABEL -o value "$BOOT_PARTITION" 2>/dev/null || true)"
|
|
BOOT_UUID="$(blkid -s UUID -o value "$BOOT_PARTITION" 2>/dev/null || true)"
|
|
|
|
[[ "$ACTUAL_BOOT_LABEL" == "$BOOT_LABEL" ]] ||
|
|
die "$BOOT_PARTITION has label '${ACTUAL_BOOT_LABEL:-none}', expected '$BOOT_LABEL'."
|
|
|
|
umount "$BOOT_MOUNT"
|
|
|
|
|
|
# ==============================================================================
|
|
# Replace only the FAT32 Alpine partition
|
|
# ==============================================================================
|
|
|
|
echo
|
|
echo "=== Replacing Alpine partition 1 ==="
|
|
|
|
BOOT_VOLUME_ID="$(printf '%s' "$BOOT_UUID" | tr -d '-')"
|
|
|
|
if [[ "$BOOT_VOLUME_ID" =~ ^[[:xdigit:]]{8}$ ]]; then
|
|
mkfs.vfat -F 32 -n "$BOOT_LABEL" -i "$BOOT_VOLUME_ID" "$BOOT_PARTITION"
|
|
else
|
|
mkfs.vfat -F 32 -n "$BOOT_LABEL" "$BOOT_PARTITION"
|
|
fi
|
|
|
|
mount "$BOOT_PARTITION" "$BOOT_MOUNT"
|
|
cp -a "$SOURCE_MOUNT/." "$BOOT_MOUNT/"
|
|
|
|
CMDLINE_FILE="$BOOT_MOUNT/cmdline.txt"
|
|
CMDLINE_WITHOUT_ROOTFLAGS="$(
|
|
sed 's/rootflags=[^[:space:]]*[[:space:]]*//g' "$CMDLINE_FILE"
|
|
)"
|
|
|
|
printf '%s rootflags=size=%s\n' \
|
|
"$CMDLINE_WITHOUT_ROOTFLAGS" \
|
|
"$DISKLESS_ROOT_SIZE" \
|
|
>"$CMDLINE_FILE"
|
|
|
|
|
|
# ==============================================================================
|
|
# Build a matched Raspberry Pi kernel/modloop with zfs-rpi on the PC
|
|
# ==============================================================================
|
|
|
|
echo
|
|
echo "=== Adding Alpine's matched Raspberry Pi OpenZFS module ==="
|
|
|
|
if ! run_kernel_builder "$CONTAINER_RUNTIME"; then
|
|
die "Kernel/modloop build failed. The SD card is not ready to boot."
|
|
fi
|
|
|
|
|
|
# ==============================================================================
|
|
# Install first-boot inputs
|
|
# ==============================================================================
|
|
|
|
echo
|
|
echo "=== Installing unattended first-boot files ==="
|
|
|
|
install -m 0644 \
|
|
"$HEADLESS_OVERLAY" \
|
|
"$BOOT_MOUNT/headless.apkovl.tar.gz"
|
|
|
|
install -m 0600 \
|
|
"$WPA_CONFIG" \
|
|
"$BOOT_MOUNT/wpa_supplicant.conf"
|
|
|
|
install -m 0755 \
|
|
"$UNATTENDED_SCRIPT" \
|
|
"$BOOT_MOUNT/unattended.sh"
|
|
|
|
install -m 0755 \
|
|
"$ZEROTIER_INSTALLER" \
|
|
"$BOOT_MOUNT/installZeroTier.sh"
|
|
|
|
install -m 0644 \
|
|
"$CONFIG_FILE" \
|
|
"$BOOT_MOUNT/NenjimHub.conf"
|
|
|
|
if (( ZEROTIER_PREBUILT_AVAILABLE == 1 )); then
|
|
install -m 0755 \
|
|
"$ZEROTIER_PREBUILT_BINARY" \
|
|
"$BOOT_MOUNT/$ZEROTIER_PREBUILT_BINARY"
|
|
fi
|
|
|
|
# Raspberry Pi Zero 2 W has no battery-backed RTC. Give first boot a recent,
|
|
# trusted starting point before it contacts HTTPS package repositories.
|
|
date -u '+%Y-%m-%d %H:%M:%S' >"$BOOT_MOUNT/$BUILD_TIME_FILE"
|
|
chmod 0644 "$BOOT_MOUNT/$BUILD_TIME_FILE"
|
|
|
|
if [[ -f "$TEMP_DIRECTORY/sysop-preserve.tar.gz" ]]; then
|
|
install -m 0600 \
|
|
"$TEMP_DIRECTORY/sysop-preserve.tar.gz" \
|
|
"$BOOT_MOUNT/sysop-preserve.tar.gz"
|
|
fi
|
|
|
|
|
|
# ==============================================================================
|
|
# Verification and finish
|
|
# ==============================================================================
|
|
|
|
echo
|
|
echo "=== Verifying and syncing partition 1 ==="
|
|
|
|
for boot_file in \
|
|
bootcode.bin \
|
|
config.txt \
|
|
fixup.dat \
|
|
start.elf \
|
|
boot/vmlinuz-rpi \
|
|
boot/initramfs-rpi \
|
|
boot/modloop-rpi \
|
|
headless.apkovl.tar.gz \
|
|
unattended.sh \
|
|
installZeroTier.sh \
|
|
NenjimHub.conf \
|
|
nenjim-build-time \
|
|
wpa_supplicant.conf
|
|
do
|
|
[[ -s "$BOOT_MOUNT/$boot_file" ]] ||
|
|
die "Missing prepared boot file: $boot_file"
|
|
done
|
|
|
|
|
|
if (( ZEROTIER_PREBUILT_AVAILABLE == 1 )); then
|
|
[[ -s "$BOOT_MOUNT/$ZEROTIER_PREBUILT_BINARY" ]] ||
|
|
die "Missing copied ZeroTier binary: $ZEROTIER_PREBUILT_BINARY"
|
|
fi
|
|
|
|
sync
|
|
umount "$SOURCE_MOUNT"
|
|
umount "$BOOT_MOUNT"
|
|
losetup -d "$LOOP_DEVICE"
|
|
LOOP_DEVICE=""
|
|
|
|
cleanup
|
|
trap - EXIT
|
|
|
|
echo
|
|
echo "============================================================================"
|
|
echo "Alpine partition 1 is ready. $POOL_NAME on partition 3 was left untouched."
|
|
if (( ZEROTIER_PREBUILT_AVAILABLE == 1 )); then
|
|
echo "ZeroTier $ZEROTIER_VERSION will use the supplied precompiled binary."
|
|
else
|
|
echo "ZeroTier $ZEROTIER_VERSION will be compiled by the Pi."
|
|
fi
|
|
echo
|
|
echo "First boot performs the unattended setup and reboots."
|
|
echo "After the second boot, use the sysop rescue/admin account:"
|
|
echo " user: sysop"
|
|
echo " password: sysop"
|
|
echo "============================================================================"
|