diff --git a/README.md b/README.md index bb28fb1..431518c 100644 --- a/README.md +++ b/README.md @@ -469,6 +469,11 @@ RDP_TIMEOUT="30" # DEFAULT VALUE: '60' APP_SCAN_TIMEOUT="60" +# WINDOWS BOOT +# - The maximum time (in seconds) to wait for the Windows VM to boot if it is not running, before attempting to launch an application. +# DEFAULT VALUE: '120' +BOOT_TIMEOUT="120" + ``` > [!IMPORTANT] diff --git a/bin/winapps b/bin/winapps index 5779f3c..f7a5c0e 100755 --- a/bin/winapps +++ b/bin/winapps @@ -50,9 +50,11 @@ RDP_SCALE=100 AUTOPAUSE="off" AUTOPAUSE_TIME="300" DEBUG="true" +BOOT_TIMEOUT=120 # OTHER FREERDP_PID=-1 +NEEDED_BOOT=false ### TRAPS ### # Catch SIGINT (CTRL+C) to call 'waCleanUp'. @@ -323,6 +325,7 @@ function waCheckVMRunning() { if (virsh list --state-shutoff --name | xargs | grep -wq "$VM_NAME"); then dprint "WINDOWS SHUT OFF. BOOTING WINDOWS." notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows." + NEEDED_BOOT=true virsh start "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_START if (virsh list --state-paused --name | xargs | grep -wq "$VM_NAME"); then dprint "WINDOWS PAUSED. RESUMING WINDOWS." @@ -344,6 +347,7 @@ function waCheckVMRunning() { dprint "WINDOWS SHUT OFF. BOOTING WINDOWS." notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows." virsh start "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_START + NEEDED_BOOT=true break fi sleep $TIME_INTERVAL @@ -357,6 +361,7 @@ function waCheckVMRunning() { dprint "WINDOWS DESTROYED. BOOTING WINDOWS." notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows." virsh start "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_START + NEEDED_BOOT=true fi elif (virsh domstate "$VM_NAME" | xargs | grep -wq "dying"); then dprint "WINDOWS DYING. WAITING." @@ -372,6 +377,7 @@ function waCheckVMRunning() { dprint "WINDOWS DESTROYED. BOOTING WINDOWS." notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows." virsh start "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_START + NEEDED_BOOT=true fi break elif (virsh list --state-shutoff --name | xargs | grep -wq "$VM_NAME"); then @@ -379,6 +385,7 @@ function waCheckVMRunning() { dprint "WINDOWS SHUT OFF. BOOTING WINDOWS." notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows." virsh start "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_START + NEEDED_BOOT=true break fi sleep $TIME_INTERVAL @@ -395,6 +402,45 @@ function waCheckVMRunning() { # Handle non-zero exit statuses. [ "$EXIT_STATUS" -ne 0 ] && waThrowExit "$EXIT_STATUS" + + # Wait for VM to be fully ready + if [[ "$NEEDED_BOOT" == "true" ]]; then + dprint "WAITING FOR VM TO BE FULLY READY..." + notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Waiting for Windows to be ready..." + + TIME_ELAPSED=0 + + while (( TIME_ELAPSED < BOOT_TIMEOUT )); do + # Check if VM is running + if (virsh list --state-running --name | xargs | grep -wq "$VM_NAME"); then + # Try to connect to RDP port to verify it's ready + if timeout 1 bash -c ">/dev/tcp/$RDP_IP/$RDP_PORT" 2>/dev/null; then + dprint "VM IS READY" + notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Windows is ready." + # Add a delay after Windows is ready + if [ "$NEEDED_BOOT" = "true" ]; then + sleep 10 + fi + break + fi + fi + + sleep 5 + TIME_ELAPSED=$((TIME_ELAPSED + 5)) + + # Show progress every 30 seconds + if (( TIME_ELAPSED % 30 == 0 )); then + notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Still waiting for Windows to be ready... ($TIME_ELAPSED seconds elapsed)" + fi + done + + # If we timed out waiting for the VM + if (( TIME_ELAPSED >= BOOT_TIMEOUT )); then + dprint "TIMEOUT WAITING FOR VM TO BE READY" + notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Timeout waiting for Windows to be ready. Please try again." + waThrowExit $EC_FAIL_START + fi + fi } # Name: 'waCheckContainerRunning' @@ -426,6 +472,7 @@ function waCheckContainerRunning() { dprint "WINDOWS CREATED. BOOTING WINDOWS." notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows." $COMPOSE_COMMAND --file "$COMPOSE_PATH" start &>/dev/null + NEEDED_BOOT=true ;; "restarting") dprint "WINDOWS RESTARTING. WAITING." @@ -436,6 +483,7 @@ function waCheckContainerRunning() { EXIT_STATUS=0 dprint "WINDOWS RESTARTED." notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Restarted Windows." + NEEDED_BOOT=true break fi sleep $TIME_INTERVAL @@ -451,11 +499,13 @@ function waCheckContainerRunning() { dprint "WINDOWS SHUT OFF. BOOTING WINDOWS." notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows." $COMPOSE_COMMAND --file "$COMPOSE_PATH" start &>/dev/null + NEEDED_BOOT=true ;; "dead") dprint "WINDOWS DEAD. RECREATING WINDOWS CONTAINER." notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Re-creating and booting Windows." $COMPOSE_COMMAND --file "$COMPOSE_PATH" down &>/dev/null && $COMPOSE_COMMAND --file "$COMPOSE_PATH" up -d &>/dev/null + NEEDED_BOOT=true ;; "unknown") EXIT_STATUS=$EC_UNKNOWN @@ -464,6 +514,45 @@ function waCheckContainerRunning() { # Handle non-zero exit statuses. [ "$EXIT_STATUS" -ne 0 ] && waThrowExit "$EXIT_STATUS" + + # Wait for container to be fully ready + if [[ "$CONTAINER_STATE" == "created" || "$CONTAINER_STATE" == "exited" || "$CONTAINER_STATE" == "dead" || "$CONTAINER_STATE" == "restarting" ]]; then + dprint "WAITING FOR CONTAINER TO BE FULLY READY..." + notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Waiting for Windows to be ready..." + + TIME_ELAPSED=0 + + while (( TIME_ELAPSED < BOOT_TIMEOUT )); do + # Check if container is running + if [[ $("$WAFLAVOR" inspect --format='{{.State.Status}}' "$CONTAINER_NAME") == "running" ]]; then + # Try to connect to RDP port to verify it's ready + if timeout 1 bash -c ">/dev/tcp/$RDP_IP/$RDP_PORT" 2>/dev/null; then + dprint "CONTAINER IS READY" + notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Windows is ready." + # Add a delay after Windows is ready + if [ "$NEEDED_BOOT" = "true" ]; then + sleep 10 + fi + break + fi + fi + + sleep 5 + TIME_ELAPSED=$((TIME_ELAPSED + 5)) + + # Show progress every 30 seconds + if (( TIME_ELAPSED % 30 == 0 )); then + notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Still waiting for Windows to be ready... ($TIME_ELAPSED seconds elapsed)" + fi + done + + # If we timed out waiting for the container + if (( TIME_ELAPSED >= BOOT_TIMEOUT )); then + dprint "TIMEOUT WAITING FOR CONTAINER TO BE READY" + notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Timeout waiting for Windows to be ready. Please try again." + waThrowExit $EC_FAIL_START + fi + fi } # Name: 'waCheckPortOpen'