BackupPC-users

Re: [BackupPC-users] UPDATED: Fully automated script for creating shadow copies a

2009-11-03 01:46:37
Subject: Re: [BackupPC-users] UPDATED: Fully automated script for creating shadow copies a
From: "Jeffrey J. Kosowsky" <backuppc AT kosowsky DOT org>
To: backuppc-users AT lists.sourceforge DOT net
Date: Tue, 03 Nov 2009 01:42:05 -0500
hga wrote at about 13:37:38 -0500 on Sunday, November 1, 2009:
 > 
 > It looks like SourceForge.net did a mass migration of pages in early August 
 > and they borked attached files such as gifs and these useful scripts (also, 
 > who can edit is restricted on the wiki).  A fair amount of time with Google 
 > failed to find copies; could someone post them to this list/forum and/or put 
 > them up somewhere else on the net?

Not sure if you are referring to the scripts I posted earlier this year.
But here are updated versions of the script (shadowmountrsync) and short
DOS helper program (shadowexec.cmd) that I wrote.
--------------------------------------------------------------------------
#!/bin/bash

# Shadowmountrsync
# Copyright Jeffrey J. Kosowsky 2008, 2009
# Version 0.4.0 (November 2009)

##############################################################################
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see
# <http://www.gnu.org/licenses/>.
##############################################################################

# Description: automagically sets up (and takes down) shadow copies,
# shadow mounts, and the rsyncd daemon to allow you to seamlessly
# rsync to shadow copies of all the active modules in your current
# rsyncd.conf file WITHOUT any configuration changes or added settings
# to your existing setup non-shadow copy rsyncd setup. In particular,
# the program determines which shadows to create based upon the
# modules in your existing /etc/rsyncd.conf combined with what mounts
# actually exist.

# Usage: (see below for more detailed usage)
# shadowmountrsync
#       Sets up shadow copies, mounts shadows, and launches rsync
# shadowmountrsync -d
#       Terminates rsync, kills shadow copies, unmounts, and cleans up
#
# If using as pre/post commands in BackupPC, try something like:
#$Conf{DumpPreUserCmd} =
#        '$sshPath -q -x -l kosowsky $hostIP /usr/local/bin/shadowmountrsync';
#
#        $Conf{DumpPostUserCmd} =
#            '$sshPath -q -x -l kosowsky $hostIP 
/usr/local/bin/vshadow-scripts/shadowmountrsync -d';
#
# NOTE: this doesn't make sense for the Restore commands since clearly
# shadow copies CANNOT be restored to since by definition they are
# read-only
#
# WARNING: This is beta software which has only been tested to work so
# far on my own XP sp3 setup in a limited number of situations. In
# particular, it may not work on other Windows versions since the
# nature of shadow copy (and vshadow and dosdev) in particular has
# changed radically for Windows versions both prior to and post
# WinXP. Given the nature of shadow copy, it *should* (theoretically)
# not be capable of doing anything destructive to the source disk, so
# it's likely that the worse that would happen is that your backups
# would fail (or potentially be incomplete/corrupted). Feedback, bug
# reports, and enhancements are always welcome!!!
#
# NOTE: both this script and the companion 1-liner 'shadowexec.cmd'
# file MUST be stored in the SAME directory on each Windows CLIENT
# machine that you want to backup using volume shadow service
# (VSS). Also, both files need to be in the $ADMINGRP group (typically
# 'Administrators' or 'root') group and set with group permissions +rx
#
# REQUIRES:
#   vshadow.exe
#       Available from Microsoft downloads (make sure you get the
#       right version for your Windows OS release!)
#       
http://www.microsoft.com/downloads/details.aspx?FamilyID=0B4F56E4-0CCC-4626-826A-ED2C4C95C871&displaylang=en
#
#   dosdev.exe
#       Again available from Microsoft downloads (as part of their
#       reporting tools). Also, be careful of the version since
#       different verssions seem to have different command line
#       interfaces. I obtained my copy from:
#        http://www.ltr-data.se/files/dosdev.zip
#
#  cygwin (a base install should be sufficient) 
#       This or an equivalent unix-like environment on Windows is
#       required to provide the Bash shell and basic gnu utilities
#       such as sed, date, cat, etc. As detailed below, you should
#       also install the sshd (ssh server) and rsync packages.
#
#  
#  rsyncd (may be easiest to use the cygwin package version)
#      Note rsyncd does not need to be set up as a windows or cygwin
#      service since the script will launch and kill the rsyncd daemon
#      as appropriate
#
#  sshd (i.e. ssh server - use the cygwin version)
#      This is required to allow the Backuppc server to login to the
#      Windows client and run this script. Technically, you could use
#      any remote shell that allows you to run commands remotely,
#      though this script is specialized to get around the user id
#      limitations of cygwin ssh.

#  Note the above programs need to be in the default path for the
#  SYSTEM and ADMINISTRATOR users (I put them in /usr/local/bin or
#  equivalently in C:\cygwin\usr\local\bin); alternatively, you can
#  specify the paths explicitly in the variables section below. Of
#  course, thes programs need to be readable and executable by the
#  above-mentioned users.

# NOTE: The program recurses through this routine 3 times because of
# the limitations first of ssh and then of vshadow. An additional
# '0th' pass is used to terminate the shadow copy & clean up.  

# In particular, cygwin ssh logins lack the authority to create shadow
# copies or set up dos devices since it is not a true authenticated
# user login. On the other hand, vshadow is quite crippled in XP since
# it cannot create shadow copies nor can they be easily
# mounted. Dosdev is necessary because cygwin does not (yet) allow
# direct mounting of shadow copy devices since they lie in the kernel
# namespace.
#
# Since the program recurses both under the login user and under the
# SYSTEM user (for ssh), you need to be attentive to permissions. The
# program tries to detect and log such errors, but in particular:
# vshadow, dosdev, this bash script (and its cmd.exe companion), the
# log & lock file, and the shadowdir should all be readable (and where
# appropriate writable & executable) by the $ADMINGRP (typically
# 'Administrators' or 'root' group). Note also that the drive letter
# string ALLDRIVES can be restricted if you are likely to be
# mounting/unmounting temporary devices (like cds, dvds) requiring a
# fixed drive letter assignment during the rsync time. You do not need
# to restrict it for fixed drives (e.g. C:) since they are
# automatically avoided if present at the time of shadow creation.
#
# The recursion operates as follows:
# First, since the ssh login lacks the authority to create shadow
# copies or to set up dos devices, the program schedules an 'at' job
# (for the next minute) to recurse again through this routine. (If not
# launched from 'ssh' then for parallelism, the recursion is directly
# launched with cmd.exe).
#
# Then, 'vshadow' calls the routine a third time once the shadow copy
# has been set up to commplete the post-processing, including setting
# up device letters and mounting them and then starting the rsyncd
# daemon on an appropriately modified version of the original
# rsyncd.conf. This shadow -exec script needs a a 1-line helper bash
# cmd script since the -exec script can't take arguments and must be
# either a .cmd or .exe file. The shadow exec script (3rd pass) also
# works to keep the shadow copy alive (mimicking persistence) by
# launching the rsync daemon with the no-detach option.
#
# Finally, when the transfer is completed and rsyncd is killed, the
# whole recursion unwinds and everything gets cleaned up
# appropriately. 

# Note that pass #2 which sets up the shadow copies (which is also
# visible as the 'at' job if under ssh) and pass #3 (which is the
# script that mounts the shadow copies and launches rsync) remain
# alive until rsyncd is terminated. Pass #1 which kicks off the
# recursion hangs around until either a setup error is detected or
# until the setup is completed with the launch of the rsync daemon, at
# which time it returns and signals the result to the calling
# process. In particular, this initial pass returns '0' on success and a
# positive error code [1-8] on error.
#
# Indeed, the easiest/cleanest/best way to unwind the recursions and
# cause everything to reset gracefully is to kill the final 'rsync'
# process for which everything else ends up waiting. The '-d' option
# first tries to clean up gracefully like this but if it fails then it
# uses brute force to kill rsyncd, delete mounts, shutdown shadow
# copies, and remove lock files.
#
# Given the complexity of the multiple recursions, the program has
# been instrumented with plentiful (but optional) logging turned on by
# setting the $LOG variable to the log file you want to use. Indeed,
# the logging is fun and instructive to watch ;)
#
# By setting $STRICT you can ensure that shadow creation will abort if
# an rsync process or shadow copy is already running. Otherwise, it
# will kill existing shadows and rsync processes. It's probably a good
# idea to keep it set unless you are sure that you won't be colliding
# with another shadow copy process. The downside is that if something
# is stuck, then the routine will abort (and optionally log the
# result) rather than forcing a clean up.
#
# Exit/Return codes:
# 0 = success
# 1 = wrong input arguments
# 2 = lock file exists
# 3 = rsync daemon already running
# 4 = active shadow copies already exist
# 5 = shadow routines (e.g, dosdev, vshadow, shadowexec.cmd) not accessible
# 6 = shadow permissions error
# 7 = shadow copies created not equal to requested
# 8 = not enough free drive letters
# 9 = timeout error waiting for shadows and mounts to set up
# 10 = rsync daemon failed to start properly
# 11 = unknown/unspecified error


# Changelog
# 0.4.0 Updated for new Cygwin which doesn't like Win32 paths

##############################################################################
##### Define variables - user modifiable

USESHADOWS=2 #0=no shadows, use straight rsync only; 
             #1=use shadows with rsync, error if shadows not available
             #2=try to use shadows first, if not available use straight rsync
RSYNCDLOCK=   #Should be set in rsyncd.conf file as "pid file" (override here)
PATH=/bin:/usr/bin:/usr/local/bin:/cygdrive/c/WINDOWS/System32
ADMINGRP=Administrators  #Note some people may need to use 'root' instead
RSYNC=rsync
DOSDEV=dosdev
VSHADOW=vshadow
SHADOWDIR=/shadow #Directory where shadow mounts are set up
   #MUST not be any dir where other things are mounted (e.g., / or /cygdrive)
   #Note this counts as part of file name length so don't make path too long
RSYNCDCONF=/etc/rsyncd.conf  # Your original (non-shadow) version of rsyncd.conf
RSYNCDCONF_SHADOW=$SHADOWDIR/rsyncd.shadow.conf #Location for modified version
                                        #of rsyncd.conf (auto-generated)
SHADOWEXECCMD=shadowexec.cmd #Name of 1-liner helper script
                             #Must be in same directory as this script
STRICT=1000  #Set '1' if you want strict checking of error conditions
                         #before starting Specifically, won't start if 'lock'
                         #exists or 'rsync' already running or if shadows 
already
                         #exist (if $USESHADOWS > 0)
             #Set '0' if you want to ignore (and kill) existing shadow
             #and/or rsync processes
             #Set to 'N>1' if you want to ignore any existing rsync or
             #shadow process provided that they are at least N minutes old

SHADOWLOCK=/var/run/shadow.pid   #Name of shadow lock file
LOG=/var/log/shadow.log #Set if you want logging (advisable at least to start)
TIMEOUT=300  #Time in seconds 1st pass waits for setting up before timing out
SHORTTIMEOUT=30 #Time in seconds to wait for cleanup to finish & rsync to set up
#The following allow additional (optional) params to be passed to rsync daemon
RSYNCDADDRESS=
RSYNCDPORT=
RSYNCDBWLIMIT=
RSYNCDOTHER=
ALLDRIVES=$(echo {Z..A}) #Reverse alphabetical (all-caps)
            #Constrain to subset if you want to 'reserve' some positions
SHADOWDRIVES=$(echo {A..Z}) #List of drives to shadow (all-caps)
            #Constrain if you *don't* want to shadow shares on certain drives

##############################################################################
##### Nothing below this line should ordinarily be user-modifiable
##############################################################################
##### Define some helper functions

#Parse options and determine pass number
function parse_opts {
        local OPTS=`getopt -o 0123da:b:c:hl:p:qs:u: --long 
delete,address:,bwlimit:,conf:,help,letters:,port:,quiet,strict:,useshadows: -n 
'${0##*/}' -- "$@"`
        eval set -- "$OPTS"

        unset PASSNUM
        while true; do
                case "$1" in 
                        #Set PASSNUM based on *first* use
                        -0|-d|--delete) PASSNUM=${PASSNUM:-0};;
                        -1 ) PASSNUM=${PASSNUM:-1};;
                        -2 ) PASSNUM=${PASSNUM:-2};;
                        -3 ) PASSNUM=${PASSNUM:-3};;
                        -a|--address) RSYNCDADDRESS=$2; shift;;
                        -b|--bwlimit) RSYNCDBWLIMIT=$2; shift;;
                        -c|--conf) RSYNCDCONF=$2; shift;;
                        -h|--help) usage; exit;;
                        -l|--letters) SHADOWDRIVES="$(echo $2 | tr 'a-z' 
'A-Z')"; shift;;
                        -p|--port) RSYNCDPORT=$2; shift;;
                        -q|--quiet) unset LOG;;
                        -s|--strict) STRICT=$2; shift;;
                        -u|--useshadows) USESHADOWS=$2; shift;;
                        --) shift ; break ;;
                        *) echo "Internal error!" ; exit 1 ;;
                esac
                shift
        done
        PASSNUM=${PASSNUM:-1} #Default to pass 1 if not set
        PASS="[Pass #$PASSNUM]"
        if [ $PPID -ne 1 -a $PASSNUM -gt 1 ] ; then
                [ -n "$LOG" ] && echo -e "Error: passes 2 & 3 cannot be called 
directly...\n" >> $LOG
                exit 1
        fi
}

#Usage message
function usage {
    cat <<EOF
  
 Usage: (see below for more detailed usage)
    shadowmountrsync [options]
       Sets up shadow copies, mounts shadows, and launches rsync
    shadowmountrsync [-0|-d|--delete] [options]
       Terminates rsync, kills shadow copies, unmounts, and cleans up

   NOTE: all of the above options can be set to defaults in the
   script itself.  The options are there to allow you to override the
   defaults. Also, only the [-u|--useshadows] makes sense for the
   terminate [-d] usage.
     [-h|--help]
     [-a|--address] [address]  Override rsyncd.conf file to use given IP address
     [-b|--bwlimit] [kbps]     Override rsyncd.conf file to limit I/O bandwidth
     [-l|--letters] [ABC..]    Only create shadows for given letters (no spaces)
     [-p|--port] [port]        Override rsyncd.conf file to use given port
     [-q|--quiet]              Disable logging
         [-r|--rsyncconf] [file]   Rsyncd.conf file location
     [-s|--strict] [N] where:
            N=0 Proceed and start new process even if there are  existing 
                shadow copies, rsyncd daemons, and shadow lock files
            N=1 Exit with error if any of the above exist
            N>1 Proceed if all of the above haven't been modified in >N minutes
     [-u|--useshadows] [N] where:
            N=0 Bypass shadow copy code and just start regular rsync daemon
            N=1 Create shadow copy and mounts - abort on error
            N=2 Try to create shadow copies, but revert to regular rsync daemon
                if not successful
            In the -d case, setting N=0 means that it won't attempt to kill
            any existing shadow copies.

  Description: automagically sets up (and takes down) shadow copies,
  shadow mounts, and the rsyncd daemon to allow you to seamlessly
  rsync to shadow copies of all the active modules in your current
  rsyncd.conf file WITHOUT any configuration changes or added settings
  to your existing setup non-shadow copy rsyncd setup
EOF
    exit
}

# Launches passed input via 'at' to get around $USERNAME=SYSTEM
# problem under ssh login where the shell lacks permsisions to run
# commmands like vshadow or dosdev
function at_launch {
    local h m s wait command
        command=$@
    set -- $(date +"%H %M %S")
        h=$((10#$1))  #Note explicitly use base 10 so that 08 and 09 not 
interpreted as bad octal
        m=$((10#$2 +1)) #Advance minutes by 1
        s=$((10#$3))
        wait=$((60 - $s))
    [ $s -gt 55 ] && let "m += 1" "wait += 60" # Make sure >5 seconds left
    [ $m -ge 60 ] && let "m %= 60" "h += 1" #Overflow minutes
    let "h %= 24"
        at $h:$m $(cygpath -w $(which bash.exe)) -c \"$command\" > /dev/null
        return $wait
}

#Return 0 if $SHADOWLOCK, $RSYNCDLOCK, and all the shadow images are older
#than $1 (where $1 is in minutes)
function get_strict_age {
        AGE=$(( $(date +%s) - 60*$1 ))
        get_rsyncdlock
        [ -e "$RSYNCDLOCK" ] && \
                [ $(( $(stat -c %Y $RSYNCDLOCK) - AGE )) -gt 0 ] && return 1    
        if [ $USESHADOWS -gt 0 ] ; then
                [ -e "$SHADOWLOCK" ] && \
                        [ $(( $(stat -c %Y $SHADOWLOCK) - AGE )) -gt 0 ] && 
return 1
                ( IFS=$'\n'
                        for date in $($VSHADOW -q | sed -ne "s/.*- Creation 
Time: \(.*M\) *$/\1/p"); do
                                [ $(( $(date -d "$date" +%s) - AGE )) -gt 0 ] 
&& return 1
                        done
                        )
        fi
        return 0
}

# Populate SHADOWMOUNT with all drive letters (plus colon) that are present
# in rsyncd.conf and are actually mounted and are part of SHADOWDRIVES list
function get_shadowmounts {
        #Extract list of drive letter (followed by :) from rsyncd.conf, 
    #converting to uppercase
        local RSYNCMOUNTS=$(sed -ne "s%^[ \t]*path[ \t]*=[ 
\t]*/\(cygdrive/\)\?\([A-Za-z]\)\([ \t]*$\|[ \t]*#\|/\).*%\u\2%p" $RSYNCDCONF | 
sort | uniq | sed -e "s|$|:|")
        SHADOWMOUNT=()
        for mount in $RSYNCMOUNTS ; do #Eliminate mounts that are not actually 
present
                [ -d $(cygpath -u $mount) ] && [[ "$SHADOWDRIVES" =~ 
"${mount%:}" ]] && \
                        SHADOWMOUNT=("${SHADOWMOUNT[@]}" "$mount") #array
        done
}

# Populate FREEDRIVES with $1 *free* drive letters starting from list ALLDRIVES
function get_freedrives {
        FREEDRIVES=()
mount >> $LOG #JJKCRAP
        for letter in $ALLDRIVES ; do
                mount | egrep -q "^$letter:" || \
                        FREEDRIVES=("${FREEDRIVES[@]}" "$letter") #array
                [ ${#FREEDRIVES[@]} -eq $1 ] && return
        done
}

# If not set manually, then get name of rsyncd lock file from rsyncd.conf
function get_rsyncdlock {
        [ -z "$RSYNCDLOCK" ] && RSYNCDLOCK=$(sed -ne "s/[ ]*pid file[ ]*=[ 
]*\(.*[^ ]\)[ ]*\(#.*\|$\)/\1/p" $RSYNCDCONF)
}

# Kill rsyncd process
function kill_rsyncd {
        # Note we only want to kill rsync daemons that are bound to our rsync 
pid
        # file. In particular, we don't want to kill regular rsync processes
        # or even rsync daemons with an alternative rsync pid file that may be
        # associated with another conf file (and different port/address).
        get_rsyncdlock
        [ -e "$RSYNCDLOCK" ] || return #No running rsyncd with our pid file
        local PID=$(< "$RSYNCDLOCK") # Read in PID
        if [ -n $PID ]; then
                ps -e | grep -q "^[^0-9]*${PID}.*${RSYNC}$" && kill -TERM $PID 
&& sleep 1
                ps -e | grep -q "^[^0-9]*${PID}.*${RSYNC}$" && kill -KILL $PID 
&& sleep 1
        fi
        [ -e "$RSYNCDLOCK" ] && rm -f "$RSYNCDLOCK" #Shouldn't be necessary...
}

# Delete shadows, unmount shadow mounts, and remove drive letters
function remove_shadows {
        local shadowmounts letter

        ( echo Y | $VSHADOW -da ) > /dev/null 2>&1 #Delete shadow copies and as 
a
        #positive side effect kills shadow process and spawned routines

        shadowmounts=$(mount | sed -ne "s|^\([A-Za-z]\): on $SHADOWDIR/[A-Za-z] 
type.*|\1|p")
        
        for letter in $shadowmounts ; do
                umount $SHADOWDIR/$letter 2> /dev/null #Unmount
                $DOSDEV /D "${letter}:" 2> /dev/null 
        # Note dosdev doesn't work from ssh due to permissions and probably
                # not necessary after shadows killed, but good hygiene
        done
}

#If possible, gracefully unwind shadows by killing the shadowexec.cmd process
#Return 0 if unwinds successfully
function unwind_shadows {
        if [ "$USESHADOWS" -gt 0 -a -e $SHADOWLOCK ]; then
                SCRIPTPID=$(< $SHADOWLOCK)
                if [[ -n "$SCRIPTPID" && $SCRIPTPID -gt 0 ]]; then
                        [ -n "$LOG" ] && echo "--Attempting to clean up 
gracefully by unwinding shadow recursion...$PASS" >> $LOG
                        ps -e | grep -q "^[^0-9]*$SCRIPTPID.*/usr/bin/bash$" \
                                && kill -TERM $SCRIPTPID && sleep 1
                        ps -e | grep -q "^[^0-9]*$SCRIPTPID.*/usr/bin/bash$" \
                                && kill -KILL $SCRIPTPID && sleep 1
                        for ((WAIT=1; WAIT <=SHORTTIMEOUT; WAIT++)) ; do 
            #Wait for unwinding recursion to clean up by itself 
                                if [ ! -e "$SHADOWLOCK" ]; then #Successfully 
cleaned up by itself
                                        [ -n "$LOG" ] && echo  "--Successfully 
unwound shadow recursion...$PASS" >> $LOG
                                        return 0
                                fi
                                sleep 1
                        done
                fi
        fi
        return 1 #Not able to clean up by unwinding recursion
}

#Clean up manually by killing rsync daemon & shadow processes and removing locks
function clean_up {
         kill_rsyncd
         [ $USESHADOWS -gt 0 ] && remove_shadows
         rm -f $RSYNCDCONF_SHADOW $SHADOWLOCK
         [ -n "$LOG" ] && echo "..Finished clean_up... $PASS" >> $LOG
}
        
##############################################################################
##### Start of main code

parse_opts "$@"
[ -n "$LOG" ] && echo -e "\n[$(date +"%m/%d/%y %H:%M:%S")] 
[$PPID|$$][$USER|$USERNAME|$(id -un)|$(id -run)] $0 $* $PASS" >> $LOG #Log and 
timestamp each pass

# Check permissions each time through since (potentially) different user context
# i.e. make sure vshadow, dosdev and shadowexec.cmd are accessible
if [ $USESHADOWS -gt 0 ]; then
        VSHADOW=$(which $VSHADOW 2>/dev/null)
        DOSDEV=$(which $DOSDEV 2>/dev/null)
#       SHADOWEXECCMDWIN=$(cygpath -w $( (cd -P $(dirname $0) && pwd 
))/$SHADOWEXECCMD)
        SHADOWEXECCMD=$(which $SHADOWEXECCMD 2>/dev/null)
        SHADOWEXECCMDWIN=$(cygpath -w $SHADOWEXECCMD 2>/dev/null)
        if [ -z "$VSHADOW" -o -z "$DOSDEV" -o -z "$SHADOWEXECCMD"  \
                -o -z "$SHADOWEXECCMDWIN"]      > /dev/null 2>&1 ; then
                [ -n "$LOG" ] && echo -e "ERROR: Can't access either '$VSHADOW' 
or '$DOSDEV' or '$SHADOWEXECCMD'...\n" >> $LOG 
         #Note we need to protect the \'s in SHADOWEXECCMD
                if      [ $PPID -eq 1 ]; then
                        echo -5 >| $SHADOWLOCK #Signal to calling routine
                        exit 5
                elif [ $USESHADOWS -eq 1 ]; then 
                        exit 5# Shadows required so unrecoverable error
                else
                        USESHADOWS=0 # Default to not using shadows
                fi
        fi
fi

### PASS=0: Clean_up/termination (just a single pass independent of the others)
if [ $PASSNUM -eq 0 ] ; then # Terminate & clean up
        [ -n "$LOG" ] && echo "***Terminating and cleaning up shadows, mounts, 
and locks (PID=$$)...$PASS" >> $LOG
        unwind_shadows # Try first to unwind recursion gracefully
        if [ $? -ne 0 ] ; then
                [ -n "$LOG" ] && echo -e "--Manually clean up...$PASS" >> $LOG
                clean_up  #Didn't clean up by unwinding recursion, so call 
manually
        fi
        [ -n "$LOG" ] && echo -e "--Termination completed...$PASS\n" >> $LOG
        exit 0

### PASS=1: Initial time through 
elif [ $PASSNUM -eq 1 ] ; then #Launch initial recursion & wait for setup to 
complete
        [ -n "$LOG" ] && echo "***Initial pass through (PID=$$)...$PASS" >> $LOG

        get_rsyncdlock
        #First do some tests...
        if [ $STRICT -gt 1 ] ; then  #Avoid strict testing if files are aged 
enough
                get_strict_age $STRICT
                STRICT=$? #Sets to zero if aged sufficiently
        fi
        if [ $STRICT -eq 1 ] ; then
                if [ -e $SHADOWLOCK ] ; then  #Lock file exists
                        [ -n "$LOG" ] && echo -e "ERROR: Lock file 
exists...$PASS\n" >> $LOG
                        exit 2 # Lock file exists
                elif [ -e $RSYNCDLOCK ] ; then #Rsync running
                        [ -n "$LOG" ] && echo -e "ERROR: Rsync daemon already 
running...$PASS\n" >> $LOG
                        exit 3 # Rsync daemon already running
                elif [ $USESHADOWS -gt 0 ] && \
                        ! $VSHADOW -q | grep -q "There are no shadow copies in 
the system" ; then
                        [ -n "$LOG" ] && echo -e "ERROR: Active shadow copies 
exist...$PASS\n" >> $LOG
                        exit 4 # Active shadow copies already exist
                fi
        fi

    # Force clean up if needed (may be redundant but good hygeine)
        unwind_shadows || clean_up

        #Check permissions on files we call. Also touch SHADOWLOCK
        # Set up SHADOWDIR and check their permissions
        if [ $USESHADOWS -gt 0 ] && touch $SHADOWLOCK && \
                ! ( stat -c %A $0 | grep -q "^....r.x" && \
                        [ $(stat -c %G $0 ) = "$ADMINGRP" ] && \
                        stat -c %A $SHADOWEXECCMD | grep -q "^....r.x" && \
                        [ $(stat -c %G $SHADOWEXECCMD) = "$ADMINGRP" ] && \
                        mkdir -m 775 -p $SHADOWDIR && \
                        chown $USER.$ADMINGRP $SHADOWDIR $SHADOWLOCK $LOG && \
                        chmod g+rw $SHADOWLOCK $LOG ) ; then
                [ -n "$LOG" ] && echo "ERROR: Permissions error with '$0' or 
'$SHADOWEXECCMD' or '$SHADOWDIR' or '$SHADOWLOCK' or '$LOG'....$PASS" >> $LOG 
&& echo >> $LOG
                rm $SHADOWLOCK #Not using shadows
                if [ $USESHADOWS -eq 1 ]; then #Shadows required so exit with 
error
                        exit 6 # Shadow permissions error
                else
                        USESHADOWS=0 # Fall back to not using shadows
                fi
        fi

        if [ $USESHADOWS -gt 0 ] ; then
                RELAUNCHCMD="$( (cd -P $(dirname $0) && pwd ))/${0##*/} -2 $*"
                if [ -n "$SSH_CLIENT" ] ; then
        #Relaunch using 'at' if you come in via ssh ($USERNAME=SYSTEM)
        #because then you won't have privileges to run vshadow and dosdev
                        at_launch "$RELAUNCHCMD" #Recurse
                        RET=$?
                        [ -n "$LOG" ] && echo "--Relaunching as SYSTEM user via 
'at' in $RET seconds...$PASS" >> $LOG
                else #Just relaunch directly (for consistency with recursion 
above)
                        cmd.exe /C $(cygpath -w $(which bash.exe)) -c 
"$RELAUNCHCMD" &
                        [ -n "$LOG" ] && echo "--Relaunching via 
'cmd.exe'...$PASS" >> $LOG
                fi
                [ -n "$LOG" ] && echo "--Initial pass going into background 
waiting for setup to complete...$PASS" >> $LOG

                while ((TIMEOUT--)) ; do #Wait for setup completion or timeout
                        [ -s "$SHADOWLOCK" ] && break
                        sleep 1 
                done
                if [ -s "$SHADOWLOCK" ] ; then #Check to see if returned with 
errors
                        RETURN=$((-$(< $SHADOWLOCK))) #Undo negative of error 
codes
                else
                        [ -n "$LOG" ] && echo -e "ERROR: Shadow setup 
timed-out...$PASS\n" >> $LOG
                        RETURN=9 #Timeout error
                        clean_up
                fi
                if [ $RETURN -ge 7 -a $USESHADOWS -eq 2 ] ; then
                        USESHADOWS=0 # Fall back to trying rsync without shadows
                fi
        fi
    if [ $USESHADOWS -eq 0 ] ; then #Launch rsync without shadows
                for ((WAIT=1; WAIT <=SHORTTIMEOUT; WAIT++)) ; do 
        # Wait for any potential unwinding shadows to clean up
                        [ -e $SHADOWLOCK ] || break #Stop waiting when shadow 
lock deleted
                        sleep 1
                done
                [ -e $SHADOWLOCK  -o -e $RSYNCDLOCK ] && clean_up # Just in 
case...
                $RSYNC --daemon --config=${RSYNCDCONF} 
${RSYNCDADDRESS:+--address=$RSYNCDADDRESS} ${RSYNCDPORT:+--port=$RSYNCDPORT} 
${RSYNCDBWLIMIT:+--bwlimit=$RSYNCDBWLIMIT} ${RSYNCDOTHER}
                RETURN=$?
                [ -n "$LOG" ] && echo "--Starting rsyncd daemon (WITHOUT shadow 
mounts) in background...$PASS" >> $LOG
        [ -n "$LOG" ] && echo "..Rsyncd Params: Conf=$RSYNCDCONF 
Address=$RSYNCDADDRESS Port=$RSYNCDPORT BWLimit=$RSYNCDBWLIMIT 
Other=$RSYNCDOTHER...$PASS" >> $LOG
        fi

        if [ $RETURN -le 0 ] ; then  #No error
                for ((WAIT=1; WAIT <=SHORTTIMEOUT; WAIT++)) ; do # Wait for 
rsync to set up
                        if [ -e "$RSYNCDLOCK" ]; then
                                [ -n "$LOG" ] && echo -e "..Rsyncd started 
successfully (PID=$(< $RSYNCDLOCK))...$PASS\n" >> $LOG
                                exit 0 # Exit 0 on success
                        fi
                        sleep 1
                done
                [ -n "$LOG" ] && echo -e "--Rsyncd failed to start...$PASS\n" 
>> $LOG
                RETURN=10 # Rsyncd failed to start
        fi
        clean_up # Clean up on failure...
        exit $RETURN # Return failure code

### PASS=2: Called by at or cmd.exe from initial recursion
elif [ $PASSNUM -eq 2 ] ; then #Determine shadow mounts and launch vshadow
        trap clean_up HUP TERM
    [ -n "$LOG" ] && echo "**Running inital shadow setup (PID=$$)...$PASS" >> 
$LOG
         get_shadowmounts
         ( echo Y | $VSHADOW -da ) > /dev/null 2>&1 #Delete old shadow copies
                # This should be redundant, but just in case...
         [ -n "$LOG" ] && echo "--Starting shadow copy and waiting for 'exec' 
script to terminate...$PASS" >> $LOG
         export ARGS="$@" #Need to export to cmd.exe subshell since can't pass
         $VSHADOW -exec="$SHADOWEXECCMDWIN" ${SHADOWMOUNT[@]} > /dev/null 2>&1
         #NOTE: vshadow doesn't finish until $SHADOWEXECCMDWIN script completed
        [ -n "$LOG" ] && echo "--Vshadow terminated: cleaning up...$PASS" >> 
$LOG
        clean_up # Should already be mostly cleaned up by unwinding of recursion
        [ -n "$LOG" ] && echo -e "[$(date +"%m/%d/%y %H:%M:%S")] Done! Clean up 
completed...$PASS\n" >> $LOG
        exit 0

### PASS=3: Called by -exec command of vshadow
elif [ $PASSNUM -eq 3 ] ; then #Mount shadows & launch rsync
        [ -n "$LOG" ] && echo "**Running exec command spawned by vshadow 
(PID=$$)...$PASS" >> $LOG
        get_shadowmounts
        #Create paired array of the drive letter and the windows shadow path
        SHADOWPAIRS=( `$VSHADOW -q | sed -ne "s/\( *- Original Volume 
name:.*\[\([A-Z]\):.*\)\| *- Shadow copy device name: */\2/p"` )

        NUMSHADOWS=$((${#SHADOWPAIRS[@]}/2))
        if [ $NUMSHADOWS -ne ${#SHADOWMOUNT[@]} ] ; then
                [ -n "$LOG"] && echo -e "ERROR: Shadows created $NUMSHADOWS not 
equal to shadows requested (${#SHADOWMOUNT[@]})...$PASS\n" >> $LOG
                echo -7 >| $SHADOWLOCK
                exit 7
        fi
        get_freedrives $NUMSHADOWS
        [ -n "$LOG" ] && echo "..Drive letters=${FREEDRIVES[@]}   $PASS" >> $LOG
        if [ $NUMSHADOWS -gt ${#FREEDRIVES[@]} ] ; then
                [ -n "$LOG" ] && echo -e "ERROR: Not enough free drive 
letters...$PASS\n" >> $LOG
                echo -8 >| $SHADOWLOCK
                exit 8
        fi

        cp -f $RSYNCDCONF $RSYNCDCONF_SHADOW
        [ -n "$LOG" ] &&  echo "..Shadow mounts=   $PASS" >> $LOG
        for (( index=0 ; index < $NUMSHADOWS; index+=1 )) ; do
                # Create drive letter corresponding to shadow mount
                $DOSDEV "${FREEDRIVES[$index]}:" "${SHADOWPAIRS[2*$index+1]}"
                # Mount drive letter
                mount -f "${FREEDRIVES[$index]}:" 
$SHADOWDIR/${FREEDRIVES[$index]}
                [ -n "$LOG" ] &&  echo "    
${FREEDRIVES[$index]}:->${SHADOWPAIRS[2*$index]}:  ${SHADOWPAIRS[2*$index+1]}" 
>> $LOG
                [ -n "$LOG" ] && (echo -en "        " ; mount | grep 
"^${FREEDRIVES[$index]}:") >> $LOG
                #Rewrite rsyncd.conf substituting shadow paths where appropriate
                sed -i -e "s%^\([ \t]*path[ \t]*=[ 
\t]*\)/\(cygdrive/\)\?${SHADOWPAIRS[2*$index]}\([ \t]*$\|[ 
\t]*#\|/\)%\1$SHADOWDIR/${FREEDRIVES[$index]}\3%i" $RSYNCDCONF_SHADOW
        done

        kill_rsyncd #Should already be dead but kill again just in case...
        [ -n "$LOG" ] && echo "--Starting rsyncd daemon (with shadow mounts) 
and waiting for file transfer to complete...$PASS" >> $LOG
        [ -n "$LOG" ] && echo "..Rsyncd Params: Conf=$RSYNCDCONF 
Address=$RSYNCDADDRESS Port=$RSYNCDPORT BWLimit=$RSYNCDBWLIMIT 
Other=$RSYNCDOTHER...$PASS" >> $LOG
        echo $$ >| $SHADOWLOCK #Put shell PID in lockfile & signal successful 
setup
        $RSYNC --daemon --no-detach --config=${RSYNCDCONF_SHADOW} 
${RSYNCDADDRESS:+--address=$RSYNCDADDRESS} ${RSYNCDPORT:+--port=$RSYNCDPORT} 
${RSYNCDBWLIMIT:+--bwlimit=$RSYNCDBWLIMIT} ${RSYNCDOTHER}
        RET=$?
        #Rsync runs until shell or rsync killed (note we typically kill the 
shell)
        [ -n "$LOG" ] && echo "--Rsync terminated($RET): exiting & returning 
control to PASS=2...$PASS" >> $LOG
        exit $RET  #Returns exit of rsync
fi

exit 11 #Shouldn't get here...

--------------------------------------------------------------------------
REM Shadowexec.cmd
REM Version 0.4.0 (November 2009)
REM This cmd.exe script file should be in the same directory as the
REM bash script: shadowmountrsync
@echo OFF
C:\cygwin\bin\bash.exe -c "$(cygpath.exe '%~p0\shadowmountrsync') -3 %ARGS%"

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay 
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
BackupPC-users mailing list
BackupPC-users AT lists.sourceforge DOT net
List:    https://lists.sourceforge.net/lists/listinfo/backuppc-users
Wiki:    http://backuppc.wiki.sourceforge.net
Project: http://backuppc.sourceforge.net/