#!/bin/sh
############################################################ IDENT(1)
#
# $Title: Script to replay perforce checkpoint $
#
############################################################ CONFIGURATION
#
# User to perform replay as
#
P4D_USER=p4admin
#
# Local perforce server settings
# NB: Taken from rc.conf(5) on FreeBSD
#
P4D_ROOT=$( sysrc -n p4d_root 2> /dev/null )
#
# Sensible defaults (i.e., Linux)
#
: ${P4D_ROOT:=/perforce}
############################################################ GLOBALS
pgm="${0##*/}" # Program basename
#
# Global exit status
#
SUCCESS=0
FAILURE=1
#
# Command-line options
#
DEBUG= # -d
ROOTDIR= # -R dir
UNPACK_ONLY= # -U
USER= # -u user
#
# Miscellaneous
#
export LC_ALL="${LC_ALL:-en_US.ISO8859-1}" # separators in dpv(1) status
FIFO=replay_fifo.$$
FILE=
GZFILE=
STOPPED_P4D=
# TODO: Add `-V' option to perform md5(1) verification when *.md5 exists
# NOTE: LC_ALL=en_US.ISO8859-1 dpv -xmd5 $( stat -f%z $FILE ):$FILE < $FILE
# wrapped in a sub-shell to capture output and add `-t title' et al.
############################################################ FUNCTIONS
quietly(){ "$@" > /dev/null 2>&1; }
have(){ quietly type "$@"; }
die()
{
local fmt="$1"
exec >&2
if [ "$fmt" ]; then
shift 1 # fmt
printf "$fmt\n" "$@"
fi
exit $FAILURE
}
usage()
{
exec >&2
local optfmt="\t%-8s %s\n"
printf "Usage: %s [-U|-d] [OPTIONS] checkpoint.NNNN[.gz]\n" "$pgm"
printf "OPTIONS:\n"
printf "$optfmt" "-U" \
"Unpack the checkpoint if-required (first step) and exit."
printf "$optfmt" "-d" \
"Debug. Don't stop p4d and don't replay but simulate it."
printf "$optfmt" "-R dir" \
"Perform replay in dir (default $P4D_ROOT)."
printf "$optfmt" "-u user" \
"Perform replay as user (default $P4D_USER)."
exit $FAILURE
}
############################################################ MAIN
#
# Command-line options
#
while getopts dR:u:U flag; do
case "$flag" in
d) DEBUG=1 ;;
R) ROOTDIR="$OPTARG" ;;
u) USER="$OPTARG" ;;
U) UNPACK_ONLY=1 ;;
*) usage
esac
done
shift $(( $OPTIND - 1 ))
FILE="$1"
#
# Validate command-line arguments
#
[ "$FILE" ] || usage
# set after last-call to usage()
P4D_ROOT="${ROOTDIR:-$P4D_ROOT}"
P4D_USER="${USER:-$P4D_USER}"
#
# All actions should be performed within the p4d_root (e.g., /perforce)
#
cd "$P4D_ROOT" || exit
#
# Validate command-line arguments (continued)
#
[ "$FILE" != "${FILE%.[Gg][Zz]}" ] && GZFILE="$FILE" FILE="${FILE%.[Gg][Zz]}"
IN="${GZFILE:-$FILE}"
[ -e "$IN" ] || die "%s: %s: No such file or directory" "$pgm" "$IN"
[ -f "$IN" ] || die "%s: %s: Is a directory" "$pgm" "$IN"
#
# Make sure we have required users and sufficient privileges to proceed
#
[ "$( id -u )" -eq 0 ] || die "Must be root!"
id -u ${P4D_USER:+"$P4D_USER"} > /dev/null || exit
#
# If the checkpoint argument was named `*.gz' (case-insensitive), unpack the
# checkpoint first.
#
if [ "$GZFILE" -a ! -e "$FILE" ]; then
size=$( stat -f%z "$GZFILE" )
trap 'rm -f "$FILE"' SIGINT
export FILE
if ! time dpv \
-wmx 'zcat > "$FILE"' -b "$pgm" -t "zcat(1)" \
-p "Unpacking checkpoint file...\n" \
-a "\nNEXT: Stop p4d, backup files, replay checkpoint" \
"${size:+$size:} $GZFILE" "$GZFILE"
then
quietly rm -f "$FILE"
exit $FAILURE
fi
trap - SIGINT
[ ! "$P4D_USER" ] || chown "$P4D_USER" "$FILE" || exit
quietly chmod 0444 "$FILE"
fi
[ "$UNPACK_ONLY" ] && exit $SUCCESS
# NOTREACHED if given `-U' option
#
# Make a backup directory for the existing db files
#
vers=$( p4d -V | awk '/^Rev./&&split($2,vers,"/"){print vers[3]}' ) || exit
back="db-$vers@$( date +%F+%H_%M )"
create=
if [ ! "$DEBUG" ]; then
for f in db.* *.lbr journal state; do
[ -e "$f" ] || continue
create=1
break
done
[ ! "$create" ] || mkdir -pm0755 "$back" || exit
[ ! "$P4D_USER" ] || chown "$P4D_USER" "$back" || exit
fi
#
# Is p4d running? (we'll pause for 10 seconds before stopping it)
# NB: If we stop it, we'll later start it
#
if [ ! "$DEBUG" ] && quietly service p4d status; then
dialog --backtitle "$pgm" --title "Count Down" \
--hline "Press Ctrl-C to Abort" --nook --nocancel --pause \
"Stopping p4d...\nNEXT: Backup files, replay checkpoint" \
7 50 10
service p4d stop || exit
STOPPED_P4D=1
fi
#
# Move the existing db files to backup directory
#
if [ ! "$DEBUG" ]; then
for f in db.* *.lbr journal state; do
[ -e "$f" ] || continue
mv -n "$f" "$back" || exit
done
[ -d "$back" ] && dialog --backtitle "$pgm" --title "Count Down" \
--hline "Press Ctrl-C to Abort" --nook --nocancel --pause \
"Files backed up to $back\nNEXT: Replay checkpoint" 7 50 10
fi
#
# Start the replay
#
[ -e "$FIFO" ] || mkfifo "$FIFO" || exit
trap 'quietly rm -f "$FIFO"' EXIT
if [ "$DEBUG" ]; then
quietly cat "$FIFO" &
else
cmd='p4d -r "$P4D_ROOT" -jr "$FIFO"'
if [ "$P4D_USER" ]; then
export P4D_ROOT FIFO cmd
if have sudo; then
export SUDO_PROMPT="${SUDO_PROMPT:-[sudo] Password:}"
sudo -Eu "$P4D_USER" sh -c 'eval "$cmd" &'
else
export P4D_USER
# WARNING! cmd executed by tcsh(1) (root's shell)
su -m <<-'EOF'
echo 'eval "$cmd" &' | su -m "$P4D_USER"
EOF
fi
else
eval "$cmd" &
fi
fi
size=$( stat -f%z "$FILE" )
time dpv -wmo "$FIFO" -b "$pgm" -t "p4d" \
-p "Replaying checkpoint file...\n" \
-a "\nThis may take a while." \
"${size:+$size:} $FILE" "$FILE" || exit
wait $!
#
# Restart p4d if we stopped it (we'll pause for 10 seconds before starting it)
# NB: Prevents error "p4d already running? (pid=<PID>)."
#
if [ "$STOPPED_P4D" ]; then
dialog --backtitle "$pgm" --title "Count Down" \
--hline "Press Ctrl-C to Abort" --nook --nocancel --pause \
"Starting p4d..." 6 50 10
service p4d start || exit
fi
exit $SUCCESS
################################################################################
# END
################################################################################
#
# $Copyright: 2015 Devin Teske. All rights reserved. $
#
# $Header: //guest/freebsdfrau/p4t/libexec/replay_checkpoint#1 $
#
################################################################################