Last active
August 23, 2023 06:36
-
-
Save bdlow/331f3e52c289a5cbeab44dd945b3d487 to your computer and use it in GitHub Desktop.
scripts to initiate a backup from a source Synology NAS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# this script will initiate a backup from a source Synology NAS | |
# 'challenges' | |
# - there's no blocking means to run a backup task (i.e. a command to start a | |
# backup task and not return until it's complete); instead we only have | |
# asynchronous actions to start a task and then later check state | |
# - it takes some period of time to actually start the task, i.e. starting a | |
# backup and immediately checking backup state will likely (incorrectly) | |
# return "not running" | |
# - there appears to be no way to actually tell if the backup succeeded or not, | |
# other than inspecting the log file (sigh) | |
# - the synobackup.log file access permissions are borked on DSM 7 (not | |
# sufficient to just be in the `log` group) | |
# - thus the user this script runs as needs to be in the `wheel` group so as | |
# to be able to sudo without a password (ref. /etc/sudoers), | |
# e.g. for the user `offsite`: | |
# synogroup --memberadd wheel offsite | |
# (will also need to be in `administrators` in order to ssh) | |
# | |
# - the synobackup utility requires the 'task id', however there is no simple | |
# way of determining the task ID for a task created via the UI | |
# - prior to Hyperbackup 4.1.0 the task ID was listed in | |
# /usr/syno/etc/synobackup.conf ('ini' format, unparseable) | |
# however that file no longer exists and the task ID appears to be | |
# indeterminable. | |
# - fortunately it appears to be simply an index starting from 1, | |
# incremented whenever a new task is created; moreover passing an invalid | |
# task id appears to have no harm | |
# i.e. invoke synobackup with a task ID of 1, increment until you see | |
# (in the UI) the job you want to run actually start | |
TASK_ID="$1" | |
HOSTNAME="$(hostname)" | |
log () { | |
echo "$(date) [${HOSTNAME}] $*" | |
} | |
if [ $# -eq 0 ]; then | |
echo "usage: $0 <task id>" >&2 | |
exit 1 | |
fi | |
# synobackup does not provide any means to check whether | |
# a backup worked (e.g. result of last backup); have to resort to | |
# monitoring the log... | |
# - don't want the whole log, only what's added during our run | |
# - intermingle log output with ours, "FYI" | |
logfile=$(mktemp) | |
# shellcheck disable=SC2024 # just want to run tail elevated, not > file | |
sudo tail -n0 -f /var/log/synolog/synobackup.log > "$logfile" & | |
logpid=$! | |
_exit() { | |
log "exiting" | |
# stop the log tail (wait to suppress shell "Terminated" messsage) | |
# by design, sudo will not relay signals that were sent by the same process | |
# group (see sudo doc); so run the kill in a new session | |
setsid kill "$logpid" && wait "$logpid" 2>/dev/null | |
# did it work? ugh | |
rc=1 | |
if ! grep -q -i started "$logfile"; then | |
log "failed to start?" | |
else | |
grep -q -i success "$logfile" | |
rc=$? | |
if [ $rc -ne 0 ]; then | |
log "failed?" | |
fi | |
fi | |
# what happened? (if anything) | |
log "synobackup.log:" | |
cat "$logfile" | |
rm "$logfile" | |
exit "$rc" | |
} | |
trap _exit EXIT | |
# no sudo, plain ol' admins can kick off a backup... | |
/usr/syno/bin/synobackup --backup "${TASK_ID}" --type image | |
case $? in | |
0) | |
log "backup task ID ${TASK_ID} started" | |
;; | |
153) | |
log "backup task ID ${TASK_ID} already running?" | |
exit 1 | |
;; | |
*) | |
log "failed to start backup task ID ${TASK_ID}" | |
exit 1 | |
;; | |
esac | |
is_backup_running() { | |
# synobackup's exit status is backwards: | |
# 0 (i.e. success/true) if not running | |
# 1 (i.e. error/false) if running | |
if /usr/syno/bin/synobackup --is-backup-restore-running; then | |
return 1 | |
fi | |
return 0 | |
} | |
# can take a moment before synobackup reports the task state, so sleep up front | |
# "give up"logic: things don't always go to plan: | |
# wait till the backup ends; increasing intervals, to a total of ~30 hrs | |
for s in $(seq 90 15 1800) -1; do | |
if [ "${s}" -lt 0 ]; then | |
log "failed to report stop" | |
exit 2 | |
fi | |
sleep "$s" | |
if ! is_backup_running; then | |
break | |
fi | |
log "in progress" | |
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# this script will initiate a backup from a source Synology NAS | |
# - before deploying this script via the Synology task scheduler: the backup | |
# source's ssh host key needs to be recorded in ~/.ssh/known_hosts; to do so | |
# - as the user that will run this script in the Synology task scheduler - | |
# invoke this script with any argument, e.g. sudo ./pull_backup.sh nasadmin@100.x.x.x | |
# - this script needs to be run as root so as to be able to call shutdown | |
# - invoke with arg1 as target username@host; arg2 as the backup script on the target | |
# - in turn, the backup script requires the synobackup task ID | |
# e.g. | |
# sudo $0 nasadmin@100.x.x.x ./do_backup.sh 3 | |
HOSTNAME="$(hostname)" | |
log () { | |
echo "$(date) [${HOSTNAME}] $*" | |
} | |
ssh_with_args() { | |
# adjust as needed | |
ssh "$SSH_TARGET" \ | |
-o ControlMaster=auto -o ControlPersist=5m -o ControlPath=~/.ssh/cm-%r@%h:%p \ | |
"$@" | |
} | |
if [ $# -eq 0 ]; then | |
echo "usage: $0 user@host dobackup_script [dobackup_script args]" >&2 | |
exit 1 | |
fi | |
SSH_TARGET="$1" | |
shift | |
# if invoked with just one argument, do a "ssh host key scan" and exit | |
# note: synology doesn't provide ssh-keyscan... | |
if [ $# -eq 0 ]; then | |
ssh_with_args -o StrictHostKeyChecking=no exit | |
exit | |
fi | |
# shutdown will be suppressed if, when the backup ends: | |
# - anyone is logged in via ssh | |
# - this file is present: | |
NOSHUTDOWN="$0.noshutdown" | |
do_shutdown() { | |
# return true if shutdown should proceed | |
[ -e "${NOSHUTDOWN}" ] && return 1 | |
who --short | grep -q '.' && return 1 | |
return 0 | |
} | |
# can take a moment for ssh to get going (e.g. VPN/tunnel to establish) | |
for s in $(seq 30 15 90) -1; do | |
if [ "${s}" -lt 0 ]; then | |
log "failed to connect (ssh)" | |
exit 1 | |
fi | |
if ssh_with_args true; then | |
log "connected" | |
break | |
fi | |
log "waiting to connect" | |
sleep "$s" | |
done | |
if ! ssh_with_args "$@"; then | |
log "failed" | |
exit 1 | |
fi | |
log "success" | |
# shutdown ignores arguments; certainly time; so DIY | |
# this is important: need to provide opportunity to fix things | |
if do_shutdown; then | |
log "shutting down" | |
# leave the pending shutdown run in the background, so as to let any email report/etc go out | |
nohup sh -c 'sleep 300 && shutdown --poweroff' >/dev/null & | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment