#!/bin/bash # Proxmox Update Script - Simplified version # System update for Proxmox only # ====================================================== # Global variables 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 # ========================================================== OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs )" if [ -z "$OS_CODENAME" ]; then OS_CODENAME=$(lsb_release -cs 2>/dev/null || echo "bookworm") fi # ====================================================== # Auxiliary functions # ====================================================== lvm_repair_check() { msg_info "$(translate "Checking and repairing old LVM PV headers (if needed)...")" pvs_output=$(LC_ALL=C pvs -v 2>&1 | grep "old PV header") if [ -z "$pvs_output" ]; then msg_ok "$(translate "No PVs with old headers found.")" return fi declare -A vg_map while read -r line; do pv=$(echo "$line" | grep -o '/dev/[^ ]*') vg=$(pvs -o vg_name --noheadings "$pv" | awk '{print $1}') if [ -n "$vg" ]; then vg_map["$vg"]=1 fi done <<< "$pvs_output" for vg in "${!vg_map[@]}"; do msg_warn "$(translate "Old PV header(s) found in VG $vg. Updating metadata...")" vgck --updatemetadata "$vg" vgchange -ay "$vg" if [ $? -ne 0 ]; then msg_warn "$(translate "Metadata update failed for VG $vg. Review manually.")" else msg_ok "$(translate "Metadata updated successfully for VG $vg")" fi done } cleanup_duplicate_repos() { local sources_file="/etc/apt/sources.list" local temp_file=$(mktemp) local cleaned_count=0 declare -A seen_repos while IFS= read -r line || [[ -n "$line" ]]; do if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then echo "$line" >> "$temp_file" continue fi if [[ "$line" =~ ^deb ]]; then read -r _ url dist components <<< "$line" local key="${url}_${dist}" if [[ -v "seen_repos[$key]" ]]; then echo "# $line" >> "$temp_file" cleaned_count=$((cleaned_count + 1)) else echo "$line" >> "$temp_file" seen_repos[$key]="$components" fi else echo "$line" >> "$temp_file" fi done < "$sources_file" mv "$temp_file" "$sources_file" chmod 644 "$sources_file" local pve_files=(/etc/apt/sources.list.d/*proxmox*.list /etc/apt/sources.list.d/*pve*.list) local pve_public_repo="/etc/apt/sources.list.d/pve-public-repo.list" local pve_public_repo_exists=false local pve_repo_count=0 if [ -f "$pve_public_repo" ] && grep -q "^deb.*pve-no-subscription" "$pve_public_repo"; then pve_public_repo_exists=true pve_repo_count=1 fi for file in "${pve_files[@]}"; do if [ -f "$file" ] && grep -q "^deb.*pve-no-subscription" "$file"; then if ! $pve_public_repo_exists && [[ "$file" == "$pve_public_repo" ]]; then sed -i 's/^# *deb/deb/' "$file" pve_public_repo_exists=true pve_repo_count=1 elif [[ "$file" != "$pve_public_repo" ]]; then sed -i 's/^deb/# deb/' "$file" cleaned_count=$((cleaned_count + 1)) fi fi done if [ $cleaned_count -gt 0 ]; then msg_ok "$(translate "Duplicate repositories cleaned: $cleaned_count")" fi apt update } apt_upgrade() { local start_time=$(date +%s) local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log" local changes_made=false clear show_proxmenux_logo echo -e msg_title "$(translate "Proxmox system update")" # Show detected OS codename msg_info "$(translate "Detected OS codename: $OS_CODENAME")" # ====================================================== # Basic checks # ====================================================== # Check minimum disk space local available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}') if [ "$available_space" -lt 1024 ]; then msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")" return 1 fi # Check connectivity if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then msg_error "$(translate "Cannot reach Proxmox repositories")" return 1 fi # ====================================================== # Proxmox repository configuration # ====================================================== # Disable enterprise Proxmox repository if [ -f /etc/apt/sources.list.d/pve-enterprise.list ] && grep -q "^deb" /etc/apt/sources.list.d/pve-enterprise.list; then msg_info "$(translate "Disabling enterprise Proxmox repository...")" sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/pve-enterprise.list msg_ok "$(translate "Enterprise Proxmox repository disabled")" changes_made=true fi # Disable enterprise Ceph repository if [ -f /etc/apt/sources.list.d/ceph.list ] && grep -q "^deb" /etc/apt/sources.list.d/ceph.list; then msg_info "$(translate "Disabling enterprise Ceph repository...")" sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/ceph.list msg_ok "$(translate "Enterprise Ceph repository disabled")" changes_made=true fi # Enable free public repository if [ ! -f /etc/apt/sources.list.d/pve-public-repo.list ] || ! grep -q "pve-no-subscription" /etc/apt/sources.list.d/pve-public-repo.list; then msg_info "$(translate "Enabling free public Proxmox repository...")" echo "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" > /etc/apt/sources.list.d/pve-public-repo.list msg_ok "$(translate "Free public Proxmox repository enabled")" changes_made=true fi # ====================================================== # Debian repository configuration # ====================================================== local sources_file="/etc/apt/sources.list" local debian_changes=false # Clean up malformed entries first if grep -q -E "(debian-security -security|debian main$|debian -updates)" "$sources_file"; then msg_info "$(translate "Cleaning malformed repository entries...")" # Remove malformed lines that cause 404 errors sed -i '/^deb.*debian-security -security/d' "$sources_file" sed -i '/^deb.*debian main$/d' "$sources_file" sed -i '/^deb.*debian -updates/d' "$sources_file" debian_changes=true fi # Replace old mirrors if grep -q "ftp.es.debian.org" "$sources_file"; then sed -i 's|ftp.es.debian.org|deb.debian.org|g' "$sources_file" debian_changes=true fi # Fix incomplete security repository line if grep -q "^deb http://security.debian.org ${OS_CODENAME}-security main contrib$" "$sources_file"; then sed -i "s|^deb http://security.debian.org ${OS_CODENAME}-security main contrib$|deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware|" "$sources_file" debian_changes=true fi local temp_sources=$(mktemp) grep -E '^[[:space:]]*#|^[[:space:]]*$' "$sources_file" > "$temp_sources" cat >> "$temp_sources" << EOF # Debian ${OS_CODENAME} repositories deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware deb http://deb.debian.org/debian ${OS_CODENAME}-updates main contrib non-free non-free-firmware deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware EOF if ! cmp -s "$sources_file" "$temp_sources"; then cp "$sources_file" "${sources_file}.backup-$(date +%Y%m%d-%H%M%S)" mv "$temp_sources" "$sources_file" debian_changes=true msg_ok "$(translate "Debian repositories updated")" else rm "$temp_sources" fi if [ ! -f /etc/apt/apt.conf.d/no-bookworm-firmware.conf ]; then echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > /etc/apt/apt.conf.d/no-bookworm-firmware.conf debian_changes=true fi if [ "$debian_changes" = true ]; then changes_made=true fi # ====================================================== # Clean duplicate repositories # ====================================================== cleanup_duplicate_repos # ====================================================== # System update # ====================================================== # Update package lists if [ "$changes_made" = true ]; then msg_info "$(translate "Updating package lists...")" else msg_info "$(translate "Checking for available updates...")" fi if apt-get update > "$log_file" 2>&1; then msg_ok "$(translate "Package lists updated")" else msg_error "$(translate "Failed to update package lists. Check log: $log_file")" echo "$(translate "Repository errors found:")" grep -E "Err:|E:" "$log_file" | head -5 return 1 fi # Remove conflicting packages local conflicting_packages=$(dpkg -l 2>/dev/null | grep -E "^ii.*(ntp|openntpd|systemd-timesyncd)" | awk '{print $2}') if [ -n "$conflicting_packages" ]; then msg_info "$(translate "Removing conflicting packages...")" DEBIAN_FRONTEND=noninteractive apt-get -y purge $conflicting_packages >> "$log_file" 2>&1 msg_ok "$(translate "Conflicting packages removed")" fi # Show update information local upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable") if [ "$upgradable" -gt 0 ]; then msg_info "$(translate "Found $upgradable packages to upgrade")" # Show with dialog if available if command -v whiptail >/dev/null 2>&1; then if whiptail --title "$(translate "Proxmox Update")" \ --yesno "$(translate "Found $upgradable packages to upgrade.\n\nProceed with system update?")" 10 60; then clear else msg_info "$(translate "Update cancelled by user")" return 0 fi fi else msg_success "$(translate "System is already up to date")" echo -e msg_success "$(translate "Press Enter to return to menu...")" read -r return 0 fi # Perform update msg_info "$(translate "Performing system upgrade...")" echo "$(translate "This process may take several minutes...")" # Update with logging if DEBIAN_FRONTEND=noninteractive apt-get -y \ -o Dpkg::Options::='--force-confdef' \ -o Dpkg::Options::='--force-confold' \ dist-upgrade >> "$log_file" 2>&1; then msg_ok "$(translate "System upgrade completed successfully")" else msg_error "$(translate "System upgrade failed. Check log: $log_file")" return 1 fi # Install essential Proxmox packages if missing local essential_packages=("zfsutils-linux" "proxmox-backup-restore-image" "chrony") local missing_packages=() for package in "${essential_packages[@]}"; do if ! dpkg -l 2>/dev/null | grep -q "^ii $package "; then missing_packages+=("$package") fi done if [ ${#missing_packages[@]} -gt 0 ]; then msg_info "$(translate "Installing essential Proxmox packages...")" DEBIAN_FRONTEND=noninteractive apt-get -y install "${missing_packages[@]}" >> "$log_file" 2>&1 msg_ok "$(translate "Essential Proxmox packages installed")" fi # Check LVM lvm_repair_check # ====================================================== # Final summary - BEFORE reboot logic # ====================================================== local end_time=$(date +%s) local duration=$((end_time - start_time)) local minutes=$((duration / 60)) local seconds=$((duration % 60)) echo "" echo "$(translate "=== UPDATE COMPLETED ===")" echo "$(translate "Duration"): ${minutes}m ${seconds}s" echo "$(translate "Log file"): $log_file" echo "$(translate "Packages upgraded"): $upgradable" echo "" msg_success "$(translate "Proxmox system update completed successfully")" # ====================================================== # Reboot logic - After summary # ====================================================== # Check if reboot is needed (kernel updates, system packages, etc.) NECESSARY_REBOOT=0 # Check for reboot-required file if [ -f /var/run/reboot-required ]; then NECESSARY_REBOOT=1 fi # Check if kernel was updated if grep -q "linux-image" "$log_file" 2>/dev/null; then NECESSARY_REBOOT=1 fi # For system updates, it's generally safer to reboot if [ "$upgradable" -gt 0 ]; then NECESSARY_REBOOT=1 fi if [[ "$NECESSARY_REBOOT" -eq 1 ]]; then if command -v whiptail >/dev/null 2>&1; then if whiptail --title "$(translate "Reboot Required")" \ --yesno "$(translate "Some changes require a reboot to take effect. Do you want to restart now?")" 10 60; then msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" apt-get -y autoremove >/dev/null 2>&1 apt-get -y autoclean >/dev/null 2>&1 msg_ok "$(translate "Cleanup finished")" msg_success "$(translate "Press Enter to continue...")" read -r msg_warn "$(translate "Rebooting the system...")" reboot else msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" apt-get -y autoremove >/dev/null 2>&1 apt-get -y autoclean >/dev/null 2>&1 msg_ok "$(translate "Cleanup finished")" msg_info2 "$(translate "You can reboot later manually.")" msg_success "$(translate "Press Enter to continue...")" read -r return 0 fi else # Fallback without whiptail echo "$(translate "Reboot now? (y/N): ")" read -r -t 30 response if [[ "$response" =~ ^[Yy]$ ]]; then msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" apt-get -y autoremove >/dev/null 2>&1 apt-get -y autoclean >/dev/null 2>&1 msg_ok "$(translate "Cleanup finished")" msg_warn "$(translate "Rebooting the system...")" sleep 3 reboot else msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" apt-get -y autoremove >/dev/null 2>&1 apt-get -y autoclean >/dev/null 2>&1 msg_ok "$(translate "Cleanup finished")" msg_info2 "$(translate "You can reboot later manually.")" return 0 fi fi else msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" apt-get -y autoremove >/dev/null 2>&1 apt-get -y autoclean >/dev/null 2>&1 msg_ok "$(translate "Cleanup finished")" msg_success "$(translate "All changes applied. No reboot required.")" msg_success "$(translate "Press Enter to return to menu...")" read -r fi return 0 } # Function to show available update information check_updates_available() { msg_info "$(translate "Checking for available updates...")" apt-get update >/dev/null 2>&1 local upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable") local security_updates=$(apt list --upgradable 2>/dev/null | grep -c "security") if [ "$upgradable" -gt 0 ]; then echo "$(translate "Updates available"): $upgradable" echo "$(translate "Security updates"): $security_updates" if command -v whiptail >/dev/null 2>&1; then whiptail --title "$(translate "Updates Available")" \ --msgbox "$(translate "Updates available: $upgradable\nSecurity updates: $security_updates\n\nUse the update option to proceed.")" 12 60 fi else msg_ok "$(translate "System is up to date")" fi } # Execute function based on parameter case "${1:-}" in "check") check_updates_available ;; "") apt_upgrade ;; *) echo "$(translate "Usage: $0 [check]")" echo "$(translate " check - Check for available updates")" echo "$(translate " (no args) - Perform full system update")" ;; esac