Merge branch 'main' into updated_application_icons
38
README.md
@ -2,13 +2,13 @@
|
||||
<p align="center"><img align="center" width="700" src="./icons/banner_dark.svg#gh-light-mode-only"/></p>
|
||||
<hr>
|
||||
|
||||
Run Windows applications (including [Microsoft 365](https://www.microsoft365.com/) and [Adobe Creative Cloud](https://www.adobe.com/creativecloud.html)) on GNU/Linux with `KDE` or `GNOME`, integrated seamlessly as if they were native to the OS.
|
||||
Run Windows applications (including [Microsoft 365](https://www.microsoft365.com/) and [Adobe Creative Cloud](https://www.adobe.com/creativecloud.html)) on GNU+Linux with `KDE`, `GNOME` or `XFCE`, integrated seamlessly as if they were native to the OS.
|
||||
|
||||
<img src="demo/demo.gif" width=1000 alt="WinApps Demonstration Animation.">
|
||||
|
||||
## Underlying Mechanism
|
||||
WinApps works by:
|
||||
1. Running Windows in a `Docker` or `libvirt + KVM/QEMU` virtual machine (deprecated).
|
||||
1. Running Windows in a `Docker`, `Podman` or `libvirt` virtual machine.
|
||||
2. Querying Windows for all installed applications.
|
||||
3. Creating shortcuts to selected Windows applications on the host GNU/Linux OS.
|
||||
4. Using [`FreeRDP`](https://www.freerdp.com/) as a backend to seamlessly render Windows applications alongside GNU/Linux applications.
|
||||
@ -249,13 +249,13 @@ Contributing to the list of supported applications is encouraged through submiss
|
||||
|
||||
## Installation
|
||||
### Step 1: Configure a Windows VM
|
||||
The optimal choice for running a Windows VM as a subsystem for WinApps is `Docker`. `Docker` facilitates automated installation processes while leveraging a `KVM/QEMU` backend. Despite continuing to provide documentation for configuring a Windows VM using `libvirt` and `virt-manager`, this method is now considered deprecated.
|
||||
Both `Docker` and `Podman` are recommended backends for running the Windows virtual machine, as they facilitate an automated Windows installation process. WinApps is also compatible with `libvirt`. While this method requires considerably more manual configuration, it also provides greater virtual machine customisation options. All three methods leverage the `KVM` hypervisor, ensuring excellent virtual machine performance. Ultimately, the choice of backend depends on your specific use case.
|
||||
|
||||
The following guides are available:
|
||||
- [Creating a Windows VM with `Docker`](docs/docker.md)
|
||||
- [Creating a Windows VM with `virt-manager`](docs/KVM.md) (Deprecated)
|
||||
- [Creating a Windows VM with `Docker` or `Podman`](docs/docker.md)
|
||||
- [Creating a Windows VM with `libvirt`](docs/libvirt.md)
|
||||
|
||||
If you already have a Windows VM or server you wish to use with WinApps, you will need to merge `install/RDPApps.reg` into the Windows Registry.
|
||||
If you already have a Windows VM or server you wish to use with WinApps, you will need to merge `install/RDPApps.reg` into the Windows Registry manually.
|
||||
|
||||
### Step 2: Clone WinApps Repository and Dependencies
|
||||
1. Clone the WinApps GitHub repository.
|
||||
@ -281,7 +281,8 @@ If you already have a Windows VM or server you wish to use with WinApps, you wil
|
||||
sudo emerge --ask=n sys-libs/dialog net-misc/freerdp:3
|
||||
```
|
||||
|
||||
Please note that WinApps requires `FreeRDP` version 3 or later. If not available for your distribution through your package manager, you can install the [Flatpak](https://flathub.org/apps/com.freerdp.FreeRDP).
|
||||
> [!NOTE]
|
||||
> WinApps requires `FreeRDP` version 3 or later. If not available for your distribution through your package manager, you can install the [Flatpak](https://flathub.org/apps/com.freerdp.FreeRDP).
|
||||
|
||||
```bash
|
||||
flatpak install flathub com.freerdp.FreeRDP
|
||||
@ -295,24 +296,29 @@ RDP_USER="MyWindowsUser"
|
||||
RDP_PASS="MyWindowsPassword"
|
||||
#RDP_DOMAIN="MYDOMAIN"
|
||||
#RDP_IP="192.168.123.111"
|
||||
#RDP_SCALE=100
|
||||
#WAFLAVOR="docker" # Acceptable values are 'docker', 'podman' and 'libvirt'.
|
||||
#RDP_SCALE=100 # Acceptable values are 100, 140, and 180.
|
||||
#RDP_FLAGS=""
|
||||
#MULTIMON="true"
|
||||
#DEBUG="true"
|
||||
#FREERDP_COMMAND="xfreerdp"
|
||||
```
|
||||
|
||||
`RDP_USER` and `RDP_PASS` must correspond to a complete Windows user account and password, such as those created during Windows setup or for a domain user. User/PIN combinations are not valid for RDP access.
|
||||
> [!NOTE]
|
||||
> `RDP_USER` and `RDP_PASS` must correspond to a complete Windows user account and password, such as those created during Windows setup or for a domain user. User/PIN combinations are not valid for RDP access.
|
||||
|
||||
> [!NOTE]
|
||||
> If you wish to use an alternative WinApps backend (other than `Docker`), uncomment and change `WAFLAVOR="docker"` to `WAFLAVOR="podman"` or `WAFLAVOR="libvirt"`.
|
||||
|
||||
#### Configuration Options Explained
|
||||
- When using a pre-existing non-KVM RDP server, you must use `RDP_IP` to specify the location of the Windows server.
|
||||
- If running a Windows VM in KVM with NAT enabled, leave `RDP_IP` commented out and WinApps will auto-detect the local IP address for the VM.
|
||||
- If using a pre-existing Windows RDP server on your LAN, you must use `RDP_IP` to specify the location of the Windows server. You may also wish to configure a static IP address for this server.
|
||||
- If running a Windows VM using `libvirt` with NAT enabled, leave `RDP_IP` commented out and WinApps will auto-detect the local IP address for the VM.
|
||||
- For domain users, you can uncomment and change `RDP_DOMAIN`.
|
||||
- On high-resolution (UHD) displays, you can set `RDP_SCALE` to the scale you would like to use [100|140|160|180].
|
||||
- To add flags to the FreeRDP call, such as `/audio-mode:1` to pass in a microphone, uncomment and use the `RDP_FLAGS` configuration option.
|
||||
- On high-resolution (UHD) displays, you can set `RDP_SCALE` to the scale you would like to use (100, 140 or 180).
|
||||
- To add additional flags to the FreeRDP call (e.g. `/prevent-session-lock 120`), uncomment and use the `RDP_FLAGS` configuration option.
|
||||
- For multi-monitor setups, you can try enabling `MULTIMON`. A FreeRDP bug may result in a black screen however, in which case you should revert this change.
|
||||
- If you enable `DEBUG`, a log will be created on each application start in `~/.local/share/winapps/winapps.log`
|
||||
- If using a system on which the FreeRDP command is not `xfreerdp`, the correct command can be specified using `FREERDP_COMMAND`.
|
||||
- If you enable `DEBUG`, a log will be created on each application start in `~/.local/share/winapps/winapps.log`.
|
||||
- If using a system on which the FreeRDP command is not `xfreerdp` or `xfreerdp3`, the correct command can be specified using `FREERDP_COMMAND`.
|
||||
|
||||
### Step 4: Run the WinApps Installer
|
||||
Run the WinApps installer.
|
||||
@ -329,7 +335,7 @@ Adding your own applications with custom icons and MIME types to the installer i
|
||||
1. Modify the name and variables to reflect the appropriate/desired values for your application.
|
||||
2. Replace `icon.svg` with an SVG for your application (ensuring the icon is appropriately licensed).
|
||||
3. Remove and reinstall WinApps.
|
||||
4. (Optional, but strongly encouraged) Submit a pull request to add your application to WinApps as an officially supported application once you have tested your configuration files to verify functionality.
|
||||
4. Submit a pull request to add your application to WinApps as an officially supported application once you have tested and verified your configuration (optional, but encouraged).
|
||||
|
||||
## Running Applications Manually
|
||||
WinApps offers a manual mode for running applications that were not configured by the WinApps installer. This is completed with the `manual` flag. Executables that are in the Windows PATH do not require full path definition.
|
||||
|
158
bin/winapps
@ -9,10 +9,11 @@ readonly CLEAR_TEXT="\033[0m" # Clear
|
||||
readonly EC_MISSING_CONFIG=1
|
||||
readonly EC_MISSING_FREERDP=2
|
||||
readonly EC_NOT_IN_GROUP=3
|
||||
readonly EC_VM_NOT_RUNNING=4
|
||||
readonly EC_VM_NO_IP=5
|
||||
readonly EC_VM_BAD_PORT=6
|
||||
readonly EC_NOT_RUNNING=4
|
||||
readonly EC_NO_IP=5
|
||||
readonly EC_BAD_PORT=6
|
||||
readonly EC_UNSUPPORTED_APP=7
|
||||
readonly EC_INVALID_FLAVOR=8
|
||||
|
||||
# PATHS
|
||||
readonly APPDATA_PATH="${HOME}/.local/share/winapps"
|
||||
@ -24,8 +25,9 @@ readonly CONFIG_PATH="${HOME}/.config/winapps/winapps.conf"
|
||||
readonly SCRIPT_DIR_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
|
||||
# OTHER
|
||||
readonly VM_NAME="RDPWindows"
|
||||
readonly VM_NAME="RDPWindows" # FOR 'libvirt' ONLY
|
||||
readonly RDP_PORT=3389
|
||||
readonly DOCKER_IP="127.0.0.1"
|
||||
# shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment.
|
||||
readonly RUN="$(date)-${RANDOM}"
|
||||
|
||||
@ -35,6 +37,7 @@ RDP_USER=""
|
||||
RDP_PASS=""
|
||||
RDP_DOMAIN=""
|
||||
RDP_IP=""
|
||||
WAFLAVOR="docker"
|
||||
RDP_FLAGS=""
|
||||
FREERDP_COMMAND=""
|
||||
RDP_SCALE=100
|
||||
@ -68,26 +71,31 @@ function waThrowExit() {
|
||||
echo " sudo usermod -a -G libvirt $(whoami)"
|
||||
echo " sudo usermod -a -G kvm $(whoami)"
|
||||
;;
|
||||
"$EC_VM_NOT_RUNNING")
|
||||
dprint "ERROR: VM NOT RUNNING. EXITING."
|
||||
echo -e "${ERROR_TEXT}ERROR: VM NOT RUNNING.${CLEAR_TEXT}"
|
||||
echo "Please ensure the Windows VM is powered on."
|
||||
"$EC_NOT_RUNNING")
|
||||
dprint "ERROR: WINDOWS NOT RUNNING. EXITING."
|
||||
echo -e "${ERROR_TEXT}ERROR: WINDOWS NOT RUNNING.${CLEAR_TEXT}"
|
||||
echo "Please ensure Windows is running."
|
||||
;;
|
||||
"$EC_VM_NO_IP")
|
||||
dprint "ERROR: VM UNREACHABLE. EXITING."
|
||||
echo -e "${ERROR_TEXT}ERROR: VM UNREACHABLE.${CLEAR_TEXT}"
|
||||
echo "Please ensure the Windows VM is assigned an IP address."
|
||||
"$EC_NO_IP")
|
||||
dprint "ERROR: WINDOWS UNREACHABLE. EXITING."
|
||||
echo -e "${ERROR_TEXT}ERROR: WINDOWS UNREACHABLE.${CLEAR_TEXT}"
|
||||
echo "Please ensure Windows is assigned an IP address."
|
||||
;;
|
||||
"$EC_VM_BAD_PORT")
|
||||
"$EC_BAD_PORT")
|
||||
dprint "ERROR: RDP PORT CLOSED. EXITING."
|
||||
echo -e "${ERROR_TEXT}ERROR: RDP PORT CLOSED.${CLEAR_TEXT}"
|
||||
echo "Please ensure Remote Desktop is correctly configured on the Windows VM."
|
||||
echo "Please ensure Remote Desktop is correctly configured on Windows."
|
||||
;;
|
||||
"$EC_UNSUPPORTED_APP")
|
||||
dprint "ERROR: APPLICATION NOT FOUND. EXITING."
|
||||
echo -e "${ERROR_TEXT}ERROR: APPLICATION NOT FOUND.${CLEAR_TEXT}"
|
||||
echo "Please ensure the program is correctly configured as an officially supported application."
|
||||
;;
|
||||
"$EC_INVALID_FLAVOR")
|
||||
dprint "ERROR: INVALID FLAVOR. EXITING."
|
||||
echo -e "${ERROR_TEXT}ERROR: INVALID FLAVOR.${CLEAR_TEXT}"
|
||||
echo "Please ensure 'docker', 'podman' or 'libvirt' are specified as the flavor in the WinApps configuration file."
|
||||
;;
|
||||
esac
|
||||
|
||||
# Provide generic advice.
|
||||
@ -104,6 +112,40 @@ function dprint() {
|
||||
[ "$DEBUG" = "true" ] && echo "[$RUN] $1" >>"$LOG_PATH"
|
||||
}
|
||||
|
||||
# Name: 'waFixScale'
|
||||
# Role: Since FreeRDP only supports '/scale' values of 100, 140 or 180, find the closest supported argument to the user's configuration.
|
||||
function waFixScale() {
|
||||
# Define variables.
|
||||
local OLD_SCALE=100
|
||||
local VALID_SCALE_1=100
|
||||
local VALID_SCALE_2=140
|
||||
local VALID_SCALE_3=180
|
||||
|
||||
# Check for an unsupported value.
|
||||
if [ "$RDP_SCALE" != "$VALID_SCALE_1" ] && [ "$RDP_SCALE" != "$VALID_SCALE_2" ] && [ "$RDP_SCALE" != "$VALID_SCALE_3" ]; then
|
||||
# Save the unsupported scale.
|
||||
OLD_SCALE="$RDP_SCALE"
|
||||
|
||||
# Calculate the absolute differences.
|
||||
local DIFF_1=$(( RDP_SCALE > VALID_SCALE_1 ? RDP_SCALE - VALID_SCALE_1 : VALID_SCALE_1 - RDP_SCALE ))
|
||||
local DIFF_2=$(( RDP_SCALE > VALID_SCALE_2 ? RDP_SCALE - VALID_SCALE_2 : VALID_SCALE_2 - RDP_SCALE ))
|
||||
local DIFF_3=$(( RDP_SCALE > VALID_SCALE_3 ? RDP_SCALE - VALID_SCALE_3 : VALID_SCALE_3 - RDP_SCALE ))
|
||||
|
||||
# Set the final scale to the valid scale value with the smallest absolute difference.
|
||||
if (( DIFF_1 <= DIFF_2 && DIFF_1 <= DIFF_3 )); then
|
||||
RDP_SCALE="$VALID_SCALE_1"
|
||||
elif (( DIFF_2 <= DIFF_1 && DIFF_2 <= DIFF_3 )); then
|
||||
RDP_SCALE="$VALID_SCALE_2"
|
||||
else
|
||||
RDP_SCALE="$VALID_SCALE_3"
|
||||
fi
|
||||
|
||||
# Print feedback.
|
||||
dprint "WARNING: Unsupported RDP_SCALE value '${OLD_SCALE}'. Defaulting to '${RDP_SCALE}'."
|
||||
echo "WARNING: Unsupported RDP_SCALE value '${OLD_SCALE}' detected. Defaulting to '${RDP_SCALE}'."
|
||||
fi
|
||||
}
|
||||
|
||||
# Name: 'waLoadConfig'
|
||||
# Role: Load the variables within the WinApps configuration file.
|
||||
function waLoadConfig() {
|
||||
@ -118,8 +160,8 @@ function waLoadConfig() {
|
||||
# Update 'MULTI_FLAG' based on 'MULTIMON'.
|
||||
MULTI_FLAG=$([[ $MULTIMON == "true" ]] && echo "/multimon" || echo "+span")
|
||||
|
||||
# Append additional flags or parameters to FreeRDP.
|
||||
[[ -n $RDP_FLAGS ]] && FREERDP_COMMAND="${FREERDP_COMMAND} ${RDP_FLAGS}"
|
||||
# Update $RDP_SCALE.
|
||||
waFixScale
|
||||
}
|
||||
|
||||
# Name: 'waLastRun'
|
||||
@ -141,20 +183,25 @@ function waLastRun() {
|
||||
dprint "THIS_RUN: ${CURR_RUN_UNIX_TIME}"
|
||||
}
|
||||
|
||||
# Name: 'waGetFreeRDPCommand'
|
||||
# Role: Determine the correct FreeRDP command to use.
|
||||
function waGetFreeRDPCommand() {
|
||||
# Declare variables.
|
||||
local FREERDP_MAJOR_VERSION="" # Stores the major version of the installed copy of FreeRDP.
|
||||
|
||||
# Attempt to set a FreeRDP command if the command variable is empty.
|
||||
if [ -z "$FREERDP_COMMAND" ]; then
|
||||
# Check for 'xfreerdp'.
|
||||
if command -v xfreerdp &>/dev/null; then
|
||||
# Check FreeRDP major version is 3 or greater.
|
||||
FREERDP_MAJOR_VERSION=$(xfreerdp --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | cut -d'.' -f1)
|
||||
FREERDP_MAJOR_VERSION=$(xfreerdp --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | head -n 1 | cut -d'.' -f1)
|
||||
if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then
|
||||
FREERDP_COMMAND="xfreerdp"
|
||||
fi
|
||||
# Check for 'xfreerdp3'.
|
||||
elif command -v xfreerdp3 &>/dev/null; then
|
||||
# Check FreeRDP major version is 3 or greater.
|
||||
FREERDP_MAJOR_VERSION=$(xfreerdp3 --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | cut -d'.' -f1)
|
||||
FREERDP_MAJOR_VERSION=$(xfreerdp3 --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | head -n 1 | cut -d'.' -f1)
|
||||
if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then
|
||||
FREERDP_COMMAND="xfreerdp3"
|
||||
fi
|
||||
@ -176,6 +223,10 @@ function waGetFreeRDPCommand() {
|
||||
|
||||
if command -v "$FREERDP_COMMAND" &>/dev/null || [ "$FREERDP_COMMAND" = "flatpak run --command=xfreerdp com.freerdp.FreeRDP" ]; then
|
||||
dprint "Using FreeRDP command '${FREERDP_COMMAND}'."
|
||||
|
||||
# Append additional flags or parameters to FreeRDP.
|
||||
# These additional flags are loaded prior in 'waLoadConfig'.
|
||||
[[ -n $RDP_FLAGS ]] && FREERDP_COMMAND="${FREERDP_COMMAND} ${RDP_FLAGS}"
|
||||
else
|
||||
waThrowExit "$EC_MISSING_FREERDP"
|
||||
fi
|
||||
@ -194,28 +245,54 @@ function waCheckGroupMembership() {
|
||||
}
|
||||
|
||||
# Name: 'waCheckVMRunning'
|
||||
# Role: Throw an error if the Windows VM is not running.
|
||||
# Role: Throw an error if the Windows 'libvirt' VM is not running.
|
||||
function waCheckVMRunning() {
|
||||
! virsh list --state-running --name | grep -q "^${VM_NAME}$" && waThrowExit "$EC_VM_NOT_RUNNING"
|
||||
! virsh list --state-running --name | grep -q "^${VM_NAME}$" && waThrowExit "$EC_NOT_RUNNING"
|
||||
}
|
||||
|
||||
# Name: 'waCheckVMContactable'
|
||||
# Role: Assesses whether the Windows VM can be contacted.
|
||||
function waCheckVMContactable() {
|
||||
# Name: 'waCheckContainerRunning'
|
||||
# Role: Throw an error if the Docker container is not running.
|
||||
function waCheckContainerRunning() {
|
||||
# Declare variables.
|
||||
local CONTAINER_STATE=""
|
||||
|
||||
# Determine container state (docker).
|
||||
if command -v docker &>/dev/null; then
|
||||
CONTAINER_STATE=$(docker ps --filter name="WinApps" --format '{{.Status}}')
|
||||
fi
|
||||
|
||||
# Determine container state (podman).
|
||||
if [ -z "$CONTAINER_STATE" ]; then
|
||||
CONTAINER_STATE=$(podman ps --filter name="WinApps" --format '{{.Status}}')
|
||||
fi
|
||||
|
||||
CONTAINER_STATE=${CONTAINER_STATE,,} # Convert the string to lowercase.
|
||||
CONTAINER_STATE=${CONTAINER_STATE%% *} # Extract the first word.
|
||||
|
||||
# Check container state.
|
||||
[[ "$CONTAINER_STATE" != "up" ]] && waThrowExit "$EC_NOT_RUNNING"
|
||||
}
|
||||
|
||||
# Name: 'waCheckPortOpen'
|
||||
# Role: Assesses whether the RDP port on Windows is open.
|
||||
function waCheckPortOpen() {
|
||||
# Declare variables.
|
||||
local VM_MAC="" # Stores the MAC address of the Windows VM.
|
||||
|
||||
# Obtain Windows VM IP Address
|
||||
if [ -z "$RDP_IP" ]; then
|
||||
# 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.
|
||||
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=$(arp -n | grep "$VM_MAC" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}") # VM IP address.
|
||||
[ -z "$RDP_IP" ] && waThrowExit "$EC_VM_NO_IP"
|
||||
[ -z "$RDP_IP" ] && waThrowExit "$EC_NO_IP"
|
||||
fi
|
||||
|
||||
# Check for an open RDP port.
|
||||
timeout 5 nc -z "$RDP_IP" "$RDP_PORT" &>/dev/null || waThrowExit "$EC_VM_BAD_PORT"
|
||||
timeout 5 nc -z "$RDP_IP" "$RDP_PORT" &>/dev/null || waThrowExit "$EC_BAD_PORT"
|
||||
}
|
||||
|
||||
# Name: 'waRunCommand'
|
||||
# Role: Run the requested WinApps command.
|
||||
function waRunCommand() {
|
||||
# Declare variables.
|
||||
local ICON=""
|
||||
@ -223,7 +300,7 @@ function waRunCommand() {
|
||||
|
||||
# Run option.
|
||||
if [ "$1" = "windows" ]; then
|
||||
# Open Windows VM.
|
||||
# Open Windows RDP session.
|
||||
dprint "WINDOWS"
|
||||
$FREERDP_COMMAND \
|
||||
/d:"$RDP_DOMAIN" \
|
||||
@ -233,6 +310,7 @@ function waRunCommand() {
|
||||
+dynamic-resolution \
|
||||
+auto-reconnect \
|
||||
+home-drive \
|
||||
/audio-mode:1 \
|
||||
/wm-class:"Microsoft Windows" \
|
||||
/v:"$RDP_IP" &>/dev/null &
|
||||
elif [ "$1" = "manual" ]; then
|
||||
@ -247,6 +325,7 @@ function waRunCommand() {
|
||||
+auto-reconnect \
|
||||
+clipboard \
|
||||
+home-drive \
|
||||
/audio-mode:1 \
|
||||
-wallpaper \
|
||||
+dynamic-resolution \
|
||||
"$MULTI_FLAG" \
|
||||
@ -281,6 +360,7 @@ function waRunCommand() {
|
||||
+auto-reconnect \
|
||||
+clipboard \
|
||||
+home-drive \
|
||||
/audio-mode:1 \
|
||||
-wallpaper \
|
||||
+dynamic-resolution \
|
||||
"$MULTI_FLAG" \
|
||||
@ -302,6 +382,7 @@ function waRunCommand() {
|
||||
+auto-reconnect \
|
||||
+clipboard \
|
||||
+home-drive \
|
||||
/audio-mode:1 \
|
||||
-wallpaper \
|
||||
+dynamic-resolution \
|
||||
"$MULTI_FLAG" \
|
||||
@ -322,8 +403,23 @@ mkdir -p "$APPDATA_PATH"
|
||||
waLastRun
|
||||
waLoadConfig
|
||||
waGetFreeRDPCommand
|
||||
waCheckGroupMembership
|
||||
waCheckVMRunning
|
||||
waCheckVMContactable
|
||||
|
||||
# If using podman backend, modify the FreeRDP command to enter a new namespace.
|
||||
if [ "$WAFLAVOR" = "podman" ]; then
|
||||
FREERDP_COMMAND="podman unshare --rootless-netns ${FREERDP_COMMAND}"
|
||||
fi
|
||||
|
||||
if [ "$WAFLAVOR" = "docker" ] || [ "$WAFLAVOR" = "podman" ]; then
|
||||
RDP_IP="$DOCKER_IP"
|
||||
waCheckContainerRunning
|
||||
elif [ "$WAFLAVOR" = "libvirt" ]; then
|
||||
waCheckGroupMembership
|
||||
waCheckVMRunning
|
||||
else
|
||||
waThrowExit "$EC_INVALID_FLAVOR"
|
||||
fi
|
||||
|
||||
waCheckPortOpen
|
||||
waRunCommand "$@"
|
||||
|
||||
dprint "END"
|
||||
|
46
compose.yaml
@ -1,23 +1,41 @@
|
||||
name: "winapps"
|
||||
# For documentation, FAQ, additional configuration options and technical help, visit: https://github.com/dockur/windows
|
||||
|
||||
name: "winapps" # Docker Compose Project Name.
|
||||
volumes:
|
||||
# Create Volume 'data'.
|
||||
# Located @ '/var/lib/docker/volumes/winapps_data/_data' (Docker).
|
||||
# Located @ '/var/lib/containers/storage/volumes/winapps_data/_data' or '~/.local/share/containers/storage/volumes/winapps_data/_data' (Podman).
|
||||
data:
|
||||
|
||||
services:
|
||||
windows:
|
||||
image: dockurr/windows
|
||||
container_name: windows
|
||||
image: dockurr/windows # https://hub.docker.com/r/dockurr/windows
|
||||
container_name: WinApps # Created Docker VM Name.
|
||||
environment:
|
||||
# Version of Windows to configure. For valid options, visit:
|
||||
# https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-select-the-windows-version
|
||||
# https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-install-a-custom-image
|
||||
VERSION: "tiny11"
|
||||
RAM_SIZE: "4G"
|
||||
CPU_CORES: "4"
|
||||
privileged: true
|
||||
RAM_SIZE: "4G" # RAM allocated to the Windows VM.
|
||||
CPU_CORES: "4" # CPU cores allocated to the Windows VM.
|
||||
DISK_SIZE: "64G" # Size of the primary hard disk.
|
||||
#DISK2_SIZE: "32G" # Uncomment to add an additional hard disk to the Windows VM. Ensure it is mounted as a volume below.
|
||||
#USERNAME: "Docker" # Uncomment to set a custom Windows username. The default is 'Docker'.
|
||||
#PASSWORD: "" # Uncomment to set a password for the Windows user. There is no default password.
|
||||
HOME: "${HOME}" # Set path to Linux user home folder.
|
||||
privileged: true # Grant the Windows VM extended privileges.
|
||||
ports:
|
||||
- 8006:8006
|
||||
- 3389:3389/tcp
|
||||
- 3389:3389/udp
|
||||
stop_grace_period: 2m
|
||||
restart: on-failure
|
||||
- 8006:8006 # Map '8006' on Linux host to '8006' on Windows VM --> For VNC Web Interface @ http://127.0.0.1:8006.
|
||||
- 3389:3389/tcp # Map '3389' on Linux host to '3389' on Windows VM --> For Remote Desktop Protocol (RDP).
|
||||
- 3389:3389/udp # Map '3389' on Linux host to '3389' on Windows VM --> For Remote Desktop Protocol (RDP).
|
||||
stop_grace_period: 120s # Wait 120 seconds before sending SIGTERM when attempting to shut down the Windows VM.
|
||||
restart: on-failure # Restart the Windows VM if the exit code indicates an error.
|
||||
volumes:
|
||||
- data:/storage
|
||||
- ./oem:/oem
|
||||
- data:/storage # Mount volume 'data' to use as Windows 'C:' drive.
|
||||
- ${HOME}:/shared # Mount Linux user home directory @ '\\host.lan\Data'.
|
||||
#- /path/to/second/hard/disk:/storage2 # Uncomment to mount the second hard disk within the Windows VM. Ensure 'DISK2_SIZE' is specified above.
|
||||
- ./oem:/oem # Enables automatic post-install execution of 'oem/install.bat', applying Windows registry modifications contained within 'oem/RDPApps.reg'.
|
||||
#- /path/to/windows/install/media.iso:/custom.iso # Uncomment to use a custom Windows ISO. If specified, 'VERSION' (e.g. 'tiny11') will be ignored.
|
||||
devices:
|
||||
- /dev/kvm # Enable KVM.
|
||||
#- /dev/sdX:/disk1 # Uncomment to mount a disk directly within the Windows VM (Note: 'disk1' will be mounted as the main drive).
|
||||
#- /dev/sdY:/disk2 # Uncomment to mount a disk directly within the Windows VM (Note: 'disk2' and higher will be mounted as secondary drives).
|
||||
|
135
docs/docker.md
@ -1,96 +1,87 @@
|
||||
# Creating a Virtual Machine in Docker
|
||||
# Creating a Windows VM in `Docker` or `Podman`
|
||||
Although WinApps supports using `QEMU+KVM+libvirt` as a backend for running Windows virtual machines, it is recommended to use `Docker` or `Podman`. These backends automate the setup process, eliminating the need for manual configuration and optimisation of the Windows virtual machine.
|
||||
|
||||
## Why Docker?
|
||||
> [!IMPORTANT]
|
||||
Running a Windows virtual machine using `Docker` or `Podman` as a backend is only possible on GNU/Linux systems. This is due to the necessity of kernel interfaces, such as the KVM hypervisor, for achieving acceptable performance. The performance of the virtual machine can vary based on the version of the Linux kernel, with newer releases generally offering better performance.
|
||||
|
||||
While working with `virsh` is completely fine for WinApps, you have to set up and optimize your VM manually.
|
||||
Docker, on the other hand, sets up most of the stuff automatically and makes the VM highly portable between Linux distros.
|
||||
> [!IMPORTANT]
|
||||
> WinApps does NOT officially support versions of Windows prior to Windows 10. Despite this, it may be possible to achieve a successful installation with some additional experimentation. If you find a way to achieve this, please share your solution through a pull request for the benefit of other users.
|
||||
|
||||
# Requirements
|
||||
## `Docker`
|
||||
### Installation
|
||||
You can find a guide for installing `Docker Engine` [here](https://docs.docker.com/engine/install/).
|
||||
|
||||
Since Docker manages the dependencies of the container automatically, you only need to install Docker itself.
|
||||
### Setup `Docker` Container
|
||||
WinApps utilises `docker compose` to configure Windows VMs. A suitable [`compose.yaml`](https://github.com/winapps-org/winapps/blob/main/compose.yaml) file is included in the root directory of the WinApps repository.
|
||||
|
||||
You can try using Podman too because of their faster container startup times,
|
||||
but note that Podman and Docker aren't always fully interchangeable. In case you want to follow this guide using Podman,
|
||||
you will have to install the `docker` CLI to be able to run `docker compose` commands.
|
||||
You will also have to enable the Podman socket. Refer to the Podman docs for how to do that.
|
||||
Prior to initiating the installation, you can modify the RAM and number of CPU cores available to the Windows VM by changing `RAM_SIZE` and `CPU_CORES` within `compose.yaml`.
|
||||
|
||||
See:
|
||||
|
||||
- [Podman installation docs](https://podman.io/docs/installation)
|
||||
- [Docker installation docs](https://docs.docker.com/engine/install)
|
||||
- [Using `docker compose` with Podman](https://www.redhat.com/sysadmin/podman-docker-compose) (slightly outdated)
|
||||
It is also possible to specify the version of Windows you wish to install within `compose.yaml` by modifying `VERSION`.
|
||||
|
||||
> [!NOTE]
|
||||
> This will only work on Linux systems since the VM needs some kernel interfaces (like KVM). Because of this,
|
||||
> performance can vary depending on kernel version (newer will likely perform better).
|
||||
> WinApps uses a stripped-down Windows installation by default. Although this is recommended, you can request a stock Windows installation by changing `VERSION` to one of the versions listed in the README of the [original GitHub repository](https://github.com/dockur/windows).
|
||||
|
||||
# Setup Docker Container
|
||||
Please refer to the [original GitHub repository](https://github.com/dockur/windows) for more information on additional configuration options.
|
||||
|
||||
The easiest way to set up a Windows VM is by using docker compose. A compose file that looks like this is already shipped with WinApps:
|
||||
|
||||
```yaml
|
||||
name: "winapps"
|
||||
|
||||
volumes:
|
||||
data:
|
||||
|
||||
services:
|
||||
windows:
|
||||
image: dockurr/windows
|
||||
container_name: windows
|
||||
environment:
|
||||
VERSION: "tiny11"
|
||||
RAM_SIZE: "4G"
|
||||
CPU_CORES: "4"
|
||||
privileged: true
|
||||
ports:
|
||||
- 8006:8006
|
||||
- 3389:3389/tcp
|
||||
- 3389:3389/udp
|
||||
stop_grace_period: 2m
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- data:/storage
|
||||
### Installing Windows
|
||||
After navigating into the cloned WinApps repository, you can initiate the Windows installation using `docker compose`.
|
||||
```bash
|
||||
docker compose up
|
||||
```
|
||||
|
||||
Now you can tune the RAM/usage by changing `RAM_SIZE` & `CPU_CORES`. You can also specify
|
||||
the Windows versions you want to use. You might also want to take a look at the [repo of the Docker image](https://github.com/dockur/windows) for further information.
|
||||
You can then access the Windows virtual machine via a VNC connection to complete the Windows setup by navigating to http://127.0.0.1:8006 in your web browser.
|
||||
|
||||
This compose file uses Windows 11 by default. You can use Windows 10 by changing the `VERSION` to `tiny10`.
|
||||
### Installing WinApps
|
||||
`Docker` simplifies the WinApps installation process by eliminating the need for any additional configuration of the Windows virtual machine. Once the Windows virtual machine is up and running, you can directly launch the WinApps installer, which should automatically detect and interface with Windows.
|
||||
|
||||
```bash
|
||||
./installer.sh
|
||||
```
|
||||
|
||||
### Subsequent Use
|
||||
```bash
|
||||
docker compose start # Power on the Windows VM
|
||||
docker compose pause # Pause the Windows VM
|
||||
docker compose unpause # Resume the Windows VM
|
||||
docker compose restart # Restart the Windows VM
|
||||
docker compose stop # Gracefully shut down the Windows VM
|
||||
docker compose kill # Force shut down the Windows VM
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> We use a stripped-down Windows installation by default. This is recommended,
|
||||
> but you can still opt for stock Windows by changing the version to one of the versions listed in
|
||||
> the README of the images repository linked above.
|
||||
> The above `docker compose` commands must be run within the same directory containing `compose.yaml`.
|
||||
|
||||
## `Podman`
|
||||
### Installation
|
||||
1. Install `Podman` using [this guide](https://podman.io/docs/installation).
|
||||
2. Install `podman-compose` using [this guide](https://github.com/containers/podman-compose?tab=readme-ov-file#installation).
|
||||
|
||||
### Setup `Podman` Container
|
||||
Please follow the [`docker` instructions](#setup-docker-container).
|
||||
|
||||
> [!NOTE]
|
||||
> We don't officially support older versions than Windows 10. However, they might still work with some additional tuning.
|
||||
> Ensure `WAFLAVOR` is set to `"podman"` in `~/.config/winapps/winapps.conf`.
|
||||
|
||||
You can now just run:
|
||||
|
||||
```shell
|
||||
docker compose up -d
|
||||
### Installing Windows
|
||||
After navigating into the cloned WinApps repository, you can initiate the Windows installation using `podman-compose`.
|
||||
```bash
|
||||
podman-compose up
|
||||
```
|
||||
|
||||
to run the VM in the background.
|
||||
You can then access the Windows virtual machine via a VNC connection to complete the Windows setup by navigating to http://127.0.0.1:8006 in your web browser.
|
||||
|
||||
After this, just open http://127.0.0.1:8006 in your web browser and wait for the Windows installation to finish.
|
||||
### Installing WinApps
|
||||
Please follow the [`docker` instructions](#installing-winapps).
|
||||
|
||||
> [!WARNING]
|
||||
> Make sure to change the `RDP_IP` in your WinApps config to `127.0.0.1`.
|
||||
|
||||
Now you should be ready to go and try to connect to your VM with WinApps.
|
||||
|
||||
For stopping the VM, just use:
|
||||
|
||||
```shell
|
||||
docker compose stop
|
||||
### Subsequent Use
|
||||
```bash
|
||||
podman-compose start # Power on the Windows VM
|
||||
podman-compose pause # Pause the Windows VM
|
||||
podman-compose unpause # Resume the Windows VM
|
||||
podman-compose restart # Restart the Windows VM
|
||||
podman-compose stop # Gracefully shut down the Windows VM
|
||||
podman-compose kill # Force shut down the Windows VM
|
||||
```
|
||||
|
||||
For starting again afterward, use:
|
||||
|
||||
```shell
|
||||
docker compose start
|
||||
```
|
||||
|
||||
(All compose commands have to be run from the directory where the `compose.yaml` is located.)
|
||||
> [!NOTE]
|
||||
> The above `podman-compose` commands must be run within the same directory containing `compose.yaml`.
|
||||
|
555
docs/libvirt.md
Normal file
@ -0,0 +1,555 @@
|
||||
# Creating a `libvirt` Windows VM
|
||||
## Understanding The Virtualisation Stack
|
||||
This method of configuring a Windows virtual machine for use with WinApps is significantly more involved than utilising `Docker` or `Podman`. Nevertheless, expert users may prefer this method due to its greater flexibility and wider range of customisation options.
|
||||
|
||||
Before beginning, it is important to have a basic understanding of the various components involved in this particular method.
|
||||
|
||||
1. `QEMU` is a FOSS emulator that performs hardware virtualisation, enabling operating systems and applications designed for one architecture (e.g., aarch64) to run on systems with differing architectures (e.g., amd64). When used in conjunction with `KVM`, it can run virtual machines at near-native speed (provided the guest virtual machine matches the host architecture) by utilising hardware extensions like Intel VT-x or AMD-V.
|
||||
2. `KVM` is a Linux kernel module that enables the kernel to function as a type-1 hypervisor. `KVM` runs directly on the underlying hardware (as opposed to on top of the GNU/Linux host OS). For many workloads, the performance overhead is minimal, often in the range of 2-5%. `KVM` requires a CPU with hardware virtualisation extensions.
|
||||
3. `libvirt` is an open-source API, daemon, and management tool for orchestrating platform virtualisation. It provides a consistent and stable interface for managing various virtualisation technologies, including `KVM` and `QEMU` (as well as others). `libvirt` offers a wide range of functionality to control the lifecycle of virtual machines, storage, networks, and interfaces, making it easier to interact with virtualisation capabilities programmatically or via command-line tools.
|
||||
4. `virt-manager` (Virtual Machine Manager) is a GUI desktop application that provides an easy-to-use interface for creating, configuring and controlling virtual machines. `virt-manager` utilises `libvirt` as a backend.
|
||||
|
||||
Together, these components form a powerful and flexible virtualization stack, with `KVM` providing low-level kernel-based virtualisation capabilities, `QEMU` providing high-level userspace-based virtualisation functionality, `libvirt` managing the resources and `virt-manager` offering an intuitive graphical management interface.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/Virtualisation_Stack.svg" width="500px"/>
|
||||
</p>
|
||||
|
||||
## Prerequisites
|
||||
1. Ensure your CPU supports hardware virtualisation extensions by [reading this article](https://wiki.archlinux.org/title/KVM).
|
||||
|
||||
2. Install all dependencies by installing `virt-manager`. This will ensure that your package manager automatically installs all the necessary components.
|
||||
```bash
|
||||
sudo apt install virt-manager # Debian/Ubuntu
|
||||
sudo dnf install virt-manager # Fedora/RHEL
|
||||
sudo pacman -S virt-manager # Arch Linux
|
||||
sudo emerge app-emulation/virt-manager # Gentoo Linux
|
||||
```
|
||||
|
||||
3. Download a [Windows 10](https://www.microsoft.com/software-download/windows10ISO) or [Windows 11](https://www.microsoft.com/software-download/windows11) installation `.ISO` image.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 'Professional', 'Enterprise' or 'Server' editions of Windows are required to run RDP applications. Windows 'Home' will NOT suffice.
|
||||
|
||||
4. Download [VirtIO drivers](https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso) for the Windows virtual machine.
|
||||
|
||||
> [!NOTE]
|
||||
> VirtIO drivers enhance system performance and minimize overhead by enabling the Windows virtual machine to use specialised network and disk device drivers. These drivers are aware that they are operating inside a virtual machine, and cooperate with the hypervisor. This approach eliminates the need for the hypervisor to emulate physical hardware devices, which is a computationally expensive process. This setup allows guests to achieve high-performance network and disk operations, leveraging the benefits of paravirtualisation.
|
||||
> You can read more about `VirtIO` [here](https://wiki.libvirt.org/Virtio.html).
|
||||
|
||||
## Creating a Windows VM
|
||||
> [!NOTE] If you are an expert user, you may wish to:
|
||||
> - [Define a Windows virtual machine from an existing `.XML` file](#defining-windows-vm-from-xml)
|
||||
> - [Configure Rootless `libvirt`](#configuring-rootless-libvirt)
|
||||
|
||||
1. Open `virt-manager`.
|
||||
|
||||
> [!NOTE]
|
||||
> The name given to the application can vary between GNU/Linux distributions (e.g., 'Virtual Machines', 'Virtual Machine Manager', etc.)
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/00.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
2. Navigate to `Edit`→`Preferences`. Ensure `Enable XML editing` is enabled, then click the `Close` button.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/01.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
3. Create a new virtual machine by clicking the `+` button.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/02.png" width="500px" alt="Creating a new virtual machine in 'virt-manager'"/>
|
||||
</p>
|
||||
|
||||
4. Choose `Local install media` and click `Forward`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/03.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
5. Select the location of your Windows 10 or 11 `.ISO` by clicking `Browse...` and `Browse Local`. Ensure `Automatically detect from the installation media / source` is enabled.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/04_1.png" width="500px"/>
|
||||
<img src="./libvirt_images/04_2.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
5. Configure the RAM and CPU cores allocated to the Windows virtual machine. We recommend `2` CPUs and `4096MB` of RAM. We will use the `VirtIO` Memory Ballooning service, which means the virtual machine can use up to `4096MB` of memory, but it will only consume this amount if necessary.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/05.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
6. Configure the virtual disk by setting its maximum size. While this size represents the largest it can grow to, the disk will only use this space as needed.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/06.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
7. Name your virtual machine `RDPWindows` to ensure it is recognized by WinApps, and select the option to `Customize configuration before installation`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/07.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
8. After clicking `Finish`, select `Copy host CPU configuration` under 'CPUs', and then click `Apply`.
|
||||
|
||||
> [!NOTE]
|
||||
> Sometimes this feature gets disabled after installing Windows. Make sure to check and re-enable this option after the installation is complete.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/08.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
9. (Optional) Configure 'CPU pinning' by following [this excellent guide](https://wiki.archlinux.org/title/PCI_passthrough_via_OVMF#CPU_pinning).
|
||||
|
||||
> [!NOTE]
|
||||
> CPU pinning involves assigning specific physical CPU cores to a virtual machine. This can improve performance by reducing context switching and ensuring that the VM's workload consistently uses the same cores, leading to better CPU cache utilisation.
|
||||
|
||||
5. Navigate to the `XML` tab, and edit the `<clock>` section to disable all timers except for the hypervclock, thereby drastically reducing idle CPU usage. Once changed, click `Apply`.
|
||||
```xml
|
||||
<clock offset='localtime'>
|
||||
<timer name='rtc' present='no' tickpolicy='catchup'/>
|
||||
<timer name='pit' present='no' tickpolicy='delay'/>
|
||||
<timer name='hpet' present='no'/>
|
||||
<timer name='kvmclock' present='no'/>
|
||||
<timer name='hypervclock' present='yes'/>
|
||||
</clock>
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/09.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
6. Enable Hyper-V enlightenments by adding the following to the `<hyperv>` section. Once changed, click `Apply`.
|
||||
|
||||
```xml
|
||||
<hyperv>
|
||||
<relaxed state='on'/>
|
||||
<vapic state='on'/>
|
||||
<spinlocks state='on' retries='8191'/>
|
||||
<vpindex state='on'/>
|
||||
<synic state='on'/>
|
||||
<stimer state='on'>
|
||||
<direct state='on'/>
|
||||
</stimer>
|
||||
<reset state='on'/>
|
||||
<frequencies state='on'/>
|
||||
<reenlightenment state='on'/>
|
||||
<tlbflush state='on'/>
|
||||
<ipi state='on'/>
|
||||
</hyperv>
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Hyper-V enlightenments make Windows (and other Hyper-V guests) think they are running on top of a Hyper-V compatible hypervisor. This enables use of Hyper-V specific features, allowing `KVM` to implement paravirtualised interfaces for improved virtual machine performance.
|
||||
|
||||
7. In the 'Memory' section, set the `Current allocation` to the minimum amount of memory you want the virtual machine to use, with a recommended value of `1024MB`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/10.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
8. (Optional) Under `Boot Options`, enable `Start virtual machine on host boot up`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/11.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
9. Navigate to 'SATA Disk 1' and set the `Disk bus` type to `VirtIO`. This allows disk access to be paravirtualised, improving virtual machine performance.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/12.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
10. Navigate to 'NIC' and set the `Device model` type to `virtio` to enable paravirtualised networking.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/13.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
11. Click the `Add Hardware` button in the lower left, and choose `Storage`. For `Device type`, select `CDROM device` and choose the VirtIO driver `.ISO` you downloaded earlier. Click `Finish` to add the new CD-ROM device.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you skip this step, the Windows installer will fail to recognise and list the virtual hard drive you created earlier.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/14.png" width="500px"/>
|
||||
</p>
|
||||
|
||||
12. Click `Begin Installation` in the top left.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/15.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
### Example `.XML` File
|
||||
Below is an example `.XML` file that describes a Windows 11 virtual machine.
|
||||
|
||||
```xml
|
||||
<domain type="kvm">
|
||||
<name>RDPWindows</name>
|
||||
<uuid>4d76e36e-c632-43e0-83c0-dc9f36c2823a</uuid>
|
||||
<metadata>
|
||||
<libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
|
||||
<libosinfo:os id="http://microsoft.com/win/11"/>
|
||||
</libosinfo:libosinfo>
|
||||
</metadata>
|
||||
<memory unit="KiB">8388608</memory>
|
||||
<currentMemory unit="KiB">8388608</currentMemory>
|
||||
<vcpu placement="static">4</vcpu>
|
||||
<cputune>
|
||||
<vcpupin vcpu="0" cpuset="2"/>
|
||||
<vcpupin vcpu="1" cpuset="6"/>
|
||||
<vcpupin vcpu="2" cpuset="3"/>
|
||||
<vcpupin vcpu="3" cpuset="7"/>
|
||||
</cputune>
|
||||
<os firmware="efi">
|
||||
<type arch="x86_64" machine="pc-q35-8.1">hvm</type>
|
||||
<firmware>
|
||||
<feature enabled="yes" name="enrolled-keys"/>
|
||||
<feature enabled="yes" name="secure-boot"/>
|
||||
</firmware>
|
||||
<loader readonly="yes" secure="yes" type="pflash" format="qcow2">/usr/share/edk2/ovmf/OVMF_CODE_4M.secboot.qcow2</loader>
|
||||
<nvram template="/usr/share/edk2/ovmf/OVMF_VARS_4M.secboot.qcow2" format="qcow2">/var/lib/libvirt/qemu/nvram/RDPWindows_VARS.qcow2</nvram>
|
||||
<boot dev="hd"/>
|
||||
</os>
|
||||
<features>
|
||||
<acpi/>
|
||||
<apic/>
|
||||
<hyperv mode="custom">
|
||||
<relaxed state="on"/>
|
||||
<vapic state="on"/>
|
||||
<spinlocks state="on" retries="8191"/>
|
||||
<vpindex state="on"/>
|
||||
<synic state="on"/>
|
||||
<stimer state="on">
|
||||
<direct state="on"/>
|
||||
</stimer>
|
||||
<reset state="on"/>
|
||||
<frequencies state="on"/>
|
||||
<reenlightenment state="on"/>
|
||||
<tlbflush state="on"/>
|
||||
<ipi state="on"/>
|
||||
</hyperv>
|
||||
<vmport state="off"/>
|
||||
<smm state="on"/>
|
||||
</features>
|
||||
<cpu mode="host-passthrough" check="none" migratable="on">
|
||||
<topology sockets="1" dies="1" clusters="1" cores="2" threads="2"/>
|
||||
</cpu>
|
||||
<clock offset="localtime">
|
||||
<timer name="rtc" present="no" tickpolicy="catchup"/>
|
||||
<timer name="pit" present="no" tickpolicy="delay"/>
|
||||
<timer name="hpet" present="no"/>
|
||||
<timer name="kvmclock" present="no"/>
|
||||
<timer name="hypervclock" present="yes"/>
|
||||
</clock>
|
||||
<on_poweroff>destroy</on_poweroff>
|
||||
<on_reboot>restart</on_reboot>
|
||||
<on_crash>destroy</on_crash>
|
||||
<pm>
|
||||
<suspend-to-mem enabled="no"/>
|
||||
<suspend-to-disk enabled="no"/>
|
||||
</pm>
|
||||
<devices>
|
||||
<emulator>/usr/bin/qemu-system-x86_64</emulator>
|
||||
<disk type="file" device="disk">
|
||||
<driver name="qemu" type="qcow2" discard="unmap"/>
|
||||
<source file="/var/lib/libvirt/images/RDPWindows.qcow2"/>
|
||||
<target dev="vda" bus="virtio"/>
|
||||
<address type="pci" domain="0x0000" bus="0x04" slot="0x00" function="0x0"/>
|
||||
</disk>
|
||||
<disk type="file" device="cdrom">
|
||||
<driver name="qemu" type="raw"/>
|
||||
<target dev="sdb" bus="sata"/>
|
||||
<readonly/>
|
||||
<address type="drive" controller="0" bus="0" target="0" unit="1"/>
|
||||
</disk>
|
||||
<controller type="usb" index="0" model="qemu-xhci" ports="15">
|
||||
<address type="pci" domain="0x0000" bus="0x02" slot="0x00" function="0x0"/>
|
||||
</controller>
|
||||
<controller type="pci" index="0" model="pcie-root"/>
|
||||
<controller type="pci" index="1" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="1" port="0x10"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0" multifunction="on"/>
|
||||
</controller>
|
||||
<controller type="pci" index="2" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="2" port="0x11"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x1"/>
|
||||
</controller>
|
||||
<controller type="pci" index="3" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="3" port="0x12"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x2"/>
|
||||
</controller>
|
||||
<controller type="pci" index="4" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="4" port="0x13"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x3"/>
|
||||
</controller>
|
||||
<controller type="pci" index="5" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="5" port="0x14"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x4"/>
|
||||
</controller>
|
||||
<controller type="pci" index="6" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="6" port="0x15"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x5"/>
|
||||
</controller>
|
||||
<controller type="pci" index="7" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="7" port="0x16"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x6"/>
|
||||
</controller>
|
||||
<controller type="pci" index="8" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="8" port="0x17"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x7"/>
|
||||
</controller>
|
||||
<controller type="pci" index="9" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="9" port="0x18"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x0" multifunction="on"/>
|
||||
</controller>
|
||||
<controller type="pci" index="10" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="10" port="0x19"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x1"/>
|
||||
</controller>
|
||||
<controller type="pci" index="11" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="11" port="0x1a"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x2"/>
|
||||
</controller>
|
||||
<controller type="pci" index="12" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="12" port="0x1b"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x3"/>
|
||||
</controller>
|
||||
<controller type="pci" index="13" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="13" port="0x1c"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x4"/>
|
||||
</controller>
|
||||
<controller type="pci" index="14" model="pcie-root-port">
|
||||
<model name="pcie-root-port"/>
|
||||
<target chassis="14" port="0x1d"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x5"/>
|
||||
</controller>
|
||||
<controller type="sata" index="0">
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x1f" function="0x2"/>
|
||||
</controller>
|
||||
<controller type="virtio-serial" index="0">
|
||||
<address type="pci" domain="0x0000" bus="0x03" slot="0x00" function="0x0"/>
|
||||
</controller>
|
||||
<interface type="network">
|
||||
<mac address="52:54:00:81:ff:44"/>
|
||||
<source network="default"/>
|
||||
<model type="virtio"/>
|
||||
<address type="pci" domain="0x0000" bus="0x01" slot="0x00" function="0x0"/>
|
||||
</interface>
|
||||
<serial type="pty">
|
||||
<target type="isa-serial" port="0">
|
||||
<model name="isa-serial"/>
|
||||
</target>
|
||||
</serial>
|
||||
<console type="pty">
|
||||
<target type="serial" port="0"/>
|
||||
</console>
|
||||
<channel type="spicevmc">
|
||||
<target type="virtio" name="com.redhat.spice.0"/>
|
||||
<address type="virtio-serial" controller="0" bus="0" port="1"/>
|
||||
</channel>
|
||||
<input type="tablet" bus="usb">
|
||||
<address type="usb" bus="0" port="1"/>
|
||||
</input>
|
||||
<input type="mouse" bus="ps2"/>
|
||||
<input type="keyboard" bus="ps2"/>
|
||||
<tpm model="tpm-crb">
|
||||
<backend type="emulator" version="2.0"/>
|
||||
</tpm>
|
||||
<graphics type="spice" autoport="yes">
|
||||
<listen type="address"/>
|
||||
<image compression="off"/>
|
||||
</graphics>
|
||||
<sound model="ich9">
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x1b" function="0x0"/>
|
||||
</sound>
|
||||
<audio id="1" type="spice"/>
|
||||
<video>
|
||||
<model type="qxl" ram="65536" vram="65536" vgamem="16384" heads="1" primary="yes"/>
|
||||
<address type="pci" domain="0x0000" bus="0x00" slot="0x01" function="0x0"/>
|
||||
</video>
|
||||
<hostdev mode="subsystem" type="usb" managed="yes">
|
||||
<source>
|
||||
<vendor id="0x0bda"/>
|
||||
<product id="0x554e"/>
|
||||
</source>
|
||||
<address type="usb" bus="0" port="4"/>
|
||||
</hostdev>
|
||||
<redirdev bus="usb" type="spicevmc">
|
||||
<address type="usb" bus="0" port="2"/>
|
||||
</redirdev>
|
||||
<watchdog model="itco" action="reset"/>
|
||||
<memballoon model="virtio">
|
||||
<address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0"/>
|
||||
</memballoon>
|
||||
</devices>
|
||||
</domain>
|
||||
```
|
||||
|
||||
## Install Windows
|
||||
Install Windows as you would on any other machine.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/16.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
Once you get to the point of selecting the location for installation, you will see there are no disks available. This is because the `VirtIO driver` needs to be specified manually.
|
||||
1. Select `Load driver`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/17.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
2. The installer will then ask you to specify where the driver is located. Select the drive the `VirtIO` driver `.ISO` is mounted on.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/18.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
3. Choose the appropriate driver for the operating system you've selected, which is likely either the `w10` or `w11` drivers.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/19.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
4. The virtual hard disk should now be visible and available for selection.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/20.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
The next hurdle will be bypassing the network selection screen. As the `VirtIO` drivers
|
||||
for networking have not yet been loaded, the virtual machine will not be able to be connected to the internet.
|
||||
- For Windows 11: Press "Shift + F10" to open the command prompt. Type 'OOBE\BYPASSNRO' (without the surrounding quotation marks) and press Enter. The system will restart and now allow you to click `I don't have internet`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/21.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
- For Windows 10: Simply click `I don't have internet`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/22.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
Following the above, choose to `Continue with limited setup`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/23.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
## Final Configuration Steps
|
||||
Open `File Explorer` and navigate to the drive where the `VirtIO` driver `.ISO` is mounted. Run `virt-win-gt-64.exe` to launch the `VirtIO` driver installer.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/24.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
Leave everything as default and click `Next` through the installer. This will install all required device drivers as well as the 'Memory Ballooning' service.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/25.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
Once you finish the `VirtIO` driver installation, you will need to make some registry changes to enable RDP Applications to run on the system. Start by downloading the [RDPApps.reg](/install/RDPApps.reg) file, right-clicking on the `Raw` button, and clicking on `Save target as`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/26.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
Once you have downloaded the registry file, right-click on it, and choose `Merge`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/27.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
Rename the Windows virtual machine so that WinApps can locate it by navigating to the start menu and typing `About` to bring up the `About your PC` settings.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/28.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
Scroll down and click on `Rename this PC`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/29.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
Rename the PC to `RDPWindows`, but **DO NOT** restart the virtual machine.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/30.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
Scroll down to `Remote Desktop`, and enable `Enable Remote Desktop`.
|
||||
|
||||
<p align="center">
|
||||
<img src="./libvirt_images/31.png" width="700px"/>
|
||||
</p>
|
||||
|
||||
At this point, you will need to restart the Windows virtual machine.
|
||||
|
||||
You may now proceed to install other applications like 'Microsoft 365', 'Adobe Creative Cloud' or any other applications you would like to use through WinApps.
|
||||
|
||||
> [!NOTE]
|
||||
> You may also wish to install [Spice Guest Tools](https://www.spice-space.org/download/windows/spice-guest-tools/spice-guest-tools-latest.exe) inside the virtual machine, which enables features like auto-desktop resize and cut-and-paste when accessing the virtual machine through `virt-manager`. Since WinApps uses RDP, however, this is unnecessary if you don't plan to access the virtual machine via `virt-manager`.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Ensure `WAFLAVOR` is set to `"libvirt"` in your `~/.config/winapps/winapps.conf` to prevent WinApps looking for a `Docker` installation instead.
|
||||
|
||||
Finally, restart the virtual machine, but **DO NOT** log in. Close the virtual machine viewer and proceed to run the WinApps installation.
|
||||
|
||||
```bash
|
||||
./installer.sh
|
||||
```
|
||||
|
||||
## Expert Installations
|
||||
### Defining Windows VM from `.XML`
|
||||
This expert guide for `.XML` imports is specific to Ubuntu 20.04 and may not work on all hardware platforms. Please read this guide in conjunction with the steps outlined above.
|
||||
|
||||
1. Copy your Windows `.ISO` and `VirtIO` drivers `.ISO` files into the desired folder, updating the relevant paths in `kvm/RDPWindows.xml`.
|
||||
|
||||
2. Define a virtual machine called 'RDPWindows' from the sample XML file.
|
||||
``` bash
|
||||
virsh define kvm/RDPWindows.xml
|
||||
virsh autostart RDPWindows # Configure the VM to start automatically when the host machine boots
|
||||
```
|
||||
|
||||
3. Open the virtual machine's properties in `virt-manager` and ensure that `Copy host CPU configuration` is enabled.
|
||||
|
||||
4. Boot the virtual machine, install Windows and complete the final configuration steps as outlined in the main guide above.
|
||||
|
||||
### Configuring Rootless `libvirt`
|
||||
By default, `libvirt` requires sudo to run virtual machines. To enable your user to access `libvirt` and `kvm` without sudo, your user must be added to groups of the same name. Furthermore, modifications need to be made to `AppArmor` (if relevant to your distribution).
|
||||
|
||||
``` bash
|
||||
sudo sed -i "s/#user = "root"/user = "$(id -un)"/g" /etc/libvirt/qemu.conf # Uncomment the user = "root" line and replace root with the current username.
|
||||
sudo sed -i "s/#group = "root"/group = "$(id -gn)"/g" /etc/libvirt/qemu.conf # Uncomment the group = "root" line and replace root with the current user's primary group.
|
||||
sudo usermod -a -G kvm $(id -un) # Add the user to the 'kvm' group.
|
||||
sudo usermod -a -G libvirt $(id -un) # Add the user to the 'libvirt' group.
|
||||
sudo systemctl restart libvirtd # Restart the libvirt daemon.
|
||||
sudo ln -s /etc/apparmor.d/usr.sbin.libvirtd /etc/apparmor.d/disable/ # Disable AppArmor for the libvirt daemon by creating a symbolic link.
|
||||
```
|
||||
|
||||
You will need to reboot your system to ensure your user is added to the relevant groups.
|
||||
|
||||
> [!NOTE]
|
||||
> Systems with `SELinux` may also require security policy adjustments to enable correct functioning of the `libvirt` daemon.
|
BIN
docs/libvirt_images/00.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
docs/libvirt_images/01.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
docs/libvirt_images/02.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
docs/libvirt_images/03.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
docs/libvirt_images/04_1.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
docs/libvirt_images/04_2.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
docs/libvirt_images/05.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
docs/libvirt_images/06.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/libvirt_images/07.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
docs/libvirt_images/08.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
docs/libvirt_images/09.png
Normal file
After Width: | Height: | Size: 147 KiB |
BIN
docs/libvirt_images/10.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
docs/libvirt_images/11.png
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
docs/libvirt_images/12.png
Normal file
After Width: | Height: | Size: 88 KiB |
BIN
docs/libvirt_images/13.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
docs/libvirt_images/14.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
docs/libvirt_images/15.png
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
docs/libvirt_images/16.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
docs/libvirt_images/17.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
docs/libvirt_images/18.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
docs/libvirt_images/19.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
docs/libvirt_images/20.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
docs/libvirt_images/21.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
docs/libvirt_images/22.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/libvirt_images/23.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
docs/libvirt_images/24.png
Normal file
After Width: | Height: | Size: 199 KiB |
BIN
docs/libvirt_images/25.png
Normal file
After Width: | Height: | Size: 225 KiB |
BIN
docs/libvirt_images/26.png
Normal file
After Width: | Height: | Size: 198 KiB |
BIN
docs/libvirt_images/27.png
Normal file
After Width: | Height: | Size: 168 KiB |
BIN
docs/libvirt_images/28.png
Normal file
After Width: | Height: | Size: 178 KiB |
BIN
docs/libvirt_images/29.png
Normal file
After Width: | Height: | Size: 171 KiB |
BIN
docs/libvirt_images/30.png
Normal file
After Width: | Height: | Size: 157 KiB |
BIN
docs/libvirt_images/31.png
Normal file
After Width: | Height: | Size: 160 KiB |
180
docs/libvirt_images/Virtualisation_Stack.svg
Normal file
@ -0,0 +1,180 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 586 384" width="586" height="384">
|
||||
<title>Open-source-virtualization-stack</title>
|
||||
<style>
|
||||
tspan { white-space:pre }
|
||||
.s0 { fill: #c8c8c8 }
|
||||
.t1 { font-size: 16px;fill: #000000;font-weight: 700;font-family: "Unageo-Bold", "Unageo" }
|
||||
.s2 { fill: #f99b0f }
|
||||
.t3 { font-size: 16px;fill: #ffffff;font-weight: 700;font-family: "Unageo-Bold", "Unageo" }
|
||||
.s4 { fill: #e95420 }
|
||||
.s5 { fill: #ffffff;stroke: #666666;stroke-miterlimit:100;stroke-width: 5 }
|
||||
.s6 { fill: #e5e5e5 }
|
||||
.s7 { fill: #772953 }
|
||||
.s8 { fill: #ffffff }
|
||||
</style>
|
||||
<g id="Hardware">
|
||||
<path id="Hardware_Layer" class="s0" d="m20 332h546c11 0 20 9 20 20v12c0 11-9 20-20 20h-546c-11 0-20-9-20-20v-12c0-11 9-20 20-20z"/>
|
||||
<text id="Hardware_Layer_Text" style="transform: matrix(1,0,0,1,27,364)" >
|
||||
<tspan x="0" y="0" class="t1">H</tspan><tspan y="0" class="t1">a</tspan><tspan y="0" class="t1">r</tspan><tspan y="0" class="t1">d</tspan><tspan y="0" class="t1">w</tspan><tspan y="0" class="t1">a</tspan><tspan y="0" class="t1">r</tspan><tspan y="0" class="t1">e
|
||||
</tspan>
|
||||
</text>
|
||||
<g id="Hardware_Layer_Children">
|
||||
<g id="CPU">
|
||||
<path id="CPU_Box" class="s2" d="m227 348v21h-55v-21z"/>
|
||||
<text id="CPU_Text" style="transform: matrix(1,0,0,1,172,351)" >
|
||||
<tspan x="11.7" y="12.1" class="t3">C</tspan><tspan y="12.1" class="t3">P</tspan><tspan y="12.1" class="t3">U</tspan><tspan y="12.1" class="t3">
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="RAM">
|
||||
<path id="RAM_Box" class="s2" d="m382 348v21h-55v-21z"/>
|
||||
<text id="RAM_Text" style="transform: matrix(1,0,0,1,327,351)" >
|
||||
<tspan x="10.7" y="13" class="t3">R</tspan><tspan y="13" class="t3">A</tspan><tspan y="13" class="t3">M
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="Devices">
|
||||
<g id="Device_3">
|
||||
<path id="Device_Box" class="s2" d="m551 341v21h-55v-21z"/>
|
||||
<text id="Device_Text" style="transform: matrix(1,0,0,1,496,344)" >
|
||||
<tspan x="2.5" y="13" class="t3">D</tspan><tspan y="13" class="t3">e</tspan><tspan y="13" class="t3">v</tspan><tspan y="13" class="t3">i</tspan><tspan y="13" class="t3">c</tspan><tspan y="13" class="t3">e
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="Device_2">
|
||||
<path id="Device_Box" class="s2" d="m544 348v21h-55v-21z"/>
|
||||
<text id="Device_Text" style="transform: matrix(1,0,0,1,489,351)" >
|
||||
<tspan x="2.5" y="13" class="t3">D</tspan><tspan y="13" class="t3">e</tspan><tspan y="13" class="t3">v</tspan><tspan y="13" class="t3">i</tspan><tspan y="13" class="t3">c</tspan><tspan y="13" class="t3">e
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="Device_1">
|
||||
<path id="Device_Box" class="s2" d="m537 355v21h-55v-21z"/>
|
||||
<text id="Device_Text" style="transform: matrix(1,0,0,1,482,358)" >
|
||||
<tspan x="2.5" y="13" class="t3">D</tspan><tspan y="13" class="t3">e</tspan><tspan y="13" class="t3">v</tspan><tspan y="13" class="t3">i</tspan><tspan y="13" class="t3">c</tspan><tspan y="13" class="t3">e
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Linux_Kernel">
|
||||
<path id="Kernel_Layer" class="s0" d="m20 242h546c11 0 20 9 20 20v20c0 11-9 20-20 20h-546c-11 0-20-9-20-20v-20c0-11 9-20 20-20z"/>
|
||||
<text id="Kernel_Layer_Text" style="transform: matrix(1,0,0,1,65,268)" >
|
||||
<tspan x="-19.9" y="0" class="t1">L</tspan><tspan y="0" class="t1">i</tspan><tspan y="0" class="t1">n</tspan><tspan y="0" class="t1">u</tspan><tspan y="0" class="t1">x</tspan><tspan y="0" class="t1">
|
||||
</tspan>
|
||||
<tspan x="-23.6" y="19.2" class="t1">K</tspan><tspan y="19.2" class="t1">e</tspan><tspan y="19.2" class="t1">r</tspan><tspan y="19.2" class="t1">n</tspan><tspan y="19.2" class="t1">e</tspan><tspan y="19.2" class="t1">l
|
||||
</tspan>
|
||||
</text>
|
||||
<g id="KVM">
|
||||
<path id="KVM_Layer" class="s4" d="m578 249v45h-403v-45z"/>
|
||||
<text id="KVM_Text" style="transform: matrix(1,0,0,1,175,254)" >
|
||||
<tspan x="184.3" y="24.1" class="t3">K</tspan><tspan y="24.1" class="t3">V</tspan><tspan y="24.1" class="t3">M
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
<g id="QEMU">
|
||||
<path id="QEMU_Layer" class="s0" d="m189 0h376c11 0 20 9 20 20v170c0 11-9 20-20 20h-376c-11 0-20-9-20-20v-170c0-11 9-20 20-20z"/>
|
||||
<g id="VM_1">
|
||||
<path id="VM_Box" class="s5" d="m305 43v160h-122v-160z"/>
|
||||
<g id="VM_Kernel">
|
||||
<path id="VM_Kernel_Box" class="s6" d="m294 147v45h-100v-45z"/>
|
||||
<text id="VM_Kernel_text" style="transform: matrix(1,0,0,1,194,150)" >
|
||||
<tspan x="12.6" y="24.1" class="t1">V</tspan><tspan y="24.1" class="t1">M</tspan><tspan y="24.1" class="t1"> </tspan><tspan y="24.1" class="t1">K</tspan><tspan y="24.1" class="t1">e</tspan><tspan y="24.1" class="t1">r</tspan><tspan y="24.1" class="t1">n</tspan><tspan y="24.1" class="t1">e</tspan><tspan y="24.1" class="t1">l
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="VM_Applications">
|
||||
<path id="VM_Applications_Box" class="s7" d="m294 91v45h-100v-45z"/>
|
||||
<text id="VM_Applications_Text" style="transform: matrix(1,0,0,1,194,94)" >
|
||||
<tspan x="17.5" y="24.1" class="t3">V</tspan><tspan y="24.1" class="t3">M</tspan><tspan y="24.1" class="t3"> </tspan><tspan y="24.1" class="t3">A</tspan><tspan y="24.1" class="t3">p</tspan><tspan y="24.1" class="t3">p</tspan><tspan y="24.1" class="t3">s
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<text id="VM_Box_Text" style="transform: matrix(1,0,0,1,244,73)" >
|
||||
<tspan x="-41.4" y="0" class="t1">G</tspan><tspan y="0" class="t1">u</tspan><tspan y="0" class="t1">e</tspan><tspan y="0" class="t1">s</tspan><tspan y="0" class="t1">t</tspan><tspan y="0" class="t1"> </tspan><tspan y="0" class="t1">(</tspan><tspan y="0" class="t1">V</tspan><tspan y="0" class="t1">M</tspan><tspan y="0" class="t1">)
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="VM_2">
|
||||
<path id="VM_Box" class="s5" d="m438 43v160h-122v-160z"/>
|
||||
<g id="VM_Kernel">
|
||||
<path id="VM_Kernel_Box" class="s6" d="m427 147v45h-100v-45z"/>
|
||||
<text id="VM_Kernel_text" style="transform: matrix(1,0,0,1,327,150)" >
|
||||
<tspan x="12.6" y="24.1" class="t1">V</tspan><tspan y="24.1" class="t1">M</tspan><tspan y="24.1" class="t1"> </tspan><tspan y="24.1" class="t1">K</tspan><tspan y="24.1" class="t1">e</tspan><tspan y="24.1" class="t1">r</tspan><tspan y="24.1" class="t1">n</tspan><tspan y="24.1" class="t1">e</tspan><tspan y="24.1" class="t1">l
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="VM_Applications">
|
||||
<path id="VM_Applications_Box" class="s7" d="m427 91v45h-100v-45z"/>
|
||||
<text id="VM_Applications_Text" style="transform: matrix(1,0,0,1,327,94)" >
|
||||
<tspan x="17.5" y="24.1" class="t3">V</tspan><tspan y="24.1" class="t3">M</tspan><tspan y="24.1" class="t3"> </tspan><tspan y="24.1" class="t3">A</tspan><tspan y="24.1" class="t3">p</tspan><tspan y="24.1" class="t3">p</tspan><tspan y="24.1" class="t3">s
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<text id="VM_Box_Text" style="transform: matrix(1,0,0,1,377,73)" >
|
||||
<tspan x="-41.4" y="0" class="t1">G</tspan><tspan y="0" class="t1">u</tspan><tspan y="0" class="t1">e</tspan><tspan y="0" class="t1">s</tspan><tspan y="0" class="t1">t</tspan><tspan y="0" class="t1"> </tspan><tspan y="0" class="t1">(</tspan><tspan y="0" class="t1">V</tspan><tspan y="0" class="t1">M</tspan><tspan y="0" class="t1">)
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="VM_3">
|
||||
<path id="VM_Box" class="s5" d="m571 43v160h-122v-160z"/>
|
||||
<g id="VM_Kernel">
|
||||
<path id="VM_Kernel_Box" class="s6" d="m560 147v45h-100v-45z"/>
|
||||
<text id="VM_Kernel_text" style="transform: matrix(1,0,0,1,460,150)" >
|
||||
<tspan x="12.6" y="24.1" class="t1">V</tspan><tspan y="24.1" class="t1">M</tspan><tspan y="24.1" class="t1"> </tspan><tspan y="24.1" class="t1">K</tspan><tspan y="24.1" class="t1">e</tspan><tspan y="24.1" class="t1">r</tspan><tspan y="24.1" class="t1">n</tspan><tspan y="24.1" class="t1">e</tspan><tspan y="24.1" class="t1">l
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="VM_Applications">
|
||||
<path id="VM_Applications_Box" class="s7" d="m560 91v45h-100v-45z"/>
|
||||
<text id="VM_Applications_Text" style="transform: matrix(1,0,0,1,460,94)" >
|
||||
<tspan x="17.5" y="24.1" class="t3">V</tspan><tspan y="24.1" class="t3">M</tspan><tspan y="24.1" class="t3"> </tspan><tspan y="24.1" class="t3">A</tspan><tspan y="24.1" class="t3">p</tspan><tspan y="24.1" class="t3">p</tspan><tspan y="24.1" class="t3">s
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<text id="VM_Box_Text" style="transform: matrix(1,0,0,1,510,73)" >
|
||||
<tspan x="-41.4" y="0" class="t1">G</tspan><tspan y="0" class="t1">u</tspan><tspan y="0" class="t1">e</tspan><tspan y="0" class="t1">s</tspan><tspan y="0" class="t1">t</tspan><tspan y="0" class="t1"> </tspan><tspan y="0" class="t1">(</tspan><tspan y="0" class="t1">V</tspan><tspan y="0" class="t1">M</tspan><tspan y="0" class="t1">)
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<text id="QEMU_Layer_Text" style="transform: matrix(1,0,0,1,377,27)" >
|
||||
<tspan x="-23.2" y="0" class="t1">Q</tspan><tspan y="0" class="t1">E</tspan><tspan y="0" class="t1">M</tspan><tspan y="0" class="t1">U
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="GNU/Linux_Applications">
|
||||
<path id="GNU/Linux_Applications_Box" class="s0" d="m20 0h90c11 0 20 9 20 20v170c0 11-9 20-20 20h-90c-11 0-20-9-20-20v-170c0-11 9-20 20-20z"/>
|
||||
<g id="libvirt">
|
||||
<path id="libvirt_Box" class="s7" d="m116 142v50h-102v-50z"/>
|
||||
<text id="libvirt" style="transform: matrix(1,0,0,1,14,145)" >
|
||||
<tspan x="29.4" y="26.6" class="t3">l</tspan><tspan y="26.6" class="t3">i</tspan><tspan y="26.6" class="t3">b</tspan><tspan y="26.6" class="t3">v</tspan><tspan y="26.6" class="t3">i</tspan><tspan y="26.6" class="t3">r</tspan><tspan y="26.6" class="t3">t
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="virt-manager">
|
||||
<path id="virt-manager_Box" class="s7" d="m117 67v46h-104v-46z"/>
|
||||
<text id="virt-manager" style="transform: matrix(1,0,0,1,13,70)" >
|
||||
<tspan x="1.8" y="24.6" class="t3">v</tspan><tspan y="24.6" class="t3">i</tspan><tspan y="24.6" class="t3">r</tspan><tspan y="24.6" class="t3">t</tspan><tspan y="24.6" class="t3">-</tspan><tspan y="24.6" class="t3">m</tspan><tspan y="24.6" class="t3">a</tspan><tspan y="24.6" class="t3">n</tspan><tspan y="24.6" class="t3">a</tspan><tspan y="24.6" class="t3">g</tspan><tspan y="24.6" class="t3">e</tspan><tspan y="24.6" class="t3">r
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<text id="GNU/Linux_Applications_Text" style="transform: matrix(1,0,0,1,65,26)" >
|
||||
<tspan x="-40.9" y="0" class="t1">G</tspan><tspan y="0" class="t1">N</tspan><tspan y="0" class="t1">U</tspan><tspan y="0" class="t1">/</tspan><tspan y="0" class="t1">L</tspan><tspan y="0" class="t1">i</tspan><tspan y="0" class="t1">n</tspan><tspan y="0" class="t1">u</tspan><tspan y="0" class="t1">x</tspan><tspan y="0" class="t1">
|
||||
</tspan>
|
||||
<tspan x="-46" y="19.2" class="t1">A</tspan><tspan y="19.2" class="t1">p</tspan><tspan y="19.2" class="t1">p</tspan><tspan y="19.2" class="t1">l</tspan><tspan y="19.2" class="t1">i</tspan><tspan y="19.2" class="t1">c</tspan><tspan y="19.2" class="t1">a</tspan><tspan y="19.2" class="t1">t</tspan><tspan y="19.2" class="t1">i</tspan><tspan y="19.2" class="t1">o</tspan><tspan y="19.2" class="t1">n</tspan><tspan y="19.2" class="t1">s
|
||||
</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g id="Arrows">
|
||||
<path id="Arrow_0" fill-rule="evenodd" class="s8" d="m75.7 115c-0.6-0.7-1.6-0.7-2.2 0l-6.2 6.2c-0.6 0.6-0.6 1.6 0 2.2 0.6 0.6 1.6 0.6 2.2 0l3.5-3.6v18c0 0.9 0.7 1.5 1.6 1.5 0.8 0 1.5-0.6 1.5-1.5v-18l3.6 3.6c0.6 0.6 1.6 0.6 2.2 0 0.6-0.6 0.6-1.6 0-2.2 0 0-6.2-6.2-6.2-6.2z"/>
|
||||
<path id="Arrow_1" fill-rule="evenodd" class="s8" d="m53.5 138.9c0.6 0.6 1.6 0.6 2.2 0l6.2-6.2c0.6-0.6 0.6-1.6 0-2.2-0.6-0.6-1.6-0.6-2.2 0l-3.6 3.5v-17.9c0-0.9-0.7-1.6-1.5-1.6-0.9 0-1.6 0.7-1.6 1.6v17.9l-3.5-3.5c-0.6-0.6-1.6-0.6-2.2 0-0.6 0.6-0.6 1.6 0 2.2 0 0 6.2 6.2 6.2 6.2z"/>
|
||||
<path id="Arrow_2" fill-rule="evenodd" class="s8" d="m159.5 158c0.7-0.6 0.7-1.6 0-2.2l-6.2-6.2c-0.6-0.6-1.6-0.6-2.2 0-0.6 0.6-0.6 1.6 0 2.2l3.6 3.6h-18c-0.9 0-1.5 0.7-1.5 1.5 0 0.9 0.6 1.6 1.5 1.6h18l-3.6 3.5c-0.6 0.6-0.6 1.6 0 2.2 0.6 0.6 1.6 0.6 2.2 0 0 0 6.2-6.2 6.2-6.2z"/>
|
||||
<path id="Arrow_3" fill-rule="evenodd" class="s8" d="m135.6 175.8c-0.6 0.6-0.6 1.6 0 2.2l6.2 6.2c0.6 0.6 1.6 0.6 2.2 0 0.6-0.6 0.6-1.6 0-2.2l-3.5-3.5h17.9c0.9 0 1.6-0.7 1.6-1.6 0-0.8-0.7-1.5-1.6-1.5h-17.9l3.5-3.6c0.6-0.6 0.6-1.6 0-2.2-0.6-0.6-1.6-0.6-2.2 0 0 0-6.2 6.2-6.2 6.2z"/>
|
||||
<path id="Arrow_4" fill-rule="evenodd" class="s8" d="m281.5 238.9c0.6 0.6 1.6 0.6 2.2 0l6.2-6.2c0.6-0.6 0.6-1.6 0-2.2-0.6-0.6-1.6-0.6-2.2 0l-3.6 3.5v-17.9c0-0.9-0.7-1.6-1.5-1.6-0.9 0-1.6 0.7-1.6 1.6v17.9l-3.5-3.5c-0.6-0.6-1.6-0.6-2.2 0-0.6 0.6-0.6 1.6 0 2.2 0 0 6.2 6.2 6.2 6.2z"/>
|
||||
<path id="Arrow_5" fill-rule="evenodd" class="s8" d="m303.7 215c-0.6-0.7-1.6-0.7-2.2 0l-6.2 6.2c-0.6 0.6-0.6 1.6 0 2.2 0.6 0.6 1.6 0.6 2.2 0l3.5-3.6v18c0 0.9 0.7 1.5 1.6 1.5 0.8 0 1.5-0.6 1.5-1.5v-18l3.6 3.6c0.6 0.6 1.6 0.6 2.2 0 0.6-0.6 0.6-1.6 0-2.2 0 0-6.2-6.2-6.2-6.2z"/>
|
||||
<path id="Arrow_6" fill-rule="evenodd" class="s8" d="m281.5 328.9c0.6 0.6 1.6 0.6 2.2 0l6.2-6.2c0.6-0.6 0.6-1.6 0-2.2-0.6-0.6-1.6-0.6-2.2 0l-3.6 3.5v-17.9c0-0.9-0.7-1.6-1.5-1.6-0.9 0-1.6 0.7-1.6 1.6v17.9l-3.5-3.5c-0.6-0.6-1.6-0.6-2.2 0-0.6 0.6-0.6 1.6 0 2.2 0 0 6.2 6.2 6.2 6.2z"/>
|
||||
<path id="Arrow_7" fill-rule="evenodd" class="s8" d="m303.7 305c-0.6-0.7-1.6-0.7-2.2 0l-6.2 6.2c-0.6 0.6-0.6 1.6 0 2.2 0.6 0.6 1.6 0.6 2.2 0l3.5-3.6v18c0 0.9 0.7 1.5 1.6 1.5 0.8 0 1.5-0.6 1.5-1.5v-18l3.6 3.6c0.6 0.6 1.6 0.6 2.2 0 0.6-0.6 0.6-1.6 0-2.2 0 0-6.2-6.2-6.2-6.2z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 14 KiB |
335
installer.sh
@ -12,6 +12,7 @@ readonly EXIT_TEXT="\033[1;41;37m" # Bold + White + Red Background
|
||||
readonly FAIL_TEXT="\033[0;91m" # Bright Red
|
||||
readonly INFO_TEXT="\033[0;33m" # Orange/Yellow
|
||||
readonly SUCCESS_TEXT="\033[1;42;37m" # Bold + White + Green Background
|
||||
readonly WARNING_TEXT="\033[1;33m" # Bold + Orange/Yellow
|
||||
|
||||
# ERROR CODES
|
||||
readonly EC_FAILED_CD="1" # Failed to change directory to location of script.
|
||||
@ -21,13 +22,15 @@ readonly EC_NO_CONFIG="4" # Absence of a valid WinApps configuration file
|
||||
readonly EC_MISSING_DEPS="5" # Missing dependencies.
|
||||
readonly EC_NO_SUDO="6" # Insufficient privilages to invoke superuser access.
|
||||
readonly EC_NOT_IN_GROUP="7" # Current user not in group 'libvirt' and/or 'kvm'.
|
||||
readonly EC_VM_OFF="8" # Windows VM powered off.
|
||||
readonly EC_VM_PAUSED="9" # Windows VM paused.
|
||||
readonly EC_VM_ABSENT="10" # Windows VM does not exist.
|
||||
readonly EC_VM_NO_IP="11" # Windows VM does not have an IP address.
|
||||
readonly EC_VM_BAD_PORT="12" # Windows VM is unreachable via RDP_PORT.
|
||||
readonly EC_RDP_FAIL="13" # FreeRDP failed to establish a connection with the Windows VM.
|
||||
readonly EC_APPQUERY_FAIL="14" # Failed to query the Windows VM for installed applications.
|
||||
readonly EC_VM_OFF="8" # Windows 'libvirt' VM powered off.
|
||||
readonly EC_VM_PAUSED="9" # Windows 'libvirt' VM paused.
|
||||
readonly EC_VM_ABSENT="10" # Windows 'libvirt' VM does not exist.
|
||||
readonly EC_CONTAINER_OFF="11" # Windows Docker container is not running.
|
||||
readonly EC_NO_IP="12" # Windows does not have an IP address.
|
||||
readonly EC_BAD_PORT="13" # Windows is unreachable via RDP_PORT.
|
||||
readonly EC_RDP_FAIL="14" # FreeRDP failed to establish a connection with Windows.
|
||||
readonly EC_APPQUERY_FAIL="15" # Failed to query Windows for installed applications.
|
||||
readonly EC_INVALID_FLAVOR="16" # Backend specified is not 'libvirt', 'docker' or 'podman'.
|
||||
|
||||
# PATHS
|
||||
# 'BIN'
|
||||
@ -43,8 +46,8 @@ readonly SYS_APPDATA_PATH="/usr/local/share/winapps" # UNIX pat
|
||||
readonly USER_APPDATA_PATH="${HOME}/.local/share/winapps" # UNIX path to 'application data' directory for a '--user' WinApps installation.
|
||||
readonly USER_APPDATA_PATH_WIN='\\tsclient\home\.local\share\winapps' # WINDOWS path to 'application data' directory for a '--user' WinApps installation.
|
||||
# 'Installed Batch Script'
|
||||
readonly BATCH_SCRIPT_PATH="${USER_APPDATA_PATH}/installed.bat" # UNIX path to a batch script used to search the Windows VM for applications.
|
||||
readonly BATCH_SCRIPT_PATH_WIN="${USER_APPDATA_PATH_WIN}\\installed.bat" # WINDOWS path to a batch script used to search the Windows VM for applications.
|
||||
readonly BATCH_SCRIPT_PATH="${USER_APPDATA_PATH}/installed.bat" # UNIX path to a batch script used to search Windows for applications.
|
||||
readonly BATCH_SCRIPT_PATH_WIN="${USER_APPDATA_PATH_WIN}\\installed.bat" # WINDOWS path to a batch script used to search Windows for applications.
|
||||
# 'Installed File'
|
||||
readonly TMP_INST_FILE_PATH="${USER_APPDATA_PATH}/installed.tmp" # UNIX path to a temporary file containing the names of detected officially supported applications.
|
||||
readonly TMP_INST_FILE_PATH_WIN="${USER_APPDATA_PATH_WIN}\\installed.tmp" # WINDOWS path to a temporary file containing the names of detected officially supported applications.
|
||||
@ -52,8 +55,8 @@ readonly INST_FILE_PATH="${USER_APPDATA_PATH}/installed" # UNIX
|
||||
readonly INST_FILE_PATH_WIN="${USER_APPDATA_PATH_WIN}\\installed" # WINDOWS path to a file containing the names of detected officially supported applications.
|
||||
# 'PowerShell Script'
|
||||
readonly PS_SCRIPT_PATH="./install/ExtractPrograms.ps1" # UNIX path to a PowerShell script used to store the names, executable paths and icons (base64) of detected applications.
|
||||
readonly PS_SCRIPT_HOME_PATH="${USER_APPDATA_PATH}/ExtractPrograms.ps1" # UNIX path to a copy of the PowerShell script within the user's home directory to enable access by Windows VM.
|
||||
readonly PS_SCRIPT_HOME_PATH_WIN="${USER_APPDATA_PATH_WIN}\\ExtractPrograms.ps1" # WINDOWS path to a copy of the PowerShell script within the user's home directory to enable access by Windows VM.
|
||||
readonly PS_SCRIPT_HOME_PATH="${USER_APPDATA_PATH}/ExtractPrograms.ps1" # UNIX path to a copy of the PowerShell script within the user's home directory to enable access by Windows.
|
||||
readonly PS_SCRIPT_HOME_PATH_WIN="${USER_APPDATA_PATH_WIN}\\ExtractPrograms.ps1" # WINDOWS path to a copy of the PowerShell script within the user's home directory to enable access by Windows.
|
||||
# 'Detected File'
|
||||
readonly DETECTED_FILE_PATH="${USER_APPDATA_PATH}/detected" # UNIX path to a file containing the output generated by the PowerShell script, formatted to define bash arrays.
|
||||
readonly DETECTED_FILE_PATH_WIN="${USER_APPDATA_PATH_WIN}\\detected" # WINDOWS path to a file containing the output generated by the PowerShell script, formatted to define bash arrays.
|
||||
@ -66,13 +69,15 @@ readonly CONFIG_PATH="${HOME}/.config/winapps/winapps.conf" # UNIX path to the W
|
||||
readonly INQUIRER_PATH="./install/inquirer.sh" # UNIX path to the 'inquirer' script, which is used to produce selection menus.
|
||||
|
||||
# REMOTE DESKTOP CONFIGURATION
|
||||
readonly VM_NAME="RDPWindows" # Name of the Windows VM.
|
||||
readonly RDP_PORT=3389 # Port used for RDP on the Windows VM.
|
||||
readonly WINAPPS_CONFIG='RDP_USER="MyWindowsUser"
|
||||
RDP_PASS="MyWindowsPassword"
|
||||
readonly VM_NAME="RDPWindows" # Name of the Windows VM (FOR 'libvirt' ONLY).
|
||||
readonly RDP_PORT=3389 # Port used for RDP on Windows.
|
||||
readonly DOCKER_IP="127.0.0.1" # Localhost.
|
||||
RDP_USER=""
|
||||
RDP_PASS=""
|
||||
#RDP_DOMAIN="MYDOMAIN"
|
||||
#RDP_IP="192.168.123.111"
|
||||
#RDP_SCALE=100
|
||||
#WAFLAVOR="docker" # Acceptable values are 'docker', 'podman' and 'libvirt'.
|
||||
#RDP_SCALE=100 # Acceptable values are 100, 140, and 180.
|
||||
#RDP_FLAGS=""
|
||||
#MULTIMON="true"
|
||||
#DEBUG="true"
|
||||
@ -90,6 +95,7 @@ RDP_USER="" # Imported variable.
|
||||
RDP_PASS="" # Imported variable.
|
||||
RDP_DOMAIN="" # Imported variable.
|
||||
RDP_IP="" # Imported variable.
|
||||
WAFLAVOR="docker" # Imported variable.
|
||||
RDP_SCALE=100 # Imported variable.
|
||||
RDP_FLAGS="" # Imported variable.
|
||||
MULTIMON="false" # Imported variable.
|
||||
@ -384,6 +390,39 @@ function waCheckExistingInstall() {
|
||||
echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}"
|
||||
}
|
||||
|
||||
# Name: 'waFixScale'
|
||||
# Role: Since FreeRDP only supports '/scale' values of 100, 140 or 180, find the closest supported argument to the user's configuration.
|
||||
function waFixScale() {
|
||||
# Define variables.
|
||||
local OLD_SCALE=100
|
||||
local VALID_SCALE_1=100
|
||||
local VALID_SCALE_2=140
|
||||
local VALID_SCALE_3=180
|
||||
|
||||
# Check for an unsupported value.
|
||||
if [ "$RDP_SCALE" != "$VALID_SCALE_1" ] && [ "$RDP_SCALE" != "$VALID_SCALE_2" ] && [ "$RDP_SCALE" != "$VALID_SCALE_3" ]; then
|
||||
# Save the unsupported scale.
|
||||
OLD_SCALE="$RDP_SCALE"
|
||||
|
||||
# Calculate the absolute differences.
|
||||
local DIFF_1=$(( RDP_SCALE > VALID_SCALE_1 ? RDP_SCALE - VALID_SCALE_1 : VALID_SCALE_1 - RDP_SCALE ))
|
||||
local DIFF_2=$(( RDP_SCALE > VALID_SCALE_2 ? RDP_SCALE - VALID_SCALE_2 : VALID_SCALE_2 - RDP_SCALE ))
|
||||
local DIFF_3=$(( RDP_SCALE > VALID_SCALE_3 ? RDP_SCALE - VALID_SCALE_3 : VALID_SCALE_3 - RDP_SCALE ))
|
||||
|
||||
# Set the final scale to the valid scale value with the smallest absolute difference.
|
||||
if (( DIFF_1 <= DIFF_2 && DIFF_1 <= DIFF_3 )); then
|
||||
RDP_SCALE="$VALID_SCALE_1"
|
||||
elif (( DIFF_2 <= DIFF_1 && DIFF_2 <= DIFF_3 )); then
|
||||
RDP_SCALE="$VALID_SCALE_2"
|
||||
else
|
||||
RDP_SCALE="$VALID_SCALE_3"
|
||||
fi
|
||||
|
||||
# Print feedback.
|
||||
echo -e "${WARNING_TEXT}[WARNING]${CLEAR_TEXT} Unsupported RDP_SCALE value '${OLD_SCALE}' detected. Defaulting to '${RDP_SCALE}'."
|
||||
fi
|
||||
}
|
||||
|
||||
# Name: 'waLoadConfig'
|
||||
# Role: Loads settings specified within the WinApps configuration file.
|
||||
function waLoadConfig() {
|
||||
@ -463,13 +502,13 @@ function waCheckDependencies() {
|
||||
# Check common commands used to launch FreeRDP.
|
||||
if command -v xfreerdp &>/dev/null; then
|
||||
# Check FreeRDP major version is 3 or greater.
|
||||
FREERDP_MAJOR_VERSION=$(xfreerdp --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | cut -d'.' -f1)
|
||||
FREERDP_MAJOR_VERSION=$(xfreerdp --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | head -n 1 | cut -d'.' -f1)
|
||||
if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then
|
||||
FREERDP_COMMAND="xfreerdp"
|
||||
fi
|
||||
elif command -v xfreerdp3 &>/dev/null; then
|
||||
# Check FreeRDP major version is 3 or greater.
|
||||
FREERDP_MAJOR_VERSION=$(xfreerdp3 --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | cut -d'.' -f1)
|
||||
FREERDP_MAJOR_VERSION=$(xfreerdp3 --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | head -n 1 | cut -d'.' -f1)
|
||||
if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then
|
||||
FREERDP_COMMAND="xfreerdp3"
|
||||
fi
|
||||
@ -521,30 +560,71 @@ function waCheckDependencies() {
|
||||
fi
|
||||
|
||||
# 'libvirt' / 'virt-manager'.
|
||||
if ! command -v virsh &>/dev/null; then
|
||||
# Complete the previous line.
|
||||
echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n"
|
||||
if [ "$WAFLAVOR" = "libvirt" ]; then
|
||||
if ! command -v virsh &>/dev/null; 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}MISSING DEPENDENCIES.${CLEAR_TEXT}"
|
||||
# Display the error type.
|
||||
echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}"
|
||||
|
||||
# Display the error details.
|
||||
echo -e "${INFO_TEXT}Please install 'Virtual Machine Manager' to proceed.${CLEAR_TEXT}"
|
||||
# Display the error details.
|
||||
echo -e "${INFO_TEXT}Please install 'Virtual Machine Manager' to proceed.${CLEAR_TEXT}"
|
||||
|
||||
# Display the suggested action(s).
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Debian/Ubuntu-based systems:"
|
||||
echo -e " ${COMMAND_TEXT}sudo apt install virt-manager${CLEAR_TEXT}"
|
||||
echo "Red Hat/Fedora-based systems:"
|
||||
echo -e " ${COMMAND_TEXT}sudo dnf install virt-manager${CLEAR_TEXT}"
|
||||
echo "Arch Linux systems:"
|
||||
echo -e " ${COMMAND_TEXT}sudo pacman -S virt-manager${CLEAR_TEXT}"
|
||||
echo "Gentoo Linux systems:"
|
||||
echo -e " ${COMMAND_TEXT}sudo emerge --ask app-emulation/virt-manager${CLEAR_TEXT}"
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
# Display the suggested action(s).
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Debian/Ubuntu-based systems:"
|
||||
echo -e " ${COMMAND_TEXT}sudo apt install virt-manager${CLEAR_TEXT}"
|
||||
echo "Red Hat/Fedora-based systems:"
|
||||
echo -e " ${COMMAND_TEXT}sudo dnf install virt-manager${CLEAR_TEXT}"
|
||||
echo "Arch Linux systems:"
|
||||
echo -e " ${COMMAND_TEXT}sudo pacman -S virt-manager${CLEAR_TEXT}"
|
||||
echo "Gentoo Linux systems:"
|
||||
echo -e " ${COMMAND_TEXT}sudo emerge --ask app-emulation/virt-manager${CLEAR_TEXT}"
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
|
||||
# Terminate the script.
|
||||
return "$EC_MISSING_DEPS"
|
||||
# Terminate the script.
|
||||
return "$EC_MISSING_DEPS"
|
||||
fi
|
||||
elif [ "$WAFLAVOR" = "docker" ]; then
|
||||
if ! command -v docker &>/dev/null; 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}MISSING DEPENDENCIES.${CLEAR_TEXT}"
|
||||
|
||||
# Display the error details.
|
||||
echo -e "${INFO_TEXT}Please install 'Docker Engine' to proceed.${CLEAR_TEXT}"
|
||||
|
||||
# Display the suggested action(s).
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Please visit https://docs.docker.com/engine/install/ for more information."
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
|
||||
# Terminate the script.
|
||||
return "$EC_MISSING_DEPS"
|
||||
fi
|
||||
elif [ "$WAFLAVOR" = "podman" ]; then
|
||||
if ! command -v podman-compose &>/dev/null || ! command -v podman &>/dev/null; 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}MISSING DEPENDENCIES.${CLEAR_TEXT}"
|
||||
|
||||
# Display the error details.
|
||||
echo -e "${INFO_TEXT}Please install 'podman' and 'podman-compose' to proceed.${CLEAR_TEXT}"
|
||||
|
||||
# Display the suggested action(s).
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Please visit https://podman.io/docs/installation for more information."
|
||||
echo "Please visit https://github.com/containers/podman-compose for more information."
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
|
||||
# Terminate the script.
|
||||
return "$EC_MISSING_DEPS"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Print feedback.
|
||||
@ -589,7 +669,7 @@ function waCheckGroupMembership() {
|
||||
}
|
||||
|
||||
# Name: 'waCheckVMRunning'
|
||||
# Role: Checks the state of the Windows VM to ensure it is running.
|
||||
# Role: Checks the state of the Windows 'libvirt' VM to ensure it is running.
|
||||
function waCheckVMRunning() {
|
||||
# Print feedback.
|
||||
echo -n "Checking the status of the Windows VM... "
|
||||
@ -659,17 +739,65 @@ function waCheckVMRunning() {
|
||||
echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}"
|
||||
}
|
||||
|
||||
# Name: 'waCheckVMContactable'
|
||||
# Role: Assesses whether the Windows VM can be contacted (prior to attempting a remote desktop connection).
|
||||
function waCheckVMContactable() {
|
||||
# Name: 'waCheckContainerRunning'
|
||||
# Role: Throw an error if the Docker container is not running.
|
||||
function waCheckContainerRunning() {
|
||||
# Print feedback.
|
||||
echo -n "Attempting to contact the Windows VM... "
|
||||
echo -n "Checking container status... "
|
||||
|
||||
# Declare variables.
|
||||
local CONTAINER_STATE=""
|
||||
|
||||
# Determine container state (docker).
|
||||
if command -v docker &>/dev/null; then
|
||||
CONTAINER_STATE=$(docker ps --filter name="WinApps" --format '{{.Status}}')
|
||||
fi
|
||||
|
||||
# Determine container state (podman).
|
||||
if [ -z "$CONTAINER_STATE" ]; then
|
||||
CONTAINER_STATE=$(podman ps --filter name="WinApps" --format '{{.Status}}')
|
||||
fi
|
||||
|
||||
CONTAINER_STATE=${CONTAINER_STATE,,} # Convert the string to lowercase.
|
||||
CONTAINER_STATE=${CONTAINER_STATE%% *} # Extract the first word.
|
||||
|
||||
# Check container state.
|
||||
if [[ "$CONTAINER_STATE" != "up" ]]; 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}DOCKER CONTAINER NOT RUNNING.${CLEAR_TEXT}"
|
||||
|
||||
# Display the error details.
|
||||
echo -e "${INFO_TEXT}Windows is not running.${CLEAR_TEXT}"
|
||||
|
||||
# Display the suggested action(s).
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Please ensure Windows is powered on:"
|
||||
echo -e "${COMMAND_TEXT}docker compose start${CLEAR_TEXT}"
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
|
||||
# Terminate the script.
|
||||
return "$EC_CONTAINER_OFF"
|
||||
fi
|
||||
|
||||
# Print feedback.
|
||||
echo -e "${DONE_TEXT}Done!${CLEAR_TEXT}"
|
||||
}
|
||||
|
||||
# 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.
|
||||
|
||||
# Obtain Windows VM IP Address
|
||||
if [ -z "$RDP_IP" ]; then
|
||||
# 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=$(arp -n | grep "$VM_MAC" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}") # VM IP address.
|
||||
|
||||
@ -689,7 +817,7 @@ function waCheckVMContactable() {
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
|
||||
# Terminate the script.
|
||||
return "$EC_VM_NO_IP"
|
||||
return "$EC_NO_IP"
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -702,15 +830,15 @@ function waCheckVMContactable() {
|
||||
echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}NETWORK CONFIGURATION ERROR.${CLEAR_TEXT}"
|
||||
|
||||
# Display the error details.
|
||||
echo -e "${INFO_TEXT}Failed to establish a connection with the Windows VM '${VM_NAME}' at '${RDP_IP}:${RDP_PORT}'.${CLEAR_TEXT}"
|
||||
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 Remote Desktop is configured on the Windows VM as per the WinApps README."
|
||||
echo "Please ensure Remote Desktop is configured on Windows as per the WinApps README."
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
|
||||
# Terminate the script.
|
||||
return "$EC_VM_BAD_PORT"
|
||||
return "$EC_BAD_PORT"
|
||||
fi
|
||||
|
||||
# Print feedback.
|
||||
@ -718,10 +846,10 @@ function waCheckVMContactable() {
|
||||
}
|
||||
|
||||
# Name: 'waCheckRDPAccess'
|
||||
# Role: Tests if the Windows VM is accessible via remote desktop.
|
||||
# Role: Tests if Windows is accessible via RDP.
|
||||
function waCheckRDPAccess() {
|
||||
# Print feedback.
|
||||
echo -n "Establishing a Remote Desktop connection with the Windows VM... "
|
||||
echo -n "Attempting to establish a Remote Desktop connection with Windows... "
|
||||
|
||||
# Declare variables.
|
||||
local FREERDP_LOG="" # Stores the path of the FreeRDP log file.
|
||||
@ -738,7 +866,7 @@ function waCheckRDPAccess() {
|
||||
rm -f "$TEST_PATH"
|
||||
|
||||
# This command should create a file on the host filesystem before terminating the RDP session. This command is silently executed as a background process.
|
||||
# If the file is created, it means the Windows VM received the command via FreeRDP successfully and can read and write to the Linux home folder.
|
||||
# If the file is created, it means Windows received the command via FreeRDP successfully and can read and write to the Linux home folder.
|
||||
# Note: The following final line is expected within the log, indicating successful execution of the 'tsdiscon' command and termination of the RDP session.
|
||||
# [INFO][com.freerdp.core] - [rdp_print_errinfo]: ERRINFO_LOGOFF_BY_USER (0x0000000C):The disconnection was initiated by the user logging off their session on the server.
|
||||
# shellcheck disable=SC2140,SC2027 # Disable warnings regarding unquoted strings.
|
||||
@ -776,7 +904,7 @@ function waCheckRDPAccess() {
|
||||
# Check if FreeRDP process is not complete.
|
||||
if ps -p "$FREERDP_PROC" &>/dev/null; then
|
||||
# SIGKILL FreeRDP.
|
||||
kill -9 "$FREERDP_PROC"
|
||||
kill -9 "$FREERDP_PROC" &>/dev/null
|
||||
fi
|
||||
|
||||
# Check if test file does not exist.
|
||||
@ -788,18 +916,19 @@ function waCheckRDPAccess() {
|
||||
echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}REMOTE DESKTOP PROTOCOL FAILURE.${CLEAR_TEXT}"
|
||||
|
||||
# Display the error details.
|
||||
echo -e "${INFO_TEXT}FreeRDP failed to establish a connection with the Windows VM '${VM_NAME}'.${CLEAR_TEXT}"
|
||||
echo -e "${INFO_TEXT}FreeRDP failed to establish a connection with Windows.${CLEAR_TEXT}"
|
||||
|
||||
# Display the suggested action(s).
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo -e "Please view the log at ${COMMAND_TEXT}${FREERDP_LOG}${CLEAR_TEXT}."
|
||||
echo "Troubleshooting Tips:"
|
||||
echo " - Ensure the user is logged out of the Windows VM prior to initiating the WinApps installation."
|
||||
echo " - Ensure the user is logged out of Windows prior to initiating the WinApps installation."
|
||||
echo " - Ensure the credentials within the WinApps configuration file are correct."
|
||||
echo " - Ensure the Windows VM is correctly named as specified within the README."
|
||||
echo " - Ensure 'Remote Desktop' is enabled within the Windows VM."
|
||||
echo " - Ensure you have merged 'RDPApps.reg' into the Windows VM's registry."
|
||||
echo -e " - Utilise a new certificate by removing relevant certificate(s) in ${COMMAND_TEXT}${HOME}/.config/freerdp/server${CLEAR_TEXT}."
|
||||
echo " - If using 'libvirt', ensure the Windows VM is correctly named as specified within the README."
|
||||
echo " - If using 'libvirt', ensure 'Remote Desktop' is enabled within the Windows VM."
|
||||
echo " - If using 'libvirt', ensure you have merged 'RDPApps.reg' into the Windows VM's registry."
|
||||
echo " - If using 'libvirt', try logging into and back out of the Windows VM within 'virt-manager' prior to initiating the WinApps installation."
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
|
||||
# Terminate the script.
|
||||
@ -814,7 +943,7 @@ function waCheckRDPAccess() {
|
||||
}
|
||||
|
||||
# Name: 'waFindInstalled'
|
||||
# Role: Identifies installed applications on the Windows VM.
|
||||
# Role: Identifies installed applications on Windows.
|
||||
function waFindInstalled() {
|
||||
# Print feedback.
|
||||
echo -n "Checking for installed Windows applications... "
|
||||
@ -834,7 +963,7 @@ function waFindInstalled() {
|
||||
rm -f "$BATCH_SCRIPT_PATH" "$TMP_INST_FILE_PATH" "$INST_FILE_PATH" "$PS_SCRIPT_HOME_PATH" "$DETECTED_FILE_PATH"
|
||||
|
||||
# Copy PowerShell script to a directory within the user's home folder.
|
||||
# This will enable the PowerShell script to be accessed and executed by the Windows VM.
|
||||
# This will enable the PowerShell script to be accessed and executed by Windows.
|
||||
cp "$PS_SCRIPT_PATH" "$PS_SCRIPT_HOME_PATH"
|
||||
|
||||
# Enumerate over each officially supported application.
|
||||
@ -866,7 +995,7 @@ function waFindInstalled() {
|
||||
# Append a command to the batch script to terminate the remote desktop session once all previous commands are complete.
|
||||
echo "tsdiscon" >>"$BATCH_SCRIPT_PATH"
|
||||
|
||||
# Silently execute the batch script within the Windows VM in the background (Log Output To File)
|
||||
# Silently execute the batch script within Windows in the background (Log Output To File)
|
||||
# Note: The following final line is expected within the log, indicating successful execution of the 'tsdiscon' command and termination of the RDP session.
|
||||
# [INFO][com.freerdp.core] - [rdp_print_errinfo]: ERRINFO_LOGOFF_BY_USER (0x0000000C):The disconnection was initiated by the user logging off their session on the server.
|
||||
# shellcheck disable=SC2140,SC2027 # Disable warnings regarding unquoted strings.
|
||||
@ -904,7 +1033,7 @@ function waFindInstalled() {
|
||||
# Check if the FreeRDP process is not complete.
|
||||
if ps -p "$FREERDP_PROC" &>/dev/null; then
|
||||
# SIGKILL FreeRDP.
|
||||
kill -9 "$FREERDP_PROC"
|
||||
kill -9 "$FREERDP_PROC" &>/dev/null
|
||||
fi
|
||||
|
||||
# Check if test file does not exist.
|
||||
@ -916,7 +1045,7 @@ function waFindInstalled() {
|
||||
echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}APPLICATION QUERY FAILURE.${CLEAR_TEXT}"
|
||||
|
||||
# Display the error details.
|
||||
echo -e "${INFO_TEXT}Failed to query Windows VM '${VM_NAME}' for installed applications.${CLEAR_TEXT}"
|
||||
echo -e "${INFO_TEXT}Failed to query Windows for installed applications.${CLEAR_TEXT}"
|
||||
|
||||
# Display the suggested action(s).
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
@ -932,14 +1061,14 @@ function waFindInstalled() {
|
||||
}
|
||||
|
||||
# Name: 'waConfigureWindows'
|
||||
# Role: Create an application entry for launching Windows VM via Remote Desktop.
|
||||
# Role: Create an application entry for launching Windows via Remote Desktop.
|
||||
function waConfigureWindows() {
|
||||
# Print feedback.
|
||||
echo -n "Creating an application entry for Windows VM... "
|
||||
echo -n "Creating an application entry for Windows... "
|
||||
|
||||
# Declare variables.
|
||||
local WIN_BASH="" # Stores the bash script to launch the Windows VM.
|
||||
local WIN_DESKTOP="" # Stores the '.desktop' file to launch the Windows VM.
|
||||
local WIN_BASH="" # Stores the bash script to launch a Windows RDP session.
|
||||
local WIN_DESKTOP="" # Stores the '.desktop' file to launch a Windows RDP session.
|
||||
|
||||
# Populate variables.
|
||||
WIN_BASH="\
|
||||
@ -953,7 +1082,7 @@ Terminal=false
|
||||
Type=Application
|
||||
Icon=${APPDATA_PATH}/icons/windows.svg
|
||||
StartupWMClass=Microsoft Windows
|
||||
Comment=Microsoft Windows VM"
|
||||
Comment=Microsoft Windows RDP Session"
|
||||
|
||||
# Copy the 'Windows' icon.
|
||||
$SUDO cp "./icons/windows.svg" "${APPDATA_PATH}/icons/windows.svg"
|
||||
@ -972,7 +1101,7 @@ Comment=Microsoft Windows VM"
|
||||
}
|
||||
|
||||
# Name: 'waConfigureApp'
|
||||
# Role: Create application entries for a given application installed on the Windows VM.
|
||||
# Role: Create application entries for a given application installed on Windows.
|
||||
function waConfigureApp() {
|
||||
# Declare variables.
|
||||
local APP_ICON="" # Stores the path to the application icon.
|
||||
@ -1021,12 +1150,12 @@ MimeType=${MIME_TYPES}"
|
||||
}
|
||||
|
||||
# Name: 'waConfigureOfficiallySupported'
|
||||
# Role: Create application entries for officially supported applications installed on the Windows VM.
|
||||
# Role: Create application entries for officially supported applications installed on Windows.
|
||||
function waConfigureOfficiallySupported() {
|
||||
# Declare variables.
|
||||
local OSA_LIST=() # Stores a list of all officially supported applications installed on the Windows VM.
|
||||
local OSA_LIST=() # Stores a list of all officially supported applications installed on Windows.
|
||||
|
||||
# Read the list of officially supported applications that are installed on the Windows VM into an array, returning an empty array if no such files exist.
|
||||
# Read the list of officially supported applications that are installed on Windows into an array, returning an empty array if no such files exist.
|
||||
# This will remove leading and trailing whitespace characters as well as ignore empty lines.
|
||||
readarray -t OSA_LIST < <(grep -v '^[[:space:]]*$' "$INST_FILE_PATH" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' 2>/dev/null || true)
|
||||
|
||||
@ -1053,18 +1182,18 @@ function waConfigureOfficiallySupported() {
|
||||
# Role: Allow the user to select which officially supported applications to configure.
|
||||
function waConfigureApps() {
|
||||
# Declare variables.
|
||||
local OSA_LIST=() # Stores a list of all officially supported applications installed on the Windows VM.
|
||||
local OSA_LIST=() # Stores a list of all officially supported applications installed on Windows.
|
||||
local APPS=() # Stores a list of both the simplified and full names of each installed officially supported application.
|
||||
local OPTIONS=() # Stores a list of options presented to the user.
|
||||
local APP_INSTALL="" # Stores the option selected by the user.
|
||||
local SELECTED_APPS=() # Stores the officially supported applications selected by the user.
|
||||
local TEMP_ARRAY=() # Temporary array used for sorting elements of an array.
|
||||
|
||||
# Read the list of officially supported applications that are installed on the Windows VM into an array, returning an empty array if no such files exist.
|
||||
# Read the list of officially supported applications that are installed on Windows into an array, returning an empty array if no such files exist.
|
||||
# This will remove leading and trailing whitespace characters as well as ignore empty lines.
|
||||
readarray -t OSA_LIST < <(grep -v '^[[:space:]]*$' "$INST_FILE_PATH" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' 2>/dev/null || true)
|
||||
|
||||
# Loop over each officially supported application installed on the Windows VM.
|
||||
# Loop over each officially supported application installed on Windows.
|
||||
for OSA in "${OSA_LIST[@]}"; do
|
||||
# Source 'Info' File Containing:
|
||||
# - The Application Name (FULL_NAME)
|
||||
@ -1146,7 +1275,7 @@ function waConfigureDetectedApps() {
|
||||
if [ -f "$DETECTED_FILE_PATH" ]; then
|
||||
# On UNIX systems, lines are terminated with a newline character (\n).
|
||||
# On WINDOWS systems, lines are terminated with both a carriage return (\r) and a newline (\n) character.
|
||||
# Remove all carriage returns (\r) within the 'detected' file, as the file was written by the Windows VM.
|
||||
# Remove all carriage returns (\r) within the 'detected' file, as the file was written by Windows.
|
||||
sed -i 's/\r//g' "$DETECTED_FILE_PATH"
|
||||
|
||||
# Import the detected application information:
|
||||
@ -1193,7 +1322,9 @@ function waConfigureDetectedApps() {
|
||||
if [[ $APP_INSTALL == "Select which applications to set up" ]]; then
|
||||
inqChkBx "Which other applications would you like to set up?" APPS SELECTED_APPS
|
||||
elif [[ $APP_INSTALL == "Set up all detected applications" ]]; then
|
||||
readarray -t SELECTED_APPS <<<"${APPS[@]}"
|
||||
for APP in "${APPS[@]}"; do
|
||||
SELECTED_APPS+=("$APP")
|
||||
done
|
||||
fi
|
||||
|
||||
for SELECTED_APP in "${SELECTED_APPS[@]}"; do
|
||||
@ -1222,7 +1353,7 @@ function waConfigureDetectedApps() {
|
||||
NAME=\"${PROGRAM_NAME}\"
|
||||
# Used for Descriptions and Window Class
|
||||
FULL_NAME=\"${PROGRAM_NAME}\"
|
||||
# Executable within Windows VM
|
||||
# Path to executable inside Windows
|
||||
WIN_EXECUTABLE=\"${EXES[$INDEX]}\"
|
||||
# GNOME Categories
|
||||
CATEGORIES=\"WinApps\"
|
||||
@ -1268,21 +1399,53 @@ function waInstall() {
|
||||
MULTI_FLAG="+span"
|
||||
fi
|
||||
|
||||
# Update $RDP_SCALE.
|
||||
waFixScale
|
||||
|
||||
# Append additional FreeRDP flags if required.
|
||||
if [[ -n $RDP_FLAGS ]]; then
|
||||
FREERDP_COMMAND="${FREERDP_COMMAND} ${RDP_FLAGS}"
|
||||
fi
|
||||
|
||||
# Check the group membership of the current user.
|
||||
waCheckGroupMembership
|
||||
# If using 'docker' or 'podman', set RDP_IP to localhost.
|
||||
if [ "$WAFLAVOR" = "docker" ] || [ "$WAFLAVOR" = "podman" ]; then
|
||||
RDP_IP="$DOCKER_IP"
|
||||
fi
|
||||
|
||||
# Check if the Windows VM is powered on.
|
||||
waCheckVMRunning
|
||||
# If using podman backend, modify the FreeRDP command to enter a new namespace.
|
||||
if [ "$WAFLAVOR" = "podman" ]; then
|
||||
FREERDP_COMMAND="podman unshare --rootless-netns ${FREERDP_COMMAND}"
|
||||
fi
|
||||
|
||||
# Check if the Windows VM is contactable.
|
||||
waCheckVMContactable
|
||||
if [ "$WAFLAVOR" = "docker" ] || [ "$WAFLAVOR" = "podman" ]; then
|
||||
# Check if Windows is powered on.
|
||||
waCheckContainerRunning
|
||||
elif [ "$WAFLAVOR" = "libvirt" ]; then
|
||||
# Verify the current user's group membership.
|
||||
waCheckGroupMembership # Check membership
|
||||
|
||||
# Test RDP access to the Windows VM.
|
||||
# Check if the Windows VM is powered on.
|
||||
waCheckVMRunning
|
||||
else
|
||||
# Display the error type.
|
||||
echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}INVALID WINAPPS BACKEND.${CLEAR_TEXT}"
|
||||
|
||||
# Display the error details.
|
||||
echo -e "${INFO_TEXT}An invalid WinApps backend '${WAFLAVOR}' was specified.${CLEAR_TEXT}"
|
||||
|
||||
# Display the suggested action(s).
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo -e "Please ensure 'WAFLAVOR' is set to 'docker', 'podman' or 'libvirt' in ${COMMAND_TEXT}${CONFIG_PATH}${CLEAR_TEXT}."
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
|
||||
# Terminate the script.
|
||||
return "$EC_INVALID_FLAVOR"
|
||||
fi
|
||||
|
||||
# Check if the RDP port on Windows is open.
|
||||
waCheckPortOpen
|
||||
|
||||
# Test RDP access to Windows.
|
||||
waCheckRDPAccess
|
||||
|
||||
# Create required directories.
|
||||
@ -1297,7 +1460,7 @@ function waInstall() {
|
||||
# Install the WinApps bash script.
|
||||
$SUDO cp "./bin/winapps" "${BIN_PATH}/winapps"
|
||||
|
||||
# Configure the Windows VM application launcher.
|
||||
# Configure the Windows RDP session application launcher.
|
||||
waConfigureWindows
|
||||
|
||||
if [ "$OPT_AOSA" -eq 1 ]; then
|
||||
|