#!/bin/sh # # ========================================================================= # Copyright 2004 Josh Glover # # LICENCE: # # This file is distributed under the terms of the BSD License (version # 2). See the COPYING file, which should have been distributed with this # file, for details. If you did not receive the COPYING file, see: # # http://www.jmglov.net/opensource/licenses/bsd.txt # # cvs-lock # # DESCRIPTION: # # Recursively locks a directory and all below it in a CVS repository. # # USAGE: # # See usage(), below # # EXAMPLES: # # cvs-lock -d /var/cvsroot --dir foobar -t # # cvs-lock: invoked with the following settings: # # cvsroot: /var/cvsroot # dir: foobar # dryrun: 0 # lock: wfl # quiet: 0 # trace: 1 # # touch /var/cvsroot/.foobar # lock_directory /var/cvsroot/foobar # Lock: /var/cvsroot/foobar # mkdir /var/cvsroot/foobar/#cvslock # touch /var/cvsroot/foobar/#cvs.wfl.1978 # rmdir /var/cvsroot/foobar/#cvslock # cvs-lock: CVS repository /var/cvsroot directory foobar locked # echo "1978" >> /var/cvsroot/.foobar # # TODO: # # - Implement locking a remote repository using CVS_RSH # - Nothing, this code is perfect # # DEPENDENCIES: # # Bourne shell # # MODIFICATIONS: # # Josh Glover (2004/05/28): Initial revision; # modified Jennifer Vesperman's cvs-freeze.sh script (from O'Reilly's # _Essential CVS_): http://examples.oreilly.com/cvs/freeze.sh # ========================================================================= # Constant: KEYMAGIC # # Unique key to append to lockfiles KEYMAGIC="$$" # Constant: ME # # Basename of the script ME=`basename $0` # Constant: MAXTRIES # # Maximum number of times to try before giving up MAXTRIES=5 # Constant: TMPFILE # # Temporary file for recording lockfiles TMPFILE="/tmp/cvs-lock.${KEYMAGIC}" # Variable: cvsroot # # Path to CVS repository (defaults to $CVSROOT environment variable) cvsroot="${CVSROOT}" # Variable: dir # # Top-level directory in CVS repository to lock dir="" # Variable: dryrun # # If non-zero, run in dryrun mode dryrun=0 # Variable: locktype # # Type of lock (rfl == read lock, wfl == write lock) locktype="rfl" # Variable: quiet # # If non-zero, informational messages will be suppressed quiet=0 # Variable: trace # # If non-zero, each command will be spewed to STDOUT before it is run trace=0 # Function: bail_out() # # Exit abruptly with an error code after displaying an error message. # # Parameters: # # 1 - error message to display bail_out () { echo "${ME}: $1" rm -f ${TMPFILE} exit 1 } # bail_out() # Function: is_locktype_valid() # # Checks the specified locktype for validity. # # Parameters: # # 1 - locktype string to check # # Returns: # # 0 if locktype is valid, 1 otherwise is_locktype_valid () { if [ -z "$1" ]; then return 1; fi # Read lock? if [ "$1" = "rfl" ]; then return 0; fi # Write lock? if [ "$1" = "wfl" ]; then return 0; fi # If control reaches here, locktype is invalid return 1 } # is_locktype_valid() # Function: lock_directory() # # Locks the specified directory. # # Parameters: # # 1 - directory to lock # # Returns: # # 0 on success, 1 on failure lock_directory () { if [ ${quiet} -eq 0 ]; then echo "${ME}: lock: $1"; fi # Make sure we have a valid locktype if ! is_locktype_valid ${locktype}; then echo "${ME}: invalid locktype: ${locktype}" return 1 fi # if (invalid locktype) # Obtain the master lock if [ ${trace} -ne 0 ]; then echo mkdir "$1/#cvslock" fi # if (trace mode) if [ ${dryrun} -eq 0 ]; then mkdir "$1/#cvslock" fi # if (dryrun mode?) # If we could not get the master lock, return failure if [ $? != 0 ]; then return 1; fi # Create the lock if [ ${trace} -ne 0 ]; then echo touch "$1/#cvs.${locktype}.${KEYMAGIC}" fi # if (trace mode) if [ ${dryrun} -eq 0 ]; then touch "$1/#cvs.${locktype}.${KEYMAGIC}" fi # if (dryrun mode?) # Record it in case of trouble echo $1 >> $TMPFILE # Release the master lock if [ ${trace} -ne 0 ]; then echo rmdir "$1/#cvslock" fi # if (trace mode) if [ ${dryrun} -eq 0 ]; then rmdir "$1/#cvslock" fi # if (dryrun mode?) return 0 } # lock_directory() # Function: unlock_all() # # Removes all the locks we've produced during the run so far, because # if we encounter anyone else playing with the CVS locks, then there's a # small risk of deadlock. In that event, we should undo everything we've # done to the repository, wait and try again. # # Returns: # # 0 on success, 1 on failure unlock_all () { # Make sure we have a valid locktype if ! is_locktype_valid; then return 1; fi for i in `cat ${TMPFILE}`; do if [ $trace -ne 0 ]; then echo "Unlock: $i"; fi # Obtain the master lock if [ ${trace} -ne 0 ]; then echo mkdir "$i/#cvslock" fi # if (trace mode) if [ ${dryrun} -eq 0 ]; then mkdir "$i/#cvslock" fi # if (dryrun mode?) # Obtain the master lock if [ $? -eq 0 ]; then # Remove lock if [ ${trace} -ne 0 ]; then echo rm -f "$i/#cvs.${locktype}.${KEYMAGIC}" fi # if (trace mode) if [ ${dryrun} -eq 0 ]; then rm -f "$i/#cvs.${locktype}.${KEYMAGIC}" fi # if (dryrun mode?) # Remove masterlock if [ ${trace} -ne 0 ]; then echo rmdir "$dir/#cvslock" fi # if (trace mode) if [ ${dryrun} -eq 0 ]; then rmdir "$dir/#cvslock" fi # if (dryrun mode?) fi # if (obtained the master lock) done # for (looping through locked directories in ${TMPFILE}) return 0 } # unlock_all() # Function: lock_all() # # Recursively calls to lock a whole directory tree. # # Returns: # # 0 on success, 1 on failure lock_all () { for i in `find ${cvsroot}/${dir} -type d ! -iname CVS ! -iname Attic ! -iname "#cvslock"`; do if ! lock_directory $i; then # We couldn't get the master lock. Someone else must be working on # the repository, so let's back off and return failure. unlock_all return 1 fi # if (could not lock this directory) done # for (recursively freezing directories) return 0 } # lock_all() # Function: usage() # # Displays a usage message and exits with the specified code. # # Parameters: # # 1 - return code (defaults to 255) usage () { retval=$1 if [ -z "${retval}" ]; then retval=255; fi cat <} where valid