This script facilitates a 3-way synchronisation between two Windows shares and a hosted local directory. It requires no special software on the Windows machines, simply the sharing of respective directories. All the work of connecting to target machines and sychronising is done by the host using samba and unison.
It is assumed that samba, unison and a suitable mail configuration are installed on the linux host. The setup of these tools is beyond the scope of this documentation. Google is your friend if you need help.
In order to do a 3-way unison synchronisation we need first to sync between 1-2, then 2-3, then either 1-2 or 1-3 again to bring changes from 3 back into 1. So there are 3 independent sync's. For larger n-way syncs we need to add progressively more steps in order to ensure a full synchronisation. In this application where we are also hosting a known always available source we can assume will always be there. If we were simply facilitating an n-way sync without participating we would need to use a smarter algorithm to determine which end-points were available and sync between them accordingly.
As we check for a specific file before attempting a sync this file must be placed manually before any machine will be allowed to participate in sync operations. Removal of the check file will stop sync for the selected share which may be independent to each machine or common to all.
This script is suitable for running automatically as a cron job on the linux host server as often as needed.
In the event that a particular Windows machine was not found an error email will be generated.
The script allows for two names by which a Windows machine can be found. In many cases the second name will be the machine's IP address but use with care in DHCP environments. Having an alternative name or IP address is useful when a machine might sometimes be connected via LAN and other times by WiFi.
Here is the complete script which you will need to customise to suit your needs. In most cases you will only need to modify the variables as needed.
===========================================================
#!/bin/bash
# (c)2010 Robert Rath http://robertrath.com
# Common Variables
UNISON_OPTIONS="-perms 0 -batch -rsync -fastcheck true"
DESTINATION="/var/local/sync_directory_name_on_host_server"
DESTINATION_PATH=""
EMAIL="youremailaddress@youremaildomain"
EMAILMESSAGE="/tmp/emailmessage.txt"
NUMBER_OF_MACHINES="2"
# Source 1 Variables
SOURCE_MACHINE="NameOfWindowsMachine1"
ALT_MACHINE="IPAddressOfWindowsMachine1"
SOURCE_CREDENTIALS="/root/.smbpasswords/file_containing_machine1_windows_credentials"
MOUNT="/mnt/mount_directory_name_on_host_server"
SOURCE="NameOfWindowsMachine1SharedDirectory"
SOURCE_PATH="/PathFromRootOfWindowsMachine1SharedDirectory" # Leave blank,ie "", if using root of share, start with "/path" otherwise.
CHECK_FILE="initial_check_file_1" # This file must pre-exist/always-exist for sync to take place, use an initial backslash if not root of share.
# Source 2 Variables
SOURCE_MACHINE_2="NameOfWindowsMachine2"
ALT_MACHINE_2="IPAddressOfWindowsMachine2"
SOURCE_CREDENTIALS_2="/root/.smbpasswords/file_containing_machine2_windows_credentials"
MOUNT_2="/mnt/mount_directory_name_on_host_server_2"
SOURCE_2="NameOfWindowsMachine2SharedDirectory"
SOURCE_PATH_2="/PathFromRootOfWindowsMachine2SharedDirectory" # Leave blank,ie "", if using root of share, start with "/path" otherwise.
CHECK_FILE_2="initial_check_file_2" # This file must pre-exist/always-exist for sync to take place, use an initial backslash if not root of share.
# Sync the first machine with the server
# ======================================
if [ $((NUMBER_OF_MACHINES)) -ge 1 ]; then
echo sync-$DESTINATION$DESTINATION_PATH : Begin Synchronise $SOURCE Between $SOURCE_MACHINE and Server # Inform user of intention
/bin/mkdir -p "$DESTINATION$DESTINATION_PATH" # create the host sync directory
/bin/mkdir -p "$MOUNT" # create the mount point
rm -f $TMPLOG # remove an existing temp log file
# The remote machine may be at one of two host names so we need to check both in the prefered order
/bin/mount -t cifs -o credentials=$SOURCE_CREDENTIALS "//$SOURCE_MACHINE/$SOURCE" "$MOUNT" # mount machine share source to mount path
if [ ! -e "$MOUNT/$SOURCE_PATH$CHECK_FILE" ]; then # if mount was unsuccessful on 'first' then try 'alternative'
echo "Could not find primary target so attempting secondary target"
/bin/mount -t cifs -o credentials=$SOURCE_CREDENTIALS "//$ALT_MACHINE/$SOURCE" "$MOUNT" # mount alternate
fi
if [ -e "$MOUNT/$SOURCE_PATH$CHECK_FILE" ]; then # check if the mount was successful before doing sync
/usr/local/bin/unison-2.9.1 $UNISON_OPTIONS "$DESTINATION$DESTINATION_PATH" "$MOUNT$SOURCE_PATH" # use the unison program for synchronisation
if [ "$?" -eq "0" ]; then
SUBJECT="Succes: sync-$DESTINATION$DESTINATION_PATH - Synchronised sync-$SOURCE Between $SOURCE_MACHINE and Server using 'unison $UNISON_OPTIONS'" # success
elif [ "$?" -eq "1" ]; then
SUBJECT="Conflict: sync-$DESTINATION$DESTINATION_PATH - Conflicting sync-$SOURCE Between $SOURCE_MACHINE and Server using 'unison $UNISON_OPTIONS'" # sync conflict
else
SUBJECT="Error: sync-$DESTINATION$DESTINATION_PATH - Error sync-$SOURCE Between $SOURCE_MACHINE and Server using 'unison $UNISON_OPTIONS'" # sync error
fi
else
SUBJECT="Offline: sync-$DESTINATION$DESTINATION_PATH - Offline sync-$SOURCE to $SOURCE_MACHINE no sync attempt possible'" # offline error
fi
echo "$SUBJECT" # echo result to terminal
echo "$SUBJECT"> $EMAILMESSAGE
/usr/bin/mail -s "$SUBJECT" "$EMAIL" < $EMAILMESSAGE
/bin/umount "$MOUNT" # unmount mount path
fi
# Sync the second machine with the server
# ======================================
if [ $((NUMBER_OF_MACHINES)) -ge 2 ]; then
echo sync-$DESTINATION$DESTINATION_PATH : Begin Synchronise $SOURCE_2 Between $SOURCE_MACHINE_2 and Server # Inform user of intention
/bin/mkdir -p "$DESTINATION$DESTINATION_PATH" # create the host sync directory
/bin/mkdir -p "$MOUNT_2" # create the mount point
# The remote machine may be at one of two host names so we need to check both in the prefered order
/bin/mount -t cifs -o credentials=$SOURCE_CREDENTIALS_2 "//$SOURCE_MACHINE_2/$SOURCE_2" "$MOUNT_2" # mount machine share source to mount path
if [ ! -e "$MOUNT_2/$SOURCE_PATH_2$CHECK_FILE_2" ]; then # if mount was unsuccessful on 'first' then try 'alternative'
echo "Could not find primary target so attempting secondary target"
/bin/mount -t cifs -o credentials=$SOURCE_CREDENTIALS_2 "//$ALT_MACHINE_2/$SOURCE_2" "$MOUNT_2" # mount alternate
fi
if [ -e "$MOUNT_2/$SOURCE_PATH_2$CHECK_FILE_2" ]; then # check if the mount was successful before doing sync
/usr/local/bin/unison-2.9.1 $UNISON_OPTIONS "$DESTINATION$DESTINATION_PATH" "$MOUNT_2$SOURCE_PATH_2" # use the unison program for synchronisation
if [ "$?" -eq "0" ]; then
SUBJECT="Succes: sync-$DESTINATION$DESTINATION_PATH - Synchronised sync-$SOURCE_2 Between $SOURCE_MACHINE_2 and Server using 'unison $UNISON_OPTIONS'" # success
elif [ "$?" -eq "1" ]; then
SUBJECT="Conflict: sync-$DESTINATION$DESTINATION_PATH - Conflicting sync-$SOURCE_2 Between $SOURCE_MACHINE_2 and Server using 'unison $UNISON_OPTIONS'" # sync conflict
else
SUBJECT="Error: sync-$DESTINATION$DESTINATION_PATH - Error sync-$SOURCE_2 Between $SOURCE_MACHINE_2 and Server using 'unison $UNISON_OPTIONS'" # sync error
fi
else
SUBJECT="Offline: sync-$DESTINATION$DESTINATION_PATH - Offline sync-$SOURCE_2 to $SOURCE_MACHINE_2 no sync attempt possible'" # offline error
fi
echo "$SUBJECT" # echo result to terminal
echo "$SUBJECT"> $EMAILMESSAGE
/usr/bin/mail -s "$SUBJECT" "$EMAIL" < $EMAILMESSAGE
/bin/umount "$MOUNT_2" # unmount mount path
fi
# Re-Sync the first machine with the server
# =========================================
if [ $((NUMBER_OF_MACHINES)) -ge 2 ]; then
echo sync-$DESTINATION$DESTINATION_PATH : Begin Synchronise $SOURCE Between $SOURCE_MACHINE and Server # Inform user of intention
/bin/mkdir -p "$DESTINATION$DESTINATION_PATH" # create the host sync directory
/bin/mkdir -p "$MOUNT" # create the mount point
rm -f $TMPLOG # remove an existing temp log file
# The remote machine may be at one of two host names so we need to check both in the prefered order
/bin/mount -t cifs -o credentials=$SOURCE_CREDENTIALS "//$SOURCE_MACHINE/$SOURCE" "$MOUNT" # mount machine share source to mount path
if [ ! -e "$MOUNT/$SOURCE_PATH$CHECK_FILE" ]; then # if mount was unsuccessful on 'first' then try 'alternative'
echo "Could not find primary target so attempting secondary target"
/bin/mount -t cifs -o credentials=$SOURCE_CREDENTIALS "//$ALT_MACHINE/$SOURCE" "$MOUNT" # mount alternate
fi
if [ -e "$MOUNT/$SOURCE_PATH$CHECK_FILE" ]; then # check if the mount was successful before doing sync
/usr/local/bin/unison-2.9.1 $UNISON_OPTIONS "$DESTINATION$DESTINATION_PATH" "$MOUNT$SOURCE_PATH" # use the unison program for synchronisation
if [ "$?" -eq "0" ]; then
SUBJECT="Succes: sync-$DESTINATION$DESTINATION_PATH - Synchronised sync-$SOURCE Between $SOURCE_MACHINE and Server using 'unison $UNISON_OPTIONS'" # success
elif [ "$?" -eq "1" ]; then
SUBJECT="Conflict: sync-$DESTINATION$DESTINATION_PATH - Conflicting sync-$SOURCE Between $SOURCE_MACHINE and Server using 'unison $UNISON_OPTIONS'" # sync conflict
else
SUBJECT="Error: sync-$DESTINATION$DESTINATION_PATH - Error sync-$SOURCE Between $SOURCE_MACHINE and Server using 'unison $UNISON_OPTIONS'" # sync error
fi
else
SUBJECT="Offline: sync-$DESTINATION$DESTINATION_PATH - Offline sync-$SOURCE to $SOURCE_MACHINE no sync attempt possible'" # offline error
fi
echo "$SUBJECT" # echo result to terminal
echo "$SUBJECT"> $EMAILMESSAGE
/usr/bin/mail -s "$SUBJECT" "$EMAIL" < $EMAILMESSAGE
/bin/umount "$MOUNT" # unmount mount path
fi
exit 0
===========================================================
...Robert