quickemu/quickget

596 lines
17 KiB
Bash
Executable File

#!/usr/bin/env bash
function os_support() {
echo freebsd \
kubuntu \
lubuntu \
macos \
ubuntu \
ubuntu-budgie \
ubuntu-kylin \
ubuntu-mate \
ubuntu-studio \
windows \
xubuntu
}
function releases_freebsd(){
echo 12_2 \
13_0
}
function releases_macos() {
echo high-sierra \
mojave \
catalina \
big-sur
}
function releases_ubuntu() {
echo bionic \
focal \
hirsute \
devel
}
function languages_windows() {
echo Arabic \
Brazilian Portuguese \
Bulgarian \
Chinese \(Simplified\) \
Chinese \(Traditional\) \
Croatian \
Czech \
Danish \
Dutch \
English \
English International \
Estonian \
Finnish \
French \
French Canadian \
German \
Greek \
Hebrew \
Hungarian \
Italian \
Japanese \
Korean \
Latvian \
Lithuanian \
Norwegian \
Polish \
Portuguese \
Romanian \
Russian \
Serbian Latin \
Slovak \
Slovenian \
Spanish \
Spanish \(Mexico\) \
Swedish \
Thai \
Turkish \
Ukrainian
}
function releases_windows() {
echo 8 \
10 \
11
}
function unattended_windows() {
cat << 'EOF' > "${1}"
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!--
For documentation on components:
http://technet.microsoft.com/en-us/library/ff699038.aspx
-->
<settings pass="generalize">
<!--
The PersistAllDeviceInstalls setting indicates whether all plug and
play devices on the destination computer remain installed during the
generalize configuration pass.
-->
<component name="Microsoft-Windows-PnPSysprep"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
</component>
</settings>
<settings pass="windowsPE">
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<DiskConfiguration>
<Disk wcm:action="add">
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
<CreatePartitions>
<CreatePartition wcm:action="add">
<Order>1</Order>
<Type>EFI</Type>
<Size>100</Size>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>2</Order>
<Type>MSR</Type>
<Size>512</Size>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>3</Order>
<Type>Primary</Type>
<Extend>true</Extend>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add">
<Order>1</Order>
<PartitionID>1</PartitionID>
<Label>System</Label>
<Format>FAT32</Format>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>2</Order>
<PartitionID>3</PartitionID>
<Label>Windows</Label>
<Letter>C</Letter>
<Format>NTFS</Format>
</ModifyPartition>
</ModifyPartitions>
</Disk>
<WillShowUI>OnError</WillShowUI>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<WillShowUI>OnError</WillShowUI>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>3</PartitionID>
</InstallTo>
</OSImage>
</ImageInstall>
<UserData>
<AcceptEula>true</AcceptEula>
<ProductKey>
<key>VK7JG-NPHTM-C97JM-9MPGT-3V66T</key>
<WillShowUI>Never</WillShowUI>
</ProductKey>
</UserData>
<DynamicUpdate>
<Enable>true</Enable>
<WillShowUI>Never</WillShowUI>
</DynamicUpdate>
</component>
<component name="Microsoft-Windows-PnpCustomizationsWinPE"
publicKeyToken="31bf3856ad364e35" language="neutral"
versionScope="nonSxS" processorArchitecture="amd64">
<!--
This makes the VirtIO drivers available to Windows, assuming that
the VirtIO driver disk
(https://alt.fedoraproject.org/pub/alt/virtio-win/latest/images/bin/)
is available as drive E:
-->
<DriverPaths>
<PathAndCredentials wcm:action="add" wcm:keyValue="1">
<Path>E:\amd64\w10</Path>
</PathAndCredentials>
<PathAndCredentials wcm:action="add" wcm:keyValue="2">
<Path>E:\viogpudo\w10\amd64</Path>
</PathAndCredentials>
<PathAndCredentials wcm:action="add" wcm:keyValue="3">
<Path>E:\viorng\w10\amd64</Path>
</PathAndCredentials>
<PathAndCredentials wcm:action="add" wcm:keyValue="4">
<Path>E:\NetKVM\w10\amd64</Path>
</PathAndCredentials>
</DriverPaths>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<ComputerName>*</ComputerName>
<OEMInformation>
<Manufacturer>Wimpys World</Manufacturer>
<Model>Quickemu</Model>
<SupportHours>24/7</SupportHours>
<SupportPhone></SupportPhone>
<SupportProvider>Wimpys World</SupportProvider>
<SupportURL>https://github.com/wimpysworld/quickemu/issues</SupportURL>
</OEMInformation>
<OEMName>Wimpys World</OEMName>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<OOBE>
<HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
</OOBE>
</component>
</settings>
</unattend>
EOF
}
function web_get() {
local DIR="${2}"
local FILE=""
local URL="${1}"
if [ -n "${3}" ]; then
FILE="${3}"
else
FILE="${URL##*/}"
fi
mkdir -p "${DIR}" 2>/dev/null
if ! wget --quiet --continue --show-progress --progress=bar:force:noscroll "${URL}" -O "${DIR}/${FILE}"; then
echo "ERROR! Failed to download ${URL}. Try running 'quickget' again."
exit 1
fi
}
function zsync_get() {
local DIR="${2}"
local FILE=""
local OUT=""
local URL="${1}"
FILE="${URL##*/}"
if [ -n "${3}" ]; then
OUT="${3}"
else
OUT="${FILE}"
fi
mkdir -p "${DIR}" 2>/dev/null
if ! zsync "${URL}.zsync" -i "${DIR}/${OUT}" -o "${DIR}/${OUT}"; then
echo "ERROR! Failed to download ${URL}.zsync"
exit 1
fi
if [ -e "${DIR}/${FILE}.zs-old" ]; then
rm -v "${DIR}/${FILE}.zs-old"
fi
}
function make_vm_dir() {
if ! mkdir -p "${VM_PATH}" 2>/dev/null; then
echo "ERROR! Unable to create directory ${VM_PATH}"
fi
}
function make_vm_config() {
local IMAGE_FILE=""
local ISO_FILE=""
local IMAGE_TYPE=""
local GUEST=""
IMAGE_FILE="${1}"
ISO_FILE="${2}"
if [[ "${OS}" == "freebsd" ]]; then
GUEST="freebsd"
IMAGE_TYPE="iso"
elif [[ "${OS}" == *"ubuntu"* ]]; then
GUEST="linux"
IMAGE_TYPE="iso"
elif [ "${OS}" == "macos" ]; then
GUEST="macos"
IMAGE_TYPE="img"
elif [ "${OS}" == "windows" ]; then
GUEST="windows"
IMAGE_TYPE="iso"
fi
if [ ! -e "${OS}-${RELEASE}.conf" ]; then
echo "Making VM configuration for ${OS}-${RELEASE}..."
cat << EOF > "${OS}-${RELEASE}.conf"
guest_os="${GUEST}"
disk_img="${VM_PATH}/disk.qcow2"
${IMAGE_TYPE}="${VM_PATH}/${IMAGE_FILE}"
EOF
if [ -n "${ISO_FILE}" ]; then
echo "fixed_iso=\"${VM_PATH}/${ISO_FILE}\"" >> "${OS}-${RELEASE}.conf"
fi
if [ "${OS}" == "macos" ]; then
echo "macos_release=\"${RELEASE}\"" >> "${OS}-${RELEASE}.conf"
fi
# Enable TPM for Windows 11
if [ "${OS}" == "windows" ] && [ ${RELEASE} -ge 11 ]; then
echo "tpm=\"on\"" >> "${OS}-${RELEASE}.conf"
fi
fi
}
function start_vm_info() {
echo
echo "To start your ${OS} ${RELEASE} virtual machine run:"
echo " quickemu --vm ${OS}-${RELEASE}.conf"
echo
}
function get_freebsd() {
# For future releases, use dvd1 iso files.
local URL=""
local DL_BASE="https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES"
local VERSION=""
case ${RELEASE} in
12_2|13_0) VERSION=${RELEASE//_/.};;
*)
echo "ERROR! FreeBSD ${RELEASE} is not a supported release."
releases_freebsd
exit 1
;;
esac
URL="${DL_BASE}/${VERSION}/FreeBSD-${VERSION}-RELEASE-amd64-dvd1.iso"
ISO="FreeBSD-${VERSION}-RELEASE-amd64-dvd1.iso"
web_get ${URL} ${VM_PATH}
make_vm_dir
make_vm_config ${ISO}
start_vm_info
}
function get_macos() {
local CWD=""
local MACRECOVERY=""
case ${RELEASE} in
high-sierra)
BOARD_ID="Mac-7BA5B2D9E42DDD94"
MLB="00000000000J80300";;
mojave)
BOARD_ID="Mac-7BA5B2DFE22DDD8C"
MLB="00000000000KXPG00";;
catalina)
BOARD_ID="Mac-CFF7D910A743CAAF"
MLB="00000000000PHCD00";;
big-sur)
BOARD_ID="Mac-E43C1C25D4880AD6"
MLB="00000000000000000";;
*) echo "ERROR! Unknown release: ${RELEASE}"
releases_macos
exit 1;;
esac
# Use a bundled macrecovery if possible
CWD="$(dirname ${0})"
if [ -x "${CWD}/macrecovery" ]; then
MACRECOVERY="${CWD}/macrecovery"
elif [ -x /usr/bin/macrecovery ]; then
MACRECOVERY="/usr/bin/macrecovery"
else
web_get "https://raw.githubusercontent.com/acidanthera/OpenCorePkg/master/Utilities/macrecovery/macrecovery.py" "${HOME}/.quickemu"
MACRECOVERY="python3 ${HOME}/.quickemu/macrecovery.py"
sed -i 's/\<env python\>/env python3/g' "${MACRECOVERY}"
fi
if [ -z "${MACRECOVERY}" ]; then
echo "ERROR! Can not find a usable macrecovery.py."
exit 1
fi
make_vm_dir
# Get firmware
web_get "https://github.com/kholia/OSX-KVM/raw/master/OpenCore-Catalina/OpenCore.qcow2" "${VM_PATH}"
web_get "https://github.com/kholia/OSX-KVM/raw/master/OVMF_CODE.fd" "${VM_PATH}"
if [ ! -e "${VM_PATH}/OVMF_VARS-1024x768.fd" ]; then
web_get "https://github.com/kholia/OSX-KVM/raw/master/OVMF_VARS-1024x768.fd" "${VM_PATH}"
fi
if [ ! -e "${VM_PATH}/RecoveryImage.chunklist" ]; then
echo "Downloading ${RELEASE}..."
${MACRECOVERY} \
--board-id "${BOARD_ID}" \
--mlb "${MLB}" \
--basename RecoveryImage \
--outdir "${VM_PATH}" \
download
fi
if [ -e "${VM_PATH}/RecoveryImage.dmg" ] && [ ! -e "${VM_PATH}/RecoveryImage.img" ]; then
echo "Converting RecoveryImage..."
qemu-img convert "${VM_PATH}/RecoveryImage.dmg" -O raw "${VM_PATH}/RecoveryImage.img"
fi
make_vm_config RecoveryImage.img
start_vm_info
}
function get_ubuntu() {
local DEVEL="daily-live"
local ISO=""
local PROJECT=""
local RELEASES=""
local URL=""
case ${OS} in
kubuntu|lubuntu|ubuntu|ubuntu-budgie|ubuntu-mate|xubuntu)
PROJECT="${OS}";;
ubuntu-kylin)
PROJECT="ubuntukylin";;
ubuntu-studio)
PROJECT="ubuntustudio"
DEVEL="dvd";;
*)
echo "ERROR! ${OS} is not a recognised Ubuntu flavour."
exit 1;;
esac
if [ "${RELEASE}" == "devel" ]; then
URL="http://cdimage.ubuntu.com/${PROJECT}/${DEVEL}/current"
elif [ "${PROJECT}" == "ubuntu" ]; then
URL="http://releases.ubuntu.com/${RELEASE}"
else
URL="http://cdimage.ubuntu.com/${PROJECT}/releases/${RELEASE}/release"
fi
RELEASES=$(releases_ubuntu)
if [[ "${RELEASES}" != *"${RELEASE}"* ]]; then
echo "ERROR! ${RELEASE} is not a supported release."
releases_ubuntu
exit 1
fi
make_vm_dir
echo "Downloading SHA256SUMS..."
web_get "${URL}/SHA256SUMS" "${VM_PATH}"
ISO=$(grep 'desktop\|dvd' "${VM_PATH}/SHA256SUMS" | grep amd64 | cut -d' ' -f2 | sed 's|*||g')
echo "Downloading "${URL}/${ISO}"..."
if [ "${RELEASE}" == "devel" ]; then
zsync_get "${URL}/${ISO}" "${VM_PATH}" "${OS}-${RELEASE}.iso"
make_vm_config "${OS}-${RELEASE}.iso"
else
web_get "${URL}/${ISO}" "${VM_PATH}"
echo "Checking SHA256SUMS..."
cd "${VM_PATH}"
if ! sha256sum --check SHA256SUMS --ignore-missing --status; then
echo "ERROR! ${ISO} doesn't match ${VM_PATH}/SHA256SUMS. Try running 'quickget' again."
exit 1
else
echo "All good."
fi
cd ..
make_vm_config "${ISO}"
fi
start_vm_info
}
# Adapted from https://gist.github.com/hongkongkiwi/15a5bf16437315df256c118c163607cb
function get_windows() {
case ${RELEASE} in
8|10|11) true;;
*) echo "ERROR! Windows ${RELEASE} is not supported."
releases_windows
exit 1
;;
esac
local ARCH="x64"
local LANG_CODE="en"
local LATEST_WINDOWS_VERSION=""
local WINDOWS_NAME=""
local VERSION_ID=""
local EDITION_ID=""
local LANGUAGE_ID=""
local FILE_NAME=""
local ARCH_ID=""
local DOWNLOAD_INFO=""
local DOWNLOAD_ID=""
local DOWNLOAD_URL=""
echo "Getting Windows ${RELEASE} URL..."
WINDOWS_VERSIONS=$(wget -q -O- "https://tb.rg-adguard.net/php/get_version.php?type_id=1" | jq '.versions | sort_by(-(.version_id | tonumber))')
LATEST_WINDOWS_VERSION=$(echo "${WINDOWS_VERSIONS}" | jq -c 'map(select(.name | contains("Windows '${RELEASE}'")))[0]')
WINDOWS_NAME=$(echo "${LATEST_WINDOWS_VERSION}" | jq -r .name)
VERSION_ID=$(echo "${LATEST_WINDOWS_VERSION}" | jq -r .version_id)
case ${RELEASE} in
8) EDITION_ID=$(wget -q -O- "https://tb.rg-adguard.net/php/get_edition.php?version_id=${VERSION_ID}&lang=name_${LANG_CODE}" | jq -r '.editions[] | select(.name_'${LANG_CODE}'=="Windows 8.1 Pro + Core").edition_id');;
10|11) EDITION_ID=$(wget -q -O- "https://tb.rg-adguard.net/php/get_edition.php?version_id=${VERSION_ID}&lang=name_${LANG_CODE}" | jq -r '.editions[] | select(.name_'${LANG_CODE}'=="Windows '${RELEASE}'").edition_id');;
esac
LANGUAGE_ID=$(wget -q -O- "https://tb.rg-adguard.net/php/get_language.php?edition_id=${EDITION_ID}&lang=name_${LANG_CODE}" | jq -r '.languages[] | select(.name_'${LANG_CODE}'=="'"${LANG_NAME}"'").language_id')
ARCH_INFO=$(wget -q -O- "https://tb.rg-adguard.net/php/get_arch.php?language_id=${LANGUAGE_ID}")
FILE_NAME=$(echo "${ARCH_INFO}" | jq -r '.archs[] | select(.name | contains("'${ARCH}'")).name')
ARCH_ID=$(echo "${ARCH_INFO}" | jq -r '.archs[] | select(.name | contains("'${ARCH}'")).arch_id')
DOWNLOAD_INFO=$(wget -q -O- "https://tb.rg-adguard.net/dl.php?fileName=${ARCH_ID}&lang=en")
DOWNLOAD_ID=$(echo "${DOWNLOAD_INFO}" | grep -oP '(?<=https:\/\/tb\.rg-adguard\.net/dl\.php\?go=)[0-9a-z]+')
DOWNLOAD_URL="https://tb.rg-adguard.net/dl.php?go=${DOWNLOAD_ID}"
make_vm_dir
echo "Downloading ${WINDOWS_NAME}..."
web_get "${DOWNLOAD_URL}" "${VM_PATH}" "${FILE_NAME}"
echo "Downloading virtio-win.iso..."
web_get "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" "${VM_PATH}"
if [ ! -e "${VM_PATH}/unattended.iso" ]; then
case ${RELEASE} in
10|11)
echo "Making unattended.iso"
unattended_windows "${VM_PATH}/unattended/autounattend.xml"
mkisofs -quiet -l -o "${VM_PATH}/unattended.iso" "${VM_PATH}/unattended/"
;;
esac
fi
make_vm_config "${FILE_NAME}" "virtio-win.iso"
start_vm_info
}
if [ -n "${1}" ]; then
OS="${1,,}"
else
echo "ERROR! You must specify an OS:"
os_support
exit 1
fi
if [ -n "${2}" ]; then
RELEASE="${2,,}"
else
echo "ERROR! You must specify an OS release name."
if [ "${OS}" == "freebsd" ]; then
releases_freebsd
elif [ "${OS}" == "macos" ]; then
releases_macos
elif [[ "${OS}" == *"ubuntu"* ]]; then
releases_ubuntu
elif [ "${OS}" == "windows" ]; then
releases_windows
fi
exit 1
fi
VM_PATH="${OS}-${RELEASE}"
if [ "${OS}" == "macos" ]; then
get_macos
elif [[ "${OS}" == *"freebsd" ]]; then
get_freebsd
elif [[ "${OS}" == *"ubuntu"* ]]; then
get_ubuntu
elif [ "${OS}" == "windows" ]; then
if [ -n "${3}" ]; then
LANG_NAME="${3}"
LANG_NAMES=$(languages_windows)
if [[ "${LANG_NAMES}" != *"${LANG_NAME}"* ]]; then
echo "ERROR! ${LANG_NAME} is not a supported language:"
languages_windows
exit 1
fi
else
LANG_NAME="English International"
fi
get_windows "${LANG_NAME}"
else
echo "ERROR! You must specify an OS:"
os_support
exit 1
fi