#!/bin/bash # ============================================= # Debian Distribution Upgrade Script # Upgrades Debian to the next stable release # ============================================= # Configuration LOG_FILE="/var/log/debian_upgrade.log" BACKUP_DIR="/root/debian_upgrade_backup_$(date +%Y%m%d_%H%M%S)" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[1;34m' NC='\033[0m' # No Color MAX_LOG_SIZE_KB=1024 # 1MB # Função de Log (modificada para usar logger) log() { local message="$1" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') # Escreve no ficheiro de log echo -e "$timestamp: $message" | tee -a "$LOG_FILE" # Envia para o syslog (opcional) logger -t "debian_upgrade" "$message" } # Função para limitar o tamanho do log limit_log_size() { local log_file="$1" local max_size_kb="$2" local max_size_bytes=$((max_size_kb * 1024)) if [ -f "$log_file" ]; then local current_size=$(stat -c %s "$log_file" 2>/dev/null || wc -c < "$log_file" 2>/dev/null) if [ "$current_size" -gt "$max_size_bytes" ]; then log "Aviso: Ficheiro de log $log_file excedeu $max_size_kb KB. A truncar..." tail -n 500 "$log_file" > "${log_file}.tmp" && mv "${log_file}.tmp" "$log_file" log "Ficheiro de log truncado. As últimas 500 linhas foram mantidas." fi fi } # Function to ask for confirmation ask_confirmation() { local message="$1" local default="$2" # "y" or "n" while true; do if [ "$default" = "y" ]; then read -p "$message [Y/n]: " choice < /dev/tty else read -p "$message [y/N]: " choice < /dev/tty fi # Default choice if user just presses Enter if [ -z "$choice" ]; then choice="$default" fi case "$choice" in y|Y|yes|Yes|YES) return 0 ;; n|N|no|No|NO) return 1 ;; *) echo "Please answer yes or no." ;; esac done } # Function to check if running on Raspberry Pi is_raspberry_pi() { if [ -f /proc/device-tree/model ] && grep -q "Raspberry Pi" /proc/device-tree/model; then return 0 else return 1 fi } # Function to validate if curl is installed check_curl() { if ! command -v curl >/dev/null 2>&1; then log "Installing curl..." if ! apt install -y curl; then log "${RED}Failed to install curl${NC}" return 1 fi fi return 0 } # Function to get current Debian version get_current_version() { CURRENT_VERSION=$(cat /etc/debian_version | cut -d'.' -f1) CURRENT_CODENAME=$(grep -oP '(?<=VERSION_CODENAME=).*' /etc/os-release) log "Current Debian version: $CURRENT_CODENAME (Debian $CURRENT_VERSION)" } # Function to get next Debian stable version get_next_version() { if ! check_curl; then return 1 fi NEXT_CODENAME=$(curl -s "http://ftp.debian.org/debian/dists/stable/Release" | grep -oP '(?<=Codename: ).*' | head -n 1) if [ -z "$NEXT_CODENAME" ]; then log "${RED}Failed to fetch next Debian version${NC}" return 1 fi NEXT_VERSION=$(curl -s "http://ftp.debian.org/debian/dists/${NEXT_CODENAME}/Release" | grep -oP '(?<=Version: ).*' | cut -d'.' -f1) log "Next Debian version available: $NEXT_CODENAME (Debian $NEXT_VERSION)" return 0 } # Function to backup important configurations backup_configs() { log "Creating backup of important configurations in $BACKUP_DIR..." mkdir -p "$BACKUP_DIR" || { log "${RED}Failed to create backup directory${NC}" return 1 } # Backup APT sources cp -a /etc/apt/sources.list "$BACKUP_DIR/" || { log "${RED}Failed to backup /etc/apt/sources.list${NC}" return 1 } cp -a /etc/apt/sources.list.d/ "$BACKUP_DIR/" 2>/dev/null || true # Backup installed packages dpkg --get-selections > "$BACKUP_DIR/installed_packages.txt" || { log "${RED}Failed to backup installed packages${NC}" return 1 } # Backup important system configs cp -a /etc/network/interfaces "$BACKUP_DIR/" 2>/dev/null || true cp -a /etc/resolv.conf "$BACKUP_DIR/" 2>/dev/null || true cp -a /etc/ssh/sshd_config "$BACKUP_DIR/" 2>/dev/null || true log "${GREEN}Backup completed successfully${NC}" return 0 } # Function to update APT repositories update_repositories() { log "Updating APT repositories to $NEXT_CODENAME..." sed -i "s/$CURRENT_CODENAME/$NEXT_CODENAME/g" /etc/apt/sources.list || { log "${RED}Failed to update /etc/apt/sources.list${NC}" return 1 } if [ -d /etc/apt/sources.list.d/ ]; then find /etc/apt/sources.list.d/ -type f -name "*.list" -exec sed -i "s/$CURRENT_CODENAME/$NEXT_CODENAME/g" {} \; || { log "${RED}Failed to update repositories in /etc/apt/sources.list.d/${NC}" return 1 } fi return 0 } # Function to perform the upgrade perform_upgrade() { log "Updating package lists..." if ! apt update; then log "${RED}Failed to update package lists${NC}" return 1 fi log "Performing full upgrade..." if ! apt full-upgrade -y; then log "${RED}Failed to perform full upgrade${NC}" return 1 fi log "Performing distribution upgrade to $NEXT_CODENAME..." if ! apt dist-upgrade -y; then log "${RED}Failed to perform distribution upgrade${NC}" return 1 fi return 0 } # Function to clean up after upgrade cleanup() { log "Cleaning up..." apt autoremove -y || log "${YELLOW}Warning: Failed to autoremove packages${NC}" apt clean || log "${YELLOW}Warning: Failed to clean APT cache${NC}" return 0 } # Function to verify upgrade verify_upgrade() { NEW_CODENAME=$(grep -oP '(?<=VERSION_CODENAME=).*' /etc/os-release) if [ "$NEW_CODENAME" != "$NEXT_CODENAME" ]; then log "${RED}Upgrade verification failed. Current codename is still $NEW_CODENAME${NC}" return 1 fi log "${GREEN}Successfully upgraded to $NEXT_CODENAME (Debian $NEXT_VERSION)${NC}" return 0 } # Main function main() { # Check if running as root if [ "$(id -u)" -ne 0 ]; then log "${RED}This script must be run as root. Use 'sudo'.${NC}" exit 1 fi mkdir -p "$(dirname "$LOG_FILE")" limit_log_size "$LOG_FILE" "$MAX_LOG_SIZE_KB" # Verifica o tamanho do log no início # Check if this is a Debian system if [ ! -f /etc/debian_version ]; then log "${RED}This script is only for Debian-based systems.${NC}" exit 1 fi # Check if running on Raspberry Pi if is_raspberry_pi; then log "${RED}WARNING: This is a Raspberry Pi device.${NC}" log "${RED}Upgrading the Debian distribution may break compatibility with Raspberry Pi OS.${NC}" if ! ask_confirmation "Are you sure you want to continue?" "n"; then log "${YELLOW}Aborting Debian distribution upgrade${NC}" exit 0 fi fi # Get current and next Debian versions get_current_version if ! get_next_version; then exit 1 fi # Check if upgrade is needed if [ "$CURRENT_CODENAME" == "$NEXT_CODENAME" ]; then log "No new Debian stable version available. Current version: $CURRENT_CODENAME" exit 0 fi # Ask for confirmation if ! ask_confirmation "Do you want to upgrade from $CURRENT_CODENAME to $NEXT_CODENAME?" "n"; then log "${YELLOW}Skipping Debian distribution upgrade${NC}" exit 0 fi # Backup configurations if ! backup_configs; then exit 1 fi # Update repositories if ! update_repositories; then exit 1 fi # Perform upgrade if ! perform_upgrade; then exit 1 fi # Clean up cleanup # Verify upgrade if ! verify_upgrade; then exit 1 fi log "${YELLOW}Please reboot the system to complete the upgrade${NC}" exit 0 } # Call main function main