Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 69 additions & 15 deletions bin/winapps
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ Please run:
notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Windows is unreachable.\nPlease ensure Windows is assigned an IP address."
;;
"$EC_BAD_PORT")
dprint "ERROR: RDP PORT CLOSED. EXITING."
notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "The Windows RDP port '${RDP_PORT}' is closed.\nPlease ensure Remote Desktop is correctly configured on Windows."
dprint "ERROR: RDP PORT CLOSED ON '${RDP_IP}:${RDP_PORT}'. EXITING."
notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "The Windows RDP port '${RDP_PORT}' on ip '${RDP_IP}' is closed.\nPlease ensure Remote Desktop is correctly configured on Windows."
;;
"$EC_UNSUPPORTED_APP")
dprint "ERROR: APPLICATION NOT FOUND. EXITING."
Expand Down Expand Up @@ -588,14 +588,19 @@ function waCheckVMRunning() {
TIME_ELAPSED=0

while (( TIME_ELAPSED < BOOT_TIMEOUT )); do
# libvirt users need to find the IP address dynamically
if [[ -z "$RDP_IP" ]]; then
RDP_IP=$(waFindVMIP)
fi
# Check if VM is running
if (virsh list --state-running --name | grep -Fxq -- "$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
# libvirt: try each neighbour IP until RDP answers (ip neigh may list several)
if [[ -z "$RDP_IP" ]]; then
if waSelectReachableVMIP 1; then
dprint "VM IS READY"
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Windows is ready."
if [ "$NEEDED_BOOT" = "true" ]; then
sleep 10
fi
break
fi
elif 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
Expand Down Expand Up @@ -743,6 +748,7 @@ function waCheckPortOpen() {
local TIME_ELAPSED=0
local TIME_LIMIT=30
local TIME_INTERVAL=5
local SELECT_STATUS=0

# Obtain Windows VM IP Address ('libvirt' ONLY)
# Note: 'RDP_IP' should not be empty if 'WAFLAVOR' is 'docker', since it is set to localhost before this function is called.
Expand All @@ -752,16 +758,24 @@ function waCheckPortOpen() {
if [ "$TIME_ELAPSED" -eq "$TIME_INTERVAL" ]; then
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Requesting Windows IP address..."
fi
RDP_IP=$(waFindVMIP)
[ -n "$RDP_IP" ] && break
# Try every neighbour IP for this MAC until RDP answers (see #432).
waSelectReachableVMIP 10
SELECT_STATUS=$?
if [ "$SELECT_STATUS" -eq 0 ]; then
return 0
fi
# 1 = no IP in neighbour table yet; 2 = IPs present but RDP still closed (keep waiting during boot).
sleep $TIME_INTERVAL
TIME_ELAPSED=$((TIME_ELAPSED + TIME_INTERVAL))
done

[ -z "$RDP_IP" ] && waThrowExit "$EC_NO_IP"
if [ "$SELECT_STATUS" -eq 2 ] || [ -n "$RDP_IP" ]; then
waThrowExit "$EC_BAD_PORT"
fi
waThrowExit "$EC_NO_IP"
fi

# Check for an open RDP port.
# Check for an open RDP port (configured / non-libvirt path).
timeout 10 nc -z "$RDP_IP" "$RDP_PORT" &>/dev/null || waThrowExit "$EC_BAD_PORT"
}

Expand Down Expand Up @@ -1005,13 +1019,53 @@ function waTimeSync() {
} > "$SLEEP_DETECT_PATH"
}

function waFindVMIP() {
# Name: 'waFindVMIPs'
# Role: List unique IPv4 addresses associated with the Windows VM MAC in the neighbour table.
# Note: After a VM reset, 'ip neigh' can return more than one IP for the same MAC (#432).
function waFindVMIPs() {
local VM_MAC=""

VM_MAC=$(virsh domiflist "$VM_NAME" | grep -oE "([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})") # VM MAC address.
[ -z "$VM_MAC" ] && return 1

ip neigh show | grep -F -- "$VM_MAC" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" | awk '!seen[$0]++'
}

# Name: 'waSelectReachableVMIP'
# Role: Try each VM neighbour IP until RDP_PORT accepts a connection; set RDP_IP on success.
# Args: $1 - timeout seconds for each nc probe (default: 10)
# Returns: 0 if a reachable IP was selected, 1 if no IPs found, 2 if all candidates failed.
function waSelectReachableVMIP() {
local port_timeout="${1:-10}"
local candidate=""
local ips=""
local tried=0
local last_ip=""

ips=$(waFindVMIPs)
if [ -z "$ips" ]; then
return 1
fi

while IFS= read -r candidate; do
[ -z "$candidate" ] && continue
tried=$((tried + 1))
last_ip="$candidate"
if timeout "$port_timeout" nc -z "$candidate" "$RDP_PORT" &>/dev/null; then
RDP_IP="$candidate"
if [ "$tried" -gt 1 ]; then
dprint "SELECTED REACHABLE VM IP '${RDP_IP}' AFTER TRYING ${tried} ADDRESS(ES)."
else
dprint "SELECTED REACHABLE VM IP '${RDP_IP}'."
fi
return 0
fi
dprint "WARNING: RDP PORT '${RDP_PORT}' CLOSED ON IP '${candidate}'. TRYING NEXT ADDRESS IF ANY."
done <<< "$ips"

# VM IP address
ip neigh show | grep -F -- "$VM_MAC" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}"
# Preserve last candidate so BAD_PORT notifications can include the IP.
RDP_IP="$last_ip"
return 2
}

### MAIN LOGIC ###
Expand Down
77 changes: 67 additions & 10 deletions setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1039,42 +1039,99 @@ function waCheckContainerRunning() {
echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}"
}

# Name: 'waFindVMIPs'
# Role: List unique IPv4 addresses associated with the Windows VM MAC in the neighbour table.
# Note: After a VM reset, 'ip neigh' can return more than one IP for the same MAC (#432).
function waFindVMIPs() {
local VM_MAC=""

VM_MAC=$(virsh domiflist "$VM_NAME" | grep -oE "([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})") # VM MAC address.
[ -z "$VM_MAC" ] && return 1

ip neigh show | grep -F -- "$VM_MAC" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" | awk '!seen[$0]++'
}

# Name: 'waSelectReachableVMIP'
# Role: Try each VM neighbour IP until RDP_PORT accepts a connection; set RDP_IP on success.
# Returns: 0 if a reachable IP was selected, 1 if no IPs found, 2 if all candidates failed.
function waSelectReachableVMIP() {
local candidate=""
local ips=""
local last_ip=""

ips=$(waFindVMIPs)
if [ -z "$ips" ]; then
return 1
fi

while IFS= read -r candidate; do
[ -z "$candidate" ] && continue
last_ip="$candidate"
if timeout "$PORT_TIMEOUT" nc -z "$candidate" "$RDP_PORT" &>/dev/null; then
RDP_IP="$candidate"
return 0
fi
echo -e "${WARNING_TEXT}RDP port ${RDP_PORT} closed on ${candidate}; trying next address if any...${CLEAR_TEXT}"
done <<< "$ips"

# Preserve last candidate so the BAD_PORT message can include the IP.
RDP_IP="$last_ip"
return 2
}

# Name: 'waCheckPortOpen'
# Role: Assesses whether the RDP port on Windows is open.
function waCheckPortOpen() {
# Print feedback.
echo -n "Checking for an open RDP Port on Windows... "

# Declare variables.
local VM_MAC="" # Stores the MAC address of the Windows VM.
local SELECT_STATUS=0

# Obtain Windows VM IP Address (FOR 'libvirt' ONLY)
# Note: 'RDP_IP' should not be empty if 'WAFLAVOR' is 'docker', since it is set to localhost before this function is called.
if [ -z "$RDP_IP" ] && [ "$WAFLAVOR" = "libvirt" ]; then
VM_MAC=$(virsh domiflist "$VM_NAME" | grep -oE "([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})") # VM MAC address.
RDP_IP=$(ip neigh show | grep "$VM_MAC" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}") # VM IP address.

if [ -z "$RDP_IP" ]; then
waSelectReachableVMIP
SELECT_STATUS=$?
if [ "$SELECT_STATUS" -ne 0 ]; then
# Complete the previous line.
echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n"

# Display the error type.
echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}NETWORK CONFIGURATION ERROR.${CLEAR_TEXT}"

# Display the error details.
echo -e "${INFO_TEXT}The IP address of the Windows VM '${VM_NAME}' could not be found.${CLEAR_TEXT}"
if [ "$SELECT_STATUS" -eq 1 ]; then
# Display the error details.
echo -e "${INFO_TEXT}The IP address of the Windows VM '${VM_NAME}' could not be found.${CLEAR_TEXT}"

# Display the suggested action(s).
echo "--------------------------------------------------------------------------------"
echo "Please ensure networking is properly configured for the Windows VM."
echo "--------------------------------------------------------------------------------"

# Terminate the script.
return "$EC_NO_IP"
fi

# Display the error details (include IP for debugging multi-address neigh tables).
echo -e "${INFO_TEXT}Failed to establish a connection with Windows at '${RDP_IP}:${RDP_PORT}'.${CLEAR_TEXT}"

# Display the suggested action(s).
echo "--------------------------------------------------------------------------------"
echo "Please ensure networking is properly configured for the Windows VM."
echo "Please ensure Remote Desktop is configured on Windows as per the WinApps README."
echo -e "Then you can try increasing the ${COMMAND_TEXT}PORT_TIMEOUT${CLEAR_TEXT} in ${COMMAND_TEXT}${CONFIG_PATH}${CLEAR_TEXT}."
echo "--------------------------------------------------------------------------------"

# Terminate the script.
return "$EC_NO_IP"
return "$EC_BAD_PORT"
fi

# Print feedback.
echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}"
return 0
fi

# Check for an open RDP port.
# Check for an open RDP port (configured / non-libvirt path).
if ! timeout "$PORT_TIMEOUT" nc -z "$RDP_IP" "$RDP_PORT" &>/dev/null; then
# Complete the previous line.
echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n"
Expand Down
Loading