#!/bin/sh HNAME=$(uci get system.@system[0].hostname) DATE=$(date +%Y-%m-%d_%H-%M) S_MOUNT="/tmp/share" S_ADDR="nfs.lan:/mnt/media1/backup" C_LIST=" /etc/config/ /etc/xray/ /etc/unbound/ /root/.ssh/ /opt/xray-config/ /opt/backup-manager/ " H_REPLACE="wpad dnsmasq" DIR="/opt/backup-manager" SCR_BACKUP="$DIR/backup-configs.sh" SCR_OPKG="$DIR/backup-opkg.sh" check_dependencies() { echo "--- Checking NFS dependencies ---" local missing="" local pkgs="kmod-fs-nfs kmod-fs-nfs-v3 kmod-fs-nfs-v4 nfs-utils" for pkg in $pkgs; do if ! opkg list-installed | grep -q "^$pkg "; then missing="$missing $pkg" fi done if [ -n "$missing" ]; then echo " [!] Missing packages:$missing" printf "Would you like to install them now? (y/n): " read ans if [ "$ans" = "y" ] || [ "$ans" = "Y" ]; then echo "Updating package lists..." opkg update >/dev/null 2>&1 for m in $missing; do printf " [+] Installing $m... " if opkg install "$m" >/dev/null 2>&1; then echo "OK" else echo "FAILED" return 1 fi done /sbin/depmod >/dev/null 2>&1 else echo "Error: Cannot proceed without NFS dependencies." return 1 fi fi echo " [+] All dependencies are satisfied." return 0 } mount_share() { mkdir -p "$S_MOUNT" mount -t nfs "$S_ADDR" "$S_MOUNT" -o nolock,tcp >/dev/null 2>&1 if ! grep -qs "$S_MOUNT" /proc/mounts; then echo "Error: Failed to mount NFS share ($S_ADDR)" return 1 fi return 0 } check_nfs_write() { echo "--- Testing NFS Write/Read access ---" mount_share || return 1 local test_file="$S_MOUNT/.nfs_test_$(date +%s)" if ! touch "$test_file" >/dev/null 2>&1; then echo " Error: Cannot write to NFS share (Read-only?)" umount "$S_MOUNT" return 1 fi if ! cat "$test_file" >/dev/null 2>&1; then echo " Error: Cannot read from NFS share" rm -f "$test_file" umount "$S_MOUNT" return 1 fi rm -f "$test_file" echo " [+] NFS Write/Read test passed." umount "$S_MOUNT" return 0 } clean_old_backups() { mount_share || return 1 echo "--- Cleaning up backups older than 30 days ---" find "$S_MOUNT" -maxdepth 1 -type f \( -name "*.tar.gz" -o -name "*.lst" \) -mtime +30 -exec rm -f {} \; echo "Cleanup complete." umount "$S_MOUNT" } select_remote_file() { local ext="$1" check_dependencies || exit 1 check_nfs_write || exit 1 mount_share || return 1 echo "--- Select $ext file from NFS ---" >&2 FILES=$(find "$S_MOUNT" -maxdepth 1 -name "*$ext" | sort -r) if [ -z "$FILES" ]; then echo "No $ext files found." >&2 umount "$S_MOUNT" return 1 fi i=1 for f in $FILES; do size=$(du -sh "$f" | awk '{print $1}') echo " [$i] $(basename "$f") ($size)" >&2 i=$((i+1)) done printf "Selection (0 to cancel): " >&2 read choice if [ -z "$choice" ] || [ "$choice" -eq 0 ] || [ "$choice" -ge "$i" ]; then echo "Cancelled." >&2 umount "$S_MOUNT" return 1 fi SELECTED=$(echo "$FILES" | sed -n "${choice}p") RET_PATH="/tmp/restore_target$ext" cp "$SELECTED" "$RET_PATH" umount "$S_MOUNT" SELECTED_PATH="$RET_PATH" } case "$1" in backup) echo "--- Remote Backup Starting ---" check_dependencies || exit 1 check_nfs_write || exit 1 mount_share || exit 1 REMOTE_BACKUP="$S_MOUNT/backup_${HNAME}_${DATE}.tar.gz" REMOTE_LIST="$S_MOUNT/packages_${HNAME}_${DATE}.lst" $SCR_BACKUP "$REMOTE_BACKUP" "$REMOTE_LIST" "$SCR_OPKG" "$C_LIST" "$H_REPLACE" sync umount "$S_MOUNT" echo "--- Backup Successfully Saved to NFS ---" clean_old_backups ;; restore-pkgs) SELECTED_PATH="" select_remote_file ".lst" if [ -n "$SELECTED_PATH" ] && [ -f "$SELECTED_PATH" ]; then echo "Restoring packages from $SELECTED_PATH..." $SCR_OPKG restore "$SELECTED_PATH" "$H_REPLACE" rm "$SELECTED_PATH" fi ;; restore-sys) SELECTED_PATH="" select_remote_file ".tar.gz" if [ -n "$SELECTED_PATH" ] && [ -f "$SELECTED_PATH" ]; then echo "Restoring system from $SELECTED_PATH..." sysupgrade -r "$SELECTED_PATH" echo "Restore complete. Reboot is recommended." fi ;; *) echo "Usage: $0 {backup|restore-pkgs|restore-sys}" exit 1 ;; esac