Refactor first-boot documentation and scripts to enhance Chromium startup behavior. Update start-chromium.sh to prefer Wayland for better touch support and adjust fullscreen handling for both Wayland and X11 environments. Clarify CM4 EEPROM configuration in documentation to prevent conflicts with reTerminal DM display backlight. Improve user guidance on touch interactions in Chromium.
This commit is contained in:
77
emmc-provisioning/network-boot-initramfs/README.md
Normal file
77
emmc-provisioning/network-boot-initramfs/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Provisioning initramfs for network boot
|
||||
|
||||
Minimal initramfs that runs **provisioning-client.sh** after bringing up the network. Used with Raspberry Pi 4 / CM4 (reTerminal) when booting via TFTP from the provisioning LXC.
|
||||
|
||||
## What it does
|
||||
|
||||
1. Mounts `/proc`, `/sys`, `/dev`, `/dev/pts`.
|
||||
2. Ensures an IP (reuses kernel DHCP or runs `udhcpc` on eth0).
|
||||
3. Runs the provisioning client with `PROVISIONING_SERVER` (default `http://10.20.50.1:5000`, overridable via kernel cmdline).
|
||||
4. The client registers with the dashboard and polls for **Deploy** or **Backup**; on action it performs the dd + curl and exits.
|
||||
|
||||
## Build
|
||||
|
||||
**On x86_64 (e.g. your laptop):** the script uses **Podman** or **Docker** with `--platform linux/arm64` to run an arm64 container and copy busybox + curl into the initramfs. Your host must be able to *run* arm64 containers (via QEMU emulation).
|
||||
|
||||
- **Fedora:** one-time setup to enable arm64 containers:
|
||||
```bash
|
||||
sudo dnf install -y qemu-user-static
|
||||
```
|
||||
Then run the build (Podman will use QEMU automatically):
|
||||
```bash
|
||||
cd emmc-provisioning/network-boot-initramfs
|
||||
./build.sh
|
||||
```
|
||||
- If you don’t install `qemu-user-static`, the script will fail with an error and print the same instructions and an alternative (build on a Pi).
|
||||
|
||||
**On a Raspberry Pi 4 or other aarch64 host:** no Docker. Install deps and run:
|
||||
|
||||
```bash
|
||||
sudo apt install -y busybox curl
|
||||
./build.sh
|
||||
```
|
||||
|
||||
Optional: pass an output path:
|
||||
|
||||
```bash
|
||||
./build.sh /path/to/initrd.img
|
||||
```
|
||||
|
||||
## Deploy to TFTP root
|
||||
|
||||
1. Copy **initrd.img** to the LXC TFTP root (e.g. `/srv/tftpboot`):
|
||||
|
||||
```bash
|
||||
scp initrd.img root@10.130.60.141:/srv/tftpboot/
|
||||
```
|
||||
|
||||
2. In the TFTP root, ensure **config.txt** (Raspberry Pi boot config) includes the initramfs line. If you use the stock `config.txt` from the RPi firmware repo, add:
|
||||
|
||||
```
|
||||
initramfs initrd.img followkernel
|
||||
```
|
||||
|
||||
So the firmware loads the kernel and then the initrd that “follows” it. The Pi will boot the kernel and run `/init` from the initrd.
|
||||
|
||||
3. If your DHCP already points the Pi to this TFTP server and `start4cd.elf`, the Pi will load kernel + initrd from the same root. No NFS or extra server needed.
|
||||
|
||||
## Kernel cmdline (optional)
|
||||
|
||||
To override the provisioning server URL (e.g. if the dashboard is on another IP), add to **cmdline.txt** in the TFTP root (or append to the kernel command line):
|
||||
|
||||
```
|
||||
provisioning_server=http://10.20.50.1:5000
|
||||
```
|
||||
|
||||
The init script reads `provisioning_server=` from `/proc/cmdline` and exports `PROVISIONING_SERVER` for the client.
|
||||
|
||||
## Flow summary
|
||||
|
||||
1. Pi does DHCP → gets IP and TFTP server (e.g. 10.20.50.1).
|
||||
2. Pi loads via TFTP: start4cd.elf, fixup4cd.dat, config.txt, cmdline.txt, kernel8.img, **initrd.img**.
|
||||
3. Kernel boots with initrd as root; runs `/init`.
|
||||
4. Init mounts minimal fs, ensures network, runs `/provisioning-client.sh`.
|
||||
5. Client registers and polls; you choose Deploy or Backup in the dashboard; client runs dd + curl and exits.
|
||||
6. After deploy, power cycle the Pi so it boots from eMMC.
|
||||
|
||||
See **docs/NETWORK-BOOT-DEPLOYMENT-FLOW.md** for the full deployment flow.
|
||||
163
emmc-provisioning/network-boot-initramfs/build.sh
Executable file
163
emmc-provisioning/network-boot-initramfs/build.sh
Executable file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build provisioning initramfs for Raspberry Pi 4 / CM4 (aarch64).
|
||||
# Produces initrd.img (gzip cpio) for TFTP boot (config.txt: initramfs initrd.img followkernel).
|
||||
#
|
||||
# On x86_64: tries Docker/Podman with --platform linux/arm64; if that fails (no
|
||||
# arm64 emulation), downloads prebuilt static aarch64 busybox and curl. No sudo needed.
|
||||
# On aarch64 (e.g. Raspberry Pi): uses local busybox and curl if installed.
|
||||
|
||||
set -e
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
OUTPUT="${1:-$SCRIPT_DIR/initrd.img}"
|
||||
BUILD_DIR=$(mktemp -d)
|
||||
trap "rm -rf $BUILD_DIR" EXIT
|
||||
|
||||
echo "Build dir: $BUILD_DIR"
|
||||
|
||||
# Layout: /init, /provisioning-client.sh, /bin/busybox, /bin/sh, /usr/bin/curl, /lib/*.so
|
||||
mkdir -p "$BUILD_DIR"/{bin,usr/bin,proc,sys,dev,dev/pts,lib}
|
||||
cp "$SCRIPT_DIR/init" "$BUILD_DIR/init"
|
||||
cp "$SCRIPT_DIR/provisioning-client.sh" "$BUILD_DIR/provisioning-client.sh"
|
||||
chmod +x "$BUILD_DIR/init" "$BUILD_DIR/provisioning-client.sh"
|
||||
|
||||
ARCH=$(uname -m 2>/dev/null)
|
||||
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ] || [ "$ARCH" = "armv8l" ]; then
|
||||
if ! command -v busybox >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1; then
|
||||
echo "On aarch64: install busybox and curl (apt install busybox curl) then re-run."
|
||||
exit 1
|
||||
fi
|
||||
echo "Copying busybox and curl from host (aarch64)..."
|
||||
cp "$(command -v busybox)" "$BUILD_DIR/bin/busybox"
|
||||
cp "$(command -v curl)" "$BUILD_DIR/usr/bin/curl"
|
||||
chmod +x "$BUILD_DIR/bin/busybox" "$BUILD_DIR/usr/bin/curl"
|
||||
for f in $(ldd "$(command -v curl)" 2>/dev/null | awk '/=>/{print $3}'); do
|
||||
[ -f "$f" ] && cp "$f" "$BUILD_DIR/lib/"
|
||||
done
|
||||
cp /lib/ld-linux-aarch64.so.1 "$BUILD_DIR/lib/" 2>/dev/null || true
|
||||
else
|
||||
# x86_64: try container first; on "Exec format error" (no arm64 emulation) use static downloads
|
||||
CONTAINER_RUNTIME=""
|
||||
for cmd in docker podman; do
|
||||
if command -v "$cmd" >/dev/null 2>&1; then
|
||||
CONTAINER_RUNTIME="$cmd"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
CONTAINER_OK=0
|
||||
if [ -n "$CONTAINER_RUNTIME" ]; then
|
||||
echo "Trying $CONTAINER_RUNTIME (linux/arm64)..."
|
||||
CNT_NAME="cm4-initramfs-build-$$"
|
||||
# Use a container-internal dir (no bind mount); copy out with podman cp (works with rootless)
|
||||
$CONTAINER_RUNTIME run --name "$CNT_NAME" --platform linux/arm64 debian:bookworm-slim bash -c '
|
||||
apt-get update -qq && apt-get install -y -qq busybox curl
|
||||
mkdir -p /out/bin /out/usr/bin /out/lib
|
||||
cp /bin/busybox /out/bin/busybox
|
||||
cp /usr/bin/curl /out/usr/bin/curl
|
||||
chmod +x /out/bin/busybox /out/usr/bin/curl
|
||||
ldd /usr/bin/curl 2>/dev/null | awk "/=>/{print \$3}" | while read f; do [ -n "$f" ] && [ -f "$f" ] && cp "$f" /out/lib/; done
|
||||
cp /lib/ld-linux-aarch64.so.1 /out/lib/ 2>/dev/null || true
|
||||
' 2>/dev/null || true
|
||||
# Always copy from container (avoids rootless bind-mount issues)
|
||||
$CONTAINER_RUNTIME cp "$CNT_NAME:/out/bin/busybox" "$BUILD_DIR/bin/" 2>/dev/null && \
|
||||
$CONTAINER_RUNTIME cp "$CNT_NAME:/out/usr/bin/curl" "$BUILD_DIR/usr/bin/" 2>/dev/null && \
|
||||
$CONTAINER_RUNTIME cp "$CNT_NAME:/out/lib/." "$BUILD_DIR/lib/" 2>/dev/null || true
|
||||
$CONTAINER_RUNTIME rm -f "$CNT_NAME" 2>/dev/null
|
||||
if [ -f "$BUILD_DIR/bin/busybox" ] && [ -f "$BUILD_DIR/usr/bin/curl" ]; then
|
||||
CONTAINER_OK=1
|
||||
echo "Container build succeeded."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CONTAINER_OK" -ne 1 ]; then
|
||||
echo "Using prebuilt static aarch64 binaries (no container/emulation needed)..."
|
||||
DOWNLOAD_DIR=$(mktemp -d)
|
||||
trap "rm -rf $BUILD_DIR $DOWNLOAD_DIR" EXIT
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
GET="curl -sL"
|
||||
GET_O="curl -sL -o"
|
||||
else
|
||||
GET="wget -q -O -"
|
||||
GET_O="wget -q -O"
|
||||
fi
|
||||
# Static busybox aarch64: try busybox.net, then Alpine busybox-static package
|
||||
BB_OK=0
|
||||
$GET_O "$DOWNLOAD_DIR/busybox" "https://busybox.net/downloads/binaries/1.35.0-defconfig-multiarch-musl/busybox-armv8l" 2>/dev/null || true
|
||||
if [ -f "$DOWNLOAD_DIR/busybox" ] && [ -s "$DOWNLOAD_DIR/busybox" ]; then
|
||||
BB_OK=1
|
||||
fi
|
||||
if [ "$BB_OK" -ne 1 ]; then
|
||||
echo "Trying Alpine busybox-static aarch64..."
|
||||
$GET_O "$DOWNLOAD_DIR/bb.apk" "https://dl-cdn.alpinelinux.org/alpine/v3.19/main/aarch64/busybox-static-1.36.1-r11.apk" 2>/dev/null || true
|
||||
if [ -f "$DOWNLOAD_DIR/bb.apk" ] && [ -s "$DOWNLOAD_DIR/bb.apk" ]; then
|
||||
case "$(head -c 4 "$DOWNLOAD_DIR/bb.apk" | od -An -tx1 2>/dev/null | tr -d ' ')" in
|
||||
3c21444f|3c68746d) ;; # HTML response, skip extract
|
||||
*)
|
||||
(cd "$DOWNLOAD_DIR" && (tar xf bb.apk 2>/dev/null || tar xzf bb.apk 2>/dev/null) && [ -f data.tar.gz ] && tar xzf data.tar.gz 2>/dev/null)
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if [ -f "$DOWNLOAD_DIR/bin/busybox" ] && [ -s "$DOWNLOAD_DIR/bin/busybox" ]; then
|
||||
cp "$DOWNLOAD_DIR/bin/busybox" "$DOWNLOAD_DIR/busybox"
|
||||
BB_OK=1
|
||||
fi
|
||||
fi
|
||||
if [ "$BB_OK" -ne 1 ]; then
|
||||
echo "Failed to download busybox (x86 host cannot run arm64 container without emulation)."
|
||||
echo ""
|
||||
echo "Option A - Enable arm64 containers (one-time, needs sudo):"
|
||||
echo " Fedora: sudo dnf install -y qemu-user-static"
|
||||
echo " Then re-run this script (Podman will use QEMU to run the arm64 build)."
|
||||
echo ""
|
||||
echo "Option B - Build on a Raspberry Pi 4 (aarch64):"
|
||||
echo " scp -r $(dirname "$SCRIPT_DIR") pi@<pi-ip>:~/ && ssh pi@<pi-ip> 'cd ~/emmc-provisioning/network-boot-initramfs && sudo apt install -y busybox curl && ./build.sh'"
|
||||
echo " Then scp pi@<pi-ip>:~/emmc-provisioning/network-boot-initramfs/initrd.img ."
|
||||
exit 1
|
||||
fi
|
||||
chmod +x "$DOWNLOAD_DIR/busybox"
|
||||
cp "$DOWNLOAD_DIR/busybox" "$BUILD_DIR/bin/busybox"
|
||||
# Static curl aarch64 glibc (Raspberry Pi OS uses glibc)
|
||||
$GET "https://github.com/stunnel/static-curl/releases/download/8.18.0/curl-linux-aarch64-glibc-8.18.0.tar.xz" -o "$DOWNLOAD_DIR/curl.tar.xz" || true
|
||||
if [ ! -f "$DOWNLOAD_DIR/curl.tar.xz" ] || [ ! -s "$DOWNLOAD_DIR/curl.tar.xz" ]; then
|
||||
echo "Failed to download static curl."
|
||||
exit 1
|
||||
fi
|
||||
(cd "$DOWNLOAD_DIR" && tar xf curl.tar.xz)
|
||||
CURL_BIN=$(find "$DOWNLOAD_DIR" -maxdepth 3 -name "curl" -type f 2>/dev/null | head -1)
|
||||
if [ -n "$CURL_BIN" ] && [ -x "$CURL_BIN" ]; then
|
||||
cp "$CURL_BIN" "$BUILD_DIR/usr/bin/curl"
|
||||
chmod +x "$BUILD_DIR/usr/bin/curl"
|
||||
else
|
||||
echo "Could not find curl binary in tarball."
|
||||
exit 1
|
||||
fi
|
||||
rm -rf "$DOWNLOAD_DIR"
|
||||
trap "rm -rf $BUILD_DIR" EXIT
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify we have busybox (container or fallback must have left it)
|
||||
if [ ! -f "$BUILD_DIR/bin/busybox" ] || [ ! -s "$BUILD_DIR/bin/busybox" ]; then
|
||||
echo "Error: busybox not found in $BUILD_DIR/bin. If the container ran, check Podman volume mount."
|
||||
exit 1
|
||||
fi
|
||||
chmod +x "$BUILD_DIR/bin/busybox" 2>/dev/null || true
|
||||
|
||||
# Busybox applets we need (sh, mount, udhcpc, etc.)
|
||||
cd "$BUILD_DIR/bin"
|
||||
./busybox --list 2>/dev/null | while read applet; do
|
||||
case "$applet" in
|
||||
sh|ash|mount|umount|mkdir|cat|ip|udhcpc|sleep|echo|grep|cut|awk|hostname|dd) ln -sf busybox "$applet"; ;;
|
||||
esac
|
||||
done
|
||||
[ -e sh ] || ln -sf busybox sh
|
||||
|
||||
# Build cpio (gzip)
|
||||
echo "Building cpio..."
|
||||
( cd "$BUILD_DIR"; find . -print0 | cpio -o -H newc -0 2>/dev/null ) | gzip -9 > "$OUTPUT"
|
||||
echo "Written: $OUTPUT ($(stat -c%s "$OUTPUT" 2>/dev/null || stat -f%z "$OUTPUT" 2>/dev/null) bytes)"
|
||||
echo ""
|
||||
echo "Next: copy initrd.img to TFTP root (e.g. /srv/tftpboot on LXC) and in config.txt add:"
|
||||
echo " initramfs initrd.img followkernel"
|
||||
echo "Then ensure the kernel line loads the initrd (followkernel does that)."
|
||||
echo "Default provisioning server: http://10.20.50.1:5000 (override with kernel cmdline: provisioning_server=http://...)"
|
||||
@@ -0,0 +1,9 @@
|
||||
# Add this line to config.txt in the TFTP root (/srv/tftpboot) so the Pi loads
|
||||
# the provisioning initramfs after the kernel.
|
||||
#
|
||||
# initramfs initrd.img followkernel
|
||||
#
|
||||
# Ensure initrd.img is in the same directory (TFTP root). The kernel will use
|
||||
# it as the initial root and run /init, which starts the provisioning client.
|
||||
|
||||
initramfs initrd.img followkernel
|
||||
35
emmc-provisioning/network-boot-initramfs/init
Normal file
35
emmc-provisioning/network-boot-initramfs/init
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
# Init for provisioning initramfs: bring up minimal env and run provisioning-client.sh.
|
||||
# PROVISIONING_SERVER can be set via kernel cmdline: provisioning_server=http://10.20.50.1:5000
|
||||
|
||||
set -e
|
||||
export PATH=/bin:/usr/bin
|
||||
export LD_LIBRARY_PATH=/lib
|
||||
|
||||
echo "=== CM4 provisioning initramfs ==="
|
||||
|
||||
# Minimal filesystem
|
||||
mount -t proc none /proc
|
||||
mount -t sysfs none /sys
|
||||
mount -t devtmpfs none /dev
|
||||
mkdir -p /dev/pts
|
||||
mount -t devpts none /dev/pts
|
||||
|
||||
# Kernel might have brought up eth0 via ip=dhcp; ensure we have an IP
|
||||
if ! ip addr show | grep -q 'inet .* scope global'; then
|
||||
echo "Getting DHCP lease..."
|
||||
udhcpc -f -q -i eth0 -n 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Allow kernel cmdline to override: provisioning_server=http://10.20.50.1:5000
|
||||
for arg in $(cat /proc/cmdline); do
|
||||
case "$arg" in
|
||||
provisioning_server=*) export PROVISIONING_SERVER="${arg#*=}"; ;;
|
||||
esac
|
||||
done
|
||||
PROVISIONING_SERVER="${PROVISIONING_SERVER:-http://10.20.50.1:5000}"
|
||||
export PROVISIONING_SERVER
|
||||
|
||||
echo "Provisioning server: $PROVISIONING_SERVER"
|
||||
echo "Running provisioning client..."
|
||||
exec /bin/sh /provisioning-client.sh
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/bin/sh
|
||||
# POSIX sh version for initramfs. Registers with dashboard and runs Deploy or Backup.
|
||||
# PROVISIONING_SERVER must be set (e.g. http://10.20.50.1:5000).
|
||||
|
||||
BASE_URL="${PROVISIONING_SERVER:-http://10.20.50.1:5000}"
|
||||
BASE_URL="${BASE_URL%/}"
|
||||
EMMC_DEV="${EMMC_DEV:-/dev/mmcblk0}"
|
||||
|
||||
get_mac() {
|
||||
cat /sys/class/net/eth0/address 2>/dev/null || \
|
||||
cat /sys/class/net/enp0s1f0/address 2>/dev/null || \
|
||||
echo "unknown"
|
||||
}
|
||||
|
||||
get_ip() {
|
||||
hostname -I 2>/dev/null | awk '{print $1}' || echo ""
|
||||
}
|
||||
|
||||
MAC=$(get_mac)
|
||||
IP=$(get_ip)
|
||||
|
||||
echo "Registering with $BASE_URL (MAC=$MAC IP=$IP)..."
|
||||
curl -s -X POST -H "Content-Type: application/json" \
|
||||
-d "{\"mac\":\"$MAC\",\"ip\":\"$IP\"}" "$BASE_URL/api/register-device" || true
|
||||
|
||||
echo "Polling for action (Backup or Deploy)..."
|
||||
while true; do
|
||||
resp=$(curl -s "$BASE_URL/api/device-action-poll?mac=$MAC" 2>/dev/null || echo '{"action":"wait"}')
|
||||
action=$(echo "$resp" | grep -o '"action":"[^"]*"' | cut -d'"' -f4)
|
||||
url=$(echo "$resp" | grep -o '"url":"[^"]*"' | cut -d'"' -f4)
|
||||
upload_url=$(echo "$resp" | grep -o '"upload_url":"[^"]*"' | cut -d'"' -f4)
|
||||
|
||||
if [ "$action" = "deploy" ] && [ -n "$url" ]; then
|
||||
echo "Deploy: downloading image and writing to $EMMC_DEV..."
|
||||
if [ ! -b "$EMMC_DEV" ]; then
|
||||
echo "Error: $EMMC_DEV not found"
|
||||
sleep 10
|
||||
continue
|
||||
fi
|
||||
curl -sL "$url" | dd of="$EMMC_DEV" bs=4M status=progress conv=fsync
|
||||
echo "Deploy done. Reboot to run from eMMC."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$action" = "backup" ] && [ -n "$upload_url" ]; then
|
||||
echo "Backup: reading $EMMC_DEV and uploading..."
|
||||
if [ ! -b "$EMMC_DEV" ]; then
|
||||
echo "Error: $EMMC_DEV not found"
|
||||
sleep 10
|
||||
continue
|
||||
fi
|
||||
dd if="$EMMC_DEV" bs=4M status=progress 2>/dev/null | curl -s -X POST -T - "$upload_url"
|
||||
echo "Backup done."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sleep 5
|
||||
done
|
||||
Reference in New Issue
Block a user