#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos72Q src/bos/usr/lib/nim/methods/c_mkbosi.sh 1.25.3.5 
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# COPYRIGHT International Business Machines Corp. 1995,2019 
# 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 
# @(#)97 1.25.3.5 src/bos/usr/lib/nim/methods/c_mkbosi.sh, cmdnim, bos72Q, q2019_03A2 1/10/19 15:30:08 
#
#   COMPONENT_NAME: CMDNIM
#
#   FUNCTIONS: ./usr/lib/nim/methods/c_mkbosi.sh
#
#   ORIGINS: 27
#
#
#   (C) COPYRIGHT International Business Machines Corp. 1995
#   All Rights Reserved
#   Licensed Materials - Property of IBM
#   US Government Users Restricted Rights - Use, duplication or
#   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
#

# include common NIM shell defines/functions
NIMPATH=${0%/*}
NIMPATH=${NIMPATH%/*}
[[ ${NIMPATH} = ${0} ]] && NIMPATH=/usr/lpp/bos.sysmgt/nim
NIM_METHODS="${NIMPATH}/methods"
. ${NIM_METHODS}/c_sh_lib

#---------------------------- local defines     --------------------------------
EXCLUDE_ROOTVG=/etc/exclude.rootvg
EXCLUDE_PACKVG=/etc/exclude_packing.rootvg
SAVEVG=/usr/bin/savevg
MKSYSB=/usr/bin/mksysb
mksysb_mnt_dir=""

#---------------------------- module globals    --------------------------------
REQUIRED_ATTRS="location" 
OPTIONAL_ATTRS="server mksysb_flags exclude_files size_preview verbose force mount_opts" 
location=""
server=""
name=""
source=""
mksysb_flags=""
exclude_files=""
size_preview=""
force=""
verbose=""
#------------------------------- check_space -----------------------------------
#
# NAME: check_space
#
# FUNCTION:  Calculate the maximum amount of space required for 
#            the mksysb image along with the free space available
#            in the target location.  If the maximum required is 
#            greater than the space available, give an error and 
#            exit.  Otherwise, show the user the space 
#            requirements and continue.
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#       calls error on failure
#       NOTE: The mksysb image is packed, so the maximum amount
#             of space required will probably never be completely
#             used.  The compression rate varies widely depending
#             on the type of data that is being backed up. 
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#       parameters:
#       global:
#
# RETURNS: (int)
#       0                           = success
#
# OUTPUT:
#-------------------------------------------------------------------------------
function check_space {

    typeset MAX_SPACE_REQ=0
    typeset FREE_SPACE=0
    typeset USE_EXCLUDE_FILE=0  #default does not use exclude files

    # get the free space in the target filesystem 
    # (in 1024-byte blocks)
    FREE_SPACE=`${DF} -k \`${DIRNAME} ${location_access}\` | ${AWK} 'NR==2{print $3}'`
    [[ -z "${FREE_SPACE}" ]] && err_from_cmd ${DF}

    # if "e" in flags, use exclude files    
    USE_EXCLUDE_FILE=$(expr "${mksysb_flags}" : ".*e") # counts occurences of "e" => exclude

    MAX_SPACE_REQ=$(calculate_vg_space rootvg ${USE_EXCLUDE_FILE})
    (( REQ_MEG = ${MAX_SPACE_REQ} / 1024 + 1 ))
    (( FREE_MEG = ${FREE_SPACE} / 1024 + 1 ))

    # print the space information 
    ${C_ERRMSG} ${MSG_SPACE_MKSYSB} ${C_ERRMSG_MSG} $MAX_SPACE_REQ \
       $REQ_MEG $FREE_SPACE $FREE_MEG > ${TMPDIR}/msg 2>&1
    if [[ `expr "${mksysb_flags}" : ".*p"` -eq 0 ]]
    then
       cat ${TMPDIR}/msg
    else
       head -8 ${TMPDIR}/msg
    fi


    # if just previewing space then exit 
    if [ "${size_preview}" = "yes" ]
    then
        exit 0
    fi

    # is there enough space for the mksysb? 
    if (( MAX_SPACE_REQ > FREE_SPACE ))
    then
        # if force ignore space requirements 
        if [ "${force}" != "yes" ]
        then
            error ${ERR_SPACE_MKSYSB}
        fi
    fi 

} # end check_space

#--------------------------- calculate_vg_space --------------------------------
#
# NAME: calculate_vg_space
#
# FUNCTION:  Calculate space ${VG} takes up excluding files if asked
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#       VG = $1      name of volume group
#       EF = $2      0  => do not exclude files (default) 
#                    >0 => exclude files
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#       parameters:
#       global:
#
# RETURNS: (int)
#       0                           = success
#
# OUTPUT:
#-------------------------------------------------------------------------------
function calculate_vg_space {
    typeset VG=$1 EF=${2:-0}
    typeset lv fs LVLIST
    typeset BUF                   # command output buffer
    typeset TEF=$TMPDIR/ef.nim.$$ # temporary exclude file
    typeset LSVG=/usr/sbin/lsvg
    typeset LSVG=/usr/sbin/lsvg

    cd / 
    BUF=$(${LSVG} -l ${VG} | ${AWK} '$6 ~ "^open" && ($2 == "jfs2" || $2 == "jfs") {print $1,$7}') 

    if [[ ${EF} == 0 ]] ; then
        echo "${BUF}" | while read lv fs; do
            typeset LVLIST="/dev/${lv} ${LVLIST}"
        done
        ${DF} -kIM ${LVLIST} | ${AWK} 'NR > 1 {tot = tot + $4} END {printf "%d",tot+1}'
    else
        echo "${BUF}" | while read lv fs; do
            # this seems ugly but we really need to match how mksysb/savevg works, so...
            # the long pipeline follows the logic:
            # - find command to get the files and sizes per FS
            # - awk to reorder in a manner egrep and we can use (eg, $11=filename first, $2=size)
            # - egrep out based on the exclude file
            # - awk to calculate total and buffer by 512K for the header

            # setup a temporary exclude_file
            > ${TEF}

            # process the same as savevg
            if [[ -f ${EXCLUDE_ROOTVG} ]]; then
                ${CAT} ${EXCLUDE_ROOTVG} | ${SED} 's/       //g;s/^  *//g;s/  *$//g;/^$/d' > ${TEF}
            fi

            ${FIND} .$fs ! -type s \( -fstype jfs -o -fstype jfs2 \) -xdev -ls |
            ${AWK} '{print $11,$2}' | ${EGREP} -v -f ${TEF} |
            ${AWK} -v fs=".$fs" '{tot=tot+$2} END {printf ("%s\t%d\n",fs,tot)}'
        done  | ${AWK} '{tot=tot+$2} END {printf ("%.0d",tot+512)}' 

        # cleanup the temporary exclude file
        [[ -r ${TEF} ]] && rm -f ${TEF}
    fi

    return 0
}

#---------------------------- process_exclude_file ----------------------------
#
# NAME: process_exclude_file 
#
# FUNCTION:  Sets up access to the exclude file that we are 
#            using and copies it to /etc/exclude.rootvg so
#            that the savevg command will use it when backing
#            up the system.  If a previous exclude file exists
#            it will be saved and restored after the backup. 
#
#            Updated to also process /etc/exclude_packing.rootvg
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#       calls error on failure
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#       parameters:
#       global:
#
# RETURNS: (int)
#       0                           = success
#
# OUTPUT:
#-------------------------------------------------------------------------------
function process_exclude_file {

    # save the old exclude file
    if [ -f ${EXCLUDE_ROOTVG} ]
    then
        ${CP} ${EXCLUDE_ROOTVG} ${EXCLUDE_ROOTVG}.nim_save.$$ ||
            err_from_cmd "${CP} ${EXCLUDE_ROOTVG} ${EXCLUDE_ROOTVG}.nim_save.$$"
    fi
    # save the old exclude file
    if [ -f ${EXCLUDE_PACKVG} ]
    then
        ${CP} ${EXCLUDE_PACKVG} ${EXCLUDE_PACKVG}.nim_save.$$ ||
            err_from_cmd "${CP} ${EXCLUDE_PACKVG} ${EXCLUDE_PACKVG}.nim_save.$$"
    fi


    # set up access to the exclude file
    if [[ ${exclude_files} = ?*:?* ]]
    then
        # mount remote exclude_files resource
        nim_mount ${exclude_files}
        exclude_access=${access_pnt}
    else
        # the exclude_files resource is local
        exclude_access=${exclude_files}
    fi

    # just to be extra safe, in the case that the exclude_files
    # resource is local AND already meets the name/location that
    # savevg would use
    if [[ "${exclude_access}" != "${EXCLUDE_ROOTVG}" ]]
    then
        echo ${exclude_files} > /tmp/.nim_pack
        LANG=C ${EGREP} exclude_packing /tmp/.nim_pack
        RC=$?
        if [ "$RC" -ne 0 ]
        then
        # put the exclude file in /etc/exclude.${VG}
        # so that the savevg command will read it
        ${CP} ${exclude_access} ${EXCLUDE_ROOTVG} ||
                err_from_cmd "${CP} ${exclude_access} ${EXCLUDE_ROOTVG}"
        # add the -e flag since we have a exclude_rootvg file
        mksysb_flags=e${mksysb_flags}
        else
        ${CP} ${exclude_access} ${EXCLUDE_PACKVG} ||
                err_from_cmd "${CP} ${exclude_access} ${EXCLUDE_PACKVG}"
        fi
        ${RM} /tmp/.nim_pack
    fi

    nim_unmount ${access_pnt}

} # end process_exclude_file

#---------------------------- format_mksysb_flags ----------------------------
#
# NAME: format_mksysb_flags 
#
# FUNCTION:  The options to mksysb_flags are "AVXab:ePimpZ".  This function
#            makes sure that the flags are in the correct format to 
#            pass to savevg no matter what order they are entered 
#			 by the user.
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
# 		The -b option to mksysb takes a numeric argument.
# 		Make sure that there is a space after the -b flag
# 		and it's argument.  If not, savevg will take everything
# 		after the -b flag as the block_size argument.
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#       parameters:
#       global:
#
# RETURNS: 
#
# OUTPUT:
#-------------------------------------------------------------------------------
function format_mksysb_flags {

	in_flags=${1}
	fmt_flags=""
	i=1

	# get number of characters in flags 
	num_flags=`echo ${in_flags} | ${WC} -c | ${AWK} '{print $1}'`

	# Loop through the flags and make sure that there is
	# a space after the -b flag and it's argument.
	while [ $i -lt $num_flags ]
	do

  		flag=`echo ${in_flags} | ${CUT} -c $i`

		# if digit print and set digit flag 
  		if [[ "${flag}" = +([0-9]) ]]
		then

    		fmt_flags=${fmt_flags}`echo "${flag}\c"`
    		isdigit=true

		# if not digit print mksysb flag
  		elif [ ! "$isdigit" ]
		then

    		fmt_flags=${fmt_flags}`echo "${flag}\c"`

  		else

			# last thing printed was a digit so
			# print space before next mksysb flag
    		isdigit=

			# if space just print it
    		if [[ "${flag}" = " " ]]
			then
        		fmt_flags=${fmt_flags}`echo " \c"`

			# if flag print with hyphen
     		else
        		fmt_flags=${fmt_flags}`echo " -${flag}\c"`
     		fi

  		fi

		let i=i+1

	done

	echo ${fmt_flags}

} # end format_mksysb_flags

#---------------------------- c_mkbosi_cleanup ----------------------------
#
# NAME: c_mkbosi_cleanup 
#
# FUNCTION:  Replace the exclude file as it was before we ran
#            and call the generic cleanup function to make sure
#            everything gets unmounted.
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#       calls error on failure
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#       parameters:
#       global:
#
# RETURNS: 
#
# OUTPUT:
#-------------------------------------------------------------------------------
function c_mkbosi_cleanup {

    # copy the old exclude file back
    if [ -f ${EXCLUDE_ROOTVG}.nim_save.$$ ]
    then
        ${MV} ${EXCLUDE_ROOTVG}.nim_save.$$ ${EXCLUDE_ROOTVG} > /dev/null 2>&1 || 
            err_from_cmd "${MV} ${EXCLUDE_ROOTVG}.nim_save.$$ ${EXCLUDE_ROOTVG}"
    else
        # remove the exclude file we installed
        if [ -n "${exclude_files}" ] && [ -f "${EXCLUDE_ROOTVG}" ]
        then
            ${RM} ${EXCLUDE_ROOTVG} ||
            warning_from_cmd "${RM} ${EXCLUDE_ROOTVG}"
        fi
    fi

    # copy the old exclude packing file back
    if [ -f ${EXCLUDE_PACKVG}.nim_save.$$ ]
    then
        ${MV} ${EXCLUDE_PACKVG}.nim_save.$$ ${EXCLUDE_PACKVG} > /dev/null 2>&1 ||
            err_from_cmd "${MV} ${EXCLUDE_PACKVG}.nim_save.$$ ${EXCLUDE_PACKVG}"
    else
        # remove the exclude file we installed
        if [ -n "${exclude_files}" ] && [ -f "${EXCLUDE_PACKVG}" ]
        then
            ${RM} ${EXCLUDE_PACKVG} ||
            warning_from_cmd "${RM} ${EXCLUDE_PACKVG}"
        fi
    fi

    # clean up the mount point if we mounted the mksysb write directory over.
    # we need to do this here since if the unmount fails the entire directory 
    # might get erased.  
    if [ -n "${mksysb_mnt_dir}" ]
    then
        nim_unmount ${mksysb_mnt_dir}

        # Check to see if the unmount failed.  If it failed then
        # leave the temporary directory there.  We don't want to 
        # inadvertently erase extra files.
        ${MOUNT} | ${EGREP} -q ${mksysb_mnt_dir}
        if [[ $? -ne 0 ]]
        then
            ${RM} -r ${mksysb_mnt_dir}
        fi
    fi

    cleanup

} # end c_mkbosi_cleanup

#-------------------------------  MAIN  ---------------------------------------

# signal processing
trap c_mkbosi_cleanup 0
trap err_signal 1 2 11 15

# NIM initialization
nim_init


# set parameters from command line
while getopts :a:qv c
do
	case ${c} in

		a)		# validate the attr ass
				parse_attr_ass "${OPTARG}"

				# include the assignment for use in this environment
				eval ${variable}=\"${value}\"
				;;

		q)		# show attr info
				cmd_what
				exit 0
				;;

		v)		# verbose mode (for debugging)
				set -x
				for i in $(typeset +f)
				do
					typeset -ft $i
				done
				;;

		\?)	# unknown option
				error ${ERR_BAD_OPT} ${OPTARG}
				;;
	esac
done

# check for missing attrs
ck_attrs

# check that bos.sysmgt.sysbr is installed 
STATE=`${LSLPP} -qcIL bos.sysmgt.sysbr 2>/dev/null | ${CUT} -d":" -f6`  

if [ "${STATE}" != "C" ]
then
    nim_name=`${GREP} NIM_NAME ${NIMINFO} | ${CUT} -d"=" -f2`
    error ${ERR_NO_SYSBR} ${nim_name}   
fi

# handle exclude file resource 
[ -n "${exclude_files}" ] && process_exclude_file

# is this machine the image server?
if [ -n "${server}" ]
then
    # mount the remote location 

    # going to create a tmp directory at "/tmp/$$.mnt"
    # look for lowest, unused seqno
    typeset -i idx=0
    while (( ${idx} < ${MAX_MNT} ))
    do
        mksysb_mnt_dir="/tmp/$$.mnt${idx}"
        [[ ! -d ${mksysb_mnt_dir} ]] && break
            let idx=idx+1
    done
    (( ${idx} >= ${MAX_MNT} )) && error ${ERR_GEN_SEQNO} /tmp/$$.mnt

    ${MKDIR} ${mksysb_mnt_dir} 2>${ERR} || err_from_cmd ${MKDIR}

    nim_mount ${server}:`${DIRNAME} ${location}` ${mksysb_mnt_dir}
    location_access=${access_pnt}/`${BASENAME} ${location}`
else
    # location is local to this machine
    location_access=${location}
fi

# do space checking for mksysb image
check_space

# check mksysb_flags format and content
if [ -n "${mksysb_flags}" ] 
then
    # put hyphens in front of all flags
    mksysb_flags=$( ck_installp_flags "${mksysb_flags}" )

    # make sure all flags are valid
    /usr/bin/getopt "AVXab:ePimpvZT" ${mksysb_flags} > /dev/null 2>&1 ||
        error ${ERR_BAD_MKSYSB_FLAG}

    # verify that flags are in correct format
    mksysb_flags=$( format_mksysb_flags "${mksysb_flags}" )
fi

DWPAR=`/usr/sbin/lswpar -q -s D -a name`
if [ -n "$DWPAR" ]
then
        # call mksysb with the -N flag
        ${MKSYSB} -i ${mksysb_flags} -N ${location_access} 2>&1
else
        # call savevg to create the image
        ${SAVEVG} -i -f ${location_access} ${mksysb_flags} rootvg 2>&1

fi

# exit with return code from savevg or mksysb
RC=$?

# all done
exit $RC
