]> gitweb.hhaalo.de Git - simple-backup.git/commitdiff
move source files into subdir
authorBastian Dehn <hhaalo@arcor.de>
Sun, 17 May 2026 18:53:49 +0000 (20:53 +0200)
committerBastian Dehn <hhaalo@arcor.de>
Sun, 17 May 2026 18:53:49 +0000 (20:53 +0200)
Makefile
backup [deleted file]
backupmount [deleted file]
control [deleted file]
restore [deleted file]
src/backup [new file with mode: 0755]
src/backupmount [new file with mode: 0755]
src/control [new file with mode: 0644]
src/restore [new file with mode: 0755]

index 977ca6e50e23d1cb23406414a81e8474f33b9776..82d01265b5b50d87b561116d0757ca1c53c3aa00 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION=1.0.0
 PACKAGE=simple-backup
 PACKAGENAME=$(PACKAGE)-$(VERSION)$(EXTRA_VERSION)-1-x86_64
-SRCDIR=.
+SRCDIR=src
 BUILDDIR=build
 PREFIX=usr/local
 BINDIR=$(BUILDDIR)/$(PACKAGENAME)/$(PREFIX)/bin
diff --git a/backup b/backup
deleted file mode 100755 (executable)
index 6380481..0000000
--- a/backup
+++ /dev/null
@@ -1,352 +0,0 @@
-#!/bin/bash
-
-errlog()
-{
-       local lastexit=$?
-       local msg="$1"
-
-       echo "❌ ERROR: $msg"
-       echo "🛑 EXIT $lastexit"
-       return $lastexit
-}
-
-get_backup_type()
-{
-       local typ="none"
-
-       [ -f "$backup_path/day" ] && typ="day"
-       [ -f "$backup_path/week" ] && typ="week"
-       [ -f "$backup_path/month" ] && typ="month"
-
-       echo "$typ"
-}
-
-write_current_date()
-{
-       local backup_path="$1"
-       local current_date="$(date +%F)"
-       local typ=$(get_backup_type)
-
-       mkdir --parents "history"
-
-       echo "$current_date" > $backup_path/$typ
-       echo "$current_date" > "history/$typ"
-}
-
-rotate_history()
-{
-       local history_file="$1"
-
-       [ -f "$history_file" ] || return 0
-
-       history_lines=$(cat $history_file | wc -l)
-       echo "ℹ️  INFO: $history_file has $history_lines lines"
-       [ $history_lines -ge 100 ] \
-               && savelog -c 10 -l $history_file \
-               && echo "ℹ️  INFO: $history_file rotated"
-}
-
-write_history_entry()
-{
-       local msg="$1"
-       local current_date=$(date --iso-8601=seconds)
-       local typ=$(get_backup_type)
-
-       mkdir --parents "history"
-       rotate_history "history/history"
-
-       echo "ℹ️  INFO: $current_date $typ $msg"
-       echo "$current_date $typ $msg" >> "history/history"
-}
-
-get_backup_dir()
-{
-       local backup_path="$1"
-       local backup_name="$2"
-       local last_backup_number="$3"
-       local date=$(date +%F)
-
-       local backup_dir=
-       local number=
-       for number in $(seq --format "%02g" $last_backup_number 99); do
-               backup_dir="$date"_"$number"_"$backup_name"
-               [ -d "$backup_path/$backup_dir" ] || break;
-       done
-
-       echo $backup_dir
-}
-
-get_last_backup_dir_path()
-{
-       local backup_path="$1"
-       local backup_name="$2"
-
-       local last_backup_dir_path=$(find $backup_path \
-               -maxdepth 1 \
-               -type d \
-               -name "*_""$backup_name" \
-               | sort \
-               | tail --lines=1)
-
-       echo "$last_backup_dir_path"
-}
-
-print_line()
-{
-       local i=
-       for i in {1..80}; do
-               printf "#"
-       done
-
-       printf "\n"
-}
-
-backup_remote_pathes()
-{
-       local backup_path="$1"
-       local machine="$2"
-       local last_backup_dir_path="$3"
-       local hardlinkopt=
-       local remote_user=$(cat config.yaml \
-               | yq --raw-output ".machines.\"$machine\".remote_user")
-       [ -n "$remote_user" ] && [ $remote_user != "null" ] \
-               || errlog "config.yaml missing remote_user for $machine" \
-               || return $?
-       local host=$(cat config.yaml \
-               | yq --raw-output ".machines.\"$machine\".host")
-       [ -n "$host" ] && [ "$host" != "null" ] \
-               || errlog "config.yaml missing host for $machine" \
-               || return $?
-       local port=$(cat config.yaml \
-               | yq --raw-output ".machines.\"$machine\".port")
-       [ -n "$port" ] && [ "$port" != "null" ] \
-               || errlog "config.yaml missing port for $machine" \
-               || return $?
-       local pathes=$(cat config.yaml \
-               | yq --raw-output ".machines.\"$machine\".pathes[]")
-       [ -n "$pathes" ] \
-               || errlog "config.yaml missing pathes for $machine" \
-               || return $?
-
-       local YELLOW="\e[0;33m"
-       local LIGHTPURPLE="\e[1;35m"
-       local RESET="\e[0m"
-
-       local path=
-       for path in $pathes; do
-               print_line
-               printf "ℹ️  INFO: machine ${LIGHTPURPLE}%s${RESET}; " "$machine"
-               printf "path ${YELLOW}%s${RESET}\n\n" "$path"
-
-               [ -n "$last_backup_dir_path" ] \
-                       && hardlinkopt="--link-dest=$last_backup_dir_path/$machine"
-
-               rsync --archive \
-                       --verbose \
-                       --sparse \
-                       --acls \
-                       --hard-links \
-                       --relative \
-                       --human-readable \
-                       $hardlinkopt \
-                       --rsh "ssh -p $port" \
-                       $remote_user@$host:$path \
-                       $backup_path/$machine \
-                       || errlog "could not backup $machine with path $path" \
-                       || return $?
-
-               print_line
-       done
-}
-
-backup_remotes()
-{
-       local backup_path="$1"
-       local last_backup_dir_path="$2"
-       local hardlinkopt=
-       local machines=$(cat config.yaml | yq --raw-output '.machines | keys[]')
-       [ -n "$machines" ] \
-               || errlog "config.yaml no machines" \
-               || return $?
-
-       local machine=
-       for machine in $machines; do
-               echo "ℹ️  INFO: create directory $backup_path/$machine"
-               mkdir --parents $backup_path/$machine \
-                       || errlog "could not create directory $backup_path/$machine" \
-                       || return $?
-
-               backup_remote_pathes "$backup_path" \
-                       "$machine" \
-                       "$last_backup_dir_path" \
-                       || return $?
-       done
-}
-
-remove_backup_dir()
-{
-       local backup_path="$1"
-       local backup_name="$2"
-       local delete_count=$3
-
-       local backups=$(find $backup_path -maxdepth 1 -name "*$backup_name" \
-               | sort \
-               | head --lines $delete_count)
-
-       local backup=
-       for backup in $backups; do
-               echo "🗑️  DELETE: $backup"
-               rm --recursive $backup
-               write_history_entry "deleted ${backup##*/}"
-       done
-}
-
-cleanup_backups()
-{
-       local backup_path="$1"
-       local backup_name="$2"
-       local backup_count=$3
-
-       local backup_counted=$(ls --directory $backup_path/*/ \
-               | grep "_$backup_name" \
-               | sort \
-               | wc --lines)
-       local delete_count=$(( backup_counted - backup_count ))
-
-       print_line
-       echo
-       echo "ℹ️  INFO: backup count: $backup_counted"
-
-       if [ $backup_counted -gt $backup_count ]; then
-               echo "ℹ️  INFO: delete backup count: $delete_count"
-               echo
-               remove_backup_dir "$backup_path" "$backup_name" $delete_count
-       fi
-
-       echo
-       print_line
-}
-
-is_backup_old()
-{
-       local backup_typ="$1"
-       local backup_date="$2"
-       local backup_month=$(date --date="$backup_date" '+%Y-%m')
-       local now_month=$(date '+%Y-%m')
-       local diff=$(( ($(date --date="$backup_date" +%s) - $(date +%s)) / 86400 ))
-
-       case "$backup_typ" in
-       "day") [ $diff -ge 0 ] || return $? ;;
-       "week") [ $diff -ge -6 ] || return $? ;;
-       "month") [ "$backup_month" == "$now_month" ] || return $? ;;
-       *) return 0;;
-       esac
-}
-
-check_backup_date()
-{
-       local LIGHTRED="\e[1;31m"
-       local LIGHTGREEN="\e[1;32m"
-       local RESET="\e[0m"
-       local color=$LIGHTGREEN
-       local history_path="history"
-
-       print_line
-       echo
-       local typ=
-       for typ in day week month; do
-               if [ -f "$history_path/$typ" ]; then
-                       color=$LIGHTGREEN
-                       is_backup_old $typ $(cat $history_path/$typ) \
-                               || color=$LIGHTRED
-                       printf "${color}last backup %s " "$typ"
-                       printf "am %s${RESET}\n" "$(cat $history_path/$typ)"
-               else
-                       printf "${LIGHTRED}no backup %s " $typ
-                       printf "exists${RESET}\n"
-               fi
-       done
-       echo
-       print_line
-}
-
-main()
-{
-       local only_check="$1"
-
-       [ "$only_check" == "check" ] && check_backup_date && return 0
-
-       echo "⭐ START: backup"
-
-       command -v yq > /dev/null \
-               || errlog "command yq not found" \
-               || return $?
-       command -v rsync > /dev/null \
-               || errlog "command rsync not found" \
-               || return $?
-       command -v ssh > /dev/null \
-               || errlog "command ssh not found" \
-               || return $?
-       [ -f "config.yaml" ] \
-               || errlog "file config.yaml not found" \
-               || return $?
-
-       backup_path=$(cat config.yaml | yq --raw-output '.backup_path')
-       backup_name=$(cat config.yaml | yq --raw-output '.backup_name')
-       backup_count=$(cat config.yaml | yq --raw-output '.backup_count')
-
-       [ -n "$backup_path" ] && [ "$backup_path" != "null" ] \
-               || errlog "config.yaml missing backup_path" \
-               || return $?
-       [ -d "$backup_path" ] \
-               || errlog "directory $backup_path not exists" \
-               || return $?
-       echo "ℹ️  INFO: backup path $backup_path"
-       [ -n "$backup_name" ] && [ "$backup_name" != "null" ] \
-               || errlog "config.yaml missing backup_name" \
-               || return $?
-       echo "ℹ️  INFO: backup name $backup_name"
-       [ -n "$backup_count" ] && [ "$backup_count" != "null" ] \
-               || errlog "config.yaml missing backup_count" \
-               || return $?
-       echo "ℹ️  INFO: backup count $backup_count"
-
-       local last_backup_dir_path=$(get_last_backup_dir_path \
-               "$backup_path" \
-               "$backup_name")
-       echo "ℹ️  INFO: last backup dir path $last_backup_dir_path"
-
-       local today_date=$(date +%F)
-       local last_backup_number=1
-       [[ "$last_backup_dir_path" =~ "$today_date" ]] \
-               && last_backup_number=${last_backup_dir_path##*/} \
-               && last_backup_number=${last_backup_number:11:2}
-       local backup_dir=$(get_backup_dir \
-               "$backup_path" \
-               "$backup_name" \
-               "$last_backup_number")
-
-       echo "ℹ️  INFO: backup folder $backup_dir"
-       local backup_full_path=$backup_path/$backup_dir
-       echo "ℹ️  INFO: create folder $backup_full_path"
-
-       backup_remotes \
-               "$backup_full_path" \
-               "$last_backup_dir_path" \
-               || return $?
-       write_current_date "$backup_path" \
-               || return $?
-       write_history_entry "created $backup_dir" \
-               || return $?
-       cleanup_backups \
-               "$backup_path" \
-               "$backup_name" \
-               $backup_count \
-               || return $?
-       check_backup_date \
-               || return $?
-
-       echo "✅ SUCCESS: backup"
-}
-
-main $*
diff --git a/backupmount b/backupmount
deleted file mode 100755 (executable)
index 8bb65d7..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/bin/bash
-
-errlog()
-{
-       local lastexit=$?
-       local msg="$1"
-
-       echo "❌ ERROR: $msg"
-       echo "🛑 EXIT $lastexit"
-       return $lastexit
-}
-
-infolog()
-{
-       local lastexit=$?
-       local msg="$1"
-
-       echo "ℹ️  INFO: $msg"
-       return $lastexit
-}
-
-partition_mount()
-{
-       local media="$1"
-       local partition=$(cat config.yaml \
-               | yq --raw-output ".media.\"$media\".partition")
-       [ "$partition" != "null" ] \
-               || errlog "config.yaml partition for $media missing" \
-               || return $?
-       [ -b "$partition" ] \
-               || errlog "partition $partition not found" \
-               || return $?
-       local backup_path=$(cat config.yaml \
-               | yq --raw-output ".backup_path")
-       [ "$backup_path" != "null" ] \
-               || errlog "config.yaml backup path missing" \
-               || return $?
-
-       infolog "create backup path $backup_path"
-       mkdir --parents "$backup_path" \
-               || errlog "could not create directory $backup_path" \
-               || return $?
-       infolog "mount partition $partition to $backup_path"
-       mount "$partition" "$backup_path" \
-               || errlog "cloud not mount $partition to $backup_path" \
-               || return $?
-}
-
-device_mount()
-{
-       local media="$1"
-       local device="$2"
-
-       [ -b "$device" ] \
-               || infolog "block device $device for $media not found" \
-               || return $?
-       infolog "media $media with device $device found"
-       local key=$(cat "config.yaml" \
-               | yq --raw-output ".media.\"$media\".key")
-       [ "$key" != "null" ] \
-               || errlog "config.yaml key for $media missing" \
-               || return $?
-       [ -f "$key" ] \
-               || errlog "key $key not exists" \
-               || return $?
-       infolog "use key $key"
-       local volume=$(cat "config.yaml" \
-               | yq --raw-output ".backup_volume")
-       [ "$volume" != "null" ] \
-               || errlog "config.yaml volume for $media missing" \
-               || return $?
-
-       infolog "volume open $volume"
-       cryptsetup open --key-file "$key" \
-               "$device" \
-               "$volume" \
-               || errlog "could not open $device with $key" \
-               || return $?
-
-       partition_mount "$media" || return $?
-}
-
-mount_backup()
-{
-       local medias=$(cat config.yaml \
-               | yq --raw-output '.media | keys[]')
-
-       [ -n "$medias" ] && [ "$medias" != "null" ] \
-               || errlog "config.yaml missing media section" \
-               || return $?
-
-       local media=
-       local device=
-       for media in $medias; do
-               device=$(cat config.yaml \
-                       | yq --raw-output ".media.\"$media\".device")
-               [ "$device" != "null" ] \
-                       || errlog "missing path for $media" \
-                       || return $?
-               device_mount "$media" "$device" && return 0
-       done
-
-       errlog "no device was mounted"
-}
-
-umount_backup()
-{
-
-       local backup_path=$(cat config.yaml \
-               | yq --raw-output ".backup_path")
-       [ "$backup_path" != "null" ] \
-               || errlog "config.yaml backup path missing" \
-               || return $?
-
-       infolog "umount backup path $backup_path"
-       umount "$backup_path" \
-               || errlog "could not unmount $backup_path" \
-               || return $?
-
-       infolog "remove backup path directory $backup_path"
-       rmdir "$backup_path" \
-               || errlog "clound not remote $backup_path" \
-               || return $?
-
-       local volume=$(cat "config.yaml" \
-               | yq --raw-output ".backup_volume")
-       [ "$volume" != "null" ] \
-               || errlog "config.yaml volume for $media missing" \
-               || return $?
-
-       infolog "close volume $volume"
-       cryptsetup close "$volume" \
-               || errlog "could not close $volume" \
-               || return $?
-}
-
-main()
-{
-       local cmd="$1"
-
-       echo "⭐ START: backupmount"
-
-       command -v yq > /dev/null \
-               || errlog "command yq not found" \
-               || return $?
-       command -v cryptsetup > /dev/null \
-               || errlog "command cryptsetup not found" \
-               || return $?
-
-       [ -n "$cmd" ] \
-               || errlog "missing command" \
-               || return $?
-       [ -f "config.yaml" ] \
-               || errlog "file config.yaml not exists" \
-               || return $?
-
-       if [ "$cmd" == "mount" ]; then
-               mount_backup || return $?
-       elif [ "$cmd" == "umount" ]; then
-               umount_backup || return $?
-       else
-               errlog "unknown command" || return $?
-       fi
-
-       echo "✅ SUCCESS: backupmount"
-}
-
-main $*
diff --git a/control b/control
deleted file mode 100644 (file)
index 2449ffa..0000000
--- a/control
+++ /dev/null
@@ -1,8 +0,0 @@
-Package: simple-backup
-Version: $VERSION
-Section: admin
-Priority: optional
-Architecture: amd64
-Maintainer: Bastian Dehn <hhaalo@arcor.de>
-Description: backup bash script on rsync base
-Depends: yq (>= 3.4.3-2), rsync (>= 3.4.1+ds1-5+deb13u2), ssh (>= 1:10.0p1-7+deb13u4), cryptsetup (>= 2:2.7.5-2)
diff --git a/restore b/restore
deleted file mode 100755 (executable)
index 2020d40..0000000
--- a/restore
+++ /dev/null
@@ -1,184 +0,0 @@
-#!/bin/bash
-
-errlog()
-{
-       local lastexit=$?
-       local msg="$1"
-
-       echo "❌ ERROR: $msg"
-       echo "🛑 EXIT: $lastexit"
-       return $lastexit
-}
-
-useage()
-{
-       echo "$0 -d <date> -n <number> -r <host> -p <path> -k <key> -o"
-       echo
-       echo " -d    date from backup"
-       echo " -n    number of backup"
-       echo " -r    host <user>@<rechner>:<port>"
-       echo " -p    path"
-       echo " -o    restore origin (delete mode)"
-       echo
-}
-
-print_line()
-{
-       for i in {1..80}; do
-               printf "#"
-       done
-
-       printf "\n"
-}
-
-restore()
-{
-       local date="$1"
-       local number="$2"
-       local machine="$3"
-       local path="$4"
-       local origin="$5"
-
-       local backup_path=$(cat config.yaml \
-               | yq --raw-output ".backup_path")
-       local backup_name=$(cat config.yaml \
-               | yq --raw-output ".backup_name")
-       local machine=$(cat config.yaml \
-               | yq --raw-output \
-               ".machines | keys[] | select(. == \"$machine\")")
-       local remote_user=$(cat config.yaml \
-               | yq --raw-output ".machines.\"$machine\".remote_user")
-       local remote_machine=$(cat config.yaml \
-               | yq --raw-output ".machines.\"$machine\".host")
-       local remote_port=$(cat config.yaml \
-               | yq --raw-output ".machines.\"$machine\".port")
-
-       [ -d "$backup_path" ] \
-               || errlog "could not find directory $backup_path" \
-               || return $?
-       [ -n "$machine" ] \
-               || errlog "config.yaml missing machine" \
-               || return $?
-       [ "$backup_name" != "null" ] \
-               || errlog "config.yaml missing backup_name" \
-               || return $?
-       [ "$remote_user" != "null" ] \
-               || errlog "config.yaml missing remote_user" \
-               || return $?
-       [ "$remote_machine" != "null" ] \
-               || errlog "config.yaml missing host" \
-               || return $?
-       [ "$remote_port" != "null" ] \
-               || errlog "config.yaml missing port" \
-               || return $?
-
-       local backup_dir="$date"_"$number"_"$backup_name"
-
-       [ -d "$backup_path/$backup_dir" ] \
-               || errlog "could not find directory $backup_path/$backup_dir" \
-               || return $?
-
-       local restore_prefix_path="$backup_path/$backup_dir/$machine"
-       [ -d "$restore_prefix_path" ] \
-               || errlog "could not find directory $restore_prefix_path" \
-               || return $?
-
-       local restore_path="$restore_prefix_path$path"
-       local remote_path="$remote_user@$remote_machine:$path"
-
-       local mode="directory"
-       [[ "$path" =~ /$ ]] || mode="file"
-
-       [ -f "$restore_path" ] || [ -d "$restore_path" ] \
-               || errlog "could not find $restore_path" \
-               || return $?
-
-       echo "ℹ️  INFO: restore mode $mode"
-       echo
-
-       echo "ℹ️  INFO: restore path $restore_path"
-       echo "ℹ️  INFO: remote path $remote_path"
-       echo
-
-       [ "$origin" == "deletemode-on" ] \
-               && local deletemode="--delete" \
-               && echo "⚠️  WARN: delete mode on" && echo
-
-       local ask_restore="N"
-       read -p "❓ QUESTION: Do you want restore? [N/y] " ask_restore
-       local ask_restore=${ask_restore:-N}
-       local ask_restore=${ask_restore,,}
-       echo ${ask_restore,,}
-
-       [ "$ask_restore" == "y" ] \
-               || errlog "you want not do restore" \
-               || return $?
-
-       echo "ℹ️  INFO: restoring..."
-
-       rsync --archive \
-               --verbose \
-               --sparse \
-               --acls \
-               --hard-links \
-               --human-readable \
-               $deletemode \
-               --rsh "ssh -p $remote_port" \
-               $restore_path \
-               $remote_path \
-               || errlog "could not restore" \
-               || return $?
-
-       echo "ℹ️  INFO: restored"
-}
-
-main()
-{
-       local date=
-       local number=
-       local machine=
-       local path=
-       local key=
-       local origin="deletemode-off"
-
-       echo "⭐ START: restore"
-
-       while getopts "d:k:n:r:p:ho" optname; do
-               case "$optname" in
-               "d") date="$OPTARG" ;;
-               "n") number="$OPTARG" ;;
-               "r") machine="$OPTARG" ;;
-               "p") path="$OPTARG" ;;
-               "o") origin="deletemode-on" ;;
-               "h") useage && return 0 ;;
-               esac
-       done
-
-       command -v yq > /dev/null \
-               || errlog "command yq not found" \
-               || return $?
-       command -v rsync > /dev/null \
-               || errlog "command rsync not found" \
-               || return $?
-       command -v ssh > /dev/null \
-               || errlog "command ssh not found" \
-               || return $?
-
-       [ -f "config.yaml" ] \
-               || errlog "file config.yaml not found" \
-               || return $?
-       [ -n "$date" ] || errlog "missing date (-d)" || return $?
-       [ -n "$number" ] || errlog "missing number (-n)" || return $?
-       [[ "$number" =~ [[:digit:]]{2} ]] \
-               || errlog "number must two digits" \
-               || return $?
-       [ -n "$machine" ] || errlog "missing machine (-r)" || return $?
-       [ -n "$path" ] || errlog "missing path (-p)" || return $?
-
-       restore "$date" "$number" "$machine" "$path" "$origin" \
-               || return $?
-
-       echo "✅ SUCCESS: restore"
-}
-
-main $*
diff --git a/src/backup b/src/backup
new file mode 100755 (executable)
index 0000000..6380481
--- /dev/null
@@ -0,0 +1,352 @@
+#!/bin/bash
+
+errlog()
+{
+       local lastexit=$?
+       local msg="$1"
+
+       echo "❌ ERROR: $msg"
+       echo "🛑 EXIT $lastexit"
+       return $lastexit
+}
+
+get_backup_type()
+{
+       local typ="none"
+
+       [ -f "$backup_path/day" ] && typ="day"
+       [ -f "$backup_path/week" ] && typ="week"
+       [ -f "$backup_path/month" ] && typ="month"
+
+       echo "$typ"
+}
+
+write_current_date()
+{
+       local backup_path="$1"
+       local current_date="$(date +%F)"
+       local typ=$(get_backup_type)
+
+       mkdir --parents "history"
+
+       echo "$current_date" > $backup_path/$typ
+       echo "$current_date" > "history/$typ"
+}
+
+rotate_history()
+{
+       local history_file="$1"
+
+       [ -f "$history_file" ] || return 0
+
+       history_lines=$(cat $history_file | wc -l)
+       echo "ℹ️  INFO: $history_file has $history_lines lines"
+       [ $history_lines -ge 100 ] \
+               && savelog -c 10 -l $history_file \
+               && echo "ℹ️  INFO: $history_file rotated"
+}
+
+write_history_entry()
+{
+       local msg="$1"
+       local current_date=$(date --iso-8601=seconds)
+       local typ=$(get_backup_type)
+
+       mkdir --parents "history"
+       rotate_history "history/history"
+
+       echo "ℹ️  INFO: $current_date $typ $msg"
+       echo "$current_date $typ $msg" >> "history/history"
+}
+
+get_backup_dir()
+{
+       local backup_path="$1"
+       local backup_name="$2"
+       local last_backup_number="$3"
+       local date=$(date +%F)
+
+       local backup_dir=
+       local number=
+       for number in $(seq --format "%02g" $last_backup_number 99); do
+               backup_dir="$date"_"$number"_"$backup_name"
+               [ -d "$backup_path/$backup_dir" ] || break;
+       done
+
+       echo $backup_dir
+}
+
+get_last_backup_dir_path()
+{
+       local backup_path="$1"
+       local backup_name="$2"
+
+       local last_backup_dir_path=$(find $backup_path \
+               -maxdepth 1 \
+               -type d \
+               -name "*_""$backup_name" \
+               | sort \
+               | tail --lines=1)
+
+       echo "$last_backup_dir_path"
+}
+
+print_line()
+{
+       local i=
+       for i in {1..80}; do
+               printf "#"
+       done
+
+       printf "\n"
+}
+
+backup_remote_pathes()
+{
+       local backup_path="$1"
+       local machine="$2"
+       local last_backup_dir_path="$3"
+       local hardlinkopt=
+       local remote_user=$(cat config.yaml \
+               | yq --raw-output ".machines.\"$machine\".remote_user")
+       [ -n "$remote_user" ] && [ $remote_user != "null" ] \
+               || errlog "config.yaml missing remote_user for $machine" \
+               || return $?
+       local host=$(cat config.yaml \
+               | yq --raw-output ".machines.\"$machine\".host")
+       [ -n "$host" ] && [ "$host" != "null" ] \
+               || errlog "config.yaml missing host for $machine" \
+               || return $?
+       local port=$(cat config.yaml \
+               | yq --raw-output ".machines.\"$machine\".port")
+       [ -n "$port" ] && [ "$port" != "null" ] \
+               || errlog "config.yaml missing port for $machine" \
+               || return $?
+       local pathes=$(cat config.yaml \
+               | yq --raw-output ".machines.\"$machine\".pathes[]")
+       [ -n "$pathes" ] \
+               || errlog "config.yaml missing pathes for $machine" \
+               || return $?
+
+       local YELLOW="\e[0;33m"
+       local LIGHTPURPLE="\e[1;35m"
+       local RESET="\e[0m"
+
+       local path=
+       for path in $pathes; do
+               print_line
+               printf "ℹ️  INFO: machine ${LIGHTPURPLE}%s${RESET}; " "$machine"
+               printf "path ${YELLOW}%s${RESET}\n\n" "$path"
+
+               [ -n "$last_backup_dir_path" ] \
+                       && hardlinkopt="--link-dest=$last_backup_dir_path/$machine"
+
+               rsync --archive \
+                       --verbose \
+                       --sparse \
+                       --acls \
+                       --hard-links \
+                       --relative \
+                       --human-readable \
+                       $hardlinkopt \
+                       --rsh "ssh -p $port" \
+                       $remote_user@$host:$path \
+                       $backup_path/$machine \
+                       || errlog "could not backup $machine with path $path" \
+                       || return $?
+
+               print_line
+       done
+}
+
+backup_remotes()
+{
+       local backup_path="$1"
+       local last_backup_dir_path="$2"
+       local hardlinkopt=
+       local machines=$(cat config.yaml | yq --raw-output '.machines | keys[]')
+       [ -n "$machines" ] \
+               || errlog "config.yaml no machines" \
+               || return $?
+
+       local machine=
+       for machine in $machines; do
+               echo "ℹ️  INFO: create directory $backup_path/$machine"
+               mkdir --parents $backup_path/$machine \
+                       || errlog "could not create directory $backup_path/$machine" \
+                       || return $?
+
+               backup_remote_pathes "$backup_path" \
+                       "$machine" \
+                       "$last_backup_dir_path" \
+                       || return $?
+       done
+}
+
+remove_backup_dir()
+{
+       local backup_path="$1"
+       local backup_name="$2"
+       local delete_count=$3
+
+       local backups=$(find $backup_path -maxdepth 1 -name "*$backup_name" \
+               | sort \
+               | head --lines $delete_count)
+
+       local backup=
+       for backup in $backups; do
+               echo "🗑️  DELETE: $backup"
+               rm --recursive $backup
+               write_history_entry "deleted ${backup##*/}"
+       done
+}
+
+cleanup_backups()
+{
+       local backup_path="$1"
+       local backup_name="$2"
+       local backup_count=$3
+
+       local backup_counted=$(ls --directory $backup_path/*/ \
+               | grep "_$backup_name" \
+               | sort \
+               | wc --lines)
+       local delete_count=$(( backup_counted - backup_count ))
+
+       print_line
+       echo
+       echo "ℹ️  INFO: backup count: $backup_counted"
+
+       if [ $backup_counted -gt $backup_count ]; then
+               echo "ℹ️  INFO: delete backup count: $delete_count"
+               echo
+               remove_backup_dir "$backup_path" "$backup_name" $delete_count
+       fi
+
+       echo
+       print_line
+}
+
+is_backup_old()
+{
+       local backup_typ="$1"
+       local backup_date="$2"
+       local backup_month=$(date --date="$backup_date" '+%Y-%m')
+       local now_month=$(date '+%Y-%m')
+       local diff=$(( ($(date --date="$backup_date" +%s) - $(date +%s)) / 86400 ))
+
+       case "$backup_typ" in
+       "day") [ $diff -ge 0 ] || return $? ;;
+       "week") [ $diff -ge -6 ] || return $? ;;
+       "month") [ "$backup_month" == "$now_month" ] || return $? ;;
+       *) return 0;;
+       esac
+}
+
+check_backup_date()
+{
+       local LIGHTRED="\e[1;31m"
+       local LIGHTGREEN="\e[1;32m"
+       local RESET="\e[0m"
+       local color=$LIGHTGREEN
+       local history_path="history"
+
+       print_line
+       echo
+       local typ=
+       for typ in day week month; do
+               if [ -f "$history_path/$typ" ]; then
+                       color=$LIGHTGREEN
+                       is_backup_old $typ $(cat $history_path/$typ) \
+                               || color=$LIGHTRED
+                       printf "${color}last backup %s " "$typ"
+                       printf "am %s${RESET}\n" "$(cat $history_path/$typ)"
+               else
+                       printf "${LIGHTRED}no backup %s " $typ
+                       printf "exists${RESET}\n"
+               fi
+       done
+       echo
+       print_line
+}
+
+main()
+{
+       local only_check="$1"
+
+       [ "$only_check" == "check" ] && check_backup_date && return 0
+
+       echo "⭐ START: backup"
+
+       command -v yq > /dev/null \
+               || errlog "command yq not found" \
+               || return $?
+       command -v rsync > /dev/null \
+               || errlog "command rsync not found" \
+               || return $?
+       command -v ssh > /dev/null \
+               || errlog "command ssh not found" \
+               || return $?
+       [ -f "config.yaml" ] \
+               || errlog "file config.yaml not found" \
+               || return $?
+
+       backup_path=$(cat config.yaml | yq --raw-output '.backup_path')
+       backup_name=$(cat config.yaml | yq --raw-output '.backup_name')
+       backup_count=$(cat config.yaml | yq --raw-output '.backup_count')
+
+       [ -n "$backup_path" ] && [ "$backup_path" != "null" ] \
+               || errlog "config.yaml missing backup_path" \
+               || return $?
+       [ -d "$backup_path" ] \
+               || errlog "directory $backup_path not exists" \
+               || return $?
+       echo "ℹ️  INFO: backup path $backup_path"
+       [ -n "$backup_name" ] && [ "$backup_name" != "null" ] \
+               || errlog "config.yaml missing backup_name" \
+               || return $?
+       echo "ℹ️  INFO: backup name $backup_name"
+       [ -n "$backup_count" ] && [ "$backup_count" != "null" ] \
+               || errlog "config.yaml missing backup_count" \
+               || return $?
+       echo "ℹ️  INFO: backup count $backup_count"
+
+       local last_backup_dir_path=$(get_last_backup_dir_path \
+               "$backup_path" \
+               "$backup_name")
+       echo "ℹ️  INFO: last backup dir path $last_backup_dir_path"
+
+       local today_date=$(date +%F)
+       local last_backup_number=1
+       [[ "$last_backup_dir_path" =~ "$today_date" ]] \
+               && last_backup_number=${last_backup_dir_path##*/} \
+               && last_backup_number=${last_backup_number:11:2}
+       local backup_dir=$(get_backup_dir \
+               "$backup_path" \
+               "$backup_name" \
+               "$last_backup_number")
+
+       echo "ℹ️  INFO: backup folder $backup_dir"
+       local backup_full_path=$backup_path/$backup_dir
+       echo "ℹ️  INFO: create folder $backup_full_path"
+
+       backup_remotes \
+               "$backup_full_path" \
+               "$last_backup_dir_path" \
+               || return $?
+       write_current_date "$backup_path" \
+               || return $?
+       write_history_entry "created $backup_dir" \
+               || return $?
+       cleanup_backups \
+               "$backup_path" \
+               "$backup_name" \
+               $backup_count \
+               || return $?
+       check_backup_date \
+               || return $?
+
+       echo "✅ SUCCESS: backup"
+}
+
+main $*
diff --git a/src/backupmount b/src/backupmount
new file mode 100755 (executable)
index 0000000..8bb65d7
--- /dev/null
@@ -0,0 +1,168 @@
+#!/bin/bash
+
+errlog()
+{
+       local lastexit=$?
+       local msg="$1"
+
+       echo "❌ ERROR: $msg"
+       echo "🛑 EXIT $lastexit"
+       return $lastexit
+}
+
+infolog()
+{
+       local lastexit=$?
+       local msg="$1"
+
+       echo "ℹ️  INFO: $msg"
+       return $lastexit
+}
+
+partition_mount()
+{
+       local media="$1"
+       local partition=$(cat config.yaml \
+               | yq --raw-output ".media.\"$media\".partition")
+       [ "$partition" != "null" ] \
+               || errlog "config.yaml partition for $media missing" \
+               || return $?
+       [ -b "$partition" ] \
+               || errlog "partition $partition not found" \
+               || return $?
+       local backup_path=$(cat config.yaml \
+               | yq --raw-output ".backup_path")
+       [ "$backup_path" != "null" ] \
+               || errlog "config.yaml backup path missing" \
+               || return $?
+
+       infolog "create backup path $backup_path"
+       mkdir --parents "$backup_path" \
+               || errlog "could not create directory $backup_path" \
+               || return $?
+       infolog "mount partition $partition to $backup_path"
+       mount "$partition" "$backup_path" \
+               || errlog "cloud not mount $partition to $backup_path" \
+               || return $?
+}
+
+device_mount()
+{
+       local media="$1"
+       local device="$2"
+
+       [ -b "$device" ] \
+               || infolog "block device $device for $media not found" \
+               || return $?
+       infolog "media $media with device $device found"
+       local key=$(cat "config.yaml" \
+               | yq --raw-output ".media.\"$media\".key")
+       [ "$key" != "null" ] \
+               || errlog "config.yaml key for $media missing" \
+               || return $?
+       [ -f "$key" ] \
+               || errlog "key $key not exists" \
+               || return $?
+       infolog "use key $key"
+       local volume=$(cat "config.yaml" \
+               | yq --raw-output ".backup_volume")
+       [ "$volume" != "null" ] \
+               || errlog "config.yaml volume for $media missing" \
+               || return $?
+
+       infolog "volume open $volume"
+       cryptsetup open --key-file "$key" \
+               "$device" \
+               "$volume" \
+               || errlog "could not open $device with $key" \
+               || return $?
+
+       partition_mount "$media" || return $?
+}
+
+mount_backup()
+{
+       local medias=$(cat config.yaml \
+               | yq --raw-output '.media | keys[]')
+
+       [ -n "$medias" ] && [ "$medias" != "null" ] \
+               || errlog "config.yaml missing media section" \
+               || return $?
+
+       local media=
+       local device=
+       for media in $medias; do
+               device=$(cat config.yaml \
+                       | yq --raw-output ".media.\"$media\".device")
+               [ "$device" != "null" ] \
+                       || errlog "missing path for $media" \
+                       || return $?
+               device_mount "$media" "$device" && return 0
+       done
+
+       errlog "no device was mounted"
+}
+
+umount_backup()
+{
+
+       local backup_path=$(cat config.yaml \
+               | yq --raw-output ".backup_path")
+       [ "$backup_path" != "null" ] \
+               || errlog "config.yaml backup path missing" \
+               || return $?
+
+       infolog "umount backup path $backup_path"
+       umount "$backup_path" \
+               || errlog "could not unmount $backup_path" \
+               || return $?
+
+       infolog "remove backup path directory $backup_path"
+       rmdir "$backup_path" \
+               || errlog "clound not remote $backup_path" \
+               || return $?
+
+       local volume=$(cat "config.yaml" \
+               | yq --raw-output ".backup_volume")
+       [ "$volume" != "null" ] \
+               || errlog "config.yaml volume for $media missing" \
+               || return $?
+
+       infolog "close volume $volume"
+       cryptsetup close "$volume" \
+               || errlog "could not close $volume" \
+               || return $?
+}
+
+main()
+{
+       local cmd="$1"
+
+       echo "⭐ START: backupmount"
+
+       command -v yq > /dev/null \
+               || errlog "command yq not found" \
+               || return $?
+       command -v cryptsetup > /dev/null \
+               || errlog "command cryptsetup not found" \
+               || return $?
+
+       [ -n "$cmd" ] \
+               || errlog "missing command" \
+               || return $?
+       [ -f "config.yaml" ] \
+               || errlog "file config.yaml not exists" \
+               || return $?
+
+       if [ "$cmd" == "mount" ]; then
+               mount_backup || return $?
+       elif [ "$cmd" == "umount" ]; then
+               umount_backup || return $?
+       else
+               errlog "unknown command" || return $?
+       fi
+
+       echo "✅ SUCCESS: backupmount"
+}
+
+main $*
diff --git a/src/control b/src/control
new file mode 100644 (file)
index 0000000..2449ffa
--- /dev/null
@@ -0,0 +1,8 @@
+Package: simple-backup
+Version: $VERSION
+Section: admin
+Priority: optional
+Architecture: amd64
+Maintainer: Bastian Dehn <hhaalo@arcor.de>
+Description: backup bash script on rsync base
+Depends: yq (>= 3.4.3-2), rsync (>= 3.4.1+ds1-5+deb13u2), ssh (>= 1:10.0p1-7+deb13u4), cryptsetup (>= 2:2.7.5-2)
diff --git a/src/restore b/src/restore
new file mode 100755 (executable)
index 0000000..2020d40
--- /dev/null
@@ -0,0 +1,184 @@
+#!/bin/bash
+
+errlog()
+{
+       local lastexit=$?
+       local msg="$1"
+
+       echo "❌ ERROR: $msg"
+       echo "🛑 EXIT: $lastexit"
+       return $lastexit
+}
+
+useage()
+{
+       echo "$0 -d <date> -n <number> -r <host> -p <path> -k <key> -o"
+       echo
+       echo " -d    date from backup"
+       echo " -n    number of backup"
+       echo " -r    host <user>@<rechner>:<port>"
+       echo " -p    path"
+       echo " -o    restore origin (delete mode)"
+       echo
+}
+
+print_line()
+{
+       for i in {1..80}; do
+               printf "#"
+       done
+
+       printf "\n"
+}
+
+restore()
+{
+       local date="$1"
+       local number="$2"
+       local machine="$3"
+       local path="$4"
+       local origin="$5"
+
+       local backup_path=$(cat config.yaml \
+               | yq --raw-output ".backup_path")
+       local backup_name=$(cat config.yaml \
+               | yq --raw-output ".backup_name")
+       local machine=$(cat config.yaml \
+               | yq --raw-output \
+               ".machines | keys[] | select(. == \"$machine\")")
+       local remote_user=$(cat config.yaml \
+               | yq --raw-output ".machines.\"$machine\".remote_user")
+       local remote_machine=$(cat config.yaml \
+               | yq --raw-output ".machines.\"$machine\".host")
+       local remote_port=$(cat config.yaml \
+               | yq --raw-output ".machines.\"$machine\".port")
+
+       [ -d "$backup_path" ] \
+               || errlog "could not find directory $backup_path" \
+               || return $?
+       [ -n "$machine" ] \
+               || errlog "config.yaml missing machine" \
+               || return $?
+       [ "$backup_name" != "null" ] \
+               || errlog "config.yaml missing backup_name" \
+               || return $?
+       [ "$remote_user" != "null" ] \
+               || errlog "config.yaml missing remote_user" \
+               || return $?
+       [ "$remote_machine" != "null" ] \
+               || errlog "config.yaml missing host" \
+               || return $?
+       [ "$remote_port" != "null" ] \
+               || errlog "config.yaml missing port" \
+               || return $?
+
+       local backup_dir="$date"_"$number"_"$backup_name"
+
+       [ -d "$backup_path/$backup_dir" ] \
+               || errlog "could not find directory $backup_path/$backup_dir" \
+               || return $?
+
+       local restore_prefix_path="$backup_path/$backup_dir/$machine"
+       [ -d "$restore_prefix_path" ] \
+               || errlog "could not find directory $restore_prefix_path" \
+               || return $?
+
+       local restore_path="$restore_prefix_path$path"
+       local remote_path="$remote_user@$remote_machine:$path"
+
+       local mode="directory"
+       [[ "$path" =~ /$ ]] || mode="file"
+
+       [ -f "$restore_path" ] || [ -d "$restore_path" ] \
+               || errlog "could not find $restore_path" \
+               || return $?
+
+       echo "ℹ️  INFO: restore mode $mode"
+       echo
+
+       echo "ℹ️  INFO: restore path $restore_path"
+       echo "ℹ️  INFO: remote path $remote_path"
+       echo
+
+       [ "$origin" == "deletemode-on" ] \
+               && local deletemode="--delete" \
+               && echo "⚠️  WARN: delete mode on" && echo
+
+       local ask_restore="N"
+       read -p "❓ QUESTION: Do you want restore? [N/y] " ask_restore
+       local ask_restore=${ask_restore:-N}
+       local ask_restore=${ask_restore,,}
+       echo ${ask_restore,,}
+
+       [ "$ask_restore" == "y" ] \
+               || errlog "you want not do restore" \
+               || return $?
+
+       echo "ℹ️  INFO: restoring..."
+
+       rsync --archive \
+               --verbose \
+               --sparse \
+               --acls \
+               --hard-links \
+               --human-readable \
+               $deletemode \
+               --rsh "ssh -p $remote_port" \
+               $restore_path \
+               $remote_path \
+               || errlog "could not restore" \
+               || return $?
+
+       echo "ℹ️  INFO: restored"
+}
+
+main()
+{
+       local date=
+       local number=
+       local machine=
+       local path=
+       local key=
+       local origin="deletemode-off"
+
+       echo "⭐ START: restore"
+
+       while getopts "d:k:n:r:p:ho" optname; do
+               case "$optname" in
+               "d") date="$OPTARG" ;;
+               "n") number="$OPTARG" ;;
+               "r") machine="$OPTARG" ;;
+               "p") path="$OPTARG" ;;
+               "o") origin="deletemode-on" ;;
+               "h") useage && return 0 ;;
+               esac
+       done
+
+       command -v yq > /dev/null \
+               || errlog "command yq not found" \
+               || return $?
+       command -v rsync > /dev/null \
+               || errlog "command rsync not found" \
+               || return $?
+       command -v ssh > /dev/null \
+               || errlog "command ssh not found" \
+               || return $?
+
+       [ -f "config.yaml" ] \
+               || errlog "file config.yaml not found" \
+               || return $?
+       [ -n "$date" ] || errlog "missing date (-d)" || return $?
+       [ -n "$number" ] || errlog "missing number (-n)" || return $?
+       [[ "$number" =~ [[:digit:]]{2} ]] \
+               || errlog "number must two digits" \
+               || return $?
+       [ -n "$machine" ] || errlog "missing machine (-r)" || return $?
+       [ -n "$path" ] || errlog "missing path (-p)" || return $?
+
+       restore "$date" "$number" "$machine" "$path" "$origin" \
+               || return $?
+
+       echo "✅ SUCCESS: restore"
+}
+
+main $*