#!/bin/bash
#
# Mirror sync control script: Version 1.00
# NetEase igi <chenwenqin@corp.netease.com>
# 2010-07-09
#
# 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; version 2.
#     http://www.gnu.org/licenses/gpl-2.0.txt
#
# This script is derived from anonftpsync, v1.43 2008-06-15:
#     http://www.debian.org/mirror/anonftpsync
#
# Usage: scriptname {project} {start|stop|restart}
#
# exit codes:
# exit 1 : load config file error
# exit 2 : check user error
# exit 3 : unknown action
# exit 4 : variables empty
# exit 5 : stale lock and rsync still running
# exit 6 : stale lock and rsync is exited
# exit 7 : current lock file exists
# exit 8 : change update time error
# exit 9 : pid empty
# exit 10: pre_comm exec error
# exit 11: local dir was missing

#exit when command return non-zero value
set -e
umask 002

DEBUG=0

#runlog switch ( not rsync's log )
IF_RUNLOG=0

#rsync logrotate numbers
log_nums=50

USER='mirror'
SDIR='/path/to/script/'
CONFIG="$SDIR/config"
RUNLOG="$SDIR/log/run.log"
PIDDIR="$SDIR/pid"
LOGDIR="/path/for/rsync's_log/"

MAILTO=

NOWTIME=`date +'%F %H:%M'`

#exclude tmp dirs
TMP_EXCLUDE="--exclude .~tmp~/ \
     --exclude Archive-Update-in-Progress* \
     --exclude .htaccess \
"

#---------------functions start-------------------------
usage() {
    echo "usage: `basename $0` {project} {start|stop|restart}"
}


# Check variables
check_var(){
    if [ -z "$TO" ] || [ -z "$RSYNC_HOST" ] || [ -z "$RSYNC_DIR" ] || [ -z "$LOGDIR" ]
    then
    	echo "[$NOWTIME] [Error 4] variables empty"
    	exit 4
    fi
    if [ ! -d "$TO" ] || [ ! -d "$LOGDIR" ]
    then
        echo "[$NOWTIME] [Error 11] local dir was missing"
        exit 11
    fi

}


# Whether we have another running rsync
check_lock(){
    LOCK_TIMEOUT=360
    LOCK="${PIDDIR}/$PROJECT"
    if [ -f "$LOCK" ]; then
      if [ "`find $LOCK -maxdepth 1 -cmin -$LOCK_TIMEOUT`" = "" ]; then
        if ps ax | grep '[r]'sync | grep -q $RSYNC_HOST; then
          echo "[$NOWTIME] [Error 5] stale lock found and rsync is still running"
          exit 5
        else
          echo "[$NOWTIME] [Error 6] stale lock found and rsync is exited"
          rm -f $LOCK
        fi
      else
        echo "[$NOWTIME] [Error 7] current lock file exists"
        exit 7
      fi
    fi
    touch $LOCK
}

send_mail(){
    if [ -n "$MAILTO" ]; then
    	mail -s "$PROJECT archive synced" $MAILTO < $LOGFILE
    fi
}

#update the lastest update time
update_time(){
    UPDATEFILE="/path/to/update.txt"
    
    if [ ! -e "$UPDATEFILE" ]
    then
        touch "$UPDATEFILE"
    fi
    
    MIRROR_NAME="$1"
    UPDATE_TIME=`date +'%F %H:%M'`
    IF_MIRROR=`grep -c "^$MIRROR_NAME " "$UPDATEFILE"`
    
    if [ "$IF_MIRROR" -eq 1 ]
    then
        sed -i "s#\($MIRROR_NAME\) .*#\1 $UPDATE_TIME#" "$UPDATEFILE"
    else
        echo "$MIRROR_NAME $UPDATE_TIME" >> "$UPDATEFILE"
    fi
    
    if [ "$?" -ne 0 ]
    then
        echo "[$UPDATE_TIME] [Error 8] change $MIRROR_NAME update time error"
        exit 8
    fi
}

run_rsync(){
    trap "rm -f $LOCK" exit
    #echo $$ > $LOCK
    ps h -o pgrp -p $$ | tr -d ' ' > $LOCK
    

    date +["Start "%F" "%T] >> $LOGFILE

    # run pre-commands, some distros(debian and ubuntu) would need this.
    if [ ${precomm:=0} -eq 1 ] && [ $DEBUG -eq 0 ]
    then
        pre_comm >> $LOGFILE 2>&1
        if [ $? -ne 0 ]
        then
            date +["End "%F" "%T] >> $LOGFILE
            savelog -c $log_nums $LOGFILE >/dev/null 2>&1
            rm -f $LOCK
            echo "[$UPDATE_TIME] [Error 10] $MIRROR_NAME pre_comm exec error"
            exit 10
        fi
    fi
    
    if [ $DEBUG -eq 0 ]
    then
        ${RSYNC:=/usr/bin/rsync} \
                ${COMMON_OPTIONS} \
                ${EXTEND_OPTIONS} \
                $TMP_EXCLUDE $EXCLUDE \
                $RSYNC_HOST::$RSYNC_DIR/ $TO/ >> $LOGFILE 2>&1
    else
        echo "${RSYNC:=/usr/bin/rsync} \
                ${COMMON_OPTIONS} \
                ${EXTEND_OPTIONS} \
                $TMP_EXCLUDE $EXCLUDE \
                $RSYNC_HOST::$RSYNC_DIR/ $TO/ >> $LOGFILE 2>&1
             "
             
    fi

    if [ $? -eq 0 ] && [ $DEBUG -eq 0 ]
    then
        send_mail
        update_time "$PROJECT"
        
        # run post-commands, some distros(fedora) would need this.
        if [ ${aftercomm:=0} -eq 1 ]
        then
            after_comm >> $LOGFILE 2>&1
        fi
    fi

    date +["End "%F" "%T] >> $LOGFILE
    savelog -c $log_nums $LOGFILE >/dev/null 2>&1
    rm -f $LOCK
}

stop_rsync(){
    LOCK="${PIDDIR}/$PROJECT"
    if [ -r "$LOCK" ]
    then
        PID=`<$LOCK`
    else
    	echo "[$NOWTIME] [Error 9] pid empty"
        return 9
    fi 
    
    if [ -z "$PID" ]
    then
    	echo "[$NOWTIME] [Error 9] pid empty"
        return 9
    fi
    pkill -9 -g "$PID"
    savelog -c $log_nums $LOGFILE >/dev/null
    rm -f $LOCK
}

#---------------functions end---------------------------

#check DEBUG
if [ $DEBUG -eq 0 ] && [ $IF_RUNLOG -eq 1 ]
then
    exec 4>&1 
    exec 5>&2 
    exec 1>>$RUNLOG 
    exec 2>>$RUNLOG 
fi

#check running user
runuser=`whoami`
if [ $runuser != $USER ]
then
    echo "[$NOWTIME] [Error 2] Check user error $runuser"
    exit 2
fi

#check args
if [ $# -ne 2 ]
then
    usage
    exit 0
fi

PROJECT="$1"
ACTION="$2"
LOGFILE="$LOGDIR/$PROJECT-mirror.log"

COMMON_OPTIONS="--recursive \
       -D \
       --links \
       --hard-links \
       --times \
       --delay-updates \
       --delete-after \
       --verbose --progress \
       --timeout=10800 \
       --partial \
"

# Load specified project configuration.
if [ -r "$CONFIG/$PROJECT" ]
then
    source "$CONFIG/$PROJECT"
else
    echo "[$NOWTIME] [Error 1] Cound not load $CONFIG/$PROJECT"
    exit 1
fi

#set if_first for speed up. It is useful for making a new mirror
if [ ${if_first:=0} -eq 1 ]
then
    EXTEND_OPTIONS="$EXTEND_OPTIONS --size-only" 
    COMMON_OPTIONS="${COMMON_OPTIONS/--delay-updates/}"
fi

set +e

check_var


case $ACTION in 
    start)
        check_lock
        run_rsync
        ;;
    stop)
        stop_rsync
        exit $?
        ;;
    restart)
        stop_rsync
        check_lock
        run_rsync
        ;;
    *)
        echo "[$NOWTIME] [Error 3] Unknown action $ACTION"
        exit 3
        ;;
esac

if [ $DEBUG -eq 0 ] && [ $IF_RUNLOG -eq 1 ]
then
    exec 1>&4
    exec 2>&5
fi
