Added ability to automatically start Windows.

This commit is contained in:
Rohan Barar 2024-07-28 21:29:37 +10:00
parent e72ef0c039
commit 2f8cf846ea
2 changed files with 238 additions and 49 deletions

View File

@ -1,19 +1,22 @@
#!/usr/bin/env bash #!/usr/bin/env bash
### GLOBAL CONSTANTS ### ### GLOBAL CONSTANTS ###
# ANSI ESCAPE SEQUENCES
readonly ERROR_TEXT="\033[1;41;37m" # Bold + White + Red Background
readonly CLEAR_TEXT="\033[0m" # Clear
# ERROR CODES # ERROR CODES
readonly EC_MISSING_CONFIG=1 readonly EC_MISSING_CONFIG=1
readonly EC_MISSING_FREERDP=2 readonly EC_MISSING_FREERDP=2
readonly EC_NOT_IN_GROUP=3 readonly EC_NOT_IN_GROUP=3
readonly EC_NOT_RUNNING=4 readonly EC_FAIL_START=4
readonly EC_NO_IP=5 readonly EC_FAIL_RESUME=5
readonly EC_BAD_PORT=6 readonly EC_FAIL_DESTROY=6
readonly EC_UNSUPPORTED_APP=7 readonly EC_SD_TIMEOUT=7
readonly EC_INVALID_FLAVOR=8 readonly EC_DIE_TIMEOUT=8
readonly EC_RESTART_TIMEOUT=9
readonly EC_NOT_EXIST=10
readonly EC_UNKNOWN=11
readonly EC_NO_IP=12
readonly EC_BAD_PORT=13
readonly EC_UNSUPPORTED_APP=14
readonly EC_INVALID_FLAVOR=15
# PATHS # PATHS
readonly APPDATA_PATH="${HOME}/.local/share/winapps" readonly APPDATA_PATH="${HOME}/.local/share/winapps"
@ -21,11 +24,13 @@ readonly SYS_APP_PATH="/usr/local/share/winapps"
readonly LASTRUN_PATH="${APPDATA_PATH}/lastrun" readonly LASTRUN_PATH="${APPDATA_PATH}/lastrun"
readonly LOG_PATH="${APPDATA_PATH}/winapps.log" readonly LOG_PATH="${APPDATA_PATH}/winapps.log"
readonly CONFIG_PATH="${HOME}/.config/winapps/winapps.conf" readonly CONFIG_PATH="${HOME}/.config/winapps/winapps.conf"
readonly COMPOSE_PATH="${HOME}/.config/winapps/compose.yaml"
# shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment. # shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment.
readonly SCRIPT_DIR_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" readonly SCRIPT_DIR_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
# OTHER # OTHER
readonly VM_NAME="RDPWindows" # FOR 'libvirt' ONLY readonly VM_NAME="RDPWindows" # FOR 'libvirt' ONLY
readonly CONTAINER_NAME="WinApps" # FOR 'docker' AND 'podman' ONLY
readonly RDP_PORT=3389 readonly RDP_PORT=3389
readonly DOCKER_IP="127.0.0.1" readonly DOCKER_IP="127.0.0.1"
# shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment. # shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment.
@ -57,52 +62,70 @@ function waThrowExit() {
"$EC_MISSING_CONFIG") "$EC_MISSING_CONFIG")
# Missing WinApps configuration file. # Missing WinApps configuration file.
dprint "ERROR: MISSING WINAPPS CONFIGURATION FILE. EXITING." dprint "ERROR: MISSING WINAPPS CONFIGURATION FILE. EXITING."
echo -e "${ERROR_TEXT}ERROR: MISSING WINAPPS CONFIGURATION FILE.${CLEAR_TEXT}" notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "The WinApps configuration file is missing.\nPlease create a WinApps configuration file at '${CONFIG_PATH}'."
echo "Please create a WinApps configuration file at '${CONFIG_PATH}'".
;; ;;
"$EC_MISSING_FREERDP") "$EC_MISSING_FREERDP")
dprint "ERROR: FREERDP VERSION 3 IS NOT INSTALLED. EXITING." dprint "ERROR: FREERDP VERSION 3 IS NOT INSTALLED. EXITING."
echo -e "${ERROR_TEXT}ERROR: FREERDP VERSION 3 IS NOT INSTALLED.${CLEAR_TEXT}" notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "FreeRDP version 3 is not installed."
;; ;;
"$EC_NOT_IN_GROUP") "$EC_NOT_IN_GROUP")
dprint "ERROR: USER NOT PART OF REQUIRED GROUPS. EXITING." dprint "ERROR: USER NOT PART OF REQUIRED GROUPS. EXITING."
echo -e "${ERROR_TEXT}ERROR: USER NOT PART OF REQUIRED GROUPS.${CLEAR_TEXT}" notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "The user $(whoami) is not part of the required groups.
echo "Please run:" Please run:
echo " sudo usermod -a -G libvirt $(whoami)" sudo usermod -a -G libvirt $(whoami)
echo " sudo usermod -a -G kvm $(whoami)" sudo usermod -a -G kvm $(whoami)"
;; ;;
"$EC_NOT_RUNNING") "$EC_FAIL_START")
dprint "ERROR: WINDOWS NOT RUNNING. EXITING." dprint "ERROR: WINDOWS FAILED TO START. EXITING."
echo -e "${ERROR_TEXT}ERROR: WINDOWS NOT RUNNING.${CLEAR_TEXT}" notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Windows failed to start."
echo "Please ensure Windows is running." ;;
"$EC_FAIL_RESUME")
dprint "ERROR: WINDOWS FAILED TO RESUME. EXITING."
notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Windows failed to resume."
;;
"$EC_FAIL_DESTROY")
dprint "ERROR: WINDOWS FAILED TO IMMEDIATELY UNGRACEFULLY SHUT DOWN WINDOWS. EXITING."
notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Failed to ungracefully shut down Windows."
;;
"$EC_SD_TIMEOUT")
dprint "ERROR: WINDOWS TOOK TOO LONG TO SHUT DOWN. EXITING."
notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Windows took too long to shut down."
;;
"$EC_DIE_TIMEOUT")
dprint "ERROR: WINDOWS TOOK TOO LONG TO SHUT DOWN. EXITING."
notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Windows took too long to die."
;;
"$EC_RESTART_TIMEOUT")
dprint "ERROR: WINDOWS TOOK TOO LONG TO RESTART. EXITING."
notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Windows took too long to restart."
;;
"$EC_NOT_EXIST")
dprint "ERROR: WINDOWS NONEXISTENT. EXITING."
notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Windows does not exist."
;;
"$EC_UNKNOWN")
dprint "ERROR: UNKNOWN CONTAINER ERROR. EXITING."
notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Unknown Windows container error."
;; ;;
"$EC_NO_IP") "$EC_NO_IP")
dprint "ERROR: WINDOWS UNREACHABLE. EXITING." dprint "ERROR: WINDOWS UNREACHABLE. EXITING."
echo -e "${ERROR_TEXT}ERROR: WINDOWS UNREACHABLE.${CLEAR_TEXT}" notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Windows is unreachable.\nPlease ensure Windows is assigned an IP address."
echo "Please ensure Windows is assigned an IP address."
;; ;;
"$EC_BAD_PORT") "$EC_BAD_PORT")
dprint "ERROR: RDP PORT CLOSED. EXITING." dprint "ERROR: RDP PORT CLOSED. EXITING."
echo -e "${ERROR_TEXT}ERROR: RDP PORT CLOSED.${CLEAR_TEXT}" notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "The Windows RDP port '${RDP_PORT}' is closed.\nPlease ensure Remote Desktop is correctly configured on Windows."
echo "Please ensure Remote Desktop is correctly configured on Windows."
;; ;;
"$EC_UNSUPPORTED_APP") "$EC_UNSUPPORTED_APP")
dprint "ERROR: APPLICATION NOT FOUND. EXITING." dprint "ERROR: APPLICATION NOT FOUND. EXITING."
echo -e "${ERROR_TEXT}ERROR: APPLICATION NOT FOUND.${CLEAR_TEXT}" notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Application not found.\nPlease ensure the program is correctly configured as an officially supported application."
echo "Please ensure the program is correctly configured as an officially supported application."
;; ;;
"$EC_INVALID_FLAVOR") "$EC_INVALID_FLAVOR")
dprint "ERROR: INVALID FLAVOR. EXITING." dprint "ERROR: INVALID FLAVOR. EXITING."
echo -e "${ERROR_TEXT}ERROR: INVALID FLAVOR.${CLEAR_TEXT}" notify-send --expire-time=8000 --icon="dialog-error" --app-name="WinApps" --urgency="low" "WinApps" "Invalid WinApps flavor.\nPlease ensure 'docker', 'podman' or 'libvirt' are specified as the flavor in the WinApps configuration file."
echo "Please ensure 'docker', 'podman' or 'libvirt' are specified as the flavor in the WinApps configuration file."
;; ;;
esac esac
# Provide generic advice.
echo "Check the WinApps project README for more information."
# Terminate the script. # Terminate the script.
echo "Exiting with status '${ERR_CODE}'."
exit "$ERR_CODE" exit "$ERR_CODE"
} }
@ -142,7 +165,7 @@ function waFixScale() {
# Print feedback. # Print feedback.
dprint "WARNING: Unsupported RDP_SCALE value '${OLD_SCALE}'. Defaulting to '${RDP_SCALE}'." 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}'." notify-send --expire-time=4000 --icon="dialog-warning" --app-name="WinApps" --urgency="low" "WinApps" "Unsupported RDP_SCALE value '${OLD_SCALE}'.\nDefaulting to '${RDP_SCALE}'."
fi fi
} }
@ -245,32 +268,158 @@ function waCheckGroupMembership() {
} }
# Name: 'waCheckVMRunning' # Name: 'waCheckVMRunning'
# Role: Throw an error if the Windows 'libvirt' VM is not running. # Role: Check if the Windows 'libvirt' VM is running, and attempt to start it if it is not.
function waCheckVMRunning() { function waCheckVMRunning() {
! virsh list --state-running --name | grep -q "^${VM_NAME}$" && waThrowExit "$EC_NOT_RUNNING" # Declare exit status variable.
local EXIT_STATUS=0
# Declare timer variables.
local TIME_ELAPSED=0
local TIME_LIMIT=60
local TIME_INTERVAL=5
# Attempt to run the Windows virtual machine.
# Note: States 'running' and 'idle' do not require intervention, and are not checked for.
if (virsh list --all --name | xargs | grep -wq "$VM_NAME"); then
if (virsh list --state-shutoff --name | xargs | grep -wq "$VM_NAME"); then
dprint "WINDOWS SHUT OFF. BOOTING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows."
virsh start "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_START
elif (virsh list --state-paused --name | xargs | grep -wq "$VM_NAME"); then
dprint "WINDOWS PAUSED. RESUMING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Resuming Windows."
virsh resume "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_RESUME
elif (virsh list --state-other --name | xargs | grep -wq "$VM_NAME"); then
if (virsh domstate "$VM_NAME" | xargs | grep -wq "shutdown"); then
dprint "WINDOWS SHUTTING DOWN. WAITING."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Windows is currently shutting down.\nIt will automatically restart once the shutdown process is complete."
EXIT_STATUS=$EC_SD_TIMEOUT
while (( TIME_ELAPSED < TIME_LIMIT )); do
if (virsh list --state-shutoff --name | xargs | grep -wq "$VM_NAME"); then
EXIT_STATUS=0
dprint "WINDOWS SHUT OFF. BOOTING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows."
virsh start "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_START
break
fi
sleep $TIME_INTERVAL
TIME_ELAPSED=$((TIME_ELAPSED + TIME_INTERVAL))
done
elif (virsh domstate "$VM_NAME" | xargs | grep -wq "crashed"); then
dprint "WINDOWS CRASHED. DESTROYING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Windows experienced an unexpected crash.\nAttempting to restart Windows."
virsh destroy "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_DESTROY
if [ "$EXIT_STATUS" -eq 0 ]; then
dprint "WINDOWS DESTROYED. BOOTING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows."
virsh start "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_START
fi
elif (virsh domstate "$VM_NAME" | xargs | grep -wq "dying"); then
dprint "WINDOWS DYING. WAITING."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Windows is currently shutting down unexpectedly.\nIt will try to restart once the shutdown process finishes."
EXIT_STATUS=$EC_DIE_TIMEOUT
while (( TIME_ELAPSED < TIME_LIMIT )); do
if (virsh domstate "$VM_NAME" | xargs | grep -wq "crashed"); then
EXIT_STATUS=0
dprint "WINDOWS CRASHED. DESTROYING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Windows experienced an unexpected crash.\nAttempting to restart Windows."
virsh destroy "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_DESTROY
if [ "$EXIT_STATUS" -eq 0 ]; then
dprint "WINDOWS DESTROYED. BOOTING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows."
virsh start "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_START
fi
break
elif (virsh list --state-shutoff --name | xargs | grep -wq "$VM_NAME"); then
EXIT_STATUS=0
dprint "WINDOWS SHUT OFF. BOOTING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows."
virsh start "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_START
break
fi
sleep $TIME_INTERVAL
TIME_ELAPSED=$((TIME_ELAPSED + TIME_INTERVAL))
done
elif (virsh domstate "$VM_NAME" | xargs | grep -wq "pmsuspended" ); then
dprint "WINDOWS SUSPENDED. RESUMING WINDOWS."
virsh resume "$VM_NAME" &>/dev/null || EXIT_STATUS=$EC_FAIL_RESUME
fi
fi
else
EXIT_STATUS=$EC_NOT_EXIST
fi
# Handle non-zero exit statuses.
[ "$EXIT_STATUS" -ne 0 ] && waThrowExit "$EXIT_STATUS"
} }
# Name: 'waCheckContainerRunning' # Name: 'waCheckContainerRunning'
# Role: Throw an error if the Docker container is not running. # Role: Throw an error if the Docker container is not running.
function waCheckContainerRunning() { function waCheckContainerRunning() {
# Declare variables. # Declare variables.
local EXIT_STATUS=0
local CONTAINER_STATE="" local CONTAINER_STATE=""
local COMPOSE_COMMAND=""
local TIME_ELAPSED=0
local TIME_LIMIT=60
local TIME_INTERVAL=5
# Determine container state (docker). # Determine the state of the container.
if command -v docker &>/dev/null; then CONTAINER_STATE=$("$WAFLAVOR" inspect --format='{{.State.Status}}' "$CONTAINER_NAME")
CONTAINER_STATE=$(docker ps --filter name="WinApps" --format '{{.Status}}')
fi
# Determine container state (podman). # Determine the compose command.
if [ -z "$CONTAINER_STATE" ]; then case "$WAFLAVOR" in
CONTAINER_STATE=$(podman ps --filter name="WinApps" --format '{{.Status}}') "docker") COMPOSE_COMMAND="docker compose" ;;
fi "podman") COMPOSE_COMMAND="podman-compose" ;;
esac
CONTAINER_STATE=${CONTAINER_STATE,,} # Convert the string to lowercase.
CONTAINER_STATE=${CONTAINER_STATE%% *} # Extract the first word.
# Check container state. # Check container state.
[[ "$CONTAINER_STATE" != "up" ]] && waThrowExit "$EC_NOT_RUNNING" # Note: Errors DO NOT result in non-zero exit statuses.
# Docker: 'created', 'restarting', 'running', 'removing', 'paused', 'exited' or 'dead'.
# Podman: 'created', 'running', 'paused', 'exited' or 'unknown'.
case "$CONTAINER_STATE" in
"created")
dprint "WINDOWS CREATED. BOOTING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows."
$COMPOSE_COMMAND --file "$COMPOSE_PATH" start &>/dev/null
;;
"restarting")
dprint "WINDOWS RESTARTING. WAITING."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Windows is currently restarting. Please wait."
EXIT_STATUS=$EC_RESTART_TIMEOUT
while (( TIME_ELAPSED < TIME_LIMIT )); do
if [[ $("$WAFLAVOR" inspect --format='{{.State.Status}}' "$CONTAINER_NAME") == "running" ]]; then
EXIT_STATUS=0
dprint "WINDOWS RESTARTED."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Restarted Windows."
break
fi
sleep $TIME_INTERVAL
TIME_ELAPSED=$((TIME_ELAPSED + TIME_INTERVAL))
done
;;
"paused")
dprint "WINDOWS PAUSED. RESUMING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Resuming Windows."
$COMPOSE_COMMAND --file "$COMPOSE_PATH" unpause &>/dev/null
;;
"exited")
dprint "WINDOWS SHUT OFF. BOOTING WINDOWS."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Booting Windows."
$COMPOSE_COMMAND --file "$COMPOSE_PATH" start &>/dev/null
;;
"dead")
dprint "WINDOWS DEAD. RECREATING WINDOWS CONTAINER."
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Re-creating and booting Windows."
$COMPOSE_COMMAND --file "$COMPOSE_PATH" down &>/dev/null && $COMPOSE_COMMAND --file "$COMPOSE_PATH" up -d &>/dev/null
;;
"unknown")
EXIT_STATUS=$EC_UNKNOWN
;;
esac
# Handle non-zero exit statuses.
[ "$EXIT_STATUS" -ne 0 ] && waThrowExit "$EXIT_STATUS"
} }
# Name: 'waCheckPortOpen' # Name: 'waCheckPortOpen'
@ -278,17 +427,30 @@ function waCheckContainerRunning() {
function waCheckPortOpen() { function waCheckPortOpen() {
# Declare variables. # Declare variables.
local VM_MAC="" # Stores the MAC address of the Windows VM. local VM_MAC="" # Stores the MAC address of the Windows VM.
local TIME_ELAPSED=0
local TIME_LIMIT=30
local TIME_INTERVAL=5
# Obtain Windows VM IP Address ('libvirt' ONLY) # 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. # 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 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. 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.
while (( TIME_ELAPSED < TIME_LIMIT )); do
if [ "$TIME_ELAPSED" -eq "$TIME_INTERVAL" ]; then
notify-send --expire-time=4000 --icon="dialog-info" --app-name="WinApps" --urgency="low" "WinApps" "Requesting Windows IP address..."
fi
RDP_IP=$(ip neigh show | grep "$VM_MAC" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}") # VM IP address.
[ -n "$RDP_IP" ] && break
sleep $TIME_INTERVAL
TIME_ELAPSED=$((TIME_ELAPSED + TIME_INTERVAL))
done
[ -z "$RDP_IP" ] && waThrowExit "$EC_NO_IP" [ -z "$RDP_IP" ] && waThrowExit "$EC_NO_IP"
fi fi
# Check for an open RDP port. # Check for an open RDP port.
timeout 5 nc -z "$RDP_IP" "$RDP_PORT" &>/dev/null || waThrowExit "$EC_BAD_PORT" timeout 10 nc -z "$RDP_IP" "$RDP_PORT" &>/dev/null || waThrowExit "$EC_BAD_PORT"
} }
# Name: 'waRunCommand' # Name: 'waRunCommand'

View File

@ -504,6 +504,33 @@ function waCheckInstallDependencies() {
# Print feedback. # Print feedback.
echo -n "Checking whether dependencies are installed... " echo -n "Checking whether dependencies are installed... "
# 'libnotify'
if ! command -v notify-send &>/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 'libnotify' to proceed.${CLEAR_TEXT}"
# Display the suggested action(s).
echo "--------------------------------------------------------------------------------"
echo "Debian/Ubuntu-based systems:"
echo -e " ${COMMAND_TEXT}sudo apt install libnotify-bin${CLEAR_TEXT}"
echo "Red Hat/Fedora-based systems:"
echo -e " ${COMMAND_TEXT}sudo dnf install libnotify${CLEAR_TEXT}"
echo "Arch Linux systems:"
echo -e " ${COMMAND_TEXT}sudo pacman -S libnotify${CLEAR_TEXT}"
echo "Gentoo Linux systems:"
echo -e " ${COMMAND_TEXT}sudo emerge --ask x11-libs/libnotify${CLEAR_TEXT}"
echo "--------------------------------------------------------------------------------"
# Terminate the script.
return "$EC_MISSING_DEPS"
fi
# 'Netcat' # 'Netcat'
if ! command -v nc &>/dev/null; then if ! command -v nc &>/dev/null; then
# Complete the previous line. # Complete the previous line.