diff --git a/scripts/backup_restore/Proxmox_Backup_Client.AppImage b/scripts/backup_restore/Proxmox_Backup_Client.AppImage deleted file mode 100644 index 7b0c4d32..00000000 Binary files a/scripts/backup_restore/Proxmox_Backup_Client.AppImage and /dev/null differ diff --git a/scripts/backup_restore/backup_host2.sh b/scripts/backup_restore/backup_host2.sh deleted file mode 100644 index abe86ece..00000000 --- a/scripts/backup_restore/backup_host2.sh +++ /dev/null @@ -1,1061 +0,0 @@ -#!/usr/bin/env bash - -# Configuration ============================================ -LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts" -BASE_DIR="/usr/local/share/proxmenux" -UTILS_FILE="$BASE_DIR/utils.sh" -VENV_PATH="/opt/googletrans-env" - -if [[ -f "$UTILS_FILE" ]]; then - source "$UTILS_FILE" -fi -load_language -initialize_cache -# ========================================================== - - -get_external_backup_mount_point() { - local BACKUP_MOUNT_FILE="/usr/local/share/proxmenux/last_backup_mount.txt" - local STORAGE_REPO="$LOCAL_SCRIPTS/backup_restore" - local MOUNT_POINT - - if [[ -f "$BACKUP_MOUNT_FILE" ]]; then - MOUNT_POINT=$(head -n1 "$BACKUP_MOUNT_FILE" | tr -d '\r\n' | xargs) - >&2 echo "DEBUG: Valor MOUNT_POINT='$MOUNT_POINT'" - if [[ ! -d "$MOUNT_POINT" ]]; then - msg_error "Mount point does not exist: $MOUNT_POINT" - rm -f "$BACKUP_MOUNT_FILE" - return 1 - fi - if ! mountpoint -q "$MOUNT_POINT"; then - msg_error "Mount point is not mounted: $MOUNT_POINT" - rm -f "$BACKUP_MOUNT_FILE" - return 1 - fi - - echo "$MOUNT_POINT" - return 0 - else - source "$STORAGE_REPO/mount_disk_host_bk.sh" - MOUNT_POINT=$(mount_disk_host_bk) - [[ -z "$MOUNT_POINT" ]] && msg_error "$(translate "No disk mounted.")" && return 1 - echo "$MOUNT_POINT" - return 0 - fi -} - - - -# === Host Backup Main Menu === -host_backup_menu() { - while true; do - local CHOICE - CHOICE=$(dialog --backtitle "ProxMenux" \ - --title "$(translate 'Host Backup')" \ - --menu "\n$(translate 'Select backup option:')" 22 70 12 \ - "" "$(translate '--- FULL BACKUP ---')" \ - 1 "$(translate 'Full backup to Proxmox Backup Server (PBS)')" \ - 2 "$(translate 'Full backup with BorgBackup')" \ - 3 "$(translate 'Full backup to local .tar.gz')" \ - "" "$(translate '--- CUSTOM BACKUP ---')" \ - 4 "$(translate 'Custom backup to PBS')" \ - 5 "$(translate 'Custom backup with BorgBackup')" \ - 6 "$(translate 'Custom backup to local .tar.gz')" \ - 0 "$(translate 'Return')" \ - 3>&1 1>&2 2>&3) || return 0 - - case "$CHOICE" in - 1) backup_full_pbs_root ;; - 2) backup_with_borg "/boot/efi /etc/pve /etc/network /var/lib/pve-cluster /root /etc/ssh /home /usr/local/bin /etc/cron.d /etc/systemd/system /var/lib/vz" ;; - 3) backup_to_local_tar "/boot/efi /etc/pve /etc/network /var/lib/pve-cluster /root /etc/ssh /home /usr/local/bin /etc/cron.d /etc/systemd/system /var/lib/vz" ;; - 4) custom_backup_menu backup_to_pbs ;; - 5) custom_backup_menu backup_with_borg ;; - 6) custom_backup_menu backup_to_local_tar ;; - 0) break ;; - esac - done -} - - - -# === Menu checklist for custom backup === -custom_backup_menu() { - declare -A BACKUP_PATHS=( - [etc-pve]="/etc/pve" - [etc-network]="/etc/network" - [var-lib-pve-cluster]="/var/lib/pve-cluster" - [root-dir]="/root" - [etc-ssh]="/etc/ssh" - [home]="/home" - [local-bin]="/usr/local/bin" - [cron]="/etc/cron.d" - [custom-systemd]="/etc/systemd/system" - [var-lib-vz]="/var/lib/vz" - ) - local CHECKLIST_OPTIONS=() - for KEY in "${!BACKUP_PATHS[@]}"; do - DIR="${BACKUP_PATHS[$KEY]}" - CHECKLIST_OPTIONS+=("$KEY" "$DIR" "off") - done - - SELECTED_KEYS=$(dialog --separate-output --checklist \ - "$(translate 'Select directories to backup:')" 22 70 12 \ - "${CHECKLIST_OPTIONS[@]}" \ - 3>&1 1>&2 2>&3) || return 1 - - local BACKUP_DIRS=() - for KEY in $SELECTED_KEYS; do - BACKUP_DIRS+=("${BACKUP_PATHS[$KEY]}") - done - - -# "$1" "${BACKUP_DIRS[*]}" - "$1" "${BACKUP_DIRS[@]}" - - -} - - - - - - - - - - - -configure_pbs_repository() { -local PBS_REPO_FILE="/usr/local/share/proxmenux/pbs-repo.conf" -local PBS_PASS_FILE="/usr/local/share/proxmenux/pbs-pass.txt" -local PBS_TOKEN_FILE="/usr/local/share/proxmenux/pbs-token.txt" -local PBS_MANUAL_CONFIGS="/usr/local/share/proxmenux/pbs-manual-configs.txt" - -[[ ! -f "$PBS_MANUAL_CONFIGS" ]] && touch "$PBS_MANUAL_CONFIGS" - -local PBS_CONFIGS=() -local PBS_SOURCES=() -local PBS_USERNAMES=() - -if [[ -f "/etc/pve/storage.cfg" ]]; then - local current_pbs="" server="" datastore="" username="" - - while IFS= read -r line; do - if [[ $line =~ ^pbs:\ (.+)$ ]]; then - if [[ -n "$current_pbs" && -n "$server" && -n "$datastore" && -n "$username" ]]; then - PBS_CONFIGS+=("$current_pbs|$username@$server:$datastore") - PBS_SOURCES+=("proxmox|$current_pbs") - PBS_USERNAMES+=("$username") - fi - current_pbs="${BASH_REMATCH[1]}" - server="" datastore="" username="" - elif [[ -n "$current_pbs" ]]; then - if [[ $line =~ ^[[:space:]]*server[[:space:]]+(.+)$ ]]; then - server="${BASH_REMATCH[1]}" - elif [[ $line =~ ^[[:space:]]*datastore[[:space:]]+(.+)$ ]]; then - datastore="${BASH_REMATCH[1]}" - elif [[ $line =~ ^[[:space:]]*username[[:space:]]+(.+)$ ]]; then - username="${BASH_REMATCH[1]}" - elif [[ $line =~ ^[a-zA-Z]+: ]]; then - if [[ -n "$server" && -n "$datastore" && -n "$username" ]]; then - PBS_CONFIGS+=("$current_pbs|$username@$server:$datastore") - PBS_SOURCES+=("proxmox|$current_pbs") - PBS_USERNAMES+=("$username") - fi - current_pbs="" - fi - fi - done < "/etc/pve/storage.cfg" - - if [[ -n "$current_pbs" && -n "$server" && -n "$datastore" && -n "$username" ]]; then - PBS_CONFIGS+=("$current_pbs|$username@$server:$datastore") - PBS_SOURCES+=("proxmox|$current_pbs") - PBS_USERNAMES+=("$username") - fi -fi - -if [[ -f "$PBS_MANUAL_CONFIGS" ]]; then - while IFS= read -r line; do - if [[ -n "$line" ]]; then - PBS_CONFIGS+=("$line") - local name="${line%%|*}" - PBS_SOURCES+=("manual|$name") - PBS_USERNAMES+=("") - fi - done < "$PBS_MANUAL_CONFIGS" -fi - -local menu_options=() -local i=1 - -for j in "${!PBS_CONFIGS[@]}"; do - local config="${PBS_CONFIGS[$j]}" - local source="${PBS_SOURCES[$j]}" - local name="${config%%|*}" - local repo="${config##*|}" - local source_type="${source%%|*}" - - if [[ "$source_type" == "proxmox" ]]; then - menu_options+=("$i" " $name ($repo) [Proxmox]") - else - menu_options+=("$i" " $name ($repo) [Manual]") - fi - ((i++)) -done - -menu_options+=("" "") -menu_options+=("$i" "\Z4\Zb $(translate 'Configure new PBS')\Zn") -local choice -choice=$(dialog --colors --backtitle "ProxMenux" --title "PBS Server Selection" \ ---menu "$(translate 'Select PBS server for this backup:')" 22 70 12 "${menu_options[@]}" 3>&1 1>&2 2>&3) || return 1 - -if [[ $choice -eq $i ]]; then - configure_pbs_manually || return 1 -else - local selected_config="${PBS_CONFIGS[$((choice-1))]}" - local selected_source="${PBS_SOURCES[$((choice-1))]}" - local selected_username="${PBS_USERNAMES[$((choice-1))]}" - local pbs_name="${selected_config%%|*}" - local source_type="${selected_source%%|*}" - PBS_REPO="${selected_config##*|}" - - { - mkdir -p "$(dirname "$PBS_REPO_FILE")" - echo "$PBS_REPO" > "$PBS_REPO_FILE" - } >/dev/null 2>&1 - - local password_found=false - if [[ "$source_type" == "proxmox" ]]; then - local password_file="/etc/pve/priv/storage/${pbs_name}.pw" - - if [[ -f "$password_file" ]]; then - local auth_content - auth_content=$(<"$password_file") - - - [[ -f "$PBS_PASS_FILE" ]] && rm "$PBS_PASS_FILE" - [[ -f "$PBS_TOKEN_FILE" ]] && rm "$PBS_TOKEN_FILE" - - - if [[ "$selected_username" == *"@pbs!"* ]]; then - - echo "$auth_content" > "$PBS_PASS_FILE" - chmod 600 "$PBS_PASS_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') PBS Cloud Token -$(translate 'Token ID:') $selected_username" 15 80 - elif [[ "$auth_content" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]]; then - local full_token="${selected_username}:${auth_content}" - echo "$full_token" > "$PBS_TOKEN_FILE" - chmod 600 "$PBS_TOKEN_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') API Token -$(translate 'Token ID:') $selected_username" 15 80 - elif [[ "$auth_content" == *":"* ]]; then - echo "$auth_content" > "$PBS_TOKEN_FILE" - chmod 600 "$PBS_TOKEN_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') API Token (Complete)" 12 80 - else - echo "$auth_content" > "$PBS_PASS_FILE" - chmod 600 "$PBS_PASS_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') Password" 12 80 - fi - fi - else - local manual_pass_file="/usr/local/share/proxmenux/pbs-pass-${pbs_name}.txt" - if [[ -f "$manual_pass_file" ]]; then - [[ -f "$PBS_TOKEN_FILE" ]] && rm "$PBS_TOKEN_FILE" - - { - cp "$manual_pass_file" "$PBS_PASS_FILE" - chmod 600 "$PBS_PASS_FILE" - } >/dev/null 2>&1 - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using manual PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Password:') $(translate 'Previously saved')" 12 80 - fi - fi - - if ! $password_found; then - dialog --backtitle "ProxMenux" --title "Password Required" --msgbox "$(translate 'Password not found for:') $pbs_name -$(translate 'Please enter the password.')" 10 60 - get_pbs_password "$pbs_name" || return 1 - fi - - clear -fi -} - - - - - - - - - - -# ========== PBS BACKUP ========== -backup_full_pbs_root() { - local HOSTNAME PBS_REPO PBS_KEY_FILE PBS_PASS_FILE PBS_TOKEN_FILE PBS_ENCRYPTION_PASS_FILE ENCRYPT_OPT="" - HOSTNAME=$(hostname) - - - local PBS_REPO_FILE="/usr/local/share/proxmenux/pbs-repo.conf" - PBS_KEY_FILE="/usr/local/share/proxmenux/pbs-key.conf" - PBS_PASS_FILE="/usr/local/share/proxmenux/pbs-pass.txt" - PBS_TOKEN_FILE="/usr/local/share/proxmenux/pbs-token.txt" - PBS_ENCRYPTION_PASS_FILE="/usr/local/share/proxmenux/pbs-encryption-pass.txt" - LOGFILE="/tmp/pbs-backup-${HOSTNAME}.log" - - - configure_pbs_repository || return 1 - - if [[ ! -f "$PBS_REPO_FILE" ]]; then - msg_error "$(translate "Failed to configure PBS connection")" - sleep 3 - return 1 - fi - PBS_REPO=$(<"$PBS_REPO_FILE") - - - unset PBS_PASSWORD PBS_API_TOKEN PBS_API_TOKEN_ID PBS_API_TOKEN_SECRET PBS_FINGERPRINT - - - local AUTH_TYPE="password" - local PBS_AUTH_VALUE="" - - if [[ "$PBS_REPO" == *"@pbs!"* ]]; then - - AUTH_TYPE="pbs_cloud" - [[ -f "$PBS_PASS_FILE" ]] && PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - elif [[ -f "$PBS_TOKEN_FILE" ]]; then - - AUTH_TYPE="token" - PBS_AUTH_VALUE=$(<"$PBS_TOKEN_FILE") - elif [[ -f "$PBS_PASS_FILE" ]]; then - - AUTH_TYPE="password" - PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - else - msg_error "$(translate "No PBS authentication found!")" - sleep 3 - return 1 - fi - - - dialog --backtitle "ProxMenux" --title "Encryption" --yesno "$(translate 'Do you want to encrypt the backup?')" 8 60 - if [[ $? -eq 0 ]]; then - - if [[ ! -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - while true; do - PBS_KEY_PASS=$(dialog --backtitle "ProxMenux" --title "Encryption Password" --insecure --passwordbox "$(translate 'Enter encryption password (different from PBS login):')" 12 70 "" 3>&1 1>&2 2>&3) || return 1 - PBS_KEY_PASS2=$(dialog --backtitle "ProxMenux" --title "Encryption Password" --insecure --passwordbox "$(translate 'Confirm encryption password:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - if [[ "$PBS_KEY_PASS" == "$PBS_KEY_PASS2" ]]; then - break - else - dialog --backtitle "ProxMenux" --title "Error" --msgbox "$(translate 'Passwords do not match! Please try again.')" 8 50 - fi - done - - - { - echo "$PBS_KEY_PASS" > "$PBS_ENCRYPTION_PASS_FILE" - chmod 600 "$PBS_ENCRYPTION_PASS_FILE" - } >/dev/null 2>&1 - - dialog --backtitle "ProxMenux" --title "Success" --msgbox "$(translate 'Encryption password saved successfully!')" 8 50 - fi - - - if [[ ! -f "$PBS_KEY_FILE" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - - dialog --backtitle "ProxMenux" --title "Encryption" --infobox "$(translate 'Creating encryption key...')" 5 50 - - expect -c " - set timeout 30 - spawn proxmox-backup-client key create \"$PBS_KEY_FILE\" - expect { - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - \"Verify Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - eof - } - " >/dev/null 2>&1 - - if [[ ! -f "$PBS_KEY_FILE" ]]; then - dialog --backtitle "ProxMenux" --title "Error" --msgbox "$(translate 'Error creating encryption key.')" 8 40 - return 1 - fi - - dialog --backtitle "ProxMenux" --title "Important" --msgbox "$(translate 'IMPORTANT: Save the key file. Without it you will not be able to restore your backups!')\n\n$(translate 'Key file location:') $PBS_KEY_FILE" 12 70 - fi - ENCRYPT_OPT="--keyfile $PBS_KEY_FILE" - else - ENCRYPT_OPT="" - fi - - - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "Starting backup to PBS")" - echo -e - echo -e "${BL}$(translate "PBS Repository:")${WHITE} $PBS_REPO${RESET}" - echo -e "${BL}$(translate "Backup ID:")${WHITE} $HOSTNAME${RESET}" - echo -e "${BL}$(translate "Included:")${WHITE} /boot/efi /etc/pve (all root)${RESET}" - echo -e "${BL}$(translate "Encryption:")${WHITE} $([[ -n "$ENCRYPT_OPT" ]] && echo "Enabled" || echo "Disabled")${RESET}" - - - case "$AUTH_TYPE" in - "pbs_cloud") echo -e "${BL}$(translate "Authentication:")${WHITE} PBS Cloud Token${RESET}" ;; - "token") echo -e "${BL}$(translate "Authentication:")${WHITE} API Token${RESET}" ;; - "password") echo -e "${BL}$(translate "Authentication:")${WHITE} Password${RESET}" ;; - esac - - echo -e "${BL}$(translate "Log file:")${WHITE} $LOGFILE${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}-------------------------------${RESET}" - echo "" - - - local backup_cmd="proxmox-backup-client backup --include-dev /boot/efi --include-dev /etc/pve root-${HOSTNAME}.pxar:/ --repository \"$PBS_REPO\" $ENCRYPT_OPT --backup-type host --backup-id \"$HOSTNAME\" --backup-time \"$(date +%s)\"" - local backup_result=0 - - case "$AUTH_TYPE" in - "pbs_cloud") - - if [[ -n "$ENCRYPT_OPT" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - echo "$(translate "Starting encrypted full backup with PBS Cloud...")" - echo "" - - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - else - - echo "$(translate "Starting unencrypted full backup with PBS Cloud...")" - echo "" - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" bash -c "$backup_cmd" | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - fi - ;; - - "token") - - if [[ -n "$ENCRYPT_OPT" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - echo "$(translate "Starting encrypted full backup with API Token...")" - echo "" - - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - else - echo "$(translate "Starting unencrypted full backup with API Token...")" - echo "" - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" bash -c "$backup_cmd" | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - fi - ;; - - "password") - - if [[ -n "$ENCRYPT_OPT" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - echo "$(translate "Starting encrypted full backup with password...")" - echo "" - - env -i expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - else - - echo "$(translate "Starting unencrypted full backup with password...")" - echo "" - - env -i expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - fi - ;; - esac - - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" - if [[ $backup_result -eq 0 ]]; then - msg_ok "$(translate "Full backup process completed successfully")" - else - msg_error "$(translate "Backup process finished with errors")" - fi - - echo "" - msg_success "$(translate "Press Enter to return to the main menu...")" - read -r -} - - - - - - - - - - - - - - -backup_to_pbs() { - local HOSTNAME TIMESTAMP SNAPSHOT - HOSTNAME=$(hostname) - TIMESTAMP=$(date +%Y-%m-%d_%H-%M) - SNAPSHOT="${HOSTNAME}-${TIMESTAMP}" - - local PBS_REPO_FILE="/usr/local/share/proxmenux/pbs-repo.conf" - local PBS_KEY_FILE="/usr/local/share/proxmenux/pbs-key.conf" - local PBS_PASS_FILE="/usr/local/share/proxmenux/pbs-pass.txt" - local PBS_TOKEN_FILE="/usr/local/share/proxmenux/pbs-token.txt" - local PBS_ENCRYPTION_PASS_FILE="/usr/local/share/proxmenux/pbs-encryption-pass.txt" - local PBS_REPO ENCRYPT_OPT USE_ENCRYPTION - local PBS_KEY_PASS PBS_REPO_PASS - - configure_pbs_repository || return 1 - PBS_REPO=$(<"$PBS_REPO_FILE") - - - unset PBS_PASSWORD PBS_API_TOKEN PBS_API_TOKEN_ID PBS_API_TOKEN_SECRET PBS_FINGERPRINT - - USE_ENCRYPTION=false - dialog --backtitle "ProxMenux" --yesno "$(translate 'Do you want to encrypt the backup?')" 8 60 - [[ $? -eq 0 ]] && USE_ENCRYPTION=true - - if $USE_ENCRYPTION && ! command -v expect >/dev/null 2>&1; then - apt-get update -qq >/dev/null 2>&1 - apt-get install -y expect >/dev/null 2>&1 - fi - - if [[ "$#" -lt 1 ]]; then - clear - show_proxmenux_logo - msg_error "$(translate "No directories specified for backup.")" - sleep 2 - return 1 - fi - - local TOTAL="$#" - local COUNT=1 - - - local AUTH_TYPE="password" - local PBS_AUTH_VALUE="" - - if [[ "$PBS_REPO" == *"@pbs!"* ]]; then - - AUTH_TYPE="pbs_cloud" - [[ -f "$PBS_PASS_FILE" ]] && PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - elif [[ -f "$PBS_TOKEN_FILE" ]]; then - - AUTH_TYPE="token" - PBS_AUTH_VALUE=$(<"$PBS_TOKEN_FILE") - elif [[ -f "$PBS_PASS_FILE" ]]; then - - AUTH_TYPE="password" - PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - else - msg_error "$(translate "No PBS authentication found!")" - return 1 - fi - - for dir in "$@"; do - local SAFE_NAME SAFE_ID PXAR_NAME - SAFE_NAME=$(basename "$dir" | tr '.-/' '_') - PXAR_NAME="root-custom-${SAFE_NAME}-${SNAPSHOT}.pxar" - SAFE_ID="custom-${HOSTNAME}-${SAFE_NAME}" - - msg_info2 "$(translate "[$COUNT/$TOTAL] Backing up") $dir $(translate "as") $PXAR_NAME" - - ENCRYPT_OPT="" - - if $USE_ENCRYPTION; then - if [[ -f "$PBS_KEY_FILE" ]]; then - ENCRYPT_OPT="--keyfile $PBS_KEY_FILE" - else - while true; do - PBS_KEY_PASS=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Enter encryption password (different from PBS login):')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - PBS_KEY_PASS2=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Confirm encryption password:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - if [[ "$PBS_KEY_PASS" == "$PBS_KEY_PASS2" ]]; then - break - else - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Passwords do not match! Please try again.')" 8 50 - fi - done - - { - echo "$PBS_KEY_PASS" > "$PBS_ENCRYPTION_PASS_FILE" - chmod 600 "$PBS_ENCRYPTION_PASS_FILE" - } >/dev/null 2>&1 - - expect -c " - set timeout 30 - spawn proxmox-backup-client key create \"$PBS_KEY_FILE\" - expect { - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - \"Verify Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - eof - } - " >/dev/null 2>&1 - - if [[ ! -f "$PBS_KEY_FILE" ]]; then - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Error creating encryption key.')" 8 40 - return 1 - fi - ENCRYPT_OPT="--keyfile $PBS_KEY_FILE" - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Encryption key generated. Save it in a safe place!')" 10 60 - fi - fi - - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "Starting backup to PBS")" - TOTAL_SIZE=$(du -cb "$@" | awk '/total$/ {print $1}') - TOTAL_SIZE_GB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE/1024/1024/1024}") - echo -e - echo -e "${BL}$(translate "PBS Repository:")${WHITE} $PBS_REPO${RESET}" - echo -e "${BL}$(translate "Backup ID:")${WHITE} $HOSTNAME${RESET}" - echo -e "${BL}$(translate "Encryption:")${WHITE} $([[ -n "$ENCRYPT_OPT" ]] && echo "Enabled" || echo "Disabled")${RESET}" - - case "$AUTH_TYPE" in - "pbs_cloud") echo -e "${BL}$(translate "Authentication:")${WHITE} PBS Cloud Token${RESET}" ;; - "token") echo -e "${BL}$(translate "Authentication:")${WHITE} API Token${RESET}" ;; - "password") echo -e "${BL}$(translate "Authentication:")${WHITE} Password${RESET}" ;; - esac - - echo -e "${BL}$(translate "Included directories:")${WHITE} $*${RESET}" - echo -e "${BL}$(translate "Total size:")${WHITE} ${TOTAL_SIZE_GB} GB${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}-------------------------------${RESET}" - - - local backup_cmd="proxmox-backup-client backup \"${PXAR_NAME}:$dir\" --repository \"$PBS_REPO\" $ENCRYPT_OPT --backup-type host --backup-id \"$SAFE_ID\" --backup-time \"$(date +%s)\"" - - case "$AUTH_TYPE" in - "pbs_cloud") - - if $USE_ENCRYPTION && [[ -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - PBS_KEY_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" expect -c " - set timeout 300 - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - eof - } - " - else - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" bash -c "$backup_cmd" - fi - ;; - - "token") - - if $USE_ENCRYPTION && [[ -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - PBS_KEY_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" expect -c " - set timeout 300 - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - eof - } - " - else - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" bash -c "$backup_cmd" - fi - ;; - - "password") - - if $USE_ENCRYPTION && [[ -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - PBS_KEY_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - env -i expect -c " - set timeout 300 - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " - else - env -i expect -c " - set timeout 300 - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " - fi - ;; - esac - - COUNT=$((COUNT+1)) - done - - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" - msg_ok "$(translate "Backup process finished.")" - echo "" - msg_success "$(translate "Press Enter to return to the main menu...")" - read -r -} -# =============================== - - - - - - - -# ========== BORGBACKUP ========== -backup_with_borg() { -# local SRC="$1" - local BORG_APPIMAGE="/usr/local/share/proxmenux/borg" - local LOGFILE="/tmp/borg-backup.log" - local DEST - local TYPE - local ENCRYPT_OPT="" - local BORG_KEY - - if [[ ! -x "$BORG_APPIMAGE" ]]; then - clear - show_proxmenux_logo - msg_info "$(translate "BorgBackup not found. Downloading AppImage...")" - mkdir -p /usr/local/share/proxmenux - wget -qO "$BORG_APPIMAGE" "https://github.com/borgbackup/borg/releases/download/1.2.8/borg-linux64" - chmod +x "$BORG_APPIMAGE" - msg_ok "$(translate "BorgBackup downloaded and ready.")" - fi - - - TYPE=$(dialog --backtitle "ProxMenux" --menu "$(translate 'Select Borg backup destination:')" 15 60 3 \ - "local" "$(translate 'Local directory')" \ - "usb" "$(translate 'Internal/External dedicated disk')" \ - "remote" "$(translate 'Remote server')" \ - 3>&1 1>&2 2>&3) || return 1 - - if [[ "$TYPE" == "local" ]]; then - DEST=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter local directory for backup:')" 10 60 "/backup/borgbackup" 3>&1 1>&2 2>&3) || return 1 - mkdir -p "$DEST" - elif [[ "$TYPE" == "usb" ]]; then - - while true; do - BASE_DEST=$(get_external_backup_mount_point) - if [[ -z "$BASE_DEST" ]]; then - dialog --backtitle "ProxMenux" --yesno "$(translate 'No external disk detected or mounted. Would you like to retry?')" 8 60 - [[ $? -eq 0 ]] && continue - return 1 - fi - - DEST="$BASE_DEST/borgbackup" - mkdir -p "$DEST" - - DISK_DEV=$(df "$BASE_DEST" | awk 'NR==2{print $1}') - PKNAME=$(lsblk -no PKNAME "$DISK_DEV" 2>/dev/null) - [[ -z "$PKNAME" ]] && PKNAME=$(basename "$DISK_DEV" | sed 's/[0-9]*$//') - if [[ -n "$PKNAME" && -b /dev/$PKNAME ]]; then - DISK_MODEL=$(lsblk -no MODEL "/dev/$PKNAME") - else - DISK_MODEL="(unknown)" - fi - FREE_SPACE=$(df -h "$BASE_DEST" | awk 'NR==2{print $4}') - - dialog --backtitle "ProxMenux" \ - --title "$(translate "Dedicated Backup Disk")" \ - --yesno "\n$(translate "Mount point:") $DEST\n\n\ - $(translate "Disk model:") $DISK_MODEL\n\ - $(translate "Available space:") $FREE_SPACE\n\n\ - $(translate "Use this disk for backup?")" 12 70 - - if [[ $? -eq 0 ]]; then - break - else - return 1 - fi - done - - - elif [[ "$TYPE" == "remote" ]]; then - REMOTE_USER=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter SSH user for remote:')" 10 60 "root" 3>&1 1>&2 2>&3) || return 1 - REMOTE_HOST=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter SSH host:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - REMOTE_PATH=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter remote path:')" 10 60 "/backup/borgbackup" 3>&1 1>&2 2>&3) || return 1 - DEST="ssh://$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH" - fi - - - dialog --backtitle "ProxMenux" --yesno "$(translate 'Do you want to encrypt the backup?')" 8 60 - if [[ $? -eq 0 ]]; then - BORG_KEY=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter Borg encryption passphrase (will be saved):')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - ENCRYPT_OPT="--encryption=repokey" - export BORG_PASSPHRASE="$BORG_KEY" - else - ENCRYPT_OPT="--encryption=none" - fi - - if [[ "$TYPE" == "local" || "$TYPE" == "usb" ]]; then - if [[ ! -f "$DEST/config" ]]; then - "$BORG_APPIMAGE" init $ENCRYPT_OPT "$DEST" - if [[ $? -ne 0 ]]; then - clear - show_proxmenux_logo - msg_error "$(translate "Failed to initialize Borg repo at") $DEST" - sleep 5 - return 1 - fi - fi - fi - - - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Borg backup will start now. This may take a while.')" 8 40 - - clear - show_proxmenux_logo - msg_info2 "$(translate "Starting backup with BorgBackup...")" - echo -e - - TOTAL_SIZE=$(du -cb "$@" | awk '/total$/ {print $1}') - TOTAL_SIZE_GB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE/1024/1024/1024}") - - echo -e "${BL}$(translate "Included directories:")${WHITE} $*${RESET}" - echo -e "${BL}$(translate "Total size:")${WHITE} ${TOTAL_SIZE_GB} GB${RESET}" - - - # 6. Lanzar el backup y guardar log -# "$BORG_APPIMAGE" create --progress "$DEST"::"root-$(hostname)-$(date +%Y%m%d_%H%M)" $SRC 2>&1 | tee "$LOGFILE" - - "$BORG_APPIMAGE" create --progress "$DEST"::"root-$(hostname)-$(date +%Y%m%d_%H%M)" "$@" 2>&1 | tee "$LOGFILE" - - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" - msg_ok "$(translate "Backup process finished.")" - echo - msg_success "$(translate "Press Enter to return to the main menu...")" - read -r -} -# =============================== - - - - -# ========== LOCAL TAR ========== -backup_to_local_tar() { -# local SRC="$1" - local TYPE - local DEST - local LOGFILE="/tmp/tar-backup.log" - - -if ! command -v pv &>/dev/null; then - apt-get update -qq && apt-get install -y pv >/dev/null 2>&1 -fi - - - - TYPE=$(dialog --backtitle "ProxMenux" --menu "$(translate 'Select backup destination:')" 15 60 2 \ - "local" "$(translate 'Local directory')" \ - "usb" "$(translate 'Internal/External dedicated disk')" \ - 3>&1 1>&2 2>&3) || return 1 - - if [[ "$TYPE" == "local" ]]; then - DEST=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter directory for backup:')" 10 60 "/backup" 3>&1 1>&2 2>&3) || return 1 - - mkdir -p "$DEST" - - -else - - -while true; do - DEST=$(get_external_backup_mount_point) - if [[ -z "$DEST" ]]; then - dialog --backtitle "ProxMenux" --yesno "No external disk detected or mounted. Would you like to retry?" 8 60 - [[ $? -eq 0 ]] && continue - return 1 - fi - - DISK_DEV=$(df "$DEST" | awk 'NR==2{print $1}') - PKNAME=$(lsblk -no PKNAME "$DISK_DEV" 2>/dev/null) - [[ -z "$PKNAME" ]] && PKNAME=$(basename "$DISK_DEV" | sed 's/[0-9]*$//') - if [[ -n "$PKNAME" && -b /dev/$PKNAME ]]; then - DISK_MODEL=$(lsblk -no MODEL "/dev/$PKNAME") - else - DISK_MODEL="(unknown)" - fi - FREE_SPACE=$(df -h "$DEST" | awk 'NR==2{print $4}') - - - - dialog --backtitle "ProxMenux" \ - --title "$(translate "Dedicated Backup Disk")" \ - --yesno "\n$(translate "Mount point:") $DEST\n\n\ - $(translate "Disk model:") $DISK_MODEL\n\ - $(translate "Available space:") $FREE_SPACE\n\n\ - $(translate "Use this disk for backup?")" 12 70 - - - if [[ $? -eq 0 ]]; then - mkdir -p "$DEST" - break - else - return 1 - fi -done - - - -fi - - -TAR_INPUT="" -TOTAL_SIZE=0 -for src in $SRC; do - sz=$(du -sb "$src" 2>/dev/null | awk '{print $1}') - TOTAL_SIZE=$((TOTAL_SIZE + sz)) - TAR_INPUT="$TAR_INPUT $src" -done - -local FILENAME="root-$(hostname)-$(date +%Y%m%d_%H%M).tar.gz" -clear -show_proxmenux_logo -msg_info2 "$(translate "Starting backup with tar...")" -echo -e - - -TOTAL_SIZE=$(du -cb "$@" | awk '/total$/ {print $1}') -TOTAL_SIZE_GB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE/1024/1024/1024}") - -echo -e "${BL}$(translate "Included directories:")${WHITE} $*${RESET}" -echo -e "${BL}$(translate "Total size:")${WHITE} ${TOTAL_SIZE_GB} GB${RESET}" - -tar -cf - "$@" 2> >(grep -v "Removing leading \`/'" >&2) \ -| pv -s "$TOTAL_SIZE" \ -| gzip > "$DEST/$FILENAME" - - -echo -ne "\033[1A\r\033[K" - -echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" -msg_ok "$(translate "Backup process finished. Review log above or in /tmp/tar-backup.log")" -echo -msg_success "$(translate "Press Enter to return to the main menu...")" -read -r - -} -# =============================== - - -host_backup_menu diff --git a/scripts/backup_restore/backup_host3.sh b/scripts/backup_restore/backup_host3.sh deleted file mode 100644 index 9ea0d07d..00000000 --- a/scripts/backup_restore/backup_host3.sh +++ /dev/null @@ -1,1294 +0,0 @@ -#!/usr/bin/env bash - -# Configuration ============================================ -LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts" -BASE_DIR="/usr/local/share/proxmenux" -UTILS_FILE="$BASE_DIR/utils.sh" -VENV_PATH="/opt/googletrans-env" - -if [[ -f "$UTILS_FILE" ]]; then - source "$UTILS_FILE" -fi -load_language -initialize_cache -# ========================================================== - - -get_external_backup_mount_point() { - local BACKUP_MOUNT_FILE="/usr/local/share/proxmenux/last_backup_mount.txt" - local STORAGE_REPO="$LOCAL_SCRIPTS/backup_restore" - local MOUNT_POINT - - if [[ -f "$BACKUP_MOUNT_FILE" ]]; then - MOUNT_POINT=$(head -n1 "$BACKUP_MOUNT_FILE" | tr -d '\r\n' | xargs) - >&2 echo "DEBUG: Valor MOUNT_POINT='$MOUNT_POINT'" - if [[ ! -d "$MOUNT_POINT" ]]; then - msg_error "Mount point does not exist: $MOUNT_POINT" - rm -f "$BACKUP_MOUNT_FILE" - return 1 - fi - if ! mountpoint -q "$MOUNT_POINT"; then - msg_error "Mount point is not mounted: $MOUNT_POINT" - rm -f "$BACKUP_MOUNT_FILE" - return 1 - fi - - echo "$MOUNT_POINT" - return 0 - else - source "$STORAGE_REPO/mount_disk_host_bk.sh" - MOUNT_POINT=$(mount_disk_host_bk) - [[ -z "$MOUNT_POINT" ]] && msg_error "$(translate "No disk mounted.")" && return 1 - echo "$MOUNT_POINT" - return 0 - fi -} - - - -# === Host Backup Main Menu === -host_backup_menu() { - while true; do - local CHOICE - CHOICE=$(dialog --backtitle "ProxMenux" \ - --title "$(translate 'Host Backup')" \ - --menu "\n$(translate 'Select backup option:')" 22 70 12 \ - "" "$(translate '--- FULL BACKUP ---')" \ - 1 "$(translate 'Full backup to Proxmox Backup Server (PBS)')" \ - 2 "$(translate 'Full backup with BorgBackup')" \ - 3 "$(translate 'Full backup to local .tar.gz')" \ - "" "$(translate '--- CUSTOM BACKUP ---')" \ - 4 "$(translate 'Custom backup to PBS')" \ - 5 "$(translate 'Custom backup with BorgBackup')" \ - 6 "$(translate 'Custom backup to local .tar.gz')" \ - 0 "$(translate 'Return')" \ - 3>&1 1>&2 2>&3) || return 0 - - case "$CHOICE" in - 1) backup_full_pbs_root ;; - 2) backup_with_borg "/boot/efi /etc/pve /etc/network /var/lib/pve-cluster /root /etc/ssh /home /usr/local/bin /etc/cron.d /etc/systemd/system /var/lib/vz" ;; - 3) backup_to_local_tar "/boot/efi /etc/pve /etc/network /var/lib/pve-cluster /root /etc/ssh /home /usr/local/bin /etc/cron.d /etc/systemd/system /var/lib/vz" ;; - 4) custom_backup_menu backup_to_pbs ;; - 5) custom_backup_menu backup_with_borg ;; - 6) custom_backup_menu backup_to_local_tar ;; - 0) break ;; - esac - done -} - - - -# === Menu checklist for custom backup === -custom_backup_menu() { - declare -A BACKUP_PATHS=( - [etc-pve]="/etc/pve" - [etc-network]="/etc/network" - [var-lib-pve-cluster]="/var/lib/pve-cluster" - [root-dir]="/root" - [etc-ssh]="/etc/ssh" - [home]="/home" - [local-bin]="/usr/local/bin" - [cron]="/etc/cron.d" - [custom-systemd]="/etc/systemd/system" - [var-lib-vz]="/var/lib/vz" - ) - local CHECKLIST_OPTIONS=() - for KEY in "${!BACKUP_PATHS[@]}"; do - DIR="${BACKUP_PATHS[$KEY]}" - CHECKLIST_OPTIONS+=("$KEY" "$DIR" "off") - done - - SELECTED_KEYS=$(dialog --separate-output --checklist \ - "$(translate 'Select directories to backup:')" 22 70 12 \ - "${CHECKLIST_OPTIONS[@]}" \ - 3>&1 1>&2 2>&3) || return 1 - - local BACKUP_DIRS=() - for KEY in $SELECTED_KEYS; do - BACKUP_DIRS+=("${BACKUP_PATHS[$KEY]}") - done - - -# "$1" "${BACKUP_DIRS[*]}" - "$1" "${BACKUP_DIRS[@]}" - - -} - - - - - - - - - - - -configure_pbs_repository() { -local PBS_REPO_FILE="/usr/local/share/proxmenux/pbs-repo.conf" -local PBS_PASS_FILE="/usr/local/share/proxmenux/pbs-pass.txt" -local PBS_TOKEN_FILE="/usr/local/share/proxmenux/pbs-token.txt" -local PBS_MANUAL_CONFIGS="/usr/local/share/proxmenux/pbs-manual-configs.txt" - -[[ ! -f "$PBS_MANUAL_CONFIGS" ]] && touch "$PBS_MANUAL_CONFIGS" - -local PBS_CONFIGS=() -local PBS_SOURCES=() -local PBS_USERNAMES=() - -if [[ -f "/etc/pve/storage.cfg" ]]; then - local current_pbs="" server="" datastore="" username="" - - while IFS= read -r line; do - if [[ $line =~ ^pbs:\ (.+)$ ]]; then - if [[ -n "$current_pbs" && -n "$server" && -n "$datastore" && -n "$username" ]]; then - PBS_CONFIGS+=("$current_pbs|$username@$server:$datastore") - PBS_SOURCES+=("proxmox|$current_pbs") - PBS_USERNAMES+=("$username") - fi - current_pbs="${BASH_REMATCH[1]}" - server="" datastore="" username="" - elif [[ -n "$current_pbs" ]]; then - if [[ $line =~ ^[[:space:]]*server[[:space:]]+(.+)$ ]]; then - server="${BASH_REMATCH[1]}" - elif [[ $line =~ ^[[:space:]]*datastore[[:space:]]+(.+)$ ]]; then - datastore="${BASH_REMATCH[1]}" - elif [[ $line =~ ^[[:space:]]*username[[:space:]]+(.+)$ ]]; then - username="${BASH_REMATCH[1]}" - elif [[ $line =~ ^[a-zA-Z]+: ]]; then - if [[ -n "$server" && -n "$datastore" && -n "$username" ]]; then - PBS_CONFIGS+=("$current_pbs|$username@$server:$datastore") - PBS_SOURCES+=("proxmox|$current_pbs") - PBS_USERNAMES+=("$username") - fi - current_pbs="" - fi - fi - done < "/etc/pve/storage.cfg" - - if [[ -n "$current_pbs" && -n "$server" && -n "$datastore" && -n "$username" ]]; then - PBS_CONFIGS+=("$current_pbs|$username@$server:$datastore") - PBS_SOURCES+=("proxmox|$current_pbs") - PBS_USERNAMES+=("$username") - fi -fi - -if [[ -f "$PBS_MANUAL_CONFIGS" ]]; then - while IFS= read -r line; do - if [[ -n "$line" ]]; then - PBS_CONFIGS+=("$line") - local name="${line%%|*}" - PBS_SOURCES+=("manual|$name") - PBS_USERNAMES+=("") - fi - done < "$PBS_MANUAL_CONFIGS" -fi - -local menu_options=() -local i=1 - -for j in "${!PBS_CONFIGS[@]}"; do - local config="${PBS_CONFIGS[$j]}" - local source="${PBS_SOURCES[$j]}" - local name="${config%%|*}" - local repo="${config##*|}" - local source_type="${source%%|*}" - - if [[ "$source_type" == "proxmox" ]]; then - menu_options+=("$i" " $name ($repo) [Proxmox]") - else - menu_options+=("$i" " $name ($repo) [Manual]") - fi - ((i++)) -done - -menu_options+=("" "") -menu_options+=("$i" "\Z4\Zb $(translate 'Configure new PBS')\Zn") -local choice -choice=$(dialog --colors --backtitle "ProxMenux" --title "PBS Server Selection" \ ---menu "$(translate 'Select PBS server for this backup:')" 22 70 12 "${menu_options[@]}" 3>&1 1>&2 2>&3) || return 1 - -if [[ $choice -eq $i ]]; then - configure_pbs_manually || return 1 -else - local selected_config="${PBS_CONFIGS[$((choice-1))]}" - local selected_source="${PBS_SOURCES[$((choice-1))]}" - local selected_username="${PBS_USERNAMES[$((choice-1))]}" - local pbs_name="${selected_config%%|*}" - local source_type="${selected_source%%|*}" - PBS_REPO="${selected_config##*|}" - - { - mkdir -p "$(dirname "$PBS_REPO_FILE")" - echo "$PBS_REPO" > "$PBS_REPO_FILE" - } >/dev/null 2>&1 - - local password_found=false - if [[ "$source_type" == "proxmox" ]]; then - local password_file="/etc/pve/priv/storage/${pbs_name}.pw" - - if [[ -f "$password_file" ]]; then - local auth_content - auth_content=$(<"$password_file") - - - [[ -f "$PBS_PASS_FILE" ]] && rm "$PBS_PASS_FILE" - [[ -f "$PBS_TOKEN_FILE" ]] && rm "$PBS_TOKEN_FILE" - - - if [[ "$selected_username" == *"@pbs!"* ]]; then - - echo "$auth_content" > "$PBS_PASS_FILE" - chmod 600 "$PBS_PASS_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') PBS Cloud Token -$(translate 'Token ID:') $selected_username" 15 80 - elif [[ "$auth_content" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]]; then - local full_token="${selected_username}:${auth_content}" - echo "$full_token" > "$PBS_TOKEN_FILE" - chmod 600 "$PBS_TOKEN_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') API Token -$(translate 'Token ID:') $selected_username" 15 80 - elif [[ "$auth_content" == *":"* ]]; then - echo "$auth_content" > "$PBS_TOKEN_FILE" - chmod 600 "$PBS_TOKEN_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') API Token (Complete)" 12 80 - else - echo "$auth_content" > "$PBS_PASS_FILE" - chmod 600 "$PBS_PASS_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') Password" 12 80 - fi - fi - else - local manual_pass_file="/usr/local/share/proxmenux/pbs-pass-${pbs_name}.txt" - if [[ -f "$manual_pass_file" ]]; then - [[ -f "$PBS_TOKEN_FILE" ]] && rm "$PBS_TOKEN_FILE" - - { - cp "$manual_pass_file" "$PBS_PASS_FILE" - chmod 600 "$PBS_PASS_FILE" - } >/dev/null 2>&1 - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using manual PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Password:') $(translate 'Previously saved')" 12 80 - fi - fi - - if ! $password_found; then - dialog --backtitle "ProxMenux" --title "Password Required" --msgbox "$(translate 'Password not found for:') $pbs_name -$(translate 'Please enter the password.')" 10 60 - get_pbs_password "$pbs_name" || return 1 - fi - - clear -fi -} - - - - - - - - - - -# ========== PBS BACKUP ========== -backup_full_pbs_root() { - local HOSTNAME PBS_REPO PBS_KEY_FILE PBS_PASS_FILE PBS_TOKEN_FILE PBS_ENCRYPTION_PASS_FILE ENCRYPT_OPT="" - HOSTNAME=$(hostname) - - - local PBS_REPO_FILE="/usr/local/share/proxmenux/pbs-repo.conf" - PBS_KEY_FILE="/usr/local/share/proxmenux/pbs-key.conf" - PBS_PASS_FILE="/usr/local/share/proxmenux/pbs-pass.txt" - PBS_TOKEN_FILE="/usr/local/share/proxmenux/pbs-token.txt" - PBS_ENCRYPTION_PASS_FILE="/usr/local/share/proxmenux/pbs-encryption-pass.txt" - LOGFILE="/tmp/pbs-backup-${HOSTNAME}.log" - - - configure_pbs_repository || return 1 - - if [[ ! -f "$PBS_REPO_FILE" ]]; then - msg_error "$(translate "Failed to configure PBS connection")" - sleep 3 - return 1 - fi - PBS_REPO=$(<"$PBS_REPO_FILE") - - - unset PBS_PASSWORD PBS_API_TOKEN PBS_API_TOKEN_ID PBS_API_TOKEN_SECRET PBS_FINGERPRINT - - - local AUTH_TYPE="password" - local PBS_AUTH_VALUE="" - - if [[ "$PBS_REPO" == *"@pbs!"* ]]; then - - AUTH_TYPE="pbs_cloud" - [[ -f "$PBS_PASS_FILE" ]] && PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - elif [[ -f "$PBS_TOKEN_FILE" ]]; then - - AUTH_TYPE="token" - PBS_AUTH_VALUE=$(<"$PBS_TOKEN_FILE") - elif [[ -f "$PBS_PASS_FILE" ]]; then - - AUTH_TYPE="password" - PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - else - msg_error "$(translate "No PBS authentication found!")" - sleep 3 - return 1 - fi - - - dialog --backtitle "ProxMenux" --title "Encryption" --yesno "$(translate 'Do you want to encrypt the backup?')" 8 60 - if [[ $? -eq 0 ]]; then - - if [[ ! -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - while true; do - PBS_KEY_PASS=$(dialog --backtitle "ProxMenux" --title "Encryption Password" --insecure --passwordbox "$(translate 'Enter encryption password (different from PBS login):')" 12 70 "" 3>&1 1>&2 2>&3) || return 1 - PBS_KEY_PASS2=$(dialog --backtitle "ProxMenux" --title "Encryption Password" --insecure --passwordbox "$(translate 'Confirm encryption password:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - if [[ "$PBS_KEY_PASS" == "$PBS_KEY_PASS2" ]]; then - break - else - dialog --backtitle "ProxMenux" --title "Error" --msgbox "$(translate 'Passwords do not match! Please try again.')" 8 50 - fi - done - - - { - echo "$PBS_KEY_PASS" > "$PBS_ENCRYPTION_PASS_FILE" - chmod 600 "$PBS_ENCRYPTION_PASS_FILE" - } >/dev/null 2>&1 - - dialog --backtitle "ProxMenux" --title "Success" --msgbox "$(translate 'Encryption password saved successfully!')" 8 50 - fi - - - if [[ ! -f "$PBS_KEY_FILE" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - - dialog --backtitle "ProxMenux" --title "Encryption" --infobox "$(translate 'Creating encryption key...')" 5 50 - - expect -c " - set timeout 30 - spawn proxmox-backup-client key create \"$PBS_KEY_FILE\" - expect { - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - \"Verify Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - eof - } - " >/dev/null 2>&1 - - if [[ ! -f "$PBS_KEY_FILE" ]]; then - dialog --backtitle "ProxMenux" --title "Error" --msgbox "$(translate 'Error creating encryption key.')" 8 40 - return 1 - fi - - dialog --backtitle "ProxMenux" --title "Important" --msgbox "$(translate 'IMPORTANT: Save the key file. Without it you will not be able to restore your backups!')\n\n$(translate 'Key file location:') $PBS_KEY_FILE" 12 70 - fi - ENCRYPT_OPT="--keyfile $PBS_KEY_FILE" - else - ENCRYPT_OPT="" - fi - - - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "Starting backup to PBS")" - echo -e - echo -e "${BL}$(translate "PBS Repository:")${WHITE} $PBS_REPO${RESET}" - echo -e "${BL}$(translate "Backup ID:")${WHITE} $HOSTNAME${RESET}" - echo -e "${BL}$(translate "Included:")${WHITE} /boot/efi /etc/pve (all root)${RESET}" - echo -e "${BL}$(translate "Encryption:")${WHITE} $([[ -n "$ENCRYPT_OPT" ]] && echo "Enabled" || echo "Disabled")${RESET}" - - - case "$AUTH_TYPE" in - "pbs_cloud") echo -e "${BL}$(translate "Authentication:")${WHITE} PBS Cloud Token${RESET}" ;; - "token") echo -e "${BL}$(translate "Authentication:")${WHITE} API Token${RESET}" ;; - "password") echo -e "${BL}$(translate "Authentication:")${WHITE} Password${RESET}" ;; - esac - - echo -e "${BL}$(translate "Log file:")${WHITE} $LOGFILE${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}-------------------------------${RESET}" - echo "" - - - local backup_cmd="proxmox-backup-client backup --include-dev /boot/efi --include-dev /etc/pve root-${HOSTNAME}.pxar:/ --repository \"$PBS_REPO\" $ENCRYPT_OPT --backup-type host --backup-id \"$HOSTNAME\" --backup-time \"$(date +%s)\"" - local backup_result=0 - - case "$AUTH_TYPE" in - "pbs_cloud") - - if [[ -n "$ENCRYPT_OPT" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - echo "$(translate "Starting encrypted full backup with PBS Cloud...")" - echo "" - - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - else - - echo "$(translate "Starting unencrypted full backup with PBS Cloud...")" - echo "" - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" bash -c "$backup_cmd" | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - fi - ;; - - "token") - - if [[ -n "$ENCRYPT_OPT" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - echo "$(translate "Starting encrypted full backup with API Token...")" - echo "" - - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - else - echo "$(translate "Starting unencrypted full backup with API Token...")" - echo "" - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" bash -c "$backup_cmd" | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - fi - ;; - - "password") - - if [[ -n "$ENCRYPT_OPT" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - echo "$(translate "Starting encrypted full backup with password...")" - echo "" - - env -i expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - else - - echo "$(translate "Starting unencrypted full backup with password...")" - echo "" - - env -i expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - fi - ;; - esac - - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" - if [[ $backup_result -eq 0 ]]; then - msg_ok "$(translate "Full backup process completed successfully")" - else - msg_error "$(translate "Backup process finished with errors")" - fi - - echo "" - msg_success "$(translate "Press Enter to return to the main menu...")" - read -r -} - - - - - - - - - - - - - - -backup_to_pbs() { - local HOSTNAME TIMESTAMP SNAPSHOT - HOSTNAME=$(hostname) - TIMESTAMP=$(date +%Y-%m-%d_%H-%M) - SNAPSHOT="${HOSTNAME}-${TIMESTAMP}" - - local PBS_REPO_FILE="/usr/local/share/proxmenux/pbs-repo.conf" - local PBS_KEY_FILE="/usr/local/share/proxmenux/pbs-key.conf" - local PBS_PASS_FILE="/usr/local/share/proxmenux/pbs-pass.txt" - local PBS_TOKEN_FILE="/usr/local/share/proxmenux/pbs-token.txt" - local PBS_ENCRYPTION_PASS_FILE="/usr/local/share/proxmenux/pbs-encryption-pass.txt" - local PBS_REPO ENCRYPT_OPT USE_ENCRYPTION - local PBS_KEY_PASS PBS_REPO_PASS - - configure_pbs_repository || return 1 - PBS_REPO=$(<"$PBS_REPO_FILE") - - - unset PBS_PASSWORD PBS_API_TOKEN PBS_API_TOKEN_ID PBS_API_TOKEN_SECRET PBS_FINGERPRINT - - USE_ENCRYPTION=false - dialog --backtitle "ProxMenux" --yesno "$(translate 'Do you want to encrypt the backup?')" 8 60 - [[ $? -eq 0 ]] && USE_ENCRYPTION=true - - if $USE_ENCRYPTION && ! command -v expect >/dev/null 2>&1; then - apt-get update -qq >/dev/null 2>&1 - apt-get install -y expect >/dev/null 2>&1 - fi - - if [[ "$#" -lt 1 ]]; then - clear - show_proxmenux_logo - msg_error "$(translate "No directories specified for backup.")" - sleep 2 - return 1 - fi - - local TOTAL="$#" - local COUNT=1 - - - local AUTH_TYPE="password" - local PBS_AUTH_VALUE="" - - if [[ "$PBS_REPO" == *"@pbs!"* ]]; then - - AUTH_TYPE="pbs_cloud" - [[ -f "$PBS_PASS_FILE" ]] && PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - elif [[ -f "$PBS_TOKEN_FILE" ]]; then - - AUTH_TYPE="token" - PBS_AUTH_VALUE=$(<"$PBS_TOKEN_FILE") - elif [[ -f "$PBS_PASS_FILE" ]]; then - - AUTH_TYPE="password" - PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - else - msg_error "$(translate "No PBS authentication found!")" - return 1 - fi - - for dir in "$@"; do - local SAFE_NAME SAFE_ID PXAR_NAME - SAFE_NAME=$(basename "$dir" | tr '.-/' '_') - PXAR_NAME="root-custom-${SAFE_NAME}-${SNAPSHOT}.pxar" - SAFE_ID="custom-${HOSTNAME}-${SAFE_NAME}" - - msg_info2 "$(translate "[$COUNT/$TOTAL] Backing up") $dir $(translate "as") $PXAR_NAME" - - ENCRYPT_OPT="" - - if $USE_ENCRYPTION; then - if [[ -f "$PBS_KEY_FILE" ]]; then - ENCRYPT_OPT="--keyfile $PBS_KEY_FILE" - else - while true; do - PBS_KEY_PASS=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Enter encryption password (different from PBS login):')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - PBS_KEY_PASS2=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Confirm encryption password:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - if [[ "$PBS_KEY_PASS" == "$PBS_KEY_PASS2" ]]; then - break - else - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Passwords do not match! Please try again.')" 8 50 - fi - done - - { - echo "$PBS_KEY_PASS" > "$PBS_ENCRYPTION_PASS_FILE" - chmod 600 "$PBS_ENCRYPTION_PASS_FILE" - } >/dev/null 2>&1 - - expect -c " - set timeout 30 - spawn proxmox-backup-client key create \"$PBS_KEY_FILE\" - expect { - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - \"Verify Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - eof - } - " >/dev/null 2>&1 - - if [[ ! -f "$PBS_KEY_FILE" ]]; then - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Error creating encryption key.')" 8 40 - return 1 - fi - ENCRYPT_OPT="--keyfile $PBS_KEY_FILE" - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Encryption key generated. Save it in a safe place!')" 10 60 - fi - fi - - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "Starting backup to PBS")" - TOTAL_SIZE=$(du -cb "$@" | awk '/total$/ {print $1}') - TOTAL_SIZE_GB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE/1024/1024/1024}") - echo -e - echo -e "${BL}$(translate "PBS Repository:")${WHITE} $PBS_REPO${RESET}" - echo -e "${BL}$(translate "Backup ID:")${WHITE} $HOSTNAME${RESET}" - echo -e "${BL}$(translate "Encryption:")${WHITE} $([[ -n "$ENCRYPT_OPT" ]] && echo "Enabled" || echo "Disabled")${RESET}" - - case "$AUTH_TYPE" in - "pbs_cloud") echo -e "${BL}$(translate "Authentication:")${WHITE} PBS Cloud Token${RESET}" ;; - "token") echo -e "${BL}$(translate "Authentication:")${WHITE} API Token${RESET}" ;; - "password") echo -e "${BL}$(translate "Authentication:")${WHITE} Password${RESET}" ;; - esac - - echo -e "${BL}$(translate "Included directories:")${WHITE} $*${RESET}" - echo -e "${BL}$(translate "Total size:")${WHITE} ${TOTAL_SIZE_GB} GB${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}-------------------------------${RESET}" - - - local backup_cmd="proxmox-backup-client backup \"${PXAR_NAME}:$dir\" --repository \"$PBS_REPO\" $ENCRYPT_OPT --backup-type host --backup-id \"$SAFE_ID\" --backup-time \"$(date +%s)\"" - - case "$AUTH_TYPE" in - "pbs_cloud") - - if $USE_ENCRYPTION && [[ -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - PBS_KEY_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" expect -c " - set timeout 300 - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - eof - } - " - else - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" bash -c "$backup_cmd" - fi - ;; - - "token") - - if $USE_ENCRYPTION && [[ -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - PBS_KEY_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" expect -c " - set timeout 300 - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - eof - } - " - else - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" bash -c "$backup_cmd" - fi - ;; - - "password") - - if $USE_ENCRYPTION && [[ -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - PBS_KEY_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - env -i expect -c " - set timeout 300 - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " - else - env -i expect -c " - set timeout 300 - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " - fi - ;; - esac - - COUNT=$((COUNT+1)) - done - - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" - msg_ok "$(translate "Backup process finished.")" - echo "" - msg_success "$(translate "Press Enter to return to the main menu...")" - read -r -} -# =============================== - - - - - - - -# ========== BORGBACKUP ========== -backup_with_borg() { - local BORG_APPIMAGE="/usr/local/share/proxmenux/borg" - local LOGFILE="/tmp/borg-backup.log" - local BORG_PASS_FILE="/usr/local/share/proxmenux/borg-pass.txt" - local BORG_KEY_FILE="/usr/local/share/proxmenux/borg-key" - local DEST - local TYPE - local ENCRYPT_OPT="" - local BORG_KEY - - # Descargar BorgBackup si no existe - if [[ ! -x "$BORG_APPIMAGE" ]]; then - clear - show_proxmenux_logo - msg_info "$(translate "BorgBackup not found. Downloading AppImage...")" - mkdir -p /usr/local/share/proxmenux - wget -qO "$BORG_APPIMAGE" "https://github.com/borgbackup/borg/releases/download/1.2.8/borg-linux64" - chmod +x "$BORG_APPIMAGE" - msg_ok "$(translate "BorgBackup downloaded and ready.")" - fi - - # Seleccionar tipo de destino - TYPE=$(dialog --backtitle "ProxMenux" --menu "$(translate 'Select Borg backup destination:')" 15 60 4 \ - "local" "$(translate 'Local directory')" \ - "usb" "$(translate 'Internal/External dedicated disk')" \ - "remote" "$(translate 'Remote server (SSH)')" \ - "pbs" "$(translate 'remote-backups.com')" \ - 3>&1 1>&2 2>&3) || return 1 - - - if [[ "$TYPE" == "local" ]]; then - DEST=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter local directory for backup:')" 10 60 "/backup/borgbackup" 3>&1 1>&2 2>&3) || return 1 - mkdir -p "$DEST" - - elif [[ "$TYPE" == "usb" ]]; then - while true; do - BASE_DEST=$(get_external_backup_mount_point) - if [[ -z "$BASE_DEST" ]]; then - dialog --backtitle "ProxMenux" --yesno "$(translate 'No external disk detected or mounted. Would you like to retry?')" 8 60 - [[ $? -eq 0 ]] && continue - return 1 - fi - - DEST="$BASE_DEST/borgbackup" - mkdir -p "$DEST" - - DISK_DEV=$(df "$BASE_DEST" | awk 'NR==2{print $1}') - PKNAME=$(lsblk -no PKNAME "$DISK_DEV" 2>/dev/null) - [[ -z "$PKNAME" ]] && PKNAME=$(basename "$DISK_DEV" | sed 's/[0-9]*$//') - if [[ -n "$PKNAME" && -b /dev/$PKNAME ]]; then - DISK_MODEL=$(lsblk -no MODEL "/dev/$PKNAME") - else - DISK_MODEL="(unknown)" - fi - FREE_SPACE=$(df -h "$BASE_DEST" | awk 'NR==2{print $4}') - - dialog --backtitle "ProxMenux" \ - --title "$(translate "Dedicated Backup Disk")" \ - --yesno "\n$(translate "Mount point:") $DEST\n\n\ - $(translate "Disk model:") $DISK_MODEL\n\ - $(translate "Available space:") $FREE_SPACE\n\n\ - $(translate "Use this disk for backup?")" 12 70 - - if [[ $? -eq 0 ]]; then - break - else - return 1 - fi - done - - elif [[ "$TYPE" == "remote" ]]; then - - REMOTE_USER=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter SSH user for remote:')" 10 60 "root" 3>&1 1>&2 2>&3) || return 1 - REMOTE_HOST=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter SSH host:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - REMOTE_PATH=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter remote path:')" 10 60 "/backup/borgbackup" 3>&1 1>&2 2>&3) || return 1 - - - dialog --backtitle "ProxMenux" --yesno "$(translate 'Do you want to use SSH key authentication?')" 8 60 - if [[ $? -eq 0 ]]; then - SSH_KEY=$(dialog --backtitle "ProxMenux" --fselect "$HOME/.ssh/" 10 60 3>&1 1>&2 2>&3) || return 1 - export BORG_RSH="ssh -i $SSH_KEY" - fi - - DEST="ssh://$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH" - - elif [[ "$TYPE" == "pbs" ]]; then - - PBS_CUSTOMER_ID=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter PBS customer ID (from Connect panel):')" 10 70 "" 3>&1 1>&2 2>&3) || return 1 - PBS_SERVER=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter PBS server (from Connect panel):')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - - local SSH_KEYS=() - local SSH_KEY_NAMES=() - - - for key_file in ~/.ssh/id_rsa ~/.ssh/id_ed25519 ~/.ssh/pbs_host_key ~/.ssh/id_ecdsa; do - if [[ -f "$key_file" && -f "${key_file}.pub" ]]; then - SSH_KEYS+=("$key_file") - local key_type=$(head -1 "${key_file}.pub" | awk '{print $1}') - local key_comment=$(head -1 "${key_file}.pub" | awk '{print $3}') - SSH_KEY_NAMES+=("$(basename "$key_file") ($key_type) - $key_comment") - fi - done - - - if [[ ${#SSH_KEYS[@]} -eq 0 ]]; then - dialog --backtitle "ProxMenux" --yesno "$(translate 'No SSH keys found. Do you want to create one now?')" 8 60 - if [[ $? -eq 0 ]]; then - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "Creating SSH key for PBS-host.de...")" - - - ssh-keygen -t rsa -b 4096 -f ~/.ssh/pbs_host_key -N "" -C "pbs-backup-$(hostname)" - - if [[ $? -eq 0 ]]; then - SSH_KEY="$HOME/.ssh/pbs_host_key" - msg_ok "$(translate "SSH key created successfully!")" - else - msg_error "$(translate "Failed to create SSH key")" - return 1 - fi - else - return 1 - fi - elif [[ ${#SSH_KEYS[@]} -eq 1 ]]; then - - SSH_KEY="${SSH_KEYS[0]}" - else - - local menu_options=() - for i in "${!SSH_KEYS[@]}"; do - menu_options+=("$i" "${SSH_KEY_NAMES[$i]}") - done - - local choice - choice=$(dialog --backtitle "ProxMenux" --menu "$(translate 'Select SSH key to use:')" 15 80 10 "${menu_options[@]}" 3>&1 1>&2 2>&3) || return 1 - SSH_KEY="${SSH_KEYS[$choice]}" - fi - - export BORG_RSH="ssh -i $SSH_KEY" - DEST="$PBS_CUSTOMER_ID@$PBS_SERVER:./borg" - - - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "PBS-host.de Configuration")" - echo -e "${BL}$(translate "Server:")${WHITE} $PBS_SERVER${RESET}" - echo -e "${BL}$(translate "Customer ID:")${WHITE} $PBS_CUSTOMER_ID${RESET}" - echo -e "${BL}$(translate "SSH Key:")${WHITE} $SSH_KEY${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}-------------------------------${RESET}" - echo "" - - - chmod 600 "$SSH_KEY" - - - local SSH_PUB_KEY="${SSH_KEY}.pub" - if [[ -f "$SSH_PUB_KEY" ]]; then - echo -e "${BL}$(translate "Your SSH Public Key:")${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}" - echo -e "${WHITE}$(cat "$SSH_PUB_KEY")${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}" - echo "" - - - if command -v xclip >/dev/null 2>&1; then - echo -e "${BL}$(translate "Copying to clipboard...")${RESET}" - cat "$SSH_PUB_KEY" | xclip -selection clipboard - msg_ok "$(translate "Public key copied to clipboard!")" - elif command -v pbcopy >/dev/null 2>&1; then - echo -e "${BL}$(translate "Copying to clipboard...")${RESET}" - cat "$SSH_PUB_KEY" | pbcopy - msg_ok "$(translate "Public key copied to clipboard!")" - fi - - echo "" - echo -e "${BL}$(translate "INSTRUCTIONS:")${RESET}" - echo "1. $(translate "Copy the key above (or use clipboard if available)")" - echo "2. $(translate "Go to PBS-host.de panel → Your datastore → Connect")" - echo "3. $(translate "Select 'rsync / borg / sftp' tab")" - echo "4. $(translate "Paste the key in 'Save SSH Key' section")" - echo "5. $(translate "Click 'Save'")" - echo "" - - - msg_success "$(translate "Press Enter when you have uploaded the key to PBS-host.de panel...")" - read -r - fi - - - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "Testing connection to PBS-host.de...")" - echo "" - - echo "$(translate "Connecting... (you may need to type 'yes' to accept the server fingerprint)")" - echo "" - - - if timeout 30 ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o BatchMode=no "$PBS_CUSTOMER_ID@$PBS_SERVER" borg --version >/dev/null 2>&1; then - echo "" - msg_ok "$(translate "✅ Connection successful! Borg is available on the server.")" - sleep 2 - else - echo "" - msg_error "$(translate "❌ Connection failed or requires manual intervention.")" - echo "" - echo -e "${BL}$(translate "This could be normal if:")${RESET}" - echo "• $(translate "First time connecting (need to accept fingerprint)")" - echo "• $(translate "Key not yet uploaded to PBS-host.de panel")" - echo "• $(translate "Network connectivity issues")" - echo "" - - dialog --backtitle "ProxMenux" --yesno "$(translate 'Do you want to continue anyway? The backup will test the connection again.')" 10 70 - if [[ $? -ne 0 ]]; then - return 1 - fi - fi - fi - - - local USE_ENCRYPTION=false - dialog --backtitle "ProxMenux" --yesno "$(translate 'Do you want to encrypt the backup?')" 8 60 - if [[ $? -eq 0 ]]; then - USE_ENCRYPTION=true - ENCRYPT_OPT="--encryption=repokey" - - - if [[ -f "$BORG_PASS_FILE" ]]; then - dialog --backtitle "ProxMenux" --yesno "$(translate 'A saved encryption passphrase exists. Use it?')" 8 60 - if [[ $? -eq 0 ]]; then - BORG_KEY=$(<"$BORG_PASS_FILE") - else - - while true; do - BORG_KEY=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Enter Borg encryption passphrase:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - BORG_KEY2=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Confirm encryption passphrase:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - if [[ "$BORG_KEY" == "$BORG_KEY2" ]]; then - break - else - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Passphrases do not match! Please try again.')" 8 50 - fi - done - - - mkdir -p "$(dirname "$BORG_PASS_FILE")" - echo "$BORG_KEY" > "$BORG_PASS_FILE" - chmod 600 "$BORG_PASS_FILE" - fi - else - - while true; do - BORG_KEY=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Enter Borg encryption passphrase:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - BORG_KEY2=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Confirm encryption passphrase:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - if [[ "$BORG_KEY" == "$BORG_KEY2" ]]; then - break - else - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Passphrases do not match! Please try again.')" 8 50 - fi - done - - - mkdir -p "$(dirname "$BORG_PASS_FILE")" - echo "$BORG_KEY" > "$BORG_PASS_FILE" - chmod 600 "$BORG_PASS_FILE" - fi - - export BORG_PASSPHRASE="$BORG_KEY" - else - ENCRYPT_OPT="--encryption=none" - fi - - - if [[ "$TYPE" == "local" || "$TYPE" == "usb" ]]; then - if [[ ! -f "$DEST/config" ]]; then - dialog --backtitle "ProxMenux" --infobox "$(translate 'Initializing Borg repository...')" 5 50 - "$BORG_APPIMAGE" init $ENCRYPT_OPT "$DEST" - if [[ $? -ne 0 ]]; then - clear - show_proxmenux_logo - msg_error "$(translate "Failed to initialize Borg repo at") $DEST" - sleep 5 - return 1 - fi - fi - elif [[ "$TYPE" == "remote" || "$TYPE" == "pbs" ]]; then - - if ! "$BORG_APPIMAGE" list "$DEST" >/dev/null 2>&1; then - dialog --backtitle "ProxMenux" --yesno "$(translate 'Repository does not exist or is not accessible. Initialize it?')" 8 60 - if [[ $? -eq 0 ]]; then - dialog --backtitle "ProxMenux" --infobox "$(translate 'Initializing Borg repository...')" 5 50 - "$BORG_APPIMAGE" init $ENCRYPT_OPT "$DEST" - if [[ $? -ne 0 ]]; then - clear - show_proxmenux_logo - msg_error "$(translate "Failed to initialize Borg repo at") $DEST" - sleep 5 - return 1 - fi - else - return 1 - fi - fi - fi - - - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Borg backup will start now. This may take a while.')" 8 40 - - - clear - show_proxmenux_logo - msg_info2 "$(translate "Starting backup with BorgBackup...")" - echo -e - - TOTAL_SIZE=$(du -cb "$@" | awk '/total$/ {print $1}') - TOTAL_SIZE_GB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE/1024/1024/1024}") - - echo -e "${BL}$(translate "Destination:")${WHITE} $DEST${RESET}" - echo -e "${BL}$(translate "Encryption:")${WHITE} $($USE_ENCRYPTION && echo "Enabled" || echo "Disabled")${RESET}" - echo -e "${BL}$(translate "Included directories:")${WHITE} $*${RESET}" - echo -e "${BL}$(translate "Total size:")${WHITE} ${TOTAL_SIZE_GB} GB${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}-------------------------------${RESET}" - - - local ARCHIVE_NAME="root-$(hostname)-$(date +%Y%m%d_%H%M)" - - "$BORG_APPIMAGE" create --progress --stats --compression zstd "$DEST"::$ARCHIVE_NAME "$@" 2>&1 | tee "$LOGFILE" - local BACKUP_RESULT=$? - - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" - - if [[ $BACKUP_RESULT -eq 0 ]]; then - msg_ok "$(translate "Backup completed successfully!")" - - - echo -e "\n${BL}$(translate "Backup information:")${RESET}" - "$BORG_APPIMAGE" info "$DEST"::$ARCHIVE_NAME 2>&1 | tee -a "$LOGFILE" - - - if [[ "$TYPE" == "remote" || "$TYPE" == "pbs" ]]; then - echo -e "\n${BL}$(translate "Tip:")${RESET} $(translate "Run 'borg compact $DEST' to free up space after multiple backups.")" - fi - else - msg_error "$(translate "Backup process failed with error code:") $BACKUP_RESULT" - fi - - echo - msg_success "$(translate "Press Enter to return to the main menu...")" - read -r -} -# =============================== - - - - -# ========== LOCAL TAR ========== -backup_to_local_tar() { -# local SRC="$1" - local TYPE - local DEST - local LOGFILE="/tmp/tar-backup.log" - - -if ! command -v pv &>/dev/null; then - apt-get update -qq && apt-get install -y pv >/dev/null 2>&1 -fi - - - - TYPE=$(dialog --backtitle "ProxMenux" --menu "$(translate 'Select backup destination:')" 15 60 2 \ - "local" "$(translate 'Local directory')" \ - "usb" "$(translate 'Internal/External dedicated disk')" \ - 3>&1 1>&2 2>&3) || return 1 - - if [[ "$TYPE" == "local" ]]; then - DEST=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter directory for backup:')" 10 60 "/backup" 3>&1 1>&2 2>&3) || return 1 - - mkdir -p "$DEST" - - -else - - -while true; do - DEST=$(get_external_backup_mount_point) - if [[ -z "$DEST" ]]; then - dialog --backtitle "ProxMenux" --yesno "No external disk detected or mounted. Would you like to retry?" 8 60 - [[ $? -eq 0 ]] && continue - return 1 - fi - - DISK_DEV=$(df "$DEST" | awk 'NR==2{print $1}') - PKNAME=$(lsblk -no PKNAME "$DISK_DEV" 2>/dev/null) - [[ -z "$PKNAME" ]] && PKNAME=$(basename "$DISK_DEV" | sed 's/[0-9]*$//') - if [[ -n "$PKNAME" && -b /dev/$PKNAME ]]; then - DISK_MODEL=$(lsblk -no MODEL "/dev/$PKNAME") - else - DISK_MODEL="(unknown)" - fi - FREE_SPACE=$(df -h "$DEST" | awk 'NR==2{print $4}') - - - - dialog --backtitle "ProxMenux" \ - --title "$(translate "Dedicated Backup Disk")" \ - --yesno "\n$(translate "Mount point:") $DEST\n\n\ - $(translate "Disk model:") $DISK_MODEL\n\ - $(translate "Available space:") $FREE_SPACE\n\n\ - $(translate "Use this disk for backup?")" 12 70 - - - if [[ $? -eq 0 ]]; then - mkdir -p "$DEST" - break - else - return 1 - fi -done - - - -fi - - -TAR_INPUT="" -TOTAL_SIZE=0 -for src in $SRC; do - sz=$(du -sb "$src" 2>/dev/null | awk '{print $1}') - TOTAL_SIZE=$((TOTAL_SIZE + sz)) - TAR_INPUT="$TAR_INPUT $src" -done - -local FILENAME="root-$(hostname)-$(date +%Y%m%d_%H%M).tar.gz" -clear -show_proxmenux_logo -msg_info2 "$(translate "Starting backup with tar...")" -echo -e - - -TOTAL_SIZE=$(du -cb "$@" | awk '/total$/ {print $1}') -TOTAL_SIZE_GB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE/1024/1024/1024}") - -echo -e "${BL}$(translate "Included directories:")${WHITE} $*${RESET}" -echo -e "${BL}$(translate "Total size:")${WHITE} ${TOTAL_SIZE_GB} GB${RESET}" - -tar -cf - "$@" 2> >(grep -v "Removing leading \`/'" >&2) \ -| pv -s "$TOTAL_SIZE" \ -| gzip > "$DEST/$FILENAME" - - -echo -ne "\033[1A\r\033[K" - -echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" -msg_ok "$(translate "Backup process finished. Review log above or in /tmp/tar-backup.log")" -echo -msg_success "$(translate "Press Enter to return to the main menu...")" -read -r - -} -# =============================== - - -host_backup_menu diff --git a/scripts/backup_restore/backup_host4.sh b/scripts/backup_restore/backup_host4.sh deleted file mode 100644 index d77ca608..00000000 --- a/scripts/backup_restore/backup_host4.sh +++ /dev/null @@ -1,1294 +0,0 @@ -#!/usr/bin/env bash - -# Configuration ============================================ -LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts" -BASE_DIR="/usr/local/share/proxmenux" -UTILS_FILE="$BASE_DIR/utils.sh" -VENV_PATH="/opt/googletrans-env" - -if [[ -f "$UTILS_FILE" ]]; then - source "$UTILS_FILE" -fi -load_language -initialize_cache -# ========================================================== - - -get_external_backup_mount_point() { - local BACKUP_MOUNT_FILE="/usr/local/share/proxmenux/last_backup_mount.txt" - local STORAGE_REPO="$LOCAL_SCRIPTS/backup_restore" - local MOUNT_POINT - - if [[ -f "$BACKUP_MOUNT_FILE" ]]; then - MOUNT_POINT=$(head -n1 "$BACKUP_MOUNT_FILE" | tr -d '\r\n' | xargs) - >&2 echo "DEBUG: Valor MOUNT_POINT='$MOUNT_POINT'" - if [[ ! -d "$MOUNT_POINT" ]]; then - msg_error "Mount point does not exist: $MOUNT_POINT" - rm -f "$BACKUP_MOUNT_FILE" - return 1 - fi - if ! mountpoint -q "$MOUNT_POINT"; then - msg_error "Mount point is not mounted: $MOUNT_POINT" - rm -f "$BACKUP_MOUNT_FILE" - return 1 - fi - - echo "$MOUNT_POINT" - return 0 - else - source "$STORAGE_REPO/mount_disk_host_bk.sh" - MOUNT_POINT=$(mount_disk_host_bk) - [[ -z "$MOUNT_POINT" ]] && msg_error "$(translate "No disk mounted.")" && return 1 - echo "$MOUNT_POINT" - return 0 - fi -} - - - -# === Host Backup Main Menu === -host_backup_menu() { - while true; do - local CHOICE - CHOICE=$(dialog --backtitle "ProxMenux" \ - --title "$(translate 'Host Backup')" \ - --menu "\n$(translate 'Select backup option:')" 22 70 12 \ - "" "$(translate '--- FULL BACKUP ---')" \ - 1 "$(translate 'Full backup to Proxmox Backup Server (PBS)')" \ - 2 "$(translate 'Full backup with BorgBackup')" \ - 3 "$(translate 'Full backup to local .tar.gz')" \ - "" "$(translate '--- CUSTOM BACKUP ---')" \ - 4 "$(translate 'Custom backup to PBS')" \ - 5 "$(translate 'Custom backup with BorgBackup')" \ - 6 "$(translate 'Custom backup to local .tar.gz')" \ - 0 "$(translate 'Return')" \ - 3>&1 1>&2 2>&3) || return 0 - - case "$CHOICE" in - 1) backup_full_pbs_root ;; - 2) backup_with_borg "/boot/efi /etc/pve /etc/network /var/lib/pve-cluster /root /etc/ssh /home /usr/local/bin /etc/cron.d /etc/systemd/system /var/lib/vz" ;; - 3) backup_to_local_tar "/boot/efi /etc/pve /etc/network /var/lib/pve-cluster /root /etc/ssh /home /usr/local/bin /etc/cron.d /etc/systemd/system /var/lib/vz" ;; - 4) custom_backup_menu backup_to_pbs ;; - 5) custom_backup_menu backup_with_borg ;; - 6) custom_backup_menu backup_to_local_tar ;; - 0) break ;; - esac - done -} - - - -# === Menu checklist for custom backup === -custom_backup_menu() { - declare -A BACKUP_PATHS=( - [etc-pve]="/etc/pve" - [etc-network]="/etc/network" - [var-lib-pve-cluster]="/var/lib/pve-cluster" - [root-dir]="/root" - [etc-ssh]="/etc/ssh" - [home]="/home" - [local-bin]="/usr/local/bin" - [cron]="/etc/cron.d" - [custom-systemd]="/etc/systemd/system" - [var-lib-vz]="/var/lib/vz" - ) - local CHECKLIST_OPTIONS=() - for KEY in "${!BACKUP_PATHS[@]}"; do - DIR="${BACKUP_PATHS[$KEY]}" - CHECKLIST_OPTIONS+=("$KEY" "$DIR" "off") - done - - SELECTED_KEYS=$(dialog --separate-output --checklist \ - "$(translate 'Select directories to backup:')" 22 70 12 \ - "${CHECKLIST_OPTIONS[@]}" \ - 3>&1 1>&2 2>&3) || return 1 - - local BACKUP_DIRS=() - for KEY in $SELECTED_KEYS; do - BACKUP_DIRS+=("${BACKUP_PATHS[$KEY]}") - done - - -# "$1" "${BACKUP_DIRS[*]}" - "$1" "${BACKUP_DIRS[@]}" - - -} - - - - - - - - - - - -configure_pbs_repository() { -local PBS_REPO_FILE="/usr/local/share/proxmenux/pbs-repo.conf" -local PBS_PASS_FILE="/usr/local/share/proxmenux/pbs-pass.txt" -local PBS_TOKEN_FILE="/usr/local/share/proxmenux/pbs-token.txt" -local PBS_MANUAL_CONFIGS="/usr/local/share/proxmenux/pbs-manual-configs.txt" - -[[ ! -f "$PBS_MANUAL_CONFIGS" ]] && touch "$PBS_MANUAL_CONFIGS" - -local PBS_CONFIGS=() -local PBS_SOURCES=() -local PBS_USERNAMES=() - -if [[ -f "/etc/pve/storage.cfg" ]]; then - local current_pbs="" server="" datastore="" username="" - - while IFS= read -r line; do - if [[ $line =~ ^pbs:\ (.+)$ ]]; then - if [[ -n "$current_pbs" && -n "$server" && -n "$datastore" && -n "$username" ]]; then - PBS_CONFIGS+=("$current_pbs|$username@$server:$datastore") - PBS_SOURCES+=("proxmox|$current_pbs") - PBS_USERNAMES+=("$username") - fi - current_pbs="${BASH_REMATCH[1]}" - server="" datastore="" username="" - elif [[ -n "$current_pbs" ]]; then - if [[ $line =~ ^[[:space:]]*server[[:space:]]+(.+)$ ]]; then - server="${BASH_REMATCH[1]}" - elif [[ $line =~ ^[[:space:]]*datastore[[:space:]]+(.+)$ ]]; then - datastore="${BASH_REMATCH[1]}" - elif [[ $line =~ ^[[:space:]]*username[[:space:]]+(.+)$ ]]; then - username="${BASH_REMATCH[1]}" - elif [[ $line =~ ^[a-zA-Z]+: ]]; then - if [[ -n "$server" && -n "$datastore" && -n "$username" ]]; then - PBS_CONFIGS+=("$current_pbs|$username@$server:$datastore") - PBS_SOURCES+=("proxmox|$current_pbs") - PBS_USERNAMES+=("$username") - fi - current_pbs="" - fi - fi - done < "/etc/pve/storage.cfg" - - if [[ -n "$current_pbs" && -n "$server" && -n "$datastore" && -n "$username" ]]; then - PBS_CONFIGS+=("$current_pbs|$username@$server:$datastore") - PBS_SOURCES+=("proxmox|$current_pbs") - PBS_USERNAMES+=("$username") - fi -fi - -if [[ -f "$PBS_MANUAL_CONFIGS" ]]; then - while IFS= read -r line; do - if [[ -n "$line" ]]; then - PBS_CONFIGS+=("$line") - local name="${line%%|*}" - PBS_SOURCES+=("manual|$name") - PBS_USERNAMES+=("") - fi - done < "$PBS_MANUAL_CONFIGS" -fi - -local menu_options=() -local i=1 - -for j in "${!PBS_CONFIGS[@]}"; do - local config="${PBS_CONFIGS[$j]}" - local source="${PBS_SOURCES[$j]}" - local name="${config%%|*}" - local repo="${config##*|}" - local source_type="${source%%|*}" - - if [[ "$source_type" == "proxmox" ]]; then - menu_options+=("$i" " $name ($repo) [Proxmox]") - else - menu_options+=("$i" " $name ($repo) [Manual]") - fi - ((i++)) -done - -menu_options+=("" "") -menu_options+=("$i" "\Z4\Zb $(translate 'Configure new PBS')\Zn") -local choice -choice=$(dialog --colors --backtitle "ProxMenux" --title "PBS Server Selection" \ ---menu "$(translate 'Select PBS server for this backup:')" 22 70 12 "${menu_options[@]}" 3>&1 1>&2 2>&3) || return 1 - -if [[ $choice -eq $i ]]; then - configure_pbs_manually || return 1 -else - local selected_config="${PBS_CONFIGS[$((choice-1))]}" - local selected_source="${PBS_SOURCES[$((choice-1))]}" - local selected_username="${PBS_USERNAMES[$((choice-1))]}" - local pbs_name="${selected_config%%|*}" - local source_type="${selected_source%%|*}" - PBS_REPO="${selected_config##*|}" - - { - mkdir -p "$(dirname "$PBS_REPO_FILE")" - echo "$PBS_REPO" > "$PBS_REPO_FILE" - } >/dev/null 2>&1 - - local password_found=false - if [[ "$source_type" == "proxmox" ]]; then - local password_file="/etc/pve/priv/storage/${pbs_name}.pw" - - if [[ -f "$password_file" ]]; then - local auth_content - auth_content=$(<"$password_file") - - - [[ -f "$PBS_PASS_FILE" ]] && rm "$PBS_PASS_FILE" - [[ -f "$PBS_TOKEN_FILE" ]] && rm "$PBS_TOKEN_FILE" - - - if [[ "$selected_username" == *"@pbs!"* ]]; then - - echo "$auth_content" > "$PBS_PASS_FILE" - chmod 600 "$PBS_PASS_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') PBS Cloud Token -$(translate 'Token ID:') $selected_username" 15 80 - elif [[ "$auth_content" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]]; then - local full_token="${selected_username}:${auth_content}" - echo "$full_token" > "$PBS_TOKEN_FILE" - chmod 600 "$PBS_TOKEN_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') API Token -$(translate 'Token ID:') $selected_username" 15 80 - elif [[ "$auth_content" == *":"* ]]; then - echo "$auth_content" > "$PBS_TOKEN_FILE" - chmod 600 "$PBS_TOKEN_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') API Token (Complete)" 12 80 - else - echo "$auth_content" > "$PBS_PASS_FILE" - chmod 600 "$PBS_PASS_FILE" - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using Proxmox PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Authentication:') Password" 12 80 - fi - fi - else - local manual_pass_file="/usr/local/share/proxmenux/pbs-pass-${pbs_name}.txt" - if [[ -f "$manual_pass_file" ]]; then - [[ -f "$PBS_TOKEN_FILE" ]] && rm "$PBS_TOKEN_FILE" - - { - cp "$manual_pass_file" "$PBS_PASS_FILE" - chmod 600 "$PBS_PASS_FILE" - } >/dev/null 2>&1 - password_found=true - dialog --backtitle "ProxMenux" --title "PBS Selected" --msgbox "$(translate 'Using manual PBS:') $pbs_name - -$(translate 'Repository:') $PBS_REPO -$(translate 'Password:') $(translate 'Previously saved')" 12 80 - fi - fi - - if ! $password_found; then - dialog --backtitle "ProxMenux" --title "Password Required" --msgbox "$(translate 'Password not found for:') $pbs_name -$(translate 'Please enter the password.')" 10 60 - get_pbs_password "$pbs_name" || return 1 - fi - - clear -fi -} - - - - - - - - - - -# ========== PBS BACKUP ========== -backup_full_pbs_root() { - local HOSTNAME PBS_REPO PBS_KEY_FILE PBS_PASS_FILE PBS_TOKEN_FILE PBS_ENCRYPTION_PASS_FILE ENCRYPT_OPT="" - HOSTNAME=$(hostname) - - - local PBS_REPO_FILE="/usr/local/share/proxmenux/pbs-repo.conf" - PBS_KEY_FILE="/usr/local/share/proxmenux/pbs-key.conf" - PBS_PASS_FILE="/usr/local/share/proxmenux/pbs-pass.txt" - PBS_TOKEN_FILE="/usr/local/share/proxmenux/pbs-token.txt" - PBS_ENCRYPTION_PASS_FILE="/usr/local/share/proxmenux/pbs-encryption-pass.txt" - LOGFILE="/tmp/pbs-backup-${HOSTNAME}.log" - - - configure_pbs_repository || return 1 - - if [[ ! -f "$PBS_REPO_FILE" ]]; then - msg_error "$(translate "Failed to configure PBS connection")" - sleep 3 - return 1 - fi - PBS_REPO=$(<"$PBS_REPO_FILE") - - - unset PBS_PASSWORD PBS_API_TOKEN PBS_API_TOKEN_ID PBS_API_TOKEN_SECRET PBS_FINGERPRINT - - - local AUTH_TYPE="password" - local PBS_AUTH_VALUE="" - - if [[ "$PBS_REPO" == *"@pbs!"* ]]; then - - AUTH_TYPE="pbs_cloud" - [[ -f "$PBS_PASS_FILE" ]] && PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - elif [[ -f "$PBS_TOKEN_FILE" ]]; then - - AUTH_TYPE="token" - PBS_AUTH_VALUE=$(<"$PBS_TOKEN_FILE") - elif [[ -f "$PBS_PASS_FILE" ]]; then - - AUTH_TYPE="password" - PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - else - msg_error "$(translate "No PBS authentication found!")" - sleep 3 - return 1 - fi - - - dialog --backtitle "ProxMenux" --title "Encryption" --yesno "$(translate 'Do you want to encrypt the backup?')" 8 60 - if [[ $? -eq 0 ]]; then - - if [[ ! -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - while true; do - PBS_KEY_PASS=$(dialog --backtitle "ProxMenux" --title "Encryption Password" --insecure --passwordbox "$(translate 'Enter encryption password (different from PBS login):')" 12 70 "" 3>&1 1>&2 2>&3) || return 1 - PBS_KEY_PASS2=$(dialog --backtitle "ProxMenux" --title "Encryption Password" --insecure --passwordbox "$(translate 'Confirm encryption password:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - if [[ "$PBS_KEY_PASS" == "$PBS_KEY_PASS2" ]]; then - break - else - dialog --backtitle "ProxMenux" --title "Error" --msgbox "$(translate 'Passwords do not match! Please try again.')" 8 50 - fi - done - - - { - echo "$PBS_KEY_PASS" > "$PBS_ENCRYPTION_PASS_FILE" - chmod 600 "$PBS_ENCRYPTION_PASS_FILE" - } >/dev/null 2>&1 - - dialog --backtitle "ProxMenux" --title "Success" --msgbox "$(translate 'Encryption password saved successfully!')" 8 50 - fi - - - if [[ ! -f "$PBS_KEY_FILE" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - - dialog --backtitle "ProxMenux" --title "Encryption" --infobox "$(translate 'Creating encryption key...')" 5 50 - - expect -c " - set timeout 30 - spawn proxmox-backup-client key create \"$PBS_KEY_FILE\" - expect { - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - \"Verify Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - eof - } - " >/dev/null 2>&1 - - if [[ ! -f "$PBS_KEY_FILE" ]]; then - dialog --backtitle "ProxMenux" --title "Error" --msgbox "$(translate 'Error creating encryption key.')" 8 40 - return 1 - fi - - dialog --backtitle "ProxMenux" --title "Important" --msgbox "$(translate 'IMPORTANT: Save the key file. Without it you will not be able to restore your backups!')\n\n$(translate 'Key file location:') $PBS_KEY_FILE" 12 70 - fi - ENCRYPT_OPT="--keyfile $PBS_KEY_FILE" - else - ENCRYPT_OPT="" - fi - - - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "Starting backup to PBS")" - echo -e - echo -e "${BL}$(translate "PBS Repository:")${WHITE} $PBS_REPO${RESET}" - echo -e "${BL}$(translate "Backup ID:")${WHITE} $HOSTNAME${RESET}" - echo -e "${BL}$(translate "Included:")${WHITE} /boot/efi /etc/pve (all root)${RESET}" - echo -e "${BL}$(translate "Encryption:")${WHITE} $([[ -n "$ENCRYPT_OPT" ]] && echo "Enabled" || echo "Disabled")${RESET}" - - - case "$AUTH_TYPE" in - "pbs_cloud") echo -e "${BL}$(translate "Authentication:")${WHITE} PBS Cloud Token${RESET}" ;; - "token") echo -e "${BL}$(translate "Authentication:")${WHITE} API Token${RESET}" ;; - "password") echo -e "${BL}$(translate "Authentication:")${WHITE} Password${RESET}" ;; - esac - - echo -e "${BL}$(translate "Log file:")${WHITE} $LOGFILE${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}-------------------------------${RESET}" - echo "" - - - local backup_cmd="proxmox-backup-client backup --include-dev /boot/efi --include-dev /etc/pve root-${HOSTNAME}.pxar:/ --repository \"$PBS_REPO\" $ENCRYPT_OPT --backup-type host --backup-id \"$HOSTNAME\" --backup-time \"$(date +%s)\"" - local backup_result=0 - - case "$AUTH_TYPE" in - "pbs_cloud") - - if [[ -n "$ENCRYPT_OPT" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - echo "$(translate "Starting encrypted full backup with PBS Cloud...")" - echo "" - - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - else - - echo "$(translate "Starting unencrypted full backup with PBS Cloud...")" - echo "" - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" bash -c "$backup_cmd" | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - fi - ;; - - "token") - - if [[ -n "$ENCRYPT_OPT" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - echo "$(translate "Starting encrypted full backup with API Token...")" - echo "" - - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - else - echo "$(translate "Starting unencrypted full backup with API Token...")" - echo "" - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" bash -c "$backup_cmd" | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - fi - ;; - - "password") - - if [[ -n "$ENCRYPT_OPT" ]]; then - PBS_ENCRYPTION_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - echo "$(translate "Starting encrypted full backup with password...")" - echo "" - - env -i expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - \"Encryption Key Password:\" { - send \"$PBS_ENCRYPTION_PASS\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - else - - echo "$(translate "Starting unencrypted full backup with password...")" - echo "" - - env -i expect -c " - set timeout 3600 - log_file $LOGFILE - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " | tee -a "$LOGFILE" - backup_result=${PIPESTATUS[0]} - fi - ;; - esac - - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" - if [[ $backup_result -eq 0 ]]; then - msg_ok "$(translate "Full backup process completed successfully")" - else - msg_error "$(translate "Backup process finished with errors")" - fi - - echo "" - msg_success "$(translate "Press Enter to return to the main menu...")" - read -r -} - - - - - - - - - - - - - - -backup_to_pbs() { - local HOSTNAME TIMESTAMP SNAPSHOT - HOSTNAME=$(hostname) - TIMESTAMP=$(date +%Y-%m-%d_%H-%M) - SNAPSHOT="${HOSTNAME}-${TIMESTAMP}" - - local PBS_REPO_FILE="/usr/local/share/proxmenux/pbs-repo.conf" - local PBS_KEY_FILE="/usr/local/share/proxmenux/pbs-key.conf" - local PBS_PASS_FILE="/usr/local/share/proxmenux/pbs-pass.txt" - local PBS_TOKEN_FILE="/usr/local/share/proxmenux/pbs-token.txt" - local PBS_ENCRYPTION_PASS_FILE="/usr/local/share/proxmenux/pbs-encryption-pass.txt" - local PBS_REPO ENCRYPT_OPT USE_ENCRYPTION - local PBS_KEY_PASS PBS_REPO_PASS - - configure_pbs_repository || return 1 - PBS_REPO=$(<"$PBS_REPO_FILE") - - - unset PBS_PASSWORD PBS_API_TOKEN PBS_API_TOKEN_ID PBS_API_TOKEN_SECRET PBS_FINGERPRINT - - USE_ENCRYPTION=false - dialog --backtitle "ProxMenux" --yesno "$(translate 'Do you want to encrypt the backup?')" 8 60 - [[ $? -eq 0 ]] && USE_ENCRYPTION=true - - if $USE_ENCRYPTION && ! command -v expect >/dev/null 2>&1; then - apt-get update -qq >/dev/null 2>&1 - apt-get install -y expect >/dev/null 2>&1 - fi - - if [[ "$#" -lt 1 ]]; then - clear - show_proxmenux_logo - msg_error "$(translate "No directories specified for backup.")" - sleep 2 - return 1 - fi - - local TOTAL="$#" - local COUNT=1 - - - local AUTH_TYPE="password" - local PBS_AUTH_VALUE="" - - if [[ "$PBS_REPO" == *"@pbs!"* ]]; then - - AUTH_TYPE="pbs_cloud" - [[ -f "$PBS_PASS_FILE" ]] && PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - elif [[ -f "$PBS_TOKEN_FILE" ]]; then - - AUTH_TYPE="token" - PBS_AUTH_VALUE=$(<"$PBS_TOKEN_FILE") - elif [[ -f "$PBS_PASS_FILE" ]]; then - - AUTH_TYPE="password" - PBS_AUTH_VALUE=$(<"$PBS_PASS_FILE") - else - msg_error "$(translate "No PBS authentication found!")" - return 1 - fi - - for dir in "$@"; do - local SAFE_NAME SAFE_ID PXAR_NAME - SAFE_NAME=$(basename "$dir" | tr '.-/' '_') - PXAR_NAME="root-custom-${SAFE_NAME}-${SNAPSHOT}.pxar" - SAFE_ID="custom-${HOSTNAME}-${SAFE_NAME}" - - msg_info2 "$(translate "[$COUNT/$TOTAL] Backing up") $dir $(translate "as") $PXAR_NAME" - - ENCRYPT_OPT="" - - if $USE_ENCRYPTION; then - if [[ -f "$PBS_KEY_FILE" ]]; then - ENCRYPT_OPT="--keyfile $PBS_KEY_FILE" - else - while true; do - PBS_KEY_PASS=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Enter encryption password (different from PBS login):')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - PBS_KEY_PASS2=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Confirm encryption password:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - if [[ "$PBS_KEY_PASS" == "$PBS_KEY_PASS2" ]]; then - break - else - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Passwords do not match! Please try again.')" 8 50 - fi - done - - { - echo "$PBS_KEY_PASS" > "$PBS_ENCRYPTION_PASS_FILE" - chmod 600 "$PBS_ENCRYPTION_PASS_FILE" - } >/dev/null 2>&1 - - expect -c " - set timeout 30 - spawn proxmox-backup-client key create \"$PBS_KEY_FILE\" - expect { - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - \"Verify Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - eof - } - " >/dev/null 2>&1 - - if [[ ! -f "$PBS_KEY_FILE" ]]; then - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Error creating encryption key.')" 8 40 - return 1 - fi - ENCRYPT_OPT="--keyfile $PBS_KEY_FILE" - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Encryption key generated. Save it in a safe place!')" 10 60 - fi - fi - - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "Starting backup to PBS")" - TOTAL_SIZE=$(du -cb "$@" | awk '/total$/ {print $1}') - TOTAL_SIZE_GB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE/1024/1024/1024}") - echo -e - echo -e "${BL}$(translate "PBS Repository:")${WHITE} $PBS_REPO${RESET}" - echo -e "${BL}$(translate "Backup ID:")${WHITE} $HOSTNAME${RESET}" - echo -e "${BL}$(translate "Encryption:")${WHITE} $([[ -n "$ENCRYPT_OPT" ]] && echo "Enabled" || echo "Disabled")${RESET}" - - case "$AUTH_TYPE" in - "pbs_cloud") echo -e "${BL}$(translate "Authentication:")${WHITE} PBS Cloud Token${RESET}" ;; - "token") echo -e "${BL}$(translate "Authentication:")${WHITE} API Token${RESET}" ;; - "password") echo -e "${BL}$(translate "Authentication:")${WHITE} Password${RESET}" ;; - esac - - echo -e "${BL}$(translate "Included directories:")${WHITE} $*${RESET}" - echo -e "${BL}$(translate "Total size:")${WHITE} ${TOTAL_SIZE_GB} GB${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}-------------------------------${RESET}" - - - local backup_cmd="proxmox-backup-client backup \"${PXAR_NAME}:$dir\" --repository \"$PBS_REPO\" $ENCRYPT_OPT --backup-type host --backup-id \"$SAFE_ID\" --backup-time \"$(date +%s)\"" - - case "$AUTH_TYPE" in - "pbs_cloud") - - if $USE_ENCRYPTION && [[ -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - PBS_KEY_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" expect -c " - set timeout 300 - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - eof - } - " - else - env -i PBS_PASSWORD="$PBS_AUTH_VALUE" bash -c "$backup_cmd" - fi - ;; - - "token") - - if $USE_ENCRYPTION && [[ -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - PBS_KEY_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" expect -c " - set timeout 300 - spawn $backup_cmd - expect { - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - eof - } - " - else - env -i PBS_API_TOKEN="$PBS_AUTH_VALUE" bash -c "$backup_cmd" - fi - ;; - - "password") - - if $USE_ENCRYPTION && [[ -f "$PBS_ENCRYPTION_PASS_FILE" ]]; then - PBS_KEY_PASS=$(<"$PBS_ENCRYPTION_PASS_FILE") - env -i expect -c " - set timeout 300 - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - \"Encryption Key Password:\" { - send \"$PBS_KEY_PASS\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " - else - env -i expect -c " - set timeout 300 - spawn $backup_cmd - expect { - -re \"Password for .*:\" { - send \"$PBS_AUTH_VALUE\r\" - exp_continue - } - -re \"Are you sure you want to continue connecting.*\" { - send \"y\r\" - exp_continue - } - eof - } - " - fi - ;; - esac - - COUNT=$((COUNT+1)) - done - - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" - msg_ok "$(translate "Backup process finished.")" - echo "" - msg_success "$(translate "Press Enter to return to the main menu...")" - read -r -} -# =============================== - - - - - - - -# ========== BORGBACKUP ========== -backup_with_borg() { - local BORG_APPIMAGE="/usr/local/share/proxmenux/borg" - local LOGFILE="/tmp/borg-backup.log" - local BORG_PASS_FILE="/usr/local/share/proxmenux/borg-pass.txt" - local BORG_KEY_FILE="/usr/local/share/proxmenux/borg-key" - local DEST - local TYPE - local ENCRYPT_OPT="" - local BORG_KEY - - # Descargar BorgBackup si no existe - if [[ ! -x "$BORG_APPIMAGE" ]]; then - clear - show_proxmenux_logo - msg_info "$(translate "BorgBackup not found. Downloading AppImage...")" - mkdir -p /usr/local/share/proxmenux - wget -qO "$BORG_APPIMAGE" "https://github.com/borgbackup/borg/releases/download/1.2.8/borg-linux64" - chmod +x "$BORG_APPIMAGE" - msg_ok "$(translate "BorgBackup downloaded and ready.")" - fi - - # Seleccionar tipo de destino - TYPE=$(dialog --backtitle "ProxMenux" --menu "$(translate 'Select Borg backup destination:')" 15 60 4 \ - "local" "$(translate 'Local directory')" \ - "usb" "$(translate 'Internal/External dedicated disk')" \ - "remote" "$(translate 'Remote server (SSH)')" \ - "pbs" "$(translate 'PBS-host.de Cloud')" \ - 3>&1 1>&2 2>&3) || return 1 - - # Configurar destino según el tipo seleccionado - if [[ "$TYPE" == "local" ]]; then - DEST=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter local directory for backup:')" 10 60 "/backup/borgbackup" 3>&1 1>&2 2>&3) || return 1 - mkdir -p "$DEST" - - elif [[ "$TYPE" == "usb" ]]; then - while true; do - BASE_DEST=$(get_external_backup_mount_point) - if [[ -z "$BASE_DEST" ]]; then - dialog --backtitle "ProxMenux" --yesno "$(translate 'No external disk detected or mounted. Would you like to retry?')" 8 60 - [[ $? -eq 0 ]] && continue - return 1 - fi - - DEST="$BASE_DEST/borgbackup" - mkdir -p "$DEST" - - DISK_DEV=$(df "$BASE_DEST" | awk 'NR==2{print $1}') - PKNAME=$(lsblk -no PKNAME "$DISK_DEV" 2>/dev/null) - [[ -z "$PKNAME" ]] && PKNAME=$(basename "$DISK_DEV" | sed 's/[0-9]*$//') - if [[ -n "$PKNAME" && -b /dev/$PKNAME ]]; then - DISK_MODEL=$(lsblk -no MODEL "/dev/$PKNAME") - else - DISK_MODEL="(unknown)" - fi - FREE_SPACE=$(df -h "$BASE_DEST" | awk 'NR==2{print $4}') - - dialog --backtitle "ProxMenux" \ - --title "$(translate "Dedicated Backup Disk")" \ - --yesno "\n$(translate "Mount point:") $DEST\n\n\ - $(translate "Disk model:") $DISK_MODEL\n\ - $(translate "Available space:") $FREE_SPACE\n\n\ - $(translate "Use this disk for backup?")" 12 70 - - if [[ $? -eq 0 ]]; then - break - else - return 1 - fi - done - - elif [[ "$TYPE" == "remote" ]]; then - # Servidor SSH remoto genérico - REMOTE_USER=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter SSH user for remote:')" 10 60 "root" 3>&1 1>&2 2>&3) || return 1 - REMOTE_HOST=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter SSH host:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - REMOTE_PATH=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter remote path:')" 10 60 "/backup/borgbackup" 3>&1 1>&2 2>&3) || return 1 - - # Preguntar por clave SSH - dialog --backtitle "ProxMenux" --yesno "$(translate 'Do you want to use SSH key authentication?')" 8 60 - if [[ $? -eq 0 ]]; then - SSH_KEY=$(dialog --backtitle "ProxMenux" --fselect "$HOME/.ssh/" 10 60 3>&1 1>&2 2>&3) || return 1 - export BORG_RSH="ssh -i $SSH_KEY" - fi - - DEST="ssh://$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH" - - elif [[ "$TYPE" == "pbs" ]]; then - # ✅ PBS-host.de Cloud - CAMPOS LIMPIOS - PBS_CUSTOMER_ID=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter PBS customer ID (from Connect panel):')" 10 70 "" 3>&1 1>&2 2>&3) || return 1 - PBS_SERVER=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter PBS server (from Connect panel):')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - # ✅ AUTO-DETECTAR CLAVES SSH DISPONIBLES - local SSH_KEYS=() - local SSH_KEY_NAMES=() - - # Buscar claves SSH comunes - for key_file in ~/.ssh/id_rsa ~/.ssh/id_ed25519 ~/.ssh/pbs_host_key ~/.ssh/id_ecdsa; do - if [[ -f "$key_file" && -f "${key_file}.pub" ]]; then - SSH_KEYS+=("$key_file") - local key_type=$(head -1 "${key_file}.pub" | awk '{print $1}') - local key_comment=$(head -1 "${key_file}.pub" | awk '{print $3}') - SSH_KEY_NAMES+=("$(basename "$key_file") ($key_type) - $key_comment") - fi - done - - # Si no hay claves, ofrecer crear una - if [[ ${#SSH_KEYS[@]} -eq 0 ]]; then - dialog --backtitle "ProxMenux" --yesno "$(translate 'No SSH keys found. Do you want to create one now?')" 8 60 - if [[ $? -eq 0 ]]; then - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "Creating SSH key for PBS-host.de...")" - - # Crear clave SSH específica para PBS - ssh-keygen -t rsa -b 4096 -f ~/.ssh/pbs_host_key -N "" -C "pbs-backup-$(hostname)" - - if [[ $? -eq 0 ]]; then - SSH_KEY="$HOME/.ssh/pbs_host_key" - msg_ok "$(translate "SSH key created successfully!")" - else - msg_error "$(translate "Failed to create SSH key")" - return 1 - fi - else - return 1 - fi - elif [[ ${#SSH_KEYS[@]} -eq 1 ]]; then - # Solo una clave disponible, usarla automáticamente - SSH_KEY="${SSH_KEYS[0]}" - else - # Múltiples claves, permitir selección - local menu_options=() - for i in "${!SSH_KEYS[@]}"; do - menu_options+=("$i" "${SSH_KEY_NAMES[$i]}") - done - - local choice - choice=$(dialog --backtitle "ProxMenux" --menu "$(translate 'Select SSH key to use:')" 15 80 10 "${menu_options[@]}" 3>&1 1>&2 2>&3) || return 1 - SSH_KEY="${SSH_KEYS[$choice]}" - fi - - export BORG_RSH="ssh -i $SSH_KEY" - DEST="$PBS_CUSTOMER_ID@$PBS_SERVER:./borg" - - # ✅ MOSTRAR INFORMACIÓN Y CLAVE PÚBLICA AUTOMÁTICAMENTE - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "PBS-host.de Configuration")" - echo -e "${BL}$(translate "Server:")${WHITE} $PBS_SERVER${RESET}" - echo -e "${BL}$(translate "Customer ID:")${WHITE} $PBS_CUSTOMER_ID${RESET}" - echo -e "${BL}$(translate "SSH Key:")${WHITE} $SSH_KEY${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}-------------------------------${RESET}" - echo "" - - # Verificar permisos de la clave SSH - chmod 600 "$SSH_KEY" - - # ✅ MOSTRAR CLAVE PÚBLICA AUTOMÁTICAMENTE - local SSH_PUB_KEY="${SSH_KEY}.pub" - if [[ -f "$SSH_PUB_KEY" ]]; then - echo -e "${BL}$(translate "Your SSH Public Key:")${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}" - echo -e "${WHITE}$(cat "$SSH_PUB_KEY")${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}" - echo "" - - # ✅ OFRECER COPIAR AL PORTAPAPELES (si xclip está disponible) - if command -v xclip >/dev/null 2>&1; then - echo -e "${BL}$(translate "Copying to clipboard...")${RESET}" - cat "$SSH_PUB_KEY" | xclip -selection clipboard - msg_ok "$(translate "Public key copied to clipboard!")" - elif command -v pbcopy >/dev/null 2>&1; then - echo -e "${BL}$(translate "Copying to clipboard...")${RESET}" - cat "$SSH_PUB_KEY" | pbcopy - msg_ok "$(translate "Public key copied to clipboard!")" - fi - - echo "" - echo -e "${BL}$(translate "INSTRUCTIONS:")${RESET}" - echo "1. $(translate "Copy the key above (or use clipboard if available)")" - echo "2. $(translate "Go to PBS-host.de panel → Your datastore → Connect")" - echo "3. $(translate "Select 'rsync / borg / sftp' tab")" - echo "4. $(translate "Paste the key in 'Save SSH Key' section")" - echo "5. $(translate "Click 'Save'")" - echo "" - - # ✅ PAUSA PARA QUE EL USUARIO PUEDA COPIAR LA CLAVE - msg_success "$(translate "Press Enter when you have uploaded the key to PBS-host.de panel...")" - read -r - fi - - # ✅ VERIFICAR CONEXIÓN CON MEJOR FEEDBACK - clear - show_proxmenux_logo - echo -e - msg_info2 "$(translate "Testing connection to PBS-host.de...")" - echo "" - - echo "$(translate "Connecting... (you may need to type 'yes' to accept the server fingerprint)")" - echo "" - - # Test de conexión con timeout y mejor manejo - if timeout 30 ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o BatchMode=no "$PBS_CUSTOMER_ID@$PBS_SERVER" borg --version >/dev/null 2>&1; then - echo "" - msg_ok "$(translate "✅ Connection successful! Borg is available on the server.")" - sleep 2 - else - echo "" - msg_error "$(translate "❌ Connection failed or requires manual intervention.")" - echo "" - echo -e "${BL}$(translate "This could be normal if:")${RESET}" - echo "• $(translate "First time connecting (need to accept fingerprint)")" - echo "• $(translate "Key not yet uploaded to PBS-host.de panel")" - echo "• $(translate "Network connectivity issues")" - echo "" - - dialog --backtitle "ProxMenux" --yesno "$(translate 'Do you want to continue anyway? The backup will test the connection again.')" 10 70 - if [[ $? -ne 0 ]]; then - return 1 - fi - fi - fi - - # ✅ MANEJO DE ENCRIPTACIÓN CON CONTRASEÑA PERSISTENTE - local USE_ENCRYPTION=false - dialog --backtitle "ProxMenux" --yesno "$(translate 'Do you want to encrypt the backup?')" 8 60 - if [[ $? -eq 0 ]]; then - USE_ENCRYPTION=true - ENCRYPT_OPT="--encryption=repokey" - - # Verificar si ya existe una contraseña guardada - if [[ -f "$BORG_PASS_FILE" ]]; then - dialog --backtitle "ProxMenux" --yesno "$(translate 'A saved encryption passphrase exists. Use it?')" 8 60 - if [[ $? -eq 0 ]]; then - BORG_KEY=$(<"$BORG_PASS_FILE") - else - # Pedir nueva contraseña - while true; do - BORG_KEY=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Enter Borg encryption passphrase:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - BORG_KEY2=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Confirm encryption passphrase:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - if [[ "$BORG_KEY" == "$BORG_KEY2" ]]; then - break - else - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Passphrases do not match! Please try again.')" 8 50 - fi - done - - # Guardar la nueva contraseña - mkdir -p "$(dirname "$BORG_PASS_FILE")" - echo "$BORG_KEY" > "$BORG_PASS_FILE" - chmod 600 "$BORG_PASS_FILE" - fi - else - # No hay contraseña guardada, pedir una nueva - while true; do - BORG_KEY=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Enter Borg encryption passphrase:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - BORG_KEY2=$(dialog --backtitle "ProxMenux" --insecure --passwordbox "$(translate 'Confirm encryption passphrase:')" 10 60 "" 3>&1 1>&2 2>&3) || return 1 - - if [[ "$BORG_KEY" == "$BORG_KEY2" ]]; then - break - else - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Passphrases do not match! Please try again.')" 8 50 - fi - done - - # Guardar la contraseña - mkdir -p "$(dirname "$BORG_PASS_FILE")" - echo "$BORG_KEY" > "$BORG_PASS_FILE" - chmod 600 "$BORG_PASS_FILE" - fi - - export BORG_PASSPHRASE="$BORG_KEY" - else - ENCRYPT_OPT="--encryption=none" - fi - - # ✅ INICIALIZAR REPOSITORIO SI ES NECESARIO - if [[ "$TYPE" == "local" || "$TYPE" == "usb" ]]; then - if [[ ! -f "$DEST/config" ]]; then - dialog --backtitle "ProxMenux" --infobox "$(translate 'Initializing Borg repository...')" 5 50 - "$BORG_APPIMAGE" init $ENCRYPT_OPT "$DEST" - if [[ $? -ne 0 ]]; then - clear - show_proxmenux_logo - msg_error "$(translate "Failed to initialize Borg repo at") $DEST" - sleep 5 - return 1 - fi - fi - elif [[ "$TYPE" == "remote" || "$TYPE" == "pbs" ]]; then - # Verificar si el repositorio existe o necesita inicialización - if ! "$BORG_APPIMAGE" list "$DEST" >/dev/null 2>&1; then - dialog --backtitle "ProxMenux" --yesno "$(translate 'Repository does not exist or is not accessible. Initialize it?')" 8 60 - if [[ $? -eq 0 ]]; then - dialog --backtitle "ProxMenux" --infobox "$(translate 'Initializing Borg repository...')" 5 50 - "$BORG_APPIMAGE" init $ENCRYPT_OPT "$DEST" - if [[ $? -ne 0 ]]; then - clear - show_proxmenux_logo - msg_error "$(translate "Failed to initialize Borg repo at") $DEST" - sleep 5 - return 1 - fi - else - return 1 - fi - fi - fi - - # ✅ CONFIRMACIÓN ANTES DE INICIAR BACKUP - dialog --backtitle "ProxMenux" --msgbox "$(translate 'Borg backup will start now. This may take a while.')" 8 40 - - # ✅ MOSTRAR INFORMACIÓN DEL BACKUP - clear - show_proxmenux_logo - msg_info2 "$(translate "Starting backup with BorgBackup...")" - echo -e - - TOTAL_SIZE=$(du -cb "$@" | awk '/total$/ {print $1}') - TOTAL_SIZE_GB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE/1024/1024/1024}") - - echo -e "${BL}$(translate "Destination:")${WHITE} $DEST${RESET}" - echo -e "${BL}$(translate "Encryption:")${WHITE} $($USE_ENCRYPTION && echo "Enabled" || echo "Disabled")${RESET}" - echo -e "${BL}$(translate "Included directories:")${WHITE} $*${RESET}" - echo -e "${BL}$(translate "Total size:")${WHITE} ${TOTAL_SIZE_GB} GB${RESET}" - echo -e "${BOLD}${NEON_PURPLE_BLUE}-------------------------------${RESET}" - - # ✅ EJECUTAR EL BACKUP - local ARCHIVE_NAME="root-$(hostname)-$(date +%Y%m%d_%H%M)" - - "$BORG_APPIMAGE" create --progress --stats --compression zstd "$DEST"::$ARCHIVE_NAME "$@" 2>&1 | tee "$LOGFILE" - local BACKUP_RESULT=$? - - echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" - - if [[ $BACKUP_RESULT -eq 0 ]]; then - msg_ok "$(translate "Backup completed successfully!")" - - # Mostrar información del backup - echo -e "\n${BL}$(translate "Backup information:")${RESET}" - "$BORG_APPIMAGE" info "$DEST"::$ARCHIVE_NAME 2>&1 | tee -a "$LOGFILE" - - # Sugerir compactación para ahorrar espacio - if [[ "$TYPE" == "remote" || "$TYPE" == "pbs" ]]; then - echo -e "\n${BL}$(translate "Tip:")${RESET} $(translate "Run 'borg compact $DEST' to free up space after multiple backups.")" - fi - else - msg_error "$(translate "Backup process failed with error code:") $BACKUP_RESULT" - fi - - echo - msg_success "$(translate "Press Enter to return to the main menu...")" - read -r -} -# =============================== - - - - -# ========== LOCAL TAR ========== -backup_to_local_tar() { -# local SRC="$1" - local TYPE - local DEST - local LOGFILE="/tmp/tar-backup.log" - - -if ! command -v pv &>/dev/null; then - apt-get update -qq && apt-get install -y pv >/dev/null 2>&1 -fi - - - - TYPE=$(dialog --backtitle "ProxMenux" --menu "$(translate 'Select backup destination:')" 15 60 2 \ - "local" "$(translate 'Local directory')" \ - "usb" "$(translate 'Internal/External dedicated disk')" \ - 3>&1 1>&2 2>&3) || return 1 - - if [[ "$TYPE" == "local" ]]; then - DEST=$(dialog --backtitle "ProxMenux" --inputbox "$(translate 'Enter directory for backup:')" 10 60 "/backup" 3>&1 1>&2 2>&3) || return 1 - - mkdir -p "$DEST" - - -else - - -while true; do - DEST=$(get_external_backup_mount_point) - if [[ -z "$DEST" ]]; then - dialog --backtitle "ProxMenux" --yesno "No external disk detected or mounted. Would you like to retry?" 8 60 - [[ $? -eq 0 ]] && continue - return 1 - fi - - DISK_DEV=$(df "$DEST" | awk 'NR==2{print $1}') - PKNAME=$(lsblk -no PKNAME "$DISK_DEV" 2>/dev/null) - [[ -z "$PKNAME" ]] && PKNAME=$(basename "$DISK_DEV" | sed 's/[0-9]*$//') - if [[ -n "$PKNAME" && -b /dev/$PKNAME ]]; then - DISK_MODEL=$(lsblk -no MODEL "/dev/$PKNAME") - else - DISK_MODEL="(unknown)" - fi - FREE_SPACE=$(df -h "$DEST" | awk 'NR==2{print $4}') - - - - dialog --backtitle "ProxMenux" \ - --title "$(translate "Dedicated Backup Disk")" \ - --yesno "\n$(translate "Mount point:") $DEST\n\n\ - $(translate "Disk model:") $DISK_MODEL\n\ - $(translate "Available space:") $FREE_SPACE\n\n\ - $(translate "Use this disk for backup?")" 12 70 - - - if [[ $? -eq 0 ]]; then - mkdir -p "$DEST" - break - else - return 1 - fi -done - - - -fi - - -TAR_INPUT="" -TOTAL_SIZE=0 -for src in $SRC; do - sz=$(du -sb "$src" 2>/dev/null | awk '{print $1}') - TOTAL_SIZE=$((TOTAL_SIZE + sz)) - TAR_INPUT="$TAR_INPUT $src" -done - -local FILENAME="root-$(hostname)-$(date +%Y%m%d_%H%M).tar.gz" -clear -show_proxmenux_logo -msg_info2 "$(translate "Starting backup with tar...")" -echo -e - - -TOTAL_SIZE=$(du -cb "$@" | awk '/total$/ {print $1}') -TOTAL_SIZE_GB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE/1024/1024/1024}") - -echo -e "${BL}$(translate "Included directories:")${WHITE} $*${RESET}" -echo -e "${BL}$(translate "Total size:")${WHITE} ${TOTAL_SIZE_GB} GB${RESET}" - -tar -cf - "$@" 2> >(grep -v "Removing leading \`/'" >&2) \ -| pv -s "$TOTAL_SIZE" \ -| gzip > "$DEST/$FILENAME" - - -echo -ne "\033[1A\r\033[K" - -echo -e "${BOLD}${NEON_PURPLE_BLUE}===============================${RESET}\n" -msg_ok "$(translate "Backup process finished. Review log above or in /tmp/tar-backup.log")" -echo -msg_success "$(translate "Press Enter to return to the main menu...")" -read -r - -} -# =============================== - - -host_backup_menu diff --git a/scripts/backup_restore/mount_disk_host_bk.sh b/scripts/backup_restore/mount_disk_host_bk.sh deleted file mode 100644 index d195c4e0..00000000 --- a/scripts/backup_restore/mount_disk_host_bk.sh +++ /dev/null @@ -1,433 +0,0 @@ -#!/bin/bash - -# ========================================================== -# ProxMenu - Mount disk on Proxmox host for backups -# ========================================================== -# Author : MacRimi -# Copyright : (c) 2024 MacRimi -# License : GPL-3.0 -# Version : 1.3-dialog -# Last Updated: 13/12/2024 -# ========================================================== - -LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts" -BASE_DIR="/usr/local/share/proxmenux" -UTILS_FILE="$BASE_DIR/utils.sh" -VENV_PATH="/opt/googletrans-env" - -if [[ -f "$UTILS_FILE" ]]; then - source "$UTILS_FILE" -fi -load_language -initialize_cache - - -mount_disk_host_bk() { - - - -get_disk_info() { - local disk=$1 - MODEL=$(lsblk -dn -o MODEL "$disk" | xargs) - SIZE=$(lsblk -dn -o SIZE "$disk" | xargs) - echo "$MODEL" "$SIZE" -} - - -is_usb_disk() { - local disk=$1 - local disk_name=$(basename "$disk") - - - if readlink -f "/sys/block/$disk_name/device" 2>/dev/null | grep -q "usb"; then - return 0 - fi - - - if udevadm info --query=property --name="$disk" 2>/dev/null | grep -q "ID_BUS=usb"; then - return 0 - fi - - return 1 -} - - -is_system_disk() { - local disk=$1 - local disk_name=$(basename "$disk") - - - local system_mounts=$(df -h | grep -E '^\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/|/boot|/usr|/var|/home)$' | awk '{print $1}') - - - for mount_dev in $system_mounts; do - - local mount_disk="" - if [[ "$mount_dev" =~ ^/dev/mapper/ ]]; then - - local vg_name=$(lvs --noheadings -o vg_name "$mount_dev" 2>/dev/null | xargs) - if [[ -n "$vg_name" ]]; then - local pvs_list=$(pvs --noheadings -o pv_name -S vg_name="$vg_name" 2>/dev/null | xargs) - for pv in $pvs_list; do - if [[ -n "$pv" && -e "$pv" ]]; then - mount_disk=$(lsblk -no PKNAME "$pv" 2>/dev/null) - if [[ -n "$mount_disk" && "/dev/$mount_disk" == "$disk" ]]; then - return 0 - fi - fi - done - fi - elif [[ "$mount_dev" =~ ^/dev/[hsv]d[a-z][0-9]* || "$mount_dev" =~ ^/dev/nvme[0-9]+n[0-9]+p[0-9]+ ]]; then - - mount_disk=$(lsblk -no PKNAME "$mount_dev" 2>/dev/null) - if [[ -n "$mount_disk" && "/dev/$mount_disk" == "$disk" ]]; then - return 0 - fi - fi - done - - - local fs_type=$(lsblk -no FSTYPE "$disk" 2>/dev/null | head -1) - if [[ "$fs_type" == "btrfs" ]]; then - - local temp_mount=$(mktemp -d) - if mount -o ro "$disk" "$temp_mount" 2>/dev/null; then - - if btrfs subvolume list "$temp_mount" 2>/dev/null | grep -qE '(@|@home|@var|@boot|@root|root)'; then - umount "$temp_mount" 2>/dev/null - rmdir "$temp_mount" 2>/dev/null - return 0 - fi - umount "$temp_mount" 2>/dev/null - fi - rmdir "$temp_mount" 2>/dev/null - - - while read -r part; do - if [[ -n "$part" ]]; then - local part_fs=$(lsblk -no FSTYPE "/dev/$part" 2>/dev/null) - if [[ "$part_fs" == "btrfs" ]]; then - local mount_point=$(lsblk -no MOUNTPOINT "/dev/$part" 2>/dev/null) - if [[ "$mount_point" == "/" || "$mount_point" == "/boot" || "$mount_point" == "/home" || "$mount_point" == "/var" ]]; then - return 0 - fi - fi - fi - done < <(lsblk -ln -o NAME "$disk" | tail -n +2) - fi - - - local disk_uuid=$(blkid -s UUID -o value "$disk" 2>/dev/null) - local part_uuids=() - while read -r part; do - if [[ -n "$part" ]]; then - local uuid=$(blkid -s UUID -o value "/dev/$part" 2>/dev/null) - if [[ -n "$uuid" ]]; then - part_uuids+=("$uuid") - fi - fi - done < <(lsblk -ln -o NAME "$disk" | tail -n +2) - - - for uuid in "${part_uuids[@]}" "$disk_uuid"; do - if [[ -n "$uuid" ]] && grep -q "UUID=$uuid" /etc/fstab; then - local mount_point=$(grep "UUID=$uuid" /etc/fstab | awk '{print $2}') - if [[ "$mount_point" == "/" || "$mount_point" == "/boot" || "$mount_point" == "/home" || "$mount_point" == "/var" ]]; then - return 0 - fi - fi - done - - - if grep -q "$disk" /etc/fstab; then - local mount_point=$(grep "$disk" /etc/fstab | awk '{print $2}') - if [[ "$mount_point" == "/" || "$mount_point" == "/boot" || "$mount_point" == "/home" || "$mount_point" == "/var" ]]; then - return 0 - fi - fi - - - local disk_count=$(lsblk -dn -e 7,11 -o PATH | wc -l) - if [[ "$disk_count" -eq 1 ]]; then - return 0 - fi - - return 1 -} - - -USED_DISKS=$(lsblk -n -o PKNAME,TYPE | grep 'lvm' | awk '{print "/dev/" $1}') -MOUNTED_DISKS=$(lsblk -ln -o NAME,MOUNTPOINT | awk '$2!="" {print "/dev/" $1}') - -ZFS_DISKS="" -ZFS_RAW=$(zpool list -v -H 2>/dev/null | awk '{print $1}' | grep -v '^NAME$' | grep -v '^-' | grep -v '^mirror') - -for entry in $ZFS_RAW; do - path="" - if [[ "$entry" == wwn-* || "$entry" == ata-* ]]; then - if [ -e "/dev/disk/by-id/$entry" ]; then - path=$(readlink -f "/dev/disk/by-id/$entry") - fi - elif [[ "$entry" == /dev/* ]]; then - path="$entry" - fi - - if [ -n "$path" ]; then - base_disk=$(lsblk -no PKNAME "$path" 2>/dev/null) - if [ -n "$base_disk" ]; then - ZFS_DISKS+="/dev/$base_disk"$'\n' - fi - fi -done - -ZFS_DISKS=$(echo "$ZFS_DISKS" | sort -u) - -LVM_DEVICES=$( - pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | - while read -r dev; do - [[ -n "$dev" && -e "$dev" ]] && readlink -f "$dev" - done | sort -u -) - -FREE_DISKS=() - -while read -r DISK; do - [[ "$DISK" =~ /dev/zd ]] && continue - - INFO=($(get_disk_info "$DISK")) - MODEL="${INFO[@]::${#INFO[@]}-1}" - SIZE="${INFO[-1]}" - LABEL="" - SHOW_DISK=true - - IS_MOUNTED=false - IS_RAID=false - IS_ZFS=false - IS_LVM=false - IS_SYSTEM=false - IS_USB=false - - - if is_system_disk "$DISK"; then - IS_SYSTEM=true - fi - - - if is_usb_disk "$DISK"; then - IS_USB=true - fi - - while read -r part fstype; do - [[ "$fstype" == "zfs_member" ]] && IS_ZFS=true - [[ "$fstype" == "linux_raid_member" ]] && IS_RAID=true - [[ "$fstype" == "LVM2_member" ]] && IS_LVM=true - if grep -q "/dev/$part" <<< "$MOUNTED_DISKS"; then - IS_MOUNTED=true - fi - done < <(lsblk -ln -o NAME,FSTYPE "$DISK" | tail -n +2) - - REAL_PATH="" - if [[ -n "$DISK" && -e "$DISK" ]]; then - REAL_PATH=$(readlink -f "$DISK") - fi - if [[ -n "$REAL_PATH" ]] && echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then - IS_MOUNTED=true - fi - - USED_BY="" - REAL_PATH="" - if [[ -n "$DISK" && -e "$DISK" ]]; then - REAL_PATH=$(readlink -f "$DISK") - fi - CONFIG_DATA=$(grep -vE '^\s*#' /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null) - - if grep -Fq "$REAL_PATH" <<< "$CONFIG_DATA"; then - USED_BY="⚠ $(translate "In use")" - else - for SYMLINK in /dev/disk/by-id/*; do - [[ -e "$SYMLINK" ]] || continue - if [[ "$(readlink -f "$SYMLINK")" == "$REAL_PATH" ]]; then - if grep -Fq "$SYMLINK" <<< "$CONFIG_DATA"; then - USED_BY="⚠ $(translate "In use")" - break - fi - fi - done - fi - - if $IS_RAID && grep -q "$DISK" <<< "$(cat /proc/mdstat)"; then - if grep -q "active raid" /proc/mdstat; then - SHOW_DISK=false - fi - fi - if $IS_ZFS; then SHOW_DISK=false; fi - if $IS_MOUNTED; then SHOW_DISK=false; fi - if $IS_SYSTEM; then SHOW_DISK=false; fi - - if $SHOW_DISK; then - [[ -n "$USED_BY" ]] && LABEL+=" [$USED_BY]" - [[ "$IS_RAID" == true ]] && LABEL+=" ⚠ RAID" - [[ "$IS_LVM" == true ]] && LABEL+=" ⚠ LVM" - [[ "$IS_ZFS" == true ]] && LABEL+=" ⚠ ZFS" - - - if $IS_USB; then - LABEL+=" USB" - else - LABEL+=" $(translate "Internal")" - fi - - DESCRIPTION=$(printf "%-30s %10s%s" "$MODEL" "$SIZE" "$LABEL") - FREE_DISKS+=("$DISK" "$DESCRIPTION" "off") - fi -done < <(lsblk -dn -e 7,11 -o PATH) - -if [ "${#FREE_DISKS[@]}" -eq 0 ]; then - dialog --title "$(translate "Error")" --msgbox "$(translate "No available disks found on the host.")" 8 60 - - exit 1 -fi - - -# Building the array for dialog (format: tag item on/off tag item on/off...) -DLG_LIST=() -for ((i=0; i<${#FREE_DISKS[@]}; i+=3)); do - DLG_LIST+=("${FREE_DISKS[i]}" "${FREE_DISKS[i+1]}" "${FREE_DISKS[i+2]}") -done - -SELECTED=$(dialog --clear --backtitle "ProxMenux" --title "$(translate "Select Disk")" \ - --radiolist "\n$(translate "Select the disk you want to mount on the host:")" 20 90 10 \ - "${DLG_LIST[@]}" 2>&1 >/dev/tty) - -if [ -z "$SELECTED" ]; then - dialog --title "$(translate "Error")" --msgbox "$(translate "No disk was selected.")" 8 50 - - exit 1 -fi - - -# ------------------- Partitions and formatting ------------------------ - -PARTITION=$(lsblk -rno NAME "$SELECTED" | awk -v disk="$(basename "$SELECTED")" '$1 != disk {print $1; exit}') -SKIP_FORMAT=false -DEFAULT_MOUNT="/mnt/backup" - -if [ -n "$PARTITION" ]; then - PARTITION="/dev/$PARTITION" - CURRENT_FS=$(lsblk -no FSTYPE "$PARTITION" | xargs) - if [[ "$CURRENT_FS" == "ext4" || "$CURRENT_FS" == "xfs" || "$CURRENT_FS" == "btrfs" ]]; then - SKIP_FORMAT=true - - else - dialog --title "$(translate "Unsupported Filesystem")" --yesno \ - "$(translate "The partition") $PARTITION $(translate "has an unsupported filesystem ($CURRENT_FS).\nDo you want to format it?")" 10 70 - if [ $? -ne 0 ]; then exit 0; fi - fi -else - CURRENT_FS=$(lsblk -no FSTYPE "$SELECTED" | xargs) - if [[ "$CURRENT_FS" == "ext4" || "$CURRENT_FS" == "xfs" || "$CURRENT_FS" == "btrfs" ]]; then - SKIP_FORMAT=true - PARTITION="$SELECTED" - - else - dialog --title "$(translate "No Valid Partitions")" --yesno \ - "$(translate "The disk has no partitions and no valid filesystem. Do you want to create a new partition and format it?")" 10 70 - if [ $? -ne 0 ]; then exit 0; fi - - echo -e "$(translate "Creating partition table and partition...")" - parted -s "$SELECTED" mklabel gpt - parted -s "$SELECTED" mkpart primary 0% 100% - sleep 2 - partprobe "$SELECTED" - sleep 2 - - PARTITION=$(lsblk -rno NAME "$SELECTED" | awk -v disk="$(basename "$SELECTED")" '$1 != disk {print $1; exit}') - if [ -n "$PARTITION" ]; then - PARTITION="/dev/$PARTITION" - else - dialog --title "$(translate "Partition Error")" --msgbox \ - "$(translate "Failed to create partition on disk") $SELECTED." 8 70 - exit 1 - fi - fi -fi - -if [ "$SKIP_FORMAT" != true ]; then - FORMAT_TYPE=$(dialog --title "$(translate "Select Format Type")" --menu \ - "$(translate "Select the filesystem type for") $PARTITION:" 15 60 5 \ - "ext4" "$(translate "Extended Filesystem 4 (recommended)")" \ - "xfs" "XFS" \ - "btrfs" "Btrfs" 2>&1 >/dev/tty) - if [ -z "$FORMAT_TYPE" ]; then - dialog --title "$(translate "Format Cancelled")" --msgbox \ - "$(translate "Format operation cancelled. The disk will not be added.")" 8 60 - exit 0 - fi - - dialog --title "$(translate "WARNING")" --yesno \ - "$(translate "WARNING: This operation will FORMAT the disk") $PARTITION $(translate "with") $FORMAT_TYPE.\n\n$(translate "ALL DATA ON THIS DISK WILL BE PERMANENTLY LOST!")\n\n$(translate "Are you sure you want to continue")" 15 70 - if [ $? -ne 0 ]; then exit 0; fi - - echo -e "$(translate "Formatting partition") $PARTITION $(translate "with") $FORMAT_TYPE..." - case "$FORMAT_TYPE" in - "ext4") mkfs.ext4 -F "$PARTITION" ;; - "xfs") mkfs.xfs -f "$PARTITION" ;; - "btrfs") mkfs.btrfs -f "$PARTITION" ;; - esac - - if [ $? -ne 0 ]; then - cleanup - dialog --title "$(translate "Format Failed")" --msgbox \ - "$(translate "Failed to format partition") $PARTITION $(translate "with") $FORMAT_TYPE." 12 70 - exit 1 - else - - partprobe "$SELECTED" - sleep 2 - fi -fi - - -# ------------------- Mount point and permissions (modular, non-blocking) ------------------- - - - - MOUNT_POINT=$(dialog --clear --title "$(translate "Mount Point")" \ - --inputbox "$(translate "Enter the mount point for the disk (e.g., /mnt/backup):")" \ - 10 60 "$DEFAULT_MOUNT" 2>&1 >/dev/tty) - - if [ -z "$MOUNT_POINT" ]; then - >&2 echo "$(translate "No mount point was specified.")" - return 1 - fi - - mkdir -p "$MOUNT_POINT" - - UUID=$(blkid -s UUID -o value "$PARTITION") - FS_TYPE=$(lsblk -no FSTYPE "$PARTITION" | xargs) - FSTAB_ENTRY="UUID=$UUID $MOUNT_POINT $FS_TYPE defaults 0 0" - - if grep -q "UUID=$UUID" /etc/fstab; then - sed -i "s|^.*UUID=$UUID.*|$FSTAB_ENTRY|" /etc/fstab - else - echo "$FSTAB_ENTRY" >> /etc/fstab - fi - - mount "$MOUNT_POINT" 2> >(grep -v "systemd still uses") - if [ $? -eq 0 ]; then - if ! getent group sharedfiles >/dev/null; then - groupadd sharedfiles - fi - - chown root:sharedfiles "$MOUNT_POINT" - chmod 2775 "$MOUNT_POINT" - echo "$MOUNT_POINT" > /usr/local/share/proxmenux/last_backup_mount.txt - - MOUNT_POINT=$(echo "$MOUNT_POINT" | head -n1 | tr -d '\r\n\t ') - echo "$MOUNT_POINT" - else - >&2 echo "$(translate "Failed to mount the disk at") $MOUNT_POINT" - return 1 - fi - -}