A script for syncing files and directories between two trusted servers (auto ssh login) using inotify. Far from perfect but does the job and a hell lot faster and more responsive than rsync.
Few notes for implementing:
- Make sure you have inotifywait command on the machine.
- Create /usr/bin/sys_admin/syncer.list with a directory/file per line.
- Start debugging :D.
Improved
Fixed some bugs when working on files using winscp and added support for file/dir moves/rename and ownership/permission sync.
Note: required deletion from move operations is delayed until the next update.
- #!/bin/bash
- # this script requires inotifywait, on debian: apt-get install inotify
- PATH="$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/sys_admin/"
- debug=0 # set to 1 for debug only mode, no operation will be performed
- files_list="/usr/bin/sys_admin/syncer.list" # dir list, one per line, with trailing slash, no blank lines
- target="target_server"
- pid_file="/var/run/syncer.pid"
- err_log="/var/log/syncer.err"
- lock_file="/var/log/syncer.lock"
- move_interval=2
- check(){
- if [ ! -e $files_list ]; then
- echo "directoris file not found ($files_list)"
- exit 6
- fi
- if ! ping -c1 $target &>/dev/null; then
- echo "target unreacable ($target)"
- exit 7
- fi
- }
- create_dir(){
- touch $lock_file
- attrib_get $1
- (($debug)) &&\
- echo "ssh $target \"mkdir $1; chmod $perm $1; chown $own $1\";" ||\
- ssh $target "mkdir $1; chmod $perm $1; chown $own $" </dev/null
- rm -f $lock_file
- }
- remove_dir(){
- touch $lock_file
- (($debug)) &&\
- echo "ssh $target \"rmdir $1\"" ||\
- ssh $target "rmdir $1" </dev/null
- rm -f $lock_file
- }
- move_file(){
- touch $lock_file
- attrib_get $2
- (($debug)) &&\
- echo "ssh $target \"mv $1 $2; chmod $perm $2; chown $own $2;\"" ||\
- ssh $target "mv $1 $2; chmod $perm $2; chown $own $2;"
- rm -f $lock_file
- }
- remove_file(){
- touch $lock_file
- (($debug)) &&\
- echo "ssh $target \"rm $1\"" ||\
- ssh $target "rm $1" </dev/null
- rm -f $lock_file
- }
- copy_file(){
- touch $lock_file
- attrib_get $1
- (($debug)) &&\
- echo "scp $1 $target:$1" ||\
- scp $1 $target:$1
- attrib_do $1
- rm -f $lock_file
- }
- attrib_get(){
- touch $lock_file
- perm=`stat -c "%a" $1`
- own="`stat -c \"%U.%G\" $1`"
- rm -f $lock_file
- }
- attrib_do(){
- touch $lock_file
- (($debug)) &&\
- echo "ssh $target \"chmod $perm $1; chown $own $1\"" ||\
- ssh $target "chmod $perm $1; chown $own $1" </dev/null
- rm -f $lock_file
- }
- start(){
- /usr/bin/inotifywait -rm --timefmt "%s" --format "%w%f %e %T" -e create -e close_write -e delete -e attrib -e move --fromfile $files_list 2>/dev/null | while read file event time; do
- (($debug)) && echo +++++++ $file -- $event -- $time
- if [ "x$holder" != "x" ]; then
- read stamp tmp_file tmp_event <<< "$holder"
- if [ $((`date +%s`-$stamp)) -gt $move_interval ]; then
- case $tmp_event in
- MOVED_FROM,ISDIR) remove_dir $tmp_file;;
- MOVED_FROM) remove_file $tmp_file;;
- esac
- holder=""
- fi
- fi
- case $event in
- CLOSE_WRITE,CLOSE) copy_file $file;;
- CREATE) copy_file $file;; # needed for handling winscp
- DELETE) remove_file $file;;
- CREATE,ISDIR) create_dir $file;;
- DELETE,ISDIR) remove_dir $file;;
- ATTRIB*) attrib_get $file; attrib_do $file;;
- MOVED_FROM*)
- if [ "x$holder" == "x" ]; then
- holder="`date +%s` $file $event"
- (($debug)) && echo "holder: $holder"
- else
- read stamp tmp_file tmp_event <<< "$holder"
- case $tmp_event in
- MOVED_FROM,ISDIR) remove_dir $tmp_file;;
- MOVED_FROM) remove_file $tmp_file;;
- esac
- holder="`date +%s` $file $event"
- (($debug)) && echo "holder: $holder"
- fi
- ;;
- MOVED_TO*)
- if [ "x$holder" == "x" ]; then
- case $event in
- MOVED_TO,ISDIR) create_dir $file;;
- MOVED_TO) copy_file $file;;
- esac
- else
- read stamp tmp_file tmp_event <<< "$holder"
- move_file $tmp_file $file
- holder=""
- fi
- ;;
- esac
- done &> /dev/null
- }
- stop(){
- while true; do
- kill `cat $pid_file 2>/dev/null` &>/dev/null && rm -f $pid_file &> /dev/null
- if [ ! -e $lock_file ]; then
- return 0
- else
- echo -n "."
- sleep 2
- fi
- done
- }
- case $1 in
- start) echo -n "Starting syncer service... "
- if [ -f $pid_file ] && ((`ps awux | grep -v grep | grep -c inotifywait`)); then
- echo " already running: pid file found ($pid_file) and an inotifywait process is running"
- exit 1
- elif [ -f $pid_file ]; then
- echo -n "(stale pid file) "
- fi
- check
- start&
- pid="$!"
- ps --ppid $pid -o pid,cmd | grep inotifywait | awk '{print $1}' > $pid_file
- echo "Started"
- ;;
- stop) echo -n "Stopping syncer service... "
- if [ -e $pid_file ]; then
- stop
- echo "Stopped"
- exit 0
- else
- echo "pid file not found"
- exit 2
- fi
- ;;
- restart) $0 stop
- $0 start
- exit 0
- ;;
- sync) $0 status; st=$?
- ((! $st)) && $0 stop
- echo "Running initial sync:"
- cat $files_list | while read dir; do
- echo -n " Initial sync for $dir"
- rsync -ar -e ssh $dir $target:$dir &&\
- echo " Done" ||\
- echo " Error"
- done
- ((! $st)) && $0 start
- ;;
- status) echo -n "Getting status for syncer service... "
- pid=`cat $pid_file 2>/dev/null`
- if [ -f $pid_file ] && ((`ps awux | grep -v grep | egrep -c "$pid.*inotifywait"`)); then
- echo "running (pid $pid)"
- exit 0
- elif [ -f $pid_file ]; then
- echo "not runing (pid file found $pid)"
- exit 3
- elif ((`ps awux | grep -v grep | egrep -c "$pid.*inotifywait"`)); then
- echo "not running (inotifywait procs found)"
- exit 4
- else
- echo "not running"
- exit 5
- fi
- ;;
- --help|-h) echo "Syntax: $0 <start|stop|restart|status|sync>"
- ;;
- *) echo "Syntax error"
- $0 -h
- ;;
- esac
Older version
- #!/bin/bash
- # this script requires inotifywait
- PATH="$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
- debug=0
- files_list="/usr/bin/sys_admin/syncer.list"
- target="1.1.1.0"
- pid_file="/var/run/syncer.pid"
- lock_file="/var/run/syncer.lock"
- err_log="/var/log/syncer.err"
- check(){
- if [ ! -e $files_list ]; then
- echo "directoris file not found ($files_list)"
- exit 3
- fi
- if ! ping -c1 $target &>/dev/null; then
- echo "target unreacable ($target)"
- exit 4
- fi
- }
- start(){
- /usr/bin/inotifywait -rm --timefmt "%s" --format "%w%f %e %T" -e create -e modify -e delete --fromfile $files_list 2>/dev/null | while read file event time; do
- (($debug)) && echo +++++++ $file -- $event -- $time
- case $event in
- CREATE|MODIFY) (($debug)) && echo "scp $file $target:$file"
- touch $lock_file
- scp $file $target:$file
- rm -f $lock_file
- ;;
- DELETE) (($debug)) && echo "ssh $target \"rm $file\""
- touch $lock_file
- ssh $target "rm $file" </dev/null
- rm -f $lock_file
- ;;
- CREATE,ISDIR) (($debug)) && echo "ssh $target \"mkdir $file\""
- touch $lock_file
- ssh $target "mkdir $file" </dev/null
- rm -f $lock_file
- ;;
- DELETE,ISDIR) (($debug)) && echo "ssh $target \"rmdir $file\""
- touch $lock_file
- ssh $target "rmdir $file" </dev/null
- rm -f $lock_file
- ;;
- esac
- done &> /dev/null #2>&1 | sed "s#^#`date` -- #" > $err_log
- }
- stop(){
- while true; do
- kill `cat $pid_file` &>/dev/null && rm -f $pid_file &> /dev/null
- if [ ! -e $lock_file ]; then
- return 0
- else
- echo -n "."
- sleep 2
- fi
- done
- }
- case $1 in
- start) echo -n "Starting syncer service... "
- if [ -f $pid_file ] && ((`ps awux | grep -v grep | grep -c inotifywait`)); then
- echo " already running: pid file found ($pid_file) and an inotifywait process is running"
- exit 1
- elif [ -f $pid_file ]; then
- echo -n "(stale pid file) "
- fi
- check
- start&
- pid="$!"
- ps --ppid $pid -o pid,cmd | grep inotifywait | awk '{print $1}' > $pid_file
- echo "Started"
- ;;
- stop) echo -n "Stopping syncer service... "
- if [ -e $pid_file ]; then
- stop
- echo "Stopped"
- exit 0
- else
- echo "pid file not found"
- exit 2
- fi
- ;;
- restart) $0 stop
- $0 start
- exit 0
- ;;
- status) echo -n "Getting status for syncer service... "
- pid=`cat $pid_file 2>/dev/null`
- if [ -f $pid_file ] && ((`ps awux | grep -v grep | egrep -c "$pid.*inotifywait"`)); then
- echo "running (pid $pid)"
- exit 0
- elif [ -f $pid_file ]; then
- echo "not runing (pif file found $pid)"
- exit 0
- elif ((`ps awux | grep -v grep | egrep -c "$pid.*inotifywait"`)); then
- echo "not running (inotifywait procs found)"
- exit 0
- else
- echo "not running"
- exit 0
- fi
- ;;
- --help|-h) echo "Syntax: $0 <start|stop|restart|status>"
- ;;
- *) echo "Syntax error"
- $0 -h
- ;;
- esac
TODO
- Add help and documentation
- Add a background proc for MOVED_FROM timeouts