mirror of
https://github.com/dockur/windows.git
synced 2025-10-27 03:15:49 +00:00
Merge branch 'gytsto/general-improvements' into 'master'
add extra_script option and qemu-guest-agen sock wrapper See merge request low-level-hacks/third-party-build/dockur_windows!4
This commit is contained in:
commit
dfeb67a546
6 changed files with 263 additions and 9 deletions
|
|
@ -465,6 +465,11 @@
|
|||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<Order>25</Order>
|
||||
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" /v "IPAutoconfigurationEnabled" /t REG_DWORD /d 0 /f</CommandLine>
|
||||
<Description>Disable ip autoconfiguration for network interfaces</Description>
|
||||
</SynchronousCommand>
|
||||
<SynchronousCommand wcm:action="add">
|
||||
<Order>26</Order>
|
||||
<CommandLine>cmd /C "type nul > \\host.lan\Data\prepared"</CommandLine>
|
||||
<Description>Let host known that all configuration is done</Description>
|
||||
</SynchronousCommand>
|
||||
|
|
|
|||
62
scripts/enable_sshd.ps1
Normal file
62
scripts/enable_sshd.ps1
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# Define variables
|
||||
$OpenSSH_URL = "https://github.com/PowerShell/Win32-OpenSSH/releases/latest/download/OpenSSH-Win64.zip"
|
||||
$OpenSSH_Install_Path = "C:\Program Files\OpenSSH"
|
||||
$OpenSSH_Zip = "$env:TEMP\OpenSSH-Win64.zip"
|
||||
|
||||
# Function to check if running as Administrator
|
||||
function Test-Admin {
|
||||
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal($currentUser)
|
||||
return $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
if (-not (Test-Admin)) {
|
||||
Write-Host "Please run this script as Administrator!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ensure the install path exists
|
||||
if (!(Test-Path $OpenSSH_Install_Path)) {
|
||||
New-Item -ItemType Directory -Path $OpenSSH_Install_Path -Force
|
||||
}
|
||||
|
||||
# Download OpenSSH if not already present
|
||||
Write-Host "Downloading OpenSSH..." -ForegroundColor Cyan
|
||||
Invoke-WebRequest -Uri $OpenSSH_URL -OutFile $OpenSSH_Zip
|
||||
|
||||
# Extract OpenSSH
|
||||
Write-Host "Extracting OpenSSH..." -ForegroundColor Cyan
|
||||
Expand-Archive -Path $OpenSSH_Zip -DestinationPath $OpenSSH_Install_Path -Force
|
||||
|
||||
# Check if install-sshd.ps1 exists
|
||||
if (!(Test-Path "$OpenSSH_Install_Path\OpenSSH-Win64\install-sshd.ps1")) {
|
||||
Write-Host "❌ Error: install-sshd.ps1 not found in $OpenSSH_Install_Path. Extraction failed!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Navigate to OpenSSH directory
|
||||
Push-Location -Path $OpenSSH_Install_Path\OpenSSH-Win64
|
||||
|
||||
# Run install script
|
||||
Write-Host "Installing OpenSSH service..." -ForegroundColor Green
|
||||
powershell.exe -ExecutionPolicy Bypass -File install-sshd.ps1
|
||||
|
||||
# Set SSHD service to start automatically
|
||||
Write-Host "Setting SSHD to start automatically..." -ForegroundColor Green
|
||||
if (Get-Service sshd -ErrorAction SilentlyContinue) {
|
||||
Set-Service -Name sshd -StartupType Automatic
|
||||
Start-Service sshd
|
||||
} else {
|
||||
Write-Host "⚠ OpenSSH service was not installed correctly. Try running install-sshd.ps1 manually." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verify installation
|
||||
$sshdStatus = Get-Service -Name sshd -ErrorAction SilentlyContinue
|
||||
if ($sshdStatus.Status -eq 'Running') {
|
||||
Write-Host "✅ OpenSSH installation successful! You can now connect via SSH." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "⚠ OpenSSH installation failed. Try restarting your computer and rerun the script." -ForegroundColor Red
|
||||
}
|
||||
|
||||
Pop-Location
|
||||
|
|
@ -3,5 +3,6 @@ pushd "C:/OEM"
|
|||
powershell -ExecutionPolicy Bypass -File "dependencies_windows.ps1"
|
||||
powershell -ExecutionPolicy Bypass -File "optimize.ps1"
|
||||
powershell -ExecutionPolicy Bypass -File "disable_updates.ps1"
|
||||
powershell -ExecutionPolicy Bypass -File "enable_sshd.ps1"
|
||||
|
||||
popd
|
||||
|
|
|
|||
32
src/entry.sh
32
src/entry.sh
|
|
@ -36,16 +36,38 @@ terminal
|
|||
(
|
||||
sleep 30
|
||||
boot
|
||||
configure_guest_network_interface
|
||||
info "Windows started succesfully, you can now connect using RDP"
|
||||
if [[ "${NETWORK,,}" != "bridge"* ]]; then
|
||||
info "or visit http://localhost:8006/ to view the screen..."
|
||||
|
||||
if ! configure_guest_network_interface; then
|
||||
error "Failed to configure guest network interfaces"
|
||||
exit 666
|
||||
fi
|
||||
|
||||
if [[ -n "${EXTRA_SCRIPT:-}" ]]; then
|
||||
info "Executing extra script: $EXTRA_SCRIPT"
|
||||
if ! "$EXTRA_SCRIPT"; then
|
||||
error "Extra script failed"
|
||||
exit 555
|
||||
fi
|
||||
fi
|
||||
|
||||
info "Windows started successfully, you can now connect using RDP or visit http://localhost:8006/ to view the screen..."
|
||||
touch "$STORAGE/ready"
|
||||
) &
|
||||
bg_pid=$!
|
||||
|
||||
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
|
||||
cat "$QEMU_TERM" 2>/dev/null | tee "$QEMU_PTY" &
|
||||
wait $! || :
|
||||
term_pd=$!
|
||||
|
||||
wait $bg_pid
|
||||
exit_code=$?
|
||||
|
||||
if [[ $exit_code -ne 0 ]]; then
|
||||
error "A critical process failed, exiting container..."
|
||||
exit $exit_code
|
||||
fi
|
||||
|
||||
wait $term_pd || :
|
||||
|
||||
sleep 1 &
|
||||
wait $!
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ configure_guest_network_interface() {
|
|||
|
||||
if [ -z "$CURRENT_IP" ]; then
|
||||
echo "Error: Unable to retrieve the current IP address of $HOST_INTERFACE."
|
||||
exit 1
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Current Host IP: $CURRENT_IP"
|
||||
|
|
@ -84,10 +84,33 @@ configure_guest_network_interface() {
|
|||
INTERFACE_NAME="Ethernet $IDX"
|
||||
fi
|
||||
|
||||
echo -e '{"execute": "guest-exec", "arguments": {"path": "C:\\\\Windows\\\\System32\\\\netsh.exe", "capture-output": true, "arg": ["interface", "ipv4", "set", "address", "'"$INTERFACE_NAME"'", "static", "'$CURRENT_IP'", "255.255.255.0", "'$GW'"]}}' | nc -U /tmp/qga.sock -w 5
|
||||
echo -e '{"execute": "guest-exec", "arguments": {"path": "C:\\\\Windows\\\\System32\\\\netsh.exe", "capture-output": true, "arg": ["interface", "ipv4", "add", "dnsservers", "'"$INTERFACE_NAME"'", "1.1.1.1", "index=1"]}}' | nc -U /tmp/qga.sock -w 5
|
||||
exit_code=0
|
||||
python3 /run/qga.py powershell -Command "Set-NetIPInterface -InterfaceAlias '$INTERFACE_NAME' -Dhcp Disabled" || exit_code=$?
|
||||
if [[ $exit_code -ne 0 ]]; then
|
||||
echo "Failed to disable dhcp using qga.py" >&2
|
||||
return 2
|
||||
fi
|
||||
|
||||
if [[ -f "$STORAGE/interfaces_configured" ]]; then
|
||||
python3 /run/qga.py powershell -Command "Remove-NetIPAddress -IPAddress '$CURRENT_IP' -Confirm:\$false" || true
|
||||
python3 /run/qga.py powershell -Command "Remove-NetRoute -InterfaceAlias '$INTERFACE_NAME' -DestinationPrefix '0.0.0.0/0' -Confirm:\$false" || true
|
||||
fi
|
||||
|
||||
python3 /run/qga.py powershell -Command "New-NetIPAddress -InterfaceAlias '$INTERFACE_NAME' -IPAddress '$CURRENT_IP' -PrefixLength 24 -DefaultGateway '$GW'" || exit_code=$?
|
||||
if [[ $exit_code -ne 0 ]]; then
|
||||
echo "Failed to set ip address using qga.py" >&2
|
||||
return 3
|
||||
fi
|
||||
|
||||
python3 /run/qga.py powershell -Command "Set-DnsClientServerAddress -InterfaceAlias '$INTERFACE_NAME' -ServerAddresses 1.1.1.1" || exit_code=$?
|
||||
if [[ $exit_code -ne 0 ]]; then
|
||||
echo "Failed to set dns server using qga.py" >&2
|
||||
return 4
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
touch "$STORAGE/interfaces_configured"
|
||||
fi
|
||||
|
||||
return 0
|
||||
|
|
@ -465,7 +488,10 @@ closeNetwork() {
|
|||
[[ "$NETWORK" == [Nn]* ]] && return 0
|
||||
|
||||
exec 30<&- || true
|
||||
exec 40<&- || true
|
||||
for ((i = 0; i < ETH_COUNT; i++)); do
|
||||
fd=$((40 + i))
|
||||
eval "exec $fd<&-" || true
|
||||
done
|
||||
|
||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||
|
||||
|
|
|
|||
138
src/qga.py
Normal file
138
src/qga.py
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
import argparse
|
||||
import base64
|
||||
import json
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
||||
QGA_SOCKET = "/tmp/qga.sock" # Adjust if needed
|
||||
|
||||
|
||||
def send_qga_command(sock, command):
|
||||
"""Send a JSON command to the QEMU Guest Agent socket and receive the response."""
|
||||
try:
|
||||
cmd = (json.dumps(command) + "\n").encode()
|
||||
sock.sendall(cmd)
|
||||
response = sock.recv(4096)
|
||||
return json.loads(response.decode())
|
||||
except socket.timeout:
|
||||
print(f"Timeout waiting for response from {QGA_SOCKET}", file=sys.stderr)
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error communicating with socket: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
def decode_output(data):
|
||||
"""Try to decode output as hex or Base64, or return raw."""
|
||||
if not data:
|
||||
return ""
|
||||
|
||||
try:
|
||||
# Try Hex decoding first
|
||||
return bytes.fromhex(data).decode("utf-8", errors="ignore")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
# If hex fails, try Base64 decoding
|
||||
return base64.b64decode(data).decode("utf-8", errors="ignore")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# If all decoding fails, return raw
|
||||
return data
|
||||
|
||||
|
||||
def execute_command(sock, command_path, command_args):
|
||||
"""Execute a command inside the guest VM with specified path and arguments."""
|
||||
exec_request = {
|
||||
"execute": "guest-exec",
|
||||
"arguments": {
|
||||
"path": command_path,
|
||||
"arg": command_args,
|
||||
"capture-output": True, # Capture stdout and stderr
|
||||
},
|
||||
}
|
||||
response = send_qga_command(sock, exec_request)
|
||||
|
||||
if response is None:
|
||||
return None
|
||||
|
||||
if "return" not in response or "pid" not in response["return"]:
|
||||
print("Error: Failed to start execution:", response, file=sys.stderr)
|
||||
return None
|
||||
|
||||
pid = response["return"]["pid"]
|
||||
print(f"Command started with PID {pid}")
|
||||
|
||||
# Step 2: Wait for completion
|
||||
while True:
|
||||
status_request = {"execute": "guest-exec-status", "arguments": {"pid": pid}}
|
||||
status_response = send_qga_command(sock, status_request)
|
||||
|
||||
if status_response is None:
|
||||
continue
|
||||
|
||||
if "return" in status_response:
|
||||
status = status_response["return"]
|
||||
if status.get("exited", False):
|
||||
break # Command finished
|
||||
time.sleep(0.2) # Wait before checking again
|
||||
|
||||
# Step 3: Get exit code and output
|
||||
exit_code = status.get("exitcode", -1)
|
||||
stdout_data = decode_output(status.get("out-data", ""))
|
||||
stderr_data = decode_output(status.get("err-data", ""))
|
||||
|
||||
return {"exit_code": exit_code, "stdout": stdout_data, "stderr": stderr_data}
|
||||
|
||||
|
||||
def create_socket():
|
||||
"""Create and return a reusable socket connection to the QEMU Guest Agent."""
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.settimeout(30) # 30 seconds timeout
|
||||
try:
|
||||
sock.connect(QGA_SOCKET)
|
||||
return sock
|
||||
except Exception as e:
|
||||
print(f"Error creating socket: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""Parse command-line arguments."""
|
||||
parser = argparse.ArgumentParser(description="Send commands to QEMU Guest Agent.")
|
||||
parser.add_argument(
|
||||
"command", help="Path to the command to execute inside the guest VM"
|
||||
)
|
||||
parser.add_argument(
|
||||
"args", nargs=argparse.REMAINDER, help="Arguments to pass to the command"
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Parse command-line arguments
|
||||
args = parse_args()
|
||||
|
||||
# Create a reusable socket
|
||||
unix_sock = create_socket()
|
||||
if not unix_sock:
|
||||
print("Failed to create socket.", file=sys.stderr)
|
||||
sys.exit(1) # Exit if we can't connect to the socket
|
||||
|
||||
# Execute the command
|
||||
result = execute_command(unix_sock, args.command, args.args)
|
||||
if result:
|
||||
print(f"Exit Code: {result['exit_code']}")
|
||||
if result["stdout"]:
|
||||
print("STDOUT:\n", result["stdout"])
|
||||
if result["stderr"]:
|
||||
print("STDERR:\n", result["stderr"])
|
||||
|
||||
# Close the socket once all commands are executed
|
||||
unix_sock.close()
|
||||
|
||||
# Exit with the appropriate code based on command execution result
|
||||
sys.exit(result["exit_code"] if result else 2)
|
||||
Loading…
Add table
Add a link
Reference in a new issue