2026-01-08 18:33:14 +00:00
|
|
|
#!/bin/bash
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
PROJECT_ROOT="$SCRIPT_DIR/.."
|
|
|
|
|
VM_DIR="$PROJECT_ROOT/.nogit/vm"
|
|
|
|
|
ISO_PATH="$PROJECT_ROOT/.nogit/iso/ecoos.iso"
|
|
|
|
|
DISK_PATH="$VM_DIR/test-disk.qcow2"
|
|
|
|
|
MONITOR_SOCK="$VM_DIR/qemu-monitor.sock"
|
|
|
|
|
SERIAL_SOCK="$VM_DIR/serial.sock"
|
|
|
|
|
SERIAL_LOG="$VM_DIR/serial.log"
|
|
|
|
|
PID_FILE="$VM_DIR/qemu.pid"
|
|
|
|
|
|
|
|
|
|
# Create VM directory if not exists
|
|
|
|
|
mkdir -p "$VM_DIR"
|
|
|
|
|
|
|
|
|
|
# Check if ISO exists
|
|
|
|
|
if [ ! -f "$ISO_PATH" ]; then
|
|
|
|
|
echo "ERROR: ISO not found at $ISO_PATH"
|
|
|
|
|
echo "Run 'pnpm run build' first to create the ISO"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Create test disk if not exists
|
|
|
|
|
if [ ! -f "$DISK_PATH" ]; then
|
|
|
|
|
echo "Creating test disk (20GB)..."
|
|
|
|
|
qemu-img create -f qcow2 "$DISK_PATH" 20G
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Check if already running
|
|
|
|
|
if [ -f "$PID_FILE" ]; then
|
|
|
|
|
PID=$(cat "$PID_FILE")
|
|
|
|
|
if kill -0 "$PID" 2>/dev/null; then
|
|
|
|
|
echo "QEMU already running (PID: $PID)"
|
|
|
|
|
echo "Run 'pnpm run test:stop' to stop it first"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo "Starting QEMU with EcoOS ISO..."
|
|
|
|
|
|
|
|
|
|
# Check if KVM is available
|
|
|
|
|
KVM_OPTS=""
|
|
|
|
|
if [ -e /dev/kvm ] && [ -r /dev/kvm ] && [ -w /dev/kvm ]; then
|
|
|
|
|
KVM_OPTS="-enable-kvm -cpu host"
|
|
|
|
|
echo "Using KVM acceleration"
|
|
|
|
|
else
|
|
|
|
|
echo "KVM not available, using software emulation (slower)"
|
|
|
|
|
fi
|
|
|
|
|
|
2026-01-09 19:39:14 +00:00
|
|
|
# Cleanup function
|
|
|
|
|
cleanup() {
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Shutting down..."
|
|
|
|
|
if [ -n "$VIEWER_PID" ] && kill -0 "$VIEWER_PID" 2>/dev/null; then
|
|
|
|
|
kill "$VIEWER_PID" 2>/dev/null || true
|
|
|
|
|
fi
|
|
|
|
|
if [ -f "$PID_FILE" ]; then
|
|
|
|
|
PID=$(cat "$PID_FILE")
|
|
|
|
|
if kill -0 "$PID" 2>/dev/null; then
|
|
|
|
|
kill "$PID" 2>/dev/null || true
|
|
|
|
|
fi
|
|
|
|
|
rm -f "$PID_FILE"
|
|
|
|
|
fi
|
|
|
|
|
echo "Done"
|
|
|
|
|
}
|
|
|
|
|
trap cleanup EXIT INT TERM
|
|
|
|
|
|
|
|
|
|
# Start QEMU with virtio-gpu multi-head (3 outputs)
|
2026-01-08 18:33:14 +00:00
|
|
|
> "$SERIAL_LOG" # Clear old log
|
|
|
|
|
qemu-system-x86_64 \
|
|
|
|
|
$KVM_OPTS \
|
|
|
|
|
-m 4G \
|
2026-01-09 09:41:47 +00:00
|
|
|
-smp 4 \
|
2026-01-08 18:33:14 +00:00
|
|
|
-bios /usr/share/qemu/OVMF.fd \
|
|
|
|
|
-drive file="$ISO_PATH",media=cdrom \
|
|
|
|
|
-drive file="$DISK_PATH",format=qcow2,if=virtio \
|
2026-01-09 19:39:14 +00:00
|
|
|
-device virtio-vga,max_outputs=3 \
|
2026-01-08 18:33:14 +00:00
|
|
|
-display none \
|
2026-01-09 09:41:47 +00:00
|
|
|
-spice port=5930,disable-ticketing=on \
|
2026-01-09 19:39:14 +00:00
|
|
|
-device virtio-serial-pci \
|
|
|
|
|
-chardev spicevmc,id=vdagent,name=vdagent \
|
|
|
|
|
-device virtserialport,chardev=vdagent,name=com.redhat.spice.0 \
|
2026-01-08 18:33:14 +00:00
|
|
|
-serial unix:"$SERIAL_SOCK",server,nowait \
|
|
|
|
|
-monitor unix:"$MONITOR_SOCK",server,nowait \
|
|
|
|
|
-nic user,model=virtio-net-pci,hostfwd=tcp::3006-:3006,hostfwd=tcp::2222-:22 \
|
2026-01-09 09:41:47 +00:00
|
|
|
-pidfile "$PID_FILE" &
|
2026-01-08 18:33:14 +00:00
|
|
|
|
2026-01-09 19:39:14 +00:00
|
|
|
QEMU_PID=$!
|
|
|
|
|
|
2026-01-08 18:33:14 +00:00
|
|
|
echo ""
|
|
|
|
|
echo "=== EcoOS Test VM Started ==="
|
2026-01-09 19:39:14 +00:00
|
|
|
echo "QEMU PID: $QEMU_PID"
|
2026-01-08 18:33:14 +00:00
|
|
|
echo "Management UI: http://localhost:3006"
|
|
|
|
|
echo ""
|
2026-01-09 19:39:14 +00:00
|
|
|
|
|
|
|
|
# Wait for QEMU to start and SPICE to be ready
|
|
|
|
|
echo "Waiting for SPICE server..."
|
|
|
|
|
sleep 3
|
|
|
|
|
|
|
|
|
|
# Check if remote-viewer is available
|
|
|
|
|
if ! command -v remote-viewer &> /dev/null; then
|
|
|
|
|
echo "WARNING: remote-viewer not installed"
|
|
|
|
|
echo "Install with: sudo apt install virt-viewer"
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Running without display viewer. Press Ctrl-C to stop."
|
|
|
|
|
wait $QEMU_PID
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Detect DISPLAY if not set
|
|
|
|
|
if [ -z "$DISPLAY" ]; then
|
|
|
|
|
# Try to find an active X display
|
|
|
|
|
if [ -S /tmp/.X11-unix/X0 ]; then
|
|
|
|
|
export DISPLAY=:0
|
|
|
|
|
elif [ -S /tmp/.X11-unix/X1 ]; then
|
|
|
|
|
export DISPLAY=:1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Detect WAYLAND_DISPLAY if not set
|
|
|
|
|
if [ -z "$WAYLAND_DISPLAY" ] && [ -z "$DISPLAY" ]; then
|
|
|
|
|
# Try common Wayland sockets
|
|
|
|
|
if [ -S "$XDG_RUNTIME_DIR/wayland-0" ]; then
|
|
|
|
|
export WAYLAND_DISPLAY=wayland-0
|
|
|
|
|
elif [ -S "/run/user/$(id -u)/wayland-0" ]; then
|
|
|
|
|
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
|
|
|
|
|
export WAYLAND_DISPLAY=wayland-0
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Launch remote-viewer - use Xvfb if no display available
|
|
|
|
|
if [ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ]; then
|
|
|
|
|
echo "No display found, using Xvfb for headless SPICE client..."
|
|
|
|
|
# Use Xvfb with large virtual screen to support 3 monitors (5760x1080 = 3x1920x1080)
|
|
|
|
|
xvfb-run -a -s "-screen 0 5760x1080x24" remote-viewer spice://localhost:5930 &
|
|
|
|
|
VIEWER_PID=$!
|
|
|
|
|
echo "remote-viewer running headlessly under Xvfb (PID: $VIEWER_PID)"
|
|
|
|
|
else
|
|
|
|
|
echo "Launching remote-viewer (DISPLAY=$DISPLAY, WAYLAND_DISPLAY=$WAYLAND_DISPLAY)..."
|
|
|
|
|
remote-viewer spice://localhost:5930 &
|
|
|
|
|
VIEWER_PID=$!
|
|
|
|
|
fi
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
echo "=== Press Ctrl-C to stop ==="
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Tips:"
|
|
|
|
|
echo " - View > Displays > Enable Display 2/3 for multi-monitor"
|
|
|
|
|
echo " - pnpm run test:screenshot - Take screenshot"
|
|
|
|
|
echo " - http://localhost:3006 - Management UI"
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
# Wait for either process to exit
|
|
|
|
|
wait $QEMU_PID 2>/dev/null || true
|