#!/bin/ksh # # mirror # # AUTHOR: # Dan Harkless # # COPYRIGHT: # This file is Copyright (C) 1999 by Dan Harkless, and is released under the # GNU General Public License . # # USAGE: # % mirror [@] [[@]...] # # DESCRIPTION: # The only single command UNIX provides for mirroring a directory onto another # machine is the rdist command, kind of the redheaded step-child of the # r family. # # It's not a bad command, but there's a bit of a learning curve for its # "distfiles". And the main problem with it from my standpoint is that it # crawls through the directory structure and replaces files one-by-one. This # is unacceptable for directory trees like websites where files refer to one # another. If file A links to a brand new file B, and you transfer file A # over before file B, you'll have a window during which the link in file A # will appear to be broken. # # A much preferable way to mirror "live" directories like this is to leave the # remote machine's old hierarchy alone, while building a new, updated # hierarchy off to the side there. Once the new hierarchy has been completely # built (or simply transferred over), the old top directory is switched out # and the new one is put in its place. At this point the old directory # hierarchy can be deleted. Since UNIX doesn't have an atomic operation that # renames two files at a time, this method has a very small window where the # directory will not exist, but this is preferable to the many long-duration # inconsistencies that can occur with the other method. # # Currently, this script can only mirror a directory at the top level of your # home directory (e.g. ~/www). You also must have a ~/tmp directory (used as # a staging area) on both the master and slave machines. ~/tmp is used rather # than /tmp because on some OSes that directory is mode 777 instead of 1777, # allowing people to delete your tar file before you get a chance to pull your # files out of it. Also, the zcat picked by your $PATH on the remote machine # must be GNU's zcat. # # This script works quite well as a cron job to mirror your directory at # regular intervals. In this case, I suggest redirecting the script's # extensive stderr/stdout output to a log file so that if your mirroring ever # fails to occur you can determine why later. # # Note that if you accidentally mirror to a machine that has the same NFS home # directory as the one you're logged into (or that _is_ the same machine # you're logged into), you will not lose your files (since deletion of the old # copy of the directory is not done until after the new one is in place). # However, if any files in the mirrored directory were hard links to files # outside the directory, they no longer will be after the mirror operation. # # DATE MODIFICATION # ========== ================================================================== # 1999-04-22 Original. abort() { print "\n$mirror: Aborting at `date`." exit 1 } execho() { print "\n% $@" $@ } # Make sure we have gzip and whoami in our $PATH (cron default path usu. sucks). PATH=/usr/local/bin:/opt/gnu/bin:/usr/ucb:$PATH mirror=${0##*/} # get the root name of the script as installed, for printing if [[ $# < 2 ]]; then print "Usage: $mirror [@] [[@]...]" exit 1 fi print "$mirror: Started at `date`.\n" dir=$1 shift print "Directory: $dir" trap 'execho rm -f ~/tmp/$dir.tar?(.gz)' 0 1 2 3 15 umask 77 # make sure people can't get at your otherwise protected files via tar execho tar cvf ~/tmp/$dir.tar $dir || abort execho gzip ~/tmp/$dir.tar || abort while [[ $# > 0 ]]; do user_host=$1 shift print if [[ $user_host = *@* ]]; then user=${user_host%%@*} host=${user_host##*@} print "Host: $host" print "User: $user" host_user="$host -l $user" else print "Host: $user_host" print "User: `whoami`" # $USER not set under cron host_user=$user_host fi execho rcp -p ~/tmp/$dir.tar.gz $user_host:tmp || continue # Need to merge stderr with stdout first or ksh -x's trace output on stderr # will be out-of-sync with the other commands' (principally tar's) stdout. execho remsh $host_user "ksh -x 'exec 2>&1 && cd tmp"\ "&& zcat $dir.tar.gz | tar xvf -"\ "&& cd && mv $dir $dir.prev"\ "&& mv tmp/$dir .'" execho remsh $host_user "rm -rf $dir.prev" done print "\n$mirror: Done at `date`."