From 1f7d85ac60bccce2f903f4d6a369346178d0e71b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:28:06 +0200 Subject: [PATCH] [core]: unify misc/*.func scripts with centralized logic from core.func (#5316) * Core Changes * Formatting * add create_lxc patches * Update install.func * remove dev header --- misc/alpine-install.func | 80 ++--------- misc/api.func | 51 +++---- misc/build.func | 297 ++++++++++----------------------------- misc/config-file.func | 245 ++++++++++++++++---------------- misc/core.func | 29 +++- misc/create_lxc.sh | 268 ++++++++++++++++------------------- misc/install.func | 134 ++++-------------- 7 files changed, 404 insertions(+), 700 deletions(-) diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 5053519c4..909b51043 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -1,52 +1,13 @@ -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2025 community-scripts ORG # Author: tteck (tteckster) # Co-Author: MickLesk -# License: MIT -# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# This function sets color variables for formatting output in the terminal -color() { - # Colors - YW=$(echo "\033[33m") - BL=$(echo "\033[36m") - RD=$(echo "\033[01;31m") - GN=$(echo "\033[1;92m") - - # Formatting - CL=$(echo "\033[m") - BFR="\\r\\033[K" - BOLD=$(echo "\033[1m") - TAB=" " - TAB3=" " - - # System - RETRY_NUM=10 - RETRY_EVERY=3 - i=$RETRY_NUM - - # Icons - CM="${TAB}βœ”οΈ${TAB}${CL}" - CROSS="${TAB}βœ–οΈ${TAB}${CL}" - INFO="${TAB}πŸ’‘${TAB}${CL}" - NETWORK="${TAB}πŸ“‘${TAB}${CL}" - OS="${TAB}πŸ–₯️${TAB}${CL}" - HOSTNAME="${TAB}🏠${TAB}${CL}" - GATEWAY="${TAB}🌐${TAB}${CL}" -} - -# Function to set STD mode based on verbosity -set_std_mode() { - if [ "$VERBOSE" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - "$@" >/dev/null 2>&1 -} +if ! command -v curl >/dev/null 2>&1; then + apk update && apk add curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) +load_functions # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { @@ -74,33 +35,15 @@ error_handler() { echo -e "\n$error_message\n" } -# This function displays an informational message with a yellow color. -msg_info() { - local msg="$1" - echo -ne " ${TAB}${YW}${msg}" -} - -# This function displays a success message with a green color. -msg_ok() { - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -msg_error() { - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection setting_up_container() { msg_info "Setting up Container OS" - while [ "$i" -gt 0 ]; do + while [ $i -gt 0 ]; do if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then break fi echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep "$RETRY_EVERY" + sleep $RETRY_EVERY i=$((i - 1)) done @@ -149,10 +92,9 @@ update_os() { # This function modifies the message of the day (motd) and SSH settings motd_ssh() { - # Set terminal to 256-color mode echo "export TERM='xterm-256color'" >>/root/.bashrc IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) - # Get OS information + if [ -f "/etc/os-release" ]; then OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') @@ -213,6 +155,6 @@ EOF msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVE/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update } diff --git a/misc/api.func b/misc/api.func index 67f0b6510..5fd550b77 100644 --- a/misc/api.func +++ b/misc/api.func @@ -4,7 +4,7 @@ post_to_api() { - if ! command -v curl &> /dev/null; then + if ! command -v curl &>/dev/null; then return fi @@ -20,7 +20,8 @@ post_to_api() { local pve_version="not found" pve_version=$(pveversion | awk -F'[/ ]' '{print $2}') - JSON_PAYLOAD=$(cat < /dev/null; then - return - fi + if ! command -v curl &>/dev/null; then + return + fi - if [ "$POST_UPDATE_DONE" = true ]; then - return 0 - fi - local API_URL="http://api.community-scripts.org/upload/updatestatus" - local status="${1:-failed}" - local error="${2:-No error message}" + if [ "$POST_UPDATE_DONE" = true ]; then + return 0 + fi + local API_URL="http://api.community-scripts.org/upload/updatestatus" + local status="${1:-failed}" + local error="${2:-No error message}" - JSON_PAYLOAD=$(cat </dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) + load_functions + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) + load_functions + #echo "(build.func) Loaded core.func via wget" +fi # This function enables error handling in the script by setting options and defining a trap for the ERR signal. catch_errors() { set -Eeuo pipefail @@ -71,7 +35,6 @@ catch_errors() { # This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. error_handler() { source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi printf "\e[?25h" local exit_code="$?" local line_number="$1" @@ -81,78 +44,6 @@ error_handler() { echo -e "\n$error_message\n" } -# This function displays an informational message with logging support. -declare -A MSG_INFO_SHOWN -SPINNER_ACTIVE=0 -SPINNER_PID="" -SPINNER_MSG="" - -trap 'stop_spinner' EXIT INT TERM HUP - -start_spinner() { - local msg="$1" - local frames=(β ‹ β ™ β Ή β Έ β Ό β ΄ β ¦ β § β ‡ ⠏) - local spin_i=0 - local interval=0.1 - - SPINNER_MSG="$msg" - printf "\r\e[2K" >&2 - - { - while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do - printf "\r\e[2K%s %b" "${frames[spin_i]}" "${YW}${SPINNER_MSG}${CL}" >&2 - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done - } & - - SPINNER_PID=$! - disown "$SPINNER_PID" -} - -stop_spinner() { - if [[ ${SPINNER_PID+v} && -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then - kill "$SPINNER_PID" 2>/dev/null - sleep 0.1 - kill -0 "$SPINNER_PID" 2>/dev/null && kill -9 "$SPINNER_PID" 2>/dev/null - wait "$SPINNER_PID" 2>/dev/null || true - fi - SPINNER_ACTIVE=0 - unset SPINNER_PID -} - -spinner_guard() { - if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "$SPINNER_PID" ]]; then - kill "$SPINNER_PID" 2>/dev/null - wait "$SPINNER_PID" 2>/dev/null || true - SPINNER_ACTIVE=0 - unset SPINNER_PID - fi -} - -msg_info() { - local msg="$1" - [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return - MSG_INFO_SHOWN["$msg"]=1 - - spinner_guard - SPINNER_ACTIVE=1 - start_spinner "$msg" -} - -msg_ok() { - local msg="$1" - stop_spinner - printf "\r\e[2K%s %b\n" "${CM}" "${GN}${msg}${CL}" >&2 - unset MSG_INFO_SHOWN["$msg"] -} - -msg_error() { - stop_spinner - local msg="$1" - printf "\r\e[2K%s %b\n" "${CROSS}" "${RD}${msg}${CL}" >&2 -} - # Check if the shell is using bash shell_check() { if [[ "$(basename "$SHELL")" != "bash" ]]; then @@ -273,42 +164,6 @@ update_motd_ip() { fi } -# Function to download & save header files -get_header() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local header_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/headers/${app_name}" - local local_header_path="/usr/local/community-scripts/headers/${app_name}" - - mkdir -p "$(dirname "$local_header_path")" - - if [ ! -s "$local_header_path" ]; then - if ! curl -fsSL "$header_url" -o "$local_header_path"; then - echo -e "Failed to download header for ${app_name}. No header will be displayed." - return 1 - fi - fi - - cat "$local_header_path" -} - -# This function sets the APP-Name into an ASCII Header in Slant, figlet needed on proxmox main node. -header_info() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local header_content - - # Download & save Header-File locally - header_content=$(get_header "$app_name") - if [ $? -ne 0 ]; then - # Fallback: Doesn't show Header - return 0 - fi - - # Show ASCII-Header - term_width=$(tput cols 2>/dev/null || echo 120) - clear - echo "$header_content" -} - # This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit. ssh_check() { if [ -n "${SSH_CLIENT:+x}" ]; then @@ -346,8 +201,8 @@ base_settings() { SSH="no" SSH_AUTHORIZED_KEY="" TAGS="community-script;" - ENABLE_FUSE="no" - ENABLE_TUN="no" + ENABLE_FUSE="${1:-no}" + ENABLE_TUN="${1:-no}" # Override default settings with variables from ct script CT_TYPE=${var_unprivileged:-$CT_TYPE} @@ -516,32 +371,40 @@ advanced_settings() { fi done - while true; do + while true; do if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - if [[ ! -z "$PW1" ]]; then - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 - elif [ ${#PW1} -lt 5 ]; then - whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 - else - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi - else - exit_script - fi - fi - else - PW1="Automatic Login" + # Empty = Autologin + if [[ -z "$PW1" ]]; then PW="" + PW1="Automatic Login" echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" break fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi else exit_script fi @@ -565,7 +428,7 @@ advanced_settings() { else HN=$(echo "${CT_NAME,,}" | tr -d ' ') fi - + # Hostname validate (RFC 1123) if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" break @@ -824,11 +687,11 @@ advanced_settings() { echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then - VERB="yes" + VERBOSE="yes" else - VERB="no" + VERBOSE="no" fi - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERB${CL}" + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" @@ -944,18 +807,18 @@ install_script() { 1) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERB="no" + VERBOSE="no" METHOD="default" - base_settings "$VERB" + base_settings "$VERBOSE" echo_default break ;; 2) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" - VERB="yes" + VERBOSE="yes" METHOD="default" - base_settings "$VERB" + base_settings "$VERBOSE" echo_default break ;; @@ -1044,18 +907,9 @@ check_container_storage() { } start() { - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) - if command -v pveversion >/dev/null 2>&1; then - if ! (whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC" --yesno "This will create a New ${APP} LXC. Proceed?" 10 58); then - clear - exit_script - exit - fi - SPINNER_PID="" + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script - fi - - if ! command -v pveversion >/dev/null 2>&1; then + else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ "Support/Update functions for ${APP} LXC. Choose an option:" \ 12 60 3 \ @@ -1065,11 +919,11 @@ start() { case "$CHOICE" in 1) - VERB="no" + VERBOSE="no" set_std_mode ;; 2) - VERB="yes" + VERBOSE="yes" set_std_mode ;; 3) @@ -1078,15 +932,13 @@ start() { exit ;; esac - - SPINNER_PID="" update_script fi } # This function collects user settings and integrates all the collected information. build_container() { - # if [ "$VERB" == "yes" ]; then set -x; fi + # if [ "$VERBOSE" == "yes" ]; then set -x; fi if [ "$CT_TYPE" == "1" ]; then FEATURES="keyctl=1,nesting=1" @@ -1119,7 +971,7 @@ build_container() { export APPLICATION="$APP" export app="$NSAPP" export PASSWORD="$PW" - export VERBOSE="$VERB" + export VERBOSE="$VERBOSE" export SSH_ROOT="${SSH}" export SSH_AUTHORIZED_KEY export CTID="$CT_ID" @@ -1200,20 +1052,34 @@ lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF fi - # This starts the container and executes -install.sh + # This starts the container and executes -install.sh msg_info "Starting LXC Container" pct start "$CTID" msg_ok "Started LXC Container" + msg_info "Customizing LXC Container" if [ "$var_os" == "alpine" ]; then sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/community EOF' - pct exec "$CTID" -- ash -c "apk add bash >/dev/null" - fi - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/"$var_install".sh)" $? + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses >/dev/null" + else + sleep 3 + # Set locale and timezone before update + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" + pct exec "$CTID" -- bash -c "echo $tz >/etc/timezone && ln -sf /usr/share/zoneinfo/$tz /etc/localtime" + + # Install curl + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null" + fi + msg_ok "Customized LXC Container" +lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/"$var_install".sh)" $? } # This function sets the description of the container. @@ -1262,23 +1128,6 @@ EOF post_update_to_api "done" "none" } -set_std_mode() { - if [ "$VERB" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - if [ "$VERB" = "no" ]; then - "$@" >/dev/null 2>&1 || return 1 - else - "$@" || return 1 - fi -} - api_exit_script() { exit_code=$? # Capture the exit status of the last executed command #200 exit codes indicate error in create_lxc.sh diff --git a/misc/config-file.func b/misc/config-file.func index efaa2de82..401cd73cb 100644 --- a/misc/config-file.func +++ b/misc/config-file.func @@ -53,15 +53,15 @@ config_file() { fi else if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then - if [ -z "$CT_ID" ]; then - CT_ID="$NEXTID" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + else + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + fi else - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + exit_script fi - else - exit_script - fi fi if [[ -n "${CT_TYPE-}" ]]; then @@ -75,23 +75,22 @@ config_file() { fi echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" else - if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" ON \ - "0" "Privileged" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" fi - else - exit_script + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" fi + else + exit_script + fi fi - if [[ -n "${PW-}" ]]; then if [[ "$PW" == "none" ]]; then PW="" @@ -109,50 +108,50 @@ config_file() { fi else while true; do - if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - if [[ -n "$PW1" ]]; then - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 - elif [ ${#PW1} -lt 5 ]; then - whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 - else - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi + if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + if [[ -n "$PW1" ]]; then + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 + elif [ ${#PW1} -lt 5 ]; then + whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 else - exit_script + if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi fi + else + PW1="Automatic Login" + PW="" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break fi else - PW1="Automatic Login" - PW="" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" - break + exit_script fi - else - exit_script - fi - done + done fi if [[ -n "${HN-}" ]]; then echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else - HN=$(echo "${CT_NAME,,}" | tr -d ' ') + exit_script fi - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else - exit_script - fi fi if [[ -n "${DISK_SIZE-}" ]]; then @@ -164,19 +163,19 @@ config_file() { fi else if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3); then - if [ -z "$DISK_SIZE" ]; then - DISK_SIZE="$var_disk" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - else - if ! [[ $DISK_SIZE =~ $INTEGER ]]; then - echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" - advanced_settings + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + else + if ! [[ $DISK_SIZE =~ $INTEGER ]]; then + echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" + advanced_settings + fi + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" fi - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + else + exit_script fi - else - exit_script - fi fi if [[ -n "${CORE_COUNT-}" ]]; then @@ -188,15 +187,15 @@ config_file() { fi else if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3); then - if [ -z "$CORE_COUNT" ]; then - CORE_COUNT="$var_cpu" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + fi else - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + exit_script fi - else - exit_script - fi fi if [[ -n "${RAM_SIZE-}" ]]; then @@ -208,15 +207,15 @@ config_file() { fi else if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3); then - if [ -z "$RAM_SIZE" ]; then - RAM_SIZE="$var_ram" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + else + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + fi else - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + exit_script fi - else - exit_script - fi fi IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) @@ -227,20 +226,20 @@ config_file() { for iface_filepath in ${IFACE_FILEPATH_LIST}; do iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - ( grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1 ) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' > "${iface_indexes_tmpfile}" || true + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true if [ -f "${iface_indexes_tmpfile}" ]; then - while read -r pair; do - start=$(echo "${pair}" | cut -d':' -f1) - end=$(echo "${pair}" | cut -d':' -f2) - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') - BRIDGES="${iface_name}"$'\n'"${BRIDGES}" - fi + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi - done < "${iface_indexes_tmpfile}" - rm -f "${iface_indexes_tmpfile}" + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" fi done @@ -296,7 +295,7 @@ config_file() { done fi elif [[ "$NET" == *-* ]]; then - IFS="-" read -r ip_start ip_end <<< "$NET" + IFS="-" read -r ip_start ip_end <<<"$NET" if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then msg_error "Invalid IP range format, was $NET should be 0.0.0.0/0-0.0.0.0/0" @@ -309,21 +308,21 @@ config_file() { ip_to_int() { local IFS=. - read -r i1 i2 i3 i4 <<< "$1" - echo $(( (i1 << 24) + (i2 << 16) + (i3 << 8) + i4 )) + read -r i1 i2 i3 i4 <<<"$1" + echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4)) } int_to_ip() { local ip=$1 - echo "$(( (ip >> 24) & 0xFF )).$(( (ip >> 16) & 0xFF )).$(( (ip >> 8) & 0xFF )).$(( ip & 0xFF ))" + echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))" } start_int=$(ip_to_int "$ip1") end_int=$(ip_to_int "$ip2") - for ((ip_int=start_int; ip_int<=end_int; ip_int++)); do + for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do ip=$(int_to_ip $ip_int) - msg_info "Checking IP: $ip" + msg_info "Checking IP: $ip" if ! ping -c 2 -W 1 "$ip" >/dev/null 2>&1; then NET="$ip/$cidr" msg_ok "Using free IP Address: ${BGN}$NET${CL}" @@ -363,37 +362,37 @@ config_file() { fi else while true; do - NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3) - exit_status=$? - if [ $exit_status -eq 0 ]; then - if [ "$NET" = "dhcp" ]; then - echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" - break - else - if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3) + exit_status=$? + if [ $exit_status -eq 0 ]; then + if [ "$NET" = "dhcp" ]; then echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" break else - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58 + if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" + break + else + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58 + fi fi + else + exit_script fi - else - exit_script - fi done if [ "$NET" != "dhcp" ]; then - while true; do - GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) - if [ -z "$GATE1" ]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 - elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 - else - GATE=",gw=$GATE1" - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" - break - fi - done + while true; do + GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done else GATE="" echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}" @@ -406,8 +405,8 @@ config_file() { else if [[ -n "${APT_CACHER_IP-}" ]]; then if [[ ! $APT_CACHER_IP == "none" ]]; then - APT_CACHER="yes" - echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}" + APT_CACHER="yes" + echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}" else APT_CACHER="" echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}No${CL}" @@ -416,7 +415,7 @@ config_file() { if APT_CACHER_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then APT_CACHER="${APT_CACHER_IP:+yes}" echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" - if [[ -n $APT_CACHER_IP ]]; then + if [[ -n $APT_CACHER_IP ]]; then APT_CACHER_IP="none" fi else @@ -506,7 +505,7 @@ config_file() { fi echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" else - exit_script + exit_script fi fi @@ -533,7 +532,7 @@ config_file() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else - exit_script + exit_script fi fi @@ -582,7 +581,7 @@ config_file() { fi echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" else - exit_script + exit_script fi fi @@ -646,7 +645,7 @@ config_file() { exit fi else - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then VERBOSE="yes" else VERBOSE="no" diff --git a/misc/core.func b/misc/core.func index cbabf2f03..fffef59a8 100644 --- a/misc/core.func +++ b/misc/core.func @@ -46,15 +46,26 @@ load_functions() { on_error() { local exit_code="$1" local lineno="$2" - msg_error "Script failed at line $lineno with exit code $exit_code" - # Optionally log to your API or file here + + stop_spinner + + case "$exit_code" in + 1) msg_error "Generic error occurred (line $lineno)" ;; + 2) msg_error "Shell misuse (line $lineno)" ;; + 126) msg_error "Command cannot execute (line $lineno)" ;; + 127) msg_error "Command not found (line $lineno)" ;; + 128) msg_error "Invalid exit argument (line $lineno)" ;; + 130) msg_error "Script aborted by user (CTRL+C)" ;; + 143) msg_error "Script terminated by SIGTERM" ;; + *) msg_error "Script failed at line $lineno with exit code $exit_code" ;; + esac + exit "$exit_code" } on_exit() { - # Always called on script exit, success or failure - cleanup_temp_files || true - msg_info "Script exited" + cleanup_spinner || true + [[ "${VERBOSE:-no}" == "yes" ]] && msg_info "Script exited" } on_interrupt() { @@ -429,8 +440,14 @@ msg_info() { local msg="$1" [[ -z "$msg" || -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return MSG_INFO_SHOWN["$msg"]=1 + stop_spinner - start_spinner "$msg" + + if [[ "${VERBOSE:-no}" == "no" && -t 2 ]]; then + start_spinner "$msg" + else + printf "\r\e[2K%s %b"${TAB}"" "⏳" "${YW}${msg}${CL}" >&2 + fi } msg_ok() { diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 17ee1e516..632e876f2 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -3,32 +3,20 @@ # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # Co-Author: MickLesk -# License: MIT -# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # This sets verbose mode if the global variable is set to "yes" # if [ "$VERBOSE" == "yes" ]; then set -x; fi -# This function sets color variables for formatting output in the terminal -# Colors -YW=$(echo "\033[33m") -YWB=$(echo "\033[93m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -GN=$(echo "\033[1;92m") - -# Formatting -CL=$(echo "\033[m") -UL=$(echo "\033[4m") -BOLD=$(echo "\033[1m") -BFR="\\r\\033[K" -HOLD=" " -TAB=" " - -# Icons -CM="${TAB}βœ”οΈ${TAB}${CL}" -CROSS="${TAB}βœ–οΈ${TAB}${CL}" -INFO="${TAB}πŸ’‘${TAB}${CL}" +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) + load_functions + #echo "(create-lxc.sh) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) + load_functions + #echo "(create-lxc.sh) Loaded core.func via wget" +fi # This sets error handling options and defines the error_handler function to handle errors set -Eeuo pipefail @@ -36,7 +24,6 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR # This function handles errors function error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi printf "\e[?25h" local exit_code="$?" local line_number="$1" @@ -46,53 +33,6 @@ function error_handler() { exit 200 } -# This function displays a spinner. -function spinner() { - local frames=('β ‹' 'β ™' 'β Ή' 'β Έ' 'β Ό' 'β ΄' 'β ¦' 'β §' 'β ‡' '⠏') - local spin_i=0 - local interval=0.1 - printf "\e[?25l" - - local color="${YWB}" - - while true; do - printf "\r ${color}%s${CL}" "${frames[spin_i]}" - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done -} - -# This function displays an informational message with a yellow color. -function msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" - spinner & - SPINNER_PID=$! -} - -function msg_warn() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${INFO}${YWB}${msg}${CL}" -} - -# This function displays a success message with a green color. -function msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -function msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - # This checks for the presence of valid Container Storage and Template Storage locations msg_info "Validating Storage" VALIDCT=$(pvesm status -content rootdir | awk 'NR>1') @@ -126,40 +66,44 @@ function select_storage() { } ;; esac - # This Queries all storage locations + # Collect storage options local -a MENU - while read -r line; do - local TAG=$(echo $line | awk '{print $1}') - local TYPE=$(echo $line | awk '{printf "%-10s", $2}') - local FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') - local ITEM="Type: $TYPE Free: $FREE " - local OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then - local MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) - fi - MENU+=("$TAG" "$ITEM" "OFF") - done < <(pvesm status -content $CONTENT | awk 'NR>1') + local MSG_MAX_LENGTH=0 - # Select storage location - if [ $((${#MENU[@]} / 3)) -eq 1 ]; then - printf ${MENU[0]} - else - local STORAGE - while [ -z "${STORAGE:+x}" ]; do - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool would you like to use for the ${CONTENT_LABEL,,}?\nTo make a selection, use the Spacebar.\n" \ - 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${MENU[@]}" 3>&1 1>&2 2>&3) || { - msg_error "Menu aborted." - exit 202 - } - if [ $? -ne 0 ]; then - echo -e "${CROSS}${RD} Menu aborted by user.${CL}" - exit 0 - fi - done - printf "%s" "$STORAGE" + while read -r TAG TYPE _ _ _ FREE _; do + local TYPE_PADDED + local FREE_FMT + + TYPE_PADDED=$(printf "%-10s" "$TYPE") + FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.2f <<<"$FREE")B + local ITEM="Type: $TYPE_PADDED Free: $FREE_FMT" + + ((${#ITEM} + 2 > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + 2)) + + MENU+=("$TAG" "$ITEM" "OFF") + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + local OPTION_COUNT=$((${#MENU[@]} / 3)) + + # Auto-select if only one option available + if [[ "$OPTION_COUNT" -eq 1 ]]; then + echo "${MENU[0]}" + return 0 fi + + # Display selection menu + local STORAGE + while [[ -z "${STORAGE:+x}" ]]; do + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Select the storage pool to use for the ${CONTENT_LABEL,,}.\nUse the spacebar to make a selection.\n" \ + 16 $((MSG_MAX_LENGTH + 23)) 6 \ + "${MENU[@]}" 3>&1 1>&2 2>&3) || { + msg_error "Storage selection cancelled." + exit 202 + } + done + + echo "$STORAGE" } # Test if required variables are set [[ "${CTID:-}" ]] || { @@ -177,20 +121,6 @@ function select_storage() { exit 205 } -# Check for network connectivity (IPv4 & IPv6) -#function check_network() { -# local CHECK_URLS=("8.8.8.8" "1.1.1.1" "9.9.9.9" "2606:4700:4700::1111" "2001:4860:4860::8888" "2620:fe::fe") -# -# for url in "${CHECK_URLS[@]}"; do -# if ping -c 1 -W 2 "$url" &>/dev/null; then -# return 0 # Success: At least one connection works -# fi -# done -# -# msg_error "No network connection detected. Check your internet connection." -# exit 101 -#} - # Test if ID is in use if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then echo -e "ID '$CTID' is already in use." @@ -207,6 +137,13 @@ msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage." CONTAINER_STORAGE=$(select_storage container) msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage." +# Check free space on selected container storage +STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') +REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) +if [ "$STORAGE_FREE" -lt "$REQUIRED_KB" ]; then + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 +fi # Check Cluster Quorum if in Cluster if [ -f /etc/pve/corosync.conf ]; then msg_info "Checking Proxmox cluster quorum status" @@ -220,44 +157,48 @@ fi # Update LXC template list msg_info "Updating LXC Template List" -#check_network -pveam update >/dev/null -msg_ok "Updated LXC Template List" + +if ! timeout 10 pveam update >/dev/null 2>&1; then + msg_error "Failed to update LXC template list. Please check your Proxmox host's internet connection and DNS resolution." + exit 201 +fi +$STD msg_ok "LXC Template List Updated" # Get LXC template string -TEMPLATE_SEARCH=${PCT_OSTYPE}-${PCT_OSVERSION:-} +TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" mapfile -t TEMPLATES < <(pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V) -[ ${#TEMPLATES[@]} -gt 0 ] || { - msg_error "Unable to find a template when searching for '$TEMPLATE_SEARCH'." - exit 207 -} -TEMPLATE="${TEMPLATES[-1]}" -TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE)" -# Without NAS/Mount: TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" -# Check if template exists, if corrupt remove and redownload -if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE" || ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then - msg_warn "Template $TEMPLATE not found in storage or seems to be corrupted. Redownloading." - [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" - # Download with 3 attempts +if [ ${#TEMPLATES[@]} -eq 0 ]; then + msg_error "No matching LXC template found for '${TEMPLATE_SEARCH}'. Make sure your host can reach the Proxmox template repository." + exit 207 +fi + +TEMPLATE="${TEMPLATES[-1]}" +TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || echo "/var/lib/vz/template/cache/$TEMPLATE")" + +# Check if template exists and is valid +if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE" || ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then + msg_warn "Template $TEMPLATE not found or appears to be corrupted. Re-downloading." + + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" for attempt in {1..3}; do msg_info "Attempt $attempt: Downloading LXC template..." - if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then + if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then msg_ok "Template download successful." break fi if [ $attempt -eq 3 ]; then - msg_error "Three failed attempts. Aborting." + msg_error "Failed after 3 attempts. Please check your Proxmox host’s internet access or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" exit 208 fi sleep $((attempt * 5)) done fi -msg_ok "LXC Template is ready to use." +msg_ok "LXC Template '$TEMPLATE' is ready to use." # Check and fix subuid/subgid grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid @@ -266,28 +207,55 @@ grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgi PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) [[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") +# Secure creation of the LXC container with lock and template check +lockfile="/tmp/template.${TEMPLATE}.lock" +exec 9>"$lockfile" +flock -w 60 9 || { + msg_error "Timeout while waiting for template lock" + exit 211 +} msg_info "Creating LXC Container" if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then - msg_error "Container creation failed. Checking if template is corrupted." + msg_error "Container creation failed. Checking if template is corrupted or incomplete." - if ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then - msg_error "Template appears to be corrupted. Removing and re-downloading." + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_error "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + elif ! zstdcat "$TEMPLATE_PATH" | tar -tf - &>/dev/null; then + msg_error "Template appears to be corrupted – re-downloading." rm -f "$TEMPLATE_PATH" - - if ! timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then - msg_error "Failed to re-download template." - exit 208 - fi - - msg_ok "Re-downloaded LXC Template" - - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then - msg_error "Container creation failed after re-downloading template." - exit 200 - fi else - msg_error "Container creation failed, but template is not corrupted." + msg_error "Template is valid, but container creation still failed." exit 209 fi + + # Retry download + for attempt in {1..3}; do + msg_info "Attempt $attempt: Re-downloading template..." + if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then + msg_ok "Template re-download successful." + break + fi + if [ "$attempt" -eq 3 ]; then + msg_error "Three failed attempts. Aborting." + exit 208 + fi + sleep $((attempt * 5)) + done + + sleep 1 # I/O-Sync-Delay + + msg_ok "Re-downloaded LXC Template" + + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then + msg_error "Container creation failed after re-downloading template." + exit 200 + fi fi + +if ! pct status "$CTID" &>/dev/null; then + msg_error "Container not found after pct create – assuming failure." + exit 210 +fi + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." diff --git a/misc/install.func b/misc/install.func index 3aedd94e6..239a4e0c8 100644 --- a/misc/install.func +++ b/misc/install.func @@ -4,53 +4,13 @@ # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# This function sets color variables for formatting output in the terminal -color() { - # Colors - YW=$(echo "\033[33m") - YWB=$(echo "\033[93m") - BL=$(echo "\033[36m") - RD=$(echo "\033[01;31m") - GN=$(echo "\033[1;92m") - - # Formatting - CL=$(echo "\033[m") - BFR="\\r\\033[K" - BOLD=$(echo "\033[1m") - HOLD=" " - TAB=" " - TAB3=" " - - # System - RETRY_NUM=10 - RETRY_EVERY=3 - - # Icons - CM="${TAB}βœ”οΈ${TAB}${CL}" - CROSS="${TAB}βœ–οΈ${TAB}${CL}" - INFO="${TAB}πŸ’‘${TAB}${CL}" - NETWORK="${TAB}πŸ“‘${TAB}${CL}" - OS="${TAB}πŸ–₯️${TAB}${CL}" - OSVERSION="${TAB}🌟${TAB}${CL}" - HOSTNAME="${TAB}🏠${TAB}${CL}" - GATEWAY="${TAB}🌐${TAB}${CL}" - DEFAULT="${TAB}βš™οΈ${TAB}${CL}" -} - -# Function to set STD mode based on verbosity -set_std_mode() { - if [ "$VERBOSE" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - "$@" >/dev/null 2>&1 -} - +if ! command -v curl >/dev/null 2>&1; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + apt-get update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func) +load_functions # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { set_std_mode # Set STD mode based on VERBOSE @@ -70,7 +30,6 @@ catch_errors() { # This function handles errors error_handler() { source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi printf "\e[?25h" local exit_code="$?" local line_number="$1" @@ -85,62 +44,15 @@ error_handler() { fi } -# This function displays a spinner. -spinner() { - local frames=('β ‹' 'β ™' 'β Ή' 'β Έ' 'β Ό' 'β ΄' 'β ¦' 'β §' 'β ‡' '⠏') - local spin_i=0 - local interval=0.1 - printf "\e[?25l" - - local color="${YWB}" - - while true; do - printf "\r ${color}%s${CL}" "${frames[spin_i]}" - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done -} - -# This function displays an informational message with a yellow color. -msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" - spinner & - SPINNER_PID=$! -} - -# This function displays a success message with a green color. -msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection setting_up_container() { msg_info "Setting up Container OS" - sed -i "/$LANG/ s/\(^# \)//" /etc/locale.gen - locale_line=$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print $1}' | head -n 1) - echo "LANG=${locale_line}" >/etc/default/locale - locale-gen >/dev/null - export LANG=${locale_line} - echo "$tz" >/etc/timezone - ln -sf /usr/share/zoneinfo/"$tz" /etc/localtime for ((i = RETRY_NUM; i > 0; i--)); do if [ "$(hostname -I)" != "" ]; then break fi echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep "$RETRY_EVERY" + sleep $RETRY_EVERY done if [ "$(hostname -I)" = "" ]; then echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" @@ -150,7 +62,7 @@ setting_up_container() { rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED systemctl disable -q --now systemd-networkd-wait-online.service msg_ok "Set up Container OS" - msg_ok "Network Connected: ${BL}$(hostname -I)" + msg_custom "${CM}" "${GN}" "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 @@ -178,7 +90,7 @@ network_check() { # If both IPv4 and IPv6 checks fail, prompt the user if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then - read -r -p "No Internet detected,would you like to continue anyway? " prompt + read -r -p "No Internet detected, would you like to continue anyway? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" else @@ -187,8 +99,26 @@ network_check() { fi fi - RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }') - if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to ${BL}$RESOLVEDIP${CL}"; fi + # DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6) + GITHUB_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com") + GITHUB_STATUS="GitHub DNS:" + DNS_FAILED=false + + for HOST in "${GITHUB_HOSTS[@]}"; do + RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1) + if [[ -z "$RESOLVEDIP" ]]; then + GITHUB_STATUS+="$HOST:($DNSFAIL)" + DNS_FAILED=true + else + GITHUB_STATUS+=" $HOST:($DNSOK)" + fi + done + + if [[ "$DNS_FAILED" == true ]]; then + fatal "$GITHUB_STATUS" + else + msg_ok "$GITHUB_STATUS" + fi set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } @@ -213,11 +143,7 @@ EOF rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" - msg_info "Installing core dependencies" - $STD apt-get update - $STD apt-get install -y sudo curl mc gnupg2 source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) - msg_ok "Core dependencies installed" } # This function modifies the message of the day (motd) and SSH settings