From c63c25c7b174782ba7efc3b924f0353caf689dac Mon Sep 17 00:00:00 2001 From: Guofang Date: Thu, 15 Oct 2026 12:40:19 +0800 Subject: [PATCH] Warn instead of failing when a forwarded host port is in use in the Windows 11 launcher Co-Authored-By: Claude Sonnet 5.5 --- run-windows11.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/run-windows11.py b/run-windows11.py index 3ab4d44..f3a1e1c 100755 --- a/run-windows11.py +++ b/run-windows11.py @@ -4,6 +4,7 @@ import os import platform import shutil +import socket import string import subprocess import sys @@ -33,6 +34,17 @@ def capture(command): return result.stdout.rstrip("\n") +def port_in_use(port): + """True if a host TCP port cannot be bound, as QEMU does for a hostfwd rule.""" + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe: + probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + try: + probe.bind(("", int(port))) + except OSError: + return True + return False + + def is_executable(path): return path.is_file() and os.access(path, os.X_OK) @@ -296,6 +308,19 @@ except (KeyError, ValueError) as error: die(f"invalid placeholder in {qemu_config_template}: {error}") qemu_config.write_text(qemu_config_text) +# QEMU refuses to start when a forwarded host port is taken, so warn and leave +# that forward out instead. +forwards = [] +forwarded_ports = [] +for label, host_port, guest_port in (("RDP", rdp_port, 3389), ("SSH", ssh_port, 22)): + in_use = port_in_use(host_port) + forwarded_ports.append((label, host_port, not in_use)) + if in_use: + print(f"Warning: host port {host_port} is already in use; " + f"{label} will not be forwarded to the guest.", file=sys.stderr) + else: + forwards.append(f"hostfwd=tcp::{host_port}-:{guest_port}") + qemu_args = [ "-readconfig", str(qemu_config), "-readconfig", str(qemu_arch_config), @@ -304,7 +329,7 @@ qemu_args = [ # A config file section cannot hold two hostfwd entries, so the network # that the NIC in the config file uses is defined here. -nic none stops # QEMU from adding its default NIC as well. - "-netdev", f"user,id=net0,hostfwd=tcp::{rdp_port}-:3389,hostfwd=tcp::{ssh_port}-:22", + "-netdev", ",".join(["user,id=net0", *forwards]), "-nic", "none", "-display", display_backend, # "-display", "none", "-spice", "port=5900,addr=127.0.0.1,disable-ticketing=on,agent-mouse=off", "-device", "virtio-vga", @@ -324,8 +349,9 @@ elif installing: print("Starting Windows 11 from the QCOW2 base image.") else: print("Starting Windows 11 normally.") -print(f"Account: guofang / password; RDP: localhost:{rdp_port}; SSH: localhost:{ssh_port}", - flush=True) +print("Account: guofang / password; " + "; ".join( + f"{label}: localhost:{port}" for label, port, forwarded in forwarded_ports if forwarded), + flush=True) if daemonize: log_file.write_text("")