Compare commits

...

53 Commits

Author SHA1 Message Date
Kroese efe47da76d build: Improve installation tests (#2179) 2026-08-22 01:05:26 +02:00
Kroese 2fe4645685 fix: Hide long mode from 32-bit Windows guests (#2178) 2026-08-22 00:07:19 +02:00
Kroese afa55b476c feat: Disable menu animations on NT5 and Win9x (#2177) 2026-08-21 19:30:23 +02:00
Kroese 1e5d3c75a8 feat: Add additional download mirrors (#2176) 2026-08-21 18:39:01 +02:00
Kroese 4c74370da0 feat: Shuffle download mirror order (#2175) 2026-08-21 18:33:02 +02:00
Kroese ae9fa8716b feat: Enable Hyper-V for Windows Vista and 7 (#2174) 2026-08-21 18:01:30 +02:00
Kroese 0d84fd35f5 fix: Disable generic display driver for Server 2003 (#2173) 2026-08-21 17:34:05 +02:00
Kroese d68a489295 feat: Add additional download mirrors (#2172) 2026-08-21 17:10:44 +02:00
Kroese c86067cb6e feat: Disable Shutdown Tracker for Server 2003 (#2171) 2026-08-21 15:43:49 +02:00
Kroese 37f4908f22 feat: Disable Shutdown Tracker on Windows Server (#2170) 2026-08-21 15:38:29 +02:00
Kroese 09627c4e51 feat: Import driver certificate on Windows 2003 (#2169) 2026-08-21 01:09:36 +02:00
Kroese f6e6a15b42 feat: Defer mapped Z drive until first access (#2168) 2026-08-21 00:31:09 +02:00
Kroese eeaf64bdd9 feat: Install faster graphics driver for 2000/XP/2003 (#2167) 2026-08-20 22:43:01 +02:00
Kroese eb1befd90e feat: Implement Windows 95 support (#2165) 2026-08-20 04:50:57 +02:00
Kroese afb28c9234 build: Update wsddn package to v1.27 (#2164) 2026-08-20 03:06:20 +02:00
Kroese 2a116bf655 build: Update QEMU base image to v7.48 (#2163) 2026-08-19 20:11:24 +02:00
Kroese 9af2409510 feat: Implement Windows ME support (#2161) 2026-08-18 18:04:20 +02:00
Kroese 6bfe3b1adc build: Automatically open a tunnel for stalled tests (#2159) 2026-08-18 16:18:21 +02:00
Kroese 2904a8d932 build: Update VirtIO drivers to v1.9.60 (#2155) 2026-08-18 07:21:50 +02:00
Kroese e490d142ef build: Update QEMU base image to v7.47 (#2154) 2026-08-18 05:15:55 +02:00
Kroese 2bf6ae55ab feat: Hide first-logon console during answer generation (#2152) 2026-08-18 02:15:37 +02:00
Kroese a0a4520be6 docs: Environment variables (#2153) 2026-08-18 02:13:02 +02:00
Kroese 003d7da5db fix: Change socket path (#2150) 2026-08-17 14:20:01 +02:00
Kroese 1c2efa65df docs: Environment variables (#2148) 2026-08-16 16:10:40 +02:00
Kroese 00fe722d4e build: Update QEMU base image to v7.46 (#2147) 2026-08-16 03:01:20 +02:00
Kroese 603bd079fc fix: Improve failed legacy boot detection (#2146) 2026-08-15 16:46:35 +02:00
Kroese be032648e8 fix: Handle system image shutdown (#2145) 2026-08-15 16:33:41 +02:00
Kroese 1d98f065dc fix: Find nested ISO files recursively (#2144) 2026-08-15 16:10:56 +02:00
Kroese a0822d00a7 feat: Implement Windows 98 support (#2143) 2026-08-15 16:02:41 +02:00
Kroese 8713fa50e1 fix: Error handling while generating answer file (#2142) 2026-08-15 12:54:17 +02:00
Kroese e8028db237 feat: Refactor Windows installation and startup flow (#2141) 2026-08-15 12:42:13 +02:00
Kroese e4b910fba3 fix: Decrease shutdown timeout during setup (#2140) 2026-08-15 01:21:50 +02:00
Kroese 1066895aca feat: Centralize platform and image state checks (#2139) 2026-08-14 11:46:00 +02:00
Kroese 39862b25b5 feat: Centralize boot marker checks (#2138) 2026-08-14 11:19:03 +02:00
Kroese a3461db9a8 feat: Centralize image and boot state checks (#2137) 2026-08-14 11:07:46 +02:00
Kroese 019c70dbaa feat: Add qxldod driver (#2136) 2026-08-13 23:29:47 +02:00
Kroese ffec37e758 fix: Check for ACPI support before shutdown (#2135) 2026-08-13 23:15:11 +02:00
Kroese e832e705f9 feat: Support pre-installed system images (#2134) 2026-08-13 23:10:56 +02:00
Kroese db6b085ae2 fix: Improve extracted ISO size estimation (#2133) 2026-08-13 23:07:45 +02:00
Kroese cf93c6d7f3 build: Update entrypoint (#2132) 2026-08-13 23:03:28 +02:00
Kroese afce7af300 build: Update VirtIO drivers to v1.9.59 (#2131) 2026-08-13 23:00:54 +02:00
Kroese cfac963278 feat: Install the VirtIO Balloon service on 32-bit Windows (#2130) 2026-08-13 12:36:42 +02:00
Kroese 0d7e26adad feat: Use removeState helper from base (#2127) 2026-08-10 11:36:13 +02:00
Kroese 9ef6f3c4ca fix: Migrate Win9x machine type (#2126) 2026-08-10 07:10:13 +02:00
Kroese 5b9a376111 fix: Stop console after QEMU exits (#2125) 2026-08-10 03:37:05 +02:00
Kroese a4ae486d83 build: Refactor Windows installation code (#2124) 2026-08-10 02:05:45 +02:00
Kroese c9b12ba97d build: Update QEMU base image to v7.45 (#2123) 2026-08-10 00:51:53 +02:00
Kroese 7e3e24780d feat: Enforce a minimal disk size per Windows version (#2122) 2026-08-09 22:16:20 +02:00
Kroese 433c83cab0 docs: Add QMP configuration variable (#2121) 2026-08-09 11:25:25 +02:00
Kroese 19264a42da feat: Improve error handling (#2120) 2026-08-09 02:46:04 +02:00
Kroese 33ae12677e fix: Skip ISO inspection for non-ISO downloads (#2119) 2026-08-08 17:53:15 +02:00
Kroese 133c3d3bc3 feat: Move image edition selection policy (#2118) 2026-08-08 17:39:43 +02:00
Kroese 43be9024e9 fix: Do not fail if marker cannot be created (#2117) 2026-08-08 16:33:27 +02:00
37 changed files with 8270 additions and 2791 deletions
@@ -1,8 +1,8 @@
{ {
"name": "Tiny11 Nano", "name": "Windows ME",
"service": "windows", "service": "windows",
"containerEnv": { "containerEnv": {
"VERSION": "nano11" "VERSION": "me"
}, },
"forwardPorts": [8006], "forwardPorts": [8006],
"portsAttributes": { "portsAttributes": {
@@ -0,0 +1,19 @@
{
"name": "Windows 98",
"service": "windows",
"containerEnv": {
"VERSION": "98"
},
"forwardPorts": [8006],
"portsAttributes": {
"8006": {
"label": "Web",
"onAutoForward": "notify"
}
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
},
"dockerComposeFile": "../codespaces.yml",
"initializeCommand": "docker system prune --all --force"
}
@@ -0,0 +1,19 @@
{
"name": "Windows 95",
"service": "windows",
"containerEnv": {
"VERSION": "95"
},
"forwardPorts": [8006],
"portsAttributes": {
"8006": {
"label": "Web",
"onAutoForward": "notify"
}
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
},
"dockerComposeFile": "../codespaces.yml",
"initializeCommand": "docker system prune --all --force"
}
@@ -0,0 +1,19 @@
{
"name": "Reactos",
"service": "windows",
"containerEnv": {
"VERSION": "reactos"
},
"forwardPorts": [8006],
"portsAttributes": {
"8006": {
"label": "Web",
"onAutoForward": "notify"
}
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
},
"dockerComposeFile": "../codespaces.yml",
"initializeCommand": "docker system prune --all --force"
}
+319 -4
View File
@@ -73,6 +73,12 @@ on:
default: false default: false
type: boolean type: boolean
tunnel_delay:
description: Automatically open the noVNC tunnel after this many seconds
required: false
default: 0
type: number
kill_on_failure: kill_on_failure:
description: Stop the workflow when an installation failure is detected description: Stop the workflow when an installation failure is detected
required: false required: false
@@ -227,11 +233,12 @@ jobs:
shell: bash shell: bash
env: env:
CALLBACK: ${{ inputs.callback }} CALLBACK: ${{ inputs.callback }}
VERSION: ${{ inputs.version }}
run: | run: |
set -Eeuo pipefail set -Eeuo pipefail
case "$CALLBACK" in case "$CALLBACK" in
powershell | legacy) powershell | legacy | basic)
;; ;;
*) *)
echo "Unsupported guest script type: $CALLBACK" echo "Unsupported guest script type: $CALLBACK"
@@ -249,6 +256,7 @@ jobs:
printf '%s\n' "$token" > "$RUNNER_TEMP/data/validation.token" printf '%s\n' "$token" > "$RUNNER_TEMP/data/validation.token"
printf '%s\n' "$token" > "$RUNNER_TEMP/oem/validation.token" printf '%s\n' "$token" > "$RUNNER_TEMP/oem/validation.token"
if [[ "$CALLBACK" != "basic" ]]; then
cat > "$RUNNER_TEMP/oem/sync-log.bat" <<'BATCH' cat > "$RUNNER_TEMP/oem/sync-log.bat" <<'BATCH'
@echo off @echo off
setlocal setlocal
@@ -327,6 +335,7 @@ jobs:
shell.Run "cmd.exe /C echo VALIDATION_CALLBACK_FAILED^>COM1", 0, True shell.Run "cmd.exe /C echo VALIDATION_CALLBACK_FAILED^>COM1", 0, True
VBSCRIPT VBSCRIPT
fi
case "$CALLBACK" in case "$CALLBACK" in
powershell) powershell)
@@ -361,6 +370,12 @@ jobs:
} }
} }
if (-not (Test-Path -LiteralPath "Z:\" -PathType Container)) {
throw "Mapped Z: drive is not available."
}
$driveZ = "ok"
$share = ( $share = (
Get-Content ` Get-Content `
-LiteralPath "\\host.lan\Data\validation.token" ` -LiteralPath "\\host.lan\Data\validation.token" `
@@ -423,6 +438,7 @@ jobs:
version = [string]$windows.Version version = [string]$windows.Version
build = [string]$windows.BuildNumber build = [string]$windows.BuildNumber
platform = $platform platform = $platform
drive_z = $driveZ
oem_file = $oemFile oem_file = $oemFile
share = $share share = $share
share_write = "ok" share_write = "ok"
@@ -582,6 +598,15 @@ jobs:
TestSharedFolder = "ok" TestSharedFolder = "ok"
End Function End Function
Function TestMappedDrive(filesystem)
If Not filesystem.DriveExists("Z:") Then
Err.Raise vbObjectError + 4, "TestMappedDrive", _
"Mapped Z: drive is not available."
End If
TestMappedDrive = "ok"
End Function
Function TestInternet() Function TestInternet()
Dim request Dim request
Dim response Dim response
@@ -639,6 +664,7 @@ jobs:
Dim oemFile Dim oemFile
Dim share Dim share
Dim shareWrite Dim shareWrite
Dim driveZ
Dim internet Dim internet
Dim json Dim json
@@ -677,6 +703,7 @@ jobs:
oemFile = ReadTextFile(filesystem, "C:\OEM\validation.token") oemFile = ReadTextFile(filesystem, "C:\OEM\validation.token")
share = ReadTextFile(filesystem, "\\host.lan\Data\validation.token") share = ReadTextFile(filesystem, "\\host.lan\Data\validation.token")
shareWrite = TestSharedFolder(filesystem, token) shareWrite = TestSharedFolder(filesystem, token)
driveZ = TestMappedDrive(filesystem)
internet = TestInternet() internet = TestInternet()
End If End If
@@ -692,6 +719,7 @@ jobs:
"""oem_file"":""" & EscapeJson(oemFile) & """," & _ """oem_file"":""" & EscapeJson(oemFile) & """," & _
"""share"":""" & EscapeJson(share) & """," & _ """share"":""" & EscapeJson(share) & """," & _
"""share_write"":""" & EscapeJson(shareWrite) & """," & _ """share_write"":""" & EscapeJson(shareWrite) & """," & _
"""drive_z"":""" & EscapeJson(driveZ) & """," & _
"""internet"":""" & EscapeJson(internet) & """" & _ """internet"":""" & EscapeJson(internet) & """" & _
"}" "}"
@@ -764,6 +792,42 @@ jobs:
exit /B %result% exit /B %result%
EOF EOF
;; ;;
basic)
case "${VERSION,,}" in
95) share_host="Host" ;;
*) share_host="host.lan" ;;
esac
sed "s|@@SHARE_HOST@@|$share_host|g" > "$RUNNER_TEMP/oem/install.bat" <<'BATCH'
@echo off
:retry
if not exist \\@@SHARE_HOST@@\Data\validation.token goto wait
if not exist Z:\validation.token goto wait
echo TOKEN> C:\OEM\validation.result
type C:\OEM\validation.token >> C:\OEM\validation.result
echo SHARE>> C:\OEM\validation.result
type \\@@SHARE_HOST@@\Data\validation.token >> C:\OEM\validation.result
echo DRIVE_Z>> C:\OEM\validation.result
echo ok>> C:\OEM\validation.result
echo VERSION>> C:\OEM\validation.result
ver >> C:\OEM\validation.result
if exist \\@@SHARE_HOST@@\Data\validation.result del \\@@SHARE_HOST@@\Data\validation.result >nul
copy C:\OEM\validation.result \\@@SHARE_HOST@@\Data\validation.result >nul
if errorlevel 1 goto wait
goto end
:wait
ping 127.0.0.1 -n 6 >nul
goto retry
:end
BATCH
;;
esac esac
echo "token=$token" >> "$GITHUB_OUTPUT" echo "token=$token" >> "$GITHUB_OUTPUT"
@@ -890,6 +954,9 @@ jobs:
- name: Install and validate Windows - name: Install and validate Windows
shell: bash shell: bash
env: env:
CALLBACK: ${{ inputs.callback }}
ENABLE_TUNNEL: ${{ inputs.enable_tunnel }}
TUNNEL_DELAY: ${{ inputs.tunnel_delay }}
KILL_ON_FAILURE: ${{ inputs.kill_on_failure }} KILL_ON_FAILURE: ${{ inputs.kill_on_failure }}
INSTALL_TIMEOUT: ${{ inputs.install_timeout }} INSTALL_TIMEOUT: ${{ inputs.install_timeout }}
REBOOT_TIMEOUT: ${{ inputs.reboot_timeout }} REBOOT_TIMEOUT: ${{ inputs.reboot_timeout }}
@@ -901,6 +968,7 @@ jobs:
EXPECTED_PLATFORM: ${{ inputs.platform }} EXPECTED_PLATFORM: ${{ inputs.platform }}
MINIMUM_BUILD: ${{ inputs.minimum_build }} MINIMUM_BUILD: ${{ inputs.minimum_build }}
DISPLAY_NAME: ${{ inputs.name }} DISPLAY_NAME: ${{ inputs.name }}
VERSION: ${{ inputs.version }}
run: | run: |
set -Eeuo pipefail set -Eeuo pipefail
@@ -954,8 +1022,82 @@ jobs:
) & ) &
guest_logs_pid="$!" guest_logs_pid="$!"
tunnel_delay_pid=""
start_delayed_tunnel() {
local state
local novnc_url
sleep "$TUNNEL_DELAY"
state="$(
docker inspect \
--format '{{.State.Status}}' \
"$CONTAINER" 2>/dev/null || true
)"
if [[ "$state" != "running" ]]; then
return 0
fi
echo
echo "------------------------------------------------------------"
echo "Installation is still running after $((TUNNEL_DELAY / 60)) minutes."
echo "Starting temporary noVNC tunnel..."
if docker run --detach \
--name "$TUNNEL" \
--network "container:$CONTAINER" \
cloudflare/cloudflared:latest \
tunnel \
--no-autoupdate \
--url http://127.0.0.1:8006 > /dev/null 2>/dev/null; then
novnc_url=""
for _ in {1..30}; do
novnc_url="$(
docker logs "$TUNNEL" 2>&1 |
grep -Eo 'https://[-a-z0-9]+\.trycloudflare\.com' |
tail -n 1 || true
)"
[ -n "$novnc_url" ] && break
sleep 2
done
if [ -n "$novnc_url" ]; then
echo
echo "::notice title=noVNC viewer::$novnc_url"
echo "noVNC viewer: $novnc_url"
echo "Warning: this temporary URL is publicly accessible."
{
echo "### noVNC viewer"
echo
echo "[$novnc_url]($novnc_url)"
echo
echo "> This temporary URL is publicly accessible while the job is running."
} >> "$GITHUB_STEP_SUMMARY"
else
echo "::warning::Failed to obtain a noVNC tunnel URL."
docker logs "$TUNNEL" 2>&1 || true
fi
else
echo "::warning::Failed to start the noVNC tunnel."
fi
}
if [[ "$ENABLE_TUNNEL" != "true" ]] && (( TUNNEL_DELAY > 0 )); then
start_delayed_tunnel &
tunnel_delay_pid="$!"
fi
stop_logs() { stop_logs() {
if [[ -n "$tunnel_delay_pid" ]]; then
kill "$tunnel_delay_pid" 2>/dev/null || true
wait "$tunnel_delay_pid" 2>/dev/null || true
fi
kill "$logs_pid" "$guest_logs_pid" 2>/dev/null || true kill "$logs_pid" "$guest_logs_pid" 2>/dev/null || true
wait "$logs_pid" "$guest_logs_pid" 2>/dev/null || true wait "$logs_pid" "$guest_logs_pid" 2>/dev/null || true
print_guest_log print_guest_log
@@ -975,18 +1117,68 @@ jobs:
echo echo
echo "Automatic termination is disabled by kill_on_failure." echo "Automatic termination is disabled by kill_on_failure."
echo "The container and noVNC tunnel will remain available." echo "The container will remain available for debugging."
echo "::warning title=Failure detected::$message" echo "::warning title=Failure detected::$message"
failure_checks_disabled=1 failure_checks_disabled=1
return 0 return 0
} }
check_rdp_port() {
local container_ip
local attempt
container_ip="$(
docker inspect \
--format '{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' \
"$CONTAINER" 2>/dev/null |
head -n 1 || true
)"
if [[ -z "$container_ip" ]]; then
echo "Could not determine the Windows container IP address."
return 1
fi
echo
echo "Checking TCP port 3389 at $container_ip..."
for attempt in {1..30}; do
if timeout 2 bash -c \
"exec 3<>/dev/tcp/$container_ip/3389; exec 3>&-; exec 3<&-" \
2>/dev/null; then
echo "TCP port 3389 is open."
return 0
fi
sleep 2
done
case "${VERSION,,}" in
95 | 98 | me)
echo "::warning::TCP port 3389 is not open for $DISPLAY_NAME; this is allowed."
return 0
;;
esac
echo "TCP port 3389 is not open for $DISPLAY_NAME."
return 1
}
deadline=$((SECONDS + INSTALL_TIMEOUT)) deadline=$((SECONDS + INSTALL_TIMEOUT))
reboot_timeout="$REBOOT_TIMEOUT" reboot_timeout="$REBOOT_TIMEOUT"
minimum_reboots="$MINIMUM_REBOOTS" minimum_reboots="$MINIMUM_REBOOTS"
boot_loop_limit="$BOOT_LOOP_LIMIT" boot_loop_limit="$BOOT_LOOP_LIMIT"
first_bios_start=-1 first_bios_start=-1
version_lower="${VERSION,,}"
case "$version_lower" in
95 | 98 | me)
reboot_timeout=$((REBOOT_TIMEOUT * 2))
echo "Extended first reboot timeout to $((reboot_timeout / 60)) minutes for $DISPLAY_NAME."
;;
esac
while (( SECONDS < deadline )); do while (( SECONDS < deadline )); do
state="$( state="$(
docker inspect \ docker inspect \
@@ -1023,7 +1215,7 @@ jobs:
fi fi
echo "Automatic termination is disabled by kill_on_failure." echo "Automatic termination is disabled by kill_on_failure."
echo "The container and noVNC tunnel will remain available." echo "The container will remain available for debugging."
echo "::warning title=Failure detected::The guest validation callback failed." echo "::warning title=Failure detected::The guest validation callback failed."
failure_checks_disabled=1 failure_checks_disabled=1
fi fi
@@ -1108,7 +1300,8 @@ jobs:
fi fi
fi fi
if (( bios_starts >= boot_loop_limit )) && if (( failure_checks_disabled == 0 &&
bios_starts >= boot_loop_limit )) &&
(( hard_disk_boots >= boot_loop_limit || (( hard_disk_boots >= boot_loop_limit ||
dvd_boots >= boot_loop_limit || dvd_boots >= boot_loop_limit ||
not_bootable_disk >= boot_loop_limit || not_bootable_disk >= boot_loop_limit ||
@@ -1127,8 +1320,119 @@ jobs:
echo "No-bootable-device failures: $no_bootable_device" echo "No-bootable-device failures: $no_bootable_device"
echo "BOOTMGR failures: $bootmgr_missing" echo "BOOTMGR failures: $bootmgr_missing"
if ! handle_failure "Detected a repeated BIOS boot loop."; then
exit 1 exit 1
fi fi
fi
basic_result="$RUNNER_TEMP/data/validation.result"
if [[ "$CALLBACK" == "basic" && -s "$basic_result" ]]; then
basic_response="$(tr -d '\r' < "$basic_result")"
oem_file="$(
awk '$0 == "TOKEN" { getline; print; exit }' <<< "$basic_response"
)"
share="$(
awk '$0 == "SHARE" { getline; print; exit }' <<< "$basic_response"
)"
drive_z="$(
awk '$0 == "DRIVE_Z" { getline; print; exit }' <<< "$basic_response"
)"
version="$(
awk '
$0 == "VERSION" { found = 1; next }
found && NF { print; exit }
' <<< "$basic_response"
)"
if [[ -z "$oem_file" || -z "$share" || -z "$drive_z" || -z "$version" ]] ||
! grep -Eq '[0-9]+\.[0-9]+\.[0-9]+' <<< "$version"; then
sleep 1
continue
fi
stop_logs
trap - EXIT
echo
echo "------------------------------------------------------------"
echo "Received response:"
printf '%s\n' "$basic_response"
if [[ "$oem_file" != "$EXPECTED_TOKEN" ]]; then
echo "Failed to read C:\\OEM\\validation.token."
echo "Expected contents:"
echo " $EXPECTED_TOKEN"
echo "Received:"
echo " ${oem_file:-empty}"
exit 1
fi
if [[ "$share" != "$EXPECTED_TOKEN" ]]; then
echo "Failed to read \\\\host.lan\\Data\\validation.token."
echo "Expected contents:"
echo " $EXPECTED_TOKEN"
echo "Received:"
echo " ${share:-empty}"
exit 1
fi
if [[ "$drive_z" != "ok" ]]; then
echo "The mapped Z: drive test did not succeed."
exit 1
fi
normalized_caption="${version//\(R\)/}"
normalized_expected_caption="${EXPECTED_CAPTION//\(R\)/}"
if [[ "$normalized_caption" != *"$normalized_expected_caption"* ]]; then
echo "Expected caption containing:"
echo " $EXPECTED_CAPTION"
echo "Received:"
echo " $version"
exit 1
fi
version_number="$(
grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' <<< "$version" |
head -n 1 || true
)"
if [[ ! "$version_number" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Unexpected Windows version: $version"
exit 1
fi
build="${version_number##*.}"
if [[ ! "$build" =~ ^[0-9]+$ ]]; then
echo "Invalid Windows build number: $build"
exit 1
fi
if (( build < MINIMUM_BUILD )); then
echo "Expected build $MINIMUM_BUILD or newer."
echo "Received build: $build"
exit 1
fi
if ! check_rdp_port; then
exit 1
fi
echo
echo "$DISPLAY_NAME installed successfully."
echo "Version: $version_number"
echo "Build: $build"
echo "OEM files: copied successfully"
echo "Shared folder: readable and writable"
echo "Mapped Z: drive: available"
exit 0
fi
response="$( response="$(
grep -F 'VALIDATION_RESULT=' <<< "$container_log" | grep -F 'VALIDATION_RESULT=' <<< "$container_log" |
@@ -1160,6 +1464,7 @@ jobs:
oem_file="$(jq -r '.oem_file // empty' <<< "$response")" oem_file="$(jq -r '.oem_file // empty' <<< "$response")"
share="$(jq -r '.share // empty' <<< "$response")" share="$(jq -r '.share // empty' <<< "$response")"
share_write="$(jq -r '.share_write // empty' <<< "$response")" share_write="$(jq -r '.share_write // empty' <<< "$response")"
drive_z="$(jq -r '.drive_z // empty' <<< "$response")"
internet="$(jq -r '.internet // empty' <<< "$response")" internet="$(jq -r '.internet // empty' <<< "$response")"
if [[ "$token" != "$EXPECTED_TOKEN" ]]; then if [[ "$token" != "$EXPECTED_TOKEN" ]]; then
@@ -1190,6 +1495,11 @@ jobs:
exit 1 exit 1
fi fi
if [[ "$drive_z" != "ok" ]]; then
echo "The mapped Z: drive test did not succeed."
exit 1
fi
if [[ "$internet" != "ok" ]]; then if [[ "$internet" != "ok" ]]; then
echo "The guest internet connection test did not succeed." echo "The guest internet connection test did not succeed."
exit 1 exit 1
@@ -1240,6 +1550,10 @@ jobs:
exit 1 exit 1
fi fi
if ! check_rdp_port; then
exit 1
fi
echo echo
echo "$DISPLAY_NAME installed successfully." echo "$DISPLAY_NAME installed successfully."
echo "Version: $version" echo "Version: $version"
@@ -1247,6 +1561,7 @@ jobs:
echo "Platform: $platform" echo "Platform: $platform"
echo "OEM files: copied successfully" echo "OEM files: copied successfully"
echo "Shared folder: readable and writable" echo "Shared folder: readable and writable"
echo "Mapped Z: drive: available"
echo "Internet connection: accessible" echo "Internet connection: accessible"
exit 0 exit 0
fi fi
+2 -1
View File
@@ -52,9 +52,10 @@ jobs:
version: ${{ inputs.version }} version: ${{ inputs.version }}
callback: ${{ inputs.callback }} callback: ${{ inputs.callback }}
enable_tunnel: false enable_tunnel: false
tunnel_delay: 1800
kill_on_failure: true kill_on_failure: true
install_timeout: 9000 install_timeout: 9000
reboot_timeout: 1800 reboot_timeout: 18000
minimum_reboots: 1 minimum_reboots: 1
boot_loop_limit: 10 boot_loop_limit: 10
expected_caption: ${{ inputs.expected_caption }} expected_caption: ${{ inputs.expected_caption }}
+33
View File
@@ -206,6 +206,39 @@ jobs:
minimum_build: ${{ matrix.minimum_build }} minimum_build: ${{ matrix.minimum_build }}
platform: ${{ matrix.platform }} platform: ${{ matrix.platform }}
win9x:
strategy:
fail-fast: false
matrix:
include:
- name: Windows 95
version: "95"
expected_caption: Windows 95
minimum_build: 950
platform: x86
- name: Windows 98
version: "98"
expected_caption: Windows 98
minimum_build: 2222
platform: x86
- name: Windows ME
version: "me"
expected_caption: Windows Millennium
minimum_build: 3000
platform: x86
name: ${{ matrix.name }}
uses: ./.github/workflows/settings.yml
with:
name: ${{ matrix.name }}
version: ${{ matrix.version }}
callback: basic
expected_caption: ${{ matrix.expected_caption }}
minimum_build: ${{ matrix.minimum_build }}
platform: ${{ matrix.platform }}
tiny: tiny:
strategy: strategy:
fail-fast: false fail-fast: false
+5 -7
View File
@@ -3,13 +3,13 @@
ARG VERSION_ARG="latest" ARG VERSION_ARG="latest"
FROM scratch AS build-amd64 FROM scratch AS build-amd64
COPY --from=qemux/qemu:7.44 / / COPY --from=qemux/qemu:7.48 / /
ARG TARGETARCH ARG TARGETARCH
ARG VERSION_UDF="1.2.0" ARG VERSION_UDF="1.2.0"
ARG VERSION_WSDD="1.26" ARG VERSION_WSDD="1.27"
ARG VERSION_VIRTIO="1.9.58" ARG VERSION_VIRTIO="1.9.60"
ARG VERSION_BLINTER="1.0.112" ARG VERSION_BLINTER="1.0.112"
ARG DEBCONF_NOWARNINGS="yes" ARG DEBCONF_NOWARNINGS="yes"
@@ -21,6 +21,7 @@ RUN <<EOF
apt-get update apt-get update
apt-get --no-install-recommends -y install \ apt-get --no-install-recommends -y install \
gcab \
samba \ samba \
mtools \ mtools \
wimtools \ wimtools \
@@ -32,10 +33,7 @@ RUN <<EOF
libarchive-tools libarchive-tools
# Install Blinter # Install Blinter
python3 -m pip install \ python3 -m pip install --break-system-packages --root-user-action=ignore --no-cache-dir "Blinter==${VERSION_BLINTER}"
--break-system-packages \
--no-cache-dir \
"Blinter==${VERSION_BLINTER}"
# Install wsddn package # Install wsddn package
wget "https://github.com/gershnik/wsdd-native/releases/download/v${VERSION_WSDD}/wsddn_${VERSION_WSDD}_${TARGETARCH}.deb" -O /tmp/wsddn.deb -q --timeout=10 wget "https://github.com/gershnik/wsdd-native/releases/download/v${VERSION_WSDD}/wsddn_${VERSION_WSDD}_${TARGETARCH}.deb" -O /tmp/wsddn.deb -q --timeout=10
+3
View File
@@ -15,6 +15,9 @@ if exist "%SETUP_COMPLETE%" exit /b 0
type nul > "%SETUP_STARTED%" type nul > "%SETUP_STARTED%"
rem Disable Shutdown Event Tracker.
reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" /v "ShutdownReasonOn" /t REG_DWORD /d 0 /f
rem Allow guest access to network shares. rem Allow guest access to network shares.
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f
+3
View File
@@ -15,6 +15,9 @@ if exist "%SETUP_COMPLETE%" exit /b 0
type nul > "%SETUP_STARTED%" type nul > "%SETUP_STARTED%"
rem Disable Shutdown Event Tracker.
reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" /v "ShutdownReasonOn" /t REG_DWORD /d 0 /f
rem Allow guest access to network shares. rem Allow guest access to network shares.
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f
+3
View File
@@ -15,6 +15,9 @@ if exist "%SETUP_COMPLETE%" exit /b 0
type nul > "%SETUP_STARTED%" type nul > "%SETUP_STARTED%"
rem Disable Shutdown Event Tracker.
reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" /v "ShutdownReasonOn" /t REG_DWORD /d 0 /f
rem Allow guest access to network shares. rem Allow guest access to network shares.
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f
+3
View File
@@ -15,6 +15,9 @@ if exist "%SETUP_COMPLETE%" exit /b 0
type nul > "%SETUP_STARTED%" type nul > "%SETUP_STARTED%"
rem Disable Shutdown Event Tracker.
reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" /v "ShutdownReasonOn" /t REG_DWORD /d 0 /f
rem Allow guest access to network shares. rem Allow guest access to network shares.
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f
+3
View File
@@ -15,6 +15,9 @@ if exist "%SETUP_COMPLETE%" exit /b 0
type nul > "%SETUP_STARTED%" type nul > "%SETUP_STARTED%"
rem Disable Shutdown Event Tracker.
reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" /v "ShutdownReasonOn" /t REG_DWORD /d 0 /f
rem Allow guest access to network shares. rem Allow guest access to network shares.
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f
+3
View File
@@ -15,6 +15,9 @@ if exist "%SETUP_COMPLETE%" exit /b 0
type nul > "%SETUP_STARTED%" type nul > "%SETUP_STARTED%"
rem Disable Shutdown Event Tracker.
reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" /v "ShutdownReasonOn" /t REG_DWORD /d 0 /f
rem Allow guest access to network shares. rem Allow guest access to network shares.
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f
+3
View File
@@ -15,6 +15,9 @@ if exist "%SETUP_COMPLETE%" exit /b 0
type nul > "%SETUP_STARTED%" type nul > "%SETUP_STARTED%"
rem Disable Shutdown Event Tracker.
reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" /v "ShutdownReasonOn" /t REG_DWORD /d 0 /f
rem Allow guest access to network shares. rem Allow guest access to network shares.
reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f
+4
View File
@@ -59,6 +59,10 @@ netsh advfirewall firewall set rule group="@FirewallAPI.dll,-32752" new enable=Y
rem Enable File Sharing. rem Enable File Sharing.
netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes
rem Install the VirtIO Balloon service once.
sc.exe query BalloonService >nul 2>&1
if errorlevel 1 "%SystemRoot%\Drivers\Balloon\blnsvr.exe" -i
rem BEGIN PRODUCT_KEY rem BEGIN PRODUCT_KEY
rem Install the product key without activating Windows immediately. rem Install the product key without activating Windows immediately.
cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX" cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX"
+4
View File
@@ -59,6 +59,10 @@ netsh advfirewall firewall set rule group="@FirewallAPI.dll,-32752" new enable=Y
rem Enable File Sharing. rem Enable File Sharing.
netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes
rem Install the VirtIO Balloon service once.
sc.exe query BalloonService >nul 2>&1
if errorlevel 1 "%SystemRoot%\Drivers\Balloon\blnsvr.exe" -i
rem BEGIN PRODUCT_KEY rem BEGIN PRODUCT_KEY
rem Install the product key without activating Windows immediately. rem Install the product key without activating Windows immediately.
cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX" cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX"
+4
View File
@@ -59,6 +59,10 @@ netsh advfirewall firewall set rule group="@FirewallAPI.dll,-32752" new enable=Y
rem Enable File Sharing. rem Enable File Sharing.
netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes
rem Install the VirtIO Balloon service once.
sc.exe query BalloonService >nul 2>&1
if errorlevel 1 "%SystemRoot%\Drivers\Balloon\blnsvr.exe" -i
rem BEGIN PRODUCT_KEY rem BEGIN PRODUCT_KEY
rem Install the product key without activating Windows immediately. rem Install the product key without activating Windows immediately.
cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX" cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX"
+4
View File
@@ -71,6 +71,10 @@ netsh advfirewall firewall set rule group="@FirewallAPI.dll,-32752" new enable=Y
rem Enable File Sharing. rem Enable File Sharing.
netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes
rem Install the VirtIO Balloon service once.
sc.exe query BalloonService >nul 2>&1
if errorlevel 1 "%SystemRoot%\Drivers\Balloon\blnsvr.exe" -i
rem BEGIN PRODUCT_KEY rem BEGIN PRODUCT_KEY
rem Install the product key without activating Windows immediately. rem Install the product key without activating Windows immediately.
cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX" cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX"
+4
View File
@@ -71,6 +71,10 @@ netsh advfirewall firewall set rule group="@FirewallAPI.dll,-32752" new enable=Y
rem Enable File Sharing. rem Enable File Sharing.
netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes
rem Install the VirtIO Balloon service once.
sc.exe query BalloonService >nul 2>&1
if errorlevel 1 "%SystemRoot%\Drivers\Balloon\blnsvr.exe" -i
rem BEGIN PRODUCT_KEY rem BEGIN PRODUCT_KEY
rem Install the product key without activating Windows immediately. rem Install the product key without activating Windows immediately.
cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX" cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX"
+4
View File
@@ -71,6 +71,10 @@ netsh advfirewall firewall set rule group="@FirewallAPI.dll,-32752" new enable=Y
rem Enable File Sharing. rem Enable File Sharing.
netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes
rem Install the VirtIO Balloon service once.
sc.exe query BalloonService >nul 2>&1
if errorlevel 1 "%SystemRoot%\Drivers\Balloon\blnsvr.exe" -i
rem BEGIN PRODUCT_KEY rem BEGIN PRODUCT_KEY
rem Install the product key without activating Windows immediately. rem Install the product key without activating Windows immediately.
cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX" cscript.exe //B //Nologo "%SystemRoot%\System32\slmgr.vbs" /ipk "XXX"
+9 -6
View File
@@ -41,7 +41,7 @@ An empty default means the variable is unset and its value is determined automat
|---|---|---| |---|---|---|
| `DISK_SIZE` | `64G` | Size of the primary disk. | | `DISK_SIZE` | `64G` | Size of the primary disk. |
| `DISK_FMT` | `raw` | Disk image format: `raw` or `qcow2`. | | `DISK_FMT` | `raw` | Disk image format: `raw` or `qcow2`. |
| `DISK_TYPE` | `scsi` | Disk device type, such as `sata`, `scsi`, `nvme`, or `blk`. | | `DISK_TYPE` | `scsi` | Disk device type, such as `ide`, `scsi`, or `blk`. |
| `DISK_CACHE` | `none` | Disk cache mode, such as `none` or `writeback`. | | `DISK_CACHE` | `none` | Disk cache mode, such as `none` or `writeback`. |
| `DISK_IO` | `native` | Disk I/O mode, such as `native`, `threads`, or `io_uring`. | | `DISK_IO` | `native` | Disk I/O mode, such as `native`, `threads`, or `io_uring`. |
| `DISK_DISCARD` | `unmap` | Discard/TRIM mode for the primary disk. | | `DISK_DISCARD` | `unmap` | Discard/TRIM mode for the primary disk. |
@@ -79,11 +79,10 @@ An empty default means the variable is unset and its value is determined automat
| Variable | Default | Description | | Variable | Default | Description |
|---|---|---| |---|---|---|
| `DISPLAY` | `web` | Display backend, such as `web`, `vnc`, `disabled`, or `none`. | | `DISPLAY` | `web` | Display backend, such as `web`, `vnc`, `disabled`, or `none`. |
| `LOSSY` | `N` | Enables lossy VNC compression to reduce bandwidth usage. |
| `VGA` | `virtio` | QEMU video adapter model. | | `VGA` | `virtio` | QEMU video adapter model. |
| `WIDTH` | `1280` | Display width configured in Windows. | | `WIDTH` | `1280` | Display width configured in Windows. |
| `HEIGHT` | `720` | Display height configured in Windows. | | `HEIGHT` | `720` | Display height configured in Windows. |
| `GPU` | `N` | Enables experimental Intel iGPU acceleration. | | `GPU` | `N` | Enables experimental GPU acceleration. |
| `RENDERNODE` | `/dev/dri/renderD128` | Render node used for GPU acceleration. | | `RENDERNODE` | `/dev/dri/renderD128` | Render node used for GPU acceleration. |
## 🌍 Web UI ## 🌍 Web UI
@@ -93,8 +92,8 @@ An empty default means the variable is unset and its value is determined automat
| `WEB` | `Y` | Enables the web interface. | | `WEB` | `Y` | Enables the web interface. |
| `WEB_PORT` | `8006` | Port for the web interface. | | `WEB_PORT` | `8006` | Port for the web interface. |
| `VNC_PORT` | `5900` | Port for the VNC server. | | `VNC_PORT` | `5900` | Port for the VNC server. |
| `LOSSY` | `N` | Enables lossy VNC compression to reduce bandwidth usage. |
| `AUDIO` | `N` | Streams guest audio to the web viewer. | | `AUDIO` | `N` | Streams guest audio to the web viewer. |
| `SOUND` | `intel-hda` | QEMU audio device used by the web viewer. |
| `PROTECT` | `N` | Enables password protection for the web interface. | | `PROTECT` | `N` | Enables password protection for the web interface. |
## 📁 File Sharing ## 📁 File Sharing
@@ -112,9 +111,12 @@ An empty default means the variable is unset and its value is determined automat
|---|---|---| |---|---|---|
| `MACHINE` | `q35` | QEMU machine type. | | `MACHINE` | `q35` | QEMU machine type. |
| `PCI_BUS` | `pcie.0` | Overrides the PCI bus used for attached devices. | | `PCI_BUS` | `pcie.0` | Overrides the PCI bus used for attached devices. |
| `RNG` | `Y` | Adds the Virtio RNG device to the machine. |
| `UUID` | | UUID assigned to Windows. | | `UUID` | | UUID assigned to Windows. |
| `HPET` | `off` | QEMU HPET timer setting. | | `HPET` | `off` | HPET timer setting. |
| `VMPORT` | `off` | QEMU VMware port setting. | | `VMPORT` | `off` | VMware port setting. |
| `MOUSE` | `usb-tablet` | Pointing device used by the machine. |
| `SOUND` | `intel-hda` | Audio device used when `AUDIO=Y`. |
| `SM_BIOS` | | Additional arguments passed to QEMUs `-smbios` option. | | `SM_BIOS` | | Additional arguments passed to QEMUs `-smbios` option. |
| `ARGUMENTS` | | Additional raw arguments appended to the QEMU command line. | | `ARGUMENTS` | | Additional raw arguments appended to the QEMU command line. |
@@ -177,3 +179,4 @@ Also see [Dynamic memory allocation](https://github.com/qemus/qemu/blob/master/d
| `LOG` | `N` | Saves all output from `install.bat` to `C:\OEM\install.log` for troubleshooting. | | `LOG` | `N` | Saves all output from `install.bat` to `C:\OEM\install.log` for troubleshooting. |
| `SERIAL` | `mon:stdio` | QEMU serial device configuration. | | `SERIAL` | `mon:stdio` | QEMU serial device configuration. |
| `MONITOR` | | QEMU monitor configuration. | | `MONITOR` | | QEMU monitor configuration. |
| `QMP` | | QEMU Machine Protocol configuration. |
+3
View File
@@ -125,6 +125,9 @@ For a complete graphical desktop experience, see [WinBoat](https://winboat.app),
| `vu` | Windows Vista Ultimate | 3.0 GB | | `vu` | Windows Vista Ultimate | 3.0 GB |
| `xp` | Windows XP Professional | 0.6 GB | | `xp` | Windows XP Professional | 0.6 GB |
| `2k` | Windows 2000 Professional | 0.4 GB | | `2k` | Windows 2000 Professional | 0.4 GB |
| `me` | Windows ME | 0.5 GB |
| `98` | Windows 98 | 0.7 GB |
| `95` | Windows 95 | 0.6 GB |
|||| ||||
| `2025` | Windows Server 2025 | 7.6 GB | | `2025` | Windows Server 2025 | 7.6 GB |
| `2022` | Windows Server 2022 | 6.0 GB | | `2022` | Windows Server 2022 | 6.0 GB |
+227 -889
View File
File diff suppressed because it is too large Load Diff
+3259
View File
File diff suppressed because it is too large Load Diff
+954 -284
View File
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -13,9 +13,13 @@ cd /run
. init.sh # Initialize system . init.sh # Initialize system
. memory.sh # Check memory . memory.sh # Check memory
. server.sh # Start webserver . server.sh # Start webserver
. download.sh # Load functions
. define.sh # Define versions . define.sh # Define versions
. mido.sh # Download Windows . mido.sh # Download Windows
. answer.sh # Setup answer files . answer.sh # Modern unattended
. batch.sh # Win9x unattended
. sif.sh # NT5 unattended
. legacy.sh # Legacy installs
. image.sh # Detect image files . image.sh # Detect image files
. install.sh # Run installation . install.sh # Run installation
. disk.sh # Initialize disks . disk.sh # Initialize disks
+308 -431
View File
File diff suppressed because it is too large Load Diff
+1351 -1085
View File
File diff suppressed because it is too large Load Diff
+369
View File
@@ -0,0 +1,369 @@
#!/usr/bin/env bash
set -Eeuo pipefail
setMachine() {
local id="$1"
local iso="$2"
local dir="$3"
local desc="$4"
if isLegacy "$id"; then
if ! legacyInstall "$id" "$iso" "$dir" "$desc"; then
error "Failed to prepare $desc ISO!"
return 1
fi
writeState "mode" "windows_legacy" || return 1
case "${id,,}" in
"win9"* | "winnt4" | "win2k"* | "reactos" )
writeState "old" "pc" || return 1
writeState "type" "auto" || return 1 ;;
esac
case "${id,,}" in
"winnt4" )
writeState "vga" "cirrus" || return 1 ;;
*) writeState "vga" "std" || return 1 ;;
esac
case "${id,,}" in
"win9"* | "winnt4" )
writeState "hv" "N" || return 1
writeState "usb" "N" || return 1
writeState "port" "on" || return 1
writeState "net" "pcnet" || return 1
writeState "sound" "AC97" || return 1 ;;
"win2k"* )
writeState "hv" "N" || return 1
writeState "net" "rtl8139" || return 1
writeState "usb" "pci-ohci" || return 1
writeState "sound" "usb-audio" || return 1 ;;
"winxpx"* | "win2003"* )
writeState "hv" "N" || return 1
writeState "type" "blk" || return 1
writeState "net" "rtl8139" || return 1
writeState "sound" "usb-audio" || return 1 ;;
"reactos" )
writeState "sound" "AC97" || return 1
writeState "net" "rtl8139" || return 1
writeState "usb" "pci-ohci" || return 1
if isReactOSLiveCD "$iso"; then
SYSTEM="$iso"
createMarker "kill" || return 1
fi ;;
esac
fi
restoreMachine || return 1
restoreBootMode || return 1
case "${id,,}" in
"win95" | "winnt4" )
# Windows 95 does not support ACPI so disable graceful shutdown
createMarker "kill" || return 1 ;;
esac
case "${id,,}" in
"win9"* | "winnt4" | "win2k"* | *"x86"* )
# Legacy 32-bit Windows may enter an incompatible PAE/DEP path when the
# NX flag is exposed, causing installation failures or repeated resets.
#
# Long mode is unusable by 32-bit guests and may cause the firmware to
# allocate PCI resources above 4 GB, which older systems may not handle.
writeState "flag" "nx=off,lm=off" || return 1 ;;
esac
case "${id,,}" in
"win9"* | "winnt4" | "win2k"* | "winxp"* | "win2003"* | \
"winvistax86"* | "win7x86"* | "reactos" )
if isQ35 "$MACHINE"; then
# pc-q35-2.11 began advertising a synthetic 64-bit PCI MMIO aperture.
# Older Windows ACPI implementations may reject that resource layout,
# so retain the pre-2.11 behavior for these guests to prevent a
# blue screen on XP and others if the 64 bit PCI hole size is >2G.
writeState "args" "-global q35-pcihost.x-pci-hole64-fix=false" || return 1
fi ;;
esac
return 0
}
restoreMachine() {
isPlatform "x64" || return 0
# Restore the saved machine only when q35 is still the default;
# an explicit user-selected machine must remain untouched.
[[ "${MACHINE,,}" != "q35" ]] && return 0
MACHINE=""
restoreState "MACHINE" "old" || return 1
# Migrate existing Win9x installs to QEMU 11
if [[ "${MACHINE,,}" == "pc-i440fx-2.4" ]]; then
MACHINE="pc"
writeState "old" "$MACHINE" || return 1
fi
[ -z "$MACHINE" ] && MACHINE="q35"
return 0
}
restoreMachineState() {
restoreState "HV" "hv" || return 1
restoreState "VGA" "vga" || return 1
restoreState "USB" "usb" || return 1
restoreState "SOUND" "sound" || return 1
restoreState "ADAPTER" "net" || return 1
restoreState "VMPORT" "port" || return 1
restoreState "CPU_MODEL" "cpu" || return 1
restoreState "DISK_TYPE" "type" || return 1
if [ -z "${BIOS:-}" ] && [ -s "$(stateFile "bios")" ]; then
BIOS="$(stateFile "bios")"
fi
mergeState "CPU_FLAGS" "flag" "," || return 1
mergeState "ARGUMENTS" "args" " " || return 1
return 0
}
restoreBootMode() {
local current="${BOOT_MODE:-}"
local mode
mode=$(readState "mode") || return 1
[ -n "$mode" ] || return 0
# A saved legacy mode always wins. A saved modern mode only replaces the
# default mode and never an explicit user-selected boot configuration.
if [[ "${mode,,}" == "windows_legacy" ]]; then
BOOT_MODE="$mode"
return 0
fi
case "${current,,}" in
"" | "windows" | "windows_plain" )
BOOT_MODE="$mode" ;;
esac
return 0
}
extractDrivers() {
local drivers="$1"
rm -rf "$drivers" || return 1
mkdir -p "$drivers" || return 1
if ! bsdtar -xf /var/drivers.txz -C "$drivers"; then
error "Failed to extract drivers!"
return 1
fi
return 0
}
legacyInstall() {
local id="$1"
local iso="$2"
local dir="$3"
local desc="$4"
case "${id,,}" in
"win9"* )
Win9xInstall "$id" "$iso" "$dir" "$desc" || return 1 ;;
"win2k"* )
SIFInstall "$iso" "$dir" "$desc" "2k" || return 1 ;;
"winxp"* )
SIFInstall "$iso" "$dir" "$desc" "xp" || return 1 ;;
"win2003"* )
SIFInstall "$iso" "$dir" "$desc" "2k3" || return 1 ;;
esac
return 0
}
isReactOSLiveCD() {
local iso="$1"
local files
files=$(bsdtar -tf "$iso" 2>/dev/null) || return 1
! grep -Ei '(^|/)reactos/txtsetup\.sif(;1)?$' <<< "$files" >/dev/null
}
detectReactOS() {
local dir="$1"
local marker
marker=$(find "$dir" -maxdepth 2 -type f \
\( -ipath '*/reactos/reactos.inf' -o -ipath '*/reactos/unattend.inf' \) -print -quit) || return 2
[ -n "$marker" ] || return 1
DETECTED="reactos"
return 0
}
detectLegacy() {
local dir="$1"
local marker
isPlatform "x64" || return 1
# Legacy media is identified from setup marker files rather than WIM
# metadata. The order is intentional because several releases share markers.
marker=$(find "$dir" -maxdepth 1 -type d -iname 'ia64' -print -quit) || return 2
if [ -n "$marker" ]; then
error "Windows IA-64 (Itanium) images are not supported by this container!"
return 2
fi
marker=$(find "$dir" -maxdepth 1 -type d -iname WIN95 -print -quit) || return 2
if [ -n "$marker" ]; then
DETECTED="win95"
return 0
fi
marker=$(find "$dir" -maxdepth 1 -type d -iname WIN98 -print -quit) || return 2
if [ -n "$marker" ]; then
DETECTED="win98"
return 0
fi
marker=$(find "$dir" -maxdepth 1 -type d -iname WIN9X -print -quit) || return 2
if [ -n "$marker" ]; then
DETECTED="win9x"
return 0
fi
marker=$(find "$dir" -maxdepth 1 -type f \
\( \
-iname CDROM_W.40 -o \
-iname CDROM_S.40 -o \
-iname CDROM_TS.40 \
\) \
-print -quit) || return 2
if [ -n "$marker" ]; then
DETECTED="winnt4"
return 0
fi
marker=$(find "$dir" -maxdepth 1 -type f -iname CDROM_NT.5 -print -quit) || return 2
if [ -n "$marker" ]; then
marker=$(find "$dir" -maxdepth 1 -type f \
\( \
-iname CDROM_IA.5 -o \
-iname CDROM_ID.5 -o \
-iname CDROM_IP.5 -o \
-iname CDROM_IS.5 \
\) \
-print -quit) || return 2
if [ -n "$marker" ]; then
DETECTED="win2k"
return 0
fi
fi
# WIN51 identifies the NT 5.1/5.2 media family; the companion marker then
# distinguishes XP x86, XP x64, and Server 2003.
marker=$(find "$dir" -maxdepth 1 -iname WIN51 -print -quit) || return 2
[ -n "$marker" ] || return 1
marker=$(find "$dir" -maxdepth 1 -type f -iname WIN51AP -print -quit) || return 2
if [ -n "$marker" ]; then
DETECTED="winxpx64"
return 0
fi
marker=$(find "$dir" -maxdepth 1 -type f \
\( \
-iname WIN51IC -o \
-iname WIN51IP -o \
-iname setupxp.htm \
\) \
-print -quit) || return 2
if [ -n "$marker" ]; then
DETECTED="winxpx86"
return 0
fi
marker=$(find "$dir" -maxdepth 1 -type f \
\( \
-iname WIN51IS -o \
-iname WIN51IA -o \
-iname WIN51IB -o \
-iname WIN51ID -o \
-iname WIN51IL -o \
-iname WIN51AA -o \
-iname WIN51AD -o \
-iname WIN51AS -o \
-iname WIN51MA -o \
-iname WIN51MD -o \
-iname WIN51MP \
\) \
-print -quit) || return 2
if [ -n "$marker" ]; then
DETECTED="win2003r2"
return 0
fi
return 1
}
return 0
+40 -27
View File
@@ -7,8 +7,6 @@ handleCurlError() {
local server="$2" local server="$2"
local reason="${3:-}" local reason="${3:-}"
local signal
if [ -n "$reason" ] && (( code <= 125 )); then if [ -n "$reason" ] && (( code <= 125 )); then
error "Request to $server servers failed: ${reason%.}." error "Request to $server servers failed: ${reason%.}."
return 1 return 1
@@ -23,6 +21,7 @@ handleCurlError() {
return 1 return 1
fi fi
local signal
signal=$(kill -l "$((code - 128))" 2>/dev/null || true) signal=$(kill -l "$((code - 128))" 2>/dev/null || true)
case "$signal" in case "$signal" in
@@ -72,10 +71,7 @@ curlRequest() {
rm -f "$log" rm -f "$log"
handleCurlError "$rc" "$server" "$reason" || : handleCurlError "$rc" "$server" "$reason" || :
if (( rc >= 129 )); then (( rc >= 129 )) && return "$rc"
return "$rc"
fi
return 1 return 1
fi fi
@@ -179,13 +175,16 @@ downloadWindowsLink() {
--max-filesize 100K \ --max-filesize 100K \
-- "$skuUrl") || return -- "$skuUrl") || return
# Let jq parsing failures propagate so malformed API data is not mistaken skuId=$(printf '%s\n' "$skuJson" | jq --arg LANG "$language" -r 'first(.Skus[]? | select(.Language == $LANG) | .Id) // empty') 2>/dev/null || skuId=""
# for a normal missing-result response. The same applies to the link data.
skuId=$(printf '%s\n' "$skuJson" | jq --arg LANG "$language" -r 'first(.Skus[]? | select(.Language == $LANG) | .Id) // empty') 2>/dev/null || return
if [ -z "$skuId" ] || [[ "${skuId,,}" == "null" ]]; then if [ -z "$skuId" ] || [[ "${skuId,,}" == "null" ]]; then
if [[ "${lang,,}" != "en" && "${lang,,}" != "en-"* ]]; then
language=$(getLanguage "$lang" "desc") language=$(getLanguage "$lang" "desc")
error "No download in the $language language available for $desc!" error "No download in the $language language available for $desc!"
else
error "Microsoft server provided us no SKU ID in response to our request!"
info "Response: $skuJson"
fi
return 1 return 1
fi fi
@@ -218,10 +217,10 @@ downloadWindowsLink() {
return 1 return 1
fi fi
link=$(printf '%s\n' "$linkJson" | jq --argjson TYPE "$type" -r 'first(.ProductDownloadOptions[]? | select(.DownloadType == $TYPE) | .Uri) // empty') 2>/dev/null || return link=$(printf '%s\n' "$linkJson" | jq --argjson TYPE "$type" -r 'first(.ProductDownloadOptions[]? | select(.DownloadType == $TYPE) | .Uri) // empty') 2>/dev/null || link=""
if [ -z "$link" ] || [[ "${link,,}" == "null" ]]; then if [ -z "$link" ] || [[ "${link,,}" == "null" ]]; then
error "Microsoft server gave us no download link to our request for an automated download!" error "Microsoft server provided us no download link to our request for an automated download!"
info "Response: $linkJson" info "Response: $linkJson"
return 1 return 1
fi fi
@@ -291,7 +290,7 @@ downloadWindows() {
grep -Eio "<option[^>]*value=[\"'][0-9]+[\"'][^>]*>[[:space:]]*Windows[^<]*" | grep -Eio "<option[^>]*value=[\"'][0-9]+[\"'][^>]*>[[:space:]]*Windows[^<]*" |
sed -nE "s/.*value=[\"']([0-9]+)[\"'].*/\1/p" | sed -nE "s/.*value=[\"']([0-9]+)[\"'].*/\1/p" |
sed -n '1p' | sed -n '1p' |
cut -c 1-16) || return cut -c 1-16) || productId=""
enabled "$DEBUG" && echo "$productId" enabled "$DEBUG" && echo "$productId"
if [ -z "$productId" ]; then if [ -z "$productId" ]; then
@@ -321,7 +320,7 @@ downloadWindowsEval() {
type="iot" type="iot"
winVer="windows-11-iot-enterprise-ltsc-eval" ;; winVer="windows-11-iot-enterprise-ltsc-eval" ;;
"win11${PLATFORM,,}-enterprise-ltsc-eval" ) "win11${PLATFORM,,}-enterprise-ltsc-eval" )
if [[ "${PLATFORM,,}" == "arm64" ]]; then if isPlatform "arm64"; then
type="iot" type="iot"
winVer="windows-11-iot-enterprise-ltsc-eval" winVer="windows-11-iot-enterprise-ltsc-eval"
else else
@@ -579,11 +578,13 @@ getWindows() {
"win81${PLATFORM,,}"* | \ "win81${PLATFORM,,}"* | \
"win10${PLATFORM,,}-enterprise-ltsc-eval" | \ "win10${PLATFORM,,}-enterprise-ltsc-eval" | \
"win11${PLATFORM,,}-enterprise-iot-eval" ) "win11${PLATFORM,,}-enterprise-iot-eval" )
if [[ "${lang,,}" != "en" && "${lang,,}" != "en-"* ]]; then if [[ "${lang,,}" != "en" && "${lang,,}" != "en-"* ]]; then
error "No download in the $language language available for $edition!" error "No download in the $language language available for $edition!"
MIDO_URL="" MIDO_URL=""
return 1 return 1
fi ;; fi ;;
esac esac
# ARM64 downloads exist only for the explicitly supported Windows 11 # ARM64 downloads exist only for the explicitly supported Windows 11
@@ -593,24 +594,28 @@ getWindows() {
"win11${PLATFORM,,}" ) ;; "win11${PLATFORM,,}" ) ;;
"win11${PLATFORM,,}-enterprise"* ) ;; "win11${PLATFORM,,}-enterprise"* ) ;;
* ) * )
if [[ "${PLATFORM,,}" != "x64" ]]; then
if ! isPlatform "x64"; then
error "No download for the ${PLATFORM^^} platform available for $edition!" error "No download for the ${PLATFORM^^} platform available for $edition!"
MIDO_URL="" MIDO_URL=""
return 1 return 1
fi ;; fi ;;
esac esac
# Prefer live Microsoft download routes. Unsupported or failed live routes # Prefer live Microsoft download routes. Unsupported or failed live routes
# fall through to the configured static catalog below. # fall through to the configured static catalog below.
case "${version,,}" in case "${version,,}" in
"win10x64" | "win11${PLATFORM,,}" ) "win10x64" | "win11${PLATFORM,,}" )
if downloadWindows "$version" "$lang" "$edition"; then if downloadWindows "$version" "$lang" "$edition"; then
return 0 return 0
else else
rc=$? rc=$?
(( rc == 1 )) || return "$rc" fi
fi ;;
(( rc == 1 )) || return "$rc" ;;
"win11${PLATFORM,,}-enterprise"* ) "win11${PLATFORM,,}-enterprise"* )
@@ -618,8 +623,9 @@ getWindows() {
return 0 return 0
else else
rc=$? rc=$?
(( rc == 1 )) || return "$rc" fi
fi ;;
(( rc == 1 )) || return "$rc" ;;
"win2025-eval" | "win2022-eval" | "win2019-eval" | \ "win2025-eval" | "win2022-eval" | "win2019-eval" | \
"win2019-hv" | "win2016-eval" | "win2012r2-eval" ) "win2019-hv" | "win2016-eval" | "win2012r2-eval" )
@@ -628,14 +634,16 @@ getWindows() {
return 0 return 0
else else
rc=$? rc=$?
(( rc == 1 )) || return "$rc" fi
fi ;;
(( rc == 1 )) || return "$rc" ;;
"win2008r2"*| "win81${PLATFORM,,}"* | "win10${PLATFORM,,}-enterprise"* ) ;; "win2008r2"*| "win81${PLATFORM,,}"* | "win10${PLATFORM,,}-enterprise"* ) ;;
* ) * )
error "Invalid VERSION specified, value \"$version\" is not recognized!" error "Invalid VERSION specified, value \"$version\" is not recognized!"
return 1 ;; return 1 ;;
esac esac
# Static catalog URLs are the last resort after live Microsoft methods are # Static catalog URLs are the last resort after live Microsoft methods are
@@ -995,10 +1003,7 @@ getESD() {
rm -f "$log" rm -f "$log"
if (( rc >= 129 )); then (( rc >= 129 )) && return "$rc"
return "$rc"
fi
return 1 return 1
fi fi
@@ -1117,7 +1122,7 @@ verifyFile() {
info "Successfully verified $type!" && return 0 info "Successfully verified $type!" && return 0
fi fi
error "The downloaded file has an unknown $algo checksum: $hash , as the expected value was: $check. Please report this at $SUPPORT/issues" warn "the downloaded file has an unknown $algo checksum: $hash , as the expected value was: $check. Please report this at $SUPPORT/issues"
return 1 return 1
} }
@@ -1209,6 +1214,7 @@ tryDownload() {
if ! rm -f -- "$iso" "$iso.aria2"; then if ! rm -f -- "$iso" "$iso.aria2"; then
warn "failed to remove invalid download \"$iso\"!" warn "failed to remove invalid download \"$iso\"!"
fi fi
return 2 return 2
} }
@@ -1271,13 +1277,18 @@ downloadImage() {
local tried="n" success="n" seconds="5" local tried="n" success="n" seconds="5"
local i url sum size base language desc web_desc rc local i url sum size base language desc web_desc rc
if [[ "${version,,}" == "http"* ]]; then if isURL "$version"; then
base=$(basename "$iso") base=$(basename "$iso")
desc=$(fromFile "$base") desc=$(fromFile "$base")
web_desc="$desc" web_desc="$desc"
tryDownload "$iso" "$version" "" "" "$desc" "$seconds" "$web_desc" || return tryDownload "$iso" "$version" "" "" "$desc" "$seconds" "$web_desc" || {
rc=$?
error "Failed to download the Windows image from the specified URL!"
return "$rc"
}
return 0 return 0
fi fi
@@ -1446,6 +1457,8 @@ downloadImage() {
if [[ "$tried" == "n" ]]; then if [[ "$tried" == "n" ]]; then
error "No download method is available for $desc!" error "No download method is available for $desc!"
else
error "All download methods failed for $desc!"
fi fi
return 1 return 1
+43 -66
View File
@@ -11,7 +11,7 @@ SHUTDOWN_SIGNAL=0
QEMU_PTY="$QEMU_DIR/qemu.pty" QEMU_PTY="$QEMU_DIR/qemu.pty"
QEMU_END="$QEMU_DIR/qemu.end" QEMU_END="$QEMU_DIR/qemu.end"
ACPI_SOCKET="$QEMU_DIR/acpi.sock" ACPI_SOCKET="$QEMU_DIR/monitor.sock"
CONSOLE_PID="$QEMU_DIR/console.pid" CONSOLE_PID="$QEMU_DIR/console.pid"
CONSOLE_SOCKET="$QEMU_DIR/console.sock" CONSOLE_SOCKET="$QEMU_DIR/console.sock"
QEMU_START_PID="$QEMU_DIR/qemu.start.pid" QEMU_START_PID="$QEMU_DIR/qemu.start.pid"
@@ -28,7 +28,7 @@ bootStatus() {
[ ! -s "$QEMU_PTY" ] && return 1 [ ! -s "$QEMU_PTY" ] && return 1
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then if isLegacyBoot; then
local line last recent local line last recent
# Only inspect output produced after the most recent BIOS boot attempt so # Only inspect output produced after the most recent BIOS boot attempt so
@@ -44,6 +44,9 @@ bootStatus() {
if grep -Fq \ if grep -Fq \
-e "No bootable device." \ -e "No bootable device." \
-e "BOOTMGR is missing" \ -e "BOOTMGR is missing" \
-e "Replace the disk, and then press any key" \
-e "The following file is missing or corrupted:" \
-e "Type the name of the Windows loader" \
<<< "$recent"; then <<< "$recent"; then
return 2 return 2
fi fi
@@ -130,7 +133,7 @@ waitForBoot() {
elif bootKeyReady; then elif bootKeyReady; then
(( keyWait += 1 )) (( keyWait += 1 ))
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then if isLegacyBoot; then
# Keep the legacy fallback at about one second after the DVD marker. # Keep the legacy fallback at about one second after the DVD marker.
if (( keyWait >= 5 )); then if (( keyWait >= 5 )); then
if sendKey ret 0 100 6 0.25; then if sendKey ret 0 100 6 0.25; then
@@ -251,20 +254,24 @@ legacyBootReady() {
grep -Fq "Loading FreeLoader..." <<< "$recent" && return 0 grep -Fq "Loading FreeLoader..." <<< "$recent" && return 0
grep -Fq "No bootable device." <<< "$recent" && return 1 grep -Fq "No bootable device." <<< "$recent" && return 1
grep -Fq "BOOTMGR is missing" <<< "$recent" && return 1 grep -Fq "BOOTMGR is missing" <<< "$recent" && return 1
grep -Fq "Replace the disk, and then press any key" <<< "$recent" && return 1
grep -Fq "Boot failed: not a bootable disk" <<< "$recent" && return 1 grep -Fq "Boot failed: not a bootable disk" <<< "$recent" && return 1
grep -Fq "Boot failed: could not read the boot disk" <<< "$recent" && return 1 grep -Fq "Boot failed: could not read the boot disk" <<< "$recent" && return 1
grep -Fq "The following file is missing or corrupted:" <<< "$recent" && return 1
grep -Fq "Type the name of the Windows loader" <<< "$recent" && return 1
return 0 return 0
} }
ready() { ready() {
# The marker means installation completed previously, so shutdown no longer # The marker means installation completed previously, so shutdown
# needs to infer guest readiness from firmware output. # no longer needs to infer guest readiness from firmware output.
[ -f "$STORAGE/windows.boot" ] && return 0 hasBootMarker && return 0
[ ! -s "$QEMU_PTY" ] && return 1 [ ! -s "$QEMU_PTY" ] && return 1
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then if isLegacyBoot; then
legacyBootReady && return 0 legacyBootReady && return 0
return 1 return 1
fi fi
@@ -343,32 +350,26 @@ sendKey() {
return 0 return 0
} }
supportsBootKey() {
local id="$1"
[[ "${id,,}" == "win"* ]]
}
needsBootKey() { needsBootKey() {
[ ! -s "$BOOT" ] && return 1 hasImage "$BOOT" || return 1
[[ "${BOOT,,}" != *".iso" ]] && return 1
[ -f "$STORAGE/windows.boot" ] && return 1
supportsBootKey "$DETECTED" hasCompletedInstall && return 1
supportsBootKey "$DETECTED" || return 1
return 0
} }
bootKeyReady() { bootKeyReady() {
[ ! -s "$QEMU_PTY" ] && return 1 [ ! -s "$QEMU_PTY" ] && return 1
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then if isLegacyBoot; then
grep -Fq "Booting from DVD/CD" "$QEMU_PTY" grep -Fq "Booting from DVD/CD" "$QEMU_PTY"
return $? return $?
fi fi
if [[ "${PLATFORM,,}" == "arm64" ]]; then if isPlatform "arm64"; then
grep -Eq "$UEFI_USB_BOOT_PATTERN" "$QEMU_PTY" grep -Eq "$UEFI_USB_BOOT_PATTERN" "$QEMU_PTY"
else else
grep -Eq "$UEFI_DVD_BOOT_PATTERN" "$QEMU_PTY" grep -Eq "$UEFI_DVD_BOOT_PATTERN" "$QEMU_PTY"
@@ -377,7 +378,7 @@ bootKeyReady() {
getBootMarker() { getBootMarker() {
if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then if isLegacyBoot; then
grep -nE "$LEGACY_BOOT_PATTERN" "$QEMU_PTY" | tail -1 grep -nE "$LEGACY_BOOT_PATTERN" "$QEMU_PTY" | tail -1
return 0 return 0
fi fi
@@ -389,9 +390,7 @@ getBootMarker() {
markWindowsBooted() { markWindowsBooted() {
local file="$STORAGE/windows.boot" if hasBootMarker || [ ! -f "$BOOT" ]; then
if [ -f "$file" ] || [ ! -f "$BOOT" ]; then
return 0 return 0
fi fi
@@ -399,19 +398,13 @@ markWindowsBooted() {
# now booting from the installed disk rather than from setup media. # now booting from the installed disk rather than from setup media.
ready || return 0 ready || return 0
if ! touch "$file"; then createMarker "boot" || return 0
warn "failed to create Windows installation marker!"
return 0
fi
if ! setOwner "$file"; then
rm -f "$file"
warn "failed to set the owner for \"$file\" !"
return 0
fi
if ! disabled "$REMOVE"; then if ! disabled "$REMOVE"; then
removeImage "$BOOT" || : case "${BOOT,,}" in
*.img | *.raw | *.qcow2 ) ;;
* ) removeImage "$BOOT" || : ;;
esac
fi fi
rm -f "$STORAGE/setup.img" 2>/dev/null || : rm -f "$STORAGE/setup.img" 2>/dev/null || :
@@ -423,29 +416,26 @@ finish() {
local reason=$1 failed=0 local reason=$1 failed=0
# QEMU_END distinguishes an expected shutdown path from an unexpected QEMU # A nonzero exit is unexpected only when QEMU_END is missing.
# exit carrying the same process status.
if [ ! -f "$QEMU_END" ] && (( reason != 0 )); then if [ ! -f "$QEMU_END" ] && (( reason != 0 )); then
failed=1 failed=1
fi fi
touch "$QEMU_END" touch "$QEMU_END" || :
forceKillQemu "$reason" forceKillQemu "$reason"
if [ ! -f "$STORAGE/windows.boot" ]; then if ! hasBootMarker; then
markWindowsBooted markWindowsBooted
fi fi
cleanupHelpers \ cleanupHelpers "${SMB_PID:-}" "${NMB_PID:-}" "${DDN_PID:-}"
"${SMB_PID:-}" \
"${NMB_PID:-}" \
"${DDN_PID:-}"
if ! waitQemuExit 10; then if ! waitQemuExit 10; then
warn "Timed out while waiting for $(app) to exit!" warn "Timed out while waiting for $(app) to exit!"
fi fi
stopConsole
echo echo
if (( failed == 0 )); then if (( failed == 0 )); then
@@ -457,27 +447,6 @@ finish() {
exit "$reason" exit "$reason"
} }
abortDuringSetup() {
local code="$1"
# Before Windows boots from disk, ACPI may be ignored or interpreted by setup
# itself. Terminate QEMU directly instead of waiting for a graceful shutdown.
if [[ "${DETECTED,,}" != "reactos" ]] || [ -n "${CUSTOM:-}" ]; then
info "Cannot send ACPI signal during $(app) setup, terminating..."
else
info "ReactOS LiveCD does not support ACPI shutdown, terminating..."
fi
terminateQemu
if ! waitQemuExit 10; then
warn "Timed out while waiting for $(app) to exit!"
fi
finish "$code"
}
gracefulShutdown() { gracefulShutdown() {
local sig="$1" local sig="$1"
@@ -526,8 +495,16 @@ gracefulShutdown() {
finish "$code" finish "$code"
fi fi
if ! ready; then if hasMarker "kill"; then
abortDuringSetup "$code"
info "This $(app) version does not support ACPI shutdown, decreasing timeout to 1 second..."
TIMEOUT=7
elif ! ready || { hasSystemImage && ! hasBootMarker; }; then
info "$(app) will ignore ACPI signals during setup, decreasing timeout to 10 seconds..."
TIMEOUT=13
fi fi
normalizeTimeout 105 normalizeTimeout 105
+1 -1
View File
@@ -388,7 +388,7 @@ isUserMode && return 0
# Older Windows versions discover shares through NetBIOS, while modern Windows # Older Windows versions discover shares through NetBIOS, while modern Windows
# uses Web Services Discovery. # uses Web Services Discovery.
if [[ "${BOOT_MODE:-}" == "windows_legacy" ]]; then if isLegacyBoot; then
startNetbios || : startNetbios || :
else else
startWsddn || : startWsddn || :
+1251
View File
File diff suppressed because it is too large Load Diff