#! /bin/sh
#set -xv
#=========================================================================
# Copyright (C) VMware, Inc. 1986-2011.  All Rights Reserved..
#
# Name - convprepForGc61.sh
# Installed as - convprepForGc6x
#
# Purpose - This script prepares a 6.1 database for conversion to 
#           a GemStone/64 database for the sole purpose of running a fast
#           findDisconnectedObjects operation on it.  
#
#  *** Normal conversions should run convprep61 instead of this one! ***
#
# Steps for converting for a GS64 fast FDC
#
#  1) File in $GEMSTONE_22/upgrade/preConvFor61Gc.topaz to the 6.x 
#     production database.  This must be done BEFORE making the copy for
#     the conversion and offline GC.  This step needs to be performed only
#     once on the 6.x production system.
#
#  2) Shutdown the 6.x production database and make a copy of the extents.
#
#  3) Start the copy of 6.x production using startstone.
#
#  4) Run $GEMSTONE_22/upgrade/convprepForGc6x on the copy.  This will
#     shutdown the database when finished.
#
#  5) Start a new 2.x stone with a large shared page cache and pregrow 
#     the extents .
#
#  6) run $GEMSTONE_22/bin/conv61To2xForFastGc to perform the low level 
#     conversion.  The new 2.x database will be shutdown automatically 
#     when this step is completed.
#
#  7) Restart the new 2.x stone using startstone
#
#  8) Run the $GEMSTONE_22/bin/startotcachewarmers script.  This will 
#     start object table cache warmer gems to load the object table into 
#     the shared page cache.
#
#  9) Run the $GEMSTONE_22/bin/startfastfdcto6xfile script to start 
#     the fast offline FDC.  This will produce a binary file containing
#     a list of dead objects found by the FDC.  The file will be in 
#     GemStone 6.x oop format.
#
# 10) Build the offline GC user action library in
#     $GEMSTONE_22/examples/offlinegc6x.  Only this version of the 
#     offline GC user action will work correctly with the file produced
#     in step 9.  Remember to set $GEMSTONE to reference your 6.x 
#     GemStone tree when compiling and linking.  If you cannot build
#     the user action library, contact GemStone support to obtain it.
#
# 11) Use the user action library built in step 10 to run the MGC on
#     the production database at a convenient time.  See documentation
#     in $GEMSTONE_22/examples/offlinegc6x for more details.
#
# $Id: convprepForGc61.sh,v 1.2 2008-01-09 22:50:52 stever Exp $
#
#=========================================================================

if [ "a$GEMSTONE" = "a" ]; then
  echo "ERROR: GemStone scripts require a GEMSTONE environment variable."
  echo "       Please set it to the directory where GemStone resides."
  exit 1
fi

if [ "a$GEMSTONE_22" = "a" ]; then
  echo "ERROR: GEMSTONE_22 environment variable is required ."
  echo "       Please set it to the directory where GemStone64 v2.x resides."
  exit 1
fi


# maintenance symbols
comid="convprepForGc6x"         # this script's name

# make sure of a minimum path
PATH=:/bin:/usr/bin:/usr/ucb:$PATH; export PATH

. $GEMSTONE/bin/misc.sh

defaultErrorControl

info() {
  echo $comid[INFO]: $*
}
error() {
  echo $comid[ERROR]: $*
}
usage() {
cat <<EOF
Usage:
convprep61 [-h] [-s <stonename>]
Prepare a GemStone/S 6.1.5 repository for conversion to GemStone/S 64 bit 2.x for
use with the fast FDC algorithm.

Environment requirements:
    GEMSTONE          set to a GemStone/S 6.1.5 product tree
    upgradeLogDir     set to a writable directory
Parameters:
    -h print this Usage and exit
    -s <stonename>
        where <stonename> is the name of a running 6.1.5 stone (defaults
        to gemserver61)
EOF
}

# defaults
stoneName=gemserver61

#process command line
while getopts "hs:" opt; do
  case $opt in 
    h ) usage; exit 0 ;;
    s ) stoneName=$OPTARG ;;
   \? ) error "bad arg: $opt"; usage; exit 1 ;;
  esac
done
export stoneName

# make sure $upgradeLogDir has been set
info "verifying upgradeLogDir..."
if [ a$upgradeLogDir = "a" ]; then
  error "The environment variable upgradeLogDir has not been set."
  usage
  exit 1
elif [ ! -d $upgradeLogDir ]; then
  error "$upgradeLogDir is not a directory."
  usage
  exit 1
fi
info "...found upgradeLogDir..."

# make sure $upgradeLogDir is writable
touch $upgradeLogDir/tmp$$ 2>/dev/null 
if [ "$?" != "0" ]; then
  error "$upgradeLogDir is not writable."
  usage
  exit 1
fi
rm $upgradeLogDir/tmp$$
info "...upgradeLogDir is writable."

# make sure stone is running
info "verifying $stoneName is running..."
$GEMSTONE/bin/waitstone $stoneName -1 > /dev/null 2>&1
status=$?
if [ $status -ne 0 ]; then
  error "...no stone named $stoneName found"
  usage
  exit $status
fi
info "...$stoneName is running."

# clean up from previous runs, if any...
rm $upgradeLogDir/convprep61.log $upgradeLogDir/*.bm $upgradeLogDir/*.topaz \
$upgradeLogDir/ClassesForListInstances.txt $upgradeLogDir/ListOfSegments.txt \
$upgradeLogDir/from*.out >/dev/null 2>&1

# start topaz
info "starting topaz for conversion prep. This may take a while..."

$GEMSTONE/bin/topaz -l -i << EOF >$upgradeLogDir/convprep61.log 2>&1
output push $upgradeLogDir/convprep61.out only
set gemstone $stoneName
set u SystemUser p swordfish
login

iferror exit 1
display resultcheck
time

! This method was added in GS 6.1.5 and is required for conversion.
expectvalue %GsMethod
run
"Generates a run-time error if the method is not present."
Repository compiledMethodAt: #_writeGs64UpgradeDataToDirectory:
%

expectvalue true
run
| ver |
"check image version"
ver := (Globals at: #ImageVersion) at: #gsVersion.
(ver matchPattern: #('6.1')) ifFalse: [
  ((ver findPattern: #('6.2') startingAt: 1) == 0) ifTrue:[
     Object halt: 'need 6.1.5 or later image, found ImageVersion at: #gsVersion = ', ver].].
"check executable version"
ver := System gemVersionReport at: #gsRelease.
(ver matchPattern: #('6.1.' \$*)) ifFalse: [
  ((ver findPattern: #('6.2') startingAt: 1) == 0) ifTrue:[
    Object halt: 'need 6.1.5 or later system, found gsRelease = ',  ver].].
^true
%


expectvalue true
run
Globals at: #SmallDouble ifAbsent:[
  Object halt: 'Error: SmallDouble class not present.  Did you skip the preconversion step?'].
^true
%

! Write out the list of segments used by this repository, in order.
expectvalue true
run
|outFile outFileName|

outFileName := String withAll: '$upgradeLogDir'.
(outFileName last == $/)
  ifFalse:[outFileName add: $/].
outFileName addAll: 'ListOfSegments.txt'.
outFile := GsFile openOnServer: outFileName mode: 'w'.
(outFile == nil)
  ifTrue:[^(String withAll: 'Cannot open output file ') addAll: outFileName; yourself].

outFile nextPutAll: SystemRepository size asString; lf.
SystemRepository do:[:eachSegment|
  outFile nextPutAll: eachSegment asOop asString; lf.
].
outFile close.
^true
%

! Read the IdentitySet containing a list of classes that the User wants us to 
! locate all instances of during conversion.
! This is strictly optional.

expectvalue %Boolean
run

|setOfClasses outFile outFileName nextLine|

setOfClasses := System myUserProfile resolveSymbol: #ConversionClassesToFind.

(setOfClasses == nil)
  ifTrue:[^false].

setOfClasses := setOfClasses value.

(setOfClasses size == 0)
  ifTrue:[^false].

(setOfClasses isKindOf: IdentitySet)
  ifFalse:[^String withAll: 'ConversionClassesToFind expected to be anIdentitySet'].

outFileName := String withAll: '$upgradeLogDir'.
(outFileName last == $/)
  ifFalse:[outFileName add: $/].
outFileName addAll: 'ClassesForListInstances.txt'.
outFile := GsFile openOnServer: outFileName mode: 'w'.
(outFile == nil)
  ifTrue:[^(String withAll: 'Cannot open output file ') addAll: outFileName; yourself].

setOfClasses do:[:eachClass|
  (eachClass isBehavior _and:[eachClass isMeta not]) ifTrue:[
  |theName theOop|
  theName := eachClass name asString.
  theOop := eachClass asOop asString.
  outFile nextPutAll: theName; nextPut: $|; nextPutAll: theOop; lf.
  ].
].
outFile close.
^true
%


expectvalue true
run
|stoneStats|
stoneStats := System cacheStatistics: 1.
((stoneStats at: 140) == 0)
  ifFalse:[^'Error: vote state is not VOTE_IDLE'].
((stoneStats at: 50) == 0)
  ifFalse:[^'Error: Possible dead set not empty'].
((stoneStats at: 88) == 0)
  ifFalse:[^'Error: GcPossible dead set not empty'].
((stoneStats at: 51) == 0)
  ifFalse:[^'Error: DeadNotReclaimed set not empty'].
^true
%

! Stop all sessions.  Shadow page reclaim will be done by stone
expectvalue true
run
System stopOtherSessions.
[System currentSessions size == 1] whileFalse:[
  System sleep: 1.
  System stopOtherSessions.
  System sleep: 1.
].
^true
%

abort

expectvalue true
run
"This automagically puts us into and back out of out of transactionless mode"
System _reclaimAllShadowPages
%

abort
! Make sure we only have 1 Commit record left.
expectvalue 1
run
(System cacheStatistics: 1) at: 35
%

! All shadow pages should be gone, make sure
expectvalue 0
run
SystemRepository _shadowPagesCount
%


! Now, finally, write the conversion files
expectvalue %Array
run
SystemRepository _writeGs64UpgradeDataToDirectory: '$upgradeLogDir'
%

! do a clean shutdown
expecterror GemStoneError 4057
send System shutDown

exit 
EOF

status=$?
if [ $status -ne 0 ]; then
  error "...topaz exited with status $status"
else
  info "...conversion preparation complete."
  touch $upgradeLogDir/from61_1.out
fi
exit $status
