thanks ! to obvious :) - one done, more to come :)
Have this weird thing happening - the bash script I created works perfect via the web interface - the bash used then is /usr/local/bin/bash
Then tried to run the same script from of a the shell on my nas - when executing the same script I get "Missing }."
Figured that there's a different bash when open a shell on nas. and sure enough - the bash shell now is /bin/bash
Updated the script to point to this, but the same "Missing }." is thrown. Any idea why this happens...?
Also wonder how I can get this script onto my thumb drive so it's always available - as it stands now I copied it to one of my mounted volumes (which is silly)
here's the complete script as it stands (supports rsync as well as zfs snapshot backup/restore)
Code:
#!/bin/bash
#
# backup/restore syncronization
#
# https://forums.freenas.org/index.php?threads/zfs-send-to-external-backup-drive.17850/
#echo "Starting"
req="${1,,}" #convert to lower case request (backup or restore)
drive="${2^^}" #convert to upper case target/destination drive
dt="${3^^}" #convert to upper case date of a backup point to restore from
force="${3,,}" #convert flag setting to lower case
u="" #default force setting flag (rsync)
if [ "${force}" == "" ];
then
force=""
fi
back="BACKUP" #default name of backup volume
status=0
# run if user hits control-c
function control_c() {
echo -en "\n*** Ouch! Exiting ***\n"
exit $?
}
# trap keyboard interrupt (control-c)
trap control_c SIGINT
#pause prompt
function pause(){
read -p "$*"
}
#command line syntax error message
function error() {
echo "command line error ${@}"
echo " to backup zfs with snapshots:"
echo " /mnt/<drive>/backrest.sh backup <drive>"
echo " or to restore zfs with snapshots:"
echo " /mnt/<drive>/backrest.sh restore <drive> <ccyymmdd> or sync <drive>"
echo " or to do rsync do ..."
echo " backup drive to /mnt/${back}:"
echo " /mnt/<drive>/backrest.sh sync <drive>"
echo " restore from /mnt/${back}:"
echo " /mnt/<drive>/backrest.sh syncrestore <drive>"
return
}
#check if the drive is mounted or even exists on the system
function is_mounted() {
local FS=$(zfs get -H mounted "${@}")
FS_REGEX="^${@}\s+mounted\s+yes\s+-"
#check for errors
if [ "${FS}" == "" ]; then
status=$? #status from last function call
echo "${@} not found/mounted"
return 0 #evaluates to false - error condition
fi
if [[ $FS =~ $FS_REGEX ]]; then
status=$? #status from last function call
echo "${@} not mounted"
return 0 #evaluates to false - error condition
fi
#drive checked out ok
echo "${@} mounted"
return 1 #evaluates to true - success
}
#wrapper to ensure BACKUP drive and nominated target/destination drive is mounted/exists
function checkMountPoints() {
#checks if the backup/restore drive exists
#check source drive
is_mounted ${@}
status=$? #status from last function call
if [ $status == 1 ];
then
echo "${@} checked out ok"
else
return 0 #evalutes to false error condition
fi
#check backup drive
is_mounted ${back}
status=$? #status from last function call
if [ $status == 1 ];
then
echo "${back} checked out ok"
return 1
else
return 0 #evaluates to false - error condition
fi
}
#rsync across the whole source volume to backup destination volume
function RyncFromSourceToDetachedMediaBackup() {
#checkMountPoints ${drive}
checkMountPoints ${1}
status=$? #status from last function call
if [ $status == 1 ];
then
echo "rsync -pogavt${2} /mnt/${1} /mnt/${back}"
rsync -pogavt${2} /mnt/${1} /mnt/${back}
status=$? #status from last function call
if [ $status == 0 ];
then
echo "rsync from /mnt/${1} to /mnt/${back}/${1} successful"
else
echo "rsync from /mnt/${1} to /mnt/${back}/${1} unsuccessful"
fi
else
status=1
fi
return $status
}
#rsync across the whole backup volume to destination source volumne
function RsyncFromBackupToSourceRestore() {
#checkMountPoints ${drive}
checkMountPoints ${1}
status=$? #status from last function call
if [ $status == 1 ];
then
echo "rsync -pogavt${2} /mnt/${back}/${1}/ /mnt/${1}"
rsync -pogavt${2} /mnt/${back}/${1}/ /mnt/${1}
status=$? #status from last function call
if [ $status == 0 ];
then
echo "rsync from /mnt/${back}/${1}/ to /mnt/${1} successful"
else
echo "rsync from /mnt/${back}/${1}/ to /mnt/${1} unsuccessful"
fi
else
status=1
fi
return $status
}
function takesnapshot() {
#echo "start takesnapshot() of ${@} as ${@}-$(date +%Y%m%d)"
zfs snapshot -r ${@}@${@}-$(date +%Y%m%d)
status=$? #status from last function call
if [ $status == 0 ];
then
status=1
else
status=0
fi
#echo "chk outcome takesnapshot() $status"
return $status
}
function renamesnapshot() {
#echo "start renamesnapshot() ${@}@${@}-${date} to ${@}@${@}-$(date +%Y%m%d)-restored_$(date +%Y%m%d)"
zfs rename -r ${@}@${@}-${date} ${@}@${@}-$(date +%Y%m%d)-restored_$(date +%Y%m%d)
status=$? #status from last function call
if [ $status == 0 ];
then
status=1
else
status=0
fi
#echo "chk outcome renamesnapshot() $status"
return $status
}
function deletesnapshot() {
#echo "start deletesnapshot() ${@}@${@}-$(date +%Y%m%d)"
zfs destroy -r ${@}@${@}-$(date +%Y%m%d)
status=$? #status from last function call
if [ $status == 0 ];
then
status=1
else
status=0
fi
#echo "chk outcome deletesnapshot() $status"
return $status
}
function sendsnapshot () {
#echo "start sendsnapshot() ${@}@${@}-$(date +%Y%m%d) to ${back}/${@}-$(date +%Y%m%d)"
zfs send -R ${@}@${@}-$(date +%Y%m%d) | zfs receive -vF ${back}/${@}-$(date +%Y%m%d)
status=$? #status from last function call
if [ $status == 0 ];
then
status=1
else
status=0
fi
#echo "chk outcome sendsnapshot() $status"
return $status
}
#takes the backup of nominated drive, sending it to the BACKUP drive
function doBackup() {
checkMountPoints ${drive}
status=$? #status from last function call
if [ $status == 1 ];
then
echo "about to take snapshot of $drive"
takesnapshot $drive
status=$? #status from last function call
if [ $status == 1 ];
then
echo "about to send snapshot of $drive to $back"
sendsnapshot ${drive}
status=$? #status from last function call
if [ $status == 1 ];
then
echo "about to take snapshot of ${back}"
takesnapshot ${back};
status=$? #status from last function call
if [ $status == 1 ];
then
echo "about to delete snapshot of ${drive}"
deletesnapshot ${drive}
status=$? #status from last function call
if [ $status == 1 ];
then
#echo "about to delete snapshot of ${back}"
#deletesnapshot ${back}
#status=$? #status from last function call
#if [ $status == 1 ];
#then
echo "Successfully backed up ${drive} onto ${back}"
return
#fi
fi
fi
fi
fi
fi
status=$? #status from last function call
error $status
echo "Unsuccessful backup of ${@}"
return
}
function validateDate() {
# Script expecting a Date parameter in YYYYMMDD format as input
echo ${dt} | grep '^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$'
status=$? #status from last function call
if [ $status -eq 0 ]; #valid date
then
status=0 #do nothing statement
else
#since previous test failed I will try to reverse the argument assuming a reverse input
echo ${drive} | grep '^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$'
status=$? #status from last function call
if [ $status -eq 0 ]; #valid date
then
# parameters in reversed argument order - switching order
tmpdrive="${dt}"
dt="${drive}"
drive="${tmpdrive}"
fi
fi
return $status #0=success, otherwise error
}
#some checks post restore a zfs volume and all datasets
function doRestore() {
echo "Start Restore..."
validateDate
status=$? #status from last function call
#validated date params
if [ $status == 0 ];
then
checkMountPoints ${drive} #check if nominated target/destination drive/volume is mounted/available
status=$? #status from last function call
if [ $status == 1 ]; #target/destination drives(volumes) accessible
then
echo "Initiate restore of $drive from ${back}@${drive}-${dt}"
execRestore ${drive} ${dt} #do the restore of drive for date (assuming it exists for set date)
status=$? #status from last function call
if [ $status == 0 ]; #success
then
echo "Successfully restored $drive from ${back}@${drive}-${dt}"
else
echo "Unsuccessful restore of $drive from ${back}"
error $status
fi
else
echo "${drive} or ${back} not mounted"
error $status
fi
else
echo "${dt} is not a valid date"
error $status
fi
return
}
function destroyDataset () {
echo "about to delete dataset ${@}"
if [ "${@}" == "" ];
then
echo "empty dataset passed"
status=1
else
#zfs destroy -R $back/$drive-$dt/${@}
zfs destroy -R ${@}
status=$? #status from last function call
if [ $status == 0 ]; #successful rename
then
#echo "Successfully deleted dataset $back/$drive-$dt/${@}"
echo "Successfully deleted dataset ${@}"
else
#echo "Faailed to delete dataset $back/$drive-$dt/${@}"
echo "Failed to delete dataset ${@}"
fi
fi
return $status
}
function jailCleanExists () {
echo "check if ${@} exists"
zfs list ${@}
status=$? #status from last function call
if [ $status == 0 ]; #successful rename
then
status=1
else
status=0
fi
echo "status from call $status"
return $status
}
function removeJailDependencies () {
exception=0
jailCleanExists ${@}/jails/sickbeard_2
status=$? #status from last function call
if [ $status == 1 ];
then
destroyDataset ${@}/jails/sickbeard_2
status=$? #status from last function call
if [ $status == 0 ];
then
echo "successfully removed ${@}/jails/jails/sickbeard_2"
else
echo "Warning: ${@}/jails/jails/sickbeard_2 not found"
exception=1
fi
fi
jailCleanExists ${@}/jails/plexmediaserver_1
status=$? #status from last function call
if [ $status == 1 ];
then
destroyDataset ${@}/jails/plexmediaserver_1
status=$? #status from last function call
if [ $status == 0 ];
then
echo "successfully removed ${@}/jails/plexmediaserver_1"
else
echo "Warning: ${@}/jails/plexmediaserver_1 not found"
exception=1
fi
fi
jailCleanExists ${@}/jails/firefly_1
status=$? #status from last function call
if [ $status == 1 ];
then
destroyDataset ${@}/jails/firefly_1
status=$? #status from last function call
if [ $status == 0 ];
then
echo "successfully removed ${@}/jails/firefly_1"
else
echo "Warning: ${@}/jails/firefly_1 not found"
exception=1
fi
fi
jailCleanExists ${@}/jails/.warden-template-pluginjail@clean
status=$? #status from last function call
if [ $status == 1 ];
then
destroyDataset ${@}/jails/.warden-template-pluginjail@clean
status=$? #status from last function call
if [ $status == 0 ];
then
echo "successfully removed ${@}/jails/.warden-template-pluginjail@clean"
else
echo "Warning: ${@}/jails/.warden-template-pluginjail@clean not found"
exception=1
fi
fi
status=$exception
return $status
}
#restore a zfs volume and all datasets
function execRestore(){
#echo "Restoring ${drive} for ${dt} from ${back}/${drive}-${dt}..."
#test
echo "Restoring ${back}@${drive}-${dt} to ${drive}..."
#assume the backup for set date exists - the backup name is based on previous backup with inherited source volume name
zfs send -R ${back}@${drive}-${dt} | zfs receive -vF ${drive}/
status=$? #status from last function call
if [ $status == 0 ]; #worked
then
#take a new snapshot from restored data
takesnapshot ${drive}
status=$? #status from last function call
if [ $status == 0 ]; #snapshot successful
then
renamesnapshot ${drive} #rename previous snapshot on the destination volume to prevent accidental restore since the backup release superseeds this snapshot
status=$? #status from last function call
if [ $status == 0 ]; #successful rename
then
echo "about to delete snapshot of ${back}"
deletesnapshot ${back} #remove the snapshot from the backup drive since it's now been restored (not sure this is the correct approach)
status=$? #status from last function call
if [ $status == 0 ]; #successful rename
then
echo "about to delete dataset ${back}@${drive}-${dt}"
destroyDataset ${back}@${drive}-${dt}
status=$? #status from last function call
if [ $status == 0 ]; #successful rename
then
echo "check if ${drive}/jails/.warden-template-pluginjail@clean exists"
jailCleanExists ${drive}/jails/.warden-template-pluginjail@clean
status=$? #status from last function call
if [ $status == 1 ]; #@clean exists
then
echo "remove dependencies for ${back}/${drive}-${dt}/jails/.warden-template-pluginjail@clean"
removeJailDependencies ${back}/${drive}-${dt}
status=$? #status from last function call
if [ $status == 0 ]; #@cleaned
then
echo "Dependencies successfully removed for ${back}/${drive}-${dt}/jails/.warden-template-pluginjail@clean"
status=1
else
echo "Exception thrown while cleaning up dependent datasets for ${back}/${drive}-${dt}/jails/.warden-template-pluginjail@clean"
status=0
fi
else
status=1
fi
else
status=0
fi
fi
fi
fi
fi
return $status #status from last call returned (0=error, 1=success)
}
#main logic (backup or restore)
if [ "${drive}" == "" ];
then
error
else
if [ "${req}" == "backup" ];
then
doBackup $drive
else
if [ "${req}" == "restore" ];
then
doRestore $drive $dt
else
if [ "${req}" == "sync" ];
then
if [ "${force}" == "-f" ];
then
u="u"
fi
echo "WARNING: You are about to rsync /mnt/${drive} onto /mnt/${back}"
pause "Press [Enter]key to continue or CTRL+C to cancel"
RyncFromSourceToDetachedMediaBackup ${drive} ${u}
status=$? #status from last function call
if [ $status == 0 ]; #successful rename
then
echo "ok"
else
error
fi
else
if [ "${req}" == "syncrestore" ];
then
if [ "${force}" == "-f" ];
then
u="u"
fi
echo "WARNING: You are about to rsync /mnt/${back}/${drive}/ onto /mnt/${drive}"
pause "Press [Enter]key to continue or CTRL+C to cancel"
RsyncFromBackupToSourceRestore ${drive} ${u}
status=$? #status from last function call
if [ $status == 0 ]; #successful rename
then
echo "ok"
else
error
fi
else
error
fi
fi
fi
fi
fi
exit 0
#