727 lines
20 KiB
Bash
727 lines
20 KiB
Bash
#! /bin/bash
|
|
|
|
set -Eeo pipefail
|
|
umask 077
|
|
|
|
VERSION="1.1.0"
|
|
CONFIG="backup.cfg" # default config path; can be overridden with -c/--config
|
|
DRY_RUN=false
|
|
|
|
tmpdir=""
|
|
|
|
cleanup() {
|
|
if [ -n "${tmpdir-}" ] && [ -d "$tmpdir" ]; then
|
|
rm -rf "$tmpdir"
|
|
fi
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
function usage {
|
|
cat >&2 <<EOF
|
|
Usage: $0 [options]
|
|
|
|
Options:
|
|
-c, --config PATH Path to configuration file (default: backup.cfg)
|
|
-n, --dry-run Show what would be done, but do not copy/compress/push/delete
|
|
-h, --help Show this help and exit
|
|
-v, --version Show Backify version and exit
|
|
EOF
|
|
}
|
|
|
|
function show_version {
|
|
echo "Backify version $VERSION"
|
|
}
|
|
|
|
function parse_args {
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-c|--config)
|
|
if [ -n "${2-}" ]; then
|
|
CONFIG="$2"
|
|
shift 2
|
|
else
|
|
echo "Error: -c|--config requires a path argument." >&2
|
|
usage
|
|
exit 1
|
|
fi
|
|
;;
|
|
-n|--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
-v|--version)
|
|
show_version
|
|
exit 0
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
-*)
|
|
echo "Unknown option: $1" >&2
|
|
usage
|
|
exit 1
|
|
;;
|
|
*)
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
function log_enabled {
|
|
local needle="$1"
|
|
local item
|
|
for item in "${log_to_backup[@]:-}"; do
|
|
if [ "$item" = "$needle" ]; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function require_cmd {
|
|
local cmd="$1"
|
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
echo "Error: required command '$cmd' not found in PATH." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
function preflight {
|
|
require_cmd tar
|
|
|
|
if [ "${rsync_push:-false}" = true ]; then
|
|
require_cmd rsync
|
|
require_cmd ssh
|
|
fi
|
|
|
|
if [ "${docker_enabled:-false}" = true ]; then
|
|
require_cmd docker
|
|
fi
|
|
|
|
if [ "${db_backup:-false}" = true ]; then
|
|
case "${database_type:-}" in
|
|
mysql)
|
|
require_cmd mysqldump
|
|
;;
|
|
postgresql)
|
|
require_cmd pg_dump
|
|
require_cmd pg_dumpall
|
|
;;
|
|
*)
|
|
echo "Error: database_type must be 'mysql' or 'postgresql' when db_backup is true." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
function init {
|
|
echo "Backify is starting, looking for configuration file..." >&2
|
|
|
|
config="$CONFIG"
|
|
secured_config='sbackup.cfg'
|
|
|
|
if [ ! -f "$config" ]; then
|
|
echo "Error: Config file not found: $config" >&2
|
|
echo "Please create a config file or specify the location of an existing file (use -c/--config)." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if grep -E -q -v '^#|^[^ ]*=[^;]*' "$config"; then
|
|
echo "Config file is unclean, cleaning it..." >&2
|
|
grep -E '^#|^[^ ]*=[^;&]*' "$config" >"$secured_config"
|
|
config="$secured_config"
|
|
fi
|
|
|
|
source "$config"
|
|
|
|
echo "Configuration file loaded" >&2
|
|
|
|
if [ "$EUID" -ne 0 ]; then
|
|
echo "Please run as root" >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$backup_path"
|
|
if [ ! -w "$backup_path" ]; then
|
|
echo "Error: backup_path '$backup_path' is not writable." >&2
|
|
exit 1
|
|
fi
|
|
|
|
: "${retention_days:=0}"
|
|
: "${retention_keep_min:=0}"
|
|
: "${pre_backup_hook:=}"
|
|
: "${post_backup_hook:=}"
|
|
|
|
if ! declare -p log_to_backup >/dev/null 2>&1; then
|
|
log_to_backup=()
|
|
fi
|
|
|
|
if ! declare -p custom_dirs >/dev/null 2>&1; then
|
|
custom_dirs=()
|
|
fi
|
|
|
|
if ! declare -p targets >/dev/null 2>&1; then
|
|
targets=()
|
|
fi
|
|
}
|
|
|
|
function detect_system {
|
|
echo "Detecting OS type..." >&2
|
|
|
|
if [ -r /etc/os-release ]; then
|
|
. /etc/os-release
|
|
case "$ID" in
|
|
rhel|centos|rocky|almalinux)
|
|
echo "Discovered Red Hat-based OS..." >&2
|
|
SYSTEM='rhel'
|
|
;;
|
|
debian|ubuntu)
|
|
echo "Discovered Debian-based OS..." >&2
|
|
SYSTEM='debian'
|
|
;;
|
|
*)
|
|
echo "Error: Unsupported OS: $ID" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
elif [ -f /etc/redhat-release ]; then
|
|
echo "Discovered Red Hat-based OS via legacy detection..." >&2
|
|
SYSTEM='rhel'
|
|
elif [ -f /etc/lsb-release ]; then
|
|
echo "Discovered Debian-based OS via legacy detection..." >&2
|
|
SYSTEM='debian'
|
|
else
|
|
echo "Error: Unable to detect OS type." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
function makedir {
|
|
timestamp=$(date +%Y%m%d_%H%M)
|
|
tmpdir="$backup_path/backify-$timestamp"
|
|
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would create temporary directory $tmpdir" >&2
|
|
else
|
|
mkdir -p "$tmpdir"
|
|
fi
|
|
}
|
|
|
|
function wwwbackup {
|
|
if [ "$www_backup" = true ]; then
|
|
echo "Backing up wwwroot..." >&2
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy $www_dir to $tmpdir/wwwdata" >&2
|
|
return
|
|
fi
|
|
mkdir -p "$tmpdir/wwwdata"
|
|
cp -r "$www_dir/" "$tmpdir/wwwdata/"
|
|
echo "Finished" >&2
|
|
fi
|
|
}
|
|
|
|
function vhostbackup {
|
|
if [ "$vhost_backup" = true ]; then
|
|
echo "Backing up vhosts..." >&2
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy $vhost_dir to $tmpdir/vhosts" >&2
|
|
return
|
|
fi
|
|
mkdir -p "$tmpdir/vhosts"
|
|
cp -avr "$vhost_dir/" "$tmpdir/vhosts/"
|
|
echo "Finished" >&2
|
|
fi
|
|
}
|
|
|
|
function logbackup {
|
|
if [ "$log_backup" = true ]; then
|
|
echo "Backing up system logs..." >&2
|
|
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would collect selected logs into $tmpdir/syslogs" >&2
|
|
else
|
|
mkdir -p "$tmpdir/syslogs"
|
|
fi
|
|
|
|
case "$SYSTEM" in
|
|
"rhel")
|
|
|
|
if log_enabled "fail2ban"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/fail2ban.log" >&2
|
|
else
|
|
cp /var/log/fail2ban.log "$tmpdir/syslogs/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "apache"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/httpd to $tmpdir/apachelogs" >&2
|
|
else
|
|
mkdir -p "$tmpdir/apachelogs"
|
|
cp -r /var/log/httpd "$tmpdir/apachelogs" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "nginx"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/nginx to $tmpdir/nginxlogs" >&2
|
|
else
|
|
mkdir -p "$tmpdir/nginxlogs"
|
|
cp -r /var/log/nginx "$tmpdir/nginxlogs" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "pckg_mngr"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy yum/dnf logs to $tmpdir/syslogs/yum" >&2
|
|
else
|
|
mkdir -p "$tmpdir/syslogs/yum"
|
|
cp -r /var/log/yum/* "$tmpdir/syslogs/yum/" 2>/dev/null || true
|
|
cp -r /var/log/dnf* "$tmpdir/syslogs/yum/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "letsencrypt"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/letsencrypt to $tmpdir/syslogs/letsencrypt" >&2
|
|
else
|
|
mkdir -p "$tmpdir/syslogs/letsencrypt"
|
|
cp -r /var/log/letsencrypt/* "$tmpdir/syslogs/letsencrypt/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "php"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/php*.log to $tmpdir/syslogs" >&2
|
|
else
|
|
cp -r /var/log/php*.log "$tmpdir/syslogs/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "syslog"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/secure to $tmpdir/syslogs" >&2
|
|
else
|
|
cp -r /var/log/secure "$tmpdir/syslogs/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "purge"; then
|
|
echo "Purging logs..." >&2
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would truncate and clear configured logs on RHEL system" >&2
|
|
else
|
|
truncate -s 0 /var/log/messages 2>/dev/null || true
|
|
truncate -s 0 /var/log/syslog 2>/dev/null || true
|
|
|
|
if log_enabled "apache"; then
|
|
truncate -s 0 /var/log/httpd/* 2>/dev/null || true
|
|
rm /var/log/httpd/*.gz 2>/dev/null || true
|
|
fi
|
|
if log_enabled "nginx"; then
|
|
truncate -s 0 /var/log/nginx/* 2>/dev/null || true
|
|
rm /var/log/nginx/*.gz 2>/dev/null || true
|
|
fi
|
|
if log_enabled "fail2ban"; then
|
|
truncate -s 0 /var/log/fail2ban.log 2>/dev/null || true
|
|
fi
|
|
if log_enabled "pckg_mngr"; then
|
|
truncate -s 0 /var/log/yum/* 2>/dev/null || true
|
|
truncate -s 0 /var/log/dnf* 2>/dev/null || true
|
|
fi
|
|
if log_enabled "letsencrypt"; then
|
|
truncate -s 0 /var/log/letsencrypt/* 2>/dev/null || true
|
|
fi
|
|
if log_enabled "php"; then
|
|
truncate -s 0 /var/log/php*.log 2>/dev/null || true
|
|
fi
|
|
if log_enabled "syslog"; then
|
|
truncate -s 0 /var/log/secure 2>/dev/null || true
|
|
fi
|
|
fi
|
|
fi
|
|
;;
|
|
|
|
"debian")
|
|
|
|
if log_enabled "fail2ban"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/fail2ban.log" >&2
|
|
else
|
|
cp /var/log/fail2ban.log "$tmpdir/syslogs/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "apache"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/apache2 to $tmpdir/apachelogs" >&2
|
|
else
|
|
mkdir -p "$tmpdir/apachelogs"
|
|
cp -r /var/log/apache2 "$tmpdir/apachelogs" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "nginx"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/nginx to $tmpdir/nginxlogs" >&2
|
|
else
|
|
mkdir -p "$tmpdir/nginxlogs"
|
|
cp -r /var/log/nginx "$tmpdir/nginxlogs" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "pckg_mngr"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy apt logs to $tmpdir/syslogs/apt" >&2
|
|
else
|
|
mkdir -p "$tmpdir/syslogs/apt"
|
|
cp -r /var/log/apt/* "$tmpdir/syslogs/apt/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "auth"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/auth.log" >&2
|
|
else
|
|
cp -r /var/log/auth.log "$tmpdir/syslogs/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "dmesg"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/dmesg" >&2
|
|
else
|
|
cp -r /var/log/dmesg "$tmpdir/syslogs/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "dpkg"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/dpkg.log" >&2
|
|
else
|
|
cp -r /var/log/dpkg.log "$tmpdir/syslogs/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "letsencrypt"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/letsencrypt to $tmpdir/syslogs/letsencrypt" >&2
|
|
else
|
|
mkdir -p "$tmpdir/syslogs/letsencrypt"
|
|
cp -r /var/log/letsencrypt/* "$tmpdir/syslogs/letsencrypt/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "php"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/php*.log" >&2
|
|
else
|
|
cp -r /var/log/php*.log "$tmpdir/syslogs/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "syslog"; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy /var/log/syslog" >&2
|
|
else
|
|
cp -r /var/log/syslog "$tmpdir/syslogs/" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if log_enabled "purge"; then
|
|
echo "Purging logs..." >&2
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would truncate and clear configured logs on Debian system" >&2
|
|
else
|
|
truncate -s 0 /var/log/syslog 2>/dev/null || true
|
|
truncate -s 0 /var/log/message 2>/dev/null || true
|
|
|
|
if log_enabled "apache"; then
|
|
truncate -s 0 /var/log/apache2/* 2>/dev/null || true
|
|
rm /var/log/apache2/*.gz 2>/dev/null || true
|
|
fi
|
|
if log_enabled "nginx"; then
|
|
truncate -s 0 /var/log/nginx/* 2>/dev/null || true
|
|
rm /var/log/nginx/*.gz 2>/dev/null || true
|
|
fi
|
|
if log_enabled "fail2ban"; then
|
|
truncate -s 0 /var/log/fail2ban.log 2>/dev/null || true
|
|
fi
|
|
if log_enabled "pckg_mngr"; then
|
|
truncate -s 0 /var/log/apt/* 2>/dev/null || true
|
|
fi
|
|
if log_enabled "auth"; then
|
|
truncate -s 0 /var/log/auth.log 2>/dev/null || true
|
|
fi
|
|
if log_enabled "dmesg"; then
|
|
truncate -s 0 /var/log/dmesg 2>/dev/null || true
|
|
fi
|
|
if log_enabled "dpkg"; then
|
|
truncate -s 0 /var/log/dpkg.log 2>/dev/null || true
|
|
fi
|
|
if log_enabled "letsencrypt"; then
|
|
truncate -s 0 /var/log/letsencrypt/* 2>/dev/null || true
|
|
fi
|
|
if log_enabled "php"; then
|
|
truncate -s 0 /var/log/php*.log 2>/dev/null || true
|
|
fi
|
|
if log_enabled "syslog"; then
|
|
truncate -s 0 /var/log/syslog 2>/dev/null || true
|
|
fi
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
function push {
|
|
if [ "$rsync_push" = true ]; then
|
|
local archive="$backup_path/backify-$timestamp.tar.gz"
|
|
|
|
if [ "$DRY_RUN" = true ]; then
|
|
if [ "${#targets[@]}" -gt 0 ]; then
|
|
echo "[DRY-RUN] Would rsync $archive to multiple remote targets:" >&2
|
|
local t
|
|
for t in "${targets[@]}"; do
|
|
echo " - $t" >&2
|
|
done
|
|
else
|
|
echo "[DRY-RUN] Would rsync $archive to $target_user@$target_host:$target_dir" >&2
|
|
fi
|
|
return
|
|
fi
|
|
|
|
local rsync_ssh="ssh"
|
|
if [ -n "${target_key:-}" ]; then
|
|
rsync_ssh="ssh -i $target_key"
|
|
fi
|
|
|
|
if [ "${#targets[@]}" -gt 0 ]; then
|
|
local remote
|
|
for remote in "${targets[@]}"; do
|
|
echo "Pushing the backup package to $remote..." >&2
|
|
rsync -avz -e "$rsync_ssh" "$archive" "$remote"
|
|
done
|
|
else
|
|
echo "Pushing the backup package to $target_host..." >&2
|
|
rsync -avz -e "$rsync_ssh" "$archive" "$target_user@$target_host:$target_dir"
|
|
fi
|
|
|
|
if [ "$push_clean" = true ]; then
|
|
echo "Removing archive..." >&2
|
|
rm -f "$archive"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function dockerbackup {
|
|
if [ "$docker_enabled" = true ]; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would back up Docker images/volumes/data according to configuration." >&2
|
|
return
|
|
fi
|
|
|
|
if [ "$docker_images" = true ]; then
|
|
echo "Backing up Docker images..." >&2
|
|
for container_name in $(docker inspect --format='{{.Name}}' $(docker ps -q) | cut -f2 -d/); do
|
|
echo -n "$container_name - " >&2
|
|
container_image=$(docker inspect --format='{{.Config.Image}}' "$container_name")
|
|
mkdir -p "$tmpdir/containers/$container_name"
|
|
save_dir="$tmpdir/containers/$container_name/$container_name-image.tar"
|
|
docker save -o "$save_dir" "$container_image"
|
|
echo "Finished" >&2
|
|
done
|
|
fi
|
|
if [ "$docker_volumes" = true ]; then
|
|
echo "Backing up Docker volumes..." >&2
|
|
#Thanks piscue :)
|
|
for container_name in $(docker inspect --format='{{.Name}}' $(docker ps -q) | cut -f2 -d/); do
|
|
mkdir -p "$tmpdir/containers/$container_name"
|
|
echo -n "$container_name - " >&2
|
|
docker run --rm --userns=host \
|
|
--volumes-from "$container_name" \
|
|
-v "$tmpdir/containers/$container_name:/backup" \
|
|
-e TAR_OPTS="$tar_opts" \
|
|
piscue/docker-backup \
|
|
backup "$container_name-volume.tar.xz"
|
|
echo "Finished" >&2
|
|
done
|
|
fi
|
|
if [ "$docker_data" = true ]; then
|
|
echo "Backing up container information..." >&2
|
|
for container_name in $(docker inspect --format='{{.Name}}' $(docker ps -q) | cut -f2 -d/); do
|
|
echo -n "$container_name - " >&2
|
|
container_data=$(docker inspect "$container_name")
|
|
mkdir -p "$tmpdir/containers/$container_name"
|
|
echo "$container_data" >"$tmpdir/containers/$container_name/$container_name-data.txt"
|
|
echo "Finished" >&2
|
|
done
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function backup_db {
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would dump database(s) of type '$database_type' into $tmpdir/db" >&2
|
|
return
|
|
fi
|
|
|
|
mkdir -p "$tmpdir/db"
|
|
if [ "$db_all" = true ]; then
|
|
if [ "$database_type" = "mysql" ]; then
|
|
mysqldump -u "$db_username" -p"$db_password" -h "$db_host" -P"$db_port" --all-databases >"$tmpdir/db/db_all.sql"
|
|
elif [ "$database_type" = "postgresql" ]; then
|
|
PGPASSWORD="$db_password" pg_dumpall -U "$db_username" -h "$db_host" -f "$tmpdir/db/db_all.sql"
|
|
fi
|
|
else
|
|
if [ "$database_type" = "mysql" ]; then
|
|
mysqldump -u "$db_username" -p"$db_password" -h "$db_host" -P"$db_port" "$db_name" >"$tmpdir/db/$db_name.sql"
|
|
elif [ "$database_type" = "postgresql" ]; then
|
|
PGPASSWORD="$db_password" pg_dump -U "$db_username" -h "$db_host" "$db_name" -f "$tmpdir/db/$db_name.sql"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function custombackup {
|
|
if [ "$custom_backup" = true ]; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "[DRY-RUN] Would copy custom directories into $tmpdir/custom:" >&2
|
|
local i
|
|
for i in "${custom_dirs[@]}"; do
|
|
echo " - $i" >&2
|
|
done
|
|
return
|
|
fi
|
|
|
|
mkdir -p "$tmpdir/custom"
|
|
local i
|
|
for i in "${custom_dirs[@]}"; do
|
|
cp -r "$i" "$tmpdir/custom/" 2>/dev/null || true
|
|
done
|
|
fi
|
|
}
|
|
|
|
function apply_retention {
|
|
if [ "${retention_days:-0}" -le 0 ] && [ "${retention_keep_min:-0}" -le 0 ]; then
|
|
return
|
|
fi
|
|
|
|
local dir="$backup_path"
|
|
local pattern="$dir/backify-"*.tar.gz
|
|
|
|
if ! compgen -G "$pattern" >/dev/null; then
|
|
return
|
|
fi
|
|
|
|
echo "Applying retention policy in $dir..." >&2
|
|
|
|
local archives=()
|
|
local file
|
|
|
|
while IFS= read -r file; do
|
|
archives+=("$file")
|
|
done < <(ls -1 "$pattern" 2>/dev/null | sort)
|
|
|
|
local total=${#archives[@]}
|
|
if [ "$total" -eq 0 ]; then
|
|
return
|
|
fi
|
|
|
|
local keep_min=${retention_keep_min:-0}
|
|
if [ "$keep_min" -lt 0 ]; then keep_min=0; fi
|
|
|
|
local cutoff_date=""
|
|
if [ "${retention_days:-0}" -gt 0 ]; then
|
|
cutoff_date=$(date -d "-${retention_days} days" +%Y%m%d 2>/dev/null || true)
|
|
fi
|
|
|
|
local i=0
|
|
for file in "${archives[@]}"; do
|
|
i=$((i + 1))
|
|
|
|
if [ "$keep_min" -gt 0 ] && [ $((total - i)) -lt "$keep_min" ]; then
|
|
continue
|
|
fi
|
|
|
|
if [ -z "$cutoff_date" ] && [ "$keep_min" -gt 0 ]; then
|
|
echo "Removing old backup (by count): $file" >&2
|
|
rm -f "$file"
|
|
continue
|
|
elif [ -z "$cutoff_date" ]; then
|
|
continue
|
|
fi
|
|
|
|
local base
|
|
base=$(basename "$file")
|
|
local date_part=${base#backify-}
|
|
date_part=${date_part%%_*}
|
|
|
|
if [ "$date_part" -lt "$cutoff_date" ]; then
|
|
echo "Removing old backup (older than ${retention_days} days): $file" >&2
|
|
rm -f "$file"
|
|
fi
|
|
done
|
|
}
|
|
|
|
function runbackup {
|
|
init
|
|
detect_system
|
|
preflight
|
|
|
|
if [ "$enabled" = true ]; then
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "Running Backify in DRY-RUN mode. No files will be copied, compressed, pushed or deleted." >&2
|
|
fi
|
|
|
|
makedir
|
|
|
|
if [ "$DRY_RUN" = false ] && [ -n "${pre_backup_hook:-}" ] && [ -x "$pre_backup_hook" ]; then
|
|
echo "Running pre-backup hook: $pre_backup_hook" >&2
|
|
"$pre_backup_hook" "$tmpdir"
|
|
fi
|
|
|
|
wwwbackup
|
|
vhostbackup
|
|
logbackup
|
|
dockerbackup
|
|
if [ "$db_backup" = true ]; then
|
|
backup_db
|
|
fi
|
|
custombackup
|
|
|
|
if [ "$DRY_RUN" = false ]; then
|
|
echo "Creating backup archive..." >&2
|
|
tar -czvf "$backup_path/backify-$timestamp.tar.gz" -C "$backup_path" "backify-$timestamp" >> /var/log/backify-compress.log 2>&1
|
|
|
|
push
|
|
|
|
apply_retention
|
|
|
|
if [ -n "${post_backup_hook:-}" ] && [ -x "$post_backup_hook" ]; then
|
|
echo "Running post-backup hook: $post_backup_hook" >&2
|
|
local post_backup_archive
|
|
post_backup_archive="$backup_path/backify-$timestamp.tar.gz"
|
|
"$post_backup_hook" "$post_backup_archive"
|
|
fi
|
|
else
|
|
echo "[DRY-RUN] Skipping archive creation, remote push, retention and post-backup hooks." >&2
|
|
fi
|
|
|
|
echo "Voila, enjoy the rest of the day" >&2
|
|
else
|
|
echo "Backup is disabled in the configuration" >&2
|
|
fi
|
|
}
|
|
|
|
parse_args "$@"
|
|
runbackup |