Last active
February 7, 2025 01:02
-
-
Save af3556/331f3e52c289a5cbeab44dd945b3d487 to your computer and use it in GitHub Desktop.
scripts to initiate a backup from a source Synology NAS
This file contains hidden or 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) | |
# - root (sudo) is required to read journal logs; 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... | |
# examples: | |
# - partial failure (source folder not mounted): | |
# Jan 20 10:28:19 bd synobackupd[6784]: launch job [3] on pid [23763] | |
# Jan 20 10:56:24 bd img_backup[23763]: (23763) [err] backup_progress.cpp:501 Backup task [offsite - general] completes with result [2] and errorcode [3004]. Time spent: [1684 sec]. | |
# - success | |
# Jan 20 10:58:39 bd synobackupd[6784]: launch job [4] on pid [28621] | |
# Jan 20 11:00:54 bd img_backup[28621]: (28621) [err] backup_progress.cpp:504 Backup task [offsite - photo] completes with result [1]. Time spent: [132 sec]. | |
# - don't want the whole log, only what's added during our run | |
now=$(date +%s) | |
getsynolog () { | |
sudo journalctl --no-pager --unit synobackupd.service --since "@$now" | |
} | |
_exit() { | |
log "exiting" | |
getsynolog | grep --quiet --fixed-strings 'completes with result [1]' | |
rc=$? | |
if [ $rc -ne 0 ]; then | |
log "failed?" | |
fi | |
log "synobackupd log:" | |
getsynolog | |
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 "timed out: backup still running??" | |
exit 2 | |
fi | |
log "waiting ${s}s" | |
sleep "$s" | |
if ! is_backup_running; then | |
log "backup finished running" | |
break | |
fi | |
done |
This file contains hidden or 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 only the user@host argument, e.g. | |
# sudo ./pull_backup.sh [email protected] | |
# - if running via the 'Boot-up' Task Scheduler event it will be beneficial to precede | |
# invocation of this script with a `sleep 180` otherwise you may get spurious | |
# networking errors | |
# - 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 [email protected] ./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 to $SSH_TARGET" | |
exit 1 | |
fi | |
if ssh_with_args true; then | |
log "connected to $SSH_TARGET" | |
break | |
fi | |
log "waiting to connect" | |
sleep "$s" | |
done | |
log "executing $@" | |
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