#!/usr/bin/env bash # Standalone TA-managed Pulse LXC deployment bootstrap for Proxmox VE. set -u -o pipefail TAPM_PULSE_SOURCE_BRANCH="${TAPM_PULSE_SOURCE_BRANCH:-V2}" TAPM_PULSE_SOURCE_BASE="${TAPM_PULSE_SOURCE_BASE:-https://tagit.technologyarch.com/taiadmin/TA-ProxMenu/raw/branch/${TAPM_PULSE_SOURCE_BRANCH}}" TAPM_PULSE_BOOTSTRAP_DIR='' TAPM_TEMP_DIR='' declare -a TAPM_TEMP_DIRS=() declare -A idsCL=() TAPM_PULSE_VALID_SOURCE_BRANCH() { local branch="${1:-}" [[ -n "$branch" && "$branch" =~ ^[A-Za-z0-9._/-]+$ && "$branch" != /* && "$branch" != */ && "$branch" != *..* ]] } TAPM_PULSE_VALID_SOURCE_BASE() { local url="${1:-}" [[ "$url" == https://* && "$url" != *$'\n'* && "$url" != *$'\r'* && "$url" != *'"'* && "$url" != *\\* && "$url" != *[[:space:]]* ]] } TAPM_PULSE_DEFINE_COLORS() { if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then idsCL[Default]=$'\e[0m' idsCL[Red]=$'\e[31m' idsCL[Green]=$'\e[32m' idsCL[White]=$'\e[97m' idsCL[LightRed]=$'\e[91m' idsCL[LightGreen]=$'\e[92m' idsCL[LightYellow]=$'\e[93m' idsCL[LightCyan]=$'\e[96m' else idsCL[Default]='' idsCL[Red]='' idsCL[Green]='' idsCL[White]='' idsCL[LightRed]='' idsCL[LightGreen]='' idsCL[LightYellow]='' idsCL[LightCyan]='' fi } TAPM_CLEAN_TEMP_DIR() { local temp_dir="${1:-}" if [[ "$temp_dir" == /tmp/ta-proxmenu-* && -d "$temp_dir" ]]; then rm -rf -- "$temp_dir" fi } TAPM_CLEAN_ALL_TEMP_DIRS() { local temp_dir for temp_dir in "${TAPM_TEMP_DIRS[@]}"; do TAPM_CLEAN_TEMP_DIR "$temp_dir" done TAPM_CLEAN_TEMP_DIR "$TAPM_PULSE_BOOTSTRAP_DIR" } TAPM_CREATE_TEMP_DIR() { local label="${1:-installer}" TAPM_TEMP_DIR="$(mktemp -d "/tmp/ta-proxmenu-${label}.XXXXXX")" || { echo -e "${idsCL[LightRed]}Unable to create a temporary installer directory.${idsCL[Default]}" return 1 } if ! chmod 0700 "$TAPM_TEMP_DIR"; then TAPM_CLEAN_TEMP_DIR "$TAPM_TEMP_DIR" echo -e "${idsCL[LightRed]}Unable to secure the temporary installer directory.${idsCL[Default]}" return 1 fi TAPM_TEMP_DIRS+=("$TAPM_TEMP_DIR") } TAPM_DOWNLOAD_HTTPS() { local url="$1" local destination="$2" local label="${3:-Installer}" if ! TAPM_PULSE_VALID_SOURCE_BASE "$url"; then echo -e "${idsCL[LightRed]}${label} requires a valid HTTPS URL.${idsCL[Default]}" return 1 fi if ! printf 'url = "%s"\n' "$url" | curl --fail --location --silent --show-error \ --proto '=https' --proto-redir '=https' \ --output "$destination" --config -; then echo -e "${idsCL[LightRed]}${label} download failed.${idsCL[Default]}" return 1 fi if [[ ! -s "$destination" ]]; then echo -e "${idsCL[LightRed]}${label} download was empty.${idsCL[Default]}" return 1 fi } EXIT1() { stty echo 2>/dev/null || true printf '%s' "${idsCL[Default]}" } SELECT_MENU() { local title="$1" local labels_name="$2" local values_name="$3" local allow_back="${4:-1}" local -n labels_ref="$labels_name" local -n values_ref="$values_name" local selected=0 local key sequence index while true; do if [[ -t 1 ]]; then clear 2>/dev/null || printf '\e[H\e[2J' fi echo echo -e " ${idsCL[LightCyan]}${title}${idsCL[Default]}" echo for index in "${!labels_ref[@]}"; do if (( index == selected )); then printf '\e[7m %d %-64s\e[0m\n' \ "$((index + 1))" "${labels_ref[$index]}" else printf ' %d %s\n' "$((index + 1))" "${labels_ref[$index]}" fi done echo if (( allow_back == 1 )); then echo " ↑/↓ Navigate Enter Select Number Quick Select ←/Esc/B Back Q Quit" else echo " ↑/↓ Navigate Enter Select Number Quick Select Q Quit" fi IFS= read -rsn1 key case "$key" in "") MENU_SELECTION="${values_ref[$selected]}" return 0 ;; [1-9]) index=$((10#$key - 1)) if (( index < ${#values_ref[@]} )); then MENU_SELECTION="${values_ref[$index]}" return 0 fi ;; [Qq]) MENU_SELECTION=quit return 0 ;; [Bb]) if (( allow_back == 1 )); then MENU_SELECTION=back return 0 fi ;; $'\e') sequence='' IFS= read -rsn2 -t 0.1 sequence || true case "$sequence" in "[A"|"OA") ((selected = (selected - 1 + ${#labels_ref[@]}) % ${#labels_ref[@]})) ;; "[B"|"OB") ((selected = (selected + 1) % ${#labels_ref[@]})) ;; "[C"|"OC") MENU_SELECTION="${values_ref[$selected]}" return 0 ;; "[D"|"OD"|"") if (( allow_back == 1 )); then MENU_SELECTION=back return 0 fi ;; esac ;; esac done } TAPM_PULSE_STANDALONE_MAIN() { local iso_helpers pulse_module TAPM_PULSE_DEFINE_COLORS if (( BASH_VERSINFO[0] < 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 3) )); then echo "Pulse deployment requires Bash 4.3 or newer." >&2 return 1 fi if (( EUID != 0 )); then echo "Run this installer as root on a Proxmox VE host." >&2 return 1 fi for command in curl pct pvesh; do if ! command -v "$command" >/dev/null 2>&1; then echo "Required command '${command}' was not found." >&2 return 1 fi done if ! TAPM_PULSE_VALID_SOURCE_BRANCH "$TAPM_PULSE_SOURCE_BRANCH" || ! TAPM_PULSE_VALID_SOURCE_BASE "$TAPM_PULSE_SOURCE_BASE"; then echo "The configured TA-ProxMenu source branch or URL is invalid." >&2 return 1 fi TAPM_PULSE_BOOTSTRAP_DIR="$( mktemp -d /tmp/ta-proxmenu-pulse-bootstrap.XXXXXX )" || return 1 chmod 0700 "$TAPM_PULSE_BOOTSTRAP_DIR" || return 1 iso_helpers="${TAPM_PULSE_BOOTSTRAP_DIR}/deploy-iso-nfs-lxc.sh" pulse_module="${TAPM_PULSE_BOOTSTRAP_DIR}/deploy-pulse-lxc.sh" echo "Downloading the TA-managed Pulse deployment components..." TAPM_DOWNLOAD_HTTPS \ "${TAPM_PULSE_SOURCE_BASE}/inc/deploy-iso-nfs-lxc.sh" \ "$iso_helpers" "LXC storage helper" || return 1 TAPM_DOWNLOAD_HTTPS \ "${TAPM_PULSE_SOURCE_BASE}/inc/deploy-pulse-lxc.sh" \ "$pulse_module" "Pulse deployment module" || return 1 bash -n "$iso_helpers" "$pulse_module" || { echo "A downloaded Pulse deployment component failed validation." >&2 return 1 } grep -q '^TAPM_ISO_NFS_SELECT_STORAGE()' "$iso_helpers" && grep -q '^TAPM_DEPLOY_PULSE_LXC()' "$pulse_module" || { echo "The downloaded files were not recognized as TAPM deployment components." >&2 return 1 } # shellcheck disable=SC1090 source "$iso_helpers" # shellcheck disable=SC1090 source "$pulse_module" TAPM_DEPLOY_PULSE_LXC } if [[ "${TAPM_PULSE_STANDALONE_NO_MAIN:-0}" != 1 ]]; then trap TAPM_CLEAN_ALL_TEMP_DIRS EXIT TAPM_PULSE_STANDALONE_MAIN "$@" fi