From b51b792e27beb55f237bacccd2f39309c004ea88 Mon Sep 17 00:00:00 2001 From: pyrschtjag Date: Sun, 25 Jan 2026 12:40:04 +0000 Subject: [PATCH] update_0.1 --- backup-configs.sh | 33 +++++++++++++ backup-manager.sh | 122 ++++++++++++++++++++++++++++++++++++++++++++++ backup-opkg.sh | 97 ++++++++++++++++++++++++++++++++++++ 3 files changed, 252 insertions(+) create mode 100755 backup-configs.sh create mode 100755 backup-manager.sh create mode 100755 backup-opkg.sh diff --git a/backup-configs.sh b/backup-configs.sh new file mode 100755 index 0000000..a7326da --- /dev/null +++ b/backup-configs.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +OUT_FILE="$1" +LIST_FILE="$2" +OPKG_SCR="$3" +CONFIGS="$4" +H_REPLACE="$5" + +[ -z "$OUT_FILE" ] || [ -z "$LIST_FILE" ] || [ -z "$OPKG_SCR" ] || [ -z "$CONFIGS" ] || [ -z "H_REPLACE" ] && echo "Missing params!" && exit 1 + +TMP_DIR="/tmp/backup_stack" +rm -rf "$TMP_DIR" && mkdir -p "$TMP_DIR" +COUNT=0 + +echo "Writing package list..." +$OPKG_SCR write "$LIST_FILE" "$H_REPLACE" + +echo "Collecting files..." +for p in $CONFIGS; do + if [ -e "$p" ]; then + mkdir -p "$TMP_DIR$(dirname "$p")" + cp -a "$p" "$TMP_DIR$p" + echo " [+] $p" + COUNT=$((COUNT + 1)) + else + echo " [-] $p (not found)" + fi +done + +echo "Creating archive..." +tar -czf "$OUT_FILE" -C "$TMP_DIR" . +rm -rf "$TMP_DIR" +echo "Total $COUNT config paths saved to $OUT_FILE." diff --git a/backup-manager.sh b/backup-manager.sh new file mode 100755 index 0000000..934aca0 --- /dev/null +++ b/backup-manager.sh @@ -0,0 +1,122 @@ +#!/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" + +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 +} + +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" + 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 ---" + 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 diff --git a/backup-opkg.sh b/backup-opkg.sh new file mode 100755 index 0000000..8d70faa --- /dev/null +++ b/backup-opkg.sh @@ -0,0 +1,97 @@ +#!/bin/sh + +CMD="$1" +PKG_FILE="$2" +HOT_REPLACE="$3" + +[ -z "$CMD" ] || [ -z "$PKG_FILE" ] || [ -z "$HOT_REPLACE" ] && echo "Missing params!" && exit 1 + +TMP_DIR="/tmp/opkg_restore" + +fix_dns() { + local dns1="1.1.1.1" + local dns2="1.0.0.1" + + if ! grep -q "$dns1" /etc/resolv.conf; then + echo " [+] Patching DNS for restore..." + sed -i "1i nameserver $dns1" /etc/resolv.conf + sed -i "2i nameserver $dns2" /etc/resolv.conf + fi +} + +list_user_packages() { + awk -f - << 'EOF' +BEGIN { + status_file = "/usr/lib/opkg/status" + cmd="opkg info busybox | grep '^Installed-Time: '" + if ((cmd | getline line) > 0) { + FLASH_TIME = substr(line, 17) + } + close(cmd) + + while ((getline < status_file) > 0) { + if ($1 == "Package:") { pkg = $2 } + if ($1 == "Installed-Time:") { + installed_time = $2 + if (installed_time > FLASH_TIME) { + check_cmd = "opkg whatdepends " pkg " | wc -l" + check_cmd | getline whatdepends + close(check_cmd) + if (whatdepends == 3) print pkg + } + } + } +} +EOF +} + +case "$CMD" in + write) + mkdir -p "$(dirname "$PKG_FILE")" + echo "Scanning for user-installed packages..." + + > "$PKG_FILE" + + for pkg in $(list_user_packages); do + echo "$pkg" >> "$PKG_FILE" + echo " [+] $pkg" + done + + echo "Total $(wc -l < "$PKG_FILE") packages saved to $PKG_FILE" + ;; + restore) + [ ! -f "$PKG_FILE" ] && echo "File not found!" && exit 1 + + [ ! -f /etc/resolv.conf-backup ] && cp /etc/resolv.conf /etc/resolv.conf-backup + fix_dns + + echo "Updating package lists..." + opkg update >/dev/null 2>&1 + + echo "Phase 1: Removing conflicts ($HOT_REPLACE)..." + if [ -n "$HOT_REPLACE" ]; then + REMOVABLE=$(opkg list-installed | grep -E $(echo "$HOT_REPLACE" | tr ' ' '|') | awk '{print $1}') + for p in $REMOVABLE; do + opkg remove "$p" --force-depends >/dev/null 2>&1 + done + fi + + echo "Phase 2: Installing packages from list..." + for pkg in $(cat "$PKG_FILE"); do + if opkg list-installed | grep -q "^$pkg "; then + echo " [s] $pkg is already installed" + else + fix_dns + printf " [+] Installing $pkg... " + if opkg install "$pkg" >/dev/null 2>&1; then + echo "OK" + else + echo "FAILED" + fi + fi + done + + [ -f /etc/resolv.conf-backup ] && mv /etc/resolv.conf-backup /etc/resolv.conf + echo "Restore complete. Reboot is recommended." + ;; +esac