Skip to content

Instantly share code, notes, and snippets.

@bdlow
Last active August 23, 2023 06:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bdlow/331f3e52c289a5cbeab44dd945b3d487 to your computer and use it in GitHub Desktop.
Save bdlow/331f3e52c289a5cbeab44dd945b3d487 to your computer and use it in GitHub Desktop.
scripts to initiate a backup from a source Synology NAS
#!/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
#!/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