mirror of
https://github.com/dockur/macos.git
synced 2026-03-11 08:34:24 +00:00
295 lines
7.9 KiB
Bash
295 lines
7.9 KiB
Bash
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
# Docker environment variables
|
|
|
|
: "${SN:=""}" # Device serial
|
|
: "${MLB:=""}" # Board serial
|
|
: "${MAC:=""}" # MAC address
|
|
: "${UUID:=""}" # Unique ID
|
|
: "${VERSION:=""}" # OSX Version
|
|
: "${WIDTH:="1920"}" # Horizontal
|
|
: "${HEIGHT:="1080"}" # Vertical
|
|
: "${MODEL:="iMacPro1,1"}" # Device model
|
|
|
|
BASE_IMG_ID="InstallMedia"
|
|
BASE_IMG="$STORAGE/base.dmg"
|
|
|
|
function getRandom() {
|
|
local length="${1}"
|
|
local result=""
|
|
local chars=("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F")
|
|
|
|
for ((i=0; i<length; i++)); do
|
|
result+="${chars[$((RANDOM % 16))]}"
|
|
done
|
|
|
|
echo "$result"
|
|
return 0
|
|
}
|
|
|
|
delay() {
|
|
|
|
local i
|
|
local delay="$1"
|
|
local msg="Retrying failed download in X seconds..."
|
|
|
|
info "${msg/X/$delay}"
|
|
|
|
for i in $(seq "$delay" -1 1); do
|
|
html "${msg/X/$i}"
|
|
sleep 1
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
function download() {
|
|
local info=""
|
|
local dest="$1"
|
|
local board="$2"
|
|
local version="$3"
|
|
local type="latest"
|
|
local appleSession=""
|
|
local downloadLink=""
|
|
local downloadSession=""
|
|
local mlb="00000000000000000"
|
|
local rc total size progress
|
|
|
|
local msg="Downloading macOS ${version^}"
|
|
info "$msg recovery image..." && html "$msg..."
|
|
|
|
appleSession=$(curl --disable -v -H "Host: osrecovery.apple.com" \
|
|
-H "Connection: close" \
|
|
-A "InternetRecovery/1.0" https://osrecovery.apple.com/ 2>&1 | tr ';' '\n' | awk -F'session=|;' '{print $2}' | grep 1)
|
|
info=$(curl --disable -s -X POST -H "Host: osrecovery.apple.com" \
|
|
-H "Connection: close" \
|
|
-A "InternetRecovery/1.0" \
|
|
-b "session=\"${appleSession}\"" \
|
|
-H "Content-Type: text/plain" \
|
|
-d $'cid='"$(getRandom 16)"$'\nsn='"${mlb}"$'\nbid='"${board}"$'\nk='"$(getRandom 64)"$'\nfg='"$(getRandom 64)"$'\nos='"${type}" \
|
|
https://osrecovery.apple.com/InstallationPayload/RecoveryImage | tr ' ' '\n')
|
|
|
|
downloadLink=$(echo "$info" | grep 'oscdn' | grep 'dmg')
|
|
downloadSession=$(echo "$info" | grep 'expires' | grep 'dmg')
|
|
|
|
if [ -z "$downloadLink" ] || [ -z "$downloadSession" ]; then
|
|
|
|
local code="99"
|
|
msg="Failed to connect to the Apple servers, reason:"
|
|
|
|
curl --silent --max-time 10 --output /dev/null --fail -H "Host: osrecovery.apple.com" -H "Connection: close" -A "InternetRecovery/1.0" https://osrecovery.apple.com/ || {
|
|
code="$?"
|
|
}
|
|
|
|
case "${code,,}" in
|
|
"6" ) error "$msg could not resolve host!" ;;
|
|
"7" ) error "$msg no internet connection available!" ;;
|
|
"28" ) error "$msg connection timed out!" ;;
|
|
"99" )
|
|
[ -n "$info" ] && echo "$info" && echo
|
|
error "$msg unknown error" ;;
|
|
*) error "$msg $code" ;;
|
|
esac
|
|
|
|
return 1
|
|
fi
|
|
|
|
# Check if running with interactive TTY or redirected to docker log
|
|
if [ -t 1 ]; then
|
|
progress="--progress=bar:noscroll"
|
|
else
|
|
progress="--progress=dot:giga"
|
|
fi
|
|
|
|
rm -f "$dest"
|
|
/run/progress.sh "$dest" "0" "$msg ([P])..." &
|
|
|
|
{ wget "$downloadLink" -O "$dest" -q --header "Host: oscdn.apple.com" --header "Connection: close" --header "User-Agent: InternetRecovery/1.0" --header "Cookie: AssetToken=${downloadSession}" --timeout=30 --no-http-keep-alive --show-progress "$progress"; rc=$?; } || :
|
|
|
|
fKill "progress.sh"
|
|
|
|
if (( rc == 0 )) && [ -f "$dest" ]; then
|
|
total=$(stat -c%s "$dest")
|
|
size=$(formatBytes "$total")
|
|
if [ "$total" -lt 100000 ]; then
|
|
error "Invalid recovery image, file is only $size ?" && return 1
|
|
fi
|
|
html "Download finished successfully..."
|
|
return 0
|
|
fi
|
|
|
|
msg="Failed to download $downloadLink"
|
|
(( rc == 3 )) && error "$msg , cannot write file (disk full?)" && return 1
|
|
(( rc == 4 )) && error "$msg , network failure!" && return 1
|
|
(( rc == 8 )) && error "$msg , server issued an error response!" && return 1
|
|
|
|
error "$msg , reason: $rc"
|
|
return 1
|
|
}
|
|
|
|
install() {
|
|
|
|
local board
|
|
local version="$1"
|
|
local dest="$2"
|
|
|
|
case "${version,,}" in
|
|
"tahoe" | "26"* | "16"* )
|
|
board="Mac-CFF7D910A743CAAF" ;;
|
|
"sequoia" | "15"* )
|
|
board="Mac-937A206F2EE63C01" ;;
|
|
"sonoma" | "14"* )
|
|
board="Mac-827FAC58A8FDFA22" ;;
|
|
"ventura" | "13"* )
|
|
board="Mac-4B682C642B45593E" ;;
|
|
"monterey" | "12"* )
|
|
board="Mac-B809C3757DA9BB8D" ;;
|
|
"bigsur" | "big-sur" | "11"* )
|
|
board="Mac-2BD1B31983FE1663" ;;
|
|
"catalina" | "10"* )
|
|
board="Mac-00BE6ED71E35EB86" ;;
|
|
*)
|
|
error "Unknown VERSION specified, value \"${version}\" is not recognized!"
|
|
return 1 ;;
|
|
esac
|
|
|
|
rm -f "$dest"
|
|
|
|
if ! makeDir "$STORAGE"; then
|
|
error "Failed to create directory \"$STORAGE\" !" && return 1
|
|
fi
|
|
|
|
find "$STORAGE" -maxdepth 1 -type f \( -iname '*.rom' -or -iname '*.vars' \) -delete
|
|
find "$STORAGE" -maxdepth 1 -type f \( -iname 'data.*' -or -iname 'macos.*' \) -delete
|
|
|
|
if [ -f "/boot.dmg" ]; then
|
|
cp "/boot.dmg" "$dest"
|
|
return 0
|
|
fi
|
|
|
|
local file="$STORAGE/boot.dmg"
|
|
|
|
if ! download "$file" "$board" "$version"; then
|
|
delay 5
|
|
if ! download "$file" "$board" "$version"; then
|
|
rm -f "$file"
|
|
exit 60
|
|
fi
|
|
fi
|
|
|
|
mv -f "$file" "$dest"
|
|
return 0
|
|
}
|
|
|
|
generateID() {
|
|
|
|
local file="$STORAGE/$PROCESS.id"
|
|
|
|
[ -n "$UUID" ] && return 0
|
|
[ -s "$file" ] && UUID=$(<"$file")
|
|
UUID="${UUID//[![:print:]]/}"
|
|
[ -n "$UUID" ] && return 0
|
|
|
|
UUID=$(cat /proc/sys/kernel/random/uuid 2> /dev/null || uuidgen --random)
|
|
UUID="${UUID^^}"
|
|
UUID="${UUID//[![:print:]]/}"
|
|
|
|
echo "$UUID" > "$file"
|
|
! setOwner "$file" && error "Failed to set the owner for \"$file\" !"
|
|
|
|
return 0
|
|
}
|
|
|
|
generateAddress() {
|
|
|
|
local file="$STORAGE/$PROCESS.mac"
|
|
|
|
[ -n "$MAC" ] && return 0
|
|
[ -s "$file" ] && MAC=$(<"$file")
|
|
MAC="${MAC//[![:print:]]/}"
|
|
[ -n "$MAC" ] && return 0
|
|
|
|
# Generate Apple MAC address based on Docker container ID in hostname
|
|
MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/00:16:cb:\3:\4:\5/')
|
|
MAC="${MAC^^}"
|
|
|
|
echo "$MAC" > "$file"
|
|
! setOwner "$file" && error "Failed to set the owner for \"$file\" !"
|
|
|
|
return 0
|
|
}
|
|
|
|
generateSerial() {
|
|
|
|
local file="$STORAGE/$PROCESS.sn"
|
|
local file2="$STORAGE/$PROCESS.mlb"
|
|
|
|
[ -n "$SN" ] && [ -n "$MLB" ] && return 0
|
|
[ -s "$file" ] && SN=$(<"$file")
|
|
[ -s "$file2" ] && MLB=$(<"$file2")
|
|
SN="${SN//[![:print:]]/}"
|
|
MLB="${MLB//[![:print:]]/}"
|
|
[ -n "$SN" ] && [ -n "$MLB" ] && return 0
|
|
|
|
# Generate unique serial numbers for machine
|
|
SN=$(/usr/local/bin/macserial --num 1 --model "${MODEL}" 2>/dev/null)
|
|
|
|
SN="${SN##*$'\n'}"
|
|
[[ "$SN" != *" | "* ]] && error "$SN" && return 1
|
|
|
|
MLB=${SN#*|}
|
|
MLB="${MLB#"${MLB%%[![:space:]]*}"}"
|
|
SN="${SN%%|*}"
|
|
SN="${SN%"${SN##*[![:space:]]}"}"
|
|
|
|
echo "$SN" > "$file"
|
|
echo "$MLB" > "$file2"
|
|
|
|
! setOwner "$file" && error "Failed to set the owner for \"$file\" !"
|
|
! setOwner "$file2" && error "Failed to set the owner for \"$file2\" !"
|
|
|
|
return 0
|
|
}
|
|
|
|
if [[ "${VERSION}" == \"*\" || "${VERSION}" == \'*\' ]]; then
|
|
VERSION="${VERSION:1:-1}"
|
|
fi
|
|
|
|
VERSION=$(expr "$VERSION" : "^\ *\(.*[^ ]\)\ *$")
|
|
|
|
if [ -z "$VERSION" ]; then
|
|
|
|
VERSION="14"
|
|
warn "no value specified for the VERSION variable, defaulting to \"${VERSION}\"."
|
|
|
|
fi
|
|
|
|
if [ ! -f "$BASE_IMG" ] || [ ! -s "$BASE_IMG" ]; then
|
|
|
|
STORAGE="$STORAGE/${VERSION,,}"
|
|
BASE_IMG="$STORAGE/base.dmg"
|
|
|
|
if [ ! -f "$BASE_IMG" ] || [ ! -s "$BASE_IMG" ]; then
|
|
! install "$VERSION" "$BASE_IMG" && exit 34
|
|
! setOwner "$BASE_IMG" && error "Failed to set the owner for \"$BASE_IMG\" !"
|
|
fi
|
|
|
|
fi
|
|
|
|
if ! generateID; then
|
|
error "Failed to generate UUID!" && exit 35
|
|
fi
|
|
|
|
if ! generateSerial; then
|
|
error "Failed to generate serial number!" && exit 36
|
|
fi
|
|
|
|
if ! generateAddress; then
|
|
error "Failed to generate MAC address!" && exit 37
|
|
fi
|
|
|
|
DISK_OPTS="-device virtio-blk-pci,drive=${BASE_IMG_ID},bus=pcie.0,addr=0x6"
|
|
DISK_OPTS+=" -drive file=$BASE_IMG,id=$BASE_IMG_ID,format=dmg,cache=unsafe,readonly=on,if=none"
|
|
|
|
return 0
|