103 lines
2.7 KiB
Bash
Executable File
103 lines
2.7 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
set -eu
|
|
|
|
backup_dir=${BACKUP_DIR:-/backups}
|
|
ssh_key_dir=/root/.ssh
|
|
success_marker="$backup_dir/.last-success"
|
|
|
|
ssh_host=${BACKUP_SSH_HOST:-}
|
|
ssh_user=${BACKUP_SSH_USER:-backup}
|
|
ssh_port=${BACKUP_SSH_PORT:-22}
|
|
ssh_dir=${BACKUP_SSH_DIR:-}
|
|
|
|
ssh_opts="-o BatchMode=yes -o PasswordAuthentication=no -o StrictHostKeyChecking=yes"
|
|
|
|
copy_remote() {
|
|
target=$1
|
|
[ -n "$ssh_host" ] || return 0
|
|
[ -d "$ssh_key_dir" ] || {
|
|
printf 'SSH key directory %s not found\n' "$ssh_key_dir" >&2
|
|
return 1
|
|
}
|
|
case "$ssh_port" in
|
|
''|*[!0-9]*) printf 'BACKUP_SSH_PORT must be numeric\n' >&2; return 1 ;;
|
|
esac
|
|
case "$ssh_user" in
|
|
''|*[!A-Za-z0-9_.-]*) printf 'BACKUP_SSH_USER contains unsupported characters\n' >&2; return 1 ;;
|
|
esac
|
|
case "$ssh_dir" in
|
|
/|''|*[!A-Za-z0-9_./-]*)
|
|
printf 'BACKUP_SSH_DIR must be a non-root absolute path without spaces\n' >&2
|
|
return 1
|
|
;;
|
|
/*) ;;
|
|
*)
|
|
printf 'BACKUP_SSH_DIR must be an absolute path\n' >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
filename=$(basename "$target")
|
|
remote="$ssh_user@$ssh_host"
|
|
remote_target="$ssh_dir/$filename"
|
|
remote_temporary="$ssh_dir/.$filename.tmp"
|
|
|
|
ssh $ssh_opts -p "$ssh_port" "$remote" "mkdir -p '$ssh_dir'" >/dev/null
|
|
scp $ssh_opts -P "$ssh_port" "$target" "$remote:$remote_temporary"
|
|
ssh $ssh_opts -p "$ssh_port" "$remote" \
|
|
"mv '$remote_temporary' '$remote_target' && find '$ssh_dir' -maxdepth 1 -type f -name 'cents-*.dump' -mtime +29 -delete"
|
|
printf 'Backup copied to %s:%s\n' "$remote" "$remote_target"
|
|
}
|
|
|
|
run_backup() {
|
|
umask 077
|
|
mkdir -p "$backup_dir/db"
|
|
|
|
timestamp=$(date +%Y%m%d-%H%M%S)
|
|
target="$backup_dir/db/cents-$timestamp.dump"
|
|
temporary="$target.tmp"
|
|
trap 'rm -f "$temporary"' EXIT INT TERM
|
|
|
|
pg_dump --format=custom --file="$temporary"
|
|
pg_restore --list "$temporary" >/dev/null
|
|
mv "$temporary" "$target"
|
|
trap - EXIT INT TERM
|
|
|
|
printf 'Backup created: %s\n' "$target"
|
|
|
|
copy_remote "$target"
|
|
find "$backup_dir/db" -maxdepth 1 -type f -name 'cents-*.dump' -mtime +29 -delete
|
|
touch "$success_marker"
|
|
printf 'Backup completed successfully\n'
|
|
}
|
|
|
|
check_health() {
|
|
pgrep crond >/dev/null || return 1
|
|
[ -f "$success_marker" ] || return 1
|
|
now=$(date +%s)
|
|
last_success=$(stat -c %Y "$success_marker")
|
|
age=$((now - last_success))
|
|
[ "$age" -ge 0 ] && [ "$age" -le 108000 ]
|
|
}
|
|
|
|
case "${1:-cron}" in
|
|
run)
|
|
run_backup
|
|
;;
|
|
cron)
|
|
printf '%s\n' \
|
|
'0 2 * * * /usr/local/bin/cents-backup run >> /proc/1/fd/1 2>> /proc/1/fd/2' \
|
|
> /etc/crontabs/root
|
|
printf 'Backup scheduler started: daily at 02:00 %s, retaining 30 days\n' "${TZ:-local time}"
|
|
exec crond -f -l 2
|
|
;;
|
|
health)
|
|
check_health
|
|
;;
|
|
*)
|
|
printf 'Unknown command: %s\n' "$1" >&2
|
|
exit 2
|
|
;;
|
|
esac
|