#!/bin/bash # sshDirSynchro.sh # # Backup directory(ies) from/to local directory to/from remote directory using rsync/ssh # It is just an Rsync wrapper. # # V0.1 - 2014-08-17 # V0.2 - 2014-08-18 # Suppression du rapport de syncho intégré # Suppression de messages # V0.3 - 2014-08-18 # l'option "--delete" de rsync ne doit pas être positionnée par défaut (trop dangereux). # l'option "--delete" est ajoutée en parametre # l'option "--chmod" est ajoutée en parametre : elle emble nécessaire pour avoir des droits corrects # lors d'une copie depuis Windows (on fixe les droits chmod=u+rwx). TIME_START_1970=$(date +%s) #------------------------------------------------------------- # Define usage #------------------------------------------------------------- usage() { cat <<-'_usage_statement_' sshDirSynchro.sh: synchronizes a local directory and a distant directory using rsync/ssh. Usage: sshDirSynchro.sh --backup|--restore -ld -rh -ru -rd [ -h -p -bwl -ksd -lf --delete --chmod --inconv --check] ] Mandatory parameters: --backup copy from local to remote --restore copy from remote to local -ld, --local-dir Local directory from where the data are copied -rh, --remote-host Remote host to where the data are copied -ru, --remote-user Authorized user for the remote host -rd, --remote-dir Remote directory to where the data are copied Optionnal parameters: -h, --help Display usage information. -p, --ssh-port Default port is 22. -bwl, --bandwidth-limit Embeded "--bwlimit" for rsync -ksd, --kill-sleep-delay Permit to kill the script after a user defined delay. Exemple: -ksd "500s" for 500 seconds or -ksd "5h" for 5 hour. See man sleep for delay format. -lf, --log-file If no log file is set, the report is writen on stdout. If -lf is set a log file is created for the report. --delete rsync delete extraneous files from dest dirs --chmod ensure chmod=u+rwx on remote host --inconv convert chrset from Windows to Linux --check compare local and remote dir content *** Caution: This script use several ssh commands in batch mode. To avoid password asking ssh keys must be installed. See man ssh-keygen _usage_statement_ } #------------------------------------------------------------- # Analyse les arguments de la ligne de commande. #------------------------------------------------------------- SSH_PORT=22 # echo -e "SYNC_OPTION: ${SYNC_OPTION} - $#" while [[ $# > 0 ]] do # echo -e "$# - OPTION: $1" case $1 in --backup) if [ "${SYNC_OPTION}" ] then echo -e "Error: You must choose between --backup OR --restore!" exit 1 fi SYNC_OPTION="backup" shift ;; --restore) if [ "${SYNC_OPTION}" ] then echo -e "Error: You must choose between --backup OR --restore!" exit 1 fi SYNC_OPTION="restore" shift ;; -ld|--local-dir) LOCAL_WORKING_DIR=$2; shift 2 ;; -rh|--remote-host) REMOTE_HOST=$2 shift 2 ;; -ru|--remote-user) REMOTE_USER=$2 shift 2 ;; -rd|--remote-dir) REMOTE_WORKING_DIR=$2 shift 2 ;; -p|--ssh-port) SSH_PORT=$2 shift 2 ;; -bwl|--bandwidth-limit) BAND_WIDTH_LIMIT=$2 shift 2 ;; -ksd|--kill-sleep-delay) KILL_SLEEP_DELAY=$2 shift 2 ;; -lf|--log-file) LOGFILE="`basename $0`.`date +%Y\-%m\-%d\-%Hh%Mmn%S`.log" shift ;; --delete) DELETE_OPTION="--delete-before" shift ;; --chmod) CHMOD_OPTION="--chmod=u=rwX" shift ;; --iconv) ICONV_OPTION="--iconv=ISO-8859-1,utf-8 --protect-args" shift ;; --check) CHECK=$1 shift ;; -h|--help) usage exit 0 ;; *) # Unknown option echo -e "Error : Unknown option $1\n" usage exit -1 ;; esac done [ "${SYNC_OPTION}" ] || echo -e "\nError: --backup or --restore must be specified!" [ "${LOCAL_WORKING_DIR}" ] || echo -e "\nError --local-dir must be defined!!" [ ${REMOTE_HOST} ] || echo -e "\nError REMOTE_HOST must be defined!!" [ ${REMOTE_USER} ] || echo -e "\nError REMOTE_USER must be defined!!" [ ${REMOTE_WORKING_DIR} ] || echo -e "\nError REMOTE_WORKING_DIR must be defined!!" if [ ! "${LOCAL_WORKING_DIR}" ] || [ ! "${REMOTE_HOST}" ] || [ ! "${REMOTE_USER}" ] || [ ! "${REMOTE_WORKING_DIR}" ] then usage exit -1 fi #------------------------------------------------------------- # Create a custom logger #------------------------------------------------------------ if [ ${LOGFILE} ] then logprint() { #echo -e "$(date +%T): $*" >> $LOGFILE echo -e "$*" >> $LOGFILE } else logprint() { #echo -e "$(date +%T): $*" echo -e "$*" } fi #------------------------------------------------------------- # Test local diretory is ok #------------------------------------------------------------- # if [ ! -d ${LOCAL_WORKING_DIR} ] # then # logprint "ERROR: ${LOCAL_WORKING_DIR} does not exist!"; # exit -1 # fi #------------------------------------------------------------- # Test remote host is ok #------------------------------------------------------------- if [ $LOGFILE ] then echo -e "Resulats dans `pwd`/$LOGFILE" fi # if ( ! ssh $REMOTE_USER@$REMOTE_HOST -p $SSH_PORT "pwd" > /dev/null 2>&1 ) # then # logprint "ERROR: $REMOTE_HOST unreachable!" # exit -1 # fi # if ( ! ssh $REMOTE_USER@$REMOTE_HOST -p $SSH_PORT "cd $REMOTE_WORKING_DIR" > /dev/null 2>&1 ) # then # logprint "ERROR: $REMOTE_HOST/$REMOTE_WORKING_DIR unreachable!" # exit -1 # fi #------------------------------------------------------------- # Lancement du kill de ce processus en tâche de fond # pour limiter la durée d'execution (au cas où cela déborderait) #------------------------------------------------------------- # Attention: # la commande rsync s'execute un dans un process fils # l'utilisation du ssh crée un nouveau processus fils du rsync. # il faut donc faire un "pkill -P $$" pour tuer l'arborescence des processus. if [ ${KILL_SLEEP_DELAY} ] then logprint "Ce script stoppera dans $KILL_SLEEP_DELAY" sleep $KILL_SLEEP_DELAY \ && logprint \ && logprint "<> Le processus est tué car il dépasse le temps imparti" \ && logprint \ && pkill -P $$ & # variante d'écriture : (sleep 6s; kill $$ ) & # logprint "PID processus kill : $! - delay: $SLEEP_DELAY" # On stocke le PID de process lancé en backgound pour pouvoir le tuer KILL_ME_PID=$! fi #------------------------------------------------------------- # Recupération sur erreur #------------------------------------------------------------- function traitement_erreur { kill $KILL_ME_PID > /dev/null 2>&1 logprint logprint "---------------------" logprint ">>> Erreur $? >>>>" logprint "---------------------" logprint # remote_report exit -1 } trap traitement_erreur SIGHUP SIGINT SIGTERM SIGKILL #------------------------------------------------------------- # Set rsync options #------------------------------------------------------------ # options rsync: # -a, --archive mode archivage; identique à -rlptgoD (pas -H) # -v, --verbose plus loquace # -S, --sparse traite les fichiers à trous efficacement # -P équivalent à --partial --progress # --progress montre l'avancement pendant le transfert # --partial conserve les fichiers partiellement transférés # --delete delete extraneous files from dest dirs # --delete-before receiver deletes before xfer, not during # --no-p disables permissions copying # --no-g disables group copying # --chmod=ugo=rwX ensures that all non-masked bits get enabled # --delete-excluded also delete excluded files from dest dirs # --exclude=PATTERN exclude files matching PATTERN # -s, --protect-args no space-splitting; wildcard chars only # --iconv=ISO-8859-1,utf-8 de Windows vers Linux # RSYNC_OPTIONS=" -avS --delete-before --bwlimit=$BAND_WIDTH_LIMIT -e \"ssh -p $SSH_PORT \" " # le -e \"ssh -p $SSH_PORT \" pose problème dans RSYNC_OPTIONS # le --chmod=u=rwX semble nécessaire pour le Raspi RSYNC_OPTIONS="-avS --no-p --no-g --stats" [ ${CHMOD_OPTION} ] && RSYNC_OPTIONS="${RSYNC_OPTIONS} ${CHMOD_OPTION}" [ ${DELETE_OPTION} ] && RSYNC_OPTIONS="${RSYNC_OPTIONS} ${DELETE_OPTION}" [ "${ICONV_OPTION}" ] && RSYNC_OPTIONS="${RSYNC_OPTIONS} ${ICONV_OPTION}" [ ${BAND_WIDTH_LIMIT} ] && RSYNC_OPTIONS="${RSYNC_OPTIONS} --bwlimit=$BAND_WIDTH_LIMIT" #LOGFILE="`basename $0`.`date +%Y\-%m\-%d\-%Hh%Mmn%S`.log" #logprint "------------------------------------------------------" logprint "Synchronisation" if [[ ${SYNC_OPTION} = "backup" ]] then logprint " de `hostname` ${LOCAL_WORKING_DIR}" logprint " vers $REMOTE_USER@$REMOTE_HOST:$SSH_PORT $REMOTE_WORKING_DIR" fi if [[ ${SYNC_OPTION} = "restore" ]] then logprint " De $REMOTE_USER@$REMOTE_HOST:$SSH_PORT $REMOTE_WORKING_DIR" logprint " Vers `hostname` ${LOCAL_WORKING_DIR}" fi logprint " bwlimit=$BAND_WIDTH_LIMIT, delay max=$KILL_SLEEP_DELAY" logprint " rsync options: ${RSYNC_OPTIONS}" #------------------------------------------------------------- # Build final command #------------------------------------------------------------- RSYNC_COMMAND="rsync $RSYNC_OPTIONS -e \"ssh -p $SSH_PORT\"" [ "${SYNC_OPTION}" = "backup" ] && RSYNC_COMMAND="${RSYNC_COMMAND} \"${LOCAL_WORKING_DIR}\" $REMOTE_USER@$REMOTE_HOST:\"$REMOTE_WORKING_DIR\"" [ "${SYNC_OPTION}" = "restore" ] && RSYNC_COMMAND="${RSYNC_COMMAND} $REMOTE_USER@$REMOTE_HOST:\"$REMOTE_WORKING_DIR\" \"${LOCAL_WORKING_DIR}\" " [ ${LOGFILE} ] && RSYNC_COMMAND="${RSYNC_COMMAND} >> $LOGFILE 2>&1" logprint "\n$RSYNC_COMMAND" # !!!! eval introduces potential vulnerabilities and unexpected behavior situations so must be used !!! eval ${RSYNC_COMMAND} || { logprint logprint "**************************************" logprint "\n\n Erreur sur RSYNC: $?\n\n" logprint "**************************************" exit -2 } [ "${CHECK}" ] && compareLocalAndRemoteDirs.sh -ld "${LOCAL_WORKING_DIR}" -rd "$REMOTE_WORKING_DIR" -rh $REMOTE_HOST -ru $REMOTE_USER -p $SSH_PORT #------------------------------------------------------------- # On tue le process de kill (qu'il existe ou pas) # afin qu'il n'interrompe pas rapport d'execution #------------------------------------------------------------- if [ ${KILL_SLEEP_DELAY} ] then # logprint "*** processus de kill $! tué" kill $KILL_ME_PID > /dev/null 2>&1 fi #------------------------------------------------------------- # fin des opération #------------------------------------------------------------- TIME_END_1970=$(date +%s) logprint logprint "******** $(date +%T) - $(hostname): Fin de $(basename $0) OK! ($(convertDuration.sh $(expr $TIME_END_1970 - $TIME_START_1970))) **********"