#!/bin/ksh # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # bos72V src/bos/usr/lib/nim/methods/c_mkspot.sh 1.54.4.8 # # Licensed Materials - Property of IBM # # COPYRIGHT International Business Machines Corp. 1993,2020 # 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 # @(#)12 1.54.4.8 src/bos/usr/lib/nim/methods/c_mkspot.sh, cmdnim, bos72V, v2020_14B1 3/30/20 18:25:44 # # COMPONENT_NAME: CMDNIM # # FUNCTIONS: ./usr/lib/nim/methods/c_mkspot.sh # # ORIGINS: 27 # # # (C) COPYRIGHT International Business Machines Corp. 1993 # 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 -------------------------------- typeset -i FUDGE_FACTOR=5 TAR_CREATE="${TAR} -cdpf - -C" TAR_EXTRACT="${TAR} -xpf -" #---------------------------- module globals -------------------------------- REQUIRED_ATTRS="location name source type st_applied st_committed" OPTIONAL_ATTRS="arch source_name version release installp_flags auto_expand mount_opts mksysb ios_mksysb nim_sync" location="" name="" source="" type="" version="" release="" mod="" src_access="" src_lic_access="" refresh="" source_is_tape="" tftp_enabled_by_nim="" mk_usr_spot="" not_41_master="" arch="" source_name="" bos_license="no" #lpp_source="" mspot_list=/usr/lib/bootpkg/bootpkg_list LSNIM="" XARGS=/usr/bin/xargs #---------------------------- undo -------------------------------- # # NAME: undo # # FUNCTION: # backs out changes made by c_mkspot # # EXECUTION ENVIRONMENT: # # NOTES: # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # 1-> = error message stuff # global: # # RETURNS: (int) # # OUTPUT: #------------------------------------------------------------------------------- function undo { nim_unmount # umount anything which has been mounted by nim_mount [[ $# = 1 ]] && err_from_cmd $1 || error $@ } # end of undo #---------------------------- ck_size_req -------------------------------- # # NAME: ck_size_req # # FUNCTION: # verifies that the target filesystem (where the SPOT is being created) has # enough free space to store the SPOT files (which will come from the # source) # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # 1 = failure # # OUTPUT: #------------------------------------------------------------------------------- function ck_size_req { # don't check if this is for a /usr conversion [[ ${location} = /usr ]] && return 0 typeset spot_fs="" typeset first_dir="" typeset image_data_file=${1} typeset -i size_req=0 typeset -i free_blocks=0 # can we read the file? [[ ! -r "${image_data_file}" ]] && \ error ${ERR_FILE_ACCESS} ${image_data_file} # parse out the size requirement ${AWK} ' /^[ ]*$/ { fs_data = 0; fs_name = 0;};\ $1 == "fs_data:" {fs_data = 1;};\ $1 == "FS_NAME=" {if ( $2 == "/usr" ) fs_name = 1;};\ $1 == "FS_SIZE=" {if (fs_data && fs_name) print "size_req=" $2;};' \ ${image_data_file} >${TMPDIR}/size_req 2>${ERR} || err_from_cmd ${AWK} [[ -s ${TMPDIR}/size_req ]] && . ${TMPDIR}/size_req || \ error ${ERR_SIZE} ${source} # find the first directory which currently exists in the pathname of the # target location first_dir=${location} while [[ -n "${first_dir}" ]] do [[ -d ${first_dir} ]] && break first_dir=${first_dir%/*} done # is the SPOT being created in a local jfs or jfs2 filesystem? ${C_STAT} -a location=${first_dir} -a vfstype="0 3" 2>${ERR} || \ err_from_cmd ${C_STAT} # what filesystem will the SPOT reside in? spot_fs=$( ${DF} -P ${first_dir} 2>${ERR} | ${AWK} 'NR==2{print $6}' ) [[ -z "${spot_fs}" ]] && err_from_cmd ${DF} # how much free space in that filesystem? free_blocks=$( ${DF} -P ${spot_fs} 2>${ERR} | ${AWK} 'NR==2{print $4}' ) [[ -z "${free_blocks}" ]] && err_from_cmd ${DF} # is there enough space? if (( ${size_req} > ${free_blocks} )) then # should we auto expand the filesystem? [[ "${installp_flags}" != *X* ]] && [[ "${auto_expand}" = "no" ]] && \ error ${ERR_SPACE} ${spot_fs} ${size_req} ${free_blocks} # subtract the currently available space let "size_req-=free_blocks" # expand the filesystem ${CHFS} -a size=+${size_req} ${spot_fs} 2>${ERR} 1>&2 || \ err_from_cmd "${CHFS} ${spot_fs}" fi } # end of ck_size_req #---------------------------- prep_tape -------------------------------- # # NAME: prep_tape # # FUNCTION: # prepares the specified tape device for use # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function prep_tape { typeset bs="" source_is_tape=TRUE # tape needs a "no-rewind-on-close" extension # remove whatever extension was given and use ".1" src_access="${source%.*}.1" # cache the logical device name of the tape drive tape_device_name=${source##*/} tape_device_name=${tape_device_name%.*} # get current block_size bs=$( ${LSATTR} -El ${tape_device_name} 2>${ERR} | \ ${AWK} '$1=="block_size"{print $2}' ) [[ -z "${bs}" ]] && err_from_cmd ${LSATTR} # block_size needs to be 512: should we change it? if [[ ${bs} != 512 ]] then ${CHDEV} -l ${tape_device_name} -a block_size=512 1>/dev/null 2>${ERR} ||\ err_from_cmd ${CHDEV} # by setting the global "tape_block_size" var, the cleanup function will # reset the block_size to the original value tape_block_size=${bs} fi # rewind the tape ${TCTL} -f ${src_access} rewind 2>${ERR} || err_from_cmd ${TCTL} # position to 4th record ${TCTL} -f ${src_access} fsf 3 2>${ERR} || err_from_cmd ${TCTL} # need to validate the AIX release level of this image & determine its # size requirements # therefore, restore the LPP_NAME and IMAGE_DATA files now cd ${TMPDIR} 2>${ERR} || err_from_cmd cd ${RESTORE} -xqf ${src_access} ${LPP_NAME} ${IMAGE_DATA} 2>${ERR} 1>&2 || \ err_from_cmd "${RESTORE} -xqf ${src_access} ${LPP_NAME} ${IMAGE_DATA}" # validate the release level ck_rel_level ${TMPDIR}/${LPP_NAME} # make sure there's enough space to create the new SPOT ck_size_req ${TMPDIR}/${IMAGE_DATA} # pop_spot expects tape to be positioned to the BOS image, so reposition # to 4th record now ${TCTL} -f ${src_access} rewind 2>${ERR} || err_from_cmd ${TCTL} ${TCTL} -f ${src_access} fsf 3 2>${ERR} || err_from_cmd ${TCTL} # initialize the method to restore the files restore_func="${RESTORE} -xqf ${src_access}" } # end of prep_tape #---------------------------- prep_cd -------------------------------- # # NAME: prep_cd # # FUNCTION: # prepares a CDROM device as the source for a SPOT # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function prep_cd { # mount the CDROM nim_mount ${source} [[ ! -e "${access_pnt}/${BOS_PATH_ON_CDROM}" ]] && \ BOS_PATH_ON_CDROM=${BOS_PATH_ON_CDROM_OLD} src_access=${access_pnt}/${BOS_PATH_ON_CDROM} src_lic_access=${access_pnt}/${LIC_PATH_ON_CDROM} # need to validate the AIX release level of this image & determine its # size requirements # therefore, restore the LPP_NAME and IMAGE_DATA files now cd ${TMPDIR} 2>${ERR} || err_from_cmd cd ${RESTORE} -xqf ${src_access} ${LPP_NAME} ${IMAGE_DATA} >/dev/null \ 2>${ERR} || err_from_cmd ${RESTORE} # validate the release level ck_rel_level ${TMPDIR}/${LPP_NAME} # make sure there's enough space to create the new SPOT ck_size_req ${TMPDIR}/${IMAGE_DATA} # initialize the method to restore the files restore_func="${RESTORE} -xqf ${src_access}" } # end of prep_cd #---------------------------- prep_SPOT -------------------------------- # # NAME: prep_SPOT # # FUNCTION: # prepares an existing SPOT to be used as a source for the creation of the # new SPOT # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function prep_SPOT { typeset -i spot_version=0 typeset -i spot_release=0 typeset -i spot_mod=0 # make sure source pathname ends with "/usr" if [[ "${source##*/}" != usr ]] then source=${source}/usr fi # setup local access nim_mount ${source} src_access=${access_pnt} # validate the version/release/mod level by looking in the product # database of the SPOT ODMDIR=${src_access}/lib/objrepos ${ODMGET} -qlpp_name=bos.rte product \ >${TMPDIR}/spot.product 2>${ERR} || err_from_cmd ${ODMGET} let spot_version=$( ${AWK} '$1=="ver"{print $3}' ${TMPDIR}/spot.product \ 2>/dev/null ) let spot_release=$( ${AWK} '$1=="rel"{print $3}' ${TMPDIR}/spot.product \ 2>/dev/null ) let spot_mod=$( ${AWK} '$1=="mod"{print $3}' ${TMPDIR}/spot.product \ 2>/dev/null ) if ((${spot_version}<${version})) || ((${spot_release}<${release})) then error ${ERR_RELEASE} ${spot_version} ${spot_release} ${source} fi let version=${spot_version} let release=${spot_release} let mod=${spot_mod} # make sure there's enough space to create the new SPOT ck_size_req ${src_access}/${IMAGE_DATA_IN_SPOT} # append "/usr" to (because "/usr" part will not come from source) location=${location}/usr # initialize the method to restore the files restore_func="copy_SPOT ${src_access} ${location}" } # end of prep_SPOT #---------------------------- copy_SPOT -------------------------------- # # NAME: copy_SPOT # # FUNCTION: # copies the specified SPOT to the specified destination # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function copy_SPOT { typeset origin=${1} typeset dest=${2} # cd to original SPOT cd ${origin} 2>${ERR} || err_from_cmd cd # copy all the files ${TAR_CREATE} ${origin} . | ( cd ${dest}; ${TAR_EXTRACT} ) || return 1 } # end of copy_SPOT #---------------------------- get_newest_ver -------------------------------- # # NAME: get_newest_ver # # FUNCTION: # Given a version number in VRMF, and a list of versions in VRMF, # output the latest version in the list that is <= the given version # number # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: file with list of VRMFs # a version in VRMF # global: # # RETURNS: (int) # 0 = success # # OUTPUT: a VRMF version number #------------------------------------------------------------------------------- function get_newest_ver { typeset VERSION_FILE=$1 typeset MYVER=$2 typeset -i10 sver=0 srel=0 smod=0 sfix=0 typeset -i10 this_ver=0 this_rel=0 this_mod=0 this_fix=0 typeset final LINE [[ -f $VERSION_FILE ]] || error $ERR_FILE_MODE "open" $VERSION_FILE sver=`echo $MYVER | $CUT -d '.' -f 1` || err_from_cmd cut srel=`echo $MYVER | $CUT -d '.' -f 2` || err_from_cmd cut smod=`echo $MYVER | $CUT -d '.' -f 3` || err_from_cmd cut sfix=`echo $MYVER | $CUT -d '.' -f 4` || err_from_cmd cut while read LINE do if [[ $MYVER = $LINE ]] then final=$MYVER break fi this_ver=`echo $LINE | $CUT -d '.' -f 1` || err_from_cmd cut this_rel=`echo $LINE | $CUT -d '.' -f 2` || err_from_cmd cut this_mod=`echo $LINE | $CUT -d '.' -f 3` || err_from_cmd cut this_fix=`echo $LINE | $CUT -d '.' -f 4` || err_from_cmd cut if [[ $sver -gt $this_ver ]] then final=$LINE elif [[ $sver -eq $this_ver && $srel -gt $this_rel ]] then final=$LINE elif [[ $sver -eq $this_ver && $srel -eq $this_rel && $smod -gt $this_mod ]] then final=$LINE elif [[ $sver -eq $this_ver && $srel -eq $this_rel && $smod -eq $this_mod && $sfix -gt $this_fix ]] then final=$LINE fi done <$VERSION_FILE echo $final } #---------------------------- prep_mksysb -------------------------------- # # NAME: prep_mksysb # # FUNCTION: # when the source is a mksysb, mounts resources and prepares variables # to later execute mksysb_SPOT # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function prep_mksysb { typeset WORKDIR typeset this_list=/tmp/$$.list typeset ver_list tar_list ls_list tf WORKDIR=$(/usr/lib/instl/libinst_mkworkdir) || err_from_cmd libinst_mkworkdir this_list=$WORKDIR/list ver_list=$WORKDIR/ver_list tar_list=$WORKDIR/tar_list ls_list=$WORKDIR/ls_list tf=$WORDIR/tmp_file $RM -f $this_list $ver_list $tar_list $tf $ls_list #mount the mksysb resource nim_mount ${source} mksysb_loc=${access_pnt} [[ -z $mksysb_loc ]] && error ${ERR_NFS_MNT} ${mksysb} "a temporary directory" (cd $TMPDIR;$RESTORE $mksysb_loc ./image.data 2>&1 >$ERR || err_from_cmd restore) mksysb_ver=$($GREP "OSLEVEL=" $TMPDIR/image.data 2>/dev/null | $CUT -d ' ' -f 2) # get the right list from the SPOT list file $EGREP '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:' $mspot_list >$tf || err_from_cmd egrep $SED 's/://g' $tf >$ver_list || err_from_cmd sed final=$(get_newest_ver $ver_list $mksysb_ver) || err_from_cmd get_newest_var ${AWK} -vver=$final 'BEGIN{this=0} /.*:$/ {this=0;if ($1 == ver ":") {this=1;next}} {if (this == 1 && NF > 0) print;}' $mspot_list > $this_list 2> $ERR || err_from_cmd awk # now find the right tar file (for the libnim.a.min) $LS /usr/lib/bootpkg/*-spotpkg.tar >$ls_list || err_from_cmd ls $AWK '{gsub("/usr/lib/bootpkg/", "", $0);gsub("-spotpkg.tar", "", $0); print}' $ls_list >$tar_list || err_from_cmd awk final=$(get_newest_ver $tar_list $mksysb_ver) || err_from_cmd get_newest_var tar_file=/usr/lib/bootpkg/${final}-spotpkg.tar #if the list is empty, error out [[ ! -s $this_list ]] && error ${ERR_NO_LIST} # initialize the method to restore the files restore_func="mksysb_SPOT ${mksysb_loc} ${location} ${this_list}" #${lpp_loc} } #---------------------------- restore_symlinks -------------------------------- # # NAME: restore_symlinks # # FUNCTION: # Given a symlink and a backup file, this function will restore the file # linked to by the symbolic link and will continually follow successive # symlinks restored until a regular file is restored. # # EXECUTION ENVIRONMENT: # # NOTES: # makes recursive calls # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: relative path of symlink # path to backup file # boolean recursive value # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function restore_symlinks { SYMLINK=$1 BACKUP_NAME=$2 ONE_MORE=$3 #restore the actual file $RESTORE $BACKUP_NAME $SYMLINK > /dev/null 2> ${ERR} || err_from_cmd restore #base case is that the current file is not a symlink if [[ $ONE_MORE = "0" ]] then #progress is made by following whatever is linked to next NEW_LINK=$($LS -l $SYMLINK | $AWK '{print $11}') #stop the recursion if the current file is not a symlink [[ -h $SYMLINK ]] || ONE_MORE=1 [[ $ONE_MORE = "0" ]] && restore_symlinks ".$NEW_LINK" $BACKUP_NAME $ONE_MORE fi } #end restore_symlinks #---------------------------- mksysb_SPOT -------------------------------- # # NAME: mksysb_SPOT # # FUNCTION: # Extracts necessary files from the source mksysb based on the SPOT lists and # the .proto files contained on the mksysb # # EXECUTION ENVIRONMENT: # # NOTES: # # # RECOVERY OPERATION: # calls error function # # DATA STRUCTURES: # parameters: mksysb path, spot path, list file path, lpp_source mount point # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function mksysb_SPOT { MKSYSB=$1 SPOTDIR=$2 LISTFILE=$3 TEMP1=/tmp/$$.ong TEMP2=/tmp/$$.tuk NEW_ROOT=$SPOTDIR/usr/lpp/bos/inst_root no_proto= no_file= #here, generate list of .proto files to restore using lsnim -a if_supported master PROTO_LIST=$(${LSNIM} -a if_defined master | ${AWK} '/.*:/{} /.*=.*/{printf("%s%s%s%s\n", $1, $2, $3, $4)}' | $AWK -F'=' '{print $2}' | $AWK -F'.' '{printf("./usr/lib/boot/network/%s.%s.proto\n", $1, $3)}') #we'll need the disk proto file for atm boot images PROTO_LIST="$PROTO_LIST ./usr/lib/boot/chrp.disk.proto" $GREP /usr/lpp/bos/inst_root $LISTFILE | $SED 's/\/usr\/lpp\/bos\/inst_root//' > $TEMP1 $GREP -v /usr/lpp/bos/inst_root $LISTFILE > $TEMP2 #first restore all proto files and all protoext files, then restore the /usr file system of the SPOT cd $SPOTDIR ${RESTORE} $MKSYSB ./usr/lib/boot/network/*.proto ./usr/lib/boot/*.proto ./usr/lib/boot/protoext/* ./usr/lib/boot/unix /usr/lib/boot/unix_up ./usr/lpp/bosinst/BosMenus ./usr/bin/tee ./usr/lpp/bos.sysmgt/nim/methods/c_* > /dev/null 2> ${ERR} || err_from_cmd restore /usr/sbin/restore -xqdf $MKSYSB ./usr/lib/methods ./usr/lib/drivers ./usr/lib/microcode ./usr/lpp/bosinst/iscsi > /dev/null 2> ${ERR} || err_from_cmd restore ${XARGS} ${RESTORE} $MKSYSB < $TEMP2 > /dev/null 2> ${ERR} || err_from_cmd xargs #restore the files from bos.sysmgt.nim.spot and c_errmsg $TAR -xf $tar_file 2>$ERR >/dev/null || err_from_cmd tar #restore all boot images from MKSYSB's /tftpboot into TMPDIR/tftpboot cd ${TMPDIR} || err_from_cmd cd /usr/sbin/restore -xqdf $MKSYSB ./tftpboot >/dev/null 2>&1 if [[ -s ./tftpboot ]]; then cd ./tftpboot || err_from_cmd cd #rename boot image files to match the current SPOT name stored in $name for file in `${LS} *.*.*` do # extract the SPOT name from boot images in MKSYSB prefix=`echo $file | ${AWK} -F"." '{print $1}'` # replace with our SPOT name from $name new_booti=`echo $file | ${SED} "s/$prefix/$name/g"` # move images to /tftpboot ${MV} $file $TFTPBOOT/$new_booti > /dev/null 2> ${ERR} || err_from_cmd mv done cd $SPOTDIR || err_from_cmd cd ${RM} -rf $TMPDIR/tftpboot > /dev/null 2> ${ERR} || err_from_cmd rm fi ODMDIR=$SPOTDIR/usr/lib/objrepos #see what filesets need to be added to the ODM for i in $DEFAULT_SPOT_OPTIONS_43 do #fake out the ODM to make NIM think that required filesets were normally installed echo "product:\nlpp_name=\"$i\"\nstate=5" | $ODMADD >/dev/null 2>${ERR} || err_from_cmd odmadd done ODMDIR=/etc/objrepos #restore the inst_root part of the SPOT $MKDIR -p $NEW_ROOT >/dev/null 2>${ERR} || err_from_cmd mkdir $LN -fs $SPOTDIR/usr/bin $NEW_ROOT/bin >/dev/null 2>${ERR} || err_from_cmd ln $LN -fs $SPOTDIR/usr/lib $NEW_ROOT/lib >/dev/null 2>${ERR} || err_from_cmd ln cd $NEW_ROOT >/dev/null 2>${ERR} || err_from_cmd cd ${RESTORE} $MKSYSB ./etc/objrepos/Cu* ./etc/objrepos/Pd* > /dev/null 2> ${ERR} || err_from_cmd restore /usr/sbin/restore -xqdf $MKSYSB ./sbin/helpers > /dev/null 2> ${ERR} || err_from_cmd restore ${XARGS} ${RESTORE} $MKSYSB < $TEMP1 > /dev/null 2> ${ERR} || err_from_cmd xargs $MKDIR -p $NEW_ROOT/var/adm/nim 2>&1 > /dev/null $RM $TEMP1 $TEMP2 $LISTFILE 2>&1 > /dev/null for i in $PROTO_LIST do if [[ -f $SPOTDIR/$i ]] then #get the name of the boot device this proto file is for dev=$(echo ${i##*/} | $AWK -F. '{print $2}') #grab all the files needed in the /usr part of the SPOT # (they are the 6th field in the proto file, if we're looking at a regular file) $AWK '{if (length($6) > 0 && $2 == "----" && substr($6, 1, 4) == "/usr"){print "."$6}}' $SPOTDIR/$i >$TEMP1 2>${ERR} || err_from_cmd awk #grab all the files needed in the root part of the SPOT (same rules as above) $AWK '{if (length($6) > 0 && $2 == "----" && substr($6, 1, 4) != "/usr"){print "."$6}}' $SPOTDIR/$i >$TEMP2 2>${ERR} || err_from_cmd awk #add all files listed in this device's protoext files (there's only /usr files in there) if $LS $SPOTDIR/usr/lib/boot/protoext/${dev}.proto.ext.* > /dev/null 2>&1; then $AWK '{if (length($6) > 0){print "."$6}}' $SPOTDIR/usr/lib/boot/protoext/${dev}.proto.ext.* >>$TEMP1 2>${ERR} || err_from_cmd awk fi #Some protoext files may refer to symbolic links even though it is not noted in the file #we still have to track down these links and restore them. This will be done through the #calls to restore_symlinks below. #restore /usr part and then / part cd $SPOTDIR [[ -s $TEMP1 ]] && ${XARGS} ${RESTORE} $MKSYSB < $TEMP1 > /dev/null 2> ${ERR} || err_from_cmd restore #in /usr part, if a proto file is a symlink, restore the file(s) it links to # also, warn if proto files are missing for i in $($CAT $TEMP1) do [[ ! -e $i ]] && no_file=1 #check for missing files [[ -h $i ]] && restore_symlinks $i $MKSYSB 0 done #do the same for root part cd $NEW_ROOT [[ -s $TEMP2 ]] && ${XARGS} ${RESTORE} $MKSYSB < $TEMP2 > /dev/null 2> ${ERR} || err_from_cmd restore for i in $($CAT $TEMP2) do [[ ! -e $i ]] && no_file=1 #check for missing files [[ -h $i ]] && restore_symlinks $i $MKSYSB 0 done $RM $TEMP1 $TEMP2 2>&1 > /dev/null else no_proto=1 fi done [[ -n $no_proto ]] && warning ${ERR_MISSING_PROTO_FILES} [[ -n $no_file ]] && warning ${ERR_MISSING_FILES} [[ ! -f $SPOTDIR/usr/lib/boot/unix_mp ]] && [[ -n `$LSNIM -a if_defined master | ${GREP} mp` ]] && warning ${ERR_MISSING_UNIX_MP} #restore the symlinks in inst_root $LN -fs /usr/bin $NEW_ROOT/bin >/dev/null 2>${ERR} || err_from_cmd ln $LN -fs /usr/lib $NEW_ROOT/lib >/dev/null 2>${ERR} || err_from_cmd ln echo mksysb_source=${mksysb} } #---------------------------- prep_file -------------------------------- # # NAME: prep_file # # FUNCTION: # prepares environment to use a file as the source # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function prep_file { typeset dir=${source%/*} typeset filename=${source##*/} # setup local access to source nim_mount ${dir} src_access=${access_pnt}/${filename} # need to validate the AIX release level of this image & determine its # size requirements # therefore, restore the LPP_NAME and IMAGE_DATA files now cd ${TMPDIR} 2>${ERR} || err_from_cmd cd ${RESTORE} -xqf ${src_access} ${LPP_NAME} ${IMAGE_DATA} >/dev/null \ 2>${ERR} || err_from_cmd ${RESTORE} # validate the release level ck_rel_level ${TMPDIR}/${LPP_NAME} # make sure there's enough space to create the new SPOT ck_size_req ${TMPDIR}/${IMAGE_DATA} # initialize the method to restore the files restore_func="${RESTORE} -xqf ${src_access}" } # end of prep_file #---------------------------- prep_dir -------------------------------- # # NAME: prep_dir # # FUNCTION: # prepares environment to access a directory containing a BOS image # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function prep_dir { # setup local access to BOS image nim_mount ${source} # check for installp/ppc path location [[ -e "${access_pnt}/installp/ppc/bos" ]] && \ src_access=${access_pnt}/installp/ppc/bos || \ src_access=${access_pnt}/bos src_lic_access=${access_pnt}/${LIC_PATH_ON_CDROM} # need to validate the AIX release level of this image & determine its # size requirements # therefore, restore the LPP_NAME and IMAGE_DATA files now cd ${TMPDIR} 2>${ERR} || err_from_cmd cd ${RESTORE} -xqf ${src_access} ${LPP_NAME} ${IMAGE_DATA} >/dev/null \ 2>${ERR} || err_from_cmd ${RESTORE} # validate the release level ck_rel_level ${TMPDIR}/${LPP_NAME} # make sure there's enough space to create the new SPOT ck_size_req ${TMPDIR}/${IMAGE_DATA} # initialize the method to restore the files restore_func="${RESTORE} -xqf ${src_access}" } # end of prep_dir #---------------------------- prep_source -------------------------------- # # NAME: prep_source # # FUNCTION: # prepares the specified source device for use # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function prep_source { # what kind of source? case ${type} in device) # tape or CDROM? if [[ ${source} = /dev/rmt[0-9]* ]] then prep_tape elif [[ ${source} = /dev/cd[0-9]* ]] || [[ -f "${source}" ]] || [[ ${source} = /dev/usbms[0-9]* ]] then prep_cd else error ${ERR_SOURCE} ${source} fi ;; spot) # source is another SPOT prep_SPOT ;; lpp_source) # source is directory containing a BOS image prep_dir ;; mksysb|ios_mksysb) # source is a mksysb of a system prep_mksysb ;; *) # unknown type error ${ERR_SYS} "unknown source type - \"${type}\"" ;; esac } # end of prep_source #---------------------------- pop_spot -------------------------------- # # NAME: pop_spot # # FUNCTION: # creates a new SPOT by creating the directory and filling it files from # the specified source # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function pop_spot { # does location exist? if [[ ! -d ${location} ]] then # create the new directory & include the first_dir info ${C_MKDIR} -alocation=${location} -aperms=${DEFAULT_PERMS} \ >${TMPDIR}/tmp 2>${ERR} || err_from_cmd ${C_MKDIR} [[ -s ${TMPDIR}/tmp ]] && . ${TMPDIR}/tmp first_dir=${first_dir:-${location}} fi # let's get into the new directory cd ${location} 2>${ERR} || err_from_cmd cd # restore the source files [[ -z "${restore_func}" ]] && error ${ERR_SOURCE} ${source} if ${restore_func} >/dev/null 2>${ERR} then # keep what we've got so far # copy licensing data [[ -n $src_lic_access ]] && [[ -d $src_lic_access ]] && ${MKDIR} -p ${location}/${LIC_PATH_ON_CDROM} 2>/dev/null && if ${CP} -R $src_lic_access/* ${location}/${LIC_PATH_ON_CDROM} 2>/dev/null then print "bos_license=yes" fi else # SPOT creation failed - remove the directory ${RM} -r ${first_dir} 1>/dev/null 2>&1 # display error msg err_from_cmd "${restore_func}" fi # When performing a spot-copy, we can skip the rest of # the file hierarchy processing. No reason to duplicate linkage [[ ${type} = "spot" ]] && return 0 # Create directories in the SPOT where the server's # libraries will be mounted for the chroot environment ${MKDIR} -p ${location}${NIM_CHROOT_LIBS1} ${MKDIR} -p ${location}${NIM_CHROOT_LIBS2} nim_mount /usr/ccs/lib ${location}${NIM_CHROOT_LIBS1} nim_mount /usr/lib ${location}${NIM_CHROOT_LIBS2} # We now have our system's /usr/ccs/lib and /usr/lib mounted within # our spot which we'll use in our LIBPATH instead of the spot's # /usr/ccs/lib and /usr/lib in the chroot environment. However, # these mounted directories still contain symbolic links to # /usr/ccs/lib and /usr/lib which resolve to the spot's # /usr/ccs/lib and /usr/lib in the chroot environment. Therefore we # need to create two new directories which contain these same links # except have them point to our mounted directories and also have # them take precedence in our LIBPATH. # iterate through the two library directories we've overmounted for lib in /usr/ccs/lib /usr/lib do # create directory hierarchy to hold symbolic links for dir in `$FIND $lib -type d`; do [[ ! -d ${location}${NIM_CHROOT_LIBS_LINKS_BASE}${dir} ]] && \ $MKDIR -p ${location}${NIM_CHROOT_LIBS_LINKS_BASE}${dir} done # create symbolic links # 'linkFrom' is the pathname to an existing symbolic link # 'linkTo' is the pathname 'linkFrom' points to for linkFrom in `$FIND $lib -type l`; do # need to locate the end of a potential chain of symbolic links linkTo=$linkFrom; while true; do # extract the path where the symbolc link points to linkTo=`$LS -l $linkTo 2>/dev/null | $AWK '{print $11}'` # no need to process any further if its a relative link if echo $linkTo | $EGREP -v '^/' > /dev/null 2>&1; then break; fi # finish looping when theres no more links to follow if [[ `$LS -l $linkTo 2>/dev/null | $AWK '{print $10}'` != '->' ]]; then break; fi done # create the link echo $linkTo | $EGREP '^(/usr/ccs/lib|/usr/lib)' > /dev/null 2>&1 && \ [[ ! -e ${location}${NIM_CHROOT_LIBS_LINKS_BASE}${linkFrom} ]] && \ $LN -s ${NIM_CHROOT_LIBS_BASE}${linkTo} ${location}${NIM_CHROOT_LIBS_LINKS_BASE}${linkFrom} done done nim_unmount ${location}${NIM_CHROOT_LIBS1} nim_unmount ${location}${NIM_CHROOT_LIBS2} } # end of pop_spot #---------------------------- ck_this_machine ------------------------------- # # NAME: ck_this_machine # # FUNCTION: # checks to make sure that the machine we're currently running on is at # version 4 or later # this is important because when we build a SPOT, we need to use version 4 # commands/libs to do it correctly # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function ck_this_machine { typeset msg="" typeset i="" typeset -i bos_version=0 typeset -i bos_release=0 # get the current version & release let bos_version=$(${UNAME} -v 2>/dev/null) let bos_release=$(${UNAME} -r 2>/dev/null) # check version/release if ((${bos_version}<${version})) || ((${bos_release}<${release})) then error ${ERR_RELEASE} ${bos_version} ${bos_release} server fi # check for options required to be a SPOT server if ck_spot_options /usr "${SPOT_SERVER_OPTIONS}" >${TMPDIR}/missing then # nothing else to do - server has all the required options return 0 fi # missing some stuff - format the error message if [[ -s ${TMPDIR}/missing ]] then # missing some stuff - format the error message error ${ERR_MISSING_OPTIONS_OLD} server \ "$( ${AWK} -F "=" '$1=="missing"{print "\t" $2}' ${TMPDIR}/missing)" else # missing some stuff - format the error message for i in ${SPOT_SERVER_OPTIONS} do msg="${msg}\t${i}\n" done error ${ERR_MISSING_OPTIONS_OLD} server "${msg}" fi } # end of ck_this_machine #---------------------------- secure_tftp_access ---------------------------- # # NAME: secure_tftp_access # # FUNCTION: # Modifies or creates /etc/tftpaccess.ctl to ensure tftp access # is not granted to more than /tftpboot as a result of SPOT # creation on a machine in the NIM env. # # # EXECUTION ENVIRONMENT: # # NOTES: # -- uncomments #allow:/tftpboot # -- comments out deny:/tftpboot # -- when looking for the above, assumes that whitespace may # have been inserted anywhere in the line (even though this # is illegal). # -- creates file if does not exist # -- changes/sets permissions on /etc/tftpaccess.ctl # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # tftp_enabled_by_nim = tells if NIM uncommented the # tftp entry in /etc/inetd.conf # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function secure_tftp_access { # if file already exists... if [ -s ${TFTPACCESS} ] then # is there a "deny" for /tftpboot that needs commenting out? DENY="^[ ]*deny:[ ]*\/tftpboot[ ]*$" ${GREP} -qE "${DENY}" ${TFTPACCESS} if [[ $? -eq 0 ]] then # comment it out ${SED} "s/${DENY}/#deny:\/tftpboot/g" ${TFTPACCESS} \ > ${TMPDIR}/.tftpaccess.ctl.$$ [ $? -eq 0 ] && \ ${MV} ${TMPDIR}/.tftpaccess.ctl.$$ ${TFTPACCESS} fi # is there an "allow" for /tftpboot that needs uncommenting? ALLOW="^[ ]*#[ ]*allow:[ ]*\/tftpboot[ ]*$" ${GREP} -qE "${ALLOW}" ${TFTPACCESS} if [[ $? -eq 0 ]] then # uncomment the allow ${SED} "s/${ALLOW}/allow:\/tftpboot/g" ${TFTPACCESS} \ > ${TMPDIR}/.tftpaccess.ctl.$$ [ $? -eq 0 ] && \ ${MV} ${TMPDIR}/.tftpaccess.ctl.$$ ${TFTPACCESS} has_access=1 else # is there already access to /tftpboot? ${GREP} -E "^allow:\/tftpboot$" ${TFTPACCESS} >/dev/null 2>&1 [[ $? -eq 0 ]] && has_access=1 fi else # File doesn't exist. Leave it that way (unrestricted) if # NIM didn't just grant tftp access to this machine [[ -z "${tftp_enabled_by_nim}" ]] && return 0 fi # append to file if necessary (or create if not there) if [[ -z "${has_access}" ]] then # add access for /tftpboot echo "# NIM access for network boot" >> ${TFTPACCESS} echo "allow:/tftpboot" >> ${TFTPACCESS} fi # make sure permissions and owner are correct ${CHMOD} 644 ${TFTPACCESS} ${CHOWN} root.system ${TFTPACCESS} return 0 } #---------------------------- usr_spot -------------------------------- # # NAME: usr_spot # # FUNCTION: # converts a /usr filesystem into a SPOT # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # # OUTPUT: #------------------------------------------------------------------------------- function usr_spot { typeset inst_root="${location}/${INST_ROOT}" # first, check to make sure that inst_roots haven't been removed ${INURID} -q >/dev/null 2>${ERR} if [[ $? -ne 0 ]] then # let's display the output as a warning if it produced any # (inurid normally doesn't return any output from this test.) [[ -s ${ERR} ]] && warning_from_cmd ${INURID} # print our own error msg error ${ERR_NO_INST_ROOTS} fi # remove any INST_ROOT remaining around # we do this to ensure we're not left with a partial INST_ROOT protected_dir ${inst_root} || ${RM} -r ${inst_root} 2>/dev/null # generate the list of inst_root files to be restored ${RESTORE} -Tvqf${src_access} 2>/dev/null | \ ${GREP} inst_root | ${AWK} '{print $2}' \ >${TMPDIR}/inst_root.files 2>${ERR} || err_from_cmd ${AWK} [[ ! -s ${TMPDIR}/inst_root.files ]] && \ error ${ERR_SYS} "unable to determine the list of inst_root files" # "cd" to "/" cd / 2>${ERR} || err_from_cmd cd # if source is a tape... if [[ -n "${source_is_tape}" ]] then # rewind the tape ${TCTL} -f ${src_access} rewind 2>${ERR} || err_from_cmd ${TCTL} # position to 4th record ${TCTL} -f ${src_access} fsf 3 2>${ERR} || err_from_cmd ${TCTL} fi # restore the BOS inst_root files from the source ${restore_func} $(${CAT} ${TMPDIR}/inst_root.files) \ >/dev/null 2>${ERR} || err_from_cmd ${restore_func} # add variable to NIMINFO file ${C_NIMINFO} -alocation=${NIMINFO} NIM_USR_SPOT=${name} 2>${ERR} || \ err_from_cmd ${C_NIMINFO} } # end of usr_spot #---------------------------- up_inetd_conf ------------------------------- # # NAME: up_inetd_conf # # FUNCTION: # updates the inetd.conf file in leu of an AIX command to do so (inetserv # has been withdrawn) # # EXECUTION ENVIRONMENT: # # NOTES: # calls error on failure # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # 1 = name of daemon to uncomment # global: # refresh = non-NULL if inetd daemon needs to be refreshed # # RETURNS: (int) # 0 = success # 1 = failure # # OUTPUT: #------------------------------------------------------------------------------- function up_inetd_conf { typeset daemon=${1} typeset line="" typeset first_field="" [[ -z "${daemon}" ]] && return 1 # make sure /etc/inetd.conf file is writable if [[ ! -w ${INETD_CONF} ]] then warning ${ERR_FILE_ACCESS} ${INETD_CONF} return 1 fi # is there a line referencing already? line=$(${GREP} -E "^${daemon}|^#${daemon}" ${INETD_CONF} 2>/dev/null) if [[ -z "${line}" ]] then warning ${ERR_FILE_MOD} ${INETD_CONF} return 1 fi # is it active? if [[ ${line} = ${daemon}?* ]] then # already uncommented - nothing else to do return 0 fi if [[ ${daemon} = "tftp" ]] then tftp_enabled_by_nim=1 fi # uncomment the entry first_field=${line%%[ ]*} line=${line#\#} if ${AWK} -v ff="${first_field}" -v new="${line}" \ '{if ($1==ff) $0=new; print}' ${INETD_CONF} \ >${TMPDIR}/$$.inetd_conf 2>${ERR} then if ${CAT} ${TMPDIR}/$$.inetd_conf >${INETD_CONF} 2>${ERR} then refresh=yes else warning ${ERR_FILE_MOD} ${INETD_CONF} fi else warning ${ERR_FILE_MOD} ${INETD_CONF} return 1 fi } # end of up_inetd_conf #---------------------------- start_daemons -------------------------------- # # NAME: start_daemons # # FUNCTION: # starts the daemons required in order to access a SPOT (boopd & tftp) # # EXECUTION ENVIRONMENT: # # NOTES: # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # 1 = failure # # OUTPUT: #------------------------------------------------------------------------------- function start_daemons { # NOTE that the inetserv command is no longer available and, currently, there # is no analogous command available # therefore, we'll update the inetd info ourselves refresh="" # for bootpd up_inetd_conf bootps # for tftp up_inetd_conf tftp # refresh the inetd daemon? if [[ -n "${refresh}" ]] then ${REFRESH} -s inetd 2>${ERR} 1>&2 || \ warning_from_cmd "${REFRESH} -s inetd" fi } # end of start_daemons #---------------------------- get_boot_images -------------------------------- # # NAME: get_boot_images # # FUNCTION: # gets the boot images from a source spot which was determined to be # cross platform # # # EXECUTION ENVIRONMENT: # # NOTES: # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = success # 1 = failure # # OUTPUT: #------------------------------------------------------------------------------- function get_boot_images { host=${source%%:*} list_cmd="" tftpboot_fs="" tftpboot_available=0 tftpboot_needed=0 DEFAULT_TFTPBOOT_SPACE=8000 # set a command for use as "lsnim" or "nimclient -l" ${CAT} /etc/niminfo | ${GREP} "NIM_NAME=master" >/dev/null if [[ $? -eq 0 ]] then list_cmd="/usr/sbin/lsnim" else list_cmd="${NIMCLIENT} -l" fi # create list of if_supported image files if_list=`${list_cmd} -a if_supported ${source_name} | ${AWK} '(NR>1){print $3"."$4}'` # determine available space (exand if allowed) # which filesystem does tftpboot reside tftpboot_fs=$( ${DF} -Pk ${TFTPBOOT} 2>/dev/null | \ ${AWK} 'NR==2{print $6}' ) tftpboot_available=$( ${DF} -Pk ${TFTPBOOT} 2>/dev/null | \ ${AWK} 'NR==2{print $4}' ) for i in ${if_list} do let tftpboot_needed=tftpboot_needed+${DEFAULT_TFTPBOOT_SPACE} done # is there enough free space in /tftpboot? if (( tftpboot_needed > tftpboot_available )) then if [[ "${auto_expand}" = "no" ]] then error ${ERR_SPACE} /tftpboot "${tftpboot_needed}" "${tftpboot_available}" fi # subtract the currently available space let "tftpboot_needed-=tftpboot_available" # chfs needs a block size of 512 let "tftpboot_needed*=2" #expand to fil requirements if ${CHFS} -a size=+${tftpboot_needed} ${tftpboot_fs} 2>${ERR} 1>&2 then : else err_from_cmd "${CHFS} /tftpboot" fi fi # if source is local, host variable will be same as source if [[ "${source}" = "${host}" ]] then for if_def in ${if_list} do ${CP} ${TFTPBOOT}/${source_name}.${if_def} ${TFTPBOOT}/${name}.${if_def} done elif [[ -n ${host} ]] then for if_def in ${if_list} do # usage - `tftp -o localfile host remotefile image` /bin/tftp -o ${TFTPBOOT}/${name}.${if_def} ${host} ${TFTPBOOT}/${source_name}.${if_def} >/dev/null 2>&1 done else error ${ERR_SYS} "null host assignment" fi } # end of get_boot_images #---------------------------- c_mkspot -------------------------------- # # NAME: c_mkspot # # FUNCTION: # creates a SPOT by populating the specified directory with the bos.rte # files from the specified source # # EXECUTION ENVIRONMENT: # # NOTES: # this method does NOT install packages into the SPOT - that work is done # by c_instspot # # RECOVERY OPERATION: # # DATA STRUCTURES: # parameters: # global: # # RETURNS: (int) # 0 = SPOT created # 1 = error encountered - message on stderr # # OUTPUT: #------------------------------------------------------------------------------- # signal processing trap cleanup 0 trap err_signal 1 2 11 15 # NIM initialization nim_init # initialize local variables # set parameters from command line while getopts :a:qvf 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 ;; f) # not a 4.1 NIM master not_41_master=1 ;; 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 [[ -n $ios_mksysb ]] && mksysb=$ios_mksysb # set defaults let version=${version:-4} let release=${release:-0} installp_flags=$( ck_installp_flags "${installp_flags}" ) # check this machine's version - it must be at version 4 or greater ck_this_machine # Perform some SPOT location-specific tasks before calling prep_source... if [[ ${location} != /usr ]] then # Need to append the SPOTname to if [[ -n ${nim_sync} ]] then # When a spot is renamed, the location directory is unchanged # and uses the original name. In the case of nim sync, we must # use the location of the source spot. We can do that by appending # the original name of the spot to the location variable. typeset orignametmp=${source%/usr} typeset origname=`/usr/bin/basename $orignametmp` location=${location}/${origname} else location=${location}/${name} fi else # global variable to be used in ck_rel_level mk_usr_spot=yes fi #make an LSNIM variable and make it point to either lsnim or to nimclient -l depending on whether #this is on a NIM master if [[ $NIM_CONFIGURATION = "master" ]] then LSNIM="/usr/sbin/lsnim" else LSNIM="/usr/sbin/nimclient -l" fi [[ -n $mksysb ]] && RESTORE="$RESTORE -xqf" undo_on_interrupt="undo" # prepare the source device for use prep_source # /usr SPOT or new location? if [[ ${location} = /usr ]] then # converting a /usr filesystem is relatively easy, but does require some # checking # do so now usr_spot else # need to create a directory and populate it pop_spot fi # if we get this far, we've got something worth keeping # return the version, release and modification level on stdout # as attr assignments so the calling method will pick them up let version=${version} let release=${release} print "version=${version}" print "release=${release}" # modification level is only applicable for SPOT # definitions starting with 4.2 and later and when # the NIM master is running 4.2 or greater if (( (( (( ${version} == 4 )) && (( ${release} >= 2 )) )) || (( ${version} > 4 )) )) && [[ -n "${not_41_master}" ]] then let mod=${mod} print "mod=${mod}" fi # start the daemons start_daemons # set up tftp permissions for /tftpboot secure_tftp_access # if different arch, we need to get boot images for new spot if [[ -n ${arch} ]] then get_boot_images fi # all done exit 0