Files
matrix-screen-controller/核桃派软件源代码/scripts/image_firstboot.sh
T

171 lines
7.4 KiB
Bash

#!/bin/bash
set -Eeuo pipefail
STATUS=/boot/MSCSTAT.TXT
PACKAGE_LOG=/boot/MSCPKG.TXT
PACKAGE_INVENTORY=/boot/MSCPKGS.TSV
DEPLOY_LOG=/boot/MSCDEPLOY.TXT
CONFIG=/boot/MSCCFG.BIN
BUNDLE=/opt/matrix-image-bootstrap/app/MSCBOOT.TGZ
META=/boot/MSCMETA.JSN
WORK=/run/matrix-image-provision
KERNEL_ROOT=/opt/matrix-image-bootstrap/axp313a
KERNEL_ARCHIVE=$KERNEL_ROOT/axp313a-kernel-6.1.31-matrix-axp313a1.tar.gz
KERNEL_METADATA=$KERNEL_ROOT/METADATA.json
KERNEL_WORK=/var/tmp/matrix-axp313a-image-install
status() {
printf '%s\n' "$1" > "$STATUS"
sync "$STATUS" || true
}
failed() {
code=$?
trap - ERR
rm -rf -- /var/tmp/matrix-axp313a-image-install
status "FAILED: stage=${STAGE:-starting}; code=$code; line=${BASH_LINENO[0]:-unknown}. Power off, remove the TF card, and inspect this file on Windows."
exit "$code"
}
trap failed ERR
if [ "$(id -u)" -ne 0 ]; then
status "FAILED: the one-time provisioner did not run as root."
exit 1
fi
STAGE=validate
status "RUNNING: validating the offline image payload."
rm -rf -- "$WORK"
install -d -m 0700 "$WORK"
python3 - "$META" "$BUNDLE" "$KERNEL_ARCHIVE" "$KERNEL_METADATA" <<'PY'
import hashlib
import json
from pathlib import Path
import sys
meta = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
bundle = Path(sys.argv[2])
kernel_artifact = Path(sys.argv[3])
kernel = json.loads(Path(sys.argv[4]).read_text(encoding="utf-8"))
assert meta == {
"format_version": 3,
"product": "matrix-screen-controller-walnutpi",
"software_version": meta["software_version"],
"bundle_path": "/opt/matrix-image-bootstrap/app/MSCBOOT.TGZ",
"bundle_bytes": meta["bundle_bytes"],
"bundle_sha256": meta["bundle_sha256"],
"kernel_release": "6.1.31-matrix-axp313a1",
"kernel_artifact": "axp313a-kernel-6.1.31-matrix-axp313a1.tar.gz",
"kernel_artifact_bytes": meta["kernel_artifact_bytes"],
"kernel_artifact_sha256": meta["kernel_artifact_sha256"],
"kernel_unpacked_file_bytes": meta["kernel_unpacked_file_bytes"],
"boot_required_bytes": meta["boot_required_bytes"],
"boot_safety_bytes": 32 * 1024 * 1024,
}
assert isinstance(meta["boot_required_bytes"], int) and meta["boot_required_bytes"] > 0
assert bundle.stat().st_size == meta["bundle_bytes"]
digest = hashlib.sha256()
with bundle.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
assert digest.hexdigest() == meta["bundle_sha256"]
assert kernel["schema_version"] == 1
assert kernel["architecture"] == "aarch64"
assert kernel["kernel_release"] == meta["kernel_release"]
assert kernel["artifact"] == meta["kernel_artifact"]
assert kernel["artifact_bytes"] == meta["kernel_artifact_bytes"] == kernel_artifact.stat().st_size
assert kernel["artifact_sha256"] == meta["kernel_artifact_sha256"]
assert kernel["unpacked_file_bytes"] == meta["kernel_unpacked_file_bytes"]
digest = hashlib.sha256()
with kernel_artifact.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
assert digest.hexdigest() == meta["kernel_artifact_sha256"]
PY
tar -xzf "$BUNDLE" -C "$WORK"
cd "$WORK/project/核桃派软件源代码"
PYTHONPATH=. python3 scripts/kernel_artifact.py --kernel "$KERNEL_ROOT" >/dev/null
cd "$WORK/project/离线依赖/其他依赖/aarch64-py311"
sha256sum -c SHA256SUMS
cd "$WORK/project/离线依赖/其他依赖/debian12-aarch64"
sha256sum -c SHA256SUMS
STAGE=packages
status "RUNNING: installing offline Debian packages."
export DEBIAN_FRONTEND=noninteractive
dpkg-query -W -f='${db:Status-Abbrev}\t${binary:Package}\t${Version}\n' >"$PACKAGE_INVENTORY"
if ! dpkg -i "$WORK"/project/离线依赖/其他依赖/debian12-aarch64/*.deb >"$PACKAGE_LOG" 2>&1; then
status "FAILED: stage=packages; offline Debian package installation failed. Inspect MSCPKG.TXT on Windows; it contains package diagnostics but no configured credentials."
exit 1
fi
rm -f -- "$PACKAGE_LOG" "$PACKAGE_INVENTORY"
STAGE=network
status "RUNNING: applying the requested network configuration."
cd "$WORK/project/核桃派软件源代码"
PYTHONPATH=. python3 -m scripts.provision_device network --config "$CONFIG"
systemctl restart NetworkManager.service
STAGE=deploy
status "RUNNING: installing and testing matrix-screen-controller."
{
if [ -e /opt/matrix-screen-controller ]; then
printf '%s\n' 'A previous deployment attempt left /opt/matrix-screen-controller in place.'
systemctl --no-pager --full status matrix-screen-controller.service || true
journalctl --no-pager -u matrix-screen-controller.service -n 120 || true
fi
} >"$DEPLOY_LOG" 2>&1
FRPC_RUN_USER=$(PYTHONPATH=. python3 -c 'from pathlib import Path; from scripts.image_config import read_config_path; import sys; print(read_config_path(Path(sys.argv[1]))["account"]["username"])' "$CONFIG")
if ! FRPC_RUN_USER="$FRPC_RUN_USER" DEFER_SERVICE_START=1 WHEELHOUSE="$WORK/project/离线依赖/其他依赖/aarch64-py311" FRPC_BUNDLE="$WORK/project/离线依赖/其他依赖/frp/0.71.0/linux-arm64" ./scripts/deploy_walnutpi.sh >>"$DEPLOY_LOG" 2>&1; then
status "FAILED: stage=deploy; controller deployment failed. Inspect MSCDEPLOY.TXT on Windows; it contains deployment diagnostics but no configured credentials."
exit 1
fi
rm -f -- "$DEPLOY_LOG"
STAGE=account
status "RUNNING: creating the configured account and unique device identity."
PYTHONPATH=. python3 -m scripts.provision_device account --config "$CONFIG"
FRPC_RUN_USER="$FRPC_RUN_USER" DEFER_SERVICE_START=1 FRPC_BUNDLE="$WORK/project/离线依赖/其他依赖/frp/0.71.0/linux-arm64" /bin/sh ./scripts/install_frpc_system.sh
systemctl disable boot_script.service >/dev/null 2>&1 || true
PYTHONPATH=. python3 -m scripts.provision_device identity --config "$CONFIG"
STAGE=ssh
status "RUNNING: enabling password-authenticated SSH for the configured administrator."
PYTHONPATH=. python3 -m scripts.provision_device ssh --config "$CONFIG"
STAGE=kernel
status "RUNNING: installing the verified offline AXP313A kernel."
kernel_unpacked_bytes=$(python3 -c 'import json; print(json.load(open("/opt/matrix-image-bootstrap/axp313a/METADATA.json", encoding="utf-8"))["unpacked_file_bytes"])')
available_bytes=$(df -Pk /var/tmp | awk 'NR == 2 { printf "%.0f\n", $4 * 1024 }')
required_bytes=$((kernel_unpacked_bytes * 2 + 268435456))
if [ "$available_bytes" -lt "$required_bytes" ]; then
printf '%s\n' 'insufficient root filesystem space to install the offline kernel safely' >&2
exit 1
fi
rm -rf -- "$KERNEL_WORK"
install -d -m 0700 "$KERNEL_WORK"
tar -xzf "$KERNEL_ARCHIVE" -C "$KERNEL_WORK"
cd "$WORK/project/核桃派软件源代码"
./scripts/install_axp313a_kernel.sh --artifact "$KERNEL_WORK"
rm -rf -- "$KERNEL_WORK"
rm -rf -- /opt/matrix-image-bootstrap
STAGE=finish
install -d -m 0711 /var/lib/matrix-screen-controller
printf '%s\n' "$(cat VERSION)" > /var/lib/matrix-screen-controller/.factory-image-version
if command -v shred >/dev/null 2>&1; then
shred -n 1 -z "$CONFIG" || true
fi
rm -f -- "$CONFIG" "$META" /boot/MSCINIT
systemctl disable matrix-image-firstboot.service >/dev/null 2>&1 || true
rm -f -- /etc/systemd/system/matrix-image-firstboot.service
rm -rf -- "$WORK"
sync
if nmcli -t -f NAME connection show --active 2>/dev/null | grep -Fxq 'matrix-screen' \
&& ip route show default | grep -q .; then
status "SUCCESS: provisioning completed; the device is rebooting into the installed controller."
else
status "SUCCESS: provisioning completed while Wi-Fi is not connected; the installed controller will keep trying after reboot."
fi
systemctl reboot