#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos720 src/bos/usr/samples/odm/odmclean.sh 1.5 
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2001,2007 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)75        1.5  src/bos/usr/samples/odm/odmclean.sh, cmdodm, bos720 4/19/07 14:05:00
# 
# This utility should be used to cleanup odm databases.
# Normally, when we delete a object from a odm database, we may or may  
# not use this deleted object space. If a database is heavily 
# updated, me may have considerable amount of unused space, causing
# the file sizes to be too big.

# Algorithm:
#       Usage: odmclean -d <database name >
#
#       1. Make sure  we have enough space in /tmp and in database directory.
#       2. Make a backup copy of the database being cleaned up.
#       3. Read all the objects from the mentioned database in to a "temp" file. 
#       4. Remove all the objects from the database, this should cause the database
#          size to be about 4096 bytes.
#       5. Add all objects saved in "temp" file. 
#       6. Read all the objects from the new database in to another "temp" file.
#       7. Make sure data is identical by doing a diff on two temp files.
#       8. Make sure object count is the same.
#    

#Global Variables
dirname="/tmp"          #temporary working directory
vc_sz=0                 #.vc file size
db_sz=0                 #databe file size
db_name=""              #database name
db_fname=""             #database full path name
vc_fname=""             #.vc full path mame
odm_path=""             #ODMDIR
total_sz=0              #Min. space required in /tmp
tmp_fname=""            #Temporary data file name format is (/tmp/odmclean.$db_name)
obj_cnt_old=0           #no. of objects before cleanup
obj_cnt_new=0           #no. of object after cleanup
time_stamp=0
r_err=0                 #error during restoration
vc_exists=0             #database has .vc file
update_flag=0		#update in progress flag  
my_args=0               # for getopt


# Get all the objects on database and save it in $tmp_fname  file using
# odmget command.
#

odm_read() 
{
        echo "\tReading odm data .. \n" >&2
        tmp_fname="/tmp/odmclean.$db_name"

        `/usr/bin/odmget $db_name > $tmp_fname`

        if [ $? != 0 ]
        then
                echo " Could not read objects from $db_name database\n"  >&2
                exit 22;
        fi
        echo "\tDone reading.\n" >&2
}


# Remove all the objects from database using odmdelete command
#
odm_remove() 
{

        echo "\tDeleting objects ..\n" >&2
	update_flag=1		#update in progress
        /usr/bin/odmdelete -o $db_name > /dev/null
        if [ $? != 0 ]
        then
                echo " Could not remove objects from $db_name database.\n"  >&2
                Restore
                Cleanup
                exit 23;
        fi

        echo "\tAll objects deleted successfully.\n" >&2
}

# Add all the objects saved in "temp" file using odmadd command
odm_add() 
{
        echo "\tAdding objects to database ...\n" >&2
        /usr/bin/odmadd /tmp/odmclean.$db_name
        if [ $? != 0 ]
        then
                echo " Could not add objects to $db_name database.\n" >&2
                Restore
                Cleanup
                exit 24;
        fi

	update_flag=0		#update done
        echo "\tDone adding objects to the database.\n" >&2
}


# Restore the original database
Restore()
{
        #Restore the database file
	echo "Restoring the database ...\n" >&2
        /usr/bin/cp $db_fname.$time_stamp $db_fname
        rc=$?
        if [ $rc != 0 ] 
        then    echo "Error occured while trying to restore $db_fname.\n" >&2 
                echo "Fix the reported problem and copy $db_fname.$time_stamp to $db_fname.\n" >&2
        fi

        vrc=0        
        if [ "$vc_fname" != "" ]
        then
                /usr/bin/cp $vc_fname.$time_stamp $vc_fname
                vrc=$?
                if [ $vrc != 0 ]
                then    echo " Error occured while trying to restore $vc_fname.\n" >&2
                        echo "Fix the reported problem and copy $vc_fname.$time_stamp to $vc_fname.\n" >&2
                fi
        fi

        if [ $rc != 0 ] || [ $vrc != 0 ]
        then
		r_err=1 	#set restore error flag.
                Cleanup
                [ $vrc != 0 ] && exit 25
                exit 26
        fi      
	echo "Database restored successfully.\n" >&2

}

#Make sure new database has same contents as old one
odm_verify()
{
        echo "\tVerification in progress ..\n" >&2
        #Get the object count 
        obj_cnt_new=`/usr/bin/odmshow $db_name | /usr/bin/grep "population:" | /usr/bin/awk '{print $4}'| /usr/bin/cut -c2-`
        if [ $obj_cnt_new != $obj_cnt_old ]
        then
                echo "\t No. old objects=$obj_cnt_old\n" >&2
                echo "\t No. new objects=$obj_cnt_new\n" >&2
                echo "\t Error occured during cleanup\n" >&2
                Restore
                exit 27;
        fi

        #Make sure contents are the same
        /usr/bin/odmget  $db_name | /usr/bin/diff -  $tmp_fname
        if [ $? != 0 ]
        then
                echo "\t Contents of old database differs from cleaned database.\n" >&2
                echo "\t Error occured during cleanup.\n" >&2
                Restore
                exit 28;
        fi
        echo "\tVerification done.\n" >&2
}

signal_handler()
{
	echo "Aborting odmclean ....\n" >&2
	if [ $update_flag -eq 1 ]
	then
		Restore
	fi

	Cleanup

	exit 29;


}
#remove all temp files
Cleanup() 
{

        
        #remove temp data file
        /usr/bin/rm -f $tmp_fname

        #remove backup database file
        if [ $r_err -eq 0 ]
        then  
                /usr/bin/rm $db_fname.$time_stamp
        fi

        if [ "$vc_fname" != "" ] && [ $r_err -eq 0 ]
        then
                /usr/bin/rm $vc_fname.$time_stamp
        fi

}

#checks for available space in /tmp and database directory
check_space()
{
        freespace=`/usr/bin/df -k $dirname`
        freespace=`echo $freespace | /usr/bin/awk '{print $11}'` 
	exitsw=0

        if [ $freespace -lt $total_tmp_sz ]
        then
                echo "\t\tAvailable space is $freespace kbytes.\n" >&2
                echo "ERROR: Not enough space. Increase the $dirname file system size.\n" >&2
                exitsw=1
        fi
        echo "\t\tAvailable space in $dirname is  $freespace kbytes\n" >&2

        #check for space in database directory
        freespace=`/usr/bin/df -k $db_fname`
        freespace=`echo $freespace | /usr/bin/awk '{print $11}'`
        if [ $freespace -lt $total_sz ]
        then
                echo "\t\tAvailable space is $freespace kbytes.\n" >&2
                echo "ERROR: Not enough space. Increase the $db_fnamefile system size.\n" >&2
                exitsw=1
        fi
	[ $exitsw = 1 ] && exit 5
        echo "\t\tAvailable space in $db_fname is  $freespace kbytes\n" >&2
}

 # 
 # pass1():     
 #      1. Check for space needed for "temp" file in /tmp
 #              This should be at least twice the total filesizes.
 #      2. Check for enough space in database directory.
 #              This should be atleast total filesizes.
 #              This is needed for backup copy of database. 
 # 
pass1() 
{

        echo "Pass1() in progress .... \n" >&2

        #Make sure database is accessible 
        /usr/bin/odmshow $db_name > /dev/null    
        if [ $? != 0 ]
        then
                echo "Cannot access database $db_name.\n" >&2
                echo "Check ODMDIR environment variable set to a valid path\n" >&2
                exit 1
        fi

        #initialize database name
        db_fname="$odm_path/$db_name"

        #check if class has "vchar" or "nchar" types. That would
        # indicate that database has a .vc file 
        /usr/bin/odmshow  $db_name | /usr/bin/awk '{print $1}' |/usr/bin/grep vchar > /dev/null
        if [ $? != 0 ]
        then
                /usr/bin/odmshow  $db_name | /usr/bin/awk '{print $1}' | /usr/bin/grep nchar > /dev/null
        fi

        if [ $? -eq 0 ] 
        then 
                        vc_fname=$db_fname.vc
        fi      


        #get database file size
        if [ -f $db_fname ]
        then
                db_sz=`/usr/bin/ls -lL $db_fname | /usr/bin/awk '{print $5}'`
        else
                echo "Error: Specified database does not exist.\n" >&2
                Usage
                exit 2;
        fi

        if [ $? != 0 ]
        then
                echo "Error while listing file $db_fname. \n" >&2
                Usage
                exit 3;
        fi

        #get .vc file size
        if [ "$vc_fname" != "" ]
        then
                vc_sz=`/usr/bin/ls -lL $vc_fname | /usr/bin/awk '{print $5}'`
        fi

        if [ $? != 0 ]
        then
                echo "Error while listing file $db_fname. \n" >&2
                Usage
                exit 4;
        fi
        
        #total required space in /tmp for temp data
        let total_sz=$db_sz+$vc_sz      
        let total_tmp_sz=$total_sz+$total_sz # We need twice the amount 
        let total_sz=$total_sz/1024             #size in KB
	let total_tmp_sz=$total_tmp_sz/1024     #size in KB
        check_space                     #check space in /tmp and in databse directory
        obj_cnt_old=`/usr/bin/odmshow $db_name |  /usr/bin/grep "population:" | /usr/bin/awk '{print $4}' |  /usr/bin/cut -c2-`

        echo "Pass1 completed successfully.\n" >&2

}

#Do the odmclean
pass2() {

        
        echo "Pass2 in progress ...\n" >&2
        # Make sure no other odmclean operation in progress.
        # This is done by checking for /tmp/odmclean.$db_name file.

        if [ -f /tmp/odmclean.$db_name ]
        then
                echo "odm clean opeartion is already in progress for this database.\n" >&2
                exit 6;
        fi

        echo "\tMaking temporary backup copies of the databases ..\n" >&2
        #Make a backup copy of database being cleaned
        /usr/bin/cp $db_fname $db_fname.$time_stamp
        if [ $? != 0 ]
        then
                echo " Error creating a backup copy of the database.\n" >&2
                exit $?
        fi

        #Make a backup copy of .vc file
        if [  "$vc_fname" != "" ]
        then
                /usr/bin/cp $vc_fname $vc_fname.$time_stamp      
        fi

        if [ $? != 0 ]
        then
                echo " Error creating a backup copy of the databse.\n" >&2
		Cleanup
                exit $?
        fi
        echo "\tBacking up done. \n" >&2
        
        #Gather current odm data in to /tmp
        odm_read

        #remove all the objects
        odm_remove
        
        #add objects
        odm_add
        #verify the databse
        odm_verify
        
        echo "Pass2 completed successfully.\n" >&2
}

Usage() 
{
        echo "Usage:" >&2
        echo "\n\todmclean -d <database name>" >&2

} 

#parse Arguments
parseArgs () 
{

# We cannot use the set command with a back quoted getopt directly 
# as the exit code from the getopt will be overwritten by the exit
# code from the set command which is always 0.

        my_args=`getopt d: $*`

        if [ $? != 0 ]
        then
                Usage
                exit 1
        fi

	set -- $my_args

        if  [ $1 = -- ]
        then
                Usage
                exit 1
        fi

        while [ $1 != -- ]
        do
                case $1 in
                -d)    
                        db_name=$2
                        shift;;

                 *)    echo "Error: Invalid argument" >&2 ; Usage; exit 1;
                esac
                shift   # next flag

        done


}


trap "signal_handler" 1 2 3 15 9

#main 

        #set LANG nvironment
        export LC_MESSAGES=C
        
        #get pid
        pid=$$

        #get time stamp required for backup copy database
        time_stamp=`/usr/bin/date +\%d\%m\%H\%M\%S`
        rc=$?
        if [ $rc != 0 ]
        then
                echo "Error while executing date command \n" >&2
                exit $rc
        fi
        
        #get ODMDIR
        odm_path=$ODMDIR

        #parse arguments and initialization
        parseArgs $@

        #calculate space required
        pass1
        
        #do odmclean
        pass2

        #do cleanup 
        Cleanup

        echo "odmclean completed successfully.\n" >&2
        exit 0