kool_newt, 11 months ago I made my own bash script that uses rsync. I stopped using Github so here’s a paste lol. I define the backups like this, first item is source, other items on that line are it’s exclusions. <pre style="background-color:#ffffff;"> <span style="color:#323232;">/home/shared </span><span style="color:#323232;">/home/jamie tmp/ dj_music/ Car_Music_USB </span><span style="color:#323232;">/home/jamie_work </span><span style="color:#323232;"> </span> <pre style="background-color:#ffffff;"> <span style="color:#323232;">#!/usr/bin/ssh-agent /bin/bash </span><span style="color:#323232;"> </span><span style="color:#323232;"># chronicle.sh </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Get absolute directory chronicle.sh is in </span><span style="color:#323232;">REAL_PATH=`(cd $(dirname "$0"); pwd)` </span><span style="color:#323232;"> </span><span style="color:#323232;"># Defaults </span><span style="color:#323232;">BACKUP_DEF_FILE="${REAL_PATH}/backup.conf" </span><span style="color:#323232;">CONF_FILE="${REAL_PATH}/chronicle.conf" </span><span style="color:#323232;">FAIL_IF_PRE_FAILS='0' </span><span style="color:#323232;">FIXPERMS='true' </span><span style="color:#323232;">FORCE='false' </span><span style="color:#323232;">LOG_DIR='/var/log/chronicle' </span><span style="color:#323232;">LOG_PREFIX='chronicle' </span><span style="color:#323232;">NAME='backup' </span><span style="color:#323232;">PID_FILE='~/chronicle/chronicle.pid' </span><span style="color:#323232;">RSYNC_OPTS="-qRrltH --perms --delete --delete-excluded" </span><span style="color:#323232;">SSH_KEYFILE="${HOME}/.ssh/id_rsa" </span><span style="color:#323232;">TIMESTAMP='date +%Y%m%d-%T' </span><span style="color:#323232;"> </span><span style="color:#323232;"># Set PID file for root user </span><span style="color:#323232;">[ $EUID = 0 ] && PID_FILE='/var/run/chronicle.pid' </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Print an error message and exit </span><span style="color:#323232;">ERROUT () { </span><span style="color:#323232;"> TS="$(TS)" </span><span style="color:#323232;"> echo "$TS $LOG_PREFIX (error): $1" </span><span style="color:#323232;"> echo "$TS $LOG_PREFIX (error): Backup failed" </span><span style="color:#323232;"> rm -f "$PID_FILE" </span><span style="color:#323232;"> exit 1 </span><span style="color:#323232;">} </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Usage output </span><span style="color:#323232;">USAGE () { </span><span style="color:#323232;">cat << EOF </span><span style="color:#323232;">USAGE chronicle.sh [ OPTIONS ] </span><span style="color:#323232;"> </span><span style="color:#323232;">OPTIONS </span><span style="color:#323232;"> -f path configuration file (default: chronicle.conf) </span><span style="color:#323232;"> -F force overwrite incomplete backup </span><span style="color:#323232;"> -h display this help </span><span style="color:#323232;">EOF </span><span style="color:#323232;">exit 0 </span><span style="color:#323232;">} </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Timestamp </span><span style="color:#323232;">TS () </span><span style="color:#323232;">{ </span><span style="color:#323232;"> if </span><span style="color:#323232;"> echo $TIMESTAMP | grep tai64n &>/dev/null </span><span style="color:#323232;"> then </span><span style="color:#323232;"> echo "" | eval $TIMESTAMP </span><span style="color:#323232;"> else </span><span style="color:#323232;"> eval $TIMESTAMP </span><span style="color:#323232;"> fi </span><span style="color:#323232;">} </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Logger function </span><span style="color:#323232;"># First positional parameter is message severity (notice|warn|error) </span><span style="color:#323232;"># The log message can be the second positional parameter, stdin, or a HERE string </span><span style="color:#323232;">LOG () { </span><span style="color:#323232;"> local TS="$(TS)" </span><span style="color:#323232;"> # local input="" </span><span style="color:#323232;"> </span><span style="color:#323232;"> msg_type="$1" </span><span style="color:#323232;"> </span><span style="color:#323232;"> # if [[ -p /dev/stdin ]]; then </span><span style="color:#323232;"> # msg="$(cat -)" </span><span style="color:#323232;"> # else </span><span style="color:#323232;"> shift </span><span style="color:#323232;"> msg="${@}" </span><span style="color:#323232;"> # fi </span><span style="color:#323232;"> echo "$TS chronicle ("$msg_type"): $msg" </span><span style="color:#323232;">} </span><span style="color:#323232;"> </span><span style="color:#323232;"># Logger function </span><span style="color:#323232;"># First positional parameter is message severity (notice|warn|error) </span><span style="color:#323232;"># The log message canbe stdin or a HERE string </span><span style="color:#323232;">LOGPIPE () { </span><span style="color:#323232;"> local TS="$(TS)" </span><span style="color:#323232;"> msg_type="$1" </span><span style="color:#323232;"> msg="$(cat -)" </span><span style="color:#323232;"> echo "$TS chronicle ("$msg_type"): $msg" </span><span style="color:#323232;">} </span><span style="color:#323232;"> </span><span style="color:#323232;"># Process Options </span><span style="color:#323232;">while </span><span style="color:#323232;"> getopts ":d:f:Fmh" options; do </span><span style="color:#323232;"> case $options in </span><span style="color:#323232;"> d ) BACKUP_DEF_FILE="$OPTARG" ;; </span><span style="color:#323232;"> f ) CONF_FILE="$OPTARG" ;; </span><span style="color:#323232;"> F ) FORCE='true' ;; </span><span style="color:#323232;"> m ) FIXPERMS='false' ;; </span><span style="color:#323232;"> h ) USAGE; exit 0 ;; </span><span style="color:#323232;"> * ) USAGE; exit 1 ;; </span><span style="color:#323232;"> esac </span><span style="color:#323232;">done </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Ensure a configuration file is found </span><span style="color:#323232;">if </span><span style="color:#323232;"> [ "x${CONF_FILE}" = 'x' ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "Cannot find configuration file $CONF_FILE" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"># Read the config file </span><span style="color:#323232;">. "$CONF_FILE" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Set the owner and mode for backup files </span><span style="color:#323232;">if [ $FIXPERMS = 'true' ]; then </span><span style="color:#323232;">#FIXVAR="--chown=${SSH_USER}:${SSH_USER} --chmod=D770,F660" </span><span style="color:#323232;">FIXVAR="--usermap=*:${SSH_USER} --groupmap=*:${SSH_USER} --chmod=D770,F660" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Set up logging </span><span style="color:#323232;"> </span><span style="color:#323232;">if [ "${LOG_DIR}x" = 'x' ]; then </span><span style="color:#323232;"> ERROUT "(error): ${LOG_DIR} not specified" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;">mkdir -p "$LOG_DIR" </span><span style="color:#323232;">LOGFILE="${LOG_DIR}/chronicle.log" </span><span style="color:#323232;"> </span><span style="color:#323232;"># Make all output go to the log file </span><span style="color:#323232;">exec >> $LOGFILE 2>&1 </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Ensure a backup definitions file is found </span><span style="color:#323232;">if </span><span style="color:#323232;"> [ "x${BACKUP_DEF_FILE}" = 'x' ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "Cannot find backup definitions file $BACKUP_DEF_FILE" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Check for essential variables </span><span style="color:#323232;">VARS='BACKUP_SERVER SSH_USER BACKUP_DIR BACKUP_QTY NAME TIMESTAMP' </span><span style="color:#323232;">for var in $VARS; do </span><span style="color:#323232;"> if [ ${var}x = x ]; then </span><span style="color:#323232;"> ERROUT "${var} not specified" </span><span style="color:#323232;"> fi </span><span style="color:#323232;">done </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">LOG notice "Backup started, keeping $BACKUP_QTY snapshots with name "$NAME"" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Export variables for use with external scripts </span><span style="color:#323232;">export SSH_USER RSYNC_USER BACKUP_SERVER BACKUP_DIR LOG_DIR NAME REAL_PATH </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Check for PID </span><span style="color:#323232;">if </span><span style="color:#323232;"> [ -e "$PID_FILE" ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> LOG error "$PID_FILE exists" </span><span style="color:#323232;"> LOG error 'Backup failed' </span><span style="color:#323232;"> exit 1 </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"># Write PID </span><span style="color:#323232;">touch "$PID_FILE" </span><span style="color:#323232;"> </span><span style="color:#323232;"># Add key to SSH agent </span><span style="color:#323232;">ssh-add "$SSH_KEYFILE" 2>&1 | LOGPIPE notice - </span><span style="color:#323232;"> </span><span style="color:#323232;"># enhance script readability </span><span style="color:#323232;">CONN="${SSH_USER}@${BACKUP_SERVER}" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Make sure the SSH server is available </span><span style="color:#323232;">if </span><span style="color:#323232;"> ! ssh $CONN echo -n '' </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "$BACKUP_SERVER is unreachable" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Fail if ${NAME}.0.tmp is found on the backup server. </span><span style="color:#323232;">if </span><span style="color:#323232;"> ssh ${CONN} [ -e "${BACKUP_DIR}/${NAME}.0.tmp" ] && [ "$FORCE" = 'false' ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "${NAME}.0.tmp exists, ensure backup data is in order on the server" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Try to create the destination directory if it does not already exist </span><span style="color:#323232;">if </span><span style="color:#323232;"> ssh $CONN [ ! -d $BACKUP_DIR ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> if </span><span style="color:#323232;"> ssh $CONN mkdir -p "$BACKUP_DIR" </span><span style="color:#323232;"> ssh $CONN chown ${SSH_USER}:${SSH_USER} "$BACKUP_DIR" </span><span style="color:#323232;"> then : </span><span style="color:#323232;"> else </span><span style="color:#323232;"> ERROUT "Cannot create $BACKUP_DIR" </span><span style="color:#323232;"> fi </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"># Create metadata directory </span><span style="color:#323232;">ssh $CONN mkdir -p "$BACKUP_DIR/chronicle_metadata" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= </span><span style="color:#323232;"># PRE_COMMAND </span><span style="color:#323232;"> </span><span style="color:#323232;">if </span><span style="color:#323232;"> [ -n "$PRE_COMMAND" ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> LOG notice "Running ${PRE_COMMAND}" </span><span style="color:#323232;"> if </span><span style="color:#323232;"> $PRE_COMMAND </span><span style="color:#323232;"> then </span><span style="color:#323232;"> LOG notice "${PRE_COMMAND} complete" </span><span style="color:#323232;"> else </span><span style="color:#323232;"> LOG error "Execution of ${PRE_COMMAND} was not successful" </span><span style="color:#323232;"> if [ "$FAIL_IF_PRE_FAILS" -eq 1 ]; then </span><span style="color:#323232;"> ERROUT 'Command specified by PRE_COMMAND failed and FAIL_IF_PRE_FAILS enabled' </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> fi </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= </span><span style="color:#323232;"># Backup </span><span style="color:#323232;"> </span><span style="color:#323232;"># Make a hard link copy of backup.0 to rsync with </span><span style="color:#323232;">if [ $FORCE = 'false' ]; then </span><span style="color:#323232;"> ssh $CONN "[ -d ${BACKUP_DIR}/${NAME}.0 ] && cp -al ${BACKUP_DIR}/${NAME}.0 ${BACKUP_DIR}/${NAME}.0.tmp" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">while read -u 9 l; do </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Skip commented lines </span><span style="color:#323232;"> if [[ "$l" =~ ^#.* ]]; then </span><span style="color:#323232;"> continue </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> if [[ $l = '/*'* ]]; then </span><span style="color:#323232;"> LOG warn "$SOURCE is not an absolute path" </span><span style="color:#323232;"> continue </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Reduce whitespace to one tab </span><span style="color:#323232;"> line=$(echo $l | tr -s [:space:] 't') </span><span style="color:#323232;"> </span><span style="color:#323232;"> # get the source </span><span style="color:#323232;"> SOURCE=$(echo "$line" | cut -f1) </span><span style="color:#323232;"> </span><span style="color:#323232;"> # get the exclusions </span><span style="color:#323232;"> EXCLUSIONS=$(echo "$line" | cut -f2-) </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Format exclusions for the rsync command </span><span style="color:#323232;"> unset exclude_line </span><span style="color:#323232;"> if [ ! "$EXCLUSIONS" = '' ]; then </span><span style="color:#323232;"> for each in $EXCLUSIONS; do </span><span style="color:#323232;"> exclude_line="$exclude_line--exclude $each " </span><span style="color:#323232;"> done </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"> LOG notice "Using SSH transport for $SOURCE" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"> # get directory metadata </span><span style="color:#323232;"> PERMS="$(getfacl -pR "$SOURCE")" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Copy metadata </span><span style="color:#323232;"> ssh $CONN mkdir -p ${BACKUP_DIR}/chronicle_metadata/${SOURCE} </span><span style="color:#323232;"> echo "$PERMS" | ssh $CONN -T "cat > ${BACKUP_DIR}/chronicle_metadata/${SOURCE}/metadata" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"> LOG debug "rsync $RSYNC_OPTS $exclude_line "$FIXVAR" "$SOURCE" </span><span style="color:#323232;"> "${SSH_USER}"@"$BACKUP_SERVER":"${BACKUP_DIR}/${NAME}.0.tmp"" </span><span style="color:#323232;"> </span><span style="color:#323232;"> rsync $RSYNC_OPTS $exclude_line $FIXVAR "$SOURCE" </span><span style="color:#323232;"> "${SSH_USER}"@"$BACKUP_SERVER":"${BACKUP_DIR}/${NAME}.0.tmp" </span><span style="color:#323232;"> </span><span style="color:#323232;">done 9< "${BACKUP_DEF_FILE}" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= </span><span style="color:#323232;"># Try to see if the backup succeeded </span><span style="color:#323232;"> </span><span style="color:#323232;">if </span><span style="color:#323232;"> ssh $CONN [ ! -d "${BACKUP_DIR}/${NAME}.0.tmp" ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "${BACKUP_DIR}/${NAME}.0.tmp not found, no new backup created" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Test for empty temp directory </span><span style="color:#323232;">if </span><span style="color:#323232;"> ssh $CONN [ ! -z "$(ls -A ${BACKUP_DIR}/${NAME}.0.tmp 2>/dev/null)" ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "No new backup created" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= </span><span style="color:#323232;"># Rotate </span><span style="color:#323232;"> </span><span style="color:#323232;"># Number of oldest backup </span><span style="color:#323232;">X=`expr $BACKUP_QTY - 1` </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">LOG notice 'Rotating previous backups' </span><span style="color:#323232;"> </span><span style="color:#323232;"># keep oldest directory temporarily in case rotation fails </span><span style="color:#323232;">ssh $CONN [ -d "${BACKUP_DIR}/${NAME}.${X}" ] && </span><span style="color:#323232;">ssh $CONN mv "${BACKUP_DIR}/${NAME}.${X}" "${BACKUP_DIR}/${NAME}.${X}.tmp" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Rotate previous backups </span><span style="color:#323232;">until [ $X -eq -1 ]; do </span><span style="color:#323232;"> Y=$X </span><span style="color:#323232;"> X=`expr $X - 1` </span><span style="color:#323232;"> </span><span style="color:#323232;"> ssh $CONN [ -d "${BACKUP_DIR}/${NAME}.${X}" ] && </span><span style="color:#323232;"> ssh $CONN mv "${BACKUP_DIR}/${NAME}.${X}" "${BACKUP_DIR}/${NAME}.${Y}" </span><span style="color:#323232;"> [ $X -eq 0 ] && break </span><span style="color:#323232;">done </span><span style="color:#323232;"> </span><span style="color:#323232;"># Create "backup.0" directory </span><span style="color:#323232;">ssh $CONN mkdir -p "${BACKUP_DIR}/${NAME}.0" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Get individual items in "backup.0.tmp" directory into "backup.0" </span><span style="color:#323232;"># so that items removed from backup definitions rotate out </span><span style="color:#323232;">while read -u 9 l; do </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Skip commented lines </span><span style="color:#323232;"> if [[ "$l" =~ ^#.* ]]; then </span><span style="color:#323232;"> continue </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Skip invalid sources that are not an absolute path" </span><span style="color:#323232;"> if [[ $l = '/*'* ]]; then </span><span style="color:#323232;"> continue </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Reduce multiple tabs to one </span><span style="color:#323232;"> line=$(echo $l | tr -s [:space:] 't') </span><span style="color:#323232;"> </span><span style="color:#323232;"> source=$(echo "$line" | cut -f1) </span><span style="color:#323232;"> </span><span style="color:#323232;"> source_basedir="$(dirname $source)" </span><span style="color:#323232;"> </span><span style="color:#323232;"> ssh $CONN mkdir -p "${BACKUP_DIR}/${NAME}.0/${source_basedir}" </span><span style="color:#323232;"> </span><span style="color:#323232;"> LOG debug "ssh $CONN cp -al "${BACKUP_DIR}/${NAME}.0.tmp${source}" "${BACKUP_DIR}/${NAME}.0${source_basedir}"" </span><span style="color:#323232;"> </span><span style="color:#323232;"> ssh $CONN cp -al "${BACKUP_DIR}/${NAME}.0.tmp${source}" "${BACKUP_DIR}/${NAME}.0${source_basedir}" </span><span style="color:#323232;"> </span><span style="color:#323232;">done 9< "${BACKUP_DEF_FILE}" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Remove oldest backup </span><span style="color:#323232;">X=`expr $BACKUP_QTY - 1` # Number of oldest backup </span><span style="color:#323232;">ssh $CONN rm -Rf "${BACKUP_DIR}/${NAME}.${X}.tmp" </span><span style="color:#323232;"> </span><span style="color:#323232;"># Set time stamp on backup directory </span><span style="color:#323232;">ssh $CONN touch -m "${BACKUP_DIR}/${NAME}.0" </span><span style="color:#323232;"> </span><span style="color:#323232;"># Delete temp copy of backup </span><span style="color:#323232;">ssh $CONN rm -Rf "${BACKUP_DIR}/${NAME}.0.tmp" </span><span style="color:#323232;"> </span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= </span><span style="color:#323232;"># Post Command </span><span style="color:#323232;"> </span><span style="color:#323232;">if </span><span style="color:#323232;"> [ ! "${POST_COMMAND}x" = 'x' ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> LOG notice "Running ${POST_COMMAND}" </span><span style="color:#323232;"> if </span><span style="color:#323232;"> $POST_COMMAND </span><span style="color:#323232;"> then </span><span style="color:#323232;"> LOG notice "${POST_COMMAND} complete" </span><span style="color:#323232;"> else </span><span style="color:#323232;"> LOG warning "${POST_COMMAND} complete with errors" </span><span style="color:#323232;"> fi </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"># Delete PID file </span><span style="color:#323232;">rm -f "$PID_FILE" </span><span style="color:#323232;"> </span><span style="color:#323232;"># Log success message </span><span style="color:#323232;">LOG notice 'Backup completed successfully' </span><span style="color:#323232;"> </span>
I made my own bash script that uses rsync. I stopped using Github so here’s a paste lol.
I define the backups like this, first item is source, other items on that line are it’s exclusions.
<pre style="background-color:#ffffff;"> <span style="color:#323232;">/home/shared </span><span style="color:#323232;">/home/jamie tmp/ dj_music/ Car_Music_USB </span><span style="color:#323232;">/home/jamie_work </span><span style="color:#323232;"> </span>
<pre style="background-color:#ffffff;"> <span style="color:#323232;">#!/usr/bin/ssh-agent /bin/bash </span><span style="color:#323232;"> </span><span style="color:#323232;"># chronicle.sh </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Get absolute directory chronicle.sh is in </span><span style="color:#323232;">REAL_PATH=`(cd $(dirname "$0"); pwd)` </span><span style="color:#323232;"> </span><span style="color:#323232;"># Defaults </span><span style="color:#323232;">BACKUP_DEF_FILE="${REAL_PATH}/backup.conf" </span><span style="color:#323232;">CONF_FILE="${REAL_PATH}/chronicle.conf" </span><span style="color:#323232;">FAIL_IF_PRE_FAILS='0' </span><span style="color:#323232;">FIXPERMS='true' </span><span style="color:#323232;">FORCE='false' </span><span style="color:#323232;">LOG_DIR='/var/log/chronicle' </span><span style="color:#323232;">LOG_PREFIX='chronicle' </span><span style="color:#323232;">NAME='backup' </span><span style="color:#323232;">PID_FILE='~/chronicle/chronicle.pid' </span><span style="color:#323232;">RSYNC_OPTS="-qRrltH --perms --delete --delete-excluded" </span><span style="color:#323232;">SSH_KEYFILE="${HOME}/.ssh/id_rsa" </span><span style="color:#323232;">TIMESTAMP='date +%Y%m%d-%T' </span><span style="color:#323232;"> </span><span style="color:#323232;"># Set PID file for root user </span><span style="color:#323232;">[ $EUID = 0 ] && PID_FILE='/var/run/chronicle.pid' </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Print an error message and exit </span><span style="color:#323232;">ERROUT () { </span><span style="color:#323232;"> TS="$(TS)" </span><span style="color:#323232;"> echo "$TS $LOG_PREFIX (error): $1" </span><span style="color:#323232;"> echo "$TS $LOG_PREFIX (error): Backup failed" </span><span style="color:#323232;"> rm -f "$PID_FILE" </span><span style="color:#323232;"> exit 1 </span><span style="color:#323232;">} </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Usage output </span><span style="color:#323232;">USAGE () { </span><span style="color:#323232;">cat << EOF </span><span style="color:#323232;">USAGE chronicle.sh [ OPTIONS ] </span><span style="color:#323232;"> </span><span style="color:#323232;">OPTIONS </span><span style="color:#323232;"> -f path configuration file (default: chronicle.conf) </span><span style="color:#323232;"> -F force overwrite incomplete backup </span><span style="color:#323232;"> -h display this help </span><span style="color:#323232;">EOF </span><span style="color:#323232;">exit 0 </span><span style="color:#323232;">} </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Timestamp </span><span style="color:#323232;">TS () </span><span style="color:#323232;">{ </span><span style="color:#323232;"> if </span><span style="color:#323232;"> echo $TIMESTAMP | grep tai64n &>/dev/null </span><span style="color:#323232;"> then </span><span style="color:#323232;"> echo "" | eval $TIMESTAMP </span><span style="color:#323232;"> else </span><span style="color:#323232;"> eval $TIMESTAMP </span><span style="color:#323232;"> fi </span><span style="color:#323232;">} </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Logger function </span><span style="color:#323232;"># First positional parameter is message severity (notice|warn|error) </span><span style="color:#323232;"># The log message can be the second positional parameter, stdin, or a HERE string </span><span style="color:#323232;">LOG () { </span><span style="color:#323232;"> local TS="$(TS)" </span><span style="color:#323232;"> # local input="" </span><span style="color:#323232;"> </span><span style="color:#323232;"> msg_type="$1" </span><span style="color:#323232;"> </span><span style="color:#323232;"> # if [[ -p /dev/stdin ]]; then </span><span style="color:#323232;"> # msg="$(cat -)" </span><span style="color:#323232;"> # else </span><span style="color:#323232;"> shift </span><span style="color:#323232;"> msg="${@}" </span><span style="color:#323232;"> # fi </span><span style="color:#323232;"> echo "$TS chronicle ("$msg_type"): $msg" </span><span style="color:#323232;">} </span><span style="color:#323232;"> </span><span style="color:#323232;"># Logger function </span><span style="color:#323232;"># First positional parameter is message severity (notice|warn|error) </span><span style="color:#323232;"># The log message canbe stdin or a HERE string </span><span style="color:#323232;">LOGPIPE () { </span><span style="color:#323232;"> local TS="$(TS)" </span><span style="color:#323232;"> msg_type="$1" </span><span style="color:#323232;"> msg="$(cat -)" </span><span style="color:#323232;"> echo "$TS chronicle ("$msg_type"): $msg" </span><span style="color:#323232;">} </span><span style="color:#323232;"> </span><span style="color:#323232;"># Process Options </span><span style="color:#323232;">while </span><span style="color:#323232;"> getopts ":d:f:Fmh" options; do </span><span style="color:#323232;"> case $options in </span><span style="color:#323232;"> d ) BACKUP_DEF_FILE="$OPTARG" ;; </span><span style="color:#323232;"> f ) CONF_FILE="$OPTARG" ;; </span><span style="color:#323232;"> F ) FORCE='true' ;; </span><span style="color:#323232;"> m ) FIXPERMS='false' ;; </span><span style="color:#323232;"> h ) USAGE; exit 0 ;; </span><span style="color:#323232;"> * ) USAGE; exit 1 ;; </span><span style="color:#323232;"> esac </span><span style="color:#323232;">done </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Ensure a configuration file is found </span><span style="color:#323232;">if </span><span style="color:#323232;"> [ "x${CONF_FILE}" = 'x' ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "Cannot find configuration file $CONF_FILE" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"># Read the config file </span><span style="color:#323232;">. "$CONF_FILE" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Set the owner and mode for backup files </span><span style="color:#323232;">if [ $FIXPERMS = 'true' ]; then </span><span style="color:#323232;">#FIXVAR="--chown=${SSH_USER}:${SSH_USER} --chmod=D770,F660" </span><span style="color:#323232;">FIXVAR="--usermap=*:${SSH_USER} --groupmap=*:${SSH_USER} --chmod=D770,F660" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Set up logging </span><span style="color:#323232;"> </span><span style="color:#323232;">if [ "${LOG_DIR}x" = 'x' ]; then </span><span style="color:#323232;"> ERROUT "(error): ${LOG_DIR} not specified" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;">mkdir -p "$LOG_DIR" </span><span style="color:#323232;">LOGFILE="${LOG_DIR}/chronicle.log" </span><span style="color:#323232;"> </span><span style="color:#323232;"># Make all output go to the log file </span><span style="color:#323232;">exec >> $LOGFILE 2>&1 </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Ensure a backup definitions file is found </span><span style="color:#323232;">if </span><span style="color:#323232;"> [ "x${BACKUP_DEF_FILE}" = 'x' ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "Cannot find backup definitions file $BACKUP_DEF_FILE" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Check for essential variables </span><span style="color:#323232;">VARS='BACKUP_SERVER SSH_USER BACKUP_DIR BACKUP_QTY NAME TIMESTAMP' </span><span style="color:#323232;">for var in $VARS; do </span><span style="color:#323232;"> if [ ${var}x = x ]; then </span><span style="color:#323232;"> ERROUT "${var} not specified" </span><span style="color:#323232;"> fi </span><span style="color:#323232;">done </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">LOG notice "Backup started, keeping $BACKUP_QTY snapshots with name "$NAME"" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Export variables for use with external scripts </span><span style="color:#323232;">export SSH_USER RSYNC_USER BACKUP_SERVER BACKUP_DIR LOG_DIR NAME REAL_PATH </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Check for PID </span><span style="color:#323232;">if </span><span style="color:#323232;"> [ -e "$PID_FILE" ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> LOG error "$PID_FILE exists" </span><span style="color:#323232;"> LOG error 'Backup failed' </span><span style="color:#323232;"> exit 1 </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"># Write PID </span><span style="color:#323232;">touch "$PID_FILE" </span><span style="color:#323232;"> </span><span style="color:#323232;"># Add key to SSH agent </span><span style="color:#323232;">ssh-add "$SSH_KEYFILE" 2>&1 | LOGPIPE notice - </span><span style="color:#323232;"> </span><span style="color:#323232;"># enhance script readability </span><span style="color:#323232;">CONN="${SSH_USER}@${BACKUP_SERVER}" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Make sure the SSH server is available </span><span style="color:#323232;">if </span><span style="color:#323232;"> ! ssh $CONN echo -n '' </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "$BACKUP_SERVER is unreachable" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Fail if ${NAME}.0.tmp is found on the backup server. </span><span style="color:#323232;">if </span><span style="color:#323232;"> ssh ${CONN} [ -e "${BACKUP_DIR}/${NAME}.0.tmp" ] && [ "$FORCE" = 'false' ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "${NAME}.0.tmp exists, ensure backup data is in order on the server" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Try to create the destination directory if it does not already exist </span><span style="color:#323232;">if </span><span style="color:#323232;"> ssh $CONN [ ! -d $BACKUP_DIR ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> if </span><span style="color:#323232;"> ssh $CONN mkdir -p "$BACKUP_DIR" </span><span style="color:#323232;"> ssh $CONN chown ${SSH_USER}:${SSH_USER} "$BACKUP_DIR" </span><span style="color:#323232;"> then : </span><span style="color:#323232;"> else </span><span style="color:#323232;"> ERROUT "Cannot create $BACKUP_DIR" </span><span style="color:#323232;"> fi </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"># Create metadata directory </span><span style="color:#323232;">ssh $CONN mkdir -p "$BACKUP_DIR/chronicle_metadata" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= </span><span style="color:#323232;"># PRE_COMMAND </span><span style="color:#323232;"> </span><span style="color:#323232;">if </span><span style="color:#323232;"> [ -n "$PRE_COMMAND" ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> LOG notice "Running ${PRE_COMMAND}" </span><span style="color:#323232;"> if </span><span style="color:#323232;"> $PRE_COMMAND </span><span style="color:#323232;"> then </span><span style="color:#323232;"> LOG notice "${PRE_COMMAND} complete" </span><span style="color:#323232;"> else </span><span style="color:#323232;"> LOG error "Execution of ${PRE_COMMAND} was not successful" </span><span style="color:#323232;"> if [ "$FAIL_IF_PRE_FAILS" -eq 1 ]; then </span><span style="color:#323232;"> ERROUT 'Command specified by PRE_COMMAND failed and FAIL_IF_PRE_FAILS enabled' </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> fi </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= </span><span style="color:#323232;"># Backup </span><span style="color:#323232;"> </span><span style="color:#323232;"># Make a hard link copy of backup.0 to rsync with </span><span style="color:#323232;">if [ $FORCE = 'false' ]; then </span><span style="color:#323232;"> ssh $CONN "[ -d ${BACKUP_DIR}/${NAME}.0 ] && cp -al ${BACKUP_DIR}/${NAME}.0 ${BACKUP_DIR}/${NAME}.0.tmp" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">while read -u 9 l; do </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Skip commented lines </span><span style="color:#323232;"> if [[ "$l" =~ ^#.* ]]; then </span><span style="color:#323232;"> continue </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> if [[ $l = '/*'* ]]; then </span><span style="color:#323232;"> LOG warn "$SOURCE is not an absolute path" </span><span style="color:#323232;"> continue </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Reduce whitespace to one tab </span><span style="color:#323232;"> line=$(echo $l | tr -s [:space:] 't') </span><span style="color:#323232;"> </span><span style="color:#323232;"> # get the source </span><span style="color:#323232;"> SOURCE=$(echo "$line" | cut -f1) </span><span style="color:#323232;"> </span><span style="color:#323232;"> # get the exclusions </span><span style="color:#323232;"> EXCLUSIONS=$(echo "$line" | cut -f2-) </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Format exclusions for the rsync command </span><span style="color:#323232;"> unset exclude_line </span><span style="color:#323232;"> if [ ! "$EXCLUSIONS" = '' ]; then </span><span style="color:#323232;"> for each in $EXCLUSIONS; do </span><span style="color:#323232;"> exclude_line="$exclude_line--exclude $each " </span><span style="color:#323232;"> done </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"> LOG notice "Using SSH transport for $SOURCE" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"> # get directory metadata </span><span style="color:#323232;"> PERMS="$(getfacl -pR "$SOURCE")" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Copy metadata </span><span style="color:#323232;"> ssh $CONN mkdir -p ${BACKUP_DIR}/chronicle_metadata/${SOURCE} </span><span style="color:#323232;"> echo "$PERMS" | ssh $CONN -T "cat > ${BACKUP_DIR}/chronicle_metadata/${SOURCE}/metadata" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"> LOG debug "rsync $RSYNC_OPTS $exclude_line "$FIXVAR" "$SOURCE" </span><span style="color:#323232;"> "${SSH_USER}"@"$BACKUP_SERVER":"${BACKUP_DIR}/${NAME}.0.tmp"" </span><span style="color:#323232;"> </span><span style="color:#323232;"> rsync $RSYNC_OPTS $exclude_line $FIXVAR "$SOURCE" </span><span style="color:#323232;"> "${SSH_USER}"@"$BACKUP_SERVER":"${BACKUP_DIR}/${NAME}.0.tmp" </span><span style="color:#323232;"> </span><span style="color:#323232;">done 9< "${BACKUP_DEF_FILE}" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= </span><span style="color:#323232;"># Try to see if the backup succeeded </span><span style="color:#323232;"> </span><span style="color:#323232;">if </span><span style="color:#323232;"> ssh $CONN [ ! -d "${BACKUP_DIR}/${NAME}.0.tmp" ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "${BACKUP_DIR}/${NAME}.0.tmp not found, no new backup created" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Test for empty temp directory </span><span style="color:#323232;">if </span><span style="color:#323232;"> ssh $CONN [ ! -z "$(ls -A ${BACKUP_DIR}/${NAME}.0.tmp 2>/dev/null)" ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> ERROUT "No new backup created" </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= </span><span style="color:#323232;"># Rotate </span><span style="color:#323232;"> </span><span style="color:#323232;"># Number of oldest backup </span><span style="color:#323232;">X=`expr $BACKUP_QTY - 1` </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;">LOG notice 'Rotating previous backups' </span><span style="color:#323232;"> </span><span style="color:#323232;"># keep oldest directory temporarily in case rotation fails </span><span style="color:#323232;">ssh $CONN [ -d "${BACKUP_DIR}/${NAME}.${X}" ] && </span><span style="color:#323232;">ssh $CONN mv "${BACKUP_DIR}/${NAME}.${X}" "${BACKUP_DIR}/${NAME}.${X}.tmp" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Rotate previous backups </span><span style="color:#323232;">until [ $X -eq -1 ]; do </span><span style="color:#323232;"> Y=$X </span><span style="color:#323232;"> X=`expr $X - 1` </span><span style="color:#323232;"> </span><span style="color:#323232;"> ssh $CONN [ -d "${BACKUP_DIR}/${NAME}.${X}" ] && </span><span style="color:#323232;"> ssh $CONN mv "${BACKUP_DIR}/${NAME}.${X}" "${BACKUP_DIR}/${NAME}.${Y}" </span><span style="color:#323232;"> [ $X -eq 0 ] && break </span><span style="color:#323232;">done </span><span style="color:#323232;"> </span><span style="color:#323232;"># Create "backup.0" directory </span><span style="color:#323232;">ssh $CONN mkdir -p "${BACKUP_DIR}/${NAME}.0" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Get individual items in "backup.0.tmp" directory into "backup.0" </span><span style="color:#323232;"># so that items removed from backup definitions rotate out </span><span style="color:#323232;">while read -u 9 l; do </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Skip commented lines </span><span style="color:#323232;"> if [[ "$l" =~ ^#.* ]]; then </span><span style="color:#323232;"> continue </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Skip invalid sources that are not an absolute path" </span><span style="color:#323232;"> if [[ $l = '/*'* ]]; then </span><span style="color:#323232;"> continue </span><span style="color:#323232;"> fi </span><span style="color:#323232;"> </span><span style="color:#323232;"> # Reduce multiple tabs to one </span><span style="color:#323232;"> line=$(echo $l | tr -s [:space:] 't') </span><span style="color:#323232;"> </span><span style="color:#323232;"> source=$(echo "$line" | cut -f1) </span><span style="color:#323232;"> </span><span style="color:#323232;"> source_basedir="$(dirname $source)" </span><span style="color:#323232;"> </span><span style="color:#323232;"> ssh $CONN mkdir -p "${BACKUP_DIR}/${NAME}.0/${source_basedir}" </span><span style="color:#323232;"> </span><span style="color:#323232;"> LOG debug "ssh $CONN cp -al "${BACKUP_DIR}/${NAME}.0.tmp${source}" "${BACKUP_DIR}/${NAME}.0${source_basedir}"" </span><span style="color:#323232;"> </span><span style="color:#323232;"> ssh $CONN cp -al "${BACKUP_DIR}/${NAME}.0.tmp${source}" "${BACKUP_DIR}/${NAME}.0${source_basedir}" </span><span style="color:#323232;"> </span><span style="color:#323232;">done 9< "${BACKUP_DEF_FILE}" </span><span style="color:#323232;"> </span><span style="color:#323232;"> </span><span style="color:#323232;"># Remove oldest backup </span><span style="color:#323232;">X=`expr $BACKUP_QTY - 1` # Number of oldest backup </span><span style="color:#323232;">ssh $CONN rm -Rf "${BACKUP_DIR}/${NAME}.${X}.tmp" </span><span style="color:#323232;"> </span><span style="color:#323232;"># Set time stamp on backup directory </span><span style="color:#323232;">ssh $CONN touch -m "${BACKUP_DIR}/${NAME}.0" </span><span style="color:#323232;"> </span><span style="color:#323232;"># Delete temp copy of backup </span><span style="color:#323232;">ssh $CONN rm -Rf "${BACKUP_DIR}/${NAME}.0.tmp" </span><span style="color:#323232;"> </span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= </span><span style="color:#323232;"># Post Command </span><span style="color:#323232;"> </span><span style="color:#323232;">if </span><span style="color:#323232;"> [ ! "${POST_COMMAND}x" = 'x' ] </span><span style="color:#323232;">then </span><span style="color:#323232;"> LOG notice "Running ${POST_COMMAND}" </span><span style="color:#323232;"> if </span><span style="color:#323232;"> $POST_COMMAND </span><span style="color:#323232;"> then </span><span style="color:#323232;"> LOG notice "${POST_COMMAND} complete" </span><span style="color:#323232;"> else </span><span style="color:#323232;"> LOG warning "${POST_COMMAND} complete with errors" </span><span style="color:#323232;"> fi </span><span style="color:#323232;">fi </span><span style="color:#323232;"> </span><span style="color:#323232;"># Delete PID file </span><span style="color:#323232;">rm -f "$PID_FILE" </span><span style="color:#323232;"> </span><span style="color:#323232;"># Log success message </span><span style="color:#323232;">LOG notice 'Backup completed successfully' </span><span style="color:#323232;"> </span>