mirror of
https://github.com/dockur/windows.git
synced 2025-10-27 19:35:49 +00:00
add extra_script option and qemu-guest-agen sock wrapper
This commit is contained in:
parent
e77b22aaa1
commit
d04cc507f0
6 changed files with 263 additions and 9 deletions
|
|
@ -465,6 +465,11 @@
|
||||||
</SynchronousCommand>
|
</SynchronousCommand>
|
||||||
<SynchronousCommand wcm:action="add">
|
<SynchronousCommand wcm:action="add">
|
||||||
<Order>25</Order>
|
<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>
|
<CommandLine>cmd /C "type nul > \\host.lan\Data\prepared"</CommandLine>
|
||||||
<Description>Let host known that all configuration is done</Description>
|
<Description>Let host known that all configuration is done</Description>
|
||||||
</SynchronousCommand>
|
</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 "dependencies_windows.ps1"
|
||||||
powershell -ExecutionPolicy Bypass -File "optimize.ps1"
|
powershell -ExecutionPolicy Bypass -File "optimize.ps1"
|
||||||
powershell -ExecutionPolicy Bypass -File "disable_updates.ps1"
|
powershell -ExecutionPolicy Bypass -File "disable_updates.ps1"
|
||||||
|
powershell -ExecutionPolicy Bypass -File "enable_sshd.ps1"
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
|
||||||
32
src/entry.sh
32
src/entry.sh
|
|
@ -36,16 +36,38 @@ terminal
|
||||||
(
|
(
|
||||||
sleep 30
|
sleep 30
|
||||||
boot
|
boot
|
||||||
configure_guest_network_interface
|
|
||||||
info "Windows started succesfully, you can now connect using RDP"
|
if ! configure_guest_network_interface; then
|
||||||
if [[ "${NETWORK,,}" != "bridge"* ]]; then
|
error "Failed to configure guest network interfaces"
|
||||||
info "or visit http://localhost:8006/ to view the screen..."
|
exit 666
|
||||||
fi
|
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"
|
touch "$STORAGE/ready"
|
||||||
) &
|
) &
|
||||||
|
bg_pid=$!
|
||||||
|
|
||||||
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
|
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
|
||||||
cat "$QEMU_TERM" 2>/dev/null | tee "$QEMU_PTY" &
|
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 &
|
sleep 1 &
|
||||||
wait $!
|
wait $!
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ configure_guest_network_interface() {
|
||||||
|
|
||||||
if [ -z "$CURRENT_IP" ]; then
|
if [ -z "$CURRENT_IP" ]; then
|
||||||
echo "Error: Unable to retrieve the current IP address of $HOST_INTERFACE."
|
echo "Error: Unable to retrieve the current IP address of $HOST_INTERFACE."
|
||||||
exit 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Current Host IP: $CURRENT_IP"
|
echo "Current Host IP: $CURRENT_IP"
|
||||||
|
|
@ -84,10 +84,33 @@ configure_guest_network_interface() {
|
||||||
INTERFACE_NAME="Ethernet $IDX"
|
INTERFACE_NAME="Ethernet $IDX"
|
||||||
fi
|
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
|
exit_code=0
|
||||||
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
|
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
|
done
|
||||||
|
|
||||||
|
touch "$STORAGE/interfaces_configured"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -465,7 +488,10 @@ closeNetwork() {
|
||||||
[[ "$NETWORK" == [Nn]* ]] && return 0
|
[[ "$NETWORK" == [Nn]* ]] && return 0
|
||||||
|
|
||||||
exec 30<&- || true
|
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
|
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