#!/bin/bash # First-boot: packages, Chromium kiosk, rpd-labwc + touch, reTerminal DM drivers. # Run by cloud-init (user-data-remote-gnss.example). Run as root. set -e export DEBIAN_FRONTEND=noninteractive # --- Constants --- # All first-boot assets live in portal-files/first-boot/ on the file server. FILE_SERVER="http://10.130.60.141:5000/files/first-boot" HOSTNAME="gnss.guard" PI_USER="pi" PI_HOME="/home/$PI_USER" AUTOSTART="$PI_HOME/.config/autostart" LOGFILE="/var/log/first-boot.log" PLYMOUTH_DIR="/usr/share/plymouth/themes/custom" WALLPAPER_PATH="/usr/share/rpd-wallpaper/splash.png" # --- Logging --- log() { echo "[$(date -Iseconds)] $*"; } exec > >(tee -a "$LOGFILE") 2>&1 log "=== first-boot.sh started ===" log "FILE_SERVER=$FILE_SERVER PI_USER=$PI_USER LOGFILE=$LOGFILE" # --- 0. Hostname and /etc/hosts (avoids "unable to resolve host" with sudo) --- log "--- Hostname: $HOSTNAME ---" echo "$HOSTNAME" > /etc/hostname hostnamectl set-hostname "$HOSTNAME" 2>/dev/null || true # Ensure hostname resolves so sudo and other tools don't warn if ! grep -q "127.0.1.1[[:space:]]*$HOSTNAME" /etc/hosts 2>/dev/null; then sed -i "/127.0.1.1[[:space:]].*$/d" /etc/hosts echo "127.0.1.1 $HOSTNAME" >> /etc/hosts fi log "Hostname set to $HOSTNAME; /etc/hosts updated" # --- Helpers --- # Download script + .desktop from FILE_SERVER and install as one-shot autostart (runs once at pi's first login, then deletes itself). install_oneshot() { local name="$1" log "--- Installing one-shot: $name ---" if curl -fsSL "${FILE_SERVER}/${name}.sh" -o "$PI_HOME/${name}.sh"; then log "Downloaded ${name}.sh to $PI_HOME/${name}.sh" else log "WARNING: Could not download ${name}.sh"; return 1 fi if curl -fsSL "${FILE_SERVER}/${name}.desktop" -o "$AUTOSTART/${name}.desktop"; then log "Downloaded ${name}.desktop to $AUTOSTART/${name}.desktop" else log "WARNING: Could not download ${name}.desktop"; return 1 fi chmod 755 "$PI_HOME/${name}.sh" && chmod 644 "$AUTOSTART/${name}.desktop" chown "$PI_USER:$PI_USER" "$PI_HOME/${name}.sh" "$AUTOSTART/${name}.desktop" log "One-shot $name installed (will run at first login and then remove itself)" } # --- 1. Packages --- log "--- Installing packages ---" log "Running apt-get update ..." apt-get update -qq log "Installing: git chromium wmctrl openssh-server swaybg wlr-randr maliit-keyboard xinput-calibrator" apt-get install -y -qq git chromium wmctrl openssh-server \ swaybg wlr-randr maliit-keyboard xinput-calibrator log "Packages installed successfully" # --- 2. Dirs and kiosk files from file server --- log "--- Kiosk files ---" log "Creating $AUTOSTART" mkdir -p "$AUTOSTART" log "Downloading start-chromium.sh from ${FILE_SERVER}/start-chromium.sh" curl -fsSL "${FILE_SERVER}/start-chromium.sh" -o "$PI_HOME/start-chromium.sh" log "Downloading chromium-kiosk.desktop from ${FILE_SERVER}/chromium-kiosk.desktop" curl -fsSL "${FILE_SERVER}/chromium-kiosk.desktop" -o "$AUTOSTART/chromium-kiosk.desktop" chmod 755 "$PI_HOME/start-chromium.sh" && chmod 644 "$AUTOSTART/chromium-kiosk.desktop" chown -R "$PI_USER:$PI_USER" "$PI_HOME/start-chromium.sh" "$AUTOSTART/chromium-kiosk.desktop" log "Kiosk files installed under $PI_HOME and $AUTOSTART" # --- 3. Boot splash and wallpaper (splash.png + Plymouth theme from file server) --- log "--- Boot splash and wallpaper ---" log "Creating $PLYMOUTH_DIR and /usr/share/rpd-wallpaper" mkdir -p "$PLYMOUTH_DIR" /usr/share/rpd-wallpaper if curl -fsSL "${FILE_SERVER}/splash.png" -o "$PLYMOUTH_DIR/splash.png"; then log "Downloaded splash.png; copying to $WALLPAPER_PATH" cp "$PLYMOUTH_DIR/splash.png" "$WALLPAPER_PATH" chmod 644 "$PLYMOUTH_DIR/splash.png" "$WALLPAPER_PATH" if curl -fsSL "${FILE_SERVER}/custom.plymouth" -o "$PLYMOUTH_DIR/custom.plymouth" \ && curl -fsSL "${FILE_SERVER}/custom.script" -o "$PLYMOUTH_DIR/custom.script"; then chmod 644 "$PLYMOUTH_DIR/custom.plymouth" "$PLYMOUTH_DIR/custom.script" log "Plymouth theme files (custom.plymouth, custom.script) installed" else log "WARNING: Could not download custom.plymouth/custom.script; boot splash theme may be incomplete" fi grep -q '^Theme=custom' /etc/plymouth/plymouthd.conf 2>/dev/null || printf '%s\n' '[Daemon]' 'Theme=custom' >> /etc/plymouth/plymouthd.conf log "Running update-initramfs (may take a moment) ..." update-initramfs -u -k all 2>/dev/null || true mkdir -p /etc/lightdm/lightdm.conf.d curl -fsSL "${FILE_SERVER}/99-wallpaper.conf" -o /etc/lightdm/lightdm.conf.d/99-wallpaper.conf 2>/dev/null || log "WARNING: Could not download 99-wallpaper.conf" log "Splash and wallpaper set from file server" else log "WARNING: Could not download splash.png" fi # --- 4. LightDM: rpd-labwc session + configs from file server --- log "--- LightDM session (rpd-labwc) ---" mkdir -p /etc/lightdm/lightdm.conf.d if curl -fsSL "${FILE_SERVER}/99-default-session.conf" -o /etc/lightdm/lightdm.conf.d/99-default-session.conf 2>/dev/null; then log "99-default-session.conf installed" else log "WARNING: Could not download 99-default-session.conf" fi # Raspberry Pi OS may apply main lightdm.conf after .conf.d; force session in main config too if [[ -f /etc/lightdm/lightdm.conf ]]; then sed -i 's/^user-session=.*/user-session=rpd-labwc/' /etc/lightdm/lightdm.conf sed -i 's/^autologin-session=.*/autologin-session=rpd-labwc/' /etc/lightdm/lightdm.conf log "Patched /etc/lightdm/lightdm.conf to use rpd-labwc" fi # --- 5. Maliit on-screen keyboard (from file server) --- log "--- Maliit ---" mkdir -p "$AUTOSTART" "$PI_HOME/.config" curl -fsSL "${FILE_SERVER}/maliit-keyboard.desktop" -o "$AUTOSTART/maliit-keyboard.desktop" 2>/dev/null && log "maliit-keyboard.desktop installed" || log "WARNING: Could not download maliit-keyboard.desktop" chown -R "$PI_USER:$PI_USER" "$PI_HOME/.config" # --- 6. reTerminal DM drivers (Seeed) --- log "--- reTerminal DM drivers ---" REPO_DIR="/tmp/seeed-linux-dtoverlays" log "Cloning seeed-linux-dtoverlays to $REPO_DIR ..." git clone --depth 1 https://github.com/Seeed-Studio/seeed-linux-dtoverlays "$REPO_DIR" # Script must run from repo root (it uses pwd for MOD_PATH). On bookworm+ --compat-kernel is not supported. log "Running reTerminal.sh --device reTerminal-DM from $REPO_DIR ..." if ( cd "$REPO_DIR" && "$REPO_DIR/scripts/reTerminal.sh" --device reTerminal-DM ); then log "reTerminal DM drivers installed (reboot will apply)" else log "WARNING: reTerminal.sh failed (see log above). Display/touch may still work; you can retry later with: cd $REPO_DIR && sudo ./scripts/reTerminal.sh --device reTerminal-DM" fi log "Removing $REPO_DIR" rm -rf "$REPO_DIR" # --- 6b. Re-apply splash and display (Seeed script sets disable_splash=1 and can duplicate Plymouth theme) --- log "--- Re-applying boot splash and Plymouth theme ---" CFG_PATH="/boot/firmware/config.txt" [[ -f /boot/firmware/config.txt ]] || CFG_PATH="/boot/config.txt" if [[ -f "$CFG_PATH" ]]; then if grep -q '^disable_splash=1' "$CFG_PATH"; then sed -i 's/^disable_splash=1$/disable_splash=0/' "$CFG_PATH" log "Set disable_splash=0 so Plymouth splash is shown" fi fi # Ensure Plymouth uses our custom theme only (remove Theme=pix, set Theme=custom) if [[ -f /etc/plymouth/plymouthd.conf ]]; then sed -i '/^Theme=/d' /etc/plymouth/plymouthd.conf grep -q '^\[Daemon\]' /etc/plymouth/plymouthd.conf || echo '[Daemon]' >> /etc/plymouth/plymouthd.conf echo 'Theme=custom' >> /etc/plymouth/plymouthd.conf log "Plymouth theme set to custom only" fi log "Running update-initramfs to apply Plymouth theme ..." update-initramfs -u -k all 2>/dev/null || true # --- 6c. Boot order: network first, then eMMC/SD (for future network boot / re-provisioning) --- # BOOT_ORDER: 0x2 = network, 0x1 = SD/eMMC. 0x21 = try network first, then local storage. log "--- Boot order (network first, then eMMC/SD) ---" if command -v rpi-eeprom-config >/dev/null 2>&1 && command -v rpi-eeprom-update >/dev/null 2>&1; then BOOTCONF="/tmp/first-boot-eeprom-conf.txt" if PEE="$(rpi-eeprom-update -l 2>/dev/null)" && [[ -n "$PEE" ]] && [[ -f "$PEE" ]]; then rpi-eeprom-config "$PEE" > "$BOOTCONF" 2>/dev/null || true fi if [[ -s "$BOOTCONF" ]]; then sed -i 's/^BOOT_ORDER=.*/BOOT_ORDER=0x21/' "$BOOTCONF" grep -q '^BOOT_ORDER=' "$BOOTCONF" || echo 'BOOT_ORDER=0x21' >> "$BOOTCONF" if rpi-eeprom-config --apply "$BOOTCONF" 2>/dev/null; then log "Boot order set to 0x21 (network first, then eMMC/SD); EEPROM update scheduled for next reboot" else log "WARNING: rpi-eeprom-config --apply failed; boot order unchanged" fi else log "WARNING: Could not read current EEPROM config; skipping boot order change" fi rm -f "$BOOTCONF" else log "rpi-eeprom-config/rpi-eeprom-update not found; skipping boot order (not a Pi4/CM4 or package missing)" fi # --- 7. One-shots (rotation + wallpaper at first login) --- log "--- One-shot scripts (run at pi first login) ---" install_oneshot set-rotation-once || true install_oneshot set-wallpaper-once || true log "One-shots will append to $LOGFILE when they run at first login" # --- 8. Allow pi to append to first-boot.log (for one-shot scripts) --- chmod 666 "$LOGFILE" log "Log file $LOGFILE is now appendable by user $PI_USER for one-shot scripts" # --- 9. Reboot --- log "=== first-boot.sh finished, rebooting ===" reboot