#!/bin/sh

# ClonePanel - Manages duplicate accounts on two or more webservers,
# including snapshot backups, monitoring and failover dns.
# Copyright (C)2006 Chris Cheers, Internet Lynx.
# Contact chris[at]clonepanel[dot]com.
# Internet Lynx, PO Box 7117, Mannering Park, NSW 2259, Australia
#
# Credits:
# This script is based in no small part on Mike Rubel's excellent tutorial
# http://www.mikerubel.org/computers/rsync_snapshots/

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

Version=0.33
# backup -h for built-in documentation

set -u
# Be strict about variable declaration

unset PATH
#avoid use of $PATH - limit script to system commands we choose

PROGRAM_DIR=$(/bin/echo $0 |/bin/sed -e "s/\/[a-zA-Z0-9_]*$//")
# Extract the working directory from command line
# NB - if your system doesn't have echo and sed in these locations
# then this line will need to be changed in all shell scripts

# Standard include files:
. $PROGRAM_DIR/includes

# Built-in documentation:
function showdocs {
	$CAT <<-EODOC
		Backup web or e-mail documents from a host in clonepanel system.
		Both accounts must already be set up using setup_account
		Similar to sync except the host to backup from can be specified
		and nothing is synced back up to a slave host 
	
		Command line options: 
		-h		Print these instructions and exit
		-H HOSTNAME	Hostname (nickname) to backup from
		-t WEB|MAIL	Type of documents to sync (web or e-mail)
		-u user		CPanel username of the account
		-c mysql40	(optional) Use this compatibility mode for mysql dump
		

EODOC
	exit $DOC_REQUEST
}

function setup_mirror_dirs {

	case "$stype" in
		WEB)
# define variables for web sync (we can't combine this with the earlier
# case because MIRROR_DIRS may be host dependent)
			MIRROR_DIRS[0]=$MIRROR_DIR_WEB
			MIRROR_SS[0]=$MIRROR_SS_WEB
			EXCLUDES=$USER_DIR/$EXCLUDESFILE_WEB
			NOBACKUPS=$USER_DIR/$NOBACKUPFILE_WEB
			if [ "`$LS $USER_DIR/$DATABASE_DIRNAME`" ]; then
				MIRROR_DIRS[1]=$DATABASE_STORENAME
				MIRROR_SS[1]=$DATABASE_STORENAME
			fi
			;;
		MAIL)
# define variables for mail sync			
			MIRROR_DIRS[0]=$MIRROR_DIR_MAIL
			MIRROR_SS[0]=$MIRROR_SS_MAIL
			EXCLUDES=$USER_DIR/$EXCLUDESFILE_MAIL
			NOBACKUPS=$USER_DIR/$NOBACKUPFILE_MAIL
			;;
	esac
	mirror_dir_count=${#MIRROR_DIRS[@]}
}
function check_remote_error {
# Called with arguments status ($?) and error_code, check for errors in remote script
	status=$1
	ecode=$2
#	if [ -f "$TEMP_SYNC_RES_FILE" ]; then
# Display result for user
#		$CAT $TEMP_SYNC_RES_FILE
#	fi
	checkerror $status $ecode
# This should stop the sync if an error's found, but...

# Unfortunately CPanel jailshell doesn't return status code correctly so
# we should by now have trapped the output to check for errors...
	if [ -f "$TEMP_SYNC_RES_FILE" ]; then
		if [ "`$GREP "$errorcode" $TEMP_SYNC_RES_FILE`" ]; then
# Search for the error colour code in the output file
			cecho -c $error "Remote error $ecode - exiting"
			exit $ecode
		fi
# Finally, remove temp file
#		$RM $TEMP_SYNC_RES_FILE
	else
# If we didn't trap it, that's an error anyway
		cecho -c $error "Error $ecode - No sync result file found"
		exit $ecode
	fi
}

# Initialise own input variables:
stype='WEB'
user=''
compat=''
hostname=''

# Initialise other variables:
status=0

# Start real script


# Check for help request and input variables in the command line options:
while getopts ":ht:H:u:c:" opt
do
	case $opt in
		h)	showdocs
			;;
		H)	hostname=$OPTARG
			;;
		t)	stype=$OPTARG
			;;
		u)	user=$OPTARG
			;;
		c)	compat=$OPTARG
			;;
		*)	$ECHO "Unknown option. Use -h for instructions"; exit $E_UNKNOWN_OPT
			;;
	esac
done
shift $(($OPTIND - 1))

OPTIND=1
# reset getopts for next time

if [ ! $user -o ! $stype ]; then
	cecho -c $error "Username and sync type must be specified"
	exit $E_BLANK_INPUTS
fi


# User-specific Include files (only after defining $user):
config -u $user -t $stype

if [ ! $hostname ]; then
	case "$stype" in
		WEB)
# use default for web sync			
			hostname=$WEB_MASTER ;
			cecho -c $warning "Host not specified - defaults to WEB_MASTER which is $hostname"
			;;
		MAIL)
# use default for mail sync			
			hostname=MAIL_SLAVE ;
			cecho -c $warning "Host not specified - defaults to MAIL_SLAVE which is $host"
# NB - for mail, we collect occasional messages received on the secondary MX and transfer
# to the primary server (MAIL_MASTER) - hence reversal of push / pull
			;;
		*)
			cecho -c $error "Unknown type"
			exit 1 ;
			;;
	esac
fi

if [ "$compat" ]; then
	compat="-c $compat"
	# mysql dump compatibility mode
fi


cecho -c $ok "$($DATE) - Starting backup"

cecho -c $info "Phase A - Preparing directories"

# First check that we have a host to pull data from - if not don't rotate snapshots

if [ ! $hostname ]; then
	cecho -c $error "No host found - cannot continue"
	exit $E_NO_PULLFROM_HOST
fi

# Host-specific Include files (only after defining $hostname):
config -H $hostname

ip_pull=$A
if [ ! "$ip_pull" ]; then
	cecho -c $error "Host $hostname has no ip - check host config file"
	exit $E_NO_PULLFROM_IP
fi

username_pull=$REMOTEHOST_USERNAME
# username on remote host (usually same as account name)

remote_home_pull=$REMOTEHOST_HOME
#remote directory (normally /home/username)
if [ ! $remote_home_pull ]; then
	cecho -c $error "Host $hostname has no remote dir - check remote_dir file"
	exit $E_NO_PULLFROM_DIR
fi

setup_mirror_dirs
# Create the list of directories to backup / mirror
# (may depend on pull host config)

cecho -c $info "Phase B - Rotating snapshots of $MIRROR_DIRS"

# step B1: delete the oldest hourly snapshot, if it exists:
if [ -d $SNAPSHOT_SUBDIR/hourly.$NO_SNAPSHOTS ] ; then
	$RM -rf $SNAPSHOT_SUBDIR/hourly.$NO_SNAPSHOTS ;
fi ;
	
# step B2: shift the middle snapshots(s) back by one, if they exist as far as 1->2

for (( i=$NO_SNAPSHOTS; i > 1; i-- ))
do
	j=$($EXPR $i - 1) ;
	if [ -d $SNAPSHOT_SUBDIR/hourly.$j ] ; then
		$MV $SNAPSHOT_SUBDIR/hourly.$j $SNAPSHOT_SUBDIR/hourly.$i
	fi
done
	
# step B3: make a hard-link-only (except for dirs) copy of the current mirror dir.
# NB - newer alternative method using --link-dest flag on rsync seems to
# involve a lot more traffic on the rsync link...
if [ -d $SNAPSHOT_SUBDIR/hourly.0 ] ; then
	$CP -al $SNAPSHOT_SUBDIR/hourly.0 $SNAPSHOT_SUBDIR/hourly.1 ;
else 
	$MKDIR $SNAPSHOT_SUBDIR/hourly.0
fi;

cecho -c $info "Phase C - rsync 'pull' from the remote host ($hostname)"

remote_ssh_pull="$SSH -p $SSH_PORT -i $AUTH_DIR/$hostname";
# how to authenticate with remote domain (restricted password-less key system)

# Step C1 - Pre-process
$remote_ssh_pull $username_pull@$ip_pull "pre_process -f -t $stype -d PULL $compat" >$TEMP_SYNC_RES_FILE
# Option -f (force) to permit backup from slave
check_remote_error $? $E_PRE_PULL_FAILED
$CAT $TEMP_SYNC_RES_FILE

# Step C2 - Sync
$TOUCH $SNAPSHOT_SUBDIR/hourly.0

for (( i = 0 ; i < $mirror_dir_count ; i++ ))
do
	dir=${MIRROR_DIRS[$i]}
	ssdir=${MIRROR_SS[$i]}
	if [ ! -d $SNAPSHOT_SUBDIR/hourly.0/$ssdir ]; then
		$MKDIR $SNAPSHOT_SUBDIR/hourly.0/$ssdir
	fi
        if [ -f "$NOBACKUPS" ]; then
                NOBACKUPS="--exclude-from=$NOBACKUPS"
        else
                NOBACKUPS=''
	fi

        :  >$TEMP_SYNC_RES_FILE
# Clear the sync results file ready for append

	$RSYNC	--bwlimit=$RSYNC_BWLIMIT			\
		-avz --delete					\
                $NOBACKUPS					\
		-e "$remote_ssh_pull" 				\
		$username_pull@$ip_pull:$remote_home_pull/$dir/ $SNAPSHOT_SUBDIR/hourly.0/$ssdir |
# Rsync output is piped to a while loop for display on screen and save to file
# (surely there must be an easier way to do this??)
                while read line
                do
                        $ECHO $line
                        $ECHO $line >>$TEMP_SYNC_RES_FILE
                done
	check_remote_error $? $E_RSYNC_PULL_FAILED

# NB - exclude-from="$EXCLUDES" only on push. Excluded files should be
# backed up but not transferred (eg. config files - system specific)
# This means that special config files on slave are not normally backed up,
# unless backup -H SLAVEHOSTNAME is used to specifically backup the slave.

# For files you really don't want backed up (eg. large temporary files)
# use web_nobackup instead.


done

cecho -c $ok "$($DATE) backup completed successfully"
