#!/bin/sh
# smoothback.sh v.1.4
# http://smoothback.net
# Originally written and distributed by Dayid Alan <code@dayid.org>
# Published under the Pancake Public License v.1.0
# available at: http://code.dayid.org/ppl/ppl.txt
#
# This script is set to backup a remote system to your machine.
# Logs are stored in ${home}/logs.
##########################################################################################
# These variables definitely need to be modified:
# Set the remote machine or hostname:
bkup='CHANGEME'
# Set arguments for rsync to pass:
# -n is for --dry-run: this just shows what would be done.
# You should remove -n if you want this script to do something.
rsyncopt="-c -g -l -o -r --stats -t -u -z -n"
##########################################################################################
# These variables may need to be modified:
# Name for backups to be kept under:
name="${bkup}"
# 1=send e-mail, 0=do not
mailb="1"
# Subject for mail output:
mailsub="$0 output"
# Who to send the mail to: (current user by default)
mailuser=`id -un`
mailtmp="${HOME}/.$$"
# If you use hardlinks, along with the backup $name from above, you will end up having directories e.g.,:
# /usr/backup/hostname/ & /usr/backup/hostname.1/
# and so on, where as the number rises, the backup is older.
# Set the destination folders on the local machine (one dir, no trailing /)
path="/usr/backup/${name}"
# Set how many copies of backups to keep with hardlinks (0 means none)
numback="3"
# Set the directories to be synced: full path, space seperated, no trailing /
dir="/usr/home /etc /root"
# Set the user to login to the remote machine as: (current user by default)
user=`id -un`
# Set parent dir for logs/pid, no trailing /
home="${HOME}"
# Find where nice and rsync commands are located:
lnice=`which nice`
lrsync=`which rsync`
# Set how much to nice the rsync process (here, -19 nices to 19, *not* -19)
hownice="-19"
# Set number of runs to do before using the ${rsyncopt} (7 times by default)
# This does not apply if you are using hardlinks.
num="7" 
# Set the numer of lines to keep when cleaning out the logfile (5 by default)
keep="5"
# Set the location and name for the smoothback logfile: (~/logs/${bkup}.log by default)
logfile="${bkup}.log"
# No trailing /
logdir="${home}/logs"
pid="${home}/.${bkup}.pid"
##########################################################################################
# These variables should not need to be modified:
# Set the long rsync command:
prsync="${lnice} ${hownice} ${lrsync} ${rsyncopt}"
# Set arguments for rsync to pass on alt-time periods:
rsyncalt="--delete"
# Set the argument to pass to rsync for the hardlinks:
hl="--delete"
allowed=`expr ${num} + ${keep}`
log="${logdir}/${logfile}"
##########################################################################################
# You should not modify below this point.
clear >/dev/null 2>&1
numback=`expr "${numback}" - "1"`
num2="${numback}"
num3="${numback}"
num4="${numback}"
[ -d ${path} ] || mkdir -p ${path} # Check for path, create if needed
[ -d ${logdir} ] || mkdir -p ${logdir} 2> /dev/null
[ -f ${log} ] || touch ${log} # Check for logfile, create if needed
[ "${mailb}" -eq "1" ] || mailtmp="/dev/null" # If not using mail, set mailtmp to null
if [ -f ${pid} ] # Check for PID file
   then # Use the information from PID file to see if this script is running:
        proc=`awk '{ print $1 }' ${pid}` && runtest=`ps ax | awk '{ print $1 }' | fgrep ^${proc} | grep -v grep | wc -l`
        # If still running, fail and give warning; if dead, remove the pid file and restart
        [ "${runtest}" -gt "0" ] && echo "`date` Sync FAILED due to ${pid}" >> ${log} && exit 1 \
	|| rm -f ${pid} && clear && echo "Stale lockfile. Restarting in 3 seconds..." && sleep 3 && /bin/sh $0
   else echo $$ > ${pid} # Create PID file if needed
        echo "If you have a failure, you may need to manually delete ${home}/.$$"
        [ "${numback}" -ge "1" ] && rsyncalt="" || hl="" # Blank rsyncalt if using hardlinks. Blank hl if not. 
        while [ "${numback}" -ge "1" ]; do num2=`expr ${numback} - 1` # If numhl is less than 1, hardlinks are off, none of this section is processed.
              if [ "${numback}" -eq "1" ] # While the number is greater than or equal to one, do this loop:
                 # If the number is 1, then move path to path.1 - this should happen last.
                 then [ -d ${path} ] && rsync -au --delete --link-dest="${path}" ${path}/ ${path}.1/ && echo "Moving ${path}   to ${path}.1"
                 else [ -d ${path}.${num2} ] || mkdir -p ${path}.${num2} # If the next directory exists we continue. If not, we create it.
                      [ -d ${path}.${numback} ] || mkdir -p ${path}.${numback} # If the next directory exists, we continue. If not, we create it.
                      # We move the next-to-oldest directory to the oldest's position:
                      for location in ${dir}; do [ -d ${path}.${numback}${location} ] || mkdir -p ${path}.${numback}${location}; done
                      rsync -au --delete --link-dest="${path}" ${path}.${num2}/ ${path}.${numback}/ && echo "Moving ${path}.${num2} to ${path}.${numback}"
              fi
              numback="${num2}" # Set the next value for numback
        done
        # Using the logfile, see how many times the script has run with a regular sync:
        runtimes=`wc -l ${log} | awk '{ print $1 }'`
        # On the Xth time the script runs, use the ${rsyncalt} argument
        if [ "${runtimes}" -gt "${allowed}" ]
           # Set use to use the rsyncalt argument & rotate the log
           then use="${prsync} ${rsyncalt} ${hl}" && tail -n ${keep} ${log} > ${home}/$$ && mv ${home}/$$ ${log} && \
                echo "`date` -- Log Rotated --" >> ${log} && echo -n "`date` Core update with ${rsyncalt}:" >> ${log}
           # Set use to not use rsyncalt:
           else use="${prsync} ${hl}" && echo -n "`date` Core update:" >> ${log} 
        fi
        # For each directory to backup, do this:
        for place in ${dir}; do
        # Check for the path/dir to exist. If so, continue; if not, create it.
            [ -d ${path}${place} ] || mkdir -p ${path}${place} && echo "Creating ${path}${place}"
            # Now process each directory and echo the status out to the log.
            while [ "${num3}" -ge "1" ]; do hl2="${hl2} --link-dest="${path}.${num3}${place}"" && num3=`expr ${num3} - 1`; done
            echo "\n\n=========================================\nRESULTS FOR: ${place}:" 1>> ${mailtmp} 2>&1
            ${use} ${hl2} ${user}@${bkup}:${place}/ ${path}${place}/ 1>>${mailtmp} 2>&1 && echo -n "${place} successful " >> ${log} || echo -n "${place} FAILED " >> ${log}
	    num3="${num4}"; hl2=""
        done
        # Add a newline to the log file and put the date we completed at.
        echo "...complete @ `date`" >> ${log} 
	# If mail is on, send the mail:
        [ "${mailb}" -eq "1" ] && cat ${mailtmp} | mail -s "${mailsub}" ${mailuser}
        # Remove the pid file
        rm -f ${pid}; [ "${mailb}" -eq "1" ] && rm -f ${mailtmp}
fi

