Merge pull request #1123 from community-scripts/ref_core_descriptions

Add detailed documentation and refactor core functions
This commit is contained in:
CanbiZ 2025-11-17 11:17:54 +01:00 committed by GitHub
commit b69102ba26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 920 additions and 393 deletions

View File

@ -2,57 +2,71 @@
# Author: michelroegl-brunner # Author: michelroegl-brunner
# License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE # License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE
get_error_description() { # ==============================================================================
local exit_code="$1" # API.FUNC - TELEMETRY & DIAGNOSTICS API
case "$exit_code" in # ==============================================================================
0) echo " " ;; #
1) echo "General error: An unspecified error occurred." ;; # Provides functions for sending anonymous telemetry data to Community-Scripts
2) echo "Incorrect shell usage or invalid command arguments." ;; # API for analytics and diagnostics purposes.
3) echo "Unexecuted function or invalid shell condition." ;; #
4) echo "Error opening a file or invalid path." ;; # Features:
5) echo "I/O error: An input/output failure occurred." ;; # - Container/VM creation statistics
6) echo "No such device or address." ;; # - Installation success/failure tracking
7) echo "Insufficient memory or resource exhaustion." ;; # - Error code mapping and reporting
8) echo "Non-executable file or invalid file format." ;; # - Privacy-respecting anonymous telemetry
9) echo "Failed child process execution." ;; #
18) echo "Connection to a remote server failed." ;; # Usage:
22) echo "Invalid argument or faulty network connection." ;; # source <(curl -fsSL .../api.func)
28) echo "No space left on device." ;; # post_to_api # Report container creation
35) echo "Timeout while establishing a connection." ;; # post_update_to_api # Report installation status
56) echo "Faulty TLS connection." ;; #
60) echo "SSL certificate error." ;; # Privacy:
100) echo "LXC install error: Unexpected error in create_lxc.sh." ;; # - Only anonymous statistics (no personal data)
101) echo "LXC install error: No network connection detected." ;; # - User can opt-out via diagnostics settings
200) echo "LXC creation failed." ;; # - Random UUID for session tracking only
201) echo "LXC error: Invalid Storage class." ;; #
202) echo "User aborted menu in create_lxc.sh." ;; # ==============================================================================
203) echo "CTID not set in create_lxc.sh." ;;
204) echo "PCT_OSTYPE not set in create_lxc.sh." ;;
205) echo "CTID cannot be less than 100 in create_lxc.sh." ;;
206) echo "CTID already in use in create_lxc.sh." ;;
207) echo "Template not found in create_lxc.sh." ;;
208) echo "Error downloading template in create_lxc.sh." ;;
209) echo "Container creation failed, but template is intact in create_lxc.sh." ;;
125) echo "Docker error: Container could not start." ;;
126) echo "Command not executable: Incorrect permissions or missing dependencies." ;;
127) echo "Command not found: Incorrect path or missing dependency." ;;
128) echo "Invalid exit signal, e.g., incorrect Git command." ;;
129) echo "Signal 1 (SIGHUP): Process terminated due to hangup." ;;
130) echo "Signal 2 (SIGINT): Manual termination via Ctrl+C." ;;
132) echo "Signal 4 (SIGILL): Illegal machine instruction." ;;
133) echo "Signal 5 (SIGTRAP): Debugging error or invalid breakpoint signal." ;;
134) echo "Signal 6 (SIGABRT): Program aborted itself." ;;
135) echo "Signal 7 (SIGBUS): Memory error, invalid memory address." ;;
137) echo "Signal 9 (SIGKILL): Process forcibly terminated (OOM-killer or 'kill -9')." ;;
139) echo "Signal 11 (SIGSEGV): Segmentation fault, possibly due to invalid pointer access." ;;
141) echo "Signal 13 (SIGPIPE): Pipe closed unexpectedly." ;;
143) echo "Signal 15 (SIGTERM): Process terminated normally." ;;
152) echo "Signal 24 (SIGXCPU): CPU time limit exceeded." ;;
255) echo "Unknown critical error, often due to missing permissions or broken scripts." ;;
*) echo "Unknown error code ($exit_code)." ;;
esac
}
# ==============================================================================
# SECTION 1: DEPENDENCY LOADING
# ==============================================================================
# Load error_handler.func for explain_exit_code() function
# This provides centralized error code descriptions (exit codes 1-255, shell, package managers, databases, custom Proxmox codes)
if [[ -z "${COMMUNITY_SCRIPTS_BASE_URL:-}" ]]; then
COMMUNITY_SCRIPTS_BASE_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main"
fi
if ! declare -f explain_exit_code >/dev/null 2>&1; then
source <(curl -fsSL "${COMMUNITY_SCRIPTS_BASE_URL}/misc/error_handler.func") || {
echo "Failed to load error_handler.func" >&2
return 1
}
fi
# ==============================================================================
# SECTION 2: TELEMETRY FUNCTIONS
# ==============================================================================
# ------------------------------------------------------------------------------
# post_to_api()
#
# - Sends LXC container creation statistics to Community-Scripts API
# - Only executes if:
# * curl is available
# * DIAGNOSTICS=yes
# * RANDOM_UUID is set
# - Payload includes:
# * Container type, disk size, CPU cores, RAM
# * OS type and version
# * IPv6 disable status
# * Application name (NSAPP)
# * Installation method
# * PVE version
# * Status: "installing"
# * Random UUID for session tracking
# - Anonymous telemetry (no personal data)
# ------------------------------------------------------------------------------
post_to_api() { post_to_api() {
if ! command -v curl &>/dev/null; then if ! command -v curl &>/dev/null; then
@ -98,6 +112,18 @@ EOF
} }
# ------------------------------------------------------------------------------
# post_to_api_vm()
#
# - Sends VM creation statistics to Community-Scripts API
# - Similar to post_to_api() but for virtual machines (not containers)
# - Reads DIAGNOSTICS from /usr/local/community-scripts/diagnostics file
# - Payload differences:
# * ct_type=2 (VM instead of LXC)
# * type="vm"
# * Disk size without 'G' suffix (parsed from DISK_SIZE variable)
# - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set
# ------------------------------------------------------------------------------
post_to_api_vm() { post_to_api_vm() {
if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then
@ -148,7 +174,24 @@ EOF
fi fi
} }
POST_UPDATE_DONE=false # ------------------------------------------------------------------------------
# post_update_to_api()
#
# - Reports installation completion status to API
# - Prevents duplicate submissions via POST_UPDATE_DONE flag
# - Arguments:
# * $1: status ("success" or "failed")
# * $2: exit_code (default: 1 for failed, 0 for success)
# - Payload includes:
# * Final status (success/failed)
# * Error description via get_error_description()
# * Random UUID for session correlation
# - Only executes once per session
# - Silently returns if:
# * curl not available
# * Already reported (POST_UPDATE_DONE=true)
# * DIAGNOSTICS=no
# ------------------------------------------------------------------------------
post_update_to_api() { post_update_to_api() {
if ! command -v curl &>/dev/null; then if ! command -v curl &>/dev/null; then
@ -171,7 +214,7 @@ post_update_to_api() {
exit_code=1 exit_code=1
fi fi
error=$(get_error_description "$exit_code") error=$(explain_exit_code "$exit_code")
if [ -z "$error" ]; then if [ -z "$error" ]; then
error="Unknown error" error="Unknown error"

View File

@ -1,23 +1,42 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Copyright (c) 2021-2025 community-scripts ORG # Copyright (c) 2021-2025 community-scripts ORG
# Author: tteck (tteckster) | MickLesk | michelroegl-brunner # Author: tteck (tteckster) | MickLesk | michelroegl-brunner
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/branch/main/LICENSE
# Revision: 1 # Revision: 1
# ============================================================================== # ==============================================================================
# SECTION 1: CORE INITIALIZATION & VARIABLES # BUILD.FUNC - LXC CONTAINER BUILD & CONFIGURATION
# ==============================================================================
#
# This file provides the main build functions for creating and configuring
# LXC containers in Proxmox VE. It handles:
#
# - Variable initialization and defaults
# - Container creation and resource allocation
# - Storage selection and management
# - Advanced configuration and customization
# - User interaction menus and prompts
#
# Usage:
# - Sourced automatically by CT creation scripts
# - Requires core.func and error_handler.func to be loaded first
#
# ==============================================================================
# ==============================================================================
# SECTION 1: INITIALIZATION & CORE VARIABLES
# ============================================================================== # ==============================================================================
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# variables() # variables()
# #
# - Normalize application name (NSAPP = lowercase, no spaces) # - Initializes core variables for container creation
# - Build installer filename (var_install) # - Normalizes application name (NSAPP = lowercase, no spaces)
# - Define regex for integer validation # - Builds installer filename (var_install)
# - Fetch hostname of Proxmox node # - Defines regex patterns for validation
# - Set default values for diagnostics/method # - Fetches Proxmox hostname and version
# - Generate random UUID for tracking # - Generates unique session ID for tracking and logging
# - Get Proxmox VE version and kernel version # - Captures app-declared resource defaults (CPU, RAM, Disk)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
variables() { variables() {
NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces.
@ -171,6 +190,10 @@ elif command -v wget >/dev/null 2>&1; then
#echo "(build.func) Loaded core.func via wget" #echo "(build.func) Loaded core.func via wget"
fi fi
# ==============================================================================
# SECTION 2: PRE-FLIGHT CHECKS & SYSTEM VALIDATION
# ==============================================================================
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# maxkeys_check() # maxkeys_check()
# #
@ -224,12 +247,17 @@ maxkeys_check() {
# Silent success - only show errors if they exist # Silent success - only show errors if they exist
} }
# ==============================================================================
# SECTION 3: CONTAINER SETUP UTILITIES
# ==============================================================================
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# get_current_ip() # get_current_ip()
# #
# - Returns current container IP depending on OS type # - Returns current container IP depending on OS type
# - Debian/Ubuntu: uses `hostname -I` # - Debian/Ubuntu: uses `hostname -I`
# - Alpine: parses eth0 via `ip -4 addr` # - Alpine: parses eth0 via `ip -4 addr`
# - Returns "Unknown" if OS type cannot be determined
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
get_current_ip() { get_current_ip() {
if [ -f /etc/os-release ]; then if [ -f /etc/os-release ]; then
@ -356,7 +384,17 @@ find_host_ssh_keys() {
) )
} }
# ===== Unified storage selection & writing to vars files ===== # ==============================================================================
# SECTION 4: STORAGE & RESOURCE MANAGEMENT
# ==============================================================================
# ------------------------------------------------------------------------------
# _write_storage_to_vars()
#
# - Writes storage selection to vars file
# - Removes old entries (commented and uncommented) to avoid duplicates
# - Arguments: vars_file, key (var_container_storage/var_template_storage), value
# ------------------------------------------------------------------------------
_write_storage_to_vars() { _write_storage_to_vars() {
# $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value
local vf="$1" key="$2" val="$3" local vf="$1" key="$2" val="$3"
@ -407,6 +445,10 @@ choose_and_set_storage_for_file() {
# Silent operation - no output message # Silent operation - no output message
} }
# ==============================================================================
# SECTION 5: CONFIGURATION & DEFAULTS MANAGEMENT
# ==============================================================================
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# base_settings() # base_settings()
# #
@ -414,6 +456,7 @@ choose_and_set_storage_for_file() {
# - Reads from environment variables (var_*) # - Reads from environment variables (var_*)
# - Provides fallback defaults for OS type/version # - Provides fallback defaults for OS type/version
# - App-specific values take precedence when they are HIGHER (for CPU, RAM, DISK) # - App-specific values take precedence when they are HIGHER (for CPU, RAM, DISK)
# - Sets up container type, resources, network, SSH, features, and tags
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
base_settings() { base_settings() {
# Default Settings # Default Settings
@ -1014,13 +1057,27 @@ ensure_global_default_vars_file() {
echo "$vars_path" echo "$vars_path"
} }
# ==============================================================================
# SECTION 6: ADVANCED INTERACTIVE CONFIGURATION
# ==============================================================================
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# advanced_settings() # advanced_settings()
# #
# - Interactive whiptail menu for advanced configuration # - Interactive whiptail menu for comprehensive container configuration
# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM # - Allows user to customize:
# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode # * Container type (privileged/unprivileged)
# - Ends with confirmation or re-entry if cancelled # * Root password
# * Container ID (CTID)
# * Hostname
# * Resources (disk size, CPU cores, RAM)
# * Network (IPv4/IPv6, gateway, DNS, MAC, VLAN, MTU)
# * SSH settings and key injection
# * Advanced features (FUSE, TUN, keyctl)
# * Tags for organization
# * Verbose/debug mode
# - Loops until user confirms or cancels
# - Validates all input and shows current selections
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
advanced_settings() { advanced_settings() {
whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58
@ -1487,10 +1544,19 @@ advanced_settings() {
fi fi
} }
# ==============================================================================
# SECTION 7: USER INTERFACE & DIAGNOSTICS
# ==============================================================================
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# diagnostics_check() # diagnostics_check()
# #
# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics # - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics
# - Creates file if missing with default DIAGNOSTICS=yes
# - Reads current diagnostics setting from file
# - Sets global DIAGNOSTICS variable for API telemetry opt-in/out
# ------------------------------------------------------------------------------
diagnostics_check() {
# - Asks user whether to send anonymous diagnostic data # - Asks user whether to send anonymous diagnostic data
# - Saves DIAGNOSTICS=yes/no in the config file # - Saves DIAGNOSTICS=yes/no in the config file
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -2054,13 +2120,22 @@ start() {
fi fi
} }
# ==============================================================================
# SECTION 8: CONTAINER CREATION & DEPLOYMENT
# ==============================================================================
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# build_container() # build_container()
# #
# - Creates and configures the LXC container # - Main function for creating and configuring LXC container
# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) # - Builds network configuration string (IP, gateway, VLAN, MTU, MAC, IPv6)
# - Creates container via pct create with all specified settings
# - Applies features: FUSE, TUN, keyctl, VAAPI passthrough
# - Starts container and waits for network connectivity # - Starts container and waits for network connectivity
# - Installs base packages, SSH keys, and runs <app>-install.sh # - Installs base packages (curl, sudo, etc.)
# - Injects SSH keys if configured
# - Executes <app>-install.sh inside container
# - Posts installation telemetry to API if diagnostics enabled
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
build_container() { build_container() {
# if [ "$VERBOSE" == "yes" ]; then set -x; fi # if [ "$VERBOSE" == "yes" ]; then set -x; fi
@ -3389,12 +3464,21 @@ create_lxc_container() {
msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created."
} }
# ==============================================================================
# SECTION 9: POST-INSTALLATION & FINALIZATION
# ==============================================================================
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# description() # description()
# #
# - Sets container description with HTML content (logo, links, badges) # - Sets container description with formatted HTML content
# - Restarts ping-instances.service if present # - Includes:
# - Posts status "done" to API # * Community-Scripts logo
# * Application name
# * Links to GitHub, Discussions, Issues
# * Ko-fi donation badge
# - Restarts ping-instances.service if present (monitoring)
# - Posts final "done" status to API telemetry
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
description() { description() {
IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1)
@ -3439,31 +3523,23 @@ EOF
post_update_to_api "done" "none" post_update_to_api "done" "none"
} }
# ==============================================================================
# SECTION 10: ERROR HANDLING & EXIT TRAPS
# ==============================================================================
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# api_exit_script() # api_exit_script()
# #
# - Exit trap handler # - Exit trap handler for reporting to API telemetry
# - Reports exit codes to API with detailed reason # - Captures exit code and reports to API using centralized error descriptions
# - Handles known codes (100209) and maps them to errors # - Uses explain_exit_code() from error_handler.func for consistent error messages
# - Posts failure status with exit code to API (error description added automatically)
# - Only executes on non-zero exit codes
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
api_exit_script() { api_exit_script() {
exit_code=$? exit_code=$?
if [ $exit_code -ne 0 ]; then if [ $exit_code -ne 0 ]; then
case $exit_code in post_update_to_api "failed" "$exit_code"
100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;;
101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;;
200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;;
201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;;
202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;;
203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;;
204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;;
205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;;
206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;;
207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;;
208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;;
209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;;
*) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;;
esac
fi fi
} }

View File

@ -2,13 +2,34 @@
# Copyright (c) 2021-2025 community-scripts ORG # Copyright (c) 2021-2025 community-scripts ORG
# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE # License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE
# ------------------------------------------------------------------------------ # ==============================================================================
# Loads core utility groups once (colors, formatting, icons, defaults). # CORE FUNCTIONS - LXC CONTAINER UTILITIES
# ------------------------------------------------------------------------------ # ==============================================================================
#
# This file provides core utility functions for LXC container management
# including colors, formatting, validation checks, message output, and
# execution helpers used throughout the Community-Scripts ecosystem.
#
# Usage:
# source <(curl -fsSL https://git.community-scripts.org/.../core.func)
# load_functions
#
# ==============================================================================
[[ -n "${_CORE_FUNC_LOADED:-}" ]] && return [[ -n "${_CORE_FUNC_LOADED:-}" ]] && return
_CORE_FUNC_LOADED=1 _CORE_FUNC_LOADED=1
# ==============================================================================
# SECTION 1: INITIALIZATION & SETUP
# ==============================================================================
# ------------------------------------------------------------------------------
# load_functions()
#
# - Initializes all core utility groups (colors, formatting, icons, defaults)
# - Ensures functions are loaded only once via __FUNCTIONS_LOADED flag
# - Must be called at start of any script using these utilities
# ------------------------------------------------------------------------------
load_functions() { load_functions() {
[[ -n "${__FUNCTIONS_LOADED:-}" ]] && return [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return
__FUNCTIONS_LOADED=1 __FUNCTIONS_LOADED=1
@ -17,11 +38,14 @@ load_functions() {
icons icons
default_vars default_vars
set_std_mode set_std_mode
# add more
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Sets ANSI color codes used for styled terminal output. # color()
#
# - Sets ANSI color codes for styled terminal output
# - Variables: YW (yellow), YWB (yellow bright), BL (blue), RD (red)
# GN (green), DGN (dark green), BGN (background green), CL (clear)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
color() { color() {
YW=$(echo "\033[33m") YW=$(echo "\033[33m")
@ -34,7 +58,14 @@ color() {
CL=$(echo "\033[m") CL=$(echo "\033[m")
} }
# Special for spinner and colorized output via printf # ------------------------------------------------------------------------------
# color_spinner()
#
# - Sets ANSI color codes specifically for spinner animation
# - Variables: CS_YW (spinner yellow), CS_YWB (spinner yellow bright),
# CS_CL (spinner clear)
# - Used by spinner() function to avoid color conflicts
# ------------------------------------------------------------------------------
color_spinner() { color_spinner() {
CS_YW=$'\033[33m' CS_YW=$'\033[33m'
CS_YWB=$'\033[93m' CS_YWB=$'\033[93m'
@ -42,7 +73,12 @@ color_spinner() {
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Defines formatting helpers like tab, bold, and line reset sequences. # formatting()
#
# - Defines formatting helpers for terminal output
# - BFR: Backspace and clear line sequence
# - BOLD: Bold text escape code
# - TAB/TAB3: Indentation spacing
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
formatting() { formatting() {
BFR="\\r\\033[K" BFR="\\r\\033[K"
@ -53,7 +89,11 @@ formatting() {
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Sets symbolic icons used throughout user feedback and prompts. # icons()
#
# - Sets symbolic emoji icons used throughout user feedback
# - Provides consistent visual indicators for success, error, info, etc.
# - Icons: CM (checkmark), CROSS (error), INFO (info), HOURGLASS (wait), etc.
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
icons() { icons() {
CM="${TAB}✔️${TAB}" CM="${TAB}✔️${TAB}"
@ -84,21 +124,28 @@ icons() {
ADVANCED="${TAB}🧩${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}"
FUSE="${TAB}🗂️${TAB}${CL}" FUSE="${TAB}🗂️${TAB}${CL}"
HOURGLASS="${TAB}⏳${TAB}" HOURGLASS="${TAB}⏳${TAB}"
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Sets default retry and wait variables used for system actions. # default_vars()
#
# - Sets default retry and wait variables used for system actions
# - RETRY_NUM: Maximum number of retry attempts (default: 10)
# - RETRY_EVERY: Seconds to wait between retries (default: 3)
# - i: Counter variable initialized to RETRY_NUM
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
default_vars() { default_vars() {
RETRY_NUM=10 RETRY_NUM=10
RETRY_EVERY=3 RETRY_EVERY=3
i=$RETRY_NUM i=$RETRY_NUM
#[[ "${VAR_OS:-}" == "unknown" ]]
} }
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Sets default verbose mode for script and os execution. # set_std_mode()
#
# - Sets default verbose mode for script and OS execution
# - If VERBOSE=yes: STD="" (show all output)
# - If VERBOSE=no: STD="silent" (suppress output via silent() wrapper)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
set_std_mode() { set_std_mode() {
if [ "${VERBOSE:-no}" = "yes" ]; then if [ "${VERBOSE:-no}" = "yes" ]; then
@ -108,8 +155,148 @@ set_std_mode() {
fi fi
} }
# ==============================================================================
# SECTION 2: VALIDATION CHECKS
# ==============================================================================
# ------------------------------------------------------------------------------
# shell_check()
#
# - Verifies that the script is running under Bash shell
# - Exits with error message if different shell is detected
# - Required because scripts use Bash-specific features
# ------------------------------------------------------------------------------
shell_check() {
if [[ "$(ps -p $$ -o comm=)" != "bash" ]]; then
clear
msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell."
echo -e "\nExiting..."
sleep 2
exit
fi
}
# ------------------------------------------------------------------------------
# root_check()
#
# - Verifies script is running with root privileges
# - Detects if executed via sudo (which can cause issues)
# - Exits with error if not running as root directly
# ------------------------------------------------------------------------------
root_check() {
if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then
clear
msg_error "Please run this script as root."
echo -e "\nExiting..."
sleep 2
exit
fi
}
# ------------------------------------------------------------------------------
# pve_check()
#
# - Validates Proxmox VE version compatibility
# - Supported: PVE 8.0-8.9 and PVE 9.0 only
# - Exits with error message if unsupported version detected
# ------------------------------------------------------------------------------
pve_check() {
local PVE_VER
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
# Check for Proxmox VE 8.x: allow 8.08.9
if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR < 0 || MINOR > 9)); then
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported: Proxmox VE version 8.0 8.9"
exit 1
fi
return 0
fi
# Check for Proxmox VE 9.x: allow ONLY 9.0
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR != 0)); then
msg_error "This version of Proxmox VE is not yet supported."
msg_error "Supported: Proxmox VE version 9.0"
exit 1
fi
return 0
fi
# All other unsupported versions
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported versions: Proxmox VE 8.0 8.x or 9.0"
exit 1
}
# ------------------------------------------------------------------------------
# arch_check()
#
# - Validates system architecture is amd64/x86_64
# - Exits with error message for unsupported architectures (e.g., ARM/PiMox)
# - Provides link to ARM64-compatible scripts
# ------------------------------------------------------------------------------
arch_check() {
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n"
echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n"
echo -e "Exiting..."
sleep 2
exit
fi
}
# ------------------------------------------------------------------------------
# ssh_check()
#
# - Detects if script is running over SSH connection
# - Warns user for external SSH connections (recommends Proxmox shell)
# - Skips warning for local/same-subnet connections
# - Does not abort execution, only warns
# ------------------------------------------------------------------------------
ssh_check() {
if [ -n "$SSH_CLIENT" ]; then
local client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT")
local host_ip=$(hostname -I | awk '{print $1}')
# Check if connection is local (Proxmox WebUI or same machine)
# - localhost (127.0.0.1, ::1)
# - same IP as host
# - local network range (10.x, 172.16-31.x, 192.168.x)
if [[ "$client_ip" == "127.0.0.1" || "$client_ip" == "::1" || "$client_ip" == "$host_ip" ]]; then
return
fi
# Check if client is in same local network (optional, safer approach)
local host_subnet=$(echo "$host_ip" | cut -d. -f1-3)
local client_subnet=$(echo "$client_ip" | cut -d. -f1-3)
if [[ "$host_subnet" == "$client_subnet" ]]; then
return
fi
# Only warn for truly external connections
msg_warn "Running via external SSH (client: $client_ip)."
msg_warn "For better stability, consider using the Proxmox Shell (Console) instead."
fi
}
# ==============================================================================
# SECTION 3: EXECUTION HELPERS
# ==============================================================================
SILENT_LOGFILE="/tmp/install-$(date +%Y%m%d_%H%M%S)_${SESSION_ID:-$(date +%s)}.log" SILENT_LOGFILE="/tmp/install-$(date +%Y%m%d_%H%M%S)_${SESSION_ID:-$(date +%s)}.log"
# ------------------------------------------------------------------------------
# silent()
#
# - Executes command with output redirected to SILENT_LOGFILE
# - On error: displays last 10 lines of log and exits with original exit code
# - Temporarily disables error trap to capture exit code correctly
# - Sources explain_exit_code() for detailed error messages
# ------------------------------------------------------------------------------
silent() { silent() {
local cmd="$*" local cmd="$*"
local caller_line="${BASH_LINENO[0]:-unknown}" local caller_line="${BASH_LINENO[0]:-unknown}"
@ -152,206 +339,47 @@ silent() {
fi fi
} }
# Check if the shell is using bash
shell_check() {
if [[ "$(ps -p $$ -o comm=)" != "bash" ]]; then
clear
msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell."
echo -e "\nExiting..."
sleep 2
exit
fi
}
# Run as root only
root_check() {
if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then
clear
msg_error "Please run this script as root."
echo -e "\nExiting..."
sleep 2
exit
fi
}
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
# Supported: Proxmox VE 8.0.x 8.9.x and 9.0 (NOT 9.1+)
pve_check() {
local PVE_VER
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
# Check for Proxmox VE 8.x: allow 8.08.9
if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR < 0 || MINOR > 9)); then
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported: Proxmox VE version 8.0 8.9"
exit 1
fi
return 0
fi
# Check for Proxmox VE 9.x: allow ONLY 9.0
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
local MINOR="${BASH_REMATCH[1]}"
if ((MINOR != 0)); then
msg_error "This version of Proxmox VE is not yet supported."
msg_error "Supported: Proxmox VE version 9.0"
exit 1
fi
return 0
fi
# All other unsupported versions
msg_error "This version of Proxmox VE is not supported."
msg_error "Supported versions: Proxmox VE 8.0 8.x or 9.0"
exit 1
}
# This function checks the system architecture and exits if it's not "amd64".
arch_check() {
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n"
echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n"
echo -e "Exiting..."
sleep 2
exit
fi
}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# ssh_check() # spinner()
# #
# - Detects if script is running over SSH # - Displays animated spinner with rotating characters (⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏)
# - Warns user and recommends using Proxmox shell # - Shows SPINNER_MSG alongside animation
# - User can choose to continue or abort # - Runs in infinite loop until killed by stop_spinner()
# - Uses color_spinner() colors for output
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
ssh_check() {
if [ -n "$SSH_CLIENT" ]; then
local client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT")
local host_ip=$(hostname -I | awk '{print $1}')
# Check if connection is local (Proxmox WebUI or same machine)
# - localhost (127.0.0.1, ::1)
# - same IP as host
# - local network range (10.x, 172.16-31.x, 192.168.x)
if [[ "$client_ip" == "127.0.0.1" || "$client_ip" == "::1" || "$client_ip" == "$host_ip" ]]; then
return
fi
# Check if client is in same local network (optional, safer approach)
local host_subnet=$(echo "$host_ip" | cut -d. -f1-3)
local client_subnet=$(echo "$client_ip" | cut -d. -f1-3)
if [[ "$host_subnet" == "$client_subnet" ]]; then
return
fi
# Only warn for truly external connections
msg_warn "Running via external SSH (client: $client_ip)."
msg_warn "For better stability, consider using the Proxmox Shell (Console) instead."
fi
}
# ------------------------------------------------------------------------------
# exit_script()
#
# - Called when user cancels an action
# - Clears screen and exits gracefully
# ------------------------------------------------------------------------------
exit_script() {
clear
echo -e "\n${CROSS}${RD}User exited script${CL}\n"
exit
}
# Function to download & save header files
get_header() {
local app_name=$(echo "${APP,,}" | tr -d ' ')
local app_type=${APP_TYPE:-ct} # Default zu 'ct' falls nicht gesetzt
local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/${app_type}/headers/${app_name}"
local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}"
mkdir -p "$(dirname "$local_header_path")"
if [ ! -s "$local_header_path" ]; then
if ! curl -fsSL "$header_url" -o "$local_header_path"; then
return 1
fi
fi
cat "$local_header_path" 2>/dev/null || true
}
header_info() {
local app_name=$(echo "${APP,,}" | tr -d ' ')
local header_content
header_content=$(get_header "$app_name") || header_content=""
clear
local term_width
term_width=$(tput cols 2>/dev/null || echo 120)
if [ -n "$header_content" ]; then
echo "$header_content"
fi
}
ensure_tput() {
if ! command -v tput >/dev/null 2>&1; then
if grep -qi 'alpine' /etc/os-release; then
apk add --no-cache ncurses >/dev/null 2>&1
elif command -v apt-get >/dev/null 2>&1; then
apt-get update -qq >/dev/null
apt-get install -y -qq ncurses-bin >/dev/null 2>&1
fi
fi
}
is_alpine() {
local os_id="${var_os:-${PCT_OSTYPE:-}}"
if [[ -z "$os_id" && -f /etc/os-release ]]; then
os_id="$(
. /etc/os-release 2>/dev/null
echo "${ID:-}"
)"
fi
[[ "$os_id" == "alpine" ]]
}
is_verbose_mode() {
local verbose="${VERBOSE:-${var_verbose:-no}}"
local tty_status
if [[ -t 2 ]]; then
tty_status="interactive"
else
tty_status="not-a-tty"
fi
[[ "$verbose" != "no" || ! -t 2 ]]
}
fatal() {
msg_error "$1"
kill -INT $$
}
spinner() { spinner() {
local chars=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) local chars=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏)
local msg="${SPINNER_MSG:-Processing...}"
local i=0 local i=0
while true; do while true; do
local index=$((i++ % ${#chars[@]})) local index=$((i++ % ${#chars[@]}))
printf "\r\033[2K%s %b" "${CS_YWB}${chars[$index]}${CS_CL}" "${CS_YWB}${SPINNER_MSG:-}${CS_CL}" printf "\r\033[2K%s %b" "${CS_YWB}${chars[$index]}${CS_CL}" "${CS_YWB}${msg}${CS_CL}"
sleep 0.1 sleep 0.1
done done
} }
# ------------------------------------------------------------------------------
# clear_line()
#
# - Clears current terminal line using tput or ANSI escape codes
# - Moves cursor to beginning of line (carriage return)
# - Erases from cursor to end of line
# - Fallback to ANSI codes if tput not available
# ------------------------------------------------------------------------------
clear_line() { clear_line() {
tput cr 2>/dev/null || echo -en "\r" tput cr 2>/dev/null || echo -en "\r"
tput el 2>/dev/null || echo -en "\033[K" tput el 2>/dev/null || echo -en "\033[K"
} }
# ------------------------------------------------------------------------------
# stop_spinner()
#
# - Stops running spinner process by PID
# - Reads PID from SPINNER_PID variable or /tmp/.spinner.pid file
# - Attempts graceful kill, then forced kill if needed
# - Cleans up temp file and resets terminal state
# - Unsets SPINNER_PID and SPINNER_MSG variables
# ------------------------------------------------------------------------------
stop_spinner() { stop_spinner() {
local pid="${SPINNER_PID:-}" local pid="${SPINNER_PID:-}"
[[ -z "$pid" && -f /tmp/.spinner.pid ]] && pid=$(</tmp/.spinner.pid) [[ -z "$pid" && -f /tmp/.spinner.pid ]] && pid=$(</tmp/.spinner.pid)
@ -369,6 +397,19 @@ stop_spinner() {
stty sane 2>/dev/null || true stty sane 2>/dev/null || true
} }
# ==============================================================================
# SECTION 4: MESSAGE OUTPUT
# ==============================================================================
# ------------------------------------------------------------------------------
# msg_info()
#
# - Displays informational message with spinner animation
# - Shows each unique message only once (tracked via MSG_INFO_SHOWN)
# - In verbose/Alpine mode: shows hourglass icon instead of spinner
# - Stops any existing spinner before starting new one
# - Backgrounds spinner process and stores PID for later cleanup
# ------------------------------------------------------------------------------
msg_info() { msg_info() {
local msg="$1" local msg="$1"
[[ -z "$msg" ]] && return [[ -z "$msg" ]] && return
@ -395,6 +436,14 @@ msg_info() {
disown "$SPINNER_PID" 2>/dev/null || true disown "$SPINNER_PID" 2>/dev/null || true
} }
# ------------------------------------------------------------------------------
# msg_ok()
#
# - Displays success message with checkmark icon
# - Stops spinner and clears line before output
# - Removes message from MSG_INFO_SHOWN to allow re-display
# - Uses green color for success indication
# ------------------------------------------------------------------------------
msg_ok() { msg_ok() {
local msg="$1" local msg="$1"
[[ -z "$msg" ]] && return [[ -z "$msg" ]] && return
@ -404,18 +453,42 @@ msg_ok() {
unset MSG_INFO_SHOWN["$msg"] unset MSG_INFO_SHOWN["$msg"]
} }
# ------------------------------------------------------------------------------
# msg_error()
#
# - Displays error message with cross/X icon
# - Stops spinner before output
# - Uses red color for error indication
# - Outputs to stderr
# ------------------------------------------------------------------------------
msg_error() { msg_error() {
stop_spinner stop_spinner
local msg="$1" local msg="$1"
echo -e "${BFR:-}${CROSS:-✖️} ${RD}${msg}${CL}" >&2 echo -e "${BFR:-}${CROSS:-✖️} ${RD}${msg}${CL}" >&2
} }
# ------------------------------------------------------------------------------
# msg_warn()
#
# - Displays warning message with info/lightbulb icon
# - Stops spinner before output
# - Uses bright yellow color for warning indication
# - Outputs to stderr
# ------------------------------------------------------------------------------
msg_warn() { msg_warn() {
stop_spinner stop_spinner
local msg="$1" local msg="$1"
echo -e "${BFR:-}${INFO:-} ${YWB}${msg}${CL}" >&2 echo -e "${BFR:-}${INFO:-} ${YWB}${msg}${CL}" >&2
} }
# ------------------------------------------------------------------------------
# msg_custom()
#
# - Displays custom message with user-defined symbol and color
# - Arguments: symbol, color code, message text
# - Stops spinner before output
# - Useful for specialized status messages
# ------------------------------------------------------------------------------
msg_custom() { msg_custom() {
local symbol="${1:-"[*]"}" local symbol="${1:-"[*]"}"
local color="${2:-"\e[36m"}" local color="${2:-"\e[36m"}"
@ -425,13 +498,169 @@ msg_custom() {
echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}" echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}"
} }
function msg_debug() { # ------------------------------------------------------------------------------
# msg_debug()
#
# - Displays debug message with timestamp when var_full_verbose=1
# - Automatically enables var_verbose if not already set
# - Shows date/time prefix for log correlation
# - Uses bright yellow color for debug output
# ------------------------------------------------------------------------------
msg_debug() {
if [[ "${var_full_verbose:-0}" == "1" ]]; then if [[ "${var_full_verbose:-0}" == "1" ]]; then
[[ "${var_verbose:-0}" != "1" ]] && var_verbose=1 [[ "${var_verbose:-0}" != "1" ]] && var_verbose=1
echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*" echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*"
fi fi
} }
# ------------------------------------------------------------------------------
# fatal()
#
# - Displays error message and immediately terminates script
# - Sends SIGINT to current process to trigger error handler
# - Use for unrecoverable errors that require immediate exit
# ------------------------------------------------------------------------------
fatal() {
msg_error "$1"
kill -INT $$
}
# ==============================================================================
# SECTION 5: UTILITY FUNCTIONS
# ==============================================================================
# ------------------------------------------------------------------------------
# exit_script()
#
# - Called when user cancels an action
# - Clears screen and displays exit message
# - Exits with default exit code
# ------------------------------------------------------------------------------
exit_script() {
clear
echo -e "\n${CROSS}${RD}User exited script${CL}\n"
exit
}
# ------------------------------------------------------------------------------
# get_header()
#
# - Downloads and caches application header ASCII art
# - Falls back to local cache if already downloaded
# - Determines app type (ct/vm) from APP_TYPE variable
# - Returns header content or empty string on failure
# ------------------------------------------------------------------------------
get_header() {
local app_name=$(echo "${APP,,}" | tr -d ' ')
local app_type=${APP_TYPE:-ct} # Default zu 'ct' falls nicht gesetzt
local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/${app_type}/headers/${app_name}"
local local_header_path="/usr/local/community-scripts/headers/${app_type}/${app_name}"
mkdir -p "$(dirname "$local_header_path")"
if [ ! -s "$local_header_path" ]; then
if ! curl -fsSL "$header_url" -o "$local_header_path"; then
return 1
fi
fi
cat "$local_header_path" 2>/dev/null || true
}
# ------------------------------------------------------------------------------
# header_info()
#
# - Displays application header ASCII art at top of screen
# - Clears screen before displaying header
# - Detects terminal width for formatting
# - Returns silently if header not available
# ------------------------------------------------------------------------------
header_info() {
local app_name=$(echo "${APP,,}" | tr -d ' ')
local header_content
header_content=$(get_header "$app_name") || header_content=""
clear
local term_width
term_width=$(tput cols 2>/dev/null || echo 120)
if [ -n "$header_content" ]; then
echo "$header_content"
fi
}
# ------------------------------------------------------------------------------
# ensure_tput()
#
# - Ensures tput command is available for terminal control
# - Installs ncurses-bin on Debian/Ubuntu or ncurses on Alpine
# - Required for clear_line() and terminal width detection
# ------------------------------------------------------------------------------
ensure_tput() {
if ! command -v tput >/dev/null 2>&1; then
if grep -qi 'alpine' /etc/os-release; then
apk add --no-cache ncurses >/dev/null 2>&1
elif command -v apt-get >/dev/null 2>&1; then
apt-get update -qq >/dev/null
apt-get install -y -qq ncurses-bin >/dev/null 2>&1
fi
fi
}
# ------------------------------------------------------------------------------
# is_alpine()
#
# - Detects if running on Alpine Linux
# - Checks var_os, PCT_OSTYPE, or /etc/os-release
# - Returns 0 if Alpine, 1 otherwise
# - Used to adjust behavior for Alpine-specific commands
# ------------------------------------------------------------------------------
is_alpine() {
local os_id="${var_os:-${PCT_OSTYPE:-}}"
if [[ -z "$os_id" && -f /etc/os-release ]]; then
os_id="$(
. /etc/os-release 2>/dev/null
echo "${ID:-}"
)"
fi
[[ "$os_id" == "alpine" ]]
}
# ------------------------------------------------------------------------------
# is_verbose_mode()
#
# - Determines if script should run in verbose mode
# - Checks VERBOSE and var_verbose variables
# - Also returns true if not running in TTY (pipe/redirect scenario)
# - Used by msg_info() to decide between spinner and static output
# ------------------------------------------------------------------------------
is_verbose_mode() {
local verbose="${VERBOSE:-${var_verbose:-no}}"
local tty_status
if [[ -t 2 ]]; then
tty_status="interactive"
else
tty_status="not-a-tty"
fi
[[ "$verbose" != "no" || ! -t 2 ]]
}
# ==============================================================================
# SECTION 6: CLEANUP & MAINTENANCE
# ==============================================================================
# ------------------------------------------------------------------------------
# cleanup_lxc()
#
# - Comprehensive cleanup of package managers, caches, and logs
# - Supports Alpine (apk), Debian/Ubuntu (apt), and language package managers
# - Cleans: Python (pip/uv), Node.js (npm/yarn/pnpm), Go, Rust, Ruby, PHP
# - Truncates log files and vacuums systemd journal
# - Run at end of container creation to minimize disk usage
# ------------------------------------------------------------------------------
cleanup_lxc() { cleanup_lxc() {
msg_info "Cleaning up" msg_info "Cleaning up"
@ -480,6 +709,16 @@ cleanup_lxc() {
msg_ok "Cleaned" msg_ok "Cleaned"
} }
# ------------------------------------------------------------------------------
# check_or_create_swap()
#
# - Checks if swap is active on system
# - Offers to create swap file if none exists
# - Prompts user for swap size in MB
# - Creates /swapfile with specified size
# - Activates swap immediately
# - Returns 0 if swap active or successfully created, 1 if declined/failed
# ------------------------------------------------------------------------------
check_or_create_swap() { check_or_create_swap() {
msg_info "Checking for active swap" msg_info "Checking for active swap"
@ -518,4 +757,8 @@ check_or_create_swap() {
fi fi
} }
# ==============================================================================
# SIGNAL TRAPS
# ==============================================================================
trap 'stop_spinner' EXIT INT TERM trap 'stop_spinner' EXIT INT TERM

View File

@ -1,12 +1,44 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Error & Signal Handling for ProxmoxVED Scripts # ERROR HANDLER - ERROR & SIGNAL MANAGEMENT
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Copyright (c) 2021-2025 community-scripts ORG # Copyright (c) 2021-2025 community-scripts ORG
# Author: MickLesk (CanbiZ) # Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
#
# Provides comprehensive error handling and signal management for all scripts.
# Includes:
# - Exit code explanations (shell, package managers, databases, custom codes)
# - Error handler with detailed logging
# - Signal handlers (EXIT, INT, TERM)
# - Initialization function for trap setup
#
# Usage:
# source <(curl -fsSL .../error_handler.func)
# catch_errors
#
# ------------------------------------------------------------------------------
# ==============================================================================
# SECTION 1: EXIT CODE EXPLANATIONS
# ==============================================================================
# ------------------------------------------------------------------------------
# explain_exit_code()
#
# - Maps numeric exit codes to human-readable error descriptions
# - Supports:
# * Generic/Shell errors (1, 2, 126, 127, 128, 130, 137, 139, 143)
# * Package manager errors (APT, DPKG: 100, 101, 255)
# * Node.js/npm errors (243-249, 254)
# * Python/pip/uv errors (210-212)
# * PostgreSQL errors (231-234)
# * MySQL/MariaDB errors (241-244)
# * MongoDB errors (251-254)
# * Proxmox custom codes (200-231)
# - Returns description string for given exit code
# ------------------------------------------------------------------------------
explain_exit_code() { explain_exit_code() {
local code="$1" local code="$1"
case "$code" in case "$code" in
@ -79,7 +111,26 @@ explain_exit_code() {
esac esac
} }
# === Error handler ============================================================ # ==============================================================================
# SECTION 2: ERROR HANDLERS
# ==============================================================================
# ------------------------------------------------------------------------------
# error_handler()
#
# - Main error handler triggered by ERR trap
# - Arguments: exit_code, command, line_number
# - Behavior:
# * Returns silently if exit_code is 0 (success)
# * Sources explain_exit_code() for detailed error description
# * Displays error message with:
# - Line number where error occurred
# - Exit code with explanation
# - Command that failed
# * Shows last 20 lines of SILENT_LOGFILE if available
# * Copies log to container /root for later inspection
# * Exits with original exit code
# ------------------------------------------------------------------------------
error_handler() { error_handler() {
local exit_code=${1:-$?} local exit_code=${1:-$?}
local command=${2:-${BASH_COMMAND:-unknown}} local command=${2:-${BASH_COMMAND:-unknown}}
@ -141,14 +192,31 @@ error_handler() {
exit "$exit_code" exit "$exit_code"
} }
# === Exit handler ============================================================= # ==============================================================================
# SECTION 3: SIGNAL HANDLERS
# ==============================================================================
# ------------------------------------------------------------------------------
# on_exit()
#
# - EXIT trap handler
# - Cleans up lock files if lockfile variable is set
# - Exits with captured exit code
# - Always runs on script termination (success or failure)
# ------------------------------------------------------------------------------
on_exit() { on_exit() {
local exit_code=$? local exit_code=$?
[[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"
exit "$exit_code" exit "$exit_code"
} }
# === Signal handlers ========================================================== # ------------------------------------------------------------------------------
# on_interrupt()
#
# - SIGINT (Ctrl+C) trap handler
# - Displays "Interrupted by user" message
# - Exits with code 130 (128 + SIGINT=2)
# ------------------------------------------------------------------------------
on_interrupt() { on_interrupt() {
if declare -f msg_error >/dev/null 2>&1; then if declare -f msg_error >/dev/null 2>&1; then
msg_error "Interrupted by user (SIGINT)" msg_error "Interrupted by user (SIGINT)"
@ -158,6 +226,14 @@ on_interrupt() {
exit 130 exit 130
} }
# ------------------------------------------------------------------------------
# on_terminate()
#
# - SIGTERM trap handler
# - Displays "Terminated by signal" message
# - Exits with code 143 (128 + SIGTERM=15)
# - Triggered by external process termination
# ------------------------------------------------------------------------------
on_terminate() { on_terminate() {
if declare -f msg_error >/dev/null 2>&1; then if declare -f msg_error >/dev/null 2>&1; then
msg_error "Terminated by signal (SIGTERM)" msg_error "Terminated by signal (SIGTERM)"
@ -167,7 +243,25 @@ on_terminate() {
exit 143 exit 143
} }
# === Init traps =============================================================== # ==============================================================================
# SECTION 4: INITIALIZATION
# ==============================================================================
# ------------------------------------------------------------------------------
# catch_errors()
#
# - Initializes error handling and signal traps
# - Enables strict error handling:
# * set -Ee: Exit on error, inherit ERR trap in functions
# * set -o pipefail: Pipeline fails if any command fails
# * set -u: (optional) Exit on undefined variable (if STRICT_UNSET=1)
# - Sets up traps:
# * ERR → error_handler
# * EXIT → on_exit
# * INT → on_interrupt
# * TERM → on_terminate
# - Call this function early in every script
# ------------------------------------------------------------------------------
catch_errors() { catch_errors() {
set -Ee -o pipefail set -Ee -o pipefail
if [ "${STRICT_UNSET:-0}" = "1" ]; then if [ "${STRICT_UNSET:-0}" = "1" ]; then

View File

@ -4,6 +4,30 @@
# Co-Author: michelroegl-brunner # Co-Author: michelroegl-brunner
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
# ==============================================================================
# INSTALL.FUNC - CONTAINER INSTALLATION & SETUP
# ==============================================================================
#
# This file provides installation functions executed inside LXC containers
# after creation. Handles:
#
# - Network connectivity verification (IPv4/IPv6)
# - OS updates and package installation
# - DNS resolution checks
# - MOTD and SSH configuration
# - Container customization and auto-login
#
# Usage:
# - Sourced by <app>-install.sh scripts
# - Executes via pct exec inside container
# - Requires internet connectivity
#
# ==============================================================================
# ==============================================================================
# SECTION 1: INITIALIZATION
# ==============================================================================
if ! command -v curl >/dev/null 2>&1; then if ! command -v curl >/dev/null 2>&1; then
printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2
apt-get update >/dev/null 2>&1 apt-get update >/dev/null 2>&1
@ -14,7 +38,17 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV
load_functions load_functions
catch_errors catch_errors
# This function enables IPv6 if it's not disabled and sets verbose mode # ==============================================================================
# SECTION 2: NETWORK & CONNECTIVITY
# ==============================================================================
# ------------------------------------------------------------------------------
# verb_ip6()
#
# - Configures IPv6 based on DISABLEIPV6 variable
# - If DISABLEIPV6=yes: disables IPv6 via sysctl
# - Sets verbose mode via set_std_mode()
# ------------------------------------------------------------------------------
verb_ip6() { verb_ip6() {
set_std_mode # Set STD mode based on VERBOSE set_std_mode # Set STD mode based on VERBOSE
@ -24,29 +58,15 @@ verb_ip6() {
fi fi
} }
# # This function sets error handling options and defines the error_handler function to handle errors # ------------------------------------------------------------------------------
# catch_errors() { # setting_up_container()
# set -Eeuo pipefail #
# trap 'error_handler $LINENO "$BASH_COMMAND"' ERR # - Verifies network connectivity via hostname -I
# } # - Retries up to RETRY_NUM times with RETRY_EVERY seconds delay
# - Removes Python EXTERNALLY-MANAGED restrictions
# # This function handles errors # - Disables systemd-networkd-wait-online.service for faster boot
# error_handler() { # - Exits with error if network unavailable after retries
# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) # ------------------------------------------------------------------------------
# local exit_code="$1"
# local line_number="$2"
# local command="${3:-}"
# if [[ "$exit_code" -eq 0 ]]; then
# return 0
# fi
# printf "\e[?25h"
# echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n"
# exit "$exit_code"
#}
# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection
setting_up_container() { setting_up_container() {
msg_info "Setting up Container OS" msg_info "Setting up Container OS"
for ((i = RETRY_NUM; i > 0; i--)); do for ((i = RETRY_NUM; i > 0; i--)); do
@ -68,7 +88,17 @@ setting_up_container() {
msg_ok "Network Connected: ${BL}$(hostname -I)" msg_ok "Network Connected: ${BL}$(hostname -I)"
} }
# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected # ------------------------------------------------------------------------------
# network_check()
#
# - Comprehensive network connectivity check for IPv4 and IPv6
# - Tests connectivity to multiple DNS servers:
# * IPv4: 1.1.1.1 (Cloudflare), 8.8.8.8 (Google), 9.9.9.9 (Quad9)
# * IPv6: 2606:4700:4700::1111, 2001:4860:4860::8888, 2620:fe::fe
# - Verifies DNS resolution for GitHub and Community-Scripts domains
# - Prompts user to continue if no internet detected
# - Uses fatal() on DNS resolution failure for critical hosts
# ------------------------------------------------------------------------------
network_check() { network_check() {
set +e set +e
trap - ERR trap - ERR
@ -128,7 +158,19 @@ network_check() {
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
} }
# This function updates the Container OS by running apt-get update and upgrade # ==============================================================================
# SECTION 3: OS UPDATE & PACKAGE MANAGEMENT
# ==============================================================================
# ------------------------------------------------------------------------------
# update_os()
#
# - Updates container OS via apt-get update and dist-upgrade
# - Configures APT cacher proxy if CACHER=yes (accelerates package downloads)
# - Removes Python EXTERNALLY-MANAGED restrictions for pip
# - Sources tools.func for additional setup functions after update
# - Uses $STD wrapper to suppress output unless VERBOSE=yes
# ------------------------------------------------------------------------------
update_os() { update_os() {
msg_info "Updating Container OS" msg_info "Updating Container OS"
if [[ "$CACHER" == "yes" ]]; then if [[ "$CACHER" == "yes" ]]; then
@ -150,7 +192,24 @@ EOF
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func)
} }
# This function modifies the message of the day (motd) and SSH settings # ==============================================================================
# SECTION 4: MOTD & SSH CONFIGURATION
# ==============================================================================
# ------------------------------------------------------------------------------
# motd_ssh()
#
# - Configures Message of the Day (MOTD) with container information
# - Creates /etc/profile.d/00_lxc-details.sh with:
# * Application name
# * Warning banner (DEV repository)
# * OS name and version
# * Hostname and IP address
# * GitHub repository link
# - Disables executable flag on /etc/update-motd.d/* scripts
# - Enables root SSH access if SSH_ROOT=yes
# - Configures TERM environment variable for better terminal support
# ------------------------------------------------------------------------------
motd_ssh() { motd_ssh() {
grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc
@ -180,7 +239,19 @@ motd_ssh() {
fi fi
} }
# This function customizes the container by modifying the getty service and enabling auto-login for the root user # ==============================================================================
# SECTION 5: CONTAINER CUSTOMIZATION
# ==============================================================================
# ------------------------------------------------------------------------------
# customize()
#
# - Customizes container for passwordless root login if PASSWORD is empty
# - Configures getty for auto-login via /etc/systemd/system/container-getty@1.service.d/override.conf
# - Creates /usr/bin/update script for easy application updates
# - Injects SSH authorized keys if SSH_AUTHORIZED_KEY variable is set
# - Sets proper permissions on SSH directories and key files
# ------------------------------------------------------------------------------
customize() { customize() {
if [[ "$PASSWORD" == "" ]]; then if [[ "$PASSWORD" == "" ]]; then
msg_info "Customizing Container" msg_info "Customizing Container"