vmix.nix/lib/images/macos/guest/pe-lib.sh
Git Sagar 8dc8f4265d macOS: drive the install and all customization from a Recovery "PE", no GUI
Replace the screenshot/OCR/keystroke driving of Apple's Recovery with a
"PE": BaseSystem.dmg (a journaled HFS+ volume, writable from Linux) with one
LaunchDaemon added (makeRecoveryPE) that runs /Volumes/VMIX/run.sh as root at
boot, records the status and powers off. launchd loads it alongside its signed
cache (verified on Tahoe 26.6.2); same idea as AutoNBI/Imagr NetBoot images.

- makeImage: the PE runs vmix-install.sh (erase, installer app, SharedSupport
  pkgdmg, startosinstall). Progress is read from the serial console
  (boot-args serial=3 -v, VMIX-* markers) and screenshots (brightness only).
  Fully offline; prepare now takes ~5 min instead of ~10.
- customizeImage: boots the PE with the image attached and runs the template
  offline against the mounted System/Data volumes; OpenCore ScanPolicy
  restricted to HFS+/SATA so only the PE can boot. One PE boot ~30 s. The
  installed macOS is never booted for customization, so nothing depends on
  launchd/BTM approval or a first-boot agent (removed).
- templates rewritten for offline use: generalize creates the user with
  dscl -f (admin, home, auto-login kcpassword, Setup Assistant suppression,
  hostname, locale, timezone, keyboard type, container resize); remote-access,
  no-updates, performance edit the target's plists.
- makeBootDisk: build-time OpenCore variant (serial console, ScanPolicy).
- vm-driver.py rewritten: passive observation only (serial markers, kernel
  boots, panics, brightness), disk+serial-aware hang watchdog, reboot-death
  reset, halt/loginwindow detection. No OCR/tesseract.
- OpenCore: four SMBIOS DIMMs for MacPro7,1 (no "Memory Modules
  Misconfigured" warning).
- tools/soak.sh: repeatability harness.

Verified on daku: base install 23 min end to end; basic + generalize in three
~30 s PE boots; the result auto-logs into the desktop with the created user.

Root cause of the "first-boot hang" (from the serial log): the guest's restart
path panics (IOPlatformHaltRestartAction -> AppleSMC, SMCWDT smcWriteKey
kSMCBadCommand, nested panic) because the pinned OSX-KVM Lilu disables itself
on macOS 26, so VirtualSMC never loads. Handled by the driver (reset within
60 s); kext update to follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XsESshRCoBoUVWV9qKURUF
2026-09-10 13:33:38 -03:00

51 lines
2.4 KiB
Bash

# vmix PE helpers, sourced by run.sh scripts running in the recovery.
# Expects V=/Volumes/VMIX (set by pe.sh) and VOLUME_NAME from vmix.conf.
V=${V:-/Volumes/VMIX}
[ -f "$V/vmix.conf" ] && . "$V/vmix.conf"
VOLUME_NAME=${VOLUME_NAME:-Macintosh HD}
pe_log() { echo "VMIX: $*"; }
pe_fail() { echo "VMIX-FAIL: $*"; exit 1; }
# Mount the installed system's APFS volume group (System read-only, Data rw) and
# export SYS / DATA mount points plus SYS_ID / DATA_ID device identifiers.
pe_mount_target() {
local list; list=$(diskutil list)
DATA_ID=$(echo "$list" | awk -v n="APFS Volume $VOLUME_NAME - Data" 'index($0, n) {print $NF; exit}')
SYS_ID=$(echo "$list" | awk -v n="APFS Volume $VOLUME_NAME " '!/ - Data/ && index($0, n) {print $NF; exit}')
[ -n "$DATA_ID" ] && [ -n "$SYS_ID" ] || { pe_log "target volumes not found"; echo "$list"; return 1; }
diskutil mount "$SYS_ID" >/dev/null 2>&1 || true
diskutil mount "$DATA_ID" >/dev/null 2>&1 || true
SYS=$(diskutil info "$SYS_ID" | sed -n 's/^ *Mount Point: *//p')
DATA=$(diskutil info "$DATA_ID" | sed -n 's/^ *Mount Point: *//p')
[ -d "$DATA/private/var/db" ] || { pe_log "Data volume not mounted (SYS=[$SYS] DATA=[$DATA])"; return 1; }
pe_log "target mounted: SYS=[$SYS] DATA=[$DATA]"
export SYS DATA SYS_ID DATA_ID
}
pe_unmount_target() {
sync
diskutil unmount "$DATA_ID" >/dev/null 2>&1 || true
diskutil unmount "$SYS_ID" >/dev/null 2>&1 || true
}
# plist helpers on files of the (offline) target: create the file if missing.
pe_plist_set() { # FILE KEYPATH TYPE VALUE (TYPE: string|bool|integer|float)
local f=$1 k=$2 t=$3 v=$4
[ -f "$f" ] || plutil -create xml1 "$f"
plutil -replace "$k" "-$t" "$v" "$f"
}
pe_plist_dict() { # FILE KEYPATH — make sure a dictionary exists at KEYPATH
local f=$1 k=$2
[ -f "$f" ] || plutil -create xml1 "$f"
plutil -extract "$k" xml1 -o /dev/null "$f" >/dev/null 2>&1 || plutil -insert "$k" -dictionary "$f"
}
# launchd service override on the target (disabled.plist): pe_service LABEL true|false
pe_service_disabled() {
local f="$DATA/private/var/db/com.apple.xpc.launchd/disabled.plist"
mkdir -p "$(dirname "$f")"
pe_plist_set "$f" "$1" bool "$2"
}
# version of the installed system
pe_target_version() { plutil -extract ProductVersion raw -o - "$SYS/System/Library/CoreServices/SystemVersion.plist" 2>/dev/null; }
pe_target_build() { plutil -extract ProductBuildVersion raw -o - "$SYS/System/Library/CoreServices/SystemVersion.plist" 2>/dev/null; }